mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-27 15:41:46 +00:00
Constant expression evaluation: support for evaluation of structs and unions of
literal types, as well as derived-to-base casts for lvalues and derived-to-virtual-base casts. llvm-svn: 144265
This commit is contained in:
parent
4d14296263
commit
d62306a481
@ -17,11 +17,13 @@
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/APSInt.h"
|
||||
#include "llvm/ADT/APFloat.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
|
||||
namespace clang {
|
||||
class CharUnits;
|
||||
class DiagnosticBuilder;
|
||||
class Expr;
|
||||
class FieldDecl;
|
||||
class Decl;
|
||||
|
||||
/// APValue - This class implements a discriminated union of [uninitialized]
|
||||
@ -39,14 +41,21 @@ public:
|
||||
ComplexFloat,
|
||||
LValue,
|
||||
Vector,
|
||||
Array
|
||||
Array,
|
||||
Struct,
|
||||
Union
|
||||
};
|
||||
typedef llvm::PointerIntPair<const Decl *, 1, bool> BaseOrMemberType;
|
||||
union LValuePathEntry {
|
||||
const Decl *BaseOrMember;
|
||||
/// BaseOrMember - The FieldDecl or CXXRecordDecl indicating the next item
|
||||
/// in the path. An opaque value of type BaseOrMemberType.
|
||||
void *BaseOrMember;
|
||||
/// ArrayIndex - The array index of the next item in the path.
|
||||
uint64_t ArrayIndex;
|
||||
};
|
||||
struct NoLValuePath {};
|
||||
struct UninitArray {};
|
||||
struct UninitStruct {};
|
||||
private:
|
||||
ValueKind Kind;
|
||||
|
||||
@ -71,6 +80,19 @@ private:
|
||||
Arr(unsigned NumElts, unsigned ArrSize);
|
||||
~Arr();
|
||||
};
|
||||
struct StructData {
|
||||
APValue *Elts;
|
||||
unsigned NumBases;
|
||||
unsigned NumFields;
|
||||
StructData(unsigned NumBases, unsigned NumFields);
|
||||
~StructData();
|
||||
};
|
||||
struct UnionData {
|
||||
const FieldDecl *Field;
|
||||
APValue *Value;
|
||||
UnionData();
|
||||
~UnionData();
|
||||
};
|
||||
|
||||
enum {
|
||||
MaxSize = (sizeof(ComplexAPSInt) > sizeof(ComplexAPFloat) ?
|
||||
@ -114,6 +136,13 @@ public:
|
||||
APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(Uninitialized) {
|
||||
MakeArray(InitElts, Size);
|
||||
}
|
||||
APValue(UninitStruct, unsigned B, unsigned M) : Kind(Uninitialized) {
|
||||
MakeStruct(B, M);
|
||||
}
|
||||
explicit APValue(const FieldDecl *D, const APValue &V = APValue())
|
||||
: Kind(Uninitialized) {
|
||||
MakeUnion(); setUnion(D, V);
|
||||
}
|
||||
|
||||
~APValue() {
|
||||
MakeUninit();
|
||||
@ -128,6 +157,8 @@ public:
|
||||
bool isLValue() const { return Kind == LValue; }
|
||||
bool isVector() const { return Kind == Vector; }
|
||||
bool isArray() const { return Kind == Array; }
|
||||
bool isStruct() const { return Kind == Struct; }
|
||||
bool isUnion() const { return Kind == Union; }
|
||||
|
||||
void print(raw_ostream &OS) const;
|
||||
void dump() const;
|
||||
@ -229,6 +260,41 @@ public:
|
||||
return ((const Arr*)(const void *)Data)->ArrSize;
|
||||
}
|
||||
|
||||
unsigned getStructNumBases() const {
|
||||
assert(isStruct() && "Invalid accessor");
|
||||
return ((StructData*)(char*)Data)->NumBases;
|
||||
}
|
||||
unsigned getStructNumFields() const {
|
||||
assert(isStruct() && "Invalid accessor");
|
||||
return ((StructData*)(char*)Data)->NumFields;
|
||||
}
|
||||
APValue &getStructBase(unsigned i) {
|
||||
assert(isStruct() && "Invalid accessor");
|
||||
return ((StructData*)(char*)Data)->Elts[i];
|
||||
}
|
||||
APValue &getStructField(unsigned i) {
|
||||
assert(isStruct() && "Invalid accessor");
|
||||
return ((StructData*)(char*)Data)->Elts[getStructNumBases() + i];
|
||||
}
|
||||
const APValue &getStructBase(unsigned i) const {
|
||||
return const_cast<APValue*>(this)->getStructBase(i);
|
||||
}
|
||||
const APValue &getStructField(unsigned i) const {
|
||||
return const_cast<APValue*>(this)->getStructField(i);
|
||||
}
|
||||
|
||||
const FieldDecl *getUnionField() const {
|
||||
assert(isUnion() && "Invalid accessor");
|
||||
return ((UnionData*)(char*)Data)->Field;
|
||||
}
|
||||
APValue &getUnionValue() {
|
||||
assert(isUnion() && "Invalid accessor");
|
||||
return *((UnionData*)(char*)Data)->Value;
|
||||
}
|
||||
const APValue &getUnionValue() const {
|
||||
return const_cast<APValue*>(this)->getUnionValue();
|
||||
}
|
||||
|
||||
void setInt(const APSInt &I) {
|
||||
assert(isInt() && "Invalid accessor");
|
||||
*(APSInt*)(char*)Data = I;
|
||||
@ -261,6 +327,11 @@ public:
|
||||
void setLValue(const Expr *B, const CharUnits &O, NoLValuePath);
|
||||
void setLValue(const Expr *B, const CharUnits &O,
|
||||
ArrayRef<LValuePathEntry> Path);
|
||||
void setUnion(const FieldDecl *Field, const APValue &Value) {
|
||||
assert(isUnion() && "Invalid accessor");
|
||||
((UnionData*)(char*)Data)->Field = Field;
|
||||
*((UnionData*)(char*)Data)->Value = Value;
|
||||
}
|
||||
|
||||
const APValue &operator=(const APValue &RHS);
|
||||
|
||||
@ -293,6 +364,16 @@ private:
|
||||
}
|
||||
void MakeLValue();
|
||||
void MakeArray(unsigned InitElts, unsigned Size);
|
||||
void MakeStruct(unsigned B, unsigned M) {
|
||||
assert(isUninit() && "Bad state change");
|
||||
new ((void*)(char*)Data) StructData(B, M);
|
||||
Kind = Struct;
|
||||
}
|
||||
void MakeUnion() {
|
||||
assert(isUninit() && "Bad state change");
|
||||
new ((void*)(char*)Data) UnionData();
|
||||
Kind = Union;
|
||||
}
|
||||
};
|
||||
|
||||
inline raw_ostream &operator<<(raw_ostream &OS, const APValue &V) {
|
||||
|
@ -2049,7 +2049,7 @@ public:
|
||||
|
||||
/// isBuiltinCall - If this is a call to a builtin, return the builtin ID. If
|
||||
/// not, return 0.
|
||||
unsigned isBuiltinCall(const ASTContext &Context) const;
|
||||
unsigned isBuiltinCall() const;
|
||||
|
||||
/// getCallReturnType - Get the return type of the call expr. This is not
|
||||
/// always the type of the expr itself, if the return type is a reference
|
||||
|
@ -45,6 +45,7 @@ struct APValue::LV : LVBase {
|
||||
void allocPath() {
|
||||
if (hasPathPtr()) PathPtr = new LValuePathEntry[PathLength];
|
||||
}
|
||||
void freePath() { if (hasPathPtr()) delete [] PathPtr; }
|
||||
|
||||
bool hasPath() const { return PathLength != (unsigned)-1; }
|
||||
bool hasPathPtr() const { return hasPath() && PathLength > InlinePathSpace; }
|
||||
@ -62,13 +63,27 @@ APValue::Arr::Arr(unsigned NumElts, unsigned Size) :
|
||||
NumElts(NumElts), ArrSize(Size) {}
|
||||
APValue::Arr::~Arr() { delete [] Elts; }
|
||||
|
||||
APValue::StructData::StructData(unsigned NumBases, unsigned NumFields) :
|
||||
Elts(new APValue[NumBases+NumFields]),
|
||||
NumBases(NumBases), NumFields(NumFields) {}
|
||||
APValue::StructData::~StructData() {
|
||||
delete [] Elts;
|
||||
}
|
||||
|
||||
APValue::UnionData::UnionData() : Field(0), Value(new APValue) {}
|
||||
APValue::UnionData::~UnionData () {
|
||||
delete Value;
|
||||
}
|
||||
|
||||
APValue::APValue(const Expr* B) : Kind(Uninitialized) {
|
||||
MakeLValue();
|
||||
setLValue(B, CharUnits::Zero(), ArrayRef<LValuePathEntry>());
|
||||
}
|
||||
|
||||
const APValue &APValue::operator=(const APValue &RHS) {
|
||||
if (Kind != RHS.Kind) {
|
||||
if (this == &RHS)
|
||||
return *this;
|
||||
if (Kind != RHS.Kind || Kind == Array || Kind == Struct) {
|
||||
MakeUninit();
|
||||
if (RHS.isInt())
|
||||
MakeInt();
|
||||
@ -84,6 +99,10 @@ const APValue &APValue::operator=(const APValue &RHS) {
|
||||
MakeLValue();
|
||||
else if (RHS.isArray())
|
||||
MakeArray(RHS.getArrayInitializedElts(), RHS.getArraySize());
|
||||
else if (RHS.isStruct())
|
||||
MakeStruct(RHS.getStructNumBases(), RHS.getStructNumFields());
|
||||
else if (RHS.isUnion())
|
||||
MakeUnion();
|
||||
}
|
||||
if (isInt())
|
||||
setInt(RHS.getInt());
|
||||
@ -106,7 +125,13 @@ const APValue &APValue::operator=(const APValue &RHS) {
|
||||
getArrayInitializedElt(I) = RHS.getArrayInitializedElt(I);
|
||||
if (RHS.hasArrayFiller())
|
||||
getArrayFiller() = RHS.getArrayFiller();
|
||||
}
|
||||
} else if (isStruct()) {
|
||||
for (unsigned I = 0, N = RHS.getStructNumBases(); I != N; ++I)
|
||||
getStructBase(I) = RHS.getStructBase(I);
|
||||
for (unsigned I = 0, N = RHS.getStructNumFields(); I != N; ++I)
|
||||
getStructField(I) = RHS.getStructField(I);
|
||||
} else if (isUnion())
|
||||
setUnion(RHS.getUnionField(), RHS.getUnionValue());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -125,6 +150,10 @@ void APValue::MakeUninit() {
|
||||
((LV*)(char*)Data)->~LV();
|
||||
else if (Kind == Array)
|
||||
((Arr*)(char*)Data)->~Arr();
|
||||
else if (Kind == Struct)
|
||||
((StructData*)(char*)Data)->~StructData();
|
||||
else if (Kind == Union)
|
||||
((UnionData*)(char*)Data)->~UnionData();
|
||||
Kind = Uninitialized;
|
||||
}
|
||||
|
||||
@ -143,7 +172,6 @@ static double GetApproxValue(const llvm::APFloat &F) {
|
||||
|
||||
void APValue::print(raw_ostream &OS) const {
|
||||
switch (getKind()) {
|
||||
default: llvm_unreachable("Unknown APValue kind!");
|
||||
case Uninitialized:
|
||||
OS << "Uninitialized";
|
||||
return;
|
||||
@ -178,22 +206,38 @@ void APValue::print(raw_ostream &OS) const {
|
||||
OS << getArraySize() - getArrayInitializedElts() << " x "
|
||||
<< getArrayFiller();
|
||||
return;
|
||||
case Struct:
|
||||
OS << "Struct ";
|
||||
if (unsigned N = getStructNumBases()) {
|
||||
OS << " bases: " << getStructBase(0);
|
||||
for (unsigned I = 1; I != N; ++I)
|
||||
OS << ", " << getStructBase(I);
|
||||
}
|
||||
if (unsigned N = getStructNumFields()) {
|
||||
OS << " fields: " << getStructField(0);
|
||||
for (unsigned I = 1; I != N; ++I)
|
||||
OS << ", " << getStructField(I);
|
||||
}
|
||||
return;
|
||||
case Union:
|
||||
OS << "Union: " << getUnionValue();
|
||||
return;
|
||||
}
|
||||
llvm_unreachable("Unknown APValue kind!");
|
||||
}
|
||||
|
||||
static void WriteShortAPValueToStream(raw_ostream& Out,
|
||||
const APValue& V) {
|
||||
switch (V.getKind()) {
|
||||
default: llvm_unreachable("Unknown APValue kind!");
|
||||
case APValue::Uninitialized:
|
||||
Out << "Uninitialized";
|
||||
break;
|
||||
return;
|
||||
case APValue::Int:
|
||||
Out << V.getInt();
|
||||
break;
|
||||
return;
|
||||
case APValue::Float:
|
||||
Out << GetApproxValue(V.getFloat());
|
||||
break;
|
||||
return;
|
||||
case APValue::Vector:
|
||||
Out << '[';
|
||||
WriteShortAPValueToStream(Out, V.getVectorElt(0));
|
||||
@ -202,17 +246,17 @@ static void WriteShortAPValueToStream(raw_ostream& Out,
|
||||
WriteShortAPValueToStream(Out, V.getVectorElt(i));
|
||||
}
|
||||
Out << ']';
|
||||
break;
|
||||
return;
|
||||
case APValue::ComplexInt:
|
||||
Out << V.getComplexIntReal() << "+" << V.getComplexIntImag() << "i";
|
||||
break;
|
||||
return;
|
||||
case APValue::ComplexFloat:
|
||||
Out << GetApproxValue(V.getComplexFloatReal()) << "+"
|
||||
<< GetApproxValue(V.getComplexFloatImag()) << "i";
|
||||
break;
|
||||
return;
|
||||
case APValue::LValue:
|
||||
Out << "LValue: <todo>";
|
||||
break;
|
||||
return;
|
||||
case APValue::Array:
|
||||
Out << '{';
|
||||
if (unsigned N = V.getArrayInitializedElts()) {
|
||||
@ -221,8 +265,28 @@ static void WriteShortAPValueToStream(raw_ostream& Out,
|
||||
Out << ", " << V.getArrayInitializedElt(I);
|
||||
}
|
||||
Out << '}';
|
||||
break;
|
||||
return;
|
||||
case APValue::Struct:
|
||||
Out << '{';
|
||||
if (unsigned N = V.getStructNumBases()) {
|
||||
Out << V.getStructBase(0);
|
||||
for (unsigned I = 1; I != N; ++I)
|
||||
Out << ", " << V.getStructBase(I);
|
||||
if (V.getStructNumFields())
|
||||
Out << ", ";
|
||||
}
|
||||
if (unsigned N = V.getStructNumFields()) {
|
||||
Out << V.getStructField(0);
|
||||
for (unsigned I = 1; I != N; ++I)
|
||||
Out << ", " << V.getStructField(I);
|
||||
}
|
||||
Out << '}';
|
||||
return;
|
||||
case APValue::Union:
|
||||
Out << '{' << V.getUnionValue() << '}';
|
||||
return;
|
||||
}
|
||||
llvm_unreachable("Unknown APValue kind!");
|
||||
}
|
||||
|
||||
const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB,
|
||||
@ -257,6 +321,7 @@ ArrayRef<APValue::LValuePathEntry> APValue::getLValuePath() const {
|
||||
void APValue::setLValue(const Expr *B, const CharUnits &O, NoLValuePath) {
|
||||
assert(isLValue() && "Invalid accessor");
|
||||
LV &LVal = *((LV*)(char*)Data);
|
||||
LVal.freePath();
|
||||
LVal.Base = B;
|
||||
LVal.Offset = O;
|
||||
LVal.PathLength = (unsigned)-1;
|
||||
@ -266,6 +331,7 @@ void APValue::setLValue(const Expr *B, const CharUnits &O,
|
||||
ArrayRef<LValuePathEntry> Path) {
|
||||
assert(isLValue() && "Invalid accessor");
|
||||
LV &LVal = *((LV*)(char*)Data);
|
||||
LVal.freePath();
|
||||
LVal.Base = B;
|
||||
LVal.Offset = O;
|
||||
LVal.PathLength = Path.size();
|
||||
|
@ -2186,31 +2186,27 @@ unsigned FieldDecl::getBitWidthValue(const ASTContext &Ctx) const {
|
||||
unsigned FieldDecl::getFieldIndex() const {
|
||||
if (CachedFieldIndex) return CachedFieldIndex - 1;
|
||||
|
||||
unsigned index = 0;
|
||||
unsigned Index = 0;
|
||||
const RecordDecl *RD = getParent();
|
||||
const FieldDecl *LastFD = 0;
|
||||
bool IsMsStruct = RD->hasAttr<MsStructAttr>();
|
||||
|
||||
RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end();
|
||||
while (true) {
|
||||
assert(i != e && "failed to find field in parent!");
|
||||
if (*i == this)
|
||||
break;
|
||||
|
||||
for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
|
||||
I != E; ++I, ++Index) {
|
||||
(*I)->CachedFieldIndex = Index + 1;
|
||||
|
||||
if (IsMsStruct) {
|
||||
// Zero-length bitfields following non-bitfield members are ignored.
|
||||
if (getASTContext().ZeroBitfieldFollowsNonBitfield((*i), LastFD)) {
|
||||
++i;
|
||||
if (getASTContext().ZeroBitfieldFollowsNonBitfield((*I), LastFD)) {
|
||||
--Index;
|
||||
continue;
|
||||
}
|
||||
LastFD = (*i);
|
||||
LastFD = (*I);
|
||||
}
|
||||
++i;
|
||||
++index;
|
||||
}
|
||||
|
||||
CachedFieldIndex = index + 1;
|
||||
return index;
|
||||
assert(CachedFieldIndex && "failed to find field in parent");
|
||||
return CachedFieldIndex - 1;
|
||||
}
|
||||
|
||||
SourceRange FieldDecl::getSourceRange() const {
|
||||
|
@ -834,7 +834,7 @@ void CallExpr::setNumArgs(ASTContext& C, unsigned NumArgs) {
|
||||
|
||||
/// isBuiltinCall - If this is a call to a builtin, return the builtin ID. If
|
||||
/// not, return 0.
|
||||
unsigned CallExpr::isBuiltinCall(const ASTContext &Context) const {
|
||||
unsigned CallExpr::isBuiltinCall() const {
|
||||
// All simple function calls (e.g. func()) are implicitly cast to pointer to
|
||||
// function. As a result, we try and obtain the DeclRefExpr from the
|
||||
// ImplicitCastExpr.
|
||||
@ -2475,15 +2475,20 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const {
|
||||
const CXXConstructExpr *CE = cast<CXXConstructExpr>(this);
|
||||
|
||||
// Only if it's
|
||||
// 1) an application of the trivial default constructor or
|
||||
if (!CE->getConstructor()->isTrivial()) return false;
|
||||
if (!CE->getNumArgs()) return true;
|
||||
if (CE->getConstructor()->isTrivial()) {
|
||||
// 1) an application of the trivial default constructor or
|
||||
if (!CE->getNumArgs()) return true;
|
||||
|
||||
// 2) an elidable trivial copy construction of an operand which is
|
||||
// itself a constant initializer. Note that we consider the
|
||||
// operand on its own, *not* as a reference binding.
|
||||
return CE->isElidable() &&
|
||||
CE->getArg(0)->isConstantInitializer(Ctx, false);
|
||||
// 2) an elidable trivial copy construction of an operand which is
|
||||
// itself a constant initializer. Note that we consider the
|
||||
// operand on its own, *not* as a reference binding.
|
||||
if (CE->isElidable() &&
|
||||
CE->getArg(0)->isConstantInitializer(Ctx, false))
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3) a foldable constexpr constructor.
|
||||
break;
|
||||
}
|
||||
case CompoundLiteralExprClass: {
|
||||
// This handles gcc's extension that allows global initializers like
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -908,7 +908,7 @@ public:
|
||||
}
|
||||
case Expr::CallExprClass: {
|
||||
CallExpr* CE = cast<CallExpr>(E);
|
||||
unsigned builtin = CE->isBuiltinCall(CGM.getContext());
|
||||
unsigned builtin = CE->isBuiltinCall();
|
||||
if (builtin !=
|
||||
Builtin::BI__builtin___CFStringMakeConstantString &&
|
||||
builtin !=
|
||||
@ -1071,7 +1071,8 @@ llvm::Constant *CodeGenModule::EmitConstantExpr(const Expr *E,
|
||||
return llvm::ConstantVector::get(Inits);
|
||||
}
|
||||
case APValue::Array:
|
||||
assert(0 && "shouldn't see array constants here yet");
|
||||
case APValue::Struct:
|
||||
case APValue::Union:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2439,7 +2439,7 @@ void Sema::CheckStrlcpycatArguments(const CallExpr *Call,
|
||||
else {
|
||||
// Look for 'strlcpy(dst, x, strlen(x))'
|
||||
if (const CallExpr *SizeCall = dyn_cast<CallExpr>(SizeArg)) {
|
||||
if (SizeCall->isBuiltinCall(Context) == Builtin::BIstrlen
|
||||
if (SizeCall->isBuiltinCall() == Builtin::BIstrlen
|
||||
&& SizeCall->getNumArgs() == 1)
|
||||
CompareWithSrc = ignoreLiteralAdditions(SizeCall->getArg(0), Context);
|
||||
}
|
||||
@ -2887,12 +2887,12 @@ void Sema::CheckFloatComparison(SourceLocation Loc, Expr* LHS, Expr *RHS) {
|
||||
// Check for comparisons with builtin types.
|
||||
if (EmitWarning)
|
||||
if (CallExpr* CL = dyn_cast<CallExpr>(LeftExprSansParen))
|
||||
if (CL->isBuiltinCall(Context))
|
||||
if (CL->isBuiltinCall())
|
||||
EmitWarning = false;
|
||||
|
||||
if (EmitWarning)
|
||||
if (CallExpr* CR = dyn_cast<CallExpr>(RightExprSansParen))
|
||||
if (CR->isBuiltinCall(Context))
|
||||
if (CR->isBuiltinCall())
|
||||
EmitWarning = false;
|
||||
|
||||
// Emit the diagnostic.
|
||||
|
@ -83,8 +83,6 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
|
||||
if (!C || !PM)
|
||||
return;
|
||||
|
||||
ASTContext &Ctx = B.getContext();
|
||||
|
||||
// Find CFGBlocks that were not covered by any node
|
||||
for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) {
|
||||
const CFGBlock *CB = *I;
|
||||
@ -117,7 +115,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
|
||||
ci != ce; ++ci) {
|
||||
if (const CFGStmt *S = (*ci).getAs<CFGStmt>())
|
||||
if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) {
|
||||
if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) {
|
||||
if (CE->isBuiltinCall() == Builtin::BI__builtin_unreachable) {
|
||||
foundUnreachable = true;
|
||||
break;
|
||||
}
|
||||
|
@ -117,8 +117,7 @@ constexpr int square(int x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
// FIXME: The initializer is a constant expression.
|
||||
constexpr pixel large(4); // unexpected-error {{must be initialized by a constant expression}}
|
||||
constexpr pixel large(4);
|
||||
|
||||
int next(constexpr int x) { // expected-error {{function parameter cannot be constexpr}}
|
||||
return x + 1;
|
||||
|
@ -33,6 +33,50 @@ struct MemberZero {
|
||||
constexpr int zero() { return 0; }
|
||||
};
|
||||
|
||||
namespace DerivedToVBaseCast {
|
||||
|
||||
struct U { int n; };
|
||||
struct V : U { int n; };
|
||||
struct A : virtual V { int n; };
|
||||
struct Aa { int n; };
|
||||
struct B : virtual A, Aa {};
|
||||
struct C : virtual A, Aa {};
|
||||
struct D : B, C {};
|
||||
|
||||
D d;
|
||||
constexpr B *p = &d;
|
||||
constexpr C *q = &d;
|
||||
static_assert_fold((void*)p != (void*)q, "");
|
||||
static_assert_fold((A*)p == (A*)q, "");
|
||||
static_assert_fold((Aa*)p != (Aa*)q, "");
|
||||
|
||||
constexpr B &pp = d;
|
||||
constexpr C &qq = d;
|
||||
static_assert_fold((void*)&pp != (void*)&qq, "");
|
||||
static_assert_fold(&(A&)pp == &(A&)qq, "");
|
||||
static_assert_fold(&(Aa&)pp != &(Aa&)qq, "");
|
||||
|
||||
constexpr V *v = p;
|
||||
constexpr V *w = q;
|
||||
constexpr V *x = (A*)p;
|
||||
static_assert_fold(v == w, "");
|
||||
static_assert_fold(v == x, "");
|
||||
|
||||
static_assert_fold((U*)&d == p, "");
|
||||
static_assert_fold((U*)&d == q, "");
|
||||
static_assert_fold((U*)&d == v, "");
|
||||
static_assert_fold((U*)&d == w, "");
|
||||
static_assert_fold((U*)&d == x, "");
|
||||
|
||||
struct X {};
|
||||
struct Y1 : virtual X {};
|
||||
struct Y2 : X {};
|
||||
struct Z : Y1, Y2 {};
|
||||
Z z;
|
||||
static_assert_fold((X*)(Y1*)&z != (X*)(Y2*)&z, "");
|
||||
|
||||
}
|
||||
|
||||
namespace TemplateArgumentConversion {
|
||||
template<int n> struct IntParam {};
|
||||
|
||||
@ -268,6 +312,10 @@ static_assert_fold(sameTemporary(9), "");
|
||||
|
||||
}
|
||||
|
||||
constexpr int strcmp_ce(const char *p, const char *q) {
|
||||
return (!*p || *p != *q) ? *p - *q : strcmp_ce(p+1, q+1);
|
||||
}
|
||||
|
||||
namespace StringLiteral {
|
||||
|
||||
// FIXME: Refactor this once we support constexpr templates.
|
||||
@ -309,6 +357,11 @@ constexpr const char *max = max_element(begin(str), end(str));
|
||||
static_assert_fold(*max == 'z', "");
|
||||
static_assert_fold(max == str + 38, "");
|
||||
|
||||
static_assert_fold(strcmp_ce("hello world", "hello world") == 0, "");
|
||||
static_assert_fold(strcmp_ce("hello world", "hello clang") > 0, "");
|
||||
static_assert_fold(strcmp_ce("constexpr", "test") < 0, "");
|
||||
static_assert_fold(strcmp_ce("", " ") < 0, "");
|
||||
|
||||
}
|
||||
|
||||
namespace Array {
|
||||
@ -350,6 +403,17 @@ static_assert_fold(zs[1][1][1][1] == 16, "");
|
||||
static_assert_fold(zs[0][0][0][2] == 3, ""); // expected-error {{constant expression}}
|
||||
static_assert_fold((&zs[0][0][0][2])[-1] == 2, "");
|
||||
static_assert_fold(**(**(zs + 1) + 1) == 11, "");
|
||||
static_assert_fold(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][-1] + 1) == 11, "");
|
||||
|
||||
constexpr int arr[40] = { 1, 2, 3, [8] = 4 };
|
||||
constexpr int SumNonzero(const int *p) {
|
||||
return *p + (*p ? SumNonzero(p+1) : 0);
|
||||
}
|
||||
constexpr int CountZero(const int *p, const int *q) {
|
||||
return p == q ? 0 : (*p == 0) + CountZero(p+1, q);
|
||||
}
|
||||
static_assert_fold(SumNonzero(arr) == 6, "");
|
||||
static_assert_fold(CountZero(arr, arr + 40) == 36, "");
|
||||
|
||||
}
|
||||
|
||||
@ -367,3 +431,180 @@ template<bool B> struct S {
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace Class {
|
||||
|
||||
struct A { constexpr A(int a, int b) : k(a + b) {} int k; };
|
||||
constexpr int fn(const A &a) { return a.k; }
|
||||
static_assert_fold(fn(A(4,5)) == 9, "");
|
||||
|
||||
struct B { int n; int m; } constexpr b = { 0, b.n }; // expected-warning {{uninitialized}}
|
||||
struct C {
|
||||
constexpr C(C *this_) : m(42), n(this_->m) {} // ok
|
||||
int m, n;
|
||||
};
|
||||
struct D {
|
||||
C c;
|
||||
constexpr D() : c(&c) {}
|
||||
};
|
||||
static_assert_fold(D().c.n == 42, "");
|
||||
|
||||
struct E {
|
||||
constexpr E() : p(&p) {}
|
||||
void *p;
|
||||
};
|
||||
constexpr const E &e1 = E(); // expected-error {{constant expression}}
|
||||
// This is a constant expression if we elide the copy constructor call, and
|
||||
// is not a constant expression if we don't! But we do, so it is.
|
||||
// FIXME: The move constructor is not currently implicitly defined as constexpr.
|
||||
// We notice this when evaluating an expression which uses it, but not when
|
||||
// checking its initializer.
|
||||
constexpr E e2 = E(); // unexpected-error {{constant expression}}
|
||||
static_assert_fold(e2.p == &e2.p, ""); // unexpected-error {{constant expression}}
|
||||
// FIXME: We don't pass through the fact that 'this' is ::e3 when checking the
|
||||
// initializer of this declaration.
|
||||
constexpr E e3; // unexpected-error {{constant expression}}
|
||||
static_assert_fold(e3.p == &e3.p, "");
|
||||
|
||||
extern const class F f;
|
||||
struct F {
|
||||
constexpr F() : p(&f.p) {}
|
||||
const void *p;
|
||||
};
|
||||
constexpr F f = F();
|
||||
|
||||
struct G {
|
||||
struct T {
|
||||
constexpr T(T *p) : u1(), u2(p) {}
|
||||
union U1 {
|
||||
constexpr U1() {}
|
||||
int a, b = 42;
|
||||
} u1;
|
||||
union U2 {
|
||||
constexpr U2(T *p) : c(p->u1.b) {}
|
||||
int c, d;
|
||||
} u2;
|
||||
} t;
|
||||
constexpr G() : t(&t) {}
|
||||
} constexpr g;
|
||||
|
||||
static_assert_fold(g.t.u1.a == 42, ""); // expected-error {{constant expression}}
|
||||
static_assert_fold(g.t.u1.b == 42, "");
|
||||
static_assert_fold(g.t.u2.c == 42, "");
|
||||
static_assert_fold(g.t.u2.d == 42, ""); // expected-error {{constant expression}}
|
||||
|
||||
struct S {
|
||||
int a, b;
|
||||
const S *p;
|
||||
double d;
|
||||
const char *q;
|
||||
|
||||
constexpr S(int n, const S *p) : a(5), b(n), p(p), d(n), q("hello") {}
|
||||
};
|
||||
|
||||
S global(43, &global);
|
||||
|
||||
static_assert_fold(S(15, &global).b == 15, "");
|
||||
|
||||
constexpr bool CheckS(const S &s) {
|
||||
return s.a == 5 && s.b == 27 && s.p == &global && s.d == 27. && s.q[3] == 'l';
|
||||
}
|
||||
static_assert_fold(CheckS(S(27, &global)), "");
|
||||
|
||||
struct Arr {
|
||||
char arr[3];
|
||||
constexpr Arr() : arr{'x', 'y', 'z'} {}
|
||||
};
|
||||
constexpr int hash(Arr &&a) {
|
||||
return a.arr[0] + a.arr[1] * 0x100 + a.arr[2] * 0x10000;
|
||||
}
|
||||
constexpr int k = hash(Arr());
|
||||
static_assert_fold(k == 0x007a7978, "");
|
||||
|
||||
|
||||
struct AggregateInit {
|
||||
const char &c;
|
||||
int n;
|
||||
double d;
|
||||
int arr[5];
|
||||
void *p;
|
||||
};
|
||||
|
||||
constexpr AggregateInit agg1 = { "hello"[0] };
|
||||
|
||||
static_assert_fold(strcmp_ce(&agg1.c, "hello") == 0, "");
|
||||
static_assert_fold(agg1.n == 0, "");
|
||||
static_assert_fold(agg1.d == 0.0, "");
|
||||
static_assert_fold(agg1.arr[-1] == 0, ""); // expected-error {{constant expression}}
|
||||
static_assert_fold(agg1.arr[0] == 0, "");
|
||||
static_assert_fold(agg1.arr[4] == 0, "");
|
||||
static_assert_fold(agg1.arr[5] == 0, ""); // expected-error {{constant expression}}
|
||||
static_assert_fold(agg1.p == nullptr, "");
|
||||
|
||||
namespace SimpleDerivedClass {
|
||||
|
||||
struct B {
|
||||
constexpr B(int n) : a(n) {}
|
||||
int a;
|
||||
};
|
||||
struct D : B {
|
||||
constexpr D(int n) : B(n) {}
|
||||
};
|
||||
constexpr D d(3);
|
||||
static_assert_fold(d.a == 3, "");
|
||||
|
||||
}
|
||||
|
||||
struct Base {
|
||||
constexpr Base(int a = 42, const char *b = "test") : a(a), b(b) {}
|
||||
int a;
|
||||
const char *b;
|
||||
};
|
||||
struct Base2 {
|
||||
constexpr Base2(const int &r) : r(r) {}
|
||||
int q = 123;
|
||||
// FIXME: When we track the global for which we are computing the initializer,
|
||||
// use a reference here.
|
||||
//const int &r;
|
||||
int r;
|
||||
};
|
||||
struct Derived : Base, Base2 {
|
||||
constexpr Derived() : Base(76), Base2(a) {}
|
||||
int c = r + b[1];
|
||||
};
|
||||
|
||||
constexpr bool operator==(const Base &a, const Base &b) {
|
||||
return a.a == b.a && strcmp_ce(a.b, b.b) == 0;
|
||||
}
|
||||
|
||||
constexpr Base base;
|
||||
constexpr Base base2(76);
|
||||
constexpr Derived derived;
|
||||
static_assert_fold(derived.a == 76, "");
|
||||
static_assert_fold(derived.b[2] == 's', "");
|
||||
static_assert_fold(derived.c == 76 + 'e', "");
|
||||
static_assert_fold(derived.q == 123, "");
|
||||
static_assert_fold(derived.r == 76, "");
|
||||
static_assert_fold(&derived.r == &derived.a, ""); // expected-error {{}}
|
||||
|
||||
static_assert_fold(!(derived == base), "");
|
||||
static_assert_fold(derived == base2, "");
|
||||
|
||||
}
|
||||
|
||||
namespace Union {
|
||||
|
||||
union U {
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
|
||||
constexpr U u[4] = { { .a = 0 }, { .b = 1 }, { .a = 2 }, { .b = 3 } };
|
||||
static_assert_fold(u[0].a == 0, "");
|
||||
static_assert_fold(u[0].b, ""); // expected-error {{constant expression}}
|
||||
static_assert_fold(u[1].b == 1, "");
|
||||
static_assert_fold((&u[1].b)[1] == 2, ""); // expected-error {{constant expression}}
|
||||
static_assert_fold(*(&(u[1].b) + 1 + 1) == 3, ""); // expected-error {{constant expression}}
|
||||
static_assert_fold((&(u[1]) + 1 + 1)->b == 3, "");
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user