My question is this: why is it that an error is returned for a raw number, but not for any of the variables?
Because absence of compiler warnings is not a guarantee of good program behavior. The compiler would be right to emit a warning for a << x
, but it does not have to.
They, I think, should be treated the same
The compiler is doing you a favor when it warns for a << 33
. It is not doing you any favor when it doesn't warn for a << y
, but the compiler does not have to do you any favor.
If you want to be certain that your program does not contain undefined behavior, you cannot rely on the absence of compiler warnings, but you can use a sound static analyzer. If a sound static analyzer for undefined behavior does not detect any in your program, then you can conclude that it does not produce any (modulo the conditions of use that would be documented for the analyzer in question). For instance:
$ frama-c -val t.c
...
t.c:13:[kernel] warning: invalid RHS operand for shift. assert 0 ≤ x < 32;
in practice it seems that it is E1 << (E2%32)
The reason you are seeing this is that this is the behavior implemented by the shift instructions in x86_64's instruction set.
However, shifting by a negative number or by a number larger than the width of the type is undefined behavior. It works differently on other architectures, and even some compiler for your architecture may compute it at compile-time (as part of the constant propagation phase) with rules that differ from the one you have noticed. Do not rely on the result being E1 << (E2%32)
any more than you would rely on memory still containing the correct results after being free()
d.