Refactor generic argument count check in check/mod.rs
This commit is contained in:
parent
68b0e7dd99
commit
04d33bbdb3
10 changed files with 65 additions and 163 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue