about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-11-28 10:36:56 +0000
committerbors <bors@rust-lang.org>2019-11-28 10:36:56 +0000
commit4752c05af4a5f392de026f9ae1877eae030be359 (patch)
tree8444aeb3326abf880a4dedc27e5869ea40ac6674 /src
parent2539b5f157599fbcc005371a34897a42fa0a2421 (diff)
parent5bc15866d752d4ba4a02e9ab130b08cb8186d8f9 (diff)
Auto merge of #66246 - matthewjasper:simplify-mem-cat, r=pnkfelix
Simplify memory categorization

With AST borrowck gone, mem_categorization can be simplified, a lot.

* `cmt_` is now called `Place`. Most local variable names have been updated to reflect this, but the `cat_*` methods retain their names.
* `MemCategorizationContext` no longer needs a `ScopeTree` and always needs an `InferCtxt`.
* `Place` now uses a similar representation to `mir::Place` with a `Vec` of projections.
* `Upvar` places don't include the implicit environment and capture derefs. These are now handled by `regionck` when needed.
* Various types, methods and variants only used by AST borrowck have been removed.
* `ExprUseVisitor` now lives in `rustc_typeck::expr_use_visitor`.
* `MemCategorizationContext` and `Place` live in `rustc_typeck::mem_categorization`.
* `Place` is re-exported in `rustc_typeck::expr_use_visitor` so that Clippy can access it.

The loss of an error in `issue-4335.rs` is due to a change in capture inference in ill-formed programs. If any projection from a variable is moved from then we capture that variable by move, whether or not the place being moved from allows this.

Closes #66270
Diffstat (limited to 'src')
-rw-r--r--src/librustc/lib.rs2
-rw-r--r--src/librustc/middle/mem_categorization.rs1508
-rw-r--r--src/librustc_typeck/check/regionck.rs269
-rw-r--r--src/librustc_typeck/check/upvar.rs237
-rw-r--r--src/librustc_typeck/expr_use_visitor.rs (renamed from src/librustc/middle/expr_use_visitor.rs)193
-rw-r--r--src/librustc_typeck/lib.rs4
-rw-r--r--src/librustc_typeck/mem_categorization.rs707
-rw-r--r--src/test/ui/issues/issue-4335.rs3
-rw-r--r--src/test/ui/issues/issue-4335.stderr23
-rw-r--r--src/test/ui/regions/regions-addr-of-upvar-self.stderr2
-rw-r--r--src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr2
11 files changed, 993 insertions, 1957 deletions
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 1fc157eca3e..481ae3b9329 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -96,7 +96,6 @@ pub mod infer;
 pub mod lint;
 
 pub mod middle {
-    pub mod expr_use_visitor;
     pub mod cstore;
     pub mod dependency_format;
     pub mod diagnostic_items;
@@ -104,7 +103,6 @@ pub mod middle {
     pub mod free_region;
     pub mod lib_features;
     pub mod lang_items;
-    pub mod mem_categorization;
     pub mod privacy;
     pub mod reachable;
     pub mod region;
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
deleted file mode 100644
index 3510fe4d123..00000000000
--- a/src/librustc/middle/mem_categorization.rs
+++ /dev/null
@@ -1,1508 +0,0 @@
-//! # Categorization
-//!
-//! The job of the categorization module is to analyze an expression to
-//! determine what kind of memory is used in evaluating it (for example,
-//! where dereferences occur and what kind of pointer is dereferenced;
-//! whether the memory is mutable, etc.).
-//!
-//! Categorization effectively transforms all of our expressions into
-//! expressions of the following forms (the actual enum has many more
-//! possibilities, naturally, but they are all variants of these base
-//! forms):
-//!
-//!     E = rvalue    // some computed rvalue
-//!       | x         // address of a local variable or argument
-//!       | *E        // deref of a ptr
-//!       | E.comp    // access to an interior component
-//!
-//! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an
-//! address where the result is to be found. If Expr is a place, then this
-//! is the address of the place. If `Expr` is an rvalue, this is the address of
-//! some temporary spot in memory where the result is stored.
-//!
-//! Now, `cat_expr()` classifies the expression `Expr` and the address `A = ToAddr(Expr)`
-//! as follows:
-//!
-//! - `cat`: what kind of expression was this? This is a subset of the
-//!   full expression forms which only includes those that we care about
-//!   for the purpose of the analysis.
-//! - `mutbl`: mutability of the address `A`.
-//! - `ty`: the type of data found at the address `A`.
-//!
-//! The resulting categorization tree differs somewhat from the expressions
-//! themselves. For example, auto-derefs are explicit. Also, an index a[b] is
-//! decomposed into two operations: a dereference to reach the array data and
-//! then an index to jump forward to the relevant item.
-//!
-//! ## By-reference upvars
-//!
-//! One part of the codegen which may be non-obvious is that we translate
-//! closure upvars into the dereference of a borrowed pointer; this more closely
-//! resembles the runtime codegen. So, for example, if we had:
-//!
-//!     let mut x = 3;
-//!     let y = 5;
-//!     let inc = || x += y;
-//!
-//! Then when we categorize `x` (*within* the closure) we would yield a
-//! result of `*x'`, effectively, where `x'` is a `Categorization::Upvar` reference
-//! tied to `x`. The type of `x'` will be a borrowed pointer.
-
-#![allow(non_camel_case_types)]
-
-pub use self::PointerKind::*;
-pub use self::InteriorKind::*;
-pub use self::MutabilityCategory::*;
-pub use self::AliasableReason::*;
-pub use self::Note::*;
-
-use self::Aliasability::*;
-
-use crate::middle::region;
-use crate::hir::def_id::{DefId, LocalDefId};
-use crate::hir::Node;
-use crate::infer::InferCtxt;
-use crate::hir::def::{CtorOf, Res, DefKind, CtorKind};
-use crate::ty::adjustment;
-use crate::ty::{self, DefIdTree, Ty, TyCtxt};
-use crate::ty::fold::TypeFoldable;
-
-use crate::hir::{Mutability, PatKind};
-use crate::hir::pat_util::EnumerateAndAdjustIterator;
-use crate::hir;
-use syntax::ast::{self, Name};
-use syntax::symbol::sym;
-use syntax_pos::Span;
-
-use std::borrow::Cow;
-use std::fmt;
-use std::hash::{Hash, Hasher};
-use rustc_data_structures::fx::FxIndexMap;
-use std::rc::Rc;
-
-#[derive(Clone, Debug, PartialEq)]
-pub enum Categorization<'tcx> {
-    Rvalue,                              // temporary val
-    ThreadLocal,                         // value that cannot move, but still restricted in scope
-    StaticItem,
-    Upvar(Upvar),                        // upvar referenced by closure env
-    Local(hir::HirId),                   // local variable
-    Deref(cmt<'tcx>, PointerKind<'tcx>), // deref of a ptr
-    Interior(cmt<'tcx>, InteriorKind),   // something interior: field, tuple, etc
-    Downcast(cmt<'tcx>, DefId),          // selects a particular enum variant (*1)
-
-    // (*1) downcast is only required if the enum has more than one variant
-}
-
-// Represents any kind of upvar
-#[derive(Clone, Copy, PartialEq)]
-pub struct Upvar {
-    pub id: ty::UpvarId,
-    pub kind: ty::ClosureKind
-}
-
-// different kinds of pointers:
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum PointerKind<'tcx> {
-    /// `Box<T>`
-    Unique,
-
-    /// `&T`
-    BorrowedPtr(ty::BorrowKind, ty::Region<'tcx>),
-
-    /// `*T`
-    UnsafePtr(hir::Mutability),
-}
-
-// We use the term "interior" to mean "something reachable from the
-// base without a pointer dereference", e.g., a field
-#[derive(Clone, PartialEq)]
-pub enum InteriorKind {
-    InteriorField(FieldIndex),
-    InteriorElement(InteriorOffsetKind),
-}
-
-// Contains index of a field that is actually used for loan path comparisons and
-// string representation of the field that should be used only for diagnostics.
-#[derive(Clone, Copy, Eq)]
-pub struct FieldIndex(pub usize, pub Name);
-
-impl PartialEq for FieldIndex {
-    fn eq(&self, rhs: &Self) -> bool {
-        self.0 == rhs.0
-    }
-}
-
-impl Hash for FieldIndex {
-    fn hash<H: Hasher>(&self, h: &mut H) {
-        self.0.hash(h)
-    }
-}
-
-#[derive(Clone, PartialEq)]
-pub enum InteriorOffsetKind {
-    Index,   // e.g., `array_expr[index_expr]`
-    Pattern, // e.g., `fn foo([_, a, _, _]: [A; 4]) { ... }`
-}
-
-#[derive(Clone, Copy, PartialEq, Debug)]
-pub enum MutabilityCategory {
-    McImmutable, // Immutable.
-    McDeclared,  // Directly declared as mutable.
-    McInherited, // Inherited from the fact that owner is mutable.
-}
-
-// A note about the provenance of a `cmt`.  This is used for
-// special-case handling of upvars such as mutability inference.
-// Upvar categorization can generate a variable number of nested
-// derefs.  The note allows detecting them without deep pattern
-// matching on the categorization.
-#[derive(Clone, Copy, PartialEq, Debug)]
-pub enum Note {
-    NoteClosureEnv(ty::UpvarId), // Deref through closure env
-    NoteUpvarRef(ty::UpvarId),   // Deref through by-ref upvar
-    NoteIndex,                   // Deref as part of desugaring `x[]` into its two components
-    NoteNone                     // Nothing special
-}
-
-// `cmt`: "Category, Mutability, and Type".
-//
-// a complete categorization of a value indicating where it originated
-// and how it is located, as well as the mutability of the memory in
-// which the value is stored.
-//
-// *WARNING* The field `cmt.type` is NOT necessarily the same as the
-// result of `node_type(cmt.id)`.
-//
-// (FIXME: rewrite the following comment given that `@x` managed
-// pointers have been obsolete for quite some time.)
-//
-// This is because the `id` is always the `id` of the node producing the
-// type; in an expression like `*x`, the type of this deref node is the
-// deref'd type (`T`), but in a pattern like `@x`, the `@x` pattern is
-// again a dereference, but its type is the type *before* the
-// dereference (`@T`). So use `cmt.ty` to find the type of the value in
-// a consistent fashion. For more details, see the method `cat_pattern`
-#[derive(Clone, Debug, PartialEq)]
-pub struct cmt_<'tcx> {
-    pub hir_id: hir::HirId,        // HIR id of expr/pat producing this value
-    pub span: Span,                // span of same expr/pat
-    pub cat: Categorization<'tcx>, // categorization of expr
-    pub mutbl: MutabilityCategory, // mutability of expr as place
-    pub ty: Ty<'tcx>,              // type of the expr (*see WARNING above*)
-    pub note: Note,                // Note about the provenance of this cmt
-}
-
-pub type cmt<'tcx> = Rc<cmt_<'tcx>>;
-
-pub trait HirNode {
-    fn hir_id(&self) -> hir::HirId;
-    fn span(&self) -> Span;
-}
-
-impl HirNode for hir::Expr {
-    fn hir_id(&self) -> hir::HirId { self.hir_id }
-    fn span(&self) -> Span { self.span }
-}
-
-impl HirNode for hir::Pat {
-    fn hir_id(&self) -> hir::HirId { self.hir_id }
-    fn span(&self) -> Span { self.span }
-}
-
-#[derive(Clone)]
-pub struct MemCategorizationContext<'a, 'tcx> {
-    pub tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    pub body_owner: DefId,
-    pub upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
-    pub region_scope_tree: &'a region::ScopeTree,
-    pub tables: &'a ty::TypeckTables<'tcx>,
-    infcx: Option<&'a InferCtxt<'a, 'tcx>>,
-}
-
-pub type McResult<T> = Result<T, ()>;
-
-impl MutabilityCategory {
-    pub fn from_mutbl(m: hir::Mutability) -> MutabilityCategory {
-        let ret = match m {
-            Mutability::Immutable => McImmutable,
-            Mutability::Mutable => McDeclared
-        };
-        debug!("MutabilityCategory::{}({:?}) => {:?}",
-               "from_mutbl", m, ret);
-        ret
-    }
-
-    pub fn from_borrow_kind(borrow_kind: ty::BorrowKind) -> MutabilityCategory {
-        let ret = match borrow_kind {
-            ty::ImmBorrow => McImmutable,
-            ty::UniqueImmBorrow => McImmutable,
-            ty::MutBorrow => McDeclared,
-        };
-        debug!("MutabilityCategory::{}({:?}) => {:?}",
-               "from_borrow_kind", borrow_kind, ret);
-        ret
-    }
-
-    fn from_pointer_kind(base_mutbl: MutabilityCategory,
-                         ptr: PointerKind<'_>) -> MutabilityCategory {
-        let ret = match ptr {
-            Unique => {
-                base_mutbl.inherit()
-            }
-            BorrowedPtr(borrow_kind, _) => {
-                MutabilityCategory::from_borrow_kind(borrow_kind)
-            }
-            UnsafePtr(m) => {
-                MutabilityCategory::from_mutbl(m)
-            }
-        };
-        debug!("MutabilityCategory::{}({:?}, {:?}) => {:?}",
-               "from_pointer_kind", base_mutbl, ptr, ret);
-        ret
-    }
-
-    fn from_local(
-        tcx: TyCtxt<'_>,
-        tables: &ty::TypeckTables<'_>,
-        id: hir::HirId,
-    ) -> MutabilityCategory {
-        let ret = match tcx.hir().get(id) {
-            Node::Binding(p) => match p.kind {
-                PatKind::Binding(..) => {
-                    let bm = *tables.pat_binding_modes()
-                                    .get(p.hir_id)
-                                    .expect("missing binding mode");
-                    if bm == ty::BindByValue(Mutability::Mutable) {
-                        McDeclared
-                    } else {
-                        McImmutable
-                    }
-                }
-                _ => span_bug!(p.span, "expected identifier pattern")
-            },
-            _ => span_bug!(tcx.hir().span(id), "expected identifier pattern")
-        };
-        debug!("MutabilityCategory::{}(tcx, id={:?}) => {:?}",
-               "from_local", id, ret);
-        ret
-    }
-
-    pub fn inherit(&self) -> MutabilityCategory {
-        let ret = match *self {
-            McImmutable => McImmutable,
-            McDeclared => McInherited,
-            McInherited => McInherited,
-        };
-        debug!("{:?}.inherit() => {:?}", self, ret);
-        ret
-    }
-
-    pub fn is_mutable(&self) -> bool {
-        let ret = match *self {
-            McImmutable => false,
-            McInherited => true,
-            McDeclared => true,
-        };
-        debug!("{:?}.is_mutable() => {:?}", self, ret);
-        ret
-    }
-
-    pub fn is_immutable(&self) -> bool {
-        let ret = match *self {
-            McImmutable => true,
-            McDeclared | McInherited => false
-        };
-        debug!("{:?}.is_immutable() => {:?}", self, ret);
-        ret
-    }
-
-    pub fn to_user_str(&self) -> &'static str {
-        match *self {
-            McDeclared | McInherited => "mutable",
-            McImmutable => "immutable",
-        }
-    }
-}
-
-impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
-    pub fn new(
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        body_owner: DefId,
-        region_scope_tree: &'a region::ScopeTree,
-        tables: &'a ty::TypeckTables<'tcx>,
-    ) -> MemCategorizationContext<'a, 'tcx> {
-        MemCategorizationContext {
-            tcx,
-            body_owner,
-            upvars: tcx.upvars(body_owner),
-            region_scope_tree,
-            tables,
-            infcx: None,
-            param_env,
-        }
-    }
-}
-
-impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
-    /// Creates a `MemCategorizationContext` during type inference.
-    /// This is used during upvar analysis and a few other places.
-    /// Because the typeck tables are not yet complete, the results
-    /// from the analysis must be used with caution:
-    ///
-    /// - rvalue promotions are not known, so the lifetimes of
-    ///   temporaries may be overly conservative;
-    /// - similarly, as the results of upvar analysis are not yet
-    ///   known, the results around upvar accesses may be incorrect.
-    pub fn with_infer(
-        infcx: &'a InferCtxt<'a, 'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        body_owner: DefId,
-        region_scope_tree: &'a region::ScopeTree,
-        tables: &'a ty::TypeckTables<'tcx>,
-    ) -> MemCategorizationContext<'a, 'tcx> {
-        let tcx = infcx.tcx;
-
-        MemCategorizationContext {
-            tcx,
-            body_owner,
-            upvars: tcx.upvars(body_owner),
-            region_scope_tree,
-            tables,
-            infcx: Some(infcx),
-            param_env,
-        }
-    }
-
-    pub fn type_is_copy_modulo_regions(
-        &self,
-        param_env: ty::ParamEnv<'tcx>,
-        ty: Ty<'tcx>,
-        span: Span,
-    ) -> bool {
-        self.infcx.map(|infcx| infcx.type_is_copy_modulo_regions(param_env, ty, span))
-            .or_else(|| {
-                if (param_env, ty).has_local_value() {
-                    None
-                } else {
-                    Some(ty.is_copy_modulo_regions(self.tcx, param_env, span))
-                }
-            })
-            .unwrap_or(true)
-    }
-
-    fn resolve_vars_if_possible<T>(&self, value: &T) -> T
-        where T: TypeFoldable<'tcx>
-    {
-        self.infcx.map(|infcx| infcx.resolve_vars_if_possible(value))
-            .unwrap_or_else(|| value.clone())
-    }
-
-    fn is_tainted_by_errors(&self) -> bool {
-        self.infcx.map_or(false, |infcx| infcx.is_tainted_by_errors())
-    }
-
-    fn resolve_type_vars_or_error(&self,
-                                  id: hir::HirId,
-                                  ty: Option<Ty<'tcx>>)
-                                  -> McResult<Ty<'tcx>> {
-        match ty {
-            Some(ty) => {
-                let ty = self.resolve_vars_if_possible(&ty);
-                if ty.references_error() || ty.is_ty_var() {
-                    debug!("resolve_type_vars_or_error: error from {:?}", ty);
-                    Err(())
-                } else {
-                    Ok(ty)
-                }
-            }
-            // FIXME
-            None if self.is_tainted_by_errors() => Err(()),
-            None => {
-                bug!("no type for node {}: {} in mem_categorization",
-                     id, self.tcx.hir().node_to_string(id));
-            }
-        }
-    }
-
-    pub fn node_ty(&self,
-                   hir_id: hir::HirId)
-                   -> McResult<Ty<'tcx>> {
-        self.resolve_type_vars_or_error(hir_id,
-                                        self.tables.node_type_opt(hir_id))
-    }
-
-    pub fn expr_ty(&self, expr: &hir::Expr) -> McResult<Ty<'tcx>> {
-        self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_opt(expr))
-    }
-
-    pub fn expr_ty_adjusted(&self, expr: &hir::Expr) -> McResult<Ty<'tcx>> {
-        self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_adjusted_opt(expr))
-    }
-
-    /// Returns the type of value that this pattern matches against.
-    /// Some non-obvious cases:
-    ///
-    /// - a `ref x` binding matches against a value of type `T` and gives
-    ///   `x` the type `&T`; we return `T`.
-    /// - a pattern with implicit derefs (thanks to default binding
-    ///   modes #42640) may look like `Some(x)` but in fact have
-    ///   implicit deref patterns attached (e.g., it is really
-    ///   `&Some(x)`). In that case, we return the "outermost" type
-    ///   (e.g., `&Option<T>).
-    pub fn pat_ty_adjusted(&self, pat: &hir::Pat) -> McResult<Ty<'tcx>> {
-        // Check for implicit `&` types wrapping the pattern; note
-        // that these are never attached to binding patterns, so
-        // actually this is somewhat "disjoint" from the code below
-        // that aims to account for `ref x`.
-        if let Some(vec) = self.tables.pat_adjustments().get(pat.hir_id) {
-            if let Some(first_ty) = vec.first() {
-                debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty);
-                return Ok(first_ty);
-            }
-        }
-
-        self.pat_ty_unadjusted(pat)
-    }
-
-
-    /// Like `pat_ty`, but ignores implicit `&` patterns.
-    fn pat_ty_unadjusted(&self, pat: &hir::Pat) -> McResult<Ty<'tcx>> {
-        let base_ty = self.node_ty(pat.hir_id)?;
-        debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty);
-
-        // This code detects whether we are looking at a `ref x`,
-        // and if so, figures out what the type *being borrowed* is.
-        let ret_ty = match pat.kind {
-            PatKind::Binding(..) => {
-                let bm = *self.tables
-                              .pat_binding_modes()
-                              .get(pat.hir_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) {
-                        Some(t) => t.ty,
-                        None => {
-                            debug!("By-ref binding of non-derefable type {:?}", base_ty);
-                            return Err(());
-                        }
-                    }
-                } else {
-                    base_ty
-                }
-            }
-            _ => base_ty,
-        };
-        debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty);
-
-        Ok(ret_ty)
-    }
-
-    pub fn cat_expr(&self, expr: &hir::Expr) -> McResult<cmt_<'tcx>> {
-        // This recursion helper avoids going through *too many*
-        // adjustments, since *only* non-overloaded deref recurses.
-        fn helper<'a, 'tcx>(
-            mc: &MemCategorizationContext<'a, 'tcx>,
-            expr: &hir::Expr,
-            adjustments: &[adjustment::Adjustment<'tcx>],
-        ) -> McResult<cmt_<'tcx>> {
-            match adjustments.split_last() {
-                None => mc.cat_expr_unadjusted(expr),
-                Some((adjustment, previous)) => {
-                    mc.cat_expr_adjusted_with(expr, || helper(mc, expr, previous), adjustment)
-                }
-            }
-        }
-
-        helper(self, expr, self.tables.expr_adjustments(expr))
-    }
-
-    pub fn cat_expr_adjusted(&self, expr: &hir::Expr,
-                             previous: cmt_<'tcx>,
-                             adjustment: &adjustment::Adjustment<'tcx>)
-                             -> McResult<cmt_<'tcx>> {
-        self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment)
-    }
-
-    fn cat_expr_adjusted_with<F>(&self, expr: &hir::Expr,
-                                 previous: F,
-                                 adjustment: &adjustment::Adjustment<'tcx>)
-                                 -> McResult<cmt_<'tcx>>
-        where F: FnOnce() -> McResult<cmt_<'tcx>>
-    {
-        debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr);
-        let target = self.resolve_vars_if_possible(&adjustment.target);
-        match adjustment.kind {
-            adjustment::Adjust::Deref(overloaded) => {
-                // Equivalent to *expr or something similar.
-                let base = Rc::new(if let Some(deref) = overloaded {
-                    let ref_ty = self.tcx.mk_ref(deref.region, ty::TypeAndMut {
-                        ty: target,
-                        mutbl: deref.mutbl,
-                    });
-                    self.cat_rvalue_node(expr.hir_id, expr.span, ref_ty)
-                } else {
-                    previous()?
-                });
-                self.cat_deref(expr, base, NoteNone)
-            }
-
-            adjustment::Adjust::NeverToAny |
-            adjustment::Adjust::Pointer(_) |
-            adjustment::Adjust::Borrow(_) => {
-                // Result is an rvalue.
-                Ok(self.cat_rvalue_node(expr.hir_id, expr.span, target))
-            }
-        }
-    }
-
-    pub fn cat_expr_unadjusted(&self, expr: &hir::Expr) -> McResult<cmt_<'tcx>> {
-        debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr);
-
-        let expr_ty = self.expr_ty(expr)?;
-        match expr.kind {
-            hir::ExprKind::Unary(hir::UnDeref, ref e_base) => {
-                if self.tables.is_method_call(expr) {
-                    self.cat_overloaded_place(expr, e_base, NoteNone)
-                } else {
-                    let base_cmt = Rc::new(self.cat_expr(&e_base)?);
-                    self.cat_deref(expr, base_cmt, NoteNone)
-                }
-            }
-
-            hir::ExprKind::Field(ref base, f_ident) => {
-                let base_cmt = Rc::new(self.cat_expr(&base)?);
-                debug!("cat_expr(cat_field): id={} expr={:?} base={:?}",
-                       expr.hir_id,
-                       expr,
-                       base_cmt);
-                let f_index = self.tcx.field_index(expr.hir_id, self.tables);
-                Ok(self.cat_field(expr, base_cmt, f_index, f_ident, expr_ty))
-            }
-
-            hir::ExprKind::Index(ref base, _) => {
-                if self.tables.is_method_call(expr) {
-                    // If this is an index implemented by a method call, then it
-                    // will include an implicit deref of the result.
-                    // The call to index() returns a `&T` value, which
-                    // is an rvalue. That is what we will be
-                    // dereferencing.
-                    self.cat_overloaded_place(expr, base, NoteIndex)
-                } else {
-                    let base_cmt = Rc::new(self.cat_expr(&base)?);
-                    self.cat_index(expr, base_cmt, expr_ty, InteriorOffsetKind::Index)
-                }
-            }
-
-            hir::ExprKind::Path(ref qpath) => {
-                let res = self.tables.qpath_res(qpath, expr.hir_id);
-                self.cat_res(expr.hir_id, expr.span, expr_ty, res)
-            }
-
-            hir::ExprKind::Type(ref e, _) => {
-                self.cat_expr(&e)
-            }
-
-            hir::ExprKind::AddrOf(..) | hir::ExprKind::Call(..) |
-            hir::ExprKind::Assign(..) | hir::ExprKind::AssignOp(..) |
-            hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) |
-            hir::ExprKind::Unary(..) | hir::ExprKind::Yield(..) |
-            hir::ExprKind::MethodCall(..) | hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) |
-            hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) |
-            hir::ExprKind::Binary(..) |
-            hir::ExprKind::Block(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..) |
-            hir::ExprKind::Lit(..) | hir::ExprKind::Break(..) |
-            hir::ExprKind::Continue(..) | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) |
-            hir::ExprKind::InlineAsm(..) | hir::ExprKind::Box(..) | hir::ExprKind::Err => {
-                Ok(self.cat_rvalue_node(expr.hir_id, expr.span, expr_ty))
-            }
-        }
-    }
-
-    pub fn cat_res(&self,
-                   hir_id: hir::HirId,
-                   span: Span,
-                   expr_ty: Ty<'tcx>,
-                   res: Res)
-                   -> McResult<cmt_<'tcx>> {
-        debug!("cat_res: id={:?} expr={:?} def={:?}",
-               hir_id, expr_ty, res);
-
-        match res {
-            Res::Def(DefKind::Ctor(..), _)
-            | Res::Def(DefKind::Const, _)
-            | Res::Def(DefKind::ConstParam, _)
-            | Res::Def(DefKind::AssocConst, _)
-            | Res::Def(DefKind::Fn, _)
-            | Res::Def(DefKind::Method, _)
-            | Res::SelfCtor(..) => {
-                Ok(self.cat_rvalue_node(hir_id, span, expr_ty))
-            }
-
-            Res::Def(DefKind::Static, def_id) => {
-                // `#[thread_local]` statics may not outlive the current function, but
-                // they also cannot be moved out of.
-                let is_thread_local = self.tcx.get_attrs(def_id)[..]
-                    .iter()
-                    .any(|attr| attr.check_name(sym::thread_local));
-
-                let cat = if is_thread_local {
-                    Categorization::ThreadLocal
-                } else {
-                    Categorization::StaticItem
-                };
-
-                Ok(cmt_ {
-                    hir_id,
-                    span,
-                    cat,
-                    mutbl: match self.tcx.static_mutability(def_id).unwrap() {
-                        Mutability::Immutable => McImmutable,
-                        Mutability::Mutable => McDeclared,
-                    },
-                    ty:expr_ty,
-                    note: NoteNone
-                })
-            }
-
-            Res::Local(var_id) => {
-                if self.upvars.map_or(false, |upvars| upvars.contains_key(&var_id)) {
-                    self.cat_upvar(hir_id, span, var_id)
-                } else {
-                    Ok(cmt_ {
-                        hir_id,
-                        span,
-                        cat: Categorization::Local(var_id),
-                        mutbl: MutabilityCategory::from_local(self.tcx, self.tables, var_id),
-                        ty: expr_ty,
-                        note: NoteNone
-                    })
-                }
-            }
-
-            def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def)
-        }
-    }
-
-    // Categorize an upvar, complete with invisible derefs of closure
-    // environment and upvar reference as appropriate.
-    fn cat_upvar(
-        &self,
-        hir_id: hir::HirId,
-        span: Span,
-        var_id: hir::HirId,
-    ) -> McResult<cmt_<'tcx>> {
-        // An upvar can have up to 3 components. We translate first to a
-        // `Categorization::Upvar`, which is itself a fiction -- it represents the reference to the
-        // field from the environment.
-        //
-        // `Categorization::Upvar`.  Next, we add a deref through the implicit
-        // environment pointer with an anonymous free region 'env and
-        // appropriate borrow kind for closure kinds that take self by
-        // reference.  Finally, if the upvar was captured
-        // by-reference, we add a deref through that reference.  The
-        // region of this reference is an inference variable 'up that
-        // was previously generated and recorded in the upvar borrow
-        // map.  The borrow kind bk is inferred by based on how the
-        // upvar is used.
-        //
-        // This results in the following table for concrete closure
-        // types:
-        //
-        //                | move                 | ref
-        // ---------------+----------------------+-------------------------------
-        // Fn             | copied -> &'env      | upvar -> &'env -> &'up bk
-        // FnMut          | copied -> &'env mut  | upvar -> &'env mut -> &'up bk
-        // FnOnce         | copied               | upvar -> &'up bk
-
-        let closure_expr_def_id = self.body_owner;
-        let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id(
-            LocalDefId::from_def_id(closure_expr_def_id),
-        );
-        let ty = self.node_ty(fn_hir_id)?;
-        let kind = match ty.kind {
-            ty::Generator(..) => ty::ClosureKind::FnOnce,
-            ty::Closure(closure_def_id, substs) => {
-                match self.infcx {
-                    // During upvar inference we may not know the
-                    // closure kind, just use the LATTICE_BOTTOM value.
-                    Some(infcx) =>
-                        infcx.closure_kind(
-                            closure_def_id,
-                            substs
-                        ).unwrap_or(ty::ClosureKind::LATTICE_BOTTOM),
-
-                    None =>
-                        substs.as_closure().kind(closure_def_id, self.tcx),
-                }
-            }
-            _ => span_bug!(span, "unexpected type for fn in mem_categorization: {:?}", ty),
-        };
-
-        let upvar_id = ty::UpvarId {
-            var_path: ty::UpvarPath { hir_id: var_id },
-            closure_expr_id: closure_expr_def_id.to_local(),
-        };
-
-        let var_ty = self.node_ty(var_id)?;
-
-        // Mutability of original variable itself
-        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
-        // this field further, but it will do for now).
-        let cmt_result = cmt_ {
-            hir_id,
-            span,
-            cat: Categorization::Upvar(Upvar {id: upvar_id, kind: kind}),
-            mutbl: var_mutbl,
-            ty: var_ty,
-            note: NoteNone
-        };
-
-        // If this is a `FnMut` or `Fn` closure, then the above is
-        // conceptually a `&mut` or `&` reference, so we have to add a
-        // deref.
-        let cmt_result = match kind {
-            ty::ClosureKind::FnOnce => {
-                cmt_result
-            }
-            ty::ClosureKind::FnMut => {
-                self.env_deref(hir_id, span, upvar_id, var_mutbl, ty::MutBorrow, cmt_result)
-            }
-            ty::ClosureKind::Fn => {
-                self.env_deref(hir_id, span, upvar_id, var_mutbl, ty::ImmBorrow, cmt_result)
-            }
-        };
-
-        // If this is a by-ref capture, then the upvar we loaded is
-        // actually a reference, so we have to add an implicit deref
-        // for that.
-        let upvar_capture = self.tables.upvar_capture(upvar_id);
-        let cmt_result = match upvar_capture {
-            ty::UpvarCapture::ByValue => {
-                cmt_result
-            }
-            ty::UpvarCapture::ByRef(upvar_borrow) => {
-                let ptr = BorrowedPtr(upvar_borrow.kind, upvar_borrow.region);
-                cmt_ {
-                    hir_id,
-                    span,
-                    cat: Categorization::Deref(Rc::new(cmt_result), ptr),
-                    mutbl: MutabilityCategory::from_borrow_kind(upvar_borrow.kind),
-                    ty: var_ty,
-                    note: NoteUpvarRef(upvar_id)
-                }
-            }
-        };
-
-        let ret = cmt_result;
-        debug!("cat_upvar ret={:?}", ret);
-        Ok(ret)
-    }
-
-    fn env_deref(&self,
-                 hir_id: hir::HirId,
-                 span: Span,
-                 upvar_id: ty::UpvarId,
-                 upvar_mutbl: MutabilityCategory,
-                 env_borrow_kind: ty::BorrowKind,
-                 cmt_result: cmt_<'tcx>)
-                 -> cmt_<'tcx>
-    {
-        // Region of environment pointer
-        let env_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion {
-            // The environment of a closure is guaranteed to
-            // outlive any bindings introduced in the body of the
-            // closure itself.
-            scope: upvar_id.closure_expr_id.to_def_id(),
-            bound_region: ty::BrEnv
-        }));
-
-        let env_ptr = BorrowedPtr(env_borrow_kind, env_region);
-
-        let var_ty = cmt_result.ty;
-
-        // We need to add the env deref.  This means
-        // that the above is actually immutable and
-        // has a ref type.  However, nothing should
-        // actually look at the type, so we can get
-        // away with stuffing a `Error` in there
-        // instead of bothering to construct a proper
-        // one.
-        let cmt_result = cmt_ {
-            mutbl: McImmutable,
-            ty: self.tcx.types.err,
-            ..cmt_result
-        };
-
-        let mut deref_mutbl = MutabilityCategory::from_borrow_kind(env_borrow_kind);
-
-        // Issue #18335. If variable is declared as immutable, override the
-        // mutability from the environment and substitute an `&T` anyway.
-        match upvar_mutbl {
-            McImmutable => { deref_mutbl = McImmutable; }
-            McDeclared | McInherited => { }
-        }
-
-        let ret = cmt_ {
-            hir_id,
-            span,
-            cat: Categorization::Deref(Rc::new(cmt_result), env_ptr),
-            mutbl: deref_mutbl,
-            ty: var_ty,
-            note: NoteClosureEnv(upvar_id)
-        };
-
-        debug!("env_deref ret {:?}", ret);
-
-        ret
-    }
-
-    pub fn cat_rvalue_node(&self,
-                           hir_id: hir::HirId,
-                           span: Span,
-                           expr_ty: Ty<'tcx>)
-                           -> cmt_<'tcx> {
-        debug!("cat_rvalue_node(id={:?}, span={:?}, expr_ty={:?})",
-               hir_id, span, expr_ty);
-
-        let ret = self.cat_rvalue(hir_id, span, expr_ty);
-        debug!("cat_rvalue_node ret {:?}", ret);
-        ret
-    }
-
-    pub fn cat_rvalue(&self,
-                      cmt_hir_id: hir::HirId,
-                      span: Span,
-                      expr_ty: Ty<'tcx>) -> cmt_<'tcx> {
-        let ret = cmt_ {
-            hir_id: cmt_hir_id,
-            span:span,
-            cat:Categorization::Rvalue,
-            mutbl:McDeclared,
-            ty:expr_ty,
-            note: NoteNone
-        };
-        debug!("cat_rvalue ret {:?}", ret);
-        ret
-    }
-
-    pub fn cat_field<N: HirNode>(&self,
-                                 node: &N,
-                                 base_cmt: cmt<'tcx>,
-                                 f_index: usize,
-                                 f_ident: ast::Ident,
-                                 f_ty: Ty<'tcx>)
-                                 -> cmt_<'tcx> {
-        let ret = cmt_ {
-            hir_id: node.hir_id(),
-            span: node.span(),
-            mutbl: base_cmt.mutbl.inherit(),
-            cat: Categorization::Interior(base_cmt,
-                                          InteriorField(FieldIndex(f_index, f_ident.name))),
-            ty: f_ty,
-            note: NoteNone
-        };
-        debug!("cat_field ret {:?}", ret);
-        ret
-    }
-
-    fn cat_overloaded_place(
-        &self,
-        expr: &hir::Expr,
-        base: &hir::Expr,
-        note: Note,
-    ) -> McResult<cmt_<'tcx>> {
-        debug!("cat_overloaded_place(expr={:?}, base={:?}, note={:?})",
-               expr,
-               base,
-               note);
-
-        // Reconstruct the output assuming it's a reference with the
-        // same region and mutability as the receiver. This holds for
-        // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
-        let place_ty = self.expr_ty(expr)?;
-        let base_ty = self.expr_ty_adjusted(base)?;
-
-        let (region, mutbl) = match base_ty.kind {
-            ty::Ref(region, _, mutbl) => (region, mutbl),
-            _ => span_bug!(expr.span, "cat_overloaded_place: base is not a reference")
-        };
-        let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut {
-            ty: place_ty,
-            mutbl,
-        });
-
-        let base_cmt = Rc::new(self.cat_rvalue_node(expr.hir_id, expr.span, ref_ty));
-        self.cat_deref(expr, base_cmt, note)
-    }
-
-    pub fn cat_deref(
-        &self,
-        node: &impl HirNode,
-        base_cmt: cmt<'tcx>,
-        note: Note,
-    ) -> McResult<cmt_<'tcx>> {
-        debug!("cat_deref: base_cmt={:?}", base_cmt);
-
-        let base_cmt_ty = base_cmt.ty;
-        let deref_ty = match base_cmt_ty.builtin_deref(true) {
-            Some(mt) => mt.ty,
-            None => {
-                debug!("explicit deref of non-derefable type: {:?}", base_cmt_ty);
-                return Err(());
-            }
-        };
-
-        let ptr = match base_cmt.ty.kind {
-            ty::Adt(def, ..) if def.is_box() => Unique,
-            ty::RawPtr(ref mt) => UnsafePtr(mt.mutbl),
-            ty::Ref(r, _, mutbl) => {
-                let bk = ty::BorrowKind::from_mutbl(mutbl);
-                BorrowedPtr(bk, r)
-            }
-            _ => bug!("unexpected type in cat_deref: {:?}", base_cmt.ty)
-        };
-        let ret = cmt_ {
-            hir_id: node.hir_id(),
-            span: node.span(),
-            // For unique ptrs, we inherit mutability from the owning reference.
-            mutbl: MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr),
-            cat: Categorization::Deref(base_cmt, ptr),
-            ty: deref_ty,
-            note: note,
-        };
-        debug!("cat_deref ret {:?}", ret);
-        Ok(ret)
-    }
-
-    fn cat_index<N: HirNode>(&self,
-                             elt: &N,
-                             base_cmt: cmt<'tcx>,
-                             element_ty: Ty<'tcx>,
-                             context: InteriorOffsetKind)
-                             -> McResult<cmt_<'tcx>> {
-        //! Creates a cmt for an indexing operation (`[]`).
-        //!
-        //! One subtle aspect of indexing that may not be
-        //! immediately obvious: for anything other than a fixed-length
-        //! vector, an operation like `x[y]` actually consists of two
-        //! disjoint (from the point of view of borrowck) operations.
-        //! The first is a deref of `x` to create a pointer `p` that points
-        //! at the first element in the array. The second operation is
-        //! an index which adds `y*sizeof(T)` to `p` to obtain the
-        //! pointer to `x[y]`. `cat_index` will produce a resulting
-        //! cmt containing both this deref and the indexing,
-        //! presuming that `base_cmt` is not of fixed-length type.
-        //!
-        //! # Parameters
-        //! - `elt`: the HIR node being indexed
-        //! - `base_cmt`: the cmt of `elt`
-
-        let interior_elem = InteriorElement(context);
-        let ret = self.cat_imm_interior(elt, base_cmt, element_ty, interior_elem);
-        debug!("cat_index ret {:?}", ret);
-        return Ok(ret);
-    }
-
-    pub fn cat_imm_interior<N:HirNode>(&self,
-                                       node: &N,
-                                       base_cmt: cmt<'tcx>,
-                                       interior_ty: Ty<'tcx>,
-                                       interior: InteriorKind)
-                                       -> cmt_<'tcx> {
-        let ret = cmt_ {
-            hir_id: node.hir_id(),
-            span: node.span(),
-            mutbl: base_cmt.mutbl.inherit(),
-            cat: Categorization::Interior(base_cmt, interior),
-            ty: interior_ty,
-            note: NoteNone
-        };
-        debug!("cat_imm_interior ret={:?}", ret);
-        ret
-    }
-
-    pub fn cat_downcast_if_needed<N:HirNode>(&self,
-                                             node: &N,
-                                             base_cmt: cmt<'tcx>,
-                                             variant_did: DefId)
-                                             -> cmt<'tcx> {
-        // univariant enums do not need downcasts
-        let base_did = self.tcx.parent(variant_did).unwrap();
-        if self.tcx.adt_def(base_did).variants.len() != 1 {
-            let base_ty = base_cmt.ty;
-            let ret = Rc::new(cmt_ {
-                hir_id: node.hir_id(),
-                span: node.span(),
-                mutbl: base_cmt.mutbl.inherit(),
-                cat: Categorization::Downcast(base_cmt, variant_did),
-                ty: base_ty,
-                note: NoteNone
-            });
-            debug!("cat_downcast ret={:?}", ret);
-            ret
-        } else {
-            debug!("cat_downcast univariant={:?}", base_cmt);
-            base_cmt
-        }
-    }
-
-    pub fn cat_pattern<F>(&self, cmt: cmt<'tcx>, pat: &hir::Pat, mut op: F) -> McResult<()>
-        where F: FnMut(cmt<'tcx>, &hir::Pat),
-    {
-        self.cat_pattern_(cmt, pat, &mut op)
-    }
-
-    // FIXME(#19596) This is a workaround, but there should be a better way to do this
-    fn cat_pattern_<F>(&self, mut cmt: cmt<'tcx>, pat: &hir::Pat, op: &mut F) -> McResult<()>
-        where F : FnMut(cmt<'tcx>, &hir::Pat)
-    {
-        // Here, `cmt` is the categorization for the value being
-        // matched and pat is the pattern it is being matched against.
-        //
-        // In general, the way that this works is that we walk down
-        // the pattern, constructing a cmt that represents the path
-        // that will be taken to reach the value being matched.
-        //
-        // When we encounter named bindings, we take the cmt that has
-        // been built up and pass it off to guarantee_valid() so that
-        // we can be sure that the binding will remain valid for the
-        // duration of the arm.
-        //
-        // (*2) There is subtlety concerning the correspondence between
-        // pattern ids and types as compared to *expression* ids and
-        // types. This is explained briefly. on the definition of the
-        // type `cmt`, so go off and read what it says there, then
-        // come back and I'll dive into a bit more detail here. :) OK,
-        // back?
-        //
-        // In general, the id of the cmt should be the node that
-        // "produces" the value---patterns aren't executable code
-        // exactly, but I consider them to "execute" when they match a
-        // value, and I consider them to produce the value that was
-        // matched. So if you have something like:
-        //
-        // (FIXME: `@@3` is not legal code anymore!)
-        //
-        //     let x = @@3;
-        //     match x {
-        //       @@y { ... }
-        //     }
-        //
-        // In this case, the cmt and the relevant ids would be:
-        //
-        //     CMT             Id                  Type of Id Type of cmt
-        //
-        //     local(x)->@->@
-        //     ^~~~~~~^        `x` from discr      @@int      @@int
-        //     ^~~~~~~~~~^     `@@y` pattern node  @@int      @int
-        //     ^~~~~~~~~~~~~^  `@y` pattern node   @int       int
-        //
-        // You can see that the types of the id and the cmt are in
-        // sync in the first line, because that id is actually the id
-        // of an expression. But once we get to pattern ids, the types
-        // step out of sync again. So you'll see below that we always
-        // get the type of the *subpattern* and use that.
-
-        debug!("cat_pattern(pat={:?}, cmt={:?})", pat, cmt);
-
-        // If (pattern) adjustments are active for this pattern, adjust the `cmt` correspondingly.
-        // `cmt`s are constructed differently from patterns. For example, in
-        //
-        // ```
-        // match foo {
-        //     &&Some(x, ) => { ... },
-        //     _ => { ... },
-        // }
-        // ```
-        //
-        // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the
-        // corresponding `cmt` we start with a `cmt` for `foo`, and then, by traversing the
-        // pattern, try to answer the question: given the address of `foo`, how is `x` reached?
-        //
-        // `&&Some(x,)` `cmt_foo`
-        //  `&Some(x,)` `deref { cmt_foo}`
-        //   `Some(x,)` `deref { deref { cmt_foo }}`
-        //        (x,)` `field0 { deref { deref { cmt_foo }}}` <- resulting cmt
-        //
-        // The above example has no adjustments. If the code were instead the (after adjustments,
-        // equivalent) version
-        //
-        // ```
-        // match foo {
-        //     Some(x, ) => { ... },
-        //     _ => { ... },
-        // }
-        // ```
-        //
-        // Then we see that to get the same result, we must start with `deref { deref { cmt_foo }}`
-        // instead of `cmt_foo` since the pattern is now `Some(x,)` and not `&&Some(x,)`, even
-        // though its assigned type is that of `&&Some(x,)`.
-        for _ in 0..self.tables
-                        .pat_adjustments()
-                        .get(pat.hir_id)
-                        .map(|v| v.len())
-                        .unwrap_or(0)
-        {
-            debug!("cat_pattern: applying adjustment to cmt={:?}", cmt);
-            cmt = Rc::new(self.cat_deref(pat, cmt, NoteNone)?);
-        }
-        let cmt = cmt; // lose mutability
-        debug!("cat_pattern: applied adjustment derefs to get cmt={:?}", cmt);
-
-        // Invoke the callback, but only now, after the `cmt` has adjusted.
-        //
-        // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that
-        // case, the initial `cmt` will be that for `&Some(3)` and the pattern is `Some(x)`. We
-        // don't want to call `op` with these incompatible values. As written, what happens instead
-        // is that `op` is called with the adjusted cmt (that for `*&Some(3)`) and the pattern
-        // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)`
-        // result in the cmt `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with
-        // that (where the `ref` on `x` is implied).
-        op(cmt.clone(), pat);
-
-        match pat.kind {
-            PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => {
-                let res = self.tables.qpath_res(qpath, pat.hir_id);
-                let (cmt, expected_len) = match res {
-                    Res::Err => {
-                        debug!("access to unresolvable pattern {:?}", pat);
-                        return Err(())
-                    }
-                    Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), variant_ctor_did) => {
-                        let variant_did = self.tcx.parent(variant_ctor_did).unwrap();
-                        let enum_did = self.tcx.parent(variant_did).unwrap();
-                        (self.cat_downcast_if_needed(pat, cmt, variant_did),
-                         self.tcx.adt_def(enum_did)
-                             .variant_with_ctor_id(variant_ctor_did).fields.len())
-                    }
-                    Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), _)
-                    | Res::SelfCtor(..) => {
-                        let ty = self.pat_ty_unadjusted(&pat)?;
-                        match ty.kind {
-                            ty::Adt(adt_def, _) => {
-                                (cmt, adt_def.non_enum_variant().fields.len())
-                            }
-                            _ => {
-                                span_bug!(pat.span,
-                                          "tuple struct pattern unexpected type {:?}", ty);
-                            }
-                        }
-                    }
-                    def => {
-                        debug!(
-                            "tuple struct pattern didn't resolve to variant or struct {:?} at {:?}",
-                            def,
-                            pat.span,
-                        );
-                        self.tcx.sess.delay_span_bug(pat.span, &format!(
-                            "tuple struct pattern didn't resolve to variant or struct {:?}",
-                            def,
-                        ));
-                        return Err(());
-                    }
-                };
-
-                for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) {
-                    let subpat_ty = self.pat_ty_adjusted(&subpat)?; // see (*2)
-                    let interior = InteriorField(FieldIndex(i, sym::integer(i)));
-                    let subcmt = Rc::new(
-                        self.cat_imm_interior(pat, cmt.clone(), subpat_ty, interior));
-                    self.cat_pattern_(subcmt, &subpat, op)?;
-                }
-            }
-
-            PatKind::Struct(ref qpath, ref field_pats, _) => {
-                // {f1: p1, ..., fN: pN}
-                let res = self.tables.qpath_res(qpath, pat.hir_id);
-                let cmt = match res {
-                    Res::Err => {
-                        debug!("access to unresolvable pattern {:?}", pat);
-                        return Err(())
-                    }
-                    Res::Def(DefKind::Ctor(CtorOf::Variant, _), variant_ctor_did) => {
-                        let variant_did = self.tcx.parent(variant_ctor_did).unwrap();
-                        self.cat_downcast_if_needed(pat, cmt, variant_did)
-                    }
-                    Res::Def(DefKind::Variant, variant_did) => {
-                        self.cat_downcast_if_needed(pat, cmt, variant_did)
-                    }
-                    _ => cmt,
-                };
-
-                for fp in field_pats {
-                    let field_ty = self.pat_ty_adjusted(&fp.pat)?; // see (*2)
-                    let f_index = self.tcx.field_index(fp.hir_id, self.tables);
-                    let cmt_field = Rc::new(self.cat_field(pat, cmt.clone(), f_index,
-                                                           fp.ident, field_ty));
-                    self.cat_pattern_(cmt_field, &fp.pat, op)?;
-                }
-            }
-
-            PatKind::Or(ref pats) => {
-                for pat in pats {
-                    self.cat_pattern_(cmt.clone(), &pat, op)?;
-                }
-            }
-
-            PatKind::Binding(.., Some(ref subpat)) => {
-                self.cat_pattern_(cmt, &subpat, op)?;
-            }
-
-            PatKind::Tuple(ref subpats, ddpos) => {
-                // (p1, ..., pN)
-                let ty = self.pat_ty_unadjusted(&pat)?;
-                let expected_len = match ty.kind {
-                    ty::Tuple(ref tys) => tys.len(),
-                    _ => span_bug!(pat.span, "tuple pattern unexpected type {:?}", ty),
-                };
-                for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) {
-                    let subpat_ty = self.pat_ty_adjusted(&subpat)?; // see (*2)
-                    let interior = InteriorField(FieldIndex(i, sym::integer(i)));
-                    let subcmt = Rc::new(
-                        self.cat_imm_interior(pat, cmt.clone(), subpat_ty, interior));
-                    self.cat_pattern_(subcmt, &subpat, op)?;
-                }
-            }
-
-            PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => {
-                // box p1, &p1, &mut p1.  we can ignore the mutability of
-                // PatKind::Ref since that information is already contained
-                // in the type.
-                let subcmt = Rc::new(self.cat_deref(pat, cmt, NoteNone)?);
-                self.cat_pattern_(subcmt, &subpat, op)?;
-            }
-
-            PatKind::Slice(ref before, ref slice, ref after) => {
-                let element_ty = match cmt.ty.builtin_index() {
-                    Some(ty) => ty,
-                    None => {
-                        debug!("explicit index of non-indexable type {:?}", cmt);
-                        return Err(());
-                    }
-                };
-                let context = InteriorOffsetKind::Pattern;
-                let elt_cmt = Rc::new(self.cat_index(pat, cmt, element_ty, context)?);
-                for before_pat in before {
-                    self.cat_pattern_(elt_cmt.clone(), &before_pat, op)?;
-                }
-                if let Some(ref slice_pat) = *slice {
-                    self.cat_pattern_(elt_cmt.clone(), &slice_pat, op)?;
-                }
-                for after_pat in after {
-                    self.cat_pattern_(elt_cmt.clone(), &after_pat, op)?;
-                }
-            }
-
-            PatKind::Path(_) | PatKind::Binding(.., None) |
-            PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild => {
-                // always ok
-            }
-        }
-
-        Ok(())
-    }
-}
-
-#[derive(Clone, Debug)]
-pub enum Aliasability {
-    FreelyAliasable(AliasableReason),
-    NonAliasable,
-    ImmutableUnique(Box<Aliasability>),
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum AliasableReason {
-    AliasableBorrowed,
-    AliasableStatic,
-    AliasableStaticMut,
-}
-
-impl<'tcx> cmt_<'tcx> {
-    pub fn guarantor(&self) -> cmt_<'tcx> {
-        //! Returns `self` after stripping away any derefs or
-        //! interior content. The return value is basically the `cmt` which
-        //! determines how long the value in `self` remains live.
-
-        match self.cat {
-            Categorization::Rvalue |
-            Categorization::StaticItem |
-            Categorization::ThreadLocal |
-            Categorization::Local(..) |
-            Categorization::Deref(_, UnsafePtr(..)) |
-            Categorization::Deref(_, BorrowedPtr(..)) |
-            Categorization::Upvar(..) => {
-                (*self).clone()
-            }
-            Categorization::Downcast(ref b, _) |
-            Categorization::Interior(ref b, _) |
-            Categorization::Deref(ref b, Unique) => {
-                b.guarantor()
-            }
-        }
-    }
-
-    /// Returns `FreelyAliasable(_)` if this place represents a freely aliasable pointer type.
-    pub fn freely_aliasable(&self) -> Aliasability {
-        // Maybe non-obvious: copied upvars can only be considered
-        // non-aliasable in once closures, since any other kind can be
-        // aliased and eventually recused.
-
-        match self.cat {
-            Categorization::Deref(ref b, BorrowedPtr(ty::MutBorrow, _)) |
-            Categorization::Deref(ref b, BorrowedPtr(ty::UniqueImmBorrow, _)) |
-            Categorization::Deref(ref b, Unique) |
-            Categorization::Downcast(ref b, _) |
-            Categorization::Interior(ref b, _) => {
-                // Aliasability depends on base cmt
-                b.freely_aliasable()
-            }
-
-            Categorization::Rvalue |
-            Categorization::ThreadLocal |
-            Categorization::Local(..) |
-            Categorization::Upvar(..) |
-            Categorization::Deref(_, UnsafePtr(..)) => { // yes, it's aliasable, but...
-                NonAliasable
-            }
-
-            Categorization::StaticItem => {
-                if self.mutbl.is_mutable() {
-                    FreelyAliasable(AliasableStaticMut)
-                } else {
-                    FreelyAliasable(AliasableStatic)
-                }
-            }
-
-            Categorization::Deref(_, BorrowedPtr(ty::ImmBorrow, _)) => {
-                FreelyAliasable(AliasableBorrowed)
-            }
-        }
-    }
-
-    // Digs down through one or two layers of deref and grabs the
-    // Categorization of the cmt for the upvar if a note indicates there is
-    // one.
-    pub fn upvar_cat(&self) -> Option<&Categorization<'tcx>> {
-        match self.note {
-            NoteClosureEnv(..) | NoteUpvarRef(..) => {
-                Some(match self.cat {
-                    Categorization::Deref(ref inner, _) => {
-                        match inner.cat {
-                            Categorization::Deref(ref inner, _) => &inner.cat,
-                            Categorization::Upvar(..) => &inner.cat,
-                            _ => bug!()
-                        }
-                    }
-                    _ => bug!()
-                })
-            }
-            NoteIndex | NoteNone => None
-        }
-    }
-
-    pub fn descriptive_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
-        match self.cat {
-            Categorization::StaticItem => {
-                "static item".into()
-            }
-            Categorization::ThreadLocal => {
-                "thread-local static item".into()
-            }
-            Categorization::Rvalue => {
-                "non-place".into()
-            }
-            Categorization::Local(vid) => {
-                if tcx.hir().is_argument(vid) {
-                    "argument"
-                } else {
-                    "local variable"
-                }.into()
-            }
-            Categorization::Deref(_, pk) => {
-                match self.upvar_cat() {
-                    Some(&Categorization::Upvar(ref var)) => {
-                        var.to_string().into()
-                    }
-                    Some(_) => bug!(),
-                    None => {
-                        match pk {
-                            Unique => {
-                                "`Box` content"
-                            }
-                            UnsafePtr(..) => {
-                                "dereference of raw pointer"
-                            }
-                            BorrowedPtr(..) => {
-                                match self.note {
-                                    NoteIndex => "indexed content",
-                                    _ => "borrowed content"
-                                }
-                            }
-                        }.into()
-                    }
-                }
-            }
-            Categorization::Interior(_, InteriorField(..)) => {
-                "field".into()
-            }
-            Categorization::Interior(_, InteriorElement(InteriorOffsetKind::Index)) => {
-                "indexed content".into()
-            }
-            Categorization::Interior(_, InteriorElement(InteriorOffsetKind::Pattern)) => {
-                "pattern-bound indexed content".into()
-            }
-            Categorization::Upvar(ref var) => {
-                var.to_string().into()
-            }
-            Categorization::Downcast(ref cmt, _) => {
-                cmt.descriptive_string(tcx).into()
-            }
-        }
-    }
-}
-
-pub fn ptr_sigil(ptr: PointerKind<'_>) -> &'static str {
-    match ptr {
-        Unique => "Box",
-        BorrowedPtr(ty::ImmBorrow, _) => "&",
-        BorrowedPtr(ty::MutBorrow, _) => "&mut",
-        BorrowedPtr(ty::UniqueImmBorrow, _) => "&unique",
-        UnsafePtr(_) => "*",
-    }
-}
-
-impl fmt::Debug for InteriorKind {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            InteriorField(FieldIndex(_, info)) => write!(f, "{}", info),
-            InteriorElement(..) => write!(f, "[]"),
-        }
-    }
-}
-
-impl fmt::Debug for Upvar {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{:?}/{:?}", self.id, self.kind)
-    }
-}
-
-impl fmt::Display for Upvar {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let kind = match self.kind {
-            ty::ClosureKind::Fn => "Fn",
-            ty::ClosureKind::FnMut => "FnMut",
-            ty::ClosureKind::FnOnce => "FnOnce",
-        };
-        write!(f, "captured outer variable in an `{}` closure", kind)
-    }
-}
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index f4fdc2882e7..8ceb2174852 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -74,8 +74,7 @@
 
 use crate::check::dropck;
 use crate::check::FnCtxt;
-use crate::middle::mem_categorization as mc;
-use crate::middle::mem_categorization::Categorization;
+use crate::mem_categorization as mc;
 use crate::middle::region;
 use rustc::hir::def_id::DefId;
 use rustc::infer::outlives::env::OutlivesEnvironment;
@@ -88,7 +87,6 @@ use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc::hir::{self, PatKind};
 use std::mem;
 use std::ops::Deref;
-use std::rc::Rc;
 use syntax_pos::Span;
 
 // a variation on try that just returns unit
@@ -814,18 +812,17 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
     where
         F: for<'b> FnOnce(mc::MemCategorizationContext<'b, 'tcx>) -> R,
     {
-        f(mc::MemCategorizationContext::with_infer(
+        f(mc::MemCategorizationContext::new(
             &self.infcx,
             self.outlives_environment.param_env,
             self.body_owner,
-            &self.region_scope_tree,
             &self.tables.borrow(),
         ))
     }
 
     /// Invoked on any adjustments that occur. Checks that if this is a region pointer being
     /// dereferenced, the lifetime of the pointer includes the deref expr.
-    fn constrain_adjustments(&mut self, expr: &hir::Expr) -> mc::McResult<mc::cmt_<'tcx>> {
+    fn constrain_adjustments(&mut self, expr: &hir::Expr) -> mc::McResult<mc::Place<'tcx>> {
         debug!("constrain_adjustments(expr={:?})", expr);
 
         let mut cmt = self.with_mc(|mc| mc.cat_expr_unadjusted(expr))?;
@@ -899,10 +896,6 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
             }
 
             cmt = self.with_mc(|mc| mc.cat_expr_adjusted(expr, cmt, &adjustment))?;
-
-            if let Categorization::Deref(_, mc::BorrowedPtr(_, r_ptr)) = cmt.cat {
-                self.mk_subregion_due_to_dereference(expr.span, expr_region, r_ptr);
-            }
         }
 
         Ok(cmt)
@@ -921,16 +914,22 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
         )
     }
 
-    fn check_safety_of_rvalue_destructor_if_necessary(&mut self, cmt: &mc::cmt_<'tcx>, span: Span) {
-        if let Categorization::Rvalue = cmt.cat {
-            let typ = self.resolve_type(cmt.ty);
-            let body_id = self.body_id;
-            let _ = dropck::check_drop_obligations(
-                self,
-                typ,
-                span,
-                body_id,
-            );
+    fn check_safety_of_rvalue_destructor_if_necessary(
+        &mut self,
+        place: &mc::Place<'tcx>,
+        span: Span,
+    ) {
+        if let mc::PlaceBase::Rvalue = place.base {
+            if place.projections.is_empty() {
+                let typ = self.resolve_type(place.ty);
+                let body_id = self.body_id;
+                let _ = dropck::check_drop_obligations(
+                    self,
+                    typ,
+                    span,
+                    body_id,
+                );
+            }
         }
     }
 
@@ -1035,7 +1034,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
             }
             Some(ref expr) => &**expr,
         };
-        let discr_cmt = Rc::new(ignore_err!(self.with_mc(|mc| mc.cat_expr(init_expr))));
+        let discr_cmt = ignore_err!(self.with_mc(|mc| mc.cat_expr(init_expr)));
         self.link_pattern(discr_cmt, &local.pat);
     }
 
@@ -1044,7 +1043,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
     /// linked to the lifetime of its guarantor (if any).
     fn link_match(&self, discr: &hir::Expr, arms: &[hir::Arm]) {
         debug!("regionck::for_match()");
-        let discr_cmt = Rc::new(ignore_err!(self.with_mc(|mc| mc.cat_expr(discr))));
+        let discr_cmt = ignore_err!(self.with_mc(|mc| mc.cat_expr(discr)));
         debug!("discr_cmt={:?}", discr_cmt);
         for arm in arms {
             self.link_pattern(discr_cmt.clone(), &arm.pat);
@@ -1058,7 +1057,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
         for param in params {
             let param_ty = self.node_ty(param.hir_id);
             let param_cmt = self.with_mc(|mc| {
-                Rc::new(mc.cat_rvalue(param.hir_id, param.pat.span, param_ty))
+                mc.cat_rvalue(param.hir_id, param.pat.span, param_ty)
             });
             debug!("param_ty={:?} param_cmt={:?} param={:?}", param_ty, param_cmt, param);
             self.link_pattern(param_cmt, &param.pat);
@@ -1067,7 +1066,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
 
     /// Link lifetimes of any ref bindings in `root_pat` to the pointers found
     /// in the discriminant, if needed.
-    fn link_pattern(&self, discr_cmt: mc::cmt<'tcx>, root_pat: &hir::Pat) {
+    fn link_pattern(&self, discr_cmt: mc::Place<'tcx>, root_pat: &hir::Pat) {
         debug!(
             "link_pattern(discr_cmt={:?}, root_pat={:?})",
             discr_cmt, root_pat
@@ -1100,7 +1099,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
     fn link_autoref(
         &self,
         expr: &hir::Expr,
-        expr_cmt: &mc::cmt_<'tcx>,
+        expr_cmt: &mc::Place<'tcx>,
         autoref: &adjustment::AutoBorrow<'tcx>,
     ) {
         debug!(
@@ -1130,7 +1129,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
         span: Span,
         id: hir::HirId,
         mutbl: hir::Mutability,
-        cmt_borrowed: &mc::cmt_<'tcx>,
+        cmt_borrowed: &mc::Place<'tcx>,
     ) {
         debug!(
             "link_region_from_node_type(id={:?}, mutbl={:?}, cmt_borrowed={:?})",
@@ -1153,61 +1152,35 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
         span: Span,
         borrow_region: ty::Region<'tcx>,
         borrow_kind: ty::BorrowKind,
-        borrow_cmt: &mc::cmt_<'tcx>,
+        borrow_place: &mc::Place<'tcx>,
     ) {
-        let origin = infer::DataBorrowed(borrow_cmt.ty, span);
-        self.type_must_outlive(origin, borrow_cmt.ty, borrow_region);
+        let origin = infer::DataBorrowed(borrow_place.ty, span);
+        self.type_must_outlive(origin, borrow_place.ty, borrow_region);
 
-        let mut borrow_kind = borrow_kind;
-        let mut borrow_cmt_cat = borrow_cmt.cat.clone();
-
-        loop {
+        for pointer_ty in borrow_place.deref_tys() {
             debug!(
-                "link_region(borrow_region={:?}, borrow_kind={:?}, borrow_cmt={:?})",
-                borrow_region, borrow_kind, borrow_cmt
+                "link_region(borrow_region={:?}, borrow_kind={:?}, pointer_ty={:?})",
+                borrow_region, borrow_kind, borrow_place
             );
-            match borrow_cmt_cat {
-                Categorization::Deref(ref_cmt, mc::BorrowedPtr(ref_kind, ref_region)) => {
-                    match self.link_reborrowed_region(
+            match pointer_ty.kind {
+                ty::RawPtr(_) => return,
+                ty::Ref(ref_region, _, ref_mutability) => {
+                    if self.link_reborrowed_region(
                         span,
                         borrow_region,
-                        borrow_kind,
-                        ref_cmt,
                         ref_region,
-                        ref_kind,
-                        borrow_cmt.note,
+                        ref_mutability,
                     ) {
-                        Some((c, k)) => {
-                            borrow_cmt_cat = c.cat.clone();
-                            borrow_kind = k;
-                        }
-                        None => {
-                            return;
-                        }
+                        return;
                     }
-                }
-
-                Categorization::Downcast(cmt_base, _)
-                | Categorization::Deref(cmt_base, mc::Unique)
-                | Categorization::Interior(cmt_base, _) => {
-                    // Borrowing interior or owned data requires the base
-                    // to be valid and borrowable in the same fashion.
-                    borrow_cmt_cat = cmt_base.cat.clone();
-                    borrow_kind = borrow_kind;
-                }
 
-                Categorization::Deref(_, mc::UnsafePtr(..))
-                | Categorization::StaticItem
-                | Categorization::Upvar(..)
-                | Categorization::Local(..)
-                | Categorization::ThreadLocal
-                | Categorization::Rvalue => {
-                    // These are all "base cases" with independent lifetimes
-                    // that are not subject to inference
-                    return;
                 }
+                _ => assert!(pointer_ty.is_box(), "unexpected built-in deref type {}", pointer_ty)
             }
         }
+        if let mc::PlaceBase::Upvar(upvar_id) = borrow_place.base {
+            self.link_upvar_region(span, borrow_region, upvar_id);
+        }
     }
 
     /// This is the most complicated case: the path being borrowed is
@@ -1231,83 +1204,28 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
     ///
     /// Here `bk` stands for some borrow-kind (e.g., `mut`, `uniq`, etc).
     ///
-    /// Unfortunately, there are some complications beyond the simple
-    /// scenario I just painted:
-    ///
-    /// 1. The reference `r` might in fact be a "by-ref" upvar. In that
-    ///    case, we have two jobs. First, we are inferring whether this reference
-    ///    should be an `&T`, `&mut T`, or `&uniq T` reference, and we must
-    ///    adjust that based on this borrow (e.g., if this is an `&mut` borrow,
-    ///    then `r` must be an `&mut` reference). Second, whenever we link
-    ///    two regions (here, `'z <= 'a`), we supply a *cause*, and in this
-    ///    case we adjust the cause to indicate that the reference being
-    ///    "reborrowed" is itself an upvar. This provides a nicer error message
-    ///    should something go wrong.
+    /// There is a complication beyond the simple scenario I just painted: there
+    /// may in fact be more levels of reborrowing. In the example, I said the
+    /// borrow was like `&'z *r`, but it might in fact be a borrow like
+    /// `&'z **q` where `q` has type `&'a &'b mut T`. In that case, we want to
+    /// ensure that `'z <= 'a` and `'z <= 'b`.
     ///
-    /// 2. There may in fact be more levels of reborrowing. In the
-    ///    example, I said the borrow was like `&'z *r`, but it might
-    ///    in fact be a borrow like `&'z **q` where `q` has type `&'a
-    ///    &'b mut T`. In that case, we want to ensure that `'z <= 'a`
-    ///    and `'z <= 'b`. This is explained more below.
+    /// The return value of this function indicates whether we *don't* need to
+    /// the recurse to the next reference up.
     ///
-    /// The return value of this function indicates whether we need to
-    /// recurse and process `ref_cmt` (see case 2 above).
+    /// This is explained more below.
     fn link_reborrowed_region(
         &self,
         span: Span,
         borrow_region: ty::Region<'tcx>,
-        borrow_kind: ty::BorrowKind,
-        ref_cmt: mc::cmt<'tcx>,
         ref_region: ty::Region<'tcx>,
-        mut ref_kind: ty::BorrowKind,
-        note: mc::Note,
-    ) -> Option<(mc::cmt<'tcx>, ty::BorrowKind)> {
-        // Possible upvar ID we may need later to create an entry in the
-        // maybe link map.
-
-        // Detect by-ref upvar `x`:
-        let cause = match note {
-            mc::NoteUpvarRef(ref upvar_id) => {
-                match self.tables.borrow().upvar_capture_map.get(upvar_id) {
-                    Some(&ty::UpvarCapture::ByRef(ref upvar_borrow)) => {
-                        // The mutability of the upvar may have been modified
-                        // by the above adjustment, so update our local variable.
-                        ref_kind = upvar_borrow.kind;
-
-                        infer::ReborrowUpvar(span, *upvar_id)
-                    }
-                    _ => {
-                        span_bug!(span, "Illegal upvar id: {:?}", upvar_id);
-                    }
-                }
-            }
-            mc::NoteClosureEnv(ref upvar_id) => {
-                // We don't have any mutability changes to propagate, but
-                // we do want to note that an upvar reborrow caused this
-                // link
-                infer::ReborrowUpvar(span, *upvar_id)
-            }
-            _ => infer::Reborrow(span),
-        };
-
+        ref_mutability: hir::Mutability,
+    ) -> bool {
         debug!(
             "link_reborrowed_region: {:?} <= {:?}",
             borrow_region, ref_region
         );
-        self.sub_regions(cause, borrow_region, ref_region);
-
-        // If we end up needing to recurse and establish a region link
-        // with `ref_cmt`, calculate what borrow kind we will end up
-        // needing. This will be used below.
-        //
-        // One interesting twist is that we can weaken the borrow kind
-        // when we recurse: to reborrow an `&mut` referent as mutable,
-        // borrowck requires a unique path to the `&mut` reference but not
-        // necessarily a *mutable* path.
-        let new_borrow_kind = match borrow_kind {
-            ty::ImmBorrow => ty::ImmBorrow,
-            ty::MutBorrow | ty::UniqueImmBorrow => ty::UniqueImmBorrow,
-        };
+        self.sub_regions(infer::Reborrow(span), borrow_region, ref_region);
 
         // Decide whether we need to recurse and link any regions within
         // the `ref_cmt`. This is concerned for the case where the value
@@ -1336,24 +1254,83 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
         // (Note that since we have not examined `ref_cmt.cat`, we don't
         // know whether this scenario has occurred; but I wanted to show
         // how all the types get adjusted.)
-        match ref_kind {
-            ty::ImmBorrow => {
+        match ref_mutability {
+            hir::Mutability::Immutable => {
                 // The reference being reborrowed is a shareable ref of
                 // type `&'a T`. In this case, it doesn't matter where we
                 // *found* the `&T` pointer, the memory it references will
                 // be valid and immutable for `'a`. So we can stop here.
-                //
-                // (Note that the `borrow_kind` must also be ImmBorrow or
-                // else the user is borrowed imm memory as mut memory,
-                // which means they'll get an error downstream in borrowck
-                // anyhow.)
-                return None;
+                true
+            }
+
+            hir::Mutability::Mutable => {
+                // The reference being reborrowed is either an `&mut T`. This is
+                // the case where recursion is needed.
+                false
             }
+        }
+    }
 
-            ty::MutBorrow | ty::UniqueImmBorrow => {
-                // The reference being reborrowed is either an `&mut T` or
-                // `&uniq T`. This is the case where recursion is needed.
-                return Some((ref_cmt, new_borrow_kind));
+    /// An upvar may be behind up to 2 references:
+    ///
+    /// * One can come from the reference to a "by-reference" upvar.
+    /// * Another one can come from the reference to the closure itself if it's
+    ///   a `FnMut` or `Fn` closure.
+    ///
+    /// This function links the lifetimes of those references to the lifetime
+    /// of the borrow that's provided. See [link_reborrowed_region] for some
+    /// more explanation of this in the general case.
+    ///
+    /// We also supply a *cause*, and in this case we set the cause to
+    /// indicate that the reference being "reborrowed" is itself an upvar. This
+    /// provides a nicer error message should something go wrong.
+    fn link_upvar_region(
+        &self,
+        span: Span,
+        borrow_region: ty::Region<'tcx>,
+        upvar_id: ty::UpvarId,
+    ) {
+        debug!("link_upvar_region(borrorw_region={:?}, upvar_id={:?}", borrow_region, upvar_id);
+        // A by-reference upvar can't be borrowed for longer than the
+        // upvar is borrowed from the environment.
+        match self.tables.borrow().upvar_capture(upvar_id) {
+            ty::UpvarCapture::ByRef(upvar_borrow) => {
+                self.sub_regions(
+                    infer::ReborrowUpvar(span, upvar_id),
+                    borrow_region,
+                    upvar_borrow.region,
+                );
+                if let ty::ImmBorrow = upvar_borrow.kind {
+                    debug!("link_upvar_region: capture by shared ref");
+                    return;
+                }
+            }
+            ty::UpvarCapture::ByValue => {}
+        }
+        let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id);
+        let ty = self.resolve_node_type(fn_hir_id);
+        debug!("link_upvar_region: ty={:?}", ty);
+
+        // A closure capture can't be borrowed for longer than the
+        // reference to the closure.
+        if let ty::Closure(closure_def_id, substs) = ty.kind {
+            match self.infcx.closure_kind(closure_def_id, substs) {
+                Some(ty::ClosureKind::Fn) | Some(ty::ClosureKind::FnMut) => {
+                    // Region of environment pointer
+                    let env_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion {
+                        scope: upvar_id.closure_expr_id.to_def_id(),
+                        bound_region: ty::BrEnv
+                    }));
+                    self.sub_regions(
+                        infer::ReborrowUpvar(span, upvar_id),
+                        borrow_region,
+                        env_region,
+                    );
+                }
+                Some(ty::ClosureKind::FnOnce) => {}
+                None => {
+                    span_bug!(span, "Have not inferred closure kind before regionck");
+                }
             }
         }
     }
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index 390ee0d0f83..3002459d56f 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -32,9 +32,9 @@
 
 use super::FnCtxt;
 
-use crate::middle::expr_use_visitor as euv;
-use crate::middle::mem_categorization as mc;
-use crate::middle::mem_categorization::Categorization;
+use crate::expr_use_visitor as euv;
+use crate::mem_categorization as mc;
+use crate::mem_categorization::PlaceBase;
 use rustc::hir;
 use rustc::hir::def_id::DefId;
 use rustc::hir::def_id::LocalDefId;
@@ -76,6 +76,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+    /// Analysis starting point.
     fn analyze_closure(
         &self,
         closure_hir_id: hir::HirId,
@@ -83,9 +84,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         body: &hir::Body,
         capture_clause: hir::CaptureBy,
     ) {
-        /*!
-         * Analysis starting point.
-         */
 
         debug!(
             "analyze_closure(id={:?}, body.id={:?})",
@@ -171,20 +169,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id());
         assert_eq!(body_owner_def_id, closure_def_id);
-        let region_scope_tree = &self.tcx.region_scope_tree(body_owner_def_id);
         let mut delegate = InferBorrowKind {
             fcx: self,
-            closure_def_id: closure_def_id,
+            closure_def_id,
             current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
             current_origin: None,
             adjust_upvar_captures: ty::UpvarCaptureMap::default(),
         };
-        euv::ExprUseVisitor::with_infer(
+        euv::ExprUseVisitor::new(
             &mut delegate,
             &self.infcx,
             body_owner_def_id,
             self.param_env,
-            region_scope_tree,
             &self.tables.borrow(),
         )
         .consume_body(body);
@@ -312,13 +308,10 @@ struct InferBorrowKind<'a, 'tcx> {
 impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
     fn adjust_upvar_borrow_kind_for_consume(
         &mut self,
-        cmt: &mc::cmt_<'tcx>,
+        place: &mc::Place<'tcx>,
         mode: euv::ConsumeMode,
     ) {
-        debug!(
-            "adjust_upvar_borrow_kind_for_consume(cmt={:?}, mode={:?})",
-            cmt, mode
-        );
+        debug!("adjust_upvar_borrow_kind_for_consume(place={:?}, mode={:?})", place, mode);
 
         // we only care about moves
         match mode {
@@ -329,132 +322,68 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
         }
 
         let tcx = self.fcx.tcx;
+        let upvar_id = if let PlaceBase::Upvar(upvar_id) = place.base {
+            upvar_id
+        } else {
+            return;
+        };
 
-        // watch out for a move of the deref of a borrowed pointer;
-        // for that to be legal, the upvar would have to be borrowed
-        // by value instead
-        let guarantor = cmt.guarantor();
-        debug!(
-            "adjust_upvar_borrow_kind_for_consume: guarantor={:?}",
-            guarantor
-        );
-        debug!(
-            "adjust_upvar_borrow_kind_for_consume: guarantor.cat={:?}",
-            guarantor.cat
-        );
-        if let Categorization::Deref(_, mc::BorrowedPtr(..)) = guarantor.cat {
-            debug!(
-                "adjust_upvar_borrow_kind_for_consume: found deref with note {:?}",
-                cmt.note
-            );
-            match guarantor.note {
-                mc::NoteUpvarRef(upvar_id) => {
-                    debug!(
-                        "adjust_upvar_borrow_kind_for_consume: \
-                         setting upvar_id={:?} to by value",
-                        upvar_id
-                    );
+        debug!("adjust_upvar_borrow_kind_for_consume: upvar={:?}", upvar_id);
 
-                    // to move out of an upvar, this must be a FnOnce closure
-                    self.adjust_closure_kind(
-                        upvar_id.closure_expr_id,
-                        ty::ClosureKind::FnOnce,
-                        guarantor.span,
-                        var_name(tcx, upvar_id.var_path.hir_id),
-                    );
+        // To move out of an upvar, this must be a FnOnce closure
+        self.adjust_closure_kind(
+            upvar_id.closure_expr_id,
+            ty::ClosureKind::FnOnce,
+            place.span,
+            var_name(tcx, upvar_id.var_path.hir_id),
+        );
 
-                    self.adjust_upvar_captures
-                        .insert(upvar_id, ty::UpvarCapture::ByValue);
-                }
-                mc::NoteClosureEnv(upvar_id) => {
-                    // we get just a closureenv ref if this is a
-                    // `move` closure, or if the upvar has already
-                    // been inferred to by-value. In any case, we
-                    // must still adjust the kind of the closure
-                    // to be a FnOnce closure to permit moves out
-                    // of the environment.
-                    self.adjust_closure_kind(
-                        upvar_id.closure_expr_id,
-                        ty::ClosureKind::FnOnce,
-                        guarantor.span,
-                        var_name(tcx, upvar_id.var_path.hir_id),
-                    );
-                }
-                mc::NoteIndex | mc::NoteNone => {}
-            }
-        }
+        self.adjust_upvar_captures.insert(upvar_id, ty::UpvarCapture::ByValue);
     }
 
-    /// Indicates that `cmt` is being directly mutated (e.g., assigned
-    /// to). If cmt contains any by-ref upvars, this implies that
-    /// those upvars must be borrowed using an `&mut` borrow.
-    fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: &mc::cmt_<'tcx>) {
-        debug!("adjust_upvar_borrow_kind_for_mut(cmt={:?})", cmt);
-
-        match cmt.cat.clone() {
-            Categorization::Deref(base, mc::Unique)
-            | Categorization::Interior(base, _)
-            | Categorization::Downcast(base, _) => {
-                // Interior or owned data is mutable if base is
-                // mutable, so iterate to the base.
-                self.adjust_upvar_borrow_kind_for_mut(&base);
-            }
-
-            Categorization::Deref(base, mc::BorrowedPtr(..)) => {
-                if !self.try_adjust_upvar_deref(cmt, ty::MutBorrow) {
+    /// Indicates that `place` is being directly mutated (e.g., assigned
+    /// to). If the place is based on a by-ref upvar, this implies that
+    /// the upvar must be borrowed using an `&mut` borrow.
+    fn adjust_upvar_borrow_kind_for_mut(&mut self, place: &mc::Place<'tcx>) {
+        debug!("adjust_upvar_borrow_kind_for_mut(place={:?})", place);
+
+        if let PlaceBase::Upvar(upvar_id) = place.base {
+            let mut borrow_kind = ty::MutBorrow;
+            for pointer_ty in place.deref_tys() {
+                match pointer_ty.kind {
+                    // Raw pointers don't inherit mutability.
+                    ty::RawPtr(_) => return,
                     // assignment to deref of an `&mut`
                     // borrowed pointer implies that the
                     // pointer itself must be unique, but not
                     // necessarily *mutable*
-                    self.adjust_upvar_borrow_kind_for_unique(&base);
+                    ty::Ref(.., hir::Mutability::Mutable) => borrow_kind = ty::UniqueImmBorrow,
+                    _ => (),
                 }
             }
-
-            Categorization::Deref(_, mc::UnsafePtr(..))
-            | Categorization::StaticItem
-            | Categorization::ThreadLocal
-            | Categorization::Rvalue
-            | Categorization::Local(_)
-            | Categorization::Upvar(..) => {
-                return;
-            }
+            self.adjust_upvar_deref(upvar_id, place.span, borrow_kind);
         }
     }
 
-    fn adjust_upvar_borrow_kind_for_unique(&mut self, cmt: &mc::cmt_<'tcx>) {
-        debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})", cmt);
+    fn adjust_upvar_borrow_kind_for_unique(&mut self, place: &mc::Place<'tcx>) {
+        debug!("adjust_upvar_borrow_kind_for_unique(place={:?})", place);
 
-        match cmt.cat.clone() {
-            Categorization::Deref(base, mc::Unique)
-            | Categorization::Interior(base, _)
-            | Categorization::Downcast(base, _) => {
-                // Interior or owned data is unique if base is
-                // unique.
-                self.adjust_upvar_borrow_kind_for_unique(&base);
-            }
-
-            Categorization::Deref(base, mc::BorrowedPtr(..)) => {
-                if !self.try_adjust_upvar_deref(cmt, ty::UniqueImmBorrow) {
-                    // for a borrowed pointer to be unique, its
-                    // base must be unique
-                    self.adjust_upvar_borrow_kind_for_unique(&base);
-                }
+        if let PlaceBase::Upvar(upvar_id) = place.base {
+            if place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
+                // Raw pointers don't inherit mutability.
+                return;
             }
-
-            Categorization::Deref(_, mc::UnsafePtr(..))
-            | Categorization::StaticItem
-            | Categorization::ThreadLocal
-            | Categorization::Rvalue
-            | Categorization::Local(_)
-            | Categorization::Upvar(..) => {}
+            // for a borrowed pointer to be unique, its base must be unique
+            self.adjust_upvar_deref(upvar_id, place.span, ty::UniqueImmBorrow);
         }
     }
 
-    fn try_adjust_upvar_deref(
+    fn adjust_upvar_deref(
         &mut self,
-        cmt: &mc::cmt_<'tcx>,
+        upvar_id: ty::UpvarId,
+        place_span: Span,
         borrow_kind: ty::BorrowKind,
-    ) -> bool {
+    ) {
         assert!(match borrow_kind {
             ty::MutBorrow => true,
             ty::UniqueImmBorrow => true,
@@ -465,39 +394,19 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
 
         let tcx = self.fcx.tcx;
 
-        match cmt.note {
-            mc::NoteUpvarRef(upvar_id) => {
-                // if this is an implicit deref of an
-                // upvar, then we need to modify the
-                // borrow_kind of the upvar to make sure it
-                // is inferred to mutable if necessary
-                self.adjust_upvar_borrow_kind(upvar_id, borrow_kind);
-
-                // also need to be in an FnMut closure since this is not an ImmBorrow
-                self.adjust_closure_kind(
-                    upvar_id.closure_expr_id,
-                    ty::ClosureKind::FnMut,
-                    cmt.span,
-                    var_name(tcx, upvar_id.var_path.hir_id),
-                );
-
-                true
-            }
-            mc::NoteClosureEnv(upvar_id) => {
-                // this kind of deref occurs in a `move` closure, or
-                // for a by-value upvar; in either case, to mutate an
-                // upvar, we need to be an FnMut closure
-                self.adjust_closure_kind(
-                    upvar_id.closure_expr_id,
-                    ty::ClosureKind::FnMut,
-                    cmt.span,
-                    var_name(tcx, upvar_id.var_path.hir_id),
-                );
-
-                true
-            }
-            mc::NoteIndex | mc::NoteNone => false,
-        }
+        // if this is an implicit deref of an
+        // upvar, then we need to modify the
+        // borrow_kind of the upvar to make sure it
+        // is inferred to mutable if necessary
+        self.adjust_upvar_borrow_kind(upvar_id, borrow_kind);
+
+        // also need to be in an FnMut closure since this is not an ImmBorrow
+        self.adjust_closure_kind(
+            upvar_id.closure_expr_id,
+            ty::ClosureKind::FnMut,
+            place_span,
+            var_name(tcx, upvar_id.var_path.hir_id),
+        );
     }
 
     /// We infer the borrow_kind with which to borrow upvars in a stack closure.
@@ -509,7 +418,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
         let upvar_capture = self
             .adjust_upvar_captures
             .get(&upvar_id)
-            .cloned()
+            .copied()
             .unwrap_or_else(|| self.fcx.tables.borrow().upvar_capture(upvar_id));
         debug!(
             "adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
@@ -586,29 +495,29 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
-    fn consume(&mut self, cmt: &mc::cmt_<'tcx>,mode: euv::ConsumeMode) {
-        debug!("consume(cmt={:?},mode={:?})", cmt, mode);
-        self.adjust_upvar_borrow_kind_for_consume(cmt, mode);
+    fn consume(&mut self, place: &mc::Place<'tcx>,mode: euv::ConsumeMode) {
+        debug!("consume(place={:?},mode={:?})", place, mode);
+        self.adjust_upvar_borrow_kind_for_consume(place, mode);
     }
 
-    fn borrow(&mut self, cmt: &mc::cmt_<'tcx>, bk: ty::BorrowKind) {
-        debug!("borrow(cmt={:?}, bk={:?})", cmt, bk);
+    fn borrow(&mut self, place: &mc::Place<'tcx>, bk: ty::BorrowKind) {
+        debug!("borrow(place={:?}, bk={:?})", place, bk);
 
         match bk {
             ty::ImmBorrow => {}
             ty::UniqueImmBorrow => {
-                self.adjust_upvar_borrow_kind_for_unique(cmt);
+                self.adjust_upvar_borrow_kind_for_unique(place);
             }
             ty::MutBorrow => {
-                self.adjust_upvar_borrow_kind_for_mut(cmt);
+                self.adjust_upvar_borrow_kind_for_mut(place);
             }
         }
     }
 
-    fn mutate(&mut self, assignee_cmt: &mc::cmt_<'tcx>) {
-        debug!("mutate(assignee_cmt={:?})", assignee_cmt);
+    fn mutate(&mut self, assignee_place: &mc::Place<'tcx>) {
+        debug!("mutate(assignee_place={:?})", assignee_place);
 
-        self.adjust_upvar_borrow_kind_for_mut(assignee_cmt);
+        self.adjust_upvar_borrow_kind_for_mut(assignee_place);
     }
 }
 
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc_typeck/expr_use_visitor.rs
index 00bddf50c29..03d7ab2d633 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc_typeck/expr_use_visitor.rs
@@ -5,16 +5,17 @@
 pub use self::ConsumeMode::*;
 use self::OverloadedCallType::*;
 
-use crate::hir::def::Res;
-use crate::hir::def_id::DefId;
-use crate::hir::ptr::P;
-use crate::infer::InferCtxt;
-use crate::middle::mem_categorization as mc;
-use crate::middle::region;
-use crate::ty::{self, TyCtxt, adjustment};
-
-use crate::hir::{self, PatKind};
-use std::rc::Rc;
+// Export these here so that Clippy can use them.
+pub use mc::{PlaceBase, Place, Projection};
+
+use rustc::hir::{self, PatKind};
+use rustc::hir::def::Res;
+use rustc::hir::def_id::DefId;
+use rustc::hir::ptr::P;
+use rustc::infer::InferCtxt;
+use rustc::ty::{self, TyCtxt, adjustment};
+
+use crate::mem_categorization as mc;
 use syntax_pos::Span;
 
 ///////////////////////////////////////////////////////////////////////////
@@ -23,15 +24,15 @@ use syntax_pos::Span;
 /// This trait defines the callbacks you can expect to receive when
 /// employing the ExprUseVisitor.
 pub trait Delegate<'tcx> {
-    // The value found at `cmt` is either copied or moved, depending
+    // The value found at `place` is either copied or moved, depending
     // on mode.
-    fn consume(&mut self, cmt: &mc::cmt_<'tcx>, mode: ConsumeMode);
+    fn consume(&mut self, place: &mc::Place<'tcx>, mode: ConsumeMode);
 
-    // The value found at `cmt` is being borrowed with kind `bk`.
-    fn borrow(&mut self, cmt: &mc::cmt_<'tcx>, bk: ty::BorrowKind);
+    // The value found at `place` is being borrowed with kind `bk`.
+    fn borrow(&mut self, place: &mc::Place<'tcx>, bk: ty::BorrowKind);
 
-    // The path at `cmt` is being assigned to.
-    fn mutate(&mut self, assignee_cmt: &mc::cmt_<'tcx>);
+    // The path at `place` is being assigned to.
+    fn mutate(&mut self, assignee_place: &mc::Place<'tcx>);
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
@@ -85,7 +86,6 @@ impl OverloadedCallType {
 pub struct ExprUseVisitor<'a, 'tcx> {
     mc: mc::MemCategorizationContext<'a, 'tcx>,
     delegate: &'a mut dyn Delegate<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
 }
 
 // If the MC results in an error, it's because the type check
@@ -112,49 +112,22 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
     ///
     /// - `delegate` -- who receives the callbacks
     /// - `param_env` --- parameter environment for trait lookups (esp. pertaining to `Copy`)
-    /// - `region_scope_tree` --- region scope tree for the code being analyzed
     /// - `tables` --- typeck results for the code being analyzed
-    ///
-    /// See also `with_infer`, which is used *during* typeck.
     pub fn new(
         delegate: &'a mut (dyn Delegate<'tcx> + 'a),
-        tcx: TyCtxt<'tcx>,
-        body_owner: DefId,
-        param_env: ty::ParamEnv<'tcx>,
-        region_scope_tree: &'a region::ScopeTree,
-        tables: &'a ty::TypeckTables<'tcx>,
-    ) -> Self {
-        ExprUseVisitor {
-            mc: mc::MemCategorizationContext::new(tcx,
-                                                  param_env,
-                                                  body_owner,
-                                                  region_scope_tree,
-                                                  tables),
-            delegate,
-            param_env,
-        }
-    }
-}
-
-impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
-    pub fn with_infer(
-        delegate: &'a mut (dyn Delegate<'tcx> + 'a),
         infcx: &'a InferCtxt<'a, 'tcx>,
         body_owner: DefId,
         param_env: ty::ParamEnv<'tcx>,
-        region_scope_tree: &'a region::ScopeTree,
         tables: &'a ty::TypeckTables<'tcx>,
     ) -> Self {
         ExprUseVisitor {
-            mc: mc::MemCategorizationContext::with_infer(
+            mc: mc::MemCategorizationContext::new(
                 infcx,
                 param_env,
                 body_owner,
-                region_scope_tree,
                 tables,
             ),
             delegate,
-            param_env,
         }
     }
 
@@ -165,26 +138,23 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
             let param_ty = return_if_err!(self.mc.pat_ty_adjusted(&param.pat));
             debug!("consume_body: param_ty = {:?}", param_ty);
 
-            let param_cmt = Rc::new(self.mc.cat_rvalue(
-                param.hir_id,
-                param.pat.span,
-                param_ty));
+            let param_place = self.mc.cat_rvalue(param.hir_id, param.pat.span, param_ty);
 
-            self.walk_irrefutable_pat(param_cmt, &param.pat);
+            self.walk_irrefutable_pat(&param_place, &param.pat);
         }
 
         self.consume_expr(&body.value);
     }
 
     fn tcx(&self) -> TyCtxt<'tcx> {
-        self.mc.tcx
+        self.mc.tcx()
     }
 
-    fn delegate_consume(&mut self, cmt: &mc::cmt_<'tcx>) {
-        debug!("delegate_consume(cmt={:?})", cmt);
+    fn delegate_consume(&mut self, place: &Place<'tcx>) {
+        debug!("delegate_consume(place={:?})", place);
 
-        let mode = copy_or_move(&self.mc, self.param_env, cmt);
-        self.delegate.consume(cmt, mode);
+        let mode = copy_or_move(&self.mc, place);
+        self.delegate.consume(place, mode);
     }
 
     fn consume_exprs(&mut self, exprs: &[hir::Expr]) {
@@ -196,22 +166,22 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
     pub fn consume_expr(&mut self, expr: &hir::Expr) {
         debug!("consume_expr(expr={:?})", expr);
 
-        let cmt = return_if_err!(self.mc.cat_expr(expr));
-        self.delegate_consume(&cmt);
+        let place = return_if_err!(self.mc.cat_expr(expr));
+        self.delegate_consume(&place);
         self.walk_expr(expr);
     }
 
     fn mutate_expr(&mut self, expr: &hir::Expr) {
-        let cmt = return_if_err!(self.mc.cat_expr(expr));
-        self.delegate.mutate(&cmt);
+        let place = return_if_err!(self.mc.cat_expr(expr));
+        self.delegate.mutate(&place);
         self.walk_expr(expr);
     }
 
     fn borrow_expr(&mut self, expr: &hir::Expr, bk: ty::BorrowKind) {
         debug!("borrow_expr(expr={:?}, bk={:?})", expr, bk);
 
-        let cmt = return_if_err!(self.mc.cat_expr(expr));
-        self.delegate.borrow(&cmt, bk);
+        let place = return_if_err!(self.mc.cat_expr(expr));
+        self.delegate.borrow(&place, bk);
 
         self.walk_expr(expr)
     }
@@ -263,12 +233,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
             }
 
             hir::ExprKind::Match(ref discr, ref arms, _) => {
-                let discr_cmt = Rc::new(return_if_err!(self.mc.cat_expr(&discr)));
+                let discr_place = return_if_err!(self.mc.cat_expr(&discr));
                 self.borrow_expr(&discr, ty::ImmBorrow);
 
                 // treatment of the discriminant is handled while walking the arms.
                 for arm in arms {
-                    self.walk_arm(discr_cmt.clone(), arm);
+                    self.walk_arm(&discr_place, arm);
                 }
             }
 
@@ -414,8 +384,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
             // "assigns", which is handled by
             // `walk_pat`:
             self.walk_expr(&expr);
-            let init_cmt = Rc::new(return_if_err!(self.mc.cat_expr(&expr)));
-            self.walk_irrefutable_pat(init_cmt, &local.pat);
+            let init_place = return_if_err!(self.mc.cat_expr(&expr));
+            self.walk_irrefutable_pat(&init_place, &local.pat);
         }
     }
 
@@ -446,11 +416,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
             None => { return; }
         };
 
-        let with_cmt = Rc::new(return_if_err!(self.mc.cat_expr(&with_expr)));
+        let with_place = return_if_err!(self.mc.cat_expr(&with_expr));
 
         // Select just those fields of the `with`
         // expression that will actually be used
-        match with_cmt.ty.kind {
+        match with_place.ty.kind {
             ty::Adt(adt, substs) if adt.is_struct() => {
                 // Consume those fields of the with expression that are needed.
                 for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() {
@@ -458,14 +428,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                         self.tcx().field_index(f.hir_id, self.mc.tables) == f_index
                     });
                     if !is_mentioned {
-                        let cmt_field = self.mc.cat_field(
+                        let field_place = self.mc.cat_projection(
                             &*with_expr,
-                            with_cmt.clone(),
-                            f_index,
-                            with_field.ident,
-                            with_field.ty(self.tcx(), substs)
+                            with_place.clone(),
+                            with_field.ty(self.tcx(), substs),
                         );
-                        self.delegate_consume(&cmt_field);
+                        self.delegate_consume(&field_place);
                     }
                 }
             }
@@ -492,7 +460,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
     // process.
     fn walk_adjustment(&mut self, expr: &hir::Expr) {
         let adjustments = self.mc.tables.expr_adjustments(expr);
-        let mut cmt = return_if_err!(self.mc.cat_expr_unadjusted(expr));
+        let mut place = return_if_err!(self.mc.cat_expr_unadjusted(expr));
         for adjustment in adjustments {
             debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
             match adjustment.kind {
@@ -500,7 +468,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                 adjustment::Adjust::Pointer(_)  => {
                     // Creating a closure/fn-pointer or unsizing consumes
                     // the input and stores it into the resulting rvalue.
-                    self.delegate_consume(&cmt);
+                    self.delegate_consume(&place);
                 }
 
                 adjustment::Adjust::Deref(None) => {}
@@ -512,47 +480,47 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                 // this is an autoref of `x`.
                 adjustment::Adjust::Deref(Some(ref deref)) => {
                     let bk = ty::BorrowKind::from_mutbl(deref.mutbl);
-                    self.delegate.borrow(&cmt, bk);
+                    self.delegate.borrow(&place, bk);
                 }
 
                 adjustment::Adjust::Borrow(ref autoref) => {
-                    self.walk_autoref(expr, &cmt, autoref);
+                    self.walk_autoref(expr, &place, autoref);
                 }
             }
-            cmt = return_if_err!(self.mc.cat_expr_adjusted(expr, cmt, &adjustment));
+            place = return_if_err!(self.mc.cat_expr_adjusted(expr, place, &adjustment));
         }
     }
 
     /// Walks the autoref `autoref` applied to the autoderef'd
-    /// `expr`. `cmt_base` is the mem-categorized form of `expr`
+    /// `expr`. `base_place` is the mem-categorized form of `expr`
     /// after all relevant autoderefs have occurred.
     fn walk_autoref(&mut self,
                     expr: &hir::Expr,
-                    cmt_base: &mc::cmt_<'tcx>,
+                    base_place: &mc::Place<'tcx>,
                     autoref: &adjustment::AutoBorrow<'tcx>) {
-        debug!("walk_autoref(expr.hir_id={} cmt_base={:?} autoref={:?})",
+        debug!("walk_autoref(expr.hir_id={} base_place={:?} autoref={:?})",
                expr.hir_id,
-               cmt_base,
+               base_place,
                autoref);
 
         match *autoref {
             adjustment::AutoBorrow::Ref(_, m) => {
-                self.delegate.borrow(cmt_base, ty::BorrowKind::from_mutbl(m.into()));
+                self.delegate.borrow(base_place, ty::BorrowKind::from_mutbl(m.into()));
             }
 
             adjustment::AutoBorrow::RawPtr(m) => {
-                debug!("walk_autoref: expr.hir_id={} cmt_base={:?}",
+                debug!("walk_autoref: expr.hir_id={} base_place={:?}",
                        expr.hir_id,
-                       cmt_base);
+                       base_place);
 
 
-                self.delegate.borrow(cmt_base, ty::BorrowKind::from_mutbl(m));
+                self.delegate.borrow(base_place, ty::BorrowKind::from_mutbl(m));
             }
         }
     }
 
-    fn walk_arm(&mut self, discr_cmt: mc::cmt<'tcx>, arm: &hir::Arm) {
-        self.walk_pat(discr_cmt.clone(), &arm.pat);
+    fn walk_arm(&mut self, discr_place: &Place<'tcx>, arm: &hir::Arm) {
+        self.walk_pat(discr_place, &arm.pat);
 
         if let Some(hir::Guard::If(ref e)) = arm.guard {
             self.consume_expr(e)
@@ -563,22 +531,22 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
 
     /// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
     /// let binding, and *not* a match arm or nested pat.)
-    fn walk_irrefutable_pat(&mut self, cmt_discr: mc::cmt<'tcx>, pat: &hir::Pat) {
-        self.walk_pat(cmt_discr, pat);
+    fn walk_irrefutable_pat(&mut self, discr_place: &Place<'tcx>, pat: &hir::Pat) {
+        self.walk_pat(discr_place, pat);
     }
 
 
     /// The core driver for walking a pattern
-    fn walk_pat(&mut self, cmt_discr: mc::cmt<'tcx>, pat: &hir::Pat) {
-        debug!("walk_pat(cmt_discr={:?}, pat={:?})", cmt_discr, pat);
+    fn walk_pat(&mut self, discr_place: &Place<'tcx>, pat: &hir::Pat) {
+        debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat);
 
         let tcx = self.tcx();
-        let ExprUseVisitor { ref mc, ref mut delegate, param_env } = *self;
-        return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |cmt_pat, pat| {
+        let ExprUseVisitor { ref mc, ref mut delegate } = *self;
+        return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| {
             if let PatKind::Binding(_, canonical_id, ..) = pat.kind {
                 debug!(
-                    "walk_pat: binding cmt_pat={:?} pat={:?}",
-                    cmt_pat,
+                    "walk_pat: binding place={:?} pat={:?}",
+                    place,
                     pat,
                 );
                 if let Some(&bm) = mc.tables.pat_binding_modes().get(pat.hir_id) {
@@ -591,20 +559,20 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                     // Each match binding is effectively an assignment to the
                     // binding being produced.
                     let def = Res::Local(canonical_id);
-                    if let Ok(ref binding_cmt) = mc.cat_res(pat.hir_id, pat.span, pat_ty, def) {
-                        delegate.mutate(binding_cmt);
+                    if let Ok(ref binding_place) = mc.cat_res(pat.hir_id, pat.span, pat_ty, def) {
+                        delegate.mutate(binding_place);
                     }
 
                     // It is also a borrow or copy/move of the value being matched.
                     match bm {
                         ty::BindByReference(m) => {
                             let bk = ty::BorrowKind::from_mutbl(m);
-                            delegate.borrow(&cmt_pat, bk);
+                            delegate.borrow(place, bk);
                         }
                         ty::BindByValue(..) => {
-                            let mode = copy_or_move(mc, param_env, &cmt_pat);
+                            let mode = copy_or_move(mc, place);
                             debug!("walk_pat binding consuming pat");
-                            delegate.consume(&cmt_pat, mode);
+                            delegate.consume(place, mode);
                         }
                     }
                 } else {
@@ -625,16 +593,18 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                     closure_expr_id: closure_def_id.to_local(),
                 };
                 let upvar_capture = self.mc.tables.upvar_capture(upvar_id);
-                let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.hir_id,
-                                                                   fn_decl_span,
-                                                                   var_id));
+                let captured_place = return_if_err!(self.cat_captured_var(
+                    closure_expr.hir_id,
+                    fn_decl_span,
+                    var_id,
+                ));
                 match upvar_capture {
                     ty::UpvarCapture::ByValue => {
-                        let mode = copy_or_move(&self.mc, self.param_env, &cmt_var);
-                        self.delegate.consume(&cmt_var, mode);
+                        let mode = copy_or_move(&self.mc, &captured_place);
+                        self.delegate.consume(&captured_place, mode);
                     }
                     ty::UpvarCapture::ByRef(upvar_borrow) => {
-                        self.delegate.borrow(&cmt_var, upvar_borrow.kind);
+                        self.delegate.borrow(&captured_place, upvar_borrow.kind);
                     }
                 }
             }
@@ -645,8 +615,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                         closure_hir_id: hir::HirId,
                         closure_span: Span,
                         var_id: hir::HirId)
-                        -> mc::McResult<mc::cmt_<'tcx>> {
-        // Create the cmt for the variable being borrowed, from the
+                        -> mc::McResult<mc::Place<'tcx>> {
+        // Create the place for the variable being borrowed, from the
         // perspective of the creator (parent) of the closure.
         let var_ty = self.mc.node_ty(var_id)?;
         self.mc.cat_res(closure_hir_id, closure_span, var_ty, Res::Local(var_id))
@@ -655,10 +625,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
 
 fn copy_or_move<'a, 'tcx>(
     mc: &mc::MemCategorizationContext<'a, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cmt: &mc::cmt_<'tcx>,
+    place: &Place<'tcx>,
 ) -> ConsumeMode {
-    if !mc.type_is_copy_modulo_regions(param_env, cmt.ty, cmt.span) {
+    if !mc.type_is_copy_modulo_regions(place.ty, place.span) {
         Move
     } else {
         Copy
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 0f147f92b5f..c606feab087 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -75,6 +75,9 @@ This API is completely unstable and subject to change.
 
 #[macro_use] extern crate rustc;
 
+// This is used by Clippy.
+pub mod expr_use_visitor;
+
 mod astconv;
 mod check;
 mod check_unused;
@@ -83,6 +86,7 @@ mod collect;
 mod constrained_generic_params;
 mod structured_errors;
 mod impl_wf_check;
+mod mem_categorization;
 mod namespace;
 mod outlives;
 mod variance;
diff --git a/src/librustc_typeck/mem_categorization.rs b/src/librustc_typeck/mem_categorization.rs
new file mode 100644
index 00000000000..84e5d234da9
--- /dev/null
+++ b/src/librustc_typeck/mem_categorization.rs
@@ -0,0 +1,707 @@
+//! # Categorization
+//!
+//! The job of the categorization module is to analyze an expression to
+//! determine what kind of memory is used in evaluating it (for example,
+//! where dereferences occur and what kind of pointer is dereferenced;
+//! whether the memory is mutable, etc.).
+//!
+//! Categorization effectively transforms all of our expressions into
+//! expressions of the following forms (the actual enum has many more
+//! possibilities, naturally, but they are all variants of these base
+//! forms):
+//!
+//!     E = rvalue    // some computed rvalue
+//!       | x         // address of a local variable or argument
+//!       | *E        // deref of a ptr
+//!       | E.comp    // access to an interior component
+//!
+//! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an
+//! address where the result is to be found. If Expr is a place, then this
+//! is the address of the place. If `Expr` is an rvalue, this is the address of
+//! some temporary spot in memory where the result is stored.
+//!
+//! Now, `cat_expr()` classifies the expression `Expr` and the address `A = ToAddr(Expr)`
+//! as follows:
+//!
+//! - `cat`: what kind of expression was this? This is a subset of the
+//!   full expression forms which only includes those that we care about
+//!   for the purpose of the analysis.
+//! - `mutbl`: mutability of the address `A`.
+//! - `ty`: the type of data found at the address `A`.
+//!
+//! The resulting categorization tree differs somewhat from the expressions
+//! themselves. For example, auto-derefs are explicit. Also, an index a[b] is
+//! decomposed into two operations: a dereference to reach the array data and
+//! then an index to jump forward to the relevant item.
+//!
+//! ## By-reference upvars
+//!
+//! One part of the codegen which may be non-obvious is that we translate
+//! closure upvars into the dereference of a borrowed pointer; this more closely
+//! resembles the runtime codegen. So, for example, if we had:
+//!
+//!     let mut x = 3;
+//!     let y = 5;
+//!     let inc = || x += y;
+//!
+//! Then when we categorize `x` (*within* the closure) we would yield a
+//! result of `*x'`, effectively, where `x'` is a `Categorization::Upvar` reference
+//! tied to `x`. The type of `x'` will be a borrowed pointer.
+
+use rustc::hir;
+use rustc::hir::PatKind;
+use rustc::hir::def_id::DefId;
+use rustc::hir::def::{Res, DefKind};
+use rustc::infer::InferCtxt;
+use rustc::ty::adjustment;
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::fold::TypeFoldable;
+
+use syntax_pos::Span;
+
+use rustc_data_structures::fx::FxIndexMap;
+
+#[derive(Clone, Debug)]
+pub enum PlaceBase {
+    /// A temporary variable
+    Rvalue,
+    /// A named `static` item
+    StaticItem,
+    /// A named local variable
+    Local(hir::HirId),
+    /// An upvar referenced by closure env
+    Upvar(ty::UpvarId),
+}
+
+#[derive(Clone, Debug)]
+pub enum Projection<'tcx> {
+    /// A dereference of a pointer, reference or `Box<T>` of the given type
+    Deref(Ty<'tcx>),
+    /// An index or a field
+    Other,
+}
+
+/// A `Place` represents how a value is located in memory.
+///
+/// This is an HIR version of `mir::Place`
+#[derive(Clone, Debug)]
+pub struct Place<'tcx> {
+    /// `HirId` of the expression or pattern producing this value.
+    pub hir_id: hir::HirId,
+    /// The `Span` of the expression or pattern producing this value.
+    pub span: Span,
+    /// The type of the `Place`
+    pub ty: Ty<'tcx>,
+    /// The "outermost" place that holds this value.
+    pub base: PlaceBase,
+    /// How this place is derived from the base place.
+    pub projections: Vec<Projection<'tcx>>,
+}
+
+impl<'tcx> Place<'tcx> {
+    /// Returns an iterator of the types that have to be dereferenced to access
+    /// the `Place`.
+    ///
+    /// The types are in the reverse order that they are applied. So if
+    /// `x: &*const u32` and the `Place` is `**x`, then the types returned are
+    ///`*const u32` then `&*const u32`.
+    crate fn deref_tys(&self) -> impl Iterator<Item=Ty<'tcx>> + '_ {
+        self.projections.iter().rev().filter_map(|proj| if let Projection::Deref(deref_ty) = *proj {
+            Some(deref_ty)
+        } else {
+            None
+        })
+    }
+}
+
+crate trait HirNode {
+    fn hir_id(&self) -> hir::HirId;
+    fn span(&self) -> Span;
+}
+
+impl HirNode for hir::Expr {
+    fn hir_id(&self) -> hir::HirId { self.hir_id }
+    fn span(&self) -> Span { self.span }
+}
+
+impl HirNode for hir::Pat {
+    fn hir_id(&self) -> hir::HirId { self.hir_id }
+    fn span(&self) -> Span { self.span }
+}
+
+#[derive(Clone)]
+crate struct MemCategorizationContext<'a, 'tcx> {
+    crate tables: &'a ty::TypeckTables<'tcx>,
+    infcx: &'a InferCtxt<'a, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    body_owner: DefId,
+    upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
+}
+
+crate type McResult<T> = Result<T, ()>;
+
+impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
+    /// Creates a `MemCategorizationContext`.
+    crate fn new(
+        infcx: &'a InferCtxt<'a, 'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        body_owner: DefId,
+        tables: &'a ty::TypeckTables<'tcx>,
+    ) -> MemCategorizationContext<'a, 'tcx> {
+        MemCategorizationContext {
+            tables,
+            infcx,
+            param_env,
+            body_owner,
+            upvars: infcx.tcx.upvars(body_owner),
+        }
+    }
+
+    crate fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    crate fn type_is_copy_modulo_regions(
+        &self,
+        ty: Ty<'tcx>,
+        span: Span,
+    ) -> bool {
+        self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span)
+    }
+
+    fn resolve_vars_if_possible<T>(&self, value: &T) -> T
+        where T: TypeFoldable<'tcx>
+    {
+        self.infcx.resolve_vars_if_possible(value)
+    }
+
+    fn is_tainted_by_errors(&self) -> bool {
+        self.infcx.is_tainted_by_errors()
+    }
+
+    fn resolve_type_vars_or_error(&self,
+                                  id: hir::HirId,
+                                  ty: Option<Ty<'tcx>>)
+                                  -> McResult<Ty<'tcx>> {
+        match ty {
+            Some(ty) => {
+                let ty = self.resolve_vars_if_possible(&ty);
+                if ty.references_error() || ty.is_ty_var() {
+                    debug!("resolve_type_vars_or_error: error from {:?}", ty);
+                    Err(())
+                } else {
+                    Ok(ty)
+                }
+            }
+            // FIXME
+            None if self.is_tainted_by_errors() => Err(()),
+            None => {
+                bug!("no type for node {}: {} in mem_categorization",
+                     id, self.tcx().hir().node_to_string(id));
+            }
+        }
+    }
+
+    crate fn node_ty(&self, hir_id: hir::HirId) -> McResult<Ty<'tcx>> {
+        self.resolve_type_vars_or_error(hir_id, self.tables.node_type_opt(hir_id))
+    }
+
+    fn expr_ty(&self, expr: &hir::Expr) -> McResult<Ty<'tcx>> {
+        self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_opt(expr))
+    }
+
+    crate fn expr_ty_adjusted(&self, expr: &hir::Expr) -> McResult<Ty<'tcx>> {
+        self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_adjusted_opt(expr))
+    }
+
+    /// Returns the type of value that this pattern matches against.
+    /// Some non-obvious cases:
+    ///
+    /// - a `ref x` binding matches against a value of type `T` and gives
+    ///   `x` the type `&T`; we return `T`.
+    /// - a pattern with implicit derefs (thanks to default binding
+    ///   modes #42640) may look like `Some(x)` but in fact have
+    ///   implicit deref patterns attached (e.g., it is really
+    ///   `&Some(x)`). In that case, we return the "outermost" type
+    ///   (e.g., `&Option<T>).
+    crate fn pat_ty_adjusted(&self, pat: &hir::Pat) -> McResult<Ty<'tcx>> {
+        // Check for implicit `&` types wrapping the pattern; note
+        // that these are never attached to binding patterns, so
+        // actually this is somewhat "disjoint" from the code below
+        // that aims to account for `ref x`.
+        if let Some(vec) = self.tables.pat_adjustments().get(pat.hir_id) {
+            if let Some(first_ty) = vec.first() {
+                debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty);
+                return Ok(first_ty);
+            }
+        }
+
+        self.pat_ty_unadjusted(pat)
+    }
+
+
+    /// Like `pat_ty`, but ignores implicit `&` patterns.
+    fn pat_ty_unadjusted(&self, pat: &hir::Pat) -> McResult<Ty<'tcx>> {
+        let base_ty = self.node_ty(pat.hir_id)?;
+        debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty);
+
+        // This code detects whether we are looking at a `ref x`,
+        // and if so, figures out what the type *being borrowed* is.
+        let ret_ty = match pat.kind {
+            PatKind::Binding(..) => {
+                let bm = *self.tables
+                              .pat_binding_modes()
+                              .get(pat.hir_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) {
+                        Some(t) => t.ty,
+                        None => {
+                            debug!("By-ref binding of non-derefable type {:?}", base_ty);
+                            return Err(());
+                        }
+                    }
+                } else {
+                    base_ty
+                }
+            }
+            _ => base_ty,
+        };
+        debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty);
+
+        Ok(ret_ty)
+    }
+
+    crate fn cat_expr(&self, expr: &hir::Expr) -> McResult<Place<'tcx>> {
+        // This recursion helper avoids going through *too many*
+        // adjustments, since *only* non-overloaded deref recurses.
+        fn helper<'a, 'tcx>(
+            mc: &MemCategorizationContext<'a, 'tcx>,
+            expr: &hir::Expr,
+            adjustments: &[adjustment::Adjustment<'tcx>],
+        ) -> McResult<Place<'tcx>> {
+            match adjustments.split_last() {
+                None => mc.cat_expr_unadjusted(expr),
+                Some((adjustment, previous)) => {
+                    mc.cat_expr_adjusted_with(expr, || helper(mc, expr, previous), adjustment)
+                }
+            }
+        }
+
+        helper(self, expr, self.tables.expr_adjustments(expr))
+    }
+
+    crate fn cat_expr_adjusted(&self, expr: &hir::Expr,
+                             previous: Place<'tcx>,
+                             adjustment: &adjustment::Adjustment<'tcx>)
+                             -> McResult<Place<'tcx>> {
+        self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment)
+    }
+
+    fn cat_expr_adjusted_with<F>(&self, expr: &hir::Expr,
+                                 previous: F,
+                                 adjustment: &adjustment::Adjustment<'tcx>)
+                                 -> McResult<Place<'tcx>>
+        where F: FnOnce() -> McResult<Place<'tcx>>
+    {
+        debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr);
+        let target = self.resolve_vars_if_possible(&adjustment.target);
+        match adjustment.kind {
+            adjustment::Adjust::Deref(overloaded) => {
+                // Equivalent to *expr or something similar.
+                let base = if let Some(deref) = overloaded {
+                    let ref_ty = self.tcx().mk_ref(deref.region, ty::TypeAndMut {
+                        ty: target,
+                        mutbl: deref.mutbl,
+                    });
+                    self.cat_rvalue(expr.hir_id, expr.span, ref_ty)
+                } else {
+                    previous()?
+                };
+                self.cat_deref(expr, base)
+            }
+
+            adjustment::Adjust::NeverToAny |
+            adjustment::Adjust::Pointer(_) |
+            adjustment::Adjust::Borrow(_) => {
+                // Result is an rvalue.
+                Ok(self.cat_rvalue(expr.hir_id, expr.span, target))
+            }
+        }
+    }
+
+    crate fn cat_expr_unadjusted(&self, expr: &hir::Expr) -> McResult<Place<'tcx>> {
+        debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr);
+
+        let expr_ty = self.expr_ty(expr)?;
+        match expr.kind {
+            hir::ExprKind::Unary(hir::UnDeref, ref e_base) => {
+                if self.tables.is_method_call(expr) {
+                    self.cat_overloaded_place(expr, e_base)
+                } else {
+                    let base = self.cat_expr(&e_base)?;
+                    self.cat_deref(expr, base)
+                }
+            }
+
+            hir::ExprKind::Field(ref base, _) => {
+                let base = self.cat_expr(&base)?;
+                debug!("cat_expr(cat_field): id={} expr={:?} base={:?}",
+                       expr.hir_id,
+                       expr,
+                       base);
+                Ok(self.cat_projection(expr, base, expr_ty))
+            }
+
+            hir::ExprKind::Index(ref base, _) => {
+                if self.tables.is_method_call(expr) {
+                    // If this is an index implemented by a method call, then it
+                    // will include an implicit deref of the result.
+                    // The call to index() returns a `&T` value, which
+                    // is an rvalue. That is what we will be
+                    // dereferencing.
+                    self.cat_overloaded_place(expr, base)
+                } else {
+                    let base = self.cat_expr(&base)?;
+                    Ok(self.cat_projection(expr, base, expr_ty))
+                }
+            }
+
+            hir::ExprKind::Path(ref qpath) => {
+                let res = self.tables.qpath_res(qpath, expr.hir_id);
+                self.cat_res(expr.hir_id, expr.span, expr_ty, res)
+            }
+
+            hir::ExprKind::Type(ref e, _) => {
+                self.cat_expr(&e)
+            }
+
+            hir::ExprKind::AddrOf(..) | hir::ExprKind::Call(..) |
+            hir::ExprKind::Assign(..) | hir::ExprKind::AssignOp(..) |
+            hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) |
+            hir::ExprKind::Unary(..) | hir::ExprKind::Yield(..) |
+            hir::ExprKind::MethodCall(..) | hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) |
+            hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) |
+            hir::ExprKind::Binary(..) |
+            hir::ExprKind::Block(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..) |
+            hir::ExprKind::Lit(..) | hir::ExprKind::Break(..) |
+            hir::ExprKind::Continue(..) | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) |
+            hir::ExprKind::InlineAsm(..) | hir::ExprKind::Box(..) | hir::ExprKind::Err => {
+                Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty))
+            }
+        }
+    }
+
+    crate fn cat_res(&self,
+                   hir_id: hir::HirId,
+                   span: Span,
+                   expr_ty: Ty<'tcx>,
+                   res: Res)
+                   -> McResult<Place<'tcx>> {
+        debug!("cat_res: id={:?} expr={:?} def={:?}",
+               hir_id, expr_ty, res);
+
+        match res {
+            Res::Def(DefKind::Ctor(..), _)
+            | Res::Def(DefKind::Const, _)
+            | Res::Def(DefKind::ConstParam, _)
+            | Res::Def(DefKind::AssocConst, _)
+            | Res::Def(DefKind::Fn, _)
+            | Res::Def(DefKind::Method, _)
+            | Res::SelfCtor(..) => {
+                Ok(self.cat_rvalue(hir_id, span, expr_ty))
+            }
+
+            Res::Def(DefKind::Static, _) => {
+                Ok(Place {
+                    hir_id,
+                    span,
+                    ty: expr_ty,
+                    base: PlaceBase::StaticItem,
+                    projections: Vec::new(),
+                })
+            }
+
+            Res::Local(var_id) => {
+                if self.upvars.map_or(false, |upvars| upvars.contains_key(&var_id)) {
+                    self.cat_upvar(hir_id, span, var_id)
+                } else {
+                    Ok(Place {
+                        hir_id,
+                        span,
+                        ty: expr_ty,
+                        base: PlaceBase::Local(var_id),
+                        projections: Vec::new(),
+                    })
+                }
+            }
+
+            def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def)
+        }
+    }
+
+    /// Categorize an upvar.
+    ///
+    /// Note: the actual upvar access contains invisible derefs of closure
+    /// environment and upvar reference as appropriate. Only regionck cares
+    /// about these dereferences, so we let it compute them as needed.
+    fn cat_upvar(
+        &self,
+        hir_id: hir::HirId,
+        span: Span,
+        var_id: hir::HirId,
+    ) -> McResult<Place<'tcx>> {
+        let closure_expr_def_id = self.body_owner;
+
+        let upvar_id = ty::UpvarId {
+            var_path: ty::UpvarPath { hir_id: var_id },
+            closure_expr_id: closure_expr_def_id.to_local(),
+        };
+        let var_ty = self.node_ty(var_id)?;
+
+        let ret = Place {
+            hir_id,
+            span,
+            ty: var_ty,
+            base: PlaceBase::Upvar(upvar_id),
+            projections: Vec::new(),
+        };
+
+        debug!("cat_upvar ret={:?}", ret);
+        Ok(ret)
+    }
+
+    crate fn cat_rvalue(&self, hir_id: hir::HirId, span: Span, expr_ty: Ty<'tcx>) -> Place<'tcx> {
+        debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span);
+        let ret = Place {
+            hir_id,
+            span,
+            base: PlaceBase::Rvalue,
+            projections: Vec::new(),
+            ty: expr_ty,
+        };
+        debug!("cat_rvalue ret={:?}", ret);
+        ret
+    }
+
+    crate fn cat_projection<N: HirNode>(
+        &self,
+        node: &N,
+        base_place: Place<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Place<'tcx> {
+        let mut projections = base_place.projections;
+        projections.push(Projection::Other);
+        let ret = Place {
+            hir_id: node.hir_id(),
+            span: node.span(),
+            ty,
+            base: base_place.base,
+            projections,
+        };
+        debug!("cat_field ret {:?}", ret);
+        ret
+    }
+
+    fn cat_overloaded_place(
+        &self,
+        expr: &hir::Expr,
+        base: &hir::Expr,
+    ) -> McResult<Place<'tcx>> {
+        debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base);
+
+        // Reconstruct the output assuming it's a reference with the
+        // same region and mutability as the receiver. This holds for
+        // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
+        let place_ty = self.expr_ty(expr)?;
+        let base_ty = self.expr_ty_adjusted(base)?;
+
+        let (region, mutbl) = match base_ty.kind {
+            ty::Ref(region, _, mutbl) => (region, mutbl),
+            _ => span_bug!(expr.span, "cat_overloaded_place: base is not a reference")
+        };
+        let ref_ty = self.tcx().mk_ref(region, ty::TypeAndMut {
+            ty: place_ty,
+            mutbl,
+        });
+
+        let base = self.cat_rvalue(expr.hir_id, expr.span, ref_ty);
+        self.cat_deref(expr, base)
+    }
+
+    fn cat_deref(
+        &self,
+        node: &impl HirNode,
+        base_place: Place<'tcx>,
+    ) -> McResult<Place<'tcx>> {
+        debug!("cat_deref: base_place={:?}", base_place);
+
+        let base_ty = base_place.ty;
+        let deref_ty = match base_ty.builtin_deref(true) {
+            Some(mt) => mt.ty,
+            None => {
+                debug!("explicit deref of non-derefable type: {:?}", base_ty);
+                return Err(());
+            }
+        };
+        let mut projections = base_place.projections;
+        projections.push(Projection::Deref(base_ty));
+
+        let ret = Place {
+            hir_id: node.hir_id(),
+            span: node.span(),
+            ty: deref_ty,
+            base: base_place.base,
+            projections,
+        };
+        debug!("cat_deref ret {:?}", ret);
+        Ok(ret)
+    }
+
+    crate fn cat_pattern<F>(&self, place: Place<'tcx>, pat: &hir::Pat, mut op: F) -> McResult<()>
+        where F: FnMut(&Place<'tcx>, &hir::Pat),
+    {
+        self.cat_pattern_(place, pat, &mut op)
+    }
+
+    // FIXME(#19596) This is a workaround, but there should be a better way to do this
+    fn cat_pattern_<F>(&self, mut place: Place<'tcx>, pat: &hir::Pat, op: &mut F) -> McResult<()>
+        where F: FnMut(&Place<'tcx>, &hir::Pat)
+    {
+        // Here, `place` is the `Place` being matched and pat is the pattern it
+        // is being matched against.
+        //
+        // In general, the way that this works is that we walk down the pattern,
+        // constructing a `Place` that represents the path that will be taken
+        // to reach the value being matched.
+
+        debug!("cat_pattern(pat={:?}, place={:?})", pat, place);
+
+        // If (pattern) adjustments are active for this pattern, adjust the `Place` correspondingly.
+        // `Place`s are constructed differently from patterns. For example, in
+        //
+        // ```
+        // match foo {
+        //     &&Some(x, ) => { ... },
+        //     _ => { ... },
+        // }
+        // ```
+        //
+        // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the
+        // corresponding `Place` we start with the `Place` for `foo`, and then, by traversing the
+        // pattern, try to answer the question: given the address of `foo`, how is `x` reached?
+        //
+        // `&&Some(x,)` `place_foo`
+        //  `&Some(x,)` `deref { place_foo}`
+        //   `Some(x,)` `deref { deref { place_foo }}`
+        //        (x,)` `field0 { deref { deref { place_foo }}}` <- resulting place
+        //
+        // The above example has no adjustments. If the code were instead the (after adjustments,
+        // equivalent) version
+        //
+        // ```
+        // match foo {
+        //     Some(x, ) => { ... },
+        //     _ => { ... },
+        // }
+        // ```
+        //
+        // Then we see that to get the same result, we must start with
+        // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
+        // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
+        for _ in 0..self.tables
+                        .pat_adjustments()
+                        .get(pat.hir_id)
+                        .map(|v| v.len())
+                        .unwrap_or(0)
+        {
+            debug!("cat_pattern: applying adjustment to place={:?}", place);
+            place = self.cat_deref(pat, place)?;
+        }
+        let place = place; // lose mutability
+        debug!("cat_pattern: applied adjustment derefs to get place={:?}", place);
+
+        // Invoke the callback, but only now, after the `place` has adjusted.
+        //
+        // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that
+        // case, the initial `place` will be that for `&Some(3)` and the pattern is `Some(x)`. We
+        // don't want to call `op` with these incompatible values. As written, what happens instead
+        // is that `op` is called with the adjusted place (that for `*&Some(3)`) and the pattern
+        // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)`
+        // result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with
+        // that (where the `ref` on `x` is implied).
+        op(&place, pat);
+
+        match pat.kind {
+            PatKind::TupleStruct(_, ref subpats, _)
+            | PatKind::Tuple(ref subpats, _) => {
+                // S(p1, ..., pN) or (p1, ..., pN)
+                for subpat in subpats.iter() {
+                    let subpat_ty = self.pat_ty_adjusted(&subpat)?;
+                    let sub_place = self.cat_projection(pat, place.clone(), subpat_ty);
+                    self.cat_pattern_(sub_place, &subpat, op)?;
+                }
+            }
+
+            PatKind::Struct(_, ref field_pats, _) => {
+                // S { f1: p1, ..., fN: pN }
+                for fp in field_pats {
+                    let field_ty = self.pat_ty_adjusted(&fp.pat)?;
+                    let field_place = self.cat_projection(pat, place.clone(), field_ty);
+                    self.cat_pattern_(field_place, &fp.pat, op)?;
+                }
+            }
+
+            PatKind::Or(ref pats) => {
+                for pat in pats {
+                    self.cat_pattern_(place.clone(), &pat, op)?;
+                }
+            }
+
+            PatKind::Binding(.., Some(ref subpat)) => {
+                self.cat_pattern_(place, &subpat, op)?;
+            }
+
+            PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => {
+                // box p1, &p1, &mut p1.  we can ignore the mutability of
+                // PatKind::Ref since that information is already contained
+                // in the type.
+                let subplace = self.cat_deref(pat, place)?;
+                self.cat_pattern_(subplace, &subpat, op)?;
+            }
+
+            PatKind::Slice(ref before, ref slice, ref after) => {
+                let element_ty = match place.ty.builtin_index() {
+                    Some(ty) => ty,
+                    None => {
+                        debug!("explicit index of non-indexable type {:?}", place);
+                        return Err(());
+                    }
+                };
+                let elt_place = self.cat_projection(pat, place.clone(), element_ty);
+                for before_pat in before {
+                    self.cat_pattern_(elt_place.clone(), &before_pat, op)?;
+                }
+                if let Some(ref slice_pat) = *slice {
+                    let slice_pat_ty = self.pat_ty_adjusted(&slice_pat)?;
+                    let slice_place = self.cat_projection(pat, place, slice_pat_ty);
+                    self.cat_pattern_(slice_place, &slice_pat, op)?;
+                }
+                for after_pat in after {
+                    self.cat_pattern_(elt_place.clone(), &after_pat, op)?;
+                }
+            }
+
+            PatKind::Path(_) | PatKind::Binding(.., None) |
+            PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild => {
+                // always ok
+            }
+        }
+
+        Ok(())
+    }
+}
diff --git a/src/test/ui/issues/issue-4335.rs b/src/test/ui/issues/issue-4335.rs
index a10ae9a1243..c5914a17cf9 100644
--- a/src/test/ui/issues/issue-4335.rs
+++ b/src/test/ui/issues/issue-4335.rs
@@ -4,8 +4,7 @@ fn id<T>(t: T) -> T { t }
 
 fn f<'r, T>(v: &'r T) -> Box<dyn FnMut() -> T + 'r> {
     id(Box::new(|| *v))
-        //~^ ERROR E0373
-        //~| ERROR E0507
+        //~^ ERROR E0507
 }
 
 fn main() {
diff --git a/src/test/ui/issues/issue-4335.stderr b/src/test/ui/issues/issue-4335.stderr
index ca1c0b68d2a..f187969ff4e 100644
--- a/src/test/ui/issues/issue-4335.stderr
+++ b/src/test/ui/issues/issue-4335.stderr
@@ -6,25 +6,6 @@ LL | fn f<'r, T>(v: &'r T) -> Box<dyn FnMut() -> T + 'r> {
 LL |     id(Box::new(|| *v))
    |                    ^^ move occurs because `*v` has type `T`, which does not implement the `Copy` trait
 
-error[E0373]: closure may outlive the current function, but it borrows `v`, which is owned by the current function
-  --> $DIR/issue-4335.rs:6:17
-   |
-LL |     id(Box::new(|| *v))
-   |                 ^^  - `v` is borrowed here
-   |                 |
-   |                 may outlive borrowed value `v`
-   |
-note: closure is returned here
-  --> $DIR/issue-4335.rs:6:5
-   |
-LL |     id(Box::new(|| *v))
-   |     ^^^^^^^^^^^^^^^^^^^
-help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword
-   |
-LL |     id(Box::new(move || *v))
-   |                 ^^^^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0373, E0507.
-For more information about an error, try `rustc --explain E0373`.
+For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/regions/regions-addr-of-upvar-self.stderr b/src/test/ui/regions/regions-addr-of-upvar-self.stderr
index 7a051b8ac83..005800d50a4 100644
--- a/src/test/ui/regions/regions-addr-of-upvar-self.stderr
+++ b/src/test/ui/regions/regions-addr-of-upvar-self.stderr
@@ -9,7 +9,7 @@ note: first, the lifetime cannot outlive the lifetime `'_` as defined on the bod
    |
 LL |         let _f = || {
    |                  ^^
-note: ...so that reference does not outlive borrowed content
+note: ...so that closure can access `self`
   --> $DIR/regions-addr-of-upvar-self.rs:10:41
    |
 LL |             let p: &'static mut usize = &mut self.food;
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr
index 9287353245e..68ec8d2ba82 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr
@@ -2,7 +2,7 @@ error[E0525]: expected a closure that implements the `Fn` trait, but this closur
   --> $DIR/unboxed-closures-infer-fn-once-move-from-projection.rs:14:13
    |
 LL |     let c = || drop(y.0);
-   |             ^^^^^^^^-^^^
+   |             ^^^^^^^^---^
    |             |       |
    |             |       closure is `FnOnce` because it moves the variable `y` out of its environment
    |             this closure implements `FnOnce`, not `Fn`