Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove restriction on type parameters preceding consts w/ feature const-generics #74953

Merged
merged 9 commits into from
Aug 10, 2020
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 },
JulianKnodt marked this conversation as resolved.
Show resolved Hide resolved
}

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
Member

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
Member

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