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.