C++ Logo


Advanced search

Re: [std-proposals] Problematic integral promotions on bitfields

From: Lénárd Szolnoki <cpp_at_[hidden]>
Date: Fri, 02 Dec 2022 21:03:18 +0000

On 2 December 2022 20:04:08 GMT, "Johnston, Daniel via Std-Proposals" <std-proposals_at_[hidden]> wrote:
>Reference: https://github.com/llvm/llvm-project/issues/59306
>Short version, this code seems to be undefined behavior:
>struct Foo
> uint64_t Stuff : 6;
> uint64_t Value: 27;
> uint64_t OtherStuff: 31;
>Foo f;
>f.Value = (uint64_t(1) << 27) - 1;
>// Caution UB here due to integral promotion of bitifields!
>uint64_t shiftedValue = f.Value << 6;
>uint64_t maskedValue = reinterpret_cast<uint64_t&>(f) & (((uint64_t(1) << 27) - 1) << 6);

This is tangential, but reading through this reinterpret_cast is undefined behavior. You should use bit_cast or memcpy.

>assert(shiftedValue == maskedValue); // this may fail depending on compiler
>In the above scenario, a bitwidth for Value of 32 or higher guarantees correct operation. A bitwidth of less than (32 - shift amount) works as well. A bitwidth that exactly matches (32 - shift amount) could *sign extend* the result even though it should be unsigned.
>This seems like a defect in the standard and has caused bugs for us when simulating HW that requires structures with these bit layouts.
>Can we fix the standard to disallow integral promotion of bitfields completely? I *always* want the type of the bitfield value to match what was specified.
>Thank you,

I agree that this integral promotion rule is pretty weird. Integral promotion is quite broken in general. But I suspect that changing promotion rules would just break too much code. Also this would need to be coordinated with the C standard as well, which probably has the same rule.

I suppose the workaround is

uint64_t shiftedValue = (uint64_t)f.Value << 6;

 I wonder if this weird promotion rule also affects function overloading with bitfield arguments.


Received on 2022-12-02 21:03:21