about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-03-22 06:37:12 -0400
committerNiko Matsakis <niko@alum.mit.edu>2016-05-17 20:25:18 -0400
commitccfb74e800eff83f316f55ecae337569d7e83ee4 (patch)
tree36722ea65bec3d6f97ba4e23effdd7515d91bcff
parent97ca8d799c2b6e6a4ce1b55b7d6e28526d3f23d5 (diff)
warn for where/return-types that reference regions
This is a step towards fixing #32330. The full fix would be a breaking
change, so we begin by issuing warnings for scenarios that will break.
-rw-r--r--src/librustc/lint/builtin.rs10
-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/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
10 files changed, 449 insertions, 15 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/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/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