aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2011-11-10 06:34:14 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2011-11-10 06:34:14 +0000
commit180f47959a066795cc0f409433023af448bb0328 (patch)
treee5a8c5e3c47c68d4ee460e09489ebac5cf60b8a4
parent64b4b43a23aa8b8009470e3cc451333f623d7d58 (diff)
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144265 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/AST/APValue.h85
-rw-r--r--include/clang/AST/Expr.h2
-rw-r--r--lib/AST/APValue.cpp90
-rw-r--r--lib/AST/Decl.cpp24
-rw-r--r--lib/AST/Expr.cpp25
-rw-r--r--lib/AST/ExprConstant.cpp762
-rw-r--r--lib/CodeGen/CGExprConstant.cpp5
-rw-r--r--lib/Sema/SemaChecking.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp4
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp3
-rw-r--r--test/SemaCXX/constant-expression-cxx11.cpp241
11 files changed, 1056 insertions, 191 deletions
diff --git a/include/clang/AST/APValue.h b/include/clang/AST/APValue.h
index 57c89d62be..177d7baf4a 100644
--- a/include/clang/AST/APValue.h
+++ b/include/clang/AST/APValue.h
@@ -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) {
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h
index f0a4802db1..33447f7716 100644
--- a/include/clang/AST/Expr.h
+++ b/include/clang/AST/Expr.h
@@ -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
diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp
index af34642bea..e64bf7cbce 100644
--- a/lib/AST/APValue.cpp
+++ b/lib/AST/APValue.cpp
@@ -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();
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 095491aafe..299ddd4b72 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -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 {
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 92b05344ec..c2880a0ad1 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -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;
-
- // 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);
+ 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.
+ 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
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index f2e3d36d75..25c7a90caa 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -43,9 +43,32 @@ using llvm::APFloat;
/// evaluate the expression regardless of what the RHS is, but C only allows
/// certain things in certain situations.
namespace {
+ struct LValue;
struct CallStackFrame;
struct EvalInfo;
+ /// Get an LValue path entry, which is known to not be an array index, as a
+ /// field declaration.
+ const FieldDecl *getAsField(APValue::LValuePathEntry E) {
+ APValue::BaseOrMemberType Value;
+ Value.setFromOpaqueValue(E.BaseOrMember);
+ return dyn_cast<FieldDecl>(Value.getPointer());
+ }
+ /// Get an LValue path entry, which is known to not be an array index, as a
+ /// base class declaration.
+ const CXXRecordDecl *getAsBaseClass(APValue::LValuePathEntry E) {
+ APValue::BaseOrMemberType Value;
+ Value.setFromOpaqueValue(E.BaseOrMember);
+ return dyn_cast<CXXRecordDecl>(Value.getPointer());
+ }
+ /// Determine whether this LValue path entry for a base class names a virtual
+ /// base class.
+ bool isVirtualBaseClass(APValue::LValuePathEntry E) {
+ APValue::BaseOrMemberType Value;
+ Value.setFromOpaqueValue(E.BaseOrMember);
+ return Value.getInt();
+ }
+
/// Determine whether the described subobject is an array element.
static bool SubobjectIsArrayElement(QualType Base,
ArrayRef<APValue::LValuePathEntry> Path) {
@@ -55,7 +78,7 @@ namespace {
IsArrayElement = T && T->isArrayType();
if (IsArrayElement)
T = T->getBaseElementTypeUnsafe();
- else if (const FieldDecl *FD = dyn_cast<FieldDecl>(Path[I].BaseOrMember))
+ else if (const FieldDecl *FD = getAsField(Path[I]))
T = FD->getType().getTypePtr();
else
// Path[I] describes a base class.
@@ -117,14 +140,15 @@ namespace {
}
/// Update this designator to refer to the given base or member of this
/// object.
- void addDecl(const Decl *D) {
+ void addDecl(const Decl *D, bool Virtual = false) {
if (Invalid) return;
if (OnePastTheEnd) {
setInvalid();
return;
}
PathEntry Entry;
- Entry.BaseOrMember = D;
+ APValue::BaseOrMemberType Value(D, Virtual);
+ Entry.BaseOrMember = Value.getOpaqueValue();
Entries.push_back(Entry);
ArrayElement = false;
}
@@ -192,6 +216,9 @@ namespace {
/// Parent - The caller of this stack frame.
CallStackFrame *Caller;
+ /// This - The binding for the this pointer in this call, if any.
+ const LValue *This;
+
/// ParmBindings - Parameter bindings for this function call, indexed by
/// parameters' function scope indices.
const CCValue *Arguments;
@@ -201,7 +228,8 @@ namespace {
/// Temporaries - Temporary lvalues materialized within this stack frame.
MapTy Temporaries;
- CallStackFrame(EvalInfo &Info, const CCValue *Arguments);
+ CallStackFrame(EvalInfo &Info, const LValue *This,
+ const CCValue *Arguments);
~CallStackFrame();
};
@@ -229,10 +257,18 @@ namespace {
/// initialized last.
CallStackFrame BottomFrame;
+ /// EvaluatingDecl - This is the declaration whose initializer is being
+ /// evaluated, if any.
+ const VarDecl *EvaluatingDecl;
+
+ /// EvaluatingDeclValue - This is the value being constructed for the
+ /// declaration whose initializer is being evaluated, if any.
+ APValue *EvaluatingDeclValue;
+
EvalInfo(const ASTContext &C, Expr::EvalStatus &S)
: Ctx(C), EvalStatus(S), CurrentCall(0), NumCalls(0), CallStackDepth(0),
- BottomFrame(*this, 0) {}
+ BottomFrame(*this, 0, 0), EvaluatingDecl(0), EvaluatingDeclValue(0) {}
const CCValue *getOpaqueValue(const OpaqueValueExpr *e) const {
MapTy::const_iterator i = OpaqueValues.find(e);
@@ -240,11 +276,17 @@ namespace {
return &i->second;
}
+ void setEvaluatingDecl(const VarDecl *VD, APValue &Value) {
+ EvaluatingDecl = VD;
+ EvaluatingDeclValue = &Value;
+ }
+
const LangOptions &getLangOpts() { return Ctx.getLangOptions(); }
};
- CallStackFrame::CallStackFrame(EvalInfo &Info, const CCValue *Arguments)
- : Info(Info), Caller(Info.CurrentCall), Arguments(Arguments) {
+ CallStackFrame::CallStackFrame(EvalInfo &Info, const LValue *This,
+ const CCValue *Arguments)
+ : Info(Info), Caller(Info.CurrentCall), This(This), Arguments(Arguments) {
Info.CurrentCall = this;
++Info.CallStackDepth;
}
@@ -330,7 +372,7 @@ namespace {
static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E);
static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info,
- const Expr *E);
+ const LValue &This, const Expr *E);
static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info);
static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info);
static bool EvaluateInteger(const Expr *E, APSInt &Result, EvalInfo &Info);
@@ -343,24 +385,52 @@ static bool EvaluateComplex(const Expr *E, ComplexValue &Res, EvalInfo &Info);
// Misc utilities
//===----------------------------------------------------------------------===//
+/// Should this call expression be treated as a string literal?
+static bool IsStringLiteralCall(const CallExpr *E) {
+ unsigned Builtin = E->isBuiltinCall();
+ return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
+ Builtin == Builtin::BI__builtin___NSStringMakeConstantString);
+}
+
static bool IsGlobalLValue(const Expr* E) {
+ // C++11 [expr.const]p3 An address constant expression is a prvalue core
+ // constant expression of pointer type that evaluates to...
+
+ // ... a null pointer value, or a prvalue core constant expression of type
+ // std::nullptr_t.
if (!E) return true;
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
- if (isa<FunctionDecl>(DRE->getDecl()))
- return true;
+ switch (E->getStmtClass()) {
+ default:
+ return false;
+ case Expr::DeclRefExprClass: {
+ const DeclRefExpr *DRE = cast<DeclRefExpr>(E);
+ // ... the address of an object with static storage duration,
if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()))
return VD->hasGlobalStorage();
+ // ... to the address of a function,
+ if (isa<FunctionDecl>(DRE->getDecl()))
+ return true;
return false;
}
-
- if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(E))
- return CLE->isFileScope();
-
- if (isa<MemberExpr>(E) || isa<MaterializeTemporaryExpr>(E))
- return false;
-
- return true;
+ case Expr::CompoundLiteralExprClass:
+ return cast<CompoundLiteralExpr>(E)->isFileScope();
+ // A string literal has static storage duration.
+ case Expr::StringLiteralClass:
+ case Expr::PredefinedExprClass:
+ case Expr::ObjCStringLiteralClass:
+ case Expr::ObjCEncodeExprClass:
+ return true;
+ case Expr::CallExprClass:
+ return IsStringLiteralCall(cast<CallExpr>(E));
+ // For GCC compatibility, &&label has static storage duration.
+ case Expr::AddrLabelExprClass:
+ return true;
+ // A Block literal expression may be used as the initialization value for
+ // Block variables at global or local static scope.
+ case Expr::BlockExprClass:
+ return !cast<BlockExpr>(E)->getBlockDecl()->hasCaptures();
+ }
}
/// Check that this reference or pointer core constant expression is a valid
@@ -381,6 +451,8 @@ static bool CheckLValueConstantExpression(const T &LVal, APValue &Value) {
return true;
}
+ // FIXME: Null references are not constant expressions.
+
Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(),
Designator.Entries);
return true;
@@ -477,6 +549,8 @@ static bool HandleConversionToBool(const CCValue &Val, bool &Result) {
}
case APValue::Vector:
case APValue::Array:
+ case APValue::Struct:
+ case APValue::Union:
return false;
}
@@ -534,8 +608,142 @@ static APFloat HandleIntToFloatCast(QualType DestType, QualType SrcType,
return Result;
}
+/// If the given LValue refers to a base subobject of some object, find the most
+/// derived object and the corresponding complete record type. This is necessary
+/// in order to find the offset of a virtual base class.
+static bool ExtractMostDerivedObject(EvalInfo &Info, LValue &Result,
+ const CXXRecordDecl *&MostDerivedType) {
+ SubobjectDesignator &D = Result.Designator;
+ if (D.Invalid || !Result.Base)
+ return false;
+
+ const Type *T = Result.Base->getType().getTypePtr();
+
+ // Find path prefix which leads to the most-derived subobject.
+ unsigned MostDerivedPathLength = 0;
+ MostDerivedType = T->getAsCXXRecordDecl();
+ bool MostDerivedIsArrayElement = false;
+
+ for (unsigned I = 0, N = D.Entries.size(); I != N; ++I) {
+ bool IsArray = T && T->isArrayType();
+ if (IsArray)
+ T = T->getBaseElementTypeUnsafe();
+ else if (const FieldDecl *FD = getAsField(D.Entries[I]))
+ T = FD->getType().getTypePtr();
+ else
+ T = 0;
+
+ if (T) {
+ MostDerivedType = T->getAsCXXRecordDecl();
+ MostDerivedPathLength = I + 1;
+ MostDerivedIsArrayElement = IsArray;
+ }
+ }
+
+ if (!MostDerivedType)
+ return false;
+
+ // (B*)&d + 1 has no most-derived object.
+ if (D.OnePastTheEnd && MostDerivedPathLength != D.Entries.size())
+ return false;
+
+ // Remove the trailing base class path entries and their offsets.
+ const RecordDecl *RD = MostDerivedType;
+ for (unsigned I = MostDerivedPathLength, N = D.Entries.size(); I != N; ++I) {
+ const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
+ const CXXRecordDecl *Base = getAsBaseClass(D.Entries[I]);
+ if (isVirtualBaseClass(D.Entries[I])) {
+ assert(I == MostDerivedPathLength &&
+ "virtual base class must be immediately after most-derived class");
+ Result.Offset -= Layout.getVBaseClassOffset(Base);
+ } else
+ Result.Offset -= Layout.getBaseClassOffset(Base);
+ RD = Base;
+ }
+ D.Entries.resize(MostDerivedPathLength);
+ D.ArrayElement = MostDerivedIsArrayElement;
+ return true;
+}
+
+static void HandleLValueDirectBase(EvalInfo &Info, LValue &Obj,
+ const CXXRecordDecl *Derived,
+ const CXXRecordDecl *Base,
+ const ASTRecordLayout *RL = 0) {
+ if (!RL) RL = &Info.Ctx.getASTRecordLayout(Derived);
+ Obj.getLValueOffset() += RL->getBaseClassOffset(Base);
+ Obj.Designator.addDecl(Base, /*Virtual*/ false);
+}
+
+static bool HandleLValueBase(EvalInfo &Info, LValue &Obj,
+ const CXXRecordDecl *DerivedDecl,
+ const CXXBaseSpecifier *Base) {
+ const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl();
+
+ if (!Base->isVirtual()) {
+ HandleLValueDirectBase(Info, Obj, DerivedDecl, BaseDecl);
+ return true;
+ }
+
+ // Extract most-derived object and corresponding type.
+ if (!ExtractMostDerivedObject(Info, Obj, DerivedDecl))
+ return false;
+
+ const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(DerivedDecl);
+ Obj.getLValueOffset() += Layout.getVBaseClassOffset(BaseDecl);
+ Obj.Designator.addDecl(BaseDecl, /*Virtual*/ true);
+ return true;
+}
+
+/// Update LVal to refer to the given field, which must be a member of the type
+/// currently described by LVal.
+static void HandleLValueMember(EvalInfo &Info, LValue &LVal,
+ const FieldDecl *FD,
+ const ASTRecordLayout *RL = 0) {
+ if (!RL)
+ RL = &Info.Ctx.getASTRecordLayout(FD->getParent());
+
+ unsigned I = FD->getFieldIndex();
+ LVal.Offset += Info.Ctx.toCharUnitsFromBits(RL->getFieldOffset(I));
+ LVal.Designator.addDecl(FD);
+}
+
+/// Get the size of the given type in char units.
+static bool HandleSizeof(EvalInfo &Info, QualType Type, CharUnits &Size) {
+ // sizeof(void), __alignof__(void), sizeof(function) = 1 as a gcc
+ // extension.
+ if (Type->isVoidType() || Type->isFunctionType()) {
+ Size = CharUnits::One();
+ return true;
+ }
+
+ if (!Type->isConstantSizeType()) {
+ // sizeof(vla) is not a constantexpr: C99 6.5.3.4p2.
+ return false;
+ }
+
+ Size = Info.Ctx.getTypeSizeInChars(Type);
+ return true;
+}
+
+/// Update a pointer value to model pointer arithmetic.
+/// \param Info - Information about the ongoing evaluation.
+/// \param LVal - The pointer value to be updated.
+/// \param EltTy - The pointee type represented by LVal.
+/// \param Adjustment - The adjustment, in objects of type EltTy, to add.
+static bool HandleLValueArrayAdjustment(EvalInfo &Info, LValue &LVal,
+ QualType EltTy, int64_t Adjustment) {
+ CharUnits SizeOfPointee;
+ if (!HandleSizeof(Info, EltTy, SizeOfPointee))
+ return false;
+
+ // Compute the new offset in the appropriate width.
+ LVal.Offset += Adjustment * SizeOfPointee;
+ LVal.Designator.adjustIndex(Adjustment);
+ return true;
+}
+
/// Try to evaluate the initializer for a variable declaration.
-static bool EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD,
+static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,const VarDecl *VD,
CallStackFrame *Frame, CCValue &Result) {
// If this is a parameter to an active constexpr function call, perform
// argument substitution.
@@ -546,6 +754,13 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD,
return true;
}
+ // If we're currently evaluating the initializer of this declaration, use that
+ // in-flight value.
+ if (Info.EvaluatingDecl == VD) {
+ Result = CCValue(*Info.EvaluatingDeclValue, CCValue::GlobalValue());
+ return !Result.isUninit();
+ }
+
// Never evaluate the initializer of a weak variable. We can't be sure that
// this is the definition which will be used.
if (IsWeakDecl(VD))
@@ -567,10 +782,13 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD,
Expr::EvalStatus EStatus;
EvalInfo InitInfo(Info.Ctx, EStatus);
+ APValue EvalResult;
+ InitInfo.setEvaluatingDecl(VD, EvalResult);
+ LValue LVal;
+ LVal.setExpr(E);
// FIXME: The caller will need to know whether the value was a constant
// expression. If not, we should propagate up a diagnostic.
- APValue EvalResult;
- if (!EvaluateConstantExpression(EvalResult, InitInfo, Init)) {
+ if (!EvaluateConstantExpression(EvalResult, InitInfo, LVal, Init)) {
// FIXME: If the evaluation failure was not permanent (for instance, if we
// hit a variable with no declaration yet, or a constexpr function with no
// definition yet), the standard is unclear as to how we should behave.
@@ -605,10 +823,10 @@ static bool ExtractSubobject(EvalInfo &Info, CCValue &Obj, QualType ObjType,
assert(!Obj.isLValue() && "extracting subobject of lvalue");
const APValue *O = &Obj;
+ // Walk the designator's path to find the subobject.
for (unsigned I = 0, N = Sub.Entries.size(); I != N; ++I) {
- if (O->isUninit())
- return false;
if (ObjType->isArrayType()) {
+ // Next subobject is an array element.
const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(ObjType);
if (!CAT)
return false;
@@ -620,10 +838,40 @@ static bool ExtractSubobject(EvalInfo &Info, CCValue &Obj, QualType ObjType,
else
O = &O->getArrayFiller();
ObjType = CAT->getElementType();
+ } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
+ // Next subobject is a class, struct or union field.
+ RecordDecl *RD = ObjType->castAs<RecordType>()->getDecl();
+ if (RD->isUnion()) {
+ const FieldDecl *UnionField = O->getUnionField();
+ if (!UnionField ||
+ UnionField->getCanonicalDecl() != Field->getCanonicalDecl())
+ return false;
+ O = &O->getUnionValue();
+ } else
+ O = &O->getStructField(Field->getFieldIndex());
+ ObjType = Field->getType();
} else {
- // FIXME: Support handling of subobjects of structs and unions. Also
- // for vector elements, if we want to support those?
+ // Next subobject is a base class.
+ const CXXRecordDecl *RD =
+ cast<CXXRecordDecl>(ObjType->castAs<RecordType>()->getDecl());
+ const CXXRecordDecl *Base =
+ getAsBaseClass(Sub.Entries[I])->getCanonicalDecl();
+ unsigned Index = 0;
+ for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(),
+ E = RD->bases_end(); I != E; ++I, ++Index) {
+ QualType BT = I->getType();
+ if (BT->castAs<RecordType>()->getDecl()->getCanonicalDecl() == Base) {
+ O = &O->getStructBase(Index);
+ ObjType = BT;
+ break;
+ }
+ }
+ if (Index == RD->getNumBases())
+ return false;
}
+
+ if (O->isUninit())
+ return false;
}
assert(Info.Ctx.hasSameUnqualifiedType(ObjType, SubType) &&
@@ -632,6 +880,14 @@ static bool ExtractSubobject(EvalInfo &Info, CCValue &Obj, QualType O