Skip to content
37 changes: 34 additions & 3 deletions src/librustc_ast/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};

use std::cmp::Ordering;
use std::convert::TryFrom;
use std::fmt;
use std::iter;
Expand Down Expand Up @@ -309,19 +310,49 @@ pub type GenericBounds = Vec<GenericBound>;
/// Specifies the enforced ordering for generic parameters. In the future,
/// if we wanted to relax this order, we could override `PartialEq` and
/// `PartialOrd`, to allow the kinds to be unordered.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
#[derive(Hash, Clone, Copy)]
pub enum ParamKindOrd {
Lifetime,
Type,
Const,
// `unordered` is only `true` if `sess.has_features().const_generics`
// is active. Specifically, if it's only `min_const_generics`, it will still require
// ordering consts after types.
Const { unordered: bool },
}

impl Ord for ParamKindOrd {
fn cmp(&self, other: &Self) -> Ordering {
use ParamKindOrd::*;
let to_int = |v| match v {
Lifetime => 0,
Type | Const { unordered: true } => 1,
// technically both consts should be ordered equally,
// but only one is ever encountered at a time, so this is
// fine.
Const { unordered: false } => 2,
};

to_int(*self).cmp(&to_int(*other))
}
}
impl PartialOrd for ParamKindOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for ParamKindOrd {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for ParamKindOrd {}

impl fmt::Display for ParamKindOrd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParamKindOrd::Lifetime => "lifetime".fmt(f),
ParamKindOrd::Type => "type".fmt(f),
ParamKindOrd::Const => "const".fmt(f),
ParamKindOrd::Const { .. } => "const".fmt(f),
}
}
}
Expand Down
18 changes: 11 additions & 7 deletions src/librustc_ast_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,13 +775,13 @@ fn validate_generic_param_order<'a>(
err.span_suggestion(
span,
&format!(
"reorder the parameters: lifetimes, then types{}",
if sess.features_untracked().const_generics
|| sess.features_untracked().min_const_generics
{
", then consts"
"reorder the parameters: lifetimes{}",
if sess.features_untracked().const_generics {
", then consts and types"
} else if sess.features_untracked().min_const_generics {
", then consts, then types"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be ", then types, then consts"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ups

Copy link
Contributor

@lcnr lcnr Aug 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JulianKnodt do you want to open a PR for this? Otherwise I will quickly fix that

} else {
""
", then types"
},
),
ordered_params.clone(),
Expand Down Expand Up @@ -1158,7 +1158,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident),
GenericParamKind::Const { ref ty, kw_span: _ } => {
let ty = pprust::ty_to_string(ty);
(ParamKindOrd::Const, Some(format!("const {}: {}", param.ident, ty)))
let unordered = self.session.features_untracked().const_generics;
(
ParamKindOrd::Const { unordered },
Some(format!("const {}: {}", param.ident, ty)),
)
}
};
(kind, Some(&*param.bounds), param.ident.span, ident)
Expand Down
28 changes: 18 additions & 10 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,28 +489,31 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
kind,
);

let unordered = sess.features_untracked().const_generics;
let kind_ord = match kind {
"lifetime" => ParamKindOrd::Lifetime,
"type" => ParamKindOrd::Type,
"constant" => ParamKindOrd::Const,
"constant" => ParamKindOrd::Const { unordered },
// It's more concise to match on the string representation, though it means
// the match is non-exhaustive.
_ => bug!("invalid generic parameter kind {}", kind),
};
let arg_ord = match arg {
GenericArg::Lifetime(_) => ParamKindOrd::Lifetime,
GenericArg::Type(_) => ParamKindOrd::Type,
GenericArg::Const(_) => ParamKindOrd::Const,
GenericArg::Const(_) => ParamKindOrd::Const { unordered },
};

// This note will be true as long as generic parameters are strictly ordered by their kind.
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));

if let Some(help) = help {
err.help(help);
// This note is only true when generic parameters are strictly ordered by their kind.
if 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));
if let Some(help) = help {
err.help(help);
}
}

err.emit();
}

Expand Down Expand Up @@ -672,7 +675,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
ParamKindOrd::Type
}
GenericParamDefKind::Const => {
ParamKindOrd::Const
ParamKindOrd::Const {
unordered: tcx
.sess
.features_untracked()
.const_generics,
}
}
},
param,
Expand Down
7 changes: 3 additions & 4 deletions src/test/ui/const-generics/argument_order.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#![feature(const_generics)]
//~^ WARN the feature `const_generics` is incomplete
#![allow(incomplete_features)]

struct Bad<const N: usize, T> { //~ ERROR type parameters must be declared prior
struct Bad<const N: usize, T> {
arr: [u8; { N }],
another: T,
}

struct AlsoBad<const N: usize, 'a, T, 'b, const M: usize, U> {
//~^ ERROR type parameters must be declared prior
//~| ERROR lifetime parameters must be declared prior
//~^ ERROR lifetime parameters must be declared prior
a: &'a T,
b: &'b U,
}
Expand Down
29 changes: 4 additions & 25 deletions src/test/ui/const-generics/argument_order.stderr
Original file line number Diff line number Diff line change
@@ -1,39 +1,18 @@
error: type parameters must be declared prior to const parameters
--> $DIR/argument_order.rs:4:28
|
LL | struct Bad<const N: usize, T> {
| -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `<T, const N: usize>`

error: lifetime parameters must be declared prior to const parameters
--> $DIR/argument_order.rs:9:32
|
LL | struct AlsoBad<const N: usize, 'a, T, 'b, const M: usize, U> {
| -----------------^^-----^^-------------------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T, U, const N: usize, const M: usize>`

error: type parameters must be declared prior to const parameters
--> $DIR/argument_order.rs:9:36
|
LL | struct AlsoBad<const N: usize, 'a, T, 'b, const M: usize, U> {
| ---------------------^----------------------^- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b, T, U, const N: usize, const M: usize>`

warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/argument_order.rs:1:12
|
LL | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
| -----------------^^-----^^-------------------- help: reorder the parameters: lifetimes, then consts and types: `<'a, 'b, const N: usize, T, const M: usize, U>`

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

error: aborting due to 4 previous errors; 1 warning emitted
error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0747`.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![feature(const_generics)]
//~^ WARN the feature `const_generics` is incomplete
#![allow(incomplete_features)]

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

Expand Down
14 changes: 1 addition & 13 deletions src/test/ui/const-generics/const-arg-type-arg-misordered.stderr
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/const-arg-type-arg-misordered.rs:1:12
|
LL | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information

error[E0747]: constant provided when a type was expected
--> $DIR/const-arg-type-arg-misordered.rs:6:35
|
LL | fn foo<const N: usize>() -> Array<N, ()> {
| ^
|
= note: type arguments must be provided before constant arguments
= help: reorder the arguments: types, then consts: `<T, N>`

error: aborting due to previous error; 1 warning emitted
error: aborting due to previous error

For more information about this error, try `rustc --explain E0747`.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ fn bar<const X: (), 'a>(_: &'a ()) {
//~^ ERROR lifetime parameters must be declared prior to const parameters
}

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

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

error: type parameters must be declared prior to const parameters
--> $DIR/const-param-before-other-params.rs:8:21
|
LL | fn foo<const X: (), T>(_: &T) {
| --------------^- help: reorder the parameters: lifetimes, then types, then consts: `<T, const X: ()>`

error: aborting due to 2 previous errors
error: aborting due to previous error

20 changes: 20 additions & 0 deletions src/test/ui/const-generics/defaults/complex-unord-param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// run-pass
// Checks a complicated usage of unordered params

#![feature(const_generics)]
#![allow(incomplete_features)]
#![allow(dead_code)]

struct NestedArrays<'a, const N: usize, A: 'a, const M: usize, T:'a =u32> {
args: &'a [&'a [T; M]; N],
specifier: A,
}

fn main() {
let array = [1, 2, 3];
let nest = [&array];
let _ = NestedArrays {
args: &nest,
specifier: true,
};
}
12 changes: 12 additions & 0 deletions src/test/ui/const-generics/defaults/intermixed-lifetime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Checks that lifetimes cannot be interspersed between consts and types.

#![feature(const_generics)]
#![allow(incomplete_features)]

struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
//~^ Error lifetime parameters must be declared prior to const parameters

struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
//~^ Error lifetime parameters must be declared prior to type parameters

fn main() {}
14 changes: 14 additions & 0 deletions src/test/ui/const-generics/defaults/intermixed-lifetime.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: lifetime parameters must be declared prior to const parameters
--> $DIR/intermixed-lifetime.rs:6:28
|
LL | struct Foo<const N: usize, 'a, T = u32>(&'a (), T);
| -----------------^^---------- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T>`

error: lifetime parameters must be declared prior to type parameters
--> $DIR/intermixed-lifetime.rs:9:37
|
LL | struct Bar<const N: usize, T = u32, 'a>(&'a (), T);
| --------------------------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T>`

error: aborting due to 2 previous errors

8 changes: 8 additions & 0 deletions src/test/ui/const-generics/defaults/needs-feature.min.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: type parameters must be declared prior to const parameters
--> $DIR/needs-feature.rs:10:26
|
LL | struct A<const N: usize, T=u32>(T);
| -----------------^----- help: reorder the parameters: lifetimes, then consts, then types: `<T, const N: usize>`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message is wrong.


error: aborting due to previous error

18 changes: 18 additions & 0 deletions src/test/ui/const-generics/defaults/needs-feature.none.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: type parameters must be declared prior to const parameters
--> $DIR/needs-feature.rs:10:26
|
LL | struct A<const N: usize, T=u32>(T);
| -----------------^----- help: reorder the parameters: lifetimes, then types: `<T, const N: usize>`

error[E0658]: const generics are unstable
--> $DIR/needs-feature.rs:10:16
|
LL | struct A<const N: usize, T=u32>(T);
| ^
|
= note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
= help: add `#![feature(min_const_generics)]` to the crate attributes to enable

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0658`.
17 changes: 17 additions & 0 deletions src/test/ui/const-generics/defaults/needs-feature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//[full] run-pass
// Verifies that having generic parameters after constants is not permitted without the
// `const_generics` feature.
// revisions: none min full

#![cfg_attr(full, feature(const_generics))]
#![cfg_attr(full, allow(incomplete_features))]
#![cfg_attr(min, feature(min_const_generics))]

struct A<const N: usize, T=u32>(T);
//[none]~^ ERROR type parameters must be declared prior
//[none]~| ERROR const generics are unstable
//[min]~^^^ ERROR type parameters must be declared prior

fn main() {
let _: A<3> = A(0);
}
15 changes: 15 additions & 0 deletions src/test/ui/const-generics/defaults/simple-defaults.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// run-pass
// Checks some basic test cases for defaults.
#![feature(const_generics)]
#![allow(incomplete_features)]
#![allow(dead_code)]

struct FixedOutput<'a, const N: usize, T=u32> {
out: &'a [T; N],
}

trait FixedOutputter {
fn out(&self) -> FixedOutput<'_, 10>;
}

fn main() {}
10 changes: 10 additions & 0 deletions src/test/ui/const-generics/type-after-const-ok.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// run-pass
// Verifies that having generic parameters after constants is permitted

#![feature(const_generics)]
#![allow(incomplete_features)]

#[allow(dead_code)]
struct A<const N: usize, T>(T);

fn main() {}
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-59508-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: lifetime parameters must be declared prior to type parameters
--> $DIR/issue-59508-1.rs:12:25
|
LL | pub fn do_things<T, 'a, 'b: 'a>() {
| ----^^--^^----- help: reorder the parameters: lifetimes, then types, then consts: `<'a, 'b: 'a, T>`
| ----^^--^^----- help: reorder the parameters: lifetimes, then consts and types: `<'a, 'b: 'a, T>`

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