Skip to content

Commit 83c659e

Browse files
committed
Auto merge of #42492 - petrochenkov:methlife, r=nikomatsakis
Support generic lifetime arguments in method calls Fixes #42403 Fixes #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
2 parents 2e63340 + 39114f9 commit 83c659e

19 files changed

+417
-206
lines changed

src/librustc/ich/impls_ty.rs

+2
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for ty::Ge
346346
// `def_id.index` (`def_id.krate` is the same as the item's).
347347
type_param_to_index: _, // Don't hash this
348348
has_self,
349+
has_late_bound_regions,
349350
} = *self;
350351

351352
parent.hash_stable(hcx, hasher);
@@ -354,6 +355,7 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for ty::Ge
354355
regions.hash_stable(hcx, hasher);
355356
types.hash_stable(hcx, hasher);
356357
has_self.hash_stable(hcx, hasher);
358+
has_late_bound_regions.hash_stable(hcx, hasher);
357359
}
358360
}
359361

src/librustc/lint/builtin.rs

+7
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@ declare_lint! {
204204
"detects parenthesized generic parameters in type and module names"
205205
}
206206

207+
declare_lint! {
208+
pub LATE_BOUND_LIFETIME_ARGUMENTS,
209+
Warn,
210+
"detects generic lifetime arguments in path segments with late bound lifetime parameters"
211+
}
212+
207213
declare_lint! {
208214
pub DEPRECATED,
209215
Warn,
@@ -249,6 +255,7 @@ impl LintPass for HardwiredLints {
249255
LEGACY_CONSTRUCTOR_VISIBILITY,
250256
MISSING_FRAGMENT_SPECIFIER,
251257
PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES,
258+
LATE_BOUND_LIFETIME_ARGUMENTS,
252259
DEPRECATED
253260
)
254261
}

src/librustc/ty/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,7 @@ pub struct Generics {
719719
pub type_param_to_index: BTreeMap<DefIndex, u32>,
720720

721721
pub has_self: bool,
722+
pub has_late_bound_regions: bool,
722723
}
723724

724725
impl Generics {

src/librustc_lint/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
235235
FutureIncompatibleInfo {
236236
id: LintId::of(PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES),
237237
reference: "issue #42238 <https://github.com/rust-lang/rust/issues/42238>",
238-
}
238+
},
239+
FutureIncompatibleInfo {
240+
id: LintId::of(LATE_BOUND_LIFETIME_ARGUMENTS),
241+
reference: "issue #42868 <https://github.com/rust-lang/rust/issues/42868>",
242+
},
239243
]);
240244

241245
// Register renamed and removed lints

src/librustc_passes/ast_validation.rs

+3-12
Original file line numberDiff line numberDiff line change
@@ -127,18 +127,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
127127
}
128128
ExprKind::MethodCall(ref segment, ..) => {
129129
if let Some(ref params) = segment.parameters {
130-
match **params {
131-
PathParameters::AngleBracketed(ref param_data) => {
132-
if !param_data.bindings.is_empty() {
133-
let binding_span = param_data.bindings[0].span;
134-
self.err_handler().span_err(binding_span,
135-
"type bindings cannot be used in method calls");
136-
}
137-
}
138-
PathParameters::Parenthesized(..) => {
139-
self.err_handler().span_err(expr.span,
140-
"parenthesized parameters cannot be used on method calls");
141-
}
130+
if let PathParameters::Parenthesized(..) = **params {
131+
self.err_handler().span_err(expr.span,
132+
"parenthesized parameters cannot be used on method calls");
142133
}
143134
}
144135
}

src/librustc_typeck/check/method/confirm.rs

+18-41
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use super::{probe, MethodCallee};
1212

13+
use astconv::AstConv;
1314
use check::{FnCtxt, LvalueOp, callee};
1415
use hir::def_id::DefId;
1516
use rustc::ty::subst::Substs;
@@ -280,62 +281,38 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
280281
fn instantiate_method_substs(&mut self,
281282
pick: &probe::Pick<'tcx>,
282283
segment: &hir::PathSegment,
283-
substs: &Substs<'tcx>)
284+
parent_substs: &Substs<'tcx>)
284285
-> &'tcx Substs<'tcx> {
285-
let supplied_method_types = match segment.parameters {
286-
hir::AngleBracketedParameters(ref data) => &data.types,
287-
_ => bug!("unexpected generic arguments: {:?}", segment.parameters),
288-
};
289-
290286
// Determine the values for the generic parameters of the method.
291287
// If they were not explicitly supplied, just construct fresh
292288
// variables.
293-
let num_supplied_types = supplied_method_types.len();
294289
let method_generics = self.tcx.generics_of(pick.item.def_id);
295-
let num_method_types = method_generics.types.len();
296-
297-
if num_supplied_types > 0 && num_supplied_types != num_method_types {
298-
if num_method_types == 0 {
299-
struct_span_err!(self.tcx.sess,
300-
self.span,
301-
E0035,
302-
"does not take type parameters")
303-
.span_label(self.span, "called with unneeded type parameters")
304-
.emit();
305-
} else {
306-
struct_span_err!(self.tcx.sess,
307-
self.span,
308-
E0036,
309-
"incorrect number of type parameters given for this method: \
310-
expected {}, found {}",
311-
num_method_types,
312-
num_supplied_types)
313-
.span_label(self.span,
314-
format!("Passed {} type argument{}, expected {}",
315-
num_supplied_types,
316-
if num_supplied_types != 1 { "s" } else { "" },
317-
num_method_types))
318-
.emit();
319-
}
320-
}
290+
let mut fn_segment = Some((segment, method_generics));
291+
self.fcx.check_path_parameter_count(self.span, &mut fn_segment, true);
321292

322293
// Create subst for early-bound lifetime parameters, combining
323294
// parameters from the type and those from the method.
324-
//
325-
// FIXME -- permit users to manually specify lifetimes
326-
let supplied_start = substs.len() + method_generics.regions.len();
295+
let (supplied_types, supplied_lifetimes) = match segment.parameters {
296+
hir::AngleBracketedParameters(ref data) => (&data.types, &data.lifetimes),
297+
_ => bug!("unexpected generic arguments: {:?}", segment.parameters),
298+
};
299+
assert_eq!(method_generics.parent_count(), parent_substs.len());
327300
Substs::for_item(self.tcx, pick.item.def_id, |def, _| {
328301
let i = def.index as usize;
329-
if i < substs.len() {
330-
substs.region_at(i)
302+
if i < parent_substs.len() {
303+
parent_substs.region_at(i)
304+
} else if let Some(lifetime) =
305+
supplied_lifetimes.get(i - parent_substs.len()) {
306+
AstConv::ast_region_to_region(self.fcx, lifetime, Some(def))
331307
} else {
332308
self.region_var_for_def(self.span, def)
333309
}
334310
}, |def, cur_substs| {
335311
let i = def.index as usize;
336-
if i < substs.len() {
337-
substs.type_at(i)
338-
} else if let Some(ast_ty) = supplied_method_types.get(i - supplied_start) {
312+
if i < parent_substs.len() {
313+
parent_substs.type_at(i)
314+
} else if let Some(ast_ty) =
315+
supplied_types.get(i - parent_substs.len() - method_generics.regions.len()) {
339316
self.to_ty(ast_ty)
340317
} else {
341318
self.type_var_for_def(self.span, def, cur_substs)

src/librustc_typeck/check/mod.rs

+51-30
Original file line numberDiff line numberDiff line change
@@ -4491,8 +4491,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
44914491
// variables. If the user provided some types, we may still need
44924492
// to add defaults. If the user provided *too many* types, that's
44934493
// a problem.
4494-
self.check_path_parameter_count(span, &mut type_segment);
4495-
self.check_path_parameter_count(span, &mut fn_segment);
4494+
self.check_path_parameter_count(span, &mut type_segment, false);
4495+
self.check_path_parameter_count(span, &mut fn_segment, false);
44964496

44974497
let (fn_start, has_self) = match (type_segment, fn_segment) {
44984498
(_, Some((_, generics))) => {
@@ -4618,7 +4618,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
46184618
/// Report errors if the provided parameters are too few or too many.
46194619
fn check_path_parameter_count(&self,
46204620
span: Span,
4621-
segment: &mut Option<(&hir::PathSegment, &ty::Generics)>) {
4621+
segment: &mut Option<(&hir::PathSegment, &ty::Generics)>,
4622+
is_method_call: bool) {
46224623
let (lifetimes, types, infer_types, bindings) = {
46234624
match segment.map(|(s, _)| &s.parameters) {
46244625
Some(&hir::AngleBracketedParameters(ref data)) => {
@@ -4632,6 +4633,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
46324633
None => (&[][..], &[][..], true, &[][..])
46334634
}
46344635
};
4636+
let infer_lifetimes = lifetimes.len() == 0;
46354637

46364638
let count_lifetime_params = |n| {
46374639
format!("{} lifetime parameter{}", n, if n == 1 { "" } else { "s" })
@@ -4640,32 +4642,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
46404642
format!("{} type parameter{}", n, if n == 1 { "" } else { "s" })
46414643
};
46424644

4643-
// Check provided lifetime parameters.
4644-
let lifetime_defs = segment.map_or(&[][..], |(_, generics)| &generics.regions);
4645-
if lifetimes.len() > lifetime_defs.len() {
4646-
let expected_text = count_lifetime_params(lifetime_defs.len());
4647-
let actual_text = count_lifetime_params(lifetimes.len());
4648-
struct_span_err!(self.tcx.sess, span, E0088,
4649-
"too many lifetime parameters provided: \
4650-
expected at most {}, found {}",
4651-
expected_text, actual_text)
4652-
.span_label(span, format!("expected {}", expected_text))
4653-
.emit();
4654-
} else if lifetimes.len() > 0 && lifetimes.len() < lifetime_defs.len() {
4655-
let expected_text = count_lifetime_params(lifetime_defs.len());
4656-
let actual_text = count_lifetime_params(lifetimes.len());
4657-
struct_span_err!(self.tcx.sess, span, E0090,
4658-
"too few lifetime parameters provided: \
4659-
expected {}, found {}",
4660-
expected_text, actual_text)
4661-
.span_label(span, format!("expected {}", expected_text))
4662-
.emit();
4663-
}
4664-
4665-
// The case where there is not enough lifetime parameters is not checked,
4666-
// because this is not possible - a function never takes lifetime parameters.
4667-
// See discussion for Pull Request 36208.
4668-
46694645
// Check provided type parameters.
46704646
let type_defs = segment.map_or(&[][..], |(_, generics)| {
46714647
if generics.parent.is_none() {
@@ -4690,7 +4666,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
46904666
// type parameters, we force instantiate_value_path to
46914667
// use inference variables instead of the provided types.
46924668
*segment = None;
4693-
} else if !infer_types && types.len() < required_len {
4669+
} else if types.len() < required_len && !infer_types {
46944670
let expected_text = count_type_params(required_len);
46954671
let actual_text = count_type_params(types.len());
46964672
struct_span_err!(self.tcx.sess, span, E0089,
@@ -4706,6 +4682,51 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
47064682
"unexpected binding of associated item in expression path \
47074683
(only allowed in type paths)");
47084684
}
4685+
4686+
// Check provided lifetime parameters.
4687+
let lifetime_defs = segment.map_or(&[][..], |(_, generics)| &generics.regions);
4688+
let required_len = lifetime_defs.len();
4689+
4690+
// Prohibit explicit lifetime arguments if late bound lifetime parameters are present.
4691+
let has_late_bound_lifetime_defs =
4692+
segment.map_or(false, |(_, generics)| generics.has_late_bound_regions);
4693+
if has_late_bound_lifetime_defs && !lifetimes.is_empty() {
4694+
// Report this as a lint only if no error was reported previously.
4695+
if !is_method_call && (lifetimes.len() > lifetime_defs.len() ||
4696+
lifetimes.len() < required_len && !infer_lifetimes) {
4697+
self.tcx.sess.span_err(lifetimes[0].span,
4698+
"cannot specify lifetime arguments explicitly \
4699+
if late bound lifetime parameters are present");
4700+
*segment = None;
4701+
} else {
4702+
self.tcx.sess.add_lint(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
4703+
lifetimes[0].id, lifetimes[0].span,
4704+
format!("cannot specify lifetime arguments explicitly \
4705+
if late bound lifetime parameters are present"));
4706+
}
4707+
return;
4708+
}
4709+
4710+
if lifetimes.len() > lifetime_defs.len() {
4711+
let span = lifetimes[lifetime_defs.len()].span;
4712+
let expected_text = count_lifetime_params(lifetime_defs.len());
4713+
let actual_text = count_lifetime_params(lifetimes.len());
4714+
struct_span_err!(self.tcx.sess, span, E0088,
4715+
"too many lifetime parameters provided: \
4716+
expected at most {}, found {}",
4717+
expected_text, actual_text)
4718+
.span_label(span, format!("expected {}", expected_text))
4719+
.emit();
4720+
} else if lifetimes.len() < required_len && !infer_lifetimes {
4721+
let expected_text = count_lifetime_params(lifetime_defs.len());
4722+
let actual_text = count_lifetime_params(lifetimes.len());
4723+
struct_span_err!(self.tcx.sess, span, E0090,
4724+
"too few lifetime parameters provided: \
4725+
expected {}, found {}",
4726+
expected_text, actual_text)
4727+
.span_label(span, format!("expected {}", expected_text))
4728+
.emit();
4729+
}
47094730
}
47104731

47114732
fn structurally_resolve_type_or_else<F>(&self, sp: Span, ty: Ty<'tcx>, f: F)

src/librustc_typeck/collect.rs

+91-1
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,95 @@ fn trait_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
772772
tcx.alloc_trait_def(def)
773773
}
774774

775+
fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
776+
node: hir_map::Node<'tcx>)
777+
-> bool {
778+
struct LateBoundRegionsDetector<'a, 'tcx: 'a> {
779+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
780+
binder_depth: u32,
781+
has_late_bound_regions: bool,
782+
}
783+
784+
impl<'a, 'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'a, 'tcx> {
785+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
786+
NestedVisitorMap::None
787+
}
788+
789+
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
790+
if self.has_late_bound_regions { return }
791+
match ty.node {
792+
hir::TyBareFn(..) => {
793+
self.binder_depth += 1;
794+
intravisit::walk_ty(self, ty);
795+
self.binder_depth -= 1;
796+
}
797+
_ => intravisit::walk_ty(self, ty)
798+
}
799+
}
800+
801+
fn visit_poly_trait_ref(&mut self,
802+
tr: &'tcx hir::PolyTraitRef,
803+
m: hir::TraitBoundModifier) {
804+
if self.has_late_bound_regions { return }
805+
self.binder_depth += 1;
806+
intravisit::walk_poly_trait_ref(self, tr, m);
807+
self.binder_depth -= 1;
808+
}
809+
810+
fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) {
811+
if self.has_late_bound_regions { return }
812+
813+
match self.tcx.named_region_map.defs.get(&lt.id).cloned() {
814+
Some(rl::Region::Static) | Some(rl::Region::EarlyBound(..)) => {}
815+
Some(rl::Region::LateBound(debruijn, _)) |
816+
Some(rl::Region::LateBoundAnon(debruijn, _))
817+
if debruijn.depth < self.binder_depth => {}
818+
_ => self.has_late_bound_regions = true,
819+
}
820+
}
821+
}
822+
823+
fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
824+
generics: &'tcx hir::Generics,
825+
decl: &'tcx hir::FnDecl)
826+
-> bool {
827+
let mut visitor = LateBoundRegionsDetector {
828+
tcx, binder_depth: 1, has_late_bound_regions: false
829+
};
830+
for lifetime in &generics.lifetimes {
831+
if tcx.named_region_map.late_bound.contains(&lifetime.lifetime.id) {
832+
return true;
833+
}
834+
}
835+
visitor.visit_fn_decl(decl);
836+
visitor.has_late_bound_regions
837+
}
838+
839+
match node {
840+
hir_map::NodeTraitItem(item) => match item.node {
841+
hir::TraitItemKind::Method(ref sig, _) =>
842+
has_late_bound_regions(tcx, &sig.generics, &sig.decl),
843+
_ => false,
844+
},
845+
hir_map::NodeImplItem(item) => match item.node {
846+
hir::ImplItemKind::Method(ref sig, _) =>
847+
has_late_bound_regions(tcx, &sig.generics, &sig.decl),
848+
_ => false,
849+
},
850+
hir_map::NodeForeignItem(item) => match item.node {
851+
hir::ForeignItemFn(ref fn_decl, _, ref generics) =>
852+
has_late_bound_regions(tcx, generics, fn_decl),
853+
_ => false,
854+
},
855+
hir_map::NodeItem(item) => match item.node {
856+
hir::ItemFn(ref fn_decl, .., ref generics, _) =>
857+
has_late_bound_regions(tcx, generics, fn_decl),
858+
_ => false,
859+
},
860+
_ => false
861+
}
862+
}
863+
775864
fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
776865
def_id: DefId)
777866
-> &'tcx ty::Generics {
@@ -959,7 +1048,8 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
9591048
regions: regions,
9601049
types: types,
9611050
type_param_to_index: type_param_to_index,
962-
has_self: has_self || parent_has_self
1051+
has_self: has_self || parent_has_self,
1052+
has_late_bound_regions: has_late_bound_regions(tcx, node),
9631053
})
9641054
}
9651055

0 commit comments

Comments
 (0)