Skip to content

Commit 08324fe

Browse files
committed
Auto merge of #74953 - JulianKnodt:master, r=lcnr
Remove restriction on type parameters preceding consts w/ feature const-generics Removed the restriction on type parameters preceding const parameters when the feature const-generics is enabled. Builds on #74676, which deals with unsorted generic parameters. This just lifts the check in lowering the AST to HIR that permits consts and types to be reordered with respect to each other. Lifetimes still must precede both This change is not intended for min-const-generics, and is gated behind the `#![feature(const_generics)]`. One thing is that it also permits type parameters without a default to come after consts, which I expected to not work, and was hoping to get more guidance on whether that should be permitted or how to prevent it otherwise. I did not go through the RFC process for this pull request because there was prior work to get this feature added. In the previous PR that was cited, work was done to enable this change. r? @lcnr
2 parents 4c336d4 + 64f6437 commit 08324fe

18 files changed

+190
-75
lines changed

src/librustc_ast/ast.rs

+34-3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use rustc_span::source_map::{respan, Spanned};
3535
use rustc_span::symbol::{kw, sym, Ident, Symbol};
3636
use rustc_span::{Span, DUMMY_SP};
3737

38+
use std::cmp::Ordering;
3839
use std::convert::TryFrom;
3940
use std::fmt;
4041
use std::iter;
@@ -309,19 +310,49 @@ pub type GenericBounds = Vec<GenericBound>;
309310
/// Specifies the enforced ordering for generic parameters. In the future,
310311
/// if we wanted to relax this order, we could override `PartialEq` and
311312
/// `PartialOrd`, to allow the kinds to be unordered.
312-
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
313+
#[derive(Hash, Clone, Copy)]
313314
pub enum ParamKindOrd {
314315
Lifetime,
315316
Type,
316-
Const,
317+
// `unordered` is only `true` if `sess.has_features().const_generics`
318+
// is active. Specifically, if it's only `min_const_generics`, it will still require
319+
// ordering consts after types.
320+
Const { unordered: bool },
321+
}
322+
323+
impl Ord for ParamKindOrd {
324+
fn cmp(&self, other: &Self) -> Ordering {
325+
use ParamKindOrd::*;
326+
let to_int = |v| match v {
327+
Lifetime => 0,
328+
Type | Const { unordered: true } => 1,
329+
// technically both consts should be ordered equally,
330+
// but only one is ever encountered at a time, so this is
331+
// fine.
332+
Const { unordered: false } => 2,
333+
};
334+
335+
to_int(*self).cmp(&to_int(*other))
336+
}
337+
}
338+
impl PartialOrd for ParamKindOrd {
339+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
340+
Some(self.cmp(other))
341+
}
342+
}
343+
impl PartialEq for ParamKindOrd {
344+
fn eq(&self, other: &Self) -> bool {
345+
self.cmp(other) == Ordering::Equal
346+
}
317347
}
348+
impl Eq for ParamKindOrd {}
318349

319350
impl fmt::Display for ParamKindOrd {
320351
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321352
match self {
322353
ParamKindOrd::Lifetime => "lifetime".fmt(f),
323354
ParamKindOrd::Type => "type".fmt(f),
324-
ParamKindOrd::Const => "const".fmt(f),
355+
ParamKindOrd::Const { .. } => "const".fmt(f),
325356
}
326357
}
327358
}

src/librustc_ast_passes/ast_validation.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -773,13 +773,13 @@ fn validate_generic_param_order<'a>(
773773
err.span_suggestion(
774774
span,
775775
&format!(
776-
"reorder the parameters: lifetimes, then types{}",
777-
if sess.features_untracked().const_generics
778-
|| sess.features_untracked().min_const_generics
779-
{
780-
", then consts"
776+
"reorder the parameters: lifetimes{}",
777+
if sess.features_untracked().const_generics {
778+
", then consts and types"
779+
} else if sess.features_untracked().min_const_generics {
780+
", then consts, then types"
781781
} else {
782-
""
782+
", then types"
783783
},
784784
),
785785
ordered_params.clone(),
@@ -1156,7 +1156,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11561156
GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident),
11571157
GenericParamKind::Const { ref ty, kw_span: _ } => {
11581158
let ty = pprust::ty_to_string(ty);
1159-
(ParamKindOrd::Const, Some(format!("const {}: {}", param.ident, ty)))
1159+
let unordered = self.session.features_untracked().const_generics;
1160+
(
1161+
ParamKindOrd::Const { unordered },
1162+
Some(format!("const {}: {}", param.ident, ty)),
1163+
)
11601164
}
11611165
};
11621166
(kind, Some(&*param.bounds), param.ident.span, ident)

src/librustc_typeck/astconv.rs

+18-10
Original file line numberDiff line numberDiff line change
@@ -489,28 +489,31 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
489489
kind,
490490
);
491491

492+
let unordered = sess.features_untracked().const_generics;
492493
let kind_ord = match kind {
493494
"lifetime" => ParamKindOrd::Lifetime,
494495
"type" => ParamKindOrd::Type,
495-
"constant" => ParamKindOrd::Const,
496+
"constant" => ParamKindOrd::Const { unordered },
496497
// It's more concise to match on the string representation, though it means
497498
// the match is non-exhaustive.
498499
_ => bug!("invalid generic parameter kind {}", kind),
499500
};
500501
let arg_ord = match arg {
501502
GenericArg::Lifetime(_) => ParamKindOrd::Lifetime,
502503
GenericArg::Type(_) => ParamKindOrd::Type,
503-
GenericArg::Const(_) => ParamKindOrd::Const,
504+
GenericArg::Const(_) => ParamKindOrd::Const { unordered },
504505
};
505506

506-
// This note will be true as long as generic parameters are strictly ordered by their kind.
507-
let (first, last) =
508-
if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) };
509-
err.note(&format!("{} arguments must be provided before {} arguments", first, last));
510-
511-
if let Some(help) = help {
512-
err.help(help);
507+
// This note is only true when generic parameters are strictly ordered by their kind.
508+
if kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal {
509+
let (first, last) =
510+
if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) };
511+
err.note(&format!("{} arguments must be provided before {} arguments", first, last));
512+
if let Some(help) = help {
513+
err.help(help);
514+
}
513515
}
516+
514517
err.emit();
515518
}
516519

@@ -672,7 +675,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
672675
ParamKindOrd::Type
673676
}
674677
GenericParamDefKind::Const => {
675-
ParamKindOrd::Const
678+
ParamKindOrd::Const {
679+
unordered: tcx
680+
.sess
681+
.features_untracked()
682+
.const_generics,
683+
}
676684
}
677685
},
678686
param,

src/test/ui/const-generics/argument_order.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
#![feature(const_generics)]
2-
//~^ WARN the feature `const_generics` is incomplete
2+
#![allow(incomplete_features)]
33

4-
struct Bad<const N: usize, T> { //~ ERROR type parameters must be declared prior
4+
struct Bad<const N: usize, T> {
55
arr: [u8; { N }],
66
another: T,
77
}
88

99
struct AlsoBad<const N: usize, 'a, T, 'b, const M: usize, U> {
10-
//~^ ERROR type parameters must be declared prior
11-
//~| ERROR lifetime parameters must be declared prior
10+
//~^ ERROR lifetime parameters must be declared prior
1211
a: &'a T,
1312
b: &'b U,
1413
}
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,18 @@
1-
error: type parameters must be declared prior to const parameters
2-
--> $DIR/argument_order.rs:4:28
3-
|
4-
LL | struct Bad<const N: usize, T> {
5-
| -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `<T, const N: usize>`
6-
71
error: lifetime parameters must be declared prior to const parameters
82
--> $DIR/argument_order.rs:9:32
93
|
104
LL | struct AlsoBad<const N: usize, 'a, T, 'b, const M: usize, U> {
11-
| -----------------^^-----^^-------------------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T, U, const N: usize, const M: usize>`
12-
13-
error: type parameters must be declared prior to const parameters
14-
--> $DIR/argument_order.rs:9:36
15-
|
16-
LL | struct AlsoBad<const N: usize, 'a, T, 'b, const M: usize, U> {
17-
| ---------------------^----------------------^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T, U, const N: usize, const M: usize>`
18-
19-
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
20-
--> $DIR/argument_order.rs:1:12
21-
|
22-
LL | #![feature(const_generics)]
23-
| ^^^^^^^^^^^^^^
24-
|
25-
= note: `#[warn(incomplete_features)]` on by default
26-
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
5+
| -----------------^^-----^^-------------------- help: reorder the parameters: lifetimes, then consts and types: `<'a, 'b, const N: usize, T, const M: usize, U>`
276

287
error[E0747]: lifetime provided when a type was expected
29-
--> $DIR/argument_order.rs:17:23
8+
--> $DIR/argument_order.rs:16:23
309
|
3110
LL | let _: AlsoBad<7, 'static, u32, 'static, 17, u16>;
3211
| ^^^^^^^
3312
|
3413
= note: lifetime arguments must be provided before type arguments
35-
= help: reorder the arguments: lifetimes, then types, then consts: `<'a, 'b, T, U, N, M>`
14+
= help: reorder the arguments: lifetimes, then consts: `<'a, 'b, N, T, M, U>`
3615

37-
error: aborting due to 4 previous errors; 1 warning emitted
16+
error: aborting due to 2 previous errors
3817

3918
For more information about this error, try `rustc --explain E0747`.

src/test/ui/const-generics/const-arg-type-arg-misordered.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![feature(const_generics)]
2-
//~^ WARN the feature `const_generics` is incomplete
2+
#![allow(incomplete_features)]
33

44
type Array<T, const N: usize> = [T; N];
55

Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
1-
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
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-
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
9-
101
error[E0747]: constant provided when a type was expected
112
--> $DIR/const-arg-type-arg-misordered.rs:6:35
123
|
134
LL | fn foo<const N: usize>() -> Array<N, ()> {
145
| ^
15-
|
16-
= note: type arguments must be provided before constant arguments
17-
= help: reorder the arguments: types, then consts: `<T, N>`
186

19-
error: aborting due to previous error; 1 warning emitted
7+
error: aborting due to previous error
208

219
For more information about this error, try `rustc --explain E0747`.

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

+1-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ fn bar<const X: (), 'a>(_: &'a ()) {
55
//~^ ERROR lifetime parameters must be declared prior to const parameters
66
}
77

8-
fn foo<const X: (), T>(_: &T) {
9-
//~^ ERROR type parameters must be declared prior to const parameters
10-
}
8+
fn foo<const X: (), T>(_: &T) {}
119

1210
fn main() {}

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

+2-8
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,7 @@ error: lifetime parameters must be declared prior to const parameters
22
--> $DIR/const-param-before-other-params.rs:4:21
33
|
44
LL | fn bar<const X: (), 'a>(_: &'a ()) {
5-
| --------------^^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, const X: ()>`
5+
| --------------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, const X: ()>`
66

7-
error: type parameters must be declared prior to const parameters
8-
--> $DIR/const-param-before-other-params.rs:8:21
9-
|
10-
LL | fn foo<const X: (), T>(_: &T) {
11-
| --------------^- help: reorder the parameters: lifetimes, then types, then consts: `<T, const X: ()>`
12-
13-
error: aborting due to 2 previous errors
7+
error: aborting due to previous error
148

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// run-pass
2+
// Checks a complicated usage of unordered params
3+
4+
#![feature(const_generics)]
5+
#![allow(incomplete_features)]
6+
#![allow(dead_code)]
7+
8+
struct NestedArrays<'a, const N: usize, A: 'a, const M: usize, T:'a =u32> {
9+
args: &'a [&'a [T; M]; N],
10+
specifier: A,
11+
}
12+
13+
fn main() {
14+
let array = [1, 2, 3];
15+
let nest = [&array];
16+
let _ = NestedArrays {
17+
args: &nest,
18+
specifier: true,
19+
};
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Checks that lifetimes cannot be interspersed between consts and types.
2+
3+
#![feature(const_generics)]
4+
#![allow(incomplete_features)]
5+
6+
struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
7+
//~^ Error lifetime parameters must be declared prior to const parameters
8+
9+
struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
10+
//~^ Error lifetime parameters must be declared prior to type parameters
11+
12+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: lifetime parameters must be declared prior to const parameters
2+
--> $DIR/intermixed-lifetime.rs:6:28
3+
|
4+
LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
5+
| -----------------^^---------- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T>`
6+
7+
error: lifetime parameters must be declared prior to type parameters
8+
--> $DIR/intermixed-lifetime.rs:9:37
9+
|
10+
LL | struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
11+
| --------------------------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T>`
12+
13+
error: aborting due to 2 previous errors
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: type parameters must be declared prior to const parameters
2+
--> $DIR/needs-feature.rs:10:26
3+
|
4+
LL | struct A<const N: usize, T=u32>(T);
5+
| -----------------^----- help: reorder the parameters: lifetimes, then consts, then types: `<T, const N: usize>`
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: type parameters must be declared prior to const parameters
2+
--> $DIR/needs-feature.rs:10:26
3+
|
4+
LL | struct A<const N: usize, T=u32>(T);
5+
| -----------------^----- help: reorder the parameters: lifetimes, then types: `<T, const N: usize>`
6+
7+
error[E0658]: const generics are unstable
8+
--> $DIR/needs-feature.rs:10:16
9+
|
10+
LL | struct A<const N: usize, T=u32>(T);
11+
| ^
12+
|
13+
= note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
14+
= help: add `#![feature(min_const_generics)]` to the crate attributes to enable
15+
16+
error: aborting due to 2 previous errors
17+
18+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//[full] run-pass
2+
// Verifies that having generic parameters after constants is not permitted without the
3+
// `const_generics` feature.
4+
// revisions: none min full
5+
6+
#![cfg_attr(full, feature(const_generics))]
7+
#![cfg_attr(full, allow(incomplete_features))]
8+
#![cfg_attr(min, feature(min_const_generics))]
9+
10+
struct A<const N: usize, T=u32>(T);
11+
//[none]~^ ERROR type parameters must be declared prior
12+
//[none]~| ERROR const generics are unstable
13+
//[min]~^^^ ERROR type parameters must be declared prior
14+
15+
fn main() {
16+
let _: A<3> = A(0);
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// run-pass
2+
// Checks some basic test cases for defaults.
3+
#![feature(const_generics)]
4+
#![allow(incomplete_features)]
5+
#![allow(dead_code)]
6+
7+
struct FixedOutput<'a, const N: usize, T=u32> {
8+
out: &'a [T; N],
9+
}
10+
11+
trait FixedOutputter {
12+
fn out(&self) -> FixedOutput<'_, 10>;
13+
}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// run-pass
2+
// Verifies that having generic parameters after constants is permitted
3+
4+
#![feature(const_generics)]
5+
#![allow(incomplete_features)]
6+
7+
#[allow(dead_code)]
8+
struct A<const N: usize, T>(T);
9+
10+
fn main() {}

src/test/ui/issues/issue-59508-1.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: lifetime parameters must be declared prior to type parameters
22
--> $DIR/issue-59508-1.rs:12:25
33
|
44
LL | pub fn do_things<T, 'a, 'b: 'a>() {
5-
| ----^^--^^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b: 'a, T>`
5+
| ----^^--^^----- help: reorder the parameters: lifetimes, then consts and types: `<'a, 'b: 'a, T>`
66

77
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
88
--> $DIR/issue-59508-1.rs:2:12

0 commit comments

Comments
 (0)