@@ -297,6 +297,17 @@ impl std::ops::Deref for Validator<'a, 'tcx> {
297
297
struct Unpromotable ;
298
298
299
299
impl < ' 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
+
300
311
fn validate_candidate ( & self , candidate : Candidate ) -> Result < ( ) , Unpromotable > {
301
312
match candidate {
302
313
Candidate :: Ref ( loc) => {
@@ -365,10 +376,8 @@ impl<'tcx> Validator<'_, 'tcx> {
365
376
// mutably without consequences. However, only &mut []
366
377
// is allowed right now, and only in functions.
367
378
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.
370
379
match len. try_eval_usize ( self . tcx , self . param_env ) {
371
- Some ( 0 ) if self . const_kind . is_none ( ) => { }
380
+ Some ( 0 ) => { }
372
381
_ => return Err ( Unpromotable ) ,
373
382
}
374
383
} else {
@@ -495,9 +504,10 @@ impl<'tcx> Validator<'_, 'tcx> {
495
504
match place {
496
505
PlaceRef { local, projection : [ ] } => self . validate_local ( local) ,
497
506
PlaceRef { local, projection : [ proj_base @ .., elem] } => {
507
+ // Validate topmost projection, then recurse.
498
508
match * elem {
499
509
ProjectionElem :: Deref => {
500
- let mut not_promotable = true ;
510
+ let mut promotable = false ;
501
511
// This is a special treatment for cases like *&STATIC where STATIC is a
502
512
// global static variable.
503
513
// This pattern is generated only when global static variables are directly
@@ -512,6 +522,9 @@ impl<'tcx> Validator<'_, 'tcx> {
512
522
} ) = def_stmt
513
523
{
514
524
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.
515
528
if let Some ( hir:: ConstContext :: Static ( ..) ) = self . const_kind {
516
529
// The `is_empty` predicate is introduced to exclude the case
517
530
// where the projection operations are [ .field, * ].
@@ -524,13 +537,13 @@ impl<'tcx> Validator<'_, 'tcx> {
524
537
if proj_base. is_empty ( )
525
538
&& !self . tcx . is_thread_local_static ( did)
526
539
{
527
- not_promotable = false ;
540
+ promotable = true ;
528
541
}
529
542
}
530
543
}
531
544
}
532
545
}
533
- if not_promotable {
546
+ if !promotable {
534
547
return Err ( Unpromotable ) ;
535
548
}
536
549
}
@@ -545,7 +558,7 @@ impl<'tcx> Validator<'_, 'tcx> {
545
558
}
546
559
547
560
ProjectionElem :: Field ( ..) => {
548
- if self . const_kind . is_none ( ) {
561
+ if self . maybe_runtime ( ) {
549
562
let base_ty =
550
563
Place :: ty_from ( place. local , proj_base, self . body , self . tcx ) . ty ;
551
564
if let Some ( def) = base_ty. ty_adt_def ( ) {
@@ -571,13 +584,6 @@ impl<'tcx> Validator<'_, 'tcx> {
571
584
// `validate_rvalue` upon access.
572
585
Operand :: Constant ( c) => {
573
586
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
-
581
587
let is_thread_local = self . tcx . is_thread_local_static ( def_id) ;
582
588
if is_thread_local {
583
589
return Err ( Unpromotable ) ;
@@ -591,20 +597,20 @@ impl<'tcx> Validator<'_, 'tcx> {
591
597
592
598
fn validate_rvalue ( & self , rvalue : & Rvalue < ' tcx > ) -> Result < ( ) , Unpromotable > {
593
599
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 ( ) => {
595
601
let operand_ty = operand. ty ( self . body , self . tcx ) ;
596
602
let cast_in = CastTy :: from_ty ( operand_ty) . expect ( "bad input type for cast" ) ;
597
603
let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
598
604
match ( cast_in, cast_out) {
599
605
( CastTy :: Ptr ( _) | CastTy :: FnPtr , CastTy :: Int ( _) ) => {
600
- // in normal functions, mark such casts as not promotable
606
+ // ptr-to-int casts are not promotable
601
607
return Err ( Unpromotable ) ;
602
608
}
603
609
_ => { }
604
610
}
605
611
}
606
612
607
- Rvalue :: BinaryOp ( op, ref lhs, _) if self . const_kind . is_none ( ) => {
613
+ Rvalue :: BinaryOp ( op, ref lhs, _) if self . maybe_runtime ( ) => {
608
614
if let ty:: RawPtr ( _) | ty:: FnPtr ( ..) = lhs. ty ( self . body , self . tcx ) . kind ( ) {
609
615
assert ! (
610
616
op == BinOp :: Eq
@@ -623,6 +629,7 @@ impl<'tcx> Validator<'_, 'tcx> {
623
629
624
630
Rvalue :: NullaryOp ( NullOp :: Box , _) => return Err ( Unpromotable ) ,
625
631
632
+ // FIXME(RalfJung): the rest is *implicitly considered promotable*... that seems dangerous.
626
633
_ => { }
627
634
}
628
635
@@ -644,8 +651,8 @@ impl<'tcx> Validator<'_, 'tcx> {
644
651
}
645
652
646
653
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 .
649
656
if let [ proj_base @ .., ProjectionElem :: Deref ] = place. projection . as_ref ( ) {
650
657
let base_ty = Place :: ty_from ( place. local , proj_base, self . body , self . tcx ) . ty ;
651
658
if let ty:: Ref ( ..) = base_ty. kind ( ) {
@@ -666,10 +673,8 @@ impl<'tcx> Validator<'_, 'tcx> {
666
673
// mutably without consequences. However, only &mut []
667
674
// is allowed right now, and only in functions.
668
675
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.
671
676
match len. try_eval_usize ( self . tcx , self . param_env ) {
672
- Some ( 0 ) if self . const_kind . is_none ( ) => { }
677
+ Some ( 0 ) => { }
673
678
_ => return Err ( Unpromotable ) ,
674
679
}
675
680
} else {
@@ -734,14 +739,7 @@ impl<'tcx> Validator<'_, 'tcx> {
734
739
) -> Result < ( ) , Unpromotable > {
735
740
let fn_ty = callee. ty ( self . body , self . tcx ) ;
736
741
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 ( ) {
745
743
if let ty:: FnDef ( def_id, _) = * fn_ty. kind ( ) {
746
744
// Never promote runtime `const fn` calls of
747
745
// functions without `#[rustc_promotable]`.
0 commit comments