Skip to content

Commit 69b43c2

Browse files
committed
improve error message for const ty param mismatch
1 parent 06c9c59 commit 69b43c2

25 files changed

+194
-275
lines changed

compiler/rustc_hir/src/hir.rs

+8
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,14 @@ impl GenericArg<'_> {
282282
GenericArg::Const(_) => "constant",
283283
}
284284
}
285+
286+
pub fn short_descr(&self) -> &'static str {
287+
match self {
288+
GenericArg::Lifetime(_) => "lifetime",
289+
GenericArg::Type(_) => "type",
290+
GenericArg::Const(_) => "const",
291+
}
292+
}
285293
}
286294

287295
#[derive(Debug, HashStable_Generic)]

compiler/rustc_typeck/src/astconv/generics.rs

+66-80
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
2222
sess: &Session,
2323
arg: &GenericArg<'_>,
2424
kind: &'static str,
25+
possible_ordering_error: bool,
2526
help: Option<&str>,
2627
) {
2728
let mut err = struct_span_err!(
@@ -48,8 +49,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
4849
GenericArg::Const(_) => ParamKindOrd::Const { unordered },
4950
};
5051

52+
if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. }))
53+
&& matches!(kind_ord, ParamKindOrd::Const { .. })
54+
{
55+
let suggestions = vec![
56+
(arg.span().shrink_to_lo(), String::from("{ ")),
57+
(arg.span().shrink_to_hi(), String::from(" }")),
58+
];
59+
err.multipart_suggestion(
60+
"if this generic argument was intended as a const parameter, \
61+
try surrounding it with braces:",
62+
suggestions,
63+
Applicability::MaybeIncorrect,
64+
);
65+
}
66+
5167
// This note is only true when generic parameters are strictly ordered by their kind.
52-
if kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal {
68+
if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal {
5369
let (first, last) =
5470
if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) };
5571
err.note(&format!("{} arguments must be provided before {} arguments", first, last));
@@ -153,8 +169,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
153169
// Check whether this segment takes generic arguments and the user has provided any.
154170
let (generic_args, infer_args) = args_for_def_id(def_id);
155171

156-
let mut args =
157-
generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable();
172+
let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter());
173+
let mut args = args_iter.clone().peekable();
158174

159175
// If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
160176
// If we later encounter a lifetime, we know that the arguments were provided in the
@@ -221,8 +237,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
221237
GenericParamDefKind::Const => {
222238
ParamKindOrd::Const {
223239
unordered: tcx
224-
.sess
225-
.features_untracked()
240+
.features()
226241
.const_generics,
227242
}
228243
}
@@ -242,6 +257,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
242257
tcx.sess,
243258
arg,
244259
kind.descr(),
260+
!args_iter.clone().is_sorted_by_key(|arg| match arg {
261+
GenericArg::Lifetime(_) => ParamKindOrd::Lifetime,
262+
GenericArg::Type(_) => ParamKindOrd::Type,
263+
GenericArg::Const(_) => ParamKindOrd::Const {
264+
unordered: tcx.features().const_generics,
265+
},
266+
}),
245267
Some(&format!(
246268
"reorder the arguments: {}: `<{}>`",
247269
param_types_present
@@ -293,7 +315,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
293315
assert_eq!(kind, "lifetime");
294316
let provided =
295317
force_infer_lt.expect("lifetimes ought to have been inferred");
296-
Self::generic_arg_mismatch_err(tcx.sess, provided, kind, None);
318+
Self::generic_arg_mismatch_err(tcx.sess, provided, kind, false, None);
297319
}
298320

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

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

395418
if silent {
396-
return Err((0i32, None));
419+
return false;
397420
}
398421

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

412-
let (spans, label) = if provided > permitted {
435+
let (spans, labels) = if provided > permitted {
413436
// In the case when the user has provided too many arguments,
414437
// we want to point to the unexpected arguments.
415-
let spans: Vec<Span> = args.args[offset + permitted..offset + provided]
438+
let (spans, labels): (Vec<Span>, Vec<String>) = args.args
439+
[offset + permitted..offset + provided]
416440
.iter()
417-
.map(|arg| arg.span())
418-
.collect();
441+
.map(|arg| (arg.span(), format!("unexpected {} argument", arg.short_descr())))
442+
.unzip();
419443
unexpected_spans.extend(spans.clone());
420-
(spans, format!("unexpected {} argument", kind))
444+
(spans, labels)
421445
} else {
422446
(
423447
vec![span],
424-
format!(
448+
vec![format!(
425449
"expected {}{} {} argument{}",
426450
quantifier,
427451
bound,
428452
kind,
429453
pluralize!(bound),
430-
),
454+
)],
431455
)
432456
};
433457

@@ -439,12 +463,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
439463
),
440464
DiagnosticId::Error("E0107".into()),
441465
);
442-
for span in spans {
466+
for (span, label) in spans.into_iter().zip(labels) {
443467
err.span_label(span, label.as_str());
444468
}
445-
446-
assert_ne!(bound, provided);
447-
Err((bound as i32 - provided as i32, Some(err)))
469+
err.emit();
470+
false
448471
};
449472

450473
let mut unexpected_spans = vec![];
@@ -459,75 +482,38 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
459482
explicit_late_bound == ExplicitLateBound::Yes,
460483
);
461484

462-
// FIXME(const_generics:defaults)
463-
let mut const_count_correct = check_kind_count(
464-
"const",
465-
if infer_args { 0 } else { param_counts.consts },
466-
param_counts.consts,
467-
arg_counts.consts,
468-
arg_counts.lifetimes + arg_counts.types,
469-
&mut unexpected_spans,
470-
false,
471-
);
485+
let kind_str = if param_counts.consts + arg_counts.consts == 0 {
486+
"type"
487+
} else if named_type_param_count + arg_counts.types == 0 {
488+
"const"
489+
} else {
490+
"generic"
491+
};
472492

473-
// Note that type errors are currently be emitted *after* const errors.
474-
let mut type_count_correct = check_kind_count(
475-
"type",
476-
if infer_args { 0 } else { param_counts.types - defaults.types - has_self as usize },
477-
param_counts.types - has_self as usize,
478-
arg_counts.types,
493+
let arg_count_correct = check_kind_count(
494+
kind_str,
495+
if infer_args {
496+
0
497+
} else {
498+
param_counts.consts + named_type_param_count - defaults.types
499+
},
500+
param_counts.consts + named_type_param_count,
501+
arg_counts.consts + arg_counts.types,
479502
arg_counts.lifetimes,
480503
&mut unexpected_spans,
481504
false,
482505
);
483506

484-
// Emit a help message if it's possible that a type could be surrounded in braces
485-
if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct {
486-
if let Err((_, Some(ref mut type_err))) = type_count_correct {
487-
let possible_matches = args.args[arg_counts.lifetimes..]
488-
.iter()
489-
.filter(|arg| {
490-
matches!(
491-
arg,
492-
GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. })
493-
)
494-
})
495-
.take(c_mismatch.max(0) as usize);
496-
for arg in possible_matches {
497-
let suggestions = vec![
498-
(arg.span().shrink_to_lo(), String::from("{ ")),
499-
(arg.span().shrink_to_hi(), String::from(" }")),
500-
];
501-
type_err.multipart_suggestion(
502-
"If this generic argument was intended as a const parameter, \
503-
try surrounding it with braces:",
504-
suggestions,
505-
Applicability::MaybeIncorrect,
506-
);
507-
}
508-
}
509-
}
510-
511-
let emit_correct =
512-
|correct: Result<(), (_, Option<rustc_errors::DiagnosticBuilder<'_>>)>| match correct {
513-
Ok(()) => Ok(()),
514-
Err((_, None)) => Err(()),
515-
Err((_, Some(mut err))) => {
516-
err.emit();
517-
Err(())
518-
}
519-
};
520-
521-
let arg_count_correct = emit_correct(lifetime_count_correct)
522-
.and(emit_correct(const_count_correct))
523-
.and(emit_correct(type_count_correct));
524-
525507
GenericArgCountResult {
526508
explicit_late_bound,
527-
correct: arg_count_correct.map_err(|()| GenericArgCountMismatch {
528-
reported: Some(ErrorReported),
529-
invalid_args: unexpected_spans,
530-
}),
509+
correct: if lifetime_count_correct && arg_count_correct {
510+
Ok(())
511+
} else {
512+
Err(GenericArgCountMismatch {
513+
reported: Some(ErrorReported),
514+
invalid_args: unexpected_spans,
515+
})
516+
},
531517
}
532518
}
533519

compiler/rustc_typeck/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ This API is completely unstable and subject to change.
6161
#![feature(box_syntax)]
6262
#![feature(crate_visibility_modifier)]
6363
#![feature(in_band_lifetimes)]
64+
#![feature(is_sorted)]
6465
#![feature(nll)]
6566
#![feature(or_patterns)]
6667
#![feature(try_blocks)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![feature(min_const_generics)]
2+
3+
type N = u32;
4+
struct Foo<const M: usize>;
5+
fn test<const N: usize>() -> Foo<N> { //~ ERROR type provided when
6+
Foo
7+
}
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0747]: type provided when a constant was expected
2+
--> $DIR/const-param-shadowing.rs:5:34
3+
|
4+
LL | fn test<const N: usize>() -> Foo<N> {
5+
| ^
6+
|
7+
help: if this generic argument was intended as a const parameter, try surrounding it with braces:
8+
|
9+
LL | fn test<const N: usize>() -> Foo<{ N }> {
10+
| ^ ^
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0747`.
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
fn main() {
2-
let _: Vec<&str, "a"> = Vec::new(); //~ ERROR wrong number of const arguments
2+
let _: Vec<&str, "a"> = Vec::new();
3+
//~^ ERROR wrong number of generic arguments
34
}

src/test/ui/const-generics/invalid-constant-in-args.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0107]: wrong number of const arguments: expected 0, found 1
1+
error[E0107]: wrong number of generic arguments: expected 1, found 2
22
--> $DIR/invalid-constant-in-args.rs:2:22
33
|
44
LL | let _: Vec<&str, "a"> = Vec::new();

src/test/ui/const-generics/invalid-enum.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,16 @@ impl<const CF: CompileFlag, T> Example<CF, T> {
2020
pub fn main() {
2121
test_1::<CompileFlag::A>();
2222
//~^ ERROR: expected type, found variant
23-
//~| ERROR: wrong number of const arguments
24-
//~| ERROR: wrong number of type arguments
23+
//~| ERROR: type provided when a constant was expected
2524

2625
test_2::<_, CompileFlag::A>(0);
2726
//~^ ERROR: expected type, found variant
28-
//~| ERROR: wrong number of const arguments
29-
//~| ERROR: wrong number of type arguments
27+
//~| ERROR: type provided when a constant was expected
3028

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

3633
let _: Example<Example::ASSOC_FLAG, _> = Example { x: 0 };
37-
//~^ ERROR: wrong number of const arguments
38-
//~| ERROR: wrong number of type arguments
34+
//~^ ERROR: type provided when a constant was expected
3935
}

0 commit comments

Comments
 (0)