Note that type aliases cannot be recursive

This commit is contained in:
Noah Lev 2021-08-19 20:30:33 -07:00
parent 2f48bfa88c
commit cd0fc444fb
7 changed files with 84 additions and 2 deletions

View file

@ -20,6 +20,12 @@ pub trait Key {
/// In the event that a cycle occurs, if no explicit span has been
/// given for a query with key `self`, what span should we use?
fn default_span(&self, tcx: TyCtxt<'_>) -> Span;
/// If the key is a [`DefId`] or `DefId`--equivalent, return that `DefId`.
/// Otherwise, return `None`.
fn key_as_def_id(&self) -> Option<DefId> {
None
}
}
impl Key for () {
@ -95,6 +101,9 @@ impl Key for LocalDefId {
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
self.to_def_id().default_span(tcx)
}
fn key_as_def_id(&self) -> Option<DefId> {
Some(self.to_def_id())
}
}
impl Key for DefId {
@ -105,6 +114,10 @@ impl Key for DefId {
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
tcx.def_span(*self)
}
#[inline(always)]
fn key_as_def_id(&self) -> Option<DefId> {
Some(*self)
}
}
impl Key for ty::WithOptConstParam<LocalDefId> {

View file

@ -51,6 +51,8 @@ pub use on_disk_cache::OnDiskCache;
mod profiling_support;
pub use self::profiling_support::alloc_self_profile_query_strings;
mod util;
rustc_query_append! { [define_queries!][<'tcx>] }
impl<'tcx> Queries<'tcx> {

View file

@ -337,6 +337,11 @@ macro_rules! define_queries {
} else {
Some(key.default_span(*tcx))
};
let def_id = key.key_as_def_id();
let def_kind = def_id.map(|def_id| {
let def_kind = tcx.def_kind(def_id);
$crate::util::def_kind_to_simple_def_kind(def_kind)
});
let hash = || {
let mut hcx = tcx.create_stable_hashing_context();
let mut hasher = StableHasher::new();
@ -345,7 +350,7 @@ macro_rules! define_queries {
hasher.finish::<u64>()
};
QueryStackFrame::new(name, description, span, hash)
QueryStackFrame::new(name, description, span, def_kind, hash)
})*
}

View file

@ -0,0 +1,18 @@
use rustc_hir::def::DefKind;
use rustc_query_system::query::SimpleDefKind;
/// Convert a [`DefKind`] to a [`SimpleDefKind`].
///
/// *See [`SimpleDefKind`]'s docs for more information.*
pub(crate) fn def_kind_to_simple_def_kind(def_kind: DefKind) -> SimpleDefKind {
match def_kind {
DefKind::Struct => SimpleDefKind::Struct,
DefKind::Enum => SimpleDefKind::Enum,
DefKind::Union => SimpleDefKind::Union,
DefKind::Trait => SimpleDefKind::Trait,
DefKind::TyAlias => SimpleDefKind::TyAlias,
DefKind::TraitAlias => SimpleDefKind::TraitAlias,
_ => SimpleDefKind::Other,
}
}

View file

@ -1,6 +1,6 @@
use crate::dep_graph::DepContext;
use crate::query::plumbing::CycleError;
use crate::query::{QueryContext, QueryStackFrame};
use crate::query::{QueryContext, QueryStackFrame, SimpleDefKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, Handler, Level};
@ -600,6 +600,18 @@ pub(crate) fn report_cycle<'a>(
));
}
if !stack.is_empty()
&& stack.iter().all(|entry| {
entry.query.def_kind.map_or(false, |def_kind| {
matches!(def_kind, SimpleDefKind::TyAlias | SimpleDefKind::TraitAlias)
})
})
{
err.note("type aliases cannot be recursive");
err.help("consider using a struct, enum, or union instead to break the cycle");
err.help("see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information");
}
if let Some((span, query)) = usage {
err.span_note(fix_span(span, &query), &format!("cycle used when {}", query.description));
}

View file

@ -29,24 +29,53 @@ pub struct QueryStackFrame {
pub name: &'static str,
pub description: String,
span: Option<Span>,
/// The `DefKind` this query frame is associated with, if applicable.
///
/// We can't use `rustc_hir::def::DefKind` because `rustc_hir` is not
/// available in `rustc_query_system`. Instead, we have a simplified
/// custom version of it, called [`SimpleDefKind`].
def_kind: Option<SimpleDefKind>,
/// This hash is used to deterministically pick
/// a query to remove cycles in the parallel compiler.
#[cfg(parallel_compiler)]
hash: u64,
}
/// A simplified version of `rustc_hir::def::DefKind`.
///
/// It was added to help improve cycle errors caused by recursive type aliases.
/// As of August 2021, `rustc_query_system` cannot depend on `rustc_hir`
/// because it would create a dependency cycle. So, instead, a simplified
/// version of `DefKind` was added to `rustc_query_system`.
///
/// `DefKind`s are converted to `SimpleDefKind`s in `rustc_query_impl`.
#[derive(Debug, Copy, Clone)]
pub enum SimpleDefKind {
Struct,
Enum,
Union,
Trait,
TyAlias,
TraitAlias,
// FIXME: add more from `rustc_hir::def::DefKind` and then remove `Other`
Other,
}
impl QueryStackFrame {
#[inline]
pub fn new(
name: &'static str,
description: String,
span: Option<Span>,
def_kind: Option<SimpleDefKind>,
_hash: impl FnOnce() -> u64,
) -> Self {
Self {
name,
description,
span,
def_kind,
#[cfg(parallel_compiler)]
hash: _hash(),
}

View file

@ -5,6 +5,9 @@ LL | type X = Vec<X>;
| ^
|
= note: ...which immediately requires expanding type alias `X` again
= note: type aliases cannot be recursive
= help: consider using a struct, enum, or union instead to break the cycle
= help: see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information
note: cycle used when collecting item types in top-level module
--> $DIR/infinite-vec-type-recursion.rs:1:1
|