@@ -211,36 +211,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
211
211
..
212
212
} ) => {
213
213
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
-
244
214
// Note: we don't need to change the return type from `T` to
245
215
// `impl Future<Output = T>` here because lower_body
246
216
// only cares about the input argument patterns in the function
@@ -254,6 +224,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
254
224
coroutine_kind,
255
225
body. as_deref ( ) ,
256
226
attrs,
227
+ contract. as_deref ( ) ,
257
228
) ;
258
229
259
230
let itctx = ImplTraitContext :: Universal ;
@@ -803,6 +774,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
803
774
( generics, kind, expr. is_some ( ) )
804
775
}
805
776
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.
806
779
let names = self . lower_fn_params_to_names ( & sig. decl ) ;
807
780
let ( generics, sig) = self . lower_method_sig (
808
781
generics,
@@ -814,7 +787,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
814
787
) ;
815
788
( generics, hir:: TraitItemKind :: Fn ( sig, hir:: TraitFn :: Required ( names) ) , false )
816
789
}
817
- AssocItemKind :: Fn ( box Fn { sig, generics, body : Some ( body) , .. } ) => {
790
+ AssocItemKind :: Fn ( box Fn { sig, generics, body : Some ( body) , contract , .. } ) => {
818
791
let body_id = self . lower_maybe_coroutine_body (
819
792
sig. span ,
820
793
i. span ,
@@ -823,6 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
823
796
sig. header . coroutine_kind ,
824
797
Some ( body) ,
825
798
attrs,
799
+ contract. as_deref ( ) ,
826
800
) ;
827
801
let ( generics, sig) = self . lower_method_sig (
828
802
generics,
@@ -931,7 +905,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
931
905
hir:: ImplItemKind :: Const ( ty, body)
932
906
} ,
933
907
) ,
934
- AssocItemKind :: Fn ( box Fn { sig, generics, body, .. } ) => {
908
+ AssocItemKind :: Fn ( box Fn { sig, generics, body, contract , .. } ) => {
935
909
let body_id = self . lower_maybe_coroutine_body (
936
910
sig. span ,
937
911
i. span ,
@@ -940,6 +914,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
940
914
sig. header . coroutine_kind ,
941
915
body. as_deref ( ) ,
942
916
attrs,
917
+ contract. as_deref ( ) ,
943
918
) ;
944
919
let ( generics, sig) = self . lower_method_sig (
945
920
generics,
@@ -1088,72 +1063,89 @@ impl<'hir> LoweringContext<'_, 'hir> {
1088
1063
pub ( super ) fn lower_fn_body (
1089
1064
& mut self ,
1090
1065
decl : & FnDecl ,
1066
+ contract : Option < & FnContract > ,
1091
1067
body : impl FnOnce ( & mut Self ) -> hir:: Expr < ' hir > ,
1092
1068
) -> hir:: BodyId {
1093
1069
self . lower_body ( |this| {
1094
1070
let params =
1095
1071
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 ( ) ;
1099
1072
1073
+ // Optionally lower the fn contract, which turns:
1074
+ //
1100
1075
// { 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 (
1133
1110
None ,
1134
- ens . span ,
1111
+ ens_span ,
1135
1112
Some ( postcond_checker) ,
1136
- this. arena . alloc ( checker_binding_pat ) ,
1113
+ this. arena . alloc ( checker_pat ) ,
1137
1114
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
+ ) ;
1145
1116
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
+ }
1152
1139
} )
1153
1140
}
1154
1141
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) )
1157
1149
}
1158
1150
1159
1151
pub ( super ) fn lower_const_body ( & mut self , span : Span , expr : Option < & Expr > ) -> hir:: BodyId {
@@ -1179,12 +1171,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
1179
1171
coroutine_kind : Option < CoroutineKind > ,
1180
1172
body : Option < & Block > ,
1181
1173
attrs : & ' hir [ hir:: Attribute ] ,
1174
+ contract : Option < & FnContract > ,
1182
1175
) -> hir:: BodyId {
1183
1176
let Some ( body) = body else {
1184
1177
// Functions without a body are an error, except if this is an intrinsic. For those we
1185
1178
// create a fake body so that the entire rest of the compiler doesn't have to deal with
1186
1179
// this as a special case.
1187
- return self . lower_fn_body ( decl, |this| {
1180
+ return self . lower_fn_body ( decl, contract , |this| {
1188
1181
if attrs. iter ( ) . any ( |a| a. name_or_empty ( ) == sym:: rustc_intrinsic) {
1189
1182
let span = this. lower_span ( span) ;
1190
1183
let empty_block = hir:: Block {
@@ -1209,8 +1202,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
1209
1202
} ;
1210
1203
let Some ( coroutine_kind) = coroutine_kind else {
1211
1204
// Typical case: not a coroutine.
1212
- return self . lower_fn_body_block ( decl, body) ;
1205
+ return self . lower_fn_body_block ( decl, body, contract ) ;
1213
1206
} ;
1207
+ // FIXME(contracts): Support contracts on async fn.
1214
1208
self . lower_body ( |this| {
1215
1209
let ( parameters, expr) = this. lower_coroutine_body_with_moved_arguments (
1216
1210
decl,
0 commit comments