diff --git a/src/librustc/dep_graph/README.md b/src/librustc/dep_graph/README.md
index 21742d9935dc2..ece5819829baa 100644
--- a/src/librustc/dep_graph/README.md
+++ b/src/librustc/dep_graph/README.md
@@ -51,7 +51,7 @@ could invalidate work done for other items. So, for example:
    not shared state, because if it changes it does not itself
    invalidate other functions (though it may be that it causes new
    monomorphizations to occur, but that's handled independently).
-   
+
 Put another way: if the HIR for an item changes, we are going to
 recompile that item for sure. But we need the dep tracking map to tell
 us what *else* we have to recompile. Shared state is anything that is
@@ -177,7 +177,7 @@ reads from `item`, there would be missing edges in the graph:
       |                                 ^
       |                                 |
       +---------------------------------+ // added by `visit_all_items_in_krate`
-    
+
 In particular, the edge from `Hir(X)` to `ItemSignature(X)` is only
 present because we called `read` ourselves when entering the `ItemSignature(X)`
 task.
@@ -273,8 +273,8 @@ should not exist.  In contrast, using the memoized helper, you get:
     ... -> MapVariant(key) -> A
                  |
                  +----------> B
-                 
-which is much cleaner.                 
+
+which is much cleaner.
 
 **Be aware though that the closure is executed with `MapVariant(key)`
 pushed onto the stack as the current task!** That means that you must
@@ -387,4 +387,3 @@ RUST_DEP_GRAPH_FILTER='Hir&foo -> TypeckItemBody & bar'
 This will dump out all the nodes that lead from `Hir(foo)` to
 `TypeckItemBody(bar)`, from which you can (hopefully) see the source
 of the erroneous edge.
-
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 51985be96db53..89b57e0d90a00 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -25,6 +25,7 @@ use middle::expr_use_visitor as euv;
 use middle::infer;
 use middle::mem_categorization::{cmt};
 use middle::pat_util::*;
+use middle::traits::ProjectionMode;
 use middle::ty::*;
 use middle::ty;
 use std::cmp::Ordering;
@@ -1101,7 +1102,8 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
                         //FIXME: (@jroesch) this code should be floated up as well
                         let infcx = infer::new_infer_ctxt(cx.tcx,
                                                           &cx.tcx.tables,
-                                                          Some(cx.param_env.clone()));
+                                                          Some(cx.param_env.clone()),
+                                                          ProjectionMode::AnyFinal);
                         if infcx.type_moves_by_default(pat_ty, pat.span) {
                             check_move(p, sub.as_ref().map(|p| &**p));
                         }
@@ -1133,7 +1135,8 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>,
 
     let infcx = infer::new_infer_ctxt(cx.tcx,
                                       &cx.tcx.tables,
-                                      Some(checker.cx.param_env.clone()));
+                                      Some(checker.cx.param_env.clone()),
+                                      ProjectionMode::AnyFinal);
 
     let mut visitor = ExprUseVisitor::new(&mut checker, &infcx);
     visitor.walk_expr(guard);
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index d97df585edc05..8a1a0080eb0c2 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -24,6 +24,7 @@ use middle::def_id::DefId;
 use middle::pat_util::def_to_path;
 use middle::ty::{self, Ty, TyCtxt};
 use middle::ty::util::IntTypeExt;
+use middle::traits::ProjectionMode;
 use middle::astconv_util::ast_ty_to_prim_ty;
 use util::nodemap::NodeMap;
 
@@ -1049,7 +1050,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
            trait_ref);
 
     tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id());
-    let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
+    let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal);
 
     let mut selcx = traits::SelectionContext::new(&infcx);
     let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
@@ -1067,6 +1068,11 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>,
         }
     };
 
+    // NOTE: this code does not currently account for specialization, but when
+    // it does so, it should hook into the ProjectionMode to determine when the
+    // constant should resolve; this will also require plumbing through to this
+    // function whether we are in "trans mode" to pick the right ProjectionMode
+    // when constructing the inference context above.
     match selection {
         traits::VtableImpl(ref impl_data) => {
             match tcx.associated_consts(impl_data.impl_def_id)
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 193492ee7e10c..6a4680ecbaf31 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -176,6 +176,7 @@ pub trait CrateStore<'tcx> : Any {
                                   -> Option<ty::adjustment::CustomCoerceUnsized>;
     fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId)
                          -> Vec<Rc<ty::AssociatedConst<'tcx>>>;
+    fn impl_parent(&self, impl_def_id: DefId) -> Option<DefId>;
 
     // trait/impl-item info
     fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)
@@ -346,6 +347,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
         { unimplemented!() }
     fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId)
                          -> Vec<Rc<ty::AssociatedConst<'tcx>>> { unimplemented!() }
+    fn impl_parent(&self, def: DefId) -> Option<DefId> { unimplemented!() }
 
     // trait/impl-item info
     fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index e671dd73431e6..4ff1de422117b 100644
--- a/src/librustc/middle/infer/mod.rs
+++ b/src/librustc/middle/infer/mod.rs
@@ -27,7 +27,7 @@ use middle::region::CodeExtent;
 use middle::subst;
 use middle::subst::Substs;
 use middle::subst::Subst;
-use middle::traits;
+use middle::traits::{self, ProjectionMode};
 use middle::ty::adjustment;
 use middle::ty::{TyVid, IntVid, FloatVid};
 use middle::ty::{self, Ty, TyCtxt};
@@ -99,6 +99,11 @@ pub struct InferCtxt<'a, 'tcx: 'a> {
     // directly.
     normalize: bool,
 
+    // Sadly, the behavior of projection varies a bit depending on the
+    // stage of compilation. The specifics are given in the
+    // documentation for `ProjectionMode`.
+    projection_mode: ProjectionMode,
+
     err_count_on_creation: usize,
 }
 
@@ -354,7 +359,8 @@ pub fn fixup_err_to_string(f: FixupError) -> String {
 
 pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
                                 tables: &'a RefCell<ty::Tables<'tcx>>,
-                                param_env: Option<ty::ParameterEnvironment<'a, 'tcx>>)
+                                param_env: Option<ty::ParameterEnvironment<'a, 'tcx>>,
+                                projection_mode: ProjectionMode)
                                 -> InferCtxt<'a, 'tcx> {
     InferCtxt {
         tcx: tcx,
@@ -366,14 +372,16 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
         parameter_environment: param_env.unwrap_or(tcx.empty_parameter_environment()),
         reported_trait_errors: RefCell::new(FnvHashSet()),
         normalize: false,
+        projection_mode: projection_mode,
         err_count_on_creation: tcx.sess.err_count()
     }
 }
 
 pub fn normalizing_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>,
-                                        tables: &'a RefCell<ty::Tables<'tcx>>)
+                                        tables: &'a RefCell<ty::Tables<'tcx>>,
+                                        projection_mode: ProjectionMode)
                                         -> InferCtxt<'a, 'tcx> {
-    let mut infcx = new_infer_ctxt(tcx, tables, None);
+    let mut infcx = new_infer_ctxt(tcx, tables, None, projection_mode);
     infcx.normalize = true;
     infcx
 }
@@ -514,6 +522,7 @@ pub struct CombinedSnapshot {
     region_vars_snapshot: RegionSnapshot,
 }
 
+// NOTE: Callable from trans only!
 pub fn normalize_associated_type<'tcx,T>(tcx: &TyCtxt<'tcx>, value: &T) -> T
     where T : TypeFoldable<'tcx>
 {
@@ -525,7 +534,7 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &TyCtxt<'tcx>, value: &T) -> T
         return value;
     }
 
-    let infcx = new_infer_ctxt(tcx, &tcx.tables, None);
+    let infcx = new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Any);
     let mut selcx = traits::SelectionContext::new(&infcx);
     let cause = traits::ObligationCause::dummy();
     let traits::Normalized { value: result, obligations } =
@@ -593,6 +602,10 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
 }
 
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+    pub fn projection_mode(&self) -> ProjectionMode {
+        self.projection_mode
+    }
+
     pub fn freshen<T:TypeFoldable<'tcx>>(&self, t: T) -> T {
         t.fold_with(&mut self.freshener())
     }
@@ -1025,8 +1038,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                               substs: &mut Substs<'tcx>,
                               defs: &[ty::TypeParameterDef<'tcx>]) {
 
-        let mut vars = Vec::with_capacity(defs.len());
-
         for def in defs.iter() {
             let default = def.default.map(|default| {
                 type_variable::Default {
@@ -1038,7 +1049,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
             let ty_var = self.next_ty_var_with_default(default);
             substs.types.push(space, ty_var);
-            vars.push(ty_var)
         }
     }
 
diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs
index 02dfeb80b928f..510a3dd454b95 100644
--- a/src/librustc/middle/subst.rs
+++ b/src/librustc/middle/subst.rs
@@ -160,6 +160,15 @@ impl<'tcx> Substs<'tcx> {
         Substs { types: types, regions: regions }
     }
 
+    pub fn with_method_from_subst(self, other: &Substs<'tcx>) -> Substs<'tcx> {
+        let Substs { types, regions } = self;
+        let types = types.with_slice(FnSpace, other.types.get_slice(FnSpace));
+        let regions = regions.map(|r| {
+            r.with_slice(FnSpace, other.regions().get_slice(FnSpace))
+        });
+        Substs { types: types, regions: regions }
+    }
+
     /// Creates a trait-ref out of this substs, ignoring the FnSpace substs
     pub fn to_trait_ref(&self, tcx: &TyCtxt<'tcx>, trait_id: DefId)
                         -> ty::TraitRef<'tcx> {
diff --git a/src/librustc/middle/traits/README.md b/src/librustc/middle/traits/README.md
index 92982af92dcfe..ff72f9dd07e36 100644
--- a/src/librustc/middle/traits/README.md
+++ b/src/librustc/middle/traits/README.md
@@ -428,3 +428,43 @@ We used to try and draw finer-grained distinctions, but that led to a
 serious of annoying and weird bugs like #22019 and #18290. This simple
 rule seems to be pretty clearly safe and also still retains a very
 high hit rate (~95% when compiling rustc).
+
+# Specialization
+
+Defined in the `specialize` module.
+
+The basic strategy is to build up a *specialization graph* during
+coherence checking. Insertion into the graph locates the right place
+to put an impl in the specialization hierarchy; if there is no right
+place (due to partial overlap but no containment), you get an overlap
+error. Specialization is consulted when selecting an impl (of course),
+and the graph is consulted when propagating defaults down the
+specialization hierarchy.
+
+You might expect that the specialization graph would be used during
+selection -- i.e., when actually performing specialization. This is
+not done for two reasons:
+
+- It's merely an optimization: given a set of candidates that apply,
+  we can determine the most specialized one by comparing them directly
+  for specialization, rather than consulting the graph. Given that we
+  also cache the results of selection, the benefit of this
+  optimization is questionable.
+
+- To build the specialization graph in the first place, we need to use
+  selection (because we need to determine whether one impl specializes
+  another). Dealing with this reentrancy would require some additional
+  mode switch for selection. Given that there seems to be no strong
+  reason to use the graph anyway, we stick with a simpler approach in
+  selection, and use the graph only for propagating default
+  implementations.
+
+Trait impl selection can succeed even when multiple impls can apply,
+as long as they are part of the same specialization family. In that
+case, it returns a *single* impl on success -- this is the most
+specialized impl *known* to apply. However, if there are any inference
+variables in play, the returned impl may not be the actual impl we
+will use at trans time. Thus, we take special care to avoid projecting
+associated types unless either (1) the associated type does not use
+`default` and thus cannot be overridden or (2) all input types are
+known concretely.
diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs
index 6005d36ff4eb2..64d1c992cbf1d 100644
--- a/src/librustc/middle/traits/coherence.rs
+++ b/src/librustc/middle/traits/coherence.rs
@@ -10,8 +10,7 @@
 
 //! See `README.md` for high-level documentation
 
-use super::{SelectionContext};
-use super::{Obligation, ObligationCause};
+use super::{SelectionContext, Obligation, ObligationCause};
 
 use middle::cstore::LOCAL_CRATE;
 use middle::def_id::DefId;
@@ -23,8 +22,8 @@ use syntax::codemap::DUMMY_SP;
 #[derive(Copy, Clone)]
 struct InferIsLocal(bool);
 
-/// If there are types that satisfy both impls, returns an `ImplTy`
-/// with those types substituted (by updating the given `infcx`)
+/// If there are types that satisfy both impls, returns a suitably-freshened
+/// `ImplHeader` with those types substituted
 pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
                                     impl1_def_id: DefId,
                                     impl2_def_id: DefId)
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index 8a2f0c0c09304..5f66e9e6344ad 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -36,20 +36,18 @@ pub use self::coherence::orphan_check;
 pub use self::coherence::overlapping_impls;
 pub use self::coherence::OrphanCheckErr;
 pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
-pub use self::project::MismatchedProjectionTypes;
-pub use self::project::normalize;
-pub use self::project::Normalized;
+pub use self::project::{MismatchedProjectionTypes, ProjectionMode};
+pub use self::project::{normalize, Normalized};
 pub use self::object_safety::is_object_safe;
 pub use self::object_safety::astconv_object_safety_violations;
 pub use self::object_safety::object_safety_violations;
 pub use self::object_safety::ObjectSafetyViolation;
 pub use self::object_safety::MethodViolationCode;
 pub use self::object_safety::is_vtable_safe_method;
-pub use self::select::EvaluationCache;
-pub use self::select::SelectionContext;
-pub use self::select::SelectionCache;
+pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
 pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
 pub use self::select::{MethodMatchedData}; // intentionally don't export variants
+pub use self::specialize::{Overlap, specialization_graph, specializes, translate_substs};
 pub use self::util::elaborate_predicates;
 pub use self::util::get_vtable_index_of_object_method;
 pub use self::util::trait_ref_for_builtin_bound;
@@ -67,6 +65,7 @@ mod fulfill;
 mod project;
 mod object_safety;
 mod select;
+mod specialize;
 mod structural_impls;
 mod util;
 
@@ -434,7 +433,10 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi
 
     let elaborated_env = unnormalized_env.with_caller_bounds(predicates);
 
-    let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env));
+    let infcx = infer::new_infer_ctxt(tcx,
+                                      &tcx.tables,
+                                      Some(elaborated_env),
+                                      ProjectionMode::AnyFinal);
     let predicates = match fully_normalize(&infcx,
                                            cause,
                                            &infcx.parameter_environment.caller_bounds) {
diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs
index e36307feddbf7..e86f3ed01a49d 100644
--- a/src/librustc/middle/traits/project.rs
+++ b/src/librustc/middle/traits/project.rs
@@ -12,6 +12,8 @@
 
 use super::elaborate_predicates;
 use super::report_overflow_error;
+use super::specialization_graph;
+use super::translate_substs;
 use super::Obligation;
 use super::ObligationCause;
 use super::PredicateObligation;
@@ -21,13 +23,103 @@ use super::VtableClosureData;
 use super::VtableImplData;
 use super::util;
 
+use middle::def_id::DefId;
 use middle::infer::{self, TypeOrigin};
 use middle::subst::Subst;
 use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
 use middle::ty::fold::{TypeFoldable, TypeFolder};
 use syntax::parse::token;
+use syntax::ast;
 use util::common::FN_OUTPUT_NAME;
 
+use std::rc::Rc;
+
+/// Depending on the stage of compilation, we want projection to be
+/// more or less conservative.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum ProjectionMode {
+    /// FIXME (#32205)
+    /// At coherence-checking time, we're still constructing the
+    /// specialization graph, and thus we only project project
+    /// non-`default` associated types that are defined directly in
+    /// the applicable impl. (This behavior should be improved over
+    /// time, to allow for successful projections modulo cycles
+    /// between different impls).
+    ///
+    /// Here's an example that will fail due to the restriction:
+    ///
+    /// ```
+    /// trait Assoc {
+    ///     type Output;
+    /// }
+    ///
+    /// impl<T> Assoc for T {
+    ///     type Output = bool;
+    /// }
+    ///
+    /// impl Assoc for u8 {} // <- inherits the non-default type from above
+    ///
+    /// trait Foo {}
+    /// impl Foo for u32 {}
+    /// impl Foo for <u8 as Assoc>::Output {}  // <- this projection will fail
+    /// ```
+    ///
+    /// The projection would succeed if `Output` had been defined
+    /// directly in the impl for `u8`.
+    Topmost,
+
+    /// At type-checking time, we refuse to project any associated
+    /// type that is marked `default`. Non-`default` ("final") types
+    /// are always projected. This is necessary in general for
+    /// soundness of specialization. However, we *could* allow
+    /// projections in fully-monomorphic cases. We choose not to,
+    /// because we prefer for `default type` to force the type
+    /// definition to be treated abstractly by any consumers of the
+    /// impl. Concretely, that means that the following example will
+    /// fail to compile:
+    ///
+    /// ```
+    /// trait Assoc {
+    ///     type Output;
+    /// }
+    ///
+    /// impl<T> Assoc for T {
+    ///     default type Output = bool;
+    /// }
+    ///
+    /// fn main() {
+    ///     let <() as Assoc>::Output = true;
+    /// }
+    AnyFinal,
+
+    /// At trans time, all projections will succeed.
+    Any,
+}
+
+impl ProjectionMode {
+    pub fn is_topmost(&self) -> bool {
+        match *self {
+            ProjectionMode::Topmost => true,
+            _ => false,
+        }
+    }
+
+    pub fn is_any_final(&self) -> bool {
+        match *self {
+            ProjectionMode::AnyFinal => true,
+            _ => false,
+        }
+    }
+
+    pub fn is_any(&self) -> bool {
+        match *self {
+            ProjectionMode::Any => true,
+            _ => false,
+        }
+    }
+}
+
+
 pub type PolyProjectionObligation<'tcx> =
     Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
 
@@ -566,7 +658,76 @@ fn project_type<'cx,'tcx>(
 
     assert!(candidates.vec.len() <= 1);
 
-    match candidates.vec.pop() {
+    let possible_candidate = candidates.vec.pop().and_then(|candidate| {
+        // In Any (i.e. trans) mode, all projections succeed;
+        // otherwise, we need to be sensitive to `default` and
+        // specialization.
+        if !selcx.projection_mode().is_any() {
+            if let ProjectionTyCandidate::Impl(ref impl_data) = candidate {
+                if let Some(node_item) = assoc_ty_def(selcx,
+                                                      impl_data.impl_def_id,
+                                                      obligation.predicate.item_name) {
+                    if node_item.node.is_from_trait() {
+                        if node_item.item.ty.is_some() {
+                            // If the associated type has a default from the
+                            // trait, that should be considered `default` and
+                            // hence not projected.
+                            //
+                            // Note, however, that we allow a projection from
+                            // the trait specifically in the case that the trait
+                            // does *not* give a default. This is purely to
+                            // avoid spurious errors: the situation can only
+                            // arise when *no* impl in the specialization chain
+                            // has provided a definition for the type. When we
+                            // confirm the candidate, we'll turn the projection
+                            // into a TyError, since the actual error will be
+                            // reported in `check_impl_items_against_trait`.
+                            return None;
+                        }
+                    } else if node_item.item.defaultness.is_default() {
+                        return None;
+                    }
+                } else {
+                    // Normally this situation could only arise througha
+                    // compiler bug, but at coherence-checking time we only look
+                    // at the topmost impl (we don't even consider the trait
+                    // itself) for the definition -- so we can fail to find a
+                    // definition of the type even if it exists.
+
+                    // For now, we just unconditionally ICE, because otherwise,
+                    // examples like the following will succeed:
+                    //
+                    // ```
+                    // trait Assoc {
+                    //     type Output;
+                    // }
+                    //
+                    // impl<T> Assoc for T {
+                    //     default type Output = bool;
+                    // }
+                    //
+                    // impl Assoc for u8 {}
+                    // impl Assoc for u16 {}
+                    //
+                    // trait Foo {}
+                    // impl Foo for <u8 as Assoc>::Output {}
+                    // impl Foo for <u16 as Assoc>::Output {}
+                    //     return None;
+                    // }
+                    // ```
+                    //
+                    // The essential problem here is that the projection fails,
+                    // leaving two unnormalized types, which appear not to unify
+                    // -- so the overlap check succeeds, when it should fail.
+                    selcx.tcx().sess.bug("Tried to project an inherited associated type during \
+                                          coherence checking, which is currently not supported.");
+                }
+            }
+        }
+        Some(candidate)
+    });
+
+    match possible_candidate {
         Some(candidate) => {
             let (ty, obligations) = confirm_candidate(selcx, obligation, candidate);
             Ok(ProjectedTy::Progress(ty, obligations))
@@ -941,43 +1102,63 @@ fn confirm_impl_candidate<'cx,'tcx>(
     impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>)
     -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
 {
-    // there don't seem to be nicer accessors to these:
-    let impl_or_trait_items_map = selcx.tcx().impl_or_trait_items.borrow();
-
-    // Look for the associated type in the impl
-    for impl_item in &selcx.tcx().impl_items.borrow()[&impl_vtable.impl_def_id] {
-        if let ty::TypeTraitItem(ref assoc_ty) = impl_or_trait_items_map[&impl_item.def_id()] {
-            if assoc_ty.name == obligation.predicate.item_name {
-                return (assoc_ty.ty.unwrap().subst(selcx.tcx(), impl_vtable.substs),
-                        impl_vtable.nested);
-            }
+    let VtableImplData { substs, nested, impl_def_id } = impl_vtable;
+
+    let tcx = selcx.tcx();
+    let trait_ref = obligation.predicate.trait_ref;
+    let assoc_ty = assoc_ty_def(selcx, impl_def_id, obligation.predicate.item_name);
+
+    match assoc_ty {
+        Some(node_item) => {
+            let ty = node_item.item.ty.unwrap_or_else(|| {
+                // This means that the impl is missing a definition for the
+                // associated type. This error will be reported by the type
+                // checker method `check_impl_items_against_trait`, so here we
+                // just return TyError.
+                debug!("confirm_impl_candidate: no associated type {:?} for {:?}",
+                       node_item.item.name,
+                       obligation.predicate.trait_ref);
+                tcx.types.err
+            });
+            let substs = translate_substs(selcx.infcx(), impl_def_id, substs, node_item.node);
+            (ty.subst(tcx, substs), nested)
+        }
+        None => {
+            tcx.sess.span_bug(obligation.cause.span,
+                              &format!("No associated type for {:?}", trait_ref));
         }
     }
+}
 
-    // It is not in the impl - get the default from the trait.
-    let trait_ref = obligation.predicate.trait_ref;
-    for trait_item in selcx.tcx().trait_items(trait_ref.def_id).iter() {
-        if let &ty::TypeTraitItem(ref assoc_ty) = trait_item {
-            if assoc_ty.name == obligation.predicate.item_name {
-                if let Some(ty) = assoc_ty.ty {
-                    return (ty.subst(selcx.tcx(), trait_ref.substs),
-                            impl_vtable.nested);
-                } else {
-                    // This means that the impl is missing a
-                    // definition for the associated type. This error
-                    // ought to be reported by the type checker method
-                    // `check_impl_items_against_trait`, so here we
-                    // just return TyError.
-                    debug!("confirm_impl_candidate: no associated type {:?} for {:?}",
-                           assoc_ty.name,
-                           trait_ref);
-                    return (selcx.tcx().types.err, vec!());
+/// Locate the definition of an associated type in the specialization hierarchy,
+/// starting from the given impl.
+///
+/// Based on the "projection mode", this lookup may in fact only examine the
+/// topmost impl. See the comments for `ProjectionMode` for more details.
+fn assoc_ty_def<'cx, 'tcx>(selcx: &SelectionContext<'cx, 'tcx>,
+                           impl_def_id: DefId,
+                           assoc_ty_name: ast::Name)
+                           -> Option<specialization_graph::NodeItem<Rc<ty::AssociatedType<'tcx>>>>
+{
+    let trait_def_id = selcx.tcx().impl_trait_ref(impl_def_id).unwrap().def_id;
+
+    if selcx.projection_mode().is_topmost() {
+        let impl_node = specialization_graph::Node::Impl(impl_def_id);
+        for item in impl_node.items(selcx.tcx()) {
+            if let ty::TypeTraitItem(assoc_ty) = item {
+                if assoc_ty.name == assoc_ty_name {
+                    return Some(specialization_graph::NodeItem {
+                        node: specialization_graph::Node::Impl(impl_def_id),
+                        item: assoc_ty,
+                    });
                 }
             }
         }
+        None
+    } else {
+        selcx.tcx().lookup_trait_def(trait_def_id)
+            .ancestors(impl_def_id)
+            .type_defs(selcx.tcx(), assoc_ty_name)
+            .next()
     }
-
-    selcx.tcx().sess.span_bug(obligation.cause.span,
-                              &format!("No associated type for {:?}",
-                                       trait_ref));
 }
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index fbfd4b67b5bd5..3ef163f225c4a 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -25,6 +25,7 @@ use super::report_overflow_error;
 use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation};
 use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
 use super::{ObjectCastObligation, Obligation};
+use super::ProjectionMode;
 use super::TraitNotObjectSafe;
 use super::Selection;
 use super::SelectionResult;
@@ -40,6 +41,7 @@ use middle::infer;
 use middle::infer::{InferCtxt, TypeFreshener, TypeOrigin};
 use middle::subst::{Subst, Substs, TypeSpace};
 use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
+use middle::traits;
 use middle::ty::fast_reject;
 use middle::ty::relate::TypeRelation;
 
@@ -75,7 +77,6 @@ pub struct SelectionContext<'cx, 'tcx:'cx> {
     /// other words, we consider `$0 : Bar` to be unimplemented if
     /// there is no type that the user could *actually name* that
     /// would satisfy it. This avoids crippling inference, basically.
-
     intercrate: bool,
 }
 
@@ -224,6 +225,12 @@ struct SelectionCandidateSet<'tcx> {
     ambiguous: bool,
 }
 
+#[derive(PartialEq,Eq,Debug,Clone)]
+struct EvaluatedCandidate<'tcx> {
+    candidate: SelectionCandidate<'tcx>,
+    evaluation: EvaluationResult,
+}
+
 enum BuiltinBoundConditions<'tcx> {
     If(ty::Binder<Vec<Ty<'tcx>>>),
     ParameterBuiltin,
@@ -251,8 +258,7 @@ pub struct EvaluationCache<'tcx> {
 }
 
 impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
-    pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>)
-               -> SelectionContext<'cx, 'tcx> {
+    pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> {
         SelectionContext {
             infcx: infcx,
             freshener: infcx.freshener(),
@@ -260,8 +266,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
     }
 
-    pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>)
-                      -> SelectionContext<'cx, 'tcx> {
+    pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> {
         SelectionContext {
             infcx: infcx,
             freshener: infcx.freshener(),
@@ -285,6 +290,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         self.infcx
     }
 
+    pub fn projection_mode(&self) -> ProjectionMode {
+        self.infcx.projection_mode()
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Selection
     //
@@ -558,7 +567,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // this crate, perhaps the type would be unified with
         // something from another crate that does provide an impl.
         //
-        // In intracrate mode, we must still be conservative. The reason is
+        // In intra mode, we must still be conservative. The reason is
         // that we want to avoid cycles. Imagine an impl like:
         //
         //     impl<T:Eq> Eq for Vec<T>
@@ -746,6 +755,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         candidate
     }
 
+    // Treat negative impls as unimplemented
+    fn filter_negative_impls(&self, candidate: SelectionCandidate<'tcx>)
+                             -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+        if let ImplCandidate(def_id) = candidate {
+            if self.tcx().trait_impl_polarity(def_id) == Some(hir::ImplPolarity::Negative) {
+                return Err(Unimplemented)
+            }
+        }
+        Ok(Some(candidate))
+    }
+
     fn candidate_from_obligation_no_cache<'o>(&mut self,
                                               stack: &TraitObligationStack<'o, 'tcx>)
                                               -> SelectionResult<'tcx, SelectionCandidate<'tcx>>
@@ -762,7 +782,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         if !self.is_knowable(stack) {
-            debug!("intercrate not knowable");
+            debug!("coherence stage: not knowable");
             return Ok(None);
         }
 
@@ -803,12 +823,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // we were to winnow, we'd wind up with zero candidates.
         // Instead, we select the right impl now but report `Bar does
         // not implement Clone`.
-        if candidates.len() > 1 {
-            candidates.retain(|c| self.evaluate_candidate(stack, c).may_apply())
-        }
+        if candidates.len() == 1 {
+            return self.filter_negative_impls(candidates.pop().unwrap());
+        }
+
+        // Winnow, but record the exact outcome of evaluation, which
+        // is needed for specialization.
+        let mut candidates: Vec<_> = candidates.into_iter().filter_map(|c| {
+            let eval = self.evaluate_candidate(stack, &c);
+            if eval.may_apply() {
+                Some(EvaluatedCandidate {
+                    candidate: c,
+                    evaluation: eval,
+                })
+            } else {
+                None
+            }
+        }).collect();
 
-        // If there are STILL multiple candidate, we can further reduce
-        // the list by dropping duplicates.
+        // If there are STILL multiple candidate, we can further
+        // reduce the list by dropping duplicates -- including
+        // resolving specializations.
         if candidates.len() > 1 {
             let mut i = 0;
             while i < candidates.len() {
@@ -836,8 +871,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return Ok(None);
         }
 
-
-        // If there are *NO* candidates, that there are no impls --
+        // If there are *NO* candidates, then there are no impls --
         // that we know of, anyway. Note that in the case where there
         // are unbound type variables within the obligation, it might
         // be the case that you could still satisfy the obligation
@@ -851,19 +885,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         // Just one candidate left.
-        let candidate = candidates.pop().unwrap();
-
-        match candidate {
-            ImplCandidate(def_id) => {
-                match self.tcx().trait_impl_polarity(def_id) {
-                    Some(hir::ImplPolarity::Negative) => return Err(Unimplemented),
-                    _ => {}
-                }
-            }
-            _ => {}
-        }
-
-        Ok(Some(candidate))
+        self.filter_negative_impls(candidates.pop().unwrap().candidate)
     }
 
     fn is_knowable<'o>(&mut self,
@@ -1565,41 +1587,54 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// candidates and prefer where-clause candidates.
     ///
     /// See the comment for "SelectionCandidate" for more details.
-    fn candidate_should_be_dropped_in_favor_of<'o>(&mut self,
-                                                   victim: &SelectionCandidate<'tcx>,
-                                                   other: &SelectionCandidate<'tcx>)
-                                                   -> bool
+    fn candidate_should_be_dropped_in_favor_of<'o>(
+        &mut self,
+        victim: &EvaluatedCandidate<'tcx>,
+        other: &EvaluatedCandidate<'tcx>)
+        -> bool
     {
-        if victim == other {
+        if victim.candidate == other.candidate {
             return true;
         }
 
-        match other {
-            &ObjectCandidate |
-            &ParamCandidate(_) | &ProjectionCandidate => match victim {
-                &DefaultImplCandidate(..) => {
+        match other.candidate {
+            ObjectCandidate |
+            ParamCandidate(_) | ProjectionCandidate => match victim.candidate {
+                DefaultImplCandidate(..) => {
                     self.tcx().sess.bug(
                         "default implementations shouldn't be recorded \
                          when there are other valid candidates");
                 }
-                &ImplCandidate(..) |
-                &ClosureCandidate(..) |
-                &FnPointerCandidate |
-                &BuiltinObjectCandidate |
-                &BuiltinUnsizeCandidate |
-                &DefaultImplObjectCandidate(..) |
-                &BuiltinCandidate(..) => {
+                ImplCandidate(..) |
+                ClosureCandidate(..) |
+                FnPointerCandidate |
+                BuiltinObjectCandidate |
+                BuiltinUnsizeCandidate |
+                DefaultImplObjectCandidate(..) |
+                BuiltinCandidate(..) => {
                     // We have a where-clause so don't go around looking
                     // for impls.
                     true
                 }
-                &ObjectCandidate |
-                &ProjectionCandidate => {
+                ObjectCandidate |
+                ProjectionCandidate => {
                     // Arbitrarily give param candidates priority
                     // over projection and object candidates.
                     true
                 },
-                &ParamCandidate(..) => false,
+                ParamCandidate(..) => false,
+            },
+            ImplCandidate(other_def) => {
+                // See if we can toss out `victim` based on specialization.
+                // This requires us to know *for sure* that the `other` impl applies
+                // i.e. EvaluatedToOk:
+                if other.evaluation == EvaluatedToOk {
+                    if let ImplCandidate(victim_def) = victim.candidate {
+                        return traits::specializes(self.tcx(), other_def, victim_def);
+                    }
+                }
+
+                false
             },
             _ => false
         }
diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs
new file mode 100644
index 0000000000000..a692fe55a7789
--- /dev/null
+++ b/src/librustc/middle/traits/specialize/mod.rs
@@ -0,0 +1,229 @@
+// 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.
+
+// Logic and data structures related to impl specialization, explained in
+// greater detail below.
+//
+// At the moment, this implementation support only the simple "chain" rule:
+// If any two impls overlap, one must be a strict subset of the other.
+//
+// See traits/README.md for a bit more detail on how specialization
+// fits together with the rest of the trait machinery.
+
+use super::{SelectionContext, FulfillmentContext};
+use super::util::{fresh_type_vars_for_impl, impl_trait_ref_and_oblig};
+
+use middle::cstore::CrateStore;
+use middle::def_id::DefId;
+use middle::infer::{self, InferCtxt, TypeOrigin};
+use middle::region;
+use middle::subst::{Subst, Substs};
+use middle::traits::{self, ProjectionMode, ObligationCause, Normalized};
+use middle::ty::{self, TyCtxt};
+use syntax::codemap::DUMMY_SP;
+
+pub mod specialization_graph;
+
+/// Information pertinent to an overlapping impl error.
+pub struct Overlap<'a, 'tcx: 'a> {
+    pub in_context: InferCtxt<'a, 'tcx>,
+    pub with_impl: DefId,
+    pub on_trait_ref: ty::TraitRef<'tcx>,
+}
+
+/// Given a subst for the requested impl, translate it to a subst
+/// appropriate for the actual item definition (whether it be in that impl,
+/// a parent impl, or the trait).
+/// When we have selected one impl, but are actually using item definitions from
+/// a parent impl providing a default, we need a way to translate between the
+/// type parameters of the two impls. Here the `source_impl` is the one we've
+/// selected, and `source_substs` is a substitution of its generics (and
+/// possibly some relevant `FnSpace` variables as well). And `target_node` is
+/// the impl/trait we're actually going to get the definition from. The resulting
+/// substitution will map from `target_node`'s generics to `source_impl`'s
+/// generics as instantiated by `source_subst`.
+///
+/// For example, consider the following scenario:
+///
+/// ```rust
+/// trait Foo { ... }
+/// impl<T, U> Foo for (T, U) { ... }  // target impl
+/// impl<V> Foo for (V, V) { ... }     // source impl
+/// ```
+///
+/// Suppose we have selected "source impl" with `V` instantiated with `u32`.
+/// This function will produce a substitution with `T` and `U` both mapping to `u32`.
+///
+/// Where clauses add some trickiness here, because they can be used to "define"
+/// an argument indirectly:
+///
+/// ```rust
+/// impl<'a, I, T: 'a> Iterator for Cloned<I>
+///    where I: Iterator<Item=&'a T>, T: Clone
+/// ```
+///
+/// In a case like this, the substitution for `T` is determined indirectly,
+/// through associated type projection. We deal with such cases by using
+/// *fulfillment* to relate the two impls, requiring that all projections are
+/// resolved.
+pub fn translate_substs<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                                  source_impl: DefId,
+                                  source_substs: &'tcx Substs<'tcx>,
+                                  target_node: specialization_graph::Node)
+                                  -> &'tcx Substs<'tcx> {
+    let source_trait_ref = infcx.tcx
+                                .impl_trait_ref(source_impl)
+                                .unwrap()
+                                .subst(infcx.tcx, &source_substs);
+
+    // translate the Self and TyParam parts of the substitution, since those
+    // vary across impls
+    let target_substs = match target_node {
+        specialization_graph::Node::Impl(target_impl) => {
+            // no need to translate if we're targetting the impl we started with
+            if source_impl == target_impl {
+                return source_substs;
+            }
+
+            fulfill_implication(infcx, source_trait_ref, target_impl).unwrap_or_else(|_| {
+                infcx.tcx
+                     .sess
+                     .bug("When translating substitutions for specialization, the expected \
+                           specializaiton failed to hold")
+            })
+        }
+        specialization_graph::Node::Trait(..) => source_trait_ref.substs.clone(),
+    };
+
+    // retain erasure mode
+    // NB: this must happen before inheriting method generics below
+    let target_substs = if source_substs.regions.is_erased() {
+        target_substs.erase_regions()
+    } else {
+        target_substs
+    };
+
+    // directly inherent the method generics, since those do not vary across impls
+    infcx.tcx.mk_substs(target_substs.with_method_from_subst(source_substs))
+}
+
+/// Is impl1 a specialization of impl2?
+///
+/// Specialization is determined by the sets of types to which the impls apply;
+/// impl1 specializes impl2 if it applies to a subset of the types impl2 applies
+/// to.
+pub fn specializes(tcx: &TyCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool {
+    // The feature gate should prevent introducing new specializations, but not
+    // taking advantage of upstream ones.
+    if !tcx.sess.features.borrow().specialization &&
+        (impl1_def_id.is_local() || impl2_def_id.is_local()) {
+        return false;
+    }
+
+    // We determine whether there's a subset relationship by:
+    //
+    // - skolemizing impl1,
+    // - assuming the where clauses for impl1,
+    // - instantiating impl2 with fresh inference variables,
+    // - unifying,
+    // - attempting to prove the where clauses for impl2
+    //
+    // The last three steps are encapsulated in `fulfill_implication`.
+    //
+    // See RFC 1210 for more details and justification.
+
+    // Currently we do not allow e.g. a negative impl to specialize a positive one
+    if tcx.trait_impl_polarity(impl1_def_id) != tcx.trait_impl_polarity(impl2_def_id) {
+        return false;
+    }
+
+    let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Topmost);
+
+    // create a parameter environment corresponding to a (skolemized) instantiation of impl1
+    let scheme = tcx.lookup_item_type(impl1_def_id);
+    let predicates = tcx.lookup_predicates(impl1_def_id);
+    let mut penv = tcx.construct_parameter_environment(DUMMY_SP,
+                                                       &scheme.generics,
+                                                       &predicates,
+                                                       region::DUMMY_CODE_EXTENT);
+    let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id)
+                             .unwrap()
+                             .subst(tcx, &penv.free_substs);
+
+    // Normalize the trait reference, adding any obligations that arise into the impl1 assumptions
+    let Normalized { value: impl1_trait_ref, obligations: normalization_obligations } = {
+        let selcx = &mut SelectionContext::new(&infcx);
+        traits::normalize(selcx, ObligationCause::dummy(), &impl1_trait_ref)
+    };
+    penv.caller_bounds.extend(normalization_obligations.into_iter().map(|o| o.predicate));
+
+    // Install the parameter environment, taking the predicates of impl1 as assumptions:
+    infcx.parameter_environment = penv;
+
+    // Attempt to prove that impl2 applies, given all of the above.
+    fulfill_implication(&infcx, impl1_trait_ref, impl2_def_id).is_ok()
+}
+
+/// Attempt to fulfill all obligations of `target_impl` after unification with
+/// `source_trait_ref`. If successful, returns a substitution for *all* the
+/// generics of `target_impl`, including both those needed to unify with
+/// `source_trait_ref` and those whose identity is determined via a where
+/// clause in the impl.
+fn fulfill_implication<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                                 source_trait_ref: ty::TraitRef<'tcx>,
+                                 target_impl: DefId)
+                                 -> Result<Substs<'tcx>, ()> {
+    infcx.commit_if_ok(|_| {
+        let selcx = &mut SelectionContext::new(&infcx);
+        let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
+        let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
+                                                                       target_impl,
+                                                                       &target_substs);
+
+        // do the impls unify? If not, no specialization.
+        if let Err(_) = infer::mk_eq_trait_refs(&infcx,
+                                                true,
+                                                TypeOrigin::Misc(DUMMY_SP),
+                                                source_trait_ref,
+                                                target_trait_ref) {
+            debug!("fulfill_implication: {:?} does not unify with {:?}",
+                   source_trait_ref,
+                   target_trait_ref);
+            return Err(());
+        }
+
+        // attempt to prove all of the predicates for impl2 given those for impl1
+        // (which are packed up in penv)
+
+        let mut fulfill_cx = FulfillmentContext::new();
+        for oblig in obligations.into_iter() {
+            fulfill_cx.register_predicate_obligation(&infcx, oblig);
+        }
+
+        if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) {
+            // no dice!
+            debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
+                    {:?}",
+                   source_trait_ref,
+                   target_trait_ref,
+                   errors,
+                   infcx.parameter_environment.caller_bounds);
+            Err(())
+        } else {
+            debug!("fulfill_implication: an impl for {:?} specializes {:?}",
+                   source_trait_ref,
+                   target_trait_ref);
+
+            // Now resolve the *substitution* we built for the target earlier, replacing
+            // the inference variables inside with whatever we got from fulfillment.
+            Ok(infcx.resolve_type_vars_if_possible(&target_substs))
+        }
+    })
+}
diff --git a/src/librustc/middle/traits/specialize/specialization_graph.rs b/src/librustc/middle/traits/specialize/specialization_graph.rs
new file mode 100644
index 0000000000000..f2170f75a11fe
--- /dev/null
+++ b/src/librustc/middle/traits/specialize/specialization_graph.rs
@@ -0,0 +1,393 @@
+// Copyright 2016 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 std::cell;
+use std::rc::Rc;
+
+use super::{Overlap, specializes};
+
+use middle::cstore::CrateStore;
+use middle::def_id::DefId;
+use middle::infer;
+use middle::traits::{self, ProjectionMode};
+use middle::ty::{self, TyCtxt, ImplOrTraitItem, TraitDef, TypeFoldable};
+use syntax::ast::Name;
+use util::nodemap::DefIdMap;
+
+/// A per-trait graph of impls in specialization order. At the moment, this
+/// graph forms a tree rooted with the trait itself, with all other nodes
+/// representing impls, and parent-child relationships representing
+/// specializations.
+///
+/// The graph provides two key services:
+///
+/// - Construction, which implicitly checks for overlapping impls (i.e., impls
+///   that overlap but where neither specializes the other -- an artifact of the
+///   simple "chain" rule.
+///
+/// - Parent extraction. In particular, the graph can give you the *immediate*
+///   parents of a given specializing impl, which is needed for extracting
+///   default items amongst other thigns. In the simple "chain" rule, every impl
+///   has at most one parent.
+pub struct Graph {
+    // all impls have a parent; the "root" impls have as their parent the def_id
+    // of the trait
+    parent: DefIdMap<DefId>,
+
+    // the "root" impls are found by looking up the trait's def_id.
+    children: DefIdMap<Vec<DefId>>,
+}
+
+impl Graph {
+    pub fn new() -> Graph {
+        Graph {
+            parent: Default::default(),
+            children: Default::default(),
+        }
+    }
+
+    /// Insert a local impl into the specialization graph. If an existing impl
+    /// conflicts with it (has overlap, but neither specializes the other),
+    /// information about the area of overlap is returned in the `Err`.
+    pub fn insert<'a, 'tcx>(&mut self,
+                            tcx: &'a TyCtxt<'tcx>,
+                            impl_def_id: DefId)
+                            -> Result<(), Overlap<'a, 'tcx>> {
+        assert!(impl_def_id.is_local());
+
+        let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+        let trait_def_id = trait_ref.def_id;
+
+        debug!("insert({:?}): inserting TraitRef {:?} into specialization graph",
+               impl_def_id, trait_ref);
+
+        // if the reference itself contains an earlier error (e.g., due to a
+        // resolution failure), then we just insert the impl at the top level of
+        // the graph and claim that there's no overlap (in order to supress
+        // bogus errors).
+        if trait_ref.references_error() {
+            debug!("insert: inserting dummy node for erroneous TraitRef {:?}, \
+                    impl_def_id={:?}, trait_def_id={:?}",
+                   trait_ref, impl_def_id, trait_def_id);
+
+            self.parent.insert(impl_def_id, trait_def_id);
+            self.children.entry(trait_def_id).or_insert(vec![]).push(impl_def_id);
+            return Ok(());
+        }
+
+        let mut parent = trait_def_id;
+
+        // Ugly hack around borrowck limitations. Assigned only in the case
+        // where we bump downward an existing node in the graph.
+        let child_to_insert;
+
+        'descend: loop {
+            let mut possible_siblings = self.children.entry(parent).or_insert(vec![]);
+
+            for slot in possible_siblings.iter_mut() {
+                let possible_sibling = *slot;
+
+                let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Topmost);
+                let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id);
+
+                if let Some(impl_header) = overlap {
+                    let le = specializes(tcx, impl_def_id, possible_sibling);
+                    let ge = specializes(tcx, possible_sibling, impl_def_id);
+
+                    if le && !ge {
+                        debug!("descending as child of TraitRef {:?}",
+                               tcx.impl_trait_ref(possible_sibling).unwrap());
+
+                        // the impl specializes possible_sibling
+                        parent = possible_sibling;
+                        continue 'descend;
+                    } else if ge && !le {
+                        debug!("placing as parent of TraitRef {:?}",
+                               tcx.impl_trait_ref(possible_sibling).unwrap());
+
+                        // possible_sibling specializes the impl
+                        *slot = impl_def_id;
+                        self.parent.insert(impl_def_id, parent);
+                        self.parent.insert(possible_sibling, impl_def_id);
+                        // we have to defer the insertion, because we can't
+                        // relinquish the borrow of `self.children`
+                        child_to_insert = possible_sibling;
+                        break 'descend;
+                    } else {
+                        // overlap, but no specialization; error out
+                        return Err(Overlap {
+                            with_impl: possible_sibling,
+                            on_trait_ref: impl_header.trait_ref.unwrap(),
+                            in_context: infcx,
+                        });
+                    }
+                }
+            }
+
+            // no overlap with any potential siblings, so add as a new sibling
+            debug!("placing as new sibling");
+            self.parent.insert(impl_def_id, parent);
+            possible_siblings.push(impl_def_id);
+            return Ok(());
+        }
+
+        self.children.insert(impl_def_id, vec![child_to_insert]);
+        Ok(())
+    }
+
+    /// Insert cached metadata mapping from a child impl back to its parent.
+    pub fn record_impl_from_cstore(&mut self, parent: DefId, child: DefId) {
+        if self.parent.insert(child, parent).is_some() {
+            panic!("When recording an impl from the crate store, information about its parent \
+                    was already present.");
+        }
+
+        self.children.entry(parent).or_insert(vec![]).push(child);
+    }
+
+    /// The parent of a given impl, which is the def id of the trait when the
+    /// impl is a "specialization root".
+    pub fn parent(&self, child: DefId) -> DefId {
+        *self.parent.get(&child).unwrap()
+    }
+}
+
+/// A node in the specialization graph is either an impl or a trait
+/// definition; either can serve as a source of item definitions.
+/// There is always exactly one trait definition node: the root.
+#[derive(Debug, Copy, Clone)]
+pub enum Node {
+    Impl(DefId),
+    Trait(DefId),
+}
+
+impl Node {
+    pub fn is_from_trait(&self) -> bool {
+        match *self {
+            Node::Trait(..) => true,
+            _ => false,
+        }
+    }
+
+    /// Iterate over the items defined directly by the given (impl or trait) node.
+    pub fn items<'a, 'tcx>(&self, tcx: &'a TyCtxt<'tcx>) -> NodeItems<'a, 'tcx> {
+        match *self {
+            Node::Impl(impl_def_id) => {
+                NodeItems::Impl {
+                    tcx: tcx,
+                    items: cell::Ref::map(tcx.impl_items.borrow(),
+                                          |impl_items| &impl_items[&impl_def_id]),
+                    idx: 0,
+                }
+            }
+            Node::Trait(trait_def_id) => {
+                NodeItems::Trait {
+                    items: tcx.trait_items(trait_def_id).clone(),
+                    idx: 0,
+                }
+            }
+        }
+    }
+
+    pub fn def_id(&self) -> DefId {
+        match *self {
+            Node::Impl(did) => did,
+            Node::Trait(did) => did,
+        }
+    }
+}
+
+/// An iterator over the items defined within a trait or impl.
+pub enum NodeItems<'a, 'tcx: 'a> {
+    Impl {
+        tcx: &'a TyCtxt<'tcx>,
+        items: cell::Ref<'a, Vec<ty::ImplOrTraitItemId>>,
+        idx: usize,
+    },
+    Trait {
+        items: Rc<Vec<ImplOrTraitItem<'tcx>>>,
+        idx: usize,
+    },
+}
+
+impl<'a, 'tcx> Iterator for NodeItems<'a, 'tcx> {
+    type Item = ImplOrTraitItem<'tcx>;
+    fn next(&mut self) -> Option<ImplOrTraitItem<'tcx>> {
+        match *self {
+            NodeItems::Impl { tcx, ref items, ref mut idx } => {
+                let items_table = tcx.impl_or_trait_items.borrow();
+                if *idx < items.len() {
+                    let item_def_id = items[*idx].def_id();
+                    let item = items_table[&item_def_id].clone();
+                    *idx += 1;
+                    Some(item)
+                } else {
+                    None
+                }
+            }
+            NodeItems::Trait { ref items, ref mut idx } => {
+                if *idx < items.len() {
+                    let item = items[*idx].clone();
+                    *idx += 1;
+                    Some(item)
+                } else {
+                    None
+                }
+            }
+        }
+    }
+}
+
+pub struct Ancestors<'a, 'tcx: 'a> {
+    trait_def: &'a TraitDef<'tcx>,
+    current_source: Option<Node>,
+}
+
+impl<'a, 'tcx> Iterator for Ancestors<'a, 'tcx> {
+    type Item = Node;
+    fn next(&mut self) -> Option<Node> {
+        let cur = self.current_source.take();
+        if let Some(Node::Impl(cur_impl)) = cur {
+            let parent = self.trait_def.specialization_graph.borrow().parent(cur_impl);
+            if parent == self.trait_def.def_id() {
+                self.current_source = Some(Node::Trait(parent));
+            } else {
+                self.current_source = Some(Node::Impl(parent));
+            }
+        }
+        cur
+    }
+}
+
+pub struct NodeItem<T> {
+    pub node: Node,
+    pub item: T,
+}
+
+impl<T> NodeItem<T> {
+    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> NodeItem<U> {
+        NodeItem {
+            node: self.node,
+            item: f(self.item),
+        }
+    }
+}
+
+pub struct TypeDefs<'a, 'tcx: 'a> {
+    // generally only invoked once or twice, so the box doesn't hurt
+    iter: Box<Iterator<Item = NodeItem<Rc<ty::AssociatedType<'tcx>>>> + 'a>,
+}
+
+impl<'a, 'tcx> Iterator for TypeDefs<'a, 'tcx> {
+    type Item = NodeItem<Rc<ty::AssociatedType<'tcx>>>;
+    fn next(&mut self) -> Option<Self::Item> {
+        self.iter.next()
+    }
+}
+
+pub struct FnDefs<'a, 'tcx: 'a> {
+    // generally only invoked once or twice, so the box doesn't hurt
+    iter: Box<Iterator<Item = NodeItem<Rc<ty::Method<'tcx>>>> + 'a>,
+}
+
+impl<'a, 'tcx> Iterator for FnDefs<'a, 'tcx> {
+    type Item = NodeItem<Rc<ty::Method<'tcx>>>;
+    fn next(&mut self) -> Option<Self::Item> {
+        self.iter.next()
+    }
+}
+
+pub struct ConstDefs<'a, 'tcx: 'a> {
+    // generally only invoked once or twice, so the box doesn't hurt
+    iter: Box<Iterator<Item = NodeItem<Rc<ty::AssociatedConst<'tcx>>>> + 'a>,
+}
+
+impl<'a, 'tcx> Iterator for ConstDefs<'a, 'tcx> {
+    type Item = NodeItem<Rc<ty::AssociatedConst<'tcx>>>;
+    fn next(&mut self) -> Option<Self::Item> {
+        self.iter.next()
+    }
+}
+
+impl<'a, 'tcx> Ancestors<'a, 'tcx> {
+    /// Search the items from the given ancestors, returning each type definition
+    /// with the given name.
+    pub fn type_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> TypeDefs<'a, 'tcx> {
+        let iter = self.flat_map(move |node| {
+            node.items(tcx)
+                .filter_map(move |item| {
+                    if let ty::TypeTraitItem(assoc_ty) = item {
+                        if assoc_ty.name == name {
+                            return Some(NodeItem {
+                                node: node,
+                                item: assoc_ty,
+                            });
+                        }
+                    }
+                    None
+                })
+
+        });
+        TypeDefs { iter: Box::new(iter) }
+    }
+
+    /// Search the items from the given ancestors, returning each fn definition
+    /// with the given name.
+    pub fn fn_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> FnDefs<'a, 'tcx> {
+        let iter = self.flat_map(move |node| {
+            node.items(tcx)
+                .filter_map(move |item| {
+                    if let ty::MethodTraitItem(method) = item {
+                        if method.name == name {
+                            return Some(NodeItem {
+                                node: node,
+                                item: method,
+                            });
+                        }
+                    }
+                    None
+                })
+
+        });
+        FnDefs { iter: Box::new(iter) }
+    }
+
+    /// Search the items from the given ancestors, returning each const
+    /// definition with the given name.
+    pub fn const_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> ConstDefs<'a, 'tcx> {
+        let iter = self.flat_map(move |node| {
+            node.items(tcx)
+                .filter_map(move |item| {
+                    if let ty::ConstTraitItem(konst) = item {
+                        if konst.name == name {
+                            return Some(NodeItem {
+                                node: node,
+                                item: konst,
+                            });
+                        }
+                    }
+                    None
+                })
+
+        });
+        ConstDefs { iter: Box::new(iter) }
+    }
+}
+
+/// Walk up the specialization ancestors of a given impl, starting with that
+/// impl itself.
+pub fn ancestors<'a, 'tcx>(trait_def: &'a TraitDef<'tcx>,
+                           start_from_impl: DefId)
+                           -> Ancestors<'a, 'tcx> {
+    Ancestors {
+        trait_def: trait_def,
+        current_source: Some(Node::Impl(start_from_impl)),
+    }
+}
diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs
index 08d504143c7cc..1e37600bc0449 100644
--- a/src/librustc/middle/traits/util.rs
+++ b/src/librustc/middle/traits/util.rs
@@ -10,13 +10,13 @@
 
 use middle::def_id::DefId;
 use middle::infer::InferCtxt;
-use middle::subst::Substs;
+use middle::subst::{Subst, Substs};
 use middle::ty::{self, Ty, TyCtxt, ToPredicate, ToPolyTraitRef};
 use syntax::codemap::Span;
 use util::common::ErrorReported;
 use util::nodemap::FnvHashSet;
 
-use super::{Obligation, ObligationCause, PredicateObligation};
+use super::{Obligation, ObligationCause, PredicateObligation, SelectionContext, Normalized};
 
 struct PredicateSet<'a,'tcx:'a> {
     tcx: &'a TyCtxt<'tcx>,
@@ -299,6 +299,38 @@ impl<'tcx,I:Iterator<Item=ty::Predicate<'tcx>>> Iterator for FilterToTraits<I> {
 // Other
 ///////////////////////////////////////////////////////////////////////////
 
+/// Instantiate all bound parameters of the impl with the given substs,
+/// returning the resulting trait ref and all obligations that arise.
+/// The obligations are closed under normalization.
+pub fn impl_trait_ref_and_oblig<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
+                                         impl_def_id: DefId,
+                                         impl_substs: &Substs<'tcx>)
+                                         -> (ty::TraitRef<'tcx>,
+                                             Vec<PredicateObligation<'tcx>>)
+{
+    let impl_trait_ref =
+        selcx.tcx().impl_trait_ref(impl_def_id).unwrap();
+    let impl_trait_ref =
+        impl_trait_ref.subst(selcx.tcx(), impl_substs);
+    let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
+        super::normalize(selcx, ObligationCause::dummy(), &impl_trait_ref);
+
+    let predicates = selcx.tcx().lookup_predicates(impl_def_id);
+    let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
+    let Normalized { value: predicates, obligations: normalization_obligations2 } =
+        super::normalize(selcx, ObligationCause::dummy(), &predicates);
+    let impl_obligations =
+        predicates_for_generics(ObligationCause::dummy(), 0, &predicates);
+
+    let impl_obligations: Vec<_> =
+        impl_obligations.into_iter()
+        .chain(normalization_obligations1)
+        .chain(normalization_obligations2)
+        .collect();
+
+    (impl_trait_ref, impl_obligations)
+}
+
 // determine the `self` type, using fresh variables for all variables
 // declared on the impl declaration e.g., `impl<A,B> for Box<[(A,B)]>`
 // would return ($0, $1) where $0 and $1 are freshly instantiated type
@@ -349,7 +381,6 @@ pub fn trait_ref_for_builtin_bound<'tcx>(
     }
 }
 
-
 pub fn predicate_for_trait_ref<'tcx>(
     cause: ObligationCause<'tcx>,
     trait_ref: ty::TraitRef<'tcx>,
diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs
index 2491da7f5f317..081196835936c 100644
--- a/src/librustc/middle/ty/mod.rs
+++ b/src/librustc/middle/ty/mod.rs
@@ -281,6 +281,7 @@ pub struct Method<'tcx> {
     pub fty: BareFnTy<'tcx>,
     pub explicit_self: ExplicitSelfCategory,
     pub vis: hir::Visibility,
+    pub defaultness: hir::Defaultness,
     pub def_id: DefId,
     pub container: ImplOrTraitItemContainer,
 }
@@ -292,16 +293,18 @@ impl<'tcx> Method<'tcx> {
                fty: BareFnTy<'tcx>,
                explicit_self: ExplicitSelfCategory,
                vis: hir::Visibility,
+               defaultness: hir::Defaultness,
                def_id: DefId,
                container: ImplOrTraitItemContainer)
                -> Method<'tcx> {
-       Method {
+        Method {
             name: name,
             generics: generics,
             predicates: predicates,
             fty: fty,
             explicit_self: explicit_self,
             vis: vis,
+            defaultness: defaultness,
             def_id: def_id,
             container: container,
         }
@@ -334,6 +337,7 @@ pub struct AssociatedConst<'tcx> {
     pub name: Name,
     pub ty: Ty<'tcx>,
     pub vis: hir::Visibility,
+    pub defaultness: hir::Defaultness,
     pub def_id: DefId,
     pub container: ImplOrTraitItemContainer,
     pub has_value: bool
@@ -344,6 +348,7 @@ pub struct AssociatedType<'tcx> {
     pub name: Name,
     pub ty: Option<Ty<'tcx>>,
     pub vis: hir::Visibility,
+    pub defaultness: hir::Defaultness,
     pub def_id: DefId,
     pub container: ImplOrTraitItemContainer,
 }
@@ -2451,8 +2456,13 @@ impl<'tcx> TyCtxt<'tcx> {
         for impl_def_id in self.sess.cstore.implementations_of_trait(trait_id) {
             let impl_items = self.sess.cstore.impl_items(impl_def_id);
             let trait_ref = self.impl_trait_ref(impl_def_id).unwrap();
+
             // Record the trait->implementation mapping.
-            def.record_impl(self, impl_def_id, trait_ref);
+            if let Some(parent) = self.sess.cstore.impl_parent(impl_def_id) {
+                def.record_remote_impl(self, impl_def_id, trait_ref, parent);
+            } else {
+                def.record_remote_impl(self, impl_def_id, trait_ref, trait_id);
+            }
 
             // For any methods that use a default implementation, add them to
             // the map. This is a bit unfortunate.
@@ -2660,7 +2670,6 @@ impl<'tcx> TyCtxt<'tcx> {
         Some(self.tables.borrow().upvar_capture_map.get(&upvar_id).unwrap().clone())
     }
 
-
     pub fn visit_all_items_in_krate<V,F>(&self,
                                          dep_node_fn: F,
                                          visitor: &mut V)
@@ -2668,6 +2677,16 @@ impl<'tcx> TyCtxt<'tcx> {
     {
         dep_graph::visit_all_items_in_krate(self, dep_node_fn, visitor);
     }
+    /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err`
+    /// with the name of the crate containing the impl.
+    pub fn span_of_impl(&self, impl_did: DefId) -> Result<Span, String> {
+        if impl_did.is_local() {
+            let node_id = self.map.as_local_node_id(impl_did).unwrap();
+            Ok(self.map.span(node_id))
+        } else {
+            Err(self.sess.cstore.crate_name(impl_did.krate))
+        }
+    }
 }
 
 /// The category of explicit self.
@@ -2709,28 +2728,4 @@ impl<'tcx> TyCtxt<'tcx> {
             Some(d) => f(&d[..])
         }
     }
-
-    pub fn make_substs_for_receiver_types(&self,
-                                          trait_ref: &ty::TraitRef<'tcx>,
-                                          method: &ty::Method<'tcx>)
-                                          -> subst::Substs<'tcx>
-    {
-        /*!
-         * Substitutes the values for the receiver's type parameters
-         * that are found in method, leaving the method's type parameters
-         * intact.
-         */
-
-        let meth_tps: Vec<Ty> =
-            method.generics.types.get_slice(subst::FnSpace)
-                  .iter()
-                  .map(|def| self.mk_param_from_def(def))
-                  .collect();
-        let meth_regions: Vec<ty::Region> =
-            method.generics.regions.get_slice(subst::FnSpace)
-                  .iter()
-                  .map(|def| def.to_early_bound_region())
-                  .collect();
-        trait_ref.substs.clone().with_method(meth_tps, meth_regions)
-    }
 }
diff --git a/src/librustc/middle/ty/sty.rs b/src/librustc/middle/ty/sty.rs
index 2d7b7dc6e9b58..bbc5948f2cac7 100644
--- a/src/librustc/middle/ty/sty.rs
+++ b/src/librustc/middle/ty/sty.rs
@@ -1106,6 +1106,13 @@ impl<'tcx> TyS<'tcx> {
         }
     }
 
+    pub fn has_concrete_skeleton(&self) -> bool {
+        match self.sty {
+            TyParam(_) | TyInfer(_) | TyError => false,
+            _ => true,
+        }
+    }
+
     // Returns the type and mutability of *ty.
     //
     // The parameter `explicit` indicates if this is an *explicit* dereference.
diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs
index 5ecbbbcfbde6d..3d7b3bf263425 100644
--- a/src/librustc/middle/ty/trait_def.rs
+++ b/src/librustc/middle/ty/trait_def.rs
@@ -10,9 +10,10 @@
 
 use dep_graph::DepNode;
 use middle::def_id::DefId;
+use middle::traits::{self, specialization_graph};
 use middle::ty;
 use middle::ty::fast_reject;
-use middle::ty::{Ty, TyCtxt};
+use middle::ty::{Ty, TyCtxt, TraitRef};
 use std::borrow::{Borrow};
 use std::cell::{Cell, Ref, RefCell};
 use syntax::ast::Name;
@@ -59,6 +60,9 @@ pub struct TraitDef<'tcx> {
     /// Blanket impls associated with the trait.
     blanket_impls: RefCell<Vec<DefId>>,
 
+    /// The specialization order for impls of this trait.
+    pub specialization_graph: RefCell<traits::specialization_graph::Graph>,
+
     /// Various flags
     pub flags: Cell<TraitFlags>
 }
@@ -78,7 +82,8 @@ impl<'tcx> TraitDef<'tcx> {
             associated_type_names: associated_type_names,
             nonblanket_impls: RefCell::new(FnvHashMap()),
             blanket_impls: RefCell::new(vec![]),
-            flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS)
+            flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS),
+            specialization_graph: RefCell::new(traits::specialization_graph::Graph::new()),
         }
     }
 
@@ -114,11 +119,14 @@ impl<'tcx> TraitDef<'tcx> {
         tcx.dep_graph.read(DepNode::TraitImpls(self.trait_ref.def_id));
     }
 
-    /// Records a trait-to-implementation mapping.
-    pub fn record_impl(&self,
-                       tcx: &TyCtxt<'tcx>,
-                       impl_def_id: DefId,
-                       impl_trait_ref: ty::TraitRef<'tcx>) {
+    /// Records a basic trait-to-implementation mapping.
+    ///
+    /// Returns `true` iff the impl has not previously been recorded.
+    fn record_impl(&self,
+                   tcx: &TyCtxt<'tcx>,
+                   impl_def_id: DefId,
+                   impl_trait_ref: TraitRef<'tcx>)
+                   -> bool {
         debug!("TraitDef::record_impl for {:?}, from {:?}",
                self, impl_trait_ref);
 
@@ -134,22 +142,71 @@ impl<'tcx> TraitDef<'tcx> {
                                                       impl_trait_ref.self_ty(), false) {
             if let Some(is) = self.nonblanket_impls.borrow().get(&sty) {
                 if is.contains(&impl_def_id) {
-                    return // duplicate - skip
+                    return false; // duplicate - skip
                 }
             }
 
             self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id)
         } else {
             if self.blanket_impls.borrow().contains(&impl_def_id) {
-                return // duplicate - skip
+                return false; // duplicate - skip
             }
             self.blanket_impls.borrow_mut().push(impl_def_id)
         }
+
+        true
     }
 
-    pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: &TyCtxt<'tcx>, mut f: F)  {
-        self.read_trait_impls(tcx);
+    /// Records a trait-to-implementation mapping for a crate-local impl.
+    pub fn record_local_impl(&self,
+                             tcx: &TyCtxt<'tcx>,
+                             impl_def_id: DefId,
+                             impl_trait_ref: TraitRef<'tcx>) {
+        assert!(impl_def_id.is_local());
+        let was_new = self.record_impl(tcx, impl_def_id, impl_trait_ref);
+        assert!(was_new);
+    }
+
+    /// Records a trait-to-implementation mapping for a non-local impl.
+    ///
+    /// The `parent_impl` is the immediately-less-specialized impl, or the
+    /// trait's def ID if the impl is is not a specialization -- information that
+    /// should be pulled from the metadata.
+    pub fn record_remote_impl(&self,
+                              tcx: &TyCtxt<'tcx>,
+                              impl_def_id: DefId,
+                              impl_trait_ref: TraitRef<'tcx>,
+                              parent_impl: DefId) {
+        assert!(!impl_def_id.is_local());
+
+        // if the impl has not previously been recorded
+        if self.record_impl(tcx, impl_def_id, impl_trait_ref) {
+            // if the impl is non-local, it's placed directly into the
+            // specialization graph using parent information drawn from metadata.
+            self.specialization_graph.borrow_mut()
+                .record_impl_from_cstore(parent_impl, impl_def_id)
+        }
+    }
+
+    /// Adds a local impl into the specialization graph, returning an error with
+    /// overlap information if the impl overlaps but does not specialize an
+    /// existing impl.
+    pub fn add_impl_for_specialization<'a>(&self,
+                                           tcx: &'a TyCtxt<'tcx>,
+                                           impl_def_id: DefId)
+                                           -> Result<(), traits::Overlap<'a, 'tcx>> {
+        assert!(impl_def_id.is_local());
+
+        self.specialization_graph.borrow_mut()
+            .insert(tcx, impl_def_id)
+    }
 
+    pub fn ancestors<'a>(&'a self, of_impl: DefId) -> specialization_graph::Ancestors<'a, 'tcx> {
+        specialization_graph::ancestors(self, of_impl)
+    }
+
+        pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: &TyCtxt<'tcx>, mut f: F)  {
+            self.read_trait_impls(tcx);
         tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
 
         for &impl_def_id in self.blanket_impls.borrow().iter() {
@@ -223,4 +280,3 @@ bitflags! {
         const IMPLS_VALID           = 1 << 3,
     }
 }
-
diff --git a/src/librustc/middle/ty/util.rs b/src/librustc/middle/ty/util.rs
index c91441a3f8a4b..5af40a3675ff7 100644
--- a/src/librustc/middle/ty/util.rs
+++ b/src/librustc/middle/ty/util.rs
@@ -14,10 +14,10 @@ use back::svh::Svh;
 use middle::const_eval::{self, ConstVal, ErrKind};
 use middle::const_eval::EvalHint::UncheckedExprHint;
 use middle::def_id::DefId;
-use middle::subst::{self, Subst, Substs};
+use middle::subst;
 use middle::infer;
 use middle::pat_util;
-use middle::traits;
+use middle::traits::{self, ProjectionMode};
 use middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable};
 use middle::ty::{Disr, ParameterEnvironment};
 use middle::ty::TypeVariants::*;
@@ -26,7 +26,6 @@ use rustc_const_eval::{ConstInt, ConstIsize, ConstUsize};
 
 use std::cmp;
 use std::hash::{Hash, SipHasher, Hasher};
-use std::rc::Rc;
 use syntax::ast::{self, Name};
 use syntax::attr::{self, AttrMetaMethods, SignedInt, UnsignedInt};
 use syntax::codemap::Span;
@@ -131,7 +130,10 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
         let tcx = self.tcx;
 
         // FIXME: (@jroesch) float this code up
-        let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(self.clone()));
+        let infcx = infer::new_infer_ctxt(tcx,
+                                          &tcx.tables,
+                                          Some(self.clone()),
+                                          ProjectionMode::AnyFinal);
 
         let adt = match self_type.sty {
             ty::TyStruct(struct_def, substs) => {
@@ -536,58 +538,6 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 }
 
-#[derive(Debug)]
-pub struct ImplMethod<'tcx> {
-    pub method: Rc<ty::Method<'tcx>>,
-    pub substs: &'tcx Substs<'tcx>,
-    pub is_provided: bool
-}
-
-impl<'tcx> TyCtxt<'tcx> {
-    pub fn get_impl_method(&self,
-                           impl_def_id: DefId,
-                           substs: &'tcx Substs<'tcx>,
-                           name: Name)
-                           -> ImplMethod<'tcx>
-    {
-        // there don't seem to be nicer accessors to these:
-        let impl_or_trait_items_map = self.impl_or_trait_items.borrow();
-
-        for impl_item in &self.impl_items.borrow()[&impl_def_id] {
-            if let ty::MethodTraitItem(ref meth) =
-                impl_or_trait_items_map[&impl_item.def_id()] {
-                if meth.name == name {
-                    return ImplMethod {
-                        method: meth.clone(),
-                        substs: substs,
-                        is_provided: false
-                    }
-                }
-            }
-        }
-
-        // It is not in the impl - get the default from the trait.
-        let trait_ref = self.impl_trait_ref(impl_def_id).unwrap();
-        for trait_item in self.trait_items(trait_ref.def_id).iter() {
-            if let &ty::MethodTraitItem(ref meth) = trait_item {
-                if meth.name == name {
-                    let impl_to_trait_substs = self
-                        .make_substs_for_receiver_types(&trait_ref, meth);
-                    let substs = impl_to_trait_substs.subst(self, substs);
-                    return ImplMethod {
-                        method: meth.clone(),
-                        substs: self.mk_substs(substs),
-                        is_provided: true
-                    }
-                }
-            }
-        }
-
-        self.sess.bug(&format!("method {:?} not found in {:?}",
-                               name, impl_def_id))
-    }
-}
-
 impl<'tcx> ty::TyS<'tcx> {
     fn impls_bound<'a>(&'tcx self, param_env: &ParameterEnvironment<'a,'tcx>,
                        bound: ty::BuiltinBound,
@@ -595,7 +545,10 @@ impl<'tcx> ty::TyS<'tcx> {
                        -> bool
     {
         let tcx = param_env.tcx;
-        let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env.clone()));
+        let infcx = infer::new_infer_ctxt(tcx,
+                                          &tcx.tables,
+                                          Some(param_env.clone()),
+                                          ProjectionMode::AnyFinal);
 
         let is_impld = traits::type_known_to_meet_builtin_bound(&infcx,
                                                                 self, bound, span);
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index 4a1c115e65599..9186765e6d02c 100644
--- a/src/librustc_borrowck/borrowck/check_loans.rs
+++ b/src/librustc_borrowck/borrowck/check_loans.rs
@@ -27,6 +27,7 @@ use rustc::middle::mem_categorization as mc;
 use rustc::middle::mem_categorization::Categorization;
 use rustc::middle::region;
 use rustc::middle::ty::{self, TyCtxt};
+use rustc::middle::traits::ProjectionMode;
 use syntax::ast;
 use syntax::codemap::Span;
 use rustc_front::hir;
@@ -202,7 +203,10 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
     debug!("check_loans(body id={})", body.id);
 
     let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
-    let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env));
+    let infcx = infer::new_infer_ctxt(bccx.tcx,
+                                      &bccx.tcx.tables,
+                                      Some(param_env),
+                                      ProjectionMode::AnyFinal);
 
     let mut clcx = CheckLoanCtxt {
         bccx: bccx,
diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
index e2543b289103a..2d255c054548f 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
@@ -24,6 +24,7 @@ use rustc::middle::mem_categorization as mc;
 use rustc::middle::mem_categorization::Categorization;
 use rustc::middle::region;
 use rustc::middle::ty::{self, TyCtxt};
+use rustc::middle::traits::ProjectionMode;
 
 use syntax::ast;
 use syntax::codemap::Span;
@@ -55,7 +56,10 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
     };
 
     let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
-    let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env));
+    let infcx = infer::new_infer_ctxt(bccx.tcx,
+                                      &bccx.tcx.tables,
+                                      Some(param_env),
+                                      ProjectionMode::AnyFinal);
     {
         let mut euv = euv::ExprUseVisitor::new(&mut glcx, &infcx);
         euv.walk_fn(decl, body);
@@ -525,7 +529,10 @@ struct StaticInitializerCtxt<'a, 'tcx: 'a> {
 impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> {
     fn visit_expr(&mut self, ex: &Expr) {
         if let hir::ExprAddrOf(mutbl, ref base) = ex.node {
-            let infcx = infer::new_infer_ctxt(self.bccx.tcx, &self.bccx.tcx.tables, None);
+            let infcx = infer::new_infer_ctxt(self.bccx.tcx,
+                                              &self.bccx.tcx.tables,
+                                              None,
+                                              ProjectionMode::AnyFinal);
             let mc = mc::MemCategorizationContext::new(&infcx);
             let base_cmt = mc.cat_expr(&base).unwrap();
             let borrow_kind = ty::BorrowKind::from_mutbl(mutbl);
diff --git a/src/librustc_data_structures/obligation_forest/README.md b/src/librustc_data_structures/obligation_forest/README.md
index d76d7f6ba340e..982a2bacce164 100644
--- a/src/librustc_data_structures/obligation_forest/README.md
+++ b/src/librustc_data_structures/obligation_forest/README.md
@@ -60,7 +60,7 @@ which includes three bits of information:
   `process_obligations` would simply yield back further ambiguous
   results. This is used by the `FulfillmentContext` to decide when it
   has reached a steady state.
-  
+
 #### Snapshots
 
 The `ObligationForest` supports a limited form of snapshots; see
@@ -79,5 +79,3 @@ parent and (for convenience) its root (which may be itself). It also
 has a current state, described by `NodeState`. After each
 processing step, we compress the vector to remove completed and error
 nodes, which aren't needed anymore.
-
-  
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index 3cab9cfb88ca9..437672c551464 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -22,6 +22,7 @@ use rustc_typeck::middle::resolve_lifetime;
 use rustc_typeck::middle::stability;
 use rustc_typeck::middle::subst;
 use rustc_typeck::middle::subst::Subst;
+use rustc_typeck::middle::traits::ProjectionMode;
 use rustc_typeck::middle::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc_typeck::middle::ty::relate::TypeRelation;
 use rustc_typeck::middle::infer::{self, TypeOrigin};
@@ -143,7 +144,10 @@ fn test_env<F>(source_string: &str,
                                lang_items,
                                index,
                                |tcx| {
-                                   let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
+                                   let infcx = infer::new_infer_ctxt(tcx,
+                                                                     &tcx.tables,
+                                                                     None,
+                                                                     ProjectionMode::AnyFinal);
                                    body(Env { infcx: &infcx });
                                    let free_regions = FreeRegionMap::new();
                                    infcx.resolve_regions_and_report_errors(&free_regions,
diff --git a/src/librustc_front/fold.rs b/src/librustc_front/fold.rs
index beedb3d70b699..6ae59122f71c4 100644
--- a/src/librustc_front/fold.rs
+++ b/src/librustc_front/fold.rs
@@ -839,6 +839,7 @@ pub fn noop_fold_impl_item<T: Folder>(i: ImplItem, folder: &mut T) -> ImplItem {
         name: folder.fold_name(i.name),
         attrs: fold_attrs(i.attrs, folder),
         vis: i.vis,
+        defaultness: i.defaultness,
         node: match i.node {
             ImplItemKind::Const(ty, expr) => {
                 ImplItemKind::Const(folder.fold_ty(ty), folder.fold_expr(expr))
diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs
index cc7c0f7865ea5..0b1418fc87845 100644
--- a/src/librustc_front/hir.rs
+++ b/src/librustc_front/hir.rs
@@ -864,10 +864,10 @@ pub struct MethodSig {
     pub explicit_self: ExplicitSelf,
 }
 
-/// Represents a method declaration in a trait declaration, possibly including
-/// a default implementation A trait method is either required (meaning it
-/// doesn't have an implementation, just a signature) or provided (meaning it
-/// has a default implementation).
+/// Represents an item declaration within a trait declaration,
+/// possibly including a default implementation. A trait item is
+/// either required (meaning it doesn't have an implementation, just a
+/// signature) or provided (meaning it has a default implementation).
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct TraitItem {
     pub id: NodeId,
@@ -889,6 +889,7 @@ pub struct ImplItem {
     pub id: NodeId,
     pub name: Name,
     pub vis: Visibility,
+    pub defaultness: Defaultness,
     pub attrs: HirVec<Attribute>,
     pub node: ImplItemKind,
     pub span: Span,
@@ -1046,6 +1047,22 @@ pub enum Constness {
     NotConst,
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub enum Defaultness {
+    Default,
+    Final,
+}
+
+impl Defaultness {
+    pub fn is_final(&self) -> bool {
+        *self == Defaultness::Final
+    }
+
+    pub fn is_default(&self) -> bool {
+        *self == Defaultness::Default
+    }
+}
+
 impl fmt::Display for Unsafety {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt(match *self {
diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs
index 291df66755e7d..825ab3fbd4c82 100644
--- a/src/librustc_front/lowering.rs
+++ b/src/librustc_front/lowering.rs
@@ -756,6 +756,7 @@ pub fn lower_impl_item(lctx: &LoweringContext, i: &ImplItem) -> hir::ImplItem {
         name: i.ident.name,
         attrs: lower_attrs(lctx, &i.attrs),
         vis: lower_visibility(lctx, i.vis),
+        defaultness: lower_defaultness(lctx, i.defaultness),
         node: match i.node {
             ImplItemKind::Const(ref ty, ref expr) => {
                 hir::ImplItemKind::Const(lower_ty(lctx, ty), lower_expr(lctx, expr))
@@ -1707,6 +1708,13 @@ pub fn lower_visibility(_lctx: &LoweringContext, v: Visibility) -> hir::Visibili
     }
 }
 
+pub fn lower_defaultness(_lctx: &LoweringContext, d: Defaultness) -> hir::Defaultness {
+    match d {
+        Defaultness::Default => hir::Defaultness::Default,
+        Defaultness::Final => hir::Defaultness::Final,
+    }
+}
+
 pub fn lower_block_check_mode(lctx: &LoweringContext, b: &BlockCheckMode) -> hir::BlockCheckMode {
     match *b {
         BlockCheckMode::Default => hir::DefaultBlock,
diff --git a/src/librustc_front/print/pprust.rs b/src/librustc_front/print/pprust.rs
index 143dfce09b602..1100f084454c7 100644
--- a/src/librustc_front/print/pprust.rs
+++ b/src/librustc_front/print/pprust.rs
@@ -1014,6 +1014,11 @@ impl<'a> State<'a> {
         try!(self.hardbreak_if_not_bol());
         try!(self.maybe_print_comment(ii.span.lo));
         try!(self.print_outer_attributes(&ii.attrs));
+
+        if let hir::Defaultness::Default = ii.defaultness {
+            try!(self.word_nbsp("default"));
+        }
+
         match ii.node {
             hir::ImplItemKind::Const(ref ty, ref expr) => {
                 try!(self.print_associated_const(ii.name, &ty, Some(&expr), ii.vis));
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 0c906f8eb546c..88027931022e7 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -35,6 +35,7 @@ use middle::def_id::DefId;
 use middle::subst::Substs;
 use middle::ty::{self, Ty, TyCtxt};
 use middle::ty::adjustment;
+use middle::traits::ProjectionMode;
 use rustc::front::map as hir_map;
 use util::nodemap::{NodeSet};
 use lint::{Level, LateContext, LintContext, LintArray, Lint};
@@ -868,7 +869,10 @@ impl LateLintPass for UnconditionalRecursion {
                     let node_id = tcx.map.as_local_node_id(method.def_id).unwrap();
 
                     let param_env = ty::ParameterEnvironment::for_item(tcx, node_id);
-                    let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env));
+                    let infcx = infer::new_infer_ctxt(tcx,
+                                                      &tcx.tables,
+                                                      Some(param_env),
+                                                      ProjectionMode::AnyFinal);
                     let mut selcx = traits::SelectionContext::new(&infcx);
                     match selcx.select(&obligation) {
                         // The method comes from a `T: Trait` bound.
diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs
index 991cbe137ecf9..a0cbba279acc0 100644
--- a/src/librustc_metadata/common.rs
+++ b/src/librustc_metadata/common.rs
@@ -241,6 +241,10 @@ pub const tag_items_data_item_constness: usize = 0xa6;
 
 pub const tag_items_data_item_deprecation: usize = 0xa7;
 
+pub const tag_items_data_item_defaultness: usize = 0xa8;
+
+pub const tag_items_data_parent_impl: usize = 0xa9;
+
 pub const tag_rustc_version: usize = 0x10f;
 pub fn rustc_version() -> String {
     format!(
diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs
index b3f24b8f16b16..2cd119cfc48be 100644
--- a/src/librustc_metadata/csearch.rs
+++ b/src/librustc_metadata/csearch.rs
@@ -225,6 +225,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::get_associated_consts(self.intr.clone(), &cdata, def.index, tcx)
     }
 
+    fn impl_parent(&self, impl_def: DefId) -> Option<DefId> {
+        let cdata = self.get_crate_data(impl_def.krate);
+        decoder::get_parent_impl(&*cdata, impl_def.index)
+    }
+
     fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId) -> Option<DefId>
     {
         let cdata = self.get_crate_data(def_id.krate);
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index dfc794dc5b874..38a2a7794bcbd 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -165,6 +165,19 @@ fn fn_constness(item: rbml::Doc) -> hir::Constness {
     }
 }
 
+fn item_defaultness(item: rbml::Doc) -> hir::Defaultness {
+    match reader::maybe_get_doc(item, tag_items_data_item_defaultness) {
+        None => hir::Defaultness::Default, // should occur only for default impls on traits
+        Some(defaultness_doc) => {
+            match reader::doc_as_u8(defaultness_doc) as char {
+                'd' => hir::Defaultness::Default,
+                'f' => hir::Defaultness::Final,
+                _ => panic!("unknown defaultness character")
+            }
+        }
+    }
+}
+
 fn item_sort(item: rbml::Doc) -> Option<char> {
     reader::tagged_docs(item, tag_item_trait_item_sort).nth(0).map(|doc| {
         doc.as_str_slice().as_bytes()[0] as char
@@ -551,6 +564,13 @@ pub fn get_visibility(cdata: Cmd, id: DefIndex) -> hir::Visibility {
     item_visibility(cdata.lookup_item(id))
 }
 
+pub fn get_parent_impl(cdata: Cmd, id: DefIndex) -> Option<DefId> {
+    let item = cdata.lookup_item(id);
+    reader::maybe_get_doc(item, tag_items_data_parent_impl).map(|doc| {
+        translated_def_id(cdata, doc)
+    })
+}
+
 pub fn get_repr_attrs(cdata: Cmd, id: DefIndex) -> Vec<attr::ReprAttr> {
     let item = cdata.lookup_item(id);
     match reader::maybe_get_doc(item, tag_items_data_item_repr).map(|doc| {
@@ -976,6 +996,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc<IdentInterner>,
 
     let name = item_name(&intr, item_doc);
     let vis = item_visibility(item_doc);
+    let defaultness = item_defaultness(item_doc);
 
     match item_sort(item_doc) {
         sort @ Some('C') | sort @ Some('c') => {
@@ -984,6 +1005,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc<IdentInterner>,
                 name: name,
                 ty: ty,
                 vis: vis,
+                defaultness: defaultness,
                 def_id: def_id,
                 container: container,
                 has_value: sort == Some('C')
@@ -1007,6 +1029,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc<IdentInterner>,
                                                         fty,
                                                         explicit_self,
                                                         vis,
+                                                        defaultness,
                                                         def_id,
                                                         container)))
         }
@@ -1016,6 +1039,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc<IdentInterner>,
                 name: name,
                 ty: ty,
                 vis: vis,
+                defaultness: defaultness,
                 def_id: def_id,
                 container: container,
             }))
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index 3dc0e53b254c4..41baa0b159148 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -25,6 +25,7 @@ use middle::def_id::{CRATE_DEF_INDEX, DefId};
 use middle::dependency_format::Linkage;
 use middle::stability;
 use middle::subst;
+use middle::traits::specialization_graph;
 use middle::ty::{self, Ty, TyCtxt};
 use middle::ty::util::IntTypeExt;
 
@@ -451,6 +452,14 @@ fn encode_constness(rbml_w: &mut Encoder, constness: hir::Constness) {
     rbml_w.end_tag();
 }
 
+fn encode_defaultness(rbml_w: &mut Encoder, defaultness: hir::Defaultness) {
+    let ch = match defaultness {
+        hir::Defaultness::Default => 'd',
+        hir::Defaultness::Final => 'f',
+    };
+    rbml_w.wr_tagged_u8(tag_items_data_item_defaultness, ch as u8);
+}
+
 fn encode_explicit_self(rbml_w: &mut Encoder,
                         explicit_self: &ty::ExplicitSelfCategory) {
     let tag = tag_item_trait_method_explicit_self;
@@ -674,6 +683,7 @@ fn encode_info_for_associated_const<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
 
     if let Some(ii) = impl_item_opt {
         encode_attributes(rbml_w, &ii.attrs);
+        encode_defaultness(rbml_w, ii.defaultness);
         encode_inlined_item(ecx,
                             rbml_w,
                             InlinedItemRef::ImplItem(ecx.tcx.map.local_def_id(parent_id),
@@ -725,6 +735,7 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                                                              impl_item));
             }
             encode_constness(rbml_w, sig.constness);
+            encode_defaultness(rbml_w, impl_item.defaultness);
             if !any_types {
                 let m_id = ecx.local_id(m.def_id);
                 encode_symbol(ecx, rbml_w, m_id);
@@ -767,6 +778,7 @@ fn encode_info_for_associated_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
 
     if let Some(ii) = impl_item_opt {
         encode_attributes(rbml_w, &ii.attrs);
+        encode_defaultness(rbml_w, ii.defaultness);
     } else {
         encode_predicates(rbml_w, ecx, index,
                           &ecx.tcx.lookup_predicates(associated_type.def_id),
@@ -873,6 +885,12 @@ fn encode_deprecation(rbml_w: &mut Encoder, depr_opt: Option<attr::Deprecation>)
     });
 }
 
+fn encode_parent_impl(rbml_w: &mut Encoder, parent_opt: Option<DefId>) {
+    parent_opt.map(|parent| {
+        rbml_w.wr_tagged_u64(tag_items_data_parent_impl, def_to_u64(parent));
+    });
+}
+
 fn encode_xrefs<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                           rbml_w: &mut Encoder,
                           xrefs: FnvHashMap<XRef<'tcx>, u32>)
@@ -1150,8 +1168,19 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
             }
             rbml_w.end_tag();
         }
-        if let Some(trait_ref) = tcx.impl_trait_ref(ecx.tcx.map.local_def_id(item.id)) {
+        let did = ecx.tcx.map.local_def_id(item.id);
+        if let Some(trait_ref) = tcx.impl_trait_ref(did) {
             encode_trait_ref(rbml_w, ecx, trait_ref, tag_item_trait_ref);
+
+            let trait_def = tcx.lookup_trait_def(trait_ref.def_id);
+            let parent = trait_def.ancestors(did)
+                .skip(1)
+                .next()
+                .and_then(|node| match node {
+                    specialization_graph::Node::Impl(parent) => Some(parent),
+                    _ => None,
+                });
+            encode_parent_impl(rbml_w, parent);
         }
         encode_path(rbml_w, path.clone());
         encode_stability(rbml_w, stab);
diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs
index 2e13e7b42bd6c..13521de78af28 100644
--- a/src/librustc_mir/mir_map.rs
+++ b/src/librustc_mir/mir_map.rs
@@ -27,6 +27,7 @@ use hair::cx::Cx;
 use rustc::mir::mir_map::MirMap;
 use rustc::middle::infer;
 use rustc::middle::region::CodeExtentData;
+use rustc::middle::traits::ProjectionMode;
 use rustc::middle::ty::{self, Ty, TyCtxt};
 use rustc::util::common::ErrorReported;
 use rustc::util::nodemap::NodeMap;
@@ -137,7 +138,11 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
         };
 
         let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
-        let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));
+        let infcx = infer::new_infer_ctxt(self.tcx,
+                                          &self.tcx.tables,
+                                          Some(param_env),
+                                          ProjectionMode::AnyFinal);
+
         match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
             Ok(mir) => assert!(self.map.map.insert(id, mir).is_none()),
             Err(ErrorReported) => {}
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index 45393d57101e5..d99e6ff4bf55d 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -13,7 +13,7 @@
 
 use rustc::dep_graph::DepNode;
 use rustc::middle::infer::{self, InferCtxt};
-use rustc::middle::traits;
+use rustc::middle::traits::{self, ProjectionMode};
 use rustc::middle::ty::fold::TypeFoldable;
 use rustc::middle::ty::{self, Ty, TyCtxt};
 use rustc::mir::repr::*;
@@ -582,7 +582,10 @@ impl<'tcx> MirPass<'tcx> for TypeckMir {
         }
         let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(id));
         let param_env = ty::ParameterEnvironment::for_item(tcx, id);
-        let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env));
+        let infcx = infer::new_infer_ctxt(tcx,
+                                          &tcx.tables,
+                                          Some(param_env),
+                                          ProjectionMode::AnyFinal);
         let mut checker = TypeChecker::new(&infcx);
         {
             let mut verifier = TypeVerifier::new(&mut checker, mir);
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index a9e03bba7db8e..6be7f6c200247 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -35,8 +35,8 @@ use rustc::middle::expr_use_visitor as euv;
 use rustc::middle::infer;
 use rustc::middle::mem_categorization as mc;
 use rustc::middle::mem_categorization::Categorization;
-use rustc::middle::traits;
 use rustc::middle::ty::{self, Ty, TyCtxt};
+use rustc::middle::traits::{self, ProjectionMode};
 use rustc::util::nodemap::NodeMap;
 use rustc::middle::const_qualif::ConstQualif;
 use rustc::lint::builtin::CONST_ERR;
@@ -92,7 +92,10 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
             None => self.tcx.empty_parameter_environment()
         };
 
-        let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));
+        let infcx = infer::new_infer_ctxt(self.tcx,
+                                          &self.tcx.tables,
+                                          Some(param_env),
+                                          ProjectionMode::AnyFinal);
 
         f(&mut euv::ExprUseVisitor::new(self, &infcx))
     }
@@ -247,7 +250,10 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
 
     fn check_static_type(&self, e: &hir::Expr) {
         let ty = self.tcx.node_id_to_type(e.id);
-        let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
+        let infcx = infer::new_infer_ctxt(self.tcx,
+                                          &self.tcx.tables,
+                                          None,
+                                          ProjectionMode::AnyFinal);
         let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
         let mut fulfillment_cx = traits::FulfillmentContext::new();
         fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
diff --git a/src/librustc_passes/rvalues.rs b/src/librustc_passes/rvalues.rs
index 7eef69ca50fd0..88048b514e1f5 100644
--- a/src/librustc_passes/rvalues.rs
+++ b/src/librustc_passes/rvalues.rs
@@ -16,6 +16,7 @@ use rustc::middle::expr_use_visitor as euv;
 use rustc::middle::infer;
 use rustc::middle::mem_categorization as mc;
 use rustc::middle::ty::{self, TyCtxt, ParameterEnvironment};
+use rustc::middle::traits::ProjectionMode;
 
 use rustc_front::hir;
 use rustc_front::intravisit;
@@ -43,7 +44,8 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for RvalueContext<'a, 'tcx> {
             let param_env = ParameterEnvironment::for_item(self.tcx, fn_id);
             let infcx = infer::new_infer_ctxt(self.tcx,
                                               &self.tcx.tables,
-                                              Some(param_env.clone()));
+                                              Some(param_env.clone()),
+                                              ProjectionMode::AnyFinal);
             let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: &param_env };
             let mut euv = euv::ExprUseVisitor::new(&mut delegate, &infcx);
             euv.walk_fn(fd, b);
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index d1567cc6fa543..f5fbec0b1879e 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -217,6 +217,7 @@ use trans::tvec;
 use trans::type_of;
 use trans::Disr;
 use middle::ty::{self, Ty, TyCtxt};
+use middle::traits::ProjectionMode;
 use session::config::NoDebugInfo;
 use util::common::indenter;
 use util::nodemap::FnvHashMap;
@@ -1475,7 +1476,9 @@ fn is_discr_reassigned(bcx: Block, discr: &hir::Expr, body: &hir::Expr) -> bool
         reassigned: false
     };
     {
-        let infcx = infer::normalizing_infer_ctxt(bcx.tcx(), &bcx.tcx().tables);
+        let infcx = infer::normalizing_infer_ctxt(bcx.tcx(),
+                                                  &bcx.tcx().tables,
+                                                  ProjectionMode::Any);
         let mut visitor = euv::ExprUseVisitor::new(&mut rc, &infcx);
         visitor.walk_expr(body);
     }
diff --git a/src/librustc_trans/trans/attributes.rs b/src/librustc_trans/trans/attributes.rs
index 009d43e813ebc..d93d32f8e0d06 100644
--- a/src/librustc_trans/trans/attributes.rs
+++ b/src/librustc_trans/trans/attributes.rs
@@ -13,6 +13,7 @@ use libc::{c_uint, c_ulonglong};
 use llvm::{self, ValueRef, AttrHelper};
 use middle::ty;
 use middle::infer;
+use middle::traits::ProjectionMode;
 use session::config::NoDebugInfo;
 use syntax::abi::Abi;
 pub use syntax::attr::InlineAttr;
@@ -133,7 +134,9 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx
     let (fn_sig, abi, env_ty) = match fn_type.sty {
         ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => (&f.sig, f.abi, None),
         ty::TyClosure(closure_did, ref substs) => {
-            let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables);
+            let infcx = infer::normalizing_infer_ctxt(ccx.tcx(),
+                                                      &ccx.tcx().tables,
+                                                      ProjectionMode::Any);
             function_type = infcx.closure_type(closure_did, substs);
             let self_type = base::self_type_for_closure(ccx, closure_did, fn_type);
             (&function_type.sig, Abi::RustCall, Some(self_type))
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs
index 95ca250e84445..11c03fe7a7dc7 100644
--- a/src/librustc_trans/trans/closure.rs
+++ b/src/librustc_trans/trans/closure.rs
@@ -13,6 +13,7 @@ use back::link::{self, mangle_internal_name_by_path_and_seq};
 use llvm::{ValueRef, get_params};
 use middle::def_id::DefId;
 use middle::infer;
+use middle::traits::ProjectionMode;
 use trans::adt;
 use trans::attributes;
 use trans::base::*;
@@ -206,7 +207,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
     // this function (`trans_closure`) is invoked at the point
     // of the closure expression.
 
-    let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables);
+    let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any);
     let function_type = infcx.closure_type(closure_def_id, closure_substs);
 
     let freevars: Vec<ty::Freevar> =
@@ -329,7 +330,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
            ccx.tn().val_to_string(llreffn));
 
     let tcx = ccx.tcx();
-    let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables);
+    let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any);
 
     // Find a version of the closure type. Substitute static for the
     // region since it doesn't really matter.
diff --git a/src/librustc_trans/trans/collector.rs b/src/librustc_trans/trans/collector.rs
index abfd127f38860..cea97c1a1e77f 100644
--- a/src/librustc_trans/trans/collector.rs
+++ b/src/librustc_trans/trans/collector.rs
@@ -819,10 +819,11 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             nested: _ }) =>
         {
             let callee_substs = impl_substs.with_method_from(&rcvr_substs);
-            let impl_method = tcx.get_impl_method(impl_did,
-                                                  tcx.mk_substs(callee_substs),
-                                                  trait_method.name);
-            Some((impl_method.method.def_id, impl_method.substs))
+            let impl_method = meth::get_impl_method(tcx,
+                                                    impl_did,
+                                                    tcx.mk_substs(callee_substs),
+                                                    trait_method.name);
+            Some((impl_method.method.def_id, &impl_method.substs))
         }
         // If we have a closure or a function pointer, we will also encounter
         // the concrete closure/function somewhere else (during closure or fn
@@ -982,7 +983,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                             if can_have_local_instance(ccx, impl_method.method.def_id) {
                                 Some(create_fn_trans_item(ccx,
                                                           impl_method.method.def_id,
-                                                          impl_method.substs,
+                                                          &impl_method.substs,
                                                           &Substs::trans_empty()))
                             } else {
                                 None
@@ -1160,13 +1161,14 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
                     // The substitutions we have are on the impl, so we grab
                     // the method type from the impl to substitute into.
-                    let mth = tcx.get_impl_method(impl_def_id,
-                                                  callee_substs,
-                                                  default_impl.name);
+                    let mth = meth::get_impl_method(tcx,
+                                                    impl_def_id,
+                                                    callee_substs,
+                                                    default_impl.name);
 
                     assert!(mth.is_provided);
 
-                    let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs);
+                    let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs);
                     if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
                         continue;
                     }
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index 34ef4f4acec5e..0aa69dec253a1 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -37,8 +37,8 @@ use trans::machine;
 use trans::monomorphize;
 use trans::type_::Type;
 use trans::type_of;
-use middle::traits;
 use middle::ty::{self, Ty, TyCtxt};
+use middle::traits::{self, SelectionContext, ProjectionMode};
 use middle::ty::fold::{TypeFolder, TypeFoldable};
 use rustc_front::hir;
 use rustc::mir::repr::Mir;
@@ -1137,8 +1137,8 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
     // Do the initial selection for the obligation. This yields the
     // shallow result we are looking for -- that is, what specific impl.
-    let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables);
-    let mut selcx = traits::SelectionContext::new(&infcx);
+    let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any);
+    let mut selcx = SelectionContext::new(&infcx);
 
     let obligation =
         traits::Obligation::new(traits::ObligationCause::misc(span, ast::DUMMY_NODE_ID),
@@ -1198,8 +1198,8 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
            predicates);
 
     let tcx = ccx.tcx();
-    let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables);
-    let mut selcx = traits::SelectionContext::new(&infcx);
+    let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any);
+    let mut selcx = SelectionContext::new(&infcx);
     let mut fulfill_cx = traits::FulfillmentContext::new();
     let cause = traits::ObligationCause::dummy();
     let traits::Normalized { value: predicates, obligations } =
diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs
index 38e456c068829..0c512200ff3d5 100644
--- a/src/librustc_trans/trans/declare.rs
+++ b/src/librustc_trans/trans/declare.rs
@@ -22,6 +22,7 @@
 use llvm::{self, ValueRef};
 use middle::ty;
 use middle::infer;
+use middle::traits::ProjectionMode;
 use syntax::abi::Abi;
 use trans::attributes;
 use trans::base;
@@ -111,7 +112,9 @@ pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str,
             (&f.sig, f.abi, None)
         }
         ty::TyClosure(closure_did, ref substs) => {
-            let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables);
+            let infcx = infer::normalizing_infer_ctxt(ccx.tcx(),
+                                                      &ccx.tcx().tables,
+                                                      ProjectionMode::Any);
             function_type = infcx.closure_type(closure_did, substs);
             let self_type = base::self_type_for_closure(ccx, closure_did, fn_type);
             let llenvironment_type = type_of::type_of_explicit_arg(ccx, self_type);
diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs
index 78b86dafa18ad..7397ccc2505f1 100644
--- a/src/librustc_trans/trans/meth.rs
+++ b/src/librustc_trans/trans/meth.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::rc::Rc;
+
 use arena::TypedArena;
 use back::link;
 use llvm::{ValueRef, get_params};
@@ -15,7 +17,7 @@ use middle::def_id::DefId;
 use middle::infer;
 use middle::subst::{Subst, Substs};
 use middle::subst;
-use middle::traits;
+use middle::traits::{self, ProjectionMode};
 use trans::base::*;
 use trans::build::*;
 use trans::callee::{Callee, Virtual, ArgVals,
@@ -31,9 +33,9 @@ use trans::glue;
 use trans::machine;
 use trans::type_::Type;
 use trans::type_of::*;
-use middle::ty::{self, Ty, TyCtxt};
+use middle::ty::{self, Ty, TyCtxt, TypeFoldable};
 
-use syntax::ast;
+use syntax::ast::{self, Name};
 use syntax::attr;
 use syntax::codemap::DUMMY_SP;
 
@@ -107,7 +109,7 @@ pub fn callee_for_trait_impl<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             // those from the impl and those from the method:
             let impl_substs = vtable_impl.substs.with_method_from(&substs);
             let substs = ccx.tcx().mk_substs(impl_substs);
-            let mth = ccx.tcx().get_impl_method(impl_did, substs, mname);
+            let mth = get_impl_method(ccx.tcx(), impl_did, substs, mname);
 
             // Translate the function, bypassing Callee::def.
             // That is because default methods have the same ID as the
@@ -315,7 +317,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 trans_fn_ref_with_substs(ccx,
                                                          mth.method.def_id,
                                                          None,
-                                                         mth.substs).val
+                                                         &mth.substs).val
                             }
                             None => nullptr
                         }
@@ -378,7 +380,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                     impl_id: DefId,
                                     substs: &'tcx subst::Substs<'tcx>)
-                                    -> Vec<Option<ty::util::ImplMethod<'tcx>>>
+                                    -> Vec<Option<ImplMethod<'tcx>>>
 {
     let tcx = ccx.tcx();
 
@@ -428,7 +430,7 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
             // The substitutions we have are on the impl, so we grab
             // the method type from the impl to substitute into.
-            let mth = tcx.get_impl_method(impl_id, substs, name);
+            let mth = get_impl_method(tcx, impl_id, substs, name);
 
             debug!("get_vtable_methods: mth={:?}", mth);
 
@@ -438,7 +440,7 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             // method could then never be called, so we do not want to
             // try and trans it, in that case. Issue #23435.
             if mth.is_provided {
-                let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs);
+                let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs);
                 if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
                     debug!("get_vtable_methods: predicates do not hold");
                     return None;
@@ -466,3 +468,37 @@ fn opaque_method_ty<'tcx>(tcx: &TyCtxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>)
         }),
     })
 }
+
+#[derive(Debug)]
+pub struct ImplMethod<'tcx> {
+    pub method: Rc<ty::Method<'tcx>>,
+    pub substs: &'tcx Substs<'tcx>,
+    pub is_provided: bool
+}
+
+/// Locates the applicable definition of a method, given its name.
+pub fn get_impl_method<'tcx>(tcx: &TyCtxt<'tcx>,
+                             impl_def_id: DefId,
+                             substs: &'tcx Substs<'tcx>,
+                             name: Name)
+                             -> ImplMethod<'tcx>
+{
+    assert!(!substs.types.needs_infer());
+
+    let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
+    let trait_def = tcx.lookup_trait_def(trait_def_id);
+    let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any);
+
+    match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() {
+        Some(node_item) => {
+            ImplMethod {
+                method: node_item.item,
+                substs: traits::translate_substs(&infcx, impl_def_id, substs, node_item.node),
+                is_provided: node_item.node.is_from_trait(),
+            }
+        }
+        None => {
+            tcx.sess.bug(&format!("method {:?} not found in {:?}", name, impl_def_id))
+        }
+    }
+}
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index ff7b809577f64..899f79b3dff94 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -10,8 +10,8 @@
 
 use middle::free_region::FreeRegionMap;
 use middle::infer::{self, TypeOrigin};
-use middle::traits;
 use middle::ty::{self, TyCtxt};
+use middle::traits::{self, ProjectionMode};
 use middle::subst::{self, Subst, Substs, VecPerParamSpace};
 
 use syntax::ast;
@@ -42,7 +42,7 @@ pub fn compare_impl_method<'tcx>(tcx: &TyCtxt<'tcx>,
     debug!("compare_impl_method: impl_trait_ref (liberated) = {:?}",
            impl_trait_ref);
 
-    let mut infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
+    let mut infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal);
     let mut fulfillment_cx = traits::FulfillmentContext::new();
 
     let trait_to_impl_substs = &impl_trait_ref.substs;
@@ -416,7 +416,7 @@ pub fn compare_const_impl<'tcx>(tcx: &TyCtxt<'tcx>,
     debug!("compare_const_impl(impl_trait_ref={:?})",
            impl_trait_ref);
 
-    let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
+    let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal);
     let mut fulfillment_cx = traits::FulfillmentContext::new();
 
     // The below is for the most part highly similar to the procedure
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 4ebe4c25dd1d3..4ed1bab46b2d9 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -15,8 +15,8 @@ use middle::free_region::FreeRegionMap;
 use middle::infer;
 use middle::region;
 use middle::subst::{self, Subst};
-use middle::traits;
 use middle::ty::{self, Ty, TyCtxt};
+use middle::traits::{self, ProjectionMode};
 use util::nodemap::FnvHashSet;
 
 use syntax::ast;
@@ -82,7 +82,10 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
     // check that the impl type can be made to match the trait type.
 
     let impl_param_env = ty::ParameterEnvironment::for_item(tcx, self_type_node_id);
-    let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(impl_param_env));
+    let infcx = infer::new_infer_ctxt(tcx,
+                                      &tcx.tables,
+                                      Some(impl_param_env),
+                                      ProjectionMode::AnyFinal);
     let mut fulfillment_cx = traits::FulfillmentContext::new();
 
     let named_type = tcx.lookup_item_type(self_type_did).ty;
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 19731407d9e7a..0d5e25efd68c9 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -92,7 +92,7 @@ use middle::infer;
 use middle::infer::{TypeOrigin, TypeTrace, type_variable};
 use middle::pat_util::{self, pat_id_map};
 use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace};
-use middle::traits::{self, report_fulfillment_errors};
+use middle::traits::{self, report_fulfillment_errors, ProjectionMode};
 use middle::ty::{GenericPredicates, TypeScheme};
 use middle::ty::{ParamTy, ParameterEnvironment};
 use middle::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
@@ -307,7 +307,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
            -> Inherited<'a, 'tcx> {
 
         Inherited {
-            infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env)),
+            infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env), ProjectionMode::AnyFinal),
             fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()),
             locals: RefCell::new(NodeMap()),
             tables: tables,
@@ -672,10 +672,12 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
       hir::ItemFn(..) => {} // entirely within check_item_body
       hir::ItemImpl(_, _, _, _, _, ref impl_items) => {
           debug!("ItemImpl {} with id {}", it.name, it.id);
-          match ccx.tcx.impl_trait_ref(ccx.tcx.map.local_def_id(it.id)) {
+          let impl_def_id = ccx.tcx.map.local_def_id(it.id);
+          match ccx.tcx.impl_trait_ref(impl_def_id) {
               Some(impl_trait_ref) => {
                 check_impl_items_against_trait(ccx,
                                                it.span,
+                                               impl_def_id,
                                                &impl_trait_ref,
                                                impl_items);
               }
@@ -862,12 +864,71 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     check_bare_fn(ccx, &sig.decl, body, id, span, fty, param_env);
 }
 
+fn report_forbidden_specialization(tcx: &TyCtxt,
+                                   impl_item: &hir::ImplItem,
+                                   parent_impl: DefId)
+{
+    let mut err = struct_span_err!(
+        tcx.sess, impl_item.span, E0520,
+        "item `{}` is provided by an `impl` that specializes \
+         another, but the item in the parent `impl` is not \
+         marked `default` and so it cannot be specialized.",
+        impl_item.name);
+
+    match tcx.span_of_impl(parent_impl) {
+        Ok(span) => {
+            err.span_note(span, "parent implementation is here:");
+        }
+        Err(cname) => {
+            err.note(&format!("parent implementation is in crate `{}`", cname));
+        }
+    }
+
+    err.emit();
+}
+
+fn check_specialization_validity<'tcx>(tcx: &TyCtxt<'tcx>, trait_def: &ty::TraitDef<'tcx>,
+                                       impl_id: DefId, impl_item: &hir::ImplItem)
+{
+    let ancestors = trait_def.ancestors(impl_id);
+
+    let parent = match impl_item.node {
+        hir::ImplItemKind::Const(..) => {
+            ancestors.const_defs(tcx, impl_item.name).skip(1).next()
+                .map(|node_item| node_item.map(|parent| parent.defaultness))
+        }
+        hir::ImplItemKind::Method(..) => {
+            ancestors.fn_defs(tcx, impl_item.name).skip(1).next()
+                .map(|node_item| node_item.map(|parent| parent.defaultness))
+
+        }
+        hir::ImplItemKind::Type(_) => {
+            ancestors.type_defs(tcx, impl_item.name).skip(1).next()
+                .map(|node_item| node_item.map(|parent| parent.defaultness))
+        }
+    };
+
+    if let Some(parent) = parent {
+        if parent.item.is_final() {
+            report_forbidden_specialization(tcx, impl_item, parent.node.def_id());
+        }
+    }
+
+}
+
 fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                             impl_span: Span,
+                                            impl_id: DefId,
                                             impl_trait_ref: &ty::TraitRef<'tcx>,
                                             impl_items: &[hir::ImplItem]) {
-    // Locate trait methods
+    // If the trait reference itself is erroneous (so the compilation is going
+    // to fail), skip checking the items here -- the `impl_item` table in `tcx`
+    // isn't populated for such impls.
+    if impl_trait_ref.references_error() { return; }
+
+    // Locate trait definition and items
     let tcx = ccx.tcx;
+    let trait_def = tcx.lookup_trait_def(impl_trait_ref.def_id);
     let trait_items = tcx.trait_items(impl_trait_ref.def_id);
     let mut overridden_associated_type = None;
 
@@ -878,6 +939,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
         let ty_trait_item = trait_items.iter()
             .find(|ac| ac.name() == ty_impl_item.name());
 
+        // Check that impl definition matches trait definition
         if let Some(ty_trait_item) = ty_trait_item {
             match impl_item.node {
                 hir::ImplItemKind::Const(..) => {
@@ -944,6 +1006,8 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                 }
             }
         }
+
+        check_specialization_validity(tcx, trait_def, impl_id, impl_item);
     }
 
     // Check for missing items from trait
@@ -952,9 +1016,13 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     let mut invalidated_items = Vec::new();
     let associated_type_overridden = overridden_associated_type.is_some();
     for trait_item in trait_items.iter() {
+        let is_implemented;
+        let is_provided;
+
         match *trait_item {
             ty::ConstTraitItem(ref associated_const) => {
-                let is_implemented = impl_items.iter().any(|ii| {
+                is_provided = associated_const.has_value;
+                is_implemented = impl_items.iter().any(|ii| {
                     match ii.node {
                         hir::ImplItemKind::Const(..) => {
                             ii.name == associated_const.name
@@ -962,53 +1030,30 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                         _ => false,
                     }
                 });
-                let is_provided = associated_const.has_value;
-
-                if !is_implemented {
-                    if !is_provided {
-                        missing_items.push(associated_const.name);
-                    } else if associated_type_overridden {
-                        invalidated_items.push(associated_const.name);
-                    }
-                }
             }
             ty::MethodTraitItem(ref trait_method) => {
-                let is_implemented =
-                    impl_items.iter().any(|ii| {
-                        match ii.node {
-                            hir::ImplItemKind::Method(..) => {
-                                ii.name == trait_method.name
-                            }
-                            _ => false,
-                        }
-                    });
-                let is_provided =
-                    provided_methods.iter().any(|m| m.name == trait_method.name);
-                if !is_implemented {
-                    if !is_provided {
-                        missing_items.push(trait_method.name);
-                    } else if associated_type_overridden {
-                        invalidated_items.push(trait_method.name);
-                    }
-                }
+                is_provided = provided_methods.iter().any(|m| m.name == trait_method.name);
+                is_implemented = trait_def.ancestors(impl_id)
+                    .fn_defs(tcx, trait_method.name)
+                    .next()
+                    .map(|node_item| !node_item.node.is_from_trait())
+                    .unwrap_or(false);
             }
-            ty::TypeTraitItem(ref associated_type) => {
-                let is_implemented = impl_items.iter().any(|ii| {
-                    match ii.node {
-                        hir::ImplItemKind::Type(_) => {
-                            ii.name == associated_type.name
-                        }
-                        _ => false,
-                    }
-                });
-                let is_provided = associated_type.ty.is_some();
-                if !is_implemented {
-                    if !is_provided {
-                        missing_items.push(associated_type.name);
-                    } else if associated_type_overridden {
-                        invalidated_items.push(associated_type.name);
-                    }
-                }
+            ty::TypeTraitItem(ref trait_assoc_ty) => {
+                is_provided = trait_assoc_ty.ty.is_some();
+                is_implemented = trait_def.ancestors(impl_id)
+                    .type_defs(tcx, trait_assoc_ty.name)
+                    .next()
+                    .map(|node_item| !node_item.node.is_from_trait())
+                    .unwrap_or(false);
+            }
+        }
+
+        if !is_implemented {
+            if !is_provided {
+                missing_items.push(trait_item.name());
+            } else if associated_type_overridden {
+                invalidated_items.push(trait_item.name());
             }
         }
     }
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index 9dc8d7ae943a1..278d4d8b5b44a 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -15,12 +15,11 @@
 // done by the orphan and overlap modules. Then we build up various
 // mappings. That mapping code resides here.
 
-
 use middle::def_id::DefId;
 use middle::lang_items::UnsizeTraitLangItem;
 use middle::subst::{self, Subst};
-use middle::traits;
 use middle::ty::{self, TyCtxt, TypeFoldable};
+use middle::traits::{self, ProjectionMode};
 use middle::ty::{ImplOrTraitItemId, ConstTraitItemId};
 use middle::ty::{MethodTraitItemId, TypeTraitItemId, ParameterEnvironment};
 use middle::ty::{Ty, TyBool, TyChar, TyEnum, TyError};
@@ -197,7 +196,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
         debug!("add_trait_impl: impl_trait_ref={:?} impl_def_id={:?}",
                impl_trait_ref, impl_def_id);
         let trait_def = self.crate_context.tcx.lookup_trait_def(impl_trait_ref.def_id);
-        trait_def.record_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref);
+        trait_def.record_local_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref);
     }
 
     // Converts an implementation in the AST to a vector of items.
@@ -385,7 +384,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
             debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)",
                    source, target);
 
-            let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env));
+            let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env), ProjectionMode::Topmost);
 
             let origin = TypeOrigin::Misc(span);
             let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, mt_b: ty::TypeAndMut<'tcx>,
@@ -530,7 +529,10 @@ pub fn report_duplicate_item<'tcx>(tcx: &TyCtxt<'tcx>, sp: Span, name: ast::Name
 
 pub fn check_coherence(crate_context: &CrateCtxt) {
     let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence);
-    let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None);
+    let infcx = new_infer_ctxt(crate_context.tcx,
+                               &crate_context.tcx.tables,
+                               None,
+                               ProjectionMode::Topmost);
     CoherenceChecker {
         crate_context: crate_context,
         inference_context: infcx,
diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs
index 80430076f197d..f7fa3e1b1429a 100644
--- a/src/librustc_typeck/coherence/overlap.rs
+++ b/src/librustc_typeck/coherence/overlap.rs
@@ -12,21 +12,20 @@
 //! same type. Likewise, no two inherent impls for a given type
 //! constructor provide a method with the same name.
 
-use middle::cstore::{CrateStore, LOCAL_CRATE};
+use middle::cstore::CrateStore;
 use middle::def_id::DefId;
-use middle::traits;
-use middle::ty::{self, TyCtxt};
+use middle::traits::{self, ProjectionMode};
 use middle::infer;
+use middle::ty::{self, TyCtxt};
 use syntax::ast;
 use syntax::codemap::Span;
 use rustc::dep_graph::DepNode;
 use rustc_front::hir;
 use rustc_front::intravisit;
-use util::nodemap::{DefIdMap, DefIdSet};
+use util::nodemap::DefIdMap;
 
 pub fn check(tcx: &TyCtxt) {
     let mut overlap = OverlapChecker { tcx: tcx,
-                                       traits_checked: DefIdSet(),
                                        default_impls: DefIdMap() };
 
     // this secondary walk specifically checks for some other cases,
@@ -37,134 +36,11 @@ pub fn check(tcx: &TyCtxt) {
 struct OverlapChecker<'cx, 'tcx:'cx> {
     tcx: &'cx TyCtxt<'tcx>,
 
-    // The set of traits where we have checked for overlap.  This is
-    // used to avoid checking the same trait twice.
-    //
-    // NB. It's ok to skip tracking this set because we fully
-    // encapsulate it, and we always create a task
-    // (`CoherenceOverlapCheck`) corresponding to each entry.
-    traits_checked: DefIdSet,
-
     // maps from a trait def-id to an impl id
     default_impls: DefIdMap<ast::NodeId>,
 }
 
 impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
-    fn check_for_overlapping_impls_of_trait(&mut self, trait_def_id: DefId) {
-        debug!("check_for_overlapping_impls_of_trait(trait_def_id={:?})",
-               trait_def_id);
-
-        let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id));
-        if !self.traits_checked.insert(trait_def_id) {
-            return;
-        }
-
-        let trait_def = self.tcx.lookup_trait_def(trait_def_id);
-        self.tcx.populate_implementations_for_trait_if_necessary(
-            trait_def.trait_ref.def_id);
-
-        // We should already know all impls of this trait, so these
-        // borrows are safe.
-        let (blanket_impls, nonblanket_impls) = trait_def.borrow_impl_lists(self.tcx);
-
-        // Conflicts can only occur between a blanket impl and another impl,
-        // or between 2 non-blanket impls of the same kind.
-
-        for (i, &impl1_def_id) in blanket_impls.iter().enumerate() {
-            for &impl2_def_id in &blanket_impls[(i+1)..] {
-                self.check_if_impls_overlap(impl1_def_id,
-                                            impl2_def_id);
-            }
-
-            for v in nonblanket_impls.values() {
-                for &impl2_def_id in v {
-                    self.check_if_impls_overlap(impl1_def_id,
-                                                impl2_def_id);
-                }
-            }
-        }
-
-        for impl_group in nonblanket_impls.values() {
-            for (i, &impl1_def_id) in impl_group.iter().enumerate() {
-                for &impl2_def_id in &impl_group[(i+1)..] {
-                    self.check_if_impls_overlap(impl1_def_id,
-                                                impl2_def_id);
-                }
-            }
-        }
-    }
-
-    // We need to coherently pick which impl will be displayed
-    // as causing the error message, and it must be the in the current
-    // crate. Just pick the smaller impl in the file.
-    fn order_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId)
-            -> Option<(DefId, DefId)> {
-        if impl1_def_id.krate != LOCAL_CRATE {
-            if impl2_def_id.krate != LOCAL_CRATE {
-                // we don't need to check impls if both are external;
-                // that's the other crate's job.
-                None
-            } else {
-                Some((impl2_def_id, impl1_def_id))
-            }
-        } else if impl2_def_id.krate != LOCAL_CRATE {
-            Some((impl1_def_id, impl2_def_id))
-        } else if impl1_def_id < impl2_def_id {
-            Some((impl1_def_id, impl2_def_id))
-        } else {
-            Some((impl2_def_id, impl1_def_id))
-        }
-    }
-
-    fn check_if_impls_overlap(&self,
-                              impl1_def_id: DefId,
-                              impl2_def_id: DefId)
-    {
-        if let Some((impl1_def_id, impl2_def_id)) = self.order_impls(
-            impl1_def_id, impl2_def_id)
-        {
-            debug!("check_if_impls_overlap({:?}, {:?})",
-                   impl1_def_id,
-                   impl2_def_id);
-
-            let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
-            if let Some(header) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
-                self.report_overlap_error(impl1_def_id, impl2_def_id, header.trait_ref.unwrap());
-            }
-        }
-    }
-
-    fn report_overlap_error(&self,
-                            impl1: DefId,
-                            impl2: DefId,
-                            trait_ref: ty::TraitRef)
-    {
-        // only print the Self type if it's concrete; otherwise, it's not adding much information.
-        let self_type = {
-            trait_ref.substs.self_ty().and_then(|ty| {
-                if let ty::TyInfer(_) = ty.sty {
-                    None
-                } else {
-                    Some(format!(" for type `{}`", ty))
-                }
-            }).unwrap_or(String::new())
-        };
-
-        let mut err = struct_span_err!(self.tcx.sess, self.span_of_def_id(impl1), E0119,
-                                       "conflicting implementations of trait `{}`{}:",
-                                       trait_ref,
-                                       self_type);
-
-        if impl2.is_local() {
-            span_note!(&mut err, self.span_of_def_id(impl2),
-                       "conflicting implementation is here:");
-        } else {
-            let cname = self.tcx.sess.cstore.crate_name(impl2.krate);
-            err.note(&format!("conflicting implementation in crate `{}`", cname));
-        }
-        err.emit();
-    }
-
     fn span_of_def_id(&self, did: DefId) -> Span {
         let node_id = self.tcx.map.as_local_node_id(did).unwrap();
         self.tcx.map.span(node_id)
@@ -213,7 +89,10 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
 
         for (i, &impl1_def_id) in impls.iter().enumerate() {
             for &impl2_def_id in &impls[(i+1)..] {
-                let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
+                let infcx = infer::new_infer_ctxt(self.tcx,
+                                                  &self.tcx.tables,
+                                                  None,
+                                                  ProjectionMode::Topmost);
                 if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() {
                     self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id)
                 }
@@ -222,15 +101,9 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
     }
 }
 
-
 impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
     fn visit_item(&mut self, item: &'v hir::Item) {
         match item.node {
-            hir::ItemTrait(..) => {
-                let trait_def_id = self.tcx.map.local_def_id(item.id);
-                self.check_for_overlapping_impls_of_trait(trait_def_id);
-            }
-
             hir::ItemEnum(..) | hir::ItemStruct(..) => {
                 let type_def_id = self.tcx.map.local_def_id(item.id);
                 self.check_for_overlapping_inherent_impls(type_def_id);
@@ -243,50 +116,90 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
                 let impl_def_id = self.tcx.map.local_def_id(item.id);
                 let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
 
-                self.check_for_overlapping_impls_of_trait(trait_ref.def_id);
-
                 let prev_default_impl = self.default_impls.insert(trait_ref.def_id, item.id);
-                match prev_default_impl {
-                    Some(prev_id) => {
-                        self.report_overlap_error(impl_def_id,
-                                                  self.tcx.map.local_def_id(prev_id),
-                                                  trait_ref);
-                    }
-                    None => { }
+                if let Some(prev_id) = prev_default_impl {
+                    let mut err = struct_span_err!(
+                        self.tcx.sess,
+                        self.tcx.span_of_impl(impl_def_id).unwrap(), E0521,
+                        "redundant default implementations of trait `{}`:",
+                        trait_ref);
+                    err.span_note(self.tcx.span_of_impl(self.tcx.map.local_def_id(prev_id))
+                                      .unwrap(),
+                                  "redundant implementation is here:");
+                    err.emit();
                 }
             }
             hir::ItemImpl(_, _, _, Some(_), _, _) => {
                 let impl_def_id = self.tcx.map.local_def_id(item.id);
                 let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
                 let trait_def_id = trait_ref.def_id;
-                self.check_for_overlapping_impls_of_trait(trait_def_id);
-                match trait_ref.self_ty().sty {
-                    ty::TyTrait(ref data) => {
-                        // This is something like impl Trait1 for Trait2. Illegal
-                        // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
 
-                        if !traits::is_object_safe(self.tcx, data.principal_def_id()) {
-                            // This is an error, but it will be
-                            // reported by wfcheck.  Ignore it
-                            // here. This is tested by
-                            // `coherence-impl-trait-for-trait-object-safe.rs`.
-                        } else {
-                            let mut supertrait_def_ids =
-                                traits::supertrait_def_ids(self.tcx, data.principal_def_id());
-                            if supertrait_def_ids.any(|d| d == trait_def_id) {
-                                span_err!(self.tcx.sess, item.span, E0371,
-                                          "the object type `{}` automatically \
-                                           implements the trait `{}`",
-                                          trait_ref.self_ty(),
-                                          self.tcx.item_path_str(trait_def_id));
+                let _task = self.tcx.dep_graph.in_task(
+                    DepNode::CoherenceOverlapCheck(trait_def_id));
+
+                let def = self.tcx.lookup_trait_def(trait_def_id);
+
+                // attempt to insert into the specialization graph
+                let insert_result = def.add_impl_for_specialization(self.tcx, impl_def_id);
+
+                // insertion failed due to overlap
+                if let Err(overlap) = insert_result {
+                    // only print the Self type if it has at least some outer
+                    // concrete shell; otherwise, it's not adding much
+                    // information.
+                    let self_type = {
+                        overlap.on_trait_ref.substs.self_ty().and_then(|ty| {
+                            if ty.has_concrete_skeleton() {
+                                Some(format!(" for type `{}`", ty))
+                            } else {
+                                None
                             }
+                        }).unwrap_or(String::new())
+                    };
+
+                    let mut err = struct_span_err!(
+                        self.tcx.sess, self.tcx.span_of_impl(impl_def_id).unwrap(), E0119,
+                        "conflicting implementations of trait `{}`{}:",
+                        overlap.on_trait_ref,
+                        self_type);
+
+                    match self.tcx.span_of_impl(overlap.with_impl) {
+                        Ok(span) => {
+                            err.span_note(span, "conflicting implementation is here:");
+                        }
+                        Err(cname) => {
+                            err.note(&format!("conflicting implementation in crate `{}`",
+                                              cname));
+                        }
+                    }
+
+                    err.emit();
+                }
+
+                // check for overlap with the automatic `impl Trait for Trait`
+                if let ty::TyTrait(ref data) = trait_ref.self_ty().sty {
+                    // This is something like impl Trait1 for Trait2. Illegal
+                    // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
+
+                    if !traits::is_object_safe(self.tcx, data.principal_def_id()) {
+                        // This is an error, but it will be
+                        // reported by wfcheck.  Ignore it
+                        // here. This is tested by
+                        // `coherence-impl-trait-for-trait-object-safe.rs`.
+                    } else {
+                        let mut supertrait_def_ids =
+                            traits::supertrait_def_ids(self.tcx, data.principal_def_id());
+                        if supertrait_def_ids.any(|d| d == trait_def_id) {
+                            span_err!(self.tcx.sess, item.span, E0371,
+                                      "the object type `{}` automatically \
+                                       implements the trait `{}`",
+                                      trait_ref.self_ty(),
+                                      self.tcx.item_path_str(trait_def_id));
                         }
                     }
-                    _ => { }
                 }
             }
-            _ => {
-            }
+            _ => {}
         }
     }
 }
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index b97d3f4993a40..0f88640b62951 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -533,6 +533,7 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                             id: ast::NodeId,
                             vis: hir::Visibility,
                             sig: &hir::MethodSig,
+                            defaultness: hir::Defaultness,
                             untransformed_rcvr_ty: Ty<'tcx>,
                             rcvr_ty_generics: &ty::Generics<'tcx>,
                             rcvr_ty_predicates: &ty::GenericPredicates<'tcx>) {
@@ -554,6 +555,7 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                     fty,
                                     explicit_self_category,
                                     vis,
+                                    defaultness,
                                     def_id,
                                     container);
 
@@ -600,6 +602,7 @@ fn convert_associated_const<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                       name: ast::Name,
                                       id: ast::NodeId,
                                       vis: hir::Visibility,
+                                      defaultness: hir::Defaultness,
                                       ty: ty::Ty<'tcx>,
                                       has_value: bool)
 {
@@ -611,6 +614,7 @@ fn convert_associated_const<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     let associated_const = Rc::new(ty::AssociatedConst {
         name: name,
         vis: vis,
+        defaultness: defaultness,
         def_id: ccx.tcx.map.local_def_id(id),
         container: container,
         ty: ty,
@@ -625,11 +629,13 @@ fn convert_associated_type<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                      name: ast::Name,
                                      id: ast::NodeId,
                                      vis: hir::Visibility,
+                                     defaultness: hir::Defaultness,
                                      ty: Option<Ty<'tcx>>)
 {
     let associated_type = Rc::new(ty::AssociatedType {
         name: name,
         vis: vis,
+        defaultness: defaultness,
         ty: ty,
         def_id: ccx.tcx.map.local_def_id(id),
         container: container
@@ -767,6 +773,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
                     convert_associated_const(ccx, ImplContainer(def_id),
                                              impl_item.name, impl_item.id,
                                              impl_item.vis.inherit_from(parent_visibility),
+                                             impl_item.defaultness,
                                              ty, true /* has_value */);
                 }
             }
@@ -783,7 +790,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
 
                     convert_associated_type(ccx, ImplContainer(def_id),
                                             impl_item.name, impl_item.id, impl_item.vis,
-                                            Some(typ));
+                                            impl_item.defaultness, Some(typ));
                 }
             }
 
@@ -797,7 +804,8 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
 
                     convert_method(ccx, ImplContainer(def_id),
                                    impl_item.name, impl_item.id, method_vis,
-                                   sig, selfty, &ty_generics, &ty_predicates);
+                                   sig, impl_item.defaultness, selfty, &ty_generics,
+                                   &ty_predicates);
                 }
             }
 
@@ -831,6 +839,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
                                              trait_item.name,
                                              trait_item.id,
                                              hir::Public,
+                                             hir::Defaultness::Default,
                                              ty,
                                              default.is_some())
                 }
@@ -848,6 +857,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
                                             trait_item.name,
                                             trait_item.id,
                                             hir::Public,
+                                            hir::Defaultness::Default,
                                             typ);
                 }
             }
@@ -861,6 +871,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
                                    trait_item.id,
                                    hir::Inherited,
                                    sig,
+                                   hir::Defaultness::Default,
                                    tcx.mk_self_type(),
                                    &trait_def.generics,
                                    &trait_predicates);
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 5c411bec5065d..19cfc13ea615c 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -3695,5 +3695,7 @@ register_diagnostics! {
     E0399, // trait items need to be implemented because the associated
            // type `{}` was overridden
     E0436, // functional record update requires a struct
-    E0513  // no type for local variable ..
+    E0513, // no type for local variable ..
+    E0520, // cannot specialize non-default item
+    E0521  // redundant default implementations of trait
 }
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 125c3d426a803..936be80919406 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -106,6 +106,7 @@ use middle::def::Def;
 use middle::infer::{self, TypeOrigin};
 use middle::subst::Substs;
 use middle::ty::{self, Ty, TyCtxt, TypeFoldable};
+use middle::traits::ProjectionMode;
 use session::{config, CompileResult};
 use util::common::time;
 use rustc_front::hir;
@@ -196,7 +197,7 @@ fn require_same_types<'a, 'tcx, M>(tcx: &TyCtxt<'tcx>,
 {
     let result = match maybe_infcx {
         None => {
-            let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
+            let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal);
             infer::mk_eqty(&infcx, t1_is_expected, TypeOrigin::Misc(span), t1, t2)
         }
         Some(infcx) => {
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index f7621b0131ad4..c830fed5db9a7 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1328,10 +1328,10 @@ pub struct MethodSig {
     pub explicit_self: ExplicitSelf,
 }
 
-/// Represents a method declaration in a trait declaration, possibly including
-/// a default implementation. A trait method is either required (meaning it
-/// doesn't have an implementation, just a signature) or provided (meaning it
-/// has a default implementation).
+/// Represents an item declaration within a trait declaration,
+/// possibly including a default implementation. A trait item is
+/// either required (meaning it doesn't have an implementation, just a
+/// signature) or provided (meaning it has a default implementation).
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct TraitItem {
     pub id: NodeId,
@@ -1353,6 +1353,7 @@ pub struct ImplItem {
     pub id: NodeId,
     pub ident: Ident,
     pub vis: Visibility,
+    pub defaultness: Defaultness,
     pub attrs: Vec<Attribute>,
     pub node: ImplItemKind,
     pub span: Span,
@@ -1654,6 +1655,12 @@ pub enum Constness {
     NotConst,
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub enum Defaultness {
+    Default,
+    Final,
+}
+
 impl fmt::Display for Unsafety {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt(match *self {
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index f5794f7219bcf..5bfdab791d638 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -1061,6 +1061,7 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
             ident: ii.ident,
             attrs: ii.attrs,
             vis: ii.vis,
+            defaultness: ii.defaultness,
             node: match ii.node  {
                 ast::ImplItemKind::Method(sig, body) => {
                     let (sig, body) = expand_and_rename_method(sig, body, fld);
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 14a3f93738a32..fbaf28332c42c 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -247,7 +247,10 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
     ("inclusive_range_syntax", "1.7.0", Some(28237), Active),
 
     // `expr?`
-    ("question_mark", "1.9.0", Some(31436), Active)
+    ("question_mark", "1.9.0", Some(31436), Active),
+
+    // impl specialization (RFC 1210)
+    ("specialization", "1.7.0", Some(31844), Active),
 ];
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
@@ -574,6 +577,7 @@ pub struct Features {
     pub stmt_expr_attributes: bool,
     pub deprecated: bool,
     pub question_mark: bool,
+    pub specialization: bool,
 }
 
 impl Features {
@@ -608,6 +612,7 @@ impl Features {
             stmt_expr_attributes: false,
             deprecated: false,
             question_mark: false,
+            specialization: false,
         }
     }
 }
@@ -1102,6 +1107,12 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
     }
 
     fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
+        if ii.defaultness == ast::Defaultness::Default {
+            self.gate_feature("specialization",
+                              ii.span,
+                              "specialization is unstable");
+        }
+
         match ii.node {
             ast::ImplItemKind::Const(..) => {
                 self.gate_feature("associated_consts",
@@ -1212,6 +1223,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
         stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
         deprecated: cx.has_feature("deprecated"),
         question_mark: cx.has_feature("question_mark"),
+        specialization: cx.has_feature("specialization"),
     }
 }
 
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 9056103d30086..cd8998a211ae7 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -993,6 +993,7 @@ pub fn noop_fold_impl_item<T: Folder>(i: ImplItem, folder: &mut T)
         ident: folder.fold_ident(i.ident),
         attrs: fold_attrs(i.attrs, folder),
         vis: i.vis,
+        defaultness: i.defaultness,
         node: match i.node  {
             ast::ImplItemKind::Const(ty, expr) => {
                 ast::ImplItemKind::Const(folder.fold_ty(ty), folder.fold_expr(expr))
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 5884be401503d..6839f11cd709d 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -18,7 +18,7 @@ use ast::{Mod, Arg, Arm, Attribute, BindingMode, TraitItemKind};
 use ast::Block;
 use ast::{BlockCheckMode, CaptureBy};
 use ast::{Constness, Crate, CrateConfig};
-use ast::{Decl, DeclKind};
+use ast::{Decl, DeclKind, Defaultness};
 use ast::{EMPTY_CTXT, EnumDef, ExplicitSelf};
 use ast::{Expr, ExprKind, RangeLimits};
 use ast::{Field, FnDecl};
@@ -644,6 +644,25 @@ impl<'a> Parser<'a> {
         }
     }
 
+    pub fn check_contextual_keyword(&mut self, ident: Ident) -> bool {
+        let tok = token::Ident(ident, token::Plain);
+        self.expected_tokens.push(TokenType::Token(tok));
+        if let token::Ident(ref cur_ident, _) = self.token {
+            cur_ident.name == ident.name
+        } else {
+            false
+        }
+    }
+
+    pub fn eat_contextual_keyword(&mut self, ident: Ident) -> bool {
+        if self.check_contextual_keyword(ident) {
+            self.bump();
+            true
+        } else {
+            false
+        }
+    }
+
     /// If the given word is not a keyword, signal an error.
     /// If the next token is not the given word, signal an error.
     /// Otherwise, eat it.
@@ -705,7 +724,6 @@ impl<'a> Parser<'a> {
         }
     }
 
-
     /// Attempt to consume a `<`. If `<<` is seen, replace it with a single
     /// `<` and continue. If a `<` is not seen, return false.
     ///
@@ -4846,6 +4864,7 @@ impl<'a> Parser<'a> {
         let mut attrs = try!(self.parse_outer_attributes());
         let lo = self.span.lo;
         let vis = try!(self.parse_visibility());
+        let defaultness = try!(self.parse_defaultness());
         let (name, node) = if self.eat_keyword(keywords::Type) {
             let name = try!(self.parse_ident());
             try!(self.expect(&token::Eq));
@@ -4872,6 +4891,7 @@ impl<'a> Parser<'a> {
             span: mk_sp(lo, self.last_span.hi),
             ident: name,
             vis: vis,
+            defaultness: defaultness,
             attrs: attrs,
             node: node
         })
@@ -5208,6 +5228,15 @@ impl<'a> Parser<'a> {
         else { Ok(Visibility::Inherited) }
     }
 
+    /// Parse defaultness: DEFAULT or nothing
+    fn parse_defaultness(&mut self) -> PResult<'a, Defaultness> {
+        if self.eat_contextual_keyword(special_idents::DEFAULT) {
+            Ok(Defaultness::Default)
+        } else {
+            Ok(Defaultness::Final)
+        }
+    }
+
     /// Given a termination token, parse all of the items in a module
     fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> PResult<'a, Mod> {
         let mut items = vec![];
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 294cbf358954f..033ac9440bcec 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -545,66 +545,67 @@ declare_special_idents_and_keywords! {
         (9,                          __unused1,              "<__unused1>");
         (super::SELF_TYPE_KEYWORD_NAME_NUM, type_self,       "Self");
         (11,                         prelude_import,         "prelude_import");
+        (12,                         DEFAULT,                "default");
     }
 
     pub mod keywords {
         // These ones are variants of the Keyword enum
 
         'strict:
-        (12,                         As,         "as");
-        (13,                         Break,      "break");
-        (14,                         Crate,      "crate");
-        (15,                         Else,       "else");
-        (16,                         Enum,       "enum");
-        (17,                         Extern,     "extern");
-        (18,                         False,      "false");
-        (19,                         Fn,         "fn");
-        (20,                         For,        "for");
-        (21,                         If,         "if");
-        (22,                         Impl,       "impl");
-        (23,                         In,         "in");
-        (24,                         Let,        "let");
-        (25,                         Loop,       "loop");
-        (26,                         Match,      "match");
-        (27,                         Mod,        "mod");
-        (28,                         Move,       "move");
-        (29,                         Mut,        "mut");
-        (30,                         Pub,        "pub");
-        (31,                         Ref,        "ref");
-        (32,                         Return,     "return");
+        (13,                         As,         "as");
+        (14,                         Break,      "break");
+        (15,                         Crate,      "crate");
+        (16,                         Else,       "else");
+        (17,                         Enum,       "enum");
+        (18,                         Extern,     "extern");
+        (19,                         False,      "false");
+        (20,                         Fn,         "fn");
+        (21,                         For,        "for");
+        (22,                         If,         "if");
+        (23,                         Impl,       "impl");
+        (24,                         In,         "in");
+        (25,                         Let,        "let");
+        (26,                         Loop,       "loop");
+        (27,                         Match,      "match");
+        (28,                         Mod,        "mod");
+        (29,                         Move,       "move");
+        (30,                         Mut,        "mut");
+        (31,                         Pub,        "pub");
+        (32,                         Ref,        "ref");
+        (33,                         Return,     "return");
         // Static and Self are also special idents (prefill de-dupes)
         (super::STATIC_KEYWORD_NAME_NUM, Static, "static");
         (super::SELF_KEYWORD_NAME_NUM, SelfValue, "self");
         (super::SELF_TYPE_KEYWORD_NAME_NUM, SelfType, "Self");
-        (33,                         Struct,     "struct");
+        (34,                         Struct,     "struct");
         (super::SUPER_KEYWORD_NAME_NUM, Super,   "super");
-        (34,                         True,       "true");
-        (35,                         Trait,      "trait");
-        (36,                         Type,       "type");
-        (37,                         Unsafe,     "unsafe");
-        (38,                         Use,        "use");
-        (39,                         While,      "while");
-        (40,                         Continue,   "continue");
-        (41,                         Box,        "box");
-        (42,                         Const,      "const");
-        (43,                         Where,      "where");
+        (35,                         True,       "true");
+        (36,                         Trait,      "trait");
+        (37,                         Type,       "type");
+        (38,                         Unsafe,     "unsafe");
+        (39,                         Use,        "use");
+        (40,                         While,      "while");
+        (41,                         Continue,   "continue");
+        (42,                         Box,        "box");
+        (43,                         Const,      "const");
+        (44,                         Where,      "where");
         'reserved:
-        (44,                         Virtual,    "virtual");
-        (45,                         Proc,       "proc");
-        (46,                         Alignof,    "alignof");
-        (47,                         Become,     "become");
-        (48,                         Offsetof,   "offsetof");
-        (49,                         Priv,       "priv");
-        (50,                         Pure,       "pure");
-        (51,                         Sizeof,     "sizeof");
-        (52,                         Typeof,     "typeof");
-        (53,                         Unsized,    "unsized");
-        (54,                         Yield,      "yield");
-        (55,                         Do,         "do");
-        (56,                         Abstract,   "abstract");
-        (57,                         Final,      "final");
-        (58,                         Override,   "override");
-        (59,                         Macro,      "macro");
+        (45,                         Virtual,    "virtual");
+        (46,                         Proc,       "proc");
+        (47,                         Alignof,    "alignof");
+        (48,                         Become,     "become");
+        (49,                         Offsetof,   "offsetof");
+        (50,                         Priv,       "priv");
+        (51,                         Pure,       "pure");
+        (52,                         Sizeof,     "sizeof");
+        (53,                         Typeof,     "typeof");
+        (54,                         Unsized,    "unsized");
+        (55,                         Yield,      "yield");
+        (56,                         Do,         "do");
+        (57,                         Abstract,   "abstract");
+        (58,                         Final,      "final");
+        (59,                         Override,   "override");
+        (60,                         Macro,      "macro");
     }
 }
 
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 2cfed1f82f7ec..533487ae1c547 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1582,6 +1582,9 @@ impl<'a> State<'a> {
         try!(self.hardbreak_if_not_bol());
         try!(self.maybe_print_comment(ii.span.lo));
         try!(self.print_outer_attributes(&ii.attrs));
+        if let ast::Defaultness::Default = ii.defaultness {
+            try!(self.word_nbsp("default"));
+        }
         match ii.node {
             ast::ImplItemKind::Const(ref ty, ref expr) => {
                 try!(self.print_associated_const(ii.ident, &ty, Some(&expr), ii.vis));
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index c0237a5d29a41..937055fcfa672 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -476,6 +476,7 @@ impl<'a> TraitDef<'a> {
                 span: self.span,
                 ident: ident,
                 vis: ast::Visibility::Inherited,
+                defaultness: ast::Defaultness::Final,
                 attrs: Vec::new(),
                 node: ast::ImplItemKind::Type(type_def.to_ty(cx,
                     self.span,
@@ -893,6 +894,7 @@ impl<'a> MethodDef<'a> {
             attrs: self.attributes.clone(),
             span: trait_.span,
             vis: ast::Visibility::Inherited,
+            defaultness: ast::Defaultness::Final,
             ident: method_ident,
             node: ast::ImplItemKind::Method(ast::MethodSig {
                 generics: fn_generics,
diff --git a/src/test/auxiliary/go_trait.rs b/src/test/auxiliary/go_trait.rs
index 0a921c8f5b3a0..044bb606b40e2 100644
--- a/src/test/auxiliary/go_trait.rs
+++ b/src/test/auxiliary/go_trait.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(specialization)]
+
 // Common code used for tests that model the Fn/FnMut/FnOnce hierarchy.
 
 pub trait Go {
@@ -37,7 +39,7 @@ pub fn go_once<G:GoOnce>(this: G, arg: isize) {
 impl<G> GoMut for G
     where G : Go
 {
-    fn go_mut(&mut self, arg: isize) {
+    default fn go_mut(&mut self, arg: isize) {
         go(&*self, arg)
     }
 }
@@ -45,7 +47,7 @@ impl<G> GoMut for G
 impl<G> GoOnce for G
     where G : GoMut
 {
-    fn go_once(mut self, arg: isize) {
+    default fn go_once(mut self, arg: isize) {
         go_mut(&mut self, arg)
     }
 }
diff --git a/src/test/auxiliary/specialization_cross_crate.rs b/src/test/auxiliary/specialization_cross_crate.rs
new file mode 100644
index 0000000000000..1d235336de821
--- /dev/null
+++ b/src/test/auxiliary/specialization_cross_crate.rs
@@ -0,0 +1,82 @@
+// 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.
+
+#![feature(specialization)]
+
+pub trait Foo {
+    fn foo(&self) -> &'static str;
+}
+
+impl<T> Foo for T {
+    default fn foo(&self) -> &'static str {
+        "generic"
+    }
+}
+
+impl<T: Clone> Foo for T {
+    default fn foo(&self) -> &'static str {
+        "generic Clone"
+    }
+}
+
+impl<T, U> Foo for (T, U) where T: Clone, U: Clone {
+    default fn foo(&self) -> &'static str {
+        "generic pair"
+    }
+}
+
+impl<T: Clone> Foo for (T, T) {
+    default fn foo(&self) -> &'static str {
+        "generic uniform pair"
+    }
+}
+
+impl Foo for (u8, u32) {
+    default fn foo(&self) -> &'static str {
+        "(u8, u32)"
+    }
+}
+
+impl Foo for (u8, u8) {
+    default fn foo(&self) -> &'static str {
+        "(u8, u8)"
+    }
+}
+
+impl<T: Clone> Foo for Vec<T> {
+    default fn foo(&self) -> &'static str {
+        "generic Vec"
+    }
+}
+
+impl Foo for Vec<i32> {
+    fn foo(&self) -> &'static str {
+        "Vec<i32>"
+    }
+}
+
+impl Foo for String {
+    fn foo(&self) -> &'static str {
+        "String"
+    }
+}
+
+impl Foo for i32 {
+    fn foo(&self) -> &'static str {
+        "i32"
+    }
+}
+
+pub trait MyMarker {}
+impl<T: Clone + MyMarker> Foo for T {
+    default fn foo(&self) -> &'static str {
+        "generic Clone + MyMarker"
+    }
+}
diff --git a/src/test/auxiliary/specialization_cross_crate_defaults.rs b/src/test/auxiliary/specialization_cross_crate_defaults.rs
new file mode 100755
index 0000000000000..b62d80b589fda
--- /dev/null
+++ b/src/test/auxiliary/specialization_cross_crate_defaults.rs
@@ -0,0 +1,49 @@
+// 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.
+
+
+#![feature(specialization)]
+
+// First, test only use of explicit `default` items:
+
+pub trait Foo {
+    fn foo(&self) -> bool;
+}
+
+impl<T> Foo for T {
+    default fn foo(&self) -> bool { false }
+}
+
+impl Foo for i32 {}
+
+impl Foo for i64 {
+    fn foo(&self) -> bool { true }
+}
+
+// Next, test mixture of explicit `default` and provided methods:
+
+pub trait Bar {
+    fn bar(&self) -> i32 { 0 }
+}
+
+impl<T> Bar for T {} // use the provided method
+
+impl Bar for i32 {
+    fn bar(&self) -> i32 { 1 }
+}
+impl<'a> Bar for &'a str {}
+
+impl<T> Bar for Vec<T> {
+    default fn bar(&self) -> i32 { 2 }
+}
+impl Bar for Vec<i32> {}
+impl Bar for Vec<i64> {
+    fn bar(&self) -> i32 { 3 }
+}
diff --git a/src/test/auxiliary/xcrate_associated_type_defaults.rs b/src/test/auxiliary/xcrate_associated_type_defaults.rs
index 43852a4e793f3..6779438c67226 100644
--- a/src/test/auxiliary/xcrate_associated_type_defaults.rs
+++ b/src/test/auxiliary/xcrate_associated_type_defaults.rs
@@ -10,9 +10,13 @@
 
 #![feature(associated_type_defaults)]
 
-pub trait Foo {
-    type Input = usize;
-    fn bar(&self, _: Self::Input) {}
+pub trait Foo<T: Default + ToString> {
+    type Out: Default + ToString = T;
 }
 
-impl Foo for () {}
+impl Foo<u32> for () {
+}
+
+impl Foo<u64> for () {
+    type Out = bool;
+}
diff --git a/src/test/compile-fail/associated-types-coherence-failure.rs b/src/test/compile-fail/associated-types-coherence-failure.rs
index 6d68da54112f2..786a25500a886 100644
--- a/src/test/compile-fail/associated-types-coherence-failure.rs
+++ b/src/test/compile-fail/associated-types-coherence-failure.rs
@@ -22,21 +22,21 @@ pub trait IntoCow<'a, B: ?Sized> {
     fn into_cow(self) -> Cow<'a, B>;
 }
 
-impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned {
-//~^ ERROR E0119
+impl<'a, B: ?Sized> IntoCow<'a, B> for <B as ToOwned>::Owned where B: ToOwned {
     fn into_cow(self) -> Cow<'a, B> {
-        self
+        Cow(PhantomData)
     }
 }
 
-impl<'a, B: ?Sized> IntoCow<'a, B> for <B as ToOwned>::Owned where B: ToOwned {
+impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned {
 //~^ ERROR E0119
     fn into_cow(self) -> Cow<'a, B> {
-        Cow(PhantomData)
+        self
     }
 }
 
 impl<'a, B: ?Sized> IntoCow<'a, B> for &'a B where B: ToOwned {
+//~^ ERROR E0119
     fn into_cow(self) -> Cow<'a, B> {
         Cow(PhantomData)
     }
diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs
index b771b959d3e50..434d77828b44a 100644
--- a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs
+++ b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs
@@ -27,11 +27,11 @@ impl Even for isize { }
 
 impl Odd for usize { }
 
-impl<T:Even> MyTrait for T { //~ ERROR E0119
+impl<T:Even> MyTrait for T {
     fn get(&self) -> usize { 0 }
 }
 
-impl<T:Odd> MyTrait for T {
+impl<T:Odd> MyTrait for T { //~ ERROR E0119
     fn get(&self) -> usize { 0 }
 }
 
diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs
index d3b0e7f10b91b..7ad5cd71ca8c2 100644
--- a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs
+++ b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs
@@ -23,11 +23,11 @@ trait Even {}
 
 trait Odd {}
 
-impl<T:Even> MyTrait for T { //~ ERROR E0119
+impl<T:Even> MyTrait for T {
     fn get(&self) -> usize { 0 }
 }
 
-impl<T:Odd> MyTrait for T {
+impl<T:Odd> MyTrait for T { //~ ERROR E0119
     fn get(&self) -> usize { 0 }
 }
 
diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs
index 7b60a5ecbd71f..1defe6c8b20e3 100644
--- a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs
+++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs
@@ -18,7 +18,7 @@ trait MyTrait<T> {
     fn get(&self) -> T;
 }
 
-impl<T> MyTrait<T> for T { //~ ERROR E0119
+impl<T> MyTrait<T> for T {
     fn get(&self) -> T {
         panic!()
     }
@@ -29,7 +29,7 @@ struct MyType {
     dummy: usize
 }
 
-impl MyTrait<MyType> for MyType {
+impl MyTrait<MyType> for MyType { //~ ERROR E0119
     fn get(&self) -> usize { (*self).clone() }
 }
 
diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs
index eeaa68677eb67..5c5c4d32d675c 100644
--- a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs
+++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs
@@ -19,7 +19,7 @@ trait MyTrait {
     fn get(&self) -> usize;
 }
 
-impl<T:OtherTrait> MyTrait for T { //~ ERROR E0119
+impl<T:OtherTrait> MyTrait for T {
     fn get(&self) -> usize { 0 }
 }
 
@@ -27,7 +27,7 @@ struct MyType {
     dummy: usize
 }
 
-impl MyTrait for MyType {
+impl MyTrait for MyType { //~ ERROR E0119
     fn get(&self) -> usize { self.dummy }
 }
 
diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs
index d218b64af0527..57d71b44b0f6b 100644
--- a/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs
+++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs
@@ -18,7 +18,7 @@ trait MyTrait {
     fn get(&self) -> usize;
 }
 
-impl<T> MyTrait for T { //~ ERROR E0119
+impl<T> MyTrait for T {
     fn get(&self) -> usize { 0 }
 }
 
@@ -26,7 +26,7 @@ struct MyType {
     dummy: usize
 }
 
-impl MyTrait for MyType {
+impl MyTrait for MyType { //~ ERROR E0119
     fn get(&self) -> usize { self.dummy }
 }
 
diff --git a/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs b/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs
index 344ec89d25de9..c123e381ab7d7 100644
--- a/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs
+++ b/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs
@@ -15,8 +15,6 @@ trait MyTrait {}
 struct TestType<T>(::std::marker::PhantomData<T>);
 
 unsafe impl<T: MyTrait+'static> Send for TestType<T> {}
-//~^ ERROR conflicting implementations of trait `core::marker::Send`
-//~^^ ERROR conflicting implementations of trait `core::marker::Send`
 
 impl<T: MyTrait> !Send for TestType<T> {}
 //~^ ERROR conflicting implementations of trait `core::marker::Send`
diff --git a/src/test/compile-fail/coherence-cross-crate-conflict.rs b/src/test/compile-fail/coherence-cross-crate-conflict.rs
index a020b518d8273..9f74afbb2b3b5 100644
--- a/src/test/compile-fail/coherence-cross-crate-conflict.rs
+++ b/src/test/compile-fail/coherence-cross-crate-conflict.rs
@@ -8,8 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Regression test for #3512 - conflicting trait impls in different crates should give a
-// 'conflicting implementations' error message.
+// The error here is strictly due to orphan rules; the impl here
+// generalizes the one upstream
 
 // aux-build:trait_impl_conflict.rs
 extern crate trait_impl_conflict;
@@ -17,7 +17,6 @@ use trait_impl_conflict::Foo;
 
 impl<A> Foo for A {
     //~^ ERROR type parameter `A` must be used as the type parameter for some local type
-    //~^^ ERROR E0119
 }
 
 fn main() {
diff --git a/src/test/compile-fail/coherence-default-trait-impl.rs b/src/test/compile-fail/coherence-default-trait-impl.rs
index 0705702b031ee..3d109de76ccd1 100644
--- a/src/test/compile-fail/coherence-default-trait-impl.rs
+++ b/src/test/compile-fail/coherence-default-trait-impl.rs
@@ -15,7 +15,7 @@ trait MyTrait {}
 impl MyTrait for .. {}
 
 impl MyTrait for .. {}
-//~^ ERROR conflicting implementations of trait `MyTrait`
+//~^ ERROR redundant default implementations of trait `MyTrait`
 
 trait MySafeTrait {}
 
diff --git a/src/test/compile-fail/coherence-no-direct-lifetime-dispatch.rs b/src/test/compile-fail/coherence-no-direct-lifetime-dispatch.rs
new file mode 100644
index 0000000000000..6de338f1db0fa
--- /dev/null
+++ b/src/test/compile-fail/coherence-no-direct-lifetime-dispatch.rs
@@ -0,0 +1,18 @@
+// 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.
+
+// Test that you cannot *directly* dispatch on lifetime requirements
+
+trait MyTrait {}
+
+impl<T> MyTrait for T {}
+impl<T: 'static> MyTrait for T {} //~ ERROR E0119
+
+fn main() {}
diff --git a/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs b/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs
index 3fd635b3d616f..928ba7a36db26 100644
--- a/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs
+++ b/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs
@@ -19,10 +19,10 @@
 trait From<U> {
 }
 
-impl <T> From<T> for T { //~ ERROR E0119
+impl <T> From<T> for T {
 }
 
-impl <T11, U11> From<(U11,)> for (T11,) {
+impl <T11, U11> From<(U11,)> for (T11,) { //~ ERROR E0119
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/coherence-overlap-issue-23516.rs b/src/test/compile-fail/coherence-overlap-issue-23516.rs
index d7f060a3bfe73..51d7c3e8b4cb1 100644
--- a/src/test/compile-fail/coherence-overlap-issue-23516.rs
+++ b/src/test/compile-fail/coherence-overlap-issue-23516.rs
@@ -14,6 +14,6 @@
 
 pub trait Sugar { fn dummy(&self) { } }
 pub trait Sweet { fn dummy(&self) { } }
-impl<T:Sugar> Sweet for T { } //~ ERROR E0119
-impl<U:Sugar> Sweet for Box<U> { }
+impl<T:Sugar> Sweet for T { }
+impl<U:Sugar> Sweet for Box<U> { } //~ ERROR E0119
 fn main() { }
diff --git a/src/test/compile-fail/coherence-overlap-messages.rs b/src/test/compile-fail/coherence-overlap-messages.rs
index 4f1092f960e0d..0ae8135221c21 100644
--- a/src/test/compile-fail/coherence-overlap-messages.rs
+++ b/src/test/compile-fail/coherence-overlap-messages.rs
@@ -10,33 +10,23 @@
 
 trait Foo {}
 
-impl<T> Foo for T {} //~ ERROR conflicting implementations of trait `Foo`:
-impl<U> Foo for U {}
+impl<T> Foo for T {}
+impl<U> Foo for U {} //~ ERROR conflicting implementations of trait `Foo`:
 
 trait Bar {}
 
-impl<T> Bar for T {} //~ ERROR conflicting implementations of trait `Bar` for type `u8`:
-impl Bar for u8 {}
+impl<T> Bar for (T, u8) {}
+impl<T> Bar for (u8, T) {} //~ ERROR conflicting implementations of trait `Bar` for type `(u8, u8)`:
 
 trait Baz<T> {}
 
-impl<T, U> Baz<U> for T {} //~ ERROR conflicting implementations of trait `Baz<_>` for type `u8`:
-impl<T> Baz<T> for u8 {}
+impl<T> Baz<u8> for T {}
+impl<T> Baz<T> for u8 {} //~ ERROR conflicting implementations of trait `Baz<u8>` for type `u8`:
 
-trait Quux<T> {}
+trait Quux<U, V> {}
 
-impl<T, U> Quux<U> for T {} //~ ERROR conflicting implementations of trait `Quux<_>`:
-impl<T> Quux<T> for T {}
-
-trait Qaar<T> {}
-
-impl<T, U> Qaar<U> for T {} //~ ERROR conflicting implementations of trait `Qaar<u8>`:
-impl<T> Qaar<u8> for T {}
-
-trait Qaax<T> {}
-
-impl<T, U> Qaax<U> for T {}
-//~^ ERROR conflicting implementations of trait `Qaax<u8>` for type `u32`:
-impl Qaax<u8> for u32 {}
+impl<T, U, V> Quux<U, V> for T {}
+impl<T, U> Quux<U, U> for T {} //~ ERROR conflicting implementations of trait `Quux<_, _>`:
+impl<T, V> Quux<T, V> for T {} //~ ERROR conflicting implementations of trait `Quux<_, _>`:
 
 fn main() {}
diff --git a/src/test/compile-fail/coherence-projection-conflict-orphan.rs b/src/test/compile-fail/coherence-projection-conflict-orphan.rs
index 3de7945439838..3ed3549de89aa 100644
--- a/src/test/compile-fail/coherence-projection-conflict-orphan.rs
+++ b/src/test/compile-fail/coherence-projection-conflict-orphan.rs
@@ -21,8 +21,8 @@ pub trait Bar {
     type Output: 'static;
 }
 
-impl Foo<i32> for i32 { } //~ ERROR E0119
+impl Foo<i32> for i32 { }
 
-impl<A:Iterator> Foo<A::Item> for A { }
+impl<A:Iterator> Foo<A::Item> for A { }  //~ ERROR E0119
 
 fn main() {}
diff --git a/src/test/compile-fail/coherence-projection-conflict-ty-param.rs b/src/test/compile-fail/coherence-projection-conflict-ty-param.rs
index 6880f3e9a3cc9..f04902a70f68c 100644
--- a/src/test/compile-fail/coherence-projection-conflict-ty-param.rs
+++ b/src/test/compile-fail/coherence-projection-conflict-ty-param.rs
@@ -15,8 +15,8 @@ use std::marker::PhantomData;
 
 pub trait Foo<P> {}
 
-impl <P, T: Foo<P>> Foo<P> for Option<T> {} //~ ERROR E0119
+impl <P, T: Foo<P>> Foo<P> for Option<T> {}
 
-impl<T, U> Foo<T> for Option<U> { }
+impl<T, U> Foo<T> for Option<U> { } //~ ERROR E0119
 
 fn main() {}
diff --git a/src/test/compile-fail/coherence-projection-conflict.rs b/src/test/compile-fail/coherence-projection-conflict.rs
index 2236e71b53fff..6d3ab32f06f43 100644
--- a/src/test/compile-fail/coherence-projection-conflict.rs
+++ b/src/test/compile-fail/coherence-projection-conflict.rs
@@ -16,9 +16,9 @@ pub trait Bar {
     type Output: 'static;
 }
 
-impl Foo<i32> for i32 { } //~ ERROR E0119
+impl Foo<i32> for i32 { }
 
-impl<A:Bar> Foo<A::Output> for A { }
+impl<A:Bar> Foo<A::Output> for A { } //~ ERROR E0119
 
 impl Bar for i32 {
     type Output = i32;
diff --git a/src/test/compile-fail/coherence-tuple-conflict.rs b/src/test/compile-fail/coherence-tuple-conflict.rs
index 87b007fdd6982..7807f93df1a67 100644
--- a/src/test/compile-fail/coherence-tuple-conflict.rs
+++ b/src/test/compile-fail/coherence-tuple-conflict.rs
@@ -18,11 +18,11 @@ trait MyTrait {
     fn get(&self) -> usize;
 }
 
-impl<T> MyTrait for (T,T) { //~ ERROR E0119
+impl<T> MyTrait for (T,T) {
     fn get(&self) -> usize { 0 }
 }
 
-impl<A,B> MyTrait for (A,B) {
+impl<A,B> MyTrait for (A,B) { //~ ERROR E0119
     fn get(&self) -> usize { self.dummy }
 }
 
diff --git a/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs b/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs
index a6b62d17bc4e6..8e3e3f31cb5f1 100644
--- a/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs
+++ b/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs
@@ -21,10 +21,10 @@ struct MyType { x: i32 }
 
 trait MyTrait { }
 
-impl<T: lib::MyCopy> MyTrait for T { } //~ ERROR E0119
+impl<T: lib::MyCopy> MyTrait for T { }
 
 // Tuples are not fundamental.
-impl MyTrait for lib::MyFundamentalStruct<(MyType,)> { }
+impl MyTrait for lib::MyFundamentalStruct<(MyType,)> { } //~ ERROR E0119
 
 #[rustc_error]
 fn main() { }
diff --git a/src/test/compile-fail/coherence_copy_like_err_struct.rs b/src/test/compile-fail/coherence_copy_like_err_struct.rs
index 5a9f440f8bb6a..35bc17b8e8870 100644
--- a/src/test/compile-fail/coherence_copy_like_err_struct.rs
+++ b/src/test/compile-fail/coherence_copy_like_err_struct.rs
@@ -18,7 +18,7 @@ extern crate coherence_copy_like_lib as lib;
 struct MyType { x: i32 }
 
 trait MyTrait { }
-impl<T: lib::MyCopy> MyTrait for T { } //~ ERROR E0119
+impl<T: lib::MyCopy> MyTrait for T { }
 
 // `MyStruct` is not declared fundamental, therefore this would
 // require that
@@ -26,6 +26,6 @@ impl<T: lib::MyCopy> MyTrait for T { } //~ ERROR E0119
 //     MyStruct<MyType>: !MyTrait
 //
 // which we cannot approve.
-impl MyTrait for lib::MyStruct<MyType> { }
+impl MyTrait for lib::MyStruct<MyType> { } //~ ERROR E0119
 
 fn main() { }
diff --git a/src/test/compile-fail/coherence_copy_like_err_tuple.rs b/src/test/compile-fail/coherence_copy_like_err_tuple.rs
index ee0d5550fd61f..a70cc92955fb0 100644
--- a/src/test/compile-fail/coherence_copy_like_err_tuple.rs
+++ b/src/test/compile-fail/coherence_copy_like_err_tuple.rs
@@ -18,13 +18,13 @@ extern crate coherence_copy_like_lib as lib;
 struct MyType { x: i32 }
 
 trait MyTrait { }
-impl<T: lib::MyCopy> MyTrait for T { } //~ ERROR E0119
+impl<T: lib::MyCopy> MyTrait for T { }
 
 // Tuples are not fundamental, therefore this would require that
 //
 //     (MyType,): !MyTrait
 //
 // which we cannot approve.
-impl MyTrait for (MyType,) { }
+impl MyTrait for (MyType,) { } //~ ERROR E0119
 
 fn main() { }
diff --git a/src/test/compile-fail/issue-28568.rs b/src/test/compile-fail/issue-28568.rs
index 1dfff144cef98..7c051784f61a7 100644
--- a/src/test/compile-fail/issue-28568.rs
+++ b/src/test/compile-fail/issue-28568.rs
@@ -11,12 +11,12 @@
 struct MyStruct;
 
 impl Drop for MyStruct {
-//~^ ERROR conflicting implementations of trait
+//~^ NOTE conflicting implementation is here
     fn drop(&mut self) { }
 }
 
 impl Drop for MyStruct {
-//~^ NOTE conflicting implementation is here
+//~^ ERROR conflicting implementations of trait
     fn drop(&mut self) { }
 }
 
diff --git a/src/test/compile-fail/private-in-public-warn.rs b/src/test/compile-fail/private-in-public-warn.rs
index 9aab06ce14ee1..b9d632a8cf07e 100644
--- a/src/test/compile-fail/private-in-public-warn.rs
+++ b/src/test/compile-fail/private-in-public-warn.rs
@@ -198,9 +198,11 @@ mod aliases_pub {
     use self::m::PubTr as PrivUseAliasTr;
     type PrivAlias = m::Pub2;
     trait PrivTr {
+        type AssocAlias;
+    }
+    impl PrivTr for Priv {
         type AssocAlias = m::Pub3;
     }
-    impl PrivTr for Priv {}
 
     pub fn f1(arg: PrivUseAlias) {} // OK
 
@@ -245,9 +247,11 @@ mod aliases_priv {
     use self::PrivTr1 as PrivUseAliasTr;
     type PrivAlias = Priv2;
     trait PrivTr {
+        type AssocAlias;
+    }
+    impl PrivTr for Priv {
         type AssocAlias = Priv3;
     }
-    impl PrivTr for Priv {}
 
     pub trait Tr1: PrivUseAliasTr {} //~ WARN private trait in public interface
         //~^ WARNING hard error
diff --git a/src/test/compile-fail/specialization/README.md b/src/test/compile-fail/specialization/README.md
new file mode 100644
index 0000000000000..f2b4bf946c537
--- /dev/null
+++ b/src/test/compile-fail/specialization/README.md
@@ -0,0 +1,21 @@
+This directory contains the test for incorrect usage of specialization that
+should lead to compile failure. Those tests break down into a few categories:
+
+- Feature gating
+  - [On use of the `default` keyword](specialization-feature-gate-default.rs)
+  - [On overlapping impls](specialization-feature-gate-overlap.rs)
+
+- Overlap checking with specialization enabled
+  - [Basic overlap scenarios](specialization-overlap.rs)
+    - Includes purely structural overlap
+    - Includes purely trait-based overlap
+    - Includes mix
+  - [Overlap with differing polarity](specialization-overlap-negative.rs)
+
+- [Attempt to specialize without using `default`](specialization-no-default.rs)
+
+- [Attempt to change impl polarity in a specialization](specialization-polarity.rs)
+
+- Attempt to rely on projection of a `default` type
+  - [Rely on it externally in both generic and monomorphic contexts](specialization-default-projection.rs)
+  - [Rely on it both within an impl and outside it](specialization-default-types.rs)
diff --git a/src/test/compile-fail/specialization/specialization-default-projection.rs b/src/test/compile-fail/specialization/specialization-default-projection.rs
new file mode 100644
index 0000000000000..96cbd7a485251
--- /dev/null
+++ b/src/test/compile-fail/specialization/specialization-default-projection.rs
@@ -0,0 +1,46 @@
+// 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.
+
+#![feature(specialization)]
+
+// Make sure we can't project defaulted associated types
+
+trait Foo {
+    type Assoc;
+}
+
+impl<T> Foo for T {
+    default type Assoc = ();
+}
+
+impl Foo for u8 {
+    type Assoc = String;
+}
+
+fn generic<T>() -> <T as Foo>::Assoc {
+    // `T` could be some downstream crate type that specializes (or,
+    // for that matter, `u8`).
+
+    () //~ ERROR mismatched types
+}
+
+fn monomorphic() -> () {
+    // Even though we know that `()` is not specialized in a
+    // downstream crate, typeck refuses to project here.
+
+    generic::<()>() //~ ERROR mismatched types
+}
+
+fn main() {
+    // No error here, we CAN project from `u8`, as there is no `default`
+    // in that impl.
+    let s: String = generic::<u8>();
+    println!("{}", s); // bad news if this all compiles
+}
diff --git a/src/test/compile-fail/specialization/specialization-default-types.rs b/src/test/compile-fail/specialization/specialization-default-types.rs
new file mode 100644
index 0000000000000..18acecb42296a
--- /dev/null
+++ b/src/test/compile-fail/specialization/specialization-default-types.rs
@@ -0,0 +1,45 @@
+// 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.
+
+// It should not be possible to use the concrete value of a defaulted
+// associated type in the impl defining it -- otherwise, what happens
+// if it's overridden?
+
+#![feature(specialization)]
+
+trait Example {
+    type Output;
+    fn generate(self) -> Self::Output;
+}
+
+impl<T> Example for T {
+    default type Output = Box<T>;
+    default fn generate(self) -> Self::Output {
+        Box::new(self) //~ ERROR mismatched types
+    }
+}
+
+impl Example for bool {
+    type Output = bool;
+    fn generate(self) -> bool { self }
+}
+
+fn trouble<T>(t: T) -> Box<T> {
+    Example::generate(t) //~ ERROR mismatched types
+}
+
+fn weaponize() -> bool {
+    let b: Box<bool> = trouble(true);
+    *b
+}
+
+fn main() {
+    weaponize();
+}
diff --git a/src/test/compile-fail/specialization/specialization-feature-gate-default.rs b/src/test/compile-fail/specialization/specialization-feature-gate-default.rs
new file mode 100644
index 0000000000000..e7c194ce84df9
--- /dev/null
+++ b/src/test/compile-fail/specialization/specialization-feature-gate-default.rs
@@ -0,0 +1,21 @@
+// 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.
+
+// Check that specialization must be ungated to use the `default` keyword
+
+trait Foo {
+    fn foo(&self);
+}
+
+impl<T> Foo for T {
+    default fn foo(&self) {} //~ ERROR specialization is unstable
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/specialization/specialization-feature-gate-overlap.rs b/src/test/compile-fail/specialization/specialization-feature-gate-overlap.rs
new file mode 100644
index 0000000000000..d11ab56ff7e87
--- /dev/null
+++ b/src/test/compile-fail/specialization/specialization-feature-gate-overlap.rs
@@ -0,0 +1,25 @@
+// 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.
+
+// Check that writing an overlapping impl is not allow unless specialization is ungated.
+
+trait Foo {
+    fn foo(&self);
+}
+
+impl<T> Foo for T {
+    fn foo(&self) {}
+}
+
+impl Foo for u8 { //~ ERROR E0119
+    fn foo(&self) {}
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/specialization/specialization-no-default.rs b/src/test/compile-fail/specialization/specialization-no-default.rs
new file mode 100644
index 0000000000000..961561685437a
--- /dev/null
+++ b/src/test/compile-fail/specialization/specialization-no-default.rs
@@ -0,0 +1,95 @@
+// 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.
+
+#![feature(specialization)]
+
+// Check a number of scenarios in which one impl tries to override another,
+// without correctly using `default`.
+
+////////////////////////////////////////////////////////////////////////////////
+// Test 1: one layer of specialization, multiple methods, missing `default`
+////////////////////////////////////////////////////////////////////////////////
+
+trait Foo {
+    fn foo(&self);
+    fn bar(&self);
+}
+
+impl<T> Foo for T {
+    fn foo(&self) {}
+    fn bar(&self) {}
+}
+
+impl Foo for u8 {}
+impl Foo for u16 {
+    fn foo(&self) {} //~ ERROR E0520
+}
+impl Foo for u32 {
+    fn bar(&self) {} //~ ERROR E0520
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Test 2: one layer of specialization, missing `default` on associated type
+////////////////////////////////////////////////////////////////////////////////
+
+trait Bar {
+    type T;
+}
+
+impl<T> Bar for T {
+    type T = u8;
+}
+
+impl Bar for u8 {
+    type T = (); //~ ERROR E0520
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Test 3a: multiple layers of specialization, missing interior `default`
+////////////////////////////////////////////////////////////////////////////////
+
+trait Baz {
+    fn baz(&self);
+}
+
+impl<T> Baz for T {
+    default fn baz(&self) {}
+}
+
+impl<T: Clone> Baz for T {
+    fn baz(&self) {}
+}
+
+impl Baz for i32 {
+    fn baz(&self) {} //~ ERROR E0520
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Test 3b: multiple layers of specialization, missing interior `default`,
+// redundant `default` in bottom layer.
+////////////////////////////////////////////////////////////////////////////////
+
+trait Redundant {
+    fn redundant(&self);
+}
+
+impl<T> Redundant for T {
+    default fn redundant(&self) {}
+}
+
+impl<T: Clone> Redundant for T {
+    fn redundant(&self) {}
+}
+
+impl Redundant for i32 {
+    default fn redundant(&self) {} //~ ERROR E0520
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/specialization/specialization-overlap-negative.rs b/src/test/compile-fail/specialization/specialization-overlap-negative.rs
new file mode 100644
index 0000000000000..62a6d8d9b5031
--- /dev/null
+++ b/src/test/compile-fail/specialization/specialization-overlap-negative.rs
@@ -0,0 +1,21 @@
+// 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.
+
+#![feature(optin_builtin_traits)]
+#![feature(specialization)]
+
+trait MyTrait {}
+
+struct TestType<T>(::std::marker::PhantomData<T>);
+
+unsafe impl<T: Clone> Send for TestType<T> {}
+impl<T: MyTrait> !Send for TestType<T> {} //~ ERROR E0119
+
+fn main() {}
diff --git a/src/test/compile-fail/specialization/specialization-overlap.rs b/src/test/compile-fail/specialization/specialization-overlap.rs
new file mode 100644
index 0000000000000..f579817100107
--- /dev/null
+++ b/src/test/compile-fail/specialization/specialization-overlap.rs
@@ -0,0 +1,29 @@
+// 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.
+
+#![feature(specialization)]
+
+trait Foo {}
+impl<T: Clone> Foo for T {}
+impl<T> Foo for Vec<T> {} //~ ERROR E0119
+
+trait Bar {}
+impl<T> Bar for (T, u8) {}
+impl<T> Bar for (u8, T) {} //~ ERROR E0119
+
+trait Baz<U> {}
+impl<T> Baz<T> for u8 {}
+impl<T> Baz<u8> for T {} //~ ERROR E0119
+
+trait Qux {}
+impl<T: Clone> Qux for T {}
+impl<T: Eq> Qux for T {} //~ ERROR E0119
+
+fn main() {}
diff --git a/src/test/compile-fail/specialization/specialization-polarity.rs b/src/test/compile-fail/specialization/specialization-polarity.rs
new file mode 100755
index 0000000000000..27a3e31491b82
--- /dev/null
+++ b/src/test/compile-fail/specialization/specialization-polarity.rs
@@ -0,0 +1,30 @@
+// 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.
+
+// Make sure specialization cannot change impl polarity
+
+#![feature(optin_builtin_traits)]
+#![feature(specialization)]
+
+trait Foo {}
+
+impl Foo for .. {}
+
+impl<T> Foo for T {}
+impl !Foo for u8 {} //~ ERROR E0119
+
+trait Bar {}
+
+impl Bar for .. {}
+
+impl<T> !Bar for T {}
+impl Bar for u8 {} //~ ERROR E0119
+
+fn main() {}
diff --git a/src/test/parse-fail/default.rs b/src/test/parse-fail/default.rs
new file mode 100644
index 0000000000000..d18401e67646c
--- /dev/null
+++ b/src/test/parse-fail/default.rs
@@ -0,0 +1,35 @@
+// 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.
+
+// compile-flags: -Z parse-only
+
+// Test successful and unsucessful parsing of the `default` contextual keyword
+
+trait Foo {
+    fn foo<T: Default>() -> T;
+}
+
+impl Foo for u8 {
+    default fn foo<T: Default>() -> T {
+        T::default()
+    }
+}
+
+impl Foo for u16 {
+    pub default fn foo<T: Default>() -> T {
+        T::default()
+    }
+}
+
+impl Foo for u32 {
+    default pub fn foo<T: Default>() -> T { T::default() } //~ ERROR expected one of
+}
+
+fn main() {}
diff --git a/src/test/parse-fail/issue-20711-2.rs b/src/test/parse-fail/issue-20711-2.rs
index be6bd516d6fe4..a489864e3f737 100644
--- a/src/test/parse-fail/issue-20711-2.rs
+++ b/src/test/parse-fail/issue-20711-2.rs
@@ -16,6 +16,6 @@ impl Foo {
     fn foo() {}
 
     #[stable(feature = "rust1", since = "1.0.0")]
-} //~ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}`
+} //~ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe`
 
 fn main() {}
diff --git a/src/test/parse-fail/issue-20711.rs b/src/test/parse-fail/issue-20711.rs
index d1d8d3acf9187..d9789d55a6faf 100644
--- a/src/test/parse-fail/issue-20711.rs
+++ b/src/test/parse-fail/issue-20711.rs
@@ -14,6 +14,6 @@ struct Foo;
 
 impl Foo {
     #[stable(feature = "rust1", since = "1.0.0")]
-} //~ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}`
+} //~ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe`
 
 fn main() {}
diff --git a/src/test/parse-fail/removed-syntax-static-fn.rs b/src/test/parse-fail/removed-syntax-static-fn.rs
index 7b6caad86b6cc..b4c25a75c9086 100644
--- a/src/test/parse-fail/removed-syntax-static-fn.rs
+++ b/src/test/parse-fail/removed-syntax-static-fn.rs
@@ -15,4 +15,4 @@ struct S;
 impl S {
     static fn f() {}
 }
-//~^^ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `static`
+//~^^ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`
diff --git a/src/test/run-pass/default-associated-types.rs b/src/test/run-pass/default-associated-types.rs
index 3e6c72c993a0a..ed55d5c8b171e 100644
--- a/src/test/run-pass/default-associated-types.rs
+++ b/src/test/run-pass/default-associated-types.rs
@@ -10,23 +10,22 @@
 
 #![feature(associated_type_defaults)]
 
-trait Foo<T> {
-    type Out = T;
-    fn foo(&self) -> Self::Out;
+trait Foo<T: Default + ToString> {
+    type Out: Default + ToString = T;
 }
 
 impl Foo<u32> for () {
-    fn foo(&self) -> u32 {
-        4u32
-    }
 }
 
-impl Foo<u64> for bool {
-    type Out = ();
-    fn foo(&self) {}
+impl Foo<u64> for () {
+    type Out = bool;
 }
 
 fn main() {
-    assert_eq!(<() as Foo<u32>>::foo(&()), 4u32);
-    assert_eq!(<bool as Foo<u64>>::foo(&true), ());
+    assert_eq!(
+        <() as Foo<u32>>::Out::default().to_string(),
+        "0");
+    assert_eq!(
+        <() as Foo<u64>>::Out::default().to_string(),
+        "false");
 }
diff --git a/src/test/run-pass/specialization/README.md b/src/test/run-pass/specialization/README.md
new file mode 100644
index 0000000000000..1373a2cf81b3a
--- /dev/null
+++ b/src/test/run-pass/specialization/README.md
@@ -0,0 +1,37 @@
+Tests that specialization is working correctly:
+
+- Dispatch
+  - [On methods](specialization-basics.rs), includes:
+    - Specialization via adding a trait bound
+      - Including both remote and local traits
+    - Specialization via pure structure (e.g. `(T, U)` vs `(T, T)`)
+    - Specialization via concrete types vs unknown types
+      - In top level of the trait reference
+      - Embedded within another type (`Vec<T>` vs `Vec<i32>`)
+  - [Specialization based on super trait relationships](specialization-super-traits.rs)
+  - [On assoc fns](specialization-assoc-fns.rs)
+  - [Ensure that impl order doesn't matter](specialization-out-of-order.rs)
+
+- Item inheritance
+  - [Correct default cascading for methods](specialization-default-methods.rs)
+  - Inheritance works across impls with varying generics
+    - [With projections](specialization-translate-projections.rs)
+    - [With projections that involve input types](specialization-translate-projections-with-params.rs)
+
+- Normalization issues
+  - [Non-default assoc types can be projected](specialization-projection.rs)
+    - Including non-specialized cases
+    - Including specialized cases
+  - [Specialized Impls can happen on projections](specialization-on-projection.rs)
+  - [Projections and aliases play well together](specialization-projection-alias.rs)
+  - [Projections involving specialization allowed in the trait ref for impls, and overlap can still be determined](specialization-overlap-projection.rs)
+    - Only works for the simple case where the most specialized impl directly
+      provides a non-`default` associated type
+
+- Across crates
+  - [For traits defined in upstream crate](specialization-allowed-cross-crate.rs)
+  - [Full method dispatch tests, drawing from upstream crate](specialization-cross-crate.rs)
+    - Including *additional* local specializations
+  - [Full method dispatch tests, *without* turning on specialization in local crate](specialization-cross-crate-no-gate.rs)
+  - [Test that defaults cascade correctly from upstream crates](specialization-cross-crate-defaults.rs)
+    - Including *additional* local use of defaults
diff --git a/src/test/run-pass/specialization/specialization-allowed-cross-crate.rs b/src/test/run-pass/specialization/specialization-allowed-cross-crate.rs
new file mode 100644
index 0000000000000..6b999f3835835
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-allowed-cross-crate.rs
@@ -0,0 +1,31 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:go_trait.rs
+
+#![feature(specialization)]
+
+extern crate go_trait;
+
+use go_trait::{Go,GoMut};
+use std::fmt::Debug;
+use std::default::Default;
+
+struct MyThingy;
+
+impl Go for MyThingy {
+    fn go(&self, arg: isize) { }
+}
+
+impl GoMut for MyThingy {
+    fn go_mut(&mut self, arg: isize) { }
+}
+
+fn main() { }
diff --git a/src/test/run-pass/specialization/specialization-assoc-fns.rs b/src/test/run-pass/specialization/specialization-assoc-fns.rs
new file mode 100644
index 0000000000000..577f217862da3
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-assoc-fns.rs
@@ -0,0 +1,37 @@
+// 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.
+
+// Test that non-method associated functions can be specialized
+
+#![feature(specialization)]
+
+trait Foo {
+    fn mk() -> Self;
+}
+
+impl<T: Default> Foo for T {
+    default fn mk() -> T {
+        T::default()
+    }
+}
+
+impl Foo for Vec<u8> {
+    fn mk() -> Vec<u8> {
+        vec![0]
+    }
+}
+
+fn main() {
+    let v1: Vec<i32> = Foo::mk();
+    let v2: Vec<u8> = Foo::mk();
+
+    assert!(v1.len() == 0);
+    assert!(v2.len() == 1);
+}
diff --git a/src/test/run-pass/specialization/specialization-basics.rs b/src/test/run-pass/specialization/specialization-basics.rs
new file mode 100644
index 0000000000000..b11495e9edf1a
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-basics.rs
@@ -0,0 +1,106 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(specialization)]
+
+// Tests a variety of basic specialization scenarios and method
+// dispatch for them.
+
+trait Foo {
+    fn foo(&self) -> &'static str;
+}
+
+impl<T> Foo for T {
+    default fn foo(&self) -> &'static str {
+        "generic"
+    }
+}
+
+impl<T: Clone> Foo for T {
+    default fn foo(&self) -> &'static str {
+        "generic Clone"
+    }
+}
+
+impl<T, U> Foo for (T, U) where T: Clone, U: Clone {
+    default fn foo(&self) -> &'static str {
+        "generic pair"
+    }
+}
+
+impl<T: Clone> Foo for (T, T) {
+    default fn foo(&self) -> &'static str {
+        "generic uniform pair"
+    }
+}
+
+impl Foo for (u8, u32) {
+    default fn foo(&self) -> &'static str {
+        "(u8, u32)"
+    }
+}
+
+impl Foo for (u8, u8) {
+    default fn foo(&self) -> &'static str {
+        "(u8, u8)"
+    }
+}
+
+impl<T: Clone> Foo for Vec<T> {
+    default fn foo(&self) -> &'static str {
+        "generic Vec"
+    }
+}
+
+impl Foo for Vec<i32> {
+    fn foo(&self) -> &'static str {
+        "Vec<i32>"
+    }
+}
+
+impl Foo for String {
+    fn foo(&self) -> &'static str {
+        "String"
+    }
+}
+
+impl Foo for i32 {
+    fn foo(&self) -> &'static str {
+        "i32"
+    }
+}
+
+struct NotClone;
+
+trait MyMarker {}
+impl<T: Clone + MyMarker> Foo for T {
+    default fn foo(&self) -> &'static str {
+        "generic Clone + MyMarker"
+    }
+}
+
+#[derive(Clone)]
+struct MarkedAndClone;
+impl MyMarker for MarkedAndClone {}
+
+fn  main() {
+    assert!(NotClone.foo() == "generic");
+    assert!(0u8.foo() == "generic Clone");
+    assert!(vec![NotClone].foo() == "generic");
+    assert!(vec![0u8].foo() == "generic Vec");
+    assert!(vec![0i32].foo() == "Vec<i32>");
+    assert!(0i32.foo() == "i32");
+    assert!(String::new().foo() == "String");
+    assert!(((), 0).foo() == "generic pair");
+    assert!(((), ()).foo() == "generic uniform pair");
+    assert!((0u8, 0u32).foo() == "(u8, u32)");
+    assert!((0u8, 0u8).foo() == "(u8, u8)");
+    assert!(MarkedAndClone.foo() == "generic Clone + MyMarker");
+}
diff --git a/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs b/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs
new file mode 100644
index 0000000000000..bc695ea821d0a
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs
@@ -0,0 +1,49 @@
+// 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.
+
+// aux-build:specialization_cross_crate_defaults.rs
+
+#![feature(specialization)]
+
+extern crate specialization_cross_crate_defaults;
+
+use specialization_cross_crate_defaults::*;
+
+struct LocalDefault;
+struct LocalOverride;
+
+impl Foo for LocalDefault {}
+
+impl Foo for LocalOverride {
+    fn foo(&self) -> bool { true }
+}
+
+fn test_foo() {
+    assert!(0i8.foo() == false);
+    assert!(0i32.foo() == false);
+    assert!(0i64.foo() == true);
+
+    assert!(LocalDefault.foo() == false);
+    assert!(LocalOverride.foo() == true);
+}
+
+fn test_bar() {
+    assert!(0u8.bar() == 0);
+    assert!(0i32.bar() == 1);
+    assert!("hello".bar() == 0);
+    assert!(vec![()].bar() == 2);
+    assert!(vec![0i32].bar() == 2);
+    assert!(vec![0i64].bar() == 3);
+}
+
+fn main() {
+    test_foo();
+    test_bar();
+}
diff --git a/src/test/run-pass/specialization/specialization-cross-crate-no-gate.rs b/src/test/run-pass/specialization/specialization-cross-crate-no-gate.rs
new file mode 100644
index 0000000000000..b9548539e1649
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-cross-crate-no-gate.rs
@@ -0,0 +1,29 @@
+// 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.
+
+// Test that specialization works even if only the upstream crate enables it
+
+// aux-build:specialization_cross_crate.rs
+
+extern crate specialization_cross_crate;
+
+use specialization_cross_crate::*;
+
+fn  main() {
+    assert!(0u8.foo() == "generic Clone");
+    assert!(vec![0u8].foo() == "generic Vec");
+    assert!(vec![0i32].foo() == "Vec<i32>");
+    assert!(0i32.foo() == "i32");
+    assert!(String::new().foo() == "String");
+    assert!(((), 0).foo() == "generic pair");
+    assert!(((), ()).foo() == "generic uniform pair");
+    assert!((0u8, 0u32).foo() == "(u8, u32)");
+    assert!((0u8, 0u8).foo() == "(u8, u8)");
+}
diff --git a/src/test/run-pass/specialization/specialization-cross-crate.rs b/src/test/run-pass/specialization/specialization-cross-crate.rs
new file mode 100644
index 0000000000000..7593ac4fb1dd2
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-cross-crate.rs
@@ -0,0 +1,58 @@
+// 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.
+
+// aux-build:specialization_cross_crate.rs
+
+#![feature(specialization)]
+
+extern crate specialization_cross_crate;
+
+use specialization_cross_crate::*;
+
+struct NotClone;
+
+#[derive(Clone)]
+struct MarkedAndClone;
+impl MyMarker for MarkedAndClone {}
+
+struct MyType<T>(T);
+impl<T> Foo for MyType<T> {
+    default fn foo(&self) -> &'static str {
+        "generic MyType"
+    }
+}
+
+impl Foo for MyType<u8> {
+    fn foo(&self) -> &'static str {
+        "MyType<u8>"
+    }
+}
+
+struct MyOtherType;
+impl Foo for MyOtherType {}
+
+fn  main() {
+    assert!(NotClone.foo() == "generic");
+    assert!(0u8.foo() == "generic Clone");
+    assert!(vec![NotClone].foo() == "generic");
+    assert!(vec![0u8].foo() == "generic Vec");
+    assert!(vec![0i32].foo() == "Vec<i32>");
+    assert!(0i32.foo() == "i32");
+    assert!(String::new().foo() == "String");
+    assert!(((), 0).foo() == "generic pair");
+    assert!(((), ()).foo() == "generic uniform pair");
+    assert!((0u8, 0u32).foo() == "(u8, u32)");
+    assert!((0u8, 0u8).foo() == "(u8, u8)");
+    assert!(MarkedAndClone.foo() == "generic Clone + MyMarker");
+
+    assert!(MyType(()).foo() == "generic MyType");
+    assert!(MyType(0u8).foo() == "MyType<u8>");
+    assert!(MyOtherType.foo() == "generic");
+}
diff --git a/src/test/run-pass/specialization/specialization-default-methods.rs b/src/test/run-pass/specialization/specialization-default-methods.rs
new file mode 100644
index 0000000000000..3f0f21ff03f27
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-default-methods.rs
@@ -0,0 +1,94 @@
+// 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.
+
+#![feature(specialization)]
+
+// Test that default methods are cascaded correctly
+
+// First, test only use of explicit `default` items:
+
+trait Foo {
+    fn foo(&self) -> bool;
+}
+
+// Specialization tree for Foo:
+//
+//        T
+//       / \
+//    i32   i64
+
+impl<T> Foo for T {
+    default fn foo(&self) -> bool { false }
+}
+
+impl Foo for i32 {}
+
+impl Foo for i64 {
+    fn foo(&self) -> bool { true }
+}
+
+fn test_foo() {
+    assert!(0i8.foo() == false);
+    assert!(0i32.foo() == false);
+    assert!(0i64.foo() == true);
+}
+
+// Next, test mixture of explicit `default` and provided methods:
+
+trait Bar {
+    fn bar(&self) -> i32 { 0 }
+}
+
+// Specialization tree for Bar.
+// Uses of $ designate that method is provided
+//
+//           $Bar   (the trait)
+//             |
+//             T
+//            /|\
+//           / | \
+//          /  |  \
+//         /   |   \
+//        /    |    \
+//       /     |     \
+//     $i32   &str  $Vec<T>
+//                    /\
+//                   /  \
+//            Vec<i32>  $Vec<i64>
+
+// use the provided method
+impl<T> Bar for T {}
+
+impl Bar for i32 {
+    fn bar(&self) -> i32 { 1 }
+}
+impl<'a> Bar for &'a str {}
+
+impl<T> Bar for Vec<T> {
+    default fn bar(&self) -> i32 { 2 }
+}
+impl Bar for Vec<i32> {}
+impl Bar for Vec<i64> {
+    fn bar(&self) -> i32 { 3 }
+}
+
+fn test_bar() {
+    assert!(0u8.bar() == 0);
+    assert!(0i32.bar() == 1);
+    assert!("hello".bar() == 0);
+    assert!(vec![()].bar() == 2);
+    assert!(vec![0i32].bar() == 2);
+    assert!(vec![0i64].bar() == 3);
+}
+
+fn main() {
+    test_foo();
+    test_bar();
+}
diff --git a/src/test/run-pass/specialization/specialization-on-projection.rs b/src/test/run-pass/specialization/specialization-on-projection.rs
new file mode 100644
index 0000000000000..acf78def1b967
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-on-projection.rs
@@ -0,0 +1,31 @@
+// 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.
+
+#![feature(specialization)]
+
+// Ensure that specialization works for impls defined directly on a projection
+
+trait Foo<T> {}
+
+trait Assoc {
+    type Item;
+}
+
+impl<T: Assoc> Foo<T::Item> for T {}
+
+struct Struct;
+
+impl Assoc for Struct {
+    type Item = u8;
+}
+
+impl Foo<u8> for Struct {}
+
+fn main() {}
diff --git a/src/test/run-pass/specialization/specialization-out-of-order.rs b/src/test/run-pass/specialization/specialization-out-of-order.rs
new file mode 100644
index 0000000000000..2d293f494a347
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-out-of-order.rs
@@ -0,0 +1,27 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that you can list the more specific impl before the more general one.
+
+#![feature(specialization)]
+
+trait Foo {
+    type Out;
+}
+
+impl Foo for bool {
+    type Out = ();
+}
+
+impl<T> Foo for T {
+    default type Out = bool;
+}
+
+fn main() {}
diff --git a/src/test/run-pass/specialization/specialization-overlap-projection.rs b/src/test/run-pass/specialization/specialization-overlap-projection.rs
new file mode 100644
index 0000000000000..20046ee66b0e2
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-overlap-projection.rs
@@ -0,0 +1,33 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that impls on projected self types can resolve overlap, even when the
+// projections involve specialization, so long as the associated type is
+// provided by the most specialized impl.
+
+#![feature(specialization)]
+
+trait Assoc {
+    type Output;
+}
+
+impl<T> Assoc for T {
+    default type Output = bool;
+}
+
+impl Assoc for u8 { type Output = u8; }
+impl Assoc for u16 { type Output = u16; }
+
+trait Foo {}
+impl Foo for u32 {}
+impl Foo for <u8 as Assoc>::Output {}
+impl Foo for <u16 as Assoc>::Output {}
+
+fn main() {}
diff --git a/src/test/run-pass/specialization/specialization-projection-alias.rs b/src/test/run-pass/specialization/specialization-projection-alias.rs
new file mode 100644
index 0000000000000..7fce1cca582c1
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-projection-alias.rs
@@ -0,0 +1,32 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(specialization)]
+
+// Regression test for ICE when combining specialized associated types and type
+// aliases
+
+trait Id_ {
+    type Out;
+}
+
+type Id<T> = <T as Id_>::Out;
+
+impl<T> Id_ for T {
+    default type Out = T;
+}
+
+fn test_proection() {
+    let x: Id<bool> = panic!();
+}
+
+fn main() {
+
+}
diff --git a/src/test/run-pass/specialization/specialization-projection.rs b/src/test/run-pass/specialization/specialization-projection.rs
new file mode 100644
index 0000000000000..4e0bdec297fe2
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-projection.rs
@@ -0,0 +1,49 @@
+// 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.
+
+#![feature(specialization)]
+
+// Make sure we *can* project non-defaulted associated types
+// cf compile-fail/specialization-default-projection.rs
+
+// First, do so without any use of specialization
+
+trait Foo {
+    type Assoc;
+}
+
+impl<T> Foo for T {
+    type Assoc = ();
+}
+
+fn generic_foo<T>() -> <T as Foo>::Assoc {
+    ()
+}
+
+// Next, allow for one layer of specialization
+
+trait Bar {
+    type Assoc;
+}
+
+impl<T> Bar for T {
+    default type Assoc = ();
+}
+
+impl<T: Clone> Bar for T {
+    type Assoc = u8;
+}
+
+fn generic_bar_clone<T: Clone>() -> <T as Bar>::Assoc {
+    0u8
+}
+
+fn main() {
+}
diff --git a/src/test/run-pass/specialization/specialization-super-traits.rs b/src/test/run-pass/specialization/specialization-super-traits.rs
new file mode 100644
index 0000000000000..a9b3bfca53d18
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-super-traits.rs
@@ -0,0 +1,25 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(specialization)]
+
+// Test that you can specialize via an explicit trait hierarchy
+
+// FIXME: this doesn't work yet...
+
+trait Parent {}
+trait Child: Parent {}
+
+trait Foo {}
+
+impl<T: Parent> Foo for T {}
+impl<T: Child> Foo for T {}
+
+fn main() {}
diff --git a/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs b/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs
new file mode 100644
index 0000000000000..647d5523c376c
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs
@@ -0,0 +1,40 @@
+// 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.
+
+// Ensure that provided items are inherited properly even when impls vary in
+// type parameters *and* rely on projections, and the type parameters are input
+// types on the trait.
+
+#![feature(specialization)]
+
+trait Trait<T> {
+    fn convert(&self) -> T;
+}
+trait WithAssoc {
+    type Item;
+    fn as_item(&self) -> &Self::Item;
+}
+
+impl<T, U> Trait<U> for T where T: WithAssoc<Item=U>, U: Clone {
+    fn convert(&self) -> U {
+        self.as_item().clone()
+    }
+}
+
+impl WithAssoc for u8 {
+    type Item = u8;
+    fn as_item(&self) -> &u8 { self }
+}
+
+impl Trait<u8> for u8 {}
+
+fn main() {
+    assert!(3u8.convert() == 3u8);
+}
diff --git a/src/test/run-pass/specialization/specialization-translate-projections.rs b/src/test/run-pass/specialization/specialization-translate-projections.rs
new file mode 100644
index 0000000000000..11e1d997fdda0
--- /dev/null
+++ b/src/test/run-pass/specialization/specialization-translate-projections.rs
@@ -0,0 +1,41 @@
+// 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.
+
+// Ensure that provided items are inherited properly even when impls vary in
+// type parameters *and* rely on projections.
+
+#![feature(specialization)]
+
+use std::convert::Into;
+
+trait Trait {
+    fn to_u8(&self) -> u8;
+}
+trait WithAssoc {
+    type Item;
+    fn to_item(&self) -> Self::Item;
+}
+
+impl<T, U> Trait for T where T: WithAssoc<Item=U>, U: Into<u8> {
+    fn to_u8(&self) -> u8 {
+        self.to_item().into()
+    }
+}
+
+impl WithAssoc for u8 {
+    type Item = u8;
+    fn to_item(&self) -> u8 { *self }
+}
+
+impl Trait for u8 {}
+
+fn main() {
+    assert!(3u8.to_u8() == 3u8);
+}
diff --git a/src/test/run-pass/xcrate-associated-type-defaults.rs b/src/test/run-pass/xcrate-associated-type-defaults.rs
index 1b6de3b2f7bcf..2dacbe0966ee3 100644
--- a/src/test/run-pass/xcrate-associated-type-defaults.rs
+++ b/src/test/run-pass/xcrate-associated-type-defaults.rs
@@ -13,6 +13,26 @@
 extern crate xcrate_associated_type_defaults;
 use xcrate_associated_type_defaults::Foo;
 
+struct LocalDefault;
+impl Foo<u32> for LocalDefault {}
+
+struct LocalOverride;
+impl Foo<u64> for LocalOverride {
+    type Out = bool;
+}
+
 fn main() {
-    ().bar(5);
+    assert_eq!(
+        <() as Foo<u32>>::Out::default().to_string(),
+        "0");
+    assert_eq!(
+        <() as Foo<u64>>::Out::default().to_string(),
+        "false");
+
+    assert_eq!(
+        <LocalDefault as Foo<u32>>::Out::default().to_string(),
+        "0");
+    assert_eq!(
+        <LocalOverride as Foo<u64>>::Out::default().to_string(),
+        "false");
 }