auto merge of #16053 : pcwalton/rust/at-pattern-bindings, r=pnkfelix

This is an alternative to upgrading the way rvalues are handled in the
borrow check. Making rvalues handled more like lvalues in the borrow
check caused numerous problems related to double mutable borrows and
rvalue scopes. Rather than come up with more borrow check rules to try
to solve these problems, I decided to just forbid pattern bindings after
`@`. This affected fewer than 10 lines of code in the compiler and
libraries.

This breaks code like:

    match x {
        y @ z => { ... }
    }

    match a {
        b @ Some(c) => { ... }
    }

Change this code to use nested `match` or `let` expressions. For
example:

    match x {
        y => {
            let z = y;
            ...
        }
    }

    match a {
        Some(c) => {
            let b = Some(c);
            ...
        }
    }

Closes #14587.

[breaking-change]

May need discussion at the meeting, but r? @nikomatsakis anyway
This commit is contained in:
bors 2014-08-01 16:01:04 +00:00
commit f2fa55903e
11 changed files with 103 additions and 129 deletions

View File

@ -3309,7 +3309,12 @@ enum List { Nil, Cons(uint, Box<List>) }
fn is_sorted(list: &List) -> bool {
match *list {
Nil | Cons(_, box Nil) => true,
Cons(x, ref r @ box Cons(y, _)) => (x <= y) && is_sorted(&**r)
Cons(x, ref r @ box Cons(_, _)) => {
match *r {
box Cons(y, _) => (x <= y) && is_sorted(&**r),
_ => fail!()
}
}
}
}

View File

@ -1724,7 +1724,7 @@ mod tests {
}
}
let mut count_x @ mut count_y = 0;
let (mut count_x, mut count_y) = (0, 0);
{
let mut tv = TwoVec {
x: Vec::new(),

View File

@ -630,8 +630,9 @@ impl LintPass for GatherNodeLevels {
match it.node {
ast::ItemEnum(..) => {
let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCE);
match cx.lints.get_level_source(lint_id) {
lvlsrc @ (lvl, _) if lvl != Allow => {
let lvlsrc = cx.lints.get_level_source(lint_id);
match lvlsrc {
(lvl, _) if lvl != Allow => {
cx.node_levels.borrow_mut()
.insert((it.id, lint_id), lvlsrc);
},

View File

@ -139,8 +139,10 @@ impl<'a> RestrictionsContext<'a> {
Safe
}
mc::cat_deref(cmt_base, _, pk @ mc::BorrowedPtr(ty::MutBorrow, lt)) |
mc::cat_deref(cmt_base, _, pk @ mc::Implicit(ty::MutBorrow, lt)) => {
mc::cat_deref(cmt_base, _, pk) => {
match pk {
mc::BorrowedPtr(ty::MutBorrow, lt) |
mc::Implicit(ty::MutBorrow, lt) => {
// R-Deref-Mut-Borrowed
if !self.bccx.is_subregion_of(self.loan_region, lt) {
self.bccx.report(
@ -156,11 +158,18 @@ impl<'a> RestrictionsContext<'a> {
let result = self.restrict(cmt_base);
self.extend(result, cmt.mutbl, LpDeref(pk))
}
mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
// We are very trusting when working with unsafe pointers.
mc::UnsafePtr(..) => {
// We are very trusting when working with unsafe
// pointers.
Safe
}
_ => {
self.bccx.tcx.sess.span_bug(self.span,
"unhandled memcat in \
cat_deref")
}
}
}
mc::cat_discr(cmt_base, _) => {
self.restrict(cmt_base)

View File

@ -145,6 +145,9 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
check_legality_of_move_bindings(cx,
arm.guard.is_some(),
arm.pats.as_slice());
for pat in arm.pats.iter() {
check_legality_of_bindings_in_at_patterns(cx, &**pat);
}
}
// Second, if there is a guard on each arm, make sure it isn't
@ -200,6 +203,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
// Check legality of move bindings.
check_legality_of_move_bindings(cx, false, [ *pat ]);
check_legality_of_bindings_in_at_patterns(cx, &**pat);
}
_ => ()
}
@ -455,8 +459,12 @@ fn all_constructors(cx: &MatchCheckCtxt, left_ty: ty::t,
// Note: is_useful doesn't work on empty types, as the paper notes.
// So it assumes that v is non-empty.
fn is_useful(cx: &MatchCheckCtxt, matrix @ &Matrix(ref rows): &Matrix,
v: &[Gc<Pat>], witness: WitnessPreference) -> Usefulness {
fn is_useful(cx: &MatchCheckCtxt,
matrix: &Matrix,
v: &[Gc<Pat>],
witness: WitnessPreference)
-> Usefulness {
let &Matrix(ref rows) = matrix;
debug!("{:}", matrix);
if rows.len() == 0u {
return match witness {
@ -819,8 +827,9 @@ fn check_local(cx: &mut MatchCheckCtxt, loc: &Local) {
None => ()
}
// Check legality of move bindings.
// Check legality of move bindings and `@` patterns.
check_legality_of_move_bindings(cx, false, [ loc.pat ]);
check_legality_of_bindings_in_at_patterns(cx, &*loc.pat);
}
fn check_fn(cx: &mut MatchCheckCtxt,
@ -840,6 +849,7 @@ fn check_fn(cx: &mut MatchCheckCtxt,
None => ()
}
check_legality_of_move_bindings(cx, false, [input.pat]);
check_legality_of_bindings_in_at_patterns(cx, &*input.pat);
}
}
@ -856,7 +866,6 @@ fn is_refutable(cx: &MatchCheckCtxt, pat: Gc<Pat>) -> Option<Gc<Pat>> {
}
// Legality of move bindings checking
fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
has_guard: bool,
pats: &[Gc<Pat>]) {
@ -966,3 +975,32 @@ impl<'a> Delegate for MutationChecker<'a> {
}
}
/// Forbids bindings in `@` patterns. This is necessary for memory safety,
/// because of the way rvalues are handled in the borrow check. (See issue
/// #14587.)
fn check_legality_of_bindings_in_at_patterns(cx: &MatchCheckCtxt, pat: &Pat) {
let mut visitor = AtBindingPatternVisitor {
cx: cx,
};
visitor.visit_pat(pat, true);
}
struct AtBindingPatternVisitor<'a,'b> {
cx: &'a MatchCheckCtxt<'b>,
}
impl<'a,'b> Visitor<bool> for AtBindingPatternVisitor<'a,'b> {
fn visit_pat(&mut self, pat: &Pat, bindings_allowed: bool) {
if !bindings_allowed && pat_is_binding(&self.cx.tcx.def_map, pat) {
self.cx.tcx.sess.span_err(pat.span,
"pattern bindings are not allowed \
after an `@`");
}
match pat.node {
PatIdent(_, _, Some(_)) => visit::walk_pat(self, pat, false),
_ => visit::walk_pat(self, pat, bindings_allowed),
}
}
}

View File

@ -600,8 +600,9 @@ impl<'a> RegionVarBindings<'a> {
b).as_slice());
}
(f @ ReFree(ref fr), ReScope(s_id)) |
(ReScope(s_id), f @ ReFree(ref fr)) => {
(ReFree(ref fr), ReScope(s_id)) |
(ReScope(s_id), ReFree(ref fr)) => {
let f = ReFree(*fr);
// A "free" region can be interpreted as "some region
// at least as big as the block fr.scope_id". So, we can
// reasonably compare free regions and scopes:
@ -706,8 +707,9 @@ impl<'a> RegionVarBindings<'a> {
b).as_slice());
}
(ReFree(ref fr), s @ ReScope(s_id)) |
(s @ ReScope(s_id), ReFree(ref fr)) => {
(ReFree(ref fr), ReScope(s_id)) |
(ReScope(s_id), ReFree(ref fr)) => {
let s = ReScope(s_id);
// Free region is something "at least as big as
// `fr.scope_id`." If we find that the scope `fr.scope_id` is bigger
// than the scope `s_id`, then we can say that the GLB

View File

@ -1,25 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct X { x: () }
impl Drop for X {
fn drop(&mut self) {
println!("destructor runs");
}
}
fn main() {
let x = Some(X { x: () });
match x {
Some(ref _y @ _z) => { }, //~ ERROR cannot bind by-move and by-ref in the same pattern
None => fail!()
}
}

View File

@ -1,21 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Issue #12534.
struct A(Box<uint>);
fn f(a @ A(u): A) -> Box<uint> { //~ ERROR cannot bind by-move with sub-bindings
drop(a);
u
}
fn main() {}

View File

@ -1,4 +1,4 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@ -8,18 +8,19 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct X { x: (), }
impl Drop for X {
fn drop(&mut self) {
println!("destructor runs");
}
enum Option<T> {
None,
Some(T),
}
fn main() {
let x = Some(X { x: () });
match x {
Some(_y @ ref _z) => { }, //~ ERROR cannot bind by-move with sub-bindings
None => fail!()
match &mut Some(1i) {
ref mut z @ &Some(ref a) => {
//~^ ERROR pattern bindings are not allowed after an `@`
**z = None;
println!("{}", *a);
}
_ => ()
}
}

View File

@ -15,24 +15,15 @@ fn main() {
ref b @ None => b
}, &Some(1i));
assert_eq!(match value {
ref a @ ref _c @ Some(_) => a,
ref b @ None => b
}, &Some(1i));
assert_eq!(match value {
_a @ ref c @ Some(_) => c,
ref c @ Some(_) => c,
ref b @ None => b
}, &Some(1i));
assert_eq!(match "foobarbaz" {
_a @ b @ _ => b
b @ _ => b
}, "foobarbaz");
let a @ b @ c = "foobarbaz";
let a @ _ = "foobarbaz";
assert_eq!(a, "foobarbaz");
assert_eq!(b, "foobarbaz");
assert_eq!(c, "foobarbaz");
let value = Some(true);
let ref a @ b @ ref c = value;
let ref a @ _ = value;
assert_eq!(a, &Some(true));
assert_eq!(b, Some(true));
assert_eq!(c, &Some(true));
}

View File

@ -1,27 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct A { a: int, b: int }
struct B { a: int, b: C }
struct D { a: int, d: C }
struct C { c: int }
pub fn main() {
match (A {a: 10, b: 20}) {
x@A {a, b: 20} => { assert!(x.a == 10); assert!(a == 10); }
A {b: _b, ..} => { fail!(); }
}
let mut x@B {b, ..} = B {a: 10, b: C {c: 20}};
x.b.c = 30;
assert_eq!(b.c, 20);
let mut y@D {d, ..} = D {a: 10, d: C {c: 20}};
y.d.c = 30;
assert_eq!(d.c, 20);
}