@@ -7,10 +7,12 @@ use proc_macro2::TokenTree;
77use quote:: ToTokens ;
88use quote:: format_ident;
99use quote:: { quote, quote_spanned} ;
10+ use syn:: token:: Comma ;
1011use verus_syn:: BroadcastUse ;
1112use verus_syn:: DefaultEnsures ;
1213use verus_syn:: ExprBlock ;
1314use verus_syn:: ExprForLoop ;
15+ use verus_syn:: Generics ;
1416use verus_syn:: parse:: { Parse , ParseStream } ;
1517use verus_syn:: parse_quote_spanned;
1618use verus_syn:: punctuated:: Punctuated ;
@@ -508,6 +510,11 @@ impl Visitor {
508510 ret_pat : Option < ( Pat , TType ) > ,
509511 final_ret_pat : Option < Pat > , // Some(pat) if different from ret_pat,
510512 _span : Span ,
513+ is_impl_fn : bool , // is the function a ImplItemFn or TraitImplFn
514+ is_closure : bool , // some closures also use this function to handle
515+ ident : impl ToTokens , // function name.
516+ generics : Option < impl ToTokens > ,
517+ inputs : ( Option < impl ToTokens > , impl ToTokens ) , // optional self and args
511518 ) -> Vec < Stmt > {
512519 let requires = self . take_ghost ( & mut spec. requires ) ;
513520 let recommends = self . take_ghost ( & mut spec. recommends ) ;
@@ -518,6 +525,8 @@ impl Visitor {
518525 let opens_invariants = self . take_ghost ( & mut spec. invariants ) ;
519526 let unwind = self . take_ghost ( & mut spec. unwind ) ;
520527
528+ let ( self_token_op, args) = inputs;
529+
521530 let ensures = merge_default_ensures ( ensures, default_ensures) ;
522531
523532 let mut spec_stmts = Vec :: new ( ) ;
@@ -611,12 +620,46 @@ impl Visitor {
611620 )
612621 }
613622 }
614- spec_stmts. push ( Stmt :: Expr (
615- Expr :: Verbatim (
616- quote_spanned_builtin ! ( verus_builtin, token. span => #verus_builtin:: ensures( |#p: #ty| [ #exprs] ) ) ,
617- ) ,
618- Some ( Semi { spans : [ token. span ] } ) ,
619- ) ) ;
623+ if is_closure {
624+ // closures cannot return impl xxx so it's safe to
625+ spec_stmts. push ( Stmt :: Expr (
626+ Expr :: Verbatim (
627+ quote_spanned_builtin ! ( verus_builtin, token. span => #verus_builtin:: ensures( |#p: #ty| [ #exprs] ) ) ,
628+ ) ,
629+ Some ( Semi { spans : [ token. span ] } ) ,
630+ ) ) ;
631+ } else {
632+ let constrain_type = {
633+ let generics_token = {
634+ match generics {
635+ Some ( generics) => {
636+ Some ( quote_spanned ! ( token. span => :: #generics) )
637+ }
638+ None => None ,
639+ }
640+ } ;
641+ let receiver_token = {
642+ match ( is_impl_fn, self_token_op) {
643+ ( true , None ) => Some ( quote_spanned ! ( token. span => Self :: ) ) ,
644+ ( true , Some ( self_token) ) => {
645+ Some ( quote_spanned ! ( token. span => #self_token. ) )
646+ }
647+ ( false , None ) => None ,
648+ ( false , Some ( self_token) ) => {
649+ Some ( quote_spanned ! ( token. span => #self_token. ) )
650+ }
651+ }
652+ } ;
653+ quote_spanned_builtin ! ( verus_builtin, token. span => #verus_builtin:: constrain_type( #p, #receiver_token#ident#generics_token( #args) ) )
654+ } ;
655+ let contrain_typ_expr = Expr :: Verbatim ( constrain_type) ;
656+ spec_stmts. push ( Stmt :: Expr (
657+ Expr :: Verbatim (
658+ quote_spanned_builtin ! ( verus_builtin, token. span => #verus_builtin:: ensures( |#p| [ #contrain_typ_expr, #exprs] ) ) ,
659+ ) ,
660+ Some ( Semi { spans : [ token. span ] } ) ,
661+ ) ) ;
662+ }
620663 } else {
621664 spec_stmts. push ( Stmt :: Expr (
622665 Expr :: Verbatim (
@@ -746,6 +789,7 @@ impl Visitor {
746789 sig : & mut Signature ,
747790 semi_token : Option < Token ! [ ; ] > ,
748791 is_trait : bool ,
792+ is_impl_fn : bool ,
749793 ) -> Vec < Stmt > {
750794 let mut stmts: Vec < Stmt > = Vec :: new ( ) ;
751795 let mut unwrap_ghost_tracked: Vec < Stmt > = Vec :: new ( ) ;
@@ -931,7 +975,24 @@ impl Visitor {
931975 self . inside_ghost += 1 ; // for requires, ensures, etc.
932976
933977 let sig_span = sig. span ( ) . clone ( ) ;
934- let spec_stmts = self . take_sig_specs ( & mut sig. spec , ret_pat, None , sig_span) ;
978+
979+ if let Some ( ( p, _) ) = & ret_pat {
980+ if let Some ( err_stmt) = check_verus_return_ident ( p, & sig. inputs ) {
981+ stmts. push ( err_stmt) ;
982+ }
983+ }
984+
985+ let spec_stmts = self . take_sig_specs (
986+ & mut sig. spec ,
987+ ret_pat,
988+ None ,
989+ sig_span,
990+ is_impl_fn,
991+ false ,
992+ sig. ident . clone ( ) ,
993+ verus_generic_to_tokens ( & sig. generics ) ,
994+ verus_inputs_to_tokens ( & sig. inputs ) ,
995+ ) ;
935996 if !self . erase_ghost . erase ( ) {
936997 stmts. extend ( spec_stmts) ;
937998 }
@@ -1624,6 +1685,7 @@ impl Visitor {
16241685 & mut item_fn. sig ,
16251686 Some ( semi) ,
16261687 false ,
1688+ false ,
16271689 ) ;
16281690
16291691 if self . rustdoc && matches ! ( vstd_kind( ) , VstdKind :: IsVstd ) {
@@ -3795,8 +3857,14 @@ impl VisitMut for Visitor {
37953857 if self . rustdoc {
37963858 crate :: rustdoc:: process_item_fn ( fun) ;
37973859 }
3798- let stmts =
3799- self . visit_fn ( & mut fun. attrs , Some ( & fun. vis ) , & mut fun. sig , fun. semi_token , false ) ;
3860+ let stmts = self . visit_fn (
3861+ & mut fun. attrs ,
3862+ Some ( & fun. vis ) ,
3863+ & mut fun. sig ,
3864+ fun. semi_token ,
3865+ false ,
3866+ false ,
3867+ ) ;
38003868 fun. block . stmts . splice ( 0 ..0 , stmts) ;
38013869 fun. semi_token = None ;
38023870 let is_external_code = has_external_code ( & fun. attrs ) ;
@@ -3820,6 +3888,7 @@ impl VisitMut for Visitor {
38203888 & mut method. sig ,
38213889 method. semi_token ,
38223890 false ,
3891+ true ,
38233892 ) ;
38243893 method. block . stmts . splice ( 0 ..0 , stmts) ;
38253894 method. semi_token = None ;
@@ -3836,7 +3905,7 @@ impl VisitMut for Visitor {
38363905 fn visit_trait_item_fn_mut ( & mut self , method : & mut TraitItemFn ) {
38373906 let is_spec_method = method. sig . ident . to_string ( ) . starts_with ( VERUS_SPEC ) ;
38383907 let mut stmts =
3839- self . visit_fn ( & mut method. attrs , None , & mut method. sig , method. semi_token , true ) ;
3908+ self . visit_fn ( & mut method. attrs , None , & mut method. sig , method. semi_token , true , true ) ;
38403909 if let Some ( block) = & mut method. default {
38413910 block. stmts . splice ( 0 ..0 , stmts) ;
38423911 } else if self . erase_ghost . keep ( ) && is_spec_method {
@@ -4658,10 +4727,115 @@ fn take_sig_with_spec(
46584727 } ;
46594728 spec_stmts
46604729}
4730+
4731+ pub ( crate ) fn verus_inputs_to_tokens (
4732+ inputs : & Punctuated < FnArg , Token ! [ , ] > ,
4733+ ) -> ( Option < TokenStream > , TokenStream ) {
4734+ let mut arg_tokens = TokenStream :: new ( ) ;
4735+ let mut args: Punctuated < verus_syn:: Expr , Comma > = Punctuated :: new ( ) ;
4736+ let mut self_token = None ;
4737+ for input in inputs. iter ( ) {
4738+ match ( & input. tracked , & input. kind ) {
4739+ ( _, FnArgKind :: Receiver ( receiver) ) => {
4740+ self_token = Some ( receiver. self_token . clone ( ) . to_token_stream ( ) ) ;
4741+ }
4742+ ( _, FnArgKind :: Typed ( pat_type) ) => match & * pat_type. pat {
4743+ Pat :: Ident ( pat_ident) => {
4744+ args. push ( Expr :: Verbatim ( pat_ident. ident . to_token_stream ( ) ) ) ;
4745+ }
4746+ _ => {
4747+ args. push ( Expr :: Verbatim ( quote_spanned ! ( input. span( ) =>
4748+ compile_error!( "verus! macro error: input of the function is not an Ident" ) ) ) ) ;
4749+ }
4750+ } ,
4751+ }
4752+ }
4753+ args. to_tokens ( & mut arg_tokens) ;
4754+ ( self_token, arg_tokens)
4755+ }
4756+
4757+ pub ( crate ) fn inputs_to_tokens (
4758+ inputs : & syn:: punctuated:: Punctuated < syn:: FnArg , syn:: Token ![ , ] > ,
4759+ ) -> ( Option < TokenStream > , TokenStream ) {
4760+ let mut ret = TokenStream :: new ( ) ;
4761+ let mut args: Punctuated < verus_syn:: Expr , Comma > = Punctuated :: new ( ) ;
4762+ let mut self_token = None ;
4763+ for input in inputs. iter ( ) {
4764+ match input {
4765+ syn:: FnArg :: Receiver ( receiver) => {
4766+ self_token = Some ( receiver. self_token . clone ( ) . to_token_stream ( ) ) ;
4767+ }
4768+ syn:: FnArg :: Typed ( pat_type) => match & * pat_type. pat {
4769+ syn:: Pat :: Ident ( pat_ident) => {
4770+ args. push ( Expr :: Verbatim ( pat_ident. ident . to_token_stream ( ) ) ) ;
4771+ }
4772+ _ => {
4773+ args. push ( Expr :: Verbatim ( quote_spanned ! ( input. span( ) =>
4774+ compile_error!( "verus! macro error: input of the function is not an Ident" ) ) ) ) ;
4775+ }
4776+ } ,
4777+ }
4778+ }
4779+ args. to_tokens ( & mut ret) ;
4780+ ( self_token, ret)
4781+ }
4782+
4783+ pub ( crate ) fn verus_generic_to_tokens ( generic : & Generics ) -> Option < TokenStream > {
4784+ if generic. lt_token . is_none ( ) {
4785+ return None ;
4786+ }
4787+ let mut ret = TokenStream :: new ( ) ;
4788+ generic. lt_token . to_tokens ( & mut ret) ;
4789+ let mut params: Punctuated < verus_syn:: GenericArgument , Comma > = Punctuated :: new ( ) ;
4790+ for gen_arg in generic. params . iter ( ) {
4791+ match gen_arg {
4792+ verus_syn:: GenericParam :: Lifetime ( _) => { }
4793+ verus_syn:: GenericParam :: Type ( type_param) => {
4794+ params. push ( verus_syn:: GenericArgument :: Type ( Type :: Verbatim (
4795+ type_param. ident . to_token_stream ( ) ,
4796+ ) ) ) ;
4797+ }
4798+ verus_syn:: GenericParam :: Const ( const_param) => {
4799+ params. push ( verus_syn:: GenericArgument :: Const ( Expr :: Verbatim (
4800+ const_param. ident . to_token_stream ( ) ,
4801+ ) ) ) ;
4802+ }
4803+ }
4804+ }
4805+ params. to_tokens ( & mut ret) ;
4806+ generic. gt_token . to_tokens ( & mut ret) ;
4807+ Some ( ret)
4808+ }
4809+
4810+ pub ( crate ) fn generic_to_tokens ( generic : & syn:: Generics ) -> Option < TokenStream > {
4811+ if generic. lt_token . is_none ( ) {
4812+ return None ;
4813+ }
4814+ let mut ret = TokenStream :: new ( ) ;
4815+ generic. lt_token . to_tokens ( & mut ret) ;
4816+ let mut params: Punctuated < Ident , Comma > = Punctuated :: new ( ) ;
4817+ for gen_args in generic. params . iter ( ) {
4818+ match gen_args {
4819+ syn:: GenericParam :: Lifetime ( _) => { }
4820+ syn:: GenericParam :: Type ( type_param) => {
4821+ params. push ( type_param. ident . clone ( ) ) ;
4822+ }
4823+ syn:: GenericParam :: Const ( const_param) => {
4824+ params. push ( const_param. ident . clone ( ) ) ;
4825+ }
4826+ }
4827+ }
4828+ params. to_tokens ( & mut ret) ;
4829+ generic. gt_token . to_tokens ( & mut ret) ;
4830+ Some ( ret)
4831+ }
4832+
46614833pub ( crate ) fn sig_specs_attr (
46624834 erase_ghost : EraseGhost ,
46634835 spec_attr : SignatureSpecAttr ,
46644836 sig : & mut syn:: Signature ,
4837+ is_impl_fn : bool ,
4838+ is_closure : bool ,
46654839) -> Vec < Stmt > {
46664840 let SignatureSpecAttr { ret_pat, mut spec } = spec_attr;
46674841 let mut spec_stmts = vec ! [ ] ;
@@ -4697,8 +4871,25 @@ pub(crate) fn sig_specs_attr(
46974871 assign_to : false ,
46984872 rustdoc : env_rustdoc ( ) ,
46994873 } ;
4874+
4875+ if let Some ( ( p, _) ) = & ret_pat {
4876+ if let Some ( err_stmt) = check_return_ident ( p, & sig. inputs ) {
4877+ spec_stmts. push ( err_stmt) ;
4878+ }
4879+ }
4880+
47004881 let sig_span = sig. span ( ) . clone ( ) ;
4701- spec_stmts. extend ( visitor. take_sig_specs ( & mut spec, ret_pat, final_ret_pat, sig_span) ) ;
4882+ spec_stmts. extend ( visitor. take_sig_specs (
4883+ & mut spec,
4884+ ret_pat,
4885+ final_ret_pat,
4886+ sig_span,
4887+ is_impl_fn,
4888+ is_closure,
4889+ sig. ident . clone ( ) ,
4890+ generic_to_tokens ( & sig. generics ) ,
4891+ inputs_to_tokens ( & sig. inputs ) ,
4892+ ) ) ;
47024893 spec_stmts
47034894}
47044895
@@ -5227,3 +5418,43 @@ fn get_ex_ident_mangle_path(qself: &Option<verus_syn::QSelf>, path: &Path) -> Id
52275418
52285419 return Ident :: new ( & s, path. span ( ) ) ;
52295420}
5421+
5422+ /// In VIR there's the same check, but Rustc will complain first, and throw out
5423+ /// some errors about "constrain_type", which ar confusing and the users should not see.
5424+ /// Instead we give an early error with nice error msg here.
5425+ fn check_return_ident (
5426+ ret_pat : & Pat ,
5427+ input_args : & syn:: punctuated:: Punctuated < syn:: FnArg , Comma > ,
5428+ ) -> Option < Stmt > {
5429+ for input in input_args {
5430+ if let syn:: FnArg :: Typed ( pt) = & input {
5431+ if pt. pat . to_token_stream ( ) . to_string ( ) == ret_pat. to_token_stream ( ) . to_string ( ) {
5432+ return Some ( stmt_with_semi ! (
5433+ input. span( ) =>
5434+ compile_error!( "parameter name cannot be the same as the return value name" )
5435+ ) ) ;
5436+ }
5437+ }
5438+ }
5439+ None
5440+ }
5441+
5442+ /// In VIR there's the same check, but Rustc will complain first, and throw out
5443+ /// some errors about "constrain_type", which ar confusing and the users should not see.
5444+ /// Instead we give an early error with nice error msg here.
5445+ fn check_verus_return_ident (
5446+ ret_pat : & Pat ,
5447+ input_args : & Punctuated < FnArg , verus_syn:: token:: Comma > ,
5448+ ) -> Option < Stmt > {
5449+ for input in input_args {
5450+ if let FnArgKind :: Typed ( pt) = & input. kind {
5451+ if pt. pat . to_token_stream ( ) . to_string ( ) == ret_pat. to_token_stream ( ) . to_string ( ) {
5452+ return Some ( stmt_with_semi ! (
5453+ input. span( ) =>
5454+ compile_error!( "parameter name cannot be the same as the return value name" )
5455+ ) ) ;
5456+ }
5457+ }
5458+ }
5459+ None
5460+ }
0 commit comments