44use std:: cell:: Cell ;
55
66use rustc_ast:: ast:: Mutability ;
7+ use rustc_data_structures:: fx:: FxHashSet ;
78use rustc_hir:: def:: DefKind ;
89use rustc_hir:: HirId ;
910use rustc_index:: bit_set:: BitSet ;
@@ -28,7 +29,7 @@ use rustc_trait_selection::traits;
2829use crate :: const_eval:: error_to_const_error;
2930use crate :: interpret:: {
3031 self , compile_time_machine, AllocId , Allocation , Frame , ImmTy , Immediate , InterpCx , LocalState ,
31- LocalValue , Memory , MemoryKind , OpTy , Operand as InterpOperand , PlaceTy , Pointer ,
32+ LocalValue , MemPlace , Memory , MemoryKind , OpTy , Operand as InterpOperand , PlaceTy , Pointer ,
3233 ScalarMaybeUninit , StackPopCleanup ,
3334} ;
3435use crate :: transform:: { MirPass , MirSource } ;
@@ -151,11 +152,19 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
151152struct ConstPropMachine < ' mir , ' tcx > {
152153 /// The virtual call stack.
153154 stack : Vec < Frame < ' mir , ' tcx , ( ) , ( ) > > ,
155+ /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end.
156+ written_only_inside_own_block_locals : FxHashSet < Local > ,
157+ /// Locals that need to be cleared after every block terminates.
158+ only_propagate_inside_block_locals : BitSet < Local > ,
154159}
155160
156161impl < ' mir , ' tcx > ConstPropMachine < ' mir , ' tcx > {
157- fn new ( ) -> Self {
158- Self { stack : Vec :: new ( ) }
162+ fn new ( only_propagate_inside_block_locals : BitSet < Local > ) -> Self {
163+ Self {
164+ stack : Vec :: new ( ) ,
165+ written_only_inside_own_block_locals : Default :: default ( ) ,
166+ only_propagate_inside_block_locals,
167+ }
159168 }
160169}
161170
@@ -227,6 +236,18 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
227236 l. access ( )
228237 }
229238
239+ fn access_local_mut < ' a > (
240+ ecx : & ' a mut InterpCx < ' mir , ' tcx , Self > ,
241+ frame : usize ,
242+ local : Local ,
243+ ) -> InterpResult < ' tcx , Result < & ' a mut LocalValue < Self :: PointerTag > , MemPlace < Self :: PointerTag > > >
244+ {
245+ if frame == 0 && ecx. machine . only_propagate_inside_block_locals . contains ( local) {
246+ ecx. machine . written_only_inside_own_block_locals . insert ( local) ;
247+ }
248+ ecx. machine . stack [ frame] . locals [ local] . access_mut ( )
249+ }
250+
230251 fn before_access_global (
231252 _memory_extra : & ( ) ,
232253 _alloc_id : AllocId ,
@@ -274,8 +295,6 @@ struct ConstPropagator<'mir, 'tcx> {
274295 // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
275296 // the last known `SourceInfo` here and just keep revisiting it.
276297 source_info : Option < SourceInfo > ,
277- // Locals we need to forget at the end of the current block
278- locals_of_current_block : BitSet < Local > ,
279298}
280299
281300impl < ' mir , ' tcx > LayoutOf for ConstPropagator < ' mir , ' tcx > {
@@ -313,8 +332,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
313332 let param_env = tcx. param_env ( def_id) . with_reveal_all ( ) ;
314333
315334 let span = tcx. def_span ( def_id) ;
316- let mut ecx = InterpCx :: new ( tcx, span, param_env, ConstPropMachine :: new ( ) , ( ) ) ;
317335 let can_const_prop = CanConstProp :: check ( body) ;
336+ let mut only_propagate_inside_block_locals = BitSet :: new_empty ( can_const_prop. len ( ) ) ;
337+ for ( l, mode) in can_const_prop. iter_enumerated ( ) {
338+ if * mode == ConstPropMode :: OnlyInsideOwnBlock {
339+ only_propagate_inside_block_locals. insert ( l) ;
340+ }
341+ }
342+ let mut ecx = InterpCx :: new (
343+ tcx,
344+ span,
345+ param_env,
346+ ConstPropMachine :: new ( only_propagate_inside_block_locals) ,
347+ ( ) ,
348+ ) ;
318349
319350 let ret = ecx
320351 . layout_of ( body. return_ty ( ) . subst ( tcx, substs) )
@@ -345,7 +376,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
345376 //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
346377 local_decls : body. local_decls . clone ( ) ,
347378 source_info : None ,
348- locals_of_current_block : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
349379 }
350380 }
351381
@@ -899,7 +929,6 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
899929 Will remove it from const-prop after block is finished. Local: {:?}",
900930 place. local
901931 ) ;
902- self . locals_of_current_block . insert ( place. local ) ;
903932 }
904933 ConstPropMode :: OnlyPropagateInto | ConstPropMode :: NoPropagation => {
905934 trace ! ( "can't propagate into {:?}" , place) ;
@@ -1088,10 +1117,27 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
10881117 }
10891118 }
10901119 }
1091- // We remove all Locals which are restricted in propagation to their containing blocks.
1092- for local in self . locals_of_current_block . iter ( ) {
1120+
1121+ // We remove all Locals which are restricted in propagation to their containing blocks and
1122+ // which were modified in the current block.
1123+ // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`
1124+ let mut locals = std:: mem:: take ( & mut self . ecx . machine . written_only_inside_own_block_locals ) ;
1125+ for & local in locals. iter ( ) {
10931126 Self :: remove_const ( & mut self . ecx , local) ;
10941127 }
1095- self . locals_of_current_block . clear ( ) ;
1128+ locals. clear ( ) ;
1129+ // Put it back so we reuse the heap of the storage
1130+ self . ecx . machine . written_only_inside_own_block_locals = locals;
1131+ if cfg ! ( debug_assertions) {
1132+ // Ensure we are correctly erasing locals with the non-debug-assert logic.
1133+ for local in self . ecx . machine . only_propagate_inside_block_locals . iter ( ) {
1134+ assert ! (
1135+ self . get_const( local. into( ) ) . is_none( )
1136+ || self
1137+ . layout_of( self . local_decls[ local] . ty)
1138+ . map_or( true , |layout| layout. is_zst( ) )
1139+ )
1140+ }
1141+ }
10961142 }
10971143}
0 commit comments