about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/middle/traits/error_reporting.rs132
-rw-r--r--src/librustc/middle/traits/fulfill.rs135
-rw-r--r--src/librustc/middle/traits/mod.rs14
-rw-r--r--src/librustc/middle/traits/project.rs369
-rw-r--r--src/librustc_trans/trans/common.rs2
-rw-r--r--src/librustc_trans/trans/monomorphize.rs64
-rw-r--r--src/librustc_typeck/check/assoc.rs89
-rw-r--r--src/librustc_typeck/check/mod.rs25
-rw-r--r--src/test/compile-fail/associated-types-path-2.rs29
-rw-r--r--src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs3
-rw-r--r--src/test/compile-fail/associated-types-unconstrained.rs28
-rw-r--r--src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs2
-rw-r--r--src/test/run-pass/associated-types-impl-redirect.rs58
13 files changed, 575 insertions, 375 deletions
diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs
index a3d92c698a2..a0413701abc 100644
--- a/src/librustc/middle/traits/error_reporting.rs
+++ b/src/librustc/middle/traits/error_reporting.rs
@@ -77,13 +77,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                     "overflow evaluating the requirement `{}`",
                     predicate.user_string(infcx.tcx)).as_slice());
 
-            let current_limit = infcx.tcx.sess.recursion_limit.get();
-            let suggested_limit = current_limit * 2;
-            infcx.tcx.sess.span_note(
-                obligation.cause.span,
-                format!(
-                    "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
-                    suggested_limit)[]);
+            suggest_new_overflow_limit(infcx, obligation.cause.span);
 
             note_obligation_cause(infcx, obligation);
         }
@@ -165,73 +159,76 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
     // ambiguous impls. The latter *ought* to be a
     // coherence violation, so we don't report it here.
 
-    let trait_ref = match obligation.predicate {
-        ty::Predicate::Trait(ref trait_predicate) => {
-            infcx.resolve_type_vars_if_possible(
-                &trait_predicate.to_poly_trait_ref())
-        }
-        _ => {
-            infcx.tcx.sess.span_bug(
-                obligation.cause.span,
-                format!("ambiguity from something other than a trait: {}",
-                        obligation.predicate.repr(infcx.tcx)).as_slice());
-        }
-    };
-
-    let self_ty = trait_ref.self_ty();
+    let predicate = infcx.resolve_type_vars_if_possible(&obligation.predicate);
 
-    debug!("maybe_report_ambiguity(trait_ref={}, self_ty={}, obligation={})",
-           trait_ref.repr(infcx.tcx),
-           self_ty.repr(infcx.tcx),
+    debug!("maybe_report_ambiguity(predicate={}, obligation={})",
+           predicate.repr(infcx.tcx),
            obligation.repr(infcx.tcx));
-    let all_types = &trait_ref.substs().types;
-    if all_types.iter().any(|&t| ty::type_is_error(t)) {
-    } else if all_types.iter().any(|&t| ty::type_needs_infer(t)) {
-        // This is kind of a hack: it frequently happens that some earlier
-        // error prevents types from being fully inferred, and then we get
-        // a bunch of uninteresting errors saying something like "<generic
-        // #0> doesn't implement Sized".  It may even be true that we
-        // could just skip over all checks where the self-ty is an
-        // inference variable, but I was afraid that there might be an
-        // inference variable created, registered as an obligation, and
-        // then never forced by writeback, and hence by skipping here we'd
-        // be ignoring the fact that we don't KNOW the type works
-        // out. Though even that would probably be harmless, given that
-        // we're only talking about builtin traits, which are known to be
-        // inhabited. But in any case I just threw in this check for
-        // has_errors() to be sure that compilation isn't happening
-        // anyway. In that case, why inundate the user.
-        if !infcx.tcx.sess.has_errors() {
-            if infcx.tcx.lang_items.sized_trait()
-                  .map_or(false, |sized_id| sized_id == trait_ref.def_id()) {
-                infcx.tcx.sess.span_err(
+
+    match predicate {
+        ty::Predicate::Trait(ref data) => {
+            let trait_ref = data.to_poly_trait_ref();
+            let self_ty = trait_ref.self_ty();
+            let all_types = &trait_ref.substs().types;
+            if all_types.iter().any(|&t| ty::type_is_error(t)) {
+            } else if all_types.iter().any(|&t| ty::type_needs_infer(t)) {
+                // This is kind of a hack: it frequently happens that some earlier
+                // error prevents types from being fully inferred, and then we get
+                // a bunch of uninteresting errors saying something like "<generic
+                // #0> doesn't implement Sized".  It may even be true that we
+                // could just skip over all checks where the self-ty is an
+                // inference variable, but I was afraid that there might be an
+                // inference variable created, registered as an obligation, and
+                // then never forced by writeback, and hence by skipping here we'd
+                // be ignoring the fact that we don't KNOW the type works
+                // out. Though even that would probably be harmless, given that
+                // we're only talking about builtin traits, which are known to be
+                // inhabited. But in any case I just threw in this check for
+                // has_errors() to be sure that compilation isn't happening
+                // anyway. In that case, why inundate the user.
+                if !infcx.tcx.sess.has_errors() {
+                    if
+                        infcx.tcx.lang_items.sized_trait()
+                        .map_or(false, |sized_id| sized_id == trait_ref.def_id())
+                    {
+                        infcx.tcx.sess.span_err(
+                            obligation.cause.span,
+                            format!(
+                                "unable to infer enough type information about `{}`; \
+                                 type annotations required",
+                                self_ty.user_string(infcx.tcx)).as_slice());
+                    } else {
+                        infcx.tcx.sess.span_err(
+                            obligation.cause.span,
+                            format!(
+                                "type annotations required: cannot resolve `{}`",
+                                predicate.user_string(infcx.tcx)).as_slice());
+                        note_obligation_cause(infcx, obligation);
+                    }
+                }
+            } else if !infcx.tcx.sess.has_errors() {
+                // Ambiguity. Coherence should have reported an error.
+                infcx.tcx.sess.span_bug(
                     obligation.cause.span,
                     format!(
-                        "unable to infer enough type information about `{}`; type annotations \
-                         required",
+                        "coherence failed to report ambiguity: \
+                         cannot locate the impl of the trait `{}` for \
+                         the type `{}`",
+                        trait_ref.user_string(infcx.tcx),
                         self_ty.user_string(infcx.tcx)).as_slice());
-            } else {
+            }
+        }
+
+        _ => {
+            if !infcx.tcx.sess.has_errors() {
                 infcx.tcx.sess.span_err(
                     obligation.cause.span,
                     format!(
-                        "unable to infer enough type information to \
-                         locate the impl of the trait `{}` for \
-                         the type `{}`; type annotations required",
-                        trait_ref.user_string(infcx.tcx),
-                        self_ty.user_string(infcx.tcx)).as_slice());
+                        "type annotations required: cannot resolve `{}`",
+                        predicate.user_string(infcx.tcx)).as_slice());
                 note_obligation_cause(infcx, obligation);
             }
         }
-    } else if !infcx.tcx.sess.has_errors() {
-         // Ambiguity. Coherence should have reported an error.
-        infcx.tcx.sess.span_bug(
-            obligation.cause.span,
-            format!(
-                "coherence failed to report ambiguity: \
-                 cannot locate the impl of the trait `{}` for \
-                 the type `{}`",
-                trait_ref.user_string(infcx.tcx),
-                self_ty.user_string(infcx.tcx)).as_slice());
     }
 }
 
@@ -335,3 +332,12 @@ fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
     }
 }
 
+pub fn suggest_new_overflow_limit(infcx: &InferCtxt, span: Span) {
+    let current_limit = infcx.tcx.sess.recursion_limit.get();
+    let suggested_limit = current_limit * 2;
+    infcx.tcx.sess.span_note(
+        span,
+        format!(
+            "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
+            suggested_limit)[]);
+}
diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs
index 7ec221fcfa9..c85baccd633 100644
--- a/src/librustc/middle/traits/fulfill.rs
+++ b/src/librustc/middle/traits/fulfill.rs
@@ -8,9 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use middle::infer::{mod, InferCtxt};
+use middle::infer::{InferCtxt};
 use middle::mem_categorization::Typer;
-use middle::ty::{mod, AsPredicate, RegionEscape, Ty, ToPolyTraitRef};
+use middle::ty::{mod, RegionEscape, Ty};
 use std::collections::HashSet;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::default::Default;
@@ -23,7 +23,6 @@ use super::CodeAmbiguity;
 use super::CodeProjectionError;
 use super::CodeSelectionError;
 use super::FulfillmentError;
-use super::Obligation;
 use super::ObligationCause;
 use super::PredicateObligation;
 use super::project;
@@ -110,6 +109,8 @@ impl<'tcx> FulfillmentContext<'tcx> {
     /// `projection_ty` again.
     pub fn normalize_projection_type<'a>(&mut self,
                                          infcx: &InferCtxt<'a,'tcx>,
+                                         param_env: &ty::ParameterEnvironment<'tcx>,
+                                         typer: &Typer<'tcx>,
                                          projection_ty: ty::ProjectionTy<'tcx>,
                                          cause: ObligationCause<'tcx>)
                                          -> Ty<'tcx>
@@ -121,18 +122,16 @@ impl<'tcx> FulfillmentContext<'tcx> {
 
         // FIXME(#20304) -- cache
 
-        let ty_var = infcx.next_ty_var();
-        let projection =
-            ty::Binder(ty::ProjectionPredicate {
-                projection_ty: projection_ty,
-                ty: ty_var
-            });
-        let obligation = Obligation::new(cause, projection.as_predicate());
-        self.register_predicate(infcx, obligation);
+        let mut selcx = SelectionContext::new(infcx, param_env, typer);
+        let normalized = project::normalize_projection_type(&mut selcx, projection_ty, cause, 0);
+
+        for obligation in normalized.obligations.into_iter() {
+            self.register_predicate_obligation(infcx, obligation);
+        }
 
-        debug!("normalize_associated_type: result={}", ty_var.repr(infcx.tcx));
+        debug!("normalize_associated_type: result={}", normalized.value.repr(infcx.tcx));
 
-        ty_var
+        normalized.value
     }
 
     pub fn register_builtin_bound<'a>(&mut self,
@@ -143,7 +142,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
     {
         match predicate_for_builtin_bound(infcx.tcx, cause, builtin_bound, 0, ty) {
             Ok(predicate) => {
-                self.register_predicate(infcx, predicate);
+                self.register_predicate_obligation(infcx, predicate);
             }
             Err(ErrorReported) => { }
         }
@@ -158,10 +157,14 @@ impl<'tcx> FulfillmentContext<'tcx> {
         register_region_obligation(infcx.tcx, t_a, r_b, cause, &mut self.region_obligations);
     }
 
-    pub fn register_predicate<'a>(&mut self,
-                                  infcx: &InferCtxt<'a,'tcx>,
-                                  obligation: PredicateObligation<'tcx>)
+    pub fn register_predicate_obligation<'a>(&mut self,
+                                             infcx: &InferCtxt<'a,'tcx>,
+                                             obligation: PredicateObligation<'tcx>)
     {
+        // this helps to reduce duplicate errors, as well as making
+        // debug output much nicer to read and so on.
+        let obligation = infcx.resolve_type_vars_if_possible(&obligation);
+
         if !self.duplicate_set.insert(obligation.predicate.clone()) {
             debug!("register_predicate({}) -- already seen, skip", obligation.repr(infcx.tcx));
             return;
@@ -290,7 +293,7 @@ impl<'tcx> FulfillmentContext<'tcx> {
             // Now go through all the successful ones,
             // registering any nested obligations for the future.
             for new_obligation in new_obligations.into_iter() {
-                self.register_predicate(selcx.infcx(), new_obligation);
+                self.register_predicate_obligation(selcx.infcx(), new_obligation);
             }
         }
 
@@ -398,104 +401,18 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
                    project_obligation.repr(tcx),
                    result.repr(tcx));
             match result {
-                Ok(()) => {
+                Ok(Some(obligations)) => {
+                    new_obligations.extend(obligations.into_iter());
                     true
                 }
-                Err(project::ProjectionError::TooManyCandidates) => {
-                    // Without more type information, we can't say much.
+                Ok(None) => {
                     false
                 }
-                Err(project::ProjectionError::NoCandidate) => {
-                    // This means that we have a type like `<T as
-                    // Trait>::name = U` but we couldn't find any more
-                    // information. This could just be that we're in a
-                    // function like:
-                    //
-                    //     fn foo<T:Trait>(...)
-                    //
-                    // in which case this is not an error. But it
-                    // might also mean we're in a situation where we
-                    // don't actually know that `T : Trait` holds,
-                    // which would be weird (e.g., if `T` was not a
-                    // parameter type but a normal type, like `int`).
-                    //
-                    // So what we do is to (1) add a requirement that
-                    // `T : Trait` (just in case) and (2) try to unify
-                    // `U` with `<T as Trait>::name`.
-
-                    if !ty::binds_late_bound_regions(selcx.tcx(), data) {
-                        // Check that `T : Trait` holds.
-                        let trait_ref = data.to_poly_trait_ref();
-                        new_obligations.push(obligation.with(trait_ref.as_predicate()));
-
-                        // Fallback to `<T as Trait>::name`. If this
-                        // fails, then the output must be at least
-                        // somewhat constrained, and we cannot verify
-                        // that constraint, so yield an error.
-                        let ty_projection = ty::mk_projection(tcx,
-                                                              trait_ref.0.clone(),
-                                                              data.0.projection_ty.item_name);
-
-                        debug!("process_predicate: falling back to projection {}",
-                               ty_projection.repr(selcx.tcx()));
-
-                        match infer::mk_eqty(selcx.infcx(),
-                                             true,
-                                             infer::EquatePredicate(obligation.cause.span),
-                                             ty_projection,
-                                             data.0.ty) {
-                            Ok(()) => { }
-                            Err(_) => {
-                                debug!("process_predicate: fallback failed to unify; error");
-                                errors.push(
-                                    FulfillmentError::new(
-                                        obligation.clone(),
-                                        CodeSelectionError(Unimplemented)));
-                            }
-                        }
-
-                        true
-                    } else {
-                        // If we have something like
-                        //
-                        //     for<'a> <T<'a> as Trait>::name == &'a int
-                        //
-                        // there is no "canonical form" for us to
-                        // make, so just report the lack of candidates
-                        // as an error.
-
-                        debug!("process_predicate: can't fallback, higher-ranked");
-                        errors.push(
-                            FulfillmentError::new(
-                                obligation.clone(),
-                                CodeSelectionError(Unimplemented)));
-
-                        true
-                    }
-                }
-                Err(project::ProjectionError::MismatchedTypes(e)) => {
+                Err(err) => {
                     errors.push(
                         FulfillmentError::new(
                             obligation.clone(),
-                            CodeProjectionError(e)));
-                    true
-                }
-                Err(project::ProjectionError::TraitSelectionError(_)) => {
-                    // There was an error matching `T : Trait` (which
-                    // is a pre-requisite for `<T as Trait>::Name`
-                    // being valid).  We could just report the error
-                    // now, but that tends to lead to double error
-                    // reports for the user (one for the obligation `T
-                    // : Trait`, typically incurred somewhere else,
-                    // and one from here). Instead, we'll create the
-                    // `T : Trait` obligation and add THAT as a
-                    // requirement. This will (eventually) trigger the
-                    // same error, but it will also wind up flagged as
-                    // a duplicate if another requirement that `T :
-                    // Trait` arises from somewhere else.
-                    let trait_predicate = data.to_poly_trait_ref();
-                    let trait_obligation = obligation.with(trait_predicate.as_predicate());
-                    new_obligations.push(trait_obligation);
+                            CodeProjectionError(err)));
                     true
                 }
             }
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index d4fa0c98ad5..b10dfa5b718 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -27,8 +27,8 @@ use util::ppaux::Repr;
 pub use self::error_reporting::report_fulfillment_errors;
 pub use self::fulfill::{FulfillmentContext, RegionObligation};
 pub use self::project::MismatchedProjectionTypes;
-pub use self::project::project_type;
-pub use self::project::ProjectionResult;
+pub use self::project::normalize;
+pub use self::project::Normalized;
 pub use self::select::SelectionContext;
 pub use self::select::SelectionCache;
 pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
@@ -320,6 +320,16 @@ impl<'tcx,O> Obligation<'tcx,O> {
                      predicate: trait_ref }
     }
 
+    fn with_depth(cause: ObligationCause<'tcx>,
+                  recursion_depth: uint,
+                  trait_ref: O)
+                  -> Obligation<'tcx, O>
+    {
+        Obligation { cause: cause,
+                     recursion_depth: recursion_depth,
+                     predicate: trait_ref }
+    }
+
     pub fn misc(span: Span, body_id: ast::NodeId, trait_ref: O) -> Obligation<'tcx, O> {
         Obligation::new(ObligationCause::misc(span, body_id), trait_ref)
     }
diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs
index 0fa171523f4..28a826b859b 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::Obligation;
+use super::ObligationCause;
+use super::Overflow;
 use super::PredicateObligation;
 use super::SelectionContext;
 use super::SelectionError;
@@ -19,7 +21,8 @@ use super::VtableImplData;
 
 use middle::infer;
 use middle::subst::Subst;
-use middle::ty::{mod, AsPredicate, ToPolyTraitRef, Ty};
+use middle::ty::{mod, AsPredicate, RegionEscape, HasProjectionTypes, ToPolyTraitRef, Ty};
+use middle::ty_fold::{mod, TypeFoldable, TypeFolder};
 use util::ppaux::Repr;
 
 pub type PolyProjectionObligation<'tcx> =
@@ -31,21 +34,11 @@ pub type ProjectionObligation<'tcx> =
 pub type ProjectionTyObligation<'tcx> =
     Obligation<'tcx, ty::ProjectionTy<'tcx>>;
 
-/// When attempting to resolve `<T as TraitRef>::Name == U`...
-pub enum ProjectionError<'tcx> {
-    /// ...we could not find any helpful information on what `Name`
-    /// might be. This could occur, for example, if there is a where
-    /// clause `T : TraitRef` but not `T : TraitRef<Name=V>`. When
-    /// normalizing, this case is where we opt to normalize back to
-    /// the projection type `<T as TraitRef>::Name`.
-    NoCandidate,
-
+/// When attempting to resolve `<T as TraitRef>::Name` ...
+pub enum ProjectionTyError<'tcx> {
     /// ...we found multiple sources of information and couldn't resolve the ambiguity.
     TooManyCandidates,
 
-    /// ...`<T as TraitRef::Name>` ws resolved to some type `V` that failed to unify with `U`
-    MismatchedTypes(MismatchedProjectionTypes<'tcx>),
-
     /// ...an error occurred matching `T : TraitRef`
     TraitSelectionError(SelectionError<'tcx>),
 }
@@ -55,8 +48,6 @@ pub struct MismatchedProjectionTypes<'tcx> {
     pub err: ty::type_err<'tcx>
 }
 
-pub type ProjectionResult<'tcx, T> = Result<T, ProjectionError<'tcx>>;
-
 enum ProjectionTyCandidate<'tcx> {
     ParamEnv(ty::PolyProjectionPredicate<'tcx>),
     Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>),
@@ -70,24 +61,43 @@ struct ProjectionTyCandidateSet<'tcx> {
 pub fn poly_project_and_unify_type<'cx,'tcx>(
     selcx: &mut SelectionContext<'cx,'tcx>,
     obligation: &PolyProjectionObligation<'tcx>)
-    -> ProjectionResult<'tcx, ()>
+    -> Result<Option<Vec<PredicateObligation<'tcx>>>, MismatchedProjectionTypes<'tcx>>
 {
     debug!("poly_project(obligation={})",
            obligation.repr(selcx.tcx()));
 
     let infcx = selcx.infcx();
-
-    infcx.try(|snapshot| {
+    let result = infcx.try(|snapshot| {
         let (skol_predicate, skol_map) =
             infcx.skolemize_late_bound_regions(&obligation.predicate, snapshot);
 
         let skol_obligation = obligation.with(skol_predicate);
-        let () = try!(project_and_unify_type(selcx, &skol_obligation));
-        match infcx.leak_check(&skol_map, snapshot) {
-            Ok(()) => Ok(()),
-            Err(e) => Err(ProjectionError::MismatchedTypes(MismatchedProjectionTypes{err: e})),
+        match project_and_unify_type(selcx, &skol_obligation) {
+            Ok(Some(obligations)) => {
+                match infcx.leak_check(&skol_map, snapshot) {
+                    Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &obligations)),
+                    Err(e) => Err(Some(MismatchedProjectionTypes { err: e })),
+                }
+            }
+            Ok(None) => {
+                // Signal ambiguity using Err just so that infcx.try()
+                // rolls back the snapshot. We adapt below.
+                Err(None)
+            }
+            Err(e) => {
+                Err(Some(e))
+            }
         }
-    })
+    });
+
+    // Above, we use Err(None) to signal ambiguity so that the
+    // snapshot will be rolled back. But here, we want to translate to
+    // Ok(None). Kind of weird.
+    match result {
+        Ok(obligations) => Ok(Some(obligations)),
+        Err(None) => Ok(None),
+        Err(Some(e)) => Err(e),
+    }
 }
 
 /// Compute result of projecting an associated type and unify it with
@@ -95,48 +105,281 @@ pub fn poly_project_and_unify_type<'cx,'tcx>(
 pub fn project_and_unify_type<'cx,'tcx>(
     selcx: &mut SelectionContext<'cx,'tcx>,
     obligation: &ProjectionObligation<'tcx>)
-    -> ProjectionResult<'tcx, ()>
+    -> Result<Option<Vec<PredicateObligation<'tcx>>>, MismatchedProjectionTypes<'tcx>>
 {
     debug!("project_and_unify(obligation={})",
            obligation.repr(selcx.tcx()));
 
-    let ty_obligation = obligation.with(obligation.predicate.projection_ty.clone());
-    let projected_ty = try!(project_type(selcx, &ty_obligation));
+    let Normalized { value: normalized_ty, obligations } =
+        match opt_normalize_projection_type(selcx,
+                                            obligation.predicate.projection_ty.clone(),
+                                            obligation.cause.clone(),
+                                            obligation.recursion_depth) {
+            Some(n) => n,
+            None => { return Ok(None); }
+        };
+
+    debug!("project_and_unify_type: normalized_ty={} obligations={}",
+           normalized_ty.repr(selcx.tcx()),
+           obligations.repr(selcx.tcx()));
+
     let infcx = selcx.infcx();
     let origin = infer::RelateOutputImplTypes(obligation.cause.span);
-    debug!("project_and_unify_type: projected_ty = {}", projected_ty.repr(selcx.tcx()));
-    match infer::mk_eqty(infcx, true, origin, projected_ty, obligation.predicate.ty) {
-        Ok(()) => Ok(()),
-        Err(e) => Err(ProjectionError::MismatchedTypes(MismatchedProjectionTypes{err: e})),
+    match infer::mk_eqty(infcx, true, origin, normalized_ty, obligation.predicate.ty) {
+        Ok(()) => Ok(Some(obligations)),
+        Err(err) => Err(MismatchedProjectionTypes { err: err }),
+    }
+}
+
+pub fn normalize<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tcx>,
+                               cause: ObligationCause<'tcx>,
+                               value: &T)
+                               -> Normalized<'tcx, T>
+    where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone
+{
+    let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, 0);
+    let result = normalizer.fold(value);
+    Normalized {
+        value: result,
+        obligations: normalizer.obligations,
+    }
+}
+
+struct AssociatedTypeNormalizer<'a,'b:'a,'tcx:'b> {
+    selcx: &'a mut SelectionContext<'b,'tcx>,
+    cause: ObligationCause<'tcx>,
+    obligations: Vec<PredicateObligation<'tcx>>,
+    depth: uint,
+}
+
+impl<'a,'b,'tcx> AssociatedTypeNormalizer<'a,'b,'tcx> {
+    fn new(selcx: &'a mut SelectionContext<'b,'tcx>,
+           cause: ObligationCause<'tcx>,
+           depth: uint)
+           -> AssociatedTypeNormalizer<'a,'b,'tcx>
+    {
+        AssociatedTypeNormalizer {
+            selcx: selcx,
+            cause: cause,
+            obligations: vec!(),
+            depth: depth,
+        }
+    }
+
+    fn fold<T:TypeFoldable<'tcx> + HasProjectionTypes + Clone>(&mut self, value: &T) -> T {
+        let value = self.selcx.infcx().resolve_type_vars_if_possible(value);
+
+        if !value.has_projection_types() {
+            value.clone()
+        } else {
+            value.fold_with(self)
+        }
+    }
+}
+
+impl<'a,'b,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'b,'tcx> {
+    fn tcx(&self) -> &ty::ctxt<'tcx> {
+        self.selcx.tcx()
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        // We don't want to normalize associated types that occur inside of region
+        // binders, because they may contain bound regions, and we can't cope with that.
+        //
+        // Example:
+        //
+        //     for<'a> fn(<T as Foo<&'a>>::A)
+        //
+        // Instead of normalizing `<T as Foo<&'a>>::A` here, we'll
+        // normalize it when we instantiate those bound regions (which
+        // should occur eventually).
+
+        match ty.sty {
+            ty::ty_projection(ref data) if !data.has_escaping_regions() => { // (*)
+
+                // (*) This is kind of hacky -- we need to be able to
+                // handle normalization within binders because
+                // otherwise we wind up a need to normalize when doing
+                // trait matching (since you can have a trait
+                // obligation like `for<'a> T::B : Fn(&'a int)`), but
+                // we can't normalize with bound regions in scope. So
+                // far now we just ignore binders but only normalize
+                // if all bound regions are gone (and then we still
+                // have to renormalize whenever we instantiate a
+                // binder). It would be better to normalize in a
+                // binding-aware fashion.
+
+                let Normalized { value: ty, obligations } =
+                    normalize_projection_type(self.selcx,
+                                              data.clone(),
+                                              self.cause.clone(),
+                                              self.depth);
+                self.obligations.extend(obligations.into_iter());
+                ty
+            }
+            _ => {
+                ty_fold::super_fold_ty(self, ty)
+            }
+        }
+    }
+}
+
+pub struct Normalized<'tcx,T> {
+    pub value: T,
+    pub obligations: Vec<PredicateObligation<'tcx>>,
+}
+
+pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>;
+
+pub fn normalize_projection_type<'a,'b,'tcx>(
+    selcx: &'a mut SelectionContext<'b,'tcx>,
+    projection_ty: ty::ProjectionTy<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: uint)
+    -> NormalizedTy<'tcx>
+{
+    opt_normalize_projection_type(selcx, projection_ty.clone(), cause.clone(), depth)
+        .unwrap_or_else(move || {
+            // if we bottom out in ambiguity, create a type variable
+            // and a deferred predicate to resolve this when more type
+            // information is available.
+
+            let ty_var = selcx.infcx().next_ty_var();
+            let projection = ty::Binder(ty::ProjectionPredicate {
+                projection_ty: projection_ty,
+                ty: ty_var
+            });
+            let obligation = Obligation::with_depth(cause, depth+1, projection.as_predicate());
+            Normalized {
+                value: ty_var,
+                obligations: vec!(obligation)
+            }
+        })
+}
+
+fn opt_normalize_projection_type<'a,'b,'tcx>(
+    selcx: &'a mut SelectionContext<'b,'tcx>,
+    projection_ty: ty::ProjectionTy<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: uint)
+    -> Option<NormalizedTy<'tcx>>
+{
+    debug!("normalize_projection_type(\
+           projection_ty={}, \
+           depth={})",
+           projection_ty.repr(selcx.tcx()),
+           depth);
+
+    let obligation = Obligation::with_depth(cause.clone(), depth, projection_ty.clone());
+    match project_type(selcx, &obligation) {
+        Ok(ProjectedTy::Progress(projected_ty, mut obligations)) => {
+            // if projection succeeded, then what we get out of this
+            // is also non-normalized (consider: it was derived from
+            // an impl, where-clause etc) and hence we must
+            // re-normalize it
+
+            debug!("normalize_projection_type: projected_ty={} depth={}",
+                   projected_ty.repr(selcx.tcx()),
+                   depth);
+
+            if ty::type_has_projection(projected_ty) {
+                let tcx = selcx.tcx();
+                let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, depth);
+                let normalized_ty = normalizer.fold(&projected_ty);
+
+                debug!("normalize_projection_type: normalized_ty={} depth={}",
+                       normalized_ty.repr(tcx),
+                       depth);
+
+                obligations.extend(normalizer.obligations.into_iter());
+                Some(Normalized {
+                    value: normalized_ty,
+                    obligations: obligations,
+                })
+            } else {
+                Some(Normalized {
+                    value: projected_ty,
+                    obligations: obligations,
+                })
+            }
+        }
+        Ok(ProjectedTy::NoProgress(projected_ty)) => {
+            Some(Normalized {
+                value: projected_ty,
+                obligations: vec!()
+            })
+        }
+        Err(ProjectionTyError::TooManyCandidates) => {
+            None
+        }
+        Err(ProjectionTyError::TraitSelectionError(_)) => {
+            // if we got an error processing the `T as Trait` part,
+            // just return `ty::err` but add the obligation `T :
+            // Trait`, which when processed will cause the error to be
+            // reported later
+
+            Some(normalize_to_error(selcx, projection_ty, cause, depth))
+        }
+    }
+}
+
+/// in various error cases, we just set ty_err and return an obligation
+/// that, when fulfiled, will lead to an error
+fn normalize_to_error<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
+                               projection_ty: ty::ProjectionTy<'tcx>,
+                               cause: ObligationCause<'tcx>,
+                               depth: uint)
+                               -> NormalizedTy<'tcx>
+{
+    let trait_ref = projection_ty.trait_ref.to_poly_trait_ref();
+    let trait_obligation = Obligation { cause: cause,
+                                        recursion_depth: depth,
+                                        predicate: trait_ref.as_predicate() };
+    Normalized {
+        value: selcx.tcx().types.err,
+        obligations: vec!(trait_obligation)
     }
 }
 
+enum ProjectedTy<'tcx> {
+    Progress(Ty<'tcx>, Vec<PredicateObligation<'tcx>>),
+    NoProgress(Ty<'tcx>),
+}
+
 /// Compute the result of a projection type (if we can).
-pub fn project_type<'cx,'tcx>(
+fn project_type<'cx,'tcx>(
     selcx: &mut SelectionContext<'cx,'tcx>,
     obligation: &ProjectionTyObligation<'tcx>)
-    -> ProjectionResult<'tcx, Ty<'tcx>>
+    -> Result<ProjectedTy<'tcx>, ProjectionTyError<'tcx>>
 {
     debug!("project(obligation={})",
            obligation.repr(selcx.tcx()));
 
+    let recursion_limit = selcx.tcx().sess.recursion_limit.get();
+    if obligation.recursion_depth >= recursion_limit {
+        debug!("project: overflow!");
+        return Err(ProjectionTyError::TraitSelectionError(Overflow));
+    }
+
     let mut candidates = ProjectionTyCandidateSet {
         vec: Vec::new(),
         ambiguous: false,
     };
 
-    let () = assemble_candidates_from_object_type(selcx,
-                                                  obligation,
-                                                  &mut candidates);
+    assemble_candidates_from_object_type(selcx,
+                                         obligation,
+                                         &mut candidates);
 
     if candidates.vec.is_empty() {
-        let () = assemble_candidates_from_param_env(selcx,
-                                                    obligation,
-                                                    &mut candidates);
-
-        let () = try!(assemble_candidates_from_impls(selcx,
-                                                     obligation,
-                                                     &mut candidates));
+        assemble_candidates_from_param_env(selcx,
+                                           obligation,
+                                           &mut candidates);
+
+        if let Err(e) = assemble_candidates_from_impls(selcx,
+                                                       obligation,
+                                                       &mut candidates) {
+            return Err(ProjectionTyError::TraitSelectionError(e));
+        }
     }
 
     debug!("{} candidates, ambiguous={}",
@@ -146,15 +389,18 @@ pub fn project_type<'cx,'tcx>(
     // We probably need some winnowing logic similar to select here.
 
     if candidates.ambiguous || candidates.vec.len() > 1 {
-        return Err(ProjectionError::TooManyCandidates);
+        return Err(ProjectionTyError::TooManyCandidates);
     }
 
     match candidates.vec.pop() {
         Some(candidate) => {
-            Ok(try!(confirm_candidate(selcx, obligation, candidate)))
+            let (ty, obligations) = confirm_candidate(selcx, obligation, candidate);
+            Ok(ProjectedTy::Progress(ty, obligations))
         }
         None => {
-            Err(ProjectionError::NoCandidate)
+            Ok(ProjectedTy::NoProgress(ty::mk_projection(selcx.tcx(),
+                                                         obligation.predicate.trait_ref.clone(),
+                                                         obligation.predicate.item_name)))
         }
     }
 }
@@ -230,9 +476,9 @@ fn assemble_candidates_from_object_type<'cx,'tcx>(
 
 fn assemble_candidates_from_impls<'cx,'tcx>(
     selcx: &mut SelectionContext<'cx,'tcx>,
-    obligation:  &ProjectionTyObligation<'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
     candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
-    -> ProjectionResult<'tcx, ()>
+    -> Result<(), SelectionError<'tcx>>
 {
     // If we are resolving `<T as TraitRef<...>>::Item == Type`,
     // start out by selecting the predicate `T as TraitRef<...>`:
@@ -249,7 +495,7 @@ fn assemble_candidates_from_impls<'cx,'tcx>(
         Err(e) => {
             debug!("assemble_candidates_from_impls: selection error {}",
                    e.repr(selcx.tcx()));
-            return Err(ProjectionError::TraitSelectionError(e));
+            return Err(e);
         }
     };
 
@@ -301,9 +547,9 @@ fn assemble_candidates_from_impls<'cx,'tcx>(
 
 fn confirm_candidate<'cx,'tcx>(
     selcx: &mut SelectionContext<'cx,'tcx>,
-    obligation:  &ProjectionTyObligation<'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
     candidate: ProjectionTyCandidate<'tcx>)
-    -> ProjectionResult<'tcx, Ty<'tcx>>
+    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
 {
     let infcx = selcx.infcx();
 
@@ -311,7 +557,7 @@ fn confirm_candidate<'cx,'tcx>(
            candidate.repr(infcx.tcx),
            obligation.repr(infcx.tcx));
 
-    let projected_ty = match candidate {
+    match candidate {
         ProjectionTyCandidate::ParamEnv(poly_projection) => {
             let projection =
                 infcx.replace_late_bound_regions_with_fresh_var(
@@ -338,7 +584,7 @@ fn confirm_candidate<'cx,'tcx>(
                 }
             }
 
-            projection.ty
+            (projection.ty, vec!())
         }
 
         ProjectionTyCandidate::Impl(impl_vtable) => {
@@ -363,10 +609,8 @@ fn confirm_candidate<'cx,'tcx>(
                 break;
             }
 
-            // TODO we need the impl_vtable items here
-
             match impl_ty {
-                Some(ty) => ty,
+                Some(ty) => (ty, impl_vtable.nested.to_vec()),
                 None => {
                     selcx.tcx().sess.span_bug(
                         obligation.cause.span,
@@ -376,21 +620,15 @@ fn confirm_candidate<'cx,'tcx>(
                 }
             }
         }
-    };
-
-    Ok(projected_ty)
+    }
 }
 
-impl<'tcx> Repr<'tcx> for ProjectionError<'tcx> {
+impl<'tcx> Repr<'tcx> for ProjectionTyError<'tcx> {
     fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
         match *self {
-            ProjectionError::NoCandidate =>
+            ProjectionTyError::TooManyCandidates =>
                 format!("NoCandidate"),
-            ProjectionError::TooManyCandidates =>
-                format!("NoCandidate"),
-            ProjectionError::MismatchedTypes(ref m) =>
-                format!("MismatchedTypes({})", m.repr(tcx)),
-            ProjectionError::TraitSelectionError(ref e) =>
+            ProjectionTyError::TraitSelectionError(ref e) =>
                 format!("TraitSelectionError({})", e.repr(tcx)),
         }
     }
@@ -406,4 +644,3 @@ impl<'tcx> Repr<'tcx> for ProjectionTyCandidate<'tcx> {
         }
     }
 }
-
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index 06819aac5bc..1b00a77fe51 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -960,7 +960,7 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     // iterating early.
     let mut fulfill_cx = traits::FulfillmentContext::new();
     let vtable = selection.map_move_nested(|predicate| {
-        fulfill_cx.register_predicate(&infcx, predicate);
+        fulfill_cx.register_predicate_obligation(&infcx, predicate);
     });
     match fulfill_cx.select_all_or_error(&infcx, &param_env, tcx) {
         Ok(()) => { }
diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs
index 3b7043e4f40..c693c6ea428 100644
--- a/src/librustc_trans/trans/monomorphize.rs
+++ b/src/librustc_trans/trans/monomorphize.rs
@@ -16,7 +16,7 @@ use middle::infer;
 use middle::subst;
 use middle::subst::{Subst, Substs};
 use middle::traits;
-use middle::ty_fold::{mod, TypeFolder, TypeFoldable};
+use middle::ty_fold::{TypeFolder, TypeFoldable};
 use trans::base::{set_llvm_fn_attrs, set_inline_hint};
 use trans::base::{trans_enum_variant, push_ctxt, get_item_val};
 use trans::base::{trans_fn, decl_internal_rust_fn};
@@ -32,7 +32,6 @@ use syntax::ast_map;
 use syntax::ast_util::{local_def, PostExpansionMethod};
 use syntax::attr;
 use std::hash::{sip, Hash};
-use std::rc::Rc;
 
 pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                 fn_id: ast::DefId,
@@ -310,13 +309,13 @@ pub fn apply_param_substs<'tcx,T>(tcx: &ty::ctxt<'tcx>,
 /// monomorphization, we know that only concrete types are involved,
 /// and hence we can be sure that all associated types will be
 /// completely normalized away.
-pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, t: &T) -> T
+pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T
     where T : TypeFoldable<'tcx> + Repr<'tcx> + HasProjectionTypes + Clone
 {
-    debug!("normalize_associated_type(t={})", t.repr(tcx));
+    debug!("normalize_associated_type(t={})", value.repr(tcx));
 
-    if !t.has_projection_types() {
-        return t.clone();
+    if !value.has_projection_types() {
+        return value.clone();
     }
 
     // FIXME(#20304) -- cache
@@ -324,52 +323,15 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, t: &T) -> T
     let infcx = infer::new_infer_ctxt(tcx);
     let param_env = ty::empty_parameter_environment();
     let mut selcx = traits::SelectionContext::new(&infcx, &param_env, tcx);
-    let mut normalizer = AssociatedTypeNormalizer { selcx: &mut selcx };
-    let result = t.fold_with(&mut normalizer);
+    let cause = traits::ObligationCause::dummy();
+    let traits::Normalized { value: result, obligations } =
+        traits::normalize(&mut selcx, cause, value);
 
-    debug!("normalize_associated_type: t={} result={}",
-           t.repr(tcx),
-           result.repr(tcx));
+    debug!("normalize_associated_type: result={} obligations={}",
+           result.repr(tcx),
+           obligations.repr(tcx));
 
-    result
-}
-
-struct AssociatedTypeNormalizer<'a,'tcx:'a> {
-    selcx: &'a mut traits::SelectionContext<'a,'tcx>,
-}
-
-impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> {
-    fn tcx(&self) -> &ty::ctxt<'tcx> { self.selcx.tcx() }
-
-    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        match ty.sty {
-            ty::ty_projection(ref data) => {
-                debug!("ty_projection({})", data.repr(self.tcx()));
-
-                let tcx = self.selcx.tcx();
-                let substs = data.trait_ref.substs.clone().erase_regions();
-                let substs = self.tcx().mk_substs(substs);
-                assert!(substs.types.iter().all(|&t| (!ty::type_has_params(t) &&
-                                                      !ty::type_has_self(t))));
-                let trait_ref = Rc::new(ty::TraitRef::new(data.trait_ref.def_id, substs));
-                let projection_ty = ty::ProjectionTy { trait_ref: trait_ref.clone(),
-                                                       item_name: data.item_name };
-                let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
-                                                         projection_ty);
-                match traits::project_type(self.selcx, &obligation) {
-                    Ok(ty) => ty,
-                    Err(errors) => {
-                        tcx.sess.bug(
-                            format!("Encountered error(s) `{}` selecting `{}` during trans",
-                                    errors.repr(tcx),
-                                    trait_ref.repr(tcx)).as_slice());
-                    }
-                }
-            }
+    assert_eq!(obligations.len(), 0); // TODO not good enough
 
-            _ => {
-                ty_fold::super_fold_ty(self, ty)
-            }
-        }
-    }
+    result
 }
diff --git a/src/librustc_typeck/check/assoc.rs b/src/librustc_typeck/check/assoc.rs
index 081959a4efa..98081e28f2f 100644
--- a/src/librustc_typeck/check/assoc.rs
+++ b/src/librustc_typeck/check/assoc.rs
@@ -9,85 +9,34 @@
 // except according to those terms.
 
 use middle::infer::InferCtxt;
-use middle::traits::{ObligationCause, ObligationCauseCode, FulfillmentContext};
-use middle::ty::{mod, RegionEscape, HasProjectionTypes, Ty};
-use middle::ty_fold::{mod, TypeFoldable, TypeFolder};
+use middle::mem_categorization as mc;
+use middle::traits::{mod, FulfillmentContext, Normalized, MiscObligation,
+                     SelectionContext, ObligationCause};
+use middle::ty::{mod, HasProjectionTypes};
+use middle::ty_fold::{TypeFoldable, TypeFolder};
 use syntax::ast;
 use syntax::codemap::Span;
+use util::ppaux::Repr;
 
 pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
+                                                param_env: &ty::ParameterEnvironment<'tcx>,
+                                                typer: &(mc::Typer<'tcx>+'a),
                                                 fulfillment_cx: &mut FulfillmentContext<'tcx>,
                                                 span: Span,
                                                 body_id: ast::NodeId,
                                                 value: &T)
                                                 -> T
-    where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone
+    where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone + Repr<'tcx>
 {
-    let value = infcx.resolve_type_vars_if_possible(value);
-
-    if !value.has_projection_types() {
-        return value.clone();
-    }
-
-    let mut normalizer = AssociatedTypeNormalizer { span: span,
-                                                    body_id: body_id,
-                                                    infcx: infcx,
-                                                    fulfillment_cx: fulfillment_cx };
-    value.fold_with(&mut normalizer)
-}
-
-struct AssociatedTypeNormalizer<'a,'tcx:'a> {
-    infcx: &'a InferCtxt<'a, 'tcx>,
-    fulfillment_cx: &'a mut FulfillmentContext<'tcx>,
-    span: Span,
-    body_id: ast::NodeId,
-}
-
-impl<'a,'tcx> TypeFolder<'tcx> for AssociatedTypeNormalizer<'a,'tcx> {
-    fn tcx(&self) -> &ty::ctxt<'tcx> {
-        self.infcx.tcx
-    }
-
-    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        // We don't want to normalize associated types that occur inside of region
-        // binders, because they may contain bound regions, and we can't cope with that.
-        //
-        // Example:
-        //
-        //     for<'a> fn(<T as Foo<&'a>>::A)
-        //
-        // Instead of normalizing `<T as Foo<&'a>>::A` here, we'll
-        // normalize it when we instantiate those bound regions (which
-        // should occur eventually).
-
-        match ty.sty {
-            ty::ty_projection(ref data) if !data.has_escaping_regions() => { // (*)
-
-                // (*) This is kind of hacky -- we need to be able to
-                // handle normalization within binders because
-                // otherwise we wind up a need to normalize when doing
-                // trait matching (since you can have a trait
-                // obligation like `for<'a> T::B : Fn(&'a int)`), but
-                // we can't normalize with bound regions in scope. So
-                // far now we just ignore binders but only normalize
-                // if all bound regions are gone (and then we still
-                // have to renormalize whenever we instantiate a
-                // binder). It would be better to normalize in a
-                // binding-aware fashion.
-
-                let cause =
-                    ObligationCause::new(
-                        self.span,
-                        self.body_id,
-                        ObligationCauseCode::MiscObligation);
-                self.fulfillment_cx
-                    .normalize_projection_type(self.infcx,
-                                               data.clone(),
-                                               cause)
-            }
-            _ => {
-                ty_fold::super_fold_ty(self, ty)
-            }
-        }
+    debug!("normalize_associated_types_in(value={})", value.repr(infcx.tcx));
+    let mut selcx = SelectionContext::new(infcx, param_env, typer);
+    let cause = ObligationCause::new(span, body_id, MiscObligation);
+    let Normalized { value: result, obligations } = traits::normalize(&mut selcx, cause, value);
+    debug!("normalize_associated_types_in: result={} predicates={}",
+           result.repr(infcx.tcx),
+           obligations.repr(infcx.tcx));
+    for obligation in obligations.into_iter() {
+        fulfillment_cx.register_predicate_obligation(infcx, obligation);
     }
+    result
 }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index e2691a778ac..12cebabc997 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -350,11 +350,18 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
         }
     }
 
-    fn normalize_associated_types_in<T>(&self, span: Span, body_id: ast::NodeId, value: &T) -> T
-        where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes
+    fn normalize_associated_types_in<T>(&self,
+                                        typer: &mc::Typer<'tcx>,
+                                        span: Span,
+                                        body_id: ast::NodeId,
+                                        value: &T)
+                                        -> T
+        where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + Repr<'tcx>
     {
         let mut fulfillment_cx = self.fulfillment_cx.borrow_mut();
         assoc::normalize_associated_types_in(&self.infcx,
+                                             &self.param_env,
+                                             typer,
                                              &mut *fulfillment_cx, span,
                                              body_id,
                                              value)
@@ -438,7 +445,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
             let fn_sig =
                 liberate_late_bound_regions(ccx.tcx, CodeExtent::from_node_id(body.id), &fn_sig);
             let fn_sig =
-                inh.normalize_associated_types_in(body.span, body.id, &fn_sig);
+                inh.normalize_associated_types_in(ccx.tcx, body.span, body.id, &fn_sig);
 
             let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_sig,
                                decl, id, body, &inh);
@@ -1190,6 +1197,8 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
             impl_sig.subst(tcx, impl_to_skol_substs);
         let impl_sig =
             assoc::normalize_associated_types_in(&infcx,
+                                                 &impl_param_env,
+                                                 infcx.tcx,
                                                  &mut fulfillment_cx,
                                                  impl_m_span,
                                                  impl_m_body_id,
@@ -1209,6 +1218,8 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
             trait_sig.subst(tcx, &trait_to_skol_substs);
         let trait_sig =
             assoc::normalize_associated_types_in(&infcx,
+                                                 &impl_param_env,
+                                                 infcx.tcx,
                                                  &mut fulfillment_cx,
                                                  impl_m_span,
                                                  impl_m_body_id,
@@ -1756,9 +1767,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
 
     fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
-        where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes
+        where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + Repr<'tcx>
     {
-        self.inh.normalize_associated_types_in(span, self.body_id, value)
+        self.inh.normalize_associated_types_in(self, span, self.body_id, value)
     }
 
     fn normalize_associated_type(&self,
@@ -1773,6 +1784,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.inh.fulfillment_cx
             .borrow_mut()
             .normalize_projection_type(self.infcx(),
+                                       &self.inh.param_env,
+                                       self,
                                        ty::ProjectionTy {
                                            trait_ref: trait_ref,
                                            item_name: item_name,
@@ -1943,7 +1956,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         self.inh.fulfillment_cx
             .borrow_mut()
-            .register_predicate(self.infcx(), obligation);
+            .register_predicate_obligation(self.infcx(), obligation);
     }
 
     pub fn to_ty(&self, ast_t: &ast::Ty) -> Ty<'tcx> {
diff --git a/src/test/compile-fail/associated-types-path-2.rs b/src/test/compile-fail/associated-types-path-2.rs
index 486d3d38c60..989214d81da 100644
--- a/src/test/compile-fail/associated-types-path-2.rs
+++ b/src/test/compile-fail/associated-types-path-2.rs
@@ -25,11 +25,30 @@ pub fn f2<T: Foo>(a: T) -> T::A {
     panic!();
 }
 
-pub fn main() {
-    f1(2i, 4i); //~ ERROR expected uint, found int
+pub fn f1_int_int() {
+    f1(2i, 4i);
+    //~^ ERROR expected uint, found int
+}
+
+pub fn f1_int_uint() {
     f1(2i, 4u);
-    f1(2u, 4u); //~ ERROR the trait `Foo` is not implemented
-    f1(2u, 4i); //~ ERROR the trait `Foo` is not implemented
+}
+
+pub fn f1_uint_uint() {
+    f1(2u, 4u);
+    //~^ ERROR the trait `Foo` is not implemented
+    //~| ERROR the trait `Foo` is not implemented
+}
+
+pub fn f1_uint_int() {
+    f1(2u, 4i);
+    //~^ ERROR the trait `Foo` is not implemented
+    //~| ERROR the trait `Foo` is not implemented
+}
 
-    let _: int = f2(2i); //~ERROR expected `int`, found `uint`
+pub fn f2_int() {
+    let _: int = f2(2i);
+    //~^ ERROR expected `int`, found `uint`
 }
+
+pub fn main() { }
diff --git a/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs b/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs
index 8cdca50d9b6..0e13efdebc9 100644
--- a/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs
+++ b/src/test/compile-fail/associated-types-project-from-hrtb-in-fn-body.rs
@@ -29,9 +29,10 @@ fn bar<'a, 'b, I : for<'x> Foo<&'x int>>(
     x: <I as Foo<&'a int>>::A,
     y: <I as Foo<&'b int>>::A,
     cond: bool)
-{ //~ ERROR cannot infer
+{
     // x and y here have two distinct lifetimes:
     let z: I::A = if cond { x } else { y };
+    //~^ ERROR cannot infer
 }
 
 pub fn main() {}
diff --git a/src/test/compile-fail/associated-types-unconstrained.rs b/src/test/compile-fail/associated-types-unconstrained.rs
new file mode 100644
index 00000000000..02e11218806
--- /dev/null
+++ b/src/test/compile-fail/associated-types-unconstrained.rs
@@ -0,0 +1,28 @@
+// 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.
+
+// Check that an associated type cannot be bound in an expression path.
+
+#![feature(associated_types)]
+
+trait Foo {
+    type A;
+    fn bar() -> int;
+}
+
+impl Foo for int {
+    type A = uint;
+    fn bar() -> int { 42 }
+}
+
+pub fn main() {
+    let x: int = Foo::bar();
+    //~^ ERROR type annotations required
+}
diff --git a/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs b/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs
index 9ceae41d1a4..3c461fd5b4b 100644
--- a/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs
+++ b/src/test/compile-fail/traits-multidispatch-convert-ambig-dest.rs
@@ -33,7 +33,7 @@ where T : Convert<U>
 }
 
 fn a() {
-    test(22_i32, 44); //~ ERROR unable to infer
+    test(22_i32, 44); //~ ERROR type annotations required
 }
 
 fn main() {}
diff --git a/src/test/run-pass/associated-types-impl-redirect.rs b/src/test/run-pass/associated-types-impl-redirect.rs
new file mode 100644
index 00000000000..a28cf346336
--- /dev/null
+++ b/src/test/run-pass/associated-types-impl-redirect.rs
@@ -0,0 +1,58 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test how resolving a projection interacts with inference.  In this
+// case, we were eagerly unifying the type variable for the iterator
+// type with `I` from the where clause, ignoring the in-scope `impl`
+// for `ByRef`. The right answer was to consider the result ambiguous
+// until more type information was available.
+
+// ignore-pretty -- FIXME(#17362)
+
+#![feature(associated_types, lang_items, unboxed_closures)]
+#![no_implicit_prelude]
+
+use std::option::Option::{None, Some, mod};
+
+trait Iterator {
+    type Item;
+
+    fn next(&mut self) -> Option<Self::Item>;
+}
+
+trait IteratorExt: Iterator {
+    fn by_ref(&mut self) -> ByRef<Self> {
+        ByRef(self)
+    }
+}
+
+impl<I> IteratorExt for I where I: Iterator {}
+
+struct ByRef<'a, I: 'a + Iterator>(&'a mut I);
+
+impl<'a, I: Iterator> Iterator for ByRef<'a, I> {
+    type Item = I::Item;
+
+    fn next(&mut self) -> Option< <I as Iterator>::Item > {
+        self.0.next()
+    }
+}
+
+fn is_iterator_of<A, I: Iterator<Item=A>>(_: &I) {}
+
+fn test<A, I: Iterator<Item=A>>(mut it: I) {
+    is_iterator_of::<A, _>(&it.by_ref());
+}
+
+fn test2<A, I1: Iterator<Item=A>, I2: Iterator<Item=I1::Item>>(mut it: I2) {
+    is_iterator_of::<A, _>(&it)
+}
+
+fn main() { }