-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Allow *local* mutation and assignments in pattern guards #1006
Comments
The failing code fails with the following error:
But there's no mutation here. Something seems rather confused. |
`any()` is mutably borrowing `iv.iter()`.
|
Oh I suppose it is. I was interpreting it as trying to mutably borrow the |
It would be really nice to make this check more discriminating. This code (nearly the same as @Stebalien's) shows why it's nonsensical: #[inline(always)]
fn check<'a, I: Iterator<Item=&'a i32>>(mut i: I) -> bool {
i.any(|&x| x == 2)
}
fn main() {
let slice = [1, 2, 3];
let x = match 42 {
_ if slice.iter().any(|&x| x == 2) => { true },
_ if check(slice.iter()) => { true },
_ => { false }
};
println!("{}", x);
} There's an error when the |
It's a strange error to exist at all given how easy it is to subvert.
It seems like something that would be better served as a warning if the intention is to prevent accidental mutation in pattern guards? |
Ping. I'm pretty sure this rule is essentially a compiler deficiency rather than anything with a principled reason to exist. The problems mentioned in #15989 would be solved by treating the expression being matched on as borrowed (in full) during the pattern guard. I suppose this would probably come for free with non-lexical lifetimes?... cc @pcwalton |
See @pnkfelix' comments in the original PR (#15989). It is something we should be able to relax. |
This just came up again on IRC. Is it something that's actually difficult to implement (so an RFC would require a lot of Detailed Design), or can the proposal simply be "turn off this part of the mutation check"? |
@durka It seems like a "MIR borrowck allows us to check complex interaction correctly" kind of issue. |
This is more or less a duplicate of rust-lang/rust#27282, rust-lang/rust#31287, etc. We plan to support mutation in match arms, we're working on making it sound. I think I'll just close the issue if that's ok =) |
Now, if you pass `-Z disable-ast-check-for-mutation-in-guard`, then we will just allow you to mutably-borrow and assign in guards of `match` arms. This is wildly unsound with AST-borrowck. It is also unsound with MIR-borrowck without further adjustments, which come in later in the commit series on this Pull Request. See also rust-lang#24535 and rust-lang/rfcs#1006.
…ake-three, r=nikomatsakis every match arm reads the match's borrowed input This PR changes the `match` codegen under NLL (and just NLL, at least for now) to make the following adjustments: * It adds a `-Z disable-ast-check-for-mutation-in-guard` which, as described, turns off the naive (conservative but also not 100% sound) check for mutation in guards of match arms. * We now borrow the match input at the outset and emit a special `ReadForMatch` statement (that, according to the *static* semantics, reads that borrowed match input) at the start of each match arm. The intent here is to catch cases where the match guard mutates the match input, either via an independent borrow or via `ref mut` borrows in that arm's pattern. * In order to ensure that `ref mut` borrows do not actually conflict with the emitted `ReadForMatch` statements, I expanded the two-phase-borrow system slightly, and also changed the MIR code gen so that under NLL, when there is a guard on a match arm, then each pattern variable ends up having *three* temporaries associated with it: 1. The first temporary will hold the substructure being matched; this is what we will move the (substructural) value into *if* the guard succeeds. 2. The second temporary also corresponds to the same value as the first, but we are just constructing this temporarily for use during the scope of the guard; it is unaliased and its sole referrer is the third temporary. 3. The third temporary is a reference to the second temporary. * (This sounds complicated, I know, but its actually *simpler* than what I was doing before and had checked into the repo, which was to sometimes construct the final value and then take a reference to it before evaluating the guard. See also PR #49870.) Fix #27282 This also provides a path towards resolving #24535 aka rust-lang/rfcs#1006, at least once the `-Z disable-ast-check-for-mutation-in-guard` is just turned on by default (under NLL, that is. It is not sound under AST-borrowck). * But I did not want to make `#![feature(nll)]` imply that as part of this PR; that seemed like too drastic a change to me.
Due to rust-lang/rust#15989, it isn't possible to assign/mutate in pattern guards. However, assignment/mutation of variables local to the pattern guard should be perfectly safe.
As reported on reddit: https://pay.reddit.com/r/rust/comments/2ztiag/cannot_mutably_borrow_in_a_pattern_guard/ by qthree:
For completeness, the failing code is:
However, the following works:
The text was updated successfully, but these errors were encountered: