diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index f8d041bc923..a39be31c4b5 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -16,6 +16,7 @@
 
 #![allow(non_camel_case_types)]
 
+use middle::subst;
 use middle::ty;
 
 use std::rc::Rc;
@@ -25,7 +26,6 @@ use std::uint;
 use syntax::abi;
 use syntax::ast;
 use syntax::ast::*;
-use syntax::owned_slice::OwnedSlice;
 use syntax::parse::token;
 
 // Compact string representation for ty::t values. API ty_str &
@@ -133,7 +133,7 @@ pub fn parse_trait_ref_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tc
 }
 
 pub fn parse_substs_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tcx: &ty::ctxt,
-                         conv: conv_did) -> ty::substs {
+                         conv: conv_did) -> subst::Substs {
     let mut st = parse_state_from_data(data, crate_num, pos, tcx);
     parse_substs(&mut st, conv)
 }
@@ -162,7 +162,7 @@ fn parse_trait_store(st: &mut PState, conv: conv_did) -> ty::TraitStore {
     }
 }
 
-fn parse_substs(st: &mut PState, conv: conv_did) -> ty::substs {
+fn parse_substs(st: &mut PState, conv: conv_did) -> subst::Substs {
     let regions = parse_region_substs(st, |x,y| conv(x,y));
 
     let self_ty = parse_opt(st, |st| parse_ty(st, |x,y| conv(x,y)) );
@@ -172,16 +172,16 @@ fn parse_substs(st: &mut PState, conv: conv_did) -> ty::substs {
     while peek(st) != ']' { params.push(parse_ty(st, |x,y| conv(x,y))); }
     st.pos = st.pos + 1u;
 
-    return ty::substs {
+    return subst::Substs {
         regions: regions,
         self_ty: self_ty,
         tps: params
     };
 }
 
-fn parse_region_substs(st: &mut PState, conv: conv_did) -> ty::RegionSubsts {
+fn parse_region_substs(st: &mut PState, conv: conv_did) -> subst::RegionSubsts {
     match next(st) {
-        'e' => ty::ErasedRegions,
+        'e' => subst::ErasedRegions,
         'n' => {
             let mut regions = vec!();
             while peek(st) != '.' {
@@ -189,7 +189,7 @@ fn parse_region_substs(st: &mut PState, conv: conv_did) -> ty::RegionSubsts {
                 regions.push(r);
             }
             assert_eq!(next(st), '.');
-            ty::NonerasedRegions(OwnedSlice::from_vec(regions))
+            subst::NonerasedRegions(regions)
         }
         _ => fail!("parse_bound_region: bad input")
     }
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 5d2d6ed5815..f48dbecc530 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -17,6 +17,7 @@ use std::cell::RefCell;
 use std::collections::HashMap;
 use std::io::MemWriter;
 
+use middle::subst;
 use middle::ty::param_ty;
 use middle::ty;
 
@@ -96,7 +97,7 @@ fn enc_opt<T>(w: &mut MemWriter, t: Option<T>, enc_f: |&mut MemWriter, T|) {
     }
 }
 
-pub fn enc_substs(w: &mut MemWriter, cx: &ctxt, substs: &ty::substs) {
+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, "[");
@@ -104,12 +105,12 @@ pub fn enc_substs(w: &mut MemWriter, cx: &ctxt, substs: &ty::substs) {
     mywrite!(w, "]");
 }
 
-fn enc_region_substs(w: &mut MemWriter, cx: &ctxt, substs: &ty::RegionSubsts) {
+fn enc_region_substs(w: &mut MemWriter, cx: &ctxt, substs: &subst::RegionSubsts) {
     match *substs {
-        ty::ErasedRegions => {
+        subst::ErasedRegions => {
             mywrite!(w, "e");
         }
-        ty::NonerasedRegions(ref regions) => {
+        subst::NonerasedRegions(ref regions) => {
             mywrite!(w, "n");
             for &r in regions.iter() {
                 enc_region(w, cx, r);
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index aec25071249..9706ca578f7 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -23,6 +23,7 @@ use metadata::tydecode;
 use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter,
                          RegionParameter};
 use metadata::tyencode;
+use middle::subst;
 use middle::typeck::{MethodCall, MethodCallee, MethodOrigin};
 use middle::{ty, typeck};
 use util::ppaux::ty_to_str;
@@ -796,7 +797,7 @@ trait ebml_writer_helpers {
     fn emit_tpbt(&mut self,
                  ecx: &e::EncodeContext,
                  tpbt: ty::ty_param_bounds_and_ty);
-    fn emit_substs(&mut self, ecx: &e::EncodeContext, substs: &ty::substs);
+    fn emit_substs(&mut self, ecx: &e::EncodeContext, substs: &subst::Substs);
     fn emit_auto_adjustment(&mut self, ecx: &e::EncodeContext, adj: &ty::AutoAdjustment);
 }
 
@@ -842,7 +843,7 @@ impl<'a> ebml_writer_helpers for Encoder<'a> {
         });
     }
 
-    fn emit_substs(&mut self, ecx: &e::EncodeContext, substs: &ty::substs) {
+    fn emit_substs(&mut self, ecx: &e::EncodeContext, substs: &subst::Substs) {
         self.emit_opaque(|this| Ok(tyencode::enc_substs(this.writer,
                                                            &ecx.ty_str_ctxt(),
                                                            substs)));
@@ -1076,7 +1077,7 @@ trait ebml_decoder_decoder_helpers {
                            -> ty::TypeParameterDef;
     fn read_ty_param_bounds_and_ty(&mut self, xcx: &ExtendedDecodeContext)
                                 -> ty::ty_param_bounds_and_ty;
-    fn read_substs(&mut self, xcx: &ExtendedDecodeContext) -> ty::substs;
+    fn read_substs(&mut self, xcx: &ExtendedDecodeContext) -> subst::Substs;
     fn read_auto_adjustment(&mut self, xcx: &ExtendedDecodeContext) -> ty::AutoAdjustment;
     fn convert_def_id(&mut self,
                       xcx: &ExtendedDecodeContext,
@@ -1093,7 +1094,7 @@ trait ebml_decoder_decoder_helpers {
                       cdata: &cstore::crate_metadata) -> Vec<ty::t>;
     fn read_substs_noxcx(&mut self, tcx: &ty::ctxt,
                          cdata: &cstore::crate_metadata)
-                         -> ty::substs;
+                         -> subst::Substs;
 }
 
 impl<'a> ebml_decoder_decoder_helpers for reader::Decoder<'a> {
@@ -1121,7 +1122,7 @@ impl<'a> ebml_decoder_decoder_helpers for reader::Decoder<'a> {
     fn read_substs_noxcx(&mut self,
                          tcx: &ty::ctxt,
                          cdata: &cstore::crate_metadata)
-                         -> ty::substs
+                         -> subst::Substs
     {
         self.read_opaque(|_, doc| {
             Ok(tydecode::parse_substs_data(
@@ -1210,7 +1211,7 @@ impl<'a> ebml_decoder_decoder_helpers for reader::Decoder<'a> {
         }).unwrap()
     }
 
-    fn read_substs(&mut self, xcx: &ExtendedDecodeContext) -> ty::substs {
+    fn read_substs(&mut self, xcx: &ExtendedDecodeContext) -> subst::Substs {
         self.read_opaque(|this, doc| {
             Ok(tydecode::parse_substs_data(doc.data,
                                         xcx.dcx.cdata.cnum,
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index b6614d15106..fe57e54c778 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -11,6 +11,7 @@
 
 use middle::freevars::freevar_entry;
 use middle::freevars;
+use middle::subst;
 use middle::ty;
 use middle::typeck;
 use util::ppaux::{Repr, ty_to_str};
@@ -19,7 +20,6 @@ use util::ppaux::UserString;
 use syntax::ast::*;
 use syntax::attr;
 use syntax::codemap::Span;
-use syntax::owned_slice::OwnedSlice;
 use syntax::print::pprust::{expr_to_str,path_to_str};
 use syntax::{visit,ast_util};
 use syntax::visit::Visitor;
@@ -87,8 +87,8 @@ fn check_struct_safe_for_destructor(cx: &mut Context,
                                     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, ty::substs {
-            regions: ty::NonerasedRegions(OwnedSlice::empty()),
+        let struct_ty = ty::mk_struct(cx.tcx, struct_did, subst::Substs {
+            regions: subst::NonerasedRegions(Vec::new()),
             self_ty: None,
             tps: Vec::new()
         });
diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs
index e4bd8243e43..a34fb0a99e1 100644
--- a/src/librustc/middle/subst.rs
+++ b/src/librustc/middle/subst.rs
@@ -15,8 +15,70 @@ use middle::ty_fold;
 use middle::ty_fold::{TypeFoldable, TypeFolder};
 use util::ppaux::Repr;
 
+use std::vec::Vec;
 use syntax::codemap::Span;
 
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ * 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, Eq, TotalEq, 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, Eq, TotalEq, Hash)]
+pub struct Substs {
+    pub self_ty: Option<ty::t>,
+    pub tps: Vec<ty::t>,
+    pub regions: RegionSubsts,
+}
+
+impl Substs {
+    pub fn empty() -> Substs {
+        Substs {
+            self_ty: None,
+            tps: Vec::new(),
+            regions: NonerasedRegions(Vec::new())
+        }
+    }
+
+    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()
+        };
+
+        self.tps.len() == 0u &&
+            regions_is_noop &&
+            self.self_ty.is_none()
+    }
+
+    pub fn self_ty(&self) -> ty::t {
+        self.self_ty.unwrap()
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Public trait `Subst`
 //
@@ -25,12 +87,12 @@ use syntax::codemap::Span;
 // there is more information available (for better errors).
 
 pub trait Subst {
-    fn subst(&self, tcx: &ty::ctxt, substs: &ty::substs) -> Self {
+    fn subst(&self, tcx: &ty::ctxt, substs: &Substs) -> Self {
         self.subst_spanned(tcx, substs, None)
     }
 
     fn subst_spanned(&self, tcx: &ty::ctxt,
-                     substs: &ty::substs,
+                     substs: &Substs,
                      span: Option<Span>)
                      -> Self;
 }
@@ -38,7 +100,7 @@ pub trait Subst {
 impl<T:TypeFoldable> Subst for T {
     fn subst_spanned(&self,
                      tcx: &ty::ctxt,
-                     substs: &ty::substs,
+                     substs: &Substs,
                      span: Option<Span>)
                      -> T
     {
@@ -56,7 +118,7 @@ impl<T:TypeFoldable> Subst for T {
 
 struct SubstFolder<'a> {
     tcx: &'a ty::ctxt,
-    substs: &'a ty::substs,
+    substs: &'a Substs,
 
     // The location for which the substitution is performed, if available.
     span: Option<Span>,
@@ -81,8 +143,8 @@ impl<'a> TypeFolder for SubstFolder<'a> {
         match r {
             ty::ReEarlyBound(_, i, _) => {
                 match self.substs.regions {
-                    ty::ErasedRegions => ty::ReStatic,
-                    ty::NonerasedRegions(ref regions) => *regions.get(i),
+                    ErasedRegions => ty::ReStatic,
+                    NonerasedRegions(ref regions) => *regions.get(i),
                 }
             }
             _ => r
diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs
index 45baf07c07c..5f51f80299f 100644
--- a/src/librustc/middle/trans/adt.rs
+++ b/src/librustc/middle/trans/adt.rs
@@ -51,6 +51,8 @@ use std::num::{Bitwise};
 use std::rc::Rc;
 
 use lib::llvm::{ValueRef, True, IntEQ, IntNE};
+use middle::subst;
+use middle::subst::Subst;
 use middle::trans::_match;
 use middle::trans::build::*;
 use middle::trans::common::*;
@@ -304,10 +306,10 @@ impl Case {
     }
 }
 
-fn get_cases(tcx: &ty::ctxt, def_id: ast::DefId, substs: &ty::substs) -> Vec<Case> {
+fn get_cases(tcx: &ty::ctxt, def_id: ast::DefId, substs: &subst::Substs) -> Vec<Case> {
     ty::enum_variants(tcx, def_id).iter().map(|vi| {
         let arg_tys = vi.args.iter().map(|&raw_ty| {
-            ty::subst(tcx, substs, raw_ty)
+            raw_ty.subst(tcx, substs)
         }).collect();
         Case { discr: vi.disr_val, tys: arg_tys }
     }).collect()
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 6096060f975..23156882c7c 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -40,6 +40,8 @@ use middle::lint;
 use middle::astencode;
 use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
 use middle::weak_lang_items;
+use middle::subst;
+use middle::subst::Subst;
 use middle::trans::_match;
 use middle::trans::adt;
 use middle::trans::build::*;
@@ -442,7 +444,7 @@ pub fn get_res_dtor(ccx: &CrateContext,
                     did: ast::DefId,
                     t: ty::t,
                     parent_id: ast::DefId,
-                    substs: &ty::substs)
+                    substs: &subst::Substs)
                  -> ValueRef {
     let _icx = push_ctxt("trans_res_dtor");
     let did = if did.krate != ast::LOCAL_CRATE {
@@ -463,8 +465,7 @@ pub fn get_res_dtor(ccx: &CrateContext,
     } else {
         let tcx = ccx.tcx();
         let name = csearch::get_symbol(&ccx.sess().cstore, did);
-        let class_ty = ty::subst(tcx, substs,
-                                 ty::lookup_item_type(tcx, parent_id).ty);
+        let class_ty = ty::lookup_item_type(tcx, parent_id).ty.subst(tcx, substs);
         let llty = type_of_dtor(ccx, class_ty);
         let dtor_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID,
                                      [glue::get_drop_glue_type(ccx, t)], ty::mk_nil());
@@ -633,7 +634,7 @@ pub fn iter_structural_ty<'r,
                     repr: &adt::Repr,
                     av: ValueRef,
                     variant: &ty::VariantInfo,
-                    substs: &ty::substs,
+                    substs: &subst::Substs,
                     f: val_and_ty_fn<'r,'b>)
                     -> &'b Block<'b> {
         let _icx = push_ctxt("iter_variant");
@@ -643,7 +644,7 @@ pub fn iter_structural_ty<'r,
         for (i, &arg) in variant.args.iter().enumerate() {
             cx = f(cx,
                    adt::trans_field_ptr(cx, repr, av, variant.disr_val, i),
-                   ty::subst(tcx, substs, arg));
+                   arg.subst(tcx, substs));
         }
         return cx;
     }
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index b5cf3fb8e73..a488770689d 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -21,6 +21,8 @@ use driver::session;
 use lib::llvm::ValueRef;
 use lib::llvm::llvm;
 use metadata::csearch;
+use middle::subst;
+use middle::subst::Subst;
 use middle::trans::base;
 use middle::trans::base::*;
 use middle::trans::build::*;
@@ -39,7 +41,6 @@ use middle::trans::monomorphize;
 use middle::trans::type_of;
 use middle::trans::foreign;
 use middle::ty;
-use middle::subst::Subst;
 use middle::typeck;
 use middle::typeck::coherence::make_substs_for_receiver_types;
 use middle::typeck::MethodCall;
@@ -184,7 +185,7 @@ pub fn trans_fn_ref(bcx: &Block, def_id: ast::DefId, node: ExprOrMethodCall) ->
 fn trans_fn_ref_with_vtables_to_callee<'a>(bcx: &'a Block<'a>,
                                            def_id: ast::DefId,
                                            ref_id: ast::NodeId,
-                                           substs: ty::substs,
+                                           substs: subst::Substs,
                                            vtables: Option<typeck::vtable_res>)
                                            -> Callee<'a> {
     Callee {bcx: bcx,
@@ -195,7 +196,7 @@ 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: &ty::substs,
+                                  substs: &subst::Substs,
                                   impl_vtables: Option<typeck::vtable_res>)
                           -> (typeck::vtable_res, typeck::vtable_param_res) {
 
@@ -241,7 +242,7 @@ 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: ty::substs, // values for fn's ty params
+        substs: subst::Substs, // values for fn's ty params
         vtables: Option<typeck::vtable_res>) // vtables for the call
      -> ValueRef {
     /*!
@@ -504,7 +505,7 @@ pub fn trans_lang_call<'a>(
                                 trans_fn_ref_with_vtables_to_callee(bcx,
                                                                     did,
                                                                     0,
-                                                                    ty::substs::empty(),
+                                                                    subst::Substs::empty(),
                                                                     None)
                              },
                              ArgVals(args),
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index 5b6815dbb6b..46c5edf5f5b 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -18,13 +18,14 @@ use lib::llvm::{True, False, Bool};
 use lib::llvm::llvm;
 use lib;
 use middle::lang_items::LangItem;
+use middle::subst;
+use middle::subst::Subst;
 use middle::trans::build;
 use middle::trans::cleanup;
 use middle::trans::datum;
 use middle::trans::debuginfo;
 use middle::trans::type_::Type;
 use middle::ty;
-use middle::subst::Subst;
 use middle::typeck;
 use util::ppaux::Repr;
 use util::nodemap::NodeMap;
@@ -177,7 +178,7 @@ pub type ExternMap = HashMap<String, ValueRef>;
 // Here `self_ty` is the real type of the self parameter to this method. It
 // will only be set in the case of default methods.
 pub struct param_substs {
-    pub substs: ty::substs,
+    pub substs: subst::Substs,
     pub vtables: Option<typeck::vtable_res>,
     pub self_vtables: Option<typeck::vtable_param_res>
 }
@@ -697,7 +698,7 @@ pub fn is_null(val: ValueRef) -> bool {
 pub fn monomorphize_type(bcx: &Block, t: ty::t) -> ty::t {
     match bcx.fcx.param_substs {
         Some(ref substs) => {
-            ty::subst(bcx.tcx(), &substs.substs, t)
+            t.subst(bcx.tcx(), &substs.substs)
         }
         _ => {
             assert!(!ty::type_has_params(t));
@@ -733,7 +734,7 @@ pub enum ExprOrMethodCall {
 
 pub fn node_id_substs(bcx: &Block,
                       node: ExprOrMethodCall)
-                      -> ty::substs {
+                      -> subst::Substs {
     let tcx = bcx.tcx();
 
     let substs = match node {
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index 6e0d6d491a5..e21c8f63d09 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -131,6 +131,7 @@ use lib::llvm::llvm;
 use lib::llvm::{ModuleRef, ContextRef, ValueRef};
 use lib::llvm::debuginfo::*;
 use metadata::csearch;
+use middle::subst;
 use middle::trans::adt;
 use middle::trans::common::*;
 use middle::trans::datum::{Datum, Lvalue};
@@ -1356,7 +1357,7 @@ impl StructMemberDescriptionFactory {
 fn prepare_struct_metadata(cx: &CrateContext,
                            struct_type: ty::t,
                            def_id: ast::DefId,
-                           substs: &ty::substs,
+                           substs: &subst::Substs,
                            span: Span)
                         -> RecursiveTypeDescription {
     let struct_name = ppaux::ty_to_str(cx.tcx(), struct_type);
@@ -2251,7 +2252,7 @@ fn subroutine_type_metadata(cx: &CrateContext,
 fn trait_metadata(cx: &CrateContext,
                   def_id: ast::DefId,
                   trait_type: ty::t,
-                  substs: &ty::substs,
+                  substs: &subst::Substs,
                   trait_store: ty::TraitStore,
                   _: &ty::BuiltinBounds)
                -> DIType {
diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs
index 80ccf810e5a..7d05b352807 100644
--- a/src/librustc/middle/trans/glue.rs
+++ b/src/librustc/middle/trans/glue.rs
@@ -18,6 +18,7 @@ use back::link::*;
 use lib::llvm::{llvm, ValueRef, True};
 use lib;
 use middle::lang_items::{FreeFnLangItem, ExchangeFreeFnLangItem};
+use middle::subst;
 use middle::trans::adt;
 use middle::trans::base::*;
 use middle::trans::build::*;
@@ -229,7 +230,7 @@ fn trans_struct_drop_flag<'a>(bcx: &'a Block<'a>,
                               v0: ValueRef,
                               dtor_did: ast::DefId,
                               class_did: ast::DefId,
-                              substs: &ty::substs)
+                              substs: &subst::Substs)
                               -> &'a Block<'a> {
     let repr = adt::represent_type(bcx.ccx(), t);
     let drop_flag = adt::trans_drop_flag_ptr(bcx, &*repr, v0);
@@ -243,7 +244,7 @@ fn trans_struct_drop<'a>(bcx: &'a Block<'a>,
                          v0: ValueRef,
                          dtor_did: ast::DefId,
                          class_did: ast::DefId,
-                         substs: &ty::substs)
+                         substs: &subst::Substs)
                          -> &'a Block<'a> {
     let repr = adt::represent_type(bcx.ccx(), t);
 
diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs
index e735fd21034..fe251ad88b0 100644
--- a/src/librustc/middle/trans/meth.rs
+++ b/src/librustc/middle/trans/meth.rs
@@ -14,6 +14,7 @@ use lib::llvm::llvm;
 use lib::llvm::ValueRef;
 use lib;
 use metadata::csearch;
+use middle::subst;
 use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::callee::*;
@@ -277,9 +278,9 @@ fn trans_monomorphized_callee<'a>(bcx: &'a Block<'a>,
 fn combine_impl_and_methods_tps(bcx: &Block,
                                 mth_did: ast::DefId,
                                 node: ExprOrMethodCall,
-                                rcvr_substs: ty::substs,
+                                rcvr_substs: subst::Substs,
                                 rcvr_origins: typeck::vtable_res)
-                                -> (ty::substs, typeck::vtable_res)
+                                -> (subst::Substs, typeck::vtable_res)
 {
     /*!
      * Creates a concatenated set of substitutions which includes
@@ -335,9 +336,9 @@ fn combine_impl_and_methods_tps(bcx: &Block,
         }
     }
 
-    let ty_substs = ty::substs {
+    let ty_substs = subst::Substs {
         tps: tps,
-        regions: ty::ErasedRegions,
+        regions: subst::ErasedRegions,
         self_ty: rcvr_self_ty
     };
 
@@ -493,7 +494,7 @@ pub fn make_vtable<I: Iterator<ValueRef>>(ccx: &CrateContext,
 
 fn emit_vtable_methods(bcx: &Block,
                        impl_id: ast::DefId,
-                       substs: ty::substs,
+                       substs: subst::Substs,
                        vtables: typeck::vtable_res)
                        -> Vec<ValueRef> {
     let ccx = bcx.ccx();
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index 913f333b0f3..695f9c2ce8d 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -11,6 +11,8 @@
 use back::link::exported_name;
 use driver::session;
 use lib::llvm::ValueRef;
+use middle::subst;
+use middle::subst::Subst;
 use middle::trans::base::{set_llvm_fn_attrs, set_inline_hint};
 use middle::trans::base::{trans_enum_variant, push_ctxt, get_item_val};
 use middle::trans::base::{trans_fn, decl_internal_rust_fn};
@@ -29,7 +31,7 @@ use std::hash::{sip, Hash};
 
 pub fn monomorphic_fn(ccx: &CrateContext,
                       fn_id: ast::DefId,
-                      real_substs: &ty::substs,
+                      real_substs: &subst::Substs,
                       vtables: Option<typeck::vtable_res>,
                       self_vtables: Option<typeck::vtable_param_res>,
                       ref_id: Option<ast::NodeId>)
@@ -139,7 +141,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
 
     debug!("monomorphic_fn about to subst into {}", llitem_ty.repr(ccx.tcx()));
     let mono_ty = match is_static_provided {
-        None => ty::subst(ccx.tcx(), real_substs, llitem_ty),
+        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.
@@ -161,14 +163,14 @@ pub fn monomorphic_fn(ccx: &CrateContext,
             tps.push(real_substs.self_ty.unwrap());
             tps.push_all(real_substs.tps.tailn(idx));
 
-            let substs = ty::substs { regions: ty::ErasedRegions,
-                                      self_ty: None,
-                                      tps: tps };
+            let substs = subst::Substs { regions: subst::ErasedRegions,
+                                         self_ty: None,
+                                         tps: tps };
 
             debug!("static default: changed substitution to {}",
                    substs.repr(ccx.tcx()));
 
-            ty::subst(ccx.tcx(), &substs, llitem_ty)
+            llitem_ty.subst(ccx.tcx(), &substs)
         }
     };
 
diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs
index 0f28ce4d972..bf5bedd98e8 100644
--- a/src/librustc/middle/trans/type_of.rs
+++ b/src/librustc/middle/trans/type_of.rs
@@ -10,6 +10,7 @@
 
 #![allow(non_camel_case_types)]
 
+use middle::subst;
 use middle::trans::adt;
 use middle::trans::common::*;
 use middle::trans::foreign;
@@ -21,7 +22,6 @@ use middle::trans::type_::Type;
 
 use syntax::abi;
 use syntax::ast;
-use syntax::owned_slice::OwnedSlice;
 
 pub fn arg_is_indirect(ccx: &CrateContext, arg_ty: ty::t) -> bool {
     !type_is_immediate(ccx, arg_ty)
@@ -310,8 +310,7 @@ pub fn llvm_type_name(cx: &CrateContext,
     let tstr = ppaux::parameterized(cx.tcx(),
                                     ty::item_path_str(cx.tcx(),
                                                       did).as_slice(),
-                                    &ty::NonerasedRegions(
-                                        OwnedSlice::empty()),
+                                    &subst::ErasedRegions,
                                     tps,
                                     did,
                                     false);
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 9ecd6d48e12..c47abf5fb73 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -22,8 +22,9 @@ use middle::lang_items::{TyDescStructLangItem, TyVisitorTraitLangItem};
 use middle::freevars;
 use middle::resolve;
 use middle::resolve_lifetime;
+use middle::subst;
+use middle::subst::{Subst, Substs};
 use middle::ty;
-use middle::subst::Subst;
 use middle::typeck;
 use middle::typeck::MethodCall;
 use middle::ty_fold;
@@ -207,7 +208,7 @@ pub enum AutoAdjustment {
     AutoObject(ty::TraitStore,
                ty::BuiltinBounds,
                ast::DefId, /* Trait ID */
-               ty::substs /* Trait substitutions */)
+               subst::Substs /* Trait substitutions */)
 }
 
 #[deriving(Clone, Decodable, Encodable)]
@@ -639,40 +640,6 @@ pub enum BoundRegion {
     BrFresh(uint),
 }
 
-/**
- * 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(OwnedSlice<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<t>,
-    pub regions: RegionSubsts,
-}
-
 mod primitives {
     use super::t_box_;
 
@@ -731,7 +698,7 @@ pub enum sty {
     ty_int(ast::IntTy),
     ty_uint(ast::UintTy),
     ty_float(ast::FloatTy),
-    ty_enum(DefId, substs),
+    ty_enum(DefId, Substs),
     ty_box(t),
     ty_uniq(t),
     ty_str,
@@ -741,7 +708,7 @@ pub enum sty {
     ty_bare_fn(BareFnTy),
     ty_closure(Box<ClosureTy>),
     ty_trait(Box<TyTrait>),
-    ty_struct(DefId, substs),
+    ty_struct(DefId, Substs),
     ty_tup(Vec<t>),
 
     ty_param(param_ty), // type parameter
@@ -757,7 +724,7 @@ pub enum sty {
 #[deriving(Clone, PartialEq, Eq, Hash)]
 pub struct TyTrait {
     pub def_id: DefId,
-    pub substs: substs,
+    pub substs: Substs,
     pub store: TraitStore,
     pub bounds: BuiltinBounds
 }
@@ -765,7 +732,7 @@ pub struct TyTrait {
 #[deriving(PartialEq, Eq, Hash)]
 pub struct TraitRef {
     pub def_id: DefId,
-    pub substs: substs
+    pub substs: Substs
 }
 
 #[deriving(Clone, PartialEq)]
@@ -1032,7 +999,7 @@ pub struct ParameterEnvironment {
     /// In general, this means converting from bound parameters to
     /// free parameters. Since we currently represent bound/free type
     /// parameters in the same way, this only has an affect on regions.
-    pub free_substs: ty::substs,
+    pub free_substs: Substs,
 
     /// Bound on the Self parameter
     pub self_param_bound: Option<Rc<TraitRef>>,
@@ -1068,11 +1035,11 @@ pub struct TraitDef {
 /// item into the monotype of an item reference.
 #[deriving(Clone)]
 pub struct ItemSubsts {
-    pub substs: ty::substs,
+    pub substs: Substs,
 }
 
 pub struct ty_param_substs_and_ty {
-    pub substs: ty::substs,
+    pub substs: Substs,
     pub ty: ty::t
 }
 
@@ -1176,12 +1143,12 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
             }
         }
     }
-    fn sflags(substs: &substs) -> uint {
+    fn sflags(substs: &Substs) -> uint {
         let mut f = 0u;
         for tt in substs.tps.iter() { f |= get(*tt).flags; }
         match substs.regions {
-            ErasedRegions => {}
-            NonerasedRegions(ref regions) => {
+            subst::ErasedRegions => {}
+            subst::NonerasedRegions(ref regions) => {
                 for r in regions.iter() {
                     f |= rflags(*r)
                 }
@@ -1369,7 +1336,7 @@ pub fn mk_str_slice(cx: &ctxt, r: Region, m: ast::Mutability) -> t {
             })
 }
 
-pub fn mk_enum(cx: &ctxt, did: ast::DefId, substs: substs) -> t {
+pub fn mk_enum(cx: &ctxt, did: ast::DefId, substs: Substs) -> t {
     // take a copy of substs so that we own the vectors inside
     mk_t(cx, ty_enum(did, substs))
 }
@@ -1444,7 +1411,7 @@ pub fn mk_ctor_fn(cx: &ctxt,
 
 pub fn mk_trait(cx: &ctxt,
                 did: ast::DefId,
-                substs: substs,
+                substs: Substs,
                 store: TraitStore,
                 bounds: BuiltinBounds)
              -> t {
@@ -1458,7 +1425,7 @@ pub fn mk_trait(cx: &ctxt,
     mk_t(cx, ty_trait(inner))
 }
 
-pub fn mk_struct(cx: &ctxt, struct_id: ast::DefId, substs: substs) -> t {
+pub fn mk_struct(cx: &ctxt, struct_id: ast::DefId, substs: Substs) -> t {
     // take a copy of substs so that we own the vectors inside
     mk_t(cx, ty_struct(struct_id, substs))
 }
@@ -1524,38 +1491,14 @@ pub fn walk_regions_and_ty(cx: &ctxt, ty: t, fldr: |r: Region|, fldt: |t: t|)
 
 impl ItemSubsts {
     pub fn empty() -> ItemSubsts {
-        ItemSubsts {
-            substs: substs::empty(),
-        }
+        ItemSubsts { substs: Substs::empty() }
     }
 
     pub fn is_noop(&self) -> bool {
-        ty::substs_is_noop(&self.substs)
+        self.substs.is_noop()
     }
 }
 
-pub fn substs_is_noop(substs: &substs) -> bool {
-    let regions_is_noop = match substs.regions {
-        ErasedRegions => false, // may be used to canonicalize
-        NonerasedRegions(ref regions) => regions.is_empty()
-    };
-
-    substs.tps.len() == 0u &&
-        regions_is_noop &&
-        substs.self_ty.is_none()
-}
-
-pub fn substs_to_str(cx: &ctxt, substs: &substs) -> String {
-    substs.repr(cx)
-}
-
-pub fn subst(cx: &ctxt,
-             substs: &substs,
-             typ: t)
-          -> t {
-    typ.subst(cx, substs)
-}
-
 // Type utilities
 
 pub fn type_is_nil(ty: t) -> bool { get(ty).sty == ty_nil }
@@ -1744,7 +1687,7 @@ fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t,
           ty_enum(did, ref substs) => {
             for v in (*enum_variants(cx, did)).iter() {
                 for aty in v.args.iter() {
-                    let t = subst(cx, substs, *aty);
+                    let t = aty.subst(cx, substs);
                     needs_unwind_cleanup |=
                         type_needs_unwind_cleanup_(cx, t, tycache,
                                                    encountered_box);
@@ -2376,7 +2319,7 @@ pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool {
                 let vs = enum_variants(cx, did);
                 let r = !vs.is_empty() && vs.iter().all(|variant| {
                     variant.args.iter().any(|aty| {
-                        let sty = subst(cx, substs, *aty);
+                        let sty = aty.subst(cx, substs);
                         type_requires(cx, seen, r_ty, sty)
                     })
                 });
@@ -3688,13 +3631,13 @@ impl VariantInfo {
 
 pub fn substd_enum_variants(cx: &ctxt,
                             id: ast::DefId,
-                            substs: &substs)
+                            substs: &Substs)
                          -> Vec<Rc<VariantInfo>> {
     enum_variants(cx, id).iter().map(|variant_info| {
         let substd_args = variant_info.args.iter()
-            .map(|aty| subst(cx, substs, *aty)).collect();
+            .map(|aty| aty.subst(cx, substs)).collect();
 
-        let substd_ctor_ty = subst(cx, substs, variant_info.ctor_ty);
+        let substd_ctor_ty = variant_info.ctor_ty.subst(cx, substs);
 
         Rc::new(VariantInfo {
             args: substd_args,
@@ -3944,7 +3887,7 @@ pub fn lookup_repr_hint(tcx: &ctxt, did: DefId) -> attr::ReprAttr {
 pub fn lookup_field_type(tcx: &ctxt,
                          struct_id: DefId,
                          id: DefId,
-                         substs: &substs)
+                         substs: &Substs)
                       -> ty::t {
     let t = if id.krate == ast::LOCAL_CRATE {
         node_id_to_type(tcx, id.node)
@@ -3959,7 +3902,7 @@ pub fn lookup_field_type(tcx: &ctxt,
            }
         }
     };
-    subst(tcx, substs, t)
+    t.subst(tcx, substs)
 }
 
 // Lookup all ancestor structs of a struct indicated by did. That is the reflexive,
@@ -4027,7 +3970,7 @@ pub fn lookup_struct_field(cx: &ctxt,
 
 // Returns a list of fields corresponding to the struct's items. trans uses
 // this. Takes a list of substs with which to instantiate field types.
-pub fn struct_fields(cx: &ctxt, did: ast::DefId, substs: &substs)
+pub fn struct_fields(cx: &ctxt, did: ast::DefId, substs: &Substs)
                      -> Vec<field> {
     lookup_struct_fields(cx, did).iter().map(|f| {
        field {
@@ -4140,11 +4083,11 @@ pub fn normalize_ty(cx: &ctxt, t: t) -> t {
         }
 
         fn fold_substs(&mut self,
-                       substs: &substs)
-                       -> substs {
-            substs { regions: ErasedRegions,
-                     self_ty: substs.self_ty.fold_with(self),
-                     tps: substs.tps.fold_with(self) }
+                       substs: &subst::Substs)
+                       -> subst::Substs {
+            subst::Substs { regions: subst::ErasedRegions,
+                            self_ty: substs.self_ty.fold_with(self),
+                            tps: substs.tps.fold_with(self) }
         }
 
         fn fold_sig(&mut self,
@@ -4292,8 +4235,8 @@ pub fn visitor_object_ty(tcx: &ctxt,
         Ok(id) => id,
         Err(s) => { return Err(s); }
     };
-    let substs = substs {
-        regions: ty::NonerasedRegions(OwnedSlice::empty()),
+    let substs = Substs {
+        regions: subst::NonerasedRegions(Vec::new()),
         self_ty: None,
         tps: Vec::new()
     };
@@ -4676,10 +4619,10 @@ pub fn construct_parameter_environment(
         push_region_params(t, free_id, method_region_params)
     };
 
-    let free_substs = substs {
+    let free_substs = Substs {
         self_ty: self_ty,
         tps: type_params,
-        regions: ty::NonerasedRegions(OwnedSlice::from_vec(region_params))
+        regions: subst::NonerasedRegions(region_params)
     };
 
     //
@@ -4712,16 +4655,6 @@ pub fn construct_parameter_environment(
     }
 }
 
-impl substs {
-    pub fn empty() -> substs {
-        substs {
-            self_ty: None,
-            tps: Vec::new(),
-            regions: NonerasedRegions(OwnedSlice::empty())
-        }
-    }
-}
-
 impl BorrowKind {
     pub fn from_mutbl(m: ast::Mutability) -> BorrowKind {
         match m {
diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs
index 27a02ea47cb..e8f043b5f86 100644
--- a/src/librustc/middle/ty_fold.rs
+++ b/src/librustc/middle/ty_fold.rs
@@ -10,6 +10,7 @@
 
 // Generalized type folding mechanism.
 
+use middle::subst;
 use middle::ty;
 use middle::typeck;
 use std::rc::Rc;
@@ -50,8 +51,8 @@ pub trait TypeFolder {
     }
 
     fn fold_substs(&mut self,
-                   substs: &ty::substs)
-                   -> ty::substs {
+                   substs: &subst::Substs)
+                   -> subst::Substs {
         super_fold_substs(self, substs)
     }
 
@@ -180,8 +181,8 @@ impl TypeFoldable for ty::Region {
     }
 }
 
-impl TypeFoldable for ty::substs {
-    fn fold_with<F:TypeFolder>(&self, folder: &mut F) -> ty::substs {
+impl TypeFoldable for subst::Substs {
+    fn fold_with<F:TypeFolder>(&self, folder: &mut F) -> subst::Substs {
         folder.fold_substs(self)
     }
 }
@@ -278,20 +279,20 @@ pub fn super_fold_ty<T:TypeFolder>(this: &mut T,
 }
 
 pub fn super_fold_substs<T:TypeFolder>(this: &mut T,
-                                       substs: &ty::substs)
-                                       -> ty::substs {
+                                       substs: &subst::Substs)
+                                       -> subst::Substs {
     let regions = match substs.regions {
-        ty::ErasedRegions => {
-            ty::ErasedRegions
+        subst::ErasedRegions => {
+            subst::ErasedRegions
         }
-        ty::NonerasedRegions(ref regions) => {
-            ty::NonerasedRegions(regions.fold_with(this))
+        subst::NonerasedRegions(ref regions) => {
+            subst::NonerasedRegions(regions.fold_with(this))
         }
     };
 
-    ty::substs { regions: regions,
-                 self_ty: substs.self_ty.fold_with(this),
-                 tps: substs.tps.fold_with(this) }
+    subst::Substs { regions: regions,
+                    self_ty: substs.self_ty.fold_with(this),
+                    tps: substs.tps.fold_with(this) }
 }
 
 pub fn super_fold_sig<T:TypeFolder>(this: &mut T,
diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs
index 5323d4468c9..35cfbdeb344 100644
--- a/src/librustc/middle/typeck/astconv.rs
+++ b/src/librustc/middle/typeck/astconv.rs
@@ -51,8 +51,8 @@
 
 
 use middle::const_eval;
-use middle::subst::Subst;
-use middle::ty::{substs};
+use middle::subst;
+use middle::subst::{Subst, Substs};
 use middle::ty::{ty_param_substs_and_ty};
 use middle::ty;
 use middle::typeck::rscope;
@@ -152,7 +152,7 @@ fn ast_path_substs<AC:AstConv,RS:RegionScope>(
     rscope: &RS,
     decl_generics: &ty::Generics,
     self_ty: Option<ty::t>,
-    path: &ast::Path) -> ty::substs
+    path: &ast::Path) -> subst::Substs
 {
     /*!
      * Given a path `path` that refers to an item `I` with the
@@ -232,8 +232,8 @@ fn ast_path_substs<AC:AstConv,RS:RegionScope>(
                             .map(|&a_t| ast_ty_to_ty(this, rscope, a_t))
                             .collect();
 
-    let mut substs = substs {
-        regions: ty::NonerasedRegions(OwnedSlice::from_vec(regions)),
+    let mut substs = subst::Substs {
+        regions: subst::NonerasedRegions(regions),
         self_ty: self_ty,
         tps: tps
     };
@@ -261,7 +261,7 @@ pub fn ast_path_to_substs_and_ty<AC:AstConv,
     } = this.get_item_ty(did);
 
     let substs = ast_path_substs(this, rscope, &generics, None, path);
-    let ty = ty::subst(tcx, &substs, decl_ty);
+    let ty = decl_ty.subst(tcx, &substs);
     ty_param_substs_and_ty { substs: substs, ty: ty }
 }
 
diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs
index 62c4e92997c..cccb40be5ae 100644
--- a/src/librustc/middle/typeck/check/_match.rs
+++ b/src/librustc/middle/typeck/check/_match.rs
@@ -11,6 +11,8 @@
 #![allow(non_camel_case_types)]
 
 use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding, pat_is_const};
+use middle::subst;
+use middle::subst::Subst;
 use middle::ty;
 use middle::typeck::check::demand;
 use middle::typeck::check::{check_expr, check_expr_has_type, FnCtxt};
@@ -151,7 +153,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: &ast::Pat, path: &ast::Path,
                             if var_tpt.generics.type_param_defs().len() ==
                                 expected_substs.tps.len()
                             {
-                                ty::subst(tcx, expected_substs, *t)
+                                t.subst(tcx, expected_substs)
                             }
                             else {
                                 *t // In this case, an error was already signaled
@@ -301,7 +303,7 @@ pub fn check_struct_pat_fields(pcx: &pat_ctxt,
                                fields: &[ast::FieldPat],
                                class_fields: Vec<ty::field_ty>,
                                class_id: ast::DefId,
-                               substitutions: &ty::substs,
+                               substitutions: &subst::Substs,
                                etc: bool) {
     let tcx = pcx.fcx.ccx.tcx;
 
@@ -362,7 +364,7 @@ pub fn check_struct_pat(pcx: &pat_ctxt, pat_id: ast::NodeId, span: Span,
                         expected: ty::t, path: &ast::Path,
                         fields: &[ast::FieldPat], etc: bool,
                         struct_id: ast::DefId,
-                        substitutions: &ty::substs) {
+                        substitutions: &subst::Substs) {
     let fcx = pcx.fcx;
     let tcx = pcx.fcx.ccx.tcx;
 
@@ -400,7 +402,7 @@ pub fn check_struct_like_enum_variant_pat(pcx: &pat_ctxt,
                                           fields: &[ast::FieldPat],
                                           etc: bool,
                                           enum_id: ast::DefId,
-                                          substitutions: &ty::substs) {
+                                          substitutions: &subst::Substs) {
     let fcx = pcx.fcx;
     let tcx = pcx.fcx.ccx.tcx;
 
@@ -565,10 +567,10 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) {
                                           fields.as_slice(),
                                           etc,
                                           supplied_def_id,
-                                          &ty::substs {
+                                          &subst::Substs {
                                               self_ty: None,
                                               tps: Vec::new(),
-                                              regions: ty::ErasedRegions,
+                                              regions: subst::ErasedRegions,
                                           });
                     }
                     _ => () // 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 b32875b06ee..0d4fea56e77 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -80,6 +80,7 @@ obtained the type `Foo`, we would never match this method.
 */
 
 
+use middle::subst;
 use middle::subst::Subst;
 use middle::ty::*;
 use middle::ty;
@@ -104,7 +105,6 @@ use syntax::ast::{MutMutable, MutImmutable};
 use syntax::ast;
 use syntax::codemap::Span;
 use syntax::parse::token;
-use syntax::owned_slice::OwnedSlice;
 
 #[deriving(PartialEq)]
 pub enum CheckTraitsFlag {
@@ -233,7 +233,7 @@ fn construct_transformed_self_ty_for_object(
     tcx: &ty::ctxt,
     span: Span,
     trait_def_id: ast::DefId,
-    rcvr_substs: &ty::substs,
+    rcvr_substs: &subst::Substs,
     method_ty: &ty::Method)
     -> ty::t {
     /*!
@@ -257,7 +257,7 @@ fn construct_transformed_self_ty_for_object(
         * match below.
         */
 
-    let substs = ty::substs {regions: rcvr_substs.regions.clone(),
+    let substs = subst::Substs {regions: rcvr_substs.regions.clone(),
                                 self_ty: None,
                                 tps: rcvr_substs.tps.clone()};
     match method_ty.explicit_self {
@@ -319,7 +319,7 @@ struct LookupContext<'a> {
 #[deriving(Clone)]
 struct Candidate {
     rcvr_match_condition: RcvrMatchCondition,
-    rcvr_substs: ty::substs,
+    rcvr_substs: subst::Substs,
     method_ty: Rc<ty::Method>,
     origin: MethodOrigin,
 }
@@ -500,7 +500,7 @@ impl<'a> LookupContext<'a> {
 
     fn push_inherent_candidates_from_object(&mut self,
                                             did: DefId,
-                                            substs: &ty::substs) {
+                                            substs: &subst::Substs) {
         debug!("push_inherent_candidates_from_object(did={}, substs={})",
                self.did_to_str(did),
                substs.repr(self.tcx()));
@@ -516,7 +516,7 @@ impl<'a> LookupContext<'a> {
         //
         // `confirm_candidate()` also relies upon this substitution
         // for Self. (fix)
-        let rcvr_substs = substs {
+        let rcvr_substs = subst::Substs {
             self_ty: Some(ty::mk_err()),
             ..(*substs).clone()
         };
@@ -1047,7 +1047,7 @@ impl<'a> LookupContext<'a> {
             return Some(MethodCallee {
                 origin: relevant_candidates.get(0).origin,
                 ty: ty::mk_err(),
-                substs: substs::empty()
+                substs: subst::Substs::empty()
             });
         }
 
@@ -1140,8 +1140,10 @@ impl<'a> LookupContext<'a> {
         // Determine values for the early-bound lifetime parameters.
         // FIXME -- permit users to manually specify lifetimes
         let mut all_regions: Vec<Region> = match candidate.rcvr_substs.regions {
-            NonerasedRegions(ref v) => v.iter().map(|r| r.clone()).collect(),
-            ErasedRegions => tcx.sess.span_bug(self.span, "ErasedRegions")
+            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(
@@ -1153,9 +1155,9 @@ impl<'a> LookupContext<'a> {
 
         // Construct the full set of type parameters for the method,
         // which is equal to the class tps + the method tps.
-        let all_substs = substs {
+        let all_substs = subst::Substs {
             tps: candidate.rcvr_substs.tps.clone().append(m_substs.as_slice()),
-            regions: NonerasedRegions(OwnedSlice::from_vec(all_regions)),
+            regions: subst::NonerasedRegions(all_regions),
             self_ty: candidate.rcvr_substs.self_ty,
         };
 
@@ -1164,7 +1166,7 @@ impl<'a> LookupContext<'a> {
         // Compute the method type with type parameters substituted
         debug!("fty={} all_substs={}",
                bare_fn_ty.repr(tcx),
-               ty::substs_to_str(tcx, &all_substs));
+               all_substs.repr(tcx));
 
         let fn_sig = &bare_fn_ty.sig;
         let inputs = match candidate.origin {
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 4ec6de9e4d2..a2bf979180f 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -83,10 +83,11 @@ use middle::lang_items::{ManagedHeapLangItem};
 use middle::lint::UnreachableCode;
 use middle::pat_util::pat_id_map;
 use middle::pat_util;
-use middle::subst::Subst;
+use middle::subst;
+use middle::subst::{Subst, Substs};
 use middle::ty::{FnSig, VariantInfo};
 use middle::ty::{ty_param_bounds_and_ty, ty_param_substs_and_ty};
-use middle::ty::{substs, param_ty, Disr, ExprTyProvider};
+use middle::ty::{param_ty, Disr, ExprTyProvider};
 use middle::ty;
 use middle::ty_fold::TypeFolder;
 use middle::typeck::astconv::AstConv;
@@ -283,9 +284,11 @@ fn blank_fn_ctxt<'a>(ccx: &'a CrateCtxt<'a>,
 fn blank_inherited_fields<'a>(ccx: &'a CrateCtxt<'a>) -> Inherited<'a> {
     // It's kind of a kludge to manufacture a fake function context
     // and statement context, but we might as well do write the code only once
-    let param_env = ty::ParameterEnvironment { free_substs: substs::empty(),
-                                               self_param_bound: None,
-                                               type_param_bounds: Vec::new() };
+    let param_env = ty::ParameterEnvironment {
+        free_substs: subst::Substs::empty(),
+        self_param_bound: None,
+        type_param_bounds: Vec::new()
+    };
     Inherited::new(ccx.tcx, param_env)
 }
 
@@ -855,7 +858,7 @@ fn compare_impl_method(tcx: &ty::ctxt,
                        impl_m_span: Span,
                        impl_m_body_id: ast::NodeId,
                        trait_m: &ty::Method,
-                       trait_substs: &ty::substs) {
+                       trait_substs: &subst::Substs) {
     debug!("compare_impl_method()");
     let infcx = infer::new_infer_ctxt(tcx);
 
@@ -983,15 +986,15 @@ fn compare_impl_method(tcx: &ty::ctxt,
         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: OwnedSlice<ty::Region> =
+    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 = ty::substs {
+    let dummy_substs = subst::Substs {
         tps: dummy_impl_tps.append(dummy_method_tps.as_slice()),
-        regions: ty::NonerasedRegions(dummy_impl_regions),
+        regions: subst::NonerasedRegions(dummy_impl_regions),
         self_ty: None };
 
     // Create a bare fn type for trait/impl
@@ -1012,10 +1015,10 @@ fn compare_impl_method(tcx: &ty::ctxt,
     };
     debug!("impl_fty (post-subst): {}", ppaux::ty_to_str(tcx, impl_fty));
     let trait_fty = {
-        let substs { regions: trait_regions,
-                     tps: trait_tps,
-                     self_ty: self_ty } = trait_substs.subst(tcx, &dummy_substs);
-        let substs = substs {
+        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,
@@ -1107,7 +1110,7 @@ impl<'a> FnCtxt<'a> {
     }
 
     pub fn write_substs(&self, node_id: ast::NodeId, substs: ty::ItemSubsts) {
-        if !ty::substs_is_noop(&substs.substs) {
+        if !substs.substs.is_noop() {
             debug!("write_substs({}, {}) in fcx {}",
                    node_id,
                    substs.repr(self.tcx()),
@@ -1121,7 +1124,7 @@ impl<'a> FnCtxt<'a> {
                            node_id: ast::NodeId,
                            ty: ty::t,
                            substs: ty::ItemSubsts) {
-        let ty = ty::subst(self.tcx(), &substs.substs, ty);
+        let ty = ty.subst(self.tcx(), &substs.substs);
         self.write_ty(node_id, ty);
         self.write_substs(node_id, substs);
     }
@@ -1185,7 +1188,7 @@ impl<'a> FnCtxt<'a> {
         }
     }
 
-    pub fn method_ty_substs(&self, id: ast::NodeId) -> ty::substs {
+    pub fn method_ty_substs(&self, id: ast::NodeId) -> subst::Substs {
         match self.inh.method_map.borrow().find(&MethodCall::expr(id)) {
             Some(method) => method.substs.clone(),
             None => {
@@ -1488,12 +1491,12 @@ pub fn impl_self_ty(vcx: &VtableContext,
     let rps = vcx.infcx.region_vars_for_defs(span, rps);
     let tps = vcx.infcx.next_ty_vars(n_tps);
 
-    let substs = substs {
-        regions: ty::NonerasedRegions(rps),
+    let substs = subst::Substs {
+        regions: subst::NonerasedRegions(rps),
         self_ty: None,
         tps: tps,
     };
-    let substd_ty = ty::subst(tcx, &substs, raw_ty);
+    let substd_ty = raw_ty.subst(tcx, &substs);
 
     ty_param_substs_and_ty { substs: substs, ty: substd_ty }
 }
@@ -1504,7 +1507,7 @@ pub fn lookup_field_ty(tcx: &ty::ctxt,
                        class_id: ast::DefId,
                        items: &[ty::field_ty],
                        fieldname: ast::Name,
-                       substs: &ty::substs) -> Option<ty::t> {
+                       substs: &subst::Substs) -> Option<ty::t> {
 
     let o_field = items.iter().find(|f| f.name == fieldname);
     o_field.map(|f| ty::lookup_field_type(tcx, class_id, f.id, substs))
@@ -2437,7 +2440,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                                       span: Span,
                                       class_id: ast::DefId,
                                       node_id: ast::NodeId,
-                                      substitutions: ty::substs,
+                                      substitutions: subst::Substs,
                                       field_types: &[ty::field_ty],
                                       ast_fields: &[ast::Field],
                                       check_completeness: bool)  {
@@ -2543,13 +2546,13 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
         // 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 = substs {
-            regions: ty::NonerasedRegions(regions),
+        let substitutions = subst::Substs {
+            regions: subst::NonerasedRegions(regions),
             self_ty: None,
             tps: type_parameters
         };
 
-        let mut struct_type = ty::subst(tcx, &substitutions, raw_type);
+        let mut struct_type = raw_type.subst(tcx, &substitutions);
 
         // Look up and check the fields.
         let class_fields = ty::lookup_struct_fields(tcx, class_id);
@@ -2599,13 +2602,13 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
         // 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 = substs {
-            regions: ty::NonerasedRegions(regions),
+        let substitutions = subst::Substs {
+            regions: subst::NonerasedRegions(regions),
             self_ty: None,
             tps: type_parameters
         };
 
-        let enum_type = ty::subst(tcx, &substitutions, raw_type);
+        let enum_type = raw_type.subst(tcx, &substitutions);
 
         // Look up and check the enum variant fields.
         let variant_fields = ty::lookup_struct_fields(tcx, variant_id);
@@ -2734,10 +2737,10 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                                       }
                                   };
                               let regions =
-                                  ty::NonerasedRegions(OwnedSlice::empty());
+                                  subst::NonerasedRegions(Vec::new());
                               let sty = ty::mk_struct(tcx,
                                                       gc_struct_id,
-                                                      substs {
+                                                      subst::Substs {
                                                         self_ty: None,
                                                         tps: vec!(
                                                             fcx.expr_ty(
@@ -3888,8 +3891,10 @@ pub fn instantiate_path(fcx: &FnCtxt,
     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 {
-        OwnedSlice::from_vec(pth.segments.last().unwrap().lifetimes.iter().map(
-            |l| ast_region_to_region(fcx.tcx(), l)).collect())
+        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(
@@ -3904,7 +3909,7 @@ pub fn instantiate_path(fcx: &FnCtxt,
 
         fcx.infcx().region_vars_for_defs(span, tpt.generics.region_param_defs.as_slice())
     };
-    let regions = ty::NonerasedRegions(regions);
+    let regions = subst::NonerasedRegions(regions);
 
     // Special case: If there is a self parameter, omit it from the list of
     // type parameters.
@@ -3980,7 +3985,7 @@ pub fn instantiate_path(fcx: &FnCtxt,
             tps.push(ty)
         }
 
-        let mut substs = substs {
+        let mut substs = subst::Substs {
             regions: regions,
             self_ty: None,
             tps: tps
@@ -4020,13 +4025,13 @@ pub fn instantiate_path(fcx: &FnCtxt,
 
         assert_eq!(substs.tps.len(), ty_param_count)
 
-        let substs {tps, regions, ..} = substs;
+        let subst::Substs {tps, regions, ..} = substs;
         (tps, regions)
     };
 
-    let substs = substs { regions: regions,
-                          self_ty: None,
-                          tps: tps };
+    let substs = subst::Substs { regions: regions,
+                                 self_ty: None,
+                                 tps: tps };
 
     fcx.write_ty_substs(node_id, tpt.ty, ty::ItemSubsts {
         substs: substs,
@@ -4261,10 +4266,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, substs {
+                    Ok(did) => (1u, Vec::new(), ty::mk_struct(ccx.tcx, did, subst::Substs {
                                                  self_ty: None,
                                                  tps: Vec::new(),
-                                                 regions: ty::NonerasedRegions(OwnedSlice::empty())
+                                                 regions: subst::NonerasedRegions(Vec::new())
                                                  }) ),
                     Err(msg) => {
                         tcx.sess.span_fatal(it.span, msg.as_slice());
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index 7ad18ddfe5c..3f8d8285ae4 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -23,6 +23,7 @@ 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::MethodCall;
+use middle::subst;
 use middle::subst::Subst;
 use util::common::indenter;
 use util::ppaux;
@@ -81,7 +82,7 @@ fn has_trait_bounds(type_param_defs: &[ty::TypeParameterDef]) -> bool {
 fn lookup_vtables(vcx: &VtableContext,
                   span: Span,
                   type_param_defs: &[ty::TypeParameterDef],
-                  substs: &ty::substs,
+                  substs: &subst::Substs,
                   is_early: bool) -> vtable_res {
     debug!("lookup_vtables(span={:?}, \
             type_param_defs={}, \
@@ -118,7 +119,7 @@ fn lookup_vtables(vcx: &VtableContext,
 fn lookup_vtables_for_param(vcx: &VtableContext,
                             span: Span,
                             // None for substs means the identity
-                            substs: Option<&ty::substs>,
+                            substs: Option<&subst::Substs>,
                             type_param_bounds: &ty::ParamBounds,
                             ty: ty::t,
                             is_early: bool) -> vtable_param_res {
@@ -464,9 +465,9 @@ fn search_for_vtable(vcx: &VtableContext,
 fn fixup_substs(vcx: &VtableContext,
                 span: Span,
                 id: ast::DefId,
-                substs: ty::substs,
+                substs: subst::Substs,
                 is_early: bool)
-                -> Option<ty::substs> {
+                -> Option<subst::Substs> {
     let tcx = vcx.tcx();
     // use a dummy type just to package up the substs that need fixing up
     let t = ty::mk_trait(tcx,
@@ -503,7 +504,7 @@ fn fixup_ty(vcx: &VtableContext,
 
 fn connect_trait_tps(vcx: &VtableContext,
                      span: Span,
-                     impl_substs: &ty::substs,
+                     impl_substs: &subst::Substs,
                      trait_ref: Rc<ty::TraitRef>,
                      impl_did: ast::DefId) {
     let tcx = vcx.tcx();
@@ -566,7 +567,7 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
                       let vcx = fcx.vtable_context();
                       let target_trait_ref = Rc::new(ty::TraitRef {
                           def_id: target_def_id,
-                          substs: ty::substs {
+                          substs: subst::Substs {
                               tps: target_substs.tps.clone(),
                               regions: target_substs.regions.clone(),
                               self_ty: Some(typ)
@@ -799,7 +800,7 @@ pub fn resolve_impl(tcx: &ty::ctxt,
 /// Resolve vtables for a method call after typeck has finished.
 /// Used by trans to monomorphize artificial method callees (e.g. drop).
 pub fn trans_resolve_method(tcx: &ty::ctxt, id: ast::NodeId,
-                            substs: &ty::substs) -> Option<vtable_res> {
+                            substs: &subst::Substs) -> Option<vtable_res> {
     let generics = ty::lookup_item_type(tcx, ast_util::local_def(id)).generics;
     let type_param_defs = &*generics.type_param_defs;
     if has_trait_bounds(type_param_defs.as_slice()) {
diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs
index 133fc9b1530..514bb85349e 100644
--- a/src/librustc/middle/typeck/check/writeback.rs
+++ b/src/librustc/middle/typeck/check/writeback.rs
@@ -14,6 +14,7 @@
 
 
 use middle::pat_util;
+use middle::subst;
 use middle::ty;
 use middle::ty_fold::{TypeFolder,TypeFoldable};
 use middle::typeck::astconv::AstConv;
@@ -291,7 +292,7 @@ impl<'cx> WritebackCx<'cx> {
                 // probably for invocations on objects, and this
                 // causes encoding failures). -nmatsakis
                 new_method.substs.self_ty = None;
-                new_method.substs.regions = ty::ErasedRegions;
+                new_method.substs.regions = subst::ErasedRegions;
 
                 self.tcx().method_map.borrow_mut().insert(
                     method_call,
diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs
index e4b2d2da2e8..f9729ff7676 100644
--- a/src/librustc/middle/typeck/coherence.rs
+++ b/src/librustc/middle/typeck/coherence.rs
@@ -17,9 +17,11 @@
 
 use metadata::csearch::{each_impl, get_impl_trait, each_implementation_for_trait};
 use metadata::csearch;
+use middle::subst;
+use middle::subst::{Substs};
 use middle::ty::get;
-use middle::ty::{ImplContainer, lookup_item_type, subst};
-use middle::ty::{substs, t, ty_bool, ty_char, ty_bot, ty_box, ty_enum, ty_err};
+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};
@@ -41,7 +43,6 @@ use syntax::ast_map::NodeItem;
 use syntax::ast_map;
 use syntax::ast_util::{def_id_of_def, local_def};
 use syntax::codemap::Span;
-use syntax::owned_slice::OwnedSlice;
 use syntax::parse::token;
 use syntax::visit;
 
@@ -505,14 +506,12 @@ impl<'a> CoherenceChecker<'a> {
         let bounds_count = polytype.generics.type_param_defs().len();
         let type_parameters = self.inference_context.next_ty_vars(bounds_count);
 
-        let substitutions = substs {
-            regions: ty::NonerasedRegions(region_parameters),
+        let substitutions = subst::Substs {
+            regions: subst::NonerasedRegions(region_parameters),
             self_ty: None,
             tps: type_parameters
         };
-        let monotype = subst(self.crate_context.tcx,
-                             &substitutions,
-                             polytype.ty);
+        let monotype = polytype.ty.subst(self.crate_context.tcx, &substitutions);
 
         UniversalQuantificationResult {
             monotype: monotype,
@@ -730,7 +729,7 @@ pub fn make_substs_for_receiver_types(tcx: &ty::ctxt,
                                       impl_id: ast::DefId,
                                       trait_ref: &ty::TraitRef,
                                       method: &ty::Method)
-                                      -> ty::substs {
+                                      -> subst::Substs {
     /*!
      * Substitutes the values for the receiver's type parameters
      * that are found in method, leaving the method's type parameters
@@ -753,17 +752,17 @@ pub fn make_substs_for_receiver_types(tcx: &ty::ctxt,
     let mut combined_tps = trait_ref.substs.tps.clone();
     combined_tps.push_all_move(meth_tps);
     let combined_regions = match &trait_ref.substs.regions {
-        &ty::ErasedRegions =>
+        &subst::ErasedRegions =>
             fail!("make_substs_for_receiver_types: unexpected ErasedRegions"),
 
-        &ty::NonerasedRegions(ref rs) => {
-            let mut rs = rs.clone().into_vec();
+        &subst::NonerasedRegions(ref rs) => {
+            let mut rs = rs.clone();
             rs.push_all_move(meth_regions);
-            ty::NonerasedRegions(OwnedSlice::from_vec(rs))
+            subst::NonerasedRegions(rs)
         }
     };
 
-    ty::substs {
+    subst::Substs {
         regions: combined_regions,
         self_ty: trait_ref.substs.self_ty,
         tps: combined_tps
diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs
index c32aa2dd31c..a6478675a89 100644
--- a/src/librustc/middle/typeck/collect.rs
+++ b/src/librustc/middle/typeck/collect.rs
@@ -34,10 +34,11 @@ are represented as `ty_param()` instances.
 use metadata::csearch;
 use middle::lang_items::SizedTraitLangItem;
 use middle::resolve_lifetime;
-use middle::ty::{ImplContainer, MethodContainer, TraitContainer, substs};
+use middle::subst;
+use middle::subst::{Subst, Substs};
+use middle::ty::{ImplContainer, MethodContainer, TraitContainer};
 use middle::ty::{ty_param_bounds_and_ty};
 use middle::ty;
-use middle::subst::Subst;
 use middle::typeck::astconv::{AstConv, ty_of_arg};
 use middle::typeck::astconv::{ast_ty_to_ty};
 use middle::typeck::astconv;
@@ -320,16 +321,14 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) {
         //     A,B,C => A',B',C'
         //     Self => D'
         //     D,E,F => E',F',G'
-        let substs = substs {
-            regions: ty::NonerasedRegions(rps_from_trait),
+        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::subst(tcx,
-                           &substs,
-                           ty::mk_bare_fn(tcx, m.fty.clone()));
+        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.
@@ -1211,17 +1210,18 @@ 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::substs
+                      self_ty: Option<ty::t>)
+                      -> 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 regions: OwnedSlice<ty::Region> =
+    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();
 
-    substs {regions: ty::NonerasedRegions(regions),
-            self_ty: self_ty,
-            tps: params}
+    subst::Substs {regions: subst::NonerasedRegions(regions),
+                   self_ty: self_ty,
+                   tps: params}
 }
diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs
index 819a69cfad1..4a2cd7cbec2 100644
--- a/src/librustc/middle/typeck/infer/coercion.rs
+++ b/src/librustc/middle/typeck/infer/coercion.rs
@@ -64,7 +64,7 @@ we may want to adjust precisely when coercions occur.
 
 */
 
-
+use middle::subst;
 use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowObj, AutoDerefRef};
 use middle::ty::{mt};
 use middle::ty;
@@ -443,7 +443,7 @@ impl<'f> Coerce<'f> {
                          sty_a: &ty::sty,
                          b: ty::t,
                          trait_def_id: ast::DefId,
-                         trait_substs: &ty::substs,
+                         trait_substs: &subst::Substs,
                          trait_store: ty::TraitStore,
                          bounds: ty::BuiltinBounds) -> CoerceResult {
 
diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs
index 8ac6b19f657..2e8698e59aa 100644
--- a/src/librustc/middle/typeck/infer/combine.rs
+++ b/src/librustc/middle/typeck/infer/combine.rs
@@ -47,8 +47,10 @@
 // now.
 
 
+use middle::subst;
+use middle::subst::Substs;
 use middle::ty::{FloatVar, FnSig, IntVar, TyVar};
-use middle::ty::{IntType, UintType, substs};
+use middle::ty::{IntType, UintType};
 use middle::ty::{BuiltinBounds};
 use middle::ty;
 use middle::typeck::infer::{then, ToUres};
@@ -66,7 +68,6 @@ use std::result;
 
 use syntax::ast::{Onceness, FnStyle};
 use syntax::ast;
-use syntax::owned_slice::OwnedSlice;
 use syntax::abi;
 
 pub trait Combine {
@@ -127,22 +128,23 @@ pub trait Combine {
 
     fn substs(&self,
               item_def_id: ast::DefId,
-              as_: &ty::substs,
-              bs: &ty::substs) -> cres<ty::substs> {
-
+              as_: &subst::Substs,
+              bs: &subst::Substs)
+              -> cres<subst::Substs>
+    {
         fn relate_region_params<C:Combine>(this: &C,
                                            item_def_id: ast::DefId,
-                                           a: &ty::RegionSubsts,
-                                           b: &ty::RegionSubsts)
-                                           -> cres<ty::RegionSubsts> {
+                                           a: &subst::RegionSubsts,
+                                           b: &subst::RegionSubsts)
+                                           -> cres<subst::RegionSubsts> {
             let tcx = this.infcx().tcx;
             match (a, b) {
-                (&ty::ErasedRegions, _) | (_, &ty::ErasedRegions) => {
-                    Ok(ty::ErasedRegions)
+                (&subst::ErasedRegions, _) | (_, &subst::ErasedRegions) => {
+                    Ok(subst::ErasedRegions)
                 }
 
-                (&ty::NonerasedRegions(ref a_rs),
-                 &ty::NonerasedRegions(ref b_rs)) => {
+                (&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();
@@ -175,7 +177,7 @@ pub trait Combine {
                         };
                         rs.push(if_ok!(r));
                     }
-                    Ok(ty::NonerasedRegions(OwnedSlice::from_vec(rs)))
+                    Ok(subst::NonerasedRegions(rs))
                 }
             }
         }
@@ -186,9 +188,9 @@ pub trait Combine {
                                                   item_def_id,
                                                   &as_.regions,
                                                   &bs.regions));
-        Ok(substs { regions: regions,
-                    self_ty: self_ty,
-                    tps: tps.clone() })
+        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/mod.rs b/src/librustc/middle/typeck/infer/mod.rs
index 9462094e1a6..646dad879ee 100644
--- a/src/librustc/middle/typeck/infer/mod.rs
+++ b/src/librustc/middle/typeck/infer/mod.rs
@@ -41,7 +41,6 @@ use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap;
 use syntax::codemap::Span;
-use syntax::owned_slice::OwnedSlice;
 use util::common::indent;
 use util::ppaux::{bound_region_to_str, ty_to_str, trait_ref_to_str, Repr};
 
@@ -625,7 +624,7 @@ impl<'a> InferCtxt<'a> {
     pub fn region_vars_for_defs(&self,
                                 span: Span,
                                 defs: &[ty::RegionParameterDef])
-                                -> OwnedSlice<ty::Region> {
+                                -> Vec<ty::Region> {
         defs.iter()
             .map(|d| self.next_region_var(EarlyBoundRegion(span, d.name)))
             .collect()
diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs
index b3cabbfb7e2..d1c6618a9c0 100644
--- a/src/librustc/middle/typeck/mod.rs
+++ b/src/librustc/middle/typeck/mod.rs
@@ -64,6 +64,7 @@ independently:
 use driver::config;
 
 use middle::resolve;
+use middle::subst;
 use middle::ty;
 use util::common::time;
 use util::ppaux::Repr;
@@ -144,7 +145,7 @@ pub struct MethodObject {
 pub struct MethodCallee {
     pub origin: MethodOrigin,
     pub ty: ty::t,
-    pub substs: ty::substs
+    pub substs: subst::Substs
 }
 
 #[deriving(Clone, PartialEq, Eq, Hash, Show)]
@@ -184,7 +185,7 @@ pub enum vtable_origin {
       from whence comes the vtable, and tys are the type substs.
       vtable_res is the vtable itself
      */
-    vtable_static(ast::DefId, ty::substs, vtable_res),
+    vtable_static(ast::DefId, subst::Substs, vtable_res),
 
     /*
       Dynamic vtable, comes from a parameter that has a bound on it:
diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs
index 8ee6aef3386..04244ff31a8 100644
--- a/src/librustc/middle/typeck/variance.rs
+++ b/src/librustc/middle/typeck/variance.rs
@@ -195,6 +195,7 @@ represents the "variance transform" as defined in the paper:
 use std::collections::HashMap;
 use arena;
 use arena::Arena;
+use middle::subst;
 use middle::ty;
 use std::fmt;
 use std::rc::Rc;
@@ -798,7 +799,7 @@ impl<'a> ConstraintContext<'a> {
     fn add_constraints_from_substs(&mut self,
                                    def_id: ast::DefId,
                                    generics: &ty::Generics,
-                                   substs: &ty::substs,
+                                   substs: &subst::Substs,
                                    variance: VarianceTermPtr<'a>) {
         debug!("add_constraints_from_substs(def_id={:?})", def_id);
 
@@ -810,8 +811,8 @@ impl<'a> ConstraintContext<'a> {
         }
 
         match substs.regions {
-            ty::ErasedRegions => {}
-            ty::NonerasedRegions(ref rps) => {
+            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);
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index e14fd89fc74..a9ac1e76f11 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -9,6 +9,8 @@
 // except according to those terms.
 
 
+use middle::subst;
+use middle::subst::Subst;
 use middle::ty::{ReSkolemized, ReVar};
 use middle::ty::{BoundRegion, BrAnon, BrNamed};
 use middle::ty::{BrFresh, ctxt};
@@ -419,15 +421,15 @@ pub fn ty_to_str(cx: &ctxt, typ: t) -> String {
 
 pub fn parameterized(cx: &ctxt,
                      base: &str,
-                     regions: &ty::RegionSubsts,
+                     regions: &subst::RegionSubsts,
                      tps: &[ty::t],
                      did: ast::DefId,
                      is_trait: bool)
                      -> String {
     let mut strs = Vec::new();
     match *regions {
-        ty::ErasedRegions => { }
-        ty::NonerasedRegions(ref regions) => {
+        subst::ErasedRegions => { }
+        subst::NonerasedRegions(ref regions) => {
             for &r in regions.iter() {
                 strs.push(region_to_str(cx, "", false, r))
             }
@@ -443,7 +445,7 @@ pub fn parameterized(cx: &ctxt,
     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 = ty::substs {
+        let mut substs = subst::Substs {
             tps: Vec::from_slice(tps),
             regions: regions.clone(),
             self_ty: None
@@ -451,7 +453,7 @@ pub fn parameterized(cx: &ctxt,
         ty_params.iter().zip(tps.iter()).rev().take_while(|&(def, &actual)| {
             substs.tps.pop();
             match def.default {
-                Some(default) => ty::subst(cx, &substs, default) == actual,
+                Some(default) => default.subst(cx, &substs) == actual,
                 None => false
             }
         }).len()
@@ -565,7 +567,7 @@ impl Repr for ty::t {
     }
 }
 
-impl Repr for ty::substs {
+impl Repr for subst::Substs {
     fn repr(&self, tcx: &ctxt) -> String {
         format!("substs(regions={}, self_ty={}, tps={})",
                 self.regions.repr(tcx),
@@ -580,11 +582,11 @@ impl Repr for ty::ItemSubsts {
     }
 }
 
-impl Repr for ty::RegionSubsts {
+impl Repr for subst::RegionSubsts {
     fn repr(&self, tcx: &ctxt) -> String {
         match *self {
-            ty::ErasedRegions => "erased".to_string(),
-            ty::NonerasedRegions(ref regions) => regions.repr(tcx)
+            subst::ErasedRegions => "erased".to_string(),
+            subst::NonerasedRegions(ref regions) => regions.repr(tcx)
         }
     }
 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 4614d7cee3a..70c90b67d78 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -25,6 +25,7 @@ use rustc::driver::driver;
 use rustc::metadata::cstore;
 use rustc::metadata::csearch;
 use rustc::metadata::decoder;
+use rustc::middle::subst;
 use rustc::middle::ty;
 
 use std::rc::Rc;
@@ -486,14 +487,14 @@ impl Clean<TyParamBound> for ast::TyParamBound {
     }
 }
 
-fn external_path(name: &str, substs: &ty::substs) -> Path {
+fn external_path(name: &str, substs: &subst::Substs) -> Path {
     Path {
         global: false,
         segments: vec![PathSegment {
             name: name.to_string(),
             lifetimes: match substs.regions {
-                ty::ErasedRegions => Vec::new(),
-                ty::NonerasedRegions(ref v) => {
+                subst::ErasedRegions => Vec::new(),
+                subst::NonerasedRegions(ref v) => {
                     v.iter().filter_map(|v| v.clean()).collect()
                 }
             },
@@ -509,7 +510,7 @@ impl Clean<TyParamBound> for ty::BuiltinBound {
             core::Typed(ref tcx) => tcx,
             core::NotTyped(_) => return RegionBound,
         };
-        let empty = ty::substs::empty();
+        let empty = subst::Substs::empty();
         let (did, path) = match *self {
             ty::BoundStatic => return RegionBound,
             ty::BoundSend =>
@@ -574,12 +575,12 @@ impl Clean<Vec<TyParamBound>> for ty::ParamBounds {
     }
 }
 
-impl Clean<Option<Vec<TyParamBound>>> for ty::substs {
+impl Clean<Option<Vec<TyParamBound>>> for subst::Substs {
     fn clean(&self) -> Option<Vec<TyParamBound>> {
         let mut v = Vec::new();
         match self.regions {
-            ty::NonerasedRegions(..) => v.push(RegionBound),
-            ty::ErasedRegions => {}
+            subst::NonerasedRegions(..) => v.push(RegionBound),
+            subst::ErasedRegions => {}
         }
         v.extend(self.tps.iter().map(|t| TraitBound(t.clean())));