1

I am trying to generate a warning for undefined behavior on left shifting a negative number. According to this answer, left shift of a negative number in C is undefined.

The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1×2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and non-negative value, and E1×2E2 is representable in the result type, then that is the resulting value; otherwise, the behaviour is undefined.

I am trying to understand why I don't get a warning with this code: x << 3

gcc -Wall (version 9.1.0)

int main ()
{
    int x= -4, y, z=5;
    y = z << x;
    y = x << 3;
    return y;
}

On a separate note, I also don't get warned about left shifting by a negative number z << x

  • _why I don't get a warning with this code: `x << 3`?_ Because left hand operand `x` can be signed or unsigned type. Standard doesn't restrict that `x` should be only unsigned type i.e it can be signed type i.e it can hold negative values. – Achal Jun 29 '19 at 17:36
  • 1
    But you *do* get a warning for `y = z << -4;` (your last sentence). The compiler tries to be helpful but it does not run the code, to determine if `x` is always negative. It might not even *know*, if the value for `x` is a user input at run time. – Weather Vane Jun 29 '19 at 17:47

3 Answers3

1

In 5 << -4, both GCC 9.1.0 and Apple LLVM 10.0.1 with clang-1001.0.46.4, targeting x86-64, issue a warning message (“left shift count is negative” for GCC and “shift count is negative” for LLVM-clang). In -4 << 3, GCC does not issue a warning, but LLVM-clang does (“shifting a negative value is undefined”).

The C standard does not require a diagnostic in either of these cases, so whether a compiler does or not is a quality of implementation issue (or, possibly, that the compiler extends C by defining left shifts of negative values and therefore does not consider it an error).

When an operand is not a constant, as in z << x and x << 3, we may surmise the compiler fails to see the operand is negative, so it does not issue a warning. In general, these expressions may have defined behavior: If x is not negative and is within suitable bounds (is not larger than the width of z in the former case and is not so large that x << 3 overflows in the latter case), the behavior is defined (excepting the possibility that z << x may overflow). The C standard does not say that left shifts with types that may have negative values, i.e., signed types, are undefined, just that left shifts with negative values are undefined. Therefore, it would be an error for the compiler to issue a warning message whenever x were a signed type in an expression such as z << x or x << 3.

Of course, given int x = -4 and the lack of any change to x between that initialization and the expressions z << x and x << 3, a compiler could deduce that the shifts are undefined in this specific case and issue a warning. This is not required by the standard, and the failure of the compiler to do so is simply a matter of the quality of the implementation of the compiler.

ajaysinghdav10d
  • 1,771
  • 3
  • 23
  • 33
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • I think I mentioned I used "gcc -Wall". I did *not* mention the version. It is 9.1.0. As for, x <<3, I would imagine something like copy propagation would help the compiler figure out that x was a negative value. I just noticed that for gcc, the compiler does not even warn me for -4 << 3, though it does warn me about 5 << -4. https://godbolt.org/z/i34yM2 – abjoshi - Reinstate Monica Jun 30 '19 at 00:24
  • Also, given that the compiler does not warn that x << 3 is undefined, (which it definitely is, in this case), can I assume that it won't perform any sneaky optimization behind my back (assuming undefined behavior)?. – abjoshi - Reinstate Monica Jun 30 '19 at 00:31
  • 1
    @abjoshi: No, the lack of a compiler warning does not mean you may assume the compiler will not “optimize” the code. – Eric Postpischil Jun 30 '19 at 00:33
  • I hope I was clear that I don't mean generic optimizations. I just mean the ones that are performed _specifically_ because the compiler assumes that we won't have undefined behavior in our program. – abjoshi - Reinstate Monica Jun 30 '19 at 00:39
  • 1
    @abjoshi: The lack of a compiler warning does not mean the compiler will not completely replace the undefined behavior in your code with anything it wants. – Eric Postpischil Jun 30 '19 at 00:50
  • If two implementations support the popular "process left-shifts the same way as C89 did, even in cases where C99 would no longer require it" extension, I would not generally regard one makes it hard to reliably use that extension without compiler squawks as being of higher quality than one which allows programmers to simply make use of that extension without cluttering up the diagnostic report. – supercat Jul 01 '19 at 17:22
1

The short explanation for the compiler not issuing a warning in such cases is because it isn't required to.

The definition of "undefined" in the standard also explicitly states "no diagnostic required". Which means the standard does not require an implementation to issue a diagnostic in such cases. The reason for that is that, technically, compilers may not be able to detect all instances of undefined behaviour within a reasonable time or (in some cases) at all. Some cases can only be detected at run time. Compilers are complicated pieces of code so - even if a human can easily recognise a problem - a compiler may not (on the other hand, compilers also detect problems that humans cannot find easily).

When no diagnostics are required, there are several things - all discretionary - that must happen before a compiler will issue a warning.

The first things that happen amount to "quality of implementation" concerns - a vendor or compiler developer elects to write code that detects particular cases and other code that issues a warning

The two steps (detecting a case, and warning about it) are separate and completely discretionary - the standard does not require a diagnostic so, even if code is written that detects a particular case, the compiler is still not required to issue a warning about it. Practically, even if problems are detected, the compiler developers may elect not to issue a warning for various reasons. Some warnings are about rare edge cases, so it is not worth the effort to emit them. Some warnings have an inherent "false positive" rate which tends to result in developers whinging in bug reports to the compiler vendor about unnecessary warnings - so the compiler is configured by default to not emit them.

The next requirement for a warning to be issued is that the user of the compiler must elect to have the warnings shown. Thanks to vehement lobbying of vendors by developers - most modern compilers are configured BY DEFAULT so they only issue a relatively small number of warnings. So developers who want as much help as possible from the compiler need to explicitly enable it. For example, by using -Wall options with gcc and clang.

Peter
  • 35,646
  • 4
  • 32
  • 74
0

The behavior of left-shifting any value by an amount smaller than the word size was specified in C89 for all integer values of the left operand, even though the specified behavior for negative left operands would often have been less than ideal on non-two's-complement complement platforms, or on error-trapping platforms in cases where iterated multiplication by two would overflow.

To avoid requiring implementations to behave in less-than-ideal fashion, the authors of C99 decided to allow implementations to process such cases however they saw fit. Rather than try to guess at all the places where implementations might have a good reason to deviate from the C89 behavior, the authors of the C99 Standard presumably expected that implementations would follow the C89 behavior, absent a good reason to otherwise, whether the Standard compelled them to do so or not.

If the authors of the Standard had intended any change to the processing of left-shift operations in cases where the C89 behavior made sense, there should be some mention of the change in the published Rationale document. There is none whatsoever. The only inference I can see from such omission is that going from "Behave in a particular fashion that happens to almost always make sense" to "Behave in that fashion if the implementer judges that it makes sense, and otherwise process the code in whatever other fashion the implementer sees fit" wasn't seen as a sufficient change as to justify comment. The fact that the authors of the Standard failed to mandate a behavior in no way implies that they didn't expect that general-purpose two's-complement implementations wouldn't continue to process such shifts as they always had, nor that programmers shouldn't be entitled to similar expectations.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • 2
    The question does not ask why left shifting a negative value is not defined or what the history is. It asks why the compiler does not issue a warning message for `z << x` or `x << 3` when `x` is negative (in spite of the fact that it does for `z << -4` or `-4 << 3`). – Eric Postpischil Jun 29 '19 at 22:55
  • @EricPostpischil: Many compilers define more behaviors than the bare minimum described by the Standard. The behavior was defined in C89, and the authors of gcc likely thought that it was easier to treat the action as defined regardless of whether the compiler was in C89 or C99 mode, than to only treat it as defined in the former case. – supercat Jun 30 '19 at 04:09