Skip to content

Commit cf85310

Browse files
committed
Auto merge of #95161 - JakobDegen:fix-early-otherwise-branch, r=wesleywiser
Disable almost certainly unsound early otherwise branch MIR opt Originally thought this was just an MIR semantics issue, but it's not. r? rust-lang/mir-opt
2 parents 3c17c84 + a2f3a17 commit cf85310

File tree

1 file changed

+32
-1
lines changed

1 file changed

+32
-1
lines changed

compiler/rustc_mir_transform/src/early_otherwise_branch.rs

+32-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub struct EarlyOtherwiseBranch;
9595

9696
impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
9797
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
98-
sess.mir_opt_level() >= 2
98+
sess.mir_opt_level() >= 3 && sess.opts.debugging_opts.unsound_mir_opts
9999
}
100100

101101
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
@@ -226,6 +226,37 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
226226

227227
/// Returns true if computing the discriminant of `place` may be hoisted out of the branch
228228
fn may_hoist<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, place: Place<'tcx>) -> bool {
229+
// FIXME(JakobDegen): This is unsound. Someone could write code like this:
230+
// ```rust
231+
// let Q = val;
232+
// if discriminant(P) == otherwise {
233+
// let ptr = &mut Q as *mut _ as *mut u8;
234+
// unsafe { *ptr = 10; } // Any invalid value for the type
235+
// }
236+
//
237+
// match P {
238+
// A => match Q {
239+
// A => {
240+
// // code
241+
// }
242+
// _ => {
243+
// // don't use Q
244+
// }
245+
// }
246+
// _ => {
247+
// // don't use Q
248+
// }
249+
// };
250+
// ```
251+
//
252+
// Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an
253+
// invalid value, which is UB.
254+
//
255+
// In order to fix this, we would either need to show that the discriminant computation of
256+
// `place` is computed in all branches, including the `otherwise` branch, or we would need
257+
// another analysis pass to determine that the place is fully initialized. It might even be best
258+
// to have the hoisting be performed in a different pass and just do the CFG changing in this
259+
// pass.
229260
for (place, proj) in place.iter_projections() {
230261
match proj {
231262
// Dereferencing in the computation of `place` might cause issues from one of two

0 commit comments

Comments
 (0)