From aaf7d53968c6658cb81d472faeb0b1f646ece8af Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 29 Jun 2025 15:25:18 +0200 Subject: [PATCH] Wip Signed-off-by: Jonathan Brouwer --- Cargo.lock | 2 + .../src/attributes.rs | 117 ++++++ .../src/encode_cross_crate.rs | 1 + compiler/rustc_attr_parsing/Cargo.toml | 1 + compiler/rustc_attr_parsing/messages.ftl | 334 ++++++++++++++++++ .../src/attributes/link_attrs.rs | 315 ++++++++++++++++- compiler/rustc_attr_parsing/src/context.rs | 3 +- compiler/rustc_attr_parsing/src/lib.rs | 1 + compiler/rustc_attr_parsing/src/parser.rs | 18 + .../src/session_diagnostics.rs | 217 ++++++++++++ compiler/rustc_codegen_ssa/src/back/link.rs | 2 +- .../src/back/link/raw_dylib.rs | 2 +- compiler/rustc_codegen_ssa/src/common.rs | 3 +- compiler/rustc_codegen_ssa/src/lib.rs | 2 +- compiler/rustc_metadata/src/native_libs.rs | 327 ++--------------- compiler/rustc_passes/src/check_attr.rs | 9 +- compiler/rustc_session/Cargo.toml | 1 + .../rustc_session/src/config/native_libs.rs | 3 +- compiler/rustc_session/src/cstore.rs | 21 +- compiler/rustc_session/src/utils.rs | 64 +--- 20 files changed, 1053 insertions(+), 390 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1cf17e2c01ca..1df0e93eb866c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3368,6 +3368,7 @@ dependencies = [ "rustc_macros", "rustc_session", "rustc_span", + "rustc_target", "thin-vec", ] @@ -4443,6 +4444,7 @@ dependencies = [ "rand 0.9.1", "rustc_abi", "rustc_ast", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_feature", diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 5eb1931152d31..95c7242bb6d37 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -142,6 +142,120 @@ pub enum UsedBy { Linker, } +/// Different ways that the PE Format can decorate a symbol name. +/// From +#[derive( + Copy, + Clone, + Debug, + Encodable, + Decodable, + HashStable_Generic, + PartialEq, + Eq, + PrintAttribute +)] +pub enum PeImportNameType { + /// IMPORT_ORDINAL + /// Uses the ordinal (i.e., a number) rather than the name. + Ordinal(u16), + /// Same as IMPORT_NAME + /// Name is decorated with all prefixes and suffixes. + Decorated, + /// Same as IMPORT_NAME_NOPREFIX + /// Prefix (e.g., the leading `_` or `@`) is skipped, but suffix is kept. + NoPrefix, + /// Same as IMPORT_NAME_UNDECORATE + /// Prefix (e.g., the leading `_` or `@`) and suffix (the first `@` and all + /// trailing characters) are skipped. + Undecorated, +} + +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Encodable, + Decodable, + PrintAttribute +)] +#[derive(HashStable_Generic)] +pub enum NativeLibKind { + /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) + Static { + /// Whether to bundle objects from static library into produced rlib + bundle: Option, + /// Whether to link static library without throwing any object files away + whole_archive: Option, + }, + /// Dynamic library (e.g. `libfoo.so` on Linux) + /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC). + Dylib { + /// Whether the dynamic library will be linked only if it satisfies some undefined symbols + as_needed: Option, + }, + /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library. + /// On Linux, it refers to a generated shared library stub. + RawDylib, + /// A macOS-specific kind of dynamic libraries. + Framework { + /// Whether the framework will be linked only if it satisfies some undefined symbols + as_needed: Option, + }, + /// Argument which is passed to linker, relative order with libraries and other arguments + /// is preserved + LinkArg, + + /// Module imported from WebAssembly + WasmImportModule, + + /// The library kind wasn't specified, `Dylib` is currently used as a default. + Unspecified, +} + +impl NativeLibKind { + pub fn has_modifiers(&self) -> bool { + match self { + NativeLibKind::Static { bundle, whole_archive } => { + bundle.is_some() || whole_archive.is_some() + } + NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } => { + as_needed.is_some() + } + NativeLibKind::RawDylib + | NativeLibKind::Unspecified + | NativeLibKind::LinkArg + | NativeLibKind::WasmImportModule => false, + } + } + + pub fn is_statically_included(&self) -> bool { + matches!(self, NativeLibKind::Static { .. }) + } + + pub fn is_dllimport(&self) -> bool { + matches!( + self, + NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified + ) + } +} + +#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)] +pub struct LinkEntry { + pub span: Span, + pub kind: NativeLibKind, + pub name: Symbol, + pub cfg: Option<()>, //TODO + pub verbatim: Option, + pub import_name_type: Option<(PeImportNameType, Span)>, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -256,6 +370,9 @@ pub enum AttributeKind { /// Represents `#[link_name]`. LinkName { name: Symbol, span: Span }, + /// Represents `#[link]`. + Link(ThinVec), + /// Represents `#[loop_match]`. LoopMatch(Span), diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 0d6ee77c718d3..2727de7f03b95 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -29,6 +29,7 @@ impl AttributeKind { Stability { .. } => Yes, Cold(..) => No, ConstContinue(..) => No, + Link(..) => No, LinkName { .. } => Yes, LoopMatch(..) => No, MayDangle(..) => No, diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 32029137268ba..0fc38a661ca25 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -17,5 +17,6 @@ rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } thin-vec = "0.2.12" # tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 39652335f5553..d30e02bf82d5f 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -149,3 +149,337 @@ attr_parsing_unused_multiple = -attr_parsing_perviously_accepted = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + + +attr_parsing_as_needed_compatibility = + linking modifier `as-needed` is only compatible with `dylib` and `framework` linking kinds + +attr_parsing_async_drop_types_in_dependency = + found async drop types in dependency `{$extern_crate}`, but async_drop feature is disabled for `{$local_crate}` + .help = if async drop type will be dropped in a crate without `feature(async_drop)`, sync Drop will be used + +attr_parsing_bad_panic_strategy = + the linked panic runtime `{$runtime}` is not compiled with this crate's panic strategy `{$strategy}` + +attr_parsing_binary_output_to_tty = + option `-o` or `--emit` is used to write binary output type `metadata` to stdout, but stdout is a tty + +attr_parsing_bundle_needs_static = + linking modifier `bundle` is only compatible with `static` linking kind + +attr_parsing_cannot_find_crate = + can't find crate for `{$crate_name}`{$add_info} + +attr_parsing_cant_find_crate = + can't find crate + +attr_parsing_compiler_missing_profiler = + the compiler may have been built without the profiler runtime + +attr_parsing_conflicting_alloc_error_handler = + the `#[alloc_error_handler]` in {$other_crate_name} conflicts with allocation error handler in: {$crate_name} + +attr_parsing_conflicting_global_alloc = + the `#[global_allocator]` in {$other_crate_name} conflicts with global allocator in: {$crate_name} + +attr_parsing_consider_adding_std = + consider adding the standard library to the sysroot with `x build library --target {$locator_triple}` + +attr_parsing_consider_building_std = + consider building the standard library from source with `cargo build -Zbuild-std` + +attr_parsing_consider_downloading_target = + consider downloading the target with `rustup target add {$locator_triple}` + +attr_parsing_crate_dep_multiple = + cannot satisfy dependencies so `{$crate_name}` only shows up once + .help = having upstream crates all available in one format will likely make this go away + +attr_parsing_crate_dep_not_static = + `{$crate_name}` was unavailable as a static crate, preventing fully static linking + +attr_parsing_crate_dep_rustc_driver = + `feature(rustc_private)` is needed to link to the compiler's `rustc_driver` library + +attr_parsing_crate_location_unknown_type = + extern location for {$crate_name} is of an unknown type: {$path} + +attr_parsing_crate_not_compiler_builtins = + the crate `{$crate_name}` resolved as `compiler_builtins` but is not `#![compiler_builtins]` + +attr_parsing_crate_not_panic_runtime = + the crate `{$crate_name}` is not a panic runtime + +attr_parsing_dl_error = + {$path}{$err} + +attr_parsing_empty_link_name = + link name must not be empty + .label = empty link name + +attr_parsing_empty_renaming_target = + an empty renaming target was specified for library `{$lib_name}` + +attr_parsing_extern_location_not_exist = + extern location for {$crate_name} does not exist: {$location} + +attr_parsing_extern_location_not_file = + extern location for {$crate_name} is not a file: {$location} + +attr_parsing_fail_create_file_encoder = + failed to create file encoder: {$err} + +attr_parsing_fail_write_file = + failed to write to `{$path}`: {$err} + +attr_parsing_failed_copy_to_stdout = + failed to copy {$filename} to stdout: {$err} + +attr_parsing_failed_create_encoded_metadata = + failed to create encoded metadata from file: {$err} + +attr_parsing_failed_create_file = + failed to create the file {$filename}: {$err} + +attr_parsing_failed_create_tempdir = + couldn't create a temp dir: {$err} + +attr_parsing_failed_write_error = + failed to write {$filename}: {$err} + +attr_parsing_found_crate_versions = + the following crate versions were found:{$found_crates} + +attr_parsing_found_staticlib = + found staticlib `{$crate_name}` instead of rlib or dylib{$add_info} + .help = please recompile that crate using --crate-type lib + +attr_parsing_full_metadata_not_found = + only metadata stub found for `{$flavor}` dependency `{$crate_name}` + please provide path to the corresponding .rmeta file with full metadata + +attr_parsing_global_alloc_required = + no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait + +attr_parsing_import_name_type_form = + import name type must be of the form `import_name_type = "string"` + +attr_parsing_import_name_type_raw = + import name type can only be used with link kind `raw-dylib` + +attr_parsing_import_name_type_x86 = + import name type is only supported on x86 + +attr_parsing_incompatible_panic_in_drop_strategy = + the crate `{$crate_name}` is compiled with the panic-in-drop strategy `{$found_strategy}` which is incompatible with this crate's strategy of `{$desired_strategy}` + +attr_parsing_incompatible_rustc = + found crate `{$crate_name}` compiled by an incompatible version of rustc{$add_info} + .help = please recompile that crate using this compiler ({$rustc_version}) (consider running `cargo clean` first) + +attr_parsing_incompatible_target_modifiers = + mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` + .note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}` + .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely +attr_parsing_incompatible_target_modifiers_help_allow = if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error +attr_parsing_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$extern_value}` in this crate or `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}` + +attr_parsing_incompatible_target_modifiers_help_fix_l_missed = set `{$flag_name_prefixed}={$extern_value}` in this crate or unset `{$flag_name_prefixed}` in `{$extern_crate}` + +attr_parsing_incompatible_target_modifiers_help_fix_r_missed = unset `{$flag_name_prefixed}` in this crate or set `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}` + +attr_parsing_incompatible_target_modifiers_l_missed = + mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` + .note = unset `{$flag_name_prefixed}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}` + .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely +attr_parsing_incompatible_target_modifiers_r_missed = + mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` + .note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with unset `{$flag_name_prefixed}` in dependency `{$extern_crate}` + .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely +attr_parsing_incompatible_wasm_link = + `wasm_import_module` is incompatible with other arguments in `#[link]` attributes + +attr_parsing_install_missing_components = + maybe you need to install the missing components with: `rustup component add rust-src rustc-dev llvm-tools-preview` + +attr_parsing_invalid_link_modifier = + invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed + +attr_parsing_invalid_meta_files = + found invalid metadata files for crate `{$crate_name}`{$add_info} + +attr_parsing_lib_filename_form = + file name should be lib*.rlib or {$dll_prefix}*{$dll_suffix} + +attr_parsing_lib_framework_apple = + library kind `framework` is only supported on Apple targets + +attr_parsing_lib_required = + crate `{$crate_name}` required to be available in {$kind} format, but was not found in this form + +attr_parsing_link_arg_unstable = + link kind `link-arg` is unstable + +attr_parsing_link_cfg_form = + link cfg must be of the form `cfg(/* predicate */)` + +attr_parsing_link_cfg_single_predicate = + link cfg must have a single predicate argument + +attr_parsing_link_cfg_unstable = + link cfg is unstable + +attr_parsing_link_framework_apple = + link kind `framework` is only supported on Apple targets + +attr_parsing_link_kind_form = + link kind must be of the form `kind = "string"` + +attr_parsing_link_modifiers_form = + link modifiers must be of the form `modifiers = "string"` + +attr_parsing_link_name_form = + link name must be of the form `name = "string"` + +attr_parsing_link_ordinal_raw_dylib = + `#[link_ordinal]` is only supported if link kind is `raw-dylib` + +attr_parsing_link_requires_name = + `#[link]` attribute requires a `name = "string"` argument + .label = missing `name` argument + +attr_parsing_missing_native_library = + could not find native static library `{$libname}`, perhaps an -L flag is missing? + +attr_parsing_multiple_candidates = + multiple candidates for `{$flavor}` dependency `{$crate_name}` found + +attr_parsing_multiple_cfgs = + multiple `cfg` arguments in a single `#[link]` attribute + +attr_parsing_multiple_import_name_type = + multiple `import_name_type` arguments in a single `#[link]` attribute + +attr_parsing_multiple_kinds_in_link = + multiple `kind` arguments in a single `#[link]` attribute + +attr_parsing_multiple_link_modifiers = + multiple `modifiers` arguments in a single `#[link]` attribute + +attr_parsing_multiple_modifiers = + multiple `{$modifier}` modifiers in a single `modifiers` argument + +attr_parsing_multiple_names_in_link = + multiple `name` arguments in a single `#[link]` attribute + +attr_parsing_multiple_renamings = + multiple renamings were specified for library `{$lib_name}` + +attr_parsing_multiple_wasm_import = + multiple `wasm_import_module` arguments in a single `#[link]` attribute + +attr_parsing_newer_crate_version = + found possibly newer version of crate `{$crate_name}`{$add_info} + .note = perhaps that crate needs to be recompiled? + +attr_parsing_no_crate_with_triple = + couldn't find crate `{$crate_name}` with expected target triple {$locator_triple}{$add_info} + +attr_parsing_no_link_mod_override = + overriding linking modifiers from command line is not supported + +attr_parsing_no_multiple_alloc_error_handler = + cannot define multiple allocation error handlers + .label = cannot define a new allocation error handler + +attr_parsing_no_multiple_global_alloc = + cannot define multiple global allocators + .label = cannot define a new global allocator + +attr_parsing_no_panic_strategy = + the crate `{$crate_name}` does not have the panic strategy `{$strategy}` + +attr_parsing_no_transitive_needs_dep = + the crate `{$crate_name}` cannot depend on a crate that needs {$needs_crate_name}, but it depends on `{$deps_crate_name}` + +attr_parsing_non_ascii_name = + cannot load a crate with a non-ascii name `{$crate_name}` + +attr_parsing_not_profiler_runtime = + the crate `{$crate_name}` is not a profiler runtime + +attr_parsing_only_provide_library_name = only provide the library name `{$suggested_name}`, not the full filename + +attr_parsing_prev_alloc_error_handler = + previous allocation error handler defined here + +attr_parsing_prev_global_alloc = + previous global allocator defined here + +attr_parsing_raw_dylib_elf_unstable = + link kind `raw-dylib` is unstable on ELF platforms + +attr_parsing_raw_dylib_no_nul = + link name must not contain NUL characters if link kind is `raw-dylib` + +attr_parsing_raw_dylib_only_windows = + link kind `raw-dylib` is only supported on Windows targets + +attr_parsing_raw_dylib_unsupported_abi = + ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture + +attr_parsing_renaming_no_link = + renaming of the library `{$lib_name}` was specified, however this crate contains no `#[link(...)]` attributes referencing this library + +attr_parsing_required_panic_strategy = + the crate `{$crate_name}` requires panic strategy `{$found_strategy}` which is incompatible with this crate's strategy of `{$desired_strategy}` + +attr_parsing_rlib_required = + crate `{$crate_name}` required to be available in rlib format, but was not found in this form + +attr_parsing_rustc_lib_required = + crate `{$crate_name}` required to be available in {$kind} format, but was not found in this form + .note = only .rmeta files are distributed for `rustc_private` crates other than `rustc_driver` + .help = try adding `extern crate rustc_driver;` at the top level of this crate + +attr_parsing_stable_crate_id_collision = + found crates (`{$crate_name0}` and `{$crate_name1}`) with colliding StableCrateId values + +attr_parsing_std_required = + `std` is required by `{$current_crate}` because it does not declare `#![no_std]` + +attr_parsing_symbol_conflicts_current = + the current crate is indistinguishable from one of its dependencies: it has the same crate-name `{$crate_name}` and was compiled with the same `-C metadata` arguments, so this will result in symbol conflicts between the two + +attr_parsing_target_no_std_support = + the `{$locator_triple}` target may not support the standard library + +attr_parsing_target_not_installed = + the `{$locator_triple}` target may not be installed + +attr_parsing_two_panic_runtimes = + cannot link together two panic runtimes: {$prev_name} and {$cur_name} + +attr_parsing_unexpected_link_arg = + unexpected `#[link]` argument, expected one of: name, kind, modifiers, cfg, wasm_import_module, import_name_type + +attr_parsing_unknown_import_name_type = + unknown import name type `{$import_name_type}`, expected one of: decorated, noprefix, undecorated + +attr_parsing_unknown_link_kind = + unknown link kind `{$kind}`, expected one of: static, dylib, framework, raw-dylib, link-arg + .label = unknown link kind + +attr_parsing_unknown_link_modifier = + unknown linking modifier `{$modifier}`, expected one of: bundle, verbatim, whole-archive, as-needed + +attr_parsing_unknown_target_modifier_unsafe_allowed = unknown target modifier `{$flag_name}`, requested by `-Cunsafe-allow-abi-mismatch={$flag_name}` + +attr_parsing_wasm_c_abi = + older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88 + +attr_parsing_wasm_import_form = + wasm import module must be of the form `wasm_import_module = "string"` + +attr_parsing_whole_archive_needs_static = + linking modifier `whole-archive` is only compatible with `static` linking kind diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 740222178ed58..ccb77d206d51f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,11 +1,17 @@ -use rustc_attr_data_structures::AttributeKind; use rustc_attr_data_structures::AttributeKind::LinkName; +use rustc_attr_data_structures::{AttributeKind, LinkEntry, NativeLibKind, PeImportNameType}; use rustc_feature::{AttributeTemplate, template}; +use rustc_session::parse::feature_err; use rustc_span::{Symbol, sym}; +use rustc_target::spec::BinaryFormat; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::attributes::{ + AttributeOrder, CombineAttributeParser, ConvertFn, OnDuplicate, SingleAttributeParser, +}; use crate::context::{AcceptContext, Stage}; +use crate::fluent_generated; use crate::parser::ArgParser; +use crate::session_diagnostics::*; pub(crate) struct LinkNameParser; @@ -28,3 +34,308 @@ impl SingleAttributeParser for LinkNameParser { Some(LinkName { name, span: cx.attr_span }) } } +pub(crate) struct LinkParser; + +impl CombineAttributeParser for LinkParser { + type Item = LinkEntry; + const PATH: &[Symbol] = &[sym::link]; + const CONVERT: ConvertFn = AttributeKind::Link; + const TEMPLATE: AttributeTemplate = template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#); + + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator + 'c { + let mut result = None; + let Some(items) = args.list() else { + return result; + }; + + let sess = cx.sess(); + let features = cx.features(); + + let mut name = None; + let mut kind = None; + let mut modifiers = None; + let cfg = None::<()>; + let mut wasm_import_module = None; + let mut import_name_type = None; + for item in items.mixed() { + let Some(item) = item.meta_item() else { + cx.unexpected_literal(item.span()); + return result; + }; + + match item.path().single_segment().map(|ident| ident.name) { + Some(sym::name) => { + if name.is_some() { + cx.emit_err(MultipleNamesInLink { span: item.span() }); + return result; + } + let Some((link_name, link_name_span)) = item.args().name_value_str() else { + cx.emit_err(LinkNameForm { span: item.span() }); + return result; + }; + if link_name.is_empty() { + cx.emit_err(EmptyLinkName { span: link_name_span }); + } + name = Some((link_name, link_name_span)); + } + Some(sym::kind) => { + if kind.is_some() { + cx.emit_err(MultipleKindsInLink { span: item.span() }); + return result; + } + let Some((link_kind, link_kind_span)) = item.args().name_value_str() else { + cx.emit_err(LinkKindForm { span: item.span() }); + return result; + }; + + let link_kind = match link_kind.as_str() { + "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, + "dylib" => NativeLibKind::Dylib { as_needed: None }, + "framework" => { + if !sess.target.is_like_darwin { + cx.emit_err(LinkFrameworkApple { span: link_kind_span }); + } + NativeLibKind::Framework { as_needed: None } + } + "raw-dylib" => { + if sess.target.is_like_windows { + // raw-dylib is stable and working on Windows + } else if sess.target.binary_format == BinaryFormat::Elf + && features.raw_dylib_elf() + { + // raw-dylib is unstable on ELF, but the user opted in + } else if sess.target.binary_format == BinaryFormat::Elf + && sess.is_nightly_build() + { + feature_err( + sess, + sym::raw_dylib_elf, + link_kind_span, + fluent_generated::attr_parsing_raw_dylib_elf_unstable, + ) + .emit(); + } else { + cx.emit_err(RawDylibOnlyWindows { span: link_kind_span }); + } + + NativeLibKind::RawDylib + } + "link-arg" => { + if !features.link_arg_attribute() { + feature_err( + sess, + sym::link_arg_attribute, + link_kind_span, + fluent_generated::attr_parsing_link_arg_unstable, + ) + .emit(); + } + NativeLibKind::LinkArg + } + kind => { + cx.emit_err(UnknownLinkKind { span: link_kind_span, kind }); + return result; + } + }; + kind = Some(link_kind); + } + Some(sym::modifiers) => { + if modifiers.is_some() { + cx.emit_err(MultipleLinkModifiers { span: item.span() }); + return result; + } + let Some((link_modifiers, link_modifiers_span)) = item.args().name_value_str() + else { + cx.emit_err(LinkModifiersForm { span: item.span() }); + return result; + }; + modifiers = Some((link_modifiers, link_modifiers_span)); + } + Some(sym::cfg) => { + if cfg.is_some() { + cx.emit_err(MultipleCfgs { span: item.span() }); + return result; + } + let Some(link_cfg) = item.args().list() else { + cx.emit_err(LinkCfgForm { span: item.span() }); + return result; + }; + let Some(link_cfg) = link_cfg.single() else { + cx.emit_err(LinkCfgSinglePredicate { span: item.span() }); + return result; + }; + + //TODO outsource this to the #[cfg] parser + // let link_cfg = match link_cfg { + // MetaItemOrLitParser::MetaItemParser(_item) => Some(self), + // MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. }) => Some(self), + // _ => None, + // }; + // + // let Some(link_cfg) = link_cfg.meta_item_or_bool() else { + // cx + // .emit_err(LinkCfgSinglePredicate { span: item.span() }); + // return result; + // }; + // if !features.link_cfg() { + // feature_err( + // sess, + // sym::link_cfg, + // item.span(), + // fluent_generated::attr_parsing_link_cfg_unstable, + // ) + // .emit(); + // } + // cfg = Some(link_cfg.clone()); + } + Some(sym::wasm_import_module) => { + if wasm_import_module.is_some() { + cx.emit_err(MultipleWasmImport { span: item.span() }); + return result; + } + let Some((link_wasm_import_module, _)) = item.args().name_value_str() else { + cx.emit_err(WasmImportForm { span: item.span() }); + return result; + }; + wasm_import_module = Some((link_wasm_import_module, item.span())); + } + Some(sym::import_name_type) => { + if import_name_type.is_some() { + cx.emit_err(MultipleImportNameType { span: item.span() }); + return result; + } + let Some((link_import_name_type, _)) = item.args().name_value_str() else { + cx.emit_err(ImportNameTypeForm { span: item.span() }); + return result; + }; + if cx.sess().target.arch != "x86" { + cx.emit_err(ImportNameTypeX86 { span: item.span() }); + return result; + } + + let link_import_name_type = match link_import_name_type.as_str() { + "decorated" => PeImportNameType::Decorated, + "noprefix" => PeImportNameType::NoPrefix, + "undecorated" => PeImportNameType::Undecorated, + import_name_type => { + cx.emit_err(UnknownImportNameType { + span: item.span(), + import_name_type, + }); + return result; + } + }; + import_name_type = Some((link_import_name_type, item.span())); + } + _ => { + cx.emit_err(UnexpectedLinkArg { span: item.span() }); + } + } + } + + // Do this outside the above loop so we don't depend on modifiers coming after kinds + let mut verbatim = None; + if let Some((modifiers, span)) = modifiers { + for modifier in modifiers.as_str().split(',') { + let (modifier, value) = match modifier.strip_prefix(&['+', '-']) { + Some(m) => (m, modifier.starts_with('+')), + None => { + cx.emit_err(InvalidLinkModifier { span }); + return result; + } + }; + + macro report_unstable_modifier($feature: ident) { + if !features.$feature() { + // FIXME: make this translatable + #[expect(rustc::untranslatable_diagnostic)] + feature_err( + sess, + sym::$feature, + span, + format!("linking modifier `{modifier}` is unstable"), + ) + .emit(); + } + } + let assign_modifier = |dst: &mut Option| { + if dst.is_some() { + cx.emit_err(MultipleModifiers { span, modifier }); + } else { + *dst = Some(value); + } + }; + match (modifier, &mut kind) { + ("bundle", Some(NativeLibKind::Static { bundle, .. })) => { + assign_modifier(bundle) + } + ("bundle", _) => { + cx.emit_err(BundleNeedsStatic { span }); + } + + ("verbatim", _) => assign_modifier(&mut verbatim), + + ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => { + assign_modifier(whole_archive) + } + ("whole-archive", _) => { + cx.emit_err(WholeArchiveNeedsStatic { span }); + } + + ("as-needed", Some(NativeLibKind::Dylib { as_needed })) + | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => { + report_unstable_modifier!(native_link_modifiers_as_needed); + assign_modifier(as_needed) + } + ("as-needed", _) => { + cx.emit_err(AsNeededCompatibility { span }); + } + + _ => { + cx.emit_err(UnknownLinkModifier { span, modifier }); + } + } + } + } + + if let Some((_, span)) = wasm_import_module { + if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() { + cx.emit_err(IncompatibleWasmLink { span }); + } + } + + if wasm_import_module.is_some() { + (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule)); + } + let Some((name, name_span)) = name else { + cx.emit_err(LinkRequiresName { span: cx.attr_span }); + return result; + }; + + // Do this outside of the loop so that `import_name_type` can be specified before `kind`. + if let Some((_, span)) = import_name_type { + if kind != Some(NativeLibKind::RawDylib) { + cx.emit_err(ImportNameTypeRaw { span }); + } + } + + if let Some(NativeLibKind::RawDylib) = kind + && name.as_str().contains('\0') + { + cx.emit_err(RawDylibNoNul { span: name_span }); + } + + result = Some(LinkEntry { + span: cx.attr_span, + kind: kind.unwrap_or(NativeLibKind::Unspecified), + name, + cfg, + verbatim, + import_name_type, + }); + result + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 1ac41b9c7e8b5..0800a0cd48b04 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -22,7 +22,7 @@ use crate::attributes::codegen_attrs::{ use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; -use crate::attributes::link_attrs::LinkNameParser; +use crate::attributes::link_attrs::{LinkNameParser, LinkParser}; use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser}; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::must_use::MustUseParser; @@ -111,6 +111,7 @@ attribute_parsers!( // tidy-alphabetical-start Combine, Combine, + Combine, Combine, // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 47eeb63bad3db..b5396212c9ea3 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -80,6 +80,7 @@ #![allow(internal_features)] #![doc(rust_logo)] #![feature(rustdoc_internals)] +#![feature(decl_macro)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index aecaae947c99b..4c36b85ebec9d 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -55,6 +55,15 @@ impl<'a> PathParser<'a> { SegmentIterator { offset: 0, path: self } } + pub fn single_segment(&'a self) -> Option { + let mut segments = self.segments(); + let single = segments.next()?; + if segments.next().is_some() { + return None; + } + Some(*single) + } + pub fn span(&self) -> Span { match self { PathParser::Ast(path) => path.span, @@ -169,6 +178,15 @@ impl<'a> ArgParser<'a> { } } + /// Asserts that this MetaItem is a name-value pair, and that the value is a string. + /// Returns the value and the span of the value. + pub fn name_value_str(&self) -> Option<(Symbol, Span)> { + match self { + Self::NameValue(n) => n.value_as_str().map(|s| (s, n.value_span)), + Self::List(_) | Self::NoArgs => None, + } + } + /// Assert that there were no args. /// If there were, get a span to the arguments /// (to pass to [`AcceptContext::expected_no_args`](crate::context::AcceptContext::expected_no_args)). diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 7cfce5799792a..54b26115d160c 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -620,3 +620,220 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { diag } } + +#[derive(Diagnostic)] +#[diag(attr_parsing_multiple_names_in_link)] +pub(crate) struct MultipleNamesInLink { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_link_name_form)] +pub(crate) struct LinkNameForm { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_empty_link_name, code = E0454)] +pub(crate) struct EmptyLinkName { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_multiple_kinds_in_link)] +pub(crate) struct MultipleKindsInLink { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_link_kind_form)] +pub(crate) struct LinkKindForm { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_link_framework_apple, code = E0455)] +pub(crate) struct LinkFrameworkApple { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_unknown_link_modifier)] +pub(crate) struct UnknownLinkModifier<'a> { + #[primary_span] + pub span: Span, + pub modifier: &'a str, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_incompatible_wasm_link)] +pub(crate) struct IncompatibleWasmLink { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_link_requires_name, code = E0459)] +pub(crate) struct LinkRequiresName { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_raw_dylib_no_nul)] +pub(crate) struct RawDylibNoNul { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_raw_dylib_only_windows, code = E0455)] +pub(crate) struct RawDylibOnlyWindows { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_unknown_link_kind, code = E0458)] +pub(crate) struct UnknownLinkKind<'a> { + #[primary_span] + #[label] + pub span: Span, + pub kind: &'a str, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_multiple_link_modifiers)] +pub(crate) struct MultipleLinkModifiers { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_multiple_cfgs)] +pub(crate) struct MultipleCfgs { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_link_cfg_single_predicate)] +pub(crate) struct LinkCfgSinglePredicate { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_multiple_wasm_import)] +pub(crate) struct MultipleWasmImport { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_unexpected_link_arg)] +pub(crate) struct UnexpectedLinkArg { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_invalid_link_modifier)] +pub(crate) struct InvalidLinkModifier { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_multiple_modifiers)] +pub(crate) struct MultipleModifiers<'a> { + #[primary_span] + pub span: Span, + pub modifier: &'a str, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_link_modifiers_form)] +pub(crate) struct LinkModifiersForm { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_link_cfg_form)] +pub(crate) struct LinkCfgForm { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_wasm_import_form)] +pub(crate) struct WasmImportForm { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_multiple_import_name_type)] +pub(crate) struct MultipleImportNameType { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_import_name_type_form)] +pub(crate) struct ImportNameTypeForm { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_import_name_type_x86)] +pub(crate) struct ImportNameTypeX86 { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_unknown_import_name_type)] +pub(crate) struct UnknownImportNameType<'a> { + #[primary_span] + pub span: Span, + pub import_name_type: &'a str, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_bundle_needs_static)] +pub(crate) struct BundleNeedsStatic { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_whole_archive_needs_static)] +pub(crate) struct WholeArchiveNeedsStatic { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_as_needed_compatibility)] +pub(crate) struct AsNeededCompatibility { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_import_name_type_raw)] +pub(crate) struct ImportNameTypeRaw { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 4a2425967e4fa..579df0aeb5fee 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -14,6 +14,7 @@ use itertools::Itertools; use regex::Regex; use rustc_arena::TypedArena; use rustc_ast::CRATE_NODE_ID; +use rustc_attr_data_structures::NativeLibKind; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; @@ -38,7 +39,6 @@ use rustc_session::config::{ use rustc_session::lint::builtin::LINKER_MESSAGES; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::search_paths::PathKind; -use rustc_session::utils::NativeLibKind; /// For all the linkers we support, and information they might /// need out of the shared crate context before we get rid of it. use rustc_session::{Session, filesearch}; diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs index 74f39022afb7b..8e03c101d5446 100644 --- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -3,13 +3,13 @@ use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; use rustc_abi::Endian; +use rustc_attr_data_structures::NativeLibKind; use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::StableHasher; use rustc_hashes::Hash128; use rustc_session::Session; use rustc_session::cstore::DllImport; -use rustc_session::utils::NativeLibKind; use rustc_span::Symbol; use crate::back::archive::ImportLibraryItem; diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 48565e0b4de47..dcb6067c8ee4b 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -1,10 +1,11 @@ #![allow(non_camel_case_types)] +use rustc_attr_data_structures::PeImportNameType; use rustc_hir::LangItem; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; -use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType}; +use rustc_session::cstore::{DllCallingConvention, DllImport}; use rustc_span::Span; use rustc_target::spec::Target; diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 23ed387a3ff97..2f828c83d4767 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -26,6 +26,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use rustc_ast as ast; +use rustc_attr_data_structures::NativeLibKind; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordMap; use rustc_hir::CRATE_HIR_ID; @@ -45,7 +46,6 @@ use rustc_session::Session; use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc_session::cstore::{self, CrateSource}; use rustc_session::lint::builtin::LINKER_MESSAGES; -use rustc_session::utils::NativeLibKind; use rustc_span::Symbol; pub mod assert_module_sources; diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index f10d71f4c6540..3f4c42ad5a443 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -3,23 +3,20 @@ use std::path::{Path, PathBuf}; use rustc_abi::ExternAbi; use rustc_ast::CRATE_NODE_ID; +use rustc_attr_data_structures::{AttributeKind, NativeLibKind, PeImportNameType, find_attr}; use rustc_attr_parsing as attr; use rustc_data_structures::fx::FxHashSet; use rustc_middle::query::LocalCrate; use rustc_middle::ty::{self, List, Ty, TyCtxt}; use rustc_session::Session; use rustc_session::config::CrateType; -use rustc_session::cstore::{ - DllCallingConvention, DllImport, ForeignModule, NativeLib, PeImportNameType, -}; -use rustc_session::parse::feature_err; +use rustc_session::cstore::{DllCallingConvention, DllImport, ForeignModule, NativeLib}; use rustc_session::search_paths::PathKind; -use rustc_session::utils::NativeLibKind; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::{Symbol, sym}; -use rustc_target::spec::{BinaryFormat, LinkSelfContainedComponents}; +use rustc_target::spec::LinkSelfContainedComponents; -use crate::{errors, fluent_generated}; +use crate::errors; /// The fallback directories are passed to linker, but not used when rustc does the search, /// because in the latter case the set of fallback directories cannot always be determined @@ -211,289 +208,22 @@ impl<'tcx> Collector<'tcx> { return; } - // Process all of the #[link(..)]-style arguments - let features = self.tcx.features(); - - for m in self.tcx.get_attrs(def_id, sym::link) { - let Some(items) = m.meta_item_list() else { - continue; - }; - - let mut name = None; - let mut kind = None; - let mut modifiers = None; - let mut cfg = None; - let mut wasm_import_module = None; - let mut import_name_type = None; - for item in items.iter() { - match item.name() { - Some(sym::name) => { - if name.is_some() { - sess.dcx().emit_err(errors::MultipleNamesInLink { span: item.span() }); - continue; - } - let Some(link_name) = item.value_str() else { - sess.dcx().emit_err(errors::LinkNameForm { span: item.span() }); - continue; - }; - let span = item.name_value_literal_span().unwrap(); - if link_name.is_empty() { - sess.dcx().emit_err(errors::EmptyLinkName { span }); - } - name = Some((link_name, span)); - } - Some(sym::kind) => { - if kind.is_some() { - sess.dcx().emit_err(errors::MultipleKindsInLink { span: item.span() }); - continue; - } - let Some(link_kind) = item.value_str() else { - sess.dcx().emit_err(errors::LinkKindForm { span: item.span() }); - continue; - }; - - let span = item.name_value_literal_span().unwrap(); - let link_kind = match link_kind.as_str() { - "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, - "dylib" => NativeLibKind::Dylib { as_needed: None }, - "framework" => { - if !sess.target.is_like_darwin { - sess.dcx().emit_err(errors::LinkFrameworkApple { span }); - } - NativeLibKind::Framework { as_needed: None } - } - "raw-dylib" => { - if sess.target.is_like_windows { - // raw-dylib is stable and working on Windows - } else if sess.target.binary_format == BinaryFormat::Elf - && features.raw_dylib_elf() - { - // raw-dylib is unstable on ELF, but the user opted in - } else if sess.target.binary_format == BinaryFormat::Elf - && sess.is_nightly_build() - { - feature_err( - sess, - sym::raw_dylib_elf, - span, - fluent_generated::metadata_raw_dylib_elf_unstable, - ) - .emit(); - } else { - sess.dcx().emit_err(errors::RawDylibOnlyWindows { span }); - } - - NativeLibKind::RawDylib - } - "link-arg" => { - if !features.link_arg_attribute() { - feature_err( - sess, - sym::link_arg_attribute, - span, - fluent_generated::metadata_link_arg_unstable, - ) - .emit(); - } - NativeLibKind::LinkArg - } - kind => { - sess.dcx().emit_err(errors::UnknownLinkKind { span, kind }); - continue; - } - }; - kind = Some(link_kind); - } - Some(sym::modifiers) => { - if modifiers.is_some() { - sess.dcx() - .emit_err(errors::MultipleLinkModifiers { span: item.span() }); - continue; - } - let Some(link_modifiers) = item.value_str() else { - sess.dcx().emit_err(errors::LinkModifiersForm { span: item.span() }); - continue; - }; - modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap())); - } - Some(sym::cfg) => { - if cfg.is_some() { - sess.dcx().emit_err(errors::MultipleCfgs { span: item.span() }); - continue; - } - let Some(link_cfg) = item.meta_item_list() else { - sess.dcx().emit_err(errors::LinkCfgForm { span: item.span() }); - continue; - }; - let [link_cfg] = link_cfg else { - sess.dcx() - .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); - continue; - }; - let Some(link_cfg) = link_cfg.meta_item_or_bool() else { - sess.dcx() - .emit_err(errors::LinkCfgSinglePredicate { span: item.span() }); - continue; - }; - if !features.link_cfg() { - feature_err( - sess, - sym::link_cfg, - item.span(), - fluent_generated::metadata_link_cfg_unstable, - ) - .emit(); - } - cfg = Some(link_cfg.clone()); - } - Some(sym::wasm_import_module) => { - if wasm_import_module.is_some() { - sess.dcx().emit_err(errors::MultipleWasmImport { span: item.span() }); - continue; - } - let Some(link_wasm_import_module) = item.value_str() else { - sess.dcx().emit_err(errors::WasmImportForm { span: item.span() }); - continue; - }; - wasm_import_module = Some((link_wasm_import_module, item.span())); - } - Some(sym::import_name_type) => { - if import_name_type.is_some() { - sess.dcx() - .emit_err(errors::MultipleImportNameType { span: item.span() }); - continue; - } - let Some(link_import_name_type) = item.value_str() else { - sess.dcx().emit_err(errors::ImportNameTypeForm { span: item.span() }); - continue; - }; - if self.tcx.sess.target.arch != "x86" { - sess.dcx().emit_err(errors::ImportNameTypeX86 { span: item.span() }); - continue; - } - - let link_import_name_type = match link_import_name_type.as_str() { - "decorated" => PeImportNameType::Decorated, - "noprefix" => PeImportNameType::NoPrefix, - "undecorated" => PeImportNameType::Undecorated, - import_name_type => { - sess.dcx().emit_err(errors::UnknownImportNameType { - span: item.span(), - import_name_type, - }); - continue; - } - }; - import_name_type = Some((link_import_name_type, item.span())); - } - _ => { - sess.dcx().emit_err(errors::UnexpectedLinkArg { span: item.span() }); - } - } - } - - // Do this outside the above loop so we don't depend on modifiers coming after kinds - let mut verbatim = None; - if let Some((modifiers, span)) = modifiers { - for modifier in modifiers.as_str().split(',') { - let (modifier, value) = match modifier.strip_prefix(&['+', '-']) { - Some(m) => (m, modifier.starts_with('+')), - None => { - sess.dcx().emit_err(errors::InvalidLinkModifier { span }); - continue; - } - }; - - macro report_unstable_modifier($feature: ident) { - if !features.$feature() { - // FIXME: make this translatable - #[expect(rustc::untranslatable_diagnostic)] - feature_err( - sess, - sym::$feature, - span, - format!("linking modifier `{modifier}` is unstable"), - ) - .emit(); - } - } - let assign_modifier = |dst: &mut Option| { - if dst.is_some() { - sess.dcx().emit_err(errors::MultipleModifiers { span, modifier }); - } else { - *dst = Some(value); - } - }; - match (modifier, &mut kind) { - ("bundle", Some(NativeLibKind::Static { bundle, .. })) => { - assign_modifier(bundle) - } - ("bundle", _) => { - sess.dcx().emit_err(errors::BundleNeedsStatic { span }); - } - - ("verbatim", _) => assign_modifier(&mut verbatim), - - ("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => { - assign_modifier(whole_archive) - } - ("whole-archive", _) => { - sess.dcx().emit_err(errors::WholeArchiveNeedsStatic { span }); - } - - ("as-needed", Some(NativeLibKind::Dylib { as_needed })) - | ("as-needed", Some(NativeLibKind::Framework { as_needed })) => { - report_unstable_modifier!(native_link_modifiers_as_needed); - assign_modifier(as_needed) - } - ("as-needed", _) => { - sess.dcx().emit_err(errors::AsNeededCompatibility { span }); - } - - _ => { - sess.dcx().emit_err(errors::UnknownLinkModifier { span, modifier }); - } - } - } - } - - if let Some((_, span)) = wasm_import_module { - if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() { - sess.dcx().emit_err(errors::IncompatibleWasmLink { span }); - } - } - - if wasm_import_module.is_some() { - (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule)); - } - let Some((name, name_span)) = name else { - sess.dcx().emit_err(errors::LinkRequiresName { span: m.span() }); - continue; - }; - - // Do this outside of the loop so that `import_name_type` can be specified before `kind`. - if let Some((_, span)) = import_name_type { - if kind != Some(NativeLibKind::RawDylib) { - sess.dcx().emit_err(errors::ImportNameTypeRaw { span }); - } - } - - let dll_imports = match kind { - Some(NativeLibKind::RawDylib) => { - if name.as_str().contains('\0') { - sess.dcx().emit_err(errors::RawDylibNoNul { span: name_span }); - } - foreign_items - .iter() - .map(|&child_item| { - self.build_dll_import( - abi, - import_name_type.map(|(import_name_type, _)| import_name_type), - child_item, - ) - }) - .collect() - } + for attr in find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::Link(links) => links) + .iter() + .map(|v| v.iter()) + .flatten() + { + let dll_imports = match attr.kind { + NativeLibKind::RawDylib => foreign_items + .iter() + .map(|&child_item| { + self.build_dll_import( + abi, + attr.import_name_type.map(|(import_name_type, _)| import_name_type), + child_item, + ) + }) + .collect(), _ => { for &child_item in foreign_items { if self.tcx.def_kind(child_item).has_codegen_attrs() @@ -511,15 +241,20 @@ impl<'tcx> Collector<'tcx> { } }; - let kind = kind.unwrap_or(NativeLibKind::Unspecified); - let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx); + let filename = find_bundled_library( + attr.name, + attr.verbatim, + attr.kind, + attr.cfg.is_some(), + self.tcx, + ); self.libs.push(NativeLib { - name, + name: attr.name, filename, - kind, - cfg, + kind: attr.kind, + cfg: None, //TODO foreign_module: Some(def_id.to_def_id()), - verbatim, + verbatim: attr.verbatim, dll_imports, }); } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ae486a58062bf..6d273112ad5f6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -206,6 +206,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::Used { span: attr_span, .. }) => { self.check_used(*attr_span, target, span); } + Attribute::Parsed(AttributeKind::Link(links)) => { + // TODO rebase on target_feature for span + self.check_link(hir_id, links[0].span, span, target) + } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { @@ -285,7 +289,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target), [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target), [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target), - [sym::link, ..] => self.check_link(hir_id, attr, span, target), [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target), [sym::macro_use, ..] | [sym::macro_escape, ..] => { self.check_macro_use(hir_id, attr, target) @@ -1588,7 +1591,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[link]` is applied to an item other than a foreign module. - fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_link(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) { if target == Target::ForeignMod && let hir::Node::Item(item) = self.tcx.hir_node(hir_id) && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item @@ -1600,7 +1603,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::Link { span: (target != Target::ForeignMod).then_some(span) }, ); } diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 5b88a7017c51b..4543643c783cd 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -10,6 +10,7 @@ getopts = "0.2" rand = "0.9.0" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_session/src/config/native_libs.rs b/compiler/rustc_session/src/config/native_libs.rs index f1f0aeb5e599b..dd8effe8cd7c5 100644 --- a/compiler/rustc_session/src/config/native_libs.rs +++ b/compiler/rustc_session/src/config/native_libs.rs @@ -4,11 +4,12 @@ //! (There is also a similar but separate syntax for `#[link]` attributes, //! which have their own parser in `rustc_metadata`.) +use rustc_attr_data_structures::NativeLibKind; use rustc_feature::UnstableFeatures; use crate::EarlyDiagCtxt; use crate::config::UnstableOptions; -use crate::utils::{NativeLib, NativeLibKind}; +use crate::utils::NativeLib; #[cfg(test)] mod tests; diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index 4cfc745dec28a..d43c61760bd51 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -7,6 +7,7 @@ use std::path::PathBuf; use rustc_abi::ExternAbi; use rustc_ast as ast; +use rustc_attr_data_structures::{NativeLibKind, PeImportNameType}; use rustc_data_structures::sync::{self, AppendOnlyIndexVec, FreezeLock}; use rustc_hir::def_id::{ CrateNum, DefId, LOCAL_CRATE, LocalDefId, StableCrateId, StableCrateIdMap, @@ -16,7 +17,6 @@ use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::{Span, Symbol}; use crate::search_paths::PathKind; -use crate::utils::NativeLibKind; // lonely orphan structs and enums looking for a better home @@ -88,25 +88,6 @@ impl NativeLib { } } -/// Different ways that the PE Format can decorate a symbol name. -/// From -#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic, PartialEq, Eq)] -pub enum PeImportNameType { - /// IMPORT_ORDINAL - /// Uses the ordinal (i.e., a number) rather than the name. - Ordinal(u16), - /// Same as IMPORT_NAME - /// Name is decorated with all prefixes and suffixes. - Decorated, - /// Same as IMPORT_NAME_NOPREFIX - /// Prefix (e.g., the leading `_` or `@`) is skipped, but suffix is kept. - NoPrefix, - /// Same as IMPORT_NAME_UNDECORATE - /// Prefix (e.g., the leading `_` or `@`) and suffix (the first `@` and all - /// trailing characters) are skipped. - Undecorated, -} - #[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)] pub struct DllImport { pub name: Symbol, diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index e9ddd66b5e8b3..8d3011e122830 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; use std::sync::OnceLock; +use rustc_attr_data_structures::NativeLibKind; use rustc_data_structures::profiling::VerboseTimingGuard; use rustc_fs_util::try_canonicalize; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; @@ -17,69 +18,6 @@ impl Session { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] -#[derive(HashStable_Generic)] -pub enum NativeLibKind { - /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) - Static { - /// Whether to bundle objects from static library into produced rlib - bundle: Option, - /// Whether to link static library without throwing any object files away - whole_archive: Option, - }, - /// Dynamic library (e.g. `libfoo.so` on Linux) - /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC). - Dylib { - /// Whether the dynamic library will be linked only if it satisfies some undefined symbols - as_needed: Option, - }, - /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library. - /// On Linux, it refers to a generated shared library stub. - RawDylib, - /// A macOS-specific kind of dynamic libraries. - Framework { - /// Whether the framework will be linked only if it satisfies some undefined symbols - as_needed: Option, - }, - /// Argument which is passed to linker, relative order with libraries and other arguments - /// is preserved - LinkArg, - - /// Module imported from WebAssembly - WasmImportModule, - - /// The library kind wasn't specified, `Dylib` is currently used as a default. - Unspecified, -} - -impl NativeLibKind { - pub fn has_modifiers(&self) -> bool { - match self { - NativeLibKind::Static { bundle, whole_archive } => { - bundle.is_some() || whole_archive.is_some() - } - NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } => { - as_needed.is_some() - } - NativeLibKind::RawDylib - | NativeLibKind::Unspecified - | NativeLibKind::LinkArg - | NativeLibKind::WasmImportModule => false, - } - } - - pub fn is_statically_included(&self) -> bool { - matches!(self, NativeLibKind::Static { .. }) - } - - pub fn is_dllimport(&self) -> bool { - matches!( - self, - NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified - ) - } -} - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] #[derive(HashStable_Generic)] pub struct NativeLib {