1use rustc_hir::attrs::RustcAbiAttrKind;
2use rustc_hir::def::DefKind;
3use rustc_hir::def_id::LocalDefId;
4use rustc_hir::find_attr;
5use rustc_middle::span_bug;
6use rustc_middle::ty::layout::{FnAbiError, LayoutError};
7use rustc_middle::ty::{self, GenericArgs, Instance, Ty, TyCtxt};
8use rustc_span::Span;
9use rustc_target::callconv::FnAbi;
10
11use super::layout_test::ensure_wf;
12use crate::errors::{AbiInvalidAttribute, AbiNe, AbiOf, UnrecognizedArgument};
13
14pub fn test_abi(tcx: TyCtxt<'_>) {
15 if !tcx.features().rustc_attrs() {
16 return;
18 }
19 for id in tcx.hir_crate_items(()).definitions() {
20 let Some((attr_span, attr_kind)) =
21 {
{
'done:
{
for i in ::rustc_hir::attrs::HasAttrs::get_attrs(id, &tcx) {
#[allow(unused_imports)]
use rustc_hir::attrs::AttributeKind::*;
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(RustcAbi { attr_span, kind })
=> {
break 'done Some((*attr_span, *kind));
}
rustc_hir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}find_attr!(tcx, id, RustcAbi{ attr_span, kind } => (*attr_span, *kind))
22 else {
23 continue;
24 };
25 match tcx.def_kind(id) {
26 DefKind::Fn | DefKind::AssocFn => {
27 dump_abi_of_fn_item(tcx, id, attr_span, attr_kind);
28 }
29 DefKind::TyAlias => {
30 dump_abi_of_fn_type(tcx, id, attr_span, attr_kind);
31 }
32 _ => {
33 tcx.dcx().emit_err(AbiInvalidAttribute { span: tcx.def_span(id) });
34 }
35 }
36 }
37}
38
39fn unwrap_fn_abi<'tcx>(
40 abi: Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>>,
41 tcx: TyCtxt<'tcx>,
42 item_def_id: LocalDefId,
43) -> &'tcx FnAbi<'tcx, Ty<'tcx>> {
44 match abi {
45 Ok(abi) => abi,
46 Err(FnAbiError::Layout(layout_error)) => {
47 tcx.dcx().span_fatal(tcx.def_span(item_def_id), layout_error.to_string());
48 }
49 }
50}
51
52fn dump_abi_of_fn_item(
53 tcx: TyCtxt<'_>,
54 item_def_id: LocalDefId,
55 attr_span: Span,
56 attr_kind: RustcAbiAttrKind,
57) {
58 let typing_env = ty::TypingEnv::post_analysis(tcx, item_def_id);
59 let args = GenericArgs::identity_for_item(tcx, item_def_id);
60 let instance = match Instance::try_resolve(tcx, typing_env, item_def_id.into(), args) {
61 Ok(Some(instance)) => instance,
62 Ok(None) => {
63 let ty = tcx.type_of(item_def_id).instantiate_identity();
65 tcx.dcx().span_fatal(tcx.def_span(item_def_id), LayoutError::Unknown(ty).to_string());
66 }
67 Err(_guaranteed) => return,
68 };
69 let abi = unwrap_fn_abi(
70 tcx.fn_abi_of_instance(
71 typing_env.as_query_input((instance, ty::List::empty())),
72 ),
73 tcx,
74 item_def_id,
75 );
76
77 match attr_kind {
80 RustcAbiAttrKind::Debug => {
81 let fn_name = tcx.item_name(item_def_id);
82 tcx.dcx().emit_err(AbiOf {
83 span: tcx.def_span(item_def_id),
84 fn_name,
85 fn_abi: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:#?}", abi))
})format!("{:#?}", abi),
87 });
88 }
89 _ => {
90 tcx.dcx().emit_err(UnrecognizedArgument { span: attr_span });
91 }
92 }
93}
94
95fn test_abi_eq<'tcx>(abi1: &'tcx FnAbi<'tcx, Ty<'tcx>>, abi2: &'tcx FnAbi<'tcx, Ty<'tcx>>) -> bool {
96 if abi1.conv != abi2.conv
97 || abi1.args.len() != abi2.args.len()
98 || abi1.c_variadic != abi2.c_variadic
99 || abi1.fixed_count != abi2.fixed_count
100 || abi1.can_unwind != abi2.can_unwind
101 {
102 return false;
103 }
104
105 abi1.ret.eq_abi(&abi2.ret)
106 && abi1.args.iter().zip(abi2.args.iter()).all(|(arg1, arg2)| arg1.eq_abi(arg2))
107}
108
109fn dump_abi_of_fn_type(
110 tcx: TyCtxt<'_>,
111 item_def_id: LocalDefId,
112 attr_span: Span,
113 attr_kind: RustcAbiAttrKind,
114) {
115 let typing_env = ty::TypingEnv::post_analysis(tcx, item_def_id);
116 let ty = tcx.type_of(item_def_id).instantiate_identity();
117 let span = tcx.def_span(item_def_id);
118 if !ensure_wf(tcx, typing_env, ty, item_def_id, span) {
119 return;
120 }
121
122 match attr_kind {
123 RustcAbiAttrKind::Debug => {
124 let ty::FnPtr(sig_tys, hdr) = ty.kind() else {
125 ::rustc_middle::util::bug::span_bug_fmt(attr_span,
format_args!("`#[rustc_abi(debug)]` on a type alias requires function pointer type"));span_bug!(
126 attr_span,
127 "`#[rustc_abi(debug)]` on a type alias requires function pointer type"
128 );
129 };
130 let abi =
131 unwrap_fn_abi(
132 tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
133 sig_tys.with(*hdr),
134 ty::List::empty(),
135 ))),
136 tcx,
137 item_def_id,
138 );
139
140 let fn_name = tcx.item_name(item_def_id);
141 tcx.dcx().emit_err(AbiOf { span, fn_name, fn_abi: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:#?}", abi))
})format!("{:#?}", abi) });
142 }
143 RustcAbiAttrKind::AssertEq => {
144 let ty::Tuple(fields) = ty.kind() else {
145 ::rustc_middle::util::bug::span_bug_fmt(attr_span,
format_args!("`#[rustc_abi(assert_eq)]` on a type alias requires pair type"));span_bug!(
146 attr_span,
147 "`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
148 );
149 };
150 let [field1, field2] = ***fields else {
151 ::rustc_middle::util::bug::span_bug_fmt(attr_span,
format_args!("`#[rustc_abi(assert_eq)]` on a type alias requires pair type"));span_bug!(
152 attr_span,
153 "`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
154 );
155 };
156 let ty::FnPtr(sig_tys1, hdr1) = field1.kind() else {
157 ::rustc_middle::util::bug::span_bug_fmt(attr_span,
format_args!("`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"));span_bug!(
158 attr_span,
159 "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
160 );
161 };
162 let abi1 = unwrap_fn_abi(
163 tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
164 sig_tys1.with(*hdr1),
165 ty::List::empty(),
166 ))),
167 tcx,
168 item_def_id,
169 );
170 let ty::FnPtr(sig_tys2, hdr2) = field2.kind() else {
171 ::rustc_middle::util::bug::span_bug_fmt(attr_span,
format_args!("`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"));span_bug!(
172 attr_span,
173 "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
174 );
175 };
176 let abi2 = unwrap_fn_abi(
177 tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
178 sig_tys2.with(*hdr2),
179 ty::List::empty(),
180 ))),
181 tcx,
182 item_def_id,
183 );
184
185 if !test_abi_eq(abi1, abi2) {
186 tcx.dcx().emit_err(AbiNe {
187 span,
188 left: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:#?}", abi1))
})format!("{:#?}", abi1),
189 right: ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:#?}", abi2))
})format!("{:#?}", abi2),
190 });
191 }
192 }
193 }
194}