|
@@ -213,7 +213,7 @@ namespace {
|
|
|
// 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.
|
|
@@ -622,40 +622,6 @@ namespace {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- /// 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
|
|
@@ -706,35 +672,32 @@ namespace {
|
|
|
/// 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
|
|
@@ -823,6 +786,7 @@ namespace {
|
|
|
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
|
|
|
EvaluatingDecl = Base;
|
|
|
EvaluatingDeclValue = &Value;
|
|
|
+ EvaluatingConstructors.insert({Base, {0, 0}});
|
|
|
}
|
|
|
|
|
|
const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
|
|
@@ -1326,22 +1290,14 @@ void EvalInfo::addCallStack(unsigned Limit) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// 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:
|
|
@@ -2828,73 +2784,28 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,
|
|
|
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
|
|
@@ -2917,78 +2828,31 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
|
|
|
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;
|
|
@@ -3034,8 +2898,10 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
|
|
|
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()) {
|
|
@@ -3047,8 +2913,11 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
|
|
|
: 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);
|
|
@@ -3069,17 +2938,34 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
|
|
|
} 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();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -3260,7 +3146,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
|
|
// 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;
|
|
@@ -3269,16 +3155,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
|
|
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.
|
|
@@ -3298,29 +3178,37 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
|
|
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);
|
|
@@ -3329,8 +3217,6 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
|
|
}
|
|
|
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)
|
|
@@ -3390,8 +3276,6 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
|
|
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();
|
|
@@ -3399,9 +3283,8 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
|
|
|
|
|
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();
|
|
|
}
|
|
@@ -3409,6 +3292,29 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
|
|
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
|
|
@@ -3418,10 +3324,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
|
|
|
// 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
|
|
@@ -3455,7 +3361,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
|
|
|
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
|
|
@@ -4501,48 +4407,6 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
|
|
|
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) {
|
|
@@ -4656,9 +4520,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
|
|
|
}
|
|
|
|
|
|
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
|
|
@@ -4732,11 +4595,6 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
|
|
|
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;
|
|
@@ -5127,7 +4985,7 @@ public:
|
|
|
} else
|
|
|
return Error(E);
|
|
|
|
|
|
- if (This && !checkMemberCallThisPointer(Info, E, *This))
|
|
|
+ if (This && !This->checkSubobject(Info, E, CSK_This))
|
|
|
return false;
|
|
|
|
|
|
const FunctionDecl *Definition = nullptr;
|
|
@@ -5163,8 +5021,6 @@ public:
|
|
|
|
|
|
/// 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;
|
|
@@ -5179,10 +5035,7 @@ public:
|
|
|
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);
|
|
|
|
|
@@ -6776,12 +6629,6 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
|
|
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();
|
|
@@ -6808,6 +6655,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
|
|
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()));
|
|
@@ -6815,7 +6663,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
|
|
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);
|
|
@@ -6832,8 +6680,6 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
|
|
}
|
|
|
++ElementNo;
|
|
|
}
|
|
|
-
|
|
|
- EvalObj.finishedConstructingBases();
|
|
|
}
|
|
|
|
|
|
// Initialize members.
|