about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/metadata/common.rs10
-rw-r--r--src/librustc/metadata/csearch.rs8
-rw-r--r--src/librustc/metadata/decoder.rs68
-rw-r--r--src/librustc/metadata/encoder.rs33
-rw-r--r--src/librustc/metadata/tydecode.rs103
-rw-r--r--src/librustc/metadata/tyencode.rs48
-rw-r--r--src/librustc/middle/astencode.rs125
-rw-r--r--src/librustc/middle/def.rs5
-rw-r--r--src/librustc/middle/kind.rs143
-rw-r--r--src/librustc/middle/resolve.rs93
-rw-r--r--src/librustc/middle/resolve_lifetime.rs66
-rw-r--r--src/librustc/middle/save/mod.rs2
-rw-r--r--src/librustc/middle/subst.rs453
-rw-r--r--src/librustc/middle/trans/base.rs4
-rw-r--r--src/librustc/middle/trans/callee.rs63
-rw-r--r--src/librustc/middle/trans/common.rs65
-rw-r--r--src/librustc/middle/trans/debuginfo.rs19
-rw-r--r--src/librustc/middle/trans/inline.rs16
-rw-r--r--src/librustc/middle/trans/intrinsic.rs46
-rw-r--r--src/librustc/middle/trans/meth.rs95
-rw-r--r--src/librustc/middle/trans/monomorphize.rs64
-rw-r--r--src/librustc/middle/trans/reflect.rs1
-rw-r--r--src/librustc/middle/trans/type_of.rs29
-rw-r--r--src/librustc/middle/ty.rs279
-rw-r--r--src/librustc/middle/ty_fold.rs30
-rw-r--r--src/librustc/middle/typeck/astconv.rs125
-rw-r--r--src/librustc/middle/typeck/check/_match.rs29
-rw-r--r--src/librustc/middle/typeck/check/method.rs137
-rw-r--r--src/librustc/middle/typeck/check/mod.rs912
-rw-r--r--src/librustc/middle/typeck/check/vtable.rs247
-rw-r--r--src/librustc/middle/typeck/check/writeback.rs21
-rw-r--r--src/librustc/middle/typeck/coherence.rs122
-rw-r--r--src/librustc/middle/typeck/collect.rs611
-rw-r--r--src/librustc/middle/typeck/infer/combine.rs184
-rw-r--r--src/librustc/middle/typeck/infer/error_reporting.rs8
-rw-r--r--src/librustc/middle/typeck/infer/mod.rs29
-rw-r--r--src/librustc/middle/typeck/mod.rs58
-rw-r--r--src/librustc/middle/typeck/variance.rs147
-rw-r--r--src/librustc/util/ppaux.rs168
-rw-r--r--src/librustdoc/clean/mod.rs74
-rw-r--r--src/libsyntax/ast.rs8
-rw-r--r--src/test/compile-fail/bad-mid-path-type-params.rs13
-rw-r--r--src/test/compile-fail/generic-impl-less-params-with-defaults.rs4
-rw-r--r--src/test/compile-fail/generic-impl-more-params-with-defaults.rs4
-rw-r--r--src/test/compile-fail/issue-11844.rs2
-rw-r--r--src/test/compile-fail/issue-13466.rs1
-rw-r--r--src/test/compile-fail/issue-7092.rs1
-rw-r--r--src/test/compile-fail/variance-regions-direct.rs14
-rw-r--r--src/test/compile-fail/variance-regions-indirect.rs10
49 files changed, 2571 insertions, 2226 deletions
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index 2ff656853c3..5a974aecabc 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -189,9 +189,7 @@ pub static tag_impls_impl: uint = 0x81;
 pub static tag_items_data_item_inherent_impl: uint = 0x82;
 pub static tag_items_data_item_extension_impl: uint = 0x83;
 
-pub static tag_region_param_def: uint = 0x84;
-pub static tag_region_param_def_ident: uint = 0x85;
-pub static tag_region_param_def_def_id: uint = 0x86;
+// GAP 0x84, 0x85, 0x86
 
 pub static tag_native_libraries: uint = 0x87;
 pub static tag_native_libraries_lib: uint = 0x88;
@@ -217,3 +215,9 @@ pub struct LinkMeta {
     pub crateid: CrateId,
     pub crate_hash: Svh,
 }
+
+pub static tag_region_param_def: uint = 0x90;
+pub static tag_region_param_def_ident: uint = 0x91;
+pub static tag_region_param_def_def_id: uint = 0x92;
+pub static tag_region_param_def_space: uint = 0x93;
+pub static tag_region_param_def_index: uint = 0x94;
diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs
index ee5179e9cef..43c895a201f 100644
--- a/src/librustc/metadata/csearch.rs
+++ b/src/librustc/metadata/csearch.rs
@@ -18,6 +18,7 @@ use metadata::decoder;
 use middle::lang_items;
 use middle::ty;
 use middle::typeck;
+use middle::subst::VecPerParamSpace;
 
 use serialize::ebml;
 use serialize::ebml::reader;
@@ -223,8 +224,8 @@ pub fn get_field_type(tcx: &ty::ctxt, class_id: ast::DefId,
         });
     let ty = decoder::item_type(def, the_field, tcx, &*cdata);
     ty::ty_param_bounds_and_ty {
-        generics: ty::Generics {type_param_defs: Rc::new(Vec::new()),
-                                region_param_defs: Rc::new(Vec::new())},
+        generics: ty::Generics {types: VecPerParamSpace::empty(),
+                                regions: VecPerParamSpace::empty()},
         ty: ty
     }
 }
@@ -240,7 +241,8 @@ pub fn get_impl_trait(tcx: &ty::ctxt,
 
 // Given a def_id for an impl, return information about its vtables
 pub fn get_impl_vtables(tcx: &ty::ctxt,
-                        def: ast::DefId) -> typeck::impl_res {
+                        def: ast::DefId)
+                        -> typeck::vtable_res {
     let cstore = &tcx.sess.cstore;
     let cdata = cstore.get_crate_data(def.krate);
     decoder::get_impl_vtables(&*cdata, def.node, tcx)
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index d088d0d953d..68aa2bacd08 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -23,6 +23,7 @@ use metadata::tydecode::{parse_ty_data, parse_def_id,
                          parse_bare_fn_ty_data, parse_trait_ref_data};
 use middle::lang_items;
 use middle::def;
+use middle::subst;
 use middle::ty::{ImplContainer, TraitContainer};
 use middle::ty;
 use middle::typeck;
@@ -257,34 +258,44 @@ fn item_ty_param_defs(item: ebml::Doc,
                       tcx: &ty::ctxt,
                       cdata: Cmd,
                       tag: uint)
-                      -> Rc<Vec<ty::TypeParameterDef> > {
-    let mut bounds = Vec::new();
+                      -> subst::VecPerParamSpace<ty::TypeParameterDef> {
+    let mut bounds = subst::VecPerParamSpace::empty();
     reader::tagged_docs(item, tag, |p| {
         let bd = parse_type_param_def_data(
             p.data, p.start, cdata.cnum, tcx,
             |_, did| translate_def_id(cdata, did));
-        bounds.push(bd);
+        bounds.push(bd.space, bd);
         true
     });
-    Rc::new(bounds)
+    bounds
 }
 
 fn item_region_param_defs(item_doc: ebml::Doc, cdata: Cmd)
-                          -> Rc<Vec<ty::RegionParameterDef> > {
-    let mut v = Vec::new();
+                          -> subst::VecPerParamSpace<ty::RegionParameterDef>
+{
+    let mut v = subst::VecPerParamSpace::empty();
     reader::tagged_docs(item_doc, tag_region_param_def, |rp_doc| {
-            let ident_str_doc = reader::get_doc(rp_doc,
-                                                tag_region_param_def_ident);
-            let ident = item_name(&*token::get_ident_interner(), ident_str_doc);
-            let def_id_doc = reader::get_doc(rp_doc,
-                                             tag_region_param_def_def_id);
-            let def_id = reader::with_doc_data(def_id_doc, parse_def_id);
-            let def_id = translate_def_id(cdata, def_id);
-            v.push(ty::RegionParameterDef { name: ident.name,
-                                            def_id: def_id });
-            true
-        });
-    Rc::new(v)
+        let ident_str_doc = reader::get_doc(rp_doc,
+                                            tag_region_param_def_ident);
+        let ident = item_name(&*token::get_ident_interner(), ident_str_doc);
+        let def_id_doc = reader::get_doc(rp_doc,
+                                         tag_region_param_def_def_id);
+        let def_id = reader::with_doc_data(def_id_doc, parse_def_id);
+        let def_id = translate_def_id(cdata, def_id);
+
+        let doc = reader::get_doc(rp_doc, tag_region_param_def_space);
+        let space = subst::ParamSpace::from_uint(reader::doc_as_u64(doc) as uint);
+
+        let doc = reader::get_doc(rp_doc, tag_region_param_def_index);
+        let index = reader::doc_as_u64(doc) as uint;
+
+        v.push(space, ty::RegionParameterDef { name: ident.name,
+                                               def_id: def_id,
+                                               space: space,
+                                               index: index });
+        true
+    });
+    v
 }
 
 fn enum_variant_ids(item: ebml::Doc, cdata: Cmd) -> Vec<ast::DefId> {
@@ -403,8 +414,8 @@ pub fn get_trait_def(cdata: Cmd,
     }
 
     ty::TraitDef {
-        generics: ty::Generics {type_param_defs: tp_defs,
-                                region_param_defs: rp_defs},
+        generics: ty::Generics {types: tp_defs,
+                                regions: rp_defs},
         bounds: bounds,
         trait_ref: Rc::new(item_trait_ref(item_doc, tcx, cdata))
     }
@@ -422,8 +433,8 @@ pub fn get_type(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt)
     let rp_defs = item_region_param_defs(item, cdata);
 
     ty::ty_param_bounds_and_ty {
-        generics: ty::Generics {type_param_defs: tp_defs,
-                                region_param_defs: rp_defs},
+        generics: ty::Generics {types: tp_defs,
+                                regions: rp_defs},
         ty: t
     }
 }
@@ -440,16 +451,13 @@ pub fn get_impl_trait(cdata: Cmd,
 
 pub fn get_impl_vtables(cdata: Cmd,
                         id: ast::NodeId,
-                        tcx: &ty::ctxt) -> typeck::impl_res
+                        tcx: &ty::ctxt)
+                        -> typeck::vtable_res
 {
     let item_doc = lookup_item(id, cdata.data());
     let vtables_doc = reader::get_doc(item_doc, tag_item_impl_vtables);
     let mut decoder = reader::Decoder::new(vtables_doc);
-
-    typeck::impl_res {
-        trait_vtables: decoder.read_vtable_res(tcx, cdata),
-        self_vtables: decoder.read_vtable_param_res(tcx, cdata)
-    }
+    decoder.read_vtable_res(tcx, cdata)
 }
 
 
@@ -802,8 +810,8 @@ pub fn get_method(intr: Rc<IdentInterner>, cdata: Cmd, id: ast::NodeId,
     ty::Method::new(
         name,
         ty::Generics {
-            type_param_defs: type_param_defs,
-            region_param_defs: rp_defs,
+            types: type_param_defs,
+            regions: rp_defs,
         },
         fty,
         explicit_self,
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index f5672dce16c..98d9b45738f 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -19,6 +19,7 @@ use metadata::common::*;
 use metadata::cstore;
 use metadata::decoder;
 use metadata::tyencode;
+use middle::subst::VecPerParamSpace;
 use middle::ty::{node_id_to_type, lookup_item_type};
 use middle::astencode;
 use middle::ty;
@@ -128,10 +129,9 @@ fn encode_trait_ref(ebml_w: &mut Encoder,
 
 fn encode_impl_vtables(ebml_w: &mut Encoder,
                        ecx: &EncodeContext,
-                       vtables: &typeck::impl_res) {
+                       vtables: &typeck::vtable_res) {
     ebml_w.start_tag(tag_item_impl_vtables);
-    astencode::encode_vtable_res(ecx, ebml_w, &vtables.trait_vtables);
-    astencode::encode_vtable_param_res(ecx, ebml_w, &vtables.self_vtables);
+    astencode::encode_vtable_res(ecx, ebml_w, vtables);
     ebml_w.end_tag();
 }
 
@@ -148,7 +148,7 @@ pub fn def_to_str(did: DefId) -> String {
 
 fn encode_ty_type_param_defs(ebml_w: &mut Encoder,
                              ecx: &EncodeContext,
-                             params: &[ty::TypeParameterDef],
+                             params: &VecPerParamSpace<ty::TypeParameterDef>,
                              tag: uint) {
     let ty_str_ctxt = &tyencode::ctxt {
         diag: ecx.diag,
@@ -164,7 +164,7 @@ fn encode_ty_type_param_defs(ebml_w: &mut Encoder,
 }
 
 fn encode_region_param_defs(ebml_w: &mut Encoder,
-                            params: &[ty::RegionParameterDef]) {
+                            params: &VecPerParamSpace<ty::RegionParameterDef>) {
     for param in params.iter() {
         ebml_w.start_tag(tag_region_param_def);
 
@@ -175,6 +175,12 @@ fn encode_region_param_defs(ebml_w: &mut Encoder,
         ebml_w.wr_tagged_str(tag_region_param_def_def_id,
                              def_to_str(param.def_id).as_slice());
 
+        ebml_w.wr_tagged_u64(tag_region_param_def_space,
+                             param.space.to_uint() as u64);
+
+        ebml_w.wr_tagged_u64(tag_region_param_def_index,
+                             param.index as u64);
+
         ebml_w.end_tag();
     }
 }
@@ -191,9 +197,9 @@ fn encode_item_variances(ebml_w: &mut Encoder,
 fn encode_bounds_and_type(ebml_w: &mut Encoder,
                           ecx: &EncodeContext,
                           tpt: &ty::ty_param_bounds_and_ty) {
-    encode_ty_type_param_defs(ebml_w, ecx, tpt.generics.type_param_defs(),
+    encode_ty_type_param_defs(ebml_w, ecx, &tpt.generics.types,
                               tag_items_data_item_ty_param_bounds);
-    encode_region_param_defs(ebml_w, tpt.generics.region_param_defs());
+    encode_region_param_defs(ebml_w, &tpt.generics.regions);
     encode_type(ecx, ebml_w, tpt.ty);
 }
 
@@ -725,8 +731,7 @@ fn encode_method_ty_fields(ecx: &EncodeContext,
                            method_ty: &ty::Method) {
     encode_def_id(ebml_w, method_ty.def_id);
     encode_name(ebml_w, method_ty.ident.name);
-    encode_ty_type_param_defs(ebml_w, ecx,
-                              method_ty.generics.type_param_defs(),
+    encode_ty_type_param_defs(ebml_w, ecx, &method_ty.generics.types,
                               tag_item_method_tps);
     encode_method_fty(ecx, ebml_w, &method_ty.fty);
     encode_visibility(ebml_w, method_ty.vis);
@@ -770,10 +775,8 @@ fn encode_info_for_method(ecx: &EncodeContext,
     }
 
     for &ast_method in ast_method_opt.iter() {
-        let num_params = tpt.generics.type_param_defs().len();
-        if num_params > 0u ||
-                is_default_impl ||
-                should_inline(ast_method.attrs.as_slice()) {
+        let any_types = !tpt.generics.types.is_empty();
+        if any_types || is_default_impl || should_inline(ast_method.attrs.as_slice()) {
             encode_inlined_item(ecx, ebml_w,
                                 IIMethodRef(local_def(parent_id), false,
                                             &*ast_method));
@@ -1125,9 +1128,9 @@ fn encode_info_for_item(ecx: &EncodeContext,
         encode_item_variances(ebml_w, ecx, item.id);
         let trait_def = ty::lookup_trait_def(tcx, def_id);
         encode_ty_type_param_defs(ebml_w, ecx,
-                                  trait_def.generics.type_param_defs(),
+                                  &trait_def.generics.types,
                                   tag_items_data_item_ty_param_bounds);
-        encode_region_param_defs(ebml_w, trait_def.generics.region_param_defs());
+        encode_region_param_defs(ebml_w, &trait_def.generics.regions);
         encode_trait_ref(ebml_w, ecx, &*trait_def.trait_ref, tag_item_trait_ref);
         encode_name(ebml_w, item.ident.name);
         encode_attributes(ebml_w, item.attrs.as_slice());
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index a39be31c4b5..6a60f91a1ae 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -17,6 +17,7 @@
 #![allow(non_camel_case_types)]
 
 use middle::subst;
+use middle::subst::VecPerParamSpace;
 use middle::ty;
 
 use std::rc::Rc;
@@ -114,26 +115,45 @@ pub fn parse_state_from_data<'a>(data: &'a [u8], crate_num: ast::CrateNum,
     }
 }
 
+fn data_log_string(data: &[u8], pos: uint) -> String {
+    let mut buf = String::new();
+    buf.push_str("<<");
+    for i in range(pos, data.len()) {
+        let c = data[i];
+        if c > 0x20 && c <= 0x7F {
+            buf.push_char(c as char);
+        } else {
+            buf.push_char('.');
+        }
+    }
+    buf.push_str(">>");
+    buf
+}
+
 pub fn parse_ty_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tcx: &ty::ctxt,
                      conv: conv_did) -> ty::t {
+    debug!("parse_ty_data {}", data_log_string(data, pos));
     let mut st = parse_state_from_data(data, crate_num, pos, tcx);
     parse_ty(&mut st, conv)
 }
 
 pub fn parse_bare_fn_ty_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tcx: &ty::ctxt,
                              conv: conv_did) -> ty::BareFnTy {
+    debug!("parse_bare_fn_ty_data {}", data_log_string(data, pos));
     let mut st = parse_state_from_data(data, crate_num, pos, tcx);
     parse_bare_fn_ty(&mut st, conv)
 }
 
 pub fn parse_trait_ref_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tcx: &ty::ctxt,
                             conv: conv_did) -> ty::TraitRef {
+    debug!("parse_trait_ref_data {}", data_log_string(data, pos));
     let mut st = parse_state_from_data(data, crate_num, pos, tcx);
     parse_trait_ref(&mut st, conv)
 }
 
 pub fn parse_substs_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tcx: &ty::ctxt,
                          conv: conv_did) -> subst::Substs {
+    debug!("parse_substs_data {}", data_log_string(data, pos));
     let mut st = parse_state_from_data(data, crate_num, pos, tcx);
     parse_substs(&mut st, conv)
 }
@@ -162,34 +182,39 @@ fn parse_trait_store(st: &mut PState, conv: conv_did) -> ty::TraitStore {
     }
 }
 
-fn parse_substs(st: &mut PState, conv: conv_did) -> subst::Substs {
-    let regions = parse_region_substs(st, |x,y| conv(x,y));
+fn parse_vec_per_param_space<T>(st: &mut PState,
+                                f: |&mut PState| -> T)
+                                -> VecPerParamSpace<T>
+{
+    let mut r = VecPerParamSpace::empty();
+    for &space in subst::ParamSpace::all().iter() {
+        assert_eq!(next(st), '[');
+        while peek(st) != ']' {
+            r.push(space, f(st));
+        }
+        assert_eq!(next(st), ']');
+    }
+    r
+}
 
-    let self_ty = parse_opt(st, |st| parse_ty(st, |x,y| conv(x,y)) );
+fn parse_substs(st: &mut PState, conv: conv_did) -> subst::Substs {
+    let regions =
+        parse_region_substs(st, |x,y| conv(x,y));
 
-    assert_eq!(next(st), '[');
-    let mut params: Vec<ty::t> = Vec::new();
-    while peek(st) != ']' { params.push(parse_ty(st, |x,y| conv(x,y))); }
-    st.pos = st.pos + 1u;
+    let types =
+        parse_vec_per_param_space(st, |st| parse_ty(st, |x,y| conv(x,y)));
 
-    return subst::Substs {
-        regions: regions,
-        self_ty: self_ty,
-        tps: params
-    };
+    return subst::Substs { types: types,
+                           regions: regions };
 }
 
 fn parse_region_substs(st: &mut PState, conv: conv_did) -> subst::RegionSubsts {
     match next(st) {
         'e' => subst::ErasedRegions,
         'n' => {
-            let mut regions = vec!();
-            while peek(st) != '.' {
-                let r = parse_region(st, |x,y| conv(x,y));
-                regions.push(r);
-            }
-            assert_eq!(next(st), '.');
-            subst::NonerasedRegions(regions)
+            subst::NonerasedRegions(
+                parse_vec_per_param_space(
+                    st, |st| parse_region(st, |x,y| conv(x,y))))
         }
         _ => fail!("parse_bound_region: bad input")
     }
@@ -230,10 +255,12 @@ fn parse_region(st: &mut PState, conv: conv_did) -> ty::Region {
         assert_eq!(next(st), '[');
         let node_id = parse_uint(st) as ast::NodeId;
         assert_eq!(next(st), '|');
+        let space = parse_param_space(st);
+        assert_eq!(next(st), '|');
         let index = parse_uint(st);
         assert_eq!(next(st), '|');
         let nm = token::str_to_ident(parse_str(st, ']').as_slice());
-        ty::ReEarlyBound(node_id, index, nm.name)
+        ty::ReEarlyBound(node_id, space, index, nm.name)
       }
       'f' => {
         assert_eq!(next(st), '[');
@@ -327,11 +354,11 @@ fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t {
       'p' => {
         let did = parse_def(st, TypeParameter, |x,y| conv(x,y));
         debug!("parsed ty_param: did={:?}", did);
-        return ty::mk_param(st.tcx, parse_uint(st), did);
-      }
-      's' => {
-        let did = parse_def(st, TypeParameter, |x,y| conv(x,y));
-        return ty::mk_self(st.tcx, did);
+        let index = parse_uint(st);
+        assert_eq!(next(st), '|');
+        let space = parse_param_space(st);
+        assert_eq!(next(st), '|');
+        return ty::mk_param(st.tcx, space, index, did);
       }
       '@' => return ty::mk_box(st.tcx, parse_ty(st, |x,y| conv(x,y))),
       '~' => return ty::mk_uniq(st.tcx, parse_ty(st, |x,y| conv(x,y))),
@@ -395,6 +422,9 @@ fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t {
           assert_eq!(next(st), ']');
           return ty::mk_struct(st.tcx, did, substs);
       }
+      'e' => {
+          return ty::mk_err();
+      }
       c => { fail!("unexpected char in type string: {}", c);}
     }
 }
@@ -427,6 +457,10 @@ fn parse_uint(st: &mut PState) -> uint {
     };
 }
 
+fn parse_param_space(st: &mut PState) -> subst::ParamSpace {
+    subst::ParamSpace::from_uint(parse_uint(st))
+}
+
 fn parse_hex(st: &mut PState) -> uint {
     let mut n = 0u;
     loop {
@@ -546,11 +580,22 @@ pub fn parse_type_param_def_data(data: &[u8], start: uint,
 }
 
 fn parse_type_param_def(st: &mut PState, conv: conv_did) -> ty::TypeParameterDef {
+    let ident = parse_ident(st, ':');
+    let def_id = parse_def(st, NominalType, |x,y| conv(x,y));
+    let space = parse_param_space(st);
+    assert_eq!(next(st), '|');
+    let index = parse_uint(st);
+    assert_eq!(next(st), '|');
+    let bounds = Rc::new(parse_bounds(st, |x,y| conv(x,y)));
+    let default = parse_opt(st, |st| parse_ty(st, |x,y| conv(x,y)));
+
     ty::TypeParameterDef {
-        ident: parse_ident(st, ':'),
-        def_id: parse_def(st, NominalType, |x,y| conv(x,y)),
-        bounds: Rc::new(parse_bounds(st, |x,y| conv(x,y))),
-        default: parse_opt(st, |st| parse_ty(st, |x,y| conv(x,y)))
+        ident: ident,
+        def_id: def_id,
+        space: space,
+        index: index,
+        bounds: bounds,
+        default: default
     }
 }
 
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 8b36256492f..b8987a382da 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -18,7 +18,8 @@ use std::collections::HashMap;
 use std::io::MemWriter;
 
 use middle::subst;
-use middle::ty::param_ty;
+use middle::subst::VecPerParamSpace;
+use middle::ty::ParamTy;
 use middle::ty;
 
 use syntax::abi::Abi;
@@ -118,12 +119,23 @@ fn enc_opt<T>(w: &mut MemWriter, t: Option<T>, enc_f: |&mut MemWriter, T|) {
     }
 }
 
+fn enc_vec_per_param_space<T>(w: &mut MemWriter,
+                              cx: &ctxt,
+                              v: &VecPerParamSpace<T>,
+                              op: |&mut MemWriter, &ctxt, &T|) {
+    for &space in subst::ParamSpace::all().iter() {
+        mywrite!(w, "[");
+        for t in v.get_vec(space).iter() {
+            op(w, cx, t);
+        }
+        mywrite!(w, "]");
+    }
+}
+
 pub fn enc_substs(w: &mut MemWriter, cx: &ctxt, substs: &subst::Substs) {
     enc_region_substs(w, cx, &substs.regions);
-    enc_opt(w, substs.self_ty, |w, t| enc_ty(w, cx, t));
-    mywrite!(w, "[");
-    for t in substs.tps.iter() { enc_ty(w, cx, *t); }
-    mywrite!(w, "]");
+    enc_vec_per_param_space(w, cx, &substs.types,
+                            |w, cx, &ty| enc_ty(w, cx, ty));
 }
 
 fn enc_region_substs(w: &mut MemWriter, cx: &ctxt, substs: &subst::RegionSubsts) {
@@ -133,10 +145,8 @@ fn enc_region_substs(w: &mut MemWriter, cx: &ctxt, substs: &subst::RegionSubsts)
         }
         subst::NonerasedRegions(ref regions) => {
             mywrite!(w, "n");
-            for &r in regions.iter() {
-                enc_region(w, cx, r);
-            }
-            mywrite!(w, ".");
+            enc_vec_per_param_space(w, cx, regions,
+                                    |w, cx, &r| enc_region(w, cx, r));
         }
     }
 }
@@ -148,9 +158,10 @@ fn enc_region(w: &mut MemWriter, cx: &ctxt, r: ty::Region) {
             enc_bound_region(w, cx, br);
             mywrite!(w, "]");
         }
-        ty::ReEarlyBound(node_id, index, name) => {
-            mywrite!(w, "B[{}|{}|{}]",
+        ty::ReEarlyBound(node_id, space, index, name) => {
+            mywrite!(w, "B[{}|{}|{}|{}]",
                      node_id,
+                     space.to_uint(),
                      index,
                      token::get_name(name));
         }
@@ -293,18 +304,17 @@ fn enc_sty(w: &mut MemWriter, cx: &ctxt, st: &ty::sty) {
         ty::ty_infer(_) => {
             cx.diag.handler().bug("cannot encode inference variable types");
         }
-        ty::ty_param(param_ty {idx: id, def_id: did}) => {
-            mywrite!(w, "p{}|{}", (cx.ds)(did), id);
-        }
-        ty::ty_self(did) => {
-            mywrite!(w, "s{}|", (cx.ds)(did));
+        ty::ty_param(ParamTy {space, idx: id, def_id: did}) => {
+            mywrite!(w, "p{}|{}|{}|", (cx.ds)(did), id, space.to_uint())
         }
         ty::ty_struct(def, ref substs) => {
             mywrite!(w, "a[{}|", (cx.ds)(def));
             enc_substs(w, cx, substs);
             mywrite!(w, "]");
         }
-        ty::ty_err => fail!("shouldn't encode error type")
+        ty::ty_err => {
+            mywrite!(w, "e");
+        }
     }
 }
 
@@ -378,7 +388,9 @@ fn enc_bounds(w: &mut MemWriter, cx: &ctxt, bs: &ty::ParamBounds) {
 }
 
 pub fn enc_type_param_def(w: &mut MemWriter, cx: &ctxt, v: &ty::TypeParameterDef) {
-    mywrite!(w, "{}:{}|", token::get_ident(v.ident), (cx.ds)(v.def_id));
+    mywrite!(w, "{}:{}|{}|{}|",
+             token::get_ident(v.ident), (cx.ds)(v.def_id),
+             v.space.to_uint(), v.index);
     enc_bounds(w, cx, &*v.bounds);
     enc_opt(w, v.default, |w, t| enc_ty(w, cx, t));
 }
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index cc356a8bf07..289c2feef2d 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -25,6 +25,7 @@ use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter,
                          RegionParameter};
 use metadata::tyencode;
 use middle::subst;
+use middle::subst::VecPerParamSpace;
 use middle::typeck::{MethodCall, MethodCallee, MethodOrigin};
 use middle::{ty, typeck};
 use util::ppaux::ty_to_str;
@@ -39,7 +40,7 @@ use libc;
 use std::io::Seek;
 use std::io::MemWriter;
 use std::mem;
-use std::rc::Rc;
+use std::string::String;
 
 use serialize::ebml::reader;
 use serialize::ebml;
@@ -433,7 +434,7 @@ impl tr for def::Def {
           def::DefTrait(did) => def::DefTrait(did.tr(xcx)),
           def::DefTy(did) => def::DefTy(did.tr(xcx)),
           def::DefPrimTy(p) => def::DefPrimTy(p),
-          def::DefTyParam(did, v) => def::DefTyParam(did.tr(xcx), v),
+          def::DefTyParam(s, did, v) => def::DefTyParam(s, did.tr(xcx), v),
           def::DefBinding(nid, bm) => def::DefBinding(xcx.tr_id(nid), bm),
           def::DefUse(did) => def::DefUse(did.tr(xcx)),
           def::DefUpvar(nid1, def, nid2, nid3) => {
@@ -476,13 +477,18 @@ impl tr for ty::AutoRef {
 impl tr for ty::Region {
     fn tr(&self, xcx: &ExtendedDecodeContext) -> ty::Region {
         match *self {
-            ty::ReLateBound(id, br) => ty::ReLateBound(xcx.tr_id(id),
-                                                       br.tr(xcx)),
-            ty::ReEarlyBound(id, index, ident) => ty::ReEarlyBound(xcx.tr_id(id),
-                                                                     index,
-                                                                     ident),
-            ty::ReScope(id) => ty::ReScope(xcx.tr_id(id)),
-            ty::ReEmpty | ty::ReStatic | ty::ReInfer(..) => *self,
+            ty::ReLateBound(id, br) => {
+                ty::ReLateBound(xcx.tr_id(id), br.tr(xcx))
+            }
+            ty::ReEarlyBound(id, space, index, ident) => {
+                ty::ReEarlyBound(xcx.tr_id(id), space, index, ident)
+            }
+            ty::ReScope(id) => {
+                ty::ReScope(xcx.tr_id(id))
+            }
+            ty::ReEmpty | ty::ReStatic | ty::ReInfer(..) => {
+                *self
+            }
             ty::ReFree(ref fr) => {
                 ty::ReFree(ty::FreeRegion {scope_id: xcx.tr_id(fr.scope_id),
                                             bound_region: fr.bound_region.tr(xcx)})
@@ -634,15 +640,16 @@ fn encode_vtable_res_with_key(ecx: &e::EncodeContext,
 }
 
 pub fn encode_vtable_res(ecx: &e::EncodeContext,
-                     ebml_w: &mut Encoder,
-                     dr: &typeck::vtable_res) {
+                         ebml_w: &mut Encoder,
+                         dr: &typeck::vtable_res) {
     // can't autogenerate this code because automatic code of
     // ty::t doesn't work, and there is no way (atm) to have
     // hand-written encoding routines combine with auto-generated
-    // ones.  perhaps we should fix this.
-    ebml_w.emit_from_vec(dr.as_slice(), |ebml_w, param_tables| {
-        Ok(encode_vtable_param_res(ecx, ebml_w, param_tables))
-    }).unwrap()
+    // ones. perhaps we should fix this.
+    encode_vec_per_param_space(
+        ebml_w, dr,
+        |ebml_w, param_tables| encode_vtable_param_res(ecx, ebml_w,
+                                                       param_tables))
 }
 
 pub fn encode_vtable_param_res(ecx: &e::EncodeContext,
@@ -673,7 +680,7 @@ pub fn encode_vtable_origin(ecx: &e::EncodeContext,
             })
           }
           typeck::vtable_param(pn, bn) => {
-            ebml_w.emit_enum_variant("vtable_param", 1u, 2u, |ebml_w| {
+            ebml_w.emit_enum_variant("vtable_param", 1u, 3u, |ebml_w| {
                 ebml_w.emit_enum_variant_arg(0u, |ebml_w| {
                     pn.encode(ebml_w)
                 });
@@ -682,11 +689,19 @@ pub fn encode_vtable_origin(ecx: &e::EncodeContext,
                 })
             })
           }
+          typeck::vtable_error => {
+            ebml_w.emit_enum_variant("vtable_error", 2u, 3u, |_ebml_w| {
+                Ok(())
+            })
+          }
         }
     }).unwrap()
 }
 
 pub trait vtable_decoder_helpers {
+    fn read_vec_per_param_space<T>(&mut self,
+                                   f: |&mut Self| -> T)
+                                   -> VecPerParamSpace<T>;
     fn read_vtable_res_with_key(&mut self,
                                 tcx: &ty::ctxt,
                                 cdata: &cstore::crate_metadata)
@@ -703,6 +718,16 @@ pub trait vtable_decoder_helpers {
 }
 
 impl<'a> vtable_decoder_helpers for reader::Decoder<'a> {
+    fn read_vec_per_param_space<T>(&mut self,
+                                   f: |&mut reader::Decoder<'a>| -> T)
+                                   -> VecPerParamSpace<T>
+    {
+        let types = self.read_to_vec(|this| Ok(f(this))).unwrap();
+        let selfs = self.read_to_vec(|this| Ok(f(this))).unwrap();
+        let fns = self.read_to_vec(|this| Ok(f(this))).unwrap();
+        VecPerParamSpace::new(types, selfs, fns)
+    }
+
     fn read_vtable_res_with_key(&mut self,
                                 tcx: &ty::ctxt,
                                 cdata: &cstore::crate_metadata)
@@ -718,10 +743,12 @@ impl<'a> vtable_decoder_helpers for reader::Decoder<'a> {
     }
 
     fn read_vtable_res(&mut self,
-                       tcx: &ty::ctxt, cdata: &cstore::crate_metadata)
-                      -> typeck::vtable_res {
-        self.read_to_vec(|this| Ok(this.read_vtable_param_res(tcx, cdata)))
-             .unwrap().move_iter().collect()
+                       tcx: &ty::ctxt,
+                       cdata: &cstore::crate_metadata)
+                       -> typeck::vtable_res
+    {
+        self.read_vec_per_param_space(
+            |this| this.read_vtable_param_res(tcx, cdata))
     }
 
     fn read_vtable_param_res(&mut self,
@@ -737,7 +764,7 @@ impl<'a> vtable_decoder_helpers for reader::Decoder<'a> {
         self.read_enum("vtable_origin", |this| {
             this.read_enum_variant(["vtable_static",
                                     "vtable_param",
-                                    "vtable_self"],
+                                    "vtable_error"],
                                    |this, i| {
                 Ok(match i {
                   0 => {
@@ -763,7 +790,9 @@ impl<'a> vtable_decoder_helpers for reader::Decoder<'a> {
                         }).unwrap()
                     )
                   }
-                  // hard to avoid - user input
+                  2 => {
+                    typeck::vtable_error
+                  }
                   _ => fail!("bad enum variant")
                 })
             })
@@ -771,6 +800,18 @@ impl<'a> vtable_decoder_helpers for reader::Decoder<'a> {
     }
 }
 
+// ___________________________________________________________________________
+//
+
+fn encode_vec_per_param_space<T>(ebml_w: &mut Encoder,
+                                 v: &subst::VecPerParamSpace<T>,
+                                 f: |&mut Encoder, &T|) {
+    for &space in subst::ParamSpace::all().iter() {
+        ebml_w.emit_from_vec(v.get_vec(space).as_slice(),
+                             |ebml_w, n| Ok(f(ebml_w, n))).unwrap();
+    }
+}
+
 // ______________________________________________________________________
 // Encoding and decoding the side tables
 
@@ -827,14 +868,15 @@ impl<'a> ebml_writer_helpers for Encoder<'a> {
         self.emit_struct("ty_param_bounds_and_ty", 2, |this| {
             this.emit_struct_field("generics", 0, |this| {
                 this.emit_struct("Generics", 2, |this| {
-                    this.emit_struct_field("type_param_defs", 0, |this| {
-                        this.emit_from_vec(tpbt.generics.type_param_defs(),
-                                           |this, type_param_def| {
-                            Ok(this.emit_type_param_def(ecx, type_param_def))
-                        })
+                    this.emit_struct_field("types", 0, |this| {
+                        Ok(encode_vec_per_param_space(
+                            this, &tpbt.generics.types,
+                            |this, def| this.emit_type_param_def(ecx, def)))
                     });
-                    this.emit_struct_field("region_param_defs", 1, |this| {
-                        tpbt.generics.region_param_defs().encode(this)
+                    this.emit_struct_field("regions", 1, |this| {
+                        Ok(encode_vec_per_param_space(
+                            this, &tpbt.generics.regions,
+                            |this, def| def.encode(this).unwrap()))
                     })
                 })
             });
@@ -1186,22 +1228,17 @@ impl<'a> ebml_decoder_decoder_helpers for reader::Decoder<'a> {
                 generics: this.read_struct_field("generics", 0, |this| {
                     this.read_struct("Generics", 2, |this| {
                         Ok(ty::Generics {
-                            type_param_defs:
-                                this.read_struct_field("type_param_defs",
-                                                       0,
-                                                       |this| {
-                                    Ok(Rc::new(this.read_to_vec(|this|
-                                                             Ok(this.read_type_param_def(xcx)))
-                                                .unwrap()
-                                                .move_iter()
-                                                .collect()))
+                            types:
+                            this.read_struct_field("types", 0, |this| {
+                                Ok(this.read_vec_per_param_space(
+                                    |this| this.read_type_param_def(xcx)))
                             }).unwrap(),
-                            region_param_defs:
-                                this.read_struct_field("region_param_defs",
-                                                       1,
-                                                       |this| {
-                                    Decodable::decode(this)
-                                }).unwrap()
+
+                            regions:
+                            this.read_struct_field("regions", 1, |this| {
+                                Ok(this.read_vec_per_param_space(
+                                    |this| Decodable::decode(this).unwrap()))
+                            }).unwrap()
                         })
                     })
                 }).unwrap(),
diff --git a/src/librustc/middle/def.rs b/src/librustc/middle/def.rs
index 6805c86f169..7ee8b33b1fa 100644
--- a/src/librustc/middle/def.rs
+++ b/src/librustc/middle/def.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use middle::subst::ParamSpace;
 use syntax::ast;
 use syntax::ast_util::local_def;
 
@@ -27,7 +28,7 @@ pub enum Def {
     DefTy(ast::DefId),
     DefTrait(ast::DefId),
     DefPrimTy(ast::PrimTy),
-    DefTyParam(ast::DefId, uint),
+    DefTyParam(ParamSpace, ast::DefId, uint),
     DefBinding(ast::NodeId, ast::BindingMode),
     DefUse(ast::DefId),
     DefUpvar(ast::NodeId,  // id of closed over var
@@ -61,7 +62,7 @@ impl Def {
         match *self {
             DefFn(id, _) | DefStaticMethod(id, _, _) | DefMod(id) |
             DefForeignMod(id) | DefStatic(id, _) |
-            DefVariant(_, id, _) | DefTy(id) | DefTyParam(id, _) |
+            DefVariant(_, id, _) | DefTy(id) | DefTyParam(_, id, _) |
             DefUse(id) | DefStruct(id) | DefTrait(id) | DefMethod(id, _) => {
                 id
             }
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index df6b20b62f5..9979f130935 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -86,12 +86,9 @@ fn check_struct_safe_for_destructor(cx: &mut Context,
                                     span: Span,
                                     struct_did: DefId) {
     let struct_tpt = ty::lookup_item_type(cx.tcx, struct_did);
-    if !struct_tpt.generics.has_type_params() {
-        let struct_ty = ty::mk_struct(cx.tcx, struct_did, subst::Substs {
-            regions: subst::NonerasedRegions(Vec::new()),
-            self_ty: None,
-            tps: Vec::new()
-        });
+    if !struct_tpt.generics.has_type_params(subst::TypeSpace) {
+        let struct_ty = ty::mk_struct(cx.tcx, struct_did,
+                                      subst::Substs::empty());
         if !ty::type_is_sendable(cx.tcx, struct_ty) {
             cx.tcx.sess.span_err(span,
                                  "cannot implement a destructor on a \
@@ -245,51 +242,7 @@ pub fn check_expr(cx: &mut Context, e: &Expr) {
     debug!("kind::check_expr({})", expr_to_str(e));
 
     // Handle any kind bounds on type parameters
-    {
-        let method_map = cx.tcx.method_map.borrow();
-        let method = method_map.find(&typeck::MethodCall::expr(e.id));
-        let item_substs = cx.tcx.item_substs.borrow();
-        let r = match method {
-            Some(method) => Some(&method.substs.tps),
-            None => item_substs.find(&e.id).map(|s| &s.substs.tps)
-        };
-        for ts in r.iter() {
-            let def_map = cx.tcx.def_map.borrow();
-            let type_param_defs = match e.node {
-              ExprPath(_) => {
-                let did = def_map.get_copy(&e.id).def_id();
-                ty::lookup_item_type(cx.tcx, did).generics.type_param_defs.clone()
-              }
-              _ => {
-                // Type substitutions should only occur on paths and
-                // method calls, so this needs to be a method call.
-
-                // Even though the callee_id may have been the id with
-                // node_type_substs, e.id is correct here.
-                match method {
-                    Some(method) => {
-                        ty::method_call_type_param_defs(cx.tcx, method.origin)
-                    }
-                    None => {
-                        cx.tcx.sess.span_bug(e.span,
-                            "non path/method call expr has type substs??");
-                    }
-                }
-              }
-            };
-            if ts.len() != type_param_defs.len() {
-                // Fail earlier to make debugging easier
-                fail!("internal error: in kind::check_expr, length \
-                      mismatch between actual and declared bounds: actual = \
-                      {}, declared = {}",
-                      ts.repr(cx.tcx),
-                      type_param_defs.repr(cx.tcx));
-            }
-            for (&ty, type_param_def) in ts.iter().zip(type_param_defs.iter()) {
-                check_typaram_bounds(cx, e.span, ty, type_param_def)
-            }
-        }
-    }
+    check_bounds_on_type_parameters(cx, e);
 
     match e.node {
         ExprUnary(UnBox, ref interior) => {
@@ -331,6 +284,77 @@ pub fn check_expr(cx: &mut Context, e: &Expr) {
     visit::walk_expr(cx, e, ());
 }
 
+fn check_bounds_on_type_parameters(cx: &mut Context, e: &Expr) {
+    let method_map = cx.tcx.method_map.borrow();
+    let method = method_map.find(&typeck::MethodCall::expr(e.id));
+
+    // Find the values that were provided (if any)
+    let item_substs = cx.tcx.item_substs.borrow();
+    let (types, is_object_call) = match method {
+        Some(method) => {
+            let is_object_call = match method.origin {
+                typeck::MethodObject(..) => true,
+                typeck::MethodStatic(..) | typeck::MethodParam(..) => false
+            };
+            (&method.substs.types, is_object_call)
+        }
+        None => {
+            match item_substs.find(&e.id) {
+                None => { return; }
+                Some(s) => { (&s.substs.types, false) }
+            }
+        }
+    };
+
+    // Find the relevant type parameter definitions
+    let def_map = cx.tcx.def_map.borrow();
+    let type_param_defs = match e.node {
+        ExprPath(_) => {
+            let did = def_map.get_copy(&e.id).def_id();
+            ty::lookup_item_type(cx.tcx, did).generics.types.clone()
+        }
+        _ => {
+            // Type substitutions should only occur on paths and
+            // method calls, so this needs to be a method call.
+
+            // Even though the callee_id may have been the id with
+            // node_type_substs, e.id is correct here.
+            match method {
+                Some(method) => {
+                    ty::method_call_type_param_defs(cx.tcx, method.origin)
+                }
+                None => {
+                    cx.tcx.sess.span_bug(e.span,
+                                         "non path/method call expr has type substs??");
+                }
+            }
+        }
+    };
+
+    // Check that the value provided for each definition meets the
+    // kind requirements
+    for type_param_def in type_param_defs.iter() {
+        let ty = *types.get(type_param_def.space, type_param_def.index);
+
+        // If this is a call to an object method (`foo.bar()` where
+        // `foo` has a type like `Trait`), then the self type is
+        // unknown (after all, this is a virtual call). In that case,
+        // we will have put a ty_err in the substitutions, and we can
+        // just skip over validating the bounds (because the bounds
+        // would have been enforced when the object instance was
+        // created).
+        if is_object_call && type_param_def.space == subst::SelfSpace {
+            assert_eq!(type_param_def.index, 0);
+            assert!(ty::type_is_error(ty));
+            continue;
+        }
+
+        debug!("type_param_def space={} index={} ty={}",
+               type_param_def.space, type_param_def.index, ty.repr(cx.tcx));
+        check_typaram_bounds(cx, e.span, ty, type_param_def)
+    }
+}
+
 fn check_trait_cast(cx: &mut Context, source_ty: ty::t, target_ty: ty::t, span: Span) {
     check_cast_for_escaping_regions(cx, source_ty, target_ty, span);
     match ty::get(target_ty).sty {
@@ -350,12 +374,10 @@ fn check_ty(cx: &mut Context, aty: &Ty) {
                     let def_map = cx.tcx.def_map.borrow();
                     let did = def_map.get_copy(&id).def_id();
                     let generics = ty::lookup_item_type(cx.tcx, did).generics;
-                    let type_param_defs = generics.type_param_defs();
-                    for (&ty, type_param_def) in
-                        item_substs.substs.tps.iter().zip(
-                            type_param_defs.iter())
-                    {
-                        check_typaram_bounds(cx, aty.span, ty, type_param_def)
+                    for def in generics.types.iter() {
+                        let ty = *item_substs.substs.types.get(def.space,
+                                                               def.index);
+                        check_typaram_bounds(cx, aty.span, ty, def)
                     }
                 }
             }
@@ -555,7 +577,12 @@ pub fn check_cast_for_escaping_regions(
         |ty| {
             match ty::get(ty).sty {
                 ty::ty_param(source_param) => {
-                    if target_params.iter().any(|x| x == &source_param) {
+                    if source_param.space == subst::SelfSpace {
+                        // FIXME (#5723) -- there is no reason that
+                        // Self should be exempt from this check,
+                        // except for historical accident. Bottom
+                        // line, we need proper region bounding.
+                    } else if target_params.iter().any(|x| x == &source_param) {
                         /* case (2) */
                     } else {
                         check_static(cx.tcx, ty, source_span); /* case (3) */
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 7fc31c974e9..47bc2521689 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -17,6 +17,7 @@ use middle::def::*;
 use middle::lang_items::LanguageItems;
 use middle::lint::{UnnecessaryQualification, UnusedImports};
 use middle::pat_util::pat_bindings;
+use middle::subst::{ParamSpace, FnSpace, TypeSpace};
 use util::nodemap::{NodeMap, DefIdSet, FnvHashMap};
 
 use syntax::ast::*;
@@ -227,25 +228,20 @@ enum FallbackSuggestion {
 }
 
 enum TypeParameters<'a> {
-    NoTypeParameters,                   //< No type parameters.
-    HasTypeParameters(&'a Generics,  //< Type parameters.
-                      NodeId,          //< ID of the enclosing item
-
-                      // The index to start numbering the type parameters at.
-                      // This is zero if this is the outermost set of type
-                      // parameters, or equal to the number of outer type
-                      // parameters. For example, if we have:
-                      //
-                      //   impl I<T> {
-                      //     fn method<U>() { ... }
-                      //   }
-                      //
-                      // The index at the method site will be 1, because the
-                      // outer T had index 0.
-                      uint,
-
-                      // The kind of the rib used for type parameters.
-                      RibKind)
+    NoTypeParameters,
+    HasTypeParameters(
+        // Type parameters.
+        &'a Generics,
+
+        // Identifies the things that these parameters
+        // were declared on (type, fn, etc)
+        ParamSpace,
+
+        // ID of the enclosing item.
+        NodeId,
+
+        // The kind of the rib used for type parameters.
+        RibKind)
 }
 
 // The rib kind controls the translation of argument or local definitions
@@ -1532,8 +1528,8 @@ impl<'a> Resolver<'a> {
 
                 self.with_type_parameter_rib(
                     HasTypeParameters(generics,
+                                      FnSpace,
                                       foreign_item.id,
-                                      0,
                                       NormalRibKind),
                     f);
             }
@@ -3439,7 +3435,7 @@ impl<'a> Resolver<'a> {
                   // If the def is a ty param, and came from the parent
                   // item, it's ok
                   match def {
-                    DefTyParam(did, _) if {
+                    DefTyParam(_, did, _) if {
                         self.def_map.borrow().find(&did.node).map(|x| *x)
                             == Some(DefTyParamBinder(item_id))
                     } => {
@@ -3574,8 +3570,8 @@ impl<'a> Resolver<'a> {
                 // but maybe it's okay since the first time will signal an
                 // error if there is one? -- tjc
                 self.with_type_parameter_rib(HasTypeParameters(generics,
+                                                               TypeSpace,
                                                                item.id,
-                                                               0,
                                                                ItemRibKind),
                                              |this| {
                     visit::walk_item(this, item, ());
@@ -3584,8 +3580,8 @@ impl<'a> Resolver<'a> {
 
             ItemTy(_, ref generics) => {
                 self.with_type_parameter_rib(HasTypeParameters(generics,
+                                                               TypeSpace,
                                                                item.id,
-                                                               0,
                                                                ItemRibKind),
                                              |this| {
                     visit::walk_item(this, item, ());
@@ -3615,8 +3611,8 @@ impl<'a> Resolver<'a> {
 
                 // Create a new rib for the trait-wide type parameters.
                 self.with_type_parameter_rib(HasTypeParameters(generics,
+                                                               TypeSpace,
                                                                item.id,
-                                                               0,
                                                                NormalRibKind),
                                              |this| {
                     this.resolve_type_parameters(&generics.ty_params);
@@ -3636,8 +3632,8 @@ impl<'a> Resolver<'a> {
                           ast::Required(ref ty_m) => {
                             this.with_type_parameter_rib
                                 (HasTypeParameters(&ty_m.generics,
+                                                   FnSpace,
                                                    item.id,
-                                                   generics.ty_params.len(),
                                         MethodRibKind(item.id, Required)),
                                  |this| {
 
@@ -3655,9 +3651,8 @@ impl<'a> Resolver<'a> {
                           }
                           ast::Provided(ref m) => {
                               this.resolve_method(MethodRibKind(item.id,
-                                                     Provided(m.id)),
-                                                  &**m,
-                                                  generics.ty_params.len())
+                                                                Provided(m.id)),
+                                                  &**m)
                           }
                         }
                     }
@@ -3687,7 +3682,7 @@ impl<'a> Resolver<'a> {
                             ForeignItemFn(_, ref generics) => {
                                 this.with_type_parameter_rib(
                                     HasTypeParameters(
-                                        generics, foreign_item.id, 0,
+                                        generics, FnSpace, foreign_item.id,
                                         ItemRibKind),
                                     |this| visit::walk_foreign_item(this,
                                                                 &**foreign_item,
@@ -3708,8 +3703,8 @@ impl<'a> Resolver<'a> {
                                       Some(fn_decl),
                                       HasTypeParameters
                                         (generics,
+                                         FnSpace,
                                          item.id,
-                                         0,
                                          ItemRibKind),
                                       block);
             }
@@ -3730,7 +3725,7 @@ impl<'a> Resolver<'a> {
                                type_parameters: TypeParameters,
                                f: |&mut Resolver|) {
         match type_parameters {
-            HasTypeParameters(generics, node_id, initial_index,
+            HasTypeParameters(generics, space, node_id,
                               rib_kind) => {
 
                 let function_type_rib = Rib::new(rib_kind);
@@ -3739,9 +3734,9 @@ impl<'a> Resolver<'a> {
                     let ident = type_parameter.ident;
                     debug!("with_type_parameter_rib: {} {}", node_id,
                            type_parameter.id);
-                    let def_like = DlDef(DefTyParam
-                        (local_def(type_parameter.id),
-                         index + initial_index));
+                    let def_like = DlDef(DefTyParam(space,
+                                                    local_def(type_parameter.id),
+                                                    index));
                     // Associate this type parameter with
                     // the item that bound it
                     self.record_def(type_parameter.id,
@@ -3897,8 +3892,8 @@ impl<'a> Resolver<'a> {
                       fields: &[StructField]) {
         // If applicable, create a rib for the type parameters.
         self.with_type_parameter_rib(HasTypeParameters(generics,
+                                                       TypeSpace,
                                                        id,
-                                                       0,
                                                        ItemRibKind),
                                      |this| {
             // Resolve the type parameters.
@@ -3948,14 +3943,12 @@ impl<'a> Resolver<'a> {
     // to be NormalRibKind?
     fn resolve_method(&mut self,
                       rib_kind: RibKind,
-                      method: &Method,
-                      outer_type_parameter_count: uint) {
+                      method: &Method) {
         let method_generics = &method.generics;
-        let type_parameters =
-            HasTypeParameters(method_generics,
-                              method.id,
-                              outer_type_parameter_count,
-                              rib_kind);
+        let type_parameters = HasTypeParameters(method_generics,
+                                                FnSpace,
+                                                method.id,
+                                                rib_kind);
 
         self.resolve_function(rib_kind, Some(method.decl), type_parameters, method.body);
     }
@@ -3992,16 +3985,15 @@ impl<'a> Resolver<'a> {
     }
 
     fn resolve_implementation(&mut self,
-                                  id: NodeId,
-                                  generics: &Generics,
-                                  opt_trait_reference: &Option<TraitRef>,
-                                  self_type: &Ty,
-                                  methods: &[Gc<Method>]) {
+                              id: NodeId,
+                              generics: &Generics,
+                              opt_trait_reference: &Option<TraitRef>,
+                              self_type: &Ty,
+                              methods: &[Gc<Method>]) {
         // If applicable, create a rib for the type parameters.
-        let outer_type_parameter_count = generics.ty_params.len();
         self.with_type_parameter_rib(HasTypeParameters(generics,
+                                                       TypeSpace,
                                                        id,
-                                                       0,
                                                        NormalRibKind),
                                      |this| {
             // Resolve the type parameters.
@@ -4016,8 +4008,7 @@ impl<'a> Resolver<'a> {
                     for method in methods.iter() {
                         // We also need a new scope for the method-specific type parameters.
                         this.resolve_method(MethodRibKind(id, Provided(method.id)),
-                                            &**method,
-                                            outer_type_parameter_count);
+                                            &**method);
                     }
                 });
             });
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 7065772d74f..f416686efd8 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -18,7 +18,7 @@
  */
 
 use driver::session::Session;
-use util::nodemap::NodeMap;
+use middle::subst;
 use syntax::ast;
 use syntax::codemap::Span;
 use syntax::owned_slice::OwnedSlice;
@@ -27,10 +27,24 @@ use syntax::parse::token;
 use syntax::print::pprust::{lifetime_to_str};
 use syntax::visit;
 use syntax::visit::Visitor;
+use util::nodemap::NodeMap;
+
+#[deriving(Clone, PartialEq, Eq, Hash, Encodable, Decodable, Show)]
+pub enum DefRegion {
+    DefStaticRegion,
+    DefEarlyBoundRegion(/* space */ subst::ParamSpace,
+                        /* index */ uint,
+                        /* lifetime decl */ ast::NodeId),
+    DefLateBoundRegion(/* binder_id */ ast::NodeId,
+                       /* depth */ uint,
+                       /* lifetime decl */ ast::NodeId),
+    DefFreeRegion(/* block scope */ ast::NodeId,
+                  /* lifetime decl */ ast::NodeId),
+}
 
 // maps the id of each lifetime reference to the lifetime decl
 // that it corresponds to
-pub type NamedRegionMap = NodeMap<ast::DefRegion>;
+pub type NamedRegionMap = NodeMap<DefRegion>;
 
 // Returns an instance of some type that implements std::fmt::Show
 fn lifetime_show(lt_name: &ast::Name) -> token::InternedString {
@@ -45,7 +59,7 @@ struct LifetimeContext<'a> {
 enum ScopeChain<'a> {
     /// EarlyScope(i, ['a, 'b, ...], s) extends s with early-bound
     /// lifetimes, assigning indexes 'a => i, 'b => i+1, ... etc.
-    EarlyScope(uint, &'a Vec<ast::Lifetime>, Scope<'a>),
+    EarlyScope(subst::ParamSpace, &'a Vec<ast::Lifetime>, Scope<'a>),
     /// LateScope(binder_id, ['a, 'b, ...], s) extends s with late-bound
     /// lifetimes introduced by the declaration binder_id.
     LateScope(ast::NodeId, &'a Vec<ast::Lifetime>, Scope<'a>),
@@ -86,7 +100,7 @@ impl<'a, 'b> Visitor<Scope<'a>> for LifetimeContext<'b> {
             ast::ItemImpl(ref generics, _, _, _) |
             ast::ItemTrait(ref generics, _, _, _) => {
                 self.check_lifetime_names(&generics.lifetimes);
-                EarlyScope(0, &generics.lifetimes, &root)
+                EarlyScope(subst::TypeSpace, &generics.lifetimes, &root)
             }
         };
         debug!("entering scope {:?}", scope);
@@ -152,33 +166,13 @@ impl<'a, 'b> Visitor<Scope<'a>> for LifetimeContext<'b> {
                           lifetime_ref: &ast::Lifetime,
                           scope: Scope<'a>) {
         if lifetime_ref.name == special_idents::statik.name {
-            self.insert_lifetime(lifetime_ref, ast::DefStaticRegion);
+            self.insert_lifetime(lifetime_ref, DefStaticRegion);
             return;
         }
         self.resolve_lifetime_ref(lifetime_ref, scope);
     }
 }
 
-impl<'a> ScopeChain<'a> {
-    fn count_early_params(&self) -> uint {
-        /*!
-         * Counts the number of early-bound parameters that are in
-         * scope.  Used when checking methods: the early-bound
-         * lifetime parameters declared on the method are assigned
-         * indices that come after the indices from the type.  Given
-         * something like `impl<'a> Foo { ... fn bar<'b>(...) }`
-         * then `'a` gets index 0 and `'b` gets index 1.
-         */
-
-        match *self {
-            RootScope => 0,
-            EarlyScope(base, lifetimes, _) => base + lifetimes.len(),
-            LateScope(_, _, s) => s.count_early_params(),
-            BlockScope(_, _) => 0,
-        }
-    }
-}
-
 impl<'a> LifetimeContext<'a> {
     /// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
     fn visit_fn_decl(&mut self,
@@ -212,14 +206,11 @@ impl<'a> LifetimeContext<'a> {
 
         self.check_lifetime_names(&generics.lifetimes);
 
-        let early_count = scope.count_early_params();
         let referenced_idents = free_lifetimes(&generics.ty_params);
         debug!("pushing fn scope id={} due to fn item/method\
-               referenced_idents={:?} \
-               early_count={}",
+               referenced_idents={:?}",
                n,
-               referenced_idents.iter().map(lifetime_show).collect::<Vec<token::InternedString>>(),
-               early_count);
+               referenced_idents.iter().map(lifetime_show).collect::<Vec<token::InternedString>>());
         if referenced_idents.is_empty() {
             let scope1 = LateScope(n, &generics.lifetimes, scope);
             walk(self, &scope1)
@@ -227,7 +218,7 @@ impl<'a> LifetimeContext<'a> {
             let (early, late) = generics.lifetimes.clone().partition(
                 |l| referenced_idents.iter().any(|&i| i == l.name));
 
-            let scope1 = EarlyScope(early_count, &early, scope);
+            let scope1 = EarlyScope(subst::FnSpace, &early, scope);
             let scope2 = LateScope(n, &late, &scope1);
 
             walk(self, &scope2);
@@ -256,11 +247,10 @@ impl<'a> LifetimeContext<'a> {
                     break;
                 }
 
-                EarlyScope(base, lifetimes, s) => {
+                EarlyScope(space, lifetimes, s) => {
                     match search_lifetimes(lifetimes, lifetime_ref) {
-                        Some((offset, decl_id)) => {
-                            let index = base + offset;
-                            let def = ast::DefEarlyBoundRegion(index, decl_id);
+                        Some((index, decl_id)) => {
+                            let def = DefEarlyBoundRegion(space, index, decl_id);
                             self.insert_lifetime(lifetime_ref, def);
                             return;
                         }
@@ -274,7 +264,7 @@ impl<'a> LifetimeContext<'a> {
                 LateScope(binder_id, lifetimes, s) => {
                     match search_lifetimes(lifetimes, lifetime_ref) {
                         Some((_index, decl_id)) => {
-                            let def = ast::DefLateBoundRegion(binder_id, depth, decl_id);
+                            let def = DefLateBoundRegion(binder_id, depth, decl_id);
                             self.insert_lifetime(lifetime_ref, def);
                             return;
                         }
@@ -325,7 +315,7 @@ impl<'a> LifetimeContext<'a> {
 
         match search_result {
             Some((_depth, decl_id)) => {
-                let def = ast::DefFreeRegion(scope_id, decl_id);
+                let def = DefFreeRegion(scope_id, decl_id);
                 self.insert_lifetime(lifetime_ref, def);
             }
 
@@ -374,7 +364,7 @@ impl<'a> LifetimeContext<'a> {
 
     fn insert_lifetime(&mut self,
                        lifetime_ref: &ast::Lifetime,
-                       def: ast::DefRegion) {
+                       def: DefRegion) {
         if lifetime_ref.id == ast::DUMMY_NODE_ID {
             self.sess.span_bug(lifetime_ref.span,
                                "lifetime reference not renumbered, \
diff --git a/src/librustc/middle/save/mod.rs b/src/librustc/middle/save/mod.rs
index 2c73bc847a3..7e26d9c7938 100644
--- a/src/librustc/middle/save/mod.rs
+++ b/src/librustc/middle/save/mod.rs
@@ -231,7 +231,7 @@ impl <'l> DxrVisitor<'l> {
             def::DefTyParamBinder(_) |
             def::DefLabel(_) |
             def::DefStaticMethod(_, _, _) |
-            def::DefTyParam(_, _) |
+            def::DefTyParam(..) |
             def::DefUse(_) |
             def::DefMethod(_, _) |
             def::DefPrimTy(_) => {
diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs
index c4121d830db..5e7284dbfd1 100644
--- a/src/librustc/middle/subst.rs
+++ b/src/librustc/middle/subst.rs
@@ -15,58 +15,131 @@ use middle::ty_fold;
 use middle::ty_fold::{TypeFoldable, TypeFolder};
 use util::ppaux::Repr;
 
+use std::iter::Chain;
+use std::mem;
+use std::raw;
+use std::slice::{Items, MutItems};
 use std::vec::Vec;
-use syntax::codemap::Span;
+use syntax::codemap::{Span, DUMMY_SP};
+
+///////////////////////////////////////////////////////////////////////////
+// HomogeneousTuple3 trait
+//
+// This could be moved into standard library at some point.
+
+trait HomogeneousTuple3<T> {
+    fn len(&self) -> uint;
+    fn as_slice<'a>(&'a self) -> &'a [T];
+    fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T];
+    fn iter<'a>(&'a self) -> Items<'a, T>;
+    fn mut_iter<'a>(&'a mut self) -> MutItems<'a, T>;
+    fn get<'a>(&'a self, index: uint) -> Option<&'a T>;
+    fn get_mut<'a>(&'a mut self, index: uint) -> Option<&'a mut T>;
+}
+
+impl<T> HomogeneousTuple3<T> for (T, T, T) {
+    fn len(&self) -> uint {
+        3
+    }
+
+    fn as_slice<'a>(&'a self) -> &'a [T] {
+        unsafe {
+            let ptr: *T = mem::transmute(self);
+            let slice = raw::Slice { data: ptr, len: 3 };
+            mem::transmute(slice)
+        }
+    }
+
+    fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] {
+        unsafe {
+            let ptr: *T = mem::transmute(self);
+            let slice = raw::Slice { data: ptr, len: 3 };
+            mem::transmute(slice)
+        }
+    }
+
+    fn iter<'a>(&'a self) -> Items<'a, T> {
+        let slice: &'a [T] = self.as_slice();
+        slice.iter()
+    }
+
+    fn mut_iter<'a>(&'a mut self) -> MutItems<'a, T> {
+        self.as_mut_slice().mut_iter()
+    }
+
+    fn get<'a>(&'a self, index: uint) -> Option<&'a T> {
+        self.as_slice().get(index)
+    }
+
+    fn get_mut<'a>(&'a mut self, index: uint) -> Option<&'a mut T> {
+        Some(&mut self.as_mut_slice()[index]) // wrong: fallible
+    }
+}
 
 ///////////////////////////////////////////////////////////////////////////
 
 /**
+ * A substitution mapping type/region parameters to new values. We
+ * identify each in-scope parameter by an *index* and a *parameter
+ * space* (which indices where the parameter is defined; see
+ * `ParamSpace`).
+ */
+#[deriving(Clone, PartialEq, Eq, Hash)]
+pub struct Substs {
+    pub types: VecPerParamSpace<ty::t>,
+    pub regions: RegionSubsts,
+}
+
+/**
  * Represents the values to use when substituting lifetime parameters.
  * If the value is `ErasedRegions`, then this subst is occurring during
  * trans, and all region parameters will be replaced with `ty::ReStatic`. */
 #[deriving(Clone, PartialEq, Eq, Hash)]
 pub enum RegionSubsts {
     ErasedRegions,
-    NonerasedRegions(Vec<ty::Region>)
-}
-
-/**
- * The type `Substs` represents the kinds of things that can be substituted to
- * convert a polytype into a monotype.  Note however that substituting bound
- * regions other than `self` is done through a different mechanism:
- *
- * - `tps` represents the type parameters in scope.  They are indexed
- *   according to the order in which they were declared.
- *
- * - `self_r` indicates the region parameter `self` that is present on nominal
- *   types (enums, structs) declared as having a region parameter.  `self_r`
- *   should always be none for types that are not region-parameterized and
- *   Some(_) for types that are.  The only bound region parameter that should
- *   appear within a region-parameterized type is `self`.
- *
- * - `self_ty` is the type to which `self` should be remapped, if any.  The
- *   `self` type is rather funny in that it can only appear on traits and is
- *   always substituted away to the implementing type for a trait. */
-#[deriving(Clone, PartialEq, Eq, Hash)]
-pub struct Substs {
-    pub self_ty: Option<ty::t>,
-    pub tps: Vec<ty::t>,
-    pub regions: RegionSubsts,
+    NonerasedRegions(VecPerParamSpace<ty::Region>)
 }
 
 impl Substs {
+    pub fn new(t: VecPerParamSpace<ty::t>,
+               r: VecPerParamSpace<ty::Region>)
+               -> Substs
+    {
+        Substs { types: t, regions: NonerasedRegions(r) }
+    }
+
+    pub fn new_type(t: Vec<ty::t>,
+                    r: Vec<ty::Region>)
+                    -> Substs
+    {
+        Substs::new(VecPerParamSpace::new(t, Vec::new(), Vec::new()),
+                    VecPerParamSpace::new(r, Vec::new(), Vec::new()))
+    }
+
+    pub fn new_trait(t: Vec<ty::t>,
+                     r: Vec<ty::Region>,
+                     s: ty::t)
+                    -> Substs
+    {
+        Substs::new(VecPerParamSpace::new(t, vec!(s), Vec::new()),
+                    VecPerParamSpace::new(r, Vec::new(), Vec::new()))
+    }
+
+    pub fn erased(t: VecPerParamSpace<ty::t>) -> Substs
+    {
+        Substs { types: t, regions: ErasedRegions }
+    }
+
     pub fn empty() -> Substs {
         Substs {
-            self_ty: None,
-            tps: Vec::new(),
-            regions: NonerasedRegions(Vec::new())
+            types: VecPerParamSpace::empty(),
+            regions: NonerasedRegions(VecPerParamSpace::empty()),
         }
     }
 
     pub fn trans_empty() -> Substs {
         Substs {
-            self_ty: None,
-            tps: Vec::new(),
+            types: VecPerParamSpace::empty(),
             regions: ErasedRegions
         }
     }
@@ -74,16 +147,251 @@ impl Substs {
     pub fn is_noop(&self) -> bool {
         let regions_is_noop = match self.regions {
             ErasedRegions => false, // may be used to canonicalize
-            NonerasedRegions(ref regions) => regions.is_empty()
+            NonerasedRegions(ref regions) => regions.is_empty(),
         };
 
-        self.tps.len() == 0u &&
-            regions_is_noop &&
-            self.self_ty.is_none()
+        regions_is_noop && self.types.is_empty()
+    }
+
+    pub fn self_ty(&self) -> Option<ty::t> {
+        self.types.get_self().map(|&t| t)
+    }
+
+    pub fn with_self_ty(&self, self_ty: ty::t) -> Substs {
+        assert!(self.self_ty().is_none());
+        let mut s = (*self).clone();
+        s.types.push(SelfSpace, self_ty);
+        s
+    }
+
+    pub fn regions<'a>(&'a self) -> &'a VecPerParamSpace<ty::Region> {
+        /*!
+         * Since ErasedRegions are only to be used in trans, most of
+         * the compiler can use this method to easily access the set
+         * of region substitutions.
+         */
+
+        match self.regions {
+            ErasedRegions => fail!("Erased regions only expected in trans"),
+            NonerasedRegions(ref r) => r
+        }
     }
 
-    pub fn self_ty(&self) -> ty::t {
-        self.self_ty.unwrap()
+    pub fn mut_regions<'a>(&'a mut self) -> &'a mut VecPerParamSpace<ty::Region> {
+        /*!
+         * Since ErasedRegions are only to be used in trans, most of
+         * the compiler can use this method to easily access the set
+         * of region substitutions.
+         */
+
+        match self.regions {
+            ErasedRegions => fail!("Erased regions only expected in trans"),
+            NonerasedRegions(ref mut r) => r
+        }
+    }
+
+    pub fn with_method_from(self, substs: &Substs) -> Substs {
+        self.with_method((*substs.types.get_vec(FnSpace)).clone(),
+                         (*substs.regions().get_vec(FnSpace)).clone())
+    }
+
+    pub fn with_method(self,
+                       m_types: Vec<ty::t>,
+                       m_regions: Vec<ty::Region>)
+                       -> Substs
+    {
+        let Substs { types, regions } = self;
+        let types = types.with_vec(FnSpace, m_types);
+        let regions = regions.map(m_regions,
+                                  |r, m_regions| r.with_vec(FnSpace, m_regions));
+        Substs { types: types, regions: regions }
+    }
+}
+
+impl RegionSubsts {
+    fn map<A>(self,
+              a: A,
+              op: |VecPerParamSpace<ty::Region>, A| -> VecPerParamSpace<ty::Region>)
+              -> RegionSubsts {
+        match self {
+            ErasedRegions => ErasedRegions,
+            NonerasedRegions(r) => NonerasedRegions(op(r, a))
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// ParamSpace
+
+#[deriving(PartialOrd, Ord, PartialEq, Eq,
+           Clone, Hash, Encodable, Decodable, Show)]
+pub enum ParamSpace {
+    TypeSpace, // Type parameters attached to a type definition, trait, or impl
+    SelfSpace, // Self parameter on a trait
+    FnSpace,   // Type parameters attached to a method or fn
+}
+
+impl ParamSpace {
+    pub fn all() -> [ParamSpace, ..3] {
+        [TypeSpace, SelfSpace, FnSpace]
+    }
+
+    pub fn to_uint(self) -> uint {
+        match self {
+            TypeSpace => 0,
+            SelfSpace => 1,
+            FnSpace => 2,
+        }
+    }
+
+    pub fn from_uint(u: uint) -> ParamSpace {
+        match u {
+            0 => TypeSpace,
+            1 => SelfSpace,
+            2 => FnSpace,
+            _ => fail!("Invalid ParamSpace: {}", u)
+        }
+    }
+}
+
+/**
+ * Vector of things sorted by param space. Used to keep
+ * the set of things declared on the type, self, or method
+ * distinct.
+ */
+#[deriving(PartialEq, Eq, Clone, Hash, Encodable, Decodable)]
+pub struct VecPerParamSpace<T> {
+    vecs: (Vec<T>, Vec<T>, Vec<T>)
+}
+
+impl<T> VecPerParamSpace<T> {
+    pub fn empty() -> VecPerParamSpace<T> {
+        VecPerParamSpace {
+            vecs: (Vec::new(), Vec::new(), Vec::new())
+        }
+    }
+
+    pub fn params_from_type(types: Vec<T>) -> VecPerParamSpace<T> {
+        VecPerParamSpace::empty().with_vec(TypeSpace, types)
+    }
+
+    pub fn new(t: Vec<T>, s: Vec<T>, f: Vec<T>) -> VecPerParamSpace<T> {
+        VecPerParamSpace {
+            vecs: (t, s, f)
+        }
+    }
+
+    pub fn sort(t: Vec<T>, space: |&T| -> ParamSpace) -> VecPerParamSpace<T> {
+        let mut result = VecPerParamSpace::empty();
+        for t in t.move_iter() {
+            result.push(space(&t), t);
+        }
+        result
+    }
+
+    pub fn push(&mut self, space: ParamSpace, value: T) {
+        self.get_mut_vec(space).push(value);
+    }
+
+    pub fn get_self<'a>(&'a self) -> Option<&'a T> {
+        let v = self.get_vec(SelfSpace);
+        assert!(v.len() <= 1);
+        if v.len() == 0 { None } else { Some(v.get(0)) }
+    }
+
+    pub fn len(&self, space: ParamSpace) -> uint {
+        self.get_vec(space).len()
+    }
+
+    pub fn get_vec<'a>(&'a self, space: ParamSpace) -> &'a Vec<T> {
+        self.vecs.get(space as uint).unwrap()
+    }
+
+    pub fn get_mut_vec<'a>(&'a mut self, space: ParamSpace) -> &'a mut Vec<T> {
+        self.vecs.get_mut(space as uint).unwrap()
+    }
+
+    pub fn opt_get<'a>(&'a self,
+                       space: ParamSpace,
+                       index: uint)
+                       -> Option<&'a T> {
+        let v = self.get_vec(space);
+        if index < v.len() { Some(v.get(index)) } else { None }
+    }
+
+    pub fn get<'a>(&'a self, space: ParamSpace, index: uint) -> &'a T {
+        self.get_vec(space).get(index)
+    }
+
+    pub fn get_mut<'a>(&'a mut self,
+                       space: ParamSpace,
+                       index: uint) -> &'a mut T {
+        self.get_mut_vec(space).get_mut(index)
+    }
+
+    pub fn iter<'a>(&'a self) -> Chain<Items<'a,T>,
+                                       Chain<Items<'a,T>,
+                                             Items<'a,T>>> {
+        let (ref r, ref s, ref f) = self.vecs;
+        r.iter().chain(s.iter().chain(f.iter()))
+    }
+
+    pub fn all_vecs(&self, pred: |&Vec<T>| -> bool) -> bool {
+        self.vecs.iter().all(pred)
+    }
+
+    pub fn all(&self, pred: |&T| -> bool) -> bool {
+        self.iter().all(pred)
+    }
+
+    pub fn any(&self, pred: |&T| -> bool) -> bool {
+        self.iter().any(pred)
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.all_vecs(|v| v.is_empty())
+    }
+
+    pub fn map<U>(&self, pred: |&T| -> U) -> VecPerParamSpace<U> {
+        VecPerParamSpace::new(self.vecs.ref0().iter().map(|p| pred(p)).collect(),
+                              self.vecs.ref1().iter().map(|p| pred(p)).collect(),
+                              self.vecs.ref2().iter().map(|p| pred(p)).collect())
+    }
+
+    pub fn map_rev<U>(&self, pred: |&T| -> U) -> VecPerParamSpace<U> {
+        /*!
+         * Executes the map but in reverse order. For hacky reasons, we rely
+         * on this in table.
+         *
+         * FIXME(#5527) -- order of eval becomes irrelevant with newer
+         * trait reform, which features an idempotent algorithm that
+         * can be run to a fixed point
+         */
+
+        let mut fns: Vec<U> = self.vecs.ref2().iter().rev().map(|p| pred(p)).collect();
+
+        // NB: Calling foo.rev().map().rev() causes the calls to map
+        // to occur in the wrong order. This was somewhat surprising
+        // to me, though it makes total sense.
+        fns.reverse();
+
+        let mut selfs: Vec<U> = self.vecs.ref1().iter().rev().map(|p| pred(p)).collect();
+        selfs.reverse();
+        let mut tys: Vec<U> = self.vecs.ref0().iter().rev().map(|p| pred(p)).collect();
+        tys.reverse();
+        VecPerParamSpace::new(tys, selfs, fns)
+    }
+
+    pub fn split(self) -> (Vec<T>, Vec<T>, Vec<T>) {
+        self.vecs
+    }
+
+    pub fn with_vec(mut self, space: ParamSpace, vec: Vec<T>)
+                    -> VecPerParamSpace<T>
+    {
+        assert!(self.get_vec(space).is_empty());
+        *self.get_mut_vec(space) = vec;
+        self
     }
 }
 
@@ -149,10 +457,10 @@ impl<'a> TypeFolder for SubstFolder<'a> {
         // the specialized routine
         // `middle::typeck::check::regionmanip::replace_late_regions_in_fn_sig()`.
         match r {
-            ty::ReEarlyBound(_, i, _) => {
+            ty::ReEarlyBound(_, space, i, _) => {
                 match self.substs.regions {
                     ErasedRegions => ty::ReStatic,
-                    NonerasedRegions(ref regions) => *regions.get(i),
+                    NonerasedRegions(ref regions) => *regions.get(space, i),
                 }
             }
             _ => r
@@ -173,52 +481,11 @@ impl<'a> TypeFolder for SubstFolder<'a> {
 
         let t1 = match ty::get(t).sty {
             ty::ty_param(p) => {
-                // FIXME -- This...really shouldn't happen. We should
-                // never be substituting without knowing what's in
-                // scope and knowing that the indices will line up!
-                if p.idx < self.substs.tps.len() {
-                    *self.substs.tps.get(p.idx)
-                } else {
-                    let root_msg = match self.root_ty {
-                        Some(root) => format!(" in the substitution of `{}`",
-                                              root.repr(self.tcx)),
-                        None => "".to_string()
-                    };
-                    let m = format!("can't use type parameters from outer \
-                                    function{}; try using a local type \
-                                    parameter instead",
-                                    root_msg);
-                    match self.span {
-                        Some(span) => {
-                            self.tcx.sess.span_err(span, m.as_slice())
-                        }
-                        None => self.tcx.sess.err(m.as_slice())
-                    }
-                    ty::mk_err()
-                }
+                check(self, t, self.substs.types.opt_get(p.space, p.idx))
             }
-            ty::ty_self(_) => {
-                match self.substs.self_ty {
-                    Some(ty) => ty,
-                    None => {
-                        let root_msg = match self.root_ty {
-                            Some(root) => format!(" in the substitution of `{}`",
-                                                  root.repr(self.tcx)),
-                            None => "".to_string()
-                        };
-                        let m = format!("missing `Self` type param{}",
-                                        root_msg);
-                        match self.span {
-                            Some(span) => {
-                                self.tcx.sess.span_err(span, m.as_slice())
-                            }
-                            None => self.tcx.sess.err(m.as_slice())
-                        }
-                        ty::mk_err()
-                    }
-                }
+            _ => {
+                ty_fold::super_fold_ty(self, t)
             }
-            _ => ty_fold::super_fold_ty(self, t)
         };
 
         assert_eq!(depth + 1, self.ty_stack_depth);
@@ -227,6 +494,24 @@ impl<'a> TypeFolder for SubstFolder<'a> {
             self.root_ty = None;
         }
 
-        t1
+        return t1;
+
+        fn check(this: &SubstFolder,
+                 source_ty: ty::t,
+                 opt_ty: Option<&ty::t>)
+                 -> ty::t {
+            match opt_ty {
+                Some(t) => *t,
+                None => {
+                    let span = this.span.unwrap_or(DUMMY_SP);
+                    this.tcx().sess.span_bug(
+                        span,
+                        format!("Type parameter {} out of range \
+                                 when substituting (root type={})",
+                                source_ty.repr(this.tcx()),
+                                this.root_ty.repr(this.tcx())).as_slice());
+                }
+            }
+        }
     }
 }
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 9cebc79171f..3e15d5a1a61 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -457,11 +457,11 @@ pub fn get_res_dtor(ccx: &CrateContext,
         did
     };
 
-    if !substs.tps.is_empty() || !substs.self_ty.is_none() {
+    if !substs.types.is_empty() {
         assert_eq!(did.krate, ast::LOCAL_CRATE);
 
         let vtables = typeck::check::vtable::trans_resolve_method(ccx.tcx(), did.node, substs);
-        let (val, _) = monomorphize::monomorphic_fn(ccx, did, substs, vtables, None, None);
+        let (val, _) = monomorphize::monomorphic_fn(ccx, did, substs, vtables, None);
 
         val
     } else if did.krate == ast::LOCAL_CRATE {
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index 2f4b086b5b0..8b484e90898 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -23,7 +23,7 @@ use lib::llvm::llvm;
 use metadata::csearch;
 use middle::def;
 use middle::subst;
-use middle::subst::Subst;
+use middle::subst::{Subst, VecPerParamSpace};
 use middle::trans::base;
 use middle::trans::base::*;
 use middle::trans::build::*;
@@ -198,12 +198,10 @@ fn trans_fn_ref_with_vtables_to_callee<'a>(bcx: &'a Block<'a>,
 
 fn resolve_default_method_vtables(bcx: &Block,
                                   impl_id: ast::DefId,
-                                  method: &ty::Method,
                                   substs: &subst::Substs,
                                   impl_vtables: typeck::vtable_res)
-                          -> (typeck::vtable_res, typeck::vtable_param_res)
+                                  -> typeck::vtable_res
 {
-
     // Get the vtables that the impl implements the trait at
     let impl_res = ty::lookup_impl_vtables(bcx.tcx(), impl_id);
 
@@ -211,32 +209,30 @@ fn resolve_default_method_vtables(bcx: &Block,
     // trait_vtables under.
     let param_substs = param_substs {
         substs: (*substs).clone(),
-        vtables: impl_vtables.clone(),
-        self_vtables: None
+        vtables: impl_vtables.clone()
     };
 
     let mut param_vtables = resolve_vtables_under_param_substs(
-        bcx.tcx(), &param_substs, impl_res.trait_vtables.as_slice());
+        bcx.tcx(), &param_substs, &impl_res);
 
     // Now we pull any vtables for parameters on the actual method.
-    let num_method_vtables = method.generics.type_param_defs().len();
-    let num_impl_type_parameters = impl_vtables.len() - num_method_vtables;
-    param_vtables.push_all(impl_vtables.tailn(num_impl_type_parameters));
-
-    let self_vtables = resolve_param_vtables_under_param_substs(
-        bcx.tcx(), &param_substs, impl_res.self_vtables.as_slice());
+    param_vtables
+        .get_mut_vec(subst::FnSpace)
+        .push_all(
+            impl_vtables.get_vec(subst::FnSpace).as_slice());
 
-    (param_vtables, self_vtables)
+    param_vtables
 }
 
 
 pub fn trans_fn_ref_with_vtables(
-        bcx: &Block,       //
-        def_id: ast::DefId,   // def id of fn
-        node: ExprOrMethodCall,  // node id of use of fn; may be zero if N/A
-        substs: subst::Substs, // values for fn's ty params
-        vtables: typeck::vtable_res) // vtables for the call
-     -> ValueRef {
+    bcx: &Block,                 //
+    def_id: ast::DefId,          // def id of fn
+    node: ExprOrMethodCall,      // node id of use of fn; may be zero if N/A
+    substs: subst::Substs,       // values for fn's ty params
+    vtables: typeck::vtable_res) // vtables for the call
+    -> ValueRef
+{
     /*!
      * Translates a reference to a fn/method item, monomorphizing and
      * inlining as it goes.
@@ -264,7 +260,7 @@ pub fn trans_fn_ref_with_vtables(
            substs.repr(tcx),
            vtables.repr(tcx));
 
-    assert!(substs.tps.iter().all(|t| !ty::type_needs_infer(*t)));
+    assert!(substs.types.all(|t| !ty::type_needs_infer(*t)));
 
     // Polytype of the function item (may have type params)
     let fn_tpt = ty::lookup_item_type(tcx, def_id);
@@ -280,9 +276,9 @@ pub fn trans_fn_ref_with_vtables(
     // We need to do a bunch of special handling for default methods.
     // We need to modify the def_id and our substs in order to monomorphize
     // the function.
-    let (is_default, def_id, substs, self_vtables, vtables) =
+    let (is_default, def_id, substs, vtables) =
         match ty::provided_source(tcx, def_id) {
-        None => (false, def_id, substs, None, vtables),
+        None => (false, def_id, substs, vtables),
         Some(source_id) => {
             // There are two relevant substitutions when compiling
             // default methods. First, there is the substitution for
@@ -305,7 +301,7 @@ pub fn trans_fn_ref_with_vtables(
 
             // Compute the first substitution
             let first_subst = make_substs_for_receiver_types(
-                tcx, impl_id, &*trait_ref, &*method);
+                tcx, &*trait_ref, &*method);
 
             // And compose them
             let new_substs = first_subst.subst(tcx, &substs);
@@ -318,16 +314,14 @@ pub fn trans_fn_ref_with_vtables(
                    first_subst.repr(tcx), new_substs.repr(tcx),
                    vtables.repr(tcx));
 
-            let (param_vtables, self_vtables) =
-                resolve_default_method_vtables(bcx, impl_id,
-                                               &*method, &substs, vtables);
+            let param_vtables =
+                resolve_default_method_vtables(bcx, impl_id, &substs, vtables);
 
             debug!("trans_fn_with_vtables - default method: \
-                    self_vtable = {}, param_vtables = {}",
-                   self_vtables.repr(tcx), param_vtables.repr(tcx));
+                    param_vtables = {}",
+                   param_vtables.repr(tcx));
 
-            (true, source_id,
-             new_substs, Some(self_vtables), param_vtables)
+            (true, source_id, new_substs, param_vtables)
         }
     };
 
@@ -345,7 +339,7 @@ pub fn trans_fn_ref_with_vtables(
     // intrinsic, or is a default method.  In particular, if we see an
     // intrinsic that is inlined from a different crate, we want to reemit the
     // intrinsic instead of trying to call it in the other crate.
-    let must_monomorphise = if substs.tps.len() > 0 || is_default {
+    let must_monomorphise = if !substs.types.is_empty() || is_default {
         true
     } else if def_id.krate == ast::LOCAL_CRATE {
         let map_node = session::expect(
@@ -375,8 +369,7 @@ pub fn trans_fn_ref_with_vtables(
 
         let (val, must_cast) =
             monomorphize::monomorphic_fn(ccx, def_id, &substs,
-                                         vtables, self_vtables,
-                                         opt_ref_id);
+                                         vtables, opt_ref_id);
         let mut val = val;
         if must_cast && node != ExprId(0) {
             // Monotype of the REFERENCE to the function (type params
@@ -498,7 +491,7 @@ pub fn trans_lang_call<'a>(
                                                                     did,
                                                                     0,
                                                                     subst::Substs::empty(),
-                                                                    Vec::new())
+                                                                    VecPerParamSpace::empty())
                              },
                              ArgVals(args),
                              dest)
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index 9bd6b8ed361..565ec7d8247 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -181,25 +181,18 @@ pub type ExternMap = HashMap<String, ValueRef>;
 pub struct param_substs {
     pub substs: subst::Substs,
     pub vtables: typeck::vtable_res,
-    pub self_vtables: Option<typeck::vtable_param_res>
 }
 
 impl param_substs {
     pub fn empty() -> param_substs {
         param_substs {
             substs: subst::Substs::trans_empty(),
-            vtables: Vec::new(),
-            self_vtables: None
+            vtables: subst::VecPerParamSpace::empty(),
         }
     }
 
     pub fn validate(&self) {
-        for t in self.substs.tps.iter() {
-            assert!(!ty::type_needs_infer(*t));
-        }
-        for t in self.substs.self_ty.iter() {
-            assert!(!ty::type_needs_infer(*t));
-        }
+        assert!(self.substs.types.all(|t| !ty::type_needs_infer(*t)));
     }
 }
 
@@ -738,7 +731,7 @@ pub fn node_id_substs(bcx: &Block,
         }
     };
 
-    if !substs.tps.iter().all(|t| !ty::type_needs_infer(*t)) {
+    if substs.types.any(|t| ty::type_needs_infer(*t)) {
         bcx.sess().bug(
             format!("type parameters for node {:?} include inference types: \
                      {}",
@@ -752,14 +745,14 @@ pub fn node_id_substs(bcx: &Block,
 pub fn node_vtables(bcx: &Block, id: typeck::MethodCall)
                  -> typeck::vtable_res {
     bcx.tcx().vtable_map.borrow().find(&id).map(|vts| {
-        resolve_vtables_in_fn_ctxt(bcx.fcx, vts.as_slice())
-    }).unwrap_or_else(|| Vec::new())
+        resolve_vtables_in_fn_ctxt(bcx.fcx, vts)
+    }).unwrap_or_else(|| subst::VecPerParamSpace::empty())
 }
 
 // Apply the typaram substitutions in the FunctionContext to some
 // vtables. This should eliminate any vtable_params.
 pub fn resolve_vtables_in_fn_ctxt(fcx: &FunctionContext,
-                                  vts: &[typeck::vtable_param_res])
+                                  vts: &typeck::vtable_res)
                                   -> typeck::vtable_res {
     resolve_vtables_under_param_substs(fcx.ccx.tcx(),
                                        fcx.param_substs,
@@ -768,20 +761,21 @@ pub fn resolve_vtables_in_fn_ctxt(fcx: &FunctionContext,
 
 pub fn resolve_vtables_under_param_substs(tcx: &ty::ctxt,
                                           param_substs: &param_substs,
-                                          vts: &[typeck::vtable_param_res])
-                                          -> typeck::vtable_res {
-    vts.iter().map(|ds| {
-      resolve_param_vtables_under_param_substs(tcx,
-                                               param_substs,
-                                               ds.as_slice())
-    }).collect()
-}
-
-pub fn resolve_param_vtables_under_param_substs(
-    tcx: &ty::ctxt,
-    param_substs: &param_substs,
-    ds: &[typeck::vtable_origin])
-    -> typeck::vtable_param_res {
+                                          vts: &typeck::vtable_res)
+                                          -> typeck::vtable_res
+{
+    vts.map(|ds| {
+        resolve_param_vtables_under_param_substs(tcx,
+                                                 param_substs,
+                                                 ds)
+    })
+}
+
+pub fn resolve_param_vtables_under_param_substs(tcx: &ty::ctxt,
+                                                param_substs: &param_substs,
+                                                ds: &typeck::vtable_param_res)
+                                                -> typeck::vtable_param_res
+{
     ds.iter().map(|d| {
         resolve_vtable_under_param_substs(tcx,
                                           param_substs,
@@ -794,17 +788,20 @@ pub fn resolve_param_vtables_under_param_substs(
 pub fn resolve_vtable_under_param_substs(tcx: &ty::ctxt,
                                          param_substs: &param_substs,
                                          vt: &typeck::vtable_origin)
-                                         -> typeck::vtable_origin {
+                                         -> typeck::vtable_origin
+{
     match *vt {
         typeck::vtable_static(trait_id, ref vtable_substs, ref sub) => {
             let vtable_substs = vtable_substs.substp(tcx, param_substs);
             typeck::vtable_static(
-                trait_id, vtable_substs,
-                resolve_vtables_under_param_substs(tcx, param_substs, sub.as_slice()))
+                trait_id,
+                vtable_substs,
+                resolve_vtables_under_param_substs(tcx, param_substs, sub))
         }
         typeck::vtable_param(n_param, n_bound) => {
             find_vtable(tcx, param_substs, n_param, n_bound)
         }
+        typeck::vtable_error => typeck::vtable_error
     }
 }
 
@@ -816,12 +813,8 @@ pub fn find_vtable(tcx: &ty::ctxt,
     debug!("find_vtable(n_param={:?}, n_bound={}, ps={})",
            n_param, n_bound, ps.repr(tcx));
 
-    let param_bounds = match n_param {
-        typeck::param_self => ps.self_vtables.as_ref().expect("self vtables missing"),
-        typeck::param_numbered(n) => {
-            ps.vtables.get(n)
-        }
-    };
+    let param_bounds = ps.vtables.get(n_param.space,
+                                      n_param.index);
     param_bounds.get(n_bound).clone()
 }
 
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index 0b10db56cc4..b5a002fd824 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -520,10 +520,11 @@ impl TypeMap {
 
             // Maybe check that there is no self type here
 
-            if substs.tps.len() > 0 {
+            let tps = substs.types.get_vec(subst::TypeSpace);
+            if tps.len() > 0 {
                 output.push_char('<');
 
-                for &type_parameter in substs.tps.iter() {
+                for &type_parameter in tps.iter() {
                     let param_type_id = type_map.get_unique_type_id_of_type(cx, type_parameter);
                     let param_type_id = type_map.get_unique_type_id_as_string(param_type_id);
                     output.push_str(param_type_id.as_slice());
@@ -1209,7 +1210,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
                                file_metadata: DIFile,
                                name_to_append_suffix_to: &mut String)
                                -> DIArray {
-        let self_type = param_substs.substs.self_ty;
+        let self_type = param_substs.substs.self_ty();
 
         // Only true for static default methods:
         let has_self_type = self_type.is_some();
@@ -1263,7 +1264,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
         }
 
         // Handle other generic parameters
-        let actual_types = &param_substs.substs.tps;
+        let actual_types = param_substs.substs.types.get_vec(subst::FnSpace);
         for (index, &ast::TyParam{ ident: ident, .. }) in generics.ty_params.iter().enumerate() {
             let actual_type = *actual_types.get(index);
             // Add actual type name to <...> clause of function name
@@ -2733,13 +2734,11 @@ fn trait_metadata(cx: &CrateContext,
     let ident_string = token::get_name(last.name());
     let mut name = ppaux::trait_store_to_str(cx.tcx(), trait_store);
     name.push_str(ident_string.get());
+
     // Add type and region parameters
-    let name = ppaux::parameterized(cx.tcx(),
-                                    name.as_slice(),
-                                    &substs.regions,
-                                    substs.tps.as_slice(),
-                                    def_id,
-                                    true);
+    let trait_def = ty::lookup_trait_def(cx.tcx(), def_id);
+    let name = ppaux::parameterized(cx.tcx(), name.as_slice(),
+                                    substs, &trait_def.generics);
 
     let (containing_scope, definition_span) = get_namespace_and_span_for_item(cx, def_id);
 
diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs
index 55ea4a3a0bb..095645f4d6b 100644
--- a/src/librustc/middle/trans/inline.rs
+++ b/src/librustc/middle/trans/inline.rs
@@ -118,18 +118,18 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
             ccx.external.borrow_mut().insert(fn_id, Some(mth.id));
             ccx.external_srcs.borrow_mut().insert(mth.id, fn_id);
 
-          ccx.stats.n_inlines.set(ccx.stats.n_inlines.get() + 1);
+            ccx.stats.n_inlines.set(ccx.stats.n_inlines.get() + 1);
 
-          // If this is a default method, we can't look up the
-          // impl type. But we aren't going to translate anyways, so don't.
-          if is_provided { return local_def(mth.id); }
+            // If this is a default method, we can't look up the
+            // impl type. But we aren't going to translate anyways, so don't.
+            if is_provided { return local_def(mth.id); }
 
             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();
+            let unparameterized =
+                impl_tpt.generics.types.is_empty() &&
+                mth.generics.ty_params.is_empty();
 
-          if num_type_params == 0 {
+          if unparameterized {
               let llfn = get_item_val(ccx, mth.id);
               trans_fn(ccx, &*mth.decl, &*mth.body, llfn,
                        &param_substs::empty(), mth.id, []);
diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs
index 1edd802f144..5f37365e743 100644
--- a/src/librustc/middle/trans/intrinsic.rs
+++ b/src/librustc/middle/trans/intrinsic.rs
@@ -14,6 +14,7 @@ use arena::TypedArena;
 use lib::llvm::{SequentiallyConsistent, Acquire, Release, Xchg};
 use lib::llvm::{ValueRef, Pointer, Array, Struct};
 use lib;
+use middle::subst::FnSpace;
 use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::common::*;
@@ -295,7 +296,7 @@ pub fn trans_intrinsic(ccx: &CrateContext,
             RetVoid(bcx);
         }
         "size_of" => {
-            let tp_ty = *substs.substs.tps.get(0);
+            let tp_ty = *substs.substs.types.get(FnSpace, 0);
             let lltp_ty = type_of::type_of(ccx, tp_ty);
             Ret(bcx, C_uint(ccx, machine::llsize_of_real(ccx, lltp_ty) as uint));
         }
@@ -305,7 +306,7 @@ pub fn trans_intrinsic(ccx: &CrateContext,
             // if the value is non-immediate. Note that, with
             // intrinsics, there are no argument cleanups to
             // concern ourselves with, so we can use an rvalue datum.
-            let tp_ty = *substs.substs.tps.get(0);
+            let tp_ty = *substs.substs.types.get(FnSpace, 0);
             let mode = appropriate_rvalue_mode(ccx, tp_ty);
             let src = Datum {val: get_param(decl, first_real_arg + 1u),
                              ty: tp_ty,
@@ -314,17 +315,17 @@ pub fn trans_intrinsic(ccx: &CrateContext,
             RetVoid(bcx);
         }
         "min_align_of" => {
-            let tp_ty = *substs.substs.tps.get(0);
+            let tp_ty = *substs.substs.types.get(FnSpace, 0);
             let lltp_ty = type_of::type_of(ccx, tp_ty);
             Ret(bcx, C_uint(ccx, machine::llalign_of_min(ccx, lltp_ty) as uint));
         }
         "pref_align_of"=> {
-            let tp_ty = *substs.substs.tps.get(0);
+            let tp_ty = *substs.substs.types.get(FnSpace, 0);
             let lltp_ty = type_of::type_of(ccx, tp_ty);
             Ret(bcx, C_uint(ccx, machine::llalign_of_pref(ccx, lltp_ty) as uint));
         }
         "get_tydesc" => {
-            let tp_ty = *substs.substs.tps.get(0);
+            let tp_ty = *substs.substs.types.get(FnSpace, 0);
             let static_ti = get_tydesc(ccx, tp_ty);
             glue::lazily_emit_visit_glue(ccx, &*static_ti);
 
@@ -339,7 +340,7 @@ pub fn trans_intrinsic(ccx: &CrateContext,
         "type_id" => {
             let hash = ty::hash_crate_independent(
                 ccx.tcx(),
-                *substs.substs.tps.get(0),
+                *substs.substs.types.get(FnSpace, 0),
                 &ccx.link_meta.crate_hash);
             // NB: This needs to be kept in lockstep with the TypeId struct in
             //     libstd/unstable/intrinsics.rs
@@ -354,7 +355,7 @@ pub fn trans_intrinsic(ccx: &CrateContext,
             }
         }
         "init" => {
-            let tp_ty = *substs.substs.tps.get(0);
+            let tp_ty = *substs.substs.types.get(FnSpace, 0);
             let lltp_ty = type_of::type_of(ccx, tp_ty);
             match bcx.fcx.llretptr.get() {
                 Some(ptr) => { Store(bcx, C_null(lltp_ty), ptr); RetVoid(bcx); }
@@ -364,7 +365,7 @@ pub fn trans_intrinsic(ccx: &CrateContext,
         }
         "uninit" => {
             // Do nothing, this is effectively a no-op
-            let retty = *substs.substs.tps.get(0);
+            let retty = *substs.substs.types.get(FnSpace, 0);
             if type_is_immediate(ccx, retty) && !return_type_is_void(ccx, retty) {
                 unsafe {
                     Ret(bcx, lib::llvm::llvm::LLVMGetUndef(type_of(ccx, retty).to_ref()));
@@ -377,8 +378,8 @@ pub fn trans_intrinsic(ccx: &CrateContext,
             RetVoid(bcx);
         }
         "transmute" => {
-            let (in_type, out_type) = (*substs.substs.tps.get(0),
-                                       *substs.substs.tps.get(1));
+            let (in_type, out_type) = (*substs.substs.types.get(FnSpace, 0),
+                                       *substs.substs.types.get(FnSpace, 1));
             let llintype = type_of::type_of(ccx, in_type);
             let llouttype = type_of::type_of(ccx, out_type);
 
@@ -447,11 +448,11 @@ pub fn trans_intrinsic(ccx: &CrateContext,
             }
         }
         "needs_drop" => {
-            let tp_ty = *substs.substs.tps.get(0);
+            let tp_ty = *substs.substs.types.get(FnSpace, 0);
             Ret(bcx, C_bool(ccx, ty::type_needs_drop(ccx.tcx(), tp_ty)));
         }
         "owns_managed" => {
-            let tp_ty = *substs.substs.tps.get(0);
+            let tp_ty = *substs.substs.types.get(FnSpace, 0);
             Ret(bcx, C_bool(ccx, ty::type_contents(ccx.tcx(), tp_ty).owns_managed()));
         }
         "visit_tydesc" => {
@@ -468,19 +469,26 @@ pub fn trans_intrinsic(ccx: &CrateContext,
             Ret(bcx, lladdr);
         }
         "copy_nonoverlapping_memory" => {
-            copy_intrinsic(bcx, false, false, *substs.substs.tps.get(0))
+            copy_intrinsic(bcx, false, false, *substs.substs.types.get(FnSpace, 0))
         }
         "copy_memory" => {
-            copy_intrinsic(bcx, true, false, *substs.substs.tps.get(0))
+            copy_intrinsic(bcx, true, false, *substs.substs.types.get(FnSpace, 0))
         }
         "set_memory" => {
-            memset_intrinsic(bcx, false, *substs.substs.tps.get(0))
+            memset_intrinsic(bcx, false, *substs.substs.types.get(FnSpace, 0))
         }
 
-        "volatile_copy_nonoverlapping_memory" =>
-            copy_intrinsic(bcx, false, true, *substs.substs.tps.get(0)),
-        "volatile_copy_memory" => copy_intrinsic(bcx, true, true, *substs.substs.tps.get(0)),
-        "volatile_set_memory" => memset_intrinsic(bcx, true, *substs.substs.tps.get(0)),
+        "volatile_copy_nonoverlapping_memory" => {
+            copy_intrinsic(bcx, false, true, *substs.substs.types.get(FnSpace, 0))
+        }
+
+        "volatile_copy_memory" => {
+            copy_intrinsic(bcx, true, true, *substs.substs.types.get(FnSpace, 0))
+        }
+
+        "volatile_set_memory" => {
+            memset_intrinsic(bcx, true, *substs.substs.types.get(FnSpace, 0))
+        }
 
         "ctlz8" => count_zeros_intrinsic(bcx, "llvm.ctlz.i8"),
         "ctlz16" => count_zeros_intrinsic(bcx, "llvm.ctlz.i16"),
diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs
index 59387c549db..bde48b94473 100644
--- a/src/librustc/middle/trans/meth.rs
+++ b/src/librustc/middle/trans/meth.rs
@@ -155,22 +155,6 @@ pub fn trans_static_method_callee(bcx: &Block,
 
     ty::populate_implementations_for_trait_if_necessary(bcx.tcx(), trait_id);
 
-    // When we translate a static fn defined in a trait like:
-    //
-    //   trait<T1...Tn> Trait {
-    //       fn foo<M1...Mn>(...) {...}
-    //   }
-    //
-    // this winds up being translated as something like:
-    //
-    //   fn foo<T1...Tn,self: Trait<T1...Tn>,M1...Mn>(...) {...}
-    //
-    // So when we see a call to this function foo, we have to figure
-    // out which impl the `Trait<T1...Tn>` bound on the type `self` was
-    // bound to.
-    let bound_index = ty::lookup_trait_def(bcx.tcx(), trait_id).
-        generics.type_param_defs().len();
-
     let mname = if method_id.krate == ast::LOCAL_CRATE {
         match bcx.tcx().map.get(method_id.node) {
             ast_map::NodeTraitMethod(method) => {
@@ -189,18 +173,19 @@ pub fn trans_static_method_callee(bcx: &Block,
             name={}", method_id, expr_id, token::get_name(mname));
 
     let vtable_key = MethodCall::expr(expr_id);
-    let vtbls = resolve_vtables_in_fn_ctxt(bcx.fcx, ccx.tcx.vtable_map.borrow()
-                                                       .get(&vtable_key).as_slice());
+    let vtbls = resolve_vtables_in_fn_ctxt(
+        bcx.fcx,
+        ccx.tcx.vtable_map.borrow().get(&vtable_key));
 
-    match vtbls.move_iter().nth(bound_index).unwrap().move_iter().nth(0).unwrap() {
-        typeck::vtable_static(impl_did, rcvr_substs, rcvr_origins) => {
-            assert!(rcvr_substs.tps.iter().all(|t| !ty::type_needs_infer(*t)));
+    match *vtbls.get_self().unwrap().get(0) {
+        typeck::vtable_static(impl_did, ref rcvr_substs, ref rcvr_origins) => {
+            assert!(rcvr_substs.types.all(|t| !ty::type_needs_infer(*t)));
 
             let mth_id = method_with_name(ccx, impl_did, mname);
             let (callee_substs, callee_origins) =
                 combine_impl_and_methods_tps(
-                    bcx, mth_id, ExprId(expr_id),
-                    rcvr_substs, rcvr_origins);
+                    bcx, ExprId(expr_id),
+                    (*rcvr_substs).clone(), (*rcvr_origins).clone());
 
             let llfn = trans_fn_ref_with_vtables(bcx, mth_id, ExprId(expr_id),
                                                  callee_substs,
@@ -252,8 +237,7 @@ fn trans_monomorphized_callee<'a>(bcx: &'a Block<'a>,
           // those from the impl and those from the method:
           let (callee_substs, callee_origins) =
               combine_impl_and_methods_tps(
-                  bcx, mth_id,  MethodCall(method_call),
-                  rcvr_substs, rcvr_origins);
+                  bcx, MethodCall(method_call), rcvr_substs, rcvr_origins);
 
           // translate the function
           let llfn = trans_fn_ref_with_vtables(bcx,
@@ -265,13 +249,17 @@ fn trans_monomorphized_callee<'a>(bcx: &'a Block<'a>,
           Callee { bcx: bcx, data: Fn(llfn) }
       }
       typeck::vtable_param(..) => {
-          fail!("vtable_param left in monomorphized function's vtable substs");
+          bcx.tcx().sess.bug(
+              "vtable_param left in monomorphized function's vtable substs");
+      }
+      typeck::vtable_error => {
+          bcx.tcx().sess.bug(
+              "vtable_error left in monomorphized function's vtable substs");
       }
     }
 }
 
 fn combine_impl_and_methods_tps(bcx: &Block,
-                                mth_did: ast::DefId,
                                 node: ExprOrMethodCall,
                                 rcvr_substs: subst::Substs,
                                 rcvr_origins: typeck::vtable_res)
@@ -295,38 +283,33 @@ fn combine_impl_and_methods_tps(bcx: &Block,
      */
 
     let ccx = bcx.ccx();
-    let method = ty::method(ccx.tcx(), mth_did);
-    let n_m_tps = method.generics.type_param_defs().len();
-    let node_substs = node_id_substs(bcx, node);
-    debug!("rcvr_substs={:?}", rcvr_substs.repr(ccx.tcx()));
-    debug!("node_substs={:?}", node_substs.repr(ccx.tcx()));
-    let rcvr_self_ty = rcvr_substs.self_ty;
-    let mut tps = rcvr_substs.tps;
-    {
-        let start = node_substs.tps.len() - n_m_tps;
-        tps.extend(node_substs.tps.move_iter().skip(start));
-    }
-    debug!("n_m_tps={:?}", n_m_tps);
-    debug!("tps={}", tps.repr(ccx.tcx()));
-
 
-    // Now, do the same work for the vtables.  The vtables might not
-    // exist, in which case we need to make them.
     let vtable_key = match node {
         ExprId(id) => MethodCall::expr(id),
         MethodCall(method_call) => method_call
     };
-    let mut vtables = rcvr_origins;
-    let vt = node_vtables(bcx, vtable_key);
-    let start = vt.len() - n_m_tps;
-    vtables.extend(vt.move_iter().skip(start));
+    let node_substs = node_id_substs(bcx, node);
+    let node_vtables = node_vtables(bcx, vtable_key);
+
+    debug!("rcvr_substs={:?}", rcvr_substs.repr(ccx.tcx()));
+    debug!("node_substs={:?}", node_substs.repr(ccx.tcx()));
 
+    // Break apart the type parameters from the node and type
+    // parameters from the receiver.
+    let (_, _, node_method) = node_substs.types.split();
+    let (rcvr_type, rcvr_self, rcvr_method) = rcvr_substs.types.clone().split();
+    assert!(rcvr_method.is_empty());
     let ty_substs = subst::Substs {
-        tps: tps,
         regions: subst::ErasedRegions,
-        self_ty: rcvr_self_ty
+        types: subst::VecPerParamSpace::new(rcvr_type, rcvr_self, node_method)
     };
 
+    // Now do the same work for the vtables.
+    let (rcvr_type, rcvr_self, rcvr_method) = rcvr_origins.split();
+    let (_, _, node_method) = node_vtables.split();
+    assert!(rcvr_method.is_empty());
+    let vtables = subst::VecPerParamSpace::new(rcvr_type, rcvr_self, node_method);
+
     (ty_substs, vtables)
 }
 
@@ -426,7 +409,12 @@ pub fn trans_trait_callee_from_llval<'a>(bcx: &'a Block<'a>,
 fn get_vtable(bcx: &Block,
               self_ty: ty::t,
               origins: typeck::vtable_param_res)
-              -> ValueRef {
+              -> ValueRef
+{
+    debug!("get_vtable(self_ty={}, origins={})",
+           self_ty.repr(bcx.tcx()),
+           origins.repr(bcx.tcx()));
+
     let ccx = bcx.ccx();
     let _icx = push_ctxt("meth::get_vtable");
 
@@ -503,8 +491,9 @@ fn emit_vtable_methods(bcx: &Block,
         debug!("(making impl vtable) emitting method {} at subst {}",
                m.repr(tcx),
                substs.repr(tcx));
-        if m.generics.has_type_params() ||
-           ty::type_has_self(ty::mk_bare_fn(tcx, m.fty.clone())) {
+        if m.generics.has_type_params(subst::FnSpace) ||
+           ty::type_has_self(ty::mk_bare_fn(tcx, m.fty.clone()))
+        {
             debug!("(making impl vtable) method has self or type params: {}",
                    token::get_ident(ident));
             C_null(Type::nil(ccx).ptr_to())
@@ -551,7 +540,7 @@ pub fn trans_trait_cast<'a>(bcx: &'a Block<'a>,
         let vtable_map = ccx.tcx.vtable_map.borrow();
         resolve_param_vtables_under_param_substs(ccx.tcx(),
             bcx.fcx.param_substs,
-            vtable_map.get(&MethodCall::expr(id)).get(0).as_slice())
+            vtable_map.get(&MethodCall::expr(id)).get_self().unwrap())
     };
     let vtable = get_vtable(bcx, v_ty, origins);
     let llvtabledest = GEPi(bcx, lldest, [0u, abi::trt_field_vtable]);
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index 0315815266e..125fa6828c5 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -33,32 +33,27 @@ pub fn monomorphic_fn(ccx: &CrateContext,
                       fn_id: ast::DefId,
                       real_substs: &subst::Substs,
                       vtables: typeck::vtable_res,
-                      self_vtables: Option<typeck::vtable_param_res>,
                       ref_id: Option<ast::NodeId>)
     -> (ValueRef, bool) {
     debug!("monomorphic_fn(\
             fn_id={}, \
             real_substs={}, \
             vtables={}, \
-            self_vtable={}, \
             ref_id={:?})",
            fn_id.repr(ccx.tcx()),
            real_substs.repr(ccx.tcx()),
            vtables.repr(ccx.tcx()),
-           self_vtables.repr(ccx.tcx()),
            ref_id);
 
-    assert!(real_substs.tps.iter().all(|t| {
+    assert!(real_substs.types.all(|t| {
         !ty::type_needs_infer(*t) && !ty::type_has_params(*t)
     }));
 
     let _icx = push_ctxt("monomorphic_fn");
 
-    let substs_iter = real_substs.self_ty.iter().chain(real_substs.tps.iter());
-    let param_ids: Vec<ty::t> = substs_iter.map(|t| *t).collect();
     let hash_id = MonoId {
         def: fn_id,
-        params: param_ids
+        params: real_substs.types.clone()
     };
 
     match ccx.monomorphized.borrow().find(&hash_id) {
@@ -73,7 +68,6 @@ pub fn monomorphic_fn(ccx: &CrateContext,
     let psubsts = param_substs {
         substs: (*real_substs).clone(),
         vtables: vtables,
-        self_vtables: self_vtables
     };
 
     debug!("monomorphic_fn(\
@@ -87,10 +81,6 @@ pub fn monomorphic_fn(ccx: &CrateContext,
     let tpt = ty::lookup_item_type(ccx.tcx(), fn_id);
     let llitem_ty = tpt.ty;
 
-    // We need to do special handling of the substitutions if we are
-    // calling a static provided method. This is sort of unfortunate.
-    let mut is_static_provided = None;
-
     let map_node = session::expect(
         ccx.sess(),
         ccx.tcx.map.find(fn_id.node),
@@ -108,55 +98,11 @@ pub fn monomorphic_fn(ccx: &CrateContext,
                 return (get_item_val(ccx, fn_id.node), true);
             }
         }
-        ast_map::NodeTraitMethod(method) => {
-            match *method {
-                ast::Provided(m) => {
-                    // If this is a static provided method, indicate that
-                    // and stash the number of params on the method.
-                    if m.explicit_self.node == ast::SelfStatic {
-                        is_static_provided = Some(m.generics.ty_params.len());
-                    }
-                }
-                _ => {}
-            }
-        }
         _ => {}
     }
 
     debug!("monomorphic_fn about to subst into {}", llitem_ty.repr(ccx.tcx()));
-    let mono_ty = match is_static_provided {
-        None => llitem_ty.subst(ccx.tcx(), real_substs),
-        Some(num_method_ty_params) => {
-            // Static default methods are a little unfortunate, in
-            // that the "internal" and "external" type of them differ.
-            // Internally, the method body can refer to Self, but the
-            // externally visible type of the method has a type param
-            // inserted in between the trait type params and the
-            // method type params. The substs that we are given are
-            // the proper substs *internally* to the method body, so
-            // we have to use those when compiling it.
-            //
-            // In order to get the proper substitution to use on the
-            // type of the method, we pull apart the substitution and
-            // stick a substitution for the self type in.
-            // This is a bit unfortunate.
-
-            let idx = real_substs.tps.len() - num_method_ty_params;
-            let mut tps = Vec::new();
-            tps.push_all(real_substs.tps.slice(0, idx));
-            tps.push(real_substs.self_ty.unwrap());
-            tps.push_all(real_substs.tps.tailn(idx));
-
-            let substs = subst::Substs { regions: subst::ErasedRegions,
-                                         self_ty: None,
-                                         tps: tps };
-
-            debug!("static default: changed substitution to {}",
-                   substs.repr(ccx.tcx()));
-
-            llitem_ty.subst(ccx.tcx(), &substs)
-        }
-    };
+    let mono_ty = llitem_ty.subst(ccx.tcx(), real_substs);
 
     ccx.stats.n_monos.set(ccx.stats.n_monos.get() + 1);
 
@@ -306,7 +252,7 @@ pub struct MonoParamId {
 #[deriving(PartialEq, Eq, Hash)]
 pub struct MonoId {
     pub def: ast::DefId,
-    pub params: Vec<ty::t>
+    pub params: subst::VecPerParamSpace<ty::t>
 }
 
 pub fn make_vtable_id(_ccx: &CrateContext,
@@ -316,7 +262,7 @@ pub fn make_vtable_id(_ccx: &CrateContext,
         &typeck::vtable_static(impl_id, ref substs, _) => {
             MonoId {
                 def: impl_id,
-                params: substs.tps.iter().map(|subst| *subst).collect()
+                params: substs.types.clone()
             }
         }
 
diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs
index 3b469a1d110..f67fc57bb44 100644
--- a/src/librustc/middle/trans/reflect.rs
+++ b/src/librustc/middle/trans/reflect.rs
@@ -366,7 +366,6 @@ impl<'a, 'b> Reflector<'a, 'b> {
               let extra = vec!(self.c_uint(p.idx));
               self.visit("param", extra.as_slice())
           }
-          ty::ty_self(..) => self.leaf("self")
         }
     }
 
diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs
index 3ba3954e445..4701a9e3225 100644
--- a/src/librustc/middle/trans/type_of.rs
+++ b/src/librustc/middle/trans/type_of.rs
@@ -15,7 +15,6 @@ use middle::trans::adt;
 use middle::trans::common::*;
 use middle::trans::foreign;
 use middle::ty;
-use util::ppaux;
 use util::ppaux::Repr;
 
 use middle::trans::type_::Type;
@@ -152,7 +151,7 @@ pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type {
             }
         }
 
-        ty::ty_self(_) | ty::ty_infer(..) | ty::ty_param(..) |
+        ty::ty_infer(..) | ty::ty_param(..) |
         ty::ty_err(..) | ty::ty_vec(_, None) | ty::ty_str => {
             cx.sess().bug(format!("fictitious type {:?} in sizing_type_of()",
                                   ty::get(t).sty).as_slice())
@@ -205,7 +204,8 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type {
         // avoids creating more than one copy of the enum when one
         // of the enum's variants refers to the enum itself.
         let repr = adt::represent_type(cx, t);
-        let name = llvm_type_name(cx, an_enum, did, substs.tps.as_slice());
+        let tps = substs.types.get_vec(subst::TypeSpace);
+        let name = llvm_type_name(cx, an_enum, did, tps);
         adt::incomplete_type_of(cx, &*repr, name.as_slice())
       }
       ty::ty_box(typ) => {
@@ -260,17 +260,14 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type {
               // in *after* placing it into the type cache. This prevents
               // infinite recursion with recursive struct types.
               let repr = adt::represent_type(cx, t);
-              let name = llvm_type_name(cx,
-                                        a_struct,
-                                        did,
-                                        substs.tps.as_slice());
+              let tps = substs.types.get_vec(subst::TypeSpace);
+              let name = llvm_type_name(cx, a_struct, did, tps);
               adt::incomplete_type_of(cx, &*repr, name.as_slice())
           }
       }
 
       ty::ty_vec(_, None) => cx.sess().bug("type_of with unsized ty_vec"),
       ty::ty_str => cx.sess().bug("type_of with unsized (bare) ty_str"),
-      ty::ty_self(..) => cx.sess().unimpl("type_of with ty_self"),
       ty::ty_infer(..) => cx.sess().bug("type_of with ty_infer"),
       ty::ty_param(..) => cx.sess().bug("type_of with ty_param"),
       ty::ty_err(..) => cx.sess().bug("type_of with ty_err")
@@ -301,19 +298,17 @@ pub enum named_ty { a_struct, an_enum }
 pub fn llvm_type_name(cx: &CrateContext,
                       what: named_ty,
                       did: ast::DefId,
-                      tps: &[ty::t])
-                      -> String {
+                      tps: &Vec<ty::t>)
+                      -> String
+{
     let name = match what {
         a_struct => { "struct" }
         an_enum => { "enum" }
     };
-    let tstr = ppaux::parameterized(cx.tcx(),
-                                    ty::item_path_str(cx.tcx(),
-                                                      did).as_slice(),
-                                    &subst::ErasedRegions,
-                                    tps,
-                                    did,
-                                    false);
+
+    let base = ty::item_path_str(cx.tcx(), did);
+    let strings: Vec<String> = tps.iter().map(|t| t.repr(cx.tcx())).collect();
+    let tstr = format!("{}<{}>", base, strings);
     if did.krate == 0 {
         format!("{}.{}", name, tstr)
     } else {
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 2a0873e327b..e3d94c73bb4 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -24,7 +24,7 @@ use middle::freevars;
 use middle::resolve;
 use middle::resolve_lifetime;
 use middle::subst;
-use middle::subst::{Subst, Substs};
+use middle::subst::{Subst, Substs, VecPerParamSpace};
 use middle::ty;
 use middle::typeck;
 use middle::typeck::MethodCall;
@@ -58,7 +58,6 @@ use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::parse::token::InternedString;
 use syntax::{ast, ast_map};
-use syntax::owned_slice::OwnedSlice;
 use syntax::util::small_vector::SmallVector;
 use std::collections::enum_set::{EnumSet, CLike};
 
@@ -190,9 +189,8 @@ pub enum ast_ty_to_ty_cache_entry {
 
 #[deriving(Clone, PartialEq, Decodable, Encodable)]
 pub struct ItemVariances {
-    pub self_param: Option<Variance>,
-    pub type_params: OwnedSlice<Variance>,
-    pub region_params: OwnedSlice<Variance>
+    pub types: VecPerParamSpace<Variance>,
+    pub regions: VecPerParamSpace<Variance>,
 }
 
 #[deriving(Clone, PartialEq, Decodable, Encodable, Show)]
@@ -455,7 +453,8 @@ pub struct FnSig {
 }
 
 #[deriving(Clone, PartialEq, Eq, Hash)]
-pub struct param_ty {
+pub struct ParamTy {
+    pub space: subst::ParamSpace,
     pub idx: uint,
     pub def_id: DefId
 }
@@ -466,7 +465,10 @@ pub enum Region {
     // Region bound in a type or fn declaration which will be
     // substituted 'early' -- that is, at the same time when type
     // parameters are substituted.
-    ReEarlyBound(/* param id */ ast::NodeId, /*index*/ uint, ast::Name),
+    ReEarlyBound(/* param id */ ast::NodeId,
+                 subst::ParamSpace,
+                 /*index*/ uint,
+                 ast::Name),
 
     // Region bound in a function scope, which will be substituted when the
     // function is called. The first argument must be the `binder_id` of
@@ -713,10 +715,7 @@ pub enum sty {
     ty_struct(DefId, Substs),
     ty_tup(Vec<t>),
 
-    ty_param(param_ty), // type parameter
-    ty_self(DefId), /* special, implicit `self` type parameter;
-                      * def_id is the id of the trait */
-
+    ty_param(ParamTy), // type parameter
     ty_infer(InferTy), // something used only during inference/typeck
     ty_err, // Also only used during inference/typeck, to represent
             // the type of an erroneous expression (helps cut down
@@ -734,7 +733,7 @@ pub struct TyTrait {
 #[deriving(PartialEq, Eq, Hash)]
 pub struct TraitRef {
     pub def_id: DefId,
-    pub substs: Substs
+    pub substs: Substs,
 }
 
 #[deriving(Clone, PartialEq)]
@@ -964,6 +963,8 @@ impl fmt::Show for IntVarValue {
 pub struct TypeParameterDef {
     pub ident: ast::Ident,
     pub def_id: ast::DefId,
+    pub space: subst::ParamSpace,
+    pub index: uint,
     pub bounds: Rc<ParamBounds>,
     pub default: Option<ty::t>
 }
@@ -972,29 +973,26 @@ pub struct TypeParameterDef {
 pub struct RegionParameterDef {
     pub name: ast::Name,
     pub def_id: ast::DefId,
+    pub space: subst::ParamSpace,
+    pub index: uint,
 }
 
-/// Information about the type/lifetime parameters associated with an item.
-/// Analogous to ast::Generics.
+/// Information about the type/lifetime parameters associated with an
+/// item or method. Analogous to ast::Generics.
 #[deriving(Clone)]
 pub struct Generics {
-    /// List of type parameters declared on the item.
-    pub type_param_defs: Rc<Vec<TypeParameterDef>>,
-
-    /// List of region parameters declared on the item.
-    /// For a fn or method, only includes *early-bound* lifetimes.
-    pub region_param_defs: Rc<Vec<RegionParameterDef>>,
+    pub types: VecPerParamSpace<TypeParameterDef>,
+    pub regions: VecPerParamSpace<RegionParameterDef>,
 }
 
 impl Generics {
-    pub fn has_type_params(&self) -> bool {
-        !self.type_param_defs.is_empty()
-    }
-    pub fn type_param_defs<'a>(&'a self) -> &'a [TypeParameterDef] {
-        self.type_param_defs.as_slice()
+    pub fn empty() -> Generics {
+        Generics { types: VecPerParamSpace::empty(),
+                   regions: VecPerParamSpace::empty() }
     }
-    pub fn region_param_defs<'a>(&'a self) -> &'a [RegionParameterDef] {
-        self.region_param_defs.as_slice()
+
+    pub fn has_type_params(&self, space: subst::ParamSpace) -> bool {
+        !self.types.get_vec(space).is_empty()
     }
 }
 
@@ -1018,11 +1016,8 @@ pub struct ParameterEnvironment {
     /// parameters in the same way, this only has an affect on regions.
     pub free_substs: Substs,
 
-    /// Bound on the Self parameter
-    pub self_param_bound: Option<Rc<TraitRef>>,
-
-    /// Bounds on each numbered type parameter
-    pub type_param_bounds: Vec<ParamBounds>,
+    /// Bounds on the various type parameters
+    pub bounds: VecPerParamSpace<ParamBounds>,
 }
 
 /// A polytype.
@@ -1162,7 +1157,10 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
     }
     fn sflags(substs: &Substs) -> uint {
         let mut f = 0u;
-        for tt in substs.tps.iter() { f |= get(*tt).flags; }
+        let mut i = substs.types.iter();
+        for tt in i {
+            f |= get(*tt).flags;
+        }
         match substs.regions {
             subst::ErasedRegions => {}
             subst::NonerasedRegions(ref regions) => {
@@ -1185,9 +1183,14 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
       // so we're doing it this way.
       &ty_bot => flags |= has_ty_bot as uint,
       &ty_err => flags |= has_ty_err as uint,
-      &ty_param(_) => flags |= has_params as uint,
+      &ty_param(ref p) => {
+          if p.space == subst::SelfSpace {
+              flags |= has_self as uint;
+          } else {
+              flags |= has_params as uint;
+          }
+      }
       &ty_infer(_) => flags |= needs_infer as uint,
-      &ty_self(_) => flags |= has_self as uint,
       &ty_enum(_, ref substs) | &ty_struct(_, ref substs) => {
           flags |= sflags(substs);
       }
@@ -1455,10 +1458,16 @@ pub fn mk_float_var(cx: &ctxt, v: FloatVid) -> t { mk_infer(cx, FloatVar(v)) }
 
 pub fn mk_infer(cx: &ctxt, it: InferTy) -> t { mk_t(cx, ty_infer(it)) }
 
-pub fn mk_self(cx: &ctxt, did: ast::DefId) -> t { mk_t(cx, ty_self(did)) }
+pub fn mk_param(cx: &ctxt, space: subst::ParamSpace, n: uint, k: DefId) -> t {
+    mk_t(cx, ty_param(ParamTy { space: space, idx: n, def_id: k }))
+}
+
+pub fn mk_self_type(cx: &ctxt, did: ast::DefId) -> t {
+    mk_param(cx, subst::SelfSpace, 0, did)
+}
 
-pub fn mk_param(cx: &ctxt, n: uint, k: DefId) -> t {
-    mk_t(cx, ty_param(param_ty { idx: n, def_id: k }))
+pub fn mk_param_from_def(cx: &ctxt, def: &TypeParameterDef) -> t {
+    mk_param(cx, def.space, def.index, def.def_id)
 }
 
 pub fn walk_ty(ty: t, f: |t|) {
@@ -1471,15 +1480,17 @@ pub fn maybe_walk_ty(ty: t, f: |t| -> bool) {
     }
     match get(ty).sty {
         ty_nil | ty_bot | ty_bool | ty_char | ty_int(_) | ty_uint(_) | ty_float(_) |
-        ty_str | ty_self(_) |
-        ty_infer(_) | ty_param(_) | ty_err => {}
+        ty_str | ty_infer(_) | ty_param(_) | ty_err => {
+        }
         ty_box(ty) | ty_uniq(ty) => maybe_walk_ty(ty, f),
         ty_ptr(ref tm) | ty_rptr(_, ref tm) | ty_vec(ref tm, _) => {
             maybe_walk_ty(tm.ty, f);
         }
         ty_enum(_, ref substs) | ty_struct(_, ref substs) |
         ty_trait(box TyTrait { ref substs, .. }) => {
-            for subty in (*substs).tps.iter() { maybe_walk_ty(*subty, |x| f(x)); }
+            for subty in (*substs).types.iter() {
+                maybe_walk_ty(*subty, |x| f(x));
+            }
         }
         ty_tup(ref ts) => { for tt in ts.iter() { maybe_walk_ty(*tt, |x| f(x)); } }
         ty_bare_fn(ref ft) => {
@@ -1533,8 +1544,7 @@ pub fn type_needs_subst(ty: t) -> bool {
 }
 
 pub fn trait_ref_contains_error(tref: &ty::TraitRef) -> bool {
-    tref.substs.self_ty.iter().any(|&t| type_is_error(t)) ||
-        tref.substs.tps.iter().any(|&t| type_is_error(t))
+    tref.substs.types.any(|&t| type_is_error(t))
 }
 
 pub fn type_is_ty_var(ty: t) -> bool {
@@ -1548,7 +1558,7 @@ pub fn type_is_bool(ty: t) -> bool { get(ty).sty == ty_bool }
 
 pub fn type_is_self(ty: t) -> bool {
     match get(ty).sty {
-        ty_self(..) => true,
+        ty_param(ref p) => p.space == subst::SelfSpace,
         _ => false
     }
 }
@@ -2103,16 +2113,6 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
                                         tp_def.bounds.trait_bounds.as_slice())
             }
 
-            ty_self(def_id) => {
-                // FIXME(#4678)---self should just be a ty param
-
-                // Self may be bounded if the associated trait has builtin kinds
-                // for supertraits. If so we can use those bounds.
-                let trait_def = lookup_trait_def(cx, def_id);
-                let traits = [trait_def.trait_ref.clone()];
-                kind_bounds_to_contents(cx, trait_def.bounds, traits)
-            }
-
             ty_infer(_) => {
                 // This occurs during coherence, but shouldn't occur at other
                 // times.
@@ -2292,7 +2292,6 @@ pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool {
             ty_infer(_) |
             ty_err |
             ty_param(_) |
-            ty_self(_) |
             ty_vec(_, None) => {
                 false
             }
@@ -2688,6 +2687,14 @@ pub fn ty_region(tcx: &ctxt,
     }
 }
 
+pub fn free_region_from_def(free_id: ast::NodeId, def: &RegionParameterDef)
+    -> ty::Region
+{
+    ty::ReFree(ty::FreeRegion { scope_id: free_id,
+                                bound_region: ty::BrNamed(def.def_id,
+                                                          def.name) })
+}
+
 // Returns the type of a pattern as a monotype. Like @expr_ty, this function
 // doesn't provide type parameter substitutions.
 pub fn pat_ty(cx: &ctxt, pat: &ast::Pat) -> t {
@@ -2937,27 +2944,16 @@ impl AutoRef {
 }
 
 pub fn method_call_type_param_defs(tcx: &ctxt, origin: typeck::MethodOrigin)
-                                   -> Rc<Vec<TypeParameterDef>> {
+                                   -> VecPerParamSpace<TypeParameterDef> {
     match origin {
         typeck::MethodStatic(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.type_param_defs
+            ty::lookup_item_type(tcx, did).generics.types.clone()
         }
-        typeck::MethodParam(typeck::MethodParam {
-            trait_id: trt_id,
-            method_num: n_mth, ..}) |
-        typeck::MethodObject(typeck::MethodObject {
-            trait_id: trt_id,
-            method_num: n_mth, ..}) => {
-            // ...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 trait_type_param_defs =
-                Vec::from_slice(lookup_trait_def(tcx, trt_id).generics.type_param_defs());
-            Rc::new(trait_type_param_defs.append(
-                        ty::trait_method(tcx, trt_id, n_mth).generics.type_param_defs()))
+        typeck::MethodParam(typeck::MethodParam{trait_id: trt_id,
+                                                method_num: n_mth, ..}) |
+        typeck::MethodObject(typeck::MethodObject{trait_id: trt_id,
+                                                  method_num: n_mth, ..}) => {
+            ty::trait_method(tcx, trt_id, n_mth).generics.types.clone()
         }
     }
 }
@@ -3176,7 +3172,7 @@ pub fn method_idx(id: ast::Ident, meths: &[Rc<Method>]) -> Option<uint> {
 /// Returns a vector containing the indices of all type parameters that appear
 /// in `ty`.  The vector may contain duplicates.  Probably should be converted
 /// to a bitset or some other representation.
-pub fn param_tys_in_type(ty: t) -> Vec<param_ty> {
+pub fn param_tys_in_type(ty: t) -> Vec<ParamTy> {
     let mut rslt = Vec::new();
     walk_ty(ty, |ty| {
         match get(ty).sty {
@@ -3214,8 +3210,13 @@ pub fn ty_sort_str(cx: &ctxt, t: t) -> String {
         ty_infer(TyVar(_)) => "inferred type".to_string(),
         ty_infer(IntVar(_)) => "integral variable".to_string(),
         ty_infer(FloatVar(_)) => "floating-point variable".to_string(),
-        ty_param(_) => "type parameter".to_string(),
-        ty_self(_) => "self".to_string(),
+        ty_param(ref p) => {
+            if p.space == subst::SelfSpace {
+                "Self".to_string()
+            } else {
+                "type parameter".to_string()
+            }
+        }
         ty_err => "type error".to_string(),
     }
 }
@@ -3821,7 +3822,7 @@ pub fn lookup_item_type(cx: &ctxt,
 
 pub fn lookup_impl_vtables(cx: &ctxt,
                            did: ast::DefId)
-                     -> typeck::impl_res {
+                           -> typeck::vtable_res {
     lookup_locally_or_in_crate_store(
         "impl_vtables", did, &mut *cx.impl_vtables.borrow_mut(),
         || csearch::get_impl_vtables(cx, did) )
@@ -4103,8 +4104,7 @@ pub fn normalize_ty(cx: &ctxt, t: t) -> t {
                        substs: &subst::Substs)
                        -> subst::Substs {
             subst::Substs { regions: subst::ErasedRegions,
-                            self_ty: substs.self_ty.fold_with(self),
-                            tps: substs.tps.fold_with(self) }
+                            types: substs.types.fold_with(self) }
         }
 
         fn fold_sig(&mut self,
@@ -4252,11 +4252,7 @@ pub fn visitor_object_ty(tcx: &ctxt,
         Ok(id) => id,
         Err(s) => { return Err(s); }
     };
-    let substs = Substs {
-        regions: subst::NonerasedRegions(Vec::new()),
-        self_ty: None,
-        tps: Vec::new()
-    };
+    let substs = Substs::empty();
     let trait_ref = Rc::new(TraitRef { def_id: trait_lang_item, substs: substs });
     Ok((trait_ref.clone(),
         mk_trait(tcx,
@@ -4582,10 +4578,6 @@ pub fn hash_crate_independent(tcx: &ctxt, t: t, svh: &Svh) -> u64 {
                 hash!(p.idx);
                 did(&mut state, p.def_id);
             }
-            ty_self(d) => {
-                byte!(21);
-                did(&mut state, d);
-            }
             ty_infer(_) => unreachable!(),
             ty_err => byte!(23),
         }
@@ -4607,11 +4599,7 @@ impl Variance {
 
 pub fn construct_parameter_environment(
     tcx: &ctxt,
-    self_bound: Option<Rc<TraitRef>>,
-    item_type_params: &[TypeParameterDef],
-    method_type_params: &[TypeParameterDef],
-    item_region_params: &[RegionParameterDef],
-    method_region_params: &[RegionParameterDef],
+    generics: &ty::Generics,
     free_id: ast::NodeId)
     -> ParameterEnvironment
 {
@@ -4621,75 +4609,76 @@ pub fn construct_parameter_environment(
     // Construct the free substs.
     //
 
-    // map Self => Self
-    let self_ty = self_bound.as_ref().map(|t| ty::mk_self(tcx, t.def_id));
-
-    // map A => A
-    let num_item_type_params = item_type_params.len();
-    let num_method_type_params = method_type_params.len();
-    let num_type_params = num_item_type_params + num_method_type_params;
-    let type_params = Vec::from_fn(num_type_params, |i| {
-            let def_id = if i < num_item_type_params {
-                item_type_params[i].def_id
-            } else {
-                method_type_params[i - num_item_type_params].def_id
-            };
-
-            ty::mk_param(tcx, i, def_id)
-        });
+    // map T => T
+    let mut types = VecPerParamSpace::empty();
+    for &space in subst::ParamSpace::all().iter() {
+        push_types_from_defs(tcx, &mut types, space,
+                             generics.types.get_vec(space));
+    }
 
     // map bound 'a => free 'a
-    let region_params = {
-        fn push_region_params(mut accum: Vec<ty::Region>,
-                              free_id: ast::NodeId,
-                              region_params: &[RegionParameterDef])
-                              -> Vec<ty::Region> {
-            for r in region_params.iter() {
-                accum.push(
-                    ty::ReFree(ty::FreeRegion {
-                            scope_id: free_id,
-                            bound_region: ty::BrNamed(r.def_id, r.name)}));
-            }
-            accum
-        }
-
-        let t = push_region_params(vec!(), free_id, item_region_params);
-        push_region_params(t, free_id, method_region_params)
-    };
+    let mut regions = VecPerParamSpace::empty();
+    for &space in subst::ParamSpace::all().iter() {
+        push_region_params(&mut regions, space, free_id,
+                           generics.regions.get_vec(space));
+    }
 
     let free_substs = Substs {
-        self_ty: self_ty,
-        tps: type_params,
-        regions: subst::NonerasedRegions(region_params)
+        types: types,
+        regions: subst::NonerasedRegions(regions)
     };
 
     //
     // Compute the bounds on Self and the type parameters.
     //
 
-    let self_bound_substd = self_bound.map(|b| b.subst(tcx, &free_substs));
-    let type_param_bounds_substd = Vec::from_fn(num_type_params, |i| {
-        if i < num_item_type_params {
-            (*item_type_params[i].bounds).subst(tcx, &free_substs)
-        } else {
-            let j = i - num_item_type_params;
-            (*method_type_params[j].bounds).subst(tcx, &free_substs)
-        }
-    });
+    let mut bounds = VecPerParamSpace::empty();
+    for &space in subst::ParamSpace::all().iter() {
+        push_bounds_from_defs(tcx, &mut bounds, space, &free_substs,
+                              generics.types.get_vec(space));
+    }
 
     debug!("construct_parameter_environment: free_id={} \
            free_subst={} \
-           self_param_bound={} \
-           type_param_bound={}",
+           bounds={}",
            free_id,
            free_substs.repr(tcx),
-           self_bound_substd.repr(tcx),
-           type_param_bounds_substd.repr(tcx));
+           bounds.repr(tcx));
 
-    ty::ParameterEnvironment {
+    return ty::ParameterEnvironment {
         free_substs: free_substs,
-        self_param_bound: self_bound_substd,
-        type_param_bounds: type_param_bounds_substd,
+        bounds: bounds
+    };
+
+    fn push_region_params(regions: &mut VecPerParamSpace<ty::Region>,
+                          space: subst::ParamSpace,
+                          free_id: ast::NodeId,
+                          region_params: &Vec<RegionParameterDef>)
+    {
+        for r in region_params.iter() {
+            regions.push(space, ty::free_region_from_def(free_id, r));
+        }
+    }
+
+    fn push_types_from_defs(tcx: &ty::ctxt,
+                            types: &mut subst::VecPerParamSpace<ty::t>,
+                            space: subst::ParamSpace,
+                            defs: &Vec<TypeParameterDef>) {
+        for (i, def) in defs.iter().enumerate() {
+            let ty = ty::mk_param(tcx, space, i, def.def_id);
+            types.push(space, ty);
+        }
+    }
+
+    fn push_bounds_from_defs(tcx: &ty::ctxt,
+                             bounds: &mut subst::VecPerParamSpace<ParamBounds>,
+                             space: subst::ParamSpace,
+                             free_substs: &subst::Substs,
+                             defs: &Vec<TypeParameterDef>) {
+        for def in defs.iter() {
+            let b = (*def.bounds).subst(tcx, free_substs);
+            bounds.push(space, b);
+        }
     }
 }
 
diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs
index e8f043b5f86..e5fbe9df98f 100644
--- a/src/librustc/middle/ty_fold.rs
+++ b/src/librustc/middle/ty_fold.rs
@@ -11,6 +11,7 @@
 // Generalized type folding mechanism.
 
 use middle::subst;
+use middle::subst::VecPerParamSpace;
 use middle::ty;
 use middle::typeck;
 use std::rc::Rc;
@@ -127,6 +128,12 @@ impl<T:TypeFoldable> TypeFoldable for OwnedSlice<T> {
     }
 }
 
+impl<T:TypeFoldable> TypeFoldable for VecPerParamSpace<T> {
+    fn fold_with<F:TypeFolder>(&self, folder: &mut F) -> VecPerParamSpace<T> {
+        self.map(|t| t.fold_with(folder))
+    }
+}
+
 impl TypeFoldable for ty::TraitStore {
     fn fold_with<F:TypeFolder>(&self, folder: &mut F) -> ty::TraitStore {
         folder.fold_trait_store(*self)
@@ -212,15 +219,9 @@ impl TypeFoldable for typeck::vtable_origin {
             typeck::vtable_param(n, b) => {
                 typeck::vtable_param(n, b)
             }
-        }
-    }
-}
-
-impl TypeFoldable for typeck::impl_res {
-    fn fold_with<F:TypeFolder>(&self, folder: &mut F) -> typeck::impl_res {
-        typeck::impl_res {
-            trait_vtables: self.trait_vtables.fold_with(folder),
-            self_vtables: self.self_vtables.fold_with(folder),
+            typeck::vtable_error => {
+                typeck::vtable_error
+            }
         }
     }
 }
@@ -245,6 +246,8 @@ impl TypeFoldable for ty::TypeParameterDef {
         ty::TypeParameterDef {
             ident: self.ident,
             def_id: self.def_id,
+            space: self.space,
+            index: self.index,
             bounds: self.bounds.fold_with(folder),
             default: self.default.fold_with(folder),
         }
@@ -260,8 +263,8 @@ impl TypeFoldable for ty::RegionParameterDef {
 impl TypeFoldable for ty::Generics {
     fn fold_with<F:TypeFolder>(&self, folder: &mut F) -> ty::Generics {
         ty::Generics {
-            type_param_defs: self.type_param_defs.fold_with(folder),
-            region_param_defs: self.region_param_defs.fold_with(folder)
+            types: self.types.fold_with(folder),
+            regions: self.regions.fold_with(folder),
         }
     }
 }
@@ -291,8 +294,7 @@ pub fn super_fold_substs<T:TypeFolder>(this: &mut T,
     };
 
     subst::Substs { regions: regions,
-                    self_ty: substs.self_ty.fold_with(this),
-                    tps: substs.tps.fold_with(this) }
+                    types: substs.types.fold_with(this) }
 }
 
 pub fn super_fold_sig<T:TypeFolder>(this: &mut T,
@@ -390,7 +392,7 @@ pub fn super_fold_sty<T:TypeFolder>(this: &mut T,
         ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_str |
         ty::ty_int(_) | ty::ty_uint(_) | ty::ty_float(_) |
         ty::ty_err | ty::ty_infer(_) |
-        ty::ty_param(..) | ty::ty_self(_) => {
+        ty::ty_param(..) => {
             (*sty).clone()
         }
     }
diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs
index 7db23ee264d..83c5be23816 100644
--- a/src/librustc/middle/typeck/astconv.rs
+++ b/src/librustc/middle/typeck/astconv.rs
@@ -52,6 +52,7 @@
 use middle::const_eval;
 use middle::def;
 use middle::lang_items::FnMutTraitLangItem;
+use rl = middle::resolve_lifetime;
 use middle::subst::{Subst, Substs};
 use middle::subst;
 use middle::ty::ty_param_substs_and_ty;
@@ -85,20 +86,20 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &ast::Lifetime)
             tcx.sess.span_bug(lifetime.span, "unresolved lifetime");
         }
 
-        Some(&ast::DefStaticRegion) => {
+        Some(&rl::DefStaticRegion) => {
             ty::ReStatic
         }
 
-        Some(&ast::DefLateBoundRegion(binder_id, _, id)) => {
+        Some(&rl::DefLateBoundRegion(binder_id, _, id)) => {
             ty::ReLateBound(binder_id, ty::BrNamed(ast_util::local_def(id),
                                                    lifetime.name))
         }
 
-        Some(&ast::DefEarlyBoundRegion(index, id)) => {
-            ty::ReEarlyBound(id, index, lifetime.name)
+        Some(&rl::DefEarlyBoundRegion(space, index, id)) => {
+            ty::ReEarlyBound(id, space, index, lifetime.name)
         }
 
-        Some(&ast::DefFreeRegion(scope_id, id)) => {
+        Some(&rl::DefFreeRegion(scope_id, id)) => {
             ty::ReFree(ty::FreeRegion {
                     scope_id: scope_id,
                     bound_region: ty::BrNamed(ast_util::local_def(id),
@@ -163,10 +164,21 @@ fn ast_path_substs<AC:AstConv,RS:RegionScope>(
 
     let tcx = this.tcx();
 
+    // ast_path_substs() is only called to convert paths that are
+    // known to refer to traits, types, or structs. In these cases,
+    // all type parameters defined for the item being referenced will
+    // be in the TypeSpace or SelfSpace.
+    //
+    // Note: in the case of traits, the self parameter is also
+    // defined, but we don't currently create a `type_param_def` for
+    // `Self` because it is implicit.
+    assert!(decl_generics.regions.all(|d| d.space == subst::TypeSpace));
+    assert!(decl_generics.types.all(|d| d.space != subst::FnSpace));
+
     // If the type is parameterized by the this region, then replace this
     // region with the current anon region binding (in other words,
     // whatever & would get replaced with).
-    let expected_num_region_params = decl_generics.region_param_defs().len();
+    let expected_num_region_params = decl_generics.regions.len(subst::TypeSpace);
     let supplied_num_region_params = path.segments.last().unwrap().lifetimes.len();
     let regions = if expected_num_region_params == supplied_num_region_params {
         path.segments.last().unwrap().lifetimes.iter().map(
@@ -192,9 +204,10 @@ fn ast_path_substs<AC:AstConv,RS:RegionScope>(
     };
 
     // Convert the type parameters supplied by the user.
+    let ty_param_defs = decl_generics.types.get_vec(subst::TypeSpace);
     let supplied_ty_param_count = path.segments.iter().flat_map(|s| s.types.iter()).count();
-    let formal_ty_param_count = decl_generics.type_param_defs().len();
-    let required_ty_param_count = decl_generics.type_param_defs().iter()
+    let formal_ty_param_count = ty_param_defs.len();
+    let required_ty_param_count = ty_param_defs.iter()
                                                .take_while(|x| x.default.is_none())
                                                .count();
     if supplied_ty_param_count < required_ty_param_count {
@@ -233,37 +246,29 @@ fn ast_path_substs<AC:AstConv,RS:RegionScope>(
                             .map(|a_t| ast_ty_to_ty(this, rscope, &**a_t))
                             .collect();
 
-    let mut substs = subst::Substs {
-        regions: subst::NonerasedRegions(regions),
-        self_ty: self_ty,
-        tps: tps
-    };
+    let mut substs = subst::Substs::new_type(tps, regions);
 
-    for param in decl_generics.type_param_defs()
-                              .slice_from(supplied_ty_param_count).iter() {
-        let ty = param.default.unwrap().subst_spanned(tcx, &substs, Some(path.span));
-        substs.tps.push(ty);
+    match self_ty {
+        None => {
+            // If no self-type is provided, it's still possible that
+            // one was declared, because this could be an object type.
+        }
+        Some(ty) => {
+            // If a self-type is provided, one should have been
+            // "declared" (in other words, this should be a
+            // trait-ref).
+            assert!(decl_generics.types.get_self().is_some());
+            substs.types.push(subst::SelfSpace, ty);
+        }
     }
 
-    substs
-}
-
-pub fn ast_path_to_substs_and_ty<AC:AstConv,
-                                 RS:RegionScope>(
-                                 this: &AC,
-                                 rscope: &RS,
-                                 did: ast::DefId,
-                                 path: &ast::Path)
-                                 -> ty_param_substs_and_ty {
-    let tcx = this.tcx();
-    let ty::ty_param_bounds_and_ty {
-        generics: generics,
-        ty: decl_ty
-    } = this.get_item_ty(did);
+    for param in ty_param_defs.slice_from(supplied_ty_param_count).iter() {
+        let default = param.default.unwrap();
+        let default = default.subst_spanned(tcx, &substs, Some(path.span));
+        substs.types.push(subst::TypeSpace, default);
+    }
 
-    let substs = ast_path_substs(this, rscope, &generics, None, path);
-    let ty = decl_ty.subst(tcx, &substs);
-    ty_param_substs_and_ty { substs: substs, ty: ty }
+    substs
 }
 
 pub fn ast_path_to_trait_ref<AC:AstConv,RS:RegionScope>(
@@ -286,12 +291,14 @@ pub fn ast_path_to_ty<AC:AstConv,RS:RegionScope>(
         path: &ast::Path)
      -> ty_param_substs_and_ty
 {
-    // Look up the polytype of the item and then substitute the provided types
-    // for any type/region parameters.
-    let ty::ty_param_substs_and_ty {
-        substs: substs,
-        ty: ty
-    } = ast_path_to_substs_and_ty(this, rscope, did, path);
+    let tcx = this.tcx();
+    let ty::ty_param_bounds_and_ty {
+        generics: generics,
+        ty: decl_ty
+    } = this.get_item_ty(did);
+
+    let substs = ast_path_substs(this, rscope, &generics, None, path);
+    let ty = decl_ty.subst(tcx, &substs);
     ty_param_substs_and_ty { substs: substs, ty: ty }
 }
 
@@ -519,10 +526,12 @@ fn ast_ty_to_mt<AC:AstConv, RS:RegionScope>(this: &AC,
 
 pub fn trait_ref_for_unboxed_function<AC:AstConv,
                                       RS:RegionScope>(
-                                      this: &AC,
-                                      rscope: &RS,
-                                      unboxed_function: &ast::UnboxedFnTy)
-                                      -> ty::TraitRef {
+                                          this: &AC,
+                                          rscope: &RS,
+                                          unboxed_function: &ast::UnboxedFnTy,
+                                          self_ty: Option<ty::t>)
+    -> ty::TraitRef
+{
     let fn_mut_trait_did = this.tcx()
                                .lang_items
                                .require(FnMutTraitLangItem)
@@ -538,11 +547,14 @@ pub fn trait_ref_for_unboxed_function<AC:AstConv,
     let output_type = ast_ty_to_ty(this,
                                    rscope,
                                    &*unboxed_function.decl.output);
-    let substs = subst::Substs {
-        self_ty: None,
-        tps: vec!(input_tuple, output_type),
-        regions: subst::NonerasedRegions(Vec::new()),
-    };
+    let mut substs = subst::Substs::new_type(vec!(input_tuple, output_type),
+                                             Vec::new());
+
+    match self_ty {
+        Some(s) => substs.types.push(subst::SelfSpace, s),
+        None => ()
+    }
+
     ty::TraitRef {
         def_id: fn_mut_trait_did,
         substs: substs,
@@ -590,7 +602,8 @@ fn mk_pointer<AC:AstConv,
                 substs
             } = trait_ref_for_unboxed_function(this,
                                                rscope,
-                                               &**unboxed_function);
+                                               &**unboxed_function,
+                                               None);
             return ty::mk_trait(this.tcx(),
                                 def_id,
                                 substs,
@@ -801,9 +814,9 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>(
                     def::DefTy(did) | def::DefStruct(did) => {
                         ast_path_to_ty(this, rscope, did, path).ty
                     }
-                    def::DefTyParam(id, n) => {
+                    def::DefTyParam(space, id, n) => {
                         check_path_args(tcx, path, NO_TPS | NO_REGIONS);
-                        ty::mk_param(tcx, n, id)
+                        ty::mk_param(tcx, space, n, id)
                     }
                     def::DefSelfTy(id) => {
                         // n.b.: resolve guarantees that the this type only appears in a
@@ -811,7 +824,7 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>(
                         // substs
                         check_path_args(tcx, path, NO_TPS | NO_REGIONS);
                         let did = ast_util::local_def(id);
-                        ty::mk_self(tcx, did)
+                        ty::mk_self_type(tcx, did)
                     }
                     def::DefMod(id) => {
                         tcx.sess.span_fatal(ast_ty.span,
@@ -891,7 +904,9 @@ pub fn ty_of_method<AC:AstConv>(
     fn_style: ast::FnStyle,
     untransformed_self_ty: ty::t,
     explicit_self: ast::ExplicitSelf,
-    decl: &ast::FnDecl) -> ty::BareFnTy {
+    decl: &ast::FnDecl)
+    -> ty::BareFnTy
+{
     ty_of_method_or_bare_fn(this, id, fn_style, abi::Rust, Some(SelfInfo {
         untransformed_self_ty: untransformed_self_ty,
         explicit_self: explicit_self
diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs
index b950d569d56..3933c30d5c9 100644
--- a/src/librustc/middle/typeck/check/_match.rs
+++ b/src/librustc/middle/typeck/check/_match.rs
@@ -127,7 +127,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: &ast::Pat, path: &ast::Path,
 
     // Check to see whether this is an enum or a struct.
     match *structure_of(pcx.fcx, pat.span, expected) {
-        ty::ty_enum(_, ref expected_substs) => {
+        ty::ty_enum(expected_def_id, ref expected_substs) => {
             // Lookup the enum and variant def ids:
             let v_def = lookup_def(pcx.fcx, pat.span, pat.id);
             match v_def.variant_def_ids() {
@@ -150,18 +150,15 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: &ast::Pat, path: &ast::Path,
                     arg_types = {
                         let vinfo =
                             ty::enum_variant_with_id(tcx, enm, var);
-                        let var_tpt = ty::lookup_item_type(tcx, var);
-                        vinfo.args.iter().map(|t| {
-                            if var_tpt.generics.type_param_defs().len() ==
-                                expected_substs.tps.len()
-                            {
-                                t.subst(tcx, expected_substs)
-                            }
-                            else {
-                                *t // In this case, an error was already signaled
-                                    // anyway
-                            }
-                        }).collect()
+                        if enm == expected_def_id {
+                            vinfo.args.iter()
+                                .map(|t| t.subst(tcx, expected_substs))
+                                .collect()
+                        } else {
+                            vinfo.args.iter()
+                                .map(|_| ty::mk_err())
+                                .collect()
+                        }
                     };
 
                     kind_name = "variant";
@@ -569,11 +566,7 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) {
                                           fields.as_slice(),
                                           etc,
                                           supplied_def_id,
-                                          &subst::Substs {
-                                              self_ty: None,
-                                              tps: Vec::new(),
-                                              regions: subst::ErasedRegions,
-                                          });
+                                          &subst::Substs::empty());
                     }
                     _ => () // Error, but we're already in an error case
                 }
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index ba53cbf0cab..aa687758403 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -91,7 +91,7 @@ use middle::typeck::infer;
 use middle::typeck::MethodCallee;
 use middle::typeck::{MethodOrigin, MethodParam};
 use middle::typeck::{MethodStatic, MethodObject};
-use middle::typeck::{param_numbered, param_self, param_index};
+use middle::typeck::{param_index};
 use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig;
 use util::common::indenter;
 use util::ppaux;
@@ -235,31 +235,36 @@ fn construct_transformed_self_ty_for_object(
     trait_def_id: ast::DefId,
     rcvr_substs: &subst::Substs,
     method_ty: &ty::Method)
-    -> ty::t {
+    -> ty::t
+{
     /*!
-        * This is a bit tricky. We have a match against a trait method
-        * being invoked on an object, and we want to generate the
-        * self-type. As an example, consider a trait
-        *
-        *     trait Foo {
-        *         fn r_method<'a>(&'a self);
-        *         fn u_method(Box<self>);
-        *     }
-        *
-        * Now, assuming that `r_method` is being called, we want the
-        * result to be `&'a Foo`. Assuming that `u_method` is being
-        * called, we want the result to be `Box<Foo>`. Of course,
-        * this transformation has already been done as part of
-        * `method_ty.fty.sig.inputs[0]`, but there the type
-        * is expressed in terms of `Self` (i.e., `&'a Self`, `Box<Self>`).
-        * Because objects are not standalone types, we can't just substitute
-        * `s/Self/Foo/`, so we must instead perform this kind of hokey
-        * match below.
-        */
-
-    let substs = subst::Substs {regions: rcvr_substs.regions.clone(),
-                                self_ty: None,
-                                tps: rcvr_substs.tps.clone()};
+     * This is a bit tricky. We have a match against a trait method
+     * being invoked on an object, and we want to generate the
+     * self-type. As an example, consider a trait
+     *
+     *     trait Foo {
+     *         fn r_method<'a>(&'a self);
+     *         fn u_method(Box<self>);
+     *     }
+     *
+     * Now, assuming that `r_method` is being called, we want the
+     * result to be `&'a Foo`. Assuming that `u_method` is being
+     * called, we want the result to be `Box<Foo>`. Of course,
+     * this transformation has already been done as part of
+     * `method_ty.fty.sig.inputs[0]`, but there the type
+     * is expressed in terms of `Self` (i.e., `&'a Self`, `Box<Self>`).
+     * Because objects are not standalone types, we can't just substitute
+     * `s/Self/Foo/`, so we must instead perform this kind of hokey
+     * match below.
+     */
+
+    let mut obj_substs = rcvr_substs.clone();
+
+    // The subst we get in has Err as the "Self" type. For an object
+    // type, we don't put any type into the Self paramspace, so let's
+    // make a copy of rcvr_substs that has the Self paramspace empty.
+    obj_substs.types.get_mut_vec(subst::SelfSpace).pop().unwrap();
+
     match method_ty.explicit_self {
         ast::SelfStatic => {
             tcx.sess.span_bug(span, "static method for object type receiver");
@@ -271,13 +276,13 @@ fn construct_transformed_self_ty_for_object(
             let transformed_self_ty = *method_ty.fty.sig.inputs.get(0);
             match ty::get(transformed_self_ty).sty {
                 ty::ty_rptr(r, mt) => { // must be SelfRegion
-                    let r = r.subst(tcx, &substs); // handle Early-Bound lifetime
-                    ty::mk_trait(tcx, trait_def_id, substs,
+                    let r = r.subst(tcx, rcvr_substs); // handle Early-Bound lifetime
+                    ty::mk_trait(tcx, trait_def_id, obj_substs,
                                  RegionTraitStore(r, mt.mutbl),
                                  ty::empty_builtin_bounds())
                 }
                 ty::ty_uniq(_) => { // must be SelfUniq
-                    ty::mk_trait(tcx, trait_def_id, substs,
+                    ty::mk_trait(tcx, trait_def_id, obj_substs,
                                  UniqTraitStore,
                                  ty::empty_builtin_bounds())
                 }
@@ -456,11 +461,6 @@ impl<'a> LookupContext<'a> {
                 ty_param(p) => {
                     self.push_inherent_candidates_from_param(self_ty, restrict_to, p);
                 }
-                ty_self(..) => {
-                    // Call is of the form "self.foo()" and appears in one
-                    // of a trait's default method implementations.
-                    self.push_inherent_candidates_from_self(self_ty, restrict_to);
-                }
                 _ => { /* No bound methods in these types */ }
             }
 
@@ -516,10 +516,7 @@ impl<'a> LookupContext<'a> {
         //
         // `confirm_candidate()` also relies upon this substitution
         // for Self. (fix)
-        let rcvr_substs = subst::Substs {
-            self_ty: Some(ty::mk_err()),
-            ..(*substs).clone()
-        };
+        let rcvr_substs = substs.with_self_ty(ty::mk_err());
         let trait_ref = Rc::new(TraitRef {
             def_id: did,
             substs: rcvr_substs.clone()
@@ -552,35 +549,27 @@ impl<'a> LookupContext<'a> {
     fn push_inherent_candidates_from_param(&mut self,
                                            rcvr_ty: ty::t,
                                            restrict_to: Option<DefId>,
-                                           param_ty: param_ty) {
+                                           param_ty: ParamTy) {
         debug!("push_inherent_candidates_from_param(param_ty={:?})",
                param_ty);
-        let i = param_ty.idx;
-        match self.fcx.inh.param_env.type_param_bounds.as_slice().get(i) {
-            Some(b) => self.push_inherent_candidates_from_bounds(
-                            rcvr_ty, b.trait_bounds.as_slice(), restrict_to,
-                            param_numbered(param_ty.idx)),
-            None => {}
-        }
-    }
-
-
-    fn push_inherent_candidates_from_self(&mut self,
-                                          rcvr_ty: ty::t,
-                                          restrict_to: Option<DefId>) {
-        debug!("push_inherent_candidates_from_self()");
         self.push_inherent_candidates_from_bounds(
             rcvr_ty,
-            [self.fcx.inh.param_env.self_param_bound.clone().unwrap()],
+            param_ty.space,
+            param_ty.idx,
             restrict_to,
-            param_self)
+            param_index { space: param_ty.space, index: param_ty.idx });
     }
 
+
     fn push_inherent_candidates_from_bounds(&mut self,
                                             self_ty: ty::t,
-                                            bounds: &[Rc<TraitRef>],
+                                            space: subst::ParamSpace,
+                                            index: uint,
                                             restrict_to: Option<DefId>,
                                             param: param_index) {
+        let bounds =
+            self.fcx.inh.param_env.bounds.get(space, index).trait_bounds
+            .as_slice();
         self.push_inherent_candidates_from_bounds_inner(bounds,
             |trait_ref, m, method_num, bound_num| {
                 match restrict_to {
@@ -937,7 +926,7 @@ impl<'a> LookupContext<'a> {
             ty_bare_fn(..) | ty_box(..) | ty_uniq(..) | ty_rptr(..) |
             ty_infer(IntVar(_)) |
             ty_infer(FloatVar(_)) |
-            ty_self(_) | ty_param(..) | ty_nil | ty_bot | ty_bool |
+            ty_param(..) | ty_nil | ty_bot | ty_bool |
             ty_char | ty_int(..) | ty_uint(..) |
             ty_float(..) | ty_enum(..) | ty_ptr(..) | ty_struct(..) | ty_tup(..) |
             ty_str | ty_vec(..) | ty_trait(..) | ty_closure(..) => {
@@ -1093,7 +1082,8 @@ impl<'a> LookupContext<'a> {
     }
 
     fn confirm_candidate(&self, rcvr_ty: ty::t, candidate: &Candidate)
-                         -> MethodCallee {
+                         -> MethodCallee
+    {
         // This method performs two sets of substitutions, one after the other:
         // 1. Substitute values for any type/lifetime parameters from the impl and
         //    method declaration into the method type. This is the function type
@@ -1117,8 +1107,8 @@ impl<'a> LookupContext<'a> {
         // If they were not explicitly supplied, just construct fresh
         // variables.
         let num_supplied_tps = self.supplied_tps.len();
-        let num_method_tps = candidate.method_ty.generics.type_param_defs().len();
-        let m_substs = {
+        let num_method_tps = candidate.method_ty.generics.types.len(subst::FnSpace);
+        let m_types = {
             if num_supplied_tps == 0u {
                 self.fcx.infcx().next_ty_vars(num_method_tps)
             } else if num_method_tps == 0u {
@@ -1129,37 +1119,23 @@ impl<'a> LookupContext<'a> {
             } else if num_supplied_tps != num_method_tps {
                 tcx.sess.span_err(
                     self.span,
-                    "incorrect number of type \
-                     parameters given for this method");
+                    "incorrect number of type parameters given for this method");
                 self.fcx.infcx().next_ty_vars(num_method_tps)
             } else {
                 Vec::from_slice(self.supplied_tps)
             }
         };
 
-        // Determine values for the early-bound lifetime parameters.
+        // Create subst for early-bound lifetime parameters, combining
+        // parameters from the type and those from the method.
+        //
         // FIXME -- permit users to manually specify lifetimes
-        let mut all_regions: Vec<Region> = match candidate.rcvr_substs.regions {
-            subst::NonerasedRegions(ref v) => {
-                v.iter().map(|r| r.clone()).collect()
-            }
-            subst::ErasedRegions => tcx.sess.span_bug(self.span, "ErasedRegions")
-        };
         let m_regions =
             self.fcx.infcx().region_vars_for_defs(
                 self.span,
-                candidate.method_ty.generics.region_param_defs.as_slice());
-        for &r in m_regions.iter() {
-            all_regions.push(r);
-        }
+                candidate.method_ty.generics.regions.get_vec(subst::FnSpace));
 
-        // Construct the full set of type parameters for the method,
-        // which is equal to the class tps + the method tps.
-        let all_substs = subst::Substs {
-            tps: candidate.rcvr_substs.tps.clone().append(m_substs.as_slice()),
-            regions: subst::NonerasedRegions(all_regions),
-            self_ty: candidate.rcvr_substs.self_ty,
-        };
+        let all_substs = candidate.rcvr_substs.clone().with_method(m_types, m_regions);
 
         let ref bare_fn_ty = candidate.method_ty.fty;
 
@@ -1285,7 +1261,8 @@ impl<'a> LookupContext<'a> {
             check_for_self_ty(sig.output);
         }
 
-        if candidate.method_ty.generics.has_type_params() { // reason (b) above
+        if candidate.method_ty.generics.has_type_params(subst::FnSpace) {
+            // reason (b) above
             self.tcx().sess.span_err(
                 self.span,
                 "cannot call a generic method through an object");
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index a6615d6bce9..544990d19a5 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -83,10 +83,10 @@ use middle::lint::UnreachableCode;
 use middle::pat_util::pat_id_map;
 use middle::pat_util;
 use middle::subst;
-use middle::subst::{Subst, Substs};
+use middle::subst::{Subst, Substs, VecPerParamSpace, ParamSpace};
 use middle::ty::{FnSig, VariantInfo};
 use middle::ty::{ty_param_bounds_and_ty, ty_param_substs_and_ty};
-use middle::ty::{param_ty, Disr, ExprTyProvider};
+use middle::ty::{ParamTy, Disr, ExprTyProvider};
 use middle::ty;
 use middle::ty_fold::TypeFolder;
 use middle::typeck::astconv::AstConv;
@@ -285,8 +285,7 @@ fn blank_inherited_fields<'a>(ccx: &'a CrateCtxt<'a>) -> Inherited<'a> {
     // and statement context, but we might as well do write the code only once
     let param_env = ty::ParameterEnvironment {
         free_substs: subst::Substs::empty(),
-        self_param_bound: None,
-        type_param_bounds: Vec::new()
+        bounds: subst::VecPerParamSpace::empty()
     };
     Inherited::new(ccx.tcx, param_env)
 }
@@ -453,9 +452,9 @@ fn check_fn<'a>(ccx: &'a CrateCtxt<'a>,
     let arg_tys = fn_sig.inputs.as_slice();
     let ret_ty = fn_sig.output;
 
-    debug!("check_fn(arg_tys={:?}, ret_ty={:?})",
-           arg_tys.iter().map(|&a| ppaux::ty_to_str(tcx, a)).collect::<Vec<String>>(),
-           ppaux::ty_to_str(tcx, ret_ty));
+    debug!("check_fn(arg_tys={}, ret_ty={})",
+           arg_tys.repr(tcx),
+           ret_ty.repr(tcx));
 
     // Create the function context.  This is either derived from scratch or,
     // in the case of function expressions, based on the outer context.
@@ -647,14 +646,9 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
       ast::ItemFn(ref decl, _, _, _, ref body) => {
         let fn_tpt = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
 
-        let param_env = ty::construct_parameter_environment(
-                ccx.tcx,
-                None,
-                fn_tpt.generics.type_param_defs(),
-                [],
-                [],
-                fn_tpt.generics.region_param_defs.as_slice(),
-                body.id);
+        let param_env = ty::construct_parameter_environment(ccx.tcx,
+                                                            &fn_tpt.generics,
+                                                            body.id);
 
         check_bare_fn(ccx, &**decl, &**body, it.id, fn_tpt.ty, param_env);
       }
@@ -663,7 +657,7 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
 
         let impl_tpt = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
         for m in ms.iter() {
-            check_method_body(ccx, &impl_tpt.generics, None, &**m);
+            check_method_body(ccx, &impl_tpt.generics, &**m);
         }
 
         match *opt_trait_ref {
@@ -672,7 +666,6 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
                     ty::node_id_to_trait_ref(ccx.tcx, ast_trait_ref.ref_id);
                 check_impl_methods_against_trait(ccx,
                                              it.span,
-                                             &impl_tpt.generics,
                                              ast_trait_ref,
                                              &*impl_trait_ref,
                                              ms.as_slice());
@@ -691,8 +684,7 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
                     // bodies to check.
                 }
                 Provided(m) => {
-                    check_method_body(ccx, &trait_def.generics,
-                                      Some(trait_def.trait_ref.clone()), &*m);
+                    check_method_body(ccx, &trait_def.generics, &*m);
                 }
             }
         }
@@ -712,7 +704,7 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
         } else {
             for item in m.items.iter() {
                 let tpt = ty::lookup_item_type(ccx.tcx, local_def(item.id));
-                if tpt.generics.has_type_params() {
+                if !tpt.generics.types.is_empty() {
                     ccx.tcx.sess.span_err(item.span, "foreign items may not have type parameters");
                 }
 
@@ -734,7 +726,6 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
 
 fn check_method_body(ccx: &CrateCtxt,
                      item_generics: &ty::Generics,
-                     self_bound: Option<Rc<ty::TraitRef>>,
                      method: &ast::Method) {
     /*!
      * Type checks a method body.
@@ -746,25 +737,16 @@ fn check_method_body(ccx: &CrateCtxt,
      * - `method`: the method definition
      */
 
-    debug!("check_method_body(item_generics={}, \
-            self_bound={}, \
-            method.id={})",
+    debug!("check_method_body(item_generics={}, method.id={})",
             item_generics.repr(ccx.tcx),
-            self_bound.repr(ccx.tcx),
             method.id);
     let method_def_id = local_def(method.id);
     let method_ty = ty::method(ccx.tcx, method_def_id);
     let method_generics = &method_ty.generics;
 
-    let param_env =
-        ty::construct_parameter_environment(
-            ccx.tcx,
-            self_bound,
-            item_generics.type_param_defs(),
-            method_generics.type_param_defs(),
-            item_generics.region_param_defs(),
-            method_generics.region_param_defs(),
-            method.body.id);
+    let param_env = ty::construct_parameter_environment(ccx.tcx,
+                                                        method_generics,
+                                                        method.body.id);
 
     let fty = ty::node_id_to_type(ccx.tcx, method.id);
 
@@ -773,7 +755,6 @@ fn check_method_body(ccx: &CrateCtxt,
 
 fn check_impl_methods_against_trait(ccx: &CrateCtxt,
                                     impl_span: Span,
-                                    impl_generics: &ty::Generics,
                                     ast_trait_ref: &ast::TraitRef,
                                     impl_trait_ref: &ty::TraitRef,
                                     impl_methods: &[Gc<ast::Method>]) {
@@ -795,7 +776,6 @@ fn check_impl_methods_against_trait(ccx: &CrateCtxt,
         match opt_trait_method_ty {
             Some(trait_method_ty) => {
                 compare_impl_method(ccx.tcx,
-                                    impl_generics,
                                     &*impl_method_ty,
                                     impl_method.span,
                                     impl_method.body.id,
@@ -849,20 +829,17 @@ fn check_impl_methods_against_trait(ccx: &CrateCtxt,
  * - impl_m_span: span to use for reporting errors
  * - impl_m_body_id: id of the method body
  * - trait_m: the method in the trait
- * - trait_substs: the substitutions used on the type of the trait
+ * - trait_to_impl_substs: the substitutions used on the type of the trait
  */
 fn compare_impl_method(tcx: &ty::ctxt,
-                       impl_generics: &ty::Generics,
                        impl_m: &ty::Method,
                        impl_m_span: Span,
                        impl_m_body_id: ast::NodeId,
                        trait_m: &ty::Method,
-                       trait_substs: &subst::Substs) {
+                       trait_to_impl_substs: &subst::Substs) {
     debug!("compare_impl_method()");
     let infcx = infer::new_infer_ctxt(tcx);
 
-    let impl_tps = impl_generics.type_param_defs().len();
-
     // Try to give more informative error messages about self typing
     // mismatches.  Note that any mismatch will also be detected
     // below, where we construct a canonical function type that
@@ -897,8 +874,8 @@ fn compare_impl_method(tcx: &ty::ctxt,
         }
     }
 
-    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();
+    let num_impl_m_type_params = impl_m.generics.types.len(subst::FnSpace);
+    let num_trait_m_type_params = trait_m.generics.types.len(subst::FnSpace);
     if num_impl_m_type_params != num_trait_m_type_params {
         tcx.sess.span_err(
             impl_m_span,
@@ -925,8 +902,8 @@ fn compare_impl_method(tcx: &ty::ctxt,
         return;
     }
 
-    let it = trait_m.generics.type_param_defs().iter()
-        .zip(impl_m.generics.type_param_defs().iter());
+    let it = trait_m.generics.types.get_vec(subst::FnSpace).iter()
+        .zip(impl_m.generics.types.get_vec(subst::FnSpace).iter());
 
     for (i, (trait_param_def, impl_param_def)) in it.enumerate() {
         // Check that the impl does not require any builtin-bounds
@@ -971,62 +948,89 @@ fn compare_impl_method(tcx: &ty::ctxt,
         }
     }
 
-    // Create a substitution that maps the type parameters on the impl
-    // to themselves and which replace any references to bound regions
-    // in the self type with free regions.  So, for example, if the
-    // impl type is "&'a str", then this would replace the self
-    // type with a free region `self`.
-    let dummy_impl_tps: Vec<ty::t> =
-        impl_generics.type_param_defs().iter().enumerate().
-        map(|(i,t)| ty::mk_param(tcx, i, t.def_id)).
-        collect();
-    let dummy_method_tps: Vec<ty::t> =
-        impl_m.generics.type_param_defs().iter().enumerate().
-        map(|(i,t)| ty::mk_param(tcx, i + impl_tps, t.def_id)).
-        collect();
-    let dummy_impl_regions: Vec<ty::Region> =
-        impl_generics.region_param_defs().iter().
-        map(|l| ty::ReFree(ty::FreeRegion {
-                scope_id: impl_m_body_id,
-                bound_region: ty::BrNamed(l.def_id, l.name)})).
-        collect();
-    let dummy_substs = subst::Substs {
-        tps: dummy_impl_tps.append(dummy_method_tps.as_slice()),
-        regions: subst::NonerasedRegions(dummy_impl_regions),
-        self_ty: None };
-
-    // Create a bare fn type for trait/impl
-    // It'd be nice to refactor so as to provide the bare fn types instead.
-    let trait_fty = ty::mk_bare_fn(tcx, trait_m.fty.clone());
+    // This code is best explained by example. Consider a trait:
+    //
+    //     trait Trait<T> {
+    //          fn method<'a,M>(t: T, m: &'a M) -> Self;
+    //     }
+    //
+    // And an impl:
+    //
+    //     impl<'i, U> Trait<&'i U> for Foo {
+    //          fn method<'b,N>(t: &'i U, m: &'b N) -> Foo;
+    //     }
+    //
+    // We wish to decide if those two method types are compatible.
+    //
+    // We start out with trait_to_impl_substs, that maps the trait type
+    // parameters to impl type parameters:
+    //
+    //     trait_to_impl_substs = {T => &'i U, Self => Foo}
+    //
+    // We create a mapping `dummy_substs` that maps from the impl type
+    // parameters to fresh types and regions. For type parameters,
+    // this is the identity transform, but we could as well use any
+    // skolemized types. For regions, we convert from bound to free
+    // regions (Note: but only early-bound regions, i.e., those
+    // declared on the impl or used in type parameter bounds).
+    //
+    //     impl_to_skol_substs = {'i => 'i0, U => U0, N => N0 }
+    //
+    // Now we can apply skol_substs to the type of the impl method
+    // to yield a new function type in terms of our fresh, skolemized
+    // types:
+    //
+    //     <'b> fn(t: &'i0 U0, m: &'b) -> Foo
+    //
+    // We now want to extract and substitute the type of the *trait*
+    // method and compare it. To do so, we must create a compound
+    // substitution by combining trait_to_impl_substs and
+    // impl_to_skol_substs, and also adding a mapping for the method
+    // type parameters. We extend the mapping to also include
+    // the method parameters.
+    //
+    //     trait_to_skol_substs = { T => &'i0 U0, Self => Foo, M => N0 }
+    //
+    // Applying this to the trait method type yields:
+    //
+    //     <'a> fn(t: &'i0 U0, m: &'a) -> Foo
+    //
+    // This type is also the same but the name of the bound region ('a
+    // vs 'b).  However, the normal subtyping rules on fn types handle
+    // this kind of equivalency just fine.
+
+    // Create mapping from impl to skolemized.
+    let skol_tps =
+        impl_m.generics.types.map(
+            |d| ty::mk_param_from_def(tcx, d));
+    let skol_regions =
+        impl_m.generics.regions.map(
+            |l| ty::free_region_from_def(impl_m_body_id, l));
+    let impl_to_skol_substs =
+        subst::Substs::new(skol_tps.clone(), skol_regions.clone());
+
+    // Compute skolemized form of impl method ty.
     let impl_fty = ty::mk_bare_fn(tcx, impl_m.fty.clone());
-
-    // Perform substitutions so that the trait/impl methods are expressed
-    // in terms of the same set of type/region parameters:
-    // - replace trait type parameters with those from `trait_substs`,
-    //   except with any reference to bound self replaced with `dummy_self_r`
-    // - replace method parameters on the trait with fresh, dummy parameters
-    //   that correspond to the parameters we will find on the impl
-    // - replace self region with a fresh, dummy region
-    let impl_fty = {
-        debug!("impl_fty (pre-subst): {}", ppaux::ty_to_str(tcx, impl_fty));
-        impl_fty.subst(tcx, &dummy_substs)
-    };
-    debug!("impl_fty (post-subst): {}", ppaux::ty_to_str(tcx, impl_fty));
-    let trait_fty = {
-        let subst::Substs { regions: trait_regions,
-                            tps: trait_tps,
-                            self_ty: self_ty } = trait_substs.subst(tcx, &dummy_substs);
-        let substs = subst::Substs {
-            regions: trait_regions,
-            tps: trait_tps.append(dummy_method_tps.as_slice()),
-            self_ty: self_ty,
-        };
-        debug!("trait_fty (pre-subst): {} substs={}",
-               trait_fty.repr(tcx), substs.repr(tcx));
-        trait_fty.subst(tcx, &substs)
-    };
-    debug!("trait_fty (post-subst): {}", trait_fty.repr(tcx));
-
+    let impl_fty = impl_fty.subst(tcx, &impl_to_skol_substs);
+
+    // Compute skolemized form of trait method ty.
+    let trait_to_skol_substs =
+        trait_to_impl_substs
+        .subst(tcx, &impl_to_skol_substs)
+        .with_method(skol_tps.get_vec(subst::FnSpace).clone(),
+                     skol_regions.get_vec(subst::FnSpace).clone());
+    let trait_fty = ty::mk_bare_fn(tcx, trait_m.fty.clone());
+    let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
+
+    // Check the impl method type IM is a subtype of the trait method
+    // type TM. To see why this makes sense, think of a vtable. The
+    // expected type of the function pointers in the vtable is the
+    // type TM of the trait method.  The actual type will be the type
+    // IM of the impl method. Because we know that IM <: TM, that
+    // means that anywhere a TM is expected, a IM will do instead. In
+    // other words, anyone expecting to call a method with the type
+    // from the trait, can safely call a method with the type from the
+    // impl instead.
     match infer::mk_subty(&infcx, false, infer::MethodCompatCheck(impl_m_span),
                           impl_fty, trait_fty) {
         Ok(()) => {}
@@ -1792,18 +1796,13 @@ pub fn impl_self_ty(vcx: &VtableContext,
 
     let ity = ty::lookup_item_type(tcx, did);
     let (n_tps, rps, raw_ty) =
-        (ity.generics.type_param_defs().len(),
-         ity.generics.region_param_defs(),
+        (ity.generics.types.len(subst::TypeSpace),
+         ity.generics.regions.get_vec(subst::TypeSpace),
          ity.ty);
 
     let rps = vcx.infcx.region_vars_for_defs(span, rps);
     let tps = vcx.infcx.next_ty_vars(n_tps);
-
-    let substs = subst::Substs {
-        regions: subst::NonerasedRegions(rps),
-        self_ty: None,
-        tps: tps,
-    };
+    let substs = subst::Substs::new_type(tps, rps);
     let substd_ty = raw_ty.subst(tcx, &substs);
 
     ty_param_substs_and_ty { substs: substs, ty: substd_ty }
@@ -1850,170 +1849,6 @@ enum TupleArgumentsFlag {
     TupleArguments,
 }
 
-// Given the provenance of a static method, returns the generics of the static
-// method's container.
-fn generics_of_static_method_container(type_context: &ty::ctxt,
-                                       provenance: def::MethodProvenance)
-                                       -> ty::Generics {
-    match provenance {
-        def::FromTrait(trait_def_id) => {
-            ty::lookup_trait_def(type_context, trait_def_id).generics.clone()
-        }
-        def::FromImpl(impl_def_id) => {
-            ty::lookup_item_type(type_context, impl_def_id).generics.clone()
-        }
-    }
-}
-
-// Verifies that type parameters supplied in paths are in the right
-// locations.
-fn check_type_parameter_positions_in_path(function_context: &FnCtxt,
-                                          path: &ast::Path,
-                                          def: def::Def) {
-    // We only care about checking the case in which the path has two or
-    // more segments.
-    if path.segments.len() < 2 {
-        return
-    }
-
-    // Verify that no lifetimes or type parameters are present anywhere
-    // except the final two elements of the path.
-    for i in range(0, path.segments.len() - 2) {
-        for lifetime in path.segments.get(i).lifetimes.iter() {
-            function_context.tcx()
-                .sess
-                .span_err(lifetime.span,
-                          "lifetime parameters may not \
-                          appear here");
-            break;
-        }
-
-        for typ in path.segments.get(i).types.iter() {
-            function_context.tcx()
-                            .sess
-                            .span_err(typ.span,
-                                      "type parameters may not appear here");
-            break;
-        }
-    }
-
-    // If there are no parameters at all, there is nothing more to do; the
-    // rest of typechecking will (attempt to) infer everything.
-    if path.segments
-           .iter()
-           .all(|s| s.lifetimes.is_empty() && s.types.is_empty()) {
-        return
-    }
-
-    match def {
-        // If this is a static method of a trait or implementation, then
-        // ensure that the segment of the path which names the trait or
-        // implementation (the penultimate segment) is annotated with the
-        // right number of type parameters.
-        def::DefStaticMethod(_, provenance, _) => {
-            let generics =
-                generics_of_static_method_container(function_context.ccx.tcx,
-                                                    provenance);
-            let name = match provenance {
-                def::FromTrait(_) => "trait",
-                def::FromImpl(_) => "impl",
-            };
-
-            let trait_segment = &path.segments.get(path.segments.len() - 2);
-
-            // Make sure lifetime parameterization agrees with the trait or
-            // implementation type.
-            let trait_region_parameter_count = generics.region_param_defs().len();
-            let supplied_region_parameter_count = trait_segment.lifetimes.len();
-            if trait_region_parameter_count != supplied_region_parameter_count
-                && supplied_region_parameter_count != 0 {
-                function_context.tcx()
-                    .sess
-                    .span_err(path.span,
-                              format!("expected {} lifetime parameter{} \
-                                       found {} liftime parameter{}",
-                                      trait_region_parameter_count,
-                                      if trait_region_parameter_count == 1 {""}
-                                        else {"s"},
-                                      supplied_region_parameter_count,
-                                      if supplied_region_parameter_count == 1 {""}
-                                        else {"s"}).as_slice());
-            }
-
-            // Make sure the number of type parameters supplied on the trait
-            // or implementation segment equals the number of type parameters
-            // on the trait or implementation definition.
-            let formal_ty_param_count = generics.type_param_defs().len();
-            let required_ty_param_count = generics.type_param_defs().iter()
-                                                  .take_while(|x| x.default.is_none())
-                                                  .count();
-            let supplied_ty_param_count = trait_segment.types.len();
-            if supplied_ty_param_count < required_ty_param_count {
-                let msg = if required_ty_param_count < generics.type_param_defs().len() {
-                    format!("the {} referenced by this path needs at least \
-                             {} type parameter{}, but {} type parameters were \
-                             supplied",
-                            name,
-                            required_ty_param_count,
-                            if required_ty_param_count == 1 {""} else {"s"},
-                            supplied_ty_param_count)
-                } else {
-                    format!("the {} referenced by this path needs \
-                             {} type parameter{}, but {} type parameters were \
-                             supplied",
-                            name,
-                            required_ty_param_count,
-                            if required_ty_param_count == 1 {""} else {"s"},
-                            supplied_ty_param_count)
-                };
-                function_context.tcx().sess.span_err(path.span,
-                                                     msg.as_slice())
-            } else if supplied_ty_param_count > formal_ty_param_count {
-                let msg = if required_ty_param_count < generics.type_param_defs().len() {
-                    format!("the {} referenced by this path needs at most \
-                             {} type parameter{}, but {} type parameters were \
-                             supplied",
-                            name,
-                            formal_ty_param_count,
-                            if formal_ty_param_count == 1 {""} else {"s"},
-                            supplied_ty_param_count)
-                } else {
-                    format!("the {} referenced by this path needs \
-                             {} type parameter{}, but {} type parameters were \
-                             supplied",
-                            name,
-                            formal_ty_param_count,
-                            if formal_ty_param_count == 1 {""} else {"s"},
-                            supplied_ty_param_count)
-                };
-                function_context.tcx().sess.span_err(path.span,
-                                                     msg.as_slice())
-            }
-        }
-        _ => {
-            // Verify that no lifetimes or type parameters are present on
-            // the penultimate segment of the path.
-            let segment = &path.segments.get(path.segments.len() - 2);
-            for lifetime in segment.lifetimes.iter() {
-                function_context.tcx()
-                    .sess
-                    .span_err(lifetime.span,
-                              "lifetime parameters may not
-                              appear here");
-                break;
-            }
-            for typ in segment.types.iter() {
-                function_context.tcx()
-                                .sess
-                                .span_err(typ.span,
-                                          "type parameters may not appear \
-                                           here");
-                break;
-            }
-        }
-    }
-}
-
 /// Invariant:
 /// If an expression has any sub-expressions that result in a type error,
 /// inspecting that expression's type with `ty::type_is_error` will return
@@ -2689,19 +2524,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
         // Look up the number of type parameters and the raw type, and
         // determine whether the class is region-parameterized.
         let item_type = ty::lookup_item_type(tcx, class_id);
-        let type_parameter_count = item_type.generics.type_param_defs().len();
-        let region_param_defs = item_type.generics.region_param_defs();
         let raw_type = item_type.ty;
 
         // Generate the struct type.
-        let regions = fcx.infcx().region_vars_for_defs(span, region_param_defs);
-        let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count);
-        let substitutions = subst::Substs {
-            regions: subst::NonerasedRegions(regions),
-            self_ty: None,
-            tps: type_parameters
-        };
-
+        let substitutions = fcx.infcx().fresh_substs_for_type(
+            span, &item_type.generics);
         let mut struct_type = raw_type.subst(tcx, &substitutions);
 
         // Look up and check the fields.
@@ -2745,20 +2572,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
         // Look up the number of type parameters and the raw type, and
         // determine whether the enum is region-parameterized.
         let item_type = ty::lookup_item_type(tcx, enum_id);
-        let type_parameter_count = item_type.generics.type_param_defs().len();
-        let region_param_defs = item_type.generics.region_param_defs();
-        let raw_type = item_type.ty;
-
-        // Generate the enum type.
-        let regions = fcx.infcx().region_vars_for_defs(span, region_param_defs);
-        let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count);
-        let substitutions = subst::Substs {
-            regions: subst::NonerasedRegions(regions),
-            self_ty: None,
-            tps: type_parameters
-        };
-
-        let enum_type = raw_type.subst(tcx, &substitutions);
+        let substitutions = fcx.infcx().fresh_substs_for_type(span, &item_type.generics);
+        let enum_type = item_type.ty.subst(tcx, &substitutions);
 
         // Look up and check the enum variant fields.
         let variant_fields = ty::lookup_struct_fields(tcx, variant_id);
@@ -3039,8 +2854,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
       }
       ast::ExprPath(ref pth) => {
         let defn = lookup_def(fcx, pth.span, id);
-
-        check_type_parameter_positions_in_path(fcx, pth, defn);
         let tpt = ty_param_bounds_and_ty_for_def(fcx, expr.span, defn);
         instantiate_path(fcx, pth, tpt, defn, expr.span, expr.id);
       }
@@ -3983,179 +3796,367 @@ pub fn ty_param_bounds_and_ty_for_def(fcx: &FnCtxt,
 // Instantiates the given path, which must refer to an item with the given
 // number of type parameters and type.
 pub fn instantiate_path(fcx: &FnCtxt,
-                        pth: &ast::Path,
-                        tpt: ty_param_bounds_and_ty,
+                        path: &ast::Path,
+                        polytype: ty_param_bounds_and_ty,
                         def: def::Def,
                         span: Span,
                         node_id: ast::NodeId) {
-    debug!(">>> instantiate_path");
-
-    let ty_param_count = tpt.generics.type_param_defs().len();
-    let ty_param_req = tpt.generics.type_param_defs().iter()
-                                                   .take_while(|x| x.default.is_none())
-                                                   .count();
-    let mut ty_substs_len = 0;
-    for segment in pth.segments.iter() {
-        ty_substs_len += segment.types.len()
-    }
+    debug!("instantiate_path(path={}, def={}, node_id={}, polytype={})",
+           path.repr(fcx.tcx()),
+           def.repr(fcx.tcx()),
+           node_id,
+           polytype.repr(fcx.tcx()));
+
+    // We need to extract the type parameters supplied by the user in
+    // the path `path`. Due to the current setup, this is a bit of a
+    // tricky-process; the problem is that resolve only tells us the
+    // end-point of the path resolution, and not the intermediate steps.
+    // Luckily, we can (at least for now) deduce the intermediate steps
+    // just from the end-point.
+    //
+    // There are basically three cases to consider:
+    //
+    // 1. Reference to a *type*, such as a struct or enum:
+    //
+    //        mod a { struct Foo<T> { ... } }
+    //
+    //    Because we don't allow types to be declared within one
+    //    another, a path that leads to a type will always look like
+    //    `a::b::Foo<T>` where `a` and `b` are modules. This implies
+    //    that only the final segment can have type parameters, and
+    //    they are located in the TypeSpace.
+    //
+    //    *Note:* Generally speaking, references to types don't
+    //    actually pass through this function, but rather the
+    //    `ast_ty_to_ty` function in `astconv`. However, in the case
+    //    of struct patterns (and maybe literals) we do invoke
+    //    `instantiate_path` to get the general type of an instance of
+    //    a struct. (In these cases, there are actually no type
+    //    parameters permitted at present, but perhaps we will allow
+    //    them in the future.)
+    //
+    // 1b. Reference to a enum variant or tuple-like struct:
+    //
+    //        struct foo<T>(...)
+    //        enum E<T> { foo(...) }
+    //
+    //    In these cases, the parameters are declared in the type
+    //    space.
+    //
+    // 2. Reference to a *fn item*:
+    //
+    //        fn foo<T>() { }
+    //
+    //    In this case, the path will again always have the form
+    //    `a::b::foo::<T>` where only the final segment should have
+    //    type parameters. However, in this case, those parameters are
+    //    declared on a value, and hence are in the `FnSpace`.
+    //
+    // 3. Reference to a *method*:
+    //
+    //        impl<A> SomeStruct<A> {
+    //            fn foo<B>(...)
+    //        }
+    //
+    //    Here we can have a path like
+    //    `a::b::SomeStruct::<A>::foo::<B>`, in which case parameters
+    //    may appear in two places. The penultimate segment,
+    //    `SomeStruct::<A>`, contains parameters in TypeSpace, and the
+    //    final segment, `foo::<B>` contains parameters in fn space.
+    //
+    // The first step then is to categorize the segments appropriately.
 
-    debug!("tpt={} ty_param_count={:?} ty_substs_len={:?}",
-           tpt.repr(fcx.tcx()),
-           ty_param_count,
-           ty_substs_len);
-
-    // determine the region parameters, using the value given by the user
-    // (if any) and otherwise using a fresh region variable
-    let num_expected_regions = tpt.generics.region_param_defs().len();
-    let num_supplied_regions = pth.segments.last().unwrap().lifetimes.len();
-    let regions = if num_expected_regions == num_supplied_regions {
-        pth.segments.last().unwrap().lifetimes
-            .iter()
-            .map(|l| ast_region_to_region(fcx.tcx(), l))
-            .collect()
-    } else {
-        if num_supplied_regions != 0 {
-            fcx.ccx.tcx.sess.span_err(
-                span,
-                format!("expected {} lifetime parameter{}, \
-                         found {} lifetime parameter{}",
-                        num_expected_regions,
-                        if num_expected_regions == 1 {""} else {"s"},
-                        num_supplied_regions,
-                        if num_supplied_regions == 1 {""} else {"s"}).as_slice());
+    assert!(path.segments.len() >= 1);
+    let mut segment_spaces;
+    match def {
+        // Case 1 and 1b. Reference to a *type* or *enum variant*.
+        def::DefSelfTy(..) |
+        def::DefStruct(..) |
+        def::DefVariant(..) |
+        def::DefTyParamBinder(..) |
+        def::DefTy(..) |
+        def::DefTrait(..) |
+        def::DefPrimTy(..) |
+        def::DefTyParam(..) => {
+            // Everything but the final segment should have no
+            // parameters at all.
+            segment_spaces = Vec::from_elem(path.segments.len() - 1, None);
+            segment_spaces.push(Some(subst::TypeSpace));
         }
 
-        fcx.infcx().region_vars_for_defs(span, tpt.generics.region_param_defs.as_slice())
-    };
-    let regions = subst::NonerasedRegions(regions);
+        // Case 2. Reference to a top-level value.
+        def::DefFn(..) |
+        def::DefStatic(..) => {
+            segment_spaces = Vec::from_elem(path.segments.len() - 1, None);
+            segment_spaces.push(Some(subst::FnSpace));
+        }
 
-    // Special case: If there is a self parameter, omit it from the list of
-    // type parameters.
-    //
-    // Here we calculate the "user type parameter count", which is the number
-    // of type parameters actually manifest in the AST. This will differ from
-    // the internal type parameter count when there are self types involved.
-    let (user_ty_param_count, user_ty_param_req, self_parameter_index) = match def {
-        def::DefStaticMethod(_, provenance @ def::FromTrait(_), _) => {
-            let generics = generics_of_static_method_container(fcx.ccx.tcx,
-                                                               provenance);
-            (ty_param_count - 1, ty_param_req - 1, Some(generics.type_param_defs().len()))
+        // Case 3. Reference to a method.
+        def::DefStaticMethod(..) => {
+            assert!(path.segments.len() >= 2);
+            segment_spaces = Vec::from_elem(path.segments.len() - 2, None);
+            segment_spaces.push(Some(subst::TypeSpace));
+            segment_spaces.push(Some(subst::FnSpace));
         }
-        _ => (ty_param_count, ty_param_req, None),
-    };
 
-    // determine values for type parameters, using the values given by
-    // the user (if any) and otherwise using fresh type variables
-    let (tps, regions) = if ty_substs_len == 0 {
-        (fcx.infcx().next_ty_vars(ty_param_count), regions)
-    } else if ty_param_count == 0 {
-        fcx.ccx.tcx.sess.span_err
-            (span, "this item does not take type parameters");
-        (fcx.infcx().next_ty_vars(ty_param_count), regions)
-    } else if ty_substs_len > user_ty_param_count {
-        let expected = if user_ty_param_req < user_ty_param_count {
-            "expected at most"
-        } else {
-            "expected"
-        };
-        fcx.ccx.tcx.sess.span_err
-            (span,
-             format!("too many type parameters provided: {} {}, found {}",
-                  expected, user_ty_param_count, ty_substs_len).as_slice());
-        (fcx.infcx().next_ty_vars(ty_param_count), regions)
-    } else if ty_substs_len < user_ty_param_req {
-        let expected = if user_ty_param_req < user_ty_param_count {
-            "expected at least"
-        } else {
-            "expected"
-        };
-        fcx.ccx.tcx.sess.span_err(
-            span,
-            format!("not enough type parameters provided: {} {}, found {}",
-                    expected,
-                    user_ty_param_req,
-                    ty_substs_len).as_slice());
-        (fcx.infcx().next_ty_vars(ty_param_count), regions)
-    } else {
-        if ty_substs_len > user_ty_param_req
-            && !fcx.tcx().sess.features.default_type_params.get() {
-            fcx.tcx().sess.span_err(pth.span, "default type parameters are \
-                                               experimental and possibly buggy");
-            fcx.tcx().sess.span_note(pth.span, "add #![feature(default_type_params)] \
-                                                to the crate attributes to enable");
+        // Other cases. Various nonsense that really shouldn't show up
+        // here. If they do, an error will have been reported
+        // elsewhere. (I hope)
+        def::DefMod(..) |
+        def::DefForeignMod(..) |
+        def::DefArg(..) |
+        def::DefLocal(..) |
+        def::DefMethod(..) |
+        def::DefBinding(..) |
+        def::DefUse(..) |
+        def::DefRegion(..) |
+        def::DefLabel(..) |
+        def::DefUpvar(..) => {
+            segment_spaces = Vec::from_elem(path.segments.len(), None);
         }
+    }
+    assert_eq!(segment_spaces.len(), path.segments.len());
+
+    debug!("segment_spaces={}", segment_spaces);
+
+    // Next, examine the definition, and determine how many type
+    // parameters we expect from each space.
+    let type_defs = &polytype.generics.types;
+    let region_defs = &polytype.generics.regions;
+
+    // Now that we have categorized what space the parameters for each
+    // segment belong to, let's sort out the parameters that the user
+    // provided (if any) into their appropriate spaces. We'll also report
+    // errors if type parameters are provided in an inappropriate place.
+    let mut substs = Substs::empty();
+    for (opt_space, segment) in segment_spaces.iter().zip(path.segments.iter()) {
+        match *opt_space {
+            None => {
+                report_error_if_segment_contains_type_parameters(fcx, segment);
+            }
 
-        // Build up the list of type parameters, inserting the self parameter
-        // at the appropriate position.
-        let mut tps = Vec::new();
-        let mut pushed = false;
-        for (i, ty) in pth.segments.iter()
-                                   .flat_map(|segment| segment.types.iter())
-                                   .map(|ast_type| fcx.to_ty(&**ast_type))
-                                   .enumerate() {
-            match self_parameter_index {
-                Some(index) if index == i => {
-                    tps.push(*fcx.infcx().next_ty_vars(1).get(0));
-                    pushed = true;
-                }
-                _ => {}
+            Some(space) => {
+                push_explicit_parameters_from_segment_to_substs(fcx,
+                                                                space,
+                                                                type_defs,
+                                                                region_defs,
+                                                                segment,
+                                                                &mut substs);
             }
-            tps.push(ty)
         }
+    }
 
-        let mut substs = subst::Substs {
-            regions: regions,
-            self_ty: None,
-            tps: tps
-        };
+    // Now we have to compare the types that the user *actually*
+    // provided against the types that were *expected*. If the user
+    // did not provide any types, then we want to substitute inference
+    // variables. If the user provided some types, we may still need
+    // to add defaults. If the user provided *too many* types, that's
+    // a problem.
+    for &space in ParamSpace::all().iter() {
+        adjust_type_parameters(fcx, span, space, type_defs, &mut substs);
+        assert_eq!(substs.types.get_vec(space).len(),
+                   type_defs.get_vec(space).len());
+
+        adjust_region_parameters(fcx, span, space, region_defs, &mut substs);
+        assert_eq!(substs.regions().get_vec(space).len(),
+                   region_defs.get_vec(space).len());
+    }
 
-        let defaults = tpt.generics.type_param_defs().iter()
-                          .enumerate().filter_map(|(i, x)| {
-            match self_parameter_index {
-                Some(index) if index == i => None,
-                _ => Some(x.default)
-            }
-        });
-        for (i, default) in defaults.skip(ty_substs_len).enumerate() {
-            match self_parameter_index {
-                Some(index) if index == i + ty_substs_len => {
-                    substs.tps.push(*fcx.infcx().next_ty_vars(1).get(0));
-                    pushed = true;
+    fcx.write_ty_substs(node_id, polytype.ty, ty::ItemSubsts {
+        substs: substs,
+    });
+
+    fn report_error_if_segment_contains_type_parameters(
+        fcx: &FnCtxt,
+        segment: &ast::PathSegment)
+    {
+        for typ in segment.types.iter() {
+            fcx.tcx().sess.span_err(
+                typ.span,
+                "type parameters may not appear here");
+            break;
+        }
+
+        for lifetime in segment.lifetimes.iter() {
+            fcx.tcx().sess.span_err(
+                lifetime.span,
+                "lifetime parameters may not appear here");
+            break;
+        }
+    }
+
+    fn push_explicit_parameters_from_segment_to_substs(
+        fcx: &FnCtxt,
+        space: subst::ParamSpace,
+        type_defs: &VecPerParamSpace<ty::TypeParameterDef>,
+        region_defs: &VecPerParamSpace<ty::RegionParameterDef>,
+        segment: &ast::PathSegment,
+        substs: &mut Substs)
+    {
+        /*!
+         * Finds the parameters that the user provided and adds them
+         * to `substs`. If too many parameters are provided, then
+         * reports an error and clears the output vector.
+         *
+         * We clear the output vector because that will cause the
+         * `adjust_XXX_parameters()` later to use inference
+         * variables. This seems less likely to lead to derived
+         * errors.
+         *
+         * Note that we *do not* check for *too few* parameters here.
+         * Due to the presence of defaults etc that is more
+         * complicated. I wanted however to do the reporting of *too
+         * many* parameters here because we can easily use the precise
+         * span of the N+1'th parameter.
+         */
+
+        {
+            let type_count = type_defs.get_vec(space).len();
+            assert_eq!(substs.types.get_vec(space).len(), 0);
+            for (i, &typ) in segment.types.iter().enumerate() {
+                let t = fcx.to_ty(&*typ);
+                if i < type_count {
+                    substs.types.push(space, t);
+                } else if i == type_count {
+                    fcx.tcx().sess.span_err(
+                        typ.span,
+                        format!(
+                            "too many type parameters provided: \
+                             expected at most {} parameter(s) \
+                             but found {} parameter(s)",
+                            type_count,
+                            segment.types.len()).as_slice());
+                    substs.types.get_mut_vec(space).truncate(0);
                 }
-                _ => {}
             }
-            match default {
-                Some(default) => {
-                    let ty = default.subst_spanned(fcx.tcx(), &substs, Some(span));
-                    substs.tps.push(ty);
-                }
-                None => {
-                    fcx.tcx().sess.span_bug(span,
-                        "missing default for a not explicitly provided type param")
+        }
+
+        {
+            let region_count = region_defs.get_vec(space).len();
+            assert_eq!(substs.regions().get_vec(space).len(), 0);
+            for (i, lifetime) in segment.lifetimes.iter().enumerate() {
+                let r = ast_region_to_region(fcx.tcx(), lifetime);
+                if i < region_count {
+                    substs.mut_regions().push(space, r);
+                } else if i == region_count {
+                    fcx.tcx().sess.span_err(
+                        lifetime.span,
+                        format!(
+                            "too many lifetime parameters provided: \
+                             expected {} parameter(s) but found {} parameter(s)",
+                            region_count,
+                            segment.lifetimes.len()).as_slice());
+                    substs.mut_regions().get_mut_vec(space).truncate(0);
                 }
             }
         }
+    }
 
-        // If the self parameter goes at the end, insert it there.
-        if !pushed && self_parameter_index.is_some() {
-            substs.tps.push(*fcx.infcx().next_ty_vars(1).get(0))
+    fn adjust_type_parameters(
+        fcx: &FnCtxt,
+        span: Span,
+        space: ParamSpace,
+        defs: &VecPerParamSpace<ty::TypeParameterDef>,
+        substs: &mut Substs)
+    {
+        let provided_len = substs.types.get_vec(space).len();
+        let desired = defs.get_vec(space).as_slice();
+        let required_len = desired.iter()
+                              .take_while(|d| d.default.is_none())
+                              .count();
+
+        debug!("adjust_type_parameters(space={}, \
+               provided_len={}, \
+               desired_len={}, \
+               required_len={})",
+               space,
+               provided_len,
+               desired.len(),
+               required_len);
+
+        // Enforced by `push_explicit_parameters_from_segment_to_substs()`.
+        assert!(provided_len <= desired.len());
+
+        // Nothing specified at all: supply inference variables for
+        // everything.
+        if provided_len == 0 {
+            let provided = substs.types.get_mut_vec(space);
+            *provided = fcx.infcx().next_ty_vars(desired.len());
+            return;
         }
 
-        assert_eq!(substs.tps.len(), ty_param_count)
+        // Too few parameters specified: report an error and use Err
+        // for everything.
+        if provided_len < required_len {
+            let qualifier =
+                if desired.len() != required_len { "at least " } else { "" };
+            fcx.tcx().sess.span_err(
+                span,
+                format!("too few type parameters provided: \
+                             expected {}{} parameter(s) \
+                             but found {} parameter(s)",
+                            qualifier,
+                            required_len,
+                            provided_len).as_slice());
+            let provided = substs.types.get_mut_vec(space);
+            *provided = Vec::from_elem(desired.len(), ty::mk_err());
+            return;
+        }
 
-        let subst::Substs {tps, regions, ..} = substs;
-        (tps, regions)
-    };
+        // Otherwise, add in any optional parameters that the user
+        // omitted. The case of *too many* parameters is handled
+        // already by
+        // push_explicit_parameters_from_segment_to_substs(). Note
+        // that the *default* type are expressed in terms of all prior
+        // parameters, so we have to substitute as we go with the
+        // partial substitution that we have built up.
+        for i in range(provided_len, desired.len()) {
+            let default = desired[i].default.unwrap();
+            let default = default.subst_spanned(fcx.tcx(), substs, Some(span));
+            substs.types.push(space, default);
+        }
+        assert_eq!(substs.types.get_vec(space).len(), desired.len());
 
-    let substs = subst::Substs { regions: regions,
-                                 self_ty: None,
-                                 tps: tps };
+        debug!("Final substs: {}", substs.repr(fcx.tcx()));
+    }
 
-    fcx.write_ty_substs(node_id, tpt.ty, ty::ItemSubsts {
-        substs: substs,
-    });
+    fn adjust_region_parameters(
+        fcx: &FnCtxt,
+        span: Span,
+        space: ParamSpace,
+        defs: &VecPerParamSpace<ty::RegionParameterDef>,
+        substs: &mut Substs)
+    {
+        let provided = substs.mut_regions().get_mut_vec(space);
+        let desired = defs.get_vec(space);
+
+        // Enforced by `push_explicit_parameters_from_segment_to_substs()`.
+        assert!(provided.len() <= desired.len());
+
+        // If nothing was provided, just use inference variables.
+        if provided.len() == 0 {
+            *provided = fcx.infcx().region_vars_for_defs(span, desired);
+            return;
+        }
+
+        // If just the right number were provided, everybody is happy.
+        if provided.len() == desired.len() {
+            return;
+        }
 
-    debug!("<<<");
+        // Otherwise, too few were provided. Report an error and then
+        // use inference variables.
+        fcx.tcx().sess.span_err(
+            span,
+            format!(
+                "too few lifetime parameters provided: \
+                         expected {} parameter(s) \
+                         but found {} parameter(s)",
+                desired.len(),
+                provided.len()).as_slice());
+
+        *provided = fcx.infcx().region_vars_for_defs(span, desired);
+    }
 }
 
 // Resolves `typ` by a single level if `typ` is a type variable.  If no
@@ -4292,14 +4293,8 @@ pub fn check_bounds_are_used(ccx: &CrateCtxt,
 
     ty::walk_ty(ty, |t| {
             match ty::get(t).sty {
-                #[cfg(stage0)]
-                ty::ty_param(param_ty {idx, ..}) => {
-                    debug!("Found use of ty param \\#{}", idx);
-                    *tps_used.get_mut(idx) = true;
-                }
-                #[cfg(not(stage0))]
-                ty::ty_param(param_ty {idx, ..}) => {
-                    debug!("Found use of ty param #{}", idx);
+                ty::ty_param(ParamTy {idx, ..}) => {
+                    debug!("Found use of ty param num {}", idx);
                     *tps_used.get_mut(idx) = true;
                 }
                 _ => ()
@@ -4318,7 +4313,7 @@ pub fn check_bounds_are_used(ccx: &CrateCtxt,
 
 pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
     fn param(ccx: &CrateCtxt, n: uint) -> ty::t {
-        ty::mk_param(ccx.tcx, n, local_def(0))
+        ty::mk_param(ccx.tcx, subst::FnSpace, n, local_def(0))
     }
 
     let tcx = ccx.tcx;
@@ -4390,11 +4385,10 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
             "type_id" => {
                 let langid = ccx.tcx.lang_items.require(TypeIdLangItem);
                 match langid {
-                    Ok(did) => (1u, Vec::new(), ty::mk_struct(ccx.tcx, did, subst::Substs {
-                                                 self_ty: None,
-                                                 tps: Vec::new(),
-                                                 regions: subst::NonerasedRegions(Vec::new())
-                                                 }) ),
+                    Ok(did) => (1u,
+                                Vec::new(),
+                                ty::mk_struct(ccx.tcx, did,
+                                              subst::Substs::empty())),
                     Err(msg) => {
                         tcx.sess.span_fatal(it.span, msg.as_slice());
                     }
@@ -4593,7 +4587,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
         }
     });
     let i_ty = ty::lookup_item_type(ccx.tcx, local_def(it.id));
-    let i_n_tps = i_ty.generics.type_param_defs().len();
+    let i_n_tps = i_ty.generics.types.len(subst::FnSpace);
     if i_n_tps != n_tps {
         tcx.sess.span_err(it.span,
                           format!("intrinsic has wrong number of type \
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index 546eaa1fceb..7d2b9d9aa86 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -10,7 +10,7 @@
 
 
 use middle::ty;
-use middle::ty::{AutoAddEnv, AutoDerefRef, AutoObject, param_ty};
+use middle::ty::{AutoAddEnv, AutoDerefRef, AutoObject, ParamTy};
 use middle::ty_fold::TypeFolder;
 use middle::typeck::astconv::AstConv;
 use middle::typeck::check::{FnCtxt, impl_self_ty};
@@ -20,11 +20,11 @@ use middle::typeck::infer::fixup_err_to_str;
 use middle::typeck::infer::{resolve_and_force_all_but_regions, resolve_type};
 use middle::typeck::infer;
 use middle::typeck::{vtable_origin, vtable_res, vtable_param_res};
-use middle::typeck::{vtable_static, vtable_param, impl_res};
-use middle::typeck::{param_numbered, param_self, param_index};
+use middle::typeck::{vtable_static, vtable_param, vtable_error};
+use middle::typeck::{param_index};
 use middle::typeck::MethodCall;
 use middle::subst;
-use middle::subst::Subst;
+use middle::subst::{Subst, VecPerParamSpace};
 use util::common::indenter;
 use util::ppaux;
 use util::ppaux::Repr;
@@ -76,38 +76,32 @@ impl<'a> VtableContext<'a> {
 
 fn lookup_vtables(vcx: &VtableContext,
                   span: Span,
-                  type_param_defs: &[ty::TypeParameterDef],
+                  type_param_defs: &VecPerParamSpace<ty::TypeParameterDef>,
                   substs: &subst::Substs,
-                  is_early: bool) -> vtable_res {
-    debug!("lookup_vtables(span={:?}, \
-            type_param_defs={}, \
-            substs={}",
-           span,
+                  is_early: bool)
+                  -> VecPerParamSpace<vtable_param_res>
+{
+    debug!("lookup_vtables(\
+           type_param_defs={}, \
+           substs={}",
            type_param_defs.repr(vcx.tcx()),
            substs.repr(vcx.tcx()));
 
     // We do this backwards for reasons discussed above.
-    assert_eq!(substs.tps.len(), type_param_defs.len());
-    let mut result: Vec<vtable_param_res> =
-        substs.tps.iter()
-        .rev()
-        .zip(type_param_defs.iter().rev())
-        .map(|(ty, def)|
-            lookup_vtables_for_param(vcx, span, Some(substs),
-                                     &*def.bounds, *ty, is_early))
-        .collect();
-    result.reverse();
-
-    assert_eq!(substs.tps.len(), result.len());
+    let result = type_param_defs.map_rev(|def| {
+        let ty = *substs.types.get(def.space, def.index);
+        lookup_vtables_for_param(vcx, span, Some(substs),
+                                 &*def.bounds, ty, is_early)
+    });
+
     debug!("lookup_vtables result(\
-            span={:?}, \
             type_param_defs={}, \
             substs={}, \
             result={})",
-           span,
            type_param_defs.repr(vcx.tcx()),
            substs.repr(vcx.tcx()),
            result.repr(vcx.tcx()));
+
     result
 }
 
@@ -117,9 +111,15 @@ fn lookup_vtables_for_param(vcx: &VtableContext,
                             substs: Option<&subst::Substs>,
                             type_param_bounds: &ty::ParamBounds,
                             ty: ty::t,
-                            is_early: bool) -> vtable_param_res {
+                            is_early: bool)
+                            -> vtable_param_res {
     let tcx = vcx.tcx();
 
+    debug!("lookup_vtables_for_param(ty={}, type_param_bounds={}, is_early={})",
+           ty.repr(vcx.tcx()),
+           type_param_bounds.repr(vcx.tcx()),
+           is_early);
+
     // ty is the value supplied for the type parameter A...
     let mut param_result = Vec::new();
 
@@ -130,6 +130,10 @@ fn lookup_vtables_for_param(vcx: &VtableContext,
         // ...and here trait_ref is each bound that was declared on A,
         // expressed in terms of the type parameters.
 
+        debug!("matching ty={} trait_ref={}",
+               ty.repr(vcx.tcx()),
+               trait_ref.repr(vcx.tcx()));
+
         ty::populate_implementations_for_trait_if_necessary(tcx,
                                                             trait_ref.def_id);
 
@@ -157,11 +161,9 @@ fn lookup_vtables_for_param(vcx: &VtableContext,
     });
 
     debug!("lookup_vtables_for_param result(\
-            span={:?}, \
             type_param_bounds={}, \
             ty={}, \
             result={})",
-           span,
            type_param_bounds.repr(vcx.tcx()),
            ty.repr(vcx.tcx()),
            param_result.repr(vcx.tcx()));
@@ -216,10 +218,11 @@ fn lookup_vtable(vcx: &VtableContext,
                  ty: ty::t,
                  trait_ref: Rc<ty::TraitRef>,
                  is_early: bool)
-                 -> Option<vtable_origin> {
+                 -> Option<vtable_origin>
+{
     debug!("lookup_vtable(ty={}, trait_ref={})",
-           vcx.infcx.ty_to_str(ty),
-           vcx.infcx.trait_ref_to_str(&*trait_ref));
+           ty.repr(vcx.tcx()),
+           trait_ref.repr(vcx.tcx()));
     let _i = indenter();
 
     let ty = match fixup_ty(vcx, span, ty, is_early) {
@@ -230,32 +233,24 @@ fn lookup_vtable(vcx: &VtableContext,
             // The type has unconstrained type variables in it, so we can't
             // do early resolution on it. Return some completely bogus vtable
             // information: we aren't storing it anyways.
-            return Some(vtable_param(param_self, 0));
+            return Some(vtable_error);
         }
     };
 
+    if ty::type_is_error(ty) {
+        return Some(vtable_error);
+    }
+
     // If the type is self or a param, we look at the trait/supertrait
     // bounds to see if they include the trait we are looking for.
     let vtable_opt = match ty::get(ty).sty {
-        ty::ty_param(param_ty {idx: n, ..}) => {
-            let env_bounds = &vcx.param_env.type_param_bounds;
-            if env_bounds.len() > n {
-                let type_param_bounds: &[Rc<ty::TraitRef>] =
-                    env_bounds.get(n).trait_bounds.as_slice();
-                lookup_vtable_from_bounds(vcx, span,
-                                          type_param_bounds,
-                                          param_numbered(n),
-                                          trait_ref.clone())
-            } else {
-                None
-            }
-        }
-
-        ty::ty_self(_) => {
-            let self_param_bound = vcx.param_env.self_param_bound.clone().unwrap();
+        ty::ty_param(ParamTy {space, idx: n, ..}) => {
+            let env_bounds = &vcx.param_env.bounds;
+            let type_param_bounds = &env_bounds.get(space, n).trait_bounds;
             lookup_vtable_from_bounds(vcx, span,
-                                      [self_param_bound],
-                                      param_self,
+                                      type_param_bounds.as_slice(),
+                                      param_index { space: space,
+                                                    index: n },
                                       trait_ref.clone())
         }
 
@@ -373,8 +368,8 @@ fn search_for_vtable(vcx: &VtableContext,
         // Now, in the previous example, for_ty is bound to
         // the type self_ty, and substs is bound to [T].
         debug!("The self ty is {} and its substs are {}",
-               vcx.infcx.ty_to_str(for_ty),
-               vcx.infcx.tys_to_str(substs.tps.as_slice()));
+               for_ty.repr(tcx),
+               substs.types.repr(tcx));
 
         // Next, we unify trait_ref -- the type that we want to cast
         // to -- with of_trait_ref -- the trait that im implements. At
@@ -386,12 +381,13 @@ fn search_for_vtable(vcx: &VtableContext,
         // some value of U) with some_trait<T>. This would fail if T
         // and U weren't compatible.
 
-        debug!("(checking vtable) {}2 relating trait \
-                ty {} to of_trait_ref {}", "#",
+        let of_trait_ref = of_trait_ref.subst(tcx, &substs);
+
+        debug!("(checking vtable) num 2 relating trait \
+                ty {} to of_trait_ref {}",
                vcx.infcx.trait_ref_to_str(&*trait_ref),
                vcx.infcx.trait_ref_to_str(&*of_trait_ref));
 
-        let of_trait_ref = of_trait_ref.subst(tcx, &substs);
         relate_trait_refs(vcx, span, of_trait_ref, trait_ref.clone());
 
 
@@ -404,11 +400,12 @@ fn search_for_vtable(vcx: &VtableContext,
         // process of looking up bounds might constrain some of them.
         let im_generics =
             ty::lookup_item_type(tcx, impl_did).generics;
-        let subres = lookup_vtables(vcx, span,
-                                    im_generics.type_param_defs(), &substs,
+        let subres = lookup_vtables(vcx,
+                                    span,
+                                    &im_generics.types,
+                                    &substs,
                                     is_early);
 
-
         // substs might contain type variables, so we call
         // fixup_substs to resolve them.
         let substs_f = match fixup_substs(vcx, span,
@@ -419,15 +416,15 @@ fn search_for_vtable(vcx: &VtableContext,
             None => {
                 assert!(is_early);
                 // Bail out with a bogus answer
-                return Some(vtable_param(param_self, 0));
+                return Some(vtable_error);
             }
         };
 
         debug!("The fixed-up substs are {} - \
                 they will be unified with the bounds for \
                 the target ty, {}",
-               vcx.infcx.tys_to_str(substs_f.tps.as_slice()),
-               vcx.infcx.trait_ref_to_str(&*trait_ref));
+               substs_f.types.repr(tcx),
+               trait_ref.repr(tcx));
 
         // Next, we unify the fixed-up substitutions for the impl self
         // ty with the substitutions from the trait type that we're
@@ -515,7 +512,7 @@ fn connect_trait_tps(vcx: &VtableContext,
 }
 
 fn insert_vtables(fcx: &FnCtxt, vtable_key: MethodCall, vtables: vtable_res) {
-    debug!("insert_vtables(vtable_key={}, vtables={:?})",
+    debug!("insert_vtables(vtable_key={}, vtables={})",
            vtable_key, vtables.repr(fcx.tcx()));
     fcx.inh.vtable_map.borrow_mut().insert(vtable_key, vtables);
 }
@@ -560,12 +557,20 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
                     };
 
                       let vcx = fcx.vtable_context();
+
+                      // Take the type parameters from the object
+                      // type, but set the Self type (which is
+                      // unknown, for the object type) to be the type
+                      // we are casting from.
+                      let mut target_types = target_substs.types.clone();
+                      assert!(target_types.get_self().is_none());
+                      target_types.push(subst::SelfSpace, typ);
+
                       let target_trait_ref = Rc::new(ty::TraitRef {
                           def_id: target_def_id,
                           substs: subst::Substs {
-                              tps: target_substs.tps.clone(),
                               regions: target_substs.regions.clone(),
-                              self_ty: Some(typ)
+                              types: target_types
                           }
                       });
 
@@ -582,7 +587,9 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
                                                      is_early);
 
                       if !is_early {
-                          insert_vtables(fcx, MethodCall::expr(ex.id), vec!(vtables));
+                          let mut r = VecPerParamSpace::empty();
+                          r.push(subst::SelfSpace, vtables);
+                          insert_vtables(fcx, MethodCall::expr(ex.id), r);
                       }
 
                       // Now, if this is &trait, we need to link the
@@ -632,10 +639,10 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
             debug!("early resolve expr: def {:?} {:?}, {:?}, {}", ex.id, did, def,
                    fcx.infcx().ty_to_str(item_ty.ty));
             debug!("early_resolve_expr: looking up vtables for type params {}",
-                   item_ty.generics.type_param_defs().repr(fcx.tcx()));
+                   item_ty.generics.types.repr(fcx.tcx()));
             let vcx = fcx.vtable_context();
             let vtbls = lookup_vtables(&vcx, ex.span,
-                                       item_ty.generics.type_param_defs(),
+                                       &item_ty.generics.types,
                                        &item_substs.substs, is_early);
             if !is_early {
                 insert_vtables(fcx, MethodCall::expr(ex.id), vtbls);
@@ -657,7 +664,7 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
               let substs = fcx.method_ty_substs(ex.id);
               let vcx = fcx.vtable_context();
               let vtbls = lookup_vtables(&vcx, ex.span,
-                                         type_param_defs.as_slice(),
+                                         &type_param_defs,
                                          &substs, is_early);
               if !is_early {
                   insert_vtables(fcx, MethodCall::expr(ex.id), vtbls);
@@ -689,8 +696,7 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
                                     ty::method_call_type_param_defs(cx.tcx, method.origin);
                                 let vcx = fcx.vtable_context();
                                 let vtbls = lookup_vtables(&vcx, ex.span,
-                                                           type_param_defs.deref()
-                                                           .as_slice(),
+                                                           &type_param_defs,
                                                            &method.substs, is_early);
                                 if !is_early {
                                     insert_vtables(fcx, method_call, vtbls);
@@ -726,64 +732,84 @@ pub fn resolve_impl(tcx: &ty::ctxt,
                     impl_item: &ast::Item,
                     impl_generics: &ty::Generics,
                     impl_trait_ref: &ty::TraitRef) {
+    /*!
+     * The situation is as follows. We have some trait like:
+     *
+     *    trait Foo<A:Clone> : Bar {
+     *        fn method() { ... }
+     *    }
+     *
+     * and an impl like:
+     *
+     *    impl<B:Clone> Foo<B> for int { ... }
+     *
+     * We want to validate that the various requirements of the trait
+     * are met:
+     *
+     *    A:Clone, Self:Bar
+     *
+     * But of course after substituting the types from the impl:
+     *
+     *    B:Clone, int:Bar
+     *
+     * We store these results away as the "impl_res" for use by the
+     * default methods.
+     */
+
     debug!("resolve_impl(impl_item.id={})",
            impl_item.id);
 
-    let param_env = ty::construct_parameter_environment(
-        tcx,
-        None,
-        impl_generics.type_param_defs(),
-        [],
-        impl_generics.region_param_defs(),
-        [],
-        impl_item.id);
+    let param_env = ty::construct_parameter_environment(tcx,
+                                                        impl_generics,
+                                                        impl_item.id);
 
+    // The impl_trait_ref in our example above would be
+    //     `Foo<B> for int`
     let impl_trait_ref = impl_trait_ref.subst(tcx, &param_env.free_substs);
     debug!("impl_trait_ref={}", impl_trait_ref.repr(tcx));
 
     let infcx = &infer::new_infer_ctxt(tcx);
     let vcx = VtableContext { infcx: infcx, param_env: &param_env };
 
-    // First, check that the impl implements any trait bounds
-    // on the trait.
-    let trait_def = ty::lookup_trait_def(tcx, impl_trait_ref.def_id);
-    let vtbls = lookup_vtables(&vcx, impl_item.span,
-                               trait_def.generics.type_param_defs(),
-                               &impl_trait_ref.substs,
-                               false);
-
-    // Now, locate the vtable for the impl itself. The real
-    // purpose of this is to check for supertrait impls,
-    // but that falls out of doing this.
-    let param_bounds = ty::ParamBounds {
-        builtin_bounds: ty::empty_builtin_bounds(),
-        trait_bounds: vec!(Rc::new(impl_trait_ref))
-    };
-    let t = ty::node_id_to_type(tcx, impl_item.id);
-    let t = t.subst(tcx, &param_env.free_substs);
-    debug!("=== Doing a self lookup now.");
+    // Resolve the vtables for the trait reference on the impl.  This
+    // serves many purposes, best explained by example. Imagine we have:
+    //
+    //    trait A<T:B> : C { fn x(&self) { ... } }
+    //
+    // and
+    //
+    //    impl A<int> for uint { ... }
+    //
+    // In that case, the trait ref will be `A<int> for uint`. Resolving
+    // this will first check that the various types meet their requirements:
+    //
+    // 1. Because of T:B, int must implement the trait B
+    // 2. Because of the supertrait C, uint must implement the trait C.
+    //
+    // Simultaneously, the result of this resolution (`vtbls`), is precisely
+    // the set of vtable information needed to compile the default method
+    // `x()` adapted to the impl. (After all, a default method is basically
+    // the same as:
+    //
+    //     fn default_x<T:B, Self:A>(...) { .. .})
 
-    // Right now, we don't have any place to store this.
-    // We will need to make one so we can use this information
-    // for compiling default methods that refer to supertraits.
-    let self_vtable_res =
-        lookup_vtables_for_param(&vcx, impl_item.span, None,
-                                 &param_bounds, t, false);
+    let trait_def = ty::lookup_trait_def(tcx, impl_trait_ref.def_id);
+    let vtbls = lookup_vtables(&vcx,
+                                   impl_item.span,
+                                   &trait_def.generics.types,
+                                   &impl_trait_ref.substs,
+                                   false);
 
     infcx.resolve_regions_and_report_errors();
 
-    let res = impl_res {
-        trait_vtables: vtbls,
-        self_vtables: self_vtable_res
-    };
-    let res = writeback::resolve_impl_res(infcx, impl_item.span, &res);
+    let vtbls = writeback::resolve_impl_res(infcx, impl_item.span, &vtbls);
     let impl_def_id = ast_util::local_def(impl_item.id);
 
     debug!("impl_vtables for {} are {}",
            impl_def_id.repr(tcx),
-           res.repr(tcx));
+           vtbls.repr(tcx));
 
-    tcx.impl_vtables.borrow_mut().insert(impl_def_id, res);
+    tcx.impl_vtables.borrow_mut().insert(impl_def_id, vtbls);
 }
 
 /// Resolve vtables for a method call after typeck has finished.
@@ -791,15 +817,14 @@ pub fn resolve_impl(tcx: &ty::ctxt,
 pub fn trans_resolve_method(tcx: &ty::ctxt, id: ast::NodeId,
                             substs: &subst::Substs) -> vtable_res {
     let generics = ty::lookup_item_type(tcx, ast_util::local_def(id)).generics;
-    let type_param_defs = &*generics.type_param_defs;
     let vcx = VtableContext {
         infcx: &infer::new_infer_ctxt(tcx),
-        param_env: &ty::construct_parameter_environment(tcx, None, [], [], [], [], id)
+        param_env: &ty::construct_parameter_environment(tcx, &ty::Generics::empty(), id)
     };
 
     lookup_vtables(&vcx,
                    tcx.map.span(id),
-                   type_param_defs.as_slice(),
+                   &generics.types,
                    substs,
                    false)
 }
diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs
index d503f8bedb6..db9e90ecd50 100644
--- a/src/librustc/middle/typeck/check/writeback.rs
+++ b/src/librustc/middle/typeck/check/writeback.rs
@@ -14,7 +14,6 @@
 
 use middle::def;
 use middle::pat_util;
-use middle::subst;
 use middle::ty;
 use middle::ty_fold::{TypeFolder,TypeFoldable};
 use middle::typeck::astconv::AstConv;
@@ -22,8 +21,8 @@ use middle::typeck::check::FnCtxt;
 use middle::typeck::infer::{force_all, resolve_all, resolve_region};
 use middle::typeck::infer::resolve_type;
 use middle::typeck::infer;
-use middle::typeck::impl_res;
 use middle::typeck::{MethodCall, MethodCallee};
+use middle::typeck::vtable_res;
 use middle::typeck::write_substs_to_tcx;
 use middle::typeck::write_ty_to_tcx;
 use util::ppaux::Repr;
@@ -66,13 +65,13 @@ pub fn resolve_type_vars_in_fn(fcx: &FnCtxt,
 
 pub fn resolve_impl_res(infcx: &infer::InferCtxt,
                         span: Span,
-                        impl_res: &impl_res)
-                        -> impl_res {
+                        vtable_res: &vtable_res)
+                        -> vtable_res {
     let errors = Cell::new(false); // nobody cares
     let mut resolver = Resolver::from_infcx(infcx,
                                             &errors,
                                             ResolvingImplRes(span));
-    impl_res.resolve_in(&mut resolver)
+    vtable_res.resolve_in(&mut resolver)
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -285,22 +284,12 @@ impl<'cx> WritebackCx<'cx> {
                 debug!("writeback::resolve_method_map_entry(call={:?}, entry={})",
                        method_call,
                        method.repr(self.tcx()));
-                let mut new_method = MethodCallee {
+                let new_method = MethodCallee {
                     origin: method.origin,
                     ty: self.resolve(&method.ty, reason),
                     substs: self.resolve(&method.substs, reason),
                 };
 
-                // Wack. For some reason I don't quite know, we always
-                // hard-code the self-ty and regions to these
-                // values. Changing this causes downstream errors I
-                // don't feel like investigating right now (in
-                // particular, self_ty is set to mk_err in some cases,
-                // probably for invocations on objects, and this
-                // causes encoding failures). -nmatsakis
-                new_method.substs.self_ty = None;
-                new_method.substs.regions = subst::ErasedRegions;
-
                 self.tcx().method_map.borrow_mut().insert(
                     method_call,
                     new_method);
diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs
index d2053760467..04bf939ceb8 100644
--- a/src/librustc/middle/typeck/coherence.rs
+++ b/src/librustc/middle/typeck/coherence.rs
@@ -24,7 +24,7 @@ use middle::ty::{ImplContainer, lookup_item_type};
 use middle::ty::{t, ty_bool, ty_char, ty_bot, ty_box, ty_enum, ty_err};
 use middle::ty::{ty_str, ty_vec, ty_float, ty_infer, ty_int, ty_nil};
 use middle::ty::{ty_param, ty_param_bounds_and_ty, ty_ptr};
-use middle::ty::{ty_rptr, ty_self, ty_struct, ty_trait, ty_tup};
+use middle::ty::{ty_rptr, ty_struct, ty_trait, ty_tup};
 use middle::ty::{ty_uint, ty_uniq, ty_bare_fn, ty_closure};
 use middle::ty::type_is_ty_var;
 use middle::subst::Subst;
@@ -43,7 +43,7 @@ use syntax::ast;
 use syntax::ast_map::NodeItem;
 use syntax::ast_map;
 use syntax::ast_util::{local_def};
-use syntax::codemap::Span;
+use syntax::codemap::{Span, DUMMY_SP};
 use syntax::parse::token;
 use syntax::visit;
 
@@ -81,7 +81,7 @@ fn get_base_type(inference_context: &InferCtxt,
 
         ty_nil | ty_bot | ty_bool | ty_char | ty_int(..) | ty_uint(..) | ty_float(..) |
         ty_str(..) | ty_vec(..) | ty_bare_fn(..) | ty_closure(..) | ty_tup(..) |
-        ty_infer(..) | ty_param(..) | ty_self(..) | ty_err |
+        ty_infer(..) | ty_param(..) | ty_err |
         ty_box(_) | ty_uniq(_) | ty_ptr(_) | ty_rptr(_, _) => {
             debug!("(getting base type) no base type; found {:?}",
                    get(original_type).sty);
@@ -338,7 +338,8 @@ impl<'a> CoherenceChecker<'a> {
     // 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: DefId,
+    fn instantiate_default_methods(&self,
+                                   impl_id: DefId,
                                    trait_ref: &ty::TraitRef,
                                    all_methods: &mut Vec<DefId>) {
         let tcx = self.crate_context.tcx;
@@ -360,6 +361,7 @@ impl<'a> CoherenceChecker<'a> {
                 Rc::new(subst_receiver_types_in_method_ty(
                     tcx,
                     impl_id,
+                    &impl_poly_type,
                     trait_ref,
                     new_did,
                     &**trait_method,
@@ -368,17 +370,11 @@ impl<'a> CoherenceChecker<'a> {
             debug!("new_method_ty={}", new_method_ty.repr(tcx));
             all_methods.push(new_did);
 
-            // construct the polytype for the method based on the method_ty
-            let new_generics = ty::Generics {
-                type_param_defs:
-                    Rc::new(Vec::from_slice(impl_poly_type.generics.type_param_defs()).append(
-                            new_method_ty.generics.type_param_defs())),
-                region_param_defs:
-                    Rc::new(Vec::from_slice(impl_poly_type.generics.region_param_defs()).append(
-                            new_method_ty.generics.region_param_defs()))
-            };
+            // construct the polytype for the method based on the
+            // method_ty.  it will have all the generics from the
+            // impl, plus its own.
             let new_polytype = ty::ty_param_bounds_and_ty {
-                generics: new_generics,
+                generics: new_method_ty.generics.clone(),
                 ty: ty::mk_bare_fn(tcx, new_method_ty.fty.clone())
             };
             debug!("new_polytype={}", new_polytype.repr(tcx));
@@ -503,21 +499,11 @@ impl<'a> CoherenceChecker<'a> {
     // Converts a polytype to a monotype by replacing all parameters with
     // type variables. Returns the monotype and the type variables created.
     fn universally_quantify_polytype(&self, polytype: ty_param_bounds_and_ty)
-                                     -> UniversalQuantificationResult {
-        let region_parameters =
-            polytype.generics.region_param_defs().iter()
-            .map(|d| self.inference_context.next_region_var(
-                infer::BoundRegionInCoherence(d.name)))
-            .collect();
-
-        let bounds_count = polytype.generics.type_param_defs().len();
-        let type_parameters = self.inference_context.next_ty_vars(bounds_count);
-
-        let substitutions = subst::Substs {
-            regions: subst::NonerasedRegions(region_parameters),
-            self_ty: None,
-            tps: type_parameters
-        };
+                                     -> UniversalQuantificationResult
+    {
+        let substitutions =
+            self.inference_context.fresh_substs_for_type(DUMMY_SP,
+                                                         &polytype.generics);
         let monotype = polytype.ty.subst(self.crate_context.tcx, &substitutions);
 
         UniversalQuantificationResult {
@@ -731,69 +717,67 @@ impl<'a> CoherenceChecker<'a> {
 }
 
 pub fn make_substs_for_receiver_types(tcx: &ty::ctxt,
-                                      impl_id: ast::DefId,
                                       trait_ref: &ty::TraitRef,
                                       method: &ty::Method)
-                                      -> subst::Substs {
+                                      -> subst::Substs
+{
     /*!
      * 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.
+     * intact.
      */
 
-    let impl_polytype = ty::lookup_item_type(tcx, impl_id);
-    let num_impl_tps = impl_polytype.generics.type_param_defs().len();
-    let num_impl_regions = impl_polytype.generics.region_param_defs().len();
     let meth_tps: Vec<ty::t> =
-        method.generics.type_param_defs().iter().enumerate()
-              .map(|(i, t)| ty::mk_param(tcx, i + num_impl_tps, t.def_id))
+        method.generics.types.get_vec(subst::FnSpace)
+              .iter()
+              .map(|def| ty::mk_param_from_def(tcx, def))
               .collect();
     let meth_regions: Vec<ty::Region> =
-        method.generics.region_param_defs().iter().enumerate()
-              .map(|(i, l)| ty::ReEarlyBound(l.def_id.node, i + num_impl_regions, l.name))
+        method.generics.regions.get_vec(subst::FnSpace)
+              .iter()
+              .map(|def| ty::ReEarlyBound(def.def_id.node, def.space,
+                                          def.index, def.name))
               .collect();
-    let mut combined_tps = trait_ref.substs.tps.clone();
-    combined_tps.push_all_move(meth_tps);
-    let combined_regions = match &trait_ref.substs.regions {
-        &subst::ErasedRegions =>
-            fail!("make_substs_for_receiver_types: unexpected ErasedRegions"),
-
-        &subst::NonerasedRegions(ref rs) => {
-            let mut rs = rs.clone();
-            rs.push_all_move(meth_regions);
-            subst::NonerasedRegions(rs)
-        }
-    };
-
-    subst::Substs {
-        regions: combined_regions,
-        self_ty: trait_ref.substs.self_ty,
-        tps: combined_tps
-    }
+    trait_ref.substs.clone().with_method(meth_tps, meth_regions)
 }
 
 fn subst_receiver_types_in_method_ty(tcx: &ty::ctxt,
                                      impl_id: ast::DefId,
+                                     impl_poly_type: &ty::ty_param_bounds_and_ty,
                                      trait_ref: &ty::TraitRef,
                                      new_def_id: ast::DefId,
                                      method: &ty::Method,
                                      provided_source: Option<ast::DefId>)
-                                     -> ty::Method {
-
-    let combined_substs = make_substs_for_receiver_types(
-        tcx, impl_id, trait_ref, method);
+                                     -> ty::Method
+{
+    let combined_substs = make_substs_for_receiver_types(tcx, trait_ref, method);
+
+    debug!("subst_receiver_types_in_method_ty: combined_substs={}",
+           combined_substs.repr(tcx));
+
+    let mut method_generics = method.generics.subst(tcx, &combined_substs);
+
+    // replace the type parameters declared on the trait with those
+    // from the impl
+    for &space in [subst::TypeSpace, subst::SelfSpace].iter() {
+        *method_generics.types.get_mut_vec(space) =
+            impl_poly_type.generics.types.get_vec(space).clone();
+        *method_generics.regions.get_mut_vec(space) =
+            impl_poly_type.generics.regions.get_vec(space).clone();
+    }
 
-    ty::Method::new(
-        method.ident,
+    debug!("subst_receiver_types_in_method_ty: method_generics={}",
+           method_generics.repr(tcx));
 
-        // method types *can* appear in the generic bounds
-        method.generics.subst(tcx, &combined_substs),
+    let method_fty = method.fty.subst(tcx, &combined_substs);
 
-        // method types *can* appear in the fty
-        method.fty.subst(tcx, &combined_substs),
+    debug!("subst_receiver_types_in_method_ty: method_ty={}",
+           method.fty.repr(tcx));
 
+    ty::Method::new(
+        method.ident,
+        method_generics,
+        method_fty,
         method.explicit_self,
         method.vis,
         new_def_id,
diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs
index f1de8ff6dab..55969b79b52 100644
--- a/src/librustc/middle/typeck/collect.rs
+++ b/src/librustc/middle/typeck/collect.rs
@@ -36,7 +36,7 @@ use middle::def;
 use middle::lang_items::SizedTraitLangItem;
 use middle::resolve_lifetime;
 use middle::subst;
-use middle::subst::{Subst, Substs};
+use middle::subst::{Substs};
 use middle::ty::{ImplContainer, MethodContainer, TraitContainer};
 use middle::ty::{ty_param_bounds_and_ty};
 use middle::ty;
@@ -191,36 +191,35 @@ pub fn get_enum_variant_types(ccx: &CrateCtxt,
     }
 }
 
-pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) {
+pub fn ensure_trait_methods(ccx: &CrateCtxt,
+                            trait_id: ast::NodeId,
+                            trait_def: &ty::TraitDef) {
     let tcx = ccx.tcx;
     match tcx.map.get(trait_id) {
         ast_map::NodeItem(item) => {
             match item.node {
-                ast::ItemTrait(ref generics, _, _, ref ms) => {
-                    let trait_ty_generics = ty_generics_for_type(ccx, generics);
-
+                ast::ItemTrait(_, _, _, ref ms) => {
                     // For each method, construct a suitable ty::Method and
                     // store it into the `tcx.methods` table:
                     for m in ms.iter() {
                         let ty_method = Rc::new(match m {
                             &ast::Required(ref m) => {
                                 ty_method_of_trait_method(
-                                    ccx, trait_id, &trait_ty_generics,
+                                    ccx, trait_id, &trait_def.generics,
                                     &m.id, &m.ident, &m.explicit_self,
                                     &m.generics, &m.fn_style, &*m.decl)
                             }
 
                             &ast::Provided(ref m) => {
                                 ty_method_of_trait_method(
-                                    ccx, trait_id, &trait_ty_generics,
+                                    ccx, trait_id, &trait_def.generics,
                                     &m.id, &m.ident, &m.explicit_self,
                                     &m.generics, &m.fn_style, &*m.decl)
                             }
                         });
 
                         if ty_method.explicit_self == ast::SelfStatic {
-                            make_static_method_ty(ccx, trait_id, &*ty_method,
-                                                  &trait_ty_generics);
+                            make_static_method_ty(ccx, &*ty_method);
                         }
 
                         tcx.methods.borrow_mut().insert(ty_method.def_id,
@@ -249,129 +248,12 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) {
         _ => { /* Ignore things that aren't traits */ }
     }
 
-    fn make_static_method_ty(ccx: &CrateCtxt,
-                             trait_id: ast::NodeId,
-                             m: &ty::Method,
-                             trait_ty_generics: &ty::Generics) {
-        // If declaration is
-        //
-        //     trait Trait<'a,'b,'c,a,b,c> {
-        //        fn foo<'d,'e,'f,d,e,f>(...) -> Self;
-        //     }
-        //
-        // and we will create a function like
-        //
-        //     fn foo<'a,'b,'c,   // First the lifetime params from trait
-        //            'd,'e,'f,   // Then lifetime params from `foo()`
-        //            a,b,c,      // Then type params from trait
-        //            D:Trait<'a,'b,'c,a,b,c>, // Then this sucker
-        //            E,F,G       // Then type params from `foo()`, offset by 1
-        //           >(...) -> 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.
-        //
-        // Note also that the bound for `D` is `Trait<'a,'b,'c,a,b,c>`.
-        // This implies that the lifetime parameters that were inherited
-        // from the trait (i.e., `'a`, `'b`, and `'c`) all must be early
-        // bound, since they appear in a trait bound.
-        //
-        // 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::DefId {krate: 0, node: 0};
-
-        // Represents [A',B',C']
-        let num_trait_bounds = trait_ty_generics.type_param_defs().len();
-        let non_shifted_trait_tps = Vec::from_fn(num_trait_bounds, |i| {
-            ty::mk_param(tcx, i, trait_ty_generics.type_param_defs()[i].def_id)
-        });
-
-        // Represents [D']
-        let self_param = ty::mk_param(tcx, num_trait_bounds,
-                                      dummy_defid);
-
-        // Represents [E',F',G']
-        let num_method_bounds = m.generics.type_param_defs().len();
-        let shifted_method_tps = Vec::from_fn(num_method_bounds, |i| {
-            ty::mk_param(tcx, i + num_trait_bounds + 1,
-                         m.generics.type_param_defs()[i].def_id)
-        });
-
-        // Convert the regions 'a, 'b, 'c defined on the trait into
-        // bound regions on the fn. Note that because these appear in the
-        // bound for `Self` they must be early bound.
-        let new_early_region_param_defs = trait_ty_generics.region_param_defs.clone();
-        let rps_from_trait =
-            trait_ty_generics.region_param_defs().iter().
-            enumerate().
-            map(|(index,d)| ty::ReEarlyBound(d.def_id.node, index, d.name)).
-            collect();
-
-        // build up the substitution from
-        //     'a,'b,'c => 'a,'b,'c
-        //     A,B,C => A',B',C'
-        //     Self => D'
-        //     D,E,F => E',F',G'
-        let substs = subst::Substs {
-            regions: subst::NonerasedRegions(rps_from_trait),
-            self_ty: Some(self_param),
-            tps: non_shifted_trait_tps.append(shifted_method_tps.as_slice())
-        };
-
-        // create the type of `foo`, applying the substitution above
-        let ty = ty::mk_bare_fn(tcx, m.fty.clone()).subst(tcx, &substs);
-
-        // 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 = Vec::new();
-        let substd_type_param_defs =
-            trait_ty_generics.type_param_defs.subst(tcx, &substs);
-        new_type_param_defs.push_all(substd_type_param_defs.as_slice());
-
-        // 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 {
-            ident: special_idents::self_,
-            def_id: dummy_defid,
-            bounds: Rc::new(ty::ParamBounds {
-                builtin_bounds: ty::empty_builtin_bounds(),
-                trait_bounds: vec!(self_trait_ref)
-            }),
-            default: None
-        });
-
-        // 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.as_slice());
-
-        debug!("static method {} type_param_defs={} ty={}, substs={}",
-               m.def_id.repr(tcx),
-               new_type_param_defs.repr(tcx),
-               ty.repr(tcx),
-               substs.repr(tcx));
-
-        tcx.tcache.borrow_mut().insert(m.def_id,
-                          ty_param_bounds_and_ty {
-                              generics: ty::Generics {
-                                  type_param_defs: Rc::new(new_type_param_defs),
-                                  region_param_defs: new_early_region_param_defs
-                              },
-                              ty: ty
-                          });
+    fn make_static_method_ty(ccx: &CrateCtxt, m: &ty::Method) {
+        ccx.tcx.tcache.borrow_mut().insert(
+            m.def_id,
+            ty_param_bounds_and_ty {
+                generics: m.generics.clone(),
+                ty: ty::mk_bare_fn(ccx.tcx, m.fty.clone()) });
     }
 
     fn ty_method_of_trait_method(this: &CrateCtxt,
@@ -384,12 +266,13 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) {
                                  m_fn_style: &ast::FnStyle,
                                  m_decl: &ast::FnDecl) -> ty::Method
     {
-        let trait_self_ty = ty::mk_self(this.tcx, local_def(trait_id));
+        let trait_self_ty = ty::mk_self_type(this.tcx, local_def(trait_id));
         let fty = astconv::ty_of_method(this, *m_id, *m_fn_style, trait_self_ty,
                                         *m_explicit_self, m_decl);
-        let num_trait_type_params = trait_generics.type_param_defs().len();
-        let ty_generics = ty_generics_for_fn_or_method(this, m_generics,
-                                                       num_trait_type_params);
+        let ty_generics =
+            ty_generics_for_fn_or_method(this,
+                                         m_generics,
+                                         (*trait_generics).clone());
         ty::Method::new(
             *m_ident,
             ty_generics,
@@ -404,54 +287,6 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) {
     }
 }
 
-pub fn ensure_supertraits(ccx: &CrateCtxt,
-                          id: ast::NodeId,
-                          sp: codemap::Span,
-                          ast_trait_refs: &[ast::TraitRef],
-                          sized: ast::Sized)
-                          -> ty::BuiltinBounds
-{
-    let tcx = ccx.tcx;
-
-    // Called only the first time trait_def_of_item is called.
-    // Supertraits are ensured at the same time.
-    assert!(!tcx.supertraits.borrow().contains_key(&local_def(id)));
-
-    let self_ty = ty::mk_self(ccx.tcx, local_def(id));
-    let mut ty_trait_refs: Vec<Rc<ty::TraitRef>> = Vec::new();
-    let mut bounds = ty::empty_builtin_bounds();
-    for ast_trait_ref in ast_trait_refs.iter() {
-        let trait_def_id = ty::trait_ref_to_def_id(ccx.tcx, ast_trait_ref);
-        // FIXME(#8559): Need to instantiate the trait_ref whether or not it's a
-        // builtin trait, so that the trait's node id appears in the tcx trait_ref
-        // map. This is only needed for metadata; see the similar fixme in encoder.rs.
-        let trait_ref = instantiate_trait_ref(ccx, ast_trait_ref, self_ty);
-        if !ty::try_add_builtin_trait(ccx.tcx, trait_def_id, &mut bounds) {
-
-            // FIXME(#5527) Could have same trait multiple times
-            if ty_trait_refs.iter().any(|other_trait| other_trait.def_id == trait_ref.def_id) {
-                // This means a trait inherited from the same supertrait more
-                // than once.
-                tcx.sess.span_err(sp, "duplicate supertrait in trait declaration");
-                break;
-            } else {
-                ty_trait_refs.push(trait_ref);
-            }
-        }
-    }
-    if sized == ast::StaticSize {
-        match tcx.lang_items.require(SizedTraitLangItem) {
-            Ok(def_id) => {
-                ty::try_add_builtin_trait(tcx, def_id, &mut bounds);
-            }
-            Err(s) => tcx.sess.err(s.as_slice()),
-        };
-    }
-
-    tcx.supertraits.borrow_mut().insert(local_def(id), Rc::new(ty_trait_refs));
-    bounds
-}
-
 pub fn convert_field(ccx: &CrateCtxt,
                      struct_generics: &ty::Generics,
                      v: &ast::StructField,
@@ -490,7 +325,6 @@ fn convert_methods(ccx: &CrateCtxt,
                    ms: &[Gc<ast::Method>],
                    untransformed_rcvr_ty: ty::t,
                    rcvr_ty_generics: &ty::Generics,
-                   rcvr_ast_generics: &ast::Generics,
                    rcvr_visibility: ast::Visibility)
 {
     let tcx = ccx.tcx;
@@ -500,14 +334,11 @@ fn convert_methods(ccx: &CrateCtxt,
             tcx.sess.span_err(m.span, "duplicate method in trait impl");
         }
 
-        let num_rcvr_ty_params = rcvr_ty_generics.type_param_defs().len();
-        let m_ty_generics = ty_generics_for_fn_or_method(ccx, &m.generics,
-                                                         num_rcvr_ty_params);
         let mty = Rc::new(ty_of_method(ccx,
                                        container,
                                        &**m,
                                        untransformed_rcvr_ty,
-                                       rcvr_ast_generics,
+                                       rcvr_ty_generics,
                                        rcvr_visibility));
         let fty = ty::mk_bare_fn(tcx, mty.fty.clone());
         debug!("method {} (id {}) has type {}",
@@ -516,17 +347,8 @@ fn convert_methods(ccx: &CrateCtxt,
                 fty.repr(ccx.tcx));
         tcx.tcache.borrow_mut().insert(
             local_def(m.id),
-
-            // n.b.: the type of a method is parameterized by both
-            // the parameters on the receiver and those on the method
-            // itself
             ty_param_bounds_and_ty {
-                generics: ty::Generics {
-                    type_param_defs: Rc::new(Vec::from_slice(rcvr_ty_generics.type_param_defs())
-                                             .append(m_ty_generics.type_param_defs())),
-                    region_param_defs: Rc::new(Vec::from_slice(rcvr_ty_generics.region_param_defs())
-                                               .append(m_ty_generics.region_param_defs())),
-                },
+                generics: mty.generics.clone(),
                 ty: fty
             });
 
@@ -539,8 +361,9 @@ fn convert_methods(ccx: &CrateCtxt,
                     container: MethodContainer,
                     m: &ast::Method,
                     untransformed_rcvr_ty: ty::t,
-                    rcvr_generics: &ast::Generics,
-                    rcvr_visibility: ast::Visibility) -> ty::Method
+                    rcvr_ty_generics: &ty::Generics,
+                    rcvr_visibility: ast::Visibility)
+                    -> ty::Method
     {
         let fty = astconv::ty_of_method(ccx, m.id, m.fn_style,
                                         untransformed_rcvr_ty,
@@ -552,19 +375,17 @@ 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();
         let m_ty_generics =
-            ty_generics_for_fn_or_method(ccx, &m.generics, num_rcvr_type_params);
-        ty::Method::new(
-            m.ident,
-            m_ty_generics,
-            fty,
-            m.explicit_self.node,
-            method_vis,
-            local_def(m.id),
-            container,
-            None
-        )
+            ty_generics_for_fn_or_method(ccx, &m.generics,
+                                         (*rcvr_ty_generics).clone());
+        ty::Method::new(m.ident,
+                        m_ty_generics,
+                        fty,
+                        m.explicit_self.node,
+                        method_vis,
+                        local_def(m.id),
+                        container,
+                        None)
     }
 }
 
@@ -634,32 +455,30 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) {
                             ms.as_slice(),
                             selfty,
                             &ty_generics,
-                            generics,
                             parent_visibility);
 
             for trait_ref in opt_trait_ref.iter() {
                 instantiate_trait_ref(ccx, trait_ref, selfty);
             }
         },
-        ast::ItemTrait(ref generics, _, _, ref trait_methods) => {
+        ast::ItemTrait(_, _, _, ref trait_methods) => {
             let trait_def = trait_def_of_item(ccx, it);
 
             // Run convert_methods on the provided methods.
             let (_, provided_methods) =
                 split_trait_methods(trait_methods.as_slice());
-            let untransformed_rcvr_ty = ty::mk_self(tcx, local_def(it.id));
+            let untransformed_rcvr_ty = ty::mk_self_type(tcx, local_def(it.id));
             convert_methods(ccx,
                             TraitContainer(local_def(it.id)),
                             provided_methods.as_slice(),
                             untransformed_rcvr_ty,
                             &trait_def.generics,
-                            generics,
                             it.vis);
 
             // We need to do this *after* converting methods, since
             // convert_methods produces a tcache entry that is wrong for
             // static trait methods. This is somewhat unfortunate.
-            ensure_trait_methods(ccx, it.id);
+            ensure_trait_methods(ccx, it.id, &*trait_def);
         },
         ast::ItemStruct(struct_def, ref generics) => {
             ensure_no_ty_param_bounds(ccx, it.span, generics, "structure");
@@ -770,7 +589,7 @@ pub fn convert_struct(ccx: &CrateCtxt,
     };
     tcx.superstructs.borrow_mut().insert(local_def(id), super_struct);
 
-    let substs = mk_item_substs(ccx, &tpt.generics, None);
+    let substs = mk_item_substs(ccx, &tpt.generics);
     let selfty = ty::mk_struct(tcx, local_def(id), substs);
 
     // If this struct is enum-like or tuple-like, create the type of its
@@ -873,32 +692,124 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::Item) -> Rc<ty::TraitDef> {
         _ => {}
     }
 
-    match it.node {
+    let (generics, sized, supertraits) = match it.node {
         ast::ItemTrait(ref generics, sized, ref supertraits, _) => {
-            let self_ty = ty::mk_self(tcx, def_id);
-            let ty_generics = ty_generics_for_type(ccx, generics);
-            let substs = mk_item_substs(ccx, &ty_generics, Some(self_ty));
-            let bounds = ensure_supertraits(ccx,
-                                            it.id,
-                                            it.span,
-                                            supertraits.as_slice(),
-                                            sized);
-            let trait_def = Rc::new(ty::TraitDef {
-                generics: ty_generics,
-                bounds: bounds,
-                trait_ref: Rc::new(ty::TraitRef {
-                    def_id: def_id,
-                    substs: substs
-                })
-            });
-            tcx.trait_defs.borrow_mut().insert(def_id, trait_def.clone());
-            trait_def
+            (generics, sized, supertraits)
         }
         ref s => {
             tcx.sess.span_bug(
                 it.span,
                 format!("trait_def_of_item invoked on {:?}", s).as_slice());
         }
+    };
+
+    let substs = mk_trait_substs(ccx, it.id, generics);
+
+    let ty_generics = ty_generics_for_trait(ccx,
+                                            it.id,
+                                            &substs,
+                                            generics);
+
+    let builtin_bounds =
+        ensure_supertraits(ccx, it.id, it.span, supertraits, sized);
+
+    let substs = mk_item_substs(ccx, &ty_generics);
+    let trait_def = Rc::new(ty::TraitDef {
+        generics: ty_generics,
+        bounds: builtin_bounds,
+        trait_ref: Rc::new(ty::TraitRef {
+            def_id: def_id,
+            substs: substs
+        })
+    });
+    tcx.trait_defs.borrow_mut().insert(def_id, trait_def.clone());
+
+    return trait_def;
+
+    fn mk_trait_substs(ccx: &CrateCtxt,
+                       trait_id: ast::NodeId,
+                       generics: &ast::Generics)
+                        -> subst::Substs
+    {
+        // Creates a no-op substitution for the trait's type parameters.
+        let regions =
+            generics.lifetimes
+                    .iter()
+                    .enumerate()
+                    .map(|(i, def)| ty::ReEarlyBound(def.id,
+                                                     subst::TypeSpace,
+                                                     i, def.name))
+                    .collect();
+
+        let types =
+            generics.ty_params
+                    .iter()
+                    .enumerate()
+                    .map(|(i, def)| ty::mk_param(ccx.tcx, subst::TypeSpace,
+                                                 i, local_def(def.id)))
+                    .collect();
+
+        let self_ty =
+            ty::mk_param(ccx.tcx, subst::SelfSpace, 0, local_def(trait_id));
+
+        subst::Substs::new_trait(types, regions, self_ty)
+    }
+
+    fn ensure_supertraits(ccx: &CrateCtxt,
+                          id: ast::NodeId,
+                          sp: codemap::Span,
+                          ast_trait_refs: &Vec<ast::TraitRef>,
+                          sized: ast::Sized)
+                          -> ty::BuiltinBounds
+    {
+        let tcx = ccx.tcx;
+
+        // Called only the first time trait_def_of_item is called.
+        // Supertraits are ensured at the same time.
+        assert!(!tcx.supertraits.borrow().contains_key(&local_def(id)));
+
+        let self_ty = ty::mk_self_type(ccx.tcx, local_def(id));
+        let mut ty_trait_refs: Vec<Rc<ty::TraitRef>> = Vec::new();
+        let mut bounds = ty::empty_builtin_bounds();
+        for ast_trait_ref in ast_trait_refs.iter() {
+            let trait_def_id = ty::trait_ref_to_def_id(ccx.tcx, ast_trait_ref);
+
+            // FIXME(#8559): Need to instantiate the trait_ref whether
+            // or not it's a builtin trait, so that the trait's node
+            // id appears in the tcx trait_ref map. This is only
+            // needed for metadata; see the similar fixme in
+            // encoder.rs.
+
+            let trait_ref = instantiate_trait_ref(ccx, ast_trait_ref, self_ty);
+            if !ty::try_add_builtin_trait(ccx.tcx, trait_def_id, &mut bounds) {
+
+                // FIXME(#5527) Could have same trait multiple times
+                if ty_trait_refs.iter().any(
+                    |other_trait| other_trait.def_id == trait_ref.def_id)
+                {
+                    // This means a trait inherited from the same
+                    // supertrait more than once.
+                    tcx.sess.span_err(sp, "duplicate supertrait in \
+                                           trait declaration");
+                    break;
+                } else {
+                    ty_trait_refs.push(trait_ref);
+                }
+            }
+        }
+
+        if sized == ast::StaticSize {
+            match tcx.lang_items.require(SizedTraitLangItem) {
+                Ok(def_id) => {
+                    ty::try_add_builtin_trait(tcx, def_id, &mut bounds);
+                }
+                Err(s) => tcx.sess.err(s.as_slice()),
+            };
+        }
+
+        tcx.supertraits.borrow_mut().insert(local_def(id),
+                                            Rc::new(ty_trait_refs));
+        bounds
     }
 }
 
@@ -919,7 +830,8 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::Item)
             return tpt;
         }
         ast::ItemFn(decl, fn_style, abi, ref generics, _) => {
-            let ty_generics = ty_generics_for_fn_or_method(ccx, generics, 0);
+            let ty_generics = ty_generics_for_fn_or_method(ccx, generics,
+                                                           ty::Generics::empty());
             let tofd = astconv::ty_of_bare_fn(ccx,
                                               it.id,
                                               fn_style,
@@ -957,7 +869,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::Item)
         ast::ItemEnum(_, ref generics) => {
             // Create a new generic polytype.
             let ty_generics = ty_generics_for_type(ccx, generics);
-            let substs = mk_item_substs(ccx, &ty_generics, None);
+            let substs = mk_item_substs(ccx, &ty_generics);
             let t = ty::mk_enum(tcx, local_def(it.id), substs);
             let tpt = ty_param_bounds_and_ty {
                 generics: ty_generics,
@@ -972,7 +884,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::Item)
         }
         ast::ItemStruct(_, ref generics) => {
             let ty_generics = ty_generics_for_type(ccx, generics);
-            let substs = mk_item_substs(ccx, &ty_generics, None);
+            let substs = mk_item_substs(ccx, &ty_generics);
             let t = ty::mk_struct(tcx, local_def(it.id), substs);
             let tpt = ty_param_bounds_and_ty {
                 generics: ty_generics,
@@ -1001,10 +913,7 @@ pub fn ty_of_foreign_item(ccx: &CrateCtxt,
         }
         ast::ForeignItemStatic(t, _) => {
             ty::ty_param_bounds_and_ty {
-                generics: ty::Generics {
-                    type_param_defs: Rc::new(Vec::new()),
-                    region_param_defs: Rc::new(Vec::new()),
-                },
+                generics: ty::Generics::empty(),
                 ty: ast_ty_to_ty(ccx, &ExplicitRscope, &*t)
             }
         }
@@ -1013,75 +922,139 @@ pub fn ty_of_foreign_item(ccx: &CrateCtxt,
 
 fn ty_generics_for_type(ccx: &CrateCtxt,
                         generics: &ast::Generics)
-                        -> ty::Generics {
-    ty_generics(ccx, &generics.lifetimes, &generics.ty_params, 0)
+                        -> ty::Generics
+{
+    ty_generics(ccx, subst::TypeSpace, &generics.lifetimes,
+                &generics.ty_params, ty::Generics::empty())
+}
+
+fn ty_generics_for_trait(ccx: &CrateCtxt,
+                         trait_id: ast::NodeId,
+                         substs: &subst::Substs,
+                         generics: &ast::Generics)
+                         -> ty::Generics
+{
+    let mut generics = ty_generics(ccx, subst::TypeSpace, &generics.lifetimes,
+                                   &generics.ty_params, ty::Generics::empty());
+
+    // Something of a hack: use the node id for the trait, also as
+    // the node id for the Self type parameter.
+    let param_id = trait_id;
+
+    let self_trait_ref =
+        Rc::new(ty::TraitRef { def_id: local_def(trait_id),
+                               substs: (*substs).clone() });
+
+    let def = ty::TypeParameterDef {
+        space: subst::SelfSpace,
+        index: 0,
+        ident: special_idents::type_self,
+        def_id: local_def(param_id),
+        bounds: Rc::new(ty::ParamBounds {
+            builtin_bounds: ty::empty_builtin_bounds(),
+            trait_bounds: vec!(self_trait_ref),
+        }),
+        default: None
+    };
+
+    ccx.tcx.ty_param_defs.borrow_mut().insert(param_id, def.clone());
+
+    generics.types.push(subst::SelfSpace, def);
+
+    generics
 }
 
 fn ty_generics_for_fn_or_method(ccx: &CrateCtxt,
                                 generics: &ast::Generics,
-                                base_index: uint)
-                                -> ty::Generics {
+                                base_generics: ty::Generics)
+                                -> ty::Generics
+{
     let early_lifetimes = resolve_lifetime::early_bound_lifetimes(generics);
-    ty_generics(ccx, &early_lifetimes, &generics.ty_params, base_index)
+    ty_generics(ccx, subst::FnSpace, &early_lifetimes,
+                &generics.ty_params, base_generics)
 }
 
 fn ty_generics(ccx: &CrateCtxt,
+               space: subst::ParamSpace,
                lifetimes: &Vec<ast::Lifetime>,
-               ty_params: &OwnedSlice<ast::TyParam>,
-               base_index: uint) -> ty::Generics {
-    return ty::Generics {
-        region_param_defs: Rc::new(lifetimes.iter().map(|l| {
-                ty::RegionParameterDef { name: l.name,
-                                         def_id: local_def(l.id) }
-            }).collect()),
-        type_param_defs: Rc::new(ty_params.iter().enumerate().map(|(offset, param)| {
-            let existing_def_opt = {
-                let ty_param_defs = ccx.tcx.ty_param_defs.borrow();
-                ty_param_defs.find(&param.id).map(|def| def.clone())
-            };
-            existing_def_opt.unwrap_or_else(|| {
-                let param_ty = ty::param_ty {idx: base_index + offset,
-                                             def_id: local_def(param.id)};
-                let bounds = Rc::new(compute_bounds(ccx,
-                                                    param_ty,
-                                                    &param.bounds,
-                                                    param.sized,
-                                                    param.ident,
-                                                    param.span));
-                let default = param.default.map(|path| {
-                    let ty = ast_ty_to_ty(ccx, &ExplicitRscope, &*path);
-                    let cur_idx = param_ty.idx;
-
-                    ty::walk_ty(ty, |t| {
-                        match ty::get(t).sty {
-                            ty::ty_param(p) => if p.idx > cur_idx {
-                                ccx.tcx.sess.span_err(path.span,
-                                                        "type parameters with a default cannot use \
-                                                        forward declared identifiers")
-                            },
-                            _ => {}
-                        }
-                    });
+               types: &OwnedSlice<ast::TyParam>,
+               base_generics: ty::Generics)
+               -> ty::Generics
+{
+    let mut result = base_generics;
+
+    for (i, l) in lifetimes.iter().enumerate() {
+        result.regions.push(space,
+                            ty::RegionParameterDef { name: l.name,
+                                                     space: space,
+                                                     index: i,
+                                                     def_id: local_def(l.id) });
+    }
 
-                    ty
-                });
+    for (i, param) in types.iter().enumerate() {
+        let def = get_or_create_type_parameter_def(ccx, space, param, i);
+        debug!("def for param: {}", def.repr(ccx.tcx));
+        result.types.push(space, def);
+    }
 
-                let def = ty::TypeParameterDef {
-                    ident: param.ident,
-                    def_id: local_def(param.id),
-                    bounds: bounds,
-                    default: default
-                };
-                debug!("def for param: {}", def.repr(ccx.tcx));
-                ccx.tcx.ty_param_defs.borrow_mut().insert(param.id, def.clone());
-                def
-            })
-        }).collect()),
-    };
+    return result;
+
+    fn get_or_create_type_parameter_def(ccx: &CrateCtxt,
+                                        space: subst::ParamSpace,
+                                        param: &ast::TyParam,
+                                        index: uint)
+                                        -> ty::TypeParameterDef
+    {
+        match ccx.tcx.ty_param_defs.borrow().find(&param.id) {
+            Some(d) => { return (*d).clone(); }
+            None => { }
+        }
+
+        let param_ty = ty::ParamTy {space: space,
+                                    idx: index,
+                                    def_id: local_def(param.id)};
+        let bounds = Rc::new(compute_bounds(ccx,
+                                            param_ty,
+                                            &param.bounds,
+                                            param.sized,
+                                            param.ident,
+                                            param.span));
+        let default = param.default.map(|path| {
+            let ty = ast_ty_to_ty(ccx, &ExplicitRscope, &*path);
+            let cur_idx = param_ty.idx;
+
+            ty::walk_ty(ty, |t| {
+                match ty::get(t).sty {
+                    ty::ty_param(p) => if p.idx > cur_idx {
+                        ccx.tcx.sess.span_err(
+                            path.span,
+                            "type parameters with a default cannot use \
+                             forward declared identifiers")
+                    },
+                    _ => {}
+                }
+            });
+
+            ty
+        });
+
+        let def = ty::TypeParameterDef {
+            space: space,
+            index: index,
+            ident: param.ident,
+            def_id: local_def(param.id),
+            bounds: bounds,
+            default: default
+        };
+
+        ccx.tcx.ty_param_defs.borrow_mut().insert(param.id, def.clone());
+
+        def
+    }
 
     fn compute_bounds(
         ccx: &CrateCtxt,
-        param_ty: ty::param_ty,
+        param_ty: ty::ParamTy,
         ast_bounds: &OwnedSlice<ast::TyParamBound>,
         sized: ast::Sized,
         ident: ast::Ident,
@@ -1101,7 +1074,8 @@ fn ty_generics(ccx: &CrateCtxt,
         for ast_bound in ast_bounds.iter() {
             match *ast_bound {
                 TraitTyParamBound(ref b) => {
-                    let ty = ty::mk_param(ccx.tcx, param_ty.idx, param_ty.def_id);
+                    let ty = ty::mk_param(ccx.tcx, param_ty.space,
+                                          param_ty.idx, param_ty.def_id);
                     let trait_ref = instantiate_trait_ref(ccx, b, ty);
                     if !ty::try_add_builtin_trait(
                             ccx.tcx, trait_ref.def_id,
@@ -1117,15 +1091,15 @@ fn ty_generics(ccx: &CrateCtxt,
 
                 UnboxedFnTyParamBound(ref unboxed_function) => {
                     let rscope = ExplicitRscope;
-                    let mut trait_ref =
-                        astconv::trait_ref_for_unboxed_function(
-                            ccx,
-                            &rscope,
-                            unboxed_function);
                     let self_ty = ty::mk_param(ccx.tcx,
+                                               param_ty.space,
                                                param_ty.idx,
                                                param_ty.def_id);
-                    trait_ref.substs.self_ty = Some(self_ty);
+                    let trait_ref =
+                        astconv::trait_ref_for_unboxed_function(ccx,
+                                                                &rscope,
+                                                                unboxed_function,
+                                                                Some(self_ty));
                     param_bounds.trait_bounds.push(Rc::new(trait_ref));
                 }
 
@@ -1196,7 +1170,8 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
     }
 
     let ty_generics_for_fn_or_method =
-        ty_generics_for_fn_or_method(ccx, ast_generics, 0);
+        ty_generics_for_fn_or_method(ccx, ast_generics,
+                                     ty::Generics::empty());
     let rb = BindingRscope::new(def_id.node);
     let input_tys = decl.inputs
                         .iter()
@@ -1225,19 +1200,17 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
 }
 
 pub fn mk_item_substs(ccx: &CrateCtxt,
-                      ty_generics: &ty::Generics,
-                      self_ty: Option<ty::t>)
+                      ty_generics: &ty::Generics)
                       -> subst::Substs
 {
-    let params: Vec<ty::t> =
-        ty_generics.type_param_defs().iter().enumerate().map(
-            |(i, t)| ty::mk_param(ccx.tcx, i, t.def_id)).collect();
+    let types =
+        ty_generics.types.map(
+            |def| ty::mk_param_from_def(ccx.tcx, def));
 
-    let regions: Vec<ty::Region> =
-        ty_generics.region_param_defs().iter().enumerate().map(
-            |(i, l)| ty::ReEarlyBound(l.def_id.node, i, l.name)).collect();
+    let regions =
+        ty_generics.regions.map(
+            |def| ty::ReEarlyBound(def.def_id.node, def.space,
+                                   def.index, def.name));
 
-    subst::Substs {regions: subst::NonerasedRegions(regions),
-                   self_ty: self_ty,
-                   tps: params}
+    subst::Substs::new(types, regions)
 }
diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs
index 5099cc9c5a8..ac685151808 100644
--- a/src/librustc/middle/typeck/infer/combine.rs
+++ b/src/librustc/middle/typeck/infer/combine.rs
@@ -53,7 +53,7 @@ use middle::ty::{FloatVar, FnSig, IntVar, TyVar};
 use middle::ty::{IntType, UintType};
 use middle::ty::{BuiltinBounds};
 use middle::ty;
-use middle::typeck::infer::{then, ToUres};
+use middle::typeck::infer::{ToUres};
 use middle::typeck::infer::glb::Glb;
 use middle::typeck::infer::lub::Lub;
 use middle::typeck::infer::sub::Sub;
@@ -84,113 +84,115 @@ pub trait Combine {
     fn contratys(&self, a: ty::t, b: ty::t) -> cres<ty::t>;
     fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t>;
 
-    fn tps(&self, as_: &[ty::t], bs: &[ty::t]) -> cres<Vec<ty::t> > {
-
-        // Note: type parameters are always treated as *invariant*
-        // (otherwise the type system would be unsound).  In the
-        // future we could allow type parameters to declare a
-        // variance.
-
-        if as_.len() == bs.len() {
-            result::fold_(as_.iter().zip(bs.iter())
-                          .map(|(a, b)| eq_tys(self, *a, *b)))
-                .then(|| Ok(Vec::from_slice(as_)))
-        } else {
-            Err(ty::terr_ty_param_size(expected_found(self,
-                                                      as_.len(),
-                                                      bs.len())))
+    fn tps(&self,
+           space: subst::ParamSpace,
+           as_: &[ty::t],
+           bs: &[ty::t])
+           -> cres<Vec<ty::t>>
+    {
+        // FIXME(#5781) -- In general, we treat variance a bit wrong
+        // here. For historical reasons, we treat Self as
+        // contravariant and other tps as invariant. Both are wrong:
+        // Self may or may not be contravariant, and other tps do not
+        // need to be invariant.
+
+        if as_.len() != bs.len() {
+            return Err(ty::terr_ty_param_size(expected_found(self,
+                                                             as_.len(),
+                                                             bs.len())));
         }
-    }
-
-    fn self_tys(&self, a: Option<ty::t>, b: Option<ty::t>)
-               -> cres<Option<ty::t>> {
 
-        match (a, b) {
-            (None, None) => {
-                Ok(None)
-            }
-            (Some(a), Some(b)) => {
-                // FIXME(#5781) this should be eq_tys
-                // eq_tys(self, a, b).then(|| Ok(Some(a)) )
-                self.contratys(a, b).and_then(|t| Ok(Some(t)))
+        match space {
+            subst::SelfSpace => {
+                result::fold(as_
+                             .iter()
+                             .zip(bs.iter())
+                             .map(|(a, b)| self.contratys(*a, *b)),
+                             Vec::new(),
+                             |mut v, a| { v.push(a); v })
             }
-            (None, Some(_)) |
-                (Some(_), None) => {
-                // I think it should never happen that we unify two
-                // substs and one of them has a self_ty and one
-                // doesn't...? I could be wrong about this.
-                self.infcx().tcx.sess.bug("substitution a had a self_ty \
-                                           and substitution b didn't, or \
-                                           vice versa");
+
+            subst::TypeSpace | subst::FnSpace => {
+                try!(result::fold_(as_
+                                  .iter()
+                                  .zip(bs.iter())
+                                  .map(|(a, b)| eq_tys(self, *a, *b))));
+                Ok(Vec::from_slice(as_))
             }
         }
     }
 
     fn substs(&self,
               item_def_id: ast::DefId,
-              as_: &subst::Substs,
-              bs: &subst::Substs)
+              a_subst: &subst::Substs,
+              b_subst: &subst::Substs)
               -> cres<subst::Substs>
     {
+        let variances = ty::item_variances(self.infcx().tcx, item_def_id);
+        let mut substs = subst::Substs::empty();
+
+        for &space in subst::ParamSpace::all().iter() {
+            let a_tps = a_subst.types.get_vec(space);
+            let b_tps = b_subst.types.get_vec(space);
+            let tps = if_ok!(self.tps(space,
+                                      a_tps.as_slice(),
+                                      b_tps.as_slice()));
+
+            let a_regions = a_subst.regions().get_vec(space);
+            let b_regions = b_subst.regions().get_vec(space);
+            let r_variances = variances.regions.get_vec(space);
+            let regions = if_ok!(relate_region_params(self,
+                                                      item_def_id,
+                                                      r_variances,
+                                                      a_regions,
+                                                      b_regions));
+
+            *substs.types.get_mut_vec(space) = tps;
+            *substs.mut_regions().get_mut_vec(space) = regions;
+        }
+
+        return Ok(substs);
+
         fn relate_region_params<C:Combine>(this: &C,
                                            item_def_id: ast::DefId,
-                                           a: &subst::RegionSubsts,
-                                           b: &subst::RegionSubsts)
-                                           -> cres<subst::RegionSubsts> {
+                                           variances: &Vec<ty::Variance>,
+                                           a_rs: &Vec<ty::Region>,
+                                           b_rs: &Vec<ty::Region>)
+                                           -> cres<Vec<ty::Region>>
+        {
             let tcx = this.infcx().tcx;
-            match (a, b) {
-                (&subst::ErasedRegions, _) | (_, &subst::ErasedRegions) => {
-                    Ok(subst::ErasedRegions)
-                }
-
-                (&subst::NonerasedRegions(ref a_rs),
-                 &subst::NonerasedRegions(ref b_rs)) => {
-                    let variances = ty::item_variances(tcx, item_def_id);
-                    let region_params = &variances.region_params;
-                    let num_region_params = region_params.len();
-
-                    debug!("relate_region_params(\
-                            item_def_id={}, \
-                            a_rs={}, \
-                            b_rs={},
-                            region_params={})",
-                            item_def_id.repr(tcx),
-                            a_rs.repr(tcx),
-                            b_rs.repr(tcx),
-                            region_params.repr(tcx));
-
-                    assert_eq!(num_region_params, a_rs.len());
-                    assert_eq!(num_region_params, b_rs.len());
-                    let mut rs = vec!();
-                    for i in range(0, num_region_params) {
-                        let a_r = *a_rs.get(i);
-                        let b_r = *b_rs.get(i);
-                        let variance = *region_params.get(i);
-                        let r = match variance {
-                            ty::Invariant => {
-                                eq_regions(this, a_r, b_r)
-                                    .and_then(|()| Ok(a_r))
-                            }
-                            ty::Covariant => this.regions(a_r, b_r),
-                            ty::Contravariant => this.contraregions(a_r, b_r),
-                            ty::Bivariant => Ok(a_r),
-                        };
-                        rs.push(if_ok!(r));
+            let num_region_params = variances.len();
+
+            debug!("relate_region_params(\
+                   item_def_id={}, \
+                   a_rs={}, \
+                   b_rs={},
+                   variances={})",
+                   item_def_id.repr(tcx),
+                   a_rs.repr(tcx),
+                   b_rs.repr(tcx),
+                   variances.repr(tcx));
+
+            assert_eq!(num_region_params, a_rs.len());
+            assert_eq!(num_region_params, b_rs.len());
+            let mut rs = vec!();
+            for i in range(0, num_region_params) {
+                let a_r = *a_rs.get(i);
+                let b_r = *b_rs.get(i);
+                let variance = *variances.get(i);
+                let r = match variance {
+                    ty::Invariant => {
+                        eq_regions(this, a_r, b_r)
+                            .and_then(|()| Ok(a_r))
                     }
-                    Ok(subst::NonerasedRegions(rs))
-                }
+                    ty::Covariant => this.regions(a_r, b_r),
+                    ty::Contravariant => this.contraregions(a_r, b_r),
+                    ty::Bivariant => Ok(a_r),
+                };
+                rs.push(if_ok!(r));
             }
+            Ok(rs)
         }
-
-        let tps = if_ok!(self.tps(as_.tps.as_slice(), bs.tps.as_slice()));
-        let self_ty = if_ok!(self.self_tys(as_.self_ty, bs.self_ty));
-        let regions = if_ok!(relate_region_params(self,
-                                                  item_def_id,
-                                                  &as_.regions,
-                                                  &bs.regions));
-        Ok(subst::Substs { regions: regions,
-                           self_ty: self_ty,
-                           tps: tps.clone() })
     }
 
     fn bare_fn_tys(&self, a: &ty::BareFnTy,
diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs
index 7c313d53b2c..bcd66ed4d66 100644
--- a/src/librustc/middle/typeck/infer/error_reporting.rs
+++ b/src/librustc/middle/typeck/infer/error_reporting.rs
@@ -61,6 +61,7 @@ time of error detection.
 
 use std::collections::HashSet;
 use middle::def;
+use middle::subst;
 use middle::ty;
 use middle::ty::{Region, ReFree};
 use middle::typeck::infer;
@@ -1055,9 +1056,10 @@ impl<'a> Rebuilder<'a> {
                                 ty: _
                             } = ty::lookup_item_type(self.tcx, did);
 
-                            let expected = generics.region_param_defs().len();
-                            let lifetimes = &path.segments.last()
-                                                 .unwrap().lifetimes;
+                            let expected =
+                                generics.regions.len(subst::TypeSpace);
+                            let lifetimes =
+                                &path.segments.last().unwrap().lifetimes;
                             let mut insert = Vec::new();
                             if lifetimes.len() == 0 {
                                 let anon = self.cur_anon.get();
diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs
index 646dad879ee..f43ee1e8aab 100644
--- a/src/librustc/middle/typeck/infer/mod.rs
+++ b/src/librustc/middle/typeck/infer/mod.rs
@@ -21,7 +21,8 @@ pub use middle::typeck::infer::resolve::{resolve_ivar, resolve_all};
 pub use middle::typeck::infer::resolve::{resolve_nested_tvar};
 pub use middle::typeck::infer::resolve::{resolve_rvar};
 
-use std::collections::HashMap;
+use middle::subst;
+use middle::subst::Substs;
 use middle::ty::{TyVid, IntVid, FloatVid, RegionVid, Vid};
 use middle::ty;
 use middle::ty_fold;
@@ -37,6 +38,7 @@ use middle::typeck::infer::to_str::InferStr;
 use middle::typeck::infer::unify::{ValsAndBindings, Root};
 use middle::typeck::infer::error_reporting::ErrorReporting;
 use std::cell::{Cell, RefCell};
+use std::collections::HashMap;
 use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap;
@@ -588,6 +590,7 @@ impl<'a> InferCtxt<'a> {
             let vals = &mut ty_var_bindings.vals;
             vals.insert(id, Root(Bounds { lb: None, ub: None }, 0u));
         }
+        debug!("created type variable {}", TyVid(id));
         return TyVid(id);
     }
 
@@ -623,13 +626,35 @@ impl<'a> InferCtxt<'a> {
 
     pub fn region_vars_for_defs(&self,
                                 span: Span,
-                                defs: &[ty::RegionParameterDef])
+                                defs: &Vec<ty::RegionParameterDef>)
                                 -> Vec<ty::Region> {
         defs.iter()
             .map(|d| self.next_region_var(EarlyBoundRegion(span, d.name)))
             .collect()
     }
 
+    pub fn fresh_substs_for_type(&self,
+                                 span: Span,
+                                 generics: &ty::Generics)
+                                 -> subst::Substs
+    {
+        /*!
+         * Given a set of generics defined on a type or impl, returns
+         * a substitution mapping each type/region parameter to a
+         * fresh inference variable.
+         */
+        assert!(generics.types.len(subst::SelfSpace) == 0);
+        assert!(generics.types.len(subst::FnSpace) == 0);
+        assert!(generics.regions.len(subst::SelfSpace) == 0);
+        assert!(generics.regions.len(subst::FnSpace) == 0);
+
+        let type_parameter_count = generics.types.len(subst::TypeSpace);
+        let region_param_defs = generics.regions.get_vec(subst::TypeSpace);
+        let regions = self.region_vars_for_defs(span, region_param_defs);
+        let type_parameters = self.next_ty_vars(type_parameter_count);
+        subst::Substs::new_type(type_parameters, regions)
+    }
+
     pub fn fresh_bound_region(&self, binder_id: ast::NodeId) -> ty::Region {
         self.region_vars.new_bound(binder_id)
     }
diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs
index 56fc79fa5e7..b5c103b8481 100644
--- a/src/librustc/middle/typeck/mod.rs
+++ b/src/librustc/middle/typeck/mod.rs
@@ -66,6 +66,7 @@ use driver::config;
 use middle::def;
 use middle::resolve;
 use middle::subst;
+use middle::subst::VecPerParamSpace;
 use middle::ty;
 use util::common::time;
 use util::ppaux::Repr;
@@ -73,7 +74,6 @@ use util::ppaux;
 use util::nodemap::{DefIdMap, FnvHashMap};
 
 use std::cell::RefCell;
-use std::rc::Rc;
 use syntax::codemap::Span;
 use syntax::print::pprust::*;
 use syntax::{ast, ast_map, abi};
@@ -87,9 +87,9 @@ pub mod coherence;
 pub mod variance;
 
 #[deriving(Clone, Encodable, Decodable, PartialEq, PartialOrd)]
-pub enum param_index {
-    param_numbered(uint),
-    param_self
+pub struct param_index {
+    pub space: subst::ParamSpace,
+    pub index: uint
 }
 
 #[deriving(Clone, Encodable, Decodable)]
@@ -176,8 +176,9 @@ impl MethodCall {
 pub type MethodMap = RefCell<FnvHashMap<MethodCall, MethodCallee>>;
 
 pub type vtable_param_res = Vec<vtable_origin>;
+
 // Resolutions for bounds of all parameters, left to right, for a given path.
-pub type vtable_res = Vec<vtable_param_res>;
+pub type vtable_res = VecPerParamSpace<vtable_param_res>;
 
 #[deriving(Clone)]
 pub enum vtable_origin {
@@ -197,6 +198,14 @@ pub enum vtable_origin {
       and the second is the bound number (identifying baz)
      */
     vtable_param(param_index, uint),
+
+    /*
+      Asked to determine the vtable for ty_err. This is the value used
+      for the vtables of `Self` in a virtual call like `foo.bar()`
+      where `foo` is of object type. The same value is also used when
+      type errors occur.
+     */
+    vtable_error,
 }
 
 impl Repr for vtable_origin {
@@ -213,6 +222,10 @@ impl Repr for vtable_origin {
             vtable_param(x, y) => {
                 format!("vtable_param({:?}, {:?})", x, y)
             }
+
+            vtable_error => {
+                format!("vtable_error")
+            }
         }
     }
 }
@@ -220,33 +233,7 @@ impl Repr for vtable_origin {
 pub type vtable_map = RefCell<FnvHashMap<MethodCall, vtable_res>>;
 
 
-// Information about the vtable resolutions for a trait impl.
-// Mostly the information is important for implementing default
-// methods.
-#[deriving(Clone)]
-pub struct impl_res {
-    // resolutions for any bounded params on the trait definition
-    pub trait_vtables: vtable_res,
-    // resolutions for the trait /itself/ (and for supertraits)
-    pub self_vtables: vtable_param_res
-}
-
-impl Repr for impl_res {
-    #[cfg(stage0)]
-    fn repr(&self, tcx: &ty::ctxt) -> String {
-        format!("impl_res \\{trait_vtables={}, self_vtables={}\\}",
-                self.trait_vtables.repr(tcx),
-                self.self_vtables.repr(tcx))
-    }
-    #[cfg(not(stage0))]
-    fn repr(&self, tcx: &ty::ctxt) -> String {
-        format!("impl_res {{trait_vtables={}, self_vtables={}}}",
-                self.trait_vtables.repr(tcx),
-                self.self_vtables.repr(tcx))
-    }
-}
-
-pub type impl_vtable_map = RefCell<DefIdMap<impl_res>>;
+pub type impl_vtable_map = RefCell<DefIdMap<vtable_res>>;
 
 pub struct CrateCtxt<'a> {
     // A mapping from method call sites to traits that have that method.
@@ -268,8 +255,7 @@ pub fn write_substs_to_tcx(tcx: &ty::ctxt,
                node_id,
                item_substs.repr(tcx));
 
-        assert!(item_substs.substs.tps.iter().
-                all(|t| !ty::type_needs_infer(*t)));
+        assert!(item_substs.substs.types.all(|t| !ty::type_needs_infer(*t)));
 
         tcx.item_substs.borrow_mut().insert(node_id, item_substs);
     }
@@ -290,8 +276,8 @@ pub fn lookup_def_ccx(ccx: &CrateCtxt, sp: Span, id: ast::NodeId)
 
 pub fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
     ty::ty_param_bounds_and_ty {
-        generics: ty::Generics {type_param_defs: Rc::new(Vec::new()),
-                                region_param_defs: Rc::new(Vec::new())},
+        generics: ty::Generics {types: VecPerParamSpace::empty(),
+                                regions: VecPerParamSpace::empty()},
         ty: t
     }
 }
diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs
index 80bc09904ec..3731990e61f 100644
--- a/src/librustc/middle/typeck/variance.rs
+++ b/src/librustc/middle/typeck/variance.rs
@@ -195,14 +195,15 @@ represents the "variance transform" as defined in the paper:
 use std::collections::HashMap;
 use arena;
 use arena::Arena;
+use rl = middle::resolve_lifetime;
 use middle::subst;
+use middle::subst::{ParamSpace, FnSpace, TypeSpace, SelfSpace, VecPerParamSpace};
 use middle::ty;
 use std::fmt;
 use std::rc::Rc;
 use syntax::ast;
 use syntax::ast_map;
 use syntax::ast_util;
-use syntax::owned_slice::OwnedSlice;
 use syntax::visit;
 use syntax::visit::Visitor;
 use util::ppaux::Repr;
@@ -266,11 +267,17 @@ struct TermsContext<'a> {
     inferred_infos: Vec<InferredInfo<'a>> ,
 }
 
-enum ParamKind { TypeParam, RegionParam, SelfParam }
+#[deriving(Show)]
+enum ParamKind {
+    TypeParam,
+    RegionParam
+}
 
 struct InferredInfo<'a> {
     item_id: ast::NodeId,
     kind: ParamKind,
+    space: ParamSpace,
+    index: uint,
     param_id: ast::NodeId,
     term: VarianceTermPtr<'a>,
 }
@@ -288,9 +295,8 @@ fn determine_parameters_to_be_inferred<'a>(tcx: &'a ty::ctxt,
         // cache and share the variance struct used for items with
         // no type/region parameters
         empty_variances: Rc::new(ty::ItemVariances {
-            self_param: None,
-            type_params: OwnedSlice::empty(),
-            region_params: OwnedSlice::empty()
+            types: VecPerParamSpace::empty(),
+            regions: VecPerParamSpace::empty()
         })
     };
 
@@ -303,12 +309,15 @@ impl<'a> TermsContext<'a> {
     fn add_inferred(&mut self,
                     item_id: ast::NodeId,
                     kind: ParamKind,
+                    space: ParamSpace,
                     index: uint,
                     param_id: ast::NodeId) {
         let inf_index = InferredIndex(self.inferred_infos.len());
         let term = self.arena.alloc(|| InferredTerm(inf_index));
         self.inferred_infos.push(InferredInfo { item_id: item_id,
                                                 kind: kind,
+                                                space: space,
+                                                index: index,
                                                 param_id: param_id,
                                                 term: term });
         let newly_added = self.inferred_map.insert(param_id, inf_index);
@@ -338,7 +347,7 @@ impl<'a> Visitor<()> for TermsContext<'a> {
         // item are assigned continuous indices.
         match item.node {
             ast::ItemTrait(..) => {
-                self.add_inferred(item.id, SelfParam, 0, item.id);
+                self.add_inferred(item.id, TypeParam, SelfSpace, 0, item.id);
             }
             _ => { }
         }
@@ -348,10 +357,10 @@ impl<'a> Visitor<()> for TermsContext<'a> {
             ast::ItemStruct(_, ref generics) |
             ast::ItemTrait(ref generics, _, _, _) => {
                 for (i, p) in generics.lifetimes.iter().enumerate() {
-                    self.add_inferred(item.id, RegionParam, i, p.id);
+                    self.add_inferred(item.id, RegionParam, TypeSpace, i, p.id);
                 }
                 for (i, p) in generics.ty_params.iter().enumerate() {
-                    self.add_inferred(item.id, TypeParam, i, p.id);
+                    self.add_inferred(item.id, TypeParam, TypeSpace, i, p.id);
                 }
 
                 // If this item has no type or lifetime parameters,
@@ -399,9 +408,9 @@ struct ConstraintContext<'a> {
     // are indexed by the `ParamKind` (type, lifetime, self). Note
     // that there are no marker types for self, so the entries for
     // self are always None.
-    invariant_lang_items: [Option<ast::DefId>, ..3],
-    covariant_lang_items: [Option<ast::DefId>, ..3],
-    contravariant_lang_items: [Option<ast::DefId>, ..3],
+    invariant_lang_items: [Option<ast::DefId>, ..2],
+    covariant_lang_items: [Option<ast::DefId>, ..2],
+    contravariant_lang_items: [Option<ast::DefId>, ..2],
 
     // These are pointers to common `ConstantTerm` instances
     covariant: VarianceTermPtr<'a>,
@@ -422,9 +431,9 @@ struct Constraint<'a> {
 fn add_constraints_from_crate<'a>(terms_cx: TermsContext<'a>,
                                   krate: &ast::Crate)
                                   -> ConstraintContext<'a> {
-    let mut invariant_lang_items = [None, ..3];
-    let mut covariant_lang_items = [None, ..3];
-    let mut contravariant_lang_items = [None, ..3];
+    let mut invariant_lang_items = [None, ..2];
+    let mut covariant_lang_items = [None, ..2];
+    let mut contravariant_lang_items = [None, ..2];
 
     covariant_lang_items[TypeParam as uint] =
         terms_cx.tcx.lang_items.covariant_type();
@@ -547,7 +556,7 @@ impl<'a> ConstraintContext<'a> {
         let tcx = self.terms_cx.tcx;
         assert!(is_lifetime(&tcx.map, param_id));
         match tcx.named_region_map.find(&param_id) {
-            Some(&ast::DefEarlyBoundRegion(_, lifetime_decl_id))
+            Some(&rl::DefEarlyBoundRegion(_, _, lifetime_decl_id))
                 => lifetime_decl_id,
             Some(_) => fail!("should not encounter non early-bound cases"),
 
@@ -611,6 +620,7 @@ impl<'a> ConstraintContext<'a> {
                          param_def_id: ast::DefId,
                          item_def_id: ast::DefId,
                          kind: ParamKind,
+                         space: ParamSpace,
                          index: uint)
                          -> VarianceTermPtr<'a> {
         /*!
@@ -637,9 +647,8 @@ impl<'a> ConstraintContext<'a> {
             // variance already inferred, just look it up.
             let variances = ty::item_variances(self.tcx(), item_def_id);
             let variance = match kind {
-                SelfParam => variances.self_param.unwrap(),
-                TypeParam => *variances.type_params.get(index),
-                RegionParam => *variances.region_params.get(index),
+                TypeParam => *variances.types.get(space, index),
+                RegionParam => *variances.regions.get(space, index),
             };
             self.constant_term(variance)
         }
@@ -736,17 +745,50 @@ impl<'a> ConstraintContext<'a> {
             ty::ty_enum(def_id, ref substs) |
             ty::ty_struct(def_id, ref substs) => {
                 let item_type = ty::lookup_item_type(self.tcx(), def_id);
-                self.add_constraints_from_substs(def_id, &item_type.generics,
-                                                 substs, variance);
+                let generics = &item_type.generics;
+
+                // All type parameters on enums and structs should be
+                // in the TypeSpace.
+                assert!(generics.types.get_vec(subst::SelfSpace).is_empty());
+                assert!(generics.types.get_vec(subst::FnSpace).is_empty());
+                assert!(generics.regions.get_vec(subst::SelfSpace).is_empty());
+                assert!(generics.regions.get_vec(subst::FnSpace).is_empty());
+
+                self.add_constraints_from_substs(
+                    def_id,
+                    generics.types.get_vec(subst::TypeSpace),
+                    generics.regions.get_vec(subst::TypeSpace),
+                    substs,
+                    variance);
             }
 
             ty::ty_trait(box ty::TyTrait { def_id, ref substs, .. }) => {
                 let trait_def = ty::lookup_trait_def(self.tcx(), def_id);
-                self.add_constraints_from_substs(def_id, &trait_def.generics,
-                                                 substs, variance);
+                let generics = &trait_def.generics;
+
+                // Traits DO have a Self type parameter, but it is
+                // erased from object types.
+                assert!(!generics.types.get_vec(subst::SelfSpace).is_empty() &&
+                        substs.types.get_vec(subst::SelfSpace).is_empty());
+
+                // Traits never declare region parameters in the self
+                // space.
+                assert!(generics.regions.get_vec(subst::SelfSpace).is_empty());
+
+                // Traits never declare type/region parameters in the
+                // fn space.
+                assert!(generics.types.get_vec(subst::FnSpace).is_empty());
+                assert!(generics.regions.get_vec(subst::FnSpace).is_empty());
+
+                self.add_constraints_from_substs(
+                    def_id,
+                    generics.types.get_vec(subst::TypeSpace),
+                    generics.regions.get_vec(subst::TypeSpace),
+                    substs,
+                    variance);
             }
 
-            ty::ty_param(ty::param_ty { def_id: ref def_id, .. }) => {
+            ty::ty_param(ty::ParamTy { def_id: ref def_id, .. }) => {
                 assert_eq!(def_id.krate, ast::LOCAL_CRATE);
                 match self.terms_cx.inferred_map.find(&def_id.node) {
                     Some(&index) => {
@@ -760,12 +802,6 @@ impl<'a> ConstraintContext<'a> {
                 }
             }
 
-            ty::ty_self(ref def_id) => {
-                assert_eq!(def_id.krate, ast::LOCAL_CRATE);
-                let index = self.inferred_index(def_id.node);
-                self.add_constraint(index, variance);
-            }
-
             ty::ty_bare_fn(ty::BareFnTy { ref sig, .. }) |
             ty::ty_closure(box ty::ClosureTy {
                     ref sig,
@@ -796,28 +832,28 @@ impl<'a> ConstraintContext<'a> {
     /// object, etc) appearing in a context with ambient variance `variance`
     fn add_constraints_from_substs(&mut self,
                                    def_id: ast::DefId,
-                                   generics: &ty::Generics,
+                                   type_param_defs: &Vec<ty::TypeParameterDef>,
+                                   region_param_defs: &Vec<ty::RegionParameterDef>,
                                    substs: &subst::Substs,
                                    variance: VarianceTermPtr<'a>) {
         debug!("add_constraints_from_substs(def_id={:?})", def_id);
 
-        for (i, p) in generics.type_param_defs().iter().enumerate() {
+        for p in type_param_defs.iter() {
             let variance_decl =
-                self.declared_variance(p.def_id, def_id, TypeParam, i);
+                self.declared_variance(p.def_id, def_id, TypeParam,
+                                       p.space, p.index);
             let variance_i = self.xform(variance, variance_decl);
-            self.add_constraints_from_ty(*substs.tps.get(i), variance_i);
+            let substs_ty = *substs.types.get(p.space, p.index);
+            self.add_constraints_from_ty(substs_ty, variance_i);
         }
 
-        match substs.regions {
-            subst::ErasedRegions => {}
-            subst::NonerasedRegions(ref rps) => {
-                for (i, p) in generics.region_param_defs().iter().enumerate() {
-                    let variance_decl =
-                        self.declared_variance(p.def_id, def_id, RegionParam, i);
-                    let variance_i = self.xform(variance, variance_decl);
-                    self.add_constraints_from_region(*rps.get(i), variance_i);
-                }
-            }
+        for p in region_param_defs.iter() {
+            let variance_decl =
+                self.declared_variance(p.def_id, def_id,
+                                       RegionParam, p.space, p.index);
+            let variance_i = self.xform(variance, variance_decl);
+            let substs_r = *substs.regions().get(p.space, p.index);
+            self.add_constraints_from_region(substs_r, variance_i);
         }
     }
 
@@ -839,7 +875,7 @@ impl<'a> ConstraintContext<'a> {
                                    region: ty::Region,
                                    variance: VarianceTermPtr<'a>) {
         match region {
-            ty::ReEarlyBound(param_id, _, _) => {
+            ty::ReEarlyBound(param_id, _, _, _) => {
                 if self.is_to_be_inferred(param_id) {
                     let index = self.inferred_index(param_id);
                     self.add_constraint(index, variance);
@@ -931,7 +967,7 @@ impl<'a> SolveContext<'a> {
                 let new_value = glb(variance, old_value);
                 if old_value != new_value {
                     debug!("Updating inferred {} (node {}) \
-                            from {:?} to {:?} due to {}",
+                            from {} to {} due to {}",
                             inferred,
                             self.terms_cx
                                 .inferred_infos
@@ -965,32 +1001,29 @@ impl<'a> SolveContext<'a> {
         let num_inferred = self.terms_cx.num_inferred();
         while index < num_inferred {
             let item_id = inferred_infos.get(index).item_id;
-            let mut self_param = None;
-            let mut type_params = vec!();
-            let mut region_params = vec!();
+            let mut types = VecPerParamSpace::empty();
+            let mut regions = VecPerParamSpace::empty();
 
             while index < num_inferred &&
                   inferred_infos.get(index).item_id == item_id {
                 let info = inferred_infos.get(index);
+                let variance = *solutions.get(index);
+                debug!("Index {} Info {} / {} / {} Variance {}",
+                       index, info.index, info.kind, info.space, variance);
                 match info.kind {
-                    SelfParam => {
-                        assert!(self_param.is_none());
-                        self_param = Some(*solutions.get(index));
-                    }
                     TypeParam => {
-                        type_params.push(*solutions.get(index));
+                        types.push(info.space, variance);
                     }
                     RegionParam => {
-                        region_params.push(*solutions.get(index));
+                        regions.push(info.space, variance);
                     }
                 }
                 index += 1;
             }
 
             let item_variances = ty::ItemVariances {
-                self_param: self_param,
-                type_params: OwnedSlice::from_vec(type_params),
-                region_params: OwnedSlice::from_vec(region_params)
+                types: types,
+                regions: regions
             };
             debug!("item_id={} item_variances={}",
                     item_id,
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index ff57747256e..31994d08d23 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -9,17 +9,18 @@
 // except according to those terms.
 
 
+use middle::def;
 use middle::subst;
-use middle::subst::Subst;
+use middle::subst::{VecPerParamSpace,Subst};
 use middle::ty::{ReSkolemized, ReVar};
 use middle::ty::{BoundRegion, BrAnon, BrNamed};
 use middle::ty::{BrFresh, ctxt};
-use middle::ty::{mt, t, param_ty};
+use middle::ty::{mt, t, ParamTy};
 use middle::ty::{ReFree, ReScope, ReInfer, ReStatic, Region,
                  ReEmpty};
 use middle::ty::{ty_bool, ty_char, ty_bot, ty_box, ty_struct, ty_enum};
 use middle::ty::{ty_err, ty_str, ty_vec, ty_float, ty_bare_fn, ty_closure};
-use middle::ty::{ty_nil, ty_param, ty_ptr, ty_rptr, ty_self, ty_tup};
+use middle::ty::{ty_nil, ty_param, ty_ptr, ty_rptr, ty_tup};
 use middle::ty::{ty_uniq, ty_trait, ty_int, ty_uint, ty_infer};
 use middle::ty;
 use middle::typeck;
@@ -188,7 +189,7 @@ pub fn region_to_str(cx: &ctxt, prefix: &str, space: bool, region: Region) -> St
     // `explain_region()` or `note_and_explain_region()`.
     match region {
         ty::ReScope(_) => prefix.to_string(),
-        ty::ReEarlyBound(_, _, name) => {
+        ty::ReEarlyBound(_, _, _, name) => {
             token::get_name(name).get().to_string()
         }
         ty::ReLateBound(_, br) => bound_region_to_str(cx, prefix, space, br),
@@ -375,7 +376,7 @@ pub fn ty_to_str(cx: &ctxt, typ: t) -> String {
       }
       ty_infer(infer_ty) => infer_ty.to_str(),
       ty_err => "[type error]".to_string(),
-      ty_param(param_ty {idx: id, def_id: did}) => {
+      ty_param(ParamTy {idx: id, def_id: did, ..}) => {
           let ident = match cx.ty_param_defs.borrow().find(&did.node) {
               Some(def) => token::get_ident(def.ident).get().to_string(),
               // This can only happen when a type mismatch error happens and
@@ -391,29 +392,25 @@ pub fn ty_to_str(cx: &ctxt, typ: t) -> String {
               format!("{}:{:?}", ident, did)
           }
       }
-      ty_self(..) => "Self".to_string(),
       ty_enum(did, ref substs) | ty_struct(did, ref substs) => {
-        let base = ty::item_path_str(cx, did);
-        parameterized(cx,
-                      base.as_slice(),
-                      &substs.regions,
-                      substs.tps.as_slice(),
-                      did,
-                      false)
+          let base = ty::item_path_str(cx, did);
+          let generics = ty::lookup_item_type(cx, did).generics;
+          parameterized(cx, base.as_slice(), substs, &generics)
       }
       ty_trait(box ty::TyTrait {
           def_id: did, ref substs, store, ref bounds
       }) => {
-        let base = ty::item_path_str(cx, did);
-        let ty = parameterized(cx, base.as_slice(), &substs.regions,
-                               substs.tps.as_slice(), did, true);
-        let bound_sep = if bounds.is_empty() { "" } else { ":" };
-        let bound_str = bounds.repr(cx);
-        format!("{}{}{}{}",
-                trait_store_to_str(cx, store),
-                ty,
-                bound_sep,
-                bound_str)
+          let base = ty::item_path_str(cx, did);
+          let trait_def = ty::lookup_trait_def(cx, did);
+          let ty = parameterized(cx, base.as_slice(),
+                                 substs, &trait_def.generics);
+          let bound_sep = if bounds.is_empty() { "" } else { ":" };
+          let bound_str = bounds.repr(cx);
+          format!("{}{}{}{}",
+                  trait_store_to_str(cx, store),
+                  ty,
+                  bound_sep,
+                  bound_str)
       }
       ty_str => "str".to_string(),
       ty_vec(ref mt, sz) => {
@@ -429,39 +426,38 @@ pub fn ty_to_str(cx: &ctxt, typ: t) -> String {
 
 pub fn parameterized(cx: &ctxt,
                      base: &str,
-                     regions: &subst::RegionSubsts,
-                     tps: &[ty::t],
-                     did: ast::DefId,
-                     is_trait: bool)
-                     -> String {
+                     substs: &subst::Substs,
+                     generics: &ty::Generics)
+                     -> String
+{
     let mut strs = Vec::new();
-    match *regions {
+
+    match substs.regions {
         subst::ErasedRegions => { }
         subst::NonerasedRegions(ref regions) => {
             for &r in regions.iter() {
-                strs.push(region_to_str(cx, "", false, r))
+                let s = region_to_str(cx, "", false, r);
+                if !s.is_empty() {
+                    strs.push(s)
+                } else {
+                    // This happens when the value of the region
+                    // parameter is not easily serialized. This may be
+                    // because the user omitted it in the first place,
+                    // or because it refers to some block in the code,
+                    // etc. I'm not sure how best to serialize this.
+                    strs.push(format!("'_"));
+                }
             }
         }
     }
 
-    let generics = if is_trait {
-        ty::lookup_trait_def(cx, did).generics.clone()
-    } else {
-        ty::lookup_item_type(cx, did).generics
-    };
-    let ty_params = generics.type_param_defs();
+    let tps = substs.types.get_vec(subst::TypeSpace);
+    let ty_params = generics.types.get_vec(subst::TypeSpace);
     let has_defaults = ty_params.last().map_or(false, |def| def.default.is_some());
-    let num_defaults = if has_defaults {
-        // We should have a borrowed version of substs instead of cloning.
-        let mut substs = subst::Substs {
-            tps: Vec::from_slice(tps),
-            regions: regions.clone(),
-            self_ty: None
-        };
+    let num_defaults = if has_defaults && !cx.sess.verbose() {
         ty_params.iter().zip(tps.iter()).rev().take_while(|&(def, &actual)| {
-            substs.tps.pop();
             match def.default {
-                Some(default) => default.subst(cx, &substs) == actual,
+                Some(default) => default.subst(cx, substs) == actual,
                 None => false
             }
         }).count()
@@ -473,6 +469,12 @@ pub fn parameterized(cx: &ctxt,
         strs.push(ty_to_str(cx, *t))
     }
 
+    if cx.sess.verbose() {
+        for t in substs.types.get_vec(subst::SelfSpace).iter() {
+            strs.push(format!("for {}", t.repr(cx)));
+        }
+    }
+
     if strs.len() > 0u {
         format!("{}<{}>", base, strs.connect(","))
     } else {
@@ -554,6 +556,12 @@ impl<T:Repr> Repr for Vec<T> {
     }
 }
 
+impl Repr for def::Def {
+    fn repr(&self, _tcx: &ctxt) -> String {
+        format!("{:?}", *self)
+    }
+}
+
 impl Repr for ty::TypeParameterDef {
     fn repr(&self, tcx: &ctxt) -> String {
         format!("TypeParameterDef({:?}, {})", self.def_id,
@@ -577,10 +585,18 @@ impl Repr for ty::t {
 
 impl Repr for subst::Substs {
     fn repr(&self, tcx: &ctxt) -> String {
-        format!("substs(regions={}, self_ty={}, tps={})",
-                self.regions.repr(tcx),
-                self.self_ty.repr(tcx),
-                self.tps.repr(tcx))
+        format!("Substs[types={}, regions={}]",
+                       self.types.repr(tcx),
+                       self.regions.repr(tcx))
+    }
+}
+
+impl<T:Repr> Repr for subst::VecPerParamSpace<T> {
+    fn repr(&self, tcx: &ctxt) -> String {
+        format!("[{};{};{}]",
+                       self.get_vec(subst::TypeSpace).repr(tcx),
+                       self.get_vec(subst::SelfSpace).repr(tcx),
+                       self.get_vec(subst::FnSpace).repr(tcx))
     }
 }
 
@@ -630,6 +646,12 @@ impl Repr for ast::Expr {
     }
 }
 
+impl Repr for ast::Path {
+    fn repr(&self, _tcx: &ctxt) -> String {
+        format!("path({})", pprust::path_to_str(self))
+    }
+}
+
 impl Repr for ast::Item {
     fn repr(&self, tcx: &ctxt) -> String {
         format!("item({})", tcx.map.node_to_str(self.id))
@@ -665,11 +687,12 @@ impl Repr for ty::BoundRegion {
 impl Repr for ty::Region {
     fn repr(&self, tcx: &ctxt) -> String {
         match *self {
-            ty::ReEarlyBound(id, index, name) => {
-                format!("ReEarlyBound({}, {}, {})",
-                        id,
-                        index,
-                        token::get_name(name))
+            ty::ReEarlyBound(id, space, index, name) => {
+                format!("ReEarlyBound({}, {}, {}, {})",
+                               id,
+                               space,
+                               index,
+                               token::get_name(name))
             }
 
             ty::ReLateBound(binder_id, ref bound_region) => {
@@ -697,9 +720,7 @@ impl Repr for ty::Region {
             }
 
             ty::ReInfer(ReSkolemized(id, ref bound_region)) => {
-                format!("re_skolemized({}, {})",
-                               id,
-                               bound_region.repr(tcx))
+                format!("re_skolemized({}, {})", id, bound_region.repr(tcx))
             }
 
             ty::ReEmpty => {
@@ -753,18 +774,18 @@ impl Repr for ty::ty_param_bounds_and_ty {
 
 impl Repr for ty::Generics {
     fn repr(&self, tcx: &ctxt) -> String {
-        format!("Generics(type_param_defs: {}, region_param_defs: {})",
-                self.type_param_defs().repr(tcx),
-                self.region_param_defs().repr(tcx))
+        format!("Generics(types: {}, regions: {})",
+                self.types.repr(tcx),
+                self.regions.repr(tcx))
     }
 }
 
 impl Repr for ty::ItemVariances {
     fn repr(&self, tcx: &ctxt) -> String {
-        format!("IterVariances(self_param={}, type_params={}, region_params={})",
-                self.self_param.repr(tcx),
-                self.type_params.repr(tcx),
-                self.region_params.repr(tcx))
+        format!("ItemVariances(types={}, \
+                regions={})",
+                self.types.repr(tcx),
+                self.regions.repr(tcx))
     }
 }
 
@@ -952,23 +973,8 @@ impl UserString for ty::BuiltinBounds {
 impl UserString for ty::TraitRef {
     fn user_string(&self, tcx: &ctxt) -> String {
         let base = ty::item_path_str(tcx, self.def_id);
-        if tcx.sess.verbose() && self.substs.self_ty.is_some() {
-            let mut all_tps = self.substs.tps.clone();
-            for &t in self.substs.self_ty.iter() { all_tps.push(t); }
-            parameterized(tcx,
-                          base.as_slice(),
-                          &self.substs.regions,
-                          all_tps.as_slice(),
-                          self.def_id,
-                          true)
-        } else {
-            parameterized(tcx,
-                          base.as_slice(),
-                          &self.substs.regions,
-                          self.substs.tps.as_slice(),
-                          self.def_id,
-                          true)
-        }
+        let trait_def = ty::lookup_trait_def(tcx, self.def_id);
+        parameterized(tcx, base.as_slice(), &self.substs, &trait_def.generics)
     }
 }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index db65939bdfc..dd6cc978ae7 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -27,6 +27,7 @@ use rustc::metadata::csearch;
 use rustc::metadata::decoder;
 use rustc::middle::def;
 use rustc::middle::subst;
+use rustc::middle::subst::VecPerParamSpace;
 use rustc::middle::ty;
 
 use std::rc::Rc;
@@ -53,6 +54,12 @@ impl<T: Clean<U>, U> Clean<Vec<U>> for Vec<T> {
     }
 }
 
+impl<T: Clean<U>, U> Clean<VecPerParamSpace<U>> for VecPerParamSpace<T> {
+    fn clean(&self) -> VecPerParamSpace<U> {
+        self.map(|x| x.clean())
+    }
+}
+
 impl<T: Clean<U>, U> Clean<U> for Gc<T> {
     fn clean(&self) -> U {
         (**self).clean()
@@ -488,17 +495,17 @@ impl Clean<TyParamBound> for ast::TyParamBound {
 }
 
 fn external_path(name: &str, substs: &subst::Substs) -> Path {
+    let lifetimes = substs.regions().get_vec(subst::TypeSpace)
+                    .iter()
+                    .filter_map(|v| v.clean())
+                    .collect();
+    let types = substs.types.get_vec(subst::TypeSpace).clean();
     Path {
         global: false,
         segments: vec![PathSegment {
             name: name.to_string(),
-            lifetimes: match substs.regions {
-                subst::ErasedRegions => Vec::new(),
-                subst::NonerasedRegions(ref v) => {
-                    v.iter().filter_map(|v| v.clean()).collect()
-                }
-            },
-            types: substs.tps.clean(),
+            lifetimes: lifetimes,
+            types: types,
         }],
     }
 }
@@ -578,12 +585,8 @@ impl Clean<Vec<TyParamBound>> for ty::ParamBounds {
 impl Clean<Option<Vec<TyParamBound>>> for subst::Substs {
     fn clean(&self) -> Option<Vec<TyParamBound>> {
         let mut v = Vec::new();
-        match self.regions {
-            subst::NonerasedRegions(..) => v.push(RegionBound),
-            subst::ErasedRegions => {}
-        }
-        v.extend(self.tps.iter().map(|t| TraitBound(t.clean())));
-
+        v.extend(self.regions().iter().map(|_| RegionBound));
+        v.extend(self.types.iter().map(|t| TraitBound(t.clean())));
         if v.len() > 0 {Some(v)} else {None}
     }
 }
@@ -617,7 +620,7 @@ impl Clean<Option<Lifetime>> for ty::Region {
             ty::ReStatic => Some(Lifetime("static".to_string())),
             ty::ReLateBound(_, ty::BrNamed(_, name)) =>
                 Some(Lifetime(token::get_name(name).get().to_string())),
-            ty::ReEarlyBound(_, _, name) => Some(Lifetime(name.clean())),
+            ty::ReEarlyBound(_, _, _, name) => Some(Lifetime(name.clean())),
 
             ty::ReLateBound(..) |
             ty::ReFree(..) |
@@ -638,17 +641,41 @@ pub struct Generics {
 impl Clean<Generics> for ast::Generics {
     fn clean(&self) -> Generics {
         Generics {
-            lifetimes: self.lifetimes.clean().move_iter().collect(),
-            type_params: self.ty_params.clean().move_iter().collect(),
+            lifetimes: self.lifetimes.clean(),
+            type_params: self.ty_params.clean(),
         }
     }
 }
 
 impl Clean<Generics> for ty::Generics {
     fn clean(&self) -> Generics {
+        // In the type space, generics can come in one of multiple
+        // namespaces.  This means that e.g. for fn items the type
+        // parameters will live in FnSpace, but for types the
+        // parameters will live in TypeSpace (trait definitions also
+        // define a parameter in SelfSpace). *Method* definitions are
+        // the one exception: they combine the TypeSpace parameters
+        // from the enclosing impl/trait with their own FnSpace
+        // parameters.
+        //
+        // In general, when we clean, we are trying to produce the
+        // "user-facing" generics. Hence we select the most specific
+        // namespace that is occupied, ignoring SelfSpace because it
+        // is implicit.
+
+        let space = {
+            if !self.types.get_vec(subst::FnSpace).is_empty() ||
+                !self.regions.get_vec(subst::FnSpace).is_empty()
+            {
+                subst::FnSpace
+            } else {
+                subst::TypeSpace
+            }
+        };
+
         Generics {
-            lifetimes: self.region_param_defs.clean(),
-            type_params: self.type_param_defs.clean(),
+            type_params: self.types.get_vec(space).clean(),
+            lifetimes: self.regions.get_vec(space).clean(),
         }
     }
 }
@@ -1259,8 +1286,13 @@ impl Clean<Type> for ty::t {
             }
             ty::ty_tup(ref t) => Tuple(t.iter().map(|t| t.clean()).collect()),
 
-            ty::ty_param(ref p) => Generic(p.def_id),
-            ty::ty_self(did) => Self(did),
+            ty::ty_param(ref p) => {
+                if p.space == subst::SelfSpace {
+                    Self(p.def_id)
+                } else {
+                    Generic(p.def_id)
+                }
+            }
 
             ty::ty_infer(..) => fail!("ty_infer"),
             ty::ty_err => fail!("ty_err"),
@@ -1968,7 +2000,7 @@ fn resolve_type(path: Path, tpbs: Option<Vec<TyParamBound>>,
             ast::TyFloat(ast::TyF64) => return Primitive(F64),
             ast::TyFloat(ast::TyF128) => return Primitive(F128),
         },
-        def::DefTyParam(i, _) => return Generic(i),
+        def::DefTyParam(_, i, _) => return Generic(i),
         def::DefTyParamBinder(i) => return TyParamBinder(i),
         _ => {}
     };
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 7c1e82a2a6f..941078b158b 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -208,14 +208,6 @@ impl Generics {
     }
 }
 
-#[deriving(Clone, PartialEq, Eq, Hash, Encodable, Decodable, Show)]
-pub enum DefRegion {
-    DefStaticRegion,
-    DefEarlyBoundRegion(/* index */ uint, /* lifetime decl */ NodeId),
-    DefLateBoundRegion(/* binder_id */ NodeId, /* depth */ uint, /* lifetime decl */ NodeId),
-    DefFreeRegion(/* block scope */ NodeId, /* lifetime decl */ NodeId),
-}
-
 // The set of MetaItems that define the compilation environment of the crate,
 // used to drive conditional compilation
 pub type CrateConfig = Vec<Gc<MetaItem>>;
diff --git a/src/test/compile-fail/bad-mid-path-type-params.rs b/src/test/compile-fail/bad-mid-path-type-params.rs
index 58ed6624cbf..7ce6f7188b8 100644
--- a/src/test/compile-fail/bad-mid-path-type-params.rs
+++ b/src/test/compile-fail/bad-mid-path-type-params.rs
@@ -45,11 +45,16 @@ impl Trait<int> for S2 {
 
 fn foo<'a>() {
     let _ = S::new::<int,f64>(1, 1.0);
-    //~^ ERROR the impl referenced by this path needs 1 type parameter, but 0 type parameters were supplied
-    let _ = S::<'a,int>::new::<f64>(1, 1.0); //~ ERROR expected 0 lifetime parameters
+    //~^ ERROR too many type parameters provided
+
+    let _ = S::<'a,int>::new::<f64>(1, 1.0);
+    //~^ ERROR too many lifetime parameters provided
+
     let _: S2 = Trait::new::<int,f64>(1, 1.0);
-    //~^ ERROR the trait referenced by this path needs 1 type parameter, but 0 type parameters were supplied
-    let _: S2 = Trait::<'a,int>::new::<f64>(1, 1.0); //~ ERROR expected 0 lifetime parameters
+    //~^ ERROR too many type parameters provided
+
+    let _: S2 = Trait::<'a,int>::new::<f64>(1, 1.0);
+    //~^ ERROR too many lifetime parameters provided
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/generic-impl-less-params-with-defaults.rs b/src/test/compile-fail/generic-impl-less-params-with-defaults.rs
index d6a47ffd786..7e7eee3cfac 100644
--- a/src/test/compile-fail/generic-impl-less-params-with-defaults.rs
+++ b/src/test/compile-fail/generic-impl-less-params-with-defaults.rs
@@ -18,7 +18,5 @@ impl<A, B, C = (A, B)> Foo<A, B, C> {
 
 fn main() {
     Foo::<int>::new();
-    //~^ ERROR the impl referenced by this path needs at least 2 type parameters,
-    //         but 1 was supplied
-    //~^^^ ERROR not enough type parameters provided: expected at least 2, found 1
+    //~^ ERROR too few type parameters provided
 }
diff --git a/src/test/compile-fail/generic-impl-more-params-with-defaults.rs b/src/test/compile-fail/generic-impl-more-params-with-defaults.rs
index b0040878d62..ceaed9438be 100644
--- a/src/test/compile-fail/generic-impl-more-params-with-defaults.rs
+++ b/src/test/compile-fail/generic-impl-more-params-with-defaults.rs
@@ -20,7 +20,5 @@ impl<T, A = Heap> Vec<T, A> {
 
 fn main() {
     Vec::<int, Heap, bool>::new();
-    //~^ ERROR the impl referenced by this path needs at most 2 type parameters,
-    //         but 3 were supplied
-    //~^^^ ERROR too many type parameters provided: expected at most 2, found 3
+    //~^ ERROR too many type parameters provided
 }
diff --git a/src/test/compile-fail/issue-11844.rs b/src/test/compile-fail/issue-11844.rs
index 4e11481b5d2..e5400bf60c3 100644
--- a/src/test/compile-fail/issue-11844.rs
+++ b/src/test/compile-fail/issue-11844.rs
@@ -12,7 +12,7 @@ fn main() {
     let a = Some(box 1);
     match a {
         Ok(a) => //~ ERROR: mismatched types
-            println!("{}",a), //~ ERROR: failed to find an implementation of trait
+            println!("{}",a),
         None => fail!()
     }
 }
diff --git a/src/test/compile-fail/issue-13466.rs b/src/test/compile-fail/issue-13466.rs
index 44a52148e5b..14cc0a82df5 100644
--- a/src/test/compile-fail/issue-13466.rs
+++ b/src/test/compile-fail/issue-13466.rs
@@ -15,7 +15,6 @@ pub fn main() {
     // the actual arm `Result<T, E>` has two. typeck should not be
     // tricked into looking up a non-existing second type parameter.
     let _x: uint = match Some(1u) {
-    //~^ ERROR mismatched types: expected `uint` but found `<generic #0>`
         Ok(u) => u, //~ ERROR  mismatched types: expected `core::option::Option<uint>`
         Err(e) => fail!(e)  //~ ERROR mismatched types: expected `core::option::Option<uint>`
     };
diff --git a/src/test/compile-fail/issue-7092.rs b/src/test/compile-fail/issue-7092.rs
index bcecab80758..4b1c6791874 100644
--- a/src/test/compile-fail/issue-7092.rs
+++ b/src/test/compile-fail/issue-7092.rs
@@ -15,7 +15,6 @@ fn foo(x: Whatever) {
     match x {
         Some(field) => field.access(),
         //~^ ERROR: mismatched types: expected `Whatever` but found
-        //~^^ ERROR: does not implement any method in scope named `access`
     }
 }
 
diff --git a/src/test/compile-fail/variance-regions-direct.rs b/src/test/compile-fail/variance-regions-direct.rs
index ae8444f015e..da4f9846187 100644
--- a/src/test/compile-fail/variance-regions-direct.rs
+++ b/src/test/compile-fail/variance-regions-direct.rs
@@ -14,7 +14,7 @@
 // Regions that just appear in normal spots are contravariant:
 
 #[rustc_variance]
-struct Test2<'a, 'b, 'c> { //~ ERROR region_params=[-, -, -]
+struct Test2<'a, 'b, 'c> { //~ ERROR regions=[[-, -, -];[];[]]
     x: &'a int,
     y: &'b [int],
     c: &'c str
@@ -23,7 +23,7 @@ struct Test2<'a, 'b, 'c> { //~ ERROR region_params=[-, -, -]
 // Those same annotations in function arguments become covariant:
 
 #[rustc_variance]
-struct Test3<'a, 'b, 'c> { //~ ERROR region_params=[+, +, +]
+struct Test3<'a, 'b, 'c> { //~ ERROR regions=[[+, +, +];[];[]]
     x: extern "Rust" fn(&'a int),
     y: extern "Rust" fn(&'b [int]),
     c: extern "Rust" fn(&'c str),
@@ -32,7 +32,7 @@ struct Test3<'a, 'b, 'c> { //~ ERROR region_params=[+, +, +]
 // Mutability induces invariance:
 
 #[rustc_variance]
-struct Test4<'a, 'b> { //~ ERROR region_params=[-, o]
+struct Test4<'a, 'b> { //~ ERROR regions=[[-, o];[];[]]
     x: &'a mut &'b int,
 }
 
@@ -40,7 +40,7 @@ struct Test4<'a, 'b> { //~ ERROR region_params=[-, o]
 // contravariant context:
 
 #[rustc_variance]
-struct Test5<'a, 'b> { //~ ERROR region_params=[+, o]
+struct Test5<'a, 'b> { //~ ERROR regions=[[+, o];[];[]]
     x: extern "Rust" fn(&'a mut &'b int),
 }
 
@@ -50,21 +50,21 @@ struct Test5<'a, 'b> { //~ ERROR region_params=[+, o]
 // argument list occurs in an invariant context.
 
 #[rustc_variance]
-struct Test6<'a, 'b> { //~ ERROR region_params=[-, o]
+struct Test6<'a, 'b> { //~ ERROR regions=[[-, o];[];[]]
     x: &'a mut extern "Rust" fn(&'b int),
 }
 
 // No uses at all is bivariant:
 
 #[rustc_variance]
-struct Test7<'a> { //~ ERROR region_params=[*]
+struct Test7<'a> { //~ ERROR regions=[[*];[];[]]
     x: int
 }
 
 // Try enums too.
 
 #[rustc_variance]
-enum Test8<'a, 'b, 'c> { //~ ERROR region_params=[+, -, o]
+enum Test8<'a, 'b, 'c> { //~ ERROR regions=[[+, -, o];[];[]]
     Test8A(extern "Rust" fn(&'a int)),
     Test8B(&'b [int]),
     Test8C(&'b mut &'c str),
diff --git a/src/test/compile-fail/variance-regions-indirect.rs b/src/test/compile-fail/variance-regions-indirect.rs
index fa22bb41aa3..913335fa51b 100644
--- a/src/test/compile-fail/variance-regions-indirect.rs
+++ b/src/test/compile-fail/variance-regions-indirect.rs
@@ -13,29 +13,29 @@
 // Try enums too.
 
 #[rustc_variance]
-enum Base<'a, 'b, 'c, 'd> { //~ ERROR region_params=[+, -, o, *]
+enum Base<'a, 'b, 'c, 'd> { //~ ERROR regions=[[+, -, o, *];[];[]]
     Test8A(extern "Rust" fn(&'a int)),
     Test8B(&'b [int]),
     Test8C(&'b mut &'c str),
 }
 
 #[rustc_variance]
-struct Derived1<'w, 'x, 'y, 'z> { //~ ERROR region_params=[*, o, -, +]
+struct Derived1<'w, 'x, 'y, 'z> { //~ ERROR regions=[[*, o, -, +];[];[]]
     f: Base<'z, 'y, 'x, 'w>
 }
 
 #[rustc_variance] // Combine - and + to yield o
-struct Derived2<'a, 'b, 'c> { //~ ERROR region_params=[o, o, *]
+struct Derived2<'a, 'b, 'c> { //~ ERROR regions=[[o, o, *];[];[]]
     f: Base<'a, 'a, 'b, 'c>
 }
 
 #[rustc_variance] // Combine + and o to yield o (just pay attention to 'a here)
-struct Derived3<'a, 'b, 'c> { //~ ERROR region_params=[o, -, *]
+struct Derived3<'a, 'b, 'c> { //~ ERROR regions=[[o, -, *];[];[]]
     f: Base<'a, 'b, 'a, 'c>
 }
 
 #[rustc_variance] // Combine + and * to yield + (just pay attention to 'a here)
-struct Derived4<'a, 'b, 'c> { //~ ERROR region_params=[+, -, o]
+struct Derived4<'a, 'b, 'c> { //~ ERROR regions=[[+, -, o];[];[]]
     f: Base<'a, 'b, 'c, 'a>
 }