11use std:: iter;
22
3- use rustc_abi:: ExternAbi ;
3+ use rustc_abi:: { CanonAbi , ExternAbi } ;
44use rustc_ast:: util:: parser:: ExprPrecedence ;
55use rustc_errors:: { Applicability , Diag , ErrorGuaranteed , StashKey } ;
66use rustc_hir:: def:: { self , CtorKind , Namespace , Res } ;
@@ -16,6 +16,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
1616use rustc_middle:: { bug, span_bug} ;
1717use rustc_span:: def_id:: LocalDefId ;
1818use rustc_span:: { Span , sym} ;
19+ use rustc_target:: spec:: { AbiMap , AbiMapping } ;
1920use rustc_trait_selection:: error_reporting:: traits:: DefIdOrName ;
2021use rustc_trait_selection:: infer:: InferCtxtExt as _;
2122use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt as _;
@@ -84,7 +85,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8485 while result. is_none ( ) && autoderef. next ( ) . is_some ( ) {
8586 result = self . try_overloaded_call_step ( call_expr, callee_expr, arg_exprs, & autoderef) ;
8687 }
87- self . check_call_custom_abi ( autoderef. final_ty ( false ) , call_expr. span ) ;
88+
89+ match autoderef. final_ty ( false ) . kind ( ) {
90+ ty:: FnDef ( def_id, _) => {
91+ let abi = self . tcx . fn_sig ( def_id) . skip_binder ( ) . skip_binder ( ) . abi ;
92+ self . check_call_abi ( abi, call_expr. span ) ;
93+ }
94+ ty:: FnPtr ( _, header) => {
95+ self . check_call_abi ( header. abi , call_expr. span ) ;
96+ }
97+ _ => { /* cannot have a non-rust abi */ }
98+ }
99+
88100 self . register_predicates ( autoderef. into_obligations ( ) ) ;
89101
90102 let output = match result {
@@ -137,19 +149,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
137149 output
138150 }
139151
140- /// Functions of type `extern "custom" fn(/* ... */)` cannot be called using `ExprKind::Call`.
152+ /// Can a function with this ABI be called with a rust call expression?
141153 ///
142- /// These functions have a calling convention that is unknown to rust, hence it cannot generate
143- /// code for the call. The only way to execute such a function is via inline assembly.
144- fn check_call_custom_abi ( & self , callee_ty : Ty < ' tcx > , span : Span ) {
145- let abi = match callee_ty. kind ( ) {
146- ty:: FnDef ( def_id, _) => self . tcx . fn_sig ( def_id) . skip_binder ( ) . skip_binder ( ) . abi ,
147- ty:: FnPtr ( _, header) => header. abi ,
148- _ => return ,
154+ /// Some ABIs cannot be called from rust, either because rust does not know how to generate
155+ /// code for the call, or because a call does not semantically make sense.
156+ pub ( crate ) fn check_call_abi ( & self , abi : ExternAbi , span : Span ) {
157+ let canon_abi = match AbiMap :: from_target ( & self . sess ( ) . target ) . canonize_abi ( abi, false ) {
158+ AbiMapping :: Direct ( canon_abi) | AbiMapping :: Deprecated ( canon_abi) => canon_abi,
159+ AbiMapping :: Invalid => return ,
160+ } ;
161+
162+ let valid = match canon_abi {
163+ // Rust doesn't know how to call functions with this ABI.
164+ CanonAbi :: Custom => false ,
165+
166+ // These is an entry point for the host, and cannot be called on the GPU.
167+ CanonAbi :: GpuKernel => false ,
168+
169+ // The interrupt ABIs should only be called by the CPU. They have complex
170+ // pre- and postconditions, and can use non-standard instructions like `iret` on x86.
171+ CanonAbi :: Interrupt ( _) => false ,
172+
173+ CanonAbi :: C
174+ | CanonAbi :: Rust
175+ | CanonAbi :: RustCold
176+ | CanonAbi :: Arm ( _)
177+ | CanonAbi :: X86 ( _) => true ,
149178 } ;
150179
151- if let ExternAbi :: Custom = abi {
152- self . tcx . dcx ( ) . emit_err ( errors:: AbiCustomCall { span } ) ;
180+ if !valid {
181+ let err = crate :: errors:: AbiCannotBeCalled { span, abi } ;
182+ self . tcx . dcx ( ) . emit_err ( err) ;
153183 }
154184 }
155185
0 commit comments