2626//! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we
2727//! naively generate still contains the `_a = ()` write in the unreachable block "after" the
2828//! return.
29+ //!
30+ //! **WARNING**: This is one of the few optimizations that runs on built and analysis MIR, and
31+ //! so its effects may affect the type-checking, borrow-checking, and other analysis of MIR.
32+ //! We must be extremely careful to only apply optimizations that preserve UB and all
33+ //! non-determinism, since changes here can affect which programs compile in an insta-stable way.
34+ //! The normal logic that a program with UB can be changed to do anything does not apply to
35+ //! pre-"runtime" MIR!
2936
3037use rustc_index:: { Idx , IndexSlice , IndexVec } ;
3138use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
@@ -66,8 +73,8 @@ impl SimplifyCfg {
6673 }
6774}
6875
69- pub ( super ) fn simplify_cfg ( body : & mut Body < ' _ > ) {
70- CfgSimplifier :: new ( body) . simplify ( ) ;
76+ pub ( super ) fn simplify_cfg < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
77+ CfgSimplifier :: new ( tcx , body) . simplify ( ) ;
7178 remove_dead_blocks ( body) ;
7279
7380 // FIXME: Should probably be moved into some kind of pass manager
@@ -79,9 +86,9 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg {
7986 self . name ( )
8087 }
8188
82- fn run_pass ( & self , _ : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
89+ fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
8390 debug ! ( "SimplifyCfg({:?}) - simplifying {:?}" , self . name( ) , body. source) ;
84- simplify_cfg ( body) ;
91+ simplify_cfg ( tcx , body) ;
8592 }
8693
8794 fn is_required ( & self ) -> bool {
@@ -90,12 +97,13 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg {
9097}
9198
9299struct CfgSimplifier < ' a , ' tcx > {
100+ preserve_switch_reads : bool ,
93101 basic_blocks : & ' a mut IndexSlice < BasicBlock , BasicBlockData < ' tcx > > ,
94102 pred_count : IndexVec < BasicBlock , u32 > ,
95103}
96104
97105impl < ' a , ' tcx > CfgSimplifier < ' a , ' tcx > {
98- fn new ( body : & ' a mut Body < ' tcx > ) -> Self {
106+ fn new ( tcx : TyCtxt < ' tcx > , body : & ' a mut Body < ' tcx > ) -> Self {
99107 let mut pred_count = IndexVec :: from_elem ( 0u32 , & body. basic_blocks ) ;
100108
101109 // we can't use mir.predecessors() here because that counts
@@ -110,9 +118,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
110118 }
111119 }
112120
121+ // Preserve `SwitchInt` reads on built and analysis MIR, or if `-Zmir-preserve-ub`.
122+ let preserve_switch_reads = matches ! ( body. phase, MirPhase :: Built | MirPhase :: Analysis ( _) )
123+ || tcx. sess . opts . unstable_opts . mir_preserve_ub ;
113124 let basic_blocks = body. basic_blocks_mut ( ) ;
114125
115- CfgSimplifier { basic_blocks, pred_count }
126+ CfgSimplifier { preserve_switch_reads , basic_blocks, pred_count }
116127 }
117128
118129 fn simplify ( mut self ) {
@@ -253,9 +264,15 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
253264
254265 // turn a branch with all successors identical to a goto
255266 fn simplify_branch ( & mut self , terminator : & mut Terminator < ' tcx > ) -> bool {
256- match terminator. kind {
257- TerminatorKind :: SwitchInt { .. } => { }
258- _ => return false ,
267+ // Removing a `SwitchInt` terminator may remove reads that result in UB,
268+ // so we must not apply this optimization before borrowck or when
269+ // `-Zmir-preserve-ub` is set.
270+ if self . preserve_switch_reads {
271+ return false ;
272+ }
273+
274+ let TerminatorKind :: SwitchInt { .. } = terminator. kind else {
275+ return false ;
259276 } ;
260277
261278 let first_succ = {
0 commit comments