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 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),
|
|
||||||
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