C++11 generalized constant expressions: support pointer comparisons where the
result is not unspecified. llvm-svn: 143329
This commit is contained in:
parent
cd196f6b4f
commit
8b3497ee54
|
@ -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() ||
|
||||
|
|
|
@ -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];
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue