This self-answer is just to spell things out, for future reference.
First, there is an assignment of a too-large unsigned value to a signed type:
int32_t x = 0x80000000U;
ThatBefore C++20 (see @shananton's comment), that is generally "implementation defined" behavior. Both Clang and GCC (all versions) perform the expected two's-compliment wraparound, here — but that's not guaranteed by the standard, before C++20.
So, x gets a value of -2147483648 (which is INT_MIN). You see this in the printout.
Then, the comparison of x == -x is made.
But -x is undefined, for INT_MIN, since the result will not fit in the signed type.
With undefined behavior, the compiler is free to assume it can never happen. So, in this case, GCC optimizes away the path where they might be equal, since that undefined behavior is "not allowed" to happen.
This behavior can be altered with -fno-strict-overflow (which disables such optimizations), or with the even stronger -fwrapv, which makes signed integer overflow fully-defined.
This self-answer is just to spell things out, for future reference.
First, there is an assignment of a too-large unsigned value to a signed type:
int32_t x = 0x80000000U;
That is generally "implementation defined" behavior. Both Clang and GCC (all versions) perform the expected two's-compliment wraparound, here — but that's not guaranteed by the standard.
So, x gets a value of -2147483648 (which is INT_MIN). You see this in the printout.
Then, the comparison of x == -x is made.
But -x is undefined, for INT_MIN, since the result will not fit in the signed type.
With undefined behavior, the compiler is free to assume it can never happen. So, in this case, GCC optimizes away the path where they might be equal, since that undefined behavior is "not allowed" to happen.
This behavior can be altered with -fno-strict-overflow (which disables such optimizations), or with the even stronger -fwrapv, which makes signed integer overflow fully-defined.
This self-answer is just to spell things out, for future reference.
First, there is an assignment of a too-large unsigned value to a signed type:
int32_t x = 0x80000000U;
Before C++20 (see @shananton's comment), that is generally "implementation defined" behavior. Both Clang and GCC (all versions) perform the expected two's-compliment wraparound, here — but that's not guaranteed by the standard, before C++20.
So, x gets a value of -2147483648 (which is INT_MIN). You see this in the printout.
Then, the comparison of x == -x is made.
But -x is undefined, for INT_MIN, since the result will not fit in the signed type.
With undefined behavior, the compiler is free to assume it can never happen. So, in this case, GCC optimizes away the path where they might be equal, since that undefined behavior is "not allowed" to happen.
This behavior can be altered with -fno-strict-overflow (which disables such optimizations), or with the even stronger -fwrapv, which makes signed integer overflow fully-defined.
This self-answer is just to spell things out, for future reference.
First, there is an assignment of a too-large unsigned value to a signed type:
int32_t x = 0x80000000U;
That is generally "implementation defined" behavior. Both Clang and GCC (all versions) perform the expected two's-compliment wraparound, here — but that's not guaranteed by the standard.
So, x gets a value of -2147483648 (which is INT_MIN). You see this in the printout.
Then, the comparison of x == -x is made.
But -x is undefined, for INT_MIN, since the result will not fit in the signed type.
With undefined behavior, the compiler is free to assume it can never happen. So, in this case, GCC optimizes away the path where they might be equal, since that undefined behavior is not allowed"not allowed" to happen.
This behavior can be altered with -fno-strict-overflow (which disables such optimizations), or with the even stronger -fwrapv, which makes signed integer overflow fully-defined.
This self-answer is just to spell things out, for future reference.
First, there is an assignment of a too-large unsigned value to a signed type:
int32_t x = 0x80000000U;
That is generally "implementation defined" behavior. Both Clang and GCC (all versions) perform the expected two's-compliment wraparound, here — but that's not guaranteed by the standard.
So, x gets a value of -2147483648 (which is INT_MIN). You see this in the printout.
Then, the comparison of x == -x is made.
But -x is undefined, for INT_MIN, since the result will not fit in the signed type.
With undefined behavior, the compiler is free to assume it can never happen. So, in this case, GCC optimizes away the path where they might be equal, since that undefined behavior is not allowed to happen.
This behavior can be altered with -fno-strict-overflow (which disables such optimizations), or with the even stronger -fwrapv, which makes signed integer overflow fully-defined.
This self-answer is just to spell things out, for future reference.
First, there is an assignment of a too-large unsigned value to a signed type:
int32_t x = 0x80000000U;
That is generally "implementation defined" behavior. Both Clang and GCC (all versions) perform the expected two's-compliment wraparound, here — but that's not guaranteed by the standard.
So, x gets a value of -2147483648 (which is INT_MIN). You see this in the printout.
Then, the comparison of x == -x is made.
But -x is undefined, for INT_MIN, since the result will not fit in the signed type.
With undefined behavior, the compiler is free to assume it can never happen. So, in this case, GCC optimizes away the path where they might be equal, since that undefined behavior is "not allowed" to happen.
This behavior can be altered with -fno-strict-overflow (which disables such optimizations), or with the even stronger -fwrapv, which makes signed integer overflow fully-defined.
This self-answer is just to spell things out, for future reference.
First, there is an assignment of a too-large unsigned value to a signed type:
int32_t x = 0x80000000U;
That is generally "implementation defined" behavior. Both Clang and GCC (all versions) perform the expected two's-compliment wraparound, here — but that's not guaranteed by the standard.
So, x gets a value of -2147483648 (which is INT_MIN). You see this in the printout.
Then, the comparison of x == -x is made.
But -x is undefined, for INT_MIN, since the result will not fit in the signed type.
With undefined behavior, the compiler is free to assume it can never happen. So, in this case, GCC optimizes away the path where they might be equal, since that undefined behavior is not allowed to happen.
This behavior can be altered with -fno-strict-overflow (which disables such optimizations), or with the even stronger -fwrapv, which makes signed integer overflow fully-defined.