Skip to content

Commit 0b862fa

Browse files
committed
Add more robust handling of nested query cycles
1 parent a7db9c1 commit 0b862fa

File tree

13 files changed

+155
-42
lines changed

13 files changed

+155
-42
lines changed

compiler/rustc_middle/src/queries.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ use rustc_data_structures::sorted_map::SortedMap;
6060
use rustc_data_structures::steal::Steal;
6161
use rustc_data_structures::svh::Svh;
6262
use rustc_data_structures::unord::{UnordMap, UnordSet};
63-
use rustc_errors::ErrorGuaranteed;
63+
use rustc_errors::{ErrorGuaranteed, catch_fatal_errors};
6464
use rustc_hir as hir;
6565
use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem};
6666
use rustc_hir::def::{DefKind, DocLinkResMap};

compiler/rustc_middle/src/query/plumbing.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ pub struct QuerySystem<'tcx> {
167167
pub extern_providers: ExternProviders,
168168

169169
pub jobs: AtomicU64,
170+
171+
pub cycle_handler_nesting: Lock<u8>,
170172
}
171173

172174
#[derive(Copy, Clone)]
@@ -446,6 +448,11 @@ macro_rules! define_callbacks {
446448
}
447449
}
448450

451+
/// Calls `self.description` or returns a fallback if there was a fatal error
452+
pub fn catch_description(&self, tcx: TyCtxt<'tcx>) -> String {
453+
catch_fatal_errors(|| self.description(tcx)).unwrap_or_else(|_| format!("<error describing {}>", self.query_name()))
454+
}
455+
449456
/// Returns the default span for this query if `span` is a dummy span.
450457
pub fn default_span(&self, tcx: TyCtxt<'tcx>, span: Span) -> Span {
451458
if !span.is_dummy() {
@@ -463,6 +470,11 @@ macro_rules! define_callbacks {
463470
)*
464471
}
465472
}
473+
474+
/// Calls `self.default_span` or returns `DUMMY_SP` if there was a fatal error
475+
pub fn catch_default_span(&self, tcx: TyCtxt<'tcx>, span: Span) -> Span {
476+
catch_fatal_errors(|| self.default_span(tcx, span)).unwrap_or(DUMMY_SP)
477+
}
466478
}
467479

468480
/// Holds a `QueryVTable` for each query.

compiler/rustc_query_impl/src/error.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,28 @@ pub(crate) struct Cycle {
7979
)]
8080
pub note_span: (),
8181
}
82+
83+
#[derive(Subdiagnostic)]
84+
#[note("...when {$stack_bottom}")]
85+
pub(crate) struct NestedCycleBottom {
86+
pub stack_bottom: String,
87+
}
88+
89+
#[derive(Diagnostic)]
90+
#[diag("internal compiler error: query cycle when printing cycle detected")]
91+
pub(crate) struct NestedCycle {
92+
#[primary_span]
93+
pub span: Span,
94+
#[subdiagnostic]
95+
pub stack_bottom: NestedCycleBottom,
96+
#[subdiagnostic]
97+
pub cycle_stack: Vec<CycleStack>,
98+
#[subdiagnostic]
99+
pub stack_count: StackCount,
100+
#[subdiagnostic]
101+
pub cycle_usage: Option<CycleUsage>,
102+
#[note(
103+
"see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information"
104+
)]
105+
pub note_span: (),
106+
}

compiler/rustc_query_impl/src/execution.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::mem::ManuallyDrop;
44
use rustc_data_structures::hash_table::{Entry, HashTable};
55
use rustc_data_structures::stack::ensure_sufficient_stack;
66
use rustc_data_structures::sync::{DynSend, DynSync};
7-
use rustc_data_structures::{outline, sharded, sync};
7+
use rustc_data_structures::{defer, outline, sharded, sync};
88
use rustc_errors::FatalError;
99
use rustc_middle::dep_graph::{DepGraphData, DepNodeKey, SerializedDepNodeIndex};
1010
use rustc_middle::query::{
@@ -17,6 +17,7 @@ use rustc_span::{DUMMY_SP, Span};
1717
use tracing::warn;
1818

1919
use crate::dep_graph::{DepNode, DepNodeIndex};
20+
use crate::handle_cycle_error;
2021
use crate::job::{QueryJobInfo, QueryJobMap, create_cycle_error, find_cycle_in_stack};
2122
use crate::plumbing::{current_query_job, loadable_from_disk, next_job_id, start_query};
2223
use crate::query_impl::for_each_query_vtable;
@@ -114,8 +115,29 @@ fn handle_cycle<'tcx, C: QueryCache>(
114115
key: C::Key,
115116
cycle: Cycle<'tcx>,
116117
) -> C::Value {
117-
let error = create_cycle_error(tcx, &cycle);
118-
(query.handle_cycle_error_fn)(tcx, key, cycle, error)
118+
let nested;
119+
{
120+
let mut nesting = tcx.query_system.cycle_handler_nesting.lock();
121+
nested = match *nesting {
122+
0 => false,
123+
1 => true,
124+
_ => {
125+
// Don't print further nested errors to avoid cases of infinite recursion
126+
tcx.dcx().delayed_bug("doubly nested cycle error").raise_fatal()
127+
}
128+
};
129+
*nesting += 1;
130+
}
131+
let _guard = defer(|| *tcx.query_system.cycle_handler_nesting.lock() -= 1);
132+
133+
let error = create_cycle_error(tcx, &cycle, nested);
134+
135+
if nested {
136+
// Avoid custom handlers and only use the robust `create_cycle_error` for nested cycle errors
137+
handle_cycle_error::default(error)
138+
} else {
139+
(query.handle_cycle_error_fn)(tcx, key, cycle, error)
140+
}
119141
}
120142

121143
/// Guard object representing the responsibility to execute a query job and

compiler/rustc_query_impl/src/handle_cycle_error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ fn layout_of<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> &'tcx ty::layout::L
200200
ControlFlow::Continue(())
201201
}
202202
},
203-
|| create_cycle_error(tcx, &cycle),
203+
|| create_cycle_error(tcx, &cycle, false),
204204
);
205205

206206
let guar = diag.emit();

compiler/rustc_query_impl/src/job.rs

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -454,15 +454,16 @@ pub fn print_query_stack<'tcx>(
454454
pub(crate) fn create_cycle_error<'tcx>(
455455
tcx: TyCtxt<'tcx>,
456456
Cycle { usage, frames }: &Cycle<'tcx>,
457+
nested: bool,
457458
) -> Diag<'tcx> {
458459
assert!(!frames.is_empty());
459460

460-
let span = frames[0].tagged_key.default_span(tcx, frames[1 % frames.len()].span);
461+
let span = frames[0].tagged_key.catch_default_span(tcx, frames[1 % frames.len()].span);
461462

462463
let mut cycle_stack = Vec::new();
463464

464465
use crate::error::StackCount;
465-
let stack_bottom = frames[0].tagged_key.description(tcx);
466+
let stack_bottom = frames[0].tagged_key.catch_description(tcx);
466467
let stack_count = if frames.len() == 1 {
467468
StackCount::Single { stack_bottom: stack_bottom.clone() }
468469
} else {
@@ -471,14 +472,14 @@ pub(crate) fn create_cycle_error<'tcx>(
471472

472473
for i in 1..frames.len() {
473474
let frame = &frames[i];
474-
let span = frame.tagged_key.default_span(tcx, frames[(i + 1) % frames.len()].span);
475+
let span = frame.tagged_key.catch_default_span(tcx, frames[(i + 1) % frames.len()].span);
475476
cycle_stack
476-
.push(crate::error::CycleStack { span, desc: frame.tagged_key.description(tcx) });
477+
.push(crate::error::CycleStack { span, desc: frame.tagged_key.catch_description(tcx) });
477478
}
478479

479480
let cycle_usage = usage.as_ref().map(|usage| crate::error::CycleUsage {
480-
span: usage.tagged_key.default_span(tcx, usage.span),
481-
usage: usage.tagged_key.description(tcx),
481+
span: usage.tagged_key.catch_default_span(tcx, usage.span),
482+
usage: usage.tagged_key.catch_description(tcx),
482483
});
483484

484485
let is_all_def_kind = |def_kind| {
@@ -495,23 +496,36 @@ pub(crate) fn create_cycle_error<'tcx>(
495496
})
496497
};
497498

498-
let alias = if is_all_def_kind(DefKind::TyAlias) {
499-
Some(crate::error::Alias::Ty)
500-
} else if is_all_def_kind(DefKind::TraitAlias) {
501-
Some(crate::error::Alias::Trait)
499+
let alias = if !nested {
500+
if is_all_def_kind(DefKind::TyAlias) {
501+
Some(crate::error::Alias::Ty)
502+
} else if is_all_def_kind(DefKind::TraitAlias) {
503+
Some(crate::error::Alias::Trait)
504+
} else {
505+
None
506+
}
502507
} else {
503508
None
504509
};
505510

506-
let cycle_diag = crate::error::Cycle {
507-
span,
508-
cycle_stack,
509-
stack_bottom,
510-
alias,
511-
cycle_usage,
512-
stack_count,
513-
note_span: (),
514-
};
515-
516-
tcx.sess.dcx().create_err(cycle_diag)
511+
if nested {
512+
tcx.sess.dcx().create_err(crate::error::NestedCycle {
513+
span,
514+
cycle_stack,
515+
stack_bottom: crate::error::NestedCycleBottom { stack_bottom },
516+
cycle_usage,
517+
stack_count,
518+
note_span: (),
519+
})
520+
} else {
521+
tcx.sess.dcx().create_err(crate::error::Cycle {
522+
span,
523+
cycle_stack,
524+
stack_bottom,
525+
alias,
526+
cycle_usage,
527+
stack_count,
528+
note_span: (),
529+
})
530+
}
517531
}

compiler/rustc_query_impl/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#![feature(try_blocks)]
99
// tidy-alphabetical-end
1010

11-
use rustc_data_structures::sync::AtomicU64;
11+
use rustc_data_structures::sync::{AtomicU64, Lock};
1212
use rustc_middle::dep_graph;
1313
use rustc_middle::queries::{ExternProviders, Providers};
1414
use rustc_middle::query::on_disk_cache::OnDiskCache;
@@ -58,6 +58,7 @@ pub fn query_system<'tcx>(
5858
local_providers,
5959
extern_providers,
6060
jobs: AtomicU64::new(1),
61+
cycle_handler_nesting: Lock::new(0),
6162
}
6263
}
6364

tests/ui/parallel-rustc/default-trait-shadow-cycle-issue-151358.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Test for #151358, assertion failed: !worker_thread.is_null()
2-
//~^ ERROR cycle detected when looking up span for `Default`
2+
//~^ ERROR internal compiler error: query cycle when printing cycle detected
3+
//~^^ ERROR cycle detected when getting the resolver for lowering
34
//
45
//@ compile-flags: -Z threads=2
56
//@ compare-output-by-lines
Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
1-
error[E0391]: cycle detected when looking up span for `Default`
1+
error: internal compiler error: query cycle when printing cycle detected
22
|
3-
= note: ...which immediately requires looking up span for `Default` again
4-
= note: cycle used when perform lints prior to AST lowering
3+
= note: ...when getting HIR ID of `Default`
4+
= note: ...which requires getting the crate HIR...
5+
= note: ...which requires perform lints prior to AST lowering...
6+
= note: ...which requires looking up span for `Default`...
7+
= note: ...which again requires getting HIR ID of `Default`, completing the cycle
8+
= note: cycle used when getting the resolver for lowering
59
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
610

7-
error: aborting due to 1 previous error
11+
error[E0391]: cycle detected when getting the resolver for lowering
12+
|
13+
= note: ...which requires getting HIR ID of `Default`...
14+
= note: ...which requires getting the crate HIR...
15+
= note: ...which requires perform lints prior to AST lowering...
16+
= note: ...which again requires getting the resolver for lowering, completing the cycle
17+
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
18+
19+
error: aborting due to 2 previous errors
820

921
For more information about this error, try `rustc --explain E0391`.

tests/ui/query-system/query-cycle-printing-issue-151358.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
//~ ERROR: cycle detected when looking up span for `Default`
1+
//~ ERROR: cycle when printing cycle detected
2+
//~^ ERROR: cycle detected
23
trait Default {}
34
use std::num::NonZero;
45
fn main() {

0 commit comments

Comments
 (0)