Skip to content

Commit 73d1dea

Browse files
committed
Lift the same-signature restriction for extern "tail"
1 parent b998449 commit 73d1dea

10 files changed

Lines changed: 177 additions & 13 deletions

File tree

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1317,7 +1317,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
13171317
}
13181318
LocalRef::Operand(arg) => {
13191319
let Ref(place_value) = arg.val else {
1320-
bug!("only `Ref` should use `PassMode::Indirect`");
1320+
bug!(
1321+
"only `Ref` should use `PassMode::Indirect`, but got {:?}",
1322+
arg.val
1323+
);
13211324
};
13221325
bx.typed_place_copy(place_value, tmp.val, fn_abi.args[i].layout);
13231326
op.val = arg.val;

compiler/rustc_hir_typeck/src/check.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>(
9090
}
9191

9292
// Check that argument is Sized.
93-
if !params_can_be_unsized {
93+
if !params_can_be_unsized || fn_sig.abi() == rustc_abi::ExternAbi::RustTail {
9494
fcx.require_type_is_sized(
9595
param_ty,
9696
param.ty_span,

compiler/rustc_mir_build/src/check_tail_calls.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
147147
// ```
148148
// we should think what is the expected behavior here.
149149
// (we should probably just accept this by revealing opaques?)
150-
if caller_sig.inputs_and_output != callee_sig.inputs_and_output {
150+
if caller_sig.inputs_and_output != callee_sig.inputs_and_output
151+
&& !matches!(callee_sig.abi(), ExternAbi::RustTail)
152+
{
151153
let caller_ty = self.tcx.type_of(self.caller_def_id).skip_binder();
152154

153155
self.report_signature_mismatch(
@@ -189,6 +191,12 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
189191
if callee_sig.c_variadic() {
190192
self.report_c_variadic_callee(expr.span);
191193
}
194+
195+
for &arg_ty in callee_sig.inputs() {
196+
if !arg_ty.is_sized(self.tcx, self.typing_env) {
197+
self.report_unsized_argument(expr.span, arg_ty);
198+
}
199+
}
192200
}
193201

194202
/// Returns true if the caller function needs a location argument
@@ -417,6 +425,17 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
417425

418426
self.found_errors = Err(err);
419427
}
428+
429+
fn report_unsized_argument(&mut self, sp: Span, arg_ty: Ty<'tcx>) {
430+
let err = self
431+
.tcx
432+
.dcx()
433+
.struct_span_err(sp, format!("unsized arguments cannot be used in a tail call"))
434+
.with_note(format!("unsized argument of type `{arg_ty}`"))
435+
.emit();
436+
437+
self.found_errors = Err(err);
438+
}
420439
}
421440

422441
impl<'a, 'tcx> Visitor<'a, 'tcx> for TailCallCkVisitor<'a, 'tcx> {

compiler/rustc_target/src/callconv/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,10 @@ impl<'a, Ty> FnAbi<'a, Ty> {
828828
};
829829
arg.cast_to_with_attrs(Reg { kind: RegKind::Integer, size }, attr.into());
830830
}
831+
832+
if self.conv == CanonAbi::RustTail && arg.layout.is_sized() {
833+
arg.pass_by_stack_offset(None);
834+
}
831835
}
832836

833837
BackendRepr::SimdVector { .. } => {

compiler/rustc_ty_utils/src/abi.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,10 @@ fn fn_abi_sanity_check<'tcx>(
458458
// omitted entirely in the calling convention.
459459
assert!(arg.is_ignore());
460460
}
461-
if let PassMode::Indirect { on_stack, .. } = arg.mode {
462-
assert!(!on_stack, "rust abi shouldn't use on_stack");
461+
if let PassMode::Indirect { on_stack, .. } = arg.mode
462+
&& spec_abi != ExternAbi::RustTail
463+
{
464+
assert!(!on_stack, "rustic abi {spec_abi:?} shouldn't use on_stack");
463465
}
464466
} else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
465467
assert_matches!(
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//@ check-fail
2+
//@ min-llvm-version: 22
3+
//
4+
//@ revisions: x86 x86_64 aarch64
5+
//
6+
//@ [x86] only-x86
7+
//@ [x86_64] only-x86_64
8+
//@ [aarch64] only-aarch64
9+
#![feature(explicit_tail_calls, rust_tail_cc, unsized_fn_params)]
10+
#![allow(incomplete_features, internal_features)]
11+
#![crate_type = "lib"]
12+
13+
fn vanilla(_: [u8]) -> u8 {
14+
fn unsized_argument(x: [u8]) -> u8 {
15+
x[0]
16+
}
17+
18+
let b: Box<[u8]> = Box::new([1, 2, 3]);
19+
20+
// Non-tail call.
21+
let _ = unsized_argument(*b);
22+
23+
become unsized_argument(*b);
24+
//~^ ERROR unsized arguments cannot be used in a tail call
25+
}
26+
27+
extern "tail" fn tailcc() -> u8 {
28+
// `extern "tail"` is special because we also can't unsized parameters in standard definitions
29+
// and calls.
30+
extern "tail" fn unsized_argument(x: [u8]) -> u8 {
31+
//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time
32+
x[0]
33+
}
34+
let b: Box<[u8]> = Box::new([1, 2, 3]);
35+
36+
// Vanilla call.
37+
let _ = unsized_argument(*b);
38+
39+
become unsized_argument(*b);
40+
//~^ ERROR unsized arguments cannot be used in a tail call
41+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
2+
--> $DIR/no-unsized-arguments.rs:32:42
3+
|
4+
LL | extern "tail" fn unsized_argument(x: [u8]) -> u8 {
5+
| ^^^^ doesn't have a size known at compile-time
6+
|
7+
= help: the trait `Sized` is not implemented for `[u8]`
8+
help: function arguments must have a statically known size, borrowed slices always have a known size
9+
|
10+
LL | extern "tail" fn unsized_argument(x: &[u8]) -> u8 {
11+
| +
12+
13+
error: unsized arguments cannot be used in a tail call
14+
--> $DIR/no-unsized-arguments.rs:24:9
15+
|
16+
LL | become unsized_argument(*b);
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
|
19+
= note: unsized argument of type `[u8]`
20+
21+
error: unsized arguments cannot be used in a tail call
22+
--> $DIR/no-unsized-arguments.rs:43:9
23+
|
24+
LL | become unsized_argument(*b);
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
|
27+
= note: unsized argument of type `[u8]`
28+
29+
error: unsized arguments cannot be used in a tail call
30+
--> $DIR/no-unsized-arguments.rs:53:9
31+
|
32+
LL | become unsized_argument(*b);
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
34+
|
35+
= note: unsized argument of type `[u8]`
36+
37+
error: aborting due to 4 previous errors
38+
39+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![expect(incomplete_features)]
2-
#![feature(explicit_tail_calls)]
2+
#![feature(explicit_tail_calls, rust_tail_cc)]
33
#![feature(c_variadic)]
44

55
fn _f0((): ()) {
@@ -8,26 +8,30 @@ fn _f0((): ()) {
88

99
fn _g0() {}
1010

11-
1211
fn _f1() {
1312
become _g1(()); //~ error: mismatched signatures
1413
}
1514

1615
fn _g1((): ()) {}
1716

18-
1917
extern "C" fn _f2() {
2018
become _g2(); //~ error: mismatched function ABIs
2119
}
2220

2321
fn _g2() {}
2422

25-
2623
fn _f3() {
2724
become _g3(); //~ error: mismatched function ABIs
2825
}
2926

3027
extern "C" fn _g3() {}
3128

29+
extern "tail" fn _tailcc() {}
30+
31+
fn _f4() {
32+
// tailcc does not need the signatures to match,
33+
// but only tailcc can tail call tailcc.
34+
become _tailcc(); //~ error: mismatched function ABIs
35+
}
3236

3337
fn main() {}

tests/ui/explicit-tail-calls/signature-mismatch.stderr

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ LL | become _g0();
99
= note: callee signature: `fn()`
1010

1111
error: mismatched signatures
12-
--> $DIR/signature-mismatch.rs:13:5
12+
--> $DIR/signature-mismatch.rs:12:5
1313
|
1414
LL | become _g1(());
1515
| ^^^^^^^^^^^^^^
@@ -19,7 +19,7 @@ LL | become _g1(());
1919
= note: callee signature: `fn(())`
2020

2121
error: mismatched function ABIs
22-
--> $DIR/signature-mismatch.rs:20:5
22+
--> $DIR/signature-mismatch.rs:18:5
2323
|
2424
LL | become _g2();
2525
| ^^^^^^^^^^^^
@@ -28,13 +28,22 @@ LL | become _g2();
2828
= note: caller ABI is `"C"`, while callee ABI is `"Rust"`
2929

3030
error: mismatched function ABIs
31-
--> $DIR/signature-mismatch.rs:27:5
31+
--> $DIR/signature-mismatch.rs:24:5
3232
|
3333
LL | become _g3();
3434
| ^^^^^^^^^^^^
3535
|
3636
= note: `become` requires caller and callee to have the same ABI
3737
= note: caller ABI is `"Rust"`, while callee ABI is `"C"`
3838

39-
error: aborting due to 4 previous errors
39+
error: mismatched function ABIs
40+
--> $DIR/signature-mismatch.rs:34:5
41+
|
42+
LL | become _tailcc();
43+
| ^^^^^^^^^^^^^^^^
44+
|
45+
= note: `become` requires caller and callee to have the same ABI
46+
= note: caller ABI is `"Rust"`, while callee ABI is `"tail"`
47+
48+
error: aborting due to 5 previous errors
4049

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//@ run-pass
2+
//@ min-llvm-version: 22
3+
//@ revisions: x86 x86_64 aarch64
4+
//
5+
//@ [x86] only-x86
6+
//@ [x86_64] only-x86_64
7+
//@ [aarch64] only-aarch64
8+
#![feature(explicit_tail_calls, rust_tail_cc)]
9+
10+
pub extern "tail" fn add() -> u64 {
11+
#[inline(never)]
12+
extern "tail" fn add(a: u64, b: u64) -> u64 {
13+
a.wrapping_add(b)
14+
}
15+
16+
become add(1, 2);
17+
}
18+
19+
pub extern "tail" fn pass_struct(a: u64, d: u64) -> u64 {
20+
#[derive(Clone, Copy)]
21+
pub struct Large {
22+
pub a: u64,
23+
pub b: u64,
24+
pub c: u64,
25+
pub d: u64,
26+
}
27+
28+
#[inline(never)]
29+
extern "tail" fn add(large: Large) -> u64 {
30+
let _ = large.b;
31+
let _ = large.c;
32+
large.a.wrapping_add(large.d)
33+
}
34+
35+
let large = Large { a, b: 0xBBBB_BBBB_BBBB_BBBB, c: 0xCCCC_CCCC_CCCC_CCCC, d };
36+
become add(large);
37+
}
38+
39+
fn main() {
40+
assert_eq!(add(), 3);
41+
42+
assert_eq!(pass_struct(5, 6), 5 + 6);
43+
}

0 commit comments

Comments
 (0)