It seems that the two flags are expanded to int when they are connected to each other.
This is a whole advance and is defined in the strangely formulated section 6.3.1.1-2 of standard C99:
The following expressions can be used in an expression: int or unsigned int can be used:
...
- a bit field of the type _Bool, int, signed int or unsigned int. If int can represent all values โโof the original type, the value is equally converted to int; otherwise, it will be converted to unsigned int. They are called whole stocks. All other types do not change through whole promotions.
Firstly, the processor does not calculate directly from bit fields and may also not have instructions for calculating narrower integer types char and short . The C standard fixes this by having arithmetic operations defined only on int , unsigned int and wider integer types. If the standard says โcan be usedโ above, he tries (poorly) to express that all are short types, and bit fields should be promoted to int or unsigned int before participating in arithmetic.
Secondly, all unsigned bit fields that are not wide enough to include values โโthat cannot be represented as int advance to int . In other words, GCC behaves in accordance with the standard, pushing your unsigned bit field into a signed int , and adding an explicit cast, like you, seems like the best policy against bad surprises in the future (and against a warning).
I think it's really strange that casting either of the two flags to unsigned cancels the warning.
Conventional arithmetic conversions, another interesting concept in the C standard (6.3.1.8 to C99), have the consequence that if either of the two operands is explicitly converted to unsigned int , then the other operand is also implicitly converted to unsigned int , and the operation | is an unsigned int operation that creates an unsigned int result.
In other words, (unsigned)b.flag | c.flag (unsigned)b.flag | c.flag strictly equivalent to (unsigned)b.flag | (unsigned)c.flag (unsigned)b.flag | (unsigned)c.flag . In this case, the compiler believes that there is no reason to warn about the assignment, since the result of the calculation is an unsigned int .