-
Notifications
You must be signed in to change notification settings - Fork 352
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
Test whether self-referential futures are compatible with stacked borrows #532
Comments
I'm actually working on trying to create a step-by-step example of how an
There are only two different kinds that I know of, and fundamentally they should be the same. You can have references to upvars that have been moved into the generator and references to locals created while the generator is running. Once the generator transform has run these both just become references into the generator state variable, and I don't think they're distinguishable in the MIR. |
Actually I might as well throw something together now, here should be a small example showing all kinds of references in an There's also the minimal framework for running an |
@RalfJung might it be easier to focus on generators before async fn, since they have direct access to |
Here's a self-referential generator example: #![feature(generators, generator_trait)]
use std::ops::Generator;
fn main() {
let mut generator = || {
let mut x = Box::new(5);
let y = &mut *x;
*y = 5;
yield ();
*y = 10;
*x
};
unsafe { generator.resume() };
} |
@cramertj Thanks! I thought there had to be |
To my big surprise, this actually passes in miri :D EDIT: Even more interestingly though, it no longer passes locally... |
Oh. The generator MIR transform happens after we added the retag statements, leading to... strange results. However, the generator MIR stuff happens extremely late -- it happens after inlining! That seems really strange to me, shouldn't we first get the MIR "complete" before doing interesting transformations? |
@withoutboats That makes sense, I wasn't aware of the layering here. This seems covered by #536 though, and it's actually working (with a small rustc fix), which is great! However, something like @Nemo157's example also seems like a nice addition. Thanks for that @Nemo157! It also seems to pass, to my ever greater surprise.^^ |
With barriers entering the picture, I now see a violation of Stacked Borrows in async functions. The error happens with the following stacktrace:
The relevant parts are: There is a call to /// Sets the thread-local task context used by async/await futures.
pub fn set_task_waker<F, R>(lw: &LocalWaker, f: F) -> R
where
F: FnOnce() -> R
{
let old_waker = TLS_WAKER.with(|tls_waker| {
tls_waker.replace(Some(NonNull::from(lw)))
});
let _reset_waker = SetOnDrop(old_waker);
f()
} Notice that pub fn get_task_waker<F, R>(f: F) -> R
where
F: FnOnce(&LocalWaker) -> R
{
let waker_ptr = TLS_WAKER.with(|tls_waker| {
// Clear the entry so that nested `get_task_waker` calls
// will fail or set their own value.
tls_waker.replace(None)
});
let _reset_waker = SetOnDrop(waker_ptr);
let mut waker_ptr = waker_ptr.expect(
"TLS LocalWaker not set. This is a rustc bug. \
Please file an issue on https://github.com/rust-lang/rust.");
unsafe { f(waker_ptr.as_mut()) }
} Notice the It also likely is just a typo, |
I confirmed that rust-lang/rust#56319 fixes this. |
This bug would have also been eliminated when the desugaring uses some sort of resume argument instead of TLS (that is, the entire context in which the mistake occurred is incidental complexity to the implementation) |
fix futures creating aliasing mutable and shared ref Fixes the problem described in rust-lang/miri#532 (comment): `set_task_waker` takes a shared reference and puts a copy into the TLS (in a `NonNull`), but `get_task_waker` gets it back out as a mutable reference. That violates "mutable references must not alias anything"!
Turns out I was very right to be suspicious, but didn't come up with the right test case. But the issue with Stacked Borrows and self-referential generators is a spec one, not a Miri one, so it is being tracked at rust-lang/unsafe-code-guidelines#148. |
I have no idea if an
async
function that borrows across a yield point is compatible with Stacked Borrows. We should find out! I just am not sure how.@cramertj @withoutboats I could use your help. :) What is a minimal example for code that creates a self-referential future and then runs it? Ideally with no dependencies beyond libstd but that might not be possible.^^ Also, if there are different cases/"kinds" of self-referential fields in futures, we should make sure we cover all of them.
rustc must test these things somehow, right? Might be worth looking into that, too.
The text was updated successfully, but these errors were encountered: