https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128146

            Bug ID: 128146
           Summary: Warn when arithmetic on a bit-field wider than int
                    wraps at the field's width (clang and C++ use the
                    declared type)
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: a1ba.omarov at gmail dot com
  Target Milestone: ---

Hello, GCC developers!

This is not a request to change the semantics, only for a diagnostics
improvement.

As explained in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88731 (comment 2)
and DR 315, GCC's C front end gives a bit-field a type of its width. For
uint64_t : 45, arithmetic happens in a 45-bit unsigned type and wraps there
before the result is widened.  Clang, and g++ for the same code, compute in the
declared type. The same source therefore gives different results with gcc and
clang, and nothing warns:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

struct reg {
    uint64_t     : 3;
    uint64_t ip  : 45;
};

int main(void)
{
    struct reg r = { .ip = 0xe20000080830ULL >> 3 };
    uint64_t addr = r.ip << 3;

    printf("%" PRIx64 "\n", addr);
    return 0;
}

gcc 16.2.1 (any -O):  20000080830
clang 22.1.8:         e20000080830
g++ 16.2.1:           e20000080830

https://godbolt.org/z/59c59GTWc

~r.ip, r.ip + 1, r.ip * k and unary minus differ the same way, and so does a
comparison with a 64-bit constant, which GCC shortens to the field's type.
Fields of up to 32 bits behave alike in both compilers.

Others also keep running into this
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92684 (closed as duplicate of
previously mentioned 88731). I ran into this while building system software
that had only been built with an EDG-based compiler, which computes in the
declared type.

I suggest adding a warning (-Wbitfield-arith possibly, maybe in -Wextra): warn
when an expression whose type is such a narrow bit-field type is widened (cast,
assignment, argument, return value, usual arithmetic conversions, or a
comparison before it is shortened) and was computed with +, -, *, <<, unary -
or ~ in the narrow type.  It would stay quiet where the result is the same
under either reading: the value stored back into a field of the same width,
fields of up to 32 bits, & with a mask that fits in int, and an explicit
widening cast before the arithmetic ((uint64_t) r.ip << 3), which is also the
portable fix.

Reply via email to