int main() {
struct { unsigned int a:20; } s;
unsigned int val = 0xaabbc000;
s.a = val & 0xfffff; // 1) works
s.a = (val >> 12) & 0xfffff; // 2) generates -Wconversion warning
}
I'm compiling a project with -Wconversion
and I hit a case where I'm unable to convince the compiler that I'm fine with the conversion.
In case 1, I'm using the same solution proposed in c++ bit fields and -Wconversion and it works great. It forces the compiler to accept the conversion because of the bitmask which limits value's range.
In case 2, however, because of the shift (but why?) the compiler refuses to accept the conversion. And complains in the following way:
$ gcc wconv.c -Wconversion -Werror
wconv.c: In function ‘main’:
wconv.c:8:11: error: conversion to ‘unsigned int:20’ from ‘unsigned int’ may alter its value [-Werror=conversion]
s.a = (val >> 12) & 0xfffff; // 2) generates -Wconversion warning
^
cc1: all warnings being treated as errors
(Interesting note: with clang the code compiles without issues. I've observed so far that clang's -Wconversion
is much less stricter than GCC's.)
Questions:
unsigned int
type, a bit shift operation should not alter its type.Note[1]: this question is not a duplicate of: c++ bit fields and -Wconversion because the solutions proposed there simply do not work in my case.
Note[2]: this question is not a duplicate of: Why >>24 causes -Wconversion but >>23 doesn't? because refers to a different bug (or different manifestation of the same core-bug) and has a simple workaround using a cast, like proposed in c++ bit fields and -Wconversion, at least with GCC 7.3.
I just discovered that in the GCC's bug tracker there are several bugs related with -Wconversion
. In particular: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39170
Specifically, comment #18
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39170#c18) reports an example almost identical to mine:
#include <stdint.h>
struct foo
{
unsigned bar: 30;
unsigned fill: 2;
};
struct foo test(uint32_t value)
{
struct foo foo;
foo.bar = (value >> 2) & 0x3fffffffU;
return foo;
}
Therefore, I believe that this issue is definitively a gcc bug.
Given the compiler's bug, my personal workaround was to just wrap the right shift operation in a static
always_inline
function, even if I'm not particularly happy by this hack.
#include <stdint.h>
static __attribute__((always_inline)) inline uintptr_t
rshift(uintptr_t val, uintptr_t bits)
{
return val >> bits;
}
int main() {
struct { unsigned int a:20; } s;
unsigned int val = 0xaabbc000;
s.a = val & 0xfffff; // 1) works
s.a = (rshift(val, 12)) & 0xfffff; // 2) works
}
s.a = (unsigned){(val >> 12)} & 0xfffff; // works
Which is my favorite by now.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With