From 9967e9e3e974872a311399a913e6a2d57a16b697 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 7 Jun 2017 00:17:14 +0300 Subject: [PATCH 1/5] Support generic lifetime arguments in method calls --- src/librustc_passes/ast_validation.rs | 15 +--- src/librustc_typeck/check/method/confirm.rs | 46 +++------- src/librustc_typeck/diagnostics.rs | 88 +------------------ src/test/compile-fail/E0036.rs | 24 ----- ...> method-call-lifetime-args-unresolved.rs} | 12 +-- .../compile-fail/method-call-lifetime-args.rs | 33 +++++++ .../compile-fail/method-call-type-binding.rs | 2 +- src/test/compile-fail/trait-test-2.rs | 5 +- 8 files changed, 53 insertions(+), 172 deletions(-) delete mode 100644 src/test/compile-fail/E0036.rs rename src/test/compile-fail/{E0035.rs => method-call-lifetime-args-unresolved.rs} (63%) create mode 100644 src/test/compile-fail/method-call-lifetime-args.rs diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 72c7b92fe6e30..99a49dbd7d732 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 0829951e12deb..a647a8bf6f86c 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; @@ -282,52 +283,25 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { segment: &hir::PathSegment, 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); // 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), + }; Substs::for_item(self.tcx, pick.item.def_id, |def, _| { let i = def.index as usize; if i < substs.len() { substs.region_at(i) + } else if let Some(lifetime) = supplied_lifetimes.get(i - substs.len()) { + AstConv::ast_region_to_region(self.fcx, lifetime, Some(def)) } else { self.region_var_for_def(self.span, def) } @@ -335,7 +309,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { 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) { + } else if let Some(ast_ty) = supplied_types.get(i - substs.len()) { self.to_ty(ast_ty) } else { self.type_var_for_def(self.span, def, cur_substs) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 87e59683fd2a8..1e26a734e7640 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::(); // 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(&self, v: &[T]) -> usize { - v.len() - } -} - -fn main() { - let x = Test; - let v = &[0]; - - x.method::(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(&self, v: &[T]) -> usize { - v.len() - } -} - -fn main() { - let x = Test; - let v = &[0]; - - x.method::(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(&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/E0036.rs b/src/test/compile-fail/E0036.rs deleted file mode 100644 index ecb6dac66f218..0000000000000 --- a/src/test/compile-fail/E0036.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2016 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. - -struct Test; - -impl Test { - fn method(&self, v: &[T]) -> usize { - v.len() - } -} - -fn main() { - let x = Test; - let v = &[0]; - x.method::(v); //~ ERROR E0036 - //~| NOTE Passed 2 type arguments, expected 1 -} diff --git a/src/test/compile-fail/E0035.rs b/src/test/compile-fail/method-call-lifetime-args-unresolved.rs similarity index 63% rename from src/test/compile-fail/E0035.rs rename to src/test/compile-fail/method-call-lifetime-args-unresolved.rs index 9322d21d2a88d..4910bfaf4f60d 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::(); //~ 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 0000000000000..75c469ecd3268 --- /dev/null +++ b/src/test/compile-fail/method-call-lifetime-args.rs @@ -0,0 +1,33 @@ +// 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. + +struct S; + +impl S { + fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} + fn early<'a, 'b>(self) -> (&'a 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 + 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::(); + S.life_and_type::<'static, u8>(); +} diff --git a/src/test/compile-fail/method-call-type-binding.rs b/src/test/compile-fail/method-call-type-binding.rs index acffb06ebecf2..3ae878ed1cbc0 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::(); //~ ERROR type bindings cannot be used in method calls + 0.clone::(); //~ 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 2d4df77f96045..b08aab6da852a 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(&self) {} } impl bar for u32 { fn dup(&self) -> u32 { *self } fn blah(&self) {} } fn main() { - 10.dup::(); //~ ERROR does not take type parameters - 10.blah::(); - //~^ ERROR incorrect number of type parameters given for this method: expected 1, found 2 + 10.dup::(); //~ ERROR expected at most 0 type parameters, found 1 type parameter + 10.blah::(); //~ ERROR expected at most 1 type parameter, found 2 type parameters (box 10 as Box).dup(); //~^ ERROR E0038 //~| ERROR E0038 From 7ca378b251329755ca94fa70221c4aa7f7af06ac Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 24 Jun 2017 00:56:25 +0300 Subject: [PATCH 2/5] Prohibit lifetime arguments in path segments with late bound lifetime parameters --- src/librustc/ich/impls_ty.rs | 2 + src/librustc/lint/builtin.rs | 7 ++ src/librustc/ty/mod.rs | 1 + src/librustc_lint/lib.rs | 6 +- src/librustc_typeck/check/method/confirm.rs | 2 +- src/librustc_typeck/check/mod.rs | 81 ++++++++++++------- src/librustc_typeck/collect.rs | 6 +- src/test/compile-fail/E0088.rs | 11 +-- .../method-call-lifetime-args-lint.rs | 76 +++++++++++++++++ .../compile-fail/method-call-lifetime-args.rs | 56 +++++++++++-- src/test/incremental/hashes/inherent_impls.rs | 2 +- src/test/incremental/hashes/trait_defs.rs | 2 +- 12 files changed, 202 insertions(+), 50 deletions(-) create mode 100644 src/test/compile-fail/method-call-lifetime-args-lint.rs diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index a1dd2caf786c2..3e227872848ef 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 2d088c4f6d172..3e71ac539a341 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 5aaba526e265f..1fee0dd98634a 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 a03f12c3dfbca..21dca7f6c61c4 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 a647a8bf6f86c..c28ddf876b3cd 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 cdbe5e14e9094..917bffbc22f00 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 002a148c459a3..25707229dbc5a 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 de188677a1121..db84a4edc487c 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 0000000000000..28f1035cad7e0 --- /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 75c469ecd3268..00c03c3c8599a 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 899aefa24a033..370e07ba6c9e9 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 e47556a790b62..44950ee8a601f 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>(); } From e40cedb3932060abf4496254959e9dd307eb6a2f Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 24 Jun 2017 23:30:05 +0300 Subject: [PATCH 3/5] Detect implicitly defined late bound lifetime parameters as well --- src/librustc_typeck/collect.rs | 91 ++++++++++++++++++- .../compile-fail/constructor-lifetime-args.rs | 36 ++++++++ .../method-call-lifetime-args-lint.rs | 38 ++++---- .../compile-fail/method-call-lifetime-args.rs | 38 ++++++-- src/test/incremental/hashes/inherent_impls.rs | 2 +- 5 files changed, 171 insertions(+), 34 deletions(-) create mode 100644 src/test/compile-fail/constructor-lifetime-args.rs diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 25707229dbc5a..32ccfc511fc4b 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -772,6 +772,92 @@ 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: usize, + 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(<.id).cloned() { + Some(rl::Region::Static) | Some(rl::Region::EarlyBound(..)) => {} + _ => 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: 0, 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 { @@ -876,13 +962,11 @@ 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) @@ -900,7 +984,6 @@ 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); @@ -963,7 +1046,7 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, types: types, type_param_to_index: type_param_to_index, has_self: has_self || parent_has_self, - has_late_bound_regions: has_late_bound_regions || parent_has_late_bound_regions, + has_late_bound_regions: has_late_bound_regions(tcx, node), }) } 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 0000000000000..50db9707355f4 --- /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 or the MIT license +// , 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::` is interpreted kinda like an associated item `S::::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 index 28f1035cad7e0..9bf34de92fe8d 100644 --- a/src/test/compile-fail/method-call-lifetime-args-lint.rs +++ b/src/test/compile-fail/method-call-lifetime-args-lint.rs @@ -42,25 +42,25 @@ fn method_call() { //~| 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::<'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); - //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 + //~^ 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 } fn ufcs() { @@ -69,8 +69,8 @@ fn ufcs() { //~| 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 + //~^ ERROR cannot specify lifetime arguments explicitly + //~| 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 00c03c3c8599a..c29701804a741 100644 --- a/src/test/compile-fail/method-call-lifetime-args.rs +++ b/src/test/compile-fail/method-call-lifetime-args.rs @@ -16,7 +16,17 @@ impl S { 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 {} } + + // 'late lifetimes here belong to nested types not to the tested functions. + fn early_tricky_explicit<'a>(_: for<'late> fn(&'late u8), + _: Box Fn(&'late u8)>) + -> &'a u8 { loop {} } + fn early_tricky_implicit<'a>(_: fn(&u8), + _: Box) + -> &'a u8 { loop {} } } fn method_call() { @@ -46,21 +56,26 @@ fn ufcs() { 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 + //~^ 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 + //~^ 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 + //~^ 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 + //~^ 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 + //~^ 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); @@ -70,6 +85,9 @@ fn ufcs() { let _: &u8 = S::life_and_type::<'static>(S); S::life_and_type::(S); S::life_and_type::<'static, u8>(S); + + S::early_tricky_explicit::<'static>(loop {}, loop {}); // OK + S::early_tricky_implicit::<'static>(loop {}, loop {}); // OK } fn main() {} diff --git a/src/test/incremental/hashes/inherent_impls.rs b/src/test/incremental/hashes/inherent_impls.rs index 370e07ba6c9e9..daddc0c9f5459 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_dirty(cfg="cfail2")] + #[rustc_metadata_clean(cfg="cfail2")] #[rustc_metadata_clean(cfg="cfail3")] pub fn add_lifetime_parameter_to_method<'a>(&self) { } } From 46f427bee9ac60d4ce31baa95430223d5ec110b3 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 11 Jul 2017 23:12:06 +0300 Subject: [PATCH 4/5] Fix incorrect subst index Fix treatment of lifetimes defined in nested types during detection of late bound regions in signatures. Do not replace substs with inference variables when "cannot specify lifetime arguments explicitly..." is reported as a lint. --- src/librustc_typeck/check/method/confirm.rs | 17 +++++++------ src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/collect.rs | 9 ++++--- .../method-call-lifetime-args-lint.rs | 20 +++++++++++++++ .../method-call-lifetime-args-subst-index.rs | 25 +++++++++++++++++++ .../compile-fail/method-call-lifetime-args.rs | 11 -------- 6 files changed, 62 insertions(+), 22 deletions(-) create mode 100644 src/test/compile-fail/method-call-lifetime-args-subst-index.rs diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index c28ddf876b3cd..ad4ee5a9d6dcf 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -281,7 +281,7 @@ 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> { // Determine the values for the generic parameters of the method. // If they were not explicitly supplied, just construct fresh @@ -296,20 +296,23 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { 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) - } else if let Some(lifetime) = supplied_lifetimes.get(i - substs.len()) { + 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_types.get(i - substs.len()) { + 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 917bffbc22f00..af11cacb247b6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4697,13 +4697,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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")); } - *segment = None; return; } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 32ccfc511fc4b..72bd084330dd3 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -777,7 +777,7 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, -> bool { struct LateBoundRegionsDetector<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, - binder_depth: usize, + binder_depth: u32, has_late_bound_regions: bool, } @@ -812,7 +812,10 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, match self.tcx.named_region_map.defs.get(<.id).cloned() { Some(rl::Region::Static) | Some(rl::Region::EarlyBound(..)) => {} - _ => self.has_late_bound_regions = true + Some(rl::Region::LateBound(debruijn, _)) | + Some(rl::Region::LateBoundAnon(debruijn, _)) + if debruijn.depth < self.binder_depth => {} + _ => self.has_late_bound_regions = true, } } } @@ -822,7 +825,7 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, decl: &'tcx hir::FnDecl) -> bool { let mut visitor = LateBoundRegionsDetector { - tcx, binder_depth: 0, has_late_bound_regions: false + 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) { diff --git a/src/test/compile-fail/method-call-lifetime-args-lint.rs b/src/test/compile-fail/method-call-lifetime-args-lint.rs index 9bf34de92fe8d..b206924e538f3 100644 --- a/src/test/compile-fail/method-call-lifetime-args-lint.rs +++ b/src/test/compile-fail/method-call-lifetime-args-lint.rs @@ -17,6 +17,14 @@ impl S { 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 Fn(&'late u8)>) + -> &'a u8 { loop {} } + fn early_tricky_implicit<'a>(_: fn(&u8), + _: Box) + -> &'a u8 { loop {} } } fn method_call() { @@ -61,6 +69,9 @@ fn method_call() { 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() { @@ -73,4 +84,13 @@ fn ufcs() { //~| 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/method-call-lifetime-args-subst-index.rs b/src/test/compile-fail/method-call-lifetime-args-subst-index.rs new file mode 100644 index 0000000000000..a9505e4f936a1 --- /dev/null +++ b/src/test/compile-fail/method-call-lifetime-args-subst-index.rs @@ -0,0 +1,25 @@ +// 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. + +#![feature(rustc_attrs)] +#![allow(unused)] + +struct S; + +impl S { + fn early_and_type<'a, T>(self) -> &'a T { loop {} } +} + +fn test() { + S.early_and_type::(); +} + +#[rustc_error] +fn main() {} //~ ERROR compilation successful diff --git a/src/test/compile-fail/method-call-lifetime-args.rs b/src/test/compile-fail/method-call-lifetime-args.rs index c29701804a741..f0a87c7470386 100644 --- a/src/test/compile-fail/method-call-lifetime-args.rs +++ b/src/test/compile-fail/method-call-lifetime-args.rs @@ -19,14 +19,6 @@ impl S { 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 {} } - - // 'late lifetimes here belong to nested types not to the tested functions. - fn early_tricky_explicit<'a>(_: for<'late> fn(&'late u8), - _: Box Fn(&'late u8)>) - -> &'a u8 { loop {} } - fn early_tricky_implicit<'a>(_: fn(&u8), - _: Box) - -> &'a u8 { loop {} } } fn method_call() { @@ -85,9 +77,6 @@ fn ufcs() { let _: &u8 = S::life_and_type::<'static>(S); S::life_and_type::(S); S::life_and_type::<'static, u8>(S); - - S::early_tricky_explicit::<'static>(loop {}, loop {}); // OK - S::early_tricky_implicit::<'static>(loop {}, loop {}); // OK } fn main() {} From 39114f916905b16b414f51031426309b63f856a9 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 18 Jul 2017 00:33:44 +0300 Subject: [PATCH 5/5] Make `late_bound_lifetime_arguments` lint warn-by-default --- src/librustc/lint/builtin.rs | 2 +- src/test/compile-fail/method-call-lifetime-args-lint.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 3e71ac539a341..cbe642a9a76a6 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -206,7 +206,7 @@ declare_lint! { declare_lint! { pub LATE_BOUND_LIFETIME_ARGUMENTS, - Deny, + Warn, "detects generic lifetime arguments in path segments with late bound 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 index b206924e538f3..b2a94e0af420d 100644 --- a/src/test/compile-fail/method-call-lifetime-args-lint.rs +++ b/src/test/compile-fail/method-call-lifetime-args-lint.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![deny(late_bound_lifetime_arguments)] #![allow(unused)] struct S;