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 checks for tail calls #133607

Merged
merged 6 commits into from
Dec 6, 2024
Merged

Conversation

WaffleLapkin
Copy link
Member

@WaffleLapkin WaffleLapkin commented Nov 29, 2024

Quoting the RFC draft:

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 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 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 #112657 which was written more than a year ago, so I expect we might need to change some things ^^"

this implements checks necessary to guarantee that we can actually
perform a tail call. while extremely restrictive, this is what is
documented in the RFC, and all these checks are needed for one reason or
another.
@WaffleLapkin WaffleLapkin added F-explicit_tail_calls `#![feature(explicit_tail_calls)]` A-THIR Area: Typed HIR labels Nov 29, 2024
@rustbot
Copy link
Collaborator

rustbot commented Nov 29, 2024

r? @lcnr

rustbot has assigned @lcnr.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@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 29, 2024
compiler/rustc_mir_build/src/check_tail_calls.rs Outdated Show resolved Hide resolved
tcx,
thir,
found_errors: Ok(()),
// FIXME(#132279): we're clearly in a body here.
Copy link
Member

Choose a reason for hiding this comment

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

yes but it would define opaque types :3

but maybe we want to use @lcnr's post borrowck analysis

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure what options get us there, but I think this code should be accepted:

fn a() -> impl Sized {
    become b()
}

fn b() -> u8 { 0 }

which means that we should reveal opaques before comparing things 🤔

Copy link
Member

Choose a reason for hiding this comment

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

well, so we probably want to reveal only the opaques local to the body

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 this is good for now, and having a fixme maybe for deciding on behavior is good enough!

compiler/rustc_mir_build/src/check_tail_calls.rs Outdated Show resolved Hide resolved
@WaffleLapkin WaffleLapkin marked this pull request as ready for review November 29, 2024 19:30
@WaffleLapkin
Copy link
Member Author

@compiler-errors thank you for your suggestions, they made things quite a bit better :3

@lcnr
Copy link
Contributor

lcnr commented Dec 2, 2024

r? @compiler-errors as you've already partially reviewed this

@rustbot rustbot assigned compiler-errors and unassigned lcnr Dec 2, 2024
@compiler-errors
Copy link
Member

@bors r+

@bors
Copy link
Contributor

bors commented Dec 5, 2024

📌 Commit 144d6cc has been approved by compiler-errors

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-review Status: Awaiting review from the assignee but also interested parties. labels Dec 5, 2024
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 ^^"
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 5, 2024
…llaumeGomez

Rollup of 8 pull requests

Successful merges:

 - rust-lang#132155 (Always display first line of impl blocks even when collapsed)
 - rust-lang#133256 (CI: use free runners for i686-gnu jobs)
 - rust-lang#133607 (implement checks for tail calls)
 - rust-lang#133821 (Replace black with ruff in `tidy`)
 - rust-lang#133827 (CI: rfl: move job forward to Linux v6.13-rc1)
 - rust-lang#133910 (Normalize target-cpus.rs stdout test for LLVM changes)
 - rust-lang#133921 (Adapt codegen tests for NUW inference)
 - rust-lang#133936 (Avoid fetching the anon const hir node that is already available)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit e941e73 into rust-lang:master Dec 6, 2024
6 checks passed
@rustbot rustbot added this to the 1.85.0 milestone Dec 6, 2024
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 ^^"
@nnethercote
Copy link
Contributor

This introduced lots of perf regressions, as reported in #133940. @WaffleLapkin: Is that expected? Can anything be done to mitigate them?

@nnethercote nnethercote added the perf-regression Performance regression. label Dec 6, 2024
@WaffleLapkin
Copy link
Member Author

WaffleLapkin commented Dec 7, 2024

@nnethercote this is not entirely expected, but I potentially see the problem. the added check loops over all THIR nodes, so even if the check doesn't do anything, it may add up into something measurable?

One solution to this I could see is to merge check_unsafety and this, so that we only need to loop once. Unsure if this will actually help though. Ig the underlying problem/question is "do we have a good way of finding all nodes of a certain kind (in this case, Become)"

Upd: actually, one other idea is that the issue is query rehashing the input/storing output to disk. This can easily be fixed by merging queries between check_unsafety and this, and that would not make the implementation readability worse (as those, I assume, can still have their own visitors, we only need to merge cache behavior)

@WaffleLapkin WaffleLapkin deleted the tail-call-checks branch December 7, 2024 10:16
@nnethercote
Copy link
Contributor

@WaffleLapkin: a couple of good sounding ideas there, are you happy to try them out?

/// Checks well-formedness of tail calls (`become f()`).
query check_tail_calls(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
desc { |tcx| "tail-call-checking `{}`", tcx.def_path_str(key) }
cache_on_disk_if { true }
Copy link
Member

Choose a reason for hiding this comment

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

probably dont need this caching

Copy link
Member

Choose a reason for hiding this comment

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

or at least possibly seeing if this is perf sensitive

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 was copied from check_unsafety, we might want to change it too 🤔

@WaffleLapkin
Copy link
Member Author

@nnethercote yes, I'll try them out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-THIR Area: Typed HIR F-explicit_tail_calls `#![feature(explicit_tail_calls)]` perf-regression Performance regression. 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.

6 participants