@@ -349,8 +349,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
349
349
}
350
350
}
351
351
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 ( ) ;
354
354
355
355
// Try to read the local as an immediate so that if it is representable as a scalar, we can
356
356
// handle it as such, but otherwise, just return the value as is.
@@ -772,13 +772,25 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
772
772
fn visit_local ( & mut self , & local: & Local , context : PlaceContext , _: Location ) {
773
773
use rustc_middle:: mir:: visit:: PlaceContext :: * ;
774
774
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 ) => {
779
779
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
+ }
782
794
}
783
795
}
784
796
// Reading constants is allowed an arbitrary number of times
@@ -787,12 +799,6 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
787
799
| NonMutatingUse ( NonMutatingUseContext :: Inspect )
788
800
| NonMutatingUse ( NonMutatingUseContext :: Projection )
789
801
| 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
- }
796
802
_ => {
797
803
trace ! ( "local {:?} can't be propagaged because it's used: {:?}" , local, context) ;
798
804
self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
@@ -826,40 +832,50 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
826
832
if let StatementKind :: Assign ( box ( place, ref mut rval) ) = statement. kind {
827
833
let place_ty: Ty < ' tcx > = place. ty ( & self . local_decls , self . tcx ) . ty ;
828
834
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) ;
851
848
}
852
849
}
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
+ }
853
857
}
854
858
}
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
857
861
{
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 ) ;
861
865
}
862
866
}
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 ) ;
863
879
}
864
880
}
865
881
} else {
@@ -993,7 +1009,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
993
1009
arguments are of the variant `Operand::Copy`. This allows us to
994
1010
simplify our handling of `Operands` in this case.
995
1011
*/
996
- if let Some ( l) = opr. place ( ) . and_then ( |p| p . as_local ( ) ) {
1012
+ if let Some ( l) = opr. place ( ) {
997
1013
if let Some ( value) = self . get_const ( l) {
998
1014
if self . should_const_prop ( value) {
999
1015
// FIXME(felix91gr): this code only handles `Scalar` cases.
0 commit comments