1
+ use thin_vec:: thin_vec;
2
+
1
3
use crate :: LoweringContext ;
2
4
3
5
impl < ' a , ' hir > LoweringContext < ' a , ' hir > {
6
+ /// Lowered contracts are guarded with the `contract_checks` compiler flag,
7
+ /// i.e. the flag turns into a boolean guard in the lowered HIR. The reason
8
+ /// for not eliminating the contract code entirely when the `contract_checks`
9
+ /// flag is disabled is so that contracts can be type checked, even when
10
+ /// they are disabled, which avoids them becoming stale (i.e. out of sync
11
+ /// with the codebase) over time.
12
+ ///
13
+ /// The optimiser should be able to eliminate all contract code guarded
14
+ /// by `if false`, leaving the original body intact when runtime contract
15
+ /// checks are disabled.
4
16
pub ( super ) fn lower_contract (
5
17
& mut self ,
6
18
body : impl FnOnce ( & mut Self ) -> rustc_hir:: Expr < ' hir > ,
@@ -14,14 +26,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
14
26
//
15
27
// into:
16
28
//
29
+ // let __postcond = if contract_checks {
30
+ // contract_check_requires(PRECOND);
31
+ // Some(|ret_val| POSTCOND)
32
+ // } else {
33
+ // None
34
+ // };
17
35
// {
18
- // let __postcond = if contracts_checks() {
19
- // contract_check_requires(PRECOND);
20
- // Some(|ret_val| POSTCOND)
21
- // } else {
22
- // None
23
- // };
24
- // contract_check_ensures(__postcond, { body })
36
+ // let ret = { body };
37
+ //
38
+ // if contract_checks {
39
+ // contract_check_ensures(__postcond, ret)
40
+ // } else {
41
+ // ret
42
+ // }
25
43
// }
26
44
27
45
let precond = self . lower_precond ( req) ;
@@ -41,13 +59,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
41
59
//
42
60
// into:
43
61
//
62
+ // let __postcond = if contract_checks {
63
+ // Some(|ret_val| POSTCOND)
64
+ // } else {
65
+ // None
66
+ // };
44
67
// {
45
- // let __postcond = if contracts_check() {
46
- // Some(|ret_val| POSTCOND)
47
- // } else {
48
- // None
49
- // };
50
- // __postcond({ body })
68
+ // let ret = { body };
69
+ //
70
+ // if contract_checks {
71
+ // contract_check_ensures(__postcond, ret)
72
+ // } else {
73
+ // ret
74
+ // }
51
75
// }
52
76
53
77
let postcond_checker = self . lower_postcond_checker ( ens) ;
@@ -66,7 +90,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
66
90
// into:
67
91
//
68
92
// {
69
- // if contracts_check() {
93
+ // if contracts_checks {
70
94
// contract_requires(PRECOND);
71
95
// }
72
96
// body
@@ -129,11 +153,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
129
153
let then_block = self . arena . alloc ( self . expr_block ( & then_block_stmts) ) ;
130
154
131
155
let precond_check = rustc_hir:: ExprKind :: If (
132
- self . expr_call_lang_item_fn (
133
- precond. span ,
134
- rustc_hir:: LangItem :: ContractChecks ,
135
- Default :: default ( ) ,
136
- ) ,
156
+ self . arena . alloc ( self . expr_bool_literal ( precond. span , self . tcx . sess . contract_checks ( ) ) ) ,
137
157
then_block,
138
158
None ,
139
159
) ;
@@ -170,11 +190,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
170
190
let else_block = self . arena . alloc ( self . expr_block ( else_block) ) ;
171
191
172
192
let contract_check = rustc_hir:: ExprKind :: If (
173
- self . expr_call_lang_item_fn (
174
- span,
175
- rustc_hir:: LangItem :: ContractChecks ,
176
- Default :: default ( ) ,
177
- ) ,
193
+ self . arena . alloc ( self . expr_bool_literal ( span, self . tcx . sess . contract_checks ( ) ) ) ,
178
194
then_block,
179
195
Some ( else_block) ,
180
196
) ;
@@ -249,12 +265,60 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
249
265
cond_ident : rustc_span:: Ident ,
250
266
cond_hir_id : rustc_hir:: HirId ,
251
267
) -> & ' hir rustc_hir:: Expr < ' hir > {
268
+ // {
269
+ // let ret = { body };
270
+ //
271
+ // if contract_checks {
272
+ // contract_check_ensures(__postcond, ret)
273
+ // } else {
274
+ // ret
275
+ // }
276
+ // }
277
+ let ret_ident: rustc_span:: Ident = rustc_span:: Ident :: from_str_and_span ( "__ret" , span) ;
278
+
279
+ // Set up the return `let` statement.
280
+ let ( ret_pat, ret_hir_id) =
281
+ self . pat_ident_binding_mode_mut ( span, ret_ident, rustc_hir:: BindingMode :: NONE ) ;
282
+
283
+ let ret_stmt = self . stmt_let_pat (
284
+ None ,
285
+ span,
286
+ Some ( expr) ,
287
+ self . arena . alloc ( ret_pat) ,
288
+ rustc_hir:: LocalSource :: Contract ,
289
+ ) ;
290
+
291
+ let ret = self . expr_ident ( span, ret_ident, ret_hir_id) ;
292
+
252
293
let cond_fn = self . expr_ident ( span, cond_ident, cond_hir_id) ;
253
- let call_expr = self . expr_call_lang_item_fn_mut (
294
+ let contract_check = self . expr_call_lang_item_fn_mut (
254
295
span,
255
296
rustc_hir:: LangItem :: ContractCheckEnsures ,
256
- arena_vec ! [ self ; * cond_fn, * expr ] ,
297
+ arena_vec ! [ self ; * cond_fn, * ret ] ,
257
298
) ;
258
- self . arena . alloc ( call_expr)
299
+ let contract_check = self . arena . alloc ( contract_check) ;
300
+ let call_expr = self . block_expr_block ( contract_check) ;
301
+
302
+ // same ident can't be used in 2 places, so we create a new one for the
303
+ // else branch
304
+ let ret = self . expr_ident ( span, ret_ident, ret_hir_id) ;
305
+ let ret_block = self . block_expr_block ( ret) ;
306
+
307
+ let contracts_enabled: rustc_hir:: Expr < ' _ > =
308
+ self . expr_bool_literal ( span, self . tcx . sess . contract_checks ( ) ) ;
309
+ let contract_check = self . arena . alloc ( self . expr (
310
+ span,
311
+ rustc_hir:: ExprKind :: If (
312
+ self . arena . alloc ( contracts_enabled) ,
313
+ call_expr,
314
+ Some ( ret_block) ,
315
+ ) ,
316
+ ) ) ;
317
+
318
+ let attrs: rustc_ast:: AttrVec = thin_vec ! [ self . unreachable_code_attr( span) ] ;
319
+ self . lower_attrs ( contract_check. hir_id , & attrs, span, rustc_hir:: Target :: Expression ) ;
320
+
321
+ let ret_block = self . block_all ( span, arena_vec ! [ self ; ret_stmt] , Some ( contract_check) ) ;
322
+ self . arena . alloc ( self . expr_block ( self . arena . alloc ( ret_block) ) )
259
323
}
260
324
}
0 commit comments