Description
This example should do one of these three things, but it doesn't (playground):
- compile successfully (
PADDED == FILLED
after all, field-wise) - error on
FILLED
's definition (due to evaluated constant not fitting type) - error when
FILLED
is used as an argument toPhantom
#![feature(const_generics)]
union Transmute<T: Copy, U: Copy> {
from: T,
to: U,
}
const PADDED: (u8, u16) = (0, 0);
const FILLED: (u8, u16) = unsafe { Transmute { from: [0u8; 4] }.to };
struct Phantom<const X: (u8, u16)>;
fn test(x: Phantom<PADDED>) -> Phantom<FILLED> {
x
}
Instead, Phantom<PADDED>
and Phantom<FILLED>
are considered different types.
If we want to make it compile, we could normalize FILLED
to also have that padding byte marked as "undef", but I'm not sure if we can do this normalization if the values were behind a reference.
So we might want to error if e.g. &[0u8; 4]
was transmuted to &(u8, u16)
, because normalizing it would change what runtime code would see. Again, we have two places where we can error.
If we want to error without causing no breaking changes, either always or just indirect case, we can do so by introducing a second, stricter, validity check in ty::Const
Well-Formed rules (which we should be able to post-#70107).
That check should enforce that the value is a tree of constructors (&_
would be treated as a constructor) with integer leaves (no relocations, i.e. no raw/fn
pointers), where any user ADTs are structurally-matchable, and all padding bytes (not occupied by leaves) are "undef".
cc @rust-lang/wg-const-eval @varkor @yodaldevoid