@@ -11,6 +11,7 @@ use super::simplify::simplify_cfg;
1111/// let y: Option<()>;
1212/// match (x,y) {
1313/// (Some(_), Some(_)) => {0},
14+ /// (None, None) => {2},
1415/// _ => {1}
1516/// }
1617/// ```
@@ -23,10 +24,10 @@ use super::simplify::simplify_cfg;
2324/// if discriminant_x == discriminant_y {
2425/// match x {
2526/// Some(_) => 0,
26- /// _ => 1, // <----
27- /// } // | Actually the same bb
28- /// } else { // |
29- /// 1 // <--------------
27+ /// None => 2,
28+ /// }
29+ /// } else {
30+ /// 1
3031/// }
3132/// ```
3233///
@@ -47,18 +48,18 @@ use super::simplify::simplify_cfg;
4748/// | | |
4849/// ================= | | |
4950/// | 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
5657/// | BB9 | <--------------- | otherwise |
5758/// |---------------| ============================
5859/// | ... |
5960/// =================
6061/// ```
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
6263/// code:
6364/// - `BB1` is `parent` and `BBC, BBD` are children
6465/// - `P` is `child_place`
@@ -78,7 +79,7 @@ use super::simplify::simplify_cfg;
7879/// |---------------------| | | switchInt(Q) |
7980/// | switchInt(_t) | | | c | ---> BBC.2
8081/// | false | --------/ | d | ---> BBD.2
81- /// | otherwise | ------- --------- | otherwise |
82+ /// | otherwise | / --------- | otherwise |
8283/// ======================= | ============================
8384/// |
8485/// ================= |
@@ -219,37 +220,6 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
219220
220221/// Returns true if computing the discriminant of `place` may be hoisted out of the branch
221222fn 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.
253223 for ( place, proj) in place. iter_projections ( ) {
254224 match proj {
255225 // Dereferencing in the computation of `place` might cause issues from one of two
@@ -315,18 +285,38 @@ fn evaluate_candidate<'tcx>(
315285 return None ;
316286 } ;
317287 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+ }
330320 let ( _, child) = targets. iter ( ) . next ( ) ?;
331321 let child_terminator = & bbs[ child] . terminator ( ) ;
332322 let TerminatorKind :: SwitchInt { targets : child_targets, discr : child_discr } =
@@ -344,7 +334,7 @@ fn evaluate_candidate<'tcx>(
344334 let ( _, Rvalue :: Discriminant ( child_place) ) = & * * boxed else {
345335 return None ;
346336 } ;
347- let destination = parent_dest . unwrap_or ( child_targets. otherwise ( ) ) ;
337+ let destination = child_targets. otherwise ( ) ;
348338
349339 // Verify that the optimization is legal in general
350340 // We can hoist evaluating the child discriminant out of the branch
@@ -411,5 +401,5 @@ fn verify_candidate_branch<'tcx>(
411401 if let Some ( _) = iter. next ( ) {
412402 return false ;
413403 }
414- return true ;
404+ true
415405}
0 commit comments