about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-07-31 20:47:42 +0000
committerbors <bors@rust-lang.org>2017-07-31 20:47:42 +0000
commit37c7d0ebb3ec5b62bd37df9ee8826194e3c6300a (patch)
treee4f9fd14238ef8bc56860caf3a01825306a546c7 /src
parent2a6828e7f1ed3163a2797f0a111570ef130f7b6b (diff)
parent8f67f1efaf792a0c3ef629e1e62e53eba7365a1c (diff)
downloadrust-37c7d0ebb3ec5b62bd37df9ee8826194e3c6300a.tar.gz
rust-37c7d0ebb3ec5b62bd37df9ee8826194e3c6300a.zip
Auto merge of #43399 - tschottdorf:bndmode-pat-adjustments, r=nikomatsakis
default binding modes: add pat_binding_modes

This PR kicks off the implementation of the [default binding modes RFC][1] by
introducing the `pat_binding_modes` typeck table mentioned in the [mentoring
instructions][2].

It is a WIP because I wasn't able to avoid all uses of the binding modes as
not all call sites are close enough to the typeck tables. I added marker
comments to any line matching `BindByRef|BindByValue` so that reviewers
are aware of all of them.

I will look into changing the HIR (as suggested in [2]) to not carry a
`BindingMode` unless one was explicitly specified, but this PR is good for
a first round of comments.

The actual changes are quite small and CI will fail due to overlong lines
caused by the marker comments.

See #42640.

cc @nikomatsakis

[1]: https://github.com/rust-lang/rfcs/pull/2005
[2]: https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089
Diffstat (limited to 'src')
-rw-r--r--src/librustc/hir/lowering.rs22
-rw-r--r--src/librustc/hir/mod.rs27
-rw-r--r--src/librustc/hir/pat_util.rs40
-rw-r--r--src/librustc/hir/print.rs12
-rw-r--r--src/librustc/ich/impls_hir.rs8
-rw-r--r--src/librustc/ich/impls_ty.rs2
-rw-r--r--src/librustc/middle/expr_use_visitor.rs28
-rw-r--r--src/librustc/middle/mem_categorization.rs34
-rw-r--r--src/librustc/middle/region.rs26
-rw-r--r--src/librustc/ty/binding.rs35
-rw-r--r--src/librustc/ty/context.rs5
-rw-r--r--src/librustc/ty/mod.rs4
-rw-r--r--src/librustc_borrowck/borrowck/mod.rs11
-rw-r--r--src/librustc_const_eval/check_match.rs26
-rw-r--r--src/librustc_const_eval/pattern.rs20
-rw-r--r--src/librustc_lint/unused.rs8
-rw-r--r--src/librustc_resolve/lib.rs3
-rw-r--r--src/librustc_typeck/check/_match.rs68
-rw-r--r--src/librustc_typeck/check/mod.rs4
-rw-r--r--src/librustc_typeck/check/regionck.rs10
-rw-r--r--src/librustc_typeck/check/writeback.rs9
-rw-r--r--src/libsyntax/parse/mod.rs15
22 files changed, 311 insertions, 106 deletions
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 3ae3671b593..421a81c0d23 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -2191,7 +2191,7 @@ impl<'a> LoweringContext<'a> {
                 let next_ident = self.str_to_ident("__next");
                 let next_pat = self.pat_ident_binding_mode(e.span,
                                                            next_ident,
-                                                           hir::BindByValue(hir::MutMutable));
+                                                           hir::BindingAnnotation::Mutable);
 
                 // `::std::option::Option::Some(val) => next = val`
                 let pat_arm = {
@@ -2215,8 +2215,9 @@ impl<'a> LoweringContext<'a> {
                 };
 
                 // `mut iter`
-                let iter_pat = self.pat_ident_binding_mode(e.span, iter,
-                                                           hir::BindByValue(hir::MutMutable));
+                let iter_pat = self.pat_ident_binding_mode(e.span,
+                                                           iter,
+                                                           hir::BindingAnnotation::Mutable);
 
                 // `match ::std::iter::Iterator::next(&mut iter) { ... }`
                 let match_expr = {
@@ -2503,10 +2504,13 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
-    fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingMode {
+    fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingAnnotation {
         match *b {
-            BindingMode::ByRef(m) => hir::BindByRef(self.lower_mutability(m)),
-            BindingMode::ByValue(m) => hir::BindByValue(self.lower_mutability(m)),
+            BindingMode::ByValue(Mutability::Immutable) =>
+                hir::BindingAnnotation::Unannotated,
+            BindingMode::ByRef(Mutability::Immutable) => hir::BindingAnnotation::Ref,
+            BindingMode::ByValue(Mutability::Mutable) => hir::BindingAnnotation::Mutable,
+            BindingMode::ByRef(Mutability::Mutable) => hir::BindingAnnotation::RefMut,
         }
     }
 
@@ -2647,7 +2651,7 @@ impl<'a> LoweringContext<'a> {
     fn stmt_let(&mut self, sp: Span, mutbl: bool, ident: Name, ex: P<hir::Expr>)
                 -> (hir::Stmt, NodeId) {
         let pat = if mutbl {
-            self.pat_ident_binding_mode(sp, ident, hir::BindByValue(hir::MutMutable))
+            self.pat_ident_binding_mode(sp, ident, hir::BindingAnnotation::Mutable)
         } else {
             self.pat_ident(sp, ident)
         };
@@ -2703,10 +2707,10 @@ impl<'a> LoweringContext<'a> {
     }
 
     fn pat_ident(&mut self, span: Span, name: Name) -> P<hir::Pat> {
-        self.pat_ident_binding_mode(span, name, hir::BindByValue(hir::MutImmutable))
+        self.pat_ident_binding_mode(span, name, hir::BindingAnnotation::Unannotated)
     }
 
-    fn pat_ident_binding_mode(&mut self, span: Span, name: Name, bm: hir::BindingMode)
+    fn pat_ident_binding_mode(&mut self, span: Span, name: Name, bm: hir::BindingAnnotation)
                               -> P<hir::Pat> {
         let id = self.next_id();
         let parent_def = self.parent_def.unwrap();
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index a3a133daa09..7f1d1480d46 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -10,7 +10,6 @@
 
 // The Rust HIR.
 
-pub use self::BindingMode::*;
 pub use self::BinOp_::*;
 pub use self::BlockCheckMode::*;
 pub use self::CaptureClause::*;
@@ -628,10 +627,28 @@ pub struct FieldPat {
     pub is_shorthand: bool,
 }
 
+/// Explicit binding annotations given in the HIR for a binding. Note
+/// that this is not the final binding *mode* that we infer after type
+/// inference.
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
-pub enum BindingMode {
-    BindByRef(Mutability),
-    BindByValue(Mutability),
+pub enum BindingAnnotation {
+  /// No binding annotation given: this means that the final binding mode
+  /// will depend on whether we have skipped through a `&` reference
+  /// when matching. For example, the `x` in `Some(x)` will have binding
+  /// mode `None`; if you do `let Some(x) = &Some(22)`, it will
+  /// ultimately be inferred to be by-reference.
+  ///
+  /// Note that implicit reference skipping is not implemented yet (#42640).
+  Unannotated,
+
+  /// Annotated with `mut x` -- could be either ref or not, similar to `None`.
+  Mutable,
+
+  /// Annotated as `ref`, like `ref x`
+  Ref,
+
+  /// Annotated as `ref mut x`.
+  RefMut,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -647,7 +664,7 @@ pub enum PatKind {
 
     /// A fresh binding `ref mut binding @ OPT_SUBPATTERN`.
     /// The `DefId` is for the definition of the variable being bound.
-    Binding(BindingMode, DefId, Spanned<Name>, Option<P<Pat>>),
+    Binding(BindingAnnotation, DefId, Spanned<Name>, Option<P<Pat>>),
 
     /// A struct or struct variant pattern, e.g. `Variant {x, y, ..}`.
     /// The `bool` is `true` in the presence of a `..`.
diff --git a/src/librustc/hir/pat_util.rs b/src/librustc/hir/pat_util.rs
index 0190e74df69..144cb34ee35 100644
--- a/src/librustc/hir/pat_util.rs
+++ b/src/librustc/hir/pat_util.rs
@@ -87,7 +87,7 @@ impl hir::Pat {
     /// Call `f` on every "binding" in a pattern, e.g., on `a` in
     /// `match foo() { Some(a) => (), None => () }`
     pub fn each_binding<F>(&self, mut f: F)
-        where F: FnMut(hir::BindingMode, ast::NodeId, Span, &Spanned<ast::Name>),
+        where F: FnMut(hir::BindingAnnotation, ast::NodeId, Span, &Spanned<ast::Name>),
     {
         self.walk(|p| {
             if let PatKind::Binding(binding_mode, _, ref pth, _) = p.node {
@@ -130,12 +130,10 @@ impl hir::Pat {
 
     pub fn simple_name(&self) -> Option<ast::Name> {
         match self.node {
-            PatKind::Binding(hir::BindByValue(..), _, ref path1, None) => {
-                Some(path1.node)
-            }
-            _ => {
-                None
-            }
+            PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ref path1, None) |
+            PatKind::Binding(hir::BindingAnnotation::Mutable, _, ref path1, None) =>
+                Some(path1.node),
+            _ => None,
         }
     }
 
@@ -163,16 +161,22 @@ impl hir::Pat {
     }
 
     /// Checks if the pattern contains any `ref` or `ref mut` bindings,
-    /// and if yes whether its containing mutable ones or just immutables ones.
-    pub fn contains_ref_binding(&self) -> Option<hir::Mutability> {
+    /// and if yes whether it contains mutable or just immutables ones.
+    ///
+    /// FIXME(tschottdorf): this is problematic as the HIR is being scraped,
+    /// but ref bindings may be implicit after #42640.
+    pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
         let mut result = None;
-        self.each_binding(|mode, _, _, _| {
-            if let hir::BindingMode::BindByRef(m) = mode {
-                // Pick Mutable as maximum
-                match result {
-                    None | Some(hir::MutImmutable) => result = Some(m),
-                    _ => (),
+        self.each_binding(|annotation, _, _, _| {
+            match annotation {
+                hir::BindingAnnotation::Ref => {
+                    match result {
+                        None | Some(hir::MutImmutable) => result = Some(hir::MutImmutable),
+                        _ => (),
+                    }
                 }
+                hir::BindingAnnotation::RefMut => result = Some(hir::MutMutable),
+                _ => (),
             }
         });
         result
@@ -182,9 +186,11 @@ impl hir::Pat {
 impl hir::Arm {
     /// Checks if the patterns for this arm contain any `ref` or `ref mut`
     /// bindings, and if yes whether its containing mutable ones or just immutables ones.
-    pub fn contains_ref_binding(&self) -> Option<hir::Mutability> {
+    pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
+        // FIXME(tschottdorf): contains_explicit_ref_binding() must be removed
+        // for #42640.
         self.pats.iter()
-                 .filter_map(|pat| pat.contains_ref_binding())
+                 .filter_map(|pat| pat.contains_explicit_ref_binding())
                  .max_by_key(|m| match *m {
                     hir::MutMutable => 1,
                     hir::MutImmutable => 0,
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index beaf65b77d8..abfb00a24a1 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -1651,12 +1651,16 @@ impl<'a> State<'a> {
             PatKind::Wild => self.s.word("_")?,
             PatKind::Binding(binding_mode, _, ref path1, ref sub) => {
                 match binding_mode {
-                    hir::BindByRef(mutbl) => {
+                    hir::BindingAnnotation::Ref => {
                         self.word_nbsp("ref")?;
-                        self.print_mutability(mutbl)?;
+                        self.print_mutability(hir::MutImmutable)?;
                     }
-                    hir::BindByValue(hir::MutImmutable) => {}
-                    hir::BindByValue(hir::MutMutable) => {
+                    hir::BindingAnnotation::RefMut => {
+                        self.word_nbsp("ref")?;
+                        self.print_mutability(hir::MutMutable)?;
+                    }
+                    hir::BindingAnnotation::Unannotated => {}
+                    hir::BindingAnnotation::Mutable => {
                         self.word_nbsp("mut")?;
                     }
                 }
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs
index 7805029a67f..b344084f580 100644
--- a/src/librustc/ich/impls_hir.rs
+++ b/src/librustc/ich/impls_hir.rs
@@ -442,9 +442,11 @@ impl_stable_hash_for!(struct hir::FieldPat {
     is_shorthand
 });
 
-impl_stable_hash_for!(enum hir::BindingMode {
-    BindByRef(mutability),
-    BindByValue(mutability)
+impl_stable_hash_for!(enum hir::BindingAnnotation {
+    Unannotated,
+    Mutable,
+    Ref,
+    RefMut
 });
 
 impl_stable_hash_for!(enum hir::RangeEnd {
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index 42e4338ca30..5269ec90def 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -616,6 +616,7 @@ for ty::TypeckTables<'tcx> {
             ref node_types,
             ref node_substs,
             ref adjustments,
+            ref pat_binding_modes,
             ref upvar_capture_map,
             ref closure_tys,
             ref closure_kinds,
@@ -636,6 +637,7 @@ for ty::TypeckTables<'tcx> {
             ich::hash_stable_nodemap(hcx, hasher, node_types);
             ich::hash_stable_nodemap(hcx, hasher, node_substs);
             ich::hash_stable_nodemap(hcx, hasher, adjustments);
+            ich::hash_stable_nodemap(hcx, hasher, pat_binding_modes);
             ich::hash_stable_hashmap(hcx, hasher, upvar_capture_map, |hcx, up_var_id| {
                 let ty::UpvarId {
                     var_id,
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 259bd4f0999..87e933e85e2 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -796,16 +796,19 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
         debug!("determine_pat_move_mode cmt_discr={:?} pat={:?}", cmt_discr,
                pat);
         return_if_err!(self.mc.cat_pattern(cmt_discr, pat, |cmt_pat, pat| {
-            match pat.node {
-                PatKind::Binding(hir::BindByRef(..), ..) =>
-                    mode.lub(BorrowingMatch),
-                PatKind::Binding(hir::BindByValue(..), ..) => {
-                    match copy_or_move(&self.mc, self.param_env, &cmt_pat, PatBindingMove) {
-                        Copy => mode.lub(CopyingMatch),
-                        Move(..) => mode.lub(MovingMatch),
+            if let PatKind::Binding(..) = pat.node {
+                let bm = *self.mc.tables.pat_binding_modes.get(&pat.id)
+                                                          .expect("missing binding mode");
+                match bm {
+                    ty::BindByReference(..) =>
+                        mode.lub(BorrowingMatch),
+                    ty::BindByValue(..) => {
+                        match copy_or_move(&self.mc, self.param_env, &cmt_pat, PatBindingMove) {
+                            Copy => mode.lub(CopyingMatch),
+                            Move(..) => mode.lub(MovingMatch),
+                        }
                     }
                 }
-                _ => {}
             }
         }));
     }
@@ -818,8 +821,9 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
 
         let ExprUseVisitor { ref mc, ref mut delegate, param_env } = *self;
         return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |cmt_pat, pat| {
-            if let PatKind::Binding(bmode, def_id, ..) = pat.node {
+            if let PatKind::Binding(_, def_id, ..) = pat.node {
                 debug!("binding cmt_pat={:?} pat={:?} match_mode={:?}", cmt_pat, pat, match_mode);
+                let bm = *mc.tables.pat_binding_modes.get(&pat.id).expect("missing binding mode");
 
                 // pat_ty: the type of the binding being produced.
                 let pat_ty = return_if_err!(mc.node_ty(pat.id));
@@ -832,14 +836,14 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                 }
 
                 // It is also a borrow or copy/move of the value being matched.
-                match bmode {
-                    hir::BindByRef(m) => {
+                match bm {
+                    ty::BindByReference(m) => {
                         if let ty::TyRef(r, _) = pat_ty.sty {
                             let bk = ty::BorrowKind::from_mutbl(m);
                             delegate.borrow(pat.id, pat.span, cmt_pat, r, bk, RefBinding);
                         }
                     }
-                    hir::BindByValue(..) => {
+                    ty::BindByValue(..) => {
                         let mode = copy_or_move(mc, param_env, &cmt_pat, PatBindingMove);
                         debug!("walk_pat binding consuming pat");
                         delegate.consume_pat(pat, cmt_pat, mode);
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 557d4b24f30..b4993aafc4c 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -330,11 +330,12 @@ impl MutabilityCategory {
         ret
     }
 
-    fn from_local(tcx: TyCtxt, id: ast::NodeId) -> MutabilityCategory {
+    fn from_local(tcx: TyCtxt, tables: &ty::TypeckTables, id: ast::NodeId) -> MutabilityCategory {
         let ret = match tcx.hir.get(id) {
             hir_map::NodeLocal(p) => match p.node {
-                PatKind::Binding(bind_mode, ..) => {
-                    if bind_mode == hir::BindByValue(hir::MutMutable) {
+                PatKind::Binding(..) => {
+                    let bm = *tables.pat_binding_modes.get(&p.id).expect("missing binding mode");
+                    if bm == ty::BindByValue(hir::MutMutable) {
                         McDeclared
                     } else {
                         McImmutable
@@ -475,16 +476,21 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
         // *being borrowed* is.  But ideally we would put in a more
         // fundamental fix to this conflated use of the node id.
         let ret_ty = match pat.node {
-            PatKind::Binding(hir::BindByRef(_), ..) => {
-                // a bind-by-ref means that the base_ty will be the type of the ident itself,
-                // but what we want here is the type of the underlying value being borrowed.
-                // So peel off one-level, turning the &T into T.
-                match base_ty.builtin_deref(false, ty::NoPreference) {
-                    Some(t) => t.ty,
-                    None => {
-                        debug!("By-ref binding of non-derefable type {:?}", base_ty);
-                        return Err(());
+            PatKind::Binding(..) => {
+                let bm = *self.tables.pat_binding_modes.get(&pat.id).expect("missing binding mode");
+                if let ty::BindByReference(_) = bm {
+                    // a bind-by-ref means that the base_ty will be the type of the ident itself,
+                    // but what we want here is the type of the underlying value being borrowed.
+                    // So peel off one-level, turning the &T into T.
+                    match base_ty.builtin_deref(false, ty::NoPreference) {
+                        Some(t) => t.ty,
+                        None => {
+                            debug!("By-ref binding of non-derefable type {:?}", base_ty);
+                            return Err(());
+                        }
                     }
+                } else {
+                    base_ty
                 }
             }
             _ => base_ty,
@@ -659,7 +665,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
                 id,
                 span,
                 cat: Categorization::Local(vid),
-                mutbl: MutabilityCategory::from_local(self.tcx, vid),
+                mutbl: MutabilityCategory::from_local(self.tcx, self.tables, vid),
                 ty: expr_ty,
                 note: NoteNone
             }))
@@ -711,7 +717,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
         let var_ty = self.node_ty(var_id)?;
 
         // Mutability of original variable itself
-        let var_mutbl = MutabilityCategory::from_local(self.tcx, var_id);
+        let var_mutbl = MutabilityCategory::from_local(self.tcx, self.tables, var_id);
 
         // Construct the upvar. This represents access to the field
         // from the environment (perhaps we should eventually desugar
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index 39cb5d1b8c8..9133a5e777d 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -889,8 +889,32 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
     ///        | ( ..., P&, ... )
     ///        | box P&
     fn is_binding_pat(pat: &hir::Pat) -> bool {
+        // Note that the code below looks for *explicit* refs only, that is, it won't
+        // know about *implicit* refs as introduced in #42640.
+        //
+        // This is not a problem. For example, consider
+        //
+        //      let (ref x, ref y) = (Foo { .. }, Bar { .. });
+        //
+        // Due to the explicit refs on the left hand side, the below code would signal
+        // that the temporary value on the right hand side should live until the end of
+        // the enclosing block (as opposed to being dropped after the let is complete).
+        //
+        // To create an implicit ref, however, you must have a borrowed value on the RHS
+        // already, as in this example (which won't compile before #42640):
+        //
+        //      let Foo { x, .. } = &Foo { x: ..., ... };
+        //
+        // in place of
+        //
+        //      let Foo { ref x, .. } = Foo { ... };
+        //
+        // In the former case (the implicit ref version), the temporary is created by the
+        // & expression, and its lifetime would be extended to the end of the block (due
+        // to a different rule, not the below code).
         match pat.node {
-            PatKind::Binding(hir::BindByRef(_), ..) => true,
+            PatKind::Binding(hir::BindingAnnotation::Ref, ..) |
+            PatKind::Binding(hir::BindingAnnotation::RefMut, ..) => true,
 
             PatKind::Struct(_, ref field_pats, _) => {
                 field_pats.iter().any(|fp| is_binding_pat(&fp.node.pat))
diff --git a/src/librustc/ty/binding.rs b/src/librustc/ty/binding.rs
new file mode 100644
index 00000000000..3db61b76cc5
--- /dev/null
+++ b/src/librustc/ty/binding.rs
@@ -0,0 +1,35 @@
+// Copyright 2017 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.
+
+use hir::BindingAnnotation::*;
+use hir::BindingAnnotation;
+use hir::Mutability;
+
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
+pub enum BindingMode {
+    BindByReference(Mutability),
+    BindByValue(Mutability),
+}
+
+impl BindingMode {
+    pub fn convert(ba: BindingAnnotation) -> BindingMode {
+        match ba {
+            Unannotated => BindingMode::BindByValue(Mutability::MutImmutable),
+            Mutable => BindingMode::BindByValue(Mutability::MutMutable),
+            Ref => BindingMode::BindByReference(Mutability::MutImmutable),
+            RefMut => BindingMode::BindByReference(Mutability::MutMutable),
+        }
+    }
+}
+
+impl_stable_hash_for!(enum self::BindingMode {
+    BindByReference(mutability),
+    BindByValue(mutability)
+});
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 45ddd4c0ff1..be3cd99426d 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -40,6 +40,7 @@ use ty::layout::{Layout, TargetDataLayout};
 use ty::inhabitedness::DefIdForest;
 use ty::maps;
 use ty::steal::Steal;
+use ty::BindingMode;
 use util::nodemap::{NodeMap, NodeSet, DefIdSet};
 use util::nodemap::{FxHashMap, FxHashSet};
 use rustc_data_structures::accumulate_vec::AccumulateVec;
@@ -223,6 +224,9 @@ pub struct TypeckTables<'tcx> {
 
     pub adjustments: NodeMap<Vec<ty::adjustment::Adjustment<'tcx>>>,
 
+    // Stores the actual binding mode for all instances of hir::BindingAnnotation.
+    pub pat_binding_modes: NodeMap<BindingMode>,
+
     /// Borrows
     pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>,
 
@@ -274,6 +278,7 @@ impl<'tcx> TypeckTables<'tcx> {
             node_types: FxHashMap(),
             node_substs: NodeMap(),
             adjustments: NodeMap(),
+            pat_binding_modes: NodeMap(),
             upvar_capture_map: FxHashMap(),
             closure_tys: NodeMap(),
             closure_kinds: NodeMap(),
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 2ee7149fc13..e0b0aca1261 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -73,6 +73,9 @@ pub use self::sty::InferTy::*;
 pub use self::sty::RegionKind::*;
 pub use self::sty::TypeVariants::*;
 
+pub use self::binding::BindingMode;
+pub use self::binding::BindingMode::*;
+
 pub use self::context::{TyCtxt, GlobalArenas, tls};
 pub use self::context::{Lift, TypeckTables};
 
@@ -83,6 +86,7 @@ pub use self::trait_def::TraitDef;
 pub use self::maps::queries;
 
 pub mod adjustment;
+pub mod binding;
 pub mod cast;
 pub mod error;
 pub mod fast_reject;
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 1bfc5805bc8..0124a77349c 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -871,14 +871,15 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
         }
     }
 
-    fn local_binding_mode(&self, node_id: ast::NodeId) -> hir::BindingMode {
+    fn local_binding_mode(&self, node_id: ast::NodeId) -> ty::BindingMode {
         let pat = match self.tcx.hir.get(node_id) {
             hir_map::Node::NodeLocal(pat) => pat,
             node => bug!("bad node for local: {:?}", node)
         };
 
         match pat.node {
-            hir::PatKind::Binding(mode, ..) => mode,
+            hir::PatKind::Binding(..) =>
+                *self.tables.pat_binding_modes.get(&pat.id).expect("missing binding mode"),
             _ => bug!("local is not a binding: {:?}", pat)
         }
     }
@@ -913,7 +914,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             Some(ImmutabilityBlame::ClosureEnv(_)) => {}
             Some(ImmutabilityBlame::ImmLocal(node_id)) => {
                 let let_span = self.tcx.hir.span(node_id);
-                if let hir::BindingMode::BindByValue(..) = self.local_binding_mode(node_id) {
+                if let ty::BindByValue(..) = self.local_binding_mode(node_id) {
                     if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(let_span) {
                         let (_, is_implicit_self) = self.local_ty(node_id);
                         if is_implicit_self && snippet != "self" {
@@ -930,7 +931,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             Some(ImmutabilityBlame::LocalDeref(node_id)) => {
                 let let_span = self.tcx.hir.span(node_id);
                 match self.local_binding_mode(node_id) {
-                    hir::BindingMode::BindByRef(..) => {
+                    ty::BindByReference(..) => {
                         let snippet = self.tcx.sess.codemap().span_to_snippet(let_span);
                         if let Ok(snippet) = snippet {
                             db.span_label(
@@ -940,7 +941,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                             );
                         }
                     }
-                    hir::BindingMode::BindByValue(..) => {
+                    ty::BindByValue(..) => {
                         if let (Some(local_ty), is_implicit_self) = self.local_ty(node_id) {
                             if let Some(msg) =
                                  self.suggest_mut_for_immutable(local_ty, is_implicit_self) {
diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs
index 95c8613232e..060ff503d4e 100644
--- a/src/librustc_const_eval/check_match.rs
+++ b/src/librustc_const_eval/check_match.rs
@@ -268,7 +268,12 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
 
 fn check_for_bindings_named_the_same_as_variants(cx: &MatchVisitor, pat: &Pat) {
     pat.walk(|p| {
-        if let PatKind::Binding(hir::BindByValue(hir::MutImmutable), _, name, None) = p.node {
+        if let PatKind::Binding(_, _, name, None) = p.node {
+            let bm = *cx.tables.pat_binding_modes.get(&p.id).expect("missing binding mode");
+            if bm != ty::BindByValue(hir::MutImmutable) {
+                // Nothing to check.
+                return true;
+            }
             let pat_ty = cx.tables.pat_ty(p);
             if let ty::TyAdt(edef, _) = pat_ty.sty {
                 if edef.is_enum() && edef.variants.iter().any(|variant| {
@@ -452,8 +457,9 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
                                    pats: &[P<Pat>]) {
     let mut by_ref_span = None;
     for pat in pats {
-        pat.each_binding(|bm, _, span, _path| {
-            if let hir::BindByRef(..) = bm {
+        pat.each_binding(|_, id, span, _path| {
+            let bm = *cx.tables.pat_binding_modes.get(&id).expect("missing binding mode");
+            if let ty::BindByReference(..) = bm {
                 by_ref_span = Some(span);
             }
         })
@@ -484,10 +490,16 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
 
     for pat in pats {
         pat.walk(|p| {
-            if let PatKind::Binding(hir::BindByValue(..), _, _, ref sub) = p.node {
-                let pat_ty = cx.tables.node_id_to_type(p.id);
-                if pat_ty.moves_by_default(cx.tcx, cx.param_env, pat.span) {
-                    check_move(p, sub.as_ref().map(|p| &**p));
+            if let PatKind::Binding(_, _, _, ref sub) = p.node {
+                let bm = *cx.tables.pat_binding_modes.get(&p.id).expect("missing binding mode");
+                match bm {
+                    ty::BindByValue(..) => {
+                        let pat_ty = cx.tables.node_id_to_type(p.id);
+                        if pat_ty.moves_by_default(cx.tcx, cx.param_env, pat.span) {
+                            check_move(p, sub.as_ref().map(|p| &**p));
+                        }
+                    }
+                    _ => {}
                 }
             }
             true
diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs
index ab919da8152..f37a112a596 100644
--- a/src/librustc_const_eval/pattern.rs
+++ b/src/librustc_const_eval/pattern.rs
@@ -374,27 +374,31 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
                 }
             }
 
-            PatKind::Binding(bm, def_id, ref ident, ref sub) => {
+            PatKind::Binding(_, def_id, ref ident, ref sub) => {
                 let id = self.tcx.hir.as_local_node_id(def_id).unwrap();
                 let var_ty = self.tables.node_id_to_type(pat.id);
                 let region = match var_ty.sty {
                     ty::TyRef(r, _) => Some(r),
                     _ => None,
                 };
+                let bm = *self.tables.pat_binding_modes.get(&pat.id)
+                                                       .expect("missing binding mode");
                 let (mutability, mode) = match bm {
-                    hir::BindByValue(hir::MutMutable) =>
+                    ty::BindByValue(hir::MutMutable) =>
                         (Mutability::Mut, BindingMode::ByValue),
-                    hir::BindByValue(hir::MutImmutable) =>
+                    ty::BindByValue(hir::MutImmutable) =>
                         (Mutability::Not, BindingMode::ByValue),
-                    hir::BindByRef(hir::MutMutable) =>
-                        (Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Mut)),
-                    hir::BindByRef(hir::MutImmutable) =>
-                        (Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Shared)),
+                    ty::BindByReference(hir::MutMutable) =>
+                        (Mutability::Not, BindingMode::ByRef(
+                            region.unwrap(), BorrowKind::Mut)),
+                    ty::BindByReference(hir::MutImmutable) =>
+                        (Mutability::Not, BindingMode::ByRef(
+                            region.unwrap(), BorrowKind::Shared)),
                 };
 
                 // A ref x pattern is the same node used for x, and as such it has
                 // x's type, which is &T, where we want T (the type being matched).
-                if let hir::BindByRef(_) = bm {
+                if let ty::BindByReference(_) = bm {
                     if let ty::TyRef(_, mt) = ty.sty {
                         ty = mt.ty;
                     } else {
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index 473c0f3ffda..d7d0dc7cb35 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -44,9 +44,13 @@ impl UnusedMut {
 
         let mut mutables = FxHashMap();
         for p in pats {
-            p.each_binding(|mode, id, _, path1| {
+            p.each_binding(|_, id, span, path1| {
+                let bm = match cx.tables.pat_binding_modes.get(&id) {
+                    Some(&bm) => bm,
+                    None => span_bug!(span, "missing binding mode"),
+                };
                 let name = path1.node;
-                if let hir::BindByValue(hir::MutMutable) = mode {
+                if let ty::BindByValue(hir::MutMutable) = bm {
                     if !name.as_str().starts_with("_") {
                         match mutables.entry(name) {
                             Vacant(entry) => {
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 88013b45a05..349a21af895 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -2277,8 +2277,9 @@ impl<'a> Resolver<'a> {
                                                                       false, pat.span)
                                       .and_then(LexicalScopeBinding::item);
                     let resolution = binding.map(NameBinding::def).and_then(|def| {
+                        let ivmode = BindingMode::ByValue(Mutability::Immutable);
                         let always_binding = !pat_src.is_refutable() || opt_pat.is_some() ||
-                                             bmode != BindingMode::ByValue(Mutability::Immutable);
+                                             bmode != ivmode;
                         match def {
                             Def::StructCtor(_, CtorKind::Const) |
                             Def::VariantCtor(_, CtorKind::Const) |
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 68726a7b1c4..eaff8e7b8ac 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -113,10 +113,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 self.demand_eqtype(pat.span, expected, rhs_ty);
                 common_type
             }
-            PatKind::Binding(bm, def_id, _, ref sub) => {
+            PatKind::Binding(ba, def_id, _, ref sub) => {
+                // Note the binding mode in the typeck tables. For now, what we store is always
+                // identical to what could be scraped from the HIR, but this will change with
+                // default binding modes (#42640).
+                let bm = ty::BindingMode::convert(ba);
+                self.inh.tables.borrow_mut().pat_binding_modes.insert(pat.id, bm);
+
                 let typ = self.local_ty(pat.span, pat.id);
                 match bm {
-                    hir::BindByRef(mutbl) => {
+                    ty::BindByReference(mutbl) => {
                         // if the binding is like
                         //    ref x | ref const x | ref mut x
                         // then `x` is assigned a value of type `&M T` where M is the mutability
@@ -131,7 +137,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         self.demand_eqtype(pat.span, region_ty, typ);
                     }
                     // otherwise the type of x is the expected type T
-                    hir::BindByValue(_) => {
+                    ty::BindByValue(_) => {
                         // As above, `T <: typeof(x)` is required but we
                         // use equality, see (*) below.
                         self.demand_eqtype(pat.span, expected, typ);
@@ -396,11 +402,59 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                        match_src: hir::MatchSource) -> Ty<'tcx> {
         let tcx = self.tcx;
 
-        // Not entirely obvious: if matches may create ref bindings, we
-        // want to use the *precise* type of the discriminant, *not* some
-        // supertype, as the "discriminant type" (issue #23116).
+        // Not entirely obvious: if matches may create ref bindings, we want to
+        // use the *precise* type of the discriminant, *not* some supertype, as
+        // the "discriminant type" (issue #23116).
+        //
+        // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which
+        // is problematic as the HIR is being scraped, but ref bindings may be
+        // implicit after #42640. We need to make sure that pat_adjustments
+        // (once introduced) is populated by the time we get here.
+        //
+        // arielb1 [writes here in this comment thread][c] that there
+        // is certainly *some* potential danger, e.g. for an example
+        // like:
+        //
+        // [c]: https://github.com/rust-lang/rust/pull/43399#discussion_r130223956
+        //
+        // ```
+        // let Foo(x) = f()[0];
+        // ```
+        //
+        // Then if the pattern matches by reference, we want to match
+        // `f()[0]` as a lexpr, so we can't allow it to be
+        // coerced. But if the pattern matches by value, `f()[0]` is
+        // still syntactically a lexpr, but we *do* want to allow
+        // coercions.
+        //
+        // However, *likely* we are ok with allowing coercions to
+        // happen if there are no explicit ref mut patterns - all
+        // implicit ref mut patterns must occur behind a reference, so
+        // they will have the "correct" variance and lifetime.
+        //
+        // This does mean that the following pattern would be legal:
+        //
+        // ```
+        // struct Foo(Bar);
+        // struct Bar(u32);
+        // impl Deref for Foo {
+        //     type Target = Bar;
+        //     fn deref(&self) -> &Bar { &self.0 }
+        // }
+        // impl DerefMut for Foo {
+        //     fn deref_mut(&mut self) -> &mut Bar { &mut self.0 }
+        // }
+        // fn foo(x: &mut Foo) {
+        //     {
+        //         let Bar(z): &mut Bar = x;
+        //         *z = 42;
+        //     }
+        //     assert_eq!(foo.0.0, 42);
+        // }
+        // ```
+
         let contains_ref_bindings = arms.iter()
-                                        .filter_map(|a| a.contains_ref_binding())
+                                        .filter_map(|a| a.contains_explicit_ref_binding())
                                         .max_by_key(|m| match *m {
                                             hir::MutMutable => 1,
                                             hir::MutImmutable => 0,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index cb22dcc21de..17aae7dc04f 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3999,7 +3999,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                   local: &'gcx hir::Local,
                                   init: &'gcx hir::Expr) -> Ty<'tcx>
     {
-        let ref_bindings = local.pat.contains_ref_binding();
+        // FIXME(tschottdorf): contains_explicit_ref_binding() must be removed
+        // for #42640.
+        let ref_bindings = local.pat.contains_explicit_ref_binding();
 
         let local_ty = self.local_ty(init.span, local.id);
         if let Some(m) = ref_bindings {
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 82207428efc..9b7ecc194ca 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -1196,9 +1196,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
             mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, sub_pat| {
                 match sub_pat.node {
                     // `ref x` pattern
-                    PatKind::Binding(hir::BindByRef(mutbl), ..) => {
-                        self.link_region_from_node_type(sub_pat.span, sub_pat.id,
-                                                        mutbl, sub_cmt);
+                    PatKind::Binding(..) => {
+                        let bm = *mc.tables.pat_binding_modes.get(&sub_pat.id)
+                                                             .expect("missing binding mode");
+                        if let ty::BindByReference(mutbl) = bm {
+                            self.link_region_from_node_type(sub_pat.span, sub_pat.id,
+                                                            mutbl, sub_cmt);
+                        }
                     }
                     _ => {}
                 }
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 81e5dae5477..0a323efabec 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -178,6 +178,15 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> {
     }
 
     fn visit_pat(&mut self, p: &'gcx hir::Pat) {
+        match p.node {
+            hir::PatKind::Binding(..) => {
+                let bm = *self.fcx.tables.borrow().pat_binding_modes.get(&p.id)
+                                                                    .expect("missing binding mode");
+                self.tables.pat_binding_modes.insert(p.id, bm);
+            }
+            _ => {}
+        };
+
         self.visit_node_id(p.span, p.id);
         intravisit::walk_pat(self, p);
     }
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index 7b105a8fa14..893bada2670 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -867,13 +867,14 @@ mod tests {
                                     pat: P(ast::Pat {
                                         id: ast::DUMMY_NODE_ID,
                                         node: PatKind::Ident(
-                                            ast::BindingMode::ByValue(ast::Mutability::Immutable),
-                                                Spanned{
-                                                    span: sp(6,7),
-                                                    node: Ident::from_str("b")},
-                                                None
-                                                    ),
-                                            span: sp(6,7)
+                                            ast::BindingMode::ByValue(
+                                                ast::Mutability::Immutable),
+                                            Spanned{
+                                                span: sp(6,7),
+                                                node: Ident::from_str("b")},
+                                            None
+                                        ),
+                                        span: sp(6,7)
                                     }),
                                         id: ast::DUMMY_NODE_ID
                                     }],