[clang] [clang] Fix shift-count diagnostics for compound assignments (PR #234830)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 22 20:48:09 PDT 2026


https://github.com/ShivaGanesh265 created https://github.com/llvm/llvm-project/pull/234830

Fix shift-count warnings for compound shift assignments when the left operand gets promoted to a wider integer type.

Now, `DiagnoseBadShiftValues` checks the shift count against the promoted type of the left operand. For ex: with `unsigned char x; x >>= 8;`, the shift uses the promoted type rather than the original type of `x`.

Added tests for C and C++ covering valid and invalid shift counts, along with constant-evaluation cases.

Fixes #234217.

## Testing

- `clang/test/Sema/shift.c`
- `clang/test/SemaCXX/shift.cpp`
- `clang/test/AST/ByteCode/shifts.cpp`
- Related shift-count regression tests
- Sema and SemaCXX test suites

Sema and SemaCXX tests passed with no unexpected failures.

GenAI(Chatgpt) was used for understanding of the source code and learning the concepts

>From 50ff5c6d4d6f2deeb3e3a85ad7148b907f6195ce Mon Sep 17 00:00:00 2001
From: ShivaGanesh265 <cs24btech11003 at iith.ac.in>
Date: Fri, 23 Oct 2026 03:38:13 +0000
Subject: [PATCH] [clang] Fix shift-count diagnostics for compound assignments

---
 clang/lib/Sema/SemaExpr.cpp        |  4 ++-
 clang/test/AST/ByteCode/shifts.cpp | 22 ++++----------
 clang/test/Sema/shift.c            | 46 +++++++++++++++++++++++++++---
 clang/test/SemaCXX/shift.cpp       | 46 +++++++++++++++++++++++++++---
 4 files changed, 93 insertions(+), 25 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 563b9297a6f288..6d0d81834f789f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -12174,7 +12174,9 @@ static void DiagnoseBadShiftValues(Sema& S, ExprResult &LHS, ExprResult &RHS,
     return;
   }
 
-  QualType LHSExprType = LHS.get()->getType();
+  // Check the shift count against the width of the promoted left operand.
+  // For compound assignments, use the promoted type, not the original LHS type.
+  QualType LHSExprType = LHSType;
   uint64_t LeftSize = S.Context.getTypeSize(LHSExprType);
   if (LHSExprType->isBitIntType())
     LeftSize = S.Context.getIntWidth(LHSExprType);
diff --git a/clang/test/AST/ByteCode/shifts.cpp b/clang/test/AST/ByteCode/shifts.cpp
index 58118bbea79413..119b00bd06931d 100644
--- a/clang/test/AST/ByteCode/shifts.cpp
+++ b/clang/test/AST/ByteCode/shifts.cpp
@@ -57,22 +57,12 @@ namespace shifts {
                   // cxx17-warning {{shift count >= width of type}} \
                   // ref-warning {{shift count >= width of type}} \
                   // ref-cxx17-warning {{shift count >= width of type}}
-    c <<= __CHAR_BIT__; // expected-warning {{shift count >= width of type}} \
-                        // cxx17-warning {{shift count >= width of type}} \
-                        // ref-warning {{shift count >= width of type}} \
-                        // ref-cxx17-warning {{shift count >= width of type}}
-    c >>= __CHAR_BIT__; // expected-warning {{shift count >= width of type}} \
-                        // cxx17-warning {{shift count >= width of type}} \
-                        // ref-warning {{shift count >= width of type}} \
-                        // ref-cxx17-warning {{shift count >= width of type}}
-    c <<= __CHAR_BIT__+1; // expected-warning {{shift count >= width of type}} \
-                          // cxx17-warning {{shift count >= width of type}} \
-                          // ref-warning {{shift count >= width of type}} \
-                          // ref-cxx17-warning {{shift count >= width of type}}
-    c >>= __CHAR_BIT__+1; // expected-warning {{shift count >= width of type}} \
-                          // cxx17-warning {{shift count >= width of type}} \
-                          // ref-warning {{shift count >= width of type}} \
-                          // ref-cxx17-warning {{shift count >= width of type}}
+        // These don't warn because the shift count is checked against the promoted
+        // left operand's width. The compound and regular shifts act in the same way.
+    c <<= __CHAR_BIT__;
+    c >>= __CHAR_BIT__;
+    c <<= __CHAR_BIT__+1;
+    c >>= __CHAR_BIT__+1;
     (void)((long)c << __CHAR_BIT__);
 
     c = 1 << (int)INT_MIN; // all-warning {{shift count is negative}}
diff --git a/clang/test/Sema/shift.c b/clang/test/Sema/shift.c
index 1ebccb1d0b3acc..abe030ebf5719c 100644
--- a/clang/test/Sema/shift.c
+++ b/clang/test/Sema/shift.c
@@ -32,10 +32,13 @@ void test(void) {
   c >>= -1; // expected-warning {{shift count is negative}}
   c <<= 999999; // expected-warning {{shift count >= width of type}}
   c >>= 999999; // expected-warning {{shift count >= width of type}}
-  c <<= CHAR_BIT; // expected-warning {{shift count >= width of type}}
-  c >>= CHAR_BIT; // expected-warning {{shift count >= width of type}}
-  c <<= CHAR_BIT+1; // expected-warning {{shift count >= width of type}}
-  c >>= CHAR_BIT+1; // expected-warning {{shift count >= width of type}}
+  // These do not warn: the shift count is checked against the promoted left
+  // operand's width, not the LHS's storage width, so the compound forms are
+  // diagnosed just like their non-compound equivalents.
+  c <<= CHAR_BIT;
+  c >>= CHAR_BIT;
+  c <<= CHAR_BIT+1;
+  c >>= CHAR_BIT+1;
   (void)((long)c << CHAR_BIT);
 
   int i;
@@ -71,6 +74,41 @@ void test_shift_too_much(char x) {
   (void) (x >> 80); // expected-warning {{shift count >= width of type}}
 }
 
+// For compound shift assignments, check the shift count against the width of
+// the promoted LHS, just like regular shift assignments.
+// https://github.com/llvm/llvm-project/issues/234217
+unsigned char char_compound(unsigned char x) {
+  x >>= 8;
+  return x;
+}
+
+unsigned char char_ordinary(unsigned char x) {
+  x = x >> 8;
+  return x;
+}
+
+unsigned short short_compound(unsigned short x) {
+  x >>= 16;
+  return x;
+}
+
+unsigned short short_ordinary(unsigned short x) {
+  x = x >> 16;
+  return x;
+}
+
+unsigned char count_32(unsigned char x) {
+  x >>= 32; // expected-warning {{shift count >= width of type}}
+  x = x >> 32; // expected-warning {{shift count >= width of type}}
+  return x;
+}
+
+unsigned char count_negative(unsigned char x) {
+  x >>= -1; // expected-warning {{shift count is negative}}
+  x = x >> -1; // expected-warning {{shift count is negative}}
+  return x;
+}
+
 typedef unsigned vec16 __attribute__((vector_size(16)));
 typedef unsigned vec8 __attribute__((vector_size(8)));
 
diff --git a/clang/test/SemaCXX/shift.cpp b/clang/test/SemaCXX/shift.cpp
index 6367e6d2f250bb..717871e1c42966 100644
--- a/clang/test/SemaCXX/shift.cpp
+++ b/clang/test/SemaCXX/shift.cpp
@@ -33,10 +33,13 @@ void test() {
   c >>= -1; // expected-warning {{shift count is negative}}
   c <<= 999999; // expected-warning {{shift count >= width of type}}
   c >>= 999999; // expected-warning {{shift count >= width of type}}
-  c <<= CHAR_BIT; // expected-warning {{shift count >= width of type}}
-  c >>= CHAR_BIT; // expected-warning {{shift count >= width of type}}
-  c <<= CHAR_BIT+1; // expected-warning {{shift count >= width of type}}
-  c >>= CHAR_BIT+1; // expected-warning {{shift count >= width of type}}
+  // These do not warn: the shift count is checked against the promoted left
+  // operand's width, not the LHS's storage width, so the compound forms are
+  // diagnosed just like their non-compound equivalents.
+  c <<= CHAR_BIT;
+  c >>= CHAR_BIT;
+  c <<= CHAR_BIT+1;
+  c >>= CHAR_BIT+1;
   (void)((long)c << CHAR_BIT);
 
   int i;
@@ -72,6 +75,41 @@ void test_shift_too_much(char x) {
   (void) (x >> 80); // expected-warning {{shift count >= width of type}}
 }
 
+// For compound shift assignments, check the shift count against the width of
+// the promoted LHS, just like regular shift assignments.
+// https://github.com/llvm/llvm-project/issues/234217
+unsigned char char_compound(unsigned char x) {
+  x >>= 8;
+  return x;
+}
+
+unsigned char char_ordinary(unsigned char x) {
+  x = x >> 8;
+  return x;
+}
+
+unsigned short short_compound(unsigned short x) {
+  x >>= 16;
+  return x;
+}
+
+unsigned short short_ordinary(unsigned short x) {
+  x = x >> 16;
+  return x;
+}
+
+unsigned char count_32(unsigned char x) {
+  x >>= 32; // expected-warning {{shift count >= width of type}}
+  x = x >> 32; // expected-warning {{shift count >= width of type}}
+  return x;
+}
+
+unsigned char count_negative(unsigned char x) {
+  x >>= -1; // expected-warning {{shift count is negative}}
+  x = x >> -1; // expected-warning {{shift count is negative}}
+  return x;
+}
+
 typedef unsigned vec16 __attribute__((vector_size(16)));
 typedef unsigned vec8 __attribute__((vector_size(8)));
 



More information about the cfe-commits mailing list