Skip to content

Commit c4f26b1

Browse files
committed
Auto merge of #88121 - camelid:better-recursive-alias-error, r=estebank
Improve errors for recursive type aliases Fixes #17539.
2 parents c2a4088 + d96234b commit c4f26b1

23 files changed

+227
-25
lines changed

compiler/rustc_middle/src/query/mod.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,18 @@ rustc_queries! {
120120

121121
/// Records the type of every item.
122122
query type_of(key: DefId) -> Ty<'tcx> {
123-
desc { |tcx| "computing type of `{}`", tcx.def_path_str(key) }
123+
desc { |tcx|
124+
"{action} `{path}`",
125+
action = {
126+
use rustc_hir::def::DefKind;
127+
match tcx.def_kind(key) {
128+
DefKind::TyAlias => "expanding type alias",
129+
DefKind::TraitAlias => "expanding trait alias",
130+
_ => "computing type of",
131+
}
132+
},
133+
path = tcx.def_path_str(key),
134+
}
124135
cache_on_disk_if { key.is_local() }
125136
}
126137

compiler/rustc_query_impl/src/keys.rs

+17
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ pub trait Key {
2020
/// In the event that a cycle occurs, if no explicit span has been
2121
/// given for a query with key `self`, what span should we use?
2222
fn default_span(&self, tcx: TyCtxt<'_>) -> Span;
23+
24+
/// If the key is a [`DefId`] or `DefId`--equivalent, return that `DefId`.
25+
/// Otherwise, return `None`.
26+
fn key_as_def_id(&self) -> Option<DefId> {
27+
None
28+
}
2329
}
2430

2531
impl Key for () {
@@ -95,6 +101,9 @@ impl Key for LocalDefId {
95101
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
96102
self.to_def_id().default_span(tcx)
97103
}
104+
fn key_as_def_id(&self) -> Option<DefId> {
105+
Some(self.to_def_id())
106+
}
98107
}
99108

100109
impl Key for DefId {
@@ -105,6 +114,10 @@ impl Key for DefId {
105114
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
106115
tcx.def_span(*self)
107116
}
117+
#[inline(always)]
118+
fn key_as_def_id(&self) -> Option<DefId> {
119+
Some(*self)
120+
}
108121
}
109122

110123
impl Key for ty::WithOptConstParam<LocalDefId> {
@@ -165,6 +178,10 @@ impl Key for (DefId, Option<Ident>) {
165178
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
166179
tcx.def_span(self.0)
167180
}
181+
#[inline(always)]
182+
fn key_as_def_id(&self) -> Option<DefId> {
183+
Some(self.0)
184+
}
168185
}
169186

170187
impl Key for (DefId, LocalDefId, Ident) {

compiler/rustc_query_impl/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ pub use on_disk_cache::OnDiskCache;
5151
mod profiling_support;
5252
pub use self::profiling_support::alloc_self_profile_query_strings;
5353

54+
mod util;
55+
5456
rustc_query_append! { [define_queries!][<'tcx>] }
5557

5658
impl<'tcx> Queries<'tcx> {

compiler/rustc_query_impl/src/plumbing.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,13 @@ macro_rules! define_queries {
337337
} else {
338338
Some(key.default_span(*tcx))
339339
};
340+
let def_id = key.key_as_def_id();
341+
let def_kind = def_id
342+
.and_then(|def_id| def_id.as_local())
343+
// Use `tcx.hir().opt_def_kind()` to reduce the chance of
344+
// accidentally triggering an infinite query loop.
345+
.and_then(|def_id| tcx.hir().opt_def_kind(def_id))
346+
.map(|def_kind| $crate::util::def_kind_to_simple_def_kind(def_kind));
340347
let hash = || {
341348
let mut hcx = tcx.create_stable_hashing_context();
342349
let mut hasher = StableHasher::new();
@@ -345,7 +352,7 @@ macro_rules! define_queries {
345352
hasher.finish::<u64>()
346353
};
347354

348-
QueryStackFrame::new(name, description, span, hash)
355+
QueryStackFrame::new(name, description, span, def_kind, hash)
349356
})*
350357
}
351358

compiler/rustc_query_impl/src/util.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use rustc_hir::def::DefKind;
2+
use rustc_query_system::query::SimpleDefKind;
3+
4+
/// Convert a [`DefKind`] to a [`SimpleDefKind`].
5+
///
6+
/// *See [`SimpleDefKind`]'s docs for more information.*
7+
pub(crate) fn def_kind_to_simple_def_kind(def_kind: DefKind) -> SimpleDefKind {
8+
match def_kind {
9+
DefKind::Struct => SimpleDefKind::Struct,
10+
DefKind::Enum => SimpleDefKind::Enum,
11+
DefKind::Union => SimpleDefKind::Union,
12+
DefKind::Trait => SimpleDefKind::Trait,
13+
DefKind::TyAlias => SimpleDefKind::TyAlias,
14+
DefKind::TraitAlias => SimpleDefKind::TraitAlias,
15+
16+
_ => SimpleDefKind::Other,
17+
}
18+
}

compiler/rustc_query_system/src/query/job.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::dep_graph::DepContext;
22
use crate::query::plumbing::CycleError;
3-
use crate::query::{QueryContext, QueryStackFrame};
3+
use crate::query::{QueryContext, QueryStackFrame, SimpleDefKind};
44

55
use rustc_data_structures::fx::FxHashMap;
66
use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, Handler, Level};
@@ -591,10 +591,33 @@ pub(crate) fn report_cycle<'a>(
591591
err.span_note(span, &format!("...which requires {}...", query.description));
592592
}
593593

594-
err.note(&format!(
595-
"...which again requires {}, completing the cycle",
596-
stack[0].query.description
597-
));
594+
if stack.len() == 1 {
595+
err.note(&format!("...which immediately requires {} again", stack[0].query.description));
596+
} else {
597+
err.note(&format!(
598+
"...which again requires {}, completing the cycle",
599+
stack[0].query.description
600+
));
601+
}
602+
603+
if stack.iter().all(|entry| {
604+
entry.query.def_kind.map_or(false, |def_kind| {
605+
matches!(def_kind, SimpleDefKind::TyAlias | SimpleDefKind::TraitAlias)
606+
})
607+
}) {
608+
if stack.iter().all(|entry| {
609+
entry
610+
.query
611+
.def_kind
612+
.map_or(false, |def_kind| matches!(def_kind, SimpleDefKind::TyAlias))
613+
}) {
614+
err.note("type aliases cannot be recursive");
615+
err.help("consider using a struct, enum, or union instead to break the cycle");
616+
err.help("see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information");
617+
} else {
618+
err.note("trait aliases cannot be recursive");
619+
}
620+
}
598621

599622
if let Some((span, query)) = usage {
600623
err.span_note(fix_span(span, &query), &format!("cycle used when {}", query.description));

compiler/rustc_query_system/src/query/mod.rs

+29
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,53 @@ pub struct QueryStackFrame {
2929
pub name: &'static str,
3030
pub description: String,
3131
span: Option<Span>,
32+
/// The `DefKind` this query frame is associated with, if applicable.
33+
///
34+
/// We can't use `rustc_hir::def::DefKind` because `rustc_hir` is not
35+
/// available in `rustc_query_system`. Instead, we have a simplified
36+
/// custom version of it, called [`SimpleDefKind`].
37+
def_kind: Option<SimpleDefKind>,
3238
/// This hash is used to deterministically pick
3339
/// a query to remove cycles in the parallel compiler.
3440
#[cfg(parallel_compiler)]
3541
hash: u64,
3642
}
3743

44+
/// A simplified version of `rustc_hir::def::DefKind`.
45+
///
46+
/// It was added to help improve cycle errors caused by recursive type aliases.
47+
/// As of August 2021, `rustc_query_system` cannot depend on `rustc_hir`
48+
/// because it would create a dependency cycle. So, instead, a simplified
49+
/// version of `DefKind` was added to `rustc_query_system`.
50+
///
51+
/// `DefKind`s are converted to `SimpleDefKind`s in `rustc_query_impl`.
52+
#[derive(Debug, Copy, Clone)]
53+
pub enum SimpleDefKind {
54+
Struct,
55+
Enum,
56+
Union,
57+
Trait,
58+
TyAlias,
59+
TraitAlias,
60+
61+
// FIXME: add more from `rustc_hir::def::DefKind` and then remove `Other`
62+
Other,
63+
}
64+
3865
impl QueryStackFrame {
3966
#[inline]
4067
pub fn new(
4168
name: &'static str,
4269
description: String,
4370
span: Option<Span>,
71+
def_kind: Option<SimpleDefKind>,
4472
_hash: impl FnOnce() -> u64,
4573
) -> Self {
4674
Self {
4775
name,
4876
description,
4977
span,
78+
def_kind,
5079
#[cfg(parallel_compiler)]
5180
hash: _hash(),
5281
}

src/test/ui/associated-type-bounds/ambiguous-associated-type2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0391]: cycle detected when computing the super traits of `Baz` with assoc
44
LL | trait Baz: Foo + Bar<Self::Item> {}
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7-
= note: ...which again requires computing the super traits of `Baz` with associated type name `Item`, completing the cycle
7+
= note: ...which immediately requires computing the super traits of `Baz` with associated type name `Item` again
88
note: cycle used when computing the super traits of `Baz`
99
--> $DIR/ambiguous-associated-type2.rs:7:1
1010
|

src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ error[E0391]: cycle detected when building specialization graph of trait `Trait`
1414
LL | trait Trait<T> { type Assoc; }
1515
| ^^^^^^^^^^^^^^
1616
|
17-
= note: ...which again requires building specialization graph of trait `Trait`, completing the cycle
17+
= note: ...which immediately requires building specialization graph of trait `Trait` again
1818
note: cycle used when coherence checking all impls of trait `Trait`
1919
--> $DIR/coherence-inherited-assoc-ty-cycle-err.rs:9:1
2020
|

src/test/ui/cycle-trait/cycle-trait-default-type-trait.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0391]: cycle detected when computing type of `Foo::X`
44
LL | trait Foo<X = Box<dyn Foo>> {
55
| ^^^
66
|
7-
= note: ...which again requires computing type of `Foo::X`, completing the cycle
7+
= note: ...which immediately requires computing type of `Foo::X` again
88
note: cycle used when collecting item types in top-level module
99
--> $DIR/cycle-trait-default-type-trait.rs:4:1
1010
|
@@ -17,7 +17,7 @@ error[E0391]: cycle detected when computing type of `Foo::X`
1717
LL | trait Foo<X = Box<dyn Foo>> {
1818
| ^^^
1919
|
20-
= note: ...which again requires computing type of `Foo::X`, completing the cycle
20+
= note: ...which immediately requires computing type of `Foo::X` again
2121
note: cycle used when collecting item types in top-level module
2222
--> $DIR/cycle-trait-default-type-trait.rs:4:1
2323
|

src/test/ui/infinite/infinite-struct.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ error[E0391]: cycle detected when computing drop-check constraints for `Take`
1818
LL | struct Take(Take);
1919
| ^^^^^^^^^^^^^^^^^^
2020
|
21-
= note: ...which again requires computing drop-check constraints for `Take`, completing the cycle
21+
= note: ...which immediately requires computing drop-check constraints for `Take` again
2222
= note: cycle used when computing dropck types for `Canonical { max_universe: U0, variables: [], value: ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: Take } }`
2323

2424
error: aborting due to 2 previous errors

src/test/ui/infinite/infinite-tag-type-recursion.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ error[E0391]: cycle detected when computing drop-check constraints for `MList`
1717
LL | enum MList { Cons(isize, MList), Nil }
1818
| ^^^^^^^^^^
1919
|
20-
= note: ...which again requires computing drop-check constraints for `MList`, completing the cycle
20+
= note: ...which immediately requires computing drop-check constraints for `MList` again
2121
= note: cycle used when computing dropck types for `Canonical { max_universe: U0, variables: [], value: ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: MList } }`
2222

2323
error: aborting due to 2 previous errors
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![feature(trait_alias)]
2+
3+
trait T1 = T2;
4+
//~^ ERROR cycle detected when computing the super predicates of `T1`
5+
6+
trait T2 = T3;
7+
8+
trait T3 = T1 + T3;
9+
10+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
error[E0391]: cycle detected when computing the super predicates of `T1`
2+
--> $DIR/infinite-trait-alias-recursion.rs:3:1
3+
|
4+
LL | trait T1 = T2;
5+
| ^^^^^^^^^^^^^^
6+
|
7+
note: ...which requires computing the super traits of `T1`...
8+
--> $DIR/infinite-trait-alias-recursion.rs:3:12
9+
|
10+
LL | trait T1 = T2;
11+
| ^^
12+
note: ...which requires computing the super predicates of `T2`...
13+
--> $DIR/infinite-trait-alias-recursion.rs:6:1
14+
|
15+
LL | trait T2 = T3;
16+
| ^^^^^^^^^^^^^^
17+
note: ...which requires computing the super traits of `T2`...
18+
--> $DIR/infinite-trait-alias-recursion.rs:6:12
19+
|
20+
LL | trait T2 = T3;
21+
| ^^
22+
note: ...which requires computing the super predicates of `T3`...
23+
--> $DIR/infinite-trait-alias-recursion.rs:8:1
24+
|
25+
LL | trait T3 = T1 + T3;
26+
| ^^^^^^^^^^^^^^^^^^^
27+
note: ...which requires computing the super traits of `T3`...
28+
--> $DIR/infinite-trait-alias-recursion.rs:8:12
29+
|
30+
LL | trait T3 = T1 + T3;
31+
| ^^
32+
= note: ...which again requires computing the super predicates of `T1`, completing the cycle
33+
= note: trait aliases cannot be recursive
34+
note: cycle used when collecting item types in top-level module
35+
--> $DIR/infinite-trait-alias-recursion.rs:3:1
36+
|
37+
LL | trait T1 = T2;
38+
| ^^^^^^^^^^^^^^
39+
40+
error: aborting due to previous error
41+
42+
For more information about this error, try `rustc --explain E0391`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type X1 = X2;
2+
//~^ ERROR cycle detected when expanding type alias `X1`
3+
type X2 = X3;
4+
type X3 = X1;
5+
6+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
error[E0391]: cycle detected when expanding type alias `X1`
2+
--> $DIR/infinite-type-alias-mutual-recursion.rs:1:11
3+
|
4+
LL | type X1 = X2;
5+
| ^^
6+
|
7+
note: ...which requires expanding type alias `X2`...
8+
--> $DIR/infinite-type-alias-mutual-recursion.rs:3:11
9+
|
10+
LL | type X2 = X3;
11+
| ^^
12+
note: ...which requires expanding type alias `X3`...
13+
--> $DIR/infinite-type-alias-mutual-recursion.rs:4:11
14+
|
15+
LL | type X3 = X1;
16+
| ^^
17+
= note: ...which again requires expanding type alias `X1`, completing the cycle
18+
= note: type aliases cannot be recursive
19+
= help: consider using a struct, enum, or union instead to break the cycle
20+
= help: see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information
21+
note: cycle used when collecting item types in top-level module
22+
--> $DIR/infinite-type-alias-mutual-recursion.rs:1:1
23+
|
24+
LL | / type X1 = X2;
25+
LL | |
26+
LL | | type X2 = X3;
27+
LL | | type X3 = X1;
28+
LL | |
29+
LL | | fn main() {}
30+
| |____________^
31+
32+
error: aborting due to previous error
33+
34+
For more information about this error, try `rustc --explain E0391`.

src/test/ui/infinite/infinite-vec-type-recursion.stderr

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
error[E0391]: cycle detected when computing type of `X`
1+
error[E0391]: cycle detected when expanding type alias `X`
22
--> $DIR/infinite-vec-type-recursion.rs:1:14
33
|
44
LL | type X = Vec<X>;
55
| ^
66
|
7-
= note: ...which again requires computing type of `X`, completing the cycle
7+
= note: ...which immediately requires expanding type alias `X` again
8+
= note: type aliases cannot be recursive
9+
= help: consider using a struct, enum, or union instead to break the cycle
10+
= help: see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information
811
note: cycle used when collecting item types in top-level module
912
--> $DIR/infinite-vec-type-recursion.rs:1:1
1013
|

src/test/ui/issues/issue-20772.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | |
66
LL | | {}
77
| |__^
88
|
9-
= note: ...which again requires computing the super traits of `T` with associated type name `Item`, completing the cycle
9+
= note: ...which immediately requires computing the super traits of `T` with associated type name `Item` again
1010
note: cycle used when computing the super traits of `T`
1111
--> $DIR/issue-20772.rs:1:1
1212
|

src/test/ui/issues/issue-20825.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0391]: cycle detected when computing the super traits of `Processor` with
44
LL | pub trait Processor: Subscriber<Input = Self::Input> {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7-
= note: ...which again requires computing the super traits of `Processor` with associated type name `Input`, completing the cycle
7+
= note: ...which immediately requires computing the super traits of `Processor` with associated type name `Input` again
88
note: cycle used when computing the super traits of `Processor`
99
--> $DIR/issue-20825.rs:5:1
1010
|

0 commit comments

Comments
 (0)