Overly conservative async capture analysis when values are borrowed
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
There's probably a bug filed already but this code does not compile, though it really ought to:
use std::sync::Mutex;
async fn foo(m: &Mutex<u32>) {
let lock = m.lock().unwrap();
if condition(&lock) {
drop(lock);
return bar().await;
}
drop(lock);
return bar().await;
}
async fn bar() { }
fn is_send<T: Send>(t: T) { }
fn condition(x: &u32) -> bool {
false
}
fn main() {
let m = Mutex::new(22);
is_send(foo(&m));
}
I believe the problem is specific to the &lock, which causes the capture analysis to get nervous -- even though lock is dropped. This is distilled from real-world code within Amazon.
Error you get today:
error: future cannot be sent between threads safely
--> src/main.rs:26:13
|
26 | is_send(foo(&m));
| ^^^^^^^ future returned by `foo` is not `Send`
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `MutexGuard<'_, u32>`, which is required by `impl Future<Output = ()>: Send`
note: future is not `Send` as this value is used across an await
--> src/main.rs:9:22
|
6 | let lock = m.lock().unwrap();
| ---- has type `MutexGuard<'_, u32>` which is not `Send`
...
9 | return bar().await;
| ^^^^^ await occurs here, with `lock` maybe used later
note: required by a bound in `is_send`
--> src/main.rs:18:15
|
18 | fn is_send<T: Send>(t: T) { }
| ^^^^ required by this bound in `is_send`
I'm nominated for async just to get some eyes on this. I'd be interested to discuss fixes, I have a few thoughts, though I'd have to look at the code too.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the example from the linked Rust Playground and inspect the compiler's async capture analysis, especially the borrowed &lock case. Determine why the dropped MutexGuard is still considered live across bar().await; done means the example compiles with the returned future satisfying Send, with a regression test covering this behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100