-
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 functions fail to move values into generated futures in the latest nightly #64391
Comments
This can be hacked around via an diff --git a/tokio-postgres/src/lib.rs b/tokio-postgres/src/lib.rs
index 98c245f..eaf43f1 100644
--- a/tokio-postgres/src/lib.rs
+++ b/tokio-postgres/src/lib.rs
@@ -168,7 +168,9 @@ where
T: MakeTlsConnect<Socket>,
{
let config = config.parse::<Config>()?;
- config.connect(tls).await
+ async move {
+ config.connect(tls).await
+ }.await
} |
I think the root cause of this is #46413 -- feels like we ought to have more test cases covering these patterns, but oh well. I haven't tried to look closely enough to decide what possible solution is. |
I can confirm that this is actually a duplicate of #64512, so this is a genuine bug. An alternative workaround is |
Minimized test case is just this: async fn add(x: u32, y: u32) -> u32 {
async { x + y }.await
}
fn main() { } |
#64525 doesn't seem to have fixed this for my specific case:
|
Reduced: async fn connect() {
let config = 666;
connect2(&config, "".to_string()).await
}
async fn connect2(_config: &u32, _tls: String) {
unimplemented!()
} |
I recently tried to compile my codebase on nightly-2019-09-13 and I seem to have similar issues. I have hundreds of errors like @sfackler mentioned. Just to be sure I'm having the same issue, here is an example: let fut_listener = async move {
let mut access_control = AccessControlPk::new();
inner_client_listener(
connector,
&mut access_control,
&mut incoming_access_control,
connections_sender,
keepalive_transform,
conn_timeout_ticks,
timer_client,
c_spawner,
Some(event_sender)
).await
} This is the compilation error I get: $ cargo test -p offst-relay
Compiling offst-relay v0.1.0 (/home/real/projects/d/offst/components/relay)
error[E0597]: `access_control` does not live long enough
--> components/relay/src/client/client_listener.rs:512:17
|
510 | / inner_client_listener(
511 | | connector,
512 | | &mut access_control,
| | ^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
513 | | &mut incoming_access_control,
... |
519 | | Some(event_sender)
520 | | ).await
| |_____________- a temporary with access to the borrow is created here ...
521 | }
| -
| |
| `access_control` dropped here while still borrowed
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `impl core::future::future::Future`
|
= note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block.
error: aborting due to previous error
For more information about this error, try `rustc --explain E0597`.
error: Could not compile `offst-relay`. Indeed saving the result into a temporary variable |
OK, so I dug into this a bit. The errors are... correct-ish, but horrific. They are an outcome of the current temporary-lifetime rules, and this is in that sense a dup of #46413. That said, there is some imprecision here, and it may be possible to work-around the problem by increasing the precision of our analysis without actually changing the dynamic semantics of any existing code. Let me explain what's going on to start. The desugaring converts So, in the case of our example, we have async fn connect() {
let config = 666;
connect2(&config, "".to_string()).await
} which is effectively desugared into something like let config = 666;
match connect2(&config, "".to_string()) {
the_future => $await(the_future ),
} this then gets further desugared, as part of MIR lowering, into this:
Now, here's the interesting thing: the borrow checker is smart enough to see that the The answer requires a bit more digging. It turns out that when we desugar the let _temp;
let config = 666;
let _4 = &config;
let _6 = "".to_string();
_3 = connect2(move _4, move _6);
drop(_6); // <-- interesting!
let the_future = _3;
$await(the_future);
...
drop(_3); Here we have a Now this unwind path is problematic -- along that path we wind up doing the following:
Along this path, indeed, we are dropping So there you have it:
I haven't 100% verified this is where the error arises, but I'm 99.9% sure. So it seems like we could fix this if we avoided inserting the |
Looked into this a bit. Here are a few tips of what I've found so far: We have a notion of a "local operand". This refers to an operand that is going to get freed when the expression is done, and it includes call arguments: rust/src/librustc_mir/build/expr/as_operand.rs Lines 10 to 22 in 9b9d2af
We create call arguments by invoking that method: rust/src/librustc_mir/build/expr/into.rs Lines 240 to 243 in 9b9d2af
If you trace that method out, it ultimately bottoms out in the rust/src/librustc_mir/build/expr/as_temp.rs Lines 115 to 123 in 9b9d2af
This is all correct so far and should not change. After all, we may need to execute a drop if (e.g.) one of the arguments being evaluated should unwind. We just don't need them once all the call arguments succeed. What we can do however is -- once the call instruction has been emitted -- we could flag those operands as having been consumed (actually, we could probably do it right before emitting the call terminator, since it will move them before it could unwind). This would effectively cancel the calls to drop. I am imagining a call to a method |
I'm experimenting in a branch here. I actually took a slightly different tack than what I described above -- I'm not "removing" the drop but just recording that the operand was moved, and taking advantage of that info on the non-unwind path. We'll see how it goes, but it seems simpler. |
OK, my patch works, though I'm not entirely sure how happy I am with it. =) |
Checking rust-postgres with #64584. I have checked out the commit b7fe6bece5dd11ffc04d8178d5105f9e1a354ebd with the following diff applied: diff --git a/tokio-postgres/src/lib.rs b/tokio-postgres/src/lib.rs
index 29f378a..98c245f 100644
--- a/tokio-postgres/src/lib.rs
+++ b/tokio-postgres/src/lib.rs
@@ -168,10 +168,7 @@ where
T: MakeTlsConnect<Socket>,
{
let config = config.parse::<Config>()?;
- // FIXME https://github.com/rust-lang/rust/issues/64391
- async move {
- config.connect(tls).await
- }.await
+ config.connect(tls).await
}
/// An asynchronous notification. I can confirm it does not build with nightly, but it does with #64584. |
Now that #64584 has landed, I'm going to close this again, since afaik we've fixed all the known bugs here. |
This simple async function fails to compile on the newest nightly: https://github.com/sfackler/rust-postgres/blob/2cc5bbf21b654c26bfd8b1e80b1e5c7cbd81235f/tokio-postgres/src/lib.rs#L163-L172
I'm guessing this was caused by #64292 (cc @davidtwco)
The text was updated successfully, but these errors were encountered: