@@ -297,6 +297,17 @@ impl std::ops::Deref for Validator<'a, 'tcx> {
297297struct Unpromotable ;
298298
299299impl < ' tcx > Validator < ' _ , ' tcx > {
300+ //! Determines if this code could be executed at runtime and thus is subject to codegen.
301+ //! That means even unused constants need to be evaluated.
302+ //!
303+ //! `const_kind` should not be used in this file other than through this method!
304+ fn maybe_runtime ( & self ) -> bool {
305+ match self . const_kind {
306+ None | Some ( hir:: ConstContext :: ConstFn ) => true ,
307+ Some ( hir:: ConstContext :: Static ( _) | hir:: ConstContext :: Const ) => false ,
308+ }
309+ }
310+
300311 fn validate_candidate ( & self , candidate : Candidate ) -> Result < ( ) , Unpromotable > {
301312 match candidate {
302313 Candidate :: Ref ( loc) => {
@@ -365,10 +376,8 @@ impl<'tcx> Validator<'_, 'tcx> {
365376 // mutably without consequences. However, only &mut []
366377 // is allowed right now, and only in functions.
367378 if let ty:: Array ( _, len) = ty. kind ( ) {
368- // FIXME(eddyb) the `self.is_non_const_fn` condition
369- // seems unnecessary, given that this is merely a ZST.
370379 match len. try_eval_usize ( self . tcx , self . param_env ) {
371- Some ( 0 ) if self . const_kind . is_none ( ) => { }
380+ Some ( 0 ) => { }
372381 _ => return Err ( Unpromotable ) ,
373382 }
374383 } else {
@@ -495,9 +504,10 @@ impl<'tcx> Validator<'_, 'tcx> {
495504 match place {
496505 PlaceRef { local, projection : [ ] } => self . validate_local ( local) ,
497506 PlaceRef { local, projection : [ proj_base @ .., elem] } => {
507+ // Validate topmost projection, then recurse.
498508 match * elem {
499509 ProjectionElem :: Deref => {
500- let mut not_promotable = true ;
510+ let mut promotable = false ;
501511 // This is a special treatment for cases like *&STATIC where STATIC is a
502512 // global static variable.
503513 // This pattern is generated only when global static variables are directly
@@ -512,6 +522,9 @@ impl<'tcx> Validator<'_, 'tcx> {
512522 } ) = def_stmt
513523 {
514524 if let Some ( did) = c. check_static_ptr ( self . tcx ) {
525+ // Evaluating a promoted may not read statics except if it got
526+ // promoted from a static (this is a CTFE check). So we
527+ // can only promoted static accesses inside statics.
515528 if let Some ( hir:: ConstContext :: Static ( ..) ) = self . const_kind {
516529 // The `is_empty` predicate is introduced to exclude the case
517530 // where the projection operations are [ .field, * ].
@@ -524,13 +537,13 @@ impl<'tcx> Validator<'_, 'tcx> {
524537 if proj_base. is_empty ( )
525538 && !self . tcx . is_thread_local_static ( did)
526539 {
527- not_promotable = false ;
540+ promotable = true ;
528541 }
529542 }
530543 }
531544 }
532545 }
533- if not_promotable {
546+ if !promotable {
534547 return Err ( Unpromotable ) ;
535548 }
536549 }
@@ -545,7 +558,7 @@ impl<'tcx> Validator<'_, 'tcx> {
545558 }
546559
547560 ProjectionElem :: Field ( ..) => {
548- if self . const_kind . is_none ( ) {
561+ if self . maybe_runtime ( ) {
549562 let base_ty =
550563 Place :: ty_from ( place. local , proj_base, self . body , self . tcx ) . ty ;
551564 if let Some ( def) = base_ty. ty_adt_def ( ) {
@@ -571,13 +584,6 @@ impl<'tcx> Validator<'_, 'tcx> {
571584 // `validate_rvalue` upon access.
572585 Operand :: Constant ( c) => {
573586 if let Some ( def_id) = c. check_static_ptr ( self . tcx ) {
574- // Only allow statics (not consts) to refer to other statics.
575- // FIXME(eddyb) does this matter at all for promotion?
576- let is_static = matches ! ( self . const_kind, Some ( hir:: ConstContext :: Static ( _) ) ) ;
577- if !is_static {
578- return Err ( Unpromotable ) ;
579- }
580-
581587 let is_thread_local = self . tcx . is_thread_local_static ( def_id) ;
582588 if is_thread_local {
583589 return Err ( Unpromotable ) ;
@@ -591,20 +597,20 @@ impl<'tcx> Validator<'_, 'tcx> {
591597
592598 fn validate_rvalue ( & self , rvalue : & Rvalue < ' tcx > ) -> Result < ( ) , Unpromotable > {
593599 match * rvalue {
594- Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if self . const_kind . is_none ( ) => {
600+ Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if self . maybe_runtime ( ) => {
595601 let operand_ty = operand. ty ( self . body , self . tcx ) ;
596602 let cast_in = CastTy :: from_ty ( operand_ty) . expect ( "bad input type for cast" ) ;
597603 let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
598604 match ( cast_in, cast_out) {
599605 ( CastTy :: Ptr ( _) | CastTy :: FnPtr , CastTy :: Int ( _) ) => {
600- // in normal functions, mark such casts as not promotable
606+ // ptr-to-int casts are not promotable
601607 return Err ( Unpromotable ) ;
602608 }
603609 _ => { }
604610 }
605611 }
606612
607- Rvalue :: BinaryOp ( op, ref lhs, _) if self . const_kind . is_none ( ) => {
613+ Rvalue :: BinaryOp ( op, ref lhs, _) if self . maybe_runtime ( ) => {
608614 if let ty:: RawPtr ( _) | ty:: FnPtr ( ..) = lhs. ty ( self . body , self . tcx ) . kind ( ) {
609615 assert ! (
610616 op == BinOp :: Eq
@@ -623,6 +629,7 @@ impl<'tcx> Validator<'_, 'tcx> {
623629
624630 Rvalue :: NullaryOp ( NullOp :: Box , _) => return Err ( Unpromotable ) ,
625631
632+ // FIXME(RalfJung): the rest is *implicitly considered promotable*... that seems dangerous.
626633 _ => { }
627634 }
628635
@@ -644,8 +651,8 @@ impl<'tcx> Validator<'_, 'tcx> {
644651 }
645652
646653 Rvalue :: AddressOf ( _, place) => {
647- // Raw reborrows can come from reference to pointer coercions,
648- // so are allowed .
654+ // We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is
655+ // no problem, only using it is .
649656 if let [ proj_base @ .., ProjectionElem :: Deref ] = place. projection . as_ref ( ) {
650657 let base_ty = Place :: ty_from ( place. local , proj_base, self . body , self . tcx ) . ty ;
651658 if let ty:: Ref ( ..) = base_ty. kind ( ) {
@@ -666,10 +673,8 @@ impl<'tcx> Validator<'_, 'tcx> {
666673 // mutably without consequences. However, only &mut []
667674 // is allowed right now, and only in functions.
668675 if let ty:: Array ( _, len) = ty. kind ( ) {
669- // FIXME(eddyb): We only return `Unpromotable` for `&mut []` inside a
670- // const context which seems unnecessary given that this is merely a ZST.
671676 match len. try_eval_usize ( self . tcx , self . param_env ) {
672- Some ( 0 ) if self . const_kind . is_none ( ) => { }
677+ Some ( 0 ) => { }
673678 _ => return Err ( Unpromotable ) ,
674679 }
675680 } else {
@@ -734,14 +739,7 @@ impl<'tcx> Validator<'_, 'tcx> {
734739 ) -> Result < ( ) , Unpromotable > {
735740 let fn_ty = callee. ty ( self . body , self . tcx ) ;
736741
737- // `const` and `static` use the explicit rules for promotion regardless of the `Candidate`,
738- // meaning calls to `const fn` can be promoted.
739- let context_uses_explicit_promotion_rules = matches ! (
740- self . const_kind,
741- Some ( hir:: ConstContext :: Static ( _) | hir:: ConstContext :: Const )
742- ) ;
743-
744- if !self . explicit && !context_uses_explicit_promotion_rules {
742+ if !self . explicit && self . maybe_runtime ( ) {
745743 if let ty:: FnDef ( def_id, _) = * fn_ty. kind ( ) {
746744 // Never promote runtime `const fn` calls of
747745 // functions without `#[rustc_promotable]`.
0 commit comments