4
4
use std:: cell:: Cell ;
5
5
6
6
use rustc_ast:: ast:: Mutability ;
7
+ use rustc_data_structures:: fx:: FxHashSet ;
7
8
use rustc_hir:: def:: DefKind ;
8
9
use rustc_hir:: HirId ;
9
10
use rustc_index:: bit_set:: BitSet ;
@@ -28,7 +29,7 @@ use rustc_trait_selection::traits;
28
29
use crate :: const_eval:: error_to_const_error;
29
30
use crate :: interpret:: {
30
31
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 ,
32
33
ScalarMaybeUninit , StackPopCleanup ,
33
34
} ;
34
35
use crate :: transform:: { MirPass , MirSource } ;
@@ -151,11 +152,19 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
151
152
struct ConstPropMachine < ' mir , ' tcx > {
152
153
/// The virtual call stack.
153
154
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 > ,
154
159
}
155
160
156
161
impl < ' 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
+ }
159
168
}
160
169
}
161
170
@@ -227,6 +236,18 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
227
236
l. access ( )
228
237
}
229
238
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
+
230
251
fn before_access_global (
231
252
_memory_extra : & ( ) ,
232
253
_alloc_id : AllocId ,
@@ -274,8 +295,6 @@ struct ConstPropagator<'mir, 'tcx> {
274
295
// Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
275
296
// the last known `SourceInfo` here and just keep revisiting it.
276
297
source_info : Option < SourceInfo > ,
277
- // Locals we need to forget at the end of the current block
278
- locals_of_current_block : BitSet < Local > ,
279
298
}
280
299
281
300
impl < ' mir , ' tcx > LayoutOf for ConstPropagator < ' mir , ' tcx > {
@@ -313,8 +332,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
313
332
let param_env = tcx. param_env ( def_id) . with_reveal_all ( ) ;
314
333
315
334
let span = tcx. def_span ( def_id) ;
316
- let mut ecx = InterpCx :: new ( tcx, span, param_env, ConstPropMachine :: new ( ) , ( ) ) ;
317
335
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
+ ) ;
318
349
319
350
let ret = ecx
320
351
. layout_of ( body. return_ty ( ) . subst ( tcx, substs) )
@@ -345,7 +376,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
345
376
//FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
346
377
local_decls : body. local_decls . clone ( ) ,
347
378
source_info : None ,
348
- locals_of_current_block : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
349
379
}
350
380
}
351
381
@@ -900,7 +930,6 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
900
930
Will remove it from const-prop after block is finished. Local: {:?}",
901
931
place. local
902
932
) ;
903
- self . locals_of_current_block . insert ( place. local ) ;
904
933
}
905
934
ConstPropMode :: OnlyPropagateInto | ConstPropMode :: NoPropagation => {
906
935
trace ! ( "can't propagate into {:?}" , place) ;
@@ -1089,10 +1118,27 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
1089
1118
}
1090
1119
}
1091
1120
}
1092
- // We remove all Locals which are restricted in propagation to their containing blocks.
1093
- for local in self . locals_of_current_block . iter ( ) {
1121
+
1122
+ // We remove all Locals which are restricted in propagation to their containing blocks and
1123
+ // which were modified in the current block.
1124
+ // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`
1125
+ let mut locals = std:: mem:: take ( & mut self . ecx . machine . written_only_inside_own_block_locals ) ;
1126
+ for & local in locals. iter ( ) {
1094
1127
Self :: remove_const ( & mut self . ecx , local) ;
1095
1128
}
1096
- self . locals_of_current_block . clear ( ) ;
1129
+ locals. clear ( ) ;
1130
+ // Put it back so we reuse the heap of the storage
1131
+ self . ecx . machine . written_only_inside_own_block_locals = locals;
1132
+ if cfg ! ( debug_assertions) {
1133
+ // Ensure we are correctly erasing locals with the non-debug-assert logic.
1134
+ for local in self . ecx . machine . only_propagate_inside_block_locals . iter ( ) {
1135
+ assert ! (
1136
+ self . get_const( local. into( ) ) . is_none( )
1137
+ || self
1138
+ . layout_of( self . local_decls[ local] . ty)
1139
+ . map_or( true , |layout| layout. is_zst( ) )
1140
+ )
1141
+ }
1142
+ }
1097
1143
}
1098
1144
}
0 commit comments