From: Richard Smith Date: Tue, 21 Oct 2014 23:01:04 +0000 (+0000) Subject: PR21327 / C++ DR1652 / C++ DR73: comparing a past-the-end pointer for one X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=817b2aacb4cf8b18cc97d2eaf5366aa47416a932;p=clang PR21327 / C++ DR1652 / C++ DR73: comparing a past-the-end pointer for one complete object to a pointer to the start of another complete object does not evaluate to the constant 'false'. All other comparisons between the addresses of subobjects of distinct complete objects still do. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@220343 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 532720666b..78de2578be 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -6461,6 +6461,27 @@ static bool HasSameBase(const LValue &A, const LValue &B) { A.getLValueCallIndex() == B.getLValueCallIndex(); } +/// \brief Determine whether this is a pointer past the end of the complete +/// object referred to by the lvalue. +static bool isOnePastTheEndOfCompleteObject(const ASTContext &Ctx, + const LValue &LV) { + // A null pointer can be viewed as being "past the end" but we don't + // choose to look at it that way here. + if (!LV.getLValueBase()) + return false; + + // If the designator is valid and refers to a subobject, we're not pointing + // past the end. + if (!LV.getLValueDesignator().Invalid && + !LV.getLValueDesignator().isOnePastTheEnd()) + return false; + + // We're a past-the-end pointer if we point to the byte after the object, + // no matter what our type or path is. + auto Size = Ctx.getTypeSizeInChars(getType(LV.getLValueBase())); + return LV.getLValueOffset() == Size; +} + namespace { /// \brief Data recursive integer evaluator of certain binary operators. @@ -6923,6 +6944,13 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { // object. if (IsWeakLValue(LHSValue) || IsWeakLValue(RHSValue)) return Error(E); + // We can't compare the address of the start of one object with the + // past-the-end address of another object, per C++ DR1652. + if ((LHSValue.Base && LHSValue.Offset.isZero() && + isOnePastTheEndOfCompleteObject(Info.Ctx, RHSValue)) || + (RHSValue.Base && RHSValue.Offset.isZero() && + isOnePastTheEndOfCompleteObject(Info.Ctx, LHSValue))) + return Error(E); // Pointers with different bases cannot represent the same object. // (Note that clang defaults to -fmerge-all-constants, which can // lead to inconsistent results for comparisons involving the address diff --git a/test/CXX/drs/dr0xx.cpp b/test/CXX/drs/dr0xx.cpp index 781c1883ff..011b4201b0 100644 --- a/test/CXX/drs/dr0xx.cpp +++ b/test/CXX/drs/dr0xx.cpp @@ -823,7 +823,7 @@ namespace dr70 { // dr70: yes namespace dr73 { // dr73: no // The resolution to dr73 is unworkable. Consider: int a, b; - static_assert(&a + 1 != &b, ""); + static_assert(&a + 1 != &b, ""); // expected-error {{not an integral constant expression}} } #endif diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 24a9d670e8..7c938d54ff 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1935,3 +1935,7 @@ namespace PR19010 { }; void test() { constexpr Test t; } } + +void PR21327(int a, int b) { + static_assert(&a + 1 != &b, ""); // expected-error {{constant expression}} +}