11//! Propagates constants for early reporting of statically known
22//! assertion failures
33
4- use std:: cell:: Cell ;
5-
64use either:: { Left , Right } ;
75
86use rustc_const_eval:: interpret:: Immediate ;
97use rustc_const_eval:: interpret:: {
10- self , InterpCx , InterpResult , LocalState , LocalValue , MemoryKind , OpTy , Scalar , StackPopCleanup ,
8+ self , InterpCx , InterpResult , LocalValue , MemoryKind , OpTy , Scalar , StackPopCleanup ,
119} ;
1210use rustc_hir:: def:: DefKind ;
1311use rustc_hir:: HirId ;
14- use rustc_index:: bit_set:: BitSet ;
1512use rustc_index:: vec:: IndexVec ;
1613use 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:: * ;
2215use rustc_middle:: ty:: layout:: { LayoutError , LayoutOf , LayoutOfHelpers , TyAndLayout } ;
2316use rustc_middle:: ty:: InternalSubsts ;
2417use rustc_middle:: ty:: {
@@ -185,17 +178,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
185178 let param_env = tcx. param_env_reveal_all_normalized ( def_id) ;
186179
187180 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- }
194181 let mut ecx = InterpCx :: new (
195182 tcx,
196183 tcx. def_span ( def_id) ,
197184 param_env,
198- ConstPropMachine :: new ( only_propagate_inside_block_locals , can_const_prop) ,
185+ ConstPropMachine :: new ( can_const_prop) ,
199186 ) ;
200187
201188 let ret_layout = ecx
@@ -258,10 +245,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
258245 /// Remove `local` from the pool of `Locals`. Allows writing to them,
259246 /// but not reading from them anymore.
260247 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 ) ) ;
265250 }
266251
267252 fn lint_root ( & self , source_info : SourceInfo ) -> Option < HirId > {
@@ -420,12 +405,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
420405 Some ( ( ) )
421406 }
422407
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 < ( ) > {
429409 // Perform any special handling for specific Rvalue types.
430410 // Generally, checks here fall into one of two categories:
431411 // 1. Additional checking to provide useful lints to the user
@@ -501,7 +481,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
501481 return None ;
502482 }
503483
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+ }
505498 }
506499}
507500
@@ -522,82 +515,78 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
522515 self . eval_constant ( constant, self . source_info . unwrap ( ) ) ;
523516 }
524517
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+
525554 fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
526555 trace ! ( "visit_statement: {:?}" , statement) ;
527556 let source_info = statement. source_info ;
528557 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 {
543574 Self :: remove_const ( & mut self . ecx , place. local ) ;
544575 }
545576 }
546- ConstPropMode :: FullConstProp => { }
547577 }
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 ) ;
565578 }
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 ;
597587 }
588+ _ => { }
598589 }
599-
600- self . super_statement ( statement, location) ;
601590 }
602591
603592 fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
@@ -694,27 +683,25 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
694683 | TerminatorKind :: Call { .. }
695684 | TerminatorKind :: InlineAsm { .. } => { }
696685 }
686+ }
687+
688+ fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & BasicBlockData < ' tcx > ) {
689+ self . super_basic_block_data ( block, data) ;
697690
698691 // We remove all Locals which are restricted in propagation to their containing blocks and
699692 // which were modified in the current block.
700693 // 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+ }
717703 }
718704 }
705+ self . ecx . machine . can_const_prop = can_const_prop;
719706 }
720707}
0 commit comments