about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_interface/src/tests.rs2
-rw-r--r--compiler/rustc_mir_transform/src/early_otherwise_branch.rs2
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs4
-rw-r--r--compiler/rustc_mir_transform/src/jump_threading.rs135
-rw-r--r--compiler/rustc_mir_transform/src/match_branches.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_place_mention.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_unneeded_drops.rs2
-rw-r--r--compiler/rustc_mir_transform/src/simplify.rs35
-rw-r--r--compiler/rustc_session/src/options.rs6
-rw-r--r--library/core/src/iter/adapters/enumerate.rs33
-rw-r--r--library/coretests/tests/iter/adapters/enumerate.rs10
-rw-r--r--library/coretests/tests/lib.rs1
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs3
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs3
-rw-r--r--src/etc/rust_analyzer_eglot.el7
-rw-r--r--src/etc/rust_analyzer_settings.json11
-rw-r--r--src/etc/rust_analyzer_zed.json13
-rw-r--r--src/librustdoc/clean/mod.rs47
-rw-r--r--src/tools/miri/src/lib.rs2
-rw-r--r--src/tools/miri/tests/fail/read_from_trivial_switch.rs14
-rw-r--r--src/tools/miri/tests/fail/read_from_trivial_switch.stderr15
-rw-r--r--tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir34
-rw-r--r--tests/mir-opt/dead-store-elimination/place_mention.rs2
-rw-r--r--tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir38
-rw-r--r--tests/mir-opt/read_from_trivial_switch.main.SimplifyCfg-initial.diff49
-rw-r--r--tests/mir-opt/read_from_trivial_switch.rs15
-rw-r--r--tests/ui/pattern/uninit-trivial.rs8
-rw-r--r--tests/ui/pattern/uninit-trivial.stderr16
-rw-r--r--triagebot.toml1
29 files changed, 363 insertions, 149 deletions
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index d405d044cae..5c8c51c8bbc 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -818,8 +818,8 @@ fn test_unstable_options_tracking_hash() {
     tracked!(min_function_alignment, Some(Align::EIGHT));
     tracked!(mir_emit_retag, true);
     tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]);
-    tracked!(mir_keep_place_mention, true);
     tracked!(mir_opt_level, Some(4));
+    tracked!(mir_preserve_ub, true);
     tracked!(move_size_limit, Some(4096));
     tracked!(mutable_noalias, false);
     tracked!(next_solver, NextSolverConfig { coherence: true, globally: true });
diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
index d49f5d9f9c3..c7feb9e949b 100644
--- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
+++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
@@ -223,7 +223,7 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
         // Since this optimization adds new basic blocks and invalidates others,
         // clean up the cfg to make it nicer for other passes
         if should_cleanup {
-            simplify_cfg(body);
+            simplify_cfg(tcx, body);
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 69e80ed54ea..c5732194424 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -63,7 +63,7 @@ impl<'tcx> crate::MirPass<'tcx> for Inline {
         let _guard = span.enter();
         if inline::<NormalInliner<'tcx>>(tcx, body) {
             debug!("running simplify cfg on {:?}", body.source);
-            simplify_cfg(body);
+            simplify_cfg(tcx, body);
             deref_finder(tcx, body);
         }
     }
@@ -99,7 +99,7 @@ impl<'tcx> crate::MirPass<'tcx> for ForceInline {
         let _guard = span.enter();
         if inline::<ForceInliner<'tcx>>(tcx, body) {
             debug!("running simplify cfg on {:?}", body.source);
-            simplify_cfg(body);
+            simplify_cfg(tcx, body);
             deref_finder(tcx, body);
         }
     }
diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs
index 8b4b214a3d4..9732225e48d 100644
--- a/compiler/rustc_mir_transform/src/jump_threading.rs
+++ b/compiler/rustc_mir_transform/src/jump_threading.rs
@@ -90,11 +90,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
         };
 
         for bb in body.basic_blocks.indices() {
-            let old_len = finder.opportunities.len();
-            // If we have any const-eval errors discard any opportunities found
-            if finder.start_from_switch(bb).is_none() {
-                finder.opportunities.truncate(old_len);
-            }
+            finder.start_from_switch(bb);
         }
 
         let opportunities = finder.opportunities;
@@ -201,28 +197,26 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
 
     /// Recursion entry point to find threading opportunities.
     #[instrument(level = "trace", skip(self))]
-    fn start_from_switch(&mut self, bb: BasicBlock) -> Option<()> {
+    fn start_from_switch(&mut self, bb: BasicBlock) {
         let bbdata = &self.body[bb];
         if bbdata.is_cleanup || self.loop_headers.contains(bb) {
-            return Some(());
+            return;
         }
-        let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return Some(()) };
-        let Some(discr) = discr.place() else { return Some(()) };
+        let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return };
+        let Some(discr) = discr.place() else { return };
         debug!(?discr, ?bb);
 
         let discr_ty = discr.ty(self.body, self.tcx).ty;
-        let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else {
-            return Some(());
-        };
+        let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return };
 
-        let Some(discr) = self.map.find(discr.as_ref()) else { return Some(()) };
+        let Some(discr) = self.map.find(discr.as_ref()) else { return };
         debug!(?discr);
 
         let cost = CostChecker::new(self.tcx, self.typing_env, None, self.body);
         let mut state = State::new_reachable();
 
         let conds = if let Some((value, then, else_)) = targets.as_static_if() {
-            let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
+            let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return };
             self.arena.alloc_from_iter([
                 Condition { value, polarity: Polarity::Eq, target: then },
                 Condition { value, polarity: Polarity::Ne, target: else_ },
@@ -248,10 +242,10 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
         mut state: State<ConditionSet<'a>>,
         mut cost: CostChecker<'_, 'tcx>,
         depth: usize,
-    ) -> Option<()> {
+    ) {
         // Do not thread through loop headers.
         if self.loop_headers.contains(bb) {
-            return Some(());
+            return;
         }
 
         debug!(cost = ?cost.cost());
@@ -259,16 +253,16 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
             self.body.basic_blocks[bb].statements.iter().enumerate().rev()
         {
             if self.is_empty(&state) {
-                return Some(());
+                return;
             }
 
             cost.visit_statement(stmt, Location { block: bb, statement_index });
             if cost.cost() > MAX_COST {
-                return Some(());
+                return;
             }
 
             // Attempt to turn the `current_condition` on `lhs` into a condition on another place.
-            self.process_statement(bb, stmt, &mut state)?;
+            self.process_statement(bb, stmt, &mut state);
 
             // When a statement mutates a place, assignments to that place that happen
             // above the mutation cannot fulfill a condition.
@@ -280,7 +274,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
         }
 
         if self.is_empty(&state) || depth >= MAX_BACKTRACK {
-            return Some(());
+            return;
         }
 
         let last_non_rec = self.opportunities.len();
@@ -293,9 +287,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
             match term.kind {
                 TerminatorKind::SwitchInt { ref discr, ref targets } => {
                     self.process_switch_int(discr, targets, bb, &mut state);
-                    self.find_opportunity(pred, state, cost, depth + 1)?;
+                    self.find_opportunity(pred, state, cost, depth + 1);
                 }
-                _ => self.recurse_through_terminator(pred, || state, &cost, depth)?,
+                _ => self.recurse_through_terminator(pred, || state, &cost, depth),
             }
         } else if let &[ref predecessors @ .., last_pred] = &predecessors[..] {
             for &pred in predecessors {
@@ -320,13 +314,12 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
             let first = &mut new_tos[0];
             *first = ThreadingOpportunity { chain: vec![bb], target: first.target };
             self.opportunities.truncate(last_non_rec + 1);
-            return Some(());
+            return;
         }
 
         for op in self.opportunities[last_non_rec..].iter_mut() {
             op.chain.push(bb);
         }
-        Some(())
     }
 
     /// Extract the mutated place from a statement.
@@ -440,23 +433,23 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
         lhs: PlaceIndex,
         rhs: &Operand<'tcx>,
         state: &mut State<ConditionSet<'a>>,
-    ) -> Option<()> {
+    ) {
         match rhs {
             // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
             Operand::Constant(constant) => {
-                let constant = self
-                    .ecx
-                    .eval_mir_constant(&constant.const_, constant.span, None)
-                    .discard_err()?;
+                let Some(constant) =
+                    self.ecx.eval_mir_constant(&constant.const_, constant.span, None).discard_err()
+                else {
+                    return;
+                };
                 self.process_constant(bb, lhs, constant, state);
             }
             // Transfer the conditions on the copied rhs.
             Operand::Move(rhs) | Operand::Copy(rhs) => {
-                let Some(rhs) = self.map.find(rhs.as_ref()) else { return Some(()) };
+                let Some(rhs) = self.map.find(rhs.as_ref()) else { return };
                 state.insert_place_idx(rhs, lhs, &self.map);
             }
         }
-        Some(())
     }
 
     #[instrument(level = "trace", skip(self))]
@@ -466,18 +459,14 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
         lhs_place: &Place<'tcx>,
         rhs: &Rvalue<'tcx>,
         state: &mut State<ConditionSet<'a>>,
-    ) -> Option<()> {
-        let Some(lhs) = self.map.find(lhs_place.as_ref()) else {
-            return Some(());
-        };
+    ) {
+        let Some(lhs) = self.map.find(lhs_place.as_ref()) else { return };
         match rhs {
-            Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?,
+            Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state),
             // Transfer the conditions on the copy rhs.
-            Rvalue::CopyForDeref(rhs) => {
-                self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)?
-            }
+            Rvalue::CopyForDeref(rhs) => self.process_operand(bb, lhs, &Operand::Copy(*rhs), state),
             Rvalue::Discriminant(rhs) => {
-                let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return Some(()) };
+                let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return };
                 state.insert_place_idx(rhs, lhs, &self.map);
             }
             // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
@@ -485,7 +474,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
                 let agg_ty = lhs_place.ty(self.body, self.tcx).ty;
                 let lhs = match kind {
                     // Do not support unions.
-                    AggregateKind::Adt(.., Some(_)) => return Some(()),
+                    AggregateKind::Adt(.., Some(_)) => return,
                     AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => {
                         if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant)
                             && let Some(discr_value) = self
@@ -498,23 +487,23 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
                         if let Some(idx) = self.map.apply(lhs, TrackElem::Variant(*variant_index)) {
                             idx
                         } else {
-                            return Some(());
+                            return;
                         }
                     }
                     _ => lhs,
                 };
                 for (field_index, operand) in operands.iter_enumerated() {
                     if let Some(field) = self.map.apply(lhs, TrackElem::Field(field_index)) {
-                        self.process_operand(bb, field, operand, state)?;
+                        self.process_operand(bb, field, operand, state);
                     }
                 }
             }
             // Transfer the conditions on the copy rhs, after inverting the value of the condition.
             Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => {
                 let layout = self.ecx.layout_of(place.ty(self.body, self.tcx).ty).unwrap();
-                let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return Some(()) };
-                let Some(place) = self.map.find(place.as_ref()) else { return Some(()) };
-                let conds = conditions.map(self.arena, |mut cond| {
+                let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return };
+                let Some(place) = self.map.find(place.as_ref()) else { return };
+                let Some(conds) = conditions.map(self.arena, |mut cond| {
                     cond.value = self
                         .ecx
                         .unary_op(UnOp::Not, &ImmTy::from_scalar_int(cond.value, layout))
@@ -522,7 +511,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
                         .to_scalar_int()
                         .discard_err()?;
                     Some(cond)
-                })?;
+                }) else {
+                    return;
+                };
                 state.insert_value_idx(place, conds, &self.map);
             }
             // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`.
@@ -532,34 +523,38 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
                 box (Operand::Move(place) | Operand::Copy(place), Operand::Constant(value))
                 | box (Operand::Constant(value), Operand::Move(place) | Operand::Copy(place)),
             ) => {
-                let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return Some(()) };
-                let Some(place) = self.map.find(place.as_ref()) else { return Some(()) };
+                let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return };
+                let Some(place) = self.map.find(place.as_ref()) else { return };
                 let equals = match op {
                     BinOp::Eq => ScalarInt::TRUE,
                     BinOp::Ne => ScalarInt::FALSE,
-                    _ => return Some(()),
+                    _ => return,
                 };
                 if value.const_.ty().is_floating_point() {
                     // Floating point equality does not follow bit-patterns.
                     // -0.0 and NaN both have special rules for equality,
                     // and therefore we cannot use integer comparisons for them.
                     // Avoid handling them, though this could be extended in the future.
-                    return Some(());
+                    return;
                 }
-                let value = value.const_.try_eval_scalar_int(self.tcx, self.typing_env)?;
-                let conds = conditions.map(self.arena, |c| {
+                let Some(value) = value.const_.try_eval_scalar_int(self.tcx, self.typing_env)
+                else {
+                    return;
+                };
+                let Some(conds) = conditions.map(self.arena, |c| {
                     Some(Condition {
                         value,
                         polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne },
                         ..c
                     })
-                })?;
+                }) else {
+                    return;
+                };
                 state.insert_value_idx(place, conds, &self.map);
             }
 
             _ => {}
         }
-        Some(())
     }
 
     #[instrument(level = "trace", skip(self))]
@@ -568,7 +563,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
         bb: BasicBlock,
         stmt: &Statement<'tcx>,
         state: &mut State<ConditionSet<'a>>,
-    ) -> Option<()> {
+    ) {
         let register_opportunity = |c: Condition| {
             debug!(?bb, ?c.target, "register");
             self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
@@ -581,32 +576,30 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
             // If we expect `discriminant(place) ?= A`,
             // we have an opportunity if `variant_index ?= A`.
             StatementKind::SetDiscriminant { box place, variant_index } => {
-                let Some(discr_target) = self.map.find_discr(place.as_ref()) else {
-                    return Some(());
-                };
+                let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return };
                 let enum_ty = place.ty(self.body, self.tcx).ty;
                 // `SetDiscriminant` guarantees that the discriminant is now `variant_index`.
                 // Even if the discriminant write does nothing due to niches, it is UB to set the
                 // discriminant when the data does not encode the desired discriminant.
-                let discr =
-                    self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err()?;
-                self.process_immediate(bb, discr_target, discr, state);
+                let Some(discr) =
+                    self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err()
+                else {
+                    return;
+                };
+                self.process_immediate(bb, discr_target, discr, state)
             }
             // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
             StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(
                 Operand::Copy(place) | Operand::Move(place),
             )) => {
-                let Some(conditions) = state.try_get(place.as_ref(), &self.map) else {
-                    return Some(());
-                };
-                conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity);
+                let Some(conditions) = state.try_get(place.as_ref(), &self.map) else { return };
+                conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity)
             }
             StatementKind::Assign(box (lhs_place, rhs)) => {
-                self.process_assign(bb, lhs_place, rhs, state)?;
+                self.process_assign(bb, lhs_place, rhs, state)
             }
             _ => {}
         }
-        Some(())
     }
 
     #[instrument(level = "trace", skip(self, state, cost))]
@@ -617,7 +610,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
         state: impl FnOnce() -> State<ConditionSet<'a>>,
         cost: &CostChecker<'_, 'tcx>,
         depth: usize,
-    ) -> Option<()> {
+    ) {
         let term = self.body.basic_blocks[bb].terminator();
         let place_to_flood = match term.kind {
             // We come from a target, so those are not possible.
@@ -632,9 +625,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
             | TerminatorKind::FalseUnwind { .. }
             | TerminatorKind::Yield { .. } => bug!("{term:?} invalid"),
             // Cannot reason about inline asm.
-            TerminatorKind::InlineAsm { .. } => return Some(()),
+            TerminatorKind::InlineAsm { .. } => return,
             // `SwitchInt` is handled specially.
-            TerminatorKind::SwitchInt { .. } => return Some(()),
+            TerminatorKind::SwitchInt { .. } => return,
             // We can recurse, no thing particular to do.
             TerminatorKind::Goto { .. } => None,
             // Flood the overwritten place, and progress through.
diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index 5059837328e..b37241185c9 100644
--- a/compiler/rustc_mir_transform/src/match_branches.rs
+++ b/compiler/rustc_mir_transform/src/match_branches.rs
@@ -43,7 +43,7 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification {
         }
 
         if should_cleanup {
-            simplify_cfg(body);
+            simplify_cfg(tcx, body);
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs
index 15fe77d5319..cb598ceb4df 100644
--- a/compiler/rustc_mir_transform/src/remove_place_mention.rs
+++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs
@@ -8,7 +8,7 @@ pub(super) struct RemovePlaceMention;
 
 impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        !sess.opts.unstable_opts.mir_keep_place_mention
+        !sess.opts.unstable_opts.mir_preserve_ub
     }
 
     fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
index 8a8cdafc690..43f80508e4a 100644
--- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
@@ -35,7 +35,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops {
         // if we applied optimizations, we potentially have some cfg to cleanup to
         // make it easier for further passes
         if should_simplify {
-            simplify_cfg(body);
+            simplify_cfg(tcx, body);
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs
index 5947637cded..4f2cce8ac10 100644
--- a/compiler/rustc_mir_transform/src/simplify.rs
+++ b/compiler/rustc_mir_transform/src/simplify.rs
@@ -26,6 +26,13 @@
 //! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we
 //! naively generate still contains the `_a = ()` write in the unreachable block "after" the
 //! return.
+//!
+//! **WARNING**: This is one of the few optimizations that runs on built and analysis MIR, and
+//! so its effects may affect the type-checking, borrow-checking, and other analysis of MIR.
+//! We must be extremely careful to only apply optimizations that preserve UB and all
+//! non-determinism, since changes here can affect which programs compile in an insta-stable way.
+//! The normal logic that a program with UB can be changed to do anything does not apply to
+//! pre-"runtime" MIR!
 
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
@@ -66,8 +73,8 @@ impl SimplifyCfg {
     }
 }
 
-pub(super) fn simplify_cfg(body: &mut Body<'_>) {
-    CfgSimplifier::new(body).simplify();
+pub(super) fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+    CfgSimplifier::new(tcx, body).simplify();
     remove_dead_blocks(body);
 
     // FIXME: Should probably be moved into some kind of pass manager
@@ -79,9 +86,9 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg {
         self.name()
     }
 
-    fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source);
-        simplify_cfg(body);
+        simplify_cfg(tcx, body);
     }
 
     fn is_required(&self) -> bool {
@@ -90,12 +97,13 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg {
 }
 
 struct CfgSimplifier<'a, 'tcx> {
+    preserve_switch_reads: bool,
     basic_blocks: &'a mut IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
     pred_count: IndexVec<BasicBlock, u32>,
 }
 
 impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
-    fn new(body: &'a mut Body<'tcx>) -> Self {
+    fn new(tcx: TyCtxt<'tcx>, body: &'a mut Body<'tcx>) -> Self {
         let mut pred_count = IndexVec::from_elem(0u32, &body.basic_blocks);
 
         // we can't use mir.predecessors() here because that counts
@@ -110,9 +118,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
             }
         }
 
+        // Preserve `SwitchInt` reads on built and analysis MIR, or if `-Zmir-preserve-ub`.
+        let preserve_switch_reads = matches!(body.phase, MirPhase::Built | MirPhase::Analysis(_))
+            || tcx.sess.opts.unstable_opts.mir_preserve_ub;
         let basic_blocks = body.basic_blocks_mut();
 
-        CfgSimplifier { basic_blocks, pred_count }
+        CfgSimplifier { preserve_switch_reads, basic_blocks, pred_count }
     }
 
     fn simplify(mut self) {
@@ -253,9 +264,15 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
 
     // turn a branch with all successors identical to a goto
     fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool {
-        match terminator.kind {
-            TerminatorKind::SwitchInt { .. } => {}
-            _ => return false,
+        // Removing a `SwitchInt` terminator may remove reads that result in UB,
+        // so we must not apply this optimization before borrowck or when
+        // `-Zmir-preserve-ub` is set.
+        if self.preserve_switch_reads {
+            return false;
+        }
+
+        let TerminatorKind::SwitchInt { .. } = terminator.kind else {
+            return false;
         };
 
         let first_succ = {
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 1b0794f79d3..36eee5f3086 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -2322,12 +2322,12 @@ options! {
     mir_include_spans: MirIncludeSpans = (MirIncludeSpans::default(), parse_mir_include_spans, [UNTRACKED],
         "include extra comments in mir pretty printing, like line numbers and statement indices, \
          details about types, etc. (boolean for all passes, 'nll' to enable in NLL MIR only, default: 'nll')"),
-    mir_keep_place_mention: bool = (false, parse_bool, [TRACKED],
-        "keep place mention MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
-        (default: no)"),
     #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")]
     mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED],
         "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
+    mir_preserve_ub: bool = (false, parse_bool, [TRACKED],
+        "keep place mention statements and reads in trivial SwitchInt terminators, which are interpreted \
+        e.g., by miri; implies -Zmir-opt-level=0 (default: no)"),
     mir_strip_debuginfo: MirStripDebugInfo = (MirStripDebugInfo::None, parse_mir_strip_debuginfo, [TRACKED],
         "Whether to remove some of the MIR debug info from methods.  Default: None"),
     move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED],
diff --git a/library/core/src/iter/adapters/enumerate.rs b/library/core/src/iter/adapters/enumerate.rs
index bd093e279c3..f7b9f0b7a5e 100644
--- a/library/core/src/iter/adapters/enumerate.rs
+++ b/library/core/src/iter/adapters/enumerate.rs
@@ -23,6 +23,39 @@ impl<I> Enumerate<I> {
     pub(in crate::iter) fn new(iter: I) -> Enumerate<I> {
         Enumerate { iter, count: 0 }
     }
+
+    /// Retrieve the current position of the iterator.
+    ///
+    /// If the iterator has not advanced, the position returned will be 0.
+    ///
+    /// The position may also exceed the bounds of the iterator to allow for calculating
+    /// the displacement of the iterator from following calls to [`Iterator::next`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(next_index)]
+    ///
+    /// let arr = ['a', 'b'];
+    ///
+    /// let mut iter = arr.iter().enumerate();
+    ///
+    /// assert_eq!(iter.next_index(), 0);
+    /// assert_eq!(iter.next(), Some((0, &'a')));
+    ///
+    /// assert_eq!(iter.next_index(), 1);
+    /// assert_eq!(iter.next_index(), 1);
+    /// assert_eq!(iter.next(), Some((1, &'b')));
+    ///
+    /// assert_eq!(iter.next_index(), 2);
+    /// assert_eq!(iter.next(), None);
+    /// assert_eq!(iter.next_index(), 2);
+    /// ```
+    #[inline]
+    #[unstable(feature = "next_index", issue = "130711")]
+    pub fn next_index(&self) -> usize {
+        self.count
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/coretests/tests/iter/adapters/enumerate.rs b/library/coretests/tests/iter/adapters/enumerate.rs
index b57d51c077e..2294f856b58 100644
--- a/library/coretests/tests/iter/adapters/enumerate.rs
+++ b/library/coretests/tests/iter/adapters/enumerate.rs
@@ -120,3 +120,13 @@ fn test_double_ended_enumerate() {
     assert_eq!(it.next_back(), Some((2, 3)));
     assert_eq!(it.next(), None);
 }
+
+#[test]
+fn test_empty_iterator_enumerate_next_index() {
+    let mut it = empty::<i32>().enumerate();
+    assert_eq!(it.next_index(), 0);
+    assert_eq!(it.next_index(), 0);
+    assert_eq!(it.next(), None);
+    assert_eq!(it.next_index(), 0);
+    assert_eq!(it.next_index(), 0);
+}
diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs
index 1c43bfe0ed4..ac111575938 100644
--- a/library/coretests/tests/lib.rs
+++ b/library/coretests/tests/lib.rs
@@ -63,6 +63,7 @@
 #![feature(maybe_uninit_write_slice)]
 #![feature(min_specialization)]
 #![feature(never_type)]
+#![feature(next_index)]
 #![feature(numfmt)]
 #![feature(pattern)]
 #![feature(pointer_is_aligned_to)]
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 6a5b38dd504..68fa42ee9e6 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1194,8 +1194,7 @@ pub fn rustc_cargo(
         let enzyme_dir = builder.build.out.join(arch).join("enzyme").join("lib");
         cargo.rustflag("-L").rustflag(enzyme_dir.to_str().expect("Invalid path"));
 
-        if !builder.config.dry_run() {
-            let llvm_config = builder.llvm_config(builder.config.build).unwrap();
+        if let Some(llvm_config) = builder.llvm_config(builder.config.build) {
             let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config);
             cargo.rustflag("-l").rustflag(&format!("Enzyme-{llvm_version_major}"));
         }
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index 80d92135dd3..83083e12ef1 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -584,6 +584,7 @@ Select which editor you would like to set up [default: None]: ";
                 "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0",
                 "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45",
                 "b5dd299b93dca3ceeb9b335f929293cb3d4bf4977866fbe7ceeac2a8a9f99088",
+                "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9",
             ],
             EditorKind::Helix => &[
                 "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233",
@@ -602,10 +603,12 @@ Select which editor you would like to set up [default: None]: ";
                 "4eecb58a2168b252077369da446c30ed0e658301efe69691979d1ef0443928f4",
                 "c394386e6133bbf29ffd32c8af0bb3d4aac354cba9ee051f29612aa9350f8f8d",
                 "e53e9129ca5ee5dcbd6ec8b68c2d87376474eb154992deba3c6d9ab1703e0717",
+                "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893",
             ],
             EditorKind::Zed => &[
                 "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c",
                 "a5380cf5dd9328731aecc5dfb240d16dac46ed272126b9728006151ef42f5909",
+                "2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26",
             ],
         }
     }
diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el
index 6b40371d9af..90bd38aa894 100644
--- a/src/etc/rust_analyzer_eglot.el
+++ b/src/etc/rust_analyzer_eglot.el
@@ -8,10 +8,11 @@
                                                          "check"
                                                          "--json-output"])
                  :linkedProjects ["Cargo.toml"
-                                  "src/bootstrap/Cargo.toml"
-                                  "src/tools/rust-analyzer/Cargo.toml"
                                   "compiler/rustc_codegen_cranelift/Cargo.toml"
-                                  "compiler/rustc_codegen_gcc/Cargo.toml"]
+                                  "compiler/rustc_codegen_gcc/Cargo.toml"
+                                  "library/Cargo.toml"
+                                  "src/bootstrap/Cargo.toml"
+                                  "src/tools/rust-analyzer/Cargo.toml"]
                  :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt"
                                               "--edition=2021"])
                  :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json
index da7d326a512..5ce886a9b65 100644
--- a/src/etc/rust_analyzer_settings.json
+++ b/src/etc/rust_analyzer_settings.json
@@ -9,11 +9,11 @@
     ],
     "rust-analyzer.linkedProjects": [
         "Cargo.toml",
+        "compiler/rustc_codegen_cranelift/Cargo.toml",
+        "compiler/rustc_codegen_gcc/Cargo.toml",
         "library/Cargo.toml",
         "src/bootstrap/Cargo.toml",
-        "src/tools/rust-analyzer/Cargo.toml",
-        "compiler/rustc_codegen_cranelift/Cargo.toml",
-        "compiler/rustc_codegen_gcc/Cargo.toml"
+        "src/tools/rust-analyzer/Cargo.toml"
     ],
     "rust-analyzer.rustfmt.overrideCommand": [
         "${workspaceFolder}/build/host/rustfmt/bin/rustfmt",
@@ -36,5 +36,10 @@
     },
     "rust-analyzer.server.extraEnv": {
         "RUSTUP_TOOLCHAIN": "nightly"
+    },
+    "files.associations": {
+        "*.fixed": "rust",
+        "*.pp": "rust",
+        "*.mir": "rust"
     }
 }
diff --git a/src/etc/rust_analyzer_zed.json b/src/etc/rust_analyzer_zed.json
index abc6ddbc213..3461ff887d9 100644
--- a/src/etc/rust_analyzer_zed.json
+++ b/src/etc/rust_analyzer_zed.json
@@ -21,15 +21,15 @@
         },
         "linkedProjects": [
           "Cargo.toml",
+          "compiler/rustc_codegen_cranelift/Cargo.toml",
+          "compiler/rustc_codegen_gcc/Cargo.toml",
           "library/Cargo.toml",
           "src/bootstrap/Cargo.toml",
-          "src/tools/rust-analyzer/Cargo.toml",
-          "compiler/rustc_codegen_cranelift/Cargo.toml",
-          "compiler/rustc_codegen_gcc/Cargo.toml"
+          "src/tools/rust-analyzer/Cargo.toml"
         ],
         "procMacro": {
-            "enable": true,
-            "server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
+          "enable": true,
+          "server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
         },
         "rustc": {
           "source": "./Cargo.toml"
@@ -47,5 +47,8 @@
         }
       }
     }
+  },
+  "file_types": {
+    "Rust": ["fixed", "pp", "mir"]
   }
 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 034ecb2f6c1..6ecb67c776c 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2773,10 +2773,35 @@ fn clean_maybe_renamed_item<'tcx>(
 ) -> Vec<Item> {
     use hir::ItemKind;
 
-    let def_id = item.owner_id.to_def_id();
-    let mut name = if renamed.is_some() { renamed } else { cx.tcx.hir_opt_name(item.hir_id()) };
+    fn get_name(
+        cx: &DocContext<'_>,
+        item: &hir::Item<'_>,
+        renamed: Option<Symbol>,
+    ) -> Option<Symbol> {
+        renamed.or_else(|| cx.tcx.hir_opt_name(item.hir_id()))
+    }
 
+    let def_id = item.owner_id.to_def_id();
     cx.with_param_env(def_id, |cx| {
+        // These kinds of item either don't need a `name` or accept a `None` one so we handle them
+        // before.
+        match item.kind {
+            ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx),
+            ItemKind::Use(path, kind) => {
+                return clean_use_statement(
+                    item,
+                    get_name(cx, item, renamed),
+                    path,
+                    kind,
+                    cx,
+                    &mut FxHashSet::default(),
+                );
+            }
+            _ => {}
+        }
+
+        let mut name = get_name(cx, item, renamed).unwrap();
+
         let kind = match item.kind {
             ItemKind::Static(_, ty, mutability, body_id) => StaticItem(Static {
                 type_: Box::new(clean_ty(ty, cx)),
@@ -2815,7 +2840,7 @@ fn clean_maybe_renamed_item<'tcx>(
                         item_type: Some(type_),
                     })),
                     item.owner_id.def_id.to_def_id(),
-                    name.unwrap(),
+                    name,
                     import_id,
                     renamed,
                 ));
@@ -2838,17 +2863,14 @@ fn clean_maybe_renamed_item<'tcx>(
                 generics: clean_generics(generics, cx),
                 fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
             }),
-            ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx),
             ItemKind::Macro(_, macro_def, MacroKind::Bang) => MacroItem(Macro {
-                source: display_macro_source(cx, name.unwrap(), macro_def),
+                source: display_macro_source(cx, name, macro_def),
                 macro_rules: macro_def.macro_rules,
             }),
-            ItemKind::Macro(_, _, macro_kind) => {
-                clean_proc_macro(item, name.as_mut().unwrap(), macro_kind, cx)
-            }
+            ItemKind::Macro(_, _, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx),
             // proc macros can have a name set by attributes
             ItemKind::Fn { ref sig, generics, body: body_id, .. } => {
-                clean_fn_or_proc_macro(item, sig, generics, body_id, name.as_mut().unwrap(), cx)
+                clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
             }
             ItemKind::Trait(_, _, _, generics, bounds, item_ids) => {
                 let items = item_ids
@@ -2864,10 +2886,7 @@ fn clean_maybe_renamed_item<'tcx>(
                 }))
             }
             ItemKind::ExternCrate(orig_name, _) => {
-                return clean_extern_crate(item, name.unwrap(), orig_name, cx);
-            }
-            ItemKind::Use(path, kind) => {
-                return clean_use_statement(item, name, path, kind, cx, &mut FxHashSet::default());
+                return clean_extern_crate(item, name, orig_name, cx);
             }
             _ => span_bug!(item.span, "not yet converted"),
         };
@@ -2876,7 +2895,7 @@ fn clean_maybe_renamed_item<'tcx>(
             cx,
             kind,
             item.owner_id.def_id.to_def_id(),
-            name.unwrap(),
+            name,
             import_id,
             renamed,
         )]
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 5921ba86639..b6b2684dc6d 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -169,7 +169,7 @@ pub const MIRI_DEFAULT_ARGS: &[&str] = &[
     "-Zalways-encode-mir",
     "-Zextra-const-ub-checks",
     "-Zmir-emit-retag",
-    "-Zmir-keep-place-mention",
+    "-Zmir-preserve-ub",
     "-Zmir-opt-level=0",
     "-Zmir-enable-passes=-CheckAlignment,-CheckNull",
     // Deduplicating diagnostics means we miss events when tracking what happens during an
diff --git a/src/tools/miri/tests/fail/read_from_trivial_switch.rs b/src/tools/miri/tests/fail/read_from_trivial_switch.rs
new file mode 100644
index 00000000000..d34b1cd5820
--- /dev/null
+++ b/src/tools/miri/tests/fail/read_from_trivial_switch.rs
@@ -0,0 +1,14 @@
+// Ensure that we don't optimize out `SwitchInt` reads even if that terminator
+// branches to the same basic block on every target, since the operand may have
+// side-effects that affect analysis of the MIR.
+//
+// See <https://github.com/rust-lang/miri/issues/4237>.
+
+use std::mem::MaybeUninit;
+
+fn main() {
+    let uninit: MaybeUninit<i32> = MaybeUninit::uninit();
+    let bad_ref: &i32 = unsafe { uninit.assume_init_ref() };
+    let &(0 | _) = bad_ref;
+    //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+}
diff --git a/src/tools/miri/tests/fail/read_from_trivial_switch.stderr b/src/tools/miri/tests/fail/read_from_trivial_switch.stderr
new file mode 100644
index 00000000000..6b3d4539b96
--- /dev/null
+++ b/src/tools/miri/tests/fail/read_from_trivial_switch.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+  --> tests/fail/read_from_trivial_switch.rs:LL:CC
+   |
+LL |     let &(0 | _) = bad_ref;
+   |         ^^^^^^^^ using uninitialized data, but this operation requires initialized memory
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at tests/fail/read_from_trivial_switch.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir
index d52241b459e..2a965fe67b9 100644
--- a/tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir
+++ b/tests/mir-opt/building/match/exponential_or.match_tuple.SimplifyCfg-initial.after.mir
@@ -24,43 +24,47 @@ fn match_tuple(_1: (u32, bool, Option<i32>, u32)) -> u32 {
 
     bb1: {
         _0 = const 0_u32;
-        goto -> bb10;
+        goto -> bb11;
     }
 
     bb2: {
-        _2 = discriminant((_1.2: std::option::Option<i32>));
-        switchInt(move _2) -> [0: bb4, 1: bb3, otherwise: bb1];
+        switchInt(copy (_1.1: bool)) -> [0: bb3, otherwise: bb3];
     }
 
     bb3: {
-        switchInt(copy (((_1.2: std::option::Option<i32>) as Some).0: i32)) -> [1: bb4, 8: bb4, otherwise: bb1];
+        _2 = discriminant((_1.2: std::option::Option<i32>));
+        switchInt(move _2) -> [0: bb5, 1: bb4, otherwise: bb1];
     }
 
     bb4: {
-        _5 = Le(const 6_u32, copy (_1.3: u32));
-        switchInt(move _5) -> [0: bb5, otherwise: bb7];
+        switchInt(copy (((_1.2: std::option::Option<i32>) as Some).0: i32)) -> [1: bb5, 8: bb5, otherwise: bb1];
     }
 
     bb5: {
-        _3 = Le(const 13_u32, copy (_1.3: u32));
-        switchInt(move _3) -> [0: bb1, otherwise: bb6];
+        _5 = Le(const 6_u32, copy (_1.3: u32));
+        switchInt(move _5) -> [0: bb6, otherwise: bb8];
     }
 
     bb6: {
-        _4 = Le(copy (_1.3: u32), const 16_u32);
-        switchInt(move _4) -> [0: bb1, otherwise: bb8];
+        _3 = Le(const 13_u32, copy (_1.3: u32));
+        switchInt(move _3) -> [0: bb1, otherwise: bb7];
     }
 
     bb7: {
-        _6 = Le(copy (_1.3: u32), const 9_u32);
-        switchInt(move _6) -> [0: bb5, otherwise: bb8];
+        _4 = Le(copy (_1.3: u32), const 16_u32);
+        switchInt(move _4) -> [0: bb1, otherwise: bb9];
     }
 
     bb8: {
-        falseEdge -> [real: bb9, imaginary: bb1];
+        _6 = Le(copy (_1.3: u32), const 9_u32);
+        switchInt(move _6) -> [0: bb6, otherwise: bb9];
     }
 
     bb9: {
+        falseEdge -> [real: bb10, imaginary: bb1];
+    }
+
+    bb10: {
         StorageLive(_7);
         _7 = copy (_1.0: u32);
         StorageLive(_8);
@@ -74,10 +78,10 @@ fn match_tuple(_1: (u32, bool, Option<i32>, u32)) -> u32 {
         StorageDead(_9);
         StorageDead(_8);
         StorageDead(_7);
-        goto -> bb10;
+        goto -> bb11;
     }
 
-    bb10: {
+    bb11: {
         return;
     }
 }
diff --git a/tests/mir-opt/dead-store-elimination/place_mention.rs b/tests/mir-opt/dead-store-elimination/place_mention.rs
index 5e4a286a208..1848a028297 100644
--- a/tests/mir-opt/dead-store-elimination/place_mention.rs
+++ b/tests/mir-opt/dead-store-elimination/place_mention.rs
@@ -2,7 +2,7 @@
 // and don't remove it as a dead store.
 //
 //@ test-mir-pass: DeadStoreElimination-initial
-//@ compile-flags: -Zmir-keep-place-mention
+//@ compile-flags: -Zmir-preserve-ub
 
 // EMIT_MIR place_mention.main.DeadStoreElimination-initial.diff
 fn main() {
diff --git a/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir
index 889ff6f9f5e..be0931eaa61 100644
--- a/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir
+++ b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir
@@ -14,7 +14,7 @@ fn single_switchint() -> () {
     }
 
     bb1: {
-        switchInt(copy (_2.0: i32)) -> [3: bb8, 4: bb8, otherwise: bb7];
+        switchInt(copy (_2.0: i32)) -> [3: bb9, 4: bb9, otherwise: bb8];
     }
 
     bb2: {
@@ -22,7 +22,7 @@ fn single_switchint() -> () {
     }
 
     bb3: {
-        falseEdge -> [real: bb12, imaginary: bb4];
+        falseEdge -> [real: bb14, imaginary: bb4];
     }
 
     bb4: {
@@ -30,43 +30,51 @@ fn single_switchint() -> () {
     }
 
     bb5: {
-        falseEdge -> [real: bb11, imaginary: bb6];
+        falseEdge -> [real: bb13, imaginary: bb6];
     }
 
     bb6: {
-        falseEdge -> [real: bb10, imaginary: bb1];
+        switchInt(copy (_2.1: bool)) -> [0: bb7, otherwise: bb7];
     }
 
     bb7: {
-        _1 = const 5_i32;
-        goto -> bb13;
+        falseEdge -> [real: bb12, imaginary: bb1];
     }
 
     bb8: {
-        falseEdge -> [real: bb9, imaginary: bb7];
+        _1 = const 5_i32;
+        goto -> bb15;
     }
 
     bb9: {
-        _1 = const 4_i32;
-        goto -> bb13;
+        switchInt(copy (_2.1: bool)) -> [0: bb10, otherwise: bb10];
     }
 
     bb10: {
-        _1 = const 3_i32;
-        goto -> bb13;
+        falseEdge -> [real: bb11, imaginary: bb8];
     }
 
     bb11: {
-        _1 = const 2_i32;
-        goto -> bb13;
+        _1 = const 4_i32;
+        goto -> bb15;
     }
 
     bb12: {
-        _1 = const 1_i32;
-        goto -> bb13;
+        _1 = const 3_i32;
+        goto -> bb15;
     }
 
     bb13: {
+        _1 = const 2_i32;
+        goto -> bb15;
+    }
+
+    bb14: {
+        _1 = const 1_i32;
+        goto -> bb15;
+    }
+
+    bb15: {
         StorageDead(_2);
         StorageDead(_1);
         _0 = const ();
diff --git a/tests/mir-opt/read_from_trivial_switch.main.SimplifyCfg-initial.diff b/tests/mir-opt/read_from_trivial_switch.main.SimplifyCfg-initial.diff
new file mode 100644
index 00000000000..87758408a1c
--- /dev/null
+++ b/tests/mir-opt/read_from_trivial_switch.main.SimplifyCfg-initial.diff
@@ -0,0 +1,49 @@
+- // MIR for `main` before SimplifyCfg-initial
++ // MIR for `main` after SimplifyCfg-initial
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: &i32;
+      let _2: i32;
+      scope 1 {
+          debug ref_ => _1;
+          scope 2 {
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = const 1_i32;
+          _1 = &_2;
+          FakeRead(ForLet(None), _1);
+          PlaceMention(_1);
+-         switchInt(copy (*_1)) -> [0: bb2, otherwise: bb1];
++         switchInt(copy (*_1)) -> [0: bb1, otherwise: bb1];
+      }
+  
+      bb1: {
+-         goto -> bb5;
+-     }
+- 
+-     bb2: {
+-         goto -> bb5;
+-     }
+- 
+-     bb3: {
+-         goto -> bb1;
+-     }
+- 
+-     bb4: {
+-         FakeRead(ForMatchedPlace(None), _1);
+-         unreachable;
+-     }
+- 
+-     bb5: {
+          _0 = const ();
+          StorageDead(_2);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/read_from_trivial_switch.rs b/tests/mir-opt/read_from_trivial_switch.rs
new file mode 100644
index 00000000000..1c64c1d45e8
--- /dev/null
+++ b/tests/mir-opt/read_from_trivial_switch.rs
@@ -0,0 +1,15 @@
+// Ensure that we don't optimize out `SwitchInt` reads even if that terminator
+// branches to the same basic block on every target, since the operand may have
+// side-effects that affect analysis of the MIR.
+//
+// See <https://github.com/rust-lang/miri/issues/4237>.
+
+//@ test-mir-pass: SimplifyCfg-initial
+//@ compile-flags: -Zmir-preserve-ub
+
+// EMIT_MIR read_from_trivial_switch.main.SimplifyCfg-initial.diff
+fn main() {
+    let ref_ = &1i32;
+    // CHECK: switchInt
+    let &(0 | _) = ref_;
+}
diff --git a/tests/ui/pattern/uninit-trivial.rs b/tests/ui/pattern/uninit-trivial.rs
new file mode 100644
index 00000000000..6ea6796c1c1
--- /dev/null
+++ b/tests/ui/pattern/uninit-trivial.rs
@@ -0,0 +1,8 @@
+// Regression test for the semantic changes in
+// <https://github.com/rust-lang/rust/pull/139042>.
+
+fn main() {
+    let x;
+    let (0 | _) = x;
+    //~^ ERROR used binding `x` isn't initialized
+}
diff --git a/tests/ui/pattern/uninit-trivial.stderr b/tests/ui/pattern/uninit-trivial.stderr
new file mode 100644
index 00000000000..2ff8557c945
--- /dev/null
+++ b/tests/ui/pattern/uninit-trivial.stderr
@@ -0,0 +1,16 @@
+error[E0381]: used binding `x` isn't initialized
+  --> $DIR/uninit-trivial.rs:6:10
+   |
+LL |     let x;
+   |         - binding declared here but left uninitialized
+LL |     let (0 | _) = x;
+   |          ^^^^^ `x` used here but it isn't initialized
+   |
+help: consider assigning a value
+   |
+LL |     let x = 42;
+   |           ++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0381`.
diff --git a/triagebot.toml b/triagebot.toml
index 226f024c156..0f17d022fbb 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -659,6 +659,7 @@ message_on_add = [
     """\
 /poll Approve stable backport of #{number}?
 approve
+approve (but does not justify new dot release on its own)
 decline
 don't know
 """,