about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/metadata/common.rs2
-rw-r--r--src/librustc/metadata/csearch.rs2
-rw-r--r--src/librustc/metadata/decoder.rs40
-rw-r--r--src/librustc/metadata/encoder.rs28
-rw-r--r--src/librustc/metadata/tydecode.rs14
-rw-r--r--src/librustc/metadata/tyencode.rs8
-rw-r--r--src/librustc/middle/astencode.rs42
-rw-r--r--src/librustc/middle/borrowck/gather_loans.rs4
-rw-r--r--src/librustc/middle/kind.rs33
-rw-r--r--src/librustc/middle/subst.rs177
-rw-r--r--src/librustc/middle/trans/base.rs13
-rw-r--r--src/librustc/middle/trans/callee.rs46
-rw-r--r--src/librustc/middle/trans/common.rs52
-rw-r--r--src/librustc/middle/trans/consts.rs4
-rw-r--r--src/librustc/middle/trans/inline.rs12
-rw-r--r--src/librustc/middle/trans/meth.rs33
-rw-r--r--src/librustc/middle/trans/monomorphize.rs47
-rw-r--r--src/librustc/middle/ty.rs225
-rw-r--r--src/librustc/middle/typeck/astconv.rs19
-rw-r--r--src/librustc/middle/typeck/check/_match.rs4
-rw-r--r--src/librustc/middle/typeck/check/method.rs160
-rw-r--r--src/librustc/middle/typeck/check/mod.rs20
-rw-r--r--src/librustc/middle/typeck/check/vtable.rs113
-rw-r--r--src/librustc/middle/typeck/check/writeback.rs2
-rw-r--r--src/librustc/middle/typeck/coherence.rs153
-rw-r--r--src/librustc/middle/typeck/collect.rs247
-rw-r--r--src/librustc/middle/typeck/infer/combine.rs7
-rw-r--r--src/librustc/middle/typeck/mod.rs16
-rw-r--r--src/librustc/rustc.rc1
-rw-r--r--src/librustc/util/ppaux.rs278
-rw-r--r--src/libsyntax/opt_vec.rs10
-rw-r--r--src/test/run-pass/trait-inheritance-self-in-supertype.rs61
32 files changed, 1253 insertions, 620 deletions
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index 71a1f1b3f9b..e2672338a8a 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -127,7 +127,7 @@ pub enum astencode_tag { // Reserves 0x50 -- 0x6f
     tag_table_node_type_subst = 0x58,
     tag_table_freevars = 0x59,
     tag_table_tcache = 0x5a,
-    tag_table_param_bounds = 0x5b,
+    tag_table_param_defs = 0x5b,
     tag_table_inferred_modes = 0x5c,
     tag_table_mutbl = 0x5d,
     tag_table_last_use = 0x5e,
diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs
index 74e891a0194..f7de281194f 100644
--- a/src/librustc/metadata/csearch.rs
+++ b/src/librustc/metadata/csearch.rs
@@ -210,7 +210,7 @@ pub fn get_field_type(tcx: ty::ctxt, class_id: ast::def_id,
     debug!("got field data %?", the_field);
     let ty = decoder::item_type(def, the_field, tcx, cdata);
     ty::ty_param_bounds_and_ty {
-        generics: ty::Generics {bounds: @~[],
+        generics: ty::Generics {type_param_defs: @~[],
                                 region_param: None},
         ty: ty
     }
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index d9ef30ff297..472b455b735 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -19,7 +19,8 @@ use metadata::csearch::{ProvidedTraitMethodInfo, StaticMethodInfo};
 use metadata::csearch;
 use metadata::cstore;
 use metadata::decoder;
-use metadata::tydecode::{parse_ty_data, parse_def_id, parse_bounds_data,
+use metadata::tydecode::{parse_ty_data, parse_def_id,
+                         parse_type_param_def_data,
                          parse_bare_fn_ty_data, parse_trait_ref_data};
 use middle::{ty, resolve};
 
@@ -266,13 +267,14 @@ fn item_trait_ref(doc: ebml::Doc, tcx: ty::ctxt, cdata: cmd) -> ty::TraitRef {
     doc_trait_ref(tp, tcx, cdata)
 }
 
-fn item_ty_param_bounds(item: ebml::Doc, tcx: ty::ctxt, cdata: cmd,
-                        tag: uint)
-    -> @~[ty::param_bounds] {
+fn item_ty_param_defs(item: ebml::Doc, tcx: ty::ctxt, cdata: cmd,
+                      tag: uint)
+    -> @~[ty::TypeParameterDef] {
     let mut bounds = ~[];
     for reader::tagged_docs(item, tag) |p| {
-        let bd = parse_bounds_data(p.data, p.start, cdata.cnum, tcx,
-                                   |_, did| translate_def_id(cdata, did));
+        let bd = parse_type_param_def_data(
+            p.data, p.start, cdata.cnum, tcx,
+            |_, did| translate_def_id(cdata, did));
         bounds.push(bd);
     }
     @bounds
@@ -378,11 +380,11 @@ pub fn get_trait_def(cdata: cmd,
                      tcx: ty::ctxt) -> ty::TraitDef
 {
     let item_doc = lookup_item(item_id, cdata.data);
-    let tp_bounds = item_ty_param_bounds(item_doc, tcx, cdata,
-                                         tag_items_data_item_ty_param_bounds);
+    let tp_defs = item_ty_param_defs(item_doc, tcx, cdata,
+                                     tag_items_data_item_ty_param_bounds);
     let rp = item_ty_region_param(item_doc);
     ty::TraitDef {
-        generics: ty::Generics {bounds: tp_bounds,
+        generics: ty::Generics {type_param_defs: tp_defs,
                                 region_param: rp},
         trait_ref: @item_trait_ref(item_doc, tcx, cdata)
     }
@@ -394,12 +396,12 @@ pub fn get_type(cdata: cmd, id: ast::node_id, tcx: ty::ctxt)
     let item = lookup_item(id, cdata.data);
     let t = item_type(ast::def_id { crate: cdata.cnum, node: id }, item, tcx,
                       cdata);
-    let tp_bounds = if family_has_type_params(item_family(item)) {
-        item_ty_param_bounds(item, tcx, cdata, tag_items_data_item_ty_param_bounds)
+    let tp_defs = if family_has_type_params(item_family(item)) {
+        item_ty_param_defs(item, tcx, cdata, tag_items_data_item_ty_param_bounds)
     } else { @~[] };
     let rp = item_ty_region_param(item);
     ty::ty_param_bounds_and_ty {
-        generics: ty::Generics {bounds: tp_bounds,
+        generics: ty::Generics {type_param_defs: tp_defs,
                                 region_param: rp},
         ty: t
     }
@@ -753,9 +755,8 @@ pub fn get_method(intr: @ident_interner, cdata: cmd, id: ast::node_id,
     let method_doc = lookup_item(id, cdata.data);
     let def_id = item_def_id(method_doc, cdata);
     let name = item_name(intr, method_doc);
-    let bounds =
-        item_ty_param_bounds(method_doc, tcx, cdata,
-                             tag_item_method_tps);
+    let type_param_defs = item_ty_param_defs(method_doc, tcx, cdata,
+                                             tag_item_method_tps);
     let transformed_self_ty = doc_transformed_self_ty(method_doc, tcx, cdata);
     let fty = doc_method_fty(method_doc, tcx, cdata);
     let vis = item_visibility(method_doc);
@@ -763,7 +764,7 @@ pub fn get_method(intr: @ident_interner, cdata: cmd, id: ast::node_id,
     ty::method {
         ident: name,
         generics: ty::Generics {
-            bounds: bounds,
+            type_param_defs: type_param_defs,
             region_param: None
         },
         transformed_self_ty: transformed_self_ty,
@@ -797,8 +798,9 @@ pub fn get_provided_trait_methods(intr: @ident_interner, cdata: cmd,
 
         let did = item_def_id(mth, cdata);
 
-        let bounds = item_ty_param_bounds(mth, tcx, cdata,
-                                          tag_items_data_item_ty_param_bounds);
+        let type_param_defs =
+            item_ty_param_defs(mth, tcx, cdata,
+                               tag_items_data_item_ty_param_bounds);
         let name = item_name(intr, mth);
         let ty = doc_type(mth, tcx, cdata);
 
@@ -815,7 +817,7 @@ pub fn get_provided_trait_methods(intr: @ident_interner, cdata: cmd,
         let ty_method = ty::method {
             ident: name,
             generics: ty::Generics {
-                bounds: bounds,
+                type_param_defs: type_param_defs,
                 region_param: None
             },
             transformed_self_ty: transformed_self_ty,
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index e251af7c8a8..b9f0f63040a 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -179,10 +179,10 @@ fn encode_family(ebml_w: writer::Encoder, c: char) {
 
 pub fn def_to_str(did: def_id) -> ~str { fmt!("%d:%d", did.crate, did.node) }
 
-fn encode_ty_type_param_bounds(ebml_w: writer::Encoder,
-                               ecx: @EncodeContext,
-                               params: @~[ty::param_bounds],
-                               tag: uint) {
+fn encode_ty_type_param_defs(ebml_w: writer::Encoder,
+                             ecx: @EncodeContext,
+                             params: @~[ty::TypeParameterDef],
+                             tag: uint) {
     let ty_str_ctxt = @tyencode::ctxt {
         diag: ecx.diag,
         ds: def_to_str,
@@ -191,7 +191,7 @@ fn encode_ty_type_param_bounds(ebml_w: writer::Encoder,
         abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)};
     for params.each |param| {
         ebml_w.start_tag(tag);
-        tyencode::enc_bounds(ebml_w.writer, ty_str_ctxt, *param);
+        tyencode::enc_type_param_def(ebml_w.writer, ty_str_ctxt, param);
         ebml_w.end_tag();
     }
 }
@@ -199,10 +199,10 @@ fn encode_ty_type_param_bounds(ebml_w: writer::Encoder,
 fn encode_type_param_bounds(ebml_w: writer::Encoder,
                             ecx: @EncodeContext,
                             params: &OptVec<TyParam>) {
-    let ty_param_bounds =
-        @params.map_to_vec(|param| *ecx.tcx.ty_param_bounds.get(&param.id));
-    encode_ty_type_param_bounds(ebml_w, ecx, ty_param_bounds,
-                                tag_items_data_item_ty_param_bounds);
+    let ty_param_defs =
+        @params.map_to_vec(|param| *ecx.tcx.ty_param_defs.get(&param.id));
+    encode_ty_type_param_defs(ebml_w, ecx, ty_param_defs,
+                              tag_items_data_item_ty_param_bounds);
 }
 
 
@@ -588,8 +588,9 @@ fn encode_method_ty_fields(ecx: @EncodeContext,
 {
     encode_def_id(ebml_w, method_ty.def_id);
     encode_name(ecx, ebml_w, method_ty.ident);
-    encode_ty_type_param_bounds(ebml_w, ecx, method_ty.generics.bounds,
-                                tag_item_method_tps);
+    encode_ty_type_param_defs(ebml_w, ecx,
+                              method_ty.generics.type_param_defs,
+                              tag_item_method_tps);
     encode_transformed_self_ty(ecx, ebml_w, method_ty.transformed_self_ty);
     encode_method_fty(ecx, ebml_w, &method_ty.fty);
     encode_visibility(ebml_w, method_ty.vis);
@@ -952,8 +953,9 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
                                       method_ty.fty.purity));
 
                     let tpt = ty::lookup_item_type(tcx, method_def_id);
-                    encode_ty_type_param_bounds(ebml_w, ecx, tpt.generics.bounds,
-                                                tag_items_data_item_ty_param_bounds);
+                    encode_ty_type_param_defs(ebml_w, ecx,
+                                              tpt.generics.type_param_defs,
+                                              tag_items_data_item_ty_param_bounds);
                     encode_type(ecx, ebml_w, tpt.ty);
                 }
 
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index 2ec13abb483..867fe0cd816 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -547,11 +547,17 @@ pub fn parse_def_id(buf: &[u8]) -> ast::def_id {
     ast::def_id { crate: crate_num, node: def_num }
 }
 
-pub fn parse_bounds_data(data: @~[u8], start: uint,
-                         crate_num: int, tcx: ty::ctxt, conv: conv_did)
-                      -> @~[ty::param_bound] {
+pub fn parse_type_param_def_data(data: @~[u8], start: uint,
+                                 crate_num: int, tcx: ty::ctxt,
+                                 conv: conv_did) -> ty::TypeParameterDef
+{
     let st = parse_state_from_data(data, crate_num, start, tcx);
-    parse_bounds(st, conv)
+    parse_type_param_def(st, conv)
+}
+
+fn parse_type_param_def(st: @mut PState, conv: conv_did) -> ty::TypeParameterDef {
+    ty::TypeParameterDef {def_id: parse_def(st, NominalType, conv),
+                          bounds: parse_bounds(st, conv)}
 }
 
 fn parse_bounds(st: @mut PState, conv: conv_did) -> @~[ty::param_bound] {
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index ca2d66de9c4..238f34b8c01 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -412,7 +412,7 @@ fn enc_fn_sig(w: @io::Writer, cx: @ctxt, fsig: &ty::FnSig) {
     enc_ty(w, cx, fsig.output);
 }
 
-pub fn enc_bounds(w: @io::Writer, cx: @ctxt, bs: @~[ty::param_bound]) {
+fn enc_bounds(w: @io::Writer, cx: @ctxt, bs: @~[ty::param_bound]) {
     for vec::each(*bs) |bound| {
         match *bound {
           ty::bound_owned => w.write_char('S'),
@@ -428,6 +428,12 @@ pub fn enc_bounds(w: @io::Writer, cx: @ctxt, bs: @~[ty::param_bound]) {
     w.write_char('.');
 }
 
+pub fn enc_type_param_def(w: @io::Writer, cx: @ctxt, v: &ty::TypeParameterDef) {
+    w.write_str((cx.ds)(v.def_id));
+    w.write_char('|');
+    enc_bounds(w, cx, v.bounds);
+}
+
 //
 // Local Variables:
 // mode: rust
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 20b72a19df7..68989b31425 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -741,7 +741,9 @@ trait ebml_writer_helpers {
     fn emit_ty(&self, ecx: @e::EncodeContext, ty: ty::t);
     fn emit_vstore(&self, ecx: @e::EncodeContext, vstore: ty::vstore);
     fn emit_tys(&self, ecx: @e::EncodeContext, tys: ~[ty::t]);
-    fn emit_bounds(&self, ecx: @e::EncodeContext, bs: ty::param_bounds);
+    fn emit_type_param_def(&self,
+                           ecx: @e::EncodeContext,
+                           type_param_def: &ty::TypeParameterDef);
     fn emit_tpbt(&self, ecx: @e::EncodeContext,
                  tpbt: ty::ty_param_bounds_and_ty);
 }
@@ -771,9 +773,12 @@ impl ebml_writer_helpers for writer::Encoder {
         }
     }
 
-    fn emit_bounds(&self, ecx: @e::EncodeContext, bs: ty::param_bounds) {
+    fn emit_type_param_def(&self,
+                           ecx: @e::EncodeContext,
+                           type_param_def: &ty::TypeParameterDef) {
         do self.emit_opaque {
-            tyencode::enc_bounds(self.writer, ecx.ty_str_ctxt(), bs)
+            tyencode::enc_type_param_def(self.writer, ecx.ty_str_ctxt(),
+                                         type_param_def)
         }
     }
 
@@ -782,9 +787,11 @@ impl ebml_writer_helpers for writer::Encoder {
         do self.emit_struct("ty_param_bounds_and_ty", 2) {
             do self.emit_field(~"generics", 0) {
                 do self.emit_struct("Generics", 2) {
-                    do self.emit_field(~"bounds", 0) {
-                        do self.emit_from_vec(*tpbt.generics.bounds) |bs| {
-                            self.emit_bounds(ecx, *bs);
+                    do self.emit_field(~"type_param_defs", 0) {
+                        do self.emit_from_vec(*tpbt.generics.type_param_defs)
+                            |type_param_def|
+                        {
+                            self.emit_type_param_def(ecx, type_param_def);
                         }
                     }
                     do self.emit_field(~"region_param", 1) {
@@ -889,11 +896,11 @@ fn encode_side_tables_for_id(ecx: @e::EncodeContext,
         }
     }
 
-    for tcx.ty_param_bounds.find(&id).each |&pbs| {
-        do ebml_w.tag(c::tag_table_param_bounds) {
+    for tcx.ty_param_defs.find(&id).each |&type_param_def| {
+        do ebml_w.tag(c::tag_table_param_defs) {
             ebml_w.id(id);
             do ebml_w.tag(c::tag_table_val) {
-                ebml_w.emit_bounds(ecx, *pbs)
+                ebml_w.emit_type_param_def(ecx, type_param_def)
             }
         }
     }
@@ -990,7 +997,7 @@ trait ebml_decoder_decoder_helpers {
     fn read_arg(&self, xcx: @ExtendedDecodeContext) -> ty::arg;
     fn read_ty(&self, xcx: @ExtendedDecodeContext) -> ty::t;
     fn read_tys(&self, xcx: @ExtendedDecodeContext) -> ~[ty::t];
-    fn read_bounds(&self, xcx: @ExtendedDecodeContext) -> @~[ty::param_bound];
+    fn read_type_param_def(&self, xcx: @ExtendedDecodeContext) -> ty::TypeParameterDef;
     fn read_ty_param_bounds_and_ty(&self, xcx: @ExtendedDecodeContext)
                                 -> ty::ty_param_bounds_and_ty;
     fn convert_def_id(&self, xcx: @ExtendedDecodeContext,
@@ -1038,10 +1045,9 @@ impl ebml_decoder_decoder_helpers for reader::Decoder {
         self.read_to_vec(|| self.read_ty(xcx) )
     }
 
-    fn read_bounds(&self, xcx: @ExtendedDecodeContext)
-                  -> @~[ty::param_bound] {
+    fn read_type_param_def(&self, xcx: @ExtendedDecodeContext) -> ty::TypeParameterDef {
         do self.read_opaque |doc| {
-            tydecode::parse_bounds_data(
+            tydecode::parse_type_param_def_data(
                 doc.data, doc.start, xcx.dcx.cdata.cnum, xcx.dcx.tcx,
                 |s, a| self.convert_def_id(xcx, s, a))
         }
@@ -1054,8 +1060,8 @@ impl ebml_decoder_decoder_helpers for reader::Decoder {
             ty::ty_param_bounds_and_ty {
                 generics: do self.read_struct("Generics", 2) {
                     ty::Generics {
-                        bounds: self.read_field(~"bounds", 0, || {
-                            @self.read_to_vec(|| self.read_bounds(xcx) )
+                        type_param_defs: self.read_field("type_param_defs", 0, || {
+                            @self.read_to_vec(|| self.read_type_param_def(xcx))
                         }),
                         region_param: self.read_field(~"region_param", 1, || {
                             Decodable::decode(self)
@@ -1134,9 +1140,9 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext,
                 let tpbt = val_dsr.read_ty_param_bounds_and_ty(xcx);
                 let lid = ast::def_id { crate: ast::local_crate, node: id };
                 dcx.tcx.tcache.insert(lid, tpbt);
-            } else if tag == (c::tag_table_param_bounds as uint) {
-                let bounds = val_dsr.read_bounds(xcx);
-                dcx.tcx.ty_param_bounds.insert(id, bounds);
+            } else if tag == (c::tag_table_param_defs as uint) {
+                let bounds = val_dsr.read_type_param_def(xcx);
+                dcx.tcx.ty_param_defs.insert(id, bounds);
             } else if tag == (c::tag_table_last_use as uint) {
                 let ids = val_dsr.read_to_vec(|| {
                     xcx.tr_id(val_dsr.read_int())
diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs
index 122e2c64b21..233b1e191dc 100644
--- a/src/librustc/middle/borrowck/gather_loans.rs
+++ b/src/librustc/middle/borrowck/gather_loans.rs
@@ -29,7 +29,7 @@ use middle::pat_util;
 use middle::ty::{ty_region};
 use middle::ty;
 use util::common::indenter;
-use util::ppaux::{expr_repr, region_to_str};
+use util::ppaux::{Repr, region_to_str};
 
 use core::hashmap::{HashSet, HashMap};
 use core::vec;
@@ -282,7 +282,7 @@ pub impl GatherLoanCtxt {
                              expr: @ast::expr,
                              adjustment: &ty::AutoAdjustment) {
         debug!("guarantee_adjustments(expr=%s, adjustment=%?)",
-               expr_repr(self.tcx(), expr), adjustment);
+               expr.repr(self.tcx()), adjustment);
         let _i = indenter();
 
         match *adjustment {
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index 96e8ef40770..e82b6ba5e77 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::{ty_to_str, tys_to_str};
+use util::ppaux::{Repr, ty_to_str, tys_to_str};
 
 use syntax::ast::*;
 use syntax::attr::attrs_contains_name;
@@ -91,7 +91,7 @@ fn check_struct_safe_for_destructor(cx: Context,
                                     span: span,
                                     struct_did: def_id) {
     let struct_tpt = ty::lookup_item_type(cx.tcx, struct_did);
-    if struct_tpt.generics.bounds.len() == 0 {
+    if !struct_tpt.generics.has_type_params() {
         let struct_ty = ty::mk_struct(cx.tcx, struct_did, ty::substs {
             self_r: None,
             self_ty: None,
@@ -276,10 +276,10 @@ pub fn check_expr(e: @expr, cx: Context, v: visit::vt<Context>) {
     for cx.tcx.node_type_substs.find(&type_parameter_id).each |ts| {
         // FIXME(#5562): removing this copy causes a segfault before stage2
         let ts = /*bad*/ copy **ts;
-        let bounds = match e.node {
+        let type_param_defs = match e.node {
           expr_path(_) => {
             let did = ast_util::def_id_of_def(*cx.tcx.def_map.get(&e.id));
-            ty::lookup_item_type(cx.tcx, did).generics.bounds
+            ty::lookup_item_type(cx.tcx, did).generics.type_param_defs
           }
           _ => {
             // Type substitutions should only occur on paths and
@@ -287,20 +287,20 @@ pub fn check_expr(e: @expr, cx: Context, v: visit::vt<Context>) {
 
             // Even though the callee_id may have been the id with
             // node_type_substs, e.id is correct here.
-            ty::method_call_bounds(cx.tcx, cx.method_map, e.id).expect(
+            ty::method_call_type_param_defs(cx.tcx, cx.method_map, e.id).expect(
                 ~"non path/method call expr has type substs??")
           }
         };
-        if ts.len() != bounds.len() {
+        if ts.len() != type_param_defs.len() {
             // Fail earlier to make debugging easier
             fail!(fmt!("internal error: in kind::check_expr, length \
                        mismatch between actual and declared bounds: actual = \
-                        %s (%u tys), declared = %? (%u tys)",
-                      tys_to_str(cx.tcx, ts), ts.len(),
-                      *bounds, bounds.len()));
+                        %s, declared = %s",
+                       ts.repr(cx.tcx),
+                       type_param_defs.repr(cx.tcx)));
         }
-        for vec::each2(ts, *bounds) |ty, bound| {
-            check_bounds(cx, type_parameter_id, e.span, *ty, *bound)
+        for vec::each2(ts, *type_param_defs) |&ty, type_param_def| {
+            check_bounds(cx, type_parameter_id, e.span, ty, type_param_def)
         }
     }
 
@@ -340,9 +340,10 @@ fn check_ty(aty: @Ty, cx: Context, v: visit::vt<Context>) {
             // FIXME(#5562): removing this copy causes a segfault before stage2
             let ts = /*bad*/ copy **ts;
             let did = ast_util::def_id_of_def(*cx.tcx.def_map.get(&id));
-            let bounds = ty::lookup_item_type(cx.tcx, did).generics.bounds;
-            for vec::each2(ts, *bounds) |ty, bound| {
-                check_bounds(cx, aty.id, aty.span, *ty, *bound)
+            let type_param_defs =
+                ty::lookup_item_type(cx.tcx, did).generics.type_param_defs;
+            for vec::each2(ts, *type_param_defs) |&ty, type_param_def| {
+                check_bounds(cx, aty.id, aty.span, ty, type_param_def)
             }
         }
       }
@@ -355,11 +356,11 @@ pub fn check_bounds(cx: Context,
                     _type_parameter_id: node_id,
                     sp: span,
                     ty: ty::t,
-                    bounds: ty::param_bounds)
+                    type_param_def: &ty::TypeParameterDef)
 {
     let kind = ty::type_contents(cx.tcx, ty);
     let mut missing = ~[];
-    for bounds.each |bound| {
+    for type_param_def.bounds.each |bound| {
         match *bound {
             ty::bound_trait(_) => {
                 /* Not our job, checking in typeck */
diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs
new file mode 100644
index 00000000000..a754f93f010
--- /dev/null
+++ b/src/librustc/middle/subst.rs
@@ -0,0 +1,177 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Type substitutions.
+
+use core::prelude::*;
+use middle::ty;
+use util::ppaux::Repr;
+
+///////////////////////////////////////////////////////////////////////////
+// Public trait `Subst`
+//
+// Just call `foo.subst(tcx, substs)` to perform a substitution across
+// `foo`.
+
+pub trait Subst {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> Self;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Substitution over types
+//
+// Because this is so common, we make a special optimization to avoid
+// doing anything if `substs` is a no-op.  I tried to generalize these
+// to all subst methods but ran into trouble due to the limitations of
+// our current method/trait matching algorithm. - Niko
+
+trait EffectfulSubst {
+    fn effectfulSubst(&self, tcx: ty::ctxt, substs: &ty::substs) -> Self;
+}
+
+impl Subst for ty::t {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::t {
+        if ty::substs_is_noop(substs) {
+            return *self;
+        } else {
+            return self.effectfulSubst(tcx, substs);
+        }
+    }
+}
+
+impl EffectfulSubst for ty::t {
+    fn effectfulSubst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::t {
+        if !ty::type_needs_subst(*self) {
+            return *self;
+        }
+
+        match ty::get(*self).sty {
+            ty::ty_param(p) => {
+                substs.tps[p.idx]
+            }
+            ty::ty_self(_) => {
+                substs.self_ty.expect("ty_self not found in substs")
+            }
+            _ => {
+                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
+                    },
+                    |t| t.effectfulSubst(tcx, substs),
+                    |t| t.effectfulSubst(tcx, substs))
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Other types
+
+impl<T:Subst> Subst for ~[T] {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ~[T] {
+        self.map(|t| t.subst(tcx, substs))
+    }
+}
+
+impl<T:Subst> Subst for @~[T] {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> @~[T] {
+        @(**self).subst(tcx, substs)
+    }
+}
+
+impl<T:Subst> Subst for Option<T> {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> Option<T> {
+        self.map(|t| t.subst(tcx, substs))
+    }
+}
+
+impl Subst for ty::TraitRef {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::TraitRef {
+        ty::TraitRef {
+            def_id: self.def_id,
+            substs: self.substs.subst(tcx, substs)
+        }
+    }
+}
+
+impl Subst for ty::substs {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::substs {
+        ty::substs {
+            self_r: self.self_r,
+            self_ty: self.self_ty.map(|typ| typ.subst(tcx, substs)),
+            tps: self.tps.map(|typ| typ.subst(tcx, substs))
+        }
+    }
+}
+
+impl Subst for ty::BareFnTy {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::BareFnTy {
+        ty::fold_bare_fn_ty(self, |t| t.subst(tcx, substs))
+    }
+}
+
+impl Subst for ty::param_bound {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::param_bound {
+        match self {
+            &ty::bound_copy |
+            &ty::bound_durable |
+            &ty::bound_owned |
+            &ty::bound_const => {
+                *self
+            }
+
+            &ty::bound_trait(tref) => {
+                ty::bound_trait(@tref.subst(tcx, substs))
+            }
+        }
+    }
+}
+
+impl Subst for ty::TypeParameterDef {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::TypeParameterDef {
+        ty::TypeParameterDef {
+            def_id: self.def_id,
+            bounds: self.bounds.subst(tcx, substs)
+        }
+    }
+}
+
+impl Subst for ty::Generics {
+    fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::Generics {
+        ty::Generics {
+            type_param_defs: self.type_param_defs.subst(tcx, substs),
+            region_param: self.region_param
+        }
+    }
+}
+
+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 {
+            generics: self.generics.subst(tcx, substs),
+            ty: self.ty.subst(tcx, substs)
+        }
+    }
+}
+
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index a8fe60ba68e..2eb7f8332ca 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -63,7 +63,7 @@ use middle::trans::type_of;
 use middle::trans::type_of::*;
 use middle::ty;
 use util::common::indenter;
-use util::ppaux::ty_to_str;
+use util::ppaux::{Repr, ty_to_str};
 use util::ppaux;
 
 use core::hash;
@@ -1592,11 +1592,11 @@ pub fn new_fn_ctxt_w_id(ccx: @CrateContext,
     for param_substs.each |p| { p.validate(); }
 
     debug!("new_fn_ctxt_w_id(path=%s, id=%?, impl_id=%?, \
-            param_substs=%s",
+            param_substs=%s)",
            path_str(ccx.sess, path),
            id,
            impl_id,
-           opt_param_substs_to_str(ccx.tcx, &param_substs));
+           param_substs.repr(ccx.tcx));
 
     let llbbs = mk_standard_basic_blocks(llfndecl);
     return @mut fn_ctxt_ {
@@ -1788,6 +1788,9 @@ pub fn trans_closure(ccx: @CrateContext,
     let _icx = ccx.insn_ctxt("trans_closure");
     set_uwtable(llfndecl);
 
+    debug!("trans_closure(..., param_substs=%s)",
+           param_substs.repr(ccx.tcx));
+
     // Set up arguments to the function.
     let fcx = new_fn_ctxt_w_id(ccx, path, llfndecl, id, impl_id, param_substs,
                                   Some(body.span));
@@ -1849,7 +1852,9 @@ pub fn trans_fn(ccx: @CrateContext,
     let do_time = ccx.sess.trans_stats();
     let start = if do_time { time::get_time() }
                 else { time::Timespec::new(0, 0) };
-    debug!("trans_fn(ty_self=%?)", ty_self);
+    debug!("trans_fn(ty_self=%?, param_substs=%s)",
+           ty_self,
+           param_substs.repr(ccx.tcx));
     let _icx = ccx.insn_ctxt("trans_fn");
     ccx.stats.n_fns += 1;
     let the_path_str = path_str(ccx.sess, path);
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index de14266573a..20382676fed 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -42,6 +42,7 @@ use middle::trans::type_of;
 use middle::ty;
 use middle::typeck;
 use util::common::indenter;
+use util::ppaux::Repr;
 
 use syntax::ast;
 use syntax::ast_map;
@@ -74,24 +75,13 @@ pub struct Callee {
 
 pub fn trans(bcx: block, expr: @ast::expr) -> Callee {
     let _icx = bcx.insn_ctxt("trans_callee");
+    debug!("callee::trans(expr=%s)", expr.repr(bcx.tcx()));
 
     // pick out special kinds of expressions that can be called:
     match expr.node {
         ast::expr_path(_) => {
             return trans_def(bcx, bcx.def(expr.id), expr);
         }
-        ast::expr_field(base, _, _) => {
-            match bcx.ccx().maps.method_map.find(&expr.id) {
-                Some(origin) => { // An impl method
-                    // FIXME(#5562): removing this copy causes a segfault
-                    //               before stage2
-                    let origin = /*bad*/ copy *origin;
-                    return meth::trans_method_callee(bcx, expr.id,
-                                                     base, origin);
-                }
-                None => {} // not a method, just a field
-            }
-        }
         _ => {}
     }
 
@@ -178,11 +168,13 @@ pub fn trans_fn_ref(bcx: block,
      * with id `def_id` into a function pointer.  This may require
      * monomorphization or inlining. */
 
-    let _icx = bcx.insn_ctxt("trans_fn");
+    let _icx = bcx.insn_ctxt("trans_fn_ref");
 
     let type_params = node_id_type_params(bcx, ref_id);
-
     let vtables = node_vtables(bcx, ref_id);
+    debug!("trans_fn_ref(def_id=%s, ref_id=%?, type_params=%s, vtables=%s)",
+           def_id.repr(bcx.tcx()), ref_id, type_params.repr(bcx.tcx()),
+           vtables.repr(bcx.tcx()));
     trans_fn_ref_with_vtables(bcx, def_id, ref_id, type_params, vtables)
 }
 
@@ -224,12 +216,13 @@ pub fn trans_fn_ref_with_vtables(
     let ccx = bcx.ccx();
     let tcx = ccx.tcx;
 
-    debug!("trans_fn_ref_with_vtables(bcx=%s, def_id=%?, ref_id=%?, \
-            type_params=%?, vtables=%?)",
-           bcx.to_str(), def_id, ref_id,
-           type_params.map(|t| bcx.ty_to_str(*t)),
-           vtables);
-    let _indenter = indenter();
+    debug!("trans_fn_ref_with_vtables(bcx=%s, def_id=%s, ref_id=%?, \
+            type_params=%s, vtables=%s)",
+           bcx.to_str(),
+           def_id.repr(bcx.tcx()),
+           ref_id,
+           type_params.repr(bcx.tcx()),
+           vtables.repr(bcx.tcx()));
 
     assert!(type_params.all(|t| !ty::type_needs_infer(*t)));
 
@@ -335,6 +328,9 @@ pub fn trans_method_call(in_cx: block,
                          dest: expr::Dest)
                       -> block {
     let _icx = in_cx.insn_ctxt("trans_method_call");
+    debug!("trans_method_call(call_ex=%s, rcvr=%s)",
+           call_ex.repr(in_cx.tcx()),
+           rcvr.repr(in_cx.tcx()));
     trans_call_inner(
         in_cx,
         call_ex.info(),
@@ -343,9 +339,14 @@ pub fn trans_method_call(in_cx: block,
         |cx| {
             match cx.ccx().maps.method_map.find(&call_ex.id) {
                 Some(origin) => {
+                    debug!("origin for %s: %s",
+                           call_ex.repr(in_cx.tcx()),
+                           origin.repr(in_cx.tcx()));
+
                     // FIXME(#5562): removing this copy causes a segfault
                     //               before stage2
                     let origin = /*bad*/ copy *origin;
+
                     meth::trans_method_callee(cx,
                                               call_ex.callee_id,
                                               rcvr,
@@ -641,8 +642,9 @@ pub fn trans_arg_expr(bcx: block,
 
     debug!("trans_arg_expr(formal_ty=(%?,%s), arg_expr=%s, \
             ret_flag=%?)",
-           formal_ty.mode, bcx.ty_to_str(formal_ty.ty),
-           bcx.expr_to_str(arg_expr),
+           formal_ty.mode,
+           formal_ty.ty.repr(bcx.tcx()),
+           arg_expr.repr(bcx.tcx()),
            ret_flag.map(|v| bcx.val_str(*v)));
     let _indenter = indenter();
 
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index 593d37c2ff8..2cfd7a25d68 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -41,7 +41,7 @@ use middle::trans::type_use;
 use middle::ty::substs;
 use middle::ty;
 use middle::typeck;
-use util::ppaux::{expr_repr, ty_to_str};
+use util::ppaux::{Repr};
 
 use core::cast;
 use core::hash;
@@ -250,7 +250,7 @@ pub enum local_val { local_mem(ValueRef), local_imm(ValueRef), }
 pub struct param_substs {
     tys: ~[ty::t],
     vtables: Option<typeck::vtable_res>,
-    bounds: @~[ty::param_bounds],
+    type_param_defs: @~[ty::TypeParameterDef],
     self_ty: Option<ty::t>
 }
 
@@ -261,16 +261,25 @@ pub impl param_substs {
     }
 }
 
-pub fn param_substs_to_str(tcx: ty::ctxt, substs: &param_substs) -> ~str {
-    fmt!("param_substs {tys:%?, vtables:%?, bounds:%?}",
-         substs.tys.map(|t| ty_to_str(tcx, *t)),
-         substs.vtables.map(|vs| vs.map(|v| v.to_str(tcx))),
-         substs.bounds.map(|b| ty::param_bounds_to_str(tcx, *b)))
+fn param_substs_to_str(self: &param_substs,
+                       tcx: ty::ctxt) -> ~str
+{
+    fmt!("param_substs {tys:%s, vtables:%s, type_param_defs:%s}",
+         self.tys.repr(tcx),
+         self.vtables.repr(tcx),
+         self.type_param_defs.repr(tcx))
 }
 
-pub fn opt_param_substs_to_str(tcx: ty::ctxt,
-                               substs: &Option<@param_substs>) -> ~str {
-    substs.map_default(~"None", |&ps| param_substs_to_str(tcx, ps))
+impl Repr for param_substs {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        param_substs_to_str(self, tcx)
+    }
+}
+
+impl Repr for @param_substs {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        param_substs_to_str(*self, tcx)
+    }
 }
 
 // Function context.  Every LLVM function we create will have one of
@@ -413,8 +422,9 @@ pub fn root_for_cleanup(bcx: block, v: ValueRef, t: ty::t)
 pub fn add_clean(bcx: block, val: ValueRef, t: ty::t) {
     if !ty::type_needs_drop(bcx.tcx(), t) { return; }
     debug!("add_clean(%s, %s, %s)",
-           bcx.to_str(), val_str(bcx.ccx().tn, val),
-           ty_to_str(bcx.ccx().tcx, t));
+           bcx.to_str(),
+           val_str(bcx.ccx().tn, val),
+           t.repr(bcx.tcx()));
     let (root, rooted) = root_for_cleanup(bcx, val, t);
     let cleanup_type = cleanup_type(bcx.tcx(), t);
     do in_scope_cx(bcx) |scope_info| {
@@ -429,7 +439,7 @@ pub fn add_clean_temp_immediate(cx: block, val: ValueRef, ty: ty::t) {
     if !ty::type_needs_drop(cx.tcx(), ty) { return; }
     debug!("add_clean_temp_immediate(%s, %s, %s)",
            cx.to_str(), val_str(cx.ccx().tn, val),
-           ty_to_str(cx.ccx().tcx, ty));
+           ty.repr(cx.tcx()));
     let cleanup_type = cleanup_type(cx.tcx(), ty);
     do in_scope_cx(cx) |scope_info| {
         scope_info.cleanups.push(
@@ -442,7 +452,7 @@ pub fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) {
     if !ty::type_needs_drop(bcx.tcx(), t) { return; }
     debug!("add_clean_temp_mem(%s, %s, %s)",
            bcx.to_str(), val_str(bcx.ccx().tn, val),
-           ty_to_str(bcx.ccx().tcx, t));
+           t.repr(bcx.tcx()));
     let (root, rooted) = root_for_cleanup(bcx, val, t);
     let cleanup_type = cleanup_type(bcx.tcx(), t);
     do in_scope_cx(bcx) |scope_info| {
@@ -455,7 +465,7 @@ pub fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) {
 pub fn add_clean_frozen_root(bcx: block, val: ValueRef, t: ty::t) {
     debug!("add_clean_frozen_root(%s, %s, %s)",
            bcx.to_str(), val_str(bcx.ccx().tn, val),
-           ty_to_str(bcx.ccx().tcx, t));
+           t.repr(bcx.tcx()));
     let (root, rooted) = root_for_cleanup(bcx, val, t);
     let cleanup_type = cleanup_type(bcx.tcx(), t);
     do in_scope_cx(bcx) |scope_info| {
@@ -703,7 +713,7 @@ pub impl block_ {
     }
 
     fn expr_to_str(@mut self, e: @ast::expr) -> ~str {
-        expr_repr(self.tcx(), e)
+        e.repr(self.tcx())
     }
 
     fn expr_is_lval(@mut self, e: @ast::expr) -> bool {
@@ -733,7 +743,7 @@ pub impl block_ {
     }
 
     fn ty_to_str(@mut self, t: ty::t) -> ~str {
-        ty_to_str(self.tcx(), t)
+        t.repr(self.tcx())
     }
     fn to_str(@mut self) -> ~str {
         match self.node_info {
@@ -1445,14 +1455,14 @@ pub fn resolve_vtable_in_fn_ctxt(fcx: fn_ctxt, +vt: typeck::vtable_origin)
 pub fn find_vtable(tcx: ty::ctxt, ps: &param_substs,
                    n_param: uint, n_bound: uint)
     -> typeck::vtable_origin {
-    debug!("find_vtable_in_fn_ctxt(n_param=%u, n_bound=%u, ps=%?)",
-           n_param, n_bound, param_substs_to_str(tcx, ps));
+    debug!("find_vtable(n_param=%u, n_bound=%u, ps=%s)",
+           n_param, n_bound, ps.repr(tcx));
 
     // Vtables are stored in a flat array, finding the right one is
     // somewhat awkward
-    let first_n_bounds = ps.bounds.slice(0, n_param);
+    let first_n_type_param_defs = ps.type_param_defs.slice(0, n_param);
     let vtables_to_skip =
-        ty::count_traits_and_supertraits(tcx, first_n_bounds);
+        ty::count_traits_and_supertraits(tcx, first_n_type_param_defs);
     let vtable_off = vtables_to_skip + n_bound;
     /*bad*/ copy ps.vtables.get()[vtable_off]
 }
diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs
index 55e6b30439b..c5e708569dc 100644
--- a/src/librustc/middle/trans/consts.rs
+++ b/src/librustc/middle/trans/consts.rs
@@ -25,7 +25,7 @@ use middle::trans::inline;
 use middle::trans::machine;
 use middle::trans::type_of;
 use middle::ty;
-use util::ppaux::{expr_repr, ty_to_str};
+use util::ppaux::{Repr, ty_to_str};
 
 use core::libc::c_uint;
 use syntax::{ast, ast_util, ast_map};
@@ -237,7 +237,7 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef {
             llvm::LLVMDumpValue(C_undef(llty));
         }
         cx.sess.bug(fmt!("const %s of type %s has size %u instead of %u",
-                         expr_repr(cx.tcx, e), ty_to_str(cx.tcx, ety),
+                         e.repr(cx.tcx), ty_to_str(cx.tcx, ety),
                          csize, tsize));
     }
     llconst
diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs
index 2ca19b90754..15c2e8e3d93 100644
--- a/src/librustc/middle/trans/inline.rs
+++ b/src/librustc/middle/trans/inline.rs
@@ -86,13 +86,11 @@ pub fn maybe_instantiate_inline(ccx: @CrateContext, fn_id: ast::def_id,
           csearch::found(ast::ii_method(impl_did, mth)) => {
             ccx.stats.n_inlines += 1;
             ccx.external.insert(fn_id, Some(mth.id));
-            let ty::ty_param_bounds_and_ty {
-                generics: ty::Generics { bounds: impl_bnds, _ },
-                ty: _
-            } = ty::lookup_item_type(ccx.tcx, impl_did);
-            if translate &&
-                impl_bnds.len() + mth.generics.ty_params.len() == 0u
-            {
+            let impl_tpt = ty::lookup_item_type(ccx.tcx, impl_did);
+            let num_type_params =
+                impl_tpt.generics.type_param_defs.len() +
+                mth.generics.ty_params.len();
+            if translate && num_type_params == 0 {
                 let llfn = get_item_val(ccx, mth.id);
                 let path = vec::append(
                     ty::item_path(ccx.tcx, impl_did),
diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs
index bbf53480a56..c518605faf1 100644
--- a/src/librustc/middle/trans/meth.rs
+++ b/src/librustc/middle/trans/meth.rs
@@ -30,7 +30,7 @@ use middle::ty;
 use middle::ty::arg;
 use middle::typeck;
 use util::common::indenter;
-use util::ppaux::ty_to_str;
+use util::ppaux::Repr;
 
 use syntax::ast_map::{path, path_mod, path_name};
 use syntax::ast_util;
@@ -61,7 +61,7 @@ pub fn trans_impl(ccx: @CrateContext, +path: path, name: ast::ident,
                     param_substs_opt = Some(@param_substs {
                         tys: ~[],
                         vtables: None,
-                        bounds: @~[],
+                        type_param_defs: @~[],
                         self_ty: Some(self_ty)
                     });
                 }
@@ -115,11 +115,8 @@ pub fn trans_method(ccx: @CrateContext,
             }
         };
         debug!("calling trans_fn with base_self_ty %s, self_ty %s",
-               match base_self_ty {
-                    None => ~"(none)",
-                    Some(x) => ty_to_str(ccx.tcx, x),
-               },
-               ty_to_str(ccx.tcx, self_ty));
+               base_self_ty.repr(ccx.tcx),
+               self_ty.repr(ccx.tcx));
         match method.self_ty.node {
           ast::sty_value => {
             impl_owned_self(self_ty)
@@ -175,8 +172,9 @@ pub fn trans_method_callee(bcx: block,
     let _icx = bcx.insn_ctxt("impl::trans_method_callee");
     let tcx = bcx.tcx();
 
-    debug!("trans_method_callee(callee_id=%?, self=%s, mentry=%?)",
-           callee_id, bcx.expr_to_str(self), mentry);
+    debug!("trans_method_callee(callee_id=%?, self=%s, mentry=%s)",
+           callee_id, bcx.expr_to_str(self),
+           mentry.repr(bcx.tcx()));
 
     // Replace method_self with method_static here.
     let mut origin = mentry.origin;
@@ -302,7 +300,8 @@ pub fn trans_static_method_callee(bcx: block,
     // one we are interested in.
     let bound_index = {
         let trait_def = ty::lookup_trait_def(bcx.tcx(), trait_id);
-        ty::count_traits_and_supertraits(bcx.tcx(), *trait_def.generics.bounds)
+        ty::count_traits_and_supertraits(
+            bcx.tcx(), *trait_def.generics.type_param_defs)
     };
 
     let mname = if method_id.crate == ast::local_crate {
@@ -552,14 +551,15 @@ pub fn combine_impl_and_methods_origins(bcx: block,
     let ccx = bcx.ccx(), tcx = bcx.tcx();
     let n_m_tps = method_ty_param_count(ccx, mth_did, impl_did);
     let ty::ty_param_bounds_and_ty {
-        generics: ty::Generics {bounds: r_m_bounds, _},
+        generics: r_m_generics,
         _
     } = ty::lookup_item_type(tcx, mth_did);
-    let n_r_m_tps = r_m_bounds.len(); // rcvr + method tps
-    let m_boundss = vec::slice(*r_m_bounds, n_r_m_tps - n_m_tps, n_r_m_tps);
+    let n_r_m_tps = r_m_generics.type_param_defs.len(); // rcvr + method tps
+    let m_type_param_defs =
+        vec::slice(*r_m_generics.type_param_defs, n_r_m_tps - n_m_tps, n_r_m_tps);
 
     // Flatten out to find the number of vtables the method expects.
-    let m_vtables = ty::count_traits_and_supertraits(tcx, m_boundss);
+    let m_vtables = ty::count_traits_and_supertraits(tcx, m_type_param_defs);
 
     // Find the vtables we computed at type check time and monomorphize them
     let r_m_origins = match node_vtables(bcx, callee_id) {
@@ -787,12 +787,13 @@ pub fn make_impl_vtable(ccx: @CrateContext,
     // XXX: This should support multiple traits.
     let trt_id = ty::impl_trait_refs(tcx, impl_id)[0].def_id;
 
-    let has_tps = ty::lookup_item_type(ccx.tcx, impl_id).generics.bounds.len() > 0u;
+    let has_tps =
+        !ty::lookup_item_type(ccx.tcx, impl_id).generics.type_param_defs.is_empty();
     make_vtable(ccx, ty::trait_method_def_ids(tcx, trt_id).map(|method_def_id| {
         let im = ty::method(tcx, *method_def_id);
         let fty = ty::subst_tps(tcx, substs, None,
                                 ty::mk_bare_fn(tcx, copy im.fty));
-        if im.generics.bounds.len() > 0u || ty::type_has_self(fty) {
+        if im.generics.has_type_params() || ty::type_has_self(fty) {
             debug!("(making impl vtable) method has self or type params: %s",
                    *tcx.sess.str_of(im.ident));
             C_null(T_ptr(T_nil()))
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index 319f57fb75c..97dc3fed780 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -30,7 +30,7 @@ use middle::trans::type_use;
 use middle::ty;
 use middle::ty::{FnSig};
 use middle::typeck;
-use util::ppaux::ty_to_str;
+use util::ppaux::Repr;
 
 use core::vec;
 use syntax::ast;
@@ -46,8 +46,21 @@ pub fn monomorphic_fn(ccx: @CrateContext,
                       real_substs: &[ty::t],
                       vtables: Option<typeck::vtable_res>,
                       impl_did_opt: Option<ast::def_id>,
-                      ref_id: Option<ast::node_id>) ->
-                      (ValueRef, bool) {
+                      ref_id: Option<ast::node_id>)
+    -> (ValueRef, bool)
+{
+    debug!("monomorphic_fn(\
+            fn_id=%s, \
+            real_substs=%s, \
+            vtables=%s, \
+            impl_did_opt=%s, \
+            ref_id=%?)",
+           fn_id.repr(ccx.tcx),
+           real_substs.repr(ccx.tcx),
+           vtables.repr(ccx.tcx),
+           impl_did_opt.repr(ccx.tcx),
+           ref_id);
+
     assert!(real_substs.all(|t| !ty::type_needs_infer(*t)));
     let _icx = ccx.insn_ctxt("monomorphic_fn");
     let mut must_cast = false;
@@ -69,13 +82,15 @@ pub fn monomorphic_fn(ccx: @CrateContext,
         must_cast = true;
     }
 
-    debug!("monomorphic_fn(fn_id=%? (%s), vtables=%?, \
-            real_substs=%?, substs=%?, \
-            hash_id = %?",
-           fn_id, ty::item_path_str(ccx.tcx, fn_id),
-           vtables,
-           real_substs.map(|s| ty_to_str(ccx.tcx, *s)),
-           substs.map(|s| ty_to_str(ccx.tcx, *s)), hash_id);
+    debug!("monomorphic_fn(\
+            fn_id=%s, \
+            vtables=%s, \
+            substs=%s, \
+            hash_id=%?)",
+           fn_id.repr(ccx.tcx),
+           vtables.repr(ccx.tcx),
+           substs.repr(ccx.tcx),
+           hash_id);
 
     match ccx.monomorphized.find(&hash_id) {
       Some(&val) => {
@@ -169,7 +184,7 @@ pub fn monomorphic_fn(ccx: @CrateContext,
     let psubsts = Some(@param_substs {
         tys: substs,
         vtables: vtables,
-        bounds: tpt.generics.bounds,
+        type_param_defs: tpt.generics.type_param_defs,
         self_ty: impl_ty_opt
     });
 
@@ -322,17 +337,19 @@ pub fn normalize_for_monomorphization(tcx: ty::ctxt,
     }
 }
 
-pub fn make_mono_id(ccx: @CrateContext, item: ast::def_id, substs: &[ty::t],
+pub fn make_mono_id(ccx: @CrateContext,
+                    item: ast::def_id,
+                    substs: &[ty::t],
                     vtables: Option<typeck::vtable_res>,
                     impl_did_opt: Option<ast::def_id>,
                     +param_uses: Option<~[type_use::type_uses]>) -> mono_id {
     let precise_param_ids = match vtables {
       Some(vts) => {
-        let bounds = ty::lookup_item_type(ccx.tcx, item).generics.bounds;
+        let item_ty = ty::lookup_item_type(ccx.tcx, item);
         let mut i = 0;
-        vec::map2(*bounds, substs, |bounds, subst| {
+        vec::map2(*item_ty.generics.type_param_defs, substs, |type_param_def, subst| {
             let mut v = ~[];
-            for bounds.each |bound| {
+            for type_param_def.bounds.each |bound| {
                 match *bound {
                   ty::bound_trait(_) => {
                     v.push(meth::vtable_id(ccx, /*bad*/copy vts[i]));
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index c29029de351..74215312434 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -20,12 +20,13 @@ use middle::lint;
 use middle::resolve::{Impl, MethodInfo};
 use middle::resolve;
 use middle::ty;
+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_ref_to_str};
+use util::ppaux::Repr;
 use util::common::{indenter};
 
 use core;
@@ -277,7 +278,7 @@ struct ctxt_ {
     tc_cache: @mut HashMap<uint, TypeContents>,
     ast_ty_to_ty_cache: @mut HashMap<node_id, ast_ty_to_ty_cache_entry>,
     enum_var_cache: @mut HashMap<def_id, @~[VariantInfo]>,
-    ty_param_bounds: @mut HashMap<ast::node_id, param_bounds>,
+    ty_param_defs: @mut HashMap<ast::node_id, TypeParameterDef>,
     inferred_modes: @mut HashMap<ast::node_id, ast::mode>,
     adjustments: @mut HashMap<ast::node_id, @AutoAdjustment>,
     normalized_cache: @mut HashMap<t, t>,
@@ -400,19 +401,12 @@ impl to_bytes::IterBytes for ClosureTy {
     }
 }
 
-#[deriving(Eq)]
+#[deriving(Eq, IterBytes)]
 pub struct param_ty {
     idx: uint,
     def_id: def_id
 }
 
-impl to_bytes::IterBytes for param_ty {
-    fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
-        to_bytes::iter_bytes_2(&self.idx, &self.def_id, lsb0, f)
-    }
-}
-
-
 /// Representation of regions:
 #[auto_encode]
 #[auto_decode]
@@ -754,13 +748,24 @@ impl to_bytes::IterBytes for RegionVid {
     }
 }
 
+pub struct TypeParameterDef {
+    def_id: ast::def_id,
+    bounds: param_bounds
+}
+
 /// Information about the type/lifetime parametesr associated with an item.
 /// Analogous to ast::Generics.
 pub struct Generics {
-    bounds: @~[param_bounds],
+    type_param_defs: @~[TypeParameterDef],
     region_param: Option<region_variance>,
 }
 
+pub impl Generics {
+    fn has_type_params(&self) -> bool {
+        !self.type_param_defs.is_empty()
+    }
+}
+
 /// A polytype.
 ///
 /// - `bounds`: The list of bounds for each type parameter.  The length of the
@@ -853,7 +858,7 @@ pub fn mk_ctxt(s: session::Session,
         methods: @mut HashMap::new(),
         trait_method_def_ids: @mut HashMap::new(),
         trait_methods_cache: @mut HashMap::new(),
-        ty_param_bounds: @mut HashMap::new(),
+        ty_param_defs: @mut HashMap::new(),
         inferred_modes: @mut HashMap::new(),
         adjustments: @mut HashMap::new(),
         normalized_cache: new_ty_hash(),
@@ -1227,6 +1232,12 @@ pub fn fold_sig(sig: &FnSig, fldop: &fn(t) -> t) -> FnSig {
     }
 }
 
+pub fn fold_bare_fn_ty(fty: &BareFnTy, fldop: &fn(t) -> t) -> BareFnTy {
+    BareFnTy {sig: fold_sig(&fty.sig, fldop),
+              abis: fty.abis,
+              purity: fty.purity}
+}
+
 fn fold_sty(sty: &sty, fldop: &fn(t) -> t) -> sty {
     fn fold_substs(substs: &substs, fldop: &fn(t) -> t) -> substs {
         substs {self_r: substs.self_r,
@@ -1261,8 +1272,7 @@ fn fold_sty(sty: &sty, fldop: &fn(t) -> t) -> sty {
             ty_tup(new_ts)
         }
         ty_bare_fn(ref f) => {
-            let sig = fold_sig(&f.sig, fldop);
-            ty_bare_fn(BareFnTy {sig: sig, abis: f.abis, purity: f.purity})
+            ty_bare_fn(fold_bare_fn_ty(f, fldop))
         }
         ty_closure(ref f) => {
             let sig = fold_sig(&f.sig, fldop);
@@ -1409,94 +1419,22 @@ pub fn substs_is_noop(substs: &substs) -> bool {
 }
 
 pub fn substs_to_str(cx: ctxt, substs: &substs) -> ~str {
-    fmt!("substs(self_r=%s, self_ty=%s, tps=%?)",
-         substs.self_r.map_default(~"none", |r| region_to_str(cx, *r)),
-         substs.self_ty.map_default(~"none",
-                                    |t| ::util::ppaux::ty_to_str(cx, *t)),
-         tys_to_str(cx, substs.tps))
+    substs.repr(cx)
 }
 
 pub fn param_bound_to_str(cx: ctxt, pb: &param_bound) -> ~str {
-    match *pb {
-        bound_copy => ~"copy",
-        bound_durable => ~"'static",
-        bound_owned => ~"owned",
-        bound_const => ~"const",
-        bound_trait(t) => ::util::ppaux::trait_ref_to_str(cx, t)
-    }
+    pb.repr(cx)
 }
 
 pub fn param_bounds_to_str(cx: ctxt, pbs: param_bounds) -> ~str {
-    fmt!("%?", pbs.map(|pb| param_bound_to_str(cx, pb)))
+    pbs.repr(cx)
 }
 
 pub fn subst(cx: ctxt,
              substs: &substs,
              typ: t)
           -> t {
-    debug!("subst(substs=%s, typ=%s)",
-           substs_to_str(cx, substs),
-           ::util::ppaux::ty_to_str(cx, typ));
-
-    if substs_is_noop(substs) { return typ; }
-    let r = do_subst(cx, substs, typ);
-    debug!("  r = %s", ::util::ppaux::ty_to_str(cx, r));
-    return r;
-
-    fn do_subst(cx: ctxt,
-                substs: &substs,
-                typ: t) -> t {
-        let tb = get(typ);
-        if !tbox_has_flag(tb, needs_subst) { return typ; }
-        match tb.sty {
-          ty_param(p) => substs.tps[p.idx],
-          ty_self(_) => substs.self_ty.get(),
-          _ => {
-            fold_regions_and_ty(
-                cx, typ,
-                |r| match r {
-                    re_bound(br_self) => {
-                        match substs.self_r {
-                            None => {
-                                cx.sess.bug(
-                                    fmt!("ty::subst: \
-                                  Reference to self region when given substs \
-                                  with no self region, ty = %s",
-                                  ::util::ppaux::ty_to_str(cx, typ)))
-                            }
-                            Some(self_r) => self_r
-                        }
-                    }
-                    _ => r
-                },
-                |t| do_subst(cx, substs, t),
-                |t| do_subst(cx, substs, t))
-          }
-        }
-    }
-}
-
-pub fn subst_in_trait_ref(cx: ctxt,
-                          substs: &substs,
-                          trait_ref: &ty::TraitRef) -> ty::TraitRef
-{
-    ty::TraitRef {
-        def_id: trait_ref.def_id,
-        substs: subst_in_substs(cx, substs, &trait_ref.substs)
-    }
-}
-
-// Performs substitutions on a set of substitutions (result = sup(sub)) to
-// yield a new set of substitutions. This is used in trait inheritance.
-pub fn subst_in_substs(cx: ctxt,
-                       substs: &substs,
-                       in_substs: &substs) -> substs
-{
-    substs {
-        self_r: in_substs.self_r,
-        self_ty: in_substs.self_ty.map(|&typ| subst(cx, substs, typ)),
-        tps: in_substs.tps.map(|&typ| subst(cx, substs, typ))
-    }
+    typ.subst(cx, substs)
 }
 
 // Type utilities
@@ -1511,6 +1449,10 @@ pub fn type_is_error(ty: t) -> bool {
     (get(ty).flags & (has_ty_err as uint)) != 0
 }
 
+pub fn type_needs_subst(ty: t) -> bool {
+    tbox_has_flag(get(ty), needs_subst)
+}
+
 pub fn trait_ref_contains_error(tref: &ty::TraitRef) -> bool {
     tref.substs.self_ty.any(|&t| type_is_error(t)) ||
         tref.substs.tps.any(|&t| type_is_error(t))
@@ -2046,8 +1988,8 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
                 // def-id.
                 assert!(p.def_id.crate == ast::local_crate);
 
-                param_bounds_to_contents(
-                    cx, *cx.ty_param_bounds.get(&p.def_id.node))
+                type_param_def_to_contents(
+                    cx, cx.ty_param_defs.get(&p.def_id.node))
             }
 
             ty_self(_) => {
@@ -2141,13 +2083,13 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
         st + rt + ot
     }
 
-    fn param_bounds_to_contents(cx: ctxt,
-                                bounds: param_bounds) -> TypeContents
+    fn type_param_def_to_contents(cx: ctxt,
+                                  type_param_def: &TypeParameterDef) -> TypeContents
     {
-        debug!("param_bounds_to_contents()");
+        debug!("type_param_def_to_contents(%s)", type_param_def.repr(cx));
         let _i = indenter();
 
-        let r = bounds.foldl(TC_ALL, |tc, bound| {
+        let r = type_param_def.bounds.foldl(TC_ALL, |tc, bound| {
             debug!("tc = %s, bound = %?", tc.to_str(), bound);
             match *bound {
                 bound_copy => tc - TypeContents::nonimplicitly_copyable(cx),
@@ -3042,16 +2984,18 @@ pub fn expr_has_ty_params(cx: ctxt, expr: @ast::expr) -> bool {
     return node_id_has_type_params(cx, expr.id);
 }
 
-pub fn method_call_bounds(tcx: ctxt, method_map: typeck::method_map,
-                          id: ast::node_id)
-    -> Option<@~[param_bounds]> {
+pub fn method_call_type_param_defs(
+    tcx: ctxt,
+    method_map: typeck::method_map,
+    id: ast::node_id) -> Option<@~[TypeParameterDef]>
+{
     do method_map.find(&id).map |method| {
         match method.origin {
           typeck::method_static(did) => {
             // n.b.: When we encode impl methods, the bounds
             // that we encode include both the impl bounds
             // and then the method bounds themselves...
-            ty::lookup_item_type(tcx, did).generics.bounds
+            ty::lookup_item_type(tcx, did).generics.type_param_defs
           }
           typeck::method_param(typeck::method_param {
               trait_id: trt_id,
@@ -3062,9 +3006,11 @@ pub fn method_call_bounds(tcx: ctxt, method_map: typeck::method_map,
             // ...trait methods bounds, in contrast, include only the
             // method bounds, so we must preprend the tps from the
             // trait itself.  This ought to be harmonized.
-            let trt_bounds = ty::lookup_trait_def(tcx, trt_id).generics.bounds;
-            @(vec::append(/*bad*/copy *trt_bounds,
-                          *ty::trait_method(tcx, trt_id, n_mth).generics.bounds))
+            let trait_type_param_defs =
+                ty::lookup_trait_def(tcx, trt_id).generics.type_param_defs;
+            @vec::append(
+                copy *trait_type_param_defs,
+                *ty::trait_method(tcx, trt_id, n_mth).generics.type_param_defs)
           }
         }
     }
@@ -3614,6 +3560,12 @@ pub fn trait_supertraits(cx: ctxt,
     return result;
 }
 
+pub fn trait_ref_supertraits(cx: ctxt, trait_ref: &ty::TraitRef) -> ~[@TraitRef] {
+    let supertrait_refs = trait_supertraits(cx, trait_ref.def_id);
+    supertrait_refs.map(
+        |supertrait_ref| @supertrait_ref.subst(cx, &trait_ref.substs))
+}
+
 fn lookup_locally_or_in_crate_store<V:Copy>(
     descr: &str,
     def_id: ast::def_id,
@@ -4327,11 +4279,9 @@ pub fn determine_inherited_purity(parent_purity: ast::purity,
 // Here, the supertraits are the transitive closure of the supertrait
 // relation on the supertraits from each bounded trait's constraint
 // list.
-pub fn iter_bound_traits_and_supertraits(tcx: ctxt,
+pub fn each_bound_trait_and_supertraits(tcx: ctxt,
                                          bounds: param_bounds,
                                          f: &fn(&TraitRef) -> bool) {
-    let mut fin = false;
-
     for bounds.each |bound| {
         let bound_trait_ref = match *bound {
             ty::bound_trait(bound_t) => bound_t,
@@ -4343,51 +4293,46 @@ pub fn iter_bound_traits_and_supertraits(tcx: ctxt,
         };
 
         let mut supertrait_set = HashMap::new();
-        let mut seen_def_ids = ~[];
+        let mut trait_refs = ~[];
         let mut i = 0;
-        let trait_ty_id = bound_trait_ref.def_id;
-        let mut trait_ref = bound_trait_ref;
 
-        debug!("iter_bound_traits_and_supertraits: trait_ref = %s",
-               trait_ref_to_str(tcx, trait_ref));
+        // Seed the worklist with the trait from the bound
+        supertrait_set.insert(bound_trait_ref.def_id, ());
+        trait_refs.push(bound_trait_ref);
 
         // Add the given trait ty to the hash map
-        supertrait_set.insert(trait_ty_id, ());
-        seen_def_ids.push(trait_ty_id);
-
-        if f(trait_ref) {
-            // Add all the supertraits to the hash map,
-            // executing <f> on each of them
-            while i < supertrait_set.len() && !fin {
-                let init_trait_id = seen_def_ids[i];
-                i += 1;
-
-                 // Add supertraits to supertrait_set
-                let supertrait_refs = trait_supertraits(tcx, init_trait_id);
-                for supertrait_refs.each |&supertrait_ref| {
-                    let d_id = supertrait_ref.def_id;
-                    if !supertrait_set.contains_key(&d_id) {
-                        // FIXME(#5527) Could have same trait multiple times
-                        supertrait_set.insert(d_id, ());
-                        trait_ref = supertrait_ref;
-                        seen_def_ids.push(d_id);
-                    }
-                    debug!("A super_t = %s", trait_ref_to_str(tcx, trait_ref));
-                    if !f(trait_ref) {
-                        fin = true;
-                    }
+        while i < trait_refs.len() {
+            debug!("each_bound_trait_and_supertraits(i=%?, trait_ref=%s)",
+                   i, trait_refs[i].repr(tcx));
+
+            if !f(trait_refs[i]) {
+                return;
+            }
+
+            // Add supertraits to supertrait_set
+            let supertrait_refs = trait_ref_supertraits(tcx, trait_refs[i]);
+            for supertrait_refs.each |&supertrait_ref| {
+                debug!("each_bound_trait_and_supertraits(supertrait_ref=%s)",
+                       supertrait_ref.repr(tcx));
+
+                let d_id = supertrait_ref.def_id;
+                if !supertrait_set.contains_key(&d_id) {
+                    // FIXME(#5527) Could have same trait multiple times
+                    supertrait_set.insert(d_id, ());
+                    trait_refs.push(supertrait_ref);
                 }
             }
-        };
-        fin = false;
+
+            i += 1;
+        }
     }
 }
 
 pub fn count_traits_and_supertraits(tcx: ctxt,
-                                    boundses: &[param_bounds]) -> uint {
+                                    type_param_defs: &[TypeParameterDef]) -> uint {
     let mut total = 0;
-    for boundses.each |bounds| {
-        for iter_bound_traits_and_supertraits(tcx, *bounds) |_trait_ty| {
+    for type_param_defs.each |type_param_def| {
+        for each_bound_trait_and_supertraits(tcx, type_param_def.bounds) |_| {
             total += 1;
         }
     }
diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs
index 209091ecd66..bfc9d646dbb 100644
--- a/src/librustc/middle/typeck/astconv.rs
+++ b/src/librustc/middle/typeck/astconv.rs
@@ -135,6 +135,7 @@ fn ast_path_substs<AC:AstConv,RS:region_scope + Copy + Durable>(
     rscope: &RS,
     def_id: ast::def_id,
     decl_generics: &ty::Generics,
+    self_ty: Option<ty::t>,
     path: @ast::path) -> ty::substs
 {
     /*!
@@ -172,15 +173,15 @@ fn ast_path_substs<AC:AstConv,RS:region_scope + Copy + Durable>(
     };
 
     // Convert the type parameters supplied by the user.
-    if !vec::same_length(*decl_generics.bounds, path.types) {
+    if !vec::same_length(*decl_generics.type_param_defs, path.types) {
         self.tcx().sess.span_fatal(
             path.span,
             fmt!("wrong number of type arguments: expected %u but found %u",
-                 decl_generics.bounds.len(), path.types.len()));
+                 decl_generics.type_param_defs.len(), path.types.len()));
     }
     let tps = path.types.map(|a_t| ast_ty_to_ty(self, rscope, *a_t));
 
-    substs {self_r:self_r, self_ty:None, tps:tps}
+    substs {self_r:self_r, self_ty:self_ty, tps:tps}
 }
 
 pub fn ast_path_to_substs_and_ty<AC:AstConv,RS:region_scope + Copy + Durable>(
@@ -195,7 +196,7 @@ pub fn ast_path_to_substs_and_ty<AC:AstConv,RS:region_scope + Copy + Durable>(
         ty: decl_ty
     } = self.get_item_ty(did);
 
-    let substs = ast_path_substs(self, rscope, did, &generics, path);
+    let substs = ast_path_substs(self, rscope, did, &generics, None, path);
     let ty = ty::subst(tcx, &substs, decl_ty);
     ty_param_substs_and_ty { substs: substs, ty: ty }
 }
@@ -204,14 +205,18 @@ pub fn ast_path_to_trait_ref<AC:AstConv,RS:region_scope + Copy + Durable>(
     self: &AC,
     rscope: &RS,
     trait_def_id: ast::def_id,
+    self_ty: Option<ty::t>,
     path: @ast::path) -> @ty::TraitRef
 {
     let trait_def =
         self.get_trait_def(trait_def_id);
     let substs =
         ast_path_substs(
-            self, rscope,
-            trait_def.trait_ref.def_id, &trait_def.generics,
+            self,
+            rscope,
+            trait_def.trait_ref.def_id,
+            &trait_def.generics,
+            self_ty,
             path);
     let trait_ref =
         @ty::TraitRef {def_id: trait_def_id,
@@ -280,7 +285,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:region_scope + Copy + Durable>(
                     }
                     Some(&ast::def_trait(trait_def_id)) => {
                         let result = ast_path_to_trait_ref(
-                            self, rscope, trait_def_id, path);
+                            self, rscope, trait_def_id, None, path);
                         let trait_store = match vst {
                             ty::vstore_box => ty::BoxTraitStore,
                             ty::vstore_uniq => ty::UniqTraitStore,
diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs
index 605caba74a3..aa2414eac62 100644
--- a/src/librustc/middle/typeck/check/_match.rs
+++ b/src/librustc/middle/typeck/check/_match.rs
@@ -135,7 +135,9 @@ pub fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
                     ty::enum_variant_with_id(tcx, enm, var);
                 let var_tpt = ty::lookup_item_type(tcx, var);
                 vinfo.args.map(|t| {
-                    if var_tpt.generics.bounds.len() == expected_substs.tps.len() {
+                    if var_tpt.generics.type_param_defs.len() ==
+                        expected_substs.tps.len()
+                    {
                         ty::subst(tcx, expected_substs, *t)
                     }
                     else {
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index fa3cb43d9ab..6a274e7f9eb 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -94,7 +94,6 @@ use middle::typeck::{method_map_entry, method_origin, method_param};
 use middle::typeck::{method_self, method_static, method_trait, method_super};
 use middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig;
 use util::common::indenter;
-use util::ppaux::expr_repr;
 
 use core::hashmap::HashSet;
 use core::result;
@@ -149,8 +148,7 @@ pub fn lookup(
         autoderef_receiver: autoderef_receiver,
     };
     let mme = lcx.do_lookup(self_ty);
-    debug!("method lookup for %s yielded %?",
-           expr_repr(fcx.tcx(), expr), mme);
+    debug!("method lookup for %s yielded %?", expr.repr(fcx.tcx()), mme);
     return mme;
 }
 
@@ -197,9 +195,8 @@ pub impl<'self> LookupContext<'self> {
 
         debug!("do_lookup(self_ty=%s, expr=%s, self_expr=%s)",
                self.ty_to_str(self_ty),
-               expr_repr(self.tcx(), self.expr),
-               expr_repr(self.tcx(), self.self_expr));
-        let _indenter = indenter();
+               self.expr.repr(self.tcx()),
+               self.self_expr.repr(self.tcx()));
 
         // Prepare the list of candidates
         self.push_inherent_candidates(self_ty);
@@ -379,105 +376,59 @@ pub impl<'self> LookupContext<'self> {
 
         let tcx = self.tcx();
         let mut next_bound_idx = 0; // count only trait bounds
-        let bounds = tcx.ty_param_bounds.get(&param_ty.def_id.node);
-
-        for bounds.each |bound| {
-            let bound_trait_ref = match *bound {
-                ty::bound_trait(bound_t) => bound_t,
+        let type_param_def = match tcx.ty_param_defs.find(&param_ty.def_id.node) {
+            Some(t) => t,
+            None => {
+                tcx.sess.span_bug(
+                    self.expr.span,
+                    fmt!("No param def for %?", param_ty));
+            }
+        };
 
-                ty::bound_copy | ty::bound_owned |
-                ty::bound_const | ty::bound_durable => {
-                    loop; // skip non-trait bounds
+        for ty::each_bound_trait_and_supertraits(tcx, type_param_def.bounds)
+            |bound_trait_ref|
+        {
+            let this_bound_idx = next_bound_idx;
+            next_bound_idx += 1;
+
+            let trait_methods = ty::trait_methods(tcx, bound_trait_ref.def_id);
+            let pos = {
+                match trait_methods.position(|m| {
+                    m.self_ty != ast::sty_static &&
+                        m.ident == self.m_name })
+                {
+                    Some(pos) => pos,
+                    None => {
+                        debug!("trait doesn't contain method: %?",
+                               bound_trait_ref.def_id);
+                        loop; // check next trait or bound
+                    }
                 }
             };
+            let method = trait_methods[pos];
 
-            // Loop over the trait and all of its supertraits.
-            let mut worklist = ~[];
-
-            let init_trait_ref = bound_trait_ref;
-
-            // Replace any appearance of `self` with the type of the
-            // generic parameter itself.  Note that this is the only
-            // case where this replacement is necessary: in all other
-            // cases, we are either invoking a method directly from an
-            // impl or class (where the self type is not permitted),
-            // or from a trait type (in which case methods that refer
-            // to self are not permitted).
-            let init_substs = substs {
-                self_ty: Some(rcvr_ty),
-                ..copy bound_trait_ref.substs
-            };
-
-            worklist.push((init_trait_ref.def_id, init_substs));
-
-            let mut i = 0;
-            while i < worklist.len() {
-                let (init_trait_id, init_substs) = /*bad*/copy worklist[i];
-                i += 1;
-
-                // Add all the supertraits of this trait to the worklist.
-                let supertraits = ty::trait_supertraits(tcx, init_trait_id);
-                for supertraits.each |supertrait_ref| {
-                    debug!("adding supertrait: %?",
-                           supertrait_ref.def_id);
-
-                    let new_substs = ty::subst_in_substs(
-                        tcx,
-                        &init_substs,
-                        &supertrait_ref.substs);
-
-                    // Again replacing the self type
-                    let new_substs = substs {
-                        self_ty: Some(rcvr_ty),
-                        ..new_substs
-                    };
-
-                    worklist.push((supertrait_ref.def_id, new_substs));
-                }
-
-
-                let this_bound_idx = next_bound_idx;
-                next_bound_idx += 1;
-
-                let trait_methods = ty::trait_methods(tcx, init_trait_id);
-                let pos = {
-                    match trait_methods.position(|m| {
-                        m.self_ty != ast::sty_static &&
-                            m.ident == self.m_name })
-                    {
-                        Some(pos) => pos,
-                        None => {
-                            debug!("trait doesn't contain method: %?",
-                                   init_trait_id);
-                            loop; // check next trait or bound
-                        }
-                    }
-                };
-                let method = trait_methods[pos];
-
-                let (rcvr_ty, rcvr_substs) =
-                    self.create_rcvr_ty_and_substs_for_method(
-                        method.self_ty,
-                        rcvr_ty,
-                        init_substs,
-                        TransformTypeNormally);
+            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,
-                    method_ty: method,
-                    origin: method_param(
-                        method_param {
-                            trait_id: init_trait_id,
-                            method_num: pos,
-                            param_num: param_ty.idx,
-                            bound_num: this_bound_idx,
-                        })
-                };
+            let cand = Candidate {
+                rcvr_ty: rcvr_ty,
+                rcvr_substs: rcvr_substs,
+                method_ty: method,
+                origin: method_param(
+                    method_param {
+                        trait_id: bound_trait_ref.def_id,
+                        method_num: pos,
+                        param_num: param_ty.idx,
+                        bound_num: this_bound_idx,
+                    })
+            };
 
-                debug!("pushing inherent candidate for param: %?", cand);
-                self.inherent_candidates.push(cand);
-            }
+            debug!("pushing inherent candidate for param: %?", cand);
+            self.inherent_candidates.push(cand);
         }
     }
 
@@ -499,7 +450,7 @@ pub impl<'self> LookupContext<'self> {
         };
         let method = ms[index];
 
-        /* FIXME(#3157) we should transform the vstore in accordance
+        /* FIXME(#5762) we should transform the vstore in accordance
            with the self type
 
         match method.self_type {
@@ -517,6 +468,9 @@ pub impl<'self> LookupContext<'self> {
         // `trait_ty` for `self` here, because it allows the compiler
         // to soldier on.  An error will be reported should this
         // candidate be selected if the method refers to `self`.
+        //
+        // NB: `confirm_candidate()` also relies upon this substitution
+        // for Self.
         let rcvr_substs = substs {
             self_ty: Some(self_ty),
             ../*bad*/copy *substs
@@ -1075,7 +1029,7 @@ pub impl<'self> LookupContext<'self> {
         let fty = self.fn_ty_from_origin(&candidate.origin);
 
         debug!("confirm_candidate(expr=%s, candidate=%s, fty=%s)",
-               expr_repr(tcx, self.expr),
+               self.expr.repr(tcx),
                self.cand_to_str(candidate),
                self.ty_to_str(fty));
 
@@ -1101,7 +1055,7 @@ pub impl<'self> LookupContext<'self> {
         // If they were not explicitly supplied, just construct fresh
         // type variables.
         let num_supplied_tps = self.supplied_tps.len();
-        let num_method_tps = candidate.method_ty.generics.bounds.len();
+        let num_method_tps = candidate.method_ty.generics.type_param_defs.len();
         let m_substs = {
             if num_supplied_tps == 0u {
                 self.fcx.infcx().next_ty_vars(num_method_tps)
@@ -1195,7 +1149,7 @@ pub impl<'self> LookupContext<'self> {
                   self-type through a boxed trait");
         }
 
-        if candidate.method_ty.generics.bounds.len() > 0 {
+        if candidate.method_ty.generics.has_type_params() {
             self.tcx().sess.span_err(
                 self.expr.span,
                 ~"cannot call a generic method through a boxed trait");
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index c3ec2d14d83..24552019074 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -107,7 +107,7 @@ use middle::typeck::{isr_alist, lookup_def_ccx};
 use middle::typeck::no_params;
 use middle::typeck::{require_same_types, method_map, vtable_map};
 use util::common::{block_query, indenter, loop_query};
-use util::ppaux::{bound_region_to_str, expr_repr, pat_repr};
+use util::ppaux::{bound_region_to_str};
 use util::ppaux;
 
 use core::hashmap::HashMap;
@@ -610,7 +610,7 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) {
         } else {
             for m.items.each |item| {
                 let tpt = ty::lookup_item_type(ccx.tcx, local_def(item.id));
-                if !tpt.generics.bounds.is_empty() {
+                if tpt.generics.has_type_params() {
                     ccx.tcx.sess.span_err(
                         item.span,
                         fmt!("foreign items may not have type parameters"));
@@ -761,11 +761,11 @@ pub impl FnCtxt {
     }
 
     fn expr_to_str(&self, expr: @ast::expr) -> ~str {
-        expr_repr(self.tcx(), expr)
+        expr.repr(self.tcx())
     }
 
     fn pat_to_str(&self, pat: @ast::pat) -> ~str {
-        pat_repr(self.tcx(), pat)
+        pat.repr(self.tcx())
     }
 
     fn expr_ty(&self, ex: @ast::expr) -> ty::t {
@@ -1068,7 +1068,7 @@ pub fn impl_self_ty(vcx: &VtableContext,
 
     let (n_tps, region_param, raw_ty) = {
         let ity = ty::lookup_item_type(tcx, did);
-        (ity.generics.bounds.len(), ity.generics.region_param, ity.ty)
+        (ity.generics.type_param_defs.len(), ity.generics.region_param, ity.ty)
     };
 
     let self_r = if region_param.is_some() {
@@ -1893,7 +1893,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
             }
         } else {
             let item_type = ty::lookup_item_type(tcx, class_id);
-            type_parameter_count = item_type.generics.bounds.len();
+            type_parameter_count = item_type.generics.type_param_defs.len();
             region_parameterized = item_type.generics.region_param;
             raw_type = item_type.ty;
         }
@@ -1981,7 +1981,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
             }
         } else {
             let item_type = ty::lookup_item_type(tcx, enum_id);
-            type_parameter_count = item_type.generics.bounds.len();
+            type_parameter_count = item_type.generics.type_param_defs.len();
             region_parameterized = item_type.generics.region_param;
             raw_type = item_type.ty;
         }
@@ -3153,7 +3153,7 @@ pub fn ty_param_bounds_and_ty_for_def(fcx: @mut FnCtxt,
         // extern functions are just u8 pointers
         return ty_param_bounds_and_ty {
             generics: ty::Generics {
-                bounds: @~[],
+                type_param_defs: @~[],
                 region_param: None
             },
             ty: ty::mk_ptr(
@@ -3218,7 +3218,7 @@ pub fn instantiate_path(fcx: @mut FnCtxt,
                         region_lb: ty::Region) {
     debug!(">>> instantiate_path");
 
-    let ty_param_count = tpt.generics.bounds.len();
+    let ty_param_count = tpt.generics.type_param_defs.len();
     let ty_substs_len = vec::len(pth.types);
 
     debug!("ty_param_count=%? ty_substs_len=%?",
@@ -3692,7 +3692,7 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) {
                     output: output}
     });
     let i_ty = ty::lookup_item_type(ccx.tcx, local_def(it.id));
-    let i_n_tps = i_ty.generics.bounds.len();
+    let i_n_tps = i_ty.generics.type_param_defs.len();
     if i_n_tps != n_tps {
         tcx.sess.span_err(it.span, fmt!("intrinsic has wrong number \
                                          of type parameters: found %u, \
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index c30765baf37..6ea668605fd 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -20,6 +20,7 @@ use middle::typeck::infer::{resolve_and_force_all_but_regions, resolve_type};
 use middle::typeck::infer;
 use middle::typeck::{CrateCtxt, vtable_origin, vtable_param, vtable_res};
 use middle::typeck::vtable_static;
+use middle::subst::Subst;
 use util::common::indenter;
 use util::ppaux::tys_to_str;
 use util::ppaux;
@@ -27,7 +28,6 @@ use util::ppaux;
 use core::result::{Ok, Err};
 use core::result;
 use core::uint;
-use core::vec;
 use core::hashmap::HashSet;
 use syntax::ast;
 use syntax::ast_util;
@@ -70,45 +70,43 @@ pub impl VtableContext {
     fn tcx(&const self) -> ty::ctxt { self.ccx.tcx }
 }
 
-fn has_trait_bounds(tps: ~[ty::param_bounds]) -> bool {
-    vec::any(tps, |bs| {
-        bs.any(|b| {
-            match b { &ty::bound_trait(_) => true, _ => false }
-        })
-    })
+fn has_trait_bounds(type_param_defs: &[ty::TypeParameterDef]) -> bool {
+    type_param_defs.any(
+        |type_param_def| type_param_def.bounds.any(
+            |bound| match bound { &ty::bound_trait(*) => true, _ => false }))
 }
 
 fn lookup_vtables(vcx: &VtableContext,
                   location_info: &LocationInfo,
-                  bounds: @~[ty::param_bounds],
+                  type_param_defs: &[ty::TypeParameterDef],
                   substs: &ty::substs,
                   is_early: bool) -> vtable_res {
-    debug!("lookup_vtables(location_info=%?,
-            # bounds=%?, \
+    debug!("lookup_vtables(location_info=%?, \
+            type_param_defs=%s, \
             substs=%s",
            location_info,
-           bounds.len(),
-           ty::substs_to_str(vcx.tcx(), substs));
+           type_param_defs.repr(vcx.tcx()),
+           substs.repr(vcx.tcx()));
     let _i = indenter();
 
     let tcx = vcx.tcx();
     let mut result = ~[], i = 0u;
     for substs.tps.each |ty| {
-        for ty::iter_bound_traits_and_supertraits(
-            tcx, bounds[i]) |trait_ref|
+        // ty is the value supplied for the type parameter A...
+
+        for ty::each_bound_trait_and_supertraits(
+            tcx, type_param_defs[i].bounds) |trait_ref|
         {
-            debug!("about to subst: %?, %?",
-                   ppaux::trait_ref_to_str(tcx, trait_ref),
-                   ty::substs_to_str(tcx, substs));
+            // ...and here trait_ref is each bound that was declared on A,
+            // expressed in terms of the type parameters.
+
+            debug!("about to subst: %s, %s", trait_ref.repr(tcx), substs.repr(tcx));
 
-            let new_substs = substs {
-                self_ty: Some(*ty),
-                ../*bad*/copy *substs
-            };
-            let trait_ref = ty::subst_in_trait_ref(tcx, &new_substs, trait_ref);
+            // Substitute the values of the type parameters that may
+            // appear in the bound.
+            let trait_ref = trait_ref.subst(tcx, substs);
 
-            debug!("after subst: %?",
-                   vcx.infcx.trait_ref_to_str(&trait_ref));
+            debug!("after subst: %s", trait_ref.repr(tcx));
 
             match lookup_vtable(vcx, location_info, *ty, &trait_ref, is_early) {
                 Some(vtable) => result.push(vtable),
@@ -125,14 +123,14 @@ fn lookup_vtables(vcx: &VtableContext,
         i += 1u;
     }
     debug!("lookup_vtables result(\
-            location_info=%?,
-            # bounds=%?, \
+            location_info=%?, \
+            type_param_defs=%s, \
             substs=%s, \
-            result=%?",
+            result=%s)",
            location_info,
-           bounds.len(),
-           ty::substs_to_str(vcx.tcx(), substs),
-           result);
+           type_param_defs.repr(vcx.tcx()),
+           substs.repr(vcx.tcx()),
+           result.repr(vcx.tcx()));
     @result
 }
 
@@ -219,12 +217,11 @@ fn lookup_vtable(vcx: &VtableContext,
     match ty::get(ty).sty {
         ty::ty_param(param_ty {idx: n, def_id: did}) => {
             let mut n_bound = 0;
-            let bounds = *tcx.ty_param_bounds.get(&did.node);
-            for ty::iter_bound_traits_and_supertraits(
-                tcx, bounds) |bound_trait_ref|
+            let type_param_def = tcx.ty_param_defs.get(&did.node);
+            for ty::each_bound_trait_and_supertraits(
+                tcx, type_param_def.bounds) |bound_trait_ref|
             {
-                debug!("checking bounds trait %?",
-                       vcx.infcx.trait_ref_to_str(bound_trait_ref));
+                debug!("checking bounds trait %s", bound_trait_ref.repr(vcx.tcx()));
 
                 if bound_trait_ref.def_id == trait_ref.def_id {
                     relate_trait_refs(vcx,
@@ -302,6 +299,8 @@ fn lookup_vtable(vcx: &VtableContext,
                             // of the thing that we're trying to cast
                             // to some_trait.  If not, then we try the next
                             // impl.
+                            //
+                            // FIXME(#5781) this should be mk_eqty not mk_subty
                             let ty::ty_param_substs_and_ty {
                                 substs: substs,
                                 ty: for_ty
@@ -341,8 +340,7 @@ fn lookup_vtable(vcx: &VtableContext,
                                    vcx.infcx.trait_ref_to_str(trait_ref),
                                    vcx.infcx.trait_ref_to_str(of_trait_ref));
 
-                            let of_trait_ref =
-                                ty::subst_in_trait_ref(tcx, &substs, of_trait_ref);
+                            let of_trait_ref = of_trait_ref.subst(tcx, &substs);
                             relate_trait_refs(
                                 vcx, location_info,
                                 &of_trait_ref, trait_ref);
@@ -387,15 +385,16 @@ fn lookup_vtable(vcx: &VtableContext,
                             // to. connect_trait_tps requires these
                             // lists of types to unify pairwise.
 
-                            let im_bs =
-                                ty::lookup_item_type(tcx, im.did).generics.bounds;
+                            let im_generics =
+                                ty::lookup_item_type(tcx, im.did).generics;
                             connect_trait_tps(vcx,
                                               location_info,
                                               &substs_f,
                                               trait_ref,
                                               im.did);
                             let subres = lookup_vtables(
-                                vcx, location_info, im_bs, &substs_f,
+                                vcx, location_info,
+                                *im_generics.type_param_defs, &substs_f,
                                 is_early);
 
                             // Finally, we register that we found a
@@ -460,15 +459,15 @@ fn connect_trait_tps(vcx: &VtableContext,
 
     // XXX: This should work for multiple traits.
     let impl_trait_ref = ty::impl_trait_refs(tcx, impl_did)[0];
-    let impl_trait_ref = ty::subst_in_trait_ref(tcx, impl_substs, impl_trait_ref);
-    relate_trait_refs(vcx, location_info, trait_ref, &impl_trait_ref);
+    let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
+    relate_trait_refs(vcx, location_info, &impl_trait_ref, trait_ref);
 }
 
 fn insert_vtables(fcx: @mut FnCtxt,
                   callee_id: ast::node_id,
                   vtables: vtable_res) {
     debug!("insert_vtables(callee_id=%d, vtables=%?)",
-           callee_id, vtables.map(|v| v.to_str(fcx.tcx())));
+           callee_id, vtables.repr(fcx.tcx()));
     fcx.inh.vtable_map.insert(callee_id, vtables);
 }
 
@@ -493,17 +492,15 @@ pub fn early_resolve_expr(ex: @ast::expr,
             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);
-            debug!("early resolve expr: def %? %?, %?, %?", ex.id, did, def,
+            debug!("early resolve expr: def %? %?, %?, %s", ex.id, did, def,
                    fcx.infcx().ty_to_str(item_ty.ty));
-            if has_trait_bounds(/*bad*/copy *item_ty.generics.bounds) {
-                for item_ty.generics.bounds.each |bounds| {
-                    debug!("early_resolve_expr: looking up vtables for bound \
-                            %s",
-                           ty::param_bounds_to_str(fcx.tcx(), *bounds));
-                }
+            if has_trait_bounds(*item_ty.generics.type_param_defs) {
+                debug!("early_resolve_expr: looking up vtables for type params %s",
+                       item_ty.generics.type_param_defs.repr(fcx.tcx()));
                 let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() };
                 let vtbls = lookup_vtables(&vcx, &location_info_for_expr(ex),
-                                           item_ty.generics.bounds, substs, is_early);
+                                           *item_ty.generics.type_param_defs,
+                                           substs, is_early);
                 if !is_early {
                     insert_vtables(fcx, ex.id, vtbls);
                 }
@@ -519,9 +516,9 @@ pub fn early_resolve_expr(ex: @ast::expr,
       ast::expr_binary(*) |
       ast::expr_unary(*) | ast::expr_assign_op(*) |
       ast::expr_index(*) | ast::expr_method_call(*) => {
-        match ty::method_call_bounds(cx.tcx, fcx.inh.method_map, ex.id) {
-          Some(bounds) => {
-            if has_trait_bounds(/*bad*/copy *bounds) {
+        match ty::method_call_type_param_defs(cx.tcx, fcx.inh.method_map, ex.id) {
+          Some(type_param_defs) => {
+            if has_trait_bounds(*type_param_defs) {
                 let callee_id = match ex.node {
                   ast::expr_field(_, _, _) => ex.id,
                   _ => ex.callee_id
@@ -530,7 +527,7 @@ pub fn early_resolve_expr(ex: @ast::expr,
                 let substs = fcx.node_ty_substs(callee_id);
                 let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() };
                 let vtbls = lookup_vtables(&vcx, &location_info_for_expr(ex),
-                                           bounds, &substs, is_early);
+                                           *type_param_defs, &substs, is_early);
                 if !is_early {
                     insert_vtables(fcx, callee_id, vtbls);
                 }
@@ -561,7 +558,11 @@ pub fn early_resolve_expr(ex: @ast::expr,
                           };
                           let target_trait_ref = ty::TraitRef {
                               def_id: target_def_id,
-                              substs: copy *target_substs
+                              substs: ty::substs {
+                                  tps: copy target_substs.tps,
+                                  self_r: target_substs.self_r,
+                                  self_ty: Some(mt.ty)
+                              }
                           };
                           let vtable_opt =
                               lookup_vtable(&vcx,
diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs
index 31301db2a0a..3af4cb7f10c 100644
--- a/src/librustc/middle/typeck/check/writeback.rs
+++ b/src/librustc/middle/typeck/check/writeback.rs
@@ -91,7 +91,7 @@ fn resolve_vtable_map_entry(fcx: @mut FnCtxt, sp: span, id: ast::node_id) {
             let vtable_map = fcx.ccx.vtable_map;
             vtable_map.insert(id, r_origins);
             debug!("writeback::resolve_vtable_map_entry(id=%d, vtables=%?)",
-                   id, r_origins.map(|v| v.to_str(fcx.tcx())));
+                   id, r_origins.repr(fcx.tcx()));
         }
     }
 
diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs
index b0e5ecee01e..174a20dd7f4 100644
--- a/src/librustc/middle/typeck/coherence.rs
+++ b/src/librustc/middle/typeck/coherence.rs
@@ -32,6 +32,7 @@ use middle::ty::{ty_rptr, ty_self, ty_struct, ty_trait, ty_tup};
 use middle::ty::{ty_type, ty_uint, ty_uniq, ty_bare_fn, ty_closure};
 use middle::ty::{ty_opaque_closure_ptr, ty_unboxed_vec};
 use middle::ty::{type_is_ty_var};
+use middle::subst::Subst;
 use middle::ty;
 use middle::typeck::CrateCtxt;
 use middle::typeck::infer::combine::Combine;
@@ -59,7 +60,7 @@ use core::uint;
 pub struct UniversalQuantificationResult {
     monotype: t,
     type_variables: ~[ty::t],
-    bounds: @~[param_bounds]
+    type_param_defs: @~[ty::TypeParameterDef]
 }
 
 pub fn get_base_type(inference_context: @mut InferCtxt,
@@ -269,17 +270,16 @@ pub impl CoherenceChecker {
         // We only want to generate one Impl structure. When we generate one,
         // we store it here so that we don't recreate it.
         let mut implementation_opt = None;
-        for associated_traits.each |associated_trait| {
-            let trait_did =
-                self.trait_ref_to_trait_def_id(*associated_trait);
-            debug!("(checking implementation) adding impl for trait \
-                    '%s', item '%s'",
-                    ast_map::node_id_to_str(
-                        self.crate_context.tcx.items, trait_did.node,
-                        self.crate_context.tcx.sess.parse_sess.interner),
-                    *self.crate_context.tcx.sess.str_of(item.ident));
-
-            self.instantiate_default_methods(item.id, trait_did);
+        for associated_traits.each |&associated_trait| {
+            let trait_ref =
+                ty::node_id_to_trait_ref(
+                    self.crate_context.tcx,
+                    associated_trait.ref_id);
+            debug!("(checking implementation) adding impl for trait '%s', item '%s'",
+                   trait_ref.repr(self.crate_context.tcx),
+                   *self.crate_context.tcx.sess.str_of(item.ident));
+
+            self.instantiate_default_methods(item.id, trait_ref);
 
             let implementation;
             if implementation_opt.is_none() {
@@ -287,7 +287,7 @@ pub impl CoherenceChecker {
                 implementation_opt = Some(implementation);
             }
 
-            self.add_trait_method(trait_did, implementation_opt.get());
+            self.add_trait_method(trait_ref.def_id, implementation_opt.get());
         }
 
         // Add the implementation to the mapping from implementation to base
@@ -325,22 +325,48 @@ pub impl CoherenceChecker {
     // Creates default method IDs and performs type substitutions for an impl
     // and trait pair. Then, for each provided method in the trait, inserts a
     // `ProvidedMethodInfo` instance into the `provided_method_sources` map.
-    fn instantiate_default_methods(&self, impl_id: ast::node_id,
-                                   trait_did: ast::def_id) {
-        for self.each_provided_trait_method(trait_did) |trait_method| {
+    fn instantiate_default_methods(&self,
+                                   impl_id: ast::node_id,
+                                   trait_ref: &ty::TraitRef) {
+        let tcx = self.crate_context.tcx;
+        debug!("instantiate_default_methods(impl_id=%?, trait_ref=%s)",
+               impl_id, trait_ref.repr(tcx));
+
+        let impl_poly_type = ty::lookup_item_type(tcx, local_def(impl_id));
+
+        for self.each_provided_trait_method(trait_ref.def_id) |trait_method| {
             // Synthesize an ID.
-            let tcx = self.crate_context.tcx;
             let new_id = parse::next_node_id(tcx.sess.parse_sess);
             let new_did = local_def(new_id);
 
-            let new_method_ty = @ty::method {
-                def_id: new_did,
-                ..copy *trait_method
+            debug!("new_did=%? trait_method=%s", new_did, trait_method.repr(tcx));
+
+            // Create substitutions for the various trait parameters.
+            let new_method_ty =
+                @subst_receiver_types_in_method_ty(
+                    tcx,
+                    impl_id,
+                    trait_ref,
+                    new_did,
+                    trait_method);
+
+            debug!("new_method_ty=%s", new_method_ty.repr(tcx));
+
+            // construct the polytype for the method based on the method_ty
+            let new_generics = ty::Generics {
+                type_param_defs:
+                    @vec::append(
+                        copy *impl_poly_type.generics.type_param_defs,
+                        *new_method_ty.generics.type_param_defs),
+                region_param:
+                    impl_poly_type.generics.region_param
+            };
+            let new_polytype = ty::ty_param_bounds_and_ty {
+                generics: new_generics,
+                ty: ty::mk_bare_fn(tcx, copy new_method_ty.fty)
             };
+            debug!("new_polytype=%s", new_polytype.repr(tcx));
 
-            // XXX: Perform substitutions.
-            let new_polytype = ty::lookup_item_type(tcx,
-                                                    trait_method.def_id);
             tcx.tcache.insert(new_did, new_polytype);
             tcx.methods.insert(new_did, new_method_ty);
 
@@ -358,7 +384,7 @@ pub impl CoherenceChecker {
                 @ProvidedMethodInfo {
                     method_info: @MethodInfo {
                         did: new_did,
-                        n_tps: trait_method.generics.bounds.len(),
+                        n_tps: trait_method.generics.type_param_defs.len(),
                         ident: trait_method.ident,
                         self_type: trait_method.self_ty
                     },
@@ -545,9 +571,8 @@ pub impl CoherenceChecker {
             polytype.generics.region_param.map(
                 |_r| self.inference_context.next_region_var_nb(dummy_sp()));
 
-        let bounds_count = polytype.generics.bounds.len();
-        let type_parameters =
-            self.inference_context.next_ty_vars(bounds_count);
+        let bounds_count = polytype.generics.type_param_defs.len();
+        let type_parameters = self.inference_context.next_ty_vars(bounds_count);
 
         let substitutions = substs {
             self_r: self_region,
@@ -565,7 +590,7 @@ pub impl CoherenceChecker {
         UniversalQuantificationResult {
             monotype: monotype,
             type_variables: type_parameters,
-            bounds: polytype.generics.bounds
+            type_param_defs: polytype.generics.type_param_defs
         }
     }
 
@@ -582,13 +607,13 @@ pub impl CoherenceChecker {
                 // Check to ensure that each parameter binding respected its
                 // kind bounds.
                 for [ a, b ].each |result| {
-                    for vec::each2(result.type_variables, *result.bounds)
-                            |ty_var, bounds| {
+                    for vec::each2(result.type_variables, *result.type_param_defs)
+                            |ty_var, type_param_def| {
                         match resolve_type(self.inference_context,
                                            *ty_var,
                                            resolve_nested_tvar) {
                             Ok(resolved_ty) => {
-                                for bounds.each |bound| {
+                                for type_param_def.bounds.each |bound| {
                                     match *bound {
                                         bound_copy => {
                                             if !ty::type_is_copyable(
@@ -914,7 +939,7 @@ pub impl CoherenceChecker {
                 @ProvidedMethodInfo {
                     method_info: @MethodInfo {
                         did: new_did,
-                        n_tps: trait_method_info.ty.generics.bounds.len(),
+                        n_tps: trait_method_info.ty.generics.type_param_defs.len(),
                         ident: trait_method_info.ty.ident,
                         self_type: trait_method_info.ty.self_ty
                     },
@@ -1010,6 +1035,70 @@ pub impl CoherenceChecker {
     }
 }
 
+fn subst_receiver_types_in_method_ty(
+    tcx: ty::ctxt,
+    impl_id: ast::node_id,
+    trait_ref: &ty::TraitRef,
+    new_def_id: ast::def_id,
+    method: &ty::method) -> ty::method
+{
+    /*!
+     * Substitutes the values for the receiver's type parameters
+     * that are found in method, leaving the method's type parameters
+     * intact.  This is in fact a mildly complex operation,
+     * largely because of the hokey way that we concatenate the
+     * receiver and method generics.
+     */
+
+    // determine how many type parameters were declared on the impl
+    let num_impl_type_parameters = {
+        let impl_polytype = ty::lookup_item_type(tcx, local_def(impl_id));
+        impl_polytype.generics.type_param_defs.len()
+    };
+
+    // determine how many type parameters appear on the trait
+    let num_trait_type_parameters = trait_ref.substs.tps.len();
+
+    // the current method type has the type parameters from the trait + method
+    let num_method_type_parameters =
+        num_trait_type_parameters + method.generics.type_param_defs.len();
+
+    // the new method type will have the type parameters from the impl + method
+    let combined_tps = vec::from_fn(num_method_type_parameters, |i| {
+        if i < num_trait_type_parameters {
+            // replace type parameters that come from trait with new value
+            trait_ref.substs.tps[i]
+        } else {
+            // replace type parameters that belong to method with another
+            // type parameter, this time with the index adjusted
+            let method_index = i - num_trait_type_parameters;
+            let type_param_def = &method.generics.type_param_defs[method_index];
+            let new_index = num_impl_type_parameters + method_index;
+            ty::mk_param(tcx, new_index, type_param_def.def_id)
+        }
+    });
+
+    let combined_substs = ty::substs {
+        self_r: trait_ref.substs.self_r,
+        self_ty: trait_ref.substs.self_ty,
+        tps: combined_tps
+    };
+
+    ty::method {
+        ident: method.ident,
+
+        // method tps cannot appear in the self_ty, so use `substs` from trait ref
+        transformed_self_ty: method.transformed_self_ty.subst(tcx, &trait_ref.substs),
+
+        // method types *can* appear in the generic bounds or the fty
+        generics: method.generics.subst(tcx, &combined_substs),
+        fty: method.fty.subst(tcx, &combined_substs),
+        self_ty: method.self_ty,
+        vis: method.vis,
+        def_id: new_def_id
+    }
+}
+
 pub fn check_coherence(crate_context: @mut CrateCtxt, crate: @crate) {
     let coherence_checker = @CoherenceChecker(crate_context);
     coherence_checker.check_coherence(crate);
diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs
index a9d54716cd8..a3296f6c209 100644
--- a/src/librustc/middle/typeck/collect.rs
+++ b/src/librustc/middle/typeck/collect.rs
@@ -35,6 +35,7 @@ use core::prelude::*;
 use metadata::csearch;
 use middle::ty::{substs, ty_param_bounds_and_ty};
 use middle::ty;
+use middle::subst::Subst;
 use middle::typeck::astconv::{AstConv, ty_of_arg};
 use middle::typeck::astconv::{ast_ty_to_ty};
 use middle::typeck::astconv;
@@ -186,7 +187,7 @@ pub fn get_enum_variant_types(ccx: &CrateCtxt,
 
             ast::struct_variant_kind(struct_def) => {
                 let tpt = ty_param_bounds_and_ty {
-                    generics: ty_generics(ccx, rp, generics),
+                    generics: ty_generics(ccx, rp, generics, 0),
                     ty: enum_ty
                 };
 
@@ -207,7 +208,7 @@ pub fn get_enum_variant_types(ccx: &CrateCtxt,
             None => {}
             Some(result_ty) => {
                 let tpt = ty_param_bounds_and_ty {
-                    generics: ty_generics(ccx, rp, generics),
+                    generics: ty_generics(ccx, rp, generics, 0),
                     ty: result_ty
                 };
                 tcx.tcache.insert(local_def(variant.node.id), tpt);
@@ -227,7 +228,7 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt,
             node: ast::item_trait(ref generics, _, ref ms),
             _
         }, _) => {
-            let trait_ty_generics = ty_generics(ccx, region_paramd, generics);
+            let trait_ty_generics = ty_generics(ccx, region_paramd, generics, 0);
 
             // For each method, construct a suitable ty::method and
             // store it into the `tcx.methods` table:
@@ -274,48 +275,99 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt,
                              trait_id: ast::node_id,
                              m: &ty::method,
                              trait_ty_generics: &ty::Generics) {
-        // We need to create a typaram that replaces self. This param goes
-        // *in between* the typarams from the trait and those from the
-        // method (since its bound can depend on the trait? or
-        // something like that).
+        // If declaration is
+        //
+        //     trait<A,B,C> {
+        //        fn foo<D,E,F>(...) -> Self;
+        //     }
+        //
+        // and we will create a function like
+        //
+        //     fn foo<A',B',C',D',E',F',G'>(...) -> D' {}
+        //
+        // Note that `Self` is replaced with an explicit type
+        // parameter D' that is sandwiched in between the trait params
+        // and the method params, and thus the indices of the method
+        // type parameters are offset by 1 (that is, the method
+        // parameters are mapped from D, E, F to E', F', and G').  The
+        // choice of this ordering is somewhat arbitrary.
+        //
+        // Also, this system is rather a hack that should be replaced
+        // with a more uniform treatment of Self (which is partly
+        // underway).
 
         // build up a subst that shifts all of the parameters over
         // by one and substitute in a new type param for self
 
+        let tcx = ccx.tcx;
+
         let dummy_defid = ast::def_id {crate: 0, node: 0};
 
-        let num_trait_bounds = trait_ty_generics.bounds.len();
+        // Represents [A',B',C']
+        let num_trait_bounds = trait_ty_generics.type_param_defs.len();
         let non_shifted_trait_tps = do vec::from_fn(num_trait_bounds) |i| {
-            ty::mk_param(ccx.tcx, i, dummy_defid)
+            ty::mk_param(tcx, i, dummy_defid)
         };
-        let self_param = ty::mk_param(ccx.tcx, num_trait_bounds,
+
+        // Represents [D']
+        let self_param = ty::mk_param(tcx, num_trait_bounds,
                                       dummy_defid);
-        let shifted_method_tps = do vec::from_fn(m.generics.bounds.len()) |i| {
-            ty::mk_param(ccx.tcx, i + 1, dummy_defid)
+
+        // Represents [E',F',G']
+        let num_method_bounds = m.generics.type_param_defs.len();
+        let shifted_method_tps = do vec::from_fn(num_method_bounds) |i| {
+            ty::mk_param(tcx, i + 1, dummy_defid)
         };
 
+        // build up the substitution from
+        //     A,B,C => A',B',C'
+        //     Self => D'
+        //     D,E,F => E',F',G'
         let substs = substs {
             self_r: None,
             self_ty: Some(self_param),
             tps: non_shifted_trait_tps + shifted_method_tps
         };
-        let ty = ty::subst(ccx.tcx,
+
+        // create the type of `foo`, applying the substitution above
+        let ty = ty::subst(tcx,
                            &substs,
-                           ty::mk_bare_fn(ccx.tcx, copy m.fty));
-        let trait_def = get_trait_def(ccx, local_def(trait_id));
-        let trait_ref = trait_def.trait_ref;
-        let mut new_bounds = ~[];
-        new_bounds.push_all(*trait_ty_generics.bounds);
-        new_bounds.push(@~[ty::bound_trait(trait_ref)]);
-        new_bounds.push_all(*m.generics.bounds);
-        ccx.tcx.tcache.insert(m.def_id,
-                              ty_param_bounds_and_ty {
-                                  generics: ty::Generics {
-                                      bounds: @new_bounds,
-                                      region_param: trait_ty_generics.region_param
-                                  },
-                                  ty: ty
-                              });
+                           ty::mk_bare_fn(tcx, copy m.fty));
+
+        // create the type parameter definitions for `foo`, applying
+        // the substitution to any traits that appear in their bounds.
+
+        // add in the type parameters from the trait
+        let mut new_type_param_defs = ~[];
+        let substd_type_param_defs =
+            trait_ty_generics.type_param_defs.subst(tcx, &substs);
+        new_type_param_defs.push_all(*substd_type_param_defs);
+
+        // add in the "self" type parameter
+        let self_trait_def = get_trait_def(ccx, local_def(trait_id));
+        let self_trait_ref = @self_trait_def.trait_ref.subst(tcx, &substs);
+        new_type_param_defs.push(ty::TypeParameterDef {
+            def_id: dummy_defid,
+            bounds: @~[ty::bound_trait(self_trait_ref)]
+        });
+
+        // add in the type parameters from the method
+        let substd_type_param_defs = m.generics.type_param_defs.subst(tcx, &substs);
+        new_type_param_defs.push_all(*substd_type_param_defs);
+
+        debug!("static method %s type_param_defs=%s substs=%s",
+               m.def_id.repr(tcx),
+               new_type_param_defs.repr(tcx),
+               substs.repr(tcx));
+
+        tcx.tcache.insert(m.def_id,
+                          ty_param_bounds_and_ty {
+                              generics: ty::Generics {
+                                  type_param_defs: @new_type_param_defs,
+                                  region_param: trait_ty_generics.region_param
+                              },
+                              ty: ty
+                          });
     }
 
     fn ty_method_of_trait_method(self: &CrateCtxt,
@@ -334,9 +386,10 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt,
         let (transformed_self_ty, fty) =
             astconv::ty_of_method(self, &rscope, *m_purity, &m_generics.lifetimes,
                                   trait_self_ty, *m_self_ty, m_decl);
+        let num_trait_type_params = trait_generics.ty_params.len();
         ty::method {
             ident: *m_ident,
-            generics: ty_generics(self, None, m_generics),
+            generics: ty_generics(self, None, m_generics, num_trait_type_params),
             transformed_self_ty: transformed_self_ty,
             fty: fty,
             self_ty: m_self_ty.node,
@@ -357,9 +410,11 @@ pub fn ensure_supertraits(ccx: &CrateCtxt,
     let tcx = ccx.tcx;
     if tcx.supertraits.contains_key(&local_def(id)) { return; }
 
+    let self_ty = ty::mk_self(ccx.tcx, local_def(id));
     let mut ty_trait_refs: ~[@ty::TraitRef] = ~[];
     for ast_trait_refs.each |&ast_trait_ref| {
-        let trait_ref = instantiate_trait_ref(ccx, ast_trait_ref, rp, generics);
+        let trait_ref = instantiate_trait_ref(ccx, ast_trait_ref, rp,
+                                              generics, self_ty);
 
         // FIXME(#5527) Could have same trait multiple times
         if ty_trait_refs.any(|other_trait| other_trait.def_id == trait_ref.def_id) {
@@ -426,15 +481,18 @@ pub fn compare_impl_method(tcx: ty::ctxt,
         }
     }
 
-    if impl_m.generics.bounds.len() != trait_m.generics.bounds.len() {
+    let num_impl_m_type_params = impl_m.generics.type_param_defs.len();
+    let num_trait_m_type_params = trait_m.generics.type_param_defs.len();
+    if num_impl_m_type_params != num_trait_m_type_params {
         tcx.sess.span_err(
             cm.span,
             fmt!("method `%s` has %u type %s, but its trait \
                   declaration has %u type %s",
-                 *tcx.sess.str_of(trait_m.ident), impl_m.generics.bounds.len(),
-                 pluralize(impl_m.generics.bounds.len(), ~"parameter"),
-                 trait_m.generics.bounds.len(),
-                 pluralize(trait_m.generics.bounds.len(), ~"parameter")));
+                 *tcx.sess.str_of(trait_m.ident),
+                 num_impl_m_type_params,
+                 pluralize(num_impl_m_type_params, ~"parameter"),
+                 num_trait_m_type_params,
+                 pluralize(num_trait_m_type_params, ~"parameter")));
         return;
     }
 
@@ -452,23 +510,23 @@ pub fn compare_impl_method(tcx: ty::ctxt,
     // FIXME(#2687)---we should be checking that the bounds of the
     // trait imply the bounds of the subtype, but it appears
     // we are...not checking this.
-    for trait_m.generics.bounds.eachi() |i, trait_param_bounds| {
+    for trait_m.generics.type_param_defs.eachi |i, trait_param_def| {
         // For each of the corresponding impl ty param's bounds...
-        let impl_param_bounds = impl_m.generics.bounds[i];
+        let impl_param_def = &impl_m.generics.type_param_defs[i];
         // Make sure the bounds lists have the same length
         // Would be nice to use the ty param names in the error message,
         // but we don't have easy access to them here
-        if impl_param_bounds.len() != trait_param_bounds.len() {
+        if impl_param_def.bounds.len() != trait_param_def.bounds.len() {
            tcx.sess.span_err(
                cm.span,
                fmt!("in method `%s`, \
                      type parameter %u has %u %s, but the same type \
                      parameter in its trait declaration has %u %s",
                     *tcx.sess.str_of(trait_m.ident),
-                    i, impl_param_bounds.len(),
-                    pluralize(impl_param_bounds.len(), ~"bound"),
-                    trait_param_bounds.len(),
-                    pluralize(trait_param_bounds.len(), ~"bound")));
+                    i, impl_param_def.bounds.len(),
+                    pluralize(impl_param_def.bounds.len(), ~"bound"),
+                    trait_param_def.bounds.len(),
+                    pluralize(trait_param_def.bounds.len(), ~"bound")));
            return;
         }
     }
@@ -492,12 +550,12 @@ pub fn compare_impl_method(tcx: ty::ctxt,
         debug!("impl_fty (pre-subst): %s", ppaux::ty_to_str(tcx, impl_fty));
         replace_bound_self(tcx, impl_fty, dummy_self_r)
     };
-    debug!("impl_fty: %s", ppaux::ty_to_str(tcx, impl_fty));
+    debug!("impl_fty (post-subst): %s", ppaux::ty_to_str(tcx, impl_fty));
     let trait_fty = {
-        let dummy_tps = do vec::from_fn(trait_m.generics.bounds.len()) |i| {
-            // hack: we don't know the def id of the impl tp, but it
-            // is not important for unification
-            ty::mk_param(tcx, i + impl_tps, ast::def_id {crate: 0, node: 0})
+        let num_trait_m_type_params = trait_m.generics.type_param_defs.len();
+        let dummy_tps = do vec::from_fn(num_trait_m_type_params) |i| {
+            ty::mk_param(tcx, i + impl_tps,
+                         impl_m.generics.type_param_defs[i].def_id)
         };
         let trait_tps = trait_substs.tps.map(
             |t| replace_bound_self(tcx, *t, dummy_self_r));
@@ -507,9 +565,11 @@ pub fn compare_impl_method(tcx: ty::ctxt,
             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", ppaux::ty_to_str(tcx, trait_fty));
+        debug!("trait_fty (pre-subst): %s substs=%s",
+               trait_fty.repr(tcx), substs.repr(tcx));
         ty::subst(tcx, &substs, trait_fty)
     };
+    debug!("trait_fty (post-subst): %s", trait_fty.repr(tcx));
 
     let infcx = infer::new_infer_ctxt(tcx);
     match infer::mk_subty(infcx, false, cm.span, impl_fty, trait_fty) {
@@ -542,7 +602,8 @@ pub fn check_methods_against_trait(ccx: &CrateCtxt,
                                    impl_ms: &[ConvertedMethod])
 {
     let tcx = ccx.tcx;
-    let trait_ref = instantiate_trait_ref(ccx, a_trait_ty, rp, generics);
+    let trait_ref = instantiate_trait_ref(ccx, a_trait_ty, rp,
+                                          generics, selfty);
 
     if trait_ref.def_id.crate == ast::local_crate {
         ensure_trait_methods(ccx, trait_ref.def_id.node);
@@ -574,7 +635,7 @@ pub fn check_methods_against_trait(ccx: &CrateCtxt,
 
 pub fn convert_field(ccx: &CrateCtxt,
                      rp: Option<ty::region_variance>,
-                     bounds: @~[ty::param_bounds],
+                     type_param_defs: @~[ty::TypeParameterDef],
                      v: @ast::struct_field,
                      generics: &ast::Generics) {
     let region_parameterization =
@@ -585,7 +646,7 @@ pub fn convert_field(ccx: &CrateCtxt,
     ccx.tcx.tcache.insert(local_def(v.node.id),
                           ty::ty_param_bounds_and_ty {
                               generics: ty::Generics {
-                                  bounds: bounds,
+                                  type_param_defs: type_param_defs,
                                   region_param: rp
                               },
                               ty: tt
@@ -609,8 +670,10 @@ pub fn convert_methods(ccx: &CrateCtxt,
 {
     let tcx = ccx.tcx;
     return vec::map(ms, |m| {
+        let num_rcvr_ty_params = rcvr_ty_generics.type_param_defs.len();
         let m_ty_generics =
-            ty_generics(ccx, rcvr_ty_generics.region_param, &m.generics);
+            ty_generics(ccx, rcvr_ty_generics.region_param, &m.generics,
+                        num_rcvr_ty_params);
         let mty =
             @ty_of_method(ccx, *m, rcvr_ty_generics.region_param,
                           untransformed_rcvr_ty,
@@ -625,8 +688,9 @@ pub fn convert_methods(ccx: &CrateCtxt,
             // the tps on the receiver and those on the method itself
             ty_param_bounds_and_ty {
                 generics: ty::Generics {
-                    bounds: @(vec::append(copy *rcvr_ty_generics.bounds,
-                                          *m_ty_generics.bounds)),
+                    type_param_defs: @vec::append(
+                        copy *rcvr_ty_generics.type_param_defs,
+                        *m_ty_generics.type_param_defs),
                     region_param: rcvr_ty_generics.region_param
                 },
                 ty: fty
@@ -660,9 +724,10 @@ pub fn convert_methods(ccx: &CrateCtxt,
         // foo(); }`).
         let method_vis = m.vis.inherit_from(rcvr_visibility);
 
+        let num_rcvr_type_params = rcvr_generics.ty_params.len();
         ty::method {
             ident: m.ident,
-            generics: ty_generics(ccx, None, &m.generics),
+            generics: ty_generics(ccx, None, &m.generics, num_rcvr_type_params),
             transformed_self_ty: transformed_self_ty,
             fty: fty,
             self_ty: m.self_ty.node,
@@ -705,7 +770,7 @@ pub fn convert(ccx: &CrateCtxt, it: @ast::item) {
                                rp);
       }
       ast::item_impl(ref generics, opt_trait_ref, selfty, ref ms) => {
-        let i_ty_generics = ty_generics(ccx, rp, generics);
+        let i_ty_generics = ty_generics(ccx, rp, generics, 0);
         let region_parameterization =
             RegionParameterization::from_variance_and_generics(rp, generics);
         let selfty = ccx.to_ty(&type_rscope(region_parameterization), selfty);
@@ -741,8 +806,9 @@ pub fn convert(ccx: &CrateCtxt, it: @ast::item) {
 
           let (_, provided_methods) =
               split_trait_methods(*trait_methods);
-          let (ty_generics, _) = mk_substs(ccx, generics, rp);
           let untransformed_rcvr_ty = ty::mk_self(tcx, local_def(it.id));
+          let (ty_generics, _) = mk_item_substs(ccx, generics, rp,
+                                                Some(untransformed_rcvr_ty));
           let _ = convert_methods(ccx, provided_methods,
                                   untransformed_rcvr_ty,
                                   &ty_generics, generics,
@@ -799,7 +865,7 @@ pub fn convert_struct(ccx: &CrateCtxt,
         tcx.tcache.insert(local_def(dtor.node.id),
                           ty_param_bounds_and_ty {
                               generics: ty::Generics {
-                                  bounds: tpt.generics.bounds,
+                                  type_param_defs: tpt.generics.type_param_defs,
                                   region_param: rp
                               },
                               ty: t_dtor});
@@ -807,9 +873,9 @@ pub fn convert_struct(ccx: &CrateCtxt,
 
     // Write the type of each of the members
     for struct_def.fields.each |f| {
-       convert_field(ccx, rp, tpt.generics.bounds, *f, generics);
+       convert_field(ccx, rp, tpt.generics.type_param_defs, *f, generics);
     }
-    let (_, substs) = mk_substs(ccx, generics, rp);
+    let (_, substs) = mk_item_substs(ccx, generics, rp, None);
     let selfty = ty::mk_struct(tcx, local_def(id), substs);
 
     // If this struct is enum-like or tuple-like, create the type of its
@@ -850,7 +916,8 @@ pub fn convert_foreign(ccx: &CrateCtxt, i: @ast::foreign_item) {
 pub fn instantiate_trait_ref(ccx: &CrateCtxt,
                              ast_trait_ref: @ast::trait_ref,
                              rp: Option<ty::region_variance>,
-                             generics: &ast::Generics) -> @ty::TraitRef
+                             generics: &ast::Generics,
+                             self_ty: ty::t) -> @ty::TraitRef
 {
     /*!
      * Instantiates the path for the given trait reference, assuming that
@@ -866,7 +933,7 @@ pub fn instantiate_trait_ref(ccx: &CrateCtxt,
         ast::def_trait(trait_did) => {
             let trait_ref =
                 astconv::ast_path_to_trait_ref(
-                    ccx, &rscope, trait_did, ast_trait_ref.path);
+                    ccx, &rscope, trait_did, Some(self_ty), ast_trait_ref.path);
             ccx.tcx.trait_refs.insert(
                 ast_trait_ref.ref_id, trait_ref);
             return trait_ref;
@@ -903,7 +970,9 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: @ast::item) -> @ty::TraitDef {
     let rp = tcx.region_paramd_items.find(&it.id).map_consume(|x| *x);
     match it.node {
         ast::item_trait(ref generics, _, _) => {
-            let (ty_generics, substs) = mk_substs(ccx, generics, rp);
+            let self_ty = ty::mk_self(tcx, def_id);
+            let (ty_generics, substs) = mk_item_substs(ccx, generics, rp,
+                                                       Some(self_ty));
             let trait_ref = @ty::TraitRef {def_id: def_id,
                                            substs: substs};
             let trait_def = @ty::TraitDef {generics: ty_generics,
@@ -937,7 +1006,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: @ast::item)
       }
       ast::item_fn(ref decl, purity, _, ref generics, _) => {
         assert!(rp.is_none());
-        let ty_generics = ty_generics(ccx, None, generics);
+        let ty_generics = ty_generics(ccx, None, generics, 0);
         let tofd = astconv::ty_of_bare_fn(ccx,
                                           &empty_rscope,
                                           purity,
@@ -946,7 +1015,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: @ast::item)
                                           decl);
         let tpt = ty_param_bounds_and_ty {
             generics: ty::Generics {
-                bounds: ty_generics.bounds,
+                type_param_defs: ty_generics.type_param_defs,
                 region_param: None
             },
             ty: ty::mk_bare_fn(ccx.tcx, tofd)
@@ -979,7 +1048,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: @ast::item)
                 }
             };
             ty_param_bounds_and_ty {
-                generics: ty_generics(ccx, rp, generics),
+                generics: ty_generics(ccx, rp, generics, 0),
                 ty: ty
             }
         };
@@ -989,7 +1058,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: @ast::item)
       }
       ast::item_enum(_, ref generics) => {
         // Create a new generic polytype.
-        let (ty_generics, substs) = mk_substs(ccx, generics, rp);
+        let (ty_generics, substs) = mk_item_substs(ccx, generics, rp, None);
         let t = ty::mk_enum(tcx, local_def(it.id), substs);
         let tpt = ty_param_bounds_and_ty {
             generics: ty_generics,
@@ -1004,7 +1073,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: @ast::item)
               fmt!("Invoked ty_of_item on trait"));
       }
       ast::item_struct(_, ref generics) => {
-          let (ty_generics, substs) = mk_substs(ccx, generics, rp);
+          let (ty_generics, substs) = mk_item_substs(ccx, generics, rp, None);
           let t = ty::mk_struct(tcx, local_def(it.id), substs);
           let tpt = ty_param_bounds_and_ty {
               generics: ty_generics,
@@ -1031,7 +1100,7 @@ pub fn ty_of_foreign_item(ccx: &CrateCtxt, it: @ast::foreign_item)
         ast::foreign_item_const(t) => {
             ty::ty_param_bounds_and_ty {
                 generics: ty::Generics {
-                    bounds: @~[],
+                    type_param_defs: @~[],
                     region_param: None,
                 },
                 ty: ast_ty_to_ty(ccx, &empty_rscope, t)
@@ -1042,16 +1111,25 @@ pub fn ty_of_foreign_item(ccx: &CrateCtxt, it: @ast::foreign_item)
 
 pub fn ty_generics(ccx: &CrateCtxt,
                    rp: Option<ty::region_variance>,
-                   generics: &ast::Generics) -> ty::Generics {
+                   generics: &ast::Generics,
+                   base_index: uint) -> ty::Generics {
     return ty::Generics {
         region_param: rp,
-        bounds: @generics.ty_params.map_to_vec(|param| {
-            match ccx.tcx.ty_param_bounds.find(&param.id) {
-                Some(&bs) => bs,
+        type_param_defs: @generics.ty_params.mapi_to_vec(|offset, param| {
+            match ccx.tcx.ty_param_defs.find(&param.id) {
+                Some(&def) => def,
                 None => {
-                    let bounds = compute_bounds(ccx, rp, generics, param.bounds);
-                    ccx.tcx.ty_param_bounds.insert(param.id, bounds);
-                    bounds
+                    let param_ty = ty::param_ty {idx: base_index + offset,
+                                                 def_id: local_def(param.id)};
+                    let bounds = compute_bounds(ccx, rp, generics,
+                                                param_ty, param.bounds);
+                    let def = ty::TypeParameterDef {
+                        def_id: local_def(param.id),
+                        bounds: bounds
+                    };
+                    debug!("def for param: %s", def.repr(ccx.tcx));
+                    ccx.tcx.ty_param_defs.insert(param.id, def);
+                    def
                 }
             }
         })
@@ -1061,6 +1139,7 @@ pub fn ty_generics(ccx: &CrateCtxt,
         ccx: &CrateCtxt,
         rp: Option<ty::region_variance>,
         generics: &ast::Generics,
+        param_ty: ty::param_ty,
         ast_bounds: @OptVec<ast::TyParamBound>) -> ty::param_bounds
     {
         /*!
@@ -1076,7 +1155,8 @@ pub fn ty_generics(ccx: &CrateCtxt,
             match b {
                 &TraitTyParamBound(b) => {
                     let li = &ccx.tcx.lang_items;
-                    let trait_ref = instantiate_trait_ref(ccx, b, rp, generics);
+                    let ty = ty::mk_param(ccx.tcx, param_ty.idx, param_ty.def_id);
+                    let trait_ref = instantiate_trait_ref(ccx, b, rp, generics, ty);
                     if trait_ref.def_id == li.owned_trait() {
                         ~[ty::bound_owned]
                     } else if trait_ref.def_id == li.copy_trait() {
@@ -1104,7 +1184,7 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
                              def_id: ast::def_id,
                              ast_generics: &ast::Generics)
                           -> ty::ty_param_bounds_and_ty {
-    let ty_generics = ty_generics(ccx, None, ast_generics);
+    let ty_generics = ty_generics(ccx, None, ast_generics, 0);
     let region_param_names = RegionParamNames::from_generics(ast_generics);
     let rb = in_binding_rscope(&empty_rscope, region_param_names);
     let input_tys = decl.inputs.map(|a| ty_of_arg(ccx, &rb, *a, None) );
@@ -1127,17 +1207,18 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
     return tpt;
 }
 
-pub fn mk_substs(ccx: &CrateCtxt,
-                 ast_generics: &ast::Generics,
-                 rp: Option<ty::region_variance>) -> (ty::Generics, ty::substs)
+pub fn mk_item_substs(ccx: &CrateCtxt,
+                      ast_generics: &ast::Generics,
+                      rp: Option<ty::region_variance>,
+                      self_ty: Option<ty::t>) -> (ty::Generics, ty::substs)
 {
     let mut i = 0;
-    let ty_generics = ty_generics(ccx, rp, ast_generics);
+    let ty_generics = ty_generics(ccx, rp, ast_generics, 0);
     let params = ast_generics.ty_params.map_to_vec(|atp| {
         let t = ty::mk_param(ccx.tcx, i, local_def(atp.id));
         i += 1u;
         t
     });
     let self_r = rscope::bound_self_region(rp);
-    (ty_generics, substs {self_r: self_r, self_ty: None, tps: params})
+    (ty_generics, substs {self_r: self_r, self_ty: self_ty, tps: params})
 }
diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs
index 69c7b1d616b..be1d291cfe5 100644
--- a/src/librustc/middle/typeck/infer/combine.rs
+++ b/src/librustc/middle/typeck/infer/combine.rs
@@ -274,15 +274,14 @@ pub fn super_tps<C:Combine>(
 pub fn super_self_tys<C:Combine>(
     self: &C, a: Option<ty::t>, b: Option<ty::t>) -> cres<Option<ty::t>> {
 
-    // Note: the self type parameter is (currently) always treated as
-    // *invariant* (otherwise the type system would be unsound).
-
     match (a, b) {
       (None, None) => {
         Ok(None)
       }
       (Some(a), Some(b)) => {
-        eq_tys(self, a, b).then(|| Ok(Some(a)) )
+          // FIXME(#5781) this should be eq_tys
+          // eq_tys(self, a, b).then(|| Ok(Some(a)) )
+          self.contratys(a, b).chain(|t| Ok(Some(t)))
       }
       (None, Some(_)) |
       (Some(_), None) => {
diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs
index d3502adb33a..fdb8049d79b 100644
--- a/src/librustc/middle/typeck/mod.rs
+++ b/src/librustc/middle/typeck/mod.rs
@@ -53,6 +53,7 @@ use core::prelude::*;
 use middle::resolve;
 use middle::ty;
 use util::common::time;
+use util::ppaux::Repr;
 use util::ppaux;
 
 use core::hashmap::HashMap;
@@ -153,14 +154,15 @@ pub enum vtable_origin {
     vtable_param(uint, uint)
 }
 
-pub impl vtable_origin {
-    fn to_str(&self, tcx: ty::ctxt) -> ~str {
+impl Repr for vtable_origin {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
         match *self {
             vtable_static(def_id, ref tys, ref vtable_res) => {
-                fmt!("vtable_static(%?:%s, %?, %?)",
-                     def_id, ty::item_path_str(tcx, def_id),
-                     tys,
-                     vtable_res.map(|o| o.to_str(tcx)))
+                fmt!("vtable_static(%?:%s, %s, %s)",
+                     def_id,
+                     ty::item_path_str(tcx, def_id),
+                     tys.repr(tcx),
+                     vtable_res.repr(tcx))
             }
 
             vtable_param(x, y) => {
@@ -222,7 +224,7 @@ pub fn lookup_def_ccx(ccx: @mut CrateCtxt, sp: span, id: ast::node_id)
 
 pub fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
     ty::ty_param_bounds_and_ty {
-        generics: ty::Generics {bounds: @~[],
+        generics: ty::Generics {type_param_defs: @~[],
                                 region_param: None},
         ty: t
     }
diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc
index 3a974fcdaae..94fcb85d1d8 100644
--- a/src/librustc/rustc.rc
+++ b/src/librustc/rustc.rc
@@ -83,6 +83,7 @@ pub mod middle {
         pub mod asm;
     }
     pub mod ty;
+    pub mod subst;
     pub mod resolve;
     #[path = "typeck/mod.rs"]
     pub mod typeck;
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 3b3c42d5d8b..f26eeeca446 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -11,6 +11,7 @@
 use core::prelude::*;
 
 use middle::ty;
+use middle::typeck;
 use middle::ty::canon_mode;
 use middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid,
                  br_fresh};
@@ -35,6 +36,10 @@ use syntax::abi::AbiSet;
 use core::str;
 use core::vec;
 
+pub trait Repr {
+    fn repr(&self, tcx: ctxt) -> ~str;
+}
+
 pub fn note_and_explain_region(cx: ctxt,
                                prefix: ~str,
                                region: ty::Region,
@@ -257,18 +262,6 @@ pub fn vstore_ty_to_str(cx: ctxt, ty: ~str, vs: ty::vstore) -> ~str {
     }
 }
 
-pub fn expr_repr(cx: ctxt, expr: @ast::expr) -> ~str {
-    fmt!("expr(%d: %s)",
-         expr.id,
-         pprust::expr_to_str(expr, cx.sess.intr()))
-}
-
-pub fn pat_repr(cx: ctxt, pat: @ast::pat) -> ~str {
-    fmt!("pat(%d: %s)",
-         pat.id,
-         pprust::pat_to_str(pat, cx.sess.intr()))
-}
-
 pub fn tys_to_str(cx: ctxt, ts: &[t]) -> ~str {
     let tstrs = ts.map(|t| ty_to_str(cx, *t));
     fmt!("(%s)", str::connect(tstrs, ", "))
@@ -287,7 +280,13 @@ pub fn fn_sig_to_str(cx: ctxt, typ: &ty::FnSig) -> ~str {
 pub fn trait_ref_to_str(cx: ctxt, trait_ref: &ty::TraitRef) -> ~str {
     let path = ty::item_path(cx, trait_ref.def_id);
     let base = ast_map::path_to_str(path, cx.sess.intr());
-    parameterized(cx, base, trait_ref.substs.self_r, trait_ref.substs.tps)
+    if cx.sess.verbose() && trait_ref.substs.self_ty.is_some() {
+        let mut all_tps = copy trait_ref.substs.tps;
+        for trait_ref.substs.self_ty.each |&t| { all_tps.push(t); }
+        parameterized(cx, base, trait_ref.substs.self_r, all_tps)
+    } else {
+        parameterized(cx, base, trait_ref.substs.self_r, trait_ref.substs.tps)
+    }
 }
 
 pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
@@ -497,10 +496,261 @@ pub fn ty_to_short_str(cx: ctxt, typ: t) -> ~str {
     return s;
 }
 
+impl<T:Repr> Repr for Option<T> {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        match self {
+            &None => ~"None",
+            &Some(ref t) => fmt!("Some(%s)", t.repr(tcx))
+        }
+    }
+}
+
+/*
+Annoyingly, these conflict with @ast::expr.
+
+impl<T:Repr> Repr for @T {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        (&**self).repr(tcx)
+    }
+}
+
+impl<T:Repr> Repr for ~T {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        (&**self).repr(tcx)
+    }
+}
+*/
+
+fn repr_vec<T:Repr>(tcx: ctxt, v: &[T]) -> ~str {
+    fmt!("[%s]", str::connect(v.map(|t| t.repr(tcx)), ","))
+}
+
+impl<'self, T:Repr> Repr for &'self [T] {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        repr_vec(tcx, *self)
+    }
+}
+
+// This is necessary to handle types like Option<@~[T]>, for which
+// autoderef cannot convert the &[T] handler
+impl<T:Repr> Repr for @~[T] {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        repr_vec(tcx, **self)
+    }
+}
+
+impl Repr for ty::TypeParameterDef {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("TypeParameterDef {%?, bounds: %s}",
+             self.def_id, self.bounds.repr(tcx))
+    }
+}
+
+impl Repr for ty::t {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        ty_to_str(tcx, *self)
+    }
+}
+
+impl Repr for ty::substs {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("substs(self_r=%s, self_ty=%s, tps=%s)",
+             self.self_r.repr(tcx),
+             self.self_ty.repr(tcx),
+             self.tps.repr(tcx))
+    }
+}
+
+impl Repr for ty::param_bound {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        match *self {
+            ty::bound_copy => ~"copy",
+            ty::bound_durable => ~"'static",
+            ty::bound_owned => ~"owned",
+            ty::bound_const => ~"const",
+            ty::bound_trait(ref t) => t.repr(tcx)
+        }
+    }
+}
+
+impl Repr for ty::TraitRef {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        trait_ref_to_str(tcx, self)
+    }
+}
+
+impl Repr for @ast::expr {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("expr(%d: %s)",
+             self.id,
+             pprust::expr_to_str(*self, tcx.sess.intr()))
+    }
+}
+
+impl Repr for @ast::pat {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("pat(%d: %s)",
+             self.id,
+             pprust::pat_to_str(*self, tcx.sess.intr()))
+    }
+}
+
+impl Repr for ty::Region {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        region_to_str(tcx, *self)
+    }
+}
+
+impl Repr for ast::def_id {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        // Unfortunately, there seems to be no way to attempt to print
+        // a path for a def-id, so I'll just make a best effort for now
+        // and otherwise fallback to just printing the crate/node pair
+        if self.crate == ast::local_crate {
+            match tcx.items.find(&self.node) {
+                Some(&ast_map::node_item(*)) |
+                Some(&ast_map::node_foreign_item(*)) |
+                Some(&ast_map::node_method(*)) |
+                Some(&ast_map::node_trait_method(*)) |
+                Some(&ast_map::node_variant(*)) |
+                Some(&ast_map::node_struct_ctor(*)) => {
+                    return fmt!("%?:%s", *self, ty::item_path_str(tcx, *self));
+                }
+                _ => {}
+            }
+        }
+        return fmt!("%?", *self);
+    }
+}
+
+impl Repr for ty::ty_param_bounds_and_ty {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("ty_param_bounds_and_ty {generics: %s, ty: %s}",
+             self.generics.repr(tcx),
+             self.ty.repr(tcx))
+    }
+}
+
+impl Repr for ty::Generics {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("Generics {type_param_defs: %s, region_param: %?}",
+             self.type_param_defs.repr(tcx),
+             self.region_param)
+    }
+}
+
+impl Repr for ty::method {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("method {ident: %s, generics: %s, transformed_self_ty: %s, \
+              fty: %s, self_ty: %s, vis: %s, def_id: %s}",
+             self.ident.repr(tcx),
+             self.generics.repr(tcx),
+             self.transformed_self_ty.repr(tcx),
+             self.fty.repr(tcx),
+             self.self_ty.repr(tcx),
+             self.vis.repr(tcx),
+             self.def_id.repr(tcx))
+    }
+}
+
+impl Repr for ast::ident {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        copy *tcx.sess.intr().get(*self)
+    }
+}
+
+impl Repr for ast::self_ty_ {
+    fn repr(&self, _tcx: ctxt) -> ~str {
+        fmt!("%?", *self)
+    }
+}
+
+impl Repr for ast::visibility {
+    fn repr(&self, _tcx: ctxt) -> ~str {
+        fmt!("%?", *self)
+    }
+}
+
+impl Repr for ty::BareFnTy {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("BareFnTy {purity: %?, abis: %s, sig: %s}",
+             self.purity,
+             self.abis.to_str(),
+             self.sig.repr(tcx))
+    }
+}
+
+impl Repr for ty::FnSig {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fn_sig_to_str(tcx, self)
+    }
+}
+
+impl Repr for typeck::method_map_entry {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("method_map_entry {self_arg: %s, \
+              explicit_self: %s, \
+              origin: %s}",
+             self.self_arg.repr(tcx),
+             self.explicit_self.repr(tcx),
+             self.origin.repr(tcx))
+    }
+}
+
+impl Repr for ty::arg {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("%?(%s)", self.mode, self.ty.repr(tcx))
+    }
+}
+
+impl Repr for typeck::method_origin {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        match self {
+            &typeck::method_super(def_id, n) => {
+                fmt!("method_super(%s, %?)",
+                     def_id.repr(tcx), n)
+            }
+            &typeck::method_static(def_id) => {
+                fmt!("method_static(%s)", def_id.repr(tcx))
+            }
+            &typeck::method_param(ref p) => {
+                p.repr(tcx)
+            }
+            &typeck::method_trait(def_id, n, st) => {
+                fmt!("method_trait(%s, %?, %s)", def_id.repr(tcx), n,
+                     st.repr(tcx))
+            }
+            &typeck::method_self(def_id, n) => {
+                fmt!("method_self(%s, %?)", def_id.repr(tcx), n)
+            }
+        }
+    }
+}
+
+impl Repr for typeck::method_param {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        fmt!("method_param(%s,%?,%?,%?)",
+             self.trait_id.repr(tcx),
+             self.method_num,
+             self.param_num,
+             self.bound_num)
+    }
+}
+
+impl Repr for ty::TraitStore {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        match self {
+            &ty::BoxTraitStore => ~"@Trait",
+            &ty::UniqTraitStore => ~"~Trait",
+            &ty::RegionTraitStore(r) => fmt!("&%s Trait", r.repr(tcx))
+        }
+    }
+}
+
 // Local Variables:
 // mode: rust
 // fill-column: 78;
 // indent-tabs-mode: nil
 // c-basic-offset: 4
 // buffer-file-coding-system: utf-8-unix
-// End:
+// End
diff --git a/src/libsyntax/opt_vec.rs b/src/libsyntax/opt_vec.rs
index 67c3d19dd0f..fd54746f3dc 100644
--- a/src/libsyntax/opt_vec.rs
+++ b/src/libsyntax/opt_vec.rs
@@ -102,6 +102,16 @@ impl<T:Copy> OptVec<T> {
             self.push(copy *e);
         }
     }
+
+    #[inline(always)]
+    fn mapi_to_vec<B>(&self, op: &fn(uint, &T) -> B) -> ~[B] {
+        let mut index = 0;
+        iter::map_to_vec(self, |a| {
+            let i = index;
+            index += 1;
+            op(i, a)
+        })
+    }
 }
 
 impl<A:Eq> Eq for OptVec<A> {
diff --git a/src/test/run-pass/trait-inheritance-self-in-supertype.rs b/src/test/run-pass/trait-inheritance-self-in-supertype.rs
new file mode 100644
index 00000000000..8105cf23d80
--- /dev/null
+++ b/src/test/run-pass/trait-inheritance-self-in-supertype.rs
@@ -0,0 +1,61 @@
+// Test for issue #4183: use of Self in supertraits.
+
+pub static FUZZY_EPSILON: float = 0.1;
+
+pub trait FuzzyEq<Eps> {
+    fn fuzzy_eq(&self, other: &Self) -> bool;
+    fn fuzzy_eq_eps(&self, other: &Self, epsilon: &Eps) -> bool;
+}
+
+trait Float: FuzzyEq<Self> {
+    fn two_pi() -> Self;
+}
+
+impl FuzzyEq<f32> for f32 {
+    fn fuzzy_eq(&self, other: &f32) -> bool {
+        self.fuzzy_eq_eps(other, &(FUZZY_EPSILON as f32))
+    }
+
+    fn fuzzy_eq_eps(&self, other: &f32, epsilon: &f32) -> bool {
+        f32::abs(*self - *other) < *epsilon
+    }
+}
+
+impl Float for f32 {
+    fn two_pi() -> f32 { 6.28318530717958647692528676655900576_f32 }
+}
+
+impl FuzzyEq<f64> for f64 {
+    fn fuzzy_eq(&self, other: &f64) -> bool {
+        self.fuzzy_eq_eps(other, &(FUZZY_EPSILON as f64))
+    }
+
+    fn fuzzy_eq_eps(&self, other: &f64, epsilon: &f64) -> bool {
+        f64::abs(*self - *other) < *epsilon
+    }
+}
+
+impl Float for f64 {
+    fn two_pi() -> f64 { 6.28318530717958647692528676655900576_f64 }
+}
+
+fn compare<F:Float>(f1: F) -> bool {
+    let f2 = Float::two_pi();
+    f1.fuzzy_eq(&f2)
+}
+
+pub fn main() {
+    assert!(compare::<f32>(6.28318530717958647692528676655900576));
+    assert!(compare::<f32>(6.29));
+    assert!(compare::<f32>(6.3));
+    assert!(compare::<f32>(6.19));
+    assert!(!compare::<f32>(7.28318530717958647692528676655900576));
+    assert!(!compare::<f32>(6.18));
+
+    assert!(compare::<f64>(6.28318530717958647692528676655900576));
+    assert!(compare::<f64>(6.29));
+    assert!(compare::<f64>(6.3));
+    assert!(compare::<f64>(6.19));
+    assert!(!compare::<f64>(7.28318530717958647692528676655900576));
+    assert!(!compare::<f64>(6.18));
+}
\ No newline at end of file