Files
rust/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

711 lines
28 KiB
Rust
Raw Normal View History

use std::any::Any;
use std::mem;
use std::sync::Arc;
2025-07-31 11:00:40 +02:00
use rustc_hir::attrs::Deprecation;
use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE};
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::bug;
use rustc_middle::metadata::ModChild;
2020-03-29 17:19:48 +02:00
use rustc_middle::middle::exported_symbols::ExportedSymbol;
use rustc_middle::middle::stability::DeprecationEntry;
use rustc_middle::query::{ExternProviders, LocalCrate};
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::util::Providers;
use rustc_session::cstore::{CrateStore, ExternCrate};
use rustc_session::{Session, StableCrateId};
use rustc_span::hygiene::ExpnId;
use rustc_span::{Span, Symbol, kw};
use super::{Decodable, DecodeContext, DecodeIterator};
use crate::creader::{CStore, LoadedMacro};
use crate::rmeta::AttrFlags;
use crate::rmeta::table::IsDefault;
2019-02-08 20:50:17 +09:00
use crate::{foreign_modules, native_libs};
trait ProcessQueryValue<'tcx, T> {
fn process_decoded(self, _tcx: TyCtxt<'tcx>, _err: impl Fn() -> !) -> T;
}
2024-08-28 23:22:40 -04:00
impl<T> ProcessQueryValue<'_, T> for T {
#[inline(always)]
2024-08-28 23:22:40 -04:00
fn process_decoded(self, _tcx: TyCtxt<'_>, _err: impl Fn() -> !) -> T {
self
}
}
2024-08-28 23:22:40 -04:00
impl<'tcx, T> ProcessQueryValue<'tcx, ty::EarlyBinder<'tcx, T>> for T {
#[inline(always)]
fn process_decoded(self, _tcx: TyCtxt<'_>, _err: impl Fn() -> !) -> ty::EarlyBinder<'tcx, T> {
ty::EarlyBinder::bind(self)
}
}
impl<T> ProcessQueryValue<'_, T> for Option<T> {
#[inline(always)]
fn process_decoded(self, _tcx: TyCtxt<'_>, err: impl Fn() -> !) -> T {
if let Some(value) = self { value } else { err() }
}
}
impl<'tcx, T: ArenaAllocatable<'tcx>> ProcessQueryValue<'tcx, &'tcx T> for Option<T> {
#[inline(always)]
fn process_decoded(self, tcx: TyCtxt<'tcx>, err: impl Fn() -> !) -> &'tcx T {
if let Some(value) = self { tcx.arena.alloc(value) } else { err() }
}
}
impl<T, E> ProcessQueryValue<'_, Result<Option<T>, E>> for Option<T> {
#[inline(always)]
fn process_decoded(self, _tcx: TyCtxt<'_>, _err: impl Fn() -> !) -> Result<Option<T>, E> {
Ok(self)
}
}
impl<'a, 'tcx, T: Copy + Decodable<DecodeContext<'a, 'tcx>>> ProcessQueryValue<'tcx, &'tcx [T]>
for Option<DecodeIterator<'a, 'tcx, T>>
{
#[inline(always)]
fn process_decoded(self, tcx: TyCtxt<'tcx>, err: impl Fn() -> !) -> &'tcx [T] {
if let Some(iter) = self { tcx.arena.alloc_from_iter(iter) } else { err() }
}
}
impl<'a, 'tcx, T: Copy + Decodable<DecodeContext<'a, 'tcx>>>
ProcessQueryValue<'tcx, Option<&'tcx [T]>> for Option<DecodeIterator<'a, 'tcx, T>>
{
#[inline(always)]
fn process_decoded(self, tcx: TyCtxt<'tcx>, _err: impl Fn() -> !) -> Option<&'tcx [T]> {
if let Some(iter) = self { Some(&*tcx.arena.alloc_from_iter(iter)) } else { None }
}
}
impl ProcessQueryValue<'_, Option<DeprecationEntry>> for Option<Deprecation> {
#[inline(always)]
fn process_decoded(self, _tcx: TyCtxt<'_>, _err: impl Fn() -> !) -> Option<DeprecationEntry> {
self.map(DeprecationEntry::external)
}
}
2022-02-17 21:33:23 +01:00
macro_rules! provide_one {
($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => { table }) => {
2022-02-17 21:33:23 +01:00
provide_one! {
$tcx, $def_id, $other, $cdata, $name => {
$cdata
.root
.tables
.$name
.get($cdata, $def_id.index)
.map(|lazy| lazy.decode(($cdata, $tcx)))
.process_decoded($tcx, || panic!("{:?} does not have a {:?}", $def_id, stringify!($name)))
}
}
};
($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => { table_defaulted_array }) => {
provide_one! {
$tcx, $def_id, $other, $cdata, $name => {
let lazy = $cdata.root.tables.$name.get($cdata, $def_id.index);
2024-08-28 23:22:40 -04:00
let value = if lazy.is_default() {
&[] as &[_]
} else {
$tcx.arena.alloc_from_iter(lazy.decode(($cdata, $tcx)))
};
value.process_decoded($tcx, || panic!("{:?} does not have a {:?}", $def_id, stringify!($name)))
}
}
};
($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => { table_direct }) => {
provide_one! {
$tcx, $def_id, $other, $cdata, $name => {
// We don't decode `table_direct`, since it's not a Lazy, but an actual value
$cdata
.root
.tables
.$name
.get($cdata, $def_id.index)
.process_decoded($tcx, || panic!("{:?} does not have a {:?}", $def_id, stringify!($name)))
2022-02-17 21:33:23 +01:00
}
}
};
($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => $compute:block) => {
fn $name<'tcx>(
$tcx: TyCtxt<'tcx>,
2023-05-18 03:39:35 +02:00
def_id_arg: rustc_middle::query::queries::$name::Key<'tcx>,
) -> rustc_middle::query::queries::$name::ProvidedValue<'tcx> {
2022-02-17 21:33:23 +01:00
let _prof_timer =
$tcx.prof.generic_activity(concat!("metadata_decode_entry_", stringify!($name)));
#[allow(unused_variables)]
let ($def_id, $other) = def_id_arg.into_args();
assert!(!$def_id.is_local());
// External query providers call `crate_hash` in order to register a dependency
// on the crate metadata. The exception is `crate_hash` itself, which obviously
// doesn't need to do this (and can't, as it would cause a query cycle).
use rustc_middle::dep_graph::dep_kinds;
if dep_kinds::$name != dep_kinds::crate_hash && $tcx.dep_graph.is_fully_enabled() {
$tcx.ensure_ok().crate_hash($def_id.krate);
2022-02-17 21:33:23 +01:00
}
2023-09-09 16:02:11 +02:00
let cdata = rustc_data_structures::sync::FreezeReadGuard::map(CStore::from_tcx($tcx), |c| {
c.get_crate_data($def_id.krate).cdata
});
let $cdata = crate::creader::CrateMetadataRef {
cdata: &cdata,
cstore: &CStore::from_tcx($tcx),
};
2022-02-17 21:33:23 +01:00
$compute
}
};
}
macro_rules! provide {
($tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
2022-02-17 21:33:23 +01:00
$($name:ident => { $($compute:tt)* })*) => {
fn provide_extern(providers: &mut ExternProviders) {
2022-02-17 21:33:23 +01:00
$(provide_one! {
$tcx, $def_id, $other, $cdata, $name => { $($compute)* }
})*
*providers = ExternProviders {
$($name,)*
..*providers
};
}
}
}
// small trait to work around different signature queries all being defined via
// the macro above.
trait IntoArgs {
type Other;
fn into_args(self) -> (DefId, Self::Other);
}
impl IntoArgs for DefId {
type Other = ();
fn into_args(self) -> (DefId, ()) {
(self, ())
}
}
impl IntoArgs for CrateNum {
type Other = ();
fn into_args(self) -> (DefId, ()) {
(self.as_def_id(), ())
}
}
impl IntoArgs for (CrateNum, DefId) {
type Other = DefId;
fn into_args(self) -> (DefId, DefId) {
(self.0.as_def_id(), self.1)
}
}
2024-06-16 21:35:16 -04:00
impl<'tcx> IntoArgs for ty::InstanceKind<'tcx> {
type Other = ();
fn into_args(self) -> (DefId, ()) {
(self.def_id(), ())
}
}
impl IntoArgs for (CrateNum, SimplifiedType) {
type Other = SimplifiedType;
fn into_args(self) -> (DefId, SimplifiedType) {
(self.0.as_def_id(), self.1)
}
}
provide! { tcx, def_id, other, cdata,
2024-08-28 23:22:40 -04:00
explicit_item_bounds => { table_defaulted_array }
explicit_item_self_bounds => { table_defaulted_array }
2022-02-17 21:33:23 +01:00
explicit_predicates_of => { table }
generics_of => { table }
inferred_outlives_of => { table_defaulted_array }
explicit_super_predicates_of => { table_defaulted_array }
explicit_implied_predicates_of => { table_defaulted_array }
2022-02-17 21:33:23 +01:00
type_of => { table }
2024-08-28 23:22:40 -04:00
type_alias_is_lazy => { table_direct }
2022-02-17 21:33:23 +01:00
variances_of => { table }
fn_sig => { table }
codegen_fn_attrs => { table }
impl_trait_header => { table }
2022-02-17 21:33:23 +01:00
const_param_default => { table }
object_lifetime_default => { table }
2022-02-17 21:33:23 +01:00
thir_abstract_const => { table }
optimized_mir => { table }
mir_for_ctfe => { table }
trivial_const => { table }
closure_saved_names_of_captured_variables => { table }
2023-10-19 21:46:28 +00:00
mir_coroutine_witnesses => { table }
2022-02-17 21:33:23 +01:00
promoted_mir => { table }
def_span => { table }
def_ident_span => { table }
lookup_stability => { table }
lookup_const_stability => { table }
lookup_default_body_stability => { table }
2022-02-17 21:33:23 +01:00
lookup_deprecation_entry => { table }
2022-08-15 14:11:11 -05:00
params_in_repr => { table }
def_kind => { cdata.def_kind(def_id.index) }
2022-02-17 22:00:03 +01:00
impl_parent => { table }
defaultness => { table_direct }
constness => { table_direct }
const_conditions => { table }
explicit_implied_const_bounds => { table_defaulted_array }
2024-01-23 15:23:22 +00:00
coerce_unsized_info => {
Ok(cdata
.root
.tables
.coerce_unsized_info
.get(cdata, def_id.index)
.map(|lazy| lazy.decode((cdata, tcx)))
.process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) }
2022-02-18 18:53:47 +01:00
mir_const_qualif => { table }
2022-02-18 19:09:02 +01:00
rendered_const => { table }
rendered_precise_capturing_args => { table }
asyncness => { table_direct }
fn_arg_idents => { table }
2023-12-21 01:43:10 +00:00
coroutine_kind => { table_direct }
coroutine_for_closure => { table }
coroutine_by_move_body_def_id => { table }
eval_static_initializer => {
Ok(cdata
.root
.tables
.eval_static_initializer
.get(cdata, def_id.index)
.map(|lazy| lazy.decode((cdata, tcx)))
.unwrap_or_else(|| panic!("{def_id:?} does not have eval_static_initializer")))
}
2022-03-10 19:07:27 +01:00
trait_def => { table }
deduced_param_attrs => {
// FIXME: `deduced_param_attrs` has some sketchy encoding settings,
// where we don't encode unless we're optimizing, doing codegen,
// and not incremental (see `encoder.rs`). I don't think this is right!
cdata
.root
.tables
.deduced_param_attrs
.get(cdata, def_id.index)
.map(|lazy| {
&*tcx.arena.alloc_from_iter(lazy.decode((cdata, tcx)))
})
.unwrap_or_default()
}
2024-10-02 23:16:31 -04:00
opaque_ty_origin => { table }
assumed_wf_types_for_rpitit => { table }
2022-12-28 04:16:36 +00:00
collect_return_position_impl_trait_in_trait_tys => {
2022-09-23 00:56:55 +00:00
Ok(cdata
.root
.tables
.trait_impl_trait_tys
.get(cdata, def_id.index)
.map(|lazy| lazy.decode((cdata, tcx)))
.process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys")))
}
2022-02-17 21:33:23 +01:00
2025-07-13 02:52:03 +08:00
associated_types_for_impl_traits_in_trait_or_impl => { table }
visibility => { cdata.get_visibility(def_id.index) }
adt_def => { cdata.get_adt_def(def_id.index, tcx) }
2025-03-27 12:08:00 +00:00
adt_destructor => { table }
2025-03-27 12:46:04 +00:00
adt_async_destructor => { table }
associated_item_def_ids => {
tcx.arena.alloc_from_iter(cdata.get_associated_item_or_field_def_ids(def_id.index))
}
2022-08-04 22:03:16 +02:00
associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) }
inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
2024-10-17 01:14:01 +02:00
attrs_for_def => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) }
is_mir_available => { cdata.is_item_mir_available(def_id.index) }
is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) }
2024-08-28 23:22:40 -04:00
cross_crate_inlinable => { table_direct }
2017-06-14 22:49:07 -07:00
dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) }
is_private_dep => { cdata.private_dep }
is_panic_runtime => { cdata.root.panic_runtime }
is_compiler_builtins => { cdata.root.compiler_builtins }
has_global_allocator => { cdata.root.has_global_allocator }
has_alloc_error_handler => { cdata.root.has_alloc_error_handler }
has_panic_handler => { cdata.root.has_panic_handler }
is_profiler_runtime => { cdata.root.profiler_runtime }
required_panic_strategy => { cdata.root.required_panic_strategy }
panic_in_drop_strategy => { cdata.root.panic_in_drop_strategy }
extern_crate => { cdata.extern_crate.map(|c| &*tcx.arena.alloc(c)) }
is_no_builtins => { cdata.root.no_builtins }
symbol_mangling_version => { cdata.root.symbol_mangling_version }
specialization_enabled_in => { cdata.root.specialization_enabled_in }
reachable_non_generics => {
let reachable_non_generics = tcx
.exported_non_generic_symbols(cdata.cnum)
.iter()
.filter_map(|&(exported_symbol, export_info)| {
if let ExportedSymbol::NonGeneric(def_id) = exported_symbol {
Some((def_id, export_info))
} else {
None
}
})
.collect();
2020-03-27 20:26:20 +01:00
reachable_non_generics
}
native_libraries => { cdata.get_native_libraries(tcx.sess).collect() }
foreign_modules => { cdata.get_foreign_modules(tcx.sess).map(|m| (m.def_id, m)).collect() }
crate_hash => { cdata.root.header.hash }
crate_host_hash => { cdata.host_hash }
crate_name => { cdata.root.header.name }
num_extern_def_ids => { cdata.num_def_ids() }
extra_filename => { cdata.root.extra_filename.clone() }
traits => { tcx.arena.alloc_from_iter(cdata.get_traits()) }
trait_impls_in_crate => { tcx.arena.alloc_from_iter(cdata.get_trait_impls()) }
implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) }
crate_incoherent_impls => { cdata.get_incoherent_impls(tcx, other) }
dep_kind => { cdata.dep_kind }
module_children => {
tcx.arena.alloc_from_iter(cdata.get_module_children(def_id.index, tcx.sess))
}
2023-11-17 23:40:04 +00:00
lib_features => { cdata.get_lib_features() }
stability_implications => {
cdata.get_stability_implications(tcx).iter().copied().collect()
}
stripped_cfg_items => { cdata.get_stripped_cfg_items(cdata.cnum, tcx) }
intrinsic_raw => { cdata.get_intrinsic(def_id.index) }
2022-03-18 17:02:32 +01:00
defined_lang_items => { cdata.get_lang_items(tcx) }
2020-03-27 20:26:20 +01:00
diagnostic_items => { cdata.get_diagnostic_items() }
missing_lang_items => { cdata.get_missing_lang_items(tcx) }
missing_extern_crate_item => {
matches!(cdata.extern_crate, Some(extern_crate) if !extern_crate.is_direct())
}
used_crate_source => { Arc::clone(&cdata.source) }
debugger_visualizers => { cdata.get_debugger_visualizers() }
2017-09-20 20:42:49 +02:00
exportable_items => { tcx.arena.alloc_from_iter(cdata.get_exportable_items()) }
stable_order_of_exportable_impls => { tcx.arena.alloc(cdata.get_stable_order_of_exportable_impls().collect()) }
exported_non_generic_symbols => { cdata.exported_non_generic_symbols(tcx) }
exported_generic_symbols => { cdata.exported_generic_symbols(tcx) }
crate_extern_paths => { cdata.source().paths().cloned().collect() }
expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) }
default_field => { cdata.get_default_field(def_id.index) }
is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) }
doc_link_resolutions => { tcx.arena.alloc(cdata.get_doc_link_resolutions(def_id.index)) }
doc_link_traits_in_scope => {
tcx.arena.alloc_from_iter(cdata.get_doc_link_traits_in_scope(def_id.index))
}
2025-03-12 11:23:20 +00:00
anon_const_kind => { table }
}
pub(in crate::rmeta) fn provide(providers: &mut Providers) {
provide_cstore_hooks(providers);
providers.queries = rustc_middle::query::Providers {
2021-06-06 11:30:08 +02:00
allocator_kind: |tcx, ()| CStore::from_tcx(tcx).allocator_kind(),
alloc_error_handler_kind: |tcx, ()| CStore::from_tcx(tcx).alloc_error_handler_kind(),
2023-03-13 22:22:59 +00:00
is_private_dep: |_tcx, LocalCrate| false,
2022-07-12 13:52:35 -07:00
native_library: |tcx, id| {
tcx.native_libraries(id.krate)
.iter()
.filter(|lib| native_libs::relevant_lib(tcx.sess, lib))
rustc: Add a `#[wasm_import_module]` attribute This commit adds a new attribute to the Rust compiler specific to the wasm target (and no other targets). The `#[wasm_import_module]` attribute is used to specify the module that a name is imported from, and is used like so: #[wasm_import_module = "./foo.js"] extern { fn some_js_function(); } Here the import of the symbol `some_js_function` is tagged with the `./foo.js` module in the wasm output file. Wasm-the-format includes two fields on all imports, a module and a field. The field is the symbol name (`some_js_function` above) and the module has historically unconditionally been `"env"`. I'm not sure if this `"env"` convention has asm.js or LLVM roots, but regardless we'd like the ability to configure it! The proposed ES module integration with wasm (aka a wasm module is "just another ES module") requires that the import module of wasm imports is interpreted as an ES module import, meaning that you'll need to encode paths, NPM packages, etc. As a result, we'll need this to be something other than `"env"`! Unfortunately neither our version of LLVM nor LLD supports custom import modules (aka anything not `"env"`). My hope is that by the time LLVM 7 is released both will have support, but in the meantime this commit adds some primitive encoding/decoding of wasm files to the compiler. This way rustc postprocesses the wasm module that LLVM emits to ensure it's got all the imports we'd like to have in it. Eventually I'd ideally like to unconditionally require this attribute to be placed on all `extern { ... }` blocks. For now though it seemed prudent to add it as an unstable attribute, so for now it's not required (as that'd force usage of a feature gate). Hopefully it doesn't take too long to "stabilize" this! cc rust-lang-nursery/rust-wasm#29
2018-02-10 14:28:17 -08:00
.find(|lib| {
2022-02-19 00:48:49 +01:00
let Some(fm_id) = lib.foreign_module else {
return false;
rustc: Add a `#[wasm_import_module]` attribute This commit adds a new attribute to the Rust compiler specific to the wasm target (and no other targets). The `#[wasm_import_module]` attribute is used to specify the module that a name is imported from, and is used like so: #[wasm_import_module = "./foo.js"] extern { fn some_js_function(); } Here the import of the symbol `some_js_function` is tagged with the `./foo.js` module in the wasm output file. Wasm-the-format includes two fields on all imports, a module and a field. The field is the symbol name (`some_js_function` above) and the module has historically unconditionally been `"env"`. I'm not sure if this `"env"` convention has asm.js or LLVM roots, but regardless we'd like the ability to configure it! The proposed ES module integration with wasm (aka a wasm module is "just another ES module") requires that the import module of wasm imports is interpreted as an ES module import, meaning that you'll need to encode paths, NPM packages, etc. As a result, we'll need this to be something other than `"env"`! Unfortunately neither our version of LLVM nor LLD supports custom import modules (aka anything not `"env"`). My hope is that by the time LLVM 7 is released both will have support, but in the meantime this commit adds some primitive encoding/decoding of wasm files to the compiler. This way rustc postprocesses the wasm module that LLVM emits to ensure it's got all the imports we'd like to have in it. Eventually I'd ideally like to unconditionally require this attribute to be placed on all `extern { ... }` blocks. For now though it seemed prudent to add it as an unstable attribute, so for now it's not required (as that'd force usage of a feature gate). Hopefully it doesn't take too long to "stabilize" this! cc rust-lang-nursery/rust-wasm#29
2018-02-10 14:28:17 -08:00
};
2020-10-27 15:01:03 +01:00
let map = tcx.foreign_modules(id.krate);
map.get(&fm_id)
rustc: Add a `#[wasm_import_module]` attribute This commit adds a new attribute to the Rust compiler specific to the wasm target (and no other targets). The `#[wasm_import_module]` attribute is used to specify the module that a name is imported from, and is used like so: #[wasm_import_module = "./foo.js"] extern { fn some_js_function(); } Here the import of the symbol `some_js_function` is tagged with the `./foo.js` module in the wasm output file. Wasm-the-format includes two fields on all imports, a module and a field. The field is the symbol name (`some_js_function` above) and the module has historically unconditionally been `"env"`. I'm not sure if this `"env"` convention has asm.js or LLVM roots, but regardless we'd like the ability to configure it! The proposed ES module integration with wasm (aka a wasm module is "just another ES module") requires that the import module of wasm imports is interpreted as an ES module import, meaning that you'll need to encode paths, NPM packages, etc. As a result, we'll need this to be something other than `"env"`! Unfortunately neither our version of LLVM nor LLD supports custom import modules (aka anything not `"env"`). My hope is that by the time LLVM 7 is released both will have support, but in the meantime this commit adds some primitive encoding/decoding of wasm files to the compiler. This way rustc postprocesses the wasm module that LLVM emits to ensure it's got all the imports we'd like to have in it. Eventually I'd ideally like to unconditionally require this attribute to be placed on all `extern { ... }` blocks. For now though it seemed prudent to add it as an unstable attribute, so for now it's not required (as that'd force usage of a feature gate). Hopefully it doesn't take too long to "stabilize" this! cc rust-lang-nursery/rust-wasm#29
2018-02-10 14:28:17 -08:00
.expect("failed to find foreign module")
.foreign_items
.contains(&id)
})
},
2023-07-15 10:17:37 +00:00
native_libraries: native_libs::collect,
2023-07-15 10:09:36 +00:00
foreign_modules: foreign_modules::collect,
// Returns a map from a sufficiently visible external item (i.e., an
// external item that is visible from at least one local module) to a
// sufficiently visible parent (considering modules that re-export the
// external item to be parents).
2021-05-11 14:16:48 +02:00
visible_parent_map: |tcx, ()| {
use std::collections::hash_map::Entry;
use std::collections::vec_deque::VecDeque;
let mut visible_parent_map: DefIdMap<DefId> = Default::default();
// This is a secondary visible_parent_map, storing the DefId of
// parents that re-export the child as `_` or module parents
// which are `#[doc(hidden)]`. Since we prefer paths that don't
// do this, merge this map at the end, only if we're missing
// keys from the former.
// This is a rudimentary check that does not catch all cases,
// just the easiest.
let mut fallback_map: Vec<(DefId, DefId)> = Default::default();
// Issue 46112: We want the map to prefer the shortest
// paths when reporting the path to an item. Therefore we
// build up the map via a breadth-first search (BFS),
// which naturally yields minimal-length paths.
//
// Note that it needs to be a BFS over the whole forest of
// crates, not just each individual crate; otherwise you
// only get paths that are locally minimal with respect to
// whatever crate we happened to encounter first in this
// traversal, but not globally minimal across all crates.
let bfs_queue = &mut VecDeque::new();
for &cnum in tcx.crates(()) {
// Ignore crates without a corresponding local `extern crate` item.
if tcx.missing_extern_crate_item(cnum) {
continue;
}
bfs_queue.push_back(cnum.as_def_id());
}
let mut add_child = |bfs_queue: &mut VecDeque<_>, child: &ModChild, parent: DefId| {
if !child.vis.is_public() {
2021-09-17 09:13:16 +02:00
return;
}
if let Some(def_id) = child.res.opt_def_id() {
if child.ident.name == kw::Underscore {
fallback_map.push((def_id, parent));
return;
}
if tcx.is_doc_hidden(parent) {
fallback_map.push((def_id, parent));
return;
}
match visible_parent_map.entry(def_id) {
2021-09-17 09:13:16 +02:00
Entry::Occupied(mut entry) => {
// If `child` is defined in crate `cnum`, ensure
// that it is mapped to a parent in `cnum`.
if def_id.is_local() && entry.get().is_local() {
entry.insert(parent);
}
}
2021-09-17 09:13:16 +02:00
Entry::Vacant(entry) => {
entry.insert(parent);
if child.res.module_like_def_id().is_some() {
bfs_queue.push_back(def_id);
}
2021-09-17 09:13:16 +02:00
}
}
2021-09-17 09:13:16 +02:00
}
};
2021-09-17 09:13:16 +02:00
while let Some(def) = bfs_queue.pop_front() {
for child in tcx.module_children(def).iter() {
2021-09-17 09:13:16 +02:00
add_child(bfs_queue, child, def);
}
}
// Fill in any missing entries with the less preferable path.
// If this path re-exports the child as `_`, we still use this
// path in a diagnostic that suggests importing `::*`.
for (child, parent) in fallback_map {
visible_parent_map.entry(child).or_insert(parent);
}
visible_parent_map
},
dependency_formats: |tcx, ()| Arc::new(crate::dependency_format::calculate(tcx)),
2023-03-13 22:22:59 +00:00
has_global_allocator: |tcx, LocalCrate| CStore::from_tcx(tcx).has_global_allocator(),
has_alloc_error_handler: |tcx, LocalCrate| CStore::from_tcx(tcx).has_alloc_error_handler(),
2021-05-11 14:50:54 +02:00
postorder_cnums: |tcx, ()| {
tcx.arena.alloc_from_iter(
CStore::from_tcx(tcx).crate_dependencies_in_postorder(LOCAL_CRATE).into_iter(),
)
},
crates: |tcx, ()| {
// The list of loaded crates is now frozen in query cache,
// so make sure cstore is not mutably accessed from here on.
2023-09-09 16:02:11 +02:00
tcx.untracked().cstore.freeze();
tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum))
},
used_crates: |tcx, ()| {
// The list of loaded crates is now frozen in query cache,
// so make sure cstore is not mutably accessed from here on.
tcx.untracked().cstore.freeze();
tcx.arena.alloc_from_iter(
CStore::from_tcx(tcx)
.iter_crate_data()
.filter_map(|(cnum, data)| data.used().then_some(cnum)),
)
},
..providers.queries
2017-06-11 21:16:26 -07:00
};
provide_extern(&mut providers.extern_queries);
}
impl CStore {
pub fn ctor_untracked(&self, def: DefId) -> Option<(CtorKind, DefId)> {
self.get_crate_data(def.krate).get_ctor(def.index)
}
pub fn load_macro_untracked(&self, id: DefId, tcx: TyCtxt<'_>) -> LoadedMacro {
let sess = tcx.sess;
let _prof_timer = sess.prof.generic_activity("metadata_load_macro");
let data = self.get_crate_data(id.krate);
if data.root.is_proc_macro_crate() {
LoadedMacro::ProcMacro(data.load_proc_macro(id.index, tcx))
} else {
LoadedMacro::MacroDef {
def: data.get_macro(id.index, sess),
ident: data.item_ident(id.index, sess),
attrs: data.get_item_attrs(id.index, sess).collect(),
span: data.get_span(id.index, sess),
edition: data.root.edition,
}
}
2016-10-28 06:52:45 +00:00
}
pub fn def_span_untracked(&self, def_id: DefId, sess: &Session) -> Span {
self.get_crate_data(def_id.krate).get_span(def_id.index, sess)
}
pub fn def_kind_untracked(&self, def: DefId) -> DefKind {
self.get_crate_data(def.krate).def_kind(def.index)
}
pub fn expn_that_defined_untracked(&self, def_id: DefId, sess: &Session) -> ExpnId {
self.get_crate_data(def_id.krate).get_expn_that_defined(def_id.index, sess)
2020-03-17 11:45:02 -04:00
}
2020-12-02 23:39:05 +03:00
/// Only public-facing way to traverse all the definitions in a non-local crate.
/// Critically useful for this third-party project: <https://github.com/hacspec/hacspec>.
/// See <https://github.com/rust-lang/rust/pull/85889> for context.
pub fn num_def_ids_untracked(&self, cnum: CrateNum) -> usize {
self.get_crate_data(cnum).num_def_ids()
}
Implement span quoting for proc-macros This PR implements span quoting, allowing proc-macros to produce spans pointing *into their own crate*. This is used by the unstable `proc_macro::quote!` macro, allowing us to get error messages like this: ``` error[E0412]: cannot find type `MissingType` in this scope --> $DIR/auxiliary/span-from-proc-macro.rs:37:20 | LL | pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> TokenStream { | ----------------------------------------------------------------------------------- in this expansion of procedural macro `#[error_from_attribute]` ... LL | field: MissingType | ^^^^^^^^^^^ not found in this scope | ::: $DIR/span-from-proc-macro.rs:8:1 | LL | #[error_from_attribute] | ----------------------- in this macro invocation ``` Here, `MissingType` occurs inside the implementation of the proc-macro `#[error_from_attribute]`. Previosuly, this would always result in a span pointing at `#[error_from_attribute]` This will make many proc-macro-related error message much more useful - when a proc-macro generates code containing an error, users will get an error message pointing directly at that code (within the macro definition), instead of always getting a span pointing at the macro invocation site. This is implemented as follows: * When a proc-macro crate is being *compiled*, it causes the `quote!` macro to get run. This saves all of the sapns in the input to `quote!` into the metadata of *the proc-macro-crate* (which we are currently compiling). The `quote!` macro then expands to a call to `proc_macro::Span::recover_proc_macro_span(id)`, where `id` is an opaque identifier for the span in the crate metadata. * When the same proc-macro crate is *run* (e.g. it is loaded from disk and invoked by some consumer crate), the call to `proc_macro::Span::recover_proc_macro_span` causes us to load the span from the proc-macro crate's metadata. The proc-macro then produces a `TokenStream` containing a `Span` pointing into the proc-macro crate itself. The recursive nature of 'quote!' can be difficult to understand at first. The file `src/test/ui/proc-macro/quote-debug.stdout` shows the output of the `quote!` macro, which should make this eaier to understand. This PR also supports custom quoting spans in custom quote macros (e.g. the `quote` crate). All span quoting goes through the `proc_macro::quote_span` method, which can be called by a custom quote macro to perform span quoting. An example of this usage is provided in `src/test/ui/proc-macro/auxiliary/custom-quote.rs` Custom quoting currently has a few limitations: In order to quote a span, we need to generate a call to `proc_macro::Span::recover_proc_macro_span`. However, proc-macros support renaming the `proc_macro` crate, so we can't simply hardcode this path. Previously, the `quote_span` method used the path `crate::Span` - however, this only works when it is called by the builtin `quote!` macro in the same crate. To support being called from arbitrary crates, we need access to the name of the `proc_macro` crate to generate a path. This PR adds an additional argument to `quote_span` to specify the name of the `proc_macro` crate. Howver, this feels kind of hacky, and we may want to change this before stabilizing anything quote-related. Additionally, using `quote_span` currently requires enabling the `proc_macro_internals` feature. The builtin `quote!` macro has an `#[allow_internal_unstable]` attribute, but this won't work for custom quote implementations. This will likely require some additional tricks to apply `allow_internal_unstable` to the span of `proc_macro::Span::recover_proc_macro_span`.
2020-08-02 19:52:16 -04:00
pub fn get_proc_macro_quoted_span_untracked(
&self,
cnum: CrateNum,
id: usize,
sess: &Session,
) -> Span {
self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess)
}
pub fn set_used_recursively(&mut self, cnum: CrateNum) {
let cmeta = self.get_crate_data_mut(cnum);
if !cmeta.used {
cmeta.used = true;
let dependencies = mem::take(&mut cmeta.dependencies);
for &dep_cnum in &dependencies {
self.set_used_recursively(dep_cnum);
}
self.get_crate_data_mut(cnum).dependencies = dependencies;
}
}
2025-07-03 13:41:54 +01:00
/// Track how an extern crate has been loaded. Called after resolving an import in the local crate.
///
/// * the `name` is for [`Self::set_resolved_extern_crate_name`] saving `--extern name=`
/// * `extern_crate` is for diagnostics
2025-07-03 13:41:54 +01:00
pub(crate) fn update_extern_crate(
&mut self,
cnum: CrateNum,
name: Symbol,
2025-07-03 13:41:54 +01:00
extern_crate: ExternCrate,
) {
debug_assert_eq!(
extern_crate.dependency_of, LOCAL_CRATE,
"this function should not be called on transitive dependencies"
);
self.set_resolved_extern_crate_name(name, cnum);
2025-07-03 13:41:54 +01:00
self.update_transitive_extern_crate_diagnostics(cnum, extern_crate);
}
/// `CrateMetadata` uses `ExternCrate` only for diagnostics
fn update_transitive_extern_crate_diagnostics(
&mut self,
cnum: CrateNum,
extern_crate: ExternCrate,
) {
let cmeta = self.get_crate_data_mut(cnum);
2025-07-03 13:41:54 +01:00
if cmeta.update_extern_crate_diagnostics(extern_crate) {
// Propagate the extern crate info to dependencies if it was updated.
let extern_crate = ExternCrate { dependency_of: cnum, ..extern_crate };
let dependencies = mem::take(&mut cmeta.dependencies);
for &dep_cnum in &dependencies {
2025-07-03 13:41:54 +01:00
self.update_transitive_extern_crate_diagnostics(dep_cnum, extern_crate);
}
self.get_crate_data_mut(cnum).dependencies = dependencies;
}
}
}
impl CrateStore for CStore {
fn as_any(&self) -> &dyn Any {
self
}
fn untracked_as_any(&mut self) -> &mut dyn Any {
self
}
fn crate_name(&self, cnum: CrateNum) -> Symbol {
self.get_crate_data(cnum).root.header.name
}
fn stable_crate_id(&self, cnum: CrateNum) -> StableCrateId {
self.get_crate_data(cnum).root.stable_crate_id
}
/// Returns the `DefKey` for a given `DefId`. This indicates the
/// parent `DefId` as well as some idea of what kind of data the
/// `DefId` refers to.
fn def_key(&self, def: DefId) -> DefKey {
self.get_crate_data(def.krate).def_key(def.index)
}
fn def_path(&self, def: DefId) -> DefPath {
self.get_crate_data(def.krate).def_path(def.index)
}
fn def_path_hash(&self, def: DefId) -> DefPathHash {
self.get_crate_data(def.krate).def_path_hash(def.index)
}
}
fn provide_cstore_hooks(providers: &mut Providers) {
2024-03-26 15:53:05 +00:00
providers.hooks.def_path_hash_to_def_id_extern = |tcx, hash, stable_crate_id| {
// If this is a DefPathHash from an upstream crate, let the CrateStore map
// it to a DefId.
let cstore = CStore::from_tcx(tcx);
let cnum = *tcx
.untracked()
.stable_crate_ids
.read()
.get(&stable_crate_id)
.unwrap_or_else(|| bug!("uninterned StableCrateId: {stable_crate_id:?}"));
assert_ne!(cnum, LOCAL_CRATE);
let def_index = cstore.get_crate_data(cnum).def_path_hash_to_def_index(hash)?;
Some(DefId { krate: cnum, index: def_index })
2024-03-26 15:53:05 +00:00
};
providers.hooks.expn_hash_to_expn_id = |tcx, cnum, index_guess, hash| {
let cstore = CStore::from_tcx(tcx);
cstore.get_crate_data(cnum).expn_hash_to_expn_id(tcx.sess, index_guess, hash)
};
providers.hooks.import_source_files = |tcx, cnum| {
let cstore = CStore::from_tcx(tcx);
let cdata = cstore.get_crate_data(cnum);
2022-08-06 23:00:49 +02:00
for file_index in 0..cdata.root.source_map.size() {
cdata.imported_source_file(file_index as u32, tcx.sess);
2022-08-06 23:00:49 +02:00
}
};
}