@@ -415,10 +415,11 @@ impl<'tcx> Validator<'_, 'tcx> {
415415 // FIXME(eddyb) maybe cache this?
416416 fn validate_local ( & self , local : Local ) -> Result < ( ) , Unpromotable > {
417417 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 ( ) ;
419420
420421 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 ] ;
422423 match & statement. kind {
423424 StatementKind :: Assign ( box ( _, rhs) ) => self . validate_rvalue ( rhs) ,
424425 _ => {
@@ -430,7 +431,7 @@ impl<'tcx> Validator<'_, 'tcx> {
430431 }
431432 }
432433 } else {
433- let terminator = self . body [ loc . block ] . terminator ( ) ;
434+ let terminator = block. terminator ( ) ;
434435 match & terminator. kind {
435436 TerminatorKind :: Call { func, args, .. } => self . validate_call ( func, args) ,
436437 TerminatorKind :: Yield { .. } => Err ( Unpromotable ) ,
@@ -452,22 +453,15 @@ impl<'tcx> Validator<'_, 'tcx> {
452453 match elem {
453454 ProjectionElem :: Deref => {
454455 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.
459457 // Discussion can be found at
460458 // 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 ( ) {
464460 // This is a special treatment for cases like *&STATIC where STATIC is a
465461 // global static variable.
466462 // This pattern is generated only when global static variables are directly
467463 // 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] {
471465 let def_stmt = self . body [ location. block ]
472466 . statements
473467 . get ( location. statement_index ) ;
@@ -505,6 +499,50 @@ impl<'tcx> Validator<'_, 'tcx> {
505499 ProjectionElem :: ConstantIndex { .. } | ProjectionElem :: Subslice { .. } => { }
506500
507501 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+ }
508546 self . validate_local ( local) ?;
509547 }
510548
@@ -589,9 +627,7 @@ impl<'tcx> Validator<'_, 'tcx> {
589627
590628 fn validate_rvalue ( & self , rvalue : & Rvalue < ' tcx > ) -> Result < ( ) , Unpromotable > {
591629 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, _) => {
595631 self . validate_operand ( operand) ?;
596632 }
597633
@@ -616,10 +652,26 @@ impl<'tcx> Validator<'_, 'tcx> {
616652 self . validate_operand ( operand) ?;
617653 }
618654
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+
619669 Rvalue :: BinaryOp ( op, lhs, rhs) | Rvalue :: CheckedBinaryOp ( op, lhs, rhs) => {
620670 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.
623675 assert ! ( matches!(
624676 op,
625677 BinOp :: Eq
@@ -634,7 +686,22 @@ impl<'tcx> Validator<'_, 'tcx> {
634686 }
635687
636688 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.
638705 BinOp :: Eq
639706 | BinOp :: Ne
640707 | BinOp :: Le
@@ -645,8 +712,6 @@ impl<'tcx> Validator<'_, 'tcx> {
645712 | BinOp :: Add
646713 | BinOp :: Sub
647714 | BinOp :: Mul
648- | BinOp :: Div
649- | BinOp :: Rem
650715 | BinOp :: BitXor
651716 | BinOp :: BitAnd
652717 | BinOp :: BitOr
@@ -658,11 +723,6 @@ impl<'tcx> Validator<'_, 'tcx> {
658723 self . validate_operand ( rhs) ?;
659724 }
660725
661- Rvalue :: NullaryOp ( op, _) => match op {
662- NullOp :: Box => return Err ( Unpromotable ) ,
663- NullOp :: SizeOf => { }
664- } ,
665-
666726 Rvalue :: AddressOf ( _, place) => {
667727 // We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is
668728 // no problem, only using it is.
0 commit comments