about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-05-17 18:10:53 -0700
committerbors <bors@rust-lang.org>2016-05-17 18:10:53 -0700
commit75e23e1b032d87300392a4f3835bde8d5d873823 (patch)
tree82f44282079fd2ff88f8cb45e5877441546867a7
parent0667ae93fb72eb25594258e55de9b4ae8f9f02a8 (diff)
parent639890d92da1bbe62dc669ff1a122a23da7bc0cd (diff)
downloadrust-75e23e1b032d87300392a4f3835bde8d5d873823.tar.gz
rust-75e23e1b032d87300392a4f3835bde8d5d873823.zip
Auto merge of #33137 - nikomatsakis:issue-32330-lbr-in-return-type-warning-2, r=aturon
Warnings for issue #32330

This is an extension of the previous PR that issues warnings in more situations than before. It does not handle *all* cases of #32330 but I believe it issues warnings for all cases I've seen in practice.

Before merging I'd like to address:

- open a good issue explaining the problem and how to fix it (I have a [draft writeup][])
- work on the error message, which I think is not as clear as it could/should be (suggestions welcome)

r? @aturon

[draft writeup]: https://gist.github.com/nikomatsakis/631ec8b4af9a18b5d062d9d9b7d3d967
-rw-r--r--src/librustc/lint/builtin.rs10
-rw-r--r--src/librustc/traits/project.rs469
-rw-r--r--src/librustc/ty/fold.rs80
-rw-r--r--src/librustc_lint/lib.rs6
-rw-r--r--src/librustc_typeck/astconv.rs95
-rw-r--r--src/librustc_typeck/check/mod.rs1
-rw-r--r--src/librustc_typeck/collect.rs3
-rw-r--r--src/libsyntax/errors/mod.rs60
-rw-r--r--src/test/compile-fail/associated-types-eq-hr.rs49
-rw-r--r--src/test/compile-fail/associated-types/bound-lifetime-constrained.rs66
-rw-r--r--src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs90
-rw-r--r--src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs64
12 files changed, 760 insertions, 233 deletions
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 6dd98425df3..d7971cd2cf0 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -168,6 +168,13 @@ declare_lint! {
 }
 
 declare_lint! {
+    pub HR_LIFETIME_IN_ASSOC_TYPE,
+    Warn,
+    "binding for associated type references higher-ranked lifetime \
+     that does not appear in the trait input types"
+}
+
+declare_lint! {
     pub OVERLAPPING_INHERENT_IMPLS,
     Warn,
     "two overlapping inherent impls define an item with the same name were erroneously allowed"
@@ -234,7 +241,8 @@ impl LintPass for HardwiredLints {
             RENAMED_AND_REMOVED_LINTS,
             SUPER_OR_SELF_IN_GLOBAL_PATH,
             UNSIZED_IN_TUPLE,
-            OBJECT_UNSAFE_FRAGMENT
+            OBJECT_UNSAFE_FRAGMENT,
+            HR_LIFETIME_IN_ASSOC_TYPE
         )
     }
 }
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 9abb179f288..5c7095beb79 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -152,14 +152,8 @@ enum ProjectionTyCandidate<'tcx> {
     // from the definition of `Trait` when you have something like <<A as Trait>::B as Trait2>::C
     TraitDef(ty::PolyProjectionPredicate<'tcx>),
 
-    // defined in an impl
-    Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>),
-
-    // closure return type
-    Closure(VtableClosureData<'tcx, PredicateObligation<'tcx>>),
-
-    // fn pointer return type
-    FnPointer(VtableFnPointerData<'tcx, PredicateObligation<'tcx>>),
+    // from a "impl" (or a "pseudo-impl" returned by select)
+    Select,
 }
 
 struct ProjectionTyCandidateSet<'tcx> {
@@ -599,10 +593,8 @@ fn project_type<'cx, 'gcx, 'tcx>(
         debug!("retaining param-env candidates only from {:?}", candidates.vec);
         candidates.vec.retain(|c| match *c {
             ProjectionTyCandidate::ParamEnv(..) => true,
-            ProjectionTyCandidate::Impl(..) |
-            ProjectionTyCandidate::Closure(..) |
             ProjectionTyCandidate::TraitDef(..) |
-            ProjectionTyCandidate::FnPointer(..) => false,
+            ProjectionTyCandidate::Select => false,
         });
         debug!("resulting candidate set: {:?}", candidates.vec);
         if candidates.vec.len() != 1 {
@@ -612,78 +604,12 @@ fn project_type<'cx, 'gcx, 'tcx>(
 
     assert!(candidates.vec.len() <= 1);
 
-    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.
-                    bug!("Tried to project an inherited associated type during \
-                          coherence checking, which is currently not supported.");
-                }
-            }
-        }
-        Some(candidate)
-    });
-
-    match possible_candidate {
+    match candidates.vec.pop() {
         Some(candidate) => {
-            let (ty, obligations) = confirm_candidate(selcx, obligation, candidate);
+            let (ty, obligations) = confirm_candidate(selcx,
+                                                      obligation,
+                                                      &obligation_trait_ref,
+                                                      candidate);
             Ok(ProjectedTy::Progress(ty, obligations))
         }
         None => {
@@ -802,38 +728,6 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
     }
 }
 
-fn assemble_candidates_from_object_type<'cx, 'gcx, 'tcx>(
-    selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
-    obligation:  &ProjectionTyObligation<'tcx>,
-    obligation_trait_ref: &ty::TraitRef<'tcx>,
-    candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
-{
-    let self_ty = obligation_trait_ref.self_ty();
-    let object_ty = selcx.infcx().shallow_resolve(self_ty);
-    debug!("assemble_candidates_from_object_type(object_ty={:?})",
-           object_ty);
-    let data = match object_ty.sty {
-        ty::TyTrait(ref data) => data,
-        _ => {
-            span_bug!(
-                obligation.cause.span,
-                "assemble_candidates_from_object_type called with non-object: {:?}",
-                object_ty);
-        }
-    };
-    let projection_bounds = data.projection_bounds_with_self_ty(selcx.tcx(), object_ty);
-    let env_predicates = projection_bounds.iter()
-                                          .map(|p| p.to_predicate())
-                                          .collect();
-    let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates);
-    assemble_candidates_from_predicates(selcx,
-                                        obligation,
-                                        obligation_trait_ref,
-                                        candidate_set,
-                                        ProjectionTyCandidate::ParamEnv,
-                                        env_predicates)
-}
-
 fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
@@ -845,82 +739,183 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
     // start out by selecting the predicate `T as TraitRef<...>`:
     let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
     let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate());
-    let vtable = match selcx.select(&trait_obligation) {
-        Ok(Some(vtable)) => vtable,
-        Ok(None) => {
-            candidate_set.ambiguous = true;
-            return Ok(());
-        }
-        Err(e) => {
-            debug!("assemble_candidates_from_impls: selection error {:?}",
-                   e);
-            return Err(e);
-        }
-    };
+    selcx.infcx().probe(|_| {
+        let vtable = match selcx.select(&trait_obligation) {
+            Ok(Some(vtable)) => vtable,
+            Ok(None) => {
+                candidate_set.ambiguous = true;
+                return Ok(());
+            }
+            Err(e) => {
+                debug!("assemble_candidates_from_impls: selection error {:?}",
+                       e);
+                return Err(e);
+            }
+        };
 
-    match vtable {
-        super::VtableImpl(data) => {
-            debug!("assemble_candidates_from_impls: impl candidate {:?}",
-                   data);
+        match vtable {
+            super::VtableClosure(_) |
+            super::VtableFnPointer(_) |
+            super::VtableObject(_) => {
+                debug!("assemble_candidates_from_impls: vtable={:?}",
+                       vtable);
 
-            candidate_set.vec.push(
-                ProjectionTyCandidate::Impl(data));
-        }
-        super::VtableObject(_) => {
-            assemble_candidates_from_object_type(
-                selcx, obligation, obligation_trait_ref, candidate_set);
-        }
-        super::VtableClosure(data) => {
-            candidate_set.vec.push(
-                ProjectionTyCandidate::Closure(data));
-        }
-        super::VtableFnPointer(data) => {
-            candidate_set.vec.push(
-                ProjectionTyCandidate::FnPointer(data));
-        }
-        super::VtableParam(..) => {
-            // This case tell us nothing about the value of an
-            // associated type. Consider:
-            //
-            // ```
-            // trait SomeTrait { type Foo; }
-            // fn foo<T:SomeTrait>(...) { }
-            // ```
-            //
-            // If the user writes `<T as SomeTrait>::Foo`, then the `T
-            // : SomeTrait` binding does not help us decide what the
-            // type `Foo` is (at least, not more specifically than
-            // what we already knew).
-            //
-            // But wait, you say! What about an example like this:
-            //
-            // ```
-            // fn bar<T:SomeTrait<Foo=usize>>(...) { ... }
-            // ```
-            //
-            // Doesn't the `T : Sometrait<Foo=usize>` predicate help
-            // resolve `T::Foo`? And of course it does, but in fact
-            // that single predicate is desugared into two predicates
-            // in the compiler: a trait predicate (`T : SomeTrait`) and a
-            // projection. And the projection where clause is handled
-            // in `assemble_candidates_from_param_env`.
-        }
-        super::VtableDefaultImpl(..) |
-        super::VtableBuiltin(..) => {
-            // These traits have no associated types.
-            span_bug!(
-                obligation.cause.span,
-                "Cannot project an associated type from `{:?}`",
-                vtable);
+                candidate_set.vec.push(ProjectionTyCandidate::Select);
+            }
+            super::VtableImpl(ref impl_data) if !selcx.projection_mode().is_any() => {
+                // We have to be careful when projecting out of an
+                // impl because of specialization. If we are not in
+                // trans (i.e., projection mode is not "any"), and the
+                // impl's type is declared as default, then we disable
+                // projection (even if the trait ref is fully
+                // monomorphic). In the case where trait ref is not
+                // fully monomorphic (i.e., includes type parameters),
+                // this is because those type parameters may
+                // ultimately be bound to types from other crates that
+                // may have specialized impls we can't see. In the
+                // case where the trait ref IS fully monomorphic, this
+                // is a policy decision that we made in the RFC in
+                // order to preserve flexibility for the crate that
+                // defined the specializable impl to specialize later
+                // for existing types.
+                //
+                // In either case, we handle this by not adding a
+                // candidate for an impl if it contains a `default`
+                // type.
+                let opt_node_item = assoc_ty_def(selcx,
+                                                 impl_data.impl_def_id,
+                                                 obligation.predicate.item_name);
+                let new_candidate = if let Some(node_item) = opt_node_item {
+                    if node_item.node.is_from_trait() {
+                        if node_item.item.ty.is_some() {
+                            // The impl inherited a `type Foo =
+                            // Bar` given in the trait, which is
+                            // implicitly default. No candidate.
+                            None
+                        } else {
+                            // The impl did not specify `type` and neither
+                            // did the trait:
+                            //
+                            // ```rust
+                            // trait Foo { type T; }
+                            // impl Foo for Bar { }
+                            // ```
+                            //
+                            // This is an error, but it will be
+                            // reported in `check_impl_items_against_trait`.
+                            // We accept it here but will flag it as
+                            // an error when we confirm the candidate
+                            // (which will ultimately lead to `normalize_to_error`
+                            // being invoked).
+                            Some(ProjectionTyCandidate::Select)
+                        }
+                    } else if node_item.item.defaultness.is_default() {
+                        // The impl specified `default type Foo =
+                        // Bar`. No candidate.
+                        None
+                    } else {
+                        // The impl specified `type Foo = Bar`
+                        // with no default. Add a candidate.
+                        Some(ProjectionTyCandidate::Select)
+                    }
+                } else {
+                    // This is saying that neither the trait nor
+                    // the impl contain a definition for this
+                    // associated type.  Normally this situation
+                    // could only arise through a compiler bug --
+                    // if the user wrote a bad item name, it
+                    // should have failed in astconv. **However**,
+                    // at coherence-checking time, we only look at
+                    // the topmost impl (we don't even consider
+                    // the trait itself) for the definition -- and
+                    // so in that case it may be that the trait
+                    // *DOES* have a declaration, but we don't see
+                    // it, and we end up in this branch.
+                    //
+                    // This is kind of tricky to handle actually.
+                    // 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.
+                    bug!("Tried to project an inherited associated type during \
+                          coherence checking, which is currently not supported.");
+                };
+                candidate_set.vec.extend(new_candidate);
+            }
+            super::VtableImpl(_) => {
+                // In trans mode, we can just project out of impls, no prob.
+                assert!(selcx.projection_mode().is_any());
+                candidate_set.vec.push(ProjectionTyCandidate::Select);
+            }
+            super::VtableParam(..) => {
+                // This case tell us nothing about the value of an
+                // associated type. Consider:
+                //
+                // ```
+                // trait SomeTrait { type Foo; }
+                // fn foo<T:SomeTrait>(...) { }
+                // ```
+                //
+                // If the user writes `<T as SomeTrait>::Foo`, then the `T
+                // : SomeTrait` binding does not help us decide what the
+                // type `Foo` is (at least, not more specifically than
+                // what we already knew).
+                //
+                // But wait, you say! What about an example like this:
+                //
+                // ```
+                // fn bar<T:SomeTrait<Foo=usize>>(...) { ... }
+                // ```
+                //
+                // Doesn't the `T : Sometrait<Foo=usize>` predicate help
+                // resolve `T::Foo`? And of course it does, but in fact
+                // that single predicate is desugared into two predicates
+                // in the compiler: a trait predicate (`T : SomeTrait`) and a
+                // projection. And the projection where clause is handled
+                // in `assemble_candidates_from_param_env`.
+            }
+            super::VtableDefaultImpl(..) |
+            super::VtableBuiltin(..) => {
+                // These traits have no associated types.
+                span_bug!(
+                    obligation.cause.span,
+                    "Cannot project an associated type from `{:?}`",
+                    vtable);
+            }
         }
-    }
 
-    Ok(())
+        Ok(())
+    })
 }
 
 fn confirm_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
+    obligation_trait_ref: &ty::TraitRef<'tcx>,
     candidate: ProjectionTyCandidate<'tcx>)
     -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
 {
@@ -934,18 +929,116 @@ fn confirm_candidate<'cx, 'gcx, 'tcx>(
             confirm_param_env_candidate(selcx, obligation, poly_projection)
         }
 
-        ProjectionTyCandidate::Impl(impl_vtable) => {
-            confirm_impl_candidate(selcx, obligation, impl_vtable)
+        ProjectionTyCandidate::Select => {
+            confirm_select_candidate(selcx, obligation, obligation_trait_ref)
+        }
+    }
+}
+
+fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    obligation_trait_ref: &ty::TraitRef<'tcx>)
+    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
+{
+    let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
+    let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate());
+    let vtable = match selcx.select(&trait_obligation) {
+        Ok(Some(vtable)) => vtable,
+        _ => {
+            span_bug!(
+                obligation.cause.span,
+                "Failed to select `{:?}`",
+                trait_obligation);
         }
+    };
+
+    match vtable {
+        super::VtableImpl(data) =>
+            confirm_impl_candidate(selcx, obligation, data),
+        super::VtableClosure(data) =>
+            confirm_closure_candidate(selcx, obligation, data),
+        super::VtableFnPointer(data) =>
+            confirm_fn_pointer_candidate(selcx, obligation, data),
+        super::VtableObject(_) =>
+            confirm_object_candidate(selcx, obligation, obligation_trait_ref),
+        super::VtableDefaultImpl(..) |
+        super::VtableParam(..) |
+        super::VtableBuiltin(..) =>
+            // we don't create Select candidates with this kind of resolution
+            span_bug!(
+                obligation.cause.span,
+                "Cannot project an associated type from `{:?}`",
+                vtable),
+    }
+}
 
-        ProjectionTyCandidate::Closure(closure_vtable) => {
-            confirm_closure_candidate(selcx, obligation, closure_vtable)
+fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
+    obligation:  &ProjectionTyObligation<'tcx>,
+    obligation_trait_ref: &ty::TraitRef<'tcx>)
+    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
+{
+    let self_ty = obligation_trait_ref.self_ty();
+    let object_ty = selcx.infcx().shallow_resolve(self_ty);
+    debug!("confirm_object_candidate(object_ty={:?})",
+           object_ty);
+    let data = match object_ty.sty {
+        ty::TyTrait(ref data) => data,
+        _ => {
+            span_bug!(
+                obligation.cause.span,
+                "confirm_object_candidate called with non-object: {:?}",
+                object_ty);
         }
+    };
+    let projection_bounds = data.projection_bounds_with_self_ty(selcx.tcx(), object_ty);
+    let env_predicates = projection_bounds.iter()
+                                          .map(|p| p.to_predicate())
+                                          .collect();
+    let env_predicate = {
+        let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates);
+
+        // select only those projections that are actually projecting an
+        // item with the correct name
+        let env_predicates = env_predicates.filter_map(|p| match p {
+            ty::Predicate::Projection(data) =>
+                if data.item_name() == obligation.predicate.item_name {
+                    Some(data)
+                } else {
+                    None
+                },
+            _ => None
+        });
 
-        ProjectionTyCandidate::FnPointer(fn_pointer_vtable) => {
-            confirm_fn_pointer_candidate(selcx, obligation, fn_pointer_vtable)
+        // select those with a relevant trait-ref
+        let mut env_predicates = env_predicates.filter(|data| {
+            let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span);
+            let data_poly_trait_ref = data.to_poly_trait_ref();
+            let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
+            selcx.infcx().probe(|_| {
+                selcx.infcx().sub_poly_trait_refs(false,
+                                                  origin,
+                                                  data_poly_trait_ref,
+                                                  obligation_poly_trait_ref).is_ok()
+            })
+        });
+
+        // select the first matching one; there really ought to be one or
+        // else the object type is not WF, since an object type should
+        // include all of its projections explicitly
+        match env_predicates.next() {
+            Some(env_predicate) => env_predicate,
+            None => {
+                debug!("confirm_object_candidate: no env-predicate \
+                        found in object type `{:?}`; ill-formed",
+                       object_ty);
+                return (selcx.tcx().types.err, vec!());
+            }
         }
-    }
+    };
+
+    confirm_param_env_candidate(selcx, obligation, env_predicate)
 }
 
 fn confirm_fn_pointer_candidate<'cx, 'gcx, 'tcx>(
diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs
index 14b369f244d..4a14185b6e3 100644
--- a/src/librustc/ty/fold.rs
+++ b/src/librustc/ty/fold.rs
@@ -382,6 +382,35 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    /// Returns a set of all late-bound regions that are constrained
+    /// by `value`, meaning that if we instantiate those LBR with
+    /// variables and equate `value` with something else, those
+    /// variables will also be equated.
+    pub fn collect_constrained_late_bound_regions<T>(&self, value: &Binder<T>)
+                                                     -> FnvHashSet<ty::BoundRegion>
+        where T : TypeFoldable<'tcx>
+    {
+        self.collect_late_bound_regions(value, true)
+    }
+
+    /// Returns a set of all late-bound regions that appear in `value` anywhere.
+    pub fn collect_referenced_late_bound_regions<T>(&self, value: &Binder<T>)
+                                                    -> FnvHashSet<ty::BoundRegion>
+        where T : TypeFoldable<'tcx>
+    {
+        self.collect_late_bound_regions(value, false)
+    }
+
+    fn collect_late_bound_regions<T>(&self, value: &Binder<T>, just_constraint: bool)
+                                     -> FnvHashSet<ty::BoundRegion>
+        where T : TypeFoldable<'tcx>
+    {
+        let mut collector = LateBoundRegionsCollector::new(just_constraint);
+        let result = value.skip_binder().visit_with(&mut collector);
+        assert!(!result); // should never have stopped early
+        collector.regions
+    }
+
     /// Replace any late-bound regions bound in `value` with `'static`. Useful in trans but also
     /// method lookup and a few other places where precise region relationships are not required.
     pub fn erase_late_bound_regions<T>(self, value: &Binder<T>) -> T
@@ -625,3 +654,54 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
         false
     }
 }
+
+/// Collects all the late-bound regions it finds into a hash set.
+struct LateBoundRegionsCollector {
+    current_depth: u32,
+    regions: FnvHashSet<ty::BoundRegion>,
+    just_constrained: bool,
+}
+
+impl LateBoundRegionsCollector {
+    fn new(just_constrained: bool) -> Self {
+        LateBoundRegionsCollector {
+            current_depth: 1,
+            regions: FnvHashSet(),
+            just_constrained: just_constrained,
+        }
+    }
+}
+
+impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector {
+    fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<T>) -> bool {
+        self.current_depth += 1;
+        let result = t.super_visit_with(self);
+        self.current_depth -= 1;
+        result
+    }
+
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+        // if we are only looking for "constrained" region, we have to
+        // ignore the inputs to a projection, as they may not appear
+        // in the normalized form
+        if self.just_constrained {
+            match t.sty {
+                ty::TyProjection(..) => { return false; }
+                _ => { }
+            }
+        }
+
+        t.super_visit_with(self)
+    }
+
+    fn visit_region(&mut self, r: ty::Region) -> bool {
+        match r {
+            ty::ReLateBound(debruijn, br) if debruijn.depth == self.current_depth => {
+                self.regions.insert(br);
+            }
+            _ => { }
+        }
+        false
+    }
+}
+
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index e0abe1aebd2..9fca6d3d201 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -197,7 +197,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
         FutureIncompatibleInfo {
             id: LintId::of(OBJECT_UNSAFE_FRAGMENT),
             reference: "issue #33243 <https://github.com/rust-lang/rust/issues/33243>",
-        }
+        },
+        FutureIncompatibleInfo {
+            id: LintId::of(HR_LIFETIME_IN_ASSOC_TYPE),
+            reference: "issue #33685 <https://github.com/rust-lang/rust/issues/33685>",
+        },
         ]);
 
     // We have one lint pass defined specially
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 4faefb61056..ac86b7c8740 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -52,13 +52,17 @@ use middle::const_val::ConstVal;
 use rustc_const_eval::{eval_const_expr_partial, ConstEvalErr};
 use rustc_const_eval::EvalHint::UncheckedExprHint;
 use rustc_const_eval::ErrKind::ErroneousReferencedConstant;
+use hir::{self, SelfKind};
 use hir::def::{self, Def};
 use hir::def_id::DefId;
+use hir::print as pprust;
 use middle::resolve_lifetime as rl;
+use rustc::lint;
 use rustc::ty::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs, ParamSpace};
 use rustc::traits;
 use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable};
 use rustc::ty::wf::object_region_bounds;
+use rustc_back::slice;
 use require_c_abi_if_variadic;
 use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope,
              ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope,
@@ -74,10 +78,6 @@ use syntax::errors::DiagnosticBuilder;
 use syntax::feature_gate::{GateIssue, emit_feature_err};
 use syntax::parse::token::{self, keywords};
 
-use rustc::hir::print as pprust;
-use rustc::hir::{self, SelfKind};
-use rustc_back::slice;
-
 pub trait AstConv<'gcx, 'tcx> {
     fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>;
 
@@ -679,6 +679,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                                         PathParamMode::Explicit,
                                         trait_def_id,
                                         self_ty,
+                                        trait_ref.ref_id,
                                         trait_ref.path.segments.last().unwrap(),
                                         poly_projections)
     }
@@ -723,6 +724,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
         span: Span,
         param_mode: PathParamMode,
         trait_def_id: DefId,
+        trait_path_ref_id: ast::NodeId,
         trait_segment: &hir::PathSegment,
         mut projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
         -> ty::PolyTraitRef<'tcx>
@@ -732,6 +734,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                                         param_mode,
                                         trait_def_id,
                                         None,
+                                        trait_path_ref_id,
                                         trait_segment,
                                         projections)
     }
@@ -742,6 +745,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
         param_mode: PathParamMode,
         trait_def_id: DefId,
         self_ty: Option<Ty<'tcx>>,
+        path_id: ast::NodeId,
         trait_segment: &hir::PathSegment,
         poly_projections: &mut Vec<ty::PolyProjectionPredicate<'tcx>>)
         -> ty::PolyTraitRef<'tcx>
@@ -770,7 +774,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                 .filter_map(|binding| {
                     // specify type to assert that error was already reported in Err case:
                     let predicate: Result<_, ErrorReported> =
-                        self.ast_type_binding_to_poly_projection_predicate(poly_trait_ref.clone(),
+                        self.ast_type_binding_to_poly_projection_predicate(path_id,
+                                                                           poly_trait_ref.clone(),
                                                                            self_ty,
                                                                            binding);
                     predicate.ok() // ok to ignore Err() because ErrorReported (see above)
@@ -863,7 +868,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
         (self.tcx().mk_substs(substs), assoc_bindings)
     }
 
-    fn ast_type_binding_to_poly_projection_predicate(&self,
+    fn ast_type_binding_to_poly_projection_predicate(
+        &self,
+        path_id: ast::NodeId,
         mut trait_ref: ty::PolyTraitRef<'tcx>,
         self_ty: Option<Ty<'tcx>>,
         binding: &ConvertedBinding<'tcx>)
@@ -887,6 +894,36 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
         //
         // We want to produce `<B as SuperTrait<int>>::T == foo`.
 
+        // Find any late-bound regions declared in `ty` that are not
+        // declared in the trait-ref. These are not wellformed.
+        //
+        // Example:
+        //
+        //     for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
+        //     for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
+        let late_bound_in_trait_ref = tcx.collect_constrained_late_bound_regions(&trait_ref);
+        let late_bound_in_ty = tcx.collect_referenced_late_bound_regions(&ty::Binder(binding.ty));
+        debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
+        debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
+        for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) {
+            let br_name = match *br {
+                ty::BrNamed(_, name) => name,
+                _ => {
+                    span_bug!(
+                        binding.span,
+                        "anonymous bound region {:?} in binding but not trait ref",
+                        br);
+                }
+            };
+            tcx.sess.add_lint(
+                lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
+                path_id,
+                binding.span,
+                format!("binding for associated type `{}` references lifetime `{}`, \
+                         which does not appear in the trait input types",
+                        binding.item_name, br_name));
+        }
+
         // Simple case: X is defined in the current trait.
         if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) {
             return Ok(ty::Binder(ty::ProjectionPredicate {      // <-------------------+
@@ -1012,6 +1049,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                                                                path.span,
                                                                PathParamMode::Explicit,
                                                                trait_def_id,
+                                                               ty.id,
                                                                path.segments.last().unwrap(),
                                                                &mut projection_bounds);
                         Ok((trait_ref, projection_bounds))
@@ -1416,6 +1454,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                       param_mode: PathParamMode,
                       def: Def,
                       opt_self_ty: Option<Ty<'tcx>>,
+                      base_path_ref_id: ast::NodeId,
                       base_segments: &[hir::PathSegment])
                       -> Ty<'tcx> {
         let tcx = self.tcx();
@@ -1434,6 +1473,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                                                        span,
                                                        param_mode,
                                                        trait_def_id,
+                                                       base_path_ref_id,
                                                        base_segments.last().unwrap(),
                                                        &mut projection_bounds);
 
@@ -1518,6 +1558,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                                       param_mode: PathParamMode,
                                       mut def: Def,
                                       opt_self_ty: Option<Ty<'tcx>>,
+                                      base_path_ref_id: ast::NodeId,
                                       base_segments: &[hir::PathSegment],
                                       assoc_segments: &[hir::PathSegment])
                                       -> (Ty<'tcx>, Def) {
@@ -1532,6 +1573,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                                          param_mode,
                                          def,
                                          opt_self_ty,
+                                         base_path_ref_id,
                                          base_segments);
         debug!("finish_resolving_def_to_ty: base_def_to_ty returned {:?}", ty);
         // If any associated type segments remain, attempt to resolve them.
@@ -1607,7 +1649,45 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
             }
             hir::TyBareFn(ref bf) => {
                 require_c_abi_if_variadic(tcx, &bf.decl, bf.abi, ast_ty.span);
-                tcx.mk_fn_ptr(self.ty_of_bare_fn(bf.unsafety, bf.abi, &bf.decl))
+                let bare_fn_ty = self.ty_of_bare_fn(bf.unsafety, bf.abi, &bf.decl);
+
+                // Find any late-bound regions declared in return type that do
+                // not appear in the arguments. These are not wellformed.
+                //
+                // Example:
+                //
+                //     for<'a> fn() -> &'a str <-- 'a is bad
+                //     for<'a> fn(&'a String) -> &'a str <-- 'a is ok
+                //
+                // Note that we do this check **here** and not in
+                // `ty_of_bare_fn` because the latter is also used to make
+                // the types for fn items, and we do not want to issue a
+                // warning then. (Once we fix #32330, the regions we are
+                // checking for here would be considered early bound
+                // anyway.)
+                let inputs = bare_fn_ty.sig.inputs();
+                let late_bound_in_args = tcx.collect_constrained_late_bound_regions(&inputs);
+                let output = bare_fn_ty.sig.output();
+                let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
+                for br in late_bound_in_ret.difference(&late_bound_in_args) {
+                    let br_name = match *br {
+                        ty::BrNamed(_, name) => name,
+                        _ => {
+                            span_bug!(
+                                bf.decl.output.span(),
+                                "anonymous bound region {:?} in return but not args",
+                                br);
+                        }
+                    };
+                    tcx.sess.add_lint(
+                        lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
+                        ast_ty.id,
+                        ast_ty.span,
+                        format!("return type references lifetime `{}`, \
+                                 which does not appear in the trait input types",
+                                br_name));
+                }
+                tcx.mk_fn_ptr(bare_fn_ty)
             }
             hir::TyPolyTraitRef(ref bounds) => {
                 self.conv_ty_poly_trait_ref(rscope, ast_ty.span, bounds)
@@ -1635,6 +1715,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                                                                  PathParamMode::Explicit,
                                                                  def,
                                                                  opt_self_ty,
+                                                                 ast_ty.id,
                                                                  &path.segments[..base_ty_end],
                                                                  &path.segments[base_ty_end..]);
 
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index ed2edc30c9d..1ee9d1032a6 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3866,6 +3866,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                                                  PathParamMode::Optional,
                                                                  def,
                                                                  opt_self_ty,
+                                                                 node_id,
                                                                  &ty_segments[..base_ty_end],
                                                                  &ty_segments[base_ty_end..]);
             let item_segment = path.segments.last().unwrap();
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index f9a22e2a577..5896a34b0d1 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -568,7 +568,8 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 
     let (fty, explicit_self_category) =
         AstConv::ty_of_method(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)),
-                              sig, untransformed_rcvr_ty);
+                              sig,
+                              untransformed_rcvr_ty);
 
     let def_id = ccx.tcx.map.local_def_id(id);
     let substs = mk_item_substs(ccx, &ty_generics);
diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs
index 7592214c0ab..2ca61ba76d4 100644
--- a/src/libsyntax/errors/mod.rs
+++ b/src/libsyntax/errors/mod.rs
@@ -180,7 +180,7 @@ impl error::Error for ExplicitBug {
 #[must_use]
 #[derive(Clone)]
 pub struct DiagnosticBuilder<'a> {
-    emitter: &'a RefCell<Box<Emitter>>,
+    handler: &'a Handler,
     level: Level,
     message: String,
     code: Option<String>,
@@ -204,8 +204,9 @@ impl<'a> DiagnosticBuilder<'a> {
             return;
         }
 
-        self.emitter.borrow_mut().emit_struct(&self);
+        self.handler.emit.borrow_mut().emit_struct(&self);
         self.cancel();
+        self.handler.panic_if_treat_err_as_bug();
 
         // if self.is_fatal() {
         //     panic!(FatalError);
@@ -321,11 +322,11 @@ impl<'a> DiagnosticBuilder<'a> {
 
     /// Convenience function for internal use, clients should use one of the
     /// struct_* methods on Handler.
-    fn new(emitter: &'a RefCell<Box<Emitter>>,
+    fn new(handler: &'a Handler,
            level: Level,
            message: &str) -> DiagnosticBuilder<'a> {
         DiagnosticBuilder {
-            emitter: emitter,
+            handler: handler,
             level: level,
             message: message.to_owned(),
             code: None,
@@ -362,10 +363,10 @@ impl<'a> fmt::Debug for DiagnosticBuilder<'a> {
 impl<'a> Drop for DiagnosticBuilder<'a> {
     fn drop(&mut self) {
         if !panicking() && !self.cancelled() {
-            self.emitter.borrow_mut().emit(&MultiSpan::new(),
-                                           "Error constructed but not emitted",
-                                           None,
-                                           Bug);
+            self.handler.emit.borrow_mut().emit(&MultiSpan::new(),
+                                                "Error constructed but not emitted",
+                                                None,
+                                                Bug);
             panic!();
         }
     }
@@ -412,14 +413,14 @@ impl Handler {
     }
 
     pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> {
-        DiagnosticBuilder::new(&self.emit, Level::Cancelled, "")
+        DiagnosticBuilder::new(self, Level::Cancelled, "")
     }
 
     pub fn struct_span_warn<'a, S: Into<MultiSpan>>(&'a self,
                                                     sp: S,
                                                     msg: &str)
                                                     -> DiagnosticBuilder<'a> {
-        let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
+        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
         result.set_span(sp);
         if !self.can_emit_warnings {
             result.cancel();
@@ -431,7 +432,7 @@ impl Handler {
                                                               msg: &str,
                                                               code: &str)
                                                               -> DiagnosticBuilder<'a> {
-        let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
+        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
         result.set_span(sp);
         result.code(code.to_owned());
         if !self.can_emit_warnings {
@@ -440,7 +441,7 @@ impl Handler {
         result
     }
     pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
-        let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
+        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
         if !self.can_emit_warnings {
             result.cancel();
         }
@@ -451,7 +452,7 @@ impl Handler {
                                                    msg: &str)
                                                    -> DiagnosticBuilder<'a> {
         self.bump_err_count();
-        let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
+        let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
         result.set_span(sp);
         result
     }
@@ -461,21 +462,21 @@ impl Handler {
                                                              code: &str)
                                                              -> DiagnosticBuilder<'a> {
         self.bump_err_count();
-        let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
+        let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
         result.set_span(sp);
         result.code(code.to_owned());
         result
     }
     pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
         self.bump_err_count();
-        DiagnosticBuilder::new(&self.emit, Level::Error, msg)
+        DiagnosticBuilder::new(self, Level::Error, msg)
     }
     pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self,
                                                      sp: S,
                                                      msg: &str)
                                                      -> DiagnosticBuilder<'a> {
         self.bump_err_count();
-        let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
+        let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
         result.set_span(sp);
         result
     }
@@ -485,14 +486,14 @@ impl Handler {
                                                                code: &str)
                                                                -> DiagnosticBuilder<'a> {
         self.bump_err_count();
-        let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
+        let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
         result.set_span(sp);
         result.code(code.to_owned());
         result
     }
     pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
         self.bump_err_count();
-        DiagnosticBuilder::new(&self.emit, Level::Fatal, msg)
+        DiagnosticBuilder::new(self, Level::Fatal, msg)
     }
 
     pub fn cancel(&mut self, err: &mut DiagnosticBuilder) {
@@ -503,36 +504,35 @@ impl Handler {
         err.cancel();
     }
 
-    pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError {
+    fn panic_if_treat_err_as_bug(&self) {
         if self.treat_err_as_bug {
-            self.span_bug(sp, msg);
+            panic!("encountered error with `-Z treat_err_as_bug");
         }
+    }
+
+    pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str)
+                                          -> FatalError {
         self.emit(&sp.into(), msg, Fatal);
         self.bump_err_count();
+        self.panic_if_treat_err_as_bug();
         return FatalError;
     }
     pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str)
-    -> FatalError {
-        if self.treat_err_as_bug {
-            self.span_bug(sp, msg);
-        }
+                                                    -> FatalError {
         self.emit_with_code(&sp.into(), msg, code, Fatal);
         self.bump_err_count();
+        self.panic_if_treat_err_as_bug();
         return FatalError;
     }
     pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        if self.treat_err_as_bug {
-            self.span_bug(sp, msg);
-        }
         self.emit(&sp.into(), msg, Error);
         self.bump_err_count();
+        self.panic_if_treat_err_as_bug();
     }
     pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
-        if self.treat_err_as_bug {
-            self.span_bug(sp, msg);
-        }
         self.emit_with_code(&sp.into(), msg, code, Error);
         self.bump_err_count();
+        self.panic_if_treat_err_as_bug();
     }
     pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
         self.emit(&sp.into(), msg, Warning);
diff --git a/src/test/compile-fail/associated-types-eq-hr.rs b/src/test/compile-fail/associated-types-eq-hr.rs
index d5678c155fd..52a2ca9082d 100644
--- a/src/test/compile-fail/associated-types-eq-hr.rs
+++ b/src/test/compile-fail/associated-types-eq-hr.rs
@@ -40,6 +40,17 @@ impl<'a> TheTrait<&'a isize> for UintStruct {
     }
 }
 
+struct Tuple {
+}
+
+impl<'a> TheTrait<(&'a isize, &'a isize)> for Tuple {
+    type A = &'a isize;
+
+    fn get(&self, t: (&'a isize, &'a isize)) -> &'a isize {
+        t.0
+    }
+}
+
 fn foo<T>()
     where T : for<'x> TheTrait<&'x isize, A = &'x isize>
 {
@@ -52,10 +63,28 @@ fn bar<T>()
     // ok for UintStruct, but not IntStruct
 }
 
-fn baz<T>()
-    where T : for<'x,'y> TheTrait<&'x isize, A = &'y isize>
+fn tuple_one<T>()
+    where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize>
+{
+    // not ok for tuple, two lifetimes and we pick first
+}
+
+fn tuple_two<T>()
+    where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize>
 {
-    // not ok for either struct, due to the use of two lifetimes
+    // not ok for tuple, two lifetimes and we pick second
+}
+
+fn tuple_three<T>()
+    where T : for<'x> TheTrait<(&'x isize, &'x isize), A = &'x isize>
+{
+    // ok for tuple
+}
+
+fn tuple_four<T>()
+    where T : for<'x,'y> TheTrait<(&'x isize, &'y isize)>
+{
+    // not ok for tuple, two lifetimes, and lifetime matching is invariant
 }
 
 pub fn main() {
@@ -65,6 +94,16 @@ pub fn main() {
     bar::<IntStruct>(); //~ ERROR type mismatch
     bar::<UintStruct>();
 
-    baz::<IntStruct>(); //~ ERROR type mismatch
-    baz::<UintStruct>(); //~ ERROR type mismatch
+    tuple_one::<Tuple>();
+    //~^ ERROR E0277
+    //~| ERROR type mismatch
+
+    tuple_two::<Tuple>();
+    //~^ ERROR E0277
+    //~| ERROR type mismatch
+
+    tuple_three::<Tuple>();
+
+    tuple_four::<Tuple>();
+    //~^ ERROR E0277
 }
diff --git a/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs b/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs
new file mode 100644
index 00000000000..f60f06b4ec8
--- /dev/null
+++ b/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs
@@ -0,0 +1,66 @@
+// Copyright 2012 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.
+
+// revisions: func object clause
+
+#![allow(dead_code)]
+#![feature(rustc_attrs)]
+#![feature(unboxed_closures)]
+#![deny(hr_lifetime_in_assoc_type)]
+
+trait Foo<'a> {
+    type Item;
+}
+
+impl<'a> Foo<'a> for() {
+    type Item = ();
+}
+
+// Check that appearing in a projection input in the argument is not enough:
+#[cfg(func)]
+fn func1(_: for<'a> fn(<() as Foo<'a>>::Item) -> &'a i32) {
+    //[func]~^ ERROR return type references lifetime `'a`
+    //[func]~| WARNING previously accepted
+}
+
+// Check that appearing in a projection input in the return still
+// causes an error:
+#[cfg(func)]
+fn func2(_: for<'a> fn() -> <() as Foo<'a>>::Item) {
+    //[func]~^ ERROR return type references lifetime `'a`
+    //[func]~| WARNING previously accepted
+}
+
+#[cfg(object)]
+fn object1(_: Box<for<'a> Fn(<() as Foo<'a>>::Item) -> &'a i32>) {
+    //[object]~^ ERROR `Output` references lifetime `'a`
+    //[object]~| WARNING previously accepted
+}
+
+#[cfg(object)]
+fn object2(_: Box<for<'a> Fn() -> <() as Foo<'a>>::Item>) {
+    //[object]~^ ERROR `Output` references lifetime `'a`
+    //[object]~| WARNING previously accepted
+}
+
+#[cfg(clause)]
+fn clause1<T>() where T: for<'a> Fn(<() as Foo<'a>>::Item) -> &'a i32 {
+    //[clause]~^ ERROR `Output` references lifetime `'a`
+    //[clause]~| WARNING previously accepted
+}
+
+#[cfg(clause)]
+fn clause2<T>() where T: for<'a> Fn() -> <() as Foo<'a>>::Item {
+    //[clause]~^ ERROR `Output` references lifetime `'a`
+    //[clause]~| WARNING previously accepted
+}
+
+#[rustc_error]
+fn main() { } //[ok]~ ERROR compilation successful
diff --git a/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs b/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs
new file mode 100644
index 00000000000..020c9e5e1db
--- /dev/null
+++ b/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs
@@ -0,0 +1,90 @@
+// Copyright 2012 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.
+
+// revisions: angle paren ok elision
+
+#![allow(dead_code)]
+#![feature(rustc_attrs)]
+#![feature(unboxed_closures)]
+#![deny(hr_lifetime_in_assoc_type)]
+
+trait Foo {
+    type Item;
+}
+
+#[cfg(angle)]
+fn angle<T: for<'a> Foo<Item=&'a i32>>() {
+    //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
+    //[angle]~| WARNING previously accepted
+}
+
+#[cfg(angle)]
+fn angle1<T>() where T: for<'a> Foo<Item=&'a i32> {
+    //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
+    //[angle]~| WARNING previously accepted
+}
+
+#[cfg(angle)]
+fn angle2<T>() where for<'a> T: Foo<Item=&'a i32> {
+    //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
+    //[angle]~| WARNING previously accepted
+}
+
+#[cfg(angle)]
+fn angle3(_: &for<'a> Foo<Item=&'a i32>) {
+    //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a`
+    //[angle]~| WARNING previously accepted
+}
+
+#[cfg(paren)]
+fn paren<T: for<'a> Fn() -> &'a i32>() {
+    //[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
+    //[paren]~| WARNING previously accepted
+}
+
+#[cfg(paren)]
+fn paren1<T>() where T: for<'a> Fn() -> &'a i32 {
+    //[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
+    //[paren]~| WARNING previously accepted
+}
+
+#[cfg(paren)]
+fn paren2<T>() where for<'a> T: Fn() -> &'a i32 {
+    //[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
+    //[paren]~| WARNING previously accepted
+}
+
+#[cfg(paren)]
+fn paren3(_: &for<'a> Fn() -> &'a i32) {
+    //[paren]~^ ERROR binding for associated type `Output` references lifetime `'a`
+    //[paren]~| WARNING previously accepted
+}
+
+#[cfg(elision)]
+fn elision<T: Fn() -> &i32>() {
+    //[elision]~^ ERROR E0106
+}
+
+struct Parameterized<'a> { x: &'a str }
+
+#[cfg(ok)]
+fn ok1<T: for<'a> Fn(&Parameterized<'a>) -> &'a i32>() {
+}
+
+#[cfg(ok)]
+fn ok2<T: for<'a,'b> Fn<(&'b Parameterized<'a>,), Output=&'a i32>>() {
+}
+
+#[cfg(ok)]
+fn ok3<T>() where for<'a> Parameterized<'a>: Foo<Item=&'a i32> {
+}
+
+#[rustc_error]
+fn main() { } //[ok]~ ERROR compilation successful
diff --git a/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs b/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs
new file mode 100644
index 00000000000..0b4a9bf58a6
--- /dev/null
+++ b/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs
@@ -0,0 +1,64 @@
+// Copyright 2012 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.
+
+// revisions: sig local structure ok elision
+
+#![allow(dead_code)]
+#![feature(rustc_attrs)]
+#![feature(unboxed_closures)]
+#![deny(hr_lifetime_in_assoc_type)]
+
+trait Foo {
+    type Item;
+}
+
+#[cfg(sig)]
+fn sig1(_: for<'a> fn() -> &'a i32) {
+    //[sig]~^ ERROR return type references lifetime `'a`
+    //[sig]~| WARNING previously accepted
+}
+
+#[cfg(sig)]
+fn sig2(_: for<'a, 'b> fn(&'b i32) -> &'a i32) {
+    //[sig]~^ ERROR return type references lifetime `'a`
+    //[sig]~| WARNING previously accepted
+}
+
+#[cfg(local)]
+fn local1() {
+    let _: for<'a> fn() -> &'a i32 = loop { };
+    //[local]~^ ERROR return type references lifetime `'a`
+    //[local]~| WARNING previously accepted
+}
+
+#[cfg(structure)]
+struct Struct1 {
+    x: for<'a> fn() -> &'a i32
+    //[structure]~^ ERROR return type references lifetime `'a`
+    //[structure]~| WARNING previously accepted
+}
+
+#[cfg(elision)]
+fn elision(_: fn() -> &i32) {
+    //[elision]~^ ERROR E0106
+}
+
+struct Parameterized<'a> { x: &'a str }
+
+#[cfg(ok)]
+fn ok1(_: &for<'a> Fn(&Parameterized<'a>) -> &'a i32) {
+}
+
+#[cfg(ok)]
+fn ok2(_: &for<'a,'b> Fn<(&'b Parameterized<'a>,), Output=&'a i32>) {
+}
+
+#[rustc_error]
+fn main() { } //[ok]~ ERROR compilation successful