@@ -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) => {
@@ -363,12 +374,10 @@ impl<'tcx> Validator<'_, 'tcx> {
363374
364375 // In theory, any zero-sized value could be borrowed
365376 // mutably without consequences. However, only &mut []
366- // is allowed right now, and only in functions .
377+ // is allowed right now.
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 promote 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 ( ) {
@@ -573,6 +586,10 @@ impl<'tcx> Validator<'_, 'tcx> {
573586 if let Some ( def_id) = c. check_static_ptr ( self . tcx ) {
574587 // Only allow statics (not consts) to refer to other statics.
575588 // FIXME(eddyb) does this matter at all for promotion?
589+ // FIXME(RalfJung) it makes little sense to not promote this in `fn`/`const fn`,
590+ // and in `const` this cannot occur anyway. The only concern is that we might
591+ // promote even `let x = &STATIC` which would be useless, but this applies to
592+ // promotion inside statics as well.
576593 let is_static = matches ! ( self . const_kind, Some ( hir:: ConstContext :: Static ( _) ) ) ;
577594 if !is_static {
578595 return Err ( Unpromotable ) ;
@@ -591,20 +608,20 @@ impl<'tcx> Validator<'_, 'tcx> {
591608
592609 fn validate_rvalue ( & self , rvalue : & Rvalue < ' tcx > ) -> Result < ( ) , Unpromotable > {
593610 match * rvalue {
594- Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) if self . const_kind . is_none ( ) => {
611+ Rvalue :: Cast ( CastKind :: Misc , ref operand, cast_ty) => {
595612 let operand_ty = operand. ty ( self . body , self . tcx ) ;
596613 let cast_in = CastTy :: from_ty ( operand_ty) . expect ( "bad input type for cast" ) ;
597614 let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
598615 match ( cast_in, cast_out) {
599616 ( CastTy :: Ptr ( _) | CastTy :: FnPtr , CastTy :: Int ( _) ) => {
600- // in normal functions, mark such casts as not promotable
617+ // ptr-to-int casts are not possible in consts and thus not promotable
601618 return Err ( Unpromotable ) ;
602619 }
603620 _ => { }
604621 }
605622 }
606623
607- Rvalue :: BinaryOp ( op, ref lhs, _) if self . const_kind . is_none ( ) => {
624+ Rvalue :: BinaryOp ( op, ref lhs, _) => {
608625 if let ty:: RawPtr ( _) | ty:: FnPtr ( ..) = lhs. ty ( self . body , self . tcx ) . kind ( ) {
609626 assert ! (
610627 op == BinOp :: Eq
@@ -616,13 +633,14 @@ impl<'tcx> Validator<'_, 'tcx> {
616633 || op == BinOp :: Offset
617634 ) ;
618635
619- // raw pointer operations are not allowed inside promoteds
636+ // raw pointer operations are not allowed inside consts and thus not promotable
620637 return Err ( Unpromotable ) ;
621638 }
622639 }
623640
624641 Rvalue :: NullaryOp ( NullOp :: Box , _) => return Err ( Unpromotable ) ,
625642
643+ // FIXME(RalfJung): the rest is *implicitly considered promotable*... that seems dangerous.
626644 _ => { }
627645 }
628646
@@ -644,8 +662,8 @@ impl<'tcx> Validator<'_, 'tcx> {
644662 }
645663
646664 Rvalue :: AddressOf ( _, place) => {
647- // Raw reborrows can come from reference to pointer coercions,
648- // so are allowed .
665+ // We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is
666+ // no problem, only using it is .
649667 if let [ proj_base @ .., ProjectionElem :: Deref ] = place. projection . as_ref ( ) {
650668 let base_ty = Place :: ty_from ( place. local , proj_base, self . body , self . tcx ) . ty ;
651669 if let ty:: Ref ( ..) = base_ty. kind ( ) {
@@ -664,12 +682,10 @@ impl<'tcx> Validator<'_, 'tcx> {
664682
665683 // In theory, any zero-sized value could be borrowed
666684 // mutably without consequences. However, only &mut []
667- // is allowed right now, and only in functions .
685+ // is allowed right now.
668686 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.
671687 match len. try_eval_usize ( self . tcx , self . param_env ) {
672- Some ( 0 ) if self . const_kind . is_none ( ) => { }
688+ Some ( 0 ) => { }
673689 _ => return Err ( Unpromotable ) ,
674690 }
675691 } else {
@@ -734,14 +750,7 @@ impl<'tcx> Validator<'_, 'tcx> {
734750 ) -> Result < ( ) , Unpromotable > {
735751 let fn_ty = callee. ty ( self . body , self . tcx ) ;
736752
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 {
753+ if !self . explicit && self . maybe_runtime ( ) {
745754 if let ty:: FnDef ( def_id, _) = * fn_ty. kind ( ) {
746755 // Never promote runtime `const fn` calls of
747756 // functions without `#[rustc_promotable]`.
0 commit comments