Closed
Description
Consider the following code:
fn d<T: Sized>(t: T) -> impl std::future::Future<Output = T> + Send {
async { t }
}
This produces the following error message in cargo build
:
error[E0277]: `T` cannot be sent between threads safely
--> src/main.rs:27:25
|
27 | fn d<T: Sized>(t: T) -> impl std::future::Future<Output = T> + Send {
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `T` cannot be sent between threads safely
| |
| help: consider further restricting this bound: `T: std::marker::Send +`
28 | async { t }
| ----------- this returned value is of type `impl std::future::Future`
|
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `T`
= note: required because it appears within the type `[static generator@src/main.rs:28:11: 28:16 t:T _]`
= note: required because it appears within the type `std::future::GenFuture<[static generator@src/main.rs:28:11: 28:16 t:T _]>`
= note: required because it appears within the type `impl std::future::Future`
= note: the return type of a function must have a statically known size
and the following tooltip in VS Code:
`T` cannot be sent between threads safely
`T` cannot be sent between threads safely
help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `T`
note: required because it appears within the type `[static generator@src/main.rs:28:11: 28:16 t:T _]`
note: required because it appears within the type `std::future::GenFuture<[static generator@src/main.rs:28:11: 28:16 t:T _]>`
note: required because it appears within the type `impl std::future::Future`
note: the return type of a function must have a statically known sizerustc(E0277)
future.rs(21, 58): within this `impl std::future::Future`
main.rs(27, 18): `T` cannot be sent between threads safely
main.rs(28, 5): this returned value is of type `impl std::future::Future`
@eddyb asked me to open this issue because the notes are mostly confusing (at least they won’t help mere mortals) and said that the message should not mention GenFuture nor [static generator ...]
. He used the following code for a larger error message (playground):
async fn foo<T>(x: T) -> T {
x
}
fn assert_send(_: impl Send) {}
fn test<T>(x: T) {
assert_send(foo(x));
}
Metadata
Metadata
Assignees
Labels
Area: Async & AwaitArea: Messages for errors, warnings, and lintsAsync-await issues that have been triaged during a working group meeting.Category: An issue proposing an enhancement or a PR with one.Call for participation: Medium difficulty. Experience needed to fix: Intermediate.Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.Medium priorityRelevant to the compiler team, which will review and decide on the PR/issue.
Type
Projects
Status
Done
Activity
nikomatsakis commentedon Apr 6, 2020
Hmm, so the error message for the second example is the following:
I think it would be helpful to try and enumerate what the "ideal" error in these two cases would look like. I see a few problems:
GenFuture
and static generators) are really leaking implementation details of rustc. Maybe we should just suppress those particular types.x
in both cases, we should probably underline that?async fn
, we talk about animpl Future
that the user didn't even write, which is not particularly great.eddyb commentedon Apr 6, 2020
@nikomatsakis Also, I think what happened in the past is that #64895 + #65345 improved the errors when a value is kept across an
.await
but not when something is just captured... maybe the suboptimal output is only shown if there is no.await
at all in the body of theasync fn
?So this might be a strawman example we've stumbled over?
tmandry commentedon Apr 8, 2020
We could fix this by outputting the nicer error messages, which does seem to happen anytime there exists an
await
in the async fn.The reason we don't output the nice error messages is that this code doesn't create a
GeneratorInteriorTypeCause
for types which are captured from the generator's environment (as in the case of async fn args). As soon as we have an await anywhere in the function, a cause is added forT
and we get the nice error message (from this code):However, this logic can still lead to bad diagnostics for captured vars. For example, when we drop
x
before awaiting, the diagnostics claimx
may still be used after the await:So what we need is a way to express a cause for captured vars which doesn't include an await/yield point, and for the diagnostics code to handle these causes.
5 remaining items