Skip to content

Commit d8d4b89

Browse files
committed
Lift the same-signature restriction for extern "tail"
1 parent 9030e34 commit d8d4b89

12 files changed

Lines changed: 240 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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,9 @@ impl<'a, Ty> FnAbi<'a, Ty> {
827827
ArgAttribute::default()
828828
};
829829
arg.cast_to_with_attrs(Reg { kind: RegKind::Integer, size }, attr.into());
830+
} else if self.conv == CanonAbi::RustTail {
831+
assert!(arg.layout.is_sized(), "extern \"tail\" arguments must be sized");
832+
arg.pass_by_stack_offset(None);
830833
}
831834
}
832835

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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
2+
--> $DIR/no-unsized-arguments.rs:40: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:33:5
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:48:5
23+
|
24+
LL | become unsized_argument(*b);
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
|
27+
= note: unsized argument of type `[u8]`
28+
29+
error: aborting due to 3 previous errors
30+
31+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//@ add-minicore
2+
//@ ignore-backends: gcc
3+
//@ min-llvm-version: 22
4+
//
5+
//@ revisions: x86 x86_64 aarch64
6+
//
7+
//@ [x86] compile-flags: --target=i686-unknown-linux-gnu
8+
//@ [x86] needs-llvm-components: x86
9+
//@ [x86_64] compile-flags: --target=x86_64-unknown-linux-gnu
10+
//@ [x86_64] needs-llvm-components: x86
11+
//@ [aarch64] compile-flags: --target=aarch64-unknown-linux-gnu
12+
//@ [aarch64] needs-llvm-components: aarch64
13+
#![feature(explicit_tail_calls, rust_tail_cc, unsized_fn_params, no_core)]
14+
#![allow(incomplete_features, internal_features)]
15+
#![no_core]
16+
#![crate_type = "lib"]
17+
18+
extern crate minicore;
19+
use minicore::*;
20+
21+
extern "C" {
22+
fn extract(_: [u8]) -> u8;
23+
}
24+
25+
fn vanilla(b: [u8]) -> u8 {
26+
fn unsized_argument(x: [u8]) -> u8 {
27+
unsafe { extract(x) }
28+
}
29+
30+
// Non-tail call.
31+
let _ = unsized_argument(b);
32+
33+
become unsized_argument(b);
34+
//~^ ERROR unsized arguments cannot be used in a tail call
35+
}
36+
37+
extern "tail" fn tailcc(b: &[u8]) -> u8 {
38+
// `extern "tail"` is special because we also can't unsized parameters in standard definitions
39+
// and calls.
40+
extern "tail" fn unsized_argument(x: [u8]) -> u8 {
41+
//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time
42+
unsafe { extract(x) }
43+
}
44+
45+
// Vanilla call.
46+
let _ = unsized_argument(*b);
47+
48+
become unsized_argument(*b);
49+
//~^ ERROR unsized arguments cannot be used in a tail call
50+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
2+
--> $DIR/no-unsized-arguments.rs:40: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:33:5
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:48:5
23+
|
24+
LL | become unsized_argument(*b);
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
|
27+
= note: unsized argument of type `[u8]`
28+
29+
error: aborting due to 3 previous errors
30+
31+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
2+
--> $DIR/no-unsized-arguments.rs:40: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:33:5
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:48:5
23+
|
24+
LL | become unsized_argument(*b);
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
|
27+
= note: unsized argument of type `[u8]`
28+
29+
error: aborting due to 3 previous errors
30+
31+
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() {}

0 commit comments

Comments
 (0)