26
26
//! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we
27
27
//! naively generate still contains the `_a = ()` write in the unreachable block "after" the
28
28
//! 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 the semantics of the
33
+ //! code, since changes here can affect which programs compile in an insta-stable way.
29
34
30
35
use rustc_index:: { Idx , IndexSlice , IndexVec } ;
31
36
use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
@@ -66,8 +71,8 @@ impl SimplifyCfg {
66
71
}
67
72
}
68
73
69
- pub ( super ) fn simplify_cfg ( body : & mut Body < ' _ > ) {
70
- CfgSimplifier :: new ( body) . simplify ( ) ;
74
+ pub ( super ) fn simplify_cfg < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
75
+ CfgSimplifier :: new ( tcx , body) . simplify ( ) ;
71
76
remove_dead_blocks ( body) ;
72
77
73
78
// FIXME: Should probably be moved into some kind of pass manager
@@ -79,9 +84,9 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg {
79
84
self . name ( )
80
85
}
81
86
82
- fn run_pass ( & self , _ : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
87
+ fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
83
88
debug ! ( "SimplifyCfg({:?}) - simplifying {:?}" , self . name( ) , body. source) ;
84
- simplify_cfg ( body) ;
89
+ simplify_cfg ( tcx , body) ;
85
90
}
86
91
87
92
fn is_required ( & self ) -> bool {
@@ -90,12 +95,13 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg {
90
95
}
91
96
92
97
struct CfgSimplifier < ' a , ' tcx > {
98
+ preserve_switch_reads : bool ,
93
99
basic_blocks : & ' a mut IndexSlice < BasicBlock , BasicBlockData < ' tcx > > ,
94
100
pred_count : IndexVec < BasicBlock , u32 > ,
95
101
}
96
102
97
103
impl < ' a , ' tcx > CfgSimplifier < ' a , ' tcx > {
98
- fn new ( body : & ' a mut Body < ' tcx > ) -> Self {
104
+ fn new ( tcx : TyCtxt < ' tcx > , body : & ' a mut Body < ' tcx > ) -> Self {
99
105
let mut pred_count = IndexVec :: from_elem ( 0u32 , & body. basic_blocks ) ;
100
106
101
107
// we can't use mir.predecessors() here because that counts
@@ -110,9 +116,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
110
116
}
111
117
}
112
118
119
+ // Preserve `SwitchInt` reads on built and analysis MIR, or if `-Zmir-preserve-ub`.
120
+ let preserve_switch_reads = matches ! ( body. phase, MirPhase :: Built | MirPhase :: Analysis ( _) )
121
+ || tcx. sess . opts . unstable_opts . mir_preserve_ub ;
113
122
let basic_blocks = body. basic_blocks_mut ( ) ;
114
123
115
- CfgSimplifier { basic_blocks, pred_count }
124
+ CfgSimplifier { preserve_switch_reads , basic_blocks, pred_count }
116
125
}
117
126
118
127
fn simplify ( mut self ) {
@@ -253,9 +262,15 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
253
262
254
263
// turn a branch with all successors identical to a goto
255
264
fn simplify_branch ( & mut self , terminator : & mut Terminator < ' tcx > ) -> bool {
256
- match terminator. kind {
257
- TerminatorKind :: SwitchInt { .. } => { }
258
- _ => return false ,
265
+ // Removing a `SwitchInt` terminator may remove reads that result in UB,
266
+ // so we must not apply this optimization before borrowck or when
267
+ // `-Zmir-preserve-ub` is set.
268
+ if self . preserve_switch_reads {
269
+ return false ;
270
+ }
271
+
272
+ let TerminatorKind :: SwitchInt { .. } = terminator. kind else {
273
+ return false ;
259
274
} ;
260
275
261
276
let first_succ = {
0 commit comments