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
This commit is contained in:
bors 2020-08-10 15:19:46 +00:00
commit 08324fe6f7
18 changed files with 190 additions and 75 deletions

View File

@ -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;
@ -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),
}
}
}

View File

@ -773,13 +773,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"
} else {
""
", then types"
},
),
ordered_params.clone(),
@ -1156,7 +1156,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)

View File

@ -489,10 +489,11 @@ 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),
@ -500,17 +501,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
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();
}
@ -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,

View File

@ -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,
}

View File

@ -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`.

View File

@ -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];

View File

@ -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`.

View File

@ -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() {}

View File

@ -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

View File

@ -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,
};
}

View File

@ -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() {}

View File

@ -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

View File

@ -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>`
error: aborting due to previous error

View File

@ -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`.

View File

@ -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);
}

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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