Skip to content
New issue

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

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

Already on GitHub? # to your account

Tracking Issue for min_const_generics #74878

Closed
5 of 8 tasks
lcnr opened this issue Jul 28, 2020 · 15 comments
Closed
5 of 8 tasks

Tracking Issue for min_const_generics #74878

lcnr opened this issue Jul 28, 2020 · 15 comments
Labels
A-const-generics Area: const generics (parameters and arguments) C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC F-const_generics `#![feature(const_generics)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Milestone

Comments

@lcnr
Copy link
Contributor

lcnr commented Jul 28, 2020

This is a tracking issue for the minimal usable subset of RFC 2000 (rust-lang/rfcs#2000).
The feature gate for the issue is #![feature(min_const_generics)].

See rust-lang/lang-team#37 for more details.

About tracking issues

Tracking issues are used to record the overall progress of implementation.
They are also uses as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.

Steps

Completed:

Implementation history

@lcnr lcnr added C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC A-const-generics Area: const generics (parameters and arguments) F-const_generics `#![feature(const_generics)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Jul 28, 2020
@lcnr lcnr changed the title Tracking Issue for `min_const_generics Tracking Issue for min_const_generics Jul 28, 2020
@rust-lang rust-lang locked and limited conversation to collaborators Jul 28, 2020
@rust-lang rust-lang unlocked this conversation Jul 29, 2020
@alex
Copy link
Member

alex commented Aug 13, 2020

👋 I'm trying to understand the limitations of min_const_generics. Is it expected that https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=59cca2f3891e9fa921afdcbcda9f2588 compiles ok without min_const_generics, but fails with it?

@lcnr
Copy link
Contributor Author

lcnr commented Aug 13, 2020

😐 min_const_generics should not cause breaking changes, that's a bug. Opened #75486

We will most probably write a more detailed summary of what will be allowed with this feature in the near future, for now I can recommend the post by @withoutboats about this: https://without.boats/blog/shipping-const-generics/

In general, please note that a tracking issue is not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter.

Feel free to ping @lcnr (me) when opening a new issue concerning this feature.

@bstrie
Copy link
Contributor

bstrie commented Sep 13, 2020

Regarding some of the steps listed here:

  • For "const wf (generic params in associated consts)", what is the issue? Implement the min_const_generics feature gate #74877 specifically calls out that const ASSOC: usize = 64 / N; should work, so is this step about discussing whether or not it should be stabilized, or is it calling out something that hasn't been implemented yet? Can someone with more background open an issue that we can link to?
  • For the second step, can it be checked off now that make ConstEvaluatable more strict #74595 is merged?
  • For "fix remaining blocking issues", do we have a list of these, or at least a vague upper bound on how many issues might exist? The F-min_const_generics label only lists three issues including this one, are either of the other two blocking?

@varkor
Copy link
Member

varkor commented Sep 13, 2020

I'm going to go through and triage the issues for F-min_const_generics soon, so we can get a better picture of what's remaining.

@varkor
Copy link
Member

varkor commented Oct 1, 2020

I've added several labels, and come up with a system to help us track what remains. It's slightly complicated, because we need to track three versions of Rust essentially: nightly, #![feature(min_const_generics)], and #![feature(const_generics)], which can all behave differently.

  • const-generics-blocking tracks any bug or non-diagnostics issue that is important enough that it ought to block stabilisation of min_const_generics (we can later reuse this label for const_generics).
  • const-generics-bad-diagnostics tracks issues where we emit unhelpful diagnostics that make const generics more difficult to use. I think these should be addressed before stabilisation of min_const_generics (at least where they make sense for min_const_generics).
  • const-generics-fixed-by-min_const_generics is a bug that occurs with or without a const_generics feature flag, which behaves correctly after enabling min_const_generics.
  • const-generics-fixed-by-const_generics is a bug that is fixed by enabling const_generics.

If an issue is marked with const-generics-fixed-by-min_const_generics and E-needs-test, we need to add a test for it under #[feature(min_const_generics)]. After adding the test, we can remove E-needs-test. If the test already works on nightly, then we can also remove const-generics-fixed-by-min_const_generics.

If an issue is marked with const-generics-fixed-by-const_generics and E-needs-test, we need to add a test for it under #[feature(const_generics)]. After adding the test, we can remove E-needs-test.

In summary, after addressing E-needs-test, if the issue still occurs on nightly or under #![feature(const_generics)], don't close the issue.


I'll try to deal with the current E-needs-test issues now, so no-one has to deal with my overly complicated labelling system immediately.

@varkor
Copy link
Member

varkor commented Oct 2, 2020

Regarding the remaining steps: there are only a handful of issues I think are blocking min_const_generics at this stage (several of which already have open pull requests). We also want to add many more tests, which is something anyone can easily help with. If you want to tackle one of the issues, feel free to ask @lcnr or myself how to get started; I'm going to dedicate time to fixing whichever remain.

I'm not sure if anything remains to be done with the WF bullet point: perhaps @lcnr could clarify?

@fogti

This comment has been minimized.

@varkor

This comment has been minimized.

@jacobchrismarsh
Copy link

jacobchrismarsh commented Oct 13, 2020

If you want to tackle one of the issues, feel free to ask @lcnr or myself how to get started;

@lcnr and @varkor, I'd love to help out on this. Where can I start?

@varkor
Copy link
Member

varkor commented Oct 21, 2020

@jacobchrismarsh: thanks for offering (and sorry for taking so long to reply: it's been really busy on my end recently)! We seem to be wrapping up F-min_const_generics at the moment (all the existing issues have open PRs), so the last big thing remaining is improving the test suite coverage for the feature. We're going to plan out what kind of things we need to have tests for, and we can let you know when that's ready: we'd be eager to have help writing tests.

If you're keen to fix some bugs now, there are still a number of (non-min_)const_generics issues left. I think most of the bugs will require a little bit of investigation to fix, but there are still a few easier ones. Some examples are #75763, #65349 (for which there's an old PR you could revive and fix up), and #61414. But feel free to take a look at any of the issues and comment there, and I'll try to give you pointers on how to get started!

@est31
Copy link
Member

est31 commented Nov 4, 2020

Re the test coverage point, there is a tracking issue now for it: #78433

@mark-i-m
Copy link
Member

#78433 is now complete 🎉

github-actions bot pushed a commit to rust-lang/glacier that referenced this issue Nov 15, 2020
=== stdout ===
=== stderr ===
error[E0658]: const generics are unstable
 --> /home/runner/work/glacier/glacier/ices/69239.rs:1:19
  |
1 | trait Trait<const S: &'static str> {}
  |                   ^
  |
  = note: see issue #74878 <rust-lang/rust#74878> for more information
  = help: add `#![feature(min_const_generics)]` to the crate attributes to enable

error[E0658]: use of unstable library feature 'core_intrinsics': intrinsics are unlikely to ever be stabilized, instead they should be used through stabilized interfaces in the rest of the standard library
 --> /home/runner/work/glacier/glacier/ices/69239.rs:5:15
  |
5 |     T: Trait<{std::intrinsics::type_name::<T>()}>
  |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = help: add `#![feature(core_intrinsics)]` to the crate attributes to enable

error: `std::intrinsics::type_name` is not yet stable as a const fn
 --> /home/runner/work/glacier/glacier/ices/69239.rs:5:15
  |
5 |     T: Trait<{std::intrinsics::type_name::<T>()}>
  |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = help: add `#![feature(const_type_name)]` to the crate attributes to enable

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0658`.
==============
bors added a commit to rust-lang-ci/rust that referenced this issue Dec 27, 2020
…ination, r=varkor

stabilize `#![feature(min_const_generics)]` in 1.51

*A new Kind*
*A Sort long Prophesized*
*Once Fragile, now Eternal*

blocked on rust-lang#79073.

# Stabilization report

This is the stabilization report for `#![feature(min_const_generics)]` (tracking issue rust-lang#74878), a subset of `#![feature(const_generics)]` (tracking issue rust-lang#44580), based on rust-lang/rfcs#2000.

The [version target](https://forge.rust-lang.org/#current-release-versions) is ~~1.50 (2020-12-31 => beta, 2021-02-11 => stable)~~ 1.51 (2021-02-111 => beta, 2021-03-25 => stable).

This report is a collaborative effort of `@varkor,` `@shepmaster` and `@lcnr.`

## Summary

It is currently possible to parameterize functions, type aliases, types, traits and implementations by types and lifetimes.
With `#![feature(min_const_generics)]`, it becomes possible, in addition, to parameterize these by constants.

This is done using the syntax `const IDENT: Type` in the parameter listing. Unlike full const generics, `min_const_generics` is limited to parameterization by integers, and constants of type `char` or `bool`.

We already use `#![feature(min_const_generics)]` on stable to implement many common traits for arrays. See [the documentation](https://doc.rust-lang.org/nightly/std/primitive.array.html) for specific examples.

Generic const arguments, for now, are not permitted to involve computations depending on generic parameters. This means that const parameters may only be instantiated using either:

1. const expressions that do not depend on any generic parameters, e.g. `{ foo() + 1 }`, where `foo` is a `const fn`
1. standalone const parameters, e.g. `{N}`

### Example

```rust
#![feature(min_const_generics)]

trait Foo<const N: usize> {
    fn method<const M: usize>(&mut self, arr: [[u8; M]; N]);
}

struct Bar<T, const N: usize> {
    inner: [T; N],
}

impl<const N: usize> Foo<N> for Bar<u8, N> {
    fn method<const M: usize>(&mut self, arr: [[u8; M]; N]) {
        for (elem, s) in self.inner.iter_mut().zip(arr.iter()) {
            for &x in s {
                *elem &= x;
            }
        }
    }
}

fn function<const N: u16>() -> u16 {
    // Const parameters can be used freely inside of functions.
    (N + 1) / 2 * N
}

fn main() {
    let mut bar = Bar { inner: [0xff; 3] };
    // This infers the value of `M` from the type of the function argument.
    bar.method([[0b11_00, 0b01_00], [0b00_11, 0b00_01], [0b11_00, 0b00_11]]);
    assert_eq!(bar.inner, [0b01_00, 0b00_01, 0b00_00]);

    // You can also explicitly specify the value of `N`.
    assert_eq!(function::<17>(), 153);
}
```

## Motivation

Rust has the built-in array type, which is parametric over a constant. Without const generics, this type can be quite cumbersome to use as it is not possible to generically implement a trait for arrays of different lengths. For example, this meant that, for a long time, the standard library only contained trait implementations for arrays up to a length of 32. This restriction has since been lifted through the use of const generics.

Const parameters allow users to naturally specify variants of a generic type which are more naturally parameterized by values, rather than by types. For example, using const generics, many of the uses of the crate [typenum](https://crates.io/crates/typenum) may now be replaced with const parameters, improving compilation time as well as code readability and diagnostics.

The subset described by `min_const_generics` is self-contained, but extensive enough to help with the most frequent issues: implementing traits for arrays and using arbitrarily-sized arrays inside of other types. Furthermore, it extends naturally to full `const_generics` once the remaining design and implementation questions have been resolved.

## In-depth feature description

### Declaring const parameters

*Const parameters* are allowed in all places where types and lifetimes are supported. They use the syntax `const IDENT: Type`. Currently, const parameters must be declared after lifetime and type parameters. Their scope is equal to the scope of other generic parameters. They live in the value namespace.

`Type` must be one of `u8`, `u16`, `u32`, `u64`, `u128`, `usize`, `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `char` and `bool`. This restriction is implemented in two places:

1. during name resolution, where we forbid generic parameters
1. during well-formedness checking, where we only allow the types listed above

The updated syntax of parameter listings is:

```
GenericParams:
    (OuterAttr* LifetimeParam),* (OuterAttr* TypeParam),* (OuterAttr* ConstParam),*

OuterAttr: '#[' ... ']'
LifetimeParam: ...
TypeParam: ...
ConstParam: 'const' IDENT ':' Type
```

Unlike type and lifetime parameters, const parameters of types can be used without being mentioned inside of a parameterized type because const parameters do not have issues concerning variance. This means that the following types are allowed:

```rust
struct Foo<const N: usize>;
enum Bar<const M: usize> { A, B }
```

### Const arguments

Const parameters are instantiated using *const arguments*. Any concrete const expression or const parameter as a standalone argument can be used. When applying an expression as const parameter, most expressions must be contained within a block, with two exceptions:

1. literals and single-segment path expressions
1. array lengths

This syntactic restriction is necessary to avoid ambiguity, or requiring infinite lookahead when parsing an expression as a generic argument.

In the cases where a generic argument could be resolved as either a type or const argument, we always interpret it as a type. This causes the following test to fail:

```rust
type N = u32;
struct Foo<const N: usize>;
fn foo<const N: usize>() -> Foo<N> { todo!() } // ERR
```

To circumvent this, the user may wrap the const parameter with braces, at which point it is unambiguously accepted.

```rust
type N = u32;
struct Foo<const N: usize>;
fn bar<const N: usize>() -> Foo<{ N }> { todo!() } // ok
```

Operations depending on generic parameters are **not** allowed, which is enforced during well-formedness checking. Allowing generic unevaluated constants would require a way to check if they would always evaluate successfully to prevent errors that are not caught at declaration time. This ability forms part of `#![feature(const_evaluatable_checked)]`, which is not yet being stabilised.

Since we are not yet stabilizing `#![feature(lazy_normalization_consts)]`, we must not supply the parent generics to anonymous constants except for repeat expressions. Doing so can cause cycle errors for arrays used in `where`-bounds. Not supplying the parent generics can however lead to ICEs occurring before well-formedness checking when trying to use a generic parameter. See rust-lang#56445 for details.

Since we expect cases like this to occur more frequently once `min_const_generics` is stabilized, we have chosen to forbid generic parameters in anonymous constants during name resolution. While this changes the ICE in the situation above to an ordinary error, this is theoretically a breaking change, as early-bound lifetimes were previously permitted in repeat expressions but now are disallowed, causing the following snippet to break:

```rust
fn late_bound<'a>() {
    let _ = [0; {
        let _: &'a (); // ICE ==> ERR
        3
    }];
}

fn early_bound<'a>() where &'a (): Sized {
    let _ = [0; {
        let _: &'a (); // ok ==> ERR
        3
    }];
}
```

### Using const parameters

Const parameters can be used almost everywhere ordinary constants are allowed, except that they may not be used in the construction of consts, statics, functions, or types inside a function body and are subject to the generic argument restrictions mentioned above.

Expressions containing const parameters are eligible for promotion:

```rust
fn test<const N: usize>() -> &'static usize {
    &(3 + N)
}
```

### Symbol mangling

See the [Rust symbol name mangling RFC](https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html) for an overview. Generic const parameters take the form `K[type][value]` when the value is known, or `Kp` where the value is not known, where:
- `[type]` is any integral type, `bool`, or `char`.
- `[value]` is the unsigned hex value for integers, preceded by `n` when negative; is `0` or `1` for `bool`; is the hex value for `char`.

### Exhaustiveness checking

We do not check the exhaustiveness of impls, meaning that the following example does **not** compile:

```rust
struct Foo<const B: bool>;
trait Bar {}
impl Bar for Foo<true> {}
impl Bar for Foo<false> {}

fn needs_bar(_: impl Bar) {}
fn generic<const B: bool>() {
    let v = Foo::<B>;
    needs_bar(v);
}
```

### Type inference

The value of const parameters can be inferred during typeck. One interesting case is the length of generic arrays, which can also be inferred from patterns (implemented in rust-lang#70562). Practical usage of this can be seen in rust-lang#76825.

### Equality of constants

`#![feature(min_const_generics)]` only permits generic parameters to be used as standalone generic arguments. We compare two parameters to be equal if they are literally the same generic parameter.

### Associated constants

Associated constants can use const parameters without restriction, see rust-lang#79135 (comment) for more details.

## Future work

As this is a limited subset of rust-lang/rfcs#2000, there are quite a few extensions we will be looking into next.

### Lazy normalization of constants

Stabilizing `#![feature(lazy_normalization_consts)]` (tracking issue rust-lang#72219) will remove some special cases that are currently necessary for `min_const_generics`, and unblocks operations on const parameters.

### Relaxing ordering requirements between const and type parameters

We currently restrict the order of generic parameters so that types must come before consts. We could relax this, as is currently done with `const_generics`. Without this it is not possible to use both type defaults and const parameters at the same time.

Unrestricting the order will require us to improve some diagnostics that expect there to be a strict order between type and const parameters.

### Allowing more parameter types

We would like to support const parameters of more types, especially`&str` and user-defined types. Both are blocked on [valtrees]. There are also open questions regarding the design of `structural_match` concerning the latter. Supporting generic const parameter types such as `struct Foo<T, const N: T>` will be a lot harder and is unlikely to be implemented in the near future.

### Default values of const parameters

We do not yet support default values for const parameters. There is work in progress to enable this on nightly (see rust-lang#75384).

### Generic const operations

With `#![feature(min_const_generics)]`, only concrete const expressions and parameters as standalone arguments are allowed in types and repeat expressions. However, supporting generic const operations, such as `N + 1` or `std::mem::size_of::<T>()` is highly desirable. This feature is in early development under `#![feature(const_evaluatable_checked)]`.

## Implementation history

Many people have contributed to the design and implementation of const generics over the last three years. See rust-lang#44580 (comment) for a summary. Once again thank you to everybody who helped out here!

[valtrees]: rust-lang#72396

---

r? `@varkor`
@alex
Copy link
Member

alex commented Dec 28, 2020

This is now stable on the latest nightly.

@lcnr lcnr closed this as completed Dec 28, 2020
@joshtriplett
Copy link
Member

We discussed this in the @rust-lang/lang meeting today, and we feel that this feature warrants a Rust blog post. Would one of the const-generics team members be up for writing that post?

@lcnr
Copy link
Contributor Author

lcnr commented Jan 19, 2021

yes, I still intend to write a post for when this feature hits beta.

rouge8 added a commit to rouge8/indexmap that referenced this issue Oct 24, 2021
@fee1-dead fee1-dead added this to the 1.51.0 milestone Nov 18, 2021
rouge8 added a commit to rouge8/indexmap that referenced this issue Dec 9, 2021
rouge8 added a commit to rouge8/indexmap that referenced this issue Jan 6, 2022
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-const-generics Area: const generics (parameters and arguments) C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC F-const_generics `#![feature(const_generics)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

10 participants