// The order of this enum is important for diagnostics.
enum CheckSubobjectKind {
CSK_Base, CSK_Derived, CSK_Field, CSK_ArrayToPointer, CSK_ArrayIndex,
- CSK_Real, CSK_Imag
+ CSK_This, CSK_Real, CSK_Imag
};
/// A path from a glvalue to a subobject of that glvalue.
}
};
- /// A reference to an object whose construction we are currently evaluating.
- struct ObjectUnderConstruction {
- APValue::LValueBase Base;
- ArrayRef<APValue::LValuePathEntry> Path;
- friend bool operator==(const ObjectUnderConstruction &LHS,
- const ObjectUnderConstruction &RHS) {
- return LHS.Base == RHS.Base && LHS.Path == RHS.Path;
- }
- friend llvm::hash_code hash_value(const ObjectUnderConstruction &Obj) {
- return llvm::hash_combine(Obj.Base, Obj.Path);
- }
- };
- enum class ConstructionPhase { None, Bases, AfterBases };
-}
-
-namespace llvm {
-template<> struct DenseMapInfo<ObjectUnderConstruction> {
- using Base = DenseMapInfo<APValue::LValueBase>;
- static ObjectUnderConstruction getEmptyKey() {
- return {Base::getEmptyKey(), {}}; }
- static ObjectUnderConstruction getTombstoneKey() {
- return {Base::getTombstoneKey(), {}};
- }
- static unsigned getHashValue(const ObjectUnderConstruction &Object) {
- return hash_value(Object);
- }
- static bool isEqual(const ObjectUnderConstruction &LHS,
- const ObjectUnderConstruction &RHS) {
- return LHS == RHS;
- }
-};
-}
-
-namespace {
/// EvalInfo - This is a private struct used by the evaluator to capture
/// information about a subexpression as it is folded. It retains information
/// about the AST context, but also maintains information about the folded
/// declaration whose initializer is being evaluated, if any.
APValue *EvaluatingDeclValue;
- /// Set of objects that are currently being constructed.
- llvm::DenseMap<ObjectUnderConstruction, ConstructionPhase>
- ObjectsUnderConstruction;
+ /// EvaluatingObject - Pair of the AST node that an lvalue represents and
+ /// the call index that that lvalue was allocated in.
+ typedef std::pair<APValue::LValueBase, std::pair<unsigned, unsigned>>
+ EvaluatingObject;
+
+ /// EvaluatingConstructors - Set of objects that are currently being
+ /// constructed.
+ llvm::DenseSet<EvaluatingObject> EvaluatingConstructors;
struct EvaluatingConstructorRAII {
EvalInfo &EI;
- ObjectUnderConstruction Object;
+ EvaluatingObject Object;
bool DidInsert;
- EvaluatingConstructorRAII(EvalInfo &EI, ObjectUnderConstruction Object,
- bool HasBases)
+ EvaluatingConstructorRAII(EvalInfo &EI, EvaluatingObject Object)
: EI(EI), Object(Object) {
- DidInsert =
- EI.ObjectsUnderConstruction
- .insert({Object, HasBases ? ConstructionPhase::Bases
- : ConstructionPhase::AfterBases})
- .second;
- }
- void finishedConstructingBases() {
- EI.ObjectsUnderConstruction[Object] = ConstructionPhase::AfterBases;
+ DidInsert = EI.EvaluatingConstructors.insert(Object).second;
}
~EvaluatingConstructorRAII() {
- if (DidInsert) EI.ObjectsUnderConstruction.erase(Object);
+ if (DidInsert) EI.EvaluatingConstructors.erase(Object);
}
};
- ConstructionPhase
- isEvaluatingConstructor(APValue::LValueBase Base,
- ArrayRef<APValue::LValuePathEntry> Path) {
- return ObjectsUnderConstruction.lookup({Base, Path});
+ bool isEvaluatingConstructor(APValue::LValueBase Decl, unsigned CallIndex,
+ unsigned Version) {
+ return EvaluatingConstructors.count(
+ EvaluatingObject(Decl, {CallIndex, Version}));
}
/// If we're currently speculatively evaluating, the outermost call stack
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
EvaluatingDecl = Base;
EvaluatingDeclValue = &Value;
+ EvaluatingConstructors.insert({Base, {0, 0}});
}
const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
}
}
-/// Kinds of access we can perform on an object, for diagnostics. Note that
-/// we consider a member function call to be a kind of access, even though
-/// it is not formally an access of the object, because it has (largely) the
-/// same set of semantic restrictions.
+/// Kinds of access we can perform on an object, for diagnostics.
enum AccessKinds {
AK_Read,
AK_Assign,
AK_Increment,
- AK_Decrement,
- AK_MemberCall,
+ AK_Decrement
};
-static bool isModification(AccessKinds AK) {
- return AK != AK_Read && AK != AK_MemberCall;
-}
-
namespace {
struct ComplexValue {
private:
return false;
}
-static bool lifetimeStartedInEvaluation(EvalInfo &Info,
- APValue::LValueBase Base) {
- // A temporary we created.
- if (Base.getCallIndex())
- return true;
-
- auto *Evaluating = Info.EvaluatingDecl.dyn_cast<const ValueDecl*>();
- if (!Evaluating)
- return false;
-
- // The variable whose initializer we're evaluating.
- if (auto *BaseD = Base.dyn_cast<const ValueDecl*>())
- if (declaresSameEntity(Evaluating, BaseD))
- return true;
-
- // A temporary lifetime-extended by the variable whose initializer we're
- // evaluating.
- if (auto *BaseE = Base.dyn_cast<const Expr *>())
- if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))
- if (declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating))
- return true;
-
- return false;
-}
-
namespace {
/// A handle to a complete object (an object that is not a subobject of
/// another object).
struct CompleteObject {
- /// The identity of the object.
- APValue::LValueBase Base;
/// The value of the complete object.
APValue *Value;
/// The type of the complete object.
QualType Type;
+ bool LifetimeStartedInEvaluation;
CompleteObject() : Value(nullptr) {}
- CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type)
- : Base(Base), Value(Value), Type(Type) {}
-
- bool mayReadMutableMembers(EvalInfo &Info) const {
- // In C++14 onwards, it is permitted to read a mutable member whose
- // lifetime began within the evaluation.
- // FIXME: Should we also allow this in C++11?
- if (!Info.getLangOpts().CPlusPlus14)
- return false;
- return lifetimeStartedInEvaluation(Info, Base);
+ CompleteObject(APValue *Value, QualType Type,
+ bool LifetimeStartedInEvaluation)
+ : Value(Value), Type(Type),
+ LifetimeStartedInEvaluation(LifetimeStartedInEvaluation) {
+ assert(Value && "missing value for complete object");
}
- explicit operator bool() const { return !Type.isNull(); }
+ explicit operator bool() const { return Value; }
};
} // end anonymous namespace
-static QualType getSubobjectType(QualType ObjType, QualType SubobjType,
- bool IsMutable = false) {
- // C++ [basic.type.qualifier]p1:
- // - A const object is an object of type const T or a non-mutable subobject
- // of a const object.
- if (ObjType.isConstQualified() && !IsMutable)
- SubobjType.addConst();
- // - A volatile object is an object of type const T or a subobject of a
- // volatile object.
- if (ObjType.isVolatileQualified())
- SubobjType.addVolatile();
- return SubobjType;
-}
-
/// Find the designated sub-object of an rvalue.
template<typename SubobjectHandler>
typename SubobjectHandler::result_type
APValue *O = Obj.Value;
QualType ObjType = Obj.Type;
const FieldDecl *LastField = nullptr;
- const FieldDecl *VolatileField = nullptr;
+ const bool MayReadMutableMembers =
+ Obj.LifetimeStartedInEvaluation && Info.getLangOpts().CPlusPlus14;
// Walk the designator's path to find the subobject.
for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
if (O->isUninit()) {
if (!Info.checkingPotentialConstantExpression())
- Info.FFDiag(E, diag::note_constexpr_access_uninit)
- << handler.AccessKind;
+ Info.FFDiag(E, diag::note_constexpr_access_uninit) << handler.AccessKind;
return handler.failed();
}
- // C++ [class.ctor]p5:
- // const and volatile semantics are not applied on an object under
- // construction.
- if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) &&
- ObjType->isRecordType() &&
- Info.isEvaluatingConstructor(
- Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(),
- Sub.Entries.begin() + I)) !=
- ConstructionPhase::None) {
- ObjType = Info.Ctx.getCanonicalType(ObjType);
- ObjType.removeLocalConst();
- ObjType.removeLocalVolatile();
- }
-
- // If this is our last pass, check that the final object type is OK.
- if (I == N || (I == N - 1 && ObjType->isAnyComplexType())) {
- // Accesses to volatile objects are prohibited.
- if (ObjType.isVolatileQualified() &&
- handler.AccessKind != AK_MemberCall) {
- if (Info.getLangOpts().CPlusPlus) {
- int DiagKind;
- SourceLocation Loc;
- const NamedDecl *Decl = nullptr;
- if (VolatileField) {
- DiagKind = 2;
- Loc = VolatileField->getLocation();
- Decl = VolatileField;
- } else if (auto *VD = Obj.Base.dyn_cast<const ValueDecl*>()) {
- DiagKind = 1;
- Loc = VD->getLocation();
- Decl = VD;
- } else {
- DiagKind = 0;
- if (auto *E = Obj.Base.dyn_cast<const Expr *>())
- Loc = E->getExprLoc();
- }
- Info.FFDiag(E, diag::note_constexpr_access_volatile_obj, 1)
- << handler.AccessKind << DiagKind << Decl;
- Info.Note(Loc, diag::note_constexpr_volatile_here) << DiagKind;
- } else {
- Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
- }
- return handler.failed();
- }
-
+ if (I == N) {
// If we are reading an object of class type, there may still be more
// things we need to check: if there are any mutable subobjects, we
// cannot perform this read. (This only happens when performing a trivial
// copy or assignment.)
if (ObjType->isRecordType() && handler.AccessKind == AK_Read &&
- !Obj.mayReadMutableMembers(Info) &&
- diagnoseUnreadableFields(Info, E, ObjType))
+ !MayReadMutableMembers && diagnoseUnreadableFields(Info, E, ObjType))
return handler.failed();
- }
- if (I == N) {
if (!handler.found(*O, ObjType))
return false;
// If we modified a bit-field, truncate it to the right width.
- if (isModification(handler.AccessKind) &&
+ if (handler.AccessKind != AK_Read &&
LastField && LastField->isBitField() &&
!truncateBitfieldValue(Info, E, *O, LastField))
return false;
return handler.failed();
}
- ObjType = getSubobjectType(
- ObjType, ObjType->castAs<ComplexType>()->getElementType());
+ bool WasConstQualified = ObjType.isConstQualified();
+ ObjType = ObjType->castAs<ComplexType>()->getElementType();
+ if (WasConstQualified)
+ ObjType.addConst();
assert(I == N - 1 && "extracting subobject of scalar?");
if (O->isComplexInt()) {
: O->getComplexFloatReal(), ObjType);
}
} else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
+ // In C++14 onwards, it is permitted to read a mutable member whose
+ // lifetime began within the evaluation.
+ // FIXME: Should we also allow this in C++11?
if (Field->isMutable() && handler.AccessKind == AK_Read &&
- !Obj.mayReadMutableMembers(Info)) {
+ !MayReadMutableMembers) {
Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1)
<< Field;
Info.Note(Field->getLocation(), diag::note_declared_at);
} else
O = &O->getStructField(Field->getFieldIndex());
- ObjType = getSubobjectType(ObjType, Field->getType(), Field->isMutable());
+ bool WasConstQualified = ObjType.isConstQualified();
+ ObjType = Field->getType();
+ if (WasConstQualified && !Field->isMutable())
+ ObjType.addConst();
+
+ if (ObjType.isVolatileQualified()) {
+ if (Info.getLangOpts().CPlusPlus) {
+ // FIXME: Include a description of the path to the volatile subobject.
+ Info.FFDiag(E, diag::note_constexpr_access_volatile_obj, 1)
+ << handler.AccessKind << 2 << Field;
+ Info.Note(Field->getLocation(), diag::note_declared_at);
+ } else {
+ Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
+ }
+ return handler.failed();
+ }
+
LastField = Field;
- if (Field->getType().isVolatileQualified())
- VolatileField = Field;
} else {
// Next subobject is a base class.
const CXXRecordDecl *Derived = ObjType->getAsCXXRecordDecl();
const CXXRecordDecl *Base = getAsBaseClass(Sub.Entries[I]);
O = &O->getStructBase(getBaseIndex(Derived, Base));
- ObjType = getSubobjectType(ObjType, Info.Ctx.getRecordType(Base));
+ bool WasConstQualified = ObjType.isConstQualified();
+ ObjType = Info.Ctx.getRecordType(Base);
+ if (WasConstQualified)
+ ObjType.addConst();
}
}
}
// is not a constant expression (even if the object is non-volatile). We also
// apply this rule to C++98, in order to conform to the expected 'volatile'
// semantics.
- if (AK != AK_MemberCall && LValType.isVolatileQualified()) {
+ if (LValType.isVolatileQualified()) {
if (Info.getLangOpts().CPlusPlus)
Info.FFDiag(E, diag::note_constexpr_access_volatile_type)
<< AK << LValType;
return CompleteObject();
}
- // The wording is unclear on this, but for the purpose of determining the
- // validity of a member function call, we assume that all objects whose
- // lifetimes did not start within the constant evaluation are in fact within
- // their lifetimes, so member calls on them are valid. (This simultaneously
- // includes all members of a union!)
- bool NeedValue = AK != AK_MemberCall;
-
// Compute value storage location and type of base object.
APValue *BaseVal = nullptr;
QualType BaseType = getType(LVal.Base);
+ bool LifetimeStartedInEvaluation = Frame;
if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl*>()) {
// In C++98, const, non-volatile integers initialized with ICEs are ICEs.
return CompleteObject();
}
+ // Accesses of volatile-qualified objects are not allowed.
+ if (BaseType.isVolatileQualified()) {
+ if (Info.getLangOpts().CPlusPlus) {
+ Info.FFDiag(E, diag::note_constexpr_access_volatile_obj, 1)
+ << AK << 1 << VD;
+ Info.Note(VD->getLocation(), diag::note_declared_at);
+ } else {
+ Info.FFDiag(E);
+ }
+ return CompleteObject();
+ }
+
// Unless we're looking at a local variable or argument in a constexpr call,
// the variable we're reading must be const.
if (!Frame) {
if (Info.getLangOpts().CPlusPlus14 &&
- declaresSameEntity(
- VD, Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) {
+ VD == Info.EvaluatingDecl.dyn_cast<const ValueDecl *>()) {
// OK, we can read and modify an object if we're in the process of
// evaluating its initializer, because its lifetime began in this
// evaluation.
- } else if (isModification(AK)) {
- // All the remaining cases do not permit modification of the object.
+ } else if (AK != AK_Read) {
+ // All the remaining cases only permit reading.
Info.FFDiag(E, diag::note_constexpr_modify_global);
return CompleteObject();
} else if (VD->isConstexpr()) {
// OK, we can read this variable.
} else if (BaseType->isIntegralOrEnumerationType()) {
- // In OpenCL if a variable is in constant address space it is a const
- // value.
+ // In OpenCL if a variable is in constant address space it is a const value.
if (!(BaseType.isConstQualified() ||
(Info.getLangOpts().OpenCL &&
BaseType.getAddressSpace() == LangAS::opencl_constant))) {
- if (!NeedValue)
- return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
if (Info.getLangOpts().CPlusPlus) {
Info.FFDiag(E, diag::note_constexpr_ltor_non_const_int, 1) << VD;
Info.Note(VD->getLocation(), diag::note_declared_at);
}
return CompleteObject();
}
- } else if (!NeedValue) {
- return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
} else if (BaseType->isFloatingType() && BaseType.isConstQualified()) {
// We support folding of const floating-point types, in order to make
// static const data members of such types (supported as an extension)
if (!(BaseType.isConstQualified() &&
BaseType->isIntegralOrEnumerationType()) &&
!(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) {
- if (!NeedValue)
- return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
Info.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
Info.Note(MTE->getExprLoc(), diag::note_constexpr_temporary_here);
return CompleteObject();
BaseVal = Info.Ctx.getMaterializedTemporaryValue(MTE, false);
assert(BaseVal && "got reference to unevaluated temporary");
+ LifetimeStartedInEvaluation = true;
} else {
- if (!NeedValue)
- return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
Info.FFDiag(E);
return CompleteObject();
}
BaseVal = Frame->getTemporary(Base, LVal.Base.getVersion());
assert(BaseVal && "missing value for temporary");
}
+
+ // Volatile temporary objects cannot be accessed in constant expressions.
+ if (BaseType.isVolatileQualified()) {
+ if (Info.getLangOpts().CPlusPlus) {
+ Info.FFDiag(E, diag::note_constexpr_access_volatile_obj, 1)
+ << AK << 0;
+ Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here);
+ } else {
+ Info.FFDiag(E);
+ }
+ return CompleteObject();
+ }
+ }
+
+ // During the construction of an object, it is not yet 'const'.
+ // FIXME: This doesn't do quite the right thing for const subobjects of the
+ // object under construction.
+ if (Info.isEvaluatingConstructor(LVal.getLValueBase(),
+ LVal.getLValueCallIndex(),
+ LVal.getLValueVersion())) {
+ BaseType = Info.Ctx.getCanonicalType(BaseType);
+ BaseType.removeLocalConst();
+ LifetimeStartedInEvaluation = true;
}
// In C++14, we can't safely access any mutable state when we might be
// to be read here (but take care with 'mutable' fields).
if ((Frame && Info.getLangOpts().CPlusPlus14 &&
Info.EvalStatus.HasSideEffects) ||
- (isModification(AK) && Depth < Info.SpeculativeEvaluationDepth))
+ (AK != AK_Read && Depth < Info.SpeculativeEvaluationDepth))
return CompleteObject();
- return CompleteObject(LVal.getLValueBase(), BaseVal, BaseType);
+ return CompleteObject(BaseVal, BaseType, LifetimeStartedInEvaluation);
}
/// Perform an lvalue-to-rvalue conversion on the given glvalue. This
APValue Lit;
if (!Evaluate(Lit, Info, CLE->getInitializer()))
return false;
- CompleteObject LitObj(LVal.Base, &Lit, Base->getType());
+ CompleteObject LitObj(&Lit, Base->getType(), false);
return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal);
} else if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) {
// Special-case character extraction so we don't have to construct an
return false;
}
-namespace {
-struct CheckMemberCallThisPointerHandler {
- static const AccessKinds AccessKind = AK_MemberCall;
- typedef bool result_type;
- bool failed() { return false; }
- bool found(APValue &Subobj, QualType SubobjType) { return true; }
- bool found(APSInt &Value, QualType SubobjType) { return true; }
- bool found(APFloat &Value, QualType SubobjType) { return true; }
-};
-} // end anonymous namespace
-
-const AccessKinds CheckMemberCallThisPointerHandler::AccessKind;
-
-/// Check that the pointee of the 'this' pointer in a member function call is
-/// either within its lifetime or in its period of construction or destruction.
-static bool checkMemberCallThisPointer(EvalInfo &Info, const Expr *E,
- const LValue &This) {
- CompleteObject Obj =
- findCompleteObject(Info, E, AK_MemberCall, This, QualType());
-
- if (!Obj)
- return false;
-
- if (!Obj.Value) {
- // The object is not usable in constant expressions, so we can't inspect
- // its value to see if it's in-lifetime or what the active union members
- // are. We can still check for a one-past-the-end lvalue.
- if (This.Designator.isOnePastTheEnd() ||
- This.Designator.isMostDerivedAnUnsizedArray()) {
- Info.FFDiag(E, This.Designator.isOnePastTheEnd()
- ? diag::note_constexpr_access_past_end
- : diag::note_constexpr_access_unsized_array)
- << AK_MemberCall;
- return false;
- }
- return true;
- }
-
- CheckMemberCallThisPointerHandler Handler;
- return Obj && findSubobject(Info, E, Obj, This.Designator, Handler);
-}
-
/// Determine if a class has any fields that might need to be copied by a
/// trivial copy or move operation.
static bool hasFields(const CXXRecordDecl *RD) {
}
EvalInfo::EvaluatingConstructorRAII EvalObj(
- Info,
- ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries},
- RD->getNumBases());
+ Info, {This.getLValueBase(),
+ {This.getLValueCallIndex(), This.getLValueVersion()}});
CallStackFrame Frame(Info, CallLoc, Definition, &This, ArgValues);
// FIXME: Creating an APValue just to hold a nonexistent return value is
BaseType->getAsCXXRecordDecl(), &Layout))
return false;
Value = &Result.getStructBase(BasesSeen++);
-
- // This is the point at which the dynamic type of the object becomes this
- // class type.
- if (BasesSeen == RD->getNumBases())
- EvalObj.finishedConstructingBases();
} else if ((FD = I->getMember())) {
if (!HandleLValueMember(Info, I->getInit(), Subobject, FD, &Layout))
return false;
} else
return Error(E);
- if (This && !checkMemberCallThisPointer(Info, E, *This))
+ if (This && !This->checkSubobject(Info, E, CSK_This))
return false;
const FunctionDecl *Definition = nullptr;
/// A member expression where the object is a prvalue is itself a prvalue.
bool VisitMemberExpr(const MemberExpr *E) {
- assert(!Info.Ctx.getLangOpts().CPlusPlus11 &&
- "missing temporary materialization conversion");
assert(!E->isArrow() && "missing call to bound member function?");
APValue Val;
assert(BaseTy->castAs<RecordType>()->getDecl()->getCanonicalDecl() ==
FD->getParent()->getCanonicalDecl() && "record / field mismatch");
- // Note: there is no lvalue base here. But this case should only ever
- // happen in C or in C++98, where we cannot be evaluating a constexpr
- // constructor, which is the only case the base matters.
- CompleteObject Obj(APValue::LValueBase(), &Val, BaseTy);
+ CompleteObject Obj(&Val, BaseTy, true);
SubobjectDesignator Designator(BaseTy);
Designator.addDeclUnchecked(FD);
const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl();
if (RD->isInvalidDecl()) return false;
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
- auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
-
- EvalInfo::EvaluatingConstructorRAII EvalObj(
- Info,
- ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries},
- CXXRD && CXXRD->getNumBases());
if (RD->isUnion()) {
const FieldDecl *Field = E->getInitializedFieldInUnion();
return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr);
}
+ auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
if (Result.isUninit())
Result = APValue(APValue::UninitStruct(), CXXRD ? CXXRD->getNumBases() : 0,
std::distance(RD->field_begin(), RD->field_end()));
bool Success = true;
// Initialize base classes.
- if (CXXRD && CXXRD->getNumBases()) {
+ if (CXXRD) {
for (const auto &Base : CXXRD->bases()) {
assert(ElementNo < E->getNumInits() && "missing init for base class");
const Expr *Init = E->getInit(ElementNo);
}
++ElementNo;
}
-
- EvalObj.finishedConstructingBases();
}
// Initialize members.