@@ -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) => {
@@ -363,12 +374,10 @@ impl<'tcx> Validator<'_, 'tcx> {
363
374
364
375
// In theory, any zero-sized value could be borrowed
365
376
// mutably without consequences. However, only &mut []
366
- // is allowed right now, and only in functions .
377
+ // is allowed right now.
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 promote 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 ( ) {
@@ -573,6 +586,10 @@ impl<'tcx> Validator<'_, 'tcx> {
573
586
if let Some ( def_id) = c. check_static_ptr ( self . tcx ) {
574
587
// Only allow statics (not consts) to refer to other statics.
575
588
// 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.
576
593
let is_static = matches ! ( self . const_kind, Some ( hir:: ConstContext :: Static ( _) ) ) ;
577
594
if !is_static {
578
595
return Err ( Unpromotable ) ;
@@ -591,20 +608,20 @@ impl<'tcx> Validator<'_, 'tcx> {
591
608
592
609
fn validate_rvalue ( & self , rvalue : & Rvalue < ' tcx > ) -> Result < ( ) , Unpromotable > {
593
610
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) => {
595
612
let operand_ty = operand. ty ( self . body , self . tcx ) ;
596
613
let cast_in = CastTy :: from_ty ( operand_ty) . expect ( "bad input type for cast" ) ;
597
614
let cast_out = CastTy :: from_ty ( cast_ty) . expect ( "bad output type for cast" ) ;
598
615
match ( cast_in, cast_out) {
599
616
( 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
601
618
return Err ( Unpromotable ) ;
602
619
}
603
620
_ => { }
604
621
}
605
622
}
606
623
607
- Rvalue :: BinaryOp ( op, ref lhs, _) if self . const_kind . is_none ( ) => {
624
+ Rvalue :: BinaryOp ( op, ref lhs, _) => {
608
625
if let ty:: RawPtr ( _) | ty:: FnPtr ( ..) = lhs. ty ( self . body , self . tcx ) . kind ( ) {
609
626
assert ! (
610
627
op == BinOp :: Eq
@@ -616,13 +633,14 @@ impl<'tcx> Validator<'_, 'tcx> {
616
633
|| op == BinOp :: Offset
617
634
) ;
618
635
619
- // raw pointer operations are not allowed inside promoteds
636
+ // raw pointer operations are not allowed inside consts and thus not promotable
620
637
return Err ( Unpromotable ) ;
621
638
}
622
639
}
623
640
624
641
Rvalue :: NullaryOp ( NullOp :: Box , _) => return Err ( Unpromotable ) ,
625
642
643
+ // FIXME(RalfJung): the rest is *implicitly considered promotable*... that seems dangerous.
626
644
_ => { }
627
645
}
628
646
@@ -644,8 +662,8 @@ impl<'tcx> Validator<'_, 'tcx> {
644
662
}
645
663
646
664
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 .
649
667
if let [ proj_base @ .., ProjectionElem :: Deref ] = place. projection . as_ref ( ) {
650
668
let base_ty = Place :: ty_from ( place. local , proj_base, self . body , self . tcx ) . ty ;
651
669
if let ty:: Ref ( ..) = base_ty. kind ( ) {
@@ -664,12 +682,10 @@ impl<'tcx> Validator<'_, 'tcx> {
664
682
665
683
// In theory, any zero-sized value could be borrowed
666
684
// mutably without consequences. However, only &mut []
667
- // is allowed right now, and only in functions .
685
+ // is allowed right now.
668
686
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
687
match len. try_eval_usize ( self . tcx , self . param_env ) {
672
- Some ( 0 ) if self . const_kind . is_none ( ) => { }
688
+ Some ( 0 ) => { }
673
689
_ => return Err ( Unpromotable ) ,
674
690
}
675
691
} else {
@@ -734,14 +750,7 @@ impl<'tcx> Validator<'_, 'tcx> {
734
750
) -> Result < ( ) , Unpromotable > {
735
751
let fn_ty = callee. ty ( self . body , self . tcx ) ;
736
752
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 ( ) {
745
754
if let ty:: FnDef ( def_id, _) = * fn_ty. kind ( ) {
746
755
// Never promote runtime `const fn` calls of
747
756
// functions without `#[rustc_promotable]`.
0 commit comments