contracts: Use block instead of closure for original function body

I don't know why Contracts uses a closure, code block works just fine
while avoiding bugs. I may find out the hard way later.
This commit is contained in:
Mikolaj Wielgus 2023-09-15 00:13:39 +02:00
parent 35484972ef
commit 82081ef170
1 changed files with 2 additions and 174 deletions

View File

@ -4,10 +4,7 @@
use proc_macro2::{Ident, Span, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
use quote::ToTokens; use quote::ToTokens;
use syn::{ use syn::{spanned::Spanned, visit_mut as visitor, Attribute, Expr, ExprCall};
spanned::Spanned, visit_mut as visitor, Attribute, Expr, ExprCall,
ReturnType,
};
use crate::implementation::{ use crate::implementation::{
Contract, ContractMode, ContractType, FuncWithContracts, Contract, ContractMode, ContractType, FuncWithContracts,
@ -319,11 +316,9 @@ pub(crate) fn generate(
// wrap the function body in a closure if we have any postconditions // wrap the function body in a closure if we have any postconditions
// //
let body = if post.is_empty() { let body = {
let block = &func.function.block; let block = &func.function.block;
quote::quote! { let ret = #block; } quote::quote! { let ret = #block; }
} else {
create_body_closure(&func.function)
}; };
// //
@ -368,170 +363,3 @@ impl syn::visit_mut::VisitMut for SelfReplacer<'_> {
} }
} }
} }
fn ty_contains_impl_trait(ty: &syn::Type) -> bool {
use syn::visit::Visit;
struct TyContainsImplTrait {
seen_impl_trait: bool,
}
impl syn::visit::Visit<'_> for TyContainsImplTrait {
fn visit_type_impl_trait(&mut self, _: &syn::TypeImplTrait) {
self.seen_impl_trait = true;
}
}
let mut vis = TyContainsImplTrait {
seen_impl_trait: false,
};
vis.visit_type(ty);
vis.seen_impl_trait
}
fn create_body_closure(func: &syn::ItemFn) -> TokenStream {
let is_method = func.sig.receiver().is_some();
// If the function has a receiver (e.g. `self`, `&mut self`, or `self: Box<Self>`) rename it
// to `this` within the closure
let mut block = func.block.clone();
let mut closure_args = vec![];
let mut arg_names = vec![];
if is_method {
// `mixed_site` makes the identifier hygienic so it won't collide with a local variable
// named `this`.
let this_ident = syn::Ident::new("this", Span::mixed_site());
let mut receiver = func.sig.inputs[0].clone();
match receiver {
// self, &self, &mut self
syn::FnArg::Receiver(rcv) => {
// The `Self` type.
let self_ty = Box::new(syn::Type::Path(syn::TypePath {
qself: None,
path: syn::Path::from(syn::Ident::new("Self", rcv.span())),
}));
let ty = if let Some((and_token, ref lifetime)) = rcv.reference
{
Box::new(syn::Type::Reference(syn::TypeReference {
and_token,
lifetime: lifetime.clone(),
mutability: rcv.mutability,
elem: self_ty,
}))
} else {
self_ty
};
let pat_mut = if rcv.reference.is_none() {
rcv.mutability
} else {
None
};
// this: [& [mut]] Self
let new_rcv = syn::PatType {
attrs: rcv.attrs.clone(),
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
attrs: vec![],
by_ref: None,
mutability: pat_mut,
ident: this_ident.clone(),
subpat: None,
})),
colon_token: syn::Token![:](rcv.span()),
ty,
};
receiver = syn::FnArg::Typed(new_rcv);
}
// self: Box<Self>
syn::FnArg::Typed(ref mut pat) => {
if let syn::Pat::Ident(ref mut ident) = *pat.pat {
if ident.ident == "self" {
ident.ident = this_ident.clone();
}
}
}
}
closure_args.push(receiver);
match &func.sig.inputs[0] {
syn::FnArg::Receiver(receiver) => {
arg_names
.push(syn::Ident::new("self", receiver.self_token.span()));
}
syn::FnArg::Typed(pat) => {
if let syn::Pat::Ident(ident) = &*pat.pat {
arg_names.push(ident.ident.clone());
} else {
// Non-trivial receiver pattern => do not capture
closure_args.pop();
}
}
};
// Replace any references to `self` in the function body with references to `this`.
syn::visit_mut::visit_block_mut(
&mut SelfReplacer {
replace_with: &this_ident,
},
&mut block,
);
}
// Any function arguments of the form `ident: ty` become closure arguments and get passed
// explicitly. More complex ones, e.g. pattern matching like `(a, b): (u32, u32)`, are
// captured instead.
let args = func.sig.inputs.iter().skip(if is_method { 1 } else { 0 });
for arg in args {
match arg {
syn::FnArg::Receiver(_) => unreachable!("Multiple receivers?"),
syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => {
if !ty_contains_impl_trait(ty) {
if let syn::Pat::Ident(ident) = &**pat {
let ident_str = ident.ident.to_string();
// Any function argument identifier starting with '_' signals
// that the binding will not be used.
if !ident_str.starts_with('_')
|| ident_str.starts_with("__")
{
arg_names.push(ident.ident.clone());
closure_args.push(arg.clone());
}
}
}
}
}
}
let ret_ty = match &func.sig.output {
ReturnType::Type(_, ty) => {
let span = ty.span();
match ty.as_ref() {
syn::Type::ImplTrait(_) => quote::quote! {},
ty => quote::quote_spanned! { span=>
-> #ty
},
}
}
ReturnType::Default => quote::quote! {},
};
let closure_args = closure_args.iter();
let arg_names = arg_names.iter();
quote::quote! {
#[allow(unused_mut)]
let mut run = |#(#closure_args),*| #ret_ty #block;
let ret = run(#(#arg_names),*);
}
}