Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1317,7 +1317,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
LocalRef::Operand(arg) => {
let Ref(place_value) = arg.val else {
bug!("only `Ref` should use `PassMode::Indirect`");
bug!(
"only `Ref` should use `PassMode::Indirect`, but got {:?}",
arg.val
);
};
bx.typed_place_copy(place_value, tmp.val, fn_abi.args[i].layout);
op.val = arg.val;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>(
}

// Check that argument is Sized.
if !params_can_be_unsized {
if !params_can_be_unsized || fn_sig.abi() == rustc_abi::ExternAbi::RustTail {
Comment thread
bjorn3 marked this conversation as resolved.
fcx.require_type_is_sized(
param_ty,
param.ty_span,
Expand Down
21 changes: 20 additions & 1 deletion compiler/rustc_mir_build/src/check_tail_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
// ```
// we should think what is the expected behavior here.
// (we should probably just accept this by revealing opaques?)
if caller_sig.inputs_and_output != callee_sig.inputs_and_output {
if caller_sig.inputs_and_output != callee_sig.inputs_and_output
&& !matches!(callee_sig.abi(), ExternAbi::RustTail)
{
let caller_ty = self.tcx.type_of(self.caller_def_id).skip_binder();

self.report_signature_mismatch(
Expand Down Expand Up @@ -189,6 +191,12 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
if callee_sig.c_variadic() {
self.report_c_variadic_callee(expr.span);
}

for &arg_ty in callee_sig.inputs() {
if !arg_ty.is_sized(self.tcx, self.typing_env) {
self.report_unsized_argument(expr.span, arg_ty);
}
}
}

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

self.found_errors = Err(err);
}

fn report_unsized_argument(&mut self, sp: Span, arg_ty: Ty<'tcx>) {
let err = self
.tcx
.dcx()
.struct_span_err(sp, format!("unsized arguments cannot be used in a tail call"))
.with_note(format!("unsized argument of type `{arg_ty}`"))
.emit();

self.found_errors = Err(err);
}
Comment thread
bjorn3 marked this conversation as resolved.
}

impl<'a, 'tcx> Visitor<'a, 'tcx> for TailCallCkVisitor<'a, 'tcx> {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_target/src/callconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,9 @@ impl<'a, Ty> FnAbi<'a, Ty> {
ArgAttribute::default()
};
arg.cast_to_with_attrs(Reg { kind: RegKind::Integer, size }, attr.into());
} else if self.conv == CanonAbi::RustTail {
assert!(arg.layout.is_sized(), "extern \"tail\" arguments must be sized");
arg.pass_by_stack_offset(None);
Comment thread
WaffleLapkin marked this conversation as resolved.
}
}

Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_ty_utils/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,8 +458,10 @@ fn fn_abi_sanity_check<'tcx>(
// omitted entirely in the calling convention.
assert!(arg.is_ignore());
}
if let PassMode::Indirect { on_stack, .. } = arg.mode {
assert!(!on_stack, "rust abi shouldn't use on_stack");
if let PassMode::Indirect { on_stack, .. } = arg.mode
&& spec_abi != ExternAbi::RustTail
{
assert!(!on_stack, "rustic abi {spec_abi:?} shouldn't use on_stack");
}
} else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
assert_matches!(
Expand Down
31 changes: 31 additions & 0 deletions tests/ui/explicit-tail-calls/no-unsized-arguments.aarch64.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/no-unsized-arguments.rs:40:42
|
LL | extern "tail" fn unsized_argument(x: [u8]) -> u8 {
| ^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
help: function arguments must have a statically known size, borrowed slices always have a known size
|
LL | extern "tail" fn unsized_argument(x: &[u8]) -> u8 {
| +

error: unsized arguments cannot be used in a tail call
--> $DIR/no-unsized-arguments.rs:33:5
|
LL | become unsized_argument(b);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: unsized argument of type `[u8]`

error: unsized arguments cannot be used in a tail call
--> $DIR/no-unsized-arguments.rs:48:5
|
LL | become unsized_argument(*b);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: unsized argument of type `[u8]`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
50 changes: 50 additions & 0 deletions tests/ui/explicit-tail-calls/no-unsized-arguments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//@ add-minicore
//@ ignore-backends: gcc
//@ min-llvm-version: 22
//
//@ revisions: x86 x86_64 aarch64
//
//@ [x86] compile-flags: --target=i686-unknown-linux-gnu
//@ [x86] needs-llvm-components: x86
//@ [x86_64] compile-flags: --target=x86_64-unknown-linux-gnu
//@ [x86_64] needs-llvm-components: x86
//@ [aarch64] compile-flags: --target=aarch64-unknown-linux-gnu
//@ [aarch64] needs-llvm-components: aarch64
#![feature(explicit_tail_calls, rust_tail_cc, unsized_fn_params, no_core)]
#![allow(incomplete_features, internal_features)]
#![no_core]
#![crate_type = "lib"]

extern crate minicore;
use minicore::*;

extern "C" {
fn extract(_: [u8]) -> u8;
}

fn vanilla(b: [u8]) -> u8 {
fn unsized_argument(x: [u8]) -> u8 {
unsafe { extract(x) }
}

// Non-tail call.
let _ = unsized_argument(b);

become unsized_argument(b);
//~^ ERROR unsized arguments cannot be used in a tail call
}

extern "tail" fn tailcc(b: &[u8]) -> u8 {
// `extern "tail"` is special because we also can't unsized parameters in standard definitions
// and calls.
extern "tail" fn unsized_argument(x: [u8]) -> u8 {
//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time
unsafe { extract(x) }
}

// Vanilla call.
let _ = unsized_argument(*b);

become unsized_argument(*b);
//~^ ERROR unsized arguments cannot be used in a tail call
}
31 changes: 31 additions & 0 deletions tests/ui/explicit-tail-calls/no-unsized-arguments.x86.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/no-unsized-arguments.rs:40:42
|
LL | extern "tail" fn unsized_argument(x: [u8]) -> u8 {
| ^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
help: function arguments must have a statically known size, borrowed slices always have a known size
|
LL | extern "tail" fn unsized_argument(x: &[u8]) -> u8 {
| +

error: unsized arguments cannot be used in a tail call
--> $DIR/no-unsized-arguments.rs:33:5
|
LL | become unsized_argument(b);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: unsized argument of type `[u8]`

error: unsized arguments cannot be used in a tail call
--> $DIR/no-unsized-arguments.rs:48:5
|
LL | become unsized_argument(*b);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: unsized argument of type `[u8]`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
31 changes: 31 additions & 0 deletions tests/ui/explicit-tail-calls/no-unsized-arguments.x86_64.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/no-unsized-arguments.rs:40:42
|
LL | extern "tail" fn unsized_argument(x: [u8]) -> u8 {
| ^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
help: function arguments must have a statically known size, borrowed slices always have a known size
|
LL | extern "tail" fn unsized_argument(x: &[u8]) -> u8 {
| +

error: unsized arguments cannot be used in a tail call
--> $DIR/no-unsized-arguments.rs:33:5
|
LL | become unsized_argument(b);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: unsized argument of type `[u8]`

error: unsized arguments cannot be used in a tail call
--> $DIR/no-unsized-arguments.rs:48:5
|
LL | become unsized_argument(*b);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: unsized argument of type `[u8]`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
12 changes: 8 additions & 4 deletions tests/ui/explicit-tail-calls/signature-mismatch.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![expect(incomplete_features)]
#![feature(explicit_tail_calls)]
#![feature(explicit_tail_calls, rust_tail_cc)]
#![feature(c_variadic)]

fn _f0((): ()) {
Expand All @@ -8,26 +8,30 @@ fn _f0((): ()) {

fn _g0() {}


fn _f1() {
become _g1(()); //~ error: mismatched signatures
}

fn _g1((): ()) {}


extern "C" fn _f2() {
become _g2(); //~ error: mismatched function ABIs
}

fn _g2() {}


fn _f3() {
become _g3(); //~ error: mismatched function ABIs
}

extern "C" fn _g3() {}

extern "tail" fn _tailcc() {}

fn _f4() {
// tailcc does not need the signatures to match,
// but only tailcc can tail call tailcc.
become _tailcc(); //~ error: mismatched function ABIs
}

fn main() {}
17 changes: 13 additions & 4 deletions tests/ui/explicit-tail-calls/signature-mismatch.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL | become _g0();
= note: callee signature: `fn()`

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

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

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

error: aborting due to 4 previous errors
error: mismatched function ABIs
--> $DIR/signature-mismatch.rs:34:5
|
LL | become _tailcc();
| ^^^^^^^^^^^^^^^^
|
= note: `become` requires caller and callee to have the same ABI
= note: caller ABI is `"Rust"`, while callee ABI is `"tail"`

error: aborting due to 5 previous errors

49 changes: 49 additions & 0 deletions tests/ui/explicit-tail-calls/tailcc-no-signature-restriction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//@ run-pass
//@ ignore-backends: gcc
//@ min-llvm-version: 22
//@ revisions: x86_64 aarch64
//
// FIXME: enable x86 on LLVM 23.
//@ [x86_64] only-x86_64
//@ [aarch64] only-aarch64
#![feature(explicit_tail_calls, rust_tail_cc)]

#[inline(never)]
pub extern "tail" fn add() -> u64 {
#[inline(never)]
extern "tail" fn add(a: u64, b: u64) -> u64 {
a.wrapping_add(b)
}

become add(1, 2);
}

#[inline(never)]
pub extern "tail" fn pass_struct(a: u64, d: u64) -> u64 {
#[derive(Clone, Copy)]
pub struct Large {
pub a: u64,
pub b: u64,
pub c: u64,
pub d: u64,
}

#[inline(never)]
extern "tail" fn add(large: Large) -> u64 {
let _ = large.b;
let _ = large.c;
large.a.wrapping_add(large.d)
}

let large = Large { a, b: 0xBBBB_BBBB_BBBB_BBBB, c: 0xCCCC_CCCC_CCCC_CCCC, d };
become add(large);
}

fn main() {
assert_eq!(add(), 3);

// FIXME: LLVM 22 has a bug which makes this miscompile.
if false {
assert_eq!(pass_struct(5, 6), 5 + 6);
}
Comment on lines +45 to +48

@folkertdev folkertdev Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything that doesn't fit into register actually runs into this bug, which is fixed in LLVM 23.

View changes since the review

}
Loading