From 94a0819fe3a0d679c3042a77bfe6a2afc505daea Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:11:28 +0200 Subject: Adding upstream version 1.66.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_query_system/Cargo.toml | 9 ++- .../rustc_query_system/src/dep_graph/dep_node.rs | 73 +++++++++++++++++ compiler/rustc_query_system/src/dep_graph/mod.rs | 45 +++++++++-- compiler/rustc_query_system/src/error.rs | 93 ++++++++++++++++++++++ compiler/rustc_query_system/src/ich/hcx.rs | 28 ++----- compiler/rustc_query_system/src/ich/impls_hir.rs | 24 +----- .../rustc_query_system/src/ich/impls_syntax.rs | 8 +- compiler/rustc_query_system/src/lib.rs | 10 ++- compiler/rustc_query_system/src/query/config.rs | 22 ++--- compiler/rustc_query_system/src/query/job.rs | 90 ++++++++++++--------- compiler/rustc_query_system/src/query/mod.rs | 15 +++- compiler/rustc_query_system/src/query/plumbing.rs | 93 +++++++++++++++------- compiler/rustc_query_system/src/values.rs | 15 ++++ 13 files changed, 385 insertions(+), 140 deletions(-) create mode 100644 compiler/rustc_query_system/src/error.rs create mode 100644 compiler/rustc_query_system/src/values.rs (limited to 'compiler/rustc_query_system') diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index b7787aeb8..faddad741 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -4,12 +4,10 @@ version = "0.0.0" edition = "2021" [lib] -doctest = false [dependencies] +parking_lot = "0.11" rustc_arena = { path = "../rustc_arena" } -tracing = "0.1" -rustc-rayon-core = { version = "0.4.0", optional = true } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -17,12 +15,15 @@ rustc_feature = { path = "../rustc_feature" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } +rustc-rayon-core = { version = "0.4.0", optional = true } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -parking_lot = "0.11" +rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +thin-vec = "0.2.8" +tracing = "0.1" [features] rustc_use_parallel_compiler = ["rustc-rayon-core"] diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 162c274d8..5c6ce0556 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -47,6 +47,7 @@ use crate::ich::StableHashingContext; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir::definitions::DefPathHash; use std::fmt; use std::hash::Hash; @@ -88,6 +89,17 @@ impl DepNode { dep_node } + + /// Construct a DepNode from the given DepKind and DefPathHash. This + /// method will assert that the given DepKind actually requires a + /// single DefId/DefPathHash parameter. + pub fn from_def_path_hash(tcx: Ctxt, def_path_hash: DefPathHash, kind: K) -> Self + where + Ctxt: super::DepContext, + { + debug_assert!(tcx.fingerprint_style(kind) == FingerprintStyle::DefPathHash); + DepNode { kind, hash: def_path_hash.0.into() } + } } impl fmt::Debug for DepNode { @@ -149,6 +161,67 @@ where } } +/// This struct stores metadata about each DepKind. +/// +/// Information is retrieved by indexing the `DEP_KINDS` array using the integer value +/// of the `DepKind`. Overall, this allows to implement `DepContext` using this manual +/// jump table instead of large matches. +pub struct DepKindStruct { + /// Anonymous queries cannot be replayed from one compiler invocation to the next. + /// When their result is needed, it is recomputed. They are useful for fine-grained + /// dependency tracking, and caching within one compiler invocation. + pub is_anon: bool, + + /// Eval-always queries do not track their dependencies, and are always recomputed, even if + /// their inputs have not changed since the last compiler invocation. The result is still + /// cached within one compiler invocation. + pub is_eval_always: bool, + + /// Whether the query key can be recovered from the hashed fingerprint. + /// See [DepNodeParams] trait for the behaviour of each key type. + pub fingerprint_style: FingerprintStyle, + + /// The red/green evaluation system will try to mark a specific DepNode in the + /// dependency graph as green by recursively trying to mark the dependencies of + /// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` + /// where we don't know if it is red or green and we therefore actually have + /// to recompute its value in order to find out. Since the only piece of + /// information that we have at that point is the `DepNode` we are trying to + /// re-evaluate, we need some way to re-run a query from just that. This is what + /// `force_from_dep_node()` implements. + /// + /// In the general case, a `DepNode` consists of a `DepKind` and an opaque + /// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint + /// is usually constructed by computing a stable hash of the query-key that the + /// `DepNode` corresponds to. Consequently, it is not in general possible to go + /// back from hash to query-key (since hash functions are not reversible). For + /// this reason `force_from_dep_node()` is expected to fail from time to time + /// because we just cannot find out, from the `DepNode` alone, what the + /// corresponding query-key is and therefore cannot re-run the query. + /// + /// The system deals with this case letting `try_mark_green` fail which forces + /// the root query to be re-evaluated. + /// + /// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. + /// Fortunately, we can use some contextual information that will allow us to + /// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we + /// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a + /// valid `DefPathHash`. Since we also always build a huge table that maps every + /// `DefPathHash` in the current codebase to the corresponding `DefId`, we have + /// everything we need to re-run the query. + /// + /// Take the `mir_promoted` query as an example. Like many other queries, it + /// just has a single parameter: the `DefId` of the item it will compute the + /// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` + /// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` + /// is actually a `DefPathHash`, and can therefore just look up the corresponding + /// `DefId` in `tcx.def_path_hash_to_def_id`. + pub force_from_dep_node: Option) -> bool>, + + /// Invoke a query to put the on-disk cached value in memory. + pub try_load_from_on_disk_cache: Option)>, +} + /// A "work product" corresponds to a `.o` (or other) file that we /// save in between runs. These IDs do not have a `DefId` but rather /// some independent path or string that persists between runs without diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 342d95ca4..da2075fd5 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -4,7 +4,7 @@ mod graph; mod query; mod serialized; -pub use dep_node::{DepNode, DepNodeParams, WorkProductId}; +pub use dep_node::{DepKindStruct, DepNode, DepNodeParams, WorkProductId}; pub use graph::{ hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, TaskDepsRef, WorkProduct, }; @@ -34,16 +34,43 @@ pub trait DepContext: Copy { /// Access the compiler session. fn sess(&self) -> &Session; - /// Return whether this kind always require evaluation. - fn is_eval_always(&self, kind: Self::DepKind) -> bool; + fn dep_kind_info(&self, dep_node: Self::DepKind) -> &DepKindStruct; - fn fingerprint_style(&self, kind: Self::DepKind) -> FingerprintStyle; + #[inline(always)] + fn fingerprint_style(&self, kind: Self::DepKind) -> FingerprintStyle { + let data = self.dep_kind_info(kind); + if data.is_anon { + return FingerprintStyle::Opaque; + } + data.fingerprint_style + } + + #[inline(always)] + /// Return whether this kind always require evaluation. + fn is_eval_always(&self, kind: Self::DepKind) -> bool { + self.dep_kind_info(kind).is_eval_always + } /// Try to force a dep node to execute and see if it's green. - fn try_force_from_dep_node(&self, dep_node: DepNode) -> bool; + fn try_force_from_dep_node(self, dep_node: DepNode) -> bool { + debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); + + let cb = self.dep_kind_info(dep_node.kind); + if let Some(f) = cb.force_from_dep_node { + f(self, dep_node); + true + } else { + false + } + } /// Load data from the on-disk cache. - fn try_load_from_on_disk_cache(&self, dep_node: DepNode); + fn try_load_from_on_disk_cache(self, dep_node: DepNode) { + let cb = self.dep_kind_info(dep_node.kind); + if let Some(f) = cb.try_load_from_on_disk_cache { + f(self, dep_node) + } + } } pub trait HasDepContext: Copy { @@ -67,6 +94,8 @@ impl HasDepContext for T { pub enum FingerprintStyle { /// The fingerprint is actually a DefPathHash. DefPathHash, + /// The fingerprint is actually a HirId. + HirId, /// Query key was `()` or equivalent, so fingerprint is just zero. Unit, /// Some opaque hash. @@ -77,7 +106,9 @@ impl FingerprintStyle { #[inline] pub fn reconstructible(self) -> bool { match self { - FingerprintStyle::DefPathHash | FingerprintStyle::Unit => true, + FingerprintStyle::DefPathHash | FingerprintStyle::Unit | FingerprintStyle::HirId => { + true + } FingerprintStyle::Opaque => false, } } diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs new file mode 100644 index 000000000..7a20eaceb --- /dev/null +++ b/compiler/rustc_query_system/src/error.rs @@ -0,0 +1,93 @@ +use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_session::Limit; +use rustc_span::{Span, Symbol}; + +#[derive(Subdiagnostic)] +#[note(query_system_cycle_stack_middle)] +pub struct CycleStack { + #[primary_span] + pub span: Span, + pub desc: String, +} + +#[derive(Copy, Clone)] +pub enum HandleCycleError { + Error, + Fatal, + DelayBug, +} + +#[derive(Subdiagnostic)] +pub enum StackCount { + #[note(query_system_cycle_stack_single)] + Single, + #[note(query_system_cycle_stack_multiple)] + Multiple, +} + +#[derive(Subdiagnostic)] +pub enum Alias { + #[note(query_system_cycle_recursive_ty_alias)] + #[help(query_system_cycle_recursive_ty_alias_help1)] + #[help(query_system_cycle_recursive_ty_alias_help2)] + Ty, + #[note(query_system_cycle_recursive_trait_alias)] + Trait, +} + +#[derive(Subdiagnostic)] +#[note(query_system_cycle_usage)] +pub struct CycleUsage { + #[primary_span] + pub span: Span, + pub usage: String, +} + +#[derive(Diagnostic)] +#[diag(query_system_cycle, code = "E0391")] +pub struct Cycle { + #[primary_span] + pub span: Span, + pub stack_bottom: String, + #[subdiagnostic(eager)] + pub cycle_stack: Vec, + #[subdiagnostic] + pub stack_count: StackCount, + #[subdiagnostic] + pub alias: Option, + #[subdiagnostic] + pub cycle_usage: Option, +} + +#[derive(Diagnostic)] +#[diag(query_system_reentrant)] +pub struct Reentrant; + +#[derive(Diagnostic)] +#[diag(query_system_increment_compilation)] +#[help] +#[note(query_system_increment_compilation_note1)] +#[note(query_system_increment_compilation_note2)] +pub struct IncrementCompilation { + pub run_cmd: String, + pub dep_node: String, +} + +#[derive(Diagnostic)] +#[help] +#[diag(query_system_query_overflow)] +pub struct QueryOverflow { + #[primary_span] + pub span: Option, + #[subdiagnostic] + pub layout_of_depth: Option, + pub suggested_limit: Limit, + pub crate_name: Symbol, +} + +#[derive(Subdiagnostic)] +#[note(query_system_layout_of_depth)] +pub struct LayoutOfDepth { + pub desc: String, + pub depth: usize, +} diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index 217fac341..148eabb38 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -12,7 +12,7 @@ use rustc_session::cstore::CrateStore; use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, CachingSourceMapView, SourceFile, Span, SpanData}; +use rustc_span::{BytePos, CachingSourceMapView, SourceFile, Span, SpanData, DUMMY_SP}; /// This is the context state available during incr. comp. hashing. It contains /// enough information to transform `DefId`s and `HirId`s into stable `DefPath`s (i.e., @@ -40,9 +40,9 @@ pub struct StableHashingContext<'a> { #[derive(Clone, Copy)] pub(super) enum BodyResolver<'tcx> { Forbidden, + Ignore, Traverse { - hash_bodies: bool, - owner: LocalDefId, + owner: hir::OwnerId, bodies: &'tcx SortedMap>, }, } @@ -98,32 +98,20 @@ impl<'a> StableHashingContext<'a> { Self::new_with_or_without_spans(sess, definitions, cstore, source_span, always_ignore_spans) } - /// Allow hashing #[inline] - pub fn while_hashing_hir_bodies(&mut self, hb: bool, f: impl FnOnce(&mut Self)) { - let prev = match &mut self.body_resolver { - BodyResolver::Forbidden => panic!("Hashing HIR bodies is forbidden."), - BodyResolver::Traverse { ref mut hash_bodies, .. } => { - std::mem::replace(hash_bodies, hb) - } - }; - f(self); - match &mut self.body_resolver { - BodyResolver::Forbidden => unreachable!(), - BodyResolver::Traverse { ref mut hash_bodies, .. } => *hash_bodies = prev, - } + pub fn without_hir_bodies(&mut self, f: impl FnOnce(&mut StableHashingContext<'_>)) { + f(&mut StableHashingContext { body_resolver: BodyResolver::Ignore, ..self.clone() }); } #[inline] pub fn with_hir_bodies( &mut self, - hash_bodies: bool, - owner: LocalDefId, + owner: hir::OwnerId, bodies: &SortedMap>, f: impl FnOnce(&mut StableHashingContext<'_>), ) { f(&mut StableHashingContext { - body_resolver: BodyResolver::Traverse { hash_bodies, owner, bodies }, + body_resolver: BodyResolver::Traverse { owner, bodies }, ..self.clone() }); } @@ -197,7 +185,7 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { #[inline] fn def_span(&self, def_id: LocalDefId) -> Span { - self.source_span[def_id] + *self.source_span.get(def_id).unwrap_or(&DUMMY_SP) } #[inline] diff --git a/compiler/rustc_query_system/src/ich/impls_hir.rs b/compiler/rustc_query_system/src/ich/impls_hir.rs index 3390ed9eb..aa008d404 100644 --- a/compiler/rustc_query_system/src/ich/impls_hir.rs +++ b/compiler/rustc_query_system/src/ich/impls_hir.rs @@ -12,31 +12,11 @@ impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { let hcx = self; match hcx.body_resolver { BodyResolver::Forbidden => panic!("Hashing HIR bodies is forbidden."), - BodyResolver::Traverse { hash_bodies: false, .. } => {} - BodyResolver::Traverse { hash_bodies: true, owner, bodies } => { + BodyResolver::Ignore => {} + BodyResolver::Traverse { owner, bodies } => { assert_eq!(id.hir_id.owner, owner); bodies[&id.hir_id.local_id].hash_stable(hcx, hasher); } } } - - fn hash_hir_expr(&mut self, expr: &hir::Expr<'_>, hasher: &mut StableHasher) { - self.while_hashing_hir_bodies(true, |hcx| { - let hir::Expr { hir_id, ref span, ref kind } = *expr; - - hir_id.hash_stable(hcx, hasher); - span.hash_stable(hcx, hasher); - kind.hash_stable(hcx, hasher); - }) - } - - fn hash_hir_ty(&mut self, ty: &hir::Ty<'_>, hasher: &mut StableHasher) { - self.while_hashing_hir_bodies(true, |hcx| { - let hir::Ty { hir_id, ref kind, ref span } = *ty; - - hir_id.hash_stable(hcx, hasher); - kind.hash_stable(hcx, hasher); - span.hash_stable(hcx, hasher); - }) - } } diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 1fa085926..0bc811eb0 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -42,12 +42,12 @@ impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> { debug_assert!(!attr.is_doc_comment()); let ast::Attribute { kind, id: _, style, span } = attr; - if let ast::AttrKind::Normal(item, tokens) = kind { - item.hash_stable(self, hasher); + if let ast::AttrKind::Normal(normal) = kind { + normal.item.hash_stable(self, hasher); style.hash_stable(self, hasher); span.hash_stable(self, hasher); assert_matches!( - tokens.as_ref(), + normal.tokens.as_ref(), None, "Tokens should have been removed during lowering!" ); @@ -148,3 +148,5 @@ impl<'tcx> HashStable> for rustc_feature::Features { }); } } + +impl<'ctx> rustc_type_ir::HashStableContext for StableHashingContext<'ctx> {} diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 68284dcaa..f47760e9a 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -1,10 +1,11 @@ #![feature(assert_matches)] #![feature(core_intrinsics)] #![feature(hash_raw_entry)] -#![feature(let_else)] #![feature(min_specialization)] #![feature(extern_types)] #![allow(rustc::potential_query_instability)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate tracing; @@ -15,5 +16,12 @@ extern crate rustc_macros; pub mod cache; pub mod dep_graph; +mod error; pub mod ich; pub mod query; +mod values; + +pub use error::HandleCycleError; +pub use error::LayoutOfDepth; +pub use error::QueryOverflow; +pub use values::Value; diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index 964914a13..0a1cffa3b 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs @@ -2,12 +2,12 @@ use crate::dep_graph::DepNode; use crate::dep_graph::SerializedDepNodeIndex; +use crate::error::HandleCycleError; use crate::ich::StableHashingContext; use crate::query::caches::QueryCache; use crate::query::{QueryContext, QueryState}; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; use std::fmt::Debug; use std::hash::Hash; @@ -19,15 +19,17 @@ pub trait QueryConfig { type Stored: Clone; } +#[derive(Copy, Clone)] pub struct QueryVTable { pub anon: bool, pub dep_kind: CTX::DepKind, pub eval_always: bool, - pub cache_on_disk: bool, + pub depth_limit: bool, pub compute: fn(CTX::DepContext, K) -> V, pub hash_result: Option, &V) -> Fingerprint>, - pub handle_cycle_error: fn(CTX, DiagnosticBuilder<'_, ErrorGuaranteed>) -> V, + pub handle_cycle_error: HandleCycleError, + // NOTE: this is also `None` if `cache_on_disk()` returns false, not just if it's unsupported by the query pub try_load_from_disk: Option Option>, } @@ -42,22 +44,11 @@ impl QueryVTable { pub(crate) fn compute(&self, tcx: CTX::DepContext, key: K) -> V { (self.compute)(tcx, key) } - - pub(crate) fn try_load_from_disk(&self, tcx: CTX, index: SerializedDepNodeIndex) -> Option { - self.try_load_from_disk - .expect("QueryDescription::load_from_disk() called for an unsupported query.")( - tcx, index, - ) - } } pub trait QueryDescription: QueryConfig { - const TRY_LOAD_FROM_DISK: Option Option>; - type Cache: QueryCache; - fn describe(tcx: CTX, key: Self::Key) -> String; - // Don't use this method to access query results, instead use the methods on TyCtxt fn query_state<'a>(tcx: CTX) -> &'a QueryState where @@ -72,4 +63,7 @@ pub trait QueryDescription: QueryConfig { fn make_vtable(tcx: CTX, key: &Self::Key) -> QueryVTable; fn cache_on_disk(tcx: CTX::DepContext, key: &Self::Key) -> bool; + + // Don't use this method to compute query results, instead use the methods on TyCtxt + fn execute_query(tcx: CTX::DepContext, k: Self::Key) -> Self::Stored; } diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 6d2aff381..ed65393f5 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -1,11 +1,12 @@ +use crate::error::CycleStack; use crate::query::plumbing::CycleError; use crate::query::{QueryContext, QueryStackFrame}; -use rustc_hir::def::DefKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ - struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level, + Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, Level, }; +use rustc_hir::def::DefKind; use rustc_session::Session; use rustc_span::Span; @@ -60,6 +61,7 @@ impl QueryJobId { } } +#[derive(Clone)] pub struct QueryJobInfo { pub query: QueryStackFrame, pub job: QueryJob, @@ -117,10 +119,10 @@ impl QueryJob { } } -#[cfg(not(parallel_compiler))] impl QueryJobId { #[cold] #[inline(never)] + #[cfg(not(parallel_compiler))] pub(super) fn find_cycle_in_stack( &self, query_map: QueryMap, @@ -157,6 +159,24 @@ impl QueryJobId { panic!("did not find a cycle") } + + #[cold] + #[inline(never)] + pub fn try_find_layout_root(&self, query_map: QueryMap) -> Option<(QueryJobInfo, usize)> { + let mut last_layout = None; + let mut current_id = Some(*self); + let mut depth = 0; + + while let Some(id) = current_id { + let info = query_map.get(&id).unwrap(); + if info.query.name == "layout_of" { + depth += 1; + last_layout = Some((info.clone(), depth)); + } + current_id = info.job.parent; + } + last_layout + } } #[cfg(parallel_compiler)] @@ -531,51 +551,49 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) { #[cold] pub(crate) fn report_cycle<'a>( sess: &'a Session, - CycleError { usage, cycle: stack }: CycleError, + CycleError { usage, cycle: stack }: &CycleError, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { assert!(!stack.is_empty()); let span = stack[0].query.default_span(stack[1 % stack.len()].span); - let mut err = - struct_span_err!(sess, span, E0391, "cycle detected when {}", stack[0].query.description); + + let mut cycle_stack = Vec::new(); + + use crate::error::StackCount; + let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; for i in 1..stack.len() { let query = &stack[i].query; let span = query.default_span(stack[(i + 1) % stack.len()].span); - err.span_note(span, &format!("...which requires {}...", query.description)); + cycle_stack.push(CycleStack { span, desc: query.description.to_owned() }); } - if stack.len() == 1 { - err.note(&format!("...which immediately requires {} again", stack[0].query.description)); - } else { - err.note(&format!( - "...which again requires {}, completing the cycle", - stack[0].query.description - )); + let mut cycle_usage = None; + if let Some((span, ref query)) = *usage { + cycle_usage = Some(crate::error::CycleUsage { + span: query.default_span(span), + usage: query.description.to_string(), + }); } - if stack.iter().all(|entry| { - entry - .query - .def_kind - .map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias | DefKind::TraitAlias)) - }) { - if stack.iter().all(|entry| { - entry.query.def_kind.map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias)) - }) { - err.note("type aliases cannot be recursive"); - err.help("consider using a struct, enum, or union instead to break the cycle"); - err.help("see for more information"); - } else { - err.note("trait aliases cannot be recursive"); - } - } - - if let Some((span, query)) = usage { - err.span_note(query.default_span(span), &format!("cycle used when {}", query.description)); - } - - err + let alias = if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TyAlias)) { + Some(crate::error::Alias::Ty) + } else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) { + Some(crate::error::Alias::Trait) + } else { + None + }; + + let cycle_diag = crate::error::Cycle { + span, + cycle_stack, + stack_bottom: stack[0].query.description.to_owned(), + alias, + cycle_usage: cycle_usage, + stack_count, + }; + + cycle_diag.into_diagnostic(&sess.parse_sess.span_diagnostic) } pub fn print_query_stack( diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index fb2258434..118703fc0 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -15,12 +15,12 @@ mod config; pub use self::config::{QueryConfig, QueryDescription, QueryVTable}; use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; - use rustc_data_structures::sync::Lock; -use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::Diagnostic; use rustc_hir::def::DefKind; +use rustc_span::def_id::DefId; use rustc_span::Span; +use thin_vec::ThinVec; /// Description of a frame in the query stack. /// @@ -30,7 +30,9 @@ pub struct QueryStackFrame { pub name: &'static str, pub description: String, span: Option, - def_kind: Option, + pub def_id: Option, + pub def_kind: Option, + pub ty_adt_id: Option, /// This hash is used to deterministically pick /// a query to remove cycles in the parallel compiler. #[cfg(parallel_compiler)] @@ -43,14 +45,18 @@ impl QueryStackFrame { name: &'static str, description: String, span: Option, + def_id: Option, def_kind: Option, + ty_adt_id: Option, _hash: impl FnOnce() -> u64, ) -> Self { Self { name, description, span, + def_id, def_kind, + ty_adt_id, #[cfg(parallel_compiler)] hash: _hash(), } @@ -119,7 +125,10 @@ pub trait QueryContext: HasDepContext { fn start_query( &self, token: QueryJobId, + depth_limit: bool, diagnostics: Option<&Lock>>, compute: impl FnOnce() -> R, ) -> R; + + fn depth_limit_error(&self, job: QueryJobId); } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 5e8ea07d0..15b89daa6 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -7,6 +7,8 @@ use crate::query::caches::QueryCache; use crate::query::config::{QueryDescription, QueryVTable}; use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo}; use crate::query::{QueryContext, QueryMap, QuerySideEffects, QueryStackFrame}; +use crate::values::Value; +use crate::HandleCycleError; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; #[cfg(parallel_compiler)] @@ -14,7 +16,6 @@ use rustc_data_structures::profiling::TimingGuard; #[cfg(parallel_compiler)] use rustc_data_structures::sharded::Sharded; use rustc_data_structures::sync::Lock; -use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError}; use rustc_session::Session; use rustc_span::{Span, DUMMY_SP}; @@ -24,6 +25,7 @@ use std::fmt::Debug; use std::hash::Hash; use std::mem; use std::ptr; +use thin_vec::ThinVec; pub struct QueryState { #[cfg(parallel_compiler)] @@ -117,20 +119,48 @@ where #[inline(never)] fn mk_cycle( tcx: CTX, - error: CycleError, - handle_cycle_error: fn(CTX, DiagnosticBuilder<'_, ErrorGuaranteed>) -> V, + cycle_error: CycleError, + handler: HandleCycleError, cache: &dyn crate::query::QueryStorage, ) -> R where CTX: QueryContext, - V: std::fmt::Debug, + V: std::fmt::Debug + Value, R: Clone, { - let error = report_cycle(tcx.dep_context().sess(), error); - let value = handle_cycle_error(tcx, error); + let error = report_cycle(tcx.dep_context().sess(), &cycle_error); + let value = handle_cycle_error(*tcx.dep_context(), &cycle_error, error, handler); cache.store_nocache(value) } +fn handle_cycle_error( + tcx: CTX, + cycle_error: &CycleError, + mut error: DiagnosticBuilder<'_, ErrorGuaranteed>, + handler: HandleCycleError, +) -> V +where + CTX: DepContext, + V: Value, +{ + use HandleCycleError::*; + match handler { + Error => { + error.emit(); + Value::from_cycle_error(tcx, &cycle_error.cycle) + } + Fatal => { + error.emit(); + tcx.sess().abort_if_errors(); + unreachable!() + } + DelayBug => { + error.delay_as_bug(); + Value::from_cycle_error(tcx, &cycle_error.cycle) + } + } +} + impl<'tcx, K> JobOwner<'tcx, K> where K: Eq + Hash + Clone, @@ -336,6 +366,7 @@ fn try_execute_query( where C: QueryCache, C::Key: Clone + DepNodeParams, + C::Value: Value, CTX: QueryContext, { match JobOwner::<'_, C::Key>::try_start(&tcx, state, span, key.clone()) { @@ -381,7 +412,9 @@ where // Fast path for when incr. comp. is off. if !dep_graph.is_fully_enabled() { let prof_timer = tcx.dep_context().profiler().query_provider(); - let result = tcx.start_query(job_id, None, || query.compute(*tcx.dep_context(), key)); + let result = tcx.start_query(job_id, query.depth_limit, None, || { + query.compute(*tcx.dep_context(), key) + }); let dep_node_index = dep_graph.next_virtual_depnode_index(); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); return (result, dep_node_index); @@ -394,7 +427,7 @@ where // The diagnostics for this query will be promoted to the current session during // `try_mark_green()`, so we can ignore them here. - if let Some(ret) = tcx.start_query(job_id, None, || { + if let Some(ret) = tcx.start_query(job_id, false, None, || { try_load_from_disk_and_cache_in_memory(tcx, &key, &dep_node, query) }) { return ret; @@ -404,18 +437,20 @@ where let prof_timer = tcx.dep_context().profiler().query_provider(); let diagnostics = Lock::new(ThinVec::new()); - let (result, dep_node_index) = tcx.start_query(job_id, Some(&diagnostics), || { - if query.anon { - return dep_graph.with_anon_task(*tcx.dep_context(), query.dep_kind, || { - query.compute(*tcx.dep_context(), key) - }); - } + let (result, dep_node_index) = + tcx.start_query(job_id, query.depth_limit, Some(&diagnostics), || { + if query.anon { + return dep_graph.with_anon_task(*tcx.dep_context(), query.dep_kind, || { + query.compute(*tcx.dep_context(), key) + }); + } - // `to_dep_node` is expensive for some `DepKind`s. - let dep_node = dep_node_opt.unwrap_or_else(|| query.to_dep_node(*tcx.dep_context(), &key)); + // `to_dep_node` is expensive for some `DepKind`s. + let dep_node = + dep_node_opt.unwrap_or_else(|| query.to_dep_node(*tcx.dep_context(), &key)); - dep_graph.with_task(dep_node, *tcx.dep_context(), key, query.compute, query.hash_result) - }); + dep_graph.with_task(dep_node, *tcx.dep_context(), key, query.compute, query.hash_result) + }); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -454,14 +489,14 @@ where // First we try to load the result from the on-disk cache. // Some things are never cached on disk. - if query.cache_on_disk { + if let Some(try_load_from_disk) = query.try_load_from_disk { let prof_timer = tcx.dep_context().profiler().incr_cache_loading(); // The call to `with_query_deserialization` enforces that no new `DepNodes` // are created during deserialization. See the docs of that method for more // details. - let result = dep_graph - .with_query_deserialization(|| query.try_load_from_disk(tcx, prev_dep_node_index)); + let result = + dep_graph.with_query_deserialization(|| try_load_from_disk(tcx, prev_dep_node_index)); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -614,16 +649,12 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true)); if old_in_panic { - sess.struct_err( - "internal compiler error: re-entrant incremental verify failure, suppressing message", - ) - .emit(); + sess.emit_err(crate::error::Reentrant); } else { - sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node)) - .help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd)) - .note("Please follow the instructions below to create a bug report with the provided information") - .note("See for more information") - .emit(); + sess.emit_err(crate::error::IncrementCompilation { + run_cmd, + dep_node: format!("{:?}", dep_node), + }); panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result); } @@ -686,6 +717,7 @@ pub fn get_query(tcx: CTX, span: Span, key: Q::Key, mode: QueryMode) -> where Q: QueryDescription, Q::Key: DepNodeParams, + Q::Value: Value, CTX: QueryContext, { let query = Q::make_vtable(tcx, &key); @@ -718,6 +750,7 @@ pub fn force_query(tcx: CTX, key: Q::Key, dep_node: DepNode, Q::Key: DepNodeParams, + Q::Value: Value, CTX: QueryContext, { // We may be concurrently trying both execute and force a query. diff --git a/compiler/rustc_query_system/src/values.rs b/compiler/rustc_query_system/src/values.rs new file mode 100644 index 000000000..67fbf14e6 --- /dev/null +++ b/compiler/rustc_query_system/src/values.rs @@ -0,0 +1,15 @@ +use crate::dep_graph::DepContext; +use crate::query::QueryInfo; + +pub trait Value: Sized { + fn from_cycle_error(tcx: CTX, cycle: &[QueryInfo]) -> Self; +} + +impl Value for T { + default fn from_cycle_error(tcx: CTX, _: &[QueryInfo]) -> T { + tcx.sess().abort_if_errors(); + // Ideally we would use `bug!` here. But bug! is only defined in rustc_middle, and it's + // non-trivial to define it earlier. + panic!("Value::from_cycle_error called without errors"); + } +} -- cgit v1.2.3