-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Closed
Closed
Copy link
Labels
A-NLLArea: Non-lexical lifetimes (NLL)Area: Non-lexical lifetimes (NLL)A-async-awaitArea: Async & AwaitArea: Async & AwaitAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.Async-await issues that have been triaged during a working group meeting.C-enhancementCategory: An issue proposing an enhancement or a PR with one.Category: An issue proposing an enhancement or a PR with one.E-mediumCall for participation: Medium difficulty. Experience needed to fix: Intermediate.Call for participation: Medium difficulty. Experience needed to fix: Intermediate.E-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
Description
My co-worker stumbled upon this curious case where failing typecheck can be made to succeed only by making "equivalent" lowerings to code. In particular the following snippet fails to compile because there's a !Send
type being retained across the yield point:
Failing code example
use std::ops::Index;
/// A `Send + !Sync` for demonstration purposes.
struct Banana(*mut ());
unsafe impl Send for Banana {}
impl Banana {
/// Make a static mutable reference to Banana for convenience purposes.
///
/// Any potential unsoundness here is not super relevant to the issue at hand.
fn new() -> &'static mut Banana {
static mut BANANA: Banana = Banana(std::ptr::null_mut());
unsafe {
&mut BANANA
}
}
}
// Peach is still Send (because `impl Send for &mut T where T: Send`)
struct Peach<'a>(&'a mut Banana);
impl<'a> std::ops::Index<usize> for Peach<'a> {
type Output = ();
fn index(&self, idx: usize) -> &() {
&()
}
}
async fn baz(v: &()) {}
async fn bar() -> () {
let peach = Peach(Banana::new());
let r = &peach[0];
baz(r).await;
peach.index(0); // make sure peach is retained across yield point
}
fn assert_send<T: Send>(_: T) {}
pub fn main() {
assert_send(bar())
}
This snippet will fail with the following error (playground):
error: future cannot be sent between threads safely
--> src/main.rs:41:5
suggesting that a &peach
is being retained across the yield point baz(r).await
. What is curious, however, that lowering the indexing operation to a method call on Index
will make code build (playground):
Succeeding code example
use std::ops::Index;
/// A `Send + !Sync` for demonstration purposes.
struct Banana(*mut ());
unsafe impl Send for Banana {}
impl Banana {
/// Make a static mutable reference to Banana for convenience purposes.
///
/// Any potential unsoundness here is not super relevant to the issue at hand.
fn new() -> &'static mut Banana {
static mut BANANA: Banana = Banana(std::ptr::null_mut());
unsafe {
&mut BANANA
}
}
}
// Peach is still Send (because `impl Send for &mut T where T: Send`)
struct Peach<'a>(&'a mut Banana);
impl<'a> std::ops::Index<usize> for Peach<'a> {
type Output = ();
fn index(&self, idx: usize) -> &() {
&()
}
}
async fn baz(v: &()) {}
async fn bar() -> () {
let peach = Peach(Banana::new());
let r = &*peach.index(0);
baz(r).await;
peach.index(0); // make sure peach is retained across yield point
}
fn assert_send<T: Send>(_: T) {}
pub fn main() {
assert_send(bar())
}
I’m not sure quite yet whether its incorrect that we successfully build the latter example or incorrect in that we retain an immutable reference in the former example.
Metadata
Metadata
Assignees
Labels
A-NLLArea: Non-lexical lifetimes (NLL)Area: Non-lexical lifetimes (NLL)A-async-awaitArea: Async & AwaitArea: Async & AwaitAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.Async-await issues that have been triaged during a working group meeting.C-enhancementCategory: An issue proposing an enhancement or a PR with one.Category: An issue proposing an enhancement or a PR with one.E-mediumCall for participation: Medium difficulty. Experience needed to fix: Intermediate.Call for participation: Medium difficulty. Experience needed to fix: Intermediate.E-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
Type
Projects
Status
Done