@@ -326,6 +326,8 @@ struct ConstPropagator<'mir, 'tcx> {
326
326
// Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
327
327
// the last known `SourceInfo` here and just keep revisiting it.
328
328
source_info : Option < SourceInfo > ,
329
+ // Locals we need to forget at the end of the current block
330
+ locals_of_current_block : BitSet < Local > ,
329
331
}
330
332
331
333
impl < ' mir , ' tcx > LayoutOf for ConstPropagator < ' mir , ' tcx > {
@@ -395,6 +397,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
395
397
//FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
396
398
local_decls : body. local_decls . clone ( ) ,
397
399
source_info : None ,
400
+ locals_of_current_block : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
398
401
}
399
402
}
400
403
@@ -409,8 +412,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
409
412
}
410
413
}
411
414
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] =
414
419
LocalState { value : LocalValue :: Uninitialized , layout : Cell :: new ( None ) } ;
415
420
}
416
421
@@ -756,6 +761,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
756
761
enum ConstPropMode {
757
762
/// The `Local` can be propagated into and reads of this `Local` can also be propagated.
758
763
FullConstProp ,
764
+ /// The `Local` can only be propagated into and from its own block.
765
+ OnlyInsideOwnBlock ,
759
766
/// The `Local` can be propagated into but reads cannot be propagated.
760
767
OnlyPropagateInto ,
761
768
/// No propagation is allowed at all.
@@ -787,10 +794,18 @@ impl CanConstProp {
787
794
// lint for x != y
788
795
// FIXME(oli-obk): lint variables until they are used in a condition
789
796
// 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 {
792
798
* 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
+ ) ;
794
809
}
795
810
}
796
811
cpv. visit_body ( & body) ;
@@ -858,25 +873,35 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
858
873
if let Some ( local) = place. as_local ( ) {
859
874
let can_const_prop = self . can_const_prop [ local] ;
860
875
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.
864
879
if let Some ( value) = self . get_const ( local) {
865
880
if self . should_const_prop ( value) {
866
881
trace ! ( "replacing {:?} with {:?}" , rval, value) ;
867
882
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
+ {
870
886
trace ! ( "propagated into {:?}" , local) ;
871
887
}
872
888
}
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
+ }
873
896
}
874
897
}
875
898
}
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
+ {
877
902
trace ! ( "can't propagate into {:?}" , local) ;
878
903
if local != RETURN_PLACE {
879
- self . remove_const ( local) ;
904
+ Self :: remove_const ( & mut self . ecx , local) ;
880
905
}
881
906
}
882
907
}
@@ -915,7 +940,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
915
940
// doesn't use the invalid value
916
941
match cond {
917
942
Operand :: Move ( ref place) | Operand :: Copy ( ref place) => {
918
- self . remove_const ( place. local ) ;
943
+ Self :: remove_const ( & mut self . ecx , place. local ) ;
919
944
}
920
945
Operand :: Constant ( _) => { }
921
946
}
@@ -992,5 +1017,13 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
992
1017
//FIXME(wesleywiser) Call does have Operands that could be const-propagated
993
1018
TerminatorKind :: Call { .. } => { }
994
1019
}
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 ( ) ;
995
1028
}
996
1029
}
0 commit comments