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

improve type const mismatch errors #79032

Merged
merged 3 commits into from
Nov 17, 2020
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
8 changes: 8 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,14 @@ impl GenericArg<'_> {
GenericArg::Const(_) => "constant",
}
}

pub fn short_descr(&self) -> &'static str {
match self {
GenericArg::Lifetime(_) => "lifetime",
GenericArg::Type(_) => "type",
GenericArg::Const(_) => "const",
}
}
}

#[derive(Debug, HashStable_Generic)]
Expand Down
182 changes: 79 additions & 103 deletions compiler/rustc_typeck/src/astconv/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
sess: &Session,
arg: &GenericArg<'_>,
kind: &'static str,
possible_ordering_error: bool,
help: Option<&str>,
) {
let mut err = struct_span_err!(
Expand All @@ -48,8 +49,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
GenericArg::Const(_) => ParamKindOrd::Const { unordered },
};

if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. }))
&& matches!(kind_ord, ParamKindOrd::Const { .. })
{
let suggestions = vec![
(arg.span().shrink_to_lo(), String::from("{ ")),
(arg.span().shrink_to_hi(), String::from(" }")),
];
err.multipart_suggestion(
"if this generic argument was intended as a const parameter, \
try surrounding it with braces:",
suggestions,
Applicability::MaybeIncorrect,
);
}

// This note is only true when generic parameters are strictly ordered by their kind.
if kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal {
if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal {
let (first, last) =
if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) };
err.note(&format!("{} arguments must be provided before {} arguments", first, last));
Expand Down Expand Up @@ -153,8 +169,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// Check whether this segment takes generic arguments and the user has provided any.
let (generic_args, infer_args) = args_for_def_id(def_id);

let mut args =
generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable();
let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter());
let mut args = args_iter.clone().peekable();

// If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
// If we later encounter a lifetime, we know that the arguments were provided in the
Expand Down Expand Up @@ -221,8 +237,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
GenericParamDefKind::Const => {
ParamKindOrd::Const {
unordered: tcx
.sess
.features_untracked()
.features()
.const_generics,
}
}
Expand All @@ -242,6 +257,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
tcx.sess,
arg,
kind.descr(),
!args_iter.clone().is_sorted_by_key(|arg| match arg {
GenericArg::Lifetime(_) => ParamKindOrd::Lifetime,
GenericArg::Type(_) => ParamKindOrd::Type,
GenericArg::Const(_) => ParamKindOrd::Const {
unordered: tcx.features().const_generics,
},
}),
Some(&format!(
"reorder the arguments: {}: `<{}>`",
param_types_present
Expand Down Expand Up @@ -293,7 +315,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
assert_eq!(kind, "lifetime");
let provided =
force_infer_lt.expect("lifetimes ought to have been inferred");
Self::generic_arg_mismatch_err(tcx.sess, provided, kind, None);
Self::generic_arg_mismatch_err(tcx.sess, provided, kind, false, None);
}

break;
Expand Down Expand Up @@ -351,6 +373,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// that lifetimes will proceed types. So it suffices to check the number of each generic
// arguments in order to validate them with respect to the generic parameters.
let param_counts = def.own_counts();
let named_type_param_count = param_counts.types - has_self as usize;
let arg_counts = args.own_counts();
let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0;

Expand Down Expand Up @@ -389,11 +412,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// For kinds without defaults (e.g.., lifetimes), `required == permitted`.
// For other kinds (i.e., types), `permitted` may be greater than `required`.
if required <= provided && provided <= permitted {
return Ok(());
return true;
}

if silent {
return Err((0i32, None));
return false;
}

// Unfortunately lifetime and type parameter mismatches are typically styled
Expand All @@ -409,25 +432,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
(required, "")
};

let (spans, label) = if required == permitted && provided > permitted {
let (spans, labels) = if provided > permitted {
// In the case when the user has provided too many arguments,
// we want to point to the unexpected arguments.
let spans: Vec<Span> = args.args[offset + permitted..offset + provided]
let (spans, labels): (Vec<Span>, Vec<String>) = args.args
[offset + permitted..offset + provided]
.iter()
.map(|arg| arg.span())
.collect();
.map(|arg| (arg.span(), format!("unexpected {} argument", arg.short_descr())))
.unzip();
unexpected_spans.extend(spans.clone());
(spans, format!("unexpected {} argument", kind))
(spans, labels)
} else {
(
vec![span],
format!(
vec![format!(
"expected {}{} {} argument{}",
quantifier,
bound,
kind,
pluralize!(bound),
),
)],
)
};

Expand All @@ -439,105 +463,57 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
),
DiagnosticId::Error("E0107".into()),
);
for span in spans {
for (span, label) in spans.into_iter().zip(labels) {
err.span_label(span, label.as_str());
}

assert_ne!(bound, provided);
Err((bound as i32 - provided as i32, Some(err)))
err.emit();
false
};

let mut unexpected_spans = vec![];

let mut lifetime_count_correct = Ok(());
if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
lifetime_count_correct = check_kind_count(
"lifetime",
param_counts.lifetimes,
param_counts.lifetimes,
arg_counts.lifetimes,
0,
&mut unexpected_spans,
explicit_late_bound == ExplicitLateBound::Yes,
);
}

// FIXME(const_generics:defaults)
let mut const_count_correct = Ok(());
if !infer_args || arg_counts.consts > param_counts.consts {
const_count_correct = check_kind_count(
"const",
param_counts.consts,
param_counts.consts,
arg_counts.consts,
arg_counts.lifetimes + arg_counts.types,
&mut unexpected_spans,
false,
);
}

// Note that type errors are currently be emitted *after* const errors.
let mut type_count_correct = Ok(());
if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize
{
type_count_correct = check_kind_count(
"type",
param_counts.types - defaults.types - has_self as usize,
param_counts.types - has_self as usize,
arg_counts.types,
arg_counts.lifetimes,
&mut unexpected_spans,
false,
);
}

// Emit a help message if it's possible that a type could be surrounded in braces
if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct {
if let Err((_, Some(ref mut type_err))) = type_count_correct {
let possible_matches = args.args[arg_counts.lifetimes..]
.iter()
.filter(|arg| {
matches!(
arg,
GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. })
)
})
.take(c_mismatch.max(0) as usize);
for arg in possible_matches {
let suggestions = vec![
(arg.span().shrink_to_lo(), String::from("{ ")),
(arg.span().shrink_to_hi(), String::from(" }")),
];
type_err.multipart_suggestion(
"If this generic argument was intended as a const parameter, \
try surrounding it with braces:",
suggestions,
Applicability::MaybeIncorrect,
);
}
}
}
let lifetime_count_correct = check_kind_count(
"lifetime",
if infer_lifetimes { 0 } else { param_counts.lifetimes },
param_counts.lifetimes,
arg_counts.lifetimes,
0,
&mut unexpected_spans,
explicit_late_bound == ExplicitLateBound::Yes,
);

let emit_correct =
|correct: Result<(), (_, Option<rustc_errors::DiagnosticBuilder<'_>>)>| match correct {
Ok(()) => Ok(()),
Err((_, None)) => Err(()),
Err((_, Some(mut err))) => {
err.emit();
Err(())
}
};
let kind_str = if param_counts.consts + arg_counts.consts == 0 {
"type"
} else if named_type_param_count + arg_counts.types == 0 {
"const"
} else {
"generic"
};

let arg_count_correct = emit_correct(lifetime_count_correct)
.and(emit_correct(const_count_correct))
.and(emit_correct(type_count_correct));
let arg_count_correct = check_kind_count(
kind_str,
if infer_args {
0
} else {
param_counts.consts + named_type_param_count - defaults.types
},
param_counts.consts + named_type_param_count,
arg_counts.consts + arg_counts.types,
arg_counts.lifetimes,
&mut unexpected_spans,
false,
);

GenericArgCountResult {
explicit_late_bound,
correct: arg_count_correct.map_err(|()| GenericArgCountMismatch {
reported: Some(ErrorReported),
invalid_args: unexpected_spans,
}),
correct: if lifetime_count_correct && arg_count_correct {
Ok(())
} else {
Err(GenericArgCountMismatch {
reported: Some(ErrorReported),
invalid_args: unexpected_spans,
})
},
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ This API is completely unstable and subject to change.
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(in_band_lifetimes)]
#![feature(is_sorted)]
#![feature(nll)]
#![feature(or_patterns)]
#![feature(try_blocks)]
Expand Down
9 changes: 9 additions & 0 deletions src/test/ui/const-generics/const-param-shadowing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![feature(min_const_generics)]

type N = u32;
struct Foo<const M: usize>;
fn test<const N: usize>() -> Foo<N> { //~ ERROR type provided when
Foo
}

fn main() {}
14 changes: 14 additions & 0 deletions src/test/ui/const-generics/const-param-shadowing.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0747]: type provided when a constant was expected
--> $DIR/const-param-shadowing.rs:5:34
|
LL | fn test<const N: usize>() -> Foo<N> {
| ^
|
help: if this generic argument was intended as a const parameter, try surrounding it with braces:
|
LL | fn test<const N: usize>() -> Foo<{ N }> {
| ^ ^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0747`.
3 changes: 2 additions & 1 deletion src/test/ui/const-generics/invalid-constant-in-args.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
fn main() {
let _: Vec<&str, "a"> = Vec::new(); //~ ERROR wrong number of const arguments
let _: Vec<&str, "a"> = Vec::new();
//~^ ERROR wrong number of generic arguments
}
2 changes: 1 addition & 1 deletion src/test/ui/const-generics/invalid-constant-in-args.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0107]: wrong number of const arguments: expected 0, found 1
error[E0107]: wrong number of generic arguments: expected 1, found 2
--> $DIR/invalid-constant-in-args.rs:2:22
|
LL | let _: Vec<&str, "a"> = Vec::new();
Expand Down
12 changes: 4 additions & 8 deletions src/test/ui/const-generics/invalid-enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,16 @@ impl<const CF: CompileFlag, T> Example<CF, T> {
pub fn main() {
test_1::<CompileFlag::A>();
//~^ ERROR: expected type, found variant
//~| ERROR: wrong number of const arguments
//~| ERROR: wrong number of type arguments
//~| ERROR: type provided when a constant was expected

test_2::<_, CompileFlag::A>(0);
//~^ ERROR: expected type, found variant
//~| ERROR: wrong number of const arguments
//~| ERROR: wrong number of type arguments
//~| ERROR: type provided when a constant was expected

let _: Example<CompileFlag::A, _> = Example { x: 0 };
//~^ ERROR: expected type, found variant
//~| ERROR: wrong number of const arguments
//~| ERROR: wrong number of type arguments
//~| ERROR: type provided when a constant was expected

let _: Example<Example::ASSOC_FLAG, _> = Example { x: 0 };
//~^ ERROR: wrong number of const arguments
//~| ERROR: wrong number of type arguments
//~^ ERROR: type provided when a constant was expected
}
Loading