From bf6c007760169e9c382d3700fd1cdd20037e4343 Mon Sep 17 00:00:00 2001
From: Huon Wilson <dbau.pp+github@gmail.com>
Date: Fri, 5 Dec 2014 15:56:25 -0800
Subject: [PATCH] Change `&` pat to only work with &T, and `&mut` with &mut T.

This implements RFC 179 by making the pattern `&<pat>` require matching
against a variable of type `&T`, and introducing the pattern `&mut
<pat>` which only works with variables of type `&mut T`.

The pattern `&mut x` currently parses as `&(mut x)` i.e. a pattern match
through a `&T` or a `&mut T` that binds the variable `x` to have type
`T` and to be mutable. This should be rewritten as follows, for example,

    for &mut x in slice.iter() {

becomes

    for &x in slice.iter() {
        let mut x = x;

Due to this, this is a

[breaking-change]

Closes #20496.
---
 src/doc/reference.md                          |  5 ++--
 src/libcore/str/mod.rs                        |  2 +-
 src/librustc/middle/cfg/construct.rs          |  2 +-
 src/librustc/middle/check_match.rs            |  6 ++---
 src/librustc/middle/mem_categorization.rs     |  6 +++--
 src/librustc_trans/trans/_match.rs            |  4 +--
 src/librustc_trans/trans/debuginfo.rs         |  2 +-
 src/librustc_typeck/check/_match.rs           | 10 ++++---
 src/librustdoc/clean/mod.rs                   |  2 +-
 src/libsyntax/ast.rs                          |  2 +-
 src/libsyntax/ast_util.rs                     |  2 +-
 src/libsyntax/ext/deriving/generic/mod.rs     |  2 +-
 src/libsyntax/fold.rs                         |  2 +-
 src/libsyntax/parse/parser.rs                 |  9 +++++--
 src/libsyntax/print/pprust.rs                 |  5 +++-
 src/libsyntax/visit.rs                        |  2 +-
 src/libtest/stats.rs                          |  3 ++-
 .../mut-pattern-internal-mutability.rs        | 24 +++++++++++++++++
 .../compile-fail/mut-pattern-mismatched.rs    | 26 +++++++++++++++++++
 19 files changed, 91 insertions(+), 25 deletions(-)
 create mode 100644 src/test/compile-fail/mut-pattern-internal-mutability.rs
 create mode 100644 src/test/compile-fail/mut-pattern-mismatched.rs

diff --git a/src/doc/reference.md b/src/doc/reference.md
index d7930285260..0d3f9430bd6 100644
--- a/src/doc/reference.md
+++ b/src/doc/reference.md
@@ -3484,8 +3484,9 @@ fn main() {
 
 ```
 
-Patterns can also dereference pointers by using the `&`, `box` symbols,
-as appropriate. For example, these two matches on `x: &int` are equivalent:
+Patterns can also dereference pointers by using the `&`, `&mut` and `box`
+symbols, as appropriate. For example, these two matches on `x: &int` are
+equivalent:
 
 ```
 # let x = &3i;
diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs
index d069744f8da..b497e9733bc 100644
--- a/src/libcore/str/mod.rs
+++ b/src/libcore/str/mod.rs
@@ -230,7 +230,7 @@ impl<F> CharEq for F where F: FnMut(char) -> bool {
 impl<'a> CharEq for &'a [char] {
     #[inline]
     fn matches(&mut self, c: char) -> bool {
-        self.iter().any(|&mut m| m.matches(c))
+        self.iter().any(|&m| { let mut m = m; m.matches(c) })
     }
 
     #[inline]
diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs
index de81f307c4d..3c672d0fdb6 100644
--- a/src/librustc/middle/cfg/construct.rs
+++ b/src/librustc/middle/cfg/construct.rs
@@ -119,7 +119,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
             }
 
             ast::PatBox(ref subpat) |
-            ast::PatRegion(ref subpat) |
+            ast::PatRegion(ref subpat, _) |
             ast::PatIdent(_, _, Some(ref subpat)) => {
                 let subpat_exit = self.pat(&**subpat, pred);
                 self.add_node(pat.id, &[subpat_exit])
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 2d9284846ac..f2b9ecb5ec4 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -473,7 +473,7 @@ fn construct_witness(cx: &MatchCheckCtxt, ctor: &Constructor,
             }
         }
 
-        ty::ty_rptr(_, ty::mt { ty, .. }) => {
+        ty::ty_rptr(_, ty::mt { ty, mutbl }) => {
             match ty.sty {
                ty::ty_vec(_, Some(n)) => match ctor {
                     &Single => {
@@ -493,7 +493,7 @@ fn construct_witness(cx: &MatchCheckCtxt, ctor: &Constructor,
 
                 _ => {
                     assert_eq!(pats_len, 1);
-                    ast::PatRegion(pats.nth(0).unwrap())
+                    ast::PatRegion(pats.nth(0).unwrap(), mutbl)
                 }
             }
         }
@@ -860,7 +860,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
         ast::PatTup(ref args) =>
             Some(args.iter().map(|p| &**p).collect()),
 
-        ast::PatBox(ref inner) | ast::PatRegion(ref inner) =>
+        ast::PatBox(ref inner) | ast::PatRegion(ref inner, _) =>
             Some(vec![&**inner]),
 
         ast::PatLit(ref expr) => {
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 31c3ca4199f..f600086fc5e 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -1262,8 +1262,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
             }
           }
 
-          ast::PatBox(ref subpat) | ast::PatRegion(ref subpat) => {
-            // @p1, ~p1, ref p1
+          ast::PatBox(ref subpat) | ast::PatRegion(ref subpat, _) => {
+            // box p1, &p1, &mut p1.  we can ignore the mutability of
+            // PatRegion since that information is already contained
+            // in the type.
             let subcmt = try!(self.cat_deref(pat, cmt, 0, false));
               try!(self.cat_pattern_(subcmt, &**subpat, op));
           }
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index 50cbe664b90..fed0931cab7 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -683,7 +683,7 @@ fn any_uniq_pat(m: &[Match], col: uint) -> bool {
 }
 
 fn any_region_pat(m: &[Match], col: uint) -> bool {
-    any_pat!(m, col, ast::PatRegion(_))
+    any_pat!(m, col, ast::PatRegion(..))
 }
 
 fn any_irrefutable_adt_pat(tcx: &ty::ctxt, m: &[Match], col: uint) -> bool {
@@ -1725,7 +1725,7 @@ fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             let llbox = Load(bcx, val);
             bcx = bind_irrefutable_pat(bcx, &**inner, llbox, cleanup_scope);
         }
-        ast::PatRegion(ref inner) => {
+        ast::PatRegion(ref inner, _) => {
             let loaded_val = Load(bcx, val);
             bcx = bind_irrefutable_pat(bcx, &**inner, loaded_val, cleanup_scope);
         }
diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs
index 916fcbfe13e..0789be95642 100644
--- a/src/librustc_trans/trans/debuginfo.rs
+++ b/src/librustc_trans/trans/debuginfo.rs
@@ -3472,7 +3472,7 @@ fn create_scope_map(cx: &CrateContext,
                 }
             }
 
-            ast::PatBox(ref sub_pat) | ast::PatRegion(ref sub_pat) => {
+            ast::PatBox(ref sub_pat) | ast::PatRegion(ref sub_pat, _) => {
                 scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
                 walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
             }
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index d8b410abf84..1efff9d41f1 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -146,12 +146,16 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
                 check_pat(pcx, &**inner, tcx.types.err);
             }
         }
-        ast::PatRegion(ref inner) => {
+        ast::PatRegion(ref inner, mutbl) => {
             let inner_ty = fcx.infcx().next_ty_var();
 
-            let mutbl =
+            // SNAP c894171 remove this `if`-`else` entirely after next snapshot
+            let mutbl = if mutbl == ast::MutImmutable {
                 ty::deref(fcx.infcx().shallow_resolve(expected), true)
-                .map_or(ast::MutImmutable, |mt| mt.mutbl);
+                    .map_or(ast::MutImmutable, |mt| mt.mutbl)
+            } else {
+                mutbl
+            };
 
             let mt = ty::mt { ty: inner_ty, mutbl: mutbl };
             let region = fcx.infcx().next_region_var(infer::PatternRegion(pat.span));
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index f4d0bb79d88..6a4337a664b 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2235,7 +2235,7 @@ fn name_from_pat(p: &ast::Pat) -> String {
         PatTup(ref elts) => format!("({})", elts.iter().map(|p| name_from_pat(&**p))
                                             .collect::<Vec<String>>().connect(", ")),
         PatBox(ref p) => name_from_pat(&**p),
-        PatRegion(ref p) => name_from_pat(&**p),
+        PatRegion(ref p, _) => name_from_pat(&**p),
         PatLit(..) => {
             warn!("tried to get argument name from PatLit, \
                   which is silly in function arguments");
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index a33ee44be89..d61b7a17a93 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -556,7 +556,7 @@ pub enum Pat_ {
     PatStruct(Path, Vec<Spanned<FieldPat>>, bool),
     PatTup(Vec<P<Pat>>),
     PatBox(P<Pat>),
-    PatRegion(P<Pat>), // reference pattern
+    PatRegion(P<Pat>, Mutability), // reference pattern
     PatLit(P<Expr>),
     PatRange(P<Expr>, P<Expr>),
     /// [a, b, ..i, y, z] is represented as:
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 4026da6cf8e..5e03afec16c 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -633,7 +633,7 @@ pub fn walk_pat<F>(pat: &Pat, mut it: F) -> bool where F: FnMut(&Pat) -> bool {
             PatEnum(_, Some(ref s)) | PatTup(ref s) => {
                 s.iter().all(|p| walk_pat_(&**p, it))
             }
-            PatBox(ref s) | PatRegion(ref s) => {
+            PatBox(ref s) | PatRegion(ref s, _) => {
                 walk_pat_(&**s, it)
             }
             PatVec(ref before, ref slice, ref after) => {
diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs
index 8863de8757b..d522c346fa0 100644
--- a/src/libsyntax/ext/deriving/generic/mod.rs
+++ b/src/libsyntax/ext/deriving/generic/mod.rs
@@ -940,7 +940,7 @@ impl<'a> MethodDef<'a> {
                                                                          &**variant,
                                                                          self_arg_name,
                                                                          ast::MutImmutable);
-                    (cx.pat(sp, ast::PatRegion(p)), idents)
+                    (cx.pat(sp, ast::PatRegion(p, ast::MutImmutable)), idents)
                 };
 
                 // A single arm has form (&VariantK, &VariantK, ...) => BodyK
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 3d3068f6868..03a4f9046b5 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1267,7 +1267,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
             }
             PatTup(elts) => PatTup(elts.move_map(|x| folder.fold_pat(x))),
             PatBox(inner) => PatBox(folder.fold_pat(inner)),
-            PatRegion(inner) => PatRegion(folder.fold_pat(inner)),
+            PatRegion(inner, mutbl) => PatRegion(folder.fold_pat(inner), mutbl),
             PatRange(e1, e2) => {
                 PatRange(folder.fold_expr(e1), folder.fold_expr(e2))
             },
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 37ac86a3324..d9183ef0c49 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -3357,11 +3357,16 @@ impl<'a> Parser<'a> {
             })
           }
           token::BinOp(token::And) | token::AndAnd => {
-            // parse &pat
+            // parse &pat and &mut pat
             let lo = self.span.lo;
             self.expect_and();
+            let mutability = if self.eat_keyword(keywords::Mut) {
+                ast::MutMutable
+            } else {
+                ast::MutImmutable
+            };
             let sub = self.parse_pat();
-            pat = PatRegion(sub);
+            pat = PatRegion(sub, mutability);
             hi = self.last_span.hi;
             return P(ast::Pat {
                 id: ast::DUMMY_NODE_ID,
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 9702c79719c..9b2ee9de358 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2092,8 +2092,11 @@ impl<'a> State<'a> {
                 try!(word(&mut self.s, "box "));
                 try!(self.print_pat(&**inner));
             }
-            ast::PatRegion(ref inner) => {
+            ast::PatRegion(ref inner, mutbl) => {
                 try!(word(&mut self.s, "&"));
+                if mutbl == ast::MutMutable {
+                    try!(word(&mut self.s, "mut "));
+                }
                 try!(self.print_pat(&**inner));
             }
             ast::PatLit(ref e) => try!(self.print_expr(&**e)),
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index ec6b2cfa5c3..99701ec9710 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -519,7 +519,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
             }
         }
         PatBox(ref subpattern) |
-        PatRegion(ref subpattern) => {
+        PatRegion(ref subpattern, _) => {
             visitor.visit_pat(&**subpattern)
         }
         PatIdent(_, ref pth1, ref optional_subpattern) => {
diff --git a/src/libtest/stats.rs b/src/libtest/stats.rs
index 35af0e763d7..c42cf4d0551 100644
--- a/src/libtest/stats.rs
+++ b/src/libtest/stats.rs
@@ -169,7 +169,8 @@ impl<T: FloatMath + FromPrimitive> Stats<T> for [T] {
     fn sum(&self) -> T {
         let mut partials = vec![];
 
-        for &mut x in self.iter() {
+        for &x in self.iter() {
+            let mut x = x;
             let mut j = 0;
             // This inner loop applies `hi`/`lo` summation to each
             // partial so that the list of partial sums remains exact.
diff --git a/src/test/compile-fail/mut-pattern-internal-mutability.rs b/src/test/compile-fail/mut-pattern-internal-mutability.rs
new file mode 100644
index 00000000000..05c6c4a9655
--- /dev/null
+++ b/src/test/compile-fail/mut-pattern-internal-mutability.rs
@@ -0,0 +1,24 @@
+// Copyright 2015 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.
+
+fn main() {
+    let foo = &mut 1i;
+
+    let &mut x = foo;
+    x += 1; //~ ERROR re-assignment of immutable variable
+
+    // explicitly mut-ify internals
+    let &mut mut x = foo;
+    x += 1;
+
+    // check borrowing is detected successfully
+    let &mut ref x = foo;
+    *foo += 1; //~ ERROR cannot assign to `*foo` because it is borrowed
+}
diff --git a/src/test/compile-fail/mut-pattern-mismatched.rs b/src/test/compile-fail/mut-pattern-mismatched.rs
new file mode 100644
index 00000000000..74e6141a2b3
--- /dev/null
+++ b/src/test/compile-fail/mut-pattern-mismatched.rs
@@ -0,0 +1,26 @@
+// 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.
+//
+// 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.
+
+fn main() {
+    let foo = &mut 1i;
+
+    // (separate lines to ensure the spans are accurate)
+
+    // SNAP c894171 uncomment this after the next snapshot
+    // NOTE(stage0) just in case tidy doesn't check SNAP's in tests
+    // let &_ // ~ ERROR expected `&mut int`, found `&_`
+    //    = foo;
+    let &mut _ = foo;
+
+    let bar = &1i;
+    let &_ = bar;
+    let &mut _ //~ ERROR expected `&int`, found `&mut _`
+         = bar;
+}