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

librustc: Disallow mutation and assignment in pattern guards, and modify #15989

Closed
wants to merge 1 commit into from

Conversation

pcwalton
Copy link
Contributor

the CFG for match statements.

There were two bugs in issue #14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.

I discussed this with Niko and we decided this was the best plan of
action.

This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:

impl Foo {
    fn f(&mut self, ...) {}
    fn g(&mut self, ...) {
        match bar {
            Baz if self.f(...) => { ... }
            _ => { ... }
        }
    }
}

Change this code to not use a guard. For example:

impl Foo {
    fn f(&mut self, ...) {}
    fn g(&mut self, ...) {
        match bar {
            Baz => {
                if self.f(...) {
                    ...
                } else {
                    ...
                }
            }
            _ => { ... }
        }
    }
}

Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.

Closes #14684.

[breaking-change]

r? @pnkfelix

@pcwalton
Copy link
Contributor Author

Note that the code in the parser that I had to change was actually quite dangerous—it was matching on self.token and mutating self.token in the guard! This to me is evidence that this change is worthwhile.

the CFG for match statements.

There were two bugs in issue rust-lang#14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.

I discussed this with Niko and we decided this was the best plan of
action.

This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:

    impl Foo {
        fn f(&mut self, ...) {}
        fn g(&mut self, ...) {
            match bar {
                Baz if self.f(...) => { ... }
                _ => { ... }
            }
        }
    }

Change this code to not use a guard. For example:

    impl Foo {
        fn f(&mut self, ...) {}
        fn g(&mut self, ...) {
            match bar {
                Baz => {
                    if self.f(...) {
                        ...
                    } else {
                        ...
                    }
                }
                _ => { ... }
            }
        }
    }

Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.

Closes rust-lang#14684.

[breaking-change]
@pnkfelix
Copy link
Member

@pcwalton Am I right in thinking that in principle, we could later (backward compatibly) loosen the restrictions imposed here so that one could again mutate, assign, and mutably-borrow bindings that came from outside the current match expression that the guard occurs on?

@pcwalton
Copy link
Contributor Author

Yes.

bors added a commit that referenced this pull request Jul 29, 2014
the CFG for match statements.

There were two bugs in issue #14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.

I discussed this with Niko and we decided this was the best plan of
action.

This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:

    impl Foo {
        fn f(&mut self, ...) {}
        fn g(&mut self, ...) {
            match bar {
                Baz if self.f(...) => { ... }
                _ => { ... }
            }
        }
    }

Change this code to not use a guard. For example:

    impl Foo {
        fn f(&mut self, ...) {}
        fn g(&mut self, ...) {
            match bar {
                Baz => {
                    if self.f(...) {
                        ...
                    } else {
                        ...
                    }
                }
                _ => { ... }
            }
        }
    }

Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.

Closes #14684.

[breaking-change]

r? @pnkfelix
@bors bors closed this Jul 29, 2014
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mutating matched value in pattern guard causes segfault in safe code
3 participants