Skip to content

Commit 835faa5

Browse files
authored
Rollup merge of #79032 - lcnr:arg-count, r=varkor
improve type const mismatch errors Doesn't completely remove `check_generic_arg_count` as that would have required some more complex changes but instead checks type and const params in only one step. Also moved the help added by `@JulianKnodt` in #75611 to `generic_arg_mismatch_err`. r? `@varkor` cc `@petrochenkov`
2 parents bac213b + 69b43c2 commit 835faa5

27 files changed

+211
-302
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

+79-103
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
2323
sess: &Session,
2424
arg: &GenericArg<'_>,
2525
kind: &'static str,
26+
possible_ordering_error: bool,
2627
help: Option<&str>,
2728
) {
2829
let mut err = struct_span_err!(
@@ -49,8 +50,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
4950
GenericArg::Const(_) => ParamKindOrd::Const { unordered },
5051
};
5152

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

151-
let mut args =
152-
generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable();
167+
let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter());
168+
let mut args = args_iter.clone().peekable();
153169

154170
// If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
155171
// If we later encounter a lifetime, we know that the arguments were provided in the
@@ -216,8 +232,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
216232
GenericParamDefKind::Const => {
217233
ParamKindOrd::Const {
218234
unordered: tcx
219-
.sess
220-
.features_untracked()
235+
.features()
221236
.const_generics,
222237
}
223238
}
@@ -237,6 +252,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
237252
tcx.sess,
238253
arg,
239254
kind.descr(),
255+
!args_iter.clone().is_sorted_by_key(|arg| match arg {
256+
GenericArg::Lifetime(_) => ParamKindOrd::Lifetime,
257+
GenericArg::Type(_) => ParamKindOrd::Type,
258+
GenericArg::Const(_) => ParamKindOrd::Const {
259+
unordered: tcx.features().const_generics,
260+
},
261+
}),
240262
Some(&format!(
241263
"reorder the arguments: {}: `<{}>`",
242264
param_types_present
@@ -288,7 +310,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
288310
assert_eq!(kind, "lifetime");
289311
let provided =
290312
force_infer_lt.expect("lifetimes ought to have been inferred");
291-
Self::generic_arg_mismatch_err(tcx.sess, provided, kind, None);
313+
Self::generic_arg_mismatch_err(tcx.sess, provided, kind, false, None);
292314
}
293315

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

@@ -384,11 +407,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
384407
// For kinds without defaults (e.g.., lifetimes), `required == permitted`.
385408
// For other kinds (i.e., types), `permitted` may be greater than `required`.
386409
if required <= provided && provided <= permitted {
387-
return Ok(());
410+
return true;
388411
}
389412

390413
if silent {
391-
return Err((0i32, None));
414+
return false;
392415
}
393416

394417
// Unfortunately lifetime and type parameter mismatches are typically styled
@@ -404,25 +427,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
404427
(required, "")
405428
};
406429

407-
let (spans, label) = if required == permitted && provided > permitted {
430+
let (spans, labels) = if provided > permitted {
408431
// In the case when the user has provided too many arguments,
409432
// we want to point to the unexpected arguments.
410-
let spans: Vec<Span> = args.args[offset + permitted..offset + provided]
433+
let (spans, labels): (Vec<Span>, Vec<String>) = args.args
434+
[offset + permitted..offset + provided]
411435
.iter()
412-
.map(|arg| arg.span())
413-
.collect();
436+
.map(|arg| (arg.span(), format!("unexpected {} argument", arg.short_descr())))
437+
.unzip();
414438
unexpected_spans.extend(spans.clone());
415-
(spans, format!("unexpected {} argument", kind))
439+
(spans, labels)
416440
} else {
417441
(
418442
vec![span],
419-
format!(
443+
vec![format!(
420444
"expected {}{} {} argument{}",
421445
quantifier,
422446
bound,
423447
kind,
424448
pluralize!(bound),
425-
),
449+
)],
426450
)
427451
};
428452

@@ -434,105 +458,57 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
434458
),
435459
DiagnosticId::Error("E0107".into()),
436460
);
437-
for span in spans {
461+
for (span, label) in spans.into_iter().zip(labels) {
438462
err.span_label(span, label.as_str());
439463
}
440-
441-
assert_ne!(bound, provided);
442-
Err((bound as i32 - provided as i32, Some(err)))
464+
err.emit();
465+
false
443466
};
444467

445468
let mut unexpected_spans = vec![];
446469

447-
let mut lifetime_count_correct = Ok(());
448-
if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
449-
lifetime_count_correct = check_kind_count(
450-
"lifetime",
451-
param_counts.lifetimes,
452-
param_counts.lifetimes,
453-
arg_counts.lifetimes,
454-
0,
455-
&mut unexpected_spans,
456-
explicit_late_bound == ExplicitLateBound::Yes,
457-
);
458-
}
459-
460-
// FIXME(const_generics:defaults)
461-
let mut const_count_correct = Ok(());
462-
if !infer_args || arg_counts.consts > param_counts.consts {
463-
const_count_correct = check_kind_count(
464-
"const",
465-
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-
);
472-
}
473-
474-
// Note that type errors are currently be emitted *after* const errors.
475-
let mut type_count_correct = Ok(());
476-
if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize
477-
{
478-
type_count_correct = check_kind_count(
479-
"type",
480-
param_counts.types - defaults.types - has_self as usize,
481-
param_counts.types - has_self as usize,
482-
arg_counts.types,
483-
arg_counts.lifetimes,
484-
&mut unexpected_spans,
485-
false,
486-
);
487-
}
488-
489-
// Emit a help message if it's possible that a type could be surrounded in braces
490-
if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct {
491-
if let Err((_, Some(ref mut type_err))) = type_count_correct {
492-
let possible_matches = args.args[arg_counts.lifetimes..]
493-
.iter()
494-
.filter(|arg| {
495-
matches!(
496-
arg,
497-
GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. })
498-
)
499-
})
500-
.take(c_mismatch.max(0) as usize);
501-
for arg in possible_matches {
502-
let suggestions = vec![
503-
(arg.span().shrink_to_lo(), String::from("{ ")),
504-
(arg.span().shrink_to_hi(), String::from(" }")),
505-
];
506-
type_err.multipart_suggestion(
507-
"If this generic argument was intended as a const parameter, \
508-
try surrounding it with braces:",
509-
suggestions,
510-
Applicability::MaybeIncorrect,
511-
);
512-
}
513-
}
514-
}
470+
let lifetime_count_correct = check_kind_count(
471+
"lifetime",
472+
if infer_lifetimes { 0 } else { param_counts.lifetimes },
473+
param_counts.lifetimes,
474+
arg_counts.lifetimes,
475+
0,
476+
&mut unexpected_spans,
477+
explicit_late_bound == ExplicitLateBound::Yes,
478+
);
515479

516-
let emit_correct =
517-
|correct: Result<(), (_, Option<rustc_errors::DiagnosticBuilder<'_>>)>| match correct {
518-
Ok(()) => Ok(()),
519-
Err((_, None)) => Err(()),
520-
Err((_, Some(mut err))) => {
521-
err.emit();
522-
Err(())
523-
}
524-
};
480+
let kind_str = if param_counts.consts + arg_counts.consts == 0 {
481+
"type"
482+
} else if named_type_param_count + arg_counts.types == 0 {
483+
"const"
484+
} else {
485+
"generic"
486+
};
525487

526-
let arg_count_correct = emit_correct(lifetime_count_correct)
527-
.and(emit_correct(const_count_correct))
528-
.and(emit_correct(type_count_correct));
488+
let arg_count_correct = check_kind_count(
489+
kind_str,
490+
if infer_args {
491+
0
492+
} else {
493+
param_counts.consts + named_type_param_count - defaults.types
494+
},
495+
param_counts.consts + named_type_param_count,
496+
arg_counts.consts + arg_counts.types,
497+
arg_counts.lifetimes,
498+
&mut unexpected_spans,
499+
false,
500+
);
529501

530502
GenericArgCountResult {
531503
explicit_late_bound,
532-
correct: arg_count_correct.map_err(|()| GenericArgCountMismatch {
533-
reported: Some(ErrorReported),
534-
invalid_args: unexpected_spans,
535-
}),
504+
correct: if lifetime_count_correct && arg_count_correct {
505+
Ok(())
506+
} else {
507+
Err(GenericArgCountMismatch {
508+
reported: Some(ErrorReported),
509+
invalid_args: unexpected_spans,
510+
})
511+
},
536512
}
537513
}
538514

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)