-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
Holding a non-Send Copy type across a yield should be allowed #99104
Comments
cc @tmandry, not sure if people have explicitly thought about this or it just happens to be forbidden by the current implementation. |
We talked about this issue in triage last time and this time (because I forgot to mark it as triaged the first time). Thanks for the PR! I'll take a closer look at it soon. It looks simpler than I was expecting, so that's a pleasant surprise. I was afraid fixing this issue would need a liveness pass, but maybe we already have the information we need? The first time we talked about this there was some question about whether we want this behavior. They main concerns were around backwards compatibility and how complex we want the algorithm here to be. Although if it turns out we already have the analysis we need, I think that makes the case stronger for accepting this. At any rate, it's probably good to loop in T-Lang. @rustbot label +AsyncAwait-Triaged |
Seems fine to me from a lang perspective. I do think this needs a lang FCP for the behavior change, though. |
@rfcbot merge |
Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members: Concerns:
Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
What's the definition of "holding" here? Since there's no use after the EDIT: Oh, I guess this is |
Confirming something: this is using (If we were copying the value, we should have the lint about large copies. But if we're not copying the value, that's not an issue.) |
@jyn514 Could you retitle this issue and update the summary to make it clearer that this is only for not actually holding the type, and doesn't work if you actually use it after the await? |
Yes, exactly. Theoretically we could do this for types like enum TrivialDrop {
A, B, C
}
impl !Sync for TrivialDrop {} too, but haven't thought that far ahead. |
I think the explicit promise is important for semver. Especially if you have a type with private fields, |
@rfcbot concern needs-exact-proposed-rule I think this needs a precise statement of when something is "held" across things, how that's detected, why it'll be reliably consistent across changes in rustc, etc. |
I agree, but will also note that we already do this with auto traits. Side note: In the future it would be good to have a mechanism for promising to uphold auto traits, so semver breakage is more easily caught. We could then add a lint for public types that don't explicitly say whether they implement |
That's true, but also a very special exception. |
+1 to the previous comments-- I think the rule here winds up resembling NLL in that it's important whether or not the value is actually used after the yield. |
@rfcbot concern early-storage-dead-of-stack-value After some further thought, I think that allowing this is not any easier than solving the more general problem of early stack re-use / StorageDead marking. In fact, it is somewhat more challenging in that the resulting rule would be directly observable from safe Rust code. Consider this example: async fn foo() {
let x = CreateMyNonSendCopyType();
let y = &x as *const _;
leak_ptr_to_somewhere(y);
some_future.await();
... // some other code that doesn't reference `x` or `y`
} In this case, I believe we have to continue storing |
This builds on the generator drop tracking work, which makes it so you don't have to declare bindings in a nested scope anymore to satisfy the type checker. It does this by approximating some MIR analysis in HIR, but doesn't work at all for For generator layouts we check if a reference to a local has been taken before an await point. If it was we never reuse its storage. One approach is to recreate that (pretty simplistic) check in HIR. In addition to affecting the size of a generator struct, it would now be observable in the trait system. The main downside I see is that the "was ever borrowed" check is conservative in a way that isn't intuitive – see #62321 for an example. Creating a temporary borrow and then dropping it, or to passing it to a function that doesn't capture the address, means that your local is now captured by your generator and impacts its auto traits. We could perhaps fix the first case easily, the second one not so much.1 async fn foo(x: *const usize) {
let _ = self.hash_map.get(&x); // Won't work
bar().await;
} Your only workaround will be to copy the value to a helper function and do the borrow there: async fn foo(x: *const usize) {
let _ = self.get_from_hash_map(x);
bar().await;
} We're going to need compiler diagnostics to help you out in any case. The question, for me, is whether making it less likely that you hit these issues (without eliminating them entirely) is worth the complexity of extra rules like this. Footnotes
|
@rfcbot fcp cancel Given that this RFC has been pending since August and there is still a need for more details, I'm going to cancel it for now, we can re-start when there's a concrete proposal we want to move forward. |
@nikomatsakis proposal cancelled. |
For non-Send types with trivial drop, we still consider them held across the generator, even with
-Zdrop-tracking
:There's no reason to do this;
*const T
has no drop impl and it's not possible to read or write it if it's not explicitly used after the yield.Originally posted by @jyn514 in #98754 (comment)
The text was updated successfully, but these errors were encountered: