Rollup merge of #143217 - Periodic1911:link-ordinal, r=jdonszelmann

Port #[link_ordinal] to the new attribute parsing infrastructure

Ports link_ordinal to the new attribute parsing infrastructure for https://github.com/rust-lang/rust/issues/131229#issuecomment-2971353197
This commit is contained in:
Jakub Beránek 2025-07-14 11:04:52 +02:00 committed by GitHub
commit 93c10272d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 159 additions and 150 deletions

View file

@ -297,6 +297,9 @@ pub enum AttributeKind {
/// Represents `#[link_name]`.
LinkName { name: Symbol, span: Span },
/// Represents `#[link_ordinal]`.
LinkOrdinal { ordinal: u16, span: Span },
/// Represents [`#[link_section]`](https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute)
LinkSection { name: Symbol, span: Span },

View file

@ -41,6 +41,7 @@ impl AttributeKind {
Ignore { .. } => No,
Inline(..) => No,
LinkName { .. } => Yes,
LinkOrdinal { .. } => No,
LinkSection { .. } => No,
LoopMatch(..) => No,
MacroTransparency(..) => Yes,

View file

@ -78,6 +78,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'

View file

@ -1,14 +1,14 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_attr_data_structures::AttributeKind::{LinkName, LinkSection};
use rustc_attr_data_structures::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Span, Symbol, sym};
use crate::attributes::{
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
};
use crate::context::{AcceptContext, Stage};
use crate::context::{AcceptContext, Stage, parse_single_integer};
use crate::parser::ArgParser;
use crate::session_diagnostics::NullOnLinkSection;
use crate::session_diagnostics::{LinkOrdinalOutOfRange, NullOnLinkSection};
pub(crate) struct LinkNameParser;
@ -87,3 +87,36 @@ impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser {
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
}
pub(crate) struct LinkOrdinalParser;
impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
const PATH: &[Symbol] = &[sym::link_ordinal];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(List: "ordinal");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let ordinal = parse_single_integer(cx, args)?;
// 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.try_into() else {
cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal });
return None;
};
Some(LinkOrdinal { ordinal, span: cx.attr_span })
}
}

View file

@ -1,10 +1,9 @@
use rustc_ast::LitKind;
use rustc_attr_data_structures::AttributeKind;
use rustc_feature::{AttributeTemplate, template};
use rustc_span::{Symbol, sym};
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, Stage};
use crate::context::{AcceptContext, Stage, parse_single_integer};
use crate::parser::ArgParser;
pub(crate) struct RustcLayoutScalarValidRangeStart;
@ -16,8 +15,8 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart {
const TEMPLATE: AttributeTemplate = template!(List: "start");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
parse_rustc_layout_scalar_valid_range(cx, args)
.map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(n, cx.attr_span))
parse_single_integer(cx, args)
.map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(Box::new(n), cx.attr_span))
}
}
@ -30,34 +29,11 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEnd {
const TEMPLATE: AttributeTemplate = template!(List: "end");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
parse_rustc_layout_scalar_valid_range(cx, args)
.map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(n, cx.attr_span))
parse_single_integer(cx, args)
.map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(Box::new(n), cx.attr_span))
}
}
fn parse_rustc_layout_scalar_valid_range<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser<'_>,
) -> Option<Box<u128>> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
return None;
};
let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
};
let Some(lit) = single.lit() else {
cx.expected_integer_literal(single.span());
return None;
};
let LitKind::Int(num, _ty) = lit.kind else {
cx.expected_integer_literal(single.span());
return None;
};
Some(Box::new(num.0))
}
pub(crate) struct RustcObjectLifetimeDefaultParser;
impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {

View file

@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
use private::Sealed;
use rustc_ast::{self as ast, MetaItemLit, NodeId};
use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId};
use rustc_attr_data_structures::AttributeKind;
use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
use rustc_errors::{DiagCtxtHandle, Diagnostic};
@ -24,8 +24,8 @@ use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::dummy::DummyParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
use crate::attributes::link_attrs::{
ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkSectionParser,
StdInternalSymbolParser,
ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkOrdinalParser,
LinkSectionParser, StdInternalSymbolParser,
};
use crate::attributes::lint_helpers::{
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
@ -143,6 +143,7 @@ attribute_parsers!(
Single<IgnoreParser>,
Single<InlineParser>,
Single<LinkNameParser>,
Single<LinkOrdinalParser>,
Single<LinkSectionParser>,
Single<MustUseParser>,
Single<OptimizeParser>,
@ -775,3 +776,32 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
}
}
}
/// Parse a single integer.
///
/// Used by attributes that take a single integer as argument, such as
/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
/// `cx` is the context given to the attribute.
/// `args` is the parser for the attribute arguments.
pub(crate) fn parse_single_integer<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser<'_>,
) -> Option<u128> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span);
return None;
};
let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
};
let Some(lit) = single.lit() else {
cx.expected_integer_literal(single.span());
return None;
};
let LitKind::Int(num, _ty) = lit.kind else {
cx.expected_integer_literal(single.span());
return None;
};
Some(num.0)
}

View file

@ -514,6 +514,15 @@ pub(crate) struct NakedFunctionIncompatibleAttribute {
pub attr: String,
}
#[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<Span> },

View file

@ -80,9 +80,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 "}
@ -93,9 +90,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`

View file

@ -116,6 +116,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::LinkSection { name, .. } => {
codegen_fn_attrs.link_section = Some(*name)
}
@ -250,12 +254,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() {
@ -568,45 +566,6 @@ fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Align> {
tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment
}
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
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,

View file

@ -1108,22 +1108,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 {

View file

@ -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 });
}
}

View file

@ -310,6 +310,7 @@ pub fn check_builtin_meta_item(
| sym::must_use
| sym::track_caller
| sym::link_name
| sym::link_ordinal
| sym::export_name
| sym::rustc_macro_transparency
| sym::link_section

View file

@ -258,6 +258,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)
}
@ -335,7 +338,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::rustc_has_incoherent_inherent_impls, ..] => {
self.check_has_incoherent_inherent_impls(attr, span, target)
}
[sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
[sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
self.check_macro_use(hir_id, attr, target)
@ -2265,11 +2269,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 });
}
}
}

View file

@ -116,12 +116,6 @@ error: malformed `cfi_encoding` attribute input
LL | #[cfi_encoding]
| ^^^^^^^^^^^^^^^ help: must be of the form: `#[cfi_encoding = "encoding"]`
error: malformed `link_ordinal` attribute input
--> $DIR/malformed-attrs.rs:167:5
|
LL | #[link_ordinal]
| ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_ordinal(ordinal)]`
error: malformed `linkage` attribute input
--> $DIR/malformed-attrs.rs:173:5
|
@ -531,6 +525,15 @@ LL | #[unsafe(ffi_pure = 1)]
| | didn't expect any arguments here
| help: must be of the form: `#[ffi_pure]`
error[E0539]: malformed `link_ordinal` attribute input
--> $DIR/malformed-attrs.rs:167:5
|
LL | #[link_ordinal]
| ^^^^^^^^^^^^^^^
| |
| expected this to be a list
| help: must be of the form: `#[link_ordinal(ordinal)]`
error[E0565]: malformed `ffi_const` attribute input
--> $DIR/malformed-attrs.rs:171:5
|

View file

@ -1,10 +1,10 @@
#[link(name = "foo")]
extern "C" {
#[link_ordinal("JustMonika")]
//~^ ERROR illegal ordinal format in `link_ordinal`
//~^ ERROR malformed `link_ordinal` attribute input
fn foo();
#[link_ordinal("JustMonika")]
//~^ ERROR illegal ordinal format in `link_ordinal`
//~^ ERROR malformed `link_ordinal` attribute input
static mut imported_variable: i32;
}

View file

@ -1,18 +1,21 @@
error: illegal ordinal format in `link_ordinal`
error[E0539]: malformed `link_ordinal` attribute input
--> $DIR/link-ordinal-invalid-format.rs:3:5
|
LL | #[link_ordinal("JustMonika")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an unsuffixed integer value, e.g., `1`, is expected
| ^^^^^^^^^^^^^^^------------^^
| | |
| | expected an integer literal here
| help: must be of the form: `#[link_ordinal(ordinal)]`
error: illegal ordinal format in `link_ordinal`
error[E0539]: malformed `link_ordinal` attribute input
--> $DIR/link-ordinal-invalid-format.rs:6:5
|
LL | #[link_ordinal("JustMonika")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an unsuffixed integer value, e.g., `1`, is expected
| ^^^^^^^^^^^^^^^------------^^
| | |
| | expected an integer literal 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 E0539`.

View file

@ -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;
}

View file

@ -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`.

View file

@ -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;
}

View file

@ -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`.