Author: Timm Bäder Date: 2023-07-20T15:35:47+02:00 New Revision: d6b0af0574ca21ae7b0fa6aa364345159f451da1
URL: https://github.com/llvm/llvm-project/commit/d6b0af0574ca21ae7b0fa6aa364345159f451da1 DIFF: https://github.com/llvm/llvm-project/commit/d6b0af0574ca21ae7b0fa6aa364345159f451da1.diff LOG: [clang][Interp] Add more shift error checking Differential Revision: https://reviews.llvm.org/D150209 Added: Modified: clang/lib/AST/Interp/Integral.h clang/lib/AST/Interp/Interp.h clang/test/AST/Interp/shifts.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/Integral.h b/clang/lib/AST/Interp/Integral.h index cc7c7a9d2430ee..de588ab8c9f191 100644 --- a/clang/lib/AST/Interp/Integral.h +++ b/clang/lib/AST/Interp/Integral.h @@ -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) diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index d70b732f6fd865..15112001536650 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -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(); @@ -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; } @@ -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; @@ -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; diff --git a/clang/test/AST/Interp/shifts.cpp b/clang/test/AST/Interp/shifts.cpp index 3b2fc2619fe355..cf71e7145c2742 100644 --- a/clang/test/AST/Interp/shifts.cpp +++ b/clang/test/AST/Interp/shifts.cpp @@ -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)'}} }; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits