about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-03-13 15:24:45 +0000
committerbors <bors@rust-lang.org>2018-03-13 15:24:45 +0000
commit8c4ff22a2d745097197c659ef9e3b04b8ceeb070 (patch)
tree61da4bce1ccb69b9b35362a967ba2db8a58e1dd0
parente65547d4fad0425d1db4f33a4d8134bf2cad939e (diff)
parent17c4103f3f0cc8bd4dea9de5e7ef155daf363cfe (diff)
downloadrust-8c4ff22a2d745097197c659ef9e3b04b8ceeb070.tar.gz
rust-8c4ff22a2d745097197c659ef9e3b04b8ceeb070.zip
Auto merge of #48411 - nikomatsakis:chalkify-canonical-query-mir, r=eddyb
introduce canonical queries, use for normalization and dropck-outlives

This branch adds in the concept of a **canonicalized trait query** and uses it for three specific operations:

- `infcx.at(cause, param_env).normalize(type_foldable)`
    - normalizes all associated types in `type_foldable`
- `tcx.normalize_erasing_regions(param_env, type_foldable)`
    - like normalize, but erases regions first and in the result; this leads to better caching
- `infcx.at(cause, param_env).dropck_outlives(ty)`
    - produces the set of types that must be live when a value of type `ty` is dropped
    - used from dropck but also NLL outlives

This is a kind of "first step" towards a more Chalk-ified approach. It leads to a **big** speedup for NLL, which is basically dominated by the dropck-outlives computation. Here are some timing measurements for the `syn` crate (pre-branch measurements coming soon):

| Commit | NLL disabled | NLL enabled |
| ------- | --- | --- |
| Before my branch | 5.43s | 8.99s |
| After my branch | 5.36s | 7.25s |

(Note that NLL enabled still does *all the work* that NLL disabled does, so this is not really a way to compare the performance of NLL versus the AST-based borrow checker directly.) Since this affects all codepaths, I'd like to do a full perf run before we land anything.

Also, this is not the "final point" for canonicalization etc. I think canonicalization can be made substantially faster, for one thing. But it seems like a reasonable starting point for a branch that's gotten a bit larger than I would have liked.

**Commit convention:** First of all, this entire branch ought to be a "pure refactoring", I believe, not changing anything about external behavior. Second, I've tagged the most important commits with `[VIC]` (very important commit), so you can scan for those. =)

r? @eddyb
-rw-r--r--src/Cargo.lock14
-rw-r--r--src/librustc/dep_graph/dep_node.rs54
-rw-r--r--src/librustc/ich/impls_ty.rs65
-rw-r--r--src/librustc/infer/at.rs20
-rw-r--r--src/librustc/infer/canonical.rs942
-rw-r--r--src/librustc/infer/combine.rs1
-rw-r--r--src/librustc/infer/error_reporting/mod.rs2
-rw-r--r--src/librustc/infer/freshen.rs6
-rw-r--r--src/librustc/infer/lexical_region_resolve/mod.rs2
-rw-r--r--src/librustc/infer/mod.rs238
-rw-r--r--src/librustc/infer/outlives/obligations.rs10
-rw-r--r--src/librustc/infer/region_constraints/mod.rs4
-rw-r--r--src/librustc/lib.rs3
-rw-r--r--src/librustc/lint/context.rs3
-rw-r--r--src/librustc/macros.rs319
-rw-r--r--src/librustc/mir/cache.rs4
-rw-r--r--src/librustc/mir/mod.rs186
-rw-r--r--src/librustc/mir/tcx.rs24
-rw-r--r--src/librustc/session/mod.rs21
-rw-r--r--src/librustc/traits/coherence.rs4
-rw-r--r--src/librustc/traits/error_reporting.rs3
-rw-r--r--src/librustc/traits/mod.rs9
-rw-r--r--src/librustc/traits/query/dropck_outlives.rs264
-rw-r--r--src/librustc/traits/query/mod.rs35
-rw-r--r--src/librustc/traits/query/normalize.rs274
-rw-r--r--src/librustc/traits/query/normalize_erasing_regions.rs88
-rw-r--r--src/librustc/traits/select.rs17
-rw-r--r--src/librustc/traits/specialize/mod.rs4
-rw-r--r--src/librustc/traits/structural_impls.rs283
-rw-r--r--src/librustc/traits/trans/mod.rs152
-rw-r--r--src/librustc/ty/context.rs38
-rw-r--r--src/librustc/ty/error.rs1
-rw-r--r--src/librustc/ty/flags.rs12
-rw-r--r--src/librustc/ty/fold.rs3
-rw-r--r--src/librustc/ty/instance.rs16
-rw-r--r--src/librustc/ty/layout.rs18
-rw-r--r--src/librustc/ty/maps/config.rs30
-rw-r--r--src/librustc/ty/maps/keys.rs21
-rw-r--r--src/librustc/ty/maps/mod.rs35
-rw-r--r--src/librustc/ty/maps/plumbing.rs4
-rw-r--r--src/librustc/ty/maps/values.rs6
-rw-r--r--src/librustc/ty/mod.rs184
-rw-r--r--src/librustc/ty/relate.rs47
-rw-r--r--src/librustc/ty/structural_impls.rs541
-rw-r--r--src/librustc/ty/sty.rs12
-rw-r--r--src/librustc/ty/subst.rs14
-rw-r--r--src/librustc/ty/util.rs124
-rw-r--r--src/librustc/util/common.rs10
-rw-r--r--src/librustc/util/ppaux.rs9
-rw-r--r--src/librustc_borrowck/borrowck/check_loans.rs1
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/mod.rs1
-rw-r--r--src/librustc_const_eval/lib.rs1
-rw-r--r--src/librustc_data_structures/indexed_vec.rs2
-rw-r--r--src/librustc_driver/Cargo.toml1
-rw-r--r--src/librustc_driver/driver.rs2
-rw-r--r--src/librustc_driver/lib.rs1
-rw-r--r--src/librustc_driver/test.rs4
-rw-r--r--src/librustc_lint/builtin.rs4
-rw-r--r--src/librustc_lint/types.rs19
-rw-r--r--src/librustc_metadata/lib.rs2
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs1
-rw-r--r--src/librustc_mir/borrow_check/mod.rs2
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/liveness.rs101
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs41
-rw-r--r--src/librustc_mir/interpret/eval_context.rs21
-rw-r--r--src/librustc_mir/interpret/terminator/mod.rs21
-rw-r--r--src/librustc_mir/monomorphize/collector.rs64
-rw-r--r--src/librustc_mir/monomorphize/item.rs5
-rw-r--r--src/librustc_mir/monomorphize/mod.rs6
-rw-r--r--src/librustc_mir/monomorphize/partitioning.rs6
-rw-r--r--src/librustc_mir/shim.rs9
-rw-r--r--src/librustc_mir/transform/inline.rs34
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs4
-rw-r--r--src/librustc_mir/util/elaborate_drops.rs9
-rw-r--r--src/librustc_passes/rvalue_promotion.rs3
-rw-r--r--src/librustc_save_analysis/lib.rs1
-rw-r--r--src/librustc_traits/Cargo.toml18
-rw-r--r--src/librustc_traits/dropck_outlives.rs285
-rw-r--r--src/librustc_traits/lib.rs44
-rw-r--r--src/librustc_traits/normalize_erasing_regions.rs62
-rw-r--r--src/librustc_traits/normalize_projection_ty.rs55
-rw-r--r--src/librustc_traits/util.rs117
-rw-r--r--src/librustc_trans/abi.rs2
-rw-r--r--src/librustc_trans/base.rs2
-rw-r--r--src/librustc_trans/callee.rs3
-rw-r--r--src/librustc_trans/common.rs7
-rw-r--r--src/librustc_trans/context.rs5
-rw-r--r--src/librustc_trans/debuginfo/metadata.rs9
-rw-r--r--src/librustc_trans/debuginfo/mod.rs12
-rw-r--r--src/librustc_trans/debuginfo/type_names.rs8
-rw-r--r--src/librustc_trans/declare.rs4
-rw-r--r--src/librustc_trans/intrinsic.rs7
-rw-r--r--src/librustc_trans/mir/block.rs13
-rw-r--r--src/librustc_trans/mir/constant.rs9
-rw-r--r--src/librustc_trans/mir/mod.rs9
-rw-r--r--src/librustc_trans/type_of.rs5
-rw-r--r--src/librustc_typeck/check/compare_method.rs4
-rw-r--r--src/librustc_typeck/check/dropck.rs54
-rw-r--r--src/librustc_typeck/check/mod.rs2
-rw-r--r--src/librustc_typeck/check/regionck.rs6
-rw-r--r--src/librustc_typeck/lib.rs4
-rw-r--r--src/librustc_typeck/variance/constraints.rs1
-rw-r--r--src/librustdoc/clean/mod.rs1
-rw-r--r--src/test/ui/issue-48132.rs42
-rw-r--r--src/test/ui/issue-48179.rs51
-rw-r--r--src/test/ui/nll/issue-31567.rs37
-rw-r--r--src/test/ui/nll/issue-31567.stderr18
-rw-r--r--src/test/ui/nll/issue-47470.rs34
-rw-r--r--src/test/ui/nll/issue-47470.stderr17
109 files changed, 3783 insertions, 1706 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock
index f8c04fe9272..ed32984bb58 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -1884,6 +1884,7 @@ dependencies = [
  "rustc_privacy 0.0.0",
  "rustc_resolve 0.0.0",
  "rustc_save_analysis 0.0.0",
+ "rustc_traits 0.0.0",
  "rustc_trans_utils 0.0.0",
  "rustc_typeck 0.0.0",
  "serialize 0.0.0",
@@ -2069,6 +2070,19 @@ dependencies = [
 ]
 
 [[package]]
+name = "rustc_traits"
+version = "0.0.0"
+dependencies = [
+ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "graphviz 0.0.0",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc 0.0.0",
+ "rustc_data_structures 0.0.0",
+ "syntax 0.0.0",
+ "syntax_pos 0.0.0",
+]
+
+[[package]]
 name = "rustc_trans"
 version = "0.0.0"
 dependencies = [
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 7d8709a82f4..8d7fef90b75 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -67,11 +67,12 @@ use hir::{HirId, ItemLocalId};
 
 use ich::{Fingerprint, StableHashingContext};
 use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
-use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty};
-use ty::subst::Substs;
 use std::fmt;
 use std::hash::Hash;
 use syntax_pos::symbol::InternedString;
+use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal};
+use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty};
+use ty::subst::Substs;
 
 // erase!() just makes tokens go away. It's used to specify which macro argument
 // is repeated (i.e. which sub-expression of the macro we are in) but don't need
@@ -80,6 +81,10 @@ macro_rules! erase {
     ($x:tt) => ({})
 }
 
+macro_rules! replace {
+    ($x:tt with $($y:tt)*) => ($($y)*)
+}
+
 macro_rules! is_anon_attr {
     (anon) => (true);
     ($attr:ident) => (false);
@@ -111,7 +116,7 @@ macro_rules! define_dep_nodes {
     (<$tcx:tt>
     $(
         [$($attr:ident),* ]
-        $variant:ident $(( $($tuple_arg:tt),* ))*
+        $variant:ident $(( $tuple_arg_ty:ty $(,)* ))*
                        $({ $($struct_arg_name:ident : $struct_arg_ty:ty),* })*
       ,)*
     ) => (
@@ -134,7 +139,7 @@ macro_rules! define_dep_nodes {
 
                             // tuple args
                             $({
-                                return <( $($tuple_arg,)* ) as DepNodeParams>
+                                return <$tuple_arg_ty as DepNodeParams>
                                     ::CAN_RECONSTRUCT_QUERY_KEY;
                             })*
 
@@ -186,7 +191,7 @@ macro_rules! define_dep_nodes {
                         DepKind :: $variant => {
                             // tuple args
                             $({
-                                $(erase!($tuple_arg);)*
+                                erase!($tuple_arg_ty);
                                 return true;
                             })*
 
@@ -205,7 +210,7 @@ macro_rules! define_dep_nodes {
 
         pub enum DepConstructor<$tcx> {
             $(
-                $variant $(( $($tuple_arg),* ))*
+                $variant $(( $tuple_arg_ty ))*
                          $({ $($struct_arg_name : $struct_arg_ty),* })*
             ),*
         }
@@ -227,15 +232,14 @@ macro_rules! define_dep_nodes {
             {
                 match dep {
                     $(
-                        DepConstructor :: $variant $(( $($tuple_arg),* ))*
+                        DepConstructor :: $variant $(( replace!(($tuple_arg_ty) with arg) ))*
                                                    $({ $($struct_arg_name),* })*
                             =>
                         {
                             // tuple args
                             $({
-                                let tupled_args = ( $($tuple_arg,)* );
-                                let hash = DepNodeParams::to_fingerprint(&tupled_args,
-                                                                         tcx);
+                                erase!($tuple_arg_ty);
+                                let hash = DepNodeParams::to_fingerprint(&arg, tcx);
                                 let dep_node = DepNode {
                                     kind: DepKind::$variant,
                                     hash
@@ -247,7 +251,7 @@ macro_rules! define_dep_nodes {
                                     tcx.sess.opts.debugging_opts.query_dep_graph)
                                 {
                                     tcx.dep_graph.register_dep_node_debug_str(dep_node, || {
-                                        tupled_args.to_debug_str(tcx)
+                                        arg.to_debug_str(tcx)
                                     });
                                 }
 
@@ -631,7 +635,9 @@ define_dep_nodes!( <'tcx>
     [] CodegenUnit(InternedString),
     [] CompileCodegenUnit(InternedString),
     [input] OutputFilenames,
-    [anon] NormalizeTy,
+    [] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>),
+    [] NormalizeTyAfterErasingRegions(ParamEnvAnd<'tcx, Ty<'tcx>>),
+    [] DropckOutlives(CanonicalTyGoal<'tcx>),
 
     [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) },
 
@@ -679,43 +685,43 @@ impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a, T> DepNodeParams<'a, 'gcx, 'tcx> for T
     }
 }
 
-impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (DefId,) {
+impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for DefId {
     const CAN_RECONSTRUCT_QUERY_KEY: bool = true;
 
     fn to_fingerprint(&self, tcx: TyCtxt) -> Fingerprint {
-        tcx.def_path_hash(self.0).0
+        tcx.def_path_hash(*self).0
     }
 
     fn to_debug_str(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> String {
-        tcx.item_path_str(self.0)
+        tcx.item_path_str(*self)
     }
 }
 
-impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (DefIndex,) {
+impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for DefIndex {
     const CAN_RECONSTRUCT_QUERY_KEY: bool = true;
 
     fn to_fingerprint(&self, tcx: TyCtxt) -> Fingerprint {
-        tcx.hir.definitions().def_path_hash(self.0).0
+        tcx.hir.definitions().def_path_hash(*self).0
     }
 
     fn to_debug_str(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> String {
-        tcx.item_path_str(DefId::local(self.0))
+        tcx.item_path_str(DefId::local(*self))
     }
 }
 
-impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (CrateNum,) {
+impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for CrateNum {
     const CAN_RECONSTRUCT_QUERY_KEY: bool = true;
 
     fn to_fingerprint(&self, tcx: TyCtxt) -> Fingerprint {
         let def_id = DefId {
-            krate: self.0,
+            krate: *self,
             index: CRATE_DEF_INDEX,
         };
         tcx.def_path_hash(def_id).0
     }
 
     fn to_debug_str(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> String {
-        tcx.crate_name(self.0).as_str().to_string()
+        tcx.crate_name(*self).as_str().to_string()
     }
 }
 
@@ -743,17 +749,17 @@ impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (DefId, De
     }
 }
 
-impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (HirId,) {
+impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for HirId {
     const CAN_RECONSTRUCT_QUERY_KEY: bool = false;
 
     // We actually would not need to specialize the implementation of this
     // method but it's faster to combine the hashes than to instantiate a full
     // hashing context and stable-hashing state.
     fn to_fingerprint(&self, tcx: TyCtxt) -> Fingerprint {
-        let (HirId {
+        let HirId {
             owner,
             local_id: ItemLocalId(local_id),
-        },) = *self;
+        } = *self;
 
         let def_path_hash = tcx.def_path_hash(DefId::local(owner));
         let local_id = Fingerprint::from_smaller_hash(local_id as u64);
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index d927a151610..4eb4f0edafe 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -19,6 +19,7 @@ use std::cell::RefCell;
 use std::hash as std_hash;
 use std::mem;
 use middle::region;
+use infer;
 use traits;
 use ty;
 use mir;
@@ -85,6 +86,9 @@ for ty::RegionKind {
             ty::ReEmpty => {
                 // No variant fields to hash for these ...
             }
+            ty::ReCanonical(c) => {
+                c.hash_stable(hcx, hasher);
+            }
             ty::ReLateBound(db, ty::BrAnon(i)) => {
                 db.depth.hash_stable(hcx, hasher);
                 i.hash_stable(hcx, hasher);
@@ -130,6 +134,16 @@ impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionVid {
     }
 }
 
+impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::CanonicalVar {
+    #[inline]
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'gcx>,
+                                          hasher: &mut StableHasher<W>) {
+        use rustc_data_structures::indexed_vec::Idx;
+        self.index().hash_stable(hcx, hasher);
+    }
+}
+
 impl<'a, 'gcx> HashStable<StableHashingContext<'a>>
 for ty::adjustment::AutoBorrow<'gcx> {
     fn hash_stable<W: StableHasherResult>(&self,
@@ -1003,12 +1017,6 @@ impl_stable_hash_for!(struct ty::Destructor {
     did
 });
 
-impl_stable_hash_for!(struct ty::DtorckConstraint<'tcx> {
-    outlives,
-    dtorck_types
-});
-
-
 impl<'a> HashStable<StableHashingContext<'a>> for ty::CrateVariancesMap {
     fn hash_stable<W: StableHasherResult>(&self,
                                           hcx: &mut StableHashingContext<'a>,
@@ -1229,11 +1237,52 @@ for traits::VtableGeneratorData<'gcx, N> where N: HashStable<StableHashingContex
     }
 }
 
-impl<'gcx> HashStable<StableHashingContext<'gcx>>
+impl<'a> HashStable<StableHashingContext<'a>>
 for ty::UniverseIndex {
     fn hash_stable<W: StableHasherResult>(&self,
-                                          hcx: &mut StableHashingContext<'gcx>,
+                                          hcx: &mut StableHashingContext<'a>,
                                           hasher: &mut StableHasher<W>) {
         self.depth().hash_stable(hcx, hasher);
     }
 }
+
+impl_stable_hash_for!(
+    impl<'tcx, V> for struct infer::canonical::Canonical<'tcx, V> {
+        variables, value
+    }
+);
+
+impl_stable_hash_for!(
+    impl<'tcx> for struct infer::canonical::CanonicalVarValues<'tcx> {
+        var_values
+    }
+);
+
+impl_stable_hash_for!(struct infer::canonical::CanonicalVarInfo {
+    kind
+});
+
+impl_stable_hash_for!(enum infer::canonical::CanonicalVarKind {
+    Ty(k),
+    Region
+});
+
+impl_stable_hash_for!(enum infer::canonical::CanonicalTyVarKind {
+    General,
+    Int,
+    Float
+});
+
+impl_stable_hash_for!(
+    impl<'tcx, R> for struct infer::canonical::QueryResult<'tcx, R> {
+        var_values, region_constraints, certainty, value
+    }
+);
+
+impl_stable_hash_for!(struct infer::canonical::QueryRegionConstraints<'tcx> {
+    region_outlives, ty_outlives
+});
+
+impl_stable_hash_for!(enum infer::canonical::Certainty {
+    Proven, Ambiguous
+});
diff --git a/src/librustc/infer/at.rs b/src/librustc/infer/at.rs
index 3fd7ee27672..89dbc76c8a6 100644
--- a/src/librustc/infer/at.rs
+++ b/src/librustc/infer/at.rs
@@ -40,9 +40,9 @@ use super::*;
 use ty::relate::{Relate, TypeRelation};
 
 pub struct At<'a, 'gcx: 'tcx, 'tcx: 'a> {
-    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
-    cause: &'a ObligationCause<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
+    pub infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    pub cause: &'a ObligationCause<'tcx>,
+    pub param_env: ty::ParamEnv<'tcx>,
 }
 
 pub struct Trace<'a, 'gcx: 'tcx, 'tcx: 'a> {
@@ -281,6 +281,20 @@ impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {
     }
 }
 
+impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> {
+    fn to_trace(cause: &ObligationCause<'tcx>,
+                a_is_expected: bool,
+                a: Self,
+                b: Self)
+                -> TypeTrace<'tcx>
+    {
+        TypeTrace {
+            cause: cause.clone(),
+            values: Regions(ExpectedFound::new(a_is_expected, a, b))
+        }
+    }
+}
+
 impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> {
     fn to_trace(cause: &ObligationCause<'tcx>,
                 a_is_expected: bool,
diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical.rs
new file mode 100644
index 00000000000..9519baa3ff7
--- /dev/null
+++ b/src/librustc/infer/canonical.rs
@@ -0,0 +1,942 @@
+// 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.
+
+//! **Canonicalization** is the key to constructing a query in the
+//! middle of type inference. Ordinarily, it is not possible to store
+//! types from type inference in query keys, because they contain
+//! references to inference variables whose lifetimes are too short
+//! and so forth. Canonicalizing a value T1 using `canonicalize_query`
+//! produces two things:
+//!
+//! - a value T2 where each unbound inference variable has been
+//!   replaced with a **canonical variable**;
+//! - a map M (of type `CanonicalVarValues`) from those canonical
+//!   variables back to the original.
+//!
+//! We can then do queries using T2. These will give back constriants
+//! on the canonical variables which can be translated, using the map
+//! M, into constraints in our source context. This process of
+//! translating the results back is done by the
+//! `instantiate_query_result` method.
+//!
+//! For a more detailed look at what is happening here, check
+//! out the [chapter in the rustc guide][c].
+//!
+//! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html
+
+use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin};
+use rustc_data_structures::indexed_vec::Idx;
+use std::fmt::Debug;
+use std::ops::Index;
+use syntax::codemap::Span;
+use traits::{Obligation, ObligationCause, PredicateObligation};
+use ty::{self, CanonicalVar, Lift, Region, Slice, Ty, TyCtxt, TypeFlags};
+use ty::subst::{Kind, UnpackedKind};
+use ty::fold::{TypeFoldable, TypeFolder};
+use util::common::CellUsizeExt;
+
+use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_data_structures::fx::FxHashMap;
+
+/// A "canonicalized" type `V` is one where all free inference
+/// variables have been rewriten to "canonical vars". These are
+/// numbered starting from 0 in order of first appearance.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Canonical<'gcx, V> {
+    pub variables: CanonicalVarInfos<'gcx>,
+    pub value: V,
+}
+
+pub type CanonicalVarInfos<'gcx> = &'gcx Slice<CanonicalVarInfo>;
+
+/// A set of values corresponding to the canonical variables from some
+/// `Canonical`. You can give these values to
+/// `canonical_value.substitute` to substitute them into the canonical
+/// value at the right places.
+///
+/// When you canonicalize a value `V`, you get back one of these
+/// vectors with the original values that were replaced by canonical
+/// variables.
+///
+/// You can also use `infcx.fresh_inference_vars_for_canonical_vars`
+/// to get back a `CanonicalVarValues` containing fresh inference
+/// variables.
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct CanonicalVarValues<'tcx> {
+    pub var_values: IndexVec<CanonicalVar, Kind<'tcx>>,
+}
+
+/// Information about a canonical variable that is included with the
+/// canonical value. This is sufficient information for code to create
+/// a copy of the canonical value in some other inference context,
+/// with fresh inference variables replacing the canonical values.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct CanonicalVarInfo {
+    pub kind: CanonicalVarKind,
+}
+
+/// Describes the "kind" of the canonical variable. This is a "kind"
+/// in the type-theory sense of the term -- i.e., a "meta" type system
+/// that analyzes type-like values.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum CanonicalVarKind {
+    /// Some kind of type inference variable.
+    Ty(CanonicalTyVarKind),
+
+    /// Region variable `'?R`.
+    Region,
+}
+
+/// Rust actually has more than one category of type variables;
+/// notably, the type variables we create for literals (e.g., 22 or
+/// 22.) can only be instantiated with integral/float types (e.g.,
+/// usize or f32). In order to faithfully reproduce a type, we need to
+/// know what set of types a given type variable can be unified with.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum CanonicalTyVarKind {
+    /// General type variable `?T` that can be unified with arbitrary types.
+    General,
+
+    /// Integral type variable `?I` (that can only be unified with integral types).
+    Int,
+
+    /// Floating-point type variable `?F` (that can only be unified with float types).
+    Float,
+}
+
+/// After we execute a query with a canonicalized key, we get back a
+/// `Canonical<QueryResult<..>>`. You can use
+/// `instantiate_query_result` to access the data in this result.
+#[derive(Clone, Debug)]
+pub struct QueryResult<'tcx, R> {
+    pub var_values: CanonicalVarValues<'tcx>,
+    pub region_constraints: QueryRegionConstraints<'tcx>,
+    pub certainty: Certainty,
+    pub value: R,
+}
+
+/// Indicates whether or not we were able to prove the query to be
+/// true.
+#[derive(Copy, Clone, Debug)]
+pub enum Certainty {
+    /// The query is known to be true, presuming that you apply the
+    /// given `var_values` and the region-constraints are satisfied.
+    Proven,
+
+    /// The query is not known to be true, but also not known to be
+    /// false. The `var_values` represent *either* values that must
+    /// hold in order for the query to be true, or helpful tips that
+    /// *might* make it true. Currently rustc's trait solver cannot
+    /// distinguish the two (e.g., due to our preference for where
+    /// clauses over impls).
+    ///
+    /// After some unifiations and things have been done, it makes
+    /// sense to try and prove again -- of course, at that point, the
+    /// canonical form will be different, making this a distinct
+    /// query.
+    Ambiguous,
+}
+
+impl Certainty {
+    pub fn is_proven(&self) -> bool {
+        match self {
+            Certainty::Proven => true,
+            Certainty::Ambiguous => false,
+        }
+    }
+
+    pub fn is_ambiguous(&self) -> bool {
+        !self.is_proven()
+    }
+}
+
+impl<'tcx, R> QueryResult<'tcx, R> {
+    pub fn is_proven(&self) -> bool {
+        self.certainty.is_proven()
+    }
+
+    pub fn is_ambiguous(&self) -> bool {
+        !self.is_proven()
+    }
+}
+
+impl<'tcx, R> Canonical<'tcx, QueryResult<'tcx, R>> {
+    pub fn is_proven(&self) -> bool {
+        self.value.is_proven()
+    }
+
+    pub fn is_ambiguous(&self) -> bool {
+        !self.is_proven()
+    }
+}
+
+/// Subset of `RegionConstraintData` produced by trait query.
+#[derive(Clone, Debug, Default)]
+pub struct QueryRegionConstraints<'tcx> {
+    pub region_outlives: Vec<(Region<'tcx>, Region<'tcx>)>,
+    pub ty_outlives: Vec<(Ty<'tcx>, Region<'tcx>)>,
+}
+
+/// Trait implemented by values that can be canonicalized. It mainly
+/// serves to identify the interning table we will use.
+pub trait Canonicalize<'gcx: 'tcx, 'tcx>: TypeFoldable<'tcx> + Lift<'gcx> {
+    type Canonicalized: 'gcx + Debug;
+
+    /// After a value has been fully canonicalized and lifted, this
+    /// method will allocate it in a global arena.
+    fn intern(
+        gcx: TyCtxt<'_, 'gcx, 'gcx>,
+        value: Canonical<'gcx, Self::Lifted>,
+    ) -> Self::Canonicalized;
+}
+
+impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
+    /// Creates a substitution S for the canonical value with fresh
+    /// inference variables and applies it to the canonical value.
+    /// Returns both the instantiated result *and* the substitution S.
+    ///
+    /// This is useful at the start of a query: it basically brings
+    /// the canonical value "into scope" within your new infcx. At the
+    /// end of processing, the substitution S (once canonicalized)
+    /// then represents the values that you computed for each of the
+    /// canonical inputs to your query.
+    pub fn instantiate_canonical_with_fresh_inference_vars<T>(
+        &self,
+        span: Span,
+        canonical: &Canonical<'tcx, T>,
+    ) -> (T, CanonicalVarValues<'tcx>)
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        let canonical_inference_vars =
+            self.fresh_inference_vars_for_canonical_vars(span, canonical.variables);
+        let result = canonical.substitute(self.tcx, &canonical_inference_vars);
+        (result, canonical_inference_vars)
+    }
+
+    /// Given the "infos" about the canonical variables from some
+    /// canonical, creates fresh inference variables with the same
+    /// characteristics. You can then use `substitute` to instantiate
+    /// the canonical variable with these inference variables.
+    pub fn fresh_inference_vars_for_canonical_vars(
+        &self,
+        span: Span,
+        variables: &Slice<CanonicalVarInfo>,
+    ) -> CanonicalVarValues<'tcx> {
+        let var_values: IndexVec<CanonicalVar, Kind<'tcx>> = variables
+            .iter()
+            .map(|info| self.fresh_inference_var_for_canonical_var(span, *info))
+            .collect();
+
+        CanonicalVarValues { var_values }
+    }
+
+    /// Given the "info" about a canonical variable, creates a fresh
+    /// inference variable with the same characteristics.
+    pub fn fresh_inference_var_for_canonical_var(
+        &self,
+        span: Span,
+        cv_info: CanonicalVarInfo,
+    ) -> Kind<'tcx> {
+        match cv_info.kind {
+            CanonicalVarKind::Ty(ty_kind) => {
+                let ty = match ty_kind {
+                    CanonicalTyVarKind::General => {
+                        self.next_ty_var(
+                            // FIXME(#48696) this handling of universes is not right.
+                            ty::UniverseIndex::ROOT,
+                            TypeVariableOrigin::MiscVariable(span),
+                        )
+                    }
+
+                    CanonicalTyVarKind::Int => self.tcx.mk_int_var(self.next_int_var_id()),
+
+                    CanonicalTyVarKind::Float => self.tcx.mk_float_var(self.next_float_var_id()),
+                };
+                Kind::from(ty)
+            }
+
+            CanonicalVarKind::Region => {
+                Kind::from(self.next_region_var(RegionVariableOrigin::MiscVariable(span)))
+            }
+        }
+    }
+
+    /// Given the (canonicalized) result to a canonical query,
+    /// instantiates the result so it can be used, plugging in the
+    /// values from the canonical query. (Note that the result may
+    /// have been ambiguous; you should check the certainty level of
+    /// the query before applying this function.)
+    ///
+    /// To get a good understanding of what is happening here, check
+    /// out the [chapter in the rustc guide][c].
+    ///
+    /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html#processing-the-canonicalized-query-result
+    pub fn instantiate_query_result<R>(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        original_values: &CanonicalVarValues<'tcx>,
+        query_result: &Canonical<'tcx, QueryResult<'tcx, R>>,
+    ) -> InferResult<'tcx, R>
+    where
+        R: Debug + TypeFoldable<'tcx>,
+    {
+        debug!(
+            "instantiate_query_result(original_values={:#?}, query_result={:#?})",
+            original_values, query_result,
+        );
+
+        // Every canonical query result includes values for each of
+        // the inputs to the query. Therefore, we begin by unifying
+        // these values with the original inputs that were
+        // canonicalized.
+        let result_values = &query_result.value.var_values;
+        assert_eq!(original_values.len(), result_values.len());
+
+        // Quickly try to find initial values for the canonical
+        // variables in the result in terms of the query. We do this
+        // by iterating down the values that the query gave to each of
+        // the canonical inputs. If we find that one of those values
+        // is directly equal to one of the canonical variables in the
+        // result, then we can type the corresponding value from the
+        // input. See the example above.
+        let mut opt_values: IndexVec<CanonicalVar, Option<Kind<'tcx>>> =
+            IndexVec::from_elem_n(None, query_result.variables.len());
+
+        // In terms of our example above, we are iterating over pairs like:
+        // [(?A, Vec<?0>), ('static, '?1), (?B, ?0)]
+        for (original_value, result_value) in original_values.iter().zip(result_values) {
+            match result_value.unpack() {
+                UnpackedKind::Type(result_value) => {
+                    // e.g., here `result_value` might be `?0` in the example above...
+                    if let ty::TyInfer(ty::InferTy::CanonicalTy(index)) = result_value.sty {
+                        // in which case we would set `canonical_vars[0]` to `Some(?U)`.
+                        opt_values[index] = Some(original_value);
+                    }
+                }
+                UnpackedKind::Lifetime(result_value) => {
+                    // e.g., here `result_value` might be `'?1` in the example above...
+                    if let &ty::RegionKind::ReCanonical(index) = result_value {
+                        // in which case we would set `canonical_vars[0]` to `Some('static)`.
+                        opt_values[index] = Some(original_value);
+                    }
+                }
+            }
+        }
+
+        // Create a result substitution: if we found a value for a
+        // given variable in the loop above, use that. Otherwise, use
+        // a fresh inference variable.
+        let result_subst = &CanonicalVarValues {
+            var_values: query_result
+                .variables
+                .iter()
+                .enumerate()
+                .map(|(index, info)| match opt_values[CanonicalVar::new(index)] {
+                    Some(k) => k,
+                    None => self.fresh_inference_var_for_canonical_var(cause.span, *info),
+                })
+                .collect(),
+        };
+
+        // Unify the original values for the canonical variables in
+        // the input with the value found in the query
+        // post-substitution. Often, but not always, this is a no-op,
+        // because we already found the mapping in the first step.
+        let substituted_values = |index: CanonicalVar| -> Kind<'tcx> {
+            query_result.substitute_projected(self.tcx, result_subst, |v| &v.var_values[index])
+        };
+        let mut obligations =
+            self.unify_canonical_vars(cause, param_env, original_values, substituted_values)?
+                .into_obligations();
+
+        obligations.extend(self.query_region_constraints_into_obligations(
+            cause,
+            param_env,
+            &query_result.value.region_constraints,
+            result_subst,
+        ));
+
+        let user_result: R =
+            query_result.substitute_projected(self.tcx, result_subst, |q_r| &q_r.value);
+
+        Ok(InferOk {
+            value: user_result,
+            obligations,
+        })
+    }
+
+    /// Converts the region constraints resulting from a query into an
+    /// iterator of obligations.
+    fn query_region_constraints_into_obligations<'a>(
+        &'a self,
+        cause: &'a ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        unsubstituted_region_constraints: &'a QueryRegionConstraints<'tcx>,
+        result_subst: &'a CanonicalVarValues<'tcx>,
+    ) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'a {
+        let QueryRegionConstraints {
+            region_outlives,
+            ty_outlives,
+        } = unsubstituted_region_constraints;
+
+        let region_obligations = region_outlives.iter().map(move |(r1, r2)| {
+            let r1 = substitute_value(self.tcx, result_subst, r1);
+            let r2 = substitute_value(self.tcx, result_subst, r2);
+            Obligation::new(
+                cause.clone(),
+                param_env,
+                ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r1, r2))),
+            )
+        });
+
+        let ty_obligations = ty_outlives.iter().map(move |(t1, r2)| {
+            let t1 = substitute_value(self.tcx, result_subst, t1);
+            let r2 = substitute_value(self.tcx, result_subst, r2);
+            Obligation::new(
+                cause.clone(),
+                param_env,
+                ty::Predicate::TypeOutlives(ty::Binder(ty::OutlivesPredicate(t1, r2))),
+            )
+        });
+
+        region_obligations.chain(ty_obligations)
+    }
+
+    /// Given two sets of values for the same set of canonical variables, unify them.
+    /// The second set is produced lazilly by supplying indices from the first set.
+    fn unify_canonical_vars(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        variables1: &CanonicalVarValues<'tcx>,
+        variables2: impl Fn(CanonicalVar) -> Kind<'tcx>,
+    ) -> InferResult<'tcx, ()> {
+        self.commit_if_ok(|_| {
+            let mut obligations = vec![];
+            for (index, value1) in variables1.var_values.iter_enumerated() {
+                let value2 = variables2(index);
+
+                match (value1.unpack(), value2.unpack()) {
+                    (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => {
+                        obligations
+                            .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations());
+                    }
+                    (
+                        UnpackedKind::Lifetime(ty::ReErased),
+                        UnpackedKind::Lifetime(ty::ReErased),
+                    ) => {
+                        // no action needed
+                    }
+                    (UnpackedKind::Lifetime(v1), UnpackedKind::Lifetime(v2)) => {
+                        obligations
+                            .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations());
+                    }
+                    _ => {
+                        bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,);
+                    }
+                }
+            }
+            Ok(InferOk {
+                value: (),
+                obligations,
+            })
+        })
+    }
+
+    /// Canonicalizes a query value `V`. When we canonicalize a query,
+    /// we not only canonicalize unbound inference variables, but we
+    /// *also* replace all free regions whatsoever. So for example a
+    /// query like `T: Trait<'static>` would be canonicalized to
+    ///
+    /// ```text
+    /// T: Trait<'?0>
+    /// ```
+    ///
+    /// with a mapping M that maps `'?0` to `'static`.
+    ///
+    /// To get a good understanding of what is happening here, check
+    /// out the [chapter in the rustc guide][c].
+    ///
+    /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html#canonicalizing-the-query
+    pub fn canonicalize_query<V>(&self, value: &V) -> (V::Canonicalized, CanonicalVarValues<'tcx>)
+    where
+        V: Canonicalize<'gcx, 'tcx>,
+    {
+        self.tcx.sess.perf_stats.queries_canonicalized.increment();
+
+        Canonicalizer::canonicalize(
+            value,
+            Some(self),
+            self.tcx,
+            CanonicalizeAllFreeRegions(true),
+        )
+    }
+
+    /// Canonicalizes a query *response* `V`. When we canonicalize a
+    /// query response, we only canonicalize unbound inference
+    /// variables, and we leave other free regions alone. So,
+    /// continuing with the example from `canonicalize_query`, if
+    /// there was an input query `T: Trait<'static>`, it would have
+    /// been canonicalized to
+    ///
+    /// ```text
+    /// T: Trait<'?0>
+    /// ```
+    ///
+    /// with a mapping M that maps `'?0` to `'static`. But if we found that there
+    /// exists only one possible impl of `Trait`, and it looks like
+    ///
+    ///     impl<T> Trait<'static> for T { .. }
+    ///
+    /// then we would prepare a query result R that (among other
+    /// things) includes a mapping to `'?0 := 'static`. When
+    /// canonicalizing this query result R, we would leave this
+    /// reference to `'static` alone.
+    ///
+    /// To get a good understanding of what is happening here, check
+    /// out the [chapter in the rustc guide][c].
+    ///
+    /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html#canonicalizing-the-query-result
+    pub fn canonicalize_response<V>(
+        &self,
+        value: &V,
+    ) -> (V::Canonicalized, CanonicalVarValues<'tcx>)
+    where
+        V: Canonicalize<'gcx, 'tcx>,
+    {
+        Canonicalizer::canonicalize(
+            value,
+            Some(self),
+            self.tcx,
+            CanonicalizeAllFreeRegions(false),
+        )
+    }
+}
+
+/// If this flag is true, then all free regions will be replaced with
+/// a canonical var. This is used to make queries as generic as
+/// possible. For example, the query `F: Foo<'static>` would be
+/// canonicalized to `F: Foo<'0>`.
+struct CanonicalizeAllFreeRegions(bool);
+
+struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>,
+    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+    variables: IndexVec<CanonicalVar, CanonicalVarInfo>,
+    indices: FxHashMap<Kind<'tcx>, CanonicalVar>,
+    var_values: IndexVec<CanonicalVar, Kind<'tcx>>,
+    canonicalize_all_free_regions: CanonicalizeAllFreeRegions,
+    needs_canonical_flags: TypeFlags,
+}
+
+impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> {
+        self.tcx
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match *r {
+            ty::ReLateBound(..) => {
+                // leave bound regions alone
+                r
+            }
+
+            ty::ReVar(vid) => {
+                let r = self.infcx
+                    .unwrap()
+                    .borrow_region_constraints()
+                    .opportunistic_resolve_var(self.tcx, vid);
+                let info = CanonicalVarInfo {
+                    kind: CanonicalVarKind::Region,
+                };
+                debug!(
+                    "canonical: region var found with vid {:?}, \
+                     opportunistically resolved to {:?}",
+                    vid, r
+                );
+                let cvar = self.canonical_var(info, Kind::from(r));
+                self.tcx().mk_region(ty::ReCanonical(cvar))
+            }
+
+            ty::ReStatic
+            | ty::ReEarlyBound(..)
+            | ty::ReFree(_)
+            | ty::ReScope(_)
+            | ty::ReSkolemized(..)
+            | ty::ReEmpty
+            | ty::ReErased => {
+                if self.canonicalize_all_free_regions.0 {
+                    let info = CanonicalVarInfo {
+                        kind: CanonicalVarKind::Region,
+                    };
+                    let cvar = self.canonical_var(info, Kind::from(r));
+                    self.tcx().mk_region(ty::ReCanonical(cvar))
+                } else {
+                    r
+                }
+            }
+
+            ty::ReClosureBound(..) | ty::ReCanonical(_) => {
+                bug!("canonical region encountered during canonicalization")
+            }
+        }
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match t.sty {
+            ty::TyInfer(ty::TyVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::General, t),
+
+            ty::TyInfer(ty::IntVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Int, t),
+
+            ty::TyInfer(ty::FloatVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Float, t),
+
+            ty::TyInfer(ty::FreshTy(_))
+            | ty::TyInfer(ty::FreshIntTy(_))
+            | ty::TyInfer(ty::FreshFloatTy(_)) => {
+                bug!("encountered a fresh type during canonicalization")
+            }
+
+            ty::TyInfer(ty::CanonicalTy(_)) => {
+                bug!("encountered a canonical type during canonicalization")
+            }
+
+            // Replace a `()` that "would've fallen back" to `!` with just `()`.
+            ty::TyTuple(ref tys, true) => {
+                assert!(tys.is_empty());
+                self.tcx().mk_nil()
+            }
+
+            ty::TyClosure(..)
+            | ty::TyGenerator(..)
+            | ty::TyGeneratorWitness(..)
+            | ty::TyBool
+            | ty::TyChar
+            | ty::TyInt(..)
+            | ty::TyUint(..)
+            | ty::TyFloat(..)
+            | ty::TyAdt(..)
+            | ty::TyStr
+            | ty::TyError
+            | ty::TyArray(..)
+            | ty::TySlice(..)
+            | ty::TyRawPtr(..)
+            | ty::TyRef(..)
+            | ty::TyFnDef(..)
+            | ty::TyFnPtr(_)
+            | ty::TyDynamic(..)
+            | ty::TyNever
+            | ty::TyTuple(_, false)
+            | ty::TyProjection(..)
+            | ty::TyForeign(..)
+            | ty::TyParam(..)
+            | ty::TyAnon(..) => {
+                if t.flags.intersects(self.needs_canonical_flags) {
+                    t.super_fold_with(self)
+                } else {
+                    t
+                }
+            }
+        }
+    }
+}
+
+impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
+    /// The main `canonicalize` method, shared impl of
+    /// `canonicalize_query` and `canonicalize_response`.
+    fn canonicalize<V>(
+        value: &V,
+        infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>,
+        tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+        canonicalize_all_free_regions: CanonicalizeAllFreeRegions,
+    ) -> (V::Canonicalized, CanonicalVarValues<'tcx>)
+    where
+        V: Canonicalize<'gcx, 'tcx>,
+    {
+        debug_assert!(
+            !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS),
+            "canonicalizing a canonical value: {:?}",
+            value,
+        );
+
+        let needs_canonical_flags = if canonicalize_all_free_regions.0 {
+            TypeFlags::HAS_FREE_REGIONS | TypeFlags::KEEP_IN_LOCAL_TCX
+        } else {
+            TypeFlags::KEEP_IN_LOCAL_TCX
+        };
+
+        let gcx = tcx.global_tcx();
+
+        // Fast path: nothing that needs to be canonicalized.
+        if !value.has_type_flags(needs_canonical_flags) {
+            let out_value = gcx.lift(value).unwrap();
+            let canon_value = V::intern(
+                gcx,
+                Canonical {
+                    variables: Slice::empty(),
+                    value: out_value,
+                },
+            );
+            let values = CanonicalVarValues {
+                var_values: IndexVec::default(),
+            };
+            return (canon_value, values);
+        }
+
+        let mut canonicalizer = Canonicalizer {
+            infcx,
+            tcx,
+            canonicalize_all_free_regions,
+            needs_canonical_flags,
+            variables: IndexVec::default(),
+            indices: FxHashMap::default(),
+            var_values: IndexVec::default(),
+        };
+        let out_value = value.fold_with(&mut canonicalizer);
+
+        // Once we have canonicalized `out_value`, it should not
+        // contain anything that ties it to this inference context
+        // anymore, so it should live in the global arena.
+        let out_value = gcx.lift(&out_value).unwrap_or_else(|| {
+            bug!(
+                "failed to lift `{:?}`, canonicalized from `{:?}`",
+                out_value,
+                value
+            )
+        });
+
+        let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables.raw);
+
+        let canonical_value = V::intern(
+            gcx,
+            Canonical {
+                variables: canonical_variables,
+                value: out_value,
+            },
+        );
+        let canonical_var_values = CanonicalVarValues {
+            var_values: canonicalizer.var_values,
+        };
+        (canonical_value, canonical_var_values)
+    }
+
+    /// Creates a canonical variable replacing `kind` from the input,
+    /// or returns an existing variable if `kind` has already been
+    /// seen. `kind` is expected to be an unbound variable (or
+    /// potentially a free region).
+    fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> CanonicalVar {
+        let Canonicalizer {
+            indices,
+            variables,
+            var_values,
+            ..
+        } = self;
+
+        indices
+            .entry(kind)
+            .or_insert_with(|| {
+                let cvar1 = variables.push(info);
+                let cvar2 = var_values.push(kind);
+                assert_eq!(cvar1, cvar2);
+                cvar1
+            })
+            .clone()
+    }
+
+    /// Given a type variable `ty_var` of the given kind, first check
+    /// if `ty_var` is bound to anything; if so, canonicalize
+    /// *that*. Otherwise, create a new canonical variable for
+    /// `ty_var`.
+    fn canonicalize_ty_var(&mut self, ty_kind: CanonicalTyVarKind, ty_var: Ty<'tcx>) -> Ty<'tcx> {
+        let infcx = self.infcx.expect("encountered ty-var without infcx");
+        let bound_to = infcx.shallow_resolve(ty_var);
+        if bound_to != ty_var {
+            self.fold_ty(bound_to)
+        } else {
+            let info = CanonicalVarInfo {
+                kind: CanonicalVarKind::Ty(ty_kind),
+            };
+            let cvar = self.canonical_var(info, Kind::from(ty_var));
+            self.tcx().mk_infer(ty::InferTy::CanonicalTy(cvar))
+        }
+    }
+}
+
+impl<'tcx, V> Canonical<'tcx, V> {
+    /// Instantiate the wrapped value, replacing each canonical value
+    /// with the value given in `var_values`.
+    fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
+    where
+        V: TypeFoldable<'tcx>,
+    {
+        self.substitute_projected(tcx, var_values, |value| value)
+    }
+
+    /// Invoke `projection_fn` with `self.value` to get a value V that
+    /// is expressed in terms of the same canonical variables bound in
+    /// `self`. Apply the substitution `var_values` to this value V,
+    /// replacing each of the canonical variables.
+    fn substitute_projected<T>(
+        &self,
+        tcx: TyCtxt<'_, '_, 'tcx>,
+        var_values: &CanonicalVarValues<'tcx>,
+        projection_fn: impl FnOnce(&V) -> &T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        assert_eq!(self.variables.len(), var_values.var_values.len());
+        let value = projection_fn(&self.value);
+        substitute_value(tcx, var_values, value)
+    }
+}
+
+/// Substitute the values from `var_values` into `value`. `var_values`
+/// must be values for the set of cnaonical variables that appear in
+/// `value`.
+fn substitute_value<'a, 'tcx, T>(
+    tcx: TyCtxt<'_, '_, 'tcx>,
+    var_values: &CanonicalVarValues<'tcx>,
+    value: &'a T,
+) -> T
+where
+    T: TypeFoldable<'tcx>,
+{
+    if var_values.var_values.is_empty() {
+        debug_assert!(!value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS));
+        value.clone()
+    } else if !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) {
+        value.clone()
+    } else {
+        value.fold_with(&mut CanonicalVarValuesSubst { tcx, var_values })
+    }
+}
+
+struct CanonicalVarValuesSubst<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+    var_values: &'cx CanonicalVarValues<'tcx>,
+}
+
+impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'gcx, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx> {
+        self.tcx
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match t.sty {
+            ty::TyInfer(ty::InferTy::CanonicalTy(c)) => {
+                match self.var_values.var_values[c].unpack() {
+                    UnpackedKind::Type(ty) => ty,
+                    r => bug!("{:?} is a type but value is {:?}", c, r),
+                }
+            }
+            _ => {
+                if !t.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) {
+                    t
+                } else {
+                    t.super_fold_with(self)
+                }
+            }
+        }
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match r {
+            ty::RegionKind::ReCanonical(c) => match self.var_values.var_values[*c].unpack() {
+                UnpackedKind::Lifetime(l) => l,
+                r => bug!("{:?} is a region but value is {:?}", c, r),
+            },
+            _ => r.super_fold_with(self),
+        }
+    }
+}
+
+CloneTypeFoldableAndLiftImpls! {
+    for <'tcx> {
+        ::infer::canonical::Certainty,
+        ::infer::canonical::CanonicalVarInfo,
+        ::infer::canonical::CanonicalVarInfos<'tcx>,
+        ::infer::canonical::CanonicalVarKind,
+    }
+}
+
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, C> TypeFoldable<'tcx> for Canonical<'tcx, C> {
+        variables,
+        value,
+    } where C: TypeFoldable<'tcx>
+}
+
+impl<'tcx> CanonicalVarValues<'tcx> {
+    fn iter<'a>(&'a self) -> impl Iterator<Item = Kind<'tcx>> + 'a {
+        self.var_values.iter().cloned()
+    }
+
+    fn len(&self) -> usize {
+        self.var_values.len()
+    }
+}
+
+impl<'a, 'tcx> IntoIterator for &'a CanonicalVarValues<'tcx> {
+    type Item = Kind<'tcx>;
+    type IntoIter = ::std::iter::Cloned<::std::slice::Iter<'a, Kind<'tcx>>>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.var_values.iter().cloned()
+    }
+}
+
+BraceStructLiftImpl! {
+    impl<'a, 'tcx> Lift<'tcx> for CanonicalVarValues<'a> {
+        type Lifted = CanonicalVarValues<'tcx>;
+        var_values,
+    }
+}
+
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for CanonicalVarValues<'tcx> {
+        var_values,
+    }
+}
+
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for QueryRegionConstraints<'tcx> {
+        region_outlives, ty_outlives
+    }
+}
+
+BraceStructLiftImpl! {
+    impl<'a, 'tcx> Lift<'tcx> for QueryRegionConstraints<'a> {
+        type Lifted = QueryRegionConstraints<'tcx>;
+        region_outlives, ty_outlives
+    }
+}
+
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, R> TypeFoldable<'tcx> for QueryResult<'tcx, R> {
+        var_values, region_constraints, certainty, value
+    } where R: TypeFoldable<'tcx>,
+}
+
+BraceStructLiftImpl! {
+    impl<'a, 'tcx, R> Lift<'tcx> for QueryResult<'a, R> {
+        type Lifted = QueryResult<'tcx, R::Lifted>;
+        var_values, region_constraints, certainty, value
+    } where R: Lift<'tcx>
+}
+
+impl<'tcx> Index<CanonicalVar> for CanonicalVarValues<'tcx> {
+    type Output = Kind<'tcx>;
+
+    fn index(&self, value: CanonicalVar) -> &Kind<'tcx> {
+        &self.var_values[value]
+    }
+}
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 469cb459112..1c581c44464 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -476,6 +476,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                 }
             }
 
+            ty::ReCanonical(..) |
             ty::ReClosureBound(..) => {
                 span_bug!(
                     self.span,
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 52caf46878b..96c23098821 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -154,6 +154,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             }
 
             // We shouldn't encounter an error message with ReClosureBound.
+            ty::ReCanonical(..) |
             ty::ReClosureBound(..) => {
                 bug!("encountered unexpected ReClosureBound: {:?}", region,);
             }
@@ -975,6 +976,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
         match *values {
             infer::Types(ref exp_found) => self.expected_found_str_ty(exp_found),
+            infer::Regions(ref exp_found) => self.expected_found_str(exp_found),
             infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found),
             infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found),
         }
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs
index ee0921f4b07..6074bfd083d 100644
--- a/src/librustc/infer/freshen.rs
+++ b/src/librustc/infer/freshen.rs
@@ -114,9 +114,10 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
                 self.tcx().types.re_erased
             }
 
+            ty::ReCanonical(..) |
             ty::ReClosureBound(..) => {
                 bug!(
-                    "encountered unexpected ReClosureBound: {:?}",
+                    "encountered unexpected region: {:?}",
                     r,
                 );
             }
@@ -170,6 +171,9 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
                 t
             }
 
+            ty::TyInfer(ty::CanonicalTy(..)) =>
+                bug!("encountered canonical ty during freshening"),
+
             ty::TyGenerator(..) |
             ty::TyBool |
             ty::TyChar |
diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs
index 3ac4ec5bee4..00b2ac7449f 100644
--- a/src/librustc/infer/lexical_region_resolve/mod.rs
+++ b/src/librustc/infer/lexical_region_resolve/mod.rs
@@ -258,6 +258,8 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
     fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
         let tcx = self.region_rels.tcx;
         match (a, b) {
+            (&ty::ReCanonical(..), _) |
+            (_, &ty::ReCanonical(..)) |
             (&ty::ReClosureBound(..), _) |
             (_, &ty::ReClosureBound(..)) |
             (&ReLateBound(..), _) |
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 5d44b2043e2..fe919775da0 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -21,21 +21,20 @@ use hir::def_id::DefId;
 use middle::free_region::RegionRelations;
 use middle::region;
 use middle::lang_items;
-use mir::tcx::PlaceTy;
 use ty::subst::Substs;
 use ty::{TyVid, IntVid, FloatVid};
 use ty::{self, Ty, TyCtxt};
 use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
-use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
+use ty::fold::TypeFoldable;
 use ty::relate::RelateResult;
-use traits::{self, ObligationCause, PredicateObligations, Reveal};
+use traits::{self, ObligationCause, PredicateObligations};
 use rustc_data_structures::unify as ut;
 use std::cell::{Cell, RefCell, Ref, RefMut};
 use std::collections::BTreeMap;
 use std::fmt;
 use syntax::ast;
 use errors::DiagnosticBuilder;
-use syntax_pos::{self, Span, DUMMY_SP};
+use syntax_pos::{self, Span};
 use util::nodemap::FxHashMap;
 use arena::DroplessArena;
 
@@ -50,6 +49,7 @@ use self::unify_key::ToType;
 
 pub mod anon_types;
 pub mod at;
+pub mod canonical;
 mod combine;
 mod equate;
 pub mod error_reporting;
@@ -68,6 +68,7 @@ pub mod type_variable;
 pub mod unify_key;
 
 #[must_use]
+#[derive(Debug)]
 pub struct InferOk<'tcx, T> {
     pub value: T,
     pub obligations: PredicateObligations<'tcx>,
@@ -191,6 +192,7 @@ pub type SkolemizationMap<'tcx> = BTreeMap<ty::BoundRegion, ty::Region<'tcx>>;
 #[derive(Clone, Debug)]
 pub enum ValuePairs<'tcx> {
     Types(ExpectedFound<Ty<'tcx>>),
+    Regions(ExpectedFound<ty::Region<'tcx>>),
     TraitRefs(ExpectedFound<ty::TraitRef<'tcx>>),
     PolyTraitRefs(ExpectedFound<ty::PolyTraitRef<'tcx>>),
 }
@@ -472,6 +474,12 @@ impl<'tcx, T> InferOk<'tcx, T> {
     }
 }
 
+impl<'tcx> InferOk<'tcx, ()> {
+    pub fn into_obligations(self) -> PredicateObligations<'tcx> {
+        self.obligations
+    }
+}
+
 #[must_use = "once you start a snapshot, you should always consume it"]
 pub struct CombinedSnapshot<'a, 'tcx:'a> {
     projection_cache_snapshot: traits::ProjectionCacheSnapshot,
@@ -484,176 +492,7 @@ pub struct CombinedSnapshot<'a, 'tcx:'a> {
     _in_progress_tables: Option<Ref<'a, ty::TypeckTables<'tcx>>>,
 }
 
-/// Helper trait for shortening the lifetimes inside a
-/// value for post-type-checking normalization.
-pub trait TransNormalize<'gcx>: TypeFoldable<'gcx> {
-    fn trans_normalize<'a, 'tcx>(&self,
-                                 infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-                                 param_env: ty::ParamEnv<'tcx>)
-                                 -> Self;
-}
-
-macro_rules! items { ($($item:item)+) => ($($item)+) }
-macro_rules! impl_trans_normalize {
-    ($lt_gcx:tt, $($ty:ty),+) => {
-        items!($(impl<$lt_gcx> TransNormalize<$lt_gcx> for $ty {
-            fn trans_normalize<'a, 'tcx>(&self,
-                                         infcx: &InferCtxt<'a, $lt_gcx, 'tcx>,
-                                         param_env: ty::ParamEnv<'tcx>)
-                                         -> Self {
-                infcx.normalize_projections_in(param_env, self)
-            }
-        })+);
-    }
-}
-
-impl_trans_normalize!('gcx,
-    Ty<'gcx>,
-    &'gcx ty::Const<'gcx>,
-    &'gcx Substs<'gcx>,
-    ty::FnSig<'gcx>,
-    ty::PolyFnSig<'gcx>,
-    ty::ClosureSubsts<'gcx>,
-    ty::PolyTraitRef<'gcx>,
-    ty::ExistentialTraitRef<'gcx>
-);
-
-impl<'gcx> TransNormalize<'gcx> for PlaceTy<'gcx> {
-    fn trans_normalize<'a, 'tcx>(&self,
-                                 infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-                                 param_env: ty::ParamEnv<'tcx>)
-                                 -> Self {
-        match *self {
-            PlaceTy::Ty { ty } => PlaceTy::Ty { ty: ty.trans_normalize(infcx, param_env) },
-            PlaceTy::Downcast { adt_def, substs, variant_index } => {
-                PlaceTy::Downcast {
-                    adt_def,
-                    substs: substs.trans_normalize(infcx, param_env),
-                    variant_index,
-                }
-            }
-        }
-    }
-}
-
-// NOTE: Callable from trans only!
-impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
-    /// Currently, higher-ranked type bounds inhibit normalization. Therefore,
-    /// each time we erase them in translation, we need to normalize
-    /// the contents.
-    pub fn erase_late_bound_regions_and_normalize<T>(self, value: &ty::Binder<T>)
-        -> T
-        where T: TransNormalize<'tcx>
-    {
-        assert!(!value.needs_subst());
-        let value = self.erase_late_bound_regions(value);
-        self.fully_normalize_associated_types_in(&value)
-    }
-
-    /// Fully normalizes any associated types in `value`, using an
-    /// empty environment and `Reveal::All` mode (therefore, suitable
-    /// only for monomorphized code during trans, basically).
-    pub fn fully_normalize_associated_types_in<T>(self, value: &T) -> T
-        where T: TransNormalize<'tcx>
-    {
-        debug!("fully_normalize_associated_types_in(t={:?})", value);
-
-        let param_env = ty::ParamEnv::empty(Reveal::All);
-        let value = self.erase_regions(value);
-
-        if !value.has_projections() {
-            return value;
-        }
-
-        self.infer_ctxt().enter(|infcx| {
-            value.trans_normalize(&infcx, param_env)
-        })
-    }
-
-    /// Does a best-effort to normalize any associated types in
-    /// `value`; this includes revealing specializable types, so this
-    /// should be not be used during type-checking, but only during
-    /// optimization and code generation.
-    pub fn normalize_associated_type_in_env<T>(
-        self, value: &T, env: ty::ParamEnv<'tcx>
-    ) -> T
-        where T: TransNormalize<'tcx>
-    {
-        debug!("normalize_associated_type_in_env(t={:?})", value);
-
-        let value = self.erase_regions(value);
-
-        if !value.has_projections() {
-            return value;
-        }
-
-        self.infer_ctxt().enter(|infcx| {
-            value.trans_normalize(&infcx, env.reveal_all())
-       })
-    }
-}
-
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
-    fn normalize_projections_in<T>(&self, param_env: ty::ParamEnv<'tcx>, value: &T) -> T::Lifted
-        where T: TypeFoldable<'tcx> + ty::Lift<'gcx>
-    {
-        let mut selcx = traits::SelectionContext::new(self);
-        let cause = traits::ObligationCause::dummy();
-        let traits::Normalized { value: result, obligations } =
-            traits::normalize(&mut selcx, param_env, cause, value);
-
-        debug!("normalize_projections_in: result={:?} obligations={:?}",
-                result, obligations);
-
-        let mut fulfill_cx = traits::FulfillmentContext::new();
-
-        for obligation in obligations {
-            fulfill_cx.register_predicate_obligation(self, obligation);
-        }
-
-        self.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &result)
-    }
-
-    /// Finishes processes any obligations that remain in the
-    /// fulfillment context, and then returns the result with all type
-    /// variables removed and regions erased. Because this is intended
-    /// for use after type-check has completed, if any errors occur,
-    /// it will panic. It is used during normalization and other cases
-    /// where processing the obligations in `fulfill_cx` may cause
-    /// type inference variables that appear in `result` to be
-    /// unified, and hence we need to process those obligations to get
-    /// the complete picture of the type.
-    pub fn drain_fulfillment_cx_or_panic<T>(&self,
-                                            span: Span,
-                                            fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
-                                            result: &T)
-                                            -> T::Lifted
-        where T: TypeFoldable<'tcx> + ty::Lift<'gcx>
-    {
-        debug!("drain_fulfillment_cx_or_panic()");
-
-        // In principle, we only need to do this so long as `result`
-        // contains unbound type parameters. It could be a slight
-        // optimization to stop iterating early.
-        match fulfill_cx.select_all_or_error(self) {
-            Ok(()) => { }
-            Err(errors) => {
-                span_bug!(span, "Encountered errors `{:?}` resolving bounds after type-checking",
-                          errors);
-            }
-        }
-
-        let result = self.resolve_type_vars_if_possible(result);
-        let result = self.tcx.erase_regions(&result);
-
-        match self.tcx.lift_to_global(&result) {
-            Some(result) => result,
-            None => {
-                span_bug!(span, "Uninferred types/regions in `{:?}`", result);
-            }
-        }
-    }
-
     pub fn is_in_snapshot(&self) -> bool {
         self.in_snapshot.get()
     }
@@ -1212,6 +1051,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.borrow_region_constraints().take_and_reset_data()
     }
 
+    /// Gives temporary access to the region constraint data.
+    #[allow(non_camel_case_types)] // bug with impl trait
+    pub fn with_region_constraints<R>(
+        &self,
+        op: impl FnOnce(&RegionConstraintData<'tcx>) -> R,
+    ) -> R {
+        let region_constraints = self.borrow_region_constraints();
+        op(region_constraints.data())
+    }
+
     /// Takes ownership of the list of variable regions. This implies
     /// that all the region constriants have already been taken, and
     /// hence that `resolve_regions_and_report_errors` can never be
@@ -1246,7 +1095,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 // it can be resolved to an int/float variable, which
                 // can then be recursively resolved, hence the
                 // recursion. Note though that we prevent type
-                // variables from unifying to other type variables
+                // variables from unifyxing to other type variables
                 // directly (though they may be embedded
                 // structurally), and we prevent cycles in any case,
                 // so this recursion should always be of very limited
@@ -1623,40 +1472,12 @@ impl RegionVariableOrigin {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ValuePairs<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        match *self {
-            ValuePairs::Types(ref ef) => {
-                ValuePairs::Types(ef.fold_with(folder))
-            }
-            ValuePairs::TraitRefs(ref ef) => {
-                ValuePairs::TraitRefs(ef.fold_with(folder))
-            }
-            ValuePairs::PolyTraitRefs(ref ef) => {
-                ValuePairs::PolyTraitRefs(ef.fold_with(folder))
-            }
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        match *self {
-            ValuePairs::Types(ref ef) => ef.visit_with(visitor),
-            ValuePairs::TraitRefs(ref ef) => ef.visit_with(visitor),
-            ValuePairs::PolyTraitRefs(ref ef) => ef.visit_with(visitor),
-        }
-    }
-}
-
-impl<'tcx> TypeFoldable<'tcx> for TypeTrace<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        TypeTrace {
-            cause: self.cause.fold_with(folder),
-            values: self.values.fold_with(folder)
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.cause.visit_with(visitor) || self.values.visit_with(visitor)
+EnumTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ValuePairs<'tcx> {
+        (ValuePairs::Types)(a),
+        (ValuePairs::Regions)(a),
+        (ValuePairs::TraitRefs)(a),
+        (ValuePairs::PolyTraitRefs)(a),
     }
 }
 
@@ -1667,4 +1488,3 @@ impl<'tcx> fmt::Debug for RegionObligation<'tcx> {
                self.sup_type)
     }
 }
-
diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs
index 36e657f78b4..e5461685bd4 100644
--- a/src/librustc/infer/outlives/obligations.rs
+++ b/src/librustc/infer/outlives/obligations.rs
@@ -99,6 +99,16 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
             .push((body_id, obligation));
     }
 
+    /// Trait queries just want to pass back type obligations "as is"
+    pub fn take_registered_region_obligations(
+        &self,
+    ) -> Vec<(ast::NodeId, RegionObligation<'tcx>)> {
+        ::std::mem::replace(
+            &mut *self.region_obligations.borrow_mut(),
+            vec![],
+        )
+    }
+
     /// Process the region obligations that must be proven (during
     /// `regionck`) for the given `body_id`, given information about
     /// the region bounds in scope and so forth. This function must be
diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs
index ed89d1d2f57..0c8e49fda18 100644
--- a/src/librustc/infer/region_constraints/mod.rs
+++ b/src/librustc/infer/region_constraints/mod.rs
@@ -350,6 +350,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         mem::replace(data, RegionConstraintData::default())
     }
 
+    pub fn data(&self) -> &RegionConstraintData<'tcx> {
+        &self.data
+    }
+
     fn in_snapshot(&self) -> bool {
         !self.undo_log.is_empty()
     }
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 56de2939ffa..77b3a87c0ed 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -57,8 +57,9 @@
 #![feature(inclusive_range)]
 #![feature(inclusive_range_syntax)]
 #![cfg_attr(windows, feature(libc))]
-#![feature(macro_vis_matcher)]
 #![feature(match_default_bindings)]
+#![feature(macro_lifetime_matcher)]
+#![feature(macro_vis_matcher)]
 #![feature(never_type)]
 #![feature(non_exhaustive)]
 #![feature(nonzero)]
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index a1381817223..4fa6594df16 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -34,7 +34,6 @@ use lint::levels::{LintLevelSets, LintLevelsBuilder};
 use middle::privacy::AccessLevels;
 use rustc_serialize::{Decoder, Decodable, Encoder, Encodable};
 use session::{config, early_error, Session};
-use traits::Reveal;
 use ty::{self, TyCtxt, Ty};
 use ty::layout::{LayoutError, LayoutOf, TyLayout};
 use util::nodemap::FxHashMap;
@@ -1055,7 +1054,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
     let mut cx = LateContext {
         tcx,
         tables: &ty::TypeckTables::empty(None),
-        param_env: ty::ParamEnv::empty(Reveal::UserFacing),
+        param_env: ty::ParamEnv::empty(),
         access_levels,
         lint_sess: LintSession::new(&tcx.sess.lint_store),
         last_ast_node_with_lint_attrs: ast::CRATE_NODE_ID,
diff --git a/src/librustc/macros.rs b/src/librustc/macros.rs
index 9a394e52481..d8a723e184d 100644
--- a/src/librustc/macros.rs
+++ b/src/librustc/macros.rs
@@ -119,6 +119,25 @@ macro_rules! impl_stable_hash_for {
             }
         }
     };
+
+    (impl<$tcx:lifetime $(, $T:ident)*> for struct $struct_name:path {
+        $($field:ident),* $(,)*
+    }) => {
+        impl<'a, $tcx, $($T,)*> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $struct_name
+            where $($T: ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>>),*
+        {
+            #[inline]
+            fn hash_stable<W: ::rustc_data_structures::stable_hasher::StableHasherResult>(&self,
+                                                  __ctx: &mut $crate::ich::StableHashingContext<'a>,
+                                                  __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher<W>) {
+                let $struct_name {
+                    $(ref $field),*
+                } = *self;
+
+                $( $field.hash_stable(__ctx, __hasher));*
+            }
+        }
+    };
 }
 
 #[macro_export]
@@ -138,3 +157,303 @@ macro_rules! impl_stable_hash_for_spanned {
     );
 }
 
+///////////////////////////////////////////////////////////////////////////
+// Lift and TypeFoldable macros
+//
+// When possible, use one of these (relatively) convenient macros to write
+// the impls for you.
+
+#[macro_export]
+macro_rules! CloneLiftImpls {
+    (for <$tcx:lifetime> { $($ty:ty,)+ }) => {
+        $(
+            impl<$tcx> $crate::ty::Lift<$tcx> for $ty {
+                type Lifted = Self;
+                fn lift_to_tcx<'a, 'gcx>(&self, _: $crate::ty::TyCtxt<'a, 'gcx, $tcx>) -> Option<Self> {
+                    Some(Clone::clone(self))
+                }
+            }
+        )+
+    };
+
+    ($($ty:ty,)+) => {
+        CloneLiftImpls! {
+            for <'tcx> {
+                $($ty,)+
+            }
+        }
+    };
+}
+
+/// Used for types that are `Copy` and which **do not care arena
+/// allocated data** (i.e., don't need to be folded).
+#[macro_export]
+macro_rules! CloneTypeFoldableImpls {
+    (for <$tcx:lifetime> { $($ty:ty,)+ }) => {
+        $(
+            impl<$tcx> $crate::ty::fold::TypeFoldable<$tcx> for $ty {
+                fn super_fold_with<'gcx: $tcx, F: $crate::ty::fold::TypeFolder<'gcx, $tcx>>(
+                    &self,
+                    _: &mut F
+                ) -> $ty {
+                    Clone::clone(self)
+                }
+
+                fn super_visit_with<F: $crate::ty::fold::TypeVisitor<$tcx>>(
+                    &self,
+                    _: &mut F)
+                    -> bool
+                {
+                    false
+                }
+            }
+        )+
+    };
+
+    ($($ty:ty,)+) => {
+        CloneTypeFoldableImpls! {
+            for <'tcx> {
+                $($ty,)+
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! CloneTypeFoldableAndLiftImpls {
+    ($($t:tt)*) => {
+        CloneTypeFoldableImpls! { $($t)* }
+        CloneLiftImpls! { $($t)* }
+    }
+}
+
+#[macro_export]
+macro_rules! BraceStructLiftImpl {
+    (impl<$($p:tt),*> Lift<$tcx:tt> for $s:path {
+        type Lifted = $lifted:ty;
+        $($field:ident),* $(,)*
+    } $(where $($wc:tt)*)*) => {
+        impl<$($p),*> $crate::ty::Lift<$tcx> for $s
+            $(where $($wc)*)*
+        {
+            type Lifted = $lifted;
+
+            fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<$lifted> {
+                $(let $field = tcx.lift(&self.$field)?;)*
+                Some(Self::Lifted { $($field),* })
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! EnumLiftImpl {
+    (impl<$($p:tt),*> Lift<$tcx:tt> for $s:path {
+        type Lifted = $lifted:ty;
+        $(
+            ($variant:path) ( $( $variant_arg:ident),* )
+        ),*
+        $(,)*
+    } $(where $($wc:tt)*)*) => {
+        impl<$($p),*> $crate::ty::Lift<$tcx> for $s
+            $(where $($wc)*)*
+        {
+            type Lifted = $lifted;
+
+            fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<$lifted> {
+                match self {
+                    $($variant ( $($variant_arg),* ) => {
+                        Some($variant ( $(tcx.lift($variant_arg)?),* ))
+                    })*
+                }
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! BraceStructTypeFoldableImpl {
+    (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path {
+        $($field:ident),* $(,)*
+    } $(where $($wc:tt)*)*) => {
+        impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s
+            $(where $($wc)*)*
+        {
+            fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>(
+                &self,
+                folder: &mut V,
+            ) -> Self {
+                let $s { $($field,)* } = self;
+                $s { $($field: $crate::ty::fold::TypeFoldable::fold_with($field, folder),)* }
+            }
+
+            fn super_visit_with<V: $crate::ty::fold::TypeVisitor<$tcx>>(
+                &self,
+                visitor: &mut V,
+            ) -> bool {
+                let $s { $($field,)* } = self;
+                false $(|| $crate::ty::fold::TypeFoldable::visit_with($field, visitor))*
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! TupleStructTypeFoldableImpl {
+    (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path {
+        $($field:ident),* $(,)*
+    } $(where $($wc:tt)*)*) => {
+        impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s
+            $(where $($wc)*)*
+        {
+            fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>(
+                &self,
+                folder: &mut V,
+            ) -> Self {
+                let $s($($field,)*)= self;
+                $s($($crate::ty::fold::TypeFoldable::fold_with($field, folder),)*)
+            }
+
+            fn super_visit_with<V: $crate::ty::fold::TypeVisitor<$tcx>>(
+                &self,
+                visitor: &mut V,
+            ) -> bool {
+                let $s($($field,)*) = self;
+                false $(|| $crate::ty::fold::TypeFoldable::visit_with($field, visitor))*
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! EnumTypeFoldableImpl {
+    (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path {
+        $($variants:tt)*
+    } $(where $($wc:tt)*)*) => {
+        impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s
+            $(where $($wc)*)*
+        {
+            fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>(
+                &self,
+                folder: &mut V,
+            ) -> Self {
+                EnumTypeFoldableImpl!(@FoldVariants(self, folder) input($($variants)*) output())
+            }
+
+            fn super_visit_with<V: $crate::ty::fold::TypeVisitor<$tcx>>(
+                &self,
+                visitor: &mut V,
+            ) -> bool {
+                EnumTypeFoldableImpl!(@VisitVariants(self, visitor) input($($variants)*) output())
+            }
+        }
+    };
+
+    (@FoldVariants($this:expr, $folder:expr) input() output($($output:tt)*)) => {
+        match $this {
+            $($output)*
+        }
+    };
+
+    (@FoldVariants($this:expr, $folder:expr)
+     input( ($variant:path) ( $($variant_arg:ident),* ) , $($input:tt)*)
+     output( $($output:tt)*) ) => {
+        EnumTypeFoldableImpl!(
+            @FoldVariants($this, $folder)
+                input($($input)*)
+                output(
+                    $variant ( $($variant_arg),* ) => {
+                        $variant (
+                            $($crate::ty::fold::TypeFoldable::fold_with($variant_arg, $folder)),*
+                        )
+                    }
+                    $($output)*
+                )
+        )
+    };
+
+    (@FoldVariants($this:expr, $folder:expr)
+     input( ($variant:path) { $($variant_arg:ident),* $(,)* } , $($input:tt)*)
+     output( $($output:tt)*) ) => {
+        EnumTypeFoldableImpl!(
+            @FoldVariants($this, $folder)
+                input($($input)*)
+                output(
+                    $variant { $($variant_arg),* } => {
+                        $variant {
+                            $($variant_arg: $crate::ty::fold::TypeFoldable::fold_with(
+                                $variant_arg, $folder
+                            )),* }
+                    }
+                    $($output)*
+                )
+        )
+    };
+
+    (@FoldVariants($this:expr, $folder:expr)
+     input( ($variant:path), $($input:tt)*)
+     output( $($output:tt)*) ) => {
+        EnumTypeFoldableImpl!(
+            @FoldVariants($this, $folder)
+                input($($input)*)
+                output(
+                    $variant => { $variant }
+                    $($output)*
+                )
+        )
+    };
+
+    (@VisitVariants($this:expr, $visitor:expr) input() output($($output:tt)*)) => {
+        match $this {
+            $($output)*
+        }
+    };
+
+    (@VisitVariants($this:expr, $visitor:expr)
+     input( ($variant:path) ( $($variant_arg:ident),* ) , $($input:tt)*)
+     output( $($output:tt)*) ) => {
+        EnumTypeFoldableImpl!(
+            @VisitVariants($this, $visitor)
+                input($($input)*)
+                output(
+                    $variant ( $($variant_arg),* ) => {
+                        false $(|| $crate::ty::fold::TypeFoldable::visit_with(
+                            $variant_arg, $visitor
+                        ))*
+                    }
+                    $($output)*
+                )
+        )
+    };
+
+    (@VisitVariants($this:expr, $visitor:expr)
+     input( ($variant:path) { $($variant_arg:ident),* $(,)* } , $($input:tt)*)
+     output( $($output:tt)*) ) => {
+        EnumTypeFoldableImpl!(
+            @VisitVariants($this, $visitor)
+                input($($input)*)
+                output(
+                    $variant { $($variant_arg),* } => {
+                        false $(|| $crate::ty::fold::TypeFoldable::visit_with(
+                            $variant_arg, $visitor
+                        ))*
+                    }
+                    $($output)*
+                )
+        )
+    };
+
+    (@VisitVariants($this:expr, $visitor:expr)
+     input( ($variant:path), $($input:tt)*)
+     output( $($output:tt)*) ) => {
+        EnumTypeFoldableImpl!(
+            @VisitVariants($this, $visitor)
+                input($($input)*)
+                output(
+                    $variant => { false }
+                    $($output)*
+                )
+        )
+    };
+}
+
diff --git a/src/librustc/mir/cache.rs b/src/librustc/mir/cache.rs
index 45cb70d0070..9b75c19a875 100644
--- a/src/librustc/mir/cache.rs
+++ b/src/librustc/mir/cache.rs
@@ -76,3 +76,7 @@ fn calculate_predecessors(mir: &Mir) -> IndexVec<BasicBlock, Vec<BasicBlock>> {
 
     result
 }
+
+CloneTypeFoldableAndLiftImpls! {
+    Cache,
+}
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index e39765699f9..939710ffd2b 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -2109,148 +2109,90 @@ pub enum ClosureOutlivesSubject<'tcx> {
  * TypeFoldable implementations for MIR types
  */
 
-impl<'tcx> TypeFoldable<'tcx> for Mir<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        Mir {
-            basic_blocks: self.basic_blocks.fold_with(folder),
-            visibility_scopes: self.visibility_scopes.clone(),
-            visibility_scope_info: self.visibility_scope_info.clone(),
-            promoted: self.promoted.fold_with(folder),
-            yield_ty: self.yield_ty.fold_with(folder),
-            generator_drop: self.generator_drop.fold_with(folder),
-            generator_layout: self.generator_layout.fold_with(folder),
-            local_decls: self.local_decls.fold_with(folder),
-            arg_count: self.arg_count,
-            upvar_decls: self.upvar_decls.clone(),
-            spread_arg: self.spread_arg,
-            span: self.span,
-            cache: cache::Cache::new()
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.basic_blocks.visit_with(visitor) ||
-        self.generator_drop.visit_with(visitor) ||
-        self.generator_layout.visit_with(visitor) ||
-        self.yield_ty.visit_with(visitor) ||
-        self.promoted.visit_with(visitor)     ||
-        self.local_decls.visit_with(visitor)
-    }
+CloneTypeFoldableAndLiftImpls! {
+    Mutability,
+    SourceInfo,
+    UpvarDecl,
+    ValidationOp,
+    VisibilityScopeData,
+    VisibilityScope,
+    VisibilityScopeInfo,
 }
 
-impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        GeneratorLayout {
-            fields: self.fields.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.fields.visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for Mir<'tcx> {
+        basic_blocks,
+        visibility_scopes,
+        visibility_scope_info,
+        promoted,
+        yield_ty,
+        generator_drop,
+        generator_layout,
+        local_decls,
+        arg_count,
+        upvar_decls,
+        spread_arg,
+        span,
+        cache,
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for LocalDecl<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        LocalDecl {
-            ty: self.ty.fold_with(folder),
-            ..self.clone()
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.ty.visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
+        fields
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for BasicBlockData<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        BasicBlockData {
-            statements: self.statements.fold_with(folder),
-            terminator: self.terminator.fold_with(folder),
-            is_cleanup: self.is_cleanup
-        }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for LocalDecl<'tcx> {
+        mutability,
+        is_user_variable,
+        internal,
+        ty,
+        name,
+        source_info,
+        syntactic_scope,
     }
+}
 
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.statements.visit_with(visitor) || self.terminator.visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for BasicBlockData<'tcx> {
+        statements,
+        terminator,
+        is_cleanup,
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx, Place<'tcx>> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ValidationOperand {
-            place: self.place.fold_with(folder),
-            ty: self.ty.fold_with(folder),
-            re: self.re,
-            mutbl: self.mutbl,
-        }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx, Place<'tcx>> {
+        place, ty, re, mutbl
     }
+}
 
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.place.visit_with(visitor) || self.ty.visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
+        source_info, kind
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        use mir::StatementKind::*;
-
-        let kind = match self.kind {
-            Assign(ref place, ref rval) => Assign(place.fold_with(folder), rval.fold_with(folder)),
-            SetDiscriminant { ref place, variant_index } => SetDiscriminant {
-                place: place.fold_with(folder),
-                variant_index,
-            },
-            StorageLive(ref local) => StorageLive(local.fold_with(folder)),
-            StorageDead(ref local) => StorageDead(local.fold_with(folder)),
-            InlineAsm { ref asm, ref outputs, ref inputs } => InlineAsm {
-                asm: asm.clone(),
-                outputs: outputs.fold_with(folder),
-                inputs: inputs.fold_with(folder)
-            },
-
-            // Note for future: If we want to expose the region scopes
-            // during the fold, we need to either generalize EndRegion
-            // to carry `[ty::Region]`, or extend the `TypeFolder`
-            // trait with a `fn fold_scope`.
-            EndRegion(ref region_scope) => EndRegion(region_scope.clone()),
-
-            Validate(ref op, ref places) =>
-                Validate(op.clone(),
-                         places.iter().map(|operand| operand.fold_with(folder)).collect()),
-
-            Nop => Nop,
-        };
-        Statement {
-            source_info: self.source_info,
-            kind,
-        }
+EnumTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for StatementKind<'tcx> {
+        (StatementKind::Assign)(a, b),
+        (StatementKind::SetDiscriminant) { place, variant_index },
+        (StatementKind::StorageLive)(a),
+        (StatementKind::StorageDead)(a),
+        (StatementKind::InlineAsm) { asm, outputs, inputs },
+        (StatementKind::Validate)(a, b),
+        (StatementKind::EndRegion)(a),
+        (StatementKind::Nop),
     }
+}
 
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        use mir::StatementKind::*;
-
-        match self.kind {
-            Assign(ref place, ref rval) => { place.visit_with(visitor) || rval.visit_with(visitor) }
-            SetDiscriminant { ref place, .. } => place.visit_with(visitor),
-            StorageLive(ref local) |
-            StorageDead(ref local) => local.visit_with(visitor),
-            InlineAsm { ref outputs, ref inputs, .. } =>
-                outputs.visit_with(visitor) || inputs.visit_with(visitor),
-
-            // Note for future: If we want to expose the region scopes
-            // during the visit, we need to either generalize EndRegion
-            // to carry `[ty::Region]`, or extend the `TypeVisitor`
-            // trait with a `fn visit_scope`.
-            EndRegion(ref _scope) => false,
-
-            Validate(ref _op, ref places) =>
-                places.iter().any(|ty_and_place| ty_and_place.visit_with(visitor)),
-
-            Nop => false,
-        }
-    }
+EnumTypeFoldableImpl! {
+    impl<'tcx, T> TypeFoldable<'tcx> for ClearCrossCrate<T> {
+        (ClearCrossCrate::Clear),
+        (ClearCrossCrate::Set)(a),
+    } where T: TypeFoldable<'tcx>
 }
 
 impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index 7d232ac20bf..d779ccd1736 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -16,7 +16,6 @@
 use mir::*;
 use ty::subst::{Subst, Substs};
 use ty::{self, AdtDef, Ty, TyCtxt};
-use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 use hir;
 use ty::util::IntTypeExt;
 
@@ -100,25 +99,10 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for PlaceTy<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        match *self {
-            PlaceTy::Ty { ty } => PlaceTy::Ty { ty: ty.fold_with(folder) },
-            PlaceTy::Downcast { adt_def, substs, variant_index } => {
-                PlaceTy::Downcast {
-                    adt_def,
-                    substs: substs.fold_with(folder),
-                    variant_index,
-                }
-            }
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        match *self {
-            PlaceTy::Ty { ty } => ty.visit_with(visitor),
-            PlaceTy::Downcast { substs, .. } => substs.visit_with(visitor)
-        }
+EnumTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for PlaceTy<'tcx> {
+        (PlaceTy::Ty) { ty },
+        (PlaceTy::Downcast) { adt_def, substs, variant_index },
     }
 }
 
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 01e1037b622..3f52ecfc099 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -173,6 +173,15 @@ pub struct PerfStats {
     pub symbol_hash_time: Cell<Duration>,
     /// The accumulated time spent decoding def path tables from metadata
     pub decode_def_path_tables_time: Cell<Duration>,
+    /// Total number of values canonicalized queries constructed.
+    pub queries_canonicalized: Cell<usize>,
+    /// Number of times we canonicalized a value and found that the
+    /// result had already been canonicalized.
+    pub canonicalized_values_allocated: Cell<usize>,
+    /// Number of times this query is invoked.
+    pub normalize_ty_after_erasing_regions: Cell<usize>,
+    /// Number of times this query is invoked.
+    pub normalize_projection_ty: Cell<usize>,
 }
 
 /// Enum to support dispatch of one-time diagnostics (in Session.diag_once)
@@ -858,6 +867,14 @@ impl Session {
             "Total time spent decoding DefPath tables:      {}",
             duration_to_secs_str(self.perf_stats.decode_def_path_tables_time.get())
         );
+        println!("Total queries canonicalized:                   {}",
+                 self.perf_stats.queries_canonicalized.get());
+        println!("Total canonical values interned:               {}",
+                 self.perf_stats.canonicalized_values_allocated.get());
+        println!("normalize_ty_after_erasing_regions:            {}",
+                 self.perf_stats.normalize_ty_after_erasing_regions.get());
+        println!("normalize_projection_ty:                       {}",
+                 self.perf_stats.normalize_projection_ty.get());
     }
 
     /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n.
@@ -1144,6 +1161,10 @@ pub fn build_session_(
             incr_comp_bytes_hashed: Cell::new(0),
             symbol_hash_time: Cell::new(Duration::from_secs(0)),
             decode_def_path_tables_time: Cell::new(Duration::from_secs(0)),
+            queries_canonicalized: Cell::new(0),
+            canonicalized_values_allocated: Cell::new(0),
+            normalize_ty_after_erasing_regions: Cell::new(0),
+            normalize_projection_ty: Cell::new(0),
         },
         code_stats: RefCell::new(CodeStats::new()),
         optimization_fuel_crate,
diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs
index 183b1a5470e..06926b1648d 100644
--- a/src/librustc/traits/coherence.rs
+++ b/src/librustc/traits/coherence.rs
@@ -16,7 +16,7 @@
 
 use hir::def_id::{DefId, LOCAL_CRATE};
 use syntax_pos::DUMMY_SP;
-use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause, Reveal};
+use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause};
 use traits::IntercrateMode;
 use traits::select::IntercrateAmbiguityCause;
 use ty::{self, Ty, TyCtxt};
@@ -125,7 +125,7 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     // types into scope; instead, we replace the generic types with
     // fresh type variables, and hence we do our evaluations in an
     // empty environment.
-    let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
+    let param_env = ty::ParamEnv::empty();
 
     let a_impl_header = with_fresh_ty_vars(selcx, param_env, a_def_id);
     let b_impl_header = with_fresh_ty_vars(selcx, param_env, b_def_id);
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index cd2d0d7e2a0..b8455b97fa4 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -21,7 +21,6 @@ use super::{
     TraitNotObjectSafe,
     ConstEvalFailure,
     PredicateObligation,
-    Reveal,
     SelectionContext,
     SelectionError,
     ObjectSafetyViolation,
@@ -140,7 +139,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 // FIXME: I'm just not taking associated types at all here.
                 // Eventually I'll need to implement param-env-aware
                 // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic.
-                let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
+                let param_env = ty::ParamEnv::empty();
                 if let Ok(_) = self.can_sub(param_env, error, implication) {
                     debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication);
                     return true
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index e0e85600b90..a2a5aa246cf 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -63,6 +63,8 @@ mod structural_impls;
 pub mod trans;
 mod util;
 
+pub mod query;
+
 // Whether to enable bug compatibility with issue #43355
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum IntercrateMode {
@@ -695,7 +697,7 @@ fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
            predicates);
 
     let result = tcx.infer_ctxt().enter(|infcx| {
-        let param_env = ty::ParamEnv::empty(Reveal::All);
+        let param_env = ty::ParamEnv::reveal_all();
         let mut selcx = SelectionContext::new(&infcx);
         let mut fulfill_cx = FulfillmentContext::new();
         let cause = ObligationCause::dummy();
@@ -768,7 +770,10 @@ fn vtable_methods<'a, 'tcx>(
                 // the trait type may have higher-ranked lifetimes in it;
                 // so erase them if they appear, so that we get the type
                 // at some particular call site
-                let substs = tcx.erase_late_bound_regions_and_normalize(&ty::Binder(substs));
+                let substs = tcx.normalize_erasing_late_bound_regions(
+                    ty::ParamEnv::reveal_all(),
+                    &ty::Binder(substs),
+                );
 
                 // It's possible that the method relies on where clauses that
                 // do not hold for this particular set of type parameters.
diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs
new file mode 100644
index 00000000000..0fe4daa36ed
--- /dev/null
+++ b/src/librustc/traits/query/dropck_outlives.rs
@@ -0,0 +1,264 @@
+// 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.
+
+use infer::at::At;
+use infer::canonical::{Canonical, Canonicalize, QueryResult};
+use infer::InferOk;
+use std::iter::FromIterator;
+use traits::query::CanonicalTyGoal;
+use ty::{self, Ty, TyCtxt};
+use ty::subst::Kind;
+use std::rc::Rc;
+
+impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> {
+    /// Given a type `ty` of some value being dropped, computes a set
+    /// of "kinds" (types, regions) that must be outlive the execution
+    /// of the destructor. These basically correspond to data that the
+    /// destructor might access. This is used during regionck to
+    /// impose "outlives" constraints on any lifetimes referenced
+    /// within.
+    ///
+    /// The rules here are given by the "dropck" RFCs, notably [#1238]
+    /// and [#1327]. This is a fixed-point computation, where we
+    /// explore all the data that will be dropped (transitively) when
+    /// a value of type `ty` is dropped. For each type T that will be
+    /// dropped and which has a destructor, we must assume that all
+    /// the types/regions of T are live during the destructor, unless
+    /// they are marked with a special attribute (`#[may_dangle]`).
+    ///
+    /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md
+    /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md
+    pub fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec<Kind<'tcx>>> {
+        debug!(
+            "dropck_outlives(ty={:?}, param_env={:?})",
+            ty, self.param_env,
+        );
+
+        // Quick check: there are a number of cases that we know do not require
+        // any destructor.
+        let tcx = self.infcx.tcx;
+        if trivial_dropck_outlives(tcx, ty) {
+            return InferOk { value: vec![], obligations: vec![] };
+        }
+
+        let gcx = tcx.global_tcx();
+        let (c_ty, orig_values) = self.infcx.canonicalize_query(&self.param_env.and(ty));
+        let span = self.cause.span;
+        match &gcx.dropck_outlives(c_ty) {
+            Ok(result) if result.is_proven() => {
+                match self.infcx.instantiate_query_result(
+                    self.cause,
+                    self.param_env,
+                    &orig_values,
+                    result,
+                ) {
+                    Ok(InferOk {
+                        value: DropckOutlivesResult { kinds, overflows },
+                        obligations,
+                    }) => {
+                        for overflow_ty in overflows.into_iter().take(1) {
+                            let mut err = struct_span_err!(
+                                tcx.sess,
+                                span,
+                                E0320,
+                                "overflow while adding drop-check rules for {}",
+                                self.infcx.resolve_type_vars_if_possible(&ty),
+                            );
+                            err.note(&format!("overflowed on {}", overflow_ty));
+                            err.emit();
+                        }
+
+                        return InferOk {
+                            value: kinds,
+                            obligations,
+                        };
+                    }
+
+                    Err(_) => { /* fallthrough to error-handling code below */ }
+                }
+            }
+
+            _ => { /* fallthrough to error-handling code below */ }
+        }
+
+        // Errors and ambiuity in dropck occur in two cases:
+        // - unresolved inference variables at the end of typeck
+        // - non well-formed types where projections cannot be resolved
+        // Either of these should hvae created an error before.
+        tcx.sess
+            .delay_span_bug(span, "dtorck encountered internal error");
+        return InferOk {
+            value: vec![],
+            obligations: vec![],
+        };
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct DropckOutlivesResult<'tcx> {
+    pub kinds: Vec<Kind<'tcx>>,
+    pub overflows: Vec<Ty<'tcx>>,
+}
+
+/// A set of constraints that need to be satisfied in order for
+/// a type to be valid for destruction.
+#[derive(Clone, Debug)]
+pub struct DtorckConstraint<'tcx> {
+    /// Types that are required to be alive in order for this
+    /// type to be valid for destruction.
+    pub outlives: Vec<ty::subst::Kind<'tcx>>,
+
+    /// Types that could not be resolved: projections and params.
+    pub dtorck_types: Vec<Ty<'tcx>>,
+
+    /// If, during the computation of the dtorck constraint, we
+    /// overflow, that gets recorded here. The caller is expected to
+    /// report an error.
+    pub overflows: Vec<Ty<'tcx>>,
+}
+
+impl<'tcx> DtorckConstraint<'tcx> {
+    pub fn empty() -> DtorckConstraint<'tcx> {
+        DtorckConstraint {
+            outlives: vec![],
+            dtorck_types: vec![],
+            overflows: vec![],
+        }
+    }
+}
+
+impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx> {
+    fn from_iter<I: IntoIterator<Item = DtorckConstraint<'tcx>>>(iter: I) -> Self {
+        let mut result = Self::empty();
+
+        for DtorckConstraint {
+            outlives,
+            dtorck_types,
+            overflows,
+        } in iter
+        {
+            result.outlives.extend(outlives);
+            result.dtorck_types.extend(dtorck_types);
+            result.overflows.extend(overflows);
+        }
+
+        result
+    }
+}
+impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ty::ParamEnvAnd<'tcx, Ty<'tcx>> {
+    type Canonicalized = CanonicalTyGoal<'gcx>;
+
+    fn intern(
+        _gcx: TyCtxt<'_, 'gcx, 'gcx>,
+        value: Canonical<'gcx, Self::Lifted>,
+    ) -> Self::Canonicalized {
+        value
+    }
+}
+
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for DropckOutlivesResult<'tcx> {
+        kinds, overflows
+    }
+}
+
+BraceStructLiftImpl! {
+    impl<'a, 'tcx> Lift<'tcx> for DropckOutlivesResult<'a> {
+        type Lifted = DropckOutlivesResult<'tcx>;
+        kinds, overflows
+    }
+}
+
+impl_stable_hash_for!(struct DropckOutlivesResult<'tcx> {
+    kinds, overflows
+});
+
+impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for QueryResult<'tcx, DropckOutlivesResult<'tcx>> {
+    // we ought to intern this, but I'm too lazy just now
+    type Canonicalized = Rc<Canonical<'gcx, QueryResult<'gcx, DropckOutlivesResult<'gcx>>>>;
+
+    fn intern(
+        _gcx: TyCtxt<'_, 'gcx, 'gcx>,
+        value: Canonical<'gcx, Self::Lifted>,
+    ) -> Self::Canonicalized {
+        Rc::new(value)
+    }
+}
+
+impl_stable_hash_for!(struct DtorckConstraint<'tcx> {
+    outlives,
+    dtorck_types,
+    overflows
+});
+
+/// This returns true if the type `ty` is "trivial" for
+/// dropck-outlives -- that is, if it doesn't require any types to
+/// outlive. This is similar but not *quite* the same as the
+/// `needs_drop` test in the compiler already -- that is, for every
+/// type T for which this function return true, needs-drop would
+/// return false. But the reverse does not hold: in particular,
+/// `needs_drop` returns false for `PhantomData`, but it is not
+/// trivial for dropck-outlives.
+///
+/// Note also that `needs_drop` requires a "global" type (i.e., one
+/// with erased regions), but this funtcion does not.
+fn trivial_dropck_outlives<'cx, 'tcx>(tcx: TyCtxt<'cx, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
+    match ty.sty {
+        // None of these types have a destructor and hence they do not
+        // require anything in particular to outlive the dtor's
+        // execution.
+        ty::TyInfer(ty::FreshIntTy(_))
+        | ty::TyInfer(ty::FreshFloatTy(_))
+        | ty::TyBool
+        | ty::TyInt(_)
+        | ty::TyUint(_)
+        | ty::TyFloat(_)
+        | ty::TyNever
+        | ty::TyFnDef(..)
+        | ty::TyFnPtr(_)
+        | ty::TyChar
+        | ty::TyGeneratorWitness(..)
+        | ty::TyRawPtr(_)
+        | ty::TyRef(..)
+        | ty::TyStr
+        | ty::TyForeign(..)
+        | ty::TyError => true,
+
+        // [T; N] and [T] have same properties as T.
+        ty::TyArray(ty, _) | ty::TySlice(ty) => trivial_dropck_outlives(tcx, ty),
+
+        // (T1..Tn) and closures have same properties as T1..Tn --
+        // check if *any* of those are trivial.
+        ty::TyTuple(ref tys, _) => tys.iter().cloned().all(|t| trivial_dropck_outlives(tcx, t)),
+        ty::TyClosure(def_id, ref substs) => substs
+            .upvar_tys(def_id, tcx)
+            .all(|t| trivial_dropck_outlives(tcx, t)),
+
+        ty::TyAdt(def, _) => {
+            if def.is_union() {
+                // Unions never run have a dtor.
+                true
+            } else {
+                // Other types might. Moreover, PhantomData doesn't
+                // have a dtor, but it is considered to own its
+                // content, so it is non-trivial.
+                false
+            }
+        }
+
+        // The following *might* require a destructor: it would deeper inspection to tell.
+        ty::TyDynamic(..)
+        | ty::TyProjection(..)
+        | ty::TyParam(_)
+        | ty::TyAnon(..)
+        | ty::TyInfer(_)
+        | ty::TyGenerator(..) => false,
+    }
+}
diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs
new file mode 100644
index 00000000000..f1f9256f825
--- /dev/null
+++ b/src/librustc/traits/query/mod.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.
+
+//! Experimental types for the trait query interface. The methods
+//! defined in this module are all based on **canonicalization**,
+//! which makes a canonical query by replacing unbound inference
+//! variables and regions, so that results can be reused more broadly.
+//! The providers for the queries defined here can be found in
+//! `librustc_traits`.
+
+use infer::canonical::Canonical;
+use ty::{self, Ty};
+
+pub mod dropck_outlives;
+pub mod normalize;
+pub mod normalize_erasing_regions;
+
+pub type CanonicalProjectionGoal<'tcx> =
+    Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>>>;
+
+pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct NoSolution;
+
+pub type Fallible<T> = Result<T, NoSolution>;
+
+impl_stable_hash_for!(struct NoSolution { });
diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs
new file mode 100644
index 00000000000..70c5cf5f390
--- /dev/null
+++ b/src/librustc/traits/query/normalize.rs
@@ -0,0 +1,274 @@
+// 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.
+
+//! Code for the 'normalization' query. This consists of a wrapper
+//! which folds deeply, invoking the underlying
+//! `normalize_projection_ty` query when it encounters projections.
+
+use infer::{InferCtxt, InferOk};
+use infer::at::At;
+use infer::canonical::{Canonical, Canonicalize, QueryResult};
+use middle::const_val::ConstVal;
+use mir::interpret::GlobalId;
+use std::rc::Rc;
+use traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
+use traits::query::CanonicalProjectionGoal;
+use traits::project::Normalized;
+use ty::{self, Ty, TyCtxt};
+use ty::fold::{TypeFoldable, TypeFolder};
+use ty::subst::{Subst, Substs};
+
+use super::NoSolution;
+
+impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> {
+    /// Normalize `value` in the context of the inference context,
+    /// yielding a resulting type, or an error if `value` cannot be
+    /// normalized. If you don't care about regions, you should prefer
+    /// `normalize_erasing_regions`, which is more efficient.
+    ///
+    /// If the normalization succeeds and is unambigious, returns back
+    /// the normalized value along with various outlives relations (in
+    /// the form of obligations that must be discharged).
+    ///
+    /// NB. This will *eventually* be the main means of
+    /// normalizing, but for now should be used only when we actually
+    /// know that normalization will succeed, since error reporting
+    /// and other details are still "under development".
+    pub fn normalize<T>(&self, value: &T) -> Result<Normalized<'tcx, T>, NoSolution>
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        debug!(
+            "normalize::<{}>(value={:?}, param_env={:?})",
+            unsafe { ::std::intrinsics::type_name::<T>() },
+            value,
+            self.param_env,
+        );
+        let mut normalizer = QueryNormalizer {
+            infcx: self.infcx,
+            cause: self.cause,
+            param_env: self.param_env,
+            obligations: vec![],
+            error: false,
+            anon_depth: 0,
+        };
+        if !value.has_projections() {
+            return Ok(Normalized {
+                value: value.clone(),
+                obligations: vec![],
+            });
+        }
+
+        let value1 = value.fold_with(&mut normalizer);
+        if normalizer.error {
+            Err(NoSolution)
+        } else {
+            Ok(Normalized {
+                value: value1,
+                obligations: normalizer.obligations,
+            })
+        }
+    }
+}
+
+/// Result from the `normalize_projection_ty` query.
+#[derive(Clone, Debug)]
+pub struct NormalizationResult<'tcx> {
+    /// Result of normalization.
+    pub normalized_ty: Ty<'tcx>,
+}
+
+struct QueryNormalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+    cause: &'cx ObligationCause<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    obligations: Vec<PredicateObligation<'tcx>>,
+    error: bool,
+    anon_depth: usize,
+}
+
+impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx> {
+    fn tcx<'c>(&'c self) -> TyCtxt<'c, 'gcx, 'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let ty = ty.super_fold_with(self);
+        match ty.sty {
+            ty::TyAnon(def_id, substs) if !substs.has_escaping_regions() => {
+                // (*)
+                // Only normalize `impl Trait` after type-checking, usually in trans.
+                match self.param_env.reveal {
+                    Reveal::UserFacing => ty,
+
+                    Reveal::All => {
+                        let recursion_limit = self.tcx().sess.recursion_limit.get();
+                        if self.anon_depth >= recursion_limit {
+                            let obligation = Obligation::with_depth(
+                                self.cause.clone(),
+                                recursion_limit,
+                                self.param_env,
+                                ty,
+                            );
+                            self.infcx.report_overflow_error(&obligation, true);
+                        }
+
+                        let generic_ty = self.tcx().type_of(def_id);
+                        let concrete_ty = generic_ty.subst(self.tcx(), substs);
+                        self.anon_depth += 1;
+                        let folded_ty = self.fold_ty(concrete_ty);
+                        self.anon_depth -= 1;
+                        folded_ty
+                    }
+                }
+            }
+
+            ty::TyProjection(ref data) if !data.has_escaping_regions() => {
+                // (*)
+                // (*) This is kind of hacky -- we need to be able to
+                // handle normalization within binders because
+                // otherwise we wind up a need to normalize when doing
+                // trait matching (since you can have a trait
+                // obligation like `for<'a> T::B : Fn(&'a int)`), but
+                // we can't normalize with bound regions in scope. So
+                // far now we just ignore binders but only normalize
+                // if all bound regions are gone (and then we still
+                // have to renormalize whenever we instantiate a
+                // binder). It would be better to normalize in a
+                // binding-aware fashion.
+
+                let gcx = self.infcx.tcx.global_tcx();
+
+                let (c_data, orig_values) =
+                    self.infcx.canonicalize_query(&self.param_env.and(*data));
+                debug!("QueryNormalizer: c_data = {:#?}", c_data);
+                debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
+                match gcx.normalize_projection_ty(c_data) {
+                    Ok(result) => {
+                        // We don't expect ambiguity.
+                        if result.is_ambiguous() {
+                            self.error = true;
+                            return ty;
+                        }
+
+                        match self.infcx.instantiate_query_result(
+                            self.cause,
+                            self.param_env,
+                            &orig_values,
+                            &result,
+                        ) {
+                            Ok(InferOk {
+                                value: result,
+                                obligations,
+                            }) => {
+                                debug!("QueryNormalizer: result = {:#?}", result);
+                                debug!("QueryNormalizer: obligations = {:#?}", obligations);
+                                self.obligations.extend(obligations);
+                                return result.normalized_ty;
+                            }
+
+                            Err(_) => {
+                                self.error = true;
+                                return ty;
+                            }
+                        }
+                    }
+
+                    Err(NoSolution) => {
+                        self.error = true;
+                        ty
+                    }
+                }
+            }
+
+            _ => ty,
+        }
+    }
+
+    fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+        if let ConstVal::Unevaluated(def_id, substs) = constant.val {
+            let tcx = self.infcx.tcx.global_tcx();
+            if let Some(param_env) = self.tcx().lift_to_global(&self.param_env) {
+                if substs.needs_infer() {
+                    let identity_substs = Substs::identity_for_item(tcx, def_id);
+                    let instance = ty::Instance::resolve(tcx, param_env, def_id, identity_substs);
+                    if let Some(instance) = instance {
+                        let cid = GlobalId {
+                            instance,
+                            promoted: None,
+                        };
+                        match tcx.const_eval(param_env.and(cid)) {
+                            Ok(evaluated) => {
+                                let evaluated = evaluated.subst(self.tcx(), substs);
+                                return self.fold_const(evaluated);
+                            }
+                            Err(_) => {}
+                        }
+                    }
+                } else {
+                    if let Some(substs) = self.tcx().lift_to_global(&substs) {
+                        let instance = ty::Instance::resolve(tcx, param_env, def_id, substs);
+                        if let Some(instance) = instance {
+                            let cid = GlobalId {
+                                instance,
+                                promoted: None,
+                            };
+                            match tcx.const_eval(param_env.and(cid)) {
+                                Ok(evaluated) => return self.fold_const(evaluated),
+                                Err(_) => {}
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        constant
+    }
+}
+
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for NormalizationResult<'tcx> {
+        normalized_ty
+    }
+}
+
+BraceStructLiftImpl! {
+    impl<'a, 'tcx> Lift<'tcx> for NormalizationResult<'a> {
+        type Lifted = NormalizationResult<'tcx>;
+        normalized_ty
+    }
+}
+
+impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>> {
+    type Canonicalized = CanonicalProjectionGoal<'gcx>;
+
+    fn intern(
+        _gcx: TyCtxt<'_, 'gcx, 'gcx>,
+        value: Canonical<'gcx, Self::Lifted>,
+    ) -> Self::Canonicalized {
+        value
+    }
+}
+
+impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for QueryResult<'tcx, NormalizationResult<'tcx>> {
+    // we ought to intern this, but I'm too lazy just now
+    type Canonicalized = Rc<Canonical<'gcx, QueryResult<'gcx, NormalizationResult<'gcx>>>>;
+
+    fn intern(
+        _gcx: TyCtxt<'_, 'gcx, 'gcx>,
+        value: Canonical<'gcx, Self::Lifted>,
+    ) -> Self::Canonicalized {
+        Rc::new(value)
+    }
+}
+
+impl_stable_hash_for!(struct NormalizationResult<'tcx> {
+    normalized_ty
+});
diff --git a/src/librustc/traits/query/normalize_erasing_regions.rs b/src/librustc/traits/query/normalize_erasing_regions.rs
new file mode 100644
index 00000000000..a9734e9c298
--- /dev/null
+++ b/src/librustc/traits/query/normalize_erasing_regions.rs
@@ -0,0 +1,88 @@
+// 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.
+
+//! Methods for normalizing when you don't care about regions (and
+//! aren't doing type inference). If either of those things don't
+//! apply to you, use `infcx.normalize(...)`.
+//!
+//! The methods in this file use a `TypeFolder` to recursively process
+//! contents, invoking the underlying
+//! `normalize_ty_after_erasing_regions` query for each type found
+//! within. (This underlying query is what is cached.)
+
+use ty::{self, Ty, TyCtxt};
+use ty::fold::{TypeFoldable, TypeFolder};
+
+impl<'cx, 'tcx> TyCtxt<'cx, 'tcx, 'tcx> {
+    /// Erase the regions in `value` and then fully normalize all the
+    /// types found within. The result will also have regions erased.
+    ///
+    /// This is appropriate to use only after type-check: it assumes
+    /// that normalization will succeed, for example.
+    pub fn normalize_erasing_regions<T>(self, param_env: ty::ParamEnv<'tcx>, value: T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        debug!(
+            "normalize_erasing_regions::<{}>(value={:?}, param_env={:?})",
+            unsafe { ::std::intrinsics::type_name::<T>() },
+            value,
+            param_env,
+        );
+
+        // Erase first before we do the real query -- this keeps the
+        // cache from being too polluted.
+        let value = self.erase_regions(&value);
+        if !value.has_projections() {
+            value
+        } else {
+            value.fold_with(&mut NormalizeAfterErasingRegionsFolder {
+                tcx: self,
+                param_env: param_env,
+            })
+        }
+    }
+
+    /// If you have a `Binder<T>`, you can do this to strip out the
+    /// late-bound regions and then normalize the result, yielding up
+    /// a `T` (with regions erased). This is appropriate when the
+    /// binder is being instantiated at the call site.
+    ///
+    /// NB. Currently, higher-ranked type bounds inhibit
+    /// normalization. Therefore, each time we erase them in
+    /// translation, we need to normalize the contents.
+    pub fn normalize_erasing_late_bound_regions<T>(
+        self,
+        param_env: ty::ParamEnv<'tcx>,
+        value: &ty::Binder<T>,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        assert!(!value.needs_subst());
+        let value = self.erase_late_bound_regions(value);
+        self.normalize_erasing_regions(param_env, value)
+    }
+}
+
+struct NormalizeAfterErasingRegionsFolder<'cx, 'tcx: 'cx> {
+    tcx: TyCtxt<'cx, 'tcx, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'cx, 'tcx> TypeFolder<'tcx, 'tcx> for NormalizeAfterErasingRegionsFolder<'cx, 'tcx> {
+    fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> {
+        self.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        self.tcx.normalize_ty_after_erasing_regions(self.param_env.and(ty))
+    }
+}
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 91d86394b01..f2f54dcedfd 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -777,7 +777,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             // value in order to work, so we can clear out the param env and get better
             // caching. (If the current param env is inconsistent, we don't care what happens).
             debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation);
-            obligation.param_env = ty::ParamEnv::empty(obligation.param_env.reveal);
+            obligation.param_env = obligation.param_env.without_caller_bounds();
         }
 
         let stack = self.push_stack(previous_stack, &obligation);
@@ -2083,9 +2083,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             ty::TyProjection(_) | ty::TyParam(_) | ty::TyAnon(..) => None,
             ty::TyInfer(ty::TyVar(_)) => Ambiguous,
 
-            ty::TyInfer(ty::FreshTy(_))
-            | ty::TyInfer(ty::FreshIntTy(_))
-            | ty::TyInfer(ty::FreshFloatTy(_)) => {
+            ty::TyInfer(ty::CanonicalTy(_)) |
+            ty::TyInfer(ty::FreshTy(_)) |
+            ty::TyInfer(ty::FreshIntTy(_)) |
+            ty::TyInfer(ty::FreshFloatTy(_)) => {
                 bug!("asked to assemble builtin bounds of unexpected type: {:?}",
                      self_ty);
             }
@@ -2154,9 +2155,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 Ambiguous
             }
 
-            ty::TyInfer(ty::FreshTy(_))
-            | ty::TyInfer(ty::FreshIntTy(_))
-            | ty::TyInfer(ty::FreshFloatTy(_)) => {
+            ty::TyInfer(ty::CanonicalTy(_)) |
+            ty::TyInfer(ty::FreshTy(_)) |
+            ty::TyInfer(ty::FreshIntTy(_)) |
+            ty::TyInfer(ty::FreshFloatTy(_)) => {
                 bug!("asked to assemble builtin bounds of unexpected type: {:?}",
                      self_ty);
             }
@@ -2195,6 +2197,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             ty::TyParam(..) |
             ty::TyForeign(..) |
             ty::TyProjection(..) |
+            ty::TyInfer(ty::CanonicalTy(_)) |
             ty::TyInfer(ty::TyVar(_)) |
             ty::TyInfer(ty::FreshTy(_)) |
             ty::TyInfer(ty::FreshIntTy(_)) |
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index d11565618a6..5ea089abb8e 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -26,7 +26,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use hir::def_id::DefId;
 use infer::{InferCtxt, InferOk};
 use ty::subst::{Subst, Substs};
-use traits::{self, Reveal, ObligationCause};
+use traits::{self, ObligationCause};
 use traits::select::IntercrateAmbiguityCause;
 use ty::{self, TyCtxt, TypeFoldable};
 use syntax_pos::DUMMY_SP;
@@ -132,7 +132,7 @@ pub fn find_associated_item<'a, 'tcx>(
     match ancestors.defs(tcx, item.name, item.kind, trait_def_id).next() {
         Some(node_item) => {
             let substs = tcx.infer_ctxt().enter(|infcx| {
-                let param_env = ty::ParamEnv::empty(Reveal::All);
+                let param_env = ty::ParamEnv::reveal_all();
                 let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs);
                 let substs = translate_substs(&infcx, param_env, impl_data.impl_def_id,
                                               substs, node_item.node);
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index 9dd5aaee7b7..a2d98a456f4 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -363,258 +363,65 @@ impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx
     }
 }
 
-impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableImplData<'tcx, N> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        traits::VtableImplData {
-            impl_def_id: self.impl_def_id,
-            substs: self.substs.fold_with(folder),
-            nested: self.nested.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.substs.visit_with(visitor) || self.nested.visit_with(visitor)
-    }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableImplData<'tcx, N> {
+        impl_def_id, substs, nested
+    } where N: TypeFoldable<'tcx>
 }
 
-impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableGeneratorData<'tcx, N> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        traits::VtableGeneratorData {
-            closure_def_id: self.closure_def_id,
-            substs: self.substs.fold_with(folder),
-            nested: self.nested.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.substs.visit_with(visitor) || self.nested.visit_with(visitor)
-    }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableGeneratorData<'tcx, N> {
+        closure_def_id, substs, nested
+    } where N: TypeFoldable<'tcx>
 }
 
-impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableClosureData<'tcx, N> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        traits::VtableClosureData {
-            closure_def_id: self.closure_def_id,
-            substs: self.substs.fold_with(folder),
-            nested: self.nested.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.substs.visit_with(visitor) || self.nested.visit_with(visitor)
-    }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableClosureData<'tcx, N> {
+        closure_def_id, substs, nested
+    } where N: TypeFoldable<'tcx>
 }
 
-impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableAutoImplData<N> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        traits::VtableAutoImplData {
-            trait_def_id: self.trait_def_id,
-            nested: self.nested.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.nested.visit_with(visitor)
-    }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableAutoImplData<N> {
+        trait_def_id, nested
+    } where N: TypeFoldable<'tcx>
 }
 
-impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableBuiltinData<N> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        traits::VtableBuiltinData {
-            nested: self.nested.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.nested.visit_with(visitor)
-    }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableBuiltinData<N> {
+        nested
+    } where N: TypeFoldable<'tcx>
 }
 
-impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx, N> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        traits::VtableObjectData {
-            upcast_trait_ref: self.upcast_trait_ref.fold_with(folder),
-            vtable_base: self.vtable_base,
-            nested: self.nested.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.upcast_trait_ref.visit_with(visitor) || self.nested.visit_with(visitor)
-    }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx, N> {
+        upcast_trait_ref, vtable_base, nested
+    } where N: TypeFoldable<'tcx>
 }
 
-impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableFnPointerData<'tcx, N> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        traits::VtableFnPointerData {
-            fn_ty: self.fn_ty.fold_with(folder),
-            nested: self.nested.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.fn_ty.visit_with(visitor) || self.nested.visit_with(visitor)
-    }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableFnPointerData<'tcx, N> {
+        fn_ty,
+        nested
+    } where N: TypeFoldable<'tcx>
 }
 
-impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        match *self {
-            traits::VtableImpl(ref v) => traits::VtableImpl(v.fold_with(folder)),
-            traits::VtableAutoImpl(ref t) => traits::VtableAutoImpl(t.fold_with(folder)),
-            traits::VtableGenerator(ref d) => {
-                traits::VtableGenerator(d.fold_with(folder))
-            }
-            traits::VtableClosure(ref d) => {
-                traits::VtableClosure(d.fold_with(folder))
-            }
-            traits::VtableFnPointer(ref d) => {
-                traits::VtableFnPointer(d.fold_with(folder))
-            }
-            traits::VtableParam(ref n) => traits::VtableParam(n.fold_with(folder)),
-            traits::VtableBuiltin(ref d) => traits::VtableBuiltin(d.fold_with(folder)),
-            traits::VtableObject(ref d) => traits::VtableObject(d.fold_with(folder)),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        match *self {
-            traits::VtableImpl(ref v) => v.visit_with(visitor),
-            traits::VtableAutoImpl(ref t) => t.visit_with(visitor),
-            traits::VtableGenerator(ref d) => d.visit_with(visitor),
-            traits::VtableClosure(ref d) => d.visit_with(visitor),
-            traits::VtableFnPointer(ref d) => d.visit_with(visitor),
-            traits::VtableParam(ref n) => n.visit_with(visitor),
-            traits::VtableBuiltin(ref d) => d.visit_with(visitor),
-            traits::VtableObject(ref d) => d.visit_with(visitor),
-        }
-    }
-}
-
-impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Normalized<'tcx, T> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        Normalized {
-            value: self.value.fold_with(folder),
-            obligations: self.obligations.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.value.visit_with(visitor) || self.obligations.visit_with(visitor)
-    }
-}
-
-impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        match *self {
-            super::ExprAssignable |
-            super::MatchExpressionArm { arm_span: _, source: _ } |
-            super::IfExpression |
-            super::IfExpressionWithNoElse |
-            super::MainFunctionType |
-            super::StartFunctionType |
-            super::IntrinsicType |
-            super::MethodReceiver |
-            super::MiscObligation |
-            super::SliceOrArrayElem |
-            super::TupleElem |
-            super::ItemObligation(_) |
-            super::AssignmentLhsSized |
-            super::TupleInitializerSized |
-            super::StructInitializerSized |
-            super::VariableType(_) |
-            super::ReturnType(_) |
-            super::SizedReturnType |
-            super::SizedYieldType |
-            super::ReturnNoExpression |
-            super::RepeatVec |
-            super::FieldSized(_) |
-            super::ConstSized |
-            super::SharedStatic |
-            super::BlockTailExpression(_) |
-            super::CompareImplMethodObligation { .. } => self.clone(),
-
-            super::ProjectionWf(proj) => super::ProjectionWf(proj.fold_with(folder)),
-            super::ReferenceOutlivesReferent(ty) => {
-                super::ReferenceOutlivesReferent(ty.fold_with(folder))
-            }
-            super::ObjectTypeBound(ty, r) => {
-                super::ObjectTypeBound(ty.fold_with(folder), r.fold_with(folder))
-            }
-            super::ObjectCastObligation(ty) => {
-                super::ObjectCastObligation(ty.fold_with(folder))
-            }
-            super::BuiltinDerivedObligation(ref cause) => {
-                super::BuiltinDerivedObligation(cause.fold_with(folder))
-            }
-            super::ImplDerivedObligation(ref cause) => {
-                super::ImplDerivedObligation(cause.fold_with(folder))
-            }
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        match *self {
-            super::ExprAssignable |
-            super::MatchExpressionArm { arm_span: _, source: _ } |
-            super::IfExpression |
-            super::IfExpressionWithNoElse |
-            super::MainFunctionType |
-            super::StartFunctionType |
-            super::IntrinsicType |
-            super::MethodReceiver |
-            super::MiscObligation |
-            super::SliceOrArrayElem |
-            super::TupleElem |
-            super::ItemObligation(_) |
-            super::AssignmentLhsSized |
-            super::TupleInitializerSized |
-            super::StructInitializerSized |
-            super::VariableType(_) |
-            super::ReturnType(_) |
-            super::SizedReturnType |
-            super::SizedYieldType |
-            super::ReturnNoExpression |
-            super::RepeatVec |
-            super::FieldSized(_) |
-            super::ConstSized |
-            super::SharedStatic |
-            super::BlockTailExpression(_) |
-            super::CompareImplMethodObligation { .. } => false,
-
-            super::ProjectionWf(proj) => proj.visit_with(visitor),
-            super::ReferenceOutlivesReferent(ty) => ty.visit_with(visitor),
-            super::ObjectTypeBound(ty, r) => ty.visit_with(visitor) || r.visit_with(visitor),
-            super::ObjectCastObligation(ty) => ty.visit_with(visitor),
-            super::BuiltinDerivedObligation(ref cause) => cause.visit_with(visitor),
-            super::ImplDerivedObligation(ref cause) => cause.visit_with(visitor)
-        }
-    }
-}
-
-impl<'tcx> TypeFoldable<'tcx> for traits::DerivedObligationCause<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        traits::DerivedObligationCause {
-            parent_trait_ref: self.parent_trait_ref.fold_with(folder),
-            parent_code: self.parent_code.fold_with(folder)
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.parent_trait_ref.visit_with(visitor) || self.parent_code.visit_with(visitor)
-    }
+EnumTypeFoldableImpl! {
+    impl<'tcx, N> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> {
+        (traits::VtableImpl)(a),
+        (traits::VtableAutoImpl)(a),
+        (traits::VtableGenerator)(a),
+        (traits::VtableClosure)(a),
+        (traits::VtableFnPointer)(a),
+        (traits::VtableParam)(a),
+        (traits::VtableBuiltin)(a),
+        (traits::VtableObject)(a),
+    } where N: TypeFoldable<'tcx>
 }
 
-impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCause<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        traits::ObligationCause {
-            span: self.span,
-            body_id: self.body_id,
-            code: self.code.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.code.visit_with(visitor)
-    }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, T> TypeFoldable<'tcx> for Normalized<'tcx, T> {
+        value,
+        obligations
+    } where T: TypeFoldable<'tcx>
 }
diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs
index c873580e3ad..cc8b74e0ee2 100644
--- a/src/librustc/traits/trans/mod.rs
+++ b/src/librustc/traits/trans/mod.rs
@@ -14,14 +14,14 @@
 // general routines.
 
 use dep_graph::{DepKind, DepTrackingMapConfig};
-use infer::TransNormalize;
 use std::marker::PhantomData;
 use syntax_pos::DUMMY_SP;
-use hir::def_id::DefId;
+use infer::InferCtxt;
+use syntax_pos::Span;
 use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, Vtable};
 use ty::{self, Ty, TyCtxt};
 use ty::subst::{Subst, Substs};
-use ty::fold::{TypeFoldable, TypeFolder};
+use ty::fold::TypeFoldable;
 
 /// Attempts to resolve an obligation to a vtable.. The result is
 /// a shallow vtable resolution -- meaning that we do not
@@ -87,111 +87,29 @@ pub fn trans_fulfill_obligation<'a, 'tcx>(ty: TyCtxt<'a, 'tcx, 'tcx>,
 }
 
 impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
-    /// Monomorphizes a type from the AST by first applying the in-scope
-    /// substitutions and then normalizing any associated types.
-    pub fn trans_apply_param_substs<T>(self,
-                                       param_substs: &Substs<'tcx>,
-                                       value: &T)
-                                       -> T
-        where T: TransNormalize<'tcx>
-    {
-        debug!("apply_param_substs(param_substs={:?}, value={:?})", param_substs, value);
-        let substituted = value.subst(self, param_substs);
-        let substituted = self.erase_regions(&substituted);
-        AssociatedTypeNormalizer::new(self).fold(&substituted)
-    }
-
-    pub fn trans_apply_param_substs_env<T>(
+    /// Monomorphizes a type from the AST by first applying the
+    /// in-scope substitutions and then normalizing any associated
+    /// types.
+    pub fn subst_and_normalize_erasing_regions<T>(
         self,
         param_substs: &Substs<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
-        value: &T,
+        value: &T
     ) -> T
     where
-        T: TransNormalize<'tcx>,
+        T: TypeFoldable<'tcx>,
     {
         debug!(
-            "apply_param_substs_env(param_substs={:?}, value={:?}, param_env={:?})",
+            "subst_and_normalize_erasing_regions(\
+             param_substs={:?}, \
+             value={:?}, \
+             param_env={:?})",
             param_substs,
             value,
             param_env,
         );
         let substituted = value.subst(self, param_substs);
-        let substituted = self.erase_regions(&substituted);
-        AssociatedTypeNormalizerEnv::new(self, param_env).fold(&substituted)
-    }
-
-    pub fn trans_impl_self_ty(&self, def_id: DefId, substs: &'tcx Substs<'tcx>)
-                              -> Ty<'tcx>
-    {
-        self.trans_apply_param_substs(substs, &self.type_of(def_id))
-    }
-}
-
-struct AssociatedTypeNormalizer<'a, 'gcx: 'a> {
-    tcx: TyCtxt<'a, 'gcx, 'gcx>,
-}
-
-impl<'a, 'gcx> AssociatedTypeNormalizer<'a, 'gcx> {
-    fn new(tcx: TyCtxt<'a, 'gcx, 'gcx>) -> Self {
-        AssociatedTypeNormalizer { tcx }
-    }
-
-    fn fold<T:TypeFoldable<'gcx>>(&mut self, value: &T) -> T {
-        if !value.has_projections() {
-            value.clone()
-        } else {
-            value.fold_with(self)
-        }
-    }
-}
-
-impl<'a, 'gcx> TypeFolder<'gcx, 'gcx> for AssociatedTypeNormalizer<'a, 'gcx> {
-    fn tcx<'c>(&'c self) -> TyCtxt<'c, 'gcx, 'gcx> {
-        self.tcx
-    }
-
-    fn fold_ty(&mut self, ty: Ty<'gcx>) -> Ty<'gcx> {
-        if !ty.has_projections() {
-            ty
-        } else {
-            debug!("AssociatedTypeNormalizer: ty={:?}", ty);
-            self.tcx.fully_normalize_monormophic_ty(ty)
-        }
-    }
-}
-
-struct AssociatedTypeNormalizerEnv<'a, 'gcx: 'a> {
-    tcx: TyCtxt<'a, 'gcx, 'gcx>,
-    param_env: ty::ParamEnv<'gcx>,
-}
-
-impl<'a, 'gcx> AssociatedTypeNormalizerEnv<'a, 'gcx> {
-    fn new(tcx: TyCtxt<'a, 'gcx, 'gcx>, param_env: ty::ParamEnv<'gcx>) -> Self {
-        Self { tcx, param_env }
-    }
-
-    fn fold<T: TypeFoldable<'gcx>>(&mut self, value: &T) -> T {
-        if !value.has_projections() {
-            value.clone()
-        } else {
-            value.fold_with(self)
-        }
-    }
-}
-
-impl<'a, 'gcx> TypeFolder<'gcx, 'gcx> for AssociatedTypeNormalizerEnv<'a, 'gcx> {
-    fn tcx<'c>(&'c self) -> TyCtxt<'c, 'gcx, 'gcx> {
-        self.tcx
-    }
-
-    fn fold_ty(&mut self, ty: Ty<'gcx>) -> Ty<'gcx> {
-        if !ty.has_projections() {
-            ty
-        } else {
-            debug!("AssociatedTypeNormalizerEnv: ty={:?}", ty);
-            self.tcx.normalize_associated_type_in_env(&ty, self.param_env)
-        }
+        self.normalize_erasing_regions(param_env, substituted)
     }
 }
 
@@ -221,3 +139,45 @@ impl<'gcx> DepTrackingMapConfig for ProjectionCache<'gcx> {
         DepKind::TraitSelect
     }
 }
+
+impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
+    /// Finishes processes any obligations that remain in the
+    /// fulfillment context, and then returns the result with all type
+    /// variables removed and regions erased. Because this is intended
+    /// for use after type-check has completed, if any errors occur,
+    /// it will panic. It is used during normalization and other cases
+    /// where processing the obligations in `fulfill_cx` may cause
+    /// type inference variables that appear in `result` to be
+    /// unified, and hence we need to process those obligations to get
+    /// the complete picture of the type.
+    fn drain_fulfillment_cx_or_panic<T>(&self,
+                                        span: Span,
+                                        fulfill_cx: &mut FulfillmentContext<'tcx>,
+                                        result: &T)
+                                        -> T::Lifted
+        where T: TypeFoldable<'tcx> + ty::Lift<'gcx>
+    {
+        debug!("drain_fulfillment_cx_or_panic()");
+
+        // In principle, we only need to do this so long as `result`
+        // contains unbound type parameters. It could be a slight
+        // optimization to stop iterating early.
+        match fulfill_cx.select_all_or_error(self) {
+            Ok(()) => { }
+            Err(errors) => {
+                span_bug!(span, "Encountered errors `{:?}` resolving bounds after type-checking",
+                          errors);
+            }
+        }
+
+        let result = self.resolve_type_vars_if_possible(result);
+        let result = self.tcx.erase_regions(&result);
+
+        match self.tcx.lift_to_global(&result) {
+            Some(result) => result,
+            None => {
+                span_bug!(span, "Uninferred types/regions in `{:?}`", result);
+            }
+        }
+    }
+}
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index b760649c37d..9a687028b58 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -23,6 +23,7 @@ use hir::map as hir_map;
 use hir::map::DefPathHash;
 use lint::{self, Lint};
 use ich::{StableHashingContext, NodeIdHashingMode};
+use infer::canonical::{CanonicalVarInfo, CanonicalVarInfos};
 use infer::outlives::free_region_map::FreeRegionMap;
 use middle::const_val::ConstVal;
 use middle::cstore::{CrateStore, LinkMeta};
@@ -131,6 +132,7 @@ pub struct CtxtInterners<'tcx> {
     type_: RefCell<FxHashSet<Interned<'tcx, TyS<'tcx>>>>,
     type_list: RefCell<FxHashSet<Interned<'tcx, Slice<Ty<'tcx>>>>>,
     substs: RefCell<FxHashSet<Interned<'tcx, Substs<'tcx>>>>,
+    canonical_var_infos: RefCell<FxHashSet<Interned<'tcx, Slice<CanonicalVarInfo>>>>,
     region: RefCell<FxHashSet<Interned<'tcx, RegionKind>>>,
     existential_predicates: RefCell<FxHashSet<Interned<'tcx, Slice<ExistentialPredicate<'tcx>>>>>,
     predicates: RefCell<FxHashSet<Interned<'tcx, Slice<Predicate<'tcx>>>>>,
@@ -146,6 +148,7 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> {
             substs: RefCell::new(FxHashSet()),
             region: RefCell::new(FxHashSet()),
             existential_predicates: RefCell::new(FxHashSet()),
+            canonical_var_infos: RefCell::new(FxHashSet()),
             predicates: RefCell::new(FxHashSet()),
             const_: RefCell::new(FxHashSet()),
         }
@@ -1508,7 +1511,7 @@ impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> {
 /// pointer differs. The latter case is possible if a primitive type,
 /// e.g. `()` or `u8`, was interned in a different context.
 pub trait Lift<'tcx> {
-    type Lifted;
+    type Lifted: 'tcx;
     fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self::Lifted>;
 }
 
@@ -1838,6 +1841,12 @@ impl<'tcx: 'lcx, 'lcx> Borrow<[Ty<'lcx>]> for Interned<'tcx, Slice<Ty<'tcx>>> {
     }
 }
 
+impl<'tcx: 'lcx, 'lcx> Borrow<[CanonicalVarInfo]> for Interned<'tcx, Slice<CanonicalVarInfo>> {
+    fn borrow<'a>(&'a self) -> &'a [CanonicalVarInfo] {
+        &self.0[..]
+    }
+}
+
 impl<'tcx: 'lcx, 'lcx> Borrow<[Kind<'lcx>]> for Interned<'tcx, Substs<'tcx>> {
     fn borrow<'a>(&'a self) -> &'a [Kind<'lcx>] {
         &self.0[..]
@@ -1970,6 +1979,22 @@ slice_interners!(
     substs: _intern_substs(Kind)
 );
 
+// This isn't a perfect fit: CanonicalVarInfo slices are always
+// allocated in the global arena, so this `intern_method!` macro is
+// overly general.  But we just return false for the code that checks
+// whether they belong in the thread-local arena, so no harm done, and
+// seems better than open-coding the rest.
+intern_method! {
+    'tcx,
+    canonical_var_infos: _intern_canonical_var_infos(
+        &[CanonicalVarInfo],
+        alloc_slice,
+        Deref::deref,
+        |xs: &[CanonicalVarInfo]| -> &Slice<CanonicalVarInfo> { unsafe { mem::transmute(xs) } },
+        |_xs: &[CanonicalVarInfo]| -> bool { false }
+    ) -> Slice<CanonicalVarInfo>
+}
+
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     /// Given a `fn` type, returns an equivalent `unsafe fn` type;
     /// that is, a `fn` type that is equivalent in every way for being
@@ -2257,6 +2282,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    pub fn intern_canonical_var_infos(self, ts: &[CanonicalVarInfo]) -> CanonicalVarInfos<'gcx> {
+        if ts.len() == 0 {
+            Slice::empty()
+        } else {
+            self.global_tcx()._intern_canonical_var_infos(ts)
+        }
+    }
+
     pub fn mk_fn_sig<I>(self,
                         inputs: I,
                         output: I::Item,
@@ -2504,9 +2537,6 @@ pub fn provide(providers: &mut ty::maps::Providers) {
         assert_eq!(cnum, LOCAL_CRATE);
         tcx.features().clone_closures
     };
-    providers.fully_normalize_monormophic_ty = |tcx, ty| {
-        tcx.fully_normalize_associated_types_in(&ty)
-    };
     providers.features_query = |tcx, cnum| {
         assert_eq!(cnum, LOCAL_CRATE);
         Lrc::new(tcx.sess.features_untracked().clone())
diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs
index dcb70a8f86a..8a0253ed2f1 100644
--- a/src/librustc/ty/error.rs
+++ b/src/librustc/ty/error.rs
@@ -220,6 +220,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> {
             ty::TyInfer(ty::TyVar(_)) => "inferred type".to_string(),
             ty::TyInfer(ty::IntVar(_)) => "integral variable".to_string(),
             ty::TyInfer(ty::FloatVar(_)) => "floating-point variable".to_string(),
+            ty::TyInfer(ty::CanonicalTy(_)) |
             ty::TyInfer(ty::FreshTy(_)) => "skolemized type".to_string(),
             ty::TyInfer(ty::FreshIntTy(_)) => "skolemized integral type".to_string(),
             ty::TyInfer(ty::FreshFloatTy(_)) => "skolemized floating-point type".to_string(),
diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs
index f067789771c..cae64fd4c95 100644
--- a/src/librustc/ty/flags.rs
+++ b/src/librustc/ty/flags.rs
@@ -112,8 +112,16 @@ impl FlagComputation {
                 match infer {
                     ty::FreshTy(_) |
                     ty::FreshIntTy(_) |
-                    ty::FreshFloatTy(_) => {}
-                    _ => self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX)
+                    ty::FreshFloatTy(_) |
+                    ty::CanonicalTy(_) => {
+                        self.add_flags(TypeFlags::HAS_CANONICAL_VARS);
+                    }
+
+                    ty::TyVar(_) |
+                    ty::IntVar(_) |
+                    ty::FloatVar(_) => {
+                        self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX)
+                    }
                 }
             }
 
diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs
index 4cc7406af72..8071cd0c639 100644
--- a/src/librustc/ty/fold.rs
+++ b/src/librustc/ty/fold.rs
@@ -49,6 +49,9 @@ use util::nodemap::FxHashSet;
 
 /// The TypeFoldable trait is implemented for every type that can be folded.
 /// Basically, every type that has a corresponding method in TypeFolder.
+///
+/// To implement this conveniently, use the
+/// `BraceStructTypeFoldableImpl` etc macros found in `macros.rs`.
 pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self;
     fn fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs
index 614158bafa6..76f7a0b59a2 100644
--- a/src/librustc/ty/instance.rs
+++ b/src/librustc/ty/instance.rs
@@ -51,7 +51,11 @@ impl<'a, 'tcx> Instance<'tcx> {
               -> Ty<'tcx>
     {
         let ty = tcx.type_of(self.def.def_id());
-        tcx.trans_apply_param_substs(self.substs, &ty)
+        tcx.subst_and_normalize_erasing_regions(
+            self.substs,
+            ty::ParamEnv::reveal_all(),
+            &ty,
+        )
     }
 }
 
@@ -184,7 +188,11 @@ impl<'a, 'b, 'tcx> Instance<'tcx> {
             resolve_associated_item(tcx, &item, param_env, trait_def_id, substs)
         } else {
             let ty = tcx.type_of(def_id);
-            let item_type = tcx.trans_apply_param_substs_env(substs, param_env, &ty);
+            let item_type = tcx.subst_and_normalize_erasing_regions(
+                substs,
+                param_env,
+                &ty,
+            );
 
             let def = match item_type.sty {
                 ty::TyFnDef(..) if {
@@ -199,7 +207,7 @@ impl<'a, 'b, 'tcx> Instance<'tcx> {
                 _ => {
                     if Some(def_id) == tcx.lang_items().drop_in_place_fn() {
                         let ty = substs.type_at(0);
-                        if ty.needs_drop(tcx, ty::ParamEnv::empty(traits::Reveal::All)) {
+                        if ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) {
                             debug!(" => nontrivial drop glue");
                             ty::InstanceDef::DropGlue(def_id, Some(ty))
                         } else {
@@ -352,7 +360,7 @@ fn fn_once_adapter_instance<'a, 'tcx>(
         closure_did, substs);
 
     let sig = substs.closure_sig(closure_did, tcx);
-    let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
+    let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
     assert_eq!(sig.inputs().len(), 1);
     let substs = tcx.mk_substs([Kind::from(self_ty), sig.inputs()[0].into()].iter().cloned());
 
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 1b919ad68d0..04353d2ece1 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -1213,7 +1213,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                     data_ptr.valid_range.start = 1;
                 }
 
-                let pointee = tcx.normalize_associated_type_in_env(&pointee, param_env);
+                let pointee = tcx.normalize_erasing_regions(param_env, pointee);
                 if pointee.is_sized(tcx.at(DUMMY_SP), param_env) {
                     return Ok(tcx.intern_layout(LayoutDetails::scalar(self, data_ptr)));
                 }
@@ -1241,7 +1241,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
             // Arrays and slices.
             ty::TyArray(element, mut count) => {
                 if count.has_projections() {
-                    count = tcx.normalize_associated_type_in_env(&count, param_env);
+                    count = tcx.normalize_erasing_regions(param_env, count);
                     if count.has_projections() {
                         return Err(LayoutError::Unknown(ty));
                     }
@@ -1686,7 +1686,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
 
             // Types with no meaningful known layout.
             ty::TyProjection(_) | ty::TyAnon(..) => {
-                let normalized = tcx.normalize_associated_type_in_env(&ty, param_env);
+                let normalized = tcx.normalize_erasing_regions(param_env, ty);
                 if ty == normalized {
                     return Err(LayoutError::Unknown(ty));
                 }
@@ -1953,7 +1953,7 @@ impl<'a, 'tcx> SizeSkeleton<'tcx> {
             }
 
             ty::TyProjection(_) | ty::TyAnon(..) => {
-                let normalized = tcx.normalize_associated_type_in_env(&ty, param_env);
+                let normalized = tcx.normalize_erasing_regions(param_env, ty);
                 if ty == normalized {
                     Err(err)
                 } else {
@@ -2058,8 +2058,8 @@ impl<'a, 'tcx> LayoutOf<Ty<'tcx>> for LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
     /// Computes the layout of a type. Note that this implicitly
     /// executes in "reveal all" mode.
     fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
-        let param_env = self.param_env.reveal_all();
-        let ty = self.tcx.normalize_associated_type_in_env(&ty, param_env);
+        let param_env = self.param_env.with_reveal_all();
+        let ty = self.tcx.normalize_erasing_regions(param_env, ty);
         let details = self.tcx.layout_raw(param_env.and(ty))?;
         let layout = TyLayout {
             ty,
@@ -2084,9 +2084,9 @@ impl<'a, 'tcx> LayoutOf<Ty<'tcx>> for LayoutCx<'tcx, ty::maps::TyCtxtAt<'a, 'tcx
     /// Computes the layout of a type. Note that this implicitly
     /// executes in "reveal all" mode.
     fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
-        let param_env = self.param_env.reveal_all();
-        let ty = self.tcx.normalize_associated_type_in_env(&ty, param_env.reveal_all());
-        let details = self.tcx.layout_raw(param_env.reveal_all().and(ty))?;
+        let param_env = self.param_env.with_reveal_all();
+        let ty = self.tcx.normalize_erasing_regions(param_env, ty);
+        let details = self.tcx.layout_raw(param_env.and(ty))?;
         let layout = TyLayout {
             ty,
             details
diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs
index 21affcbc9ed..dbfe7770bbd 100644
--- a/src/librustc/ty/maps/config.rs
+++ b/src/librustc/ty/maps/config.rs
@@ -11,7 +11,8 @@
 use dep_graph::SerializedDepNodeIndex;
 use hir::def_id::{CrateNum, DefId, DefIndex};
 use mir::interpret::{GlobalId};
-use ty::{self, Ty, TyCtxt};
+use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal};
+use ty::{self, ParamEnvAnd, Ty, TyCtxt};
 use ty::subst::Substs;
 use ty::maps::queries;
 
@@ -51,6 +52,27 @@ impl<'tcx, M: QueryConfig<Key=DefId>> QueryDescription<'tcx> for M {
     }
 }
 
+impl<'tcx> QueryDescription<'tcx> for queries::normalize_projection_ty<'tcx> {
+    fn describe(
+        _tcx: TyCtxt,
+        goal: CanonicalProjectionGoal<'tcx>,
+    ) -> String {
+        format!("normalizing `{:?}`", goal)
+    }
+}
+
+impl<'tcx> QueryDescription<'tcx> for queries::dropck_outlives<'tcx> {
+    fn describe(_tcx: TyCtxt, goal: CanonicalTyGoal<'tcx>) -> String {
+        format!("computing dropck types for `{:?}`", goal)
+    }
+}
+
+impl<'tcx> QueryDescription<'tcx> for queries::normalize_ty_after_erasing_regions<'tcx> {
+    fn describe(_tcx: TyCtxt, goal: ParamEnvAnd<'tcx, Ty<'tcx>>) -> String {
+        format!("normalizing `{:?}`", goal)
+    }
+}
+
 impl<'tcx> QueryDescription<'tcx> for queries::is_copy_raw<'tcx> {
     fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String {
         format!("computing whether `{}` is `Copy`", env.value)
@@ -588,12 +610,6 @@ impl<'tcx> QueryDescription<'tcx> for queries::has_copy_closures<'tcx> {
     }
 }
 
-impl<'tcx> QueryDescription<'tcx> for queries::fully_normalize_monormophic_ty<'tcx> {
-    fn describe(_tcx: TyCtxt, _: Ty) -> String {
-        format!("normalizing types")
-    }
-}
-
 impl<'tcx> QueryDescription<'tcx> for queries::features_query<'tcx> {
     fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
         format!("looking up enabled feature gates")
diff --git a/src/librustc/ty/maps/keys.rs b/src/librustc/ty/maps/keys.rs
index 8fb1ad0da82..b8167ec9186 100644
--- a/src/librustc/ty/maps/keys.rs
+++ b/src/librustc/ty/maps/keys.rs
@@ -11,6 +11,7 @@
 //! Defines the set of legal keys that can be used in queries.
 
 use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex};
+use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal};
 use ty::{self, Ty, TyCtxt};
 use ty::subst::Substs;
 use ty::fast_reject::SimplifiedType;
@@ -170,3 +171,23 @@ impl Key for InternedString {
         DUMMY_SP
     }
 }
+
+impl<'tcx> Key for CanonicalProjectionGoal<'tcx> {
+    fn map_crate(&self) -> CrateNum {
+        LOCAL_CRATE
+    }
+
+    fn default_span(&self, _tcx: TyCtxt) -> Span {
+        DUMMY_SP
+    }
+}
+
+impl<'tcx> Key for CanonicalTyGoal<'tcx> {
+    fn map_crate(&self) -> CrateNum {
+        LOCAL_CRATE
+    }
+
+    fn default_span(&self, _tcx: TyCtxt) -> Span {
+        DUMMY_SP
+    }
+}
diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs
index 0ded759fec7..7d726d2e3cd 100644
--- a/src/librustc/ty/maps/mod.rs
+++ b/src/librustc/ty/maps/mod.rs
@@ -14,6 +14,7 @@ use hir::def_id::{CrateNum, DefId, DefIndex};
 use hir::def::{Def, Export};
 use hir::{self, TraitCandidate, ItemLocalId, TransFnAttrs};
 use hir::svh::Svh;
+use infer::canonical::{Canonical, QueryResult};
 use lint;
 use middle::borrowck::BorrowCheckResult;
 use middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary,
@@ -33,8 +34,11 @@ use mir::interpret::{GlobalId};
 use session::{CompileResult, CrateDisambiguator};
 use session::config::OutputFilenames;
 use traits::Vtable;
+use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal, NoSolution};
+use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult};
+use traits::query::normalize::NormalizationResult;
 use traits::specialization_graph;
-use ty::{self, CrateInherentImpls, Ty, TyCtxt};
+use ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
 use ty::steal::Steal;
 use ty::subst::Substs;
 use util::nodemap::{DefIdSet, DefIdMap, ItemLocalSet};
@@ -111,7 +115,9 @@ define_maps! { <'tcx>
     [] fn adt_def: AdtDefOfItem(DefId) -> &'tcx ty::AdtDef,
     [] fn adt_destructor: AdtDestructor(DefId) -> Option<ty::Destructor>,
     [] fn adt_sized_constraint: SizedConstraint(DefId) -> &'tcx [Ty<'tcx>],
-    [] fn adt_dtorck_constraint: DtorckConstraint(DefId) -> ty::DtorckConstraint<'tcx>,
+    [] fn adt_dtorck_constraint: DtorckConstraint(
+        DefId
+    ) -> Result<DtorckConstraint<'tcx>, NoSolution>,
 
     /// True if this is a const fn
     [] fn is_const_fn: IsConstFn(DefId) -> bool,
@@ -378,7 +384,27 @@ define_maps! { <'tcx>
     // Normally you would just use `tcx.erase_regions(&value)`,
     // however, which uses this query as a kind of cache.
     [] fn erase_regions_ty: erase_regions_ty(Ty<'tcx>) -> Ty<'tcx>,
-    [] fn fully_normalize_monormophic_ty: normalize_ty_node(Ty<'tcx>) -> Ty<'tcx>,
+
+    /// Do not call this query directly: invoke `normalize` instead.
+    [] fn normalize_projection_ty: NormalizeProjectionTy(
+        CanonicalProjectionGoal<'tcx>
+    ) -> Result<
+        Lrc<Canonical<'tcx, QueryResult<'tcx, NormalizationResult<'tcx>>>>,
+        NoSolution,
+    >,
+
+    /// Do not call this query directly: invoke `normalize_erasing_regions` instead.
+    [] fn normalize_ty_after_erasing_regions: NormalizeTyAfterErasingRegions(
+        ParamEnvAnd<'tcx, Ty<'tcx>>
+    ) -> Ty<'tcx>,
+
+    /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead.
+    [] fn dropck_outlives: DropckOutlives(
+        CanonicalTyGoal<'tcx>
+    ) -> Result<
+        Lrc<Canonical<'tcx, QueryResult<'tcx, DropckOutlivesResult<'tcx>>>>,
+        NoSolution,
+    >,
 
     [] fn substitute_normalize_and_test_predicates:
         substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool,
@@ -537,9 +563,6 @@ fn output_filenames_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> {
 fn vtable_methods_node<'tcx>(trait_ref: ty::PolyTraitRef<'tcx>) -> DepConstructor<'tcx> {
     DepConstructor::VtableMethods{ trait_ref }
 }
-fn normalize_ty_node<'tcx>(_: Ty<'tcx>) -> DepConstructor<'tcx> {
-    DepConstructor::NormalizeTy
-}
 
 fn substitute_normalize_and_test_predicates_node<'tcx>(key: (DefId, &'tcx Substs<'tcx>))
                                             -> DepConstructor<'tcx> {
diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs
index 68d10888902..bc7186f781a 100644
--- a/src/librustc/ty/maps/plumbing.rs
+++ b/src/librustc/ty/maps/plumbing.rs
@@ -772,7 +772,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
         DepKind::FulfillObligation |
         DepKind::VtableMethods |
         DepKind::EraseRegionsTy |
-        DepKind::NormalizeTy |
+        DepKind::NormalizeProjectionTy |
+        DepKind::NormalizeTyAfterErasingRegions |
+        DepKind::DropckOutlives |
         DepKind::SubstituteNormalizeAndTestPredicates |
         DepKind::InstanceDefSizeEstimate |
 
diff --git a/src/librustc/ty/maps/values.rs b/src/librustc/ty/maps/values.rs
index 165798d19f1..8d38d7dbbbb 100644
--- a/src/librustc/ty/maps/values.rs
+++ b/src/librustc/ty/maps/values.rs
@@ -35,12 +35,6 @@ impl<'tcx> Value<'tcx> for Ty<'tcx> {
     }
 }
 
-impl<'tcx> Value<'tcx> for ty::DtorckConstraint<'tcx> {
-    fn from_cycle_error<'a>(_: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
-        Self::empty()
-    }
-}
-
 impl<'tcx> Value<'tcx> for ty::SymbolName {
     fn from_cycle_error<'a>(_: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
         ty::SymbolName { name: Symbol::intern("<error>").as_str() }
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 63494438f7d..fc1d26b0e09 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -29,20 +29,18 @@ use mir::Mir;
 use mir::interpret::{GlobalId, Value, PrimVal};
 use mir::GeneratorLayout;
 use session::CrateDisambiguator;
-use traits;
+use traits::{self, Reveal};
 use ty;
 use ty::subst::{Subst, Substs};
 use ty::util::{IntTypeExt, Discr};
 use ty::walk::TypeWalker;
-use util::common::ErrorReported;
-use util::nodemap::{NodeSet, DefIdMap, FxHashMap, FxHashSet};
+use util::nodemap::{NodeSet, DefIdMap, FxHashMap};
 
 use serialize::{self, Encodable, Encoder};
 use std::cell::RefCell;
 use std::cmp;
 use std::fmt;
 use std::hash::{Hash, Hasher};
-use std::iter::FromIterator;
 use std::ops::Deref;
 use rustc_data_structures::sync::Lrc;
 use std::slice;
@@ -60,7 +58,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult,
 
 use hir;
 
-pub use self::sty::{Binder, DebruijnIndex};
+pub use self::sty::{Binder, CanonicalVar, DebruijnIndex};
 pub use self::sty::{FnSig, GenSig, PolyFnSig, PolyGenSig};
 pub use self::sty::{InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
 pub use self::sty::{ClosureSubsts, GeneratorInterior, TypeAndMut};
@@ -452,6 +450,10 @@ bitflags! {
         // Currently we can't normalize projections w/ bound regions.
         const HAS_NORMALIZABLE_PROJECTION = 1 << 12;
 
+        // Set if this includes a "canonical" type or region var --
+        // ought to be true only for the results of canonicalization.
+        const HAS_CANONICAL_VARS = 1 << 13;
+
         const NEEDS_SUBST        = TypeFlags::HAS_PARAMS.bits |
                                    TypeFlags::HAS_SELF.bits |
                                    TypeFlags::HAS_RE_EARLY_BOUND.bits;
@@ -470,7 +472,8 @@ bitflags! {
                                   TypeFlags::HAS_PROJECTION.bits |
                                   TypeFlags::HAS_TY_CLOSURE.bits |
                                   TypeFlags::HAS_LOCAL_NAMES.bits |
-                                  TypeFlags::KEEP_IN_LOCAL_TCX.bits;
+                                  TypeFlags::KEEP_IN_LOCAL_TCX.bits |
+                                  TypeFlags::HAS_CANONICAL_VARS.bits;
     }
 }
 
@@ -1396,32 +1399,81 @@ pub struct ParamEnv<'tcx> {
 }
 
 impl<'tcx> ParamEnv<'tcx> {
+    /// Construct a trait environment suitable for contexts where
+    /// there are no where clauses in scope. Hidden types (like `impl
+    /// Trait`) are left hidden, so this is suitable for ordinary
+    /// type-checking.
+    pub fn empty() -> Self {
+        Self::new(ty::Slice::empty(), Reveal::UserFacing, ty::UniverseIndex::ROOT)
+    }
+
+    /// Construct a trait environment with no where clauses in scope
+    /// where the values of all `impl Trait` and other hidden types
+    /// are revealed. This is suitable for monomorphized, post-typeck
+    /// environments like trans or doing optimizations.
+    ///
+    /// NB. If you want to have predicates in scope, use `ParamEnv::new`,
+    /// or invoke `param_env.with_reveal_all()`.
+    pub fn reveal_all() -> Self {
+        Self::new(ty::Slice::empty(), Reveal::All, ty::UniverseIndex::ROOT)
+    }
+
+    /// Construct a trait environment with the given set of predicates.
+    pub fn new(caller_bounds: &'tcx ty::Slice<ty::Predicate<'tcx>>,
+               reveal: Reveal,
+               universe: ty::UniverseIndex)
+               -> Self {
+        ty::ParamEnv { caller_bounds, reveal, universe }
+    }
+
+    /// Returns a new parameter environment with the same clauses, but
+    /// which "reveals" the true results of projections in all cases
+    /// (even for associated types that are specializable).  This is
+    /// the desired behavior during trans and certain other special
+    /// contexts; normally though we want to use `Reveal::UserFacing`,
+    /// which is the default.
+    pub fn with_reveal_all(self) -> Self {
+        ty::ParamEnv { reveal: Reveal::All, ..self }
+    }
+
+    /// Returns this same environment but with no caller bounds.
+    pub fn without_caller_bounds(self) -> Self {
+        ty::ParamEnv { caller_bounds: ty::Slice::empty(), ..self }
+    }
+
     /// Creates a suitable environment in which to perform trait
-    /// queries on the given value. This will either be `self` *or*
-    /// the empty environment, depending on whether `value` references
-    /// type parameters that are in scope. (If it doesn't, then any
-    /// judgements should be completely independent of the context,
-    /// and hence we can safely use the empty environment so as to
-    /// enable more sharing across functions.)
+    /// queries on the given value. When type-checking, this is simply
+    /// the pair of the environment plus value. But when reveal is set to
+    /// All, then if `value` does not reference any type parameters, we will
+    /// pair it with the empty environment. This improves caching and is generally
+    /// invisible.
     ///
-    /// NB: This is a mildly dubious thing to do, in that a function
-    /// (or other environment) might have wacky where-clauses like
+    /// NB: We preserve the environment when type-checking because it
+    /// is possible for the user to have wacky where-clauses like
     /// `where Box<u32>: Copy`, which are clearly never
-    /// satisfiable. The code will at present ignore these,
-    /// effectively, when type-checking the body of said
-    /// function. This preserves existing behavior in any
-    /// case. --nmatsakis
+    /// satisfiable. We generally want to behave as if they were true,
+    /// although the surrounding function is never reachable.
     pub fn and<T: TypeFoldable<'tcx>>(self, value: T) -> ParamEnvAnd<'tcx, T> {
-        assert!(!value.needs_infer());
-        if value.has_param_types() || value.has_self_ty() {
-            ParamEnvAnd {
-                param_env: self,
-                value,
+        match self.reveal {
+            Reveal::UserFacing => {
+                ParamEnvAnd {
+                    param_env: self,
+                    value,
+                }
             }
-        } else {
-            ParamEnvAnd {
-                param_env: ParamEnv::empty(self.reveal),
-                value,
+
+            Reveal::All => {
+                if value.needs_infer() || value.has_param_types() || value.has_self_ty() {
+                    ParamEnvAnd {
+                        param_env: self,
+                        value,
+                    }
+                } else {
+                    ParamEnvAnd {
+                        param_env: self.without_caller_bounds(),
+                        value,
+                    }
+                }
             }
         }
     }
@@ -1829,7 +1881,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
         tcx: TyCtxt<'a, 'gcx, 'tcx>,
         expr_did: DefId,
     ) -> Option<Discr<'tcx>> {
-        let param_env = ParamEnv::empty(traits::Reveal::UserFacing);
+        let param_env = ParamEnv::empty();
         let repr_type = self.repr.discr_type();
         let bit_size = layout::Integer::from_attr(tcx, repr_type).size().bits();
         let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did);
@@ -2607,38 +2659,6 @@ fn adt_sized_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     result
 }
 
-/// Calculates the dtorck constraint for a type.
-fn adt_dtorck_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                   def_id: DefId)
-                                   -> DtorckConstraint<'tcx> {
-    let def = tcx.adt_def(def_id);
-    let span = tcx.def_span(def_id);
-    debug!("dtorck_constraint: {:?}", def);
-
-    if def.is_phantom_data() {
-        let result = DtorckConstraint {
-            outlives: vec![],
-            dtorck_types: vec![
-                tcx.mk_param_from_def(&tcx.generics_of(def_id).types[0])
-           ]
-        };
-        debug!("dtorck_constraint: {:?} => {:?}", def, result);
-        return result;
-    }
-
-    let mut result = def.all_fields()
-        .map(|field| tcx.type_of(field.did))
-        .map(|fty| tcx.dtorck_constraint_for_ty(span, fty, 0, fty))
-        .collect::<Result<DtorckConstraint, ErrorReported>>()
-        .unwrap_or(DtorckConstraint::empty());
-    result.outlives.extend(tcx.destructor_constraints(def));
-    result.dedup();
-
-    debug!("dtorck_constraint: {:?} => {:?}", def, result);
-
-    result
-}
-
 fn associated_item_def_ids<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                      def_id: DefId)
                                      -> Lrc<Vec<DefId>> {
@@ -2754,7 +2774,6 @@ pub fn provide(providers: &mut ty::maps::Providers) {
         associated_item,
         associated_item_def_ids,
         adt_sized_constraint,
-        adt_dtorck_constraint,
         def_span,
         param_env,
         trait_of_item,
@@ -2777,49 +2796,6 @@ pub struct CrateInherentImpls {
     pub inherent_impls: DefIdMap<Lrc<Vec<DefId>>>,
 }
 
-/// A set of constraints that need to be satisfied in order for
-/// a type to be valid for destruction.
-#[derive(Clone, Debug)]
-pub struct DtorckConstraint<'tcx> {
-    /// Types that are required to be alive in order for this
-    /// type to be valid for destruction.
-    pub outlives: Vec<ty::subst::Kind<'tcx>>,
-    /// Types that could not be resolved: projections and params.
-    pub dtorck_types: Vec<Ty<'tcx>>,
-}
-
-impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx>
-{
-    fn from_iter<I: IntoIterator<Item=DtorckConstraint<'tcx>>>(iter: I) -> Self {
-        let mut result = Self::empty();
-
-        for constraint in iter {
-            result.outlives.extend(constraint.outlives);
-            result.dtorck_types.extend(constraint.dtorck_types);
-        }
-
-        result
-    }
-}
-
-
-impl<'tcx> DtorckConstraint<'tcx> {
-    fn empty() -> DtorckConstraint<'tcx> {
-        DtorckConstraint {
-            outlives: vec![],
-            dtorck_types: vec![]
-        }
-    }
-
-    fn dedup<'a>(&mut self) {
-        let mut outlives = FxHashSet();
-        let mut dtorck_types = FxHashSet();
-
-        self.outlives.retain(|&val| outlives.replace(val).is_none());
-        self.dtorck_types.retain(|&val| dtorck_types.replace(val).is_none());
-    }
-}
-
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)]
 pub struct SymbolName {
     // FIXME: we don't rely on interning or equality here - better have
diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs
index bae1ce31a5e..72e7d16b64d 100644
--- a/src/librustc/ty/relate.rs
+++ b/src/librustc/ty/relate.rs
@@ -15,10 +15,8 @@
 
 use hir::def_id::DefId;
 use middle::const_val::ConstVal;
-use traits::Reveal;
-use ty::subst::{UnpackedKind, Substs};
+use ty::subst::{Kind, UnpackedKind, Substs};
 use ty::{self, Ty, TyCtxt, TypeFoldable};
-use ty::fold::{TypeVisitor, TypeFolder};
 use ty::error::{ExpectedFound, TypeError};
 use mir::interpret::{GlobalId, Value, PrimVal};
 use util::common::ErrorReported;
@@ -143,15 +141,7 @@ pub fn relate_substs<'a, 'gcx, 'tcx, R>(relation: &mut R,
 
     let params = a_subst.iter().zip(b_subst).enumerate().map(|(i, (a, b))| {
         let variance = variances.map_or(ty::Invariant, |v| v[i]);
-        match (a.unpack(), b.unpack()) {
-            (UnpackedKind::Lifetime(a_lt), UnpackedKind::Lifetime(b_lt)) => {
-                Ok(relation.relate_with_variance(variance, &a_lt, &b_lt)?.into())
-            }
-            (UnpackedKind::Type(a_ty), UnpackedKind::Type(b_ty)) => {
-                Ok(relation.relate_with_variance(variance, &a_ty, &b_ty)?.into())
-            }
-            (UnpackedKind::Lifetime(_), _) | (UnpackedKind::Type(_), _) => bug!()
-        }
+        relation.relate_with_variance(variance, a, b)
     });
 
     Ok(tcx.mk_substs(params)?)
@@ -326,13 +316,9 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> {
 #[derive(Debug, Clone)]
 struct GeneratorWitness<'tcx>(&'tcx ty::Slice<Ty<'tcx>>);
 
-impl<'tcx> TypeFoldable<'tcx> for GeneratorWitness<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        GeneratorWitness(self.0.fold_with(folder))
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.0.visit_with(visitor)
+TupleStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for GeneratorWitness<'tcx> {
+        a
     }
 }
 
@@ -486,7 +472,7 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
                     ConstVal::Value(Value::ByVal(prim)) => Ok(prim.to_u64().unwrap()),
                     ConstVal::Unevaluated(def_id, substs) => {
                         // FIXME(eddyb) get the right param_env.
-                        let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
+                        let param_env = ty::ParamEnv::empty();
                         match tcx.lift_to_global(&substs) {
                             Some(substs) => {
                                 let instance = ty::Instance::resolve(
@@ -698,6 +684,27 @@ impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for Box<T> {
     }
 }
 
+impl<'tcx> Relate<'tcx> for Kind<'tcx> {
+    fn relate<'a, 'gcx, R>(
+        relation: &mut R,
+        a: &Kind<'tcx>,
+        b: &Kind<'tcx>
+    ) -> RelateResult<'tcx, Kind<'tcx>>
+    where
+        R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'a+'tcx, 'tcx: 'a,
+    {
+        match (a.unpack(), b.unpack()) {
+            (UnpackedKind::Lifetime(a_lt), UnpackedKind::Lifetime(b_lt)) => {
+                Ok(relation.relate(&a_lt, &b_lt)?.into())
+            }
+            (UnpackedKind::Type(a_ty), UnpackedKind::Type(b_ty)) => {
+                Ok(relation.relate(&a_ty, &b_ty)?.into())
+            }
+            (UnpackedKind::Lifetime(_), _) | (UnpackedKind::Type(_), _) => bug!()
+        }
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Error handling
 
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 78fccaa1131..0627bcdfb0e 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -28,154 +28,38 @@ use std::rc::Rc;
 // For things that don't carry any arena-allocated data (and are
 // copy...), just add them to this list.
 
-macro_rules! CopyImpls {
-    ($($ty:ty,)+) => {
-        $(
-            impl<'tcx> Lift<'tcx> for $ty {
-                type Lifted = Self;
-                fn lift_to_tcx<'a, 'gcx>(&self, _: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self> {
-                    Some(*self)
-                }
-            }
-
-            impl<'tcx> TypeFoldable<'tcx> for $ty {
-                fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> $ty {
-                    *self
-                }
-
-                fn super_visit_with<F: TypeVisitor<'tcx>>(&self, _: &mut F) -> bool {
-                    false
-                }
-            }
-        )+
-    }
-}
-
-CopyImpls! {
+CloneTypeFoldableAndLiftImpls! {
     (),
+    bool,
+    usize,
+    u64,
+    ::middle::region::Scope,
+    ::syntax::ast::FloatTy,
+    ::syntax::ast::NodeId,
+    ::syntax_pos::symbol::Symbol,
+    ::hir::def::Def,
+    ::hir::def_id::DefId,
+    ::hir::InlineAsm,
+    ::hir::MatchSource,
+    ::hir::Mutability,
     ::hir::Unsafety,
     ::syntax::abi::Abi,
-    ::hir::def_id::DefId,
     ::mir::Local,
     ::mir::Promoted,
     ::traits::Reveal,
+    ::ty::adjustment::AutoBorrowMutability,
+    ::ty::AdtKind,
+    // Including `BoundRegion` is a *bit* dubious, but direct
+    // references to bound region appear in `ty::Error`, and aren't
+    // really meant to be folded. In general, we can only fold a fully
+    // general `Region`.
+    ::ty::BoundRegion,
+    ::ty::ClosureKind,
+    ::ty::IntVarValue,
     ::syntax_pos::Span,
 }
 
 ///////////////////////////////////////////////////////////////////////////
-// Macros
-//
-// When possible, use one of these (relatively) convenient macros to write
-// the impls for you.
-
-#[macro_export]
-macro_rules! BraceStructLiftImpl {
-    (impl<$($p:tt),*> Lift<$tcx:tt> for $s:path {
-        type Lifted = $lifted:ty;
-        $($field:ident),* $(,)*
-    } $(where $($wc:tt)*)*) => {
-        impl<$($p),*> $crate::ty::Lift<$tcx> for $s
-            $(where $($wc)*)*
-        {
-            type Lifted = $lifted;
-
-            fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<$lifted> {
-                $(let $field = tcx.lift(&self.$field)?;)*
-                Some(Self::Lifted { $($field),* })
-            }
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! EnumLiftImpl {
-    (impl<$($p:tt),*> Lift<$tcx:tt> for $s:path {
-        type Lifted = $lifted:ty;
-        $(
-            ($variant:path) ( $( $variant_arg:ident),* )
-        ),*
-        $(,)*
-    } $(where $($wc:tt)*)*) => {
-        impl<$($p),*> $crate::ty::Lift<$tcx> for $s
-            $(where $($wc)*)*
-        {
-            type Lifted = $lifted;
-
-            fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<$lifted> {
-                match self {
-                    $($variant ( $($variant_arg),* ) => {
-                        Some($variant ( $(tcx.lift($variant_arg)?),* ))
-                    })*
-                }
-            }
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! BraceStructTypeFoldableImpl {
-    (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path {
-        $($field:ident),* $(,)*
-    } $(where $($wc:tt)*)*) => {
-        impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s
-            $(where $($wc)*)*
-        {
-            fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>(
-                &self,
-                folder: &mut V,
-            ) -> Self {
-                let $s { $($field,)* } = self;
-                $s { $($field: $field.fold_with(folder),)* }
-            }
-
-            fn super_visit_with<V: $crate::ty::fold::TypeVisitor<$tcx>>(
-                &self,
-                visitor: &mut V,
-            ) -> bool {
-                let $s { $($field,)* } = self;
-                false $(|| $field.visit_with(visitor))*
-            }
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! EnumTypeFoldableImpl {
-    (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path {
-        $(
-            ($variant:path) ( $( $variant_arg:ident),* )
-        ),*
-        $(,)*
-    } $(where $($wc:tt)*)*) => {
-        impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s
-            $(where $($wc)*)*
-        {
-            fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>(
-                &self,
-                folder: &mut V,
-            ) -> Self {
-                match self {
-                    $($variant ( $($variant_arg),* ) => {
-                        $variant ( $($variant_arg.fold_with(folder)),* )
-                    })*
-                }
-            }
-
-            fn super_visit_with<V: $crate::ty::fold::TypeVisitor<$tcx>>(
-                &self,
-                visitor: &mut V,
-            ) -> bool {
-                match self {
-                    $($variant ( $($variant_arg),* ) => {
-                        false $(|| $variant_arg.visit_with(visitor))*
-                    })*
-                }
-            }
-        }
-    };
-}
-
-///////////////////////////////////////////////////////////////////////////
 // Lift implementations
 
 impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) {
@@ -776,6 +660,17 @@ BraceStructLiftImpl! {
 // can easily refactor the folding into the TypeFolder trait as
 // needed.
 
+/// AdtDefs are basically the same as a DefId.
+impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::AdtDef {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _folder: &mut F) -> Self {
+        *self
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> bool {
+        false
+    }
+}
+
 impl<'tcx, T:TypeFoldable<'tcx>, U:TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> (T, U) {
         (self.0.fold_with(folder), self.1.fold_with(folder))
@@ -786,14 +681,11 @@ impl<'tcx, T:TypeFoldable<'tcx>, U:TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T
     }
 }
 
-impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Option<T> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        self.as_ref().map(|t| t.fold_with(folder))
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.iter().any(|t| t.visit_with(visitor))
-    }
+EnumTypeFoldableImpl! {
+    impl<'tcx, T> TypeFoldable<'tcx> for Option<T> {
+        (Some)(a),
+        (None),
+    } where T: TypeFoldable<'tcx>
 }
 
 impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc<T> {
@@ -881,22 +773,11 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice<ty::ExistentialPredicate<'tcx>
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialPredicate<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self  {
-        use ty::ExistentialPredicate::*;
-        match *self {
-            Trait(ref tr) => Trait(tr.fold_with(folder)),
-            Projection(ref p) => Projection(p.fold_with(folder)),
-            AutoTrait(did) => AutoTrait(did),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        match *self {
-            ty::ExistentialPredicate::Trait(ref tr) => tr.visit_with(visitor),
-            ty::ExistentialPredicate::Projection(ref p) => p.visit_with(visitor),
-            ty::ExistentialPredicate::AutoTrait(_) => false,
-        }
+EnumTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialPredicate<'tcx> {
+        (ty::ExistentialPredicate::Trait)(a),
+        (ty::ExistentialPredicate::Projection)(a),
+        (ty::ExistentialPredicate::AutoTrait)(a),
     }
 }
 
@@ -1049,13 +930,9 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::TypeAndMut<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::TypeAndMut { ty: self.ty.fold_with(folder), mutbl: self.mutbl }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.ty.visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::TypeAndMut<'tcx> {
+        ty, mutbl
     }
 }
 
@@ -1065,20 +942,9 @@ BraceStructTypeFoldableImpl! {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        let inputs_and_output = self.inputs_and_output.fold_with(folder);
-        ty::FnSig {
-            inputs_and_output: folder.tcx().intern_type_list(&inputs_and_output),
-            variadic: self.variadic,
-            unsafety: self.unsafety,
-            abi: self.abi,
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.inputs().iter().any(|i| i.visit_with(visitor)) ||
-        self.output().visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> {
+        inputs_and_output, variadic, unsafety, abi
     }
 }
 
@@ -1117,28 +983,15 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Region<'tcx> {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::ClosureSubsts<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::ClosureSubsts {
-            substs: self.substs.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.substs.visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::ClosureSubsts<'tcx> {
+        substs,
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::GeneratorInterior<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::GeneratorInterior {
-           witness: self.witness.fold_with(folder),
-           movable: self.movable,
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.witness.visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::GeneratorInterior<'tcx> {
+        witness, movable,
     }
 }
 
@@ -1149,74 +1002,32 @@ BraceStructTypeFoldableImpl! {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::Adjust<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        match *self {
-            ty::adjustment::Adjust::NeverToAny |
-            ty::adjustment::Adjust::ReifyFnPointer |
-            ty::adjustment::Adjust::UnsafeFnPointer |
-            ty::adjustment::Adjust::ClosureFnPointer |
-            ty::adjustment::Adjust::MutToConstPointer |
-            ty::adjustment::Adjust::Unsize => self.clone(),
-            ty::adjustment::Adjust::Deref(ref overloaded) => {
-                ty::adjustment::Adjust::Deref(overloaded.fold_with(folder))
-            }
-            ty::adjustment::Adjust::Borrow(ref autoref) => {
-                ty::adjustment::Adjust::Borrow(autoref.fold_with(folder))
-            }
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        match *self {
-            ty::adjustment::Adjust::NeverToAny |
-            ty::adjustment::Adjust::ReifyFnPointer |
-            ty::adjustment::Adjust::UnsafeFnPointer |
-            ty::adjustment::Adjust::ClosureFnPointer |
-            ty::adjustment::Adjust::MutToConstPointer |
-            ty::adjustment::Adjust::Unsize => false,
-            ty::adjustment::Adjust::Deref(ref overloaded) => {
-                overloaded.visit_with(visitor)
-            }
-            ty::adjustment::Adjust::Borrow(ref autoref) => {
-                autoref.visit_with(visitor)
-            }
-        }
+EnumTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::Adjust<'tcx> {
+        (ty::adjustment::Adjust::NeverToAny),
+        (ty::adjustment::Adjust::ReifyFnPointer),
+        (ty::adjustment::Adjust::UnsafeFnPointer),
+        (ty::adjustment::Adjust::ClosureFnPointer),
+        (ty::adjustment::Adjust::MutToConstPointer),
+        (ty::adjustment::Adjust::Unsize),
+        (ty::adjustment::Adjust::Deref)(a),
+        (ty::adjustment::Adjust::Borrow)(a),
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::OverloadedDeref<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::adjustment::OverloadedDeref {
-            region: self.region.fold_with(folder),
-            mutbl: self.mutbl,
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.region.visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::OverloadedDeref<'tcx> {
+        region, mutbl,
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::AutoBorrow<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        match *self {
-            ty::adjustment::AutoBorrow::Ref(ref r, m) => {
-                ty::adjustment::AutoBorrow::Ref(r.fold_with(folder), m)
-            }
-            ty::adjustment::AutoBorrow::RawPtr(m) => ty::adjustment::AutoBorrow::RawPtr(m)
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        match *self {
-            ty::adjustment::AutoBorrow::Ref(r, _m) => r.visit_with(visitor),
-            ty::adjustment::AutoBorrow::RawPtr(_m) => false,
-        }
+EnumTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::AutoBorrow<'tcx> {
+        (ty::adjustment::AutoBorrow::Ref)(a, b),
+        (ty::adjustment::AutoBorrow::RawPtr)(m),
     }
 }
 
-
 BraceStructTypeFoldableImpl! {
     impl<'tcx> TypeFoldable<'tcx> for ty::GenericPredicates<'tcx> {
         parent, predicates
@@ -1234,43 +1045,17 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice<ty::Predicate<'tcx>> {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        match *self {
-            ty::Predicate::Trait(ref a) =>
-                ty::Predicate::Trait(a.fold_with(folder)),
-            ty::Predicate::Subtype(ref binder) =>
-                ty::Predicate::Subtype(binder.fold_with(folder)),
-            ty::Predicate::RegionOutlives(ref binder) =>
-                ty::Predicate::RegionOutlives(binder.fold_with(folder)),
-            ty::Predicate::TypeOutlives(ref binder) =>
-                ty::Predicate::TypeOutlives(binder.fold_with(folder)),
-            ty::Predicate::Projection(ref binder) =>
-                ty::Predicate::Projection(binder.fold_with(folder)),
-            ty::Predicate::WellFormed(data) =>
-                ty::Predicate::WellFormed(data.fold_with(folder)),
-            ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) =>
-                ty::Predicate::ClosureKind(closure_def_id, closure_substs.fold_with(folder), kind),
-            ty::Predicate::ObjectSafe(trait_def_id) =>
-                ty::Predicate::ObjectSafe(trait_def_id),
-            ty::Predicate::ConstEvaluatable(def_id, substs) =>
-                ty::Predicate::ConstEvaluatable(def_id, substs.fold_with(folder)),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        match *self {
-            ty::Predicate::Trait(ref a) => a.visit_with(visitor),
-            ty::Predicate::Subtype(ref binder) => binder.visit_with(visitor),
-            ty::Predicate::RegionOutlives(ref binder) => binder.visit_with(visitor),
-            ty::Predicate::TypeOutlives(ref binder) => binder.visit_with(visitor),
-            ty::Predicate::Projection(ref binder) => binder.visit_with(visitor),
-            ty::Predicate::WellFormed(data) => data.visit_with(visitor),
-            ty::Predicate::ClosureKind(_closure_def_id, closure_substs, _kind) =>
-                closure_substs.visit_with(visitor),
-            ty::Predicate::ObjectSafe(_trait_def_id) => false,
-            ty::Predicate::ConstEvaluatable(_def_id, substs) => substs.visit_with(visitor),
-        }
+EnumTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
+        (ty::Predicate::Trait)(a),
+        (ty::Predicate::Subtype)(a),
+        (ty::Predicate::RegionOutlives)(a),
+        (ty::Predicate::TypeOutlives)(a),
+        (ty::Predicate::Projection)(a),
+        (ty::Predicate::WellFormed)(a),
+        (ty::Predicate::ClosureKind)(a, b, c),
+        (ty::Predicate::ObjectSafe)(a),
+        (ty::Predicate::ConstEvaluatable)(a, b),
     }
 }
 
@@ -1298,71 +1083,40 @@ BraceStructTypeFoldableImpl! {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::SubtypePredicate<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::SubtypePredicate {
-            a_is_expected: self.a_is_expected,
-            a: self.a.fold_with(folder),
-            b: self.b.fold_with(folder)
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.a.visit_with(visitor) || self.b.visit_with(visitor)
-    }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, T> TypeFoldable<'tcx> for ty::ParamEnvAnd<'tcx, T> {
+        param_env, value
+    } where T: TypeFoldable<'tcx>
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::TraitPredicate<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::TraitPredicate {
-            trait_ref: self.trait_ref.fold_with(folder)
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.trait_ref.visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::SubtypePredicate<'tcx> {
+        a_is_expected, a, b
     }
 }
 
-impl<'tcx,T,U> TypeFoldable<'tcx> for ty::OutlivesPredicate<T,U>
-    where T : TypeFoldable<'tcx>,
-          U : TypeFoldable<'tcx>,
-{
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::OutlivesPredicate(self.0.fold_with(folder),
-                              self.1.fold_with(folder))
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.0.visit_with(visitor) || self.1.visit_with(visitor)
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::TraitPredicate<'tcx> {
+        trait_ref
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::ClosureUpvar<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::ClosureUpvar {
-            def: self.def,
-            span: self.span,
-            ty: self.ty.fold_with(folder),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.ty.visit_with(visitor)
-    }
+TupleStructTypeFoldableImpl! {
+    impl<'tcx,T,U> TypeFoldable<'tcx> for ty::OutlivesPredicate<T,U> {
+        a, b
+    } where T : TypeFoldable<'tcx>, U : TypeFoldable<'tcx>,
 }
 
-impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::error::ExpectedFound<T> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        ty::error::ExpectedFound {
-            expected: self.expected.fold_with(folder),
-            found: self.found.fold_with(folder),
-        }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::ClosureUpvar<'tcx> {
+        def, span, ty
     }
+}
 
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        self.expected.visit_with(visitor) || self.found.visit_with(visitor)
-    }
+BraceStructTypeFoldableImpl! {
+    impl<'tcx, T> TypeFoldable<'tcx> for ty::error::ExpectedFound<T> {
+        expected, found
+    } where T: TypeFoldable<'tcx>
 }
 
 impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec<I, T> {
@@ -1375,69 +1129,28 @@ impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec<I, T>
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
-    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
-        use ty::error::TypeError::*;
-
-        match *self {
-            Mismatch => Mismatch,
-            UnsafetyMismatch(x) => UnsafetyMismatch(x.fold_with(folder)),
-            AbiMismatch(x) => AbiMismatch(x.fold_with(folder)),
-            Mutability => Mutability,
-            TupleSize(x) => TupleSize(x),
-            FixedArraySize(x) => FixedArraySize(x),
-            ArgCount => ArgCount,
-            RegionsDoesNotOutlive(a, b) => {
-                RegionsDoesNotOutlive(a.fold_with(folder), b.fold_with(folder))
-            },
-            RegionsInsufficientlyPolymorphic(a, b) => {
-                RegionsInsufficientlyPolymorphic(a, b.fold_with(folder))
-            },
-            RegionsOverlyPolymorphic(a, b) => {
-                RegionsOverlyPolymorphic(a, b.fold_with(folder))
-            },
-            IntMismatch(x) => IntMismatch(x),
-            FloatMismatch(x) => FloatMismatch(x),
-            Traits(x) => Traits(x),
-            VariadicMismatch(x) => VariadicMismatch(x),
-            CyclicTy(t) => CyclicTy(t.fold_with(folder)),
-            ProjectionMismatched(x) => ProjectionMismatched(x),
-            ProjectionBoundsLength(x) => ProjectionBoundsLength(x),
-            Sorts(x) => Sorts(x.fold_with(folder)),
-            ExistentialMismatch(x) => ExistentialMismatch(x.fold_with(folder)),
-            OldStyleLUB(ref x) => OldStyleLUB(x.fold_with(folder)),
-        }
-    }
-
-    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        use ty::error::TypeError::*;
-
-        match *self {
-            UnsafetyMismatch(x) => x.visit_with(visitor),
-            AbiMismatch(x) => x.visit_with(visitor),
-            RegionsDoesNotOutlive(a, b) => {
-                a.visit_with(visitor) || b.visit_with(visitor)
-            },
-            RegionsInsufficientlyPolymorphic(_, b) |
-            RegionsOverlyPolymorphic(_, b) => {
-                b.visit_with(visitor)
-            },
-            Sorts(x) => x.visit_with(visitor),
-            OldStyleLUB(ref x) => x.visit_with(visitor),
-            ExistentialMismatch(x) => x.visit_with(visitor),
-            CyclicTy(t) => t.visit_with(visitor),
-            Mismatch |
-            Mutability |
-            TupleSize(_) |
-            FixedArraySize(_) |
-            ArgCount |
-            IntMismatch(_) |
-            FloatMismatch(_) |
-            Traits(_) |
-            VariadicMismatch(_) |
-            ProjectionMismatched(_) |
-            ProjectionBoundsLength(_) => false,
-        }
+EnumTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
+        (ty::error::TypeError::Mismatch),
+        (ty::error::TypeError::UnsafetyMismatch)(x),
+        (ty::error::TypeError::AbiMismatch)(x),
+        (ty::error::TypeError::Mutability),
+        (ty::error::TypeError::TupleSize)(x),
+        (ty::error::TypeError::FixedArraySize)(x),
+        (ty::error::TypeError::ArgCount),
+        (ty::error::TypeError::RegionsDoesNotOutlive)(a, b),
+        (ty::error::TypeError::RegionsInsufficientlyPolymorphic)(a, b),
+        (ty::error::TypeError::RegionsOverlyPolymorphic)(a, b),
+        (ty::error::TypeError::IntMismatch)(x),
+        (ty::error::TypeError::FloatMismatch)(x),
+        (ty::error::TypeError::Traits)(x),
+        (ty::error::TypeError::VariadicMismatch)(x),
+        (ty::error::TypeError::CyclicTy)(t),
+        (ty::error::TypeError::ProjectionMismatched)(x),
+        (ty::error::TypeError::ProjectionBoundsLength)(x),
+        (ty::error::TypeError::Sorts)(x),
+        (ty::error::TypeError::ExistentialMismatch)(x),
+        (ty::error::TypeError::OldStyleLUB)(x),
     }
 }
 
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 7bcc816b5f0..109422564c8 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -1047,6 +1047,9 @@ pub enum RegionKind {
     /// `ClosureRegionRequirements` that are produced by MIR borrowck.
     /// See `ClosureRegionRequirements` for more details.
     ReClosureBound(RegionVid),
+
+    /// Canonicalized region, used only when preparing a trait query.
+    ReCanonical(CanonicalVar),
 }
 
 impl<'tcx> serialize::UseSpecializedDecodable for Region<'tcx> {}
@@ -1091,8 +1094,13 @@ pub enum InferTy {
     FreshTy(u32),
     FreshIntTy(u32),
     FreshFloatTy(u32),
+
+    /// Canonicalized type variable, used only when preparing a trait query.
+    CanonicalTy(CanonicalVar),
 }
 
+newtype_index!(CanonicalVar);
+
 /// A `ProjectionPredicate` for an `ExistentialTraitRef`.
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct ExistentialProjection<'tcx> {
@@ -1213,6 +1221,10 @@ impl RegionKind {
             }
             ty::ReErased => {
             }
+            ty::ReCanonical(..) => {
+                flags = flags | TypeFlags::HAS_FREE_REGIONS;
+                flags = flags | TypeFlags::HAS_CANONICAL_VARS;
+            }
             ty::ReClosureBound(..) => {
                 flags = flags | TypeFlags::HAS_FREE_REGIONS;
             }
diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs
index 5e3417e98c2..a301049fe1c 100644
--- a/src/librustc/ty/subst.rs
+++ b/src/librustc/ty/subst.rs
@@ -11,7 +11,7 @@
 // Type substitutions.
 
 use hir::def_id::DefId;
-use ty::{self, Slice, Region, Ty, TyCtxt};
+use ty::{self, Lift, Slice, Region, Ty, TyCtxt};
 use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 
 use serialize::{self, Encodable, Encoder, Decodable, Decoder};
@@ -40,6 +40,7 @@ const TAG_MASK: usize = 0b11;
 const TYPE_TAG: usize = 0b00;
 const REGION_TAG: usize = 0b01;
 
+#[derive(Debug)]
 pub enum UnpackedKind<'tcx> {
     Lifetime(ty::Region<'tcx>),
     Type(Ty<'tcx>),
@@ -113,6 +114,17 @@ impl<'tcx> fmt::Display for Kind<'tcx> {
     }
 }
 
+impl<'a, 'tcx> Lift<'tcx> for Kind<'a> {
+    type Lifted = Kind<'tcx>;
+
+    fn lift_to_tcx<'cx, 'gcx>(&self, tcx: TyCtxt<'cx, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        match self.unpack() {
+            UnpackedKind::Lifetime(a) => a.lift_to_tcx(tcx).map(|a| a.into()),
+            UnpackedKind::Type(a) => a.lift_to_tcx(tcx).map(|a| a.into()),
+        }
+    }
+}
+
 impl<'tcx> TypeFoldable<'tcx> for Kind<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         match self.unpack() {
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index cbd9a1b8d4f..753f89d8cd2 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -16,10 +16,10 @@ use hir::map::{DefPathData, Node};
 use hir;
 use ich::NodeIdHashingMode;
 use middle::const_val::ConstVal;
-use traits::{self, Reveal};
+use traits;
 use ty::{self, Ty, TyCtxt, TypeFoldable};
 use ty::fold::TypeVisitor;
-use ty::subst::{Subst, UnpackedKind};
+use ty::subst::UnpackedKind;
 use ty::maps::TyCtxtAt;
 use ty::TypeVariants::*;
 use ty::layout::Integer;
@@ -182,30 +182,6 @@ pub enum Representability {
 }
 
 impl<'tcx> ty::ParamEnv<'tcx> {
-    /// Construct a trait environment suitable for contexts where
-    /// there are no where clauses in scope.
-    pub fn empty(reveal: Reveal) -> Self {
-        Self::new(ty::Slice::empty(), reveal, ty::UniverseIndex::ROOT)
-    }
-
-    /// Construct a trait environment with the given set of predicates.
-    pub fn new(caller_bounds: &'tcx ty::Slice<ty::Predicate<'tcx>>,
-               reveal: Reveal,
-               universe: ty::UniverseIndex)
-               -> Self {
-        ty::ParamEnv { caller_bounds, reveal, universe }
-    }
-
-    /// Returns a new parameter environment with the same clauses, but
-    /// which "reveals" the true results of projections in all cases
-    /// (even for associated types that are specializable).  This is
-    /// the desired behavior during trans and certain other special
-    /// contexts; normally though we want to use `Reveal::UserFacing`,
-    /// which is the default.
-    pub fn reveal_all(self) -> Self {
-        ty::ParamEnv { reveal: Reveal::All, ..self }
-    }
-
     pub fn can_type_implement_copy<'a>(self,
                                        tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                        self_type: Ty<'tcx>, span: Span)
@@ -561,99 +537,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         result
     }
 
-    /// Return a set of constraints that needs to be satisfied in
-    /// order for `ty` to be valid for destruction.
-    pub fn dtorck_constraint_for_ty(self,
-                                    span: Span,
-                                    for_ty: Ty<'tcx>,
-                                    depth: usize,
-                                    ty: Ty<'tcx>)
-                                    -> Result<ty::DtorckConstraint<'tcx>, ErrorReported>
-    {
-        debug!("dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})",
-               span, for_ty, depth, ty);
-
-        if depth >= self.sess.recursion_limit.get() {
-            let mut err = struct_span_err!(
-                self.sess, span, E0320,
-                "overflow while adding drop-check rules for {}", for_ty);
-            err.note(&format!("overflowed on {}", ty));
-            err.emit();
-            return Err(ErrorReported);
-        }
-
-        let result = match ty.sty {
-            ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) |
-            ty::TyFloat(_) | ty::TyStr | ty::TyNever | ty::TyForeign(..) |
-            ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) |
-            ty::TyGeneratorWitness(..) => {
-                // these types never have a destructor
-                Ok(ty::DtorckConstraint::empty())
-            }
-
-            ty::TyArray(ety, _) | ty::TySlice(ety) => {
-                // single-element containers, behave like their element
-                self.dtorck_constraint_for_ty(span, for_ty, depth+1, ety)
-            }
-
-            ty::TyTuple(tys, _) => {
-                tys.iter().map(|ty| {
-                    self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty)
-                }).collect()
-            }
-
-            ty::TyClosure(def_id, substs) => {
-                substs.upvar_tys(def_id, self).map(|ty| {
-                    self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty)
-                }).collect()
-            }
-
-            ty::TyGenerator(def_id, substs, _) => {
-                // Note that the interior types are ignored here.
-                // Any type reachable inside the interior must also be reachable
-                // through the upvars.
-                substs.upvar_tys(def_id, self).map(|ty| {
-                    self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty)
-                }).collect()
-            }
-
-            ty::TyAdt(def, substs) => {
-                let ty::DtorckConstraint {
-                    dtorck_types, outlives
-                } = self.at(span).adt_dtorck_constraint(def.did);
-                Ok(ty::DtorckConstraint {
-                    // FIXME: we can try to recursively `dtorck_constraint_on_ty`
-                    // there, but that needs some way to handle cycles.
-                    dtorck_types: dtorck_types.subst(self, substs),
-                    outlives: outlives.subst(self, substs)
-                })
-            }
-
-            // Objects must be alive in order for their destructor
-            // to be called.
-            ty::TyDynamic(..) => Ok(ty::DtorckConstraint {
-                outlives: vec![ty.into()],
-                dtorck_types: vec![],
-            }),
-
-            // Types that can't be resolved. Pass them forward.
-            ty::TyProjection(..) | ty::TyAnon(..) | ty::TyParam(..) => {
-                Ok(ty::DtorckConstraint {
-                    outlives: vec![],
-                    dtorck_types: vec![ty],
-                })
-            }
-
-            ty::TyInfer(..) | ty::TyError => {
-                self.sess.delay_span_bug(span, "unresolved type in dtorck");
-                Err(ErrorReported)
-            }
-        };
-
-        debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result);
-        result
-    }
-
     pub fn is_closure(self, def_id: DefId) -> bool {
         self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr
     }
@@ -858,6 +741,9 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W>
             ty::ReEmpty => {
                 // No variant fields to hash for these ...
             }
+            ty::ReCanonical(c) => {
+                self.hash(c);
+            }
             ty::ReLateBound(db, ty::BrAnon(i)) => {
                 self.hash(db.depth);
                 self.hash(i);
diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs
index e1ae41f2472..96b77d351e2 100644
--- a/src/librustc/util/common.rs
+++ b/src/librustc/util/common.rs
@@ -373,3 +373,13 @@ fn test_to_readable_str() {
     assert_eq!("1_000_000", to_readable_str(1_000_000));
     assert_eq!("1_234_567", to_readable_str(1_234_567));
 }
+
+pub trait CellUsizeExt {
+    fn increment(&self);
+}
+
+impl CellUsizeExt for Cell<usize> {
+    fn increment(&self) {
+        self.set(self.get() + 1);
+    }
+}
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 5e2792ee641..efa53c775ae 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -721,6 +721,9 @@ define_print! {
                 ty::ReEarlyBound(ref data) => {
                     write!(f, "{}", data.name)
                 }
+                ty::ReCanonical(_) => {
+                    write!(f, "'_")
+                }
                 ty::ReLateBound(_, br) |
                 ty::ReFree(ty::FreeRegion { bound_region: br, .. }) |
                 ty::ReSkolemized(_, br) => {
@@ -785,6 +788,10 @@ define_print! {
                     write!(f, "{:?}", vid)
                 }
 
+                ty::ReCanonical(c) => {
+                    write!(f, "'?{}", c.index())
+                }
+
                 ty::ReSkolemized(id, ref bound_region) => {
                     write!(f, "ReSkolemized({:?}, {:?})", id, bound_region)
                 }
@@ -888,6 +895,7 @@ define_print! {
                     ty::TyVar(_) => write!(f, "_"),
                     ty::IntVar(_) => write!(f, "{}", "{integer}"),
                     ty::FloatVar(_) => write!(f, "{}", "{float}"),
+                    ty::CanonicalTy(_) => write!(f, "_"),
                     ty::FreshTy(v) => write!(f, "FreshTy({})", v),
                     ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v),
                     ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v)
@@ -899,6 +907,7 @@ define_print! {
                 ty::TyVar(ref v) => write!(f, "{:?}", v),
                 ty::IntVar(ref v) => write!(f, "{:?}", v),
                 ty::FloatVar(ref v) => write!(f, "{:?}", v),
+                ty::CanonicalTy(v) => write!(f, "?{:?}", v.index()),
                 ty::FreshTy(v) => write!(f, "FreshTy({:?})", v),
                 ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v),
                 ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v)
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index 9888b2fffc7..a01b3cbf47b 100644
--- a/src/librustc_borrowck/borrowck/check_loans.rs
+++ b/src/librustc_borrowck/borrowck/check_loans.rs
@@ -427,6 +427,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
 
             // These cannot exist in borrowck
             RegionKind::ReVar(..) |
+            RegionKind::ReCanonical(..) |
             RegionKind::ReSkolemized(..) |
             RegionKind::ReClosureBound(..) |
             RegionKind::ReErased => span_bug!(borrow_span,
diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
index 5cbe2822e5c..49234f4ed7f 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
@@ -366,6 +366,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
 
                     ty::ReStatic => self.item_ub,
 
+                    ty::ReCanonical(_) |
                     ty::ReEmpty |
                     ty::ReClosureBound(..) |
                     ty::ReLateBound(..) |
diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs
index 9d636b48bd0..459aa9ea488 100644
--- a/src/librustc_const_eval/lib.rs
+++ b/src/librustc_const_eval/lib.rs
@@ -23,6 +23,7 @@
 #![feature(slice_patterns)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
+#![feature(macro_lifetime_matcher)]
 #![feature(i128_type)]
 #![feature(from_ref)]
 
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs
index 11c2bd73687..a5b1a7e57ab 100644
--- a/src/librustc_data_structures/indexed_vec.rs
+++ b/src/librustc_data_structures/indexed_vec.rs
@@ -330,7 +330,7 @@ macro_rules! newtype_index {
     );
 }
 
-#[derive(Clone, PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq, Hash)]
 pub struct IndexVec<I: Idx, T> {
     pub raw: Vec<T>,
     _marker: PhantomData<fn(&I)>
diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml
index 6a1d9e56534..3bff79ed3a6 100644
--- a/src/librustc_driver/Cargo.toml
+++ b/src/librustc_driver/Cargo.toml
@@ -28,6 +28,7 @@ rustc_plugin = { path = "../librustc_plugin" }
 rustc_privacy = { path = "../librustc_privacy" }
 rustc_resolve = { path = "../librustc_resolve" }
 rustc_save_analysis = { path = "../librustc_save_analysis" }
+rustc_traits = { path = "../librustc_traits" }
 rustc_trans_utils = { path = "../librustc_trans_utils" }
 rustc_typeck = { path = "../librustc_typeck" }
 serialize = { path = "../libserialize" }
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 485ee1130d3..542f818c381 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -31,6 +31,7 @@ use rustc_incremental;
 use rustc_resolve::{MakeGlobMap, Resolver, ResolverArenas};
 use rustc_metadata::creader::CrateLoader;
 use rustc_metadata::cstore::{self, CStore};
+use rustc_traits;
 use rustc_trans_utils::trans_crate::TransCrate;
 use rustc_typeck as typeck;
 use rustc_privacy;
@@ -942,6 +943,7 @@ pub fn default_provide(providers: &mut ty::maps::Providers) {
     traits::provide(providers);
     reachable::provide(providers);
     rustc_passes::provide(providers);
+    rustc_traits::provide(providers);
     middle::region::provide(providers);
     cstore::provide(providers);
     lint::provide(providers);
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index f6aa58213fc..746f2db4767 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -46,6 +46,7 @@ extern crate rustc_metadata;
 extern crate rustc_mir;
 extern crate rustc_resolve;
 extern crate rustc_save_analysis;
+extern crate rustc_traits;
 extern crate rustc_trans_utils;
 extern crate rustc_typeck;
 extern crate serialize;
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index becdbb54e5b..06610609ebd 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -18,7 +18,7 @@ use rustc_lint;
 use rustc_resolve::MakeGlobMap;
 use rustc::middle::region;
 use rustc::ty::subst::Subst;
-use rustc::traits::{ObligationCause, Reveal};
+use rustc::traits::ObligationCause;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc::ty::maps::OnDiskCache;
 use rustc::infer::{self, InferOk, InferResult};
@@ -153,7 +153,7 @@ fn test_env<F>(source_string: &str,
                              |tcx| {
         tcx.infer_ctxt().enter(|infcx| {
             let mut region_scope_tree = region::ScopeTree::default();
-            let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
+            let param_env = ty::ParamEnv::empty();
             body(Env {
                 infcx: &infcx,
                 region_scope_tree: &mut region_scope_tree,
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index c6698cbd006..031033f7208 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -33,7 +33,7 @@ use rustc::hir::def_id::DefId;
 use rustc::cfg;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty};
-use rustc::traits::{self, Reveal};
+use rustc::traits;
 use rustc::hir::map as hir_map;
 use util::nodemap::NodeSet;
 use lint::{LateContext, LintContext, LintArray};
@@ -525,7 +525,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations {
         if def.has_dtor(cx.tcx) {
             return;
         }
-        let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
+        let param_env = ty::ParamEnv::empty();
         if !ty.moves_by_default(cx.tcx, param_env, item.span) {
             return;
         }
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index 266f322e397..445fe0cc401 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -12,7 +12,7 @@
 
 use rustc::hir::map as hir_map;
 use rustc::ty::subst::Substs;
-use rustc::ty::{self, AdtKind, Ty, TyCtxt};
+use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
 use rustc::ty::layout::{self, LayoutOf};
 use util::nodemap::FxHashSet;
 use lint::{LateContext, LintContext, LintArray};
@@ -509,8 +509,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                         // make sure the fields are actually safe.
                         let mut all_phantom = true;
                         for field in &def.non_enum_variant().fields {
-                            let field_ty = cx.fully_normalize_associated_types_in(
-                                &field.ty(cx, substs)
+                            let field_ty = cx.normalize_erasing_regions(
+                                ParamEnv::reveal_all(),
+                                field.ty(cx, substs),
                             );
                             // repr(transparent) types are allowed to have arbitrary ZSTs, not just
                             // PhantomData -- skip checking all ZST fields
@@ -556,8 +557,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
                         let mut all_phantom = true;
                         for field in &def.non_enum_variant().fields {
-                            let field_ty = cx.fully_normalize_associated_types_in(
-                                &field.ty(cx, substs)
+                            let field_ty = cx.normalize_erasing_regions(
+                                ParamEnv::reveal_all(),
+                                field.ty(cx, substs),
                             );
                             let r = self.check_type_for_ffi(cache, field_ty);
                             match r {
@@ -596,8 +598,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                         // Check the contained variants.
                         for variant in &def.variants {
                             for field in &variant.fields {
-                                let arg = cx.fully_normalize_associated_types_in(
-                                    &field.ty(cx, substs)
+                                let arg = cx.normalize_erasing_regions(
+                                    ParamEnv::reveal_all(),
+                                    field.ty(cx, substs),
                                 );
                                 let r = self.check_type_for_ffi(cache, arg);
                                 match r {
@@ -716,7 +719,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
     fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>) {
         // it is only OK to use this function because extern fns cannot have
         // any generic types right now:
-        let ty = self.cx.tcx.fully_normalize_associated_types_in(&ty);
+        let ty = self.cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
 
         match self.check_type_for_ffi(&mut FxHashSet(), ty) {
             FfiResult::FfiSafe => {}
diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs
index da0da622d52..f77c22bd895 100644
--- a/src/librustc_metadata/lib.rs
+++ b/src/librustc_metadata/lib.rs
@@ -18,7 +18,9 @@
 #![feature(fs_read_write)]
 #![feature(i128_type)]
 #![feature(libc)]
+#![feature(macro_lifetime_matcher)]
 #![feature(proc_macro_internals)]
+#![feature(macro_lifetime_matcher)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(specialization)]
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 7ab52e98a0e..84ba1367450 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -457,6 +457,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             (RegionKind::ReLateBound(_, _), _)
             | (RegionKind::ReSkolemized(_, _), _)
             | (RegionKind::ReClosureBound(_), _)
+            | (RegionKind::ReCanonical(_), _)
             | (RegionKind::ReErased, _) => {
                 span_bug!(drop_span, "region does not make sense in this context");
             }
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 3f111ebcb78..8da7c497973 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -732,7 +732,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 for (index, field) in def.all_fields().enumerate() {
                     let gcx = self.tcx.global_tcx();
                     let field_ty = field.ty(gcx, substs);
-                    let field_ty = gcx.normalize_associated_type_in_env(&field_ty, self.param_env);
+                    let field_ty = gcx.normalize_erasing_regions(self.param_env, field_ty);
                     let place = drop_place.clone().field(Field::new(index), field_ty);
 
                     self.visit_terminator_drop(loc, term, flow_state, &place, field_ty, span);
diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs
index a50b9993747..d19fd2bb596 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs
@@ -14,13 +14,9 @@ use dataflow::MaybeInitializedPlaces;
 use dataflow::move_paths::{HasMoveData, MoveData};
 use rustc::mir::{BasicBlock, Location, Mir};
 use rustc::mir::Local;
-use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
-use rustc::traits;
+use rustc::ty::{Ty, TyCtxt, TypeFoldable};
 use rustc::infer::InferOk;
-use rustc::util::common::ErrorReported;
 use borrow_check::nll::type_check::AtLocation;
-use rustc_data_structures::fx::FxHashSet;
-use syntax::codemap::DUMMY_SP;
 use util::liveness::LivenessResults;
 
 use super::TypeChecker;
@@ -105,8 +101,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
             for live_local in live_locals {
                 debug!(
                     "add_liveness_constraints: location={:?} live_local={:?}",
-                    location,
-                    live_local
+                    location, live_local
                 );
 
                 self.flow_inits.each_state_bit(|mpi_init| {
@@ -157,8 +152,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
     {
         debug!(
             "push_type_live_constraint(live_ty={:?}, location={:?})",
-            value,
-            location
+            value, location
         );
 
         self.tcx.for_each_free_region(&value, |live_region| {
@@ -182,9 +176,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
     ) {
         debug!(
             "add_drop_live_constraint(dropped_local={:?}, dropped_ty={:?}, location={:?})",
-            dropped_local,
-            dropped_ty,
-            location
+            dropped_local, dropped_ty, location
         );
 
         // If we end visiting the same type twice (usually due to a cycle involving
@@ -197,73 +189,34 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
         //
         // For this reason, we avoid calling TypeChecker.normalize, instead doing all normalization
         // ourselves in one large 'fully_perform_op' callback.
-        let (type_constraints, kind_constraints) = self.cx.fully_perform_op(location.at_self(),
-            |cx| {
-
-            let tcx = cx.infcx.tcx;
-            let mut selcx = traits::SelectionContext::new(cx.infcx);
-            let cause = cx.misc(cx.last_span);
-
-            let mut types = vec![(dropped_ty, 0)];
-            let mut final_obligations = Vec::new();
-            let mut type_constraints = Vec::new();
-            let mut kind_constraints = Vec::new();
-
-            let mut known = FxHashSet();
-
-            while let Some((ty, depth)) = types.pop() {
-                let span = DUMMY_SP; // FIXME
-                let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
-                    Ok(result) => result,
-                    Err(ErrorReported) => {
-                        continue;
-                    }
-                };
-
-                let ty::DtorckConstraint {
-                    outlives,
-                    dtorck_types,
-                } = result;
-
-                // All things in the `outlives` array may be touched by
-                // the destructor and must be live at this point.
-                for outlive in outlives {
+        let kind_constraints = self.cx
+            .fully_perform_op(location.at_self(), |cx| {
+                let span = cx.last_span;
+
+                let mut final_obligations = Vec::new();
+                let mut kind_constraints = Vec::new();
+
+                let InferOk {
+                    value: kinds,
+                    obligations,
+                } = cx.infcx
+                    .at(&cx.misc(span), cx.param_env)
+                    .dropck_outlives(dropped_ty);
+                for kind in kinds {
+                    // All things in the `outlives` array may be touched by
+                    // the destructor and must be live at this point.
                     let cause = Cause::DropVar(dropped_local, location);
-                    kind_constraints.push((outlive, location, cause));
+                    kind_constraints.push((kind, location, cause));
                 }
 
-                // However, there may also be some types that
-                // `dtorck_constraint_for_ty` could not resolve (e.g.,
-                // associated types and parameters). We need to normalize
-                // associated types here and possibly recursively process.
-                for ty in dtorck_types {
-                    let traits::Normalized { value: ty, obligations } =
-                        traits::normalize(&mut selcx, cx.param_env, cause.clone(), &ty);
-
-                    final_obligations.extend(obligations);
+                final_obligations.extend(obligations);
 
-                    let ty = cx.infcx.resolve_type_and_region_vars_if_possible(&ty);
-                    match ty.sty {
-                        ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
-                            let cause = Cause::DropVar(dropped_local, location);
-                            type_constraints.push((ty, location, cause));
-                        }
-
-                        _ => if known.insert(ty) {
-                            types.push((ty, depth + 1));
-                        },
-                    }
-                }
-            }
-
-            Ok(InferOk {
-                value: (type_constraints, kind_constraints), obligations: final_obligations
+                Ok(InferOk {
+                    value: kind_constraints,
+                    obligations: final_obligations,
+                })
             })
-        }).unwrap();
-
-        for (ty, location, cause) in type_constraints {
-            self.push_type_live_constraint(ty, location, cause);
-        }
+            .unwrap();
 
         for (kind, location, cause) in kind_constraints {
             self.push_type_live_constraint(kind, location, cause);
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index 36e173dd5d6..06e6be5cd56 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -20,7 +20,8 @@ use dataflow::move_paths::MoveData;
 use rustc::hir::def_id::DefId;
 use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
 use rustc::infer::region_constraints::{GenericKind, RegionConstraintData};
-use rustc::traits::{self, FulfillmentContext};
+use rustc::traits::{self, Normalized, FulfillmentContext};
+use rustc::traits::query::NoSolution;
 use rustc::ty::error::TypeError;
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants};
@@ -243,8 +244,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
     fn sanitize_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
         debug!(
             "sanitize_constant(constant={:?}, location={:?})",
-            constant,
-            location
+            constant, location
         );
 
         let expected_ty = match constant.literal {
@@ -678,8 +678,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
 
         let data = self.infcx.take_and_reset_region_constraints();
         if !data.is_empty() {
-            debug!("fully_perform_op: constraints generated at {:?} are {:#?}",
-                   locations, data);
+            debug!(
+                "fully_perform_op: constraints generated at {:?} are {:#?}",
+                locations, data
+            );
             self.constraints
                 .outlives_sets
                 .push(OutlivesSet { locations, data });
@@ -1137,12 +1139,16 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             }
             TerminatorKind::FalseUnwind {
                 real_target,
-                unwind
+                unwind,
             } => {
                 self.assert_iscleanup(mir, block_data, real_target, is_cleanup);
                 if let Some(unwind) = unwind {
                     if is_cleanup {
-                        span_mirbug!(self, block_data, "cleanup in cleanup block via false unwind");
+                        span_mirbug!(
+                            self,
+                            block_data,
+                            "cleanup in cleanup block via false unwind"
+                        );
                     }
                     self.assert_iscleanup(mir, block_data, unwind, true);
                 }
@@ -1435,8 +1441,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
 
         debug!(
             "prove_aggregate_predicates(aggregate_kind={:?}, location={:?})",
-            aggregate_kind,
-            location
+            aggregate_kind, location
         );
 
         let instantiated_predicates = match aggregate_kind {
@@ -1502,8 +1507,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     fn prove_predicates(&mut self, predicates: &[ty::Predicate<'tcx>], location: Location) {
         debug!(
             "prove_predicates(predicates={:?}, location={:?})",
-            predicates,
-            location
+            predicates, location
         );
         self.fully_perform_op(location.at_self(), |this| {
             let cause = this.misc(this.last_span);
@@ -1550,10 +1554,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     {
         debug!("normalize(value={:?}, location={:?})", value, location);
         self.fully_perform_op(location.at_self(), |this| {
-            let mut selcx = traits::SelectionContext::new(this.infcx);
-            let cause = this.misc(this.last_span);
-            let traits::Normalized { value, obligations } =
-                traits::normalize(&mut selcx, this.param_env, cause, value);
+            let Normalized { value, obligations } = this.infcx
+                .at(&this.misc(this.last_span), this.param_env)
+                .normalize(value)
+                .unwrap_or_else(|NoSolution| {
+                    span_bug!(
+                        this.last_span,
+                        "normalization of `{:?}` failed at {:?}",
+                        value,
+                        location,
+                    );
+                });
             Ok(InferOk { value, obligations })
         }).unwrap()
     }
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index 13090ca5330..c236ce2abc5 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -249,7 +249,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
         trace!("resolve: {:?}, {:#?}", def_id, substs);
         trace!("substs: {:#?}", self.substs());
         trace!("param_env: {:#?}", self.param_env);
-        let substs = self.tcx.trans_apply_param_substs_env(self.substs(), self.param_env, &substs);
+        let substs = self.tcx.subst_and_normalize_erasing_regions(
+            self.substs(),
+            self.param_env,
+            &substs,
+        );
         ty::Instance::resolve(
             *self.tcx,
             self.param_env,
@@ -285,10 +289,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
     pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
         // miri doesn't care about lifetimes, and will choke on some crazy ones
         // let's simply get rid of them
-        let without_lifetimes = self.tcx.erase_regions(&ty);
-        let substituted = without_lifetimes.subst(*self.tcx, substs);
-        let substituted = self.tcx.fully_normalize_monormophic_ty(&substituted);
-        substituted
+        let substituted = ty.subst(*self.tcx, substs);
+        self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substituted)
     }
 
     /// Return the size and aligment of the value at the given type.
@@ -724,7 +726,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
                     ClosureFnPointer => {
                         match self.eval_operand(operand)?.ty.sty {
                             ty::TyClosure(def_id, substs) => {
-                                let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs);
+                                let substs = self.tcx.subst_and_normalize_erasing_regions(
+                                    self.substs(),
+                                    ty::ParamEnv::reveal_all(),
+                                    &substs,
+                                );
                                 let instance = ty::Instance::resolve_closure(
                                     *self.tcx,
                                     def_id,
@@ -949,8 +955,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
 
     pub fn const_eval(&self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> {
         let param_env = if self.tcx.is_static(gid.instance.def_id()).is_some() {
-            use rustc::traits;
-            ty::ParamEnv::empty(traits::Reveal::All)
+            ty::ParamEnv::reveal_all()
         } else {
             self.param_env
         };
diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs
index babc7847014..be34b8705eb 100644
--- a/src/librustc_mir/interpret/terminator/mod.rs
+++ b/src/librustc_mir/interpret/terminator/mod.rs
@@ -75,8 +75,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
                         match instance_ty.sty {
                             ty::TyFnDef(..) => {
                                 let real_sig = instance_ty.fn_sig(*self.tcx);
-                                let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig);
-                                let real_sig = self.tcx.erase_late_bound_regions_and_normalize(&real_sig);
+                                let sig = self.tcx.normalize_erasing_late_bound_regions(
+                                    ty::ParamEnv::reveal_all(),
+                                    &sig,
+                                );
+                                let real_sig = self.tcx.normalize_erasing_late_bound_regions(
+                                    ty::ParamEnv::reveal_all(),
+                                    &real_sig,
+                                );
                                 if !self.check_sig_compat(sig, real_sig)? {
                                     return err!(FunctionPointerTyMismatch(real_sig, sig));
                                 }
@@ -95,7 +101,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
                     }
                 };
                 let args = self.operands_to_args(args)?;
-                let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig);
+                let sig = self.tcx.normalize_erasing_late_bound_regions(
+                    ty::ParamEnv::reveal_all(),
+                    &sig,
+                );
                 self.eval_fn_call(
                     fn_def,
                     destination,
@@ -113,7 +122,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
                 // FIXME(CTFE): forbid drop in const eval
                 let place = self.eval_place(location)?;
                 let ty = self.place_ty(location);
-                let ty = self.tcx.trans_apply_param_substs(self.substs(), &ty);
+                let ty = self.tcx.subst_and_normalize_erasing_regions(
+                    self.substs(),
+                    ty::ParamEnv::reveal_all(),
+                    &ty,
+                );
                 trace!("TerminatorKind::drop: {:?}, type {}", location, ty);
 
                 let instance = ::monomorphize::resolve_drop_in_place(*self.tcx, ty);
diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs
index f6b47efca31..7fd2ea265de 100644
--- a/src/librustc_mir/monomorphize/collector.rs
+++ b/src/librustc_mir/monomorphize/collector.rs
@@ -196,7 +196,6 @@ use rustc::hir::def_id::DefId;
 use rustc::middle::const_val::ConstVal;
 use rustc::mir::interpret::{Value, PrimVal, AllocId, Pointer};
 use rustc::middle::lang_items::{ExchangeMallocFnLangItem, StartFnLangItem};
-use rustc::traits;
 use rustc::ty::subst::{Substs, Kind};
 use rustc::ty::{self, TypeFoldable, Ty, TyCtxt};
 use rustc::ty::adjustment::CustomCoerceUnsized;
@@ -383,7 +382,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 instance,
                 promoted: None,
             };
-            let param_env = ty::ParamEnv::empty(traits::Reveal::All);
+            let param_env = ty::ParamEnv::reveal_all();
 
             match tcx.const_eval(param_env.and(cid)) {
                 Ok(val) => collect_const(tcx, val, instance.substs, &mut neighbors),
@@ -524,11 +523,17 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
             // have to instantiate all methods of the trait being cast to, so we
             // can build the appropriate vtable.
             mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, target_ty) => {
-                let target_ty = self.tcx.trans_apply_param_substs(self.param_substs,
-                                                                  &target_ty);
+                let target_ty = self.tcx.subst_and_normalize_erasing_regions(
+                    self.param_substs,
+                    ty::ParamEnv::reveal_all(),
+                    &target_ty,
+                );
                 let source_ty = operand.ty(self.mir, self.tcx);
-                let source_ty = self.tcx.trans_apply_param_substs(self.param_substs,
-                                                                  &source_ty);
+                let source_ty = self.tcx.subst_and_normalize_erasing_regions(
+                    self.param_substs,
+                    ty::ParamEnv::reveal_all(),
+                    &source_ty,
+                );
                 let (source_ty, target_ty) = find_vtable_types_for_unsizing(self.tcx,
                                                                             source_ty,
                                                                             target_ty);
@@ -544,14 +549,20 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
             }
             mir::Rvalue::Cast(mir::CastKind::ReifyFnPointer, ref operand, _) => {
                 let fn_ty = operand.ty(self.mir, self.tcx);
-                let fn_ty = self.tcx.trans_apply_param_substs(self.param_substs,
-                                                              &fn_ty);
+                let fn_ty = self.tcx.subst_and_normalize_erasing_regions(
+                    self.param_substs,
+                    ty::ParamEnv::reveal_all(),
+                    &fn_ty,
+                );
                 visit_fn_use(self.tcx, fn_ty, false, &mut self.output);
             }
             mir::Rvalue::Cast(mir::CastKind::ClosureFnPointer, ref operand, _) => {
                 let source_ty = operand.ty(self.mir, self.tcx);
-                let source_ty = self.tcx.trans_apply_param_substs(self.param_substs,
-                                                                  &source_ty);
+                let source_ty = self.tcx.subst_and_normalize_erasing_regions(
+                    self.param_substs,
+                    ty::ParamEnv::reveal_all(),
+                    &source_ty,
+                );
                 match source_ty.sty {
                     ty::TyClosure(def_id, substs) => {
                         let instance = monomorphize::resolve_closure(
@@ -596,14 +607,22 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
         match *kind {
             mir::TerminatorKind::Call { ref func, .. } => {
                 let callee_ty = func.ty(self.mir, tcx);
-                let callee_ty = tcx.trans_apply_param_substs(self.param_substs, &callee_ty);
+                let callee_ty = tcx.subst_and_normalize_erasing_regions(
+                    self.param_substs,
+                    ty::ParamEnv::reveal_all(),
+                    &callee_ty,
+                );
                 visit_fn_use(self.tcx, callee_ty, true, &mut self.output);
             }
             mir::TerminatorKind::Drop { ref location, .. } |
             mir::TerminatorKind::DropAndReplace { ref location, .. } => {
                 let ty = location.ty(self.mir, self.tcx)
                     .to_ty(self.tcx);
-                let ty = tcx.trans_apply_param_substs(self.param_substs, &ty);
+                let ty = tcx.subst_and_normalize_erasing_regions(
+                    self.param_substs,
+                    ty::ParamEnv::reveal_all(),
+                    &ty,
+                );
                 visit_drop_use(self.tcx, ty, true, self.output);
             }
             mir::TerminatorKind::Goto { .. } |
@@ -654,7 +673,7 @@ fn visit_fn_use<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 {
     if let ty::TyFnDef(def_id, substs) = ty.sty {
         let instance = ty::Instance::resolve(tcx,
-                                             ty::ParamEnv::empty(traits::Reveal::All),
+                                             ty::ParamEnv::reveal_all(),
                                              def_id,
                                              substs).unwrap();
         visit_instance_use(tcx, instance, is_direct_call, output);
@@ -776,7 +795,7 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| {
         let type_has_metadata = |ty: Ty<'tcx>| -> bool {
             use syntax_pos::DUMMY_SP;
-            if ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::empty(traits::Reveal::All)) {
+            if ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) {
                 return false;
             }
             let tail = tcx.struct_tail(ty);
@@ -859,7 +878,7 @@ fn create_mono_items_for_vtable_methods<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             let methods = methods.iter().cloned().filter_map(|method| method)
                 .map(|(def_id, substs)| ty::Instance::resolve(
                         tcx,
-                        ty::ParamEnv::empty(traits::Reveal::All),
+                        ty::ParamEnv::reveal_all(),
                         def_id,
                         substs).unwrap())
                 .filter(|&instance| should_monomorphize_locally(tcx, &instance))
@@ -1013,7 +1032,7 @@ impl<'b, 'a, 'v> RootCollector<'b, 'a, 'v> {
 
         let start_instance = Instance::resolve(
             self.tcx,
-            ty::ParamEnv::empty(traits::Reveal::All),
+            ty::ParamEnv::reveal_all(),
             start_def_id,
             self.tcx.mk_substs(iter::once(Kind::from(main_ret_ty)))
         ).unwrap();
@@ -1062,7 +1081,7 @@ fn create_mono_items_for_default_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                     }
 
                     let instance = ty::Instance::resolve(tcx,
-                                                         ty::ParamEnv::empty(traits::Reveal::All),
+                                                         ty::ParamEnv::reveal_all(),
                                                          method.def_id,
                                                          callee_substs).unwrap();
 
@@ -1120,7 +1139,7 @@ fn collect_neighbours<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         output,
         param_substs: instance.substs,
     }.visit_mir(&mir);
-    let param_env = ty::ParamEnv::empty(traits::Reveal::All);
+    let param_env = ty::ParamEnv::reveal_all();
     for (i, promoted) in mir.promoted.iter().enumerate() {
         use rustc_data_structures::indexed_vec::Idx;
         let cid = GlobalId {
@@ -1155,9 +1174,12 @@ fn collect_const<'a, 'tcx>(
 
     let val = match constant.val {
         ConstVal::Unevaluated(def_id, substs) => {
-            let param_env = ty::ParamEnv::empty(traits::Reveal::All);
-            let substs = tcx.trans_apply_param_substs(param_substs,
-                                                        &substs);
+            let param_env = ty::ParamEnv::reveal_all();
+            let substs = tcx.subst_and_normalize_erasing_regions(
+                param_substs,
+                param_env,
+                &substs,
+            );
             let instance = ty::Instance::resolve(tcx,
                                                 param_env,
                                                 def_id,
diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs
index 1f7f1237ba7..10e2a84038d 100644
--- a/src/librustc_mir/monomorphize/item.rs
+++ b/src/librustc_mir/monomorphize/item.rs
@@ -347,7 +347,10 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> {
 
                 output.push_str("fn(");
 
-                let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig);
+                let sig = self.tcx.normalize_erasing_late_bound_regions(
+                    ty::ParamEnv::reveal_all(),
+                    &sig,
+                );
 
                 if !sig.inputs().is_empty() {
                     for &parameter_type in sig.inputs() {
diff --git a/src/librustc_mir/monomorphize/mod.rs b/src/librustc_mir/monomorphize/mod.rs
index 2ca6c76a800..5c38735d920 100644
--- a/src/librustc_mir/monomorphize/mod.rs
+++ b/src/librustc_mir/monomorphize/mod.rs
@@ -88,7 +88,7 @@ fn fn_once_adapter_instance<'a, 'tcx>(
         closure_did, substs);
 
     let sig = substs.closure_sig(closure_did, tcx);
-    let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
+    let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
     assert_eq!(sig.inputs().len(), 1);
     let substs = tcx.mk_substs([
         Kind::from(self_ty),
@@ -154,7 +154,7 @@ pub fn resolve_drop_in_place<'a, 'tcx>(
 {
     let def_id = tcx.require_lang_item(DropInPlaceFnLangItem);
     let substs = tcx.intern_substs(&[ty.into()]);
-    Instance::resolve(tcx, ty::ParamEnv::empty(traits::Reveal::All), def_id, substs).unwrap()
+    Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap()
 }
 
 pub fn custom_coerce_unsize_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -168,7 +168,7 @@ pub fn custom_coerce_unsize_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         substs: tcx.mk_substs_trait(source_ty, &[target_ty])
     });
 
-    match tcx.trans_fulfill_obligation( (ty::ParamEnv::empty(traits::Reveal::All), trait_ref)) {
+    match tcx.trans_fulfill_obligation( (ty::ParamEnv::reveal_all(), trait_ref)) {
         traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => {
             tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap()
         }
diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs
index d65c1e03298..3789342b389 100644
--- a/src/librustc_mir/monomorphize/partitioning.rs
+++ b/src/librustc_mir/monomorphize/partitioning.rs
@@ -645,7 +645,11 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
                 // This is a method within an inherent impl, find out what the
                 // self-type is:
-                let impl_self_ty = tcx.trans_impl_self_ty(impl_def_id, instance.substs);
+                let impl_self_ty = tcx.subst_and_normalize_erasing_regions(
+                    instance.substs,
+                    ty::ParamEnv::reveal_all(),
+                    &tcx.type_of(impl_def_id),
+                );
                 if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) {
                     return Some(def_id);
                 }
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index 9aff7fa2a2c..62af09cc491 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -832,14 +832,11 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
     let tcx = infcx.tcx;
     let gcx = tcx.global_tcx();
     let def_id = tcx.hir.local_def_id(ctor_id);
-    let sig = gcx.fn_sig(def_id).no_late_bound_regions()
-        .expect("LBR in ADT constructor signature");
-    let sig = gcx.erase_regions(&sig);
     let param_env = gcx.param_env(def_id);
 
-    // Normalize the sig now that we have liberated the late-bound
-    // regions.
-    let sig = gcx.normalize_associated_type_in_env(&sig, param_env);
+    // Normalize the sig.
+    let sig = gcx.fn_sig(def_id).no_late_bound_regions().expect("LBR in ADT constructor signature");
+    let sig = gcx.normalize_erasing_regions(param_env, sig);
 
     let (adt_def, substs) = match sig.output().sty {
         ty::TyAdt(adt_def, substs) => (adt_def, substs),
diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs
index b8a0e0f8907..6380d2a5c15 100644
--- a/src/librustc_mir/transform/inline.rs
+++ b/src/librustc_mir/transform/inline.rs
@@ -19,7 +19,7 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 
 use rustc::mir::*;
 use rustc::mir::visit::*;
-use rustc::ty::{self, Instance, Ty, TyCtxt, TypeFoldable};
+use rustc::ty::{self, Instance, Ty, TyCtxt};
 use rustc::ty::subst::{Subst,Substs};
 
 use std::collections::VecDeque;
@@ -129,8 +129,12 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
                 let callee_mir = match ty::queries::optimized_mir::try_get(self.tcx,
                                                                            callsite.location.span,
                                                                            callsite.callee) {
-                    Ok(ref callee_mir) if self.should_inline(callsite, callee_mir) => {
-                        subst_and_normalize(callee_mir, self.tcx, &callsite.substs, param_env)
+                    Ok(callee_mir) if self.should_inline(callsite, callee_mir) => {
+                        self.tcx.subst_and_normalize_erasing_regions(
+                            &callsite.substs,
+                            param_env,
+                            callee_mir,
+                        )
                     }
                     Ok(_) => continue,
 
@@ -664,30 +668,6 @@ fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     tcx.layout_of(param_env.and(ty)).ok().map(|layout| layout.size.bytes())
 }
 
-fn subst_and_normalize<'a, 'tcx: 'a>(
-    mir: &Mir<'tcx>,
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    substs: &'tcx ty::subst::Substs<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-) -> Mir<'tcx> {
-    struct Folder<'a, 'tcx: 'a> {
-        tcx: TyCtxt<'a, 'tcx, 'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        substs: &'tcx ty::subst::Substs<'tcx>,
-    }
-    impl<'a, 'tcx: 'a> ty::fold::TypeFolder<'tcx, 'tcx> for Folder<'a, 'tcx> {
-        fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> {
-            self.tcx
-        }
-
-        fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-            self.tcx.trans_apply_param_substs_env(&self.substs, self.param_env, &t)
-        }
-    }
-    let mut f = Folder { tcx, param_env, substs };
-    mir.fold_with(&mut f)
-}
-
 /**
  * Integrator.
  *
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 88618122e4f..94446a98e63 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -21,7 +21,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc::hir;
 use rustc::hir::def_id::DefId;
 use rustc::middle::const_val::ConstVal;
-use rustc::traits::{self, Reveal};
+use rustc::traits;
 use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
 use rustc::ty::cast::CastTy;
 use rustc::ty::maps::Providers;
@@ -1237,7 +1237,7 @@ impl MirPass for QualifyAndPromoteConstants {
             }
             let ty = mir.return_ty();
             tcx.infer_ctxt().enter(|infcx| {
-                let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
+                let param_env = ty::ParamEnv::empty();
                 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
                 let mut fulfillment_cx = traits::FulfillmentContext::new();
                 fulfillment_cx.register_bound(&infcx,
diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs
index 458dd488409..3f5208dd2d4 100644
--- a/src/librustc_mir/util/elaborate_drops.rs
+++ b/src/librustc_mir/util/elaborate_drops.rs
@@ -206,11 +206,10 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
             let field = Field::new(i);
             let subpath = self.elaborator.field_subpath(variant_path, field);
 
-            let field_ty =
-                self.tcx().normalize_associated_type_in_env(
-                    &f.ty(self.tcx(), substs),
-                    self.elaborator.param_env()
-                );
+            let field_ty = self.tcx().normalize_erasing_regions(
+                self.elaborator.param_env(),
+                f.ty(self.tcx(), substs),
+            );
             (base_place.clone().field(field, field_ty), subpath)
         }).collect()
     }
diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs
index 16278c37a0c..356ad9ec11b 100644
--- a/src/librustc_passes/rvalue_promotion.rs
+++ b/src/librustc_passes/rvalue_promotion.rs
@@ -34,7 +34,6 @@ use rustc::middle::mem_categorization::Categorization;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::maps::Providers;
 use rustc::ty::subst::Substs;
-use rustc::traits::Reveal;
 use rustc::util::nodemap::{ItemLocalSet, NodeSet};
 use rustc::hir;
 use rustc_data_structures::sync::Lrc;
@@ -87,7 +86,7 @@ fn rvalue_promotable_map<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         in_static: false,
         promotable: false,
         mut_rvalue_borrows: NodeSet(),
-        param_env: ty::ParamEnv::empty(Reveal::UserFacing),
+        param_env: ty::ParamEnv::empty(),
         identity_substs: Substs::empty(),
         result: ItemLocalSet(),
     };
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index 490dc4e5ac4..95374775651 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -13,6 +13,7 @@
        html_root_url = "https://doc.rust-lang.org/nightly/")]
 #![deny(warnings)]
 #![feature(custom_attribute)]
+#![feature(macro_lifetime_matcher)]
 #![allow(unused_attributes)]
 
 #[macro_use]
diff --git a/src/librustc_traits/Cargo.toml b/src/librustc_traits/Cargo.toml
new file mode 100644
index 00000000000..dc2a21cdab2
--- /dev/null
+++ b/src/librustc_traits/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_traits"
+version = "0.0.0"
+
+[lib]
+name = "rustc_traits"
+path = "lib.rs"
+crate-type = ["dylib"]
+
+[dependencies]
+bitflags = "1.0"
+graphviz = { path = "../libgraphviz" }
+log = { version = "0.4" }
+rustc = { path = "../librustc" }
+rustc_data_structures = { path = "../librustc_data_structures" }
+syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs
new file mode 100644
index 00000000000..2274f3942bd
--- /dev/null
+++ b/src/librustc_traits/dropck_outlives.rs
@@ -0,0 +1,285 @@
+// 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.
+
+use rustc::infer::canonical::{Canonical, QueryResult};
+use rustc::hir::def_id::DefId;
+use rustc::traits::{FulfillmentContext, Normalized, ObligationCause};
+use rustc::traits::query::{CanonicalTyGoal, NoSolution};
+use rustc::traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult};
+use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt};
+use rustc::ty::subst::Subst;
+use rustc::util::nodemap::FxHashSet;
+use std::rc::Rc;
+use syntax::codemap::{Span, DUMMY_SP};
+use util;
+
+crate fn dropck_outlives<'tcx>(
+    tcx: TyCtxt<'_, 'tcx, 'tcx>,
+    goal: CanonicalTyGoal<'tcx>,
+) -> Result<Rc<Canonical<'tcx, QueryResult<'tcx, DropckOutlivesResult<'tcx>>>>, NoSolution> {
+    debug!("dropck_outlives(goal={:#?})", goal);
+
+    tcx.infer_ctxt().enter(|ref infcx| {
+        let tcx = infcx.tcx;
+        let (
+            ParamEnvAnd {
+                param_env,
+                value: for_ty,
+            },
+            canonical_inference_vars,
+        ) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &goal);
+
+        let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] };
+
+        // A stack of types left to process. Each round, we pop
+        // something from the stack and invoke
+        // `dtorck_constraint_for_ty`. This may produce new types that
+        // have to be pushed on the stack. This continues until we have explored
+        // all the reachable types from the type `for_ty`.
+        //
+        // Example: Imagine that we have the following code:
+        //
+        // ```rust
+        // struct A {
+        //     value: B,
+        //     children: Vec<A>,
+        // }
+        //
+        // struct B {
+        //     value: u32
+        // }
+        //
+        // fn f() {
+        //   let a: A = ...;
+        //   ..
+        // } // here, `a` is dropped
+        // ```
+        //
+        // at the point where `a` is dropped, we need to figure out
+        // which types inside of `a` contain region data that may be
+        // accessed by any destructors in `a`. We begin by pushing `A`
+        // onto the stack, as that is the type of `a`. We will then
+        // invoke `dtorck_constraint_for_ty` which will expand `A`
+        // into the types of its fields `(B, Vec<A>)`. These will get
+        // pushed onto the stack. Eventually, expanding `Vec<A>` will
+        // lead to us trying to push `A` a second time -- to prevent
+        // infinite recusion, we notice that `A` was already pushed
+        // once and stop.
+        let mut ty_stack = vec![(for_ty, 0)];
+
+        // Set used to detect infinite recursion.
+        let mut ty_set = FxHashSet();
+
+        let fulfill_cx = &mut FulfillmentContext::new();
+
+        let cause = ObligationCause::dummy();
+        while let Some((ty, depth)) = ty_stack.pop() {
+            let DtorckConstraint {
+                dtorck_types,
+                outlives,
+                overflows,
+            } = dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty)?;
+
+            // "outlives" represent types/regions that may be touched
+            // by a destructor.
+            result.kinds.extend(outlives);
+            result.overflows.extend(overflows);
+
+            // dtorck types are "types that will get dropped but which
+            // do not themselves define a destructor", more or less. We have
+            // to push them onto the stack to be expanded.
+            for ty in dtorck_types {
+                match infcx.at(&cause, param_env).normalize(&ty) {
+                    Ok(Normalized {
+                        value: ty,
+                        obligations,
+                    }) => {
+                        fulfill_cx.register_predicate_obligations(infcx, obligations);
+
+                        debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
+
+                        match ty.sty {
+                            // All parameters live for the duration of the
+                            // function.
+                            ty::TyParam(..) => {}
+
+                            // A projection that we couldn't resolve - it
+                            // might have a destructor.
+                            ty::TyProjection(..) | ty::TyAnon(..) => {
+                                result.kinds.push(ty.into());
+                            }
+
+                            _ => {
+                                if ty_set.insert(ty) {
+                                    ty_stack.push((ty, depth + 1));
+                                }
+                            }
+                        }
+                    }
+
+                    // We don't actually expect to fail to normalize.
+                    // That implies a WF error somewhere else.
+                    Err(NoSolution) => {
+                        return Err(NoSolution);
+                    }
+                }
+            }
+        }
+
+        debug!("dropck_outlives: result = {:#?}", result);
+
+        util::make_query_response(infcx, canonical_inference_vars, result, fulfill_cx)
+    })
+}
+
+/// Return a set of constraints that needs to be satisfied in
+/// order for `ty` to be valid for destruction.
+fn dtorck_constraint_for_ty<'a, 'gcx, 'tcx>(
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    span: Span,
+    for_ty: Ty<'tcx>,
+    depth: usize,
+    ty: Ty<'tcx>,
+) -> Result<DtorckConstraint<'tcx>, NoSolution> {
+    debug!(
+        "dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})",
+        span, for_ty, depth, ty
+    );
+
+    if depth >= tcx.sess.recursion_limit.get() {
+        return Ok(DtorckConstraint {
+            outlives: vec![],
+            dtorck_types: vec![],
+            overflows: vec![ty],
+        });
+    }
+
+    let result = match ty.sty {
+        ty::TyBool
+        | ty::TyChar
+        | ty::TyInt(_)
+        | ty::TyUint(_)
+        | ty::TyFloat(_)
+        | ty::TyStr
+        | ty::TyNever
+        | ty::TyForeign(..)
+        | ty::TyRawPtr(..)
+        | ty::TyRef(..)
+        | ty::TyFnDef(..)
+        | ty::TyFnPtr(_)
+        | ty::TyGeneratorWitness(..) => {
+            // these types never have a destructor
+            Ok(DtorckConstraint::empty())
+        }
+
+        ty::TyArray(ety, _) | ty::TySlice(ety) => {
+            // single-element containers, behave like their element
+            dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety)
+        }
+
+        ty::TyTuple(tys, _) => tys.iter()
+            .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty))
+            .collect(),
+
+        ty::TyClosure(def_id, substs) => substs
+            .upvar_tys(def_id, tcx)
+            .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty))
+            .collect(),
+
+        ty::TyGenerator(def_id, substs, _) => {
+            // Note that the interior types are ignored here.
+            // Any type reachable inside the interior must also be reachable
+            // through the upvars.
+            substs
+                .upvar_tys(def_id, tcx)
+                .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty))
+                .collect()
+        }
+
+        ty::TyAdt(def, substs) => {
+            let DtorckConstraint {
+                dtorck_types,
+                outlives,
+                overflows,
+            } = tcx.at(span).adt_dtorck_constraint(def.did)?;
+            Ok(DtorckConstraint {
+                // FIXME: we can try to recursively `dtorck_constraint_on_ty`
+                // there, but that needs some way to handle cycles.
+                dtorck_types: dtorck_types.subst(tcx, substs),
+                outlives: outlives.subst(tcx, substs),
+                overflows: overflows.subst(tcx, substs),
+            })
+        }
+
+        // Objects must be alive in order for their destructor
+        // to be called.
+        ty::TyDynamic(..) => Ok(DtorckConstraint {
+            outlives: vec![ty.into()],
+            dtorck_types: vec![],
+            overflows: vec![],
+        }),
+
+        // Types that can't be resolved. Pass them forward.
+        ty::TyProjection(..) | ty::TyAnon(..) | ty::TyParam(..) => Ok(DtorckConstraint {
+            outlives: vec![],
+            dtorck_types: vec![ty],
+            overflows: vec![],
+        }),
+
+        ty::TyInfer(..) | ty::TyError => {
+            // By the time this code runs, all type variables ought to
+            // be fully resolved.
+            Err(NoSolution)
+        }
+    };
+
+    debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result);
+    result
+}
+
+/// Calculates the dtorck constraint for a type.
+crate fn adt_dtorck_constraint<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    def_id: DefId,
+) -> Result<DtorckConstraint<'tcx>, NoSolution> {
+    let def = tcx.adt_def(def_id);
+    let span = tcx.def_span(def_id);
+    debug!("dtorck_constraint: {:?}", def);
+
+    if def.is_phantom_data() {
+        let result = DtorckConstraint {
+            outlives: vec![],
+            dtorck_types: vec![tcx.mk_param_from_def(&tcx.generics_of(def_id).types[0])],
+            overflows: vec![],
+        };
+        debug!("dtorck_constraint: {:?} => {:?}", def, result);
+        return Ok(result);
+    }
+
+    let mut result = def.all_fields()
+        .map(|field| tcx.type_of(field.did))
+        .map(|fty| dtorck_constraint_for_ty(tcx, span, fty, 0, fty))
+        .collect::<Result<DtorckConstraint, NoSolution>>()?;
+    result.outlives.extend(tcx.destructor_constraints(def));
+    dedup_dtorck_constraint(&mut result);
+
+    debug!("dtorck_constraint: {:?} => {:?}", def, result);
+
+    Ok(result)
+}
+
+fn dedup_dtorck_constraint<'tcx>(c: &mut DtorckConstraint<'tcx>) {
+    let mut outlives = FxHashSet();
+    let mut dtorck_types = FxHashSet();
+
+    c.outlives.retain(|&val| outlives.replace(val).is_none());
+    c.dtorck_types
+        .retain(|&val| dtorck_types.replace(val).is_none());
+}
diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs
new file mode 100644
index 00000000000..45d23a2733a
--- /dev/null
+++ b/src/librustc_traits/lib.rs
@@ -0,0 +1,44 @@
+// 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.
+
+//! New recursive solver modeled on Chalk's recursive solver. Most of
+//! the guts are broken up into modules; see the comments in those modules.
+
+#![deny(warnings)]
+
+#![feature(crate_visibility_modifier)]
+#![feature(match_default_bindings)]
+#![feature(underscore_lifetimes)]
+
+#[macro_use]
+extern crate log;
+#[macro_use]
+extern crate rustc;
+extern crate rustc_data_structures;
+extern crate syntax;
+extern crate syntax_pos;
+
+mod dropck_outlives;
+mod normalize_projection_ty;
+mod normalize_erasing_regions;
+mod util;
+
+use rustc::ty::maps::Providers;
+
+pub fn provide(p: &mut Providers) {
+    *p = Providers {
+        dropck_outlives: dropck_outlives::dropck_outlives,
+        adt_dtorck_constraint: dropck_outlives::adt_dtorck_constraint,
+        normalize_projection_ty: normalize_projection_ty::normalize_projection_ty,
+        normalize_ty_after_erasing_regions:
+            normalize_erasing_regions::normalize_ty_after_erasing_regions,
+        ..*p
+    };
+}
diff --git a/src/librustc_traits/normalize_erasing_regions.rs b/src/librustc_traits/normalize_erasing_regions.rs
new file mode 100644
index 00000000000..14f8694dbf7
--- /dev/null
+++ b/src/librustc_traits/normalize_erasing_regions.rs
@@ -0,0 +1,62 @@
+// 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.
+
+use rustc::traits::{Normalized, ObligationCause};
+use rustc::traits::query::NoSolution;
+use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt};
+use rustc::util::common::CellUsizeExt;
+
+crate fn normalize_ty_after_erasing_regions<'tcx>(
+    tcx: TyCtxt<'_, 'tcx, 'tcx>,
+    goal: ParamEnvAnd<'tcx, Ty<'tcx>>,
+) -> Ty<'tcx> {
+    let ParamEnvAnd { param_env, value } = goal;
+    tcx.sess
+        .perf_stats
+        .normalize_ty_after_erasing_regions
+        .increment();
+    tcx.infer_ctxt().enter(|infcx| {
+        let cause = ObligationCause::dummy();
+        match infcx.at(&cause, param_env).normalize(&value) {
+            Ok(Normalized {
+                value: normalized_value,
+                obligations: normalized_obligations,
+            }) => {
+                // We don't care about the `obligations`; they are
+                // always only region relations, and we are about to
+                // erase those anyway:
+                debug_assert_eq!(
+                    normalized_obligations
+                        .iter()
+                        .find(|p| not_outlives_predicate(&p.predicate)),
+                    None,
+                );
+
+                let normalized_value = infcx.resolve_type_vars_if_possible(&normalized_value);
+                let normalized_value = infcx.tcx.erase_regions(&normalized_value);
+                tcx.lift_to_global(&normalized_value).unwrap()
+            }
+            Err(NoSolution) => bug!("could not fully normalize `{:?}`", value),
+        }
+    })
+}
+
+fn not_outlives_predicate(p: &ty::Predicate<'_>) -> bool {
+    match p {
+        ty::Predicate::RegionOutlives(..) | ty::Predicate::TypeOutlives(..) => false,
+        ty::Predicate::Trait(..)
+        | ty::Predicate::Projection(..)
+        | ty::Predicate::WellFormed(..)
+        | ty::Predicate::ObjectSafe(..)
+        | ty::Predicate::ClosureKind(..)
+        | ty::Predicate::Subtype(..)
+        | ty::Predicate::ConstEvaluatable(..) => true,
+    }
+}
diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs
new file mode 100644
index 00000000000..55785d9586c
--- /dev/null
+++ b/src/librustc_traits/normalize_projection_ty.rs
@@ -0,0 +1,55 @@
+// 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.
+
+use rustc::infer::canonical::{Canonical, QueryResult};
+use rustc::traits::{self, FulfillmentContext, Normalized, ObligationCause,
+                    SelectionContext};
+use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult};
+use rustc::ty::{ParamEnvAnd, TyCtxt};
+use rustc::util::common::CellUsizeExt;
+use std::rc::Rc;
+use syntax::ast::DUMMY_NODE_ID;
+use syntax_pos::DUMMY_SP;
+use util;
+
+crate fn normalize_projection_ty<'tcx>(
+    tcx: TyCtxt<'_, 'tcx, 'tcx>,
+    goal: CanonicalProjectionGoal<'tcx>,
+) -> Result<Rc<Canonical<'tcx, QueryResult<'tcx, NormalizationResult<'tcx>>>>, NoSolution> {
+    debug!("normalize_provider(goal={:#?})", goal);
+
+    tcx.sess.perf_stats.normalize_projection_ty.increment();
+    tcx.infer_ctxt().enter(|ref infcx| {
+        let (
+            ParamEnvAnd {
+                param_env,
+                value: goal,
+            },
+            canonical_inference_vars,
+        ) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &goal);
+        let fulfill_cx = &mut FulfillmentContext::new();
+        let selcx = &mut SelectionContext::new(infcx);
+        let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID);
+        let Normalized {
+            value: answer,
+            obligations,
+        } = traits::normalize_projection_type(selcx, param_env, goal, cause, 0);
+        fulfill_cx.register_predicate_obligations(infcx, obligations);
+
+        // Now that we have fulfilled as much as we can, create a solution
+        // from what we've learned.
+        util::make_query_response(
+            infcx,
+            canonical_inference_vars,
+            NormalizationResult { normalized_ty: answer },
+            fulfill_cx,
+        )
+    })
+}
diff --git a/src/librustc_traits/util.rs b/src/librustc_traits/util.rs
new file mode 100644
index 00000000000..976eb442a0d
--- /dev/null
+++ b/src/librustc_traits/util.rs
@@ -0,0 +1,117 @@
+// 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.
+
+use rustc::infer::InferCtxt;
+use rustc::infer::canonical::{CanonicalVarValues, Canonicalize, Certainty, QueryRegionConstraints,
+                              QueryResult};
+use rustc::infer::region_constraints::{Constraint, RegionConstraintData};
+use rustc::traits::FulfillmentContext;
+use rustc::traits::query::NoSolution;
+use rustc::ty;
+use std::fmt::Debug;
+
+/// The canonicalization form of `QueryResult<'tcx, T>`.
+type CanonicalizedQueryResult<'gcx, 'tcx, T> =
+    <QueryResult<'tcx, T> as Canonicalize<'gcx, 'tcx>>::Canonicalized;
+
+crate fn make_query_response<'gcx, 'tcx, T>(
+    infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+    inference_vars: CanonicalVarValues<'tcx>,
+    answer: T,
+    fulfill_cx: &mut FulfillmentContext<'tcx>,
+) -> Result<CanonicalizedQueryResult<'gcx, 'tcx, T>, NoSolution>
+where
+    T: Debug,
+    QueryResult<'tcx, T>: Canonicalize<'gcx, 'tcx>,
+{
+    let tcx = infcx.tcx;
+
+    debug!(
+        "make_query_response(\
+         inference_vars={:?}, \
+         answer={:?})",
+        inference_vars, answer,
+    );
+
+    // Select everything, returning errors.
+    let true_errors = match fulfill_cx.select_where_possible(infcx) {
+        Ok(()) => vec![],
+        Err(errors) => errors,
+    };
+    debug!("true_errors = {:#?}", true_errors);
+
+    if !true_errors.is_empty() {
+        // FIXME -- we don't indicate *why* we failed to solve
+        debug!("make_query_response: true_errors={:#?}", true_errors);
+        return Err(NoSolution);
+    }
+
+    // Anything left unselected *now* must be an ambiguity.
+    let ambig_errors = match fulfill_cx.select_all_or_error(infcx) {
+        Ok(()) => vec![],
+        Err(errors) => errors,
+    };
+    debug!("ambig_errors = {:#?}", ambig_errors);
+
+    let region_obligations = infcx.take_registered_region_obligations();
+
+    let (region_outlives, ty_outlives) = infcx.with_region_constraints(|region_constraints| {
+        let RegionConstraintData {
+            constraints,
+            verifys,
+            givens,
+        } = region_constraints;
+
+        assert!(verifys.is_empty());
+        assert!(givens.is_empty());
+
+        let region_outlives: Vec<_> = constraints
+            .into_iter()
+            .map(|(k, _)| match *k {
+                Constraint::VarSubVar(v1, v2) => {
+                    (tcx.mk_region(ty::ReVar(v1)), tcx.mk_region(ty::ReVar(v2)))
+                }
+                Constraint::VarSubReg(v1, r2) => (tcx.mk_region(ty::ReVar(v1)), r2),
+                Constraint::RegSubVar(r1, v2) => (r1, tcx.mk_region(ty::ReVar(v2))),
+                Constraint::RegSubReg(r1, r2) => (r1, r2),
+            })
+            .collect();
+
+        let ty_outlives: Vec<_> = region_obligations
+            .into_iter()
+            .map(|(_, r_o)| (r_o.sup_type, r_o.sub_region))
+            .collect();
+
+        (region_outlives, ty_outlives)
+    });
+
+    let certainty = if ambig_errors.is_empty() {
+        Certainty::Proven
+    } else {
+        Certainty::Ambiguous
+    };
+
+    let (canonical_result, _) = infcx.canonicalize_response(&QueryResult {
+        var_values: inference_vars,
+        region_constraints: QueryRegionConstraints {
+            region_outlives,
+            ty_outlives,
+        },
+        certainty,
+        value: answer,
+    });
+
+    debug!(
+        "make_query_response: canonical_result = {:#?}",
+        canonical_result
+    );
+
+    Ok(canonical_result)
+}
diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs
index ee0f2415bd8..c1dc8c6684a 100644
--- a/src/librustc_trans/abi.rs
+++ b/src/librustc_trans/abi.rs
@@ -650,7 +650,7 @@ impl<'a, 'tcx> FnType<'tcx> {
                        -> Self {
         let fn_ty = instance.ty(cx.tcx);
         let sig = ty_fn_sig(cx, fn_ty);
-        let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig);
+        let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
         FnType::new(cx, sig, &[])
     }
 
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index 76e05ae7dcb..4da082e9d50 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -462,7 +462,7 @@ pub fn trans_instance<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, instance: Instance<'tc
 
     let fn_ty = instance.ty(cx.tcx);
     let sig = common::ty_fn_sig(cx, fn_ty);
-    let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig);
+    let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
 
     let lldecl = match cx.instances.borrow().get(&instance) {
         Some(&val) => val,
diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs
index 54cc561e804..1dcf349e23b 100644
--- a/src/librustc_trans/callee.rs
+++ b/src/librustc_trans/callee.rs
@@ -25,7 +25,6 @@ use type_of::LayoutLlvmExt;
 use rustc::hir::def_id::DefId;
 use rustc::ty::{self, TypeFoldable};
 use rustc::ty::layout::LayoutOf;
-use rustc::traits;
 use rustc::ty::subst::Substs;
 use rustc_back::PanicStrategy;
 
@@ -185,7 +184,7 @@ pub fn resolve_and_get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
         cx,
         ty::Instance::resolve(
             cx.tcx,
-            ty::ParamEnv::empty(traits::Reveal::All),
+            ty::ParamEnv::reveal_all(),
             def_id,
             substs
         ).unwrap()
diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs
index 7c4e2340d5b..c71c0cc0ebf 100644
--- a/src/librustc_trans/common.rs
+++ b/src/librustc_trans/common.rs
@@ -25,7 +25,6 @@ use declare;
 use type_::Type;
 use type_of::LayoutLlvmExt;
 use value::Value;
-use rustc::traits;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::layout::{HasDataLayout, LayoutOf};
 use rustc::hir;
@@ -40,15 +39,15 @@ use syntax_pos::{Span, DUMMY_SP};
 pub use context::CodegenCx;
 
 pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool {
-    ty.needs_drop(tcx, ty::ParamEnv::empty(traits::Reveal::All))
+    ty.needs_drop(tcx, ty::ParamEnv::reveal_all())
 }
 
 pub fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool {
-    ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::empty(traits::Reveal::All))
+    ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all())
 }
 
 pub fn type_is_freeze<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool {
-    ty.is_freeze(tcx, ty::ParamEnv::empty(traits::Reveal::All), DUMMY_SP)
+    ty.is_freeze(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP)
 }
 
 /*
diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs
index dc5e7889594..1f2c3cc883c 100644
--- a/src/librustc_trans/context.rs
+++ b/src/librustc_trans/context.rs
@@ -14,7 +14,6 @@ use llvm::{ContextRef, ModuleRef, ValueRef};
 use rustc::dep_graph::DepGraphSafe;
 use rustc::hir;
 use rustc::hir::def_id::DefId;
-use rustc::traits;
 use debuginfo;
 use callee;
 use base;
@@ -435,7 +434,7 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> {
 
     pub fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool {
         use syntax_pos::DUMMY_SP;
-        if ty.is_sized(self.tcx.at(DUMMY_SP), ty::ParamEnv::empty(traits::Reveal::All)) {
+        if ty.is_sized(self.tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) {
             return false;
         }
 
@@ -464,7 +463,7 @@ impl<'a, 'tcx> LayoutOf<Ty<'tcx>> for &'a CodegenCx<'a, 'tcx> {
     type TyLayout = TyLayout<'tcx>;
 
     fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
-        self.tcx.layout_of(ty::ParamEnv::empty(traits::Reveal::All).and(ty))
+        self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty))
             .unwrap_or_else(|e| match e {
                 LayoutError::SizeOverflow(_) => self.sess().fatal(&e.to_string()),
                 _ => bug!("failed to get layout for `{}`: {}", ty, e)
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index d20b51ca0fd..20cc57522b5 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -30,7 +30,7 @@ use rustc::ty::util::TypeIdHasher;
 use rustc::ich::Fingerprint;
 use rustc::ty::Instance;
 use common::CodegenCx;
-use rustc::ty::{self, AdtKind, Ty, TyCtxt};
+use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
 use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout};
 use rustc::session::config;
 use rustc::util::nodemap::FxHashMap;
@@ -353,7 +353,10 @@ fn subroutine_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
                                       span: Span)
                                       -> MetadataCreationResult
 {
-    let signature = cx.tcx.erase_late_bound_regions_and_normalize(&signature);
+    let signature = cx.tcx.normalize_erasing_late_bound_regions(
+        ty::ParamEnv::reveal_all(),
+        &signature,
+    );
 
     let mut signature_metadata: Vec<DIType> = Vec::with_capacity(signature.inputs().len() + 1);
 
@@ -589,7 +592,7 @@ pub fn type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
         }
         ty::TyGenerator(def_id, substs, _) => {
             let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx).map(|t| {
-                cx.tcx.fully_normalize_associated_types_in(&t)
+                cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)
             }).collect();
             prepare_tuple_metadata(cx,
                                    t,
diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs
index 16279f31836..c13b91eb3b6 100644
--- a/src/librustc_trans/debuginfo/mod.rs
+++ b/src/librustc_trans/debuginfo/mod.rs
@@ -30,7 +30,7 @@ use abi::Abi;
 use common::CodegenCx;
 use builder::Builder;
 use monomorphize::Instance;
-use rustc::ty::{self, Ty};
+use rustc::ty::{self, ParamEnv, Ty};
 use rustc::mir;
 use rustc::session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo};
 use rustc::util::nodemap::{DefIdMap, FxHashMap, FxHashSet};
@@ -378,7 +378,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
                 name_to_append_suffix_to.push_str(",");
             }
 
-            let actual_type = cx.tcx.fully_normalize_associated_types_in(&actual_type);
+            let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), actual_type);
             // Add actual type name to <...> clause of function name
             let actual_type_name = compute_debuginfo_type_name(cx,
                                                                actual_type,
@@ -391,7 +391,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
         let template_params: Vec<_> = if cx.sess().opts.debuginfo == FullDebugInfo {
             let names = get_type_parameter_names(cx, generics);
             substs.types().zip(names).map(|(ty, name)| {
-                let actual_type = cx.tcx.fully_normalize_associated_types_in(&ty);
+                let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
                 let actual_type_metadata = type_metadata(cx, actual_type, syntax_pos::DUMMY_SP);
                 let name = CString::new(name.as_str().as_bytes()).unwrap();
                 unsafe {
@@ -429,7 +429,11 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
         let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| {
             // If the method does *not* belong to a trait, proceed
             if cx.tcx.trait_id_of_impl(impl_def_id).is_none() {
-                let impl_self_ty = cx.tcx.trans_impl_self_ty(impl_def_id, instance.substs);
+                let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions(
+                    instance.substs,
+                    ty::ParamEnv::reveal_all(),
+                    &cx.tcx.type_of(impl_def_id),
+                );
 
                 // Only "class" methods are generally understood by LLVM,
                 // so avoid methods on other types (e.g. `<*mut T>::null`).
diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs
index a88eb9ae354..211de95c96e 100644
--- a/src/librustc_trans/debuginfo/type_names.rs
+++ b/src/librustc_trans/debuginfo/type_names.rs
@@ -117,8 +117,10 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
         },
         ty::TyDynamic(ref trait_data, ..) => {
             if let Some(principal) = trait_data.principal() {
-                let principal = cx.tcx.erase_late_bound_regions_and_normalize(
-                    &principal);
+                let principal = cx.tcx.normalize_erasing_late_bound_regions(
+                    ty::ParamEnv::reveal_all(),
+                    &principal,
+                );
                 push_item_name(cx, principal.def_id, false, output);
                 push_type_params(cx, principal.substs, output);
             }
@@ -138,7 +140,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
 
             output.push_str("fn(");
 
-            let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig);
+            let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
             if !sig.inputs().is_empty() {
                 for &parameter_type in sig.inputs() {
                     push_debuginfo_type_name(cx, parameter_type, true, output);
diff --git a/src/librustc_trans/declare.rs b/src/librustc_trans/declare.rs
index aa1cd0c27e7..c2010feb1b6 100644
--- a/src/librustc_trans/declare.rs
+++ b/src/librustc_trans/declare.rs
@@ -22,7 +22,7 @@
 
 use llvm::{self, ValueRef};
 use llvm::AttributePlace::Function;
-use rustc::ty::Ty;
+use rustc::ty::{self, Ty};
 use rustc::session::config::Sanitizer;
 use rustc_back::PanicStrategy;
 use abi::{Abi, FnType};
@@ -127,7 +127,7 @@ pub fn declare_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, name: &str,
                             fn_type: Ty<'tcx>) -> ValueRef {
     debug!("declare_rust_fn(name={:?}, fn_type={:?})", name, fn_type);
     let sig = common::ty_fn_sig(cx, fn_type);
-    let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig);
+    let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
     debug!("declare_rust_fn (after region erasure) sig={:?}", sig);
 
     let fty = FnType::new(cx, sig, &[]);
diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs
index 3f87ce7e047..4cef7470c62 100644
--- a/src/librustc_trans/intrinsic.rs
+++ b/src/librustc_trans/intrinsic.rs
@@ -100,7 +100,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>,
     };
 
     let sig = callee_ty.fn_sig(tcx);
-    let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
+    let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
     let arg_tys = sig.inputs();
     let ret_ty = sig.output();
     let name = &*tcx.item_name(def_id);
@@ -1035,7 +1035,10 @@ fn generic_simd_intrinsic<'a, 'tcx>(
 
 
     let tcx = bx.tcx();
-    let sig = tcx.erase_late_bound_regions_and_normalize(&callee_ty.fn_sig(tcx));
+    let sig = tcx.normalize_erasing_late_bound_regions(
+        ty::ParamEnv::reveal_all(),
+        &callee_ty.fn_sig(tcx),
+    );
     let arg_tys = sig.inputs();
 
     // every intrinsic takes a SIMD vector as its first argument
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index efb5338f680..96c5bb3b91d 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -12,7 +12,6 @@ use llvm::{self, ValueRef, BasicBlockRef};
 use rustc::middle::lang_items;
 use rustc::ty::{self, TypeFoldable};
 use rustc::ty::layout::{self, LayoutOf};
-use rustc::traits;
 use rustc::mir;
 use abi::{Abi, FnType, ArgType, PassMode};
 use base;
@@ -282,7 +281,10 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
                     ty::TyDynamic(..) => {
                         let fn_ty = drop_fn.ty(bx.cx.tcx);
                         let sig = common::ty_fn_sig(bx.cx, fn_ty);
-                        let sig = bx.tcx().erase_late_bound_regions_and_normalize(&sig);
+                        let sig = bx.tcx().normalize_erasing_late_bound_regions(
+                            ty::ParamEnv::reveal_all(),
+                            &sig,
+                        );
                         let fn_ty = FnType::new_vtable(bx.cx, sig, &[]);
                         args = &args[..1];
                         (meth::DESTRUCTOR.get_fn(&bx, place.llextra, &fn_ty), fn_ty)
@@ -419,7 +421,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
                 let (instance, mut llfn) = match callee.layout.ty.sty {
                     ty::TyFnDef(def_id, substs) => {
                         (Some(ty::Instance::resolve(bx.cx.tcx,
-                                                    ty::ParamEnv::empty(traits::Reveal::All),
+                                                    ty::ParamEnv::reveal_all(),
                                                     def_id,
                                                     substs).unwrap()),
                          None)
@@ -431,7 +433,10 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
                 };
                 let def = instance.map(|i| i.def);
                 let sig = callee.layout.ty.fn_sig(bx.tcx());
-                let sig = bx.tcx().erase_late_bound_regions_and_normalize(&sig);
+                let sig = bx.tcx().normalize_erasing_late_bound_regions(
+                    ty::ParamEnv::reveal_all(),
+                    &sig,
+                );
                 let abi = sig.abi;
 
                 // Handle intrinsics old trans wants Expr's for, ourselves.
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index 6aa8b7e5449..977c7c983d6 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -12,7 +12,6 @@ use llvm::{self, ValueRef};
 use rustc::middle::const_val::{ConstVal, ConstEvalErr};
 use rustc_mir::interpret::{read_target_uint, const_val_field};
 use rustc::hir::def_id::DefId;
-use rustc::traits;
 use rustc::mir;
 use rustc_data_structures::indexed_vec::Idx;
 use rustc::mir::interpret::{Allocation, GlobalId, MemoryPointer, PrimVal, Value as MiriValue};
@@ -126,7 +125,7 @@ pub fn trans_static_initializer<'a, 'tcx>(
         instance,
         promoted: None
     };
-    let param_env = ty::ParamEnv::empty(traits::Reveal::All);
+    let param_env = ty::ParamEnv::reveal_all();
     cx.tcx.const_eval(param_env.and(cid))?;
 
     let alloc_id = cx
@@ -152,7 +151,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
         match constant.val {
             ConstVal::Unevaluated(def_id, ref substs) => {
                 let tcx = bx.tcx();
-                let param_env = ty::ParamEnv::empty(traits::Reveal::All);
+                let param_env = ty::ParamEnv::reveal_all();
                 let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap();
                 let cid = GlobalId {
                     instance,
@@ -172,7 +171,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
     ) -> Result<MiriValue, ConstEvalErr<'tcx>> {
         match constant.literal {
             mir::Literal::Promoted { index } => {
-                let param_env = ty::ParamEnv::empty(traits::Reveal::All);
+                let param_env = ty::ParamEnv::reveal_all();
                 let cid = mir::interpret::GlobalId {
                     instance: self.instance,
                     promoted: Some(index),
@@ -201,7 +200,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
                 let values: Result<Vec<ValueRef>, _> = (0..fields).map(|field| {
                     let field = const_val_field(
                         bx.tcx(),
-                        ty::ParamEnv::empty(traits::Reveal::All),
+                        ty::ParamEnv::reveal_all(),
                         self.instance,
                         None,
                         mir::Field::new(field as usize),
diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs
index a1044ac87e4..0533b04a0c1 100644
--- a/src/librustc_trans/mir/mod.rs
+++ b/src/librustc_trans/mir/mod.rs
@@ -16,7 +16,6 @@ use rustc::ty::{self, TypeFoldable};
 use rustc::ty::layout::{LayoutOf, TyLayout};
 use rustc::mir::{self, Mir};
 use rustc::ty::subst::Substs;
-use rustc::infer::TransNormalize;
 use rustc::session::config::FullDebugInfo;
 use base;
 use builder::Builder;
@@ -108,9 +107,13 @@ pub struct FunctionCx<'a, 'tcx:'a> {
 
 impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
     pub fn monomorphize<T>(&self, value: &T) -> T
-        where T: TransNormalize<'tcx>
+        where T: TypeFoldable<'tcx>
     {
-        self.cx.tcx.trans_apply_param_substs(self.param_substs, value)
+        self.cx.tcx.subst_and_normalize_erasing_regions(
+            self.param_substs,
+            ty::ParamEnv::reveal_all(),
+            value,
+        )
     }
 
     pub fn set_debug_loc(&mut self, bx: &Builder, source_info: mir::SourceInfo) {
diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs
index af957500f70..f37114ee4ac 100644
--- a/src/librustc_trans/type_of.rs
+++ b/src/librustc_trans/type_of.rs
@@ -258,7 +258,10 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
                     cx.layout_of(self.ty.boxed_ty()).llvm_type(cx).ptr_to()
                 }
                 ty::TyFnPtr(sig) => {
-                    let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig);
+                    let sig = cx.tcx.normalize_erasing_late_bound_regions(
+                        ty::ParamEnv::reveal_all(),
+                        &sig,
+                    );
                     FnType::new(cx, sig, &[]).llvm_type(cx).ptr_to()
                 }
                 _ => self.scalar_llvm_type_at(cx, scalar, Size::from_bytes(0))
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index a3a1f2a9307..b6459b62410 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -501,7 +501,7 @@ fn compare_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             ty::TraitContainer(_) => tcx.mk_self_type()
         };
         let self_arg_ty = *tcx.fn_sig(method.def_id).input(0).skip_binder();
-        let param_env = ty::ParamEnv::empty(Reveal::All);
+        let param_env = ty::ParamEnv::reveal_all();
 
         tcx.infer_ctxt().enter(|infcx| {
             let self_arg_ty = tcx.liberate_late_bound_regions(
@@ -759,7 +759,7 @@ pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
 
     tcx.infer_ctxt().enter(|infcx| {
-        let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
+        let param_env = ty::ParamEnv::empty();
         let inh = Inherited::new(infcx, impl_c.def_id);
         let infcx = &inh.infcx;
 
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 165cfe6604e..67c9832cbf9 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -16,10 +16,10 @@ use rustc::infer::outlives::env::OutlivesEnvironment;
 use rustc::middle::region;
 use rustc::ty::subst::{Subst, Substs, UnpackedKind};
 use rustc::ty::{self, Ty, TyCtxt};
-use rustc::traits::{self, Reveal, ObligationCause};
+use rustc::traits::{self, ObligationCause};
 use util::common::ErrorReported;
-use util::nodemap::FxHashSet;
 
+use syntax::ast;
 use syntax_pos::Span;
 
 /// check_drop_impl confirms that the Drop implementation identified by
@@ -126,7 +126,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
         // it did the wrong thing, so I chose to preserve existing
         // behavior, since it ought to be simply more
         // conservative. -nmatsakis
-        let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty(Reveal::UserFacing));
+        let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty());
 
         infcx.resolve_regions_and_report_errors(drop_impl_did, &region_scope_tree, &outlives_env);
         Ok(())
@@ -282,6 +282,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>(
     rcx: &mut RegionCtxt<'a, 'gcx, 'tcx>,
     ty: Ty<'tcx>,
     span: Span,
+    body_id: ast::NodeId,
     scope: region::Scope)
     -> Result<(), ErrorReported>
 {
@@ -297,46 +298,15 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>(
     };
     let parent_scope = rcx.tcx.mk_region(ty::ReScope(parent_scope));
     let origin = || infer::SubregionOrigin::SafeDestructor(span);
-
-    let ty = rcx.fcx.resolve_type_vars_if_possible(&ty);
-    let for_ty = ty;
-    let mut types = vec![(ty, 0)];
-    let mut known = FxHashSet();
-    while let Some((ty, depth)) = types.pop() {
-        let ty::DtorckConstraint {
-            dtorck_types, outlives
-        } = rcx.tcx.dtorck_constraint_for_ty(span, for_ty, depth, ty)?;
-
-        for ty in dtorck_types {
-            let ty = rcx.fcx.normalize_associated_types_in(span, &ty);
-            let ty = rcx.fcx.resolve_type_vars_with_obligations(ty);
-            let ty = rcx.fcx.resolve_type_and_region_vars_if_possible(&ty);
-            match ty.sty {
-                // All parameters live for the duration of the
-                // function.
-                ty::TyParam(..) => {}
-
-                // A projection that we couldn't resolve - it
-                // might have a destructor.
-                ty::TyProjection(..) | ty::TyAnon(..) => {
-                    rcx.type_must_outlive(origin(), ty, parent_scope);
-                }
-
-                _ => {
-                    if let None = known.replace(ty) {
-                        types.push((ty, depth+1));
-                    }
-                }
-            }
-        }
-
-        for outlive in outlives {
-            match outlive.unpack() {
-                UnpackedKind::Lifetime(lt) => rcx.sub_regions(origin(), parent_scope, lt),
-                UnpackedKind::Type(ty) => rcx.type_must_outlive(origin(), ty, parent_scope),
-            }
+    let cause = &ObligationCause::misc(span, body_id);
+    let infer_ok = rcx.infcx.at(cause, rcx.fcx.param_env).dropck_outlives(ty);
+    debug!("dropck_outlives = {:#?}", infer_ok);
+    let kinds = rcx.fcx.register_infer_ok_obligations(infer_ok);
+    for kind in kinds {
+        match kind.unpack() {
+            UnpackedKind::Lifetime(r) => rcx.sub_regions(origin(), parent_scope, r),
+            UnpackedKind::Type(ty) => rcx.type_must_outlive(origin(), ty, parent_scope),
         }
     }
-
     Ok(())
 }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 1ea1ff1fae2..48591998a1f 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3997,7 +3997,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
           }
           hir::ExprRepeat(ref element, count) => {
             let count_def_id = tcx.hir.body_owner_def_id(count);
-            let param_env = ty::ParamEnv::empty(traits::Reveal::UserFacing);
+            let param_env = ty::ParamEnv::empty();
             let substs = Substs::identity_for_item(tcx.global_tcx(), count_def_id);
             let instance = ty::Instance::resolve(
                 tcx.global_tcx(),
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index cfe8aa99bfa..9ed4ab45a1b 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -411,8 +411,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
             self.type_of_node_must_outlive(origin, hir_id, var_region);
 
             let typ = self.resolve_node_type(hir_id);
+            let body_id = self.body_id;
             let _ = dropck::check_safety_of_destructor_if_necessary(
-                self, typ, span, var_scope);
+                self, typ, span, body_id, var_scope);
         })
     }
 }
@@ -884,8 +885,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
                 match *region {
                     ty::ReScope(rvalue_scope) => {
                         let typ = self.resolve_type(cmt.ty);
+                        let body_id = self.body_id;
                         let _ = dropck::check_safety_of_destructor_if_necessary(
-                            self, typ, span, rvalue_scope);
+                            self, typ, span, body_id, rvalue_scope);
                     }
                     ty::ReStatic => {}
                     _ => {
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 49a23f14338..ea90c35cb8f 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -110,7 +110,7 @@ use rustc::infer::InferOk;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::maps::Providers;
-use rustc::traits::{FulfillmentContext, ObligationCause, ObligationCauseCode, Reveal};
+use rustc::traits::{FulfillmentContext, ObligationCause, ObligationCauseCode};
 use session::{CompileIncomplete, config};
 use util::common::time;
 
@@ -158,7 +158,7 @@ fn require_same_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                 actual: Ty<'tcx>)
                                 -> bool {
     tcx.infer_ctxt().enter(|ref infcx| {
-        let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
+        let param_env = ty::ParamEnv::empty();
         let mut fulfill_cx = FulfillmentContext::new();
         match infcx.at(&cause, param_env).eq(expected, actual) {
             Ok(InferOk { obligations, .. }) => {
diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs
index 44ac7a10e82..2a4e92034de 100644
--- a/src/librustc_typeck/variance/constraints.rs
+++ b/src/librustc_typeck/variance/constraints.rs
@@ -423,6 +423,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
                 // way early-bound regions do, so we skip them here.
             }
 
+            ty::ReCanonical(_) |
             ty::ReFree(..) |
             ty::ReClosureBound(..) |
             ty::ReScope(..) |
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 5d4addce2c4..ff281a53ab7 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1490,6 +1490,7 @@ impl Clean<Option<Lifetime>> for ty::RegionKind {
             ty::ReSkolemized(..) |
             ty::ReEmpty |
             ty::ReClosureBound(_) |
+            ty::ReCanonical(_) |
             ty::ReErased => None
         }
     }
diff --git a/src/test/ui/issue-48132.rs b/src/test/ui/issue-48132.rs
new file mode 100644
index 00000000000..87fee986de7
--- /dev/null
+++ b/src/test/ui/issue-48132.rs
@@ -0,0 +1,42 @@
+// Copyright 2015 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.
+
+// Regression test for #48132. This was failing due to problems around
+// the projection caching and dropck type enumeration.
+
+// run-pass
+
+#![feature(nll)]
+#![allow(warnings)]
+
+struct Inner<I, V> {
+    iterator: I,
+    item: V,
+}
+
+struct Outer<I: Iterator> {
+    inner: Inner<I, I::Item>,
+}
+
+fn outer<I>(iterator: I) -> Outer<I>
+where I: Iterator,
+      I::Item: Default,
+{
+    Outer {
+        inner: Inner {
+            iterator: iterator,
+            item: Default::default(),
+        }
+    }
+}
+
+fn main() {
+    outer(std::iter::once(&1).cloned());
+}
diff --git a/src/test/ui/issue-48179.rs b/src/test/ui/issue-48179.rs
new file mode 100644
index 00000000000..745b59e0658
--- /dev/null
+++ b/src/test/ui/issue-48179.rs
@@ -0,0 +1,51 @@
+// Copyright 2015 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.
+
+// Regression test for #48132. This was failing due to problems around
+// the projection caching and dropck type enumeration.
+
+// run-pass
+
+#![feature(nll)]
+#![allow(warnings)]
+
+pub struct Container<T: Iterator> {
+    value: Option<T::Item>,
+}
+
+impl<T: Iterator> Container<T> {
+    pub fn new(iter: T) -> Self {
+        panic!()
+    }
+}
+
+pub struct Wrapper<'a> {
+    content: &'a Content,
+}
+
+impl<'a, 'de> Wrapper<'a> {
+    pub fn new(content: &'a Content) -> Self {
+        Wrapper {
+            content: content,
+        }
+    }
+}
+
+pub struct Content;
+
+fn crash_it(content: Content) {
+    let items = vec![content];
+    let map = items.iter().map(|ref o| Wrapper::new(o));
+
+    let mut map_visitor = Container::new(map);
+
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/issue-31567.rs b/src/test/ui/nll/issue-31567.rs
new file mode 100644
index 00000000000..a0d1faf1f0e
--- /dev/null
+++ b/src/test/ui/nll/issue-31567.rs
@@ -0,0 +1,37 @@
+// Copyright 2017 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.
+
+// Regression test for #31567: cached results of projections were
+// causing region relations not to be enforced at all the places where
+// they have to be enforced.
+
+#![feature(nll)]
+
+struct VecWrapper<'a>(&'a mut S);
+
+struct S(Box<u32>);
+
+fn get_dangling<'a>(v: VecWrapper<'a>) -> &'a u32 {
+    let s_inner: &'a S = &*v.0; //~ ERROR `*v.0` does not live long enough
+    &s_inner.0
+}
+
+impl<'a> Drop for VecWrapper<'a> {
+    fn drop(&mut self) {
+        *self.0 = S(Box::new(0));
+    }
+}
+
+fn main() {
+    let mut s = S(Box::new(11));
+    let vw = VecWrapper(&mut s);
+    let dangling = get_dangling(vw);
+    println!("{}", dangling);
+}
diff --git a/src/test/ui/nll/issue-31567.stderr b/src/test/ui/nll/issue-31567.stderr
new file mode 100644
index 00000000000..e0ff653e2b4
--- /dev/null
+++ b/src/test/ui/nll/issue-31567.stderr
@@ -0,0 +1,18 @@
+error[E0597]: `*v.0` does not live long enough
+  --> $DIR/issue-31567.rs:22:26
+   |
+LL |     let s_inner: &'a S = &*v.0; //~ ERROR `*v.0` does not live long enough
+   |                          ^^^^^ borrowed value does not live long enough
+LL |     &s_inner.0
+LL | }
+   | - borrowed value only lives until here
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the function body at 21:1...
+  --> $DIR/issue-31567.rs:21:1
+   |
+LL | fn get_dangling<'a>(v: VecWrapper<'a>) -> &'a u32 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+If you want more information on this error, try using "rustc --explain E0597"
diff --git a/src/test/ui/nll/issue-47470.rs b/src/test/ui/nll/issue-47470.rs
new file mode 100644
index 00000000000..c962f193cd5
--- /dev/null
+++ b/src/test/ui/nll/issue-47470.rs
@@ -0,0 +1,34 @@
+// Copyright 2017 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.
+
+// Regression test for #47470: cached results of projections were
+// causing region relations not to be enforced at all the places where
+// they have to be enforced.
+
+#![feature(nll)]
+
+struct Foo<'a>(&'a ());
+trait Bar {
+    type Assoc;
+    fn get(self) -> Self::Assoc;
+}
+
+impl<'a> Bar for Foo<'a> {
+    type Assoc = &'a u32;
+    fn get(self) -> Self::Assoc {
+        let local = 42;
+        &local //~ ERROR `local` does not live long enough
+    }
+}
+
+fn main() {
+    let f = Foo(&()).get();
+    println!("{}", f);
+}
diff --git a/src/test/ui/nll/issue-47470.stderr b/src/test/ui/nll/issue-47470.stderr
new file mode 100644
index 00000000000..1356461a6e4
--- /dev/null
+++ b/src/test/ui/nll/issue-47470.stderr
@@ -0,0 +1,17 @@
+error[E0597]: `local` does not live long enough
+  --> $DIR/issue-47470.rs:27:9
+   |
+LL |         &local //~ ERROR `local` does not live long enough
+   |         ^^^^^^ borrowed value does not live long enough
+LL |     }
+   |     - borrowed value only lives until here
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the impl at 23:1...
+  --> $DIR/issue-47470.rs:23:1
+   |
+LL | impl<'a> Bar for Foo<'a> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+If you want more information on this error, try using "rustc --explain E0597"