Skip to content

Inject compiler_builtins during postprocessing rather than via AST #136226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 13 additions & 36 deletions compiler/rustc_builtin_macros/src/standard_library_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,12 @@ pub fn inject(
let edition = sess.psess.edition;

// the first name in this list is the crate name of the crate with the prelude
let names: &[Symbol] = if attr::contains_name(pre_configured_attrs, sym::no_core) {
let name: Symbol = if attr::contains_name(pre_configured_attrs, sym::no_core) {
return 0;
} else if attr::contains_name(pre_configured_attrs, sym::no_std) {
if attr::contains_name(pre_configured_attrs, sym::compiler_builtins) {
&[sym::core]
} else {
&[sym::core, sym::compiler_builtins]
}
sym::core
} else {
&[sym::std]
sym::std
};

let expn_id = resolver.expansion_for_ast_pass(
Expand All @@ -43,36 +39,16 @@ pub fn inject(
let ecfg = ExpansionConfig::default("std_lib_injection".to_string(), features);
let cx = ExtCtxt::new(sess, ecfg, resolver, None);

// .rev() to preserve ordering above in combination with insert(0, ...)
for &name in names.iter().rev() {
let ident_span = if edition >= Edition2018 { span } else { call_site };
let item = if name == sym::compiler_builtins {
// compiler_builtins is a private implementation detail. We only
// need to insert it into the crate graph for linking and should not
// expose any of its public API.
//
// FIXME(#113634) We should inject this during post-processing like
// we do for the panic runtime, profiler runtime, etc.
cx.item(
span,
Ident::new(kw::Underscore, ident_span),
thin_vec![],
ast::ItemKind::ExternCrate(Some(name)),
)
} else {
cx.item(
span,
Ident::new(name, ident_span),
thin_vec![cx.attr_word(sym::macro_use, span)],
ast::ItemKind::ExternCrate(None),
)
};
krate.items.insert(0, item);
}
let ident_span = if edition >= Edition2018 { span } else { call_site };

// The crates have been injected, the assumption is that the first one is
// the one with the prelude.
let name = names[0];
let item = cx.item(
span,
Ident::new(name, ident_span),
thin_vec![cx.attr_word(sym::macro_use, span)],
ast::ItemKind::ExternCrate(None),
);

krate.items.insert(0, item);

let root = (edition == Edition2015).then_some(kw::PathRoot);

Expand All @@ -88,6 +64,7 @@ pub fn inject(
.map(|&symbol| Ident::new(symbol, span))
.collect();

// Inject the relevant crate's prelude.
let use_item = cx.item(
span,
Ident::empty(),
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_metadata/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ metadata_crate_dep_rustc_driver =
metadata_crate_location_unknown_type =
extern location for {$crate_name} is of an unknown type: {$path}

metadata_crate_not_compiler_builtins =
the crate `{$crate_name}` resolved as `compiler_builtins` but is not `#![compiler_builtins]`

metadata_crate_not_panic_runtime =
the crate `{$crate_name}` is not a panic runtime

Expand Down
142 changes: 121 additions & 21 deletions compiler/rustc_metadata/src/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ impl<'a> std::fmt::Debug for CrateDump<'a> {
writeln!(fmt, " cnum: {cnum}")?;
writeln!(fmt, " hash: {}", data.hash())?;
writeln!(fmt, " reqd: {:?}", data.dep_kind())?;
writeln!(fmt, " priv: {:?}", data.is_private_dep())?;
let CrateSource { dylib, rlib, rmeta } = data.source();
if let Some(dylib) = dylib {
writeln!(fmt, " dylib: {}", dylib.0.display())?;
Expand All @@ -157,6 +158,43 @@ impl<'a> std::fmt::Debug for CrateDump<'a> {
}
}

/// Reason that a crate is being sourced as a dependency.
#[derive(Clone, Copy)]
enum CrateOrigin<'a> {
/// This crate was a dependency of another crate.
Dependency {
dep_root: &'a CratePaths,
/// Dependency info about this crate.
dep: &'a CrateDep,
},
/// Injected by `rustc`.
Injected,
/// An extern that has been provided with the `force` option.
ForcedExtern,
/// Part of the extern prelude.
ExternPrelude,
/// Provided by `extern crate foo`.
AstExtern,
}

impl<'a> CrateOrigin<'a> {
/// Return the dependency root, if any.
fn dep_root(&self) -> Option<&'a CratePaths> {
match self {
CrateOrigin::Dependency { dep_root, .. } => Some(dep_root),
_ => None,
}
}

/// Return dependency information, if any.
fn dep(&self) -> Option<&'a CrateDep> {
match self {
CrateOrigin::Dependency { dep, .. } => Some(dep),
_ => None,
}
}
}

impl CStore {
pub fn from_tcx(tcx: TyCtxt<'_>) -> FreezeReadGuard<'_, CStore> {
FreezeReadGuard::map(tcx.untracked().cstore.read(), |cstore| {
Expand Down Expand Up @@ -404,7 +442,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
&self,
name: Symbol,
private_dep: Option<bool>,
dep_root: Option<&CratePaths>,
origin: CrateOrigin<'_>,
) -> bool {
// Standard library crates are never private.
if STDLIB_STABLE_CRATES.contains(&name) {
Expand All @@ -414,10 +452,14 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {

let extern_private = self.sess.opts.externs.get(name.as_str()).map(|e| e.is_private_dep);

if matches!(origin, CrateOrigin::Injected) {
return true;
}

// Any descendants of `std` should be private. These crates are usually not marked
// private in metadata, so we ignore that field.
if extern_private.is_none()
&& let Some(dep) = dep_root
&& let Some(dep) = origin.dep_root()
&& STDLIB_STABLE_CRATES.contains(&dep.name)
{
return true;
Expand All @@ -435,7 +477,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
fn register_crate(
&mut self,
host_lib: Option<Library>,
dep_root: Option<&CratePaths>,
origin: CrateOrigin<'_>,
lib: Library,
dep_kind: CrateDepKind,
name: Symbol,
Expand All @@ -447,7 +489,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
let Library { source, metadata } = lib;
let crate_root = metadata.get_root();
let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash());
let private_dep = self.is_private_dep(name, private_dep, dep_root);
let private_dep = self.is_private_dep(name, private_dep, origin);

// Claim this crate number and cache it
let feed = self.cstore.intern_stable_crate_id(&crate_root, self.tcx)?;
Expand All @@ -463,7 +505,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
// Maintain a reference to the top most crate.
// Stash paths for top-most crate locally if necessary.
let crate_paths;
let dep_root = if let Some(dep_root) = dep_root {
let dep_root = if let Some(dep_root) = origin.dep_root() {
dep_root
} else {
crate_paths = CratePaths::new(crate_root.name(), source.clone());
Expand Down Expand Up @@ -571,17 +613,23 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
name: Symbol,
span: Span,
dep_kind: CrateDepKind,
origin: CrateOrigin<'_>,
) -> Option<CrateNum> {
self.used_extern_options.insert(name);
match self.maybe_resolve_crate(name, dep_kind, None) {
match self.maybe_resolve_crate(name, dep_kind, origin) {
Ok(cnum) => {
self.cstore.set_used_recursively(cnum);
Some(cnum)
}
Err(err) => {
debug!("failed to resolve crate {} {:?}", name, dep_kind);
let missing_core =
self.maybe_resolve_crate(sym::core, CrateDepKind::Explicit, None).is_err();
let missing_core = self
.maybe_resolve_crate(
sym::core,
CrateDepKind::Explicit,
CrateOrigin::ExternPrelude,
)
.is_err();
err.report(self.sess, span, missing_core);
None
}
Expand All @@ -592,15 +640,15 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
&'b mut self,
name: Symbol,
mut dep_kind: CrateDepKind,
dep_of: Option<(&'b CratePaths, &'b CrateDep)>,
origin: CrateOrigin<'b>,
) -> Result<CrateNum, CrateError> {
info!("resolving crate `{}`", name);
if !name.as_str().is_ascii() {
return Err(CrateError::NonAsciiName(name));
}

let dep_root = dep_of.map(|d| d.0);
let dep = dep_of.map(|d| d.1);
let dep_root = origin.dep_root();
let dep = origin.dep();
let hash = dep.map(|d| d.hash);
let host_hash = dep.map(|d| d.host_hash).flatten();
let extra_filename = dep.map(|d| &d.extra_filename[..]);
Expand Down Expand Up @@ -638,12 +686,12 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {

match result {
(LoadResult::Previous(cnum), None) => {
info!("library for `{}` was loaded previously", name);
info!("library for `{}` was loaded previously, cnum {cnum}", name);
// When `private_dep` is none, it indicates the directly dependent crate. If it is
// not specified by `--extern` on command line parameters, it may be
// `private-dependency` when `register_crate` is called for the first time. Then it must be updated to
// `public-dependency` here.
let private_dep = self.is_private_dep(name, private_dep, dep_root);
let private_dep = self.is_private_dep(name, private_dep, origin);
let data = self.cstore.get_crate_data_mut(cnum);
if data.is_proc_macro_crate() {
dep_kind = CrateDepKind::MacrosOnly;
Expand All @@ -654,7 +702,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
}
(LoadResult::Loaded(library), host_library) => {
info!("register newly loaded library for `{}`", name);
self.register_crate(host_library, dep_root, library, dep_kind, name, private_dep)
self.register_crate(host_library, origin, library, dep_kind, name, private_dep)
}
_ => panic!(),
}
Expand Down Expand Up @@ -730,7 +778,10 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
CrateDepKind::MacrosOnly => CrateDepKind::MacrosOnly,
_ => dep.kind,
};
let cnum = self.maybe_resolve_crate(dep.name, dep_kind, Some((dep_root, &dep)))?;
let cnum = self.maybe_resolve_crate(dep.name, dep_kind, CrateOrigin::Dependency {
dep_root,
dep: &dep,
})?;
crate_num_map.push(cnum);
}

Expand Down Expand Up @@ -824,7 +875,9 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
};
info!("panic runtime not found -- loading {}", name);

let Some(cnum) = self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit) else {
let Some(cnum) =
self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit, CrateOrigin::Injected)
else {
return;
};
let data = self.cstore.get_crate_data(cnum);
Expand Down Expand Up @@ -853,7 +906,9 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
info!("loading profiler");

let name = Symbol::intern(&self.sess.opts.unstable_opts.profiler_runtime);
let Some(cnum) = self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit) else {
let Some(cnum) =
self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit, CrateOrigin::Injected)
else {
return;
};
let data = self.cstore.get_crate_data(cnum);
Expand Down Expand Up @@ -966,12 +1021,54 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
if entry.force {
let name_interned = Symbol::intern(name);
if !self.used_extern_options.contains(&name_interned) {
self.resolve_crate(name_interned, DUMMY_SP, CrateDepKind::Explicit);
self.resolve_crate(
name_interned,
DUMMY_SP,
CrateDepKind::Explicit,
CrateOrigin::ForcedExtern,
);
}
}
}
}

/// Inject the `compiler_builtins` crate if it is not already in the graph.
fn inject_compiler_builtins(&mut self, krate: &ast::Crate) {
// `compiler_builtins` does not get extern builtins, nor do `#![no_core]` crates
if attr::contains_name(&krate.attrs, sym::compiler_builtins)
|| attr::contains_name(&krate.attrs, sym::no_core)
{
info!("`compiler_builtins` unneeded");
return;
}

// If a `#![compiler_builtins]` crate already exists, avoid injecting it twice. This is
// the common case since usually it appears as a dependency of `std` or `core`.
for (cnum, cmeta) in self.cstore.iter_crate_data() {
if cmeta.is_compiler_builtins() {
info!("`compiler_builtins` already exists (cnum = {cnum}); skipping injection");
return;
}
}

// Allow builtins to remain unresolved, which will just mean linker errors if the
// relevant symbols aren't otherwise provided.
let Ok(cnum) = self.maybe_resolve_crate(
sym::compiler_builtins,
CrateDepKind::Explicit,
CrateOrigin::Injected,
) else {
info!("`compiler_builtins` not resolved");
return;
};

// Sanity check that the loaded crate is `#![compiler_builtins]`
let cmeta = self.cstore.get_crate_data(cnum);
if !cmeta.is_compiler_builtins() {
self.dcx().emit_err(errors::CrateNotCompilerBuiltins { crate_name: cmeta.name() });
}
}

fn inject_dependency_if(
&mut self,
krate: CrateNum,
Expand Down Expand Up @@ -1081,6 +1178,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
}

pub fn postprocess(&mut self, krate: &ast::Crate) {
self.inject_compiler_builtins(krate);
self.inject_forced_externs();
self.inject_profiler_runtime();
self.inject_allocator_crate(krate);
Expand All @@ -1092,6 +1190,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
info!("{:?}", CrateDump(self.cstore));
}

/// Process an `extern crate foo` AST node.
pub fn process_extern_crate(
&mut self,
item: &ast::Item,
Expand All @@ -1117,7 +1216,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
CrateDepKind::Explicit
};

let cnum = self.resolve_crate(name, item.span, dep_kind)?;
let cnum = self.resolve_crate(name, item.span, dep_kind, CrateOrigin::AstExtern)?;

let path_len = definitions.def_path(def_id).data.len();
self.cstore.update_extern_crate(cnum, ExternCrate {
Expand All @@ -1133,7 +1232,8 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
}

pub fn process_path_extern(&mut self, name: Symbol, span: Span) -> Option<CrateNum> {
let cnum = self.resolve_crate(name, span, CrateDepKind::Explicit)?;
let cnum =
self.resolve_crate(name, span, CrateDepKind::Explicit, CrateOrigin::ExternPrelude)?;

self.cstore.update_extern_crate(cnum, ExternCrate {
src: ExternCrateSource::Path,
Expand All @@ -1147,7 +1247,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
}

pub fn maybe_process_path_extern(&mut self, name: Symbol) -> Option<CrateNum> {
self.maybe_resolve_crate(name, CrateDepKind::Explicit, None).ok()
self.maybe_resolve_crate(name, CrateDepKind::Explicit, CrateOrigin::ExternPrelude).ok()
}
}

Expand Down
Loading
Loading