Skip to content

Commit 039045c

Browse files
committed
Move generic arg / param validation to create_substs_for_generic_args
1 parent 750e673 commit 039045c

21 files changed

+192
-110
lines changed

src/librustc_ast_passes/ast_validation.rs

Lines changed: 16 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -594,23 +594,15 @@ impl<'a> AstValidator<'a> {
594594
}
595595
}
596596

597-
enum GenericPosition {
598-
Param,
599-
Arg,
600-
}
601-
602-
fn validate_generics_order<'a>(
597+
fn validate_generic_param_order<'a>(
603598
sess: &Session,
604599
handler: &rustc_errors::Handler,
605600
generics: impl Iterator<Item = (ParamKindOrd, Option<&'a [GenericBound]>, Span, Option<String>)>,
606-
pos: GenericPosition,
607601
span: Span,
608602
) {
609603
let mut max_param: Option<ParamKindOrd> = None;
610604
let mut out_of_order = FxHashMap::default();
611605
let mut param_idents = vec![];
612-
let mut found_type = false;
613-
let mut found_const = false;
614606

615607
for (kind, bounds, span, ident) in generics {
616608
if let Some(ident) = ident {
@@ -624,11 +616,6 @@ fn validate_generics_order<'a>(
624616
}
625617
Some(_) | None => *max_param = Some(kind),
626618
};
627-
match kind {
628-
ParamKindOrd::Type => found_type = true,
629-
ParamKindOrd::Const => found_const = true,
630-
_ => {}
631-
}
632619
}
633620

634621
let mut ordered_params = "<".to_string();
@@ -651,42 +638,26 @@ fn validate_generics_order<'a>(
651638
}
652639
ordered_params += ">";
653640

654-
let pos_str = match pos {
655-
GenericPosition::Param => "parameter",
656-
GenericPosition::Arg => "argument",
657-
};
658-
659641
for (param_ord, (max_param, spans)) in &out_of_order {
660-
let mut err = handler.struct_span_err(
661-
spans.clone(),
662-
&format!(
663-
"{} {pos}s must be declared prior to {} {pos}s",
664-
param_ord,
665-
max_param,
666-
pos = pos_str,
667-
),
668-
);
669-
if let GenericPosition::Param = pos {
670-
err.span_suggestion(
671-
span,
642+
let mut err =
643+
handler.struct_span_err(
644+
spans.clone(),
672645
&format!(
673-
"reorder the {}s: lifetimes, then types{}",
674-
pos_str,
675-
if sess.features_untracked().const_generics { ", then consts" } else { "" },
646+
"{} parameters must be declared prior to {} parameters",
647+
param_ord, max_param,
676648
),
677-
ordered_params.clone(),
678-
Applicability::MachineApplicable,
679649
);
680-
}
650+
err.span_suggestion(
651+
span,
652+
&format!(
653+
"reorder the parameters: lifetimes, then types{}",
654+
if sess.features_untracked().const_generics { ", then consts" } else { "" },
655+
),
656+
ordered_params.clone(),
657+
Applicability::MachineApplicable,
658+
);
681659
err.emit();
682660
}
683-
684-
// FIXME(const_generics): we shouldn't have to abort here at all, but we currently get ICEs
685-
// if we don't. Const parameters and type parameters can currently conflict if they
686-
// are out-of-order.
687-
if !out_of_order.is_empty() && found_type && found_const {
688-
FatalError.raise();
689-
}
690661
}
691662

692663
impl<'a> Visitor<'a> for AstValidator<'a> {
@@ -1000,24 +971,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
1000971
match *generic_args {
1001972
GenericArgs::AngleBracketed(ref data) => {
1002973
walk_list!(self, visit_generic_arg, &data.args);
1003-
validate_generics_order(
1004-
self.session,
1005-
self.err_handler(),
1006-
data.args.iter().map(|arg| {
1007-
(
1008-
match arg {
1009-
GenericArg::Lifetime(..) => ParamKindOrd::Lifetime,
1010-
GenericArg::Type(..) => ParamKindOrd::Type,
1011-
GenericArg::Const(..) => ParamKindOrd::Const,
1012-
},
1013-
None,
1014-
arg.span(),
1015-
None,
1016-
)
1017-
}),
1018-
GenericPosition::Arg,
1019-
generic_args.span(),
1020-
);
1021974

1022975
// Type bindings such as `Item = impl Debug` in `Iterator<Item = Debug>`
1023976
// are allowed to contain nested `impl Trait`.
@@ -1054,7 +1007,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10541007
}
10551008
}
10561009

1057-
validate_generics_order(
1010+
validate_generic_param_order(
10581011
self.session,
10591012
self.err_handler(),
10601013
generics.params.iter().map(|param| {
@@ -1069,7 +1022,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10691022
};
10701023
(kind, Some(&*param.bounds), param.ident.span, ident)
10711024
}),
1072-
GenericPosition::Param,
10731025
generics.span,
10741026
);
10751027

src/librustc_error_codes/error_codes.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ E0743: include_str!("./error_codes/E0743.md"),
417417
E0744: include_str!("./error_codes/E0744.md"),
418418
E0745: include_str!("./error_codes/E0745.md"),
419419
E0746: include_str!("./error_codes/E0746.md"),
420+
E0747: include_str!("./error_codes/E0747.md"),
420421
;
421422
// E0006, // merged with E0005
422423
// E0008, // cannot bind by-move into a pattern guard
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Generic arguments must be provided in the same order as the corresponding generic
2+
parameters are declared.
3+
4+
Erroneous code example:
5+
6+
```compile_fail,E0747
7+
struct S<'a, T>(&'a T);
8+
9+
type X = S<(), 'static>; // error: the type argument is provided before the lifetime argument
10+
```

src/librustc_typeck/astconv.rs

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
481481
parent_substs: &[subst::GenericArg<'tcx>],
482482
has_self: bool,
483483
self_ty: Option<Ty<'tcx>>,
484+
arg_count_mismatch: bool,
484485
args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs<'b>>, bool),
485486
provided_kind: impl Fn(&GenericParamDef, &GenericArg<'_>) -> subst::GenericArg<'tcx>,
486487
mut inferred_kind: impl FnMut(
@@ -504,7 +505,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
504505
// methods in `subst.rs`, so that we can iterate over the arguments and
505506
// parameters in lock-step linearly, instead of trying to match each pair.
506507
let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count);
507-
508508
// Iterate over each segment of the path.
509509
while let Some((def_id, defs)) = stack.pop() {
510510
let mut params = defs.params.iter().peekable();
@@ -541,6 +541,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
541541
let mut args =
542542
generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable();
543543

544+
let arg_kind = |arg| match arg {
545+
&GenericArg::Lifetime(_) => "lifetime",
546+
&GenericArg::Type(_) => "type",
547+
&GenericArg::Const(_) => "constant",
548+
};
549+
550+
// If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
551+
// If we later encounter a lifetime, we know that the arguments were provided in the
552+
// wrong order. `force_infer_lt` records the type or const that forced lifetimes to be
553+
// inferred, so we can use it for diagnostics later.
554+
let mut force_infer_lt = None;
555+
544556
loop {
545557
// We're going to iterate through the generic arguments that the user
546558
// provided, matching them with the generic parameters we expect.
@@ -561,28 +573,74 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
561573
// We expected a lifetime argument, but got a type or const
562574
// argument. That means we're inferring the lifetimes.
563575
substs.push(inferred_kind(None, param, infer_args));
576+
force_infer_lt = Some(arg);
564577
params.next();
565578
}
566-
(_, _) => {
579+
(_, kind) => {
567580
// We expected one kind of parameter, but the user provided
568-
// another. This is an error, but we need to handle it
569-
// gracefully so we can report sensible errors.
570-
// In this case, we're simply going to infer this argument.
571-
args.next();
581+
// another. This is an error. However, if we already know that
582+
// the arguments don't match up with the parameters, we won't issue
583+
// an additional error, as the user already knows what's wrong.
584+
if !arg_count_mismatch {
585+
let param_kind = match kind {
586+
GenericParamDefKind::Lifetime => "lifetime",
587+
GenericParamDefKind::Type { .. } => "type",
588+
GenericParamDefKind::Const => "constant",
589+
};
590+
struct_span_err!(
591+
tcx.sess,
592+
arg.span(),
593+
E0747,
594+
"{} provided when a {} was expected",
595+
arg_kind(arg),
596+
param_kind,
597+
)
598+
.emit();
599+
}
600+
601+
// We've reported the error, but we want to make sure that this
602+
// problem doesn't bubble down and create additional, irrelevant
603+
// errors. In this case, we're simply going to ignore the argument
604+
// and any following arguments. The rest of the parameters will be
605+
// inferred.
606+
while args.next().is_some() {}
572607
}
573608
}
574609
}
575-
(Some(_), None) => {
610+
(Some(&arg), None) => {
576611
// We should never be able to reach this point with well-formed input.
577-
// Getting to this point means the user supplied more arguments than
578-
// there are parameters.
579-
args.next();
612+
// There are two situations in which we can encounter this issue.
613+
//
614+
// 1. The number of arguments is incorrect. In this case, an error
615+
// will already have been emitted, and we can ignore it. This case
616+
// also occurs when late-bound lifetime parameters are present, yet
617+
// the lifetime arguments have also been explicitly specified by the
618+
// user.
619+
// 2. We've inferred some lifetimes, which have been provided later (i.e.
620+
// after a type or const). We want to throw an error in this case.
621+
622+
if !arg_count_mismatch {
623+
let kind = arg_kind(arg);
624+
assert_eq!(kind, "lifetime");
625+
let provided =
626+
force_infer_lt.expect("lifetimes ought to have been inferred");
627+
struct_span_err!(
628+
tcx.sess,
629+
provided.span(),
630+
E0747,
631+
"{} provided when a {} was expected",
632+
arg_kind(provided),
633+
kind,
634+
)
635+
.emit();
636+
}
637+
638+
break;
580639
}
581640
(None, Some(&param)) => {
582641
// If there are fewer arguments than parameters, it means
583642
// we're inferring the remaining arguments.
584643
substs.push(inferred_kind(Some(&substs), param, infer_args));
585-
args.next();
586644
params.next();
587645
}
588646
(None, None) => break,
@@ -658,7 +716,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
658716
assert!(self_ty.is_none() && parent_substs.is_empty());
659717
}
660718

661-
let (_, potential_assoc_types) = Self::check_generic_arg_count(
719+
let (arg_count_mismatch, potential_assoc_types) = Self::check_generic_arg_count(
662720
tcx,
663721
span,
664722
&generic_params,
@@ -691,6 +749,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
691749
parent_substs,
692750
self_ty.is_some(),
693751
self_ty,
752+
arg_count_mismatch,
694753
// Provide the generic args, and whether types should be inferred.
695754
|did| {
696755
if did == def_id {

src/librustc_typeck/check/method/confirm.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
299299
// If they were not explicitly supplied, just construct fresh
300300
// variables.
301301
let generics = self.tcx.generics_of(pick.item.def_id);
302-
AstConv::check_generic_arg_count_for_call(
302+
let arg_count_mismatch = AstConv::check_generic_arg_count_for_call(
303303
self.tcx, self.span, &generics, &seg, true, // `is_method_call`
304304
);
305305

@@ -313,6 +313,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
313313
parent_substs,
314314
false,
315315
None,
316+
arg_count_mismatch,
316317
// Provide the generic args, and whether types should be inferred.
317318
|def_id| {
318319
// The last component of the returned tuple here is unimportant.

src/librustc_typeck/check/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5520,6 +5520,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
55205520
&[][..],
55215521
has_self,
55225522
self_ty,
5523+
!infer_args_for_err.is_empty(),
55235524
// Provide the generic args, and whether types should be inferred.
55245525
|def_id| {
55255526
if let Some(&PathSeg(_, index)) =

src/librustc_typeck/collect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics {
12691269

12701270
let object_lifetime_defaults = tcx.object_lifetime_defaults(hir_id);
12711271

1272-
// Now create the real type parameters.
1272+
// Now create the real type and const parameters.
12731273
let type_start = own_start - has_self as u32 + params.len() as u32;
12741274
let mut i = 0;
12751275
params.extend(ast_generics.params.iter().filter_map(|param| {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![feature(const_generics)]
2+
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
3+
4+
type Array<T, const N: usize> = [T; N];
5+
6+
fn foo<const N: usize>() -> Array<N, ()> { //~ ERROR constant provided when a type was expected
7+
unimplemented!()
8+
}
9+
10+
fn main() {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
2+
--> $DIR/const-arg-type-arg-misordered.rs:1:12
3+
|
4+
LL | #![feature(const_generics)]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
9+
error[E0747]: constant provided when a type was expected
10+
--> $DIR/const-arg-type-arg-misordered.rs:6:35
11+
|
12+
LL | fn foo<const N: usize>() -> Array<N, ()> {
13+
| ^
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0747`.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// check-pass
2+
3+
#![feature(const_generics)]
4+
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
5+
6+
struct Foo<const A: usize, const B: usize>;
7+
8+
impl<const A: usize> Foo<1, A> {} // ok
9+
10+
fn main() {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
2+
--> $DIR/const-param-after-const-literal-arg.rs:3:12
3+
|
4+
LL | #![feature(const_generics)]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+

src/test/ui/const-generics/const-param-before-other-params.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![feature(const_generics)]
2+
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
23

34
fn bar<const X: (), 'a>(_: &'a ()) {
45
//~^ ERROR lifetime parameters must be declared prior to const parameters

0 commit comments

Comments
 (0)