about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/kind.rs2
-rw-r--r--src/librustc/middle/region.rs29
-rw-r--r--src/librustc/middle/subst.rs47
-rw-r--r--src/librustc/middle/ty.rs3
-rw-r--r--src/librustc/middle/typeck/check/method.rs283
-rw-r--r--src/librustc/middle/typeck/check/mod.rs71
-rw-r--r--src/librustc/middle/typeck/check/regionmanip.rs42
-rw-r--r--src/librustc/middle/typeck/check/vtable.rs7
-rw-r--r--src/librustc/middle/typeck/coherence.rs2
-rw-r--r--src/librustc/middle/typeck/collect.rs97
-rw-r--r--src/librustc/middle/typeck/rscope.rs5
-rw-r--r--src/librustc/util/ppaux.rs6
-rw-r--r--src/libsyntax/ast.rs9
-rw-r--r--src/libsyntax/ext/base.rs13
-rw-r--r--src/libsyntax/opt_vec.rs11
-rw-r--r--src/libsyntax/print/pprust.rs4
16 files changed, 336 insertions, 295 deletions
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index 1f8401c0d53..e5fc9f2d603 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -16,7 +16,7 @@ use middle::liveness;
 use middle::pat_util;
 use middle::ty;
 use middle::typeck;
-use util::ppaux::{Repr, ty_to_str, tys_to_str};
+use util::ppaux::{Repr, ty_to_str};
 
 use syntax::ast::*;
 use syntax::attr::attrs_contains_name;
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index 79130097078..ecb9fc2cd08 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -544,10 +544,6 @@ pub struct DetermineRpCtxt {
     // see long discussion on region_is_relevant().
     anon_implies_rp: bool,
 
-    // true when we are not within an &self method.
-    // see long discussion on region_is_relevant().
-    self_implies_rp: bool,
-
     // encodes the context of the current type; invariant if
     // mutable, covariant otherwise
     ambient_variance: region_variance,
@@ -689,7 +685,7 @@ pub impl DetermineRpCtxt {
                 false
             }
             Some(ref l) if l.ident == special_idents::self_ => {
-                self.self_implies_rp
+                true
             }
             Some(_) => {
                 false
@@ -700,23 +696,18 @@ pub impl DetermineRpCtxt {
     fn with(@mut self,
             item_id: ast::node_id,
             anon_implies_rp: bool,
-            self_implies_rp: bool,
             f: &fn()) {
         let old_item_id = self.item_id;
         let old_anon_implies_rp = self.anon_implies_rp;
-        let old_self_implies_rp = self.self_implies_rp;
         self.item_id = item_id;
         self.anon_implies_rp = anon_implies_rp;
-        self.self_implies_rp = self_implies_rp;
-        debug!("with_item_id(%d, %b, %b)",
+        debug!("with_item_id(%d, %b)",
                item_id,
-               anon_implies_rp,
-               self_implies_rp);
+               anon_implies_rp);
         let _i = ::util::common::indenter();
         f();
         self.item_id = old_item_id;
         self.anon_implies_rp = old_anon_implies_rp;
-        self.self_implies_rp = old_self_implies_rp;
     }
 
     fn with_ambient_variance(@mut self, variance: region_variance, f: &fn()) {
@@ -730,7 +721,7 @@ pub impl DetermineRpCtxt {
 pub fn determine_rp_in_item(item: @ast::item,
                             &&cx: @mut DetermineRpCtxt,
                             visitor: visit::vt<@mut DetermineRpCtxt>) {
-    do cx.with(item.id, true, true) {
+    do cx.with(item.id, true) {
         visit::visit_item(item, cx, visitor);
     }
 }
@@ -742,12 +733,7 @@ pub fn determine_rp_in_fn(fk: &visit::fn_kind,
                           _: ast::node_id,
                           &&cx: @mut DetermineRpCtxt,
                           visitor: visit::vt<@mut DetermineRpCtxt>) {
-    let self_implies_rp = match fk {
-        &visit::fk_method(_, _, m) => !m.self_ty.node.is_borrowed(),
-        _ => true
-    };
-
-    do cx.with(cx.item_id, false, self_implies_rp) {
+    do cx.with(cx.item_id, false) {
         do cx.with_ambient_variance(rv_contravariant) {
             for decl.inputs.each |a| {
                 (visitor.visit_ty)(a.ty, cx, visitor);
@@ -763,7 +749,7 @@ pub fn determine_rp_in_fn(fk: &visit::fn_kind,
 pub fn determine_rp_in_ty_method(ty_m: &ast::ty_method,
                                  &&cx: @mut DetermineRpCtxt,
                                  visitor: visit::vt<@mut DetermineRpCtxt>) {
-    do cx.with(cx.item_id, false, !ty_m.self_ty.node.is_borrowed()) {
+    do cx.with(cx.item_id, false) {
         visit::visit_ty_method(ty_m, cx, visitor);
     }
 }
@@ -868,7 +854,7 @@ pub fn determine_rp_in_ty(ty: @ast::Ty,
       ast::ty_bare_fn(@ast::TyBareFn {decl: ref decl, _}) => {
         // fn() binds the & region, so do not consider &T types that
         // appear *inside* a fn() type to affect the enclosing item:
-        do cx.with(cx.item_id, false, true) {
+        do cx.with(cx.item_id, false) {
             // parameters are contravariant
             do cx.with_ambient_variance(rv_contravariant) {
                 for decl.inputs.each |a| {
@@ -929,7 +915,6 @@ pub fn determine_rp_in_crate(sess: Session,
         worklist: ~[],
         item_id: 0,
         anon_implies_rp: false,
-        self_implies_rp: true,
         ambient_variance: rv_covariant
     };
 
diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs
index a754f93f010..35257f9574c 100644
--- a/src/librustc/middle/subst.rs
+++ b/src/librustc/middle/subst.rs
@@ -62,22 +62,7 @@ impl EffectfulSubst for ty::t {
             _ => {
                 ty::fold_regions_and_ty(
                     tcx, *self,
-                    |r| match r {
-                        ty::re_bound(ty::br_self) => {
-                            match substs.self_r {
-                                None => {
-                                    tcx.sess.bug(
-                                        fmt!("ty::subst: \
-                                              Reference to self region when \
-                                              given substs with no self region, \
-                                              ty = %s",
-                                             self.repr(tcx)));
-                                }
-                                Some(self_r) => self_r
-                            }
-                        }
-                        _ => r
-                    },
+                    |r| r.subst(tcx, substs),
                     |t| t.effectfulSubst(tcx, substs),
                     |t| t.effectfulSubst(tcx, substs))
             }
@@ -118,7 +103,7 @@ impl Subst for ty::TraitRef {
 impl Subst for ty::substs {
     fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::substs {
         ty::substs {
-            self_r: self.self_r,
+            self_r: self.self_r.subst(tcx, substs),
             self_ty: self.self_ty.map(|typ| typ.subst(tcx, substs)),
             tps: self.tps.map(|typ| typ.subst(tcx, substs))
         }
@@ -166,6 +151,34 @@ impl Subst for ty::Generics {
     }
 }
 
+impl Subst for ty::Region {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::Region {
+        // Note: This routine only handles the self region, because it
+        // is only concerned with substitutions of regions that appear
+        // in types. Region substitution of the bound regions that
+        // appear in a function signature is done using the
+        // specialized routine
+        // `middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig()`.
+        // As we transition to the new region syntax this distinction
+        // will most likely disappear.
+        match self {
+            &ty::re_bound(ty::br_self) => {
+                match substs.self_r {
+                    None => {
+                        tcx.sess.bug(
+                            fmt!("ty::Region#subst(): \
+                                  Reference to self region when \
+                                  given substs with no self region: %s",
+                                 substs.repr(tcx)));
+                    }
+                    Some(self_r) => self_r
+                }
+            }
+            _ => *self
+        }
+    }
+}
+
 impl Subst for ty::ty_param_bounds_and_ty {
     fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::ty_param_bounds_and_ty {
         ty::ty_param_bounds_and_ty {
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 3921764e6af..2b4a4235950 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -24,8 +24,7 @@ use middle::subst::Subst;
 use middle::typeck;
 use middle;
 use util::ppaux::{note_and_explain_region, bound_region_to_str};
-use util::ppaux::{region_to_str, vstore_to_str};
-use util::ppaux::{trait_store_to_str, ty_to_str, tys_to_str};
+use util::ppaux::{trait_store_to_str, ty_to_str, vstore_to_str};
 use util::ppaux::Repr;
 use util::common::{indenter};
 
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index 6a274e7f9eb..6b09133e73a 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -178,15 +178,6 @@ pub struct Candidate {
     origin: method_origin,
 }
 
-/**
- * How the self type should be transformed according to the form of explicit
- * self provided by the method.
- */
-pub enum TransformTypeFlag {
-    TransformTypeNormally,
-    TransformTypeForObject,
-}
-
 pub impl<'self> LookupContext<'self> {
     fn do_lookup(&self, self_ty: ty::t) -> Option<method_map_entry> {
         let mut self_ty = structurally_resolved_type(self.fcx,
@@ -285,13 +276,13 @@ pub impl<'self> LookupContext<'self> {
 
     fn push_inherent_candidates(&self, self_ty: ty::t) {
         /*!
-         *
          * Collect all inherent candidates into
          * `self.inherent_candidates`.  See comment at the start of
          * the file.  To find the inherent candidates, we repeatedly
          * deref the self-ty to find the "base-type".  So, for
          * example, if the receiver is @@C where `C` is a struct type,
-         * we'll want to find the inherent impls for `C`. */
+         * we'll want to find the inherent impls for `C`.
+         */
 
         let mut enum_dids = ~[];
         let mut self_ty = self_ty;
@@ -407,16 +398,9 @@ pub impl<'self> LookupContext<'self> {
             };
             let method = trait_methods[pos];
 
-            let (rcvr_ty, rcvr_substs) =
-                self.create_rcvr_ty_and_substs_for_method(
-                    method.self_ty,
-                    rcvr_ty,
-                    copy bound_trait_ref.substs,
-                    TransformTypeNormally);
-
             let cand = Candidate {
                 rcvr_ty: rcvr_ty,
-                rcvr_substs: rcvr_substs,
+                rcvr_substs: copy bound_trait_ref.substs,
                 method_ty: method,
                 origin: method_param(
                     method_param {
@@ -476,14 +460,8 @@ pub impl<'self> LookupContext<'self> {
             ../*bad*/copy *substs
         };
 
-        let (rcvr_ty, rcvr_substs) =
-            self.create_rcvr_ty_and_substs_for_method(method.self_ty,
-                                                      self_ty,
-                                                      rcvr_substs,
-                                                      TransformTypeForObject);
-
         self.inherent_candidates.push(Candidate {
-            rcvr_ty: rcvr_ty,
+            rcvr_ty: self_ty,
             rcvr_substs: rcvr_substs,
             method_ty: method,
             origin: method_trait(did, index, store)
@@ -538,19 +516,13 @@ pub impl<'self> LookupContext<'self> {
                 // We've found a method -- return it
                 let rcvr_substs = substs {self_ty: Some(self_ty),
                                           ..copy *substs };
-                let (rcvr_ty, rcvr_substs) =
-                    self.create_rcvr_ty_and_substs_for_method(
-                        info.method_ty.self_ty,
-                        self_ty,
-                        rcvr_substs,
-                        TransformTypeNormally);
                 let origin = if did == info.trait_def_id {
                     method_self(info.trait_def_id, info.index)
                 } else {
                     method_super(info.trait_def_id, info.index)
                 };
                 self.inherent_candidates.push(Candidate {
-                    rcvr_ty: rcvr_ty,
+                    rcvr_ty: self_ty,
                     rcvr_substs: rcvr_substs,
                     method_ty: info.method_ty,
                     origin: origin
@@ -598,13 +570,6 @@ pub impl<'self> LookupContext<'self> {
             ty: impl_ty
         } = impl_self_ty(&vcx, location_info, impl_info.did);
 
-        let (impl_ty, impl_substs) =
-            self.create_rcvr_ty_and_substs_for_method(
-                method.self_ty,
-                impl_ty,
-                impl_substs,
-                TransformTypeNormally);
-
         candidates.push(Candidate {
             rcvr_ty: impl_ty,
             rcvr_substs: impl_substs,
@@ -639,69 +604,16 @@ pub impl<'self> LookupContext<'self> {
                 self_ty: None,
                 tps: ~[]
             };
-            let (impl_ty, impl_substs) =
-                self.create_rcvr_ty_and_substs_for_method(
-                    method.self_ty,
-                    self_ty,
-                    dummy_substs,
-                    TransformTypeNormally);
 
             candidates.push(Candidate {
-                rcvr_ty: impl_ty,
-                rcvr_substs: impl_substs,
+                rcvr_ty: self_ty,
+                rcvr_substs: dummy_substs,
                 method_ty: method,
                 origin: method_static(provided_method_info.method_info.did)
             });
         }
     }
 
-    fn create_rcvr_ty_and_substs_for_method(&self,
-                                            self_decl: ast::self_ty_,
-                                            self_ty: ty::t,
-                                            +self_substs: ty::substs,
-                                            transform_type: TransformTypeFlag)
-                                         -> (ty::t, ty::substs) {
-        // If the self type includes a region (like &self), we need to
-        // ensure that the receiver substitutions have a self region.
-        // If the receiver type does not itself contain borrowed
-        // pointers, there may not be one yet.
-        //
-        // FIXME(#3446)--this awkward situation comes about because
-        // the regions in the receiver are substituted before (and
-        // differently from) those in the argument types.  This
-        // shouldn't really have to be.
-        let rcvr_substs = {
-            match self_decl {
-                sty_static | sty_value |
-                sty_box(_) | sty_uniq(_) => {
-                    self_substs
-                }
-                sty_region(*) if self_substs.self_r.is_some() => {
-                    // FIXME(#4846) ignoring expl lifetime here
-                    self_substs
-                }
-                sty_region(*) => {
-                    // FIXME(#4846) ignoring expl lifetime here
-                    substs {
-                        self_r:
-                             Some(self.infcx().next_region_var(
-                                 self.expr.span,
-                                 self.expr.id)),
-                        ..self_substs
-                    }
-                }
-            }
-        };
-
-        let rcvr_ty = transform_self_type_for_method(self.tcx(),
-                                                     rcvr_substs.self_r,
-                                                     self_ty,
-                                                     self_decl,
-                                                     transform_type);
-
-        (rcvr_ty, rcvr_substs)
-    }
-
     // ______________________________________________________________________
     // Candidate selection (see comment at start of file)
 
@@ -1036,20 +948,34 @@ pub impl<'self> LookupContext<'self> {
         self.enforce_trait_instance_limitations(fty, candidate);
         self.enforce_drop_trait_limitations(candidate);
 
-        // before we only checked whether self_ty could be a subtype
-        // of rcvr_ty; now we actually make it so (this may cause
-        // variables to unify etc).  Since we checked beforehand, and
-        // nothing has changed in the meantime, this unification
-        // should never fail.
-        match self.fcx.mk_subty(false, self.self_expr.span,
-                                self_ty, candidate.rcvr_ty) {
-            result::Ok(_) => (),
-            result::Err(_) => {
-                self.bug(fmt!("%s was assignable to %s but now is not?",
-                              self.ty_to_str(self_ty),
-                              self.ty_to_str(candidate.rcvr_ty)));
+        // static methods should never have gotten this far:
+        assert!(candidate.method_ty.self_ty != sty_static);
+
+        let transformed_self_ty = match candidate.origin {
+            method_trait(*) => {
+                match candidate.method_ty.self_ty {
+                    sty_region(*) => {
+                        // FIXME(#5762) again, preserving existing
+                        // behavior here which (for &self) desires
+                        // &@Trait where @Trait is the type of the
+                        // receiver.  Here we fetch the method's
+                        // transformed_self_ty which will be something
+                        // like &'a Self.  We then perform a
+                        // substitution which will replace Self with
+                        // @Trait.
+                        let t = candidate.method_ty.transformed_self_ty.get();
+                        ty::subst(tcx, &candidate.rcvr_substs, t)
+                    }
+                    _ => {
+                        candidate.rcvr_ty
+                    }
+                }
             }
-        }
+            _ => {
+                let t = candidate.method_ty.transformed_self_ty.get();
+                ty::subst(tcx, &candidate.rcvr_substs, t)
+            }
+        };
 
         // Determine the values for the type parameters of the method.
         // If they were not explicitly supplied, just construct fresh
@@ -1100,16 +1026,32 @@ pub impl<'self> LookupContext<'self> {
                     fmt!("Invoking method with non-bare-fn ty: %?", s));
             }
         };
-        let (_, _, fn_sig) =
+        let (_, opt_transformed_self_ty, fn_sig) =
             replace_bound_regions_in_fn_sig(
-                tcx, @Nil, None, &bare_fn_ty.sig,
+                tcx, @Nil, Some(transformed_self_ty), &bare_fn_ty.sig,
                 |_br| self.fcx.infcx().next_region_var(
                     self.expr.span, self.expr.id));
+        let transformed_self_ty = opt_transformed_self_ty.get();
         let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {sig: fn_sig, ..bare_fn_ty});
         debug!("after replacing bound regions, fty=%s", self.ty_to_str(fty));
 
         let self_mode = get_mode_from_self_type(candidate.method_ty.self_ty);
 
+        // before we only checked whether self_ty could be a subtype
+        // of rcvr_ty; now we actually make it so (this may cause
+        // variables to unify etc).  Since we checked beforehand, and
+        // nothing has changed in the meantime, this unification
+        // should never fail.
+        match self.fcx.mk_subty(false, self.self_expr.span,
+                                self_ty, transformed_self_ty) {
+            result::Ok(_) => (),
+            result::Err(_) => {
+                self.bug(fmt!("%s was a subtype of %s but now is not?",
+                              self.ty_to_str(self_ty),
+                              self.ty_to_str(transformed_self_ty)));
+            }
+        }
+
         self.fcx.write_ty(self.callee_id, fty);
         self.fcx.write_substs(self.callee_id, all_substs);
         method_map_entry {
@@ -1180,7 +1122,87 @@ pub impl<'self> LookupContext<'self> {
         debug!("is_relevant(self_ty=%s, candidate=%s)",
                self.ty_to_str(self_ty), self.cand_to_str(candidate));
 
-        self.fcx.can_mk_subty(self_ty, candidate.rcvr_ty).is_ok()
+        // Check for calls to object methods.  We resolve these differently.
+        //
+        // FIXME(#5762)---we don't check that an @self method is only called
+        // on an @Trait object here and so forth
+        match candidate.origin {
+            method_trait(*) => {
+                match candidate.method_ty.self_ty {
+                    sty_static | sty_value => {
+                        return false;
+                    }
+                    sty_region(*) => {
+                        // just echoing current behavior here, which treats
+                        // an &self method on an @Trait object as requiring
+                        // an &@Trait receiver (wacky)
+                    }
+                    sty_box(*) | sty_uniq(*) => {
+                        return self.fcx.can_mk_subty(self_ty,
+                                                     candidate.rcvr_ty).is_ok();
+                    }
+                };
+            }
+            _ => {}
+        }
+
+        return match candidate.method_ty.self_ty {
+            sty_static => {
+                false
+            }
+
+            sty_value => {
+                self.fcx.can_mk_subty(self_ty, candidate.rcvr_ty).is_ok()
+            }
+
+            sty_region(_, m) => {
+                match ty::get(self_ty).sty {
+                    ty::ty_rptr(_, mt) => {
+                        mutability_matches(mt.mutbl, m) &&
+                        self.fcx.can_mk_subty(mt.ty, candidate.rcvr_ty).is_ok()
+                    }
+
+                    _ => false
+                }
+            }
+
+            sty_box(m) => {
+                match ty::get(self_ty).sty {
+                    ty::ty_box(mt) => {
+                        mutability_matches(mt.mutbl, m) &&
+                        self.fcx.can_mk_subty(mt.ty, candidate.rcvr_ty).is_ok()
+                    }
+
+                    _ => false
+                }
+            }
+
+            sty_uniq(m) => {
+                match ty::get(self_ty).sty {
+                    ty::ty_uniq(mt) => {
+                        mutability_matches(mt.mutbl, m) &&
+                        self.fcx.can_mk_subty(mt.ty, candidate.rcvr_ty).is_ok()
+                    }
+
+                    _ => false
+                }
+            }
+        };
+
+        fn mutability_matches(self_mutbl: ast::mutability,
+                              candidate_mutbl: ast::mutability) -> bool {
+            //! True if `self_mutbl <: candidate_mutbl`
+
+            match (self_mutbl, candidate_mutbl) {
+                (_, m_const) => true,
+                (m_mutbl, m_mutbl) => true,
+                (m_imm, m_imm) => true,
+                (m_mutbl, m_imm) => false,
+                (m_imm, m_mutbl) => false,
+                (m_const, m_imm) => false,
+                (m_const, m_mutbl) => false,
+            }
+        }
     }
 
     fn fn_ty_from_origin(&self, origin: &method_origin) -> ty::t {
@@ -1281,45 +1303,6 @@ pub impl<'self> LookupContext<'self> {
     }
 }
 
-pub fn transform_self_type_for_method(tcx: ty::ctxt,
-                                      self_region: Option<ty::Region>,
-                                      impl_ty: ty::t,
-                                      self_type: ast::self_ty_,
-                                      flag: TransformTypeFlag)
-                                   -> ty::t {
-    match self_type {
-      sty_static => {
-        tcx.sess.bug(~"calling transform_self_type_for_method on \
-                       static method");
-      }
-      sty_value => {
-        impl_ty
-      }
-      sty_region(_, mutability) => {
-        // FIXME(#4846) ignoring expl lifetime here
-        mk_rptr(tcx,
-                self_region.expect(~"self region missing for &self param"),
-                ty::mt { ty: impl_ty, mutbl: mutability })
-      }
-      sty_box(mutability) => {
-        match flag {
-            TransformTypeNormally => {
-                mk_box(tcx, ty::mt { ty: impl_ty, mutbl: mutability })
-            }
-            TransformTypeForObject => impl_ty
-        }
-      }
-      sty_uniq(mutability) => {
-        match flag {
-            TransformTypeNormally => {
-                mk_uniq(tcx, ty::mt { ty: impl_ty, mutbl: mutability })
-            }
-            TransformTypeForObject => impl_ty
-        }
-      }
-    }
-}
-
 pub fn get_mode_from_self_type(self_type: ast::self_ty_) -> ast::rmode {
     match self_type { sty_value => by_copy, _ => by_ref }
 }
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 6bc1317d5f9..5c7a6d9f52a 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -93,7 +93,6 @@ use middle::typeck::check::method::{AutoderefReceiver};
 use middle::typeck::check::method::{AutoderefReceiverFlag};
 use middle::typeck::check::method::{CheckTraitsAndInherentMethods};
 use middle::typeck::check::method::{CheckTraitsOnly, DontAutoderefReceiver};
-use middle::typeck::check::method::{TransformTypeNormally};
 use middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig;
 use middle::typeck::check::regionmanip::relate_free_regions;
 use middle::typeck::check::vtable::{LocationInfo, VtableContext};
@@ -101,9 +100,8 @@ use middle::typeck::CrateCtxt;
 use middle::typeck::infer::{resolve_type, force_tvar};
 use middle::typeck::infer;
 use middle::typeck::rscope::bound_self_region;
-use middle::typeck::rscope::{RegionError, RegionParameterization};
+use middle::typeck::rscope::{RegionError};
 use middle::typeck::rscope::region_scope;
-use middle::typeck::rscope;
 use middle::typeck::{isr_alist, lookup_def_ccx};
 use middle::typeck::no_params;
 use middle::typeck::{require_same_types, method_map, vtable_map};
@@ -280,7 +278,7 @@ pub fn check_bare_fn(ccx: @mut CrateCtxt,
 }
 
 pub fn check_fn(ccx: @mut CrateCtxt,
-                +self_info: Option<SelfInfo>,
+                opt_self_info: Option<SelfInfo>,
                 purity: ast::purity,
                 fn_sig: &ty::FnSig,
                 decl: &ast::fn_decl,
@@ -307,23 +305,28 @@ pub fn check_fn(ccx: @mut CrateCtxt,
     // First, we have to replace any bound regions in the fn and self
     // types with free ones.  The free region references will be bound
     // the node_id of the body block.
-
-    let (isr, self_info, fn_sig) = {
-        replace_bound_regions_in_fn_sig(
-            tcx, inherited_isr, self_info, fn_sig,
-            |br| ty::re_free(ty::FreeRegion {scope_id: body.node.id,
-                                             bound_region: br}))
+    let (isr, opt_self_info, fn_sig) = {
+        let opt_self_ty = opt_self_info.map(|i| i.self_ty);
+        let (isr, opt_self_ty, fn_sig) =
+            replace_bound_regions_in_fn_sig(
+                tcx, inherited_isr, opt_self_ty, fn_sig,
+                |br| ty::re_free(ty::FreeRegion {scope_id: body.node.id,
+                                                 bound_region: br}));
+        let opt_self_info =
+            opt_self_info.map(
+                |si| SelfInfo {self_ty: opt_self_ty.get(), ..*si});
+        (isr, opt_self_info, fn_sig)
     };
 
-    relate_free_regions(tcx, self_info.map(|s| s.self_ty), &fn_sig);
+    relate_free_regions(tcx, opt_self_info.map(|s| s.self_ty), &fn_sig);
 
     let arg_tys = fn_sig.inputs.map(|a| a.ty);
     let ret_ty = fn_sig.output;
 
-    debug!("check_fn(arg_tys=%?, ret_ty=%?, self_info.self_ty=%?)",
-           arg_tys.map(|a| ppaux::ty_to_str(tcx, *a)),
+    debug!("check_fn(arg_tys=%?, ret_ty=%?, opt_self_ty=%?)",
+           arg_tys.map(|&a| ppaux::ty_to_str(tcx, a)),
            ppaux::ty_to_str(tcx, ret_ty),
-           self_info.map(|s| ppaux::ty_to_str(tcx, s.self_ty)));
+           opt_self_info.map(|si| ppaux::ty_to_str(tcx, si.self_ty)));
 
     // ______________________________________________________________________
     // Create the function context.  This is either derived from scratch or,
@@ -348,7 +351,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
         }
     };
 
-    gather_locals(fcx, decl, body, arg_tys, self_info);
+    gather_locals(fcx, decl, body, arg_tys, opt_self_info);
     check_block_with_expected(fcx, body, Some(ret_ty));
 
     // We unify the tail expr's type with the
@@ -366,7 +369,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
       None => ()
     }
 
-    for self_info.each |self_info| {
+    for opt_self_info.each |self_info| {
         fcx.write_ty(self_info.self_id, self_info.self_ty);
     }
     for vec::each2(decl.inputs, arg_tys) |input, arg| {
@@ -379,7 +382,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
                      decl: &ast::fn_decl,
                      body: &ast::blk,
                      arg_tys: &[ty::t],
-                     self_info: Option<SelfInfo>) {
+                     opt_self_info: Option<SelfInfo>) {
         let tcx = fcx.ccx.tcx;
 
         let assign: @fn(ast::node_id, Option<ty::t>) = |nid, ty_opt| {
@@ -398,7 +401,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
         };
 
         // Add the self parameter
-        for self_info.each |self_info| {
+        for opt_self_info.each |self_info| {
             assign(self_info.self_id, Some(self_info.self_ty));
             debug!("self is assigned to %s",
                    fcx.infcx().ty_to_str(
@@ -484,26 +487,22 @@ pub fn check_fn(ccx: @mut CrateCtxt,
 }
 
 pub fn check_method(ccx: @mut CrateCtxt,
-                    method: @ast::method,
-                    self_ty: ty::t)
+                    method: @ast::method)
 {
-    let self_info = if method.self_ty.node == ast::sty_static {None} else {
-        let ty = method::transform_self_type_for_method(
-            ccx.tcx,
-            Some(ty::re_bound(ty::br_self)),
-            self_ty,
-            method.self_ty.node,
-            TransformTypeNormally);
-        Some(SelfInfo {self_ty: ty, self_id: method.self_id,
-                       span: method.self_ty.span})
-    };
+    let method_def_id = local_def(method.id);
+    let method_ty = ty::method(ccx.tcx, method_def_id);
+    let opt_self_info = method_ty.transformed_self_ty.map(|&ty| {
+        SelfInfo {self_ty: ty,
+                  self_id: method.self_id,
+                  span: method.self_ty.span}
+    });
 
     check_bare_fn(
         ccx,
         &method.decl,
         &method.body,
         method.id,
-        self_info
+        opt_self_info
     );
 }
 
@@ -575,15 +574,12 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) {
       ast::item_fn(ref decl, _, _, _, ref body) => {
         check_bare_fn(ccx, decl, body, it.id, None);
       }
-      ast::item_impl(ref generics, _, ty, ref ms) => {
+      ast::item_impl(_, _, _, ref ms) => {
         let rp = ccx.tcx.region_paramd_items.find(&it.id).map_consume(|x| *x);
         debug!("item_impl %s with id %d rp %?",
                *ccx.tcx.sess.str_of(it.ident), it.id, rp);
-        let rp = RegionParameterization::from_variance_and_generics(
-            rp, generics);
-        let self_ty = ccx.to_ty(&rscope::type_rscope(rp), ty);
         for ms.each |m| {
-            check_method(ccx, *m, self_ty);
+            check_method(ccx, *m);
         }
       }
       ast::item_trait(_, _, ref trait_methods) => {
@@ -594,8 +590,7 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) {
                 // bodies to check.
               }
               provided(m) => {
-                let self_ty = ty::mk_self(ccx.tcx, local_def(it.id));
-                check_method(ccx, m, self_ty);
+                check_method(ccx, m);
               }
             }
         }
diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs
index 359f938d0d9..1abcefeefac 100644
--- a/src/librustc/middle/typeck/check/regionmanip.rs
+++ b/src/librustc/middle/typeck/check/regionmanip.rs
@@ -13,7 +13,7 @@
 use core::prelude::*;
 
 use middle::ty;
-use middle::typeck::check::SelfInfo;
+
 use middle::typeck::isr_alist;
 use util::common::indenter;
 use util::ppaux::region_to_str;
@@ -26,29 +26,24 @@ use std::list::Cons;
 pub fn replace_bound_regions_in_fn_sig(
     tcx: ty::ctxt,
     isr: isr_alist,
-    self_info: Option<SelfInfo>,
+    opt_self_ty: Option<ty::t>,
     fn_sig: &ty::FnSig,
     mapf: &fn(ty::bound_region) -> ty::Region)
-    -> (isr_alist, Option<SelfInfo>, ty::FnSig)
+    -> (isr_alist, Option<ty::t>, ty::FnSig)
 {
-    // Take self_info apart; the self_ty part is the only one we want
-    // to update here.
-    let self_ty = self_info.map(|s| s.self_ty);
-    let rebuild_self_info = |t| self_info.map(|s| SelfInfo{self_ty: t, ..*s});
-
     let mut all_tys = ty::tys_in_fn_sig(fn_sig);
 
-    for self_info.each |self_info| {
-        all_tys.push(self_info.self_ty);
+    for opt_self_ty.each |&self_ty| {
+        all_tys.push(self_ty);
     }
 
-    for self_ty.each |t| { all_tys.push(*t) }
+    for opt_self_ty.each |&t| { all_tys.push(t) }
 
-    debug!("replace_bound_regions_in_fn_sig(self_info.self_ty=%?, fn_sig=%s, \
+    debug!("replace_bound_regions_in_fn_sig(self_ty=%?, fn_sig=%s, \
             all_tys=%?)",
-           self_ty.map(|t| ppaux::ty_to_str(tcx, *t)),
+           opt_self_ty.map(|&t| ppaux::ty_to_str(tcx, t)),
            ppaux::fn_sig_to_str(tcx, fn_sig),
-           all_tys.map(|t| ppaux::ty_to_str(tcx, *t)));
+           all_tys.map(|&t| ppaux::ty_to_str(tcx, t)));
     let _i = indenter();
 
     let isr = do create_bound_region_mapping(tcx, isr, all_tys) |br| {
@@ -58,20 +53,15 @@ pub fn replace_bound_regions_in_fn_sig(
     let new_fn_sig = ty::fold_sig(fn_sig, |t| {
         replace_bound_regions(tcx, isr, t)
     });
-    let t_self = self_ty.map(|t| replace_bound_regions(tcx, isr, *t));
+    let new_self_ty = opt_self_ty.map(|&t| replace_bound_regions(tcx, isr, t));
 
-    debug!("result of replace_bound_regions_in_fn_sig: self_info.self_ty=%?, \
-                fn_sig=%s",
-           t_self.map(|t| ppaux::ty_to_str(tcx, *t)),
+    debug!("result of replace_bound_regions_in_fn_sig: \
+            new_self_ty=%?, \
+            fn_sig=%s",
+           new_self_ty.map(|&t| ppaux::ty_to_str(tcx, t)),
            ppaux::fn_sig_to_str(tcx, &new_fn_sig));
 
-    // Glue updated self_ty back together with its original def_id.
-    let new_self_info: Option<SelfInfo> = match t_self {
-      None    => None,
-      Some(t) => rebuild_self_info(t)
-    };
-
-    return (isr, new_self_info, new_fn_sig);
+    return (isr, new_self_ty, new_fn_sig);
 
     // Takes `isr`, a (possibly empty) mapping from in-scope region
     // names ("isr"s) to their corresponding regions; `tys`, a list of
@@ -288,4 +278,4 @@ pub fn relate_free_regions(
     }
 
     debug!("<< relate_free_regions");
-}
\ No newline at end of file
+}
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index 6ea668605fd..8245dc88114 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -11,7 +11,7 @@
 use core::prelude::*;
 
 use middle::resolve::Impl;
-use middle::ty::{param_ty, substs};
+use middle::ty::{param_ty};
 use middle::ty;
 use middle::typeck::check::{FnCtxt, impl_self_ty};
 use middle::typeck::check::{structurally_resolved_type};
@@ -489,6 +489,8 @@ pub fn early_resolve_expr(ex: @ast::expr,
     match ex.node {
       ast::expr_path(*) => {
         for fcx.opt_node_ty_substs(ex.id) |substs| {
+            debug!("vtable resolution on parameter bounds for expr %s",
+                   ex.repr(fcx.tcx()));
             let def = *cx.tcx.def_map.get(&ex.id);
             let did = ast_util::def_id_of_def(def);
             let item_ty = ty::lookup_item_type(cx.tcx, did);
@@ -518,6 +520,8 @@ pub fn early_resolve_expr(ex: @ast::expr,
       ast::expr_index(*) | ast::expr_method_call(*) => {
         match ty::method_call_type_param_defs(cx.tcx, fcx.inh.method_map, ex.id) {
           Some(type_param_defs) => {
+            debug!("vtable resolution on parameter bounds for method call %s",
+                   ex.repr(fcx.tcx()));
             if has_trait_bounds(*type_param_defs) {
                 let callee_id = match ex.node {
                   ast::expr_field(_, _, _) => ex.id,
@@ -537,6 +541,7 @@ pub fn early_resolve_expr(ex: @ast::expr,
         }
       }
       ast::expr_cast(src, _) => {
+          debug!("vtable resolution on expr %s", ex.repr(fcx.tcx()));
           let target_ty = fcx.expr_ty(ex);
           match ty::get(target_ty).sty {
               ty::ty_trait(target_def_id, ref target_substs, store) => {
diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs
index 174a20dd7f4..247b8eae2a8 100644
--- a/src/librustc/middle/typeck/coherence.rs
+++ b/src/librustc/middle/typeck/coherence.rs
@@ -24,7 +24,7 @@ use metadata::cstore::{CStore, iter_crate_data};
 use metadata::decoder::{dl_def, dl_field, dl_impl};
 use middle::resolve::{Impl, MethodInfo};
 use middle::ty::{ProvidedMethodSource, ProvidedMethodInfo, bound_copy, get};
-use middle::ty::{lookup_item_type, param_bounds, subst};
+use middle::ty::{lookup_item_type, subst};
 use middle::ty::{substs, t, ty_bool, ty_bot, ty_box, ty_enum, ty_err};
 use middle::ty::{ty_estr, ty_evec, ty_float, ty_infer, ty_int, ty_nil};
 use middle::ty::{ty_opaque_box, ty_param, ty_param_bounds_and_ty, ty_ptr};
diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs
index 0ef6e2512f3..59ea8ea039e 100644
--- a/src/librustc/middle/typeck/collect.rs
+++ b/src/librustc/middle/typeck/collect.rs
@@ -55,7 +55,7 @@ use syntax::ast_util::{local_def, split_trait_methods};
 use syntax::ast_util;
 use syntax::codemap::span;
 use syntax::codemap;
-use syntax::print::pprust::path_to_str;
+use syntax::print::pprust::{path_to_str, self_ty_to_str};
 use syntax::visit;
 use syntax::opt_vec::OptVec;
 use syntax::opt_vec;
@@ -453,31 +453,35 @@ pub fn compare_impl_method(tcx: ty::ctxt,
 
     let impl_m = &cm.mty;
 
-    // FIXME(#2687)---this check is too strict.  For example, a trait
-    // method with self type `&self` or `&mut self` should be
-    // implementable by an `&const self` method (the impl assumes less
-    // than the trait provides).
-    if impl_m.self_ty != trait_m.self_ty {
-        if impl_m.self_ty == ast::sty_static {
-            // Needs to be a fatal error because otherwise,
-            // method::transform_self_type_for_method ICEs
-            tcx.sess.span_fatal(cm.span,
-                 fmt!("method `%s` is declared as \
-                       static in its impl, but not in \
-                       its trait", *tcx.sess.str_of(impl_m.ident)));
-        }
-        else if trait_m.self_ty == ast::sty_static {
-            tcx.sess.span_fatal(cm.span,
-                 fmt!("method `%s` is declared as \
-                       static in its trait, but not in \
-                       its impl", *tcx.sess.str_of(impl_m.ident)));
+    // Try to give more informative error messages about self typing
+    // mismatches.  Note that any mismatch will also be detected
+    // below, where we construct a canonical function type that
+    // includes the self parameter as a normal parameter.  It's just
+    // that the error messages you get out of this code are a bit more
+    // inscrutable, particularly for cases where one method has no
+    // self.
+    match (&trait_m.self_ty, &impl_m.self_ty) {
+        (&ast::sty_static, &ast::sty_static) => {}
+        (&ast::sty_static, _) => {
+            tcx.sess.span_err(
+                cm.span,
+                fmt!("method `%s` has a `%s` declaration in the impl, \
+                      but not in the trait",
+                     *tcx.sess.str_of(trait_m.ident),
+                     self_ty_to_str(impl_m.self_ty, tcx.sess.intr())));
+            return;
         }
-        else {
+        (_, &ast::sty_static) => {
             tcx.sess.span_err(
                 cm.span,
-                fmt!("method `%s`'s self type does \
-                      not match the trait method's \
-                      self type", *tcx.sess.str_of(impl_m.ident)));
+                fmt!("method `%s` has a `%s` declaration in the trait, \
+                      but not in the impl",
+                     *tcx.sess.str_of(trait_m.ident),
+                     self_ty_to_str(trait_m.self_ty, tcx.sess.intr())));
+            return;
+        }
+        _ => {
+            // Let the type checker catch other errors below
         }
     }
 
@@ -539,6 +543,51 @@ pub fn compare_impl_method(tcx: ty::ctxt,
                                                    bound_region: ty::br_self});
     let self_ty = replace_bound_self(tcx, self_ty, dummy_self_r);
 
+    // We are going to create a synthetic fn type that includes
+    // both the method's self argument and its normal arguments.
+    // So a method like `fn(&self, a: uint)` would be converted
+    // into a function `fn(self: &T, a: uint)`.
+    let mut trait_fn_args = ~[];
+    let mut impl_fn_args = ~[];
+
+    // For both the trait and the impl, create an argument to
+    // represent the self argument (unless this is a static method).
+    // This argument will have the *transformed* self type.
+    for trait_m.transformed_self_ty.each |&t| {
+        trait_fn_args.push(ty::arg {mode: ast::expl(ast::by_copy), ty: t});
+    }
+    for impl_m.transformed_self_ty.each |&t| {
+        impl_fn_args.push(ty::arg {mode: ast::expl(ast::by_copy), ty: t});
+    }
+
+    // Add in the normal arguments.
+    trait_fn_args.push_all(trait_m.fty.sig.inputs);
+    impl_fn_args.push_all(impl_m.fty.sig.inputs);
+
+    // Create a bare fn type for trait/impl that includes self argument
+    let trait_fty =
+        ty::mk_bare_fn(
+            tcx,
+            ty::BareFnTy {purity: trait_m.fty.purity,
+                          abis: trait_m.fty.abis,
+                          sig: ty::FnSig {
+                              bound_lifetime_names:
+                                  copy trait_m.fty.sig.bound_lifetime_names,
+                              inputs: trait_fn_args,
+                              output: trait_m.fty.sig.output
+                          }});
+    let impl_fty =
+        ty::mk_bare_fn(
+            tcx,
+            ty::BareFnTy {purity: impl_m.fty.purity,
+                          abis: impl_m.fty.abis,
+                          sig: ty::FnSig {
+                              bound_lifetime_names:
+                                  copy impl_m.fty.sig.bound_lifetime_names,
+                              inputs: impl_fn_args,
+                              output: impl_m.fty.sig.output
+                          }});
+
     // Perform substitutions so that the trait/impl methods are expressed
     // in terms of the same set of type/region parameters:
     // - replace trait type parameters with those from `trait_substs`,
@@ -547,7 +596,6 @@ pub fn compare_impl_method(tcx: ty::ctxt,
     //   that correspond to the parameters we will find on the impl
     // - replace self region with a fresh, dummy region
     let impl_fty = {
-        let impl_fty = ty::mk_bare_fn(tcx, copy impl_m.fty);
         debug!("impl_fty (pre-subst): %s", ppaux::ty_to_str(tcx, impl_fty));
         replace_bound_self(tcx, impl_fty, dummy_self_r)
     };
@@ -565,7 +613,6 @@ pub fn compare_impl_method(tcx: ty::ctxt,
             self_ty: Some(self_ty),
             tps: vec::append(trait_tps, dummy_tps)
         };
-        let trait_fty = ty::mk_bare_fn(tcx, copy trait_m.fty);
         debug!("trait_fty (pre-subst): %s substs=%s",
                trait_fty.repr(tcx), substs.repr(tcx));
         ty::subst(tcx, &substs, trait_fty)
diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs
index eeca90dbecd..3ff36a409a7 100644
--- a/src/librustc/middle/typeck/rscope.rs
+++ b/src/librustc/middle/typeck/rscope.rs
@@ -180,12 +180,11 @@ impl region_scope for MethodRscope {
         })
     }
     fn self_region(&self, _span: span) -> Result<ty::Region, RegionError> {
-        assert!(self.variance.is_some() || self.self_ty.is_borrowed());
+        assert!(self.variance.is_some());
         match self.variance {
             None => {}  // must be borrowed self, so this is OK
             Some(_) => {
-                if !self.self_ty.is_borrowed() &&
-                        !self.region_param_names.has_self() {
+                if !self.region_param_names.has_self() {
                     return Err(RegionError {
                         msg: ~"the `self` lifetime must be declared",
                         replacement: ty::re_bound(ty::br_self)
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index f8877f76881..9b9e0e81b43 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -750,6 +750,12 @@ impl Repr for ty::TraitStore {
     }
 }
 
+impl Repr for ty::vstore {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        vstore_to_str(tcx, *self)
+    }
+}
+
 // Local Variables:
 // mode: rust
 // fill-column: 78;
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index ec77b54a853..3b2df24e7d9 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1002,15 +1002,6 @@ pub enum self_ty_ {
     sty_uniq(mutability)                       // `~self`
 }
 
-impl self_ty_ {
-    fn is_borrowed(&self) -> bool {
-        match *self {
-            sty_region(*) => true,
-            _ => false
-        }
-    }
-}
-
 pub type self_ty = spanned<self_ty_>;
 
 #[auto_encode]
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 92f0c7c7679..886af694920 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -454,6 +454,7 @@ impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{
 
     // ugh: can't get this to compile with mut because of the
     // lack of flow sensitivity.
+    #[cfg(stage0)]
     fn get_map(&self) -> &'self HashMap<K,@V> {
         match *self {
             BaseMapChain (~ref map) => map,
@@ -461,6 +462,18 @@ impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{
         }
     }
 
+    // ugh: can't get this to compile with mut because of the
+    // lack of flow sensitivity.
+    #[cfg(stage1)]
+    #[cfg(stage2)]
+    #[cfg(stage3)]
+    fn get_map<'a>(&'a self) -> &'a HashMap<K,@V> {
+        match *self {
+            BaseMapChain (~ref map) => map,
+            ConsMapChain (~ref map,_) => map
+        }
+    }
+
 // traits just don't work anywhere...?
 //pub impl Map<Name,SyntaxExtension> for MapChain {
 
diff --git a/src/libsyntax/opt_vec.rs b/src/libsyntax/opt_vec.rs
index fd54746f3dc..1604c40f917 100644
--- a/src/libsyntax/opt_vec.rs
+++ b/src/libsyntax/opt_vec.rs
@@ -61,6 +61,7 @@ impl<T> OptVec<T> {
         }
     }
 
+    #[cfg(stage0)]
     fn get(&self, i: uint) -> &'self T {
         match *self {
             Empty => fail!(fmt!("Invalid index %u", i)),
@@ -68,6 +69,16 @@ impl<T> OptVec<T> {
         }
     }
 
+    #[cfg(stage1)]
+    #[cfg(stage2)]
+    #[cfg(stage3)]
+    fn get<'a>(&'a self, i: uint) -> &'a T {
+        match *self {
+            Empty => fail!(fmt!("Invalid index %u", i)),
+            Vec(ref v) => &v[i]
+        }
+    }
+
     fn is_empty(&self) -> bool {
         self.len() == 0
     }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 20fc99baf21..36cd7c06842 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1633,6 +1633,10 @@ pub fn print_pat(s: @ps, &&pat: @ast::pat, refutable: bool) {
     (s.ann.post)(ann_node);
 }
 
+pub fn self_ty_to_str(self_ty: ast::self_ty_, intr: @ident_interner) -> ~str {
+    to_str(self_ty, |a, b| { print_self_ty(a, b); () }, intr)
+}
+
 // Returns whether it printed anything
 pub fn print_self_ty(s: @ps, self_ty: ast::self_ty_) -> bool {
     match self_ty {