]> git.lizzy.rs Git - rust.git/commitdiff
mem_categorization -- parameterize over TYPER interface, treat upvar refs as
authorNiko Matsakis <niko@alum.mit.edu>
Fri, 7 Feb 2014 19:41:58 +0000 (14:41 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Tue, 11 Feb 2014 21:55:24 +0000 (16:55 -0500)
deref'd borrowed pointers

src/librustc/middle/mem_categorization.rs

index 93f439b653c59e1df0dc1a5a22d211c205e0fdb6..b0e31a8e443947ae360f2bdd7d9c7c1afc5c461d 100644 (file)
@@ -22,7 +22,7 @@
  * forms):
  *
  *     E = rvalue    // some computed rvalue
- *       | x         // address of a local variable, arg, or upvar
+ *       | x         // address of a local variable or argument
  *       | *E        // deref of a ptr
  *       | E.comp    // access to an interior component
  *
  * themselves.  For example, auto-derefs are explicit.  Also, an index a[b] is
  * decomposed into two operations: a derefence to reach the array data and
  * then an index to jump forward to the relevant item.
+ *
+ * ## By-reference upvars
+ *
+ * One part of the translation which may be non-obvious is that we translate
+ * closure upvars into the dereference of a borrow pointer; this more closely
+ * resembles the runtime translation. 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 `cat_upvar` reference
+ * tied to `x`. The type of `x'` will be a borrowed pointer.
  */
 
 
 use middle::ty;
-use middle::typeck;
 use util::ppaux::{ty_to_str, region_ptr_to_str, Repr};
-use util::common::indenter;
 
 use syntax::ast::{MutImmutable, MutMutable};
 use syntax::ast;
@@ -63,15 +75,15 @@ pub enum categorization {
     cat_rvalue(ty::Region),            // temporary val, argument is its scope
     cat_static_item,
     cat_copied_upvar(CopiedUpvar),     // upvar copied into @fn or ~fn env
-    cat_stack_upvar(cmt),              // by ref upvar from ||
+    cat_upvar(ty::UpvarId, ty::UpvarBorrow), // by ref upvar from stack closure
     cat_local(ast::NodeId),            // local variable
     cat_arg(ast::NodeId),              // formal argument
     cat_deref(cmt, uint, PointerKind), // deref of a ptr
     cat_interior(cmt, InteriorKind),   // something interior: field, tuple, etc
-    cat_downcast(cmt),                 // selects a particular enum variant (..)
+    cat_downcast(cmt),                 // selects a particular enum variant (*1)
     cat_discr(cmt, ast::NodeId),       // match discriminant (see preserve())
 
-    // (..) downcast is only required if the enum has more than one variant
+    // (*1) downcast is only required if the enum has more than one variant
 }
 
 #[deriving(Eq)]
@@ -83,10 +95,10 @@ pub struct CopiedUpvar {
 // different kinds of pointers:
 #[deriving(Eq, IterBytes)]
 pub enum PointerKind {
-    uniq_ptr,
-    gc_ptr,
-    region_ptr(ast::Mutability, ty::Region),
-    unsafe_ptr(ast::Mutability)
+    OwnedPtr,
+    GcPtr,
+    BorrowedPtr(ty::BorrowKind, ty::Region),
+    UnsafePtr(ast::Mutability),
 }
 
 // We use the term "interior" to mean "something reachable from the
@@ -114,7 +126,7 @@ pub enum ElementKind {
 pub enum MutabilityCategory {
     McImmutable, // Immutable.
     McDeclared,  // Directly declared as mutable.
-    McInherited  // Inherited from the fact that owner is mutable.
+    McInherited, // Inherited from the fact that owner is mutable.
 }
 
 // `cmt`: "Category, Mutability, and Type".
@@ -159,30 +171,32 @@ pub fn opt_deref_kind(t: ty::t) -> Option<deref_kind> {
         ty::ty_vec(_, ty::vstore_uniq) |
         ty::ty_str(ty::vstore_uniq) |
         ty::ty_closure(ty::ClosureTy {sigil: ast::OwnedSigil, ..}) => {
-            Some(deref_ptr(uniq_ptr))
+            Some(deref_ptr(OwnedPtr))
         }
 
         ty::ty_rptr(r, mt) |
         ty::ty_vec(mt, ty::vstore_slice(r)) => {
-            Some(deref_ptr(region_ptr(mt.mutbl, r)))
+            let kind = ty::BorrowKind::from_mutbl(mt.mutbl);
+            Some(deref_ptr(BorrowedPtr(kind, r)))
         }
 
         ty::ty_trait(_, _, ty::RegionTraitStore(r), m, _) => {
-            Some(deref_ptr(region_ptr(m, r)))
+            let kind = ty::BorrowKind::from_mutbl(m);
+            Some(deref_ptr(BorrowedPtr(kind, r)))
         }
 
         ty::ty_str(ty::vstore_slice(r)) |
         ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil,
                                       region: r, ..}) => {
-            Some(deref_ptr(region_ptr(ast::MutImmutable, r)))
+            Some(deref_ptr(BorrowedPtr(ty::ImmBorrow, r)))
         }
 
-        ty::ty_box(_) => {
-            Some(deref_ptr(gc_ptr))
+        ty::ty_box(..) => {
+            Some(deref_ptr(GcPtr))
         }
 
         ty::ty_ptr(ref mt) => {
-            Some(deref_ptr(unsafe_ptr(mt.mutbl)))
+            Some(deref_ptr(UnsafePtr(mt.mutbl)))
         }
 
         ty::ty_enum(..) |
@@ -210,53 +224,7 @@ pub fn deref_kind(tcx: ty::ctxt, t: ty::t) -> deref_kind {
     }
 }
 
-pub fn cat_expr(tcx: ty::ctxt,
-                method_map: typeck::method_map,
-                expr: &ast::Expr)
-             -> cmt {
-    let mcx = &mem_categorization_ctxt {
-        tcx: tcx, method_map: method_map
-    };
-    return mcx.cat_expr(expr);
-}
-
-pub fn cat_expr_unadjusted(tcx: ty::ctxt,
-                           method_map: typeck::method_map,
-                           expr: &ast::Expr)
-                        -> cmt {
-    let mcx = &mem_categorization_ctxt {
-        tcx: tcx, method_map: method_map
-    };
-    return mcx.cat_expr_unadjusted(expr);
-}
-
-pub fn cat_expr_autoderefd(
-    tcx: ty::ctxt,
-    method_map: typeck::method_map,
-    expr: &ast::Expr,
-    autoderefs: uint) -> cmt
-{
-    let mcx = &mem_categorization_ctxt {
-        tcx: tcx, method_map: method_map
-    };
-    return mcx.cat_expr_autoderefd(expr, autoderefs);
-}
-
-pub fn cat_def(
-    tcx: ty::ctxt,
-    method_map: typeck::method_map,
-    expr_id: ast::NodeId,
-    expr_span: Span,
-    expr_ty: ty::t,
-    def: ast::Def) -> cmt {
-
-    let mcx = &mem_categorization_ctxt {
-        tcx: tcx, method_map: method_map
-    };
-    return mcx.cat_def(expr_id, expr_span, expr_ty, def);
-}
-
-pub trait ast_node {
+trait ast_node {
     fn id(&self) -> ast::NodeId;
     fn span(&self) -> Span;
 }
@@ -271,9 +239,37 @@ fn id(&self) -> ast::NodeId { self.id }
     fn span(&self) -> Span { self.span }
 }
 
-pub struct mem_categorization_ctxt {
-    tcx: ty::ctxt,
-    method_map: typeck::method_map,
+pub struct MemCategorizationContext<TYPER> {
+    typer: TYPER
+}
+
+pub type McResult<T> = Result<T, ()>;
+
+/**
+ * The `Typer` trait provides the interface fro the mem-categorization
+ * module to the results of the type check. It can be used to query
+ * the type assigned to an expression node, to inquire after adjustments,
+ * and so on.
+ *
+ * This interface is needed because mem-categorization is used from
+ * two places: `regionck` and `borrowck`. `regionck` executes before
+ * type inference is complete, and hence derives types and so on from
+ * intermediate tables.  This also implies that type errors can occur,
+ * and hence `node_ty()` and friends return a `Result` type -- any
+ * error will propagate back up through the mem-categorization
+ * routines.
+ *
+ * In the borrow checker, in contrast, type checking is complete and we
+ * know that no errors have occurred, so we simply consult the tcx and we
+ * can be sure that only `Ok` results will occur.
+ */
+pub trait Typer {
+    fn tcx(&self) -> ty::ctxt;
+    fn node_ty(&mut self, id: ast::NodeId) -> McResult<ty::t>;
+    fn adjustment(&mut self, node_id: ast::NodeId) -> Option<@ty::AutoAdjustment>;
+    fn is_method_call(&mut self, id: ast::NodeId) -> bool;
+    fn temporary_scope(&mut self, rvalue_id: ast::NodeId) -> Option<ast::NodeId>;
+    fn upvar_borrow(&mut self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow;
 }
 
 impl ToStr for MutabilityCategory {
@@ -290,18 +286,45 @@ pub fn from_mutbl(m: ast::Mutability) -> MutabilityCategory {
         }
     }
 
+    pub fn from_borrow_kind(borrow_kind: ty::BorrowKind) -> MutabilityCategory {
+        match borrow_kind {
+            ty::ImmBorrow => McImmutable,
+            ty::UniqueImmBorrow => McImmutable,
+            ty::MutBorrow => McDeclared,
+        }
+    }
+
+    pub fn from_pointer_kind(base_mutbl: MutabilityCategory,
+                             ptr: PointerKind) -> MutabilityCategory {
+        match ptr {
+            OwnedPtr => {
+                base_mutbl.inherit()
+            }
+            BorrowedPtr(borrow_kind, _) => {
+                MutabilityCategory::from_borrow_kind(borrow_kind)
+            }
+            GcPtr => {
+                McImmutable
+            }
+            UnsafePtr(m) => {
+                MutabilityCategory::from_mutbl(m)
+            }
+        }
+    }
+
     pub fn inherit(&self) -> MutabilityCategory {
         match *self {
             McImmutable => McImmutable,
             McDeclared => McInherited,
-            McInherited => McInherited
+            McInherited => McInherited,
         }
     }
 
     pub fn is_mutable(&self) -> bool {
         match *self {
             McImmutable => false,
-            McDeclared | McInherited => true
+            McInherited => true,
+            McDeclared => true,
         }
     }
 
@@ -320,55 +343,78 @@ pub fn to_user_str(&self) -> &'static str {
     }
 }
 
-impl mem_categorization_ctxt {
-    pub fn expr_ty(&self, expr: &ast::Expr) -> ty::t {
-        ty::expr_ty(self.tcx, expr)
+macro_rules! if_ok(
+    ($inp: expr) => (
+        match $inp {
+            Ok(v) => { v }
+            Err(e) => { return Err(e); }
+        }
+    )
+)
+
+impl<TYPER:Typer> MemCategorizationContext<TYPER> {
+    fn tcx(&self) -> ty::ctxt {
+        self.typer.tcx()
+    }
+
+    fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> {
+        self.typer.adjustment(id)
+    }
+
+    fn expr_ty(&mut self, expr: &ast::Expr) -> McResult<ty::t> {
+        self.typer.node_ty(expr.id)
     }
 
-    pub fn pat_ty(&self, pat: &ast::Pat) -> ty::t {
-        ty::node_id_to_type(self.tcx, pat.id)
+    fn expr_ty_adjusted(&mut self, expr: &ast::Expr) -> McResult<ty::t> {
+        let unadjusted_ty = if_ok!(self.expr_ty(expr));
+        let adjustment = self.adjustment(expr.id);
+        Ok(ty::adjust_ty(self.tcx(), expr.span, unadjusted_ty, adjustment))
     }
 
-    pub fn cat_expr(&self, expr: &ast::Expr) -> cmt {
-        let adjustments = self.tcx.adjustments.borrow();
-        match adjustments.get().find(&expr.id) {
+    fn node_ty(&mut self, id: ast::NodeId) -> McResult<ty::t> {
+        self.typer.node_ty(id)
+    }
+
+    fn pat_ty(&mut self, pat: @ast::Pat) -> McResult<ty::t> {
+        self.typer.node_ty(pat.id)
+    }
+
+    pub fn cat_expr(&mut self, expr: &ast::Expr) -> McResult<cmt> {
+        match self.adjustment(expr.id) {
             None => {
                 // No adjustments.
                 self.cat_expr_unadjusted(expr)
             }
 
             Some(adjustment) => {
-                match **adjustment {
+                match *adjustment {
                     ty::AutoObject(..) => {
                         // Implicity casts a concrete object to trait object
                         // so just patch up the type
-                        let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
-                        @cmt_ {
-                            ty: expr_ty,
-                            ..*self.cat_expr_unadjusted(expr)
-                        }
+                        let expr_ty = if_ok!(self.expr_ty_adjusted(expr));
+                        let expr_cmt = if_ok!(self.cat_expr_unadjusted(expr));
+                        Ok(@cmt_ {ty: expr_ty, ..*expr_cmt})
                     }
 
                     ty::AutoAddEnv(..) => {
                         // Convert a bare fn to a closure by adding NULL env.
                         // Result is an rvalue.
-                        let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
-                        self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)
+                        let expr_ty = if_ok!(self.expr_ty_adjusted(expr));
+                        Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
                     }
 
-                    ty::AutoDerefRef(ty::AutoDerefRef {
-                        autoref: Some(_),
-                    ..}) => {
+                    ty::AutoDerefRef(
+                        ty::AutoDerefRef {
+                            autoref: Some(_), ..}) => {
                         // Equivalent to &*expr or something similar.
                         // Result is an rvalue.
-                        let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
-                        self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)
+                        let expr_ty = if_ok!(self.expr_ty_adjusted(expr));
+                        Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
                     }
 
-                    ty::AutoDerefRef(ty::AutoDerefRef {
-                            autoref: None,
-                            autoderefs: autoderefs
-                    }) => {
+                    ty::AutoDerefRef(
+                        ty::AutoDerefRef {
+                            autoref: None, autoderefs: autoderefs}) => {
                         // Equivalent to *expr or something similar.
                         self.cat_expr_autoderefd(expr, autoderefs)
                     }
@@ -377,53 +423,51 @@ pub fn cat_expr(&self, expr: &ast::Expr) -> cmt {
         }
     }
 
-    pub fn cat_expr_autoderefd(&self, expr: &ast::Expr, autoderefs: uint)
-                               -> cmt {
-        let mut cmt = self.cat_expr_unadjusted(expr);
+    pub fn cat_expr_autoderefd(&mut self, expr: &ast::Expr, autoderefs: uint)
+                               -> McResult<cmt> {
+        let mut cmt = if_ok!(self.cat_expr_unadjusted(expr));
         for deref in range(1u, autoderefs + 1) {
             cmt = self.cat_deref(expr, cmt, deref);
         }
-        return cmt;
+        return Ok(cmt);
     }
 
-    pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> cmt {
+    pub fn cat_expr_unadjusted(&mut self, expr: &ast::Expr) -> McResult<cmt> {
         debug!("cat_expr: id={} expr={}",
-               expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr()));
+               expr.id,
+               expr.repr(self.tcx()));
 
-        let expr_ty = self.expr_ty(expr);
+        let expr_ty = if_ok!(self.expr_ty(expr));
         match expr.node {
           ast::ExprUnary(_, ast::UnDeref, e_base) => {
-            let method_map = self.method_map.borrow();
-            if method_map.get().contains_key(&expr.id) {
-                return self.cat_rvalue_node(expr.id(), expr.span(), expr_ty);
+            if self.typer.is_method_call(expr.id) {
+                return Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty));
             }
 
-            let base_cmt = self.cat_expr(e_base);
-            self.cat_deref(expr, base_cmt, 0)
+            let base_cmt = if_ok!(self.cat_expr(e_base));
+            Ok(self.cat_deref(expr, base_cmt, 0))
           }
 
           ast::ExprField(base, f_name, _) => {
             // Method calls are now a special syntactic form,
             // so `a.b` should always be a field.
-            let method_map = self.method_map.borrow();
-            assert!(!method_map.get().contains_key(&expr.id));
+            assert!(!self.typer.is_method_call(expr.id));
 
-            let base_cmt = self.cat_expr(base);
-            self.cat_field(expr, base_cmt, f_name, self.expr_ty(expr))
+            let base_cmt = if_ok!(self.cat_expr(base));
+            Ok(self.cat_field(expr, base_cmt, f_name, expr_ty))
           }
 
           ast::ExprIndex(_, base, _) => {
-            let method_map = self.method_map.borrow();
-            if method_map.get().contains_key(&expr.id) {
-                return self.cat_rvalue_node(expr.id(), expr.span(), expr_ty);
+            if self.typer.is_method_call(expr.id) {
+                return Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty));
             }
 
-            let base_cmt = self.cat_expr(base);
-            self.cat_index(expr, base_cmt, 0)
+            let base_cmt = if_ok!(self.cat_expr(base));
+            Ok(self.cat_index(expr, base_cmt, 0))
           }
 
           ast::ExprPath(_) => {
-            let def_map = self.tcx.def_map.borrow();
+            let def_map = self.tcx().def_map.borrow();
             let def = def_map.get().get_copy(&expr.id);
             self.cat_def(expr.id, expr.span, expr_ty, def)
           }
@@ -441,49 +485,48 @@ pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> cmt {
           ast::ExprLit(..) | ast::ExprBreak(..) | ast::ExprMac(..) |
           ast::ExprAgain(..) | ast::ExprStruct(..) | ast::ExprRepeat(..) |
           ast::ExprInlineAsm(..) | ast::ExprBox(..) => {
-            return self.cat_rvalue_node(expr.id(), expr.span(), expr_ty);
+            Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
           }
 
           ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop")
         }
     }
 
-    pub fn cat_def(&self,
+    pub fn cat_def(&mut self,
                    id: ast::NodeId,
                    span: Span,
                    expr_ty: ty::t,
                    def: ast::Def)
-                   -> cmt {
+                   -> McResult<cmt> {
         debug!("cat_def: id={} expr={}",
-               id, ty_to_str(self.tcx, expr_ty));
-
+               id, expr_ty.repr(self.tcx()));
 
         match def {
           ast::DefStruct(..) | ast::DefVariant(..) => {
-                self.cat_rvalue_node(id, span, expr_ty)
+                Ok(self.cat_rvalue_node(id, span, expr_ty))
           }
           ast::DefFn(..) | ast::DefStaticMethod(..) | ast::DefMod(_) |
           ast::DefForeignMod(_) | ast::DefStatic(_, false) |
           ast::DefUse(_) | ast::DefTrait(_) | ast::DefTy(_) | ast::DefPrimTy(_) |
           ast::DefTyParam(..) | ast::DefTyParamBinder(..) | ast::DefRegion(_) |
           ast::DefLabel(_) | ast::DefSelfTy(..) | ast::DefMethod(..) => {
-              @cmt_ {
+              Ok(@cmt_ {
                   id:id,
                   span:span,
                   cat:cat_static_item,
                   mutbl: McImmutable,
                   ty:expr_ty
-              }
+              })
           }
 
           ast::DefStatic(_, true) => {
-              @cmt_ {
+              Ok(@cmt_ {
                   id:id,
                   span:span,
                   cat:cat_static_item,
                   mutbl: McDeclared,
                   ty:expr_ty
-              }
+              })
           }
 
           ast::DefArg(vid, binding_mode) => {
@@ -495,17 +538,17 @@ pub fn cat_def(&self,
                 ast::BindByValue(ast::MutMutable) => McDeclared,
                 _ => McImmutable
             };
-            @cmt_ {
+            Ok(@cmt_ {
                 id: id,
                 span: span,
                 cat: cat_arg(vid),
                 mutbl: m,
                 ty:expr_ty
-            }
+            })
           }
 
-          ast::DefUpvar(upvar_id, inner, fn_node_id, _) => {
-              let ty = ty::node_id_to_type(self.tcx, fn_node_id);
+          ast::DefUpvar(var_id, _, fn_node_id, _) => {
+              let ty = if_ok!(self.node_ty(fn_node_id));
               match ty::get(ty).sty {
                   ty::ty_closure(ref closure_ty) => {
                       // Decide whether to use implicit reference or by copy/move
@@ -523,33 +566,25 @@ pub fn cat_def(&self,
 
                       };
                       if var_is_refd {
-                          let upvar_cmt =
-                              self.cat_def(id, span, expr_ty, *inner);
-                          @cmt_ {
-                              id:id,
-                              span:span,
-                              cat:cat_stack_upvar(upvar_cmt),
-                              mutbl:upvar_cmt.mutbl.inherit(),
-                              ty:upvar_cmt.ty
-                          }
+                          self.cat_upvar(id, span, var_id, fn_node_id)
                       } else {
                           // FIXME #2152 allow mutation of moved upvars
-                          @cmt_ {
+                          Ok(@cmt_ {
                               id:id,
                               span:span,
                               cat:cat_copied_upvar(CopiedUpvar {
-                                  upvar_id: upvar_id,
+                                  upvar_id: var_id,
                                   onceness: closure_ty.onceness}),
                               mutbl:McImmutable,
                               ty:expr_ty
-                          }
+                          })
                       }
                   }
                   _ => {
-                      self.tcx.sess.span_bug(
+                      self.tcx().sess.span_bug(
                           span,
-                          format!("upvar of non-closure {:?} - {}",
-                               fn_node_id, ty.repr(self.tcx)));
+                          format!("Upvar of non-closure {} - {}",
+                                  fn_node_id, ty.repr(self.tcx())));
                   }
               }
           }
@@ -562,19 +597,73 @@ pub fn cat_def(&self,
                 _ => McImmutable
             };
 
-            @cmt_ {
+            Ok(@cmt_ {
                 id: id,
                 span: span,
                 cat: cat_local(vid),
                 mutbl: m,
                 ty: expr_ty
-            }
+            })
           }
         }
     }
 
-    pub fn cat_rvalue_node(&self, id: ast::NodeId, span: Span, expr_ty: ty::t) -> cmt {
-        match self.tcx.region_maps.temporary_scope(id) {
+    fn cat_upvar(&mut self,
+                 id: ast::NodeId,
+                 span: Span,
+                 var_id: ast::NodeId,
+                 fn_node_id: ast::NodeId)
+                 -> McResult<cmt> {
+        /*!
+         * Upvars through a closure are in fact indirect
+         * references. That is, when a closure refers to a
+         * variable from a parent stack frame like `x = 10`,
+         * that is equivalent to `*x_ = 10` where `x_` is a
+         * borrowed pointer (`&mut x`) created when the closure
+         * was created and store in the environment. This
+         * equivalence is expose in the mem-categorization.
+         */
+
+        let upvar_id = ty::UpvarId { var_id: var_id,
+                                     closure_expr_id: fn_node_id };
+
+        let upvar_borrow = self.typer.upvar_borrow(upvar_id);
+
+        let var_ty = if_ok!(self.node_ty(var_id));
+
+        // We can't actually represent the types of all upvars
+        // as user-describable types, since upvars support const
+        // and unique-imm borrows! Therefore, we cheat, and just
+        // give err type. Nobody should be inspecting this type anyhow.
+        let upvar_ty = ty::mk_err();
+
+        let base_cmt = @cmt_ {
+            id:id,
+            span:span,
+            cat:cat_upvar(upvar_id, upvar_borrow),
+            mutbl:McImmutable,
+            ty:upvar_ty,
+        };
+
+        let ptr = BorrowedPtr(upvar_borrow.kind, upvar_borrow.region);
+
+        let deref_cmt = @cmt_ {
+            id:id,
+            span:span,
+            cat:cat_deref(base_cmt, 0, ptr),
+            mutbl:MutabilityCategory::from_borrow_kind(upvar_borrow.kind),
+            ty:var_ty,
+        };
+
+        Ok(deref_cmt)
+    }
+
+    pub fn cat_rvalue_node(&mut self,
+                           id: ast::NodeId,
+                           span: Span,
+                           expr_ty: ty::t)
+                           -> cmt {
+        match self.typer.temporary_scope(id) {
             Some(scope) => {
                 self.cat_rvalue(id, span, ty::ReScope(scope), expr_ty)
             }
@@ -584,7 +673,7 @@ pub fn cat_rvalue_node(&self, id: ast::NodeId, span: Span, expr_ty: ty::t) -> cm
         }
     }
 
-    pub fn cat_rvalue(&self,
+    pub fn cat_rvalue(&mut self,
                       cmt_id: ast::NodeId,
                       span: Span,
                       temp_scope: ty::Region,
@@ -602,7 +691,7 @@ pub fn cat_rvalue(&self,
     /// component is inherited from the base it is a part of. For
     /// example, a record field is mutable if it is declared mutable
     /// or if the container is mutable.
-    pub fn inherited_mutability(&self,
+    pub fn inherited_mutability(&mut self,
                                 base_m: MutabilityCategory,
                                 interior_m: ast::Mutability)
                                 -> MutabilityCategory {
@@ -612,7 +701,7 @@ pub fn inherited_mutability(&self,
         }
     }
 
-    pub fn cat_field<N:ast_node>(&self,
+    pub fn cat_field<N:ast_node>(&mut self,
                                  node: &N,
                                  base_cmt: cmt,
                                  f_name: ast::Ident,
@@ -627,7 +716,7 @@ pub fn cat_field<N:ast_node>(&self,
         }
     }
 
-    pub fn cat_deref_fn_or_obj<N:ast_node>(&self,
+    pub fn cat_deref_fn_or_obj<N:ast_node>(&mut self,
                                            node: &N,
                                            base_cmt: cmt,
                                            deref_cnt: uint)
@@ -638,11 +727,11 @@ pub fn cat_deref_fn_or_obj<N:ast_node>(&self,
         // know what type lies at the other end, so we just call it
         // `()` (the empty tuple).
 
-        let opaque_ty = ty::mk_tup(self.tcx, ~[]);
+        let opaque_ty = ty::mk_tup(self.tcx(), ~[]);
         return self.cat_deref_common(node, base_cmt, deref_cnt, opaque_ty);
     }
 
-    pub fn cat_deref<N:ast_node>(&self,
+    pub fn cat_deref<N:ast_node>(&mut self,
                                  node: &N,
                                  base_cmt: cmt,
                                  deref_cnt: uint)
@@ -650,37 +739,28 @@ pub fn cat_deref<N:ast_node>(&self,
         let mt = match ty::deref(base_cmt.ty, true) {
             Some(mt) => mt,
             None => {
-                self.tcx.sess.span_bug(
+                self.tcx().sess.span_bug(
                     node.span(),
-                    format!("explicit deref of non-derefable type: {}",
-                         ty_to_str(self.tcx, base_cmt.ty)));
+                    format!("Explicit deref of non-derefable type: {}",
+                            base_cmt.ty.repr(self.tcx())));
             }
         };
 
         return self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty);
     }
 
-    pub fn cat_deref_common<N:ast_node>(&self,
+    pub fn cat_deref_common<N:ast_node>(&mut self,
                                         node: &N,
                                         base_cmt: cmt,
                                         deref_cnt: uint,
                                         deref_ty: ty::t)
                                         -> cmt {
-        match deref_kind(self.tcx, base_cmt.ty) {
+        match deref_kind(self.tcx(), base_cmt.ty) {
             deref_ptr(ptr) => {
                 // for unique ptrs, we inherit mutability from the
                 // owning reference.
-                let m = match ptr {
-                    uniq_ptr => {
-                        base_cmt.mutbl.inherit()
-                    }
-                    gc_ptr => {
-                        McImmutable
-                    }
-                    region_ptr(m, _) | unsafe_ptr(m) => {
-                        MutabilityCategory::from_mutbl(m)
-                    }
-                };
+                let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl,
+                                                              ptr);
 
                 @cmt_ {
                     id:node.id(),
@@ -704,7 +784,7 @@ pub fn cat_deref_common<N:ast_node>(&self,
         }
     }
 
-    pub fn cat_index<N:ast_node>(&self,
+    pub fn cat_index<N:ast_node>(&mut self,
                                  elt: &N,
                                  base_cmt: cmt,
                                  derefs: uint)
@@ -743,28 +823,18 @@ pub fn cat_index<N:ast_node>(&self,
         let element_ty = match ty::index(base_cmt.ty) {
           Some(ref mt) => mt.ty,
           None => {
-            self.tcx.sess.span_bug(
+            self.tcx().sess.span_bug(
                 elt.span(),
-                format!("explicit index of non-index type `{}`",
-                     ty_to_str(self.tcx, base_cmt.ty)));
+                format!("Explicit index of non-index type `{}`",
+                     base_cmt.ty.repr(self.tcx())));
           }
         };
 
-        return match deref_kind(self.tcx, base_cmt.ty) {
+        return match deref_kind(self.tcx(), base_cmt.ty) {
           deref_ptr(ptr) => {
             // for unique ptrs, we inherit mutability from the
             // owning reference.
-            let m = match ptr {
-                uniq_ptr => {
-                    base_cmt.mutbl.inherit()
-                }
-                gc_ptr => {
-                    McImmutable
-                }
-                region_ptr(m, _) | unsafe_ptr(m) => {
-                    MutabilityCategory::from_mutbl(m)
-                }
-            };
+            let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr);
 
             // the deref is explicit in the resulting cmt
             let deref_cmt = @cmt_ {
@@ -775,7 +845,7 @@ pub fn cat_index<N:ast_node>(&self,
                 ty:element_ty
             };
 
-            interior(elt, deref_cmt, base_cmt.ty, m, element_ty)
+            interior(elt, deref_cmt, base_cmt.ty, m.inherit(), element_ty)
           }
 
           deref_interior(_) => {
@@ -801,7 +871,57 @@ fn interior<N: ast_node>(elt: &N,
         }
     }
 
-    pub fn cat_imm_interior<N:ast_node>(&self,
+    pub fn cat_slice_pattern(&mut self,
+                             vec_cmt: cmt,
+                             slice_pat: @ast::Pat)
+                             -> McResult<(cmt, ast::Mutability, ty::Region)> {
+        /*!
+         * Given a pattern P like: `[_, ..Q, _]`, where `vec_cmt` is
+         * the cmt for `P`, `slice_pat` is the pattern `Q`, returns:
+         * - a cmt for `Q`
+         * - the mutability and region of the slice `Q`
+         *
+         * These last two bits of info happen to be things that
+         * borrowck needs.
+         */
+
+        let slice_ty = if_ok!(self.node_ty(slice_pat.id));
+        let (slice_mutbl, slice_r) = vec_slice_info(self.tcx(),
+                                                    slice_pat,
+                                                    slice_ty);
+        let cmt_slice = self.cat_index(slice_pat, vec_cmt, 0);
+        return Ok((cmt_slice, slice_mutbl, slice_r));
+
+        fn vec_slice_info(tcx: ty::ctxt,
+                          pat: @ast::Pat,
+                          slice_ty: ty::t)
+                          -> (ast::Mutability, ty::Region) {
+            /*!
+             * In a pattern like [a, b, ..c], normally `c` has slice type,
+             * but if you have [a, b, ..ref c], then the type of `ref c`
+             * will be `&&[]`, so to extract the slice details we have
+             * to recurse through rptrs.
+             */
+
+            match ty::get(slice_ty).sty {
+                ty::ty_vec(slice_mt, ty::vstore_slice(slice_r)) => {
+                    (slice_mt.mutbl, slice_r)
+                }
+
+                ty::ty_rptr(_, ref mt) => {
+                    vec_slice_info(tcx, pat, mt.ty)
+                }
+
+                _ => {
+                    tcx.sess.span_bug(
+                        pat.span,
+                        format!("Type of slice pattern is not a slice"));
+                }
+            }
+        }
+    }
+
+    pub fn cat_imm_interior<N:ast_node>(&mut self,
                                         node: &N,
                                         base_cmt: cmt,
                                         interior_ty: ty::t,
@@ -816,7 +936,7 @@ pub fn cat_imm_interior<N:ast_node>(&self,
         }
     }
 
-    pub fn cat_downcast<N:ast_node>(&self,
+    pub fn cat_downcast<N:ast_node>(&mut self,
                                     node: &N,
                                     base_cmt: cmt,
                                     downcast_ty: ty::t)
@@ -830,10 +950,13 @@ pub fn cat_downcast<N:ast_node>(&self,
         }
     }
 
-    pub fn cat_pattern(&self,
+    pub fn cat_pattern(&mut self,
                        cmt: cmt,
-                       pat: &ast::Pat,
-                       op: |cmt, &ast::Pat|) {
+                       pat: @ast::Pat,
+                       op: |&mut MemCategorizationContext<TYPER>,
+                            cmt,
+                            @ast::Pat|)
+                       -> McResult<()> {
         // Here, `cmt` is the categorization for the value being
         // matched and pat is the pattern it is being matched against.
         //
@@ -846,7 +969,7 @@ pub fn cat_pattern(&self,
         // we can be sure that the binding will remain valid for the
         // duration of the arm.
         //
-        // (..) There is subtlety concerning the correspondence between
+        // (*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
@@ -856,7 +979,8 @@ pub fn cat_pattern(&self,
         // 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. So if you have something like:
+        // value, and I consider them to produce the value that was
+        // matched. So if you have something like:
         //
         //     let x = @@3;
         //     match x {
@@ -878,13 +1002,12 @@ pub fn cat_pattern(&self,
         // step out of sync again. So you'll see below that we always
         // get the type of the *subpattern* and use that.
 
-        let tcx = self.tcx;
+        let tcx = self.tcx();
         debug!("cat_pattern: id={} pat={} cmt={}",
                pat.id, pprust::pat_to_str(pat, tcx.sess.intr()),
                cmt.repr(tcx));
-        let _i = indenter();
 
-        op(cmt, pat);
+        op(self, cmt, pat);
 
         match pat.node {
           ast::PatWild | ast::PatWildMulti => {
@@ -895,13 +1018,13 @@ pub fn cat_pattern(&self,
             // variant(..)
           }
           ast::PatEnum(_, Some(ref subpats)) => {
-            let def_map = self.tcx.def_map.borrow();
+            let def_map = self.tcx().def_map.borrow();
             match def_map.get().find(&pat.id) {
                 Some(&ast::DefVariant(enum_did, _, _)) => {
                     // variant(x, y, z)
 
                     let downcast_cmt = {
-                        if ty::enum_is_univariant(tcx, enum_did) {
+                        if ty::enum_is_univariant(self.tcx(), enum_did) {
                             cmt // univariant, no downcast needed
                         } else {
                             self.cat_downcast(pat, cmt, cmt.ty)
@@ -909,34 +1032,34 @@ pub fn cat_pattern(&self,
                     };
 
                     for (i, &subpat) in subpats.iter().enumerate() {
-                        let subpat_ty = self.pat_ty(subpat); // see (..)
+                        let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2)
 
                         let subcmt =
                             self.cat_imm_interior(
                                 pat, downcast_cmt, subpat_ty,
                                 InteriorField(PositionalField(i)));
 
-                        self.cat_pattern(subcmt, subpat, |x,y| op(x,y));
+                        if_ok!(self.cat_pattern(subcmt, subpat, |x,y,z| op(x,y,z)));
                     }
                 }
                 Some(&ast::DefFn(..)) |
                 Some(&ast::DefStruct(..)) => {
                     for (i, &subpat) in subpats.iter().enumerate() {
-                        let subpat_ty = self.pat_ty(subpat); // see (..)
+                        let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2)
                         let cmt_field =
                             self.cat_imm_interior(
                                 pat, cmt, subpat_ty,
                                 InteriorField(PositionalField(i)));
-                        self.cat_pattern(cmt_field, subpat, |x,y| op(x,y));
+                        if_ok!(self.cat_pattern(cmt_field, subpat, |x,y,z| op(x,y,z)));
                     }
                 }
                 Some(&ast::DefStatic(..)) => {
                     for &subpat in subpats.iter() {
-                        self.cat_pattern(cmt, subpat, |x,y| op(x,y));
+                        if_ok!(self.cat_pattern(cmt, subpat, |x,y,z| op(x,y,z)));
                     }
                 }
                 _ => {
-                    self.tcx.sess.span_bug(
+                    self.tcx().sess.span_bug(
                         pat.span,
                         "enum pattern didn't resolve to enum or struct");
                 }
@@ -944,7 +1067,7 @@ pub fn cat_pattern(&self,
           }
 
           ast::PatIdent(_, _, Some(subpat)) => {
-              self.cat_pattern(cmt, subpat, op);
+              if_ok!(self.cat_pattern(cmt, subpat, op));
           }
 
           ast::PatIdent(_, _, None) => {
@@ -954,42 +1077,42 @@ pub fn cat_pattern(&self,
           ast::PatStruct(_, ref field_pats, _) => {
             // {f1: p1, ..., fN: pN}
             for fp in field_pats.iter() {
-                let field_ty = self.pat_ty(fp.pat); // see (..)
+                let field_ty = if_ok!(self.pat_ty(fp.pat)); // see (*2)
                 let cmt_field = self.cat_field(pat, cmt, fp.ident, field_ty);
-                self.cat_pattern(cmt_field, fp.pat, |x,y| op(x,y));
+                if_ok!(self.cat_pattern(cmt_field, fp.pat, |x,y,z| op(x,y,z)));
             }
           }
 
           ast::PatTup(ref subpats) => {
             // (p1, ..., pN)
             for (i, &subpat) in subpats.iter().enumerate() {
-                let subpat_ty = self.pat_ty(subpat); // see (..)
+                let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2)
                 let subcmt =
                     self.cat_imm_interior(
                         pat, cmt, subpat_ty,
                         InteriorField(PositionalField(i)));
-                self.cat_pattern(subcmt, subpat, |x,y| op(x,y));
+                if_ok!(self.cat_pattern(subcmt, subpat, |x,y,z| op(x,y,z)));
             }
           }
 
           ast::PatUniq(subpat) | ast::PatRegion(subpat) => {
             // @p1, ~p1
             let subcmt = self.cat_deref(pat, cmt, 0);
-            self.cat_pattern(subcmt, subpat, op);
+            if_ok!(self.cat_pattern(subcmt, subpat, op));
           }
 
           ast::PatVec(ref before, slice, ref after) => {
               let elt_cmt = self.cat_index(pat, cmt, 0);
               for &before_pat in before.iter() {
-                  self.cat_pattern(elt_cmt, before_pat, |x,y| op(x,y));
+                  if_ok!(self.cat_pattern(elt_cmt, before_pat, |x,y,z| op(x,y,z)));
               }
               for &slice_pat in slice.iter() {
-                  let slice_ty = self.pat_ty(slice_pat);
+                  let slice_ty = if_ok!(self.pat_ty(slice_pat));
                   let slice_cmt = self.cat_rvalue_node(pat.id(), pat.span(), slice_ty);
-                  self.cat_pattern(slice_cmt, slice_pat, |x,y| op(x,y));
+                  if_ok!(self.cat_pattern(slice_cmt, slice_pat, |x,y,z| op(x,y,z)));
               }
               for &after_pat in after.iter() {
-                  self.cat_pattern(elt_cmt, after_pat, |x,y| op(x,y));
+                  if_ok!(self.cat_pattern(elt_cmt, after_pat, |x,y,z| op(x,y,z)));
               }
           }
 
@@ -997,9 +1120,11 @@ pub fn cat_pattern(&self,
               /*always ok*/
           }
         }
+
+        Ok(())
     }
 
-    pub fn mut_to_str(&self, mutbl: ast::Mutability) -> ~str {
+    pub fn mut_to_str(&mut self, mutbl: ast::Mutability) -> ~str {
         match mutbl {
           MutMutable => ~"mutable",
           MutImmutable => ~"immutable"
@@ -1023,8 +1148,15 @@ pub fn cmt_to_str(&self, cmt: cmt) -> ~str {
           cat_arg(..) => {
               ~"argument"
           }
-          cat_deref(_, _, pk) => {
-              format!("dereference of {} pointer", ptr_sigil(pk))
+          cat_deref(base, _, pk) => {
+              match base.cat {
+                  cat_upvar(..) => {
+                      format!("captured outer variable")
+                  }
+                  _ => {
+                      format!("dereference of {} pointer", ptr_sigil(pk))
+                  }
+              }
           }
           cat_interior(_, InteriorField(NamedField(_))) => {
               ~"field"
@@ -1041,7 +1173,7 @@ pub fn cmt_to_str(&self, cmt: cmt) -> ~str {
           cat_interior(_, InteriorElement(OtherElement)) => {
               ~"indexed content"
           }
-          cat_stack_upvar(_) => {
+          cat_upvar(..) => {
               ~"captured outer variable"
           }
           cat_discr(cmt, _) => {
@@ -1054,7 +1186,7 @@ pub fn cmt_to_str(&self, cmt: cmt) -> ~str {
     }
 
     pub fn region_to_str(&self, r: ty::Region) -> ~str {
-        region_ptr_to_str(self.tcx, r)
+        region_ptr_to_str(self.tcx(), r)
     }
 }
 
@@ -1099,7 +1231,7 @@ pub fn field_mutbl(tcx: ty::ctxt,
 
 pub enum AliasableReason {
     AliasableManaged,
-    AliasableBorrowed(ast::Mutability),
+    AliasableBorrowed,
     AliasableOther,
     AliasableStatic,
     AliasableStaticMut,
@@ -1117,16 +1249,16 @@ pub fn guarantor(self) -> cmt {
             cat_copied_upvar(..) |
             cat_local(..) |
             cat_arg(..) |
-            cat_deref(_, _, unsafe_ptr(..)) |
-            cat_deref(_, _, gc_ptr) |
-            cat_deref(_, _, region_ptr(..)) => {
+            cat_deref(_, _, UnsafePtr(..)) |
+            cat_deref(_, _, GcPtr(..)) |
+            cat_deref(_, _, BorrowedPtr(..)) |
+            cat_upvar(..) => {
                 @self
             }
             cat_downcast(b) |
-            cat_stack_upvar(b) |
             cat_discr(b, _) |
             cat_interior(b, _) |
-            cat_deref(b, _, uniq_ptr) => {
+            cat_deref(b, _, OwnedPtr) => {
                 b.guarantor()
             }
         }
@@ -1143,10 +1275,10 @@ pub fn freely_aliasable(&self) -> Option<AliasableReason> {
         // aliased and eventually recused.
 
         match self.cat {
-            cat_deref(b, _, region_ptr(MutMutable, _)) |
+            cat_deref(b, _, BorrowedPtr(ty::MutBorrow, _)) |
+            cat_deref(b, _, BorrowedPtr(ty::UniqueImmBorrow, _)) |
             cat_downcast(b) |
-            cat_stack_upvar(b) |
-            cat_deref(b, _, uniq_ptr) |
+            cat_deref(b, _, OwnedPtr) |
             cat_interior(b, _) |
             cat_discr(b, _) => {
                 // Aliasability depends on base cmt
@@ -1156,8 +1288,9 @@ pub fn freely_aliasable(&self) -> Option<AliasableReason> {
             cat_copied_upvar(CopiedUpvar {onceness: ast::Once, ..}) |
             cat_rvalue(..) |
             cat_local(..) |
+            cat_upvar(..) |
             cat_arg(_) |
-            cat_deref(_, _, unsafe_ptr(..)) => { // yes, it's aliasable, but...
+            cat_deref(_, _, UnsafePtr(..)) => { // yes, it's aliasable, but...
                 None
             }
 
@@ -1173,12 +1306,12 @@ pub fn freely_aliasable(&self) -> Option<AliasableReason> {
                 }
             }
 
-            cat_deref(_, _, gc_ptr) => {
+            cat_deref(_, _, GcPtr) => {
                 Some(AliasableManaged)
             }
 
-            cat_deref(_, _, region_ptr(m @ MutImmutable, _)) => {
-                Some(AliasableBorrowed(m))
+            cat_deref(_, _, BorrowedPtr(ty::ImmBorrow, _)) => {
+                Some(AliasableBorrowed)
             }
         }
     }
@@ -1201,12 +1334,15 @@ fn repr(&self, tcx: ty::ctxt) -> ~str {
             cat_rvalue(..) |
             cat_copied_upvar(..) |
             cat_local(..) |
+            cat_upvar(..) |
             cat_arg(..) => {
                 format!("{:?}", *self)
             }
             cat_deref(cmt, derefs, ptr) => {
-                format!("{}->({}, {})", cmt.cat.repr(tcx),
-                     ptr_sigil(ptr), derefs)
+                format!("{}-{}{}->",
+                        cmt.cat.repr(tcx),
+                        ptr_sigil(ptr),
+                        derefs)
             }
             cat_interior(cmt, interior) => {
                 format!("{}.{}",
@@ -1216,7 +1352,6 @@ fn repr(&self, tcx: ty::ctxt) -> ~str {
             cat_downcast(cmt) => {
                 format!("{}->(enum)", cmt.cat.repr(tcx))
             }
-            cat_stack_upvar(cmt) |
             cat_discr(cmt, _) => {
                 cmt.cat.repr(tcx)
             }
@@ -1224,12 +1359,14 @@ fn repr(&self, tcx: ty::ctxt) -> ~str {
     }
 }
 
-pub fn ptr_sigil(ptr: PointerKind) -> ~str {
+pub fn ptr_sigil(ptr: PointerKind) -> &'static str {
     match ptr {
-        uniq_ptr => ~"~",
-        gc_ptr => ~"@",
-        region_ptr(_, _) => ~"&",
-        unsafe_ptr(_) => ~"*"
+        OwnedPtr => "~",
+        GcPtr => "@",
+        BorrowedPtr(ty::ImmBorrow, _) => "&",
+        BorrowedPtr(ty::MutBorrow, _) => "&mut",
+        BorrowedPtr(ty::UniqueImmBorrow, _) => "&unique",
+        UnsafePtr(_) => "*"
     }
 }