C++11 generalized constant expressions: support pointer comparisons where the

result is not unspecified.

llvm-svn: 143329
This commit is contained in:
Richard Smith 2011-10-31 01:37:14 +00:00
parent cd196f6b4f
commit 8b3497ee54
2 changed files with 99 additions and 40 deletions

View File

@ -195,9 +195,10 @@ namespace {
CharUnits Offset;
unsigned CallIndex;
const Expr *getLValueBase() { return Base; }
const Expr *getLValueBase() const { return Base; }
CharUnits &getLValueOffset() { return Offset; }
unsigned getLValueCallIndex() { return CallIndex; }
const CharUnits &getLValueOffset() const { return Offset; }
unsigned getLValueCallIndex() const { return CallIndex; }
void moveInto(CCValue &V) const {
V = CCValue(Base, Offset, CallIndex);
@ -416,6 +417,23 @@ static bool IsConstNonVolatile(QualType T) {
return Quals.hasConst() && !Quals.hasVolatile();
}
const ValueDecl *GetLValueBaseDecl(const LValue &LVal) {
if (!LVal.Base)
return 0;
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(LVal.Base))
return DRE->getDecl();
// FIXME: Static data members accessed via a MemberExpr are represented as
// that MemberExpr. We should use the Decl directly instead.
if (const MemberExpr *ME = dyn_cast<MemberExpr>(LVal.Base)) {
assert(!isa<FieldDecl>(ME->getMemberDecl()) && "shouldn't see fields here");
return ME->getMemberDecl();
}
return 0;
}
bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type,
const LValue &LVal, CCValue &RVal) {
const Expr *Base = LVal.Base;
@ -431,26 +449,12 @@ bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type,
if (!LVal.Offset.isZero())
return false;
const Decl *D = 0;
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Base)) {
if (const ValueDecl *D = GetLValueBaseDecl(LVal)) {
// If the lvalue has been cast to some other type, don't try to read it.
// FIXME: Could simulate a bitcast here.
if (!Info.Ctx.hasSameUnqualifiedType(Type, DRE->getType()))
return false;
D = DRE->getDecl();
}
if (!Info.Ctx.hasSameUnqualifiedType(Type, D->getType()))
return 0;
// FIXME: Static data members accessed via a MemberExpr are represented as
// that MemberExpr. We should use the Decl directly instead.
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Base)) {
if (!Info.Ctx.hasSameUnqualifiedType(Type, ME->getType()))
return false;
D = ME->getMemberDecl();
assert(!isa<FieldDecl>(D) && "shouldn't see fields here");
}
if (D) {
// In C++98, const, non-volatile integers initialized with ICEs are ICEs.
// In C++11, constexpr, non-volatile variables initialized with constant
// expressions are constant expressions too. Inside constexpr functions,
@ -1767,6 +1771,25 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) {
}
}
static bool HasSameBase(const LValue &A, const LValue &B) {
if (!A.getLValueBase())
return !B.getLValueBase();
if (!B.getLValueBase())
return false;
if (A.getLValueBase() != B.getLValueBase()) {
const Decl *ADecl = GetLValueBaseDecl(A);
if (!ADecl)
return false;
const Decl *BDecl = GetLValueBaseDecl(B);
if (ADecl != BDecl)
return false;
}
return IsGlobalLValue(A.getLValueBase()) ||
A.getLValueCallIndex() == B.getLValueCallIndex();
}
bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
if (E->isAssignmentOp())
return Error(E->getOperatorLoc(), diag::note_invalid_subexpr_in_ice, E);
@ -1890,7 +1913,7 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
}
if (LHSTy->isPointerType() && RHSTy->isPointerType()) {
if (E->getOpcode() == BO_Sub || E->isEqualityOp()) {
if (E->getOpcode() == BO_Sub || E->isComparisonOp()) {
LValue LHSValue;
if (!EvaluatePointer(E->getLHS(), LHSValue, Info))
return false;
@ -1899,24 +1922,17 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
if (!EvaluatePointer(E->getRHS(), RHSValue, Info))
return false;
// Reject any bases from the normal codepath; we special-case comparisons
// to null.
if (LHSValue.getLValueBase()) {
// Reject differing bases from the normal codepath; we special-case
// comparisons to null.
if (!HasSameBase(LHSValue, RHSValue)) {
if (!E->isEqualityOp())
return false;
if (RHSValue.getLValueBase() || !RHSValue.getLValueOffset().isZero())
if ((LHSValue.getLValueBase() || !LHSValue.getLValueOffset().isZero())&&
(RHSValue.getLValueBase() || !RHSValue.getLValueOffset().isZero()))
return false;
LValue &NonNull = LHSValue.getLValueBase() ? LHSValue : RHSValue;
bool bres;
if (!EvalPointerValueAsBool(LHSValue, bres))
return false;
return Success(bres ^ (E->getOpcode() == BO_EQ), E);
} else if (RHSValue.getLValueBase()) {
if (!E->isEqualityOp())
return false;
if (LHSValue.getLValueBase() || !LHSValue.getLValueOffset().isZero())
return false;
bool bres;
if (!EvalPointerValueAsBool(RHSValue, bres))
if (!EvalPointerValueAsBool(NonNull, bres))
return false;
return Success(bres ^ (E->getOpcode() == BO_EQ), E);
}
@ -1933,13 +1949,18 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
RHSValue.getLValueOffset();
return Success(Diff / ElementSize, E);
}
bool Result;
if (E->getOpcode() == BO_EQ) {
Result = LHSValue.getLValueOffset() == RHSValue.getLValueOffset();
} else {
Result = LHSValue.getLValueOffset() != RHSValue.getLValueOffset();
const CharUnits &LHSOffset = LHSValue.getLValueOffset();
const CharUnits &RHSOffset = RHSValue.getLValueOffset();
switch (E->getOpcode()) {
default: llvm_unreachable("missing comparison operator");
case BO_LT: return Success(LHSOffset < RHSOffset, E);
case BO_GT: return Success(LHSOffset > RHSOffset, E);
case BO_LE: return Success(LHSOffset <= RHSOffset, E);
case BO_GE: return Success(LHSOffset >= RHSOffset, E);
case BO_EQ: return Success(LHSOffset == RHSOffset, E);
case BO_NE: return Success(LHSOffset != RHSOffset, E);
}
return Success(Result, E);
}
}
if (!LHSTy->isIntegralOrEnumerationType() ||

View File

@ -157,3 +157,41 @@ namespace FunctionPointers {
constexpr int Invalid = Apply(Select(0), 0); // expected-error {{must be initialized by a constant expression}}
}
namespace PointerComparison {
int x, y;
constexpr bool g1 = &x == &y; // expected-error {{must be initialized by a constant expression}}
constexpr bool g2 = &x != &y; // expected-error {{must be initialized by a constant expression}}
constexpr bool g3 = &x <= &y; // expected-error {{must be initialized by a constant expression}}
constexpr bool g4 = &x >= &y; // expected-error {{must be initialized by a constant expression}}
constexpr bool g5 = &x < &y; // expected-error {{must be initialized by a constant expression}}
constexpr bool g6 = &x > &y; // expected-error {{must be initialized by a constant expression}}
struct S { int x, y; } s;
constexpr bool m1 = &s.x == &s.y;
constexpr bool m2 = &s.x != &s.y;
constexpr bool m3 = &s.x <= &s.y;
constexpr bool m4 = &s.x >= &s.y;
constexpr bool m5 = &s.x < &s.y;
constexpr bool m6 = &s.x > &s.y;
constexpr bool n1 = 0 == &y;
constexpr bool n2 = 0 != &y;
constexpr bool n3 = 0 <= &y; // expected-error {{must be initialized by a constant expression}}
constexpr bool n4 = 0 >= &y; // expected-error {{must be initialized by a constant expression}}
constexpr bool n5 = 0 < &y; // expected-error {{must be initialized by a constant expression}}
constexpr bool n6 = 0 > &y; // expected-error {{must be initialized by a constant expression}}
constexpr bool n7 = &x == 0;
constexpr bool n8 = &x != 0;
constexpr bool n9 = &x <= 0; // expected-error {{must be initialized by a constant expression}}
constexpr bool n10 = &x >= 0; // expected-error {{must be initialized by a constant expression}}
constexpr bool n11 = &x < 0; // expected-error {{must be initialized by a constant expression}}
constexpr bool n12 = &x > 0; // expected-error {{must be initialized by a constant expression}}
using check = int[m1 + (m2<<1) + (m3<<2) + (m4<<3) + (m5<<4) + (m6<<5) +
(n1<<6) + (n2<<7) + (n7<<8) + (n8<<9)];
using check = int[2+4+16+128+512];
}