Skip to content

Commit

Permalink
Auto merge of rust-lang#136226 - tgross35:builtins-is-private-dep, r=…
Browse files Browse the repository at this point in the history
…<try>

Inject `compiler_builtins` during postprocessing rather than via AST

`compiler_builtins` is currently injected into the AST as `extern crate compiler_builtins as _`. This has made gating via diagnostics difficult because it appears in the crate graph as a non-private dependency, and there isn't an easy way to differentiate between the injected AST and user-specified `extern crate compiler_builtins`.

Resolve this by injecting `compiler_builtins` during postprocessing rather than early in the AST. Most of the time this isn't even needed because it shows up in `std` or `core`'s crate graph, but injection is still needed to ensure `#![no_core]` works correctly.

A similar change was attempted at [1] but this encountered errors building `proc_macro` and `rustc-std-workspace-std`. Similar failures showed up while working on this patch, which were traced back to `compiler_builtins` showing up in the graph twice (once via dependency and once via injection). This is resolved by not injecting if a `#![compiler_builtins]` crate already exists.

This PR is a smaller subset of changes, split from rust-lang#135501. rust-lang#135501 builds on this to resolve a couple remaining privacy issues and actually do the update that has been troublesome.

[1]: rust-lang#113634

try-job: aarch64-gnu
try-job: dist-x86_64-linux
try-job: i686-mingw
try-job: x86_64-gnu-distcheck
try-job: x86_64-msvc-1
try-job: x86_64-rust-for-linux
try-job: test-various
  • Loading branch information
bors committed Jan 30, 2025
2 parents e6f12c8 + 0112499 commit 9bd0b76
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 64 deletions.
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

0 comments on commit 9bd0b76

Please sign in to comment.