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

closure captures copyable variable doesn't according capture rule #60413

Closed
uonr opened this issue Apr 30, 2019 · 1 comment
Closed

closure captures copyable variable doesn't according capture rule #60413

uonr opened this issue Apr 30, 2019 · 1 comment
Labels
A-closures Area: closures (`|args| { .. }`) A-lifetimes Area: lifetime related C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@uonr
Copy link

uonr commented Apr 30, 2019

This code will success compiled.

struct Madoka;

fn main() {
    let c = {
        let v = Madoka;
        || v
    };
}

playground

But if derived Copy:

#[derive(Copy, Clone)]
struct Madoka;

fn main() {
    let c = {
        let v = Madoka;
        || v // error[E0597]: `v` does not live long enough
    };
}
error[E0597]: `v` does not live long enough
 --> src/main.rs:8:12
  |
6 |     let c = {
  |         - borrow later stored here
7 |         let v = Madoka;
8 |         || v // error[E0597]: `v` does not live long enough
  |         -- ^ borrowed value does not live long enough
  |         |
  |         value captured here
9 |     };
  |     - `v` dropped here while still borrowed

error: aborting due to previous error

playground

this is very confusing.

MIR

struct Homura;

fn main() {
    let v = Homura;
    let c = || std::mem::drop(v); 
}

MIR of closure is

fn main::{{closure}}(_1: [closure@src/main.rs:5:13: 5:33 v:Homura]) -> () { // <- notice this
    let mut _0: ();                      // return place
    let mut _2: Homura;

    bb0: {                              
        StorageLive(_2);                 // bb0[0]: scope 0 at src/main.rs:5:31: 5:32
        _2 = move (_1.0: Homura);        // bb0[1]: scope 0 at src/main.rs:5:31: 5:32 <- notice this
        _0 = const std::mem::drop(move _2) -> bb1; // bb0[2]: scope 0 at src/main.rs:5:16: 5:33
                                         // ty::Const
                                         // + ty: fn(Homura) {std::mem::drop::<Homura>}
                                         // + val: Scalar(Bits { size: 0, bits: 0 })
                                         // mir::Constant
                                         // + span: src/main.rs:5:16: 5:30
                                         // + ty: fn(Homura) {std::mem::drop::<Homura>}
                                         // + literal: Evaluated(Const { ty: fn(Homura) {std::mem::drop::<Homura>}, val: Scalar(Bits { size: 0, bits: 0 }) })
    }

    bb1: {                              
        StorageDead(_2);                 // bb1[0]: scope 0 at src/main.rs:5:32: 5:33
        return;                          // bb1[1]: scope 0 at src/main.rs:5:33: 5:33
    }
}

but after derive Copy:

#[derive(Clone, Copy)]
struct Homura;

fn main() {
    let v = Homura;
    let c = || std::mem::drop(v); 
}

playground

fn main::{{closure}}(_1: &[closure@src/main.rs:6:13: 6:33 v:&Homura]) -> () { // <- compare to before
    let mut _0: ();                      // return place
    let mut _2: Homura;

    bb0: {                              
        StorageLive(_2);
        _2 = (*((*_1).0: &Homura));      //  <- ?????
        _0 = const std::mem::drop(move _2) -> bb1;
    }
    // ....
}

More examples

There is a blog article about this bug, but it is write up by Chinese.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a4cebe3ca7669755d34de340f14305fc

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=eddfdbcdfeed71b1311df6115f7484a5

Maybe related issue:
#47448 and #36569

@jonas-schievink jonas-schievink added A-closures Area: closures (`|args| { .. }`) A-lifetimes Area: lifetime related C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Apr 30, 2019
@eddyb
Copy link
Member

eddyb commented Apr 30, 2019

This is not a bug, but expected behavior, you need move || v to force a move, in today's Rust.

Explanation:
When the compiler can copy the capture (because it knows Madoka: Copy), it doesn't force the capture to become by-move, so it remains in the default (by-ref) mode.
This is why you see *env.0 in MIR (where env is *_1 because of the closure call also being by-ref).

In the future, a solution like #54060 might get rid of the "doesn't live long enough" error.

@eddyb eddyb closed this as completed Apr 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: closures (`|args| { .. }`) A-lifetimes Area: lifetime related C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants