Parse #[rustc_abi(..)]

This commit is contained in:
Oscar Bray 2026-02-05 19:04:44 +00:00
parent f889772d65
commit a2f9dec29c
10 changed files with 276 additions and 124 deletions

View file

@ -1,3 +1,4 @@
use rustc_hir::attrs::RustcAbiAttrKind;
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use super::prelude::*;
@ -140,3 +141,52 @@ impl<S: Stage> SingleAttributeParser<S> for ReexportTestHarnessMainParser {
Some(AttributeKind::ReexportTestHarnessMain(name))
}
}
pub(crate) struct RustcAbiParser;
impl<S: Stage> SingleAttributeParser<S> for RustcAbiParser {
const PATH: &[Symbol] = &[sym::rustc_abi];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::debug, sym::assert_eq]);
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::TyAlias),
Allow(Target::Fn),
Allow(Target::ForeignFn),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::Trait { body: false })),
Allow(Target::Method(MethodKind::TraitImpl)),
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(args) = args.list() else {
cx.expected_specific_argument_and_list(cx.attr_span, &[sym::assert_eq, sym::debug]);
return None;
};
let Some(arg) = args.single() else {
cx.expected_single_argument(cx.attr_span);
return None;
};
let fail_incorrect_argument =
|span| cx.expected_specific_argument(span, &[sym::assert_eq, sym::debug]);
let Some(arg) = arg.meta_item() else {
fail_incorrect_argument(args.span);
return None;
};
let kind: RustcAbiAttrKind = match arg.path().word_sym() {
Some(sym::assert_eq) => RustcAbiAttrKind::AssertEq,
Some(sym::debug) => RustcAbiAttrKind::Debug,
None | Some(_) => {
fail_incorrect_argument(arg.span());
return None;
}
};
Some(AttributeKind::RustcAbi { attr_span: cx.attr_span, kind })
}
}

View file

@ -191,6 +191,7 @@ attribute_parsers!(
Single<ProcMacroDeriveParser>,
Single<RecursionLimitParser>,
Single<ReexportTestHarnessMainParser>,
Single<RustcAbiParser>,
Single<RustcAllocatorZeroedVariantParser>,
Single<RustcBuiltinMacroParser>,
Single<RustcForceInlineParser>,

View file

@ -167,6 +167,14 @@ pub enum CoverageAttrKind {
Off,
}
/// Successfully-parsed value of a `#[rustc_abi(..)]` attribute.
#[derive(Copy, Debug, Eq, PartialEq, Encodable, Decodable, Clone)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum RustcAbiAttrKind {
Debug,
AssertEq,
}
impl Deprecation {
/// Whether an item marked with #[deprecated(since = "X")] is currently
/// deprecated (i.e., whether X is not greater than the current rustc
@ -1015,6 +1023,9 @@ pub enum AttributeKind {
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span },
/// Represents `#[rustc_abi(..)]`
RustcAbi { attr_span: Span, kind: RustcAbiAttrKind },
/// Represents `#[rustc_allocator]`
RustcAllocator,

View file

@ -89,6 +89,7 @@ impl AttributeKind {
RecursionLimit { .. } => No,
ReexportTestHarnessMain(..) => No,
Repr { .. } => No,
RustcAbi { .. } => No,
RustcAllocator => No,
RustcAllocatorZeroed => No,
RustcAllocatorZeroedVariant { .. } => Yes,

View file

@ -1,11 +1,12 @@
use rustc_hir::Attribute;
use rustc_hir::attrs::{AttributeKind, RustcAbiAttrKind};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::find_attr;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::{FnAbiError, LayoutError};
use rustc_middle::ty::{self, GenericArgs, Instance, Ty, TyCtxt};
use rustc_span::Span;
use rustc_span::source_map::Spanned;
use rustc_span::sym;
use rustc_target::callconv::FnAbi;
use super::layout_test::ensure_wf;
@ -17,17 +18,19 @@ pub fn test_abi(tcx: TyCtxt<'_>) {
return;
}
for id in tcx.hir_crate_items(()).definitions() {
for attr in tcx.get_attrs(id, sym::rustc_abi) {
match tcx.def_kind(id) {
DefKind::Fn | DefKind::AssocFn => {
dump_abi_of_fn_item(tcx, id, attr);
}
DefKind::TyAlias => {
dump_abi_of_fn_type(tcx, id, attr);
}
_ => {
tcx.dcx().emit_err(AbiInvalidAttribute { span: tcx.def_span(id) });
}
let Some((attr_span, attr_kind)) = find_attr!(tcx.get_all_attrs(id), AttributeKind::RustcAbi{ attr_span, kind } => (*attr_span, *kind))
else {
continue;
};
match tcx.def_kind(id) {
DefKind::Fn | DefKind::AssocFn => {
dump_abi_of_fn_item(tcx, id, attr_span, attr_kind);
}
DefKind::TyAlias => {
dump_abi_of_fn_type(tcx, id, attr_span, attr_kind);
}
_ => {
tcx.dcx().emit_err(AbiInvalidAttribute { span: tcx.def_span(id) });
}
}
}
@ -49,7 +52,12 @@ fn unwrap_fn_abi<'tcx>(
}
}
fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
fn dump_abi_of_fn_item(
tcx: TyCtxt<'_>,
item_def_id: LocalDefId,
attr_span: Span,
attr_kind: RustcAbiAttrKind,
) {
let typing_env = ty::TypingEnv::post_analysis(tcx, item_def_id);
let args = GenericArgs::identity_for_item(tcx, item_def_id);
let instance = match Instance::try_resolve(tcx, typing_env, item_def_id.into(), args) {
@ -75,22 +83,18 @@ fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut
// Check out the `#[rustc_abi(..)]` attribute to tell what to dump.
// The `..` are the names of fields to dump.
let meta_items = attr.meta_item_list().unwrap_or_default();
for meta_item in meta_items {
match meta_item.name() {
Some(sym::debug) => {
let fn_name = tcx.item_name(item_def_id);
tcx.dcx().emit_err(AbiOf {
span: tcx.def_span(item_def_id),
fn_name,
// FIXME: using the `Debug` impl here isn't ideal.
fn_abi: format!("{:#?}", abi),
});
}
_ => {
tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() });
}
match attr_kind {
RustcAbiAttrKind::Debug => {
let fn_name = tcx.item_name(item_def_id);
tcx.dcx().emit_err(AbiOf {
span: tcx.def_span(item_def_id),
fn_name,
// FIXME: using the `Debug` impl here isn't ideal.
fn_abi: format!("{:#?}", abi),
});
}
_ => {
tcx.dcx().emit_err(UnrecognizedArgument { span: attr_span });
}
}
}
@ -109,24 +113,29 @@ fn test_abi_eq<'tcx>(abi1: &'tcx FnAbi<'tcx, Ty<'tcx>>, abi2: &'tcx FnAbi<'tcx,
&& abi1.args.iter().zip(abi2.args.iter()).all(|(arg1, arg2)| arg1.eq_abi(arg2))
}
fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
fn dump_abi_of_fn_type(
tcx: TyCtxt<'_>,
item_def_id: LocalDefId,
attr_span: Span,
attr_kind: RustcAbiAttrKind,
) {
let typing_env = ty::TypingEnv::post_analysis(tcx, item_def_id);
let ty = tcx.type_of(item_def_id).instantiate_identity();
let span = tcx.def_span(item_def_id);
if !ensure_wf(tcx, typing_env, ty, item_def_id, span) {
return;
}
let meta_items = attr.meta_item_list().unwrap_or_default();
for meta_item in meta_items {
match meta_item.name() {
Some(sym::debug) => {
let ty::FnPtr(sig_tys, hdr) = ty.kind() else {
span_bug!(
meta_item.span(),
"`#[rustc_abi(debug)]` on a type alias requires function pointer type"
);
};
let abi = unwrap_fn_abi(
match attr_kind {
RustcAbiAttrKind::Debug => {
let ty::FnPtr(sig_tys, hdr) = ty.kind() else {
span_bug!(
attr_span,
"`#[rustc_abi(debug)]` on a type alias requires function pointer type"
);
};
let abi =
unwrap_fn_abi(
tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
sig_tys.with(*hdr),
/* extra_args */ ty::List::empty(),
@ -135,61 +144,57 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut
item_def_id,
);
let fn_name = tcx.item_name(item_def_id);
tcx.dcx().emit_err(AbiOf { span, fn_name, fn_abi: format!("{:#?}", abi) });
}
Some(sym::assert_eq) => {
let ty::Tuple(fields) = ty.kind() else {
span_bug!(
meta_item.span(),
"`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
);
};
let [field1, field2] = ***fields else {
span_bug!(
meta_item.span(),
"`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
);
};
let ty::FnPtr(sig_tys1, hdr1) = field1.kind() else {
span_bug!(
meta_item.span(),
"`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
);
};
let abi1 = unwrap_fn_abi(
tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
sig_tys1.with(*hdr1),
/* extra_args */ ty::List::empty(),
))),
tcx,
item_def_id,
let fn_name = tcx.item_name(item_def_id);
tcx.dcx().emit_err(AbiOf { span, fn_name, fn_abi: format!("{:#?}", abi) });
}
RustcAbiAttrKind::AssertEq => {
let ty::Tuple(fields) = ty.kind() else {
span_bug!(
attr_span,
"`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
);
let ty::FnPtr(sig_tys2, hdr2) = field2.kind() else {
span_bug!(
meta_item.span(),
"`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
);
};
let abi2 = unwrap_fn_abi(
tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
sig_tys2.with(*hdr2),
/* extra_args */ ty::List::empty(),
))),
tcx,
item_def_id,
};
let [field1, field2] = ***fields else {
span_bug!(
attr_span,
"`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
);
};
let ty::FnPtr(sig_tys1, hdr1) = field1.kind() else {
span_bug!(
attr_span,
"`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
);
};
let abi1 = unwrap_fn_abi(
tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
sig_tys1.with(*hdr1),
/* extra_args */ ty::List::empty(),
))),
tcx,
item_def_id,
);
let ty::FnPtr(sig_tys2, hdr2) = field2.kind() else {
span_bug!(
attr_span,
"`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
);
};
let abi2 = unwrap_fn_abi(
tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
sig_tys2.with(*hdr2),
/* extra_args */ ty::List::empty(),
))),
tcx,
item_def_id,
);
if !test_abi_eq(abi1, abi2) {
tcx.dcx().emit_err(AbiNe {
span,
left: format!("{:#?}", abi1),
right: format!("{:#?}", abi2),
});
}
}
_ => {
tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() });
if !test_abi_eq(abi1, abi2) {
tcx.dcx().emit_err(AbiNe {
span,
left: format!("{:#?}", abi1),
right: format!("{:#?}", abi2),
});
}
}
}

View file

@ -287,6 +287,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::ReexportTestHarnessMain(..)
// handled below this loop and elsewhere
| AttributeKind::Repr { .. }
| AttributeKind::RustcAbi { .. }
| AttributeKind::RustcAllocator
| AttributeKind::RustcAllocatorZeroed
| AttributeKind::RustcAllocatorZeroedVariant { .. }
@ -392,7 +393,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| sym::rustc_conversion_suggestion
| sym::rustc_deprecated_safe_2024
| sym::rustc_test_marker
| sym::rustc_abi
| sym::rustc_layout
| sym::rustc_proc_macro_decls
| sym::rustc_never_type_options

View file

@ -1,3 +1,36 @@
error: `#[rustc_abi]` attribute cannot be used on constants
--> $DIR/debug.rs:42:1
|
LL | #[rustc_abi(debug)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: `#[rustc_abi]` can be applied to functions and type aliases
error: `#[rustc_abi]` attribute cannot be used on associated consts
--> $DIR/debug.rs:46:5
|
LL | #[rustc_abi(debug)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: `#[rustc_abi]` can be applied to functions and type aliases
error[E0539]: malformed `rustc_abi` attribute input
--> $DIR/debug.rs:74:1
|
LL | #[rustc_abi("assert_eq")]
| ^^^^^^^^^^^-------------^
| |
| valid arguments are `assert_eq` or `debug`
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[rustc_abi("assert_eq")]
LL + #[rustc_abi(assert_eq)]
|
LL - #[rustc_abi("assert_eq")]
LL + #[rustc_abi(debug)]
|
error: fn_abi_of(test) = FnAbi {
args: [
ArgAbi {
@ -884,12 +917,6 @@ LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str)));
= help: the trait `Sized` is not implemented for `str`
= note: only the last element of a tuple may have a dynamically sized type
error: unrecognized argument
--> $DIR/debug.rs:74:13
|
LL | #[rustc_abi("assert_eq")]
| ^^^^^^^^^^^
error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions
--> $DIR/debug.rs:47:5
|
@ -986,6 +1013,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
LL | fn assoc_test(&self) {}
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 12 previous errors
error: aborting due to 14 previous errors
For more information about this error, try `rustc --explain E0277`.
Some errors have detailed explanations: E0277, E0539.
For more information about an error, try `rustc --explain E0277`.

View file

@ -1,3 +1,36 @@
error: `#[rustc_abi]` attribute cannot be used on constants
--> $DIR/debug.rs:42:1
|
LL | #[rustc_abi(debug)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: `#[rustc_abi]` can be applied to functions and type aliases
error: `#[rustc_abi]` attribute cannot be used on associated consts
--> $DIR/debug.rs:46:5
|
LL | #[rustc_abi(debug)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: `#[rustc_abi]` can be applied to functions and type aliases
error[E0539]: malformed `rustc_abi` attribute input
--> $DIR/debug.rs:74:1
|
LL | #[rustc_abi("assert_eq")]
| ^^^^^^^^^^^-------------^
| |
| valid arguments are `assert_eq` or `debug`
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[rustc_abi("assert_eq")]
LL + #[rustc_abi(assert_eq)]
|
LL - #[rustc_abi("assert_eq")]
LL + #[rustc_abi(debug)]
|
error: fn_abi_of(test) = FnAbi {
args: [
ArgAbi {
@ -884,12 +917,6 @@ LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str)));
= help: the trait `Sized` is not implemented for `str`
= note: only the last element of a tuple may have a dynamically sized type
error: unrecognized argument
--> $DIR/debug.rs:74:13
|
LL | #[rustc_abi("assert_eq")]
| ^^^^^^^^^^^
error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions
--> $DIR/debug.rs:47:5
|
@ -986,6 +1013,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
LL | fn assoc_test(&self) {}
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 12 previous errors
error: aborting due to 14 previous errors
For more information about this error, try `rustc --explain E0277`.
Some errors have detailed explanations: E0277, E0539.
For more information about an error, try `rustc --explain E0277`.

View file

@ -1,3 +1,36 @@
error: `#[rustc_abi]` attribute cannot be used on constants
--> $DIR/debug.rs:42:1
|
LL | #[rustc_abi(debug)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: `#[rustc_abi]` can be applied to functions and type aliases
error: `#[rustc_abi]` attribute cannot be used on associated consts
--> $DIR/debug.rs:46:5
|
LL | #[rustc_abi(debug)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: `#[rustc_abi]` can be applied to functions and type aliases
error[E0539]: malformed `rustc_abi` attribute input
--> $DIR/debug.rs:74:1
|
LL | #[rustc_abi("assert_eq")]
| ^^^^^^^^^^^-------------^
| |
| valid arguments are `assert_eq` or `debug`
|
help: try changing it to one of the following valid forms of the attribute
|
LL - #[rustc_abi("assert_eq")]
LL + #[rustc_abi(assert_eq)]
|
LL - #[rustc_abi("assert_eq")]
LL + #[rustc_abi(debug)]
|
error: fn_abi_of(test) = FnAbi {
args: [
ArgAbi {
@ -884,12 +917,6 @@ LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str)));
= help: the trait `Sized` is not implemented for `str`
= note: only the last element of a tuple may have a dynamically sized type
error: unrecognized argument
--> $DIR/debug.rs:74:13
|
LL | #[rustc_abi("assert_eq")]
| ^^^^^^^^^^^
error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions
--> $DIR/debug.rs:47:5
|
@ -986,6 +1013,7 @@ error: fn_abi_of(assoc_test) = FnAbi {
LL | fn assoc_test(&self) {}
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 12 previous errors
error: aborting due to 14 previous errors
For more information about this error, try `rustc --explain E0277`.
Some errors have detailed explanations: E0277, E0539.
For more information about an error, try `rustc --explain E0277`.

View file

@ -39,12 +39,12 @@ type TestFnPtr = fn(bool) -> u8; //~ ERROR: fn_abi
#[rustc_abi(debug)]
fn test_generic<T>(_x: *const T) {} //~ ERROR: fn_abi
#[rustc_abi(debug)]
const C: () = (); //~ ERROR: can only be applied to
#[rustc_abi(debug)] //~ ERROR: `#[rustc_abi]` attribute cannot be used on constants
const C: () = (); //~ ERROR: `#[rustc_abi]` can only be applied to
impl S {
#[rustc_abi(debug)]
const C: () = (); //~ ERROR: can only be applied to
#[rustc_abi(debug)] //~ ERROR: `#[rustc_abi]` attribute cannot be used on assoc
const C: () = (); //~ ERROR: `#[rustc_abi]` can only be applied to
}
impl S {
@ -71,5 +71,5 @@ type TestAbiNeSign = (fn(i32), fn(u32)); //~ ERROR: ABIs are not compatible
#[rustc_abi(assert_eq)]
type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); //~ ERROR: cannot be known at compilation time
#[rustc_abi("assert_eq")] //~ ERROR unrecognized argument
#[rustc_abi("assert_eq")] //~ ERROR malformed `rustc_abi` attribute input
type Bad = u32;