Skip to content

Commit

Permalink
Auto merge of #42492 - petrochenkov:methlife, r=nikomatsakis
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bors committed Jul 18, 2017
2 parents 2e63340 + 39114f9 commit 83c659e
Show file tree
Hide file tree
Showing 19 changed files with 417 additions and 206 deletions.
2 changes: 2 additions & 0 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ declare_lint! {
"detects parenthesized generic parameters in type and module names"
}

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,
Expand Down Expand Up @@ -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
)
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_lint/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 3 additions & 12 deletions src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
}
Expand Down
59 changes: 18 additions & 41 deletions src/librustc_typeck/check/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
81 changes: 51 additions & 30 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))) => {
Expand Down Expand Up @@ -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)) => {
Expand All @@ -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" })
Expand All @@ -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() {
Expand All @@ -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,
Expand All @@ -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)
Expand Down
92 changes: 91 additions & 1 deletion src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
})
}

Expand Down
Loading

0 comments on commit 83c659e

Please sign in to comment.