Skip to content

Commit c96259d

Browse files
committed
error when ABI does not support guaranteed tail calls
1 parent 9312cd6 commit c96259d

File tree

11 files changed

+255
-0
lines changed

11 files changed

+255
-0
lines changed

compiler/rustc_abi/src/extern_abi.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,36 @@ impl ExternAbi {
276276
_ => CVariadicStatus::NotSupported,
277277
}
278278
}
279+
280+
/// Returns whether the ABI supports guaranteed tail calls.
281+
#[cfg(feature = "nightly")]
282+
pub fn supports_guaranteed_tail_call(self) -> bool {
283+
match self {
284+
Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {
285+
// See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers
286+
// before returning, and hence cannot guarantee a tail call.
287+
false
288+
}
289+
Self::AvrInterrupt
290+
| Self::AvrNonBlockingInterrupt
291+
| Self::Msp430Interrupt
292+
| Self::RiscvInterruptM
293+
| Self::RiscvInterruptS
294+
| Self::X86Interrupt => {
295+
// See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.
296+
false
297+
}
298+
Self::GpuKernel | Self::PtxKernel => {
299+
// See https://godbolt.org/z/jq5TE5jK1.
300+
false
301+
}
302+
Self::Custom => {
303+
// This ABI does not support calls at all (except via assembly).
304+
false
305+
}
306+
_ => true,
307+
}
308+
}
279309
}
280310

281311
pub fn all_names() -> Vec<&'static str> {

compiler/rustc_mir_build/src/check_tail_calls.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
135135
self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi);
136136
}
137137

138+
if !callee_sig.abi.supports_guaranteed_tail_call() {
139+
self.report_unsupported_abi(expr.span, callee_sig.abi);
140+
}
141+
138142
// FIXME(explicit_tail_calls): this currently fails for cases where opaques are used.
139143
// e.g.
140144
// ```
@@ -358,6 +362,16 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
358362
self.found_errors = Err(err);
359363
}
360364

365+
fn report_unsupported_abi(&mut self, sp: Span, callee_abi: ExternAbi) {
366+
let err = self
367+
.tcx
368+
.dcx()
369+
.struct_span_err(sp, "ABI does not support guaranteed tail calls")
370+
.with_note(format!("`become` is not supported for `extern {callee_abi}` functions"))
371+
.emit();
372+
self.found_errors = Err(err);
373+
}
374+
361375
fn report_signature_mismatch(
362376
&mut self,
363377
sp: Span,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//@ add-minicore
2+
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
3+
//@ needs-llvm-components: arm
4+
#![expect(incomplete_features)]
5+
#![feature(no_core, explicit_tail_calls, abi_cmse_nonsecure_call)]
6+
#![no_core]
7+
8+
extern crate minicore;
9+
use minicore::*;
10+
11+
unsafe extern "C" {
12+
safe fn magic() -> extern "cmse-nonsecure-call" fn(u32, u32) -> u32;
13+
}
14+
15+
// The `cmse-nonsecure-call` ABI can only occur on function pointers:
16+
//
17+
// - a `cmse-nonsecure-call` definition throws an error
18+
// - a `cmse-nonsecure-call` become in a definition with any other ABI is an ABI mismatch
19+
#[no_mangle]
20+
extern "cmse-nonsecure-call" fn become_nonsecure_call_1(x: u32, y: u32) -> u32 {
21+
//~^ ERROR the `"cmse-nonsecure-call"` ABI is only allowed on function pointers
22+
unsafe {
23+
let f = magic();
24+
become f(1, 2)
25+
//~^ ERROR ABI does not support guaranteed tail calls
26+
}
27+
}
28+
29+
#[no_mangle]
30+
extern "C" fn become_nonsecure_call_2(x: u32, y: u32) -> u32 {
31+
unsafe {
32+
let f = magic();
33+
become f(1, 2)
34+
//~^ ERROR mismatched function ABIs
35+
//~| ERROR ABI does not support guaranteed tail calls
36+
}
37+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
error[E0781]: the `"cmse-nonsecure-call"` ABI is only allowed on function pointers
2+
--> $DIR/cmse-nonsecure-call.rs:20:1
3+
|
4+
LL | extern "cmse-nonsecure-call" fn become_nonsecure_call_1(x: u32, y: u32) -> u32 {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: ABI does not support guaranteed tail calls
8+
--> $DIR/cmse-nonsecure-call.rs:24:9
9+
|
10+
LL | become f(1, 2)
11+
| ^^^^^^^^^^^^^^
12+
|
13+
= note: `become` is not supported for `extern "cmse-nonsecure-call"` functions
14+
15+
error: mismatched function ABIs
16+
--> $DIR/cmse-nonsecure-call.rs:33:9
17+
|
18+
LL | become f(1, 2)
19+
| ^^^^^^^^^^^^^^
20+
|
21+
= note: `become` requires caller and callee to have the same ABI
22+
= note: caller ABI is `"C"`, while callee ABI is `"cmse-nonsecure-call"`
23+
24+
error: ABI does not support guaranteed tail calls
25+
--> $DIR/cmse-nonsecure-call.rs:33:9
26+
|
27+
LL | become f(1, 2)
28+
| ^^^^^^^^^^^^^^
29+
|
30+
= note: `become` is not supported for `extern "cmse-nonsecure-call"` functions
31+
32+
error: aborting due to 4 previous errors
33+
34+
For more information about this error, try `rustc --explain E0781`.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ add-minicore
2+
//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
3+
//@ needs-llvm-components: arm
4+
#![expect(incomplete_features)]
5+
#![feature(no_core, explicit_tail_calls, cmse_nonsecure_entry)]
6+
#![no_core]
7+
8+
extern crate minicore;
9+
use minicore::*;
10+
11+
#[inline(never)]
12+
extern "cmse-nonsecure-entry" fn entry(c: bool, x: u32, y: u32) -> u32 {
13+
if c { x } else { y }
14+
}
15+
16+
// A `cmse-nonsecure-entry` clears registers before returning, so a tail call cannot be guaranteed.
17+
#[unsafe(no_mangle)]
18+
extern "cmse-nonsecure-entry" fn become_nonsecure_entry(c: bool, x: u32, y: u32) -> u32 {
19+
become entry(c, x, y)
20+
//~^ ERROR ABI does not support guaranteed tail calls
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: ABI does not support guaranteed tail calls
2+
--> $DIR/cmse-nonsecure-entry.rs:19:5
3+
|
4+
LL | become entry(c, x, y)
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `become` is not supported for `extern "cmse-nonsecure-entry"` functions
8+
9+
error: aborting due to 1 previous error
10+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: functions with the "gpu-kernel" ABI cannot be called
2+
--> $DIR/gpu-kernel.rs:21:12
3+
|
4+
LL | become f2(ptr)
5+
| ^^^^^^^
6+
|
7+
note: an `extern "gpu-kernel"` function can only be called using inline assembly
8+
--> $DIR/gpu-kernel.rs:21:12
9+
|
10+
LL | become f2(ptr)
11+
| ^^^^^^^
12+
13+
error: ABI does not support guaranteed tail calls
14+
--> $DIR/gpu-kernel.rs:21:5
15+
|
16+
LL | become f2(ptr)
17+
| ^^^^^^^^^^^^^^
18+
|
19+
= note: `become` is not supported for `extern "gpu-kernel"` functions
20+
21+
error: aborting due to 2 previous errors
22+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: functions with the "gpu-kernel" ABI cannot be called
2+
--> $DIR/gpu-kernel.rs:21:12
3+
|
4+
LL | become f2(ptr)
5+
| ^^^^^^^
6+
|
7+
note: an `extern "gpu-kernel"` function can only be called using inline assembly
8+
--> $DIR/gpu-kernel.rs:21:12
9+
|
10+
LL | become f2(ptr)
11+
| ^^^^^^^
12+
13+
error: ABI does not support guaranteed tail calls
14+
--> $DIR/gpu-kernel.rs:21:5
15+
|
16+
LL | become f2(ptr)
17+
| ^^^^^^^^^^^^^^
18+
|
19+
= note: `become` is not supported for `extern "gpu-kernel"` functions
20+
21+
error: aborting due to 2 previous errors
22+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Checks that the gpu-kernel calling convention correctly translates to LLVM calling conventions.
2+
3+
//@ add-minicore
4+
//@ revisions: amdgpu nvptx
5+
//@ [amdgpu] compile-flags: --crate-type=rlib --target=amdgcn-amd-amdhsa -Ctarget-cpu=gfx900
6+
//@ [amdgpu] needs-llvm-components: amdgpu
7+
//@ [nvptx] compile-flags: --crate-type=rlib --target=nvptx64-nvidia-cuda
8+
//@ [nvptx] needs-llvm-components: nvptx
9+
#![expect(incomplete_features)]
10+
#![feature(no_core, explicit_tail_calls, abi_gpu_kernel)]
11+
#![no_core]
12+
13+
extern crate minicore;
14+
use minicore::*;
15+
16+
#[inline(never)]
17+
extern "gpu-kernel" fn f2(ptr: *mut i32) {}
18+
19+
#[no_mangle]
20+
extern "gpu-kernel" fn f1(ptr: *mut i32) {
21+
become f2(ptr)
22+
//~^ ERROR ABI does not support guaranteed tail calls
23+
//~| ERROR functions with the "gpu-kernel" ABI cannot be called
24+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ add-minicore
2+
//@ compile-flags: --target x86_64-unknown-linux-gnu --crate-type lib
3+
//@ needs-llvm-components: x86
4+
#![expect(incomplete_features)]
5+
#![feature(no_core, explicit_tail_calls, abi_x86_interrupt)]
6+
#![no_core]
7+
8+
extern crate minicore;
9+
use minicore::*;
10+
11+
#[inline(never)]
12+
extern "x86-interrupt" fn f2(_sf: *mut u8) {}
13+
14+
#[no_mangle]
15+
extern "x86-interrupt" fn f1(sf: *mut u8) {
16+
become f2(sf)
17+
//~^ ERROR ABI does not support guaranteed tail calls
18+
//~| ERROR functions with the "x86-interrupt" ABI cannot be called
19+
}

0 commit comments

Comments
 (0)