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> 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> 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..3e71ac539a3 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -204,6 +204,12 @@ declare_lint! { "detects parenthesized generic parameters in type and module names" } +declare_lint! { + pub LATE_BOUND_LIFETIME_ARGUMENTS, + Deny, + "detects generic lifetime arguments in path segments with late bound lifetime parameters" +} + declare_lint! { pub DEPRECATED, Warn, @@ -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, 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 ", - } + }, + FutureIncompatibleInfo { + id: LintId::of(LATE_BOUND_LIFETIME_ARGUMENTS), + reference: "issue #42868 ", + }, ]); // Register renamed and removed lints diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index a647a8bf6f8..c28ddf876b3 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -288,7 +288,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { // variables. let method_generics = self.tcx.generics_of(pick.item.def_id); let mut fn_segment = Some((segment, method_generics)); - self.fcx.check_path_parameter_count(self.span, &mut fn_segment); + 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. diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index cdbe5e14e90..917bffbc22f 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"); + } 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")); + } + *segment = None; + 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(&self, sp: Span, ty: Ty<'tcx>, f: F) diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 002a148c459..25707229dbc 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -876,11 +876,13 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let has_self = opt_self.is_some(); let mut parent_has_self = false; + let mut parent_has_late_bound_regions = false; let mut own_start = has_self as u32; let (parent_regions, parent_types) = parent_def_id.map_or((0, 0), |def_id| { let generics = tcx.generics_of(def_id); assert_eq!(has_self, false); parent_has_self = generics.has_self; + parent_has_late_bound_regions = generics.has_late_bound_regions; own_start = generics.count() as u32; (generics.parent_regions + generics.regions.len() as u32, generics.parent_types + generics.types.len() as u32) @@ -898,6 +900,7 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } }).collect::>(); + let has_late_bound_regions = regions.len() != ast_generics.lifetimes.len(); let object_lifetime_defaults = tcx.named_region_map.object_lifetime_defaults.get(&node_id); @@ -959,7 +962,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 || parent_has_late_bound_regions, }) } 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/method-call-lifetime-args-lint.rs b/src/test/compile-fail/method-call-lifetime-args-lint.rs new file mode 100644 index 00000000000..28f1035cad7 --- /dev/null +++ b/src/test/compile-fail/method-call-lifetime-args-lint.rs @@ -0,0 +1,76 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![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 {} } +} + +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); + //FIXME ERROR cannot specify lifetime arguments explicitly + //FIXME WARN this was previously accepted + // S.late_implicit::<'static, 'static>(&0, &0); + //FIXME ERROR cannot specify lifetime arguments explicitly + //FIXME WARN this was previously accepted + // S.late_implicit::<'static, 'static, 'static>(&0, &0); + //FIXME ERROR cannot specify lifetime arguments explicitly + //FIXME WARN this was previously accepted + S.late_implicit_early(&0); // OK + S.late_implicit_early::<'static>(&0); + //FIXME ERROR cannot specify lifetime arguments explicitly + //FIXME WARN this was previously accepted + // S.late_implicit_early::<'static, 'static>(&0); + //FIXME ERROR cannot specify lifetime arguments explicitly + //FIXME WARN this was previously accepted + // S.late_implicit_early::<'static, 'static, 'static>(&0); + //FIXME ERROR cannot specify lifetime arguments explicitly + //FIXME WARN this was previously accepted +} + +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); + //FIXME ERROR cannot specify lifetime arguments explicitly + //FIXME WARN this was previously accepted +} + +fn main() {} diff --git a/src/test/compile-fail/method-call-lifetime-args.rs b/src/test/compile-fail/method-call-lifetime-args.rs index 75c469ecd32..00c03c3c859 100644 --- a/src/test/compile-fail/method-call-lifetime-args.rs +++ b/src/test/compile-fail/method-call-lifetime-args.rs @@ -12,16 +12,14 @@ 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 life_and_type<'a, T>(&self) -> &'a T { loop {} } + fn late_early<'a, 'b>(self, _: &'a u8) -> &'b u8 { loop {} } + fn late_implicit_early<'b>(self, _: &u8) -> &'b u8 { loop {} } + fn life_and_type<'a, T>(self) -> &'a T { loop {} } } -fn main() { - S.late(&0, &0); // OK - S.late::<'static>(&0, &0); - //~^ ERROR expected at most 0 lifetime parameters, found 1 lifetime parameter - S.late::<'static, 'static, 'static>(&0, &0); - //~^ ERROR expected at most 0 lifetime parameters, found 3 lifetime parameter +fn method_call() { S.early(); // OK S.early::<'static>(); //~^ ERROR expected 2 lifetime parameters, found 1 lifetime parameter @@ -31,3 +29,47 @@ fn main() { S.life_and_type::(); 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 expected at most 0 lifetime parameters, found 1 lifetime parameter + //FIXME ERROR cannot specify lifetime arguments explicitly + S::late_implicit::<'static, 'static>(S, &0, &0); + //~^ ERROR expected at most 0 lifetime parameters, found 2 lifetime parameters + //FIXME ERROR cannot specify lifetime arguments explicitly + S::late_implicit::<'static, 'static, 'static>(S, &0, &0); + //~^ ERROR expected at most 0 lifetime parameters, found 3 lifetime parameters + //FIXME ERROR cannot specify lifetime arguments explicitly + S::late_implicit_early(S, &0); // OK + S::late_implicit_early::<'static, 'static>(S, &0); + //~^ ERROR expected at most 1 lifetime parameter, found 2 lifetime parameters + //FIXME ERROR cannot specify lifetime arguments explicitly + S::late_implicit_early::<'static, 'static, 'static>(S, &0); + //~^ ERROR expected at most 1 lifetime parameter, found 3 lifetime parameters + //FIXME 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::(S); + S::life_and_type::<'static, u8>(S); +} + +fn main() {} diff --git a/src/test/incremental/hashes/inherent_impls.rs b/src/test/incremental/hashes/inherent_impls.rs index 899aefa24a0..370e07ba6c9 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_dirty(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>(); }