Skip to content

Commit

Permalink
[clang][Interp] Add more shift error checking
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D150209
  • Loading branch information
tbaederr committed Jul 20, 2023
1 parent af67614 commit d6b0af0
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 5 deletions.
6 changes: 5 additions & 1 deletion clang/lib/AST/Interp/Integral.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ template <unsigned Bits, bool Signed> class Integral final {
return Compare(V, RHS.V);
}

unsigned countLeadingZeros() const { return llvm::countl_zero<ReprT>(V); }
unsigned countLeadingZeros() const {
if constexpr (!Signed)
return llvm::countl_zero<ReprT>(V);
llvm_unreachable("Don't call countLeadingZeros() on signed types.");
}

Integral truncate(unsigned TruncBits) const {
if (TruncBits >= Bits)
Expand Down
23 changes: 19 additions & 4 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD);
bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This);

/// Checks if the shift operation is legal.
template <typename RT>
bool CheckShift(InterpState &S, CodePtr OpPC, const RT &RHS, unsigned Bits) {
template <typename LT, typename RT>
bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS,
unsigned Bits) {
if (RHS.isNegative()) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
Expand All @@ -122,6 +123,20 @@ bool CheckShift(InterpState &S, CodePtr OpPC, const RT &RHS, unsigned Bits) {
S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
return false;
}

if (LHS.isSigned() && !S.getLangOpts().CPlusPlus20) {
const Expr *E = S.Current->getExpr(OpPC);
// C++11 [expr.shift]p2: A signed left shift must have a non-negative
// operand, and must not overflow the corresponding unsigned type.
if (LHS.isNegative())
S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << LHS.toAPSInt();
else if (LHS.toUnsigned().countLeadingZeros() < static_cast<unsigned>(RHS))
S.CCEDiag(E, diag::note_constexpr_lshift_discards);
}

// C++2a [expr.shift]p2: [P0907R4]:
// E1 << E2 is the unique value congruent to
// E1 x 2^E2 module 2^N.
return true;
}

Expand Down Expand Up @@ -1523,7 +1538,7 @@ inline bool Shr(InterpState &S, CodePtr OpPC) {
const auto &LHS = S.Stk.pop<LT>();
const unsigned Bits = LHS.bitWidth();

if (!CheckShift<RT>(S, OpPC, RHS, Bits))
if (!CheckShift(S, OpPC, LHS, RHS, Bits))
return false;

Integral<LT::bitWidth(), false> R;
Expand All @@ -1540,7 +1555,7 @@ inline bool Shl(InterpState &S, CodePtr OpPC) {
const auto &LHS = S.Stk.pop<LT>();
const unsigned Bits = LHS.bitWidth();

if (!CheckShift<RT>(S, OpPC, RHS, Bits))
if (!CheckShift(S, OpPC, LHS, RHS, Bits))
return false;

Integral<LT::bitWidth(), false> R;
Expand Down
35 changes: 35 additions & 0 deletions clang/test/AST/Interp/shifts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,39 @@ namespace shifts {
constexpr signed int R = (sizeof(unsigned) * 8) + 1;
constexpr decltype(L) M = (R > 32 && R < 64) ? L << R : 0;
constexpr decltype(L) M2 = (R > 32 && R < 64) ? L >> R : 0;


constexpr int signedShift() { // cxx17-error {{never produces a constant expression}} \
// ref-cxx17-error {{never produces a constant expression}}
return 1024 << 31; // cxx17-warning {{signed shift result}} \
// ref-cxx17-warning {{signed shift result}} \
// cxx17-note {{signed left shift discards bits}} \
// ref-cxx17-note {{signed left shift discards bits}}
}

constexpr int negativeShift() { // cxx17-error {{never produces a constant expression}} \
// ref-cxx17-error {{never produces a constant expression}}
return -1 << 2; // cxx17-warning {{shifting a negative signed value is undefined}} \
// ref-cxx17-warning {{shifting a negative signed value is undefined}} \
// cxx17-note {{left shift of negative value -1}} \
// ref-cxx17-note {{left shift of negative value -1}}
}

constexpr int foo(int a) {
return -a << 2; // cxx17-note {{left shift of negative value -10}} \
// ref-cxx17-note {{left shift of negative value -10}} \
// cxx17-note {{left shift of negative value -2}} \
// ref-cxx17-note {{left shift of negative value -2}}
}
static_assert(foo(10)); // cxx17-error {{not an integral constant expression}} \
// cxx17-note {{in call to 'foo(10)'}} \
// ref-cxx17-error {{not an integral constant expression}} \
// ref-cxx17-note {{in call to 'foo(10)'}}

constexpr int a = -2;
static_assert(foo(a));
static_assert(foo(-a)); // cxx17-error {{not an integral constant expression}} \
// cxx17-note {{in call to 'foo(2)'}} \
// ref-cxx17-error {{not an integral constant expression}} \
// ref-cxx17-note {{in call to 'foo(2)'}}
};

0 comments on commit d6b0af0

Please sign in to comment.