tbaeder created this revision.
tbaeder added reviewers: aaron.ballman, erichkeane, tahonermann, shafik.
Herald added a project: All.
tbaeder requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Add a test case based on `test/SemaCXX/shift.cpp`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D136532

Files:
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/Interp.h
  clang/lib/AST/Interp/Opcodes.td
  clang/test/AST/Interp/shifts.cpp

Index: clang/test/AST/Interp/shifts.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/Interp/shifts.cpp
@@ -0,0 +1,103 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify %s
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify=cxx17 %s
+// RUN: %clang_cc1 -std=c++20 -verify=ref %s
+// RUN: %clang_cc1 -std=c++17 -verify=ref-cxx17 %s
+
+#define CHAR_BIT (sizeof(char) * 8)
+#define WORD_BIT (sizeof(int) * 8)
+#define INT_MAX (__INT_MAX__)
+#define INT_MIN (~__INT_MAX__)
+
+
+namespace shifts {
+  constexpr void test() { // ref-error {{constexpr function never produces a constant expression}} \
+                          // ref-cxx17-error {{constexpr function never produces a constant expression}}
+
+    char c; // cxx17-warning {{uninitialized variable}} \
+            // ref-cxx17-warning {{uninitialized variable}}
+
+    c = 0 << 0;
+    c = 0 << 1;
+    c = 1 << 0;
+    c = 1 << -0;
+    c = 1 >> -0;
+    c = 1 << -1; // expected-warning {{shift count is negative}} \
+                 // cxx17-warning {{shift count is negative}} \
+                 // ref-warning {{shift count is negative}} \
+                 // ref-note {{negative shift count -1}} \
+                 // ref-cxx17-warning {{shift count is negative}} \
+                 // ref-cxx17-note {{negative shift count -1}}
+
+    c = 1 >> -1; // expected-warning {{shift count is negative}} \
+                 // cxx17-warning {{shift count is negative}} \
+                 // ref-warning {{shift count is negative}} \
+                 // ref-cxx17-warning {{shift count is negative}}
+    c = 1 << (unsigned)-1; // expected-warning {{shift count >= width of type}} \
+                           // FIXME: 'implicit conversion' warning missing in the new interpreter. \
+                           // cxx17-warning {{shift count >= width of type}} \
+                           // ref-warning {{shift count >= width of type}} \
+                           // ref-warning {{implicit conversion}} \
+                           // ref-cxx17-warning {{shift count >= width of type}} \
+                           // ref-cxx17-warning {{implicit conversion}}
+    c = 1 >> (unsigned)-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 = 1 << c;
+    // FIXME: Enable this.
+#if 0
+    //c <<= 0;
+    //c >>= 0;
+    //c <<= 1;
+    //c >>= 1;
+    //c <<= -1; // expected-warning {{shift count is negative}}
+    //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}}
+#endif
+    (void)((long)c << CHAR_BIT);
+
+    int i; // cxx17-warning {{uninitialized variable}} \
+           // ref-cxx17-warning {{uninitialized variable}}
+    i = 1 << (WORD_BIT - 2);
+    i = 2 << (WORD_BIT - 1); // cxx17-warning {{bits to represent, but 'int' only has}} \
+                             // ref-cxx17-warning {{bits to represent, but 'int' only has}}
+    i = 1 << (WORD_BIT - 1); // cxx17-warning-not {{sets the sign bit of the shift expression}}
+    i = -1 << (WORD_BIT - 1); // cxx17-warning {{shifting a negative signed value is undefined}} \
+                              // ref-cxx17-warning {{shifting a negative signed value is undefined}}
+    i = -1 << 0; // cxx17-warning {{shifting a negative signed value is undefined}} \
+                 // ref-cxx17-warning {{shifting a negative signed value is undefined}}
+    i = 0 << (WORD_BIT - 1);
+    i = (char)1 << (WORD_BIT - 2);
+
+    unsigned u; // cxx17-warning {{uninitialized variable}} \
+                // ref-cxx17-warning {{uninitialized variable}}
+    u = 1U << (WORD_BIT - 1);
+    u = 5U << (WORD_BIT - 1);
+
+    long long int lli; // cxx17-warning {{uninitialized variable}} \
+                       // ref-cxx17-warning {{uninitialized variable}}
+    lli = INT_MIN << 2; // cxx17-warning {{shifting a negative signed value is undefined}} \
+                        // ref-cxx17-warning {{shifting a negative signed value is undefined}}
+    lli = 1LL << (sizeof(long long) * CHAR_BIT - 2);
+  }
+
+  static_assert(1 << 4 == 16, "");
+  constexpr unsigned m = 2 >> 1;
+  static_assert(m == 1, "");
+  constexpr unsigned char c = 0 << 8;
+  static_assert(c == 0, "");
+  static_assert(true << 1, "");
+  static_assert(1 << (WORD_BIT +1) == 0, "");  // expected-error {{not an integral constant expression}} \
+                                               // expected-note {{>= width of type 'int'}} \
+                                               // cxx17-error {{not an integral constant expression}} \
+                                               // cxx17-note {{>= width of type 'int'}} \
+                                               // ref-error {{not an integral constant expression}} \
+                                               // ref-note {{>= width of type 'int'}} \
+                                               // ref-cxx17-error {{not an integral constant expression}} \
+                                               // ref-cxx17-note {{>= width of type 'int'}}
+};
Index: clang/lib/AST/Interp/Opcodes.td
===================================================================
--- clang/lib/AST/Interp/Opcodes.td
+++ clang/lib/AST/Interp/Opcodes.td
@@ -433,6 +433,16 @@
 def Div  : IntegerOpcode;
 def Divf : FloatOpcode;
 
+def Shl : Opcode {
+  let Types = [IntegerTypeClass, IntegerTypeClass];
+  let HasGroup = 1;
+}
+
+def Shr : Opcode {
+  let Types = [IntegerTypeClass, IntegerTypeClass];
+  let HasGroup = 1;
+}
+
 def BitAnd : IntegerOpcode;
 def BitOr : IntegerOpcode;
 def BitXor : IntegerOpcode;
Index: clang/lib/AST/Interp/Interp.h
===================================================================
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -1236,7 +1236,7 @@
   if (RHS >= V.bitWidth()) {
     S.Stk.push<T>(T::from(0, V.bitWidth()));
   } else {
-    S.Stk.push<T>(T::from(V >> RHS, V.bitWidth()));
+    S.Stk.push<T>(T::from(static_cast<unsigned>(V) >> RHS, V.bitWidth()));
   }
   return true;
 }
@@ -1251,17 +1251,17 @@
     if (V.isNegative()) {
       const Expr *E = S.Current->getExpr(OpPC);
       S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt();
-    } else if (V.countLeadingZeros() < RHS) {
-      S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards);
+      return false;
     }
   }
 
-  if (V.bitWidth() == 1) {
+  if constexpr (T::bitWidth() == 1) {
     S.Stk.push<T>(V);
   } else if (RHS >= V.bitWidth()) {
     S.Stk.push<T>(T::from(0, V.bitWidth()));
   } else {
-    S.Stk.push<T>(T::from(V.toUnsigned() << RHS, V.bitWidth()));
+    S.Stk.push<T>(
+        T::from(static_cast<unsigned>(V.toUnsigned()) << RHS, V.bitWidth()));
   }
   return true;
 }
@@ -1272,13 +1272,13 @@
   const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
   const unsigned Bits = LHS.bitWidth();
 
-  if (RHS.isSigned() && RHS.isNegative()) {
+  if (RHS.isNegative()) {
     const SourceInfo &Loc = S.Current->getSource(OpPC);
     S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
-    return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
-  } else {
-    return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
+    return false;
   }
+
+  return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
 }
 
 template <PrimType TL, PrimType TR>
@@ -1287,13 +1287,13 @@
   const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
   const unsigned Bits = LHS.bitWidth();
 
-  if (RHS.isSigned() && RHS.isNegative()) {
+  if (RHS.isNegative()) {
     const SourceInfo &Loc = S.Current->getSource(OpPC);
     S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
-    return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
-  } else {
-    return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
+    return false;
   }
+
+  return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
 }
 
 //===----------------------------------------------------------------------===//
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -294,6 +294,10 @@
     return Discard(this->emitBitOr(*T, BO));
   case BO_Xor:
     return Discard(this->emitBitXor(*T, BO));
+  case BO_Shl:
+    return Discard(this->emitShl(*LT, *RT, BO));
+  case BO_Shr:
+    return Discard(this->emitShr(*LT, *RT, BO));
   case BO_LAnd:
   case BO_LOr:
   default:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to