mirror of https://codeberg.org/topola/topola.git
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:
parent
35484972ef
commit
82081ef170
|
|
@ -4,10 +4,7 @@
|
|||
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::ToTokens;
|
||||
use syn::{
|
||||
spanned::Spanned, visit_mut as visitor, Attribute, Expr, ExprCall,
|
||||
ReturnType,
|
||||
};
|
||||
use syn::{spanned::Spanned, visit_mut as visitor, Attribute, Expr, ExprCall};
|
||||
|
||||
use crate::implementation::{
|
||||
Contract, ContractMode, ContractType, FuncWithContracts,
|
||||
|
|
@ -319,11 +316,9 @@ pub(crate) fn generate(
|
|||
// 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;
|
||||
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),
|
||||
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),*);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue