From 38ee95d96486a3b3ce6c4c4a2373a50817fd37fe Mon Sep 17 00:00:00 2001 From: Anne Stijns Date: Sun, 29 Jun 2025 23:39:09 +0200 Subject: [PATCH] Port #[link_ordinal] to the new attribute parsing infrastructure --- .../src/attributes.rs | 3 ++ .../src/encode_cross_crate.rs | 1 + compiler/rustc_attr_parsing/messages.ftl | 6 +++ .../src/attributes/link_attrs.rs | 52 ++++++++++++++++++- compiler/rustc_attr_parsing/src/context.rs | 3 +- .../src/session_diagnostics.rs | 17 ++++++ compiler/rustc_codegen_ssa/messages.ftl | 6 --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 49 ++--------------- compiler/rustc_codegen_ssa/src/errors.rs | 16 ------ compiler/rustc_metadata/src/native_libs.rs | 10 ++-- compiler/rustc_passes/src/check_attr.rs | 8 +-- .../windows/link-ordinal-missing-argument.rs | 6 ++- .../link-ordinal-missing-argument.stderr | 17 +++--- .../link-ordinal-too-many-arguments.rs | 6 ++- .../link-ordinal-too-many-arguments.stderr | 17 +++--- 15 files changed, 120 insertions(+), 97 deletions(-) diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 5eb1931152d31..3b377d98f75ff 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -256,6 +256,9 @@ pub enum AttributeKind { /// Represents `#[link_name]`. LinkName { name: Symbol, span: Span }, + /// Represents `#[link_ordinal]`. + LinkOrdinal { ordinal: u16, span: Span }, + /// 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..5b18a004b0d11 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -30,6 +30,7 @@ impl AttributeKind { Cold(..) => No, ConstContinue(..) => No, LinkName { .. } => Yes, + LinkOrdinal { .. } => Yes, LoopMatch(..) => No, MayDangle(..) => No, MustUse { .. } => Yes, diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 39652335f5553..3c67c63d95cae 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -28,6 +28,9 @@ attr_parsing_ill_formed_attribute_input = {$num_suggestions -> *[other] valid forms for the attribute are {$suggestions} } +attr_parsing_illegal_link_ordinal_format = illegal ordinal format in `link_ordinal` + .note = an unsuffixed integer value, e.g., `1`, is expected + attr_parsing_incorrect_repr_format_align_one_arg = incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses @@ -74,6 +77,9 @@ attr_parsing_invalid_repr_hint_no_value = attr_parsing_invalid_since = 'since' must be a Rust version number, such as "1.31.0" +attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}` + .note = the value may not exceed `u16::MAX` + attr_parsing_missing_feature = missing 'feature' diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 740222178ed58..ef06ce74cf626 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,11 +1,13 @@ +use rustc_ast::{LitIntType, LitKind, MetaItemLit}; use rustc_attr_data_structures::AttributeKind; -use rustc_attr_data_structures::AttributeKind::LinkName; +use rustc_attr_data_structures::AttributeKind::{LinkName, LinkOrdinal}; use rustc_feature::{AttributeTemplate, template}; use rustc_span::{Symbol, sym}; use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; +use crate::session_diagnostics::{InvalidLinkOrdinalFormat, LinkOrdinalOutOfRange}; pub(crate) struct LinkNameParser; @@ -28,3 +30,51 @@ impl SingleAttributeParser for LinkNameParser { Some(LinkName { name, span: cx.attr_span }) } } + +pub(crate) struct LinkOrdinalParser; + +impl SingleAttributeParser for LinkOrdinalParser { + const PATH: &[Symbol] = &[sym::link_ordinal]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(List: "ordinal"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(arg_list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + + let Some(single_arg) = arg_list.single() else { + cx.expected_single_argument(cx.attr_span); + return None; + }; + + let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = + single_arg.lit() + else { + cx.emit_err(InvalidLinkOrdinalFormat { span: cx.attr_span }); + return None; + }; + + // According to the table at + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the + // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined + // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import + // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t. + // + // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for + // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that + // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import + // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an + // import library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I + // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL -- + // see earlier comment about LINK.EXE failing.) + let Ok(ordinal) = ordinal.get().try_into() else { + cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal: ordinal.get() }); + return None; + }; + + Some(LinkOrdinal { ordinal, span: cx.attr_span }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 1ac41b9c7e8b5..e9b9d44506287 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, LinkOrdinalParser}; use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser}; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::must_use::MustUseParser; @@ -123,6 +123,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 7cfce5799792a..3b487194d1b71 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -500,6 +500,23 @@ pub(crate) struct NakedFunctionIncompatibleAttribute { pub attr: String, } +#[derive(Diagnostic)] +#[diag(attr_parsing_illegal_link_ordinal_format)] +#[note] +pub(crate) struct InvalidLinkOrdinalFormat { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_link_ordinal_out_of_range)] +#[note] +pub(crate) struct LinkOrdinalOutOfRange { + #[primary_span] + pub span: Span, + pub ordinal: u128, +} + pub(crate) enum AttributeParseErrorReason { ExpectedNoArgs, ExpectedStringLiteral { byte_string: Option }, diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 84d6381934358..03b5684a3b276 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -76,9 +76,6 @@ codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extensio codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced -codegen_ssa_illegal_link_ordinal_format = illegal ordinal format in `link_ordinal` - .note = an unsuffixed integer value, e.g., `1`, is expected - codegen_ssa_incorrect_cgu_reuse_type = CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least -> [one] {"at least "} @@ -89,9 +86,6 @@ codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and i codegen_ssa_invalid_instruction_set = invalid instruction set specified -codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]` - .note = the attribute requires exactly one argument - codegen_ssa_invalid_literal_value = invalid literal value .label = value must be an integer between `0` and `255` diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 7680f8e107439..c2549e27dcf25 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -124,6 +124,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name), + AttributeKind::LinkOrdinal { ordinal, span } => { + codegen_fn_attrs.link_ordinal = Some(*ordinal); + link_ordinal_span = Some(*span); + } AttributeKind::NoMangle(attr_span) => { if tcx.opt_item_name(did.to_def_id()).is_some() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; @@ -263,12 +267,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } } - sym::link_ordinal => { - link_ordinal_span = Some(attr.span()); - if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { - codegen_fn_attrs.link_ordinal = ordinal; - } - } sym::no_sanitize => { no_sanitize_span = Some(attr.span()); if let Some(list) = attr.meta_item_list() { @@ -569,45 +567,6 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { false } -fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option { - use rustc_ast::{LitIntType, LitKind, MetaItemLit}; - let meta_item_list = attr.meta_item_list()?; - let [sole_meta_list] = &meta_item_list[..] else { - tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() }); - return None; - }; - if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = - sole_meta_list.lit() - { - // According to the table at - // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the - // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined - // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import - // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t. - // - // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for - // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that - // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import - // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an - // import library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I - // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL -- - // see earlier comment about LINK.EXE failing.) - if *ordinal <= u16::MAX as u128 { - Some(ordinal.get() as u16) - } else { - let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`"); - tcx.dcx() - .struct_span_err(attr.span(), msg) - .with_note("the value may not exceed `u16::MAX`") - .emit(); - None - } - } else { - tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() }); - None - } -} - fn check_link_name_xor_ordinal( tcx: TyCtxt<'_>, codegen_fn_attrs: &CodegenFnAttrs, diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 1950a35b364dd..a4bba08e5ef6f 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1101,22 +1101,6 @@ pub(crate) struct InvalidNoSanitize { pub span: Span, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_link_ordinal_nargs)] -#[note] -pub(crate) struct InvalidLinkOrdinalNargs { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_illegal_link_ordinal_format)] -#[note] -pub(crate) struct InvalidLinkOrdinalFormat { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_target_feature_safe_trait)] pub(crate) struct TargetFeatureSafeTrait { diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index f10d71f4c6540..4d276f814ef25 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use rustc_abi::ExternAbi; use rustc_ast::CRATE_NODE_ID; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_attr_parsing as attr; use rustc_data_structures::fx::FxHashSet; use rustc_middle::query::LocalCrate; @@ -496,14 +497,9 @@ impl<'tcx> Collector<'tcx> { } _ => { for &child_item in foreign_items { - if self.tcx.def_kind(child_item).has_codegen_attrs() - && self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some() + if let Some(span) = find_attr!(self.tcx.get_all_attrs(child_item), AttributeKind::LinkOrdinal {span, ..} => *span) { - let link_ordinal_attr = - self.tcx.get_attr(child_item, sym::link_ordinal).unwrap(); - sess.dcx().emit_err(errors::LinkOrdinalRawDylib { - span: link_ordinal_attr.span(), - }); + sess.dcx().emit_err(errors::LinkOrdinalRawDylib { span }); } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ae486a58062bf..4d0bf60650c6d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -194,6 +194,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::LinkName { span: attr_span, name }) => { self.check_link_name(hir_id, *attr_span, *name, span, target) } + Attribute::Parsed(AttributeKind::LinkOrdinal { span: attr_span, .. }) => { + self.check_link_ordinal(*attr_span, span, target) + } Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => { self.check_may_dangle(hir_id, *attr_span) } @@ -284,7 +287,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, ..] => { @@ -2224,11 +2226,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) { + fn check_link_ordinal(&self, attr_span: Span, _span: Span, target: Target) { match target { Target::ForeignFn | Target::ForeignStatic => {} _ => { - self.dcx().emit_err(errors::LinkOrdinal { attr_span: attr.span() }); + self.dcx().emit_err(errors::LinkOrdinal { attr_span }); } } } diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs index 6b8cd49566dfe..2a8b9ebacf7f1 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs +++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs @@ -1,10 +1,12 @@ #[link(name = "foo")] extern "C" { #[link_ordinal()] - //~^ ERROR incorrect number of arguments to `#[link_ordinal]` + //~^ ERROR malformed `link_ordinal` attribute input + //~| NOTE expected a single argument fn foo(); #[link_ordinal()] - //~^ ERROR incorrect number of arguments to `#[link_ordinal]` + //~^ ERROR malformed `link_ordinal` attribute input + //~| NOTE expected a single argument static mut imported_variable: i32; } diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr index 1b04bb228e76a..3b2aeca60b060 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr +++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr @@ -1,18 +1,21 @@ -error: incorrect number of arguments to `#[link_ordinal]` +error[E0805]: malformed `link_ordinal` attribute input --> $DIR/link-ordinal-missing-argument.rs:3:5 | LL | #[link_ordinal()] | ^^^^^^^^^^^^^^^^^ - | - = note: the attribute requires exactly one argument + | | + | expected a single argument here + | help: must be of the form: `#[link_ordinal(ordinal)]` -error: incorrect number of arguments to `#[link_ordinal]` - --> $DIR/link-ordinal-missing-argument.rs:6:5 +error[E0805]: malformed `link_ordinal` attribute input + --> $DIR/link-ordinal-missing-argument.rs:7:5 | LL | #[link_ordinal()] | ^^^^^^^^^^^^^^^^^ - | - = note: the attribute requires exactly one argument + | | + | expected a single argument here + | help: must be of the form: `#[link_ordinal(ordinal)]` error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0805`. diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs index 9988115fd8b0d..ddf9583352fad 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs +++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs @@ -1,10 +1,12 @@ #[link(name = "foo")] extern "C" { #[link_ordinal(3, 4)] - //~^ ERROR incorrect number of arguments to `#[link_ordinal]` + //~^ ERROR malformed `link_ordinal` attribute input + //~| NOTE expected a single argument fn foo(); #[link_ordinal(3, 4)] - //~^ ERROR incorrect number of arguments to `#[link_ordinal]` + //~^ ERROR malformed `link_ordinal` attribute input + //~| NOTE expected a single argument static mut imported_variable: i32; } diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr index d5ce8aff34f20..bffb4059bdc2d 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr +++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr @@ -1,18 +1,21 @@ -error: incorrect number of arguments to `#[link_ordinal]` +error[E0805]: malformed `link_ordinal` attribute input --> $DIR/link-ordinal-too-many-arguments.rs:3:5 | LL | #[link_ordinal(3, 4)] | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: the attribute requires exactly one argument + | | + | expected a single argument here + | help: must be of the form: `#[link_ordinal(ordinal)]` -error: incorrect number of arguments to `#[link_ordinal]` - --> $DIR/link-ordinal-too-many-arguments.rs:6:5 +error[E0805]: malformed `link_ordinal` attribute input + --> $DIR/link-ordinal-too-many-arguments.rs:7:5 | LL | #[link_ordinal(3, 4)] | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: the attribute requires exactly one argument + | | + | expected a single argument here + | help: must be of the form: `#[link_ordinal(ordinal)]` error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0805`.