1

Consider the following C program:

#include <limits.h>

int main() {
  int x = INT_MIN;
  int y = -x;
  return y;
}

This program has undefined behavior, as the negation of INT_MIN is not representable; or, to be a language lawyer - since the C standard says so.

Now, the compiler knows, or can know, this is the case. And yet - neither GCC nor clang emit a warning about this, even with -W -Wall -Wextra (GodBolt); only sanitizing undefined behavior catches it - at run-time.

Why is it this the case? Is it too costly to try to prove UB is occurring at compile time, generally, so compilers don't bother?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • Making `x` const and using g++ (and without the sanitizer, which modifies the code), I do get a warning. Often this kind of warning done during optimizations (as opposed to parser warnings) causes false positives (they happen a lot in dead code that the compiler doesn't know is dead), I don't know if that's why we don't get a warning even with `-Wstrict-overflow=5 -O3`. You could try filing a bug with one of the compilers if you can't find any history on the subject... – Marc Glisse Aug 02 '21 at 17:11
  • @MarcGlisse: I thought maybe there's a more significant underlying reason then warnings about dead code. After all, one should clean up one's code and remove unused parts... – einpoklum Aug 02 '21 at 17:15
  • "dead code" in the intermediate representation of the compiler, not necessarily in the source files! When the compiler inlines functions, unrolls loops, etc, it can easily create variants of some pieces of code that can never be executed. But maybe there are other reasons than false positives... – Marc Glisse Aug 02 '21 at 17:17
  • @einpoklum: Some compilers eagerly assume that even programs which aren't intended to be portable will never receive input that would result in them performing any actions the Standard characterizes as "non-portable or erroneous", and which it would allow implementations to process "in a documented manner characteristic of the environment" in cases where doing so would be useful. Such compilers regard as "dead" code that would only be relevant if such input were received and will eliminate it, even if processing the input in a manner characteristic of the environment would be more useful. – supercat Aug 04 '21 at 20:34

1 Answers1

-1

When using some implementations or configurations, such constructs will be safe. When using clang or gcc with optimizations enabled but without -fwrapv, such constructs may cause code elsewhere to be processed nonsensically in ways that aren't bound by ordinary laws of causality.

The code is, in the words of the C Standard, non-portable or erroneous. From the published Rationale, it is clear that the Standard intended that phrase to include constructs that were non-portable but correct when used to perform low-level tasks on implementations that were intended to be suitable for low-level programming. The Standard, however, gives compiler writers whose customers wouldn't deliberately use such constructs for low-level programming the freedom to treat them as "erroneous" in cases where that would be more useful than presuming they were deliberate but non-portable. People wishing to sell their compilers to programmers who would be using them would be better placed than the Committee to know which constructs their customers would use deliberately, and thus there was no need to have the Standard itself make such distinctions.

Some compilers that are not subject to such market pressures, however, operate under a different philosophy, where the phrase "non-portable or erroneous" means "non-portable, and therefore erroneous". Thus, even on platforms where integer overflow would never have any side effects at the machine-code level, such compilers may sometimes go out of their way not to behave meaningfully in situations where they determine that integer overflow would be inevitable, even if no aspect of program behavior would otherwise have any causal dependency upon the results of the calculations.

supercat
  • 77,689
  • 9
  • 166
  • 211