@@ -16,6 +16,7 @@ use rustc_errors::struct_span_err;
1616use rustc_hir as hir;
1717use rustc_hir:: def_id:: DefId ;
1818use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
19+ use rustc_mir:: const_eval:: is_min_const_fn;
1920use rustc_span:: { sym, Span , Symbol } ;
2021use syntax:: ast:: Mutability ;
2122
@@ -27,6 +28,8 @@ enum NonConstExpr {
2728 Loop ( hir:: LoopSource ) ,
2829 Match ( hir:: MatchSource ) ,
2930 OrPattern ,
31+ LogicalOr ,
32+ LogicalAnd ,
3033}
3134
3235impl NonConstExpr {
@@ -35,6 +38,8 @@ impl NonConstExpr {
3538 Self :: Loop ( src) => format ! ( "`{}`" , src. name( ) ) ,
3639 Self :: Match ( src) => format ! ( "`{}`" , src. name( ) ) ,
3740 Self :: OrPattern => format ! ( "or-pattern" ) ,
41+ Self :: LogicalOr => format ! ( "`||`" ) ,
42+ Self :: LogicalAnd => format ! ( "`&&`" ) ,
3843 }
3944 }
4045
@@ -46,6 +51,8 @@ impl NonConstExpr {
4651 Self :: Match ( Normal )
4752 | Self :: Match ( IfDesugar { .. } )
4853 | Self :: Match ( IfLetDesugar { .. } )
54+ | Self :: LogicalOr
55+ | Self :: LogicalAnd
4956 | Self :: OrPattern => & [ sym:: const_if_match] ,
5057
5158 Self :: Loop ( Loop ) => & [ sym:: const_loop] ,
@@ -64,26 +71,34 @@ impl NonConstExpr {
6471 }
6572}
6673
67- #[ derive( Copy , Clone ) ]
74+ #[ derive( Copy , Clone , PartialEq , Eq ) ]
6875enum ConstKind {
6976 Static ,
7077 StaticMut ,
7178 ConstFn ,
79+ MinConstFn ,
7280 Const ,
7381 AnonConst ,
7482}
7583
7684impl 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) {
8288 hir:: BodyOwnerKind :: Const => Self :: Const ,
8389 hir:: BodyOwnerKind :: Static ( Mutability :: Mut ) => Self :: StaticMut ,
8490 hir:: BodyOwnerKind :: Static ( Mutability :: Not ) => Self :: Static ,
8591
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+
87102 hir:: BodyOwnerKind :: Fn | hir:: BodyOwnerKind :: Closure => return None ,
88103 } ;
89104
@@ -97,7 +112,7 @@ impl fmt::Display for ConstKind {
97112 Self :: Static => "static" ,
98113 Self :: StaticMut => "static mut" ,
99114 Self :: Const | Self :: AnonConst => "const" ,
100- Self :: ConstFn => "const fn" ,
115+ Self :: MinConstFn | Self :: ConstFn => "const fn" ,
101116 } ;
102117
103118 write ! ( f, "{}" , s)
@@ -211,7 +226,7 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
211226 }
212227
213228 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 ) ;
215230 self . recurse_into ( kind, |this| intravisit:: walk_body ( this, body) ) ;
216231 }
217232
@@ -229,6 +244,26 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
229244 // Skip the following checks if we are not currently in a const context.
230245 _ if self . const_kind . is_none ( ) => { }
231246
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+
232267 hir:: ExprKind :: Loop ( _, _, source) => {
233268 self . const_check_violated ( NonConstExpr :: Loop ( * source) , e. span ) ;
234269 }
0 commit comments