diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 4909e0d35173c..0100d34187aa7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,4 +1,6 @@ -use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy}; +use rustc_hir::attrs::{ + CoverageAttrKind, ExportVisibilityAttrValue, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy, +}; use rustc_session::parse::feature_err; use super::prelude::*; @@ -153,6 +155,43 @@ impl SingleAttributeParser for ExportNameParser { } } +pub(crate) struct ExportVisibilityParser; + +impl SingleAttributeParser for ExportVisibilityParser { + const PATH: &[rustc_span::Symbol] = &[sym::export_visibility]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::Static)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "visibility"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + let Some(sv) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + + let str_to_visibility = [("target_default", ExportVisibilityAttrValue::TargetDefault)]; + for &(s, visibility) in str_to_visibility.iter() { + if s == sv.as_str() { + return Some(AttributeKind::ExportVisibility { visibility, span: cx.attr_span }); + } + } + + let allowed_str_values = str_to_visibility + .into_iter() + .map(|(s, _visibility)| s) + .map(Symbol::intern) + .collect::>(); + cx.expected_specific_argument_strings(nv.value_span, &allowed_str_values); + None + } +} + pub(crate) struct RustcObjcClassParser; impl SingleAttributeParser for RustcObjcClassParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 802ee56f504b0..72f30016b89b5 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -184,6 +184,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 1ceb01337b118..dd6570801c05e 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,6 +1,7 @@ use rustc_abi::{Align, ExternAbi}; use rustc_hir::attrs::{ - AttributeKind, EiiImplResolution, InlineAttr, Linkage, RtsanSetting, UsedBy, + AttributeKind, EiiImplResolution, ExportVisibilityAttrValue, InlineAttr, Linkage, RtsanSetting, + UsedBy, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; @@ -70,6 +71,13 @@ fn process_builtin_attrs( match attr { AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, AttributeKind::ExportName { name, .. } => codegen_fn_attrs.symbol_name = Some(*name), + AttributeKind::ExportVisibility { visibility, .. } => { + codegen_fn_attrs.export_visibility = Some(match visibility { + ExportVisibilityAttrValue::TargetDefault => { + tcx.sess.default_visibility().into() + } + }); + } AttributeKind::Inline(inline, span) => { codegen_fn_attrs.inline = *inline; interesting_spans.inline = Some(*span); @@ -533,6 +541,16 @@ fn handle_lang_items( } err.emit(); } + + if codegen_fn_attrs.export_visibility.is_some() { + let span = find_attr!(attrs, ExportVisibility{span, ..} => *span).unwrap_or_default(); + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { + tcx.dcx().emit_err(errors::ExportVisibilityWithRustcStdInternalSymbol { span }); + } + if !codegen_fn_attrs.contains_extern_indicator() { + tcx.dcx().emit_err(errors::ExportVisibilityWithoutNoMangleNorExportName { span }); + } + } } /// Generate the [`CodegenFnAttrs`] for an item (identified by the [`LocalDefId`]). diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index be1965f674911..5d57ec8d3c6f1 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1268,3 +1268,20 @@ pub(crate) struct LtoProcMacro; #[diag("cannot prefer dynamic linking when performing LTO")] #[note("only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO")] pub(crate) struct DynamicLinkingWithLTO; + +#[derive(Diagnostic)] +#[diag("`#[export_visibility = ...]` cannot be used on internal language items")] +pub(crate) struct ExportVisibilityWithRustcStdInternalSymbol { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag( + "`#[export_visibility = ...]` will be ignored \ + without `export_name`, `no_mangle`, or similar attribute" +)] +pub(crate) struct ExportVisibilityWithoutNoMangleNorExportName { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index db8f459ef0451..d1f30a14e74c2 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -649,6 +649,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute"), FutureWarnPreceding, EncodeCrossCrate::No ), + gated!(export_visibility, Normal, template!(NameValueStr: "visibility"), ErrorPreceding, EncodeCrossCrate::No, experimental!(export_visibility)), ungated!( unsafe(Edition2024) link_section, Normal, template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1d123385961aa..6ea5b53c7ebdf 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -474,6 +474,8 @@ declare_features! ( (incomplete, explicit_tail_calls, "1.72.0", Some(112788)), /// Allows using `#[export_stable]` which indicates that an item is exportable. (incomplete, export_stable, "1.88.0", Some(139939)), + /// Allows `#[export_visibility]` on definitions of statics and/or functions. + (unstable, export_visibility, "CURRENT_RUSTC_VERSION", Some(151425)), /// Externally implementable items (unstable, extern_item_impls, "1.94.0", Some(125418)), /// Allows defining `extern type`s. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 91409108a7533..26b9def29fbd0 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -251,6 +251,15 @@ impl Deprecation { } } +/// Pre-parsed value of `#[export_visibility = ...]` attribute. +/// +/// In a future RFC we may consider adding support for `Hidden`, `Protected`, and/or +/// `Interposable`. +#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum ExportVisibilityAttrValue { + TargetDefault, +} + /// There are three valid forms of the attribute: /// `#[used]`, which is equivalent to `#[used(linker)]` on targets that support it, but `#[used(compiler)]` if not. /// `#[used(compiler)]` @@ -1045,6 +1054,12 @@ pub enum AttributeKind { /// Represents `#[export_stable]`. ExportStable, + /// Represents [`#[export_visibility = ...]`](https://github.com/rust-lang/rust/issues/151425) + ExportVisibility { + visibility: ExportVisibilityAttrValue, + span: Span, + }, + /// Represents `#[feature(...)]` Feature(ThinVec, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index cd41a2b9b28c7..7ed1ced4c6e9c 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -43,6 +43,7 @@ impl AttributeKind { EiiImpls(..) => No, ExportName { .. } => Yes, ExportStable => No, + ExportVisibility { .. } => Yes, Feature(..) => No, FfiConst(..) => No, FfiPure(..) => No, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 4f600af0cbfce..0fb60841f9faa 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -79,6 +79,12 @@ pub struct CodegenFnAttrs { /// be set when `link_name` is set. This is for foreign items with the /// "raw-dylib" kind. pub link_ordinal: Option, + /// The `#[export_visibility = "..."]` attribute, with values interpreted + /// as follows: + /// * `None` - use the "inherent" visibility (either based on the target platform, or provided via + /// `-Zdefault-visibility=...` command-line flag) + /// * `Some(...)` - use the item/symbol-specific visibility + pub export_visibility: Option, /// The `#[target_feature(enable = "...")]` attribute and the enabled /// features (only enabled features are supported right now). /// Implied target features have already been applied. @@ -224,6 +230,7 @@ impl CodegenFnAttrs { optimize: OptimizeAttr::Default, symbol_name: None, link_ordinal: None, + export_visibility: None, target_features: vec![], foreign_item_symbol_aliases: vec![], safe_target_features: false, diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index d8f4e01945075..c1d5706985cb7 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -931,6 +931,11 @@ fn mono_item_visibility<'tcx>( } fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility { + // If present, then symbol-specific `#[export_visibility = ...]` "wins". + if let Some(visibility) = tcx.codegen_fn_attrs(id).export_visibility { + return visibility; + } + // Fast-path to avoid expensive query call below if tcx.sess.default_visibility() == SymbolVisibility::Interposable { return Visibility::Default; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index dacb02afe1612..b7176c05be1ed 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -254,6 +254,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::EiiDeclaration { .. } | AttributeKind::ExportName { .. } | AttributeKind::ExportStable + | AttributeKind::ExportVisibility { .. } | AttributeKind::Feature(..) | AttributeKind::FfiConst(..) | AttributeKind::Fundamental diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 731a838530729..35c614e9e5771 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -907,6 +907,7 @@ symbols! { export_name, export_stable, export_symbols: "export-symbols", + export_visibility, expr, expr_2021, expr_fragment_specifier_2024, diff --git a/tests/codegen-llvm/export-visibility.rs b/tests/codegen-llvm/export-visibility.rs new file mode 100644 index 0000000000000..ff0b785e53d97 --- /dev/null +++ b/tests/codegen-llvm/export-visibility.rs @@ -0,0 +1,102 @@ +// Verifies that `#[export_visibility = ...]` can override the visibility +// that is normally implied by `#[export_name]` or `#[no_mangle]`. +// +// High-level test expectations for items with `#[export_name = ...]` +// (or with `#[no_mangle]`) and: +// +// * Without `#[export_visibility = ...]` => public +// * `#[export_visibility = "target_default"]` => value inherited from the target +// platform or from the `-Zdefault-visibility=...` command-line flag +// (this expectation depends on whether the `...-HIDDEN` vs `...-PROTECTED` +// test revisions are used). +// +// Note that what we call "public" in the expectations above is also referred +// to as "default" in LLVM docs - see +// https://llvm.org/docs/LangRef.html#visibility-styles + +//@ revisions: LINUX-X86-HIDDEN LINUX-X86-PROTECTED +//@[LINUX-X86-HIDDEN] compile-flags: -Zdefault-visibility=hidden +//@[LINUX-X86-PROTECTED] compile-flags: -Zdefault-visibility=protected + +// Exact LLVM IR differs depending on the target triple (e.g. `hidden constant` +// vs `internal constant` vs `constant`). Because of this, we only apply the +// specific test expectations below to one specific target triple. +// +// Note that `tests/run-make/cdylib-export-visibility` provides similar +// test coverage, but in an LLVM-IR-agnostic / platform-agnostic way. +//@[LINUX-X86-HIDDEN] needs-llvm-components: x86 +//@[LINUX-X86-HIDDEN] compile-flags: --target x86_64-unknown-linux-gnu +//@[LINUX-X86-PROTECTED] needs-llvm-components: x86 +//@[LINUX-X86-PROTECTED] compile-flags: --target x86_64-unknown-linux-gnu + +// This test focuses on rlib to exercise the scenario described in +// https://github.com/rust-lang/rust/issues/73958#issuecomment-2891711649 +#![crate_type = "rlib"] +#![feature(export_visibility)] +// Relying on `minicore` makes it easier to run the test, even if the host is +// not a linux-x86 machine. +//@ add-minicore +//@ edition: 2024 +#![feature(no_core)] +#![no_core] +use minicore::*; + +/////////////////////////////////////////////////////////////////////// +// The tests below focus on how `#[export_visibility = ...]` works for +// a `static`. The tests are based on similar tests in +// `tests/codegen/default-visibility.rs` + +#[unsafe(export_name = "static_export_name_no_attr")] +pub static TEST_STATIC_NO_ATTR: u32 = 1101; + +#[unsafe(export_name = "static_export_name_target_default")] +#[export_visibility = "target_default"] +pub static TESTED_STATIC_ATTR_ASKS_TO_TARGET_DEFAULT: u32 = 1102; + +#[unsafe(no_mangle)] +pub static static_no_mangle_no_attr: u32 = 1201; + +#[unsafe(no_mangle)] +#[export_visibility = "target_default"] +pub static static_no_mangle_target_default: u32 = 1202; + +// LINUX-X86-HIDDEN: @static_export_name_no_attr = local_unnamed_addr constant +// LINUX-X86-HIDDEN: @static_export_name_target_default = hidden local_unnamed_addr constant +// LINUX-X86-HIDDEN: @static_no_mangle_no_attr = local_unnamed_addr constant +// LINUX-X86-HIDDEN: @static_no_mangle_target_default = hidden local_unnamed_addr constant + +// LINUX-X86-PROTECTED: @static_export_name_no_attr = local_unnamed_addr constant +// LINUX-X86-PROTECTED: @static_export_name_target_default = protected local_unnamed_addr constant +// LINUX-X86-PROTECTED: @static_no_mangle_no_attr = local_unnamed_addr constant +// LINUX-X86-PROTECTED: @static_no_mangle_target_default = protected local_unnamed_addr constant + +/////////////////////////////////////////////////////////////////////// +// The tests below focus on how `#[export_visibility = ...]` works for +// a `fn`. +// +// The tests below try to mimics how `cxx` exports known/hardcoded helpers (e.g. +// `cxxbridge1$string$drop` [1]) as well as build-time-generated thunks (e.g. +// `serde_json_lenient$cxxbridge1$decode_json` from https://crbug.com/418073233#comment7). +// +// [1] +// https://github.com/dtolnay/cxx/blob/ebdd6a0c63ae10dc5224ed21970b7a0504657434/src/symbols/rust_string.rs#L83-L86 + +#[unsafe(export_name = "test_fn_no_attr")] +unsafe extern "C" fn test_fn_no_attr() -> u32 { + // We return a unique integer to ensure that each function has a unique body + // and therefore that identical code folding (ICF) won't fold the functions + // when linking. + 2001 +} + +#[unsafe(export_name = "test_fn_target_default")] +#[export_visibility = "target_default"] +unsafe extern "C" fn test_fn_asks_for_target_default() -> u32 { + 2002 +} + +// LINUX-X86-HIDDEN: define noundef i32 @test_fn_no_attr +// LINUX-X86-HIDDEN: define hidden noundef i32 @test_fn_target_default + +// LINUX-X86-PROTECTED: define noundef i32 @test_fn_no_attr +// LINUX-X86-PROTECTED: define protected noundef i32 @test_fn_target_default diff --git a/tests/run-make/cdylib-export-visibility/foo.rs b/tests/run-make/cdylib-export-visibility/foo.rs new file mode 100644 index 0000000000000..0b0b9239a2080 --- /dev/null +++ b/tests/run-make/cdylib-export-visibility/foo.rs @@ -0,0 +1,29 @@ +#![crate_type = "cdylib"] +#![feature(export_visibility)] +// `no_std` makes it slightly easier to run the test when cross-compiling. +// Ideally the test would use `no_core`, but `//@ add-minicore` doesn't seem +// to work... +#![no_std] +//@ edition: 2024 + +#[unsafe(no_mangle)] +unsafe extern "C" fn test_fn_no_export_visibility_attribute() -> u32 { + // Using unique integer means that the functions return different results + // and therefore identical code folding (ICF) in the linker won't apply. + 16 // line number; can't use `line!` with `no_core` +} + +#[unsafe(no_mangle)] +#[export_visibility = "target_default"] +unsafe extern "C" fn test_fn_export_visibility_asks_for_target_default() -> u32 { + // Using unique integer means that the functions return different results + // and therefore identical code folding (ICF) in the linker won't apply. + 24 // line number; can't use `line!` with `no_core` +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + // The infinite loop should never run - we only look at symbol visibilities + // of `test_fn_...` above. + loop {} +} diff --git a/tests/run-make/cdylib-export-visibility/rmake.rs b/tests/run-make/cdylib-export-visibility/rmake.rs new file mode 100644 index 0000000000000..20f72b5d65e4f --- /dev/null +++ b/tests/run-make/cdylib-export-visibility/rmake.rs @@ -0,0 +1,91 @@ +// This test builds `foo.rs` into a `cdylib` and verifies that +// `#[export_visibility = ...]` affects visibility of symbols. +// +// This test is loosely based on manual test steps described when +// discussing the related RFC at: +// https://github.com/rust-lang/rfcs/pull/3834#issuecomment-3403039933 + +//@ needs-crate-type: cdylib +//@ needs-dynamic-linking + +// See https://github.com/rust-lang/rust/pull/151431#issuecomment-3923604898 +// and earlier comments that explain the problems encountered when attempting +// to enable this test for WASM. +//@ ignore-wasm + +// See https://github.com/rust-lang/rust/pull/151431#issuecomment-3923203589 +// for why this test skips `nvptx64-nvidia-cuda` and similar targets. +//@ ignore-nvptx64 + +// See https://github.com/rust-lang/rust/pull/151431#issuecomment-3975560321 +// for why this test skips the `aarch64-apple-darwin` target. +//@ ignore-aarch64-apple-darwin + +use std::collections::HashSet; + +use run_make_support::symbols::exported_dynamic_symbol_names; +use run_make_support::targets::is_wasi; +use run_make_support::{dynamic_lib_name, object, rustc}; + +struct TestCase { + name: &'static str, + extra_rustc_arg: Option<&'static str>, + expected_exported_symbols: &'static [&'static str], + expected_private_symbols: &'static [&'static str], +} + +impl TestCase { + fn run(&self) { + let test_name = self.name; + + let mut rustc = rustc(); + rustc.input("foo.rs"); + rustc.arg("-Cpanic=abort"); + if let Some(extra_arg) = self.extra_rustc_arg { + rustc.arg(extra_arg); + } + rustc.run(); + + let lib_path = dynamic_lib_name("foo"); + let object_file_bytes = std::fs::read(&lib_path) + .unwrap_or_else(|e| panic!("{test_name}: failed to read `{lib_path}`: {e}")); + let object_file = object::File::parse(object_file_bytes.as_slice()) + .unwrap_or_else(|e| panic!("{test_name}: failed to parse `{lib_path}`: {e}")); + let actual_exported_symbols = + exported_dynamic_symbol_names(&object_file).into_iter().collect::>(); + + for s in self.expected_exported_symbols { + assert!( + actual_exported_symbols.contains(s), + "{test_name}: Expecting `{s}` to be an actually exported symbol in `{lib_path}`", + ); + } + for s in self.expected_private_symbols { + assert!( + !actual_exported_symbols.contains(s), + "{test_name}: Expecting `{s}` to *not* be exported from `{lib_path}`", + ); + } + } +} + +fn main() { + TestCase { + name: "Test #1", + extra_rustc_arg: Some("-Zdefault-visibility=hidden"), + expected_exported_symbols: &["test_fn_no_export_visibility_attribute"], + expected_private_symbols: &["test_fn_export_visibility_asks_for_target_default"], + } + .run(); + + TestCase { + name: "Test #2", + extra_rustc_arg: None, + expected_exported_symbols: &[ + "test_fn_no_export_visibility_attribute", + "test_fn_export_visibility_asks_for_target_default", + ], + expected_private_symbols: &[], + } + .run(); +} diff --git a/tests/ui/attributes/export-visibility-with-rustc-std-internal-symbol.rs b/tests/ui/attributes/export-visibility-with-rustc-std-internal-symbol.rs new file mode 100644 index 0000000000000..fcacaf160827d --- /dev/null +++ b/tests/ui/attributes/export-visibility-with-rustc-std-internal-symbol.rs @@ -0,0 +1,11 @@ +// This test verfies that `#[export_visibility = ...]` will report an error +// when applied to an item that also has `#[rustc_std_internal_symbol]` +// attribute. +#![feature(export_visibility)] +#![feature(rustc_attrs)] +#[export_visibility = "target_default"] +//~^ERROR: #[export_visibility = ...]` cannot be used on internal language items +#[rustc_std_internal_symbol] +pub static TESTED_STATIC: [u8; 6] = *b"foobar"; + +fn main() {} diff --git a/tests/ui/attributes/export-visibility-with-rustc-std-internal-symbol.stderr b/tests/ui/attributes/export-visibility-with-rustc-std-internal-symbol.stderr new file mode 100644 index 0000000000000..6685a20905760 --- /dev/null +++ b/tests/ui/attributes/export-visibility-with-rustc-std-internal-symbol.stderr @@ -0,0 +1,8 @@ +error: `#[export_visibility = ...]` cannot be used on internal language items + --> $DIR/export-visibility-with-rustc-std-internal-symbol.rs:6:1 + | +LL | #[export_visibility = "target_default"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/export-visibility-with-unrecognized-arg.rs b/tests/ui/attributes/export-visibility-with-unrecognized-arg.rs new file mode 100644 index 0000000000000..17aeddbd7cc13 --- /dev/null +++ b/tests/ui/attributes/export-visibility-with-unrecognized-arg.rs @@ -0,0 +1,34 @@ +// This test verfies that `#[export_visibility = ...]` will report an error +// when the argument cannot be parsed. +#![feature(export_visibility)] +#[no_mangle] +#[export_visibility = "unrecognized visibility value"] +//~^ ERROR: malformed `export_visibility` attribute input +pub static TESTED_STATIC: [u8; 6] = *b"foobar"; + +// The following `static`s verify that `hidden`, `protected`, and `interposable` +// are not supported yet. +#[no_mangle] +#[export_visibility = "hidden"] +//~^ ERROR: malformed `export_visibility` attribute input +pub static TESTED_STATIC_HIDDEN: [u8; 6] = *b"foobar"; +#[no_mangle] +#[export_visibility = "protected"] +//~^ ERROR: malformed `export_visibility` attribute input +pub static TESTED_STATIC_PROTECTED: [u8; 6] = *b"foobar"; +#[no_mangle] +#[export_visibility = "interposable"] +//~^ ERROR: malformed `export_visibility` attribute input +pub static TESTED_STATIC_INTERPOSABLE: [u8; 6] = *b"foobar"; + +// The following `static`s verify that other visibility spellings are also not supported. +#[no_mangle] +#[export_visibility = "default"] +//~^ ERROR: malformed `export_visibility` attribute input +pub static TESTED_STATIC_DEFAULT: [u8; 6] = *b"foobar"; +#[no_mangle] +#[export_visibility = "public"] +//~^ ERROR: malformed `export_visibility` attribute input +pub static TESTED_STATIC_PUBLIC: [u8; 6] = *b"foobar"; + +fn main() {} diff --git a/tests/ui/attributes/export-visibility-with-unrecognized-arg.stderr b/tests/ui/attributes/export-visibility-with-unrecognized-arg.stderr new file mode 100644 index 0000000000000..1c75610f8755c --- /dev/null +++ b/tests/ui/attributes/export-visibility-with-unrecognized-arg.stderr @@ -0,0 +1,57 @@ +error[E0539]: malformed `export_visibility` attribute input + --> $DIR/export-visibility-with-unrecognized-arg.rs:5:1 + | +LL | #[export_visibility = "unrecognized visibility value"] + | ^^^^^^^^^^^^^^^^^^^^^^-------------------------------^ + | | | + | | the only valid argument here is "target_default" + | help: must be of the form: `#[export_visibility = "visibility"]` + +error[E0539]: malformed `export_visibility` attribute input + --> $DIR/export-visibility-with-unrecognized-arg.rs:12:1 + | +LL | #[export_visibility = "hidden"] + | ^^^^^^^^^^^^^^^^^^^^^^--------^ + | | | + | | the only valid argument here is "target_default" + | help: must be of the form: `#[export_visibility = "visibility"]` + +error[E0539]: malformed `export_visibility` attribute input + --> $DIR/export-visibility-with-unrecognized-arg.rs:16:1 + | +LL | #[export_visibility = "protected"] + | ^^^^^^^^^^^^^^^^^^^^^^-----------^ + | | | + | | the only valid argument here is "target_default" + | help: must be of the form: `#[export_visibility = "visibility"]` + +error[E0539]: malformed `export_visibility` attribute input + --> $DIR/export-visibility-with-unrecognized-arg.rs:20:1 + | +LL | #[export_visibility = "interposable"] + | ^^^^^^^^^^^^^^^^^^^^^^--------------^ + | | | + | | the only valid argument here is "target_default" + | help: must be of the form: `#[export_visibility = "visibility"]` + +error[E0539]: malformed `export_visibility` attribute input + --> $DIR/export-visibility-with-unrecognized-arg.rs:26:1 + | +LL | #[export_visibility = "default"] + | ^^^^^^^^^^^^^^^^^^^^^^---------^ + | | | + | | the only valid argument here is "target_default" + | help: must be of the form: `#[export_visibility = "visibility"]` + +error[E0539]: malformed `export_visibility` attribute input + --> $DIR/export-visibility-with-unrecognized-arg.rs:30:1 + | +LL | #[export_visibility = "public"] + | ^^^^^^^^^^^^^^^^^^^^^^--------^ + | | | + | | the only valid argument here is "target_default" + | help: must be of the form: `#[export_visibility = "visibility"]` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/attributes/export-visibility-without-export-name.rs b/tests/ui/attributes/export-visibility-without-export-name.rs new file mode 100644 index 0000000000000..bc50388206ad8 --- /dev/null +++ b/tests/ui/attributes/export-visibility-without-export-name.rs @@ -0,0 +1,8 @@ +// This test verfies that `#[export_visibility = ...]` cannot be used without +// either `#[export_name = ...]` or `#[no_mangle]`. +#![feature(export_visibility)] +#[export_visibility = "target_default"] +//~^ ERROR: #[export_visibility = ...]` will be ignored +pub static TESTED_STATIC: [u8; 6] = *b"foobar"; + +fn main() {} diff --git a/tests/ui/attributes/export-visibility-without-export-name.stderr b/tests/ui/attributes/export-visibility-without-export-name.stderr new file mode 100644 index 0000000000000..027c4fd62faa0 --- /dev/null +++ b/tests/ui/attributes/export-visibility-without-export-name.stderr @@ -0,0 +1,8 @@ +error: `#[export_visibility = ...]` will be ignored without `export_name`, `no_mangle`, or similar attribute + --> $DIR/export-visibility-without-export-name.rs:4:1 + | +LL | #[export_visibility = "target_default"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/feature-gates/feature-gate-export-visibility.rs b/tests/ui/feature-gates/feature-gate-export-visibility.rs new file mode 100644 index 0000000000000..eb059b2729fe0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-export-visibility.rs @@ -0,0 +1,11 @@ +// This test verfies that `#[export_visibility = ...]` cannot be used without +// opting into the corresponding unstable feature via +// `#![feature(export_visibility)]`. +#[export_visibility = "target_default"] +//~^ ERROR: the `#[export_visibility]` attribute is an experimental feature +// `#[export_name = ...]` is present to avoid hitting the following error: +// export visibility will be ignored without `export_name`, `no_mangle`, or similar attribute +#[unsafe(export_name = "exported_static")] +pub static TESTED_STATIC: [u8; 6] = *b"foobar"; + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-export-visibility.stderr b/tests/ui/feature-gates/feature-gate-export-visibility.stderr new file mode 100644 index 0000000000000..9e5bc27bc745e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-export-visibility.stderr @@ -0,0 +1,13 @@ +error[E0658]: the `#[export_visibility]` attribute is an experimental feature + --> $DIR/feature-gate-export-visibility.rs:4:1 + | +LL | #[export_visibility = "target_default"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #151425 for more information + = help: add `#![feature(export_visibility)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`.