Skip to content
Merged
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
50 changes: 50 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rustc_hir::attrs::RustcAbiAttrKind;
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;

use super::prelude::*;
Expand Down Expand Up @@ -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 })
}
}
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ attribute_parsers!(
Single<ProcMacroDeriveParser>,
Single<RecursionLimitParser>,
Single<ReexportTestHarnessMainParser>,
Single<RustcAbiParser>,
Single<RustcAllocatorZeroedVariantParser>,
Single<RustcBuiltinMacroParser>,
Single<RustcForceInlineParser>,
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl AttributeKind {
RecursionLimit { .. } => No,
ReexportTestHarnessMain(..) => No,
Repr { .. } => No,
RustcAbi { .. } => No,
RustcAllocator => No,
RustcAllocatorZeroed => No,
RustcAllocatorZeroedVariant { .. } => Yes,
Expand Down
193 changes: 99 additions & 94 deletions compiler/rustc_passes/src/abi_test.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm does this match the attribute targets list? I guess it might if defkind fn also implies foreign functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some tests of the attr with foreign functions too so I think they should match e.g. tests/ui/abi/c-zst.rs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense

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) });
}
}
}
Expand All @@ -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) {
Expand All @@ -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 });
}
}
}
Expand All @@ -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(),
Expand All @@ -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),
});
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 { .. }
Expand Down Expand Up @@ -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
Expand Down
Loading
Loading