Skip to content
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

regression: change in async capture rules? #117059

Closed
Mark-Simulacrum opened this issue Oct 22, 2023 · 12 comments · Fixed by #117712
Closed

regression: change in async capture rules? #117059

Mark-Simulacrum opened this issue Oct 22, 2023 · 12 comments · Fixed by #117712
Labels
E-needs-mcve Call for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example I-types-nominated Nominated for discussion during a types team meeting. P-high High priority regression-from-stable-to-beta Performance or correctness regression from stable to beta. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.
Milestone

Comments

@Mark-Simulacrum
Copy link
Member

https://crater-reports.s3.amazonaws.com/beta-1.74-4/beta-2023-10-21/reg/esl01-dag-0.3.0/log.txt

[INFO] [stdout] error: future cannot be sent between threads safely
[INFO] [stdout]    --> src/nameset/lazy.rs:102:9
[INFO] [stdout]     |
[INFO] [stdout] 102 | /         Box::pin(futures::stream::unfold(self, |mut state| async move {
[INFO] [stdout] 103 | |             let result = state.next().await;
[INFO] [stdout] 104 | |             result.map(|r| (r, state))
[INFO] [stdout] 105 | |         }))
[INFO] [stdout]     | |___________^ future created by async block is not `Send`
[INFO] [stdout]     |
[INFO] [stdout]     = help: the trait `Sync` is not implemented for `(dyn VertexStream<Item = std::result::Result<VertexName, DagError>> + 'static)`
[INFO] [stdout] note: future is not `Send` as this value is used across an await
[INFO] [stdout]    --> src/nameset/lazy.rs:90:72
[INFO] [stdout]     |
[INFO] [stdout] 78  |             match inner.state {
[INFO] [stdout]     |                   ----------- has type `&lazy::Inner` which is not `Send`
[INFO] [stdout] ...
[INFO] [stdout] 90  |                             if let Err(err) = inner.load_more(1, None).await {
[INFO] [stdout]     |                                                                        ^^^^^ await occurs here, with `inner.state` maybe used later
[INFO] [stdout]     = note: required for the cast from `Pin<Box<Unfold<Iter, {[email protected]:102:48}, {async block@src/nameset/lazy.rs:102:60: 105:10}>>>` to `Pin<Box<dyn VertexStream<Item = std::result::Result<VertexName, DagError>>>>`
[INFO] [stdout]     = note: the full name for the source type has been written to '/opt/rustwide/target/debug/deps/dag-bd0c9923e92e6bff.long-type-3400019024982072103.txt'
[INFO] [stdout] 
@Mark-Simulacrum Mark-Simulacrum added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. regression-from-stable-to-beta Performance or correctness regression from stable to beta. labels Oct 22, 2023
@Mark-Simulacrum Mark-Simulacrum added this to the 1.74.0 milestone Oct 22, 2023
@rustbot rustbot added I-prioritize Issue: Indicates that prioritization has been requested for this issue. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Oct 22, 2023
@Noratrieb
Copy link
Member

that's probably coming from -Zdrop-tracking-mir
@cjgillot

@Noratrieb Noratrieb removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Oct 22, 2023
@apiraino
Copy link
Contributor

for reference: 13e6f24

Assigning priority (Zulip discussion), possibly to be adjusted after discussion.

@rustbot label -I-prioritize +P-high

@rustbot rustbot added P-high High priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Oct 23, 2023
@Noratrieb
Copy link
Member

this is basically a duplicate of #116242

@lqd
Copy link
Member

lqd commented Nov 2, 2023

Indeed bisects to #107421 cc @cjgillot.

However, it's not obviously a duplicate of #116242 to me (nothing screams dropck here), and it is indeed not fixed by @lcnr's #117134.

@wesleywiser wesleywiser added I-types-nominated Nominated for discussion during a types team meeting. T-types Relevant to the types team, which will review and decide on the PR/issue. labels Nov 2, 2023
@wesleywiser
Copy link
Member

Nominating for T-types discussion. This is a regression similar to #116242 (also caused by #107421) but is not resolved by #117134.

Do we see a possible path to fixing this or should we just accept the breakage?

@lqd lqd added the E-needs-mcve Call for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example label Nov 2, 2023
@lqd
Copy link
Member

lqd commented Nov 2, 2023

Heavily reduced, and can still be minimized (as e.g. depends on futures):

use std::sync::Arc;

use futures::lock::Mutex;
use futures::Stream;

pub trait VertexStream: Stream<Item = ()> + Send {}
impl<T> VertexStream for T where T: Stream<Item = ()> + Send {}

struct Inner {
    stream: Box<dyn VertexStream>,
    state: State,
}

enum State {
    Incomplete,
    Complete,
}
impl Inner {
    async fn load_more(&mut self) {}
}

pub(crate) struct Iter {
    inner: Arc<Mutex<Inner>>,
}
impl Iter {
    async fn next(&mut self) {
        let mut inner = self.inner.lock().await;

        match inner.state {
            State::Complete if true => {}
            State::Incomplete => {
                let _ = inner.load_more().await;
            }
            _ => {}
        }
    }
    fn into_stream(self) -> Box<dyn VertexStream> {
        Box::new(futures::stream::unfold(self, |mut state| async move {
            let _ = state.next().await;
            Some(()).map(|r| (r, state))
        }))
    }
}

@lcnr
Copy link
Contributor

lcnr commented Nov 3, 2023

further minimized, does not depend on external crates now: https://rust.godbolt.org/z/s5MbWqvWs

struct SendNotSync(*const ());
unsafe impl Send for SendNotSync {}
// impl !Sync for Foo {}

struct Inner {
    stream: SendNotSync,
    state: bool,
}

struct SendSync;
impl std::ops::Deref for SendSync {
    type Target = Inner;
    fn deref(&self) -> &Self::Target {
        todo!();
    }
}

async fn next() {
    let inner = SendSync;
    match inner.state {
        true if true => {}
        false => std::future::ready(()).await,
        _ => {}
    }
}

fn is_send<T: Send>(_: T) {}
fn main() {
    is_send(async move {
        let _ = next().await;
    })
}

@lcnr

This comment was marked as outdated.

@lcnr
Copy link
Contributor

lcnr commented Nov 8, 2023

struct SendNotSync(*const ());
unsafe impl Send for SendNotSync {}
// impl !Sync for SendNotSync {} // automatically disabled

struct Inner {
    stream: SendNotSync,
    state: bool,
}

struct SendSync;
impl std::ops::Deref for SendSync {
    type Target = Inner;
    fn deref(&self) -> &Self::Target {
        todo!();
    }
}

async fn next() {
    let inner = SendSync;
    match inner.state {
        true if true => {}
        false => async {}.await,
        _ => {}
    }
}

fn is_send<T: Send>(_: T) {}
fn main() {
    is_send(next())
}

This test fails regardless of whether needs_drop returns yes or no for generators. Even changing generators to never need drop does not fix this regression. This means it is not a change to mir_generator|coroutine_witnesses, but a divergence between HIR and MIR witness computation.

Two questions remain:

  • why do we need this weird match structure for this regression
  • was the old HIR based computation unsound, can this regression be fixed

@lcnr
Copy link
Contributor

lcnr commented Nov 8, 2023

We require a local to be stored at a yield point if it either is used afterwards directly or there exists a reference to that local. If the local has been dropped via a StorageDead we ignore it.

The local for &Inner implicitly created for the match statement is not used directly after the yield. Its StorageDead is after the yield however.

If there is a match guard, we emit a fake borrow of the scrutinee before the first deref, given (*inner).state we therefore fake borrow inner) to prevent us from overwriting it inside of the guard, the example used in the source is:

match x[10] {
    _ if { x = &[0]; false } => (),
    // would now be out of bounds
    y => (),
}

So, because of that fake borrow of &inner we consider it to be potentially borrowed and still required. However, the borrowed value is never actually used, only considered to be used by mir borrowck.

This means we should be able to ignore Shallow borrows when computing the generator layout, going to try that ✨

bors added a commit to rust-lang-ci/rust that referenced this issue Nov 9, 2023
generator layout: ignore fake borrows

fixes rust-lang#117059

We emit fake shallow borrows in case the scrutinee place uses a `Deref` and there is a match guard. This is necessary to prevent the match guard from mutating the scrutinee: https://github.com/rust-lang/rust/blob/fab1054e1742790c22ccc92a625736d658363677/compiler/rustc_mir_build/src/build/matches/mod.rs#L1250-L1265

These fake borrows end up impacting the generator witness computation in `mir_generator_witnesses`, which causes the issue in rust-lang#117059. This PR now completely ignores fake borrows during this computation. This is sound as thse are always removed after analysis and the actual computation of the generator layout happens afterwards.

Only the second commit impacts behavior, and could be backported by itself.

r? types
@bors bors closed this as completed in b7583d3 Nov 9, 2023
@lcnr
Copy link
Contributor

lcnr commented Nov 9, 2023

reopening until beta-backport is completed

@lcnr lcnr reopened this Nov 9, 2023
@Mark-Simulacrum
Copy link
Member Author

Backported in #117764

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
E-needs-mcve Call for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example I-types-nominated Nominated for discussion during a types team meeting. P-high High priority regression-from-stable-to-beta Performance or correctness regression from stable to beta. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants