Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC conversion warning when assigning to a bitfield

Is there any way to supress the warning generated by gcc in this code:

int main() {
    struct flagstruct {
        unsigned flag : 1;
    } a,b,c;

    a.flag = b.flag | c.flag;

    return a.flag;
}

The warning is

warning: conversion to 'unsigned char:1' from 'int' may alter its value [-Wconversion]

It looks like the the two flags are extended to int when ored together. What I think is really strange is that casting any of the two flags to unsigned supresses the warning.

a.flag = (unsigned)b.flag | c.flag;

Is this a compiler bug or is it supposed to work this way?

like image 584
MofX Avatar asked Aug 25 '14 06:08

MofX


2 Answers

It looks like the the two flags are extended to int when ored together.

This is integer promotion and it is defined in the strangely worded clause 6.3.1.1:2 of the C99 standard:

The following may be used in an expression wherever an int or unsigned int may be used:

— A bit-field of type _Bool, int, signed int, or unsigned int. If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.

First, the processor does not compute directly on bit-fields, and may also not have instructions to compute on the narrower integer types char and short. The C standard captures this by having arithmetic operations defined only on int, unsigned int and wider integer types. Where the standard says “may be used” above, it is trying (poorly) to express that all short types and bit-fields must be promoted to int or unsigned int before participating in arithmetic.

Second, all unsigned bit-fields that are not wide enough to include values that cannot be represented as int are promoted to int. In other words, GCC is behaving according to the standard by promoting your unsigned bit-field into a signed int, and adding an explicit cast, as you did, seems the best policy against bad surprises in the future (and against the warning).

What I think is really strange is that casting any of the two flags to unsigned supresses the warning.

Usual arithmetic conversions, another interesting concept in the C standard (6.3.1.8 in C99), have for consequence that if any of the two operands is explicitly converted to an unsigned int, then the other operand is also, implicitly this time, converted to unsigned int and the | operation is an unsigned int operation producing an unsigned int result.

In other words, (unsigned)b.flag | c.flag is strictly equivalent to (unsigned)b.flag | (unsigned)c.flag. In this case the compiler considers that there is no reason for to warn about the assignment, since the result of the computation is an unsigned int.

like image 139
Pascal Cuoq Avatar answered Oct 10 '22 23:10

Pascal Cuoq


After one year I revised the issue:

Just tested again with different compiler versions. When I first ran into this bug (now I am quite sure, I am allowed to call it a bug), I already realized the warning existed only in clang < 3.1 and all GCC versions at that time. The warning is still produced by all GCC versions < 5.

Since there is no other way to silence the error, the only solution is to update to GCC > 5 or add the unsigned cast a.flag = (unsigned)b.flag | c.flag;.

like image 43
MofX Avatar answered Oct 10 '22 22:10

MofX