@@ -349,8 +349,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
349349 }
350350 }
351351
352- fn get_const ( & self , local : Local ) -> Option < OpTy < ' tcx > > {
353- let op = self . ecx . access_local ( self . ecx . frame ( ) , local , None ) . ok ( ) ;
352+ fn get_const ( & self , place : Place < ' tcx > ) -> Option < OpTy < ' tcx > > {
353+ let op = self . ecx . eval_place_to_op ( place , None ) . ok ( ) ;
354354
355355 // Try to read the local as an immediate so that if it is representable as a scalar, we can
356356 // handle it as such, but otherwise, just return the value as is.
@@ -772,13 +772,25 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
772772 fn visit_local ( & mut self , & local: & Local , context : PlaceContext , _: Location ) {
773773 use rustc_middle:: mir:: visit:: PlaceContext :: * ;
774774 match context {
775- // Constants must have at most one write
776- // FIXME(oli-obk): we could be more powerful here, if the multiple writes
777- // only occur in independent execution paths
778- MutatingUse ( MutatingUseContext :: Store ) => {
775+ // Projections are fine, because `&mut foo.x` will be caught by
776+ // `MutatingUseContext::Borrow` elsewhere.
777+ MutatingUse ( MutatingUseContext :: Projection )
778+ | MutatingUse ( MutatingUseContext :: Store ) => {
779779 if !self . found_assignment . insert ( local) {
780- trace ! ( "local {:?} can't be propagated because of multiple assignments" , local) ;
781- self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
780+ match & mut self . can_const_prop [ local] {
781+ // If the local can only get propagated in its own block, then we don't have
782+ // to worry about multiple assignments, as we'll nuke the const state at the
783+ // end of the block anyway, and inside the block we overwrite previous
784+ // states as applicable.
785+ ConstPropMode :: OnlyInsideOwnBlock => { }
786+ other => {
787+ trace ! (
788+ "local {:?} can't be propagated because of multiple assignments" ,
789+ local,
790+ ) ;
791+ * other = ConstPropMode :: NoPropagation ;
792+ }
793+ }
782794 }
783795 }
784796 // Reading constants is allowed an arbitrary number of times
@@ -787,12 +799,6 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
787799 | NonMutatingUse ( NonMutatingUseContext :: Inspect )
788800 | NonMutatingUse ( NonMutatingUseContext :: Projection )
789801 | NonUse ( _) => { }
790- // FIXME(felix91gr): explain the reasoning behind this
791- MutatingUse ( MutatingUseContext :: Projection ) => {
792- if self . local_kinds [ local] != LocalKind :: Temp {
793- self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
794- }
795- }
796802 _ => {
797803 trace ! ( "local {:?} can't be propagaged because it's used: {:?}" , local, context) ;
798804 self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
@@ -826,40 +832,50 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
826832 if let StatementKind :: Assign ( box ( place, ref mut rval) ) = statement. kind {
827833 let place_ty: Ty < ' tcx > = place. ty ( & self . local_decls , self . tcx ) . ty ;
828834 if let Ok ( place_layout) = self . tcx . layout_of ( self . param_env . and ( place_ty) ) {
829- if let Some ( local) = place. as_local ( ) {
830- let can_const_prop = self . can_const_prop [ local] ;
831- if let Some ( ( ) ) = self . const_prop ( rval, place_layout, source_info, place) {
832- if can_const_prop != ConstPropMode :: NoPropagation {
833- // This will return None for Locals that are from other blocks,
834- // so it should be okay to propagate from here on down.
835- if let Some ( value) = self . get_const ( local) {
836- if self . should_const_prop ( value) {
837- trace ! ( "replacing {:?} with {:?}" , rval, value) ;
838- self . replace_with_const ( rval, value, statement. source_info ) ;
839- if can_const_prop == ConstPropMode :: FullConstProp
840- || can_const_prop == ConstPropMode :: OnlyInsideOwnBlock
841- {
842- trace ! ( "propagated into {:?}" , local) ;
843- }
844- }
845- if can_const_prop == ConstPropMode :: OnlyInsideOwnBlock {
846- trace ! (
847- "found local restricted to its block. Will remove it from const-prop after block is finished. Local: {:?}" ,
848- local
849- ) ;
850- self . locals_of_current_block . insert ( local) ;
835+ let can_const_prop = self . can_const_prop [ place. local ] ;
836+ if let Some ( ( ) ) = self . const_prop ( rval, place_layout, source_info, place) {
837+ if can_const_prop != ConstPropMode :: NoPropagation {
838+ // This will return None for variables that are from other blocks,
839+ // so it should be okay to propagate from here on down.
840+ if let Some ( value) = self . get_const ( place) {
841+ if self . should_const_prop ( value) {
842+ trace ! ( "replacing {:?} with {:?}" , rval, value) ;
843+ self . replace_with_const ( rval, value, statement. source_info ) ;
844+ if can_const_prop == ConstPropMode :: FullConstProp
845+ || can_const_prop == ConstPropMode :: OnlyInsideOwnBlock
846+ {
847+ trace ! ( "propagated into {:?}" , place) ;
851848 }
852849 }
850+ if can_const_prop == ConstPropMode :: OnlyInsideOwnBlock {
851+ trace ! (
852+ "found local restricted to its block. Will remove it from const-prop after block is finished. Local: {:?}" ,
853+ place. local
854+ ) ;
855+ self . locals_of_current_block . insert ( place. local ) ;
856+ }
853857 }
854858 }
855- if self . can_const_prop [ local ] == ConstPropMode :: OnlyPropagateInto
856- || self . can_const_prop [ local ] == ConstPropMode :: NoPropagation
859+ if can_const_prop == ConstPropMode :: OnlyPropagateInto
860+ || can_const_prop == ConstPropMode :: NoPropagation
857861 {
858- trace ! ( "can't propagate into {:?}" , local ) ;
859- if local != RETURN_PLACE {
860- Self :: remove_const ( & mut self . ecx , local) ;
862+ trace ! ( "can't propagate into {:?}" , place ) ;
863+ if place . local != RETURN_PLACE {
864+ Self :: remove_const ( & mut self . ecx , place . local ) ;
861865 }
862866 }
867+ } else {
868+ // Const prop failed, so erase the destination, ensuring that whatever happens
869+ // from here on, does not know about the previous value.
870+ // This is important in case we have
871+ // ```rust
872+ // let mut x = 42;
873+ // x = SOME_MUTABLE_STATIC;
874+ // // x must now be undefined
875+ // ```
876+ // FIXME: we overzealously erase the entire local, because that's easier to
877+ // implement.
878+ Self :: remove_const ( & mut self . ecx , place. local ) ;
863879 }
864880 }
865881 } else {
@@ -993,7 +1009,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
9931009 arguments are of the variant `Operand::Copy`. This allows us to
9941010 simplify our handling of `Operands` in this case.
9951011 */
996- if let Some ( l) = opr. place ( ) . and_then ( |p| p . as_local ( ) ) {
1012+ if let Some ( l) = opr. place ( ) {
9971013 if let Some ( value) = self . get_const ( l) {
9981014 if self . should_const_prop ( value) {
9991015 // FIXME(felix91gr): this code only handles `Scalar` cases.
0 commit comments