about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTrevor Gross <tmgross@umich.edu>2025-01-29 23:54:15 +0000
committerTrevor Gross <tmgross@umich.edu>2025-02-21 17:37:03 +0000
commit1412cfc987b2b6691367c2e7428c083bb207d722 (patch)
treeb0aeede5a9d9e35ce96464876a36acf2e589ddb4
parent83d70c029860e619f8cb91f15bfc34f7f2d7965f (diff)
downloadrust-1412cfc987b2b6691367c2e7428c083bb207d722.tar.gz
rust-1412cfc987b2b6691367c2e7428c083bb207d722.zip
Inject `compiler_builtins` during postprocessing rather than via AST
`compiler_builtins` is currently injected 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.

[1]: https://github.com/rust-lang/rust/pull/113634
-rw-r--r--compiler/rustc_builtin_macros/src/standard_library_imports.rs49
-rw-r--r--compiler/rustc_metadata/messages.ftl3
-rw-r--r--compiler/rustc_metadata/src/creader.rs38
-rw-r--r--compiler/rustc_metadata/src/errors.rs6
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs4
-rw-r--r--tests/ui/extern-flag/empty-extern-arg.stderr8
-rw-r--r--tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs3
-rw-r--r--tests/ui/proc-macro/issue-59191-replace-root-with-fn.stderr13
-rw-r--r--tests/ui/proc-macro/meta-macro-hygiene.stdout1
-rw-r--r--tests/ui/proc-macro/nonterminal-token-hygiene.stdout1
10 files changed, 73 insertions, 53 deletions
diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
index 1a3f4d2d449..6933ca09349 100644
--- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs
+++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
@@ -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(
@@ -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);
 
@@ -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(),
diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl
index 6fc84b06647..df0a25712cc 100644
--- a/compiler/rustc_metadata/messages.ftl
+++ b/compiler/rustc_metadata/messages.ftl
@@ -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
 
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index 582572586cf..a935bc3662d 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -1118,6 +1118,43 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
         }
     }
 
+    /// 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 `alloc`.
+        for (cnum, cmeta) in self.cstore.iter_crate_data() {
+            if cmeta.is_compiler_builtins() {
+                info!("`compiler_builtins` already exists (cnum = {cnum}); skipping injection");
+                return;
+            }
+        }
+
+        // `compiler_builtins` is not yet in the graph; inject it. Error on resolution failure.
+        let Some(cnum) = self.resolve_crate(
+            sym::compiler_builtins,
+            krate.spans.inner_span.shrink_to_lo(),
+            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,
@@ -1227,6 +1264,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);
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index a77f9bc623b..2ad6389c0b4 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -333,6 +333,12 @@ pub struct CrateNotPanicRuntime {
 }
 
 #[derive(Diagnostic)]
+#[diag(metadata_crate_not_compiler_builtins)]
+pub struct CrateNotCompilerBuiltins {
+    pub crate_name: Symbol,
+}
+
+#[derive(Diagnostic)]
 #[diag(metadata_no_panic_strategy)]
 pub struct NoPanicStrategy {
     pub crate_name: Symbol,
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 0516179f62c..ef0267eb3d9 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1948,6 +1948,10 @@ impl CrateMetadata {
         self.root.profiler_runtime
     }
 
+    pub(crate) fn is_compiler_builtins(&self) -> bool {
+        self.root.compiler_builtins
+    }
+
     pub(crate) fn needs_allocator(&self) -> bool {
         self.root.needs_allocator
     }
diff --git a/tests/ui/extern-flag/empty-extern-arg.stderr b/tests/ui/extern-flag/empty-extern-arg.stderr
index b9a128e02e1..79efcc5d8b0 100644
--- a/tests/ui/extern-flag/empty-extern-arg.stderr
+++ b/tests/ui/extern-flag/empty-extern-arg.stderr
@@ -7,11 +7,5 @@ error: unwinding panics are not supported without std
    = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding
    = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem
 
-error: requires `sized` lang_item
-  --> $DIR/empty-extern-arg.rs:6:11
-   |
-LL | fn main() {}
-   |           ^^
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs b/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs
index c1b55fd99df..6afafb7114a 100644
--- a/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs
+++ b/tests/ui/proc-macro/issue-59191-replace-root-with-fn.rs
@@ -3,7 +3,8 @@
 
 //@ edition:2018
 //@ proc-macro: issue-59191.rs
-//@ error-pattern: requires `sized` lang_item
+//@ needs-unwind (affects error output)
+//@ error-pattern: error: `#[panic_handler]` function required
 
 #![feature(custom_inner_attributes)]
 #![issue_59191::no_main]
diff --git a/tests/ui/proc-macro/issue-59191-replace-root-with-fn.stderr b/tests/ui/proc-macro/issue-59191-replace-root-with-fn.stderr
index 2d0c92ff297..3cd98d9c72b 100644
--- a/tests/ui/proc-macro/issue-59191-replace-root-with-fn.stderr
+++ b/tests/ui/proc-macro/issue-59191-replace-root-with-fn.stderr
@@ -1,10 +1,9 @@
-error: requires `sized` lang_item
-  --> $DIR/issue-59191-replace-root-with-fn.rs:9:1
-   |
-LL | #![issue_59191::no_main]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
+error: `#[panic_handler]` function required, but not found
+
+error: unwinding panics are not supported without std
    |
-   = note: this error originates in the attribute macro `issue_59191::no_main` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding
+   = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem
 
-error: aborting due to 1 previous error
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/proc-macro/meta-macro-hygiene.stdout b/tests/ui/proc-macro/meta-macro-hygiene.stdout
index fae8446515a..1ee7179e84c 100644
--- a/tests/ui/proc-macro/meta-macro-hygiene.stdout
+++ b/tests/ui/proc-macro/meta-macro-hygiene.stdout
@@ -20,7 +20,6 @@ Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro
 use core /* 0#1 */::prelude /* 0#1 */::rust_2018 /* 0#1 */::*;
 #[macro_use /* 0#1 */]
 extern crate core /* 0#1 */;
-extern crate compiler_builtins /* NNN */ as _ /* 0#1 */;
 // Don't load unnecessary hygiene information from std
 extern crate std /* 0#0 */;
 
diff --git a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout
index e7dda7d3c16..c80a33206fb 100644
--- a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout
+++ b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout
@@ -39,7 +39,6 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
 use ::core /* 0#1 */::prelude /* 0#1 */::rust_2015 /* 0#1 */::*;
 #[macro_use /* 0#1 */]
 extern crate core /* 0#2 */;
-extern crate compiler_builtins /* NNN */ as _ /* 0#2 */;
 // Don't load unnecessary hygiene information from std
 extern crate std /* 0#0 */;