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 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!
29
36
30
37
use rustc_index:: { Idx , IndexSlice , IndexVec } ;
31
38
use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
@@ -66,8 +73,8 @@ impl SimplifyCfg {
66
73
}
67
74
}
68
75
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 ( ) ;
71
78
remove_dead_blocks ( body) ;
72
79
73
80
// FIXME: Should probably be moved into some kind of pass manager
@@ -79,9 +86,9 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg {
79
86
self . name ( )
80
87
}
81
88
82
- fn run_pass ( & self , _ : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
89
+ fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
83
90
debug ! ( "SimplifyCfg({:?}) - simplifying {:?}" , self . name( ) , body. source) ;
84
- simplify_cfg ( body) ;
91
+ simplify_cfg ( tcx , body) ;
85
92
}
86
93
87
94
fn is_required ( & self ) -> bool {
@@ -90,12 +97,13 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg {
90
97
}
91
98
92
99
struct CfgSimplifier < ' a , ' tcx > {
100
+ preserve_switch_reads : bool ,
93
101
basic_blocks : & ' a mut IndexSlice < BasicBlock , BasicBlockData < ' tcx > > ,
94
102
pred_count : IndexVec < BasicBlock , u32 > ,
95
103
}
96
104
97
105
impl < ' 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 {
99
107
let mut pred_count = IndexVec :: from_elem ( 0u32 , & body. basic_blocks ) ;
100
108
101
109
// we can't use mir.predecessors() here because that counts
@@ -110,9 +118,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
110
118
}
111
119
}
112
120
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 ;
113
124
let basic_blocks = body. basic_blocks_mut ( ) ;
114
125
115
- CfgSimplifier { basic_blocks, pred_count }
126
+ CfgSimplifier { preserve_switch_reads , basic_blocks, pred_count }
116
127
}
117
128
118
129
fn simplify ( mut self ) {
@@ -253,9 +264,15 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
253
264
254
265
// turn a branch with all successors identical to a goto
255
266
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 ;
259
276
} ;
260
277
261
278
let first_succ = {
0 commit comments