about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_interface/src/tests.rs1
-rw-r--r--compiler/rustc_metadata/src/creader.rs14
-rw-r--r--compiler/rustc_session/src/config.rs18
-rw-r--r--tests/ui/extern-flag/auxiliary/panic_handler.rs17
-rw-r--r--tests/ui/extern-flag/force-extern.rs9
-rw-r--r--tests/ui/extern-flag/no-force-extern.rs10
-rw-r--r--tests/ui/extern-flag/redundant-force-extern.rs11
7 files changed, 78 insertions, 2 deletions
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index a0c576234f9..854d974d87f 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -69,6 +69,7 @@ where
         is_private_dep: false,
         add_prelude: true,
         nounused_dep: false,
+        force: false,
     }
 }
 
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index 01b69966ca9..e6e7d25773e 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -865,6 +865,17 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
         }
     }
 
+    fn inject_forced_externs(&mut self) {
+        for (name, entry) in self.sess.opts.externs.iter() {
+            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);
+                }
+            }
+        }
+    }
+
     fn inject_dependency_if(
         &self,
         krate: CrateNum,
@@ -913,7 +924,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
                 // Don't worry about pathless `--extern foo` sysroot references
                 continue;
             }
-            if entry.nounused_dep {
+            if entry.nounused_dep || entry.force {
                 // We're not worried about this one
                 continue;
             }
@@ -942,6 +953,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
     }
 
     pub fn postprocess(&mut self, krate: &ast::Crate) {
+        self.inject_forced_externs();
         self.inject_profiler_runtime(krate);
         self.inject_allocator_crate(krate);
         self.inject_panic_runtime(krate);
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 18917120256..d80cc0aa043 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -518,6 +518,12 @@ pub struct ExternEntry {
     /// `--extern nounused:std=/path/to/lib/libstd.rlib`. This is used to
     /// suppress `unused-crate-dependencies` warnings.
     pub nounused_dep: bool,
+    /// If the extern entry is not referenced in the crate, force it to be resolved anyway.
+    ///
+    /// Allows a dependency satisfying, for instance, a missing panic handler to be injected
+    /// without modifying source:
+    /// `--extern force:extras=/path/to/lib/libstd.rlib`
+    pub force: bool,
 }
 
 #[derive(Clone, Debug)]
@@ -556,7 +562,13 @@ impl Externs {
 
 impl ExternEntry {
     fn new(location: ExternLocation) -> ExternEntry {
-        ExternEntry { location, is_private_dep: false, add_prelude: false, nounused_dep: false }
+        ExternEntry {
+            location,
+            is_private_dep: false,
+            add_prelude: false,
+            nounused_dep: false,
+            force: false,
+        }
     }
 
     pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> {
@@ -2261,6 +2273,7 @@ pub fn parse_externs(
         let mut is_private_dep = false;
         let mut add_prelude = true;
         let mut nounused_dep = false;
+        let mut force = false;
         if let Some(opts) = options {
             if !is_unstable_enabled {
                 early_error(
@@ -2283,6 +2296,7 @@ pub fn parse_externs(
                         }
                     }
                     "nounused" => nounused_dep = true,
+                    "force" => force = true,
                     _ => early_error(error_format, &format!("unknown --extern option `{opt}`")),
                 }
             }
@@ -2293,6 +2307,8 @@ pub fn parse_externs(
         entry.is_private_dep |= is_private_dep;
         // likewise `nounused`
         entry.nounused_dep |= nounused_dep;
+        // and `force`
+        entry.force |= force;
         // If any flag is missing `noprelude`, then add to the prelude.
         entry.add_prelude |= add_prelude;
     }
diff --git a/tests/ui/extern-flag/auxiliary/panic_handler.rs b/tests/ui/extern-flag/auxiliary/panic_handler.rs
new file mode 100644
index 00000000000..a625761a838
--- /dev/null
+++ b/tests/ui/extern-flag/auxiliary/panic_handler.rs
@@ -0,0 +1,17 @@
+#![feature(lang_items)]
+#![no_std]
+
+// Since `rustc` generally passes `-nodefaultlibs` to the linker,
+// Rust programs link necessary system libraries via `#[link()]`
+// attributes in the `libc` crate. `libc` is a dependency of `std`,
+// but as we are `#![no_std]`, we need to include it manually.
+#![feature(rustc_private)]
+extern crate libc;
+
+#[panic_handler]
+pub fn begin_panic_handler(_info: &core::panic::PanicInfo<'_>) -> ! {
+    loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
diff --git a/tests/ui/extern-flag/force-extern.rs b/tests/ui/extern-flag/force-extern.rs
new file mode 100644
index 00000000000..f56b5378223
--- /dev/null
+++ b/tests/ui/extern-flag/force-extern.rs
@@ -0,0 +1,9 @@
+// check-pass
+// ignore-cross-compile (needs dylibs and compiletest doesn't have a more specific header)
+// aux-crate:force:panic_handler=panic_handler.rs
+// compile-flags: -Zunstable-options --crate-type dylib
+// edition:2018
+
+#![no_std]
+
+fn foo() {}
diff --git a/tests/ui/extern-flag/no-force-extern.rs b/tests/ui/extern-flag/no-force-extern.rs
new file mode 100644
index 00000000000..ce9cbfe1cd2
--- /dev/null
+++ b/tests/ui/extern-flag/no-force-extern.rs
@@ -0,0 +1,10 @@
+// aux-crate:panic_handler=panic_handler.rs
+// ignore-cross-compile (needs dylibs and compiletest doesn't have a more specific header)
+// compile_flags: -Zunstable-options --crate-type dylib
+// error-pattern: `#[panic_handler]` function required, but not found
+// dont-check-compiler-stderr
+// edition: 2018
+
+#![no_std]
+
+fn foo() {}
diff --git a/tests/ui/extern-flag/redundant-force-extern.rs b/tests/ui/extern-flag/redundant-force-extern.rs
new file mode 100644
index 00000000000..a4091616dd5
--- /dev/null
+++ b/tests/ui/extern-flag/redundant-force-extern.rs
@@ -0,0 +1,11 @@
+// check-pass
+// ignore-cross-compile (needs dylibs and compiletest doesn't have a more specific header)
+// aux-crate:force:panic_handler=panic_handler.rs
+// compile-flags: -Zunstable-options --crate-type dylib
+// edition:2018
+
+#![no_std]
+
+extern crate panic_handler;
+
+fn foo() {}