diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -237,6 +237,10 @@ return Discard(this->emitBitAnd(*T, BO)); case BO_Or: return Discard(this->emitBitOr(*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: @@ -451,7 +455,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: diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1103,84 +1103,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; } //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -402,6 +402,17 @@ let Types = [NumberTypeClass]; let HasGroup = 1; } + +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 Div : Opcode { diff --git a/clang/test/AST/Interp/shifts.cpp b/clang/test/AST/Interp/shifts.cpp new file mode 100644 --- /dev/null +++ b/clang/test/AST/Interp/shifts.cpp @@ -0,0 +1,142 @@ +// 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 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 << (__INT_WIDTH__ - 2); + i = 2 << (__INT_WIDTH__ - 1); // cxx17-warning {{bits to represent, but 'int' only has}} \ + // ref-cxx17-warning {{bits to represent, but 'int' only has}} + i = 1 << (__INT_WIDTH__ - 1); // cxx17-warning-not {{sets the sign bit of the shift expression}} + i = -1 << (__INT_WIDTH__ - 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 << (__INT_WIDTH__ - 1); + i = (char)1 << (__INT_WIDTH__ - 2); + + unsigned u; // cxx17-warning {{uninitialized variable}} \ + // ref-cxx17-warning {{uninitialized variable}} + u = 1U << (__INT_WIDTH__ - 1); + u = 5U << (__INT_WIDTH__ - 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 << (__INT_WIDTH__ +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'}} + + constexpr int i1 = 1 << -1; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{negative shift count -1}} \ + // cxx17-error {{must be initialized by a constant expression}} \ + // cxx17-note {{negative shift count -1}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{negative shift count -1}} \ + // ref-cxx17-error {{must be initialized by a constant expression}} \ + // ref-cxx17-note {{negative shift count -1}} + + constexpr int i2 = 1 << (__INT_WIDTH__ + 1); // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{>= width of type}} \ + // cxx17-error {{must be initialized by a constant expression}} \ + // cxx17-note {{>= width of type}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{>= width of type}} \ + // ref-cxx17-error {{must be initialized by a constant expression}} \ + // ref-cxx17-note {{>= width of type}} + + constexpr char c2 = 1; + constexpr int i3 = c2 << (__CHAR_BIT__ + 1); // Not ill-formed +};