Skip to content

Commit e39dfc5

Browse files
Rollup merge of #154389 - Zoxc:nested-cycles, r=petrochenkov
Add more robust handling of nested query cycles This adds more robust handling of query cycle that occur while we are already printing a query cycle error. Such nested query cycle are compiler bugs and this adds special handling so that both the nested query cycle and the outer query cycle are printed. Doubly nested query cycle errors are ignored to prevent infinite recursion.
2 parents d2fa85c + 6442b48 commit e39dfc5

13 files changed

Lines changed: 155 additions & 42 deletions

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
@@ -166,6 +166,8 @@ pub struct QuerySystem<'tcx> {
166166
pub extern_providers: ExternProviders,
167167

168168
pub jobs: AtomicU64,
169+
170+
pub cycle_handler_nesting: Lock<u8>,
169171
}
170172

171173
#[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
@@ -210,7 +210,7 @@ pub(crate) fn layout_of<'tcx>(
210210
ControlFlow::Continue(())
211211
}
212212
},
213-
|| create_cycle_error(tcx, &cycle),
213+
|| create_cycle_error(tcx, &cycle, false),
214214
);
215215

216216
diag.emit().raise_fatal()

compiler/rustc_query_impl/src/job.rs

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -413,15 +413,16 @@ pub fn print_query_stack<'tcx>(
413413
pub(crate) fn create_cycle_error<'tcx>(
414414
tcx: TyCtxt<'tcx>,
415415
Cycle { usage, frames }: &Cycle<'tcx>,
416+
nested: bool,
416417
) -> Diag<'tcx> {
417418
assert!(!frames.is_empty());
418419

419-
let span = frames[0].tagged_key.default_span(tcx, frames[1 % frames.len()].span);
420+
let span = frames[0].tagged_key.catch_default_span(tcx, frames[1 % frames.len()].span);
420421

421422
let mut cycle_stack = Vec::new();
422423

423424
use crate::error::StackCount;
424-
let stack_bottom = frames[0].tagged_key.description(tcx);
425+
let stack_bottom = frames[0].tagged_key.catch_description(tcx);
425426
let stack_count = if frames.len() == 1 {
426427
StackCount::Single { stack_bottom: stack_bottom.clone() }
427428
} else {
@@ -430,14 +431,14 @@ pub(crate) fn create_cycle_error<'tcx>(
430431

431432
for i in 1..frames.len() {
432433
let frame = &frames[i];
433-
let span = frame.tagged_key.default_span(tcx, frames[(i + 1) % frames.len()].span);
434+
let span = frame.tagged_key.catch_default_span(tcx, frames[(i + 1) % frames.len()].span);
434435
cycle_stack
435-
.push(crate::error::CycleStack { span, desc: frame.tagged_key.description(tcx) });
436+
.push(crate::error::CycleStack { span, desc: frame.tagged_key.catch_description(tcx) });
436437
}
437438

438439
let cycle_usage = usage.as_ref().map(|usage| crate::error::CycleUsage {
439-
span: usage.tagged_key.default_span(tcx, usage.span),
440-
usage: usage.tagged_key.description(tcx),
440+
span: usage.tagged_key.catch_default_span(tcx, usage.span),
441+
usage: usage.tagged_key.catch_description(tcx),
441442
});
442443

443444
let is_all_def_kind = |def_kind| {
@@ -454,23 +455,36 @@ pub(crate) fn create_cycle_error<'tcx>(
454455
})
455456
};
456457

457-
let alias = if is_all_def_kind(DefKind::TyAlias) {
458-
Some(crate::error::Alias::Ty)
459-
} else if is_all_def_kind(DefKind::TraitAlias) {
460-
Some(crate::error::Alias::Trait)
458+
let alias = if !nested {
459+
if is_all_def_kind(DefKind::TyAlias) {
460+
Some(crate::error::Alias::Ty)
461+
} else if is_all_def_kind(DefKind::TraitAlias) {
462+
Some(crate::error::Alias::Trait)
463+
} else {
464+
None
465+
}
461466
} else {
462467
None
463468
};
464469

465-
let cycle_diag = crate::error::Cycle {
466-
span,
467-
cycle_stack,
468-
stack_bottom,
469-
alias,
470-
cycle_usage,
471-
stack_count,
472-
note_span: (),
473-
};
474-
475-
tcx.sess.dcx().create_err(cycle_diag)
470+
if nested {
471+
tcx.sess.dcx().create_err(crate::error::NestedCycle {
472+
span,
473+
cycle_stack,
474+
stack_bottom: crate::error::NestedCycleBottom { stack_bottom },
475+
cycle_usage,
476+
stack_count,
477+
note_span: (),
478+
})
479+
} else {
480+
tcx.sess.dcx().create_err(crate::error::Cycle {
481+
span,
482+
cycle_stack,
483+
stack_bottom,
484+
alias,
485+
cycle_usage,
486+
stack_count,
487+
note_span: (),
488+
})
489+
}
476490
}

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;
@@ -56,6 +56,7 @@ pub fn query_system<'tcx>(
5656
local_providers,
5757
extern_providers,
5858
jobs: AtomicU64::new(1),
59+
cycle_handler_nesting: Lock::new(0),
5960
}
6061
}
6162

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)