Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement explicit tail calls #112657

Closed
wants to merge 17 commits into from

Conversation

WaffleLapkin
Copy link
Member

@WaffleLapkin WaffleLapkin commented Jun 15, 2023

This is based on rust-lang/rfcs#3407

r? @oli-obk
cc @scottmcm @phi-go @drmeepster

This is not complete, there are at least a couple of bugs (that I'm aware of, that is), however I'm starting to struggle because I've been looking at it too much, so a fresh look would be nice here.

I've tried to make commits as clean as I could, so hopefully rustfmt/clippy/libs/etc maintainers only need to look at a single commit that adds support there. (similarly all commits should pass tests at their point in time)

@rustbot rustbot added A-testsuite Area: The testsuite used to check the correctness of rustc S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 15, 2023
@rustbot
Copy link
Collaborator

rustbot commented Jun 15, 2023

This PR changes MIR

cc @oli-obk, @RalfJung, @JakobDegen, @davidtwco, @celinval, @vakaras

Some changes occurred to the CTFE / Miri engine

cc @rust-lang/miri

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

Some changes occurred in src/tools/rustfmt

cc @rust-lang/rustfmt

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

This PR changes Stable MIR

cc @oli-obk, @celinval

Some changes occurred in rustc_ty_utils::consts.rs

cc @BoxyUwU

Some changes occurred in compiler/rustc_codegen_gcc

cc @antoyo

@WaffleLapkin WaffleLapkin added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. F-explicit_tail_calls `#![feature(explicit_tail_calls)]` and removed A-testsuite Area: The testsuite used to check the correctness of rustc T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 15, 2023
@rust-log-analyzer

This comment has been minimized.

Comment on lines +109 to +111
// Erase regions since tail calls don't care about lifetimes
let callee_sig =
self.tcx.normalize_erasing_late_bound_regions(self.param_env, ty.fn_sig(self.tcx));
Copy link
Member Author

Choose a reason for hiding this comment

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

fn_sig here panics on the following snippet:

fn g() {
    become (&g)();
}

(or in general any snippet that involves &fn() and such)

Comment on lines +122 to +124
if caller_sig.output() != callee_sig.output() {
span_bug!(expr.span, "hir typeck should have checked the return type already");
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This panics w/ opaque types, for example:

fn f() -> impl Sized {
    become g();
}

fn g() {}

or

fn f() -> impl std::future::Future<Output = ()> {
    become g();
}

async fn g() {}

both of those should probably work

@rustbot rustbot added A-testsuite Area: The testsuite used to check the correctness of rustc T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) labels Jun 15, 2023
Copy link
Contributor

@JakobDegen JakobDegen left a comment

Choose a reason for hiding this comment

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

I sort of started to review some of this but this is proving pretty difficult. I think the PR is significantly too big. There is no one person with the expertise to review all of this. The history is clean right now, but that will not be true after this inevitably goes through half a dozen rounds of review with a handful of people.

I think we should be looking to split this up. Each individual PR should be small enough that it can be reviewed in its entirety by one person, in such a way that after that review is done we're confident that either the code is correct, or there are actual issues tracking critical bugs that may not be forgotten about.

Comment on lines +669 to +671
/// These are owned by the callee, which is free to modify them.
/// This allows the memory occupied by "by-value" arguments to be
/// reused across function calls without duplicating the contents.
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't make a whole lot of sense - all arguments are "by value" in Mir, operands are values after all (may edit this comment after I read the rest of this PR)

Copy link
Member Author

Choose a reason for hiding this comment

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

This comment is copied from TerminatorKind::Call::args

@@ -527,6 +527,10 @@ impl Direction for Forward {
}
}

TailCall { .. } => {
// FIXME(explicit_tail_calls): is there anything sensible to do here?
Copy link
Contributor

Choose a reason for hiding this comment

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

No; there are no successors

@@ -453,7 +453,7 @@ where
/// building up a `GenKillSet` and then throwing it away.
pub trait GenKill<T> {
/// Inserts `elem` into the state vector.
fn gen(&mut self, elem: T);
fn gen(&mut self, der: T);
Copy link
Contributor

Choose a reason for hiding this comment

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

Accidentally left in? This doesn't do anything but the old name did make more sense to me

Copy link
Member

Choose a reason for hiding this comment

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

(i think it's a joke about fn gen(der))

Copy link
Member Author

Choose a reason for hiding this comment

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

(yeah; I accidentally commited it 💀 )

@@ -1217,6 +1217,8 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
// These may unwind.
TerminatorKind::Drop { .. }
| TerminatorKind::Call { .. }
// FIXME(explicit_tail_calls): can tail calls unwind actually?..
| TerminatorKind::TailCall { .. }
Copy link
Contributor

Choose a reason for hiding this comment

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

Per the RFC, they aren't allowed in generators - I haven't yet checked if this PR implements this restriction (please unreachable! here)

@RalfJung
Copy link
Member

Agreed with @JakobDegen. Can this be split up into smaller PRs that only touch certain parts of the compiler -- the frontend, MIR building, codegen, interpreter, all separate? IIRC this is what we did with inline const.

There's no reason to have the Miri/interpreter or codegen implementation in the same PR that implements the checks enforcing the become restrictions, or the new dropck/borrowck interactions.

@WaffleLapkin
Copy link
Member Author

No problem, I'll split this up (should be easy, given that I can just cherry-pick necessary commits).

@WaffleLapkin
Copy link
Member Author

I've opened a tracking issue (#112788) alongside the first two implementation PRs (parsing and llvm ffi) (see the tasklist in the issue).

fmease added a commit to fmease/rust that referenced this pull request Dec 5, 2024
…ompiler-errors

implement checks for tail calls

Quoting the [RFC draft](https://github.com/phi-go/rfcs/blob/guaranteed-tco/text/0000-explicit-tail-calls.md):

> The argument to become is a function (or method) call, that exactly matches the function signature and calling convention of the callee. The intent is to ensure a matching ABI. Note that lifetimes may differ as long as they pass borrow checking, see [below](https://github.com/phi-go/rfcs/blob/guaranteed-tco/text/0000-explicit-tail-calls.md#return-type-coercion) for specifics on the return type.

> Tail calling closures and tail calling from closures is not allowed. This is due to the high implementation effort, see below, this restriction can be lifted by a future RFC.

> Invocations of operators were considered as valid targets but were rejected on grounds of being too error-prone. In any case, these can still be called as methods.

> Tail calling [variadic functions](https://doc.rust-lang.org/beta/unstable-book/language-features/c-variadic.html) and tail calling from variadic functions is not allowed. As support for variadic function is stabilized on a per target level, support for tail-calls regarding variadic functions would need to follow a similar approach. To avoid this complexity and to minimize implementation effort for backends, this interaction is currently not allowed but support can be added with a future RFC.

-----

The checks are implemented as a query, similarly to `check_unsafety`.

The code is cherry-picked straight out of rust-lang#112657 which was written more than a year ago, so I expect we might need to change some things ^^"
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request Dec 5, 2024
…ompiler-errors

implement checks for tail calls

Quoting the [RFC draft](https://github.com/phi-go/rfcs/blob/guaranteed-tco/text/0000-explicit-tail-calls.md):

> The argument to become is a function (or method) call, that exactly matches the function signature and calling convention of the callee. The intent is to ensure a matching ABI. Note that lifetimes may differ as long as they pass borrow checking, see [below](https://github.com/phi-go/rfcs/blob/guaranteed-tco/text/0000-explicit-tail-calls.md#return-type-coercion) for specifics on the return type.

> Tail calling closures and tail calling from closures is not allowed. This is due to the high implementation effort, see below, this restriction can be lifted by a future RFC.

> Invocations of operators were considered as valid targets but were rejected on grounds of being too error-prone. In any case, these can still be called as methods.

> Tail calling [variadic functions](https://doc.rust-lang.org/beta/unstable-book/language-features/c-variadic.html) and tail calling from variadic functions is not allowed. As support for variadic function is stabilized on a per target level, support for tail-calls regarding variadic functions would need to follow a similar approach. To avoid this complexity and to minimize implementation effort for backends, this interaction is currently not allowed but support can be added with a future RFC.

-----

The checks are implemented as a query, similarly to `check_unsafety`.

The code is cherry-picked straight out of rust-lang#112657 which was written more than a year ago, so I expect we might need to change some things ^^"
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Dec 6, 2024
Rollup merge of rust-lang#133607 - WaffleLapkin:tail-call-checks, r=compiler-errors

implement checks for tail calls

Quoting the [RFC draft](https://github.com/phi-go/rfcs/blob/guaranteed-tco/text/0000-explicit-tail-calls.md):

> The argument to become is a function (or method) call, that exactly matches the function signature and calling convention of the callee. The intent is to ensure a matching ABI. Note that lifetimes may differ as long as they pass borrow checking, see [below](https://github.com/phi-go/rfcs/blob/guaranteed-tco/text/0000-explicit-tail-calls.md#return-type-coercion) for specifics on the return type.

> Tail calling closures and tail calling from closures is not allowed. This is due to the high implementation effort, see below, this restriction can be lifted by a future RFC.

> Invocations of operators were considered as valid targets but were rejected on grounds of being too error-prone. In any case, these can still be called as methods.

> Tail calling [variadic functions](https://doc.rust-lang.org/beta/unstable-book/language-features/c-variadic.html) and tail calling from variadic functions is not allowed. As support for variadic function is stabilized on a per target level, support for tail-calls regarding variadic functions would need to follow a similar approach. To avoid this complexity and to minimize implementation effort for backends, this interaction is currently not allowed but support can be added with a future RFC.

-----

The checks are implemented as a query, similarly to `check_unsafety`.

The code is cherry-picked straight out of rust-lang#112657 which was written more than a year ago, so I expect we might need to change some things ^^"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-testsuite Area: The testsuite used to check the correctness of rustc F-explicit_tail_calls `#![feature(explicit_tail_calls)]` S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) 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.

8 participants