@@ -16,6 +16,7 @@ use rustc_errors::struct_span_err;
16
16
use rustc_hir as hir;
17
17
use rustc_hir:: def_id:: DefId ;
18
18
use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
19
+ use rustc_mir:: const_eval:: is_min_const_fn;
19
20
use rustc_span:: { sym, Span , Symbol } ;
20
21
use syntax:: ast:: Mutability ;
21
22
@@ -27,6 +28,8 @@ enum NonConstExpr {
27
28
Loop ( hir:: LoopSource ) ,
28
29
Match ( hir:: MatchSource ) ,
29
30
OrPattern ,
31
+ LogicalOr ,
32
+ LogicalAnd ,
30
33
}
31
34
32
35
impl NonConstExpr {
@@ -35,6 +38,8 @@ impl NonConstExpr {
35
38
Self :: Loop ( src) => format ! ( "`{}`" , src. name( ) ) ,
36
39
Self :: Match ( src) => format ! ( "`{}`" , src. name( ) ) ,
37
40
Self :: OrPattern => format ! ( "or-pattern" ) ,
41
+ Self :: LogicalOr => format ! ( "`||`" ) ,
42
+ Self :: LogicalAnd => format ! ( "`&&`" ) ,
38
43
}
39
44
}
40
45
@@ -46,6 +51,8 @@ impl NonConstExpr {
46
51
Self :: Match ( Normal )
47
52
| Self :: Match ( IfDesugar { .. } )
48
53
| Self :: Match ( IfLetDesugar { .. } )
54
+ | Self :: LogicalOr
55
+ | Self :: LogicalAnd
49
56
| Self :: OrPattern => & [ sym:: const_if_match] ,
50
57
51
58
Self :: Loop ( Loop ) => & [ sym:: const_loop] ,
@@ -64,26 +71,34 @@ impl NonConstExpr {
64
71
}
65
72
}
66
73
67
- #[ derive( Copy , Clone ) ]
74
+ #[ derive( Copy , Clone , PartialEq , Eq ) ]
68
75
enum ConstKind {
69
76
Static ,
70
77
StaticMut ,
71
78
ConstFn ,
79
+ MinConstFn ,
72
80
Const ,
73
81
AnonConst ,
74
82
}
75
83
76
84
impl ConstKind {
77
- fn for_body ( body : & hir:: Body < ' _ > , hir_map : Hir < ' _ > ) -> Option < Self > {
78
- let is_const_fn = |id| hir_map. fn_sig_by_hir_id ( id) . unwrap ( ) . header . is_const ( ) ;
79
-
80
- let owner = hir_map. body_owner ( body. id ( ) ) ;
81
- let const_kind = match hir_map. body_owner_kind ( owner) {
85
+ fn for_body ( tcx : TyCtxt < ' _ > , body : & hir:: Body < ' _ > ) -> Option < Self > {
86
+ let owner = tcx. hir ( ) . body_owner ( body. id ( ) ) ;
87
+ let const_kind = match tcx. hir ( ) . body_owner_kind ( owner) {
82
88
hir:: BodyOwnerKind :: Const => Self :: Const ,
83
89
hir:: BodyOwnerKind :: Static ( Mutability :: Mut ) => Self :: StaticMut ,
84
90
hir:: BodyOwnerKind :: Static ( Mutability :: Not ) => Self :: Static ,
85
91
86
- hir:: BodyOwnerKind :: Fn if is_const_fn ( owner) => Self :: ConstFn ,
92
+ hir:: BodyOwnerKind :: Fn if is_min_const_fn ( tcx, tcx. hir ( ) . local_def_id ( owner) ) => {
93
+ Self :: MinConstFn
94
+ }
95
+
96
+ // Use `is_const_fn_raw` here since we need to check the bodies of unstable `const fn`
97
+ // as well as stable ones.
98
+ hir:: BodyOwnerKind :: Fn if tcx. is_const_fn_raw ( tcx. hir ( ) . local_def_id ( owner) ) => {
99
+ Self :: ConstFn
100
+ }
101
+
87
102
hir:: BodyOwnerKind :: Fn | hir:: BodyOwnerKind :: Closure => return None ,
88
103
} ;
89
104
@@ -97,7 +112,7 @@ impl fmt::Display for ConstKind {
97
112
Self :: Static => "static" ,
98
113
Self :: StaticMut => "static mut" ,
99
114
Self :: Const | Self :: AnonConst => "const" ,
100
- Self :: ConstFn => "const fn" ,
115
+ Self :: MinConstFn | Self :: ConstFn => "const fn" ,
101
116
} ;
102
117
103
118
write ! ( f, "{}" , s)
@@ -211,7 +226,7 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
211
226
}
212
227
213
228
fn visit_body ( & mut self , body : & ' tcx hir:: Body < ' tcx > ) {
214
- let kind = ConstKind :: for_body ( body , self . tcx . hir ( ) ) ;
229
+ let kind = ConstKind :: for_body ( self . tcx , body ) ;
215
230
self . recurse_into ( kind, |this| intravisit:: walk_body ( this, body) ) ;
216
231
}
217
232
@@ -229,6 +244,26 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
229
244
// Skip the following checks if we are not currently in a const context.
230
245
_ if self . const_kind . is_none ( ) => { }
231
246
247
+ // Short-circuiting operators were forbidden outright in the min_const_fn checks. In
248
+ // other contexts, they are converted to non-short-circuiting operators while lowering
249
+ // to MIR and marked as "control-flow destroyed". Bodies whose control-flow has been
250
+ // altered in this manner are rejected during MIR const-checking if they have any
251
+ // user-declared locals, since the user could observe the change like so:
252
+ //
253
+ // let mut altered_control_flow = false;
254
+ // true && { altered_control_flow = true; false }
255
+ hir:: ExprKind :: Binary ( kind, _, _) if self . const_kind == Some ( ConstKind :: MinConstFn ) => {
256
+ let expr = match kind. node {
257
+ hir:: BinOpKind :: And => Some ( NonConstExpr :: LogicalAnd ) ,
258
+ hir:: BinOpKind :: Or => Some ( NonConstExpr :: LogicalOr ) ,
259
+ _ => None ,
260
+ } ;
261
+
262
+ if let Some ( expr) = expr {
263
+ self . const_check_violated ( expr, e. span ) ;
264
+ }
265
+ }
266
+
232
267
hir:: ExprKind :: Loop ( _, _, source) => {
233
268
self . const_check_violated ( NonConstExpr :: Loop ( * source) , e. span ) ;
234
269
}
0 commit comments