From d91ce91ca4d898fd1afe7a82842bf752948b8b3d Mon Sep 17 00:00:00 2001 From: Alex Sepkowski <5620315+alsepkow@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:42:26 -0700 Subject: [PATCH] [NFC] Address compiler warnings: C4146 - Use two's complement instead of negation (#7562) Replaces uses of the unary - operator on signed integers with the equivalent (sort of, see the details below) expression '~N + 1', assigning the result to an unsigned type. This avoids undefined behavior in edge cases and ensures correctness when certain conditions are met. Details: This transformation is valid when: The signed value N is guaranteed to be negative. The result is stored in an unsigned type that can represent the full range of the signed type (e.g., uint64_t for int64_t). The system uses two's complement representation (as is standard on modern platforms). While -N is undefined for the minimum representable value (e.g., INT64_MIN), the expression ~N + 1 remains well-defined and yields the correct bit pattern. Assigning this result to an appropriately sized unsigned type preserves the intended two's complement interpretation without triggering undefined behavior. Addresses #7561. --- lib/Support/raw_ostream.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/Support/raw_ostream.cpp b/lib/Support/raw_ostream.cpp index b11ffb15d5..595468a6dc 100644 --- a/lib/Support/raw_ostream.cpp +++ b/lib/Support/raw_ostream.cpp @@ -134,13 +134,18 @@ raw_ostream &raw_ostream::operator<<(unsigned long N) { } raw_ostream &raw_ostream::operator<<(long N) { + // A positive signed long has the same value when casted to its unsigned + // counterpart. If its negative, then we'll handle it in the below if block. + unsigned long UN = static_cast(N); + if (N < 0 && writeBase == 10) { *this << '-'; - // Avoid undefined behavior on LONG_MIN with a cast. - N = -(unsigned long)N; + // Since N is negative and we're storing the result in an unsigned Long, + // we can use the equivalence of -N == ~N + 1 to get the positive value. + UN = ~N + 1UL; } - return this->operator<<(static_cast(N)); + return this->operator<<(UN); } raw_ostream &raw_ostream::operator<<(unsigned long long N) { @@ -169,13 +174,18 @@ raw_ostream &raw_ostream::operator<<(unsigned long long N) { } raw_ostream &raw_ostream::operator<<(long long N) { + // A positive signed long has the same value when casted to its unsigned + // counterpart. If its negative, then we'll handle it in the below if block. + unsigned long long UN = static_cast(N); + if (N < 0 && writeBase == 10) { *this << '-'; - // Avoid undefined behavior on INT64_MIN with a cast. - N = -(unsigned long long)N; + // Since N is negative and we're storing the result in an unsigned Long, + // we can use the equivalence of -N == ~N + 1 to get the positive value. + UN = ~N + 1ULL; } - return this->operator<<(static_cast(N)); + return this->operator<<(UN); } // HLSL Change Starts - Generalize non-base10 printing. @@ -470,7 +480,10 @@ raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) { char *EndPtr = NumberBuffer+sizeof(NumberBuffer); char *CurPtr = EndPtr; bool Neg = (FN.DecValue < 0); - uint64_t N = Neg ? -static_cast(FN.DecValue) : FN.DecValue; + // If the value is negative, and because we are storing the result of the ~ + // operation in an unsigned value, we can use the equivalence of + // -N == ~N + 1 to get the positive value of the negative number + uint64_t N = Neg ? (~FN.DecValue + 1UL) : FN.DecValue; while (N) { *--CurPtr = '0' + char(N % 10); N /= 10;