Constant expression evaluation: add support for evaluation of member pointers

and base-to-derived casts, and add proper handling of temporaries.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144926 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2011-11-17 22:56:20 +00:00
Родитель c69c42e939
Коммит e24f5fc8c7
7 изменённых файлов: 1008 добавлений и 180 удалений

Просмотреть файл

@ -27,6 +27,7 @@ namespace clang {
class FieldDecl;
class Decl;
class ValueDecl;
class CXXRecordDecl;
/// APValue - This class implements a discriminated union of [uninitialized]
/// [APSInt] [APFloat], [Complex APSInt] [Complex APFloat], [Expr + Offset],
@ -45,7 +46,8 @@ public:
Vector,
Array,
Struct,
Union
Union,
MemberPointer
};
typedef llvm::PointerUnion<const ValueDecl *, const Expr *> LValueBase;
typedef llvm::PointerIntPair<const Decl *, 1, bool> BaseOrMemberType;
@ -96,6 +98,7 @@ private:
UnionData();
~UnionData();
};
struct MemberPointerData;
enum {
MaxSize = (sizeof(ComplexAPSInt) > sizeof(ComplexAPFloat) ?
@ -131,9 +134,10 @@ public:
: Kind(Uninitialized) {
MakeLValue(); setLValue(B, O, N);
}
APValue(LValueBase B, const CharUnits &O, ArrayRef<LValuePathEntry> Path)
APValue(LValueBase B, const CharUnits &O, ArrayRef<LValuePathEntry> Path,
bool OnePastTheEnd)
: Kind(Uninitialized) {
MakeLValue(); setLValue(B, O, Path);
MakeLValue(); setLValue(B, O, Path, OnePastTheEnd);
}
APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(Uninitialized) {
MakeArray(InitElts, Size);
@ -145,6 +149,10 @@ public:
: Kind(Uninitialized) {
MakeUnion(); setUnion(D, V);
}
APValue(const ValueDecl *Member, bool IsDerivedMember,
ArrayRef<const CXXRecordDecl*> Path) : Kind(Uninitialized) {
MakeMemberPointer(Member, IsDerivedMember, Path);
}
~APValue() {
MakeUninit();
@ -161,6 +169,7 @@ public:
bool isArray() const { return Kind == Array; }
bool isStruct() const { return Kind == Struct; }
bool isUnion() const { return Kind == Union; }
bool isMemberPointer() const { return Kind == MemberPointer; }
void print(raw_ostream &OS) const;
void dump() const;
@ -218,6 +227,7 @@ public:
const CharUnits &getLValueOffset() const {
return const_cast<APValue*>(this)->getLValueOffset();
}
bool isLValueOnePastTheEnd() const;
bool hasLValuePath() const;
ArrayRef<LValuePathEntry> getLValuePath() const;
@ -297,6 +307,10 @@ public:
return const_cast<APValue*>(this)->getUnionValue();
}
const ValueDecl *getMemberPointerDecl() const;
bool isMemberPointerToDerivedMember() const;
ArrayRef<const CXXRecordDecl*> getMemberPointerPath() const;
void setInt(const APSInt &I) {
assert(isInt() && "Invalid accessor");
*(APSInt*)(char*)Data = I;
@ -328,7 +342,7 @@ public:
}
void setLValue(LValueBase B, const CharUnits &O, NoLValuePath);
void setLValue(LValueBase B, const CharUnits &O,
ArrayRef<LValuePathEntry> Path);
ArrayRef<LValuePathEntry> Path, bool OnePastTheEnd);
void setUnion(const FieldDecl *Field, const APValue &Value) {
assert(isUnion() && "Invalid accessor");
((UnionData*)(char*)Data)->Field = Field;
@ -376,6 +390,8 @@ private:
new ((void*)(char*)Data) UnionData();
Kind = Union;
}
void MakeMemberPointer(const ValueDecl *Member, bool IsDerivedMember,
ArrayRef<const CXXRecordDecl*> Path);
};
inline raw_ostream &operator<<(raw_ostream &OS, const APValue &V) {

Просмотреть файл

@ -21,7 +21,7 @@ using namespace clang;
namespace {
struct LVBase {
APValue::LValueBase Base;
llvm::PointerIntPair<APValue::LValueBase, 1, bool> BaseAndIsOnePastTheEnd;
CharUnits Offset;
unsigned PathLength;
};
@ -40,12 +40,17 @@ struct APValue::LV : LVBase {
};
LV() { PathLength = (unsigned)-1; }
~LV() { if (hasPathPtr()) delete [] PathPtr; }
~LV() { resizePath(0); }
void allocPath() {
if (hasPathPtr()) PathPtr = new LValuePathEntry[PathLength];
void resizePath(unsigned Length) {
if (Length == PathLength)
return;
if (hasPathPtr())
delete [] PathPtr;
PathLength = Length;
if (hasPathPtr())
PathPtr = new LValuePathEntry[Length];
}
void freePath() { if (hasPathPtr()) delete [] PathPtr; }
bool hasPath() const { return PathLength != (unsigned)-1; }
bool hasPathPtr() const { return hasPath() && PathLength > InlinePathSpace; }
@ -56,6 +61,43 @@ struct APValue::LV : LVBase {
}
};
namespace {
struct MemberPointerBase {
llvm::PointerIntPair<const ValueDecl*, 1, bool> MemberAndIsDerivedMember;
unsigned PathLength;
};
}
struct APValue::MemberPointerData : MemberPointerBase {
static const unsigned InlinePathSpace =
(MaxSize - sizeof(MemberPointerBase)) / sizeof(const CXXRecordDecl*);
typedef const CXXRecordDecl *PathElem;
union {
PathElem Path[InlinePathSpace];
PathElem *PathPtr;
};
MemberPointerData() { PathLength = 0; }
~MemberPointerData() { resizePath(0); }
void resizePath(unsigned Length) {
if (Length == PathLength)
return;
if (hasPathPtr())
delete [] PathPtr;
PathLength = Length;
if (hasPathPtr())
PathPtr = new PathElem[Length];
}
bool hasPathPtr() const { return PathLength > InlinePathSpace; }
PathElem *getPath() { return hasPathPtr() ? PathPtr : Path; }
const PathElem *getPath() const {
return hasPathPtr() ? PathPtr : Path;
}
};
// FIXME: Reduce the malloc traffic here.
APValue::Arr::Arr(unsigned NumElts, unsigned Size) :
@ -78,7 +120,8 @@ APValue::UnionData::~UnionData () {
const APValue &APValue::operator=(const APValue &RHS) {
if (this == &RHS)
return *this;
if (Kind != RHS.Kind || Kind == Array || Kind == Struct) {
if (Kind != RHS.Kind || Kind == Array || Kind == Struct ||
Kind == MemberPointer) {
MakeUninit();
if (RHS.isInt())
MakeInt();
@ -98,6 +141,10 @@ const APValue &APValue::operator=(const APValue &RHS) {
MakeStruct(RHS.getStructNumBases(), RHS.getStructNumFields());
else if (RHS.isUnion())
MakeUnion();
else if (RHS.isMemberPointer())
MakeMemberPointer(RHS.getMemberPointerDecl(),
RHS.isMemberPointerToDerivedMember(),
RHS.getMemberPointerPath());
}
if (isInt())
setInt(RHS.getInt());
@ -112,7 +159,8 @@ const APValue &APValue::operator=(const APValue &RHS) {
setComplexFloat(RHS.getComplexFloatReal(), RHS.getComplexFloatImag());
else if (isLValue()) {
if (RHS.hasLValuePath())
setLValue(RHS.getLValueBase(), RHS.getLValueOffset(),RHS.getLValuePath());
setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), RHS.getLValuePath(),
RHS.isLValueOnePastTheEnd());
else
setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), NoLValuePath());
} else if (isArray()) {
@ -149,6 +197,8 @@ void APValue::MakeUninit() {
((StructData*)(char*)Data)->~StructData();
else if (Kind == Union)
((UnionData*)(char*)Data)->~UnionData();
else if (Kind == MemberPointer)
((MemberPointerData*)(char*)Data)->~MemberPointerData();
Kind = Uninitialized;
}
@ -217,6 +267,9 @@ void APValue::print(raw_ostream &OS) const {
case Union:
OS << "Union: " << getUnionValue();
return;
case MemberPointer:
OS << "MemberPointer: <todo>";
return;
}
llvm_unreachable("Unknown APValue kind!");
}
@ -280,6 +333,9 @@ static void WriteShortAPValueToStream(raw_ostream& Out,
case APValue::Union:
Out << '{' << V.getUnionValue() << '}';
return;
case APValue::MemberPointer:
Out << "MemberPointer: <todo>";
return;
}
llvm_unreachable("Unknown APValue kind!");
}
@ -294,7 +350,12 @@ const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB,
const APValue::LValueBase APValue::getLValueBase() const {
assert(isLValue() && "Invalid accessor");
return ((const LV*)(const void*)Data)->Base;
return ((const LV*)(const void*)Data)->BaseAndIsOnePastTheEnd.getPointer();
}
bool APValue::isLValueOnePastTheEnd() const {
assert(isLValue() && "Invalid accessor");
return ((const LV*)(const void*)Data)->BaseAndIsOnePastTheEnd.getInt();
}
CharUnits &APValue::getLValueOffset() {
@ -316,24 +377,41 @@ ArrayRef<APValue::LValuePathEntry> APValue::getLValuePath() const {
void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath) {
assert(isLValue() && "Invalid accessor");
LV &LVal = *((LV*)(char*)Data);
LVal.freePath();
LVal.Base = B;
LVal.BaseAndIsOnePastTheEnd.setPointer(B);
LVal.BaseAndIsOnePastTheEnd.setInt(false);
LVal.Offset = O;
LVal.PathLength = (unsigned)-1;
LVal.resizePath((unsigned)-1);
}
void APValue::setLValue(LValueBase B, const CharUnits &O,
ArrayRef<LValuePathEntry> Path) {
ArrayRef<LValuePathEntry> Path, bool IsOnePastTheEnd) {
assert(isLValue() && "Invalid accessor");
LV &LVal = *((LV*)(char*)Data);
LVal.freePath();
LVal.Base = B;
LVal.BaseAndIsOnePastTheEnd.setPointer(B);
LVal.BaseAndIsOnePastTheEnd.setInt(IsOnePastTheEnd);
LVal.Offset = O;
LVal.PathLength = Path.size();
LVal.allocPath();
LVal.resizePath(Path.size());
memcpy(LVal.getPath(), Path.data(), Path.size() * sizeof(LValuePathEntry));
}
const ValueDecl *APValue::getMemberPointerDecl() const {
assert(isMemberPointer() && "Invalid accessor");
const MemberPointerData &MPD = *((const MemberPointerData*)(const char*)Data);
return MPD.MemberAndIsDerivedMember.getPointer();
}
bool APValue::isMemberPointerToDerivedMember() const {
assert(isMemberPointer() && "Invalid accessor");
const MemberPointerData &MPD = *((const MemberPointerData*)(const char*)Data);
return MPD.MemberAndIsDerivedMember.getInt();
}
ArrayRef<const CXXRecordDecl*> APValue::getMemberPointerPath() const {
assert(isMemberPointer() && "Invalid accessor");
const MemberPointerData &MPD = *((const MemberPointerData*)(const char*)Data);
return ArrayRef<const CXXRecordDecl*>(MPD.getPath(), MPD.PathLength);
}
void APValue::MakeLValue() {
assert(isUninit() && "Bad state change");
assert(sizeof(LV) <= MaxSize && "LV too big");
@ -346,3 +424,14 @@ void APValue::MakeArray(unsigned InitElts, unsigned Size) {
new ((void*)(char*)Data) Arr(InitElts, Size);
Kind = Array;
}
void APValue::MakeMemberPointer(const ValueDecl *Member, bool IsDerivedMember,
ArrayRef<const CXXRecordDecl*> Path) {
assert(isUninit() && "Bad state change");
MemberPointerData *MPD = new ((void*)(char*)Data) MemberPointerData;
Kind = MemberPointer;
MPD->MemberAndIsDerivedMember.setPointer(Member);
MPD->MemberAndIsDerivedMember.setInt(IsDerivedMember);
MPD->resizePath(Path.size());
memcpy(MPD->getPath(), Path.data(), Path.size()*sizeof(const CXXRecordDecl*));
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1074,6 +1074,7 @@ llvm::Constant *CodeGenModule::EmitConstantExpr(const Expr *E,
case APValue::Array:
case APValue::Struct:
case APValue::Union:
case APValue::MemberPointer:
break;
}
}

Просмотреть файл

@ -55,8 +55,7 @@ void test6() {
// CHECK: define void @test7
void test7() {
int i;
// CHECK-NOT: __strcpy_chk
// CHECK: = call i8* @__inline_strcpy_chk(i8* getelementptr inbounds ([63 x i8]* @gbuf, i32 0, i32 0), i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0))
// CHECK: = call i64 @llvm.objectsize.i64(i8* {{.*}}@gbuf{{.*}}, i1 false)
strcpy((++i, gbuf), "Hi there");
}

Просмотреть файл

@ -1,8 +1,7 @@
// RUN: %clang_cc1 -O1 -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s
// Check that the following construct, which is similar to one which occurs
// in Firefox, is not misfolded (folding it correctly would be a bonus, but
// that doesn't work at the moment, hence the -O1 in the runline).
// in Firefox, is folded correctly.
struct A { char x; };
struct B { char y; };
struct C : A,B {};

Просмотреть файл

@ -94,7 +94,7 @@ namespace TemplateArgumentConversion {
template<int n> struct IntParam {};
using IntParam0 = IntParam<0>;
// FIXME: This should be accepted once we do constexpr function invocation.
// FIXME: This should be accepted once we implement the new ICE rules.
using IntParam0 = IntParam<id(0)>; // expected-error {{not an integral constant expression}}
using IntParam0 = IntParam<MemberZero().zero>; // expected-error {{did you mean to call it with no arguments?}} expected-error {{not an integral constant expression}}
}
@ -104,7 +104,7 @@ namespace CaseStatements {
switch (n) {
// FIXME: Produce the 'add ()' fixit for this.
case MemberZero().zero: // desired-error {{did you mean to call it with no arguments?}} expected-error {{not an integer constant expression}}
// FIXME: This should be accepted once we do constexpr function invocation.
// FIXME: This should be accepted once we implement the new ICE rules.
case id(1): // expected-error {{not an integer constant expression}}
return;
}
@ -432,6 +432,17 @@ constexpr int CountZero(const int *p, const int *q) {
static_assert_fold(SumNonzero(arr) == 6, "");
static_assert_fold(CountZero(arr, arr + 40) == 36, "");
struct ArrayElem {
constexpr ArrayElem() : n(0) {}
int n;
constexpr int f() { return n; }
};
struct ArrayRVal {
constexpr ArrayRVal() {}
ArrayElem elems[10];
};
static_assert_fold(ArrayRVal().elems[3].f() == 0, "");
}
namespace DependentValues {
@ -572,12 +583,13 @@ static_assert_fold(d.a == 3, "");
}
struct Base {
struct Bottom { constexpr Bottom() {} };
struct Base : Bottom {
constexpr Base(int a = 42, const char *b = "test") : a(a), b(b) {}
int a;
const char *b;
};
struct Base2 {
struct Base2 : Bottom {
constexpr Base2(const int &r) : r(r) {}
int q = 123;
// FIXME: When we track the global for which we are computing the initializer,
@ -607,6 +619,63 @@ static_assert_fold(&derived.r == &derived.a, ""); // expected-error {{}}
static_assert_fold(!(derived == base), "");
static_assert_fold(derived == base2, "");
constexpr Bottom &bot1 = (Base&)derived;
constexpr Bottom &bot2 = (Base2&)derived;
static_assert_fold(&bot1 != &bot2, "");
constexpr Bottom *pb1 = (Base*)&derived;
constexpr Bottom *pb2 = (Base2*)&derived;
static_assert_fold(pb1 != pb2, "");
static_assert_fold(pb1 == &bot1, "");
static_assert_fold(pb2 == &bot2, "");
constexpr Base2 &fail = (Base2&)bot1; // expected-error {{constant expression}}
constexpr Base &fail2 = (Base&)*pb2; // expected-error {{constant expression}}
constexpr Base2 &ok2 = (Base2&)bot2;
static_assert_fold(&ok2 == &derived, "");
constexpr Base2 *pfail = (Base2*)pb1; // expected-error {{constant expression}}
constexpr Base *pfail2 = (Base*)&bot2; // expected-error {{constant expression}}
constexpr Base2 *pok2 = (Base2*)pb2;
static_assert_fold(pok2 == &derived, "");
static_assert_fold(&ok2 == pok2, "");
static_assert_fold((Base2*)(Derived*)(Base*)pb1 == pok2, "");
static_assert_fold((Derived*)(Base*)pb1 == (Derived*)pok2, "");
constexpr Base *nullB = 42 - 6 * 7;
static_assert_fold((Bottom*)nullB == 0, "");
static_assert_fold((Derived*)nullB == 0, "");
static_assert_fold((void*)(Bottom*)nullB == (void*)(Derived*)nullB, "");
}
namespace Temporaries {
struct S {
constexpr S() {}
constexpr int f();
};
struct T : S {
constexpr T(int n) : S(), n(n) {}
int n;
};
constexpr int S::f() {
// 'this' must be the postfix-expression in a class member access expression,
// so we can't just use
// return static_cast<T*>(this)->n;
return this->*(int(S::*))&T::n;
}
// The T temporary is implicitly cast to an S subobject, but we can recover the
// T full-object via a base-to-derived cast, or a derived-to-base-casted member
// pointer.
static_assert_fold(T(3).f() == 3, "");
constexpr int f(const S &s) {
return static_cast<const T&>(s).n;
}
constexpr int n = f(T(5));
static_assert_fold(f(T(5)) == 5, "");
}
namespace Union {
@ -626,6 +695,138 @@ static_assert_fold((&(u[1]) + 1 + 1)->b == 3, "");
}
namespace MemberPointer {
struct A {
constexpr A(int n) : n(n) {}
int n;
constexpr int f() { return n + 3; }
};
constexpr A a(7);
static_assert_fold(A(5).*&A::n == 5, "");
static_assert_fold((&a)->*&A::n == 7, "");
static_assert_fold((A(8).*&A::f)() == 11, "");
static_assert_fold(((&a)->*&A::f)() == 10, "");
struct B : A {
constexpr B(int n, int m) : A(n), m(m) {}
int m;
constexpr int g() { return n + m + 1; }
};
constexpr B b(9, 13);
static_assert_fold(B(4, 11).*&A::n == 4, "");
static_assert_fold(B(4, 11).*&B::m == 11, "");
static_assert_fold(B(4, 11).*(int(A::*))&B::m == 11, "");
static_assert_fold((&b)->*&A::n == 9, "");
static_assert_fold((&b)->*&B::m == 13, "");
static_assert_fold((&b)->*(int(A::*))&B::m == 13, "");
static_assert_fold((B(4, 11).*&A::f)() == 7, "");
static_assert_fold((B(4, 11).*&B::g)() == 16, "");
static_assert_fold((B(4, 11).*(int(A::*)()const)&B::g)() == 16, "");
static_assert_fold(((&b)->*&A::f)() == 12, "");
static_assert_fold(((&b)->*&B::g)() == 23, "");
static_assert_fold(((&b)->*(int(A::*)()const)&B::g)() == 23, "");
struct S {
constexpr S(int m, int n, int (S::*pf)() const, int S::*pn) :
m(m), n(n), pf(pf), pn(pn) {}
constexpr S() : m(), n(), pf(&S::f), pn(&S::n) {}
constexpr int f() { return this->*pn; }
virtual int g() const;
int m, n;
int (S::*pf)() const;
int S::*pn;
};
constexpr int S::*pm = &S::m;
constexpr int S::*pn = &S::n;
constexpr int (S::*pf)() const = &S::f;
constexpr int (S::*pg)() const = &S::g;
constexpr S s(2, 5, &S::f, &S::m);
static_assert_fold((s.*&S::f)() == 2, "");
static_assert_fold((s.*s.pf)() == 2, "");
template<int n> struct T : T<n-1> {};
template<> struct T<0> { int n; };
template<> struct T<30> : T<29> { int m; };
T<17> t17;
T<30> t30;
constexpr int (T<10>::*deepn) = &T<0>::n;
static_assert_fold(&(t17.*deepn) == &t17.n, "");
constexpr int (T<15>::*deepm) = (int(T<10>::*))&T<30>::m;
constexpr int *pbad = &(t17.*deepm); // expected-error {{constant expression}}
static_assert_fold(&(t30.*deepm) == &t30.m, "");
constexpr T<5> *p17_5 = &t17;
constexpr T<13> *p17_13 = (T<13>*)p17_5;
constexpr T<23> *p17_23 = (T<23>*)p17_13; // expected-error {{constant expression}}
static_assert_fold(&(p17_5->*(int(T<3>::*))deepn) == &t17.n, "");
static_assert_fold(&(p17_13->*deepn) == &t17.n, "");
constexpr int *pbad2 = &(p17_13->*(int(T<9>::*))deepm); // expected-error {{constant expression}}
constexpr T<5> *p30_5 = &t30;
constexpr T<23> *p30_23 = (T<23>*)p30_5;
constexpr T<13> *p30_13 = p30_23;
static_assert_fold(&(p30_5->*(int(T<3>::*))deepn) == &t30.n, "");
static_assert_fold(&(p30_13->*deepn) == &t30.n, "");
static_assert_fold(&(p30_23->*deepn) == &t30.n, "");
static_assert_fold(&(p30_5->*(int(T<2>::*))deepm) == &t30.m, "");
static_assert_fold(&(((T<17>*)p30_13)->*deepm) == &t30.m, "");
static_assert_fold(&(p30_23->*deepm) == &t30.m, "");
}
namespace ArrayBaseDerived {
struct Base {
constexpr Base() {}
int n = 0;
};
struct Derived : Base {
constexpr Derived() {}
constexpr const int *f() { return &n; }
};
constexpr Derived a[10];
constexpr Derived *pd3 = const_cast<Derived*>(&a[3]);
constexpr Base *pb3 = const_cast<Derived*>(&a[3]);
static_assert_fold(pb3 == pd3, "");
// pb3 does not point to an array element.
constexpr Base *pb4 = pb3 + 1; // ok, one-past-the-end pointer.
constexpr int pb4n = pb4->n; // expected-error {{constant expression}}
constexpr Base *err_pb5 = pb3 + 2; // FIXME: reject this.
constexpr int err_pb5n = err_pb5->n; // expected-error {{constant expression}}
constexpr Base *err_pb2 = pb3 - 1; // FIXME: reject this.
constexpr int err_pb2n = err_pb2->n; // expected-error {{constant expression}}
constexpr Base *pb3a = pb4 - 1;
// pb4 does not point to a Derived.
constexpr Derived *err_pd4 = (Derived*)pb4; // expected-error {{constant expression}}
constexpr Derived *pd3a = (Derived*)pb3a;
constexpr int pd3n = pd3a->n;
// pd3a still points to the Derived array.
constexpr Derived *pd6 = pd3a + 3;
static_assert_fold(pd6 == &a[6], "");
constexpr Derived *pd9 = pd6 + 3;
constexpr Derived *pd10 = pd6 + 4;
constexpr int pd9n = pd9->n; // ok
constexpr int err_pd10n = pd10->n; // expected-error {{constant expression}}
constexpr int pd0n = pd10[-10].n;
constexpr int err_pdminus1n = pd10[-11].n; // expected-error {{constant expression}}
constexpr Base *pb9 = pd9;
constexpr const int *(Base::*pfb)() const =
static_cast<const int *(Base::*)() const>(&Derived::f);
static_assert_fold((pb9->*pfb)() == &a[9].n, "");
}
namespace Complex {
class complex {