简体   繁体   中英

Son of GCC conversion warning when assigning to a bitfield

I am trying to solve almost the exact same problem as GCC conversion warning when assigning to a bitfield except none of the solutions appear to work. As with the linked problem, gcc version does not appear to help, gccs 10.1, 9.1, 8.2, 8.1, 7.1, 6.1, 5.1, and 4.9.1 all fail.

typedef unsigned int uint;
struct foo { uint a:8; uint b:24; };
void bar(struct foo num, uint x) {
    num.b = (5U << 1) | (1 & 1);
    num.b = ((uint)(((5U << 1) | (uint)((uint) x & 1))) & 0xffffffU);
    num.a = (unsigned char)x;
}

Watch it fail on Godbolt. The compiler produces:

In function 'bar':
5:13: error: conversion from 'unsigned int' to 'unsigned int:24' may change value [-Werror=conversion]
    5 |     num.b = ((uint)(((5U << 1) | (uint)((uint) x & 1))) & 0xffffffU);
      |             ^

As you can see, I tried explicitly masking to 24 bits, casting random things to unsigned int, and pretty much all combinations of the above (eg just masking, just casting, casting in seemingly any relevant location, etc). The first num.b assignment works with constants, but adding the variable messes everything up.

I've worked around the problem through pragmas as below, but that is not a very satisfying solution.

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
    num.b = (5U << 1) | (x & 1U);
#pragma GCC diagnostic pop

@Artyr came up with a solution in a comment to the question. I'll accept it if he turns it into an answer. However, when investigating that solution I discovered another way to solve the problem which really doesn't make sense. In the example below, only the last assignment fails.

uint z = 5U;
num.b = (5U << 1) | (1 & 1); //OK
num.b = ((z << 1) | (x & 1)) & 0xffffffU; //OK
num.b = ((5U << 1) | (x & 1)) & 0xffffffU; //BAD

What I don't understand is why adding another variable z which is statically assigned from the same constant fixes the problem. Why does the combination of a constant and a variable cause the problem?

Without any answers for me to accept after a day, I'll summarize the discoveries from the comments.

  • @Artyer discovered that casting the variable to a Boolean fixes the problem. This fixes the tactical problem but does not solve the more general problem (eg z & 0xf ).

     num->b = (5U << 1) | (_Bool)(x & 1);
  • @MM independently discovered what I assume results in the same effect (conversion to Boolean). Using !! makes the problem go away. Again, this fixes the tactical problem but does not solve the more general problem (eg z & 0xf ).

     num->b = (5U << 1) |;!(x & 1);
  • I discovered, while investigating the above solutions, that storing the "constant" side of the expression into a variable works around the problem generally.

     unsigned int z = 5U; num->b = (z << 1) | (x & 1);
  • @yugr believes that this is a bug. Bug 95213 opened with gcc to track this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM