@@ -211,36 +211,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
211211 ..
212212 } ) => {
213213 self . with_new_scopes ( * fn_sig_span, |this| {
214- assert ! ( this. contract. is_none( ) ) ;
215- if let Some ( contract) = contract {
216- let requires = contract. requires . clone ( ) ;
217- let ensures = contract. ensures . clone ( ) ;
218- let ensures = ensures. map ( |ens| {
219- // FIXME: this needs to be a fresh (or illegal) identifier to prevent
220- // accidental capture of a parameter or global variable.
221- let checker_ident: Ident =
222- Ident :: from_str_and_span ( "__ensures_checker" , ens. span ) ;
223- let ( checker_pat, checker_hir_id) = this. pat_ident_binding_mode_mut (
224- ens. span ,
225- checker_ident,
226- hir:: BindingMode :: NONE ,
227- ) ;
228-
229- crate :: FnContractLoweringEnsures {
230- expr : ens,
231- fresh_ident : ( checker_ident, checker_pat, checker_hir_id) ,
232- }
233- } ) ;
234-
235- // Note: `with_new_scopes` will reinstall the outer
236- // item's contract (if any) after its callback finishes.
237- this. contract . replace ( crate :: FnContractLoweringInfo {
238- span,
239- requires,
240- ensures,
241- } ) ;
242- }
243-
244214 // Note: we don't need to change the return type from `T` to
245215 // `impl Future<Output = T>` here because lower_body
246216 // only cares about the input argument patterns in the function
@@ -254,6 +224,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
254224 coroutine_kind,
255225 body. as_deref ( ) ,
256226 attrs,
227+ contract. as_deref ( ) ,
257228 ) ;
258229
259230 let itctx = ImplTraitContext :: Universal ;
@@ -803,6 +774,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
803774 ( generics, kind, expr. is_some ( ) )
804775 }
805776 AssocItemKind :: Fn ( box Fn { sig, generics, body : None , .. } ) => {
777+ // FIXME(contracts): Deny contract here since it won't apply to
778+ // any impl method or callees.
806779 let names = self . lower_fn_params_to_names ( & sig. decl ) ;
807780 let ( generics, sig) = self . lower_method_sig (
808781 generics,
@@ -814,7 +787,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
814787 ) ;
815788 ( generics, hir:: TraitItemKind :: Fn ( sig, hir:: TraitFn :: Required ( names) ) , false )
816789 }
817- AssocItemKind :: Fn ( box Fn { sig, generics, body : Some ( body) , .. } ) => {
790+ AssocItemKind :: Fn ( box Fn { sig, generics, body : Some ( body) , contract , .. } ) => {
818791 let body_id = self . lower_maybe_coroutine_body (
819792 sig. span ,
820793 i. span ,
@@ -823,6 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
823796 sig. header . coroutine_kind ,
824797 Some ( body) ,
825798 attrs,
799+ contract. as_deref ( ) ,
826800 ) ;
827801 let ( generics, sig) = self . lower_method_sig (
828802 generics,
@@ -931,7 +905,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
931905 hir:: ImplItemKind :: Const ( ty, body)
932906 } ,
933907 ) ,
934- AssocItemKind :: Fn ( box Fn { sig, generics, body, .. } ) => {
908+ AssocItemKind :: Fn ( box Fn { sig, generics, body, contract , .. } ) => {
935909 let body_id = self . lower_maybe_coroutine_body (
936910 sig. span ,
937911 i. span ,
@@ -940,6 +914,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
940914 sig. header . coroutine_kind ,
941915 body. as_deref ( ) ,
942916 attrs,
917+ contract. as_deref ( ) ,
943918 ) ;
944919 let ( generics, sig) = self . lower_method_sig (
945920 generics,
@@ -1088,72 +1063,89 @@ impl<'hir> LoweringContext<'_, 'hir> {
10881063 pub ( super ) fn lower_fn_body (
10891064 & mut self ,
10901065 decl : & FnDecl ,
1066+ contract : Option < & FnContract > ,
10911067 body : impl FnOnce ( & mut Self ) -> hir:: Expr < ' hir > ,
10921068 ) -> hir:: BodyId {
10931069 self . lower_body ( |this| {
10941070 let params =
10951071 this. arena . alloc_from_iter ( decl. inputs . iter ( ) . map ( |x| this. lower_param ( x) ) ) ;
1096- let result = body ( this) ;
1097-
1098- let opt_contract = this. contract . take ( ) ;
10991072
1073+ // Optionally lower the fn contract, which turns:
1074+ //
11001075 // { body }
1101- // ==>
1102- // { contract_requires(PRECOND); { body } }
1103- let Some ( contract) = opt_contract else { return ( params, result) } ;
1104- let result_ref = this. arena . alloc ( result) ;
1105- let lit_unit = |this : & mut LoweringContext < ' _ , ' hir > | {
1106- this. expr ( contract. span , hir:: ExprKind :: Tup ( & [ ] ) )
1107- } ;
1108-
1109- let precond: hir:: Stmt < ' hir > = if let Some ( req) = contract. requires {
1110- let lowered_req = this. lower_expr_mut ( & req) ;
1111- let precond = this. expr_call_lang_item_fn_mut (
1112- req. span ,
1113- hir:: LangItem :: ContractCheckRequires ,
1114- & * arena_vec ! [ this; lowered_req] ,
1115- ) ;
1116- this. stmt_expr ( req. span , precond)
1117- } else {
1118- let u = lit_unit ( this) ;
1119- this. stmt_expr ( contract. span , u)
1120- } ;
1121-
1122- let ( postcond_checker, result) = if let Some ( ens) = contract. ensures {
1123- let crate :: FnContractLoweringEnsures { expr : ens, fresh_ident } = ens;
1124- let lowered_ens: hir:: Expr < ' hir > = this. lower_expr_mut ( & ens) ;
1125- let postcond_checker = this. expr_call_lang_item_fn (
1126- ens. span ,
1127- hir:: LangItem :: ContractBuildCheckEnsures ,
1128- & * arena_vec ! [ this; lowered_ens] ,
1129- ) ;
1130- let checker_binding_pat = fresh_ident. 1 ;
1131- (
1132- this. stmt_let_pat (
1076+ //
1077+ // into:
1078+ //
1079+ // { contract_requires(PRECOND); let __postcond = |ret_val| POSTCOND; postcond({ body }) }
1080+ if let Some ( contract) = contract {
1081+ let precond = if let Some ( req) = & contract. requires {
1082+ // Lower the precondition check intrinsic.
1083+ let lowered_req = this. lower_expr_mut ( & req) ;
1084+ let precond = this. expr_call_lang_item_fn_mut (
1085+ req. span ,
1086+ hir:: LangItem :: ContractCheckRequires ,
1087+ & * arena_vec ! [ this; lowered_req] ,
1088+ ) ;
1089+ Some ( this. stmt_expr ( req. span , precond) )
1090+ } else {
1091+ None
1092+ } ;
1093+ let ( postcond, body) = if let Some ( ens) = & contract. ensures {
1094+ let ens_span = this. lower_span ( ens. span ) ;
1095+ // Set up the postcondition `let` statement.
1096+ let check_ident: Ident =
1097+ Ident :: from_str_and_span ( "__ensures_checker" , ens_span) ;
1098+ let ( checker_pat, check_hir_id) = this. pat_ident_binding_mode_mut (
1099+ ens_span,
1100+ check_ident,
1101+ hir:: BindingMode :: NONE ,
1102+ ) ;
1103+ let lowered_ens = this. lower_expr_mut ( & ens) ;
1104+ let postcond_checker = this. expr_call_lang_item_fn (
1105+ ens_span,
1106+ hir:: LangItem :: ContractBuildCheckEnsures ,
1107+ & * arena_vec ! [ this; lowered_ens] ,
1108+ ) ;
1109+ let postcond = this. stmt_let_pat (
11331110 None ,
1134- ens . span ,
1111+ ens_span ,
11351112 Some ( postcond_checker) ,
1136- this. arena . alloc ( checker_binding_pat ) ,
1113+ this. arena . alloc ( checker_pat ) ,
11371114 hir:: LocalSource :: Contract ,
1138- ) ,
1139- this. inject_ensures_check ( result_ref, ens. span , fresh_ident. 0 , fresh_ident. 2 ) ,
1140- )
1141- } else {
1142- let u = lit_unit ( this) ;
1143- ( this. stmt_expr ( contract. span , u) , & * result_ref)
1144- } ;
1115+ ) ;
11451116
1146- let block = this. block_all (
1147- contract. span ,
1148- arena_vec ! [ this; precond, postcond_checker] ,
1149- Some ( result) ,
1150- ) ;
1151- ( params, this. expr_block ( block) )
1117+ // Install contract_ensures so we will intercept `return` statements,
1118+ // then lower the body.
1119+ this. contract_ensures = Some ( ( ens_span, check_ident, check_hir_id) ) ;
1120+ let body = this. arena . alloc ( body ( this) ) ;
1121+
1122+ // Finally, inject an ensures check on the implicit return of the body.
1123+ let body = this. inject_ensures_check ( body, ens_span, check_ident, check_hir_id) ;
1124+ ( Some ( postcond) , body)
1125+ } else {
1126+ let body = & * this. arena . alloc ( body ( this) ) ;
1127+ ( None , body)
1128+ } ;
1129+ // Flatten the body into precond, then postcond, then wrapped body.
1130+ let wrapped_body = this. block_all (
1131+ body. span ,
1132+ this. arena . alloc_from_iter ( [ precond, postcond] . into_iter ( ) . flatten ( ) ) ,
1133+ Some ( body) ,
1134+ ) ;
1135+ ( params, this. expr_block ( wrapped_body) )
1136+ } else {
1137+ ( params, body ( this) )
1138+ }
11521139 } )
11531140 }
11541141
1155- fn lower_fn_body_block ( & mut self , decl : & FnDecl , body : & Block ) -> hir:: BodyId {
1156- self . lower_fn_body ( decl, |this| this. lower_block_expr ( body) )
1142+ fn lower_fn_body_block (
1143+ & mut self ,
1144+ decl : & FnDecl ,
1145+ body : & Block ,
1146+ contract : Option < & FnContract > ,
1147+ ) -> hir:: BodyId {
1148+ self . lower_fn_body ( decl, contract, |this| this. lower_block_expr ( body) )
11571149 }
11581150
11591151 pub ( super ) fn lower_const_body ( & mut self , span : Span , expr : Option < & Expr > ) -> hir:: BodyId {
@@ -1179,12 +1171,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
11791171 coroutine_kind : Option < CoroutineKind > ,
11801172 body : Option < & Block > ,
11811173 attrs : & ' hir [ hir:: Attribute ] ,
1174+ contract : Option < & FnContract > ,
11821175 ) -> hir:: BodyId {
11831176 let Some ( body) = body else {
11841177 // Functions without a body are an error, except if this is an intrinsic. For those we
11851178 // create a fake body so that the entire rest of the compiler doesn't have to deal with
11861179 // this as a special case.
1187- return self . lower_fn_body ( decl, |this| {
1180+ return self . lower_fn_body ( decl, contract , |this| {
11881181 if attrs. iter ( ) . any ( |a| a. name_or_empty ( ) == sym:: rustc_intrinsic) {
11891182 let span = this. lower_span ( span) ;
11901183 let empty_block = hir:: Block {
@@ -1209,8 +1202,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
12091202 } ;
12101203 let Some ( coroutine_kind) = coroutine_kind else {
12111204 // Typical case: not a coroutine.
1212- return self . lower_fn_body_block ( decl, body) ;
1205+ return self . lower_fn_body_block ( decl, body, contract ) ;
12131206 } ;
1207+ // FIXME(contracts): Support contracts on async fn.
12141208 self . lower_body ( |this| {
12151209 let ( parameters, expr) = this. lower_coroutine_body_with_moved_arguments (
12161210 decl,
0 commit comments