diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 3e4c1a9dfad84..654cbf1daf930 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -86,6 +86,9 @@ attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute .suggestion = remove the `unsafe(...)` .note = extraneous unsafe is not allowed in attributes +attr_parsing_invalid_export_visibility = + invalid export visibility: {$unrecognized_visibility} + attr_parsing_invalid_issue_string = `issue` must be a non-zero numeric string or "none" .must_not_be_zero = `issue` must not be "0", use "none" instead diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 063fa12d38961..1475c2975f620 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,10 +1,14 @@ -use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy}; +use std::str::FromStr; + +use rustc_hir::attrs::{ + CoverageAttrKind, ExportVisibilityAttrValue, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy, +}; use rustc_session::parse::feature_err; use super::prelude::*; use crate::session_diagnostics::{ - NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector, - ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, + InvalidExportVisibility, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, + NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, }; use crate::target_checking::Policy::AllowSilent; @@ -153,6 +157,36 @@ 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 Ok(visibility) = ExportVisibilityAttrValue::from_str(sv.as_str()) else { + cx.emit_err(InvalidExportVisibility { + span: nv.value_span, + unrecognized_visibility: sv.to_string(), + }); + return None; + }; + Some(AttributeKind::ExportVisibility { visibility, span: cx.attr_span }) + } +} + pub(crate) struct ObjcClassParser; impl SingleAttributeParser for ObjcClassParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index e500be68e2410..94ae49f56887c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -21,10 +21,10 @@ use crate::attributes::allow_unstable::{ use crate::attributes::body::CoroutineParser; use crate::attributes::cfi_encoding::CfiEncodingParser; use crate::attributes::codegen_attrs::{ - ColdParser, CoverageParser, EiiForeignItemParser, ExportNameParser, ForceTargetFeatureParser, - NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, - PatchableFunctionEntryParser, RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, - TargetFeatureParser, ThreadLocalParser, TrackCallerParser, UsedParser, + ColdParser, CoverageParser, EiiForeignItemParser, ExportNameParser, ExportVisibilityParser, + ForceTargetFeatureParser, NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, + OptimizeParser, PatchableFunctionEntryParser, RustcPassIndirectlyInNonRusticAbisParser, + SanitizeParser, TargetFeatureParser, ThreadLocalParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ @@ -212,6 +212,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index f9748542beb94..c62cda487adf1 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -389,6 +389,14 @@ pub(crate) struct UnusedMultiple { pub name: Symbol, } +#[derive(Diagnostic)] +#[diag(attr_parsing_invalid_export_visibility)] +pub(crate) struct InvalidExportVisibility { + #[primary_span] + pub span: Span, + pub unrecognized_visibility: String, +} + #[derive(Diagnostic)] #[diag(attr_parsing_null_on_export, code = E0648)] pub(crate) struct NullOnExport { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 4cb390e7d5699..461236eb847b7 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -4,7 +4,8 @@ use rustc_abi::{Align, ExternAbi}; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner}; 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}; @@ -75,6 +76,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); @@ -542,6 +550,25 @@ fn handle_lang_items( } err.emit(); } + + if codegen_fn_attrs.export_visibility.is_some() { + let export_visibility_span = + find_attr!(attrs, AttributeKind::ExportVisibility{span, ..} => *span) + .unwrap_or_default(); + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { + tcx.dcx().span_err( + export_visibility_span, + "#[export_visibility = ...]` cannot be used on internal language items", + ); + } + if !codegen_fn_attrs.contains_extern_indicator() { + tcx.dcx().span_err( + export_visibility_span, + "#[export_visibility = ...]` will be ignored without \ + `export_name`, `no_mangle`, or similar attribute", + ); + } + } } /// Generate the [`CodegenFnAttrs`] for an item (identified by the [`LocalDefId`]). diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ded8a5a4ae51c..b971d97f6634f 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 105eb573967df..50d60cde304e6 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -476,6 +476,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 8a7dee15d4f48..28a2727c5ad0c 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::path::PathBuf; +use std::str::FromStr; pub use ReprAttr::*; use rustc_abi::Align; @@ -187,6 +188,26 @@ 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, +} + +impl FromStr for ExportVisibilityAttrValue { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "target_default" => Ok(ExportVisibilityAttrValue::TargetDefault), + _ => Err(()), + } + } +} + /// 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)]` @@ -874,6 +895,9 @@ pub enum AttributeKind { /// Represents `#[export_stable]`. ExportStable, + /// Represents [`#[export_visibility = ...]`](https://github.com/rust-lang/rust/issues/151425) + ExportVisibility { visibility: ExportVisibilityAttrValue, span: Span }, + /// Represents `#[ffi_const]`. FfiConst(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 356575416aded..a29be7b82d723 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -55,6 +55,7 @@ impl AttributeKind { EiiImpls(..) => No, ExportName { .. } => Yes, ExportStable => No, + ExportVisibility { .. } => Yes, FfiConst(..) => No, FfiPure(..) => No, Fundamental { .. } => Yes, 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 a86230e9ab22c..f1fd8b7dcb9e9 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 cdd141f9233e8..28db09d934c0a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -253,6 +253,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::EiiForeignItem | AttributeKind::ExportName { .. } | AttributeKind::ExportStable + | AttributeKind::ExportVisibility { .. } | AttributeKind::FfiConst(..) | AttributeKind::Fundamental | AttributeKind::Ignore { .. } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5767444025ec2..219a0f40a147f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -999,6 +999,7 @@ symbols! { explicit_tail_calls, export_name, export_stable, + 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..3ce4951a22e1f --- /dev/null +++ b/tests/run-make/cdylib-export-visibility/foo.rs @@ -0,0 +1,15 @@ +#![crate_type = "cdylib"] +#![feature(export_visibility)] + +#[unsafe(no_mangle)] +unsafe extern "C" fn test_fn_no_attr() -> u32 { + // Using `line!()` means that the functions return different results + // and therefore identical code folding (ICF) in the linker won't apply. + line!() +} + +#[unsafe(no_mangle)] +#[export_visibility = "target_default"] +unsafe extern "C" fn test_fn_export_visibility_asks_for_target_default() -> u32 { + line!() +} 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..8a648666a3074 --- /dev/null +++ b/tests/run-make/cdylib-export-visibility/rmake.rs @@ -0,0 +1,22 @@ +// 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 + +use run_make_support::{dynamic_lib_name, llvm_readobj, rustc}; + +fn main() { + // Compile a cdylib + rustc().input("foo.rs").arg("-Zdefault-visibility=hidden").run(); + let out = + llvm_readobj().arg("--dyn-symbols").input(dynamic_lib_name("foo")).run().stdout_utf8(); + + // `#[no_mangle]` with no other attributes means: publicly exported function. + assert!(out.contains("test_fn_no_attr"), "{out}"); + + // `#[no_mangle]` with `#[export_visibility = "target_default"]` means + // that visibility is inherited from `-Zdefault-visibility=hidden`. + assert!(!out.contains("test_fn_export_visibility_asks_for_target_default"), "{out}"); +} 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..65426310a17d2 --- /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..8977d66bc3180 --- /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: invalid export visibility +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: invalid export visibility +pub static TESTED_STATIC_HIDDEN: [u8; 6] = *b"foobar"; +#[no_mangle] +#[export_visibility = "protected"] +//~^ ERROR: invalid export visibility +pub static TESTED_STATIC_PROTECTED: [u8; 6] = *b"foobar"; +#[no_mangle] +#[export_visibility = "interposable"] +//~^ ERROR: invalid export visibility +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: invalid export visibility +pub static TESTED_STATIC_DEFAULT: [u8; 6] = *b"foobar"; +#[no_mangle] +#[export_visibility = "public"] +//~^ ERROR: invalid export visibility +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..18f9b66df42c0 --- /dev/null +++ b/tests/ui/attributes/export-visibility-with-unrecognized-arg.stderr @@ -0,0 +1,38 @@ +error: invalid export visibility: unrecognized visibility value + --> $DIR/export-visibility-with-unrecognized-arg.rs:5:23 + | +LL | #[export_visibility = "unrecognized visibility value"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: invalid export visibility: hidden + --> $DIR/export-visibility-with-unrecognized-arg.rs:12:23 + | +LL | #[export_visibility = "hidden"] + | ^^^^^^^^ + +error: invalid export visibility: protected + --> $DIR/export-visibility-with-unrecognized-arg.rs:16:23 + | +LL | #[export_visibility = "protected"] + | ^^^^^^^^^^^ + +error: invalid export visibility: interposable + --> $DIR/export-visibility-with-unrecognized-arg.rs:20:23 + | +LL | #[export_visibility = "interposable"] + | ^^^^^^^^^^^^^^ + +error: invalid export visibility: default + --> $DIR/export-visibility-with-unrecognized-arg.rs:26:23 + | +LL | #[export_visibility = "default"] + | ^^^^^^^^^ + +error: invalid export visibility: public + --> $DIR/export-visibility-with-unrecognized-arg.rs:30:23 + | +LL | #[export_visibility = "public"] + | ^^^^^^^^ + +error: aborting due to 6 previous errors + 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..2599fa1aa8bfa --- /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`.