[cfe-commits] r143922 - in /cfe/trunk: include/clang/AST/APValue.h lib/AST/APValue.cpp lib/AST/ExprConstant.cpp lib/CodeGen/CGExprConstant.cpp test/SemaCXX/constant-expression-cxx11.cpp
Richard Smith
richard-llvm at metafoo.co.uk
Mon Nov 7 01:22:26 PST 2011
Author: rsmith
Date: Mon Nov 7 03:22:26 2011
New Revision: 143922
URL: http://llvm.org/viewvc/llvm-project?rev=143922&view=rev
Log:
Constant expression evaluation: support for arrays.
Modified:
cfe/trunk/include/clang/AST/APValue.h
cfe/trunk/lib/AST/APValue.cpp
cfe/trunk/lib/AST/ExprConstant.cpp
cfe/trunk/lib/CodeGen/CGExprConstant.cpp
cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
Modified: cfe/trunk/include/clang/AST/APValue.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/APValue.h?rev=143922&r1=143921&r2=143922&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/APValue.h (original)
+++ cfe/trunk/include/clang/AST/APValue.h Mon Nov 7 03:22:26 2011
@@ -25,7 +25,8 @@
class Decl;
/// APValue - This class implements a discriminated union of [uninitialized]
-/// [APSInt] [APFloat], [Complex APSInt] [Complex APFloat], [Expr + Offset].
+/// [APSInt] [APFloat], [Complex APSInt] [Complex APFloat], [Expr + Offset],
+/// [Vector: N * APValue], [Array: N * APValue]
class APValue {
typedef llvm::APSInt APSInt;
typedef llvm::APFloat APFloat;
@@ -37,13 +38,15 @@
ComplexInt,
ComplexFloat,
LValue,
- Vector
+ Vector,
+ Array
};
union LValuePathEntry {
const Decl *BaseOrMember;
uint64_t ArrayIndex;
};
struct NoLValuePath {};
+ struct UninitArray {};
private:
ValueKind Kind;
@@ -55,15 +58,19 @@
APFloat Real, Imag;
ComplexAPFloat() : Real(0.0), Imag(0.0) {}
};
-
+ struct LV;
struct Vec {
APValue *Elts;
unsigned NumElts;
Vec() : Elts(0), NumElts(0) {}
~Vec() { delete[] Elts; }
};
-
- struct LV;
+ struct Arr {
+ APValue *Elts;
+ unsigned NumElts, ArrSize;
+ Arr(unsigned NumElts, unsigned ArrSize);
+ ~Arr();
+ };
enum {
MaxSize = (sizeof(ComplexAPSInt) > sizeof(ComplexAPFloat) ?
@@ -104,6 +111,9 @@
MakeLValue(); setLValue(B, O, Path);
}
APValue(const Expr *B);
+ APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(Uninitialized) {
+ MakeArray(InitElts, Size);
+ }
~APValue() {
MakeUninit();
@@ -117,6 +127,7 @@
bool isComplexFloat() const { return Kind == ComplexFloat; }
bool isLValue() const { return Kind == LValue; }
bool isVector() const { return Kind == Vector; }
+ bool isArray() const { return Kind == Array; }
void print(raw_ostream &OS) const;
void dump() const;
@@ -137,19 +148,6 @@
return const_cast<APValue*>(this)->getFloat();
}
- APValue &getVectorElt(unsigned i) {
- assert(isVector() && "Invalid accessor");
- return ((Vec*)(char*)Data)->Elts[i];
- }
- const APValue &getVectorElt(unsigned i) const {
- assert(isVector() && "Invalid accessor");
- return ((const Vec*)(const char*)Data)->Elts[i];
- }
- unsigned getVectorLength() const {
- assert(isVector() && "Invalid accessor");
- return ((const Vec*)(const void *)Data)->NumElts;
- }
-
APSInt &getComplexIntReal() {
assert(isComplexInt() && "Invalid accessor");
return ((ComplexAPSInt*)(char*)Data)->Real;
@@ -190,6 +188,47 @@
bool hasLValuePath() const;
ArrayRef<LValuePathEntry> getLValuePath() const;
+ APValue &getVectorElt(unsigned I) {
+ assert(isVector() && "Invalid accessor");
+ assert(I < getVectorLength() && "Index out of range");
+ return ((Vec*)(char*)Data)->Elts[I];
+ }
+ const APValue &getVectorElt(unsigned I) const {
+ return const_cast<APValue*>(this)->getVectorElt(I);
+ }
+ unsigned getVectorLength() const {
+ assert(isVector() && "Invalid accessor");
+ return ((const Vec*)(const void *)Data)->NumElts;
+ }
+
+ APValue &getArrayInitializedElt(unsigned I) {
+ assert(isArray() && "Invalid accessor");
+ assert(I < getArrayInitializedElts() && "Index out of range");
+ return ((Arr*)(char*)Data)->Elts[I];
+ }
+ const APValue &getArrayInitializedElt(unsigned I) const {
+ return const_cast<APValue*>(this)->getArrayInitializedElt(I);
+ }
+ bool hasArrayFiller() const {
+ return getArrayInitializedElts() != getArraySize();
+ }
+ APValue &getArrayFiller() {
+ assert(isArray() && "Invalid accessor");
+ assert(hasArrayFiller() && "No array filler");
+ return ((Arr*)(char*)Data)->Elts[getArrayInitializedElts()];
+ }
+ const APValue &getArrayFiller() const {
+ return const_cast<APValue*>(this)->getArrayFiller();
+ }
+ unsigned getArrayInitializedElts() const {
+ assert(isArray() && "Invalid accessor");
+ return ((const Arr*)(const void *)Data)->NumElts;
+ }
+ unsigned getArraySize() const {
+ assert(isArray() && "Invalid accessor");
+ return ((const Arr*)(const void *)Data)->ArrSize;
+ }
+
void setInt(const APSInt &I) {
assert(isInt() && "Invalid accessor");
*(APSInt*)(char*)Data = I;
@@ -253,6 +292,7 @@
Kind = ComplexFloat;
}
void MakeLValue();
+ void MakeArray(unsigned InitElts, unsigned Size);
};
inline raw_ostream &operator<<(raw_ostream &OS, const APValue &V) {
Modified: cfe/trunk/lib/AST/APValue.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/APValue.cpp?rev=143922&r1=143921&r2=143922&view=diff
==============================================================================
--- cfe/trunk/lib/AST/APValue.cpp (original)
+++ cfe/trunk/lib/AST/APValue.cpp Mon Nov 7 03:22:26 2011
@@ -55,6 +55,13 @@
}
};
+// FIXME: Reduce the malloc traffic here.
+
+APValue::Arr::Arr(unsigned NumElts, unsigned Size) :
+ Elts(new APValue[NumElts + (NumElts != Size ? 1 : 0)]),
+ NumElts(NumElts), ArrSize(Size) {}
+APValue::Arr::~Arr() { delete [] Elts; }
+
APValue::APValue(const Expr* B) : Kind(Uninitialized) {
MakeLValue();
setLValue(B, CharUnits::Zero(), ArrayRef<LValuePathEntry>());
@@ -75,6 +82,8 @@
MakeComplexFloat();
else if (RHS.isLValue())
MakeLValue();
+ else if (RHS.isArray())
+ MakeArray(RHS.getArrayInitializedElts(), RHS.getArraySize());
}
if (isInt())
setInt(RHS.getInt());
@@ -92,6 +101,11 @@
setLValue(RHS.getLValueBase(), RHS.getLValueOffset(),RHS.getLValuePath());
else
setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), NoLValuePath());
+ } else if (isArray()) {
+ for (unsigned I = 0, N = RHS.getArrayInitializedElts(); I != N; ++I)
+ getArrayInitializedElt(I) = RHS.getArrayInitializedElt(I);
+ if (RHS.hasArrayFiller())
+ getArrayFiller() = RHS.getArrayFiller();
}
return *this;
}
@@ -107,9 +121,10 @@
((ComplexAPSInt*)(char*)Data)->~ComplexAPSInt();
else if (Kind == ComplexFloat)
((ComplexAPFloat*)(char*)Data)->~ComplexAPFloat();
- else if (Kind == LValue) {
+ else if (Kind == LValue)
((LV*)(char*)Data)->~LV();
- }
+ else if (Kind == Array)
+ ((Arr*)(char*)Data)->~Arr();
Kind = Uninitialized;
}
@@ -149,9 +164,20 @@
case ComplexFloat:
OS << "ComplexFloat: " << GetApproxValue(getComplexFloatReal())
<< ", " << GetApproxValue(getComplexFloatImag());
+ return;
case LValue:
OS << "LValue: <todo>";
return;
+ case Array:
+ OS << "Array: ";
+ for (unsigned I = 0, N = getArrayInitializedElts(); I != N; ++I) {
+ OS << getArrayInitializedElt(I);
+ if (I != getArraySize() - 1) OS << ", ";
+ }
+ if (hasArrayFiller())
+ OS << getArraySize() - getArrayInitializedElts() << " x "
+ << getArrayFiller();
+ return;
}
}
@@ -187,6 +213,15 @@
case APValue::LValue:
Out << "LValue: <todo>";
break;
+ case APValue::Array:
+ Out << '{';
+ if (unsigned N = V.getArrayInitializedElts()) {
+ Out << V.getArrayInitializedElt(0);
+ for (unsigned I = 1; I != N; ++I)
+ Out << ", " << V.getArrayInitializedElt(I);
+ }
+ Out << '}';
+ break;
}
}
@@ -244,3 +279,9 @@
new ((void*)(char*)Data) LV();
Kind = LValue;
}
+
+void APValue::MakeArray(unsigned InitElts, unsigned Size) {
+ assert(isUninit() && "Bad state change");
+ new ((void*)(char*)Data) Arr(InitElts, Size);
+ Kind = Array;
+}
Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=143922&r1=143921&r2=143922&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Mon Nov 7 03:22:26 2011
@@ -132,6 +132,7 @@
void adjustIndex(uint64_t N) {
if (Invalid) return;
if (ArrayElement) {
+ // FIXME: Make sure the index stays within bounds, or one past the end.
Entries.back().ArrayIndex += N;
return;
}
@@ -475,6 +476,7 @@
return EvalPointerValueAsBool(PointerResult, Result);
}
case APValue::Vector:
+ case APValue::Array:
return false;
}
@@ -569,6 +571,13 @@
// expression. If not, we should propagate up a diagnostic.
APValue EvalResult;
if (!EvaluateConstantExpression(EvalResult, InitInfo, 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.
+ //
+ // Either the initializer should be evaluated when the variable is defined,
+ // or a failed evaluation of the initializer should be reattempted each time
+ // it is used.
VD->setEvaluatedValue(APValue());
return false;
}
@@ -583,8 +592,48 @@
return Quals.hasConst() && !Quals.hasVolatile();
}
-bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type,
- const LValue &LVal, CCValue &RVal) {
+/// Extract the designated sub-object of an rvalue.
+static bool ExtractSubobject(EvalInfo &Info, CCValue &Obj, QualType ObjType,
+ const SubobjectDesignator &Sub, QualType SubType) {
+ if (Sub.Invalid || Sub.OnePastTheEnd)
+ return false;
+ if (Sub.Entries.empty()) {
+ assert(Info.Ctx.hasSameUnqualifiedType(ObjType, SubType) &&
+ "Unexpected subobject type");
+ return true;
+ }
+
+ assert(!Obj.isLValue() && "extracting subobject of lvalue");
+ const APValue *O = &Obj;
+ for (unsigned I = 0, N = Sub.Entries.size(); I != N; ++I) {
+ if (O->isUninit())
+ return false;
+ if (ObjType->isArrayType()) {
+ const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(ObjType);
+ if (!CAT)
+ return false;
+ uint64_t Index = Sub.Entries[I].ArrayIndex;
+ if (CAT->getSize().ule(Index))
+ return false;
+ if (O->getArrayInitializedElts() > Index)
+ O = &O->getArrayInitializedElt(Index);
+ else
+ O = &O->getArrayFiller();
+ ObjType = CAT->getElementType();
+ } else {
+ // FIXME: Support handling of subobjects of structs and unions. Also
+ // for vector elements, if we want to support those?
+ }
+ }
+
+ assert(Info.Ctx.hasSameUnqualifiedType(ObjType, SubType) &&
+ "Unexpected subobject type");
+ Obj = CCValue(*O, CCValue::GlobalValue());
+ return true;
+}
+
+static bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type,
+ const LValue &LVal, CCValue &RVal) {
const Expr *Base = LVal.Base;
CallStackFrame *Frame = LVal.Frame;
@@ -620,10 +669,7 @@
return false;
if (isa<ParmVarDecl>(VD) || !VD->getAnyInitializer()->isLValue())
- // If the lvalue refers to a subobject or has been cast to some other
- // type, don't use it.
- return LVal.Offset.isZero() &&
- Info.Ctx.hasSameUnqualifiedType(Type, VT);
+ return ExtractSubobject(Info, RVal, VT, LVal.Designator, Type);
// The declaration was initialized by an lvalue, with no lvalue-to-rvalue
// conversion. This happens when the declaration and the lvalue should be
@@ -654,31 +700,22 @@
return true;
}
- // FIXME: Support accessing subobjects of objects of literal types. A simple
- // byte offset is insufficient for C++11 semantics: we need to know how the
- // reference was formed (which union member was named, for instance).
-
- // Beyond this point, we don't support accessing subobjects.
- if (!LVal.Offset.isZero() ||
- !Info.Ctx.hasSameUnqualifiedType(Type, Base->getType()))
- return false;
-
- // If this is a temporary expression with a nontrivial initializer, grab the
- // value from the relevant stack frame.
if (Frame) {
+ // If this is a temporary expression with a nontrivial initializer, grab the
+ // value from the relevant stack frame.
RVal = Frame->Temporaries[Base];
- return true;
- }
-
- // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the
- // initializer until now for such expressions. Such an expression can't be
- // an ICE in C, so this only matters for fold.
- if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(Base)) {
+ } else if (const CompoundLiteralExpr *CLE
+ = dyn_cast<CompoundLiteralExpr>(Base)) {
+ // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the
+ // initializer until now for such expressions. Such an expression can't be
+ // an ICE in C, so this only matters for fold.
assert(!Info.getLangOpts().CPlusPlus && "lvalue compound literal in c++?");
- return Evaluate(RVal, Info, CLE->getInitializer());
- }
+ if (!Evaluate(RVal, Info, CLE->getInitializer()))
+ return false;
+ } else
+ return false;
- return false;
+ return ExtractSubobject(Info, RVal, Base->getType(), LVal.Designator, Type);
}
namespace {
@@ -1600,6 +1637,55 @@
}
//===----------------------------------------------------------------------===//
+// Array Evaluation
+//===----------------------------------------------------------------------===//
+
+namespace {
+ class ArrayExprEvaluator
+ : public ExprEvaluatorBase<ArrayExprEvaluator, bool> {
+ APValue &Result;
+ public:
+
+ ArrayExprEvaluator(EvalInfo &Info, APValue &Result)
+ : ExprEvaluatorBaseTy(Info), Result(Result) {}
+
+ bool Success(const APValue &V, const Expr *E) {
+ assert(V.isArray() && "Expected array type");
+ Result = V;
+ return true;
+ }
+ bool Error(const Expr *E) { return false; }
+
+ bool VisitInitListExpr(const InitListExpr *E);
+ };
+} // end anonymous namespace
+
+static bool EvaluateArray(const Expr* E, APValue& Result, EvalInfo &Info) {
+ assert(E->isRValue() && E->getType()->isArrayType() &&
+ E->getType()->isLiteralType() && "not a literal array rvalue");
+ return ArrayExprEvaluator(Info, Result).Visit(E);
+}
+
+bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
+ const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType());
+ if (!CAT)
+ return false;
+
+ Result = APValue(APValue::UninitArray(), E->getNumInits(),
+ CAT->getSize().getZExtValue());
+ for (InitListExpr::const_iterator I = E->begin(), End = E->end();
+ I != End; ++I)
+ if (!EvaluateConstantExpression(Result.getArrayInitializedElt(I-E->begin()),
+ Info, cast<Expr>(*I)))
+ return false;
+
+ if (!Result.hasArrayFiller()) return true;
+ assert(E->hasArrayFiller() && "no array filler for incomplete init list");
+ return EvaluateConstantExpression(Result.getArrayFiller(), Info,
+ E->getArrayFiller());
+}
+
+//===----------------------------------------------------------------------===//
// Integer Evaluation
//
// As a GNU extension, we support casting pointers to sufficiently-wide integer
@@ -2173,6 +2259,10 @@
return Success(E->getOpcode() == BO_NE, E);
}
+ // FIXME: Implement the C++11 restrictions:
+ // - Pointer subtractions must be on elements of the same array.
+ // - Pointer comparisons must be between members with the same access.
+
if (E->getOpcode() == BO_Sub) {
QualType Type = E->getLHS()->getType();
QualType ElementType = Type->getAs<PointerType>()->getPointeeType();
@@ -3258,8 +3348,8 @@
// FIXME: Implement evaluation of pointer-to-member types.
return false;
} else if (E->getType()->isArrayType() && E->getType()->isLiteralType()) {
- // FIXME: Implement evaluation of array rvalues.
- return false;
+ if (!EvaluateArray(E, Result, Info))
+ return false;
} else if (E->getType()->isRecordType() && E->getType()->isLiteralType()) {
// FIXME: Implement evaluation of record rvalues.
return false;
@@ -3278,10 +3368,10 @@
if (E->isRValue() && E->getType()->isLiteralType()) {
// Evaluate arrays and record types in-place, so that later initializers can
// refer to earlier-initialized members of the object.
- if (E->getType()->isArrayType())
- // FIXME: Implement evaluation of array rvalues.
- return false;
- else if (E->getType()->isRecordType())
+ if (E->getType()->isArrayType()) {
+ if (!EvaluateArray(E, Result, Info))
+ return false;
+ } else if (E->getType()->isRecordType())
// FIXME: Implement evaluation of record rvalues.
return false;
}
@@ -3312,6 +3402,10 @@
return false;
}
+ // Don't produce array constants until CodeGen is taught to handle them.
+ if (Value.isArray())
+ return false;
+
// Check this core constant expression is a constant expression, and if so,
// convert it to one.
return CheckConstantExpression(Value, Result.Val);
Modified: cfe/trunk/lib/CodeGen/CGExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprConstant.cpp?rev=143922&r1=143921&r2=143922&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExprConstant.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExprConstant.cpp Mon Nov 7 03:22:26 2011
@@ -1070,6 +1070,9 @@
}
return llvm::ConstantVector::get(Inits);
}
+ case APValue::Array:
+ assert(0 && "shouldn't see array constants here yet");
+ break;
}
}
Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp?rev=143922&r1=143921&r2=143922&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Mon Nov 7 03:22:26 2011
@@ -280,3 +280,45 @@
static_assert_fold(max == str + 38, "");
}
+
+namespace Array {
+
+// FIXME: Use templates for these once we support constexpr templates.
+constexpr int Sum(const int *begin, const int *end) {
+ return begin == end ? 0 : *begin + Sum(begin+1, end);
+}
+constexpr const int *begin(const int (&xs)[5]) { return xs; }
+constexpr const int *end(const int (&xs)[5]) { return xs + 5; }
+
+constexpr int xs[] = { 1, 2, 3, 4, 5 };
+constexpr int ys[] = { 5, 4, 3, 2, 1 };
+constexpr int sum_xs = Sum(begin(xs), end(xs));
+static_assert_fold(sum_xs == 15, "");
+
+constexpr int ZipFoldR(int (*F)(int x, int y, int c), int n,
+ const int *xs, const int *ys, int c) {
+ return n ? F(*xs, *ys, ZipFoldR(F, n-1, xs+1, ys+1, c)) : c;
+}
+constexpr int MulAdd(int x, int y, int c) { return x * y + c; }
+constexpr int InnerProduct = ZipFoldR(MulAdd, 5, xs, ys, 0);
+static_assert_fold(InnerProduct == 35, "");
+
+constexpr int SubMul(int x, int y, int c) { return (x - y) * c; }
+constexpr int DiffProd = ZipFoldR(SubMul, 2, xs+3, ys+3, 1);
+static_assert_fold(DiffProd == 8, "");
+static_assert_fold(ZipFoldR(SubMul, 3, xs+3, ys+3, 1), ""); // expected-error {{constant expression}}
+
+constexpr const int *p = xs + 3;
+constexpr int xs4 = p[1]; // ok
+constexpr int xs5 = p[2]; // expected-error {{constant expression}}
+constexpr int xs0 = p[-3]; // ok
+constexpr int xs_1 = p[-4]; // expected-error {{constant expression}}
+
+constexpr int zs[2][2][2][2] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+static_assert_fold(zs[0][0][0][0] == 1, "");
+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, "");
+
+}
More information about the cfe-commits
mailing list