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: @@ -608,7 +612,13 @@ case BO_DivAssign: case BO_RemAssign: case BO_ShlAssign: + if (!this->emitShl(*LT, *RT, E)) + return false; + break; case BO_ShrAssign: + if (!this->emitShr(*LT, *RT, E)) + return false; + break; case BO_AndAssign: case BO_XorAssign: case BO_OrAssign: Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -1251,84 +1251,63 @@ // Shr, Shl //===----------------------------------------------------------------------===// -template ::T> -unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) { +template +inline bool Shr(InterpState &S, CodePtr OpPC) { + using LT = typename PrimConv::T; + using RT = typename PrimConv::T; + const auto &RHS = S.Stk.pop(); + const auto &LHS = S.Stk.pop(); + const unsigned Bits = LHS.bitWidth(); + + if (RHS.isNegative()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); + return false; + } + // C++11 [expr.shift]p1: Shift width must be less than the bit width of // the shifted type. - if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) { + if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) { const Expr *E = S.Current->getExpr(OpPC); - const APSInt Val = V.toAPSInt(); + const APSInt Val = RHS.toAPSInt(); QualType Ty = E->getType(); S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits; - return Bits; - } else { - return static_cast(V); - } -} - -template ::T> -inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { - if (RHS >= V.bitWidth()) { - S.Stk.push(T::from(0, V.bitWidth())); - } else { - S.Stk.push(T::from(V >> RHS, V.bitWidth())); - } - return true; -} - -template ::T> -inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { - if (V.isSigned() && !S.getLangOpts().CPlusPlus20) { - // C++11 [expr.shift]p2: A signed left shift must have a non-negative - // operand, and must not overflow the corresponding unsigned type. - // C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to - // E1 x 2^E2 module 2^N. - 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) { - 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())); - } + unsigned URHS = static_cast(RHS); + S.Stk.push(LT::from(static_cast(LHS) >> URHS, LHS.bitWidth())); return true; } -template -inline bool Shr(InterpState &S, CodePtr OpPC) { - const auto &RHS = S.Stk.pop::T>(); - const auto &LHS = S.Stk.pop::T>(); +template +inline bool Shl(InterpState &S, CodePtr OpPC) { + using LT = typename PrimConv::T; + using RT = typename PrimConv::T; + const auto &RHS = S.Stk.pop(); + const auto &LHS = S.Stk.pop(); 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; } -} -template -inline bool Shl(InterpState &S, CodePtr OpPC) { - const auto &RHS = S.Stk.pop::T>(); - const auto &LHS = S.Stk.pop::T>(); - const unsigned Bits = LHS.bitWidth(); - - if (RHS.isSigned() && 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)); + // C++11 [expr.shift]p1: Shift width must be less than the bit width of + // the shifted type. + if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) { + const Expr *E = S.Current->getExpr(OpPC); + const APSInt Val = RHS.toAPSInt(); + QualType Ty = E->getType(); + S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits; + return false; } + + unsigned URHS = static_cast(RHS); + S.Stk.push(LT::from(static_cast(LHS) << URHS, LHS.bitWidth())); + + return true; } //===----------------------------------------------------------------------===// 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,124 @@ +// 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; + c <<= 0; + c >>= 0; + c <<= 1; + c >>= 1; + c <<= -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; // 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 <<= 999999; // 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 >>= 999999; // 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; // 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}} + (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'}} +};