Skip to content

Pattern guard can consume value that is being matched #31287

Closed

Description

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);
}

Activity

  1. added
    I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
    on Jan 29, 2016
  2. apasel422 commented on Jan 29, 2016

    @apasel422
    Contributor
  3. alexcrichton commented on Jan 29, 2016

    @alexcrichton
    Member

    triage: T-lang I-nominated

    I thought we fixed this :(

  4. Aatch commented on Jan 30, 2016

    @Aatch
    Contributor

    @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.

  5. arielb1 commented on Jan 31, 2016

    @arielb1
    Contributor

    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

  6. Aatch commented on Feb 1, 2016

    @Aatch
    Contributor

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

  7. nikomatsakis commented on Feb 18, 2016

    @nikomatsakis
    Contributor

    triage: P-high

  8. nikomatsakis commented on Apr 7, 2016

    @nikomatsakis
    Contributor

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

  9. added
    I-crashIssue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics.
    on Jun 23, 2016
  10. brson commented on Jun 23, 2016

    @brson
    Contributor

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

  11. 37 remaining items

  12. landaire commented on May 22, 2019

    @landaire
    Contributor

    I think the downgrade is performed here:

    if tcx.migrate_borrowck() {
    // When borrowck=migrate, check if AST-borrowck would
    // error on the given code.
    // rust-lang/rust#55492, rust-lang/rust#58776 check the base def id
    // for errors. AST borrowck is responsible for aggregating
    // `signalled_any_error` from all of the nested closures here.
    let base_def_id = tcx.closure_base_def_id(def_id);
    match tcx.borrowck(base_def_id).signalled_any_error {
    SignalledError::NoErrorsSeen => {
    // if AST-borrowck signalled no errors, then
    // downgrade all the buffered MIR-borrowck errors
    // to warnings.
    for err in mbcx.errors_buffer.iter_mut() {
    downgrade_if_error(err);
    }

    And references these issues related to the downgrade #55492 #58776

    For the explicit #![feature(nll)], it looks like this has to do with if tcx.migrate_borrowck(), which going down the call graph leads us to:

    /// What mode(s) of borrowck should we run? AST? MIR? both?
    /// (Also considers the `#![feature(nll)]` setting.)
    pub fn borrowck_mode(&self) -> BorrowckMode {
    // Here are the main constraints we need to deal with:
    //
    // 1. An opts.borrowck_mode of `BorrowckMode::Migrate` is
    // synonymous with no `-Z borrowck=...` flag at all.
    //
    // 2. We want to allow developers on the Nightly channel
    // to opt back into the "hard error" mode for NLL,
    // (which they can do via specifying `#![feature(nll)]`
    // explicitly in their crate).
    //
    // So, this precedence list is how pnkfelix chose to work with
    // the above constraints:
    //
    // * `#![feature(nll)]` *always* means use NLL with hard
    // errors. (To simplify the code here, it now even overrides
    // a user's attempt to specify `-Z borrowck=compare`, which
    // we arguably do not need anymore and should remove.)
    //
    // * Otherwise, if no `-Z borrowck=...` then use migrate mode
    //
    // * Otherwise, use the behavior requested via `-Z borrowck=...`
    if self.features().nll { return BorrowckMode::Mir; }
    self.sess.opts.borrowck_mode
    }

    Can't seem to find the relevant discussion for the decision here though.

  13. eddyb commented on May 23, 2019

    @eddyb
    Contributor

    @landaire Yeah but shouldn't Rust 2018 turn on the NLL borrowck in its full mode?
    Unless BorrowckMode::Mir was never on stable and the Compare mode was enabled for 2015 recently?
    (after having been the mode on the 2018 edition all this time)

  14. matthewjasper commented on May 23, 2019

    @matthewjasper
    Contributor

    Unless BorrowckMode::Mir was never on stable

    it wasn't.

  15. pnkfelix commented on May 24, 2019

    @pnkfelix
    Contributor

    @eddyb wrote:

    I'm a bit surprised about #![feature(nll)] having any effect on Rust 2018, is that intentional?
    If so, where can I read more about that decision?

    and also wrote:

    @landaire Yeah but shouldn't Rust 2018 turn on the NLL borrowck in its full mode?
    Unless BorrowckMode::Mir was never on stable and the Compare mode was enabled for 2015 recently?
    (after having been the mode on the 2018 edition all this time)

    See also #58781.

    In particular is mention there of making full NLL mode the default for the 2018 edition on its own (vs doing that for all editions en masse).

    p.s. the default is BorrowckMode::Migrate, not Compare. We should kill off Compare mode in the short term, as its not serving a real purpose anymore.

  16. added 6 commits that reference this issue on Jul 28, 2019
  17. added a commit that references this issue on Jan 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

C-bugCategory: This is a bug.I-crashIssue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-mediumMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.fixed-by-NLLBugs fixed, but only when NLL is enabled.

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions