about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-07-18 10:20:13 +0000
committerbors <bors@rust-lang.org>2017-07-18 10:20:13 +0000
commit83c659ef655b1f740777f83eb415fd7ebe5a3fe5 (patch)
treeb33f8e7603cf90dba99ae18b867ebccfe552142d
parent2e6334062e2be142125e99d63867711da505cc9e (diff)
parent39114f916905b16b414f51031426309b63f856a9 (diff)
Auto merge of #42492 - petrochenkov:methlife, r=nikomatsakis
Support generic lifetime arguments in method calls

Fixes https://github.com/rust-lang/rust/issues/42403
Fixes https://github.com/rust-lang/rust/issues/42115
Lifetimes in a method call `x.f::<'a, 'b, T, U>()` are treated exactly like lifetimes in the equivalent UFCS call `X::f::<'a, 'b, T, U>`.
In addition, if the method has late bound lifetime parameters (explicit or implicit), then explicitly specifying lifetime arguments is not permitted (guarded by a compatibility lint).
[breaking-change] because previously lifetimes in method calls were accepted unconditionally.

r? @eddyb
-rw-r--r--src/librustc/ich/impls_ty.rs2
-rw-r--r--src/librustc/lint/builtin.rs7
-rw-r--r--src/librustc/ty/mod.rs1
-rw-r--r--src/librustc_lint/lib.rs6
-rw-r--r--src/librustc_passes/ast_validation.rs15
-rw-r--r--src/librustc_typeck/check/method/confirm.rs59
-rw-r--r--src/librustc_typeck/check/mod.rs81
-rw-r--r--src/librustc_typeck/collect.rs92
-rw-r--r--src/librustc_typeck/diagnostics.rs88
-rw-r--r--src/test/compile-fail/E0088.rs11
-rw-r--r--src/test/compile-fail/constructor-lifetime-args.rs36
-rw-r--r--src/test/compile-fail/method-call-lifetime-args-lint.rs97
-rw-r--r--src/test/compile-fail/method-call-lifetime-args-subst-index.rs (renamed from src/test/compile-fail/E0036.rs)23
-rw-r--r--src/test/compile-fail/method-call-lifetime-args-unresolved.rs (renamed from src/test/compile-fail/E0035.rs)12
-rw-r--r--src/test/compile-fail/method-call-lifetime-args.rs82
-rw-r--r--src/test/compile-fail/method-call-type-binding.rs2
-rw-r--r--src/test/compile-fail/trait-test-2.rs5
-rw-r--r--src/test/incremental/hashes/inherent_impls.rs2
-rw-r--r--src/test/incremental/hashes/trait_defs.rs2
19 files changed, 417 insertions, 206 deletions
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index a1dd2caf786..3e227872848 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -346,6 +346,7 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for ty::Ge
             // `def_id.index` (`def_id.krate` is the same as the item's).
             type_param_to_index: _, // Don't hash this
             has_self,
+            has_late_bound_regions,
         } = *self;
 
         parent.hash_stable(hcx, hasher);
@@ -354,6 +355,7 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for ty::Ge
         regions.hash_stable(hcx, hasher);
         types.hash_stable(hcx, hasher);
         has_self.hash_stable(hcx, hasher);
+        has_late_bound_regions.hash_stable(hcx, hasher);
     }
 }
 
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 2d088c4f6d1..cbe642a9a76 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -205,6 +205,12 @@ declare_lint! {
 }
 
 declare_lint! {
+    pub LATE_BOUND_LIFETIME_ARGUMENTS,
+    Warn,
+    "detects generic lifetime arguments in path segments with late bound lifetime parameters"
+}
+
+declare_lint! {
     pub DEPRECATED,
     Warn,
     "detects use of deprecated items"
@@ -249,6 +255,7 @@ impl LintPass for HardwiredLints {
             LEGACY_CONSTRUCTOR_VISIBILITY,
             MISSING_FRAGMENT_SPECIFIER,
             PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES,
+            LATE_BOUND_LIFETIME_ARGUMENTS,
             DEPRECATED
         )
     }
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 5aaba526e26..1fee0dd9863 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -719,6 +719,7 @@ pub struct Generics {
     pub type_param_to_index: BTreeMap<DefIndex, u32>,
 
     pub has_self: bool,
+    pub has_late_bound_regions: bool,
 }
 
 impl Generics {
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index a03f12c3dfb..21dca7f6c61 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -235,7 +235,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
         FutureIncompatibleInfo {
             id: LintId::of(PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES),
             reference: "issue #42238 <https://github.com/rust-lang/rust/issues/42238>",
-        }
+        },
+        FutureIncompatibleInfo {
+            id: LintId::of(LATE_BOUND_LIFETIME_ARGUMENTS),
+            reference: "issue #42868 <https://github.com/rust-lang/rust/issues/42868>",
+        },
         ]);
 
     // Register renamed and removed lints
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index 72c7b92fe6e..99a49dbd7d7 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -127,18 +127,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             }
             ExprKind::MethodCall(ref segment, ..) => {
                 if let Some(ref params) = segment.parameters {
-                    match **params {
-                        PathParameters::AngleBracketed(ref param_data) => {
-                            if !param_data.bindings.is_empty() {
-                                let binding_span = param_data.bindings[0].span;
-                                self.err_handler().span_err(binding_span,
-                                    "type bindings cannot be used in method calls");
-                            }
-                        }
-                        PathParameters::Parenthesized(..) => {
-                            self.err_handler().span_err(expr.span,
-                                "parenthesized parameters cannot be used on method calls");
-                        }
+                    if let PathParameters::Parenthesized(..) = **params {
+                        self.err_handler().span_err(expr.span,
+                            "parenthesized parameters cannot be used on method calls");
                     }
                 }
             }
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 0829951e12d..ad4ee5a9d6d 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -10,6 +10,7 @@
 
 use super::{probe, MethodCallee};
 
+use astconv::AstConv;
 use check::{FnCtxt, LvalueOp, callee};
 use hir::def_id::DefId;
 use rustc::ty::subst::Substs;
@@ -280,62 +281,38 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
     fn instantiate_method_substs(&mut self,
                                  pick: &probe::Pick<'tcx>,
                                  segment: &hir::PathSegment,
-                                 substs: &Substs<'tcx>)
+                                 parent_substs: &Substs<'tcx>)
                                  -> &'tcx Substs<'tcx> {
-        let supplied_method_types = match segment.parameters {
-            hir::AngleBracketedParameters(ref data) => &data.types,
-            _ => bug!("unexpected generic arguments: {:?}", segment.parameters),
-        };
-
         // Determine the values for the generic parameters of the method.
         // If they were not explicitly supplied, just construct fresh
         // variables.
-        let num_supplied_types = supplied_method_types.len();
         let method_generics = self.tcx.generics_of(pick.item.def_id);
-        let num_method_types = method_generics.types.len();
-
-        if num_supplied_types > 0 && num_supplied_types != num_method_types {
-            if num_method_types == 0 {
-                struct_span_err!(self.tcx.sess,
-                                 self.span,
-                                 E0035,
-                                 "does not take type parameters")
-                    .span_label(self.span, "called with unneeded type parameters")
-                    .emit();
-            } else {
-                struct_span_err!(self.tcx.sess,
-                                 self.span,
-                                 E0036,
-                                 "incorrect number of type parameters given for this method: \
-                                  expected {}, found {}",
-                                 num_method_types,
-                                 num_supplied_types)
-                    .span_label(self.span,
-                                format!("Passed {} type argument{}, expected {}",
-                                         num_supplied_types,
-                                         if num_supplied_types != 1 { "s" } else { "" },
-                                         num_method_types))
-                    .emit();
-            }
-        }
+        let mut fn_segment = Some((segment, method_generics));
+        self.fcx.check_path_parameter_count(self.span, &mut fn_segment, true);
 
         // Create subst for early-bound lifetime parameters, combining
         // parameters from the type and those from the method.
-        //
-        // FIXME -- permit users to manually specify lifetimes
-        let supplied_start = substs.len() + method_generics.regions.len();
+        let (supplied_types, supplied_lifetimes) = match segment.parameters {
+            hir::AngleBracketedParameters(ref data) => (&data.types, &data.lifetimes),
+            _ => bug!("unexpected generic arguments: {:?}", segment.parameters),
+        };
+        assert_eq!(method_generics.parent_count(), parent_substs.len());
         Substs::for_item(self.tcx, pick.item.def_id, |def, _| {
             let i = def.index as usize;
-            if i < substs.len() {
-                substs.region_at(i)
+            if i < parent_substs.len() {
+                parent_substs.region_at(i)
+            } else if let Some(lifetime) =
+                    supplied_lifetimes.get(i - parent_substs.len()) {
+                AstConv::ast_region_to_region(self.fcx, lifetime, Some(def))
             } else {
                 self.region_var_for_def(self.span, def)
             }
         }, |def, cur_substs| {
             let i = def.index as usize;
-            if i < substs.len() {
-                substs.type_at(i)
-            } else if let Some(ast_ty) = supplied_method_types.get(i - supplied_start) {
+            if i < parent_substs.len() {
+                parent_substs.type_at(i)
+            } else if let Some(ast_ty) =
+                    supplied_types.get(i - parent_substs.len() - method_generics.regions.len()) {
                 self.to_ty(ast_ty)
             } else {
                 self.type_var_for_def(self.span, def, cur_substs)
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index cdbe5e14e90..af11cacb247 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -4491,8 +4491,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // variables. If the user provided some types, we may still need
         // to add defaults. If the user provided *too many* types, that's
         // a problem.
-        self.check_path_parameter_count(span, &mut type_segment);
-        self.check_path_parameter_count(span, &mut fn_segment);
+        self.check_path_parameter_count(span, &mut type_segment, false);
+        self.check_path_parameter_count(span, &mut fn_segment, false);
 
         let (fn_start, has_self) = match (type_segment, fn_segment) {
             (_, Some((_, generics))) => {
@@ -4618,7 +4618,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     /// Report errors if the provided parameters are too few or too many.
     fn check_path_parameter_count(&self,
                                   span: Span,
-                                  segment: &mut Option<(&hir::PathSegment, &ty::Generics)>) {
+                                  segment: &mut Option<(&hir::PathSegment, &ty::Generics)>,
+                                  is_method_call: bool) {
         let (lifetimes, types, infer_types, bindings) = {
             match segment.map(|(s, _)| &s.parameters) {
                 Some(&hir::AngleBracketedParameters(ref data)) => {
@@ -4632,6 +4633,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 None => (&[][..], &[][..], true, &[][..])
             }
         };
+        let infer_lifetimes = lifetimes.len() == 0;
 
         let count_lifetime_params = |n| {
             format!("{} lifetime parameter{}", n, if n == 1 { "" } else { "s" })
@@ -4640,32 +4642,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             format!("{} type parameter{}", n, if n == 1 { "" } else { "s" })
         };
 
-        // Check provided lifetime parameters.
-        let lifetime_defs = segment.map_or(&[][..], |(_, generics)| &generics.regions);
-        if lifetimes.len() > lifetime_defs.len() {
-            let expected_text = count_lifetime_params(lifetime_defs.len());
-            let actual_text = count_lifetime_params(lifetimes.len());
-            struct_span_err!(self.tcx.sess, span, E0088,
-                             "too many lifetime parameters provided: \
-                              expected at most {}, found {}",
-                             expected_text, actual_text)
-                .span_label(span, format!("expected {}", expected_text))
-                .emit();
-        } else if lifetimes.len() > 0 && lifetimes.len() < lifetime_defs.len() {
-            let expected_text = count_lifetime_params(lifetime_defs.len());
-            let actual_text = count_lifetime_params(lifetimes.len());
-            struct_span_err!(self.tcx.sess, span, E0090,
-                             "too few lifetime parameters provided: \
-                              expected {}, found {}",
-                             expected_text, actual_text)
-                .span_label(span, format!("expected {}", expected_text))
-                .emit();
-        }
-
-        // The case where there is not enough lifetime parameters is not checked,
-        // because this is not possible - a function never takes lifetime parameters.
-        // See discussion for Pull Request 36208.
-
         // Check provided type parameters.
         let type_defs = segment.map_or(&[][..], |(_, generics)| {
             if generics.parent.is_none() {
@@ -4690,7 +4666,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             // type parameters, we force instantiate_value_path to
             // use inference variables instead of the provided types.
             *segment = None;
-        } else if !infer_types && types.len() < required_len {
+        } else if types.len() < required_len && !infer_types {
             let expected_text = count_type_params(required_len);
             let actual_text = count_type_params(types.len());
             struct_span_err!(self.tcx.sess, span, E0089,
@@ -4706,6 +4682,51 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                       "unexpected binding of associated item in expression path \
                        (only allowed in type paths)");
         }
+
+        // Check provided lifetime parameters.
+        let lifetime_defs = segment.map_or(&[][..], |(_, generics)| &generics.regions);
+        let required_len = lifetime_defs.len();
+
+        // Prohibit explicit lifetime arguments if late bound lifetime parameters are present.
+        let has_late_bound_lifetime_defs =
+            segment.map_or(false, |(_, generics)| generics.has_late_bound_regions);
+        if has_late_bound_lifetime_defs && !lifetimes.is_empty() {
+            // Report this as a lint only if no error was reported previously.
+            if !is_method_call && (lifetimes.len() > lifetime_defs.len() ||
+                                   lifetimes.len() < required_len && !infer_lifetimes) {
+                self.tcx.sess.span_err(lifetimes[0].span,
+                                       "cannot specify lifetime arguments explicitly \
+                                        if late bound lifetime parameters are present");
+                *segment = None;
+            } else {
+                self.tcx.sess.add_lint(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
+                                       lifetimes[0].id, lifetimes[0].span,
+                                       format!("cannot specify lifetime arguments explicitly \
+                                                if late bound lifetime parameters are present"));
+            }
+            return;
+        }
+
+        if lifetimes.len() > lifetime_defs.len() {
+            let span = lifetimes[lifetime_defs.len()].span;
+            let expected_text = count_lifetime_params(lifetime_defs.len());
+            let actual_text = count_lifetime_params(lifetimes.len());
+            struct_span_err!(self.tcx.sess, span, E0088,
+                             "too many lifetime parameters provided: \
+                              expected at most {}, found {}",
+                             expected_text, actual_text)
+                .span_label(span, format!("expected {}", expected_text))
+                .emit();
+        } else if lifetimes.len() < required_len && !infer_lifetimes {
+            let expected_text = count_lifetime_params(lifetime_defs.len());
+            let actual_text = count_lifetime_params(lifetimes.len());
+            struct_span_err!(self.tcx.sess, span, E0090,
+                             "too few lifetime parameters provided: \
+                              expected {}, found {}",
+                             expected_text, actual_text)
+                .span_label(span, format!("expected {}", expected_text))
+                .emit();
+        }
     }
 
     fn structurally_resolve_type_or_else<F>(&self, sp: Span, ty: Ty<'tcx>, f: F)
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 002a148c459..72bd084330d 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -772,6 +772,95 @@ fn trait_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     tcx.alloc_trait_def(def)
 }
 
+fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                    node: hir_map::Node<'tcx>)
+                                    -> bool {
+    struct LateBoundRegionsDetector<'a, 'tcx: 'a> {
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        binder_depth: u32,
+        has_late_bound_regions: bool,
+    }
+
+    impl<'a, 'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'a, 'tcx> {
+        fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+            NestedVisitorMap::None
+        }
+
+        fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
+            if self.has_late_bound_regions { return }
+            match ty.node {
+                hir::TyBareFn(..) => {
+                    self.binder_depth += 1;
+                    intravisit::walk_ty(self, ty);
+                    self.binder_depth -= 1;
+                }
+                _ => intravisit::walk_ty(self, ty)
+            }
+        }
+
+        fn visit_poly_trait_ref(&mut self,
+                                tr: &'tcx hir::PolyTraitRef,
+                                m: hir::TraitBoundModifier) {
+            if self.has_late_bound_regions { return }
+            self.binder_depth += 1;
+            intravisit::walk_poly_trait_ref(self, tr, m);
+            self.binder_depth -= 1;
+        }
+
+        fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) {
+            if self.has_late_bound_regions { return }
+
+            match self.tcx.named_region_map.defs.get(&lt.id).cloned() {
+                Some(rl::Region::Static) | Some(rl::Region::EarlyBound(..)) => {}
+                Some(rl::Region::LateBound(debruijn, _)) |
+                Some(rl::Region::LateBoundAnon(debruijn, _))
+                    if debruijn.depth < self.binder_depth => {}
+                _ => self.has_late_bound_regions = true,
+            }
+        }
+    }
+
+    fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                        generics: &'tcx hir::Generics,
+                                        decl: &'tcx hir::FnDecl)
+                                        -> bool {
+        let mut visitor = LateBoundRegionsDetector {
+            tcx, binder_depth: 1, has_late_bound_regions: false
+        };
+        for lifetime in &generics.lifetimes {
+            if tcx.named_region_map.late_bound.contains(&lifetime.lifetime.id) {
+                return true;
+            }
+        }
+        visitor.visit_fn_decl(decl);
+        visitor.has_late_bound_regions
+    }
+
+    match node {
+        hir_map::NodeTraitItem(item) => match item.node {
+            hir::TraitItemKind::Method(ref sig, _) =>
+                has_late_bound_regions(tcx, &sig.generics, &sig.decl),
+            _ => false,
+        },
+        hir_map::NodeImplItem(item) => match item.node {
+            hir::ImplItemKind::Method(ref sig, _) =>
+                has_late_bound_regions(tcx, &sig.generics, &sig.decl),
+            _ => false,
+        },
+        hir_map::NodeForeignItem(item) => match item.node {
+            hir::ForeignItemFn(ref fn_decl, _, ref generics) =>
+                has_late_bound_regions(tcx, generics, fn_decl),
+            _ => false,
+        },
+        hir_map::NodeItem(item) => match item.node {
+            hir::ItemFn(ref fn_decl, .., ref generics, _) =>
+                has_late_bound_regions(tcx, generics, fn_decl),
+            _ => false,
+        },
+        _ => false
+    }
+}
+
 fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                          def_id: DefId)
                          -> &'tcx ty::Generics {
@@ -959,7 +1048,8 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         regions: regions,
         types: types,
         type_param_to_index: type_param_to_index,
-        has_self: has_self || parent_has_self
+        has_self: has_self || parent_has_self,
+        has_late_bound_regions: has_late_bound_regions(tcx, node),
     })
 }
 
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 87e59683fd2..1e26a734e76 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -332,92 +332,6 @@ fn main() {
 ```
 "##,
 
-E0035: r##"
-You tried to give a type parameter where it wasn't needed. Erroneous code
-example:
-
-```compile_fail,E0035
-struct Test;
-
-impl Test {
-    fn method(&self) {}
-}
-
-fn main() {
-    let x = Test;
-
-    x.method::<i32>(); // Error: Test::method doesn't need type parameter!
-}
-```
-
-To fix this error, just remove the type parameter:
-
-```
-struct Test;
-
-impl Test {
-    fn method(&self) {}
-}
-
-fn main() {
-    let x = Test;
-
-    x.method(); // OK, we're good!
-}
-```
-"##,
-
-E0036: r##"
-This error occurrs when you pass too many or not enough type parameters to
-a method. Erroneous code example:
-
-```compile_fail,E0036
-struct Test;
-
-impl Test {
-    fn method<T>(&self, v: &[T]) -> usize {
-        v.len()
-    }
-}
-
-fn main() {
-    let x = Test;
-    let v = &[0];
-
-    x.method::<i32, i32>(v); // error: only one type parameter is expected!
-}
-```
-
-To fix it, just specify a correct number of type parameters:
-
-```
-struct Test;
-
-impl Test {
-    fn method<T>(&self, v: &[T]) -> usize {
-        v.len()
-    }
-}
-
-fn main() {
-    let x = Test;
-    let v = &[0];
-
-    x.method::<i32>(v); // OK, we're good!
-}
-```
-
-Please note on the last example that we could have called `method` like this:
-
-```
-# struct Test;
-# impl Test { fn method<T>(&self, v: &[T]) -> usize { v.len() } }
-# let x = Test;
-# let v = &[0];
-x.method(v);
-```
-"##,
-
 E0040: r##"
 It is not allowed to manually call destructors in Rust. It is also not
 necessary to do this since `drop` is called automatically whenever a value goes
@@ -4681,6 +4595,8 @@ error, just declare a function.
 }
 
 register_diagnostics! {
+//  E0035, merged into E0087/E0089
+//  E0036, merged into E0087/E0089
 //  E0068,
 //  E0085,
 //  E0086,
diff --git a/src/test/compile-fail/E0088.rs b/src/test/compile-fail/E0088.rs
index de188677a11..db84a4edc48 100644
--- a/src/test/compile-fail/E0088.rs
+++ b/src/test/compile-fail/E0088.rs
@@ -9,14 +9,9 @@
 // except according to those terms.
 
 fn f() {}
-fn g<'a>() {}
+fn g<'a>() -> &'a u8 { loop {} }
 
 fn main() {
-    f::<'static>();
-    //~^ ERROR expected at most 0 lifetime parameters, found 1 lifetime parameter [E0088]
-    //~| NOTE expected 0 lifetime parameters
-
-    g::<'static, 'static>();
-    //~^ ERROR expected at most 0 lifetime parameters, found 2 lifetime parameters [E0088]
-    //~| NOTE expected 0 lifetime parameters
+    f::<'static>(); //~ ERROR E0088
+    g::<'static, 'static>(); //~ ERROR E0088
 }
diff --git a/src/test/compile-fail/constructor-lifetime-args.rs b/src/test/compile-fail/constructor-lifetime-args.rs
new file mode 100644
index 00000000000..50db9707355
--- /dev/null
+++ b/src/test/compile-fail/constructor-lifetime-args.rs
@@ -0,0 +1,36 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// All lifetime parameters in struct constructors are currently considered early bound,
+// i.e. `S::<ARGS>` is interpreted kinda like an associated item `S::<ARGS>::ctor`.
+// This behavior is a bit weird, because if equivalent constructor were written manually
+// it would get late bound lifetime parameters.
+// Variant constructors behave in the same way, lifetime parameters are considered
+// belonging to the enum and being early bound.
+// https://github.com/rust-lang/rust/issues/30904
+
+struct S<'a, 'b>(&'a u8, &'b u8);
+enum E<'a, 'b> {
+    V(&'a u8),
+    U(&'b u8),
+}
+
+fn main() {
+    S(&0, &0); // OK
+    S::<'static>(&0, &0);
+    //~^ ERROR expected 2 lifetime parameters, found 1 lifetime parameter
+    S::<'static, 'static, 'static>(&0, &0);
+    //~^ ERROR expected at most 2 lifetime parameters, found 3 lifetime parameters
+    E::V(&0); // OK
+    E::V::<'static>(&0);
+    //~^ ERROR expected 2 lifetime parameters, found 1 lifetime parameter
+    E::V::<'static, 'static, 'static>(&0);
+    //~^ ERROR expected at most 2 lifetime parameters, found 3 lifetime parameters
+}
diff --git a/src/test/compile-fail/method-call-lifetime-args-lint.rs b/src/test/compile-fail/method-call-lifetime-args-lint.rs
new file mode 100644
index 00000000000..b2a94e0af42
--- /dev/null
+++ b/src/test/compile-fail/method-call-lifetime-args-lint.rs
@@ -0,0 +1,97 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![deny(late_bound_lifetime_arguments)]
+#![allow(unused)]
+
+struct S;
+
+impl S {
+    fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {}
+    fn late_implicit(self, _: &u8, _: &u8) {}
+    fn late_early<'a, 'b>(self, _: &'a u8) -> &'b u8 { loop {} }
+    fn late_implicit_early<'b>(self, _: &u8) -> &'b u8 { loop {} }
+
+    // 'late lifetimes here belong to nested types not to the tested functions.
+    fn early_tricky_explicit<'a>(_: for<'late> fn(&'late u8),
+                                 _: Box<for<'late> Fn(&'late u8)>)
+                                 -> &'a u8 { loop {} }
+    fn early_tricky_implicit<'a>(_: fn(&u8),
+                                 _: Box<Fn(&u8)>)
+                                 -> &'a u8 { loop {} }
+}
+
+fn method_call() {
+    S.late(&0, &0); // OK
+    S.late::<'static>(&0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+    S.late::<'static, 'static>(&0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+    S.late::<'static, 'static, 'static>(&0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+    S.late_early(&0); // OK
+    S.late_early::<'static>(&0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+    S.late_early::<'static, 'static>(&0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+    S.late_early::<'static, 'static, 'static>(&0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+
+    S.late_implicit(&0, &0); // OK
+    S.late_implicit::<'static>(&0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+    S.late_implicit::<'static, 'static>(&0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+    S.late_implicit::<'static, 'static, 'static>(&0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+    S.late_implicit_early(&0); // OK
+    S.late_implicit_early::<'static>(&0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+    S.late_implicit_early::<'static, 'static>(&0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+    S.late_implicit_early::<'static, 'static, 'static>(&0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+
+    S::early_tricky_explicit::<'static>(loop {}, loop {}); // OK
+    S::early_tricky_implicit::<'static>(loop {}, loop {}); // OK
+}
+
+fn ufcs() {
+    S::late_early::<'static>(S, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+
+    S::late_implicit_early::<'static>(S, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+}
+
+fn lint_not_inference_error() {
+    fn f<'early, 'late, T: 'early>() {}
+
+    // Make sure `u8` is substituted and not replaced with an inference variable
+    f::<'static, u8>;
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    //~| WARN this was previously accepted
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/E0036.rs b/src/test/compile-fail/method-call-lifetime-args-subst-index.rs
index ecb6dac66f2..a9505e4f936 100644
--- a/src/test/compile-fail/E0036.rs
+++ b/src/test/compile-fail/method-call-lifetime-args-subst-index.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,17 +8,18 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-struct Test;
+#![feature(rustc_attrs)]
+#![allow(unused)]
 
-impl Test {
-    fn method<T>(&self, v: &[T]) -> usize {
-        v.len()
-    }
+struct S;
+
+impl S {
+    fn early_and_type<'a, T>(self) -> &'a T { loop {} }
 }
 
-fn main() {
-    let x = Test;
-    let v = &[0];
-    x.method::<i32, i32>(v); //~ ERROR E0036
-                             //~| NOTE Passed 2 type arguments, expected 1
+fn test() {
+    S.early_and_type::<u16>();
 }
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
diff --git a/src/test/compile-fail/E0035.rs b/src/test/compile-fail/method-call-lifetime-args-unresolved.rs
index 9322d21d2a8..4910bfaf4f6 100644
--- a/src/test/compile-fail/E0035.rs
+++ b/src/test/compile-fail/method-call-lifetime-args-unresolved.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,14 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-struct Test;
-
-impl Test {
-    fn method(&self) {}
-}
-
 fn main() {
-    let x = Test;
-    x.method::<i32>(); //~ ERROR E0035
-                       //~| NOTE called with unneeded type parameters
+    0.clone::<'a>(); //~ ERROR use of undeclared lifetime name `'a`
 }
diff --git a/src/test/compile-fail/method-call-lifetime-args.rs b/src/test/compile-fail/method-call-lifetime-args.rs
new file mode 100644
index 00000000000..f0a87c74703
--- /dev/null
+++ b/src/test/compile-fail/method-call-lifetime-args.rs
@@ -0,0 +1,82 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct S;
+
+impl S {
+    fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {}
+    fn late_implicit(self, _: &u8, _: &u8) {}
+    fn early<'a, 'b>(self) -> (&'a u8, &'b u8) { loop {} }
+    fn late_early<'a, 'b>(self, _: &'a u8) -> &'b u8 { loop {} }
+    fn late_implicit_early<'b>(self, _: &u8) -> &'b u8 { loop {} }
+    fn late_implicit_self_early<'b>(&self) -> &'b u8 { loop {} }
+    fn late_unused_early<'a, 'b>(self) -> &'b u8 { loop {} }
+    fn life_and_type<'a, T>(self) -> &'a T { loop {} }
+}
+
+fn method_call() {
+    S.early(); // OK
+    S.early::<'static>();
+    //~^ ERROR expected 2 lifetime parameters, found 1 lifetime parameter
+    S.early::<'static, 'static, 'static>();
+    //~^ ERROR expected at most 2 lifetime parameters, found 3 lifetime parameters
+    let _: &u8 = S.life_and_type::<'static>();
+    S.life_and_type::<u8>();
+    S.life_and_type::<'static, u8>();
+}
+
+fn ufcs() {
+    S::late(S, &0, &0); // OK
+    S::late::<'static>(S, &0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late::<'static, 'static>(S, &0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late::<'static, 'static, 'static>(S, &0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late_early(S, &0); // OK
+    S::late_early::<'static, 'static>(S, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late_early::<'static, 'static, 'static>(S, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+
+    S::late_implicit(S, &0, &0); // OK
+    S::late_implicit::<'static>(S, &0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late_implicit::<'static, 'static>(S, &0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late_implicit::<'static, 'static, 'static>(S, &0, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late_implicit_early(S, &0); // OK
+    S::late_implicit_early::<'static, 'static>(S, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late_implicit_early::<'static, 'static, 'static>(S, &0);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late_implicit_self_early(&S); // OK
+    S::late_implicit_self_early::<'static, 'static>(&S);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late_implicit_self_early::<'static, 'static, 'static>(&S);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late_unused_early(S); // OK
+    S::late_unused_early::<'static, 'static>(S);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+    S::late_unused_early::<'static, 'static, 'static>(S);
+    //~^ ERROR cannot specify lifetime arguments explicitly
+
+    S::early(S); // OK
+    S::early::<'static>(S);
+    //~^ ERROR expected 2 lifetime parameters, found 1 lifetime parameter
+    S::early::<'static, 'static, 'static>(S);
+    //~^ ERROR expected at most 2 lifetime parameters, found 3 lifetime parameters
+    let _: &u8 = S::life_and_type::<'static>(S);
+    S::life_and_type::<u8>(S);
+    S::life_and_type::<'static, u8>(S);
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/method-call-type-binding.rs b/src/test/compile-fail/method-call-type-binding.rs
index acffb06ebec..3ae878ed1cb 100644
--- a/src/test/compile-fail/method-call-type-binding.rs
+++ b/src/test/compile-fail/method-call-type-binding.rs
@@ -9,5 +9,5 @@
 // except according to those terms.
 
 fn main() {
-    0.clone::<T = u8>(); //~ ERROR type bindings cannot be used in method calls
+    0.clone::<T = u8>(); //~ ERROR unexpected binding of associated item
 }
diff --git a/src/test/compile-fail/trait-test-2.rs b/src/test/compile-fail/trait-test-2.rs
index 2d4df77f960..b08aab6da85 100644
--- a/src/test/compile-fail/trait-test-2.rs
+++ b/src/test/compile-fail/trait-test-2.rs
@@ -15,9 +15,8 @@ impl bar for i32 { fn dup(&self) -> i32 { *self } fn blah<X>(&self) {} }
 impl bar for u32 { fn dup(&self) -> u32 { *self } fn blah<X>(&self) {} }
 
 fn main() {
-    10.dup::<i32>(); //~ ERROR does not take type parameters
-    10.blah::<i32, i32>();
-    //~^ ERROR incorrect number of type parameters given for this method: expected 1, found 2
+    10.dup::<i32>(); //~ ERROR expected at most 0 type parameters, found 1 type parameter
+    10.blah::<i32, i32>(); //~ ERROR expected at most 1 type parameter, found 2 type parameters
     (box 10 as Box<bar>).dup();
     //~^ ERROR E0038
     //~| ERROR E0038
diff --git a/src/test/incremental/hashes/inherent_impls.rs b/src/test/incremental/hashes/inherent_impls.rs
index 899aefa24a0..daddc0c9f54 100644
--- a/src/test/incremental/hashes/inherent_impls.rs
+++ b/src/test/incremental/hashes/inherent_impls.rs
@@ -369,7 +369,7 @@ impl Foo {
 impl Foo {
     #[rustc_dirty(label="Hir", cfg="cfail2")]
     #[rustc_clean(label="Hir", cfg="cfail3")]
-    #[rustc_metadata_clean(cfg="cfail2")] // Apparently unused lifetimes don't show up in the type.
+    #[rustc_metadata_clean(cfg="cfail2")]
     #[rustc_metadata_clean(cfg="cfail3")]
     pub fn add_lifetime_parameter_to_method<'a>(&self) { }
 }
diff --git a/src/test/incremental/hashes/trait_defs.rs b/src/test/incremental/hashes/trait_defs.rs
index e47556a790b..44950ee8a60 100644
--- a/src/test/incremental/hashes/trait_defs.rs
+++ b/src/test/incremental/hashes/trait_defs.rs
@@ -448,7 +448,7 @@ trait TraitAddLifetimeParameterToMethod {
 trait TraitAddLifetimeParameterToMethod {
     #[rustc_dirty(label="Hir", cfg="cfail2")]
     #[rustc_clean(label="Hir", cfg="cfail3")]
-    #[rustc_metadata_clean(cfg="cfail2")] // Unused lifetimes don't seem to show up in type?
+    #[rustc_metadata_dirty(cfg="cfail2")]
     #[rustc_metadata_clean(cfg="cfail3")]
     fn method<'a>();
 }