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

Pattern guard can consume value that is being matched #31287

Closed
tmiasko opened this Issue Jan 29, 2016 · 20 comments

Comments

@tmiasko
Copy link
Contributor

tmiasko commented Jan 29, 2016

Following compiles, and crashes when trying to print moved from value:

fn main() {
    let a = Some("...".to_owned());
    let b = match a {
        Some(_) if { drop(a); false } => None,
        x                             => x,
    };
    println!("{:?}", b);
}
@apasel422

This comment has been minimized.

Copy link
Member

apasel422 commented Jan 29, 2016

@alexcrichton

This comment has been minimized.

Copy link
Member

alexcrichton commented Jan 29, 2016

triage: T-lang I-nominated

I thought we fixed this :(

@Aatch

This comment has been minimized.

Copy link
Contributor

Aatch commented Jan 30, 2016

@alexcrichton unfortunately no. I fixed a very similar issue involving multiple guards, but not this case.

I did try to fix it, but it essentially requires SEME to work. This is because preventing this case introduces edges between successive patterns in the CFG and consequentially causes code like this:

match foo {
  (ref a, 0) => (),
  (ref a, 1) => ()
  _ => ()
}

to be rejected because the borrow in the first pattern overlaps the borrow in the second pattern. The reason why this has to be this way is because both move checking and borrow checking are done using the CFG, right now the CFG for a match "visits" all the arms in parallel, with pattern guards chaining into each other where appropriate. Improving it further requires chaining from the guard to the next pattern instead, but also requires indicating that the borrow in the pattern ends at both the "else" branch of the guard and the end of the block for the arm. In other words, we need regions with multiple exits aka SEME.

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Jan 31, 2016

Can't we just borrow the discriminant in all guards separately (cc @pnkfelix)? Actually, that might cause conflicts with ref mut borrows, but that conflict can be ignored (actually, we may want to mark patterns as aliasable in guards).

This issue won't be really fixed with MIR if we are talking about bypassing-exhaustiveness rather than

@Aatch

This comment has been minimized.

Copy link
Contributor

Aatch commented Feb 1, 2016

@arielb1 did you accidentally comment on the wrong issue? I'm not sure how discriminants and exhaustiveness come in here.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Feb 18, 2016

triage: P-high

@rust-highfive rust-highfive added P-high and removed I-nominated labels Feb 18, 2016

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Apr 7, 2016

I think this is one of those "fixed by moving region/borrowck to MIR"

@brson

This comment has been minimized.

Copy link
Contributor

brson commented Jun 23, 2016

@pnkfelix Is this still on your radar? It's been assigned a while.

@brson brson added the I-nominated label Jul 14, 2016

@brson

This comment has been minimized.

Copy link
Contributor

brson commented Jul 14, 2016

@rust-lang/lang nominated for retriage. Still looks bad. Can we make progress now?

@alexcrichton

This comment has been minimized.

Copy link
Member

alexcrichton commented Jul 14, 2016

As an update, still segfaults both on normal and MIR trans (example above)

@aturon aturon added T-compiler and removed T-lang labels Jul 14, 2016

@aturon

This comment has been minimized.

Copy link
Member

aturon commented Jul 14, 2016

Moving to compiler team; we believe this is blocked on MIR borrowck.

@pnkfelix pnkfelix removed their assignment Jul 21, 2016

@pnkfelix

This comment has been minimized.

Copy link
Member

pnkfelix commented Jul 21, 2016

triage: P-medium

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Dec 1, 2016

After some discussion in @rust-lang/compiler, the consensus was that we should increase the priority of this issue. The preferred fix is still MIR borrowck. Assigning to @pnkfelix as he has been doing work in that direction.

triage: P-high

@rust-highfive rust-highfive added P-high and removed P-medium labels Dec 1, 2016

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Dec 29, 2016

Reproducer without matching on the discriminant:

fn main() {
    let x = Box::new(0);
    match () {
        _ if { drop(x); false } => {}
        () => println!("{:?}", x)
    }
}
@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Dec 29, 2016

Related example from #37891; here the if is part of the if let desugaring:

fn main() {
    let vec = get_vec();
    if let Err(err) = Ok::<_, ()>(1) {
        println!("Got an error: {:?}", err);
    }
    else if vec.unwrap().len() == 0 {
        println!("Vec was 0 length.");
    }
    else {
        for i in vec.unwrap() {
            println!("{}", i);
        }
    }
}

fn get_vec() -> Result<Vec<i32>, i32> {
    Ok::<Vec<i32>, i32>(vec![1,2,3])
}
@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Dec 29, 2016

triage: P-medium

We decided that having this P-high wasn't really proper, since although we're actively working on a fix, it's via MIR borrowck, and not really a "drop everything" sort of situation.

@rust-highfive rust-highfive added P-medium and removed P-high labels Dec 29, 2016

@scooter-dangle

This comment has been minimized.

Copy link

scooter-dangle commented Jan 10, 2017

There's a weird facet to this issue that so far I haven't seen mentioned (unless I've just totally overlooked it). Playing with the code from @insanitybit's Golang and Rustlang Memory Safety, I found that inserting a println! prior to the offending match expression ends up causing the println! following the match expression to print the intended data, effectively masking the primary issue.

I (and the several other more experienced Rustaceans I discussed this with at Rust DC) was surprised that a legal read-only use of the variable affected behavior after where it should have been dropped.

This could make quirks arising from the actual issue far more difficult to debug and could possibly hide them from showing up until production if there are debug-only println!s in the code.

I wasn't sure whether to post this here or to #29723

https://is.gd/rnNMZb

    fn main() {
        let foo = String::from("FOO");

        // The addition of this line prevents the subsequent `println!` from
        // printing out garbage data.
        println!("1: {:?}", foo);

        let foo2 = match 0 {
            0 if {
                some_func(foo) // foo is freed here
            } => unreachable!(),
            _ => {
                // Use After Free - we return freed memory
                foo
            }
        };

        println!("2: {:?}", foo2); // And here we access the invalid memory
    }

    fn some_func(foo: String) -> bool {
        drop(foo);
        false
    }

prints out

1: "FOO"
2: "FOO"
@samueltardieu

This comment has been minimized.

Copy link

samueltardieu commented Jan 10, 2017

I find the proof of the corruption even more stunning if you add

   let bar = String::from("123456");

in front of the last println! so that memory get reused, as you now see:

1: "FOO"
2: "123"
@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Jan 11, 2018

cc #27282 -- seems like a duplicate of that

@nikomatsakis nikomatsakis added this to the NLL Future Compat Warnings milestone Jan 11, 2018

@nikomatsakis nikomatsakis modified the milestones: NLL run-pass without ICEs, NLL soundness violations Jan 19, 2018

@matthewjasper

This comment has been minimized.

Copy link
Contributor

matthewjasper commented Apr 24, 2018

Discussed in the weekly NLL triage.
All of the examples here fail to compile with NLL. This also has a test added from #47549. So we think this can be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.