@@ -326,6 +326,8 @@ struct ConstPropagator<'mir, 'tcx> {
326326 // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
327327 // the last known `SourceInfo` here and just keep revisiting it.
328328 source_info : Option < SourceInfo > ,
329+ // Locals we need to forget at the end of the current block
330+ locals_of_current_block : BitSet < Local > ,
329331}
330332
331333impl < ' mir , ' tcx > LayoutOf for ConstPropagator < ' mir , ' tcx > {
@@ -395,6 +397,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
395397 //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
396398 local_decls : body. local_decls . clone ( ) ,
397399 source_info : None ,
400+ locals_of_current_block : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
398401 }
399402 }
400403
@@ -409,8 +412,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
409412 }
410413 }
411414
412- fn remove_const ( & mut self , local : Local ) {
413- self . ecx . frame_mut ( ) . locals [ local] =
415+ /// Remove `local` from the pool of `Locals`. Allows writing to them,
416+ /// but not reading from them anymore.
417+ fn remove_const ( ecx : & mut InterpCx < ' mir , ' tcx , ConstPropMachine < ' mir , ' tcx > > , local : Local ) {
418+ ecx. frame_mut ( ) . locals [ local] =
414419 LocalState { value : LocalValue :: Uninitialized , layout : Cell :: new ( None ) } ;
415420 }
416421
@@ -756,6 +761,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
756761enum ConstPropMode {
757762 /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
758763 FullConstProp ,
764+ /// The `Local` can only be propagated into and from its own block.
765+ OnlyInsideOwnBlock ,
759766 /// The `Local` can be propagated into but reads cannot be propagated.
760767 OnlyPropagateInto ,
761768 /// No propagation is allowed at all.
@@ -787,10 +794,18 @@ impl CanConstProp {
787794 // lint for x != y
788795 // FIXME(oli-obk): lint variables until they are used in a condition
789796 // FIXME(oli-obk): lint if return value is constant
790- if cpv. local_kinds [ local] == LocalKind :: Arg || cpv. local_kinds [ local] == LocalKind :: Var
791- {
797+ if cpv. local_kinds [ local] == LocalKind :: Arg {
792798 * val = ConstPropMode :: OnlyPropagateInto ;
793- trace ! ( "local {:?} can't be const propagated because it's not a temporary" , local) ;
799+ trace ! (
800+ "local {:?} can't be const propagated because it's a function argument" ,
801+ local
802+ ) ;
803+ } else if cpv. local_kinds [ local] == LocalKind :: Var {
804+ * val = ConstPropMode :: OnlyInsideOwnBlock ;
805+ trace ! (
806+ "local {:?} will only be propagated inside its block, because it's a user variable" ,
807+ local
808+ ) ;
794809 }
795810 }
796811 cpv. visit_body ( & body) ;
@@ -858,25 +873,35 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
858873 if let Some ( local) = place. as_local ( ) {
859874 let can_const_prop = self . can_const_prop [ local] ;
860875 if let Some ( ( ) ) = self . const_prop ( rval, place_layout, source_info, place) {
861- if can_const_prop == ConstPropMode :: FullConstProp
862- || can_const_prop == ConstPropMode :: OnlyPropagateInto
863- {
876+ if can_const_prop != ConstPropMode :: NoPropagation {
877+ // This will return None for Locals that are from other blocks,
878+ // so it should be okay to propagate from here on down.
864879 if let Some ( value) = self . get_const ( local) {
865880 if self . should_const_prop ( value) {
866881 trace ! ( "replacing {:?} with {:?}" , rval, value) ;
867882 self . replace_with_const ( rval, value, statement. source_info ) ;
868-
869- if can_const_prop == ConstPropMode :: FullConstProp {
883+ if can_const_prop == ConstPropMode :: FullConstProp
884+ || can_const_prop == ConstPropMode :: OnlyInsideOwnBlock
885+ {
870886 trace ! ( "propagated into {:?}" , local) ;
871887 }
872888 }
889+ if can_const_prop == ConstPropMode :: OnlyInsideOwnBlock {
890+ trace ! (
891+ "found local restricted to its block. Will remove it from const-prop after block is finished. Local: {:?}" ,
892+ local
893+ ) ;
894+ self . locals_of_current_block . insert ( local) ;
895+ }
873896 }
874897 }
875898 }
876- if self . can_const_prop [ local] != ConstPropMode :: FullConstProp {
899+ if self . can_const_prop [ local] == ConstPropMode :: OnlyPropagateInto
900+ || self . can_const_prop [ local] == ConstPropMode :: NoPropagation
901+ {
877902 trace ! ( "can't propagate into {:?}" , local) ;
878903 if local != RETURN_PLACE {
879- self . remove_const ( local) ;
904+ Self :: remove_const ( & mut self . ecx , local) ;
880905 }
881906 }
882907 }
@@ -915,7 +940,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
915940 // doesn't use the invalid value
916941 match cond {
917942 Operand :: Move ( ref place) | Operand :: Copy ( ref place) => {
918- self . remove_const ( place. local ) ;
943+ Self :: remove_const ( & mut self . ecx , place. local ) ;
919944 }
920945 Operand :: Constant ( _) => { }
921946 }
@@ -992,5 +1017,13 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
9921017 //FIXME(wesleywiser) Call does have Operands that could be const-propagated
9931018 TerminatorKind :: Call { .. } => { }
9941019 }
1020+ // We remove all Locals which are restricted in propagation to their containing blocks.
1021+ // We wouldn't need to clone, but the borrow checker can't see that we're not aliasing
1022+ // the locals_of_current_block field, so we need to clone it first.
1023+ // let ecx = &mut self.ecx;
1024+ for local in self . locals_of_current_block . iter ( ) {
1025+ Self :: remove_const ( & mut self . ecx , local) ;
1026+ }
1027+ self . locals_of_current_block . clear ( ) ;
9951028 }
9961029}
0 commit comments