Refactor generic argument count check in check/mod.rs

This commit is contained in:
varkor 2018-08-08 00:38:31 +01:00
parent 68b0e7dd99
commit 04d33bbdb3
10 changed files with 65 additions and 163 deletions

View file

@ -90,6 +90,11 @@ struct ConvertedBinding<'tcx> {
span: Span,
}
pub struct GenericArgMismatchErrorCode {
pub lifetimes: (&'static str, &'static str),
pub types: (&'static str, &'static str),
}
/// Dummy type used for the `Self` of a `TraitRef` created for converting
/// a trait object, and which gets removed in `ExistentialTraitRef`.
/// This type must not appear anywhere in other converted types.
@ -199,6 +204,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
is_method_call: bool,
has_self: bool,
infer_types: bool,
error_codes: GenericArgMismatchErrorCode,
) -> bool {
// At this stage we are guaranteed that the generic arguments are in the correct order, e.g.
// that lifetimes will proceed types. So it suffices to check the number of each generic
@ -243,8 +249,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
}
}
let check_kind_count = |error_code_less: &str,
error_code_more: &str,
let check_kind_count = |error_code: (&str, &str),
kind,
required,
permitted,
@ -296,9 +301,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
),
DiagnosticId::Error({
if provided <= permitted {
error_code_less
error_code.0
} else {
error_code_more
error_code.1
}
}.into())
).span_label(span, label).emit();
@ -308,8 +313,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
check_kind_count(
"E0107",
"E0107",
error_codes.lifetimes,
"lifetime",
param_counts.lifetimes,
param_counts.lifetimes,
@ -319,8 +323,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
if !infer_types
|| arg_counts.types > param_counts.types - defaults.types - has_self as usize {
check_kind_count(
"E0243",
"E0244", // FIXME: E0243 and E0244 should be unified.
error_codes.types,
"type",
param_counts.types - defaults.types - has_self as usize,
param_counts.types - has_self as usize,
@ -508,6 +511,10 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
false, // `is_method_call` (irrelevant here)
has_self,
infer_types,
GenericArgMismatchErrorCode {
lifetimes: ("E0107", "E0107"),
types: ("E0243", "E0244"), // FIXME: E0243 and E0244 should be unified.
},
);
let is_object = self_ty.map_or(false, |ty| ty.sty == TRAIT_OBJECT_DUMMY_SELF);

View file

@ -10,7 +10,7 @@
use super::{probe, MethodCallee};
use astconv::AstConv;
use astconv::{AstConv, GenericArgMismatchErrorCode};
use check::{FnCtxt, PlaceOp, callee, Needs};
use hir::GenericArg;
use hir::def_id::DefId;
@ -329,9 +329,11 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
true, // `is_method_call`
method_generics.parent.is_none() && method_generics.has_self,
segment.infer_types || suppress_mismatch,
GenericArgMismatchErrorCode {
lifetimes: ("E0090", "E0088"),
types: ("E0089", "E0087"),
},
);
// self.fcx.check_generic_arg_count(self.span, &segment, &method_generics, true,
// supress_mismatch);
// Create subst for early-bound lifetime parameters, combining
// parameters from the type and those from the method.

View file

@ -84,9 +84,10 @@ pub use self::compare_method::{compare_impl_method, compare_const_impl};
use self::method::MethodCallee;
use self::TupleArgumentsFlag::*;
use astconv::AstConv;
use astconv::{AstConv, GenericArgMismatchErrorCode};
use hir::GenericArg;
use hir::def::Def;
use hir::HirVec;
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use std::slice;
use namespace::Namespace;
@ -4937,16 +4938,30 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// to add defaults. If the user provided *too many* types, that's
// a problem.
let mut supress_errors = FxHashMap();
let mut suppress_errors = FxHashMap();
for &PathSeg(def_id, index) in &path_segs {
let seg = &segments[index];
let generics = self.tcx.generics_of(def_id);
// `impl Trait` is treated as a normal generic parameter internally,
// but we don't allow users to specify the parameter's value
// explicitly, so we have to do some error-checking here.
let supress_mismatch = self.check_impl_trait(span, seg, &generics);
supress_errors.insert(index,
self.check_generic_arg_count(span, seg, &generics, false, supress_mismatch));
let suppress_mismatch = self.check_impl_trait(span, seg, &generics);
suppress_errors.insert(index, AstConv::check_generic_arg_count(
self.tcx,
span,
&generics,
&seg.args.clone().unwrap_or_else(|| P(hir::GenericArgs {
args: HirVec::new(), bindings: HirVec::new(), parenthesized: false,
})),
false, // `is_declaration`
false, // `is_method_call`
generics.parent.is_none() && generics.has_self,
seg.infer_types || suppress_mismatch,
GenericArgMismatchErrorCode {
lifetimes: ("E0090", "E0088"), // FIXME: E0090 and E0088 should be unified.
types: ("E0089", "E0087"), // FIXME: E0089 and E0087 should be unified.
},
));
}
let has_self = path_segs.last().map(|PathSeg(def_id, _)| {
@ -4968,7 +4983,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}) {
// If we've encountered an `impl Trait`-related error, we're just
// going to infer the arguments for better error messages.
if !supress_errors[&index] {
if !suppress_errors[&index] {
// Check whether the user has provided generic arguments.
if let Some(ref data) = segments[index].args {
return (Some(data), segments[index].infer_types);
@ -5097,128 +5112,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
directly, not through a function pointer");
}
/// Report errors if the provided parameters are too few or too many.
fn check_generic_arg_count(&self,
span: Span,
segment: &hir::PathSegment,
generics: &ty::Generics,
is_method_call: bool,
supress_mismatch_error: bool)
-> bool {
let mut supress_errors = false;
let (mut lifetimes, mut types) = (vec![], vec![]);
let infer_types = segment.infer_types;
let mut bindings = vec![];
if let Some(ref data) = segment.args {
data.args.iter().for_each(|arg| match arg {
GenericArg::Lifetime(lt) => lifetimes.push(lt.clone()),
GenericArg::Type(ty) => types.push(ty.clone()),
});
bindings = data.bindings.clone().to_vec();
}
struct ParamRange {
required: usize,
accepted: usize
};
let mut lt_accepted = 0;
let mut ty_params = ParamRange { required: 0, accepted: 0 };
for param in &generics.params {
match param.kind {
GenericParamDefKind::Lifetime => lt_accepted += 1,
GenericParamDefKind::Type { has_default, .. } => {
ty_params.accepted += 1;
if !has_default {
ty_params.required += 1;
}
}
};
}
if generics.parent.is_none() && generics.has_self {
ty_params.required -= 1;
ty_params.accepted -= 1;
}
let ty_accepted = ty_params.accepted;
let ty_required = ty_params.required;
let count_ty_params = |n| format!("{} type parameter{}", n, if n == 1 { "" } else { "s" });
let expected_text = count_ty_params(ty_accepted);
let actual_text = count_ty_params(types.len());
if let Some((mut err, span)) = if types.len() > ty_accepted {
// To prevent derived errors to accumulate due to extra
// type parameters, we force instantiate_value_path to
// use inference variables instead of the provided types.
supress_errors = true;
let span = types[ty_accepted].span;
Some((struct_span_err!(self.tcx.sess, span, E0087,
"too many type parameters provided: \
expected at most {}, found {}",
expected_text, actual_text), span))
} else if types.len() < ty_required && !infer_types && !supress_mismatch_error {
Some((struct_span_err!(self.tcx.sess, span, E0089,
"too few type parameters provided: \
expected {}, found {}",
expected_text, actual_text), span))
} else {
None
} {
self.set_tainted_by_errors(); // #53251
err.span_label(span, format!("expected {}", expected_text)).emit();
}
if !bindings.is_empty() {
AstConv::prohibit_assoc_ty_binding(self.tcx, bindings[0].span);
}
let infer_lifetimes = lifetimes.len() == 0;
// Prohibit explicit lifetime arguments if late bound lifetime parameters are present.
let has_late_bound_lifetime_defs = generics.has_late_bound_regions;
if let (Some(span_late), false) = (has_late_bound_lifetime_defs, lifetimes.is_empty()) {
// Report this as a lint only if no error was reported previously.
let primary_msg = "cannot specify lifetime arguments explicitly \
if late bound lifetime parameters are present";
let note_msg = "the late bound lifetime parameter is introduced here";
if !is_method_call && (lifetimes.len() > lt_accepted ||
lifetimes.len() < lt_accepted && !infer_lifetimes) {
supress_errors = true;
let mut err = self.tcx.sess.struct_span_err(lifetimes[0].span, primary_msg);
err.span_note(span_late, note_msg);
err.emit();
} else {
let mut multispan = MultiSpan::from_span(lifetimes[0].span);
multispan.push_span_label(span_late, note_msg.to_string());
self.tcx.lint_node(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
lifetimes[0].id, multispan, primary_msg);
}
return supress_errors;
}
let count_lifetime_params = |n| {
format!("{} lifetime parameter{}", n, if n == 1 { "" } else { "s" })
};
let expected_text = count_lifetime_params(lt_accepted);
let actual_text = count_lifetime_params(lifetimes.len());
if let Some((mut err, span)) = if lifetimes.len() > lt_accepted {
let span = lifetimes[lt_accepted].span;
Some((struct_span_err!(self.tcx.sess, span, E0088,
"too many lifetime parameters provided: \
expected at most {}, found {}",
expected_text, actual_text), span))
} else if lifetimes.len() < lt_accepted && !infer_lifetimes {
Some((struct_span_err!(self.tcx.sess, span, E0090,
"too few lifetime parameters provided: \
expected {}, found {}",
expected_text, actual_text), span))
} else {
None
} {
err.span_label(span, format!("expected {}", expected_text)).emit();
}
supress_errors
}
/// Report error if there is an explicit type parameter when using `impl Trait`.
fn check_impl_trait(&self,
span: Span,

View file

@ -12,7 +12,7 @@ fn foo() {}
fn bar<T>() {}
fn main() {
foo::<f64>(); //~ ERROR expected at most 0 type parameters, found 1 type parameter [E0087]
foo::<f64>(); //~ ERROR wrong number of type arguments: expected 0, found 1 [E0087]
bar::<f64, u64>(); //~ ERROR expected at most 1 type parameter, found 2 type parameters [E0087]
bar::<f64, u64>(); //~ ERROR wrong number of type arguments: expected 1, found 2 [E0087]
}

View file

@ -1,14 +1,14 @@
error[E0087]: too many type parameters provided: expected at most 0 type parameters, found 1 type parameter
--> $DIR/E0087.rs:15:11
error[E0087]: wrong number of type arguments: expected 0, found 1
--> $DIR/E0087.rs:15:5
|
LL | foo::<f64>(); //~ ERROR expected at most 0 type parameters, found 1 type parameter [E0087]
| ^^^ expected 0 type parameters
LL | foo::<f64>(); //~ ERROR wrong number of type arguments: expected 0, found 1 [E0087]
| ^^^^^^^^^^ unexpected type argument
error[E0087]: too many type parameters provided: expected at most 1 type parameter, found 2 type parameters
--> $DIR/E0087.rs:17:16
error[E0087]: wrong number of type arguments: expected 1, found 2
--> $DIR/E0087.rs:17:5
|
LL | bar::<f64, u64>(); //~ ERROR expected at most 1 type parameter, found 2 type parameters [E0087]
| ^^^ expected 1 type parameter
LL | bar::<f64, u64>(); //~ ERROR wrong number of type arguments: expected 1, found 2 [E0087]
| ^^^^^^^^^^^^^^^ unexpected type argument
error: aborting due to 2 previous errors

View file

@ -1,14 +1,14 @@
error[E0088]: too many lifetime parameters provided: expected at most 0 lifetime parameters, found 1 lifetime parameter
--> $DIR/E0088.rs:15:9
error[E0088]: wrong number of lifetime arguments: expected 0, found 1
--> $DIR/E0088.rs:15:5
|
LL | f::<'static>(); //~ ERROR E0088
| ^^^^^^^ expected 0 lifetime parameters
| ^^^^^^^^^^^^ unexpected lifetime argument
error[E0088]: too many lifetime parameters provided: expected at most 1 lifetime parameter, found 2 lifetime parameters
--> $DIR/E0088.rs:16:18
error[E0088]: wrong number of lifetime arguments: expected 1, found 2
--> $DIR/E0088.rs:16:5
|
LL | g::<'static, 'static>(); //~ ERROR E0088
| ^^^^^^^ expected 1 lifetime parameter
| ^^^^^^^^^^^^^^^^^^^^^ unexpected lifetime argument
error: aborting due to 2 previous errors

View file

@ -11,5 +11,5 @@
fn foo<T, U>() {}
fn main() {
foo::<f64>(); //~ ERROR expected 2 type parameters, found 1 type parameter [E0089]
foo::<f64>(); //~ ERROR wrong number of type arguments: expected 2, found 1 [E0089]
}

View file

@ -1,8 +1,8 @@
error[E0089]: too few type parameters provided: expected 2 type parameters, found 1 type parameter
error[E0089]: wrong number of type arguments: expected 2, found 1
--> $DIR/E0089.rs:14:5
|
LL | foo::<f64>(); //~ ERROR expected 2 type parameters, found 1 type parameter [E0089]
| ^^^^^^^^^^ expected 2 type parameters
LL | foo::<f64>(); //~ ERROR wrong number of type arguments: expected 2, found 1 [E0089]
| ^^^^^^^^^^ expected 2 type arguments
error: aborting due to previous error

View file

@ -11,5 +11,5 @@
fn foo<'a: 'b, 'b: 'a>() {}
fn main() {
foo::<'static>(); //~ ERROR expected 2 lifetime parameters, found 1 lifetime parameter [E0090]
foo::<'static>(); //~ ERROR wrong number of lifetime arguments: expected 2, found 1 [E0090]
}

View file

@ -1,8 +1,8 @@
error[E0090]: too few lifetime parameters provided: expected 2 lifetime parameters, found 1 lifetime parameter
error[E0090]: wrong number of lifetime arguments: expected 2, found 1
--> $DIR/E0090.rs:14:5
|
LL | foo::<'static>(); //~ ERROR expected 2 lifetime parameters, found 1 lifetime parameter [E0090]
| ^^^^^^^^^^^^^^ expected 2 lifetime parameters
LL | foo::<'static>(); //~ ERROR wrong number of lifetime arguments: expected 2, found 1 [E0090]
| ^^^^^^^^^^^^^^ expected 2 lifetime arguments
error: aborting due to previous error