@@ -11,6 +11,7 @@ use super::simplify::simplify_cfg;
11
11
/// let y: Option<()>;
12
12
/// match (x,y) {
13
13
/// (Some(_), Some(_)) => {0},
14
+ /// (None, None) => {2},
14
15
/// _ => {1}
15
16
/// }
16
17
/// ```
@@ -23,10 +24,10 @@ use super::simplify::simplify_cfg;
23
24
/// if discriminant_x == discriminant_y {
24
25
/// match x {
25
26
/// Some(_) => 0,
26
- /// _ => 1, // <----
27
- /// } // | Actually the same bb
28
- /// } else { // |
29
- /// 1 // <--------------
27
+ /// None => 2,
28
+ /// }
29
+ /// } else {
30
+ /// 1
30
31
/// }
31
32
/// ```
32
33
///
@@ -47,18 +48,18 @@ use super::simplify::simplify_cfg;
47
48
/// | | |
48
49
/// ================= | | |
49
50
/// | BBU | <-| | | ============================
50
- /// |---------------| | \-------> | BBD |
51
- /// |---------------| | | |--------------------------|
52
- /// | unreachable | | | | _dl = discriminant(P) |
53
- /// ================= | | |--------------------------|
54
- /// | | | switchInt(_dl) |
55
- /// ================= | | | d | ---> BBD.2
51
+ /// |---------------| \-------> | BBD |
52
+ /// |---------------| | |--------------------------|
53
+ /// | unreachable | | | _dl = discriminant(P) |
54
+ /// ================= | |--------------------------|
55
+ /// | | switchInt(_dl) |
56
+ /// ================= | | d | ---> BBD.2
56
57
/// | BB9 | <--------------- | otherwise |
57
58
/// |---------------| ============================
58
59
/// | ... |
59
60
/// =================
60
61
/// ```
61
- /// Where the `otherwise` branch on `BB1` is permitted to either go to `BBU` or to `BB9` . In the
62
+ /// Where the `otherwise` branch on `BB1` is permitted to either go to `BBU`. In the
62
63
/// code:
63
64
/// - `BB1` is `parent` and `BBC, BBD` are children
64
65
/// - `P` is `child_place`
@@ -78,7 +79,7 @@ use super::simplify::simplify_cfg;
78
79
/// |---------------------| | | switchInt(Q) |
79
80
/// | switchInt(_t) | | | c | ---> BBC.2
80
81
/// | false | --------/ | d | ---> BBD.2
81
- /// | otherwise | ------- --------- | otherwise |
82
+ /// | otherwise | / --------- | otherwise |
82
83
/// ======================= | ============================
83
84
/// |
84
85
/// ================= |
@@ -219,37 +220,6 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
219
220
220
221
/// Returns true if computing the discriminant of `place` may be hoisted out of the branch
221
222
fn may_hoist < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > , place : Place < ' tcx > ) -> bool {
222
- // FIXME(JakobDegen): This is unsound. Someone could write code like this:
223
- // ```rust
224
- // let Q = val;
225
- // if discriminant(P) == otherwise {
226
- // let ptr = &mut Q as *mut _ as *mut u8;
227
- // unsafe { *ptr = 10; } // Any invalid value for the type
228
- // }
229
- //
230
- // match P {
231
- // A => match Q {
232
- // A => {
233
- // // code
234
- // }
235
- // _ => {
236
- // // don't use Q
237
- // }
238
- // }
239
- // _ => {
240
- // // don't use Q
241
- // }
242
- // };
243
- // ```
244
- //
245
- // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an
246
- // invalid value, which is UB.
247
- //
248
- // In order to fix this, we would either need to show that the discriminant computation of
249
- // `place` is computed in all branches, including the `otherwise` branch, or we would need
250
- // another analysis pass to determine that the place is fully initialized. It might even be best
251
- // to have the hoisting be performed in a different pass and just do the CFG changing in this
252
- // pass.
253
223
for ( place, proj) in place. iter_projections ( ) {
254
224
match proj {
255
225
// Dereferencing in the computation of `place` might cause issues from one of two
@@ -315,18 +285,38 @@ fn evaluate_candidate<'tcx>(
315
285
return None ;
316
286
} ;
317
287
let parent_ty = parent_discr. ty ( body. local_decls ( ) , tcx) ;
318
- let parent_dest = {
319
- let poss = targets. otherwise ( ) ;
320
- // If the fallthrough on the parent is trivially unreachable, we can let the
321
- // children choose the destination
322
- if bbs[ poss] . statements . len ( ) == 0
323
- && bbs[ poss] . terminator ( ) . kind == TerminatorKind :: Unreachable
324
- {
325
- None
326
- } else {
327
- Some ( poss)
328
- }
329
- } ;
288
+ if !bbs[ targets. otherwise ( ) ] . is_empty_unreachable ( ) {
289
+ // Someone could write code like this:
290
+ // ```rust
291
+ // let Q = val;
292
+ // if discriminant(P) == otherwise {
293
+ // let ptr = &mut Q as *mut _ as *mut u8;
294
+ // // Any invalid value for the type. It is possible to be opaque, such as in other functions.
295
+ // unsafe { *ptr = 10; }
296
+ // }
297
+ //
298
+ // match P {
299
+ // A => match Q {
300
+ // A => {
301
+ // // code
302
+ // }
303
+ // _ => {
304
+ // // don't use Q
305
+ // }
306
+ // }
307
+ // _ => {
308
+ // // don't use Q
309
+ // }
310
+ // };
311
+ // ```
312
+ //
313
+ // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an
314
+ // invalid value, which is UB.
315
+ // In order to fix this, we would either need to show that the discriminant computation of
316
+ // `place` is computed in all branches.
317
+ // So we need the `otherwise` branch has no statements and an unreachable terminator.
318
+ return None ;
319
+ }
330
320
let ( _, child) = targets. iter ( ) . next ( ) ?;
331
321
let child_terminator = & bbs[ child] . terminator ( ) ;
332
322
let TerminatorKind :: SwitchInt { targets : child_targets, discr : child_discr } =
@@ -344,7 +334,7 @@ fn evaluate_candidate<'tcx>(
344
334
let ( _, Rvalue :: Discriminant ( child_place) ) = & * * boxed else {
345
335
return None ;
346
336
} ;
347
- let destination = parent_dest . unwrap_or ( child_targets. otherwise ( ) ) ;
337
+ let destination = child_targets. otherwise ( ) ;
348
338
349
339
// Verify that the optimization is legal in general
350
340
// We can hoist evaluating the child discriminant out of the branch
@@ -411,5 +401,5 @@ fn verify_candidate_branch<'tcx>(
411
401
if let Some ( _) = iter. next ( ) {
412
402
return false ;
413
403
}
414
- return true ;
404
+ true
415
405
}
0 commit comments