about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-11-18 19:11:43 +0000
committerbors <bors@rust-lang.org>2014-11-18 19:11:43 +0000
commitc8d6e3b2c2a780eff92299da5d1c02e081617088 (patch)
tree019ec1d05e20c8eba60f305b4c79cc942c1f85d3
parent09e2ad13d0aa01143bcb20dece3ff6c5a7e34ea3 (diff)
parent6866bf32343ce784a256cd0b9c7686a560fd8aa6 (diff)
downloadrust-c8d6e3b2c2a780eff92299da5d1c02e081617088.tar.gz
rust-c8d6e3b2c2a780eff92299da5d1c02e081617088.zip
auto merge of #18993 : nikomatsakis/rust/hrtb-5, r=pcwalton
Enough said.

Fixes #18639.

r? @pcwalton (or someone else?)

This is a [breaking-change]. In particular, several feature gates related to unboxed closures were consolidated into one (`overloaded_calls`, `unboxed_closure_sugar` => `unboxed_closures`). Otherwise, I think everything that worked before should still work. File a bug and cc @nikomatsakis if you find otherwise. :)
-rw-r--r--src/doc/reference.md12
-rw-r--r--src/librustc/diagnostics.rs3
-rw-r--r--src/librustc/metadata/tydecode.rs7
-rw-r--r--src/librustc/metadata/tyencode.rs4
-rw-r--r--src/librustc/middle/astencode.rs4
-rw-r--r--src/librustc/middle/borrowck/mod.rs4
-rw-r--r--src/librustc/middle/check_match.rs7
-rw-r--r--src/librustc/middle/liveness.rs10
-rw-r--r--src/librustc/middle/resolve.rs6
-rw-r--r--src/librustc/middle/resolve_lifetime.rs168
-rw-r--r--src/librustc/middle/subst.rs213
-rw-r--r--src/librustc/middle/traits/coherence.rs10
-rw-r--r--src/librustc/middle/traits/fulfill.rs1
-rw-r--r--src/librustc/middle/traits/mod.rs25
-rw-r--r--src/librustc/middle/traits/select.rs38
-rw-r--r--src/librustc/middle/traits/util.rs28
-rw-r--r--src/librustc/middle/ty.rs744
-rw-r--r--src/librustc/middle/ty_fold.rs228
-rw-r--r--src/librustc/middle/typeck/astconv.rs487
-rw-r--r--src/librustc/middle/typeck/check/method/confirm.rs127
-rw-r--r--src/librustc/middle/typeck/check/method/mod.rs13
-rw-r--r--src/librustc/middle/typeck/check/method/probe.rs119
-rw-r--r--src/librustc/middle/typeck/check/mod.rs331
-rw-r--r--src/librustc/middle/typeck/check/regionmanip.rs35
-rw-r--r--src/librustc/middle/typeck/check/vtable.rs4
-rw-r--r--src/librustc/middle/typeck/check/wf.rs26
-rw-r--r--src/librustc/middle/typeck/collect.rs91
-rw-r--r--src/librustc/middle/typeck/infer/combine.rs61
-rw-r--r--src/librustc/middle/typeck/infer/equate.rs5
-rw-r--r--src/librustc/middle/typeck/infer/error_reporting.rs20
-rw-r--r--src/librustc/middle/typeck/infer/glb.rs146
-rw-r--r--src/librustc/middle/typeck/infer/higher_ranked/doc.rs415
-rw-r--r--src/librustc/middle/typeck/infer/higher_ranked/mod.rs445
-rw-r--r--src/librustc/middle/typeck/infer/lattice.rs27
-rw-r--r--src/librustc/middle/typeck/infer/lub.rs98
-rw-r--r--src/librustc/middle/typeck/infer/mod.rs52
-rw-r--r--src/librustc/middle/typeck/infer/region_inference/doc.rs401
-rw-r--r--src/librustc/middle/typeck/infer/region_inference/mod.rs4
-rw-r--r--src/librustc/middle/typeck/infer/sub.rs92
-rw-r--r--src/librustc/middle/typeck/mod.rs2
-rw-r--r--src/librustc/middle/typeck/rscope.rs45
-rw-r--r--src/librustc/util/ppaux.rs65
-rw-r--r--src/librustc_trans/driver/driver.rs2
-rw-r--r--src/librustc_trans/save/mod.rs2
-rw-r--r--src/librustc_trans/test.rs396
-rw-r--r--src/librustc_trans/trans/base.rs5
-rw-r--r--src/librustc_trans/trans/callee.rs16
-rw-r--r--src/librustc_trans/trans/closure.rs2
-rw-r--r--src/librustc_trans/trans/common.rs15
-rw-r--r--src/librustc_trans/trans/debuginfo.rs4
-rw-r--r--src/librustc_trans/trans/expr.rs6
-rw-r--r--src/librustc_trans/trans/foreign.rs4
-rw-r--r--src/librustc_trans/trans/glue.rs5
-rw-r--r--src/librustc_trans/trans/meth.rs8
-rw-r--r--src/librustc_trans/trans/monomorphize.rs5
-rw-r--r--src/libsyntax/ast.rs2
-rw-r--r--src/libsyntax/ast_util.rs8
-rw-r--r--src/libsyntax/feature_gate.rs23
-rw-r--r--src/libsyntax/fold.rs10
-rw-r--r--src/libsyntax/parse/parser.rs17
-rw-r--r--src/libsyntax/print/pprust.rs4
-rw-r--r--src/libsyntax/visit.rs112
-rw-r--r--src/test/auxiliary/issue-18711.rs2
-rw-r--r--src/test/auxiliary/unboxed-closures-cross-crate.rs2
-rw-r--r--src/test/bench/shootout-reverse-complement.rs2
-rw-r--r--src/test/bench/shootout-spectralnorm.rs2
-rw-r--r--src/test/compile-fail/borrowck-overloaded-call.rs2
-rw-r--r--src/test/compile-fail/borrowck-unboxed-closures.rs2
-rw-r--r--src/test/compile-fail/explicit-self-lifetime-mismatch.rs3
-rw-r--r--src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs60
-rw-r--r--src/test/compile-fail/hrtb-higher-ranker-supertraits.rs58
-rw-r--r--src/test/compile-fail/hrtb-identity-fn-borrows.rs35
-rw-r--r--src/test/compile-fail/issue-15094.rs2
-rw-r--r--src/test/compile-fail/issue-16939.rs2
-rw-r--r--src/test/compile-fail/issue-18532.rs2
-rw-r--r--src/test/compile-fail/overloaded-calls-bad.rs2
-rw-r--r--src/test/compile-fail/overloaded-calls-nontuple.rs2
-rw-r--r--src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs2
-rw-r--r--src/test/compile-fail/regions-name-undeclared.rs3
-rw-r--r--src/test/compile-fail/stage0-clone-contravariant-lifetime.rs43
-rw-r--r--src/test/compile-fail/stage0-cmp.rs40
-rw-r--r--src/test/compile-fail/ufcs-explicit-self-bad.rs16
-rw-r--r--src/test/compile-fail/unboxed-closure-sugar-default.rs12
-rw-r--r--src/test/compile-fail/unboxed-closure-sugar-equiv.rs27
-rw-r--r--src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs2
-rw-r--r--src/test/compile-fail/unboxed-closure-sugar-region.rs19
-rw-r--r--src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs6
-rw-r--r--src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs6
-rw-r--r--src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters.rs4
-rw-r--r--src/test/compile-fail/unboxed-closure-sugar-wrong-trait.rs2
-rw-r--r--src/test/compile-fail/unboxed-closures-fnmut-as-fn.rs2
-rw-r--r--src/test/run-pass/bare-fn-implements-fn-mut.rs2
-rw-r--r--src/test/run-pass/capture-clauses-unboxed-closures.rs2
-rw-r--r--src/test/run-pass/closure-syntax.rs2
-rw-r--r--src/test/run-pass/hrtb-binder-levels-in-object-types.rs34
-rw-r--r--src/test/run-pass/hrtb-debruijn-object-types-in-closures.rs23
-rw-r--r--src/test/run-pass/hrtb-fn-like-trait-object.rs35
-rw-r--r--src/test/run-pass/hrtb-fn-like-trait.rs35
-rw-r--r--src/test/run-pass/hrtb-parse.rs1
-rw-r--r--src/test/run-pass/hrtb-resolve-lifetime.rs20
-rw-r--r--src/test/run-pass/hrtb-trait-object-paren-notation.rs37
-rw-r--r--src/test/run-pass/hrtb-trait-object-passed-to-closure.rs31
-rw-r--r--src/test/run-pass/hrtb-unboxed-closure-trait.rs22
-rw-r--r--src/test/run-pass/issue-14958.rs2
-rw-r--r--src/test/run-pass/issue-14959.rs2
-rw-r--r--src/test/run-pass/issue-16774.rs2
-rw-r--r--src/test/run-pass/issue-18652.rs2
-rw-r--r--src/test/run-pass/issue-18685.rs2
-rw-r--r--src/test/run-pass/issue-18711.rs2
-rw-r--r--src/test/run-pass/overloaded-calls-param-vtables.rs2
-rw-r--r--src/test/run-pass/overloaded-calls-simple.rs2
-rw-r--r--src/test/run-pass/overloaded-calls-zero-args.rs2
-rw-r--r--src/test/run-pass/unboxed-closures-all-traits.rs2
-rw-r--r--src/test/run-pass/unboxed-closures-by-ref.rs2
-rw-r--r--src/test/run-pass/unboxed-closures-direct-sugary-call.rs2
-rw-r--r--src/test/run-pass/unboxed-closures-drop.rs2
-rw-r--r--src/test/run-pass/unboxed-closures-extern-fn.rs4
-rw-r--r--src/test/run-pass/unboxed-closures-fn-as-fnmut-and-fnonce.rs4
-rw-r--r--src/test/run-pass/unboxed-closures-fnmut-as-fnonce.rs4
-rw-r--r--src/test/run-pass/unboxed-closures-manual-impl.rs2
-rw-r--r--src/test/run-pass/unboxed-closures-prelude.rs2
-rw-r--r--src/test/run-pass/unboxed-closures-single-word-env.rs2
-rw-r--r--src/test/run-pass/unboxed-closures-sugar-1.rs34
-rw-r--r--src/test/run-pass/unboxed-closures-sugar-object.rs1
-rw-r--r--src/test/run-pass/unboxed-closures-unboxing-shim.rs2
-rw-r--r--src/test/run-pass/unboxed-closures-unique-type-id.rs2
126 files changed, 3954 insertions, 2004 deletions
diff --git a/src/doc/reference.md b/src/doc/reference.md
index 171c39a4cbc..62e0f5e4f1f 100644
--- a/src/doc/reference.md
+++ b/src/doc/reference.md
@@ -2513,11 +2513,6 @@ The currently implemented features of the reference compiler are:
                closure as `once` is unlikely to be supported going forward. So
                they are hidden behind this feature until they are to be removed.
 
-* `overloaded_calls` - Allow implementing the `Fn*` family of traits on user
-                       types, allowing overloading the call operator (`()`).
-                       This feature may still undergo changes before being
-                       stabilized.
-
 * `phase` - Usage of the `#[phase]` attribute allows loading compiler plugins
             for custom lints or syntax extensions. The implementation is
             considered unwholesome and in need of overhaul, and it is not clear
@@ -2560,11 +2555,8 @@ The currently implemented features of the reference compiler are:
 * `trace_macros` - Allows use of the `trace_macros` macro, which is a nasty
                    hack that will certainly be removed.
 
-* `unboxed_closure_sugar` - Allows using `|Foo| -> Bar` as a trait bound
-                            meaning one of the `Fn` traits. Still
-                            experimental.
-
-* `unboxed_closures` - A work in progress feature with many known bugs.
+* `unboxed_closures` - Rust's new closure design, which is currently a work in
+                       progress feature with many known bugs.
 
 * `unsafe_destructor` - Allows use of the `#[unsafe_destructor]` attribute,
                         which is considered wildly unsafe and will be
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index b5dafdb1807..afbb18faa0b 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -144,5 +144,6 @@ register_diagnostics!(
     E0165,
     E0166,
     E0167,
-    E0168
+    E0168,
+    E0169
 )
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index e1b0797e982..69be2e34915 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -294,7 +294,7 @@ fn parse_region(st: &mut PState, conv: conv_did) -> ty::Region {
     match next(st) {
       'b' => {
         assert_eq!(next(st), '[');
-        let id = parse_uint(st) as ast::NodeId;
+        let id = ty::DebruijnIndex::new(parse_uint(st));
         assert_eq!(next(st), '|');
         let br = parse_bound_region(st, |x,y| conv(x,y));
         assert_eq!(next(st), ']');
@@ -579,8 +579,6 @@ fn parse_bare_fn_ty(st: &mut PState, conv: conv_did) -> ty::BareFnTy {
 
 fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig {
     assert_eq!(next(st), '[');
-    let id = parse_uint(st) as ast::NodeId;
-    assert_eq!(next(st), '|');
     let mut inputs = Vec::new();
     while peek(st) != ']' {
         inputs.push(parse_ty(st, |x,y| conv(x,y)));
@@ -598,8 +596,7 @@ fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig {
         }
         _ => ty::FnConverging(parse_ty(st, |x,y| conv(x,y)))
     };
-    ty::FnSig {binder_id: id,
-               inputs: inputs,
+    ty::FnSig {inputs: inputs,
                output: output,
                variadic: variadic}
 }
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 3242d396146..a53f5fa187d 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -130,7 +130,7 @@ fn enc_region_substs(w: &mut SeekableMemWriter, cx: &ctxt, substs: &subst::Regio
 pub fn enc_region(w: &mut SeekableMemWriter, cx: &ctxt, r: ty::Region) {
     match r {
         ty::ReLateBound(id, br) => {
-            mywrite!(w, "b[{}|", id);
+            mywrite!(w, "b[{}|", id.depth);
             enc_bound_region(w, cx, br);
             mywrite!(w, "]");
         }
@@ -331,7 +331,7 @@ pub fn enc_closure_ty(w: &mut SeekableMemWriter, cx: &ctxt, ft: &ty::ClosureTy)
 }
 
 fn enc_fn_sig(w: &mut SeekableMemWriter, cx: &ctxt, fsig: &ty::FnSig) {
-    mywrite!(w, "[{}|", fsig.binder_id);
+    mywrite!(w, "[");
     for ty in fsig.inputs.iter() {
         enc_ty(w, cx, *ty);
     }
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 20dcf094b66..ff6965574be 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -483,8 +483,8 @@ impl tr for def::Def {
 impl tr for ty::Region {
     fn tr(&self, dcx: &DecodeContext) -> ty::Region {
         match *self {
-            ty::ReLateBound(id, br) => {
-                ty::ReLateBound(dcx.tr_id(id), br.tr(dcx))
+            ty::ReLateBound(debruijn, br) => {
+                ty::ReLateBound(debruijn, br.tr(dcx))
             }
             ty::ReEarlyBound(id, space, index, ident) => {
                 ty::ReEarlyBound(dcx.tr_id(id), space, index, ident)
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index 44206343c10..d7925177c29 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -65,8 +65,8 @@ pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator
 
 impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
     fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
-                b: &'v Block, s: Span, n: NodeId) {
-        borrowck_fn(self, fk, fd, b, s, n);
+                b: &'v Block, s: Span, id: ast::NodeId) {
+        borrowck_fn(self, fk, fd, b, s, id);
     }
 
     fn visit_item(&mut self, item: &ast::Item) {
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index d67c5b0dece..c733084e981 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -139,8 +139,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> {
         check_local(self, l);
     }
     fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
-                b: &'v Block, s: Span, _: NodeId) {
-        check_fn(self, fk, fd, b, s);
+                b: &'v Block, s: Span, n: NodeId) {
+        check_fn(self, fk, fd, b, s, n);
     }
 }
 
@@ -920,7 +920,8 @@ fn check_fn(cx: &mut MatchCheckCtxt,
             kind: FnKind,
             decl: &FnDecl,
             body: &Block,
-            sp: Span) {
+            sp: Span,
+            _: NodeId) {
     visit::walk_fn(cx, kind, decl, body, sp);
     for input in decl.inputs.iter() {
         is_refutable(cx, &*input.pat, |pat| {
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index c773467552a..8604c3967a9 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -187,9 +187,8 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, cx: &ty::ctxt) -> String {
 }
 
 impl<'a, 'tcx, 'v> Visitor<'v> for IrMaps<'a, 'tcx> {
-    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
-                b: &'v Block, s: Span, n: NodeId) {
-        visit_fn(self, fk, fd, b, s, n);
+    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, b: &'v Block, s: Span, id: ast::NodeId) {
+        visit_fn(self, fk, fd, b, s, id);
     }
     fn visit_local(&mut self, l: &ast::Local) { visit_local(self, l); }
     fn visit_expr(&mut self, ex: &Expr) { visit_expr(self, ex); }
@@ -374,9 +373,8 @@ fn visit_fn(ir: &mut IrMaps,
             decl: &FnDecl,
             body: &Block,
             sp: Span,
-            id: NodeId) {
-    debug!("visit_fn: id={}", id);
-    let _i = ::util::common::indenter();
+            id: ast::NodeId) {
+    debug!("visit_fn");
 
     // swap in a new set of IR maps for this function body:
     let mut fn_maps = IrMaps::new(ir.tcx);
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 94b52532a94..baf53cc34ba 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -5038,10 +5038,10 @@ impl<'a> Resolver<'a> {
                 visit::walk_ty(self, ty);
             }
 
-            TyPolyTraitRef(ref poly_trait_ref) => {
-                self.resolve_poly_trait_reference(
+            TyPolyTraitRef(ref bounds) => {
+                self.resolve_type_parameter_bounds(
                     ty.id,
-                    &**poly_trait_ref,
+                    bounds,
                     TraitObject);
                 visit::walk_ty(self, ty);
             }
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 4077629f76d..a1257caf47f 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -21,11 +21,13 @@ pub use self::DefRegion::*;
 use self::ScopeChain::*;
 
 use session::Session;
+use middle::def;
+use middle::resolve::DefMap;
 use middle::subst;
+use middle::ty;
 use std::fmt;
 use syntax::ast;
 use syntax::codemap::Span;
-use syntax::owned_slice::OwnedSlice;
 use syntax::parse::token::special_idents;
 use syntax::parse::token;
 use syntax::print::pprust::{lifetime_to_string};
@@ -39,8 +41,7 @@ pub enum DefRegion {
     DefEarlyBoundRegion(/* space */ subst::ParamSpace,
                         /* index */ uint,
                         /* lifetime decl */ ast::NodeId),
-    DefLateBoundRegion(/* binder_id */ ast::NodeId,
-                       /* depth */ uint,
+    DefLateBoundRegion(ty::DebruijnIndex,
                        /* lifetime decl */ ast::NodeId),
     DefFreeRegion(/* block scope */ ast::NodeId,
                   /* lifetime decl */ ast::NodeId),
@@ -53,16 +54,17 @@ pub type NamedRegionMap = NodeMap<DefRegion>;
 struct LifetimeContext<'a> {
     sess: &'a Session,
     named_region_map: &'a mut NamedRegionMap,
-    scope: Scope<'a>
+    scope: Scope<'a>,
+    def_map: &'a DefMap,
 }
 
 enum ScopeChain<'a> {
     /// EarlyScope(i, ['a, 'b, ...], s) extends s with early-bound
     /// lifetimes, assigning indexes 'a => i, 'b => i+1, ... etc.
     EarlyScope(subst::ParamSpace, &'a Vec<ast::LifetimeDef>, Scope<'a>),
-    /// LateScope(binder_id, ['a, 'b, ...], s) extends s with late-bound
+    /// LateScope(['a, 'b, ...], s) extends s with late-bound
     /// lifetimes introduced by the declaration binder_id.
-    LateScope(ast::NodeId, &'a Vec<ast::LifetimeDef>, Scope<'a>),
+    LateScope(&'a Vec<ast::LifetimeDef>, Scope<'a>),
     /// lifetimes introduced by items within a code block are scoped
     /// to that block.
     BlockScope(ast::NodeId, Scope<'a>),
@@ -73,12 +75,13 @@ type Scope<'a> = &'a ScopeChain<'a>;
 
 static ROOT_SCOPE: ScopeChain<'static> = RootScope;
 
-pub fn krate(sess: &Session, krate: &ast::Crate) -> NamedRegionMap {
+pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegionMap {
     let mut named_region_map = NodeMap::new();
     visit::walk_crate(&mut LifetimeContext {
         sess: sess,
         named_region_map: &mut named_region_map,
-        scope: &ROOT_SCOPE
+        scope: &ROOT_SCOPE,
+        def_map: def_map,
     }, krate);
     sess.abort_if_errors();
     named_region_map
@@ -102,8 +105,7 @@ 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::ItemImpl(ref generics, _, _, _) => {
+            ast::ItemTrait(ref generics, _, _, _) => {
                 // These kinds of items have only early bound lifetime parameters.
                 let lifetimes = &generics.lifetimes;
                 self.with(EarlyScope(subst::TypeSpace, lifetimes, &ROOT_SCOPE), |this| {
@@ -111,16 +113,23 @@ 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.
+                self.visit_early_late(subst::TypeSpace, generics, |this| {
+                    this.check_lifetime_defs(&generics.lifetimes);
+                    visit::walk_item(this, item);
+                })
+            }
         }
     }
 
     fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl,
-                b: &'v ast::Block, s: Span, n: ast::NodeId) {
+                b: &'v ast::Block, s: Span, _: ast::NodeId) {
         match fk {
             visit::FkItemFn(_, generics, _, _) |
             visit::FkMethod(_, generics, _) => {
                 self.visit_early_late(
-                    subst::FnSpace, n, generics,
+                    subst::FnSpace, generics,
                     |this| visit::walk_fn(this, fk, fd, b, s))
             }
             visit::FkFnBlock(..) => {
@@ -130,21 +139,58 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
     }
 
     fn visit_ty(&mut self, ty: &ast::Ty) {
-        let lifetimes = match ty.node {
-            ast::TyClosure(ref c) | ast::TyProc(ref c) => &c.lifetimes,
-            ast::TyBareFn(ref c) => &c.lifetimes,
-            _ => return visit::walk_ty(self, ty)
-        };
-
-        self.with(LateScope(ty.id, lifetimes, self.scope), |this| {
-            this.check_lifetime_defs(lifetimes);
-            visit::walk_ty(this, ty);
-        });
+        match ty.node {
+            ast::TyClosure(ref c) | ast::TyProc(ref c) => {
+                // Careful, the bounds on a closure/proc are *not* within its binder.
+                visit::walk_ty_param_bounds_helper(self, &c.bounds);
+                visit::walk_lifetime_decls_helper(self, &c.lifetimes);
+                self.with(LateScope(&c.lifetimes, self.scope), |this| {
+                    this.check_lifetime_defs(&c.lifetimes);
+                    for argument in c.decl.inputs.iter() {
+                        this.visit_ty(&*argument.ty)
+                    }
+                    visit::walk_fn_ret_ty(this, &c.decl.output);
+                });
+            }
+            ast::TyBareFn(ref c) => {
+                visit::walk_lifetime_decls_helper(self, &c.lifetimes);
+                self.with(LateScope(&c.lifetimes, self.scope), |this| {
+                    // a bare fn has no bounds, so everything
+                    // contained within is scoped within its binder.
+                    this.check_lifetime_defs(&c.lifetimes);
+                    visit::walk_ty(this, ty);
+                });
+            }
+            ast::TyPath(ref path, ref opt_bounds, id) => {
+                // if this path references a trait, then this will resolve to
+                // a trait ref, which introduces a binding scope.
+                match self.def_map.borrow().get(&id) {
+                    Some(&def::DefTrait(..)) => {
+                        self.with(LateScope(&Vec::new(), self.scope), |this| {
+                            this.visit_path(path, id);
+                        });
+
+                        match *opt_bounds {
+                            Some(ref bounds) => {
+                                visit::walk_ty_param_bounds_helper(self, bounds);
+                            }
+                            None => { }
+                        }
+                    }
+                    _ => {
+                        visit::walk_ty(self, ty);
+                    }
+                }
+            }
+            _ => {
+                visit::walk_ty(self, ty)
+            }
+        }
     }
 
     fn visit_ty_method(&mut self, m: &ast::TypeMethod) {
         self.visit_early_late(
-            subst::FnSpace, m.id, &m.generics,
+            subst::FnSpace, &m.generics,
             |this| visit::walk_ty_method(this, m))
     }
 
@@ -162,7 +208,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
 
     fn visit_generics(&mut self, generics: &ast::Generics) {
         for ty_param in generics.ty_params.iter() {
-            self.visit_ty_param_bounds(&ty_param.bounds);
+            visit::walk_ty_param_bounds_helper(self, &ty_param.bounds);
             match ty_param.default {
                 Some(ref ty) => self.visit_ty(&**ty),
                 None => {}
@@ -170,41 +216,14 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
         }
         for predicate in generics.where_clause.predicates.iter() {
             self.visit_ident(predicate.span, predicate.ident);
-            self.visit_ty_param_bounds(&predicate.bounds);
-        }
-    }
-}
-
-impl<'a> LifetimeContext<'a> {
-    fn with(&mut self, wrap_scope: ScopeChain, f: |&mut LifetimeContext|) {
-        let LifetimeContext {sess, ref mut named_region_map, ..} = *self;
-        let mut this = LifetimeContext {
-            sess: sess,
-            named_region_map: *named_region_map,
-            scope: &wrap_scope
-        };
-        debug!("entering scope {}", this.scope);
-        f(&mut this);
-        debug!("exiting scope {}", this.scope);
-    }
-
-    fn visit_ty_param_bounds(&mut self,
-                             bounds: &OwnedSlice<ast::TyParamBound>) {
-        for bound in bounds.iter() {
-            match *bound {
-                ast::TraitTyParamBound(ref trait_ref) => {
-                    self.visit_poly_trait_ref(trait_ref);
-                }
-                ast::RegionTyParamBound(ref lifetime) => {
-                    self.visit_lifetime_ref(lifetime);
-                }
-            }
+            visit::walk_ty_param_bounds_helper(self, &predicate.bounds);
         }
     }
 
     fn visit_poly_trait_ref(&mut self, trait_ref: &ast::PolyTraitRef) {
-        let ref_id = trait_ref.trait_ref.ref_id;
-        self.with(LateScope(ref_id, &trait_ref.bound_lifetimes, self.scope), |this| {
+        debug!("visit_poly_trait_ref trait_ref={}", trait_ref);
+
+        self.with(LateScope(&trait_ref.bound_lifetimes, self.scope), |this| {
             this.check_lifetime_defs(&trait_ref.bound_lifetimes);
             for lifetime in trait_ref.bound_lifetimes.iter() {
                 this.visit_lifetime_decl(lifetime);
@@ -216,11 +235,25 @@ impl<'a> LifetimeContext<'a> {
     fn visit_trait_ref(&mut self, trait_ref: &ast::TraitRef) {
         self.visit_path(&trait_ref.path, trait_ref.ref_id);
     }
+}
+
+impl<'a> LifetimeContext<'a> {
+    fn with(&mut self, wrap_scope: ScopeChain, f: |&mut LifetimeContext|) {
+        let LifetimeContext {sess, ref mut named_region_map, ..} = *self;
+        let mut this = LifetimeContext {
+            sess: sess,
+            named_region_map: *named_region_map,
+            scope: &wrap_scope,
+            def_map: self.def_map,
+        };
+        debug!("entering scope {}", this.scope);
+        f(&mut this);
+        debug!("exiting scope {}", this.scope);
+    }
 
     /// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
     fn visit_early_late(&mut self,
                         early_space: subst::ParamSpace,
-                        binder_id: ast::NodeId,
                         generics: &ast::Generics,
                         walk: |&mut LifetimeContext|) {
         /*!
@@ -249,15 +282,14 @@ impl<'a> LifetimeContext<'a> {
 
         let referenced_idents = early_bound_lifetime_names(generics);
 
-        debug!("visit_early_late: binder_id={} referenced_idents={}",
-               binder_id,
+        debug!("visit_early_late: referenced_idents={}",
                referenced_idents);
 
         let (early, late) = generics.lifetimes.clone().partition(
             |l| referenced_idents.iter().any(|&i| i == l.lifetime.name));
 
         self.with(EarlyScope(early_space, &early, self.scope), |this| {
-            this.with(LateScope(binder_id, &late, this.scope), |this| {
+            this.with(LateScope(&late, this.scope), |this| {
                 this.check_lifetime_defs(&generics.lifetimes);
                 walk(this);
             });
@@ -271,7 +303,7 @@ impl<'a> LifetimeContext<'a> {
         // block, then the lifetime is not bound but free, so switch
         // over to `resolve_free_lifetime_ref()` to complete the
         // search.
-        let mut depth = 0;
+        let mut late_depth = 0;
         let mut scope = self.scope;
         loop {
             match *scope {
@@ -291,22 +323,22 @@ impl<'a> LifetimeContext<'a> {
                             return;
                         }
                         None => {
-                            depth += 1;
                             scope = s;
                         }
                     }
                 }
 
-                LateScope(binder_id, lifetimes, s) => {
+                LateScope(lifetimes, s) => {
                     match search_lifetimes(lifetimes, lifetime_ref) {
                         Some((_index, decl_id)) => {
-                            let def = DefLateBoundRegion(binder_id, depth, decl_id);
+                            let debruijn = ty::DebruijnIndex::new(late_depth + 1);
+                            let def = DefLateBoundRegion(debruijn, decl_id);
                             self.insert_lifetime(lifetime_ref, def);
                             return;
                         }
 
                         None => {
-                            depth += 1;
+                            late_depth += 1;
                             scope = s;
                         }
                     }
@@ -339,7 +371,7 @@ impl<'a> LifetimeContext<'a> {
                 }
 
                 EarlyScope(_, lifetimes, s) |
-                LateScope(_, lifetimes, s) => {
+                LateScope(lifetimes, s) => {
                     search_result = search_lifetimes(lifetimes, lifetime_ref);
                     if search_result.is_some() {
                         break;
@@ -467,10 +499,10 @@ fn early_bound_lifetime_names(generics: &ast::Generics) -> Vec<ast::Name> {
             FreeLifetimeCollector { early_bound: &mut early_bound,
                                     late_bound: &mut late_bound };
         for ty_param in generics.ty_params.iter() {
-            visit::walk_ty_param_bounds(&mut collector, &ty_param.bounds);
+            visit::walk_ty_param_bounds_helper(&mut collector, &ty_param.bounds);
         }
         for predicate in generics.where_clause.predicates.iter() {
-            visit::walk_ty_param_bounds(&mut collector, &predicate.bounds);
+            visit::walk_ty_param_bounds_helper(&mut collector, &predicate.bounds);
         }
     }
 
@@ -517,7 +549,7 @@ impl<'a> fmt::Show for ScopeChain<'a> {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             EarlyScope(space, defs, _) => write!(fmt, "EarlyScope({}, {})", space, defs),
-            LateScope(id, defs, _) => write!(fmt, "LateScope({}, {})", id, defs),
+            LateScope(defs, _) => write!(fmt, "LateScope({})", defs),
             BlockScope(id, _) => write!(fmt, "BlockScope({})", id),
             RootScope => write!(fmt, "RootScope"),
         }
diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs
index 520209257f5..4fabdabf3db 100644
--- a/src/librustc/middle/subst.rs
+++ b/src/librustc/middle/subst.rs
@@ -14,8 +14,7 @@ pub use self::ParamSpace::*;
 pub use self::RegionSubsts::*;
 
 use middle::ty;
-use middle::ty_fold;
-use middle::ty_fold::{TypeFoldable, TypeFolder};
+use middle::ty_fold::{mod, TypeFoldable, TypeFolder};
 use util::ppaux::Repr;
 
 use std::fmt;
@@ -101,6 +100,17 @@ impl Substs {
         regions_is_noop && self.types.is_empty()
     }
 
+    pub fn has_regions_escaping_depth(&self, depth: uint) -> bool {
+        self.types.iter().any(|&t| ty::type_escapes_depth(t, depth)) || {
+            match self.regions {
+                ErasedRegions =>
+                    false,
+                NonerasedRegions(ref regions) =>
+                    regions.iter().any(|r| r.escapes_depth(depth)),
+            }
+        }
+    }
+
     pub fn self_ty(&self) -> Option<ty::t> {
         self.types.get_self().map(|&t| t)
     }
@@ -166,6 +176,13 @@ impl RegionSubsts {
             NonerasedRegions(r) => NonerasedRegions(op(r, a))
         }
     }
+
+    pub fn is_erased(&self) -> bool {
+        match *self {
+            ErasedRegions => true,
+            NonerasedRegions(_) => false,
+        }
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -392,6 +409,10 @@ impl<T> VecPerParamSpace<T> {
         self.content.iter()
     }
 
+    pub fn iter_enumerated<'a>(&'a self) -> EnumeratedItems<'a,T> {
+        EnumeratedItems::new(self)
+    }
+
     pub fn as_slice(&self) -> &[T] {
         self.content.as_slice()
     }
@@ -421,6 +442,14 @@ impl<T> VecPerParamSpace<T> {
                                        self.assoc_limit)
     }
 
+    pub fn map_enumerated<U>(&self, pred: |(ParamSpace, uint, &T)| -> U) -> VecPerParamSpace<U> {
+        let result = self.iter_enumerated().map(pred).collect();
+        VecPerParamSpace::new_internal(result,
+                                       self.type_limit,
+                                       self.self_limit,
+                                       self.assoc_limit)
+    }
+
     pub fn map_move<U>(self, pred: |T| -> U) -> VecPerParamSpace<U> {
         let SeparateVecsPerParamSpace {
             types: t,
@@ -457,6 +486,49 @@ impl<T> VecPerParamSpace<T> {
     }
 }
 
+pub struct EnumeratedItems<'a,T:'a> {
+    vec: &'a VecPerParamSpace<T>,
+    space_index: uint,
+    elem_index: uint
+}
+
+impl<'a,T> EnumeratedItems<'a,T> {
+    fn new(v: &'a VecPerParamSpace<T>) -> EnumeratedItems<'a,T> {
+        let mut result = EnumeratedItems { vec: v, space_index: 0, elem_index: 0 };
+        result.adjust_space();
+        result
+    }
+
+    fn adjust_space(&mut self) {
+        let spaces = ParamSpace::all();
+        while
+            self.space_index < spaces.len() &&
+            self.elem_index >= self.vec.len(spaces[self.space_index])
+        {
+            self.space_index += 1;
+            self.elem_index = 0;
+        }
+    }
+}
+
+impl<'a,T> Iterator<(ParamSpace, uint, &'a T)> for EnumeratedItems<'a,T> {
+    fn next(&mut self) -> Option<(ParamSpace, uint, &'a T)> {
+        let spaces = ParamSpace::all();
+        if self.space_index < spaces.len() {
+            let space = spaces[self.space_index];
+            let index = self.elem_index;
+            let item = self.vec.get(space, index);
+
+            self.elem_index += 1;
+            self.adjust_space();
+
+            Some((space, index, item))
+        } else {
+            None
+        }
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Public trait `Subst`
 //
@@ -486,7 +558,8 @@ impl<T:TypeFoldable> Subst for T {
                                        substs: substs,
                                        span: span,
                                        root_ty: None,
-                                       ty_stack_depth: 0 };
+                                       ty_stack_depth: 0,
+                                       region_binders_passed: 0 };
         (*self).fold_with(&mut folder)
     }
 }
@@ -506,11 +579,22 @@ struct SubstFolder<'a, 'tcx: 'a> {
 
     // Depth of type stack
     ty_stack_depth: uint,
+
+    // Number of region binders we have passed through while doing the substitution
+    region_binders_passed: uint,
 }
 
 impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
     fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx }
 
+    fn enter_region_binder(&mut self) {
+        self.region_binders_passed += 1;
+    }
+
+    fn exit_region_binder(&mut self) {
+        self.region_binders_passed -= 1;
+    }
+
     fn fold_region(&mut self, r: ty::Region) -> ty::Region {
         // Note: This routine only handles regions that are bound on
         // type declarations and other outer declarations, not those
@@ -524,7 +608,9 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
                     ErasedRegions => ty::ReStatic,
                     NonerasedRegions(ref regions) =>
                         match regions.opt_get(space, i) {
-                            Some(t) => *t,
+                            Some(&r) => {
+                                self.shift_region_through_binders(r)
+                            }
                             None => {
                                 let span = self.span.unwrap_or(DUMMY_SP);
                                 self.tcx().sess.span_bug(
@@ -557,12 +643,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
 
         let t1 = match ty::get(t).sty {
             ty::ty_param(p) => {
-                check(self,
-                      p,
-                      t,
-                      self.substs.types.opt_get(p.space, p.idx),
-                      p.space,
-                      p.idx)
+                self.ty_for_param(p, t)
             }
             _ => {
                 ty_fold::super_fold_ty(self, t)
@@ -576,30 +657,100 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
         }
 
         return t1;
+    }
+}
 
-        fn check(this: &SubstFolder,
-                 p: ty::ParamTy,
-                 source_ty: ty::t,
-                 opt_ty: Option<&ty::t>,
-                 space: ParamSpace,
-                 index: uint)
-                 -> ty::t {
-            match opt_ty {
-                Some(t) => *t,
-                None => {
-                    let span = this.span.unwrap_or(DUMMY_SP);
-                    this.tcx().sess.span_bug(
-                        span,
-                        format!("Type parameter `{}` ({}/{}/{}) out of range \
+impl<'a,'tcx> SubstFolder<'a,'tcx> {
+    fn ty_for_param(&self, p: ty::ParamTy, source_ty: ty::t) -> ty::t {
+        // Look up the type in the substitutions. It really should be in there.
+        let opt_ty = self.substs.types.opt_get(p.space, p.idx);
+        let ty = match opt_ty {
+            Some(t) => *t,
+            None => {
+                let span = self.span.unwrap_or(DUMMY_SP);
+                self.tcx().sess.span_bug(
+                    span,
+                    format!("Type parameter `{}` ({}/{}/{}) out of range \
                                  when substituting (root type={}) substs={}",
-                                p.repr(this.tcx()),
-                                source_ty.repr(this.tcx()),
-                                space,
-                                index,
-                                this.root_ty.repr(this.tcx()),
-                                this.substs.repr(this.tcx())).as_slice());
-                }
+                            p.repr(self.tcx()),
+                            source_ty.repr(self.tcx()),
+                            p.space,
+                            p.idx,
+                            self.root_ty.repr(self.tcx()),
+                            self.substs.repr(self.tcx())).as_slice());
             }
+        };
+
+        self.shift_regions_through_binders(ty)
+    }
+
+    fn shift_regions_through_binders(&self, ty: ty::t) -> ty::t {
+        /*!
+         * It is sometimes necessary to adjust the debruijn indices
+         * during substitution. This occurs when we are substituting a
+         * type with escaping regions into a context where we have
+         * passed through region binders. That's quite a
+         * mouthful. Let's see an example:
+         *
+         * ```
+         * type Func<A> = fn(A);
+         * type MetaFunc = for<'a> fn(Func<&'a int>)
+         * ```
+         *
+         * The type `MetaFunc`, when fully expanded, will be
+         *
+         *     for<'a> fn(fn(&'a int))
+         *             ^~ ^~ ^~~
+         *             |  |  |
+         *             |  |  DebruijnIndex of 2
+         *             Binders
+         *
+         * Here the `'a` lifetime is bound in the outer function, but
+         * appears as an argument of the inner one. Therefore, that
+         * appearance will have a DebruijnIndex of 2, because we must
+         * skip over the inner binder (remember that we count Debruijn
+         * indices from 1). However, in the definition of `MetaFunc`,
+         * the binder is not visible, so the type `&'a int` will have
+         * a debruijn index of 1. It's only during the substitution
+         * that we can see we must increase the depth by 1 to account
+         * for the binder that we passed through.
+         *
+         * As a second example, consider this twist:
+         *
+         * ```
+         * type FuncTuple<A> = (A,fn(A));
+         * type MetaFuncTuple = for<'a> fn(FuncTuple<&'a int>)
+         * ```
+         *
+         * Here the final type will be:
+         *
+         *     for<'a> fn((&'a int, fn(&'a int)))
+         *                 ^~~         ^~~
+         *                 |           |
+         *          DebruijnIndex of 1 |
+         *                      DebruijnIndex of 2
+         *
+         * As indicated in the diagram, here the same type `&'a int`
+         * is substituted once, but in the first case we do not
+         * increase the Debruijn index and in the second case we
+         * do. The reason is that only in the second case have we
+         * passed through a fn binder.
+         */
+
+        debug!("shift_regions(ty={}, region_binders_passed={}, type_has_escaping_regions={})",
+               ty.repr(self.tcx()), self.region_binders_passed, ty::type_has_escaping_regions(ty));
+
+        if self.region_binders_passed == 0 || !ty::type_has_escaping_regions(ty) {
+            return ty;
         }
+
+        let result = ty_fold::shift_regions(self.tcx(), self.region_binders_passed, &ty);
+        debug!("shift_regions: shifted result = {}", result.repr(self.tcx()));
+
+        result
+    }
+
+    fn shift_region_through_binders(&self, region: ty::Region) -> ty::Region {
+        ty_fold::shift_region(region, self.region_binders_passed)
     }
 }
diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs
index be5a007c1eb..405f6509e59 100644
--- a/src/librustc/middle/traits/coherence.rs
+++ b/src/librustc/middle/traits/coherence.rs
@@ -17,7 +17,7 @@ use super::util;
 use middle::subst;
 use middle::subst::Subst;
 use middle::ty;
-use middle::typeck::infer::InferCtxt;
+use middle::typeck::infer::{mod, InferCtxt};
 use syntax::ast;
 use syntax::codemap::DUMMY_SP;
 use util::ppaux::Repr;
@@ -38,14 +38,18 @@ pub fn impl_can_satisfy(infcx: &InferCtxt,
         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);
+                                                   .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;
 
     // 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::misc(DUMMY_SP, impl1_trait_ref);
-    debug!("impl_can_satisfy obligation={}", obligation.repr(infcx.tcx));
+    debug!("impl_can_satisfy(obligation={})", obligation.repr(infcx.tcx));
     selcx.evaluate_impl(impl2_def_id, &obligation)
 }
 
diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs
index 6baf4e6c048..ff5d80c8a16 100644
--- a/src/librustc/middle/traits/fulfill.rs
+++ b/src/librustc/middle/traits/fulfill.rs
@@ -55,6 +55,7 @@ impl FulfillmentContext {
                                obligation: Obligation)
     {
         debug!("register_obligation({})", obligation.repr(tcx));
+        assert!(!obligation.trait_ref.has_escaping_regions());
         self.trait_obligations.push(obligation);
     }
 
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index 9e0abb897f7..c9c9e3bd4ff 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -281,33 +281,28 @@ pub fn overlapping_impls(infcx: &InferCtxt,
     coherence::impl_can_satisfy(infcx, impl2_def_id, impl1_def_id)
 }
 
-pub fn impl_obligations(tcx: &ty::ctxt,
-                        cause: ObligationCause,
-                        impl_def_id: ast::DefId,
-                        impl_substs: &subst::Substs)
-                        -> subst::VecPerParamSpace<Obligation>
-{
-    let impl_generics = ty::lookup_item_type(tcx, impl_def_id).generics;
-    obligations_for_generics(tcx, cause, &impl_generics, impl_substs)
-}
-
 pub fn obligations_for_generics(tcx: &ty::ctxt,
                                 cause: ObligationCause,
-                                generics: &ty::Generics,
-                                substs: &subst::Substs)
+                                generic_bounds: &ty::GenericBounds,
+                                type_substs: &subst::VecPerParamSpace<ty::t>)
                                 -> subst::VecPerParamSpace<Obligation>
 {
     /*!
-     * Given generics for an impl like:
+     * Given generic bounds from an impl like:
      *
      *    impl<A:Foo, B:Bar+Qux> ...
      *
-     * and a substs vector like `<A=A0, B=B0>`, yields a result like
+     * along with the bindings for the types `A` and `B` (e.g.,
+     * `<A=A0, B=B0>`), yields a result like
      *
      *    [[Foo for A0, Bar for B0, Qux for B0], [], []]
+     *
+     * Expects that `generic_bounds` have already been fully
+     * substituted, late-bound regions liberated and so forth,
+     * so that they are in the same namespace as `type_substs`.
      */
 
-    util::obligations_for_generics(tcx, cause, 0, generics, substs)
+    util::obligations_for_generics(tcx, cause, 0, generic_bounds, type_substs)
 }
 
 pub fn obligation_for_builtin_bound(tcx: &ty::ctxt,
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index bb0db874b67..a941d2b079e 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -31,9 +31,7 @@ use middle::fast_reject;
 use middle::mem_categorization::Typer;
 use middle::subst::{Subst, Substs, VecPerParamSpace};
 use middle::ty;
-use middle::typeck::check::regionmanip;
 use middle::typeck::infer;
-use middle::typeck::infer::LateBoundRegionConversionTime::*;
 use middle::typeck::infer::{InferCtxt, TypeSkolemizer};
 use middle::ty_fold::TypeFoldable;
 use std::cell::RefCell;
@@ -211,6 +209,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
          */
 
         debug!("select({})", obligation.repr(self.tcx()));
+        assert!(!obligation.trait_ref.has_escaping_regions());
 
         let stack = self.push_stack(None, obligation);
         match try!(self.candidate_from_obligation(&stack)) {
@@ -263,6 +262,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         debug!("evaluate_obligation({})",
                obligation.repr(self.tcx()));
+        assert!(!obligation.trait_ref.has_escaping_regions());
 
         let stack = self.push_stack(None, obligation);
         self.evaluate_stack(&stack).may_apply()
@@ -747,6 +747,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         debug!("candidate_from_obligation(cache_skol_trait_ref={}, obligation={})",
                cache_skol_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()) {
             Some(c) => {
@@ -1707,27 +1708,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
         };
 
-        // FIXME(pcwalton): This is a bogus thing to do, but
-        // it'll do for now until we get the new trait-bound
-        // region skolemization working.
-        let (_, new_signature) =
-            regionmanip::replace_late_bound_regions(
-                self.tcx(),
-                closure_type.sig.binder_id,
-                &closure_type.sig,
-                |br| self.infcx.next_region_var(
-                         infer::LateBoundRegion(obligation.cause.span, br,
-                                                infer::FnCall)));
-
-        let arguments_tuple = new_signature.inputs[0];
-        let trait_ref = Rc::new(ty::TraitRef {
-            def_id: obligation.trait_ref.def_id,
-            substs: Substs::new_trait(
+        let closure_sig = &closure_type.sig;
+        let arguments_tuple = closure_sig.inputs[0];
+        let substs =
+            Substs::new_trait(
                 vec![arguments_tuple.subst(self.tcx(), substs),
-                     new_signature.output.unwrap().subst(self.tcx(), substs)],
+                     closure_sig.output.unwrap().subst(self.tcx(), substs)],
                 vec![],
                 vec![],
-                obligation.self_ty())
+                obligation.self_ty());
+        let trait_ref = Rc::new(ty::TraitRef {
+            def_id: obligation.trait_ref.def_id,
+            substs: substs,
         });
 
         self.confirm(obligation.cause,
@@ -2025,10 +2017,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         impl_substs: &Substs)
                         -> VecPerParamSpace<Obligation>
     {
-        let impl_generics = ty::lookup_item_type(self.tcx(),
-                                                 impl_def_id).generics;
+        let impl_generics = ty::lookup_item_type(self.tcx(), impl_def_id).generics;
+        let bounds = impl_generics.to_bounds(self.tcx(), impl_substs);
         util::obligations_for_generics(self.tcx(), cause, recursion_depth,
-                                       &impl_generics, impl_substs)
+                                       &bounds, &impl_substs.types)
     }
 }
 
diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs
index d8043fc2aab..8f8203f0281 100644
--- a/src/librustc/middle/traits/util.rs
+++ b/src/librustc/middle/traits/util.rs
@@ -10,7 +10,7 @@
 // except according to those terms.
 
 use middle::subst;
-use middle::subst::{ParamSpace, Subst, Substs, VecPerParamSpace};
+use middle::subst::{ParamSpace, Substs, VecPerParamSpace};
 use middle::typeck::infer::InferCtxt;
 use middle::ty;
 use std::collections::HashSet;
@@ -173,25 +173,25 @@ impl fmt::Show for VtableParamData {
 pub fn obligations_for_generics(tcx: &ty::ctxt,
                                 cause: ObligationCause,
                                 recursion_depth: uint,
-                                generics: &ty::Generics,
-                                substs: &Substs)
+                                generic_bounds: &ty::GenericBounds,
+                                type_substs: &VecPerParamSpace<ty::t>)
                                 -> VecPerParamSpace<Obligation>
 {
     /*! See `super::obligations_for_generics` */
 
-    debug!("obligations_for_generics(generics={}, substs={})",
-           generics.repr(tcx), substs.repr(tcx));
+    debug!("obligations_for_generics(generic_bounds={}, type_substs={})",
+           generic_bounds.repr(tcx), type_substs.repr(tcx));
 
     let mut obligations = VecPerParamSpace::empty();
 
-    for def in generics.types.iter() {
+    for (space, index, bounds) in generic_bounds.types.iter_enumerated() {
         push_obligations_for_param_bounds(tcx,
                                           cause,
                                           recursion_depth,
-                                          def.space,
-                                          def.index,
-                                          &def.bounds,
-                                          substs,
+                                          space,
+                                          index,
+                                          bounds,
+                                          type_substs,
                                           &mut obligations);
     }
 
@@ -207,11 +207,10 @@ fn push_obligations_for_param_bounds(
     space: subst::ParamSpace,
     index: uint,
     param_bounds: &ty::ParamBounds,
-    param_substs: &Substs,
+    param_type_substs: &VecPerParamSpace<ty::t>,
     obligations: &mut VecPerParamSpace<Obligation>)
 {
-    let param_ty = *param_substs.types.get(space, index);
-
+    let param_ty = *param_type_substs.get(space, index);
     for builtin_bound in param_bounds.builtin_bounds.iter() {
         let obligation = obligation_for_builtin_bound(tcx,
                                                       cause,
@@ -225,12 +224,11 @@ fn push_obligations_for_param_bounds(
     }
 
     for bound_trait_ref in param_bounds.trait_bounds.iter() {
-        let bound_trait_ref = bound_trait_ref.subst(tcx, param_substs);
         obligations.push(
             space,
             Obligation { cause: cause,
                          recursion_depth: recursion_depth,
-                         trait_ref: bound_trait_ref });
+                         trait_ref: (*bound_trait_ref).clone() });
     }
 }
 
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 5ec89ffb9e7..9f90afa3749 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -53,7 +53,7 @@ use middle::subst::{mod, Subst, Substs, VecPerParamSpace};
 use middle::traits;
 use middle::ty;
 use middle::typeck;
-use middle::ty_fold::{mod, TypeFoldable,TypeFolder};
+use middle::ty_fold::{mod, TypeFoldable, TypeFolder, HigherRankedFoldable};
 use middle;
 use util::ppaux::{note_and_explain_region, bound_region_ptr_to_string};
 use util::ppaux::{trait_store_to_string, ty_to_string};
@@ -609,13 +609,14 @@ pub struct ctxt<'tcx> {
 // recursing over the type itself.
 bitflags! {
     flags TypeFlags: u32 {
-        const NO_TYPE_FLAGS = 0b0,
-        const HAS_PARAMS    = 0b1,
-        const HAS_SELF      = 0b10,
-        const HAS_TY_INFER  = 0b100,
-        const HAS_RE_INFER  = 0b1000,
-        const HAS_REGIONS   = 0b10000,
-        const HAS_TY_ERR    = 0b100000,
+        const NO_TYPE_FLAGS       = 0b0,
+        const HAS_PARAMS          = 0b1,
+        const HAS_SELF            = 0b10,
+        const HAS_TY_INFER        = 0b100,
+        const HAS_RE_INFER        = 0b1000,
+        const HAS_RE_LATE_BOUND   = 0b10000,
+        const HAS_REGIONS         = 0b100000,
+        const HAS_TY_ERR          = 0b1000000,
         const NEEDS_SUBST   = HAS_PARAMS.bits | HAS_SELF.bits | HAS_REGIONS.bits,
     }
 }
@@ -626,6 +627,9 @@ pub type t_box = &'static t_box_;
 pub struct t_box_ {
     pub sty: sty,
     pub flags: TypeFlags,
+
+    // the maximal depth of any bound regions appearing in this type.
+    region_depth: uint,
 }
 
 impl fmt::Show for TypeFlags {
@@ -670,6 +674,50 @@ pub fn type_needs_infer(t: t) -> bool {
     tbox_has_flag(get(t), HAS_TY_INFER | HAS_RE_INFER)
 }
 
+pub fn type_has_late_bound_regions(ty: t) -> bool {
+    get(ty).flags.intersects(HAS_RE_LATE_BOUND)
+}
+
+pub fn type_has_escaping_regions(t: t) -> bool {
+    /*!
+     * An "escaping region" is a bound region whose binder is not part of `t`.
+     *
+     * So, for example, consider a type like the following, which has two
+     * binders:
+     *
+     *    for<'a> fn(x: for<'b> fn(&'a int, &'b int))
+     *    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
+     *                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~  inner scope
+     *
+     * This type has *bound regions* (`'a`, `'b`), but it does not
+     * have escaping regions, because the binders of both `'a` and
+     * `'b` are part of the type itself. However, if we consider the
+     * *inner fn type*, that type has an escaping region: `'a`.
+     *
+     * Note that what I'm calling an "escaping region" is often just
+     * called a "free region". However, we already use the term "free
+     * region". It refers to the regions that we use to represent
+     * bound regions on a fn definition while we are typechecking its
+     * body.
+     *
+     * To clarify, conceptually there is no particular difference
+     * between an "escaping" region and a "free" region. However,
+     * there is a big difference in practice. Basically, when
+     * "entering" a binding level, one is generally required to do
+     * some sort of processing to a bound region, such as replacing it
+     * with a fresh/skolemized region, or making an entry in the
+     * environment to represent the scope to which it is attached,
+     * etc. An escaping region represents a bound region for which
+     * this processing has not yet been done.
+     */
+
+    type_escapes_depth(t, 0)
+}
+
+pub fn type_escapes_depth(t: t, depth: uint) -> bool {
+    get(t).region_depth > depth
+}
+
 #[deriving(Clone, PartialEq, Eq, Hash, Show)]
 pub struct BareFnTy {
     pub fn_style: ast::FnStyle,
@@ -706,17 +754,16 @@ impl FnOutput {
  * Signature of a function type, which I have arbitrarily
  * decided to use to refer to the input/output types.
  *
- * - `binder_id` is the node id where this fn type appeared;
- *   it is used to identify all the bound regions appearing
- *   in the input/output types that are bound by this fn type
- *   (vs some enclosing or enclosed fn type)
  * - `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 {
-    pub binder_id: ast::NodeId,
     pub inputs: Vec<t>,
     pub output: FnOutput,
     pub variadic: bool
@@ -729,6 +776,54 @@ pub struct ParamTy {
     pub def_id: DefId
 }
 
+/**
+ * A [De Bruijn index][dbi] is a standard means of representing
+ * regions (and perhaps later types) in a higher-ranked setting. In
+ * particular, imagine a type like this:
+ *
+ *     for<'a> fn(for<'b> fn(&'b int, &'a int), &'a char)
+ *     ^          ^            |        |         |
+ *     |          |            |        |         |
+ *     |          +------------+ 1      |         |
+ *     |                                |         |
+ *     +--------------------------------+ 2       |
+ *     |                                          |
+ *     +------------------------------------------+ 1
+ *
+ * In this type, there are two binders (the outer fn and the inner
+ * fn). We need to be able to determine, for any given region, which
+ * fn type it is bound by, the inner or the outer one. There are
+ * various ways you can do this, but a De Bruijn index is one of the
+ * more convenient and has some nice properties. The basic idea is to
+ * count the number of binders, inside out. Some examples should help
+ * clarify what I mean.
+ *
+ * Let's start with the reference type `&'b int` that is the first
+ * argument to the inner function. This region `'b` is assigned a De
+ * Bruijn index of 1, meaning "the innermost binder" (in this case, a
+ * fn). The region `'a` that appears in the second argument type (`&'a
+ * int`) would then be assigned a De Bruijn index of 2, meaning "the
+ * second-innermost binder". (These indices are written on the arrays
+ * in the diagram).
+ *
+ * What is interesting is that De Bruijn index attached to a particular
+ * variable will vary depending on where it appears. For example,
+ * the final type `&'a char` also refers to the region `'a` declared on
+ * the outermost fn. But this time, this reference is not nested within
+ * any other binders (i.e., it is not an argument to the inner fn, but
+ * rather the outer one). Therefore, in this case, it is assigned a
+ * De Bruijn index of 1, because the innermost binder in that location
+ * is the outer fn.
+ *
+ * [dbi]: http://en.wikipedia.org/wiki/De_Bruijn_index
+ */
+#[deriving(Clone, PartialEq, Eq, Hash, Encodable, Decodable, Show)]
+pub struct DebruijnIndex {
+    // We maintain the invariant that this is never 0. So 1 indicates
+    // the innermost binder. To ensure this, create with `DebruijnIndex::new`.
+    pub depth: uint,
+}
+
 /// Representation of regions:
 #[deriving(Clone, PartialEq, Eq, Hash, Encodable, Decodable, Show)]
 pub enum Region {
@@ -741,9 +836,8 @@ pub enum Region {
                  ast::Name),
 
     // Region bound in a function scope, which will be substituted when the
-    // function is called. The first argument must be the `binder_id` of
-    // some enclosing function signature.
-    ReLateBound(/* binder_id */ ast::NodeId, BoundRegion),
+    // function is called.
+    ReLateBound(DebruijnIndex, BoundRegion),
 
     /// When checking a function body, the types of all arguments and so forth
     /// that refer to bound region parameters are modified to refer to free
@@ -885,12 +979,19 @@ pub type UpvarBorrowMap = FnvHashMap<UpvarId, UpvarBorrow>;
 
 impl Region {
     pub fn is_bound(&self) -> bool {
-        match self {
-            &ty::ReEarlyBound(..) => true,
-            &ty::ReLateBound(..) => true,
+        match *self {
+            ty::ReEarlyBound(..) => true,
+            ty::ReLateBound(..) => true,
             _ => false
         }
     }
+
+    pub fn escapes_depth(&self, depth: uint) -> bool {
+        match *self {
+            ty::ReLateBound(debruijn, _) => debruijn.depth > depth,
+            _ => false,
+        }
+    }
 }
 
 #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Encodable, Decodable, Show)]
@@ -928,6 +1029,7 @@ mod primitives {
             pub static $name: t_box_ = t_box_ {
                 sty: $sty,
                 flags: super::NO_TYPE_FLAGS,
+                region_depth: 0,
             };
         )
     )
@@ -950,6 +1052,7 @@ mod primitives {
     pub static TY_ERR: t_box_ = t_box_ {
         sty: super::ty_err,
         flags: super::HAS_TY_ERR,
+        region_depth: 0,
     };
 }
 
@@ -1002,12 +1105,46 @@ pub struct TyTrait {
     pub bounds: ExistentialBounds
 }
 
+/**
+ * A complete reference to a trait. These take numerous guises in syntax,
+ * but perhaps the most recognizable form is in a where clause:
+ *
+ *     T : Foo<U>
+ *
+ * This would be represented by a trait-reference where the def-id is the
+ * def-id for the trait `Foo` and the substs defines `T` as parameter 0 in the
+ * `SelfSpace` and `U` as parameter 0 in the `TypeSpace`.
+ *
+ * Trait references also appear in object types like `Foo<U>`, but in
+ * that case the `Self` parameter is absent from the substitutions.
+ *
+ * Note that a `TraitRef` introduces a level of region binding, to
+ * account for higher-ranked trait bounds like `T : for<'a> Foo<&'a
+ * U>` or higher-ranked object types.
+ */
 #[deriving(Clone, PartialEq, Eq, Hash, Show)]
 pub struct TraitRef {
     pub def_id: DefId,
     pub substs: Substs,
 }
 
+/**
+ * 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 fn bind<T>(value: T) -> Binder<T> {
+    Binder { value: value }
+}
+
 #[deriving(Clone, PartialEq)]
 pub enum IntVarValue {
     IntType(ast::IntTy),
@@ -1272,6 +1409,52 @@ impl Generics {
     pub fn has_region_params(&self, space: subst::ParamSpace) -> bool {
         !self.regions.is_empty_in(space)
     }
+
+    pub fn to_bounds(&self, tcx: &ty::ctxt, substs: &Substs) -> GenericBounds {
+        GenericBounds {
+            types: self.types.map(|d| d.bounds.subst(tcx, substs)),
+            regions: self.regions.map(|d| d.bounds.subst(tcx, substs)),
+        }
+    }
+}
+
+/**
+ * Represents the bounds declared on a particular set of type
+ * parameters.  Should eventually be generalized into a flag list of
+ * where clauses.  You can obtain a `GenericBounds` list from a
+ * `Generics` by using the `to_bounds` method. Note that this method
+ * reflects an important semantic invariant of `GenericBounds`: while
+ * the bounds in a `Generics` are expressed in terms of the bound type
+ * parameters of the impl/trait/whatever, a `GenericBounds` instance
+ * represented a set of bounds for some particular instantiation,
+ * meaning that the generic parameters have been substituted with
+ * their values.
+ *
+ * Example:
+ *
+ *     struct Foo<T,U:Bar<T>> { ... }
+ *
+ * Here, the `Generics` for `Foo` would contain a list of bounds like
+ * `[[], [U:Bar<T>]]`.  Now if there were some particular reference
+ * like `Foo<int,uint>`, then the `GenericBounds` would be `[[],
+ * [uint:Bar<int>]]`.
+ */
+#[deriving(Clone, Show)]
+pub struct GenericBounds {
+    pub types: VecPerParamSpace<ParamBounds>,
+    pub regions: VecPerParamSpace<Vec<Region>>,
+}
+
+impl GenericBounds {
+    pub fn empty() -> GenericBounds {
+        GenericBounds { types: VecPerParamSpace::empty(),
+                        regions: VecPerParamSpace::empty() }
+    }
+
+    pub fn has_escaping_regions(&self) -> bool {
+        self.types.any(|pb| pb.trait_bounds.iter().any(|tr| tr.has_escaping_regions())) ||
+            self.regions.any(|rs| rs.iter().any(|r| r.escapes_depth(0)))
+    }
 }
 
 impl TraitRef {
@@ -1290,6 +1473,14 @@ impl TraitRef {
         // 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
@@ -1597,99 +1788,12 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
         _ => ()
     }
 
-    let mut flags = NO_TYPE_FLAGS;
-    fn rflags(r: Region) -> TypeFlags {
-        HAS_REGIONS | {
-            match r {
-              ty::ReInfer(_) => HAS_RE_INFER,
-              _ => NO_TYPE_FLAGS,
-            }
-        }
-    }
-    fn sflags(substs: &Substs) -> TypeFlags {
-        let mut f = NO_TYPE_FLAGS;
-        let mut i = substs.types.iter();
-        for tt in i {
-            f = f | get(*tt).flags;
-        }
-        match substs.regions {
-            subst::ErasedRegions => {}
-            subst::NonerasedRegions(ref regions) => {
-                for r in regions.iter() {
-                    f = f | rflags(*r)
-                }
-            }
-        }
-        return f;
-    }
-    fn flags_for_bounds(bounds: &ExistentialBounds) -> TypeFlags {
-        rflags(bounds.region_bound)
-    }
-    match &st {
-      &ty_bool | &ty_char | &ty_int(_) | &ty_float(_) | &ty_uint(_) |
-      &ty_str => {}
-      // You might think that we could just return ty_err for
-      // any type containing ty_err as a component, and get
-      // rid of the HAS_TY_ERR flag -- likewise for ty_bot (with
-      // the exception of function types that return bot).
-      // But doing so caused sporadic memory corruption, and
-      // neither I (tjc) nor nmatsakis could figure out why,
-      // so we're doing it this way.
-      &ty_err => flags = flags | HAS_TY_ERR,
-      &ty_param(ref p) => {
-          if p.space == subst::SelfSpace {
-              flags = flags | HAS_SELF;
-          } else {
-              flags = flags | HAS_PARAMS;
-          }
-      }
-      &ty_unboxed_closure(_, ref region, ref substs) => {
-          flags = flags | rflags(*region);
-          flags = flags | sflags(substs);
-      }
-      &ty_infer(_) => flags = flags | HAS_TY_INFER,
-      &ty_enum(_, ref substs) | &ty_struct(_, ref substs) => {
-          flags = flags | sflags(substs);
-      }
-      &ty_trait(box TyTrait { ref principal, ref bounds }) => {
-          flags = flags | sflags(&principal.substs);
-          flags = flags | flags_for_bounds(bounds);
-      }
-      &ty_uniq(tt) | &ty_vec(tt, _) | &ty_open(tt) => {
-        flags = flags | get(tt).flags
-      }
-      &ty_ptr(ref m) => {
-        flags = flags | get(m.ty).flags;
-      }
-      &ty_rptr(r, ref m) => {
-        flags = flags | rflags(r);
-        flags = flags | get(m.ty).flags;
-      }
-      &ty_tup(ref ts) => for tt in ts.iter() { flags = flags | get(*tt).flags; },
-      &ty_bare_fn(ref f) => {
-        for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; }
-        if let ty::FnConverging(output) = f.sig.output {
-            flags = flags | get(output).flags;
-        }
-      }
-      &ty_closure(ref f) => {
-        match f.store {
-            RegionTraitStore(r, _) => {
-                flags = flags | rflags(r);
-            }
-            _ => {}
-        }
-        for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; }
-        if let ty::FnConverging(output) = f.sig.output {
-            flags = flags | get(output).flags;
-        }
-        flags = flags | flags_for_bounds(&f.bounds);
-      }
-    }
+    let flags = FlagComputation::for_sty(&st);
 
     let t = cx.type_arena.alloc(t_box_ {
         sty: st,
-        flags: flags,
+        flags: flags.flags,
+        region_depth: flags.depth,
     });
 
     let sty_ptr = &t.sty as *const sty;
@@ -1705,6 +1809,188 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
     }
 }
 
+struct FlagComputation {
+    flags: TypeFlags,
+
+    // maximum depth of any bound region that we have seen thus far
+    depth: uint,
+}
+
+impl FlagComputation {
+    fn new() -> FlagComputation {
+        FlagComputation { flags: NO_TYPE_FLAGS, depth: 0 }
+    }
+
+    fn for_sty(st: &sty) -> FlagComputation {
+        let mut result = FlagComputation::new();
+        result.add_sty(st);
+        result
+    }
+
+    fn add_flags(&mut self, flags: TypeFlags) {
+        self.flags = self.flags | flags;
+    }
+
+    fn add_depth(&mut self, depth: uint) {
+        if depth > self.depth {
+            self.depth = depth;
+        }
+    }
+
+    fn add_bound_computation(&mut self, computation: &FlagComputation) {
+        /*!
+         * Adds the flags/depth from a set of types that appear within
+         * the current type, but within a region binder.
+         */
+
+        self.add_flags(computation.flags);
+
+        // The types that contributed to `computation` occured within
+        // a region binder, so subtract one from the region depth
+        // within when adding the depth to `self`.
+        let depth = computation.depth;
+        if depth > 0 {
+            self.add_depth(depth - 1);
+        }
+    }
+
+    fn add_sty(&mut self, st: &sty) {
+        match st {
+            &ty_bool |
+            &ty_char |
+            &ty_int(_) |
+            &ty_float(_) |
+            &ty_uint(_) |
+            &ty_str => {
+            }
+
+            // You might think that we could just return ty_err for
+            // any type containing ty_err as a component, and get
+            // rid of the HAS_TY_ERR flag -- likewise for ty_bot (with
+            // the exception of function types that return bot).
+            // But doing so caused sporadic memory corruption, and
+            // neither I (tjc) nor nmatsakis could figure out why,
+            // so we're doing it this way.
+            &ty_err => {
+                self.add_flags(HAS_TY_ERR)
+            }
+
+            &ty_param(ref p) => {
+                if p.space == subst::SelfSpace {
+                    self.add_flags(HAS_SELF);
+                } else {
+                    self.add_flags(HAS_PARAMS);
+                }
+            }
+
+            &ty_unboxed_closure(_, ref region, ref substs) => {
+                self.add_region(*region);
+                self.add_substs(substs);
+            }
+
+            &ty_infer(_) => {
+                self.add_flags(HAS_TY_INFER)
+            }
+
+            &ty_enum(_, ref substs) | &ty_struct(_, ref substs) => {
+                self.add_substs(substs);
+            }
+
+            &ty_trait(box TyTrait { ref principal, ref bounds }) => {
+                let mut computation = FlagComputation::new();
+                computation.add_substs(&principal.substs);
+                self.add_bound_computation(&computation);
+
+                self.add_bounds(bounds);
+            }
+
+            &ty_uniq(tt) | &ty_vec(tt, _) | &ty_open(tt) => {
+                self.add_ty(tt)
+            }
+
+            &ty_ptr(ref m) => {
+                self.add_ty(m.ty);
+            }
+
+            &ty_rptr(r, ref m) => {
+                self.add_region(r);
+                self.add_ty(m.ty);
+            }
+
+            &ty_tup(ref ts) => {
+                self.add_tys(ts[]);
+            }
+
+            &ty_bare_fn(ref f) => {
+                self.add_fn_sig(&f.sig);
+            }
+
+            &ty_closure(ref f) => {
+                match f.store {
+                    RegionTraitStore(r, _) => {
+                        self.add_region(r);
+                    }
+                    _ => {}
+                }
+                self.add_fn_sig(&f.sig);
+                self.add_bounds(&f.bounds);
+            }
+        }
+    }
+
+    fn add_ty(&mut self, t: t) {
+        let t_box = get(t);
+        self.add_flags(t_box.flags);
+        self.add_depth(t_box.region_depth);
+    }
+
+    fn add_tys(&mut self, tys: &[t]) {
+        for &ty in tys.iter() {
+            self.add_ty(ty);
+        }
+    }
+
+    fn add_fn_sig(&mut self, fn_sig: &FnSig) {
+        let mut computation = FlagComputation::new();
+
+        computation.add_tys(fn_sig.inputs[]);
+
+        if let ty::FnConverging(output) = fn_sig.output {
+            computation.add_ty(output);
+        }
+
+        self.add_bound_computation(&computation);
+    }
+
+    fn add_region(&mut self, r: Region) {
+        self.add_flags(HAS_REGIONS);
+        match r {
+            ty::ReInfer(_) => { self.add_flags(HAS_RE_INFER); }
+            ty::ReLateBound(debruijn, _) => {
+                self.add_flags(HAS_RE_LATE_BOUND);
+                self.add_depth(debruijn.depth);
+            }
+            _ => { }
+        }
+    }
+
+    fn add_substs(&mut self, substs: &Substs) {
+        self.add_tys(substs.types.as_slice());
+        match substs.regions {
+            subst::ErasedRegions => {}
+            subst::NonerasedRegions(ref regions) => {
+                for &r in regions.iter() {
+                    self.add_region(r);
+                }
+            }
+        }
+    }
+
+    fn add_bounds(&mut self, bounds: &ExistentialBounds) {
+        self.add_region(bounds.region_bound);
+    }
+}
+
 #[inline]
 pub fn mk_prim_t(primitive: &'static t_box_) -> t {
     unsafe {
@@ -1855,7 +2141,6 @@ pub fn mk_bare_fn(cx: &ctxt, fty: BareFnTy) -> t {
 }
 
 pub fn mk_ctor_fn(cx: &ctxt,
-                  binder_id: ast::NodeId,
                   input_tys: &[ty::t],
                   output: ty::t) -> t {
     let input_args = input_tys.iter().map(|t| *t).collect();
@@ -1864,7 +2149,6 @@ pub fn mk_ctor_fn(cx: &ctxt,
                    fn_style: ast::NormalFn,
                    abi: abi::Rust,
                    sig: FnSig {
-                    binder_id: binder_id,
                     inputs: input_args,
                     output: ty::FnConverging(output),
                     variadic: false
@@ -4498,9 +4782,99 @@ pub fn bounds_for_trait_ref(tcx: &ctxt,
                             -> ty::ParamBounds
 {
     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));
-    trait_def.bounds.subst(tcx, &trait_ref.substs)
+
+    // The interaction between HRTB and supertraits is not entirely
+    // obvious. Let me walk you (and myself) through an example.
+    //
+    // Let's start with an easy case. Consider two traits:
+    //
+    //     trait Foo<'a> : Bar<'a,'a> { }
+    //     trait Bar<'b,'c> { }
+    //
+    // Now, if we have a trait reference `for<'x> T : Foo<'x>`, then
+    // we can deduce that `for<'x> T : Bar<'x,'x>`. Basically, if we
+    // knew that `Foo<'x>` (for any 'x) then we also know that
+    // `Bar<'x,'x>` (for any 'x). This more-or-less falls out from
+    // normal substitution.
+    //
+    // In terms of why this is sound, the idea is that whenever there
+    // is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>`
+    // holds.  So if there is an impl of `T:Foo<'a>` that applies to
+    // all `'a`, then we must know that `T:Bar<'a,'a>` holds for all
+    // `'a`.
+    //
+    // Another example to be careful of is this:
+    //
+    //     trait Foo1<'a> : for<'b> Bar1<'a,'b> { }
+    //     trait Bar1<'b,'c> { }
+    //
+    // Here, if we have `for<'x> T : Foo1<'x>`, then what do we know?
+    // The answer is that we know `for<'x,'b> T : Bar1<'x,'b>`. The
+    // reason is similar to the previous example: any impl of
+    // `T:Foo1<'x>` must show that `for<'b> T : Bar1<'x, 'b>`.  So
+    // basically we would want to collapse the bound lifetimes from
+    // the input (`trait_ref`) and the supertraits.
+    //
+    // To achieve this in practice is fairly straightforward. Let's
+    // consider the more complicated scenario:
+    //
+    // - We start out with `for<'x> T : Foo1<'x>`. In this case, `'x`
+    //   has a De Bruijn index of 1. We want to produce `for<'x,'b> T : Bar1<'x,'b>`,
+    //   where both `'x` and `'b` would have a DB index of 1.
+    //   The substitution from the input trait-ref is therefore going to be
+    //   `'a => 'x` (where `'x` has a DB index of 1).
+    // - The super-trait-ref is `for<'b> Bar1<'a,'b>`, where `'a` is an
+    //   early-bound parameter and `'b' is a late-bound parameter with a
+    //   DB index of 1.
+    // - If we replace `'a` with `'x` from the input, it too will have
+    //   a DB index of 1, and thus we'll have `for<'x,'b> Bar1<'x,'b>`
+    //   just as we wanted.
+    //
+    // There is only one catch. If we just apply the substitution `'a
+    // => 'x` to `for<'b> Bar1<'a,'b>`, the substitution code will
+    // adjust the DB index because we substituting into a binder (it
+    // tries to be so smart...) resulting in `for<'x> for<'b>
+    // Bar1<'x,'b>` (we have no syntax for this, so use your
+    // imagination). Basically the 'x will have DB index of 2 and 'b
+    // will have DB index of 1. Not quite what we want. So we apply
+    // the substitution to the *contents* of the trait reference,
+    // rather than the trait reference itself (put another way, the
+    // substitution code expects equal binding levels in the values
+    // from the substitution and the value being substituted into, and
+    // this trick achieves that).
+
+    // Carefully avoid the binder introduced by each trait-ref by
+    // substituting over the substs, not the trait-refs themselves,
+    // thus achieving the "collapse" described in the big comment
+    // above.
+    let trait_bounds: Vec<_> =
+        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))
+        })
+        .map(|bound_trait_ref| Rc::new(bound_trait_ref))
+        .collect();
+
+    debug!("bounds_for_trait_ref: trait_bounds={}",
+           trait_bounds.repr(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);
+    let builtin_bounds =
+        trait_def.bounds.builtin_bounds.subst(tcx, &trait_ref.substs);
+
+    ty::ParamBounds {
+        trait_bounds: trait_bounds,
+        region_bounds: region_bounds,
+        builtin_bounds: builtin_bounds,
+    }
 }
 
 /// Iterate over attributes of a definition.
@@ -4783,13 +5157,12 @@ pub fn normalize_ty(cx: &ctxt, t: t) -> t {
                             types: substs.types.fold_with(self) }
         }
 
-        fn fold_sig(&mut self,
-                    sig: &ty::FnSig)
-                    -> ty::FnSig {
+        fn fold_fn_sig(&mut self,
+                       sig: &ty::FnSig)
+                       -> ty::FnSig {
             // The binder-id is only relevant to bound regions, which
             // are erased at trans time.
             ty::FnSig {
-                binder_id: ast::DUMMY_NODE_ID,
                 inputs: sig.inputs.fold_with(self),
                 output: sig.output.fold_with(self),
                 variadic: sig.variadic,
@@ -5305,11 +5678,13 @@ pub fn construct_parameter_environment(
     // Compute the bounds on Self and the type parameters.
     //
 
-    let mut bounds = VecPerParamSpace::empty();
-    for &space in subst::ParamSpace::all().iter() {
-        push_bounds_from_defs(tcx, &mut bounds, space, &free_substs,
-                              generics.types.get_slice(space));
-    }
+    let bounds = generics.to_bounds(tcx, &free_substs);
+    let bounds = liberate_late_bound_regions(tcx, free_id, &bind(bounds)).value;
+    let obligations = traits::obligations_for_generics(tcx,
+                                                       traits::ObligationCause::misc(span),
+                                                       &bounds,
+                                                       &free_substs.types);
+    let type_bounds = bounds.types.subst(tcx, &free_substs);
 
     //
     // Compute region bounds. For now, these relations are stored in a
@@ -5318,24 +5693,20 @@ pub fn construct_parameter_environment(
     //
 
     for &space in subst::ParamSpace::all().iter() {
-        record_region_bounds_from_defs(tcx, space, &free_substs,
-                                       generics.regions.get_slice(space));
+        record_region_bounds(tcx, space, &free_substs, bounds.regions.get_slice(space));
     }
 
 
-    debug!("construct_parameter_environment: free_id={} \
-           free_subst={} \
-           bounds={}",
+    debug!("construct_parameter_environment: free_id={} free_subst={} \
+           obligations={} type_bounds={}",
            free_id,
            free_substs.repr(tcx),
-           bounds.repr(tcx));
-
-    let obligations = traits::obligations_for_generics(tcx, traits::ObligationCause::misc(span),
-                                                       generics, &free_substs);
+           obligations.repr(tcx),
+           type_bounds.repr(tcx));
 
     return ty::ParameterEnvironment {
         free_substs: free_substs,
-        bounds: bounds,
+        bounds: bounds.types,
         implicit_region_bound: ty::ReScope(free_id),
         caller_obligations: obligations,
         selection_cache: traits::SelectionCache::new(),
@@ -5366,28 +5737,16 @@ pub fn construct_parameter_environment(
         }
     }
 
-    fn push_bounds_from_defs(tcx: &ty::ctxt,
-                             bounds: &mut subst::VecPerParamSpace<ParamBounds>,
-                             space: subst::ParamSpace,
-                             free_substs: &subst::Substs,
-                             defs: &[TypeParameterDef]) {
-        for def in defs.iter() {
-            let b = def.bounds.subst(tcx, free_substs);
-            bounds.push(space, b);
-        }
-    }
-
-    fn record_region_bounds_from_defs(tcx: &ty::ctxt,
-                                      space: subst::ParamSpace,
-                                      free_substs: &subst::Substs,
-                                      defs: &[RegionParameterDef]) {
-        for (subst_region, def) in
+    fn record_region_bounds(tcx: &ty::ctxt,
+                            space: subst::ParamSpace,
+                            free_substs: &Substs,
+                            bound_sets: &[Vec<ty::Region>]) {
+        for (subst_region, bound_set) in
             free_substs.regions().get_slice(space).iter().zip(
-                defs.iter())
+                bound_sets.iter())
         {
             // For each region parameter 'subst...
-            let bounds = def.bounds.subst(tcx, free_substs);
-            for bound_region in bounds.iter() {
+            for bound_region in bound_set.iter() {
                 // Which is declared with a bound like 'subst:'bound...
                 match (subst_region, bound_region) {
                     (&ty::ReFree(subst_fr), &ty::ReFree(bound_fr)) => {
@@ -5398,7 +5757,7 @@ pub fn construct_parameter_environment(
                     _ => {
                         // All named regions are instantiated with free regions.
                         tcx.sess.bug(
-                            format!("push_region_bounds_from_defs: \
+                            format!("record_region_bounds: \
                                      non free region: {} / {}",
                                     subst_region.repr(tcx),
                                     bound_region.repr(tcx)).as_slice());
@@ -5589,3 +5948,86 @@ impl AutoDerefRef {
         self.autoderefs == 0 && self.autoref.is_none()
     }
 }
+
+pub fn liberate_late_bound_regions<HR>(
+    tcx: &ty::ctxt,
+    scope_id: ast::NodeId,
+    value: &HR)
+    -> HR
+    where HR : HigherRankedFoldable
+{
+    /*!
+     * Replace any late-bound regions bound in `value` with free variants
+     * attached to scope-id `scope_id`.
+     */
+
+    replace_late_bound_regions(
+        tcx, value,
+        |br, _| ty::ReFree(ty::FreeRegion{scope_id: scope_id, bound_region: br})).0
+}
+
+pub fn erase_late_bound_regions<HR>(
+    tcx: &ty::ctxt,
+    value: &HR)
+    -> HR
+    where HR : HigherRankedFoldable
+{
+    /*!
+     * 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.
+     */
+
+    replace_late_bound_regions(tcx, value, |_, _| ty::ReStatic).0
+}
+
+pub fn replace_late_bound_regions<HR>(
+    tcx: &ty::ctxt,
+    value: &HR,
+    mapf: |BoundRegion, DebruijnIndex| -> ty::Region)
+    -> (HR, FnvHashMap<ty::BoundRegion,ty::Region>)
+    where HR : HigherRankedFoldable
+{
+    /*!
+     * Replaces the late-bound-regions in `value` that are bound by `value`.
+     */
+
+    debug!("replace_late_bound_regions({})", value.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: 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)
+}
+
+impl DebruijnIndex {
+    pub fn new(depth: uint) -> DebruijnIndex {
+        assert!(depth > 0);
+        DebruijnIndex { depth: depth }
+    }
+
+    pub fn shifted(&self, amount: uint) -> DebruijnIndex {
+        DebruijnIndex { depth: self.depth + amount }
+    }
+}
diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs
index 6d8d03aa0ab..4dfee673bca 100644
--- a/src/librustc/middle/ty_fold.rs
+++ b/src/librustc/middle/ty_fold.rs
@@ -42,7 +42,6 @@ use middle::ty;
 use middle::traits;
 use middle::typeck;
 use std::rc::Rc;
-use syntax::ast;
 use syntax::owned_slice::OwnedSlice;
 use util::ppaux::Repr;
 
@@ -63,6 +62,17 @@ pub trait TypeFoldable {
 pub trait TypeFolder<'tcx> {
     fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>;
 
+    /// Invoked by the `super_*` routines when we enter a region
+    /// binding level (for example, when entering a function
+    /// signature). This is used by clients that want to track the
+    /// Debruijn index nesting level.
+    fn enter_region_binder(&mut self) { }
+
+    /// Invoked by the `super_*` routines when we exit a region
+    /// binding level. This is used by clients that want to
+    /// track the Debruijn index nesting level.
+    fn exit_region_binder(&mut self) { }
+
     fn fold_ty(&mut self, t: ty::t) -> ty::t {
         super_fold_ty(self, t)
     }
@@ -85,10 +95,10 @@ pub trait TypeFolder<'tcx> {
         super_fold_substs(self, substs)
     }
 
-    fn fold_sig(&mut self,
+    fn fold_fn_sig(&mut self,
                 sig: &ty::FnSig)
                 -> ty::FnSig {
-        super_fold_sig(self, sig)
+        super_fold_fn_sig(self, sig)
     }
 
     fn fold_output(&mut self,
@@ -153,6 +163,12 @@ impl TypeFoldable for () {
     }
 }
 
+impl<T:TypeFoldable,U:TypeFoldable> TypeFoldable for (T, U) {
+    fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, folder: &mut F) -> (T, U) {
+        (self.0.fold_with(folder), self.1.fold_with(folder))
+    }
+}
+
 impl<T:TypeFoldable> TypeFoldable for Option<T> {
     fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Option<T> {
         self.as_ref().map(|t| t.fold_with(folder))
@@ -171,6 +187,15 @@ impl<T:TypeFoldable> TypeFoldable for Vec<T> {
     }
 }
 
+impl<T:TypeFoldable> TypeFoldable for ty::Binder<T> {
+    fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Binder<T> {
+        folder.enter_region_binder();
+        let result = ty::bind(self.value.fold_with(folder));
+        folder.exit_region_binder();
+        result
+    }
+}
+
 impl<T:TypeFoldable> TypeFoldable for OwnedSlice<T> {
     fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> OwnedSlice<T> {
         self.iter().map(|t| t.fold_with(folder)).collect()
@@ -179,7 +204,24 @@ impl<T:TypeFoldable> TypeFoldable for OwnedSlice<T> {
 
 impl<T:TypeFoldable> TypeFoldable for VecPerParamSpace<T> {
     fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> VecPerParamSpace<T> {
-        self.map(|t| t.fold_with(folder))
+
+        // Things in the Fn space take place under an additional level
+        // of region binding relative to the other spaces. This is
+        // because those entries are attached to a method, and methods
+        // always introduce a level of region binding.
+
+        let result = self.map_enumerated(|(space, index, elem)| {
+            if space == subst::FnSpace && index == 0 {
+                // enter new level when/if we reach the first thing in fn space
+                folder.enter_region_binder();
+            }
+            elem.fold_with(folder)
+        });
+        if result.len(subst::FnSpace) > 0 {
+            // if there was anything in fn space, exit the region binding level
+            folder.exit_region_binder();
+        }
+        result
     }
 }
 
@@ -221,7 +263,7 @@ impl TypeFoldable for ty::FnOutput {
 
 impl TypeFoldable for ty::FnSig {
     fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig {
-        folder.fold_sig(self)
+        folder.fold_fn_sig(self)
     }
 }
 
@@ -368,6 +410,15 @@ impl TypeFoldable for ty::Generics {
     }
 }
 
+impl TypeFoldable for ty::GenericBounds {
+    fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::GenericBounds {
+        ty::GenericBounds {
+            types: self.types.fold_with(folder),
+            regions: self.regions.fold_with(folder),
+        }
+    }
+}
+
 impl TypeFoldable for ty::UnsizeKind {
     fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::UnsizeKind {
         match *self {
@@ -434,6 +485,7 @@ impl TypeFoldable for traits::VtableParamData {
 // "super" routines: these are the default implementations for TypeFolder.
 //
 // They should invoke `foo.fold_with()` to do recursive folding.
+
 pub fn super_fold_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
                                                 t: ty::t)
                                                 -> ty::t {
@@ -457,11 +509,21 @@ pub fn super_fold_substs<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
                     types: substs.types.fold_with(this) }
 }
 
-pub fn super_fold_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
-                                                 sig: &ty::FnSig)
-                                                 -> ty::FnSig {
-    ty::FnSig { binder_id: sig.binder_id,
-                inputs: sig.inputs.fold_with(this),
+pub fn super_fold_fn_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
+                                                    sig: &ty::FnSig)
+                                                    -> ty::FnSig
+{
+    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)
+                                                             -> ty::FnSig
+{
+    ty::FnSig { inputs: sig.inputs.fold_with(this),
                 output: sig.output.fold_with(this),
                 variadic: sig.variadic }
 }
@@ -497,9 +559,21 @@ pub fn super_fold_closure_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
         abi: fty.abi,
     }
 }
+
 pub fn super_fold_trait_ref<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
                                                        t: &ty::TraitRef)
-                                                       -> ty::TraitRef {
+                                                       -> ty::TraitRef
+{
+    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)
+                                                                -> ty::TraitRef
+{
     ty::TraitRef {
         def_id: t.def_id,
         substs: t.substs.fold_with(this),
@@ -622,6 +696,42 @@ pub fn super_fold_obligation<'tcx, T:TypeFolder<'tcx>>(this: &mut T,
 }
 
 ///////////////////////////////////////////////////////////////////////////
+// Higher-ranked things
+
+/**
+ * Designates a "binder" for late-bound regions.
+ */
+pub trait HigherRankedFoldable : Repr {
+    /// Folds the contents of `self`, ignoring the region binder created
+    /// by `self`.
+    fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self;
+}
+
+impl HigherRankedFoldable for ty::FnSig {
+    fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig {
+        super_fold_fn_sig_contents(folder, self)
+    }
+}
+
+impl HigherRankedFoldable for ty::TraitRef {
+    fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::TraitRef {
+        super_fold_trait_ref_contents(folder, self)
+    }
+}
+
+impl<T:TypeFoldable+Repr> HigherRankedFoldable for ty::Binder<T> {
+    fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Binder<T> {
+        ty::bind(self.value.fold_with(folder))
+    }
+}
+
+impl<T:HigherRankedFoldable> HigherRankedFoldable for Rc<T> {
+    fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Rc<T> {
+        Rc::new((**self).fold_contents(folder))
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
 // Some sample folders
 
 pub struct BottomUpFolder<'a, 'tcx: 'a> {
@@ -655,77 +765,43 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BottomUpFolder<'a, 'tcx> {
 /// current position of the fold.)
 pub struct RegionFolder<'a, 'tcx: 'a> {
     tcx: &'a ty::ctxt<'tcx>,
-    fld_t: |ty::t|: 'a -> ty::t,
-    fld_r: |ty::Region|: 'a -> ty::Region,
-    within_binder_ids: Vec<ast::NodeId>,
+    current_depth: uint,
+    fld_r: |ty::Region, uint|: 'a -> ty::Region,
 }
 
 impl<'a, 'tcx> RegionFolder<'a, 'tcx> {
-    pub fn general(tcx: &'a ty::ctxt<'tcx>,
-                   fld_r: |ty::Region|: 'a -> ty::Region,
-                   fld_t: |ty::t|: 'a -> ty::t)
-                   -> RegionFolder<'a, 'tcx> {
+    pub fn new(tcx: &'a ty::ctxt<'tcx>, fld_r: |ty::Region, uint|: 'a -> ty::Region)
+               -> RegionFolder<'a, 'tcx> {
         RegionFolder {
             tcx: tcx,
-            fld_t: fld_t,
+            current_depth: 1,
             fld_r: fld_r,
-            within_binder_ids: vec![],
-        }
-    }
-
-    pub fn regions(tcx: &'a ty::ctxt<'tcx>, fld_r: |ty::Region|: 'a -> ty::Region)
-                   -> RegionFolder<'a, 'tcx> {
-        fn noop(t: ty::t) -> ty::t { t }
-
-        RegionFolder {
-            tcx: tcx,
-            fld_t: noop,
-            fld_r: fld_r,
-            within_binder_ids: vec![],
         }
     }
 }
 
-/// If `ty` has `FnSig` (i.e. closure or fn), return its binder_id;
-/// else None.
-fn opt_binder_id_of_function(t: ty::t) -> Option<ast::NodeId> {
-    match ty::get(t).sty {
-        ty::ty_closure(ref f) => Some(f.sig.binder_id),
-        ty::ty_bare_fn(ref f) => Some(f.sig.binder_id),
-        _                     => None,
-    }
-}
-
 impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx> {
     fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx }
 
-    fn fold_ty(&mut self, ty: ty::t) -> ty::t {
-        debug!("RegionFolder.fold_ty({})", ty.repr(self.tcx()));
-        let opt_binder_id = opt_binder_id_of_function(ty);
-        match opt_binder_id {
-            Some(binder_id) => self.within_binder_ids.push(binder_id),
-            None => {}
-        }
-
-        let t1 = super_fold_ty(self, ty);
-        let ret = (self.fld_t)(t1);
-
-        if opt_binder_id.is_some() {
-            self.within_binder_ids.pop();
-        }
+    fn enter_region_binder(&mut self) {
+        self.current_depth += 1;
+    }
 
-        ret
+    fn exit_region_binder(&mut self) {
+        self.current_depth -= 1;
     }
 
     fn fold_region(&mut self, r: ty::Region) -> ty::Region {
         match r {
-            ty::ReLateBound(binder_id, _) if self.within_binder_ids.contains(&binder_id) => {
-                debug!("RegionFolder.fold_region({}) skipped bound region", r.repr(self.tcx()));
+            ty::ReLateBound(debruijn, _) if debruijn.depth < self.current_depth => {
+                debug!("RegionFolder.fold_region({}) skipped bound region (current depth={})",
+                       r.repr(self.tcx()), self.current_depth);
                 r
             }
             _ => {
-                debug!("RegionFolder.fold_region({}) folding free region", r.repr(self.tcx()));
-                (self.fld_r)(r)
+                debug!("RegionFolder.fold_region({}) folding free region (current_depth={})",
+                       r.repr(self.tcx()), self.current_depth);
+                (self.fld_r)(r, self.current_depth)
             }
         }
     }
@@ -755,3 +831,33 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionEraser<'a, 'tcx> {
         }
     }
 }
+
+///////////////////////////////////////////////////////////////////////////
+// Region shifter
+//
+// Shifts the De Bruijn indices on all escaping bound regions by a
+// fixed amount. Useful in substitution or when otherwise introducing
+// a binding level that is not intended to capture the existing bound
+// regions. See comment on `shift_regions_through_binders` method in
+// `subst.rs` for more details.
+
+pub fn shift_region(region: ty::Region, amount: uint) -> ty::Region {
+    match region {
+        ty::ReLateBound(debruijn, br) => {
+            ty::ReLateBound(debruijn.shifted(amount), br)
+        }
+        _ => {
+            region
+        }
+    }
+}
+
+pub fn shift_regions<T:TypeFoldable+Repr>(tcx: &ty::ctxt, amount: uint, value: &T) -> T {
+    debug!("shift_regions(value={}, amount={})",
+           value.repr(tcx), amount);
+
+    value.fold_with(&mut RegionFolder::new(tcx, |region, _current_depth| {
+        shift_region(region, amount)
+    }))
+}
+
diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs
index 3ef9a59d8ad..68677492649 100644
--- a/src/librustc/middle/typeck/astconv.rs
+++ b/src/librustc/middle/typeck/astconv.rs
@@ -55,11 +55,10 @@ use middle::subst::{FnSpace, TypeSpace, AssocSpace, SelfSpace, Subst, Substs};
 use middle::subst::{VecPerParamSpace};
 use middle::ty;
 use middle::typeck::lookup_def_tcx;
-use middle::typeck::infer;
-use middle::typeck::rscope::{UnelidableRscope, RegionScope, SpecificRscope, BindingRscope};
+use middle::typeck::rscope::{UnelidableRscope, RegionScope, SpecificRscope,
+                             ShiftedRscope, BindingRscope};
 use middle::typeck::rscope;
 use middle::typeck::TypeAndSubsts;
-use middle::typeck;
 use util::nodemap::DefIdMap;
 use util::ppaux::{Repr, UserString};
 
@@ -106,9 +105,8 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &ast::Lifetime)
             ty::ReStatic
         }
 
-        Some(&rl::DefLateBoundRegion(binder_id, _, id)) => {
-            ty::ReLateBound(binder_id, ty::BrNamed(ast_util::local_def(id),
-                                                   lifetime.name))
+        Some(&rl::DefLateBoundRegion(debruijn, id)) => {
+            ty::ReLateBound(debruijn, ty::BrNamed(ast_util::local_def(id), lifetime.name))
         }
 
         Some(&rl::DefEarlyBoundRegion(space, index, id)) => {
@@ -203,15 +201,14 @@ pub fn opt_ast_region_to_region<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
     r
 }
 
-fn ast_path_substs<'tcx,AC,RS>(
+fn ast_path_substs_for_ty<'tcx,AC,RS>(
     this: &AC,
     rscope: &RS,
     decl_def_id: ast::DefId,
     decl_generics: &ty::Generics,
     self_ty: Option<ty::t>,
     associated_ty: Option<ty::t>,
-    path: &ast::Path,
-    binder_id: ast::NodeId)
+    path: &ast::Path)
     -> Substs
     where AC: AstConv<'tcx>, RS: RegionScope
 {
@@ -235,12 +232,35 @@ fn ast_path_substs<'tcx,AC,RS>(
     assert!(decl_generics.types.all(|d| d.space != FnSpace));
 
     let (regions, types) = match path.segments.last().unwrap().parameters {
-        ast::AngleBracketedParameters(ref data) =>
-            angle_bracketed_parameters(this, rscope, data),
-        ast::ParenthesizedParameters(ref data) =>
-            parenthesized_parameters(this, binder_id, data),
+        ast::AngleBracketedParameters(ref data) => {
+            convert_angle_bracketed_parameters(this, rscope, data)
+        }
+        ast::ParenthesizedParameters(ref data) => {
+            span_err!(tcx.sess, path.span, E0169,
+                      "parenthesized parameters may only be used with a trait");
+            (Vec::new(), convert_parenthesized_parameters(this, data))
+        }
     };
 
+    create_substs_for_ast_path(this, rscope, path.span, decl_def_id,
+                               decl_generics, self_ty, types, regions, associated_ty)
+}
+
+fn create_substs_for_ast_path<'tcx,AC,RS>(
+    this: &AC,
+    rscope: &RS,
+    span: Span,
+    decl_def_id: ast::DefId,
+    decl_generics: &ty::Generics,
+    self_ty: Option<ty::t>,
+    types: Vec<ty::t>,
+    regions: Vec<ty::Region>,
+    associated_ty: Option<ty::t>)
+    -> Substs
+    where AC: AstConv<'tcx>, RS: RegionScope
+{
+    let tcx = this.tcx();
+
     // If the type is parameterized by the this region, then replace this
     // region with the current anon region binding (in other words,
     // whatever & would get replaced with).
@@ -250,10 +270,10 @@ fn ast_path_substs<'tcx,AC,RS>(
         regions
     } else {
         let anon_regions =
-            rscope.anon_regions(path.span, expected_num_region_params);
+            rscope.anon_regions(span, expected_num_region_params);
 
         if supplied_num_region_params != 0 || anon_regions.is_err() {
-            span_err!(tcx.sess, path.span, E0107,
+            span_err!(tcx.sess, span, E0107,
                       "wrong number of lifetime parameters: expected {}, found {}",
                       expected_num_region_params, supplied_num_region_params);
         }
@@ -285,7 +305,7 @@ fn ast_path_substs<'tcx,AC,RS>(
         } else {
             "expected"
         };
-        this.tcx().sess.span_fatal(path.span,
+        this.tcx().sess.span_fatal(span,
                                    format!("wrong number of type arguments: {} {}, found {}",
                                            expected,
                                            required_ty_param_count,
@@ -296,7 +316,7 @@ fn ast_path_substs<'tcx,AC,RS>(
         } else {
             "expected"
         };
-        this.tcx().sess.span_fatal(path.span,
+        this.tcx().sess.span_fatal(span,
                                    format!("wrong number of type arguments: {} {}, found {}",
                                            expected,
                                            formal_ty_param_count,
@@ -305,9 +325,9 @@ fn ast_path_substs<'tcx,AC,RS>(
 
     if supplied_ty_param_count > required_ty_param_count
         && !this.tcx().sess.features.borrow().default_type_params {
-        span_err!(this.tcx().sess, path.span, E0108,
+        span_err!(this.tcx().sess, span, E0108,
             "default type parameters are experimental and possibly buggy");
-        span_help!(this.tcx().sess, path.span,
+        span_help!(this.tcx().sess, span,
             "add #![feature(default_type_params)] to the crate attributes to enable");
     }
 
@@ -333,12 +353,11 @@ fn ast_path_substs<'tcx,AC,RS>(
                 // This is a default type parameter.
                 let default = default.subst_spanned(tcx,
                                                     &substs,
-                                                    Some(path.span));
+                                                    Some(span));
                 substs.types.push(TypeSpace, default);
             }
             None => {
-                tcx.sess.span_bug(path.span,
-                                  "extra parameter without default");
+                tcx.sess.span_bug(span, "extra parameter without default");
             }
         }
     }
@@ -346,53 +365,64 @@ fn ast_path_substs<'tcx,AC,RS>(
     for param in decl_generics.types.get_slice(AssocSpace).iter() {
         substs.types.push(
             AssocSpace,
-            this.associated_type_binding(path.span,
+            this.associated_type_binding(span,
                                          associated_ty,
                                          decl_def_id,
                                          param.def_id))
     }
 
     return substs;
+}
 
-    fn angle_bracketed_parameters<'tcx, AC, RS>(this: &AC,
-                                                rscope: &RS,
-                                                data: &ast::AngleBracketedParameterData)
-                                                -> (Vec<ty::Region>, Vec<ty::t>)
-        where AC: AstConv<'tcx>, RS: RegionScope
-    {
-        let regions: Vec<_> =
-            data.lifetimes.iter()
-            .map(|l| ast_region_to_region(this.tcx(), l))
-            .collect();
-
-        let types: Vec<_> =
-            data.types.iter()
-            .map(|t| ast_ty_to_ty(this, rscope, &**t))
-            .collect();
-
-        (regions, types)
-    }
+fn convert_angle_bracketed_parameters<'tcx, AC, RS>(this: &AC,
+                                                    rscope: &RS,
+                                                    data: &ast::AngleBracketedParameterData)
+                                                    -> (Vec<ty::Region>, Vec<ty::t>)
+    where AC: AstConv<'tcx>, RS: RegionScope
+{
+    let regions: Vec<_> =
+        data.lifetimes.iter()
+        .map(|l| ast_region_to_region(this.tcx(), l))
+        .collect();
 
-    fn parenthesized_parameters<'tcx,AC>(this: &AC,
-                                         binder_id: ast::NodeId,
-                                         data: &ast::ParenthesizedParameterData)
-                                         -> (Vec<ty::Region>, Vec<ty::t>)
-        where AC: AstConv<'tcx>
-    {
-        let binding_rscope = BindingRscope::new(binder_id);
-
-        let inputs = data.inputs.iter()
-                                .map(|a_t| ast_ty_to_ty(this, &binding_rscope, &**a_t))
-                                .collect();
-        let input_ty = ty::mk_tup(this.tcx(), inputs);
-
-        let output = match data.output {
-            Some(ref output_ty) => ast_ty_to_ty(this, &binding_rscope, &**output_ty),
-            None => ty::mk_nil(this.tcx())
-        };
+    let types: Vec<_> =
+        data.types.iter()
+        .map(|t| ast_ty_to_ty(this, rscope, &**t))
+        .collect();
 
-        (Vec::new(), vec![input_ty, output])
-    }
+    (regions, types)
+}
+
+fn convert_parenthesized_parameters<'tcx,AC>(this: &AC,
+                                             data: &ast::ParenthesizedParameterData)
+                                             -> Vec<ty::t>
+    where AC: AstConv<'tcx>
+{
+    let binding_rscope = BindingRscope::new();
+
+    let inputs = data.inputs.iter()
+                            .map(|a_t| ast_ty_to_ty(this, &binding_rscope, &**a_t))
+                            .collect();
+    let input_ty = ty::mk_tup(this.tcx(), inputs);
+
+    let output = match data.output {
+        Some(ref output_ty) => ast_ty_to_ty(this, &binding_rscope, &**output_ty),
+        None => ty::mk_nil(this.tcx()),
+    };
+
+    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::t>,
+    associated_type: Option<ty::t>)
+    -> Rc<ty::TraitRef>
+    where AC: AstConv<'tcx>, RS: RegionScope
+{
+    instantiate_trait_ref(this, rscope, &ast_trait_ref.trait_ref, self_ty, associated_type)
 }
 
 pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC,
@@ -413,16 +443,9 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC,
     match lookup_def_tcx(this.tcx(),
                          ast_trait_ref.path.span,
                          ast_trait_ref.ref_id) {
-        def::DefTrait(trait_did) => {
-            let trait_ref =
-                Rc::new(ast_path_to_trait_ref(this,
-                                              rscope,
-                                              trait_did,
-                                              self_ty,
-                                              associated_type,
-                                              &ast_trait_ref.path,
-                                              ast_trait_ref.ref_id));
-
+        def::DefTrait(trait_def_id) => {
+            let trait_ref = Rc::new(ast_path_to_trait_ref(this, rscope, trait_def_id, self_ty,
+                                                          associated_type, &ast_trait_ref.path));
             this.tcx().trait_refs.borrow_mut().insert(ast_trait_ref.ref_id,
                                                       trait_ref.clone());
             trait_ref
@@ -435,36 +458,52 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC,
     }
 }
 
-pub fn ast_path_to_trait_ref<'tcx,AC,RS>(this: &AC,
-                                         rscope: &RS,
-                                         trait_def_id: ast::DefId,
-                                         self_ty: Option<ty::t>,
-                                         associated_type: Option<ty::t>,
-                                         path: &ast::Path,
-                                         binder_id: ast::NodeId)
-                                         -> ty::TraitRef
-                                         where AC: AstConv<'tcx>,
-                                               RS: RegionScope {
+fn ast_path_to_trait_ref<'tcx,AC,RS>(
+    this: &AC,
+    rscope: &RS,
+    trait_def_id: ast::DefId,
+    self_ty: Option<ty::t>,
+    associated_type: Option<ty::t>,
+    path: &ast::Path)
+    -> ty::TraitRef
+    where AC: AstConv<'tcx>, RS: RegionScope
+{
     let trait_def = this.get_trait_def(trait_def_id);
-    ty::TraitRef {
-        def_id: trait_def_id,
-        substs: ast_path_substs(this,
-                                rscope,
-                                trait_def_id,
-                                &trait_def.generics,
-                                self_ty,
-                                associated_type,
-                                path,
-                                binder_id)
-    }
+
+    // the trait reference introduces a binding level here, so
+    // we need to shift the `rscope`. It'd be nice if we could
+    // do away with this rscope stuff and work this knowledge
+    // into resolve_lifetimes, as we do with non-omitted
+    // lifetimes. Oh well, not there yet.
+    let shifted_rscope = ShiftedRscope::new(rscope);
+
+    let (regions, types) = match path.segments.last().unwrap().parameters {
+        ast::AngleBracketedParameters(ref data) => {
+            convert_angle_bracketed_parameters(this, &shifted_rscope, data)
+        }
+        ast::ParenthesizedParameters(ref data) => {
+            (Vec::new(), convert_parenthesized_parameters(this, data))
+        }
+    };
+
+    let substs = create_substs_for_ast_path(this,
+                                            &shifted_rscope,
+                                            path.span,
+                                            trait_def_id,
+                                            &trait_def.generics,
+                                            self_ty,
+                                            types,
+                                            regions,
+                                            associated_type);
+
+    ty::TraitRef::new(trait_def_id, substs)
 }
 
 pub fn ast_path_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
     this: &AC,
     rscope: &RS,
     did: ast::DefId,
-    path: &ast::Path,
-    binder_id: ast::NodeId)
+    path: &ast::Path)
     -> TypeAndSubsts
 {
     let tcx = this.tcx();
@@ -473,14 +512,13 @@ pub fn ast_path_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
         ty: decl_ty
     } = this.get_item_ty(did);
 
-    let substs = ast_path_substs(this,
-                                 rscope,
-                                 did,
-                                 &generics,
-                                 None,
-                                 None,
-                                 path,
-                                 binder_id);
+    let substs = ast_path_substs_for_ty(this,
+                                        rscope,
+                                        did,
+                                        &generics,
+                                        None,
+                                        None,
+                                        path);
     let ty = decl_ty.subst(tcx, &substs);
     TypeAndSubsts { substs: substs, ty: ty }
 }
@@ -494,8 +532,7 @@ pub fn ast_path_to_ty_relaxed<'tcx,AC,RS>(
     this: &AC,
     rscope: &RS,
     did: ast::DefId,
-    path: &ast::Path,
-    binder_id: ast::NodeId)
+    path: &ast::Path)
     -> TypeAndSubsts
     where AC : AstConv<'tcx>, RS : RegionScope
 {
@@ -521,7 +558,7 @@ pub fn ast_path_to_ty_relaxed<'tcx,AC,RS>(
         Substs::new(VecPerParamSpace::params_from_type(type_params),
                     VecPerParamSpace::params_from_type(region_params))
     } else {
-        ast_path_substs(this, rscope, did, &generics, None, None, path, binder_id)
+        ast_path_substs_for_ty(this, rscope, did, &generics, None, None, path)
     };
 
     let ty = decl_ty.subst(tcx, &substs);
@@ -628,7 +665,7 @@ pub fn ast_ty_to_builtin_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
             match a_def {
                 def::DefTy(did, _) |
                 def::DefStruct(did) if Some(did) == this.tcx().lang_items.owned_box() => {
-                    let ty = ast_path_to_ty(this, rscope, did, path, id).ty;
+                    let ty = ast_path_to_ty(this, rscope, did, path).ty;
                     match ty::get(ty).sty {
                         ty::ty_struct(struct_def_id, ref substs) => {
                             assert_eq!(struct_def_id, did);
@@ -689,8 +726,7 @@ fn mk_pointer<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
                                                        trait_def_id,
                                                        None,
                                                        None,
-                                                       path,
-                                                       id);
+                                                       path);
                     let empty_vec = [];
                     let bounds = match *opt_bounds { None => empty_vec.as_slice(),
                                                      Some(ref bounds) => bounds.as_slice() };
@@ -752,12 +788,7 @@ fn associated_ty_to_ty<'tcx,AC,RS>(this: &AC,
                                           trait_did,
                                           None,
                                           Some(for_type),
-                                          trait_path,
-                                          ast::DUMMY_NODE_ID); // *see below
-
-    // * The trait in a qualified path cannot be "higher-ranked" and
-    // hence cannot use the parenthetical sugar, so the binder-id is
-    // irrelevant.
+                                          trait_path);
 
     debug!("associated_ty_to_ty(trait_ref={})",
            trait_ref.repr(this.tcx()));
@@ -830,8 +861,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
                     tcx.sess.span_err(ast_ty.span,
                                       "variadic function must have C calling convention");
                 }
-                ty::mk_bare_fn(tcx, ty_of_bare_fn(this, ast_ty.id, bf.fn_style,
-                                                  bf.abi, &*bf.decl))
+                ty::mk_bare_fn(tcx, ty_of_bare_fn(this, bf.fn_style, bf.abi, &*bf.decl))
             }
             ast::TyClosure(ref f) => {
                 // Use corresponding trait store to figure out default bounds
@@ -842,7 +872,6 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
                                                      [].as_slice(),
                                                      f.bounds.as_slice());
                 let fn_decl = ty_of_closure(this,
-                                            ast_ty.id,
                                             f.fn_style,
                                             f.onceness,
                                             bounds,
@@ -863,7 +892,6 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
                                                      f.bounds.as_slice());
 
                 let fn_decl = ty_of_closure(this,
-                                            ast_ty.id,
                                             f.fn_style,
                                             f.onceness,
                                             bounds,
@@ -874,15 +902,8 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
 
                 ty::mk_closure(tcx, fn_decl)
             }
-            ast::TyPolyTraitRef(ref data) => {
-                // FIXME(#18639) this is just a placeholder for code to come
-                let principal = instantiate_trait_ref(this, rscope, &data.trait_ref, None, None);
-                let bounds = conv_existential_bounds(this,
-                                                     rscope,
-                                                     ast_ty.span,
-                                                     &[principal.clone()],
-                                                     &[]);
-                ty::mk_trait(tcx, (*principal).clone(), bounds)
+            ast::TyPolyTraitRef(ref bounds) => {
+                conv_ty_poly_trait_ref(this, rscope, ast_ty.span, bounds.as_slice())
             }
             ast::TyPath(ref path, ref bounds, id) => {
                 let a_def = match tcx.def_map.borrow().get(&id) {
@@ -910,8 +931,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
                                                            trait_def_id,
                                                            None,
                                                            None,
-                                                           path,
-                                                           id);
+                                                           path);
                         let empty_bounds: &[ast::TyParamBound] = &[];
                         let ast_bounds = match *bounds {
                             Some(ref b) => b.as_slice(),
@@ -922,12 +942,12 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
                                                              ast_ty.span,
                                                              &[Rc::new(result.clone())],
                                                              ast_bounds);
-                        ty::mk_trait(tcx,
-                                     result,
-                                     bounds)
+                        let result_ty = ty::mk_trait(tcx, result, bounds);
+                        debug!("ast_ty_to_ty: result_ty={}", result_ty.repr(this.tcx()));
+                        result_ty
                     }
                     def::DefTy(did, _) | def::DefStruct(did) => {
-                        ast_path_to_ty(this, rscope, did, path, id).ty
+                        ast_path_to_ty(this, rscope, did, path).ty
                     }
                     def::DefTyParam(space, id, n) => {
                         check_path_args(tcx, path, NO_TPS | NO_REGIONS);
@@ -1056,7 +1076,6 @@ struct SelfInfo<'a> {
 
 pub fn ty_of_method<'tcx, AC: AstConv<'tcx>>(
                     this: &AC,
-                    id: ast::NodeId,
                     fn_style: ast::FnStyle,
                     untransformed_self_ty: ty::t,
                     explicit_self: &ast::ExplicitSelf,
@@ -1069,7 +1088,6 @@ pub fn ty_of_method<'tcx, AC: AstConv<'tcx>>(
     });
     let (bare_fn_ty, optional_explicit_self_category) =
         ty_of_method_or_bare_fn(this,
-                                id,
                                 fn_style,
                                 abi,
                                 self_info,
@@ -1077,17 +1095,14 @@ pub fn ty_of_method<'tcx, AC: AstConv<'tcx>>(
     (bare_fn_ty, optional_explicit_self_category.unwrap())
 }
 
-pub fn ty_of_bare_fn<'tcx, AC: AstConv<'tcx>>(this: &AC, id: ast::NodeId,
-                                              fn_style: ast::FnStyle, abi: abi::Abi,
+pub fn ty_of_bare_fn<'tcx, AC: AstConv<'tcx>>(this: &AC, fn_style: ast::FnStyle, abi: abi::Abi,
                                               decl: &ast::FnDecl) -> ty::BareFnTy {
-    let (bare_fn_ty, _) =
-        ty_of_method_or_bare_fn(this, id, fn_style, abi, None, decl);
+    let (bare_fn_ty, _) = ty_of_method_or_bare_fn(this, fn_style, abi, None, decl);
     bare_fn_ty
 }
 
 fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
                            this: &AC,
-                           id: ast::NodeId,
                            fn_style: ast::FnStyle,
                            abi: abi::Abi,
                            opt_self_info: Option<SelfInfo>,
@@ -1098,7 +1113,7 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
 
     // New region names that appear inside of the arguments of the function
     // declaration are bound to that function type.
-    let rb = rscope::BindingRscope::new(id);
+    let rb = rscope::BindingRscope::new();
 
     // `implied_output_region` is the region that will be assumed for any
     // region parameters in the return type. In accordance with the rules for
@@ -1114,7 +1129,9 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
                 determine_explicit_self_category(this, &rb, &self_info);
             explicit_self_category_result = Some(explicit_self_category);
             match explicit_self_category {
-                ty::StaticExplicitSelfCategory => (None, None),
+                ty::StaticExplicitSelfCategory => {
+                    (None, None)
+                }
                 ty::ByValueExplicitSelfCategory => {
                     (Some(self_info.untransformed_self_ty), None)
                 }
@@ -1205,7 +1222,6 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
         fn_style: fn_style,
         abi: abi,
         sig: ty::FnSig {
-            binder_id: id,
             inputs: self_and_input_tys,
             output: output_ty,
             variadic: decl.variadic
@@ -1218,8 +1234,9 @@ fn determine_explicit_self_category<'tcx, AC: AstConv<'tcx>,
                                     this: &AC,
                                     rscope: &RS,
                                     self_info: &SelfInfo)
-                                    -> ty::ExplicitSelfCategory {
-    match self_info.explicit_self.node {
+                                    -> ty::ExplicitSelfCategory
+{
+    return match self_info.explicit_self.node {
         ast::SelfStatic => ty::StaticExplicitSelfCategory,
         ast::SelfValue(_) => ty::ByValueExplicitSelfCategory,
         ast::SelfRegion(ref lifetime, mutability, _) => {
@@ -1233,64 +1250,69 @@ fn determine_explicit_self_category<'tcx, AC: AstConv<'tcx>,
         ast::SelfExplicit(ref ast_type, _) => {
             let explicit_type = ast_ty_to_ty(this, rscope, &**ast_type);
 
-            {
-                let inference_context = infer::new_infer_ctxt(this.tcx());
-                let expected_self = self_info.untransformed_self_ty;
-                let actual_self = explicit_type;
-                let result = infer::mk_eqty(
-                    &inference_context,
-                    false,
-                    infer::Misc(self_info.explicit_self.span),
-                    expected_self,
-                    actual_self);
-                match result {
-                    Ok(_) => {
-                        inference_context.resolve_regions_and_report_errors();
-                        return ty::ByValueExplicitSelfCategory
-                    }
-                    Err(_) => {}
+            // We wish to (for now) categorize an explicit self
+            // declaration like `self: SomeType` into either `self`,
+            // `&self`, `&mut self`, or `Box<self>`. We do this here
+            // by some simple pattern matching. A more precise check
+            // is done later in `check_method_self_type()`.
+            //
+            // Examples:
+            //
+            // ```
+            // impl Foo for &T {
+            //     // Legal declarations:
+            //     fn method1(self: &&T); // ByReferenceExplicitSelfCategory
+            //     fn method2(self: &T); // ByValueExplicitSelfCategory
+            //     fn method3(self: Box<&T>); // ByBoxExplicitSelfCategory
+            //
+            //     // Invalid cases will be caught later by `check_method_self_type`:
+            //     fn method_err1(self: &mut T); // ByReferenceExplicitSelfCategory
+            // }
+            // ```
+            //
+            // To do the check we just count the number of "modifiers"
+            // on each type and compare them. If they are the same or
+            // the impl has more, we call it "by value". Otherwise, we
+            // look at the outermost modifier on the method decl and
+            // call it by-ref, by-box as appropriate. For method1, for
+            // example, the impl type has one modifier, but the method
+            // type has two, so we end up with
+            // ByReferenceExplicitSelfCategory.
+
+            let impl_modifiers = count_modifiers(self_info.untransformed_self_ty);
+            let method_modifiers = count_modifiers(explicit_type);
+
+            debug!("determine_explicit_self_category(self_info.untransformed_self_ty={} \
+                   explicit_type={} \
+                   modifiers=({},{})",
+                   self_info.untransformed_self_ty.repr(this.tcx()),
+                   explicit_type.repr(this.tcx()),
+                   impl_modifiers,
+                   method_modifiers);
+
+            if impl_modifiers >= method_modifiers {
+                ty::ByValueExplicitSelfCategory
+            } else {
+                match ty::get(explicit_type).sty {
+                    ty::ty_rptr(r, mt) => ty::ByReferenceExplicitSelfCategory(r, mt.mutbl),
+                    ty::ty_uniq(_) => ty::ByBoxExplicitSelfCategory,
+                    _ => ty::ByValueExplicitSelfCategory,
                 }
             }
+        }
+    };
 
-            match ty::get(explicit_type).sty {
-                ty::ty_rptr(region, tm) => {
-                    typeck::require_same_types(
-                        this.tcx(),
-                        None,
-                        false,
-                        self_info.explicit_self.span,
-                        self_info.untransformed_self_ty,
-                        tm.ty,
-                        || "not a valid type for `self`".to_string());
-                    return ty::ByReferenceExplicitSelfCategory(region,
-                                                               tm.mutbl)
-                }
-                ty::ty_uniq(typ) => {
-                    typeck::require_same_types(
-                        this.tcx(),
-                        None,
-                        false,
-                        self_info.explicit_self.span,
-                        self_info.untransformed_self_ty,
-                        typ,
-                        || "not a valid type for `self`".to_string());
-                    return ty::ByBoxExplicitSelfCategory
-                }
-                _ => {
-                    this.tcx()
-                        .sess
-                        .span_err(self_info.explicit_self.span,
-                                  "not a valid type for `self`");
-                    return ty::ByValueExplicitSelfCategory
-                }
-            }
+    fn count_modifiers(ty: ty::t) -> uint {
+        match ty::get(ty).sty {
+            ty::ty_rptr(_, mt) => count_modifiers(mt.ty) + 1,
+            ty::ty_uniq(t) => count_modifiers(t) + 1,
+            _ => 0,
         }
     }
 }
 
 pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>(
     this: &AC,
-    id: ast::NodeId,
     fn_style: ast::FnStyle,
     onceness: ast::Onceness,
     bounds: ty::ExistentialBounds,
@@ -1300,13 +1322,14 @@ pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>(
     expected_sig: Option<ty::FnSig>)
     -> ty::ClosureTy
 {
-    debug!("ty_of_fn_decl");
+    debug!("ty_of_closure(expected_sig={})",
+           expected_sig.repr(this.tcx()));
 
     // new region names that appear inside of the fn decl are bound to
     // that function type
-    let rb = rscope::BindingRscope::new(id);
+    let rb = rscope::BindingRscope::new();
 
-    let input_tys = decl.inputs.iter().enumerate().map(|(i, a)| {
+    let input_tys: Vec<_> = decl.inputs.iter().enumerate().map(|(i, a)| {
         let expected_arg_ty = expected_sig.as_ref().and_then(|e| {
             // no guarantee that the correct number of expected args
             // were supplied
@@ -1331,14 +1354,16 @@ pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>(
         ast::NoReturn(_) => ty::FnDiverging
     };
 
+    debug!("ty_of_closure: input_tys={}", input_tys.repr(this.tcx()));
+    debug!("ty_of_closure: output_ty={}", output_ty.repr(this.tcx()));
+
     ty::ClosureTy {
         fn_style: fn_style,
         onceness: onceness,
         store: store,
         bounds: bounds,
         abi: abi,
-        sig: ty::FnSig {binder_id: id,
-                        inputs: input_tys,
+        sig: ty::FnSig {inputs: input_tys,
                         output: output_ty,
                         variadic: decl.variadic}
     }
@@ -1365,15 +1390,66 @@ pub fn conv_existential_bounds<'tcx, AC: AstConv<'tcx>, RS:RegionScope>(
     let ast_bound_refs: Vec<&ast::TyParamBound> =
         ast_bounds.iter().collect();
 
+    let partitioned_bounds =
+        partition_bounds(this.tcx(), span, ast_bound_refs.as_slice());
+
+    conv_existential_bounds_from_partitioned_bounds(
+        this, rscope, span, main_trait_refs, partitioned_bounds)
+}
+
+fn conv_ty_poly_trait_ref<'tcx, AC, RS>(
+    this: &AC,
+    rscope: &RS,
+    span: Span,
+    ast_bounds: &[ast::TyParamBound])
+    -> ty::t
+    where AC: AstConv<'tcx>, RS:RegionScope
+{
+    let ast_bounds: Vec<&ast::TyParamBound> = ast_bounds.iter().collect();
+    let mut partitioned_bounds = partition_bounds(this.tcx(), span, ast_bounds[]);
+
+    let main_trait_bound = match partitioned_bounds.trait_bounds.remove(0) {
+        Some(trait_bound) => {
+            Some(instantiate_poly_trait_ref(this, rscope, trait_bound, None, None))
+        }
+        None => {
+            this.tcx().sess.span_err(
+                span,
+                "at least one non-builtin trait is required for an object type");
+            None
+        }
+    };
+
+    let bounds = conv_existential_bounds_from_partitioned_bounds(this,
+                                                                 rscope,
+                                                                 span,
+                                                                 main_trait_bound.as_slice(),
+                                                                 partitioned_bounds);
+
+    match main_trait_bound {
+        None => ty::mk_err(),
+        Some(principal) => ty::mk_trait(this.tcx(), (*principal).clone(), bounds)
+    }
+}
+
+pub fn conv_existential_bounds_from_partitioned_bounds<'tcx, AC, RS>(
+    this: &AC,
+    rscope: &RS,
+    span: Span,
+    main_trait_refs: &[Rc<ty::TraitRef>],
+    partitioned_bounds: PartitionedBounds)
+    -> ty::ExistentialBounds
+    where AC: AstConv<'tcx>, RS:RegionScope
+{
     let PartitionedBounds { builtin_bounds,
                             trait_bounds,
                             region_bounds } =
-        partition_bounds(this.tcx(), span, ast_bound_refs.as_slice());
+        partitioned_bounds;
 
     if !trait_bounds.is_empty() {
         let b = &trait_bounds[0];
         this.tcx().sess.span_err(
-            b.path.span,
+            b.trait_ref.path.span,
             format!("only the builtin traits can be used \
                      as closure or object bounds").as_slice());
     }
@@ -1505,7 +1581,7 @@ fn compute_region_bound<'tcx, AC: AstConv<'tcx>, RS:RegionScope>(
 
 pub struct PartitionedBounds<'a> {
     pub builtin_bounds: ty::BuiltinBounds,
-    pub trait_bounds: Vec<&'a ast::TraitRef>,
+    pub trait_bounds: Vec<&'a ast::PolyTraitRef>,
     pub region_bounds: Vec<&'a ast::Lifetime>,
 }
 
@@ -1527,8 +1603,7 @@ pub fn partition_bounds<'a>(tcx: &ty::ctxt,
     for &ast_bound in ast_bounds.iter() {
         match *ast_bound {
             ast::TraitTyParamBound(ref b) => {
-                let b = &b.trait_ref; // FIXME
-                match lookup_def_tcx(tcx, b.path.span, b.ref_id) {
+                match lookup_def_tcx(tcx, b.trait_ref.path.span, b.trait_ref.ref_id) {
                     def::DefTrait(trait_did) => {
                         match trait_def_ids.get(&trait_did) {
                             // Already seen this trait. We forbid
@@ -1536,10 +1611,10 @@ pub fn partition_bounds<'a>(tcx: &ty::ctxt,
                             // reason).
                             Some(span) => {
                                 span_err!(
-                                    tcx.sess, b.path.span, E0127,
+                                    tcx.sess, b.trait_ref.path.span, E0127,
                                     "trait `{}` already appears in the \
                                      list of bounds",
-                                    b.path.user_string(tcx));
+                                    b.trait_ref.path.user_string(tcx));
                                 tcx.sess.span_note(
                                     *span,
                                     "previous appearance is here");
@@ -1550,7 +1625,7 @@ pub fn partition_bounds<'a>(tcx: &ty::ctxt,
                             None => { }
                         }
 
-                        trait_def_ids.insert(trait_did, b.path.span);
+                        trait_def_ids.insert(trait_did, b.trait_ref.path.span);
 
                         if ty::try_add_builtin_trait(tcx,
                                                      trait_did,
diff --git a/src/librustc/middle/typeck/check/method/confirm.rs b/src/librustc/middle/typeck/check/method/confirm.rs
index ba64a1e23a7..b4d22f117d4 100644
--- a/src/librustc/middle/typeck/check/method/confirm.rs
+++ b/src/librustc/middle/typeck/check/method/confirm.rs
@@ -20,6 +20,7 @@ use middle::typeck::{MethodCall, MethodCallee, MethodObject, MethodOrigin,
                      MethodParam, MethodStatic, MethodTraitObject, MethodTypeParam};
 use middle::typeck::infer;
 use middle::typeck::infer::InferCtxt;
+use middle::ty_fold::HigherRankedFoldable;
 use syntax::ast;
 use syntax::codemap::Span;
 use std::rc::Rc;
@@ -32,6 +33,27 @@ struct ConfirmContext<'a, 'tcx:'a> {
     self_expr: &'a ast::Expr,
 }
 
+struct InstantiatedMethodSig {
+    /// Function signature of the method being invoked. The 0th
+    /// argument is the receiver.
+    method_sig: ty::FnSig,
+
+    /// Substitutions for all types/early-bound-regions declared on
+    /// the method.
+    all_substs: subst::Substs,
+
+    /// Substitution to use when adding obligations from the method
+    /// bounds. Normally equal to `all_substs` except for object
+    /// receivers. See FIXME in instantiate_method_sig() for
+    /// explanation.
+    method_bounds_substs: subst::Substs,
+
+    /// Generic bounds on the method's parameters which must be added
+    /// as pending obligations.
+    method_bounds: ty::GenericBounds,
+}
+
+
 pub fn confirm(fcx: &FnCtxt,
                span: Span,
                self_expr: &ast::Expr,
@@ -79,14 +101,16 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
         debug!("all_substs={}", all_substs.repr(self.tcx()));
 
         // Create the final signature for the method, replacing late-bound regions.
-        let method_sig = self.instantiate_method_sig(&pick, &all_substs);
+        let InstantiatedMethodSig {
+            method_sig, all_substs, method_bounds_substs, method_bounds
+        } = self.instantiate_method_sig(&pick, all_substs);
         let method_self_ty = method_sig.inputs[0];
 
         // Unify the (adjusted) self type with what the method expects.
         self.unify_receivers(self_ty, method_self_ty);
 
         // Add any trait/regions obligations specified on the method's type parameters.
-        self.add_obligations(&pick, &all_substs);
+        self.add_obligations(&pick, &method_bounds_substs, &method_bounds);
 
         // Create the final `MethodCallee`.
         let fty = ty::mk_bare_fn(self.tcx(), ty::BareFnTy {
@@ -176,6 +200,10 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
          * where all type and region parameters are instantiated with
          * fresh variables. This substitution does not include any
          * parameters declared on the method itself.
+         *
+         * Note that this substitution may include late-bound regions
+         * from the impl level. If so, these are instantiated later in
+         * the `instantiate_method_sig` routine.
          */
 
         match pick.kind {
@@ -354,20 +382,34 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
 
     fn instantiate_method_sig(&mut self,
                               pick: &probe::Pick,
-                              all_substs: &subst::Substs)
-                              -> ty::FnSig
+                              all_substs: subst::Substs)
+                              -> InstantiatedMethodSig
     {
-        let ref bare_fn_ty = pick.method_ty.fty;
-        let fn_sig = bare_fn_ty.sig.subst(self.tcx(), all_substs);
-        self.infcx().replace_late_bound_regions_with_fresh_var(fn_sig.binder_id,
-                                                               self.span,
-                                                               infer::FnCall,
-                                                               &fn_sig).0
-    }
-
-    fn add_obligations(&mut self,
-                       pick: &probe::Pick,
-                       all_substs: &subst::Substs) {
+        // 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()));
+
+        // Instantiate the bounds on the method with the
+        // type/early-bound-regions substitutions performed.  The only
+        // late-bound-regions that can appear in bounds are from the
+        // impl, and those were already instantiated above.
+        //
         // FIXME(DST). Super hack. For a method on a trait object
         // `Trait`, the generic signature requires that
         // `Self:Trait`. Since, for an object, we bind `Self` to the
@@ -381,24 +423,54 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
         // obligations.  This causes us to generate the obligation
         // `err:Trait`, and the error type is considered to implement
         // all traits, so we're all good. Hack hack hack.
-        match pick.kind {
+        let method_bounds_substs = match pick.kind {
             probe::ObjectPick(..) => {
                 let mut temp_substs = all_substs.clone();
                 temp_substs.types.get_mut_slice(subst::SelfSpace)[0] = ty::mk_err();
-                self.fcx.add_obligations_for_parameters(
-                    traits::ObligationCause::misc(self.span),
-                    &temp_substs,
-                    &pick.method_ty.generics);
+                temp_substs
             }
             _ => {
-                self.fcx.add_obligations_for_parameters(
-                    traits::ObligationCause::misc(self.span),
-                    all_substs,
-                    &pick.method_ty.generics);
+                all_substs.clone()
             }
+        };
+        let method_bounds =
+            method_generics.to_bounds(self.tcx(), &method_bounds_substs);
+
+        debug!("method_bounds after subst = {}",
+               method_bounds.repr(self.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 = self.replace_late_bound_regions_with_fresh_var(&method_sig);
+
+        debug!("late-bound lifetimes from method instantiated, method_sig={}",
+               method_sig.repr(self.tcx()));
+
+        InstantiatedMethodSig {
+            method_sig: method_sig,
+            all_substs: all_substs,
+            method_bounds_substs: method_bounds_substs,
+            method_bounds: method_bounds,
         }
     }
 
+    fn add_obligations(&mut self,
+                       pick: &probe::Pick,
+                       method_bounds_substs: &subst::Substs,
+                       method_bounds: &ty::GenericBounds) {
+        debug!("add_obligations: pick={} method_bounds_substs={} method_bounds={}",
+               pick.repr(self.tcx()),
+               method_bounds_substs.repr(self.tcx()),
+               method_bounds.repr(self.tcx()));
+
+        self.fcx.add_obligations_for_parameters(
+            traits::ObligationCause::misc(self.span),
+            method_bounds_substs,
+            method_bounds);
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // RECONCILIATION
 
@@ -591,6 +663,13 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
                     source_trait_ref.repr(self.tcx()),
                     target_trait_def_id.repr(self.tcx()))[]);
     }
+
+    fn replace_late_bound_regions_with_fresh_var<T>(&self, value: &T) -> T
+        where T : HigherRankedFoldable
+    {
+        self.infcx().replace_late_bound_regions_with_fresh_var(
+            self.span, infer::FnCall, value).0
+    }
 }
 
 fn wrap_autoref(mut deref: ty::AutoDerefRef,
diff --git a/src/librustc/middle/typeck/check/method/mod.rs b/src/librustc/middle/typeck/check/method/mod.rs
index d4f1d4defa3..6c7df2cd07e 100644
--- a/src/librustc/middle/typeck/check/method/mod.rs
+++ b/src/librustc/middle/typeck/check/method/mod.rs
@@ -200,10 +200,12 @@ 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(fn_sig.binder_id,
-                                                                       span,
+    let fn_sig = fcx.infcx().replace_late_bound_regions_with_fresh_var(span,
                                                                        infer::FnCall,
                                                                        &fn_sig).0;
     let transformed_self_ty = fn_sig.inputs[0];
@@ -222,10 +224,15 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
     // so this also effectively registers `obligation` as well.  (We
     // used to register `obligation` explicitly, but that resulted in
     // double error messages being reported.)
+    //
+    // Note that as the method comes from a trait, it should not have
+    // any late-bound regions appearing in its bounds.
+    let method_bounds = method_ty.generics.to_bounds(fcx.tcx(), &trait_ref.substs);
+    assert!(!method_bounds.has_escaping_regions());
     fcx.add_obligations_for_parameters(
         traits::ObligationCause::misc(span),
         &trait_ref.substs,
-        &method_ty.generics);
+        &method_bounds);
 
     // FIXME(#18653) -- Try to resolve obligations, giving us more
     // typing information, which can sometimes be needed to avoid
diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc/middle/typeck/check/method/probe.rs
index 2e5397e768e..f9c3fa86752 100644
--- a/src/librustc/middle/typeck/check/method/probe.rs
+++ b/src/librustc/middle/typeck/check/method/probe.rs
@@ -17,6 +17,7 @@ use middle::subst;
 use middle::subst::Subst;
 use middle::traits;
 use middle::ty;
+use middle::ty_fold::HigherRankedFoldable;
 use middle::typeck::check;
 use middle::typeck::check::{FnCtxt, NoPreference};
 use middle::typeck::{MethodObject};
@@ -257,29 +258,28 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         ty::populate_implementations_for_type_if_necessary(self.tcx(), def_id);
 
         for impl_infos in self.tcx().inherent_impls.borrow().get(&def_id).iter() {
-            for &impl_did in impl_infos.iter() {
-                self.assemble_inherent_impl_probe(impl_did);
+            for &impl_def_id in impl_infos.iter() {
+                self.assemble_inherent_impl_probe(impl_def_id);
             }
         }
     }
 
-    fn assemble_inherent_impl_probe(&mut self, impl_did: ast::DefId) {
-        if !self.impl_dups.insert(impl_did) {
+    fn assemble_inherent_impl_probe(&mut self, impl_def_id: ast::DefId) {
+        if !self.impl_dups.insert(impl_def_id) {
             return; // already visited
         }
 
-        let method = match impl_method(self.tcx(), impl_did, self.method_name) {
+        let method = match impl_method(self.tcx(), impl_def_id, self.method_name) {
             Some(m) => m,
             None => { return; } // No method with correct name on this impl
         };
 
         if !self.has_applicable_self(&*method) {
             // No receiver declared. Not a candidate.
-            return self.record_static_candidate(ImplSource(impl_did));
+            return self.record_static_candidate(ImplSource(impl_def_id));
         }
 
-        let impl_pty = check::impl_self_ty(self.fcx, self.span, impl_did);
-        let impl_substs = impl_pty.substs;
+        let impl_substs = self.impl_substs(impl_def_id);
 
         // Determine the receiver type that the method itself expects.
         let xform_self_ty =
@@ -288,7 +288,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
         self.inherent_candidates.push(Candidate {
             xform_self_ty: xform_self_ty,
             method_ty: method,
-            kind: InherentImplCandidate(impl_did, impl_substs)
+            kind: InherentImplCandidate(impl_def_id, impl_substs)
         });
     }
 
@@ -496,8 +496,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                 continue;
             }
 
-            let impl_pty = check::impl_self_ty(self.fcx, self.span, impl_def_id);
-            let impl_substs = impl_pty.substs;
+            let impl_substs = self.impl_substs(impl_def_id);
 
             debug!("impl_substs={}", impl_substs.repr(self.tcx()));
 
@@ -675,7 +674,9 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                            mk_autoref_ty: |ast::Mutability, ty::Region| -> ty::t)
                            -> Option<PickResult>
     {
-        let region = self.infcx().next_region_var(infer::Autoref(self.span));
+        // In general, during probing we erase regions. See
+        // `impl_self_ty()` for an explanation.
+        let region = ty::ReStatic;
 
         // Search through mutabilities in order to find one where pick works:
         [ast::MutImmutable, ast::MutMutable]
@@ -746,6 +747,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                probe.repr(self.tcx()));
 
         self.infcx().probe(|| {
+            // First check that the self type can be related.
             match self.make_sub_ty(self_ty, probe.xform_self_ty) {
                 Ok(()) => { }
                 Err(_) => {
@@ -754,23 +756,34 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                 }
             }
 
+            // If so, impls may carry other conditions (e.g., where
+            // clauses) that must be considered. Make sure that those
+            // match as well (or at least may match, sometimes we
+            // don't have enough information to fully evaluate).
             match probe.kind {
                 InherentImplCandidate(impl_def_id, ref substs) |
                 ExtensionImplCandidate(impl_def_id, _, ref substs, _) => {
                     // Check whether the impl imposes obligations we have to worry about.
+                    let impl_generics = ty::lookup_item_type(self.tcx(), impl_def_id).generics;
+                    let impl_bounds = impl_generics.to_bounds(self.tcx(), substs);
+
+                    // 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;
+
+                    // Convert the bounds into obligations.
                     let obligations =
-                        traits::impl_obligations(
+                        traits::obligations_for_generics(
                             self.tcx(),
                             traits::ObligationCause::misc(self.span),
-                            impl_def_id,
-                            substs);
-
+                            &impl_bounds,
+                            &substs.types);
                     debug!("impl_obligations={}", obligations.repr(self.tcx()));
 
+                    // Evaluate those obligations to see if they might possibly hold.
                     let mut selcx = traits::SelectionContext::new(self.infcx(),
                                                                   &self.fcx.inh.param_env,
                                                                   self.fcx);
-
                     obligations.all(|o| selcx.evaluate_obligation(o))
                 }
 
@@ -883,20 +896,78 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                 self.infcx().next_ty_vars(
                     method.generics.types.len(subst::FnSpace));
 
+            // In general, during probe we erase regions. See
+            // `impl_self_ty()` for an explanation.
             let method_regions =
-                self.infcx().region_vars_for_defs(
-                    self.span,
-                    method.generics.regions.get_slice(subst::FnSpace));
+                method.generics.regions.get_slice(subst::FnSpace)
+                .iter()
+                .map(|_| ty::ReStatic)
+                .collect();
 
             placeholder = (*substs).clone().with_method(method_types, method_regions);
             substs = &placeholder;
         }
 
+        // Replace early-bound regions and types.
         let xform_self_ty = method.fty.sig.inputs[0].subst(self.tcx(), substs);
-        self.infcx().replace_late_bound_regions_with_fresh_var(method.fty.sig.binder_id,
-                                                               self.span,
-                                                               infer::FnCall,
-                                                               &xform_self_ty).0
+
+        // 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
+    }
+
+    fn impl_substs(&self,
+                   impl_def_id: ast::DefId)
+                   -> subst::Substs
+    {
+        let impl_pty = ty::lookup_item_type(self.tcx(), impl_def_id);
+
+        let type_vars =
+            impl_pty.generics.types.map(
+                |_| self.infcx().next_ty_var());
+
+        let region_placeholders =
+            impl_pty.generics.regions.map(
+                |_| ty::ReStatic); // see erase_late_bound_regions() for an expl of why 'static
+
+        subst::Substs::new(type_vars, region_placeholders)
+    }
+
+    fn erase_late_bound_regions<T>(&self, value: &T) -> T
+        where T : HigherRankedFoldable
+    {
+        /*!
+         * Replace late-bound-regions bound by `value` with `'static`
+         * using `ty::erase_late_bound_regions`.
+         *
+         * This is only a reasonable thing to do during the *probe*
+         * phase, not the *confirm* phase, of method matching. It is
+         * reasonable during the probe phase because we don't consider
+         * region relationships at all. Therefore, we can just replace
+         * all the region variables with 'static rather than creating
+         * fresh region variables. This is nice for two reasons:
+         *
+         * 1. Because the numbers of the region variables would
+         *    otherwise be fairly unique to this particular method
+         *    call, it winds up creating fewer types overall, which
+         *    helps for memory usage. (Admittedly, this is a rather
+         *    small effect, though measureable.)
+         *
+         * 2. It makes it easier to deal with higher-ranked trait
+         *    bounds, because we can replace any late-bound regions
+         *    with 'static. Otherwise, if we were going to replace
+         *    late-bound regions with actual region variables as is
+         *    proper, we'd have to ensure that the same region got
+         *    replaced with the same variable, which requires a bit
+         *    more coordination and/or tracking the substitution and
+         *    so forth.
+         */
+
+        ty::erase_late_bound_regions(self.tcx(), value)
     }
 }
 
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 33705343dab..8ae5c3a0a95 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -97,12 +97,12 @@ use middle::ty::{FnSig, VariantInfo};
 use middle::ty::{Polytype};
 use middle::ty::{Disr, ParamTy, ParameterEnvironment};
 use middle::ty;
+use middle::ty::{replace_late_bound_regions, liberate_late_bound_regions};
 use middle::ty_fold::TypeFolder;
 use middle::typeck::astconv::AstConv;
 use middle::typeck::astconv::{ast_region_to_region, ast_ty_to_ty};
 use middle::typeck::astconv;
 use middle::typeck::check::_match::pat_ctxt;
-use middle::typeck::check::regionmanip::replace_late_bound_regions;
 use middle::typeck::CrateCtxt;
 use middle::typeck::infer;
 use middle::typeck::rscope::RegionScope;
@@ -528,9 +528,7 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
 
     // First, we have to replace any bound regions in the fn type with free ones.
     // The free region references will be bound the node_id of the body block.
-    let (_, fn_sig) = replace_late_bound_regions(tcx, fn_sig.binder_id, fn_sig, |br| {
-        ty::ReFree(ty::FreeRegion {scope_id: body.id, bound_region: br})
-    });
+    let fn_sig = liberate_late_bound_regions(tcx, body.id, fn_sig);
 
     let arg_tys = fn_sig.inputs.as_slice();
     let ret_ty = fn_sig.output;
@@ -740,6 +738,11 @@ fn check_method_body(ccx: &CrateCtxt,
     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, body_id, &ty::bind(fty)).value;
+    debug!("fty (liberated): {}", fty.repr(ccx.tcx));
 
     check_bare_fn(ccx,
                   &*method.pe_fn_decl(),
@@ -784,7 +787,7 @@ fn check_impl_items_against_trait(ccx: &CrateCtxt,
                                                     impl_method.span,
                                                     impl_method.pe_body().id,
                                                     &**trait_method_ty,
-                                                    &impl_trait_ref.substs);
+                                                    impl_trait_ref);
                             }
                             _ => {
                                 // This is span_bug as it should have already been
@@ -929,11 +932,36 @@ fn compare_impl_method(tcx: &ty::ctxt,
                        impl_m_span: Span,
                        impl_m_body_id: ast::NodeId,
                        trait_m: &ty::Method,
-                       trait_to_impl_substs: &subst::Substs) {
-    debug!("compare_impl_method(trait_to_impl_substs={})",
-           trait_to_impl_substs.repr(tcx));
+                       impl_trait_ref: &ty::TraitRef) {
+    debug!("compare_impl_method(impl_trait_ref={})",
+           impl_trait_ref.repr(tcx));
+
+    // 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_id, impl_trait_ref);
+
+    debug!("impl_trait_ref (liberated) = {}",
+           impl_trait_ref.repr(tcx));
+
     let infcx = infer::new_infer_ctxt(tcx);
 
+    let trait_to_impl_substs = &impl_trait_ref.substs;
+
     // Try to give more informative error messages about self typing
     // mismatches.  Note that any mismatch will also be detected
     // below, where we construct a canonical function type that
@@ -997,22 +1025,23 @@ fn compare_impl_method(tcx: &ty::ctxt,
 
     // This code is best explained by example. Consider a trait:
     //
-    //     trait Trait<T> {
-    //          fn method<'a,M>(t: T, m: &'a M) -> Self;
+    //     trait Trait<'t,T> {
+    //          fn method<'a,M>(t: &'t T, m: &'a M) -> Self;
     //     }
     //
     // And an impl:
     //
-    //     impl<'i, U> Trait<&'i U> for Foo {
-    //          fn method<'b,N>(t: &'i U, m: &'b N) -> Foo;
+    //     impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
+    //          fn method<'b,N>(t: &'j &'i U, m: &'b N) -> Foo;
     //     }
     //
     // We wish to decide if those two method types are compatible.
     //
-    // We start out with trait_to_impl_substs, that maps the trait type
-    // parameters to impl type parameters:
+    // We start out with trait_to_impl_substs, that maps the trait
+    // type parameters to impl type parameters. This is taken from the
+    // impl trait reference:
     //
-    //     trait_to_impl_substs = {T => &'i U, Self => Foo}
+    //     trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
     //
     // We create a mapping `dummy_substs` that maps from the impl type
     // parameters to fresh types and regions. For type parameters,
@@ -1067,6 +1096,7 @@ fn compare_impl_method(tcx: &ty::ctxt,
     if !check_region_bounds_on_impl_method(tcx,
                                            impl_m_span,
                                            impl_m,
+                                           impl_m_body_id,
                                            &trait_m.generics,
                                            &impl_m.generics,
                                            &trait_to_skol_substs,
@@ -1074,15 +1104,50 @@ fn compare_impl_method(tcx: &ty::ctxt,
         return;
     }
 
-    // Check bounds.
-    let it = trait_m.generics.types.get_slice(subst::FnSpace).iter()
-        .zip(impl_m.generics.types.get_slice(subst::FnSpace).iter());
-    for (i, (trait_param_def, impl_param_def)) in it.enumerate() {
+    // Check bounds. Note that the bounds from the impl may reference
+    // late-bound regions declared on the impl, so liberate those.
+    // This requires two artificial binding scopes -- one for the impl,
+    // and one for the method.
+    //
+    // An example would be:
+    //
+    //     trait Foo<T> { fn method<U:Bound<T>>() { ... } }
+    //
+    //     impl<'a> Foo<&'a T> for &'a U {
+    //         fn method<U:Bound<&'a T>>() { ... }
+    //     }
+    //
+    // Here, the region parameter `'a` is late-bound, so in the bound
+    // `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
+    // the method.
+    //
+    // The only late-bounded regions that can possibly appear here are
+    // from the impl, not the method. This is because region
+    // parameters declared on the method which appear in a type bound
+    // would be early bound. On the trait side, there can be no
+    // late-bound lifetimes because trait definitions do not introduce
+    // a late region binder.
+    let trait_bounds =
+        trait_m.generics.types.get_slice(subst::FnSpace).iter()
+        .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_id,
+                 &ty::bind(ty::bind(impl_param_def.bounds.clone()))).value.value);
+    for (i, (trait_param_bounds, impl_param_bounds)) in
+        trait_bounds.zip(impl_bounds).enumerate()
+    {
         // Check that the impl does not require any builtin-bounds
         // that the trait does not guarantee:
         let extra_bounds =
-            impl_param_def.bounds.builtin_bounds -
-            trait_param_def.bounds.builtin_bounds;
+            impl_param_bounds.builtin_bounds -
+            trait_param_bounds.builtin_bounds;
         if !extra_bounds.is_empty() {
             span_err!(tcx.sess, impl_m_span, E0051,
                 "in method `{}`, type parameter {} requires `{}`, \
@@ -1099,31 +1164,32 @@ fn compare_impl_method(tcx: &ty::ctxt,
         //
         // FIXME(pcwalton): We could be laxer here regarding sub- and super-
         // traits, but I doubt that'll be wanted often, so meh.
-        for impl_trait_bound in impl_param_def.bounds.trait_bounds.iter() {
+        for impl_trait_bound in impl_param_bounds.trait_bounds.iter() {
             debug!("compare_impl_method(): impl-trait-bound subst");
             let impl_trait_bound =
                 impl_trait_bound.subst(tcx, &impl_to_skol_substs);
 
-            let mut ok = false;
-            for trait_bound in trait_param_def.bounds.trait_bounds.iter() {
-                debug!("compare_impl_method(): trait-bound subst");
-                let trait_bound =
-                    trait_bound.subst(tcx, &trait_to_skol_substs);
-                let infcx = infer::new_infer_ctxt(tcx);
-                match infer::mk_sub_trait_refs(&infcx,
-                                               true,
-                                               infer::Misc(impl_m_span),
-                                               trait_bound,
-                                               impl_trait_bound.clone()) {
-                    Ok(_) => {
-                        ok = true;
-                        break
-                    }
-                    Err(_) => continue,
-                }
-            }
+            // There may be late-bound regions from the impl in the
+            // impl's bound, so "liberate" those. Note that the
+            // trait_to_skol_substs is derived from the impl's
+            // trait-ref, and the late-bound regions appearing there
+            // have already been liberated, so the result should match
+            // up.
+
+            let found_match_in_trait =
+                trait_param_bounds.trait_bounds.iter().any(|trait_bound| {
+                    debug!("compare_impl_method(): trait-bound subst");
+                    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()
+                });
 
-            if !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",
@@ -1134,9 +1200,11 @@ fn compare_impl_method(tcx: &ty::ctxt,
         }
     }
 
-    // Compute skolemized form of impl and trait method tys.
+    // Compute skolemized form of impl and trait method tys. Note
+    // that we must liberate the late-bound regions from the impl.
     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_id, &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);
 
@@ -1171,6 +1239,7 @@ fn compare_impl_method(tcx: &ty::ctxt,
     fn check_region_bounds_on_impl_method(tcx: &ty::ctxt,
                                           span: Span,
                                           impl_m: &ty::Method,
+                                          impl_m_body_id: ast::NodeId,
                                           trait_generics: &ty::Generics,
                                           impl_generics: &ty::Generics,
                                           trait_to_skol_substs: &Substs,
@@ -1216,9 +1285,13 @@ fn compare_impl_method(tcx: &ty::ctxt,
 
         debug!("check_region_bounds_on_impl_method: \
                trait_generics={} \
-               impl_generics={}",
+               impl_generics={} \
+               trait_to_skol_substs={} \
+               impl_to_skol_substs={}",
                trait_generics.repr(tcx),
-               impl_generics.repr(tcx));
+               impl_generics.repr(tcx),
+               trait_to_skol_substs.repr(tcx),
+               impl_to_skol_substs.repr(tcx));
 
         // Must have same number of early-bound lifetime parameters.
         // Unfortunately, if the user screws up the bounds, then this
@@ -1249,6 +1322,18 @@ fn compare_impl_method(tcx: &ty::ctxt,
             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_id,
+                    &ty::bind(ty::bind(impl_bounds))).value.value;
+
             debug!("check_region_bounds_on_impl_method: \
                    trait_param={} \
                    impl_param={} \
@@ -1603,15 +1688,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub fn write_ty_substs(&self,
-                           node_id: ast::NodeId,
-                           ty: ty::t,
-                           substs: ty::ItemSubsts) {
-        let ty = ty.subst(self.tcx(), &substs.substs);
-        self.write_ty(node_id, ty);
-        self.write_substs(node_id, substs);
-    }
-
     pub fn write_autoderef_adjustment(&self,
                                       node_id: ast::NodeId,
                                       span: Span,
@@ -1709,17 +1785,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub fn instantiate_item_type(&self,
-                                 span: Span,
-                                 def_id: ast::DefId)
-                                 -> TypeAndSubsts
+    pub fn instantiate_type(&self,
+                            span: Span,
+                            def_id: ast::DefId)
+                            -> TypeAndSubsts
     {
         /*!
          * Returns the type of `def_id` with all generics replaced by
          * by fresh type/region variables. Also returns the
          * substitution from the type parameters on `def_id` to the
-         * fresh variables.  Registers any trait obligations specified
+         * fresh variables. Registers any trait obligations specified
          * on `def_id` at the same time.
+         *
+         * Note that function is only intended to be used with types
+         * (notably, not impls). This is because it doesn't do any
+         * instantiation of late-bound regions.
          */
 
         let polytype =
@@ -1728,12 +1808,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.infcx().fresh_substs_for_generics(
                 span,
                 &polytype.generics);
+        let bounds =
+            polytype.generics.to_bounds(self.tcx(), &substs);
         self.add_obligations_for_parameters(
             traits::ObligationCause::new(
                 span,
                 traits::ItemObligation(def_id)),
             &substs,
-            &polytype.generics);
+            &bounds);
         let monotype =
             polytype.ty.subst(self.tcx(), &substs);
 
@@ -1958,8 +2040,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let mut region_obligations = self.inh.region_obligations.borrow_mut();
         let region_obligation = RegionObligation { sub_region: r,
-                                  sup_type: ty,
-                                  origin: origin };
+                                                   sup_type: ty,
+                                                   origin: origin };
 
         match region_obligations.entry(self.body_id) {
             Vacant(entry) => { entry.set(vec![region_obligation]); },
@@ -1970,12 +2052,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn add_obligations_for_parameters(&self,
                                           cause: traits::ObligationCause,
                                           substs: &Substs,
-                                          generics: &ty::Generics)
+                                          generic_bounds: &ty::GenericBounds)
     {
         /*!
-         * Given a set of generic parameter definitions (`generics`)
-         * and the values provided for each of them (`substs`),
-         * creates and registers suitable region obligations.
+         * Given a fully substituted set of bounds (`generic_bounds`),
+         * and the values with which each type/region parameter was
+         * instantiated (`substs`), creates and registers suitable
+         * trait/region obligations.
          *
          * For example, if there is a function:
          *
@@ -1991,60 +2074,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
          * locally.
          */
 
-        debug!("add_obligations_for_parameters(substs={}, generics={})",
+        assert!(!generic_bounds.has_escaping_regions());
+
+        debug!("add_obligations_for_parameters(substs={}, generic_bounds={})",
                substs.repr(self.tcx()),
-               generics.repr(self.tcx()));
+               generic_bounds.repr(self.tcx()));
 
-        self.add_trait_obligations_for_generics(cause, substs, generics);
-        self.add_region_obligations_for_generics(cause, substs, generics);
+        self.add_trait_obligations_for_generics(cause, substs, generic_bounds);
+        self.add_region_obligations_for_generics(cause, substs, generic_bounds);
     }
 
     fn add_trait_obligations_for_generics(&self,
                                           cause: traits::ObligationCause,
                                           substs: &Substs,
-                                          generics: &ty::Generics) {
+                                          generic_bounds: &ty::GenericBounds) {
+        assert!(!generic_bounds.has_escaping_regions());
+        assert!(!substs.has_regions_escaping_depth(0));
+
         let obligations =
             traits::obligations_for_generics(self.tcx(),
                                              cause,
-                                             generics,
-                                             substs);
+                                             generic_bounds,
+                                             &substs.types);
         obligations.map_move(|o| self.register_obligation(o));
     }
 
     fn add_region_obligations_for_generics(&self,
                                            cause: traits::ObligationCause,
                                            substs: &Substs,
-                                           generics: &ty::Generics)
+                                           generic_bounds: &ty::GenericBounds)
     {
-        assert_eq!(generics.types.iter().len(),
-                   substs.types.iter().len());
-        for (type_def, &type_param) in
-            generics.types.iter().zip(
+        assert!(!generic_bounds.has_escaping_regions());
+        assert_eq!(generic_bounds.types.iter().len(), substs.types.iter().len());
+
+        for (type_bounds, &type_param) in
+            generic_bounds.types.iter().zip(
                 substs.types.iter())
         {
-            let param_ty = ty::ParamTy { space: type_def.space,
-                                         idx: type_def.index,
-                                         def_id: type_def.def_id };
-            let bounds = type_def.bounds.subst(self.tcx(), substs);
             self.add_region_obligations_for_type_parameter(
-                cause.span, param_ty, &bounds, type_param);
+                cause.span, type_bounds, type_param);
         }
 
-        assert_eq!(generics.regions.iter().len(),
+        assert_eq!(generic_bounds.regions.iter().len(),
                    substs.regions().iter().len());
-        for (region_def, &region_param) in
-            generics.regions.iter().zip(
+        for (region_bounds, &region_param) in
+            generic_bounds.regions.iter().zip(
                 substs.regions().iter())
         {
-            let bounds = region_def.bounds.subst(self.tcx(), substs);
             self.add_region_obligations_for_region_parameter(
-                cause.span, bounds.as_slice(), region_param);
+                cause.span, region_bounds.as_slice(), region_param);
         }
     }
 
     fn add_region_obligations_for_type_parameter(&self,
                                                  span: Span,
-                                                 param_ty: ty::ParamTy,
                                                  param_bound: &ty::ParamBounds,
                                                  ty: ty::t)
     {
@@ -2056,7 +2139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 param_bound.builtin_bounds,
                 param_bound.trait_bounds.as_slice());
         for &r in region_bounds.iter() {
-            let origin = infer::RelateParamBound(span, param_ty, ty);
+            let origin = infer::RelateParamBound(span, ty);
             self.register_region_obligation(origin, ty, r);
         }
     }
@@ -2181,11 +2264,11 @@ fn try_overloaded_call<'a>(fcx: &FnCtxt,
         fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
         write_call(fcx, call_expression, output_type);
 
-        if !fcx.tcx().sess.features.borrow().overloaded_calls {
+        if !fcx.tcx().sess.features.borrow().unboxed_closures {
             span_err!(fcx.tcx().sess, call_expression.span, E0056,
                 "overloaded calls are experimental");
             span_help!(fcx.tcx().sess, call_expression.span,
-                "add `#![feature(overloaded_calls)]` to \
+                "add `#![feature(unboxed_closures)]` to \
                 the crate attributes to enable");
         }
 
@@ -3031,7 +3114,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
         // In that case, we check each argument against "error" in order to
         // set up all the node type bindings.
         let error_fn_sig = FnSig {
-            binder_id: ast::CRATE_NODE_ID,
             inputs: err_args(args.len()),
             output: ty::FnConverging(ty::mk_err()),
             variadic: false
@@ -3051,11 +3133,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
         // Replace any bound regions that appear in the function
         // signature with region variables
         let fn_sig =
-            fcx.infcx().replace_late_bound_regions_with_fresh_var(
-                fn_sig.binder_id,
-                call_expr.span,
-                infer::FnCall,
-                fn_sig).0;
+            fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span,
+                                                                  infer::FnCall,
+                                                                  fn_sig).0;
 
         // Call the generic checker.
         check_argument_types(fcx,
@@ -3437,7 +3517,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                              body: &ast::Block) {
         let mut fn_ty = astconv::ty_of_closure(
             fcx,
-            expr.id,
             ast::NormalFn,
             ast::Many,
 
@@ -3508,6 +3587,10 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                      expected: Expectation) {
         let tcx = fcx.ccx.tcx;
 
+        debug!("check_expr_fn(expr={}, expected={})",
+               expr.repr(tcx),
+               expected.repr(tcx));
+
         // Find the expected input/output types (if any). Substitute
         // fresh bound regions for any bound regions we find in the
         // expected types so as to avoid capture.
@@ -3517,10 +3600,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
              expected_bounds) = {
             match expected_sty {
                 Some(ty::ty_closure(ref cenv)) => {
-                    let (_, sig) =
+                    let (sig, _) =
                         replace_late_bound_regions(
-                            tcx, cenv.sig.binder_id, &cenv.sig,
-                            |_| fcx.inh.infcx.fresh_bound_region(expr.id));
+                            tcx,
+                            &cenv.sig,
+                            |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn));
                     let onceness = match (&store, &cenv.store) {
                         // As the closure type and onceness go, only three
                         // combinations are legit:
@@ -3561,7 +3645,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
 
         // construct the function type
         let fn_ty = astconv::ty_of_closure(fcx,
-                                           expr.id,
                                            ast::NormalFn,
                                            expected_onceness,
                                            expected_bounds,
@@ -3818,7 +3901,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
         let TypeAndSubsts {
             ty: mut struct_type,
             substs: struct_substs
-        } = fcx.instantiate_item_type(span, class_id);
+        } = fcx.instantiate_type(span, class_id);
 
         // Look up and check the fields.
         let class_fields = ty::lookup_struct_fields(tcx, class_id);
@@ -3860,7 +3943,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
         let TypeAndSubsts {
             ty: enum_type,
             substs: substitutions
-        } = fcx.instantiate_item_type(span, enum_id);
+        } = fcx.instantiate_type(span, enum_id);
 
         // Look up and check the enum variant fields.
         let variant_fields = ty::lookup_struct_fields(tcx, variant_id);
@@ -4454,8 +4537,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
             let type_and_substs = astconv::ast_path_to_ty_relaxed(fcx,
                                                                   fcx.infcx(),
                                                                   struct_id,
-                                                                  path,
-                                                                  expr.id);
+                                                                  path);
             match fcx.mk_subty(false,
                                infer::Misc(path.span),
                                actual_structure_type,
@@ -5339,14 +5421,39 @@ pub fn instantiate_path(fcx: &FnCtxt,
         assert_eq!(substs.regions().len(space), region_defs.len(space));
     }
 
+    // The things we are substituting into the type should not contain
+    // escaping late-bound regions.
+    assert!(!substs.has_regions_escaping_depth(0));
+
+    // In the case of static items taken from impls, there may be
+    // late-bound regions associated with the impl (not declared on
+    // the fn itself). Those should be replaced with fresh variables
+    // now. These can appear either on the type being referenced, or
+    // on the associated bounds.
+    let bounds = polytype.generics.to_bounds(fcx.tcx(), &substs);
+    let (ty_late_bound, bounds) =
+        fcx.infcx().replace_late_bound_regions_with_fresh_var(
+            span,
+            infer::FnCall,
+            &ty::bind((polytype.ty, bounds))).0.value;
+
+    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()));
+
     fcx.add_obligations_for_parameters(
         traits::ObligationCause::new(span, traits::ItemObligation(def.def_id())),
         &substs,
-        &polytype.generics);
+        &bounds);
 
-    fcx.write_ty_substs(node_id, polytype.ty, ty::ItemSubsts {
-        substs: substs,
-    });
+    // Substitute the values for the type parameters into the type of
+    // the referenced item.
+    let ty_substituted = ty_late_bound.subst(fcx.tcx(), &substs);
+
+    debug!("ty_substituted: ty_substituted={}", ty_substituted.repr(fcx.tcx()));
+
+    fcx.write_ty(node_id, ty_substituted);
+    fcx.write_substs(node_id, ty::ItemSubsts { substs: substs });
+    return;
 
     fn report_error_if_segment_contains_type_parameters(
         fcx: &FnCtxt,
@@ -5739,7 +5846,8 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
             "move_val_init" => {
                 (1u,
                  vec!(
-                    ty::mk_mut_rptr(tcx, ty::ReLateBound(it.id, ty::BrAnon(0)), param(ccx, 0)),
+                    ty::mk_mut_rptr(tcx, ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrAnon(0)),
+                                    param(ccx, 0)),
                     param(ccx, 0u)
                   ),
                ty::mk_nil(tcx))
@@ -5943,7 +6051,6 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
         fn_style: ast::UnsafeFn,
         abi: abi::RustIntrinsic,
         sig: FnSig {
-            binder_id: it.id,
             inputs: inputs,
             output: output,
             variadic: false,
diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs
index b7710ab7bf9..2e727a8ef9a 100644
--- a/src/librustc/middle/typeck/check/regionmanip.rs
+++ b/src/librustc/middle/typeck/check/regionmanip.rs
@@ -14,47 +14,14 @@ pub use self::WfConstraint::*;
 
 use middle::subst::{ParamSpace, Subst, Substs};
 use middle::ty;
-use middle::ty_fold;
-use middle::ty_fold::{TypeFolder, TypeFoldable};
+use middle::ty_fold::{TypeFolder};
 
 use syntax::ast;
 
-use std::collections::hash_map::{Occupied, Vacant};
-use util::nodemap::FnvHashMap;
 use util::ppaux::Repr;
 
 // Helper functions related to manipulating region types.
 
-pub fn replace_late_bound_regions<T>(
-    tcx: &ty::ctxt,
-    binder_id: ast::NodeId,
-    value: &T,
-    map_fn: |ty::BoundRegion| -> ty::Region)
-    -> (FnvHashMap<ty::BoundRegion,ty::Region>, T)
-    where T : TypeFoldable + Repr
-{
-    debug!("replace_late_bound_regions(binder_id={}, value={})",
-           binder_id, value.repr(tcx));
-
-    let mut map = FnvHashMap::new();
-    let new_value = {
-        let mut folder = ty_fold::RegionFolder::regions(tcx, |r| {
-            match r {
-                ty::ReLateBound(s, br) if s == binder_id => {
-                    match map.entry(br) {
-                        Vacant(entry) => *entry.set(map_fn(br)),
-                        Occupied(entry) => *entry.into_mut(),
-                    }
-                }
-                _ => r
-            }
-        });
-        value.fold_with(&mut folder)
-    };
-    debug!("resulting map: {}", map);
-    (map, new_value)
-}
-
 pub enum WfConstraint {
     RegionSubRegionConstraint(Option<ty::t>, ty::Region, ty::Region),
     RegionSubParamConstraint(Option<ty::t>, ty::Region, ty::ParamTy),
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index 498594716e7..041d21a8baf 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -300,14 +300,14 @@ pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
 }
 
 fn resolve_trait_ref(fcx: &FnCtxt, obligation: &Obligation)
-                     -> (ty::TraitRef, ty::t)
+                     -> (Rc<ty::TraitRef>, ty::t)
 {
     let trait_ref =
         fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
             &*obligation.trait_ref);
     let self_ty =
         trait_ref.substs.self_ty().unwrap();
-    (trait_ref, self_ty)
+    (Rc::new(trait_ref), self_ty)
 }
 
 pub fn report_fulfillment_errors(fcx: &FnCtxt,
diff --git a/src/librustc/middle/typeck/check/wf.rs b/src/librustc/middle/typeck/check/wf.rs
index b3449d658f6..8e02f9f7bfd 100644
--- a/src/librustc/middle/typeck/check/wf.rs
+++ b/src/librustc/middle/typeck/check/wf.rs
@@ -12,10 +12,10 @@ use middle::subst;
 use middle::subst::{Subst};
 use middle::traits;
 use middle::ty;
+use middle::ty::liberate_late_bound_regions;
 use middle::ty_fold::{TypeFolder, TypeFoldable};
 use middle::typeck::astconv::AstConv;
 use middle::typeck::check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck};
-use middle::typeck::check::regionmanip::replace_late_bound_regions;
 use middle::typeck::CrateCtxt;
 use util::ppaux::Repr;
 
@@ -166,16 +166,24 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
             let mut bounds_checker = BoundsChecker::new(fcx, item.span,
                                                         item.id, Some(&mut this.cache));
 
+            // 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.
             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.id, &ty::bind(self_ty)).value;
 
             bounds_checker.check_traits_in_ty(self_ty);
 
+            // Similarly, obtain an "inside" reference to the trait
+            // that the impl implements.
             let trait_ref = match ty::impl_trait_ref(fcx.tcx(), local_def(item.id)) {
                 None => { return; }
                 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.id, &trait_ref);
 
             // There are special rules that apply to drop.
             if
@@ -215,7 +223,6 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
             // FIXME -- This is a bit ill-factored. There is very similar
             // code in traits::util::obligations_for_generics.
             fcx.add_region_obligations_for_type_parameter(item.span,
-                                                          ty::ParamTy::for_self(trait_ref.def_id),
                                                           &trait_def.bounds,
                                                           trait_ref.self_ty());
             for builtin_bound in trait_def.bounds.builtin_bounds.iter() {
@@ -280,12 +287,13 @@ impl<'cx,'tcx> BoundsChecker<'cx,'tcx> {
 
         let trait_def = ty::lookup_trait_def(self.fcx.tcx(), trait_ref.def_id);
 
+        let bounds = trait_def.generics.to_bounds(self.tcx(), &trait_ref.substs);
         self.fcx.add_obligations_for_parameters(
             traits::ObligationCause::new(
                 self.span,
                 traits::ItemObligation(trait_ref.def_id)),
             &trait_ref.substs,
-            &trait_def.generics);
+            &bounds);
 
         for &ty in trait_ref.substs.types.iter() {
             self.check_traits_in_ty(ty);
@@ -335,7 +343,7 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
                         traits::ObligationCause::new(self.span,
                                                      traits::ItemObligation(type_id)),
                         substs,
-                        &polytype.generics);
+                        &polytype.generics.to_bounds(self.tcx(), substs));
                 } else {
                     // There are two circumstances in which we ignore
                     // region obligations.
@@ -363,7 +371,7 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
                         traits::ObligationCause::new(self.span,
                                                      traits::ItemObligation(type_id)),
                         substs,
-                        &polytype.generics);
+                        &polytype.generics.to_bounds(self.tcx(), substs));
                 }
 
                 self.fold_substs(substs);
@@ -372,16 +380,12 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
             ty::ty_closure(box ty::ClosureTy{sig: ref fn_sig, ..}) => {
                 self.binding_count += 1;
 
-                let (_, fn_sig) =
-                    replace_late_bound_regions(
-                        self.fcx.tcx(), fn_sig.binder_id, fn_sig,
-                        |br| ty::ReFree(ty::FreeRegion{scope_id: self.scope_id,
-                                                       bound_region: br}));
+                let fn_sig = liberate_late_bound_regions(self.fcx.tcx(), self.scope_id, fn_sig);
 
                 debug!("late-bound regions replaced: {}",
                        fn_sig.repr(self.tcx()));
 
-                self.fold_sig(&fn_sig);
+                self.fold_fn_sig(&fn_sig);
 
                 self.binding_count -= 1;
             }
diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs
index a5ca5179f08..a0b198a59c2 100644
--- a/src/librustc/middle/typeck/collect.rs
+++ b/src/librustc/middle/typeck/collect.rs
@@ -214,12 +214,11 @@ pub fn get_enum_variant_types(ccx: &CrateCtxt,
     for variant in variants.iter() {
         // Nullary enum constructors get turned into constants; n-ary enum
         // constructors get turned into functions.
-        let scope = variant.node.id;
         let result_ty = match variant.node.kind {
             ast::TupleVariantKind(ref args) if args.len() > 0 => {
                 let rs = ExplicitRscope;
                 let input_tys: Vec<_> = args.iter().map(|va| ccx.to_ty(&rs, &*va.ty)).collect();
-                ty::mk_ctor_fn(tcx, scope, input_tys.as_slice(), enum_ty)
+                ty::mk_ctor_fn(tcx, input_tys.as_slice(), enum_ty)
             }
 
             ast::TupleVariantKind(_) => {
@@ -403,7 +402,6 @@ fn collect_trait_methods(ccx: &CrateCtxt,
             let trait_self_ty = ty::mk_self_type(tmcx.tcx(),
                                                  local_def(trait_id));
             astconv::ty_of_method(&tmcx,
-                                  *m_id,
                                   *m_fn_style,
                                   trait_self_ty,
                                   m_explicit_self,
@@ -588,7 +586,6 @@ fn convert_methods<'a,I>(ccx: &CrateCtxt,
                     method_generics: &m_ty_generics,
                 };
                 astconv::ty_of_method(&imcx,
-                                      m.id,
                                       m.pe_fn_style(),
                                       untransformed_rcvr_ty,
                                       m.pe_explicit_self(),
@@ -603,7 +600,6 @@ fn convert_methods<'a,I>(ccx: &CrateCtxt,
                     method_generics: &m_ty_generics,
                 };
                 astconv::ty_of_method(&tmcx,
-                                      m.id,
                                       m.pe_fn_style(),
                                       untransformed_rcvr_ty,
                                       m.pe_explicit_self(),
@@ -1116,10 +1112,12 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) {
             for impl_item in impl_items.iter() {
                 match *impl_item {
                     ast::MethodImplItem(ref method) => {
+                        let body_id = method.pe_body().id;
                         check_method_self_type(ccx,
-                                               &BindingRscope::new(method.id),
+                                               &BindingRscope::new(),
                                                selfty,
-                                               method.pe_explicit_self());
+                                               method.pe_explicit_self(),
+                                               body_id);
                         methods.push(&**method);
                     }
                     ast::TypeImplItem(ref typedef) => {
@@ -1174,17 +1172,19 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) {
                                              local_def(it.id));
                 match *trait_method {
                     ast::RequiredMethod(ref type_method) => {
-                        let rscope = BindingRscope::new(type_method.id);
+                        let rscope = BindingRscope::new();
                         check_method_self_type(ccx,
                                                &rscope,
                                                self_type,
-                                               &type_method.explicit_self)
+                                               &type_method.explicit_self,
+                                               it.id)
                     }
                     ast::ProvidedMethod(ref method) => {
                         check_method_self_type(ccx,
-                                               &BindingRscope::new(method.id),
+                                               &BindingRscope::new(),
                                                self_type,
-                                               method.pe_explicit_self())
+                                               method.pe_explicit_self(),
+                                               it.id)
                     }
                     ast::TypeTraitItem(ref associated_type) => {
                         convert_associated_type(ccx,
@@ -1294,7 +1294,6 @@ pub fn convert_struct(ccx: &CrateCtxt,
                         |field| (*tcx.tcache.borrow())[
                             local_def(field.node.id)].ty).collect();
                 let ctor_fn_ty = ty::mk_ctor_fn(tcx,
-                                                ctor_id,
                                                 inputs.as_slice(),
                                                 selfty);
                 write_ty_to_tcx(tcx, ctor_id, ctor_fn_ty);
@@ -1465,11 +1464,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::Item)
                     ccx: ccx,
                     generics: &ty_generics,
                 };
-                astconv::ty_of_bare_fn(&fcx,
-                                       it.id,
-                                       fn_style,
-                                       abi,
-                                       &**decl)
+                astconv::ty_of_bare_fn(&fcx, fn_style, abi, &**decl)
             };
             let pty = Polytype {
                 generics: ty_generics,
@@ -2015,12 +2010,12 @@ fn conv_param_bounds<'tcx,AC>(this: &AC,
         astconv::partition_bounds(this.tcx(), span, all_bounds.as_slice());
     let trait_bounds: Vec<Rc<ty::TraitRef>> =
         trait_bounds.into_iter()
-        .map(|b| {
-            astconv::instantiate_trait_ref(this,
-                                           &ExplicitRscope,
-                                           b,
-                                           Some(param_ty.to_ty(this.tcx())),
-                                           Some(param_ty.to_ty(this.tcx())))
+        .map(|bound| {
+            astconv::instantiate_poly_trait_ref(this,
+                                                &ExplicitRscope,
+                                                bound,
+                                                Some(param_ty.to_ty(this.tcx())),
+                                                Some(param_ty.to_ty(this.tcx())))
         })
         .collect();
     let region_bounds: Vec<ty::Region> =
@@ -2091,7 +2086,7 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
             ast_generics,
             ty::Generics::empty(),
             DontCreateTypeParametersForAssociatedTypes);
-    let rb = BindingRscope::new(def_id.node);
+    let rb = BindingRscope::new();
     let input_tys = decl.inputs
                         .iter()
                         .map(|a| ty_of_arg(ccx, &rb, a, None))
@@ -2109,8 +2104,7 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
         ty::BareFnTy {
             abi: abi,
             fn_style: ast::UnsafeFn,
-            sig: ty::FnSig {binder_id: def_id.node,
-                            inputs: input_tys,
+            sig: ty::FnSig {inputs: input_tys,
                             output: output,
                             variadic: decl.variadic}
         });
@@ -2142,10 +2136,12 @@ pub fn mk_item_substs(ccx: &CrateCtxt,
 /// Verifies that the explicit self type of a method matches the impl or
 /// trait.
 fn check_method_self_type<RS:RegionScope>(
-                          crate_context: &CrateCtxt,
-                          rs: &RS,
-                          required_type: ty::t,
-                          explicit_self: &ast::ExplicitSelf) {
+    crate_context: &CrateCtxt,
+    rs: &RS,
+    required_type: ty::t,
+    explicit_self: &ast::ExplicitSelf,
+    body_id: ast::NodeId)
+{
     match explicit_self.node {
         ast::SelfExplicit(ref ast_type, _) => {
             let typ = crate_context.to_ty(rs, &**ast_type);
@@ -2154,13 +2150,44 @@ fn check_method_self_type<RS:RegionScope>(
                 ty::ty_uniq(typ) => typ,
                 _ => typ,
             };
+
+            // "Required type" comes from the trait definition. It may
+            // 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));
+            let required_type_free =
+                ty::liberate_late_bound_regions(
+                    crate_context.tcx,
+                    body_id,
+                    &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_id,
+                    &ty::bind(ty::bind(base_type))).value.value;
+            let base_type_free = // liberate method regions:
+                ty::liberate_late_bound_regions(
+                    crate_context.tcx,
+                    body_id,
+                    &ty::bind(base_type_free)).value;
+
+            debug!("required_type={} required_type_free={} \
+                    base_type={} base_type_free={}",
+                   required_type.repr(crate_context.tcx),
+                   required_type_free.repr(crate_context.tcx),
+                   base_type.repr(crate_context.tcx),
+                   base_type_free.repr(crate_context.tcx));
             let infcx = infer::new_infer_ctxt(crate_context.tcx);
             drop(typeck::require_same_types(crate_context.tcx,
                                             Some(&infcx),
                                             false,
                                             explicit_self.span,
-                                            base_type,
-                                            required_type,
+                                            base_type_free,
+                                            required_type_free,
                                             || {
                 format!("mismatched self type: expected `{}`",
                         ppaux::ty_to_string(crate_context.tcx, required_type))
diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs
index 078a2c10bcb..0f9554cd417 100644
--- a/src/librustc/middle/typeck/infer/combine.rs
+++ b/src/librustc/middle/typeck/infer/combine.rs
@@ -59,6 +59,7 @@ use syntax::codemap::Span;
 
 pub trait Combine<'tcx> {
     fn infcx<'a>(&'a self) -> &'a InferCtxt<'a, 'tcx>;
+    fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.infcx().tcx }
     fn tag(&self) -> String;
     fn a_is_expected(&self) -> bool;
     fn trace(&self) -> TypeTrace;
@@ -296,26 +297,14 @@ pub trait Combine<'tcx> {
                 Err(ty::terr_trait_stores_differ(vk, expected_found(self, a, b)))
             }
         }
-
     }
 
     fn trait_refs(&self,
                   a: &ty::TraitRef,
                   b: &ty::TraitRef)
-                  -> cres<ty::TraitRef> {
-        // Different traits cannot be related
-
-        // - NOTE in the future, expand out subtraits!
-
-        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 })
-        }
-    }
+                  -> cres<ty::TraitRef>;
+    // this must be overridden to do correctly, so as to account for higher-ranked
+    // behavior
 }
 
 #[deriving(Clone)]
@@ -334,48 +323,6 @@ pub fn expected_found<'tcx, C: Combine<'tcx>, T>(
     }
 }
 
-pub fn super_fn_sigs<'tcx, C: Combine<'tcx>>(this: &C,
-                                             a: &ty::FnSig,
-                                             b: &ty::FnSig)
-                                             -> cres<ty::FnSig> {
-
-    fn argvecs<'tcx, C: Combine<'tcx>>(this: &C,
-                                       a_args: &[ty::t],
-                                       b_args: &[ty::t])
-                                       -> cres<Vec<ty::t>> {
-        if a_args.len() == b_args.len() {
-            a_args.iter().zip(b_args.iter())
-                  .map(|(a, b)| this.args(*a, *b)).collect()
-        } else {
-            Err(ty::terr_arg_count)
-        }
-    }
-
-    if a.variadic != b.variadic {
-        return Err(ty::terr_variadic_mismatch(expected_found(this, a.variadic, b.variadic)));
-    }
-
-    let inputs = try!(argvecs(this,
-                                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!(this.tys(a_ty, b_ty)))),
-        (ty::FnDiverging, ty::FnDiverging) =>
-            Ok(ty::FnDiverging),
-        (a, b) =>
-            Err(ty::terr_convergence_mismatch(
-                expected_found(this, a != ty::FnDiverging, b != ty::FnDiverging)
-            )),
-    });
-
-    Ok(FnSig {binder_id: a.binder_id,
-              inputs: inputs,
-              output: output,
-              variadic: a.variadic})
-}
-
 pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
 
     let tcx = this.infcx().tcx;
diff --git a/src/librustc/middle/typeck/infer/equate.rs b/src/librustc/middle/typeck/infer/equate.rs
index 97453dc86ef..3874f5fc5e4 100644
--- a/src/librustc/middle/typeck/infer/equate.rs
+++ b/src/librustc/middle/typeck/infer/equate.rs
@@ -137,4 +137,9 @@ impl<'f, 'tcx> Combine<'tcx> for Equate<'f, 'tcx> {
         try!(self.sub().fn_sigs(a, b));
         self.sub().fn_sigs(b, a)
     }
+
+    fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres<ty::TraitRef> {
+        try!(self.sub().trait_refs(a, b));
+        self.sub().trait_refs(b, a)
+    }
 }
diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs
index 8c55872d962..65bd21b14e0 100644
--- a/src/librustc/middle/typeck/infer/error_reporting.rs
+++ b/src/librustc/middle/typeck/infer/error_reporting.rs
@@ -650,14 +650,12 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> {
                     sup,
                     "");
             }
-            infer::RelateParamBound(span, param_ty, ty) => {
+            infer::RelateParamBound(span, ty) => {
                 self.tcx.sess.span_err(
                     span,
-                    format!("the type `{}` (provided as the value of \
-                             the parameter `{}`) does not fulfill the \
+                    format!("the type `{}` does not fulfill the \
                              required lifetime",
-                            self.ty_to_string(ty),
-                            param_ty.user_string(self.tcx)).as_slice());
+                            self.ty_to_string(ty)).as_slice());
                 note_and_explain_region(self.tcx,
                                         "type must outlive ",
                                         sub,
@@ -1460,8 +1458,8 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> {
                 format!(" for {}in function call",
                         bound_region_to_string(self.tcx, "lifetime parameter ", true, br))
             }
-            infer::LateBoundRegion(_, br, infer::FnType) => {
-                format!(" for {}in function type",
+            infer::LateBoundRegion(_, br, infer::HigherRankedType) => {
+                format!(" for {}in generic type",
                         bound_region_to_string(self.tcx, "lifetime parameter ", true, br))
             }
             infer::EarlyBoundRegion(_, name) => {
@@ -1651,13 +1649,11 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> {
                              does not outlive the data it points at",
                             self.ty_to_string(ty)).as_slice());
             }
-            infer::RelateParamBound(span, param_ty, t) => {
+            infer::RelateParamBound(span, t) => {
                 self.tcx.sess.span_note(
                     span,
-                    format!("...so that the parameter `{}`, \
-                             when instantiated with `{}`, \
-                             will meet its declared lifetime bounds.",
-                            param_ty.user_string(self.tcx),
+                    format!("...so that the type `{}` \
+                             will meet the declared lifetime bounds.",
                             self.ty_to_string(t)).as_slice());
             }
             infer::RelateDefaultParamBound(span, t) => {
diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs
index 4ff4857ea06..287a5cfba9e 100644
--- a/src/librustc/middle/typeck/infer/glb.rs
+++ b/src/librustc/middle/typeck/infer/glb.rs
@@ -10,23 +10,18 @@
 
 
 use middle::ty::{BuiltinBounds};
-use middle::ty::RegionVid;
 use middle::ty;
 use middle::typeck::infer::combine::*;
 use middle::typeck::infer::lattice::*;
 use middle::typeck::infer::equate::Equate;
-use middle::typeck::infer::fold_regions_in_sig;
-use middle::typeck::infer::LateBoundRegionConversionTime::FnType;
+use middle::typeck::infer::higher_ranked::HigherRankedRelations;
 use middle::typeck::infer::lub::Lub;
-use middle::typeck::infer::region_inference::RegionMark;
 use middle::typeck::infer::sub::Sub;
 use middle::typeck::infer::{cres, InferCtxt};
 use middle::typeck::infer::{TypeTrace, Subtype};
 use syntax::ast::{Many, Once, MutImmutable, MutMutable};
-use syntax::ast::{NormalFn, UnsafeFn, NodeId};
+use syntax::ast::{NormalFn, UnsafeFn};
 use syntax::ast::{Onceness, FnStyle};
-use util::common::{indenter};
-use util::nodemap::FnvHashMap;
 use util::ppaux::mt_to_string;
 use util::ppaux::Repr;
 
@@ -128,139 +123,10 @@ impl<'f, 'tcx> Combine<'tcx> for Glb<'f, 'tcx> {
     }
 
     fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
-        // Note: this is a subtle algorithm.  For a full explanation,
-        // please see the large comment in `region_inference.rs`.
-
-        debug!("{}.fn_sigs({}, {})",
-               self.tag(), a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
-        let _indenter = indenter();
-
-        // Make a mark so we can examine "all bindings that were
-        // created as part of this type comparison".
-        let mark = self.fields.infcx.region_vars.mark();
-
-        // Instantiate each bound region with a fresh region variable.
-        let (a_with_fresh, a_map) =
-            self.fields.infcx.replace_late_bound_regions_with_fresh_var(
-                a.binder_id, self.trace().span(), FnType, a);
-        let a_vars = var_ids(self, &a_map);
-        let (b_with_fresh, b_map) =
-            self.fields.infcx.replace_late_bound_regions_with_fresh_var(
-                b.binder_id, self.trace().span(), FnType, b);
-        let b_vars = var_ids(self, &b_map);
-
-        // Collect constraints.
-        let sig0 = try!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh));
-        debug!("sig0 = {}", sig0.repr(self.fields.infcx.tcx));
-
-        // Generalize the regions appearing in fn_ty0 if possible
-        let new_vars =
-            self.fields.infcx.region_vars.vars_created_since_mark(mark);
-        let sig1 =
-            fold_regions_in_sig(
-                self.fields.infcx.tcx,
-                &sig0,
-                |r| {
-                generalize_region(self,
-                                  mark,
-                                  new_vars.as_slice(),
-                                  sig0.binder_id,
-                                  &a_map,
-                                  a_vars.as_slice(),
-                                  b_vars.as_slice(),
-                                  r)
-            });
-        debug!("sig1 = {}", sig1.repr(self.fields.infcx.tcx));
-        return Ok(sig1);
-
-        fn generalize_region(this: &Glb,
-                             mark: RegionMark,
-                             new_vars: &[RegionVid],
-                             new_binder_id: NodeId,
-                             a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
-                             a_vars: &[RegionVid],
-                             b_vars: &[RegionVid],
-                             r0: ty::Region) -> ty::Region {
-            if !is_var_in_set(new_vars, r0) {
-                assert!(!r0.is_bound());
-                return r0;
-            }
-
-            let tainted = this.fields.infcx.region_vars.tainted(mark, r0);
-
-            let mut a_r = None;
-            let mut b_r = None;
-            let mut only_new_vars = true;
-            for r in tainted.iter() {
-                if is_var_in_set(a_vars, *r) {
-                    if a_r.is_some() {
-                        return fresh_bound_variable(this, new_binder_id);
-                    } else {
-                        a_r = Some(*r);
-                    }
-                } else if is_var_in_set(b_vars, *r) {
-                    if b_r.is_some() {
-                        return fresh_bound_variable(this, new_binder_id);
-                    } else {
-                        b_r = Some(*r);
-                    }
-                } else if !is_var_in_set(new_vars, *r) {
-                    only_new_vars = false;
-                }
-            }
-
-            // NB---I do not believe this algorithm computes
-            // (necessarily) the GLB.  As written it can
-            // spuriously fail. In particular, if there is a case
-            // like: |fn(&a)| and fn(fn(&b)), where a and b are
-            // free, it will return fn(&c) where c = GLB(a,b).  If
-            // however this GLB is not defined, then the result is
-            // an error, even though something like
-            // "fn<X>(fn(&X))" where X is bound would be a
-            // subtype of both of those.
-            //
-            // The problem is that if we were to return a bound
-            // variable, we'd be computing a lower-bound, but not
-            // necessarily the *greatest* lower-bound.
-            //
-            // Unfortunately, this problem is non-trivial to solve,
-            // because we do not know at the time of computing the GLB
-            // whether a GLB(a,b) exists or not, because we haven't
-            // run region inference (or indeed, even fully computed
-            // the region hierarchy!). The current algorithm seems to
-            // works ok in practice.
-
-            if a_r.is_some() && b_r.is_some() && only_new_vars {
-                // Related to exactly one bound variable from each fn:
-                return rev_lookup(this, a_map, new_binder_id, a_r.unwrap());
-            } else if a_r.is_none() && b_r.is_none() {
-                // Not related to bound variables from either fn:
-                assert!(!r0.is_bound());
-                return r0;
-            } else {
-                // Other:
-                return fresh_bound_variable(this, new_binder_id);
-            }
-        }
-
-        fn rev_lookup(this: &Glb,
-                      a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
-                      new_binder_id: NodeId,
-                      r: ty::Region) -> ty::Region
-        {
-            for (a_br, a_r) in a_map.iter() {
-                if *a_r == r {
-                    return ty::ReLateBound(new_binder_id, *a_br);
-                }
-            }
-            this.fields.infcx.tcx.sess.span_bug(
-                this.fields.trace.origin.span(),
-                format!("could not find original bound region for {}",
-                        r).as_slice())
-        }
+        self.higher_ranked_glb(a, b)
+    }
 
-        fn fresh_bound_variable(this: &Glb, binder_id: NodeId) -> ty::Region {
-            this.fields.infcx.region_vars.new_bound(binder_id)
-        }
+    fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres<ty::TraitRef> {
+        self.higher_ranked_glb(a, b)
     }
 }
diff --git a/src/librustc/middle/typeck/infer/higher_ranked/doc.rs b/src/librustc/middle/typeck/infer/higher_ranked/doc.rs
new file mode 100644
index 00000000000..4c4452ac892
--- /dev/null
+++ b/src/librustc/middle/typeck/infer/higher_ranked/doc.rs
@@ -0,0 +1,415 @@
+// 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.
+
+/*!
+
+# Skolemization and functions
+
+One of the trickiest and most subtle aspects of regions is dealing
+with higher-ranked things which include bound region variables, such
+as function types. I strongly suggest that if you want to understand
+the situation, you read this paper (which is, admittedly, very long,
+but you don't have to read the whole thing):
+
+http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
+
+Although my explanation will never compete with SPJ's (for one thing,
+his is approximately 100 pages), I will attempt to explain the basic
+problem and also how we solve it. Note that the paper only discusses
+subtyping, not the computation of LUB/GLB.
+
+The problem we are addressing is that there is a kind of subtyping
+between functions with bound region parameters. Consider, for
+example, whether the following relation holds:
+
+    for<'a> fn(&'a int) <: for<'b> fn(&'b int)? (Yes, a => b)
+
+The answer is that of course it does. These two types are basically
+the same, except that in one we used the name `a` and one we used
+the name `b`.
+
+In the examples that follow, it becomes very important to know whether
+a lifetime is bound in a function type (that is, is a lifetime
+parameter) or appears free (is defined in some outer scope).
+Therefore, from now on I will always write the bindings explicitly,
+using the Rust syntax `for<'a> fn(&'a int)` to indicate that `a` is a
+lifetime parameter.
+
+Now let's consider two more function types. Here, we assume that the
+`'b` lifetime is defined somewhere outside and hence is not a lifetime
+parameter bound by the function type (it "appears free"):
+
+    for<'a> fn(&'a int) <: fn(&'b int)? (Yes, a => b)
+
+This subtyping relation does in fact hold. To see why, you have to
+consider what subtyping means. One way to look at `T1 <: T2` is to
+say that it means that it is always ok to treat an instance of `T1` as
+if it had the type `T2`. So, with our functions, it is always ok to
+treat a function that can take pointers with any lifetime as if it
+were a function that can only take a pointer with the specific
+lifetime `'b`. After all, `'b` is a lifetime, after all, and
+the function can take values of any lifetime.
+
+You can also look at subtyping as the *is a* relationship. This amounts
+to the same thing: a function that accepts pointers with any lifetime
+*is a* function that accepts pointers with some specific lifetime.
+
+So, what if we reverse the order of the two function types, like this:
+
+    fn(&'b int) <: for<'a> fn(&'a int)? (No)
+
+Does the subtyping relationship still hold?  The answer of course is
+no. In this case, the function accepts *only the lifetime `'b`*,
+so it is not reasonable to treat it as if it were a function that
+accepted any lifetime.
+
+What about these two examples:
+
+    for<'a,'b> fn(&'a int, &'b int) <: for<'a>    fn(&'a int, &'a int)? (Yes)
+    for<'a>    fn(&'a int, &'a int) <: for<'a,'b> fn(&'a int, &'b int)? (No)
+
+Here, it is true that functions which take two pointers with any two
+lifetimes can be treated as if they only accepted two pointers with
+the same lifetime, but not the reverse.
+
+## The algorithm
+
+Here is the algorithm we use to perform the subtyping check:
+
+1. Replace all bound regions in the subtype with new variables
+2. Replace all bound regions in the supertype with skolemized
+   equivalents. A "skolemized" region is just a new fresh region
+   name.
+3. Check that the parameter and return types match as normal
+4. Ensure that no skolemized regions 'leak' into region variables
+   visible from "the outside"
+
+Let's walk through some examples and see how this algorithm plays out.
+
+#### First example
+
+We'll start with the first example, which was:
+
+    1. for<'a> fn(&'a T) <: for<'b> fn(&'b T)?        Yes: a -> b
+
+After steps 1 and 2 of the algorithm we will have replaced the types
+like so:
+
+    1. fn(&'A T) <: fn(&'x T)?
+
+Here the upper case `&A` indicates a *region variable*, that is, a
+region whose value is being inferred by the system. I also replaced
+`&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`)
+to indicate skolemized region names. We can assume they don't appear
+elsewhere. Note that neither the sub- nor the supertype bind any
+region names anymore (as indicated by the absence of `<` and `>`).
+
+The next step is to check that the parameter types match. Because
+parameters are contravariant, this means that we check whether:
+
+    &'x T <: &'A T
+
+Region pointers are contravariant so this implies that
+
+    &A <= &x
+
+must hold, where `<=` is the subregion relationship. Processing
+*this* constrain simply adds a constraint into our graph that `&A <=
+&x` and is considered successful (it can, for example, be satisfied by
+choosing the value `&x` for `&A`).
+
+So far we have encountered no error, so the subtype check succeeds.
+
+#### The third example
+
+Now let's look first at the third example, which was:
+
+    3. fn(&'a T)    <: for<'b> fn(&'b T)?        No!
+
+After steps 1 and 2 of the algorithm we will have replaced the types
+like so:
+
+    3. fn(&'a T) <: fn(&'x T)?
+
+This looks pretty much the same as before, except that on the LHS
+`'a` was not bound, and hence was left as-is and not replaced with
+a variable. The next step is again to check that the parameter types
+match. This will ultimately require (as before) that `'a` <= `&x`
+must hold: but this does not hold. `self` and `x` are both distinct
+free regions. So the subtype check fails.
+
+#### Checking for skolemization leaks
+
+You may be wondering about that mysterious last step in the algorithm.
+So far it has not been relevant. The purpose of that last step is to
+catch something like *this*:
+
+    for<'a> fn() -> fn(&'a T) <: fn() -> for<'b> fn(&'b T)?   No.
+
+Here the function types are the same but for where the binding occurs.
+The subtype returns a function that expects a value in precisely one
+region. The supertype returns a function that expects a value in any
+region. If we allow an instance of the subtype to be used where the
+supertype is expected, then, someone could call the fn and think that
+the return value has type `fn<b>(&'b T)` when it really has type
+`fn(&'a T)` (this is case #3, above). Bad.
+
+So let's step through what happens when we perform this subtype check.
+We first replace the bound regions in the subtype (the supertype has
+no bound regions). This gives us:
+
+    fn() -> fn(&'A T) <: fn() -> for<'b> fn(&'b T)?
+
+Now we compare the return types, which are covariant, and hence we have:
+
+    fn(&'A T) <: for<'b> fn(&'b T)?
+
+Here we skolemize the bound region in the supertype to yield:
+
+    fn(&'A T) <: fn(&'x T)?
+
+And then proceed to compare the argument types:
+
+    &'x T <: &'A T
+    'A <= 'x
+
+Finally, this is where it gets interesting!  This is where an error
+*should* be reported. But in fact this will not happen. The reason why
+is that `A` is a variable: we will infer that its value is the fresh
+region `x` and think that everything is happy. In fact, this behavior
+is *necessary*, it was key to the first example we walked through.
+
+The difference between this example and the first one is that the variable
+`A` already existed at the point where the skolemization occurred. In
+the first example, you had two functions:
+
+    for<'a> fn(&'a T) <: for<'b> fn(&'b T)
+
+and hence `&A` and `&x` were created "together". In general, the
+intention of the skolemized names is that they are supposed to be
+fresh names that could never be equal to anything from the outside.
+But when inference comes into play, we might not be respecting this
+rule.
+
+So the way we solve this is to add a fourth step that examines the
+constraints that refer to skolemized names. Basically, consider a
+non-directed verison of the constraint graph. Let `Tainted(x)` be the
+set of all things reachable from a skolemized variable `x`.
+`Tainted(x)` should not contain any regions that existed before the
+step at which the skolemization was performed. So this case here
+would fail because `&x` was created alone, but is relatable to `&A`.
+
+## Computing the LUB and GLB
+
+The paper I pointed you at is written for Haskell. It does not
+therefore considering subtyping and in particular does not consider
+LUB or GLB computation. We have to consider this. Here is the
+algorithm I implemented.
+
+First though, let's discuss what we are trying to compute in more
+detail. The LUB is basically the "common supertype" and the GLB is
+"common subtype"; one catch is that the LUB should be the
+*most-specific* common supertype and the GLB should be *most general*
+common subtype (as opposed to any common supertype or any common
+subtype).
+
+Anyway, to help clarify, here is a table containing some function
+pairs and their LUB/GLB (for conciseness, in this table, I'm just
+including the lifetimes here, not the rest of the types, and I'm
+writing `fn<>` instead of `for<> fn`):
+
+```
+Type 1                Type 2                LUB                    GLB
+fn<'a>('a)            fn('X)                fn('X)                 fn<'a>('a)
+fn('a)                fn('X)                --                     fn<'a>('a)
+fn<'a,'b>('a, 'b)     fn<'x>('x, 'x)        fn<'a>('a, 'a)         fn<'a,'b>('a, 'b)
+fn<'a,'b>('a, 'b, 'a) fn<'x,'y>('x, 'y, 'y) fn<'a>('a, 'a, 'a)     fn<'a,'b,'c>('a,'b,'c)
+```
+
+### Conventions
+
+I use lower-case letters (e.g., `&a`) for bound regions and upper-case
+letters for free regions (`&A`).  Region variables written with a
+dollar-sign (e.g., `$a`).  I will try to remember to enumerate the
+bound-regions on the fn type as well (e.g., `for<'a> fn(&a)`).
+
+### High-level summary
+
+Both the LUB and the GLB algorithms work in a similar fashion.  They
+begin by replacing all bound regions (on both sides) with fresh region
+inference variables.  Therefore, both functions are converted to types
+that contain only free regions.  We can then compute the LUB/GLB in a
+straightforward way, as described in `combine.rs`.  This results in an
+interim type T.  The algorithms then examine the regions that appear
+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.
+
+The other important factor in deciding how to replace a region in T is
+the function `Tainted($r)` which, for a region variable, identifies
+all regions that the region variable is related to in some way
+(`Tainted()` made an appearance in the subtype computation as well).
+
+### LUB
+
+The LUB algorithm proceeds in three steps:
+
+1. Replace all bound regions (on both sides) with fresh region
+   inference variables.
+2. Compute the LUB "as normal", meaning compute the GLB of each
+   pair of argument types and the LUB of the return types and
+   so forth.  Combine those to a new function type `F`.
+3. Replace each region `R` that appears in `F` as follows:
+   - Let `V` be the set of variables created during the LUB
+     computational steps 1 and 2, as described in the previous section.
+   - If `R` is not in `V`, replace `R` with itself.
+   - If `Tainted(R)` contains a region that is not in `V`,
+     replace `R` with itself.
+   - Otherwise, select the earliest variable in `Tainted(R)` that originates
+     from the left-hand side and replace `R` with the bound region that
+     this variable was a replacement for.
+
+So, let's work through the simplest example: `fn(&A)` and `for<'a> fn(&a)`.
+In this case, `&a` will be replaced with `$a` and the interim LUB type
+`fn($b)` will be computed, where `$b=GLB(&A,$a)`.  Therefore, `V =
+{$a, $b}` and `Tainted($b) = { $b, $a, &A }`.  When we go to replace
+`$b`, we find that since `&A \in Tainted($b)` is not a member of `V`,
+we leave `$b` as is.  When region inference happens, `$b` will be
+resolved to `&A`, as we wanted.
+
+Let's look at a more complex one: `fn(&a, &b)` and `fn(&x, &x)`.  In
+this case, we'll end up with a (pre-replacement) LUB type of `fn(&g,
+&h)` and a graph that looks like:
+
+```
+     $a        $b     *--$x
+       \        \    /  /
+        \        $h-*  /
+         $g-----------*
+```
+
+Here `$g` and `$h` are fresh variables that are created to represent
+the LUB/GLB of things requiring inference.  This means that `V` and
+`Tainted` will look like:
+
+```
+V = {$a, $b, $g, $h, $x}
+Tainted($g) = Tainted($h) = { $a, $b, $h, $g, $x }
+```
+
+Therefore we replace both `$g` and `$h` with `$a`, and end up
+with the type `fn(&a, &a)`.
+
+### GLB
+
+The procedure for computing the GLB is similar.  The difference lies
+in computing the replacements for the various variables. For each
+region `R` that appears in the type `F`, we again compute `Tainted(R)`
+and examine the results:
+
+1. If `R` is not in `V`, it is not replaced.
+2. Else, if `Tainted(R)` contains only variables in `V`, and it
+   contains exactly one variable from the LHS and one variable from
+   the RHS, then `R` can be mapped to the bound version of the
+   variable from the LHS.
+3. Else, if `Tainted(R)` contains no variable from the LHS and no
+   variable from the RHS, then `R` can be mapped to itself.
+4. Else, `R` is mapped to a fresh bound variable.
+
+These rules are pretty complex.  Let's look at some examples to see
+how they play out.
+
+Out first example was `fn(&a)` and `fn(&X)`.  In this case, `&a` will
+be replaced with `$a` and we will ultimately compute a
+(pre-replacement) GLB type of `fn($g)` where `$g=LUB($a,&X)`.
+Therefore, `V={$a,$g}` and `Tainted($g)={$g,$a,&X}.  To find the
+replacement for `$g` we consult the rules above:
+- Rule (1) does not apply because `$g \in V`
+- Rule (2) does not apply because `&X \in Tainted($g)`
+- Rule (3) does not apply because `$a \in Tainted($g)`
+- Hence, by rule (4), we replace `$g` with a fresh bound variable `&z`.
+So our final result is `fn(&z)`, which is correct.
+
+The next example is `fn(&A)` and `fn(&Z)`. In this case, we will again
+have a (pre-replacement) GLB of `fn(&g)`, where `$g = LUB(&A,&Z)`.
+Therefore, `V={$g}` and `Tainted($g) = {$g, &A, &Z}`.  In this case,
+by rule (3), `$g` is mapped to itself, and hence the result is
+`fn($g)`.  This result is correct (in this case, at least), but it is
+indicative of a case that *can* lead us into concluding that there is
+no GLB when in fact a GLB does exist.  See the section "Questionable
+Results" below for more details.
+
+The next example is `fn(&a, &b)` and `fn(&c, &c)`. In this case, as
+before, we'll end up with `F=fn($g, $h)` where `Tainted($g) =
+Tainted($h) = {$g, $h, $a, $b, $c}`.  Only rule (4) applies and hence
+we'll select fresh bound variables `y` and `z` and wind up with
+`fn(&y, &z)`.
+
+For the last example, let's consider what may seem trivial, but is
+not: `fn(&a, &a)` and `fn(&b, &b)`.  In this case, we'll get `F=fn($g,
+$h)` where `Tainted($g) = {$g, $a, $x}` and `Tainted($h) = {$h, $a,
+$x}`.  Both of these sets contain exactly one bound variable from each
+side, so we'll map them both to `&a`, resulting in `fn(&a, &a)`, which
+is the desired result.
+
+### Shortcomings and correctness
+
+You may be wondering whether this algorithm is correct.  The answer is
+"sort of".  There are definitely cases where they fail to compute a
+result even though a correct result exists.  I believe, though, that
+if they succeed, then the result is valid, and I will attempt to
+convince you.  The basic argument is that the "pre-replacement" step
+computes a set of constraints.  The replacements, then, attempt to
+satisfy those constraints, using bound identifiers where needed.
+
+For now I will briefly go over the cases for LUB/GLB and identify
+their intent:
+
+- LUB:
+  - The region variables that are substituted in place of bound regions
+    are intended to collect constraints on those bound regions.
+  - If Tainted(R) contains only values in V, then this region is unconstrained
+    and can therefore be generalized, otherwise it cannot.
+- GLB:
+  - The region variables that are substituted in place of bound regions
+    are intended to collect constraints on those bound regions.
+  - If Tainted(R) contains exactly one variable from each side, and
+    only variables in V, that indicates that those two bound regions
+    must be equated.
+  - Otherwise, if Tainted(R) references any variables from left or right
+    side, then it is trying to combine a bound region with a free one or
+    multiple bound regions, so we need to select fresh bound regions.
+
+Sorry this is more of a shorthand to myself.  I will try to write up something
+more convincing in the future.
+
+#### Where are the algorithms wrong?
+
+- The pre-replacement computation can fail even though using a
+  bound-region would have succeeded.
+- We will compute GLB(fn(fn($a)), fn(fn($b))) as fn($c) where $c is the
+  GLB of $a and $b.  But if inference finds that $a and $b must be mapped
+  to regions without a GLB, then this is effectively a failure to compute
+  the GLB.  However, the result `fn<$c>(fn($c))` is a valid GLB.
+
+ */
diff --git a/src/librustc/middle/typeck/infer/higher_ranked/mod.rs b/src/librustc/middle/typeck/infer/higher_ranked/mod.rs
new file mode 100644
index 00000000000..09f0bbb2254
--- /dev/null
+++ b/src/librustc/middle/typeck/infer/higher_ranked/mod.rs
@@ -0,0 +1,445 @@
+// 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.
+
+/*!
+ * Helper routines for higher-ranked things. See the `doc` module at
+ * the end of the file for details.
+ */
+
+use middle::ty;
+use middle::ty::replace_late_bound_regions;
+use middle::typeck::infer::{mod, combine, cres, InferCtxt};
+use middle::typeck::infer::combine::Combine;
+use middle::typeck::infer::region_inference::{RegionMark};
+use middle::ty_fold::{mod, HigherRankedFoldable, TypeFoldable};
+use syntax::codemap::Span;
+use util::nodemap::FnvHashMap;
+use util::ppaux::{bound_region_to_string, Repr};
+
+pub trait HigherRankedCombineable : HigherRankedFoldable + TypeFoldable + Repr {
+    fn super_combine<'tcx,C:Combine<'tcx>>(combiner: &C, a: &Self, b: &Self) -> cres<Self>;
+}
+
+pub trait HigherRankedRelations {
+    fn higher_ranked_sub<T>(&self, a: &T, b: &T) -> cres<T>
+        where T : HigherRankedCombineable;
+
+    fn higher_ranked_lub<T>(&self, a: &T, b: &T) -> cres<T>
+        where T : HigherRankedCombineable;
+
+    fn higher_ranked_glb<T>(&self, a: &T, b: &T) -> cres<T>
+        where T : HigherRankedCombineable;
+}
+
+impl<'tcx,C> HigherRankedRelations for C
+    where C : Combine<'tcx>
+{
+    fn higher_ranked_sub<T>(&self, a: &T, b: &T) -> cres<T>
+        where T : HigherRankedCombineable
+    {
+        debug!("higher_ranked_sub(a={}, b={})",
+               a.repr(self.tcx()), b.repr(self.tcx()));
+
+        // Rather than checking the subtype relationship between `a` and `b`
+        // as-is, we need to do some extra work here in order to make sure
+        // that function subtyping works correctly with respect to regions
+        //
+        // Note: this is a subtle algorithm.  For a full explanation,
+        // 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
+        // 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(),
+                infer::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; }
+                    }
+                };
+
+                // 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()));
+
+        return Ok(result);
+    }
+
+    fn higher_ranked_lub<T>(&self, a: &T, b: &T) -> cres<T>
+        where T : HigherRankedCombineable
+    {
+        // Make a mark 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, infer::HigherRankedType, a);
+        let (b_with_fresh, _) =
+            self.infcx().replace_late_bound_regions_with_fresh_var(
+                span, infer::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);
+
+        fn generalize_region(infcx: &InferCtxt,
+                             span: Span,
+                             mark: RegionMark,
+                             debruijn: ty::DebruijnIndex,
+                             new_vars: &[ty::RegionVid],
+                             a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
+                             r0: ty::Region)
+                             -> ty::Region {
+            // Regions that pre-dated the LUB computation stay as they are.
+            if !is_var_in_set(new_vars, r0) {
+                assert!(!r0.is_bound());
+                debug!("generalize_region(r0={}): not new variable", r0);
+                return r0;
+            }
+
+            let tainted = infcx.region_vars.tainted(mark, r0);
+
+            // Variables created during LUB computation which are
+            // *related* to regions that pre-date the LUB computation
+            // stay as they are.
+            if !tainted.iter().all(|r| is_var_in_set(new_vars, *r)) {
+                debug!("generalize_region(r0={}): \
+                        non-new-variables found in {}",
+                       r0, tainted);
+                assert!(!r0.is_bound());
+                return r0;
+            }
+
+            // Otherwise, the variable must be associated with at
+            // least one of the variables representing bound regions
+            // in both A and B.  Replace the variable with the "first"
+            // bound region from A that we find it to be associated
+            // with.
+            for (a_br, a_r) in a_map.iter() {
+                if tainted.iter().any(|x| x == a_r) {
+                    debug!("generalize_region(r0={}): \
+                            replacing with {}, tainted={}",
+                           r0, *a_br, tainted);
+                    return ty::ReLateBound(debruijn, *a_br);
+                }
+            }
+
+            infcx.tcx.sess.span_bug(
+                span,
+                format!("region {} is not associated with \
+                         any bound region from A!",
+                        r0).as_slice())
+        }
+    }
+
+    fn higher_ranked_glb<T>(&self, a: &T, b: &T) -> cres<T>
+        where T : HigherRankedCombineable
+    {
+        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
+        // 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(), infer::HigherRankedType, a);
+        let (b_with_fresh, b_map) =
+            self.infcx().replace_late_bound_regions_with_fresh_var(
+                self.trace().origin.span(), infer::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);
+
+        fn generalize_region(infcx: &InferCtxt,
+                             span: Span,
+                             mark: RegionMark,
+                             debruijn: ty::DebruijnIndex,
+                             new_vars: &[ty::RegionVid],
+                             a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
+                             a_vars: &[ty::RegionVid],
+                             b_vars: &[ty::RegionVid],
+                             r0: ty::Region) -> ty::Region {
+            if !is_var_in_set(new_vars, r0) {
+                assert!(!r0.is_bound());
+                return r0;
+            }
+
+            let tainted = infcx.region_vars.tainted(mark, r0);
+
+            let mut a_r = None;
+            let mut b_r = None;
+            let mut only_new_vars = true;
+            for r in tainted.iter() {
+                if is_var_in_set(a_vars, *r) {
+                    if a_r.is_some() {
+                        return fresh_bound_variable(infcx, debruijn);
+                    } else {
+                        a_r = Some(*r);
+                    }
+                } else if is_var_in_set(b_vars, *r) {
+                    if b_r.is_some() {
+                        return fresh_bound_variable(infcx, debruijn);
+                    } else {
+                        b_r = Some(*r);
+                    }
+                } else if !is_var_in_set(new_vars, *r) {
+                    only_new_vars = false;
+                }
+            }
+
+            // NB---I do not believe this algorithm computes
+            // (necessarily) the GLB.  As written it can
+            // spuriously fail. In particular, if there is a case
+            // like: |fn(&a)| and fn(fn(&b)), where a and b are
+            // free, it will return fn(&c) where c = GLB(a,b).  If
+            // however this GLB is not defined, then the result is
+            // an error, even though something like
+            // "fn<X>(fn(&X))" where X is bound would be a
+            // subtype of both of those.
+            //
+            // The problem is that if we were to return a bound
+            // variable, we'd be computing a lower-bound, but not
+            // necessarily the *greatest* lower-bound.
+            //
+            // Unfortunately, this problem is non-trivial to solve,
+            // because we do not know at the time of computing the GLB
+            // whether a GLB(a,b) exists or not, because we haven't
+            // run region inference (or indeed, even fully computed
+            // the region hierarchy!). The current algorithm seems to
+            // works ok in practice.
+
+            if a_r.is_some() && b_r.is_some() && only_new_vars {
+                // Related to exactly one bound variable from each fn:
+                return rev_lookup(infcx, span, a_map, a_r.unwrap());
+            } else if a_r.is_none() && b_r.is_none() {
+                // Not related to bound variables from either fn:
+                assert!(!r0.is_bound());
+                return r0;
+            } else {
+                // Other:
+                return fresh_bound_variable(infcx, debruijn);
+            }
+        }
+
+        fn rev_lookup(infcx: &InferCtxt,
+                      span: Span,
+                      a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
+                      r: ty::Region) -> ty::Region
+        {
+            for (a_br, a_r) in a_map.iter() {
+                if *a_r == r {
+                    return ty::ReLateBound(ty::DebruijnIndex::new(1), *a_br);
+                }
+            }
+            infcx.tcx.sess.span_bug(
+                span,
+                format!("could not find original bound region for {}", r)[]);
+        }
+
+        fn fresh_bound_variable(infcx: &InferCtxt, debruijn: ty::DebruijnIndex) -> ty::Region {
+            infcx.region_vars.new_bound(debruijn)
+        }
+    }
+}
+
+impl HigherRankedCombineable for ty::FnSig {
+    fn super_combine<'tcx,C:Combine<'tcx>>(combiner: &C, a: &ty::FnSig, b: &ty::FnSig)
+                                           -> cres<ty::FnSig>
+    {
+        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::t],
+                                           b_args: &[ty::t])
+                                           -> cres<Vec<ty::t>>
+        {
+            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 HigherRankedCombineable for ty::TraitRef {
+    fn super_combine<'tcx,C:Combine<'tcx>>(combiner: &C, a: &ty::TraitRef, b: &ty::TraitRef)
+                                           -> cres<ty::TraitRef>
+    {
+        // 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> {
+    map.iter().map(|(_, r)| match *r {
+            ty::ReInfer(ty::ReVar(r)) => { r }
+            r => {
+                combiner.infcx().tcx.sess.span_bug(
+                    combiner.trace().origin.span(),
+                    format!("found non-region-vid: {}", r).as_slice());
+            }
+        }).collect()
+}
+
+fn is_var_in_set(new_vars: &[ty::RegionVid], r: ty::Region) -> bool {
+    match r {
+        ty::ReInfer(ty::ReVar(ref v)) => new_vars.iter().any(|x| x == v),
+        _ => false
+    }
+}
+
+fn fold_regions_in<T:HigherRankedFoldable>(tcx: &ty::ctxt,
+                                           value: &T,
+                                           fldr: |ty::Region, ty::DebruijnIndex| -> ty::Region)
+                                           -> T
+{
+    value.fold_contents(&mut ty_fold::RegionFolder::new(tcx, |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
+        assert!(match region {
+            ty::ReLateBound(..) => false,
+            _ => true
+        });
+
+        fldr(region, ty::DebruijnIndex::new(current_depth))
+    }))
+}
+
diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs
index 5dbcaadf0df..f7e6cef99af 100644
--- a/src/librustc/middle/typeck/infer/lattice.rs
+++ b/src/librustc/middle/typeck/infer/lattice.rs
@@ -31,13 +31,12 @@
  * a lattice.
  */
 
-use middle::ty::{RegionVid, TyVar};
+use middle::ty::{TyVar};
 use middle::ty;
 use middle::typeck::infer::*;
 use middle::typeck::infer::combine::*;
 use middle::typeck::infer::glb::Glb;
 use middle::typeck::infer::lub::Lub;
-use util::nodemap::FnvHashMap;
 use util::ppaux::Repr;
 
 pub trait LatticeDir {
@@ -101,27 +100,3 @@ pub fn super_lattice_tys<'tcx, L:LatticeDir+Combine<'tcx>>(this: &L,
         }
     }
 }
-
-///////////////////////////////////////////////////////////////////////////
-// Random utility functions used by LUB/GLB when computing LUB/GLB of
-// fn types
-
-pub fn var_ids<'tcx, T: Combine<'tcx>>(this: &T,
-                                       map: &FnvHashMap<ty::BoundRegion, ty::Region>)
-                                       -> Vec<RegionVid> {
-    map.iter().map(|(_, r)| match *r {
-            ty::ReInfer(ty::ReVar(r)) => { r }
-            r => {
-                this.infcx().tcx.sess.span_bug(
-                    this.trace().origin.span(),
-                    format!("found non-region-vid: {}", r).as_slice());
-            }
-        }).collect()
-}
-
-pub fn is_var_in_set(new_vars: &[RegionVid], r: ty::Region) -> bool {
-    match r {
-        ty::ReInfer(ty::ReVar(ref v)) => new_vars.iter().any(|x| x == v),
-        _ => false
-    }
-}
diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs
index aaad3d573be..8856f42d1f5 100644
--- a/src/librustc/middle/typeck/infer/lub.rs
+++ b/src/librustc/middle/typeck/infer/lub.rs
@@ -9,23 +9,19 @@
 // except according to those terms.
 
 use middle::ty::{BuiltinBounds};
-use middle::ty::RegionVid;
 use middle::ty;
 use middle::typeck::infer::combine::*;
 use middle::typeck::infer::equate::Equate;
 use middle::typeck::infer::glb::Glb;
+use middle::typeck::infer::higher_ranked::HigherRankedRelations;
 use middle::typeck::infer::lattice::*;
-use middle::typeck::infer::LateBoundRegionConversionTime::FnType;
 use middle::typeck::infer::sub::Sub;
 use middle::typeck::infer::{cres, InferCtxt};
-use middle::typeck::infer::fold_regions_in_sig;
 use middle::typeck::infer::{TypeTrace, Subtype};
-use middle::typeck::infer::region_inference::RegionMark;
-use syntax::ast::{Many, Once, NodeId};
+use syntax::ast::{Many, Once};
 use syntax::ast::{NormalFn, UnsafeFn};
 use syntax::ast::{Onceness, FnStyle};
 use syntax::ast::{MutMutable, MutImmutable};
-use util::nodemap::FnvHashMap;
 use util::ppaux::mt_to_string;
 use util::ppaux::Repr;
 
@@ -51,7 +47,7 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> {
     fn glb<'a>(&'a self) -> Glb<'a, 'tcx> { Glb(self.fields.clone()) }
 
     fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
-        let tcx = self.fields.infcx.tcx;
+        let tcx = self.tcx();
 
         debug!("{}.mts({}, {})",
                self.tag(),
@@ -111,93 +107,21 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> {
     fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
         debug!("{}.regions({}, {})",
                self.tag(),
-               a.repr(self.fields.infcx.tcx),
-               b.repr(self.fields.infcx.tcx));
+               a.repr(self.tcx()),
+               b.repr(self.tcx()));
 
-        Ok(self.fields.infcx.region_vars.lub_regions(Subtype(self.trace()), a, b))
+        Ok(self.infcx().region_vars.lub_regions(Subtype(self.trace()), a, b))
     }
 
     fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
-        // Note: this is a subtle algorithm.  For a full explanation,
-        // please see the large comment in `region_inference.rs`.
-
-        // Make a mark so we can examine "all bindings that were
-        // created as part of this type comparison".
-        let mark = self.fields.infcx.region_vars.mark();
-
-        // Instantiate each bound region with a fresh region variable.
-        let (a_with_fresh, a_map) =
-            self.fields.infcx.replace_late_bound_regions_with_fresh_var(
-                a.binder_id, self.trace().span(), FnType, a);
-        let (b_with_fresh, _) =
-            self.fields.infcx.replace_late_bound_regions_with_fresh_var(
-                b.binder_id, self.trace().span(), FnType, b);
-
-        // Collect constraints.
-        let sig0 = try!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh));
-        debug!("sig0 = {}", sig0.repr(self.fields.infcx.tcx));
-
-        // Generalize the regions appearing in sig0 if possible
-        let new_vars =
-            self.fields.infcx.region_vars.vars_created_since_mark(mark);
-        let sig1 =
-            fold_regions_in_sig(
-                self.fields.infcx.tcx,
-                &sig0,
-                |r| generalize_region(self, mark, new_vars.as_slice(),
-                                      sig0.binder_id, &a_map, r));
-        return Ok(sig1);
-
-        fn generalize_region(this: &Lub,
-                             mark: RegionMark,
-                             new_vars: &[RegionVid],
-                             new_scope: NodeId,
-                             a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
-                             r0: ty::Region)
-                             -> ty::Region {
-            // Regions that pre-dated the LUB computation stay as they are.
-            if !is_var_in_set(new_vars, r0) {
-                assert!(!r0.is_bound());
-                debug!("generalize_region(r0={}): not new variable", r0);
-                return r0;
-            }
-
-            let tainted = this.fields.infcx.region_vars.tainted(mark, r0);
-
-            // Variables created during LUB computation which are
-            // *related* to regions that pre-date the LUB computation
-            // stay as they are.
-            if !tainted.iter().all(|r| is_var_in_set(new_vars, *r)) {
-                debug!("generalize_region(r0={}): \
-                        non-new-variables found in {}",
-                       r0, tainted);
-                assert!(!r0.is_bound());
-                return r0;
-            }
-
-            // Otherwise, the variable must be associated with at
-            // least one of the variables representing bound regions
-            // in both A and B.  Replace the variable with the "first"
-            // bound region from A that we find it to be associated
-            // with.
-            for (a_br, a_r) in a_map.iter() {
-                if tainted.iter().any(|x| x == a_r) {
-                    debug!("generalize_region(r0={}): \
-                            replacing with {}, tainted={}",
-                           r0, *a_br, tainted);
-                    return ty::ReLateBound(new_scope, *a_br);
-                }
-            }
-
-            this.fields.infcx.tcx.sess.span_bug(
-                this.fields.trace.origin.span(),
-                format!("region {} is not associated with \
-                         any bound region from A!",
-                        r0).as_slice())
-        }
+        self.higher_ranked_lub(a, b)
     }
 
     fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
         super_lattice_tys(self, a, b)
     }
+
+    fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres<ty::TraitRef> {
+        self.higher_ranked_lub(a, b)
+    }
 }
diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs
index a75e3a78e4b..e69bd215766 100644
--- a/src/librustc/middle/typeck/infer/mod.rs
+++ b/src/librustc/middle/typeck/infer/mod.rs
@@ -31,10 +31,9 @@ pub use self::skolemize::TypeSkolemizer;
 use middle::subst;
 use middle::subst::Substs;
 use middle::ty::{TyVid, IntVid, FloatVid, RegionVid};
+use middle::ty::replace_late_bound_regions;
 use middle::ty;
-use middle::ty_fold;
-use middle::ty_fold::{TypeFolder, TypeFoldable};
-use middle::typeck::check::regionmanip::replace_late_bound_regions;
+use middle::ty_fold::{HigherRankedFoldable, TypeFolder, TypeFoldable};
 use std::cell::{RefCell};
 use std::rc::Rc;
 use syntax::ast;
@@ -61,6 +60,7 @@ pub mod doc;
 pub mod equate;
 pub mod error_reporting;
 pub mod glb;
+pub mod higher_ranked;
 pub mod lattice;
 pub mod lub;
 pub mod region_inference;
@@ -184,9 +184,9 @@ pub enum SubregionOrigin {
     // type of the variable outlives the lifetime bound.
     RelateProcBound(Span, ast::NodeId, ty::t),
 
-    // The given type parameter was instantiated with the given type,
+    // Some type parameter was instantiated with the given type,
     // and that type must outlive some region.
-    RelateParamBound(Span, ty::ParamTy, ty::t),
+    RelateParamBound(Span, ty::t),
 
     // The given region parameter was instantiated with a region
     // that must outlive some other region.
@@ -233,8 +233,8 @@ pub enum LateBoundRegionConversionTime {
     /// when a fn is called
     FnCall,
 
-    /// when two fn types are compared
-    FnType,
+    /// when two higher-ranked types are compared
+    HigherRankedType,
 }
 
 /// Reasons to create a region inference variable
@@ -796,8 +796,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         subst::Substs::new_trait(type_parameters, regions, assoc_type_parameters, self_ty)
     }
 
-    pub fn fresh_bound_region(&self, binder_id: ast::NodeId) -> ty::Region {
-        self.region_vars.new_bound(binder_id)
+    pub fn fresh_bound_region(&self, debruijn: ty::DebruijnIndex) -> ty::Region {
+        self.region_vars.new_bound(debruijn)
     }
 
     pub fn resolve_regions_and_report_errors(&self) {
@@ -815,8 +815,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         format!("({})", tstrs.connect(", "))
     }
 
-    pub fn trait_ref_to_string(&self, t: &ty::TraitRef) -> String {
-        let t = self.resolve_type_vars_in_trait_ref_if_possible(t);
+    pub fn trait_ref_to_string(&self, t: &Rc<ty::TraitRef>) -> String {
+        let t = self.resolve_type_vars_in_trait_ref_if_possible(&**t);
         trait_ref_to_string(self.tcx, &t)
     }
 
@@ -967,30 +967,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
     pub fn replace_late_bound_regions_with_fresh_var<T>(
         &self,
-        binder_id: ast::NodeId,
         span: Span,
         lbrct: LateBoundRegionConversionTime,
         value: &T)
         -> (T, FnvHashMap<ty::BoundRegion,ty::Region>)
-        where T : TypeFoldable + Repr
+        where T : HigherRankedFoldable
     {
-        let (map, value) =
-            replace_late_bound_regions(
-                self.tcx,
-                binder_id,
-                value,
-                |br| self.next_region_var(LateBoundRegion(span, br, lbrct)));
-        (value, map)
+        ty::replace_late_bound_regions(
+            self.tcx,
+            value,
+            |br, _| self.next_region_var(LateBoundRegion(span, br, lbrct)))
     }
 }
 
-pub fn fold_regions_in_sig(tcx: &ty::ctxt,
-                           fn_sig: &ty::FnSig,
-                           fldr: |r: ty::Region| -> ty::Region)
-                           -> ty::FnSig {
-    ty_fold::RegionFolder::regions(tcx, fldr).fold_sig(fn_sig)
-}
-
 impl TypeTrace {
     pub fn span(&self) -> Span {
         self.origin.span()
@@ -1073,7 +1062,7 @@ impl SubregionOrigin {
             IndexSlice(a) => a,
             RelateObjectBound(a) => a,
             RelateProcBound(a, _, _) => a,
-            RelateParamBound(a, _, _) => a,
+            RelateParamBound(a, _) => a,
             RelateRegionParamBound(a) => a,
             RelateDefaultParamBound(a, _) => a,
             Reborrow(a) => a,
@@ -1123,11 +1112,10 @@ impl Repr for SubregionOrigin {
                         b,
                         c.repr(tcx))
             }
-            RelateParamBound(a, b, c) => {
-                format!("RelateParamBound({},{},{})",
+            RelateParamBound(a, b) => {
+                format!("RelateParamBound({},{})",
                         a.repr(tcx),
-                        b.repr(tcx),
-                        c.repr(tcx))
+                        b.repr(tcx))
             }
             RelateRegionParamBound(a) => {
                 format!("RelateRegionParamBound({})",
diff --git a/src/librustc/middle/typeck/infer/region_inference/doc.rs b/src/librustc/middle/typeck/infer/region_inference/doc.rs
index a64f7896cfb..40b41deeb2b 100644
--- a/src/librustc/middle/typeck/infer/region_inference/doc.rs
+++ b/src/librustc/middle/typeck/infer/region_inference/doc.rs
@@ -370,404 +370,9 @@ address this problem somehow and make region inference somewhat more
 efficient. Note that this is solely a matter of performance, not
 expressiveness.
 
-# Skolemization and functions
+### Skolemization
 
-One of the trickiest and most subtle aspects of regions is dealing
-with the fact that region variables are bound in function types.  I
-strongly suggest that if you want to understand the situation, you
-read this paper (which is, admittedly, very long, but you don't have
-to read the whole thing):
-
-http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
-
-Although my explanation will never compete with SPJ's (for one thing,
-his is approximately 100 pages), I will attempt to explain the basic
-problem and also how we solve it.  Note that the paper only discusses
-subtyping, not the computation of LUB/GLB.
-
-The problem we are addressing is that there is a kind of subtyping
-between functions with bound region parameters.  Consider, for
-example, whether the following relation holds:
-
-    fn(&'a int) <: |&'b int|? (Yes, a => b)
-
-The answer is that of course it does.  These two types are basically
-the same, except that in one we used the name `a` and one we used
-the name `b`.
-
-In the examples that follow, it becomes very important to know whether
-a lifetime is bound in a function type (that is, is a lifetime
-parameter) or appears free (is defined in some outer scope).
-Therefore, from now on I will write the bindings explicitly, using a
-notation like `fn<a>(&'a int)` to indicate that `a` is a lifetime
-parameter.
-
-Now let's consider two more function types.  Here, we assume that the
-`self` lifetime is defined somewhere outside and hence is not a
-lifetime parameter bound by the function type (it "appears free"):
-
-    fn<a>(&'a int) <: |&'a int|? (Yes, a => self)
-
-This subtyping relation does in fact hold.  To see why, you have to
-consider what subtyping means.  One way to look at `T1 <: T2` is to
-say that it means that it is always ok to treat an instance of `T1` as
-if it had the type `T2`.  So, with our functions, it is always ok to
-treat a function that can take pointers with any lifetime as if it
-were a function that can only take a pointer with the specific
-lifetime `&self`.  After all, `&self` is a lifetime, after all, and
-the function can take values of any lifetime.
-
-You can also look at subtyping as the *is a* relationship.  This amounts
-to the same thing: a function that accepts pointers with any lifetime
-*is a* function that accepts pointers with some specific lifetime.
-
-So, what if we reverse the order of the two function types, like this:
-
-    fn(&'a int) <: <a>|&'a int|? (No)
-
-Does the subtyping relationship still hold?  The answer of course is
-no.  In this case, the function accepts *only the lifetime `&self`*,
-so it is not reasonable to treat it as if it were a function that
-accepted any lifetime.
-
-What about these two examples:
-
-    fn<a,b>(&'a int, &'b int) <: <a>|&'a int, &'a int|? (Yes)
-    fn<a>(&'a int, &'a int) <: <a,b>|&'a int, &'b int|? (No)
-
-Here, it is true that functions which take two pointers with any two
-lifetimes can be treated as if they only accepted two pointers with
-the same lifetime, but not the reverse.
-
-## The algorithm
-
-Here is the algorithm we use to perform the subtyping check:
-
-1. Replace all bound regions in the subtype with new variables
-2. Replace all bound regions in the supertype with skolemized
-   equivalents.  A "skolemized" region is just a new fresh region
-   name.
-3. Check that the parameter and return types match as normal
-4. Ensure that no skolemized regions 'leak' into region variables
-   visible from "the outside"
-
-Let's walk through some examples and see how this algorithm plays out.
-
-#### First example
-
-We'll start with the first example, which was:
-
-    1. fn<a>(&'a T) <: <b>|&'b T|?        Yes: a -> b
-
-After steps 1 and 2 of the algorithm we will have replaced the types
-like so:
-
-    1. fn(&'A T) <: |&'x T|?
-
-Here the upper case `&A` indicates a *region variable*, that is, a
-region whose value is being inferred by the system.  I also replaced
-`&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`)
-to indicate skolemized region names.  We can assume they don't appear
-elsewhere.  Note that neither the sub- nor the supertype bind any
-region names anymore (as indicated by the absence of `<` and `>`).
-
-The next step is to check that the parameter types match.  Because
-parameters are contravariant, this means that we check whether:
-
-    &'x T <: &'A T
-
-Region pointers are contravariant so this implies that
-
-    &A <= &x
-
-must hold, where `<=` is the subregion relationship.  Processing
-*this* constrain simply adds a constraint into our graph that `&A <=
-&x` and is considered successful (it can, for example, be satisfied by
-choosing the value `&x` for `&A`).
-
-So far we have encountered no error, so the subtype check succeeds.
-
-#### The third example
-
-Now let's look first at the third example, which was:
-
-    3. fn(&'a T)    <: <b>|&'b T|?        No!
-
-After steps 1 and 2 of the algorithm we will have replaced the types
-like so:
-
-    3. fn(&'a T) <: |&'x T|?
-
-This looks pretty much the same as before, except that on the LHS
-`&self` was not bound, and hence was left as-is and not replaced with
-a variable.  The next step is again to check that the parameter types
-match.  This will ultimately require (as before) that `&self` <= `&x`
-must hold: but this does not hold.  `self` and `x` are both distinct
-free regions.  So the subtype check fails.
-
-#### Checking for skolemization leaks
-
-You may be wondering about that mysterious last step in the algorithm.
-So far it has not been relevant.  The purpose of that last step is to
-catch something like *this*:
-
-    fn<a>() -> fn(&'a T) <: || -> fn<b>(&'b T)?   No.
-
-Here the function types are the same but for where the binding occurs.
-The subtype returns a function that expects a value in precisely one
-region.  The supertype returns a function that expects a value in any
-region.  If we allow an instance of the subtype to be used where the
-supertype is expected, then, someone could call the fn and think that
-the return value has type `fn<b>(&'b T)` when it really has type
-`fn(&'a T)` (this is case #3, above).  Bad.
-
-So let's step through what happens when we perform this subtype check.
-We first replace the bound regions in the subtype (the supertype has
-no bound regions).  This gives us:
-
-    fn() -> fn(&'A T) <: || -> fn<b>(&'b T)?
-
-Now we compare the return types, which are covariant, and hence we have:
-
-    fn(&'A T) <: <b>|&'b T|?
-
-Here we skolemize the bound region in the supertype to yield:
-
-    fn(&'A T) <: |&'x T|?
-
-And then proceed to compare the argument types:
-
-    &'x T <: &'A T
-    &A <= &x
-
-Finally, this is where it gets interesting!  This is where an error
-*should* be reported.  But in fact this will not happen.  The reason why
-is that `A` is a variable: we will infer that its value is the fresh
-region `x` and think that everything is happy.  In fact, this behavior
-is *necessary*, it was key to the first example we walked through.
-
-The difference between this example and the first one is that the variable
-`A` already existed at the point where the skolemization occurred.  In
-the first example, you had two functions:
-
-    fn<a>(&'a T) <: <b>|&'b T|
-
-and hence `&A` and `&x` were created "together".  In general, the
-intention of the skolemized names is that they are supposed to be
-fresh names that could never be equal to anything from the outside.
-But when inference comes into play, we might not be respecting this
-rule.
-
-So the way we solve this is to add a fourth step that examines the
-constraints that refer to skolemized names.  Basically, consider a
-non-directed verison of the constraint graph.  Let `Tainted(x)` be the
-set of all things reachable from a skolemized variable `x`.
-`Tainted(x)` should not contain any regions that existed before the
-step at which the skolemization was performed.  So this case here
-would fail because `&x` was created alone, but is relatable to `&A`.
-
-## Computing the LUB and GLB
-
-The paper I pointed you at is written for Haskell.  It does not
-therefore considering subtyping and in particular does not consider
-LUB or GLB computation.  We have to consider this.  Here is the
-algorithm I implemented.
-
-First though, let's discuss what we are trying to compute in more
-detail.  The LUB is basically the "common supertype" and the GLB is
-"common subtype"; one catch is that the LUB should be the
-*most-specific* common supertype and the GLB should be *most general*
-common subtype (as opposed to any common supertype or any common
-subtype).
-
-Anyway, to help clarify, here is a table containing some
-function pairs and their LUB/GLB:
-
-```
-Type 1              Type 2              LUB               GLB
-fn<a>(&a)           fn(&X)              fn(&X)            fn<a>(&a)
-fn(&A)              fn(&X)              --                fn<a>(&a)
-fn<a,b>(&a, &b)     fn<x>(&x, &x)       fn<a>(&a, &a)     fn<a,b>(&a, &b)
-fn<a,b>(&a, &b, &a) fn<x,y>(&x, &y, &y) fn<a>(&a, &a, &a) fn<a,b,c>(&a,&b,&c)
-```
-
-### Conventions
-
-I use lower-case letters (e.g., `&a`) for bound regions and upper-case
-letters for free regions (`&A`).  Region variables written with a
-dollar-sign (e.g., `$a`).  I will try to remember to enumerate the
-bound-regions on the fn type as well (e.g., `fn<a>(&a)`).
-
-### High-level summary
-
-Both the LUB and the GLB algorithms work in a similar fashion.  They
-begin by replacing all bound regions (on both sides) with fresh region
-inference variables.  Therefore, both functions are converted to types
-that contain only free regions.  We can then compute the LUB/GLB in a
-straightforward way, as described in `combine.rs`.  This results in an
-interim type T.  The algorithms then examine the regions that appear
-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.
-
-The other important factor in deciding how to replace a region in T is
-the function `Tainted($r)` which, for a region variable, identifies
-all regions that the region variable is related to in some way
-(`Tainted()` made an appearance in the subtype computation as well).
-
-### LUB
-
-The LUB algorithm proceeds in three steps:
-
-1. Replace all bound regions (on both sides) with fresh region
-   inference variables.
-2. Compute the LUB "as normal", meaning compute the GLB of each
-   pair of argument types and the LUB of the return types and
-   so forth.  Combine those to a new function type `F`.
-3. Replace each region `R` that appears in `F` as follows:
-   - Let `V` be the set of variables created during the LUB
-     computational steps 1 and 2, as described in the previous section.
-   - If `R` is not in `V`, replace `R` with itself.
-   - If `Tainted(R)` contains a region that is not in `V`,
-     replace `R` with itself.
-   - Otherwise, select the earliest variable in `Tainted(R)` that originates
-     from the left-hand side and replace `R` with the bound region that
-     this variable was a replacement for.
-
-So, let's work through the simplest example: `fn(&A)` and `fn<a>(&a)`.
-In this case, `&a` will be replaced with `$a` and the interim LUB type
-`fn($b)` will be computed, where `$b=GLB(&A,$a)`.  Therefore, `V =
-{$a, $b}` and `Tainted($b) = { $b, $a, &A }`.  When we go to replace
-`$b`, we find that since `&A \in Tainted($b)` is not a member of `V`,
-we leave `$b` as is.  When region inference happens, `$b` will be
-resolved to `&A`, as we wanted.
-
-Let's look at a more complex one: `fn(&a, &b)` and `fn(&x, &x)`.  In
-this case, we'll end up with a (pre-replacement) LUB type of `fn(&g,
-&h)` and a graph that looks like:
-
-```
-     $a        $b     *--$x
-       \        \    /  /
-        \        $h-*  /
-         $g-----------*
-```
-
-Here `$g` and `$h` are fresh variables that are created to represent
-the LUB/GLB of things requiring inference.  This means that `V` and
-`Tainted` will look like:
-
-```
-V = {$a, $b, $g, $h, $x}
-Tainted($g) = Tainted($h) = { $a, $b, $h, $g, $x }
-```
-
-Therefore we replace both `$g` and `$h` with `$a`, and end up
-with the type `fn(&a, &a)`.
-
-### GLB
-
-The procedure for computing the GLB is similar.  The difference lies
-in computing the replacements for the various variables. For each
-region `R` that appears in the type `F`, we again compute `Tainted(R)`
-and examine the results:
-
-1. If `R` is not in `V`, it is not replaced.
-2. Else, if `Tainted(R)` contains only variables in `V`, and it
-   contains exactly one variable from the LHS and one variable from
-   the RHS, then `R` can be mapped to the bound version of the
-   variable from the LHS.
-3. Else, if `Tainted(R)` contains no variable from the LHS and no
-   variable from the RHS, then `R` can be mapped to itself.
-4. Else, `R` is mapped to a fresh bound variable.
-
-These rules are pretty complex.  Let's look at some examples to see
-how they play out.
-
-Out first example was `fn(&a)` and `fn(&X)`.  In this case, `&a` will
-be replaced with `$a` and we will ultimately compute a
-(pre-replacement) GLB type of `fn($g)` where `$g=LUB($a,&X)`.
-Therefore, `V={$a,$g}` and `Tainted($g)={$g,$a,&X}.  To find the
-replacement for `$g` we consult the rules above:
-- Rule (1) does not apply because `$g \in V`
-- Rule (2) does not apply because `&X \in Tainted($g)`
-- Rule (3) does not apply because `$a \in Tainted($g)`
-- Hence, by rule (4), we replace `$g` with a fresh bound variable `&z`.
-So our final result is `fn(&z)`, which is correct.
-
-The next example is `fn(&A)` and `fn(&Z)`. In this case, we will again
-have a (pre-replacement) GLB of `fn(&g)`, where `$g = LUB(&A,&Z)`.
-Therefore, `V={$g}` and `Tainted($g) = {$g, &A, &Z}`.  In this case,
-by rule (3), `$g` is mapped to itself, and hence the result is
-`fn($g)`.  This result is correct (in this case, at least), but it is
-indicative of a case that *can* lead us into concluding that there is
-no GLB when in fact a GLB does exist.  See the section "Questionable
-Results" below for more details.
-
-The next example is `fn(&a, &b)` and `fn(&c, &c)`. In this case, as
-before, we'll end up with `F=fn($g, $h)` where `Tainted($g) =
-Tainted($h) = {$g, $h, $a, $b, $c}`.  Only rule (4) applies and hence
-we'll select fresh bound variables `y` and `z` and wind up with
-`fn(&y, &z)`.
-
-For the last example, let's consider what may seem trivial, but is
-not: `fn(&a, &a)` and `fn(&b, &b)`.  In this case, we'll get `F=fn($g,
-$h)` where `Tainted($g) = {$g, $a, $x}` and `Tainted($h) = {$h, $a,
-$x}`.  Both of these sets contain exactly one bound variable from each
-side, so we'll map them both to `&a`, resulting in `fn(&a, &a)`, which
-is the desired result.
-
-### Shortcomings and correctness
-
-You may be wondering whether this algorithm is correct.  The answer is
-"sort of".  There are definitely cases where they fail to compute a
-result even though a correct result exists.  I believe, though, that
-if they succeed, then the result is valid, and I will attempt to
-convince you.  The basic argument is that the "pre-replacement" step
-computes a set of constraints.  The replacements, then, attempt to
-satisfy those constraints, using bound identifiers where needed.
-
-For now I will briefly go over the cases for LUB/GLB and identify
-their intent:
-
-- LUB:
-  - The region variables that are substituted in place of bound regions
-    are intended to collect constraints on those bound regions.
-  - If Tainted(R) contains only values in V, then this region is unconstrained
-    and can therefore be generalized, otherwise it cannot.
-- GLB:
-  - The region variables that are substituted in place of bound regions
-    are intended to collect constraints on those bound regions.
-  - If Tainted(R) contains exactly one variable from each side, and
-    only variables in V, that indicates that those two bound regions
-    must be equated.
-  - Otherwise, if Tainted(R) references any variables from left or right
-    side, then it is trying to combine a bound region with a free one or
-    multiple bound regions, so we need to select fresh bound regions.
-
-Sorry this is more of a shorthand to myself.  I will try to write up something
-more convincing in the future.
-
-#### Where are the algorithms wrong?
-
-- The pre-replacement computation can fail even though using a
-  bound-region would have succeeded.
-- We will compute GLB(fn(fn($a)), fn(fn($b))) as fn($c) where $c is the
-  GLB of $a and $b.  But if inference finds that $a and $b must be mapped
-  to regions without a GLB, then this is effectively a failure to compute
-  the GLB.  However, the result `fn<$c>(fn($c))` is a valid GLB.
+For a discussion on skolemization and higher-ranked subtyping, please
+see the module `middle::typeck::infer::higher_ranked::doc`.
 
 */
diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs
index c65a930195c..54fb7872f3b 100644
--- a/src/librustc/middle/typeck/infer/region_inference/mod.rs
+++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs
@@ -332,7 +332,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         ReInfer(ReSkolemized(sc, br))
     }
 
-    pub fn new_bound(&self, binder_id: ast::NodeId) -> Region {
+    pub fn new_bound(&self, debruijn: ty::DebruijnIndex) -> Region {
         // Creates a fresh bound variable for use in GLB computations.
         // See discussion of GLB computation in the large comment at
         // the top of this file for more details.
@@ -358,7 +358,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
             self.tcx.sess.bug("rollover in RegionInference new_bound()");
         }
 
-        ReLateBound(binder_id, BrFresh(sc))
+        ReLateBound(debruijn, BrFresh(sc))
     }
 
     fn values_are_none(&self) -> bool {
diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs
index 2763132024b..f85cb85ff21 100644
--- a/src/librustc/middle/typeck/infer/sub.rs
+++ b/src/librustc/middle/typeck/infer/sub.rs
@@ -12,18 +12,16 @@
 use middle::ty::{BuiltinBounds};
 use middle::ty;
 use middle::ty::TyVar;
-use middle::typeck::check::regionmanip::replace_late_bound_regions;
 use middle::typeck::infer::combine::*;
 use middle::typeck::infer::{cres, CresCompare};
 use middle::typeck::infer::equate::Equate;
-use middle::typeck::infer::LateBoundRegionConversionTime::FnType;
 use middle::typeck::infer::glb::Glb;
+use middle::typeck::infer::higher_ranked::HigherRankedRelations;
 use middle::typeck::infer::InferCtxt;
 use middle::typeck::infer::lub::Lub;
 use middle::typeck::infer::{TypeTrace, Subtype};
 use middle::typeck::infer::type_variable::{SubtypeOf, SupertypeOf};
-use util::common::{indenter};
-use util::ppaux::{bound_region_to_string, Repr};
+use util::ppaux::{Repr};
 
 use syntax::ast::{Onceness, FnStyle, MutImmutable, MutMutable};
 
@@ -65,16 +63,16 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
     fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
         debug!("{}.regions({}, {})",
                self.tag(),
-               a.repr(self.fields.infcx.tcx),
-               b.repr(self.fields.infcx.tcx));
-        self.fields.infcx.region_vars.make_subregion(Subtype(self.trace()), a, b);
+               a.repr(self.tcx()),
+               b.repr(self.tcx()));
+        self.infcx().region_vars.make_subregion(Subtype(self.trace()), a, b);
         Ok(a)
     }
 
     fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
         debug!("mts({} <: {})",
-               a.repr(self.fields.infcx.tcx),
-               b.repr(self.fields.infcx.tcx));
+               a.repr(self.tcx()),
+               b.repr(self.tcx()));
 
         if a.mutbl != b.mutbl {
             return Err(ty::terr_mutability);
@@ -123,7 +121,7 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
 
     fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
         debug!("{}.tys({}, {})", self.tag(),
-               a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
+               a.repr(self.tcx()), b.repr(self.tcx()));
         if a == b { return Ok(a); }
 
         let infcx = self.fields.infcx;
@@ -158,77 +156,11 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
     }
 
     fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
-        debug!("fn_sigs(a={}, b={})",
-               a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
-        let _indenter = indenter();
-
-        // Rather than checking the subtype relationship between `a` and `b`
-        // as-is, we need to do some extra work here in order to make sure
-        // that function subtyping works correctly with respect to regions
-        //
-        // Note: this is a subtle algorithm.  For a full explanation,
-        // please see the large comment in `region_inference.rs`.
-
-        // Make a mark so we can examine "all bindings that were
-        // created as part of this type comparison".
-        let mark = self.fields.infcx.region_vars.mark();
-
-        // First, we instantiate each bound region in the subtype with a fresh
-        // region variable.
-        let (a_sig, _) =
-            self.fields.infcx.replace_late_bound_regions_with_fresh_var(
-                a.binder_id, self.trace().span(), FnType, a);
-
-        // Second, we instantiate each bound region in the supertype with a
-        // fresh concrete region.
-        let (skol_map, b_sig) = {
-            replace_late_bound_regions(self.fields.infcx.tcx, b.binder_id, b, |br| {
-                let skol = self.fields.infcx.region_vars.new_skolemized(br);
-                debug!("Bound region {} skolemized to {}",
-                       bound_region_to_string(self.fields.infcx.tcx, "", false, br),
-                       skol);
-                skol
-            })
-        };
-
-        debug!("a_sig={}", a_sig.repr(self.fields.infcx.tcx));
-        debug!("b_sig={}", b_sig.repr(self.fields.infcx.tcx));
-
-        // Compare types now that bound regions have been replaced.
-        let sig = try!(super_fn_sigs(self, &a_sig, &b_sig));
-
-        // Presuming type comparison succeeds, we need to check
-        // that the skolemized regions do not "leak".
-        let new_vars =
-            self.fields.infcx.region_vars.vars_created_since_mark(mark);
-        for (&skol_br, &skol) in skol_map.iter() {
-            let tainted = self.fields.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; }
-                    }
-                };
-
-                // 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));
-                }
-            }
-        }
+        self.higher_ranked_sub(a, b)
+    }
 
-        return Ok(sig);
+    fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres<ty::TraitRef> {
+        self.higher_ranked_sub(a, b)
     }
 }
 
diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs
index 634b153a9ce..24d11b25a60 100644
--- a/src/librustc/middle/typeck/mod.rs
+++ b/src/librustc/middle/typeck/mod.rs
@@ -383,7 +383,6 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
                 fn_style: ast::NormalFn,
                 abi: abi::Rust,
                 sig: ty::FnSig {
-                    binder_id: main_id,
                     inputs: Vec::new(),
                     output: ty::FnConverging(ty::mk_nil(tcx)),
                     variadic: false
@@ -432,7 +431,6 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
                 fn_style: ast::NormalFn,
                 abi: abi::Rust,
                 sig: ty::FnSig {
-                    binder_id: start_id,
                     inputs: vec!(
                         ty::mk_int(),
                         ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8()))
diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs
index 2845e3954b5..2f72d3cf50d 100644
--- a/src/librustc/middle/typeck/rscope.rs
+++ b/src/librustc/middle/typeck/rscope.rs
@@ -10,9 +10,9 @@
 
 
 use middle::ty;
+use middle::ty_fold;
 
 use std::cell::Cell;
-use syntax::ast;
 use syntax::codemap::Span;
 
 /// Defines strategies for handling regions that are omitted.  For
@@ -104,14 +104,12 @@ impl RegionScope for SpecificRscope {
 /// A scope in which we generate anonymous, late-bound regions for
 /// omitted regions. This occurs in function signatures.
 pub struct BindingRscope {
-    binder_id: ast::NodeId,
     anon_bindings: Cell<uint>,
 }
 
 impl BindingRscope {
-    pub fn new(binder_id: ast::NodeId) -> BindingRscope {
+    pub fn new() -> BindingRscope {
         BindingRscope {
-            binder_id: binder_id,
             anon_bindings: Cell::new(0),
         }
     }
@@ -119,7 +117,7 @@ impl BindingRscope {
     fn next_region(&self) -> ty::Region {
         let idx = self.anon_bindings.get();
         self.anon_bindings.set(idx + 1);
-        ty::ReLateBound(self.binder_id, ty::BrAnon(idx))
+        ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrAnon(idx))
     }
 }
 
@@ -138,3 +136,40 @@ impl RegionScope for BindingRscope {
     }
 }
 
+/// A scope which simply shifts the Debruijn index of other scopes
+/// to account for binding levels.
+pub struct ShiftedRscope<'r> {
+    base_scope: &'r RegionScope+'r
+}
+
+impl<'r> ShiftedRscope<'r> {
+    pub fn new(base_scope: &'r RegionScope+'r) -> ShiftedRscope<'r> {
+        ShiftedRscope { base_scope: base_scope }
+    }
+}
+
+impl<'r> RegionScope for ShiftedRscope<'r> {
+    fn default_region_bound(&self, span: Span) -> Option<ty::Region>
+    {
+        self.base_scope.default_region_bound(span)
+            .map(|r| ty_fold::shift_region(r, 1))
+    }
+
+    fn anon_regions(&self,
+                    span: Span,
+                    count: uint)
+                    -> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>>
+    {
+        match self.base_scope.anon_regions(span, count) {
+            Ok(mut v) => {
+                for r in v.iter_mut() {
+                    *r = ty_fold::shift_region(*r, 1);
+                }
+                Ok(v)
+            }
+            Err(errs) => {
+                Err(errs)
+            }
+        }
+    }
+}
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index e6015bfc074..4ce783b37b7 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -252,8 +252,7 @@ pub fn vec_map_to_string<T>(ts: &[T], f: |t: &T| -> String) -> String {
 }
 
 pub fn fn_sig_to_string(cx: &ctxt, typ: &ty::FnSig) -> String {
-    format!("fn{}{} -> {}", typ.binder_id, typ.inputs.repr(cx),
-            typ.output.repr(cx))
+    format!("fn{} -> {}", typ.inputs.repr(cx), typ.output.repr(cx))
 }
 
 pub fn trait_ref_to_string(cx: &ctxt, trait_ref: &ty::TraitRef) -> String {
@@ -262,11 +261,11 @@ pub fn trait_ref_to_string(cx: &ctxt, trait_ref: &ty::TraitRef) -> String {
 
 pub fn ty_to_string(cx: &ctxt, typ: t) -> String {
     fn bare_fn_to_string(cx: &ctxt,
-                      fn_style: ast::FnStyle,
-                      abi: abi::Abi,
-                      ident: Option<ast::Ident>,
-                      sig: &ty::FnSig)
-                      -> String {
+                         fn_style: ast::FnStyle,
+                         abi: abi::Abi,
+                         ident: Option<ast::Ident>,
+                         sig: &ty::FnSig)
+                         -> String {
         let mut s = String::new();
         match fn_style {
             ast::NormalFn => {}
@@ -732,6 +731,9 @@ impl Repr for ty::ParamBounds {
 
 impl Repr for ty::TraitRef {
     fn repr(&self, tcx: &ctxt) -> String {
+        // when printing out the debug representation, we don't need
+        // to enumerate the `for<...>` etc because the debruijn index
+        // 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!("<{} : {}>",
@@ -922,6 +924,14 @@ impl Repr for ty::Generics {
     }
 }
 
+impl Repr for ty::GenericBounds {
+    fn repr(&self, tcx: &ctxt) -> String {
+        format!("GenericBounds(types: {}, regions: {})",
+                self.types.repr(tcx),
+                self.regions.repr(tcx))
+    }
+}
+
 impl Repr for ty::ItemVariances {
     fn repr(&self, tcx: &ctxt) -> String {
         format!("ItemVariances(types={}, \
@@ -1140,9 +1150,41 @@ impl UserString for ty::BuiltinBounds {
 
 impl UserString for ty::TraitRef {
     fn user_string(&self, tcx: &ctxt) -> String {
-        let base = ty::item_path_str(tcx, self.def_id);
+        // Replace any anonymous late-bound regions with named
+        // variants, using gensym'd identifiers, so that we can
+        // clearly differentiate between named and unnamed regions in
+        // 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| {
+            ty::ReLateBound(debruijn, match br {
+                ty::BrNamed(_, name) => {
+                    names.push(token::get_name(name));
+                    br
+                }
+                ty::BrAnon(_) |
+                ty::BrFresh(_) |
+                ty::BrEnv => {
+                    let name = token::gensym("r");
+                    names.push(token::get_name(name));
+                    ty::BrNamed(ast_util::local_def(ast::DUMMY_NODE_ID), name)
+                }
+            })
+        });
+        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 trait_def = ty::lookup_trait_def(tcx, self.def_id);
-        parameterized(tcx, base.as_slice(), &self.substs, &trait_def.generics)
+        parameterized(tcx, base.as_slice(), &trait_ref.substs, &trait_def.generics)
     }
 }
 
@@ -1301,3 +1343,8 @@ impl<A:Repr,B:Repr> Repr for (A,B) {
     }
 }
 
+impl<T:Repr> Repr for ty::Binder<T> {
+    fn repr(&self, tcx: &ctxt) -> String {
+        format!("Binder({})", self.value.repr(tcx))
+    }
+}
diff --git a/src/librustc_trans/driver/driver.rs b/src/librustc_trans/driver/driver.rs
index ca70a37bf3d..a0e2bf07b83 100644
--- a/src/librustc_trans/driver/driver.rs
+++ b/src/librustc_trans/driver/driver.rs
@@ -385,7 +385,7 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
     syntax::ext::mtwt::clear_tables();
 
     let named_region_map = time(time_passes, "lifetime resolution", (),
-                                |_| middle::resolve_lifetime::krate(&sess, krate));
+                                |_| middle::resolve_lifetime::krate(&sess, krate, &def_map));
 
     time(time_passes, "looking for entry point", (),
          |_| middle::entry::find_entry_point(&sess, &ast_map));
diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs
index 38bc8c99c0f..c9410d753ae 100644
--- a/src/librustc_trans/save/mod.rs
+++ b/src/librustc_trans/save/mod.rs
@@ -1095,7 +1095,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
                 fd: &'v ast::FnDecl,
                 b: &'v ast::Block,
                 s: Span,
-                _: NodeId) {
+                _: ast::NodeId) {
         if generated_code(s) {
             return;
         }
diff --git a/src/librustc_trans/test.rs b/src/librustc_trans/test.rs
index 4648021cc5f..a17702ac1dc 100644
--- a/src/librustc_trans/test.rs
+++ b/src/librustc_trans/test.rs
@@ -14,9 +14,6 @@
 
 */
 
-// This is only used by tests, hence allow dead code.
-#![allow(dead_code)]
-
 use driver::diagnostic;
 use driver::diagnostic::Emitter;
 use driver::driver;
@@ -25,17 +22,20 @@ use middle::region;
 use middle::resolve;
 use middle::resolve_lifetime;
 use middle::stability;
+use middle::subst;
+use middle::subst::Subst;
 use middle::ty;
 use middle::typeck::infer::combine::Combine;
 use middle::typeck::infer;
 use middle::typeck::infer::lub::Lub;
 use middle::typeck::infer::glb::Glb;
-use session::{mod, config};
+use session::{mod,config};
+use syntax::{abi, ast, ast_map, ast_util};
 use syntax::codemap;
 use syntax::codemap::{Span, CodeMap, DUMMY_SP};
 use syntax::diagnostic::{Level, RenderSpan, Bug, Fatal, Error, Warning, Note, Help};
-use syntax::{ast, ast_map};
-use util::ppaux::{ty_to_string, UserString};
+use syntax::parse::token;
+use util::ppaux::{ty_to_string, Repr, UserString};
 
 use arena::TypedArena;
 
@@ -97,12 +97,12 @@ fn errors(msgs: &[&str]) -> (Box<Emitter+Send>, uint) {
     (box ExpectErrorEmitter { messages: v } as Box<Emitter+Send>, msgs.len())
 }
 
-fn test_env(_test_name: &str,
-            source_string: &str,
+fn test_env(source_string: &str,
             (emitter, expected_err_count): (Box<Emitter+Send>, uint),
             body: |Env|) {
-    let options =
+    let mut options =
         config::basic_options();
+    options.debugging_opts |= config::VERBOSE;
     let codemap =
         CodeMap::new();
     let diagnostic_handler =
@@ -125,7 +125,7 @@ fn test_env(_test_name: &str,
     let lang_items = lang_items::collect_language_items(krate, &sess);
     let resolve::CrateMap { def_map, freevars, capture_mode_map, .. } =
         resolve::resolve_crate(&sess, &lang_items, krate);
-    let named_region_map = resolve_lifetime::krate(&sess, krate);
+    let named_region_map = resolve_lifetime::krate(&sess, krate, &def_map);
     let region_map = region::resolve_crate(&sess, krate);
     let stability_index = stability::Index::build(krate);
     let type_arena = TypedArena::new();
@@ -164,6 +164,7 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
                             sub: &[]}]});
     }
 
+    #[allow(dead_code)] // this seems like it could be useful, even if we don't use it now
     pub fn lookup_item(&self, names: &[String]) -> ast::NodeId {
         return match search_mod(self, &self.infcx.tcx.map.krate().module, 0, names) {
             Some(id) => id,
@@ -237,14 +238,6 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
         }
     }
 
-    pub fn assert_not_subtype(&self, a: ty::t, b: ty::t) {
-        if self.is_subtype(a, b) {
-            panic!("{} is a subtype of {}, but it shouldn't be",
-                  self.ty_to_string(a),
-                  self.ty_to_string(b));
-        }
-    }
-
     pub fn assert_eq(&self, a: ty::t, b: ty::t) {
         self.assert_subtype(a, b);
         self.assert_subtype(b, a);
@@ -255,36 +248,91 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
     }
 
     pub fn t_fn(&self,
-                binder_id: ast::NodeId,
                 input_tys: &[ty::t],
                 output_ty: ty::t)
                 -> ty::t
     {
-        ty::mk_ctor_fn(self.infcx.tcx, binder_id, input_tys, output_ty)
+        ty::mk_ctor_fn(self.infcx.tcx, input_tys, output_ty)
+    }
+
+    pub fn t_nil(&self) -> ty::t {
+        ty::mk_nil(self.infcx.tcx)
+    }
+
+    pub fn t_pair(&self, ty1: ty::t, ty2: ty::t) -> ty::t
+    {
+        ty::mk_tup(self.infcx.tcx, vec![ty1, ty2])
+    }
+
+    pub fn t_closure(&self,
+                     input_tys: &[ty::t],
+                     output_ty: ty::t,
+                     region_bound: ty::Region)
+                     -> ty::t
+    {
+        ty::mk_closure(self.infcx.tcx, ty::ClosureTy {
+            fn_style: ast::NormalFn,
+            onceness: ast::Many,
+            store: ty::RegionTraitStore(region_bound, ast::MutMutable),
+            bounds: ty::region_existential_bound(region_bound),
+            sig: ty::FnSig {
+                inputs: input_tys.to_vec(),
+                output: ty::FnConverging(output_ty),
+                variadic: false,
+            },
+            abi: abi::Rust,
+        })
+    }
+
+    pub fn t_param(&self, space: subst::ParamSpace, index: uint) -> ty::t {
+        ty::mk_param(self.infcx.tcx, space, index, ast_util::local_def(ast::DUMMY_NODE_ID))
+    }
+
+    pub fn re_early_bound(&self,
+                          space: subst::ParamSpace,
+                          index: uint,
+                          name: &'static str)
+                          -> ty::Region
+    {
+        let name = token::intern(name);
+        ty::ReEarlyBound(ast::DUMMY_NODE_ID, space, index, name)
+    }
+
+    pub fn re_late_bound_with_debruijn(&self, id: uint, debruijn: ty::DebruijnIndex) -> ty::Region {
+        ty::ReLateBound(debruijn, ty::BrAnon(id))
+    }
+
+    pub fn t_rptr(&self, r: ty::Region) -> ty::t {
+        ty::mk_imm_rptr(self.infcx.tcx, r, ty::mk_int())
     }
 
-    pub fn t_int(&self) -> ty::t {
-        ty::mk_int()
+    pub fn t_rptr_late_bound(&self, id: uint) -> ty::t {
+        ty::mk_imm_rptr(self.infcx.tcx,
+                        self.re_late_bound_with_debruijn(id, ty::DebruijnIndex::new(1)),
+                        ty::mk_int())
     }
 
-    pub fn t_rptr_late_bound(&self, binder_id: ast::NodeId, id: uint) -> ty::t {
-        ty::mk_imm_rptr(self.infcx.tcx, ty::ReLateBound(binder_id, ty::BrAnon(id)),
-                        self.t_int())
+    pub fn t_rptr_late_bound_with_debruijn(&self, id: uint, debruijn: ty::DebruijnIndex) -> ty::t {
+        ty::mk_imm_rptr(self.infcx.tcx,
+                        self.re_late_bound_with_debruijn(id, debruijn),
+                        ty::mk_int())
     }
 
     pub fn t_rptr_scope(&self, id: ast::NodeId) -> ty::t {
-        ty::mk_imm_rptr(self.infcx.tcx, ty::ReScope(id), self.t_int())
+        ty::mk_imm_rptr(self.infcx.tcx, ty::ReScope(id), ty::mk_int())
+    }
+
+    pub fn re_free(&self, nid: ast::NodeId, id: uint) -> ty::Region {
+        ty::ReFree(ty::FreeRegion {scope_id: nid,
+                                   bound_region: ty::BrAnon(id)})
     }
 
     pub fn t_rptr_free(&self, nid: ast::NodeId, id: uint) -> ty::t {
-        ty::mk_imm_rptr(self.infcx.tcx,
-                        ty::ReFree(ty::FreeRegion {scope_id: nid,
-                                                    bound_region: ty::BrAnon(id)}),
-                        self.t_int())
+        ty::mk_imm_rptr(self.infcx.tcx, self.re_free(nid, id), ty::mk_int())
     }
 
     pub fn t_rptr_static(&self) -> ty::t {
-        ty::mk_imm_rptr(self.infcx.tcx, ty::ReStatic, self.t_int())
+        ty::mk_imm_rptr(self.infcx.tcx, ty::ReStatic, ty::mk_int())
     }
 
     pub fn dummy_type_trace(&self) -> infer::TypeTrace {
@@ -301,10 +349,6 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
         Glb(self.infcx.combine_fields(true, trace))
     }
 
-    pub fn resolve_regions(&self) {
-        self.infcx.resolve_regions_and_report_errors();
-    }
-
     pub fn make_lub_ty(&self, t1: ty::t, t2: ty::t) -> ty::t {
         match self.lub().tys(t1, t2) {
             Ok(t) => t,
@@ -345,31 +389,11 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
             }
         }
     }
-
-    /// Checks that `LUB(t1,t2)` is undefined
-    pub fn check_no_lub(&self, t1: ty::t, t2: ty::t) {
-        match self.lub().tys(t1, t2) {
-            Err(_) => {}
-            Ok(t) => {
-                panic!("unexpected success computing LUB: {}", self.ty_to_string(t))
-            }
-        }
-    }
-
-    /// Checks that `GLB(t1,t2)` is undefined
-    pub fn check_no_glb(&self, t1: ty::t, t2: ty::t) {
-        match self.glb().tys(t1, t2) {
-            Err(_) => {}
-            Ok(t) => {
-                panic!("unexpected success computing GLB: {}", self.ty_to_string(t))
-            }
-        }
-    }
 }
 
 #[test]
 fn contravariant_region_ptr_ok() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
         env.create_simple_region_hierarchy();
         let t_rptr1 = env.t_rptr_scope(1);
         let t_rptr10 = env.t_rptr_scope(10);
@@ -381,8 +405,7 @@ fn contravariant_region_ptr_ok() {
 
 #[test]
 fn contravariant_region_ptr_err() {
-    test_env("contravariant_region_ptr",
-             EMPTY_SOURCE_STR,
+    test_env(EMPTY_SOURCE_STR,
              errors(&["lifetime mismatch"]),
              |env| {
                  env.create_simple_region_hierarchy();
@@ -398,114 +421,273 @@ fn contravariant_region_ptr_err() {
 
 #[test]
 fn lub_bound_bound() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
-        let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
-        let t_rptr_bound2 = env.t_rptr_late_bound(22, 2);
-        env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
-                      env.t_fn(22, &[t_rptr_bound2], env.t_int()),
-                      env.t_fn(22, &[t_rptr_bound1], env.t_int()));
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
+        let t_rptr_bound2 = env.t_rptr_late_bound(2);
+        env.check_lub(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_bound2], ty::mk_int()),
+                      env.t_fn(&[t_rptr_bound1], ty::mk_int()));
     })
 }
 
 #[test]
 fn lub_bound_free() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
-        let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+    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_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
-                      env.t_fn(22, &[t_rptr_free1], env.t_int()),
-                      env.t_fn(22, &[t_rptr_free1], env.t_int()));
+        env.check_lub(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_free1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_free1], ty::mk_int()));
     })
 }
 
 #[test]
 fn lub_bound_static() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
-        let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
         let t_rptr_static = env.t_rptr_static();
-        env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
-                      env.t_fn(22, &[t_rptr_static], env.t_int()),
-                      env.t_fn(22, &[t_rptr_static], env.t_int()));
+        env.check_lub(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_static], ty::mk_int()),
+                      env.t_fn(&[t_rptr_static], ty::mk_int()));
     })
 }
 
 #[test]
 fn lub_bound_bound_inverse_order() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
-        let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
-        let t_rptr_bound2 = env.t_rptr_late_bound(22, 2);
-        env.check_lub(env.t_fn(22, &[t_rptr_bound1, t_rptr_bound2], t_rptr_bound1),
-                      env.t_fn(22, &[t_rptr_bound2, t_rptr_bound1], t_rptr_bound1),
-                      env.t_fn(22, &[t_rptr_bound1, t_rptr_bound1], t_rptr_bound1));
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
+        let t_rptr_bound2 = env.t_rptr_late_bound(2);
+        env.check_lub(env.t_fn(&[t_rptr_bound1, t_rptr_bound2], t_rptr_bound1),
+                      env.t_fn(&[t_rptr_bound2, t_rptr_bound1], t_rptr_bound1),
+                      env.t_fn(&[t_rptr_bound1, t_rptr_bound1], t_rptr_bound1));
     })
 }
 
 #[test]
 fn lub_free_free() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
         let t_rptr_free1 = env.t_rptr_free(0, 1);
         let t_rptr_free2 = env.t_rptr_free(0, 2);
         let t_rptr_static = env.t_rptr_static();
-        env.check_lub(env.t_fn(22, &[t_rptr_free1], env.t_int()),
-                      env.t_fn(22, &[t_rptr_free2], env.t_int()),
-                      env.t_fn(22, &[t_rptr_static], env.t_int()));
+        env.check_lub(env.t_fn(&[t_rptr_free1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_free2], ty::mk_int()),
+                      env.t_fn(&[t_rptr_static], ty::mk_int()));
     })
 }
 
 #[test]
 fn lub_returning_scope() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR,
+    test_env(EMPTY_SOURCE_STR,
              errors(&["cannot infer an appropriate lifetime"]), |env| {
                  let t_rptr_scope10 = env.t_rptr_scope(10);
                  let t_rptr_scope11 = env.t_rptr_scope(11);
 
                  // this should generate an error when regions are resolved
-                 env.make_lub_ty(env.t_fn(22, &[], t_rptr_scope10),
-                                 env.t_fn(22, &[], t_rptr_scope11));
+                 env.make_lub_ty(env.t_fn(&[], t_rptr_scope10),
+                                 env.t_fn(&[], t_rptr_scope11));
              })
 }
 
 #[test]
 fn glb_free_free_with_common_scope() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
         let t_rptr_free1 = env.t_rptr_free(0, 1);
         let t_rptr_free2 = env.t_rptr_free(0, 2);
         let t_rptr_scope = env.t_rptr_scope(0);
-        env.check_glb(env.t_fn(22, &[t_rptr_free1], env.t_int()),
-                      env.t_fn(22, &[t_rptr_free2], env.t_int()),
-                      env.t_fn(22, &[t_rptr_scope], env.t_int()));
+        env.check_glb(env.t_fn(&[t_rptr_free1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_free2], ty::mk_int()),
+                      env.t_fn(&[t_rptr_scope], ty::mk_int()));
     })
 }
 
 #[test]
 fn glb_bound_bound() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
-        let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
-        let t_rptr_bound2 = env.t_rptr_late_bound(22, 2);
-        env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
-                      env.t_fn(22, &[t_rptr_bound2], env.t_int()),
-                      env.t_fn(22, &[t_rptr_bound1], env.t_int()));
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
+        let t_rptr_bound2 = env.t_rptr_late_bound(2);
+        env.check_glb(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_bound2], ty::mk_int()),
+                      env.t_fn(&[t_rptr_bound1], ty::mk_int()));
     })
 }
 
 #[test]
 fn glb_bound_free() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
-        let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+    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_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
-                      env.t_fn(22, &[t_rptr_free1], env.t_int()),
-                      env.t_fn(22, &[t_rptr_bound1], env.t_int()));
+        env.check_glb(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_free1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_bound1], ty::mk_int()));
     })
 }
 
 #[test]
 fn glb_bound_static() {
-    test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
-        let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
         let t_rptr_static = env.t_rptr_static();
-        env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
-                      env.t_fn(22, &[t_rptr_static], env.t_int()),
-                      env.t_fn(22, &[t_rptr_bound1], env.t_int()));
+        env.check_glb(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+                      env.t_fn(&[t_rptr_static], ty::mk_int()),
+                      env.t_fn(&[t_rptr_bound1], ty::mk_int()));
     })
 }
+
+#[test]
+fn subst_ty_renumber_bound() {
+    /*!
+     * Test substituting a bound region into a function, which introduces another
+     * level of binding. This requires adjusting the Debruijn index.
+     */
+
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        // Situation:
+        // Theta = [A -> &'a foo]
+
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
+
+        // t_source = fn(A)
+        let t_source = {
+            let t_param = env.t_param(subst::TypeSpace, 0);
+            env.t_fn(&[t_param], env.t_nil())
+        };
+
+        let substs = subst::Substs::new_type(vec![t_rptr_bound1], vec![]);
+        let t_substituted = t_source.subst(env.infcx.tcx, &substs);
+
+        // t_expected = fn(&'a int)
+        let t_expected = {
+            let t_ptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
+            env.t_fn(&[t_ptr_bound2], env.t_nil())
+        };
+
+        debug!("subst_bound: t_source={} substs={} t_substituted={} t_expected={}",
+               t_source.repr(env.infcx.tcx),
+               substs.repr(env.infcx.tcx),
+               t_substituted.repr(env.infcx.tcx),
+               t_expected.repr(env.infcx.tcx));
+
+        assert_eq!(t_substituted, t_expected);
+    })
+}
+
+#[test]
+fn subst_ty_renumber_some_bounds() {
+    /*!
+     * Test substituting a bound region into a function, which introduces another
+     * level of binding. This requires adjusting the Debruijn index.
+     */
+
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        // Situation:
+        // Theta = [A -> &'a foo]
+
+        let t_rptr_bound1 = env.t_rptr_late_bound(1);
+
+        // t_source = (A, fn(A))
+        let t_source = {
+            let t_param = env.t_param(subst::TypeSpace, 0);
+            env.t_pair(t_param, env.t_fn(&[t_param], env.t_nil()))
+        };
+
+        let substs = subst::Substs::new_type(vec![t_rptr_bound1], vec![]);
+        let t_substituted = t_source.subst(env.infcx.tcx, &substs);
+
+        // t_expected = (&'a int, fn(&'a int))
+        //
+        // but not that the Debruijn index is different in the different cases.
+        let t_expected = {
+            let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
+            env.t_pair(t_rptr_bound1, env.t_fn(&[t_rptr_bound2], env.t_nil()))
+        };
+
+        debug!("subst_bound: t_source={} substs={} t_substituted={} t_expected={}",
+               t_source.repr(env.infcx.tcx),
+               substs.repr(env.infcx.tcx),
+               t_substituted.repr(env.infcx.tcx),
+               t_expected.repr(env.infcx.tcx));
+
+        assert_eq!(t_substituted, t_expected);
+    })
+}
+
+#[test]
+fn escaping() {
+    /*!
+     * Test that we correctly compute whether a type has escaping
+     * regions or not.
+     */
+
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        // Situation:
+        // Theta = [A -> &'a foo]
+
+        assert!(!ty::type_has_escaping_regions(env.t_nil()));
+
+        let t_rptr_free1 = env.t_rptr_free(0, 1);
+        assert!(!ty::type_has_escaping_regions(t_rptr_free1));
+
+        let t_rptr_bound1 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(1));
+        assert!(ty::type_has_escaping_regions(t_rptr_bound1));
+
+        let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
+        assert!(ty::type_has_escaping_regions(t_rptr_bound2));
+
+        // t_fn = fn(A)
+        let t_param = env.t_param(subst::TypeSpace, 0);
+        assert!(!ty::type_has_escaping_regions(t_param));
+        let t_fn = env.t_fn(&[t_param], env.t_nil());
+        assert!(!ty::type_has_escaping_regions(t_fn));
+
+        // t_fn = |&int|+'a
+        let t_fn = env.t_closure(&[t_rptr_bound1], env.t_nil(), env.re_free(0, 1));
+        assert!(!ty::type_has_escaping_regions(t_fn));
+
+        // t_fn = |&int|+'a (where &int has depth 2)
+        let t_fn = env.t_closure(&[t_rptr_bound2], env.t_nil(), env.re_free(0, 1));
+        assert!(ty::type_has_escaping_regions(t_fn));
+
+        // t_fn = |&int|+&int
+        let t_fn = env.t_closure(&[t_rptr_bound1], env.t_nil(),
+                                 env.re_late_bound_with_debruijn(1, ty::DebruijnIndex::new(1)));
+        assert!(ty::type_has_escaping_regions(t_fn));
+    })
+}
+
+#[test]
+fn subst_region_renumber_region() {
+    /*!
+     * Test applying a substitution where the value being substituted
+     * for an early-bound region is a late-bound region.
+     */
+
+    test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+        let re_bound1 = env.re_late_bound_with_debruijn(1, ty::DebruijnIndex::new(1));
+
+        // type t_source<'a> = fn(&'a int)
+        let t_source = {
+            let re_early = env.re_early_bound(subst::TypeSpace, 0, "'a");
+            env.t_fn(&[env.t_rptr(re_early)], env.t_nil())
+        };
+
+        let substs = subst::Substs::new_type(vec![], vec![re_bound1]);
+        let t_substituted = t_source.subst(env.infcx.tcx, &substs);
+
+        // t_expected = fn(&'a int)
+        //
+        // but not that the Debruijn index is different in the different cases.
+        let t_expected = {
+            let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
+            env.t_fn(&[t_rptr_bound2], env.t_nil())
+        };
+
+        debug!("subst_bound: t_source={} substs={} t_substituted={} t_expected={}",
+               t_source.repr(env.infcx.tcx),
+               substs.repr(env.infcx.tcx),
+               t_substituted.repr(env.infcx.tcx),
+               t_expected.repr(env.infcx.tcx));
+
+        assert_eq!(t_substituted, t_expected);
+    })
+}
+
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 39474a99f6d..9c4a532790d 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -519,8 +519,9 @@ pub fn get_res_dtor(ccx: &CrateContext,
         let name = csearch::get_symbol(&ccx.sess().cstore, did);
         let class_ty = ty::lookup_item_type(tcx, parent_id).ty.subst(tcx, substs);
         let llty = type_of_dtor(ccx, class_ty);
-        let dtor_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID,
-                                     &[glue::get_drop_glue_type(ccx, t)], ty::mk_nil(ccx.tcx()));
+        let dtor_ty = ty::mk_ctor_fn(ccx.tcx(),
+                                     &[glue::get_drop_glue_type(ccx, t)],
+                                     ty::mk_nil(ccx.tcx()));
         get_extern_fn(ccx,
                       &mut *ccx.externs().borrow_mut(),
                       name.as_slice(),
diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs
index 0544c9d43a8..de49754fe7f 100644
--- a/src/librustc_trans/trans/callee.rs
+++ b/src/librustc_trans/trans/callee.rs
@@ -269,7 +269,6 @@ pub fn trans_unboxing_shim(bcx: Block,
     let self_type = fty.sig.inputs[0];
     let boxed_self_type = ty::mk_uniq(tcx, self_type);
     let boxed_function_type = ty::FnSig {
-        binder_id: fty.sig.binder_id,
         inputs: fty.sig.inputs.iter().enumerate().map(|(i, typ)| {
             if i == 0 {
                 boxed_self_type
@@ -294,7 +293,6 @@ pub fn trans_unboxing_shim(bcx: Block,
             // RustCall so the untupled arguments can be passed
             // through verbatim.  This is kind of ugly.
             let fake_ty = ty::FnSig {
-                binder_id: fty.sig.binder_id,
                 inputs: type_of::untuple_arguments_if_necessary(ccx,
                                                                 fty.sig.inputs.as_slice(),
                                                                 fty.abi),
@@ -434,6 +432,8 @@ pub fn trans_fn_ref_with_substs(
            substs.repr(tcx));
 
     assert!(substs.types.all(|t| !ty::type_needs_infer(*t)));
+    assert!(substs.types.all(|t| !ty::type_has_escaping_regions(*t)));
+    let substs = substs.erase_regions();
 
     // Load the info for the appropriate trait if necessary.
     match ty::trait_of_item(tcx, def_id) {
@@ -467,13 +467,13 @@ pub fn trans_fn_ref_with_substs(
             let impl_or_trait_item = ty::impl_or_trait_item(tcx, source_id);
             match impl_or_trait_item {
                 ty::MethodTraitItem(method) => {
-                    let trait_ref = ty::impl_trait_ref(tcx, impl_id)
-                        .expect("could not find trait_ref for impl with \
-                                 default methods");
+                    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 = make_substs_for_receiver_types(
-                        tcx, &*trait_ref, &*method);
+                    let first_subst =
+                        make_substs_for_receiver_types(tcx, &*trait_ref, &*method)
+                        .erase_regions();
 
                     // And compose them
                     let new_substs = first_subst.subst(tcx, &substs);
@@ -663,7 +663,7 @@ pub fn trans_lang_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                 trans_fn_ref_with_substs_to_callee(bcx,
                                                                    did,
                                                                    0,
-                                                                   subst::Substs::empty())
+                                                                   subst::Substs::trans_empty())
                              },
                              ArgVals(args),
                              dest)
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs
index 139087507ff..3cb823aec34 100644
--- a/src/librustc_trans/trans/closure.rs
+++ b/src/librustc_trans/trans/closure.rs
@@ -486,7 +486,7 @@ pub fn trans_unboxed_closure<'blk, 'tcx>(
     let llfn = get_or_create_declaration_if_unboxed_closure(
         bcx,
         closure_id,
-        &bcx.fcx.param_substs.substs).unwrap();
+        bcx.fcx.param_substs.substs()).unwrap();
 
     let function_type = (*bcx.tcx().unboxed_closures.borrow())[closure_id]
                                                               .closure_type
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index d06cfa4a027..21cf3138661 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -191,10 +191,21 @@ pub type ExternMap = FnvHashMap<String, ValueRef>;
 // Here `self_ty` is the real type of the self parameter to this method. It
 // will only be set in the case of default methods.
 pub struct param_substs {
-    pub substs: subst::Substs,
+    substs: subst::Substs,
 }
 
 impl param_substs {
+    pub fn new(substs: subst::Substs) -> param_substs {
+        assert!(substs.types.all(|t| !ty::type_needs_infer(*t)));
+        assert!(substs.types.all(|t| !ty::type_has_params(*t)));
+        assert!(substs.types.all(|t| !ty::type_has_escaping_regions(*t)));
+        param_substs { substs: substs.erase_regions() }
+    }
+
+    pub fn substs(&self) -> &subst::Substs {
+        &self.substs
+    }
+
     pub fn empty() -> param_substs {
         param_substs {
             substs: subst::Substs::trans_empty(),
@@ -822,6 +833,8 @@ pub fn fulfill_obligation(ccx: &CrateContext,
         None => { }
     }
 
+    debug!("trans fulfill_obligation: trait_ref={}", trait_ref.repr(ccx.tcx()));
+
     ty::populate_implementations_for_trait_if_necessary(tcx, trait_ref.def_id);
     let infcx = infer::new_infer_ctxt(tcx);
 
diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs
index e3e36ee53fd..65fd9566760 100644
--- a/src/librustc_trans/trans/debuginfo.rs
+++ b/src/librustc_trans/trans/debuginfo.rs
@@ -1410,7 +1410,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
                                file_metadata: DIFile,
                                name_to_append_suffix_to: &mut String)
                                -> DIArray {
-        let self_type = param_substs.substs.self_ty();
+        let self_type = param_substs.substs().self_ty();
 
         // Only true for static default methods:
         let has_self_type = self_type.is_some();
@@ -1467,7 +1467,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
         }
 
         // Handle other generic parameters
-        let actual_types = param_substs.substs.types.get_slice(subst::FnSpace);
+        let actual_types = param_substs.substs().types.get_slice(subst::FnSpace);
         for (index, &ast::TyParam{ ident, .. }) in generics.ty_params.iter().enumerate() {
             let actual_type = actual_types[index];
             // Add actual type name to <...> clause of function name
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 2b7c3d0c24f..40a4d6047aa 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -329,12 +329,12 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                           bcx.ty_to_string(unsized_ty)).as_slice())
             },
             &ty::UnsizeVtable(ty::TyTrait { ref principal, .. }, _) => {
-                let substs = principal.substs.with_self_ty(unsized_ty);
+                let substs = principal.substs.with_self_ty(unsized_ty).erase_regions();
                 let trait_ref =
                     Rc::new(ty::TraitRef { def_id: principal.def_id,
                                            substs: substs });
                 let trait_ref =
-                    trait_ref.subst(bcx.tcx(), &bcx.fcx.param_substs.substs);
+                    trait_ref.subst(bcx.tcx(), bcx.fcx.param_substs.substs());
                 let box_ty = mk_ty(unsized_ty);
                 PointerCast(bcx,
                             meth::get_vtable(bcx, box_ty, trait_ref),
@@ -1122,7 +1122,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                              .map(|t| (*t).clone())
                                              .unwrap();
                 let trait_ref =
-                    trait_ref.subst(bcx.tcx(), &bcx.fcx.param_substs.substs);
+                    trait_ref.subst(bcx.tcx(), bcx.fcx.param_substs.substs());
                 let datum = unpack_datum!(bcx, trans(bcx, &**val));
                 meth::trans_trait_cast(bcx, datum, expr.id,
                                        trait_ref, dest)
diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs
index 3ca37f9e355..f86a0994bf9 100644
--- a/src/librustc_trans/trans/foreign.rs
+++ b/src/librustc_trans/trans/foreign.rs
@@ -536,7 +536,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
     let _icx = push_ctxt("foreign::build_foreign_fn");
 
     let fnty = ty::node_id_to_type(ccx.tcx(), id);
-    let mty = fnty.subst(ccx.tcx(), &param_substs.substs);
+    let mty = fnty.subst(ccx.tcx(), param_substs.substs());
     let tys = foreign_types_for_fn_ty(ccx, mty);
 
     unsafe { // unsafe because we call LLVM operations
@@ -558,7 +558,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
         let _icx = push_ctxt("foreign::foreign::build_rust_fn");
         let tcx = ccx.tcx();
         let t = ty::node_id_to_type(tcx, id).subst(
-            ccx.tcx(), &param_substs.substs);
+            ccx.tcx(), param_substs.substs());
 
         let ps = ccx.tcx().map.with_path(id, |path| {
             let abi = Some(ast_map::PathName(special_idents::clownshoe_abi.name));
diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs
index cceea96d4c1..5188ca77350 100644
--- a/src/librustc_trans/trans/glue.rs
+++ b/src/librustc_trans/trans/glue.rs
@@ -287,8 +287,9 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                              val, *ty);
         }
 
-        let dtor_ty = ty::mk_ctor_fn(variant_cx.tcx(), ast::DUMMY_NODE_ID,
-                                     &[get_drop_glue_type(bcx.ccx(), t)], ty::mk_nil(bcx.tcx()));
+        let dtor_ty = ty::mk_ctor_fn(bcx.tcx(),
+                                     &[get_drop_glue_type(bcx.ccx(), t)],
+                                     ty::mk_nil(bcx.tcx()));
         let (_, variant_cx) = invoke(variant_cx, dtor_addr, args, dtor_ty, None, false);
 
         variant_cx.fcx.pop_and_trans_custom_cleanup_scope(variant_cx, field_scope);
diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs
index 55ecd8a6554..90777afff7e 100644
--- a/src/librustc_trans/trans/meth.rs
+++ b/src/librustc_trans/trans/meth.rs
@@ -137,8 +137,11 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         }) => {
             let trait_ref =
                 Rc::new(trait_ref.subst(bcx.tcx(),
-                                        &bcx.fcx.param_substs.substs));
+                                        bcx.fcx.param_substs.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());
@@ -609,9 +612,6 @@ pub fn get_vtable(bcx: Block,
                             fn_style: closure_info.closure_type.fn_style,
                             abi: Rust,
                             sig: ty::FnSig {
-                                binder_id: closure_info.closure_type
-                                                       .sig
-                                                       .binder_id,
                                 inputs: new_inputs,
                                 output: new_output,
                                 variadic: false,
diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs
index 077c1337a44..52aa81fa427 100644
--- a/src/librustc_trans/trans/monomorphize.rs
+++ b/src/librustc_trans/trans/monomorphize.rs
@@ -63,9 +63,8 @@ pub fn monomorphic_fn(ccx: &CrateContext,
         None => ()
     }
 
-    let psubsts = param_substs {
-        substs: (*real_substs).clone(),
-    };
+    debug!("creating param_substs with real_substs={}", real_substs.repr(ccx.tcx()));
+    let psubsts = param_substs::new((*real_substs).clone());
 
     debug!("monomorphic_fn(\
             fn_id={}, \
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 0cb80d4c153..15e14902727 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1154,7 +1154,7 @@ pub enum Ty_ {
     /// Type parameters are stored in the Path itself
     TyPath(Path, Option<TyParamBounds>, NodeId), // for #7264; see above
     /// A type like `for<'a> Foo<&'a Bar>`
-    TyPolyTraitRef(P<PolyTraitRef>),
+    TyPolyTraitRef(TyParamBounds),
     /// A "qualified path", e.g. `<Vec<T> as SomeTrait>::SomeType`
     TyQPath(P<QPath>),
     /// No-op; kept solely so that we can pretty-print faithfully
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 2e3a15bfd4b..30cdecbc851 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -494,10 +494,10 @@ impl<'a, 'v, O: IdVisitingOperation> Visitor<'v> for IdVisitor<'a, O> {
         }
 
         visit::walk_fn(self,
-                        function_kind,
-                        function_declaration,
-                        block,
-                        span);
+                       function_kind,
+                       function_declaration,
+                       block,
+                       span);
 
         if !self.pass_through_items {
             match function_kind {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 0c31e9ae01d..ebdcf278934 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -58,8 +58,6 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
     ("quote", Active),
     ("linkage", Active),
     ("struct_inherit", Removed),
-    ("overloaded_calls", Active),
-    ("unboxed_closure_sugar", Active),
 
     ("quad_precision_float", Removed),
 
@@ -102,7 +100,7 @@ enum Status {
 /// A set of features to be used by later passes.
 pub struct Features {
     pub default_type_params: bool,
-    pub overloaded_calls: bool,
+    pub unboxed_closures: bool,
     pub rustc_diagnostic_macros: bool,
     pub import_shadowing: bool,
     pub visible_private_types: bool,
@@ -113,7 +111,7 @@ impl Features {
     pub fn new() -> Features {
         Features {
             default_type_params: false,
-            overloaded_calls: false,
+            unboxed_closures: false,
             rustc_diagnostic_macros: false,
             import_shadowing: false,
             visible_private_types: false,
@@ -381,7 +379,7 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
                 fn_decl: &'v ast::FnDecl,
                 block: &'v ast::Block,
                 span: Span,
-                _: NodeId) {
+                _node_id: NodeId) {
         match fn_kind {
             visit::FkItemFn(_, _, _, abi) if abi == RustIntrinsic => {
                 self.gate_feature("intrinsics",
@@ -392,6 +390,19 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
         }
         visit::walk_fn(self, fn_kind, fn_decl, block, span);
     }
+
+    fn visit_path_parameters(&mut self, path_span: Span, parameters: &'v ast::PathParameters) {
+        match *parameters {
+            ast::ParenthesizedParameters(..) => {
+                self.gate_feature("unboxed_closures",
+                                  path_span,
+                                  "parenthetical parameter notation is subject to change");
+            }
+            ast::AngleBracketedParameters(..) => { }
+        }
+
+        visit::walk_path_parameters(self, path_span, parameters)
+    }
 }
 
 pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features, Vec<Span>) {
@@ -446,7 +457,7 @@ pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features,
 
     (Features {
         default_type_params: cx.has_feature("default_type_params"),
-        overloaded_calls: cx.has_feature("overloaded_calls"),
+        unboxed_closures: cx.has_feature("unboxed_closures"),
         rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
         import_shadowing: cx.has_feature("import_shadowing"),
         visible_private_types: cx.has_feature("visible_private_types"),
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 56d91282437..b3137ff5f7e 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -445,10 +445,12 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
             TyFixedLengthVec(ty, e) => {
                 TyFixedLengthVec(fld.fold_ty(ty), fld.fold_expr(e))
             }
-            TyTypeof(expr) => TyTypeof(fld.fold_expr(expr)),
-            TyPolyTraitRef(poly_trait_ref) => {
-                TyPolyTraitRef(poly_trait_ref.map(|p| fld.fold_poly_trait_ref(p)))
-            },
+            TyTypeof(expr) => {
+                TyTypeof(fld.fold_expr(expr))
+            }
+            TyPolyTraitRef(bounds) => {
+                TyPolyTraitRef(bounds.move_map(|b| fld.fold_ty_param_bound(b)))
+            }
         },
         span: fld.new_span(span)
     })
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 98479d65cbb..40c4ac9f8c0 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1023,10 +1023,21 @@ impl<'a> Parser<'a> {
             self.parse_ty_bare_fn_or_ty_closure(lifetime_defs)
         } else if self.token == token::ModSep ||
                   self.token.is_ident() ||
-                  self.token.is_path() {
+                  self.token.is_path()
+        {
             let trait_ref = self.parse_trait_ref();
-            TyPolyTraitRef(P(PolyTraitRef { bound_lifetimes: lifetime_defs,
-                                            trait_ref: trait_ref }))
+            let poly_trait_ref = ast::PolyTraitRef { bound_lifetimes: lifetime_defs,
+                                                     trait_ref: trait_ref };
+            let other_bounds = if self.eat(&token::BinOp(token::Plus)) {
+                self.parse_ty_param_bounds()
+            } else {
+                OwnedSlice::empty()
+            };
+            let all_bounds =
+                Some(TraitTyParamBound(poly_trait_ref)).into_iter()
+                .chain(other_bounds.into_vec().into_iter())
+                .collect();
+            ast::TyPolyTraitRef(all_bounds)
         } else {
             self.parse_ty_closure(lifetime_defs)
         }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 0543b80f208..e6e0c33a42d 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -739,8 +739,8 @@ impl<'a> State<'a> {
             ast::TyPath(ref path, ref bounds, _) => {
                 try!(self.print_bounded_path(path, bounds));
             }
-            ast::TyPolyTraitRef(ref poly_trait_ref) => {
-                try!(self.print_poly_trait_ref(&**poly_trait_ref));
+            ast::TyPolyTraitRef(ref bounds) => {
+                try!(self.print_bounds("", bounds));
             }
             ast::TyQPath(ref qpath) => {
                 try!(word(&mut self.s, "<"));
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 2960c28a8b7..efe1e18eda9 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -78,6 +78,9 @@ pub trait Visitor<'v> {
     fn visit_ty_method(&mut self, t: &'v TypeMethod) { walk_ty_method(self, t) }
     fn visit_trait_item(&mut self, t: &'v TraitItem) { walk_trait_item(self, t) }
     fn visit_trait_ref(&mut self, t: &'v TraitRef) { walk_trait_ref(self, t) }
+    fn visit_ty_param_bound(&mut self, bounds: &'v TyParamBound) {
+        walk_ty_param_bound(self, bounds)
+    }
     fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef) {
         walk_poly_trait_ref(self, t)
     }
@@ -119,6 +122,12 @@ pub trait Visitor<'v> {
     fn visit_path(&mut self, path: &'v Path, _id: ast::NodeId) {
         walk_path(self, path)
     }
+    fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v PathSegment) {
+        walk_path_segment(self, path_span, path_segment)
+    }
+    fn visit_path_parameters(&mut self, path_span: Span, path_parameters: &'v PathParameters) {
+        walk_path_parameters(self, path_span, path_parameters)
+    }
     fn visit_attribute(&mut self, _attr: &'v Attribute) {}
 }
 
@@ -170,7 +179,7 @@ pub fn walk_view_item<'v, V: Visitor<'v>>(visitor: &mut V, vi: &'v ViewItem) {
                 ViewPathGlob(ref path, id) => {
                     visitor.visit_path(path, id);
                 }
-                ViewPathList(ref path, ref list, _) => {
+                ViewPathList(ref prefix, ref list, _) => {
                     for id in list.iter() {
                         match id.node {
                             PathListIdent { name, .. } => {
@@ -179,7 +188,10 @@ pub fn walk_view_item<'v, V: Visitor<'v>>(visitor: &mut V, vi: &'v ViewItem) {
                             PathListMod { .. } => ()
                         }
                     }
-                    walk_path(visitor, path);
+
+                    // Note that the `prefix` here is not a complete
+                    // path, so we don't use `visit_path`.
+                    walk_path(visitor, prefix);
                 }
             }
         }
@@ -212,7 +224,7 @@ pub fn walk_poly_trait_ref<'v, V>(visitor: &mut V,
                                          trait_ref: &'v PolyTraitRef)
     where V: Visitor<'v>
 {
-    walk_lifetime_decls(visitor, &trait_ref.bound_lifetimes);
+    walk_lifetime_decls_helper(visitor, &trait_ref.bound_lifetimes);
     visitor.visit_trait_ref(&trait_ref.trait_ref);
 }
 
@@ -290,7 +302,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
         }
         ItemTrait(ref generics, _, ref bounds, ref methods) => {
             visitor.visit_generics(generics);
-            walk_ty_param_bounds(visitor, bounds);
+            walk_ty_param_bounds_helper(visitor, bounds);
             for method in methods.iter() {
                 visitor.visit_trait_item(method)
             }
@@ -363,29 +375,29 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
                 visitor.visit_ty(&*argument.ty)
             }
             walk_fn_ret_ty(visitor, &function_declaration.decl.output);
-            walk_ty_param_bounds(visitor, &function_declaration.bounds);
-            walk_lifetime_decls(visitor, &function_declaration.lifetimes);
+            walk_ty_param_bounds_helper(visitor, &function_declaration.bounds);
+            walk_lifetime_decls_helper(visitor, &function_declaration.lifetimes);
         }
         TyProc(ref function_declaration) => {
             for argument in function_declaration.decl.inputs.iter() {
                 visitor.visit_ty(&*argument.ty)
             }
             walk_fn_ret_ty(visitor, &function_declaration.decl.output);
-            walk_ty_param_bounds(visitor, &function_declaration.bounds);
-            walk_lifetime_decls(visitor, &function_declaration.lifetimes);
+            walk_ty_param_bounds_helper(visitor, &function_declaration.bounds);
+            walk_lifetime_decls_helper(visitor, &function_declaration.lifetimes);
         }
         TyBareFn(ref function_declaration) => {
             for argument in function_declaration.decl.inputs.iter() {
                 visitor.visit_ty(&*argument.ty)
             }
             walk_fn_ret_ty(visitor, &function_declaration.decl.output);
-            walk_lifetime_decls(visitor, &function_declaration.lifetimes);
+            walk_lifetime_decls_helper(visitor, &function_declaration.lifetimes);
         }
         TyPath(ref path, ref opt_bounds, id) => {
             visitor.visit_path(path, id);
             match *opt_bounds {
                 Some(ref bounds) => {
-                    walk_ty_param_bounds(visitor, bounds);
+                    walk_ty_param_bounds_helper(visitor, bounds);
                 }
                 None => { }
             }
@@ -399,8 +411,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
             visitor.visit_ty(&**ty);
             visitor.visit_expr(&**expression)
         }
-        TyPolyTraitRef(ref poly_trait_ref) => {
-            visitor.visit_poly_trait_ref(&**poly_trait_ref)
+        TyPolyTraitRef(ref bounds) => {
+            walk_ty_param_bounds_helper(visitor, bounds)
         }
         TyTypeof(ref expression) => {
             visitor.visit_expr(&**expression)
@@ -409,8 +421,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
     }
 }
 
-fn walk_lifetime_decls<'v, V: Visitor<'v>>(visitor: &mut V,
-                                           lifetimes: &'v Vec<LifetimeDef>) {
+pub fn walk_lifetime_decls_helper<'v, V: Visitor<'v>>(visitor: &mut V,
+                                                      lifetimes: &'v Vec<LifetimeDef>) {
     for l in lifetimes.iter() {
         visitor.visit_lifetime_decl(l);
     }
@@ -418,24 +430,35 @@ fn walk_lifetime_decls<'v, V: Visitor<'v>>(visitor: &mut V,
 
 pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path) {
     for segment in path.segments.iter() {
-        visitor.visit_ident(path.span, segment.identifier);
+        visitor.visit_path_segment(path.span, segment);
+    }
+}
 
-        match segment.parameters {
-            ast::AngleBracketedParameters(ref data) => {
-                for typ in data.types.iter() {
-                    visitor.visit_ty(&**typ);
-                }
-                for lifetime in data.lifetimes.iter() {
-                    visitor.visit_lifetime_ref(lifetime);
-                }
+pub fn walk_path_segment<'v, V: Visitor<'v>>(visitor: &mut V,
+                                             path_span: Span,
+                                             segment: &'v PathSegment) {
+    visitor.visit_ident(path_span, segment.identifier);
+    visitor.visit_path_parameters(path_span, &segment.parameters);
+}
+
+pub fn walk_path_parameters<'v, V: Visitor<'v>>(visitor: &mut V,
+                                                _path_span: Span,
+                                                path_parameters: &'v PathParameters) {
+    match *path_parameters {
+        ast::AngleBracketedParameters(ref data) => {
+            for typ in data.types.iter() {
+                visitor.visit_ty(&**typ);
             }
-            ast::ParenthesizedParameters(ref data) => {
-                for typ in data.inputs.iter() {
-                    visitor.visit_ty(&**typ);
-                }
-                for typ in data.output.iter() {
-                    visitor.visit_ty(&**typ);
-                }
+            for lifetime in data.lifetimes.iter() {
+                visitor.visit_lifetime_ref(lifetime);
+            }
+        }
+        ast::ParenthesizedParameters(ref data) => {
+            for typ in data.inputs.iter() {
+                visitor.visit_ty(&**typ);
+            }
+            for typ in data.output.iter() {
+                visitor.visit_ty(&**typ);
             }
         }
     }
@@ -511,32 +534,37 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V,
     }
 }
 
-pub fn walk_ty_param_bounds<'v, V: Visitor<'v>>(visitor: &mut V,
-                                                bounds: &'v OwnedSlice<TyParamBound>) {
+pub fn walk_ty_param_bounds_helper<'v, V: Visitor<'v>>(visitor: &mut V,
+                                                       bounds: &'v OwnedSlice<TyParamBound>) {
     for bound in bounds.iter() {
-        match *bound {
-            TraitTyParamBound(ref typ) => {
-                visitor.visit_poly_trait_ref(typ)
-            }
-            RegionTyParamBound(ref lifetime) => {
-                visitor.visit_lifetime_ref(lifetime);
-            }
+        visitor.visit_ty_param_bound(bound)
+    }
+}
+
+pub fn walk_ty_param_bound<'v, V: Visitor<'v>>(visitor: &mut V,
+                                               bound: &'v TyParamBound) {
+    match *bound {
+        TraitTyParamBound(ref typ) => {
+            visitor.visit_poly_trait_ref(typ);
+        }
+        RegionTyParamBound(ref lifetime) => {
+            visitor.visit_lifetime_ref(lifetime);
         }
     }
 }
 
 pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics) {
     for type_parameter in generics.ty_params.iter() {
-        walk_ty_param_bounds(visitor, &type_parameter.bounds);
+        walk_ty_param_bounds_helper(visitor, &type_parameter.bounds);
         match type_parameter.default {
             Some(ref ty) => visitor.visit_ty(&**ty),
             None => {}
         }
     }
-    walk_lifetime_decls(visitor, &generics.lifetimes);
+    walk_lifetime_decls_helper(visitor, &generics.lifetimes);
     for predicate in generics.where_clause.predicates.iter() {
         visitor.visit_ident(predicate.span, predicate.ident);
-        walk_ty_param_bounds(visitor, &predicate.bounds);
+        walk_ty_param_bounds_helper(visitor, &predicate.bounds);
     }
 }
 
diff --git a/src/test/auxiliary/issue-18711.rs b/src/test/auxiliary/issue-18711.rs
index 454e67b8bd5..54f1595780d 100644
--- a/src/test/auxiliary/issue-18711.rs
+++ b/src/test/auxiliary/issue-18711.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(unboxed_closures, overloaded_calls)]
+#![feature(unboxed_closures)]
 #![crate_type = "rlib"]
 
 pub fn inner<F>(f: F) -> F {
diff --git a/src/test/auxiliary/unboxed-closures-cross-crate.rs b/src/test/auxiliary/unboxed-closures-cross-crate.rs
index d04829bb808..9a6a2c7495b 100644
--- a/src/test/auxiliary/unboxed-closures-cross-crate.rs
+++ b/src/test/auxiliary/unboxed-closures-cross-crate.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(unboxed_closures, overloaded_calls)]
+#![feature(unboxed_closures)]
 
 #[inline]
 pub fn has_closures() -> uint {
diff --git a/src/test/bench/shootout-reverse-complement.rs b/src/test/bench/shootout-reverse-complement.rs
index d7d8e94c8a7..ffe5739e0bb 100644
--- a/src/test/bench/shootout-reverse-complement.rs
+++ b/src/test/bench/shootout-reverse-complement.rs
@@ -40,7 +40,7 @@
 
 // ignore-android see #10393 #13206
 
-#![feature(slicing_syntax, unboxed_closures, overloaded_calls)]
+#![feature(slicing_syntax, unboxed_closures)]
 
 extern crate libc;
 
diff --git a/src/test/bench/shootout-spectralnorm.rs b/src/test/bench/shootout-spectralnorm.rs
index c5f2fbb1890..acb289aa3ad 100644
--- a/src/test/bench/shootout-spectralnorm.rs
+++ b/src/test/bench/shootout-spectralnorm.rs
@@ -41,7 +41,7 @@
 // no-pretty-expanded FIXME #15189
 
 #![allow(non_snake_case)]
-#![feature(unboxed_closures, overloaded_calls)]
+#![feature(unboxed_closures)]
 
 use std::iter::AdditiveIterator;
 use std::mem;
diff --git a/src/test/compile-fail/borrowck-overloaded-call.rs b/src/test/compile-fail/borrowck-overloaded-call.rs
index f134c2aa09b..938fc53d610 100644
--- a/src/test/compile-fail/borrowck-overloaded-call.rs
+++ b/src/test/compile-fail/borrowck-overloaded-call.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
 
 use std::ops::{Fn, FnMut, FnOnce};
 
diff --git a/src/test/compile-fail/borrowck-unboxed-closures.rs b/src/test/compile-fail/borrowck-unboxed-closures.rs
index 5f9dd72f5bf..cca3dcb8b34 100644
--- a/src/test/compile-fail/borrowck-unboxed-closures.rs
+++ b/src/test/compile-fail/borrowck-unboxed-closures.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls)]
+#![feature(overloaded_calls, unboxed_closures)]
 
 fn a<F:Fn(int, int) -> int>(mut f: F) {
     let g = &mut f;
diff --git a/src/test/compile-fail/explicit-self-lifetime-mismatch.rs b/src/test/compile-fail/explicit-self-lifetime-mismatch.rs
index d1ae535d830..9b2264b8902 100644
--- a/src/test/compile-fail/explicit-self-lifetime-mismatch.rs
+++ b/src/test/compile-fail/explicit-self-lifetime-mismatch.rs
@@ -14,12 +14,9 @@ struct Foo<'a,'b> {
 }
 
 impl<'a,'b> Foo<'a,'b> {
-    // The number of errors is related to the way invariance works.
     fn bar(self: Foo<'b,'a>) {}
     //~^ ERROR mismatched types: expected `Foo<'a, 'b>`, found `Foo<'b, 'a>`
     //~^^ ERROR mismatched types: expected `Foo<'a, 'b>`, found `Foo<'b, 'a>`
-    //~^^^ ERROR mismatched types: expected `Foo<'b, 'a>`, found `Foo<'a, 'b>`
-    //~^^^^ ERROR mismatched types: expected `Foo<'b, 'a>`, found `Foo<'a, 'b>`
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs b/src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs
new file mode 100644
index 00000000000..4199deee7b8
--- /dev/null
+++ b/src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs
@@ -0,0 +1,60 @@
+// 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 HRTB supertraits with several levels of expansion required.
+
+trait Foo<'tcx>
+{
+    fn foo(&'tcx self) -> &'tcx int;
+}
+
+trait Bar<'ccx>
+    : for<'tcx> Foo<'tcx>
+{
+    fn bar(&'ccx self) -> &'ccx int;
+}
+
+trait Baz
+    : for<'ccx> Bar<'ccx>
+{
+    fn dummy(&self);
+}
+
+trait Qux
+    : Bar<'static>
+{
+    fn dummy(&self);
+}
+
+fn want_foo_for_any_tcx<F>(f: &F)
+    where F : for<'tcx> Foo<'tcx>
+{
+}
+
+fn want_bar_for_any_ccx<B>(b: &B)
+    where B : for<'ccx> Bar<'ccx>
+{
+}
+
+fn want_baz<B>(b: &B)
+    where B : Baz
+{
+    want_foo_for_any_tcx(b);
+    want_bar_for_any_ccx(b);
+}
+
+fn want_qux<B>(b: &B)
+    where B : Qux
+{
+    want_foo_for_any_tcx(b);
+    want_bar_for_any_ccx(b); //~ ERROR not implemented
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs b/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs
new file mode 100644
index 00000000000..108ca1b82e0
--- /dev/null
+++ b/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs
@@ -0,0 +1,58 @@
+// 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 trait (`Bar`) with a higher-ranked supertrait.
+
+trait Foo<'tcx>
+{
+    fn foo(&'tcx self) -> &'tcx int;
+}
+
+trait Bar<'ccx>
+    : for<'tcx> Foo<'tcx>
+{
+    fn bar(&'ccx self) -> &'ccx int;
+}
+
+fn want_foo_for_some_tcx<'x,F>(f: &'x F)
+    where F : Foo<'x>
+{
+    want_foo_for_some_tcx(f);
+    want_foo_for_any_tcx(f); //~ ERROR not implemented
+}
+
+fn want_foo_for_any_tcx<F>(f: &F)
+    where F : for<'tcx> Foo<'tcx>
+{
+    want_foo_for_some_tcx(f);
+    want_foo_for_any_tcx(f);
+}
+
+fn want_bar_for_some_ccx<'x,B>(b: &B)
+    where B : Bar<'x>
+{
+    want_foo_for_some_tcx(b);
+    want_foo_for_any_tcx(b);
+
+    want_bar_for_some_ccx(b);
+    want_bar_for_any_ccx(b); //~ ERROR not implemented
+}
+
+fn want_bar_for_any_ccx<B>(b: &B)
+    where B : for<'ccx> Bar<'ccx>
+{
+    want_foo_for_some_tcx(b);
+    want_foo_for_any_tcx(b);
+
+    want_bar_for_some_ccx(b);
+    want_bar_for_any_ccx(b);
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/hrtb-identity-fn-borrows.rs b/src/test/compile-fail/hrtb-identity-fn-borrows.rs
new file mode 100644
index 00000000000..733a5b2a85a
--- /dev/null
+++ b/src/test/compile-fail/hrtb-identity-fn-borrows.rs
@@ -0,0 +1,35 @@
+// 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 the `'a` in the where clause correctly links the region
+// of the output to the region of the input.
+
+trait FnLike<A,R> {
+    fn call(&self, arg: A) -> R;
+}
+
+fn call_repeatedly<F>(f: F)
+    where F : for<'a> FnLike<&'a int, &'a int>
+{
+    // Result is stored: cannot re-assign `x`
+    let mut x = 3;
+    let y = f.call(&x);
+    x = 5; //~ ERROR cannot assign
+
+    // Result is not stored: can re-assign `x`
+    let mut x = 3;
+    f.call(&x);
+    f.call(&x);
+    f.call(&x);
+    x = 5;
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/issue-15094.rs b/src/test/compile-fail/issue-15094.rs
index c9e47b74d51..dd3c88b8a10 100644
--- a/src/test/compile-fail/issue-15094.rs
+++ b/src/test/compile-fail/issue-15094.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
 
 use std::{fmt, ops};
 
diff --git a/src/test/compile-fail/issue-16939.rs b/src/test/compile-fail/issue-16939.rs
index e7d3d15e5a9..7ec3fef5c87 100644
--- a/src/test/compile-fail/issue-16939.rs
+++ b/src/test/compile-fail/issue-16939.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls)]
+#![feature(overloaded_calls, unboxed_closures)]
 
 // Make sure we don't ICE when making an overloaded call with the
 // wrong arity.
diff --git a/src/test/compile-fail/issue-18532.rs b/src/test/compile-fail/issue-18532.rs
index a67f4c851bf..943d326182f 100644
--- a/src/test/compile-fail/issue-18532.rs
+++ b/src/test/compile-fail/issue-18532.rs
@@ -12,7 +12,7 @@
 // when a type error or unconstrained type variable propagates
 // into it.
 
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
 
 fn main() {
     (return)((),());
diff --git a/src/test/compile-fail/overloaded-calls-bad.rs b/src/test/compile-fail/overloaded-calls-bad.rs
index cc01c0b3c9f..b3a4c312589 100644
--- a/src/test/compile-fail/overloaded-calls-bad.rs
+++ b/src/test/compile-fail/overloaded-calls-bad.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
 
 use std::ops::FnMut;
 
diff --git a/src/test/compile-fail/overloaded-calls-nontuple.rs b/src/test/compile-fail/overloaded-calls-nontuple.rs
index ee2fe352247..396a809c2e1 100644
--- a/src/test/compile-fail/overloaded-calls-nontuple.rs
+++ b/src/test/compile-fail/overloaded-calls-nontuple.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
 
 use std::ops::FnMut;
 
diff --git a/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs b/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs
index e046b5c68ad..75e9e55138e 100644
--- a/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs
+++ b/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(unboxed_closure_sugar, unboxed_closures, overloaded_calls)]
+#![feature(unboxed_closures, overloaded_calls)]
 
 use std::ops::FnMut;
 
diff --git a/src/test/compile-fail/regions-name-undeclared.rs b/src/test/compile-fail/regions-name-undeclared.rs
index e9e585e84d0..ffd1501075e 100644
--- a/src/test/compile-fail/regions-name-undeclared.rs
+++ b/src/test/compile-fail/regions-name-undeclared.rs
@@ -44,6 +44,9 @@ fn bar<'a>(x: &'a int) {
     // &'a CAN be declared on functions and used then:
     fn g<'a>(a: &'a int) { } // OK
     fn h(a: for<'a>|&'a int|) { } // OK
+
+    // But not in the bound of a closure, it's not in scope *there*
+    fn i(a: for<'a>|&int|:'a) { } //~ ERROR undeclared lifetime
 }
 
 // Test nesting of lifetimes in fn type declarations
diff --git a/src/test/compile-fail/stage0-clone-contravariant-lifetime.rs b/src/test/compile-fail/stage0-clone-contravariant-lifetime.rs
new file mode 100644
index 00000000000..1d1b244ab5a
--- /dev/null
+++ b/src/test/compile-fail/stage0-clone-contravariant-lifetime.rs
@@ -0,0 +1,43 @@
+// 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.
+
+// A zero-dependency test that covers some basic traits, default
+// methods, etc.  When mucking about with basic type system stuff I
+// often encounter problems in the iterator trait, so it's useful to
+// have hanging around. -nmatsakis
+
+// error-pattern: requires `start` lang_item
+
+#![no_std]
+#![feature(lang_items)]
+
+#[lang = "sized"]
+pub trait Sized for Sized? {
+    // Empty.
+}
+
+pub mod std {
+    pub mod clone {
+        pub trait Clone {
+            fn clone(&self) -> Self;
+        }
+    }
+}
+
+pub struct ContravariantLifetime<'a>;
+
+impl <'a> ::std::clone::Clone for ContravariantLifetime<'a> {
+    #[inline]
+    fn clone(&self) -> ContravariantLifetime<'a> {
+        match *self { ContravariantLifetime => ContravariantLifetime, }
+    }
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/stage0-cmp.rs b/src/test/compile-fail/stage0-cmp.rs
new file mode 100644
index 00000000000..2c0772b1414
--- /dev/null
+++ b/src/test/compile-fail/stage0-cmp.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.
+
+
+// A zero-dependency test that covers some basic traits, default
+// methods, etc.  When mucking about with basic type system stuff I
+// often encounter problems in the iterator trait, so it's useful to
+// have hanging around. -nmatsakis
+
+// error-pattern: requires `start` lang_item
+
+#![no_std]
+#![feature(lang_items)]
+
+#[lang = "sized"]
+pub trait Sized for Sized? {
+    // Empty.
+}
+
+#[unstable = "Definition may change slightly after trait reform"]
+pub trait PartialEq for Sized? {
+    /// This method tests for `self` and `other` values to be equal, and is used by `==`.
+    fn eq(&self, other: &Self) -> bool;
+}
+
+#[cfg(not(stage0))]  // NOTE(stage0): remove cfg after a snapshot
+#[unstable = "Trait is unstable."]
+impl<'a, Sized? T: PartialEq> PartialEq for &'a T {
+    #[inline]
+    fn eq(&self, other: & &'a T) -> bool { PartialEq::eq(*self, *other) }
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/ufcs-explicit-self-bad.rs b/src/test/compile-fail/ufcs-explicit-self-bad.rs
index b639af61757..8d3610affdf 100644
--- a/src/test/compile-fail/ufcs-explicit-self-bad.rs
+++ b/src/test/compile-fail/ufcs-explicit-self-bad.rs
@@ -14,7 +14,6 @@ struct Foo {
 
 impl Foo {
     fn foo(self: int, x: int) -> int {  //~ ERROR mismatched self type
-//~^ ERROR not a valid type for `self`
         self.f + x
     }
 }
@@ -25,15 +24,26 @@ struct Bar<T> {
 
 impl<T> Bar<T> {
     fn foo(self: Bar<int>, x: int) -> int { //~ ERROR mismatched self type
-//~^ ERROR not a valid type for `self`
         x
     }
     fn bar(self: &Bar<uint>, x: int) -> int {   //~ ERROR mismatched self type
-//~^ ERROR not a valid type for `self`
         x
     }
 }
 
+trait SomeTrait {
+    fn dummy1(&self);
+    fn dummy2(&self);
+    fn dummy3(&self);
+}
+
+impl<'a, T> SomeTrait for &'a Bar<T> {
+    fn dummy1(self: &&'a Bar<T>) { }
+    fn dummy2(self: &Bar<T>) {} //~ ERROR mismatched self type
+    fn dummy3(self: &&Bar<T>) {} //~ ERROR lifetime mismatch
+    //~^ ERROR lifetime mismatch
+}
+
 fn main() {
     let foo = box Foo {
         f: 1,
diff --git a/src/test/compile-fail/unboxed-closure-sugar-default.rs b/src/test/compile-fail/unboxed-closure-sugar-default.rs
index 9866a200045..06a93406392 100644
--- a/src/test/compile-fail/unboxed-closure-sugar-default.rs
+++ b/src/test/compile-fail/unboxed-closure-sugar-default.rs
@@ -11,16 +11,16 @@
 // Test interaction between unboxed closure sugar and default type
 // parameters (should be exactly as if angle brackets were used).
 
-#![feature(default_type_params)]
+#![feature(default_type_params, unboxed_closures)]
 #![allow(dead_code)]
 
-struct Foo<T,U,V=T> {
-    t: T, u: U
+trait Foo<T,U,V=T> {
+    fn dummy(&self, t: T, u: U, v: V);
 }
 
-trait Eq<X> { }
-impl<X> Eq<X> for X { }
-fn eq<A,B:Eq<A>>() { }
+trait Eq<Sized? X> for Sized? { }
+impl<Sized? X> Eq<X> for X { }
+fn eq<Sized? A,Sized? B>() where A : Eq<B> { }
 
 fn test<'a,'b>() {
     // Parens are equivalent to omitting default in angle.
diff --git a/src/test/compile-fail/unboxed-closure-sugar-equiv.rs b/src/test/compile-fail/unboxed-closure-sugar-equiv.rs
index c38010c1ee2..6f875efdef7 100644
--- a/src/test/compile-fail/unboxed-closure-sugar-equiv.rs
+++ b/src/test/compile-fail/unboxed-closure-sugar-equiv.rs
@@ -13,15 +13,16 @@
 // angle brackets. This test covers only simple types and in
 // particular doesn't test bound regions.
 
+#![feature(unboxed_closures)]
 #![allow(dead_code)]
 
-struct Foo<T,U> {
-    t: T, u: U
+trait Foo<T,U> {
+    fn dummy(&self, t: T, u: U);
 }
 
-trait Eq<X> { }
-impl<X> Eq<X> for X { }
-fn eq<A,B:Eq<A>>() { }
+trait Eq<Sized? X> for Sized? { }
+impl<Sized? X> Eq<X> for X { }
+fn eq<Sized? A,Sized? B:Eq<A>>() { }
 
 fn test<'a,'b>() {
     // No errors expected:
@@ -31,6 +32,22 @@ fn test<'a,'b>() {
     eq::< Foo<(int,uint),uint>,         Foo(int,uint) -> uint         >();
     eq::< Foo<(&'a int,&'b uint),uint>, Foo(&'a int,&'b uint) -> uint >();
 
+    // Test that anonymous regions in `()` form are equivalent
+    // to fresh bound regions, and that we can intermingle
+    // named and anonymous as we choose:
+    eq::< for<'a,'b> Foo<(&'a int,&'b uint),uint>,
+          for<'a,'b> Foo(&'a int,&'b uint) -> uint            >();
+    eq::< for<'a,'b> Foo<(&'a int,&'b uint),uint>,
+          for<'a> Foo(&'a int,&uint) -> uint                  >();
+    eq::< for<'a,'b> Foo<(&'a int,&'b uint),uint>,
+          for<'b> Foo(&int,&'b uint) -> uint                  >();
+    eq::< for<'a,'b> Foo<(&'a int,&'b uint),uint>,
+          Foo(&int,&uint) -> uint                             >();
+
+    // FIXME(#18992) Test lifetime elision in `()` form:
+    // eq::< for<'a,'b> Foo<(&'a int,), &'a int>,
+    //      Foo(&int) -> &int                                   >();
+
     // Errors expected:
     eq::< Foo<(),()>,                   Foo(char)                     >();
     //~^ ERROR not implemented
diff --git a/src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs b/src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs
index d89c3802508..23e2d2f4365 100644
--- a/src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs
+++ b/src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(unboxed_closures)]
+
 fn f<F:Nonexist(int) -> int>(x: F) {} //~ ERROR nonexistent trait `Nonexist`
 
 type Typedef = int;
diff --git a/src/test/compile-fail/unboxed-closure-sugar-region.rs b/src/test/compile-fail/unboxed-closure-sugar-region.rs
index 962e233dea6..a938f126c16 100644
--- a/src/test/compile-fail/unboxed-closure-sugar-region.rs
+++ b/src/test/compile-fail/unboxed-closure-sugar-region.rs
@@ -12,20 +12,19 @@
 // parameters (should be exactly as if angle brackets were used
 // and regions omitted).
 
-#![feature(default_type_params)]
+#![feature(default_type_params, unboxed_closures)]
 #![allow(dead_code)]
 
 use std::kinds::marker;
 
-struct Foo<'a,T,U> {
-    t: T,
-    u: U,
-    m: marker::InvariantLifetime<'a>
+trait Foo<'a,T,U> {
+    fn dummy(&'a self) -> &'a (T,U);
 }
 
-trait Eq<X> { }
-impl<X> Eq<X> for X { }
-fn eq<A,B:Eq<A>>() { }
+trait Eq<Sized? X> for Sized? { }
+impl<Sized? X> Eq<X> for X { }
+fn eq<Sized? A,Sized? B:Eq<A>>() { }
+
 fn same_type<A,B:Eq<A>>(a: A, b: B) { }
 
 fn test<'a,'b>() {
@@ -34,10 +33,10 @@ fn test<'a,'b>() {
 
     // Here we specify 'static explicitly in angle-bracket version.
     // Parenthesized winds up getting inferred.
-    eq::< Foo<'static, (int,),()>,               Foo(int)                      >();
+    eq::< Foo<'static, (int,),()>,      Foo(int)                      >();
 }
 
-fn test2(x: Foo<(int,),()>, y: Foo(int)) {
+fn test2(x: &Foo<(int,),()>, y: &Foo(int)) {
     // Here, the omitted lifetimes are expanded to distinct things.
     same_type(x, y) //~ ERROR cannot infer
 }
diff --git a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs
index e122b87b1e0..d9efab974d8 100644
--- a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs
+++ b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs
@@ -8,9 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-struct One<A>;
+#![feature(unboxed_closures)]
 
-fn foo(_: One()) //~ ERROR wrong number of type arguments
+trait One<A> { fn foo(&self) -> A; }
+
+fn foo(_: &One()) //~ ERROR wrong number of type arguments
 {}
 
 fn main() { }
diff --git a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs
index 7a66abb39df..dcfcb7d4772 100644
--- a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs
+++ b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs
@@ -8,9 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-struct Three<A,B,C>;
+#![feature(unboxed_closures)]
 
-fn foo(_: Three()) //~ ERROR wrong number of type arguments
+trait Three<A,B,C> { fn dummy(&self) -> (A,B,C); }
+
+fn foo(_: &Three()) //~ ERROR wrong number of type arguments
 {}
 
 fn main() { }
diff --git a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters.rs b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters.rs
index e265a3d56b8..a8ac62444aa 100644
--- a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters.rs
+++ b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters.rs
@@ -8,7 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-struct Zero;
+#![feature(unboxed_closures)]
+
+trait Zero { fn dummy(&self); }
 
 fn foo(_: Zero()) //~ ERROR wrong number of type arguments
 {}
diff --git a/src/test/compile-fail/unboxed-closure-sugar-wrong-trait.rs b/src/test/compile-fail/unboxed-closure-sugar-wrong-trait.rs
index 1394f8fa65f..ba1e931ac64 100644
--- a/src/test/compile-fail/unboxed-closure-sugar-wrong-trait.rs
+++ b/src/test/compile-fail/unboxed-closure-sugar-wrong-trait.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(unboxed_closures)]
+
 trait Trait {}
 
 fn f<F:Trait(int) -> int>(x: F) {}
diff --git a/src/test/compile-fail/unboxed-closures-fnmut-as-fn.rs b/src/test/compile-fail/unboxed-closures-fnmut-as-fn.rs
index 20d7262432f..9fbb8a18ae9 100644
--- a/src/test/compile-fail/unboxed-closures-fnmut-as-fn.rs
+++ b/src/test/compile-fail/unboxed-closures-fnmut-as-fn.rs
@@ -11,7 +11,7 @@
 // Checks that the Fn trait hierarchy rules do not permit
 // Fn to be used where FnMut is implemented.
 
-#![feature(unboxed_closure_sugar)]
+#![feature(unboxed_closures)]
 #![feature(overloaded_calls)]
 
 use std::ops::{Fn,FnMut,FnOnce};
diff --git a/src/test/run-pass/bare-fn-implements-fn-mut.rs b/src/test/run-pass/bare-fn-implements-fn-mut.rs
index 4962e9d0f22..9d104afd646 100644
--- a/src/test/run-pass/bare-fn-implements-fn-mut.rs
+++ b/src/test/run-pass/bare-fn-implements-fn-mut.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
 
 use std::ops::FnMut;
 
diff --git a/src/test/run-pass/capture-clauses-unboxed-closures.rs b/src/test/run-pass/capture-clauses-unboxed-closures.rs
index 9f333fd043f..cd40e2a7843 100644
--- a/src/test/run-pass/capture-clauses-unboxed-closures.rs
+++ b/src/test/run-pass/capture-clauses-unboxed-closures.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls, unboxed_closures)]
+#![feature(unboxed_closures)]
 
 fn each<'a,T,F:FnMut(&'a T)>(x: &'a [T], mut f: F) {
     for val in x.iter() {
diff --git a/src/test/run-pass/closure-syntax.rs b/src/test/run-pass/closure-syntax.rs
index 9d98a7ac12f..6716c3468d0 100644
--- a/src/test/run-pass/closure-syntax.rs
+++ b/src/test/run-pass/closure-syntax.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 #![allow(dead_code)]
-#![feature(unboxed_closures, unboxed_closure_sugar)]
+#![feature(unboxed_closures)]
 
 // compile-flags:-g
 
diff --git a/src/test/run-pass/hrtb-binder-levels-in-object-types.rs b/src/test/run-pass/hrtb-binder-levels-in-object-types.rs
new file mode 100644
index 00000000000..5a793f7065a
--- /dev/null
+++ b/src/test/run-pass/hrtb-binder-levels-in-object-types.rs
@@ -0,0 +1,34 @@
+// 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 in object types correctly.
+// Initially, the reference to `'tcx` in the object type
+// `&Typer<'tcx>` was getting an incorrect binder level, yielding
+// weird compilation ICEs and so forth.
+
+trait Typer<'tcx> {
+    fn method(&self, data: &'tcx int) -> &'tcx int { data }
+}
+
+struct Tcx<'tcx> {
+    fields: &'tcx int
+}
+
+impl<'tcx> Typer<'tcx> for Tcx<'tcx> {
+}
+
+fn g<'tcx>(typer: &Typer<'tcx>) {
+}
+
+fn check_static_type<'x>(tcx: &Tcx<'x>) {
+    g(tcx)
+}
+
+fn main() { }
diff --git a/src/test/run-pass/hrtb-debruijn-object-types-in-closures.rs b/src/test/run-pass/hrtb-debruijn-object-types-in-closures.rs
new file mode 100644
index 00000000000..5bdfa3cafd7
--- /dev/null
+++ b/src/test/run-pass/hrtb-debruijn-object-types-in-closures.rs
@@ -0,0 +1,23 @@
+// 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.
+
+trait Typer<'tcx> {
+    fn method(&self, data: &'tcx int) -> &'tcx int { data }
+    fn dummy(&self) { }
+}
+
+fn g(_: |&Typer|) {
+}
+
+fn h() {
+    g(|typer| typer.dummy())
+}
+
+fn main() { }
diff --git a/src/test/run-pass/hrtb-fn-like-trait-object.rs b/src/test/run-pass/hrtb-fn-like-trait-object.rs
new file mode 100644
index 00000000000..c8992afe36a
--- /dev/null
+++ b/src/test/run-pass/hrtb-fn-like-trait-object.rs
@@ -0,0 +1,35 @@
+// 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.
+
+// A basic test of using a higher-ranked trait bound.
+
+trait FnLike<A,R> {
+    fn call(&self, arg: A) -> R;
+}
+
+type FnObject<'b> = for<'a> FnLike<&'a int, &'a int> + 'b;
+
+struct Identity;
+
+impl<'a, T> FnLike<&'a T, &'a T> for Identity {
+    fn call(&self, arg: &'a T) -> &'a T {
+        arg
+    }
+}
+
+fn call_repeatedly(f: &FnObject) {
+    let x = 3;
+    let y = f.call(&x);
+    assert_eq!(3, *y);
+}
+
+fn main() {
+    call_repeatedly(&Identity);
+}
diff --git a/src/test/run-pass/hrtb-fn-like-trait.rs b/src/test/run-pass/hrtb-fn-like-trait.rs
new file mode 100644
index 00000000000..4067b922cfd
--- /dev/null
+++ b/src/test/run-pass/hrtb-fn-like-trait.rs
@@ -0,0 +1,35 @@
+// 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.
+
+// A basic test of using a higher-ranked trait bound.
+
+trait FnLike<A,R> {
+    fn call(&self, arg: A) -> R;
+}
+
+struct Identity;
+
+impl<'a, T> FnLike<&'a T, &'a T> for Identity {
+    fn call(&self, arg: &'a T) -> &'a T {
+        arg
+    }
+}
+
+fn call_repeatedly<F>(f: F)
+    where F : for<'a> FnLike<&'a int, &'a int>
+{
+    let x = 3;
+    let y = f.call(&x);
+    assert_eq!(3, *y);
+}
+
+fn main() {
+    call_repeatedly(Identity);
+}
diff --git a/src/test/run-pass/hrtb-parse.rs b/src/test/run-pass/hrtb-parse.rs
index 080523f0060..02d3bc120da 100644
--- a/src/test/run-pass/hrtb-parse.rs
+++ b/src/test/run-pass/hrtb-parse.rs
@@ -11,6 +11,7 @@
 // Test that we can parse all the various places that a `for` keyword
 // can appear representing universal quantification.
 
+#![feature(unboxed_closures)]
 #![allow(unused_variables)]
 #![allow(dead_code)]
 
diff --git a/src/test/run-pass/hrtb-resolve-lifetime.rs b/src/test/run-pass/hrtb-resolve-lifetime.rs
new file mode 100644
index 00000000000..9b37b8e49ef
--- /dev/null
+++ b/src/test/run-pass/hrtb-resolve-lifetime.rs
@@ -0,0 +1,20 @@
+// 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.
+
+// A basic test of using a higher-ranked trait bound.
+
+trait FnLike<A,R> {
+    fn call(&self, arg: A) -> R;
+}
+
+type FnObject<'b> = for<'a> FnLike<&'a int, &'a int> + 'b;
+
+fn main() {
+}
diff --git a/src/test/run-pass/hrtb-trait-object-paren-notation.rs b/src/test/run-pass/hrtb-trait-object-paren-notation.rs
new file mode 100644
index 00000000000..e17e0ae2189
--- /dev/null
+++ b/src/test/run-pass/hrtb-trait-object-paren-notation.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.
+
+#![feature(unboxed_closures)]
+
+// A basic test of using a higher-ranked trait bound.
+
+trait FnLike<A,R> {
+    fn call(&self, arg: A) -> R;
+}
+
+type FnObject<'b> = for<'a> FnLike(&'a int) -> (&'a int) + 'b;
+
+struct Identity;
+
+impl<'a, T> FnLike<(&'a T,), &'a T> for Identity {
+    fn call(&self, (arg,): (&'a T,)) -> &'a T {
+        arg
+    }
+}
+
+fn call_repeatedly(f: &FnObject) {
+    let x = 3;
+    let y = f.call((&x,));
+    assert_eq!(3, *y);
+}
+
+fn main() {
+    call_repeatedly(&Identity);
+}
diff --git a/src/test/run-pass/hrtb-trait-object-passed-to-closure.rs b/src/test/run-pass/hrtb-trait-object-passed-to-closure.rs
new file mode 100644
index 00000000000..076b9c7684e
--- /dev/null
+++ b/src/test/run-pass/hrtb-trait-object-passed-to-closure.rs
@@ -0,0 +1,31 @@
+// 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 `&PrinterSupport`, which is really short for `&'a
+// PrinterSupport<'b>`, gets properly expanded when it appears in a
+// closure type. This used to result in messed up De Bruijn indices.
+
+trait PrinterSupport<'ast> {
+    fn ast_map(&self) -> Option<&'ast uint> { None }
+}
+
+struct NoAnn<'ast> {
+    f: Option<&'ast uint>
+}
+
+impl<'ast> PrinterSupport<'ast> for NoAnn<'ast> {
+}
+
+fn foo<'ast> (f: Option<&'ast uint>, g: |&PrinterSupport|) {
+    let annotation = NoAnn { f: f };
+    g(&annotation)
+}
+
+fn main() {}
diff --git a/src/test/run-pass/hrtb-unboxed-closure-trait.rs b/src/test/run-pass/hrtb-unboxed-closure-trait.rs
new file mode 100644
index 00000000000..fea628177da
--- /dev/null
+++ b/src/test/run-pass/hrtb-unboxed-closure-trait.rs
@@ -0,0 +1,22 @@
+// 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 HRTB used with the `Fn` trait.
+
+#![feature(unboxed_closures)]
+
+fn foo<F:Fn(&int)>(f: F) {
+    let x = 22;
+    f(&x);
+}
+
+fn main() {
+    foo(|&: x: &int| println!("{}", *x));
+}
diff --git a/src/test/run-pass/issue-14958.rs b/src/test/run-pass/issue-14958.rs
index c2bd8c5b3e5..7f3321e0b3e 100644
--- a/src/test/run-pass/issue-14958.rs
+++ b/src/test/run-pass/issue-14958.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
 
 trait Foo {}
 
diff --git a/src/test/run-pass/issue-14959.rs b/src/test/run-pass/issue-14959.rs
index 5429c519592..6cc5ab4d6cb 100644
--- a/src/test/run-pass/issue-14959.rs
+++ b/src/test/run-pass/issue-14959.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
 
 use std::ops::Fn;
 
diff --git a/src/test/run-pass/issue-16774.rs b/src/test/run-pass/issue-16774.rs
index f996f9309c1..ebc879d82fb 100644
--- a/src/test/run-pass/issue-16774.rs
+++ b/src/test/run-pass/issue-16774.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls, unboxed_closures)]
+#![feature(unboxed_closures)]
 
 struct X(Box<int>);
 
diff --git a/src/test/run-pass/issue-18652.rs b/src/test/run-pass/issue-18652.rs
index 5ca09100060..ef2c15c748c 100644
--- a/src/test/run-pass/issue-18652.rs
+++ b/src/test/run-pass/issue-18652.rs
@@ -12,7 +12,7 @@
 // once closure as an optimization by trans.  This used to hit an
 // incorrect assert.
 
-#![feature(unboxed_closures, overloaded_calls)]
+#![feature(unboxed_closures)]
 
 fn main() {
     let x = 2u8;
diff --git a/src/test/run-pass/issue-18685.rs b/src/test/run-pass/issue-18685.rs
index 1f74e2f8474..be6dd583132 100644
--- a/src/test/run-pass/issue-18685.rs
+++ b/src/test/run-pass/issue-18685.rs
@@ -11,7 +11,7 @@
 // Test that the self param space is not used in a conflicting
 // manner by unboxed closures within a default method on a trait
 
-#![feature(unboxed_closures, overloaded_calls)]
+#![feature(unboxed_closures)]
 
 trait Tr {
     fn foo(&self);
diff --git a/src/test/run-pass/issue-18711.rs b/src/test/run-pass/issue-18711.rs
index 2d16aa7f0c3..6a04e68af0c 100644
--- a/src/test/run-pass/issue-18711.rs
+++ b/src/test/run-pass/issue-18711.rs
@@ -11,7 +11,7 @@
 // Test that we don't panic on a RefCell borrow conflict in certain
 // code paths involving unboxed closures.
 
-#![feature(unboxed_closures, overloaded_calls)]
+#![feature(unboxed_closures)]
 
 // aux-build:issue-18711.rs
 extern crate "issue-18711" as issue;
diff --git a/src/test/run-pass/overloaded-calls-param-vtables.rs b/src/test/run-pass/overloaded-calls-param-vtables.rs
index 6f870f0afd5..d0dbee39ae0 100644
--- a/src/test/run-pass/overloaded-calls-param-vtables.rs
+++ b/src/test/run-pass/overloaded-calls-param-vtables.rs
@@ -10,7 +10,7 @@
 
 // Tests that nested vtables work with overloaded calls.
 
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
 
 use std::ops::Fn;
 
diff --git a/src/test/run-pass/overloaded-calls-simple.rs b/src/test/run-pass/overloaded-calls-simple.rs
index 76c7e60116f..b0a40f74ff9 100644
--- a/src/test/run-pass/overloaded-calls-simple.rs
+++ b/src/test/run-pass/overloaded-calls-simple.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(lang_items, overloaded_calls)]
+#![feature(lang_items, unboxed_closures)]
 
 use std::ops::{Fn, FnMut, FnOnce};
 
diff --git a/src/test/run-pass/overloaded-calls-zero-args.rs b/src/test/run-pass/overloaded-calls-zero-args.rs
index b868c8c96b5..809a251fe80 100644
--- a/src/test/run-pass/overloaded-calls-zero-args.rs
+++ b/src/test/run-pass/overloaded-calls-zero-args.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
 
 use std::ops::{FnMut};
 
diff --git a/src/test/run-pass/unboxed-closures-all-traits.rs b/src/test/run-pass/unboxed-closures-all-traits.rs
index 508d1e46f7e..635e1670aad 100644
--- a/src/test/run-pass/unboxed-closures-all-traits.rs
+++ b/src/test/run-pass/unboxed-closures-all-traits.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(lang_items, overloaded_calls, unboxed_closures)]
+#![feature(lang_items, unboxed_closures)]
 
 fn a<F:Fn(int, int) -> int>(f: F) -> int {
     f(1, 2)
diff --git a/src/test/run-pass/unboxed-closures-by-ref.rs b/src/test/run-pass/unboxed-closures-by-ref.rs
index 70d41a5c689..be955486dac 100644
--- a/src/test/run-pass/unboxed-closures-by-ref.rs
+++ b/src/test/run-pass/unboxed-closures-by-ref.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(overloaded_calls, unboxed_closures)]
+#![feature(unboxed_closures)]
 
 // Test by-ref capture of environment in unboxed closure types
 
diff --git a/src/test/run-pass/unboxed-closures-direct-sugary-call.rs b/src/test/run-pass/unboxed-closures-direct-sugary-call.rs
index c77ee9914ef..2854d64f663 100644
--- a/src/test/run-pass/unboxed-closures-direct-sugary-call.rs
+++ b/src/test/run-pass/unboxed-closures-direct-sugary-call.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(unboxed_closures, overloaded_calls)]
+#![feature(unboxed_closures)]
 
 fn main() {
     let mut unboxed = |&mut:| {};
diff --git a/src/test/run-pass/unboxed-closures-drop.rs b/src/test/run-pass/unboxed-closures-drop.rs
index 00bf5fac095..8d4d7b4ecb5 100644
--- a/src/test/run-pass/unboxed-closures-drop.rs
+++ b/src/test/run-pass/unboxed-closures-drop.rs
@@ -11,7 +11,7 @@
 // A battery of tests to ensure destructors of unboxed closure environments
 // run at the right times.
 
-#![feature(overloaded_calls, unboxed_closures)]
+#![feature(unboxed_closures)]
 
 static mut DROP_COUNT: uint = 0;
 
diff --git a/src/test/run-pass/unboxed-closures-extern-fn.rs b/src/test/run-pass/unboxed-closures-extern-fn.rs
index 82d51ba1f16..2628bd90eef 100644
--- a/src/test/run-pass/unboxed-closures-extern-fn.rs
+++ b/src/test/run-pass/unboxed-closures-extern-fn.rs
@@ -10,8 +10,8 @@
 
 // Checks that extern fn points implement the full range of Fn traits.
 
-#![feature(unboxed_closure_sugar)]
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
+#![feature(unboxed_closures)]
 
 use std::ops::{Fn,FnMut,FnOnce};
 
diff --git a/src/test/run-pass/unboxed-closures-fn-as-fnmut-and-fnonce.rs b/src/test/run-pass/unboxed-closures-fn-as-fnmut-and-fnonce.rs
index 90272636bc5..77d41ae1907 100644
--- a/src/test/run-pass/unboxed-closures-fn-as-fnmut-and-fnonce.rs
+++ b/src/test/run-pass/unboxed-closures-fn-as-fnmut-and-fnonce.rs
@@ -11,8 +11,8 @@
 // Checks that the Fn trait hierarchy rules permit
 // any Fn trait to be used where Fn is implemented.
 
-#![feature(unboxed_closure_sugar)]
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
+#![feature(unboxed_closures)]
 
 use std::ops::{Fn,FnMut,FnOnce};
 
diff --git a/src/test/run-pass/unboxed-closures-fnmut-as-fnonce.rs b/src/test/run-pass/unboxed-closures-fnmut-as-fnonce.rs
index bd01910a210..02395624cd1 100644
--- a/src/test/run-pass/unboxed-closures-fnmut-as-fnonce.rs
+++ b/src/test/run-pass/unboxed-closures-fnmut-as-fnonce.rs
@@ -11,8 +11,8 @@
 // Checks that the Fn trait hierarchy rules permit
 // FnMut or FnOnce to be used where FnMut is implemented.
 
-#![feature(unboxed_closure_sugar)]
-#![feature(overloaded_calls)]
+#![feature(unboxed_closures)]
+#![feature(unboxed_closures)]
 
 use std::ops::{FnMut,FnOnce};
 
diff --git a/src/test/run-pass/unboxed-closures-manual-impl.rs b/src/test/run-pass/unboxed-closures-manual-impl.rs
index 8f6cfe04997..3a750dadb91 100644
--- a/src/test/run-pass/unboxed-closures-manual-impl.rs
+++ b/src/test/run-pass/unboxed-closures-manual-impl.rs
@@ -9,7 +9,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(unboxed_closure_sugar)]
+#![feature(unboxed_closures)]
 
 use std::ops::FnMut;
 
diff --git a/src/test/run-pass/unboxed-closures-prelude.rs b/src/test/run-pass/unboxed-closures-prelude.rs
index f9d2ba02123..6f672f2f282 100644
--- a/src/test/run-pass/unboxed-closures-prelude.rs
+++ b/src/test/run-pass/unboxed-closures-prelude.rs
@@ -10,7 +10,7 @@
 
 // Tests that the reexports of `FnOnce` et al from the prelude work.
 
-#![feature(unboxed_closures, unboxed_closure_sugar)]
+#![feature(unboxed_closures)]
 
 fn main() {
     let task: Box<FnOnce(int) -> int> = box |: x| x;
diff --git a/src/test/run-pass/unboxed-closures-single-word-env.rs b/src/test/run-pass/unboxed-closures-single-word-env.rs
index 4239cfdd8cf..61ceb5e140e 100644
--- a/src/test/run-pass/unboxed-closures-single-word-env.rs
+++ b/src/test/run-pass/unboxed-closures-single-word-env.rs
@@ -11,7 +11,7 @@
 // Ensures that single-word environments work right in unboxed closures.
 // These take a different path in codegen.
 
-#![feature(overloaded_calls, unboxed_closures)]
+#![feature(unboxed_closures)]
 
 fn a<F:Fn(int, int) -> int>(f: F) -> int {
     f(1, 2)
diff --git a/src/test/run-pass/unboxed-closures-sugar-1.rs b/src/test/run-pass/unboxed-closures-sugar-1.rs
deleted file mode 100644
index b358e7ce288..00000000000
--- a/src/test/run-pass/unboxed-closures-sugar-1.rs
+++ /dev/null
@@ -1,34 +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.
-
-// Test that the unboxed closure sugar can be used with an arbitrary
-// struct type and that it is equivalent to the same syntax using
-// angle brackets. This test covers only simple types and in
-// particular doesn't test bound regions.
-
-#![allow(dead_code)]
-
-struct Foo<T,U> {
-    t: T, u: U
-}
-
-trait Eq<X> { }
-impl<X> Eq<X> for X { }
-fn eq<A,B:Eq<A>>() { }
-
-fn test<'a,'b>() {
-    eq::< Foo<(),()>,                   Foo()                         >();
-    eq::< Foo<(int,),()>,               Foo(int)                      >();
-    eq::< Foo<(int,uint),()>,           Foo(int,uint)                 >();
-    eq::< Foo<(int,uint),uint>,         Foo(int,uint) -> uint         >();
-    eq::< Foo<(&'a int,&'b uint),uint>, Foo(&'a int,&'b uint) -> uint >();
-}
-
-fn main() { }
diff --git a/src/test/run-pass/unboxed-closures-sugar-object.rs b/src/test/run-pass/unboxed-closures-sugar-object.rs
index 3b38f72432f..d65de438514 100644
--- a/src/test/run-pass/unboxed-closures-sugar-object.rs
+++ b/src/test/run-pass/unboxed-closures-sugar-object.rs
@@ -11,6 +11,7 @@
 // Test unboxed closure sugar used in object types.
 
 #![allow(dead_code)]
+#![feature(unboxed_closures)]
 
 struct Foo<T,U> {
     t: T, u: U
diff --git a/src/test/run-pass/unboxed-closures-unboxing-shim.rs b/src/test/run-pass/unboxed-closures-unboxing-shim.rs
index 426352cadd8..c41aeaa673f 100644
--- a/src/test/run-pass/unboxed-closures-unboxing-shim.rs
+++ b/src/test/run-pass/unboxed-closures-unboxing-shim.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(unboxed_closures, unboxed_closure_sugar)]
+#![feature(unboxed_closures)]
 
 use std::ops::FnOnce;
 
diff --git a/src/test/run-pass/unboxed-closures-unique-type-id.rs b/src/test/run-pass/unboxed-closures-unique-type-id.rs
index 43ab16c106f..4fdfb8cf02a 100644
--- a/src/test/run-pass/unboxed-closures-unique-type-id.rs
+++ b/src/test/run-pass/unboxed-closures-unique-type-id.rs
@@ -19,7 +19,7 @@
 //
 // compile-flags: -g
 
-#![feature(unboxed_closures, overloaded_calls)]
+#![feature(unboxed_closures)]
 
 use std::ptr;