rust-lang / rust

Empowering everyone to build reliable and efficient software.

Home Page:https://www.rust-lang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Tracking Issue for complex generic constants: `feature(generic_const_exprs)`

lcnr opened this issue · comments

commented

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.

We had a short discussion relevant to this feature in #79778.

@oli-obk
If we are to continue it, I think it will be better to do it here.

UPD: As suggested, I have created an IRLO thread: https://internals.rust-lang.org/t/13602

This issue is a tracking issue about the const_evaluatable_checked feature, any large scale changes of the kind we talked about should have their own issues (and refer to this issue) or probably better an internals thread, as github issues are not great for long running discussions.

  • fix bogus error on missing associated items

This may have been incidentally fixed.

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?

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.

commented

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?

[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.

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).

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

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)

I figure this isn't even something that would be considered during MVP, but is there any way we could potentially make:

#![feature(const_generics, const_evaluatable_checked)]
fn flatten<T, const N: usize, const M: usize>(arr: [[T; N]; M]) -> [T; N * M] {
    unsafe { core::mem::transmute(arr) }
}

work with the existing MVP? Right now transmute will always fail if two types' sizes fail to unify, rather than acting at monomorphisation time. Probably something to consider with existing design.

I don't think transmute will become smart like that. You can use transmute_copy though which has no size check (and is "even more unsafe" than transmute).

commented

work with the existing MVP

yes, and while I might be missing something, I expect us to change transmute to use a size_of::<T>() == size_of::<U>() bound once this feature works well enough.

@RalfJung why do you expect this to not be possible?

Maybe I am underestimating the power of full-blown const generics.^^

Probably a bump on this feature to be implemented.

(Sorry if I somehow misunderstood the error, but I'm up for corrections)
Currently I have a use case in which I want to call SIMD intrinsics on latest nightly (which indeed uses const generics). However, the code I am writing requires arithmetic on the consts. Here's my code:

    #[inline]
    #[target_feature(enable = "sse4.2")]
    unsafe fn prev<const IMM8: i32>(self, prev: Self) -> Self {
        Self::from(_mm_alignr_epi8::<{ 16 - IMM8 }>(
            self.0,
            prev.0
        ))
    }

Which currently does not compile with the following error:

error: unconstrained generic constant
   --> lightsaber_util\src\libcore_ext\str\validations\amd64\mod.rs:136:38
    |
136 |         Self::from(_mm_alignr_epi8::<{ 16 - IMM8 }>(
    |                                      ^^^^^^^^^^^^^
    |
    = help: try adding a `where` bound using this expression: `where [(); { 16 - IMM8 }]:`

I am not sure what does that error mean, but shouldn't that also be an "overly complex constant expression"? Thanks.

What prevents this?

struct Foo<const N: u8> ([u8; N as usize]);

Casting a u8 into a usize seems trivial, but compiler complains about "overly complex generic constant".

It's because the constants don't currently unify. Technically you could do N as u16 as usize and it would always have the same value but those two constants would be considered separate at the type level.

The current code is designed to be as conservative as possible.

It's because the constants don't currently unify.

Is there a specific tracking issue for this I could follow? I found similar issues but not for integer types...

or has this behavior been rejected?

commented

I believe the reason this isnt supported is because we just haven't done the impl work yet, it should be possible to support

const fn my_cast(arg: u8) -> usize {
  arg as usize
}
struct Foo<const N: u8>([u8; my_cast(N)]);

^^^ this is a current workaround to cast stuff

I had tried this earlier but had forgotten to add back in the right feature flags!
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=6c45a92271c9ad26eb4f479b6728d266
Thanks @BoxyUwU!

Probably a bump on this feature to be implemented.

(Sorry if I somehow misunderstood the error, but I'm up for corrections)
Currently I have a use case in which I want to call SIMD intrinsics on latest nightly (which indeed uses const generics). However, the code I am writing requires arithmetic on the consts. Here's my code:

    #[inline]
    #[target_feature(enable = "sse4.2")]
    unsafe fn prev<const IMM8: i32>(self, prev: Self) -> Self {
        Self::from(_mm_alignr_epi8::<{ 16 - IMM8 }>(
            self.0,
            prev.0
        ))
    }

Which currently does not compile with the following error:

error: unconstrained generic constant
   --> lightsaber_util\src\libcore_ext\str\validations\amd64\mod.rs:136:38
    |
136 |         Self::from(_mm_alignr_epi8::<{ 16 - IMM8 }>(
    |                                      ^^^^^^^^^^^^^
    |
    = help: try adding a `where` bound using this expression: `where [(); { 16 - IMM8 }]:`

I am not sure what does that error mean, but shouldn't that also be an "overly complex constant expression"? Thanks.

Are there any workarounds for this currently?

commented

Are there any workarounds for this currently?

This is stupid but seems to work.

const fn my_cast(arg: i32) -> usize {             
      arg as usize
  }unsafe fn prev<const IMM8: i32, const SIXTEEN: i32>(a: [u8; 16], prev: [u8; 16]) -> [u8; 16]
  where[(); my_cast({ SIXTEEN- IMM8 })]: ,           
  {                                           
      transmute(core::arch::x86_64::_mm_alignr_epi8::<{ SIXTEEN - IMM8 }>(
          transmute(a),                   
          transmute(prev),
      ))
  }                

I tried it on the Rust Playground:

error: constant expression depends on a generic parameter
  --> src/lib.rs:12:5
   |
12 |     [(); my_cast({ SIXTEEN- IMM8 })]: ,           
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this may fail depending on what value the parameter takes
commented

#![feature(const_evaluatable_checked)]

Ahhh thanks. Alright

What's the current status of this, anyways? I see that it's still an incomplete feature — anything I might be able to assist with?

I tried it on the Rust Playground:

error: constant expression depends on a generic parameter
  --> src/lib.rs:12:5
   |
12 |     [(); my_cast({ SIXTEEN- IMM8 })]: ,           
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this may fail depending on what value the parameter takes

It would be nice if this code would produce

 = help: add `#![feature(const_evaluatable_checked)]` to the crate attributes to enable

In the following code, const_main fails to compile (apparently because it has a const generic parameter) while main (which has an identical body) compiles - i.e. commenting out const_main makes it work. I know the feature is incomplete but I found the behaviour surprising and thought it worth mentioning - also I believe it use to work (I haven't validated this exact code, but I'm trying to update an old project to a more recent nightly).

#![allow(incomplete_features)]
#![feature(
    const_generics,
    const_evaluatable_checked,
)]


/// Only works on powers of 2
pub const fn simple_log_2(x: usize) -> usize {
    x.trailing_zeros() as usize
}


fn foo<const N: usize>(arr: [usize; simple_log_2(N)]) {
    println!("{:?}", arr);
}

fn const_main<const M: usize>() {
    const N: usize = 2;
    foo::<N>([0])
}

fn main() {
    const N: usize = 2;
    foo::<N>([0])
}

Error

error: unconstrained generic constant
  --> src/main.rs:20:5
   |
20 |     foo::<N>([0])
   |     ^^^^^^^^
   |
   = help: try adding a `where` bound using this expression: `where [(); simple_log_2(N)]:`
note: required by a bound in `foo`
  --> src/main.rs:14:37
   |
14 | fn foo<const N: usize>(arr: [usize; simple_log_2(N)]) {
   |                                     ^^^^^^^^^^^^^^^ required by this bound in `foo`

error: could not compile `tmp` due to previous error
[Finished running. Exit status: 101]

I’m currently trying this feature to make a change in luminance and just hit a compiler error. Not sure whether I should provide the snippet here, but in doubt, here it is.

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=1f3b71a20a79dee66d9a5e58c3d3b138

@phaazon for bugs like these, it's worth opening new issues rather than posting to tracking issues. this appears to be same ICE during type inference mentioned in #83249. if the types are provided in the wrap2 calls in main

- let x = wrap2(1., [1., 2., 3.])
+ let x = wrap2::<f32, [f32; 3]>(1., [1., 2., 3.])

(and the const_fn_trait_bound feature is enabled in your case), it compiles successfully.

Combining this with adt_const_params can give weird errors:

#![feature(generic_const_exprs, adt_const_params)]

struct FieldElement<const N: &'static str> {
    n: [u64; num_limbs(N)],
}
const fn num_limbs(_: &str) -> usize {
    0
}

Error:

error: overly complex generic constant
 --> src/lib.rs:5:14
  |
5 |     n: [u64; num_limbs(N)],
  |              ^^^^^^^^^^-^
  |                        |
  |                        dereferencing is not supported in generic constants
  |
  = help: consider moving this anonymous constant into a `const` function
  = note: this operation may be supported in the future

https://play.rust-lang.org/?gist=a6a2aecfba83485968a81b8599b86cc5

(I hoped I can try and play with these to see if it would be possible to implement typed algebraic fields)

@lcnr what's the status of this issue? It looks to me that all PRs mentioned in issue's body are merged.

Considering how the "fully implement this feature" box is still unchecked, I would hesitate to say that this feature is ready to be stabilised.

@clarfonthey then where can I learn what steps are left to do?

A lot, honestly. Right now, the hack of having no bounds on a type involving a constant is probably undesired, and I've seen a few different suggestions for how to do it. That said, there are a few cases that do not rely on this hack that could probably be stabilised, mostly involving associated consts. Since the existence of associated consts is always publicly exposed, we could easily assume that they're public APIs, and that stuff like Trait<CONST = Type::CONST> could be stabilised without needing to stabilise the full feature.

Already, I feel like allowing the use of associated consts in general could act as a workaround until the full feature is properly specked out, but I'm not on the lang team and have no idea what the const-wg has planned for this.

Perhaps someone from the lang team or the const-wg could help provide more insight? I've also seen a few blog posts regarding potential designs for this but definitely won't be able to find them easily, so, maybe if someone could share those as well it might provide some insight.

Since the existence of associated consts is always publicly exposed, we could easily assume that they're public APIs, and that stuff like Trait<CONST = Type::CONST> could be stabilised without needing to stabilise the full feature.

Could something like that provide a solution for #60551? I think that's a major blocker in the usability of the current implementation of const generics.

That issue is exactly the thing I was describing; thank you for linking it. Essentially, the idea would be to make #60551 work without adding empty bounds to where clauses, essentially reclassifying associated consts to work with the currently stabilised version of const generics.

I think below code should be allowed in the future implementation:

#![feature(generic_const_exprs)]
struct T<const A: usize>([(); A]);

impl<const L: usize> T<L> {
    fn void(&self) -> T<0> {
        T([(); L - L])
    }
}
// stable rust
struct T<const A: usize> ([(); A]);

impl<const L: usize> T<L> {
    /// function holds for all T<L>
    fn void(&self) -> T<0> {
        T([(); 0])
    }
}
commented

The relevant issues for this are tracked in https://github.com/rust-lang/project-const-generics/issues?q=is%3Aissue+is%3Aopen+label%3AC-design-docs+label%3AA-generic-exprs

it's still going to take a while until we're close to stabilization here1, going to update the tracking issue though.

Footnotes

  1. to a large part because cg currently isn't the main focus of either myself or the compiler team

Is there anyway I can do something like this in nightly?

pub struct Foo<const I: i32> {}

impl<const I: i32> Foo<I> where { I > 0 } {
  fn foo() -> SomeTypeA {}
}
impl<const I: i32> Foo<I> where { I == 0 } {
  fn foo() -> SomeTypeB {}
}
impl<const I: i32> Foo<I> where { I < 0 } {
  fn foo() -> SomeTypeC {}
}

Nope, there's currently no way to bound const generics. The best you have right now is to change the type of the generic to affect the bound.

Hi,
one question: Will it be possible to support 'generic const expression' based types recursively?
So the structure I have in mind is something like a 'cons list' where the type of each subsequent element has a const generic increased by one.

So what I want is something that will look like the following:

enum Cons<const N: usize>
where
    [(); N + 1]:,
{
    End,
    Next(Box<Cons<{ N + 1 }>>),
}

Unless we can start adding where bounds to variants, this will be an infinitely recursive type. So I think this is blocked on another feature that isn't even RFCed yet

Yeah, boxing can prevent infinite size types, but it can't stop infinitely complex types, even if said types are finitely sized.

Yes, I had also seen this problem with infinite recursive type. And yes, one can probably omit the boxing here.

Is it a possibility here to apply conditional compilation as follows?

enum Cons<const N: usize>
where
    [(); N + 1]:,
{
    End,
    #[cfg(N < 20)]
    Next(Cons<{ N + 1 }>),
}

and with structs:

struct Cons<const N: usize>
where
    [(); N + 1]:,
{
    #[cfg(N < 20)]
    next: Option<Cons<{ N + 1 }>>,
}

I could imagine that conditional variants/fields would greatly increase the complexity when used later.

These kind of cfg are what I'd expect to happen as where bounds, but that's entirely orthogonal to const generics. Probably best to start a discussion on zulip or the internals forum, ideally with examples using types and traits instead of const generics (to reduce the number of feature gates needed to come up with examples)

Hi,
Is a generic implementation of a trait possible (or planned) for a subset of a const generic?
Something like this :

#[derive(PartialEq, Eq, Debug)]
pub enum TensorSize{
    Static(usize),
    Dynamic
}

pub struct Tensor1<const U: TensorSize>;

impl<const S:usize> Add<Tensor1<{TensorSize::Static(S)}>> for Tensor1<{TensorSize::Static(S)}> {
    type Output = Tensor1<{TensorSize::Static(S)}>;
    //...
}

impl<const O:TensorSize> Add<Tensor1<O>> for Tensor1<{TensorSize::Dynamic}> {
    type Output = Tensor1<{TensorSize::Dynamic}>;
    //...
}

@oxabz that is unlikely to able to have, because RFC: enum variant types was postponed.

@oxabz that is unlikely to able to have, because RFC: enum variant types was postponed.

enum variant types are unrelated to @oxabz's question because that is for variant types, not variant values.

what is needed are enum values as const generics...which iirc depend on rustc-internal value trees -- tracking issue for feature: #95174

I found something weird, not sure if it is a bug:

const ONE : i32 = 1;
const TWO : i32 = 2;

struct Foo<const i: i32, const j: i32> {}

impl<const i: i32> Foo<i, ONE> {
    pub fn foo() {}
}

impl<const i: i32> Foo<i, TWO> {
    pub fn foo() {}
}

This can comiple without #![feature(generic_const_exprs)], but if #![feature(generic_const_exprs)] is added, it won't compile:

error[E0592]: duplicate definitions with name `foo`
  --> src/main.rs:9:5
   |
9  |     pub fn foo() {}
   |     ^^^^^^^^^^^^ duplicate definitions for `foo`
...
13 |     pub fn foo() {}
   |     ------------ other definition for `foo`

Is there a team working on this? Where can I find updates/progress/timeline/roadmap/etc? The project-const-generics hasn't been updated in a few months

I’ve run into this case and I’m unsure if this should be figured out by the compiler. It definitely makes sense to me as a human…

#![feature(generic_const_exprs)]
#![allow(incomplete_features)]

struct ArrW<T, const N: usize>([T; N]);

trait IntoArrW<T> {
    const N: usize;
    fn into_arr_w(self) -> ArrW<T, { Self::N }>;
}

impl<T, const N: usize> IntoArrW<T> for [T; N] {
    const N: usize = N;
    fn into_arr_w(self) -> ArrW<T, { Self::N }> {
        ArrW(self)
    }
}
error[[E0308]](https://doc.rust-lang.org/nightly/error_codes/E0308.html): mismatched types
  --> src/lib.rs:14:14
   |
14 |         ArrW(self)
   |              ^^^^ expected `{ Self::N }`, found `N`
   |
   = note: expected constant `{ Self::N }`
              found constant `N`

For more information about this error, try `rustc --explain E0308`.

I'd like to note for useful case.

Consider this trait:

pub trait AsBitPattern {
    /// Note: ZST must returns empty slice.
    fn as_bit_pattern(&self) -> &[u8];
}

and its blanket implementation:

impl<T> AsBitPattern for T {
    fn as_bit_pattern<'a>(&'a self) -> &'a [u8] {
        let start_addr = (self as *const T).cast::<u8>();
        // SAFETY:
        // The following property is always hold for Sized types:
        // - size_of::<T> == size_of::<T> * size_of::<u8>
        // - It will not truncate nor cause out-of-bound-read.
        let slice = unsafe {
            core::slice::from_raw_parts::<'a, u8>(start_addr, core::mem::size_of::<T>())
        };
        slice
    }
}

Using this feature, it can be rewritten as (currently, generics in const context is not allowed):

impl<T> AsBitPattern for T {
    fn as_bit_pattern<'a>(&'a self) -> &'a [u8] {
        // SAFETY:
        // The following property is always hold for Sized types:
        // - size_of::<T> == size_of::<T> * size_of::<u8>
        // ref-to-ref cast will not truncate nor cause out-of-bound-read.
        unsafe {
            core::mem::transmute::<&'a T, &'a [u8; core::mem::size_of::<T>()]>(self)
        }
    }
}

I'd like to note for useful case.

Consider this trait:

pub trait AsBitPattern {
    /// Note: ZST must returns empty slice.
    fn as_bit_pattern(&self) -> &[u8];
}

and its blanket implementation:

impl<T> AsBitPattern for T {
    fn as_bit_pattern<'a>(&'a self) -> &'a [u8] {
        let start_addr = (self as *const T).cast::<u8>();
        // SAFETY:
        // The following property is always hold for Sized types:
        // - size_of::<T> == size_of::<T> * size_of::<u8>
        // - It will not truncate nor cause out-of-bound-read.
        let slice = unsafe {
            core::slice::from_raw_parts::<'a, u8>(start_addr, core::mem::size_of::<T>())
        };
        slice
    }
}

Using this feature, it can be rewritten as (currently, generics in const context is not allowed):

impl<T> AsBitPattern for T {
    fn as_bit_pattern<'a>(&'a self) -> &'a [u8] {
        // SAFETY:
        // The following property is always hold for Sized types:
        // - size_of::<T> == size_of::<T> * size_of::<u8>
        // ref-to-ref cast will not truncate nor cause out-of-bound-read.
        unsafe {
            core::mem::transmute::<&'a T, &'a [u8; core::mem::size_of::<T>()]>(self)
        }
    }
}

Note that this snippet is unsound. See the bound of bytemuck::bytes_of.

You can also make it sound by using MaybeUninit<u8> instead of u8.

You can also make it sound by using MaybeUninit<u8> instead of u8.

that's not actually true afaict, data race:

use std::mem::MaybeUninit;
use std::sync::atomic::{AtomicU8, Ordering};
fn as_bit_pattern<'a, T>(v: &'a T) -> &'a [MaybeUninit<u8>] {
    let start_addr = (v as *const T).cast::<MaybeUninit<u8>>();
    // SAFETY: not safe
    let slice = unsafe { core::slice::from_raw_parts(start_addr, core::mem::size_of::<T>()) };
    slice
}

fn main() {
    static V: AtomicU8 = AtomicU8::new(0);
    let t = std::thread::spawn(|| V.store(1, Ordering::Relaxed));
    let r = as_bit_pattern(&V);
    let _copy = r[0]; // UB: non-atomic read races with atomic store on thread t
    t.join().unwrap();
}

try running in Miri: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=77354137238a772a639f687191258fc4

error: Undefined Behavior: Data race detected between (1) Read on thread `main` and (2) Atomic Store on thread `<unnamed>` at alloc1. (2) just happened here
  --> src/main.rs:12:35
   |
12 |     let t = std::thread::spawn(|| V.store(1, Ordering::Relaxed));
   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Data race detected between (1) Read on thread `main` and (2) Atomic Store on thread `<unnamed>` at alloc1. (2) just happened here
   |
help: and (1) occurred earlier here
  --> src/main.rs:14:17
   |
14 |     let _copy = r[0]; // UB: non-atomic read races with atomic store on thread t
   |                 ^^^^
   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior

that's not actually true afaict, data race:

Ah right, it needs to be UnsafeCell<MaybeUninit<u8>> or something like that.

commented

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.

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.

Before const generic of reference type get stabilized, we need to carefully think about and resolve this interaction with consts that point to statics (currently an unstable feature). What is the best way to track this? Usually I'd add an item to the original issue, but here that seems to be delegated to a separate repo, but that repo seems to have fairly strict rules for what the issues look like and I really just want to leave a bullet point as a starting point for something to be explored eventually.

commented

I would open an issue and label it F-adt_const_params

Ah right, this is even the wrong tracking issue oops