Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@folkertdev
Copy link
Contributor

@folkertdev folkertdev commented Nov 12, 2025

Some ABIs cannot support guaranteed tail calls. There isn't really an exhaustive list, so this is a best effort. Conveniently, we already disallow calling most of these directly anyway. The only exception that I was able to trigger an LLVM assertion with so far was cmse-nonsecure-entry.

For that calling convention, LLVM specifically notes that (guaranteed) tail calls cannot be supported:

https://github.com/llvm/llvm-project/blob/28dbbba6c3a4e026e085c48cc022cb97b5d8bc6d/llvm/lib/Target/ARM/ARMISelLowering.cpp#L2331-L2335


I have some doubts about the implementation here though. I think it would be nicer to use CanonAbi, and move the become ABI check into rustc_hir_typeck, similar to check_call_abi:

pub(crate) fn check_call_abi(&self, abi: ExternAbi, span: Span) {
let canon_abi = match AbiMap::from_target(&self.sess().target).canonize_abi(abi, false) {
AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => canon_abi,
AbiMapping::Invalid => {
// This should be reported elsewhere, but we want to taint this body
// so that we don't try to evaluate calls to ABIs that are invalid.
let guar = self.dcx().span_delayed_bug(
span,
format!("invalid abi for platform should have reported an error: {abi}"),
);
self.set_tainted_by_errors(guar);
return;
}
};
let valid = match canon_abi {
// Rust doesn't know how to call functions with this ABI.
CanonAbi::Custom => false,
// These is an entry point for the host, and cannot be called on the GPU.
CanonAbi::GpuKernel => false,
// The interrupt ABIs should only be called by the CPU. They have complex
// pre- and postconditions, and can use non-standard instructions like `iret` on x86.
CanonAbi::Interrupt(_) => false,
CanonAbi::C
| CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::Arm(_)
| CanonAbi::X86(_) => true,
};
if !valid {
let err = crate::errors::AbiCannotBeCalled { span, abi };
self.tcx.dcx().emit_err(err);
}
}

Both the check for whether an ABI is callable and whether it supports guaranteed tail calls can then be methods (containing exhaustive matches) on CanonAbi. I'm however not sure

  • if the ABI checks are deliberately only performed when constructing MIR
  • what assumptions can be made about the call expression in check_expr_become, it looks like currently the check that the "argument" to become is a function call also only occurs later during MIR construction

Are there issues with validating the ABI earlier in rustc_hir_typeck that I'm overlooking? I believe that we should already know the call's ABI and whether it is c-variadic at that point.

cc @workingjubilee for CanonAbi, @davidtwco for cmse
r? @WaffleLapkin

@folkertdev folkertdev added F-cmse_nonsecure_entry `#![feature(cmse_nonsecure_entry)]` F-abi_cmse_nonsecure_call `#![feature(abi_cmse_nonsecure_call)]` F-explicit_tail_calls `#![feature(explicit_tail_calls)]` labels Nov 12, 2025
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 12, 2025
@rustbot
Copy link
Collaborator

rustbot commented Nov 12, 2025

WaffleLapkin is not on the review rotation at the moment.
They may take a while to respond.

@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the tail-call-unsupported-abi branch from c96259d to 77632f7 Compare November 12, 2025 23:57
Copy link
Member

@WaffleLapkin WaffleLapkin left a comment

Choose a reason for hiding this comment

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

r=me with nits

re: checking this in hir_typeck, I'm working on a PR to move all tail call checks earlier, so :)

View changes since this review

@folkertdev folkertdev force-pushed the tail-call-unsupported-abi branch from 77632f7 to 78beefe Compare November 13, 2025 14:31
Comment on lines +306 to +321
Self::C { .. }
| Self::System { .. }
| Self::Rust
| Self::RustCall
| Self::RustCold
| Self::RustInvalid
| Self::Unadjusted
| Self::EfiApi
| Self::Aapcs { .. }
| Self::Cdecl { .. }
| Self::Stdcall { .. }
| Self::Fastcall { .. }
| Self::Thiscall { .. }
| Self::Vectorcall { .. }
| Self::SysV64 { .. }
| Self::Win64 { .. } => true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

When stabilization comes closer, we might want to do something like what c-variadic does above and classify specifically for which ABIs tail calls are stabilized. Because I don't think we (at least currently) test all of these ABIs thoroughly.

@WaffleLapkin WaffleLapkin added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Nov 13, 2025
@folkertdev
Copy link
Contributor Author

nits fixed, so

@bors r=WaffleLapkin

@bors
Copy link
Collaborator

bors commented Nov 13, 2025

📌 Commit 78beefe has been approved by WaffleLapkin

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Nov 13, 2025
Zalathar added a commit to Zalathar/rust that referenced this pull request Nov 13, 2025
…bi, r=WaffleLapkin

error when ABI does not support guaranteed tail calls

Some ABIs cannot support guaranteed tail calls. There isn't really an exhaustive list, so this is a best effort. Conveniently, we already disallow calling most of these directly anyway. The only exception that I was able to trigger an LLVM assertion with so far was `cmse-nonsecure-entry`.

For that calling convention, LLVM specifically notes that  (guaranteed) tail calls cannot be supported:

https://github.com/llvm/llvm-project/blob/28dbbba6c3a4e026e085c48cc022cb97b5d8bc6d/llvm/lib/Target/ARM/ARMISelLowering.cpp#L2331-L2335

---

I have some doubts about the implementation here though. I think it would be nicer to use `CanonAbi`, and move the `become` ABI check into `rustc_hir_typeck`, similar to `check_call_abi`:

https://github.com/rust-lang/rust/blob/d6deffe2debecc66501e50f9573214139ab4d678/compiler/rustc_hir_typeck/src/callee.rs#L157-L194

Both the check for whether an ABI is callable and whether it supports guaranteed tail calls can then be methods (containing exhaustive matches) on `CanonAbi`. I'm however not sure

- if the ABI checks are deliberately only performed when constructing MIR
- what assumptions can be made about the `call` expression in [`check_expr_become`](https://github.com/rust-lang/rust/blob/d6deffe2debecc66501e50f9573214139ab4d678/compiler/rustc_hir_typeck/src/expr.rs#L1126-L1150), it looks like currently the check that the "argument" to `become` is a function call also only occurs later during MIR construction

Are there issues with validating the ABI earlier in `rustc_hir_typeck` that I'm overlooking? I believe that we should already know the call's ABI and whether it is c-variadic at that point.

cc `@workingjubilee` for `CanonAbi`, `@davidtwco` for cmse
r? `@WaffleLapkin`
Zalathar added a commit to Zalathar/rust that referenced this pull request Nov 13, 2025
…bi, r=WaffleLapkin

error when ABI does not support guaranteed tail calls

Some ABIs cannot support guaranteed tail calls. There isn't really an exhaustive list, so this is a best effort. Conveniently, we already disallow calling most of these directly anyway. The only exception that I was able to trigger an LLVM assertion with so far was `cmse-nonsecure-entry`.

For that calling convention, LLVM specifically notes that  (guaranteed) tail calls cannot be supported:

https://github.com/llvm/llvm-project/blob/28dbbba6c3a4e026e085c48cc022cb97b5d8bc6d/llvm/lib/Target/ARM/ARMISelLowering.cpp#L2331-L2335

---

I have some doubts about the implementation here though. I think it would be nicer to use `CanonAbi`, and move the `become` ABI check into `rustc_hir_typeck`, similar to `check_call_abi`:

https://github.com/rust-lang/rust/blob/d6deffe2debecc66501e50f9573214139ab4d678/compiler/rustc_hir_typeck/src/callee.rs#L157-L194

Both the check for whether an ABI is callable and whether it supports guaranteed tail calls can then be methods (containing exhaustive matches) on `CanonAbi`. I'm however not sure

- if the ABI checks are deliberately only performed when constructing MIR
- what assumptions can be made about the `call` expression in [`check_expr_become`](https://github.com/rust-lang/rust/blob/d6deffe2debecc66501e50f9573214139ab4d678/compiler/rustc_hir_typeck/src/expr.rs#L1126-L1150), it looks like currently the check that the "argument" to `become` is a function call also only occurs later during MIR construction

Are there issues with validating the ABI earlier in `rustc_hir_typeck` that I'm overlooking? I believe that we should already know the call's ABI and whether it is c-variadic at that point.

cc ``@workingjubilee`` for `CanonAbi`, ``@davidtwco`` for cmse
r? ``@WaffleLapkin``
bors added a commit that referenced this pull request Nov 14, 2025
Rollup of 14 pull requests

Successful merges:

 - #146978 (Emit error when using path-segment keyword as cfg pred)
 - #148543 (Correctly link to associated trait items in reexports)
 - #148808 (Some resolve cleanups)
 - #148812 (coverage: Associate hole spans with expansion tree nodes )
 - #148826 (CStr docs: Fix CStr vs &CStr confusion)
 - #148850 (Implement `Read::read_array`)
 - #148867 (Refactor `Box::take`)
 - #148870 (Remove unused LLVMModuleRef argument)
 - #148878 (error when ABI does not support guaranteed tail calls)
 - #148901 (Disable rustdoc-test-builder test partially for SGX target.)
 - #148902 (add missing s390x target feature to std detect test)
 - #148904 (waffle: stop watching codegen ssa)
 - #148906 (Expose fmt::Arguments::from_str as unstable.)
 - #148907 (add assembly test for infinite recursion with `become`)

r? `@ghost`
`@rustbot` modify labels: rollup
Zalathar added a commit to Zalathar/rust that referenced this pull request Nov 14, 2025
…bi, r=WaffleLapkin

error when ABI does not support guaranteed tail calls

Some ABIs cannot support guaranteed tail calls. There isn't really an exhaustive list, so this is a best effort. Conveniently, we already disallow calling most of these directly anyway. The only exception that I was able to trigger an LLVM assertion with so far was `cmse-nonsecure-entry`.

For that calling convention, LLVM specifically notes that  (guaranteed) tail calls cannot be supported:

https://github.com/llvm/llvm-project/blob/28dbbba6c3a4e026e085c48cc022cb97b5d8bc6d/llvm/lib/Target/ARM/ARMISelLowering.cpp#L2331-L2335

---

I have some doubts about the implementation here though. I think it would be nicer to use `CanonAbi`, and move the `become` ABI check into `rustc_hir_typeck`, similar to `check_call_abi`:

https://github.com/rust-lang/rust/blob/d6deffe2debecc66501e50f9573214139ab4d678/compiler/rustc_hir_typeck/src/callee.rs#L157-L194

Both the check for whether an ABI is callable and whether it supports guaranteed tail calls can then be methods (containing exhaustive matches) on `CanonAbi`. I'm however not sure

- if the ABI checks are deliberately only performed when constructing MIR
- what assumptions can be made about the `call` expression in [`check_expr_become`](https://github.com/rust-lang/rust/blob/d6deffe2debecc66501e50f9573214139ab4d678/compiler/rustc_hir_typeck/src/expr.rs#L1126-L1150), it looks like currently the check that the "argument" to `become` is a function call also only occurs later during MIR construction

Are there issues with validating the ABI earlier in `rustc_hir_typeck` that I'm overlooking? I believe that we should already know the call's ABI and whether it is c-variadic at that point.

cc ```@workingjubilee``` for `CanonAbi`, ```@davidtwco``` for cmse
r? ```@WaffleLapkin```
Zalathar added a commit to Zalathar/rust that referenced this pull request Nov 14, 2025
…bi, r=WaffleLapkin

error when ABI does not support guaranteed tail calls

Some ABIs cannot support guaranteed tail calls. There isn't really an exhaustive list, so this is a best effort. Conveniently, we already disallow calling most of these directly anyway. The only exception that I was able to trigger an LLVM assertion with so far was `cmse-nonsecure-entry`.

For that calling convention, LLVM specifically notes that  (guaranteed) tail calls cannot be supported:

https://github.com/llvm/llvm-project/blob/28dbbba6c3a4e026e085c48cc022cb97b5d8bc6d/llvm/lib/Target/ARM/ARMISelLowering.cpp#L2331-L2335

---

I have some doubts about the implementation here though. I think it would be nicer to use `CanonAbi`, and move the `become` ABI check into `rustc_hir_typeck`, similar to `check_call_abi`:

https://github.com/rust-lang/rust/blob/d6deffe2debecc66501e50f9573214139ab4d678/compiler/rustc_hir_typeck/src/callee.rs#L157-L194

Both the check for whether an ABI is callable and whether it supports guaranteed tail calls can then be methods (containing exhaustive matches) on `CanonAbi`. I'm however not sure

- if the ABI checks are deliberately only performed when constructing MIR
- what assumptions can be made about the `call` expression in [`check_expr_become`](https://github.com/rust-lang/rust/blob/d6deffe2debecc66501e50f9573214139ab4d678/compiler/rustc_hir_typeck/src/expr.rs#L1126-L1150), it looks like currently the check that the "argument" to `become` is a function call also only occurs later during MIR construction

Are there issues with validating the ABI earlier in `rustc_hir_typeck` that I'm overlooking? I believe that we should already know the call's ABI and whether it is c-variadic at that point.

cc ````@workingjubilee```` for `CanonAbi`, ````@davidtwco```` for cmse
r? ````@WaffleLapkin````
bors added a commit that referenced this pull request Nov 14, 2025
Rollup of 15 pull requests

Successful merges:

 - #148543 (Correctly link to associated trait items in reexports)
 - #148808 (Some resolve cleanups)
 - #148812 (coverage: Associate hole spans with expansion tree nodes )
 - #148826 (CStr docs: Fix CStr vs &CStr confusion)
 - #148850 (Implement `Read::read_array`)
 - #148867 (Refactor `Box::take`)
 - #148870 (Remove unused LLVMModuleRef argument)
 - #148878 (error when ABI does not support guaranteed tail calls)
 - #148901 (Disable rustdoc-test-builder test partially for SGX target.)
 - #148902 (add missing s390x target feature to std detect test)
 - #148904 (waffle: stop watching codegen ssa)
 - #148906 (Expose fmt::Arguments::from_str as unstable.)
 - #148907 (add assembly test for infinite recursion with `become`)
 - #148928 (Move & adjust some `!`-adjacent tests)
 - #148929 (ignore `build-rust-analyzer` even if it's a symlink)

r? `@ghost`
`@rustbot` modify labels: rollup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

F-abi_cmse_nonsecure_call `#![feature(abi_cmse_nonsecure_call)]` F-cmse_nonsecure_entry `#![feature(cmse_nonsecure_entry)]` F-explicit_tail_calls `#![feature(explicit_tail_calls)]` S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants