c++constexprbit-fieldsbit-cast

Trying to use std::bit_cast with a bitfield struct. Why is it not constexpr?


I'm trying to get a bitmask from a bitfield struct, at compile time. One of the tricks that I tried, which looks promising to me, is using std::bit_cast, because it is supposed to be constexpr.

My test code can be found here: https://godbolt.org/z/er48M63sh

This is the source:

#include <bit>

struct Bitfield {
    int :3;
    int x:3;
};

constexpr int mask() noexcept {
    Bitfield bf{};
    bf.x -= 1;
    return std::bit_cast<int>(bf);
}

int test() {
    int mask1 = mask();
    // constinit static int mask2 = mask();     // Why doesn't this compile?
    return 0;
}

As you can see, it doesn't actually calculate the bitmask at compile time, but at runtime, so for some reason the constexpr trick isn't working. I fail to see why, however, since cppreference doesn't seem to list my case as one that defeats constexpr in std::bit_cast.

Does anybody see what's wrong? Does this thing have a chance of working?


Solution

  • The problem is that using std::bit_cast with padding bits is undefined behavior in most cases:

    [bit.cast] p2

    A bit in the value representation of the result is indeterminate if it does not correspond to a bit in the value representation of from or corresponds to a bit of an object that is not within its lifetime or has an indeterminate value.

    Most of the upper (or lower) bits in the int that results from std::bit_cast<int>(bf) are indeterminate, because it they don't correspond to bits in the value representation of the bit-field. The Bitfield class has plenty of padding bits, i.e. which are only in the object representation, not the value representation of Bitfield.

    For each bit in the value representation of the result that is indeterminate, the smallest object containing that bit has an indeterminate value; the behavior is undefined unless that object is of unsigned ordinary character type or std​::​byte type. The result does not otherwise contain any indeterminate values.

    int is not a std::byte, so it isn't exempt, and producing an int through std::bit_cast which has some indeterminate bits is undefined behavior. You only notice that it's UB in a constant expression because compilers aren't required to diagnose UB outside constant expressions.

    Possible Solutions