V.getLValuePath());
else
assert(V.getLValuePath().empty() &&"Null pointer with nonempty path");
+ OnePastTheEnd = V.isLValueOnePastTheEnd();
}
}
/// A core constant value. This can be the value of any constant expression,
/// or a pointer or reference to a non-static object or function parameter.
+ ///
+ /// For an LValue, the base and offset are stored in the APValue subobject,
+ /// but the other information is stored in the SubobjectDesignator. For all
+ /// other value kinds, the value is stored directly in the APValue subobject.
class CCValue : public APValue {
typedef llvm::APSInt APSInt;
typedef llvm::APFloat APFloat;
APValue(B, O, APValue::NoLValuePath()), CallFrame(F), Designator(D) {}
CCValue(const APValue &V, GlobalValue) :
APValue(V), CallFrame(0), Designator(V) {}
+ CCValue(const ValueDecl *D, bool IsDerivedMember,
+ ArrayRef<const CXXRecordDecl*> Path) :
+ APValue(D, IsDerivedMember, Path) {}
CallStackFrame *getLValueFrame() const {
assert(getKind() == LValue);
Designator = SubobjectDesignator();
}
};
+
+ struct MemberPtr {
+ MemberPtr() {}
+ explicit MemberPtr(const ValueDecl *Decl) :
+ DeclAndIsDerivedMember(Decl, false), Path() {}
+
+ /// The member or (direct or indirect) field referred to by this member
+ /// pointer, or 0 if this is a null member pointer.
+ const ValueDecl *getDecl() const {
+ return DeclAndIsDerivedMember.getPointer();
+ }
+ /// Is this actually a member of some type derived from the relevant class?
+ bool isDerivedMember() const {
+ return DeclAndIsDerivedMember.getInt();
+ }
+ /// Get the class which the declaration actually lives in.
+ const CXXRecordDecl *getContainingRecord() const {
+ return cast<CXXRecordDecl>(
+ DeclAndIsDerivedMember.getPointer()->getDeclContext());
+ }
+
+ void moveInto(CCValue &V) const {
+ V = CCValue(getDecl(), isDerivedMember(), Path);
+ }
+ void setFrom(const CCValue &V) {
+ assert(V.isMemberPointer());
+ DeclAndIsDerivedMember.setPointer(V.getMemberPointerDecl());
+ DeclAndIsDerivedMember.setInt(V.isMemberPointerToDerivedMember());
+ Path.clear();
+ ArrayRef<const CXXRecordDecl*> P = V.getMemberPointerPath();
+ Path.insert(Path.end(), P.begin(), P.end());
+ }
+
+ /// DeclAndIsDerivedMember - The member declaration, and a flag indicating
+ /// whether the member is a member of some class derived from the class type
+ /// of the member pointer.
+ llvm::PointerIntPair<const ValueDecl*, 1, bool> DeclAndIsDerivedMember;
+ /// Path - The path of base/derived classes from the member declaration's
+ /// class (exclusive) to the class type of the member pointer (inclusive).
+ SmallVector<const CXXRecordDecl*, 4> Path;
+
+ /// Perform a cast towards the class of the Decl (either up or down the
+ /// hierarchy).
+ bool castBack(const CXXRecordDecl *Class) {
+ assert(!Path.empty());
+ const CXXRecordDecl *Expected;
+ if (Path.size() >= 2)
+ Expected = Path[Path.size() - 2];
+ else
+ Expected = getContainingRecord();
+ if (Expected->getCanonicalDecl() != Class->getCanonicalDecl()) {
+ // C++11 [expr.static.cast]p12: In a conversion from (D::*) to (B::*),
+ // if B does not contain the original member and is not a base or
+ // derived class of the class containing the original member, the result
+ // of the cast is undefined.
+ // C++11 [conv.mem]p2 does not cover this case for a cast from (B::*) to
+ // (D::*). We consider that to be a language defect.
+ return false;
+ }
+ Path.pop_back();
+ return true;
+ }
+ /// Perform a base-to-derived member pointer cast.
+ bool castToDerived(const CXXRecordDecl *Derived) {
+ if (!getDecl())
+ return true;
+ if (!isDerivedMember()) {
+ Path.push_back(Derived);
+ return true;
+ }
+ if (!castBack(Derived))
+ return false;
+ if (Path.empty())
+ DeclAndIsDerivedMember.setInt(false);
+ return true;
+ }
+ /// Perform a derived-to-base member pointer cast.
+ bool castToBase(const CXXRecordDecl *Base) {
+ if (!getDecl())
+ return true;
+ if (Path.empty())
+ DeclAndIsDerivedMember.setInt(true);
+ if (isDerivedMember()) {
+ Path.push_back(Base);
+ return true;
+ }
+ return castBack(Base);
+ }
+ };
}
static bool Evaluate(CCValue &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 EvaluateMemberPointer(const Expr *E, MemberPtr &Result,
+ EvalInfo &Info);
+static bool EvaluateTemporary(const Expr *E, LValue &Result, EvalInfo &Info);
static bool EvaluateInteger(const Expr *E, APSInt &Result, EvalInfo &Info);
static bool EvaluateIntegerOrLValue(const Expr *E, CCValue &Result,
EvalInfo &Info);
const SubobjectDesignator &Designator = LVal.getLValueDesignator();
// A constant expression must refer to an object or be a null pointer.
- if (Designator.Invalid || Designator.OnePastTheEnd ||
+ if (Designator.Invalid ||
(!LVal.getLValueBase() && !Designator.Entries.empty())) {
- // FIXME: Check for out-of-bounds array indices.
// FIXME: This is not a constant expression.
Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(),
APValue::NoLValuePath());
return true;
}
- // FIXME: Null references are not constant expressions.
-
Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(),
- Designator.Entries);
+ Designator.Entries, Designator.OnePastTheEnd);
return true;
}
return Decl && IsWeakDecl(Decl);
}
-static bool EvalPointerValueAsBool(const LValue &Value, bool &Result) {
+static bool EvalPointerValueAsBool(const CCValue &Value, bool &Result) {
// A null base expression indicates a null pointer. These are always
// evaluatable, and they are false unless the offset is zero.
- if (!Value.Base) {
- Result = !Value.Offset.isZero();
+ if (!Value.getLValueBase()) {
+ Result = !Value.getLValueOffset().isZero();
return true;
}
// Require the base expression to be a global l-value.
// FIXME: C++11 requires such conversions. Remove this check.
- if (!IsGlobalLValue(Value.Base)) return false;
+ if (!IsGlobalLValue(Value.getLValueBase())) return false;
- // We have a non-null base expression. These are generally known to
- // be true, but if it'a decl-ref to a weak symbol it can be null at
- // runtime.
+ // We have a non-null base. These are generally known to be true, but if it's
+ // a weak declaration it can be null at runtime.
Result = true;
- return !IsWeakLValue(Value);
+ const ValueDecl *Decl = Value.getLValueBase().dyn_cast<const ValueDecl*>();
+ return !Decl || !IsWeakDecl(Decl);
}
static bool HandleConversionToBool(const CCValue &Val, bool &Result) {
Result = !Val.getComplexFloatReal().isZero() ||
!Val.getComplexFloatImag().isZero();
return true;
- case APValue::LValue: {
- LValue PointerResult;
- PointerResult.setFrom(Val);
- return EvalPointerValueAsBool(PointerResult, Result);
- }
+ case APValue::LValue:
+ return EvalPointerValueAsBool(Val, Result);
+ case APValue::MemberPointer:
+ Result = Val.getMemberPointerDecl();
+ return true;
case APValue::Vector:
case APValue::Array:
case APValue::Struct:
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)
+static bool FindMostDerivedObject(EvalInfo &Info, const LValue &LVal,
+ const CXXRecordDecl *&MostDerivedType,
+ unsigned &MostDerivedPathLength,
+ bool &MostDerivedIsArrayElement) {
+ const SubobjectDesignator &D = LVal.Designator;
+ if (D.Invalid || !LVal.Base)
return false;
- const Type *T = getType(Result.Base).getTypePtr();
+ const Type *T = getType(LVal.Base).getTypePtr();
// Find path prefix which leads to the most-derived subobject.
- unsigned MostDerivedPathLength = 0;
MostDerivedType = T->getAsCXXRecordDecl();
- bool MostDerivedIsArrayElement = false;
+ MostDerivedPathLength = 0;
+ MostDerivedIsArrayElement = false;
for (unsigned I = 0, N = D.Entries.size(); I != N; ++I) {
bool IsArray = T && T->isArrayType();
}
}
- 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) {
+ return MostDerivedType != 0;
+}
+
+static void TruncateLValueBasePath(EvalInfo &Info, LValue &Result,
+ const RecordDecl *TruncatedType,
+ unsigned TruncatedElements,
+ bool IsArrayElement) {
+ SubobjectDesignator &D = Result.Designator;
+ const RecordDecl *RD = TruncatedType;
+ for (unsigned I = TruncatedElements, 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");
+ if (isVirtualBaseClass(D.Entries[I]))
Result.Offset -= Layout.getVBaseClassOffset(Base);
- } else
+ else
Result.Offset -= Layout.getBaseClassOffset(Base);
RD = Base;
}
- D.Entries.resize(MostDerivedPathLength);
- D.ArrayElement = MostDerivedIsArrayElement;
+ D.Entries.resize(TruncatedElements);
+ D.ArrayElement = IsArrayElement;
+}
+
+/// 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) {
+ unsigned MostDerivedPathLength;
+ bool MostDerivedIsArrayElement;
+ if (!FindMostDerivedObject(Info, Result, MostDerivedType,
+ MostDerivedPathLength, MostDerivedIsArrayElement))
+ return false;
+
+ // Remove the trailing base class path entries and their offsets.
+ TruncateLValueBasePath(Info, Result, MostDerivedType, MostDerivedPathLength,
+ MostDerivedIsArrayElement);
return true;
}
if (Object->isGLValue())
return EvaluateLValue(Object, This, Info);
- // Implicitly promote a prvalue *this object to a glvalue.
- This.set(Object, Info.CurrentCall);
- return EvaluateConstantExpression(Info.CurrentCall->Temporaries[Object], Info,
- This, Object);
+ if (Object->getType()->isLiteralType())
+ return EvaluateTemporary(Object, This, Info);
+
+ return false;
+}
+
+/// HandleMemberPointerAccess - Evaluate a member access operation and build an
+/// lvalue referring to the result.
+///
+/// \param Info - Information about the ongoing evaluation.
+/// \param BO - The member pointer access operation.
+/// \param LV - Filled in with a reference to the resulting object.
+/// \param IncludeMember - Specifies whether the member itself is included in
+/// the resulting LValue subobject designator. This is not possible when
+/// creating a bound member function.
+/// \return The field or method declaration to which the member pointer refers,
+/// or 0 if evaluation fails.
+static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info,
+ const BinaryOperator *BO,
+ LValue &LV,
+ bool IncludeMember = true) {
+ assert(BO->getOpcode() == BO_PtrMemD || BO->getOpcode() == BO_PtrMemI);
+
+ if (!EvaluateObjectArgument(Info, BO->getLHS(), LV))
+ return 0;
+
+ MemberPtr MemPtr;
+ if (!EvaluateMemberPointer(BO->getRHS(), MemPtr, Info))
+ return 0;
+
+ // C++11 [expr.mptr.oper]p6: If the second operand is the null pointer to
+ // member value, the behavior is undefined.
+ if (!MemPtr.getDecl())
+ return 0;
+
+ if (MemPtr.isDerivedMember()) {
+ // This is a member of some derived class. Truncate LV appropriately.
+ const CXXRecordDecl *MostDerivedType;
+ unsigned MostDerivedPathLength;
+ bool MostDerivedIsArrayElement;
+ if (!FindMostDerivedObject(Info, LV, MostDerivedType, MostDerivedPathLength,
+ MostDerivedIsArrayElement))
+ return 0;
+
+ // The end of the derived-to-base path for the base object must match the
+ // derived-to-base path for the member pointer.
+ if (MostDerivedPathLength + MemPtr.Path.size() >
+ LV.Designator.Entries.size())
+ return 0;
+ unsigned PathLengthToMember =
+ LV.Designator.Entries.size() - MemPtr.Path.size();
+ for (unsigned I = 0, N = MemPtr.Path.size(); I != N; ++I) {
+ const CXXRecordDecl *LVDecl = getAsBaseClass(
+ LV.Designator.Entries[PathLengthToMember + I]);
+ const CXXRecordDecl *MPDecl = MemPtr.Path[I];
+ if (LVDecl->getCanonicalDecl() != MPDecl->getCanonicalDecl())
+ return 0;
+ }
+
+ // Truncate the lvalue to the appropriate derived class.
+ bool ResultIsArray = false;
+ if (PathLengthToMember == MostDerivedPathLength)
+ ResultIsArray = MostDerivedIsArrayElement;
+ TruncateLValueBasePath(Info, LV, MemPtr.getContainingRecord(),
+ PathLengthToMember, ResultIsArray);
+ } else if (!MemPtr.Path.empty()) {
+ // Extend the LValue path with the member pointer's path.
+ LV.Designator.Entries.reserve(LV.Designator.Entries.size() +
+ MemPtr.Path.size() + IncludeMember);
+
+ // Walk down to the appropriate base class.
+ QualType LVType = BO->getLHS()->getType();
+ if (const PointerType *PT = LVType->getAs<PointerType>())
+ LVType = PT->getPointeeType();
+ const CXXRecordDecl *RD = LVType->getAsCXXRecordDecl();
+ assert(RD && "member pointer access on non-class-type expression");
+ // The first class in the path is that of the lvalue.
+ for (unsigned I = 1, N = MemPtr.Path.size(); I != N; ++I) {
+ const CXXRecordDecl *Base = MemPtr.Path[N - I - 1];
+ HandleLValueDirectBase(Info, LV, RD, Base);
+ RD = Base;
+ }
+ // Finally cast to the class containing the member.
+ HandleLValueDirectBase(Info, LV, RD, MemPtr.getContainingRecord());
+ }
+
+ // Add the member. Note that we cannot build bound member functions here.
+ if (IncludeMember) {
+ // FIXME: Deal with IndirectFieldDecls.
+ const FieldDecl *FD = dyn_cast<FieldDecl>(MemPtr.getDecl());
+ if (!FD) return 0;
+ HandleLValueMember(Info, LV, FD);
+ }
+
+ return MemPtr.getDecl();
+}
+
+/// HandleBaseToDerivedCast - Apply the given base-to-derived cast operation on
+/// the provided lvalue, which currently refers to the base object.
+static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E,
+ LValue &Result) {
+ const CXXRecordDecl *MostDerivedType;
+ unsigned MostDerivedPathLength;
+ bool MostDerivedIsArrayElement;
+
+ // Check this cast doesn't take us outside the object.
+ if (!FindMostDerivedObject(Info, Result, MostDerivedType,
+ MostDerivedPathLength,
+ MostDerivedIsArrayElement))
+ return false;
+ SubobjectDesignator &D = Result.Designator;
+ if (MostDerivedPathLength + E->path_size() > D.Entries.size())
+ return false;
+
+ // Check the type of the final cast. We don't need to check the path,
+ // since a cast can only be formed if the path is unique.
+ unsigned NewEntriesSize = D.Entries.size() - E->path_size();
+ bool ResultIsArray = false;
+ QualType TargetQT = E->getType();
+ if (const PointerType *PT = TargetQT->getAs<PointerType>())
+ TargetQT = PT->getPointeeType();
+ const CXXRecordDecl *TargetType = TargetQT->getAsCXXRecordDecl();
+ const CXXRecordDecl *FinalType;
+ if (NewEntriesSize == MostDerivedPathLength) {
+ ResultIsArray = MostDerivedIsArrayElement;
+ FinalType = MostDerivedType;
+ } else
+ FinalType = getAsBaseClass(D.Entries[NewEntriesSize - 1]);
+ if (FinalType->getCanonicalDecl() != TargetType->getCanonicalDecl())
+ return false;
+
+ // Truncate the lvalue to the appropriate derived class.
+ TruncateLValueBasePath(Info, Result, TargetType, NewEntriesSize,
+ ResultIsArray);
+ return true;
}
namespace {
RetTy VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E)
{ return StmtVisitorTy::Visit(E->getExpr()); }
+ RetTy VisitBinaryOperator(const BinaryOperator *E) {
+ switch (E->getOpcode()) {
+ default:
+ return DerivedError(E);
+
+ case BO_Comma:
+ VisitIgnoredValue(E->getLHS());
+ return StmtVisitorTy::Visit(E->getRHS());
+
+ case BO_PtrMemD:
+ case BO_PtrMemI: {
+ LValue Obj;
+ if (!HandleMemberPointerAccess(Info, E, Obj))
+ return false;
+ CCValue Result;
+ if (!HandleLValueToRValueConversion(Info, E->getType(), Obj, Result))
+ return false;
+ return DerivedSuccess(Result, E);
+ }
+ }
+ }
+
RetTy VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
OpaqueValueEvaluation opaque(Info, E->getOpaqueValue(), E->getCommon());
if (opaque.hasError())
}
RetTy VisitCallExpr(const CallExpr *E) {
- const Expr *Callee = E->getCallee();
+ const Expr *Callee = E->getCallee()->IgnoreParens();
QualType CalleeType = Callee->getType();
const FunctionDecl *FD = 0;
// Extract function decl and 'this' pointer from the callee.
if (CalleeType->isSpecificBuiltinType(BuiltinType::BoundMember)) {
- // Explicit bound member calls, such as x.f() or p->g();
- // FIXME: Handle a BinaryOperator callee ('.*' or '->*').
- const MemberExpr *ME = dyn_cast<MemberExpr>(Callee->IgnoreParens());
- if (!ME)
+ if (const MemberExpr *ME = dyn_cast<MemberExpr>(Callee)) {
+ // Explicit bound member calls, such as x.f() or p->g();
+ if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal))
+ return DerivedError(ME->getBase());
+ This = &ThisVal;
+ FD = dyn_cast<FunctionDecl>(ME->getMemberDecl());
+ if (!FD)
+ return DerivedError(ME);
+ } else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) {
+ // Indirect bound member calls ('.*' or '->*').
+ const ValueDecl *Member = HandleMemberPointerAccess(Info, BE, ThisVal,
+ false);
+ This = &ThisVal;
+ FD = dyn_cast_or_null<FunctionDecl>(Member);
+ if (!FD)
+ return DerivedError(Callee);
+ } else
return DerivedError(Callee);
- if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal))
- return DerivedError(ME->getBase());
- This = &ThisVal;
- FD = dyn_cast<FunctionDecl>(ME->getMemberDecl());
- if (!FD)
- return DerivedError(ME);
} else if (CalleeType->isFunctionPointerType()) {
CCValue Call;
if (!Evaluate(Call, Info, Callee) || !Call.isLValue() ||
RetTy VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *E) {
return DerivedValueInitialization(E);
}
+ RetTy VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) {
+ return DerivedValueInitialization(E);
+ }
/// A member expression where the object is a prvalue is itself a prvalue.
RetTy VisitMemberExpr(const MemberExpr *E) {
}
+//===----------------------------------------------------------------------===//
+// Common base class for lvalue and temporary evaluation.
+//===----------------------------------------------------------------------===//
+namespace {
+template<class Derived>
+class LValueExprEvaluatorBase
+ : public ExprEvaluatorBase<Derived, bool> {
+protected:
+ LValue &Result;
+ typedef LValueExprEvaluatorBase LValueExprEvaluatorBaseTy;
+ typedef ExprEvaluatorBase<Derived, bool> ExprEvaluatorBaseTy;
+
+ bool Success(APValue::LValueBase B) {
+ Result.set(B);
+ return true;
+ }
+
+public:
+ LValueExprEvaluatorBase(EvalInfo &Info, LValue &Result) :
+ ExprEvaluatorBaseTy(Info), Result(Result) {}
+
+ bool Success(const CCValue &V, const Expr *E) {
+ Result.setFrom(V);
+ return true;
+ }
+ bool Error(const Expr *E) {
+ return false;
+ }
+
+ bool CheckValidLValue() {
+ // C++11 [basic.lval]p1: An lvalue designates a function or an object. Hence
+ // there are no null references, nor once-past-the-end references.
+ // FIXME: Check for one-past-the-end array indices
+ return Result.Base && !Result.Designator.Invalid &&
+ !Result.Designator.OnePastTheEnd;
+ }
+
+ bool VisitMemberExpr(const MemberExpr *E) {
+ // Handle non-static data members.
+ QualType BaseTy;
+ if (E->isArrow()) {
+ if (!EvaluatePointer(E->getBase(), Result, this->Info))
+ return false;
+ BaseTy = E->getBase()->getType()->getAs<PointerType>()->getPointeeType();
+ } else {
+ if (!this->Visit(E->getBase()))
+ return false;
+ BaseTy = E->getBase()->getType();
+ }
+ // FIXME: In C++11, require the result to be a valid lvalue.
+
+ const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl());
+ // FIXME: Handle IndirectFieldDecls
+ if (!FD) return false;
+ assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() ==
+ FD->getParent()->getCanonicalDecl() && "record / field mismatch");
+ (void)BaseTy;
+
+ HandleLValueMember(this->Info, Result, FD);
+
+ if (FD->getType()->isReferenceType()) {
+ CCValue RefValue;
+ if (!HandleLValueToRValueConversion(this->Info, FD->getType(), Result,
+ RefValue))
+ return false;
+ return Success(RefValue, E);
+ }
+ return true;
+ }
+
+ bool VisitBinaryOperator(const BinaryOperator *E) {
+ switch (E->getOpcode()) {
+ default:
+ return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
+
+ case BO_PtrMemD:
+ case BO_PtrMemI:
+ return HandleMemberPointerAccess(this->Info, E, Result);
+ }
+ }
+
+ bool VisitCastExpr(const CastExpr *E) {
+ switch (E->getCastKind()) {
+ default:
+ return ExprEvaluatorBaseTy::VisitCastExpr(E);
+
+ case CK_DerivedToBase:
+ case CK_UncheckedDerivedToBase: {
+ if (!this->Visit(E->getSubExpr()))
+ return false;
+ if (!CheckValidLValue())
+ return false;
+
+ // Now figure out the necessary offset to add to the base LV to get from
+ // the derived class to the base class.
+ QualType Type = E->getSubExpr()->getType();
+
+ for (CastExpr::path_const_iterator PathI = E->path_begin(),
+ PathE = E->path_end(); PathI != PathE; ++PathI) {
+ if (!HandleLValueBase(this->Info, Result, Type->getAsCXXRecordDecl(),
+ *PathI))
+ return false;
+ Type = (*PathI)->getType();
+ }
+
+ return true;
+ }
+ }
+ }
+};
+}
+
//===----------------------------------------------------------------------===//
// LValue Evaluation
//
//===----------------------------------------------------------------------===//
namespace {
class LValueExprEvaluator
- : public ExprEvaluatorBase<LValueExprEvaluator, bool> {
- LValue &Result;
- const Decl *PrevDecl;
-
- bool Success(APValue::LValueBase B) {
- Result.set(B);
- return true;
- }
+ : public LValueExprEvaluatorBase<LValueExprEvaluator> {
public:
+ LValueExprEvaluator(EvalInfo &Info, LValue &Result) :
+ LValueExprEvaluatorBaseTy(Info, Result) {}
- LValueExprEvaluator(EvalInfo &info, LValue &Result) :
- ExprEvaluatorBaseTy(info), Result(Result), PrevDecl(0) {}
-
- bool Success(const CCValue &V, const Expr *E) {
- Result.setFrom(V);
- return true;
- }
- bool Error(const Expr *E) {
- return false;
- }
-
bool VisitVarDecl(const Expr *E, const VarDecl *VD);
bool VisitDeclRefExpr(const DeclRefExpr *E);
bool VisitCastExpr(const CastExpr *E) {
switch (E->getCastKind()) {
default:
- return ExprEvaluatorBaseTy::VisitCastExpr(E);
+ return LValueExprEvaluatorBaseTy::VisitCastExpr(E);
case CK_LValueBitCast:
if (!Visit(E->getSubExpr()))
Result.Designator.setInvalid();
return true;
- case CK_DerivedToBase:
- case CK_UncheckedDerivedToBase: {
+ case CK_BaseToDerived:
if (!Visit(E->getSubExpr()))
return false;
-
- // Now figure out the necessary offset to add to the base LV to get from
- // the derived class to the base class.
- QualType Type = E->getSubExpr()->getType();
-
- for (CastExpr::path_const_iterator PathI = E->path_begin(),
- PathE = E->path_end(); PathI != PathE; ++PathI) {
- if (!HandleLValueBase(Info, Result, Type->getAsCXXRecordDecl(), *PathI))
- return false;
- Type = (*PathI)->getType();
- }
-
- return true;
- }
+ if (!CheckValidLValue())
+ return false;
+ return HandleBaseToDerivedCast(Info, E, Result);
}
}
bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *E) {
+ if (E->GetTemporaryExpr()->isRValue()) {
+ if (E->getType()->isRecordType() && E->getType()->isLiteralType())
+ return EvaluateTemporary(E->GetTemporaryExpr(), Result, Info);
+
+ Result.set(E, Info.CurrentCall);
+ return EvaluateConstantExpression(Info.CurrentCall->Temporaries[E], Info,
+ Result, E->GetTemporaryExpr());
+ }
+
+ // Materialization of an lvalue temporary occurs when we need to force a copy
+ // (for instance, if it's a bitfield).
+ // FIXME: The AST should contain an lvalue-to-rvalue node for such cases.
+ if (!Visit(E->GetTemporaryExpr()))
+ return false;
+ if (!HandleLValueToRValueConversion(Info, E->getType(), Result,
+ Info.CurrentCall->Temporaries[E]))
+ return false;
Result.set(E, Info.CurrentCall);
- return EvaluateConstantExpression(Info.CurrentCall->Temporaries[E], Info,
- Result, E->GetTemporaryExpr());
+ return true;
}
bool
}
// Handle non-static data members.
- QualType BaseTy;
- if (E->isArrow()) {
- if (!EvaluatePointer(E->getBase(), Result, Info))
- return false;
- BaseTy = E->getBase()->getType()->getAs<PointerType>()->getPointeeType();
- } else {
- if (!Visit(E->getBase()))
- return false;
- BaseTy = E->getBase()->getType();
- }
-
- const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl());
- if (!FD) return false;
- assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() ==
- FD->getParent()->getCanonicalDecl() && "record / field mismatch");
- (void)BaseTy;
-
- HandleLValueMember(Info, Result, FD);
-
- if (FD->getType()->isReferenceType()) {
- CCValue RefValue;
- if (!HandleLValueToRValueConversion(Info, FD->getType(), Result, RefValue))
- return false;
- return Success(RefValue, E);
- }
- return true;
+ return LValueExprEvaluatorBaseTy::VisitMemberExpr(E);
}
bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
= Index.isSigned() ? Index.getSExtValue()
: static_cast<int64_t>(Index.getZExtValue());
+ // FIXME: In C++11, require the result to be a valid lvalue.
return HandleLValueArrayAdjustment(Info, Result, E->getType(), IndexValue);
}
bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) {
+ // FIXME: In C++11, require the result to be a valid lvalue.
return EvaluatePointer(E->getSubExpr(), Result, Info);
}
return Success(E);
return false;
}
- bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E)
- { return ValueInitialization(E); }
bool VisitCXXThisExpr(const CXXThisExpr *E) {
if (!Info.CurrentCall->This)
return false;
bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
if (E->getOpcode() != BO_Add &&
E->getOpcode() != BO_Sub)
- return false;
+ return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
const Expr *PExp = E->getLHS();
const Expr *IExp = E->getRHS();
AdditionalOffset = -AdditionalOffset;
QualType Pointee = PExp->getType()->getAs<PointerType>()->getPointeeType();
+ // FIXME: In C++11, require the result to be a valid lvalue.
return HandleLValueArrayAdjustment(Info, Result, Pointee, AdditionalOffset);
}
case CK_UncheckedDerivedToBase: {
if (!EvaluatePointer(E->getSubExpr(), Result, Info))
return false;
+ if (!Result.Base && Result.Offset.isZero())
+ return true;
// Now figure out the necessary offset to add to the base LV to get from
// the derived class to the base class.
return true;
}
+ case CK_BaseToDerived:
+ if (!Visit(E->getSubExpr()))
+ return false;
+ if (!Result.Base && Result.Offset.isZero())
+ return true;
+ return HandleBaseToDerivedCast(Info, E, Result);
+
case CK_NullToPointer:
return ValueInitialization(E);
}
}
case CK_ArrayToPointerDecay:
- // FIXME: Support array-to-pointer decay on array rvalues.
- if (!SubExpr->isGLValue())
- return Error(E);
- if (!EvaluateLValue(SubExpr, Result, Info))
- return false;
+ if (SubExpr->isGLValue()) {
+ if (!EvaluateLValue(SubExpr, Result, Info))
+ return false;
+ } else {
+ Result.set(SubExpr, Info.CurrentCall);
+ if (!EvaluateConstantExpression(Info.CurrentCall->Temporaries[SubExpr],
+ Info, Result, SubExpr))
+ return false;
+ }
// The result is a pointer to the first element of the array.
Result.Designator.addIndex(0);
return true;
return ExprEvaluatorBaseTy::VisitCallExpr(E);
}
+//===----------------------------------------------------------------------===//
+// Member Pointer Evaluation
+//===----------------------------------------------------------------------===//
+
+namespace {
+class MemberPointerExprEvaluator
+ : public ExprEvaluatorBase<MemberPointerExprEvaluator, bool> {
+ MemberPtr &Result;
+
+ bool Success(const ValueDecl *D) {
+ Result = MemberPtr(D);
+ return true;
+ }
+public:
+
+ MemberPointerExprEvaluator(EvalInfo &Info, MemberPtr &Result)
+ : ExprEvaluatorBaseTy(Info), Result(Result) {}
+
+ bool Success(const CCValue &V, const Expr *E) {
+ Result.setFrom(V);
+ return true;
+ }
+ bool Error(const Stmt *S) {
+ return false;
+ }
+ bool ValueInitialization(const Expr *E) {
+ return Success((const ValueDecl*)0);
+ }
+
+ bool VisitCastExpr(const CastExpr *E);
+ bool VisitUnaryAddrOf(const UnaryOperator *E);
+};
+} // end anonymous namespace
+
+static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result,
+ EvalInfo &Info) {
+ assert(E->isRValue() && E->getType()->isMemberPointerType());
+ return MemberPointerExprEvaluator(Info, Result).Visit(E);
+}
+
+bool MemberPointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
+ switch (E->getCastKind()) {
+ default:
+ return ExprEvaluatorBaseTy::VisitCastExpr(E);
+
+ case CK_NullToMemberPointer:
+ return ValueInitialization(E);
+
+ case CK_BaseToDerivedMemberPointer: {
+ if (!Visit(E->getSubExpr()))
+ return false;
+ if (E->path_empty())
+ return true;
+ // Base-to-derived member pointer casts store the path in derived-to-base
+ // order, so iterate backwards. The CXXBaseSpecifier also provides us with
+ // the wrong end of the derived->base arc, so stagger the path by one class.
+ typedef std::reverse_iterator<CastExpr::path_const_iterator> ReverseIter;
+ for (ReverseIter PathI(E->path_end() - 1), PathE(E->path_begin());
+ PathI != PathE; ++PathI) {
+ assert(!(*PathI)->isVirtual() && "memptr cast through vbase");
+ const CXXRecordDecl *Derived = (*PathI)->getType()->getAsCXXRecordDecl();
+ if (!Result.castToDerived(Derived))
+ return false;
+ }
+ const Type *FinalTy = E->getType()->castAs<MemberPointerType>()->getClass();
+ if (!Result.castToDerived(FinalTy->getAsCXXRecordDecl()))
+ return false;
+ return true;
+ }
+
+ case CK_DerivedToBaseMemberPointer:
+ if (!Visit(E->getSubExpr()))
+ return false;
+ for (CastExpr::path_const_iterator PathI = E->path_begin(),
+ PathE = E->path_end(); PathI != PathE; ++PathI) {
+ assert(!(*PathI)->isVirtual() && "memptr cast through vbase");
+ const CXXRecordDecl *Base = (*PathI)->getType()->getAsCXXRecordDecl();
+ if (!Result.castToBase(Base))
+ return false;
+ }
+ return true;
+ }
+}
+
+bool MemberPointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) {
+ // C++11 [expr.unary.op]p3 has very strict rules on how the address of a
+ // member can be formed.
+ return Success(cast<DeclRefExpr>(E->getSubExpr())->getDecl());
+}
+
//===----------------------------------------------------------------------===//
// Record Evaluation
//===----------------------------------------------------------------------===//
return RecordExprEvaluator(Info, This, Result).Visit(E);
}
+//===----------------------------------------------------------------------===//
+// Temporary Evaluation
+//
+// Temporaries are represented in the AST as rvalues, but generally behave like
+// lvalues. The full-object of which the temporary is a subobject is implicitly
+// materialized so that a reference can bind to it.
+//===----------------------------------------------------------------------===//
+namespace {
+class TemporaryExprEvaluator
+ : public LValueExprEvaluatorBase<TemporaryExprEvaluator> {
+public:
+ TemporaryExprEvaluator(EvalInfo &Info, LValue &Result) :
+ LValueExprEvaluatorBaseTy(Info, Result) {}
+
+ /// Visit an expression which constructs the value of this temporary.
+ bool VisitConstructExpr(const Expr *E) {
+ Result.set(E, Info.CurrentCall);
+ return EvaluateConstantExpression(Info.CurrentCall->Temporaries[E], Info,
+ Result, E);
+ }
+
+ bool VisitCastExpr(const CastExpr *E) {
+ switch (E->getCastKind()) {
+ default:
+ return LValueExprEvaluatorBaseTy::VisitCastExpr(E);
+
+ case CK_ConstructorConversion:
+ return VisitConstructExpr(E->getSubExpr());
+ }
+ }
+ bool VisitInitListExpr(const InitListExpr *E) {
+ return VisitConstructExpr(E);
+ }
+ bool VisitCXXConstructExpr(const CXXConstructExpr *E) {
+ return VisitConstructExpr(E);
+ }
+ bool VisitCallExpr(const CallExpr *E) {
+ return VisitConstructExpr(E);
+ }
+};
+} // end anonymous namespace
+
+/// Evaluate an expression of record type as a temporary.
+static bool EvaluateTemporary(const Expr *E, LValue &Result, EvalInfo &Info) {
+ assert(E->isRValue() && E->getType()->isRecordType() &&
+ E->getType()->isLiteralType());
+ return TemporaryExprEvaluator(Info, Result).Visit(E);
+}
+
//===----------------------------------------------------------------------===//
// Vector Evaluation
//===----------------------------------------------------------------------===//
Subobject, &VIE);
}
- // FIXME: We also get CXXConstructExpr, in cases like:
- // struct S { constexpr S(); }; constexpr S s[10];
bool VisitInitListExpr(const InitListExpr *E);
+ bool VisitCXXConstructExpr(const CXXConstructExpr *E);
};
} // end anonymous namespace
Subobject, E->getArrayFiller());
}
+bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
+ const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType());
+ if (!CAT)
+ return false;
+
+ Result = APValue(APValue::UninitArray(), 0, CAT->getSize().getZExtValue());
+ if (!Result.hasArrayFiller())
+ return true;
+
+ const CXXConstructorDecl *FD = E->getConstructor();
+ const FunctionDecl *Definition = 0;
+ FD->getBody(Definition);
+
+ if (!Definition || !Definition->isConstexpr() || Definition->isInvalidDecl())
+ return false;
+
+ // FIXME: The Subobject here isn't necessarily right. This rarely matters,
+ // but sometimes does:
+ // struct S { constexpr S() : p(&p) {} void *p; };
+ // S s[10];
+ LValue Subobject = This;
+ Subobject.Designator.addIndex(0);
+ llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs());
+ return HandleConstructorCall(Subobject, Args,
+ cast<CXXConstructorDecl>(Definition),
+ Info, Result.getArrayFiller());
+}
+
//===----------------------------------------------------------------------===//
// Integer Evaluation
//
}
if (!LHSTy->isIntegralOrEnumerationType() ||
!RHSTy->isIntegralOrEnumerationType()) {
- // We can't continue from here for non-integral types, and they
- // could potentially confuse the following operations.
- return false;
+ // We can't continue from here for non-integral types.
+ return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
}
// The LHS of a constant expr is always evaluated and needed.
}
bool FloatExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
- if (E->getOpcode() == BO_Comma) {
- VisitIgnoredValue(E->getLHS());
- return Visit(E->getRHS());
- }
-
- // We can't evaluate pointer-to-member operations or assignments.
- if (E->isPtrMemOp() || E->isAssignmentOp())
- return false;
+ if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma)
+ return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
- // FIXME: Diagnostics? I really don't understand how the warnings
- // and errors are supposed to work.
APFloat RHS(0.0);
if (!EvaluateFloat(E->getLHS(), Result, Info))
return false;
}
bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
- if (E->isPtrMemOp() || E->isAssignmentOp())
+ if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma)
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
- if (E->getOpcode() == BO_Comma) {
- VisitIgnoredValue(E->getLHS());
- return Visit(E->getRHS());
- }
-
if (!Visit(E->getLHS()))
return false;
return false;
C.moveInto(Result);
} else if (E->getType()->isMemberPointerType()) {
- // FIXME: Implement evaluation of pointer-to-member types.
- return false;
+ MemberPtr P;
+ if (!EvaluateMemberPointer(E, P, Info))
+ return false;
+ P.moveInto(Result);
+ return true;
} else if (E->getType()->isArrayType() && E->getType()->isLiteralType()) {
LValue LV;
LV.set(E, Info.CurrentCall);
// FIXME: Evaluating initializers for large arrays can cause performance
// problems, and we don't use such values yet. Once we have a more efficient
// array representation, this should be reinstated, and used by CodeGen.
- if (isRValue() && getType()->isArrayType())
+ // The same problem affects large records.
+ if (isRValue() && (getType()->isArrayType() || getType()->isRecordType()) &&
+ !Ctx.getLangOptions().CPlusPlus0x)
return false;
EvalInfo Info(Ctx, Result);
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}}
}
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;
}
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 {
}
-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,
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 {
}
+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 {