about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_gcc/src/consts.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs9
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs2
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs2
-rw-r--r--compiler/rustc_passes/src/check_attr.rs34
-rw-r--r--compiler/rustc_passes/src/dead.rs5
-rw-r--r--compiler/rustc_span/src/symbol.rs3
-rw-r--r--compiler/rustc_typeck/src/collect.rs38
-rw-r--r--src/test/codegen/used_with_arg.rs10
-rw-r--r--src/test/ui/attributes/used_with_arg.rs19
-rw-r--r--src/test/ui/attributes/used_with_arg.stderr18
-rw-r--r--src/test/ui/attributes/used_with_multi_args.rs6
-rw-r--r--src/test/ui/attributes/used_with_multi_args.stderr8
-rw-r--r--src/test/ui/feature-gates/feature-gate-used_with_arg.rs7
-rw-r--r--src/test/ui/feature-gates/feature-gate-used_with_arg.stderr21
16 files changed, 182 insertions, 4 deletions
diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs
index ba4589bd810..ddc2b88191b 100644
--- a/compiler/rustc_codegen_gcc/src/consts.rs
+++ b/compiler/rustc_codegen_gcc/src/consts.rs
@@ -144,7 +144,7 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
             // TODO(antoyo): set link section.
         }
 
-        if attrs.flags.contains(CodegenFnAttrFlags::USED) {
+        if attrs.flags.contains(CodegenFnAttrFlags::USED) || attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) {
             self.add_used_global(global.to_rvalue());
         }
     }
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index d43c7c60651..6707de93352 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -522,6 +522,9 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
             }
 
             if attrs.flags.contains(CodegenFnAttrFlags::USED) {
+                // `USED` and `USED_LINKER` can't be used together.
+                assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER));
+
                 // The semantics of #[used] in Rust only require the symbol to make it into the
                 // object file. It is explicitly allowed for the linker to strip the symbol if it
                 // is dead. As such, use llvm.compiler.used instead of llvm.used.
@@ -530,6 +533,12 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
                 // in some versions of the gold linker.
                 self.add_compiler_used_global(g);
             }
+            if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) {
+                // `USED` and `USED_LINKER` can't be used together.
+                assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED));
+
+                self.add_used_global(g);
+            }
         }
     }
 
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index e5232bf3dd0..0248d3235df 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -531,6 +531,8 @@ declare_features! (
     ///
     /// NOTE: A limited form of `union U { ... }` was accepted in 1.19.0.
     (active, untagged_unions, "1.13.0", Some(55149), None),
+    /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute.
+    (active, used_with_arg, "1.60.0", Some(93798), None),
     /// Allows `extern "wasm" fn`
     (active, wasm_abi, "1.53.0", Some(83788), None),
     // !!!!    !!!!    !!!!    !!!!   !!!!    !!!!    !!!!    !!!!    !!!!    !!!!    !!!!
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index cb2562d09a5..e1f645a62da 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -324,7 +324,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
     ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
     ungated!(no_mangle, Normal, template!(Word), WarnFollowing),
-    ungated!(used, Normal, template!(Word), WarnFollowing),
+    ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing),
 
     // Limits:
     ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index b054d21adaa..54eb2dc9e28 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -89,6 +89,8 @@ bitflags! {
         /// the MIR `InstrumentCoverage` pass and not added to the coverage map
         /// during codegen.
         const NO_COVERAGE               = 1 << 15;
+        /// `#[used(linker)]`: indicates that LLVM nor the linker can eliminate this function.
+        const USED_LINKER               = 1 << 16;
     }
 }
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index ca511f7b814..479a08e43c0 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -1741,12 +1741,46 @@ impl CheckAttrVisitor<'_> {
     }
 
     fn check_used(&self, attrs: &[Attribute], target: Target) {
+        let mut used_linker_span = None;
+        let mut used_compiler_span = None;
         for attr in attrs {
             if attr.has_name(sym::used) && target != Target::Static {
                 self.tcx
                     .sess
                     .span_err(attr.span, "attribute must be applied to a `static` variable");
             }
+            let inner = attr.meta_item_list();
+            match inner.as_deref() {
+                Some([item]) if item.has_name(sym::linker) => {
+                    if used_linker_span.is_none() {
+                        used_linker_span = Some(attr.span);
+                    }
+                }
+                Some([item]) if item.has_name(sym::compiler) => {
+                    if used_compiler_span.is_none() {
+                        used_compiler_span = Some(attr.span);
+                    }
+                }
+                Some(_) => {
+                    // This error case is handled in rustc_typeck::collect.
+                }
+                None => {
+                    // Default case (compiler) when arg isn't defined.
+                    if used_compiler_span.is_none() {
+                        used_compiler_span = Some(attr.span);
+                    }
+                }
+            }
+        }
+        if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) {
+            let spans = vec![linker_span, compiler_span];
+            self.tcx
+                .sess
+                .struct_span_err(
+                    spans,
+                    "`used(compiler)` and `used(linker)` can't be used together",
+                )
+                .emit();
         }
     }
 
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index e7e56711b33..dc3ce1afa33 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -466,7 +466,10 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
 
     // #[used], #[no_mangle], #[export_name], etc also keeps the item alive
     // forcefully, e.g., for placing it in a specific section.
-    if cg_attrs.contains_extern_indicator() || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) {
+    if cg_attrs.contains_extern_indicator()
+        || cg_attrs.flags.contains(CodegenFnAttrFlags::USED)
+        || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
+    {
         return true;
     }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index ef0a1792d58..6a9c260f1c0 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -449,6 +449,7 @@ symbols! {
         compare_exchange_weak,
         compile_error,
         compile_error_macro,
+        compiler,
         compiler_builtins,
         compiler_fence,
         concat,
@@ -818,6 +819,7 @@ symbols! {
         link_ordinal,
         link_section,
         linkage,
+        linker,
         lint_reasons,
         literal,
         load,
@@ -1466,6 +1468,7 @@ symbols! {
         use_extern_macros,
         use_nested_groups,
         used,
+        used_with_arg,
         usize,
         v1,
         va_arg,
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 7c8a47d5d65..7b9f1afe271 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -1,3 +1,4 @@
+// ignore-tidy-filelength
 //! "Collection" is the process of determining the type and other external
 //! details of each item in Rust. Collection is specifically concerned
 //! with *inter-procedural* things -- for example, for a function
@@ -2856,7 +2857,42 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
         } else if attr.has_name(sym::rustc_std_internal_symbol) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
         } else if attr.has_name(sym::used) {
-            codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
+            let inner = attr.meta_item_list();
+            match inner.as_deref() {
+                Some([item]) if item.has_name(sym::linker) => {
+                    if !tcx.features().used_with_arg {
+                        feature_err(
+                            &tcx.sess.parse_sess,
+                            sym::used_with_arg,
+                            attr.span,
+                            "`#[used(linker)]` is currently unstable",
+                        )
+                        .emit();
+                    }
+                    codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
+                }
+                Some([item]) if item.has_name(sym::compiler) => {
+                    if !tcx.features().used_with_arg {
+                        feature_err(
+                            &tcx.sess.parse_sess,
+                            sym::used_with_arg,
+                            attr.span,
+                            "`#[used(compiler)]` is currently unstable",
+                        )
+                        .emit();
+                    }
+                    codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
+                }
+                Some(_) => {
+                    tcx.sess
+                        .struct_span_err(
+                            attr.span,
+                            "expected `used`, `used(compiler)` or `used(linker)`",
+                        )
+                        .emit();
+                }
+                None => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED,
+            }
         } else if attr.has_name(sym::cmse_nonsecure_entry) {
             if !matches!(tcx.fn_sig(id).abi(), abi::Abi::C { .. }) {
                 struct_span_err!(
diff --git a/src/test/codegen/used_with_arg.rs b/src/test/codegen/used_with_arg.rs
new file mode 100644
index 00000000000..5bff50a40d4
--- /dev/null
+++ b/src/test/codegen/used_with_arg.rs
@@ -0,0 +1,10 @@
+#![crate_type = "lib"]
+#![feature(used_with_arg)]
+
+// CHECK: @llvm.used = appending global [1 x i8*]{{.*}}USED_LINKER
+#[used(linker)]
+static mut USED_LINKER: [usize; 1] = [0];
+
+// CHECK-NEXT: @llvm.compiler.used = appending global [1 x i8*]{{.*}}USED_COMPILER
+#[used(compiler)]
+static mut USED_COMPILER: [usize; 1] = [0];
diff --git a/src/test/ui/attributes/used_with_arg.rs b/src/test/ui/attributes/used_with_arg.rs
new file mode 100644
index 00000000000..ad80ff53f0e
--- /dev/null
+++ b/src/test/ui/attributes/used_with_arg.rs
@@ -0,0 +1,19 @@
+#![feature(used_with_arg)]
+
+#[used(linker)]
+static mut USED_LINKER: [usize; 1] = [0];
+
+#[used(compiler)]
+static mut USED_COMPILER: [usize; 1] = [0];
+
+#[used(compiler)] //~ ERROR `used(compiler)` and `used(linker)` can't be used together
+#[used(linker)]
+static mut USED_COMPILER_LINKER2: [usize; 1] = [0];
+
+#[used(compiler)] //~ ERROR `used(compiler)` and `used(linker)` can't be used together
+#[used(linker)]
+#[used(compiler)]
+#[used(linker)]
+static mut USED_COMPILER_LINKER3: [usize; 1] = [0];
+
+fn main() {}
diff --git a/src/test/ui/attributes/used_with_arg.stderr b/src/test/ui/attributes/used_with_arg.stderr
new file mode 100644
index 00000000000..440e5c4a5a0
--- /dev/null
+++ b/src/test/ui/attributes/used_with_arg.stderr
@@ -0,0 +1,18 @@
+error: `used(compiler)` and `used(linker)` can't be used together
+  --> $DIR/used_with_arg.rs:9:1
+   |
+LL | #[used(compiler)]
+   | ^^^^^^^^^^^^^^^^^
+LL | #[used(linker)]
+   | ^^^^^^^^^^^^^^^
+
+error: `used(compiler)` and `used(linker)` can't be used together
+  --> $DIR/used_with_arg.rs:13:1
+   |
+LL | #[used(compiler)]
+   | ^^^^^^^^^^^^^^^^^
+LL | #[used(linker)]
+   | ^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/attributes/used_with_multi_args.rs b/src/test/ui/attributes/used_with_multi_args.rs
new file mode 100644
index 00000000000..2e17fcfd7a4
--- /dev/null
+++ b/src/test/ui/attributes/used_with_multi_args.rs
@@ -0,0 +1,6 @@
+#![feature(used_with_arg)]
+
+#[used(compiler, linker)] //~ expected `used`, `used(compiler)` or `used(linker)`
+static mut USED_COMPILER_LINKER: [usize; 1] = [0];
+
+fn main() {}
diff --git a/src/test/ui/attributes/used_with_multi_args.stderr b/src/test/ui/attributes/used_with_multi_args.stderr
new file mode 100644
index 00000000000..c93aafcfc7c
--- /dev/null
+++ b/src/test/ui/attributes/used_with_multi_args.stderr
@@ -0,0 +1,8 @@
+error: expected `used`, `used(compiler)` or `used(linker)`
+  --> $DIR/used_with_multi_args.rs:3:1
+   |
+LL | #[used(compiler, linker)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/feature-gates/feature-gate-used_with_arg.rs b/src/test/ui/feature-gates/feature-gate-used_with_arg.rs
new file mode 100644
index 00000000000..1c8f01bdef1
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-used_with_arg.rs
@@ -0,0 +1,7 @@
+#[used(linker)] //~ ERROR `#[used(linker)]` is currently unstable
+static mut USED_LINKER: [usize; 1] = [0];
+
+#[used(compiler)] //~ ERROR `#[used(compiler)]` is currently unstable
+static mut USED_COMPILER: [usize; 1] = [0];
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-used_with_arg.stderr b/src/test/ui/feature-gates/feature-gate-used_with_arg.stderr
new file mode 100644
index 00000000000..d115bf4e365
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-used_with_arg.stderr
@@ -0,0 +1,21 @@
+error[E0658]: `#[used(linker)]` is currently unstable
+  --> $DIR/feature-gate-used_with_arg.rs:1:1
+   |
+LL | #[used(linker)]
+   | ^^^^^^^^^^^^^^^
+   |
+   = note: see issue #93798 <https://github.com/rust-lang/rust/issues/93798> for more information
+   = help: add `#![feature(used_with_arg)]` to the crate attributes to enable
+
+error[E0658]: `#[used(compiler)]` is currently unstable
+  --> $DIR/feature-gate-used_with_arg.rs:4:1
+   |
+LL | #[used(compiler)]
+   | ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #93798 <https://github.com/rust-lang/rust/issues/93798> for more information
+   = help: add `#![feature(used_with_arg)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.