зеркало из https://github.com/microsoft/clang-1.git
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:
Родитель
c69c42e939
Коммит
e24f5fc8c7
|
@ -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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче