Skip to content

Commit 8f11852

Browse files
authored
Rollup merge of rust-lang#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 rust-lang#125323 ## Context According to the issue, the ICE happens since rust-lang#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 rust-lang#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 rust-lang#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 rust-lang#125323 . But I can discard this commit since we can fix rust-lang#125323 without it.
2 parents 6380899 + 845ff73 commit 8f11852

File tree

6 files changed

+113
-52
lines changed

6 files changed

+113
-52
lines changed

compiler/rustc_lint/src/builtin.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,10 @@ declare_lint_pass!(NonShorthandFieldPatterns => [NON_SHORTHAND_FIELD_PATTERNS]);
152152

153153
impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns {
154154
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) {
155-
if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind {
155+
// The result shouldn't be tainted, otherwise it will cause ICE.
156+
if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind
157+
&& cx.typeck_results().tainted_by_errors.is_none()
158+
{
156159
let variant = cx
157160
.typeck_results()
158161
.pat_ty(pat)

compiler/rustc_middle/src/query/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,10 +1202,10 @@ rustc_queries! {
12021202
/// Return the live symbols in the crate for dead code check.
12031203
///
12041204
/// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone).
1205-
query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx (
1205+
query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx Result<(
12061206
LocalDefIdSet,
12071207
LocalDefIdMap<FxIndexSet<DefId>>,
1208-
) {
1208+
), ErrorGuaranteed> {
12091209
arena_cache
12101210
desc { "finding live symbols in crate" }
12111211
}

compiler/rustc_passes/src/dead.rs

Lines changed: 76 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
// is dead.
55

66
use std::mem;
7+
use std::ops::ControlFlow;
78

89
use hir::def_id::{LocalDefIdMap, LocalDefIdSet};
910
use rustc_abi::FieldIdx;
1011
use rustc_data_structures::fx::FxIndexSet;
11-
use rustc_errors::MultiSpan;
12+
use rustc_errors::{ErrorGuaranteed, MultiSpan};
1213
use rustc_hir::def::{CtorOf, DefKind, Res};
1314
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
1415
use rustc_hir::intravisit::{self, Visitor};
@@ -178,12 +179,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
178179
.iter()
179180
.any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
180181
{
181-
self.visit_expr(expr);
182+
let _ = self.visit_expr(expr);
182183
} else if let hir::ExprKind::Field(base, ..) = expr.kind {
183184
// Ignore write to field
184185
self.handle_assign(base);
185186
} else {
186-
self.visit_expr(expr);
187+
let _ = self.visit_expr(expr);
187188
}
188189
}
189190

@@ -318,7 +319,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
318319
}
319320
}
320321

321-
fn mark_live_symbols(&mut self) {
322+
fn mark_live_symbols(&mut self) -> <MarkSymbolVisitor<'tcx> as Visitor<'tcx>>::Result {
322323
while let Some(work) = self.worklist.pop() {
323324
let (mut id, comes_from_allow_expect) = work;
324325

@@ -366,8 +367,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
366367
continue;
367368
}
368369

369-
self.visit_node(self.tcx.hir_node_by_def_id(id));
370+
self.visit_node(self.tcx.hir_node_by_def_id(id))?;
370371
}
372+
373+
ControlFlow::Continue(())
371374
}
372375

373376
/// Automatically generated items marked with `rustc_trivial_field_reads`
@@ -391,19 +394,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
391394
false
392395
}
393396

394-
fn visit_node(&mut self, node: Node<'tcx>) {
397+
fn visit_node(
398+
&mut self,
399+
node: Node<'tcx>,
400+
) -> <MarkSymbolVisitor<'tcx> as Visitor<'tcx>>::Result {
395401
if let Node::ImplItem(impl_item) = node
396402
&& self.should_ignore_impl_item(impl_item)
397403
{
398-
return;
404+
return ControlFlow::Continue(());
399405
}
400406

401407
let unconditionally_treated_fields_as_live =
402408
self.repr_unconditionally_treats_fields_as_live;
403409
let had_repr_simd = self.repr_has_repr_simd;
404410
self.repr_unconditionally_treats_fields_as_live = false;
405411
self.repr_has_repr_simd = false;
406-
match node {
412+
let walk_result = match node {
407413
Node::Item(item) => match item.kind {
408414
hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
409415
let def = self.tcx.adt_def(item.owner_id);
@@ -413,7 +419,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
413419

414420
intravisit::walk_item(self, item)
415421
}
416-
hir::ItemKind::ForeignMod { .. } => {}
422+
hir::ItemKind::ForeignMod { .. } => ControlFlow::Continue(()),
417423
hir::ItemKind::Trait(.., trait_item_refs) => {
418424
// mark assoc ty live if the trait is live
419425
for trait_item in trait_item_refs {
@@ -431,7 +437,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
431437
if let Some(trait_id) = self.tcx.trait_of_assoc(trait_item_id) {
432438
self.check_def_id(trait_id);
433439
}
434-
intravisit::walk_trait_item(self, trait_item);
440+
intravisit::walk_trait_item(self, trait_item)
435441
}
436442
Node::ImplItem(impl_item) => {
437443
let item = self.tcx.local_parent(impl_item.owner_id.def_id);
@@ -452,16 +458,16 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
452458
_ => {}
453459
}
454460
}
455-
intravisit::walk_impl_item(self, impl_item);
456-
}
457-
Node::ForeignItem(foreign_item) => {
458-
intravisit::walk_foreign_item(self, foreign_item);
461+
intravisit::walk_impl_item(self, impl_item)
459462
}
463+
Node::ForeignItem(foreign_item) => intravisit::walk_foreign_item(self, foreign_item),
460464
Node::OpaqueTy(opaq) => intravisit::walk_opaque_ty(self, opaq),
461-
_ => {}
462-
}
465+
_ => ControlFlow::Continue(()),
466+
};
463467
self.repr_has_repr_simd = had_repr_simd;
464468
self.repr_unconditionally_treats_fields_as_live = unconditionally_treated_fields_as_live;
469+
470+
walk_result
465471
}
466472

467473
fn mark_as_used_if_union(&mut self, adt: ty::AdtDef<'tcx>, fields: &[hir::ExprField<'_>]) {
@@ -511,15 +517,25 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
511517
}
512518

513519
impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
514-
fn visit_nested_body(&mut self, body: hir::BodyId) {
515-
let old_maybe_typeck_results =
516-
self.maybe_typeck_results.replace(self.tcx.typeck_body(body));
520+
type Result = ControlFlow<ErrorGuaranteed>;
521+
522+
fn visit_nested_body(&mut self, body: hir::BodyId) -> Self::Result {
523+
let typeck_results = self.tcx.typeck_body(body);
524+
525+
// The result shouldn't be tainted, otherwise it will cause ICE.
526+
if let Some(guar) = typeck_results.tainted_by_errors {
527+
return ControlFlow::Break(guar);
528+
}
529+
530+
let old_maybe_typeck_results = self.maybe_typeck_results.replace(typeck_results);
517531
let body = self.tcx.hir_body(body);
518-
self.visit_body(body);
532+
let result = self.visit_body(body);
519533
self.maybe_typeck_results = old_maybe_typeck_results;
534+
535+
result
520536
}
521537

522-
fn visit_variant_data(&mut self, def: &'tcx hir::VariantData<'tcx>) {
538+
fn visit_variant_data(&mut self, def: &'tcx hir::VariantData<'tcx>) -> Self::Result {
523539
let tcx = self.tcx;
524540
let unconditionally_treat_fields_as_live = self.repr_unconditionally_treats_fields_as_live;
525541
let has_repr_simd = self.repr_has_repr_simd;
@@ -536,10 +552,10 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
536552
});
537553
self.live_symbols.extend(live_fields);
538554

539-
intravisit::walk_struct_def(self, def);
555+
intravisit::walk_struct_def(self, def)
540556
}
541557

542-
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
558+
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
543559
match expr.kind {
544560
hir::ExprKind::Path(ref qpath @ QPath::TypeRelative(..)) => {
545561
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
@@ -575,20 +591,22 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
575591
_ => (),
576592
}
577593

578-
intravisit::walk_expr(self, expr);
594+
intravisit::walk_expr(self, expr)
579595
}
580596

581-
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
597+
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> Self::Result {
582598
// Inside the body, ignore constructions of variants
583599
// necessary for the pattern to match. Those construction sites
584600
// can't be reached unless the variant is constructed elsewhere.
585601
let len = self.ignore_variant_stack.len();
586602
self.ignore_variant_stack.extend(arm.pat.necessary_variants());
587-
intravisit::walk_arm(self, arm);
603+
let result = intravisit::walk_arm(self, arm);
588604
self.ignore_variant_stack.truncate(len);
605+
606+
result
589607
}
590608

591-
fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
609+
fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Self::Result {
592610
self.in_pat = true;
593611
match pat.kind {
594612
PatKind::Struct(ref path, fields, _) => {
@@ -602,11 +620,13 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
602620
_ => (),
603621
}
604622

605-
intravisit::walk_pat(self, pat);
623+
let result = intravisit::walk_pat(self, pat);
606624
self.in_pat = false;
625+
626+
result
607627
}
608628

609-
fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) {
629+
fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) -> Self::Result {
610630
match &expr.kind {
611631
rustc_hir::PatExprKind::Path(qpath) => {
612632
// mark the type of variant live when meeting E::V in expr
@@ -619,37 +639,41 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
619639
}
620640
_ => {}
621641
}
622-
intravisit::walk_pat_expr(self, expr);
642+
intravisit::walk_pat_expr(self, expr)
623643
}
624644

625-
fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
645+
fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) -> Self::Result {
626646
self.handle_res(path.res);
627-
intravisit::walk_path(self, path);
647+
intravisit::walk_path(self, path)
628648
}
629649

630-
fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
650+
fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) -> Self::Result {
631651
// When inline const blocks are used in pattern position, paths
632652
// referenced by it should be considered as used.
633653
let in_pat = mem::replace(&mut self.in_pat, false);
634654

635655
self.live_symbols.insert(c.def_id);
636-
intravisit::walk_anon_const(self, c);
656+
let result = intravisit::walk_anon_const(self, c);
637657

638658
self.in_pat = in_pat;
659+
660+
result
639661
}
640662

641-
fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
663+
fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) -> Self::Result {
642664
// When inline const blocks are used in pattern position, paths
643665
// referenced by it should be considered as used.
644666
let in_pat = mem::replace(&mut self.in_pat, false);
645667

646668
self.live_symbols.insert(c.def_id);
647-
intravisit::walk_inline_const(self, c);
669+
let result = intravisit::walk_inline_const(self, c);
648670

649671
self.in_pat = in_pat;
672+
673+
result
650674
}
651675

652-
fn visit_trait_ref(&mut self, t: &'tcx hir::TraitRef<'tcx>) {
676+
fn visit_trait_ref(&mut self, t: &'tcx hir::TraitRef<'tcx>) -> Self::Result {
653677
if let Some(trait_def_id) = t.path.res.opt_def_id()
654678
&& let Some(segment) = t.path.segments.last()
655679
&& let Some(args) = segment.args
@@ -671,7 +695,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
671695
}
672696
}
673697

674-
intravisit::walk_trait_ref(self, t);
698+
intravisit::walk_trait_ref(self, t)
675699
}
676700
}
677701

@@ -818,7 +842,7 @@ fn create_and_seed_worklist(
818842
fn live_symbols_and_ignored_derived_traits(
819843
tcx: TyCtxt<'_>,
820844
(): (),
821-
) -> (LocalDefIdSet, LocalDefIdMap<FxIndexSet<DefId>>) {
845+
) -> Result<(LocalDefIdSet, LocalDefIdMap<FxIndexSet<DefId>>), ErrorGuaranteed> {
822846
let (worklist, mut unsolved_items) = create_and_seed_worklist(tcx);
823847
let mut symbol_visitor = MarkSymbolVisitor {
824848
worklist,
@@ -832,7 +856,9 @@ fn live_symbols_and_ignored_derived_traits(
832856
ignore_variant_stack: vec![],
833857
ignored_derived_traits: Default::default(),
834858
};
835-
symbol_visitor.mark_live_symbols();
859+
if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() {
860+
return Err(guar);
861+
}
836862

837863
// We have marked the primary seeds as live. We now need to process unsolved items from traits
838864
// 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(
846872
symbol_visitor
847873
.worklist
848874
.extend(items_to_check.drain(..).map(|id| (id, ComesFromAllowExpect::No)));
849-
symbol_visitor.mark_live_symbols();
875+
if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() {
876+
return Err(guar);
877+
}
850878

851879
items_to_check.extend(unsolved_items.extract_if(.., |&mut local_def_id| {
852880
symbol_visitor.check_impl_or_impl_item_live(local_def_id)
853881
}));
854882
}
855883

856-
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
884+
Ok((symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits))
857885
}
858886

859887
struct DeadItem {
@@ -1133,7 +1161,12 @@ impl<'tcx> DeadVisitor<'tcx> {
11331161
}
11341162

11351163
fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
1136-
let (live_symbols, ignored_derived_traits) = tcx.live_symbols_and_ignored_derived_traits(());
1164+
let Ok((live_symbols, ignored_derived_traits)) =
1165+
tcx.live_symbols_and_ignored_derived_traits(()).as_ref()
1166+
else {
1167+
return;
1168+
};
1169+
11371170
let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits };
11381171

11391172
let module_items = tcx.hir_module_items(module);

tests/crashes/125323.rs

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// The test confirms ICE-125323 is fixed.
2+
//
3+
// This warning tests there is no warning about dead code
4+
// when there is a constant evaluation error.
5+
#![warn(unused)]
6+
fn should_not_be_dead() {}
7+
8+
fn main() {
9+
for _ in 0..0 {
10+
[(); loop {}]; //~ ERROR constant evaluation is taking a long time
11+
}
12+
13+
should_not_be_dead();
14+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: constant evaluation is taking a long time
2+
--> $DIR/do-not-ice-long-constant-evaluation-in-for-loop.rs:10:14
3+
|
4+
LL | [(); loop {}];
5+
| ^^^^^^^
6+
|
7+
= note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
8+
If your compilation actually takes a long time, you can safely allow the lint.
9+
help: the constant being evaluated
10+
--> $DIR/do-not-ice-long-constant-evaluation-in-for-loop.rs:10:14
11+
|
12+
LL | [(); loop {}];
13+
| ^^^^^^^
14+
= note: `#[deny(long_running_const_eval)]` on by default
15+
16+
error: aborting due to 1 previous error
17+

0 commit comments

Comments
 (0)