From a2f9dec29cfd8a0fc1b76f5ffbf7803a904c1017 Mon Sep 17 00:00:00 2001 From: Oscar Bray Date: Thu, 5 Feb 2026 19:04:44 +0000 Subject: [PATCH] Parse #[rustc_abi(..)] --- .../src/attributes/test_attrs.rs | 50 +++++ compiler/rustc_attr_parsing/src/context.rs | 1 + .../rustc_hir/src/attrs/data_structures.rs | 11 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/abi_test.rs | 193 +++++++++--------- compiler/rustc_passes/src/check_attr.rs | 2 +- tests/ui/abi/debug.generic.stderr | 44 +++- tests/ui/abi/debug.loongarch64.stderr | 44 +++- tests/ui/abi/debug.riscv64.stderr | 44 +++- tests/ui/abi/debug.rs | 10 +- 10 files changed, 276 insertions(+), 124 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index 236d10d77b92..908941c2b335 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -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 SingleAttributeParser for ReexportTestHarnessMainParser { Some(AttributeKind::ReexportTestHarnessMain(name)) } } + +pub(crate) struct RustcAbiParser; + +impl SingleAttributeParser for RustcAbiParser { + const PATH: &[Symbol] = &[sym::rustc_abi]; + const ON_DUPLICATE: OnDuplicate = 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 { + 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 }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 5abf299ec618..6259a90ea3f3 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -191,6 +191,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index a444b5e4badf..2a3adbd77e6e 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -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, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index b6249e1e2ec2..6a529cae5f19 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -89,6 +89,7 @@ impl AttributeKind { RecursionLimit { .. } => No, ReexportTestHarnessMain(..) => No, Repr { .. } => No, + RustcAbi { .. } => No, RustcAllocator => No, RustcAllocatorZeroed => No, RustcAllocatorZeroedVariant { .. } => Yes, diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 0ac42f03eb2f..c3e80208e2d5 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -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), + }); } } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 0d64e30d9c79..27398d4a9ac4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -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 diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index 8a031b79780a..b154c3fa201e 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -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`. diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 00bd3febde4e..68bcd736e47c 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -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`. diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 00bd3febde4e..68bcd736e47c 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -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`. diff --git a/tests/ui/abi/debug.rs b/tests/ui/abi/debug.rs index 2e34fa5d7df4..42ea14ec51d0 100644 --- a/tests/ui/abi/debug.rs +++ b/tests/ui/abi/debug.rs @@ -39,12 +39,12 @@ type TestFnPtr = fn(bool) -> u8; //~ ERROR: fn_abi #[rustc_abi(debug)] fn test_generic(_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;