about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcollections/btree/set.rs4
-rw-r--r--src/libcore/str.rs23
-rw-r--r--src/librustc/lint/builtin.rs4
-rw-r--r--src/librustc/metadata/decoder.rs2
-rw-r--r--src/librustc/metadata/tydecode.rs32
-rw-r--r--src/librustc/metadata/tyencode.rs20
-rw-r--r--src/librustc/middle/astencode.rs23
-rw-r--r--src/librustc/middle/check_static.rs18
-rw-r--r--src/librustc/middle/fast_reject.rs6
-rw-r--r--src/librustc/middle/infer/coercion.rs30
-rw-r--r--src/librustc/middle/infer/combine.rs118
-rw-r--r--src/librustc/middle/infer/equate.rs15
-rw-r--r--src/librustc/middle/infer/error_reporting.rs17
-rw-r--r--src/librustc/middle/infer/freshen.rs (renamed from src/librustc/middle/infer/skolemize.rs)76
-rw-r--r--src/librustc/middle/infer/glb.rs10
-rw-r--r--src/librustc/middle/infer/higher_ranked/doc.rs28
-rw-r--r--src/librustc/middle/infer/higher_ranked/mod.rs611
-rw-r--r--src/librustc/middle/infer/lub.rs10
-rw-r--r--src/librustc/middle/infer/mod.rs242
-rw-r--r--src/librustc/middle/infer/region_inference/mod.rs54
-rw-r--r--src/librustc/middle/infer/resolve.rs297
-rw-r--r--src/librustc/middle/infer/sub.rs10
-rw-r--r--src/librustc/middle/infer/type_variable.rs46
-rw-r--r--src/librustc/middle/intrinsicck.rs4
-rw-r--r--src/librustc/middle/liveness.rs1
-rw-r--r--src/librustc/middle/resolve_lifetime.rs9
-rw-r--r--src/librustc/middle/traits/coherence.rs15
-rw-r--r--src/librustc/middle/traits/doc.rs906
-rw-r--r--src/librustc/middle/traits/fulfill.rs50
-rw-r--r--src/librustc/middle/traits/mod.rs12
-rw-r--r--src/librustc/middle/traits/select.rs436
-rw-r--r--src/librustc/middle/traits/util.rs34
-rw-r--r--src/librustc/middle/ty.rs467
-rw-r--r--src/librustc/middle/ty_fold.rs136
-rw-r--r--src/librustc/util/ppaux.rs142
-rw-r--r--src/librustc/util/snapshot_vec.rs8
-rw-r--r--src/librustc_driver/test.rs127
-rw-r--r--src/librustc_trans/trans/base.rs22
-rw-r--r--src/librustc_trans/trans/callee.rs15
-rw-r--r--src/librustc_trans/trans/closure.rs6
-rw-r--r--src/librustc_trans/trans/common.rs12
-rw-r--r--src/librustc_trans/trans/context.rs8
-rw-r--r--src/librustc_trans/trans/debuginfo.rs50
-rw-r--r--src/librustc_trans/trans/expr.rs6
-rw-r--r--src/librustc_trans/trans/foreign.rs23
-rw-r--r--src/librustc_trans/trans/glue.rs4
-rw-r--r--src/librustc_trans/trans/intrinsic.rs2
-rw-r--r--src/librustc_trans/trans/meth.rs18
-rw-r--r--src/librustc_trans/trans/type_of.rs8
-rw-r--r--src/librustc_typeck/astconv.rs92
-rw-r--r--src/librustc_typeck/check/_match.rs46
-rw-r--r--src/librustc_typeck/check/closure.rs14
-rw-r--r--src/librustc_typeck/check/demand.rs9
-rw-r--r--src/librustc_typeck/check/method/confirm.rs72
-rw-r--r--src/librustc_typeck/check/method/mod.rs10
-rw-r--r--src/librustc_typeck/check/method/probe.rs117
-rw-r--r--src/librustc_typeck/check/mod.rs194
-rw-r--r--src/librustc_typeck/check/regionck.rs12
-rw-r--r--src/librustc_typeck/check/vtable.rs136
-rw-r--r--src/librustc_typeck/check/wf.rs9
-rw-r--r--src/librustc_typeck/check/writeback.rs29
-rw-r--r--src/librustc_typeck/coherence/mod.rs100
-rw-r--r--src/librustc_typeck/coherence/orphan.rs2
-rw-r--r--src/librustc_typeck/collect.rs145
-rw-r--r--src/librustc_typeck/lib.rs8
-rw-r--r--src/librustc_typeck/variance.rs14
-rw-r--r--src/librustdoc/clean/mod.rs30
-rw-r--r--src/libsyntax/visit.rs19
-rw-r--r--src/test/compile-fail/bad-match.rs3
-rw-r--r--src/test/compile-fail/hrtb-conflate-regions.rs40
-rw-r--r--src/test/compile-fail/hrtb-just-for-static.rs37
-rw-r--r--src/test/compile-fail/hrtb-perfect-forwarding.rs66
-rw-r--r--src/test/compile-fail/hrtb-type-outlives.rs59
-rw-r--r--src/test/compile-fail/issue-14366.rs1
-rw-r--r--src/test/compile-fail/issue-15965.rs5
-rw-r--r--src/test/compile-fail/issue-18345.rs4
-rw-r--r--src/test/compile-fail/issue-18532.rs1
-rw-r--r--src/test/compile-fail/kindck-inherited-copy-bound.rs7
-rw-r--r--src/test/compile-fail/unboxed-closures-unsafe-extern-fn.rs9
-rw-r--r--src/test/compile-fail/unboxed-closures-wrong-abi.rs10
-rw-r--r--src/test/compile-fail/unboxed-closures-wrong-arg-type-extern-fn.rs9
-rw-r--r--src/test/run-pass/hrtb-opt-in-copy.rs38
-rw-r--r--src/test/run-pass/issue-10501.rs14
-rw-r--r--src/test/run-pass/issue-12741.rs35
84 files changed, 3197 insertions, 2416 deletions
diff --git a/src/libcollections/btree/set.rs b/src/libcollections/btree/set.rs
index 8f75113c01d..4ef2e681992 100644
--- a/src/libcollections/btree/set.rs
+++ b/src/libcollections/btree/set.rs
@@ -762,8 +762,8 @@ mod test {
         expected: &'b [int],
     }
 
-    impl<'a, 'b> FnMut(&int) -> bool for Counter<'a, 'b> {
-        extern "rust-call" fn call_mut(&mut self, (&x,): (&int,)) -> bool {
+    impl<'a, 'b, 'c> FnMut(&'c int) -> bool for Counter<'a, 'b> {
+        extern "rust-call" fn call_mut(&mut self, (&x,): (&'c int,)) -> bool {
             assert_eq!(x, self.expected[*self.i]);
             *self.i += 1;
             true
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index 28110cf7b1a..8fe41c0bd89 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -21,6 +21,7 @@ pub use self::Searcher::{Naive, TwoWay, TwoWayLong};
 
 use char::Char;
 use char;
+use clone::Clone;
 use cmp::{Eq, mod};
 use default::Default;
 use iter::{Map, Iterator, IteratorExt, DoubleEndedIterator};
@@ -31,7 +32,7 @@ use mem;
 use num::Int;
 use option::Option;
 use option::Option::{None, Some};
-use ops::FnMut;
+use ops::{Fn, FnMut};
 use ptr::RawPtr;
 use raw::{Repr, Slice};
 use slice::{mod, SliceExt};
@@ -316,7 +317,23 @@ impl<'a> DoubleEndedIterator<(uint, char)> for CharOffsets<'a> {
 
 /// External iterator for a string's bytes.
 /// Use with the `std::iter` module.
-pub type Bytes<'a> = Map<&'a u8, u8, slice::Items<'a, u8>, fn(&u8) -> u8>;
+pub type Bytes<'a> = Map<&'a u8, u8, slice::Items<'a, u8>, BytesFn>;
+
+/// A temporary new type wrapper that ensures that the `Bytes` iterator
+/// is cloneable.
+#[deriving(Copy)]
+#[experimental = "iterator type instability"]
+pub struct BytesFn(fn(&u8) -> u8);
+
+impl<'a> Fn(&'a u8) -> u8 for BytesFn {
+    extern "rust-call" fn call(&self, (ptr,): (&'a u8,)) -> u8 {
+        (self.0)(ptr)
+    }
+}
+
+impl Clone for BytesFn {
+    fn clone(&self) -> BytesFn { *self }
+}
 
 /// An iterator over the substrings of a string, separated by `sep`.
 #[deriving(Clone)]
@@ -2009,7 +2026,7 @@ impl StrPrelude for str {
     fn bytes(&self) -> Bytes {
         fn deref(&x: &u8) -> u8 { x }
 
-        self.as_bytes().iter().map(deref)
+        self.as_bytes().iter().map(BytesFn(deref))
     }
 
     #[inline]
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 8c25bc702b3..f6fd9f60e5c 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -1784,9 +1784,7 @@ impl LintPass for Stability {
                                 method_num: index,
                                 ..
                             }) => {
-                                ty::trait_item(cx.tcx,
-                                               trait_ref.def_id,
-                                               index).def_id()
+                                ty::trait_item(cx.tcx, trait_ref.def_id, index).def_id()
                             }
                         }
                     }
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index b78112f1f78..7f1df80da3c 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -704,7 +704,7 @@ pub fn get_enum_variants<'tcx>(intr: Rc<IdentInterner>, cdata: Cmd, id: ast::Nod
         let name = item_name(&*intr, item);
         let (ctor_ty, arg_tys, arg_names) = match ctor_ty.sty {
             ty::ty_bare_fn(ref f) =>
-                (Some(ctor_ty), f.sig.inputs.clone(), None),
+                (Some(ctor_ty), f.sig.0.inputs.clone(), None),
             _ => { // Nullary or struct enum variant.
                 let mut arg_names = Vec::new();
                 let arg_tys = get_struct_fields(intr.clone(), cdata, did.node)
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index d649c649131..f2a41c48d12 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -21,7 +21,7 @@ pub use self::DefIdSource::*;
 use middle::region;
 use middle::subst;
 use middle::subst::VecPerParamSpace;
-use middle::ty::{mod, Ty};
+use middle::ty::{mod, AsPredicate, Ty};
 
 use std::rc::Rc;
 use std::str;
@@ -414,7 +414,7 @@ fn parse_ty<'a, 'tcx>(st: &mut PState<'a, 'tcx>, conv: conv_did) -> Ty<'tcx> {
       }
       'x' => {
         assert_eq!(next(st), '[');
-        let trait_ref = parse_trait_ref(st, |x,y| conv(x,y));
+        let trait_ref = ty::Binder(parse_trait_ref(st, |x,y| conv(x,y)));
         let bounds = parse_existential_bounds(st, |x,y| conv(x,y));
         assert_eq!(next(st), ']');
         return ty::mk_trait(st.tcx, trait_ref, bounds);
@@ -603,7 +603,7 @@ fn parse_bare_fn_ty<'a, 'tcx>(st: &mut PState<'a, 'tcx>,
     }
 }
 
-fn parse_sig<'a, 'tcx>(st: &mut PState<'a, 'tcx>, conv: conv_did) -> ty::FnSig<'tcx> {
+fn parse_sig<'a, 'tcx>(st: &mut PState<'a, 'tcx>, conv: conv_did) -> ty::PolyFnSig<'tcx> {
     assert_eq!(next(st), '[');
     let mut inputs = Vec::new();
     while peek(st) != ']' {
@@ -622,9 +622,9 @@ fn parse_sig<'a, 'tcx>(st: &mut PState<'a, 'tcx>, conv: conv_did) -> ty::FnSig<'
         }
         _ => ty::FnConverging(parse_ty(st, |x,y| conv(x,y)))
     };
-    ty::FnSig {inputs: inputs,
-               output: output,
-               variadic: variadic}
+    ty::Binder(ty::FnSig {inputs: inputs,
+                        output: output,
+                        variadic: variadic})
 }
 
 // Rust metadata parsing
@@ -669,13 +669,13 @@ pub fn parse_predicate<'a,'tcx>(st: &mut PState<'a, 'tcx>,
                                 -> ty::Predicate<'tcx>
 {
     match next(st) {
-        't' => ty::Predicate::Trait(Rc::new(parse_trait_ref(st, conv))),
-        'e' => ty::Predicate::Equate(parse_ty(st, |x,y| conv(x,y)),
-                                     parse_ty(st, |x,y| conv(x,y))),
-        'r' => ty::Predicate::RegionOutlives(parse_region(st, |x,y| conv(x,y)),
-                                             parse_region(st, |x,y| conv(x,y))),
-        'o' => ty::Predicate::TypeOutlives(parse_ty(st, |x,y| conv(x,y)),
-                                           parse_region(st, |x,y| conv(x,y))),
+        't' => Rc::new(ty::Binder(parse_trait_ref(st, conv))).as_predicate(),
+        'e' => ty::Binder(ty::EquatePredicate(parse_ty(st, |x,y| conv(x,y)),
+                                              parse_ty(st, |x,y| conv(x,y)))).as_predicate(),
+        'r' => ty::Binder(ty::OutlivesPredicate(parse_region(st, |x,y| conv(x,y)),
+                                                parse_region(st, |x,y| conv(x,y)))).as_predicate(),
+        'o' => ty::Binder(ty::OutlivesPredicate(parse_ty(st, |x,y| conv(x,y)),
+                                                parse_region(st, |x,y| conv(x,y)))).as_predicate(),
         c => panic!("Encountered invalid character in metadata: {}", c)
     }
 }
@@ -759,10 +759,12 @@ fn parse_bounds<'a, 'tcx>(st: &mut PState<'a, 'tcx>, conv: conv_did)
     loop {
         match next(st) {
             'R' => {
-                param_bounds.region_bounds.push(parse_region(st, |x, y| conv (x, y)));
+                param_bounds.region_bounds.push(
+                    parse_region(st, |x, y| conv (x, y)));
             }
             'I' => {
-                param_bounds.trait_bounds.push(Rc::new(parse_trait_ref(st, |x,y| conv(x,y))));
+                param_bounds.trait_bounds.push(
+                    Rc::new(ty::Binder(parse_trait_ref(st, |x,y| conv(x,y)))));
             }
             '.' => {
                 return param_bounds;
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 2a057da7db3..5d7d85d4679 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -251,7 +251,7 @@ fn enc_sty<'a, 'tcx>(w: &mut SeekableMemWriter, cx: &ctxt<'a, 'tcx>,
         ty::ty_trait(box ty::TyTrait { ref principal,
                                        ref bounds }) => {
             mywrite!(w, "x[");
-            enc_trait_ref(w, cx, principal);
+            enc_trait_ref(w, cx, &principal.0);
             enc_existential_bounds(w, cx, bounds);
             mywrite!(w, "]");
         }
@@ -351,18 +351,18 @@ pub fn enc_closure_ty<'a, 'tcx>(w: &mut SeekableMemWriter, cx: &ctxt<'a, 'tcx>,
 }
 
 fn enc_fn_sig<'a, 'tcx>(w: &mut SeekableMemWriter, cx: &ctxt<'a, 'tcx>,
-                        fsig: &ty::FnSig<'tcx>) {
+                        fsig: &ty::PolyFnSig<'tcx>) {
     mywrite!(w, "[");
-    for ty in fsig.inputs.iter() {
+    for ty in fsig.0.inputs.iter() {
         enc_ty(w, cx, *ty);
     }
     mywrite!(w, "]");
-    if fsig.variadic {
+    if fsig.0.variadic {
         mywrite!(w, "V");
     } else {
         mywrite!(w, "N");
     }
-    match fsig.output {
+    match fsig.0.output {
         ty::FnConverging(result_type) => {
             enc_ty(w, cx, result_type);
         }
@@ -401,7 +401,7 @@ pub fn enc_bounds<'a, 'tcx>(w: &mut SeekableMemWriter, cx: &ctxt<'a, 'tcx>,
 
     for tp in bs.trait_bounds.iter() {
         mywrite!(w, "I");
-        enc_trait_ref(w, cx, &**tp);
+        enc_trait_ref(w, cx, &tp.0);
     }
 
     mywrite!(w, ".");
@@ -425,19 +425,19 @@ pub fn enc_predicate<'a, 'tcx>(w: &mut SeekableMemWriter,
     match *p {
         ty::Predicate::Trait(ref trait_ref) => {
             mywrite!(w, "t");
-            enc_trait_ref(w, cx, &**trait_ref);
+            enc_trait_ref(w, cx, &trait_ref.0);
         }
-        ty::Predicate::Equate(a, b) => {
+        ty::Predicate::Equate(ty::Binder(ty::EquatePredicate(a, b))) => {
             mywrite!(w, "e");
             enc_ty(w, cx, a);
             enc_ty(w, cx, b);
         }
-        ty::Predicate::RegionOutlives(a, b) => {
+        ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(a, b))) => {
             mywrite!(w, "r");
             enc_region(w, cx, a);
             enc_region(w, cx, b);
         }
-        ty::Predicate::TypeOutlives(a, b) => {
+        ty::Predicate::TypeOutlives(ty::Binder(ty::OutlivesPredicate(a, b))) => {
             mywrite!(w, "o");
             enc_ty(w, cx, a);
             enc_region(w, cx, b);
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index a37f9cb939d..69fbd59fd92 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -1113,7 +1113,7 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
                     this.emit_enum_variant("UnsizeVtable", 2, 4, |this| {
                         this.emit_enum_variant_arg(0, |this| {
                             try!(this.emit_struct_field("principal", 0, |this| {
-                                Ok(this.emit_trait_ref(ecx, &*principal))
+                                Ok(this.emit_trait_ref(ecx, &principal.0))
                             }));
                             this.emit_struct_field("bounds", 1, |this| {
                                 Ok(this.emit_existential_bounds(ecx, b))
@@ -1277,7 +1277,7 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
         rbml_w.tag(c::tag_table_object_cast_map, |rbml_w| {
             rbml_w.id(id);
             rbml_w.tag(c::tag_table_val, |rbml_w| {
-                rbml_w.emit_trait_ref(ecx, &**trait_ref);
+                rbml_w.emit_trait_ref(ecx, &trait_ref.0);
             })
         })
     }
@@ -1356,6 +1356,8 @@ trait rbml_decoder_decoder_helpers<'tcx> {
     fn read_tys<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) -> Vec<Ty<'tcx>>;
     fn read_trait_ref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
                               -> Rc<ty::TraitRef<'tcx>>;
+    fn read_poly_trait_ref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
+                                   -> Rc<ty::PolyTraitRef<'tcx>>;
     fn read_type_param_def<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
                                    -> ty::TypeParameterDef<'tcx>;
     fn read_predicate<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
@@ -1548,6 +1550,19 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
         }).unwrap())
     }
 
+    fn read_poly_trait_ref<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
+                                   -> Rc<ty::PolyTraitRef<'tcx>> {
+        Rc::new(ty::Binder(self.read_opaque(|this, doc| {
+            let ty = tydecode::parse_trait_ref_data(
+                doc.data,
+                dcx.cdata.cnum,
+                doc.start,
+                dcx.tcx,
+                |s, a| this.convert_def_id(dcx, s, a));
+            Ok(ty)
+        }).unwrap()))
+    }
+
     fn read_type_param_def<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
                                    -> ty::TypeParameterDef<'tcx> {
         self.read_opaque(|this, doc| {
@@ -1753,7 +1768,7 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
                     2 => {
                         let ty_trait = try!(this.read_enum_variant_arg(0, |this| {
                             let principal = try!(this.read_struct_field("principal", 0, |this| {
-                                Ok(this.read_trait_ref(dcx))
+                                Ok(this.read_poly_trait_ref(dcx))
                             }));
                             Ok(ty::TyTrait {
                                 principal: (*principal).clone(),
@@ -1926,7 +1941,7 @@ fn decode_side_tables(dcx: &DecodeContext,
                         dcx.tcx.method_map.borrow_mut().insert(method_call, method);
                     }
                     c::tag_table_object_cast_map => {
-                        let trait_ref = val_dsr.read_trait_ref(dcx);
+                        let trait_ref = val_dsr.read_poly_trait_ref(dcx);
                         dcx.tcx.object_cast_map.borrow_mut()
                                                .insert(id, trait_ref);
                     }
diff --git a/src/librustc/middle/check_static.rs b/src/librustc/middle/check_static.rs
index 65412ff8eff..af2be6e088d 100644
--- a/src/librustc/middle/check_static.rs
+++ b/src/librustc/middle/check_static.rs
@@ -31,7 +31,6 @@ use middle::infer;
 use middle::traits;
 use middle::mem_categorization as mc;
 use middle::expr_use_visitor as euv;
-use util::common::ErrorReported;
 use util::nodemap::NodeSet;
 
 use syntax::ast;
@@ -122,17 +121,12 @@ impl<'a, 'tcx> CheckStaticVisitor<'a, 'tcx> {
         let ty = ty::node_id_to_type(self.tcx, e.id);
         let infcx = infer::new_infer_ctxt(self.tcx);
         let mut fulfill_cx = traits::FulfillmentContext::new();
-        match traits::trait_ref_for_builtin_bound(self.tcx, ty::BoundSync, ty) {
-            Ok(trait_ref) => {
-                fulfill_cx.register_trait_ref(self.tcx, trait_ref,
-                                              traits::ObligationCause::dummy());
-                let env = ty::empty_parameter_environment();
-                if !fulfill_cx.select_all_or_error(&infcx, &env, self.tcx).is_ok() {
-                    self.tcx.sess.span_err(e.span, "shared static items must have a \
-                                                    type which implements Sync");
-                }
-            }
-            Err(ErrorReported) => { }
+        fulfill_cx.register_builtin_bound(self.tcx, ty, ty::BoundSync,
+                                          traits::ObligationCause::dummy());
+        let env = ty::empty_parameter_environment();
+        if !fulfill_cx.select_all_or_error(&infcx, &env, self.tcx).is_ok() {
+            self.tcx.sess.span_err(e.span, "shared static items must have a \
+                                            type which implements Sync");
         }
     }
 }
diff --git a/src/librustc/middle/fast_reject.rs b/src/librustc/middle/fast_reject.rs
index 6780177933f..5c0d4b4841e 100644
--- a/src/librustc/middle/fast_reject.rs
+++ b/src/librustc/middle/fast_reject.rs
@@ -60,7 +60,7 @@ pub fn simplify_type(tcx: &ty::ctxt,
         ty::ty_vec(..) => Some(VecSimplifiedType),
         ty::ty_ptr(_) => Some(PtrSimplifiedType),
         ty::ty_trait(ref trait_info) => {
-            Some(TraitSimplifiedType(trait_info.principal.def_id))
+            Some(TraitSimplifiedType(trait_info.principal.def_id()))
         }
         ty::ty_struct(def_id, _) => {
             Some(StructSimplifiedType(def_id))
@@ -83,10 +83,10 @@ pub fn simplify_type(tcx: &ty::ctxt,
             Some(TupleSimplifiedType(tys.len()))
         }
         ty::ty_closure(ref f) => {
-            Some(FunctionSimplifiedType(f.sig.inputs.len()))
+            Some(FunctionSimplifiedType(f.sig.0.inputs.len()))
         }
         ty::ty_bare_fn(ref f) => {
-            Some(FunctionSimplifiedType(f.sig.inputs.len()))
+            Some(FunctionSimplifiedType(f.sig.0.inputs.len()))
         }
         ty::ty_param(_) => {
             if can_simplify_params {
diff --git a/src/librustc/middle/infer/coercion.rs b/src/librustc/middle/infer/coercion.rs
index e268160a42b..805d4532aa1 100644
--- a/src/librustc/middle/infer/coercion.rs
+++ b/src/librustc/middle/infer/coercion.rs
@@ -60,10 +60,9 @@
 //! sort of a minor point so I've opted to leave it for later---after all
 //! we may want to adjust precisely when coercions occur.
 
-use super::{CoerceResult, resolve_type, Coercion};
+use super::{CoerceResult, Coercion};
 use super::combine::{CombineFields, Combine};
 use super::sub::Sub;
-use super::resolve::try_resolve_tvar_shallow;
 
 use middle::subst;
 use middle::ty::{AutoPtr, AutoDerefRef, AdjustDerefRef, AutoUnsize, AutoUnsafe};
@@ -197,18 +196,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
     pub fn unpack_actual_value<T, F>(&self, a: Ty<'tcx>, f: F) -> T where
         F: FnOnce(&ty::sty<'tcx>) -> T,
     {
-        match resolve_type(self.get_ref().infcx, None,
-                           a, try_resolve_tvar_shallow) {
-            Ok(t) => {
-                f(&t.sty)
-            }
-            Err(e) => {
-                self.get_ref().infcx.tcx.sess.span_bug(
-                    self.get_ref().trace.origin.span(),
-                    format!("failed to resolve even without \
-                             any force options: {}", e).as_slice());
-            }
-        }
+        f(&self.get_ref().infcx.shallow_resolve(a).sty)
     }
 
     // ~T -> &T or &mut T -> &T (including where T = [U] or str)
@@ -286,7 +274,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                             let ty = ty::mk_rptr(self.get_ref().infcx.tcx,
                                                  r_borrow,
                                                  ty::mt{ty: ty, mutbl: mt_b.mutbl});
-                            try!(self.get_ref().infcx.try(|| sub.tys(ty, b)));
+                            try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
                             debug!("Success, coerced with AutoDerefRef(1, \
                                     AutoPtr(AutoUnsize({})))", kind);
                             Ok(Some(AdjustDerefRef(AutoDerefRef {
@@ -309,7 +297,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
                             let ty = ty::mk_ptr(self.get_ref().infcx.tcx,
                                                  ty::mt{ty: ty, mutbl: mt_b.mutbl});
-                            try!(self.get_ref().infcx.try(|| sub.tys(ty, b)));
+                            try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
                             debug!("Success, coerced with AutoDerefRef(1, \
                                     AutoPtr(AutoUnsize({})))", kind);
                             Ok(Some(AdjustDerefRef(AutoDerefRef {
@@ -327,7 +315,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     match self.unsize_ty(t_a, sty_a, t_b) {
                         Some((ty, kind)) => {
                             let ty = ty::mk_uniq(self.get_ref().infcx.tcx, ty);
-                            try!(self.get_ref().infcx.try(|| sub.tys(ty, b)));
+                            try!(self.get_ref().infcx.try(|_| sub.tys(ty, b)));
                             debug!("Success, coerced with AutoDerefRef(1, \
                                     AutoUnsizeUniq({}))", kind);
                             Ok(Some(AdjustDerefRef(AutoDerefRef {
@@ -366,7 +354,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 }
                 (_, &ty::ty_trait(box ty::TyTrait { ref principal, bounds })) => {
                     // FIXME what is the purpose of `ty`?
-                    let ty = ty::mk_trait(tcx, (*principal).clone(), bounds);
+                    let ty = ty::mk_trait(tcx, principal.clone(), bounds);
                     Some((ty, ty::UnsizeVtable(ty::TyTrait { principal: (*principal).clone(),
                                                              bounds: bounds },
                                                ty_a)))
@@ -384,7 +372,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     let mut result = None;
                     let mut tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate();
                     for (i, (tp_a, tp_b)) in tps {
-                        if self.get_ref().infcx.try(|| sub.tys(*tp_a, *tp_b)).is_ok() {
+                        if self.get_ref().infcx.try(|_| sub.tys(*tp_a, *tp_b)).is_ok() {
                             continue;
                         }
                         match
@@ -397,7 +385,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                                 let mut new_substs = substs_a.clone();
                                 new_substs.types.get_mut_slice(subst::TypeSpace)[i] = new_tp;
                                 let ty = ty::mk_struct(tcx, did_a, new_substs);
-                                if self.get_ref().infcx.try(|| sub.tys(ty, ty_b)).is_err() {
+                                if self.get_ref().infcx.try(|_| sub.tys(ty, ty_b)).is_err() {
                                     debug!("Unsized type parameter '{}', but still \
                                             could not match types {} and {}",
                                            ppaux::ty_to_string(tcx, *tp_a),
@@ -476,7 +464,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 ty::ty_trait(box ty::TyTrait { ref principal, bounds }) => {
                     debug!("mutbl={} b_mutbl={}", mutbl, b_mutbl);
                     // FIXME what is purpose of this type `tr`?
-                    let tr = ty::mk_trait(tcx, (*principal).clone(), bounds);
+                    let tr = ty::mk_trait(tcx, principal.clone(), bounds);
                     try!(self.subtype(mk_ty(tr), b));
                     Ok(Some(AdjustDerefRef(AutoDerefRef {
                         autoderefs: 1,
diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs
index 26bba55594b..82ddbcee5a7 100644
--- a/src/librustc/middle/infer/combine.rs
+++ b/src/librustc/middle/infer/combine.rs
@@ -195,7 +195,7 @@ pub trait Combine<'tcx> {
                    b: &ty::BareFnTy<'tcx>) -> cres<'tcx, ty::BareFnTy<'tcx>> {
         let unsafety = try!(self.unsafeties(a.unsafety, b.unsafety));
         let abi = try!(self.abi(a.abi, b.abi));
-        let sig = try!(self.fn_sigs(&a.sig, &b.sig));
+        let sig = try!(self.binders(&a.sig, &b.sig));
         Ok(ty::BareFnTy {unsafety: unsafety,
                          abi: abi,
                          sig: sig})
@@ -222,7 +222,7 @@ pub trait Combine<'tcx> {
         let unsafety = try!(self.unsafeties(a.unsafety, b.unsafety));
         let onceness = try!(self.oncenesses(a.onceness, b.onceness));
         let bounds = try!(self.existential_bounds(a.bounds, b.bounds));
-        let sig = try!(self.fn_sigs(&a.sig, &b.sig));
+        let sig = try!(self.binders(&a.sig, &b.sig));
         let abi = try!(self.abi(a.abi, b.abi));
         Ok(ty::ClosureTy {
             unsafety: unsafety,
@@ -234,7 +234,43 @@ pub trait Combine<'tcx> {
         })
     }
 
-    fn fn_sigs(&self, a: &ty::FnSig<'tcx>, b: &ty::FnSig<'tcx>) -> cres<'tcx, ty::FnSig<'tcx>>;
+    fn fn_sigs(&self, a: &ty::FnSig<'tcx>, b: &ty::FnSig<'tcx>) -> cres<'tcx, ty::FnSig<'tcx>> {
+        if a.variadic != b.variadic {
+            return Err(ty::terr_variadic_mismatch(expected_found(self, a.variadic, b.variadic)));
+        }
+
+        let inputs = try!(argvecs(self,
+                                  a.inputs.as_slice(),
+                                  b.inputs.as_slice()));
+
+        let output = try!(match (a.output, b.output) {
+            (ty::FnConverging(a_ty), ty::FnConverging(b_ty)) =>
+                Ok(ty::FnConverging(try!(self.tys(a_ty, b_ty)))),
+            (ty::FnDiverging, ty::FnDiverging) =>
+                Ok(ty::FnDiverging),
+            (a, b) =>
+                Err(ty::terr_convergence_mismatch(
+                    expected_found(self, a != ty::FnDiverging, b != ty::FnDiverging))),
+        });
+
+        return Ok(ty::FnSig {inputs: inputs,
+                             output: output,
+                             variadic: a.variadic});
+
+
+        fn argvecs<'tcx, C: Combine<'tcx>>(combiner: &C,
+                                           a_args: &[Ty<'tcx>],
+                                           b_args: &[Ty<'tcx>])
+                                           -> cres<'tcx, Vec<Ty<'tcx>>>
+        {
+            if a_args.len() == b_args.len() {
+                a_args.iter().zip(b_args.iter())
+                    .map(|(a, b)| combiner.args(*a, *b)).collect()
+            } else {
+                Err(ty::terr_arg_count)
+            }
+        }
+    }
 
     fn args(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
         self.contratys(a, b).and_then(|t| Ok(t))
@@ -301,11 +337,47 @@ pub trait Combine<'tcx> {
     fn trait_refs(&self,
                   a: &ty::TraitRef<'tcx>,
                   b: &ty::TraitRef<'tcx>)
-                  -> cres<'tcx, ty::TraitRef<'tcx>>;
+                  -> cres<'tcx, ty::TraitRef<'tcx>>
+    {
+        // Different traits cannot be related
+        if a.def_id != b.def_id {
+            Err(ty::terr_traits(expected_found(self, a.def_id, b.def_id)))
+        } else {
+            let substs = try!(self.substs(a.def_id, &a.substs, &b.substs));
+            Ok(ty::TraitRef { def_id: a.def_id, substs: substs })
+        }
+    }
+
+    fn binders<T>(&self, a: &ty::Binder<T>, b: &ty::Binder<T>) -> cres<'tcx, ty::Binder<T>>
+        where T : Combineable<'tcx>;
     // this must be overridden to do correctly, so as to account for higher-ranked
     // behavior
 }
 
+pub trait Combineable<'tcx> : Repr<'tcx> + TypeFoldable<'tcx> {
+    fn combine<C:Combine<'tcx>>(combiner: &C, a: &Self, b: &Self) -> cres<'tcx, Self>;
+}
+
+impl<'tcx> Combineable<'tcx> for ty::TraitRef<'tcx> {
+    fn combine<C:Combine<'tcx>>(combiner: &C,
+                                a: &ty::TraitRef<'tcx>,
+                                b: &ty::TraitRef<'tcx>)
+                                -> cres<'tcx, ty::TraitRef<'tcx>>
+    {
+        combiner.trait_refs(a, b)
+    }
+}
+
+impl<'tcx> Combineable<'tcx> for ty::FnSig<'tcx> {
+    fn combine<C:Combine<'tcx>>(combiner: &C,
+                                a: &ty::FnSig<'tcx>,
+                                b: &ty::FnSig<'tcx>)
+                                -> cres<'tcx, ty::FnSig<'tcx>>
+    {
+        combiner.fn_sigs(a, b)
+    }
+}
+
 #[deriving(Clone)]
 pub struct CombineFields<'a, 'tcx: 'a> {
     pub infcx: &'a InferCtxt<'a, 'tcx>,
@@ -410,7 +482,7 @@ pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C,
       (&ty::ty_trait(ref a_),
        &ty::ty_trait(ref b_)) => {
           debug!("Trying to match traits {} and {}", a, b);
-          let principal = try!(this.trait_refs(&a_.principal, &b_.principal));
+          let principal = try!(this.binders(&a_.principal, &b_.principal));
           let bounds = try!(this.existential_bounds(a_.bounds, b_.bounds));
           Ok(ty::mk_trait(tcx, principal, bounds))
       }
@@ -706,14 +778,38 @@ impl<'cx, 'tcx> ty_fold::TypeFolder<'tcx> for Generalizer<'cx, 'tcx> {
 
     fn fold_region(&mut self, r: ty::Region) -> ty::Region {
         match r {
-            ty::ReLateBound(..) | ty::ReEarlyBound(..) => r,
-            _ if self.make_region_vars => {
-                // FIXME: This is non-ideal because we don't give a
-                // very descriptive origin for this region variable.
-                self.infcx.next_region_var(MiscVariable(self.span))
+            // Never make variables for regions bound within the type itself.
+            ty::ReLateBound(..) => { return r; }
+
+            // Early-bound regions should really have been substituted away before
+            // we get to this point.
+            ty::ReEarlyBound(..) => {
+                self.tcx().sess.span_bug(
+                    self.span,
+                    format!("Encountered early bound region when generalizing: {}",
+                            r.repr(self.tcx()))[]);
+            }
+
+            // Always make a fresh region variable for skolemized regions;
+            // the higher-ranked decision procedures rely on this.
+            ty::ReInfer(ty::ReSkolemized(..)) => { }
+
+            // For anything else, we make a region variable, unless we
+            // are *equating*, in which case it's just wasteful.
+            ty::ReEmpty |
+            ty::ReStatic |
+            ty::ReScope(..) |
+            ty::ReInfer(ty::ReVar(..)) |
+            ty::ReFree(..) => {
+                if !self.make_region_vars {
+                    return r;
+                }
             }
-            _ => r,
         }
+
+        // FIXME: This is non-ideal because we don't give a
+        // very descriptive origin for this region variable.
+        self.infcx.next_region_var(MiscVariable(self.span))
     }
 }
 
diff --git a/src/librustc/middle/infer/equate.rs b/src/librustc/middle/infer/equate.rs
index 1738b8db99b..2a4d20f4dd3 100644
--- a/src/librustc/middle/infer/equate.rs
+++ b/src/librustc/middle/infer/equate.rs
@@ -133,15 +133,10 @@ impl<'f, 'tcx> Combine<'tcx> for Equate<'f, 'tcx> {
         }
     }
 
-    fn fn_sigs(&self, a: &ty::FnSig<'tcx>, b: &ty::FnSig<'tcx>)
-               -> cres<'tcx, ty::FnSig<'tcx>> {
-        try!(self.sub().fn_sigs(a, b));
-        self.sub().fn_sigs(b, a)
-    }
-
-    fn trait_refs(&self, a: &ty::TraitRef<'tcx>, b: &ty::TraitRef<'tcx>)
-                  -> cres<'tcx, ty::TraitRef<'tcx>> {
-        try!(self.sub().trait_refs(a, b));
-        self.sub().trait_refs(b, a)
+    fn binders<T>(&self, a: &ty::Binder<T>, b: &ty::Binder<T>) -> cres<'tcx, ty::Binder<T>>
+        where T : Combineable<'tcx>
+    {
+        try!(self.sub().binders(a, b));
+        self.sub().binders(b, a)
     }
 }
diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs
index ab685dd5dbc..b4c1c0b396b 100644
--- a/src/librustc/middle/infer/error_reporting.rs
+++ b/src/librustc/middle/infer/error_reporting.rs
@@ -395,7 +395,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
     fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<String> {
         match *values {
             infer::Types(ref exp_found) => self.expected_found_str(exp_found),
-            infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found)
+            infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found),
+            infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found)
         }
     }
 
@@ -1640,7 +1641,7 @@ pub trait Resolvable<'tcx> {
 
 impl<'tcx> Resolvable<'tcx> for Ty<'tcx> {
     fn resolve<'a>(&self, infcx: &InferCtxt<'a, 'tcx>) -> Ty<'tcx> {
-        infcx.resolve_type_vars_if_possible(*self)
+        infcx.resolve_type_vars_if_possible(self)
     }
     fn contains_error(&self) -> bool {
         ty::type_is_error(*self)
@@ -1650,13 +1651,23 @@ impl<'tcx> Resolvable<'tcx> for Ty<'tcx> {
 impl<'tcx> Resolvable<'tcx> for Rc<ty::TraitRef<'tcx>> {
     fn resolve<'a>(&self, infcx: &InferCtxt<'a, 'tcx>)
                    -> Rc<ty::TraitRef<'tcx>> {
-        Rc::new(infcx.resolve_type_vars_in_trait_ref_if_possible(&**self))
+        Rc::new(infcx.resolve_type_vars_if_possible(&**self))
     }
     fn contains_error(&self) -> bool {
         ty::trait_ref_contains_error(&**self)
     }
 }
 
+impl<'tcx> Resolvable<'tcx> for Rc<ty::PolyTraitRef<'tcx>> {
+    fn resolve<'a>(&self, infcx: &InferCtxt<'a, 'tcx>)
+                   -> Rc<ty::PolyTraitRef<'tcx>> {
+        Rc::new(infcx.resolve_type_vars_if_possible(&**self))
+    }
+    fn contains_error(&self) -> bool {
+        ty::trait_ref_contains_error(&self.0)
+    }
+}
+
 fn lifetimes_in_scope(tcx: &ty::ctxt,
                       scope_id: ast::NodeId)
                       -> Vec<ast::LifetimeDef> {
diff --git a/src/librustc/middle/infer/skolemize.rs b/src/librustc/middle/infer/freshen.rs
index 8336131c54a..ebff854060c 100644
--- a/src/librustc/middle/infer/skolemize.rs
+++ b/src/librustc/middle/infer/freshen.rs
@@ -8,21 +8,21 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! Skolemization is the process of replacing unknown variables with fresh types. The idea is that
-//! the type, after skolemization, contains no inference variables but instead contains either a
+//! Freshening is the process of replacing unknown variables with fresh types. The idea is that
+//! the type, after freshening, contains no inference variables but instead contains either a
 //! value for each variable or fresh "arbitrary" types wherever a variable would have been.
 //!
-//! Skolemization is used primarily to get a good type for inserting into a cache. The result
+//! Freshening is used primarily to get a good type for inserting into a cache. The result
 //! summarizes what the type inferencer knows "so far". The primary place it is used right now is
 //! in the trait matching algorithm, which needs to be able to cache whether an `impl` self type
 //! matches some other type X -- *without* affecting `X`. That means if that if the type `X` is in
 //! fact an unbound type variable, we want the match to be regarded as ambiguous, because depending
 //! on what type that type variable is ultimately assigned, the match may or may not succeed.
 //!
-//! Note that you should be careful not to allow the output of skolemization to leak to the user in
-//! error messages or in any other form. Skolemization is only really useful as an internal detail.
+//! Note that you should be careful not to allow the output of freshening to leak to the user in
+//! error messages or in any other form. Freshening is only really useful as an internal detail.
 //!
-//! __An important detail concerning regions.__ The skolemizer also replaces *all* regions with
+//! __An important detail concerning regions.__ The freshener also replaces *all* regions with
 //! 'static. The reason behind this is that, in general, we do not take region relationships into
 //! account when making type-overloaded decisions. This is important because of the design of the
 //! region inferencer, which is not based on unification but rather on accumulating and then
@@ -39,26 +39,26 @@ use std::collections::hash_map;
 use super::InferCtxt;
 use super::unify::InferCtxtMethodsForSimplyUnifiableTypes;
 
-pub struct TypeSkolemizer<'a, 'tcx:'a> {
+pub struct TypeFreshener<'a, 'tcx:'a> {
     infcx: &'a InferCtxt<'a, 'tcx>,
-    skolemization_count: uint,
-    skolemization_map: hash_map::HashMap<ty::InferTy, Ty<'tcx>>,
+    freshen_count: uint,
+    freshen_map: hash_map::HashMap<ty::InferTy, Ty<'tcx>>,
 }
 
-impl<'a, 'tcx> TypeSkolemizer<'a, 'tcx> {
-    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> TypeSkolemizer<'a, 'tcx> {
-        TypeSkolemizer {
+impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> TypeFreshener<'a, 'tcx> {
+        TypeFreshener {
             infcx: infcx,
-            skolemization_count: 0,
-            skolemization_map: hash_map::HashMap::new(),
+            freshen_count: 0,
+            freshen_map: hash_map::HashMap::new(),
         }
     }
 
-    fn skolemize<F>(&mut self,
-                    opt_ty: Option<Ty<'tcx>>,
-                    key: ty::InferTy,
-                    skolemizer: F)
-                    -> Ty<'tcx> where
+    fn freshen<F>(&mut self,
+                  opt_ty: Option<Ty<'tcx>>,
+                  key: ty::InferTy,
+                  freshener: F)
+                  -> Ty<'tcx> where
         F: FnOnce(uint) -> ty::InferTy,
     {
         match opt_ty {
@@ -66,12 +66,12 @@ impl<'a, 'tcx> TypeSkolemizer<'a, 'tcx> {
             None => { }
         }
 
-        match self.skolemization_map.entry(key) {
+        match self.freshen_map.entry(key) {
             hash_map::Occupied(entry) => *entry.get(),
             hash_map::Vacant(entry) => {
-                let index = self.skolemization_count;
-                self.skolemization_count += 1;
-                let t = ty::mk_infer(self.infcx.tcx, skolemizer(index));
+                let index = self.freshen_count;
+                self.freshen_count += 1;
+                let t = ty::mk_infer(self.infcx.tcx, freshener(index));
                 entry.set(t);
                 t
             }
@@ -79,7 +79,7 @@ impl<'a, 'tcx> TypeSkolemizer<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> TypeFolder<'tcx> for TypeSkolemizer<'a, 'tcx> {
+impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
     fn tcx<'b>(&'b self) -> &'b ty::ctxt<'tcx> {
         self.infcx.tcx
     }
@@ -106,37 +106,37 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeSkolemizer<'a, 'tcx> {
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
         match t.sty {
             ty::ty_infer(ty::TyVar(v)) => {
-                self.skolemize(self.infcx.type_variables.borrow().probe(v),
+                self.freshen(self.infcx.type_variables.borrow().probe(v),
                                ty::TyVar(v),
-                               ty::SkolemizedTy)
+                               ty::FreshTy)
             }
 
             ty::ty_infer(ty::IntVar(v)) => {
-                self.skolemize(self.infcx.probe_var(v),
-                               ty::IntVar(v),
-                               ty::SkolemizedIntTy)
+                self.freshen(self.infcx.probe_var(v),
+                             ty::IntVar(v),
+                             ty::FreshIntTy)
             }
 
             ty::ty_infer(ty::FloatVar(v)) => {
-                self.skolemize(self.infcx.probe_var(v),
-                               ty::FloatVar(v),
-                               ty::SkolemizedIntTy)
+                self.freshen(self.infcx.probe_var(v),
+                             ty::FloatVar(v),
+                             ty::FreshIntTy)
             }
 
-            ty::ty_infer(ty::SkolemizedTy(c)) |
-            ty::ty_infer(ty::SkolemizedIntTy(c)) => {
-                if c >= self.skolemization_count {
+            ty::ty_infer(ty::FreshTy(c)) |
+            ty::ty_infer(ty::FreshIntTy(c)) => {
+                if c >= self.freshen_count {
                     self.tcx().sess.bug(
-                        format!("Encountered a skolemized type with id {} \
+                        format!("Encountered a freshend type with id {} \
                                  but our counter is only at {}",
                                 c,
-                                self.skolemization_count).as_slice());
+                                self.freshen_count).as_slice());
                 }
                 t
             }
 
             ty::ty_open(..) => {
-                self.tcx().sess.bug("Cannot skolemize an open existential type");
+                self.tcx().sess.bug("Cannot freshen an open existential type");
             }
 
             ty::ty_bool |
diff --git a/src/librustc/middle/infer/glb.rs b/src/librustc/middle/infer/glb.rs
index 9fc4e095c43..434be32fe5f 100644
--- a/src/librustc/middle/infer/glb.rs
+++ b/src/librustc/middle/infer/glb.rs
@@ -121,13 +121,9 @@ impl<'f, 'tcx> Combine<'tcx> for Glb<'f, 'tcx> {
         super_lattice_tys(self, a, b)
     }
 
-    fn fn_sigs(&self, a: &ty::FnSig<'tcx>, b: &ty::FnSig<'tcx>)
-               -> cres<'tcx, ty::FnSig<'tcx>> {
-        self.higher_ranked_glb(a, b)
-    }
-
-    fn trait_refs(&self, a: &ty::TraitRef<'tcx>, b: &ty::TraitRef<'tcx>)
-                  -> cres<'tcx, ty::TraitRef<'tcx>> {
+    fn binders<T>(&self, a: &ty::Binder<T>, b: &ty::Binder<T>) -> cres<'tcx, ty::Binder<T>>
+        where T : Combineable<'tcx>
+    {
         self.higher_ranked_glb(a, b)
     }
 }
diff --git a/src/librustc/middle/infer/higher_ranked/doc.rs b/src/librustc/middle/infer/higher_ranked/doc.rs
index 2bad3616a05..f6f254c0e8d 100644
--- a/src/librustc/middle/infer/higher_ranked/doc.rs
+++ b/src/librustc/middle/infer/higher_ranked/doc.rs
@@ -249,19 +249,21 @@
 //! in T and try to, in some cases, replace them with bound regions to
 //! yield the final result.
 //!
-//! To decide whether to replace a region `R` that appears in `T` with a
-//! bound region, the algorithms make use of two bits of information.
-//! First is a set `V` that contains all region variables created as part
-//! of the LUB/GLB computation. `V` will contain the region variables
-//! created to replace the bound regions in the input types, but it also
-//! contains 'intermediate' variables created to represent the LUB/GLB of
-//! individual regions.  Basically, when asked to compute the LUB/GLB of a
-//! region variable with another region, the inferencer cannot oblige
-//! immediately since the values of that variables are not known.
-//! Therefore, it creates a new variable that is related to the two
-//! regions.  For example, the LUB of two variables `$x` and `$y` is a
-//! fresh variable `$z` that is constrained such that `$x <= $z` and `$y
-//! <= $z`.  So `V` will contain these intermediate variables as well.
+//! To decide whether to replace a region `R` that appears in `T` with
+//! a bound region, the algorithms make use of two bits of
+//! information.  First is a set `V` that contains all region
+//! variables created as part of the LUB/GLB computation (roughly; see
+//! `region_vars_confined_to_snapshot()` for full details). `V` will
+//! contain the region variables created to replace the bound regions
+//! in the input types, but it also contains 'intermediate' variables
+//! created to represent the LUB/GLB of individual regions.
+//! Basically, when asked to compute the LUB/GLB of a region variable
+//! with another region, the inferencer cannot oblige immediately
+//! since the values of that variables are not known.  Therefore, it
+//! creates a new variable that is related to the two regions.  For
+//! example, the LUB of two variables `$x` and `$y` is a fresh
+//! variable `$z` that is constrained such that `$x <= $z` and `$y <=
+//! $z`.  So `V` will contain these intermediate variables as well.
 //!
 //! The other important factor in deciding how to replace a region in T is
 //! the function `Tainted($r)` which, for a region variable, identifies
diff --git a/src/librustc/middle/infer/higher_ranked/mod.rs b/src/librustc/middle/infer/higher_ranked/mod.rs
index be053afcca4..ab0f98ec74a 100644
--- a/src/librustc/middle/infer/higher_ranked/mod.rs
+++ b/src/librustc/middle/infer/higher_ranked/mod.rs
@@ -11,37 +11,40 @@
 //! Helper routines for higher-ranked things. See the `doc` module at
 //! the end of the file for details.
 
-use super::{combine, cres, InferCtxt, HigherRankedType};
-use super::combine::Combine;
-use super::region_inference::{RegionMark};
+use super::{CombinedSnapshot, cres, InferCtxt, HigherRankedType, SkolemizationMap};
+use super::combine::{Combine, Combineable};
 
-use middle::ty::{mod, Ty, replace_late_bound_regions};
-use middle::ty_fold::{mod, HigherRankedFoldable, TypeFoldable};
+use middle::ty::{mod, Binder};
+use middle::ty_fold::{mod, TypeFoldable};
 use syntax::codemap::Span;
-use util::nodemap::FnvHashMap;
-use util::ppaux::{bound_region_to_string, Repr};
-
-pub trait HigherRankedCombineable<'tcx>: HigherRankedFoldable<'tcx> +
-                                         TypeFoldable<'tcx> + Repr<'tcx> {
-    fn super_combine<C:Combine<'tcx>>(combiner: &C, a: &Self, b: &Self) -> cres<'tcx, Self>;
-}
+use util::nodemap::{FnvHashMap, FnvHashSet};
+use util::ppaux::Repr;
 
 pub trait HigherRankedRelations<'tcx> {
-    fn higher_ranked_sub<T>(&self, a: &T, b: &T) -> cres<'tcx, T>
-        where T : HigherRankedCombineable<'tcx>;
+    fn higher_ranked_sub<T>(&self, a: &Binder<T>, b: &Binder<T>) -> cres<'tcx, Binder<T>>
+        where T : Combineable<'tcx>;
+
+    fn higher_ranked_lub<T>(&self, a: &Binder<T>, b: &Binder<T>) -> cres<'tcx, Binder<T>>
+        where T : Combineable<'tcx>;
+
+    fn higher_ranked_glb<T>(&self, a: &Binder<T>, b: &Binder<T>) -> cres<'tcx, Binder<T>>
+        where T : Combineable<'tcx>;
+}
 
-    fn higher_ranked_lub<T>(&self, a: &T, b: &T) -> cres<'tcx, T>
-        where T : HigherRankedCombineable<'tcx>;
+trait InferCtxtExt<'tcx> {
+    fn tainted_regions(&self, snapshot: &CombinedSnapshot, r: ty::Region) -> Vec<ty::Region>;
 
-    fn higher_ranked_glb<T>(&self, a: &T, b: &T) -> cres<'tcx, T>
-        where T : HigherRankedCombineable<'tcx>;
+    fn region_vars_confined_to_snapshot(&self,
+                                        snapshot: &CombinedSnapshot)
+                                        -> Vec<ty::RegionVid>;
 }
 
 impl<'tcx,C> HigherRankedRelations<'tcx> for C
     where C : Combine<'tcx>
 {
-    fn higher_ranked_sub<T>(&self, a: &T, b: &T) -> cres<'tcx, T>
-        where T : HigherRankedCombineable<'tcx>
+    fn higher_ranked_sub<T>(&self, a: &Binder<T>, b: &Binder<T>)
+                            -> cres<'tcx, Binder<T>>
+        where T : Combineable<'tcx>
     {
         debug!("higher_ranked_sub(a={}, b={})",
                a.repr(self.tcx()), b.repr(self.tcx()));
@@ -54,114 +57,95 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C
         // please see the large comment at the end of the file in the (inlined) module
         // `doc`.
 
-        // Make a mark so we can examine "all bindings that were
+        // Start a snapshot so we can examine "all bindings that were
         // created as part of this type comparison".
-        let mark = self.infcx().region_vars.mark();
-
-        // First, we instantiate each bound region in the subtype with a fresh
-        // region variable.
-        let (a_prime, _) =
-            self.infcx().replace_late_bound_regions_with_fresh_var(
-                self.trace().origin.span(),
-                HigherRankedType,
-                a);
-
-        // Second, we instantiate each bound region in the supertype with a
-        // fresh concrete region.
-        let (b_prime, skol_map) = {
-            replace_late_bound_regions(self.tcx(), b, |br, _| {
-                let skol = self.infcx().region_vars.new_skolemized(br);
-                debug!("Bound region {} skolemized to {}",
-                       bound_region_to_string(self.tcx(), "", false, br),
-                       skol);
-                skol
-            })
-        };
-
-        debug!("a_prime={}", a_prime.repr(self.tcx()));
-        debug!("b_prime={}", b_prime.repr(self.tcx()));
-
-        // Compare types now that bound regions have been replaced.
-        let result = try!(HigherRankedCombineable::super_combine(self, &a_prime, &b_prime));
-
-        // Presuming type comparison succeeds, we need to check
-        // that the skolemized regions do not "leak".
-        let new_vars =
-            self.infcx().region_vars.vars_created_since_mark(mark);
-        for (&skol_br, &skol) in skol_map.iter() {
-            let tainted = self.infcx().region_vars.tainted(mark, skol);
-            for tainted_region in tainted.iter() {
-                // Each skolemized should only be relatable to itself
-                // or new variables:
-                match *tainted_region {
-                    ty::ReInfer(ty::ReVar(ref vid)) => {
-                        if new_vars.iter().any(|x| x == vid) { continue; }
-                    }
-                    _ => {
-                        if *tainted_region == skol { continue; }
+        return self.infcx().try(|snapshot| {
+            // First, we instantiate each bound region in the subtype with a fresh
+            // region variable.
+            let (a_prime, _) =
+                self.infcx().replace_late_bound_regions_with_fresh_var(
+                    self.trace().origin.span(),
+                    HigherRankedType,
+                    a);
+
+            // Second, we instantiate each bound region in the supertype with a
+            // fresh concrete region.
+            let (b_prime, skol_map) =
+                self.infcx().skolemize_late_bound_regions(b, snapshot);
+
+            debug!("a_prime={}", a_prime.repr(self.tcx()));
+            debug!("b_prime={}", b_prime.repr(self.tcx()));
+
+            // Compare types now that bound regions have been replaced.
+            let result = try!(Combineable::combine(self, &a_prime, &b_prime));
+
+            // Presuming type comparison succeeds, we need to check
+            // that the skolemized regions do not "leak".
+            match leak_check(self.infcx(), &skol_map, snapshot) {
+                Ok(()) => { }
+                Err((skol_br, tainted_region)) => {
+                    if self.a_is_expected() {
+                        debug!("Not as polymorphic!");
+                        return Err(ty::terr_regions_insufficiently_polymorphic(skol_br,
+                                                                               tainted_region));
+                    } else {
+                        debug!("Overly polymorphic!");
+                        return Err(ty::terr_regions_overly_polymorphic(skol_br,
+                                                                       tainted_region));
                     }
-                };
-
-                // A is not as polymorphic as B:
-                if self.a_is_expected() {
-                    debug!("Not as polymorphic!");
-                    return Err(ty::terr_regions_insufficiently_polymorphic(
-                        skol_br, *tainted_region));
-                } else {
-                    debug!("Overly polymorphic!");
-                    return Err(ty::terr_regions_overly_polymorphic(
-                        skol_br, *tainted_region));
                 }
             }
-        }
 
-        debug!("higher_ranked_sub: OK result={}",
-               result.repr(self.tcx()));
+            debug!("higher_ranked_sub: OK result={}",
+                   result.repr(self.tcx()));
 
-        return Ok(result);
+            Ok(ty::Binder(result))
+        });
     }
 
-    fn higher_ranked_lub<T>(&self, a: &T, b: &T) -> cres<'tcx, T>
-        where T : HigherRankedCombineable<'tcx>
+    fn higher_ranked_lub<T>(&self, a: &Binder<T>, b: &Binder<T>) -> cres<'tcx, Binder<T>>
+        where T : Combineable<'tcx>
     {
-        // Make a mark so we can examine "all bindings that were
+        // Start a snapshot so we can examine "all bindings that were
         // created as part of this type comparison".
-        let mark = self.infcx().region_vars.mark();
-
-        // Instantiate each bound region with a fresh region variable.
-        let span = self.trace().origin.span();
-        let (a_with_fresh, a_map) =
-            self.infcx().replace_late_bound_regions_with_fresh_var(
-                span, HigherRankedType, a);
-        let (b_with_fresh, _) =
-            self.infcx().replace_late_bound_regions_with_fresh_var(
-                span, HigherRankedType, b);
-
-        // Collect constraints.
-        let result0 =
-            try!(HigherRankedCombineable::super_combine(self, &a_with_fresh, &b_with_fresh));
-        debug!("lub result0 = {}", result0.repr(self.tcx()));
-
-        // Generalize the regions appearing in result0 if possible
-        let new_vars = self.infcx().region_vars.vars_created_since_mark(mark);
-        let span = self.trace().origin.span();
-        let result1 =
-            fold_regions_in(
-                self.tcx(),
-                &result0,
-                |r, debruijn| generalize_region(self.infcx(), span, mark, debruijn,
-                                                new_vars.as_slice(), &a_map, r));
-
-        debug!("lub({},{}) = {}",
-               a.repr(self.tcx()),
-               b.repr(self.tcx()),
-               result1.repr(self.tcx()));
-
-        return Ok(result1);
+        return self.infcx().try(|snapshot| {
+            // Instantiate each bound region with a fresh region variable.
+            let span = self.trace().origin.span();
+            let (a_with_fresh, a_map) =
+                self.infcx().replace_late_bound_regions_with_fresh_var(
+                    span, HigherRankedType, a);
+            let (b_with_fresh, _) =
+                self.infcx().replace_late_bound_regions_with_fresh_var(
+                    span, HigherRankedType, b);
+
+            // Collect constraints.
+            let result0 =
+                try!(Combineable::combine(self, &a_with_fresh, &b_with_fresh));
+            let result0 =
+                self.infcx().resolve_type_vars_if_possible(&result0);
+            debug!("lub result0 = {}", result0.repr(self.tcx()));
+
+            // Generalize the regions appearing in result0 if possible
+            let new_vars = self.infcx().region_vars_confined_to_snapshot(snapshot);
+            let span = self.trace().origin.span();
+            let result1 =
+                fold_regions_in(
+                    self.tcx(),
+                    &result0,
+                    |r, debruijn| generalize_region(self.infcx(), span, snapshot, debruijn,
+                                                    new_vars.as_slice(), &a_map, r));
+
+            debug!("lub({},{}) = {}",
+                   a.repr(self.tcx()),
+                   b.repr(self.tcx()),
+                   result1.repr(self.tcx()));
+
+            Ok(ty::Binder(result1))
+        });
 
         fn generalize_region(infcx: &InferCtxt,
                              span: Span,
-                             mark: RegionMark,
+                             snapshot: &CombinedSnapshot,
                              debruijn: ty::DebruijnIndex,
                              new_vars: &[ty::RegionVid],
                              a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
@@ -174,7 +158,7 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C
                 return r0;
             }
 
-            let tainted = infcx.region_vars.tainted(mark, r0);
+            let tainted = infcx.tainted_regions(snapshot, r0);
 
             // Variables created during LUB computation which are
             // *related* to regions that pre-date the LUB computation
@@ -209,53 +193,55 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C
         }
     }
 
-    fn higher_ranked_glb<T>(&self, a: &T, b: &T) -> cres<'tcx, T>
-        where T : HigherRankedCombineable<'tcx>
+    fn higher_ranked_glb<T>(&self, a: &Binder<T>, b: &Binder<T>) -> cres<'tcx, Binder<T>>
+        where T : Combineable<'tcx>
     {
         debug!("{}.higher_ranked_glb({}, {})",
                self.tag(), a.repr(self.tcx()), b.repr(self.tcx()));
 
-        // Make a mark so we can examine "all bindings that were
+        // Make a snapshot so we can examine "all bindings that were
         // created as part of this type comparison".
-        let mark = self.infcx().region_vars.mark();
-
-        // Instantiate each bound region with a fresh region variable.
-        let (a_with_fresh, a_map) =
-            self.infcx().replace_late_bound_regions_with_fresh_var(
-                self.trace().origin.span(), HigherRankedType, a);
-        let (b_with_fresh, b_map) =
-            self.infcx().replace_late_bound_regions_with_fresh_var(
-                self.trace().origin.span(), HigherRankedType, b);
-        let a_vars = var_ids(self, &a_map);
-        let b_vars = var_ids(self, &b_map);
-
-        // Collect constraints.
-        let result0 =
-            try!(HigherRankedCombineable::super_combine(self, &a_with_fresh, &b_with_fresh));
-        debug!("glb result0 = {}", result0.repr(self.tcx()));
-
-        // Generalize the regions appearing in fn_ty0 if possible
-        let new_vars = self.infcx().region_vars.vars_created_since_mark(mark);
-        let span = self.trace().origin.span();
-        let result1 =
-            fold_regions_in(
-                self.tcx(),
-                &result0,
-                |r, debruijn| generalize_region(self.infcx(), span, mark, debruijn,
-                                                new_vars.as_slice(),
-                                                &a_map, a_vars.as_slice(), b_vars.as_slice(),
-                                                r));
-
-        debug!("glb({},{}) = {}",
-               a.repr(self.tcx()),
-               b.repr(self.tcx()),
-               result1.repr(self.tcx()));
-
-        return Ok(result1);
+        return self.infcx().try(|snapshot| {
+            // Instantiate each bound region with a fresh region variable.
+            let (a_with_fresh, a_map) =
+                self.infcx().replace_late_bound_regions_with_fresh_var(
+                    self.trace().origin.span(), HigherRankedType, a);
+            let (b_with_fresh, b_map) =
+                self.infcx().replace_late_bound_regions_with_fresh_var(
+                    self.trace().origin.span(), HigherRankedType, b);
+            let a_vars = var_ids(self, &a_map);
+            let b_vars = var_ids(self, &b_map);
+
+            // Collect constraints.
+            let result0 =
+                try!(Combineable::combine(self, &a_with_fresh, &b_with_fresh));
+            let result0 =
+                self.infcx().resolve_type_vars_if_possible(&result0);
+            debug!("glb result0 = {}", result0.repr(self.tcx()));
+
+            // Generalize the regions appearing in result0 if possible
+            let new_vars = self.infcx().region_vars_confined_to_snapshot(snapshot);
+            let span = self.trace().origin.span();
+            let result1 =
+                fold_regions_in(
+                    self.tcx(),
+                    &result0,
+                    |r, debruijn| generalize_region(self.infcx(), span, snapshot, debruijn,
+                                                    new_vars.as_slice(),
+                                                    &a_map, a_vars.as_slice(), b_vars.as_slice(),
+                                                    r));
+
+            debug!("glb({},{}) = {}",
+                   a.repr(self.tcx()),
+                   b.repr(self.tcx()),
+                   result1.repr(self.tcx()));
+
+            Ok(ty::Binder(result1))
+        });
 
         fn generalize_region(infcx: &InferCtxt,
                              span: Span,
-                             mark: RegionMark,
+                             snapshot: &CombinedSnapshot,
                              debruijn: ty::DebruijnIndex,
                              new_vars: &[ty::RegionVid],
                              a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
@@ -267,7 +253,7 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C
                 return r0;
             }
 
-            let tainted = infcx.region_vars.tainted(mark, r0);
+            let tainted = infcx.tainted_regions(snapshot, r0);
 
             let mut a_r = None;
             let mut b_r = None;
@@ -345,67 +331,6 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C
     }
 }
 
-impl<'tcx> HigherRankedCombineable<'tcx> for ty::FnSig<'tcx> {
-    fn super_combine<C:Combine<'tcx>>(combiner: &C, a: &ty::FnSig<'tcx>, b: &ty::FnSig<'tcx>)
-                                      -> cres<'tcx, ty::FnSig<'tcx>>
-    {
-        if a.variadic != b.variadic {
-            return Err(ty::terr_variadic_mismatch(
-                combine::expected_found(combiner, a.variadic, b.variadic)));
-        }
-
-        let inputs = try!(argvecs(combiner,
-                                  a.inputs.as_slice(),
-                                  b.inputs.as_slice()));
-
-        let output = try!(match (a.output, b.output) {
-            (ty::FnConverging(a_ty), ty::FnConverging(b_ty)) =>
-                Ok(ty::FnConverging(try!(combiner.tys(a_ty, b_ty)))),
-            (ty::FnDiverging, ty::FnDiverging) =>
-                Ok(ty::FnDiverging),
-            (a, b) =>
-                Err(ty::terr_convergence_mismatch(
-                    combine::expected_found(combiner, a != ty::FnDiverging, b != ty::FnDiverging))),
-        });
-
-        return Ok(ty::FnSig {inputs: inputs,
-                             output: output,
-                             variadic: a.variadic});
-
-
-        fn argvecs<'tcx, C: Combine<'tcx>>(combiner: &C,
-                                           a_args: &[Ty<'tcx>],
-                                           b_args: &[Ty<'tcx>])
-                                           -> cres<'tcx, Vec<Ty<'tcx>>>
-        {
-            if a_args.len() == b_args.len() {
-                a_args.iter().zip(b_args.iter())
-                    .map(|(a, b)| combiner.args(*a, *b)).collect()
-            } else {
-                Err(ty::terr_arg_count)
-            }
-        }
-    }
-}
-
-impl<'tcx> HigherRankedCombineable<'tcx> for ty::TraitRef<'tcx> {
-    fn super_combine<C:Combine<'tcx>>(combiner: &C,
-                                      a: &ty::TraitRef<'tcx>,
-                                      b: &ty::TraitRef<'tcx>)
-                                      -> cres<'tcx, ty::TraitRef<'tcx>>
-    {
-        // Different traits cannot be related
-        if a.def_id != b.def_id {
-            Err(ty::terr_traits(
-                combine::expected_found(combiner, a.def_id, b.def_id)))
-        } else {
-            let substs = try!(combiner.substs(a.def_id, &a.substs, &b.substs));
-            Ok(ty::TraitRef { def_id: a.def_id,
-                              substs: substs })
-        }
-    }
-}
-
 fn var_ids<'tcx, T: Combine<'tcx>>(combiner: &T,
                                    map: &FnvHashMap<ty::BoundRegion, ty::Region>)
                                    -> Vec<ty::RegionVid> {
@@ -426,11 +351,14 @@ fn is_var_in_set(new_vars: &[ty::RegionVid], r: ty::Region) -> bool {
     }
 }
 
-fn fold_regions_in<'tcx, T, F>(tcx: &ty::ctxt<'tcx>, value: &T, mut fldr: F) -> T where
-    T: HigherRankedFoldable<'tcx>,
-    F: FnMut(ty::Region, ty::DebruijnIndex) -> ty::Region,
+fn fold_regions_in<'tcx, T, F>(tcx: &ty::ctxt<'tcx>,
+                               unbound_value: &T,
+                               mut fldr: F)
+                               -> T
+    where T : Combineable<'tcx>,
+          F : FnMut(ty::Region, ty::DebruijnIndex) -> ty::Region,
 {
-    value.fold_contents(&mut ty_fold::RegionFolder::new(tcx, |region, current_depth| {
+    unbound_value.fold_with(&mut ty_fold::RegionFolder::new(tcx, &mut |region, current_depth| {
         // we should only be encountering "escaping" late-bound regions here,
         // because the ones at the current level should have been replaced
         // with fresh variables
@@ -443,3 +371,244 @@ fn fold_regions_in<'tcx, T, F>(tcx: &ty::ctxt<'tcx>, value: &T, mut fldr: F) ->
     }))
 }
 
+impl<'a,'tcx> InferCtxtExt<'tcx> for InferCtxt<'a,'tcx> {
+    fn tainted_regions(&self, snapshot: &CombinedSnapshot, r: ty::Region) -> Vec<ty::Region> {
+        self.region_vars.tainted(&snapshot.region_vars_snapshot, r)
+    }
+
+    fn region_vars_confined_to_snapshot(&self,
+                                        snapshot: &CombinedSnapshot)
+                                        -> Vec<ty::RegionVid>
+    {
+        /*!
+         * Returns the set of region variables that do not affect any
+         * types/regions which existed before `snapshot` was
+         * started. This is used in the sub/lub/glb computations. The
+         * idea here is that when we are computing lub/glb of two
+         * regions, we sometimes create intermediate region variables.
+         * Those region variables may touch some of the skolemized or
+         * other "forbidden" regions we created to replace bound
+         * regions, but they don't really represent an "external"
+         * constraint.
+         *
+         * However, sometimes fresh variables are created for other
+         * purposes too, and those *may* represent an external
+         * constraint. In particular, when a type variable is
+         * instantiated, we create region variables for all the
+         * regions that appear within, and if that type variable
+         * pre-existed the snapshot, then those region variables
+         * represent external constraints.
+         *
+         * An example appears in the unit test
+         * `sub_free_bound_false_infer`.  In this test, we want to
+         * know whether
+         *
+         * ```rust
+         * fn(_#0t) <: for<'a> fn(&'a int)
+         * ```
+         *
+         * Note that the subtype has a type variable. Because the type
+         * variable can't be instantiated with a region that is bound
+         * in the fn signature, this comparison ought to fail. But if
+         * we're not careful, it will succeed.
+         *
+         * The reason is that when we walk through the subtyping
+         * algorith, we begin by replacing `'a` with a skolemized
+         * variable `'1`. We then have `fn(_#0t) <: fn(&'1 int)`. This
+         * can be made true by unifying `_#0t` with `&'1 int`. In the
+         * process, we create a fresh variable for the skolemized
+         * region, `'$2`, and hence we have that `_#0t == &'$2
+         * int`. However, because `'$2` was created during the sub
+         * computation, if we're not careful we will erroneously
+         * assume it is one of the transient region variables
+         * representing a lub/glb internally. Not good.
+         *
+         * To prevent this, we check for type variables which were
+         * unified during the snapshot, and say that any region
+         * variable created during the snapshot but which finds its
+         * way into a type variable is considered to "escape" the
+         * snapshot.
+         */
+
+        let mut region_vars =
+            self.region_vars.vars_created_since_snapshot(&snapshot.region_vars_snapshot);
+
+        let escaping_types =
+            self.type_variables.borrow().types_escaping_snapshot(&snapshot.type_snapshot);
+
+        let escaping_region_vars: FnvHashSet<_> =
+            escaping_types
+            .iter()
+            .flat_map(|&t| ty_fold::collect_regions(self.tcx, &t).into_iter())
+            .collect();
+
+        region_vars.retain(|&region_vid| {
+            let r = ty::ReInfer(ty::ReVar(region_vid));
+            !escaping_region_vars.contains(&r)
+        });
+
+        debug!("region_vars_confined_to_snapshot: region_vars={} escaping_types={}",
+               region_vars.repr(self.tcx),
+               escaping_types.repr(self.tcx));
+
+        region_vars
+    }
+}
+
+pub fn skolemize_late_bound_regions<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
+                                               binder: &ty::Binder<T>,
+                                               snapshot: &CombinedSnapshot)
+                                               -> (T, SkolemizationMap)
+    where T : TypeFoldable<'tcx> + Repr<'tcx>
+{
+    /*!
+     * Replace all regions bound by `binder` with skolemized regions and
+     * return a map indicating which bound-region was replaced with what
+     * skolemized region. This is the first step of checking subtyping
+     * when higher-ranked things are involved. See `doc.rs` for more details.
+     */
+
+    let (result, map) = ty::replace_late_bound_regions(infcx.tcx, binder, |br, _| {
+        infcx.region_vars.new_skolemized(br, &snapshot.region_vars_snapshot)
+    });
+
+    debug!("skolemize_bound_regions(binder={}, result={}, map={})",
+           binder.repr(infcx.tcx),
+           result.repr(infcx.tcx),
+           map.repr(infcx.tcx));
+
+    (result, map)
+}
+
+pub fn leak_check<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
+                           skol_map: &SkolemizationMap,
+                           snapshot: &CombinedSnapshot)
+                           -> Result<(),(ty::BoundRegion,ty::Region)>
+{
+    /*!
+     * Searches the region constriants created since `snapshot` was started
+     * and checks to determine whether any of the skolemized regions created
+     * in `skol_map` would "escape" -- meaning that they are related to
+     * other regions in some way. If so, the higher-ranked subtyping doesn't
+     * hold. See `doc.rs` for more details.
+     */
+
+    debug!("leak_check: skol_map={}",
+           skol_map.repr(infcx.tcx));
+
+    let new_vars = infcx.region_vars_confined_to_snapshot(snapshot);
+    for (&skol_br, &skol) in skol_map.iter() {
+        let tainted = infcx.tainted_regions(snapshot, skol);
+        for &tainted_region in tainted.iter() {
+            // Each skolemized should only be relatable to itself
+            // or new variables:
+            match tainted_region {
+                ty::ReInfer(ty::ReVar(vid)) => {
+                    if new_vars.iter().any(|&x| x == vid) { continue; }
+                }
+                _ => {
+                    if tainted_region == skol { continue; }
+                }
+            };
+
+            debug!("{} (which replaced {}) is tainted by {}",
+                   skol.repr(infcx.tcx),
+                   skol_br.repr(infcx.tcx),
+                   tainted_region.repr(infcx.tcx));
+
+            // A is not as polymorphic as B:
+            return Err((skol_br, tainted_region));
+        }
+    }
+    Ok(())
+}
+
+/// This code converts from skolemized regions back to late-bound
+/// regions. It works by replacing each region in the taint set of a
+/// skolemized region with a bound-region. The bound region will be bound
+/// by the outer-most binder in `value`; the caller must ensure that there is
+/// such a binder and it is the right place.
+///
+/// This routine is only intended to be used when the leak-check has
+/// passed; currently, it's used in the trait matching code to create
+/// a set of nested obligations frmo an impl that matches against
+/// something higher-ranked.  More details can be found in
+/// `middle::traits::doc.rs`.
+///
+/// As a brief example, consider the obligation `for<'a> Fn(&'a int)
+/// -> &'a int`, and the impl:
+///
+///     impl<A,R> Fn<A,R> for SomethingOrOther
+///         where A : Clone
+///     { ... }
+///
+/// Here we will have replaced `'a` with a skolemized region
+/// `'0`. This means that our substitution will be `{A=>&'0
+/// int, R=>&'0 int}`.
+///
+/// When we apply the substitution to the bounds, we will wind up with
+/// `&'0 int : Clone` as a predicate. As a last step, we then go and
+/// replace `'0` with a late-bound region `'a`.  The depth is matched
+/// to the depth of the predicate, in this case 1, so that the final
+/// predicate is `for<'a> &'a int : Clone`.
+pub fn plug_leaks<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
+                             skol_map: SkolemizationMap,
+                             snapshot: &CombinedSnapshot,
+                             value: &T)
+                             -> T
+    where T : TypeFoldable<'tcx> + Repr<'tcx>
+{
+    debug_assert!(leak_check(infcx, &skol_map, snapshot).is_ok());
+
+    debug!("plug_leaks(skol_map={}, value={})",
+           skol_map.repr(infcx.tcx),
+           value.repr(infcx.tcx));
+
+    // Compute a mapping from the "taint set" of each skolemized
+    // region back to the `ty::BoundRegion` that it originally
+    // represented. Because `leak_check` passed, we know that that
+    // these taint sets are mutually disjoint.
+    let inv_skol_map: FnvHashMap<ty::Region, ty::BoundRegion> =
+        skol_map
+        .into_iter()
+        .flat_map(|(skol_br, skol)| {
+            infcx.tainted_regions(snapshot, skol)
+                .into_iter()
+                .map(move |tainted_region| (tainted_region, skol_br))
+        })
+        .collect();
+
+    debug!("plug_leaks: inv_skol_map={}",
+           inv_skol_map.repr(infcx.tcx));
+
+    // Remove any instantiated type variables from `value`; those can hide
+    // references to regions from the `fold_regions` code below.
+    let value = infcx.resolve_type_vars_if_possible(value);
+
+    // Map any skolemization byproducts back to a late-bound
+    // region. Put that late-bound region at whatever the outermost
+    // binder is that we encountered in `value`. The caller is
+    // responsible for ensuring that (a) `value` contains at least one
+    // binder and (b) that binder is the one we want to use.
+    let result = ty_fold::fold_regions(infcx.tcx, &value, |r, current_depth| {
+        match inv_skol_map.get(&r) {
+            None => r,
+            Some(br) => {
+                // It is the responsibility of the caller to ensure
+                // that each skolemized region appears within a
+                // binder. In practice, this routine is only used by
+                // trait checking, and all of the skolemized regions
+                // appear inside predicates, which always have
+                // binders, so this assert is satisfied.
+                assert!(current_depth > 1);
+
+                ty::ReLateBound(ty::DebruijnIndex::new(current_depth - 1), br.clone())
+            }
+        }
+    });
+
+    debug!("plug_leaks: result={}",
+           result.repr(infcx.tcx));
+
+    result
+}
diff --git a/src/librustc/middle/infer/lub.rs b/src/librustc/middle/infer/lub.rs
index f27b07c9c9d..f4909b28891 100644
--- a/src/librustc/middle/infer/lub.rs
+++ b/src/librustc/middle/infer/lub.rs
@@ -113,17 +113,13 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> {
         Ok(self.infcx().region_vars.lub_regions(Subtype(self.trace()), a, b))
     }
 
-    fn fn_sigs(&self, a: &ty::FnSig<'tcx>, b: &ty::FnSig<'tcx>)
-               -> cres<'tcx, ty::FnSig<'tcx>> {
-        self.higher_ranked_lub(a, b)
-    }
-
     fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
         super_lattice_tys(self, a, b)
     }
 
-    fn trait_refs(&self, a: &ty::TraitRef<'tcx>, b: &ty::TraitRef<'tcx>)
-                  -> cres<'tcx, ty::TraitRef<'tcx>> {
+    fn binders<T>(&self, a: &ty::Binder<T>, b: &ty::Binder<T>) -> cres<'tcx, ty::Binder<T>>
+        where T : Combineable<'tcx>
+    {
         self.higher_ranked_lub(a, b)
     }
 }
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index 14153907ee7..f419f050cf5 100644
--- a/src/librustc/middle/infer/mod.rs
+++ b/src/librustc/middle/infer/mod.rs
@@ -19,21 +19,14 @@ pub use self::TypeOrigin::*;
 pub use self::ValuePairs::*;
 pub use self::fixup_err::*;
 pub use middle::ty::IntVarValue;
-pub use self::resolve::resolve_and_force_all_but_regions;
-pub use self::resolve::{force_all, not_regions};
-pub use self::resolve::{force_ivar};
-pub use self::resolve::{force_tvar, force_rvar};
-pub use self::resolve::{resolve_ivar, resolve_all};
-pub use self::resolve::{resolve_nested_tvar};
-pub use self::resolve::{resolve_rvar};
-pub use self::skolemize::TypeSkolemizer;
+pub use self::freshen::TypeFreshener;
 
 use middle::subst;
 use middle::subst::Substs;
 use middle::ty::{TyVid, IntVid, FloatVid, RegionVid};
 use middle::ty::replace_late_bound_regions;
 use middle::ty::{mod, Ty};
-use middle::ty_fold::{HigherRankedFoldable, TypeFolder, TypeFoldable};
+use middle::ty_fold::{TypeFolder, TypeFoldable};
 use std::cell::{RefCell};
 use std::rc::Rc;
 use syntax::ast;
@@ -42,12 +35,11 @@ use syntax::codemap::Span;
 use util::common::indent;
 use util::nodemap::FnvHashMap;
 use util::ppaux::{ty_to_string};
-use util::ppaux::{trait_ref_to_string, Repr};
+use util::ppaux::{Repr, UserString};
 
 use self::coercion::Coerce;
 use self::combine::{Combine, CombineFields};
 use self::region_inference::{RegionVarBindings, RegionSnapshot};
-use self::resolve::{resolver};
 use self::equate::Equate;
 use self::sub::Sub;
 use self::lub::Lub;
@@ -60,12 +52,12 @@ pub mod doc;
 pub mod equate;
 pub mod error_reporting;
 pub mod glb;
-pub mod higher_ranked;
+mod higher_ranked;
 pub mod lattice;
 pub mod lub;
 pub mod region_inference;
 pub mod resolve;
-mod skolemize;
+mod freshen;
 pub mod sub;
 pub mod type_variable;
 pub mod unify;
@@ -98,6 +90,10 @@ pub struct InferCtxt<'a, 'tcx: 'a> {
         RegionVarBindings<'a, 'tcx>,
 }
 
+/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
+/// region that each late-bound region was replaced with.
+pub type SkolemizationMap = FnvHashMap<ty::BoundRegion,ty::Region>;
+
 /// Why did we require that the two types be related?
 ///
 /// See `error_reporting.rs` for more details
@@ -142,6 +138,7 @@ impl Copy for TypeOrigin {}
 pub enum ValuePairs<'tcx> {
     Types(ty::expected_found<Ty<'tcx>>),
     TraitRefs(ty::expected_found<Rc<ty::TraitRef<'tcx>>>),
+    PolyTraitRefs(ty::expected_found<Rc<ty::PolyTraitRef<'tcx>>>),
 }
 
 /// The trace designates the path through inference that we took to
@@ -353,7 +350,7 @@ pub fn can_mk_subty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
                               b: Ty<'tcx>)
                               -> ures<'tcx> {
     debug!("can_mk_subty({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
-    cx.probe(|| {
+    cx.probe(|_| {
         let trace = TypeTrace {
             origin: Misc(codemap::DUMMY_SP),
             values: Types(expected_found(true, a, b))
@@ -366,7 +363,7 @@ pub fn can_mk_eqty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
                              a: Ty<'tcx>, b: Ty<'tcx>)
                              -> ures<'tcx> {
     debug!("can_mk_subty({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
-    cx.probe(|| {
+    cx.probe(|_| {
         let trace = TypeTrace {
             origin: Misc(codemap::DUMMY_SP),
             values: Types(expected_found(true, a, b))
@@ -410,17 +407,17 @@ pub fn mk_eqty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
         || cx.eq_types(a_is_expected, origin, a, b))
 }
 
-pub fn mk_sub_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
+pub fn mk_sub_poly_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
                                    a_is_expected: bool,
                                    origin: TypeOrigin,
-                                   a: Rc<ty::TraitRef<'tcx>>,
-                                   b: Rc<ty::TraitRef<'tcx>>)
+                                   a: Rc<ty::PolyTraitRef<'tcx>>,
+                                   b: Rc<ty::PolyTraitRef<'tcx>>)
                                    -> ures<'tcx>
 {
     debug!("mk_sub_trait_refs({} <: {})",
            a.repr(cx.tcx), b.repr(cx.tcx));
     cx.commit_if_ok(
-        || cx.sub_trait_refs(a_is_expected, origin, a.clone(), b.clone()))
+        || cx.sub_poly_trait_refs(a_is_expected, origin, a.clone(), b.clone()))
 }
 
 fn expected_found<T>(a_is_expected: bool,
@@ -453,22 +450,6 @@ pub fn mk_coercety<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
     })
 }
 
-// See comment on the type `resolve_state` below
-pub fn resolve_type<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
-                              span: Option<Span>,
-                              a: Ty<'tcx>,
-                              modes: uint)
-                              -> fres<Ty<'tcx>> {
-    let mut resolver = resolver(cx, modes, span);
-    cx.commit_unconditionally(|| resolver.resolve_type_chk(a))
-}
-
-pub fn resolve_region(cx: &InferCtxt, r: ty::Region, modes: uint)
-                      -> fres<ty::Region> {
-    let mut resolver = resolver(cx, modes, None);
-    resolver.resolve_region_chk(r)
-}
-
 trait then<'tcx> {
     fn then<T, F>(&self, f: F) -> Result<T, ty::type_err<'tcx>> where
         T: Clone,
@@ -520,6 +501,7 @@ pub fn uok<'tcx>() -> ures<'tcx> {
     Ok(())
 }
 
+#[must_use = "once you start a snapshot, you should always consume it"]
 pub struct CombinedSnapshot {
     type_snapshot: type_variable::Snapshot,
     int_snapshot: unify::Snapshot<ty::IntVid>,
@@ -528,8 +510,8 @@ pub struct CombinedSnapshot {
 }
 
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
-    pub fn skolemize<T:TypeFoldable<'tcx>>(&self, t: T) -> T {
-        t.fold_with(&mut self.skolemizer())
+    pub fn freshen<T:TypeFoldable<'tcx>>(&self, t: T) -> T {
+        t.fold_with(&mut self.freshener())
     }
 
     pub fn type_var_diverges(&'a self, ty: Ty) -> bool {
@@ -539,8 +521,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
     }
 
-    pub fn skolemizer<'b>(&'b self) -> TypeSkolemizer<'b, 'tcx> {
-        skolemize::TypeSkolemizer::new(self)
+    pub fn freshener<'b>(&'b self) -> TypeFreshener<'b, 'tcx> {
+        freshen::TypeFreshener::new(self)
     }
 
     pub fn combine_fields<'b>(&'b self, a_is_expected: bool, trace: TypeTrace<'tcx>)
@@ -629,16 +611,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E> where
         F: FnOnce() -> Result<T, E>
     {
-        self.commit_unconditionally(move || self.try(move || f()))
+        self.commit_unconditionally(move || self.try(move |_| f()))
     }
 
     /// Execute `f`, unroll bindings on panic
     pub fn try<T, E, F>(&self, f: F) -> Result<T, E> where
-        F: FnOnce() -> Result<T, E>
+        F: FnOnce(&CombinedSnapshot) -> Result<T, E>
     {
         debug!("try()");
         let snapshot = self.start_snapshot();
-        let r = f();
+        let r = f(&snapshot);
         debug!("try() -- r.is_ok() = {}", r.is_ok());
         match r {
             Ok(_) => {
@@ -653,11 +635,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
     /// Execute `f` then unroll any bindings it creates
     pub fn probe<R, F>(&self, f: F) -> R where
-        F: FnOnce() -> R,
+        F: FnOnce(&CombinedSnapshot) -> R,
     {
         debug!("probe()");
         let snapshot = self.start_snapshot();
-        let r = f();
+        let r = f(&snapshot);
         self.rollback_to(snapshot);
         r
     }
@@ -715,15 +697,93 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         self.commit_if_ok(|| {
             let trace = TypeTrace {
                 origin: origin,
-                values: TraitRefs(expected_found(a_is_expected,
-                                                 a.clone(), b.clone()))
+                values: TraitRefs(expected_found(a_is_expected, a.clone(), b.clone()))
             };
             self.sub(a_is_expected, trace).trait_refs(&*a, &*b).to_ures()
         })
     }
-}
 
-impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    pub fn sub_poly_trait_refs(&self,
+                               a_is_expected: bool,
+                               origin: TypeOrigin,
+                               a: Rc<ty::PolyTraitRef<'tcx>>,
+                               b: Rc<ty::PolyTraitRef<'tcx>>)
+                               -> ures<'tcx>
+    {
+        debug!("sub_poly_trait_refs({} <: {})",
+               a.repr(self.tcx),
+               b.repr(self.tcx));
+        self.commit_if_ok(|| {
+            let trace = TypeTrace {
+                origin: origin,
+                values: PolyTraitRefs(expected_found(a_is_expected, a.clone(), b.clone()))
+            };
+            self.sub(a_is_expected, trace).binders(&*a, &*b).to_ures()
+        })
+    }
+
+    pub fn skolemize_late_bound_regions<T>(&self,
+                                           value: &ty::Binder<T>,
+                                           snapshot: &CombinedSnapshot)
+                                           -> (T, SkolemizationMap)
+        where T : TypeFoldable<'tcx> + Repr<'tcx>
+    {
+        /*! See `higher_ranked::skolemize_late_bound_regions` */
+
+        higher_ranked::skolemize_late_bound_regions(self, value, snapshot)
+    }
+
+    pub fn leak_check(&self,
+                      skol_map: &SkolemizationMap,
+                      snapshot: &CombinedSnapshot)
+                      -> ures<'tcx>
+    {
+        /*! See `higher_ranked::leak_check` */
+
+        match higher_ranked::leak_check(self, skol_map, snapshot) {
+            Ok(()) => Ok(()),
+            Err((br, r)) => Err(ty::terr_regions_insufficiently_polymorphic(br, r))
+        }
+    }
+
+    pub fn plug_leaks<T>(&self,
+                         skol_map: SkolemizationMap,
+                         snapshot: &CombinedSnapshot,
+                         value: &T)
+                         -> T
+        where T : TypeFoldable<'tcx> + Repr<'tcx>
+    {
+        /*! See `higher_ranked::leak_check` */
+
+        higher_ranked::plug_leaks(self, skol_map, snapshot, value)
+    }
+
+    pub fn equality_predicate(&self,
+                              span: Span,
+                              predicate: &ty::PolyEquatePredicate<'tcx>)
+                              -> ures<'tcx> {
+        self.try(|snapshot| {
+            let (ty::EquatePredicate(a, b), skol_map) =
+                self.skolemize_late_bound_regions(predicate, snapshot);
+            let origin = EquatePredicate(span);
+            let () = try!(mk_eqty(self, false, origin, a, b));
+            self.leak_check(&skol_map, snapshot)
+        })
+    }
+
+    pub fn region_outlives_predicate(&self,
+                                     span: Span,
+                                     predicate: &ty::PolyRegionOutlivesPredicate)
+                                     -> ures<'tcx> {
+        self.try(|snapshot| {
+            let (ty::OutlivesPredicate(r_a, r_b), skol_map) =
+                self.skolemize_late_bound_regions(predicate, snapshot);
+            let origin = RelateRegionParamBound(span);
+            let () = mk_subr(self, origin, r_b, r_a); // `b : a` ==> `a <= b`
+            self.leak_check(&skol_map, snapshot)
+        })
+    }
+
     pub fn next_ty_var_id(&self, diverging: bool) -> TyVid {
         self.type_variables
             .borrow_mut()
@@ -821,7 +881,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
     pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
         ty_to_string(self.tcx,
-                     self.resolve_type_vars_if_possible(t))
+                     self.resolve_type_vars_if_possible(&t))
     }
 
     pub fn tys_to_string(&self, ts: &[Ty<'tcx>]) -> String {
@@ -830,24 +890,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 
     pub fn trait_ref_to_string(&self, t: &Rc<ty::TraitRef<'tcx>>) -> String {
-        let t = self.resolve_type_vars_in_trait_ref_if_possible(&**t);
-        trait_ref_to_string(self.tcx, &t)
-    }
-
-    pub fn contains_unbound_type_variables(&self, typ: Ty<'tcx>) -> Ty<'tcx> {
-        match resolve_type(self,
-                           None,
-                           typ, resolve_nested_tvar | resolve_ivar) {
-          Ok(new_type) => new_type,
-          Err(_) => typ
-        }
+        let t = self.resolve_type_vars_if_possible(&**t);
+        t.user_string(self.tcx)
     }
 
     pub fn shallow_resolve(&self, typ: Ty<'tcx>) -> Ty<'tcx> {
         match typ.sty {
             ty::ty_infer(ty::TyVar(v)) => {
+                // Not entirely obvious: if `typ` is a type variable,
+                // it can be resolved to an int/float variable, which
+                // can then be recursively resolved, hence the
+                // recursion. Note though that we prevent type
+                // variables from unifying to other type variables
+                // directly (though they may be embedded
+                // structurally), and we prevent cycles in any case,
+                // so this recursion should always be of very limited
+                // depth.
                 self.type_variables.borrow()
                     .probe(v)
+                    .map(|t| self.shallow_resolve(t))
                     .unwrap_or(typ)
             }
 
@@ -867,35 +928,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
     }
 
-    pub fn resolve_type_vars_if_possible(&self, typ: Ty<'tcx>) -> Ty<'tcx> {
-        match resolve_type(self,
-                           None,
-                           typ, resolve_nested_tvar | resolve_ivar) {
-          Ok(new_type) => new_type,
-          Err(_) => typ
-        }
+    pub fn resolve_type_vars_if_possible<T:TypeFoldable<'tcx>>(&self, value: &T) -> T {
+        /*!
+         * Where possible, replaces type/int/float variables in
+         * `value` with their final value. Note that region variables
+         * are unaffected. If a type variable has not been unified, it
+         * is left as is.  This is an idempotent operation that does
+         * not affect inference state in any way and so you can do it
+         * at will.
+         */
+
+        let mut r = resolve::OpportunisticTypeResolver::new(self);
+        value.fold_with(&mut r)
     }
 
-    pub fn resolve_type_vars_in_trait_ref_if_possible(&self,
-                                                      trait_ref: &ty::TraitRef<'tcx>)
-                                                      -> ty::TraitRef<'tcx> {
-        // make up a dummy type just to reuse/abuse the resolve machinery
-        let dummy0 = ty::mk_trait(self.tcx,
-                                  (*trait_ref).clone(),
-                                  ty::region_existential_bound(ty::ReStatic));
-        let dummy1 = self.resolve_type_vars_if_possible(dummy0);
-        match dummy1.sty {
-            ty::ty_trait(box ty::TyTrait { ref principal, .. }) => {
-                (*principal).clone()
-            }
-            _ => {
-                self.tcx.sess.bug(
-                    format!("resolve_type_vars_if_possible() yielded {} \
-                             when supplied with {}",
-                            self.ty_to_string(dummy0),
-                            self.ty_to_string(dummy1)).as_slice());
-            }
-        }
+    pub fn fully_resolve<T:TypeFoldable<'tcx>>(&self, value: &T) -> fres<T> {
+        /*!
+         * Attempts to resolve all type/region variables in
+         * `value`. Region inference must have been run already (e.g.,
+         * by calling `resolve_regions_and_report_errors`).  If some
+         * variable was never unified, an `Err` results.
+         *
+         * This method is idempotent, but it not typically not invoked
+         * except during the writeback phase.
+         */
+
+        resolve::fully_resolve(self, value)
     }
 
     // [Note-Type-error-reporting]
@@ -929,9 +987,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     {
         debug!("hi! expected_ty = {}, actual_ty = {}", expected_ty, actual_ty);
 
-        let resolved_expected = expected_ty.map(|e_ty| {
-            self.resolve_type_vars_if_possible(e_ty)
-        });
+        let resolved_expected = expected_ty.map(|e_ty| self.resolve_type_vars_if_possible(&e_ty));
 
         match resolved_expected {
             Some(t) if ty::type_is_error(t) => (),
@@ -958,7 +1014,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                                  err: Option<&ty::type_err<'tcx>>) where
         M: FnOnce(String) -> String,
     {
-        let actual_ty = self.resolve_type_vars_if_possible(actual_ty);
+        let actual_ty = self.resolve_type_vars_if_possible(&actual_ty);
 
         // Don't report an error if actual type is ty_err.
         if ty::type_is_error(actual_ty) {
@@ -989,9 +1045,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         &self,
         span: Span,
         lbrct: LateBoundRegionConversionTime,
-        value: &T)
+        value: &ty::Binder<T>)
         -> (T, FnvHashMap<ty::BoundRegion,ty::Region>)
-        where T : HigherRankedFoldable<'tcx>
+        where T : TypeFoldable<'tcx> + Repr<'tcx>
     {
         ty::replace_late_bound_regions(
             self.tcx,
diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs
index ca2860ae6b3..d34373e66a1 100644
--- a/src/librustc/middle/infer/region_inference/mod.rs
+++ b/src/librustc/middle/infer/region_inference/mod.rs
@@ -81,7 +81,6 @@ impl Copy for TwoRegions {}
 pub enum UndoLogEntry {
     OpenSnapshot,
     CommitedSnapshot,
-    Mark,
     AddVar(RegionVid),
     AddConstraint(Constraint),
     AddVerify(uint),
@@ -225,19 +224,12 @@ pub struct RegionVarBindings<'a, 'tcx: 'a> {
 }
 
 #[deriving(Show)]
+#[allow(missing_copy_implementations)]
 pub struct RegionSnapshot {
-    length: uint
+    length: uint,
+    skolemization_count: uint,
 }
 
-impl Copy for RegionSnapshot {}
-
-#[deriving(Show)]
-pub struct RegionMark {
-    length: uint
-}
-
-impl Copy for RegionMark {}
-
 impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
     pub fn new(tcx: &'a ty::ctxt<'tcx>) -> RegionVarBindings<'a, 'tcx> {
         RegionVarBindings {
@@ -263,14 +255,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         let length = self.undo_log.borrow().len();
         debug!("RegionVarBindings: start_snapshot({})", length);
         self.undo_log.borrow_mut().push(OpenSnapshot);
-        RegionSnapshot { length: length }
-    }
-
-    pub fn mark(&self) -> RegionMark {
-        let length = self.undo_log.borrow().len();
-        debug!("RegionVarBindings: mark({})", length);
-        self.undo_log.borrow_mut().push(Mark);
-        RegionMark { length: length }
+        RegionSnapshot { length: length, skolemization_count: self.skolemization_count.get() }
     }
 
     pub fn commit(&self, snapshot: RegionSnapshot) {
@@ -284,6 +269,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         } else {
             (*undo_log)[snapshot.length] = CommitedSnapshot;
         }
+        self.skolemization_count.set(snapshot.skolemization_count);
     }
 
     pub fn rollback_to(&self, snapshot: RegionSnapshot) {
@@ -296,7 +282,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
                 OpenSnapshot => {
                     panic!("Failure to observe stack discipline");
                 }
-                Mark | CommitedSnapshot => { }
+                CommitedSnapshot => { }
                 AddVar(vid) => {
                     let mut var_origins = self.var_origins.borrow_mut();
                     var_origins.pop().unwrap();
@@ -322,6 +308,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         }
         let c = undo_log.pop().unwrap();
         assert!(c == OpenSnapshot);
+        self.skolemization_count.set(snapshot.skolemization_count);
     }
 
     pub fn num_vars(&self) -> uint {
@@ -340,7 +327,25 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         return vid;
     }
 
-    pub fn new_skolemized(&self, br: ty::BoundRegion) -> Region {
+    /// Creates a new skolemized region. Skolemized regions are fresh
+    /// regions used when performing higher-ranked computations. They
+    /// must be used in a very particular way and are never supposed
+    /// to "escape" out into error messages or the code at large.
+    ///
+    /// The idea is to always create a snapshot. Skolemized regions
+    /// can be created in the context of this snapshot, but once the
+    /// snapshot is commited or rolled back, their numbers will be
+    /// recycled, so you must be finished with them. See the extensive
+    /// comments in `higher_ranked.rs` to see how it works (in
+    /// particular, the subtyping comparison).
+    ///
+    /// The `snapshot` argument to this function is not really used;
+    /// it's just there to make it explicit which snapshot bounds the
+    /// skolemized region that results.
+    pub fn new_skolemized(&self, br: ty::BoundRegion, snapshot: &RegionSnapshot) -> Region {
+        assert!(self.in_snapshot());
+        assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot);
+
         let sc = self.skolemization_count.get();
         self.skolemization_count.set(sc + 1);
         ReInfer(ReSkolemized(sc, br))
@@ -597,8 +602,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         ReInfer(ReVar(c))
     }
 
-    pub fn vars_created_since_mark(&self, mark: RegionMark)
-                                   -> Vec<RegionVid>
+    pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot)
+                                       -> Vec<RegionVid>
     {
         self.undo_log.borrow()
             .slice_from(mark.length)
@@ -613,7 +618,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
     /// Computes all regions that have been related to `r0` in any way since the mark `mark` was
     /// made---`r0` itself will be the first entry. This is used when checking whether skolemized
     /// regions are being improperly related to other regions.
-    pub fn tainted(&self, mark: RegionMark, r0: Region) -> Vec<Region> {
+    pub fn tainted(&self, mark: &RegionSnapshot, r0: Region) -> Vec<Region> {
         debug!("tainted(mark={}, r0={})", mark, r0.repr(self.tcx));
         let _indenter = indenter();
 
@@ -668,7 +673,6 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
                         }
                     }
                     &AddCombination(..) |
-                    &Mark |
                     &AddVar(..) |
                     &OpenSnapshot |
                     &CommitedSnapshot => {
diff --git a/src/librustc/middle/infer/resolve.rs b/src/librustc/middle/infer/resolve.rs
index eaf363ffc74..12400de31ed 100644
--- a/src/librustc/middle/infer/resolve.rs
+++ b/src/librustc/middle/infer/resolve.rs
@@ -8,253 +8,108 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Resolution is the process of removing type variables and replacing
-// them with their inferred values.  Unfortunately our inference has
-// become fairly complex and so there are a number of options to
-// control *just how much* you want to resolve and how you want to do
-// it.
-//
-// # Controlling the scope of resolution
-//
-// The options resolve_* determine what kinds of variables get
-// resolved.  Generally resolution starts with a top-level type
-// variable; we will always resolve this.  However, once we have
-// resolved that variable, we may end up with a type that still
-// contains type variables.  For example, if we resolve `<T0>` we may
-// end up with something like `[<T1>]`.  If the option
-// `resolve_nested_tvar` is passed, we will then go and recursively
-// resolve `<T1>`.
-//
-// The options `resolve_rvar` controls whether we resolve region
-// variables. The options `resolve_fvar` and `resolve_ivar` control
-// whether we resolve floating point and integral variables,
-// respectively.
-//
-// # What do if things are unconstrained
-//
-// Sometimes we will encounter a variable that has no constraints, and
-// therefore cannot sensibly be mapped to any particular result.  By
-// default, we will leave such variables as is (so you will get back a
-// variable in your result).  The options force_* will cause the
-// resolution to fail in this case instead, except for the case of
-// integral variables, which resolve to `int` if forced.
-//
-// # resolve_all and force_all
-//
-// The options are a bit set, so you can use the *_all to resolve or
-// force all kinds of variables (including those we may add in the
-// future).  If you want to resolve everything but one type, you are
-// probably better off writing `resolve_all - resolve_ivar`.
-
-#![allow(non_upper_case_globals)]
-
-use super::{fixup_err, fres, InferCtxt};
-use super::{unresolved_int_ty,unresolved_float_ty,unresolved_ty};
-
-use middle::ty::{FloatVar, FloatVid, IntVar, IntVid, RegionVid, TyVar, TyVid};
-use middle::ty::{IntType, UintType};
+use super::{InferCtxt, fixup_err, fres, unresolved_ty, unresolved_int_ty, unresolved_float_ty};
 use middle::ty::{mod, Ty};
-use middle::ty_fold;
-use syntax::codemap::Span;
-use util::ppaux::{Repr, ty_to_string};
-
-pub const resolve_nested_tvar: uint = 0b0000000001;
-pub const resolve_rvar: uint        = 0b0000000010;
-pub const resolve_ivar: uint        = 0b0000000100;
-pub const resolve_fvar: uint        = 0b0000001000;
-pub const resolve_all: uint         = 0b0000001111;
-pub const force_tvar: uint          = 0b0000100000;
-pub const force_rvar: uint          = 0b0001000000;
-pub const force_ivar: uint          = 0b0010000000;
-pub const force_fvar: uint          = 0b0100000000;
-pub const force_all: uint           = 0b0111100000;
-
-pub const not_regions: uint         = !(force_rvar | resolve_rvar);
-
-pub const try_resolve_tvar_shallow: uint = 0;
-pub const resolve_and_force_all_but_regions: uint =
-    (resolve_all | force_all) & not_regions;
-
-pub struct ResolveState<'a, 'tcx: 'a> {
+use middle::ty_fold::{mod, TypeFoldable};
+use util::ppaux::Repr;
+
+///////////////////////////////////////////////////////////////////////////
+// OPPORTUNISTIC TYPE RESOLVER
+
+/// The opportunistic type resolver can be used at any time. It simply replaces
+/// type variables that have been unified with the things they have
+/// been unified with (similar to `shallow_resolve`, but deep). This is
+/// useful for printing messages etc but also required at various
+/// points for correctness.
+pub struct OpportunisticTypeResolver<'a, 'tcx:'a> {
     infcx: &'a InferCtxt<'a, 'tcx>,
-    modes: uint,
-    err: Option<fixup_err>,
-    type_depth: uint,
 }
 
-pub fn resolver<'a, 'tcx>(infcx: &'a InferCtxt<'a, 'tcx>,
-                          modes: uint,
-                          _: Option<Span>)
-                          -> ResolveState<'a, 'tcx> {
-    ResolveState {
-        infcx: infcx,
-        modes: modes,
-        err: None,
-        type_depth: 0,
+impl<'a, 'tcx> OpportunisticTypeResolver<'a, 'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> OpportunisticTypeResolver<'a, 'tcx> {
+        OpportunisticTypeResolver { infcx: infcx }
     }
 }
 
-impl<'a, 'tcx> ty_fold::TypeFolder<'tcx> for ResolveState<'a, 'tcx> {
+impl<'a, 'tcx> ty_fold::TypeFolder<'tcx> for OpportunisticTypeResolver<'a, 'tcx> {
     fn tcx(&self) -> &ty::ctxt<'tcx> {
         self.infcx.tcx
     }
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        self.resolve_type(t)
-    }
-
-    fn fold_region(&mut self, r: ty::Region) -> ty::Region {
-        self.resolve_region(r)
-    }
-}
-
-impl<'a, 'tcx> ResolveState<'a, 'tcx> {
-    pub fn should(&mut self, mode: uint) -> bool {
-        (self.modes & mode) == mode
-    }
-
-    pub fn resolve_type_chk(&mut self, typ: Ty<'tcx>) -> fres<Ty<'tcx>> {
-        self.err = None;
-
-        debug!("Resolving {} (modes={:x})",
-               ty_to_string(self.infcx.tcx, typ),
-               self.modes);
-
-        // n.b. This is a hokey mess because the current fold doesn't
-        // allow us to pass back errors in any useful way.
-
-        let rty = self.resolve_type(typ);
-        match self.err {
-            None => {
-                debug!("Resolved {} to {} (modes={:x})",
-                       ty_to_string(self.infcx.tcx, typ),
-                       ty_to_string(self.infcx.tcx, rty),
-                       self.modes);
-                return Ok(rty);
-            }
-            Some(e) => {
-                return Err(e);
-            }
-        }
-    }
-
-    pub fn resolve_region_chk(&mut self,
-                              orig: ty::Region)
-                              -> fres<ty::Region> {
-        self.err = None;
-        let resolved = self.resolve_region(orig);
-        match self.err {
-          None => Ok(resolved),
-          Some(e) => Err(e)
+        if !ty::type_has_ty_infer(t) {
+            t // micro-optimize -- if there is nothing in this type that this fold affects...
+        } else {
+            let t0 = self.infcx.shallow_resolve(t);
+            ty_fold::super_fold_ty(self, t0)
         }
     }
+}
 
-    pub fn resolve_type(&mut self, typ: Ty<'tcx>) -> Ty<'tcx> {
-        debug!("resolve_type({})", typ.repr(self.infcx.tcx));
-
-        if !ty::type_needs_infer(typ) {
-            return typ;
-        }
-
-        if self.type_depth > 0 && !self.should(resolve_nested_tvar) {
-            return typ;
-        }
-
-        match typ.sty {
-            ty::ty_infer(TyVar(vid)) => {
-                self.resolve_ty_var(vid)
-            }
-            ty::ty_infer(IntVar(vid)) => {
-                self.resolve_int_var(vid)
-            }
-            ty::ty_infer(FloatVar(vid)) => {
-                self.resolve_float_var(vid)
-            }
-            _ => {
-                if self.modes & resolve_all == 0 {
-                    // if we are only resolving top-level type
-                    // variables, and this is not a top-level type
-                    // variable, then shortcircuit for efficiency
-                    typ
-                } else {
-                    self.type_depth += 1;
-                    let result = ty_fold::super_fold_ty(self, typ);
-                    self.type_depth -= 1;
-                    result
-                }
-            }
-        }
+///////////////////////////////////////////////////////////////////////////
+// FULL TYPE RESOLUTION
+
+/// Full type resolution replaces all type and region variables with
+/// their concrete results. If any variable cannot be replaced (never unified, etc)
+/// then an `Err` result is returned.
+pub fn fully_resolve<'a, 'tcx, T>(infcx: &InferCtxt<'a,'tcx>, value: &T) -> fres<T>
+    where T : TypeFoldable<'tcx>
+{
+    let mut full_resolver = FullTypeResolver { infcx: infcx, err: None };
+    let result = value.fold_with(&mut full_resolver);
+    match full_resolver.err {
+        None => Ok(result),
+        Some(e) => Err(e),
     }
+}
 
-    pub fn resolve_region(&mut self, orig: ty::Region) -> ty::Region {
-        debug!("Resolve_region({})", orig.repr(self.infcx.tcx));
-        match orig {
-          ty::ReInfer(ty::ReVar(rid)) => self.resolve_region_var(rid),
-          _ => orig
-        }
-    }
+// N.B. This type is not public because the protocol around checking the
+// `err` field is not enforcable otherwise.
+struct FullTypeResolver<'a, 'tcx:'a> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+    err: Option<fixup_err>,
+}
 
-    pub fn resolve_region_var(&mut self, rid: RegionVid) -> ty::Region {
-        if !self.should(resolve_rvar) {
-            return ty::ReInfer(ty::ReVar(rid));
-        }
-        self.infcx.region_vars.resolve_var(rid)
+impl<'a, 'tcx> ty_fold::TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> {
+    fn tcx(&self) -> &ty::ctxt<'tcx> {
+        self.infcx.tcx
     }
 
-    pub fn resolve_ty_var(&mut self, vid: TyVid) -> Ty<'tcx> {
-        let tcx = self.infcx.tcx;
-        let tv = self.infcx.type_variables.borrow();
-        match tv.probe(vid) {
-            Some(t) => {
-                self.resolve_type(t)
-            }
-            None => {
-                if self.should(force_tvar) {
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        if !ty::type_needs_infer(t) {
+            t // micro-optimize -- if there is nothing in this type that this fold affects...
+        } else {
+            let t = self.infcx.shallow_resolve(t);
+            match t.sty {
+                ty::ty_infer(ty::TyVar(vid)) => {
                     self.err = Some(unresolved_ty(vid));
+                    ty::mk_err()
+                }
+                ty::ty_infer(ty::IntVar(vid)) => {
+                    self.err = Some(unresolved_int_ty(vid));
+                    ty::mk_err()
+                }
+                ty::ty_infer(ty::FloatVar(vid)) => {
+                    self.err = Some(unresolved_float_ty(vid));
+                    ty::mk_err()
+                }
+                ty::ty_infer(_) => {
+                    self.infcx.tcx.sess.bug(
+                        format!("Unexpected type in full type resolver: {}",
+                                t.repr(self.infcx.tcx))[]);
+                }
+                _ => {
+                    ty_fold::super_fold_ty(self, t)
                 }
-                ty::mk_var(tcx, vid)
-            }
-        }
-    }
-
-    pub fn resolve_int_var(&mut self, vid: IntVid) -> Ty<'tcx> {
-        if !self.should(resolve_ivar) {
-            return ty::mk_int_var(self.infcx.tcx, vid);
-        }
-
-        let tcx = self.infcx.tcx;
-        let table = &self.infcx.int_unification_table;
-        let node = table.borrow_mut().get(tcx, vid);
-        match node.value {
-          Some(IntType(t)) => ty::mk_mach_int(t),
-          Some(UintType(t)) => ty::mk_mach_uint(t),
-          None => {
-            if self.should(force_ivar) {
-                // As a last resort, emit an error.
-                self.err = Some(unresolved_int_ty(vid));
             }
-            ty::mk_int_var(self.infcx.tcx, vid)
-          }
         }
     }
 
-    pub fn resolve_float_var(&mut self, vid: FloatVid) -> Ty<'tcx> {
-        if !self.should(resolve_fvar) {
-            return ty::mk_float_var(self.infcx.tcx, vid);
-        }
-
-        let tcx = self.infcx.tcx;
-        let table = &self.infcx.float_unification_table;
-        let node = table.borrow_mut().get(tcx, vid);
-        match node.value {
-          Some(t) => ty::mk_mach_float(t),
-          None => {
-            if self.should(force_fvar) {
-                // As a last resort, emit an error.
-                self.err = Some(unresolved_float_ty(vid));
-            }
-            ty::mk_float_var(self.infcx.tcx, vid)
-          }
+    fn fold_region(&mut self, r: ty::Region) -> ty::Region {
+        match r {
+          ty::ReInfer(ty::ReVar(rid)) => self.infcx.region_vars.resolve_var(rid),
+          _ => r,
         }
     }
 }
+
diff --git a/src/librustc/middle/infer/sub.rs b/src/librustc/middle/infer/sub.rs
index 00c79bc726c..2b8adfb7c1e 100644
--- a/src/librustc/middle/infer/sub.rs
+++ b/src/librustc/middle/infer/sub.rs
@@ -155,13 +155,9 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
         }
     }
 
-    fn fn_sigs(&self, a: &ty::FnSig<'tcx>, b: &ty::FnSig<'tcx>)
-               -> cres<'tcx, ty::FnSig<'tcx>> {
-        self.higher_ranked_sub(a, b)
-    }
-
-    fn trait_refs(&self, a: &ty::TraitRef<'tcx>, b: &ty::TraitRef<'tcx>)
-                  -> cres<'tcx, ty::TraitRef<'tcx>> {
+    fn binders<T>(&self, a: &ty::Binder<T>, b: &ty::Binder<T>) -> cres<'tcx, ty::Binder<T>>
+        where T : Combineable<'tcx>
+    {
         self.higher_ranked_sub(a, b)
     }
 }
diff --git a/src/librustc/middle/infer/type_variable.rs b/src/librustc/middle/infer/type_variable.rs
index 766e930486c..0d7f542535c 100644
--- a/src/librustc/middle/infer/type_variable.rs
+++ b/src/librustc/middle/infer/type_variable.rs
@@ -13,7 +13,9 @@ use self::TypeVariableValue::*;
 use self::UndoEntry::*;
 
 use middle::ty::{mod, Ty};
+use std::cmp::min;
 use std::mem;
+use std::uint;
 use util::snapshot_vec as sv;
 
 pub struct TypeVariableTable<'tcx> {
@@ -78,7 +80,6 @@ impl<'tcx> TypeVariableTable<'tcx> {
     ///
     /// Precondition: neither `a` nor `b` are known.
     pub fn relate_vars(&mut self, a: ty::TyVid, dir: RelationDir, b: ty::TyVid) {
-
         if a != b {
             self.relations(a).push((dir, b));
             self.relations(b).push((dir.opposite(), a));
@@ -151,6 +152,49 @@ impl<'tcx> TypeVariableTable<'tcx> {
     pub fn commit(&mut self, s: Snapshot) {
         self.values.commit(s.snapshot);
     }
+
+    pub fn types_escaping_snapshot(&self, s: &Snapshot) -> Vec<Ty<'tcx>> {
+        /*!
+         * Find the set of type variables that existed *before* `s`
+         * but which have only been unified since `s` started, and
+         * return the types with which they were unified. So if we had
+         * a type variable `V0`, then we started the snapshot, then we
+         * created a type variable `V1`, unifed `V0` with `T0`, and
+         * unified `V1` with `T1`, this function would return `{T0}`.
+         */
+
+        let mut new_elem_threshold = uint::MAX;
+        let mut escaping_types = Vec::new();
+        let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot);
+        debug!("actions_since_snapshot.len() = {}", actions_since_snapshot.len());
+        for action in actions_since_snapshot.iter() {
+            match *action {
+                sv::UndoLog::NewElem(index) => {
+                    // if any new variables were created during the
+                    // snapshot, remember the lower index (which will
+                    // always be the first one we see). Note that this
+                    // action must precede those variables being
+                    // specified.
+                    new_elem_threshold = min(new_elem_threshold, index);
+                    debug!("NewElem({}) new_elem_threshold={}", index, new_elem_threshold);
+                }
+
+                sv::UndoLog::Other(SpecifyVar(vid, _)) => {
+                    if vid.index < new_elem_threshold {
+                        // quick check to see if this variable was
+                        // created since the snapshot started or not.
+                        let escaping_type = self.probe(vid).unwrap();
+                        escaping_types.push(escaping_type);
+                    }
+                    debug!("SpecifyVar({}) new_elem_threshold={}", vid, new_elem_threshold);
+                }
+
+                _ => { }
+            }
+        }
+
+        escaping_types
+    }
 }
 
 impl<'tcx> sv::SnapshotVecDelegate<TypeVariableData<'tcx>,UndoEntry> for Delegate {
diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs
index acfdf6fefb5..ea19111ce3d 100644
--- a/src/librustc/middle/intrinsicck.rs
+++ b/src/librustc/middle/intrinsicck.rs
@@ -124,8 +124,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
                     let typ = ty::node_id_to_type(self.tcx, expr.id);
                     match typ.sty {
                         ty_bare_fn(ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
-                            if let ty::FnConverging(to) = bare_fn_ty.sig.output {
-                                let from = bare_fn_ty.sig.inputs[0];
+                            if let ty::FnConverging(to) = bare_fn_ty.sig.0.output {
+                                let from = bare_fn_ty.sig.0.inputs[0];
                                 self.check_transmute(expr.span, from, to, expr.id);
                             }
                         }
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index c76d9bc6b1f..4df655882b1 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -1534,6 +1534,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                     .unwrap()
                     .closure_type
                     .sig
+                    .0
                     .output,
             _ => ty::ty_fn_ret(fn_ty)
         }
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 1923142be9e..33701905aa1 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -108,7 +108,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
                 ast::ItemTy(_, ref generics) |
                 ast::ItemEnum(_, ref generics) |
                 ast::ItemStruct(_, ref generics) |
-                ast::ItemTrait(_, ref generics, _, _, _) => {
+                ast::ItemTrait(_, ref generics, _, _, _) |
+                ast::ItemImpl(_, ref generics, _, _, _) => {
                     // These kinds of items have only early bound lifetime parameters.
                     let lifetimes = &generics.lifetimes;
                     let early_scope = EarlyScope(subst::TypeSpace, lifetimes, &ROOT_SCOPE);
@@ -117,12 +118,6 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
                         visit::walk_item(this, item);
                     });
                 }
-                ast::ItemImpl(_, ref generics, _, _, _) => {
-                    // Impls have both early- and late-bound lifetimes.
-                    this.visit_early_late(subst::TypeSpace, generics, |this| {
-                        visit::walk_item(this, item);
-                    })
-                }
             }
         });
     }
diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs
index 822979c8601..9804f6d222a 100644
--- a/src/librustc/middle/traits/coherence.rs
+++ b/src/librustc/middle/traits/coherence.rs
@@ -17,7 +17,8 @@ use super::util;
 use middle::subst;
 use middle::subst::Subst;
 use middle::ty::{mod, Ty};
-use middle::infer::{mod, InferCtxt};
+use middle::infer::InferCtxt;
+use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap::DUMMY_SP;
 use util::ppaux::Repr;
@@ -37,18 +38,14 @@ pub fn impl_can_satisfy(infcx: &InferCtxt,
     let impl1_substs =
         util::fresh_substs_for_impl(infcx, DUMMY_SP, impl1_def_id);
     let impl1_trait_ref =
-        ty::impl_trait_ref(infcx.tcx, impl1_def_id).unwrap()
-                                                   .subst(infcx.tcx, &impl1_substs);
-    let impl1_trait_ref =
-        infcx.replace_late_bound_regions_with_fresh_var(DUMMY_SP,
-                                                        infer::FnCall,
-                                                        &impl1_trait_ref).0;
+        (*ty::impl_trait_ref(infcx.tcx, impl1_def_id).unwrap()).subst(infcx.tcx, &impl1_substs);
 
     // Determine whether `impl2` can provide an implementation for those
     // same types.
     let param_env = ty::empty_parameter_environment();
     let mut selcx = SelectionContext::intercrate(infcx, &param_env, infcx.tcx);
-    let obligation = Obligation::new(ObligationCause::dummy(), impl1_trait_ref);
+    let obligation = Obligation::new(ObligationCause::dummy(),
+                                     Rc::new(ty::Binder(impl1_trait_ref)));
     debug!("impl_can_satisfy(obligation={})", obligation.repr(infcx.tcx));
     selcx.evaluate_impl(impl2_def_id, &obligation)
 }
@@ -143,7 +140,7 @@ pub fn ty_is_local<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
         }
 
         ty::ty_trait(ref tt) => {
-            tt.principal.def_id.krate == ast::LOCAL_CRATE
+            tt.principal.def_id().krate == ast::LOCAL_CRATE
         }
 
         // Type parameters may be bound to types that are not local to
diff --git a/src/librustc/middle/traits/doc.rs b/src/librustc/middle/traits/doc.rs
index 62246b77ee9..80697cb3a41 100644
--- a/src/librustc/middle/traits/doc.rs
+++ b/src/librustc/middle/traits/doc.rs
@@ -8,399 +8,513 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! # TRAIT RESOLUTION
-//!
-//! This document describes the general process and points out some non-obvious
-//! things.
-//!
-//! ## Major concepts
-//!
-//! Trait resolution is the process of pairing up an impl with each
-//! reference to a trait. So, for example, if there is a generic function like:
-//!
-//!     fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> { ... }
-//!
-//! and then a call to that function:
-//!
-//!     let v: Vec<int> = clone_slice([1, 2, 3].as_slice())
-//!
-//! it is the job of trait resolution to figure out (in which case)
-//! whether there exists an impl of `int : Clone`
-//!
-//! Note that in some cases, like generic functions, we may not be able to
-//! find a specific impl, but we can figure out that the caller must
-//! provide an impl. To see what I mean, consider the body of `clone_slice`:
-//!
-//!     fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> {
-//!         let mut v = Vec::new();
-//!         for e in x.iter() {
-//!             v.push((*e).clone()); // (*)
-//!         }
-//!     }
-//!
-//! The line marked `(*)` is only legal if `T` (the type of `*e`)
-//! implements the `Clone` trait. Naturally, since we don't know what `T`
-//! is, we can't find the specific impl; but based on the bound `T:Clone`,
-//! we can say that there exists an impl which the caller must provide.
-//!
-//! We use the term *obligation* to refer to a trait reference in need of
-//! an impl.
-//!
-//! ## Overview
-//!
-//! Trait resolution consists of three major parts:
-//!
-//! - SELECTION: Deciding how to resolve a specific obligation. For
-//!   example, selection might decide that a specific obligation can be
-//!   resolved by employing an impl which matches the self type, or by
-//!   using a parameter bound. In the case of an impl, Selecting one
-//!   obligation can create *nested obligations* because of where clauses
-//!   on the impl itself. It may also require evaluating those nested
-//!   obligations to resolve ambiguities.
-//!
-//! - FULFILLMENT: The fulfillment code is what tracks that obligations
-//!   are completely fulfilled. Basically it is a worklist of obligations
-//!   to be selected: once selection is successful, the obligation is
-//!   removed from the worklist and any nested obligations are enqueued.
-//!
-//! - COHERENCE: The coherence checks are intended to ensure that there
-//!   are never overlapping impls, where two impls could be used with
-//!   equal precedence.
-//!
-//! ## Selection
-//!
-//! Selection is the process of deciding whether an obligation can be
-//! resolved and, if so, how it is to be resolved (via impl, where clause, etc).
-//! The main interface is the `select()` function, which takes an obligation
-//! and returns a `SelectionResult`. There are three possible outcomes:
-//!
-//! - `Ok(Some(selection))` -- yes, the obligation can be resolved, and
-//!   `selection` indicates how. If the impl was resolved via an impl,
-//!   then `selection` may also indicate nested obligations that are required
-//!   by the impl.
-//!
-//! - `Ok(None)` -- we are not yet sure whether the obligation can be
-//!   resolved or not. This happens most commonly when the obligation
-//!   contains unbound type variables.
-//!
-//! - `Err(err)` -- the obligation definitely cannot be resolved due to a
-//!   type error, or because there are no impls that could possibly apply,
-//!   etc.
-//!
-//! The basic algorithm for selection is broken into two big phases:
-//! candidate assembly and confirmation.
-//!
-//! ### Candidate assembly
-//!
-//! Searches for impls/where-clauses/etc that might
-//! possibly be used to satisfy the obligation. Each of those is called
-//! a candidate. To avoid ambiguity, we want to find exactly one
-//! candidate that is definitively applicable. In some cases, we may not
-//! know whether an impl/where-clause applies or not -- this occurs when
-//! the obligation contains unbound inference variables.
-//!
-//! The basic idea for candidate assembly is to do a first pass in which
-//! we identify all possible candidates. During this pass, all that we do
-//! is try and unify the type parameters. (In particular, we ignore any
-//! nested where clauses.) Presuming that this unification succeeds, the
-//! impl is added as a candidate.
-//!
-//! Once this first pass is done, we can examine the set of candidates. If
-//! it is a singleton set, then we are done: this is the only impl in
-//! scope that could possibly apply. Otherwise, we can winnow down the set
-//! of candidates by using where clauses and other conditions. If this
-//! reduced set yields a single, unambiguous entry, we're good to go,
-//! otherwise the result is considered ambiguous.
-//!
-//! #### The basic process: Inferring based on the impls we see
-//!
-//! This process is easier if we work through some examples. Consider
-//! the following trait:
-//!
-//! ```
-//! trait Convert<Target> {
-//!     fn convert(&self) -> Target;
-//! }
-//! ```
-//!
-//! This trait just has one method. It's about as simple as it gets. It
-//! converts from the (implicit) `Self` type to the `Target` type. If we
-//! wanted to permit conversion between `int` and `uint`, we might
-//! implement `Convert` like so:
-//!
-//! ```rust
-//! impl Convert<uint> for int { ... } // int -> uint
-//! impl Convert<int> for uint { ... } // uint -> uint
-//! ```
-//!
-//! Now imagine there is some code like the following:
-//!
-//! ```rust
-//! let x: int = ...;
-//! let y = x.convert();
-//! ```
-//!
-//! The call to convert will generate a trait reference `Convert<$Y> for
-//! int`, where `$Y` is the type variable representing the type of
-//! `y`. When we match this against the two impls we can see, we will find
-//! that only one remains: `Convert<uint> for int`. Therefore, we can
-//! select this impl, which will cause the type of `$Y` to be unified to
-//! `uint`. (Note that while assembling candidates, we do the initial
-//! unifications in a transaction, so that they don't affect one another.)
-//!
-//! There are tests to this effect in src/test/run-pass:
-//!
-//!    traits-multidispatch-infer-convert-source-and-target.rs
-//!    traits-multidispatch-infer-convert-target.rs
-//!
-//! #### Winnowing: Resolving ambiguities
-//!
-//! But what happens if there are multiple impls where all the types
-//! unify? Consider this example:
-//!
-//! ```rust
-//! trait Get {
-//!     fn get(&self) -> Self;
-//! }
-//!
-//! impl<T:Copy> Get for T {
-//!     fn get(&self) -> T { *self }
-//! }
-//!
-//! impl<T:Get> Get for Box<T> {
-//!     fn get(&self) -> Box<T> { box get_it(&**self) }
-//! }
-//! ```
-//!
-//! What happens when we invoke `get_it(&box 1_u16)`, for example? In this
-//! case, the `Self` type is `Box<u16>` -- that unifies with both impls,
-//! because the first applies to all types, and the second to all
-//! boxes. In the olden days we'd have called this ambiguous. But what we
-//! do now is do a second *winnowing* pass that considers where clauses
-//! and attempts to remove candidates -- in this case, the first impl only
-//! applies if `Box<u16> : Copy`, which doesn't hold. After winnowing,
-//! then, we are left with just one candidate, so we can proceed. There is
-//! a test of this in `src/test/run-pass/traits-conditional-dispatch.rs`.
-//!
-//! #### Matching
-//!
-//! The subroutines that decide whether a particular impl/where-clause/etc
-//! applies to a particular obligation. At the moment, this amounts to
-//! unifying the self types, but in the future we may also recursively
-//! consider some of the nested obligations, in the case of an impl.
-//!
-//! #### Lifetimes and selection
-//!
-//! Because of how that lifetime inference works, it is not possible to
-//! give back immediate feedback as to whether a unification or subtype
-//! relationship between lifetimes holds or not. Therefore, lifetime
-//! matching is *not* considered during selection. This is reflected in
-//! the fact that subregion assignment is infallible. This may yield
-//! lifetime constraints that will later be found to be in error (in
-//! contrast, the non-lifetime-constraints have already been checked
-//! during selection and can never cause an error, though naturally they
-//! may lead to other errors downstream).
-//!
-//! #### Where clauses
-//!
-//! Besides an impl, the other major way to resolve an obligation is via a
-//! where clause. The selection process is always given a *parameter
-//! environment* which contains a list of where clauses, which are
-//! basically obligations that can assume are satisfiable. We will iterate
-//! over that list and check whether our current obligation can be found
-//! in that list, and if so it is considered satisfied. More precisely, we
-//! want to check whether there is a where-clause obligation that is for
-//! the same trait (or some subtrait) and for which the self types match,
-//! using the definition of *matching* given above.
-//!
-//! Consider this simple example:
-//!
-//!      trait A1 { ... }
-//!      trait A2 : A1 { ... }
-//!
-//!      trait B { ... }
-//!
-//!      fn foo<X:A2+B> { ... }
-//!
-//! Clearly we can use methods offered by `A1`, `A2`, or `B` within the
-//! body of `foo`. In each case, that will incur an obligation like `X :
-//! A1` or `X : A2`. The parameter environment will contain two
-//! where-clauses, `X : A2` and `X : B`. For each obligation, then, we
-//! search this list of where-clauses.  To resolve an obligation `X:A1`,
-//! we would note that `X:A2` implies that `X:A1`.
-//!
-//! ### Confirmation
-//!
-//! Confirmation unifies the output type parameters of the trait with the
-//! values found in the obligation, possibly yielding a type error.  If we
-//! return to our example of the `Convert` trait from the previous
-//! section, confirmation is where an error would be reported, because the
-//! impl specified that `T` would be `uint`, but the obligation reported
-//! `char`. Hence the result of selection would be an error.
-//!
-//! ### Selection during translation
-//!
-//! During type checking, we do not store the results of trait selection.
-//! We simply wish to verify that trait selection will succeed. Then
-//! later, at trans time, when we have all concrete types available, we
-//! can repeat the trait selection.  In this case, we do not consider any
-//! where-clauses to be in scope. We know that therefore each resolution
-//! will resolve to a particular impl.
-//!
-//! One interesting twist has to do with nested obligations. In general, in trans,
-//! we only need to do a "shallow" selection for an obligation. That is, we wish to
-//! identify which impl applies, but we do not (yet) need to decide how to select
-//! any nested obligations. Nonetheless, we *do* currently do a complete resolution,
-//! and that is because it can sometimes inform the results of type inference. That is,
-//! we do not have the full substitutions in terms of the type varibales of the impl available
-//! to us, so we must run trait selection to figure everything out.
-//!
-//! Here is an example:
-//!
-//!     trait Foo { ... }
-//!     impl<U,T:Bar<U>> Foo for Vec<T> { ... }
-//!
-//!     impl Bar<uint> for int { ... }
-//!
-//! After one shallow round of selection for an obligation like `Vec<int>
-//! : Foo`, we would know which impl we want, and we would know that
-//! `T=int`, but we do not know the type of `U`.  We must select the
-//! nested obligation `int : Bar<U>` to find out that `U=uint`.
-//!
-//! It would be good to only do *just as much* nested resolution as
-//! necessary. Currently, though, we just do a full resolution.
-//!
-//! ## Method matching
-//!
-//! Method dispach follows a slightly different path than normal trait
-//! selection. This is because it must account for the transformed self
-//! type of the receiver and various other complications. The procedure is
-//! described in `select.rs` in the "METHOD MATCHING" section.
-//!
-//! # Caching and subtle considerations therewith
-//!
-//! In general we attempt to cache the results of trait selection.  This
-//! is a somewhat complex process. Part of the reason for this is that we
-//! want to be able to cache results even when all the types in the trait
-//! reference are not fully known. In that case, it may happen that the
-//! trait selection process is also influencing type variables, so we have
-//! to be able to not only cache the *result* of the selection process,
-//! but *replay* its effects on the type variables.
-//!
-//! ## An example
-//!
-//! The high-level idea of how the cache works is that we first replace
-//! all unbound inference variables with skolemized versions. Therefore,
-//! if we had a trait reference `uint : Foo<$1>`, where `$n` is an unbound
-//! inference variable, we might replace it with `uint : Foo<%0>`, where
-//! `%n` is a skolemized type. We would then look this up in the cache.
-//! If we found a hit, the hit would tell us the immediate next step to
-//! take in the selection process: i.e., apply impl #22, or apply where
-//! clause `X : Foo<Y>`. Let's say in this case there is no hit.
-//! Therefore, we search through impls and where clauses and so forth, and
-//! we come to the conclusion that the only possible impl is this one,
-//! with def-id 22:
-//!
-//!     impl Foo<int> for uint { ... } // Impl #22
-//!
-//! We would then record in the cache `uint : Foo<%0> ==>
-//! ImplCandidate(22)`. Next we would confirm `ImplCandidate(22)`, which
-//! would (as a side-effect) unify `$1` with `int`.
-//!
-//! Now, at some later time, we might come along and see a `uint :
-//! Foo<$3>`.  When skolemized, this would yield `uint : Foo<%0>`, just as
-//! before, and hence the cache lookup would succeed, yielding
-//! `ImplCandidate(22)`. We would confirm `ImplCandidate(22)` which would
-//! (as a side-effect) unify `$3` with `int`.
-//!
-//! ## Where clauses and the local vs global cache
-//!
-//! One subtle interaction is that the results of trait lookup will vary
-//! depending on what where clauses are in scope. Therefore, we actually
-//! have *two* caches, a local and a global cache. The local cache is
-//! attached to the `ParameterEnvironment` and the global cache attached
-//! to the `tcx`. We use the local cache whenever the result might depend
-//! on the where clauses that are in scope. The determination of which
-//! cache to use is done by the method `pick_candidate_cache` in
-//! `select.rs`.
-//!
-//! There are two cases where we currently use the local cache. The
-//! current rules are probably more conservative than necessary.
-//!
-//! ### Trait references that involve parameter types
-//!
-//! The most obvious case where you need the local environment is
-//! when the trait reference includes parameter types. For example,
-//! consider the following function:
-//!
-//!     impl<T> Vec<T> {
-//!         fn foo(x: T)
-//!             where T : Foo
-//!         { ... }
-//!
-//!         fn bar(x: T)
-//!         { ... }
-//!     }
-//!
-//! If there is an obligation `T : Foo`, or `int : Bar<T>`, or whatever,
-//! clearly the results from `foo` and `bar` are potentially different,
-//! since the set of where clauses in scope are different.
-//!
-//! ### Trait references with unbound variables when where clauses are in scope
-//!
-//! There is another less obvious interaction which involves unbound variables
-//! where *only* where clauses are in scope (no impls). This manifested as
-//! issue #18209 (`run-pass/trait-cache-issue-18209.rs`). Consider
-//! this snippet:
-//!
-//! ```
-//! pub trait Foo {
-//!     fn load_from() -> Box<Self>;
-//!     fn load() -> Box<Self> {
-//!         Foo::load_from()
-//!     }
-//! }
-//! ```
-//!
-//! The default method will incur an obligation `$0 : Foo` from the call
-//! to `load_from`. If there are no impls, this can be eagerly resolved to
-//! `VtableParam(Self : Foo)` and cached. Because the trait reference
-//! doesn't involve any parameters types (only the resolution does), this
-//! result was stored in the global cache, causing later calls to
-//! `Foo::load_from()` to get nonsense.
-//!
-//! To fix this, we always use the local cache if there are unbound
-//! variables and where clauses in scope. This is more conservative than
-//! necessary as far as I can tell. However, it still seems to be a simple
-//! rule and I observe ~99% hit rate on rustc, so it doesn't seem to hurt
-//! us in particular.
-//!
-//! Here is an example of the kind of subtle case that I would be worried
-//! about with a more complex rule (although this particular case works
-//! out ok). Imagine the trait reference doesn't directly reference a
-//! where clause, but the where clause plays a role in the winnowing
-//! phase. Something like this:
-//!
-//! ```
-//! pub trait Foo<T> { ... }
-//! pub trait Bar { ... }
-//! impl<U,T:Bar> Foo<U> for T { ... } // Impl A
-//! impl Foo<char> for uint { ... }    // Impl B
-//! ```
-//!
-//! Now, in some function, we have no where clauses in scope, and we have
-//! an obligation `$1 : Foo<$0>`. We might then conclude that `$0=char`
-//! and `$1=uint`: this is because for impl A to apply, `uint:Bar` would
-//! have to hold, and we know it does not or else the coherence check
-//! would have failed.  So we might enter into our global cache: `$1 :
-//! Foo<$0> => Impl B`.  Then we come along in a different scope, where a
-//! generic type `A` is around with the bound `A:Bar`. Now suddenly the
-//! impl is viable.
-//!
-//! The flaw in this imaginary DOOMSDAY SCENARIO is that we would not
-//! currently conclude that `$1 : Foo<$0>` implies that `$0 == uint` and
-//! `$1 == char`, even though it is true that (absent type parameters)
-//! there is no other type the user could enter. However, it is not
-//! *completely* implausible that we *could* draw this conclusion in the
-//! future; we wouldn't have to guess types, in particular, we could be
-//! led by the impls.
+/*!
+
+# TRAIT RESOLUTION
+
+This document describes the general process and points out some non-obvious
+things.
+
+## Major concepts
+
+Trait resolution is the process of pairing up an impl with each
+reference to a trait. So, for example, if there is a generic function like:
+
+    fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> { ... }
+
+and then a call to that function:
+
+    let v: Vec<int> = clone_slice([1, 2, 3].as_slice())
+
+it is the job of trait resolution to figure out (in which case)
+whether there exists an impl of `int : Clone`
+
+Note that in some cases, like generic functions, we may not be able to
+find a specific impl, but we can figure out that the caller must
+provide an impl. To see what I mean, consider the body of `clone_slice`:
+
+    fn clone_slice<T:Clone>(x: &[T]) -> Vec<T> {
+        let mut v = Vec::new();
+        for e in x.iter() {
+            v.push((*e).clone()); // (*)
+        }
+    }
+
+The line marked `(*)` is only legal if `T` (the type of `*e`)
+implements the `Clone` trait. Naturally, since we don't know what `T`
+is, we can't find the specific impl; but based on the bound `T:Clone`,
+we can say that there exists an impl which the caller must provide.
+
+We use the term *obligation* to refer to a trait reference in need of
+an impl.
+
+## Overview
+
+Trait resolution consists of three major parts:
+
+- SELECTION: Deciding how to resolve a specific obligation. For
+  example, selection might decide that a specific obligation can be
+  resolved by employing an impl which matches the self type, or by
+  using a parameter bound. In the case of an impl, Selecting one
+  obligation can create *nested obligations* because of where clauses
+  on the impl itself. It may also require evaluating those nested
+  obligations to resolve ambiguities.
+
+- FULFILLMENT: The fulfillment code is what tracks that obligations
+  are completely fulfilled. Basically it is a worklist of obligations
+  to be selected: once selection is successful, the obligation is
+  removed from the worklist and any nested obligations are enqueued.
+
+- COHERENCE: The coherence checks are intended to ensure that there
+  are never overlapping impls, where two impls could be used with
+  equal precedence.
+
+## Selection
+
+Selection is the process of deciding whether an obligation can be
+resolved and, if so, how it is to be resolved (via impl, where clause, etc).
+The main interface is the `select()` function, which takes an obligation
+and returns a `SelectionResult`. There are three possible outcomes:
+
+- `Ok(Some(selection))` -- yes, the obligation can be resolved, and
+  `selection` indicates how. If the impl was resolved via an impl,
+  then `selection` may also indicate nested obligations that are required
+  by the impl.
+
+- `Ok(None)` -- we are not yet sure whether the obligation can be
+  resolved or not. This happens most commonly when the obligation
+  contains unbound type variables.
+
+- `Err(err)` -- the obligation definitely cannot be resolved due to a
+  type error, or because there are no impls that could possibly apply,
+  etc.
+
+The basic algorithm for selection is broken into two big phases:
+candidate assembly and confirmation.
+
+### Candidate assembly
+
+Searches for impls/where-clauses/etc that might
+possibly be used to satisfy the obligation. Each of those is called
+a candidate. To avoid ambiguity, we want to find exactly one
+candidate that is definitively applicable. In some cases, we may not
+know whether an impl/where-clause applies or not -- this occurs when
+the obligation contains unbound inference variables.
+
+The basic idea for candidate assembly is to do a first pass in which
+we identify all possible candidates. During this pass, all that we do
+is try and unify the type parameters. (In particular, we ignore any
+nested where clauses.) Presuming that this unification succeeds, the
+impl is added as a candidate.
+
+Once this first pass is done, we can examine the set of candidates. If
+it is a singleton set, then we are done: this is the only impl in
+scope that could possibly apply. Otherwise, we can winnow down the set
+of candidates by using where clauses and other conditions. If this
+reduced set yields a single, unambiguous entry, we're good to go,
+otherwise the result is considered ambiguous.
+
+#### The basic process: Inferring based on the impls we see
+
+This process is easier if we work through some examples. Consider
+the following trait:
+
+```
+trait Convert<Target> {
+    fn convert(&self) -> Target;
+}
+```
+
+This trait just has one method. It's about as simple as it gets. It
+converts from the (implicit) `Self` type to the `Target` type. If we
+wanted to permit conversion between `int` and `uint`, we might
+implement `Convert` like so:
+
+```rust
+impl Convert<uint> for int { ... } // int -> uint
+impl Convert<int> for uint { ... } // uint -> uint
+```
+
+Now imagine there is some code like the following:
+
+```rust
+let x: int = ...;
+let y = x.convert();
+```
+
+The call to convert will generate a trait reference `Convert<$Y> for
+int`, where `$Y` is the type variable representing the type of
+`y`. When we match this against the two impls we can see, we will find
+that only one remains: `Convert<uint> for int`. Therefore, we can
+select this impl, which will cause the type of `$Y` to be unified to
+`uint`. (Note that while assembling candidates, we do the initial
+unifications in a transaction, so that they don't affect one another.)
+
+There are tests to this effect in src/test/run-pass:
+
+   traits-multidispatch-infer-convert-source-and-target.rs
+   traits-multidispatch-infer-convert-target.rs
+
+#### Winnowing: Resolving ambiguities
+
+But what happens if there are multiple impls where all the types
+unify? Consider this example:
+
+```rust
+trait Get {
+    fn get(&self) -> Self;
+}
+
+impl<T:Copy> Get for T {
+    fn get(&self) -> T { *self }
+}
+
+impl<T:Get> Get for Box<T> {
+    fn get(&self) -> Box<T> { box get_it(&**self) }
+}
+```
+
+What happens when we invoke `get_it(&box 1_u16)`, for example? In this
+case, the `Self` type is `Box<u16>` -- that unifies with both impls,
+because the first applies to all types, and the second to all
+boxes. In the olden days we'd have called this ambiguous. But what we
+do now is do a second *winnowing* pass that considers where clauses
+and attempts to remove candidates -- in this case, the first impl only
+applies if `Box<u16> : Copy`, which doesn't hold. After winnowing,
+then, we are left with just one candidate, so we can proceed. There is
+a test of this in `src/test/run-pass/traits-conditional-dispatch.rs`.
+
+#### Matching
+
+The subroutines that decide whether a particular impl/where-clause/etc
+applies to a particular obligation. At the moment, this amounts to
+unifying the self types, but in the future we may also recursively
+consider some of the nested obligations, in the case of an impl.
+
+#### Lifetimes and selection
+
+Because of how that lifetime inference works, it is not possible to
+give back immediate feedback as to whether a unification or subtype
+relationship between lifetimes holds or not. Therefore, lifetime
+matching is *not* considered during selection. This is reflected in
+the fact that subregion assignment is infallible. This may yield
+lifetime constraints that will later be found to be in error (in
+contrast, the non-lifetime-constraints have already been checked
+during selection and can never cause an error, though naturally they
+may lead to other errors downstream).
+
+#### Where clauses
+
+Besides an impl, the other major way to resolve an obligation is via a
+where clause. The selection process is always given a *parameter
+environment* which contains a list of where clauses, which are
+basically obligations that can assume are satisfiable. We will iterate
+over that list and check whether our current obligation can be found
+in that list, and if so it is considered satisfied. More precisely, we
+want to check whether there is a where-clause obligation that is for
+the same trait (or some subtrait) and for which the self types match,
+using the definition of *matching* given above.
+
+Consider this simple example:
+
+     trait A1 { ... }
+     trait A2 : A1 { ... }
+
+     trait B { ... }
+
+     fn foo<X:A2+B> { ... }
+
+Clearly we can use methods offered by `A1`, `A2`, or `B` within the
+body of `foo`. In each case, that will incur an obligation like `X :
+A1` or `X : A2`. The parameter environment will contain two
+where-clauses, `X : A2` and `X : B`. For each obligation, then, we
+search this list of where-clauses.  To resolve an obligation `X:A1`,
+we would note that `X:A2` implies that `X:A1`.
+
+### Confirmation
+
+Confirmation unifies the output type parameters of the trait with the
+values found in the obligation, possibly yielding a type error.  If we
+return to our example of the `Convert` trait from the previous
+section, confirmation is where an error would be reported, because the
+impl specified that `T` would be `uint`, but the obligation reported
+`char`. Hence the result of selection would be an error.
+
+### Selection during translation
+
+During type checking, we do not store the results of trait selection.
+We simply wish to verify that trait selection will succeed. Then
+later, at trans time, when we have all concrete types available, we
+can repeat the trait selection.  In this case, we do not consider any
+where-clauses to be in scope. We know that therefore each resolution
+will resolve to a particular impl.
+
+One interesting twist has to do with nested obligations. In general, in trans,
+we only need to do a "shallow" selection for an obligation. That is, we wish to
+identify which impl applies, but we do not (yet) need to decide how to select
+any nested obligations. Nonetheless, we *do* currently do a complete resolution,
+and that is because it can sometimes inform the results of type inference. That is,
+we do not have the full substitutions in terms of the type varibales of the impl available
+to us, so we must run trait selection to figure everything out.
+
+Here is an example:
+
+    trait Foo { ... }
+    impl<U,T:Bar<U>> Foo for Vec<T> { ... }
+
+    impl Bar<uint> for int { ... }
+
+After one shallow round of selection for an obligation like `Vec<int>
+: Foo`, we would know which impl we want, and we would know that
+`T=int`, but we do not know the type of `U`.  We must select the
+nested obligation `int : Bar<U>` to find out that `U=uint`.
+
+It would be good to only do *just as much* nested resolution as
+necessary. Currently, though, we just do a full resolution.
+
+# Higher-ranked trait bounds
+
+One of the more subtle concepts at work are *higher-ranked trait
+bounds*. An example of such a bound is `for<'a> MyTrait<&'a int>`.
+Let's walk through how selection on higher-ranked trait references
+works.
+
+## Basic matching and skolemization leaks
+
+Let's walk through the test `compile-fail/hrtb-just-for-static.rs` to see
+how it works. The test starts with the trait `Foo`:
+
+```rust
+trait Foo<X> {
+    fn foo(&self, x: X) { }
+}
+```
+
+Let's say we have a function `want_hrtb` that wants a type which
+implements `Foo<&'a int>` for any `'a`:
+
+```rust
+fn want_hrtb<T>() where T : for<'a> Foo<&'a int> { ... }
+```
+
+Now we have a struct `AnyInt` that implements `Foo<&'a int>` for any
+`'a`:
+
+```rust
+struct AnyInt;
+impl<'a> Foo<&'a int> for AnyInt { }
+```
+
+And the question is, does `AnyInt : for<'a> Foo<&'a int>`? We want the
+answer to be yes. The algorithm for figuring it out is closely related
+to the subtyping for higher-ranked types (which is described in
+`middle::infer::higher_ranked::doc`, but also in a [paper by SPJ] that
+I recommend you read).
+
+1. Skolemize the obligation.
+2. Match the impl against the skolemized obligation.
+3. Check for skolemization leaks.
+
+[paper by SPJ]: http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
+
+So let's work through our example. The first thing we would do is to
+skolemize the obligation, yielding `AnyInt : Foo<&'0 int>` (here `'0`
+represents skolemized region #0). Note that now have no quantifiers;
+in terms of the compiler type, this changes from a `ty::PolyTraitRef`
+to a `TraitRef`. We would then create the `TraitRef` from the impl,
+using fresh variables for it's bound regions (and thus getting
+`Foo<&'$a int>`, where `'$a` is the inference variable for `'a`). Next
+we relate the two trait refs, yielding a graph with the constraint
+that `'0 == '$a`. Finally, we check for skolemization "leaks" -- a
+leak is basically any attempt to relate a skolemized region to another
+skolemized region, or to any region that pre-existed the impl match.
+The leak check is done by searching from the skolemized region to find
+the set of regions that it is related to in any way. This is called
+the "taint" set. To pass the check, that set must consist *solely* of
+itself and region variables from the impl. If the taint set includes
+any other region, then the match is a failure. In this case, the taint
+set for `'0` is `{'0, '$a}`, and hence the check will succeed.
+
+Let's consider a failure case. Imagine we also have a struct
+
+```rust
+struct StaticInt;
+impl Foo<&'static int> for StaticInt;
+```
+
+We want the obligation `StaticInt : for<'a> Foo<&'a int>` to be
+considered unsatisfied. The check begins just as before. `'a` is
+skolemized to `'0` and the impl trait reference is instantiated to
+`Foo<&'static int>`. When we relate those two, we get a constraint
+like `'static == '0`. This means that the taint set for `'0` is `{'0,
+'static}`, which fails the leak check.
+
+## Higher-ranked trait obligations
+
+Once the basic matching is done, we get to another interesting topic:
+how to deal with impl obligations. I'll work through a simple example
+here. Imagine we have the traits `Foo` and `Bar` and an associated impl:
+
+```
+trait Foo<X> {
+    fn foo(&self, x: X) { }
+}
+
+trait Bar<X> {
+    fn bar(&self, x: X) { }
+}
+
+impl<X,F> Foo<X> for F
+    where F : Bar<X>
+{
+}
+```
+
+Now let's say we have a obligation `for<'a> Foo<&'a int>` and we match
+this impl. What obligation is generated as a result? We want to get
+`for<'a> Bar<&'a int>`, but how does that happen?
+
+After the matching, we are in a position where we have a skolemized
+substitution like `X => &'0 int`. If we apply this substitution to the
+impl obligations, we get `F : Bar<&'0 int>`. Obviously this is not
+directly usable because the skolemized region `'0` cannot leak out of
+our computation.
+
+What we do is to create an inverse mapping from the taint set of `'0`
+back to the original bound region (`'a`, here) that `'0` resulted
+from. (This is done in `higher_ranked::plug_leaks`). We know that the
+leak check passed, so this taint set consists solely of the skolemized
+region itself plus various intermediate region variables. We then walk
+the trait-reference and convert every region in that taint set back to
+a late-bound region, so in this case we'd wind up with `for<'a> F :
+Bar<&'a int>`.
+
+# Caching and subtle considerations therewith
+
+In general we attempt to cache the results of trait selection.  This
+is a somewhat complex process. Part of the reason for this is that we
+want to be able to cache results even when all the types in the trait
+reference are not fully known. In that case, it may happen that the
+trait selection process is also influencing type variables, so we have
+to be able to not only cache the *result* of the selection process,
+but *replay* its effects on the type variables.
+
+## An example
+
+The high-level idea of how the cache works is that we first replace
+all unbound inference variables with skolemized versions. Therefore,
+if we had a trait reference `uint : Foo<$1>`, where `$n` is an unbound
+inference variable, we might replace it with `uint : Foo<%0>`, where
+`%n` is a skolemized type. We would then look this up in the cache.
+If we found a hit, the hit would tell us the immediate next step to
+take in the selection process: i.e., apply impl #22, or apply where
+clause `X : Foo<Y>`. Let's say in this case there is no hit.
+Therefore, we search through impls and where clauses and so forth, and
+we come to the conclusion that the only possible impl is this one,
+with def-id 22:
+
+    impl Foo<int> for uint { ... } // Impl #22
+
+We would then record in the cache `uint : Foo<%0> ==>
+ImplCandidate(22)`. Next we would confirm `ImplCandidate(22)`, which
+would (as a side-effect) unify `$1` with `int`.
+
+Now, at some later time, we might come along and see a `uint :
+Foo<$3>`.  When skolemized, this would yield `uint : Foo<%0>`, just as
+before, and hence the cache lookup would succeed, yielding
+`ImplCandidate(22)`. We would confirm `ImplCandidate(22)` which would
+(as a side-effect) unify `$3` with `int`.
+
+## Where clauses and the local vs global cache
+
+One subtle interaction is that the results of trait lookup will vary
+depending on what where clauses are in scope. Therefore, we actually
+have *two* caches, a local and a global cache. The local cache is
+attached to the `ParameterEnvironment` and the global cache attached
+to the `tcx`. We use the local cache whenever the result might depend
+on the where clauses that are in scope. The determination of which
+cache to use is done by the method `pick_candidate_cache` in
+`select.rs`.
+
+There are two cases where we currently use the local cache. The
+current rules are probably more conservative than necessary.
+
+### Trait references that involve parameter types
+
+The most obvious case where you need the local environment is
+when the trait reference includes parameter types. For example,
+consider the following function:
+
+    impl<T> Vec<T> {
+        fn foo(x: T)
+            where T : Foo
+        { ... }
+
+        fn bar(x: T)
+        { ... }
+    }
+
+If there is an obligation `T : Foo`, or `int : Bar<T>`, or whatever,
+clearly the results from `foo` and `bar` are potentially different,
+since the set of where clauses in scope are different.
+
+### Trait references with unbound variables when where clauses are in scope
+
+There is another less obvious interaction which involves unbound variables
+where *only* where clauses are in scope (no impls). This manifested as
+issue #18209 (`run-pass/trait-cache-issue-18209.rs`). Consider
+this snippet:
+
+```
+pub trait Foo {
+    fn load_from() -> Box<Self>;
+    fn load() -> Box<Self> {
+        Foo::load_from()
+    }
+}
+```
+
+The default method will incur an obligation `$0 : Foo` from the call
+to `load_from`. If there are no impls, this can be eagerly resolved to
+`VtableParam(Self : Foo)` and cached. Because the trait reference
+doesn't involve any parameters types (only the resolution does), this
+result was stored in the global cache, causing later calls to
+`Foo::load_from()` to get nonsense.
+
+To fix this, we always use the local cache if there are unbound
+variables and where clauses in scope. This is more conservative than
+necessary as far as I can tell. However, it still seems to be a simple
+rule and I observe ~99% hit rate on rustc, so it doesn't seem to hurt
+us in particular.
+
+Here is an example of the kind of subtle case that I would be worried
+about with a more complex rule (although this particular case works
+out ok). Imagine the trait reference doesn't directly reference a
+where clause, but the where clause plays a role in the winnowing
+phase. Something like this:
+
+```
+pub trait Foo<T> { ... }
+pub trait Bar { ... }
+impl<U,T:Bar> Foo<U> for T { ... } // Impl A
+impl Foo<char> for uint { ... }    // Impl B
+```
+
+Now, in some function, we have no where clauses in scope, and we have
+an obligation `$1 : Foo<$0>`. We might then conclude that `$0=char`
+and `$1=uint`: this is because for impl A to apply, `uint:Bar` would
+have to hold, and we know it does not or else the coherence check
+would have failed.  So we might enter into our global cache: `$1 :
+Foo<$0> => Impl B`.  Then we come along in a different scope, where a
+generic type `A` is around with the bound `A:Bar`. Now suddenly the
+impl is viable.
+
+The flaw in this imaginary DOOMSDAY SCENARIO is that we would not
+currently conclude that `$1 : Foo<$0>` implies that `$0 == uint` and
+`$1 == char`, even though it is true that (absent type parameters)
+there is no other type the user could enter. However, it is not
+*completely* implausible that we *could* draw this conclusion in the
+future; we wouldn't have to guess types, in particular, we could be
+led by the impls.
+
+*/
diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs
index 412c188f5f4..213d97b4b34 100644
--- a/src/librustc/middle/traits/fulfill.rs
+++ b/src/librustc/middle/traits/fulfill.rs
@@ -8,9 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use middle::infer::InferCtxt;
 use middle::mem_categorization::Typer;
 use middle::ty::{mod, Ty};
-use middle::infer::{mod, InferCtxt};
 use std::collections::HashSet;
 use std::collections::hash_map::{Occupied, Vacant};
 use std::default::Default;
@@ -28,7 +28,7 @@ use super::ObligationCause;
 use super::PredicateObligation;
 use super::Selection;
 use super::select::SelectionContext;
-use super::trait_ref_for_builtin_bound;
+use super::poly_trait_ref_for_builtin_bound;
 use super::Unimplemented;
 
 /// The fulfillment context is used to drive trait resolution.  It
@@ -107,7 +107,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
                                   builtin_bound: ty::BuiltinBound,
                                   cause: ObligationCause<'tcx>)
     {
-        match trait_ref_for_builtin_bound(tcx, builtin_bound, ty) {
+        match poly_trait_ref_for_builtin_bound(tcx, builtin_bound, ty) {
             Ok(trait_ref) => {
                 self.register_trait_ref(tcx, trait_ref, cause);
             }
@@ -117,7 +117,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
 
     pub fn register_trait_ref<'a>(&mut self,
                                   tcx: &ty::ctxt<'tcx>,
-                                  trait_ref: Rc<ty::TraitRef<'tcx>>,
+                                  trait_ref: Rc<ty::PolyTraitRef<'tcx>>,
                                   cause: ObligationCause<'tcx>)
     {
         /*!
@@ -329,30 +329,47 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
             }
         }
 
-        ty::Predicate::Equate(a, b) => {
-            let origin = infer::EquatePredicate(predicate.cause.span);
-            match infer::mk_eqty(selcx.infcx(), false, origin, a, b) {
-                Ok(()) => {
-                    true
-                }
+        ty::Predicate::Equate(ref binder) => {
+            match selcx.infcx().equality_predicate(predicate.cause.span, binder) {
+                Ok(()) => { }
                 Err(_) => {
                     errors.push(
                         FulfillmentError::new(
                             predicate.clone(),
                             CodeSelectionError(Unimplemented)));
-                    true
                 }
             }
+            true
         }
 
-        ty::Predicate::RegionOutlives(r_a, r_b) => {
-            let origin = infer::RelateRegionParamBound(predicate.cause.span);
-            let () = infer::mk_subr(selcx.infcx(), origin, r_b, r_a); // `b : a` ==> `a <= b`
+        ty::Predicate::RegionOutlives(ref binder) => {
+            match selcx.infcx().region_outlives_predicate(predicate.cause.span, binder) {
+                Ok(()) => { }
+                Err(_) => {
+                    errors.push(
+                        FulfillmentError::new(
+                            predicate.clone(),
+                            CodeSelectionError(Unimplemented)));
+                }
+            }
+
             true
         }
 
-        ty::Predicate::TypeOutlives(t_a, r_b) => {
-            register_region_obligation(tcx, t_a, r_b, predicate.cause, region_obligations);
+        ty::Predicate::TypeOutlives(ref binder) => {
+            // For now, we just check that there are no higher-ranked
+            // regions.  If there are, we will call this obligation an
+            // error. Eventually we should be able to support some
+            // cases here, I imagine (e.g., `for<'a> int : 'a`).
+            if ty::count_late_bound_regions(selcx.tcx(), binder) != 0 {
+                errors.push(
+                    FulfillmentError::new(
+                        predicate.clone(),
+                        CodeSelectionError(Unimplemented)));
+            } else {
+                let ty::OutlivesPredicate(t_a, r_b) = binder.0;
+                register_region_obligation(tcx, t_a, r_b, predicate.cause, region_obligations);
+            }
             true
         }
     }
@@ -385,3 +402,4 @@ fn register_region_obligation<'tcx>(tcx: &ty::ctxt<'tcx>,
     }
 
 }
+
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index 936304c5eb4..3289acd0c2e 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -33,7 +33,7 @@ pub use self::util::supertraits;
 pub use self::util::Supertraits;
 pub use self::util::search_trait_and_supertraits_from_bound;
 pub use self::util::transitive_bounds;
-pub use self::util::trait_ref_for_builtin_bound;
+pub use self::util::poly_trait_ref_for_builtin_bound;
 
 mod coherence;
 mod fulfill;
@@ -54,7 +54,7 @@ pub struct Obligation<'tcx, T> {
 }
 
 pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
-pub type TraitObligation<'tcx> = Obligation<'tcx, Rc<ty::TraitRef<'tcx>>>;
+pub type TraitObligation<'tcx> = Obligation<'tcx, Rc<ty::PolyTraitRef<'tcx>>>;
 
 /// Why did we incur this obligation? Used for error reporting.
 #[deriving(Copy, Clone)]
@@ -115,7 +115,9 @@ pub type Selection<'tcx> = Vtable<'tcx, PredicateObligation<'tcx>>;
 pub enum SelectionError<'tcx> {
     Unimplemented,
     Overflow,
-    OutputTypeParameterMismatch(Rc<ty::TraitRef<'tcx>>, Rc<ty::TraitRef<'tcx>>, ty::type_err<'tcx>),
+    OutputTypeParameterMismatch(Rc<ty::PolyTraitRef<'tcx>>,
+                                Rc<ty::PolyTraitRef<'tcx>>,
+                                ty::type_err<'tcx>),
 }
 
 pub struct FulfillmentError<'tcx> {
@@ -226,7 +228,7 @@ pub struct VtableBuiltinData<N> {
 #[deriving(PartialEq,Eq,Clone)]
 pub struct VtableParamData<'tcx> {
     // In the above example, this would `Eq`
-    pub bound: Rc<ty::TraitRef<'tcx>>,
+    pub bound: Rc<ty::PolyTraitRef<'tcx>>,
 }
 
 /// True if neither the trait nor self type is local. Note that `impl_def_id` must refer to an impl
@@ -278,7 +280,7 @@ impl<'tcx,O> Obligation<'tcx,O> {
     }
 }
 
-impl<'tcx> Obligation<'tcx,Rc<ty::TraitRef<'tcx>>> {
+impl<'tcx> TraitObligation<'tcx> {
     pub fn self_ty(&self) -> Ty<'tcx> {
         self.trait_ref.self_ty()
     }
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index f551ff06165..6c7ae666ae0 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -28,9 +28,9 @@ use super::{util};
 use middle::fast_reject;
 use middle::mem_categorization::Typer;
 use middle::subst::{Subst, Substs, VecPerParamSpace};
-use middle::ty::{mod, Ty};
+use middle::ty::{mod, AsPredicate, RegionEscape, Ty};
 use middle::infer;
-use middle::infer::{InferCtxt, TypeSkolemizer};
+use middle::infer::{InferCtxt, TypeFreshener};
 use middle::ty_fold::TypeFoldable;
 use std::cell::RefCell;
 use std::collections::hash_map::HashMap;
@@ -44,12 +44,12 @@ pub struct SelectionContext<'cx, 'tcx:'cx> {
     param_env: &'cx ty::ParameterEnvironment<'tcx>,
     typer: &'cx (Typer<'tcx>+'cx),
 
-    /// Skolemizer used specifically for skolemizing entries on the
+    /// Freshener used specifically for skolemizing entries on the
     /// obligation stack. This ensures that all entries on the stack
     /// at one time will have the same set of skolemized entries,
     /// which is important for checking for trait bounds that
     /// recursively require themselves.
-    skolemizer: TypeSkolemizer<'cx, 'tcx>,
+    freshener: TypeFreshener<'cx, 'tcx>,
 
     /// If true, indicates that the evaluation should be conservative
     /// and consider the possibility of types outside this crate.
@@ -73,15 +73,15 @@ struct TraitObligationStack<'prev, 'tcx: 'prev> {
     obligation: &'prev TraitObligation<'tcx>,
 
     /// Trait ref from `obligation` but skolemized with the
-    /// selection-context's skolemizer. Used to check for recursion.
-    skol_trait_ref: Rc<ty::TraitRef<'tcx>>,
+    /// selection-context's freshener. Used to check for recursion.
+    fresh_trait_ref: Rc<ty::PolyTraitRef<'tcx>>,
 
     previous: Option<&'prev TraitObligationStack<'prev, 'tcx>>
 }
 
 #[deriving(Clone)]
 pub struct SelectionCache<'tcx> {
-    hashmap: RefCell<HashMap<Rc<ty::TraitRef<'tcx>>,
+    hashmap: RefCell<HashMap<Rc<ty::PolyTraitRef<'tcx>>,
                              SelectionResult<'tcx, Candidate<'tcx>>>>,
 }
 
@@ -172,7 +172,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             infcx: infcx,
             param_env: param_env,
             typer: typer,
-            skolemizer: infcx.skolemizer(),
+            freshener: infcx.freshener(),
             intercrate: false,
         }
     }
@@ -185,7 +185,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             infcx: infcx,
             param_env: param_env,
             typer: typer,
-            skolemizer: infcx.skolemizer(),
+            freshener: infcx.freshener(),
             intercrate: true,
         }
     }
@@ -288,8 +288,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 self.evaluate_obligation_recursively(previous_stack, &obligation)
             }
 
-            ty::Predicate::Equate(a, b) => {
-                match infer::can_mk_eqty(self.infcx, a, b) {
+            ty::Predicate::Equate(ref p) => {
+                let result = self.infcx.probe(|_| {
+                    self.infcx.equality_predicate(obligation.cause.span, p)
+                });
+                match result {
                     Ok(()) => EvaluatedToOk,
                     Err(_) => EvaluatedToErr(Unimplemented),
                 }
@@ -347,16 +350,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // This suffices to allow chains like `FnMut` implemented in
         // terms of `Fn` etc, but we could probably make this more
         // precise still.
-        let input_types = stack.skol_trait_ref.input_types();
-        let unbound_input_types = input_types.iter().any(|&t| ty::type_is_skolemized(t));
+        let input_types = stack.fresh_trait_ref.0.input_types();
+        let unbound_input_types = input_types.iter().any(|&t| ty::type_is_fresh(t));
         if
             unbound_input_types &&
              (self.intercrate ||
               stack.iter().skip(1).any(
-                  |prev| stack.skol_trait_ref.def_id == prev.skol_trait_ref.def_id))
+                  |prev| stack.fresh_trait_ref.def_id() == prev.fresh_trait_ref.def_id()))
         {
             debug!("evaluate_stack({}) --> unbound argument, recursion -->  ambiguous",
-                   stack.skol_trait_ref.repr(self.tcx()));
+                   stack.fresh_trait_ref.repr(self.tcx()));
             return EvaluatedToAmbig;
         }
 
@@ -373,19 +376,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // `Option<Box<List<T>>>` is `Send` if `Box<List<T>>` is
         // `Send`.
         //
-        // Note that we do this comparison using the `skol_trait_ref`
+        // Note that we do this comparison using the `fresh_trait_ref`
         // fields. Because these have all been skolemized using
-        // `self.skolemizer`, we can be sure that (a) this will not
+        // `self.freshener`, we can be sure that (a) this will not
         // affect the inferencer state and (b) that if we see two
         // skolemized types with the same index, they refer to the
         // same unbound type variable.
         if
             stack.iter()
             .skip(1) // skip top-most frame
-            .any(|prev| stack.skol_trait_ref == prev.skol_trait_ref)
+            .any(|prev| stack.fresh_trait_ref == prev.fresh_trait_ref)
         {
             debug!("evaluate_stack({}) --> recursive",
-                   stack.skol_trait_ref.repr(self.tcx()));
+                   stack.fresh_trait_ref.repr(self.tcx()));
             return EvaluatedToOk;
         }
 
@@ -407,13 +410,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                impl_def_id.repr(self.tcx()),
                obligation.repr(self.tcx()));
 
-        self.infcx.probe(|| {
-            match self.match_impl(impl_def_id, obligation) {
+        self.infcx.probe(|snapshot| {
+            let (skol_obligation_trait_ref, skol_map) =
+                self.infcx().skolemize_late_bound_regions(&*obligation.trait_ref, snapshot);
+            match self.match_impl(impl_def_id, obligation, snapshot,
+                                  &skol_map, Rc::new(skol_obligation_trait_ref)) {
                 Ok(substs) => {
                     let vtable_impl = self.vtable_impl(impl_def_id,
                                                        substs,
                                                        obligation.cause,
-                                                       obligation.recursion_depth + 1);
+                                                       obligation.recursion_depth + 1,
+                                                       skol_map,
+                                                       snapshot);
                     self.winnow_selection(None, VtableImpl(vtable_impl)).may_apply()
                 }
                 Err(()) => {
@@ -445,20 +453,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         // Check the cache. Note that we skolemize the trait-ref
-        // separately rather than using `stack.skol_trait_ref` -- this
+        // separately rather than using `stack.fresh_trait_ref` -- this
         // is because we want the unbound variables to be replaced
         // with fresh skolemized types starting from index 0.
-        let cache_skol_trait_ref =
-            self.infcx.skolemize(stack.obligation.trait_ref.clone());
-        debug!("candidate_from_obligation(cache_skol_trait_ref={}, obligation={})",
-               cache_skol_trait_ref.repr(self.tcx()),
+        let cache_fresh_trait_ref =
+            self.infcx.freshen(stack.obligation.trait_ref.clone());
+        debug!("candidate_from_obligation(cache_fresh_trait_ref={}, obligation={})",
+               cache_fresh_trait_ref.repr(self.tcx()),
                stack.repr(self.tcx()));
         assert!(!stack.obligation.trait_ref.has_escaping_regions());
 
-        match self.check_candidate_cache(cache_skol_trait_ref.clone()) {
+        match self.check_candidate_cache(cache_fresh_trait_ref.clone()) {
             Some(c) => {
-                debug!("CACHE HIT: cache_skol_trait_ref={}, candidate={}",
-                       cache_skol_trait_ref.repr(self.tcx()),
+                debug!("CACHE HIT: cache_fresh_trait_ref={}, candidate={}",
+                       cache_fresh_trait_ref.repr(self.tcx()),
                        c.repr(self.tcx()));
                 return c;
             }
@@ -467,9 +475,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         // If no match, compute result and insert into cache.
         let candidate = self.candidate_from_obligation_no_cache(stack);
-        debug!("CACHE MISS: cache_skol_trait_ref={}, candidate={}",
-               cache_skol_trait_ref.repr(self.tcx()), candidate.repr(self.tcx()));
-        self.insert_candidate_cache(cache_skol_trait_ref, candidate.clone());
+        debug!("CACHE MISS: cache_fresh_trait_ref={}, candidate={}",
+               cache_fresh_trait_ref.repr(self.tcx()), candidate.repr(self.tcx()));
+        self.insert_candidate_cache(cache_fresh_trait_ref, candidate.clone());
         candidate
     }
 
@@ -569,7 +577,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     }
 
     fn pick_candidate_cache(&self,
-                            cache_skol_trait_ref: &Rc<ty::TraitRef<'tcx>>)
+                            cache_fresh_trait_ref: &Rc<ty::PolyTraitRef<'tcx>>)
                             -> &SelectionCache<'tcx>
     {
         // High-level idea: we have to decide whether to consult the
@@ -591,7 +599,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // If the trait refers to any parameters in scope, then use
         // the cache of the param-environment.
         if
-            cache_skol_trait_ref.input_types().iter().any(
+            cache_fresh_trait_ref.0.input_types().iter().any(
                 |&t| ty::type_has_self(t) || ty::type_has_params(t))
         {
             return &self.param_env.selection_cache;
@@ -604,7 +612,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // See the discussion in doc.rs for more details.
         if
             !self.param_env.caller_bounds.is_empty() &&
-            cache_skol_trait_ref.input_types().iter().any(
+            cache_fresh_trait_ref.0.input_types().iter().any(
                 |&t| ty::type_has_ty_infer(t))
         {
             return &self.param_env.selection_cache;
@@ -615,21 +623,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     }
 
     fn check_candidate_cache(&mut self,
-                             cache_skol_trait_ref: Rc<ty::TraitRef<'tcx>>)
+                             cache_fresh_trait_ref: Rc<ty::PolyTraitRef<'tcx>>)
                              -> Option<SelectionResult<'tcx, Candidate<'tcx>>>
     {
-        let cache = self.pick_candidate_cache(&cache_skol_trait_ref);
+        let cache = self.pick_candidate_cache(&cache_fresh_trait_ref);
         let hashmap = cache.hashmap.borrow();
-        hashmap.get(&cache_skol_trait_ref).map(|c| (*c).clone())
+        hashmap.get(&cache_fresh_trait_ref).map(|c| (*c).clone())
     }
 
     fn insert_candidate_cache(&mut self,
-                              cache_skol_trait_ref: Rc<ty::TraitRef<'tcx>>,
+                              cache_fresh_trait_ref: Rc<ty::PolyTraitRef<'tcx>>,
                               candidate: SelectionResult<'tcx, Candidate<'tcx>>)
     {
-        let cache = self.pick_candidate_cache(&cache_skol_trait_ref);
+        let cache = self.pick_candidate_cache(&cache_fresh_trait_ref);
         let mut hashmap = cache.hashmap.borrow_mut();
-        hashmap.insert(cache_skol_trait_ref, candidate);
+        hashmap.insert(cache_fresh_trait_ref, candidate);
     }
 
     fn assemble_candidates<'o>(&mut self,
@@ -648,7 +656,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // Other bounds. Consider both in-scope bounds from fn decl
         // and applicable impls. There is a certain set of precedence rules here.
 
-        match self.tcx().lang_items.to_builtin_kind(obligation.trait_ref.def_id) {
+        match self.tcx().lang_items.to_builtin_kind(obligation.trait_ref.def_id()) {
             Some(ty::BoundCopy) => {
                 debug!("obligation self ty is {}",
                        obligation.self_ty().repr(self.tcx()));
@@ -696,7 +704,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         debug!("assemble_candidates_from_caller_bounds({})",
                obligation.repr(self.tcx()));
 
-        let caller_trait_refs: Vec<Rc<ty::TraitRef>> =
+        let caller_trait_refs: Vec<_> =
             self.param_env.caller_bounds.predicates.iter()
             .filter_map(|o| o.to_trait())
             .collect();
@@ -708,8 +716,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let matching_bounds =
             all_bounds.filter(
                 |bound| self.infcx.probe(
-                    || self.match_trait_refs(obligation,
-                                             (*bound).clone())).is_ok());
+                    |_| self.match_where_clause(obligation, bound.clone())).is_ok());
 
         let param_candidates =
             matching_bounds.map(
@@ -731,7 +738,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                                            candidates: &mut CandidateSet<'tcx>)
                                            -> Result<(),SelectionError<'tcx>>
     {
-        let kind = match self.fn_family_trait_kind(obligation.trait_ref.def_id) {
+        let kind = match self.fn_family_trait_kind(obligation.trait_ref.def_id()) {
             Some(k) => k,
             None => { return Ok(()); }
         };
@@ -779,7 +786,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // We provide a `Fn` impl for fn pointers. There is no need to provide
         // the other traits (e.g. `FnMut`) since those are provided by blanket
         // impls.
-        if Some(obligation.trait_ref.def_id) != self.tcx().lang_items.fn_trait() {
+        if Some(obligation.trait_ref.def_id()) != self.tcx().lang_items.fn_trait() {
             return Ok(());
         }
 
@@ -793,11 +800,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             ty::ty_bare_fn(ty::BareFnTy {
                 unsafety: ast::Unsafety::Normal,
                 abi: abi::Rust,
-                sig: ty::FnSig {
+                sig: ty::Binder(ty::FnSig {
                     inputs: _,
                     output: ty::FnConverging(_),
                     variadic: false
-                }
+                })
             }) => {
                 candidates.vec.push(FnPointerCandidate);
             }
@@ -814,10 +821,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                                       candidates: &mut CandidateSet<'tcx>)
                                       -> Result<(), SelectionError<'tcx>>
     {
-        let all_impls = self.all_impls(obligation.trait_ref.def_id);
+        let all_impls = self.all_impls(obligation.trait_ref.def_id());
         for &impl_def_id in all_impls.iter() {
-            self.infcx.probe(|| {
-                match self.match_impl(impl_def_id, obligation) {
+            self.infcx.probe(|snapshot| {
+                let (skol_obligation_trait_ref, skol_map) =
+                    self.infcx().skolemize_late_bound_regions(&*obligation.trait_ref, snapshot);
+                match self.match_impl(impl_def_id, obligation, snapshot,
+                                      &skol_map, Rc::new(skol_obligation_trait_ref)) {
                     Ok(_) => {
                         candidates.vec.push(ImplCandidate(impl_def_id));
                     }
@@ -845,15 +855,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             candidate: &Candidate<'tcx>)
                             -> EvaluationResult<'tcx>
     {
-        /*!
-         * Further evaluate `candidate` to decide whether all type parameters match
-         * and whether nested obligations are met. Returns true if `candidate` remains
-         * viable after this further scrutiny.
-         */
-
-        debug!("winnow_candidate: depth={} candidate={}",
-               stack.obligation.recursion_depth, candidate.repr(self.tcx()));
-        let result = self.infcx.probe(|| {
+        debug!("winnow_candidate: candidate={}", candidate.repr(self.tcx()));
+        let result = self.infcx.probe(|_| {
             let candidate = (*candidate).clone();
             match self.confirm_candidate(stack.obligation, candidate) {
                 Ok(selection) => self.winnow_selection(Some(stack), selection),
@@ -916,18 +919,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                        candidate_i.repr(self.tcx()),
                        candidate_j.repr(self.tcx()));
 
-                self.infcx.probe(|| {
+                self.infcx.probe(|snapshot| {
+                    let (skol_obligation_trait_ref, skol_map) =
+                        self.infcx().skolemize_late_bound_regions(
+                            &*stack.obligation.trait_ref, snapshot);
                     let impl_substs =
-                        self.rematch_impl(impl_def_id, stack.obligation);
+                        self.rematch_impl(impl_def_id, stack.obligation, snapshot,
+                                          &skol_map, Rc::new(skol_obligation_trait_ref));
                     let impl_trait_ref =
                         ty::impl_trait_ref(self.tcx(), impl_def_id).unwrap();
                     let impl_trait_ref =
                         impl_trait_ref.subst(self.tcx(), &impl_substs);
+                    let poly_impl_trait_ref =
+                        Rc::new(ty::Binder((*impl_trait_ref).clone()));
                     let origin =
                         infer::RelateOutputImplTypes(stack.obligation.cause.span);
                     self.infcx
-                        .sub_trait_refs(false, origin,
-                                        impl_trait_ref, vt.bound.clone())
+                        .sub_poly_trait_refs(false, origin, poly_impl_trait_ref, vt.bound.clone())
                         .is_ok()
                 })
             }
@@ -1071,26 +1079,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 }
             }
 
-            ty::ty_trait(box ty::TyTrait { ref principal, bounds }) => {
+            ty::ty_trait(ref data) => {
                 match bound {
                     ty::BoundSized => {
                         Err(Unimplemented)
                     }
                     ty::BoundCopy | ty::BoundSync | ty::BoundSend => {
-                        if bounds.builtin_bounds.contains(&bound) {
+                        if data.bounds.builtin_bounds.contains(&bound) {
                             Ok(If(Vec::new()))
                         } else {
                             // Recursively check all supertraits to find out if any further
                             // bounds are required and thus we must fulfill.
-                            // We have to create a temp trait ref here since TyTraits don't
-                            // have actual self type info (which is required for the
-                            // supertraits iterator).
-                            let tmp_tr = Rc::new(ty::TraitRef {
-                                def_id: principal.def_id,
-                                substs: principal.substs.with_self_ty(ty::mk_err())
-                            });
+                            let tmp_tr = data.principal_trait_ref_with_self_ty(ty::mk_err());
                             for tr in util::supertraits(self.tcx(), tmp_tr) {
-                                let td = ty::lookup_trait_def(self.tcx(), tr.def_id);
+                                let td = ty::lookup_trait_def(self.tcx(), tr.def_id());
 
                                 if td.bounds.builtin_bounds.contains(&bound) {
                                     return Ok(If(Vec::new()))
@@ -1276,8 +1278,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
 
             ty::ty_open(_) |
-            ty::ty_infer(ty::SkolemizedTy(_)) |
-            ty::ty_infer(ty::SkolemizedIntTy(_)) => {
+            ty::ty_infer(ty::FreshTy(_)) |
+            ty::ty_infer(ty::FreshIntTy(_)) => {
                 self.tcx().sess.bug(
                     format!(
                         "asked to assemble builtin bounds of unexpected type: {}",
@@ -1404,10 +1406,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                obligation.repr(self.tcx()),
                param.repr(self.tcx()));
 
-        let () = try!(self.confirm(obligation.cause,
-                                   obligation.trait_ref.clone(),
-                                   param.bound.clone()));
-        Ok(param)
+        // During evaluation, we already checked that this
+        // where-clause trait-ref could be unified with the obligation
+        // trait-ref. Repeat that unification now without any
+        // transactional boundary; it should not fail.
+        match self.confirm_poly_trait_refs(obligation.cause,
+                                           obligation.trait_ref.clone(),
+                                           param.bound.clone()) {
+            Ok(()) => Ok(param),
+            Err(_) => {
+                self.tcx().sess.bug(
+                    format!("Where clause `{}` was applicable to `{}` but now is not",
+                            param.bound.repr(self.tcx()),
+                            obligation.repr(self.tcx())).as_slice());
+            }
+        }
     }
 
     fn confirm_builtin_candidate(&mut self,
@@ -1454,8 +1467,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             obligations.push(Obligation {
                 cause: obligation.cause,
                 recursion_depth: obligation.recursion_depth+1,
-                trait_ref: ty::Predicate::TypeOutlives(obligation.self_ty(),
-                                                       ty::ReStatic)
+                trait_ref: ty::Binder(ty::OutlivesPredicate(obligation.self_ty(),
+                                                            ty::ReStatic)).as_predicate(),
             });
         }
 
@@ -1480,23 +1493,44 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         // First, create the substitutions by matching the impl again,
         // this time not in a probe.
-        let substs = self.rematch_impl(impl_def_id, obligation);
-        debug!("confirm_impl_candidate substs={}", substs);
-        Ok(self.vtable_impl(impl_def_id, substs, obligation.cause, obligation.recursion_depth + 1))
+        self.infcx.try(|snapshot| {
+            let (skol_obligation_trait_ref, skol_map) =
+                self.infcx().skolemize_late_bound_regions(&*obligation.trait_ref, snapshot);
+            let substs = self.rematch_impl(impl_def_id, obligation,
+                                           snapshot, &skol_map, Rc::new(skol_obligation_trait_ref));
+            debug!("confirm_impl_candidate substs={}", substs);
+            Ok(self.vtable_impl(impl_def_id, substs, obligation.cause,
+                                obligation.recursion_depth + 1, skol_map, snapshot))
+        })
     }
 
     fn vtable_impl(&mut self,
                    impl_def_id: ast::DefId,
                    substs: Substs<'tcx>,
                    cause: ObligationCause<'tcx>,
-                   recursion_depth: uint)
+                   recursion_depth: uint,
+                   skol_map: infer::SkolemizationMap,
+                   snapshot: &infer::CombinedSnapshot)
                    -> VtableImplData<'tcx, PredicateObligation<'tcx>>
     {
+        debug!("vtable_impl(impl_def_id={}, substs={}, recursion_depth={}, skol_map={})",
+               impl_def_id.repr(self.tcx()),
+               substs.repr(self.tcx()),
+               recursion_depth,
+               skol_map.repr(self.tcx()));
+
         let impl_predicates =
             self.impl_predicates(cause,
                                  recursion_depth,
                                  impl_def_id,
-                                 &substs);
+                                 &substs,
+                                 skol_map,
+                                 snapshot);
+
+        debug!("vtable_impl: impl_def_id={} impl_predicates={}",
+               impl_def_id.repr(self.tcx()),
+               impl_predicates.repr(self.tcx()));
+
         VtableImplData { impl_def_id: impl_def_id,
                          substs: substs,
                          nested: impl_predicates }
@@ -1526,23 +1560,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
         };
 
-        let arguments_tuple = ty::mk_tup(self.tcx(), sig.inputs.to_vec());
-        let output_type = sig.output.unwrap();
+        let arguments_tuple = ty::mk_tup(self.tcx(), sig.0.inputs.to_vec());
+        let output_type = sig.0.output.unwrap();
         let substs =
             Substs::new_trait(
                 vec![arguments_tuple, output_type],
                 vec![],
                 vec![],
                 self_ty);
-        let trait_ref = Rc::new(ty::TraitRef {
-            def_id: obligation.trait_ref.def_id,
+        let trait_ref = Rc::new(ty::Binder(ty::TraitRef {
+            def_id: obligation.trait_ref.def_id(),
             substs: substs,
-        });
+        }));
 
-        let () =
-            try!(self.confirm(obligation.cause,
-                              obligation.trait_ref.clone(),
-                              trait_ref));
+        try!(self.confirm_poly_trait_refs(obligation.cause,
+                                          obligation.trait_ref.clone(),
+                                          trait_ref));
 
         Ok(self_ty)
     }
@@ -1569,26 +1602,69 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         };
 
         let closure_sig = &closure_type.sig;
-        let arguments_tuple = closure_sig.inputs[0];
+        let arguments_tuple = closure_sig.0.inputs[0];
         let substs =
             Substs::new_trait(
                 vec![arguments_tuple.subst(self.tcx(), substs),
-                     closure_sig.output.unwrap().subst(self.tcx(), substs)],
+                     closure_sig.0.output.unwrap().subst(self.tcx(), substs)],
                 vec![],
                 vec![],
                 obligation.self_ty());
-        let trait_ref = Rc::new(ty::TraitRef {
-            def_id: obligation.trait_ref.def_id,
+        let trait_ref = Rc::new(ty::Binder(ty::TraitRef {
+            def_id: obligation.trait_ref.def_id(),
             substs: substs,
-        });
+        }));
 
         debug!("confirm_unboxed_closure_candidate(closure_def_id={}, trait_ref={})",
                closure_def_id.repr(self.tcx()),
                trait_ref.repr(self.tcx()));
 
-        self.confirm(obligation.cause,
-                     obligation.trait_ref.clone(),
-                     trait_ref)
+        self.confirm_poly_trait_refs(obligation.cause,
+                                     obligation.trait_ref.clone(),
+                                     trait_ref)
+    }
+
+    /// In the case of unboxed closure types and fn pointers,
+    /// we currently treat the input type parameters on the trait as
+    /// outputs. This means that when we have a match we have only
+    /// considered the self type, so we have to go back and make sure
+    /// to relate the argument types too.  This is kind of wrong, but
+    /// since we control the full set of impls, also not that wrong,
+    /// and it DOES yield better error messages (since we don't report
+    /// errors as if there is no applicable impl, but rather report
+    /// errors are about mismatched argument types.
+    ///
+    /// Here is an example. Imagine we have an unboxed closure expression
+    /// and we desugared it so that the type of the expression is
+    /// `Closure`, and `Closure` expects an int as argument. Then it
+    /// is "as if" the compiler generated this impl:
+    ///
+    ///     impl Fn(int) for Closure { ... }
+    ///
+    /// Now imagine our obligation is `Fn(uint) for Closure`. So far
+    /// we have matched the self-type `Closure`. At this point we'll
+    /// compare the `int` to `uint` and generate an error.
+    ///
+    /// Note that this checking occurs *after* the impl has selected,
+    /// because these output type parameters should not affect the
+    /// selection of the impl. Therefore, if there is a mismatch, we
+    /// report an error to the user.
+    fn confirm_poly_trait_refs(&mut self,
+                               obligation_cause: ObligationCause,
+                               obligation_trait_ref: Rc<ty::PolyTraitRef<'tcx>>,
+                               expected_trait_ref: Rc<ty::PolyTraitRef<'tcx>>)
+                               -> Result<(), SelectionError<'tcx>>
+    {
+        let origin = infer::RelateOutputImplTypes(obligation_cause.span);
+
+        let obligation_trait_ref = obligation_trait_ref.clone();
+        match self.infcx.sub_poly_trait_refs(false,
+                                             origin,
+                                             expected_trait_ref.clone(),
+                                             obligation_trait_ref.clone()) {
+            Ok(()) => Ok(()),
+            Err(e) => Err(OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e))
+        }
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -1603,10 +1679,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
     fn rematch_impl(&mut self,
                     impl_def_id: ast::DefId,
-                    obligation: &TraitObligation<'tcx>)
+                    obligation: &TraitObligation<'tcx>,
+                    snapshot: &infer::CombinedSnapshot,
+                    skol_map: &infer::SkolemizationMap,
+                    skol_obligation_trait_ref: Rc<ty::TraitRef<'tcx>>)
                     -> Substs<'tcx>
     {
-        match self.match_impl(impl_def_id, obligation) {
+        match self.match_impl(impl_def_id, obligation, snapshot,
+                              skol_map, skol_obligation_trait_ref) {
             Ok(substs) => {
                 substs
             }
@@ -1622,11 +1702,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
     fn match_impl(&mut self,
                   impl_def_id: ast::DefId,
-                  obligation: &TraitObligation<'tcx>)
+                  obligation: &TraitObligation<'tcx>,
+                  snapshot: &infer::CombinedSnapshot,
+                  skol_map: &infer::SkolemizationMap,
+                  skol_obligation_trait_ref: Rc<ty::TraitRef<'tcx>>)
                   -> Result<Substs<'tcx>, ()>
     {
-        let impl_trait_ref = ty::impl_trait_ref(self.tcx(),
-                                                impl_def_id).unwrap();
+        let impl_trait_ref = ty::impl_trait_ref(self.tcx(), impl_def_id).unwrap();
 
         // Before we create the substitutions and everything, first
         // consider a "quick reject". This avoids creating more types
@@ -1642,10 +1724,37 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let impl_trait_ref = impl_trait_ref.subst(self.tcx(),
                                                   &impl_substs);
 
-        match self.match_trait_refs(obligation, impl_trait_ref) {
-            Ok(()) => Ok(impl_substs),
-            Err(()) => Err(())
+        debug!("match_impl(impl_def_id={}, obligation={}, \
+               impl_trait_ref={}, skol_obligation_trait_ref={})",
+               impl_def_id.repr(self.tcx()),
+               obligation.repr(self.tcx()),
+               impl_trait_ref.repr(self.tcx()),
+               skol_obligation_trait_ref.repr(self.tcx()));
+
+        let origin = infer::RelateOutputImplTypes(obligation.cause.span);
+        match self.infcx.sub_trait_refs(false,
+                                        origin,
+                                        impl_trait_ref,
+                                        skol_obligation_trait_ref) {
+            Ok(()) => { }
+            Err(e) => {
+                debug!("match_impl: failed sub_trait_refs due to `{}`",
+                       ty::type_err_to_str(self.tcx(), &e));
+                return Err(());
+            }
+        }
+
+        match self.infcx.leak_check(skol_map, snapshot) {
+            Ok(()) => { }
+            Err(e) => {
+                debug!("match_impl: failed leak check due to `{}`",
+                       ty::type_err_to_str(self.tcx(), &e));
+                return Err(());
+            }
         }
+
+        debug!("match_impl: success impl_substs={}", impl_substs.repr(self.tcx()));
+        Ok(impl_substs)
     }
 
     fn fast_reject_trait_refs(&mut self,
@@ -1671,20 +1780,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             })
     }
 
-    fn match_trait_refs(&mut self,
-                        obligation: &TraitObligation<'tcx>,
-                        trait_ref: Rc<ty::TraitRef<'tcx>>)
+    fn match_where_clause(&mut self,
+                          obligation: &TraitObligation<'tcx>,
+                          where_clause_trait_ref: Rc<ty::PolyTraitRef<'tcx>>)
                         -> Result<(),()>
     {
-        debug!("match_trait_refs: obligation={} trait_ref={}",
+        debug!("match_where_clause: obligation={} where_clause_trait_ref={}",
                obligation.repr(self.tcx()),
-               trait_ref.repr(self.tcx()));
+               where_clause_trait_ref.repr(self.tcx()));
 
         let origin = infer::RelateOutputImplTypes(obligation.cause.span);
-        match self.infcx.sub_trait_refs(false,
-                                        origin,
-                                        trait_ref,
-                                        obligation.trait_ref.clone()) {
+        match self.infcx.sub_poly_trait_refs(false,
+                                             origin,
+                                             where_clause_trait_ref,
+                                             obligation.trait_ref.clone()) {
             Ok(()) => Ok(()),
             Err(_) => Err(()),
         }
@@ -1759,78 +1868,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     }
 
     ///////////////////////////////////////////////////////////////////////////
-    // Confirmation
-    //
-    // The final step of selection: once we know how an obligation is
-    // is resolved, we confirm that selection in order to have
-    // side-effects on the typing environment. This step also unifies
-    // the output type parameters from the obligation with those found
-    // on the impl/bound, which may yield type errors.
-
-    /// Relates the output type parameters from an impl to the
-    /// trait.  This may lead to type errors. The confirmation step
-    /// is separated from the main match procedure because these
-    /// type errors do not cause us to select another impl.
-    ///
-    /// As an example, consider matching the obligation
-    /// `Iterator<char> for Elems<int>` using the following impl:
-    ///
-    ///    impl<T> Iterator<T> for Elems<T> { ... }
-    ///
-    /// The match phase will succeed with substitution `T=int`.
-    /// The confirm step will then try to unify `int` and `char`
-    /// and yield an error.
-    fn confirm_impl_vtable(&mut self,
-                           impl_def_id: ast::DefId,
-                           obligation_cause: ObligationCause<'tcx>,
-                           obligation_trait_ref: Rc<ty::TraitRef<'tcx>>,
-                           substs: &Substs<'tcx>)
-                           -> Result<(), SelectionError<'tcx>>
-    {
-        let impl_trait_ref = ty::impl_trait_ref(self.tcx(),
-                                                impl_def_id).unwrap();
-        let impl_trait_ref = impl_trait_ref.subst(self.tcx(),
-                                                  substs);
-        self.confirm(obligation_cause, obligation_trait_ref, impl_trait_ref)
-    }
-
-    /// After we have determined which impl applies, and with what substitutions, there is one last
-    /// step. We have to go back and relate the "output" type parameters from the obligation to the
-    /// types that are specified in the impl.
-    ///
-    /// For example, imagine we have:
-    ///
-    ///     impl<T> Iterator<T> for Vec<T> { ... }
-    ///
-    /// and our obligation is `Iterator<Foo> for Vec<int>` (note the mismatch in the obligation
-    /// types). Up until this step, no error would be reported: the self type is `Vec<int>`, and
-    /// that matches `Vec<T>` with the substitution `T=int`. At this stage, we could then go and
-    /// check that the type parameters to the `Iterator` trait match. (In terms of the parameters,
-    /// the `expected_trait_ref` here would be `Iterator<int> for Vec<int>`, and the
-    /// `obligation_trait_ref` would be `Iterator<Foo> for Vec<int>`.
-    ///
-    /// Note that this checking occurs *after* the impl has selected, because these output type
-    /// parameters should not affect the selection of the impl. Therefore, if there is a mismatch,
-    /// we report an error to the user.
-    fn confirm(&mut self,
-               obligation_cause: ObligationCause,
-               obligation_trait_ref: Rc<ty::TraitRef<'tcx>>,
-               expected_trait_ref: Rc<ty::TraitRef<'tcx>>)
-               -> Result<(), SelectionError<'tcx>>
-    {
-        let origin = infer::RelateOutputImplTypes(obligation_cause.span);
-
-        let obligation_trait_ref = obligation_trait_ref.clone();
-        match self.infcx.sub_trait_refs(false,
-                                        origin,
-                                        expected_trait_ref.clone(),
-                                        obligation_trait_ref.clone()) {
-            Ok(()) => Ok(()),
-            Err(e) => Err(OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e))
-        }
-    }
-
-    ///////////////////////////////////////////////////////////////////////////
     // Miscellany
 
     fn push_stack<'o,'s:'o>(&mut self,
@@ -1838,11 +1875,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             obligation: &'o TraitObligation<'tcx>)
                             -> TraitObligationStack<'o, 'tcx>
     {
-        let skol_trait_ref = obligation.trait_ref.fold_with(&mut self.skolemizer);
+        let fresh_trait_ref = obligation.trait_ref.fold_with(&mut self.freshener);
 
         TraitObligationStack {
             obligation: obligation,
-            skol_trait_ref: skol_trait_ref,
+            fresh_trait_ref: fresh_trait_ref,
             previous: previous_stack.map(|p| p), // FIXME variance
         }
     }
@@ -1861,11 +1898,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                        cause: ObligationCause<'tcx>,
                        recursion_depth: uint,
                        impl_def_id: ast::DefId,
-                       impl_substs: &Substs<'tcx>)
+                       impl_substs: &Substs<'tcx>,
+                       skol_map: infer::SkolemizationMap,
+                       snapshot: &infer::CombinedSnapshot)
                        -> VecPerParamSpace<PredicateObligation<'tcx>>
     {
         let impl_generics = ty::lookup_item_type(self.tcx(), impl_def_id).generics;
         let bounds = impl_generics.to_bounds(self.tcx(), impl_substs);
+        let bounds = self.infcx().plug_leaks(skol_map, snapshot, &bounds);
         util::predicates_for_generics(self.tcx(), cause, recursion_depth, &bounds)
     }
 
diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs
index d8956246d32..27824ba5c6e 100644
--- a/src/librustc/middle/traits/util.rs
+++ b/src/librustc/middle/traits/util.rs
@@ -47,7 +47,7 @@ struct StackEntry<'tcx> {
 
 pub fn elaborate_trait_ref<'cx, 'tcx>(
     tcx: &'cx ty::ctxt<'tcx>,
-    trait_ref: Rc<ty::TraitRef<'tcx>>)
+    trait_ref: Rc<ty::PolyTraitRef<'tcx>>)
     -> Elaborator<'cx, 'tcx>
 {
     elaborate_predicates(tcx, vec![ty::Predicate::Trait(trait_ref)])
@@ -55,7 +55,7 @@ pub fn elaborate_trait_ref<'cx, 'tcx>(
 
 pub fn elaborate_trait_refs<'cx, 'tcx>(
     tcx: &'cx ty::ctxt<'tcx>,
-    trait_refs: &[Rc<ty::TraitRef<'tcx>>])
+    trait_refs: &[Rc<ty::PolyTraitRef<'tcx>>])
     -> Elaborator<'cx, 'tcx>
 {
     let predicates = trait_refs.iter()
@@ -174,7 +174,7 @@ pub struct Supertraits<'cx, 'tcx:'cx> {
 }
 
 pub fn supertraits<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
-                              trait_ref: Rc<ty::TraitRef<'tcx>>)
+                              trait_ref: Rc<ty::PolyTraitRef<'tcx>>)
                               -> Supertraits<'cx, 'tcx>
 {
     let elaborator = elaborate_trait_ref(tcx, trait_ref);
@@ -182,15 +182,15 @@ pub fn supertraits<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
 }
 
 pub fn transitive_bounds<'cx, 'tcx>(tcx: &'cx ty::ctxt<'tcx>,
-                                    bounds: &[Rc<ty::TraitRef<'tcx>>])
+                                    bounds: &[Rc<ty::PolyTraitRef<'tcx>>])
                                     -> Supertraits<'cx, 'tcx>
 {
     let elaborator = elaborate_trait_refs(tcx, bounds);
     Supertraits { elaborator: elaborator }
 }
 
-impl<'cx, 'tcx> Iterator<Rc<ty::TraitRef<'tcx>>> for Supertraits<'cx, 'tcx> {
-    fn next(&mut self) -> Option<Rc<ty::TraitRef<'tcx>>> {
+impl<'cx, 'tcx> Iterator<Rc<ty::PolyTraitRef<'tcx>>> for Supertraits<'cx, 'tcx> {
+    fn next(&mut self) -> Option<Rc<ty::PolyTraitRef<'tcx>>> {
         loop {
             match self.elaborator.next() {
                 None => {
@@ -266,18 +266,18 @@ pub fn predicates_for_generics<'tcx>(tcx: &ty::ctxt<'tcx>,
     })
 }
 
-pub fn trait_ref_for_builtin_bound<'tcx>(
+pub fn poly_trait_ref_for_builtin_bound<'tcx>(
     tcx: &ty::ctxt<'tcx>,
     builtin_bound: ty::BuiltinBound,
     param_ty: Ty<'tcx>)
-    -> Result<Rc<ty::TraitRef<'tcx>>, ErrorReported>
+    -> Result<Rc<ty::PolyTraitRef<'tcx>>, ErrorReported>
 {
     match tcx.lang_items.from_builtin_kind(builtin_bound) {
         Ok(def_id) => {
-            Ok(Rc::new(ty::TraitRef {
+            Ok(Rc::new(ty::Binder(ty::TraitRef {
                 def_id: def_id,
                 substs: Substs::empty().with_self_ty(param_ty)
-            }))
+            })))
         }
         Err(e) => {
             tcx.sess.err(e.as_slice());
@@ -294,7 +294,7 @@ pub fn predicate_for_builtin_bound<'tcx>(
     param_ty: Ty<'tcx>)
     -> Result<PredicateObligation<'tcx>, ErrorReported>
 {
-    let trait_ref = try!(trait_ref_for_builtin_bound(tcx, builtin_bound, param_ty));
+    let trait_ref = try!(poly_trait_ref_for_builtin_bound(tcx, builtin_bound, param_ty));
     Ok(Obligation {
         cause: cause,
         recursion_depth: recursion_depth,
@@ -306,14 +306,14 @@ pub fn predicate_for_builtin_bound<'tcx>(
 /// of caller obligations), search through the trait and supertraits to find one where `test(d)` is
 /// true, where `d` is the def-id of the trait/supertrait. If any is found, return `Some(p)` where
 /// `p` is the path to that trait/supertrait. Else `None`.
-pub fn search_trait_and_supertraits_from_bound<'tcx, F>(tcx: &ty::ctxt<'tcx>,
-                                                        caller_bound: Rc<ty::TraitRef<'tcx>>,
-                                                        mut test: F)
-                                                        -> Option<VtableParamData<'tcx>> where
-    F: FnMut(ast::DefId) -> bool,
+pub fn search_trait_and_supertraits_from_bound<'tcx,F>(tcx: &ty::ctxt<'tcx>,
+                                                       caller_bound: Rc<ty::PolyTraitRef<'tcx>>,
+                                                       mut test: F)
+                                                       -> Option<VtableParamData<'tcx>>
+    where F: FnMut(ast::DefId) -> bool,
 {
     for bound in transitive_bounds(tcx, &[caller_bound]) {
-        if test(bound.def_id) {
+        if test(bound.def_id()) {
             let vtable_param = VtableParamData { bound: bound };
             return Some(vtable_param);
         }
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 0ad07bed7d9..6839e8bcc45 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -60,7 +60,7 @@ use middle::subst::{mod, Subst, Substs, VecPerParamSpace};
 use middle::traits::ObligationCause;
 use middle::traits;
 use middle::ty;
-use middle::ty_fold::{mod, TypeFoldable, TypeFolder, HigherRankedFoldable};
+use middle::ty_fold::{mod, TypeFoldable, TypeFolder};
 use util::ppaux::{note_and_explain_region, bound_region_ptr_to_string};
 use util::ppaux::{trait_store_to_string, ty_to_string};
 use util::ppaux::{Repr, UserString};
@@ -478,7 +478,9 @@ pub enum MethodOrigin<'tcx> {
 #[deriving(Clone, Show)]
 pub struct MethodParam<'tcx> {
     // the precise trait reference that occurs as a bound -- this may
-    // be a supertrait of what the user actually typed.
+    // be a supertrait of what the user actually typed. Note that it
+    // never contains bound regions; those regions should have been
+    // instantiated with fresh variables at this point.
     pub trait_ref: Rc<ty::TraitRef<'tcx>>,
 
     // index of uint in the list of methods for the trait
@@ -609,7 +611,7 @@ pub enum vtable_origin<'tcx> {
 
 // For every explicit cast into an object type, maps from the cast
 // expr to the associated trait ref.
-pub type ObjectCastMap<'tcx> = RefCell<NodeMap<Rc<ty::TraitRef<'tcx>>>>;
+pub type ObjectCastMap<'tcx> = RefCell<NodeMap<Rc<ty::PolyTraitRef<'tcx>>>>;
 
 /// A restriction that certain types must be the same size. The use of
 /// `transmute` gives rise to these restrictions.
@@ -908,7 +910,7 @@ pub fn type_escapes_depth(ty: Ty, depth: uint) -> bool {
 pub struct BareFnTy<'tcx> {
     pub unsafety: ast::Unsafety,
     pub abi: abi::Abi,
-    pub sig: FnSig<'tcx>,
+    pub sig: PolyFnSig<'tcx>,
 }
 
 #[deriving(Clone, PartialEq, Eq, Hash, Show)]
@@ -917,7 +919,7 @@ pub struct ClosureTy<'tcx> {
     pub onceness: ast::Onceness,
     pub store: TraitStore,
     pub bounds: ExistentialBounds,
-    pub sig: FnSig<'tcx>,
+    pub sig: PolyFnSig<'tcx>,
     pub abi: abi::Abi,
 }
 
@@ -944,10 +946,6 @@ impl<'tcx> Copy for FnOutput<'tcx> {}
 /// - `inputs` is the list of arguments and their modes.
 /// - `output` is the return type.
 /// - `variadic` indicates whether this is a varidic function. (only true for foreign fns)
-///
-/// Note that a `FnSig` introduces a level of region binding, to
-/// account for late-bound parameters that appear in the types of the
-/// fn's arguments or the fn's return type.
 #[deriving(Clone, PartialEq, Eq, Hash)]
 pub struct FnSig<'tcx> {
     pub inputs: Vec<Ty<'tcx>>,
@@ -955,6 +953,8 @@ pub struct FnSig<'tcx> {
     pub variadic: bool
 }
 
+pub type PolyFnSig<'tcx> = Binder<FnSig<'tcx>>;
+
 #[deriving(Clone, PartialEq, Eq, Hash, Show)]
 pub struct ParamTy {
     pub space: subst::ParamSpace,
@@ -1308,10 +1308,25 @@ pub enum sty<'tcx> {
 #[deriving(Clone, PartialEq, Eq, Hash, Show)]
 pub struct TyTrait<'tcx> {
     // Principal trait reference.
-    pub principal: TraitRef<'tcx>, // would use Rc<TraitRef>, but it runs afoul of some static rules
+    pub principal: PolyTraitRef<'tcx>,
     pub bounds: ExistentialBounds
 }
 
+impl<'tcx> TyTrait<'tcx> {
+    /// Object types don't have a self-type specified. Therefore, when
+    /// we convert the principal trait-ref into a normal trait-ref,
+    /// you must give *some* self-type. A common choice is `mk_err()`
+    /// or some skolemized type.
+    pub fn principal_trait_ref_with_self_ty(&self, self_ty: Ty<'tcx>)
+                                            -> Rc<ty::PolyTraitRef<'tcx>>
+    {
+        Rc::new(ty::Binder(ty::TraitRef {
+            def_id: self.principal.def_id(),
+            substs: self.principal.substs().with_self_ty(self_ty),
+        }))
+    }
+}
+
 /// A complete reference to a trait. These take numerous guises in syntax,
 /// but perhaps the most recognizable form is in a where clause:
 ///
@@ -1333,21 +1348,36 @@ pub struct TraitRef<'tcx> {
     pub substs: Substs<'tcx>,
 }
 
-/// Binder serves as a synthetic binder for lifetimes. It is used when
-/// we wish to replace the escaping higher-ranked lifetimes in a type
-/// or something else that is not itself a binder (this is because the
-/// `replace_late_bound_regions` function replaces all lifetimes bound
-/// by the binder supplied to it; but a type is not a binder, so you
-/// must introduce an artificial one).
-#[deriving(Clone, PartialEq, Eq, Hash, Show)]
-pub struct Binder<T> {
-    pub value: T
-}
+pub type PolyTraitRef<'tcx> = Binder<TraitRef<'tcx>>;
+
+impl<'tcx> PolyTraitRef<'tcx> {
+    pub fn self_ty(&self) -> Ty<'tcx> {
+        self.0.self_ty()
+    }
+
+    pub fn def_id(&self) -> ast::DefId {
+        self.0.def_id
+    }
+
+    pub fn substs(&self) -> &Substs<'tcx> {
+        &self.0.substs
+    }
 
-pub fn bind<T>(value: T) -> Binder<T> {
-    Binder { value: value }
+    pub fn input_types(&self) -> &[Ty<'tcx>] {
+        self.0.input_types()
+    }
 }
 
+/// Binder is a binder for higher-ranked lifetimes. It is part of the
+/// compiler's representation for things like `for<'a> Fn(&'a int)`
+/// (which would be represented by the type `PolyTraitRef ==
+/// Binder<TraitRef>`). Note that when we skolemize, instantiate,
+/// erase, or otherwise "discharge" these bound reons, we change the
+/// type from `Binder<T>` to just `T` (see
+/// e.g. `liberate_late_bound_regions`).
+#[deriving(Clone, PartialEq, Eq, Hash, Show)]
+pub struct Binder<T>(pub T);
+
 #[deriving(Clone, PartialEq)]
 pub enum IntVarValue {
     IntType(ast::IntTy),
@@ -1416,7 +1446,7 @@ impl<'tcx> Copy for type_err<'tcx> {}
 pub struct ParamBounds<'tcx> {
     pub region_bounds: Vec<ty::Region>,
     pub builtin_bounds: BuiltinBounds,
-    pub trait_bounds: Vec<Rc<TraitRef<'tcx>>>
+    pub trait_bounds: Vec<Rc<PolyTraitRef<'tcx>>>
 }
 
 /// Bounds suitable for an existentially quantified type parameter
@@ -1505,12 +1535,16 @@ pub enum InferTy {
     TyVar(TyVid),
     IntVar(IntVid),
     FloatVar(FloatVid),
-    SkolemizedTy(uint),
+
+    /// A `FreshTy` is one that is generated as a replacement for an
+    /// unbound type variable. This is convenient for caching etc. See
+    /// `middle::infer::freshen` for more details.
+    FreshTy(uint),
 
     // FIXME -- once integral fallback is impl'd, we should remove
     // this type. It's only needed to prevent spurious errors for
     // integers whose type winds up never being constrained.
-    SkolemizedIntTy(uint),
+    FreshIntTy(uint),
 }
 
 impl Copy for InferTy {}
@@ -1577,8 +1611,8 @@ impl fmt::Show for InferTy {
             TyVar(ref v) => v.fmt(f),
             IntVar(ref v) => v.fmt(f),
             FloatVar(ref v) => v.fmt(f),
-            SkolemizedTy(v) => write!(f, "SkolemizedTy({})", v),
-            SkolemizedIntTy(v) => write!(f, "SkolemizedIntTy({})", v),
+            FreshTy(v) => write!(f, "FreshTy({})", v),
+            FreshIntTy(v) => write!(f, "FreshIntTy({})", v),
         }
     }
 }
@@ -1657,30 +1691,67 @@ pub enum Predicate<'tcx> {
     /// Corresponds to `where Foo : Bar<A,B,C>`. `Foo` here would be
     /// the `Self` type of the trait reference and `A`, `B`, and `C`
     /// would be the parameters in the `TypeSpace`.
-    Trait(Rc<TraitRef<'tcx>>),
+    Trait(Rc<PolyTraitRef<'tcx>>),
 
     /// where `T1 == T2`.
-    Equate(/* T1 */ Ty<'tcx>, /* T2 */ Ty<'tcx>),
+    Equate(PolyEquatePredicate<'tcx>),
 
     /// where 'a : 'b
-    RegionOutlives(/* 'a */ Region, /* 'b */ Region),
+    RegionOutlives(PolyRegionOutlivesPredicate),
 
     /// where T : 'a
-    TypeOutlives(Ty<'tcx>, Region),
+    TypeOutlives(PolyTypeOutlivesPredicate<'tcx>),
+}
+
+#[deriving(Clone, PartialEq, Eq, Hash, Show)]
+pub struct EquatePredicate<'tcx>(pub Ty<'tcx>, pub Ty<'tcx>); // `0 == 1`
+pub type PolyEquatePredicate<'tcx> = ty::Binder<EquatePredicate<'tcx>>;
+
+#[deriving(Clone, PartialEq, Eq, Hash, Show)]
+pub struct OutlivesPredicate<A,B>(pub A, pub B); // `A : B`
+pub type PolyOutlivesPredicate<A,B> = ty::Binder<OutlivesPredicate<A,B>>;
+pub type PolyRegionOutlivesPredicate = PolyOutlivesPredicate<ty::Region, ty::Region>;
+pub type PolyTypeOutlivesPredicate<'tcx> = PolyOutlivesPredicate<Ty<'tcx>, ty::Region>;
+
+pub trait AsPredicate<'tcx> {
+    fn as_predicate(&self) -> Predicate<'tcx>;
+}
+
+impl<'tcx> AsPredicate<'tcx> for Rc<PolyTraitRef<'tcx>> {
+    fn as_predicate(&self) -> Predicate<'tcx> {
+        Predicate::Trait(self.clone())
+    }
+}
+
+impl<'tcx> AsPredicate<'tcx> for PolyEquatePredicate<'tcx> {
+    fn as_predicate(&self) -> Predicate<'tcx> {
+        Predicate::Equate(self.clone())
+    }
+}
+
+impl<'tcx> AsPredicate<'tcx> for PolyRegionOutlivesPredicate {
+    fn as_predicate(&self) -> Predicate<'tcx> {
+        Predicate::RegionOutlives(self.clone())
+    }
+}
+
+impl<'tcx> AsPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> {
+    fn as_predicate(&self) -> Predicate<'tcx> {
+        Predicate::TypeOutlives(self.clone())
+    }
 }
 
 impl<'tcx> Predicate<'tcx> {
     pub fn has_escaping_regions(&self) -> bool {
         match *self {
             Predicate::Trait(ref trait_ref) => trait_ref.has_escaping_regions(),
-            Predicate::Equate(a, b) => (ty::type_has_escaping_regions(a) ||
-                                        ty::type_has_escaping_regions(b)),
-            Predicate::RegionOutlives(a, b) => a.escapes_depth(0) || b.escapes_depth(0),
-            Predicate::TypeOutlives(a, b) => ty::type_has_escaping_regions(a) || b.escapes_depth(0),
+            Predicate::Equate(ref p) => p.has_escaping_regions(),
+            Predicate::RegionOutlives(ref p) => p.has_escaping_regions(),
+            Predicate::TypeOutlives(ref p) => p.has_escaping_regions(),
         }
     }
 
-    pub fn to_trait(&self) -> Option<Rc<TraitRef<'tcx>>> {
+    pub fn to_trait(&self) -> Option<Rc<PolyTraitRef<'tcx>>> {
         match *self {
             Predicate::Trait(ref t) => {
                 Some(t.clone())
@@ -1748,14 +1819,6 @@ impl<'tcx> TraitRef<'tcx> {
         // associated types.
         self.substs.types.as_slice()
     }
-
-    pub fn has_escaping_regions(&self) -> bool {
-        self.substs.has_regions_escaping_depth(1)
-    }
-
-    pub fn has_bound_regions(&self) -> bool {
-        self.substs.has_regions_escaping_depth(0)
-    }
 }
 
 /// When type checking, we use the `ParameterEnvironment` to track
@@ -2160,7 +2223,7 @@ impl FlagComputation {
 
             &ty_trait(box TyTrait { ref principal, ref bounds }) => {
                 let mut computation = FlagComputation::new();
-                computation.add_substs(&principal.substs);
+                computation.add_substs(principal.substs());
                 self.add_bound_computation(&computation);
 
                 self.add_bounds(bounds);
@@ -2208,12 +2271,12 @@ impl FlagComputation {
         }
     }
 
-    fn add_fn_sig(&mut self, fn_sig: &FnSig) {
+    fn add_fn_sig(&mut self, fn_sig: &PolyFnSig) {
         let mut computation = FlagComputation::new();
 
-        computation.add_tys(fn_sig.inputs[]);
+        computation.add_tys(fn_sig.0.inputs[]);
 
-        if let ty::FnConverging(output) = fn_sig.output {
+        if let ty::FnConverging(output) = fn_sig.0.output {
             computation.add_ty(output);
         }
 
@@ -2356,17 +2419,17 @@ pub fn mk_ctor_fn<'tcx>(cx: &ctxt<'tcx>,
                BareFnTy {
                    unsafety: ast::Unsafety::Normal,
                    abi: abi::Rust,
-                   sig: FnSig {
+                   sig: ty::Binder(FnSig {
                     inputs: input_args,
                     output: ty::FnConverging(output),
                     variadic: false
-                   }
+                   })
                 })
 }
 
 
 pub fn mk_trait<'tcx>(cx: &ctxt<'tcx>,
-                      principal: ty::TraitRef<'tcx>,
+                      principal: ty::PolyTraitRef<'tcx>,
                       bounds: ExistentialBounds)
                       -> Ty<'tcx> {
     // take a copy of substs so that we own the vectors inside
@@ -2439,7 +2502,7 @@ pub fn maybe_walk_ty<'tcx>(ty: Ty<'tcx>, f: |Ty<'tcx>| -> bool) {
             maybe_walk_ty(tm.ty, f);
         }
         ty_trait(box TyTrait { ref principal, .. }) => {
-            for subty in principal.substs.types.iter() {
+            for subty in principal.substs().types.iter() {
                 maybe_walk_ty(*subty, |x| f(x));
             }
         }
@@ -2452,14 +2515,14 @@ pub fn maybe_walk_ty<'tcx>(ty: Ty<'tcx>, f: |Ty<'tcx>| -> bool) {
         }
         ty_tup(ref ts) => { for tt in ts.iter() { maybe_walk_ty(*tt, |x| f(x)); } }
         ty_bare_fn(ref ft) => {
-            for a in ft.sig.inputs.iter() { maybe_walk_ty(*a, |x| f(x)); }
-            if let ty::FnConverging(output) = ft.sig.output {
+            for a in ft.sig.0.inputs.iter() { maybe_walk_ty(*a, |x| f(x)); }
+            if let ty::FnConverging(output) = ft.sig.0.output {
                 maybe_walk_ty(output, f);
             }
         }
         ty_closure(ref ft) => {
-            for a in ft.sig.inputs.iter() { maybe_walk_ty(*a, |x| f(x)); }
-            if let ty::FnConverging(output) = ft.sig.output {
+            for a in ft.sig.0.inputs.iter() { maybe_walk_ty(*a, |x| f(x)); }
+            if let ty::FnConverging(output) = ft.sig.0.output {
                 maybe_walk_ty(output, f);
             }
         }
@@ -2961,7 +3024,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents {
             }
 
             // Scalar and unique types are sendable, and durable
-            ty_infer(ty::SkolemizedIntTy(_)) |
+            ty_infer(ty::FreshIntTy(_)) |
             ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
             ty_bare_fn(_) | ty::ty_char => {
                 TC::None
@@ -3182,7 +3245,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents {
 
     fn kind_bounds_to_contents<'tcx>(cx: &ctxt<'tcx>,
                                      bounds: BuiltinBounds,
-                                     traits: &[Rc<TraitRef<'tcx>>])
+                                     traits: &[Rc<PolyTraitRef<'tcx>>])
                                      -> TypeContents {
         let _i = indenter();
         let mut tc = TC::All;
@@ -3198,7 +3261,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents {
         // those inherited from traits with builtin-kind-supertraits.
         fn each_inherited_builtin_bound<'tcx, F>(cx: &ctxt<'tcx>,
                                                  bounds: BuiltinBounds,
-                                                 traits: &[Rc<TraitRef<'tcx>>],
+                                                 traits: &[Rc<PolyTraitRef<'tcx>>],
                                                  mut f: F) where
             F: FnMut(BuiltinBound),
         {
@@ -3207,7 +3270,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents {
             }
 
             each_bound_trait_and_supertraits(cx, traits, |trait_ref| {
-                let trait_def = lookup_trait_def(cx, trait_ref.def_id);
+                let trait_def = lookup_trait_def(cx, trait_ref.def_id());
                 for bound in trait_def.bounds.builtin_bounds.iter() {
                     f(bound);
                 }
@@ -3567,10 +3630,10 @@ pub fn type_is_integral(ty: Ty) -> bool {
     }
 }
 
-pub fn type_is_skolemized(ty: Ty) -> bool {
+pub fn type_is_fresh(ty: Ty) -> bool {
     match ty.sty {
-      ty_infer(SkolemizedTy(_)) => true,
-      ty_infer(SkolemizedIntTy(_)) => true,
+      ty_infer(FreshTy(_)) => true,
+      ty_infer(FreshIntTy(_)) => true,
       _ => false
     }
 }
@@ -3828,15 +3891,15 @@ pub fn node_id_item_substs<'tcx>(cx: &ctxt<'tcx>, id: ast::NodeId) -> ItemSubsts
 
 pub fn fn_is_variadic(fty: Ty) -> bool {
     match fty.sty {
-        ty_bare_fn(ref f) => f.sig.variadic,
-        ty_closure(ref f) => f.sig.variadic,
+        ty_bare_fn(ref f) => f.sig.0.variadic,
+        ty_closure(ref f) => f.sig.0.variadic,
         ref s => {
             panic!("fn_is_variadic() called on non-fn type: {}", s)
         }
     }
 }
 
-pub fn ty_fn_sig<'tcx>(fty: Ty<'tcx>) -> &'tcx FnSig<'tcx> {
+pub fn ty_fn_sig<'tcx>(fty: Ty<'tcx>) -> &'tcx PolyFnSig<'tcx> {
     match fty.sty {
         ty_bare_fn(ref f) => &f.sig,
         ty_closure(ref f) => &f.sig,
@@ -3857,7 +3920,7 @@ pub fn ty_fn_abi(fty: Ty) -> abi::Abi {
 
 // Type accessors for substructures of types
 pub fn ty_fn_args<'tcx>(fty: Ty<'tcx>) -> &'tcx [Ty<'tcx>] {
-    ty_fn_sig(fty).inputs.as_slice()
+    ty_fn_sig(fty).0.inputs.as_slice()
 }
 
 pub fn ty_closure_store(fty: Ty) -> TraitStore {
@@ -3876,8 +3939,8 @@ pub fn ty_closure_store(fty: Ty) -> TraitStore {
 
 pub fn ty_fn_ret<'tcx>(fty: Ty<'tcx>) -> FnOutput<'tcx> {
     match fty.sty {
-        ty_bare_fn(ref f) => f.sig.output,
-        ty_closure(ref f) => f.sig.output,
+        ty_bare_fn(ref f) => f.sig.0.output,
+        ty_closure(ref f) => f.sig.0.output,
         ref s => {
             panic!("ty_fn_ret() called on non-fn type: {}", s)
         }
@@ -4393,7 +4456,7 @@ pub fn ty_sort_string<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> String {
         ty_bare_fn(_) => "extern fn".to_string(),
         ty_closure(_) => "fn".to_string(),
         ty_trait(ref inner) => {
-            format!("trait {}", item_path_str(cx, inner.principal.def_id))
+            format!("trait {}", item_path_str(cx, inner.principal.def_id()))
         }
         ty_struct(id, _) => {
             format!("struct {}", item_path_str(cx, id))
@@ -4403,8 +4466,8 @@ pub fn ty_sort_string<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> String {
         ty_infer(TyVar(_)) => "inferred type".to_string(),
         ty_infer(IntVar(_)) => "integral variable".to_string(),
         ty_infer(FloatVar(_)) => "floating-point variable".to_string(),
-        ty_infer(SkolemizedTy(_)) => "skolemized type".to_string(),
-        ty_infer(SkolemizedIntTy(_)) => "skolemized integral type".to_string(),
+        ty_infer(FreshTy(_)) => "skolemized type".to_string(),
+        ty_infer(FreshIntTy(_)) => "skolemized integral type".to_string(),
         ty_param(ref p) => {
             if p.space == subst::SelfSpace {
                 "Self".to_string()
@@ -4770,7 +4833,8 @@ pub fn impl_trait_ref<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
                         ast::ItemImpl(_, _, ref opt_trait, _, _) => {
                             match opt_trait {
                                 &Some(ref t) => {
-                                    Some(ty::node_id_to_trait_ref(cx, t.ref_id))
+                                    let trait_ref = ty::node_id_to_trait_ref(cx, t.ref_id);
+                                    Some(trait_ref)
                                 }
                                 &None => None
                             }
@@ -4813,7 +4877,7 @@ pub fn try_add_builtin_trait(
 pub fn ty_to_def_id(ty: Ty) -> Option<ast::DefId> {
     match ty.sty {
         ty_trait(ref tt) =>
-            Some(tt.principal.def_id),
+            Some(tt.principal.def_id()),
         ty_struct(id, _) |
         ty_enum(id, _) |
         ty_unboxed_closure(id, _, _) =>
@@ -5073,10 +5137,10 @@ pub fn lookup_trait_def<'tcx>(cx: &ctxt<'tcx>, did: ast::DefId)
 /// Given a reference to a trait, returns the "superbounds" declared
 /// on the trait, with appropriate substitutions applied.
 pub fn predicates_for_trait_ref<'tcx>(tcx: &ctxt<'tcx>,
-                                      trait_ref: &TraitRef<'tcx>)
+                                      trait_ref: &PolyTraitRef<'tcx>)
                                       -> Vec<ty::Predicate<'tcx>>
 {
-    let trait_def = lookup_trait_def(tcx, trait_ref.def_id);
+    let trait_def = lookup_trait_def(tcx, trait_ref.def_id());
 
     debug!("bounds_for_trait_ref(trait_def={}, trait_ref={})",
            trait_def.repr(tcx), trait_ref.repr(tcx));
@@ -5149,8 +5213,9 @@ pub fn predicates_for_trait_ref<'tcx>(tcx: &ctxt<'tcx>,
         trait_def.bounds.trait_bounds
         .iter()
         .map(|bound_trait_ref| {
-            ty::TraitRef::new(bound_trait_ref.def_id,
-                              bound_trait_ref.substs.subst(tcx, &trait_ref.substs))
+            ty::Binder(
+                ty::TraitRef::new(bound_trait_ref.def_id(),
+                                  bound_trait_ref.substs().subst(tcx, trait_ref.substs())))
         })
         .map(|bound_trait_ref| Rc::new(bound_trait_ref))
         .collect();
@@ -5161,9 +5226,9 @@ pub fn predicates_for_trait_ref<'tcx>(tcx: &ctxt<'tcx>,
     // The region bounds and builtin bounds do not currently introduce
     // binders so we can just substitute in a straightforward way here.
     let region_bounds =
-        trait_def.bounds.region_bounds.subst(tcx, &trait_ref.substs);
+        trait_def.bounds.region_bounds.subst(tcx, trait_ref.substs());
     let builtin_bounds =
-        trait_def.bounds.builtin_bounds.subst(tcx, &trait_ref.substs);
+        trait_def.bounds.builtin_bounds.subst(tcx, trait_ref.substs());
 
     let bounds = ty::ParamBounds {
         trait_bounds: trait_bounds,
@@ -5183,18 +5248,21 @@ pub fn predicates<'tcx>(
     let mut vec = Vec::new();
 
     for builtin_bound in bounds.builtin_bounds.iter() {
-        match traits::trait_ref_for_builtin_bound(tcx, builtin_bound, param_ty) {
-            Ok(trait_ref) => { vec.push(Predicate::Trait(trait_ref)); }
+        match traits::poly_trait_ref_for_builtin_bound(tcx, builtin_bound, param_ty) {
+            Ok(trait_ref) => { vec.push(trait_ref.as_predicate()); }
             Err(ErrorReported) => { }
         }
     }
 
     for &region_bound in bounds.region_bounds.iter() {
-        vec.push(Predicate::TypeOutlives(param_ty, region_bound));
+        // account for the binder being introduced below; no need to shift `param_ty`
+        // because, at present at least, it can only refer to early-bound regions
+        let region_bound = ty_fold::shift_region(region_bound, 1);
+        vec.push(ty::Binder(ty::OutlivesPredicate(param_ty, region_bound)).as_predicate());
     }
 
     for bound_trait_ref in bounds.trait_bounds.iter() {
-        vec.push(Predicate::Trait((*bound_trait_ref).clone()));
+        vec.push(bound_trait_ref.as_predicate());
     }
 
     vec
@@ -5483,18 +5551,6 @@ pub fn normalize_ty<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
             subst::Substs { regions: subst::ErasedRegions,
                             types: substs.types.fold_with(self) }
         }
-
-        fn fold_fn_sig(&mut self,
-                       sig: &ty::FnSig<'tcx>)
-                       -> ty::FnSig<'tcx> {
-            // The binder-id is only relevant to bound regions, which
-            // are erased at trans time.
-            ty::FnSig {
-                inputs: sig.inputs.fold_with(self),
-                output: sig.output.fold_with(self),
-                variadic: sig.variadic,
-            }
-        }
     }
 }
 
@@ -5545,10 +5601,10 @@ pub fn eval_repeat_count(tcx: &ctxt, count_expr: &ast::Expr) -> uint {
 // relation on the supertraits from each bounded trait's constraint
 // list.
 pub fn each_bound_trait_and_supertraits<'tcx, F>(tcx: &ctxt<'tcx>,
-                                                 bounds: &[Rc<TraitRef<'tcx>>],
+                                                 bounds: &[Rc<PolyTraitRef<'tcx>>],
                                                  mut f: F)
                                                  -> bool where
-    F: FnMut(Rc<TraitRef<'tcx>>) -> bool,
+    F: FnMut(Rc<PolyTraitRef<'tcx>>) -> bool,
 {
     for bound_trait_ref in traits::transitive_bounds(tcx, bounds) {
         if !f(bound_trait_ref) {
@@ -5559,18 +5615,18 @@ pub fn each_bound_trait_and_supertraits<'tcx, F>(tcx: &ctxt<'tcx>,
 }
 
 pub fn object_region_bounds<'tcx>(tcx: &ctxt<'tcx>,
-                                  opt_principal: Option<&TraitRef<'tcx>>, // None for boxed closures
+                                  opt_principal: Option<&PolyTraitRef<'tcx>>, // None for closures
                                   others: BuiltinBounds)
                                   -> Vec<ty::Region>
 {
     // Since we don't actually *know* the self type for an object,
     // this "open(err)" serves as a kind of dummy standin -- basically
     // a skolemized type.
-    let open_ty = ty::mk_infer(tcx, SkolemizedTy(0));
+    let open_ty = ty::mk_infer(tcx, FreshTy(0));
 
     let opt_trait_ref = opt_principal.map_or(Vec::new(), |principal| {
-        let substs = principal.substs.with_self_ty(open_ty);
-        vec!(Rc::new(ty::TraitRef::new(principal.def_id, substs)))
+        let substs = principal.substs().with_self_ty(open_ty);
+        vec!(Rc::new(ty::Binder(ty::TraitRef::new(principal.def_id(), substs))))
     });
 
     let param_bounds = ty::ParamBounds {
@@ -5583,19 +5639,27 @@ pub fn object_region_bounds<'tcx>(tcx: &ctxt<'tcx>,
     ty::required_region_bounds(tcx, open_ty, predicates)
 }
 
-/// Given a type which must meet the builtin bounds and trait bounds, returns a set of lifetimes
-/// which the type must outlive.
+/// Given a set of predicates that apply to an object type, returns
+/// the region bounds that the (erased) `Self` type must
+/// outlive. Precisely *because* the `Self` type is erased, the
+/// parameter `erased_self_ty` must be supplied to indicate what type
+/// has been used to represent `Self` in the predicates
+/// themselves. This should really be a unique type; `FreshTy(0)` is a
+/// popular choice (see `object_region_bounds` above).
 ///
-/// Requires that trait definitions have been processed.
+/// Requires that trait definitions have been processed so that we can
+/// elaborate predicates and walk supertraits.
 pub fn required_region_bounds<'tcx>(tcx: &ctxt<'tcx>,
-                                    param_ty: Ty<'tcx>,
+                                    erased_self_ty: Ty<'tcx>,
                                     predicates: Vec<ty::Predicate<'tcx>>)
                                     -> Vec<ty::Region>
 {
-    debug!("required_region_bounds(param_ty={}, predicates={})",
-           param_ty.repr(tcx),
+    debug!("required_region_bounds(erased_self_ty={}, predicates={})",
+           erased_self_ty.repr(tcx),
            predicates.repr(tcx));
 
+    assert!(!erased_self_ty.has_escaping_regions());
+
     traits::elaborate_predicates(tcx, predicates)
         .filter_map(|predicate| {
             match predicate {
@@ -5604,9 +5668,22 @@ pub fn required_region_bounds<'tcx>(tcx: &ctxt<'tcx>,
                 ty::Predicate::RegionOutlives(..) => {
                     None
                 }
-                ty::Predicate::TypeOutlives(t, r) => {
-                    if t == param_ty {
-                        Some(r)
+                ty::Predicate::TypeOutlives(ty::Binder(ty::OutlivesPredicate(t, r))) => {
+                    // Search for a bound of the form `erased_self_ty
+                    // : 'a`, but be wary of something like `for<'a>
+                    // erased_self_ty : 'a` (we interpret a
+                    // higher-ranked bound like that as 'static,
+                    // though at present the code in `fulfill.rs`
+                    // considers such bounds to be unsatisfiable, so
+                    // it's kind of a moot point since you could never
+                    // construct such an object, but this seems
+                    // correct even if that code changes).
+                    if t == erased_self_ty && !r.has_escaping_regions() {
+                        if r.has_escaping_regions() {
+                            Some(ty::ReStatic)
+                        } else {
+                            Some(r)
+                        }
                     } else {
                         None
                     }
@@ -5824,12 +5901,12 @@ pub fn trait_item_of_item(tcx: &ctxt, def_id: ast::DefId)
 
 /// Creates a hash of the type `Ty` which will be the same no matter what crate
 /// context it's calculated within. This is used by the `type_id` intrinsic.
-pub fn hash_crate_independent(tcx: &ctxt, ty: Ty, svh: &Svh) -> u64 {
+pub fn hash_crate_independent<'tcx>(tcx: &ctxt<'tcx>, ty: Ty<'tcx>, svh: &Svh) -> u64 {
     let mut state = sip::SipState::new();
     helper(tcx, ty, svh, &mut state);
     return state.result();
 
-    fn helper(tcx: &ctxt, ty: Ty, svh: &Svh, state: &mut sip::SipState) {
+    fn helper<'tcx>(tcx: &ctxt<'tcx>, ty: Ty<'tcx>, svh: &Svh, state: &mut sip::SipState) {
         macro_rules! byte( ($b:expr) => { ($b as u8).hash(state) } );
         macro_rules! hash( ($e:expr) => { $e.hash(state) } );
 
@@ -5862,7 +5939,7 @@ pub fn hash_crate_independent(tcx: &ctxt, ty: Ty, svh: &Svh) -> u64 {
         let mt = |state: &mut sip::SipState, mt: mt| {
             mt.mutbl.hash(state);
         };
-        let fn_sig = |state: &mut sip::SipState, sig: &FnSig| {
+        let fn_sig = |state: &mut sip::SipState, sig: &Binder<FnSig<'tcx>>| {
             let sig = anonymize_late_bound_regions(tcx, sig);
             for a in sig.inputs.iter() { helper(tcx, *a, svh, state); }
             if let ty::FnConverging(output) = sig.output {
@@ -5938,7 +6015,7 @@ pub fn hash_crate_independent(tcx: &ctxt, ty: Ty, svh: &Svh) -> u64 {
                 }
                 ty_trait(box TyTrait { ref principal, bounds }) => {
                     byte!(17);
-                    did(state, principal.def_id);
+                    did(state, principal.def_id());
                     hash!(bounds);
 
                     let principal = anonymize_late_bound_regions(tcx, principal);
@@ -6033,7 +6110,7 @@ pub fn construct_parameter_environment<'tcx>(
     //
 
     let bounds = generics.to_bounds(tcx, &free_substs);
-    let bounds = liberate_late_bound_regions(tcx, free_id_scope, &bind(bounds)).value;
+    let bounds = liberate_late_bound_regions(tcx, free_id_scope, &ty::Binder(bounds));
 
     //
     // Compute region bounds. For now, these relations are stored in a
@@ -6088,16 +6165,20 @@ pub fn construct_parameter_environment<'tcx>(
                 Predicate::Trait(..) | Predicate::Equate(..) | Predicate::TypeOutlives(..) => {
                     // No region bounds here
                 }
-                Predicate::RegionOutlives(ty::ReFree(fr_a), ty::ReFree(fr_b)) => {
-                    // Record that `'a:'b`. Or, put another way, `'b <= 'a`.
-                    tcx.region_maps.relate_free_regions(fr_b, fr_a);
-                }
-                Predicate::RegionOutlives(r_a, r_b) => {
-                    // All named regions are instantiated with free regions.
-                    tcx.sess.bug(
-                        format!("record_region_bounds: non free region: {} / {}",
-                                r_a.repr(tcx),
-                                r_b.repr(tcx)).as_slice());
+                Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => {
+                    match (r_a, r_b) {
+                        (ty::ReFree(fr_a), ty::ReFree(fr_b)) => {
+                            // Record that `'a:'b`. Or, put another way, `'b <= 'a`.
+                            tcx.region_maps.relate_free_regions(fr_b, fr_a);
+                        }
+                        _ => {
+                            // All named regions are instantiated with free regions.
+                            tcx.sess.bug(
+                                format!("record_region_bounds: non free region: {} / {}",
+                                        r_a.repr(tcx),
+                                        r_b.repr(tcx)).as_slice());
+                        }
+                    }
                 }
             }
         }
@@ -6200,7 +6281,7 @@ pub fn accumulate_lifetimes_in_type(accumulator: &mut Vec<ty::Region>,
                 accumulator.push(region)
             }
             ty_trait(ref t) => {
-                accumulator.push_all(t.principal.substs.regions().as_slice());
+                accumulator.push_all(t.principal.substs().regions().as_slice());
             }
             ty_enum(_, ref substs) |
             ty_struct(_, ref substs) => {
@@ -6289,25 +6370,35 @@ impl<'tcx> AutoDerefRef<'tcx> {
 
 /// Replace any late-bound regions bound in `value` with free variants attached to scope-id
 /// `scope_id`.
-pub fn liberate_late_bound_regions<'tcx, HR>(
+pub fn liberate_late_bound_regions<'tcx, T>(
     tcx: &ty::ctxt<'tcx>,
     scope: region::CodeExtent,
-    value: &HR)
-    -> HR
-    where HR : HigherRankedFoldable<'tcx>
+    value: &Binder<T>)
+    -> T
+    where T : TypeFoldable<'tcx> + Repr<'tcx>
 {
     replace_late_bound_regions(
         tcx, value,
         |br, _| ty::ReFree(ty::FreeRegion{scope: scope, bound_region: br})).0
 }
 
+pub fn count_late_bound_regions<'tcx, T>(
+    tcx: &ty::ctxt<'tcx>,
+    value: &Binder<T>)
+    -> uint
+    where T : TypeFoldable<'tcx> + Repr<'tcx>
+{
+    let (_, skol_map) = replace_late_bound_regions(tcx, value, |_, _| ty::ReStatic);
+    skol_map.len()
+}
+
 /// Replace any late-bound regions bound in `value` with `'static`. Useful in trans but also
 /// method lookup and a few other places where precise region relationships are not required.
-pub fn erase_late_bound_regions<'tcx, HR>(
+pub fn erase_late_bound_regions<'tcx, T>(
     tcx: &ty::ctxt<'tcx>,
-    value: &HR)
-    -> HR
-    where HR : HigherRankedFoldable<'tcx>
+    value: &Binder<T>)
+    -> T
+    where T : TypeFoldable<'tcx> + Repr<'tcx>
 {
     replace_late_bound_regions(tcx, value, |_, _| ty::ReStatic).0
 }
@@ -6320,8 +6411,12 @@ pub fn erase_late_bound_regions<'tcx, HR>(
 /// `FnSig`s or `TraitRef`s which are equivalent up to region naming will become
 /// structurally identical.  For example, `for<'a, 'b> fn(&'a int, &'b int)` and
 /// `for<'a, 'b> fn(&'b int, &'a int)` will become identical after anonymization.
-pub fn anonymize_late_bound_regions<'tcx, HR>(tcx: &ctxt<'tcx>, sig: &HR) -> HR
-                                              where HR: HigherRankedFoldable<'tcx> {
+pub fn anonymize_late_bound_regions<'tcx, T>(
+    tcx: &ctxt<'tcx>,
+    sig: &Binder<T>)
+    -> T
+    where T : TypeFoldable<'tcx> + Repr<'tcx>,
+{
     let mut counter = 0;
     replace_late_bound_regions(tcx, sig, |_, db| {
         counter += 1;
@@ -6330,39 +6425,35 @@ pub fn anonymize_late_bound_regions<'tcx, HR>(tcx: &ctxt<'tcx>, sig: &HR) -> HR
 }
 
 /// Replaces the late-bound-regions in `value` that are bound by `value`.
-pub fn replace_late_bound_regions<'tcx, HR, F>(
+pub fn replace_late_bound_regions<'tcx, T, F>(
     tcx: &ty::ctxt<'tcx>,
-    value: &HR,
+    binder: &Binder<T>,
     mut mapf: F)
--> (HR, FnvHashMap<ty::BoundRegion, ty::Region>) where
-    HR : HigherRankedFoldable<'tcx>,
-    F: FnMut(BoundRegion, DebruijnIndex) -> ty::Region,
+    -> (T, FnvHashMap<ty::BoundRegion,ty::Region>)
+    where T : TypeFoldable<'tcx> + Repr<'tcx>,
+          F : FnMut(BoundRegion, DebruijnIndex) -> ty::Region,
 {
-    debug!("replace_late_bound_regions({})", value.repr(tcx));
+    debug!("replace_late_bound_regions({})", binder.repr(tcx));
 
     let mut map = FnvHashMap::new();
-    let value = {
-        let mut f = ty_fold::RegionFolder::new(tcx, |region, current_depth| {
-            debug!("region={}", region.repr(tcx));
-            match region {
-                ty::ReLateBound(debruijn, br) if debruijn.depth == current_depth => {
-                    * match map.entry(br) {
-                        Vacant(entry) => entry.set(mapf(br, debruijn)),
-                        Occupied(entry) => entry.into_mut(),
-                    }
-                }
-                _ => {
-                    region
+
+    // Note: fold the field `0`, not the binder, so that late-bound
+    // regions bound by `binder` are considered free.
+    let value = ty_fold::fold_regions(tcx, &binder.0, |region, current_depth| {
+        debug!("region={}", region.repr(tcx));
+        match region {
+            ty::ReLateBound(debruijn, br) if debruijn.depth == current_depth => {
+                * match map.entry(br) {
+                    Vacant(entry) => entry.set(mapf(br, debruijn)),
+                    Occupied(entry) => entry.into_mut(),
                 }
             }
-        });
+            _ => {
+                region
+            }
+        }
+    });
 
-        // Note: use `fold_contents` not `fold_with`. If we used
-        // `fold_with`, it would consider the late-bound regions bound
-        // by `value` to be bound, but we want to consider them as
-        // `free`.
-        value.fold_contents(&mut f)
-    };
     debug!("resulting map: {} value: {}", map, value.repr(tcx));
     (value, map)
 }
@@ -6440,9 +6531,9 @@ impl<'tcx> Repr<'tcx> for ty::Predicate<'tcx> {
     fn repr(&self, tcx: &ctxt<'tcx>) -> String {
         match *self {
             Predicate::Trait(ref a) => a.repr(tcx),
-            Predicate::Equate(a, b) => format!("Equate({},{})", a.repr(tcx), b.repr(tcx)),
-            Predicate::RegionOutlives(a, b) => format!("Outlives({}:{})", a.repr(tcx), b.repr(tcx)),
-            Predicate::TypeOutlives(a, b) => format!("Outlives({}:{})", a.repr(tcx), b.repr(tcx)),
+            Predicate::Equate(ref pair) => format!("Equate({})", pair.repr(tcx)),
+            Predicate::RegionOutlives(ref pair) => format!("Outlives({})", pair.repr(tcx)),
+            Predicate::TypeOutlives(ref pair) => format!("Outlives({})", pair.repr(tcx)),
         }
     }
 }
@@ -6538,3 +6629,49 @@ pub fn can_type_implement_copy<'tcx>(tcx: &ctxt<'tcx>,
 
     Ok(())
 }
+
+pub trait RegionEscape {
+    fn has_escaping_regions(&self) -> bool {
+        self.has_regions_escaping_depth(0)
+    }
+
+    fn has_regions_escaping_depth(&self, depth: uint) -> bool;
+}
+
+impl<'tcx> RegionEscape for Ty<'tcx> {
+    fn has_regions_escaping_depth(&self, depth: uint) -> bool {
+        ty::type_escapes_depth(*self, depth)
+    }
+}
+
+impl RegionEscape for Region {
+    fn has_regions_escaping_depth(&self, depth: uint) -> bool {
+        self.escapes_depth(depth)
+    }
+}
+
+impl<'tcx> RegionEscape for TraitRef<'tcx> {
+    fn has_regions_escaping_depth(&self, depth: uint) -> bool {
+        self.substs.types.iter().any(|t| t.has_regions_escaping_depth(depth)) &&
+            self.substs.regions().iter().any(|t| t.has_regions_escaping_depth(depth))
+    }
+}
+
+impl<'tcx,T:RegionEscape> RegionEscape for Binder<T> {
+    fn has_regions_escaping_depth(&self, depth: uint) -> bool {
+        self.0.has_regions_escaping_depth(depth + 1)
+    }
+}
+
+impl<'tcx> RegionEscape for EquatePredicate<'tcx> {
+    fn has_regions_escaping_depth(&self, depth: uint) -> bool {
+        self.0.has_regions_escaping_depth(depth) || self.1.has_regions_escaping_depth(depth)
+    }
+}
+
+impl<T:RegionEscape,U:RegionEscape> RegionEscape for OutlivesPredicate<T,U> {
+    fn has_regions_escaping_depth(&self, depth: uint) -> bool {
+        self.0.has_regions_escaping_depth(depth) || self.1.has_regions_escaping_depth(depth)
+    }
+}
+
diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs
index 4877512ce58..71e42a9dbb3 100644
--- a/src/librustc/middle/ty_fold.rs
+++ b/src/librustc/middle/ty_fold.rs
@@ -93,8 +93,8 @@ pub trait TypeFolder<'tcx> {
     }
 
     fn fold_fn_sig(&mut self,
-                sig: &ty::FnSig<'tcx>)
-                -> ty::FnSig<'tcx> {
+                   sig: &ty::FnSig<'tcx>)
+                   -> ty::FnSig<'tcx> {
         super_fold_fn_sig(self, sig)
     }
 
@@ -183,7 +183,7 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec<T> {
 impl<'tcx, T:TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder<T> {
     fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Binder<T> {
         folder.enter_region_binder();
-        let result = ty::bind(self.value.fold_with(folder));
+        let result = ty::Binder(self.0.fold_with(folder));
         folder.exit_region_binder();
         result
     }
@@ -409,15 +409,12 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
         match *self {
             ty::Predicate::Trait(ref a) =>
                 ty::Predicate::Trait(a.fold_with(folder)),
-            ty::Predicate::Equate(ref a, ref b) =>
-                ty::Predicate::Equate(a.fold_with(folder),
-                                        b.fold_with(folder)),
-            ty::Predicate::RegionOutlives(ref a, ref b) =>
-                ty::Predicate::RegionOutlives(a.fold_with(folder),
-                                                b.fold_with(folder)),
-            ty::Predicate::TypeOutlives(ref a, ref b) =>
-                ty::Predicate::TypeOutlives(a.fold_with(folder),
-                                              b.fold_with(folder)),
+            ty::Predicate::Equate(ref binder) =>
+                ty::Predicate::Equate(binder.fold_with(folder)),
+            ty::Predicate::RegionOutlives(ref binder) =>
+                ty::Predicate::RegionOutlives(binder.fold_with(folder)),
+            ty::Predicate::TypeOutlives(ref binder) =>
+                ty::Predicate::TypeOutlives(binder.fold_with(folder)),
         }
     }
 }
@@ -501,6 +498,23 @@ impl<'tcx> TypeFoldable<'tcx> for traits::VtableParamData<'tcx> {
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for ty::EquatePredicate<'tcx> {
+    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::EquatePredicate<'tcx> {
+        ty::EquatePredicate(self.0.fold_with(folder),
+                            self.1.fold_with(folder))
+    }
+}
+
+impl<'tcx,T,U> TypeFoldable<'tcx> for ty::OutlivesPredicate<T,U>
+    where T : TypeFoldable<'tcx>,
+          U : TypeFoldable<'tcx>,
+{
+    fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::OutlivesPredicate<T,U> {
+        ty::OutlivesPredicate(self.0.fold_with(folder),
+                              self.1.fold_with(folder))
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // "super" routines: these are the default implementations for TypeFolder.
 //
@@ -533,16 +547,6 @@ pub fn super_fold_fn_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
                                                     sig: &ty::FnSig<'tcx>)
                                                     -> ty::FnSig<'tcx>
 {
-    this.enter_region_binder();
-    let result = super_fold_fn_sig_contents(this, sig);
-    this.exit_region_binder();
-    result
-}
-
-pub fn super_fold_fn_sig_contents<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                             sig: &ty::FnSig<'tcx>)
-                                                             -> ty::FnSig<'tcx>
-{
     ty::FnSig { inputs: sig.inputs.fold_with(this),
                 output: sig.output.fold_with(this),
                 variadic: sig.variadic }
@@ -584,16 +588,6 @@ pub fn super_fold_trait_ref<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
                                                        t: &ty::TraitRef<'tcx>)
                                                        -> ty::TraitRef<'tcx>
 {
-    this.enter_region_binder();
-    let result = super_fold_trait_ref_contents(this, t);
-    this.exit_region_binder();
-    result
-}
-
-pub fn super_fold_trait_ref_contents<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                                t: &ty::TraitRef<'tcx>)
-                                                                -> ty::TraitRef<'tcx>
-{
     ty::TraitRef {
         def_id: t.def_id,
         substs: t.substs.fold_with(this),
@@ -707,40 +701,6 @@ pub fn super_fold_item_substs<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
 }
 
 ///////////////////////////////////////////////////////////////////////////
-// Higher-ranked things
-
-/// Designates a "binder" for late-bound regions.
-pub trait HigherRankedFoldable<'tcx>: Repr<'tcx> {
-    /// Folds the contents of `self`, ignoring the region binder created
-    /// by `self`.
-    fn fold_contents<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self;
-}
-
-impl<'tcx> HigherRankedFoldable<'tcx> for ty::FnSig<'tcx> {
-    fn fold_contents<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig<'tcx> {
-        super_fold_fn_sig_contents(folder, self)
-    }
-}
-
-impl<'tcx> HigherRankedFoldable<'tcx> for ty::TraitRef<'tcx> {
-    fn fold_contents<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::TraitRef<'tcx> {
-        super_fold_trait_ref_contents(folder, self)
-    }
-}
-
-impl<'tcx, T:TypeFoldable<'tcx>+Repr<'tcx>> HigherRankedFoldable<'tcx> for ty::Binder<T> {
-    fn fold_contents<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Binder<T> {
-        ty::bind(self.value.fold_with(folder))
-    }
-}
-
-impl<'tcx, T:HigherRankedFoldable<'tcx>> HigherRankedFoldable<'tcx> for Rc<T> {
-    fn fold_contents<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Rc<T> {
-        Rc::new((**self).fold_contents(folder))
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
 // Some sample folders
 
 pub struct BottomUpFolder<'a, 'tcx: 'a, F> where F: FnMut(Ty<'tcx>) -> Ty<'tcx> {
@@ -751,7 +711,7 @@ pub struct BottomUpFolder<'a, 'tcx: 'a, F> where F: FnMut(Ty<'tcx>) -> Ty<'tcx>
 impl<'a, 'tcx, F> TypeFolder<'tcx> for BottomUpFolder<'a, 'tcx, F> where
     F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
 {
-    fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx }
+    fn tcx(&self) -> &ty::ctxt<'tcx> { self.tcx }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
         let t1 = super_fold_ty(self, ty);
@@ -770,18 +730,17 @@ impl<'a, 'tcx, F> TypeFolder<'tcx> for BottomUpFolder<'a, 'tcx, F> where
 /// regions (aka "lifetimes") that are bound within a type are not
 /// visited by this folder; only regions that occur free will be
 /// visited by `fld_r`.
-///
-/// (The distinction between "free" and "bound" is represented by
-/// keeping track of each `FnSig` in the lexical context of the
-/// current position of the fold.)
-pub struct RegionFolder<'a, 'tcx: 'a, F> where F: FnMut(ty::Region, uint) -> ty::Region {
+
+pub struct RegionFolder<'a, 'tcx: 'a> {
     tcx: &'a ty::ctxt<'tcx>,
     current_depth: uint,
-    fld_r: F,
+    fld_r: &'a mut (FnMut(ty::Region, uint) -> ty::Region + 'a),
 }
 
-impl<'a, 'tcx, F> RegionFolder<'a, 'tcx, F> where F: FnMut(ty::Region, uint) -> ty::Region {
-    pub fn new(tcx: &'a ty::ctxt<'tcx>, fld_r: F) -> RegionFolder<'a, 'tcx, F> {
+impl<'a, 'tcx> RegionFolder<'a, 'tcx> {
+    pub fn new<F>(tcx: &'a ty::ctxt<'tcx>, fld_r: &'a mut F) -> RegionFolder<'a, 'tcx>
+        where F : FnMut(ty::Region, uint) -> ty::Region
+    {
         RegionFolder {
             tcx: tcx,
             current_depth: 1,
@@ -790,10 +749,27 @@ impl<'a, 'tcx, F> RegionFolder<'a, 'tcx, F> where F: FnMut(ty::Region, uint) ->
     }
 }
 
-impl<'a, 'tcx, F> TypeFolder<'tcx> for RegionFolder<'a, 'tcx, F> where
-    F: FnMut(ty::Region, uint) -> ty::Region,
+pub fn collect_regions<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> Vec<ty::Region>
+    where T : TypeFoldable<'tcx>
+{
+    let mut vec = Vec::new();
+    fold_regions(tcx, value, |r, _| { vec.push(r); r });
+    vec
+}
+
+pub fn fold_regions<'tcx,T,F>(tcx: &ty::ctxt<'tcx>,
+                              value: &T,
+                              mut f: F)
+                              -> T
+    where F : FnMut(ty::Region, uint) -> ty::Region,
+          T : TypeFoldable<'tcx>,
 {
-    fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx }
+    value.fold_with(&mut RegionFolder::new(tcx, &mut f))
+}
+
+impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx>
+{
+    fn tcx(&self) -> &ty::ctxt<'tcx> { self.tcx }
 
     fn enter_region_binder(&mut self) {
         self.current_depth += 1;
@@ -813,7 +789,7 @@ impl<'a, 'tcx, F> TypeFolder<'tcx> for RegionFolder<'a, 'tcx, F> where
             _ => {
                 debug!("RegionFolder.fold_region({}) folding free region (current_depth={})",
                        r.repr(self.tcx()), self.current_depth);
-                (self.fld_r)(r, self.current_depth)
+                self.fld_r.call_mut((r, self.current_depth))
             }
         }
     }
@@ -869,7 +845,7 @@ pub fn shift_regions<'tcx, T:TypeFoldable<'tcx>+Repr<'tcx>>(tcx: &ty::ctxt<'tcx>
     debug!("shift_regions(value={}, amount={})",
            value.repr(tcx), amount);
 
-    value.fold_with(&mut RegionFolder::new(tcx, |region, _current_depth| {
+    value.fold_with(&mut RegionFolder::new(tcx, &mut |region, _current_depth| {
         shift_region(region, amount)
     }))
 }
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 13b5c262bf7..b0124977c9f 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -23,7 +23,10 @@ use middle::ty::{ty_param, ty_ptr, ty_rptr, ty_tup, ty_open};
 use middle::ty::{ty_unboxed_closure};
 use middle::ty::{ty_uniq, ty_trait, ty_int, ty_uint, ty_infer};
 use middle::ty;
+use middle::ty_fold::TypeFoldable;
 
+use std::collections::HashMap;
+use std::hash::{Hash, Hasher};
 use std::rc::Rc;
 use syntax::abi;
 use syntax::ast_map;
@@ -40,7 +43,7 @@ pub trait Repr<'tcx> for Sized? {
 }
 
 /// Produces a string suitable for showing to the user.
-pub trait UserString<'tcx> {
+pub trait UserString<'tcx> : Repr<'tcx> {
     fn user_string(&self, tcx: &ctxt<'tcx>) -> String;
 }
 
@@ -248,21 +251,12 @@ pub fn vec_map_to_string<T, F>(ts: &[T], f: F) -> String where
     format!("[{}]", tstrs.connect(", "))
 }
 
-pub fn fn_sig_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::FnSig<'tcx>) -> String {
-    format!("fn{} -> {}", typ.inputs.repr(cx), typ.output.repr(cx))
-}
-
-pub fn trait_ref_to_string<'tcx>(cx: &ctxt<'tcx>,
-                                 trait_ref: &ty::TraitRef<'tcx>) -> String {
-    trait_ref.user_string(cx).to_string()
-}
-
 pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String {
     fn bare_fn_to_string<'tcx>(cx: &ctxt<'tcx>,
                                unsafety: ast::Unsafety,
                                abi: abi::Abi,
                                ident: Option<ast::Ident>,
-                               sig: &ty::FnSig<'tcx>)
+                               sig: &ty::PolyFnSig<'tcx>)
                                -> String {
         let mut s = String::new();
         match unsafety {
@@ -336,15 +330,15 @@ pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String {
                                 s: &mut String,
                                 bra: char,
                                 ket: char,
-                                sig: &ty::FnSig<'tcx>,
+                                sig: &ty::PolyFnSig<'tcx>,
                                 bounds: &str) {
         s.push(bra);
-        let strs = sig.inputs
+        let strs = sig.0.inputs
             .iter()
             .map(|a| ty_to_string(cx, *a))
             .collect::<Vec<_>>();
         s.push_str(strs.connect(", ").as_slice());
-        if sig.variadic {
+        if sig.0.variadic {
             s.push_str(", ...");
         }
         s.push(ket);
@@ -354,7 +348,7 @@ pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String {
             s.push_str(bounds);
         }
 
-        match sig.output {
+        match sig.0.output {
             ty::FnConverging(t) => {
                 if !ty::type_is_nil(t) {
                    s.push_str(" -> ");
@@ -374,8 +368,8 @@ pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String {
             ty::IntVar(ref vid) if print_var_ids => vid.repr(cx),
             ty::FloatVar(ref vid) if print_var_ids => vid.repr(cx),
             ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_) => format!("_"),
-            ty::SkolemizedTy(v) => format!("SkolemizedTy({})", v),
-            ty::SkolemizedIntTy(v) => format!("SkolemizedIntTy({})", v)
+            ty::FreshTy(v) => format!("FreshTy({})", v),
+            ty::FreshIntTy(v) => format!("FreshIntTy({})", v)
         }
     }
 
@@ -433,16 +427,11 @@ pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String {
         ty_trait(box ty::TyTrait {
             ref principal, ref bounds
         }) => {
-            let base = ty::item_path_str(cx, principal.def_id);
-            let trait_def = ty::lookup_trait_def(cx, principal.def_id);
-            let did = trait_def.trait_ref.def_id;
-            let ty = parameterized(cx, base.as_slice(),
-                                   &principal.substs, &trait_def.generics,
-                                   did);
+            let principal = principal.user_string(cx);
             let bound_str = bounds.user_string(cx);
             let bound_sep = if bound_str.is_empty() { "" } else { " + " };
             format!("{}{}{}",
-                    ty,
+                    principal,
                     bound_sep,
                     bound_str)
         }
@@ -749,7 +738,7 @@ impl<'tcx> Repr<'tcx> for ty::TraitRef<'tcx> {
         // tells you everything you need to know.
         let base = ty::item_path_str(tcx, self.def_id);
         let trait_def = ty::lookup_trait_def(tcx, self.def_id);
-        format!("<{} : {}>",
+        format!("TraitRef({}, {})",
                 self.substs.self_ty().repr(tcx),
                 parameterized(tcx, base.as_slice(), &self.substs, &trait_def.generics, self.def_id))
     }
@@ -1018,7 +1007,7 @@ impl<'tcx> Repr<'tcx> for ty::BareFnTy<'tcx> {
 
 impl<'tcx> Repr<'tcx> for ty::FnSig<'tcx> {
     fn repr(&self, tcx: &ctxt<'tcx>) -> String {
-        fn_sig_to_string(tcx, self)
+        format!("fn{} -> {}", self.inputs.repr(tcx), self.output.repr(tcx))
     }
 }
 
@@ -1161,7 +1150,9 @@ impl<'tcx> UserString<'tcx> for ty::BuiltinBounds {
     }
 }
 
-impl<'tcx> UserString<'tcx> for ty::TraitRef<'tcx> {
+impl<'tcx, T> UserString<'tcx> for ty::Binder<T>
+    where T : UserString<'tcx> + TypeFoldable<'tcx>
+{
     fn user_string(&self, tcx: &ctxt<'tcx>) -> String {
         // Replace any anonymous late-bound regions with named
         // variants, using gensym'd identifiers, so that we can
@@ -1169,7 +1160,7 @@ impl<'tcx> UserString<'tcx> for ty::TraitRef<'tcx> {
         // the output. We'll probably want to tweak this over time to
         // decide just how much information to give.
         let mut names = Vec::new();
-        let (trait_ref, _) = ty::replace_late_bound_regions(tcx, self, |br, debruijn| {
+        let (unbound_value, _) = ty::replace_late_bound_regions(tcx, self, |br, debruijn| {
             ty::ReLateBound(debruijn, match br {
                 ty::BrNamed(_, name) => {
                     names.push(token::get_name(name));
@@ -1178,7 +1169,7 @@ impl<'tcx> UserString<'tcx> for ty::TraitRef<'tcx> {
                 ty::BrAnon(_) |
                 ty::BrFresh(_) |
                 ty::BrEnv => {
-                    let name = token::gensym("r");
+                    let name = token::gensym("'r");
                     names.push(token::get_name(name));
                     ty::BrNamed(ast_util::local_def(ast::DUMMY_NODE_ID), name)
                 }
@@ -1186,19 +1177,21 @@ impl<'tcx> UserString<'tcx> for ty::TraitRef<'tcx> {
         });
         let names: Vec<_> = names.iter().map(|s| s.get()).collect();
 
-        // Let the base string be either `SomeTrait` for `for<'a,'b> SomeTrait`,
-        // depending on whether there are bound regions.
-        let path_str = ty::item_path_str(tcx, self.def_id);
-        let base =
-            if names.is_empty() {
-                path_str
-            } else {
-                format!("for<{}> {}", names.connect(","), path_str)
-            };
+        let value_str = unbound_value.user_string(tcx);
+        if names.len() == 0 {
+            value_str
+        } else {
+            format!("for<{}> {}", names.connect(","), value_str)
+        }
+    }
+}
 
+impl<'tcx> UserString<'tcx> for ty::TraitRef<'tcx> {
+    fn user_string(&self, tcx: &ctxt<'tcx>) -> String {
+        let path_str = ty::item_path_str(tcx, self.def_id);
         let trait_def = ty::lookup_trait_def(tcx, self.def_id);
-        let did = trait_def.trait_ref.def_id;
-        parameterized(tcx, base.as_slice(), &trait_ref.substs, &trait_def.generics, did)
+        parameterized(tcx, path_str.as_slice(), &self.substs,
+                      &trait_def.generics, self.def_id)
     }
 }
 
@@ -1340,6 +1333,73 @@ impl<'tcx, A:Repr<'tcx>, B:Repr<'tcx>> Repr<'tcx> for (A,B) {
 
 impl<'tcx, T:Repr<'tcx>> Repr<'tcx> for ty::Binder<T> {
     fn repr(&self, tcx: &ctxt<'tcx>) -> String {
-        format!("Binder({})", self.value.repr(tcx))
+        format!("Binder({})", self.0.repr(tcx))
+    }
+}
+
+impl<'tcx, S, H, K, V> Repr<'tcx> for HashMap<K,V,H>
+    where K : Hash<S> + Eq + Repr<'tcx>,
+          V : Repr<'tcx>,
+          H : Hasher<S>
+{
+    fn repr(&self, tcx: &ctxt<'tcx>) -> String {
+        format!("HashMap({})",
+                self.iter()
+                    .map(|(k,v)| format!("{} => {}", k.repr(tcx), v.repr(tcx)))
+                    .collect::<Vec<String>>()
+                    .connect(", "))
+    }
+}
+
+impl<'tcx, T, U> Repr<'tcx> for ty::OutlivesPredicate<T,U>
+    where T : Repr<'tcx> + TypeFoldable<'tcx>,
+          U : Repr<'tcx> + TypeFoldable<'tcx>,
+{
+    fn repr(&self, tcx: &ctxt<'tcx>) -> String {
+        format!("OutlivesPredicate({}, {})",
+                self.0.repr(tcx),
+                self.1.repr(tcx))
+    }
+}
+
+impl<'tcx, T, U> UserString<'tcx> for ty::OutlivesPredicate<T,U>
+    where T : UserString<'tcx> + TypeFoldable<'tcx>,
+          U : UserString<'tcx> + TypeFoldable<'tcx>,
+{
+    fn user_string(&self, tcx: &ctxt<'tcx>) -> String {
+        format!("{} : {}",
+                self.0.user_string(tcx),
+                self.1.user_string(tcx))
+    }
+}
+
+impl<'tcx> Repr<'tcx> for ty::EquatePredicate<'tcx> {
+    fn repr(&self, tcx: &ctxt<'tcx>) -> String {
+        format!("EquatePredicate({}, {})",
+                self.0.repr(tcx),
+                self.1.repr(tcx))
+    }
+}
+
+impl<'tcx> UserString<'tcx> for ty::EquatePredicate<'tcx> {
+    fn user_string(&self, tcx: &ctxt<'tcx>) -> String {
+        format!("{} == {}",
+                self.0.user_string(tcx),
+                self.1.user_string(tcx))
+    }
+}
+
+impl<'tcx> UserString<'tcx> for ty::Predicate<'tcx> {
+    fn user_string(&self, tcx: &ctxt<'tcx>) -> String {
+        match *self {
+            ty::Predicate::Trait(ref trait_ref) => {
+                format!("{} : {}",
+                        trait_ref.self_ty().user_string(tcx),
+                        trait_ref.user_string(tcx))
+            }
+            ty::Predicate::Equate(ref predicate) => predicate.user_string(tcx),
+            ty::Predicate::RegionOutlives(ref predicate) => predicate.user_string(tcx),
+            ty::Predicate::TypeOutlives(ref predicate) => predicate.user_string(tcx),
+        }
     }
 }
diff --git a/src/librustc/util/snapshot_vec.rs b/src/librustc/util/snapshot_vec.rs
index e80e8dc5351..749c39d7a6b 100644
--- a/src/librustc/util/snapshot_vec.rs
+++ b/src/librustc/util/snapshot_vec.rs
@@ -23,7 +23,7 @@ use self::UndoLog::*;
 use std::mem;
 
 #[deriving(PartialEq)]
-enum UndoLog<T,U> {
+pub enum UndoLog<T,U> {
     /// Indicates where a snapshot started.
     OpenSnapshot,
 
@@ -113,6 +113,12 @@ impl<T,U,D:SnapshotVecDelegate<T,U>> SnapshotVec<T,U,D> {
         Snapshot { length: length }
     }
 
+    pub fn actions_since_snapshot(&self,
+                                  snapshot: &Snapshot)
+                                  -> &[UndoLog<T,U>] {
+        self.undo_log[snapshot.length..]
+    }
+
     fn assert_open_snapshot(&self, snapshot: &Snapshot) {
         // Or else there was a failure to follow a stack discipline:
         assert!(self.undo_log.len() > snapshot.length);
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index 508af4c28e6..b2c661cc58a 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -25,6 +25,7 @@ use rustc_typeck::middle::infer::combine::Combine;
 use rustc_typeck::middle::infer;
 use rustc_typeck::middle::infer::lub::Lub;
 use rustc_typeck::middle::infer::glb::Glb;
+use rustc_typeck::middle::infer::sub::Sub;
 use rustc_typeck::util::ppaux::{ty_to_string, Repr, UserString};
 use rustc::session::{mod,config};
 use syntax::{abi, ast, ast_map, ast_util};
@@ -274,11 +275,11 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
             onceness: ast::Many,
             store: ty::RegionTraitStore(region_bound, ast::MutMutable),
             bounds: ty::region_existential_bound(region_bound),
-            sig: ty::FnSig {
+            sig: ty::Binder(ty::FnSig {
                 inputs: input_tys.to_vec(),
                 output: ty::FnConverging(output_ty),
                 variadic: false,
-            },
+            }),
             abi: abi::Rust,
         })
     }
@@ -341,6 +342,11 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
         infer::TypeTrace::dummy()
     }
 
+    pub fn sub(&self) -> Sub<'a, 'tcx> {
+        let trace = self.dummy_type_trace();
+        Sub(self.infcx.combine_fields(true, trace))
+    }
+
     pub fn lub(&self) -> Lub<'a, 'tcx> {
         let trace = self.dummy_type_trace();
         Lub(self.infcx.combine_fields(true, trace))
@@ -359,6 +365,33 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
         }
     }
 
+    /// Checks that `t1 <: t2` is true (this may register additional
+    /// region checks).
+    pub fn check_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) {
+        match self.sub().tys(t1, t2) {
+            Ok(_) => { }
+            Err(ref e) => {
+                panic!("unexpected error computing sub({},{}): {}",
+                       t1.repr(self.infcx.tcx),
+                       t2.repr(self.infcx.tcx),
+                       ty::type_err_to_str(self.infcx.tcx, e));
+            }
+        }
+    }
+
+    /// Checks that `t1 <: t2` is false (this may register additional
+    /// region checks).
+    pub fn check_not_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) {
+        match self.sub().tys(t1, t2) {
+            Err(_) => { }
+            Ok(_) => {
+                panic!("unexpected success computing sub({},{})",
+                       t1.repr(self.infcx.tcx),
+                       t2.repr(self.infcx.tcx));
+            }
+        }
+    }
+
     /// Checks that `LUB(t1,t2) == t_lub`
     pub fn check_lub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>, t_lub: Ty<'tcx>) {
         match self.lub().tys(t1, t2) {
@@ -422,6 +455,74 @@ fn contravariant_region_ptr_err() {
 }
 
 #[test]
+fn sub_free_bound_false() {
+    //! Test that:
+    //!
+    //!     fn(&'a int) <: for<'b> fn(&'b int)
+    //!
+    //! does NOT hold.
+
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let t_rptr_free1 = env.t_rptr_free(0, 1);
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
+        env.check_not_sub(env.t_fn(&[t_rptr_free1], ty::mk_int()),
+                          env.t_fn(&[t_rptr_bound1], ty::mk_int()));
+    })
+}
+
+#[test]
+fn sub_bound_free_true() {
+    //! Test that:
+    //!
+    //!     for<'a> fn(&'a int) <: fn(&'b int)
+    //!
+    //! DOES hold.
+
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
+        let t_rptr_free1 = env.t_rptr_free(0, 1);
+        env.check_sub(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_free1], ty::mk_int()));
+    })
+}
+
+#[test]
+fn sub_free_bound_false_infer() {
+    //! Test that:
+    //!
+    //!     fn(_#1) <: for<'b> fn(&'b int)
+    //!
+    //! does NOT hold for any instantiation of `_#1`.
+
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let t_infer1 = env.infcx.next_ty_var();
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
+        env.check_not_sub(env.t_fn(&[t_infer1], ty::mk_int()),
+                          env.t_fn(&[t_rptr_bound1], ty::mk_int()));
+    })
+}
+
+#[test]
+fn lub_free_bound_infer() {
+    //! Test result of:
+    //!
+    //!     LUB(fn(_#1), for<'b> fn(&'b int))
+    //!
+    //! This should yield `fn(&'_ int)`. We check
+    //! that it yields `fn(&'x int)` for some free `'x`,
+    //! anyhow.
+
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let t_infer1 = env.infcx.next_ty_var();
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
+        let t_rptr_free1 = env.t_rptr_free(0, 1);
+        env.check_lub(env.t_fn(&[t_infer1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_free1], ty::mk_int()));
+    });
+}
+
+#[test]
 fn lub_bound_bound() {
     test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
         let t_rptr_bound1 = env.t_rptr_late_bound(1);
@@ -525,6 +626,28 @@ fn glb_bound_free() {
 }
 
 #[test]
+fn glb_bound_free_infer() {
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
+        let t_infer1 = env.infcx.next_ty_var();
+
+        // compute GLB(fn(_) -> int, for<'b> fn(&'b int) -> int),
+        // which should yield for<'b> fn(&'b int) -> int
+        env.check_glb(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+                      env.t_fn(&[t_infer1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_bound1], ty::mk_int()));
+
+        // as a side-effect, computing GLB should unify `_` with
+        // `&'_ int`
+        let t_resolve1 = env.infcx.shallow_resolve(t_infer1);
+        match t_resolve1.sty {
+            ty::ty_rptr(..) => { }
+            _ => { panic!("t_resolve1={}", t_resolve1.repr(env.infcx.tcx)); }
+        }
+    })
+}
+
+#[test]
 fn glb_bound_static() {
     test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
         let t_rptr_bound1 = env.t_rptr_late_bound(1);
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index f1d839e916d..b947b1746fc 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -282,10 +282,10 @@ pub fn decl_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                               fn_ty: Ty<'tcx>, name: &str) -> ValueRef {
     let (inputs, output, abi, env) = match fn_ty.sty {
         ty::ty_bare_fn(ref f) => {
-            (f.sig.inputs.clone(), f.sig.output, f.abi, None)
+            (f.sig.0.inputs.clone(), f.sig.0.output, f.abi, None)
         }
         ty::ty_closure(ref f) => {
-            (f.sig.inputs.clone(), f.sig.output, f.abi, Some(Type::i8p(ccx)))
+            (f.sig.0.inputs.clone(), f.sig.0.output, f.abi, Some(Type::i8p(ccx)))
         }
         ty::ty_unboxed_closure(closure_did, _, ref substs) => {
             let unboxed_closures = ccx.tcx().unboxed_closures.borrow();
@@ -293,8 +293,8 @@ pub fn decl_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             let function_type = unboxed_closure.closure_type.clone();
             let self_type = self_type_for_unboxed_closure(ccx, closure_did, fn_ty);
             let llenvironment_type = type_of_explicit_arg(ccx, self_type);
-            (function_type.sig.inputs.iter().map(|t| t.subst(ccx.tcx(), substs)).collect(),
-             function_type.sig.output.subst(ccx.tcx(), substs),
+            (function_type.sig.0.inputs.iter().map(|t| t.subst(ccx.tcx(), substs)).collect(),
+             function_type.sig.0.output.subst(ccx.tcx(), substs),
              RustCall,
              Some(llenvironment_type))
         }
@@ -1998,7 +1998,7 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     let tcx = ccx.tcx();
 
     let result_ty = match ctor_ty.sty {
-        ty::ty_bare_fn(ref bft) => bft.sig.output.unwrap(),
+        ty::ty_bare_fn(ref bft) => bft.sig.0.output.unwrap(),
         _ => ccx.sess().bug(
             format!("trans_enum_variant_constructor: \
                      unexpected ctor return type {}",
@@ -2070,7 +2070,7 @@ fn trans_enum_variant_or_tuple_like_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx
     let ctor_ty = ctor_ty.subst(ccx.tcx(), param_substs);
 
     let result_ty = match ctor_ty.sty {
-        ty::ty_bare_fn(ref bft) => bft.sig.output,
+        ty::ty_bare_fn(ref bft) => bft.sig.0.output,
         _ => ccx.sess().bug(
             format!("trans_enum_variant_or_tuple_like_struct: \
                      unexpected ctor return type {}",
@@ -2439,7 +2439,7 @@ pub fn get_fn_llvm_attributes<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_ty: Ty<
     // at either 1 or 2 depending on whether there's an env slot or not
     let mut first_arg_offset = if has_env { 2 } else { 1 };
     let mut attrs = llvm::AttrBuilder::new();
-    let ret_ty = fn_sig.output;
+    let ret_ty = fn_sig.0.output;
 
     // These have an odd calling convention, so we need to manually
     // unpack the input ty's
@@ -2447,15 +2447,15 @@ pub fn get_fn_llvm_attributes<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_ty: Ty<
         ty::ty_unboxed_closure(_, _, _) => {
             assert!(abi == RustCall);
 
-            match fn_sig.inputs[0].sty {
+            match fn_sig.0.inputs[0].sty {
                 ty::ty_tup(ref inputs) => inputs.clone(),
                 _ => ccx.sess().bug("expected tuple'd inputs")
             }
         },
         ty::ty_bare_fn(_) if abi == RustCall => {
-            let mut inputs = vec![fn_sig.inputs[0]];
+            let mut inputs = vec![fn_sig.0.inputs[0]];
 
-            match fn_sig.inputs[1].sty {
+            match fn_sig.0.inputs[1].sty {
                 ty::ty_tup(ref t_in) => {
                     inputs.push_all(t_in.as_slice());
                     inputs
@@ -2463,7 +2463,7 @@ pub fn get_fn_llvm_attributes<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_ty: Ty<
                 _ => ccx.sess().bug("expected tuple'd inputs")
             }
         }
-        _ => fn_sig.inputs.clone()
+        _ => fn_sig.0.inputs.clone()
     };
 
     if let ty::FnConverging(ret_ty) = ret_ty {
diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs
index ff2f686fff8..f8303a6f030 100644
--- a/src/librustc_trans/trans/callee.rs
+++ b/src/librustc_trans/trans/callee.rs
@@ -280,9 +280,9 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
         match bare_fn_ty.sty {
             ty::ty_bare_fn(ty::BareFnTy { unsafety: ast::Unsafety::Normal,
                                           abi: synabi::Rust,
-                                          sig: ty::FnSig { inputs: ref input_tys,
-                                                           output: output_ty,
-                                                           variadic: false }}) =>
+                                          sig: ty::Binder(ty::FnSig { inputs: ref input_tys,
+                                                                      output: output_ty,
+                                                                      variadic: false })}) =>
             {
                 (input_tys, output_ty)
             }
@@ -296,12 +296,12 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
     let tuple_fn_ty = ty::mk_bare_fn(tcx,
                                      ty::BareFnTy { unsafety: ast::Unsafety::Normal,
                                                     abi: synabi::RustCall,
-                                                    sig: ty::FnSig {
+                                                    sig: ty::Binder(ty::FnSig {
                                                         inputs: vec![bare_fn_ty_ref,
                                                                      tuple_input_ty],
                                                         output: output_ty,
                                                         variadic: false
-                                                    }});
+                                                    })});
     debug!("tuple_fn_ty: {}", tuple_fn_ty.repr(tcx));
 
     //
@@ -422,7 +422,6 @@ pub fn trans_fn_ref_with_substs<'blk, 'tcx>(
             match impl_or_trait_item {
                 ty::MethodTraitItem(method) => {
                     let trait_ref = ty::impl_trait_ref(tcx, impl_id).unwrap();
-                    let trait_ref = ty::erase_late_bound_regions(tcx, &trait_ref);
 
                     // Compute the first substitution
                     let first_subst =
@@ -657,8 +656,8 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
     let mut bcx = callee.bcx;
 
     let (abi, ret_ty) = match callee_ty.sty {
-        ty::ty_bare_fn(ref f) => (f.abi, f.sig.output),
-        ty::ty_closure(ref f) => (f.abi, f.sig.output),
+        ty::ty_bare_fn(ref f) => (f.abi, f.sig.0.output),
+        ty::ty_closure(ref f) => (f.abi, f.sig.0.output),
         _ => panic!("expected bare rust fn or closure in trans_call_inner")
     };
 
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs
index 1811388662c..af3daf224e3 100644
--- a/src/librustc_trans/trans/closure.rs
+++ b/src/librustc_trans/trans/closure.rs
@@ -658,9 +658,9 @@ pub fn get_wrapper_for_bare_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
     let arena = TypedArena::new();
     let empty_param_substs = Substs::trans_empty();
-    let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, true, f.sig.output,
+    let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, true, f.sig.0.output,
                           &empty_param_substs, None, &arena);
-    let bcx = init_function(&fcx, true, f.sig.output);
+    let bcx = init_function(&fcx, true, f.sig.0.output);
 
     let args = create_datums_for_fn_args(&fcx,
                                          ty::ty_fn_args(closure_ty)
@@ -676,7 +676,7 @@ pub fn get_wrapper_for_bare_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     llargs.extend(args.iter().map(|arg| arg.val));
 
     let retval = Call(bcx, fn_ptr, llargs.as_slice(), None);
-    match f.sig.output {
+    match f.sig.0.output {
         ty::FnConverging(output_type) => {
             if return_type_is_void(ccx, output_type) || fcx.llretslotptr.get().is_some() {
                 RetVoid(bcx);
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index 83938fa3357..a8e88eca0e1 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -764,7 +764,7 @@ pub fn expr_ty_adjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ex: &ast::Expr) -> T
 /// guarantee to us that all nested obligations *could be* resolved if we wanted to.
 pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                     span: Span,
-                                    trait_ref: Rc<ty::TraitRef<'tcx>>)
+                                    trait_ref: Rc<ty::PolyTraitRef<'tcx>>)
                                     -> traits::Vtable<'tcx, ()>
 {
     let tcx = ccx.tcx();
@@ -783,7 +783,7 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
     debug!("trans fulfill_obligation: trait_ref={}", trait_ref.repr(ccx.tcx()));
 
-    ty::populate_implementations_for_trait_if_necessary(tcx, trait_ref.def_id);
+    ty::populate_implementations_for_trait_if_necessary(tcx, trait_ref.def_id());
     let infcx = infer::new_infer_ctxt(tcx);
 
     // Parameter environment is used to give details about type parameters,
@@ -848,12 +848,12 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         }
     }
 
-    // Use skolemize to simultaneously replace all type variables with
+    // Use freshen to simultaneously replace all type variables with
     // their bindings and replace all regions with 'static.  This is
     // sort of overkill because we do not expect there to be any
-    // unbound type variables, hence no skolemized types should ever
-    // be inserted.
-    let vtable = vtable.fold_with(&mut infcx.skolemizer());
+    // unbound type variables, hence no `TyFresh` types should ever be
+    // inserted.
+    let vtable = vtable.fold_with(&mut infcx.freshener());
 
     info!("Cache miss: {}", trait_ref.repr(ccx.tcx()));
     ccx.trait_cache().borrow_mut().insert(trait_ref,
diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs
index 89fa6a72e88..af003b01157 100644
--- a/src/librustc_trans/trans/context.rs
+++ b/src/librustc_trans/trans/context.rs
@@ -99,7 +99,7 @@ pub struct LocalCrateContext<'tcx> {
     monomorphized: RefCell<FnvHashMap<MonoId<'tcx>, ValueRef>>,
     monomorphizing: RefCell<DefIdMap<uint>>,
     /// Cache generated vtables
-    vtables: RefCell<FnvHashMap<(Ty<'tcx>, Rc<ty::TraitRef<'tcx>>), ValueRef>>,
+    vtables: RefCell<FnvHashMap<(Ty<'tcx>, Rc<ty::PolyTraitRef<'tcx>>), ValueRef>>,
     /// Cache of constant strings,
     const_cstr_cache: RefCell<FnvHashMap<InternedString, ValueRef>>,
 
@@ -150,7 +150,7 @@ pub struct LocalCrateContext<'tcx> {
     /// contexts around the same size.
     n_llvm_insns: Cell<uint>,
 
-    trait_cache: RefCell<FnvHashMap<Rc<ty::TraitRef<'tcx>>,
+    trait_cache: RefCell<FnvHashMap<Rc<ty::PolyTraitRef<'tcx>>,
                                     traits::Vtable<'tcx, ()>>>,
 }
 
@@ -601,7 +601,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         &self.local.monomorphizing
     }
 
-    pub fn vtables<'a>(&'a self) -> &'a RefCell<FnvHashMap<(Ty<'tcx>, Rc<ty::TraitRef<'tcx>>),
+    pub fn vtables<'a>(&'a self) -> &'a RefCell<FnvHashMap<(Ty<'tcx>, Rc<ty::PolyTraitRef<'tcx>>),
                                                             ValueRef>> {
         &self.local.vtables
     }
@@ -699,7 +699,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         self.local.n_llvm_insns.set(self.local.n_llvm_insns.get() + 1);
     }
 
-    pub fn trait_cache(&self) -> &RefCell<FnvHashMap<Rc<ty::TraitRef<'tcx>>,
+    pub fn trait_cache(&self) -> &RefCell<FnvHashMap<Rc<ty::PolyTraitRef<'tcx>>,
                                                      traits::Vtable<'tcx, ()>>> {
         &self.local.trait_cache
     }
diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs
index e9730f7af0e..96c39b5796e 100644
--- a/src/librustc_trans/trans/debuginfo.rs
+++ b/src/librustc_trans/trans/debuginfo.rs
@@ -429,8 +429,8 @@ impl<'tcx> TypeMap<'tcx> {
 
                 from_def_id_and_substs(self,
                                        cx,
-                                       trait_data.principal.def_id,
-                                       &trait_data.principal.substs,
+                                       trait_data.principal.def_id(),
+                                       trait_data.principal.substs(),
                                        &mut unique_type_id);
             },
             ty::ty_bare_fn(ty::BareFnTy{ unsafety, abi, ref sig } ) => {
@@ -442,7 +442,7 @@ impl<'tcx> TypeMap<'tcx> {
 
                 unique_type_id.push_str(" fn(");
 
-                for &parameter_type in sig.inputs.iter() {
+                for &parameter_type in sig.0.inputs.iter() {
                     let parameter_type_id =
                         self.get_unique_type_id_of_type(cx, parameter_type);
                     let parameter_type_id =
@@ -451,12 +451,12 @@ impl<'tcx> TypeMap<'tcx> {
                     unique_type_id.push(',');
                 }
 
-                if sig.variadic {
+                if sig.0.variadic {
                     unique_type_id.push_str("...");
                 }
 
                 unique_type_id.push_str(")->");
-                match sig.output {
+                match sig.0.output {
                     ty::FnConverging(ret_ty) => {
                         let return_type_id = self.get_unique_type_id_of_type(cx, ret_ty);
                         let return_type_id = self.get_unique_type_id_as_string(return_type_id);
@@ -575,7 +575,7 @@ impl<'tcx> TypeMap<'tcx> {
             }
         };
 
-        for &parameter_type in sig.inputs.iter() {
+        for &parameter_type in sig.0.inputs.iter() {
             let parameter_type_id =
                 self.get_unique_type_id_of_type(cx, parameter_type);
             let parameter_type_id =
@@ -584,13 +584,13 @@ impl<'tcx> TypeMap<'tcx> {
             unique_type_id.push(',');
         }
 
-        if sig.variadic {
+        if sig.0.variadic {
             unique_type_id.push_str("...");
         }
 
         unique_type_id.push_str("|->");
 
-        match sig.output {
+        match sig.0.output {
             ty::FnConverging(ret_ty) => {
                 let return_type_id = self.get_unique_type_id_of_type(cx, ret_ty);
                 let return_type_id = self.get_unique_type_id_as_string(return_type_id);
@@ -2787,13 +2787,13 @@ fn vec_slice_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
 fn subroutine_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                       unique_type_id: UniqueTypeId,
-                                      signature: &ty::FnSig<'tcx>,
+                                      signature: &ty::PolyFnSig<'tcx>,
                                       span: Span)
                                       -> MetadataCreationResult {
-    let mut signature_metadata: Vec<DIType> = Vec::with_capacity(signature.inputs.len() + 1);
+    let mut signature_metadata: Vec<DIType> = Vec::with_capacity(signature.0.inputs.len() + 1);
 
     // return type
-    signature_metadata.push(match signature.output {
+    signature_metadata.push(match signature.0.output {
         ty::FnConverging(ret_ty) => match ret_ty.sty {
             ty::ty_tup(ref tys) if tys.is_empty() => ptr::null_mut(),
             _ => type_metadata(cx, ret_ty, span)
@@ -2802,7 +2802,7 @@ fn subroutine_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     });
 
     // regular arguments
-    for &argument_type in signature.inputs.iter() {
+    for &argument_type in signature.0.inputs.iter() {
         signature_metadata.push(type_metadata(cx, argument_type, span));
     }
 
@@ -2834,7 +2834,7 @@ fn trait_pointer_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     // But it does not describe the trait's methods.
 
     let def_id = match trait_type.sty {
-        ty::ty_trait(box ty::TyTrait { ref principal, .. }) => principal.def_id,
+        ty::ty_trait(box ty::TyTrait { ref principal, .. }) => principal.def_id(),
         _ => {
             let pp_type_name = ppaux::ty_to_string(cx.tcx(), trait_type);
             cx.sess().bug(format!("debuginfo: Unexpected trait-object type in \
@@ -3765,8 +3765,8 @@ fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             output.push(']');
         },
         ty::ty_trait(ref trait_data) => {
-            push_item_name(cx, trait_data.principal.def_id, false, output);
-            push_type_params(cx, &trait_data.principal.substs, output);
+            push_item_name(cx, trait_data.principal.def_id(), false, output);
+            push_type_params(cx, trait_data.principal.substs(), output);
         },
         ty::ty_bare_fn(ty::BareFnTy{ unsafety, abi, ref sig } ) => {
             if unsafety == ast::Unsafety::Unsafe {
@@ -3781,8 +3781,8 @@ fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
             output.push_str("fn(");
 
-            if sig.inputs.len() > 0 {
-                for &parameter_type in sig.inputs.iter() {
+            if sig.0.inputs.len() > 0 {
+                for &parameter_type in sig.0.inputs.iter() {
                     push_debuginfo_type_name(cx, parameter_type, true, output);
                     output.push_str(", ");
                 }
@@ -3790,8 +3790,8 @@ fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 output.pop();
             }
 
-            if sig.variadic {
-                if sig.inputs.len() > 0 {
+            if sig.0.variadic {
+                if sig.0.inputs.len() > 0 {
                     output.push_str(", ...");
                 } else {
                     output.push_str("...");
@@ -3800,7 +3800,7 @@ fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
             output.push(')');
 
-            match sig.output {
+            match sig.0.output {
                 ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {}
                 ty::FnConverging(result_type) => {
                     output.push_str(" -> ");
@@ -3841,8 +3841,8 @@ fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 }
             };
 
-            if sig.inputs.len() > 0 {
-                for &parameter_type in sig.inputs.iter() {
+            if sig.0.inputs.len() > 0 {
+                for &parameter_type in sig.0.inputs.iter() {
                     push_debuginfo_type_name(cx, parameter_type, true, output);
                     output.push_str(", ");
                 }
@@ -3850,8 +3850,8 @@ fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 output.pop();
             }
 
-            if sig.variadic {
-                if sig.inputs.len() > 0 {
+            if sig.0.variadic {
+                if sig.0.inputs.len() > 0 {
                     output.push_str(", ...");
                 } else {
                     output.push_str("...");
@@ -3860,7 +3860,7 @@ fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
             output.push(param_list_closing_char);
 
-            match sig.output {
+            match sig.0.output {
                 ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {}
                 ty::FnConverging(result_type) => {
                     output.push_str(" -> ");
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 304142453a9..db44e0ce271 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -316,10 +316,10 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                           bcx.ty_to_string(unadjusted_ty)).as_slice())
             },
             &ty::UnsizeVtable(ty::TyTrait { ref principal, .. }, _) => {
-                let substs = principal.substs.with_self_ty(unadjusted_ty).erase_regions();
+                let substs = principal.substs().with_self_ty(unadjusted_ty).erase_regions();
                 let trait_ref =
-                    Rc::new(ty::TraitRef { def_id: principal.def_id,
-                                           substs: substs });
+                    Rc::new(ty::Binder(ty::TraitRef { def_id: principal.def_id(),
+                                                      substs: substs }));
                 let trait_ref = trait_ref.subst(bcx.tcx(), bcx.fcx.param_substs);
                 let box_ty = mk_ty(unadjusted_ty);
                 PointerCast(bcx,
diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs
index 615d5467f84..d0720319930 100644
--- a/src/librustc_trans/trans/foreign.rs
+++ b/src/librustc_trans/trans/foreign.rs
@@ -22,7 +22,6 @@ use trans::machine;
 use trans::type_::Type;
 use trans::type_of::*;
 use trans::type_of;
-use middle::ty::FnSig;
 use middle::ty::{mod, Ty};
 use middle::subst::{Subst, Substs};
 use std::cmp;
@@ -41,7 +40,7 @@ use util::ppaux::Repr;
 
 struct ForeignTypes<'tcx> {
     /// Rust signature of the function
-    fn_sig: ty::FnSig<'tcx>,
+    fn_sig: ty::PolyFnSig<'tcx>,
 
     /// Adapter object for handling native ABI rules (trust me, you
     /// don't want to know)
@@ -179,7 +178,7 @@ pub fn register_foreign_item_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
     // Make sure the calling convention is right for variadic functions
     // (should've been caught if not in typeck)
-    if tys.fn_sig.variadic {
+    if tys.fn_sig.0.variadic {
         assert!(cc == llvm::CCallConv);
     }
 
@@ -386,7 +385,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         debug!("llforeign_ret_ty={}", ccx.tn().type_to_string(llforeign_ret_ty));
 
         if llrust_ret_ty == llforeign_ret_ty {
-            match fn_sig.output {
+            match fn_sig.0.output {
                 ty::FnConverging(result_ty) => {
                     base::store_ty(bcx, llforeign_retval, llretptr, result_ty)
                 }
@@ -632,7 +631,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         };
 
         // Push Rust return pointer, using null if it will be unused.
-        let rust_uses_outptr = match tys.fn_sig.output {
+        let rust_uses_outptr = match tys.fn_sig.0.output {
             ty::FnConverging(ret_ty) => type_of::return_uses_outptr(ccx, ret_ty),
             ty::FnDiverging => false
         };
@@ -665,7 +664,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                             return_ty={}",
                            ccx.tn().val_to_string(slot),
                            ccx.tn().type_to_string(llrust_ret_ty),
-                           tys.fn_sig.output.repr(tcx));
+                           tys.fn_sig.0.output.repr(tcx));
                     llrust_args.push(slot);
                     return_alloca = Some(slot);
                 }
@@ -680,8 +679,8 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         // Build up the arguments to the call to the rust function.
         // Careful to adapt for cases where the native convention uses
         // a pointer and Rust does not or vice versa.
-        for i in range(0, tys.fn_sig.inputs.len()) {
-            let rust_ty = tys.fn_sig.inputs[i];
+        for i in range(0, tys.fn_sig.0.inputs.len()) {
+            let rust_ty = tys.fn_sig.0.inputs[i];
             let llrust_ty = tys.llsig.llarg_tys[i];
             let rust_indirect = type_of::arg_is_indirect(ccx, rust_ty);
             let llforeign_arg_ty = tys.fn_ty.arg_tys[i];
@@ -826,10 +825,10 @@ pub fn link_name(i: &ast::ForeignItem) -> InternedString {
 /// because foreign functions just plain ignore modes. They also don't pass aggregate values by
 /// pointer like we do.
 fn foreign_signature<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                               fn_sig: &ty::FnSig<'tcx>, arg_tys: &[Ty<'tcx>])
+                               fn_sig: &ty::PolyFnSig<'tcx>, arg_tys: &[Ty<'tcx>])
                                -> LlvmSignature {
     let llarg_tys = arg_tys.iter().map(|&arg| arg_type_of(ccx, arg)).collect();
-    let (llret_ty, ret_def) = match fn_sig.output {
+    let (llret_ty, ret_def) = match fn_sig.0.output {
         ty::FnConverging(ret_ty) =>
             (type_of::arg_type_of(ccx, ret_ty), !return_type_is_void(ccx, ret_ty)),
         ty::FnDiverging =>
@@ -853,7 +852,7 @@ fn foreign_types_for_fn_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         ty::ty_bare_fn(ref fn_ty) => fn_ty.sig.clone(),
         _ => ccx.sess().bug("foreign_types_for_fn_ty called on non-function type")
     };
-    let llsig = foreign_signature(ccx, &fn_sig, fn_sig.inputs.as_slice());
+    let llsig = foreign_signature(ccx, &fn_sig, fn_sig.0.inputs.as_slice());
     let fn_ty = cabi::compute_abi_info(ccx,
                                        llsig.llarg_tys.as_slice(),
                                        llsig.llret_ty,
@@ -913,7 +912,7 @@ fn lltype_for_fn_from_foreign_types(ccx: &CrateContext, tys: &ForeignTypes) -> T
         llargument_tys.push(llarg_ty);
     }
 
-    if tys.fn_sig.variadic {
+    if tys.fn_sig.0.variadic {
         Type::variadic_func(llargument_tys.as_slice(), &llreturn_ty)
     } else {
         Type::func(llargument_tys.as_slice(), &llreturn_ty)
diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs
index 4575d8a41e5..dea095ecaf5 100644
--- a/src/librustc_trans/trans/glue.rs
+++ b/src/librustc_trans/trans/glue.rs
@@ -227,8 +227,8 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let fty = ty::lookup_item_type(bcx.tcx(), dtor_did).ty.subst(bcx.tcx(), substs);
     let self_ty = match fty.sty {
         ty::ty_bare_fn(ref f) => {
-            assert!(f.sig.inputs.len() == 1);
-            f.sig.inputs[0]
+            assert!(f.sig.0.inputs.len() == 1);
+            f.sig.0.inputs[0]
         }
         _ => bcx.sess().bug(format!("Expected function type, found {}",
                                     bcx.ty_to_string(fty)).as_slice())
diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs
index 890652401d7..a6f7c849f4d 100644
--- a/src/librustc_trans/trans/intrinsic.rs
+++ b/src/librustc_trans/trans/intrinsic.rs
@@ -150,7 +150,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     let tcx = bcx.tcx();
 
     let ret_ty = match callee_ty.sty {
-        ty::ty_bare_fn(ref f) => f.sig.output,
+        ty::ty_bare_fn(ref f) => f.sig.0.output,
         _ => panic!("expected bare_fn in trans_intrinsic_call")
     };
     let foreign_item = tcx.map.expect_foreign_item(node);
diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs
index 9a2bc38acdf..f1c3c9be396 100644
--- a/src/librustc_trans/trans/meth.rs
+++ b/src/librustc_trans/trans/meth.rs
@@ -133,16 +133,16 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             method_num
         }) => {
             let trait_ref =
-                Rc::new(trait_ref.subst(bcx.tcx(), bcx.fcx.param_substs));
+                Rc::new(ty::Binder((**trait_ref).subst(bcx.tcx(), bcx.fcx.param_substs)));
             let span = bcx.tcx().map.span(method_call.expr_id);
             debug!("method_call={} trait_ref={}",
                    method_call,
                    trait_ref.repr(bcx.tcx()));
             let origin = fulfill_obligation(bcx.ccx(),
                                             span,
-                                            (*trait_ref).clone());
+                                            trait_ref.clone());
             debug!("origin = {}", origin.repr(bcx.tcx()));
-            trans_monomorphized_callee(bcx, method_call, trait_ref.def_id,
+            trans_monomorphized_callee(bcx, method_call, trait_ref.def_id(),
                                        method_num, origin)
         }
 
@@ -239,8 +239,8 @@ pub fn trans_static_method_callee(bcx: Block,
                                              rcvr_assoc,
                                              Vec::new()));
     debug!("trait_substs={}", trait_substs.repr(bcx.tcx()));
-    let trait_ref = Rc::new(ty::TraitRef { def_id: trait_id,
-                                           substs: trait_substs });
+    let trait_ref = Rc::new(ty::Binder(ty::TraitRef { def_id: trait_id,
+                                                      substs: trait_substs }));
     let vtbl = fulfill_obligation(bcx.ccx(),
                                   DUMMY_SP,
                                   trait_ref);
@@ -480,8 +480,8 @@ pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         ty::ty_bare_fn(ref f) if f.abi == Rust || f.abi == RustCall => {
             type_of_rust_fn(ccx,
                             Some(Type::i8p(ccx)),
-                            f.sig.inputs.slice_from(1),
-                            f.sig.output,
+                            f.sig.0.inputs.slice_from(1),
+                            f.sig.0.output,
                             f.abi)
         }
         _ => {
@@ -515,7 +515,7 @@ pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 /// This will hopefully change now that DST is underway.
 pub fn get_vtable<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                               box_ty: Ty<'tcx>,
-                              trait_ref: Rc<ty::TraitRef<'tcx>>)
+                              trait_ref: Rc<ty::PolyTraitRef<'tcx>>)
                               -> ValueRef
 {
     debug!("get_vtable(box_ty={}, trait_ref={})",
@@ -670,7 +670,7 @@ fn emit_vtable_methods<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 pub fn trans_trait_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                     datum: Datum<'tcx, Expr>,
                                     id: ast::NodeId,
-                                    trait_ref: Rc<ty::TraitRef<'tcx>>,
+                                    trait_ref: Rc<ty::PolyTraitRef<'tcx>>,
                                     dest: expr::Dest)
                                     -> Block<'blk, 'tcx> {
     let mut bcx = bcx;
diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs
index adc919c91bf..aa6fd7f0b39 100644
--- a/src/librustc_trans/trans/type_of.rs
+++ b/src/librustc_trans/trans/type_of.rs
@@ -146,16 +146,16 @@ pub fn type_of_fn_from_ty<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fty: Ty<'tcx>)
         ty::ty_closure(ref f) => {
             type_of_rust_fn(cx,
                             Some(Type::i8p(cx)),
-                            f.sig.inputs.as_slice(),
-                            f.sig.output,
+                            f.sig.0.inputs.as_slice(),
+                            f.sig.0.output,
                             f.abi)
         }
         ty::ty_bare_fn(ref f) => {
             if f.abi == abi::Rust || f.abi == abi::RustCall {
                 type_of_rust_fn(cx,
                                 None,
-                                f.sig.inputs.as_slice(),
-                                f.sig.output,
+                                f.sig.0.inputs.as_slice(),
+                                f.sig.0.output,
                                 f.abi)
             } else {
                 foreign::lltype_for_foreign_fn(cx, fty)
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 87eda76db29..4f4bebabead 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -53,8 +53,7 @@ use middle::def;
 use middle::resolve_lifetime as rl;
 use middle::subst::{FnSpace, TypeSpace, AssocSpace, SelfSpace, Subst, Substs};
 use middle::subst::{VecPerParamSpace};
-use middle::ty::{mod, Ty};
-use middle::ty_fold;
+use middle::ty::{mod, RegionEscape, Ty};
 use rscope::{mod, UnelidableRscope, RegionScope, SpecificRscope,
              ShiftedRscope, BindingRscope};
 use TypeAndSubsts;
@@ -524,6 +523,20 @@ fn convert_parenthesized_parameters<'tcx,AC>(this: &AC,
     vec![input_ty, output]
 }
 
+pub fn instantiate_poly_trait_ref<'tcx,AC,RS>(
+    this: &AC,
+    rscope: &RS,
+    ast_trait_ref: &ast::PolyTraitRef,
+    self_ty: Option<Ty<'tcx>>,
+    allow_eq: AllowEqConstraints)
+    -> Rc<ty::PolyTraitRef<'tcx>>
+    where AC: AstConv<'tcx>, RS: RegionScope
+{
+    let trait_ref =
+        instantiate_trait_ref(this, rscope, &ast_trait_ref.trait_ref, self_ty, allow_eq);
+    let trait_ref = (*trait_ref).clone();
+    Rc::new(ty::Binder(trait_ref)) // Ugh.
+}
 
 /// Instantiates the path for the given trait reference, assuming that it's
 /// bound to a valid trait type. Returns the def_id for the defining trait.
@@ -537,9 +550,7 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC,
                                          where AC: AstConv<'tcx>,
                                                RS: RegionScope
 {
-    match ::lookup_def_tcx(this.tcx(),
-                           ast_trait_ref.path.span,
-                           ast_trait_ref.ref_id) {
+    match ::lookup_def_tcx(this.tcx(), ast_trait_ref.path.span, ast_trait_ref.ref_id) {
         def::DefTrait(trait_def_id) => {
             let trait_ref = Rc::new(ast_path_to_trait_ref(this,
                                                           rscope,
@@ -749,7 +760,7 @@ fn ast_ty_to_trait_ref<'tcx,AC,RS>(this: &AC,
                                    rscope: &RS,
                                    ty: &ast::Ty,
                                    bounds: &[ast::TyParamBound])
-                                   -> Result<ty::TraitRef<'tcx>, ErrorReported>
+                                   -> Result<ty::PolyTraitRef<'tcx>, ErrorReported>
     where AC : AstConv<'tcx>, RS : RegionScope
 {
     /*!
@@ -767,12 +778,12 @@ fn ast_ty_to_trait_ref<'tcx,AC,RS>(this: &AC,
         ast::TyPath(ref path, id) => {
             match this.tcx().def_map.borrow().get(&id) {
                 Some(&def::DefTrait(trait_def_id)) => {
-                    return Ok(ast_path_to_trait_ref(this,
-                                                    rscope,
-                                                    trait_def_id,
-                                                    None,
-                                                    path,
-                                                    AllowEqConstraints::Allow));
+                    return Ok(ty::Binder(ast_path_to_trait_ref(this,
+                                                               rscope,
+                                                               trait_def_id,
+                                                               None,
+                                                               path,
+                                                               AllowEqConstraints::Allow)));
                 }
                 _ => {
                     span_err!(this.tcx().sess, ty.span, E0172, "expected a reference to a trait");
@@ -814,7 +825,7 @@ fn ast_ty_to_trait_ref<'tcx,AC,RS>(this: &AC,
 fn trait_ref_to_object_type<'tcx,AC,RS>(this: &AC,
                                         rscope: &RS,
                                         span: Span,
-                                        trait_ref: ty::TraitRef<'tcx>,
+                                        trait_ref: ty::PolyTraitRef<'tcx>,
                                         bounds: &[ast::TyParamBound])
                                         -> Ty<'tcx>
     where AC : AstConv<'tcx>, RS : RegionScope
@@ -982,12 +993,12 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
                     def::DefTrait(trait_def_id) => {
                         // N.B. this case overlaps somewhat with
                         // TyObjectSum, see that fn for details
-                        let result = ast_path_to_trait_ref(this,
-                                                           rscope,
-                                                           trait_def_id,
-                                                           None,
-                                                           path,
-                                                           AllowEqConstraints::Allow);
+                        let result = ty::Binder(ast_path_to_trait_ref(this,
+                                                                      rscope,
+                                                                      trait_def_id,
+                                                                      None,
+                                                                      path,
+                                                                      AllowEqConstraints::Allow));
                         trait_ref_to_object_type(this, rscope, path.span, result, &[])
                     }
                     def::DefTy(did, _) | def::DefStruct(did) => {
@@ -1039,7 +1050,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
                                 let ty_param_defs = tcx.ty_param_defs.borrow();
                                 let tp_def = &(*ty_param_defs)[did.node];
                                 let assoc_tys = tp_def.bounds.trait_bounds.iter()
-                                    .filter_map(|b| find_assoc_ty(this, &**b, assoc_ident))
+                                    .filter_map(|b| find_assoc_ty(this, &b.0, assoc_ident))
                                     .collect();
                                 (assoc_tys, token::get_name(tp_def.name).to_string())
                             }
@@ -1189,10 +1200,9 @@ fn ty_of_method_or_bare_fn<'a, 'tcx, AC: AstConv<'tcx>>(
     let (self_ty, mut implied_output_region) = match opt_self_info {
         None => (None, None),
         Some(self_info) => {
-            // Shift regions in the self type by 1 to account for the binding
-            // level introduced by the function itself.
-            let untransformed_self_ty =
-                ty_fold::shift_regions(this.tcx(), 1, &self_info.untransformed_self_ty);
+            // This type comes from an impl or trait; no late-bound
+            // regions should be present.
+            assert!(!self_info.untransformed_self_ty.has_escaping_regions());
 
             // Figure out and record the explicit self category.
             let explicit_self_category =
@@ -1203,19 +1213,19 @@ fn ty_of_method_or_bare_fn<'a, 'tcx, AC: AstConv<'tcx>>(
                     (None, None)
                 }
                 ty::ByValueExplicitSelfCategory => {
-                    (Some(untransformed_self_ty), None)
+                    (Some(self_info.untransformed_self_ty), None)
                 }
                 ty::ByReferenceExplicitSelfCategory(region, mutability) => {
                     (Some(ty::mk_rptr(this.tcx(),
                                       region,
                                       ty::mt {
-                                        ty: untransformed_self_ty,
+                                        ty: self_info.untransformed_self_ty,
                                         mutbl: mutability
                                       })),
                      Some(region))
                 }
                 ty::ByBoxExplicitSelfCategory => {
-                    (Some(ty::mk_uniq(this.tcx(), untransformed_self_ty)), None)
+                    (Some(ty::mk_uniq(this.tcx(), self_info.untransformed_self_ty)), None)
                 }
             }
         }
@@ -1267,11 +1277,11 @@ fn ty_of_method_or_bare_fn<'a, 'tcx, AC: AstConv<'tcx>>(
     (ty::BareFnTy {
         unsafety: unsafety,
         abi: abi,
-        sig: ty::FnSig {
+        sig: ty::Binder(ty::FnSig {
             inputs: self_and_input_tys,
             output: output_ty,
             variadic: decl.variadic
-        }
+        }),
     }, explicit_self_category_result)
 }
 
@@ -1409,9 +1419,9 @@ pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>(
         store: store,
         bounds: bounds,
         abi: abi,
-        sig: ty::FnSig {inputs: input_tys,
-                        output: output_ty,
-                        variadic: decl.variadic}
+        sig: ty::Binder(ty::FnSig {inputs: input_tys,
+                                   output: output_ty,
+                                   variadic: decl.variadic}),
     }
 }
 
@@ -1423,7 +1433,7 @@ pub fn conv_existential_bounds<'tcx, AC: AstConv<'tcx>, RS:RegionScope>(
     this: &AC,
     rscope: &RS,
     span: Span,
-    principal_trait_ref: Option<&ty::TraitRef<'tcx>>, // None for boxed closures
+    principal_trait_ref: Option<&ty::PolyTraitRef<'tcx>>, // None for boxed closures
     ast_bounds: &[ast::TyParamBound])
     -> ty::ExistentialBounds
 {
@@ -1450,11 +1460,11 @@ fn conv_ty_poly_trait_ref<'tcx, AC, RS>(
 
     let main_trait_bound = match partitioned_bounds.trait_bounds.remove(0) {
         Some(trait_bound) => {
-            Some(instantiate_trait_ref(this,
-                                       rscope,
-                                       &trait_bound.trait_ref,
-                                       None,
-                                       AllowEqConstraints::Allow))
+            Some(instantiate_poly_trait_ref(this,
+                                            rscope,
+                                            trait_bound,
+                                            None,
+                                            AllowEqConstraints::Allow))
         }
         None => {
             this.tcx().sess.span_err(
@@ -1481,7 +1491,7 @@ pub fn conv_existential_bounds_from_partitioned_bounds<'tcx, AC, RS>(
     this: &AC,
     rscope: &RS,
     span: Span,
-    principal_trait_ref: Option<&ty::TraitRef<'tcx>>, // None for boxed closures
+    principal_trait_ref: Option<&ty::PolyTraitRef<'tcx>>, // None for boxed closures
     partitioned_bounds: PartitionedBounds)
     -> ty::ExistentialBounds
     where AC: AstConv<'tcx>, RS:RegionScope
@@ -1519,7 +1529,7 @@ pub fn conv_existential_bounds_from_partitioned_bounds<'tcx, AC, RS>(
 fn compute_opt_region_bound<'tcx>(tcx: &ty::ctxt<'tcx>,
                                   span: Span,
                                   explicit_region_bounds: &[&ast::Lifetime],
-                                  principal_trait_ref: Option<&ty::TraitRef<'tcx>>,
+                                  principal_trait_ref: Option<&ty::PolyTraitRef<'tcx>>,
                                   builtin_bounds: ty::BuiltinBounds)
                                   -> Option<ty::Region>
 {
@@ -1579,7 +1589,7 @@ fn compute_region_bound<'tcx, AC: AstConv<'tcx>, RS:RegionScope>(
     rscope: &RS,
     span: Span,
     region_bounds: &[&ast::Lifetime],
-    principal_trait_ref: Option<&ty::TraitRef<'tcx>>, // None for closures
+    principal_trait_ref: Option<&ty::PolyTraitRef<'tcx>>, // None for closures
     builtin_bounds: ty::BuiltinBounds)
     -> ty::Region
 {
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index b88da5d9387..44cc5fce53d 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use middle::def;
-use middle::infer::{mod, resolve};
+use middle::infer;
 use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding, pat_is_const};
 use middle::subst::{Subst, Substs};
 use middle::ty::{mod, Ty};
@@ -18,6 +18,7 @@ use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
 use check::{instantiate_path, structurally_resolved_type, valid_range_bounds};
 use require_same_types;
 use util::nodemap::FnvHashMap;
+use util::ppaux::Repr;
 
 use std::cmp;
 use std::collections::hash_map::{Occupied, Vacant};
@@ -33,6 +34,10 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
     let fcx = pcx.fcx;
     let tcx = pcx.fcx.ccx.tcx;
 
+    debug!("check_pat(pat={},expected={})",
+           pat.repr(tcx),
+           expected.repr(tcx));
+
     match pat.node {
         ast::PatWild(_) => {
             fcx.write_ty(pat.id, expected);
@@ -143,11 +148,8 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
         ast::PatRegion(ref inner) => {
             let inner_ty = fcx.infcx().next_ty_var();
 
-            let mutbl = infer::resolve_type(
-                fcx.infcx(), Some(pat.span),
-                expected, resolve::try_resolve_tvar_shallow)
-                .ok()
-                .and_then(|t| ty::deref(t, true))
+            let mutbl =
+                ty::deref(fcx.infcx().shallow_resolve(expected), true)
                 .map_or(ast::MutImmutable, |mt| mt.mutbl);
 
             let mt = ty::mt { ty: inner_ty, mutbl: mutbl };
@@ -214,23 +216,21 @@ pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
                                       inner: &ast::Pat) -> bool {
     let fcx = pcx.fcx;
     let tcx = pcx.fcx.ccx.tcx;
-    match infer::resolve_type(
-        fcx.infcx(), Some(span),
-        expected, resolve::try_resolve_tvar_shallow) {
-        Ok(t) if pat_is_binding(&tcx.def_map, inner) => {
-            ty::deref(t, true).map_or(true, |mt| match mt.ty.sty {
-                ty::ty_trait(_) => {
-                    // This is "x = SomeTrait" being reduced from
-                    // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
-                    span_err!(tcx.sess, span, E0033,
-                        "type `{}` cannot be dereferenced",
-                        fcx.infcx().ty_to_string(t));
-                    false
-                }
-                _ => true
-            })
-        }
-        _ => true
+    if pat_is_binding(&tcx.def_map, inner) {
+        let expected = fcx.infcx().shallow_resolve(expected);
+        ty::deref(expected, true).map_or(true, |mt| match mt.ty.sty {
+            ty::ty_trait(_) => {
+                // This is "x = SomeTrait" being reduced from
+                // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
+                span_err!(tcx.sess, span, E0033,
+                          "type `{}` cannot be dereferenced",
+                          fcx.infcx().ty_to_string(expected));
+                false
+            }
+            _ => true
+        })
+    } else {
+        true
     }
 }
 
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index e3fec2c8b1d..2ade3040d6c 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -129,7 +129,7 @@ fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
 
     // Tuple up the arguments and insert the resulting function type into
     // the `unboxed_closures` table.
-    fn_ty.sig.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)];
+    fn_ty.sig.0.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.0.inputs)];
 
     debug!("unboxed_closure for {} --> sig={} kind={}",
            expr_def_id.repr(fcx.tcx()),
@@ -180,7 +180,7 @@ fn deduce_unboxed_closure_expectations_from_expected_type<'a,'tcx>(
 
 fn deduce_unboxed_closure_expectations_from_trait_ref<'a,'tcx>(
     fcx: &FnCtxt<'a,'tcx>,
-    trait_ref: &ty::TraitRef<'tcx>)
+    trait_ref: &ty::PolyTraitRef<'tcx>)
     -> Option<(ty::FnSig<'tcx>, ty::UnboxedClosureKind)>
 {
     let tcx = fcx.tcx();
@@ -188,15 +188,15 @@ fn deduce_unboxed_closure_expectations_from_trait_ref<'a,'tcx>(
     debug!("deduce_unboxed_closure_expectations_from_object_type({})",
            trait_ref.repr(tcx));
 
-    let kind = match tcx.lang_items.fn_trait_kind(trait_ref.def_id) {
+    let kind = match tcx.lang_items.fn_trait_kind(trait_ref.def_id()) {
         Some(k) => k,
         None => { return None; }
     };
 
     debug!("found object type {}", kind);
 
-    let arg_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 0);
-    let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(arg_param_ty);
+    let arg_param_ty = *trait_ref.substs().types.get(subst::TypeSpace, 0);
+    let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(&arg_param_ty);
     debug!("arg_param_ty {}", arg_param_ty.repr(tcx));
 
     let input_tys = match arg_param_ty.sty {
@@ -205,8 +205,8 @@ fn deduce_unboxed_closure_expectations_from_trait_ref<'a,'tcx>(
     };
     debug!("input_tys {}", input_tys.repr(tcx));
 
-    let ret_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 1);
-    let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(ret_param_ty);
+    let ret_param_ty = *trait_ref.substs().types.get(subst::TypeSpace, 1);
+    let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(&ret_param_ty);
     debug!("ret_param_ty {}", ret_param_ty.repr(tcx));
 
     let fn_sig = ty::FnSig {
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 980097eaead..9af9eaf75f5 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -12,8 +12,6 @@
 use check::FnCtxt;
 use middle::ty::{mod, Ty};
 use middle::infer;
-use middle::infer::resolve_type;
-use middle::infer::resolve::try_resolve_tvar_shallow;
 
 use std::result::Result::{Err, Ok};
 use syntax::ast;
@@ -63,12 +61,7 @@ pub fn coerce<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
     debug!("demand::coerce(expected = {}, expr_ty = {})",
            expected.repr(fcx.ccx.tcx),
            expr_ty.repr(fcx.ccx.tcx));
-    let expected = if ty::type_needs_infer(expected) {
-        resolve_type(fcx.infcx(),
-                     None,
-                     expected,
-                     try_resolve_tvar_shallow).unwrap_or(expected)
-    } else { expected };
+    let expected = fcx.infcx().resolve_type_vars_if_possible(&expected);
     match fcx.mk_assignty(expr, expr_ty, expected) {
       Ok(()) => { /* ok */ }
       Err(ref err) => {
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 7463652a931..2c220f29826 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -16,9 +16,9 @@ use middle::traits;
 use middle::ty::{mod, Ty};
 use middle::ty::{MethodCall, MethodCallee, MethodObject, MethodOrigin,
                  MethodParam, MethodStatic, MethodTraitObject, MethodTypeParam};
+use middle::ty_fold::TypeFoldable;
 use middle::infer;
 use middle::infer::InferCtxt;
-use middle::ty_fold::HigherRankedFoldable;
 use syntax::ast;
 use syntax::codemap::Span;
 use std::rc::Rc;
@@ -114,7 +114,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
 
         // Create the final `MethodCallee`.
         let fty = ty::mk_bare_fn(self.tcx(), ty::BareFnTy {
-            sig: method_sig,
+            sig: ty::Binder(method_sig),
             unsafety: pick.method_ty.fty.unsafety,
             abi: pick.method_ty.fty.abi.clone(),
         });
@@ -222,17 +222,19 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                     // argument type), but those cases have already
                     // been ruled out when we deemed the trait to be
                     // "object safe".
-                    let substs = data.principal.substs.clone().with_self_ty(object_ty);
-                    let original_trait_ref =
-                        Rc::new(ty::TraitRef::new(data.principal.def_id, substs));
-                    let upcast_trait_ref = this.upcast(original_trait_ref.clone(), trait_def_id);
-                    debug!("original_trait_ref={} upcast_trait_ref={} target_trait={}",
-                           original_trait_ref.repr(this.tcx()),
+                    let original_poly_trait_ref =
+                        data.principal_trait_ref_with_self_ty(object_ty);
+                    let upcast_poly_trait_ref =
+                        this.upcast(original_poly_trait_ref.clone(), trait_def_id);
+                    let upcast_trait_ref =
+                        this.replace_late_bound_regions_with_fresh_var(&*upcast_poly_trait_ref);
+                    debug!("original_poly_trait_ref={} upcast_trait_ref={} target_trait={}",
+                           original_poly_trait_ref.repr(this.tcx()),
                            upcast_trait_ref.repr(this.tcx()),
                            trait_def_id.repr(this.tcx()));
                     let substs = upcast_trait_ref.substs.clone();
                     let origin = MethodTraitObject(MethodObject {
-                        trait_ref: upcast_trait_ref,
+                        trait_ref: Rc::new(upcast_trait_ref),
                         object_trait_id: trait_def_id,
                         method_num: method_num,
                         real_index: real_index,
@@ -272,16 +274,21 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                                                                  &trait_def.generics,
                                                                  self.infcx().next_ty_var());
 
-                let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, substs.clone()));
+                let trait_ref =
+                    Rc::new(ty::TraitRef::new(trait_def_id, substs.clone()));
                 let origin = MethodTypeParam(MethodParam { trait_ref: trait_ref,
                                                            method_num: method_num });
                 (substs, origin)
             }
 
-            probe::WhereClausePick(ref trait_ref, method_num) => {
-                let origin = MethodTypeParam(MethodParam { trait_ref: (*trait_ref).clone(),
+            probe::WhereClausePick(ref poly_trait_ref, method_num) => {
+                // Where clauses can have bound regions in them. We need to instantiate
+                // those to convert from a poly-trait-ref to a trait-ref.
+                let trait_ref = self.replace_late_bound_regions_with_fresh_var(&**poly_trait_ref);
+                let substs = trait_ref.substs.clone();
+                let origin = MethodTypeParam(MethodParam { trait_ref: Rc::new(trait_ref),
                                                            method_num: method_num });
-                (trait_ref.substs.clone(), origin)
+                (substs, origin)
             }
         }
     }
@@ -378,25 +385,9 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                               all_substs: subst::Substs<'tcx>)
                               -> InstantiatedMethodSig<'tcx>
     {
-        // If this method comes from an impl (as opposed to a trait),
-        // it may have late-bound regions from the impl that appear in
-        // the substitutions, method signature, and
-        // bounds. Instantiate those at this point. (If it comes from
-        // a trait, this step has no effect, as there are no
-        // late-bound regions to instantiate.)
-        //
-        // The binder level here corresponds to the impl.
-        let (all_substs, (method_sig, method_generics)) =
-            self.replace_late_bound_regions_with_fresh_var(
-                &ty::bind((all_substs,
-                           (pick.method_ty.fty.sig.clone(),
-                            pick.method_ty.generics.clone())))).value;
-
-        debug!("late-bound lifetimes from impl instantiated, \
-                all_substs={} method_sig={} method_generics={}",
-               all_substs.repr(self.tcx()),
-               method_sig.repr(self.tcx()),
-               method_generics.repr(self.tcx()));
+        debug!("instantiate_method_sig(pick={}, all_substs={})",
+               pick.repr(self.tcx()),
+               all_substs.repr(self.tcx()));
 
         // Instantiate the bounds on the method with the
         // type/early-bound-regions substitutions performed.  The only
@@ -426,8 +417,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                 all_substs.clone()
             }
         };
-        let method_bounds =
-            method_generics.to_bounds(self.tcx(), &method_bounds_substs);
+        let method_bounds = pick.method_ty.generics.to_bounds(self.tcx(), &method_bounds_substs);
 
         debug!("method_bounds after subst = {}",
                method_bounds.repr(self.tcx()));
@@ -435,7 +425,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
         // Substitute the type/early-bound-regions into the method
         // signature. In addition, the method signature may bind
         // late-bound regions, so instantiate those.
-        let method_sig = method_sig.subst(self.tcx(), &all_substs);
+        let method_sig = pick.method_ty.fty.sig.subst(self.tcx(), &all_substs);
         let method_sig = self.replace_late_bound_regions_with_fresh_var(&method_sig);
 
         debug!("late-bound lifetimes from method instantiated, method_sig={}",
@@ -481,7 +471,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
             _ => return,
         };
 
-        match sig.inputs[0].sty {
+        match sig.0.inputs[0].sty {
             ty::ty_rptr(_, ty::mt {
                 ty: _,
                 mutbl: ast::MutMutable,
@@ -637,12 +627,12 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
     }
 
     fn upcast(&mut self,
-              source_trait_ref: Rc<ty::TraitRef<'tcx>>,
+              source_trait_ref: Rc<ty::PolyTraitRef<'tcx>>,
               target_trait_def_id: ast::DefId)
-              -> Rc<ty::TraitRef<'tcx>>
+              -> Rc<ty::PolyTraitRef<'tcx>>
     {
         for super_trait_ref in traits::supertraits(self.tcx(), source_trait_ref.clone()) {
-            if super_trait_ref.def_id == target_trait_def_id {
+            if super_trait_ref.def_id() == target_trait_def_id {
                 return super_trait_ref;
             }
         }
@@ -654,8 +644,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                     target_trait_def_id.repr(self.tcx()))[]);
     }
 
-    fn replace_late_bound_regions_with_fresh_var<T>(&self, value: &T) -> T
-        where T : HigherRankedFoldable<'tcx>
+    fn replace_late_bound_regions_with_fresh_var<T>(&self, value: &ty::Binder<T>) -> T
+        where T : TypeFoldable<'tcx> + Repr<'tcx>
     {
         self.infcx().replace_late_bound_regions_with_fresh_var(
             self.span, infer::FnCall, value).0
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index d97a9c9e39b..ffaeceb3eed 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -100,7 +100,7 @@ pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
            call_expr.repr(fcx.tcx()),
            self_expr.repr(fcx.tcx()));
 
-    let self_ty = fcx.infcx().resolve_type_vars_if_possible(self_ty);
+    let self_ty = fcx.infcx().resolve_type_vars_if_possible(&self_ty);
     let pick = try!(probe::probe(fcx, span, method_name, self_ty, call_expr.id));
     Ok(confirm::confirm(fcx, span, self_expr, call_expr, self_ty, pick, supplied_method_types))
 }
@@ -169,9 +169,10 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
     let trait_ref = Rc::new(ty::TraitRef::new(trait_def_id, substs));
 
     // Construct an obligation
+    let poly_trait_ref = Rc::new(ty::Binder((*trait_ref).clone()));
     let obligation = traits::Obligation::misc(span,
                                               fcx.body_id,
-                                              ty::Predicate::Trait(trait_ref.clone()));
+                                              poly_trait_ref.as_predicate());
 
     // Now we want to know if this can be matched
     let mut selcx = traits::SelectionContext::new(fcx.infcx(),
@@ -194,9 +195,6 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
 
     // Substitute the trait parameters into the method type and
     // instantiate late-bound regions to get the actual method type.
-    //
-    // Note that as the method comes from a trait, it can only have
-    // late-bound regions from the fn itself, not the impl.
     let ref bare_fn_ty = method_ty.fty;
     let fn_sig = bare_fn_ty.sig.subst(tcx, &trait_ref.substs);
     let fn_sig = fcx.infcx().replace_late_bound_regions_with_fresh_var(span,
@@ -204,7 +202,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
                                                                        &fn_sig).0;
     let transformed_self_ty = fn_sig.inputs[0];
     let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {
-        sig: fn_sig,
+        sig: ty::Binder(fn_sig),
         unsafety: bare_fn_ty.unsafety,
         abi: bare_fn_ty.abi.clone(),
     });
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 80e511b8fdf..b5776f9aeb3 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -19,8 +19,7 @@ use middle::subst;
 use middle::subst::Subst;
 use middle::traits;
 use middle::ty::{mod, Ty};
-use middle::ty::{MethodObject};
-use middle::ty_fold::HigherRankedFoldable;
+use middle::ty_fold::TypeFoldable;
 use middle::infer;
 use middle::infer::InferCtxt;
 use syntax::ast;
@@ -58,11 +57,11 @@ struct Candidate<'tcx> {
 
 enum CandidateKind<'tcx> {
     InherentImplCandidate(/* Impl */ ast::DefId, subst::Substs<'tcx>),
-    ObjectCandidate(MethodObject<'tcx>),
+    ObjectCandidate(/* Trait */ ast::DefId, /* method_num */ uint, /* real_index */ uint),
     ExtensionImplCandidate(/* Impl */ ast::DefId, Rc<ty::TraitRef<'tcx>>,
                            subst::Substs<'tcx>, MethodIndex),
     UnboxedClosureCandidate(/* Trait */ ast::DefId, MethodIndex),
-    WhereClauseCandidate(Rc<ty::TraitRef<'tcx>>, MethodIndex),
+    WhereClauseCandidate(Rc<ty::PolyTraitRef<'tcx>>, MethodIndex),
 }
 
 pub struct Pick<'tcx> {
@@ -77,7 +76,7 @@ pub enum PickKind<'tcx> {
     ObjectPick(/* Trait */ ast::DefId, /* method_num */ uint, /* real_index */ uint),
     ExtensionImplPick(/* Impl */ ast::DefId, MethodIndex),
     TraitPick(/* Trait */ ast::DefId, MethodIndex),
-    WhereClausePick(/* Trait */ Rc<ty::TraitRef<'tcx>>, MethodIndex),
+    WhereClausePick(/* Trait */ Rc<ty::PolyTraitRef<'tcx>>, MethodIndex),
 }
 
 pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError>;
@@ -149,7 +148,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     // this creates one big transaction so that all type variables etc
     // that we create during the probe process are removed later
     let mut dummy = Some((steps, opt_simplified_steps)); // FIXME(#18101) need once closures
-    fcx.infcx().probe(|| {
+    fcx.infcx().probe(|_| {
         let (steps, opt_simplified_steps) = dummy.take().unwrap();
         let mut probe_cx = ProbeContext::new(fcx, span, method_name, steps, opt_simplified_steps);
         probe_cx.assemble_inherent_candidates();
@@ -231,9 +230,9 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                self_ty.repr(self.tcx()));
 
         match self_ty.sty {
-            ty::ty_trait(box ty::TyTrait { ref principal, bounds, .. }) => {
-                self.assemble_inherent_candidates_from_object(self_ty, &*principal, bounds);
-                self.assemble_inherent_impl_candidates_for_type(principal.def_id);
+            ty::ty_trait(box ref data) => {
+                self.assemble_inherent_candidates_from_object(self_ty, data);
+                self.assemble_inherent_impl_candidates_for_type(data.principal.def_id());
             }
             ty::ty_enum(did, _) |
             ty::ty_struct(did, _) |
@@ -290,8 +289,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
 
     fn assemble_inherent_candidates_from_object(&mut self,
                                                 self_ty: Ty<'tcx>,
-                                                principal: &ty::TraitRef<'tcx>,
-                                                _bounds: ty::ExistentialBounds) {
+                                                data: &ty::TyTrait<'tcx>) {
         debug!("assemble_inherent_candidates_from_object(self_ty={})",
                self_ty.repr(self.tcx()));
 
@@ -304,29 +302,17 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         // a substitution that replaces `Self` with the object type
         // itself. Hence, a `&self` method will wind up with an
         // argument type like `&Trait`.
-        let rcvr_substs = principal.substs.clone().with_self_ty(self_ty);
-        let trait_ref = Rc::new(ty::TraitRef {
-            def_id: principal.def_id,
-            substs: rcvr_substs.clone()
-        });
-
+        let trait_ref = data.principal_trait_ref_with_self_ty(self_ty);
         self.elaborate_bounds(&[trait_ref.clone()], false, |this, new_trait_ref, m, method_num| {
             let vtable_index =
-                get_method_index(tcx, &*new_trait_ref,
-                                 trait_ref.clone(), method_num);
+                get_method_index(tcx, &*new_trait_ref, trait_ref.clone(), method_num);
 
-            let xform_self_ty =
-                this.xform_self_ty(&m, &new_trait_ref.substs);
+            let xform_self_ty = this.xform_self_ty(&m, new_trait_ref.substs());
 
             this.inherent_candidates.push(Candidate {
                 xform_self_ty: xform_self_ty,
                 method_ty: m,
-                kind: ObjectCandidate(MethodObject {
-                    trait_ref: new_trait_ref,
-                    object_trait_id: principal.def_id,
-                    method_num: method_num,
-                    real_index: vtable_index
-                })
+                kind: ObjectCandidate(new_trait_ref.def_id(), method_num, vtable_index)
             });
         });
     }
@@ -358,27 +344,27 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
 
         self.elaborate_bounds(bounds.as_slice(), true, |this, trait_ref, m, method_num| {
             let xform_self_ty =
-                this.xform_self_ty(&m, &trait_ref.substs);
+                this.xform_self_ty(&m, trait_ref.substs());
 
             debug!("found match: trait_ref={} substs={} m={}",
                    trait_ref.repr(this.tcx()),
-                   trait_ref.substs.repr(this.tcx()),
+                   trait_ref.substs().repr(this.tcx()),
                    m.repr(this.tcx()));
             assert_eq!(m.generics.types.get_slice(subst::TypeSpace).len(),
-                       trait_ref.substs.types.get_slice(subst::TypeSpace).len());
+                       trait_ref.substs().types.get_slice(subst::TypeSpace).len());
             assert_eq!(m.generics.regions.get_slice(subst::TypeSpace).len(),
-                       trait_ref.substs.regions().get_slice(subst::TypeSpace).len());
+                       trait_ref.substs().regions().get_slice(subst::TypeSpace).len());
             assert_eq!(m.generics.types.get_slice(subst::SelfSpace).len(),
-                       trait_ref.substs.types.get_slice(subst::SelfSpace).len());
+                       trait_ref.substs().types.get_slice(subst::SelfSpace).len());
             assert_eq!(m.generics.regions.get_slice(subst::SelfSpace).len(),
-                       trait_ref.substs.regions().get_slice(subst::SelfSpace).len());
+                       trait_ref.substs().regions().get_slice(subst::SelfSpace).len());
 
             // Because this trait derives from a where-clause, it
             // should not contain any inference variables or other
             // artifacts. This means it is safe to put into the
             // `WhereClauseCandidate` and (eventually) into the
             // `WhereClausePick`.
-            assert!(trait_ref.substs.types.iter().all(|&t| !ty::type_needs_infer(t)));
+            assert!(trait_ref.substs().types.iter().all(|&t| !ty::type_needs_infer(t)));
 
             this.inherent_candidates.push(Candidate {
                 xform_self_ty: xform_self_ty,
@@ -392,10 +378,10 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
     // create the candidates.
     fn elaborate_bounds(
         &mut self,
-        bounds: &[Rc<ty::TraitRef<'tcx>>],
+        bounds: &[Rc<ty::PolyTraitRef<'tcx>>],
         num_includes_types: bool,
         mk_cand: for<'b> |this: &mut ProbeContext<'b, 'tcx>,
-                          tr: Rc<ty::TraitRef<'tcx>>,
+                          tr: Rc<ty::PolyTraitRef<'tcx>>,
                           m: Rc<ty::Method<'tcx>>,
                           method_num: uint|)
     {
@@ -405,12 +391,12 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         let mut cache = HashSet::new();
         for bound_trait_ref in traits::transitive_bounds(tcx, bounds) {
             // Already visited this trait, skip it.
-            if !cache.insert(bound_trait_ref.def_id) {
+            if !cache.insert(bound_trait_ref.def_id()) {
                 continue;
             }
 
             let (pos, method) = match trait_method(tcx,
-                                                   bound_trait_ref.def_id,
+                                                   bound_trait_ref.def_id(),
                                                    self.method_name,
                                                    num_includes_types) {
                 Some(v) => v,
@@ -418,7 +404,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
             };
 
             if !self.has_applicable_self(&*method) {
-                self.record_static_candidate(TraitSource(bound_trait_ref.def_id));
+                self.record_static_candidate(TraitSource(bound_trait_ref.def_id()));
             } else {
                 mk_cand(self, bound_trait_ref, method, pos);
             }
@@ -756,7 +742,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                self_ty.repr(self.tcx()),
                probe.repr(self.tcx()));
 
-        self.infcx().probe(|| {
+        self.infcx().probe(|_| {
             // First check that the self type can be related.
             match self.make_sub_ty(self_ty, probe.xform_self_ty) {
                 Ok(()) => { }
@@ -779,7 +765,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
 
                     // Erase any late-bound regions bound in the impl
                     // which appear in the bounds.
-                    let impl_bounds = self.erase_late_bound_regions(&ty::bind(impl_bounds)).value;
+                    let impl_bounds = self.erase_late_bound_regions(&ty::Binder(impl_bounds));
 
                     // Convert the bounds into obligations.
                     let obligations =
@@ -881,9 +867,10 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
     fn xform_self_ty(&self,
                      method: &Rc<ty::Method<'tcx>>,
                      substs: &subst::Substs<'tcx>)
-                     -> Ty<'tcx> {
+                     -> Ty<'tcx>
+    {
         debug!("xform_self_ty(self_ty={}, substs={})",
-               method.fty.sig.inputs[0].repr(self.tcx()),
+               method.fty.sig.0.inputs[0].repr(self.tcx()),
                substs.repr(self.tcx()));
 
         // It is possible for type parameters or early-bound lifetimes
@@ -916,15 +903,13 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         }
 
         // Replace early-bound regions and types.
-        let xform_self_ty = method.fty.sig.inputs[0].subst(self.tcx(), substs);
+        let xform_self_ty = method.fty.sig.0.inputs[0].subst(self.tcx(), substs);
 
         // Replace late-bound regions bound in the impl or
-        // where-clause (2 levels of binding).
-        let xform_self_ty =
-            self.erase_late_bound_regions(&ty::bind(ty::bind(xform_self_ty))).value.value;
-
-        // Replace late-bound regions bound in the method (1 level of binding).
-        self.erase_late_bound_regions(&ty::bind(xform_self_ty)).value
+        // where-clause (2 levels of binding) and method (1 level of binding).
+        self.erase_late_bound_regions(
+            &self.erase_late_bound_regions(
+                &ty::Binder(ty::Binder(xform_self_ty))))
     }
 
     fn impl_substs(&self,
@@ -962,8 +947,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
     ///    region got replaced with the same variable, which requires a bit more coordination
     ///    and/or tracking the substitution and
     ///    so forth.
-    fn erase_late_bound_regions<T>(&self, value: &T) -> T
-        where T : HigherRankedFoldable<'tcx>
+    fn erase_late_bound_regions<T>(&self, value: &ty::Binder<T>) -> T
+        where T : TypeFoldable<'tcx> + Repr<'tcx>
     {
         ty::erase_late_bound_regions(self.tcx(), value)
     }
@@ -1007,8 +992,8 @@ fn trait_method<'tcx>(tcx: &ty::ctxt<'tcx>,
 // Determine the index of a method in the list of all methods belonging
 // to a trait and its supertraits.
 fn get_method_index<'tcx>(tcx: &ty::ctxt<'tcx>,
-                          trait_ref: &ty::TraitRef<'tcx>,
-                          subtrait: Rc<ty::TraitRef<'tcx>>,
+                          trait_ref: &ty::PolyTraitRef<'tcx>,
+                          subtrait: Rc<ty::PolyTraitRef<'tcx>>,
                           n_method: uint) -> uint {
     // We need to figure the "real index" of the method in a
     // listing of all the methods of an object. We do this by
@@ -1017,10 +1002,10 @@ fn get_method_index<'tcx>(tcx: &ty::ctxt<'tcx>,
     // methods from them.
     let mut method_count = n_method;
     ty::each_bound_trait_and_supertraits(tcx, &[subtrait], |bound_ref| {
-        if bound_ref.def_id == trait_ref.def_id {
+        if bound_ref.def_id() == trait_ref.def_id() {
             false
         } else {
-            let trait_items = ty::trait_items(tcx, bound_ref.def_id);
+            let trait_items = ty::trait_items(tcx, bound_ref.def_id());
             for trait_item in trait_items.iter() {
                 match *trait_item {
                     ty::MethodTraitItem(_) => method_count += 1,
@@ -1042,8 +1027,8 @@ impl<'tcx> Candidate<'tcx> {
                 InherentImplCandidate(def_id, _) => {
                     InherentImplPick(def_id)
                 }
-                ObjectCandidate(ref data) => {
-                    ObjectPick(data.trait_ref.def_id, data.method_num, data.real_index)
+                ObjectCandidate(def_id, method_num, real_index) => {
+                    ObjectPick(def_id, method_num, real_index)
                 }
                 ExtensionImplCandidate(def_id, _, _, index) => {
                     ExtensionImplPick(def_id, index)
@@ -1057,7 +1042,7 @@ impl<'tcx> Candidate<'tcx> {
                     // inference variables or other artifacts. This
                     // means they are safe to put into the
                     // `WhereClausePick`.
-                    assert!(trait_ref.substs.types.iter().all(|&t| !ty::type_needs_infer(t)));
+                    assert!(trait_ref.substs().types.iter().all(|&t| !ty::type_needs_infer(t)));
 
                     WhereClausePick((*trait_ref).clone(), index)
                 }
@@ -1068,10 +1053,10 @@ impl<'tcx> Candidate<'tcx> {
     fn to_source(&self) -> CandidateSource {
         match self.kind {
             InherentImplCandidate(def_id, _) => ImplSource(def_id),
-            ObjectCandidate(ref obj) => TraitSource(obj.trait_ref.def_id),
+            ObjectCandidate(def_id, _, _) => TraitSource(def_id),
             ExtensionImplCandidate(def_id, _, _, _) => ImplSource(def_id),
             UnboxedClosureCandidate(trait_def_id, _) => TraitSource(trait_def_id),
-            WhereClauseCandidate(ref trait_ref, _) => TraitSource(trait_ref.def_id),
+            WhereClauseCandidate(ref trait_ref, _) => TraitSource(trait_ref.def_id()),
         }
     }
 
@@ -1084,10 +1069,12 @@ impl<'tcx> Candidate<'tcx> {
             UnboxedClosureCandidate(trait_def_id, method_num) => {
                 Some((trait_def_id, method_num))
             }
-            ExtensionImplCandidate(_, ref trait_ref, _, method_num) |
-            WhereClauseCandidate(ref trait_ref, method_num) => {
+            ExtensionImplCandidate(_, ref trait_ref, _, method_num) => {
                 Some((trait_ref.def_id, method_num))
             }
+            WhereClauseCandidate(ref trait_ref, method_num) => {
+                Some((trait_ref.def_id(), method_num))
+            }
         }
     }
 }
@@ -1105,8 +1092,8 @@ impl<'tcx> Repr<'tcx> for CandidateKind<'tcx> {
         match *self {
             InherentImplCandidate(ref a, ref b) =>
                 format!("InherentImplCandidate({},{})", a.repr(tcx), b.repr(tcx)),
-            ObjectCandidate(ref a) =>
-                format!("ObjectCandidate({})", a.repr(tcx)),
+            ObjectCandidate(a, b, c) =>
+                format!("ObjectCandidate({},{},{})", a.repr(tcx), b, c),
             ExtensionImplCandidate(ref a, ref b, ref c, ref d) =>
                 format!("ExtensionImplCandidate({},{},{},{})", a.repr(tcx), b.repr(tcx),
                         c.repr(tcx), d),
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index def82ecd6c8..9e249cc449d 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -506,7 +506,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
 fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
                       unsafety: ast::Unsafety,
                       unsafety_id: ast::NodeId,
-                      fn_sig: &ty::FnSig<'tcx>,
+                      fn_sig: &ty::PolyFnSig<'tcx>,
                       decl: &ast::FnDecl,
                       fn_id: ast::NodeId,
                       body: &ast::Block,
@@ -625,23 +625,20 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
         let param_env = ParameterEnvironment::for_item(ccx.tcx, it.id);
         check_bare_fn(ccx, &**decl, &**body, it.id, fn_pty.ty, param_env);
       }
-      ast::ItemImpl(_, _, ref opt_trait_ref, _, ref impl_items) => {
+      ast::ItemImpl(_, _, _, _, ref impl_items) => {
         debug!("ItemImpl {} with id {}", token::get_ident(it.ident), it.id);
 
         let impl_pty = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
 
-        match *opt_trait_ref {
-            Some(ref ast_trait_ref) => {
-                let impl_trait_ref =
-                    ty::node_id_to_trait_ref(ccx.tcx, ast_trait_ref.ref_id);
+          match ty::impl_trait_ref(ccx.tcx, local_def(it.id)) {
+              Some(impl_trait_ref) => {
                 check_impl_items_against_trait(ccx,
                                                it.span,
-                                               ast_trait_ref,
                                                &*impl_trait_ref,
                                                impl_items.as_slice());
-            }
-            None => { }
-        }
+              }
+              None => { }
+          }
 
         for impl_item in impl_items.iter() {
             match *impl_item {
@@ -722,12 +719,7 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     let param_env = ParameterEnvironment::for_item(ccx.tcx, method.id);
 
     let fty = ty::node_id_to_type(ccx.tcx, method.id);
-    debug!("fty (raw): {}", fty.repr(ccx.tcx));
-
-    let body_id = method.pe_body().id;
-    let fty = liberate_late_bound_regions(
-        ccx.tcx, CodeExtent::from_node_id(body_id), &ty::bind(fty)).value;
-    debug!("fty (liberated): {}", fty.repr(ccx.tcx));
+    debug!("check_method_body: fty={}", fty.repr(ccx.tcx));
 
     check_bare_fn(ccx,
                   &*method.pe_fn_decl(),
@@ -739,7 +731,6 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 
 fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                             impl_span: Span,
-                                            ast_trait_ref: &ast::TraitRef,
                                             impl_trait_ref: &ty::TraitRef<'tcx>,
                                             impl_items: &[ast::ImplItem]) {
     // Locate trait methods
@@ -772,21 +763,16 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                                     impl_method.span,
                                                     impl_method.pe_body().id,
                                                     &**trait_method_ty,
-                                                    impl_trait_ref);
+                                                    &*impl_trait_ref);
                             }
                             _ => {
                                 // This is span_bug as it should have already been
                                 // caught in resolve.
-                                tcx.sess
-                                   .span_bug(impl_method.span,
-                                             format!("item `{}` is of a \
-                                                      different kind from \
-                                                      its trait `{}`",
-                                                     token::get_name(
-                                                        impl_item_ty.name()),
-                                                     pprust::path_to_string(
-                                                        &ast_trait_ref.path))
-                                             .as_slice());
+                                tcx.sess.span_bug(
+                                    impl_method.span,
+                                    format!("item `{}` is of a different kind from its trait `{}`",
+                                            token::get_name(impl_item_ty.name()),
+                                            impl_trait_ref.repr(tcx)).as_slice());
                             }
                         }
                     }
@@ -795,11 +781,9 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                         // caught in resolve.
                         tcx.sess.span_bug(
                             impl_method.span,
-                            format!(
-                                "method `{}` is not a member of trait `{}`",
-                                token::get_name(impl_item_ty.name()),
-                                pprust::path_to_string(
-                                    &ast_trait_ref.path)).as_slice());
+                            format!("method `{}` is not a member of trait `{}`",
+                                    token::get_name(impl_item_ty.name()),
+                                    impl_trait_ref.repr(tcx)).as_slice());
                     }
                 }
             }
@@ -812,27 +796,19 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                 // corresponding type definition in the trait.
                 let opt_associated_type =
                     trait_items.iter()
-                               .find(|ti| {
-                                   ti.name() == typedef_ty.name()
-                               });
+                               .find(|ti| ti.name() == typedef_ty.name());
                 match opt_associated_type {
                     Some(associated_type) => {
                         match (associated_type, &typedef_ty) {
-                            (&ty::TypeTraitItem(_),
-                             &ty::TypeTraitItem(_)) => {}
+                            (&ty::TypeTraitItem(_), &ty::TypeTraitItem(_)) => {}
                             _ => {
                                 // This is `span_bug` as it should have
                                 // already been caught in resolve.
-                                tcx.sess
-                                   .span_bug(typedef.span,
-                                             format!("item `{}` is of a \
-                                                      different kind from \
-                                                      its trait `{}`",
-                                                     token::get_name(
-                                                        typedef_ty.name()),
-                                                     pprust::path_to_string(
-                                                        &ast_trait_ref.path))
-                                             .as_slice());
+                                tcx.sess.span_bug(
+                                    typedef.span,
+                                    format!("item `{}` is of a different kind from its trait `{}`",
+                                            token::get_name(typedef_ty.name()),
+                                            impl_trait_ref.repr(tcx)).as_slice());
                             }
                         }
                     }
@@ -845,8 +821,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                 "associated type `{}` is not a member of \
                                  trait `{}`",
                                 token::get_name(typedef_ty.name()),
-                                pprust::path_to_string(
-                                    &ast_trait_ref.path)).as_slice());
+                                impl_trait_ref.repr(tcx)).as_slice());
                     }
                 }
             }
@@ -854,8 +829,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     }
 
     // Check for missing items from trait
-    let provided_methods = ty::provided_trait_methods(tcx,
-                                                      impl_trait_ref.def_id);
+    let provided_methods = ty::provided_trait_methods(tcx, impl_trait_ref.def_id);
     let mut missing_methods = Vec::new();
     for trait_item in trait_items.iter() {
         match *trait_item {
@@ -870,8 +844,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                         }
                     });
                 let is_provided =
-                    provided_methods.iter().any(
-                        |m| m.name == trait_method.name);
+                    provided_methods.iter().any(|m| m.name == trait_method.name);
                 if !is_implemented && !is_provided {
                     missing_methods.push(format!("`{}`", token::get_name(trait_method.name)));
                 }
@@ -919,27 +892,6 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
     debug!("compare_impl_method(impl_trait_ref={})",
            impl_trait_ref.repr(tcx));
 
-    let impl_m_body_scope = CodeExtent::from_node_id(impl_m_body_id);
-
-    // The impl's trait ref may bind late-bound regions from the impl.
-    // Liberate them and assign them the scope of the method body.
-    //
-    // An example would be:
-    //
-    //     impl<'a> Foo<&'a T> for &'a U { ... }
-    //
-    // Here, the region parameter `'a` is late-bound, so the
-    // trait reference associated with the impl will be
-    //
-    //     for<'a> Foo<&'a T>
-    //
-    // liberating will convert this into:
-    //
-    //     Foo<&'A T>
-    //
-    // where `'A` is the `ReFree` version of `'a`.
-    let impl_trait_ref = liberate_late_bound_regions(tcx, impl_m_body_scope, impl_trait_ref);
-
     debug!("impl_trait_ref (liberated) = {}",
            impl_trait_ref.repr(tcx));
 
@@ -996,15 +948,15 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
         return;
     }
 
-    if impl_m.fty.sig.inputs.len() != trait_m.fty.sig.inputs.len() {
+    if impl_m.fty.sig.0.inputs.len() != trait_m.fty.sig.0.inputs.len() {
         span_err!(tcx.sess, impl_m_span, E0050,
             "method `{}` has {} parameter{} \
              but the declaration in trait `{}` has {}",
             token::get_name(trait_m.name),
-            impl_m.fty.sig.inputs.len(),
-            if impl_m.fty.sig.inputs.len() == 1 {""} else {"s"},
+            impl_m.fty.sig.0.inputs.len(),
+            if impl_m.fty.sig.0.inputs.len() == 1 {""} else {"s"},
             ty::item_path_str(tcx, trait_m.def_id),
-            trait_m.fty.sig.inputs.len());
+            trait_m.fty.sig.0.inputs.len());
         return;
     }
 
@@ -1081,7 +1033,6 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
     if !check_region_bounds_on_impl_method(tcx,
                                            impl_m_span,
                                            impl_m,
-                                           impl_m_body_scope,
                                            &trait_m.generics,
                                            &impl_m.generics,
                                            &trait_to_skol_substs,
@@ -1106,7 +1057,7 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
     // `Bound<&'a T>`, the lifetime `'a` will be late-bound with a
     // depth of 3 (it is nested within 3 binders: the impl, method,
     // and trait-ref itself). So when we do the liberation, we have
-    // two introduce two `ty::bind` scopes, one for the impl and one
+    // two introduce two `ty::Binder` scopes, one for the impl and one
     // the method.
     //
     // The only late-bounded regions that can possibly appear here are
@@ -1120,11 +1071,7 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
         .map(|trait_param_def| &trait_param_def.bounds);
     let impl_bounds =
         impl_m.generics.types.get_slice(subst::FnSpace).iter()
-        .map(|impl_param_def|
-             liberate_late_bound_regions(
-                 tcx,
-                 impl_m_body_scope,
-                 &ty::bind(ty::bind(impl_param_def.bounds.clone()))).value.value);
+        .map(|impl_param_def| &impl_param_def.bounds);
     for (i, (trait_param_bounds, impl_param_bounds)) in
         trait_bounds.zip(impl_bounds).enumerate()
     {
@@ -1167,30 +1114,27 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
                     let trait_bound =
                         trait_bound.subst(tcx, &trait_to_skol_substs);
                     let infcx = infer::new_infer_ctxt(tcx);
-                    infer::mk_sub_trait_refs(&infcx,
-                                             true,
-                                             infer::Misc(impl_m_span),
-                                             trait_bound,
-                                             impl_trait_bound.clone()).is_ok()
+                    infer::mk_sub_poly_trait_refs(&infcx,
+                                                  true,
+                                                  infer::Misc(impl_m_span),
+                                                  trait_bound,
+                                                  impl_trait_bound.clone()).is_ok()
                 });
 
             if !found_match_in_trait {
                 span_err!(tcx.sess, impl_m_span, E0052,
-                    "in method `{}`, type parameter {} requires bound `{}`, which is not \
-                     required by the corresponding type parameter in the trait declaration",
-                    token::get_name(trait_m.name),
-                    i,
-                    ppaux::trait_ref_to_string(tcx, &*impl_trait_bound));
+                          "in method `{}`, type parameter {} requires bound `{}`, which is not \
+                           required by the corresponding type parameter in the trait declaration",
+                          token::get_name(trait_m.name),
+                          i,
+                          impl_trait_bound.user_string(tcx));
             }
         }
     }
 
-    // Compute skolemized form of impl and trait method tys. Note
-    // that we must liberate the late-bound regions from the impl.
+    // Compute skolemized form of impl and trait method tys.
     let impl_fty = ty::mk_bare_fn(tcx, impl_m.fty.clone());
     let impl_fty = impl_fty.subst(tcx, &impl_to_skol_substs);
-    let impl_fty = liberate_late_bound_regions(
-        tcx, impl_m_body_scope, &ty::bind(impl_fty)).value;
     let trait_fty = ty::mk_bare_fn(tcx, trait_m.fty.clone());
     let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
 
@@ -1252,7 +1196,6 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
     fn check_region_bounds_on_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
                                                 span: Span,
                                                 impl_m: &ty::Method<'tcx>,
-                                                impl_m_body_scope: CodeExtent,
                                                 trait_generics: &ty::Generics<'tcx>,
                                                 impl_generics: &ty::Generics<'tcx>,
                                                 trait_to_skol_substs: &Substs<'tcx>,
@@ -1302,16 +1245,6 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
             let impl_bounds =
                 impl_param.bounds.subst(tcx, impl_to_skol_substs);
 
-            // The bounds may reference late-bound regions from the
-            // impl declaration. In that case, we want to replace
-            // those with the liberated variety so as to match the
-            // versions appearing in the `trait_to_skol_substs`.
-            // There are two-levels of binder to be aware of: the
-            // impl, and the method.
-            let impl_bounds =
-                ty::liberate_late_bound_regions(
-                    tcx, impl_m_body_scope, &ty::bind(ty::bind(impl_bounds))).value.value;
-
             debug!("check_region_bounds_on_impl_method: \
                    trait_param={} \
                    impl_param={} \
@@ -1390,13 +1323,7 @@ fn check_cast(fcx: &FnCtxt,
     let t_1 = fcx.to_ty(t);
     let t_1 = structurally_resolved_type(fcx, span, t_1);
 
-    if ty::type_is_scalar(t_1) {
-        // Supply the type as a hint so as to influence integer
-        // literals and other things that might care.
-        check_expr_with_expectation(fcx, e, ExpectCastableToType(t_1))
-    } else {
-        check_expr(fcx, e)
-    }
+    check_expr_with_expectation(fcx, e, ExpectCastableToType(t_1));
 
     let t_e = fcx.expr_ty(e);
 
@@ -1638,7 +1565,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub fn default_diverging_type_variables_to_nil(&self) {
         for (_, &ref ty) in self.inh.node_types.borrow_mut().iter_mut() {
-            if self.infcx().type_var_diverges(self.infcx().resolve_type_vars_if_possible(*ty)) {
+            if self.infcx().type_var_diverges(self.infcx().resolve_type_vars_if_possible(ty)) {
                 demand::eqtype(self, codemap::DUMMY_SP, *ty, ty::mk_nil(self.tcx()));
             }
         }
@@ -1653,7 +1580,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub fn write_object_cast(&self,
                              key: ast::NodeId,
-                             trait_ref: Rc<ty::TraitRef<'tcx>>) {
+                             trait_ref: Rc<ty::PolyTraitRef<'tcx>>) {
         debug!("write_object_cast key={} trait_ref={}",
                key, trait_ref.repr(self.tcx()));
         self.inh.object_cast_map.borrow_mut().insert(key, trait_ref);
@@ -1751,7 +1678,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.register_unsize_obligations(span, &**u)
             }
             ty::UnsizeVtable(ref ty_trait, self_ty) => {
-                vtable::check_object_safety(self.tcx(), &ty_trait.principal, span);
+                vtable::check_object_safety(self.tcx(), ty_trait, span);
+
                 // If the type is `Foo+'a`, ensures that the type
                 // being cast to `Foo+'a` implements `Foo`:
                 vtable::register_object_cast_obligations(self,
@@ -2486,7 +2414,7 @@ fn lookup_method_for_for_loop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     let method_type = match method {
         Some(ref method) => method.ty,
         None => {
-            let true_expr_type = fcx.infcx().resolve_type_vars_if_possible(expr_type);
+            let true_expr_type = fcx.infcx().resolve_type_vars_if_possible(&expr_type);
 
             if !ty::type_is_error(true_expr_type) {
                 let ty_string = fcx.infcx().ty_to_string(true_expr_type);
@@ -2572,13 +2500,13 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                 // HACK(eddyb) ignore self in the definition (see above).
                 check_argument_types(fcx,
                                      sp,
-                                     fty.sig.inputs.slice_from(1),
+                                     fty.sig.0.inputs.slice_from(1),
                                      callee_expr,
                                      args_no_rcvr,
                                      autoref_args,
-                                     fty.sig.variadic,
+                                     fty.sig.0.variadic,
                                      tuple_arguments);
-                fty.sig.output
+                fty.sig.0.output
             }
             _ => {
                 fcx.tcx().sess.span_bug(callee_expr.span,
@@ -2992,11 +2920,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         // This is the "default" function signature, used in case of error.
         // In that case, we check each argument against "error" in order to
         // set up all the node type bindings.
-        let error_fn_sig = FnSig {
+        let error_fn_sig = ty::Binder(FnSig {
             inputs: err_args(args.len()),
             output: ty::FnConverging(ty::mk_err()),
             variadic: false
-        };
+        });
 
         let fn_sig = match *fn_sty {
             ty::ty_bare_fn(ty::BareFnTy {ref sig, ..}) |
@@ -3976,7 +3904,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         vtable::select_new_fcx_obligations(fcx);
 
         debug!("ExprForLoop each item has type {}",
-               fcx.infcx().resolve_type_vars_if_possible(typ).repr(fcx.tcx()));
+               fcx.infcx().resolve_type_vars_if_possible(&typ).repr(fcx.tcx()));
 
         let pcx = pat_ctxt {
             fcx: fcx,
@@ -4371,11 +4299,11 @@ impl<'tcx> Expectation<'tcx> {
             }
             ExpectCastableToType(t) => {
                 ExpectCastableToType(
-                    fcx.infcx().resolve_type_vars_if_possible(t))
+                    fcx.infcx().resolve_type_vars_if_possible(&t))
             }
             ExpectHasType(t) => {
                 ExpectHasType(
-                    fcx.infcx().resolve_type_vars_if_possible(t))
+                    fcx.infcx().resolve_type_vars_if_possible(&t))
             }
         }
     }
@@ -5096,7 +5024,7 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         fcx.infcx().replace_late_bound_regions_with_fresh_var(
             span,
             infer::FnCall,
-            &ty::bind((polytype.ty, bounds))).0.value;
+            &ty::Binder((polytype.ty, bounds))).0;
 
     debug!("after late-bounds have been replaced: ty_late_bound={}", ty_late_bound.repr(fcx.tcx()));
     debug!("after late-bounds have been replaced: bounds={}", bounds.repr(fcx.tcx()));
@@ -5712,11 +5640,11 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
     let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {
         unsafety: ast::Unsafety::Unsafe,
         abi: abi::RustIntrinsic,
-        sig: FnSig {
+        sig: ty::Binder(FnSig {
             inputs: inputs,
             output: output,
             variadic: false,
-        }
+        }),
     });
     let i_ty = ty::lookup_item_type(ccx.tcx, local_def(it.id));
     let i_n_tps = i_ty.generics.types.len(subst::FnSpace);
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index bfa3c384da7..33c015a9a08 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -124,8 +124,6 @@ use middle::region::CodeExtent;
 use middle::traits;
 use middle::ty::{ReScope};
 use middle::ty::{mod, Ty, MethodCall};
-use middle::infer::resolve_and_force_all_but_regions;
-use middle::infer::resolve_type;
 use middle::infer;
 use middle::pat_util;
 use util::nodemap::{DefIdMap, NodeMap, FnvHashMap};
@@ -307,11 +305,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
     /// of b will be `&<R0>.int` and then `*b` will require that `<R0>` be bigger than the let and
     /// the `*b` expression, so we will effectively resolve `<R0>` to be the block B.
     pub fn resolve_type(&self, unresolved_ty: Ty<'tcx>) -> Ty<'tcx> {
-        match resolve_type(self.fcx.infcx(), None, unresolved_ty,
-                           resolve_and_force_all_but_regions) {
-            Ok(t) => t,
-            Err(_) => ty::mk_err()
-        }
+        self.fcx.infcx().resolve_type_vars_if_possible(&unresolved_ty)
     }
 
     /// Try to resolve the type for the given node.
@@ -1187,7 +1181,7 @@ fn constrain_autoderefs<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
                 // Treat overloaded autoderefs as if an AutoRef adjustment
                 // was applied on the base type, as that is always the case.
                 let fn_sig = ty::ty_fn_sig(method.ty);
-                let self_ty = fn_sig.inputs[0];
+                let self_ty = fn_sig.0.inputs[0];
                 let (m, r) = match self_ty.sty {
                     ty::ty_rptr(r, ref m) => (m.mutbl, r),
                     _ => rcx.tcx().sess.span_bug(deref_expr.span,
@@ -1204,7 +1198,7 @@ fn constrain_autoderefs<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
                 // Specialized version of constrain_call.
                 type_must_outlive(rcx, infer::CallRcvr(deref_expr.span),
                                   self_ty, r_deref_expr);
-                match fn_sig.output {
+                match fn_sig.0.output {
                     ty::FnConverging(return_type) => {
                         type_must_outlive(rcx, infer::CallReturn(deref_expr.span),
                                           return_type, r_deref_expr);
diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs
index 415a3d53fb2..4db795a1fda 100644
--- a/src/librustc_typeck/check/vtable.rs
+++ b/src/librustc_typeck/check/vtable.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use check::{FnCtxt, structurally_resolved_type};
-use middle::subst::{SelfSpace, FnSpace};
+use middle::subst::{FnSpace};
 use middle::traits;
 use middle::traits::{SelectionError, OutputTypeParameterMismatch, Overflow, Unimplemented};
 use middle::traits::{Obligation, ObligationCause};
@@ -44,7 +44,7 @@ pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 
             // Ensure that if ~T is cast to ~Trait, then T : Trait
             push_cast_obligation(fcx, cast_expr, object_trait, referent_ty);
-            check_object_safety(fcx.tcx(), &object_trait.principal, source_expr.span);
+            check_object_safety(fcx.tcx(), object_trait, source_expr.span);
         }
 
         (&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty,
@@ -68,7 +68,7 @@ pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                target_region,
                                referent_region);
 
-                check_object_safety(fcx.tcx(), &object_trait.principal, source_expr.span);
+                check_object_safety(fcx.tcx(), object_trait, source_expr.span);
             }
         }
 
@@ -132,24 +132,19 @@ pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 // self by value, has no type parameters and does not use the `Self` type, except
 // in self position.
 pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
-                                 object_trait: &ty::TraitRef<'tcx>,
-                                 span: Span) {
-
-    let mut object = object_trait.clone();
-    if object.substs.types.len(SelfSpace) == 0 {
-        object.substs.types.push(SelfSpace, ty::mk_err());
-    }
-
-    let object = Rc::new(object);
-    for tr in traits::supertraits(tcx, object) {
+                                 object_trait: &ty::TyTrait<'tcx>,
+                                 span: Span)
+{
+    let object_trait_ref = object_trait.principal_trait_ref_with_self_ty(ty::mk_err());
+    for tr in traits::supertraits(tcx, object_trait_ref) {
         check_object_safety_inner(tcx, &*tr, span);
     }
 }
 
 fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
-                                 object_trait: &ty::TraitRef<'tcx>,
+                                 object_trait: &ty::PolyTraitRef<'tcx>,
                                  span: Span) {
-    let trait_items = ty::trait_items(tcx, object_trait.def_id);
+    let trait_items = ty::trait_items(tcx, object_trait.def_id());
 
     let mut errors = Vec::new();
     for item in trait_items.iter() {
@@ -163,7 +158,7 @@ fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
 
     let mut errors = errors.iter().flat_map(|x| x.iter()).peekable();
     if errors.peek().is_some() {
-        let trait_name = ty::item_path_str(tcx, object_trait.def_id);
+        let trait_name = ty::item_path_str(tcx, object_trait.def_id());
         span_err!(tcx.sess, span, E0038,
             "cannot convert to a trait object because trait `{}` is not object-safe",
             trait_name);
@@ -212,12 +207,12 @@ fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>,
             }
         };
         let ref sig = method.fty.sig;
-        for &input_ty in sig.inputs[1..].iter() {
+        for &input_ty in sig.0.inputs[1..].iter() {
             if let Some(msg) = check_for_self_ty(input_ty) {
                 msgs.push(msg);
             }
         }
-        if let ty::FnConverging(result_type) = sig.output {
+        if let ty::FnConverging(result_type) = sig.0.output {
             if let Some(msg) = check_for_self_ty(result_type) {
                 msgs.push(msg);
             }
@@ -237,7 +232,7 @@ pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                                   span: Span,
                                                   object_trait: &ty::TyTrait<'tcx>,
                                                   referent_ty: Ty<'tcx>)
-                                                  -> Rc<ty::TraitRef<'tcx>>
+                                                  -> Rc<ty::PolyTraitRef<'tcx>>
 {
     // We can only make objects from sized types.
     fcx.register_builtin_bound(
@@ -256,17 +251,9 @@ pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
            referent_ty.repr(fcx.tcx()),
            object_trait_ty.repr(fcx.tcx()));
 
-    // 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 object_substs = object_trait.principal.substs.clone();
-    assert!(object_substs.self_ty().is_none());
-    object_substs.types.push(SelfSpace, referent_ty);
-
     // Create the obligation for casting from T to Trait.
     let object_trait_ref =
-        Rc::new(ty::TraitRef { def_id: object_trait.principal.def_id,
-                               substs: object_substs });
+        object_trait.principal_trait_ref_with_self_ty(referent_ty);
     let object_obligation =
         Obligation::new(
             ObligationCause::new(span,
@@ -328,36 +315,13 @@ pub fn report_selection_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     match *error {
         Overflow => {
             // We could track the stack here more precisely if we wanted, I imagine.
-            match obligation.trait_ref {
-                ty::Predicate::Trait(ref trait_ref) => {
-                    let trait_ref =
-                        fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(&**trait_ref);
-                    fcx.tcx().sess.span_err(
-                        obligation.cause.span,
-                        format!(
-                            "overflow evaluating the trait `{}` for the type `{}`",
-                            trait_ref.user_string(fcx.tcx()),
-                            trait_ref.self_ty().user_string(fcx.tcx())).as_slice());
-                }
-
-                ty::Predicate::Equate(a, b) => {
-                    let a = fcx.infcx().resolve_type_vars_if_possible(a);
-                    let b = fcx.infcx().resolve_type_vars_if_possible(b);
-                    fcx.tcx().sess.span_err(
-                        obligation.cause.span,
-                        format!(
-                            "overflow checking whether the types `{}` and `{}` are equal",
-                            a.user_string(fcx.tcx()),
-                            b.user_string(fcx.tcx())).as_slice());
-                }
-
-                ty::Predicate::TypeOutlives(..) |
-                ty::Predicate::RegionOutlives(..) => {
-                    fcx.tcx().sess.span_err(
-                        obligation.cause.span,
-                        format!("overflow evaluating lifetime predicate").as_slice());
-                }
-            }
+            let predicate =
+                fcx.infcx().resolve_type_vars_if_possible(&obligation.trait_ref);
+            fcx.tcx().sess.span_err(
+                obligation.cause.span,
+                format!(
+                    "overflow evaluating the requirement `{}`",
+                    predicate.user_string(fcx.tcx())).as_slice());
 
             let current_limit = fcx.tcx().sess.recursion_limit.get();
             let suggested_limit = current_limit * 2;
@@ -372,9 +336,7 @@ pub fn report_selection_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         Unimplemented => {
             match obligation.trait_ref {
                 ty::Predicate::Trait(ref trait_ref) => {
-                    let trait_ref =
-                        fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
-                            &**trait_ref);
+                    let trait_ref = fcx.infcx().resolve_type_vars_if_possible(&**trait_ref);
                     if !ty::type_is_error(trait_ref.self_ty()) {
                         fcx.tcx().sess.span_err(
                             obligation.cause.span,
@@ -382,41 +344,51 @@ pub fn report_selection_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                 "the trait `{}` is not implemented for the type `{}`",
                                 trait_ref.user_string(fcx.tcx()),
                                 trait_ref.self_ty().user_string(fcx.tcx())).as_slice());
-                        note_obligation_cause(fcx, obligation);
                     }
                 }
 
-                ty::Predicate::Equate(a, b) => {
-                    let a = fcx.infcx().resolve_type_vars_if_possible(a);
-                    let b = fcx.infcx().resolve_type_vars_if_possible(b);
-                    let err = infer::can_mk_eqty(fcx.infcx(), a, b).unwrap_err();
+                ty::Predicate::Equate(ref predicate) => {
+                    let predicate = fcx.infcx().resolve_type_vars_if_possible(predicate);
+                    let err = fcx.infcx().equality_predicate(obligation.cause.span,
+                                                             &predicate).unwrap_err();
                     fcx.tcx().sess.span_err(
                         obligation.cause.span,
                         format!(
-                            "mismatched types: the types `{}` and `{}` are not equal ({})",
-                            a.user_string(fcx.tcx()),
-                            b.user_string(fcx.tcx()),
+                            "the requirement `{}` is not satisfied (`{}`)",
+                            predicate.user_string(fcx.tcx()),
                             ty::type_err_to_str(fcx.tcx(), &err)).as_slice());
                 }
 
-                ty::Predicate::TypeOutlives(..) |
-                ty::Predicate::RegionOutlives(..) => {
-                    // these kinds of predicates turn into
-                    // constraints, and hence errors show up in region
-                    // inference.
-                    fcx.tcx().sess.span_bug(
+                ty::Predicate::RegionOutlives(ref predicate) => {
+                    let predicate = fcx.infcx().resolve_type_vars_if_possible(predicate);
+                    let err = fcx.infcx().region_outlives_predicate(obligation.cause.span,
+                                                                    &predicate).unwrap_err();
+                    fcx.tcx().sess.span_err(
                         obligation.cause.span,
-                        format!("region predicate error {}",
-                                obligation.repr(fcx.tcx())).as_slice());
+                        format!(
+                            "the requirement `{}` is not satisfied (`{}`)",
+                            predicate.user_string(fcx.tcx()),
+                            ty::type_err_to_str(fcx.tcx(), &err)).as_slice());
+                }
+
+                ty::Predicate::TypeOutlives(ref predicate) => {
+                    let predicate = fcx.infcx().resolve_type_vars_if_possible(predicate);
+                    fcx.tcx().sess.span_err(
+                        obligation.cause.span,
+                        format!(
+                            "the requirement `{}` is not satisfied",
+                            predicate.user_string(fcx.tcx())).as_slice());
                 }
             }
+
+            note_obligation_cause(fcx, obligation);
         }
         OutputTypeParameterMismatch(ref expected_trait_ref, ref actual_trait_ref, ref e) => {
             let expected_trait_ref =
-                fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
+                fcx.infcx().resolve_type_vars_if_possible(
                     &**expected_trait_ref);
             let actual_trait_ref =
-                fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
+                fcx.infcx().resolve_type_vars_if_possible(
                     &**actual_trait_ref);
             if !ty::type_is_error(actual_trait_ref.self_ty()) {
                 fcx.tcx().sess.span_err(
@@ -443,7 +415,7 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 
     let trait_ref = match obligation.trait_ref {
         ty::Predicate::Trait(ref trait_ref) => {
-            fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(&**trait_ref)
+            fcx.infcx().resolve_type_vars_if_possible(&**trait_ref)
         }
         _ => {
             fcx.tcx().sess.span_bug(
@@ -458,7 +430,7 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
            trait_ref.repr(fcx.tcx()),
            self_ty.repr(fcx.tcx()),
            obligation.repr(fcx.tcx()));
-    let all_types = &trait_ref.substs.types;
+    let all_types = &trait_ref.substs().types;
     if all_types.iter().any(|&t| ty::type_is_error(t)) {
     } else if all_types.iter().any(|&t| ty::type_needs_infer(t)) {
         // This is kind of a hack: it frequently happens that some earlier
@@ -477,7 +449,7 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         // anyway. In that case, why inundate the user.
         if !fcx.tcx().sess.has_errors() {
             if fcx.ccx.tcx.lang_items.sized_trait()
-                  .map_or(false, |sized_id| sized_id == trait_ref.def_id) {
+                  .map_or(false, |sized_id| sized_id == trait_ref.def_id()) {
                 fcx.tcx().sess.span_err(
                     obligation.cause.span,
                     format!(
diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs
index 8c82429e1c2..c09ce3db6dd 100644
--- a/src/librustc_typeck/check/wf.rs
+++ b/src/librustc_typeck/check/wf.rs
@@ -166,12 +166,9 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
 
             // Find the impl self type as seen from the "inside" --
             // that is, with all type parameters converted from bound
-            // to free, and any late-bound regions on the impl
-            // liberated.
+            // to free.
             let self_ty = ty::node_id_to_type(fcx.tcx(), item.id);
             let self_ty = self_ty.subst(fcx.tcx(), &fcx.inh.param_env.free_substs);
-            let self_ty = liberate_late_bound_regions(
-                fcx.tcx(), item_scope, &ty::bind(self_ty)).value;
 
             bounds_checker.check_traits_in_ty(self_ty);
 
@@ -182,7 +179,6 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                 Some(t) => { t }
             };
             let trait_ref = (*trait_ref).subst(fcx.tcx(), &fcx.inh.param_env.free_substs);
-            let trait_ref = liberate_late_bound_regions(fcx.tcx(), item_scope, &trait_ref);
 
             // There are special rules that apply to drop.
             if
@@ -222,7 +218,8 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                     traits::ItemObligation(trait_ref.def_id));
 
             // Find the supertrait bounds. This will add `int:Bar`.
-            let predicates = ty::predicates_for_trait_ref(fcx.tcx(), &trait_ref);
+            let poly_trait_ref = ty::Binder(trait_ref);
+            let predicates = ty::predicates_for_trait_ref(fcx.tcx(), &poly_trait_ref);
             for predicate in predicates.into_iter() {
                 fcx.register_predicate(traits::Obligation::new(cause, predicate));
             }
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 8d94cf5dd5e..b73381966e8 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -19,8 +19,6 @@ use middle::def;
 use middle::pat_util;
 use middle::ty::{mod, Ty, MethodCall, MethodCallee};
 use middle::ty_fold::{TypeFolder,TypeFoldable};
-use middle::infer::{force_all, resolve_all, resolve_region};
-use middle::infer::resolve_type;
 use middle::infer;
 use write_substs_to_tcx;
 use write_ty_to_tcx;
@@ -337,8 +335,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
         }
     }
 
-    fn resolve<T:ResolveIn<'tcx>>(&self, t: &T, reason: ResolveReason) -> T {
-        t.resolve_in(&mut Resolver::new(self.fcx, reason))
+    fn resolve<T:TypeFoldable<'tcx>>(&self, t: &T, reason: ResolveReason) -> T {
+        t.fold_with(&mut Resolver::new(self.fcx, reason))
     }
 }
 
@@ -376,19 +374,6 @@ impl ResolveReason {
 }
 
 ///////////////////////////////////////////////////////////////////////////
-// Convenience methods for resolving different kinds of things.
-
-trait ResolveIn<'tcx> {
-    fn resolve_in<'a>(&self, resolver: &mut Resolver<'a, 'tcx>) -> Self;
-}
-
-impl<'tcx, T: TypeFoldable<'tcx>> ResolveIn<'tcx> for T {
-    fn resolve_in<'a>(&self, resolver: &mut Resolver<'a, 'tcx>) -> T {
-        self.fold_with(resolver)
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
 // The Resolver. This is the type folding engine that detects
 // unresolved types and so forth.
 
@@ -465,13 +450,11 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
     }
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        if !ty::type_needs_infer(t) {
-            return t;
-        }
-
-        match resolve_type(self.infcx, None, t, resolve_all | force_all) {
+        match self.infcx.fully_resolve(&t) {
             Ok(t) => t,
             Err(e) => {
+                debug!("Resolver::fold_ty: input type `{}` not fully resolvable",
+                       t.repr(self.tcx));
                 self.report_error(e);
                 ty::mk_err()
             }
@@ -479,7 +462,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
     }
 
     fn fold_region(&mut self, r: ty::Region) -> ty::Region {
-        match resolve_region(self.infcx, r, resolve_all | force_all) {
+        match self.infcx.fully_resolve(&r) {
             Ok(r) => r,
             Err(e) => {
                 self.report_error(e);
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index a55f3c61919..5d0bb6622c2 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -19,6 +19,7 @@
 use metadata::csearch::{each_impl, get_impl_trait};
 use metadata::csearch;
 use middle::subst::{mod, Subst};
+use middle::ty::RegionEscape;
 use middle::ty::{ImplContainer, ImplOrTraitItemId, MethodTraitItemId};
 use middle::ty::{ParameterEnvironment, TypeTraitItemId, lookup_item_type};
 use middle::ty::{Ty, ty_bool, ty_char, ty_closure, ty_enum, ty_err};
@@ -26,12 +27,11 @@ use middle::ty::{ty_param, Polytype, ty_ptr};
 use middle::ty::{ty_rptr, ty_struct, ty_trait, ty_tup};
 use middle::ty::{ty_str, ty_vec, ty_float, ty_infer, ty_int, ty_open};
 use middle::ty::{ty_uint, ty_unboxed_closure, ty_uniq, ty_bare_fn};
-use middle::ty::{type_is_ty_var};
 use middle::ty;
 use CrateCtxt;
 use middle::infer::combine::Combine;
 use middle::infer::InferCtxt;
-use middle::infer::{new_infer_ctxt, resolve_ivar, resolve_type};
+use middle::infer::{new_infer_ctxt};
 use std::collections::{HashSet};
 use std::cell::RefCell;
 use std::rc::Rc;
@@ -52,80 +52,35 @@ mod orphan;
 mod overlap;
 mod unsafety;
 
-fn get_base_type<'a, 'tcx>(inference_context: &InferCtxt<'a, 'tcx>,
-                           span: Span,
-                           original_type: Ty<'tcx>)
-                           -> Option<Ty<'tcx>> {
-    let resolved_type = match resolve_type(inference_context,
-                                           Some(span),
-                                           original_type,
-                                           resolve_ivar) {
-        Ok(resulting_type) if !type_is_ty_var(resulting_type) => resulting_type,
-        _ => {
-            inference_context.tcx.sess.span_fatal(span,
-                                                  "the type of this value must be known in order \
-                                                   to determine the base type");
-        }
-    };
-
-    match resolved_type.sty {
-        ty_enum(..) | ty_struct(..) | ty_unboxed_closure(..) => {
-            debug!("(getting base type) found base type");
-            Some(resolved_type)
+// Returns the def ID of the base type, if there is one.
+fn get_base_type_def_id<'a, 'tcx>(inference_context: &InferCtxt<'a, 'tcx>,
+                                  span: Span,
+                                  ty: Ty<'tcx>)
+                                  -> Option<DefId> {
+    match ty.sty {
+        ty_enum(def_id, _) |
+        ty_struct(def_id, _) => {
+            Some(def_id)
         }
 
-        _ if ty::type_is_trait(resolved_type) => {
-            debug!("(getting base type) found base type (trait)");
-            Some(resolved_type)
+        ty_trait(ref t) => {
+            Some(t.principal.def_id())
         }
 
         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_err | ty_open(..) | ty_uniq(_) |
+        ty_param(..) | ty_err | ty_open(..) | ty_uniq(_) |
         ty_ptr(_) | ty_rptr(_, _) => {
-            debug!("(getting base type) no base type; found {}",
-                   original_type.sty);
             None
         }
-        ty_trait(..) => panic!("should have been caught")
-    }
-}
 
-// Returns the def ID of the base type, if there is one.
-fn get_base_type_def_id<'a, 'tcx>(inference_context: &InferCtxt<'a, 'tcx>,
-                                  span: Span,
-                                  original_type: Ty<'tcx>)
-                                  -> Option<DefId> {
-    match get_base_type(inference_context, span, original_type) {
-        None => None,
-        Some(base_type) => {
-            match base_type.sty {
-                ty_enum(def_id, _) |
-                ty_struct(def_id, _) |
-                ty_unboxed_closure(def_id, _, _) => {
-                    Some(def_id)
-                }
-                ty_ptr(ty::mt {ty, ..}) |
-                ty_rptr(_, ty::mt {ty, ..}) |
-                ty_uniq(ty) => {
-                    match ty.sty {
-                        ty_trait(box ty::TyTrait { ref principal, .. }) => {
-                            Some(principal.def_id)
-                        }
-                        _ => {
-                            panic!("get_base_type() returned a type that wasn't an \
-                                   enum, struct, or trait");
-                        }
-                    }
-                }
-                ty_trait(box ty::TyTrait { ref principal, .. }) => {
-                    Some(principal.def_id)
-                }
-                _ => {
-                    panic!("get_base_type() returned a type that wasn't an \
-                           enum, struct, or trait");
-                }
-            }
+        ty_infer(..) | ty_unboxed_closure(..) => {
+            // `ty` comes from a user declaration so we should only expect types
+            // that the user can type
+            inference_context.tcx.sess.span_bug(
+                span,
+                format!("coherence encountered unexpected type searching for base type: {}",
+                        ty.repr(inference_context.tcx))[]);
         }
     }
 }
@@ -504,6 +459,9 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
         let trait_impls = trait_impls.borrow().clone();
 
         for &impl_did in trait_impls.iter() {
+            debug!("check_implementations_of_copy: impl_did={}",
+                   impl_did.repr(tcx));
+
             if impl_did.krate != ast::LOCAL_CRATE {
                 debug!("check_implementations_of_copy(): impl not in this \
                         crate");
@@ -511,10 +469,16 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
             }
 
             let self_type = self.get_self_type_for_implementation(impl_did);
+            debug!("check_implementations_of_copy: self_type={} (bound)",
+                   self_type.repr(tcx));
+
             let span = tcx.map.span(impl_did.node);
-            let param_env = ParameterEnvironment::for_item(tcx,
-                                                           impl_did.node);
+            let param_env = ParameterEnvironment::for_item(tcx, impl_did.node);
             let self_type = self_type.ty.subst(tcx, &param_env.free_substs);
+            assert!(!self_type.has_escaping_regions());
+
+            debug!("check_implementations_of_copy: self_type={} (free)",
+                   self_type.repr(tcx));
 
             match ty::can_type_implement_copy(tcx, self_type, &param_env) {
                 Ok(()) => {}
diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs
index 1803bf766dd..bb8efd29910 100644
--- a/src/librustc_typeck/coherence/orphan.rs
+++ b/src/librustc_typeck/coherence/orphan.rs
@@ -55,7 +55,7 @@ impl<'cx, 'tcx,'v> visit::Visitor<'v> for OrphanChecker<'cx, 'tcx> {
                         self.check_def_id(item.span, def_id);
                     }
                     ty::ty_trait(box ty::TyTrait{ ref principal, ..}) => {
-                        self.check_def_id(item.span, principal.def_id);
+                        self.check_def_id(item.span, principal.def_id());
                     }
                     _ => {
                         span_err!(self.tcx.sess, item.span, E0118,
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 91947f67dd7..280b42f0959 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -42,10 +42,9 @@ use middle::region;
 use middle::resolve_lifetime;
 use middle::subst;
 use middle::subst::{Substs};
-use middle::ty::{ImplContainer, ImplOrTraitItemContainer, TraitContainer};
-use middle::ty::{Polytype};
-use middle::ty::{mod, Ty};
-use middle::ty_fold::TypeFolder;
+use middle::ty::{AsPredicate, ImplContainer, ImplOrTraitItemContainer, TraitContainer};
+use middle::ty::{mod, RegionEscape, Ty, Polytype};
+use middle::ty_fold::{mod, TypeFolder, TypeFoldable};
 use middle::infer;
 use rscope::*;
 use {CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx};
@@ -227,7 +226,7 @@ pub fn get_enum_variant_types<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 
             ast::StructVariantKind(ref struct_def) => {
                 let pty = Polytype {
-                    generics: ty_generics_for_type(
+                    generics: ty_generics_for_type_or_impl(
                         ccx,
                         generics,
                         DontCreateTypeParametersForAssociatedTypes),
@@ -240,7 +239,7 @@ pub fn get_enum_variant_types<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
         };
 
         let pty = Polytype {
-            generics: ty_generics_for_type(
+            generics: ty_generics_for_type_or_impl(
                           ccx,
                           generics,
                           DontCreateTypeParametersForAssociatedTypes),
@@ -652,7 +651,7 @@ fn is_associated_type_valid_for_param(ty: Ty,
     if let ty::ty_param(param_ty) = ty.sty {
         let type_parameter = generics.types.get(param_ty.space, param_ty.idx);
         for trait_bound in type_parameter.bounds.trait_bounds.iter() {
-            if trait_bound.def_id == trait_id {
+            if trait_bound.def_id() == trait_id {
                 return true
             }
         }
@@ -1051,7 +1050,7 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) {
                       ref selfty,
                       ref impl_items) => {
             // Create generics from the generics specified in the impl head.
-            let ty_generics = ty_generics_for_impl(
+            let ty_generics = ty_generics_for_type_or_impl(
                     ccx,
                     generics,
                     CreateTypeParametersForAssociatedTypes);
@@ -1483,7 +1482,7 @@ pub fn ty_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, it: &ast::Item)
             let pty = {
                 let ty = ccx.to_ty(&ExplicitRscope, &**t);
                 Polytype {
-                    generics: ty_generics_for_type(
+                    generics: ty_generics_for_type_or_impl(
                                   ccx,
                                   generics,
                                   DontCreateTypeParametersForAssociatedTypes),
@@ -1496,7 +1495,7 @@ pub fn ty_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, it: &ast::Item)
         }
         ast::ItemEnum(_, ref generics) => {
             // Create a new generic polytype.
-            let ty_generics = ty_generics_for_type(
+            let ty_generics = ty_generics_for_type_or_impl(
                 ccx,
                 generics,
                 DontCreateTypeParametersForAssociatedTypes);
@@ -1514,7 +1513,7 @@ pub fn ty_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, it: &ast::Item)
             tcx.sess.span_bug(it.span, "invoked ty_of_item on trait");
         }
         ast::ItemStruct(_, ref generics) => {
-            let ty_generics = ty_generics_for_type(
+            let ty_generics = ty_generics_for_type_or_impl(
                 ccx,
                 generics,
                 DontCreateTypeParametersForAssociatedTypes);
@@ -1581,11 +1580,11 @@ fn ty_of_trait_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     }
 }
 
-fn ty_generics_for_type<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                                  generics: &ast::Generics,
-                                  create_type_parameters_for_associated_types:
-                                      CreateTypeParametersForAssociatedTypesFlag)
-                                  -> ty::Generics<'tcx> {
+fn ty_generics_for_type_or_impl<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
+                                          generics: &ast::Generics,
+                                          create_type_parameters_for_associated_types:
+                                          CreateTypeParametersForAssociatedTypesFlag)
+                                          -> ty::Generics<'tcx> {
     ty_generics(ccx,
                 subst::TypeSpace,
                 generics.lifetimes.as_slice(),
@@ -1638,8 +1637,8 @@ fn ty_generics_for_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     let param_id = trait_id;
 
     let self_trait_ref =
-        Rc::new(ty::TraitRef { def_id: local_def(trait_id),
-                               substs: (*substs).clone() });
+        Rc::new(ty::Binder(ty::TraitRef { def_id: local_def(trait_id),
+                                          substs: (*substs).clone() }));
 
     let def = ty::TypeParameterDef {
         space: subst::SelfSpace,
@@ -1665,24 +1664,6 @@ fn ty_generics_for_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     generics
 }
 
-fn ty_generics_for_impl<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                                  generics: &ast::Generics,
-                                  create_type_parameters_for_associated_types:
-                                      CreateTypeParametersForAssociatedTypesFlag)
-                                  -> ty::Generics<'tcx>
-{
-    let early_lifetimes = resolve_lifetime::early_bound_lifetimes(generics);
-    debug!("ty_generics_for_impl: early_lifetimes={}",
-           early_lifetimes);
-    ty_generics(ccx,
-                subst::TypeSpace,
-                early_lifetimes.as_slice(),
-                generics.ty_params.as_slice(),
-                ty::Generics::empty(),
-                &generics.where_clause,
-                create_type_parameters_for_associated_types)
-}
-
 fn ty_generics_for_fn_or_method<'tcx,AC>(
         this: &AC,
         generics: &ast::Generics,
@@ -1920,8 +1901,12 @@ fn ty_generics<'tcx,AC>(this: &AC,
         for region_param_def in result.regions.get_slice(space).iter() {
             let region = region_param_def.to_early_bound_region();
             for &bound_region in region_param_def.bounds.iter() {
-                result.predicates.push(space, ty::Predicate::RegionOutlives(region,
-                                                                            bound_region));
+                // account for new binder introduced in the predicate below; no need
+                // to shift `region` because it is never a late-bound region
+                let bound_region = ty_fold::shift_region(bound_region, 1);
+                result.predicates.push(
+                    space,
+                    ty::Binder(ty::OutlivesPredicate(region, bound_region)).as_predicate());
             }
         }
     }
@@ -2015,7 +2000,7 @@ fn compute_bounds<'tcx,AC>(this: &AC,
                             &param_bounds,
                             span);
 
-    param_bounds.trait_bounds.sort_by(|a,b| a.def_id.cmp(&b.def_id));
+    param_bounds.trait_bounds.sort_by(|a,b| a.def_id().cmp(&b.def_id()));
 
     param_bounds
 }
@@ -2031,13 +2016,13 @@ fn check_bounds_compatible<'tcx>(tcx: &ty::ctxt<'tcx>,
             tcx,
             param_bounds.trait_bounds.as_slice(),
             |trait_ref| {
-                let trait_def = ty::lookup_trait_def(tcx, trait_ref.def_id);
+                let trait_def = ty::lookup_trait_def(tcx, trait_ref.def_id());
                 if trait_def.bounds.builtin_bounds.contains(&ty::BoundSized) {
                     span_err!(tcx.sess, span, E0129,
                               "incompatible bounds on type parameter `{}`, \
                                bound `{}` does not allow unsized type",
                               name_of_bounded_thing.user_string(tcx),
-                              ppaux::trait_ref_to_string(tcx, &*trait_ref));
+                              trait_ref.user_string(tcx));
                 }
                 true
             });
@@ -2057,14 +2042,14 @@ fn conv_param_bounds<'tcx,AC>(this: &AC,
                                      trait_bounds,
                                      region_bounds } =
         astconv::partition_bounds(this.tcx(), span, all_bounds.as_slice());
-    let trait_bounds: Vec<Rc<ty::TraitRef>> =
+    let trait_bounds: Vec<Rc<ty::PolyTraitRef>> =
         trait_bounds.into_iter()
         .map(|bound| {
-            astconv::instantiate_trait_ref(this,
-                                           &ExplicitRscope,
-                                           &bound.trait_ref,
-                                           Some(param_ty.to_ty(this.tcx())),
-                                           AllowEqConstraints::Allow)
+            astconv::instantiate_poly_trait_ref(this,
+                                                &ExplicitRscope,
+                                                bound,
+                                                Some(param_ty.to_ty(this.tcx())),
+                                                AllowEqConstraints::Allow)
         })
         .collect();
     let region_bounds: Vec<ty::Region> =
@@ -2155,9 +2140,9 @@ pub fn ty_of_foreign_fn_decl<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
         ty::BareFnTy {
             abi: abi,
             unsafety: ast::Unsafety::Unsafe,
-            sig: ty::FnSig {inputs: input_tys,
-                            output: output,
-                            variadic: decl.variadic}
+            sig: ty::Binder(ty::FnSig {inputs: input_tys,
+                                       output: output,
+                                       variadic: decl.variadic}),
         });
     let pty = Polytype {
         generics: ty_generics_for_fn_or_method,
@@ -2183,8 +2168,12 @@ pub fn mk_item_substs<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     subst::Substs::new(types, regions)
 }
 
-/// Verifies that the explicit self type of a method matches the impl or
-/// trait.
+/// Verifies that the explicit self type of a method matches the impl
+/// or trait. This is a bit weird but basically because right now we
+/// don't handle the general case, but instead map it to one of
+/// several pre-defined options using various heuristics, this method
+/// comes back to check after the fact that explicit type the user
+/// wrote actually matches what the pre-defined option said.
 fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
     crate_context: &CrateCtxt<'a, 'tcx>,
     rs: &RS,
@@ -2206,19 +2195,21 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
         // contain late-bound regions from the method, but not the
         // trait (since traits only have early-bound region
         // parameters).
-        assert!(!ty::type_escapes_depth(required_type, 1));
+        assert!(!base_type.has_regions_escaping_depth(1));
         let required_type_free =
-            ty::liberate_late_bound_regions(
-                crate_context.tcx, body_scope, &ty::bind(required_type)).value;
-
-        // The "base type" comes from the impl. It may have late-bound
-        // regions from the impl or the method.
-        let base_type_free = // liberate impl regions:
-            ty::liberate_late_bound_regions(
-                crate_context.tcx, body_scope, &ty::bind(ty::bind(base_type))).value.value;
-        let base_type_free = // liberate method regions:
-            ty::liberate_late_bound_regions(
-                crate_context.tcx, body_scope, &ty::bind(base_type_free)).value;
+            liberate_early_bound_regions(
+                crate_context.tcx, body_scope,
+                &ty::liberate_late_bound_regions(
+                    crate_context.tcx, body_scope, &ty::Binder(required_type)));
+
+        // The "base type" comes from the impl. It too may have late-bound
+        // regions from the method.
+        assert!(!base_type.has_regions_escaping_depth(1));
+        let base_type_free =
+            liberate_early_bound_regions(
+                crate_context.tcx, body_scope,
+                &ty::liberate_late_bound_regions(
+                    crate_context.tcx, body_scope, &ty::Binder(base_type)));
 
         debug!("required_type={} required_type_free={} \
                 base_type={} base_type_free={}",
@@ -2239,4 +2230,30 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
         }));
         infcx.resolve_regions_and_report_errors(body_id);
     }
+
+    fn liberate_early_bound_regions<'tcx,T>(
+        tcx: &ty::ctxt<'tcx>,
+        scope: region::CodeExtent,
+        value: &T)
+        -> T
+        where T : TypeFoldable<'tcx> + Repr<'tcx>
+    {
+        /*!
+         * Convert early-bound regions into free regions; normally this is done by
+         * applying the `free_substs` from the `ParameterEnvironment`, but this particular
+         * method-self-type check is kind of hacky and done very early in the process,
+         * before we really have a `ParameterEnvironment` to check.
+         */
+
+        ty_fold::fold_regions(tcx, value, |region, _| {
+            match region {
+                ty::ReEarlyBound(id, _, _, name) => {
+                    let def_id = local_def(id);
+                    ty::ReFree(ty::FreeRegion { scope: scope,
+                                                bound_region: ty::BrNamed(def_id, name) })
+                }
+                _ => region
+            }
+        })
+    }
 }
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index d55d642f746..5fc2466674e 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -228,11 +228,11 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
             let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy {
                 unsafety: ast::Unsafety::Normal,
                 abi: abi::Rust,
-                sig: ty::FnSig {
+                sig: ty::Binder(ty::FnSig {
                     inputs: Vec::new(),
                     output: ty::FnConverging(ty::mk_nil(tcx)),
                     variadic: false
-                }
+                })
             });
 
             require_same_types(tcx, None, false, main_span, main_t, se_ty,
@@ -276,14 +276,14 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
             let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy {
                 unsafety: ast::Unsafety::Normal,
                 abi: abi::Rust,
-                sig: ty::FnSig {
+                sig: ty::Binder(ty::FnSig {
                     inputs: vec!(
                         ty::mk_int(),
                         ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8()))
                     ),
                     output: ty::FnConverging(ty::mk_int()),
                     variadic: false
-                }
+                }),
             });
 
             require_same_types(tcx, None, false, start_span, start_t, se_ty,
diff --git a/src/librustc_typeck/variance.rs b/src/librustc_typeck/variance.rs
index 8fe14bae0f5..67478e0bfa7 100644
--- a/src/librustc_typeck/variance.rs
+++ b/src/librustc_typeck/variance.rs
@@ -777,13 +777,13 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
             }
 
             ty::ty_trait(box ty::TyTrait { ref principal, bounds }) => {
-                let trait_def = ty::lookup_trait_def(self.tcx(), principal.def_id);
+                let trait_def = ty::lookup_trait_def(self.tcx(), principal.def_id());
                 let generics = &trait_def.generics;
 
                 // Traits DO have a Self type parameter, but it is
                 // erased from object types.
                 assert!(!generics.types.is_empty_in(subst::SelfSpace) &&
-                        principal.substs.types.is_empty_in(subst::SelfSpace));
+                        principal.substs().types.is_empty_in(subst::SelfSpace));
 
                 // Traits never declare region parameters in the self
                 // space.
@@ -799,10 +799,10 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
                 self.add_constraints_from_region(bounds.region_bound, contra);
 
                 self.add_constraints_from_substs(
-                    principal.def_id,
+                    principal.def_id(),
                     generics.types.get_slice(subst::TypeSpace),
                     generics.regions.get_slice(subst::TypeSpace),
-                    &principal.substs,
+                    principal.substs(),
                     variance);
             }
 
@@ -878,13 +878,13 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
     /// Adds constraints appropriate for a function with signature
     /// `sig` appearing in a context with ambient variance `variance`
     fn add_constraints_from_sig(&mut self,
-                                sig: &ty::FnSig<'tcx>,
+                                sig: &ty::PolyFnSig<'tcx>,
                                 variance: VarianceTermPtr<'a>) {
         let contra = self.contravariant(variance);
-        for &input in sig.inputs.iter() {
+        for &input in sig.0.inputs.iter() {
             self.add_constraints_from_ty(input, contra);
         }
-        if let ty::FnConverging(result_type) = sig.output {
+        if let ty::FnConverging(result_type) = sig.0.output {
             self.add_constraints_from_ty(result_type, variance);
         }
     }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index d640f055388..661d6ec241a 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -575,6 +575,12 @@ impl Clean<TyParamBound> for ty::BuiltinBound {
     }
 }
 
+impl<'tcx> Clean<TyParamBound> for ty::PolyTraitRef<'tcx> {
+    fn clean(&self, cx: &DocContext) -> TyParamBound {
+        self.0.clean(cx)
+    }
+}
+
 impl<'tcx> Clean<TyParamBound> for ty::TraitRef<'tcx> {
     fn clean(&self, cx: &DocContext) -> TyParamBound {
         let tcx = match cx.tcx_opt() {
@@ -913,7 +919,7 @@ impl<'tcx> Clean<Type> for ty::FnOutput<'tcx> {
     }
 }
 
-impl<'a, 'tcx> Clean<FnDecl> for (ast::DefId, &'a ty::FnSig<'tcx>) {
+impl<'a, 'tcx> Clean<FnDecl> for (ast::DefId, &'a ty::PolyFnSig<'tcx>) {
     fn clean(&self, cx: &DocContext) -> FnDecl {
         let (did, sig) = *self;
         let mut names = if did.node != 0 {
@@ -925,10 +931,10 @@ impl<'a, 'tcx> Clean<FnDecl> for (ast::DefId, &'a ty::FnSig<'tcx>) {
             let _ = names.next();
         }
         FnDecl {
-            output: Return(sig.output.clean(cx)),
+            output: Return(sig.0.output.clean(cx)),
             attrs: Vec::new(),
             inputs: Arguments {
-                values: sig.inputs.iter().map(|t| {
+                values: sig.0.inputs.iter().map(|t| {
                     Argument {
                         type_: t.clean(cx),
                         id: 0,
@@ -1082,14 +1088,14 @@ impl<'tcx> Clean<Item> for ty::Method<'tcx> {
             ty::StaticExplicitSelfCategory => (ast::SelfStatic.clean(cx),
                                                self.fty.sig.clone()),
             s => {
-                let sig = ty::FnSig {
-                    inputs: self.fty.sig.inputs[1..].to_vec(),
-                    ..self.fty.sig.clone()
-                };
+                let sig = ty::Binder(ty::FnSig {
+                    inputs: self.fty.sig.0.inputs[1..].to_vec(),
+                    ..self.fty.sig.0.clone()
+                });
                 let s = match s {
                     ty::ByValueExplicitSelfCategory => SelfValue,
                     ty::ByReferenceExplicitSelfCategory(..) => {
-                        match self.fty.sig.inputs[0].sty {
+                        match self.fty.sig.0.inputs[0].sty {
                             ty::ty_rptr(r, mt) => {
                                 SelfBorrowed(r.clean(cx), mt.mutbl.clean(cx))
                             }
@@ -1097,7 +1103,7 @@ impl<'tcx> Clean<Item> for ty::Method<'tcx> {
                         }
                     }
                     ty::ByBoxExplicitSelfCategory => {
-                        SelfExplicit(self.fty.sig.inputs[0].clean(cx))
+                        SelfExplicit(self.fty.sig.0.inputs[0].clean(cx))
                     }
                     ty::StaticExplicitSelfCategory => unreachable!(),
                 };
@@ -1391,8 +1397,10 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
             }
             ty::ty_struct(did, ref substs) |
             ty::ty_enum(did, ref substs) |
-            ty::ty_trait(box ty::TyTrait { principal: ty::TraitRef { def_id: did, ref substs },
-                                           .. }) => {
+            ty::ty_trait(box ty::TyTrait {
+                principal: ty::Binder(ty::TraitRef { def_id: did, ref substs }),
+                .. }) =>
+            {
                 let fqn = csearch::get_item_path(cx.tcx(), did);
                 let fqn: Vec<String> = fqn.into_iter().map(|i| {
                     i.to_string()
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 95d7906b443..5a1a186c74c 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -105,8 +105,11 @@ pub trait Visitor<'v> {
             None => ()
         }
     }
+    fn visit_lifetime_bound(&mut self, lifetime: &'v Lifetime) {
+        walk_lifetime_bound(self, lifetime)
+    }
     fn visit_lifetime_ref(&mut self, lifetime: &'v Lifetime) {
-        self.visit_name(lifetime.span, lifetime.name)
+        walk_lifetime_ref(self, lifetime)
     }
     fn visit_lifetime_def(&mut self, lifetime: &'v LifetimeDef) {
         walk_lifetime_def(self, lifetime)
@@ -214,10 +217,20 @@ pub fn walk_lifetime_def<'v, V: Visitor<'v>>(visitor: &mut V,
                                               lifetime_def: &'v LifetimeDef) {
     visitor.visit_name(lifetime_def.lifetime.span, lifetime_def.lifetime.name);
     for bound in lifetime_def.bounds.iter() {
-        visitor.visit_lifetime_ref(bound);
+        visitor.visit_lifetime_bound(bound);
     }
 }
 
+pub fn walk_lifetime_bound<'v, V: Visitor<'v>>(visitor: &mut V,
+                                               lifetime_ref: &'v Lifetime) {
+    visitor.visit_lifetime_ref(lifetime_ref)
+}
+
+pub fn walk_lifetime_ref<'v, V: Visitor<'v>>(visitor: &mut V,
+                                             lifetime_ref: &'v Lifetime) {
+    visitor.visit_name(lifetime_ref.span, lifetime_ref.name)
+}
+
 pub fn walk_explicit_self<'v, V: Visitor<'v>>(visitor: &mut V,
                                               explicit_self: &'v ExplicitSelf) {
     match explicit_self.node {
@@ -550,7 +563,7 @@ pub fn walk_ty_param_bound<'v, V: Visitor<'v>>(visitor: &mut V,
             visitor.visit_poly_trait_ref(typ);
         }
         RegionTyParamBound(ref lifetime) => {
-            visitor.visit_lifetime_ref(lifetime);
+            visitor.visit_lifetime_bound(lifetime);
         }
     }
 }
diff --git a/src/test/compile-fail/bad-match.rs b/src/test/compile-fail/bad-match.rs
index 39c3ed3e2a3..728b577df1d 100644
--- a/src/test/compile-fail/bad-match.rs
+++ b/src/test/compile-fail/bad-match.rs
@@ -14,3 +14,6 @@ fn main() {
   let int x = 5;
   match x;
 }
+
+fn main() {
+}
diff --git a/src/test/compile-fail/hrtb-conflate-regions.rs b/src/test/compile-fail/hrtb-conflate-regions.rs
new file mode 100644
index 00000000000..5eb8fd69312
--- /dev/null
+++ b/src/test/compile-fail/hrtb-conflate-regions.rs
@@ -0,0 +1,40 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that an impl with only one bound region `'a` cannot be used to
+// satisfy a constraint where there are two bound regions.
+
+trait Foo<X> {
+    fn foo(&self, x: X) { }
+}
+
+fn want_foo2<T>()
+    where T : for<'a,'b> Foo<(&'a int, &'b int)>
+{
+}
+
+fn want_foo1<T>()
+    where T : for<'z> Foo<(&'z int, &'z int)>
+{
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Expressed as a where clause
+
+struct SomeStruct;
+
+impl<'a> Foo<(&'a int, &'a int)> for SomeStruct
+{
+}
+
+fn a() { want_foo1::<SomeStruct>(); } // OK -- foo wants just one region
+fn b() { want_foo2::<SomeStruct>(); } //~ ERROR not implemented
+
+fn main() { }
diff --git a/src/test/compile-fail/hrtb-just-for-static.rs b/src/test/compile-fail/hrtb-just-for-static.rs
new file mode 100644
index 00000000000..36a45400eec
--- /dev/null
+++ b/src/test/compile-fail/hrtb-just-for-static.rs
@@ -0,0 +1,37 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test a case where you have an impl of `Foo<X>` for all `X` that
+// is being applied to `for<'a> Foo<&'a mut X>`. Issue #19730.
+
+trait Foo<X> {
+    fn foo(&self, x: X) { }
+}
+
+fn want_hrtb<T>()
+    where T : for<'a> Foo<&'a int>
+{
+}
+
+// AnyInt implements Foo<&'a int> for any 'a, so it is a match.
+struct AnyInt;
+impl<'a> Foo<&'a int> for AnyInt { }
+fn give_any() {
+    want_hrtb::<AnyInt>()
+}
+
+// StaticInt only implements Foo<&'static int>, so it is an error.
+struct StaticInt;
+impl Foo<&'static int> for StaticInt { }
+fn give_static() {
+    want_hrtb::<StaticInt>() //~ ERROR `for<'a> Foo<&'a int>` is not implemented
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/hrtb-perfect-forwarding.rs b/src/test/compile-fail/hrtb-perfect-forwarding.rs
new file mode 100644
index 00000000000..a8ee2154fc3
--- /dev/null
+++ b/src/test/compile-fail/hrtb-perfect-forwarding.rs
@@ -0,0 +1,66 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test a case where you have an impl of `Foo<X>` for all `X` that
+// is being applied to `for<'a> Foo<&'a mut X>`. Issue #19730.
+
+trait Foo<X> {
+    fn foo(&mut self, x: X) { }
+}
+
+trait Bar<X> {
+    fn bar(&mut self, x: X) { }
+}
+
+impl<'a,X,F> Foo<X> for &'a mut F
+    where F : Foo<X> + Bar<X>
+{
+}
+
+impl<'a,X,F> Bar<X> for &'a mut F
+    where F : Bar<X>
+{
+}
+
+fn no_hrtb<'b,T>(mut t: T)
+    where T : Bar<&'b int>
+{
+    // OK -- `T : Bar<&'b int>`, and thus the impl above ensures that
+    // `&mut T : Bar<&'b int>`.
+    no_hrtb(&mut t);
+}
+
+fn bar_hrtb<T>(mut t: T)
+    where T : for<'b> Bar<&'b int>
+{
+    // OK -- `T : for<'b> Bar<&'b int>`, and thus the impl above
+    // ensures that `&mut T : for<'b> Bar<&'b int>`.  This is an
+    // example of a "perfect forwarding" impl.
+    bar_hrtb(&mut t);
+}
+
+fn foo_hrtb_bar_not<'b,T>(mut t: T)
+    where T : for<'a> Foo<&'a int> + Bar<&'b int>
+{
+    // Not OK -- The forwarding impl for `Foo` requires that `Bar` also
+    // be implemented. Thus to satisfy `&mut T : for<'a> Foo<&'a
+    // int>`, we require `T : for<'a> Bar<&'a int>`, but the where
+    // clause only specifies `T : Bar<&'b int>`.
+    foo_hrtb_bar_not(&mut t); //~ ERROR `for<'a> Bar<&'a int>` is not implemented for the type `T`
+}
+
+fn foo_hrtb_bar_hrtb<T>(mut t: T)
+    where T : for<'a> Foo<&'a int> + for<'b> Bar<&'b int>
+{
+    // OK -- now we have `T : for<'b> Bar&'b int>`.
+    foo_hrtb_bar_hrtb(&mut t);
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/hrtb-type-outlives.rs b/src/test/compile-fail/hrtb-type-outlives.rs
new file mode 100644
index 00000000000..9fe8f9ab46d
--- /dev/null
+++ b/src/test/compile-fail/hrtb-type-outlives.rs
@@ -0,0 +1,59 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test what happens when a HR obligation is applied to an impl with
+// "outlives" bounds. Currently we're pretty conservative here; this
+// will probably improve in time.
+
+trait Foo<X> {
+    fn foo(&self, x: X) { }
+}
+
+fn want_foo<T>()
+    where T : for<'a> Foo<&'a int>
+{
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Expressed as a where clause
+
+struct SomeStruct<X> {
+    x: X
+}
+
+impl<'a,X> Foo<&'a int> for SomeStruct<X>
+    where X : 'a
+{
+}
+
+fn one() {
+    // In fact there is no good reason for this to be an error, but
+    // whatever, I'm mostly concerned it doesn't ICE right now:
+    want_foo::<SomeStruct<uint>>();
+    //~^ ERROR requirement `for<'a> uint : 'a` is not satisfied
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Expressed as shorthand
+
+struct AnotherStruct<X> {
+    x: X
+}
+
+impl<'a,X:'a> Foo<&'a int> for AnotherStruct<X>
+{
+}
+
+fn two() {
+    want_foo::<AnotherStruct<uint>>();
+    //~^ ERROR requirement `for<'a> uint : 'a` is not satisfied
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/issue-14366.rs b/src/test/compile-fail/issue-14366.rs
index 01a15023fba..d03885ca713 100644
--- a/src/test/compile-fail/issue-14366.rs
+++ b/src/test/compile-fail/issue-14366.rs
@@ -11,5 +11,4 @@
 fn main() {
     let _x = "test" as &::std::any::Any;
 //~^ ERROR the trait `core::kinds::Sized` is not implemented for the type `str`
-//~^^ ERROR the trait `core::kinds::Sized` is not implemented for the type `str`
 }
diff --git a/src/test/compile-fail/issue-15965.rs b/src/test/compile-fail/issue-15965.rs
index 935e6770658..f3636edeaa5 100644
--- a/src/test/compile-fail/issue-15965.rs
+++ b/src/test/compile-fail/issue-15965.rs
@@ -10,7 +10,10 @@
 
 fn main() {
     return
-        { return () } //~ ERROR the type of this value must be known in this context
+        { return () }
+//~^ ERROR the type of this value must be known in this context
+//~| ERROR this function takes 1 parameter
+//~| ERROR mismatched types
     ()
     ;
 }
diff --git a/src/test/compile-fail/issue-18345.rs b/src/test/compile-fail/issue-18345.rs
index 298f155faff..c8b3463b091 100644
--- a/src/test/compile-fail/issue-18345.rs
+++ b/src/test/compile-fail/issue-18345.rs
@@ -13,7 +13,9 @@ type Transducer<'t, R, T, U> = |Step<'t, R, U>|: 't -> Step<'t, R, T>;
 
 fn mapping<'f, R, T, U>(f: |T|: 'f -> U) -> &'f Transducer<'f, R, T, U> {
     |step| |r, x|
-        step(r, f(x)) //~ ERROR the type of this value must be known in this context
+        step(r, f(x))
+        //~^ ERROR the type of this value must be known in this context
+        //~| ERROR this function takes 1 parameter but 2 parameters were supplied
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-18532.rs b/src/test/compile-fail/issue-18532.rs
index 9cf922ae990..ec44ab7b277 100644
--- a/src/test/compile-fail/issue-18532.rs
+++ b/src/test/compile-fail/issue-18532.rs
@@ -17,4 +17,5 @@
 fn main() {
     (return)((),());
     //~^ ERROR the type of this value must be known
+    //~| ERROR this function takes 1 parameter
 }
diff --git a/src/test/compile-fail/kindck-inherited-copy-bound.rs b/src/test/compile-fail/kindck-inherited-copy-bound.rs
index 51ee38d5cfe..f5740992af4 100644
--- a/src/test/compile-fail/kindck-inherited-copy-bound.rs
+++ b/src/test/compile-fail/kindck-inherited-copy-bound.rs
@@ -21,10 +21,15 @@ impl<T:Copy> Foo for T {
 
 fn take_param<T:Foo>(foo: &T) { }
 
-fn main() {
+fn a() {
     let x = box 3i;
     take_param(&x); //~ ERROR `core::kinds::Copy` is not implemented
+}
 
+fn b() {
+    let x = box 3i;
     let y = &x;
     let z = &x as &Foo; //~ ERROR `core::kinds::Copy` is not implemented
 }
+
+fn main() { }
diff --git a/src/test/compile-fail/unboxed-closures-unsafe-extern-fn.rs b/src/test/compile-fail/unboxed-closures-unsafe-extern-fn.rs
index a82689b1649..b0b37d077c1 100644
--- a/src/test/compile-fail/unboxed-closures-unsafe-extern-fn.rs
+++ b/src/test/compile-fail/unboxed-closures-unsafe-extern-fn.rs
@@ -20,9 +20,16 @@ fn call_it<F:Fn(&int)->int>(_: &F, _: int) -> int { 0 }
 fn call_it_mut<F:FnMut(&int)->int>(_: &mut F, _: int) -> int { 0 }
 fn call_it_once<F:FnOnce(&int)->int>(_: F, _: int) -> int { 0 }
 
-fn main() {
+fn a() {
     let x = call_it(&square, 22); //~ ERROR not implemented
+}
+
+fn b() {
     let y = call_it_mut(&mut square, 22); //~ ERROR not implemented
+}
+
+fn c() {
     let z = call_it_once(square, 22); //~ ERROR not implemented
 }
 
+fn main() { }
diff --git a/src/test/compile-fail/unboxed-closures-wrong-abi.rs b/src/test/compile-fail/unboxed-closures-wrong-abi.rs
index 920e91958ee..20a4ab85d7b 100644
--- a/src/test/compile-fail/unboxed-closures-wrong-abi.rs
+++ b/src/test/compile-fail/unboxed-closures-wrong-abi.rs
@@ -20,9 +20,17 @@ fn call_it<F:Fn(&int)->int>(_: &F, _: int) -> int { 0 }
 fn call_it_mut<F:FnMut(&int)->int>(_: &mut F, _: int) -> int { 0 }
 fn call_it_once<F:FnOnce(&int)->int>(_: F, _: int) -> int { 0 }
 
-fn main() {
+fn a() {
     let x = call_it(&square, 22); //~ ERROR not implemented
+}
+
+fn b() {
     let y = call_it_mut(&mut square, 22); //~ ERROR not implemented
+}
+
+fn c() {
     let z = call_it_once(square, 22); //~ ERROR not implemented
 }
 
+fn main() { }
+
diff --git a/src/test/compile-fail/unboxed-closures-wrong-arg-type-extern-fn.rs b/src/test/compile-fail/unboxed-closures-wrong-arg-type-extern-fn.rs
index a7a7b1c6762..f08cff3cd68 100644
--- a/src/test/compile-fail/unboxed-closures-wrong-arg-type-extern-fn.rs
+++ b/src/test/compile-fail/unboxed-closures-wrong-arg-type-extern-fn.rs
@@ -21,9 +21,16 @@ fn call_it<F:Fn(&int)->int>(_: &F, _: int) -> int { 0 }
 fn call_it_mut<F:FnMut(&int)->int>(_: &mut F, _: int) -> int { 0 }
 fn call_it_once<F:FnOnce(&int)->int>(_: F, _: int) -> int { 0 }
 
-fn main() {
+fn a() {
     let x = call_it(&square, 22); //~ ERROR not implemented
+}
+
+fn b() {
     let y = call_it_mut(&mut square, 22); //~ ERROR not implemented
+}
+
+fn c() {
     let z = call_it_once(square, 22); //~ ERROR not implemented
 }
 
+fn main() { }
diff --git a/src/test/run-pass/hrtb-opt-in-copy.rs b/src/test/run-pass/hrtb-opt-in-copy.rs
new file mode 100644
index 00000000000..b6bba363e72
--- /dev/null
+++ b/src/test/run-pass/hrtb-opt-in-copy.rs
@@ -0,0 +1,38 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we handle binder levels correctly when checking whether a
+// type can implement `Copy`. In particular, we had a bug where we failed to
+// liberate the late-bound regions from the impl, and thus wound up
+// searching for an impl of `for<'tcx> Foo<&'tcx T>`. The impl that
+// exists however is `impl<T> Copy for Foo<T>` and the current rules
+// did not consider that a match (something I would like to revise in
+// a later PR).
+
+#![allow(dead_code)]
+
+use std::kinds::marker;
+
+#[deriving(Copy)]
+struct Foo<T> { x: T }
+
+type Ty<'tcx> = &'tcx TyS<'tcx>;
+
+enum TyS<'tcx> {
+    Boop(marker::InvariantLifetime<'tcx>)
+}
+
+enum Bar<'tcx> {
+    Baz(Foo<Ty<'tcx>>)
+}
+
+impl<'tcx> Copy for Bar<'tcx> { }
+
+fn main() { }
diff --git a/src/test/run-pass/issue-10501.rs b/src/test/run-pass/issue-10501.rs
deleted file mode 100644
index 78f125398ed..00000000000
--- a/src/test/run-pass/issue-10501.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-pub type Foo = fn(&int) -> ();
-#[deriving(Clone)]
-enum Baz { Bar(Foo) }
-fn main() {}
diff --git a/src/test/run-pass/issue-12741.rs b/src/test/run-pass/issue-12741.rs
deleted file mode 100644
index e41613b4ae3..00000000000
--- a/src/test/run-pass/issue-12741.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#[deriving(Clone)]
-pub struct Foo {
-    f: fn(char, |char| -> char) -> char
-}
-
-impl Foo {
-    fn bar(&self) -> char {
-        ((*self).f)('a', |c: char| c)
-    }
-}
-
-fn bla(c: char, cb: |char| -> char) -> char {
-    cb(c)
-}
-
-pub fn make_foo() -> Foo {
-    Foo {
-        f: bla
-    }
-}
-
-fn main() {
-    let a = make_foo();
-    assert_eq!(a.bar(), 'a');
-}