From 789f9b6be5df6e5151ac35e68416cdf550db1196 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 31 Jan 2012 04:08:20 +0000 Subject: [PATCH] constexpr: catch a collection of integral undefined behaviors: -INT_MIN and INT_MIN / -1 Shift by a negative or too large quantity Left shift of negative value Overflow in left shift git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149344 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticASTKinds.td | 4 ++ lib/AST/ExprConstant.cpp | 67 +++++++++++++++++++---- test/CXX/expr/expr.const/p2-0x.cpp | 24 ++++++++ 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index eb46e5f6f0..9fe22ad3ba 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -20,6 +20,10 @@ def note_constexpr_invalid_downcast : Note< "cannot cast object of dynamic type %0 to type %1">; def note_constexpr_overflow : Note< "value %0 is outside the range of representable values of type %1">; +def note_constexpr_negative_shift : Note<"negative shift count %0">; +def note_constexpr_large_shift : Note< + "shift count %0 >= width of type %1 (%2 bit%s2)">; +def note_constexpr_lshift_of_negative : Note<"left shift of negative value %0">; def note_constexpr_invalid_function : Note< "%select{non-constexpr|undefined}0 %select{function|constructor}1 %2 cannot " "be used in a constant expression">; diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 50e96bdd4b..c038e3feae 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -348,6 +348,24 @@ namespace { *Diag << v; return *this; } + + OptionalDiagnostic &operator<<(const APSInt &I) { + if (Diag) { + llvm::SmallVector Buffer; + I.toString(Buffer); + *Diag << StringRef(Buffer.data(), Buffer.size()); + } + return *this; + } + + OptionalDiagnostic &operator<<(const APFloat &F) { + if (Diag) { + llvm::SmallVector Buffer; + F.toString(Buffer); + *Diag << StringRef(Buffer.data(), Buffer.size()); + } + return *this; + } }; struct EvalInfo { @@ -1052,10 +1070,8 @@ static bool EvaluateAsBooleanCondition(const Expr *E, bool &Result, template static bool HandleOverflow(EvalInfo &Info, const Expr *E, const T &SrcValue, QualType DestType) { - llvm::SmallVector Buffer; - SrcValue.toString(Buffer); Info.Diag(E->getExprLoc(), diag::note_constexpr_overflow) - << StringRef(Buffer.data(), Buffer.size()) << DestType; + << SrcValue << DestType; return false; } @@ -4405,33 +4421,60 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { case BO_Div: if (RHS == 0) return Error(E, diag::note_expr_divide_by_zero); + // Check for overflow case: INT_MIN / -1. + if (RHS.isNegative() && RHS.isAllOnesValue() && + LHS.isSigned() && LHS.isMinSignedValue()) + HandleOverflow(Info, E, -LHS.extend(LHS.getBitWidth() + 1), E->getType()); return Success(LHS / RHS, E); case BO_Rem: if (RHS == 0) return Error(E, diag::note_expr_divide_by_zero); return Success(LHS % RHS, E); case BO_Shl: { - // During constant-folding, a negative shift is an opposite shift. + // During constant-folding, a negative shift is an opposite shift. Such a + // shift is not a constant expression. if (RHS.isSigned() && RHS.isNegative()) { + CCEDiag(E, diag::note_constexpr_negative_shift) << RHS; RHS = -RHS; goto shift_right; } shift_left: - unsigned SA - = (unsigned) RHS.getLimitedValue(LHS.getBitWidth()-1); + // C++11 [expr.shift]p1: Shift width must be less than the bit width of the + // shifted type. + unsigned SA = (unsigned) RHS.getLimitedValue(LHS.getBitWidth()-1); + if (SA != RHS) { + CCEDiag(E, diag::note_constexpr_large_shift) + << RHS << E->getType() << LHS.getBitWidth(); + } else if (LHS.isSigned()) { + // C++11 [expr.shift]p2: A signed left shift must have a non-negative + // operand, and must not overflow. + if (LHS.isNegative()) + CCEDiag(E, diag::note_constexpr_lshift_of_negative) << LHS; + else if (LHS.countLeadingZeros() <= SA) + HandleOverflow(Info, E, LHS.extend(LHS.getBitWidth() + SA) << SA, + E->getType()); + } + return Success(LHS << SA, E); } case BO_Shr: { - // During constant-folding, a negative shift is an opposite shift. + // During constant-folding, a negative shift is an opposite shift. Such a + // shift is not a constant expression. if (RHS.isSigned() && RHS.isNegative()) { + CCEDiag(E, diag::note_constexpr_negative_shift) << RHS; RHS = -RHS; goto shift_left; } shift_right: - unsigned SA = - (unsigned) RHS.getLimitedValue(LHS.getBitWidth()-1); + // C++11 [expr.shift]p1: Shift width must be less than the bit width of the + // shifted type. + unsigned SA = (unsigned) RHS.getLimitedValue(LHS.getBitWidth()-1); + if (SA != RHS) + CCEDiag(E, diag::note_constexpr_large_shift) + << RHS << E->getType() << LHS.getBitWidth(); + return Success(LHS >> SA, E); } @@ -4605,7 +4648,11 @@ bool IntExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { if (!Visit(E->getSubExpr())) return false; if (!Result.isInt()) return Error(E); - return Success(-Result.getInt(), E); + const APSInt &Value = Result.getInt(); + if (Value.isSigned() && Value.isMinSignedValue()) + HandleOverflow(Info, E, -Value.extend(Value.getBitWidth() + 1), + E->getType()); + return Success(-Value, E); } case UO_Not: { if (!Visit(E->getSubExpr())) diff --git a/test/CXX/expr/expr.const/p2-0x.cpp b/test/CXX/expr/expr.const/p2-0x.cpp index 5659273129..a39de4d2b9 100644 --- a/test/CXX/expr/expr.const/p2-0x.cpp +++ b/test/CXX/expr/expr.const/p2-0x.cpp @@ -129,6 +129,30 @@ namespace UndefinedBehavior { } } + constexpr int int_min = ~0x7fffffff; + constexpr int minus_int_min = -int_min; // expected-error {{constant expression}} expected-note {{value 2147483648 is outside the range}} + constexpr int div0 = 3 / 0; // expected-error {{constant expression}} expected-note {{division by zero}} expected-warning {{undefined}} + constexpr int mod0 = 3 % 0; // expected-error {{constant expression}} expected-note {{division by zero}} expected-warning {{undefined}} + constexpr int int_min_div_minus_1 = int_min / -1; // expected-error {{constant expression}} expected-note {{value 2147483648 is outside the range}} + + constexpr int shl_m1 = 0 << -1; // expected-error {{constant expression}} expected-note {{negative shift count -1}} expected-warning {{negative}} + constexpr int shl_0 = 0 << 0; // ok + constexpr int shl_31 = 0 << 31; // ok + constexpr int shl_32 = 0 << 32; // expected-error {{constant expression}} expected-note {{shift count 32 >= width of type 'int' (32}} expected-warning {{>= width of type}} + constexpr int shl_unsigned_negative = unsigned(-3) << 1; // ok + constexpr int shl_unsigned_into_sign = 1u << 31; // ok + constexpr int shl_unsigned_overflow = 1024u << 31; // ok + constexpr int shl_signed_negative = (-3) << 1; // expected-error {{constant expression}} expected-note {{left shift of negative value -3}} + constexpr int shl_signed_ok = 1 << 30; // ok + constexpr int shl_signed_into_sign = 1 << 31; // expected-error {{constant expression}} expected-note {{value 2147483648 is outside the range}} + constexpr int shl_signed_overflow = 1024 << 31; // expected-error {{constant expression}} expected-note {{value 2199023255552 is outside the range}} expected-warning {{requires 43 bits to represent}} + constexpr int shl_signed_ok2 = 1024 << 20; // ok + + constexpr int shr_m1 = 0 >> -1; // expected-error {{constant expression}} expected-note {{negative shift count -1}} expected-warning {{negative}} + constexpr int shr_0 = 0 >> 0; // ok + constexpr int shr_31 = 0 >> 31; // ok + constexpr int shr_32 = 0 >> 32; // expected-error {{constant expression}} expected-note {{shift count 32 >= width of type}} expected-warning {{>= width of type}} + struct S { int m; }; -- 2.40.0