Let's take the following code which we compile for a 32 bit system:
typedef unsigned int uint32;
typedef unsigned char uint8;
...
uint8 y = 0xFF;
uint32 x = (y << 24U);
y
can be promoted to int
as, and I quote from the C Standard:
If an
int
can represent all values of the original type, the value is converted to anint
The shifting value is valid as, according to the same standard:
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
So because y
is promoted to an int
and the shift value is less than the width of the int (24 < 32), there is no undefined behaviour.
However, Misra has a rule, 12.2, where it states that:
The right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operand
Note the essential keyword in the rule text. This means that Misra only looks at the essential type of y
, which is uint8
and then sees that 24>=8 (width of uint8
) thus it treats it as a violation.
Why this discrepancy? If the C compiler allows promotion to be taken into consideration, why isn't Misra? Any C compiler performs the promotion of the left operand thus there can't be a compiler that messes up the shifting due an undefined behaviour.
The MISRA rule is intended to protect against variations in platform integer size. On a typical system with 32-bit ints, it's possible to shift a small enough unsigned char
left 24 bits without overflow (though your particular example still exhibits signed overflow). But such platforms are not the only platforms MISRA C is concerned with.
Some platforms have 16-bit ints. On a platform with 16-bit int
and 8-bit char
, trying to shift an unsigned char
left 24 bits would automatically be undefined behavior.
It would be possible for MISRA to specify a more lenient rule that still protects against platform variation, by accounting for the standard-mandated minimum width of an int, which is at least 16 bits. But the goal of MISRA C is better served by choosing consistency over leniency.
Your particular example is undefined behavior even with 32-bit ints. y
gets promoted to a signed int
, not unsigned
, and the value of y
is big enough that the shift produces signed overflow.