blob: 4b5f5b5607eae1e55779f150455662c9e33612a1 [file] [log] [blame] [edit]
#![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 rental 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 rental 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 rental 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 rental 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
}
}