@@ -35,11 +35,25 @@ 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 have to do full const-checking because syntactically, we
52+ /// are required to be "const".
53+ #[ inline]
54+ fn requires_const_checking ( self ) -> bool {
55+ self != Mode :: NonConstFn
56+ }
4357}
4458
4559impl fmt:: Display for Mode {
@@ -48,7 +62,7 @@ impl fmt::Display for Mode {
4862 Mode :: Const => write ! ( f, "constant" ) ,
4963 Mode :: Static | Mode :: StaticMut => write ! ( f, "static" ) ,
5064 Mode :: ConstFn => write ! ( f, "constant function" ) ,
51- Mode :: Fn => write ! ( f, "function" )
65+ Mode :: NonConstFn => write ! ( f, "function" )
5266 }
5367 }
5468}
@@ -135,6 +149,12 @@ enum ValueSource<'a, 'tcx> {
135149 } ,
136150}
137151
152+ /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
153+ /// code for promotion or prevent it from evaluating at compile time. So `return true` means
154+ /// "I found something bad, no reason to go on searching". `false` is only returned if we
155+ /// definitely cannot find anything bad anywhere.
156+ ///
157+ /// The default implementations proceed structurally.
138158trait Qualif {
139159 const IDX : usize ;
140160
@@ -285,7 +305,11 @@ trait Qualif {
285305 }
286306}
287307
288- // Constant containing interior mutability (UnsafeCell).
308+ /// Constant containing interior mutability (`UnsafeCell<T>`).
309+ /// This must be ruled out to make sure that evaluating the constant at compile-time
310+ /// and at *any point* during the run-time would produce the same result. In particular,
311+ /// promotion of temporaries must not change program behavior; if the promoted could be
312+ /// written to, that would be a problem.
289313struct HasMutInterior ;
290314
291315impl Qualif for HasMutInterior {
@@ -314,10 +338,10 @@ impl Qualif for HasMutInterior {
314338 _ => return true ,
315339 }
316340 } else if let ty:: Array ( _, len) = ty. sty {
317- // FIXME(eddyb) the `cx.mode == Mode::Fn ` condition
341+ // FIXME(eddyb) the `cx.mode == Mode::NonConstFn ` condition
318342 // seems unnecessary, given that this is merely a ZST.
319343 match len. assert_usize ( cx. tcx ) {
320- Some ( 0 ) if cx. mode == Mode :: Fn => { } ,
344+ Some ( 0 ) if cx. mode == Mode :: NonConstFn => { } ,
321345 _ => return true ,
322346 }
323347 } else {
@@ -343,7 +367,10 @@ impl Qualif for HasMutInterior {
343367 }
344368}
345369
346- // Constant containing an ADT that implements Drop.
370+ /// Constant containing an ADT that implements `Drop`.
371+ /// This must be ruled out (a) because we cannot run `Drop` during compile-time
372+ /// as that might not be a `const fn`, and (b) because implicit promotion would
373+ /// remove side-effects that occur as part of dropping that value.
347374struct NeedsDrop ;
348375
349376impl Qualif for NeedsDrop {
@@ -366,8 +393,12 @@ impl Qualif for NeedsDrop {
366393 }
367394}
368395
369- // Not promotable at all - non-`const fn` calls, asm!,
370- // pointer comparisons, ptr-to-int casts, etc.
396+ /// Not promotable at all - non-`const fn` calls, `asm!`,
397+ /// pointer comparisons, ptr-to-int casts, etc.
398+ /// Inside a const context all constness rules apply, so promotion simply has to follow the regular
399+ /// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
400+ /// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
401+ /// visitor enforces by emitting errors when working in const context.
371402struct IsNotPromotable ;
372403
373404impl Qualif for IsNotPromotable {
@@ -398,9 +429,10 @@ impl Qualif for IsNotPromotable {
398429 ProjectionElem :: Index ( _) => { }
399430
400431 ProjectionElem :: Field ( ..) => {
401- if cx. mode == Mode :: Fn {
432+ if cx. mode == Mode :: NonConstFn {
402433 let base_ty = proj. base . ty ( cx. body , cx. tcx ) . ty ;
403434 if let Some ( def) = base_ty. ty_adt_def ( ) {
435+ // No promotion of union field accesses.
404436 if def. is_union ( ) {
405437 return true ;
406438 }
@@ -414,7 +446,7 @@ impl Qualif for IsNotPromotable {
414446
415447 fn in_rvalue ( cx : & ConstCx < ' _ , ' tcx > , rvalue : & Rvalue < ' tcx > ) -> bool {
416448 match * rvalue {
417- Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if cx. mode == Mode :: Fn => {
449+ Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if cx. mode == Mode :: NonConstFn => {
418450 let operand_ty = operand. ty ( cx. body , cx. tcx ) ;
419451 let cast_in = CastTy :: from_ty ( operand_ty) . expect ( "bad input type for cast" ) ;
420452 let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
@@ -428,7 +460,7 @@ impl Qualif for IsNotPromotable {
428460 }
429461 }
430462
431- Rvalue :: BinaryOp ( op, ref lhs, _) if cx. mode == Mode :: Fn => {
463+ Rvalue :: BinaryOp ( op, ref lhs, _) if cx. mode == Mode :: NonConstFn => {
432464 if let ty:: RawPtr ( _) | ty:: FnPtr ( ..) = lhs. ty ( cx. body , cx. tcx ) . sty {
433465 assert ! ( op == BinOp :: Eq || op == BinOp :: Ne ||
434466 op == BinOp :: Le || op == BinOp :: Lt ||
@@ -511,12 +543,9 @@ impl Qualif for IsNotPromotable {
511543
512544/// Refers to temporaries which cannot be promoted *implicitly*.
513545/// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
514- /// Inside a const context all constness rules
515- /// apply, so implicit promotion simply has to follow the regular constant rules (modulo interior
516- /// mutability or `Drop` rules which are handled `HasMutInterior` and `NeedsDrop` respectively).
517- /// Implicit promotion inside regular functions does not happen if `const fn` calls are involved,
518- /// as the call may be perfectly alright at runtime, but fail at compile time e.g. due to addresses
519- /// being compared inside the function.
546+ /// Implicit promotion has almost the same rules, except that disallows `const fn` except for
547+ /// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run-time operation
548+ /// into a failing compile-time operation e.g. due to addresses being compared inside the function.
520549struct IsNotImplicitlyPromotable ;
521550
522551impl Qualif for IsNotImplicitlyPromotable {
@@ -528,7 +557,7 @@ impl Qualif for IsNotImplicitlyPromotable {
528557 args : & [ Operand < ' tcx > ] ,
529558 _return_ty : Ty < ' tcx > ,
530559 ) -> bool {
531- if cx. mode == Mode :: Fn {
560+ if cx. mode == Mode :: NonConstFn {
532561 if let ty:: FnDef ( def_id, _) = callee. ty ( cx. body , cx. tcx ) . sty {
533562 // Never promote runtime `const fn` calls of
534563 // functions without `#[rustc_promotable]`.
@@ -589,6 +618,11 @@ impl ConstCx<'_, 'tcx> {
589618 }
590619}
591620
621+ /// Checks MIR for being admissible as a compile-time constant, using `ConstCx`
622+ /// for value qualifications, and accumulates writes of
623+ /// rvalue/call results to locals, in `local_qualif`.
624+ /// It also records candidates for promotion in `promotion_candidates`,
625+ /// both in functions and const/static items.
592626struct Checker < ' a , ' tcx > {
593627 cx : ConstCx < ' a , ' tcx > ,
594628
@@ -672,7 +706,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
672706 // slightly pointless (even with feature-gating).
673707 fn not_const ( & mut self ) {
674708 unleash_miri ! ( self ) ;
675- if self . mode != Mode :: Fn {
709+ if self . mode . requires_const_checking ( ) {
676710 let mut err = struct_span_err ! (
677711 self . tcx. sess,
678712 self . span,
@@ -707,7 +741,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
707741 qualifs[ HasMutInterior ] = false ;
708742 qualifs[ IsNotPromotable ] = true ;
709743
710- if self . mode != Mode :: Fn {
744+ if self . mode . requires_const_checking ( ) {
711745 if let BorrowKind :: Mut { .. } = kind {
712746 let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0017 ,
713747 "references in {}s may only refer \
@@ -737,7 +771,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
737771
738772 // We might have a candidate for promotion.
739773 let candidate = Candidate :: Ref ( location) ;
740- // We can only promote interior borrows of promotable temps .
774+ // Start by traversing to the "base", with non-deref projections removed .
741775 let mut place = place;
742776 while let Place :: Projection ( ref proj) = * place {
743777 if proj. elem == ProjectionElem :: Deref {
@@ -746,6 +780,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
746780 place = & proj. base ;
747781 }
748782 debug ! ( "qualify_consts: promotion candidate: place={:?}" , place) ;
783+ // We can only promote interior borrows of promotable temps (non-temps
784+ // don't get promoted anyway).
785+ // (If we bailed out of the loop due to a `Deref` above, we will definitely
786+ // not enter the conditional here.)
749787 if let Place :: Base ( PlaceBase :: Local ( local) ) = * place {
750788 if self . body . local_kind ( local) == LocalKind :: Temp {
751789 debug ! ( "qualify_consts: promotion candidate: local={:?}" , local) ;
@@ -756,6 +794,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
756794 // `HasMutInterior`, from a type that does, e.g.:
757795 // `let _: &'static _ = &(Cell::new(1), 2).1;`
758796 let mut local_qualifs = self . qualifs_in_local ( local) ;
797+ // Any qualifications, except HasMutInterior (see above), disqualify
798+ // from promotion.
799+ // This is, in particular, the "implicit promotion" version of
800+ // the check making sure that we don't run drop glue during const-eval.
759801 local_qualifs[ HasMutInterior ] = false ;
760802 if !local_qualifs. 0 . iter ( ) . any ( |& qualif| qualif) {
761803 debug ! ( "qualify_consts: promotion candidate: {:?}" , candidate) ;
@@ -803,7 +845,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
803845 debug ! ( "store to {:?} {:?}" , kind, index) ;
804846
805847 // Only handle promotable temps in non-const functions.
806- if self . mode == Mode :: Fn {
848+ if self . mode == Mode :: NonConstFn {
807849 if kind != LocalKind :: Temp ||
808850 !self . temp_promotion_state [ index] . is_promotable ( ) {
809851 return ;
@@ -920,11 +962,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
920962 }
921963}
922964
923- /// Checks MIR for const-correctness, using `ConstCx`
924- /// for value qualifications, and accumulates writes of
925- /// rvalue/call results to locals, in `local_qualif`.
926- /// For functions (constant or not), it also records
927- /// candidates for promotion in `promotion_candidates`.
928965impl < ' a , ' tcx > Visitor < ' tcx > for Checker < ' a , ' tcx > {
929966 fn visit_place_base (
930967 & mut self ,
@@ -943,7 +980,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
943980 . get_attrs ( * def_id)
944981 . iter ( )
945982 . any ( |attr| attr. check_name ( sym:: thread_local) ) {
946- if self . mode != Mode :: Fn {
983+ if self . mode . requires_const_checking ( ) {
947984 span_err ! ( self . tcx. sess, self . span, E0625 ,
948985 "thread-local statics cannot be \
949986 accessed at compile-time") ;
@@ -967,7 +1004,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
9671004 }
9681005 unleash_miri ! ( self ) ;
9691006
970- if self . mode != Mode :: Fn {
1007+ if self . mode . requires_const_checking ( ) {
9711008 let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0013 ,
9721009 "{}s cannot refer to statics, use \
9731010 a constant instead", self . mode) ;
@@ -1005,7 +1042,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
10051042 }
10061043 let base_ty = proj. base . ty ( self . body , self . tcx ) . ty ;
10071044 match self . mode {
1008- Mode :: Fn => { } ,
1045+ Mode :: NonConstFn => { } ,
10091046 _ => {
10101047 if let ty:: RawPtr ( _) = base_ty. sty {
10111048 if !self . tcx . features ( ) . const_raw_ptr_deref {
@@ -1041,7 +1078,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
10411078 }
10421079 } ,
10431080
1044- | Mode :: Fn
1081+ | Mode :: NonConstFn
10451082 | Mode :: Static
10461083 | Mode :: StaticMut
10471084 | Mode :: Const
@@ -1131,7 +1168,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11311168 let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
11321169 match ( cast_in, cast_out) {
11331170 ( CastTy :: Ptr ( _) , CastTy :: Int ( _) ) |
1134- ( CastTy :: FnPtr , CastTy :: Int ( _) ) if self . mode != Mode :: Fn => {
1171+ ( CastTy :: FnPtr , CastTy :: Int ( _) ) if self . mode != Mode :: NonConstFn => {
11351172 unleash_miri ! ( self ) ;
11361173 if !self . tcx . features ( ) . const_raw_ptr_to_usize_cast {
11371174 // in const fn and constants require the feature gate
@@ -1158,7 +1195,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11581195 op == BinOp :: Offset ) ;
11591196
11601197 unleash_miri ! ( self ) ;
1161- if self . mode != Mode :: Fn && !self . tcx . features ( ) . const_compare_raw_pointers {
1198+ if self . mode . requires_const_checking ( ) &&
1199+ !self . tcx . features ( ) . const_compare_raw_pointers
1200+ {
11621201 // require the feature gate inside constants and const fn
11631202 // FIXME: make it unsafe to use these operations
11641203 emit_feature_err (
@@ -1174,7 +1213,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11741213
11751214 Rvalue :: NullaryOp ( NullOp :: Box , _) => {
11761215 unleash_miri ! ( self ) ;
1177- if self . mode != Mode :: Fn {
1216+ if self . mode . requires_const_checking ( ) {
11781217 let mut err = struct_span_err ! ( self . tcx. sess, self . span, E0010 ,
11791218 "allocations are not allowed in {}s" , self . mode) ;
11801219 err. span_label ( self . span , format ! ( "allocation not allowed in {}s" , self . mode) ) ;
@@ -1219,8 +1258,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
12191258 // special intrinsic that can be called diretly without an intrinsic
12201259 // feature gate needs a language feature gate
12211260 "transmute" => {
1222- // never promote transmute calls
1223- if self . mode != Mode :: Fn {
1261+ if self . mode . requires_const_checking ( ) {
12241262 // const eval transmute calls only with the feature gate
12251263 if !self . tcx . features ( ) . const_transmute {
12261264 emit_feature_err (
@@ -1243,7 +1281,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
12431281 }
12441282 _ => {
12451283 // In normal functions no calls are feature-gated.
1246- if self . mode != Mode :: Fn {
1284+ if self . mode . requires_const_checking ( ) {
12471285 let unleash_miri = self
12481286 . tcx
12491287 . sess
@@ -1302,7 +1340,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
13021340 }
13031341 }
13041342 ty:: FnPtr ( _) => {
1305- if self . mode != Mode :: Fn {
1343+ if self . mode . requires_const_checking ( ) {
13061344 let mut err = self . tcx . sess . struct_span_err (
13071345 self . span ,
13081346 & format ! ( "function pointers are not allowed in const fn" ) ) ;
@@ -1361,7 +1399,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
13611399 self . super_terminator_kind ( kind, location) ;
13621400
13631401 // Deny *any* live drops anywhere other than functions.
1364- if self . mode != Mode :: Fn {
1402+ if self . mode . requires_const_checking ( ) {
13651403 unleash_miri ! ( self ) ;
13661404 // HACK(eddyb): emulate a bit of dataflow analysis,
13671405 // conservatively, that drop elaboration will do.
@@ -1472,12 +1510,12 @@ impl MirPass for QualifyAndPromoteConstants {
14721510 let id = tcx. hir ( ) . as_local_hir_id ( def_id) . unwrap ( ) ;
14731511 let mut const_promoted_temps = None ;
14741512 let mode = match tcx. hir ( ) . body_owner_kind_by_hir_id ( id) {
1475- hir:: BodyOwnerKind :: Closure => Mode :: Fn ,
1513+ hir:: BodyOwnerKind :: Closure => Mode :: NonConstFn ,
14761514 hir:: BodyOwnerKind :: Fn => {
14771515 if tcx. is_const_fn ( def_id) {
14781516 Mode :: ConstFn
14791517 } else {
1480- Mode :: Fn
1518+ Mode :: NonConstFn
14811519 }
14821520 }
14831521 hir:: BodyOwnerKind :: Const => {
@@ -1489,7 +1527,7 @@ impl MirPass for QualifyAndPromoteConstants {
14891527 } ;
14901528
14911529 debug ! ( "run_pass: mode={:?}" , mode) ;
1492- if mode == Mode :: Fn || mode == Mode :: ConstFn {
1530+ if mode == Mode :: NonConstFn || mode == Mode :: ConstFn {
14931531 // This is ugly because Checker holds onto mir,
14941532 // which can't be mutated until its scope ends.
14951533 let ( temps, candidates) = {
0 commit comments