-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Implement explicit tail calls #112657
Conversation
Note that this doesn't implement `become` correctly, there is still no early drop or tail calls involved. Moreother, it seems like MIR simply can not represent tail calls in its current form so I'll probably need to add new statement kinds or smh. to it in order to represent them...
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 Some changes occurred in cc @BoxyUwU Some changes occurred in compiler/rustc_codegen_gcc cc @antoyo |
This comment has been minimized.
This comment has been minimized.
// 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)); |
There was a problem hiding this comment.
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)
if caller_sig.output() != callee_sig.output() { | ||
span_bug!(expr.span, "hir typeck should have checked the return type already"); | ||
} |
There was a problem hiding this comment.
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
3bdef3f
to
965c7b6
Compare
There was a problem hiding this 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.
/// 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. |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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? |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
)
There was a problem hiding this comment.
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 { .. } |
There was a problem hiding this comment.
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)
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 |
No problem, I'll split this up (should be easy, given that I can just cherry-pick necessary commits). |
I've opened a tracking issue (#112788) alongside the first two implementation PRs (parsing and llvm ffi) (see the tasklist in the issue). |
…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 ^^"
…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 ^^"
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 ^^"
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)