From c973b5051072d339c21a3e9493ff0fabac8f8f97 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 18 Sep 2026 23:47:48 -0400 Subject: [PATCH 1/2] Avoid generating overlapping assignments in DSE --- .../src/dead_store_elimination.rs | 7 +++++ ...eadStoreElimination-final.panic-abort.diff | 2 +- ...adStoreElimination-final.panic-unwind.diff | 2 +- ...eadStoreElimination-final.panic-abort.diff | 16 ++++++++++ ...adStoreElimination-final.panic-unwind.diff | 16 ++++++++++ .../dead-store-elimination/call_arg_copy.rs | 29 ++++++++++++++----- 6 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 tests/mir-opt/dead-store-elimination/call_arg_copy.ret_is_arg.DeadStoreElimination-final.panic-abort.diff create mode 100644 tests/mir-opt/dead-store-elimination/call_arg_copy.ret_is_arg.DeadStoreElimination-final.panic-unwind.diff diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 9b0e2a0663eca..7c41217f402ff 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -63,6 +63,13 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { loc, ); + // The logic in LivenessTransferFunction isn't quite what we need; it ignores call + // destinations that are just locals because they are killed by the call, which makes + // it eligible to be moved-from in the argument list. That's backwards. + if !destination.is_indirect() { + state.insert(destination.local); + } + for (index, arg) in args.iter().map(|a| &a.node).enumerate().rev() { if let Operand::Copy(place) = *arg && !place.is_indirect() diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff index 012dd7d88a5f0..2c4e7ae552844 100644 --- a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff @@ -5,7 +5,7 @@ let mut _0: (); bb0: { - _1[_2] = passthrough_usize(copy _2) -> [return: bb1, unwind unreachable]; + _1[_2] = identity::(copy _2) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff index fcd0ae43e2897..86f9b93130d72 100644 --- a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff @@ -5,7 +5,7 @@ let mut _0: (); bb0: { - _1[_2] = passthrough_usize(copy _2) -> [return: bb1, unwind continue]; + _1[_2] = identity::(copy _2) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.ret_is_arg.DeadStoreElimination-final.panic-abort.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.ret_is_arg.DeadStoreElimination-final.panic-abort.diff new file mode 100644 index 0000000000000..d4d49d0fb79e7 --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.ret_is_arg.DeadStoreElimination-final.panic-abort.diff @@ -0,0 +1,16 @@ +- // MIR for `ret_is_arg` before DeadStoreElimination-final ++ // MIR for `ret_is_arg` after DeadStoreElimination-final + + fn ret_is_arg(_1: [u64; 5]) -> [u64; 5] { + let mut _0: [u64; 5]; + + bb0: { + _1 = identity::<[u64; 5]>(copy _1) -> [return: bb1, unwind unreachable]; + } + + bb1: { + _0 = copy _1; + return; + } + } + diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.ret_is_arg.DeadStoreElimination-final.panic-unwind.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.ret_is_arg.DeadStoreElimination-final.panic-unwind.diff new file mode 100644 index 0000000000000..d4d49d0fb79e7 --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.ret_is_arg.DeadStoreElimination-final.panic-unwind.diff @@ -0,0 +1,16 @@ +- // MIR for `ret_is_arg` before DeadStoreElimination-final ++ // MIR for `ret_is_arg` after DeadStoreElimination-final + + fn ret_is_arg(_1: [u64; 5]) -> [u64; 5] { + let mut _0: [u64; 5]; + + bb0: { + _1 = identity::<[u64; 5]>(copy _1) -> [return: bb1, unwind unreachable]; + } + + bb1: { + _0 = copy _1; + return; + } + } + diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs index 00a9a49c2abb8..1a7f9a1494dbe 100644 --- a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs @@ -6,6 +6,7 @@ #![feature(custom_mir)] #![allow(internal_features)] +use std::convert::identity; use std::intrinsics::mir::*; #[inline(never)] @@ -40,22 +41,17 @@ fn move_packed(packed: Packed) { } } -#[inline(never)] -fn passthrough_usize(a: usize) -> usize { - a -} - // EMIT_MIR call_arg_copy.move_index.DeadStoreElimination-final.diff #[custom_mir(dialect = "analysis")] fn move_index(a: [usize; 10], b: usize) { // CHECK-LABEL: fn move_index( - // CHECK: = passthrough_usize(copy _2) + // CHECK: = identity::(copy _2) mir! { { // The index is used again after the operand is evaluated to - // evaluate the destionation place, so the argument cannot be turned + // evaluate the destination place, so the argument cannot be turned // into a move. - Call(a[b] = passthrough_usize(b), ReturnTo(ret), UnwindContinue()) + Call(a[b] = identity(b), ReturnTo(ret), UnwindContinue()) } ret = { Return() @@ -63,8 +59,25 @@ fn move_index(a: [usize; 10], b: usize) { } } +// EMIT_MIR call_arg_copy.ret_is_arg.DeadStoreElimination-final.diff +#[custom_mir(dialect = "runtime")] +fn ret_is_arg(x: [u64; 5]) -> [u64; 5] { + // CHECK-LABEL: fn ret_is_arg(_1 + // CHECK: _1 = identity::<[u64; 5]>(copy _1) -> + mir! { + { + Call(x = identity(x), ReturnTo(bb1), UnwindUnreachable()) + } + bb1 = { + RET = x; + Return() + } + } +} + fn main() { move_simple(1); move_packed(Packed { x: 0, y: 1 }); move_index([0; _], 1); + ret_is_arg([0; 5]); } From db6e110a114382fd2f1912354272c2f51d01ddca Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sun, 19 Oct 2025 10:03:06 +0100 Subject: [PATCH 2/2] visit_nested_body and visit_fn for LateLint should return earlier if typeck is tainted We started to check typeck result's tainted_by_errors in check_pat for LateLint, But ideally the check should be in a better place which all lints profit from it. --- compiler/rustc_lint/src/builtin.rs | 5 +---- compiler/rustc_lint/src/late.rs | 21 +++++++++++++++++-- tests/crashes/138361.rs | 6 ------ ...ng-constant-evaluation-cause-dead-code.rs} | 0 ...onstant-evaluation-cause-dead-code.stderr} | 4 ++-- ...ng-constant-evaluation-cause-ice-in-sty.rs | 5 +++++ ...onstant-evaluation-cause-ice-in-sty.stderr | 17 +++++++++++++++ 7 files changed, 44 insertions(+), 14 deletions(-) delete mode 100644 tests/crashes/138361.rs rename tests/ui/consts/{do-not-ice-long-constant-evaluation-in-for-loop.rs => long-constant-evaluation-cause-dead-code.rs} (100%) rename tests/ui/consts/{do-not-ice-long-constant-evaluation-in-for-loop.stderr => long-constant-evaluation-cause-dead-code.stderr} (78%) create mode 100644 tests/ui/consts/long-constant-evaluation-cause-ice-in-sty.rs create mode 100644 tests/ui/consts/long-constant-evaluation-cause-ice-in-sty.stderr diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 2d571d8b8e13d..d4f213833aa48 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -148,10 +148,7 @@ declare_lint_pass!(NonShorthandFieldPatterns => [NON_SHORTHAND_FIELD_PATTERNS]); impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) { - // The result shouldn't be tainted, otherwise it will cause ICE. - if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind - && cx.typeck_results().tainted_by_errors.is_none() - { + if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind { let variant = cx .typeck_results() .pat_ty(pat) diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index ac185c65aa127..93ce4118c0ab7 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -99,7 +99,15 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas // The body and typeck results are also set in `visit_fn`. // Only fetch the results if this is for a new body. if old_enclosing_body != Some(body_id) && !self.actually_rustdoc { - self.context.typeck_results = Some(self.context.tcx.typeck_body(body_id)); + let typeck_results = self.context.tcx.typeck_body(body_id); + + // To avoid ICE, skip visiting the body if the typeck_results is tainted. + if typeck_results.tainted_by_errors.is_some() { + self.context.enclosing_body = old_enclosing_body; + self.context.typeck_results = old_typeck_results; + return; + } + self.context.typeck_results = Some(typeck_results); } let body = self.context.tcx.hir_body(body_id); @@ -189,7 +197,16 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas let old_enclosing_body = self.context.enclosing_body.replace(body_id); let old_typeck_results = self.context.typeck_results; if !self.actually_rustdoc { - self.context.typeck_results = Some(self.context.tcx.typeck_body(body_id)); + let typeck_results = self.context.tcx.typeck_body(body_id); + + // To avoid ICE, skip visiting the body if the typeck_results is tainted. + if typeck_results.tainted_by_errors.is_some() { + self.context.enclosing_body = old_enclosing_body; + self.context.typeck_results = old_typeck_results; + return; + } + + self.context.typeck_results = Some(typeck_results); } let body = self.context.tcx.hir_body(body_id); lint_callback!(self, check_fn, fk, decl, body, span, id); diff --git a/tests/crashes/138361.rs b/tests/crashes/138361.rs deleted file mode 100644 index 8661ed374744a..0000000000000 --- a/tests/crashes/138361.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ known-bug: #138361 - -fn main() { - [0; loop{}]; - std::mem::transmute(4) -} diff --git a/tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.rs b/tests/ui/consts/long-constant-evaluation-cause-dead-code.rs similarity index 100% rename from tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.rs rename to tests/ui/consts/long-constant-evaluation-cause-dead-code.rs diff --git a/tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.stderr b/tests/ui/consts/long-constant-evaluation-cause-dead-code.stderr similarity index 78% rename from tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.stderr rename to tests/ui/consts/long-constant-evaluation-cause-dead-code.stderr index c28f37bc16705..08160f8c78166 100644 --- a/tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.stderr +++ b/tests/ui/consts/long-constant-evaluation-cause-dead-code.stderr @@ -1,5 +1,5 @@ error: constant evaluation is taking a long time - --> $DIR/do-not-ice-long-constant-evaluation-in-for-loop.rs:10:14 + --> $DIR/long-constant-evaluation-cause-dead-code.rs:10:14 | LL | [(); loop {}]; | ^^^^^^^ @@ -7,7 +7,7 @@ LL | [(); loop {}]; = note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. If your compilation actually takes a long time, you can safely allow the lint help: the constant being evaluated - --> $DIR/do-not-ice-long-constant-evaluation-in-for-loop.rs:10:14 + --> $DIR/long-constant-evaluation-cause-dead-code.rs:10:14 | LL | [(); loop {}]; | ^^^^^^^ diff --git a/tests/ui/consts/long-constant-evaluation-cause-ice-in-sty.rs b/tests/ui/consts/long-constant-evaluation-cause-ice-in-sty.rs new file mode 100644 index 0000000000000..04d0f7ee2de84 --- /dev/null +++ b/tests/ui/consts/long-constant-evaluation-cause-ice-in-sty.rs @@ -0,0 +1,5 @@ +// The test confirms ICE-138361 is fixed. +fn main() { + [0; loop{}]; //~ ERROR constant evaluation is taking a long time + std::mem::transmute(4) +} diff --git a/tests/ui/consts/long-constant-evaluation-cause-ice-in-sty.stderr b/tests/ui/consts/long-constant-evaluation-cause-ice-in-sty.stderr new file mode 100644 index 0000000000000..58c62d61adaab --- /dev/null +++ b/tests/ui/consts/long-constant-evaluation-cause-ice-in-sty.stderr @@ -0,0 +1,17 @@ +error: constant evaluation is taking a long time + --> $DIR/long-constant-evaluation-cause-ice-in-sty.rs:3:7 + | +LL | [0; loop{}]; + | ^^^^^^ + | + = note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. + If your compilation actually takes a long time, you can safely allow the lint +help: the constant being evaluated + --> $DIR/long-constant-evaluation-cause-ice-in-sty.rs:3:7 + | +LL | [0; loop{}]; + | ^^^^^^ + = note: `#[deny(long_running_const_eval)]` on by default + +error: aborting due to 1 previous error +