diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index ba4589bd81025..ddc2b88191bd0 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 d43c7c6065179..6707de933522b 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 bfe2459dc8dc1..c652e54ffe7b8 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -533,6 +533,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 3933746c319ec..13b482cb469bd 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 b054d21adaa13..54eb2dc9e2890 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 ca511f7b814be..479a08e43c01a 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 e7e56711b33c5..dc3ce1afa3361 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 18627cd2c099d..4a1fd476b8c0a 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, @@ -815,6 +816,7 @@ symbols! { link_ordinal, link_section, linkage, + linker, lint_reasons, literal, load, @@ -1458,6 +1460,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 cf519a9ab3274..2a280ff97e96d 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 @@ -2849,7 +2850,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 0000000000000..5bff50a40d4e8 --- /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 0000000000000..ad80ff53f0ef0 --- /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 0000000000000..440e5c4a5a020 --- /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 0000000000000..2e17fcfd7a493 --- /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 0000000000000..c93aafcfc7cce --- /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 0000000000000..1c8f01bdef116 --- /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 0000000000000..d115bf4e365a7 --- /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 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 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`.