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: 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::from(0, V.bitWidth())); } else { - S.Stk.push(T::from(V >> RHS, V.bitWidth())); + S.Stk.push(T::from(static_cast(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(V); } else if (RHS >= V.bitWidth()) { S.Stk.push(T::from(0, V.bitWidth())); } else { - S.Stk.push(T::from(V.toUnsigned() << RHS, V.bitWidth())); + S.Stk.push( + T::from(static_cast(V.toUnsigned()) << RHS, V.bitWidth())); } return true; } @@ -1272,13 +1272,13 @@ const auto &LHS = S.Stk.pop::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(S, OpPC, LHS, Trunc(S, OpPC, Bits, -RHS)); - } else { - return ShiftRight(S, OpPC, LHS, Trunc(S, OpPC, Bits, RHS)); + return false; } + + return ShiftRight(S, OpPC, LHS, Trunc(S, OpPC, Bits, RHS)); } template @@ -1287,13 +1287,13 @@ const auto &LHS = S.Stk.pop::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(S, OpPC, LHS, Trunc(S, OpPC, Bits, -RHS)); - } else { - return ShiftLeft(S, OpPC, LHS, Trunc(S, OpPC, Bits, RHS)); + return false; } + + return ShiftLeft(S, OpPC, LHS, Trunc(S, OpPC, Bits, RHS)); } //===----------------------------------------------------------------------===// 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/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'}} +};