-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Async closures are not allowed to reference all captured lifetimes if one of them is invariant #123241
Comments
@tmandry: Several things here: Firstly, regarding this message:
It's just that the borrowck diagnostics code isn't yet modified to say Secondly, I think this all might just be a red herring with the lifetime invariance. The "which makes the generic argument So the real problem here is that the future that you pass into This can be fixed by passing an The fact that Rust chooses to borrow rather than copy the captured My vibe is that the preference to borrow rather than copy/move args can allow more code to compile in some cases, but obviously this may cause other problems (e.g. this issue). This also probably has something to do with how the first example, |
Thanks for your support @compiler-errors! Five stars would minimize again. I'm also surprised by the difference from That said, in this particular API I think you always want |
@tmandry: I think my next step will be to understand the box case. I'm not certain if it's worth thinking about what to change until we can completely understand what's going on here. I do agree that the code going from fail->pass with the addition of a Regarding diagnostics, I don't know if there's a way to get borrowck to understand how to suggest this change, though, given that it's pretty far along past closure capture analysis. And the same question for making closure capture analysis more sophisticated too. Again--will probably need to understand exactly what is happening better. |
I think I found an important difference: In the box case the future is bound to outlive Adding such a bound (see line 31) fails compilation with a couple of |
Well partly this is because we don't need to prove that outlives bound for all The type erasure of |
@rustbot labels +WG-async +AsyncAwait-Triaged We discussed this in the async triage call and, based on the discussion here in this thread, this is definitely triaged, and it seems that more investigation is needed. |
Investigated this. The minimal version of this is: #![feature(async_closure)]
fn outlives<'a, T: 'a>(_: T) {}
fn hello<'a>(x: &'a i32) {
let c = async || {
outlives::<'a>(async {
let y = *x;
});
};
} And it should be fixed by #123660. |
Rollup merge of rust-lang#123660 - compiler-errors:coroutine-closure-env, r=oli-obk Make the computation of `coroutine_captures_by_ref_ty` more sophisticated Currently, we treat all the by-(mut/)ref borrows of a coroutine-closure as having a "closure env" borrowed lifetime. When we have the given code: ```rust let x: &'a i32 = ...; let c = async || { let _x = *x; }; ``` Then when we call: ```rust c() // which, because `AsyncFn` takes a `&self`, we insert an autoref: (&c /* &'env {coroutine-closure} */)() ``` We will return a future whose captures contain `&'env i32` instead of `&'a i32`, which is way more restrictive than necessary. We should be able to drop `c` while the future is alive since it's not actually borrowing any data *originating from within* the closure's captures, but since the capture has that `'env` lifetime, this is not possible. This wouldn't be true, for example, if the closure captured `i32` instead of `&'a i32`, because the `'env` lifetime is actually *necessary* since the data (`i32`) is owned by the closure. This PR identifies two criteria where we *need* to take the borrow with the closure env lifetime: 1. If the closure borrows data from inside the closure's captures. This is not true if the parent capture is by-ref, OR if the parent capture is by-move and the child capture begins with a deref projection. This is the example described above. 2. If we're dealing with mutable references, since we cannot reborrow `&'env mut &'a mut i32` into `&'a mut i32`, *only* `&'env mut i32`. See the documentation on `should_reborrow_from_env_of_parent_coroutine_closure` for more info. **important:** As disclaimer states on that function, luckily, if this heuristic is not correct, then the program is not unsound, since we still borrowck and validate the choices made from this function -- the only side-effect is that the user may receive unnecessary borrowck errors. Fixes rust-lang#123241
Full example
yields
It's reasonable that we would create a lifetime representing the closure body, but naturally, the future should be allowed to reference it. The invariant lifetime must be mucking things up somehow.
(
spawn
has no lifetime params so I would not expect there to be a new lifetime created when it is called.)The closure definitely shouldn't try to implement
Fn
in this case, I don't know why it does.It's correct that this lifetime is invariant. We must write to a vec of futures that outlive
'scope
, so any variance would be unsound.cc @compiler-errors
The text was updated successfully, but these errors were encountered: