Skip to content

Commit b09ca75

Browse files
committed
error on calls to ABIs that cannot be called
1 parent f3db639 commit b09ca75

22 files changed

+1480
-335
lines changed

compiler/rustc_hir_typeck/messages.ftl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
hir_typeck_abi_custom_call =
2-
functions with the `"custom"` ABI cannot be called
3-
.note = an `extern "custom"` function can only be called from within inline assembly
1+
hir_typeck_abi_cannot_be_called =
2+
functions with the {$abi} ABI cannot be called
3+
.note = an `extern {$abi}` function can only be called using inline assembly
44
55
hir_typeck_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function
66

compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::iter;
22

3-
use rustc_abi::ExternAbi;
3+
use rustc_abi::{CanonAbi, ExternAbi};
44
use rustc_ast::util::parser::ExprPrecedence;
55
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
66
use rustc_hir::def::{self, CtorKind, Namespace, Res};
@@ -16,6 +16,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
1616
use rustc_middle::{bug, span_bug};
1717
use rustc_span::def_id::LocalDefId;
1818
use rustc_span::{Span, sym};
19+
use rustc_target::spec::{AbiMap, AbiMapping};
1920
use rustc_trait_selection::error_reporting::traits::DefIdOrName;
2021
use rustc_trait_selection::infer::InferCtxtExt as _;
2122
use 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

compiler/rustc_hir_typeck/src/errors.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use std::borrow::Cow;
44

5+
use rustc_abi::ExternAbi;
56
use rustc_ast::Label;
67
use rustc_errors::codes::*;
78
use rustc_errors::{
@@ -1165,8 +1166,10 @@ pub(crate) struct NakedFunctionsMustNakedAsm {
11651166
}
11661167

11671168
#[derive(Diagnostic)]
1168-
#[diag(hir_typeck_abi_custom_call)]
1169-
pub(crate) struct AbiCustomCall {
1169+
#[diag(hir_typeck_abi_cannot_be_called)]
1170+
pub(crate) struct AbiCannotBeCalled {
11701171
#[primary_span]
1172+
#[note]
11711173
pub span: Span,
1174+
pub abi: ExternAbi,
11721175
}

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//!
66
//! See [`rustc_hir_analysis::check`] for more context on type checking in general.
77
8-
use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx};
8+
use rustc_abi::{FIRST_VARIANT, FieldIdx};
99
use rustc_ast::util::parser::ExprPrecedence;
1010
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1111
use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -1651,13 +1651,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16511651
Some(method.def_id),
16521652
);
16531653

1654-
// Functions of type `extern "custom" fn(/* ... */)` cannot be called using
1655-
// `ExprKind::MethodCall`. These functions have a calling convention that is
1656-
// unknown to rust, hence it cannot generate code for the call. The only way
1657-
// to execute such a function is via inline assembly.
1658-
if let ExternAbi::Custom = method.sig.abi {
1659-
self.tcx.dcx().emit_err(crate::errors::AbiCustomCall { span: expr.span });
1660-
}
1654+
self.check_call_abi(method.sig.abi, expr.span);
16611655

16621656
method.sig.output()
16631657
}

tests/ui/abi/bad-custom.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,19 +73,19 @@ unsafe extern "custom" {
7373

7474
fn caller(f: unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
7575
unsafe { f(x) }
76-
//~^ ERROR functions with the `"custom"` ABI cannot be called
76+
//~^ ERROR functions with the "custom" ABI cannot be called
7777
}
7878

7979
fn caller_by_ref(f: &unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
8080
unsafe { f(x) }
81-
//~^ ERROR functions with the `"custom"` ABI cannot be called
81+
//~^ ERROR functions with the "custom" ABI cannot be called
8282
}
8383

8484
type Custom = unsafe extern "custom" fn(i64) -> i64;
8585

8686
fn caller_alias(f: Custom, mut x: i64) -> i64 {
8787
unsafe { f(x) }
88-
//~^ ERROR functions with the `"custom"` ABI cannot be called
88+
//~^ ERROR functions with the "custom" ABI cannot be called
8989
}
9090

9191
#[unsafe(naked)]
@@ -107,15 +107,15 @@ fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() {
107107
pub fn main() {
108108
unsafe {
109109
assert_eq!(double(21), 42);
110-
//~^ ERROR functions with the `"custom"` ABI cannot be called
110+
//~^ ERROR functions with the "custom" ABI cannot be called
111111

112112
assert_eq!(unsafe { increment(41) }, 42);
113-
//~^ ERROR functions with the `"custom"` ABI cannot be called
113+
//~^ ERROR functions with the "custom" ABI cannot be called
114114

115115
assert!(Thing(41).is_even());
116-
//~^ ERROR functions with the `"custom"` ABI cannot be called
116+
//~^ ERROR functions with the "custom" ABI cannot be called
117117

118118
assert_eq!(Thing::bitwise_not(42), !42);
119-
//~^ ERROR functions with the `"custom"` ABI cannot be called
119+
//~^ ERROR functions with the "custom" ABI cannot be called
120120
}
121121
}

tests/ui/abi/bad-custom.stderr

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -245,43 +245,85 @@ LL + #[unsafe(naked)]
245245
LL | extern "custom" fn negate(a: i64) -> i64 {
246246
|
247247

248-
error: functions with the `"custom"` ABI cannot be called
248+
error: functions with the "custom" ABI cannot be called
249+
--> $DIR/bad-custom.rs:75:14
250+
|
251+
LL | unsafe { f(x) }
252+
| ^^^^
253+
|
254+
note: an `extern "custom"` function can only be called using inline assembly
249255
--> $DIR/bad-custom.rs:75:14
250256
|
251257
LL | unsafe { f(x) }
252258
| ^^^^
253259

254-
error: functions with the `"custom"` ABI cannot be called
260+
error: functions with the "custom" ABI cannot be called
261+
--> $DIR/bad-custom.rs:80:14
262+
|
263+
LL | unsafe { f(x) }
264+
| ^^^^
265+
|
266+
note: an `extern "custom"` function can only be called using inline assembly
255267
--> $DIR/bad-custom.rs:80:14
256268
|
257269
LL | unsafe { f(x) }
258270
| ^^^^
259271

260-
error: functions with the `"custom"` ABI cannot be called
272+
error: functions with the "custom" ABI cannot be called
273+
--> $DIR/bad-custom.rs:87:14
274+
|
275+
LL | unsafe { f(x) }
276+
| ^^^^
277+
|
278+
note: an `extern "custom"` function can only be called using inline assembly
261279
--> $DIR/bad-custom.rs:87:14
262280
|
263281
LL | unsafe { f(x) }
264282
| ^^^^
265283

266-
error: functions with the `"custom"` ABI cannot be called
284+
error: functions with the "custom" ABI cannot be called
285+
--> $DIR/bad-custom.rs:109:20
286+
|
287+
LL | assert_eq!(double(21), 42);
288+
| ^^^^^^^^^^
289+
|
290+
note: an `extern "custom"` function can only be called using inline assembly
267291
--> $DIR/bad-custom.rs:109:20
268292
|
269293
LL | assert_eq!(double(21), 42);
270294
| ^^^^^^^^^^
271295

272-
error: functions with the `"custom"` ABI cannot be called
296+
error: functions with the "custom" ABI cannot be called
297+
--> $DIR/bad-custom.rs:112:29
298+
|
299+
LL | assert_eq!(unsafe { increment(41) }, 42);
300+
| ^^^^^^^^^^^^^
301+
|
302+
note: an `extern "custom"` function can only be called using inline assembly
273303
--> $DIR/bad-custom.rs:112:29
274304
|
275305
LL | assert_eq!(unsafe { increment(41) }, 42);
276306
| ^^^^^^^^^^^^^
277307

278-
error: functions with the `"custom"` ABI cannot be called
308+
error: functions with the "custom" ABI cannot be called
309+
--> $DIR/bad-custom.rs:115:17
310+
|
311+
LL | assert!(Thing(41).is_even());
312+
| ^^^^^^^^^^^^^^^^^^^
313+
|
314+
note: an `extern "custom"` function can only be called using inline assembly
279315
--> $DIR/bad-custom.rs:115:17
280316
|
281317
LL | assert!(Thing(41).is_even());
282318
| ^^^^^^^^^^^^^^^^^^^
283319

284-
error: functions with the `"custom"` ABI cannot be called
320+
error: functions with the "custom" ABI cannot be called
321+
--> $DIR/bad-custom.rs:118:20
322+
|
323+
LL | assert_eq!(Thing::bitwise_not(42), !42);
324+
| ^^^^^^^^^^^^^^^^^^^^^^
325+
|
326+
note: an `extern "custom"` function can only be called using inline assembly
285327
--> $DIR/bad-custom.rs:118:20
286328
|
287329
LL | assert_eq!(Thing::bitwise_not(42), !42);

0 commit comments

Comments
 (0)