diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs index 3d26259174701..c56f0bd8420a5 100644 --- a/src/librustc_ast/ast.rs +++ b/src/librustc_ast/ast.rs @@ -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; /// 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 { + 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), } } } diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index 0e98c047c2fcf..83f64793833be 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -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" } else { - "" + ", then types" }, ), ordered_params.clone(), @@ -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) diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 79d2f104a525f..2be100ae33662 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -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, diff --git a/src/test/ui/const-generics/argument_order.rs b/src/test/ui/const-generics/argument_order.rs index 1d1adf3943471..9e071e674e709 100644 --- a/src/test/ui/const-generics/argument_order.rs +++ b/src/test/ui/const-generics/argument_order.rs @@ -1,14 +1,13 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![allow(incomplete_features)] -struct Bad { //~ ERROR type parameters must be declared prior +struct Bad { arr: [u8; { N }], another: T, } struct AlsoBad { - //~^ 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, } diff --git a/src/test/ui/const-generics/argument_order.stderr b/src/test/ui/const-generics/argument_order.stderr index 19e895b8eb886..d6546a768d23f 100644 --- a/src/test/ui/const-generics/argument_order.stderr +++ b/src/test/ui/const-generics/argument_order.stderr @@ -1,39 +1,18 @@ -error: type parameters must be declared prior to const parameters - --> $DIR/argument_order.rs:4:28 - | -LL | struct Bad { - | -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `` - error: lifetime parameters must be declared prior to const parameters --> $DIR/argument_order.rs:9:32 | LL | struct AlsoBad { - | -----------------^^-----^^-------------------- 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 { - | ---------------------^----------------------^- 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 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`. diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.rs b/src/test/ui/const-generics/const-arg-type-arg-misordered.rs index 9f989ee20a569..13ca56ad3e69b 100644 --- a/src/test/ui/const-generics/const-arg-type-arg-misordered.rs +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.rs @@ -1,5 +1,5 @@ #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete +#![allow(incomplete_features)] type Array = [T; N]; diff --git a/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr b/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr index 4a6241de1b453..2e2bfed51fb26 100644 --- a/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr +++ b/src/test/ui/const-generics/const-arg-type-arg-misordered.stderr @@ -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 for more information - error[E0747]: constant provided when a type was expected --> $DIR/const-arg-type-arg-misordered.rs:6:35 | LL | fn foo() -> Array { | ^ - | - = note: type arguments must be provided before constant arguments - = help: reorder the arguments: types, then consts: `` -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`. diff --git a/src/test/ui/const-generics/const-param-before-other-params.rs b/src/test/ui/const-generics/const-param-before-other-params.rs index 756e961ce914f..0d787d9a67b59 100644 --- a/src/test/ui/const-generics/const-param-before-other-params.rs +++ b/src/test/ui/const-generics/const-param-before-other-params.rs @@ -5,8 +5,6 @@ fn bar(_: &'a ()) { //~^ ERROR lifetime parameters must be declared prior to const parameters } -fn foo(_: &T) { - //~^ ERROR type parameters must be declared prior to const parameters -} +fn foo(_: &T) {} fn main() {} diff --git a/src/test/ui/const-generics/const-param-before-other-params.stderr b/src/test/ui/const-generics/const-param-before-other-params.stderr index 9b18b8c79edda..1194dd30f61b9 100644 --- a/src/test/ui/const-generics/const-param-before-other-params.stderr +++ b/src/test/ui/const-generics/const-param-before-other-params.stderr @@ -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(_: &'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(_: &T) { - | --------------^- help: reorder the parameters: lifetimes, then types, then consts: `` - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/const-generics/defaults/complex-unord-param.rs b/src/test/ui/const-generics/defaults/complex-unord-param.rs new file mode 100644 index 0000000000000..72967640a8e62 --- /dev/null +++ b/src/test/ui/const-generics/defaults/complex-unord-param.rs @@ -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, + }; +} diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.rs b/src/test/ui/const-generics/defaults/intermixed-lifetime.rs new file mode 100644 index 0000000000000..ea3a8c14b98cb --- /dev/null +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.rs @@ -0,0 +1,12 @@ +// Checks that lifetimes cannot be interspersed between consts and types. + +#![feature(const_generics)] +#![allow(incomplete_features)] + +struct Foo(&'a (), T); +//~^ Error lifetime parameters must be declared prior to const parameters + +struct Bar(&'a (), T); +//~^ Error lifetime parameters must be declared prior to type parameters + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/intermixed-lifetime.stderr b/src/test/ui/const-generics/defaults/intermixed-lifetime.stderr new file mode 100644 index 0000000000000..0f6d7f1065af8 --- /dev/null +++ b/src/test/ui/const-generics/defaults/intermixed-lifetime.stderr @@ -0,0 +1,14 @@ +error: lifetime parameters must be declared prior to const parameters + --> $DIR/intermixed-lifetime.rs:6:28 + | +LL | struct Foo(&'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(&'a (), T); + | --------------------------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, const N: usize, T>` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/defaults/needs-feature.min.stderr b/src/test/ui/const-generics/defaults/needs-feature.min.stderr new file mode 100644 index 0000000000000..d57190ea3bbab --- /dev/null +++ b/src/test/ui/const-generics/defaults/needs-feature.min.stderr @@ -0,0 +1,8 @@ +error: type parameters must be declared prior to const parameters + --> $DIR/needs-feature.rs:10:26 + | +LL | struct A(T); + | -----------------^----- help: reorder the parameters: lifetimes, then consts, then types: `` + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/defaults/needs-feature.none.stderr b/src/test/ui/const-generics/defaults/needs-feature.none.stderr new file mode 100644 index 0000000000000..3b6f63a8efecd --- /dev/null +++ b/src/test/ui/const-generics/defaults/needs-feature.none.stderr @@ -0,0 +1,18 @@ +error: type parameters must be declared prior to const parameters + --> $DIR/needs-feature.rs:10:26 + | +LL | struct A(T); + | -----------------^----- help: reorder the parameters: lifetimes, then types: `` + +error[E0658]: const generics are unstable + --> $DIR/needs-feature.rs:10:16 + | +LL | struct A(T); + | ^ + | + = note: see issue #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`. diff --git a/src/test/ui/const-generics/defaults/needs-feature.rs b/src/test/ui/const-generics/defaults/needs-feature.rs new file mode 100644 index 0000000000000..ec02dbf407d6d --- /dev/null +++ b/src/test/ui/const-generics/defaults/needs-feature.rs @@ -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(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); +} diff --git a/src/test/ui/const-generics/defaults/simple-defaults.rs b/src/test/ui/const-generics/defaults/simple-defaults.rs new file mode 100644 index 0000000000000..b282dfd37cc4a --- /dev/null +++ b/src/test/ui/const-generics/defaults/simple-defaults.rs @@ -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() {} diff --git a/src/test/ui/const-generics/type-after-const-ok.rs b/src/test/ui/const-generics/type-after-const-ok.rs new file mode 100644 index 0000000000000..fc977d6617c56 --- /dev/null +++ b/src/test/ui/const-generics/type-after-const-ok.rs @@ -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(T); + +fn main() {} diff --git a/src/test/ui/issues/issue-59508-1.stderr b/src/test/ui/issues/issue-59508-1.stderr index 85db20b13fb4c..5e97339f148c5 100644 --- a/src/test/ui/issues/issue-59508-1.stderr +++ b/src/test/ui/issues/issue-59508-1.stderr @@ -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() { - | ----^^--^^----- 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