@@ -35,11 +35,26 @@ use super::promote_consts::{self, Candidate, TempState};
3535/// What kind of item we are in.
3636#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
3737enum Mode {
38- Const ,
38+ /// A `static` item.
3939 Static ,
40+ /// A `static mut` item.
4041 StaticMut ,
42+ /// A `const fn` item.
4143 ConstFn ,
42- Fn
44+ /// A `const` item or an anonymous constant (e.g. in array lengths).
45+ Const ,
46+ /// Other type of `fn`.
47+ NonConstFn ,
48+ }
49+
50+ impl Mode {
51+ /// Determine whether we are running in "const context". "const context" refers
52+ /// to code type-checked according to the rules of the "const type system":
53+ /// the bodies of const/static items and `const fn`.
54+ #[ inline]
55+ fn requires_const_checking ( self ) -> bool {
56+ self != Mode :: NonConstFn
57+ }
4358}
4459
4560impl fmt:: Display for Mode {
@@ -48,7 +63,7 @@ impl fmt::Display for Mode {
4863 Mode :: Const => write ! ( f, "constant" ) ,
4964 Mode :: Static | Mode :: StaticMut => write ! ( f, "static" ) ,
5065 Mode :: ConstFn => write ! ( f, "constant function" ) ,
51- Mode :: Fn => write ! ( f, "function" )
66+ Mode :: NonConstFn => write ! ( f, "function" )
5267 }
5368 }
5469}
@@ -135,10 +150,10 @@ enum ValueSource<'a, 'tcx> {
135150 } ,
136151}
137152
138- /// A "qualif" is a way to lookg for something "bad" in the MIR that would prevent
139- /// proper const evaluation. So `return true` means "I found something bad, no reason
140- /// to go on searching". `false` is only returned if we definitely cannot find anything
141- /// bad anywhere.
153+ /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
154+ /// code for promotion or prevent it from evaluating at compile time. So `return true` means
155+ /// "I found something bad, no reason to go on searching". `false` is only returned if we
156+ /// definitely cannot find anything bad anywhere.
142157///
143158/// The default implementations proceed structurally.
144159trait Qualif {
@@ -291,9 +306,11 @@ trait Qualif {
291306 }
292307}
293308
294- /// Constant containing interior mutability (UnsafeCell).
309+ /// Constant containing interior mutability (` UnsafeCell<T>` ).
295310/// This must be ruled out to make sure that evaluating the constant at compile-time
296- /// and run-time would produce the same result.
311+ /// and run-time would produce the same result. In particular, promotion of temporaries
312+ /// must not change program behavior; if the promoted could be written to, that would
313+ /// be a problem.
297314struct HasMutInterior ;
298315
299316impl Qualif for HasMutInterior {
@@ -322,10 +339,10 @@ impl Qualif for HasMutInterior {
322339 _ => return true ,
323340 }
324341 } else if let ty:: Array ( _, len) = ty. sty {
325- // FIXME(eddyb) the `cx.mode == Mode::Fn ` condition
342+ // FIXME(eddyb) the `cx.mode == Mode::NonConstFn ` condition
326343 // seems unnecessary, given that this is merely a ZST.
327344 match len. assert_usize ( cx. tcx ) {
328- Some ( 0 ) if cx. mode == Mode :: Fn => { } ,
345+ Some ( 0 ) if cx. mode == Mode :: NonConstFn => { } ,
329346 _ => return true ,
330347 }
331348 } else {
@@ -351,9 +368,10 @@ impl Qualif for HasMutInterior {
351368 }
352369}
353370
354- /// Constant containing an ADT that implements Drop.
355- /// This must be ruled out because we cannot run `Drop` during compile-time
356- /// as that might not be a `const fn`.
371+ /// Constant containing an ADT that implements `Drop`.
372+ /// This must be ruled out (a) because we cannot run `Drop` during compile-time
373+ /// as that might not be a `const fn`, and (b) because implicit promotion would
374+ /// remove side-effects that occur as part of dropping that value.
357375struct NeedsDrop ;
358376
359377impl Qualif for NeedsDrop {
@@ -376,11 +394,12 @@ impl Qualif for NeedsDrop {
376394 }
377395}
378396
379- /// Not promotable at all - non-`const fn` calls, asm!,
397+ /// Not promotable at all - non-`const fn` calls, ` asm!` ,
380398/// pointer comparisons, ptr-to-int casts, etc.
381399/// Inside a const context all constness rules apply, so promotion simply has to follow the regular
382400/// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
383- /// and `NeedsDrop` respectively).
401+ /// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
402+ /// visitor enforces by emitting errors when working in const context.
384403struct IsNotPromotable ;
385404
386405impl Qualif for IsNotPromotable {
@@ -411,9 +430,10 @@ impl Qualif for IsNotPromotable {
411430 ProjectionElem :: Index ( _) => { }
412431
413432 ProjectionElem :: Field ( ..) => {
414- if cx. mode == Mode :: Fn {
433+ if cx. mode == Mode :: NonConstFn {
415434 let base_ty = proj. base . ty ( cx. mir , cx. tcx ) . ty ;
416435 if let Some ( def) = base_ty. ty_adt_def ( ) {
436+ // No promotion of union field accesses.
417437 if def. is_union ( ) {
418438 return true ;
419439 }
@@ -427,7 +447,7 @@ impl Qualif for IsNotPromotable {
427447
428448 fn in_rvalue ( cx : & ConstCx < ' _ , ' tcx > , rvalue : & Rvalue < ' tcx > ) -> bool {
429449 match * rvalue {
430- Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if cx. mode == Mode :: Fn => {
450+ Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if cx. mode == Mode :: NonConstFn => {
431451 let operand_ty = operand. ty ( cx. mir , cx. tcx ) ;
432452 let cast_in = CastTy :: from_ty ( operand_ty) . expect ( "bad input type for cast" ) ;
433453 let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
@@ -441,7 +461,7 @@ impl Qualif for IsNotPromotable {
441461 }
442462 }
443463
444- Rvalue :: BinaryOp ( op, ref lhs, _) if cx. mode == Mode :: Fn => {
464+ Rvalue :: BinaryOp ( op, ref lhs, _) if cx. mode == Mode :: NonConstFn => {
445465 if let ty:: RawPtr ( _) | ty:: FnPtr ( ..) = lhs. ty ( cx. mir , cx. tcx ) . sty {
446466 assert ! ( op == BinOp :: Eq || op == BinOp :: Ne ||
447467 op == BinOp :: Le || op == BinOp :: Lt ||
@@ -524,9 +544,9 @@ impl Qualif for IsNotPromotable {
524544
525545/// Refers to temporaries which cannot be promoted *implicitly*.
526546/// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
527- /// Implicit promotion has almost the same rules, except that it does not happen if `const fn`
528- /// calls are involved. The call may be perfectly alright at runtime, but fail at compile time
529- /// e.g. due to addresses being compared inside the function.
547+ /// Implicit promotion has almost the same rules, except that disallows `const fn` except for
548+ /// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run- time operation
549+ /// into a failing compile-time operation e.g. due to addresses being compared inside the function.
530550struct IsNotImplicitlyPromotable ;
531551
532552impl Qualif for IsNotImplicitlyPromotable {
@@ -538,7 +558,7 @@ impl Qualif for IsNotImplicitlyPromotable {
538558 args : & [ Operand < ' tcx > ] ,
539559 _return_ty : Ty < ' tcx > ,
540560 ) -> bool {
541- if cx. mode == Mode :: Fn {
561+ if cx. mode == Mode :: NonConstFn {
542562 if let ty:: FnDef ( def_id, _) = callee. ty ( cx. mir , cx. tcx ) . sty {
543563 // Never promote runtime `const fn` calls of
544564 // functions without `#[rustc_promotable]`.
@@ -602,8 +622,8 @@ impl ConstCx<'_, 'tcx> {
602622/// Checks MIR for const-correctness, using `ConstCx`
603623/// for value qualifications, and accumulates writes of
604624/// rvalue/call results to locals, in `local_qualif`.
605- /// For functions (constant or not), it also records
606- /// candidates for promotion in `promotion_candidates` .
625+ /// It also records candidates for promotion in `promotion_candidates`,
626+ /// both in functions and const/static items .
607627struct Checker < ' a , ' tcx > {
608628 cx : ConstCx < ' a , ' tcx > ,
609629
@@ -687,7 +707,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
687707 // slightly pointless (even with feature-gating).
688708 fn not_const ( & mut self ) {
689709 unleash_miri ! ( self ) ;
690- if self . mode != Mode :: Fn {
710+ if self . mode . requires_const_checking ( ) {
691711 let mut err = struct_span_err ! (
692712 self . tcx. sess,
693713 self . span,
@@ -722,7 +742,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
722742 qualifs[ HasMutInterior ] = false ;
723743 qualifs[ IsNotPromotable ] = true ;
724744
725- if self . mode != Mode :: Fn {
745+ if self . mode . requires_const_checking ( ) {
726746 if let BorrowKind :: Mut { .. } = kind {
727747 let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0017 ,
728748 "references in {}s may only refer \
@@ -752,7 +772,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
752772
753773 // We might have a candidate for promotion.
754774 let candidate = Candidate :: Ref ( location) ;
755- // We can only promote interior borrows of promotable temps .
775+ // Start by traversing to the "base", with non-deref projections removed .
756776 let mut place = place;
757777 while let Place :: Projection ( ref proj) = * place {
758778 if proj. elem == ProjectionElem :: Deref {
@@ -761,6 +781,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
761781 place = & proj. base ;
762782 }
763783 debug ! ( "qualify_consts: promotion candidate: place={:?}" , place) ;
784+ // We can only promote interior borrows of promotable temps (non-temps
785+ // don't get promoted anyway).
786+ // (If we bailed out of the loop due to a `Deref` above, we will definitely
787+ // not enter the conditional here.)
764788 if let Place :: Base ( PlaceBase :: Local ( local) ) = * place {
765789 if self . mir . local_kind ( local) == LocalKind :: Temp {
766790 debug ! ( "qualify_consts: promotion candidate: local={:?}" , local) ;
@@ -771,10 +795,11 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
771795 // `HasMutInterior`, from a type that does, e.g.:
772796 // `let _: &'static _ = &(Cell::new(1), 2).1;`
773797 let mut local_qualifs = self . qualifs_in_local ( local) ;
774- local_qualifs [ HasMutInterior ] = false ;
775- // Make sure there is no reason to prevent promotion.
798+ // Any qualifications, except HasMutInterior (see above), disqualify
799+ // from promotion.
776800 // This is, in particular, the "implicit promotion" version of
777801 // the check making sure that we don't run drop glue during const-eval.
802+ local_qualifs[ HasMutInterior ] = false ;
778803 if !local_qualifs. 0 . iter ( ) . any ( |& qualif| qualif) {
779804 debug ! ( "qualify_consts: promotion candidate: {:?}" , candidate) ;
780805 self . promotion_candidates . push ( candidate) ;
@@ -821,7 +846,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
821846 debug ! ( "store to {:?} {:?}" , kind, index) ;
822847
823848 // Only handle promotable temps in non-const functions.
824- if self . mode == Mode :: Fn {
849+ if self . mode == Mode :: NonConstFn {
825850 if kind != LocalKind :: Temp ||
826851 !self . temp_promotion_state [ index] . is_promotable ( ) {
827852 return ;
@@ -956,7 +981,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
956981 . get_attrs ( * def_id)
957982 . iter ( )
958983 . any ( |attr| attr. check_name ( sym:: thread_local) ) {
959- if self . mode != Mode :: Fn {
984+ if self . mode . requires_const_checking ( ) {
960985 span_err ! ( self . tcx. sess, self . span, E0625 ,
961986 "thread-local statics cannot be \
962987 accessed at compile-time") ;
@@ -980,7 +1005,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
9801005 }
9811006 unleash_miri ! ( self ) ;
9821007
983- if self . mode != Mode :: Fn {
1008+ if self . mode . requires_const_checking ( ) {
9841009 let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0013 ,
9851010 "{}s cannot refer to statics, use \
9861011 a constant instead", self . mode) ;
@@ -1018,7 +1043,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
10181043 }
10191044 let base_ty = proj. base . ty ( self . mir , self . tcx ) . ty ;
10201045 match self . mode {
1021- Mode :: Fn => { } ,
1046+ Mode :: NonConstFn => { } ,
10221047 _ => {
10231048 if let ty:: RawPtr ( _) = base_ty. sty {
10241049 if !self . tcx . features ( ) . const_raw_ptr_deref {
@@ -1054,7 +1079,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
10541079 }
10551080 } ,
10561081
1057- | Mode :: Fn
1082+ | Mode :: NonConstFn
10581083 | Mode :: Static
10591084 | Mode :: StaticMut
10601085 | Mode :: Const
@@ -1144,7 +1169,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11441169 let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
11451170 match ( cast_in, cast_out) {
11461171 ( CastTy :: Ptr ( _) , CastTy :: Int ( _) ) |
1147- ( CastTy :: FnPtr , CastTy :: Int ( _) ) if self . mode != Mode :: Fn => {
1172+ ( CastTy :: FnPtr , CastTy :: Int ( _) ) if self . mode != Mode :: NonConstFn => {
11481173 unleash_miri ! ( self ) ;
11491174 if !self . tcx . features ( ) . const_raw_ptr_to_usize_cast {
11501175 // in const fn and constants require the feature gate
@@ -1171,7 +1196,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11711196 op == BinOp :: Offset ) ;
11721197
11731198 unleash_miri ! ( self ) ;
1174- if self . mode != Mode :: Fn && !self . tcx . features ( ) . const_compare_raw_pointers {
1199+ if self . mode . requires_const_checking ( ) &&
1200+ !self . tcx . features ( ) . const_compare_raw_pointers
1201+ {
11751202 // require the feature gate inside constants and const fn
11761203 // FIXME: make it unsafe to use these operations
11771204 emit_feature_err (
@@ -1187,7 +1214,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11871214
11881215 Rvalue :: NullaryOp ( NullOp :: Box , _) => {
11891216 unleash_miri ! ( self ) ;
1190- if self . mode != Mode :: Fn {
1217+ if self . mode . requires_const_checking ( ) {
11911218 let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0010 ,
11921219 "allocations are not allowed in {}s" , self . mode) ;
11931220 err. span_label ( self . span , format ! ( "allocation not allowed in {}s" , self . mode) ) ;
@@ -1232,8 +1259,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
12321259 // special intrinsic that can be called diretly without an intrinsic
12331260 // feature gate needs a language feature gate
12341261 "transmute" => {
1235- // never promote transmute calls
1236- if self . mode != Mode :: Fn {
1262+ if self . mode . requires_const_checking ( ) {
12371263 // const eval transmute calls only with the feature gate
12381264 if !self . tcx . features ( ) . const_transmute {
12391265 emit_feature_err (
@@ -1256,7 +1282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
12561282 }
12571283 _ => {
12581284 // In normal functions no calls are feature-gated.
1259- if self . mode != Mode :: Fn {
1285+ if self . mode . requires_const_checking ( ) {
12601286 let unleash_miri = self
12611287 . tcx
12621288 . sess
@@ -1315,7 +1341,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
13151341 }
13161342 }
13171343 ty:: FnPtr ( _) => {
1318- if self . mode != Mode :: Fn {
1344+ if self . mode . requires_const_checking ( ) {
13191345 let mut err = self . tcx . sess . struct_span_err (
13201346 self . span ,
13211347 & format ! ( "function pointers are not allowed in const fn" ) ) ;
@@ -1374,7 +1400,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
13741400 self . super_terminator_kind ( kind, location) ;
13751401
13761402 // Deny *any* live drops anywhere other than functions.
1377- if self . mode != Mode :: Fn {
1403+ if self . mode . requires_const_checking ( ) {
13781404 unleash_miri ! ( self ) ;
13791405 // HACK(eddyb): emulate a bit of dataflow analysis,
13801406 // conservatively, that drop elaboration will do.
@@ -1485,12 +1511,12 @@ impl MirPass for QualifyAndPromoteConstants {
14851511 let id = tcx. hir ( ) . as_local_hir_id ( def_id) . unwrap ( ) ;
14861512 let mut const_promoted_temps = None ;
14871513 let mode = match tcx. hir ( ) . body_owner_kind_by_hir_id ( id) {
1488- hir:: BodyOwnerKind :: Closure => Mode :: Fn ,
1514+ hir:: BodyOwnerKind :: Closure => Mode :: NonConstFn ,
14891515 hir:: BodyOwnerKind :: Fn => {
14901516 if tcx. is_const_fn ( def_id) {
14911517 Mode :: ConstFn
14921518 } else {
1493- Mode :: Fn
1519+ Mode :: NonConstFn
14941520 }
14951521 }
14961522 hir:: BodyOwnerKind :: Const => {
@@ -1502,7 +1528,7 @@ impl MirPass for QualifyAndPromoteConstants {
15021528 } ;
15031529
15041530 debug ! ( "run_pass: mode={:?}" , mode) ;
1505- if mode == Mode :: Fn || mode == Mode :: ConstFn {
1531+ if mode == Mode :: NonConstFn || mode == Mode :: ConstFn {
15061532 // This is ugly because Checker holds onto mir,
15071533 // which can't be mutated until its scope ends.
15081534 let ( temps, candidates) = {
0 commit comments