summaryrefslogtreecommitdiffstats
path: root/clang/lib/AST/ExprConstant.cpp
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2012-02-01 08:10:20 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2012-02-01 08:10:20 +0000
commit1b470417e4255ac6343e0acafdcf57f21bf24e81 (patch)
treec1bcc5bca49dcb0033d9aaf9d0a288519a609b2c /clang/lib/AST/ExprConstant.cpp
parent6b2bd93918bceac12b0aea61fd13a65676cd4577 (diff)
downloadbcm5719-llvm-1b470417e4255ac6343e0acafdcf57f21bf24e81.tar.gz
bcm5719-llvm-1b470417e4255ac6343e0acafdcf57f21bf24e81.zip
constexpr: check for overflow in pointer subtraction.
This is a mess. According to the C++11 standard, pointer subtraction only has undefined behavior if the difference of the array indices does not fit into a ptrdiff_t. However, common implementations effectively perform a char* subtraction first, and then divide the result by the element size, which can cause overflows in some cases. Those cases are not considered to be undefined behavior by this change; perhaps they should be. llvm-svn: 149490
Diffstat (limited to 'clang/lib/AST/ExprConstant.cpp')
-rw-r--r--clang/lib/AST/ExprConstant.cpp29
1 files changed, 23 insertions, 6 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 731bddd6b8c..3209bb343b0 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -4347,6 +4347,9 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
// - Pointer subtractions must be on elements of the same array.
// - Pointer comparisons must be between members with the same access.
+ const CharUnits &LHSOffset = LHSValue.getLValueOffset();
+ const CharUnits &RHSOffset = RHSValue.getLValueOffset();
+
if (E->getOpcode() == BO_Sub) {
QualType Type = E->getLHS()->getType();
QualType ElementType = Type->getAs<PointerType>()->getPointeeType();
@@ -4355,14 +4358,28 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
if (!HandleSizeof(Info, ElementType, ElementSize))
return false;
- CharUnits Diff = LHSValue.getLValueOffset() -
- RHSValue.getLValueOffset();
- return Success(Diff / ElementSize, E);
+ // FIXME: LLVM and GCC both compute LHSOffset - RHSOffset at runtime,
+ // and produce incorrect results when it overflows. Such behavior
+ // appears to be non-conforming, but is common, so perhaps we should
+ // assume the standard intended for such cases to be undefined behavior
+ // and check for them.
+
+ // Compute (LHSOffset - RHSOffset) / Size carefully, checking for
+ // overflow in the final conversion to ptrdiff_t.
+ APSInt LHS(
+ llvm::APInt(65, (int64_t)LHSOffset.getQuantity(), true), false);
+ APSInt RHS(
+ llvm::APInt(65, (int64_t)RHSOffset.getQuantity(), true), false);
+ APSInt ElemSize(
+ llvm::APInt(65, (int64_t)ElementSize.getQuantity(), true), false);
+ APSInt TrueResult = (LHS - RHS) / ElemSize;
+ APSInt Result = TrueResult.trunc(Info.Ctx.getIntWidth(E->getType()));
+
+ if (Result.extend(65) != TrueResult)
+ HandleOverflow(Info, E, TrueResult, E->getType());
+ return Success(Result, E);
}
- const CharUnits &LHSOffset = LHSValue.getLValueOffset();
- const CharUnits &RHSOffset = RHSValue.getLValueOffset();
-
// C++11 [expr.rel]p3:
// Pointers to void (after pointer conversions) can be compared, with a
// result defined as follows: If both pointers represent the same
OpenPOWER on IntegriCloud