From 837b550238aa671a591ccf282dddeab29cadb206 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 04:49:42 +0200 Subject: Merging upstream version 1.71.1+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_middle/src/query/erase.rs | 13 +- compiler/rustc_middle/src/query/keys.rs | 10 +- compiler/rustc_middle/src/query/mod.rs | 391 ++++---- compiler/rustc_middle/src/query/on_disk_cache.rs | 1042 ++++++++++++++++++++++ compiler/rustc_middle/src/query/plumbing.rs | 609 +++++++++++++ 5 files changed, 1839 insertions(+), 226 deletions(-) create mode 100644 compiler/rustc_middle/src/query/on_disk_cache.rs create mode 100644 compiler/rustc_middle/src/query/plumbing.rs (limited to 'compiler/rustc_middle/src/query') diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 7d9aea022..fd02a1613 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -28,7 +28,7 @@ pub fn erase(src: T) -> Erase { }; Erased::<::Result> { - // SAFETY: Is it safe to transmute to MaybeUninit for types with the same sizes. + // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes. data: unsafe { transmute_copy(&src) }, } } @@ -82,9 +82,10 @@ impl EraseType for Result>, rustc_errors::ErrorGuarantee [u8; size_of::>, rustc_errors::ErrorGuaranteed>>()]; } -impl EraseType for Result>, rustc_errors::ErrorGuaranteed> { - type Result = - [u8; size_of::>, rustc_errors::ErrorGuaranteed>>()]; +impl EraseType for Result>>, rustc_errors::ErrorGuaranteed> { + type Result = [u8; size_of::< + Result>>, rustc_errors::ErrorGuaranteed>, + >()]; } impl EraseType for Result, traits::query::NoSolution> { @@ -171,6 +172,10 @@ impl EraseType for ty::Binder<'_, ty::FnSig<'_>> { type Result = [u8; size_of::>>()]; } +impl EraseType for ty::Binder<'_, &'_ ty::List>> { + type Result = [u8; size_of::>>>()]; +} + impl EraseType for (&'_ T0, &'_ T1) { type Result = [u8; size_of::<(&'static (), &'static ())>()]; } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 23b28ac5c..fa62b7f32 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -174,14 +174,6 @@ impl AsLocalKey for DefId { } } -impl Key for ty::WithOptConstParam { - type CacheSelector = DefaultCacheSelector; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - self.did.default_span(tcx) - } -} - impl Key for SimplifiedType { type CacheSelector = DefaultCacheSelector; @@ -313,7 +305,7 @@ impl<'tcx> Key for (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>) { type CacheSelector = DefaultCacheSelector; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - (self.0).def.did.default_span(tcx) + (self.0).def.default_span(tcx) } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 7a5a16035..1528be42f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -4,12 +4,98 @@ //! ["Queries: demand-driven compilation"](https://rustc-dev-guide.rust-lang.org/query.html). //! This chapter includes instructions for adding new queries. -use crate::ty::{self, print::describe_as_module, TyCtxt}; +#![allow(unused_parens)] + +use crate::dep_graph; +use crate::dep_graph::DepKind; +use crate::infer::canonical::{self, Canonical}; +use crate::lint::LintExpectation; +use crate::metadata::ModChild; +use crate::middle::codegen_fn_attrs::CodegenFnAttrs; +use crate::middle::debugger_visualizer::DebuggerVisualizerFile; +use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; +use crate::middle::lib_features::LibFeatures; +use crate::middle::privacy::EffectiveVisibilities; +use crate::middle::resolve_bound_vars::{ObjectLifetimeDefault, ResolveBoundVars, ResolvedArg}; +use crate::middle::stability::{self, DeprecationEntry}; +use crate::mir; +use crate::mir::interpret::GlobalId; +use crate::mir::interpret::{ + ConstValue, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, +}; +use crate::mir::interpret::{LitToConstError, LitToConstInput}; +use crate::mir::mono::CodegenUnit; +use crate::query::erase::{erase, restore, Erase}; +use crate::query::plumbing::{query_ensure, query_get_at, DynamicQuery}; +use crate::thir; +use crate::traits::query::{ + CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution, +}; +use crate::traits::query::{ + DropckConstraint, DropckOutlivesResult, MethodAutoderefStepsResult, NormalizationResult, + OutlivesBound, +}; +use crate::traits::specialization_graph; +use crate::traits::{ + CanonicalChalkEnvironmentAndGoal, CodegenObligationError, EvaluationResult, ImplSource, + ObjectSafetyViolation, ObligationCause, OverflowError, WellFormedLoc, +}; +use crate::ty::fast_reject::SimplifiedType; +use crate::ty::layout::ValidityRequirement; +use crate::ty::subst::{GenericArg, SubstsRef}; +use crate::ty::util::AlwaysRequiresDrop; +use crate::ty::GeneratorDiagnosticData; +use crate::ty::TyCtxtFeed; +use crate::ty::{ + self, print::describe_as_module, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt, + UnusedGenericParams, +}; +use rustc_arena::TypedArena; +use rustc_ast as ast; +use rustc_ast::expand::allocator::AllocatorKind; +use rustc_attr as attr; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; +use rustc_data_structures::steal::Steal; +use rustc_data_structures::svh::Svh; +use rustc_data_structures::sync::Lrc; +use rustc_data_structures::sync::WorkerLocal; +use rustc_data_structures::unord::UnordSet; +use rustc_errors::ErrorGuaranteed; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, DocLinkResMap}; +use rustc_hir::def_id::{ + CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId, LocalDefIdMap, LocalDefIdSet, +}; +use rustc_hir::lang_items::{LangItem, LanguageItems}; +use rustc_hir::{Crate, ItemLocalId, TraitCandidate}; +use rustc_index::IndexVec; +use rustc_query_system::ich::StableHashingContext; +use rustc_query_system::query::{try_get_cached, CacheSelector, QueryCache, QueryMode, QueryState}; +use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; +use rustc_session::cstore::{CrateDepKind, CrateSource}; +use rustc_session::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib}; +use rustc_session::lint::LintExpectationId; +use rustc_session::Limits; use rustc_span::def_id::LOCAL_CRATE; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi; +use rustc_target::spec::PanicStrategy; +use std::mem; +use std::ops::Deref; +use std::path::PathBuf; +use std::sync::Arc; pub mod erase; mod keys; pub use keys::{AsLocalKey, Key, LocalCrate}; +pub mod on_disk_cache; +#[macro_use] +pub mod plumbing; +pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsure, TyCtxtEnsureWithValue}; // Each of these queries corresponds to a function pointer field in the // `Providers` struct for requesting a value of that type, and a method @@ -83,7 +169,7 @@ rustc_queries! { /// Avoid calling this query directly. query hir_module_items(key: LocalDefId) -> &'tcx rustc_middle::hir::ModuleItems { arena_cache - desc { |tcx| "getting HIR module items in `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "getting HIR module items in `{}`", tcx.def_path_str(key) } cache_on_disk_if { true } } @@ -92,14 +178,14 @@ rustc_queries! { /// This can be conveniently accessed by methods on `tcx.hir()`. /// Avoid calling this query directly. query hir_owner(key: hir::OwnerId) -> Option> { - desc { |tcx| "getting HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "getting HIR owner of `{}`", tcx.def_path_str(key) } } /// Gives access to the HIR ID for the given `LocalDefId` owner `key` if any. /// /// Definitions that were generated with no HIR, would be fed to return `None`. query opt_local_def_id_to_hir_id(key: LocalDefId) -> Option{ - desc { |tcx| "getting HIR ID of `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "getting HIR ID of `{}`", tcx.def_path_str(key) } feedable } @@ -108,7 +194,7 @@ rustc_queries! { /// This can be conveniently accessed by methods on `tcx.hir()`. /// Avoid calling this query directly. query hir_owner_parent(key: hir::OwnerId) -> hir::HirId { - desc { |tcx| "getting HIR parent of `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "getting HIR parent of `{}`", tcx.def_path_str(key) } } /// Gives access to the HIR nodes and bodies inside the HIR owner `key`. @@ -116,7 +202,7 @@ rustc_queries! { /// This can be conveniently accessed by methods on `tcx.hir()`. /// Avoid calling this query directly. query hir_owner_nodes(key: hir::OwnerId) -> hir::MaybeOwner<&'tcx hir::OwnerNodes<'tcx>> { - desc { |tcx| "getting HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "getting HIR owner items in `{}`", tcx.def_path_str(key) } } /// Gives access to the HIR attributes inside the HIR owner `key`. @@ -124,30 +210,7 @@ rustc_queries! { /// This can be conveniently accessed by methods on `tcx.hir()`. /// Avoid calling this query directly. query hir_attrs(key: hir::OwnerId) -> &'tcx hir::AttributeMap<'tcx> { - desc { |tcx| "getting HIR owner attributes in `{}`", tcx.def_path_str(key.to_def_id()) } - } - - /// Computes the `DefId` of the corresponding const parameter in case the `key` is a - /// const argument and returns `None` otherwise. - /// - /// ```ignore (incomplete) - /// let a = foo::<7>(); - /// // ^ Calling `opt_const_param_of` for this argument, - /// - /// fn foo() - /// // ^ returns this `DefId`. - /// - /// fn bar() { - /// // ^ While calling `opt_const_param_of` for other bodies returns `None`. - /// } - /// ``` - // It looks like caching this query on disk actually slightly - // worsened performance in #74376. - // - // Once const generics are more prevalently used, we might want to - // consider only caching calls returning `Some`. - query opt_const_param_of(key: LocalDefId) -> Option { - desc { |tcx| "computing the optional const parameter of `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "getting HIR owner attributes in `{}`", tcx.def_path_str(key) } } /// Given the def_id of a const-generic parameter, computes the associated default const @@ -181,7 +244,7 @@ rustc_queries! { } query collect_return_position_impl_trait_in_trait_tys(key: DefId) - -> Result<&'tcx FxHashMap>, ErrorGuaranteed> + -> Result<&'tcx FxHashMap>>, ErrorGuaranteed> { desc { "comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process" } cache_on_disk_if { key.is_local() } @@ -258,6 +321,15 @@ rustc_queries! { cache_on_disk_if { key.is_local() } } + query opaque_types_defined_by( + key: LocalDefId + ) -> &'tcx [LocalDefId] { + desc { + |tcx| "computing the opaque types defined by `{}`", + tcx.def_path_str(key.to_def_id()) + } + } + /// Returns the list of bounds that can be used for /// `SelectionCandidate::ProjectionCandidate(_)` and /// `ProjectionTyCandidate::TraitDef`. @@ -274,7 +346,7 @@ rustc_queries! { /// `key` is the `DefId` of the associated type or opaque type. /// /// Bounds from the parent (e.g. with nested impl trait) are not included. - query explicit_item_bounds(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<&'tcx [(ty::Predicate<'tcx>, Span)]> { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern @@ -318,7 +390,7 @@ rustc_queries! { query shallow_lint_levels_on(key: hir::OwnerId) -> &'tcx rustc_middle::lint::ShallowLintLevelMap { eval_always // fetches `resolutions` arena_cache - desc { |tcx| "looking up lint levels for `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "looking up lint levels for `{}`", tcx.def_path_str(key) } } query lint_expectations(_: ()) -> &'tcx Vec<(LintExpectationId, LintExpectation)> { @@ -328,7 +400,7 @@ rustc_queries! { query parent_module_from_def_id(key: LocalDefId) -> LocalDefId { eval_always - desc { |tcx| "getting the parent module of `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "getting the parent module of `{}`", tcx.def_path_str(key) } } query expn_that_defined(key: DefId) -> rustc_span::ExpnId { @@ -344,7 +416,7 @@ rustc_queries! { /// Checks whether a type is representable or infinitely sized query representability(_: LocalDefId) -> rustc_middle::ty::Representability { - desc { "checking if `{}` is representable", tcx.def_path_str(key.to_def_id()) } + desc { "checking if `{}` is representable", tcx.def_path_str(key) } // infinitely sized types will cause a cycle cycle_delay_bug // we don't want recursive representability calls to be forced with @@ -369,26 +441,24 @@ rustc_queries! { } /// Fetch the THIR for a given body. If typeck for that body failed, returns an empty `Thir`. - query thir_body(key: ty::WithOptConstParam) - -> Result<(&'tcx Steal>, thir::ExprId), ErrorGuaranteed> - { + query thir_body(key: LocalDefId) -> Result<(&'tcx Steal>, thir::ExprId), ErrorGuaranteed> { // Perf tests revealed that hashing THIR is inefficient (see #85729). no_hash - desc { |tcx| "building THIR for `{}`", tcx.def_path_str(key.did.to_def_id()) } + desc { |tcx| "building THIR for `{}`", tcx.def_path_str(key) } } /// Create a THIR tree for debugging. - query thir_tree(key: ty::WithOptConstParam) -> &'tcx String { + query thir_tree(key: LocalDefId) -> &'tcx String { no_hash arena_cache - desc { |tcx| "constructing THIR tree for `{}`", tcx.def_path_str(key.did.to_def_id()) } + desc { |tcx| "constructing THIR tree for `{}`", tcx.def_path_str(key) } } /// Create a list-like THIR representation for debugging. - query thir_flat(key: ty::WithOptConstParam) -> &'tcx String { + query thir_flat(key: LocalDefId) -> &'tcx String { no_hash arena_cache - desc { |tcx| "constructing flat THIR representation for `{}`", tcx.def_path_str(key.did.to_def_id()) } + desc { |tcx| "constructing flat THIR representation for `{}`", tcx.def_path_str(key) } } /// Set of all the `DefId`s in this crate that have MIR associated with @@ -407,59 +477,35 @@ rustc_queries! { cache_on_disk_if { key.is_local() } separate_provide_extern } - query mir_const_qualif_const_arg( - key: (LocalDefId, DefId) - ) -> mir::ConstQualifs { - desc { - |tcx| "const checking the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()) - } - } /// Fetch the MIR for a given `DefId` right after it's built - this includes /// unreachable code. - query mir_built(key: ty::WithOptConstParam) -> &'tcx Steal> { - desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key.did.to_def_id()) } + query mir_built(key: LocalDefId) -> &'tcx Steal> { + desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key) } } /// Fetch the MIR for a given `DefId` up till the point where it is /// ready for const qualification. /// /// See the README for the `mir` module for details. - query mir_const(key: ty::WithOptConstParam) -> &'tcx Steal> { - desc { - |tcx| "preparing {}`{}` for borrow checking", - if key.const_param_did.is_some() { "the const argument " } else { "" }, - tcx.def_path_str(key.did.to_def_id()), - } + query mir_const(key: LocalDefId) -> &'tcx Steal> { + desc { |tcx| "preparing `{}` for borrow checking", tcx.def_path_str(key) } no_hash } /// Try to build an abstract representation of the given constant. query thir_abstract_const( key: DefId - ) -> Result>, ErrorGuaranteed> { + ) -> Result>>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for `{}`", tcx.def_path_str(key), } separate_provide_extern } - /// Try to build an abstract representation of the given constant. - query thir_abstract_const_of_const_arg( - key: (LocalDefId, DefId) - ) -> Result>, ErrorGuaranteed> { - desc { - |tcx| - "building an abstract representation for the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()), - } - } - query mir_drops_elaborated_and_const_checked( - key: ty::WithOptConstParam - ) -> &'tcx Steal> { + query mir_drops_elaborated_and_const_checked(key: LocalDefId) -> &'tcx Steal> { no_hash - desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.did.to_def_id()) } + desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key) } } query mir_for_ctfe( @@ -470,34 +516,22 @@ rustc_queries! { separate_provide_extern } - query mir_for_ctfe_of_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::Body<'tcx> { - desc { - |tcx| "caching MIR for CTFE of the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()) - } - } - - query mir_promoted(key: ty::WithOptConstParam) -> - ( - &'tcx Steal>, - &'tcx Steal>> - ) { + query mir_promoted(key: LocalDefId) -> ( + &'tcx Steal>, + &'tcx Steal>> + ) { no_hash - desc { - |tcx| "processing MIR for {}`{}`", - if key.const_param_did.is_some() { "the const argument " } else { "" }, - tcx.def_path_str(key.did.to_def_id()), - } + desc { |tcx| "promoting constants in MIR for `{}`", tcx.def_path_str(key) } } query closure_typeinfo(key: LocalDefId) -> ty::ClosureTypeInfo<'tcx> { desc { |tcx| "finding symbols for captures of closure `{}`", - tcx.def_path_str(key.to_def_id()) + tcx.def_path_str(key) } } - query mir_generator_witnesses(key: DefId) -> &'tcx mir::GeneratorLayout<'tcx> { + query mir_generator_witnesses(key: DefId) -> &'tcx Option> { arena_cache desc { |tcx| "generator witness types for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } @@ -505,7 +539,7 @@ rustc_queries! { } query check_generator_obligations(key: LocalDefId) { - desc { |tcx| "verify auto trait bounds for generator interior type `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "verify auto trait bounds for generator interior type `{}`", tcx.def_path_str(key) } } /// MIR after our optimization passes have run. This is MIR that is ready @@ -544,14 +578,6 @@ rustc_queries! { cache_on_disk_if { key.is_local() } separate_provide_extern } - query promoted_mir_of_const_arg( - key: (LocalDefId, DefId) - ) -> &'tcx IndexVec> { - desc { - |tcx| "optimizing promoted MIR for the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()), - } - } /// Erases regions from `ty` to yield a new type. /// Normally you would just use `tcx.erase_regions(value)`, @@ -595,7 +621,7 @@ rustc_queries! { /// `explicit_predicates_of` and `explicit_item_bounds` will then take /// the appropriate subsets of the predicates here. query trait_explicit_predicates_and_bounds(key: LocalDefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key) } } /// Returns the predicates written explicitly by the user. @@ -637,7 +663,7 @@ rustc_queries! { /// returns the full set of predicates. If `Some`, then the query returns only the /// subset of super-predicates that reference traits that define the given associated type. /// This is used to avoid cycles in resolving types like `T::Item`. - query super_predicates_that_define_assoc_type(key: (DefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> { + query super_predicates_that_define_assoc_item(key: (DefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing the super traits of `{}` with associated type name `{}`", tcx.def_path_str(key.0), key.1 @@ -685,13 +711,11 @@ rustc_queries! { /// `is_const_fn` function. Consider using `is_const_fn` or `is_const_fn_raw` instead. query constness(key: DefId) -> hir::Constness { desc { |tcx| "checking if item is const: `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } separate_provide_extern } query asyncness(key: DefId) -> hir::IsAsync { desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -706,17 +730,9 @@ rustc_queries! { desc { |tcx| "checking if item is promotable: `{}`", tcx.def_path_str(key) } } - /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). - query is_foreign_item(key: DefId) -> bool { - desc { |tcx| "checking if `{}` is a foreign item", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - /// Returns `Some(generator_kind)` if the node pointed to by `def_id` is a generator. query generator_kind(def_id: DefId) -> Option { desc { |tcx| "looking up generator kind of `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } separate_provide_extern } @@ -739,9 +755,10 @@ rustc_queries! { desc { "computing the inferred outlives predicates for items in this crate" } } - /// Maps from an impl/trait `DefId` to a list of the `DefId`s of its items. + /// Maps from an impl/trait or struct/variant `DefId` + /// to a list of the `DefId`s of its associated items or fields. query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { - desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } + desc { |tcx| "collecting associated items or fields of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern } @@ -814,7 +831,6 @@ rustc_queries! { } query impl_polarity(impl_id: DefId) -> ty::ImplPolarity { desc { |tcx| "computing implementation polarity of `{}`", tcx.def_path_str(impl_id) } - cache_on_disk_if { impl_id.is_local() } separate_provide_extern } @@ -837,28 +853,16 @@ rustc_queries! { /// The result of unsafety-checking this `LocalDefId`. query unsafety_check_result(key: LocalDefId) -> &'tcx mir::UnsafetyCheckResult { - desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { true } } - query unsafety_check_result_for_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::UnsafetyCheckResult { - desc { - |tcx| "unsafety-checking the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()) - } - } /// Unsafety-check this `LocalDefId` with THIR unsafeck. This should be /// used with `-Zthir-unsafeck`. query thir_check_unsafety(key: LocalDefId) { - desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { true } } - query thir_check_unsafety_for_const_arg(key: (LocalDefId, DefId)) { - desc { - |tcx| "unsafety-checking the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()) - } - } /// Returns the types assumed to be well formed while "inside" of the given item. /// @@ -913,7 +917,7 @@ rustc_queries! { desc { |tcx| "checking privacy in {}", describe_as_module(key, tcx) } } - query check_liveness(key: DefId) { + query check_liveness(key: LocalDefId) { desc { |tcx| "checking liveness of variables in `{}`", tcx.def_path_str(key) } } @@ -952,29 +956,16 @@ rustc_queries! { separate_provide_extern } - query typeck_item_bodies(_: ()) -> () { - desc { "type-checking all item bodies" } - } - query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { - desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if { true } - } - query typeck_const_arg( - key: (LocalDefId, DefId) - ) -> &'tcx ty::TypeckResults<'tcx> { - desc { - |tcx| "type-checking the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()), - } + desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if(tcx) { !tcx.is_typeck_child(key.to_def_id()) } } query diagnostic_only_typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { - desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if { true } + desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) } } query used_trait_imports(key: LocalDefId) -> &'tcx UnordSet { - desc { |tcx| "finding used_trait_imports `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "finding used_trait_imports `{}`", tcx.def_path_str(key) } cache_on_disk_if { true } } @@ -989,15 +980,9 @@ rustc_queries! { /// Borrow-checks the function body. If this is a closure, returns /// additional requirements that the closure's creator must verify. query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> { - desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) } cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) } } - query mir_borrowck_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::BorrowCheckResult<'tcx> { - desc { - |tcx| "borrow-checking the const argument`{}`", - tcx.def_path_str(key.0.to_def_id()) - } - } /// Gets a complete map from all types to their inherent impls. /// Not meant to be used directly outside of coherence. @@ -1017,7 +1002,7 @@ rustc_queries! { query orphan_check_impl(key: LocalDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking whether impl `{}` follows the orphan rules", - tcx.def_path_str(key.to_def_id()), + tcx.def_path_str(key), } } @@ -1029,7 +1014,7 @@ rustc_queries! { desc { |tcx| "computing if `{}` (transitively) calls `{}`", key.0, - tcx.def_path_str(key.1.to_def_id()), + tcx.def_path_str(key.1), } } @@ -1094,7 +1079,6 @@ rustc_queries! { key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>> ) -> Option> { desc { "destructuring MIR constant"} - remap_env_constness } /// Dereference a constant reference or raw pointer and turn the result into a constant @@ -1103,7 +1087,6 @@ rustc_queries! { key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>> ) -> mir::ConstantKind<'tcx> { desc { "dereferencing MIR constant" } - remap_env_constness } query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> { @@ -1121,8 +1104,8 @@ rustc_queries! { desc { "converting literal to mir constant" } } - query check_match(key: LocalDefId) { - desc { |tcx| "match-checking `{}`", tcx.def_path_str(key.to_def_id()) } + query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { + desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { true } } @@ -1243,7 +1226,6 @@ rustc_queries! { query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] { desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } separate_provide_extern } /// Gets the rendered value of the specified constant or associated constant. @@ -1251,12 +1233,10 @@ rustc_queries! { query rendered_const(def_id: DefId) -> &'tcx String { arena_cache desc { |tcx| "rendering constant initializer of `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } separate_provide_extern } query impl_parent(def_id: DefId) -> Option { desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } separate_provide_extern } @@ -1296,7 +1276,7 @@ rustc_queries! { query codegen_select_candidate( key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) - ) -> Result<&'tcx ImplSource<'tcx, ()>, traits::CodegenObligationError> { + ) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> { cache_on_disk_if { true } desc { |tcx| "computing candidate for `{}`", key.1 } } @@ -1317,7 +1297,7 @@ rustc_queries! { desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } cache_on_disk_if { true } } - query object_safety_violations(trait_id: DefId) -> &'tcx [traits::ObjectSafetyViolation] { + query object_safety_violations(trait_id: DefId) -> &'tcx [ObjectSafetyViolation] { desc { |tcx| "determining object safety of trait `{}`", tcx.def_path_str(trait_id) } } query check_is_object_safe(trait_id: DefId) -> bool { @@ -1346,32 +1326,26 @@ rustc_queries! { /// `ty.is_copy()`, etc, since that will prune the environment where possible. query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Copy`", env.value } - remap_env_constness } /// Query backing `Ty::is_sized`. query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Sized`", env.value } - remap_env_constness } /// Query backing `Ty::is_freeze`. query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is freeze", env.value } - remap_env_constness } /// Query backing `Ty::is_unpin`. query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Unpin`", env.value } - remap_env_constness } /// Query backing `Ty::needs_drop`. query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` needs drop", env.value } - remap_env_constness } /// Query backing `Ty::has_significant_drop_raw`. query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` has a significant drop", env.value } - remap_env_constness } /// Query backing `Ty::is_structural_eq_shallow`. @@ -1411,7 +1385,6 @@ rustc_queries! { ) -> Result, ty::layout::LayoutError<'tcx>> { depth_limit desc { "computing layout of `{}`", key.value } - remap_env_constness } /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. @@ -1422,7 +1395,6 @@ rustc_queries! { key: ty::ParamEnvAnd<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List>)> ) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, ty::layout::FnAbiError<'tcx>> { desc { "computing call ABI of `{}` function pointers", key.value.0 } - remap_env_constness } /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for @@ -1434,7 +1406,6 @@ rustc_queries! { key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)> ) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, ty::layout::FnAbiError<'tcx>> { desc { "computing call ABI of `{}`", key.value.0 } - remap_env_constness } query dylib_dependency_formats(_: CrateNum) @@ -1478,7 +1449,7 @@ rustc_queries! { separate_provide_extern } query has_ffi_unwind_calls(key: LocalDefId) -> bool { - desc { |tcx| "checking if `{}` contains FFI-unwind calls", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "checking if `{}` contains FFI-unwind calls", tcx.def_path_str(key) } cache_on_disk_if { true } } query required_panic_strategy(_: CrateNum) -> Option { @@ -1518,13 +1489,12 @@ rustc_queries! { query impl_defaultness(def_id: DefId) -> hir::Defaultness { desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } separate_provide_extern feedable } query check_well_formed(key: hir::OwnerId) -> () { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) } } // The `DefId`s of all non-generic functions and statics in the given crate @@ -1553,7 +1523,7 @@ rustc_queries! { query is_unreachable_local_definition(def_id: LocalDefId) -> bool { desc { |tcx| "checking whether `{}` is reachable from outside the crate", - tcx.def_path_str(def_id.to_def_id()), + tcx.def_path_str(def_id), } } @@ -1747,7 +1717,7 @@ rustc_queries! { separate_provide_extern } query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option { - desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id.to_def_id()) } + desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id) } } query lib_features(_: ()) -> &'tcx LibFeatures { @@ -1818,12 +1788,18 @@ rustc_queries! { desc { "looking at the source for a crate" } separate_provide_extern } + /// Returns the debugger visualizers defined for this crate. - query debugger_visualizers(_: CrateNum) -> &'tcx Vec { + /// NOTE: This query has to be marked `eval_always` because it reads data + /// directly from disk that is not tracked anywhere else. I.e. it + /// represents a genuine input to the query system. + query debugger_visualizers(_: CrateNum) -> &'tcx Vec { arena_cache desc { "looking up the debugger visualizers for this crate" } separate_provide_extern + eval_always } + query postorder_cnums(_: ()) -> &'tcx [CrateNum] { eval_always desc { "generating a postorder list of CrateNums" } @@ -1851,7 +1827,7 @@ rustc_queries! { desc { "fetching potentially unused trait imports" } } query names_imported_by_glob_use(def_id: LocalDefId) -> &'tcx UnordSet { - desc { |tcx| "finding names imported by glob use for `{}`", tcx.def_path_str(def_id.to_def_id()) } + desc { |tcx| "finding names imported by glob use for `{}`", tcx.def_path_str(def_id) } } query stability_index(_: ()) -> &'tcx stability::Index { @@ -1865,8 +1841,7 @@ rustc_queries! { } /// A list of all traits in a crate, used by rustdoc and error reporting. - /// NOTE: Not named just `traits` due to a naming conflict. - query traits_in_crate(_: CrateNum) -> &'tcx [DefId] { + query traits(_: CrateNum) -> &'tcx [DefId] { desc { "fetching all traits in a crate" } separate_provide_extern } @@ -1937,7 +1912,16 @@ rustc_queries! { NoSolution, > { desc { "normalizing `{}`", goal.value.value } - remap_env_constness + } + + /// Do not call this query directly: invoke `normalize` instead. + query normalize_inherent_projection_ty( + goal: CanonicalProjectionGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{}`", goal.value.value } } /// Do not call this query directly: invoke `try_normalize_erasing_regions` instead. @@ -1945,7 +1929,6 @@ rustc_queries! { goal: ParamEnvAnd<'tcx, GenericArg<'tcx>> ) -> Result, NoSolution> { desc { "normalizing `{}`", goal.value } - remap_env_constness } query implied_outlives_bounds( @@ -1955,7 +1938,6 @@ rustc_queries! { NoSolution, > { desc { "computing implied outlives bounds for `{}`", goal.value.value } - remap_env_constness } /// Do not call this query directly: @@ -1967,19 +1949,18 @@ rustc_queries! { NoSolution, > { desc { "computing dropck types for `{}`", goal.value.value } - remap_env_constness } /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or /// `infcx.predicate_must_hold()` instead. query evaluate_obligation( goal: CanonicalPredicateGoal<'tcx> - ) -> Result { + ) -> Result { desc { "evaluating trait selection obligation `{}`", goal.value.value } } query evaluate_goal( - goal: traits::CanonicalChalkEnvironmentAndGoal<'tcx> + goal: CanonicalChalkEnvironmentAndGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, NoSolution @@ -1995,7 +1976,6 @@ rustc_queries! { NoSolution, > { desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal.value.value } - remap_env_constness } /// Do not call this query directly: part of the `Eq` type-op @@ -2006,7 +1986,6 @@ rustc_queries! { NoSolution, > { desc { "evaluating `type_op_eq` `{:?}`", goal.value.value } - remap_env_constness } /// Do not call this query directly: part of the `Subtype` type-op @@ -2017,7 +1996,6 @@ rustc_queries! { NoSolution, > { desc { "evaluating `type_op_subtype` `{:?}`", goal.value.value } - remap_env_constness } /// Do not call this query directly: part of the `ProvePredicate` type-op @@ -2038,7 +2016,6 @@ rustc_queries! { NoSolution, > { desc { "normalizing `{}`", goal.value.value.value } - remap_env_constness } /// Do not call this query directly: part of the `Normalize` type-op @@ -2049,7 +2026,6 @@ rustc_queries! { NoSolution, > { desc { "normalizing `{:?}`", goal.value.value.value } - remap_env_constness } /// Do not call this query directly: part of the `Normalize` type-op @@ -2060,7 +2036,6 @@ rustc_queries! { NoSolution, > { desc { "normalizing `{:?}`", goal.value.value.value } - remap_env_constness } /// Do not call this query directly: part of the `Normalize` type-op @@ -2071,7 +2046,6 @@ rustc_queries! { NoSolution, > { desc { "normalizing `{:?}`", goal.value.value.value } - remap_env_constness } query subst_and_check_impossible_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { @@ -2093,7 +2067,6 @@ rustc_queries! { goal: CanonicalTyGoal<'tcx> ) -> MethodAutoderefStepsResult<'tcx> { desc { "computing autoderef types for `{}`", goal.value.value } - remap_env_constness } query supported_target_features(_: CrateNum) -> &'tcx FxHashMap> { @@ -2138,17 +2111,6 @@ rustc_queries! { key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)> ) -> Result>, ErrorGuaranteed> { desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) } - remap_env_constness - } - - query resolve_instance_of_const_arg( - key: ty::ParamEnvAnd<'tcx, (LocalDefId, DefId, SubstsRef<'tcx>)> - ) -> Result>, ErrorGuaranteed> { - desc { - "resolving instance of the const argument `{}`", - ty::Instance::new(key.value.0.to_def_id(), key.value.2), - } - remap_env_constness } query reveal_opaque_types_in_bounds(key: &'tcx ty::List>) -> &'tcx ty::List> { @@ -2168,8 +2130,8 @@ rustc_queries! { /// all of the cases that the normal `ty::Ty`-based wfcheck does. This is fine, /// because the `ty::Ty`-based wfcheck is always run. query diagnostic_hir_wf_check( - key: (ty::Predicate<'tcx>, traits::WellFormedLoc) - ) -> &'tcx Option> { + key: (ty::Predicate<'tcx>, WellFormedLoc) + ) -> &'tcx Option> { arena_cache eval_always no_hash @@ -2197,7 +2159,7 @@ rustc_queries! { query compare_impl_const( key: (LocalDefId, DefId) ) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "checking assoc const `{}` has the same type as trait item", tcx.def_path_str(key.0.to_def_id()) } + desc { |tcx| "checking assoc const `{}` has the same type as trait item", tcx.def_path_str(key.0) } } query deduced_param_attrs(def_id: DefId) -> &'tcx [ty::DeducedParamAttrs] { @@ -2224,3 +2186,6 @@ rustc_queries! { desc { "check whether two const param are definitely not equal to eachother"} } } + +rustc_query_append! { define_callbacks! } +rustc_feedable_queries! { define_feedable! } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs new file mode 100644 index 000000000..220118ae5 --- /dev/null +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -0,0 +1,1042 @@ +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::memmap::Mmap; +use rustc_data_structures::stable_hasher::Hash64; +use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, RwLock}; +use rustc_data_structures::unhash::UnhashMap; +use rustc_data_structures::unord::UnordSet; +use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, StableCrateId, LOCAL_CRATE}; +use rustc_hir::definitions::DefPathHash; +use rustc_index::{Idx, IndexVec}; +use rustc_middle::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; +use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; +use rustc_middle::mir::{self, interpret}; +use rustc_middle::ty::codec::{RefDecodable, TyDecoder, TyEncoder}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_query_system::query::QuerySideEffects; +use rustc_serialize::{ + opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder}, + Decodable, Decoder, Encodable, Encoder, +}; +use rustc_session::Session; +use rustc_span::hygiene::{ + ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext, SyntaxContextData, +}; +use rustc_span::source_map::{SourceMap, StableSourceFileId}; +use rustc_span::{BytePos, ExpnData, ExpnHash, Pos, SourceFile, Span}; +use rustc_span::{CachingSourceMapView, Symbol}; +use std::collections::hash_map::Entry; +use std::io; +use std::mem; + +const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE; + +// A normal span encoded with both location information and a `SyntaxContext` +const TAG_FULL_SPAN: u8 = 0; +// A partial span with no location information, encoded only with a `SyntaxContext` +const TAG_PARTIAL_SPAN: u8 = 1; +const TAG_RELATIVE_SPAN: u8 = 2; + +const TAG_SYNTAX_CONTEXT: u8 = 0; +const TAG_EXPN_DATA: u8 = 1; + +// Tags for encoding Symbol's +const SYMBOL_STR: u8 = 0; +const SYMBOL_OFFSET: u8 = 1; +const SYMBOL_PREINTERNED: u8 = 2; + +/// Provides an interface to incremental compilation data cached from the +/// previous compilation session. This data will eventually include the results +/// of a few selected queries (like `typeck` and `mir_optimized`) and +/// any side effects that have been emitted during a query. +pub struct OnDiskCache<'sess> { + // The complete cache data in serialized form. + serialized_data: RwLock>, + + // Collects all `QuerySideEffects` created during the current compilation + // session. + current_side_effects: Lock>, + + source_map: &'sess SourceMap, + file_index_to_stable_id: FxHashMap, + + // Caches that are populated lazily during decoding. + file_index_to_file: Lock>>, + + // A map from dep-node to the position of the cached query result in + // `serialized_data`. + query_result_index: FxHashMap, + + // A map from dep-node to the position of any associated `QuerySideEffects` in + // `serialized_data`. + prev_side_effects_index: FxHashMap, + + alloc_decoding_state: AllocDecodingState, + + // A map from syntax context ids to the position of their associated + // `SyntaxContextData`. We use a `u32` instead of a `SyntaxContext` + // to represent the fact that we are storing *encoded* ids. When we decode + // a `SyntaxContext`, a new id will be allocated from the global `HygieneData`, + // which will almost certainly be different than the serialized id. + syntax_contexts: FxHashMap, + // A map from the `DefPathHash` of an `ExpnId` to the position + // of their associated `ExpnData`. Ideally, we would store a `DefId`, + // but we need to decode this before we've constructed a `TyCtxt` (which + // makes it difficult to decode a `DefId`). + + // Note that these `DefPathHashes` correspond to both local and foreign + // `ExpnData` (e.g `ExpnData.krate` may not be `LOCAL_CRATE`). Alternatively, + // we could look up the `ExpnData` from the metadata of foreign crates, + // but it seemed easier to have `OnDiskCache` be independent of the `CStore`. + expn_data: UnhashMap, + // Additional information used when decoding hygiene data. + hygiene_context: HygieneDecodeContext, + // Maps `ExpnHash`es to their raw value from the *previous* + // compilation session. This is used as an initial 'guess' when + // we try to map an `ExpnHash` to its value in the current + // compilation session. + foreign_expn_data: UnhashMap, +} + +// This type is used only for serialization and deserialization. +#[derive(Encodable, Decodable)] +struct Footer { + file_index_to_stable_id: FxHashMap, + query_result_index: EncodedDepNodeIndex, + side_effects_index: EncodedDepNodeIndex, + // The location of all allocations. + interpret_alloc_index: Vec, + // See `OnDiskCache.syntax_contexts` + syntax_contexts: FxHashMap, + // See `OnDiskCache.expn_data` + expn_data: UnhashMap, + foreign_expn_data: UnhashMap, +} + +pub type EncodedDepNodeIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Encodable, Decodable)] +struct SourceFileIndex(u32); + +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Encodable, Decodable)] +pub struct AbsoluteBytePos(u64); + +impl AbsoluteBytePos { + #[inline] + pub fn new(pos: usize) -> AbsoluteBytePos { + AbsoluteBytePos(pos.try_into().expect("Incremental cache file size overflowed u64.")) + } + + #[inline] + fn to_usize(self) -> usize { + self.0 as usize + } +} + +/// An `EncodedSourceFileId` is the same as a `StableSourceFileId` except that +/// the source crate is represented as a [StableCrateId] instead of as a +/// `CrateNum`. This way `EncodedSourceFileId` can be encoded and decoded +/// without any additional context, i.e. with a simple `opaque::Decoder` (which +/// is the only thing available when decoding the cache's [Footer]. +#[derive(Encodable, Decodable, Clone, Debug)] +struct EncodedSourceFileId { + file_name_hash: Hash64, + stable_crate_id: StableCrateId, +} + +impl EncodedSourceFileId { + #[inline] + fn translate(&self, tcx: TyCtxt<'_>) -> StableSourceFileId { + let cnum = tcx.stable_crate_id_to_crate_num(self.stable_crate_id); + StableSourceFileId { file_name_hash: self.file_name_hash, cnum } + } + + #[inline] + fn new(tcx: TyCtxt<'_>, file: &SourceFile) -> EncodedSourceFileId { + let source_file_id = StableSourceFileId::new(file); + EncodedSourceFileId { + file_name_hash: source_file_id.file_name_hash, + stable_crate_id: tcx.stable_crate_id(source_file_id.cnum), + } + } +} + +impl<'sess> OnDiskCache<'sess> { + /// Creates a new `OnDiskCache` instance from the serialized data in `data`. + pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Self { + debug_assert!(sess.opts.incremental.is_some()); + + // Wrap in a scope so we can borrow `data`. + let footer: Footer = { + let mut decoder = MemDecoder::new(&data, start_pos); + + // Decode the *position* of the footer, which can be found in the + // last 8 bytes of the file. + let footer_pos = decoder + .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |decoder| { + IntEncodedWithFixedSize::decode(decoder).0 as usize + }); + // Decode the file footer, which contains all the lookup tables, etc. + decoder.with_position(footer_pos, |decoder| decode_tagged(decoder, TAG_FILE_FOOTER)) + }; + + Self { + serialized_data: RwLock::new(Some(data)), + file_index_to_stable_id: footer.file_index_to_stable_id, + file_index_to_file: Default::default(), + source_map: sess.source_map(), + current_side_effects: Default::default(), + query_result_index: footer.query_result_index.into_iter().collect(), + prev_side_effects_index: footer.side_effects_index.into_iter().collect(), + alloc_decoding_state: AllocDecodingState::new(footer.interpret_alloc_index), + syntax_contexts: footer.syntax_contexts, + expn_data: footer.expn_data, + foreign_expn_data: footer.foreign_expn_data, + hygiene_context: Default::default(), + } + } + + pub fn new_empty(source_map: &'sess SourceMap) -> Self { + Self { + serialized_data: RwLock::new(None), + file_index_to_stable_id: Default::default(), + file_index_to_file: Default::default(), + source_map, + current_side_effects: Default::default(), + query_result_index: Default::default(), + prev_side_effects_index: Default::default(), + alloc_decoding_state: AllocDecodingState::new(Vec::new()), + syntax_contexts: FxHashMap::default(), + expn_data: UnhashMap::default(), + foreign_expn_data: UnhashMap::default(), + hygiene_context: Default::default(), + } + } + + /// Execute all cache promotions and release the serialized backing Mmap. + /// + /// Cache promotions require invoking queries, which needs to read the serialized data. + /// In order to serialize the new on-disk cache, the former on-disk cache file needs to be + /// deleted, hence we won't be able to refer to its memmapped data. + pub fn drop_serialized_data(&self, tcx: TyCtxt<'_>) { + // Load everything into memory so we can write it out to the on-disk + // cache. The vast majority of cacheable query results should already + // be in memory, so this should be a cheap operation. + // Do this *before* we clone 'latest_foreign_def_path_hashes', since + // loading existing queries may cause us to create new DepNodes, which + // may in turn end up invoking `store_foreign_def_id_hash` + tcx.dep_graph.exec_cache_promotions(tcx); + + *self.serialized_data.write() = None; + } + + pub fn serialize(&self, tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult { + // Serializing the `DepGraph` should not modify it. + tcx.dep_graph.with_ignore(|| { + // Allocate `SourceFileIndex`es. + let (file_to_file_index, file_index_to_stable_id) = { + let files = tcx.sess.source_map().files(); + let mut file_to_file_index = + FxHashMap::with_capacity_and_hasher(files.len(), Default::default()); + let mut file_index_to_stable_id = + FxHashMap::with_capacity_and_hasher(files.len(), Default::default()); + + for (index, file) in files.iter().enumerate() { + let index = SourceFileIndex(index as u32); + let file_ptr: *const SourceFile = &**file as *const _; + file_to_file_index.insert(file_ptr, index); + let source_file_id = EncodedSourceFileId::new(tcx, &file); + file_index_to_stable_id.insert(index, source_file_id); + } + + (file_to_file_index, file_index_to_stable_id) + }; + + let hygiene_encode_context = HygieneEncodeContext::default(); + + let mut encoder = CacheEncoder { + tcx, + encoder, + type_shorthands: Default::default(), + predicate_shorthands: Default::default(), + interpret_allocs: Default::default(), + source_map: CachingSourceMapView::new(tcx.sess.source_map()), + file_to_file_index, + hygiene_context: &hygiene_encode_context, + symbol_table: Default::default(), + }; + + // Encode query results. + let mut query_result_index = EncodedDepNodeIndex::new(); + + tcx.sess.time("encode_query_results", || { + let enc = &mut encoder; + let qri = &mut query_result_index; + (tcx.query_system.fns.encode_query_results)(tcx, enc, qri); + }); + + // Encode side effects. + let side_effects_index: EncodedDepNodeIndex = self + .current_side_effects + .borrow() + .iter() + .map(|(dep_node_index, side_effects)| { + let pos = AbsoluteBytePos::new(encoder.position()); + let dep_node_index = SerializedDepNodeIndex::new(dep_node_index.index()); + encoder.encode_tagged(dep_node_index, side_effects); + + (dep_node_index, pos) + }) + .collect(); + + let interpret_alloc_index = { + let mut interpret_alloc_index = Vec::new(); + let mut n = 0; + loop { + let new_n = encoder.interpret_allocs.len(); + // If we have found new IDs, serialize those too. + if n == new_n { + // Otherwise, abort. + break; + } + interpret_alloc_index.reserve(new_n - n); + for idx in n..new_n { + let id = encoder.interpret_allocs[idx]; + let pos: u32 = encoder.position().try_into().unwrap(); + interpret_alloc_index.push(pos); + interpret::specialized_encode_alloc_id(&mut encoder, tcx, id); + } + n = new_n; + } + interpret_alloc_index + }; + + let mut syntax_contexts = FxHashMap::default(); + let mut expn_data = UnhashMap::default(); + let mut foreign_expn_data = UnhashMap::default(); + + // Encode all hygiene data (`SyntaxContextData` and `ExpnData`) from the current + // session. + + hygiene_encode_context.encode( + &mut encoder, + |encoder, index, ctxt_data| { + let pos = AbsoluteBytePos::new(encoder.position()); + encoder.encode_tagged(TAG_SYNTAX_CONTEXT, ctxt_data); + syntax_contexts.insert(index, pos); + }, + |encoder, expn_id, data, hash| { + if expn_id.krate == LOCAL_CRATE { + let pos = AbsoluteBytePos::new(encoder.position()); + encoder.encode_tagged(TAG_EXPN_DATA, data); + expn_data.insert(hash, pos); + } else { + foreign_expn_data.insert(hash, expn_id.local_id.as_u32()); + } + }, + ); + + // Encode the file footer. + let footer_pos = encoder.position() as u64; + encoder.encode_tagged( + TAG_FILE_FOOTER, + &Footer { + file_index_to_stable_id, + query_result_index, + side_effects_index, + interpret_alloc_index, + syntax_contexts, + expn_data, + foreign_expn_data, + }, + ); + + // Encode the position of the footer as the last 8 bytes of the + // file so we know where to look for it. + IntEncodedWithFixedSize(footer_pos).encode(&mut encoder.encoder); + + // DO NOT WRITE ANYTHING TO THE ENCODER AFTER THIS POINT! The address + // of the footer must be the last thing in the data stream. + + encoder.finish() + }) + } + + /// Loads a `QuerySideEffects` created during the previous compilation session. + pub fn load_side_effects( + &self, + tcx: TyCtxt<'_>, + dep_node_index: SerializedDepNodeIndex, + ) -> QuerySideEffects { + let side_effects: Option = + self.load_indexed(tcx, dep_node_index, &self.prev_side_effects_index); + + side_effects.unwrap_or_default() + } + + /// Stores a `QuerySideEffects` emitted during the current compilation session. + /// Anything stored like this will be available via `load_side_effects` in + /// the next compilation session. + pub fn store_side_effects(&self, dep_node_index: DepNodeIndex, side_effects: QuerySideEffects) { + let mut current_side_effects = self.current_side_effects.borrow_mut(); + let prev = current_side_effects.insert(dep_node_index, side_effects); + debug_assert!(prev.is_none()); + } + + /// Return whether the cached query result can be decoded. + #[inline] + pub fn loadable_from_disk(&self, dep_node_index: SerializedDepNodeIndex) -> bool { + self.query_result_index.contains_key(&dep_node_index) + // with_decoder is infallible, so we can stop here + } + + /// Returns the cached query result if there is something in the cache for + /// the given `SerializedDepNodeIndex`; otherwise returns `None`. + pub fn try_load_query_result<'tcx, T>( + &self, + tcx: TyCtxt<'tcx>, + dep_node_index: SerializedDepNodeIndex, + ) -> Option + where + T: for<'a> Decodable>, + { + let opt_value = self.load_indexed(tcx, dep_node_index, &self.query_result_index); + debug_assert_eq!(opt_value.is_some(), self.loadable_from_disk(dep_node_index)); + opt_value + } + + /// Stores side effect emitted during computation of an anonymous query. + /// Since many anonymous queries can share the same `DepNode`, we aggregate + /// them -- as opposed to regular queries where we assume that there is a + /// 1:1 relationship between query-key and `DepNode`. + pub fn store_side_effects_for_anon_node( + &self, + dep_node_index: DepNodeIndex, + side_effects: QuerySideEffects, + ) { + let mut current_side_effects = self.current_side_effects.borrow_mut(); + + let x = current_side_effects.entry(dep_node_index).or_default(); + x.append(side_effects); + } + + fn load_indexed<'tcx, T>( + &self, + tcx: TyCtxt<'tcx>, + dep_node_index: SerializedDepNodeIndex, + index: &FxHashMap, + ) -> Option + where + T: for<'a> Decodable>, + { + let pos = index.get(&dep_node_index).cloned()?; + let value = self.with_decoder(tcx, pos, |decoder| decode_tagged(decoder, dep_node_index)); + Some(value) + } + + fn with_decoder<'a, 'tcx, T, F: for<'s> FnOnce(&mut CacheDecoder<'s, 'tcx>) -> T>( + &'sess self, + tcx: TyCtxt<'tcx>, + pos: AbsoluteBytePos, + f: F, + ) -> T + where + T: Decodable>, + { + let serialized_data = self.serialized_data.read(); + let mut decoder = CacheDecoder { + tcx, + opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()), + source_map: self.source_map, + file_index_to_file: &self.file_index_to_file, + file_index_to_stable_id: &self.file_index_to_stable_id, + alloc_decoding_session: self.alloc_decoding_state.new_decoding_session(), + syntax_contexts: &self.syntax_contexts, + expn_data: &self.expn_data, + foreign_expn_data: &self.foreign_expn_data, + hygiene_context: &self.hygiene_context, + }; + f(&mut decoder) + } +} + +//- DECODING ------------------------------------------------------------------- + +/// A decoder that can read from the incremental compilation cache. It is similar to the one +/// we use for crate metadata decoding in that it can rebase spans and eventually +/// will also handle things that contain `Ty` instances. +pub struct CacheDecoder<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + opaque: MemDecoder<'a>, + source_map: &'a SourceMap, + file_index_to_file: &'a Lock>>, + file_index_to_stable_id: &'a FxHashMap, + alloc_decoding_session: AllocDecodingSession<'a>, + syntax_contexts: &'a FxHashMap, + expn_data: &'a UnhashMap, + foreign_expn_data: &'a UnhashMap, + hygiene_context: &'a HygieneDecodeContext, +} + +impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { + #[inline] + fn file_index_to_file(&self, index: SourceFileIndex) -> Lrc { + let CacheDecoder { + tcx, + ref file_index_to_file, + ref file_index_to_stable_id, + ref source_map, + .. + } = *self; + + file_index_to_file + .borrow_mut() + .entry(index) + .or_insert_with(|| { + let stable_id = file_index_to_stable_id[&index].translate(tcx); + + // If this `SourceFile` is from a foreign crate, then make sure + // that we've imported all of the source files from that crate. + // This has usually already been done during macro invocation. + // However, when encoding query results like `TypeckResults`, + // we might encode an `AdtDef` for a foreign type (because it + // was referenced in the body of the function). There is no guarantee + // that we will load the source files from that crate during macro + // expansion, so we use `import_source_files` to ensure that the foreign + // source files are actually imported before we call `source_file_by_stable_id`. + if stable_id.cnum != LOCAL_CRATE { + self.tcx.cstore_untracked().import_source_files(self.tcx.sess, stable_id.cnum); + } + + source_map + .source_file_by_stable_id(stable_id) + .expect("failed to lookup `SourceFile` in new context") + }) + .clone() + } +} + +// Decodes something that was encoded with `encode_tagged()` and verify that the +// tag matches and the correct amount of bytes was read. +fn decode_tagged(decoder: &mut D, expected_tag: T) -> V +where + T: Decodable + Eq + std::fmt::Debug, + V: Decodable, + D: Decoder, +{ + let start_pos = decoder.position(); + + let actual_tag = T::decode(decoder); + assert_eq!(actual_tag, expected_tag); + let value = V::decode(decoder); + let end_pos = decoder.position(); + + let expected_len: u64 = Decodable::decode(decoder); + assert_eq!((end_pos - start_pos) as u64, expected_len); + + value +} + +impl<'a, 'tcx> TyDecoder for CacheDecoder<'a, 'tcx> { + type I = TyCtxt<'tcx>; + const CLEAR_CROSS_CRATE: bool = false; + + #[inline] + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn cached_ty_for_shorthand(&mut self, shorthand: usize, or_insert_with: F) -> Ty<'tcx> + where + F: FnOnce(&mut Self) -> Ty<'tcx>, + { + let tcx = self.tcx; + + let cache_key = ty::CReaderCacheKey { cnum: None, pos: shorthand }; + + if let Some(&ty) = tcx.ty_rcache.borrow().get(&cache_key) { + return ty; + } + + let ty = or_insert_with(self); + // This may overwrite the entry, but it should overwrite with the same value. + tcx.ty_rcache.borrow_mut().insert_same(cache_key, ty); + ty + } + + fn with_position(&mut self, pos: usize, f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + debug_assert!(pos < self.opaque.len()); + + let new_opaque = MemDecoder::new(self.opaque.data(), pos); + let old_opaque = mem::replace(&mut self.opaque, new_opaque); + let r = f(self); + self.opaque = old_opaque; + r + } + + fn decode_alloc_id(&mut self) -> interpret::AllocId { + let alloc_decoding_session = self.alloc_decoding_session; + alloc_decoding_session.decode_alloc_id(self) + } +} + +rustc_middle::implement_ty_decoder!(CacheDecoder<'a, 'tcx>); + +// This ensures that the `Decodable::decode` specialization for `Vec` is used +// when a `CacheDecoder` is passed to `Decodable::decode`. Unfortunately, we have to manually opt +// into specializations this way, given how `CacheDecoder` and the decoding traits currently work. +impl<'a, 'tcx> Decodable> for Vec { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + Decodable::decode(&mut d.opaque) + } +} + +impl<'a, 'tcx> Decodable> for SyntaxContext { + fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self { + let syntax_contexts = decoder.syntax_contexts; + rustc_span::hygiene::decode_syntax_context(decoder, decoder.hygiene_context, |this, id| { + // This closure is invoked if we haven't already decoded the data for the `SyntaxContext` we are deserializing. + // We look up the position of the associated `SyntaxData` and decode it. + let pos = syntax_contexts.get(&id).unwrap(); + this.with_position(pos.to_usize(), |decoder| { + let data: SyntaxContextData = decode_tagged(decoder, TAG_SYNTAX_CONTEXT); + data + }) + }) + } +} + +impl<'a, 'tcx> Decodable> for ExpnId { + fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self { + let hash = ExpnHash::decode(decoder); + if hash.is_root() { + return ExpnId::root(); + } + + if let Some(expn_id) = ExpnId::from_hash(hash) { + return expn_id; + } + + let krate = decoder.tcx.stable_crate_id_to_crate_num(hash.stable_crate_id()); + + let expn_id = if krate == LOCAL_CRATE { + // We look up the position of the associated `ExpnData` and decode it. + let pos = decoder + .expn_data + .get(&hash) + .unwrap_or_else(|| panic!("Bad hash {:?} (map {:?})", hash, decoder.expn_data)); + + let data: ExpnData = decoder + .with_position(pos.to_usize(), |decoder| decode_tagged(decoder, TAG_EXPN_DATA)); + let expn_id = rustc_span::hygiene::register_local_expn_id(data, hash); + + #[cfg(debug_assertions)] + { + use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; + let local_hash = decoder.tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + expn_id.expn_data().hash_stable(&mut hcx, &mut hasher); + hasher.finish() + }); + debug_assert_eq!(hash.local_hash(), local_hash); + } + + expn_id + } else { + let index_guess = decoder.foreign_expn_data[&hash]; + decoder.tcx.cstore_untracked().expn_hash_to_expn_id( + decoder.tcx.sess, + krate, + index_guess, + hash, + ) + }; + + debug_assert_eq!(expn_id.krate, krate); + expn_id + } +} + +impl<'a, 'tcx> Decodable> for Span { + fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self { + let ctxt = SyntaxContext::decode(decoder); + let parent = Option::::decode(decoder); + let tag: u8 = Decodable::decode(decoder); + + if tag == TAG_PARTIAL_SPAN { + return Span::new(BytePos(0), BytePos(0), ctxt, parent); + } else if tag == TAG_RELATIVE_SPAN { + let dlo = u32::decode(decoder); + let dto = u32::decode(decoder); + + let enclosing = decoder.tcx.source_span_untracked(parent.unwrap()).data_untracked(); + let span = Span::new( + enclosing.lo + BytePos::from_u32(dlo), + enclosing.lo + BytePos::from_u32(dto), + ctxt, + parent, + ); + + return span; + } else { + debug_assert_eq!(tag, TAG_FULL_SPAN); + } + + let file_lo_index = SourceFileIndex::decode(decoder); + let line_lo = usize::decode(decoder); + let col_lo = BytePos::decode(decoder); + let len = BytePos::decode(decoder); + + let file_lo = decoder.file_index_to_file(file_lo_index); + let lo = file_lo.lines(|lines| lines[line_lo - 1] + col_lo); + let hi = lo + len; + + Span::new(lo, hi, ctxt, parent) + } +} + +// copy&paste impl from rustc_metadata +impl<'a, 'tcx> Decodable> for Symbol { + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + let tag = d.read_u8(); + + match tag { + SYMBOL_STR => { + let s = d.read_str(); + Symbol::intern(s) + } + SYMBOL_OFFSET => { + // read str offset + let pos = d.read_usize(); + + // move to str offset and read + d.opaque.with_position(pos, |d| { + let s = d.read_str(); + Symbol::intern(s) + }) + } + SYMBOL_PREINTERNED => { + let symbol_index = d.read_u32(); + Symbol::new_from_decoded(symbol_index) + } + _ => unreachable!(), + } + } +} + +impl<'a, 'tcx> Decodable> for CrateNum { + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + let stable_id = StableCrateId::decode(d); + let cnum = d.tcx.stable_crate_id_to_crate_num(stable_id); + cnum + } +} + +// This impl makes sure that we get a runtime error when we try decode a +// `DefIndex` that is not contained in a `DefId`. Such a case would be problematic +// because we would not know how to transform the `DefIndex` to the current +// context. +impl<'a, 'tcx> Decodable> for DefIndex { + fn decode(_d: &mut CacheDecoder<'a, 'tcx>) -> DefIndex { + panic!("trying to decode `DefIndex` outside the context of a `DefId`") + } +} + +// Both the `CrateNum` and the `DefIndex` of a `DefId` can change in between two +// compilation sessions. We use the `DefPathHash`, which is stable across +// sessions, to map the old `DefId` to the new one. +impl<'a, 'tcx> Decodable> for DefId { + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + // Load the `DefPathHash` which is was we encoded the `DefId` as. + let def_path_hash = DefPathHash::decode(d); + + // Using the `DefPathHash`, we can lookup the new `DefId`. + // Subtle: We only encode a `DefId` as part of a query result. + // If we get to this point, then all of the query inputs were green, + // which means that the definition with this hash is guaranteed to + // still exist in the current compilation session. + d.tcx.def_path_hash_to_def_id(def_path_hash, &mut || { + panic!("Failed to convert DefPathHash {def_path_hash:?}") + }) + } +} + +impl<'a, 'tcx> Decodable> for &'tcx UnordSet { + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + RefDecodable::decode(d) + } +} + +impl<'a, 'tcx> Decodable> + for &'tcx FxHashMap>> +{ + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + RefDecodable::decode(d) + } +} + +impl<'a, 'tcx> Decodable> + for &'tcx IndexVec> +{ + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + RefDecodable::decode(d) + } +} + +impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + RefDecodable::decode(d) + } +} + +impl<'a, 'tcx> Decodable> for &'tcx [(ty::Clause<'tcx>, Span)] { + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + RefDecodable::decode(d) + } +} + +impl<'a, 'tcx> Decodable> for &'tcx [rustc_ast::InlineAsmTemplatePiece] { + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + RefDecodable::decode(d) + } +} + +macro_rules! impl_ref_decoder { + (<$tcx:tt> $($ty:ty,)*) => { + $(impl<'a, $tcx> Decodable> for &$tcx [$ty] { + #[inline] + fn decode(d: &mut CacheDecoder<'a, $tcx>) -> Self { + RefDecodable::decode(d) + } + })* + }; +} + +impl_ref_decoder! {<'tcx> + Span, + rustc_ast::Attribute, + rustc_span::symbol::Ident, + ty::Variance, + rustc_span::def_id::DefId, + rustc_span::def_id::LocalDefId, + (rustc_middle::middle::exported_symbols::ExportedSymbol<'tcx>, rustc_middle::middle::exported_symbols::SymbolExportInfo), + ty::DeducedParamAttrs, +} + +//- ENCODING ------------------------------------------------------------------- + +/// An encoder that can write to the incremental compilation cache. +pub struct CacheEncoder<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + encoder: FileEncoder, + type_shorthands: FxHashMap, usize>, + predicate_shorthands: FxHashMap, usize>, + interpret_allocs: FxIndexSet, + source_map: CachingSourceMapView<'tcx>, + file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>, + hygiene_context: &'a HygieneEncodeContext, + symbol_table: FxHashMap, +} + +impl<'a, 'tcx> CacheEncoder<'a, 'tcx> { + #[inline] + fn source_file_index(&mut self, source_file: Lrc) -> SourceFileIndex { + self.file_to_file_index[&(&*source_file as *const SourceFile)] + } + + /// Encode something with additional information that allows to do some + /// sanity checks when decoding the data again. This method will first + /// encode the specified tag, then the given value, then the number of + /// bytes taken up by tag and value. On decoding, we can then verify that + /// we get the expected tag and read the expected number of bytes. + pub fn encode_tagged, V: Encodable>(&mut self, tag: T, value: &V) { + let start_pos = self.position(); + + tag.encode(self); + value.encode(self); + + let end_pos = self.position(); + ((end_pos - start_pos) as u64).encode(self); + } + + #[inline] + fn finish(self) -> Result { + self.encoder.finish() + } +} + +impl<'a, 'tcx> Encodable> for SyntaxContext { + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { + rustc_span::hygiene::raw_encode_syntax_context(*self, s.hygiene_context, s); + } +} + +impl<'a, 'tcx> Encodable> for ExpnId { + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { + s.hygiene_context.schedule_expn_data_for_encoding(*self); + self.expn_hash().encode(s); + } +} + +impl<'a, 'tcx> Encodable> for Span { + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { + let span_data = self.data_untracked(); + span_data.ctxt.encode(s); + span_data.parent.encode(s); + + if span_data.is_dummy() { + return TAG_PARTIAL_SPAN.encode(s); + } + + if let Some(parent) = span_data.parent { + let enclosing = s.tcx.source_span(parent).data_untracked(); + if enclosing.contains(span_data) { + TAG_RELATIVE_SPAN.encode(s); + (span_data.lo - enclosing.lo).to_u32().encode(s); + (span_data.hi - enclosing.lo).to_u32().encode(s); + return; + } + } + + let pos = s.source_map.byte_pos_to_line_and_col(span_data.lo); + let partial_span = match &pos { + Some((file_lo, _, _)) => !file_lo.contains(span_data.hi), + None => true, + }; + + if partial_span { + return TAG_PARTIAL_SPAN.encode(s); + } + + let (file_lo, line_lo, col_lo) = pos.unwrap(); + + let len = span_data.hi - span_data.lo; + + let source_file_index = s.source_file_index(file_lo); + + TAG_FULL_SPAN.encode(s); + source_file_index.encode(s); + line_lo.encode(s); + col_lo.encode(s); + len.encode(s); + } +} + +// copy&paste impl from rustc_metadata +impl<'a, 'tcx> Encodable> for Symbol { + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { + // if symbol preinterned, emit tag and symbol index + if self.is_preinterned() { + s.encoder.emit_u8(SYMBOL_PREINTERNED); + s.encoder.emit_u32(self.as_u32()); + } else { + // otherwise write it as string or as offset to it + match s.symbol_table.entry(*self) { + Entry::Vacant(o) => { + s.encoder.emit_u8(SYMBOL_STR); + let pos = s.encoder.position(); + o.insert(pos); + s.emit_str(self.as_str()); + } + Entry::Occupied(o) => { + let x = *o.get(); + s.emit_u8(SYMBOL_OFFSET); + s.emit_usize(x); + } + } + } + } +} + +impl<'a, 'tcx> TyEncoder for CacheEncoder<'a, 'tcx> { + type I = TyCtxt<'tcx>; + const CLEAR_CROSS_CRATE: bool = false; + + #[inline] + fn position(&self) -> usize { + self.encoder.position() + } + #[inline] + fn type_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.type_shorthands + } + #[inline] + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.predicate_shorthands + } + #[inline] + fn encode_alloc_id(&mut self, alloc_id: &interpret::AllocId) { + let (index, _) = self.interpret_allocs.insert_full(*alloc_id); + + index.encode(self); + } +} + +impl<'a, 'tcx> Encodable> for CrateNum { + #[inline] + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { + s.tcx.stable_crate_id(*self).encode(s); + } +} + +impl<'a, 'tcx> Encodable> for DefId { + #[inline] + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { + s.tcx.def_path_hash(*self).encode(s); + } +} + +impl<'a, 'tcx> Encodable> for DefIndex { + fn encode(&self, _: &mut CacheEncoder<'a, 'tcx>) { + bug!("encoding `DefIndex` without context"); + } +} + +macro_rules! encoder_methods { + ($($name:ident($ty:ty);)*) => { + #[inline] + $(fn $name(&mut self, value: $ty) { + self.encoder.$name(value) + })* + } +} + +impl<'a, 'tcx> Encoder for CacheEncoder<'a, 'tcx> { + encoder_methods! { + emit_usize(usize); + emit_u128(u128); + emit_u64(u64); + emit_u32(u32); + emit_u16(u16); + emit_u8(u8); + + emit_isize(isize); + emit_i128(i128); + emit_i64(i64); + emit_i32(i32); + emit_i16(i16); + + emit_raw_bytes(&[u8]); + } +} + +// This ensures that the `Encodable::encode` specialization for byte slices +// is used when a `CacheEncoder` having an `opaque::FileEncoder` is passed to `Encodable::encode`. +// Unfortunately, we have to manually opt into specializations this way, given how `CacheEncoder` +// and the encoding traits currently work. +impl<'a, 'tcx> Encodable> for [u8] { + fn encode(&self, e: &mut CacheEncoder<'a, 'tcx>) { + self.encode(&mut e.encoder); + } +} diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs new file mode 100644 index 000000000..97edfc2fc --- /dev/null +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -0,0 +1,609 @@ +use crate::dep_graph; +use crate::dep_graph::DepKind; +use crate::query::on_disk_cache::CacheEncoder; +use crate::query::on_disk_cache::EncodedDepNodeIndex; +use crate::query::on_disk_cache::OnDiskCache; +use crate::query::{ + DynamicQueries, ExternProviders, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates, +}; +use crate::ty::TyCtxt; +use field_offset::FieldOffset; +use measureme::StringId; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::AtomicU64; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::hir_id::OwnerId; +use rustc_query_system::dep_graph::DepNodeIndex; +use rustc_query_system::dep_graph::SerializedDepNodeIndex; +pub(crate) use rustc_query_system::query::QueryJobId; +use rustc_query_system::query::*; +use rustc_query_system::HandleCycleError; +use rustc_span::{Span, DUMMY_SP}; +use std::ops::Deref; + +pub struct QueryKeyStringCache { + pub def_id_cache: FxHashMap, +} + +impl QueryKeyStringCache { + pub fn new() -> QueryKeyStringCache { + QueryKeyStringCache { def_id_cache: Default::default() } + } +} + +pub struct DynamicQuery<'tcx, C: QueryCache> { + pub name: &'static str, + pub eval_always: bool, + pub dep_kind: DepKind, + pub handle_cycle_error: HandleCycleError, + pub query_state: FieldOffset, QueryState>, + pub query_cache: FieldOffset, C>, + pub cache_on_disk: fn(tcx: TyCtxt<'tcx>, key: &C::Key) -> bool, + pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value, + pub compute: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, + pub can_load_from_disk: bool, + pub try_load_from_disk: fn( + tcx: TyCtxt<'tcx>, + key: &C::Key, + prev_index: SerializedDepNodeIndex, + index: DepNodeIndex, + ) -> Option, + pub loadable_from_disk: + fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool, + pub hash_result: HashResult, + pub value_from_cycle_error: fn(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo]) -> C::Value, + pub format_value: fn(&C::Value) -> String, +} + +pub struct QuerySystemFns<'tcx> { + pub engine: QueryEngine, + pub local_providers: Providers, + pub extern_providers: ExternProviders, + pub encode_query_results: fn( + tcx: TyCtxt<'tcx>, + encoder: &mut CacheEncoder<'_, 'tcx>, + query_result_index: &mut EncodedDepNodeIndex, + ), + pub try_mark_green: fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool, +} + +pub struct QuerySystem<'tcx> { + pub states: QueryStates<'tcx>, + pub arenas: QueryArenas<'tcx>, + pub caches: QueryCaches<'tcx>, + pub dynamic_queries: DynamicQueries<'tcx>, + + /// This provides access to the incremental compilation on-disk cache for query results. + /// Do not access this directly. It is only meant to be used by + /// `DepGraph::try_mark_green()` and the query infrastructure. + /// This is `None` if we are not incremental compilation mode + pub on_disk_cache: Option>, + + pub fns: QuerySystemFns<'tcx>, + + pub jobs: AtomicU64, +} + +#[derive(Copy, Clone)] +pub struct TyCtxtAt<'tcx> { + pub tcx: TyCtxt<'tcx>, + pub span: Span, +} + +impl<'tcx> Deref for TyCtxtAt<'tcx> { + type Target = TyCtxt<'tcx>; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.tcx + } +} + +#[derive(Copy, Clone)] +pub struct TyCtxtEnsure<'tcx> { + pub tcx: TyCtxt<'tcx>, +} + +#[derive(Copy, Clone)] +pub struct TyCtxtEnsureWithValue<'tcx> { + pub tcx: TyCtxt<'tcx>, +} + +impl<'tcx> TyCtxt<'tcx> { + /// Returns a transparent wrapper for `TyCtxt`, which ensures queries + /// are executed instead of just returning their results. + #[inline(always)] + pub fn ensure(self) -> TyCtxtEnsure<'tcx> { + TyCtxtEnsure { tcx: self } + } + + /// Returns a transparent wrapper for `TyCtxt`, which ensures queries + /// are executed instead of just returning their results. + /// + /// This version verifies that the computed result exists in the cache before returning. + #[inline(always)] + pub fn ensure_with_value(self) -> TyCtxtEnsureWithValue<'tcx> { + TyCtxtEnsureWithValue { tcx: self } + } + + /// Returns a transparent wrapper for `TyCtxt` which uses + /// `span` as the location of queries performed through it. + #[inline(always)] + pub fn at(self, span: Span) -> TyCtxtAt<'tcx> { + TyCtxtAt { tcx: self, span } + } + + pub fn try_mark_green(self, dep_node: &dep_graph::DepNode) -> bool { + (self.query_system.fns.try_mark_green)(self, dep_node) + } +} + +#[inline] +pub fn query_get_at<'tcx, Cache>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + span: Span, + key: Cache::Key, +) -> Cache::Value +where + Cache: QueryCache, +{ + let key = key.into_query_param(); + match try_get_cached(tcx, query_cache, &key) { + Some(value) => value, + None => execute_query(tcx, span, key, QueryMode::Get).unwrap(), + } +} + +#[inline] +pub fn query_ensure<'tcx, Cache>( + tcx: TyCtxt<'tcx>, + execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option, + query_cache: &Cache, + key: Cache::Key, + check_cache: bool, +) where + Cache: QueryCache, +{ + let key = key.into_query_param(); + if try_get_cached(tcx, query_cache, &key).is_none() { + execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); + } +} + +macro_rules! query_helper_param_ty { + (DefId) => { impl IntoQueryParam }; + (LocalDefId) => { impl IntoQueryParam }; + ($K:ty) => { $K }; +} + +macro_rules! query_if_arena { + ([] $arena:tt $no_arena:tt) => { + $no_arena + }; + ([(arena_cache) $($rest:tt)*] $arena:tt $no_arena:tt) => { + $arena + }; + ([$other:tt $($modifiers:tt)*]$($args:tt)*) => { + query_if_arena!([$($modifiers)*]$($args)*) + }; +} + +/// If `separate_provide_if_extern`, then the key can be projected to its +/// local key via `<$K as AsLocalKey>::LocalKey`. +macro_rules! local_key_if_separate_extern { + ([] $($K:tt)*) => { + $($K)* + }; + ([(separate_provide_extern) $($rest:tt)*] $($K:tt)*) => { + <$($K)* as AsLocalKey>::LocalKey + }; + ([$other:tt $($modifiers:tt)*] $($K:tt)*) => { + local_key_if_separate_extern!([$($modifiers)*] $($K)*) + }; +} + +macro_rules! separate_provide_extern_decl { + ([][$name:ident]) => { + () + }; + ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { + for<'tcx> fn( + TyCtxt<'tcx>, + queries::$name::Key<'tcx>, + ) -> queries::$name::ProvidedValue<'tcx> + }; + ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { + separate_provide_extern_decl!([$($modifiers)*][$($args)*]) + }; +} + +macro_rules! separate_provide_extern_default { + ([][$name:ident]) => { + () + }; + ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { + |_, key| bug!( + "`tcx.{}({:?})` unsupported by its crate; \ + perhaps the `{}` query was never assigned a provider function", + stringify!($name), + key, + stringify!($name), + ) + }; + ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { + separate_provide_extern_default!([$($modifiers)*][$($args)*]) + }; +} + +macro_rules! define_callbacks { + ( + $($(#[$attr:meta])* + [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { + + #[allow(unused_lifetimes)] + pub mod queries { + $(pub mod $name { + use super::super::*; + + pub type Key<'tcx> = $($K)*; + pub type Value<'tcx> = $V; + + pub type LocalKey<'tcx> = local_key_if_separate_extern!([$($modifiers)*] $($K)*); + + /// This type alias specifies the type returned from query providers and the type + /// used for decoding. For regular queries this is the declared returned type `V`, + /// but `arena_cache` will use `::Target` instead. + pub type ProvidedValue<'tcx> = query_if_arena!( + [$($modifiers)*] + (<$V as Deref>::Target) + ($V) + ); + + /// This function takes `ProvidedValue` and coverts it to an erased `Value` by + /// allocating it on an arena if the query has the `arena_cache` modifier. The + /// value is then erased and returned. This will happen when computing the query + /// using a provider or decoding a stored result. + #[inline(always)] + pub fn provided_to_erased<'tcx>( + _tcx: TyCtxt<'tcx>, + value: ProvidedValue<'tcx>, + ) -> Erase> { + erase(query_if_arena!([$($modifiers)*] + { + if mem::needs_drop::>() { + &*_tcx.query_system.arenas.$name.alloc(value) + } else { + &*_tcx.arena.dropless.alloc(value) + } + } + (value) + )) + } + + pub type Storage<'tcx> = < + <$($K)* as keys::Key>::CacheSelector as CacheSelector<'tcx, Erase<$V>> + >::Cache; + + // Ensure that keys grow no larger than 64 bytes + #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] + const _: () = { + if mem::size_of::>() > 64 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a key type `", + stringify!($($K)*), + "` that is too large" + )); + } + }; + + // Ensure that values grow no larger than 64 bytes + #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] + const _: () = { + if mem::size_of::>() > 64 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a value type `", + stringify!($V), + "` that is too large" + )); + } + }; + })* + } + + pub struct QueryArenas<'tcx> { + $($(#[$attr])* pub $name: query_if_arena!([$($modifiers)*] + (WorkerLocal::Target>>) + () + ),)* + } + + impl Default for QueryArenas<'_> { + fn default() -> Self { + Self { + $($name: query_if_arena!([$($modifiers)*] + (WorkerLocal::new(|_| Default::default())) + () + ),)* + } + } + } + + #[derive(Default)] + pub struct QueryCaches<'tcx> { + $($(#[$attr])* pub $name: queries::$name::Storage<'tcx>,)* + } + + impl<'tcx> TyCtxtEnsure<'tcx> { + $($(#[$attr])* + #[inline(always)] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) { + query_ensure( + self.tcx, + self.tcx.query_system.fns.engine.$name, + &self.tcx.query_system.caches.$name, + key.into_query_param(), + false, + ); + })* + } + + impl<'tcx> TyCtxtEnsureWithValue<'tcx> { + $($(#[$attr])* + #[inline(always)] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) { + query_ensure( + self.tcx, + self.tcx.query_system.fns.engine.$name, + &self.tcx.query_system.caches.$name, + key.into_query_param(), + true, + ); + })* + } + + impl<'tcx> TyCtxt<'tcx> { + $($(#[$attr])* + #[inline(always)] + #[must_use] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V + { + self.at(DUMMY_SP).$name(key) + })* + } + + impl<'tcx> TyCtxtAt<'tcx> { + $($(#[$attr])* + #[inline(always)] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V + { + restore::<$V>(query_get_at( + self.tcx, + self.tcx.query_system.fns.engine.$name, + &self.tcx.query_system.caches.$name, + self.span, + key.into_query_param(), + )) + })* + } + + pub struct DynamicQueries<'tcx> { + $( + pub $name: DynamicQuery<'tcx, queries::$name::Storage<'tcx>>, + )* + } + + #[derive(Default)] + pub struct QueryStates<'tcx> { + $( + pub $name: QueryState<$($K)*, DepKind>, + )* + } + + pub struct Providers { + $(pub $name: for<'tcx> fn( + TyCtxt<'tcx>, + queries::$name::LocalKey<'tcx>, + ) -> queries::$name::ProvidedValue<'tcx>,)* + } + + pub struct ExternProviders { + $(pub $name: separate_provide_extern_decl!([$($modifiers)*][$name]),)* + } + + impl Default for Providers { + fn default() -> Self { + Providers { + $($name: |_, key| bug!( + "`tcx.{}({:?})` is not supported for this key;\n\ + hint: Queries can be either made to the local crate, or the external crate. \ + This error means you tried to use it for one that's not supported.\n\ + If that's not the case, {} was likely never assigned to a provider function.\n", + stringify!($name), + key, + stringify!($name), + ),)* + } + } + } + + impl Default for ExternProviders { + fn default() -> Self { + ExternProviders { + $($name: separate_provide_extern_default!([$($modifiers)*][$name]),)* + } + } + } + + impl Copy for Providers {} + impl Clone for Providers { + fn clone(&self) -> Self { *self } + } + + impl Copy for ExternProviders {} + impl Clone for ExternProviders { + fn clone(&self) -> Self { *self } + } + + pub struct QueryEngine { + $(pub $name: for<'tcx> fn( + TyCtxt<'tcx>, + Span, + queries::$name::Key<'tcx>, + QueryMode, + ) -> Option>,)* + } + }; +} + +macro_rules! hash_result { + ([]) => {{ + Some(dep_graph::hash_result) + }}; + ([(no_hash) $($rest:tt)*]) => {{ + None + }}; + ([$other:tt $($modifiers:tt)*]) => { + hash_result!([$($modifiers)*]) + }; +} + +macro_rules! define_feedable { + ($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { + $(impl<'tcx, K: IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { + $(#[$attr])* + #[inline(always)] + pub fn $name(self, value: queries::$name::ProvidedValue<'tcx>) { + let key = self.key().into_query_param(); + + let tcx = self.tcx; + let erased = queries::$name::provided_to_erased(tcx, value); + let value = restore::<$V>(erased); + let cache = &tcx.query_system.caches.$name; + + let hasher: Option, &_) -> _> = hash_result!([$($modifiers)*]); + match try_get_cached(tcx, cache, &key) { + Some(old) => { + let old = restore::<$V>(old); + if let Some(hasher) = hasher { + let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx.with_stable_hashing_context(|mut hcx| + (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) + ); + if old_hash != value_hash { + // We have an inconsistency. This can happen if one of the two + // results is tainted by errors. In this case, delay a bug to + // ensure compilation is doomed, and keep the `old` value. + tcx.sess.delay_span_bug(DUMMY_SP, format!( + "Trying to feed an already recorded value for query {} key={key:?}:\n\ + old value: {old:?}\nnew value: {value:?}", + stringify!($name), + )); + } + } else { + // The query is `no_hash`, so we have no way to perform a sanity check. + // If feeding the same value multiple times needs to be supported, + // the query should not be marked `no_hash`. + bug!( + "Trying to feed an already recorded value for query {} key={key:?}:\nold value: {old:?}\nnew value: {value:?}", + stringify!($name), + ) + } + } + None => { + let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::DepKind::$name, &key); + let dep_node_index = tcx.dep_graph.with_feed_task( + dep_node, + tcx, + key, + &value, + hash_result!([$($modifiers)*]), + ); + cache.complete(key, erased, dep_node_index); + } + } + } + })* + } +} + +// Each of these queries corresponds to a function pointer field in the +// `Providers` struct for requesting a value of that type, and a method +// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way +// which memoizes and does dep-graph tracking, wrapping around the actual +// `Providers` that the driver creates (using several `rustc_*` crates). +// +// The result type of each query must implement `Clone`, and additionally +// `ty::query::values::Value`, which produces an appropriate placeholder +// (error) value if the query resulted in a query cycle. +// Queries marked with `fatal_cycle` do not need the latter implementation, +// as they will raise an fatal error on query cycles instead. + +mod sealed { + use super::{DefId, LocalDefId, OwnerId}; + + /// An analogue of the `Into` trait that's intended only for query parameters. + /// + /// This exists to allow queries to accept either `DefId` or `LocalDefId` while requiring that the + /// user call `to_def_id` to convert between them everywhere else. + pub trait IntoQueryParam

{ + fn into_query_param(self) -> P; + } + + impl

IntoQueryParam

for P { + #[inline(always)] + fn into_query_param(self) -> P { + self + } + } + + impl<'a, P: Copy> IntoQueryParam

for &'a P { + #[inline(always)] + fn into_query_param(self) -> P { + *self + } + } + + impl IntoQueryParam for OwnerId { + #[inline(always)] + fn into_query_param(self) -> LocalDefId { + self.def_id + } + } + + impl IntoQueryParam for LocalDefId { + #[inline(always)] + fn into_query_param(self) -> DefId { + self.to_def_id() + } + } + + impl IntoQueryParam for OwnerId { + #[inline(always)] + fn into_query_param(self) -> DefId { + self.to_def_id() + } + } +} + +pub use sealed::IntoQueryParam; + +impl<'tcx> TyCtxt<'tcx> { + pub fn def_kind(self, def_id: impl IntoQueryParam) -> DefKind { + let def_id = def_id.into_query_param(); + self.opt_def_kind(def_id) + .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id)) + } +} + +impl<'tcx> TyCtxtAt<'tcx> { + pub fn def_kind(self, def_id: impl IntoQueryParam) -> DefKind { + let def_id = def_id.into_query_param(); + self.opt_def_kind(def_id) + .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id)) + } +} -- cgit v1.2.3