Returns: An object of type To. Implicitly creates objects nested within the result ([intro.object]). Each bit of the value representation of the result is equal to the corresponding bit in the object value representation of `from`. Padding bits of the result are unspecified. For the result and each object created within it, if there is no value of the object's type corresponding to the value representation produced, the behavior is undefined. If there are multiple such values, which value is produced is unspecified. A bit in the value representation of the result is zero if it corresponds to a padding bit of `from`; otherwise, its value is indeterminate if it does not correspond to a bit in the value representation of `from` or corresponds to a bit for which the smallest enclosing object is not within its lifetime or has an indeterminate value ([basic.indet]) . A bit in the value representation of the result ; otherwise, it is erroneous if it corresponds to a bit for which the smallest enclosing object has an erroneous value. For each bit b in the value representation of the result that is indeterminate or erroneous, let u be the smallest object containing that bit enclosing b:
(4.1) If u is of unsigned ordinary character type or `std::byte` type, u has an indeterminate value if any of the bits in its value representation are indeterminate, or otherwise has an erroneous value.
(4.2) Otherwise, if b is indeterminate, the behavior is undefined.
(4.3) Otherwise, the behavior is erroneous, and the result is as specified above.
The result does not otherwise contain any indeterminate or erroneous values.
That not only makes the code have well-defined behavior, but also makes similar code (e.g. with `array<int,4>` in place of `array<unsigned char,16>`) have well-defined behavior too.
This wording change does require GCC and Clang to make a change to their runtime codegen, as Thiago pointed out, but that's fine. Any user program that works in C++26 will continue to work exactly the same after this wording patch, since the only thing this patch does is to change the values you get when you use bit_cast to inspect an object's padding bits (which aren't under your control anyway).
[...]
GCC accepts that code, which it's allowed to do. It's simply undefined behavior at compile time.
No it's not. It currently (in C++26) produces an indeterminate value, which is not a constant expression, which must be rejected as the initializer for a constexpr variable.
I think I do see what you're saying about unconditional UB, though, in the case where `Dest` is not an array<unsigned char,16> but rather array<int,16> and some of the bits of the incoming object are indeterminate or erroneous. Is it
possible to have indeterminate or erroneous bits at constexpr time (except in this weird case, the result of `std::bit_cast` of padding bits, which the patch above eliminates)? If so, then I agree it would be good to also add either this patch to
[bit.cast]:
(4.1) If u is of unsigned ordinary character type or `std::byte` type, u has an indeterminate value if any of the bits in its value representation are indeterminate, or otherwise has an erroneous value.
(4.2) Otherwise, if b is indeterminate, the behavior is undefined and the result of `bit_cast` is not a constant expression ([expr.const]).
(4.3) Otherwise, the behavior is erroneous, and the result is as specified above.
(9.8) an operation that would have undefined or erroneous behavior as specified in [intro] through [cpp];
(9.x) an operation that would have undefined or erroneous behavior as specified in [bit.cast];
We can't make the former patch say "and the call to `bit_cast` is ill-formed" because in general the compiler can't tell whether at runtime the input bits will have indeterminate values (or be outside their lifetimes). But if it's all happening at constexpr time then the compiler can know and reject the expression.