@@ -415,10 +415,11 @@ impl<'tcx> Validator<'_, 'tcx> {
415
415
// FIXME(eddyb) maybe cache this?
416
416
fn validate_local ( & self , local : Local ) -> Result < ( ) , Unpromotable > {
417
417
if let TempState :: Defined { location : loc, .. } = self . temps [ local] {
418
- let num_stmts = self . body [ loc. block ] . statements . len ( ) ;
418
+ let block = & self . body [ loc. block ] ;
419
+ let num_stmts = block. statements . len ( ) ;
419
420
420
421
if loc. statement_index < num_stmts {
421
- let statement = & self . body [ loc . block ] . statements [ loc. statement_index ] ;
422
+ let statement = & block. statements [ loc. statement_index ] ;
422
423
match & statement. kind {
423
424
StatementKind :: Assign ( box ( _, rhs) ) => self . validate_rvalue ( rhs) ,
424
425
_ => {
@@ -430,7 +431,7 @@ impl<'tcx> Validator<'_, 'tcx> {
430
431
}
431
432
}
432
433
} else {
433
- let terminator = self . body [ loc . block ] . terminator ( ) ;
434
+ let terminator = block. terminator ( ) ;
434
435
match & terminator. kind {
435
436
TerminatorKind :: Call { func, args, .. } => self . validate_call ( func, args) ,
436
437
TerminatorKind :: Yield { .. } => Err ( Unpromotable ) ,
@@ -452,22 +453,15 @@ impl<'tcx> Validator<'_, 'tcx> {
452
453
match elem {
453
454
ProjectionElem :: Deref => {
454
455
let mut promotable = false ;
455
- // The `is_empty` predicate is introduced to exclude the case
456
- // where the projection operations are [ .field, * ].
457
- // The reason is because promotion will be illegal if field
458
- // accesses precede the dereferencing.
456
+ // We need to make sure this is a `Deref` of a local with no further projections.
459
457
// Discussion can be found at
460
458
// https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
461
- // There may be opportunity for generalization, but this needs to be
462
- // accounted for.
463
- if place_base. projection . is_empty ( ) {
459
+ if let Some ( local) = place_base. as_local ( ) {
464
460
// This is a special treatment for cases like *&STATIC where STATIC is a
465
461
// global static variable.
466
462
// This pattern is generated only when global static variables are directly
467
463
// accessed and is qualified for promotion safely.
468
- if let TempState :: Defined { location, .. } =
469
- self . temps [ place_base. local ]
470
- {
464
+ if let TempState :: Defined { location, .. } = self . temps [ local] {
471
465
let def_stmt = self . body [ location. block ]
472
466
. statements
473
467
. get ( location. statement_index ) ;
@@ -505,6 +499,50 @@ impl<'tcx> Validator<'_, 'tcx> {
505
499
ProjectionElem :: ConstantIndex { .. } | ProjectionElem :: Subslice { .. } => { }
506
500
507
501
ProjectionElem :: Index ( local) => {
502
+ if !self . explicit {
503
+ let mut promotable = false ;
504
+ // Only accept if we can predict the index and are indexing an array.
505
+ let val = if let TempState :: Defined { location : loc, .. } =
506
+ self . temps [ local]
507
+ {
508
+ let block = & self . body [ loc. block ] ;
509
+ if loc. statement_index < block. statements . len ( ) {
510
+ let statement = & block. statements [ loc. statement_index ] ;
511
+ match & statement. kind {
512
+ StatementKind :: Assign ( box (
513
+ _,
514
+ Rvalue :: Use ( Operand :: Constant ( c) ) ,
515
+ ) ) => c. literal . try_eval_usize ( self . tcx , self . param_env ) ,
516
+ _ => None ,
517
+ }
518
+ } else {
519
+ None
520
+ }
521
+ } else {
522
+ None
523
+ } ;
524
+ if let Some ( idx) = val {
525
+ // Determine the type of the thing we are indexing.
526
+ let ty = place_base. ty ( self . body , self . tcx ) . ty ;
527
+ match ty. kind ( ) {
528
+ ty:: Array ( _, len) => {
529
+ // It's an array; determine its length.
530
+ if let Some ( len) =
531
+ len. try_eval_usize ( self . tcx , self . param_env )
532
+ {
533
+ // If the index is in-bounds, go ahead.
534
+ if idx < len {
535
+ promotable = true ;
536
+ }
537
+ }
538
+ }
539
+ _ => { }
540
+ }
541
+ }
542
+ if !promotable {
543
+ return Err ( Unpromotable ) ;
544
+ }
545
+ }
508
546
self . validate_local ( local) ?;
509
547
}
510
548
@@ -589,9 +627,7 @@ impl<'tcx> Validator<'_, 'tcx> {
589
627
590
628
fn validate_rvalue ( & self , rvalue : & Rvalue < ' tcx > ) -> Result < ( ) , Unpromotable > {
591
629
match rvalue {
592
- Rvalue :: Use ( operand)
593
- | Rvalue :: Repeat ( operand, _)
594
- | Rvalue :: UnaryOp ( UnOp :: Not | UnOp :: Neg , operand) => {
630
+ Rvalue :: Use ( operand) | Rvalue :: Repeat ( operand, _) => {
595
631
self . validate_operand ( operand) ?;
596
632
}
597
633
@@ -616,10 +652,26 @@ impl<'tcx> Validator<'_, 'tcx> {
616
652
self . validate_operand ( operand) ?;
617
653
}
618
654
655
+ Rvalue :: NullaryOp ( op, _) => match op {
656
+ NullOp :: Box => return Err ( Unpromotable ) ,
657
+ NullOp :: SizeOf => { }
658
+ } ,
659
+
660
+ Rvalue :: UnaryOp ( op, operand) => {
661
+ match op {
662
+ // These operations can never fail.
663
+ UnOp :: Neg | UnOp :: Not => { }
664
+ }
665
+
666
+ self . validate_operand ( operand) ?;
667
+ }
668
+
619
669
Rvalue :: BinaryOp ( op, lhs, rhs) | Rvalue :: CheckedBinaryOp ( op, lhs, rhs) => {
620
670
let op = * op;
621
- if let ty:: RawPtr ( _) | ty:: FnPtr ( ..) = lhs. ty ( self . body , self . tcx ) . kind ( ) {
622
- // raw pointer operations are not allowed inside consts and thus not promotable
671
+ let lhs_ty = lhs. ty ( self . body , self . tcx ) ;
672
+
673
+ if let ty:: RawPtr ( _) | ty:: FnPtr ( ..) = lhs_ty. kind ( ) {
674
+ // Raw and fn pointer operations are not allowed inside consts and thus not promotable.
623
675
assert ! ( matches!(
624
676
op,
625
677
BinOp :: Eq
@@ -634,7 +686,22 @@ impl<'tcx> Validator<'_, 'tcx> {
634
686
}
635
687
636
688
match op {
637
- // FIXME: reject operations that can fail -- namely, division and modulo.
689
+ BinOp :: Div | BinOp :: Rem => {
690
+ if !self . explicit && lhs_ty. is_integral ( ) {
691
+ // Integer division: the RHS must be a non-zero const.
692
+ let const_val = match rhs {
693
+ Operand :: Constant ( c) => {
694
+ c. literal . try_eval_bits ( self . tcx , self . param_env , lhs_ty)
695
+ }
696
+ _ => None ,
697
+ } ;
698
+ match const_val {
699
+ Some ( x) if x != 0 => { } // okay
700
+ _ => return Err ( Unpromotable ) , // value not known or 0 -- not okay
701
+ }
702
+ }
703
+ }
704
+ // The remaining operations can never fail.
638
705
BinOp :: Eq
639
706
| BinOp :: Ne
640
707
| BinOp :: Le
@@ -645,8 +712,6 @@ impl<'tcx> Validator<'_, 'tcx> {
645
712
| BinOp :: Add
646
713
| BinOp :: Sub
647
714
| BinOp :: Mul
648
- | BinOp :: Div
649
- | BinOp :: Rem
650
715
| BinOp :: BitXor
651
716
| BinOp :: BitAnd
652
717
| BinOp :: BitOr
@@ -658,11 +723,6 @@ impl<'tcx> Validator<'_, 'tcx> {
658
723
self . validate_operand ( rhs) ?;
659
724
}
660
725
661
- Rvalue :: NullaryOp ( op, _) => match op {
662
- NullOp :: Box => return Err ( Unpromotable ) ,
663
- NullOp :: SizeOf => { }
664
- } ,
665
-
666
726
Rvalue :: AddressOf ( _, place) => {
667
727
// We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is
668
728
// no problem, only using it is.
0 commit comments