-
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
Spurious new Send requirement in async block #64477
Comments
Tests are broken on newer nightlies by rust-lang/rust#64477.
There's a similar issue hitting any usage of async fn foo(_: String) {
}
fn bar() -> impl Send {
async move {
foo(format!("{}:{}", 1, 2)).await;
}
}
|
Yeah, this is the same issue. I've written up a long report on the change that caused this and its motivations. The report also discussed whether we should consider revering that change, and what that would mean. |
Another example of this (if we want more test-cases) is: use std::cell::RefCell;
fn foo() -> impl Send {
async {
let x = RefCell::new(String::new());
match x {
ref z => {
drop(z);
async move {
x
}.await
}
}
}
} |
@nikomatsakis -- your report has this under "Longer lifetime for temporaries results in things \"living\" across await that did not used to". In my code at the top, I can't figure out what temporary might be at play. It seems like the only types that exist in the async block before the await are The required-because chain mentions this type: for<'r, 's, 't0, 't1> {for<'t2> fn(&'t2 T) -> std::pin::Pin<std::boxed::Box<(
dyn std::future::Future<Output = ()> + std::marker::Send + 'static)>> {f::<T>
}, &'r T, T, &'s T, std::pin::Pin<std::boxed::Box<(dyn std::future::Future<Ou
tput = ()> + std::marker::Send + 't0)>>, std::pin::Pin<std::boxed::Box<(dyn s
td::future::Future<Output = ()> + std::marker::Send + 't1)>>, ()} What's the |
(Marking as blocking as we must reach a decision one way or the other.) |
It could be the temporary from the reborrowing of EDIT: This playground shows that it very likely is (since |
Let me dig a bit more deeply. |
OK, I think @Nemo157 was correct -- the code which computes which types may be live across yields is over-approximating the result here to include a number of intermediaries, far more than is needed. This includes the value of type |
Specifically, this clause seems to be the problematic one: rust/src/librustc_typeck/check/generator_interior.rs Lines 188 to 191 in 64c0969
|
…es, r=eddyb record fewer adjustment types in generator witnesses, avoid spurious drops in MIR construction Don't record all intermediate adjustment types -- That's way more than is needed, and winds up recording types that will never appear in MIR. Note: I'm like 90% sure that this logic is correct, but this stuff is subtle and can be hard to keep straight. However, the risk of this PR is fairly low -- if we miss types here, I believe the most common outcome is an ICE. This fixes the original issue cited by #64477, but I'm leaving the issue open for now since there may be other cases we can detect and improve in a targeted way. r? @Zoxc
It looks like @Nemo157's case in #64477 (comment) (with |
The fn bar() -> impl Send {
async move
{
foo(
::alloc::fmt::format(::core::fmt::Arguments::new_v1(&["",
":"],
&match (&1,
&2)
{
(arg0,
arg1)
=>
[::core::fmt::ArgumentV1::new(arg0,
::core::fmt::Display::fmt),
::core::fmt::ArgumentV1::new(arg1,
::core::fmt::Display::fmt)],
}))).await;
}
} To simplify: foo(Arguments::new(&[...], &[...])).await; we are making temporaries to store those |
I'm going to close this issue for now, as a result. |
A bit sad, but understandable. And a glance through #45198 suggests that fixing the |
A simpler change might be to introduce an extra block inside the macro_rules! format {
($($arg:tt)*) => ({
let res = $crate::fmt::format($crate::__export::format_args!($($arg)*));
res
})
} |
@Nemo157 did you ever end up filing a PR for that change? This keeps biting me, and fixing it at the root would save a bunch of work. Do you see any drawback with making your proposed change? |
Currently semi-blocked on rust-lang/rust#64477. Or rather, it would take a bunch of work to fix the last error in our code. Instead, there's a small change to std that would also fix it, so waiting on that: rust-lang/rust#64477 (comment)
This places the temporaries that `format!` generates to refer to its arguments (through `&dyn Trait`) in a short-lived scope surrounding just the invocation of `format!`. This enables `format!` to be used in generators without the temporaries preventing the generator from being `Send` (due to `dyn Trait` not being `Sync`). See rust-lang#64477 for details.
`foo(format!(...)).await` no longer compiles. There's a fix in rust-lang/rust#64856, but this works around the problem.
`foo(format!(...)).await` no longer compiles. There's a fix in rust-lang/rust#64856, but this works around the problem.
Scope format! temporaries This places the temporaries that `format!` generates to refer to its arguments (through `&dyn Trait`) in a short-lived scope surrounding just the invocation of `format!`. This enables `format!` to be used in generators without the temporaries preventing the generator from being `Send` (due to `dyn Trait` not being `Sync`). See rust-lang#64477 for details.
This is minimized from code that was working with nightly-2019-09-10 but broken on nightly-2019-09-11.
0b36e9d...34e82a7
I think the old behavior was correct. No
T
-by-value exists in this code soT: Send
shouldn't come up as a requirement.Mentioning @davidtwco because #64292 looks relevant in the right commit range.
I am filing this separately from #64391 because this manifests as a type error and not a borrow checker error, though maybe they are the same thing.
The text was updated successfully, but these errors were encountered: