rental-impl 0.5.2

An implementation detail of rental. Should not be used directly.
Documentation
#![recursion_limit = "512"]

extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;

use std::iter::{self, FromIterator};
use syn::spanned::Spanned;
use syn::visit::Visit;
use syn::fold::Fold;
use quote::ToTokens;
use proc_macro2::Span;


/// From `procedural_masquerade` crate
#[doc(hidden)]
fn _extract_input(derive_input: &str) -> &str {
	let mut input = derive_input;

	for expected in &["#[allow(unused)]", "enum", "ProceduralMasqueradeDummyType", "{", "Input", "=", "(0,", "stringify!", "("] {
		input = input.trim_left();
		assert!(input.starts_with(expected), "expected prefix {:?} not found in {:?}", expected, derive_input);
		input = &input[expected.len()..];
	}

	for expected in [")", ").0,", "}"].iter().rev() {
		input = input.trim_right();
		assert!(input.ends_with(expected), "expected suffix {:?} not found in {:?}", expected, derive_input);
		let end = input.len() - expected.len();
		input = &input[..end];
	}

	input
}


#[doc(hidden)]
#[allow(non_snake_case)]
#[proc_macro_derive(__rental_traits)]
pub fn __rental_traits(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	let mut tokens = proc_macro2::TokenStream::new();

	let max_arity = _extract_input(&input.to_string()).parse::<usize>().expect("Input must be an integer literal.");
	write_rental_traits(&mut tokens, max_arity);

	tokens.into()
}


#[doc(hidden)]
#[allow(non_snake_case)]
#[proc_macro_derive(__rental_structs_and_impls)]
pub fn __rental_structs_and_impls(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	let mut tokens = proc_macro2::TokenStream::new();

	for item in syn::parse_str::<syn::File>(_extract_input(&input.to_string())).expect("Failed to parse items in module body.").items.iter() {
		match *item {
			syn::Item::Use(..) => {
				item.to_tokens(&mut tokens);
			},
			syn::Item::Type(..) => {
				item.to_tokens(&mut tokens);
			},
			syn::Item::Struct(ref struct_info) => {
				write_rental_struct_and_impls(&mut tokens, &struct_info);
			},
			_ => panic!("Item must be a `use` or `struct`."),
		}
	}

	tokens.into()
}


fn write_rental_traits(tokens: &mut proc_macro2::TokenStream, max_arity: usize) {
	let call_site: Span = Span::call_site();

	let mut lt_params = vec![syn::LifetimeDef::new(syn::Lifetime::new("'a0", call_site))];

	for arity in 2 .. max_arity + 1 {
		let trait_ident = &syn::Ident::new(&format!("Rental{}", arity), call_site);
		let lt_param = syn::LifetimeDef::new(syn::Lifetime::new(&format!("'a{}", arity - 1), call_site));
		lt_params[arity - 2].bounds.push(lt_param.lifetime.clone());
		lt_params.push(lt_param);

		let lt_params_iter = &lt_params;
		quote!(
			#[doc(hidden)]
			pub unsafe trait #trait_ident<#(#lt_params_iter),*> {
				type Borrow;
				type BorrowMut;
			}
		).to_tokens(tokens);
	}
}


fn write_rental_struct_and_impls(tokens: &mut proc_macro2::TokenStream, struct_info: &syn::ItemStruct) {
	let def_site: Span = Span::call_site(); // FIXME: hygiene
	let call_site: Span = Span::call_site();

	if let syn::Visibility::Inherited = struct_info.vis {
		panic!("Struct `{}` must be non-private.", struct_info.ident);
	}

	let attribs = get_struct_attribs(struct_info);
	let (fields, fields_brace) = prepare_fields(struct_info);

	let struct_generics = &struct_info.generics;
	let struct_rlt_args = &fields.iter().fold(Vec::new(), |mut rlt_args, field| { rlt_args.extend(field.self_rlt_args.iter()); rlt_args });
	if let Some(collide) = struct_rlt_args.iter().find(|rlt_arg| struct_generics.lifetimes().any(|lt_def| lt_def.lifetime == ***rlt_arg)) {
		panic!("Struct `{}` lifetime parameter `{}` collides with rental lifetime.", struct_info.ident, collide);
	}
	let last_rlt_arg = &struct_rlt_args[struct_rlt_args.len() - 1];
	let static_rlt_args = &iter::repeat(syn::Lifetime::new("'static", def_site)).take(struct_rlt_args.len()).collect::<Vec<_>>();
	let self_rlt_args = &iter::repeat(syn::Lifetime::new("'__s", def_site)).take(struct_rlt_args.len()).collect::<Vec<_>>();

	let item_ident = &struct_info.ident;
	let item_vis = &struct_info.vis;
	let item_ident_str = syn::LitStr::new(&item_ident.to_string(), item_ident.span());

	let (struct_impl_params, struct_impl_args, struct_where_clause) = struct_generics.split_for_impl();
	let where_extra = if let Some(ref struct_where_clause) = struct_where_clause {
		if struct_where_clause.predicates.is_empty() {
			quote!(where)
		} else if struct_where_clause.predicates.trailing_punct() {
			quote!()
		} else {
			quote!(,)
		}
	} else {
		quote!(where)
	};
	let struct_lt_params = &struct_generics.lifetimes().collect::<Vec<_>>();
	let struct_nonlt_params = &struct_generics.params.iter().filter(|param| if let syn::GenericParam::Lifetime(..) = **param { false } else { true }).collect::<Vec<_>>();
	let struct_lt_args = &struct_lt_params.iter().map(|lt_def| &lt_def.lifetime).collect::<Vec<_>>();

	let struct_nonlt_args = &struct_nonlt_params.iter().map(|param| match **param {
		syn::GenericParam::Type(ref ty) => &ty.ident,
		syn::GenericParam::Const(ref co) => &co.ident,
		syn::GenericParam::Lifetime(..) => unreachable!(),
	}).collect::<Vec<_>>();

	let rental_trait_ident = syn::Ident::new(&format!("Rental{}", struct_rlt_args.len()), def_site);
	let field_idents = &fields.iter().map(|field| &field.name).collect::<Vec<_>>();
	let local_idents = field_idents;
	let field_ident_strs = &field_idents.iter().map(|ident| syn::LitStr::new(&ident.to_string(), ident.span())).collect::<Vec<_>>();

	let (ref self_ref_param, ref self_lt_ref_param, ref self_mut_param, ref self_move_param, ref self_arg) = if attribs.is_deref_suffix {
		(quote!(_self: &Self), quote!(_self: &'__s Self),  quote!(_self: &mut Self), quote!(_self: Self), quote!(_self))
	} else {
		(quote!(&self), quote!(&'__s self), quote!(&mut self), quote!(self), quote!(self))
	};

	let borrow_ident = syn::Ident::new(&(struct_info.ident.to_string() + "_Borrow"), call_site);
	let borrow_mut_ident = syn::Ident::new(&(struct_info.ident.to_string() + "_BorrowMut"), call_site);
	let borrow_quotes = &make_borrow_quotes(self_arg, &fields, attribs.is_rental_mut);
	let borrow_tys = &borrow_quotes.iter().map(|&BorrowQuotes{ref ty, ..}| ty).collect::<Vec<_>>();
	let borrow_ty_hacks = &borrow_quotes.iter().map(|&BorrowQuotes{ref ty_hack, ..}| ty_hack).collect::<Vec<_>>();
	let borrow_suffix_ty = &borrow_tys[fields.len() - 1];
	let borrow_exprs = &borrow_quotes.iter().map(|&BorrowQuotes{ref expr, ..}| expr).collect::<Vec<_>>();
	let borrow_suffix_expr = &borrow_exprs[fields.len() - 1];
	let borrow_mut_tys = &borrow_quotes.iter().map(|&BorrowQuotes{ref mut_ty, ..}| mut_ty).collect::<Vec<_>>();
	let borrow_mut_ty_hacks = &borrow_quotes.iter().map(|&BorrowQuotes{ref mut_ty_hack, ..}| mut_ty_hack).collect::<Vec<_>>();
	let borrow_mut_suffix_ty = &borrow_mut_tys[fields.len() - 1];
	let borrow_mut_exprs = &borrow_quotes.iter().map(|&BorrowQuotes{ref mut_expr, ..}| mut_expr).collect::<Vec<_>>();
	let borrow_mut_suffix_expr = &borrow_mut_exprs[fields.len() - 1];

	let struct_rlt_params = &struct_rlt_args.iter().zip(struct_rlt_args.iter().skip(1)).map(|(rlt_arg, next_rlt_arg)| {
		syn::LifetimeDef {
			attrs: Vec::with_capacity(0),
			lifetime: (*rlt_arg).clone(),
			bounds: vec![(*next_rlt_arg).clone()].into_iter().collect(),
			colon_token: Default::default(),
		}
	}).chain(Some(syn::LifetimeDef {
			attrs: Vec::with_capacity(0),
			lifetime: struct_rlt_args[struct_rlt_args.len() - 1].clone(),
			bounds: syn::punctuated::Punctuated::new(),
			colon_token: Default::default(),
	})).collect::<Vec<_>>();

	let borrow_lt_params = &struct_rlt_params.iter().cloned()
		.chain( struct_lt_params.iter().map(|lt_def| {
			let mut lt_def = (*lt_def).clone();
			lt_def.bounds.push(struct_rlt_args[0].clone());
			lt_def
		})).collect::<Vec<_>>();

	let field_tys = &fields.iter().map(|field| &field.erased.ty).collect::<Vec<_>>();
	let head_ident = &local_idents[0];
	let head_ident_rep = &iter::repeat(&head_ident).take(fields.len() - 1).collect::<Vec<_>>();
	let head_ty = &fields[0].orig_ty;
	let tail_tys = &field_tys.iter().skip(1).collect::<Vec<_>>();
	let tail_idents = &local_idents.iter().skip(1).collect::<Vec<_>>();
	let tail_closure_tys = &fields.iter().skip(1).map(|field| syn::Ident::new(&format!("__F{}", field.name), call_site)).collect::<Vec<_>>();
	let tail_closure_quotes = make_tail_closure_quotes(&fields, borrow_quotes, attribs.is_rental_mut);
	let tail_closure_bounds = &tail_closure_quotes.iter().map(|&ClosureQuotes{ref bound, ..}| bound).collect::<Vec<_>>();
	let tail_closure_exprs = &tail_closure_quotes.iter().map(|&ClosureQuotes{ref expr, ..}| expr).collect::<Vec<_>>();
	let tail_try_closure_bounds = &tail_closure_quotes.iter().map(|&ClosureQuotes{ref try_bound, ..}| try_bound).collect::<Vec<_>>();
	let tail_try_closure_exprs = &tail_closure_quotes.iter().map(|&ClosureQuotes{ref try_expr, ..}| try_expr).collect::<Vec<_>>();
	let suffix_ident = &field_idents[fields.len() - 1];
	let suffix_ty = &fields[fields.len() - 1].erased.ty;
	let suffix_rlt_args = &fields[fields.len() - 1].self_rlt_args.iter().chain(fields[fields.len() - 1].used_rlt_args.iter()).collect::<Vec<_>>();
	let suffix_ident_str = syn::LitStr::new(&suffix_ident.to_string(), suffix_ident.span());
	let suffix_orig_ty = &fields[fields.len() - 1].orig_ty;

	let rstruct = syn::ItemStruct{
		ident: struct_info.ident.clone(),
		vis: item_vis.clone(),
		attrs: attribs.doc.clone(),
		fields: syn::Fields::Named(syn::FieldsNamed{
			brace_token: fields_brace,
			named: fields.iter().enumerate().map(|(i, field)| {
				let mut field_erased = field.erased.clone();
				if i < fields.len() - 1 {
					field_erased.attrs.push(parse_quote!(#[allow(dead_code)]));
				}
				field_erased
			}).rev().collect(),
		}),
		generics: struct_info.generics.clone(),
		struct_token: struct_info.struct_token,
		semi_token: None,
	};

	let borrow_struct = syn::ItemStruct{
		ident: borrow_ident.clone(),
		vis: item_vis.clone(),
		attrs: Vec::with_capacity(0),
		fields: syn::Fields::Named(syn::FieldsNamed{
			brace_token: Default::default(),
			named: fields.iter().zip(borrow_tys).enumerate().map(|(idx, (field, borrow_ty))| {
				let mut field = field.erased.clone();
				field.vis = if !attribs.is_rental_mut || idx == fields.len() - 1 { item_vis.clone() } else { syn::Visibility::Inherited };
				field.ty = syn::parse::<syn::Type>((**borrow_ty).clone().into()).unwrap();
				field
			}).collect(),
		}),
		generics: {
			let mut gen = struct_generics.clone();
			let params = borrow_lt_params.iter().map(|lt| syn::GenericParam::Lifetime(lt.clone()))
				.chain(gen.type_params().map(|p| syn::GenericParam::Type(p.clone())))
				.chain(gen.const_params().map(|p| syn::GenericParam::Const(p.clone())))
				.collect();
			gen.params = params;
			gen
		},
		struct_token: Default::default(),
		semi_token: None,
	};

	let borrow_mut_struct = syn::ItemStruct{
		ident: borrow_mut_ident.clone(),
		vis: item_vis.clone(),
		attrs: Vec::with_capacity(0),
		fields: syn::Fields::Named(syn::FieldsNamed{
			brace_token: Default::default(),
			named: fields.iter().zip(borrow_mut_tys).enumerate().map(|(idx, (field, borrow_mut_ty))| {
				let mut field = field.erased.clone();
				field.vis = if idx == fields.len() - 1 || !attribs.is_rental_mut { (*item_vis).clone() } else { syn::Visibility::Inherited };
				field.ty = syn::parse::<syn::Type>((**borrow_mut_ty).clone().into()).unwrap();
				field
			}).collect(),
		}),
		generics: {
			let mut gen = struct_generics.clone();
			let params = borrow_lt_params.iter().map(|lt| syn::GenericParam::Lifetime(lt.clone()))
				.chain(gen.type_params().map(|p| syn::GenericParam::Type(p.clone())))
				.chain(gen.const_params().map(|p| syn::GenericParam::Const(p.clone())))
				.collect();
			gen.params = params;
			gen
		},
		struct_token: Default::default(),
		semi_token: None,
	};

	let prefix_tys = &fields.iter().map(|field| &field.erased.ty).take(fields.len() - 1).collect::<Vec<_>>();
	let static_assert_prefix_stable_derefs = &prefix_tys.iter().map(|field| {
		if attribs.is_rental_mut {
			quote_spanned!(field.span()/*.resolved_at(def_site)*/ => __rental_prelude::static_assert_mut_stable_deref::<#field>();)
		} else {
			quote_spanned!(field.span()/*.resolved_at(def_site)*/ => __rental_prelude::static_assert_stable_deref::<#field>();)
		}
	}).collect::<Vec<_>>();
	let prefix_clone_traits = iter::repeat(quote!(__rental_prelude::CloneStableDeref)).take(prefix_tys.len());

	let struct_span = struct_info.span()/*.resolved_at(def_site)*/;
	let suffix_ty_span = suffix_ty.span()/*.resolved_at(def_site)*/;

	quote_spanned!(struct_span =>
		#rstruct

		/// Shared borrow of a rental struct.
		#[allow(non_camel_case_types, non_snake_case, dead_code)]
		#borrow_struct

		/// Mutable borrow of a rental struct.
		#[allow(non_camel_case_types, non_snake_case, dead_code)]
		#borrow_mut_struct
	).to_tokens(tokens);

	quote_spanned!(struct_span =>
		#[allow(dead_code)]
		impl<#(#borrow_lt_params,)* #(#struct_nonlt_params),*> #borrow_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*> #struct_where_clause {
			fn unify_tys_hack(#(#local_idents: #borrow_ty_hacks),*) -> #borrow_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*> {
				#borrow_ident {
					#(#field_idents: #local_idents,)*
				}
			}
		}
	).to_tokens(tokens);

	quote_spanned!(struct_span =>
		#[allow(dead_code)]
		impl<#(#borrow_lt_params,)* #(#struct_nonlt_params),*> #borrow_mut_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*> #struct_where_clause {
			fn unify_tys_hack(#(#local_idents: #borrow_mut_ty_hacks),*) -> #borrow_mut_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*> {
				#borrow_mut_ident {
					#(#field_idents: #local_idents,)*
				}
			}
		}
	).to_tokens(tokens);

	quote_spanned!(struct_span =>
		unsafe impl<#(#borrow_lt_params,)* #(#struct_nonlt_params),*> __rental_prelude::#rental_trait_ident<#(#struct_rlt_args),*> for #item_ident #struct_impl_args #struct_where_clause {
			type Borrow = #borrow_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>;
			type BorrowMut = #borrow_mut_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>;
		}
	).to_tokens(tokens);

	quote_spanned!(struct_span =>
		#[allow(dead_code, unused_mut, unused_unsafe, non_camel_case_types)]
		impl #struct_impl_params #item_ident #struct_impl_args #struct_where_clause {
			/// Create a new instance of the rental struct.
			///
			/// The first argument provided is the head, followed by a series of closures, one for each tail field. Each of these closures will receive, as its arguments, a borrow of the previous field, followed by borrows of the remaining prefix fields if the struct is a shared rental. If the struct is a mutable rental, only the immediately preceding field is passed.
			pub fn new<#(#tail_closure_tys),*>(
				mut #head_ident: #head_ty,
				#(#tail_idents: #tail_closure_tys),*
			) -> Self where #(#tail_closure_tys: #tail_closure_bounds),*
			{
				#(#static_assert_prefix_stable_derefs)*

				#(let mut #tail_idents = unsafe { __rental_prelude::transmute::<_, #tail_tys>(#tail_closure_exprs) };)*

				#item_ident {
					#(#field_idents: #local_idents,)*
				}
			}

			/// Attempt to create a new instance of the rental struct.
			///
			/// As `new`, but each closure returns a `Result`. If one of them fails, execution is short-circuited and a tuple of the error and the original head value is returned to you.
			pub fn try_new<#(#tail_closure_tys,)* __E>(
				mut #head_ident: #head_ty,
				#(#tail_idents: #tail_closure_tys),*
			) -> __rental_prelude::RentalResult<Self, __E, #head_ty> where
				#(#tail_closure_tys: #tail_try_closure_bounds,)*
			{
				#(#static_assert_prefix_stable_derefs)*

				#(let mut #tail_idents = {
					let temp = #tail_try_closure_exprs.map(|t| unsafe { __rental_prelude::transmute::<_, #tail_tys>(t) });
					match temp {
						__rental_prelude::Result::Ok(t) => t,
						__rental_prelude::Result::Err(e) => return __rental_prelude::Result::Err(__rental_prelude::RentalError(e.into(), #head_ident_rep)),
					}
				};)*

				__rental_prelude::Result::Ok(#item_ident {
					#(#field_idents: #local_idents,)*
				})
			}

			/// Attempt to create a new instance of the rental struct.
			///
			/// As `try_new`, but only the error value is returned upon failure; the head value is dropped. This method interacts more smoothly with existing error conversions.
			pub fn try_new_or_drop<#(#tail_closure_tys,)* __E>(
				mut #head_ident: #head_ty,
				#(#tail_idents: #tail_closure_tys),*
			) -> __rental_prelude::Result<Self, __E> where
				#(#tail_closure_tys: #tail_try_closure_bounds,)*
			{
				#(#static_assert_prefix_stable_derefs)*

				#(let mut #tail_idents = {
					let temp = #tail_try_closure_exprs.map(|t| unsafe { __rental_prelude::transmute::<_, #tail_tys>(t) });
					match temp {
						__rental_prelude::Result::Ok(t) => t,
						__rental_prelude::Result::Err(e) => return __rental_prelude::Result::Err(e.into()),
					}
				};)*

				__rental_prelude::Result::Ok(#item_ident {
					#(#field_idents: #local_idents,)*
				})
			}

			/// Return lifetime-erased shared borrows of the fields of the struct.
			///
			/// This is unsafe because the erased lifetimes are fake. Use this only if absolutely necessary and be very mindful of what the true lifetimes are.
			pub unsafe fn all_erased(#self_ref_param) -> <Self as __rental_prelude::#rental_trait_ident>::Borrow {
				#borrow_ident::unify_tys_hack(#(__rental_prelude::transmute(#borrow_exprs),)*)
			}

			/// Return a lifetime-erased mutable borrow of the suffix of the struct.
			///
			/// This is unsafe because the erased lifetimes are fake. Use this only if absolutely necessary and be very mindful of what the true lifetimes are.
			pub unsafe fn all_mut_erased(#self_mut_param) -> <Self as __rental_prelude::#rental_trait_ident>::BorrowMut {
				#borrow_mut_ident::unify_tys_hack(#(__rental_prelude::transmute(#borrow_mut_exprs),)*)
			}

			/// Execute a closure on the shared suffix of the struct.
			///
			/// The closure may return any value not bounded by one of the special rentail lifetimes of the struct.
			pub fn rent<__F, __R>(#self_ref_param, f: __F) -> __R where
				__F: for<#(#suffix_rlt_args,)*> __rental_prelude::FnOnce(#borrow_suffix_ty) -> __R,
				__R: #(#struct_lt_args +)*,
			{
				f(#borrow_suffix_expr)
			}

			/// Execute a closure on the mutable suffix of the struct.
			///
			/// The closure may return any value not bounded by one of the special rentail lifetimes of the struct.
			pub fn rent_mut<__F, __R>(#self_mut_param, f: __F) -> __R where
				__F: for<#(#suffix_rlt_args,)*> __rental_prelude::FnOnce(#borrow_mut_suffix_ty) -> __R,
				__R: #(#struct_lt_args +)*,
			{
				f(#borrow_mut_suffix_expr)
			}

			/// Return a shared reference from the shared suffix of the struct.
			///
			/// This is a subtle variation of `rent` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
			pub fn ref_rent<__F, __R>(#self_ref_param, f: __F) -> &__R where
				__F: for<#(#suffix_rlt_args,)*> __rental_prelude::FnOnce(#borrow_suffix_ty) -> &#last_rlt_arg __R,
				__R: ?Sized //#(#struct_lt_args +)*,
			{
				f(#borrow_suffix_expr)
			}

			/// Optionally return a shared reference from the shared suffix of the struct.
			///
			/// This is a subtle variation of `rent` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
			pub fn maybe_ref_rent<__F, __R>(#self_ref_param, f: __F) -> __rental_prelude::Option<&__R> where
				__F: for<#(#suffix_rlt_args,)*> __rental_prelude::FnOnce(#borrow_suffix_ty) -> __rental_prelude::Option<&#last_rlt_arg __R>,
				__R: ?Sized //#(#struct_lt_args +)*,
			{
				f(#borrow_suffix_expr)
			}

			/// Try to return a shared reference from the shared suffix of the struct, or an error on failure.
			///
			/// This is a subtle variation of `rent` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
			pub fn try_ref_rent<__F, __R, __E>(#self_ref_param, f: __F) -> __rental_prelude::Result<&__R, __E> where
				__F: for<#(#suffix_rlt_args,)*> __rental_prelude::FnOnce(#borrow_suffix_ty) -> __rental_prelude::Result<&#last_rlt_arg __R, __E>,
				__R: ?Sized //#(#struct_lt_args +)*,
			{
				f(#borrow_suffix_expr)
			}

			/// Return a mutable reference from the mutable suffix of the struct.
			///
			/// This is a subtle variation of `rent_mut` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
			pub fn ref_rent_mut<__F, __R>(#self_mut_param, f: __F) -> &mut __R where
				__F: for<#(#suffix_rlt_args,)*> __rental_prelude::FnOnce(#borrow_mut_suffix_ty) -> &#last_rlt_arg  mut __R,
				__R: ?Sized //#(#struct_lt_args +)*,
			{
				f(#borrow_mut_suffix_expr)
			}

			/// Optionally return a mutable reference from the mutable suffix of the struct.
			///
			/// This is a subtle variation of `rent_mut` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
			pub fn maybe_ref_rent_mut<__F, __R>(#self_mut_param, f: __F) -> __rental_prelude::Option<&mut __R> where
				__F: for<#(#suffix_rlt_args,)*> __rental_prelude::FnOnce(#borrow_mut_suffix_ty) -> __rental_prelude::Option<&#last_rlt_arg  mut __R>,
				__R: ?Sized //#(#struct_lt_args +)*,
			{
				f(#borrow_mut_suffix_expr)
			}

			/// Try to return a mutable reference from the mutable suffix of the struct, or an error on failure.
			///
			/// This is a subtle variation of `rent_mut` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
			pub fn try_ref_rent_mut<__F, __R, __E>(#self_mut_param, f: __F) -> __rental_prelude::Result<&mut __R, __E> where
				__F: for<#(#suffix_rlt_args,)*> __rental_prelude::FnOnce(#borrow_mut_suffix_ty) -> __rental_prelude::Result<&#last_rlt_arg  mut __R, __E>,
				__R: ?Sized //#(#struct_lt_args +)*,
			{
				f(#borrow_mut_suffix_expr)
			}

			/// Drop the rental struct and return the original head value to you.
			pub fn into_head(#self_move_param) -> #head_ty {
				let Self{#head_ident, ..} = #self_arg;
				#head_ident
			}
		}
	).to_tokens(tokens);

	if !attribs.is_rental_mut {
		quote_spanned!(struct_span =>
			#[allow(dead_code)]
			impl #struct_impl_params #item_ident #struct_impl_args #struct_where_clause {
				/// Return a shared reference to the head field of the struct.
				pub fn head(#self_ref_param) -> &<#head_ty as __rental_prelude::Deref>::Target {
					&*#self_arg.#head_ident
				}

				/// Execute a closure on shared borrows of the fields of the struct.
				///
				/// The closure may return any value not bounded by one of the special rentail lifetimes of the struct.
				pub fn rent_all<__F, __R>(#self_ref_param, f: __F) -> __R where
					__F: for<#(#struct_rlt_args,)*> __rental_prelude::FnOnce(#borrow_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>) -> __R,
					__R: #(#struct_lt_args +)*,
				{
					f(unsafe { #item_ident::all_erased(#self_arg) })
				}

				/// Return a shared reference from shared borrows of the fields of the struct.
				///
				/// This is a subtle variation of `rent_all` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
				pub fn ref_rent_all<__F, __R>(#self_ref_param, f: __F) -> &__R where
					__F: for<#(#struct_rlt_args,)*> __rental_prelude::FnOnce(#borrow_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>) -> &#last_rlt_arg __R,
					__R: ?Sized //#(#struct_lt_args +)*,
				{
					f(unsafe { #item_ident::all_erased(#self_arg) })
				}

				/// Optionally return a shared reference from shared borrows of the fields of the struct.
				///
				/// This is a subtle variation of `rent_all` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
				pub fn maybe_ref_rent_all<__F, __R>(#self_ref_param, f: __F) -> __rental_prelude::Option<&__R> where
					__F: for<#(#struct_rlt_args,)*> __rental_prelude::FnOnce(#borrow_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>) -> __rental_prelude::Option<&#last_rlt_arg __R>,
					__R: ?Sized //#(#struct_lt_args +)*,
				{
					f(unsafe { #item_ident::all_erased(#self_arg) })
				}

				/// Try to return a shared reference from shared borrows of the fields of the struct, or an error on failure.
				///
				/// This is a subtle variation of `rent_all` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
				pub fn try_ref_rent_all<__F, __R, __E>(#self_ref_param, f: __F) -> __rental_prelude::Result<&__R, __E> where
					__F: for<#(#struct_rlt_args,)*> __rental_prelude::FnOnce(#borrow_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>) -> __rental_prelude::Result<&#last_rlt_arg __R, __E>,
					__R: ?Sized //#(#struct_lt_args +)*,
				{
					f(unsafe { #item_ident::all_erased(#self_arg) })
				}

				/// Execute a closure on shared borrows of the prefix fields and a mutable borrow of the suffix field of the struct.
				///
				/// The closure may return any value not bounded by one of the special rentail lifetimes of the struct.
				pub fn rent_all_mut<__F, __R>(#self_mut_param, f: __F) -> __R where
					__F: for<#(#struct_rlt_args,)*> __rental_prelude::FnOnce(#borrow_mut_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>) -> __R,
					__R: #(#struct_lt_args +)*,
				{
					f(unsafe { #item_ident::all_mut_erased(#self_arg) })
				}

				/// Return a mutable reference from shared borrows of the prefix fields and a mutable borrow of the suffix field of the struct.
				///
				/// This is a subtle variation of `rent_all_mut` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
				pub fn ref_rent_all_mut<__F, __R>(#self_mut_param, f: __F) -> &mut __R where
					__F: for<#(#struct_rlt_args,)*> __rental_prelude::FnOnce(#borrow_mut_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>) -> &#last_rlt_arg mut __R,
					__R: ?Sized //#(#struct_lt_args +)*,
				{
					f(unsafe { #item_ident::all_mut_erased(#self_arg) })
				}

				/// Optionally return a mutable reference from shared borrows of the prefix fields and a mutable borrow of the suffix field of the struct.
				///
				/// This is a subtle variation of `rent_all_mut` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
				pub fn maybe_ref_rent_all_mut<__F, __R>(#self_mut_param, f: __F) -> __rental_prelude::Option<&mut __R> where
					__F: for<#(#struct_rlt_args,)*> __rental_prelude::FnOnce(#borrow_mut_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>) -> __rental_prelude::Option<&#last_rlt_arg mut __R>,
					__R: ?Sized //#(#struct_lt_args +)*,
				{
					f(unsafe { #item_ident::all_mut_erased(#self_arg) })
				}

				/// Try to return a mutable reference from shared borrows of the prefix fields and a mutable borrow of the suffix field of the struct, or an error on failure.
				///
				/// This is a subtle variation of `rent_all_mut` where it is legal to return a reference bounded by a rental lifetime, because that lifetime is reborrowed away before it is returned to you.
				pub fn try_ref_rent_all_mut<__F, __R, __E>(#self_mut_param, f: __F) -> __rental_prelude::Result<&mut __R, __E> where
					__F: for<#(#struct_rlt_args,)*> __rental_prelude::FnOnce(#borrow_mut_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>) -> __rental_prelude::Result<&#last_rlt_arg mut __R, __E>,
					__R: ?Sized //#(#struct_lt_args +)*,
				{
					f(unsafe { #item_ident::all_mut_erased(#self_arg) })
				}
			}
		).to_tokens(tokens);
	}

	if attribs.is_debug {
		if attribs.is_rental_mut {
			quote_spanned!(struct_info.ident.span()/*.resolved_at(def_site)*/ =>
				impl #struct_impl_params __rental_prelude::fmt::Debug for #item_ident #struct_impl_args #struct_where_clause #where_extra #suffix_ty: __rental_prelude::fmt::Debug {
					fn fmt(&self, f: &mut __rental_prelude::fmt::Formatter) -> __rental_prelude::fmt::Result {
						f.debug_struct(#item_ident_str)
							.field(#suffix_ident_str, &self.#suffix_ident)
							.finish()
					}
				}
			).to_tokens(tokens);
		} else {
			quote_spanned!(struct_info.ident.span()/*.resolved_at(def_site)*/ =>
				impl #struct_impl_params __rental_prelude::fmt::Debug for #item_ident #struct_impl_args #struct_where_clause #where_extra #(#field_tys: __rental_prelude::fmt::Debug),* {
					fn fmt(&self, f: &mut __rental_prelude::fmt::Formatter) -> __rental_prelude::fmt::Result {
						f.debug_struct(#item_ident_str)
							#(.field(#field_ident_strs, &self.#field_idents))*
							.finish()
					}
				}
			).to_tokens(tokens);
		}
	}

	if attribs.is_clone {
		quote_spanned!(struct_info.ident.span()/*.resolved_at(def_site)*/ =>
			impl #struct_impl_params __rental_prelude::Clone for #item_ident #struct_impl_args #struct_where_clause #where_extra #(#prefix_tys: #prefix_clone_traits,)* #suffix_ty: __rental_prelude::Clone {
				fn clone(&self) -> Self {
					#item_ident {
						#(#local_idents: __rental_prelude::Clone::clone(&self.#field_idents),)*
					}
				}
			}
		).to_tokens(tokens);
	}

//	if fields[fields.len() - 1].subrental.is_some() {
//		quote_spanned!(struct_span =>
//			impl<#(#borrow_lt_params,)* #(#struct_nonlt_params),*> __rental_prelude::IntoSuffix for #borrow_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*> #struct_where_clause {
//				type Suffix = <#borrow_suffix_ty as __rental_prelude::IntoSuffix>::Suffix;
//
//				#[allow(non_shorthand_field_patterns)]
//				fn into_suffix(self) -> <Self as __rental_prelude::IntoSuffix>::Suffix {
//					let #borrow_ident{#suffix_ident: suffix, ..};
//					suffix.into_suffix()
//				}
//			}
//		).to_tokens(tokens);
//
//		quote_spanned!(struct_span =>
//			impl<#(#borrow_lt_params,)* #(#struct_nonlt_params),*> __rental_prelude::IntoSuffix for #borrow_mut_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*> #struct_where_clause {
//				type Suffix = <#borrow_mut_suffix_ty as __rental_prelude::IntoSuffix>::Suffix;
//
//				#[allow(non_shorthand_field_patterns)]
//				fn into_suffix(self) -> <Self as __rental_prelude::IntoSuffix>::Suffix {
//					let #borrow_mut_ident{#suffix_ident: suffix, ..};
//					suffix.into_suffix()
//				}
//			}
//		).to_tokens(tokens);
//
//		if attribs.is_deref_suffix {
//			quote_spanned!(suffix_ty_span =>
//				impl #struct_impl_params __rental_prelude::Deref for #item_ident #struct_impl_args #struct_where_clause {
//					type Target = <#suffix_ty as __rental_prelude::Deref>::Target;
//
//					fn deref(&self) -> &<Self as __rental_prelude::Deref>::Target {
//						#item_ident::ref_rent(self, |suffix| &**__rental_prelude::IntoSuffix::into_suffix(suffix))
//					}
//				}
//			).to_tokens(tokens);
//		}
//
//		if attribs.is_deref_mut_suffix {
//			quote_spanned!(suffix_ty_span =>
//				impl #struct_impl_params __rental_prelude::DerefMut for #item_ident #struct_impl_args #struct_where_clause {
//					fn deref_mut(&mut self) -> &mut <Self as __rental_prelude::Deref>::Target {
//						#item_ident.ref_rent_mut(self, |suffix| &mut **__rental_prelude::IntoSuffix::into_suffix(suffix))
//					}
//				}
//			).to_tokens(tokens);
//		}
//	} else {
		quote_spanned!(struct_span =>
			impl<#(#borrow_lt_params,)* #(#struct_nonlt_params),*> __rental_prelude::IntoSuffix for #borrow_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*> #struct_where_clause {
				type Suffix = #borrow_suffix_ty;

				#[allow(non_shorthand_field_patterns)]
				fn into_suffix(self) -> <Self as __rental_prelude::IntoSuffix>::Suffix {
					let #borrow_ident{#suffix_ident: suffix, ..} = self;
					suffix
				}
			}
		).to_tokens(tokens);

		quote_spanned!(struct_span =>
			impl<#(#borrow_lt_params,)* #(#struct_nonlt_params),*> __rental_prelude::IntoSuffix for #borrow_mut_ident<#(#struct_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*> #struct_where_clause {
				type Suffix = #borrow_mut_suffix_ty;

				#[allow(non_shorthand_field_patterns)]
				fn into_suffix(self) -> <Self as __rental_prelude::IntoSuffix>::Suffix {
					let #borrow_mut_ident{#suffix_ident: suffix, ..} = self;
					suffix
				}
			}
		).to_tokens(tokens);

		if attribs.is_deref_suffix {
			quote_spanned!(suffix_ty_span =>
				impl #struct_impl_params __rental_prelude::Deref for #item_ident #struct_impl_args #struct_where_clause #where_extra {
					type Target = <#suffix_ty as __rental_prelude::Deref>::Target;

					fn deref(&self) -> &<Self as __rental_prelude::Deref>::Target {
						#item_ident::ref_rent(self, |suffix| &**suffix)
					}
				}
			).to_tokens(tokens);
		}

		if attribs.is_deref_mut_suffix {
			quote_spanned!(suffix_ty_span =>
				impl #struct_impl_params __rental_prelude::DerefMut for #item_ident #struct_impl_args #struct_where_clause {
					fn deref_mut(&mut self) -> &mut <Self as __rental_prelude::Deref>::Target {
						#item_ident::ref_rent_mut(self, |suffix| &mut **suffix)
					}
				}
			).to_tokens(tokens);
		}
//	}

	if attribs.is_deref_suffix {
		quote_spanned!(suffix_ty_span =>
			impl #struct_impl_params __rental_prelude::AsRef<<Self as __rental_prelude::Deref>::Target> for #item_ident #struct_impl_args #struct_where_clause {
				fn as_ref(&self) -> &<Self as __rental_prelude::Deref>::Target {
					&**self
				}
			}
		).to_tokens(tokens);
	}

	if attribs.is_deref_mut_suffix {
		quote_spanned!(suffix_ty_span =>
			impl #struct_impl_params __rental_prelude::AsMut<<Self as __rental_prelude::Deref>::Target> for #item_ident #struct_impl_args #struct_where_clause {
				fn as_mut(&mut self) -> &mut <Self as __rental_prelude::Deref>::Target {
					&mut **self
				}
			}
		).to_tokens(tokens);
	}

	if attribs.is_covariant {
		quote_spanned!(struct_info.ident.span()/*.resolved_at(def_site)*/ =>
			#[allow(dead_code)]
			impl #struct_impl_params #item_ident #struct_impl_args #struct_where_clause {
				/// Borrow all fields of the struct by reborrowing away the rental lifetimes.
				///
				/// This is safe because the lifetimes are verified to be covariant first.
				pub fn all<'__s>(#self_lt_ref_param) -> <Self as __rental_prelude::#rental_trait_ident>::Borrow {
					unsafe {
						let _covariant = __rental_prelude::PhantomData::<#borrow_ident<#(#static_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>>;
						let _covariant: __rental_prelude::PhantomData<#borrow_ident<#(#self_rlt_args,)* #(#struct_lt_args,)* #(#struct_nonlt_args),*>> = _covariant; 

						#item_ident::all_erased(#self_arg)
					}
				}

				/// Borrow the suffix field of the struct by reborrowing away the rental lifetimes.
				///
				/// This is safe because the lifetimes are verified to be covariant first.
				pub fn suffix(#self_ref_param) -> <<Self as __rental_prelude::#rental_trait_ident>::Borrow as __rental_prelude::IntoSuffix>::Suffix {
					&#self_arg.#suffix_ident
				}
			}
		).to_tokens(tokens);
	}

	if let Some(ref map_suffix_param) = attribs.map_suffix_param {
		let mut replacer = MapTyParamReplacer{
			ty_param: map_suffix_param,
		};

		let mapped_suffix_param = replacer.fold_type_param(map_suffix_param.clone());
		let mapped_ty = replacer.fold_path(parse_quote!(#item_ident #struct_impl_args));
		let mapped_suffix_ty = replacer.fold_type(suffix_orig_ty.clone());

		let mut map_where_clause = syn::WhereClause{
			where_token: Default::default(),
			predicates: syn::punctuated::Punctuated::from_iter(
				struct_generics.type_params().filter(|p| p.ident != map_suffix_param.ident).filter_map(|p| {
					let mut mapped_param = p.clone();
					mapped_param.bounds = syn::punctuated::Punctuated::new();

					for mapped_bound in p.bounds.iter().filter(|b| {
						let mut finder = MapTyParamFinder {
							ty_param: map_suffix_param,
							found: false,
						};
						finder.visit_type_param_bound(b);
						finder.found
					}).map(|b| {
						let mut replacer = MapTyParamReplacer{
							ty_param: map_suffix_param,
						};

						replacer.fold_type_param_bound(b.clone())
					}) {
						mapped_param.bounds.push(mapped_bound)
					}

					if mapped_param.bounds.len() > 0 {
						Some(syn::punctuated::Pair::Punctuated(parse_quote!(#mapped_param), Default::default()))
					} else {
						None
					}
				}).chain(
					struct_where_clause.iter().flat_map(|w| w.predicates.iter()).filter(|p| {
						let mut finder = MapTyParamFinder {
							ty_param: map_suffix_param,
							found: false,
						};
						finder.visit_where_predicate(p);
						finder.found
					}).map(|p| {
						let mut replacer = MapTyParamReplacer{
							ty_param: map_suffix_param,
						};

						syn::punctuated::Pair::Punctuated(replacer.fold_where_predicate(p.clone()), Default::default())
					})
				)
			)
		};

		let mut try_map_where_clause = map_where_clause.clone();
		map_where_clause.predicates.push(parse_quote!(
			__F: for<#(#suffix_rlt_args),*> __rental_prelude::FnOnce(#suffix_orig_ty) -> #mapped_suffix_ty
		));
		try_map_where_clause.predicates.push(parse_quote!(
			__F: for<#(#suffix_rlt_args),*> __rental_prelude::FnOnce(#suffix_orig_ty) -> __rental_prelude::Result<#mapped_suffix_ty, __E>
		));

		quote_spanned!(struct_span =>
			#[allow(dead_code)]
			impl #struct_impl_params #item_ident #struct_impl_args #struct_where_clause {
				/// Maps the suffix field of the rental struct to a different type.
				///
				/// Consumes the rental struct and applies the closure to the suffix field. A new rental struct is then constructed with the original prefix and new suffix.
				pub fn map<#mapped_suffix_param, __F>(#self_move_param, __f: __F) -> #mapped_ty #map_where_clause {
					let #item_ident{ #(#field_idents,)* } = #self_arg;

					let #suffix_ident = __f(#suffix_ident);

					#item_ident{ #(#field_idents: #local_idents,)* }
				}

				/// Try to map the suffix field of the rental struct to a different type.
				///
				/// As `map`, but the closure may fail. Upon failure, the tail is dropped, and the error is returned to you along with the head.
				pub fn try_map<#mapped_suffix_param, __F, __E>(#self_move_param, __f: __F) -> __rental_prelude::RentalResult<#mapped_ty, __E, #head_ty> #try_map_where_clause {
					let #item_ident{ #(#field_idents,)* } = #self_arg;

					match __f(#suffix_ident) {
						__rental_prelude::Result::Ok(#suffix_ident) => __rental_prelude::Result::Ok(#item_ident { #(#field_idents: #local_idents,)* }),
						__rental_prelude::Result::Err(__e) => __rental_prelude::Result::Err(__rental_prelude::RentalError(__e, #head_ident)),
					}
				}

				/// Try to map the suffix field of the rental struct to a different type.
				///
				/// As `map`, but the closure may fail. Upon failure, the struct is dropped and the error is returned.
				pub fn try_map_or_drop<#mapped_suffix_param, __F, __E>(#self_move_param, __f: __F) -> __rental_prelude::Result<#mapped_ty, __E> #try_map_where_clause {
					let #item_ident{ #(#field_idents,)* } = #self_arg;

					let #suffix_ident = __f(#suffix_ident)?;

					Ok(#item_ident{ #(#field_idents: #local_idents,)* })
				}
			}
		).to_tokens(tokens);
	}
}


fn get_struct_attribs(struct_info: &syn::ItemStruct) -> RentalStructAttribs
{
	let mut rattribs = struct_info.attrs.clone();

	let mut is_rental_mut = false;
	let mut is_debug = false;
	let mut is_clone = false;
	let mut is_deref_suffix = false;
	let mut is_deref_mut_suffix = false;
	let mut is_covariant = false;
	let mut map_suffix_param = None;

	if let Some(rental_pos) = rattribs.iter()/*.filter(|attr| !attr.is_sugared_doc)*/.position(|attr| match attr.interpret_meta().expect(&format!("Struct `{}` Attribute `{}` is not properly formatted.", struct_info.ident, attr.path.clone().into_token_stream())) {
		syn::Meta::Word(ref attr_ident) => {
			is_rental_mut = match attr_ident.to_string().as_str() {
				"rental" => false,
				"rental_mut" => true,
				_ => return false,
			};

			true
		},
		syn::Meta::List(ref list) => {
			is_rental_mut = match list.ident.to_string().as_str() {
				"rental" => false,
				"rental_mut" => true,
				_ => return false,
			};

			let mut leftover = list.nested.iter().filter(|nested| {
				if let syn::NestedMeta::Meta(ref meta) = **nested {
					match *meta {
						syn::Meta::Word(ref ident) => {
							match ident.to_string().as_str() {
								"debug" => {
									is_debug = true;
									false
								},
								"clone" => {
									is_clone = true;
									false
								},
								"deref_suffix" => {
									is_deref_suffix = true;
									false
								},
								"deref_mut_suffix" => {
									is_deref_suffix = true;
									is_deref_mut_suffix = true;
									false
								},
								"covariant" => {
									is_covariant = true;
									false
								},
								"map_suffix" => {
									panic!("Struct `{}` `map_suffix` flag expects ` = \"T\"`.", struct_info.ident);
								},
								_ => true,
							}
						},
						syn::Meta::NameValue(ref name_value) => {
							match name_value.ident.to_string().as_str() {
								"map_suffix" => {
									if let syn::Lit::Str(ref ty_param_str) = name_value.lit {
										let ty_param_str = ty_param_str.value();
										let ty_param = struct_info.generics.type_params().find(|ty_param| {
											if ty_param.ident.to_string() == ty_param_str {
												return true;
											}

											false
										}).unwrap_or_else(|| {
											panic!("Struct `{}` `map_suffix` param `{}` does not name a type parameter.", struct_info.ident, ty_param_str);
										});

										let mut finder = MapTyParamFinder{
											ty_param: &ty_param,
											found: false,
										};

										if struct_info.fields.iter().take(struct_info.fields.iter().count() - 1).any(|f| {
											finder.found = false;
											finder.visit_field(f);
											finder.found
										}) {
											panic!("Struct `{}` `map_suffix` type param `{}` appears in a prefix field.", struct_info.ident, ty_param_str);
										}

										finder.found = false;
										finder.visit_field(struct_info.fields.iter().last().unwrap());
										if !finder.found {
											panic!("Struct `{}` `map_suffix` type param `{}` does not appear in the suffix field.", struct_info.ident, ty_param_str);
										}

										map_suffix_param = Some(ty_param.clone());
										false
									} else {
										panic!("Struct `{}` `map_suffix` flag expects ` = \"T\"`.", struct_info.ident);
									}
								},
								_ => true,
							}
						},
						_ => true,
					}
				} else {
					true
				}
			}).count();

			if leftover > 0 {
				panic!("Struct `{}` rental attribute takes optional arguments: `debug`, `clone`, `deref_suffix`, `deref_mut_suffix`, `covariant`, and `map_suffix = \"T\"`.", struct_info.ident);
			}

			true
		},
		_ => false,
	}) {
		rattribs.remove(rental_pos);
	} else {
		panic!("Struct `{}` must have a `rental` or `rental_mut` attribute.", struct_info.ident);
	}

	if rattribs.iter().any(|attr| attr.path != syn::parse_str::<syn::Path>("doc").unwrap()) {
		panic!("Struct `{}` must not have attributes other than one `rental` or `rental_mut`.", struct_info.ident);
	}

	if is_rental_mut && is_clone {
		panic!("Struct `{}` cannot be both `rental_mut` and `clone`.", struct_info.ident);
	}

	RentalStructAttribs{
		doc: rattribs,
		is_rental_mut: is_rental_mut,
		is_debug: is_debug,
		is_clone: is_clone,
		is_deref_suffix: is_deref_suffix,
		is_deref_mut_suffix: is_deref_mut_suffix,
		is_covariant: is_covariant,
		map_suffix_param: map_suffix_param,
	}
}


fn prepare_fields(struct_info: &syn::ItemStruct) -> (Vec<RentalField>, syn::token::Brace) {
	let def_site: Span = Span::call_site(); // FIXME: hygiene
	let call_site: Span = Span::call_site();

	let (fields, fields_brace) = match struct_info.fields {
		syn::Fields::Named(ref fields) => (&fields.named, fields.brace_token),
		syn::Fields::Unnamed(..) => panic!("Struct `{}` must not be a tuple struct.", struct_info.ident),
		_ => panic!("Struct `{}` must have at least 2 fields.", struct_info.ident),
	};

	if fields.len() < 2 {
		panic!("Struct `{}` must have at least 2 fields.", struct_info.ident);
	}

	let mut rfields = Vec::with_capacity(fields.len());
	for (field_idx, field) in fields.iter().enumerate() {
		if field.vis != syn::Visibility::Inherited {
			panic!(
				"Struct `{}` field `{}` must be private.",
				struct_info.ident,
				field.ident.as_ref().map(|ident| ident.to_string()).unwrap_or_else(|| field_idx.to_string())
			);
		}

		let mut rfattribs = field.attrs.clone();
		let mut subrental = None;
		let mut target_ty_hack = None;

		if let Some(sr_pos) = rfattribs.iter().position(|attr| match attr.interpret_meta() {
			Some(syn::Meta::List(ref list)) if list.ident == "subrental" => {
				panic!(
					"`subrental` attribute on struct `{}` field `{}` expects ` = arity`.",
					struct_info.ident,
					field.ident.as_ref().map(|ident| ident.to_string()).unwrap_or_else(|| field_idx.to_string())
				);
			},
			Some(syn::Meta::Word(ref word)) if word == "subrental" => {
				panic!(
					"`subrental` attribute on struct `{}` field `{}` expects ` = arity`.",
					struct_info.ident,
					field.ident.as_ref().map(|ident| ident.to_string()).unwrap_or_else(|| field_idx.to_string())
				);
			},
			Some(syn::Meta::NameValue(ref name_value)) if name_value.ident == "subrental" => {
				match name_value.lit {
					syn::Lit::Int(ref arity) => {
						subrental = Some(Subrental{
							arity: arity.value() as usize, 
							rental_trait_ident: syn::Ident::new(&format!("Rental{}", arity.value()), def_site),
						})
					},
					_ => panic!(
						"`subrental` attribute on struct `{}` field `{}` expects ` = arity`.",
						struct_info.ident,
						field.ident.as_ref().map(|ident| ident.to_string()).unwrap_or_else(|| field_idx.to_string())
					),
				}

				true
			},
			_ => false,
		}) {
			rfattribs.remove(sr_pos);
		}

		if subrental.is_some() && field_idx == fields.len() - 1 {
			panic!(
				"struct `{}` field `{}` cannot be a subrental because it is the suffix field.",
				struct_info.ident,
				field.ident.as_ref().map(|ident| ident.to_string()).unwrap_or_else(|| field_idx.to_string())
			);
		}

		if let Some(tth_pos) = rfattribs.iter().position(|a|
			match a.interpret_meta() {
				Some(syn::Meta::NameValue(syn::MetaNameValue{ref ident, lit: syn::Lit::Str(ref ty_str), ..})) if ident == "target_ty" => {
					if let Ok(ty) = syn::parse_str::<syn::Type>(&ty_str.value()) {
						target_ty_hack = Some(ty);

						true
					} else {
						panic!(
							"`target_ty` attribute on struct `{}` field `{}` has an invalid ty string.",
							struct_info.ident,
							field.ident.as_ref().map(|ident| ident.to_string()).unwrap_or_else(|| field_idx.to_string())
						);
					}
				},
				_ => false,
			}
		) {
			rfattribs.remove(tth_pos);
		}

		if field_idx == fields.len() - 1 && target_ty_hack.is_some() {
			panic!(
				"Struct `{}` field `{}` cannot have `target_ty` attribute because it is the suffix field.",
				struct_info.ident,
				field.ident.as_ref().map(|ident| ident.to_string()).unwrap_or_else(|| field_idx.to_string())
			);
		}

		let target_ty_hack = target_ty_hack.as_ref().map(|ty| (*ty).clone()).or_else(|| if field_idx < fields.len() - 1 {
			let guess = if let syn::Type::Path(ref ty_path) = field.ty {
				match ty_path.path.segments[ty_path.path.segments.len() - 1] {
					syn::PathSegment{ref ident, arguments: syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments{ref args, ..})} => {
						if let Some(&syn::GenericArgument::Type(ref ty)) = args.first().map(|p| *p.value()) {
							if ident == "Vec" {
								Some(syn::Type::Slice(syn::TypeSlice{bracket_token: Default::default(), elem: Box::new(ty.clone())}))
							} else {
								Some(ty.clone())
							}
						} else {
							None
						}
					},
					syn::PathSegment{ref ident, arguments: syn::PathArguments::None} => {
						if ident == "String" {
							Some(parse_quote!(str))
						} else {
							None
						}
					},
					_ => {
						None
					},
				}
			} else if let syn::Type::Reference(syn::TypeReference{elem: ref box_ty, ..}) = field.ty {
				Some((**box_ty).clone())
			} else {
				None
			};

			let guess = guess.or_else(|| if field_idx == 0 {
				let field_ty = &field.ty;
				Some(parse_quote!(<#field_ty as __rental_prelude::Deref>::Target))
			} else {
				None
			});

			if guess.is_none() {
				panic!("Struct `{}` field `{}` must be a type path with 1 type param, `String`, or a reference.", struct_info.ident, field.ident.as_ref().unwrap())
			}

			guess
		} else {
			None
		});

		if subrental.is_some() {
			if match target_ty_hack {
				Some(syn::Type::Path(ref ty_path)) => ty_path.qself.is_some(),
				Some(_) => true,
				_ => false,
			} {
				panic!(
					"Struct `{}` field `{}` must have an unqualified path for its `target_ty` to be a valid subrental.",
					struct_info.ident,
					field.ident.as_ref().map(|ident| ident.to_string()).unwrap_or_else(|| field_idx.to_string())
				);
			}
		}

		let target_ty_hack_erased = target_ty_hack.as_ref().map(|tth| {
			let mut eraser = RentalLifetimeEraser{
				fields: &rfields,
				used_rlt_args: &mut Vec::new(),
			};

			eraser.fold_type(tth.clone())
		});

		let mut self_rlt_args = Vec::new();
		if let Some(Subrental{arity: sr_arity, ..}) = subrental {
			let field_ident = field.ident.as_ref().unwrap();
			for sr_idx in 0 .. sr_arity {
				self_rlt_args.push(syn::Lifetime::new(&format!("'{}_{}", field_ident, sr_idx), call_site));
			}
		} else {
			let field_ident = field.ident.as_ref().unwrap();
			self_rlt_args.push(syn::Lifetime::new(&format!("'{}", field_ident), call_site));
		}

		let mut used_rlt_args = Vec::new();
		let rty = {
			let mut eraser = RentalLifetimeEraser{
				fields: &rfields,
				used_rlt_args: &mut used_rlt_args,
			};

			eraser.fold_type(field.ty.clone())
		};

		if rfattribs.iter().any(|attr| match attr.interpret_meta() { Some(syn::Meta::NameValue(syn::MetaNameValue{ref ident, ..})) if ident == "doc" => false, _ => true }) {
			panic!(
				"Struct `{}` field `{}` must not have attributes other than one `subrental` and `target_ty`.",
				struct_info.ident,
				field.ident.as_ref().map(|ident| ident.to_string()).unwrap_or_else(|| field_idx.to_string())
			);
		}

		rfields.push(RentalField{
			name: field.ident.as_ref().unwrap().clone(),
			orig_ty: field.ty.clone(),
			erased: syn::Field{
				colon_token: field.colon_token,
				ident: field.ident.clone(),
				vis: field.vis.clone(),
				attrs: rfattribs,
				ty: rty,
			},
			subrental: subrental,
			self_rlt_args: self_rlt_args,
			used_rlt_args: used_rlt_args,
			target_ty_hack: target_ty_hack,
			target_ty_hack_erased: target_ty_hack_erased,
		});
	}

	(rfields, fields_brace)
}


fn make_borrow_quotes(self_arg: &proc_macro2::TokenStream, fields: &[RentalField], is_rental_mut: bool) -> Vec<BorrowQuotes> {
	let call_site: Span = Span::call_site();

	(0 .. fields.len()).map(|idx| {
		let (field_ty, deref) = if idx == fields.len() - 1 {
			let orig_ty = &fields[idx].orig_ty;
			(
				quote!(#orig_ty),
				quote!()
			)
		} else {
			let orig_ty = &fields[idx].orig_ty;
			(
				quote!(<#orig_ty as __rental_prelude::Deref>::Target),
				quote!(*)
			)
		};

		let field_ty_hack = fields[idx].target_ty_hack.as_ref().unwrap_or(&fields[idx].orig_ty);
		let field_ty_hack_erased = fields[idx].target_ty_hack_erased.as_ref().unwrap_or(&fields[idx].erased.ty);

		if let Some(ref subrental) = fields[idx].subrental {
			let field_ident = &fields[idx].name;
			let rental_trait_ident = &subrental.rental_trait_ident;
			let field_rlt_args = &fields[idx].self_rlt_args;

			let (ref borrow_ty_hack, ref borrow_mut_ty_hack, ref field_args) = if let syn::Type::Path(syn::TypePath{ref qself, path: ref ty_path}) = *field_ty_hack {
				let seg_idx = ty_path.segments.len() - 1;
				let ty_name = &ty_path.segments[seg_idx].ident.to_string();

				let mut borrow_ty_path = ty_path.clone();
				borrow_ty_path.segments[seg_idx].ident = syn::Ident::new(&format!("{}_Borrow", ty_name), call_site);
				borrow_ty_path.segments[seg_idx].arguments = syn::PathArguments::None;

				let mut borrow_mut_ty_path = ty_path.clone();
				borrow_mut_ty_path.segments[seg_idx].ident = syn::Ident::new(&format!("{}_BorrowMut", ty_name), call_site);
				borrow_mut_ty_path.segments[seg_idx].arguments = syn::PathArguments::None;

				match ty_path.segments[seg_idx].arguments {
					syn::PathArguments::AngleBracketed(ref args) => {
						(
							syn::Type::Path(syn::TypePath{qself: qself.clone(), path: borrow_ty_path}),
							syn::Type::Path(syn::TypePath{qself: qself.clone(), path: borrow_mut_ty_path}),
							args.args.iter().collect::<Vec<_>>(),
						)
					},
					syn::PathArguments::None => {
						(
							syn::Type::Path(syn::TypePath{qself: qself.clone(), path: borrow_ty_path}),
							syn::Type::Path(syn::TypePath{qself: qself.clone(), path: borrow_mut_ty_path}),
							Vec::with_capacity(0),
						)
					},
					_ => panic!("Field `{}` must have angle-bracketed args.", fields[idx].name),
				}
			} else {
				panic!("Field `{}` must be a type path.", fields[idx].name)
			};

			BorrowQuotes {
				ty: if idx == fields.len() - 1 || !is_rental_mut {
					quote!(<#field_ty as __rental_prelude::#rental_trait_ident<#(#field_rlt_args),*>>::Borrow)
				} else {
					quote!(__rental_prelude::PhantomData<<#field_ty as __rental_prelude::#rental_trait_ident<#(#field_rlt_args),*>>::Borrow>)
				},
				ty_hack: if idx == fields.len() - 1 || !is_rental_mut {
					quote!(#borrow_ty_hack<#(#field_rlt_args,)* #(#field_args),*>)
				} else {
					quote!(__rental_prelude::PhantomData<#borrow_ty_hack<#(#field_rlt_args,)* #(#field_args),*>>)
				},
				expr: if idx == fields.len() - 1 || !is_rental_mut {
					quote!(unsafe { <#field_ty_hack_erased>::all_erased(&#deref #self_arg.#field_ident) })
				} else {
					quote!(__rental_prelude::PhantomData::<()>)
				},

				mut_ty: if idx == fields.len() - 1 {
					quote!(<#field_ty as __rental_prelude::#rental_trait_ident<#(#field_rlt_args),*>>::BorrowMut)
				} else if !is_rental_mut {
					quote!(<#field_ty as __rental_prelude::#rental_trait_ident<#(#field_rlt_args),*>>::Borrow)
				} else {
					quote!(__rental_prelude::PhantomData<<#field_ty as __rental_prelude::#rental_trait_ident<#(#field_rlt_args),*>>::BorrowMut>)
				},
				mut_ty_hack: if idx == fields.len() - 1 {
					quote!(#borrow_mut_ty_hack<#(#field_rlt_args,)* #(#field_args),*>)
				} else if !is_rental_mut {
					quote!(#borrow_ty_hack<#(#field_rlt_args,)* #(#field_args),*>)
				} else {
					quote!(__rental_prelude::PhantomData<#borrow_mut_ty_hack<#(#field_rlt_args,)* #(#field_args),*>>)
				},
				mut_expr: if idx == fields.len() - 1 {
					quote!(unsafe { <#field_ty_hack_erased>::all_mut_erased(&mut #deref #self_arg.#field_ident) })
				} else if !is_rental_mut {
					quote!(unsafe { <#field_ty_hack_erased>::all_erased(&#deref #self_arg.#field_ident) })
				} else {
					quote!(__rental_prelude::PhantomData::<()>)
				},

				new_ty: if !is_rental_mut  {
					//quote!(<#field_ty as __rental_prelude::#rental_trait_ident<#(#field_rlt_args),*>>::Borrow)
					quote!(#borrow_ty_hack<#(#field_rlt_args,)* #(#field_args),*>)
				} else {
					//quote!(<#field_ty as __rental_prelude::#rental_trait_ident<#(#field_rlt_args),*>>::BorrowMut)
					quote!(#borrow_mut_ty_hack<#(#field_rlt_args,)* #(#field_args),*>)
				},
				new_expr: if !is_rental_mut {
					quote!(unsafe { <#field_ty_hack_erased>::all_erased(&#deref #field_ident) })
				} else {
					quote!(unsafe { <#field_ty_hack_erased>::all_mut_erased(&mut #deref #field_ident) })
				},
			}
		} else {
			let field_ident = &fields[idx].name;
			let field_rlt_arg = &fields[idx].self_rlt_args[0];

			BorrowQuotes {
				ty: if idx == fields.len() - 1 || !is_rental_mut {
					quote!(&#field_rlt_arg (#field_ty))
				} else {
					quote!(__rental_prelude::PhantomData<&#field_rlt_arg #field_ty>)
				},
				ty_hack: if idx == fields.len() - 1 || !is_rental_mut {
					quote!(&#field_rlt_arg (#field_ty_hack))
				} else {
					quote!(__rental_prelude::PhantomData<&#field_rlt_arg #field_ty_hack>)
				},
				expr: if idx == fields.len() - 1 || !is_rental_mut {
					quote!(&#deref #self_arg.#field_ident)
				} else {
					quote!(__rental_prelude::PhantomData::<()>)
				},

				mut_ty: if idx == fields.len() - 1 {
					quote!(&#field_rlt_arg mut (#field_ty))
				} else if !is_rental_mut {
					quote!(&#field_rlt_arg (#field_ty))
				} else {
					quote!(__rental_prelude::PhantomData<&#field_rlt_arg mut #field_ty>)
				},
				mut_ty_hack: if idx == fields.len() - 1 {
					quote!(&#field_rlt_arg mut (#field_ty_hack))
				} else if !is_rental_mut {
					quote!(&#field_rlt_arg (#field_ty_hack))
				} else {
					quote!(__rental_prelude::PhantomData<&#field_rlt_arg mut #field_ty_hack>)
				},
				mut_expr: if idx == fields.len() - 1 {
					quote!(&mut #deref #self_arg.#field_ident)
				} else if !is_rental_mut {
					quote!(&#deref #self_arg.#field_ident)
				} else {
					quote!(__rental_prelude::PhantomData::<()>)
				},

				new_ty: if !is_rental_mut {
					//quote!(&#field_rlt_arg #field_ty)
					quote!(&#field_rlt_arg (#field_ty_hack))
				} else {
					//quote!(&#field_rlt_arg mut #field_ty)
					quote!(&#field_rlt_arg mut (#field_ty_hack))
				},
				new_expr: if !is_rental_mut {
					quote!(& #deref #field_ident)
				} else {
					quote!(&mut #deref #field_ident)
				},
			}
		}
	}).collect()
}


fn make_tail_closure_quotes(fields: &[RentalField], borrows: &[BorrowQuotes], is_rental_mut: bool) -> Vec<ClosureQuotes> {
	(1 .. fields.len()).map(|idx| {
		let local_name = &fields[idx].name;
		let field_ty = &fields[idx].orig_ty;

		if !is_rental_mut {
			let prev_new_tys_reverse = &borrows[0 .. idx].iter().map(|b| &b.new_ty).rev().collect::<Vec<_>>();
			let prev_new_exprs_reverse = &borrows[0 .. idx].iter().map(|b| &b.new_expr).rev().collect::<Vec<_>>();;
			let mut prev_rlt_args = Vec::<syn::Lifetime>::new();
			for prev_field in &fields[0 .. idx] {
				prev_rlt_args.extend(prev_field.self_rlt_args.iter().cloned());
			}
			let prev_rlt_args = &prev_rlt_args;

			ClosureQuotes {
				bound: quote!(for<#(#prev_rlt_args),*> __rental_prelude::FnOnce(#(#prev_new_tys_reverse),*) -> #field_ty),
				expr: quote!(#local_name(#(#prev_new_exprs_reverse),*)),
				try_bound: quote!(for<#(#prev_rlt_args),*> __rental_prelude::FnOnce(#(#prev_new_tys_reverse),*) -> __rental_prelude::Result<#field_ty, __E>),
				try_expr: quote!(#local_name(#(#prev_new_exprs_reverse),*)),
			}
		} else {
			let prev_new_ty = &borrows[idx - 1].new_ty;
			let prev_new_expr = &borrows[idx - 1].new_expr;
			let prev_rlt_args = &fields[idx - 1].self_rlt_args.iter().chain(&fields[idx - 1].used_rlt_args).collect::<Vec<_>>();

			ClosureQuotes {
				bound: quote!(for<#(#prev_rlt_args),*> __rental_prelude::FnOnce(#prev_new_ty) -> #field_ty),
				expr: quote!(#local_name(#prev_new_expr)),
				try_bound: quote!(for<#(#prev_rlt_args),*> __rental_prelude::FnOnce(#prev_new_ty) -> __rental_prelude::Result<#field_ty, __E>),
				try_expr: quote!(#local_name(#prev_new_expr)),
			}
		}
	}).collect()
}


struct RentalStructAttribs {
	pub doc: Vec<syn::Attribute>,
	pub is_rental_mut: bool,
	pub is_debug: bool,
	pub is_clone: bool,
	pub is_deref_suffix: bool,
	pub is_deref_mut_suffix: bool,
	pub is_covariant: bool,
	pub map_suffix_param: Option<syn::TypeParam>,
}


struct RentalField {
	pub name: syn::Ident,
	pub orig_ty: syn::Type,
	pub erased: syn::Field,
	pub subrental: Option<Subrental>,
	pub self_rlt_args: Vec<syn::Lifetime>,
	pub used_rlt_args: Vec<syn::Lifetime>,
	pub target_ty_hack: Option<syn::Type>,
	pub target_ty_hack_erased: Option<syn::Type>,
}


struct Subrental {
	arity: usize,
	rental_trait_ident: syn::Ident,
}


struct BorrowQuotes {
	pub ty: proc_macro2::TokenStream,
	pub ty_hack: proc_macro2::TokenStream,
	pub expr: proc_macro2::TokenStream,
	pub mut_ty: proc_macro2::TokenStream,
	pub mut_ty_hack: proc_macro2::TokenStream,
	pub mut_expr: proc_macro2::TokenStream,
	pub new_ty: proc_macro2::TokenStream,
	pub new_expr: proc_macro2::TokenStream,
}


struct ClosureQuotes {
	pub bound: proc_macro2::TokenStream,
	pub expr: proc_macro2::TokenStream,
	pub try_bound: proc_macro2::TokenStream,
	pub try_expr: proc_macro2::TokenStream,
}


struct RentalLifetimeEraser<'a> {
	pub fields: &'a [RentalField],
	pub used_rlt_args: &'a mut Vec<syn::Lifetime>,
}


impl<'a> syn::fold::Fold for RentalLifetimeEraser<'a> {
	fn fold_lifetime(&mut self, lifetime: syn::Lifetime) -> syn::Lifetime {
		let def_site: Span = Span::call_site(); // FIXME: hygiene

		if self.fields.iter().any(|field| field.self_rlt_args.contains(&lifetime)) {
			if !self.used_rlt_args.contains(&lifetime) {
				self.used_rlt_args.push(lifetime.clone());
			}

			syn::Lifetime::new("'static", def_site)
		} else {
			lifetime
		}
	}
}


struct MapTyParamFinder<'p> {
	pub ty_param: &'p syn::TypeParam,
	pub found: bool,
}


impl<'p, 'ast> syn::visit::Visit<'ast> for MapTyParamFinder<'p> {
	fn visit_path(&mut self, path: &'ast syn::Path) {
		if path.leading_colon.is_none() && path.segments.len() == 1 && path.segments[0].ident == self.ty_param.ident && path.segments[0].arguments == syn::PathArguments::None {
			self.found = true;
		} else {
			for seg in &path.segments {
				self.visit_path_segment(seg)
			};
		}
	}
}


struct MapTyParamReplacer<'p> {
	pub ty_param: &'p syn::TypeParam,
}


impl<'p> syn::fold::Fold for MapTyParamReplacer<'p> {
	fn fold_path(&mut self, path: syn::Path) -> syn::Path {
		if path.leading_colon.is_none() && path.segments.len() == 1 && path.segments[0].ident == self.ty_param.ident && path.segments[0].arguments == syn::PathArguments::None {
			let def_site: Span = Span::call_site(); // FIXME: hygiene

			syn::Path{
				leading_colon: None,
				segments: syn::punctuated::Punctuated::from_iter(Some(
					syn::punctuated::Pair::End(syn::PathSegment{ident: syn::Ident::new("__U", def_site), arguments: syn::PathArguments::None})
				)),
			}
		} else {
			let syn::Path{
				leading_colon,
				segments,
			} = path;

			syn::Path{
				leading_colon: leading_colon,
				segments: syn::punctuated::Punctuated::from_iter(segments.into_pairs().map(|p| match p {
					syn::punctuated::Pair::Punctuated(seg, punc) => syn::punctuated::Pair::Punctuated(self.fold_path_segment(seg), punc),
					syn::punctuated::Pair::End(seg) => syn::punctuated::Pair::End(self.fold_path_segment(seg)),
				}))
			}
		}
	}


	fn fold_type_param(&mut self, mut ty_param: syn::TypeParam) -> syn::TypeParam {
		if ty_param.ident == self.ty_param.ident {
			let def_site: Span = Span::call_site(); // FIXME: hygiene
			ty_param.ident = syn::Ident::new("__U", def_site);
		}

		let bounds = syn::punctuated::Punctuated::from_iter(ty_param.bounds.iter().map(|b| self.fold_type_param_bound(b.clone())));
		ty_param.bounds = bounds;

		ty_param
	}
}