1
1
//! Propagates constants for early reporting of statically known
2
2
//! assertion failures
3
3
4
- use std:: cell:: Cell ;
5
-
6
4
use either:: { Left , Right } ;
7
5
8
6
use rustc_const_eval:: interpret:: Immediate ;
9
7
use rustc_const_eval:: interpret:: {
10
- self , InterpCx , InterpResult , LocalState , LocalValue , MemoryKind , OpTy , Scalar , StackPopCleanup ,
8
+ self , InterpCx , InterpResult , LocalValue , MemoryKind , OpTy , Scalar , StackPopCleanup ,
11
9
} ;
12
10
use rustc_hir:: def:: DefKind ;
13
11
use rustc_hir:: HirId ;
14
- use rustc_index:: bit_set:: BitSet ;
15
12
use rustc_index:: vec:: IndexVec ;
16
13
use rustc_middle:: mir:: visit:: Visitor ;
17
- use rustc_middle:: mir:: {
18
- AssertKind , BinOp , Body , Constant , Local , LocalDecl , Location , Operand , Place , Rvalue ,
19
- SourceInfo , SourceScope , SourceScopeData , Statement , StatementKind , Terminator , TerminatorKind ,
20
- UnOp , RETURN_PLACE ,
21
- } ;
14
+ use rustc_middle:: mir:: * ;
22
15
use rustc_middle:: ty:: layout:: { LayoutError , LayoutOf , LayoutOfHelpers , TyAndLayout } ;
23
16
use rustc_middle:: ty:: InternalSubsts ;
24
17
use rustc_middle:: ty:: {
@@ -185,17 +178,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
185
178
let param_env = tcx. param_env_reveal_all_normalized ( def_id) ;
186
179
187
180
let can_const_prop = CanConstProp :: check ( tcx, param_env, body) ;
188
- let mut only_propagate_inside_block_locals = BitSet :: new_empty ( can_const_prop. len ( ) ) ;
189
- for ( l, mode) in can_const_prop. iter_enumerated ( ) {
190
- if * mode == ConstPropMode :: OnlyInsideOwnBlock {
191
- only_propagate_inside_block_locals. insert ( l) ;
192
- }
193
- }
194
181
let mut ecx = InterpCx :: new (
195
182
tcx,
196
183
tcx. def_span ( def_id) ,
197
184
param_env,
198
- ConstPropMachine :: new ( only_propagate_inside_block_locals , can_const_prop) ,
185
+ ConstPropMachine :: new ( can_const_prop) ,
199
186
) ;
200
187
201
188
let ret_layout = ecx
@@ -258,10 +245,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
258
245
/// Remove `local` from the pool of `Locals`. Allows writing to them,
259
246
/// but not reading from them anymore.
260
247
fn remove_const ( ecx : & mut InterpCx < ' mir , ' tcx , ConstPropMachine < ' mir , ' tcx > > , local : Local ) {
261
- ecx. frame_mut ( ) . locals [ local] = LocalState {
262
- value : LocalValue :: Live ( interpret:: Operand :: Immediate ( interpret:: Immediate :: Uninit ) ) ,
263
- layout : Cell :: new ( None ) ,
264
- } ;
248
+ ecx. frame_mut ( ) . locals [ local] . value =
249
+ LocalValue :: Live ( interpret:: Operand :: Immediate ( interpret:: Immediate :: Uninit ) ) ;
265
250
}
266
251
267
252
fn lint_root ( & self , source_info : SourceInfo ) -> Option < HirId > {
@@ -420,12 +405,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
420
405
Some ( ( ) )
421
406
}
422
407
423
- fn const_prop (
424
- & mut self ,
425
- rvalue : & Rvalue < ' tcx > ,
426
- source_info : SourceInfo ,
427
- place : Place < ' tcx > ,
428
- ) -> Option < ( ) > {
408
+ fn check_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , source_info : SourceInfo ) -> Option < ( ) > {
429
409
// Perform any special handling for specific Rvalue types.
430
410
// Generally, checks here fall into one of two categories:
431
411
// 1. Additional checking to provide useful lints to the user
@@ -501,7 +481,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
501
481
return None ;
502
482
}
503
483
504
- self . use_ecx ( source_info, |this| this. ecx . eval_rvalue_into_place ( rvalue, place) )
484
+ Some ( ( ) )
485
+ }
486
+
487
+ fn ensure_not_propagated ( & mut self , local : Local ) {
488
+ if cfg ! ( debug_assertions) {
489
+ assert ! (
490
+ self . get_const( local. into( ) ) . is_none( )
491
+ || self
492
+ . layout_of( self . local_decls[ local] . ty)
493
+ . map_or( true , |layout| layout. is_zst( ) ) ,
494
+ "failed to remove values for `{local:?}`, value={:?}" ,
495
+ self . get_const( local. into( ) ) ,
496
+ )
497
+ }
505
498
}
506
499
}
507
500
@@ -522,82 +515,78 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
522
515
self . eval_constant ( constant, self . source_info . unwrap ( ) ) ;
523
516
}
524
517
518
+ fn visit_assign ( & mut self , place : & Place < ' tcx > , rvalue : & Rvalue < ' tcx > , location : Location ) {
519
+ self . super_assign ( place, rvalue, location) ;
520
+
521
+ let source_info = self . source_info . unwrap ( ) ;
522
+ let Some ( ( ) ) = self . check_rvalue ( rvalue, source_info) else { return } ;
523
+
524
+ match self . ecx . machine . can_const_prop [ place. local ] {
525
+ // Do nothing if the place is indirect.
526
+ _ if place. is_indirect ( ) => { }
527
+ ConstPropMode :: NoPropagation => self . ensure_not_propagated ( place. local ) ,
528
+ ConstPropMode :: OnlyInsideOwnBlock | ConstPropMode :: FullConstProp => {
529
+ if self
530
+ . use_ecx ( source_info, |this| this. ecx . eval_rvalue_into_place ( rvalue, * place) )
531
+ . is_none ( )
532
+ {
533
+ // Const prop failed, so erase the destination, ensuring that whatever happens
534
+ // from here on, does not know about the previous value.
535
+ // This is important in case we have
536
+ // ```rust
537
+ // let mut x = 42;
538
+ // x = SOME_MUTABLE_STATIC;
539
+ // // x must now be uninit
540
+ // ```
541
+ // FIXME: we overzealously erase the entire local, because that's easier to
542
+ // implement.
543
+ trace ! (
544
+ "propagation into {:?} failed.
545
+ Nuking the entire site from orbit, it's the only way to be sure" ,
546
+ place,
547
+ ) ;
548
+ Self :: remove_const ( & mut self . ecx , place. local ) ;
549
+ }
550
+ }
551
+ }
552
+ }
553
+
525
554
fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
526
555
trace ! ( "visit_statement: {:?}" , statement) ;
527
556
let source_info = statement. source_info ;
528
557
self . source_info = Some ( source_info) ;
529
- if let StatementKind :: Assign ( box ( place, ref rval) ) = statement. kind {
530
- let can_const_prop = self . ecx . machine . can_const_prop [ place. local ] ;
531
- if let Some ( ( ) ) = self . const_prop ( rval, source_info, place) {
532
- match can_const_prop {
533
- ConstPropMode :: OnlyInsideOwnBlock => {
534
- trace ! (
535
- "found local restricted to its block. \
536
- Will remove it from const-prop after block is finished. Local: {:?}",
537
- place. local
538
- ) ;
539
- }
540
- ConstPropMode :: OnlyPropagateInto | ConstPropMode :: NoPropagation => {
541
- trace ! ( "can't propagate into {:?}" , place) ;
542
- if place. local != RETURN_PLACE {
558
+
559
+ // We want to evaluate operands before any change to the assigned-to value,
560
+ // so we recurse first.
561
+ self . super_statement ( statement, location) ;
562
+
563
+ match statement. kind {
564
+ StatementKind :: SetDiscriminant { ref place, .. } => {
565
+ match self . ecx . machine . can_const_prop [ place. local ] {
566
+ // Do nothing if the place is indirect.
567
+ _ if place. is_indirect ( ) => { }
568
+ ConstPropMode :: NoPropagation => self . ensure_not_propagated ( place. local ) ,
569
+ ConstPropMode :: FullConstProp | ConstPropMode :: OnlyInsideOwnBlock => {
570
+ if self . use_ecx ( source_info, |this| this. ecx . statement ( statement) ) . is_some ( )
571
+ {
572
+ trace ! ( "propped discriminant into {:?}" , place) ;
573
+ } else {
543
574
Self :: remove_const ( & mut self . ecx , place. local ) ;
544
575
}
545
576
}
546
- ConstPropMode :: FullConstProp => { }
547
577
}
548
- } else {
549
- // Const prop failed, so erase the destination, ensuring that whatever happens
550
- // from here on, does not know about the previous value.
551
- // This is important in case we have
552
- // ```rust
553
- // let mut x = 42;
554
- // x = SOME_MUTABLE_STATIC;
555
- // // x must now be uninit
556
- // ```
557
- // FIXME: we overzealously erase the entire local, because that's easier to
558
- // implement.
559
- trace ! (
560
- "propagation into {:?} failed.
561
- Nuking the entire site from orbit, it's the only way to be sure" ,
562
- place,
563
- ) ;
564
- Self :: remove_const ( & mut self . ecx , place. local ) ;
565
578
}
566
- } else {
567
- match statement. kind {
568
- StatementKind :: SetDiscriminant { ref place, .. } => {
569
- match self . ecx . machine . can_const_prop [ place. local ] {
570
- ConstPropMode :: FullConstProp | ConstPropMode :: OnlyInsideOwnBlock => {
571
- if self
572
- . use_ecx ( source_info, |this| this. ecx . statement ( statement) )
573
- . is_some ( )
574
- {
575
- trace ! ( "propped discriminant into {:?}" , place) ;
576
- } else {
577
- Self :: remove_const ( & mut self . ecx , place. local ) ;
578
- }
579
- }
580
- ConstPropMode :: OnlyPropagateInto | ConstPropMode :: NoPropagation => {
581
- Self :: remove_const ( & mut self . ecx , place. local ) ;
582
- }
583
- }
584
- }
585
- StatementKind :: StorageLive ( local) | StatementKind :: StorageDead ( local) => {
586
- let frame = self . ecx . frame_mut ( ) ;
587
- frame. locals [ local] . value =
588
- if let StatementKind :: StorageLive ( _) = statement. kind {
589
- LocalValue :: Live ( interpret:: Operand :: Immediate (
590
- interpret:: Immediate :: Uninit ,
591
- ) )
592
- } else {
593
- LocalValue :: Dead
594
- } ;
595
- }
596
- _ => { }
579
+ StatementKind :: StorageLive ( local) => {
580
+ let frame = self . ecx . frame_mut ( ) ;
581
+ frame. locals [ local] . value =
582
+ LocalValue :: Live ( interpret:: Operand :: Immediate ( interpret:: Immediate :: Uninit ) ) ;
583
+ }
584
+ StatementKind :: StorageDead ( local) => {
585
+ let frame = self . ecx . frame_mut ( ) ;
586
+ frame. locals [ local] . value = LocalValue :: Dead ;
597
587
}
588
+ _ => { }
598
589
}
599
-
600
- self . super_statement ( statement, location) ;
601
590
}
602
591
603
592
fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
@@ -694,27 +683,25 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
694
683
| TerminatorKind :: Call { .. }
695
684
| TerminatorKind :: InlineAsm { .. } => { }
696
685
}
686
+ }
687
+
688
+ fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & BasicBlockData < ' tcx > ) {
689
+ self . super_basic_block_data ( block, data) ;
697
690
698
691
// We remove all Locals which are restricted in propagation to their containing blocks and
699
692
// which were modified in the current block.
700
693
// Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`.
701
- let mut locals = std:: mem:: take ( & mut self . ecx . machine . written_only_inside_own_block_locals ) ;
702
- for & local in locals. iter ( ) {
703
- Self :: remove_const ( & mut self . ecx , local) ;
704
- }
705
- locals. clear ( ) ;
706
- // Put it back so we reuse the heap of the storage
707
- self . ecx . machine . written_only_inside_own_block_locals = locals;
708
- if cfg ! ( debug_assertions) {
709
- // Ensure we are correctly erasing locals with the non-debug-assert logic.
710
- for local in self . ecx . machine . only_propagate_inside_block_locals . iter ( ) {
711
- assert ! (
712
- self . get_const( local. into( ) ) . is_none( )
713
- || self
714
- . layout_of( self . local_decls[ local] . ty)
715
- . map_or( true , |layout| layout. is_zst( ) )
716
- )
694
+ let can_const_prop = std:: mem:: take ( & mut self . ecx . machine . can_const_prop ) ;
695
+ for ( local, & mode) in can_const_prop. iter_enumerated ( ) {
696
+ match mode {
697
+ ConstPropMode :: FullConstProp => { }
698
+ ConstPropMode :: NoPropagation => self . ensure_not_propagated ( local) ,
699
+ ConstPropMode :: OnlyInsideOwnBlock => {
700
+ Self :: remove_const ( & mut self . ecx , local) ;
701
+ self . ensure_not_propagated ( local) ;
702
+ }
717
703
}
718
704
}
705
+ self . ecx . machine . can_const_prop = can_const_prop;
719
706
}
720
707
}
0 commit comments