Skip to main content

rustc_passes/
abi_test.rs

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_span::source_map::Spanned;
10use rustc_target::callconv::FnAbi;
11
12use super::layout_test::ensure_wf;
13use crate::errors::{AbiInvalidAttribute, AbiNe, AbiOf, UnrecognizedArgument};
14
15pub fn test_abi(tcx: TyCtxt<'_>) {
16    if !tcx.features().rustc_attrs() {
17        // if the `rustc_attrs` feature is not enabled, don't bother testing ABI
18        return;
19    }
20    for id in tcx.hir_crate_items(()).definitions() {
21        let Some((attr_span, attr_kind)) =
22            {

    #[allow(deprecated)]
    {
        {
            'done:
                {
                for i in tcx.get_all_attrs(id) {
                    #[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))
23        else {
24            continue;
25        };
26        match tcx.def_kind(id) {
27            DefKind::Fn | DefKind::AssocFn => {
28                dump_abi_of_fn_item(tcx, id, attr_span, attr_kind);
29            }
30            DefKind::TyAlias => {
31                dump_abi_of_fn_type(tcx, id, attr_span, attr_kind);
32            }
33            _ => {
34                tcx.dcx().emit_err(AbiInvalidAttribute { span: tcx.def_span(id) });
35            }
36        }
37    }
38}
39
40fn unwrap_fn_abi<'tcx>(
41    abi: Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>>,
42    tcx: TyCtxt<'tcx>,
43    item_def_id: LocalDefId,
44) -> &'tcx FnAbi<'tcx, Ty<'tcx>> {
45    match abi {
46        Ok(abi) => abi,
47        Err(FnAbiError::Layout(layout_error)) => {
48            tcx.dcx().emit_fatal(Spanned {
49                node: layout_error.into_diagnostic(),
50                span: tcx.def_span(item_def_id),
51            });
52        }
53    }
54}
55
56fn dump_abi_of_fn_item(
57    tcx: TyCtxt<'_>,
58    item_def_id: LocalDefId,
59    attr_span: Span,
60    attr_kind: RustcAbiAttrKind,
61) {
62    let typing_env = ty::TypingEnv::post_analysis(tcx, item_def_id);
63    let args = GenericArgs::identity_for_item(tcx, item_def_id);
64    let instance = match Instance::try_resolve(tcx, typing_env, item_def_id.into(), args) {
65        Ok(Some(instance)) => instance,
66        Ok(None) => {
67            // Not sure what to do here, but `LayoutError::Unknown` seems reasonable?
68            let ty = tcx.type_of(item_def_id).instantiate_identity();
69            tcx.dcx().emit_fatal(Spanned {
70                node: LayoutError::Unknown(ty).into_diagnostic(),
71
72                span: tcx.def_span(item_def_id),
73            });
74        }
75        Err(_guaranteed) => return,
76    };
77    let abi = unwrap_fn_abi(
78        tcx.fn_abi_of_instance(
79            typing_env.as_query_input((instance, /* extra_args */ ty::List::empty())),
80        ),
81        tcx,
82        item_def_id,
83    );
84
85    // Check out the `#[rustc_abi(..)]` attribute to tell what to dump.
86    // The `..` are the names of fields to dump.
87    match attr_kind {
88        RustcAbiAttrKind::Debug => {
89            let fn_name = tcx.item_name(item_def_id);
90            tcx.dcx().emit_err(AbiOf {
91                span: tcx.def_span(item_def_id),
92                fn_name,
93                // FIXME: using the `Debug` impl here isn't ideal.
94                fn_abi: ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:#?}", abi))
    })format!("{:#?}", abi),
95            });
96        }
97        _ => {
98            tcx.dcx().emit_err(UnrecognizedArgument { span: attr_span });
99        }
100    }
101}
102
103fn test_abi_eq<'tcx>(abi1: &'tcx FnAbi<'tcx, Ty<'tcx>>, abi2: &'tcx FnAbi<'tcx, Ty<'tcx>>) -> bool {
104    if abi1.conv != abi2.conv
105        || abi1.args.len() != abi2.args.len()
106        || abi1.c_variadic != abi2.c_variadic
107        || abi1.fixed_count != abi2.fixed_count
108        || abi1.can_unwind != abi2.can_unwind
109    {
110        return false;
111    }
112
113    abi1.ret.eq_abi(&abi2.ret)
114        && abi1.args.iter().zip(abi2.args.iter()).all(|(arg1, arg2)| arg1.eq_abi(arg2))
115}
116
117fn dump_abi_of_fn_type(
118    tcx: TyCtxt<'_>,
119    item_def_id: LocalDefId,
120    attr_span: Span,
121    attr_kind: RustcAbiAttrKind,
122) {
123    let typing_env = ty::TypingEnv::post_analysis(tcx, item_def_id);
124    let ty = tcx.type_of(item_def_id).instantiate_identity();
125    let span = tcx.def_span(item_def_id);
126    if !ensure_wf(tcx, typing_env, ty, item_def_id, span) {
127        return;
128    }
129
130    match attr_kind {
131        RustcAbiAttrKind::Debug => {
132            let ty::FnPtr(sig_tys, hdr) = ty.kind() else {
133                ::rustc_middle::util::bug::span_bug_fmt(attr_span,
    format_args!("`#[rustc_abi(debug)]` on a type alias requires function pointer type"));span_bug!(
134                    attr_span,
135                    "`#[rustc_abi(debug)]` on a type alias requires function pointer type"
136                );
137            };
138            let abi =
139                unwrap_fn_abi(
140                    tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
141                        sig_tys.with(*hdr),
142                        /* extra_args */ ty::List::empty(),
143                    ))),
144                    tcx,
145                    item_def_id,
146                );
147
148            let fn_name = tcx.item_name(item_def_id);
149            tcx.dcx().emit_err(AbiOf { span, fn_name, fn_abi: ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:#?}", abi))
    })format!("{:#?}", abi) });
150        }
151        RustcAbiAttrKind::AssertEq => {
152            let ty::Tuple(fields) = ty.kind() else {
153                ::rustc_middle::util::bug::span_bug_fmt(attr_span,
    format_args!("`#[rustc_abi(assert_eq)]` on a type alias requires pair type"));span_bug!(
154                    attr_span,
155                    "`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
156                );
157            };
158            let [field1, field2] = ***fields else {
159                ::rustc_middle::util::bug::span_bug_fmt(attr_span,
    format_args!("`#[rustc_abi(assert_eq)]` on a type alias requires pair type"));span_bug!(
160                    attr_span,
161                    "`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
162                );
163            };
164            let ty::FnPtr(sig_tys1, hdr1) = field1.kind() else {
165                ::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!(
166                    attr_span,
167                    "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
168                );
169            };
170            let abi1 = unwrap_fn_abi(
171                tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
172                    sig_tys1.with(*hdr1),
173                    /* extra_args */ ty::List::empty(),
174                ))),
175                tcx,
176                item_def_id,
177            );
178            let ty::FnPtr(sig_tys2, hdr2) = field2.kind() else {
179                ::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!(
180                    attr_span,
181                    "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
182                );
183            };
184            let abi2 = unwrap_fn_abi(
185                tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((
186                    sig_tys2.with(*hdr2),
187                    /* extra_args */ ty::List::empty(),
188                ))),
189                tcx,
190                item_def_id,
191            );
192
193            if !test_abi_eq(abi1, abi2) {
194                tcx.dcx().emit_err(AbiNe {
195                    span,
196                    left: ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:#?}", abi1))
    })format!("{:#?}", abi1),
197                    right: ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:#?}", abi2))
    })format!("{:#?}", abi2),
198                });
199            }
200        }
201    }
202}