Skip to content

Port #[link_ordinal] to the new attribute parsing infrastructure #143217

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl AttributeKind {
Cold(..) => No,
ConstContinue(..) => No,
LinkName { .. } => Yes,
LinkOrdinal { .. } => Yes,
LoopMatch(..) => No,
MayDangle(..) => No,
MustUse { .. } => Yes,
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_attr_parsing/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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'

Expand Down
52 changes: 51 additions & 1 deletion compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -28,3 +30,51 @@ impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
Some(LinkName { name, span: cx.attr_span })
}
}

pub(crate) struct LinkOrdinalParser;

impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
const PATH: &[Symbol] = &[sym::link_ordinal];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(List: "ordinal");

fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
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 })
}
}
3 changes: 2 additions & 1 deletion compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -123,6 +123,7 @@ attribute_parsers!(
Single<ExportNameParser>,
Single<InlineParser>,
Single<LinkNameParser>,
Single<LinkOrdinalParser>,
Single<LoopMatchParser>,
Single<MayDangleParser>,
Single<MustUseParser>,
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Span> },
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_codegen_ssa/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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 "}
Expand All @@ -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`

Expand Down
49 changes: 4 additions & 45 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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<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,
Expand Down
16 changes: 0 additions & 16 deletions compiler/rustc_codegen_ssa/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 3 additions & 7 deletions compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 });
}
}

Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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, ..] => {
Expand Down Expand Up @@ -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 });
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -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;
}

Expand Down
Loading
Loading