Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support generic lifetime arguments in method calls #42492

Merged
merged 5 commits into from
Jul 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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