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

Tracking Issue for complex generic constants: feature(generic_const_exprs) #76560

Open
lcnr opened this issue Sep 10, 2020 · 64 comments
Open

Tracking Issue for complex generic constants: feature(generic_const_exprs) #76560

lcnr opened this issue Sep 10, 2020 · 64 comments
Labels
A-const-generics Area: const generics (parameters and arguments) C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. F-generic_const_exprs `#![feature(generic_const_exprs)]` S-tracking-impl-incomplete Status: The implementation is incomplete. 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.

Comments

@lcnr
Copy link
Contributor

lcnr commented Sep 10, 2020

This is a tracking issue for complex generic expressions in constants which is still highly experimental.
The feature gate for the issue is #![feature(generic_const_exprs)].

Initial proposal: rust-lang/compiler-team#340
Design document: HackMD document

About tracking issues

Tracking issues are used to record the overall progress of implementation.
They are also used 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.

Status

The design questions and implementation challenges are collected in the project-const-generics repository. The implementation is still far from ready but already available for experimentation.

@lcnr lcnr added T-lang Relevant to the language team, which will review and decide on the PR/issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. A-const-generics Area: const generics (parameters and arguments) labels Sep 10, 2020
@lcnr lcnr added the F-generic_const_exprs `#![feature(generic_const_exprs)]` label Sep 19, 2020
@oli-obk oli-obk changed the title Tracking Issue for complex generic constants Tracking Issue for complex generic constants (const_evaluatable_checked) Oct 27, 2020
@newpavlov

This comment has been minimized.

@oli-obk

This comment has been minimized.

@shepmaster

This comment has been minimized.

@RalfJung
Copy link
Member

RalfJung commented Jan 2, 2021

The MCP mentions that

arbitrary function calls

are allowed and will be "unified". Isn't that incompatible with impure const fn such as what one will be able to write once const fn can perform heap allocations?

@RalfJung
Copy link
Member

RalfJung commented Jan 2, 2021

Also, in terms of arithmetic, it might be better to exclude floating-point arithmetic since at least some models propose to make that arithmetic non-deterministic (specifically, the NaN payload may be non-deterministic) -- also see rust-lang/unsafe-code-guidelines#237.

@lcnr
Copy link
Contributor Author

lcnr commented Jan 2, 2021

The MCP mentions that

arbitrary function calls

are allowed and will be "unified". Isn't that incompatible with impure const fn such as what one will be able to write once const fn can perform heap allocations?

uh, now that's a challenge I didn't think of 😆 not yet sure

how do we expect to deal with [nondet_or_unpure_const_fn(); 4]?
will we rerun the the const fn 4 times?

@RalfJung
Copy link
Member

RalfJung commented Jan 2, 2021

[nondet_or_unpure_const_fn(); 4] only compiles if the return type is Copy; it will call the function once and copy the result 4 times. No CTFE is involved.

The interesting case is [const { nondet_or_unpure_const_fn() }; 4]. I expect it evaluates the function once in CTFE and use the result 4 times.

Note that the non-determinism is very restricted; it only arises in the AllocId of pointers. So maybe for the type system this simply does not matter (assuming "value trees" consist of integers only, no raw pointers). However, we have have some ptr comparison functions... they are rather weak but still, I wonder what one could do there.

@clarfonthey
Copy link
Contributor

clarfonthey commented Jan 13, 2021

So I was looking through the current design for this feature and noticed that there isn't a clear definition of how const generics should interact with associated consts. I filed #80976 and #80977 for specific errors that show up when associated consts are used in const generics directly, but noticed that we don't actually have any defined syntax for how you would constrain associated consts in bounds, e.g. something like T: Trait<const CONST = X>. A more robust example might be a const-time matrix multiplication function:

fn mat_mul<L, R>(dest: &mut D, lhs: &L, rhs: &R)
where
    D: MatrixMut,
    L: Matrix<Scalar = D::Scalar, const ROWS = D::ROWS>,
    R: Matrix<Scalar = D::Scalar, const COLS = D::COLS, const ROWS = L::ROWS>
{
    // ...
}

I figure that this is probably something that needs to be figured out before this feature can be merged, even if the decision is that associated consts simply aren't allowed at all for MVP (which IMHO wouldn't be that great of an idea, but would still resolve the issues I mentioned).

@linclelinkpart5
Copy link

linclelinkpart5 commented Jan 21, 2021

@clarfonthey This is a feature I'm in mighty need of in my personal project using const generics, I'd love to be able to use associated consts more heavily and do something like this (Cool Things I Want to Do(tm) are highlighted with !!!!!!!!):

trait Sample: Copy + Clone + PartialOrd + PartialEq {
    const EQUILIBRIUM: Self;
}

trait Frame: Copy + Clone + PartialEq {
    type Sample: Sample;

    const NUM_CHANNELS: usize;
    const EQUILIBRIUM: Self;

    // Returns a fixed array of of `Self::Sample`s of size `Self::NUM_CHANNELS`.
    fn to_array(self) -> [Self::Sample; { Self::NUM_CHANNELS }];                                    // !!!!!!!!
}

// All fixed-size arrays of samples are frames.
impl<S, const N: usize> Frame for [S; N]
where
    S: Sample,
{
    type Sample = S;

    const NUM_CHANNELS: usize = N;
    const EQUILIBRIUM: Self = [S::EQUILIBRIUM; Self::NUM_CHANNELS];                                 // !!!!!!!!

    // Already an array, just return `self`.
    // I would hope [S; N] and [S; Self::NUM_CHANNELS] are realized to be the same type!
    fn to_array(self) -> [Self::Sample; { Self::NUM_CHANNELS }] {                                   // !!!!!!!!
        self
    }
}

// Standalone function to show example of const generic bound restrictions.
fn pair_test<A, B, F>(frame_a: A, frame_b: B, mut test_func: F) -> bool
where
    A: Frame,
    // Same constant number of channels.
    B: Frame<const NUM_CHANNELS = A::NUM_CHANNELS>,                                                 // !!!!!!!!
    F: FnMut(&A::Sample, &B::Sample) -> bool,
{
    todo!("pretend this is a calculation that expects two arrays of the same size")
}

Right now however, using #[min_const_generics] on nightly, I have to resort to something like this:

trait Sample: Copy + Clone + PartialOrd + PartialEq {
    const EQUILIBRIUM: Self;
}

// This trait now has a const generic parameter.
trait Frame<const N: usize>: Copy + Clone + PartialEq {
    type Sample: Sample;

    const NUM_CHANNELS: usize;
    const EQUILIBRIUM: Self;

    fn to_array(self) -> [Self::Sample; N];
}

impl<S, const N: usize> Frame<N> for [S; N]
where
    S: Sample,
{
    type Sample = S;

    const NUM_CHANNELS: usize = N;
    const EQUILIBRIUM: Self = [S::EQUILIBRIUM; N];

    fn to_array(self) -> [Self::Sample; N] {
        self
    }
}

// Now, I have to include a `const N: usize` bound, even though this new method
// doesn't directly use its value. The value of `N` is only used to ensure that
// the two frames have the same number of channels. This kind of const generic
// pollution is especially bad when returning values parameterized by types that
// have const generics.
fn pair_test<A, B, F, const N: usize>(frame_a: A, frame_b: B, mut test_func: F) -> bool
where
    A: Frame<N>,
    B: Frame<N>, // Same constant number of channels.
    F: FnMut(&A::Sample, &B::Sample) -> bool,
{
    todo!("pretend this is a calculation that expects two arrays of the same size")
}

@liarokapisv
Copy link

liarokapisv commented Feb 14, 2021

I would expect that the following deduction would work and so the extra bound on the final create function wouldn't be needed. Is this out of the scope of this RFC ?

[u8 ; size_assert::<HeapStorage, T>()] : Sized =>
[u8 ; HeapStorage::Asserter<T>::VALUE as usize - 1] : Sized =>
[u8 ; AlwaysTrueAsserter::VALUE as usize - 1] : Sized =>
[u8 ; true as usize - 1] : Sized =>
[u8 ; 0] : Sized

code:

#![no_std]

#![feature(generic_associated_types)]
#![feature(const_generics)]
#![feature(const_evaluatable_checked)]
#![feature(const_fn)]

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

use core::marker::PhantomData;
use core::mem::{size_of, align_of};

trait BoolConst {
    const VALUE : bool;
}

struct AlwaysTrueAsserter {}

impl BoolConst for AlwaysTrueAsserter {
    const VALUE : bool = true;
}

struct SizeAsserter<L,R>(PhantomData<(L,R)>);

impl<S,T> BoolConst for SizeAsserter<S, T> {
    const VALUE : bool = size_of::<S>() >= size_of::<T>() && 
                         align_of::<S>() % align_of::<T>() == 0;
}

trait Storage {
    type Asserter<T> : BoolConst;
}

struct HeapStorage {}

impl Storage for HeapStorage {
    type Asserter<T> = AlwaysTrueAsserter;
}

struct RawBox<T, S: Storage>(PhantomData<(T,S)>);

const fn size_assertion<S : Storage, T>() -> usize {
    S::Asserter::<T>::VALUE as usize - 1
}

impl<T, S : Storage> RawBox<T, S> {

    fn create(_val : T) -> Self where
        [u8 ; size_assertion::<S, T>()] : Sized
    {
        Self(PhantomData)
    }

}

fn create<T>(val : T) -> RawBox::<T, HeapStorage> 
    // this here is my issue
    where  [u8 ; size_assertion::<HeapStorage, T>()] : Sized
{
    RawBox::<T, HeapStorage>::create(val)
}

(btw replacing the final RawBox::<T, HeapStorage>::create(val) expression with RawBox::create(val) leads to an ICE)

github-actions bot pushed a commit to rust-lang/glacier that referenced this issue Feb 17, 2021
=== stdout ===
=== stderr ===
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
 --> /home/runner/work/glacier/glacier/ices/78246.rs:1:12
  |
1 | #![feature(const_generics, const_evaluatable_checked)]
  |            ^^^^^^^^^^^^^^
  |
  = note: `#[warn(incomplete_features)]` on by default
  = note: see issue #44580 <rust-lang/rust#44580> for more information

warning: the feature `const_evaluatable_checked` is incomplete and may not be safe to use and/or cause compiler crashes
 --> /home/runner/work/glacier/glacier/ices/78246.rs:1:28
  |
1 | #![feature(const_generics, const_evaluatable_checked)]
  |                            ^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: see issue #76560 <rust-lang/rust#76560> for more information

error[E0308]: mismatched types
  --> /home/runner/work/glacier/glacier/ices/78246.rs:15:28
   |
15 |     fn into_bytes(self) -> Self::Array {}
   |        ----------          ^^^^^^^^^^^ expected array `[(); _]`, found `()`
   |        |
   |        implicitly returns `()` as its body has no tail or `return` expression

error: aborting due to previous error; 2 warnings emitted

For more information about this error, try `rustc --explain E0308`.
==============
github-actions bot pushed a commit to rust-lang/glacier that referenced this issue Feb 17, 2021
=== stdout ===
=== stderr ===
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
 --> /home/runner/work/glacier/glacier/ices/80561.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^
  |
  = note: `#[warn(incomplete_features)]` on by default
  = note: see issue #44580 <rust-lang/rust#44580> for more information

warning: the feature `const_evaluatable_checked` is incomplete and may not be safe to use and/or cause compiler crashes
 --> /home/runner/work/glacier/glacier/ices/80561.rs:2:12
  |
2 | #![feature(const_evaluatable_checked)]
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: see issue #76560 <rust-lang/rust#76560> for more information

warning: 2 warnings emitted

==============
JohnTitor pushed a commit to rust-lang/glacier that referenced this issue Feb 17, 2021
=== stdout ===
=== stderr ===
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
 --> /home/runner/work/glacier/glacier/ices/80561.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^
  |
  = note: `#[warn(incomplete_features)]` on by default
  = note: see issue #44580 <rust-lang/rust#44580> for more information

warning: the feature `const_evaluatable_checked` is incomplete and may not be safe to use and/or cause compiler crashes
 --> /home/runner/work/glacier/glacier/ices/80561.rs:2:12
  |
2 | #![feature(const_evaluatable_checked)]
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: see issue #76560 <rust-lang/rust#76560> for more information

warning: 2 warnings emitted

==============

Co-authored-by: rustbot <rustbot@users.noreply.github.com>
@K4rakara
Copy link

I think I might have found a bug (playground link).

The following code:

#![feature(adt_const_params)]
#![feature(const_trait_impl)]
#![feature(generic_const_exprs)]

use std::marker::PhantomData;
use std::mem;

pub struct Assert<const T: bool> {
    __prevent_contstruction: PhantomData<()>,
}

impl const IsTrue for Assert<true> { }

#[const_trait]
pub trait IsTrue { }

pub struct Foo;

impl Foo {
    pub const fn bar(&self)
    where
        Assert<{ mem::size_of_val(self) == 0 }>: IsTrue,
    {
        todo!()
    }
}

Causes the compiler to emit the following error:

error[E0424]: expected value, found module `self`
  --> src/lib.rs:22:35
   |
20 | /     pub const fn bar(&self)
21 | |     where
22 | |         Assert<{ mem::size_of_val(self) == 0 }>: IsTrue,
   | |                                   ^^^^ `self` value is a keyword only available in methods with a `self` parameter
23 | |     {
24 | |         todo!()
25 | |     }
   | |_____- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters

Whether you think self parameters should be allowed in generic const exprs is probably a point of contention, but I definitely don't think this should be mentioning macros in its error message.

@rust-lang rust-lang locked as off-topic and limited conversation to collaborators Mar 20, 2023
@oli-obk
Copy link
Contributor

oli-obk commented Mar 20, 2023

Tracking issues have a tendency to become unmanageable. Please open a dedicated new issue and label it with F-generic_const_exprs `#![feature(generic_const_exprs)]` for absolutely any topics you want to discuss or have questions about. See rust-lang/compiler-team#739 for details and discussions on this prospective policy.

@RalfJung

This comment was marked as off-topic.

@BoxyUwU

This comment was marked as off-topic.

@RalfJung

This comment was marked as off-topic.

@rust-lang rust-lang unlocked this conversation Apr 15, 2024
ozaner added a commit to project-pku/zorua that referenced this issue Apr 24, 2024
Until generic_const_exprs is stabilized (rust-lang/rust#76560), or an
alternative is found, the zorua crate will be nightly only.
ozaner pushed a commit to project-pku/zorua that referenced this issue Apr 24, 2024
Until generic_const_exprs is stabilized (rust-lang/rust#76560), or an
alternative is found, the zorua crate will be nightly only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-generics Area: const generics (parameters and arguments) C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. F-generic_const_exprs `#![feature(generic_const_exprs)]` S-tracking-impl-incomplete Status: The implementation is incomplete. 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