Note: This is not a user question, I'm trying to debug a potential compiler bug (Indexing via `index` method and `[idx]` sugar works differently in `async` blocks/functions · Issue #72956 · rust-lang/rust · GitHub) and understand borrow checking and async/await lowering better.
I'm trying to understand why this program compiles:
use std::ops::Index;
pub struct A(*mut ());
unsafe impl Send for A {}
impl A {
pub fn new() -> &'static mut A {
static mut ASD: A = A(std::ptr::null_mut());
unsafe { &mut ASD }
}
}
pub struct B<'a>(pub &'a mut A);
impl<'a> std::ops::Index<usize> for B<'a> {
type Output = ();
fn index(&self, _idx: usize) -> &() {
&()
}
}
pub async fn baz(_v: &()) {}
pub fn assert_send<T: Send>(_: T) {}
pub async fn bar() -> () {
let a = B(A::new());
let r = a.index(0);
baz(r).await;
}
pub fn test() {
assert_send(bar())
}
but if I change a.index(0)
to &a[0]
I get this error:
error: future cannot be sent between threads safely
--> test_fails.rs:33:5
|
24 | pub fn assert_send<T: Send>(_: T) {}
| ---- required by this bound in `assert_send`
...
33 | assert_send(bar())
| ^^^^^^^^^^^ future returned by `bar` is not `Send`
|
= help: within `B<'_>`, the trait `Sync` is not implemented for `*mut ()`
note: future is not `Send` as this value is used across an await
--> test_fails.rs:29:5
|
28 | let r = &a[0];
| - has type `&B<'_>` which is not `Send`
29 | baz(r).await;
| ^^^^^^^^^^^^ await occurs here, with `a` maybe used later
30 | }
| - `a` is later dropped here
help: consider moving this into a `let` binding to create a shorter lived borrow
--> test_fails.rs:28:14
|
28 | let r = &a[0];
| ^^^^
error: aborting due to previous error
I've been trying to debug this on and off for a few months now. At first I thought this is a borrow checking issue, caused by the extra intermediate value in the version with index expression (generated by lowering of index expressions to MIR). However I asked this on Zulip and someone suggested that this could be an issue with capturing locals in async structs, which I think makes sense.
Now I'm wondering how to further debug this. I asked on Zulip about seeing structs, or captures/upvars, used at the yield points, but I currently don't have an answer.
Is there a way to see struct definitions for async functions at yield points? For example, in the repro above, I'd like to see the struct used (with values for the fields) returned in line baz(r).await
. Is this possible?
If not, does anyone have any suggestions on how to debug this further?
Thanks.