[clang] [Clang] Fix `-Wsign-compare` assertion when negating a vector of unsigned integers (PR #223295)

Akash Manna via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 13 20:35:21 PDT 2026


https://github.com/akash-manna-sky created https://github.com/llvm/llvm-project/pull/223295

Fixes #203575

`-1 == -*c`, where `c` points to a vector of `unsigned`, hit the `unsigned range includes negative?` assertion in `AnalyzeComparison`. The range analysis for unary `-` and `~` (added in #126846) checks whether the result type is unsigned with `isUnsignedIntegerType()`, which never looks through vector types. So the negated unsigned vector was treated as a signed negation and came back with a possibly-negative range, even though the operand's own range was correctly unsigned. Same story for `~*c`.

The check now uses `hasUnsignedIntegerRepresentation()`, which looks through vector and matrix element types the same way `IntRange::forValueOfType` and the caller in `AnalyzeComparison` already do. The binary-operator result gets the same predicate for its non-negative fixup, since it had the identical blind spot. With that, a negated unsigned vector takes the same path as a negated `unsigned` scalar, and the comparison warns about mixed signs again as it did before Clang 21.

>From d0137837a04bbc95249d50bd2a8e8ac1ccf36859 Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Mon, 14 Sep 2026 09:02:55 +0530
Subject: [PATCH] [Clang] Fix -Wsign-compare assertion when negating a vector
 of unsigned integers

TryGetExprRange decided whether unary - and ~ keep an unsigned range by
calling isUnsignedIntegerType(), which is false for vector types. A
negated vector of unsigned therefore took the signed-negation path and
came back as a possibly-negative range, tripping the "unsigned range
includes negative?" assertion in AnalyzeComparison. Use
hasUnsignedIntegerRepresentation(), which looks through vector and
matrix element types like the rest of the range analysis does, and
apply the same predicate to the binary-operator result. This restores
the pre-Clang-21 behaviour of warning about the mixed-sign comparison.

Fixes #203575
---
 clang/docs/ReleaseNotes.md      |  1 +
 clang/lib/Sema/SemaChecking.cpp |  6 +++---
 clang/test/Sema/compare.c       | 19 +++++++++++++++++++
 3 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3cca316a91d4d..e9d681eb89a00 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -521,6 +521,7 @@ features cannot lower the translation-unit ABI level;
 - Fixed an assertion when `#pragma omp declare simd` or `#pragma omp declare variant` is followed by another OpenMP declarative directive containing a qualified identifier. (#GH217204)
 - Fixed a crash when an `asm` label names the register for a global variable of incomplete type. (#GH219746)
 - Fixed an ICE hat occurred when using `__imag int/float` as lvalue in assignment. (#GH119498)
+- Fixed an assertion failure in `-Wsign-compare` when a negated or complemented vector of unsigned integers was compared against a signed constant. (#GH203575)
 
 #### Bug Fixes to Compiler Builtins
 
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 7374ce7404cbd..3b5523bf34d9b 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12207,7 +12207,7 @@ static std::optional<IntRange> TryGetExprRange(ASTContext &C, const Expr *E,
       return std::nullopt;
 
     IntRange C = Combine(*L, *R);
-    C.NonNegative |= T->isUnsignedIntegerOrEnumerationType();
+    C.NonNegative |= T->hasUnsignedIntegerRepresentation();
     C.Width = std::min(C.Width, MaxWidth);
     return C;
   }
@@ -12224,7 +12224,7 @@ static std::optional<IntRange> TryGetExprRange(ASTContext &C, const Expr *E,
       return IntRange::forValueOfType(C, GetExprType(E));
 
     case UO_Minus: {
-      if (E->getType()->isUnsignedIntegerType()) {
+      if (GetExprType(E)->hasUnsignedIntegerRepresentation()) {
         return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext,
                                Approximate);
       }
@@ -12242,7 +12242,7 @@ static std::optional<IntRange> TryGetExprRange(ASTContext &C, const Expr *E,
     }
 
     case UO_Not: {
-      if (E->getType()->isUnsignedIntegerType()) {
+      if (GetExprType(E)->hasUnsignedIntegerRepresentation()) {
         return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext,
                                Approximate);
       }
diff --git a/clang/test/Sema/compare.c b/clang/test/Sema/compare.c
index fdae3bc19841e..028d7bcfb3536 100644
--- a/clang/test/Sema/compare.c
+++ b/clang/test/Sema/compare.c
@@ -481,3 +481,22 @@ int test26(short n) {
   return ~n == 32768; // expected-warning {{result of comparison of 16-bit signed value == 32768 is always false}}
 }
 #endif
+
+// GH203575
+typedef unsigned gh203575_uvec __attribute__((__vector_size__(sizeof 2)));
+
+int gh203575_1(gh203575_uvec *c) {
+  return -1 == -*c; // expected-warning {{comparison of integers of different signs: 'int' and 'gh203575_uvec' (vector of 1 'unsigned int' value)}}
+}
+
+int gh203575_2(gh203575_uvec *c) {
+  return - 8 == -*c; // expected-warning {{comparison of integers of different signs: 'int' and 'gh203575_uvec' (vector of 1 'unsigned int' value)}}
+}
+
+int gh203575_3(gh203575_uvec *c) {
+  return -1 == ~*c; // expected-warning {{comparison of integers of different signs: 'int' and 'gh203575_uvec' (vector of 1 'unsigned int' value)}}
+}
+
+int gh203575_4(gh203575_uvec a, gh203575_uvec b) {
+  return a == -b; // no-warning
+}



More information about the cfe-commits mailing list