]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/mem_categorization.rs
auto merge of #12407 : alexcrichton/rust/up-llvm, r=sfackler
[rust.git] / src / librustc / middle / mem_categorization.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*!
12  * # Categorization
13  *
14  * The job of the categorization module is to analyze an expression to
15  * determine what kind of memory is used in evaluating it (for example,
16  * where dereferences occur and what kind of pointer is dereferenced;
17  * whether the memory is mutable; etc)
18  *
19  * Categorization effectively transforms all of our expressions into
20  * expressions of the following forms (the actual enum has many more
21  * possibilities, naturally, but they are all variants of these base
22  * forms):
23  *
24  *     E = rvalue    // some computed rvalue
25  *       | x         // address of a local variable or argument
26  *       | *E        // deref of a ptr
27  *       | E.comp    // access to an interior component
28  *
29  * Imagine a routine ToAddr(Expr) that evaluates an expression and returns an
30  * address where the result is to be found.  If Expr is an lvalue, then this
31  * is the address of the lvalue.  If Expr is an rvalue, this is the address of
32  * some temporary spot in memory where the result is stored.
33  *
34  * Now, cat_expr() classies the expression Expr and the address A=ToAddr(Expr)
35  * as follows:
36  *
37  * - cat: what kind of expression was this?  This is a subset of the
38  *   full expression forms which only includes those that we care about
39  *   for the purpose of the analysis.
40  * - mutbl: mutability of the address A
41  * - ty: the type of data found at the address A
42  *
43  * The resulting categorization tree differs somewhat from the expressions
44  * themselves.  For example, auto-derefs are explicit.  Also, an index a[b] is
45  * decomposed into two operations: a derefence to reach the array data and
46  * then an index to jump forward to the relevant item.
47  *
48  * ## By-reference upvars
49  *
50  * One part of the translation which may be non-obvious is that we translate
51  * closure upvars into the dereference of a borrowed pointer; this more closely
52  * resembles the runtime translation. So, for example, if we had:
53  *
54  *     let mut x = 3;
55  *     let y = 5;
56  *     let inc = || x += y;
57  *
58  * Then when we categorize `x` (*within* the closure) we would yield a
59  * result of `*x'`, effectively, where `x'` is a `cat_upvar` reference
60  * tied to `x`. The type of `x'` will be a borrowed pointer.
61  */
62
63
64 use middle::ty;
65 use util::ppaux::{ty_to_str, region_ptr_to_str, Repr};
66
67 use syntax::ast::{MutImmutable, MutMutable};
68 use syntax::ast;
69 use syntax::codemap::Span;
70 use syntax::print::pprust;
71 use syntax::parse::token;
72
73 #[deriving(Eq)]
74 pub enum categorization {
75     cat_rvalue(ty::Region),            // temporary val, argument is its scope
76     cat_static_item,
77     cat_copied_upvar(CopiedUpvar),     // upvar copied into @fn or ~fn env
78     cat_upvar(ty::UpvarId, ty::UpvarBorrow), // by ref upvar from stack closure
79     cat_local(ast::NodeId),            // local variable
80     cat_arg(ast::NodeId),              // formal argument
81     cat_deref(cmt, uint, PointerKind), // deref of a ptr
82     cat_interior(cmt, InteriorKind),   // something interior: field, tuple, etc
83     cat_downcast(cmt),                 // selects a particular enum variant (*1)
84     cat_discr(cmt, ast::NodeId),       // match discriminant (see preserve())
85
86     // (*1) downcast is only required if the enum has more than one variant
87 }
88
89 #[deriving(Eq)]
90 pub struct CopiedUpvar {
91     upvar_id: ast::NodeId,
92     onceness: ast::Onceness,
93 }
94
95 // different kinds of pointers:
96 #[deriving(Eq, IterBytes)]
97 pub enum PointerKind {
98     OwnedPtr,
99     GcPtr,
100     BorrowedPtr(ty::BorrowKind, ty::Region),
101     UnsafePtr(ast::Mutability),
102 }
103
104 // We use the term "interior" to mean "something reachable from the
105 // base without a pointer dereference", e.g. a field
106 #[deriving(Eq, IterBytes)]
107 pub enum InteriorKind {
108     InteriorField(FieldName),
109     InteriorElement(ElementKind),
110 }
111
112 #[deriving(Eq, IterBytes)]
113 pub enum FieldName {
114     NamedField(ast::Name),
115     PositionalField(uint)
116 }
117
118 #[deriving(Eq, IterBytes)]
119 pub enum ElementKind {
120     VecElement,
121     StrElement,
122     OtherElement,
123 }
124
125 #[deriving(Eq, IterBytes)]
126 pub enum MutabilityCategory {
127     McImmutable, // Immutable.
128     McDeclared,  // Directly declared as mutable.
129     McInherited, // Inherited from the fact that owner is mutable.
130 }
131
132 // `cmt`: "Category, Mutability, and Type".
133 //
134 // a complete categorization of a value indicating where it originated
135 // and how it is located, as well as the mutability of the memory in
136 // which the value is stored.
137 //
138 // *WARNING* The field `cmt.type` is NOT necessarily the same as the
139 // result of `node_id_to_type(cmt.id)`. This is because the `id` is
140 // always the `id` of the node producing the type; in an expression
141 // like `*x`, the type of this deref node is the deref'd type (`T`),
142 // but in a pattern like `@x`, the `@x` pattern is again a
143 // dereference, but its type is the type *before* the dereference
144 // (`@T`). So use `cmt.type` to find the type of the value in a consistent
145 // fashion. For more details, see the method `cat_pattern`
146 #[deriving(Eq)]
147 pub struct cmt_ {
148     id: ast::NodeId,          // id of expr/pat producing this value
149     span: Span,                // span of same expr/pat
150     cat: categorization,       // categorization of expr
151     mutbl: MutabilityCategory, // mutability of expr as lvalue
152     ty: ty::t                  // type of the expr (*see WARNING above*)
153 }
154
155 pub type cmt = @cmt_;
156
157 // We pun on *T to mean both actual deref of a ptr as well
158 // as accessing of components:
159 pub enum deref_kind {
160     deref_ptr(PointerKind),
161     deref_interior(InteriorKind),
162 }
163
164 // Categorizes a derefable type.  Note that we include vectors and strings as
165 // derefable (we model an index as the combination of a deref and then a
166 // pointer adjustment).
167 pub fn opt_deref_kind(t: ty::t) -> Option<deref_kind> {
168     match ty::get(t).sty {
169         ty::ty_uniq(_) |
170         ty::ty_trait(_, _, ty::UniqTraitStore, _, _) |
171         ty::ty_vec(_, ty::vstore_uniq) |
172         ty::ty_str(ty::vstore_uniq) |
173         ty::ty_closure(ty::ClosureTy {sigil: ast::OwnedSigil, ..}) => {
174             Some(deref_ptr(OwnedPtr))
175         }
176
177         ty::ty_rptr(r, mt) |
178         ty::ty_vec(mt, ty::vstore_slice(r)) => {
179             let kind = ty::BorrowKind::from_mutbl(mt.mutbl);
180             Some(deref_ptr(BorrowedPtr(kind, r)))
181         }
182
183         ty::ty_trait(_, _, ty::RegionTraitStore(r), m, _) => {
184             let kind = ty::BorrowKind::from_mutbl(m);
185             Some(deref_ptr(BorrowedPtr(kind, r)))
186         }
187
188         ty::ty_str(ty::vstore_slice(r)) |
189         ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil,
190                                       region: r, ..}) => {
191             Some(deref_ptr(BorrowedPtr(ty::ImmBorrow, r)))
192         }
193
194         ty::ty_box(..) => {
195             Some(deref_ptr(GcPtr))
196         }
197
198         ty::ty_ptr(ref mt) => {
199             Some(deref_ptr(UnsafePtr(mt.mutbl)))
200         }
201
202         ty::ty_enum(..) |
203         ty::ty_struct(..) => { // newtype
204             Some(deref_interior(InteriorField(PositionalField(0))))
205         }
206
207         ty::ty_vec(_, ty::vstore_fixed(_)) |
208         ty::ty_str(ty::vstore_fixed(_)) => {
209             Some(deref_interior(InteriorElement(element_kind(t))))
210         }
211
212         _ => None
213     }
214 }
215
216 pub fn deref_kind(tcx: ty::ctxt, t: ty::t) -> deref_kind {
217     match opt_deref_kind(t) {
218       Some(k) => k,
219       None => {
220         tcx.sess.bug(
221             format!("deref_cat() invoked on non-derefable type {}",
222                  ty_to_str(tcx, t)));
223       }
224     }
225 }
226
227 trait ast_node {
228     fn id(&self) -> ast::NodeId;
229     fn span(&self) -> Span;
230 }
231
232 impl ast_node for ast::Expr {
233     fn id(&self) -> ast::NodeId { self.id }
234     fn span(&self) -> Span { self.span }
235 }
236
237 impl ast_node for ast::Pat {
238     fn id(&self) -> ast::NodeId { self.id }
239     fn span(&self) -> Span { self.span }
240 }
241
242 pub struct MemCategorizationContext<TYPER> {
243     typer: TYPER
244 }
245
246 pub type McResult<T> = Result<T, ()>;
247
248 /**
249  * The `Typer` trait provides the interface for the mem-categorization
250  * module to the results of the type check. It can be used to query
251  * the type assigned to an expression node, to inquire after adjustments,
252  * and so on.
253  *
254  * This interface is needed because mem-categorization is used from
255  * two places: `regionck` and `borrowck`. `regionck` executes before
256  * type inference is complete, and hence derives types and so on from
257  * intermediate tables.  This also implies that type errors can occur,
258  * and hence `node_ty()` and friends return a `Result` type -- any
259  * error will propagate back up through the mem-categorization
260  * routines.
261  *
262  * In the borrow checker, in contrast, type checking is complete and we
263  * know that no errors have occurred, so we simply consult the tcx and we
264  * can be sure that only `Ok` results will occur.
265  */
266 pub trait Typer {
267     fn tcx(&self) -> ty::ctxt;
268     fn node_ty(&mut self, id: ast::NodeId) -> McResult<ty::t>;
269     fn adjustment(&mut self, node_id: ast::NodeId) -> Option<@ty::AutoAdjustment>;
270     fn is_method_call(&mut self, id: ast::NodeId) -> bool;
271     fn temporary_scope(&mut self, rvalue_id: ast::NodeId) -> Option<ast::NodeId>;
272     fn upvar_borrow(&mut self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow;
273 }
274
275 impl ToStr for MutabilityCategory {
276     fn to_str(&self) -> ~str {
277         format!("{:?}", *self)
278     }
279 }
280
281 impl MutabilityCategory {
282     pub fn from_mutbl(m: ast::Mutability) -> MutabilityCategory {
283         match m {
284             MutImmutable => McImmutable,
285             MutMutable => McDeclared
286         }
287     }
288
289     pub fn from_borrow_kind(borrow_kind: ty::BorrowKind) -> MutabilityCategory {
290         match borrow_kind {
291             ty::ImmBorrow => McImmutable,
292             ty::UniqueImmBorrow => McImmutable,
293             ty::MutBorrow => McDeclared,
294         }
295     }
296
297     pub fn from_pointer_kind(base_mutbl: MutabilityCategory,
298                              ptr: PointerKind) -> MutabilityCategory {
299         match ptr {
300             OwnedPtr => {
301                 base_mutbl.inherit()
302             }
303             BorrowedPtr(borrow_kind, _) => {
304                 MutabilityCategory::from_borrow_kind(borrow_kind)
305             }
306             GcPtr => {
307                 McImmutable
308             }
309             UnsafePtr(m) => {
310                 MutabilityCategory::from_mutbl(m)
311             }
312         }
313     }
314
315     pub fn inherit(&self) -> MutabilityCategory {
316         match *self {
317             McImmutable => McImmutable,
318             McDeclared => McInherited,
319             McInherited => McInherited,
320         }
321     }
322
323     pub fn is_mutable(&self) -> bool {
324         match *self {
325             McImmutable => false,
326             McInherited => true,
327             McDeclared => true,
328         }
329     }
330
331     pub fn is_immutable(&self) -> bool {
332         match *self {
333             McImmutable => true,
334             McDeclared | McInherited => false
335         }
336     }
337
338     pub fn to_user_str(&self) -> &'static str {
339         match *self {
340             McDeclared | McInherited => "mutable",
341             McImmutable => "immutable",
342         }
343     }
344 }
345
346 macro_rules! if_ok(
347     ($inp: expr) => (
348         match $inp {
349             Ok(v) => { v }
350             Err(e) => { return Err(e); }
351         }
352     )
353 )
354
355 impl<TYPER:Typer> MemCategorizationContext<TYPER> {
356     fn tcx(&self) -> ty::ctxt {
357         self.typer.tcx()
358     }
359
360     fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> {
361         self.typer.adjustment(id)
362     }
363
364     fn expr_ty(&mut self, expr: &ast::Expr) -> McResult<ty::t> {
365         self.typer.node_ty(expr.id)
366     }
367
368     fn expr_ty_adjusted(&mut self, expr: &ast::Expr) -> McResult<ty::t> {
369         let unadjusted_ty = if_ok!(self.expr_ty(expr));
370         let adjustment = self.adjustment(expr.id);
371         Ok(ty::adjust_ty(self.tcx(), expr.span, unadjusted_ty, adjustment))
372     }
373
374     fn node_ty(&mut self, id: ast::NodeId) -> McResult<ty::t> {
375         self.typer.node_ty(id)
376     }
377
378     fn pat_ty(&mut self, pat: @ast::Pat) -> McResult<ty::t> {
379         self.typer.node_ty(pat.id)
380     }
381
382     pub fn cat_expr(&mut self, expr: &ast::Expr) -> McResult<cmt> {
383         match self.adjustment(expr.id) {
384             None => {
385                 // No adjustments.
386                 self.cat_expr_unadjusted(expr)
387             }
388
389             Some(adjustment) => {
390                 match *adjustment {
391                     ty::AutoObject(..) => {
392                         // Implicity casts a concrete object to trait object
393                         // so just patch up the type
394                         let expr_ty = if_ok!(self.expr_ty_adjusted(expr));
395                         let expr_cmt = if_ok!(self.cat_expr_unadjusted(expr));
396                         Ok(@cmt_ {ty: expr_ty, ..*expr_cmt})
397                     }
398
399                     ty::AutoAddEnv(..) => {
400                         // Convert a bare fn to a closure by adding NULL env.
401                         // Result is an rvalue.
402                         let expr_ty = if_ok!(self.expr_ty_adjusted(expr));
403                         Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
404                     }
405
406                     ty::AutoDerefRef(
407                         ty::AutoDerefRef {
408                             autoref: Some(_), ..}) => {
409                         // Equivalent to &*expr or something similar.
410                         // Result is an rvalue.
411                         let expr_ty = if_ok!(self.expr_ty_adjusted(expr));
412                         Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
413                     }
414
415                     ty::AutoDerefRef(
416                         ty::AutoDerefRef {
417                             autoref: None, autoderefs: autoderefs}) => {
418                         // Equivalent to *expr or something similar.
419                         self.cat_expr_autoderefd(expr, autoderefs)
420                     }
421                 }
422             }
423         }
424     }
425
426     pub fn cat_expr_autoderefd(&mut self, expr: &ast::Expr, autoderefs: uint)
427                                -> McResult<cmt> {
428         let mut cmt = if_ok!(self.cat_expr_unadjusted(expr));
429         for deref in range(1u, autoderefs + 1) {
430             cmt = self.cat_deref(expr, cmt, deref);
431         }
432         return Ok(cmt);
433     }
434
435     pub fn cat_expr_unadjusted(&mut self, expr: &ast::Expr) -> McResult<cmt> {
436         debug!("cat_expr: id={} expr={}", expr.id, expr.repr(self.tcx()));
437
438         let expr_ty = if_ok!(self.expr_ty(expr));
439         match expr.node {
440           ast::ExprUnary(_, ast::UnDeref, e_base) => {
441             if self.typer.is_method_call(expr.id) {
442                 return Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty));
443             }
444
445             let base_cmt = if_ok!(self.cat_expr(e_base));
446             Ok(self.cat_deref(expr, base_cmt, 0))
447           }
448
449           ast::ExprField(base, f_name, _) => {
450             // Method calls are now a special syntactic form,
451             // so `a.b` should always be a field.
452             assert!(!self.typer.is_method_call(expr.id));
453
454             let base_cmt = if_ok!(self.cat_expr(base));
455             Ok(self.cat_field(expr, base_cmt, f_name, expr_ty))
456           }
457
458           ast::ExprIndex(_, base, _) => {
459             if self.typer.is_method_call(expr.id) {
460                 return Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty));
461             }
462
463             let base_cmt = if_ok!(self.cat_expr(base));
464             Ok(self.cat_index(expr, base_cmt, 0))
465           }
466
467           ast::ExprPath(_) => {
468             let def_map = self.tcx().def_map.borrow();
469             let def = def_map.get().get_copy(&expr.id);
470             self.cat_def(expr.id, expr.span, expr_ty, def)
471           }
472
473           ast::ExprParen(e) => self.cat_expr_unadjusted(e),
474
475           ast::ExprAddrOf(..) | ast::ExprCall(..) |
476           ast::ExprAssign(..) | ast::ExprAssignOp(..) |
477           ast::ExprFnBlock(..) | ast::ExprProc(..) | ast::ExprRet(..) |
478           ast::ExprUnary(..) |
479           ast::ExprMethodCall(..) | ast::ExprCast(..) | ast::ExprVstore(..) |
480           ast::ExprVec(..) | ast::ExprTup(..) | ast::ExprIf(..) |
481           ast::ExprLogLevel | ast::ExprBinary(..) | ast::ExprWhile(..) |
482           ast::ExprBlock(..) | ast::ExprLoop(..) | ast::ExprMatch(..) |
483           ast::ExprLit(..) | ast::ExprBreak(..) | ast::ExprMac(..) |
484           ast::ExprAgain(..) | ast::ExprStruct(..) | ast::ExprRepeat(..) |
485           ast::ExprInlineAsm(..) | ast::ExprBox(..) => {
486             Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
487           }
488
489           ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop")
490         }
491     }
492
493     pub fn cat_def(&mut self,
494                    id: ast::NodeId,
495                    span: Span,
496                    expr_ty: ty::t,
497                    def: ast::Def)
498                    -> McResult<cmt> {
499         debug!("cat_def: id={} expr={}",
500                id, expr_ty.repr(self.tcx()));
501
502         match def {
503           ast::DefStruct(..) | ast::DefVariant(..) => {
504                 Ok(self.cat_rvalue_node(id, span, expr_ty))
505           }
506           ast::DefFn(..) | ast::DefStaticMethod(..) | ast::DefMod(_) |
507           ast::DefForeignMod(_) | ast::DefStatic(_, false) |
508           ast::DefUse(_) | ast::DefTrait(_) | ast::DefTy(_) | ast::DefPrimTy(_) |
509           ast::DefTyParam(..) | ast::DefTyParamBinder(..) | ast::DefRegion(_) |
510           ast::DefLabel(_) | ast::DefSelfTy(..) | ast::DefMethod(..) => {
511               Ok(@cmt_ {
512                   id:id,
513                   span:span,
514                   cat:cat_static_item,
515                   mutbl: McImmutable,
516                   ty:expr_ty
517               })
518           }
519
520           ast::DefStatic(_, true) => {
521               Ok(@cmt_ {
522                   id:id,
523                   span:span,
524                   cat:cat_static_item,
525                   mutbl: McDeclared,
526                   ty:expr_ty
527               })
528           }
529
530           ast::DefArg(vid, binding_mode) => {
531             // Idea: make this could be rewritten to model by-ref
532             // stuff as `&const` and `&mut`?
533
534             // m: mutability of the argument
535             let m = match binding_mode {
536                 ast::BindByValue(ast::MutMutable) => McDeclared,
537                 _ => McImmutable
538             };
539             Ok(@cmt_ {
540                 id: id,
541                 span: span,
542                 cat: cat_arg(vid),
543                 mutbl: m,
544                 ty:expr_ty
545             })
546           }
547
548           ast::DefUpvar(var_id, _, fn_node_id, _) => {
549               let ty = if_ok!(self.node_ty(fn_node_id));
550               match ty::get(ty).sty {
551                   ty::ty_closure(ref closure_ty) => {
552                       // Decide whether to use implicit reference or by copy/move
553                       // capture for the upvar. This, combined with the onceness,
554                       // determines whether the closure can move out of it.
555                       let var_is_refd = match (closure_ty.sigil, closure_ty.onceness) {
556                           // Many-shot stack closures can never move out.
557                           (ast::BorrowedSigil, ast::Many) => true,
558                           // 1-shot stack closures can move out.
559                           (ast::BorrowedSigil, ast::Once) => false,
560                           // Heap closures always capture by copy/move, and can
561                           // move out if they are once.
562                           (ast::OwnedSigil, _) |
563                           (ast::ManagedSigil, _) => false,
564
565                       };
566                       if var_is_refd {
567                           self.cat_upvar(id, span, var_id, fn_node_id)
568                       } else {
569                           // FIXME #2152 allow mutation of moved upvars
570                           Ok(@cmt_ {
571                               id:id,
572                               span:span,
573                               cat:cat_copied_upvar(CopiedUpvar {
574                                   upvar_id: var_id,
575                                   onceness: closure_ty.onceness}),
576                               mutbl:McImmutable,
577                               ty:expr_ty
578                           })
579                       }
580                   }
581                   _ => {
582                       self.tcx().sess.span_bug(
583                           span,
584                           format!("Upvar of non-closure {} - {}",
585                                   fn_node_id, ty.repr(self.tcx())));
586                   }
587               }
588           }
589
590           ast::DefLocal(vid, binding_mode) |
591           ast::DefBinding(vid, binding_mode) => {
592             // by-value/by-ref bindings are local variables
593             let m = match binding_mode {
594                 ast::BindByValue(ast::MutMutable) => McDeclared,
595                 _ => McImmutable
596             };
597
598             Ok(@cmt_ {
599                 id: id,
600                 span: span,
601                 cat: cat_local(vid),
602                 mutbl: m,
603                 ty: expr_ty
604             })
605           }
606         }
607     }
608
609     fn cat_upvar(&mut self,
610                  id: ast::NodeId,
611                  span: Span,
612                  var_id: ast::NodeId,
613                  fn_node_id: ast::NodeId)
614                  -> McResult<cmt> {
615         /*!
616          * Upvars through a closure are in fact indirect
617          * references. That is, when a closure refers to a
618          * variable from a parent stack frame like `x = 10`,
619          * that is equivalent to `*x_ = 10` where `x_` is a
620          * borrowed pointer (`&mut x`) created when the closure
621          * was created and store in the environment. This
622          * equivalence is expose in the mem-categorization.
623          */
624
625         let upvar_id = ty::UpvarId { var_id: var_id,
626                                      closure_expr_id: fn_node_id };
627
628         let upvar_borrow = self.typer.upvar_borrow(upvar_id);
629
630         let var_ty = if_ok!(self.node_ty(var_id));
631
632         // We can't actually represent the types of all upvars
633         // as user-describable types, since upvars support const
634         // and unique-imm borrows! Therefore, we cheat, and just
635         // give err type. Nobody should be inspecting this type anyhow.
636         let upvar_ty = ty::mk_err();
637
638         let base_cmt = @cmt_ {
639             id:id,
640             span:span,
641             cat:cat_upvar(upvar_id, upvar_borrow),
642             mutbl:McImmutable,
643             ty:upvar_ty,
644         };
645
646         let ptr = BorrowedPtr(upvar_borrow.kind, upvar_borrow.region);
647
648         let deref_cmt = @cmt_ {
649             id:id,
650             span:span,
651             cat:cat_deref(base_cmt, 0, ptr),
652             mutbl:MutabilityCategory::from_borrow_kind(upvar_borrow.kind),
653             ty:var_ty,
654         };
655
656         Ok(deref_cmt)
657     }
658
659     pub fn cat_rvalue_node(&mut self,
660                            id: ast::NodeId,
661                            span: Span,
662                            expr_ty: ty::t)
663                            -> cmt {
664         match self.typer.temporary_scope(id) {
665             Some(scope) => {
666                 self.cat_rvalue(id, span, ty::ReScope(scope), expr_ty)
667             }
668             None => {
669                 self.cat_rvalue(id, span, ty::ReStatic, expr_ty)
670             }
671         }
672     }
673
674     pub fn cat_rvalue(&mut self,
675                       cmt_id: ast::NodeId,
676                       span: Span,
677                       temp_scope: ty::Region,
678                       expr_ty: ty::t) -> cmt {
679         @cmt_ {
680             id:cmt_id,
681             span:span,
682             cat:cat_rvalue(temp_scope),
683             mutbl:McDeclared,
684             ty:expr_ty
685         }
686     }
687
688     /// inherited mutability: used in cases where the mutability of a
689     /// component is inherited from the base it is a part of. For
690     /// example, a record field is mutable if it is declared mutable
691     /// or if the container is mutable.
692     pub fn inherited_mutability(&mut self,
693                                 base_m: MutabilityCategory,
694                                 interior_m: ast::Mutability)
695                                 -> MutabilityCategory {
696         match interior_m {
697             MutImmutable => base_m.inherit(),
698             MutMutable => McDeclared
699         }
700     }
701
702     pub fn cat_field<N:ast_node>(&mut self,
703                                  node: &N,
704                                  base_cmt: cmt,
705                                  f_name: ast::Ident,
706                                  f_ty: ty::t)
707                                  -> cmt {
708         @cmt_ {
709             id: node.id(),
710             span: node.span(),
711             cat: cat_interior(base_cmt, InteriorField(NamedField(f_name.name))),
712             mutbl: base_cmt.mutbl.inherit(),
713             ty: f_ty
714         }
715     }
716
717     pub fn cat_deref_fn_or_obj<N:ast_node>(&mut self,
718                                            node: &N,
719                                            base_cmt: cmt,
720                                            deref_cnt: uint)
721                                            -> cmt {
722         // Bit of a hack: the "dereference" of a function pointer like
723         // `@fn()` is a mere logical concept. We interpret it as
724         // dereferencing the environment pointer; of course, we don't
725         // know what type lies at the other end, so we just call it
726         // `()` (the empty tuple).
727
728         let opaque_ty = ty::mk_tup(self.tcx(), ~[]);
729         return self.cat_deref_common(node, base_cmt, deref_cnt, opaque_ty);
730     }
731
732     pub fn cat_deref<N:ast_node>(&mut self,
733                                  node: &N,
734                                  base_cmt: cmt,
735                                  deref_cnt: uint)
736                                  -> cmt {
737         let mt = match ty::deref(base_cmt.ty, true) {
738             Some(mt) => mt,
739             None => {
740                 self.tcx().sess.span_bug(
741                     node.span(),
742                     format!("Explicit deref of non-derefable type: {}",
743                             base_cmt.ty.repr(self.tcx())));
744             }
745         };
746
747         return self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty);
748     }
749
750     pub fn cat_deref_common<N:ast_node>(&mut self,
751                                         node: &N,
752                                         base_cmt: cmt,
753                                         deref_cnt: uint,
754                                         deref_ty: ty::t)
755                                         -> cmt {
756         match deref_kind(self.tcx(), base_cmt.ty) {
757             deref_ptr(ptr) => {
758                 // for unique ptrs, we inherit mutability from the
759                 // owning reference.
760                 let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl,
761                                                               ptr);
762
763                 @cmt_ {
764                     id:node.id(),
765                     span:node.span(),
766                     cat:cat_deref(base_cmt, deref_cnt, ptr),
767                     mutbl:m,
768                     ty:deref_ty
769                 }
770             }
771
772             deref_interior(interior) => {
773                 let m = base_cmt.mutbl.inherit();
774                 @cmt_ {
775                     id:node.id(),
776                     span:node.span(),
777                     cat:cat_interior(base_cmt, interior),
778                     mutbl:m,
779                     ty:deref_ty
780                 }
781             }
782         }
783     }
784
785     pub fn cat_index<N:ast_node>(&mut self,
786                                  elt: &N,
787                                  base_cmt: cmt,
788                                  derefs: uint)
789                                  -> cmt {
790         //! Creates a cmt for an indexing operation (`[]`); this
791         //! indexing operation may occurs as part of an
792         //! AutoBorrowVec, which when converting a `~[]` to an `&[]`
793         //! effectively takes the address of the 0th element.
794         //!
795         //! One subtle aspect of indexing that may not be
796         //! immediately obvious: for anything other than a fixed-length
797         //! vector, an operation like `x[y]` actually consists of two
798         //! disjoint (from the point of view of borrowck) operations.
799         //! The first is a deref of `x` to create a pointer `p` that points
800         //! at the first element in the array. The second operation is
801         //! an index which adds `y*sizeof(T)` to `p` to obtain the
802         //! pointer to `x[y]`. `cat_index` will produce a resulting
803         //! cmt containing both this deref and the indexing,
804         //! presuming that `base_cmt` is not of fixed-length type.
805         //!
806         //! In the event that a deref is needed, the "deref count"
807         //! is taken from the parameter `derefs`. See the comment
808         //! on the def'n of `root_map_key` in borrowck/mod.rs
809         //! for more details about deref counts; the summary is
810         //! that `derefs` should be 0 for an explicit indexing
811         //! operation and N+1 for an indexing that is part of
812         //! an auto-adjustment, where N is the number of autoderefs
813         //! in that adjustment.
814         //!
815         //! # Parameters
816         //! - `elt`: the AST node being indexed
817         //! - `base_cmt`: the cmt of `elt`
818         //! - `derefs`: the deref number to be used for
819         //!   the implicit index deref, if any (see above)
820
821         let element_ty = match ty::index(base_cmt.ty) {
822           Some(ref mt) => mt.ty,
823           None => {
824             self.tcx().sess.span_bug(
825                 elt.span(),
826                 format!("Explicit index of non-index type `{}`",
827                      base_cmt.ty.repr(self.tcx())));
828           }
829         };
830
831         return match deref_kind(self.tcx(), base_cmt.ty) {
832           deref_ptr(ptr) => {
833             // for unique ptrs, we inherit mutability from the
834             // owning reference.
835             let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr);
836
837             // the deref is explicit in the resulting cmt
838             let deref_cmt = @cmt_ {
839                 id:elt.id(),
840                 span:elt.span(),
841                 cat:cat_deref(base_cmt, derefs, ptr),
842                 mutbl:m,
843                 ty:element_ty
844             };
845
846             interior(elt, deref_cmt, base_cmt.ty, m.inherit(), element_ty)
847           }
848
849           deref_interior(_) => {
850             // fixed-length vectors have no deref
851             let m = base_cmt.mutbl.inherit();
852             interior(elt, base_cmt, base_cmt.ty, m, element_ty)
853           }
854         };
855
856         fn interior<N: ast_node>(elt: &N,
857                                  of_cmt: cmt,
858                                  vec_ty: ty::t,
859                                  mutbl: MutabilityCategory,
860                                  element_ty: ty::t) -> cmt
861         {
862             @cmt_ {
863                 id:elt.id(),
864                 span:elt.span(),
865                 cat:cat_interior(of_cmt, InteriorElement(element_kind(vec_ty))),
866                 mutbl:mutbl,
867                 ty:element_ty
868             }
869         }
870     }
871
872     pub fn cat_slice_pattern(&mut self,
873                              vec_cmt: cmt,
874                              slice_pat: @ast::Pat)
875                              -> McResult<(cmt, ast::Mutability, ty::Region)> {
876         /*!
877          * Given a pattern P like: `[_, ..Q, _]`, where `vec_cmt` is
878          * the cmt for `P`, `slice_pat` is the pattern `Q`, returns:
879          * - a cmt for `Q`
880          * - the mutability and region of the slice `Q`
881          *
882          * These last two bits of info happen to be things that
883          * borrowck needs.
884          */
885
886         let slice_ty = if_ok!(self.node_ty(slice_pat.id));
887         let (slice_mutbl, slice_r) = vec_slice_info(self.tcx(),
888                                                     slice_pat,
889                                                     slice_ty);
890         let cmt_slice = self.cat_index(slice_pat, vec_cmt, 0);
891         return Ok((cmt_slice, slice_mutbl, slice_r));
892
893         fn vec_slice_info(tcx: ty::ctxt,
894                           pat: @ast::Pat,
895                           slice_ty: ty::t)
896                           -> (ast::Mutability, ty::Region) {
897             /*!
898              * In a pattern like [a, b, ..c], normally `c` has slice type,
899              * but if you have [a, b, ..ref c], then the type of `ref c`
900              * will be `&&[]`, so to extract the slice details we have
901              * to recurse through rptrs.
902              */
903
904             match ty::get(slice_ty).sty {
905                 ty::ty_vec(slice_mt, ty::vstore_slice(slice_r)) => {
906                     (slice_mt.mutbl, slice_r)
907                 }
908
909                 ty::ty_rptr(_, ref mt) => {
910                     vec_slice_info(tcx, pat, mt.ty)
911                 }
912
913                 _ => {
914                     tcx.sess.span_bug(
915                         pat.span,
916                         format!("Type of slice pattern is not a slice"));
917                 }
918             }
919         }
920     }
921
922     pub fn cat_imm_interior<N:ast_node>(&mut self,
923                                         node: &N,
924                                         base_cmt: cmt,
925                                         interior_ty: ty::t,
926                                         interior: InteriorKind)
927                                         -> cmt {
928         @cmt_ {
929             id: node.id(),
930             span: node.span(),
931             cat: cat_interior(base_cmt, interior),
932             mutbl: base_cmt.mutbl.inherit(),
933             ty: interior_ty
934         }
935     }
936
937     pub fn cat_downcast<N:ast_node>(&mut self,
938                                     node: &N,
939                                     base_cmt: cmt,
940                                     downcast_ty: ty::t)
941                                     -> cmt {
942         @cmt_ {
943             id: node.id(),
944             span: node.span(),
945             cat: cat_downcast(base_cmt),
946             mutbl: base_cmt.mutbl.inherit(),
947             ty: downcast_ty
948         }
949     }
950
951     pub fn cat_pattern(&mut self,
952                        cmt: cmt,
953                        pat: @ast::Pat,
954                        op: |&mut MemCategorizationContext<TYPER>,
955                             cmt,
956                             @ast::Pat|)
957                        -> McResult<()> {
958         // Here, `cmt` is the categorization for the value being
959         // matched and pat is the pattern it is being matched against.
960         //
961         // In general, the way that this works is that we walk down
962         // the pattern, constructing a cmt that represents the path
963         // that will be taken to reach the value being matched.
964         //
965         // When we encounter named bindings, we take the cmt that has
966         // been built up and pass it off to guarantee_valid() so that
967         // we can be sure that the binding will remain valid for the
968         // duration of the arm.
969         //
970         // (*2) There is subtlety concerning the correspondence between
971         // pattern ids and types as compared to *expression* ids and
972         // types. This is explained briefly. on the definition of the
973         // type `cmt`, so go off and read what it says there, then
974         // come back and I'll dive into a bit more detail here. :) OK,
975         // back?
976         //
977         // In general, the id of the cmt should be the node that
978         // "produces" the value---patterns aren't executable code
979         // exactly, but I consider them to "execute" when they match a
980         // value, and I consider them to produce the value that was
981         // matched. So if you have something like:
982         //
983         //     let x = @@3;
984         //     match x {
985         //       @@y { ... }
986         //     }
987         //
988         // In this case, the cmt and the relevant ids would be:
989         //
990         //     CMT             Id                  Type of Id Type of cmt
991         //
992         //     local(x)->@->@
993         //     ^~~~~~~^        `x` from discr      @@int      @@int
994         //     ^~~~~~~~~~^     `@@y` pattern node  @@int      @int
995         //     ^~~~~~~~~~~~~^  `@y` pattern node   @int       int
996         //
997         // You can see that the types of the id and the cmt are in
998         // sync in the first line, because that id is actually the id
999         // of an expression. But once we get to pattern ids, the types
1000         // step out of sync again. So you'll see below that we always
1001         // get the type of the *subpattern* and use that.
1002
1003         let tcx = self.tcx();
1004         debug!("cat_pattern: id={} pat={} cmt={}",
1005                pat.id, pprust::pat_to_str(pat),
1006                cmt.repr(tcx));
1007
1008         op(self, cmt, pat);
1009
1010         match pat.node {
1011           ast::PatWild | ast::PatWildMulti => {
1012             // _
1013           }
1014
1015           ast::PatEnum(_, None) => {
1016             // variant(..)
1017           }
1018           ast::PatEnum(_, Some(ref subpats)) => {
1019             let def_map = self.tcx().def_map.borrow();
1020             match def_map.get().find(&pat.id) {
1021                 Some(&ast::DefVariant(enum_did, _, _)) => {
1022                     // variant(x, y, z)
1023
1024                     let downcast_cmt = {
1025                         if ty::enum_is_univariant(self.tcx(), enum_did) {
1026                             cmt // univariant, no downcast needed
1027                         } else {
1028                             self.cat_downcast(pat, cmt, cmt.ty)
1029                         }
1030                     };
1031
1032                     for (i, &subpat) in subpats.iter().enumerate() {
1033                         let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2)
1034
1035                         let subcmt =
1036                             self.cat_imm_interior(
1037                                 pat, downcast_cmt, subpat_ty,
1038                                 InteriorField(PositionalField(i)));
1039
1040                         if_ok!(self.cat_pattern(subcmt, subpat, |x,y,z| op(x,y,z)));
1041                     }
1042                 }
1043                 Some(&ast::DefFn(..)) |
1044                 Some(&ast::DefStruct(..)) => {
1045                     for (i, &subpat) in subpats.iter().enumerate() {
1046                         let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2)
1047                         let cmt_field =
1048                             self.cat_imm_interior(
1049                                 pat, cmt, subpat_ty,
1050                                 InteriorField(PositionalField(i)));
1051                         if_ok!(self.cat_pattern(cmt_field, subpat, |x,y,z| op(x,y,z)));
1052                     }
1053                 }
1054                 Some(&ast::DefStatic(..)) => {
1055                     for &subpat in subpats.iter() {
1056                         if_ok!(self.cat_pattern(cmt, subpat, |x,y,z| op(x,y,z)));
1057                     }
1058                 }
1059                 _ => {
1060                     self.tcx().sess.span_bug(
1061                         pat.span,
1062                         "enum pattern didn't resolve to enum or struct");
1063                 }
1064             }
1065           }
1066
1067           ast::PatIdent(_, _, Some(subpat)) => {
1068               if_ok!(self.cat_pattern(cmt, subpat, op));
1069           }
1070
1071           ast::PatIdent(_, _, None) => {
1072               // nullary variant or identifier: ignore
1073           }
1074
1075           ast::PatStruct(_, ref field_pats, _) => {
1076             // {f1: p1, ..., fN: pN}
1077             for fp in field_pats.iter() {
1078                 let field_ty = if_ok!(self.pat_ty(fp.pat)); // see (*2)
1079                 let cmt_field = self.cat_field(pat, cmt, fp.ident, field_ty);
1080                 if_ok!(self.cat_pattern(cmt_field, fp.pat, |x,y,z| op(x,y,z)));
1081             }
1082           }
1083
1084           ast::PatTup(ref subpats) => {
1085             // (p1, ..., pN)
1086             for (i, &subpat) in subpats.iter().enumerate() {
1087                 let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2)
1088                 let subcmt =
1089                     self.cat_imm_interior(
1090                         pat, cmt, subpat_ty,
1091                         InteriorField(PositionalField(i)));
1092                 if_ok!(self.cat_pattern(subcmt, subpat, |x,y,z| op(x,y,z)));
1093             }
1094           }
1095
1096           ast::PatUniq(subpat) | ast::PatRegion(subpat) => {
1097             // @p1, ~p1
1098             let subcmt = self.cat_deref(pat, cmt, 0);
1099             if_ok!(self.cat_pattern(subcmt, subpat, op));
1100           }
1101
1102           ast::PatVec(ref before, slice, ref after) => {
1103               let elt_cmt = self.cat_index(pat, cmt, 0);
1104               for &before_pat in before.iter() {
1105                   if_ok!(self.cat_pattern(elt_cmt, before_pat, |x,y,z| op(x,y,z)));
1106               }
1107               for &slice_pat in slice.iter() {
1108                   let slice_ty = if_ok!(self.pat_ty(slice_pat));
1109                   let slice_cmt = self.cat_rvalue_node(pat.id(), pat.span(), slice_ty);
1110                   if_ok!(self.cat_pattern(slice_cmt, slice_pat, |x,y,z| op(x,y,z)));
1111               }
1112               for &after_pat in after.iter() {
1113                   if_ok!(self.cat_pattern(elt_cmt, after_pat, |x,y,z| op(x,y,z)));
1114               }
1115           }
1116
1117           ast::PatLit(_) | ast::PatRange(_, _) => {
1118               /*always ok*/
1119           }
1120         }
1121
1122         Ok(())
1123     }
1124
1125     pub fn mut_to_str(&mut self, mutbl: ast::Mutability) -> ~str {
1126         match mutbl {
1127           MutMutable => ~"mutable",
1128           MutImmutable => ~"immutable"
1129         }
1130     }
1131
1132     pub fn cmt_to_str(&self, cmt: cmt) -> ~str {
1133         match cmt.cat {
1134           cat_static_item => {
1135               ~"static item"
1136           }
1137           cat_copied_upvar(_) => {
1138               ~"captured outer variable in a heap closure"
1139           }
1140           cat_rvalue(..) => {
1141               ~"non-lvalue"
1142           }
1143           cat_local(_) => {
1144               ~"local variable"
1145           }
1146           cat_arg(..) => {
1147               ~"argument"
1148           }
1149           cat_deref(base, _, pk) => {
1150               match base.cat {
1151                   cat_upvar(..) => {
1152                       format!("captured outer variable")
1153                   }
1154                   _ => {
1155                       format!("dereference of `{}`-pointer", ptr_sigil(pk))
1156                   }
1157               }
1158           }
1159           cat_interior(_, InteriorField(NamedField(_))) => {
1160               ~"field"
1161           }
1162           cat_interior(_, InteriorField(PositionalField(_))) => {
1163               ~"anonymous field"
1164           }
1165           cat_interior(_, InteriorElement(VecElement)) => {
1166               ~"vec content"
1167           }
1168           cat_interior(_, InteriorElement(StrElement)) => {
1169               ~"str content"
1170           }
1171           cat_interior(_, InteriorElement(OtherElement)) => {
1172               ~"indexed content"
1173           }
1174           cat_upvar(..) => {
1175               ~"captured outer variable"
1176           }
1177           cat_discr(cmt, _) => {
1178             self.cmt_to_str(cmt)
1179           }
1180           cat_downcast(cmt) => {
1181             self.cmt_to_str(cmt)
1182           }
1183         }
1184     }
1185
1186     pub fn region_to_str(&self, r: ty::Region) -> ~str {
1187         region_ptr_to_str(self.tcx(), r)
1188     }
1189 }
1190
1191 /// The node_id here is the node of the expression that references the field.
1192 /// This function looks it up in the def map in case the type happens to be
1193 /// an enum to determine which variant is in use.
1194 pub fn field_mutbl(tcx: ty::ctxt,
1195                    base_ty: ty::t,
1196                    // FIXME #6993: change type to Name
1197                    f_name: ast::Ident,
1198                    node_id: ast::NodeId)
1199                 -> Option<ast::Mutability> {
1200     // Need to refactor so that struct/enum fields can be treated uniformly.
1201     match ty::get(base_ty).sty {
1202       ty::ty_struct(did, _) => {
1203         let r = ty::lookup_struct_fields(tcx, did);
1204         for fld in r.iter() {
1205             if fld.name == f_name.name {
1206                 return Some(ast::MutImmutable);
1207             }
1208         }
1209       }
1210       ty::ty_enum(..) => {
1211         let def_map = tcx.def_map.borrow();
1212         match def_map.get().get_copy(&node_id) {
1213           ast::DefVariant(_, variant_id, _) => {
1214             let r = ty::lookup_struct_fields(tcx, variant_id);
1215             for fld in r.iter() {
1216                 if fld.name == f_name.name {
1217                     return Some(ast::MutImmutable);
1218                 }
1219             }
1220           }
1221           _ => {}
1222         }
1223       }
1224       _ => { }
1225     }
1226
1227     return None;
1228 }
1229
1230 pub enum AliasableReason {
1231     AliasableManaged,
1232     AliasableBorrowed,
1233     AliasableOther,
1234     AliasableStatic,
1235     AliasableStaticMut,
1236 }
1237
1238 impl cmt_ {
1239     pub fn guarantor(self) -> cmt {
1240         //! Returns `self` after stripping away any owned pointer derefs or
1241         //! interior content. The return value is basically the `cmt` which
1242         //! determines how long the value in `self` remains live.
1243
1244         match self.cat {
1245             cat_rvalue(..) |
1246             cat_static_item |
1247             cat_copied_upvar(..) |
1248             cat_local(..) |
1249             cat_arg(..) |
1250             cat_deref(_, _, UnsafePtr(..)) |
1251             cat_deref(_, _, GcPtr(..)) |
1252             cat_deref(_, _, BorrowedPtr(..)) |
1253             cat_upvar(..) => {
1254                 @self
1255             }
1256             cat_downcast(b) |
1257             cat_discr(b, _) |
1258             cat_interior(b, _) |
1259             cat_deref(b, _, OwnedPtr) => {
1260                 b.guarantor()
1261             }
1262         }
1263     }
1264
1265     pub fn freely_aliasable(&self) -> Option<AliasableReason> {
1266         /*!
1267          * Returns `Some(_)` if this lvalue represents a freely aliasable
1268          * pointer type.
1269          */
1270
1271         // Maybe non-obvious: copied upvars can only be considered
1272         // non-aliasable in once closures, since any other kind can be
1273         // aliased and eventually recused.
1274
1275         match self.cat {
1276             cat_deref(b, _, BorrowedPtr(ty::MutBorrow, _)) |
1277             cat_deref(b, _, BorrowedPtr(ty::UniqueImmBorrow, _)) |
1278             cat_downcast(b) |
1279             cat_deref(b, _, OwnedPtr) |
1280             cat_interior(b, _) |
1281             cat_discr(b, _) => {
1282                 // Aliasability depends on base cmt
1283                 b.freely_aliasable()
1284             }
1285
1286             cat_copied_upvar(CopiedUpvar {onceness: ast::Once, ..}) |
1287             cat_rvalue(..) |
1288             cat_local(..) |
1289             cat_upvar(..) |
1290             cat_arg(_) |
1291             cat_deref(_, _, UnsafePtr(..)) => { // yes, it's aliasable, but...
1292                 None
1293             }
1294
1295             cat_copied_upvar(CopiedUpvar {onceness: ast::Many, ..}) => {
1296                 Some(AliasableOther)
1297             }
1298
1299             cat_static_item(..) => {
1300                 if self.mutbl.is_mutable() {
1301                     Some(AliasableStaticMut)
1302                 } else {
1303                     Some(AliasableStatic)
1304                 }
1305             }
1306
1307             cat_deref(_, _, GcPtr) => {
1308                 Some(AliasableManaged)
1309             }
1310
1311             cat_deref(_, _, BorrowedPtr(ty::ImmBorrow, _)) => {
1312                 Some(AliasableBorrowed)
1313             }
1314         }
1315     }
1316 }
1317
1318 impl Repr for cmt_ {
1319     fn repr(&self, tcx: ty::ctxt) -> ~str {
1320         format!("\\{{} id:{} m:{:?} ty:{}\\}",
1321              self.cat.repr(tcx),
1322              self.id,
1323              self.mutbl,
1324              self.ty.repr(tcx))
1325     }
1326 }
1327
1328 impl Repr for categorization {
1329     fn repr(&self, tcx: ty::ctxt) -> ~str {
1330         match *self {
1331             cat_static_item |
1332             cat_rvalue(..) |
1333             cat_copied_upvar(..) |
1334             cat_local(..) |
1335             cat_upvar(..) |
1336             cat_arg(..) => {
1337                 format!("{:?}", *self)
1338             }
1339             cat_deref(cmt, derefs, ptr) => {
1340                 format!("{}-{}{}->",
1341                         cmt.cat.repr(tcx),
1342                         ptr_sigil(ptr),
1343                         derefs)
1344             }
1345             cat_interior(cmt, interior) => {
1346                 format!("{}.{}",
1347                      cmt.cat.repr(tcx),
1348                      interior.repr(tcx))
1349             }
1350             cat_downcast(cmt) => {
1351                 format!("{}->(enum)", cmt.cat.repr(tcx))
1352             }
1353             cat_discr(cmt, _) => {
1354                 cmt.cat.repr(tcx)
1355             }
1356         }
1357     }
1358 }
1359
1360 pub fn ptr_sigil(ptr: PointerKind) -> &'static str {
1361     match ptr {
1362         OwnedPtr => "~",
1363         GcPtr => "@",
1364         BorrowedPtr(ty::ImmBorrow, _) => "&",
1365         BorrowedPtr(ty::MutBorrow, _) => "&mut",
1366         BorrowedPtr(ty::UniqueImmBorrow, _) => "&unique",
1367         UnsafePtr(_) => "*"
1368     }
1369 }
1370
1371 impl Repr for InteriorKind {
1372     fn repr(&self, _tcx: ty::ctxt) -> ~str {
1373         match *self {
1374             InteriorField(NamedField(fld)) => {
1375                 token::get_name(fld).get().to_str()
1376             }
1377             InteriorField(PositionalField(i)) => format!("\\#{:?}", i),
1378             InteriorElement(_) => ~"[]",
1379         }
1380     }
1381 }
1382
1383 fn element_kind(t: ty::t) -> ElementKind {
1384     match ty::get(t).sty {
1385         ty::ty_vec(..) => VecElement,
1386         ty::ty_str(..) => StrElement,
1387         _ => OtherElement
1388     }
1389 }