@@ -5,9 +5,9 @@ use rustc_middle::ty::layout::{FnAbiError, LayoutError};
55use rustc_middle:: ty:: { self , GenericArgs , Instance , Ty , TyCtxt } ;
66use rustc_span:: source_map:: Spanned ;
77use rustc_span:: symbol:: sym;
8- use rustc_target:: abi:: call:: FnAbi ;
8+ use rustc_target:: abi:: call:: { ArgAbi , FnAbi } ;
99
10- use crate :: errors:: { AbiInvalidAttribute , AbiOf , UnrecognizedField } ;
10+ use crate :: errors:: { AbiInvalidAttribute , AbiNe , AbiOf , UnrecognizedField } ;
1111
1212pub fn test_abi ( tcx : TyCtxt < ' _ > ) {
1313 if !tcx. features ( ) . rustc_attrs {
@@ -114,6 +114,32 @@ fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: DefId, attr: &Attribute) {
114114 }
115115}
116116
117+ fn test_arg_abi_eq < ' tcx > (
118+ abi1 : & ' tcx ArgAbi < ' tcx , Ty < ' tcx > > ,
119+ abi2 : & ' tcx ArgAbi < ' tcx , Ty < ' tcx > > ,
120+ ) -> bool {
121+ // Ideally we'd just compare the `mode`, but that is not enough -- for some modes LLVM will look
122+ // at the type. Comparing the `mode` and `layout.abi` should catch basically everything though
123+ // (except for tricky cases around unized types).
124+ // This *is* overly strict (e.g. we compare the sign of integer `Primitive`s, or parts of `ArgAttributes` that do not affect ABI),
125+ // but for the purpose of ensuring repr(transparent) ABI compatibility that is fine.
126+ abi1. mode == abi2. mode && abi1. layout . abi == abi2. layout . abi
127+ }
128+
129+ fn test_abi_eq < ' tcx > ( abi1 : & ' tcx FnAbi < ' tcx , Ty < ' tcx > > , abi2 : & ' tcx FnAbi < ' tcx , Ty < ' tcx > > ) -> bool {
130+ if abi1. conv != abi2. conv
131+ || abi1. args . len ( ) != abi2. args . len ( )
132+ || abi1. c_variadic != abi2. c_variadic
133+ || abi1. fixed_count != abi2. fixed_count
134+ || abi1. can_unwind != abi2. can_unwind
135+ {
136+ return false ;
137+ }
138+
139+ test_arg_abi_eq ( & abi1. ret , & abi2. ret )
140+ && abi1. args . iter ( ) . zip ( abi2. args . iter ( ) ) . all ( |( arg1, arg2) | test_arg_abi_eq ( arg1, arg2) )
141+ }
142+
117143fn dump_abi_of_fn_type ( tcx : TyCtxt < ' _ > , item_def_id : DefId , attr : & Attribute ) {
118144 let param_env = tcx. param_env ( item_def_id) ;
119145 let ty = tcx. type_of ( item_def_id) . instantiate_identity ( ) ;
@@ -140,6 +166,54 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: DefId, attr: &Attribute) {
140166 fn_abi : format ! ( "{:#?}" , abi) ,
141167 } ) ;
142168 }
169+ sym:: assert_eq => {
170+ let ty:: Tuple ( fields) = ty. kind ( ) else {
171+ span_bug ! (
172+ meta_item. span( ) ,
173+ "`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
174+ ) ;
175+ } ;
176+ let [ field1, field2] = * * * fields else {
177+ span_bug ! (
178+ meta_item. span( ) ,
179+ "`#[rustc_abi(assert_eq)]` on a type alias requires pair type"
180+ ) ;
181+ } ;
182+ let ty:: FnPtr ( sig1) = field1. kind ( ) else {
183+ span_bug ! (
184+ meta_item. span( ) ,
185+ "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
186+ ) ;
187+ } ;
188+ let abi1 = unwrap_fn_abi (
189+ tcx. fn_abi_of_fn_ptr (
190+ param_env. and ( ( * sig1, /* extra_args */ ty:: List :: empty ( ) ) ) ,
191+ ) ,
192+ tcx,
193+ item_def_id,
194+ ) ;
195+ let ty:: FnPtr ( sig2) = field2. kind ( ) else {
196+ span_bug ! (
197+ meta_item. span( ) ,
198+ "`#[rustc_abi(assert_eq)]` on a type alias requires pair of function pointer types"
199+ ) ;
200+ } ;
201+ let abi2 = unwrap_fn_abi (
202+ tcx. fn_abi_of_fn_ptr (
203+ param_env. and ( ( * sig2, /* extra_args */ ty:: List :: empty ( ) ) ) ,
204+ ) ,
205+ tcx,
206+ item_def_id,
207+ ) ;
208+
209+ if !test_abi_eq ( abi1, abi2) {
210+ tcx. sess . emit_err ( AbiNe {
211+ span : tcx. def_span ( item_def_id) ,
212+ left : format ! ( "{:#?}" , abi1) ,
213+ right : format ! ( "{:#?}" , abi2) ,
214+ } ) ;
215+ }
216+ }
143217 name => {
144218 tcx. sess . emit_err ( UnrecognizedField { span : meta_item. span ( ) , name } ) ;
145219 }
0 commit comments