Unrolled build for #138679
Rollup merge of #138679 - Shunpoco:issue-125323, r=oli-obk

Issue-125323: ICE non-ADT in struct pattern when long time constant evaluation is in for loop

This PR fixes #125323

## Context
According to the issue, the ICE happens since #121206.
In the PR, some error methods were reorganized. For example, has_errors() was renamed to has_errors_exclude_lint_errors(). However, some codes which used the original has_errors() were not switched to has_errors_exclude_lint_errors(). I finally found that report_error() in writeback.rs causes this ICE. Currently the method uses tainted_by_errors() to get guar (ErrorGuaranteed), but originally it used dcx().has_errors() but it wasn't changed to has_errors_exclude_lint_errors() when changes in #121206 were merged. I don't think I fully understand how an error is propagated, but I suppose that the error from long time constant evaluation is unexpectedly propagated other parts (in this ICE, for loop), then cause the non-ADT in struct pattern ICE.

## Change
- Fix report_error() in writeback.rs: use dcx().has_errors_exclude_lint_errors() instead of tainted_by_errors() to prevent error propagation from constant evaluation.
- Add test for the ICE
- Modify some tests to align the change: Due to this fix, E0282 error happens (or not happen anymore) in some tests.

## NOTE
The 4th commit aims to revert the fix in #123516 because I confirmed that the ICE solved by the PR doesn't happen if I modify report_error(). I think the root cause of that ICE is the same as #125323 . But I can discard this commit since we can fix #125323 without it.
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 6167b0d..d9bcd5e 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -152,7 +152,10 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
 
 impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns {
     fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) {
-        if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind {
+        // 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()
+        {
             let variant = cx
                 .typeck_results()
                 .pat_ty(pat)
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 151ec4c..588ff68 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1202,10 +1202,10 @@
     /// Return the live symbols in the crate for dead code check.
     ///
     /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone).
-    query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx (
+    query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx Result<(
         LocalDefIdSet,
         LocalDefIdMap<FxIndexSet<DefId>>,
-    ) {
+    ), ErrorGuaranteed> {
         arena_cache
         desc { "finding live symbols in crate" }
     }
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 49dd21f..877b5ad 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -4,11 +4,12 @@
 // is dead.
 
 use std::mem;
+use std::ops::ControlFlow;
 
 use hir::def_id::{LocalDefIdMap, LocalDefIdSet};
 use rustc_abi::FieldIdx;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::MultiSpan;
+use rustc_errors::{ErrorGuaranteed, MultiSpan};
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
 use rustc_hir::intravisit::{self, Visitor};
@@ -178,12 +179,12 @@ fn handle_assign(&mut self, expr: &'tcx hir::Expr<'tcx>) {
             .iter()
             .any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
         {
-            self.visit_expr(expr);
+            let _ = self.visit_expr(expr);
         } else if let hir::ExprKind::Field(base, ..) = expr.kind {
             // Ignore write to field
             self.handle_assign(base);
         } else {
-            self.visit_expr(expr);
+            let _ = self.visit_expr(expr);
         }
     }
 
@@ -318,7 +319,7 @@ fn handle_offset_of(&mut self, expr: &'tcx hir::Expr<'tcx>) {
         }
     }
 
-    fn mark_live_symbols(&mut self) {
+    fn mark_live_symbols(&mut self) -> <MarkSymbolVisitor<'tcx> as Visitor<'tcx>>::Result {
         while let Some(work) = self.worklist.pop() {
             let (mut id, comes_from_allow_expect) = work;
 
@@ -366,8 +367,10 @@ fn mark_live_symbols(&mut self) {
                 continue;
             }
 
-            self.visit_node(self.tcx.hir_node_by_def_id(id));
+            self.visit_node(self.tcx.hir_node_by_def_id(id))?;
         }
+
+        ControlFlow::Continue(())
     }
 
     /// Automatically generated items marked with `rustc_trivial_field_reads`
@@ -391,11 +394,14 @@ fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool {
         false
     }
 
-    fn visit_node(&mut self, node: Node<'tcx>) {
+    fn visit_node(
+        &mut self,
+        node: Node<'tcx>,
+    ) -> <MarkSymbolVisitor<'tcx> as Visitor<'tcx>>::Result {
         if let Node::ImplItem(impl_item) = node
             && self.should_ignore_impl_item(impl_item)
         {
-            return;
+            return ControlFlow::Continue(());
         }
 
         let unconditionally_treated_fields_as_live =
@@ -403,7 +409,7 @@ fn visit_node(&mut self, node: Node<'tcx>) {
         let had_repr_simd = self.repr_has_repr_simd;
         self.repr_unconditionally_treats_fields_as_live = false;
         self.repr_has_repr_simd = false;
-        match node {
+        let walk_result = match node {
             Node::Item(item) => match item.kind {
                 hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
                     let def = self.tcx.adt_def(item.owner_id);
@@ -413,7 +419,7 @@ fn visit_node(&mut self, node: Node<'tcx>) {
 
                     intravisit::walk_item(self, item)
                 }
-                hir::ItemKind::ForeignMod { .. } => {}
+                hir::ItemKind::ForeignMod { .. } => ControlFlow::Continue(()),
                 hir::ItemKind::Trait(.., trait_item_refs) => {
                     // mark assoc ty live if the trait is live
                     for trait_item in trait_item_refs {
@@ -431,7 +437,7 @@ fn visit_node(&mut self, node: Node<'tcx>) {
                 if let Some(trait_id) = self.tcx.trait_of_assoc(trait_item_id) {
                     self.check_def_id(trait_id);
                 }
-                intravisit::walk_trait_item(self, trait_item);
+                intravisit::walk_trait_item(self, trait_item)
             }
             Node::ImplItem(impl_item) => {
                 let item = self.tcx.local_parent(impl_item.owner_id.def_id);
@@ -452,16 +458,16 @@ fn visit_node(&mut self, node: Node<'tcx>) {
                         _ => {}
                     }
                 }
-                intravisit::walk_impl_item(self, impl_item);
+                intravisit::walk_impl_item(self, impl_item)
             }
-            Node::ForeignItem(foreign_item) => {
-                intravisit::walk_foreign_item(self, foreign_item);
-            }
+            Node::ForeignItem(foreign_item) => intravisit::walk_foreign_item(self, foreign_item),
             Node::OpaqueTy(opaq) => intravisit::walk_opaque_ty(self, opaq),
-            _ => {}
-        }
+            _ => ControlFlow::Continue(()),
+        };
         self.repr_has_repr_simd = had_repr_simd;
         self.repr_unconditionally_treats_fields_as_live = unconditionally_treated_fields_as_live;
+
+        walk_result
     }
 
     fn mark_as_used_if_union(&mut self, adt: ty::AdtDef<'tcx>, fields: &[hir::ExprField<'_>]) {
@@ -511,15 +517,25 @@ fn check_impl_or_impl_item_live(&mut self, local_def_id: LocalDefId) -> bool {
 }
 
 impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
-    fn visit_nested_body(&mut self, body: hir::BodyId) {
-        let old_maybe_typeck_results =
-            self.maybe_typeck_results.replace(self.tcx.typeck_body(body));
+    type Result = ControlFlow<ErrorGuaranteed>;
+
+    fn visit_nested_body(&mut self, body: hir::BodyId) -> Self::Result {
+        let typeck_results = self.tcx.typeck_body(body);
+
+        // The result shouldn't be tainted, otherwise it will cause ICE.
+        if let Some(guar) = typeck_results.tainted_by_errors {
+            return ControlFlow::Break(guar);
+        }
+
+        let old_maybe_typeck_results = self.maybe_typeck_results.replace(typeck_results);
         let body = self.tcx.hir_body(body);
-        self.visit_body(body);
+        let result = self.visit_body(body);
         self.maybe_typeck_results = old_maybe_typeck_results;
+
+        result
     }
 
-    fn visit_variant_data(&mut self, def: &'tcx hir::VariantData<'tcx>) {
+    fn visit_variant_data(&mut self, def: &'tcx hir::VariantData<'tcx>) -> Self::Result {
         let tcx = self.tcx;
         let unconditionally_treat_fields_as_live = self.repr_unconditionally_treats_fields_as_live;
         let has_repr_simd = self.repr_has_repr_simd;
@@ -536,10 +552,10 @@ fn visit_variant_data(&mut self, def: &'tcx hir::VariantData<'tcx>) {
         });
         self.live_symbols.extend(live_fields);
 
-        intravisit::walk_struct_def(self, def);
+        intravisit::walk_struct_def(self, def)
     }
 
-    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
         match expr.kind {
             hir::ExprKind::Path(ref qpath @ QPath::TypeRelative(..)) => {
                 let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
@@ -575,20 +591,22 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
             _ => (),
         }
 
-        intravisit::walk_expr(self, expr);
+        intravisit::walk_expr(self, expr)
     }
 
-    fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
+    fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> Self::Result {
         // Inside the body, ignore constructions of variants
         // necessary for the pattern to match. Those construction sites
         // can't be reached unless the variant is constructed elsewhere.
         let len = self.ignore_variant_stack.len();
         self.ignore_variant_stack.extend(arm.pat.necessary_variants());
-        intravisit::walk_arm(self, arm);
+        let result = intravisit::walk_arm(self, arm);
         self.ignore_variant_stack.truncate(len);
+
+        result
     }
 
-    fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
+    fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Self::Result {
         self.in_pat = true;
         match pat.kind {
             PatKind::Struct(ref path, fields, _) => {
@@ -602,11 +620,13 @@ fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
             _ => (),
         }
 
-        intravisit::walk_pat(self, pat);
+        let result = intravisit::walk_pat(self, pat);
         self.in_pat = false;
+
+        result
     }
 
-    fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) {
+    fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) -> Self::Result {
         match &expr.kind {
             rustc_hir::PatExprKind::Path(qpath) => {
                 // mark the type of variant live when meeting E::V in expr
@@ -619,37 +639,41 @@ fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) {
             }
             _ => {}
         }
-        intravisit::walk_pat_expr(self, expr);
+        intravisit::walk_pat_expr(self, expr)
     }
 
-    fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
+    fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) -> Self::Result {
         self.handle_res(path.res);
-        intravisit::walk_path(self, path);
+        intravisit::walk_path(self, path)
     }
 
-    fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
+    fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) -> Self::Result {
         // When inline const blocks are used in pattern position, paths
         // referenced by it should be considered as used.
         let in_pat = mem::replace(&mut self.in_pat, false);
 
         self.live_symbols.insert(c.def_id);
-        intravisit::walk_anon_const(self, c);
+        let result = intravisit::walk_anon_const(self, c);
 
         self.in_pat = in_pat;
+
+        result
     }
 
-    fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
+    fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) -> Self::Result {
         // When inline const blocks are used in pattern position, paths
         // referenced by it should be considered as used.
         let in_pat = mem::replace(&mut self.in_pat, false);
 
         self.live_symbols.insert(c.def_id);
-        intravisit::walk_inline_const(self, c);
+        let result = intravisit::walk_inline_const(self, c);
 
         self.in_pat = in_pat;
+
+        result
     }
 
-    fn visit_trait_ref(&mut self, t: &'tcx hir::TraitRef<'tcx>) {
+    fn visit_trait_ref(&mut self, t: &'tcx hir::TraitRef<'tcx>) -> Self::Result {
         if let Some(trait_def_id) = t.path.res.opt_def_id()
             && let Some(segment) = t.path.segments.last()
             && let Some(args) = segment.args
@@ -671,7 +695,7 @@ fn visit_trait_ref(&mut self, t: &'tcx hir::TraitRef<'tcx>) {
             }
         }
 
-        intravisit::walk_trait_ref(self, t);
+        intravisit::walk_trait_ref(self, t)
     }
 }
 
@@ -818,7 +842,7 @@ fn create_and_seed_worklist(
 fn live_symbols_and_ignored_derived_traits(
     tcx: TyCtxt<'_>,
     (): (),
-) -> (LocalDefIdSet, LocalDefIdMap<FxIndexSet<DefId>>) {
+) -> Result<(LocalDefIdSet, LocalDefIdMap<FxIndexSet<DefId>>), ErrorGuaranteed> {
     let (worklist, mut unsolved_items) = create_and_seed_worklist(tcx);
     let mut symbol_visitor = MarkSymbolVisitor {
         worklist,
@@ -832,7 +856,9 @@ fn live_symbols_and_ignored_derived_traits(
         ignore_variant_stack: vec![],
         ignored_derived_traits: Default::default(),
     };
-    symbol_visitor.mark_live_symbols();
+    if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() {
+        return Err(guar);
+    }
 
     // We have marked the primary seeds as live. We now need to process unsolved items from traits
     // and trait impls: add them to the work list if the trait or the implemented type is live.
@@ -846,14 +872,16 @@ fn live_symbols_and_ignored_derived_traits(
         symbol_visitor
             .worklist
             .extend(items_to_check.drain(..).map(|id| (id, ComesFromAllowExpect::No)));
-        symbol_visitor.mark_live_symbols();
+        if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() {
+            return Err(guar);
+        }
 
         items_to_check.extend(unsolved_items.extract_if(.., |&mut local_def_id| {
             symbol_visitor.check_impl_or_impl_item_live(local_def_id)
         }));
     }
 
-    (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
+    Ok((symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits))
 }
 
 struct DeadItem {
@@ -1133,7 +1161,12 @@ fn is_live_code(&self, def_id: LocalDefId) -> bool {
 }
 
 fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
-    let (live_symbols, ignored_derived_traits) = tcx.live_symbols_and_ignored_derived_traits(());
+    let Ok((live_symbols, ignored_derived_traits)) =
+        tcx.live_symbols_and_ignored_derived_traits(()).as_ref()
+    else {
+        return;
+    };
+
     let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits };
 
     let module_items = tcx.hir_module_items(module);
diff --git a/tests/crashes/125323.rs b/tests/crashes/125323.rs
deleted file mode 100644
index 180b7bb..0000000
--- a/tests/crashes/125323.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-//@ known-bug: rust-lang/rust#125323
-fn main() {
-    for _ in 0..0 {
-        [(); loop {}];
-    }
-}
diff --git a/tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.rs b/tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.rs
new file mode 100644
index 0000000..465b54e
--- /dev/null
+++ b/tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.rs
@@ -0,0 +1,14 @@
+// The test confirms ICE-125323 is fixed.
+//
+// This warning tests there is no warning about dead code
+// when there is a constant evaluation error.
+#![warn(unused)]
+fn should_not_be_dead() {}
+
+fn main() {
+    for _ in 0..0 {
+        [(); loop {}]; //~ ERROR constant evaluation is taking a long time
+    }
+
+    should_not_be_dead();
+}
diff --git a/tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.stderr b/tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.stderr
new file mode 100644
index 0000000..32a1846
--- /dev/null
+++ b/tests/ui/consts/do-not-ice-long-constant-evaluation-in-for-loop.stderr
@@ -0,0 +1,17 @@
+error: constant evaluation is taking a long time
+  --> $DIR/do-not-ice-long-constant-evaluation-in-for-loop.rs:10:14
+   |
+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
+   |
+LL |         [(); loop {}];
+   |              ^^^^^^^
+   = note: `#[deny(long_running_const_eval)]` on by default
+
+error: aborting due to 1 previous error
+