|
@@ -134,6 +134,14 @@ namespace {
|
|
return E.getAsBaseOrMember().getInt();
|
|
return E.getAsBaseOrMember().getInt();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// Given an expression, determine the type used to store the result of
|
|
|
|
+ /// evaluating that expression.
|
|
|
|
+ static QualType getStorageType(ASTContext &Ctx, Expr *E) {
|
|
|
|
+ if (E->isRValue())
|
|
|
|
+ return E->getType();
|
|
|
|
+ return Ctx.getLValueReferenceType(E->getType());
|
|
|
|
+ }
|
|
|
|
+
|
|
/// Given a CallExpr, try to get the alloc_size attribute. May return null.
|
|
/// Given a CallExpr, try to get the alloc_size attribute. May return null.
|
|
static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) {
|
|
static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) {
|
|
const FunctionDecl *Callee = CE->getDirectCallee();
|
|
const FunctionDecl *Callee = CE->getDirectCallee();
|
|
@@ -572,7 +580,13 @@ namespace {
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
- APValue &createTemporary(const void *Key, bool IsLifetimeExtended);
|
|
|
|
|
|
+ /// Allocate storage for an object of type T in this stack frame.
|
|
|
|
+ /// Populates LV with a handle to the created object. Key identifies
|
|
|
|
+ /// the temporary within the stack frame, and must not be reused without
|
|
|
|
+ /// bumping the temporary version number.
|
|
|
|
+ template<typename KeyT>
|
|
|
|
+ APValue &createTemporary(const KeyT *Key, QualType T,
|
|
|
|
+ bool IsLifetimeExtended, LValue &LV);
|
|
|
|
|
|
void describe(llvm::raw_ostream &OS) override;
|
|
void describe(llvm::raw_ostream &OS) override;
|
|
|
|
|
|
@@ -596,18 +610,33 @@ namespace {
|
|
CallStackFrame &Frame;
|
|
CallStackFrame &Frame;
|
|
const LValue *OldThis;
|
|
const LValue *OldThis;
|
|
};
|
|
};
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool HandleDestructorCall(EvalInfo &Info, APValue::LValueBase LVBase,
|
|
|
|
+ APValue &Value, QualType T);
|
|
|
|
|
|
|
|
+namespace {
|
|
/// A cleanup, and a flag indicating whether it is lifetime-extended.
|
|
/// A cleanup, and a flag indicating whether it is lifetime-extended.
|
|
class Cleanup {
|
|
class Cleanup {
|
|
llvm::PointerIntPair<APValue*, 1, bool> Value;
|
|
llvm::PointerIntPair<APValue*, 1, bool> Value;
|
|
|
|
+ APValue::LValueBase Base;
|
|
|
|
+ QualType T;
|
|
|
|
|
|
public:
|
|
public:
|
|
- Cleanup(APValue *Val, bool IsLifetimeExtended)
|
|
|
|
- : Value(Val, IsLifetimeExtended) {}
|
|
|
|
|
|
+ Cleanup(APValue *Val, APValue::LValueBase Base, QualType T,
|
|
|
|
+ bool IsLifetimeExtended)
|
|
|
|
+ : Value(Val, IsLifetimeExtended), Base(Base), T(T) {}
|
|
|
|
|
|
bool isLifetimeExtended() const { return Value.getInt(); }
|
|
bool isLifetimeExtended() const { return Value.getInt(); }
|
|
- void endLifetime() {
|
|
|
|
|
|
+ bool endLifetime(EvalInfo &Info, bool RunDestructors) {
|
|
|
|
+ if (RunDestructors && T.isDestructedType())
|
|
|
|
+ return HandleDestructorCall(Info, Base, *Value.getPointer(), T);
|
|
*Value.getPointer() = APValue();
|
|
*Value.getPointer() = APValue();
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bool hasSideEffect() {
|
|
|
|
+ return T.isDestructedType();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
@@ -623,7 +652,13 @@ namespace {
|
|
return llvm::hash_combine(Obj.Base, Obj.Path);
|
|
return llvm::hash_combine(Obj.Base, Obj.Path);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
- enum class ConstructionPhase { None, Bases, AfterBases };
|
|
|
|
|
|
+ enum class ConstructionPhase {
|
|
|
|
+ None,
|
|
|
|
+ Bases,
|
|
|
|
+ AfterBases,
|
|
|
|
+ Destroying,
|
|
|
|
+ DestroyingBases
|
|
|
|
+ };
|
|
}
|
|
}
|
|
|
|
|
|
namespace llvm {
|
|
namespace llvm {
|
|
@@ -728,9 +763,29 @@ namespace {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ struct EvaluatingDestructorRAII {
|
|
|
|
+ EvalInfo &EI;
|
|
|
|
+ ObjectUnderConstruction Object;
|
|
|
|
+ EvaluatingDestructorRAII(EvalInfo &EI, ObjectUnderConstruction Object)
|
|
|
|
+ : EI(EI), Object(Object) {
|
|
|
|
+ bool DidInsert = EI.ObjectsUnderConstruction
|
|
|
|
+ .insert({Object, ConstructionPhase::Destroying})
|
|
|
|
+ .second;
|
|
|
|
+ (void)DidInsert;
|
|
|
|
+ assert(DidInsert && "destroyed object multiple times");
|
|
|
|
+ }
|
|
|
|
+ void startedDestroyingBases() {
|
|
|
|
+ EI.ObjectsUnderConstruction[Object] =
|
|
|
|
+ ConstructionPhase::DestroyingBases;
|
|
|
|
+ }
|
|
|
|
+ ~EvaluatingDestructorRAII() {
|
|
|
|
+ EI.ObjectsUnderConstruction.erase(Object);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
ConstructionPhase
|
|
ConstructionPhase
|
|
- isEvaluatingConstructor(APValue::LValueBase Base,
|
|
|
|
- ArrayRef<APValue::LValuePathEntry> Path) {
|
|
|
|
|
|
+ isEvaluatingCtorDtor(APValue::LValueBase Base,
|
|
|
|
+ ArrayRef<APValue::LValuePathEntry> Path) {
|
|
return ObjectsUnderConstruction.lookup({Base, Path});
|
|
return ObjectsUnderConstruction.lookup({Base, Path});
|
|
}
|
|
}
|
|
|
|
|
|
@@ -811,6 +866,10 @@ namespace {
|
|
HasFoldFailureDiagnostic(false), InConstantContext(false),
|
|
HasFoldFailureDiagnostic(false), InConstantContext(false),
|
|
EvalMode(Mode) {}
|
|
EvalMode(Mode) {}
|
|
|
|
|
|
|
|
+ ~EvalInfo() {
|
|
|
|
+ discardCleanups();
|
|
|
|
+ }
|
|
|
|
+
|
|
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
|
|
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
|
|
EvaluatingDecl = Base;
|
|
EvaluatingDecl = Base;
|
|
EvaluatingDeclValue = &Value;
|
|
EvaluatingDeclValue = &Value;
|
|
@@ -858,6 +917,25 @@ namespace {
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ void performLifetimeExtension() {
|
|
|
|
+ // Disable the cleanups for lifetime-extended temporaries.
|
|
|
|
+ CleanupStack.erase(
|
|
|
|
+ std::remove_if(CleanupStack.begin(), CleanupStack.end(),
|
|
|
|
+ [](Cleanup &C) { return C.isLifetimeExtended(); }),
|
|
|
|
+ CleanupStack.end());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Throw away any remaining cleanups at the end of evaluation. If any
|
|
|
|
+ /// cleanups would have had a side-effect, note that as an unmodeled
|
|
|
|
+ /// side-effect and return false. Otherwise, return true.
|
|
|
|
+ bool discardCleanups() {
|
|
|
|
+ for (Cleanup &C : CleanupStack)
|
|
|
|
+ if (C.hasSideEffect())
|
|
|
|
+ if (!noteSideEffect())
|
|
|
|
+ return false;
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
private:
|
|
private:
|
|
interp::Frame *getCurrentFrame() override { return CurrentCall; }
|
|
interp::Frame *getCurrentFrame() override { return CurrentCall; }
|
|
const interp::Frame *getBottomFrame() const override { return &BottomFrame; }
|
|
const interp::Frame *getBottomFrame() const override { return &BottomFrame; }
|
|
@@ -1101,29 +1179,37 @@ namespace {
|
|
// temporaries created in different iterations of a loop.
|
|
// temporaries created in different iterations of a loop.
|
|
Info.CurrentCall->pushTempVersion();
|
|
Info.CurrentCall->pushTempVersion();
|
|
}
|
|
}
|
|
|
|
+ bool destroy(bool RunDestructors = true) {
|
|
|
|
+ bool OK = cleanup(Info, RunDestructors, OldStackSize);
|
|
|
|
+ OldStackSize = -1U;
|
|
|
|
+ return OK;
|
|
|
|
+ }
|
|
~ScopeRAII() {
|
|
~ScopeRAII() {
|
|
|
|
+ if (OldStackSize != -1U)
|
|
|
|
+ destroy(false);
|
|
// Body moved to a static method to encourage the compiler to inline away
|
|
// Body moved to a static method to encourage the compiler to inline away
|
|
// instances of this class.
|
|
// instances of this class.
|
|
- cleanup(Info, OldStackSize);
|
|
|
|
Info.CurrentCall->popTempVersion();
|
|
Info.CurrentCall->popTempVersion();
|
|
}
|
|
}
|
|
private:
|
|
private:
|
|
- static void cleanup(EvalInfo &Info, unsigned OldStackSize) {
|
|
|
|
- unsigned NewEnd = OldStackSize;
|
|
|
|
- for (unsigned I = OldStackSize, N = Info.CleanupStack.size();
|
|
|
|
- I != N; ++I) {
|
|
|
|
- if (IsFullExpression && Info.CleanupStack[I].isLifetimeExtended()) {
|
|
|
|
- // Full-expression cleanup of a lifetime-extended temporary: nothing
|
|
|
|
- // to do, just move this cleanup to the right place in the stack.
|
|
|
|
- std::swap(Info.CleanupStack[I], Info.CleanupStack[NewEnd]);
|
|
|
|
- ++NewEnd;
|
|
|
|
- } else {
|
|
|
|
- // End the lifetime of the object.
|
|
|
|
- Info.CleanupStack[I].endLifetime();
|
|
|
|
|
|
+ static bool cleanup(EvalInfo &Info, bool RunDestructors, unsigned OldStackSize) {
|
|
|
|
+ // Run all cleanups for a block scope, and non-lifetime-extended cleanups
|
|
|
|
+ // for a full-expression scope.
|
|
|
|
+ for (unsigned I = Info.CleanupStack.size(); I > OldStackSize; --I) {
|
|
|
|
+ if (!(IsFullExpression && Info.CleanupStack[I-1].isLifetimeExtended())) {
|
|
|
|
+ if (!Info.CleanupStack[I-1].endLifetime(Info, RunDestructors))
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- Info.CleanupStack.erase(Info.CleanupStack.begin() + NewEnd,
|
|
|
|
- Info.CleanupStack.end());
|
|
|
|
|
|
+
|
|
|
|
+ // Compact lifetime-extended cleanups.
|
|
|
|
+ auto NewEnd = Info.CleanupStack.begin() + OldStackSize;
|
|
|
|
+ if (IsFullExpression)
|
|
|
|
+ NewEnd =
|
|
|
|
+ std::remove_if(NewEnd, Info.CleanupStack.end(),
|
|
|
|
+ [](Cleanup &C) { return !C.isLifetimeExtended(); });
|
|
|
|
+ Info.CleanupStack.erase(NewEnd, Info.CleanupStack.end());
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
typedef ScopeRAII<false> BlockScopeRAII;
|
|
typedef ScopeRAII<false> BlockScopeRAII;
|
|
@@ -1183,15 +1269,6 @@ CallStackFrame::~CallStackFrame() {
|
|
Info.CurrentCall = Caller;
|
|
Info.CurrentCall = Caller;
|
|
}
|
|
}
|
|
|
|
|
|
-APValue &CallStackFrame::createTemporary(const void *Key,
|
|
|
|
- bool IsLifetimeExtended) {
|
|
|
|
- unsigned Version = Info.CurrentCall->getTempVersion();
|
|
|
|
- APValue &Result = Temporaries[MapKeyTy(Key, Version)];
|
|
|
|
- assert(Result.isAbsent() && "temporary created multiple times");
|
|
|
|
- Info.CleanupStack.push_back(Cleanup(&Result, IsLifetimeExtended));
|
|
|
|
- return Result;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static bool isRead(AccessKinds AK) {
|
|
static bool isRead(AccessKinds AK) {
|
|
return AK == AK_Read || AK == AK_ReadObjectRepresentation;
|
|
return AK == AK_Read || AK == AK_ReadObjectRepresentation;
|
|
}
|
|
}
|
|
@@ -1544,15 +1621,6 @@ static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result,
|
|
// Misc utilities
|
|
// Misc utilities
|
|
//===----------------------------------------------------------------------===//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
-/// A helper function to create a temporary and set an LValue.
|
|
|
|
-template <class KeyTy>
|
|
|
|
-static APValue &createTemporary(const KeyTy *Key, bool IsLifetimeExtended,
|
|
|
|
- LValue &LV, CallStackFrame &Frame) {
|
|
|
|
- LV.set({Key, Frame.Info.CurrentCall->Index,
|
|
|
|
- Frame.Info.CurrentCall->getTempVersion()});
|
|
|
|
- return Frame.createTemporary(Key, IsLifetimeExtended);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/// Negate an APSInt in place, converting it to a signed form if necessary, and
|
|
/// Negate an APSInt in place, converting it to a signed form if necessary, and
|
|
/// preserving its value (by extending by up to one bit as needed).
|
|
/// preserving its value (by extending by up to one bit as needed).
|
|
static void negateAsSigned(APSInt &Int) {
|
|
static void negateAsSigned(APSInt &Int) {
|
|
@@ -1563,6 +1631,18 @@ static void negateAsSigned(APSInt &Int) {
|
|
Int = -Int;
|
|
Int = -Int;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+template<typename KeyT>
|
|
|
|
+APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T,
|
|
|
|
+ bool IsLifetimeExtended, LValue &LV) {
|
|
|
|
+ unsigned Version = Info.CurrentCall->getTempVersion();
|
|
|
|
+ APValue::LValueBase Base(Key, Index, Version);
|
|
|
|
+ LV.set(Base);
|
|
|
|
+ APValue &Result = Temporaries[MapKeyTy(Key, Version)];
|
|
|
|
+ assert(Result.isAbsent() && "temporary created multiple times");
|
|
|
|
+ Info.CleanupStack.push_back(Cleanup(&Result, Base, T, IsLifetimeExtended));
|
|
|
|
+ return Result;
|
|
|
|
+}
|
|
|
|
+
|
|
/// Produce a string describing the given constexpr call.
|
|
/// Produce a string describing the given constexpr call.
|
|
void CallStackFrame::describe(raw_ostream &Out) {
|
|
void CallStackFrame::describe(raw_ostream &Out) {
|
|
unsigned ArgIndex = 0;
|
|
unsigned ArgIndex = 0;
|
|
@@ -2858,12 +2938,12 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
|
|
return handler.failed();
|
|
return handler.failed();
|
|
}
|
|
}
|
|
|
|
|
|
- // C++ [class.ctor]p5:
|
|
|
|
|
|
+ // C++ [class.ctor]p5, C++ [class.dtor]p5:
|
|
// const and volatile semantics are not applied on an object under
|
|
// const and volatile semantics are not applied on an object under
|
|
- // construction.
|
|
|
|
|
|
+ // {con,de}struction.
|
|
if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) &&
|
|
if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) &&
|
|
ObjType->isRecordType() &&
|
|
ObjType->isRecordType() &&
|
|
- Info.isEvaluatingConstructor(
|
|
|
|
|
|
+ Info.isEvaluatingCtorDtor(
|
|
Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(),
|
|
Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(),
|
|
Sub.Entries.begin() + I)) !=
|
|
Sub.Entries.begin() + I)) !=
|
|
ConstructionPhase::None) {
|
|
ConstructionPhase::None) {
|
|
@@ -3938,7 +4018,8 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
|
|
return true;
|
|
return true;
|
|
|
|
|
|
LValue Result;
|
|
LValue Result;
|
|
- APValue &Val = createTemporary(VD, true, Result, *Info.CurrentCall);
|
|
|
|
|
|
+ APValue &Val =
|
|
|
|
+ Info.CurrentCall->createTemporary(VD, VD->getType(), true, Result);
|
|
|
|
|
|
const Expr *InitE = VD->getInit();
|
|
const Expr *InitE = VD->getInit();
|
|
if (!InitE) {
|
|
if (!InitE) {
|
|
@@ -3980,7 +4061,9 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
|
|
FullExpressionRAII Scope(Info);
|
|
FullExpressionRAII Scope(Info);
|
|
if (CondDecl && !EvaluateDecl(Info, CondDecl))
|
|
if (CondDecl && !EvaluateDecl(Info, CondDecl))
|
|
return false;
|
|
return false;
|
|
- return EvaluateAsBooleanCondition(Cond, Result, Info);
|
|
|
|
|
|
+ if (!EvaluateAsBooleanCondition(Cond, Result, Info))
|
|
|
|
+ return false;
|
|
|
|
+ return Scope.destroy();
|
|
}
|
|
}
|
|
|
|
|
|
namespace {
|
|
namespace {
|
|
@@ -4016,7 +4099,12 @@ static EvalStmtResult EvaluateLoopBody(StmtResult &Result, EvalInfo &Info,
|
|
const Stmt *Body,
|
|
const Stmt *Body,
|
|
const SwitchCase *Case = nullptr) {
|
|
const SwitchCase *Case = nullptr) {
|
|
BlockScopeRAII Scope(Info);
|
|
BlockScopeRAII Scope(Info);
|
|
- switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case)) {
|
|
|
|
|
|
+
|
|
|
|
+ EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case);
|
|
|
|
+ if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy())
|
|
|
|
+ ESR = ESR_Failed;
|
|
|
|
+
|
|
|
|
+ switch (ESR) {
|
|
case ESR_Break:
|
|
case ESR_Break:
|
|
return ESR_Succeeded;
|
|
return ESR_Succeeded;
|
|
case ESR_Succeeded:
|
|
case ESR_Succeeded:
|
|
@@ -4038,17 +4126,23 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
|
|
// Evaluate the switch condition.
|
|
// Evaluate the switch condition.
|
|
APSInt Value;
|
|
APSInt Value;
|
|
{
|
|
{
|
|
- FullExpressionRAII Scope(Info);
|
|
|
|
if (const Stmt *Init = SS->getInit()) {
|
|
if (const Stmt *Init = SS->getInit()) {
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
|
|
- if (ESR != ESR_Succeeded)
|
|
|
|
|
|
+ if (ESR != ESR_Succeeded) {
|
|
|
|
+ if (ESR != ESR_Failed && !Scope.destroy())
|
|
|
|
+ ESR = ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ FullExpressionRAII CondScope(Info);
|
|
if (SS->getConditionVariable() &&
|
|
if (SS->getConditionVariable() &&
|
|
!EvaluateDecl(Info, SS->getConditionVariable()))
|
|
!EvaluateDecl(Info, SS->getConditionVariable()))
|
|
return ESR_Failed;
|
|
return ESR_Failed;
|
|
if (!EvaluateInteger(SS->getCond(), Value, Info))
|
|
if (!EvaluateInteger(SS->getCond(), Value, Info))
|
|
return ESR_Failed;
|
|
return ESR_Failed;
|
|
|
|
+ if (!CondScope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
}
|
|
}
|
|
|
|
|
|
// Find the switch case corresponding to the value of the condition.
|
|
// Find the switch case corresponding to the value of the condition.
|
|
@@ -4072,10 +4166,14 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
|
|
}
|
|
}
|
|
|
|
|
|
if (!Found)
|
|
if (!Found)
|
|
- return ESR_Succeeded;
|
|
|
|
|
|
+ return Scope.destroy() ? ESR_Failed : ESR_Succeeded;
|
|
|
|
|
|
// Search the switch body for the switch case and evaluate it from there.
|
|
// Search the switch body for the switch case and evaluate it from there.
|
|
- switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found)) {
|
|
|
|
|
|
+ EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found);
|
|
|
|
+ if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
|
|
+
|
|
|
|
+ switch (ESR) {
|
|
case ESR_Break:
|
|
case ESR_Break:
|
|
return ESR_Succeeded;
|
|
return ESR_Succeeded;
|
|
case ESR_Succeeded:
|
|
case ESR_Succeeded:
|
|
@@ -4143,9 +4241,19 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
// (The same is true for 'for' statements.)
|
|
// (The same is true for 'for' statements.)
|
|
|
|
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, IS->getThen(), Case);
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, IS->getThen(), Case);
|
|
- if (ESR != ESR_CaseNotFound || !IS->getElse())
|
|
|
|
|
|
+ if (ESR == ESR_Failed)
|
|
|
|
+ return ESR;
|
|
|
|
+ if (ESR != ESR_CaseNotFound)
|
|
|
|
+ return Scope.destroy() ? ESR : ESR_Failed;
|
|
|
|
+ if (!IS->getElse())
|
|
|
|
+ return ESR_CaseNotFound;
|
|
|
|
+
|
|
|
|
+ ESR = EvaluateStmt(Result, Info, IS->getElse(), Case);
|
|
|
|
+ if (ESR == ESR_Failed)
|
|
return ESR;
|
|
return ESR;
|
|
- return EvaluateStmt(Result, Info, IS->getElse(), Case);
|
|
|
|
|
|
+ if (ESR != ESR_CaseNotFound)
|
|
|
|
+ return Scope.destroy() ? ESR : ESR_Failed;
|
|
|
|
+ return ESR_CaseNotFound;
|
|
}
|
|
}
|
|
|
|
|
|
case Stmt::WhileStmtClass: {
|
|
case Stmt::WhileStmtClass: {
|
|
@@ -4176,7 +4284,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
return ESR;
|
|
return ESR;
|
|
if (FS->getInc()) {
|
|
if (FS->getInc()) {
|
|
FullExpressionRAII IncScope(Info);
|
|
FullExpressionRAII IncScope(Info);
|
|
- if (!EvaluateIgnoredValue(Info, FS->getInc()))
|
|
|
|
|
|
+ if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy())
|
|
return ESR_Failed;
|
|
return ESR_Failed;
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
@@ -4209,8 +4317,10 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
if (const Expr *E = dyn_cast<Expr>(S)) {
|
|
if (const Expr *E = dyn_cast<Expr>(S)) {
|
|
// Don't bother evaluating beyond an expression-statement which couldn't
|
|
// Don't bother evaluating beyond an expression-statement which couldn't
|
|
// be evaluated.
|
|
// be evaluated.
|
|
|
|
+ // FIXME: Do we need the FullExpressionRAII object here?
|
|
|
|
+ // VisitExprWithCleanups should create one when necessary.
|
|
FullExpressionRAII Scope(Info);
|
|
FullExpressionRAII Scope(Info);
|
|
- if (!EvaluateIgnoredValue(Info, E))
|
|
|
|
|
|
+ if (!EvaluateIgnoredValue(Info, E) || !Scope.destroy())
|
|
return ESR_Failed;
|
|
return ESR_Failed;
|
|
return ESR_Succeeded;
|
|
return ESR_Succeeded;
|
|
}
|
|
}
|
|
@@ -4228,6 +4338,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
FullExpressionRAII Scope(Info);
|
|
FullExpressionRAII Scope(Info);
|
|
if (!EvaluateDecl(Info, D) && !Info.noteFailure())
|
|
if (!EvaluateDecl(Info, D) && !Info.noteFailure())
|
|
return ESR_Failed;
|
|
return ESR_Failed;
|
|
|
|
+ if (!Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
}
|
|
}
|
|
return ESR_Succeeded;
|
|
return ESR_Succeeded;
|
|
}
|
|
}
|
|
@@ -4240,7 +4352,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr)
|
|
? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr)
|
|
: Evaluate(Result.Value, Info, RetExpr)))
|
|
: Evaluate(Result.Value, Info, RetExpr)))
|
|
return ESR_Failed;
|
|
return ESR_Failed;
|
|
- return ESR_Returned;
|
|
|
|
|
|
+ return Scope.destroy() ? ESR_Returned : ESR_Failed;
|
|
}
|
|
}
|
|
|
|
|
|
case Stmt::CompoundStmtClass: {
|
|
case Stmt::CompoundStmtClass: {
|
|
@@ -4251,10 +4363,15 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, BI, Case);
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, BI, Case);
|
|
if (ESR == ESR_Succeeded)
|
|
if (ESR == ESR_Succeeded)
|
|
Case = nullptr;
|
|
Case = nullptr;
|
|
- else if (ESR != ESR_CaseNotFound)
|
|
|
|
|
|
+ else if (ESR != ESR_CaseNotFound) {
|
|
|
|
+ if (ESR != ESR_Failed && !Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- return Case ? ESR_CaseNotFound : ESR_Succeeded;
|
|
|
|
|
|
+ if (Case)
|
|
|
|
+ return ESR_CaseNotFound;
|
|
|
|
+ return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
|
|
}
|
|
}
|
|
|
|
|
|
case Stmt::IfStmtClass: {
|
|
case Stmt::IfStmtClass: {
|
|
@@ -4264,8 +4381,11 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
BlockScopeRAII Scope(Info);
|
|
BlockScopeRAII Scope(Info);
|
|
if (const Stmt *Init = IS->getInit()) {
|
|
if (const Stmt *Init = IS->getInit()) {
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
|
|
- if (ESR != ESR_Succeeded)
|
|
|
|
|
|
+ if (ESR != ESR_Succeeded) {
|
|
|
|
+ if (ESR != ESR_Failed && !Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
bool Cond;
|
|
bool Cond;
|
|
if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond))
|
|
if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond))
|
|
@@ -4273,10 +4393,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
|
|
|
|
if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) {
|
|
if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) {
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, SubStmt);
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, SubStmt);
|
|
- if (ESR != ESR_Succeeded)
|
|
|
|
|
|
+ if (ESR != ESR_Succeeded) {
|
|
|
|
+ if (ESR != ESR_Failed && !Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- return ESR_Succeeded;
|
|
|
|
|
|
+ return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
|
|
}
|
|
}
|
|
|
|
|
|
case Stmt::WhileStmtClass: {
|
|
case Stmt::WhileStmtClass: {
|
|
@@ -4291,8 +4414,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
break;
|
|
break;
|
|
|
|
|
|
EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody());
|
|
EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody());
|
|
- if (ESR != ESR_Continue)
|
|
|
|
|
|
+ if (ESR != ESR_Continue) {
|
|
|
|
+ if (ESR != ESR_Failed && !Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
|
|
+ if (!Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
}
|
|
}
|
|
return ESR_Succeeded;
|
|
return ESR_Succeeded;
|
|
}
|
|
}
|
|
@@ -4307,7 +4435,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
Case = nullptr;
|
|
Case = nullptr;
|
|
|
|
|
|
FullExpressionRAII CondScope(Info);
|
|
FullExpressionRAII CondScope(Info);
|
|
- if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info))
|
|
|
|
|
|
+ if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info) ||
|
|
|
|
+ !CondScope.destroy())
|
|
return ESR_Failed;
|
|
return ESR_Failed;
|
|
} while (Continue);
|
|
} while (Continue);
|
|
return ESR_Succeeded;
|
|
return ESR_Succeeded;
|
|
@@ -4315,14 +4444,17 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
|
|
|
|
case Stmt::ForStmtClass: {
|
|
case Stmt::ForStmtClass: {
|
|
const ForStmt *FS = cast<ForStmt>(S);
|
|
const ForStmt *FS = cast<ForStmt>(S);
|
|
- BlockScopeRAII Scope(Info);
|
|
|
|
|
|
+ BlockScopeRAII ForScope(Info);
|
|
if (FS->getInit()) {
|
|
if (FS->getInit()) {
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
|
|
- if (ESR != ESR_Succeeded)
|
|
|
|
|
|
+ if (ESR != ESR_Succeeded) {
|
|
|
|
+ if (ESR != ESR_Failed && !ForScope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
while (true) {
|
|
while (true) {
|
|
- BlockScopeRAII Scope(Info);
|
|
|
|
|
|
+ BlockScopeRAII IterScope(Info);
|
|
bool Continue = true;
|
|
bool Continue = true;
|
|
if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(),
|
|
if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(),
|
|
FS->getCond(), Continue))
|
|
FS->getCond(), Continue))
|
|
@@ -4331,16 +4463,22 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
break;
|
|
break;
|
|
|
|
|
|
EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody());
|
|
EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody());
|
|
- if (ESR != ESR_Continue)
|
|
|
|
|
|
+ if (ESR != ESR_Continue) {
|
|
|
|
+ if (ESR != ESR_Failed && (!IterScope.destroy() || !ForScope.destroy()))
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
|
|
|
|
if (FS->getInc()) {
|
|
if (FS->getInc()) {
|
|
FullExpressionRAII IncScope(Info);
|
|
FullExpressionRAII IncScope(Info);
|
|
- if (!EvaluateIgnoredValue(Info, FS->getInc()))
|
|
|
|
|
|
+ if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy())
|
|
return ESR_Failed;
|
|
return ESR_Failed;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (!IterScope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
}
|
|
}
|
|
- return ESR_Succeeded;
|
|
|
|
|
|
+ return ForScope.destroy() ? ESR_Succeeded : ESR_Failed;
|
|
}
|
|
}
|
|
|
|
|
|
case Stmt::CXXForRangeStmtClass: {
|
|
case Stmt::CXXForRangeStmtClass: {
|
|
@@ -4350,22 +4488,34 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
// Evaluate the init-statement if present.
|
|
// Evaluate the init-statement if present.
|
|
if (FS->getInit()) {
|
|
if (FS->getInit()) {
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
|
|
- if (ESR != ESR_Succeeded)
|
|
|
|
|
|
+ if (ESR != ESR_Succeeded) {
|
|
|
|
+ if (ESR != ESR_Failed && !Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
// Initialize the __range variable.
|
|
// Initialize the __range variable.
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt());
|
|
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt());
|
|
- if (ESR != ESR_Succeeded)
|
|
|
|
|
|
+ if (ESR != ESR_Succeeded) {
|
|
|
|
+ if (ESR != ESR_Failed && !Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
|
|
|
|
// Create the __begin and __end iterators.
|
|
// Create the __begin and __end iterators.
|
|
ESR = EvaluateStmt(Result, Info, FS->getBeginStmt());
|
|
ESR = EvaluateStmt(Result, Info, FS->getBeginStmt());
|
|
- if (ESR != ESR_Succeeded)
|
|
|
|
|
|
+ if (ESR != ESR_Succeeded) {
|
|
|
|
+ if (ESR != ESR_Failed && !Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
ESR = EvaluateStmt(Result, Info, FS->getEndStmt());
|
|
ESR = EvaluateStmt(Result, Info, FS->getEndStmt());
|
|
- if (ESR != ESR_Succeeded)
|
|
|
|
|
|
+ if (ESR != ESR_Succeeded) {
|
|
|
|
+ if (ESR != ESR_Failed && !Scope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
|
|
|
|
while (true) {
|
|
while (true) {
|
|
// Condition: __begin != __end.
|
|
// Condition: __begin != __end.
|
|
@@ -4381,20 +4531,29 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|
// User's variable declaration, initialized by *__begin.
|
|
// User's variable declaration, initialized by *__begin.
|
|
BlockScopeRAII InnerScope(Info);
|
|
BlockScopeRAII InnerScope(Info);
|
|
ESR = EvaluateStmt(Result, Info, FS->getLoopVarStmt());
|
|
ESR = EvaluateStmt(Result, Info, FS->getLoopVarStmt());
|
|
- if (ESR != ESR_Succeeded)
|
|
|
|
|
|
+ if (ESR != ESR_Succeeded) {
|
|
|
|
+ if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy()))
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
|
|
|
|
// Loop body.
|
|
// Loop body.
|
|
ESR = EvaluateLoopBody(Result, Info, FS->getBody());
|
|
ESR = EvaluateLoopBody(Result, Info, FS->getBody());
|
|
- if (ESR != ESR_Continue)
|
|
|
|
|
|
+ if (ESR != ESR_Continue) {
|
|
|
|
+ if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy()))
|
|
|
|
+ return ESR_Failed;
|
|
return ESR;
|
|
return ESR;
|
|
|
|
+ }
|
|
|
|
|
|
// Increment: ++__begin
|
|
// Increment: ++__begin
|
|
if (!EvaluateIgnoredValue(Info, FS->getInc()))
|
|
if (!EvaluateIgnoredValue(Info, FS->getInc()))
|
|
return ESR_Failed;
|
|
return ESR_Failed;
|
|
|
|
+
|
|
|
|
+ if (!InnerScope.destroy())
|
|
|
|
+ return ESR_Failed;
|
|
}
|
|
}
|
|
|
|
|
|
- return ESR_Succeeded;
|
|
|
|
|
|
+ return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
|
|
}
|
|
}
|
|
|
|
|
|
case Stmt::SwitchStmtClass:
|
|
case Stmt::SwitchStmtClass:
|
|
@@ -4619,16 +4778,19 @@ static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E,
|
|
ArrayRef<APValue::LValuePathEntry> Path = This.Designator.Entries;
|
|
ArrayRef<APValue::LValuePathEntry> Path = This.Designator.Entries;
|
|
for (unsigned PathLength = This.Designator.MostDerivedPathLength;
|
|
for (unsigned PathLength = This.Designator.MostDerivedPathLength;
|
|
PathLength <= Path.size(); ++PathLength) {
|
|
PathLength <= Path.size(); ++PathLength) {
|
|
- switch (Info.isEvaluatingConstructor(This.getLValueBase(),
|
|
|
|
- Path.slice(0, PathLength))) {
|
|
|
|
|
|
+ switch (Info.isEvaluatingCtorDtor(This.getLValueBase(),
|
|
|
|
+ Path.slice(0, PathLength))) {
|
|
case ConstructionPhase::Bases:
|
|
case ConstructionPhase::Bases:
|
|
- // We're constructing a base class. This is not the dynamic type.
|
|
|
|
|
|
+ case ConstructionPhase::DestroyingBases:
|
|
|
|
+ // We're constructing or destroying a base class. This is not the dynamic
|
|
|
|
+ // type.
|
|
break;
|
|
break;
|
|
|
|
|
|
case ConstructionPhase::None:
|
|
case ConstructionPhase::None:
|
|
case ConstructionPhase::AfterBases:
|
|
case ConstructionPhase::AfterBases:
|
|
- // We've finished constructing the base classes, so this is the dynamic
|
|
|
|
- // type.
|
|
|
|
|
|
+ case ConstructionPhase::Destroying:
|
|
|
|
+ // We've finished constructing the base classes and not yet started
|
|
|
|
+ // destroying them again, so this is the dynamic type.
|
|
return DynamicType{getBaseClassType(This.Designator, PathLength),
|
|
return DynamicType{getBaseClassType(This.Designator, PathLength),
|
|
PathLength};
|
|
PathLength};
|
|
}
|
|
}
|
|
@@ -5117,7 +5279,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
|
|
CXXConstructorDecl::init_const_iterator I = Definition->init_begin();
|
|
CXXConstructorDecl::init_const_iterator I = Definition->init_begin();
|
|
{
|
|
{
|
|
FullExpressionRAII InitScope(Info);
|
|
FullExpressionRAII InitScope(Info);
|
|
- if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()))
|
|
|
|
|
|
+ if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()) ||
|
|
|
|
+ !InitScope.destroy())
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
return EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed;
|
|
return EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed;
|
|
@@ -5280,7 +5443,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
|
|
}
|
|
}
|
|
|
|
|
|
return Success &&
|
|
return Success &&
|
|
- EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed;
|
|
|
|
|
|
+ EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed &&
|
|
|
|
+ LifetimeExtendedScope.destroy();
|
|
}
|
|
}
|
|
|
|
|
|
static bool HandleConstructorCall(const Expr *E, const LValue &This,
|
|
static bool HandleConstructorCall(const Expr *E, const LValue &This,
|
|
@@ -5295,6 +5459,158 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
|
|
Info, Result);
|
|
Info, Result);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static bool HandleDestructorCallImpl(EvalInfo &Info, SourceLocation CallLoc,
|
|
|
|
+ const LValue &This, APValue &Value,
|
|
|
|
+ QualType T) {
|
|
|
|
+ // Invent an expression for location purposes.
|
|
|
|
+ // FIXME: We shouldn't need to do this.
|
|
|
|
+ OpaqueValueExpr LocE(CallLoc, Info.Ctx.IntTy, VK_RValue);
|
|
|
|
+
|
|
|
|
+ // For arrays, destroy elements right-to-left.
|
|
|
|
+ if (const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(T)) {
|
|
|
|
+ uint64_t Size = CAT->getSize().getZExtValue();
|
|
|
|
+ QualType ElemT = CAT->getElementType();
|
|
|
|
+
|
|
|
|
+ LValue ElemLV = This;
|
|
|
|
+ ElemLV.addArray(Info, &LocE, CAT);
|
|
|
|
+ if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, Size))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ for (; Size != 0; --Size) {
|
|
|
|
+ APValue &Elem = Value.getArrayInitializedElt(Size - 1);
|
|
|
|
+ if (!HandleDestructorCallImpl(Info, CallLoc, ElemLV, Elem, ElemT) ||
|
|
|
|
+ !HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, -1))
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // End the lifetime of this array now.
|
|
|
|
+ Value = APValue();
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const CXXRecordDecl *RD = T->getAsCXXRecordDecl();
|
|
|
|
+ if (!RD) {
|
|
|
|
+ if (T.isDestructedType()) {
|
|
|
|
+ Info.FFDiag(CallLoc, diag::note_constexpr_unsupported_destruction) << T;
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Value = APValue();
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (RD->getNumVBases()) {
|
|
|
|
+ Info.FFDiag(CallLoc, diag::note_constexpr_virtual_base) << RD;
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const CXXDestructorDecl *DD = RD->getDestructor();
|
|
|
|
+ if (!DD) {
|
|
|
|
+ // FIXME: Can we get here for a type with an irrelevant destructor?
|
|
|
|
+ Info.FFDiag(CallLoc);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const FunctionDecl *Definition = nullptr;
|
|
|
|
+ const Stmt *Body = DD->getBody(Definition);
|
|
|
|
+
|
|
|
|
+ if ((DD && DD->isTrivial()) ||
|
|
|
|
+ (RD->isAnonymousStructOrUnion() && RD->isUnion())) {
|
|
|
|
+ // A trivial destructor just ends the lifetime of the object. Check for
|
|
|
|
+ // this case before checking for a body, because we might not bother
|
|
|
|
+ // building a body for a trivial destructor. Note that it doesn't matter
|
|
|
|
+ // whether the destructor is constexpr in this case; all trivial
|
|
|
|
+ // destructors are constexpr.
|
|
|
|
+ //
|
|
|
|
+ // If an anonymous union would be destroyed, some enclosing destructor must
|
|
|
|
+ // have been explicitly defined, and the anonymous union destruction should
|
|
|
|
+ // have no effect.
|
|
|
|
+ Value = APValue();
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!Info.CheckCallLimit(CallLoc))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (!CheckConstexprFunction(Info, CallLoc, DD, Definition, Body))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ CallStackFrame Frame(Info, CallLoc, Definition, &This, nullptr);
|
|
|
|
+
|
|
|
|
+ // We're now in the period of destruction of this object.
|
|
|
|
+ unsigned BasesLeft = RD->getNumBases();
|
|
|
|
+ EvalInfo::EvaluatingDestructorRAII EvalObj(
|
|
|
|
+ Info,
|
|
|
|
+ ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries});
|
|
|
|
+
|
|
|
|
+ // FIXME: Creating an APValue just to hold a nonexistent return value is
|
|
|
|
+ // wasteful.
|
|
|
|
+ APValue RetVal;
|
|
|
|
+ StmtResult Ret = {RetVal, nullptr};
|
|
|
|
+ if (EvaluateStmt(Ret, Info, Definition->getBody()) == ESR_Failed)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ // A union destructor does not implicitly destroy its members.
|
|
|
|
+ if (RD->isUnion())
|
|
|
|
+ return true;
|
|
|
|
+
|
|
|
|
+ const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
|
|
|
|
+
|
|
|
|
+ // We don't have a good way to iterate fields in reverse, so collect all the
|
|
|
|
+ // fields first and then walk them backwards.
|
|
|
|
+ SmallVector<FieldDecl*, 16> Fields(RD->field_begin(), RD->field_end());
|
|
|
|
+ for (const FieldDecl *FD : llvm::reverse(Fields)) {
|
|
|
|
+ if (FD->isUnnamedBitfield())
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ LValue Subobject = This;
|
|
|
|
+ if (!HandleLValueMember(Info, &LocE, Subobject, FD, &Layout))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ APValue *SubobjectValue = &Value.getStructField(FD->getFieldIndex());
|
|
|
|
+ if (!HandleDestructorCallImpl(Info, CallLoc, Subobject, *SubobjectValue,
|
|
|
|
+ FD->getType()))
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (BasesLeft != 0)
|
|
|
|
+ EvalObj.startedDestroyingBases();
|
|
|
|
+
|
|
|
|
+ // Destroy base classes in reverse order.
|
|
|
|
+ for (const CXXBaseSpecifier &Base : llvm::reverse(RD->bases())) {
|
|
|
|
+ --BasesLeft;
|
|
|
|
+
|
|
|
|
+ QualType BaseType = Base.getType();
|
|
|
|
+ LValue Subobject = This;
|
|
|
|
+ if (!HandleLValueDirectBase(Info, &LocE, Subobject, RD,
|
|
|
|
+ BaseType->getAsCXXRecordDecl(), &Layout))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ APValue *SubobjectValue = &Value.getStructBase(BasesLeft);
|
|
|
|
+ if (!HandleDestructorCallImpl(Info, CallLoc, Subobject, *SubobjectValue,
|
|
|
|
+ BaseType))
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ assert(BasesLeft == 0 && "NumBases was wrong?");
|
|
|
|
+
|
|
|
|
+ // The period of destruction ends now. The object is gone.
|
|
|
|
+ Value = APValue();
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool HandleDestructorCall(EvalInfo &Info, APValue::LValueBase LVBase,
|
|
|
|
+ APValue &Value, QualType T) {
|
|
|
|
+ SourceLocation Loc;
|
|
|
|
+ if (const ValueDecl *VD = LVBase.dyn_cast<const ValueDecl*>())
|
|
|
|
+ Loc = VD->getLocation();
|
|
|
|
+ else if (const Expr *E = LVBase.dyn_cast<const Expr*>())
|
|
|
|
+ Loc = E->getExprLoc();
|
|
|
|
+
|
|
|
|
+ LValue LV;
|
|
|
|
+ LV.set({LVBase});
|
|
|
|
+ return HandleDestructorCallImpl(Info, Loc, LV, Value, T);
|
|
|
|
+}
|
|
|
|
+
|
|
//===----------------------------------------------------------------------===//
|
|
//===----------------------------------------------------------------------===//
|
|
// Generic Evaluation
|
|
// Generic Evaluation
|
|
//===----------------------------------------------------------------------===//
|
|
//===----------------------------------------------------------------------===//
|
|
@@ -5905,10 +6221,10 @@ public:
|
|
return StmtVisitorTy::Visit(E->getExpr());
|
|
return StmtVisitorTy::Visit(E->getExpr());
|
|
}
|
|
}
|
|
|
|
|
|
- // We cannot create any objects for which cleanups are required, so there is
|
|
|
|
- // nothing to do here; all cleanups must come from unevaluated subexpressions.
|
|
|
|
- bool VisitExprWithCleanups(const ExprWithCleanups *E)
|
|
|
|
- { return StmtVisitorTy::Visit(E->getSubExpr()); }
|
|
|
|
|
|
+ bool VisitExprWithCleanups(const ExprWithCleanups *E) {
|
|
|
|
+ FullExpressionRAII Scope(Info);
|
|
|
|
+ return StmtVisitorTy::Visit(E->getSubExpr()) && Scope.destroy();
|
|
|
|
+ }
|
|
|
|
|
|
bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E) {
|
|
bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E) {
|
|
CCEDiag(E, diag::note_constexpr_invalid_cast) << 0;
|
|
CCEDiag(E, diag::note_constexpr_invalid_cast) << 0;
|
|
@@ -5948,7 +6264,10 @@ public:
|
|
bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
|
|
bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
|
|
// Evaluate and cache the common expression. We treat it as a temporary,
|
|
// Evaluate and cache the common expression. We treat it as a temporary,
|
|
// even though it's not quite the same thing.
|
|
// even though it's not quite the same thing.
|
|
- if (!Evaluate(Info.CurrentCall->createTemporary(E->getOpaqueValue(), false),
|
|
|
|
|
|
+ LValue CommonLV;
|
|
|
|
+ if (!Evaluate(Info.CurrentCall->createTemporary(
|
|
|
|
+ E->getOpaqueValue(), getStorageType(Info.Ctx, E->getOpaqueValue()),
|
|
|
|
+ false, CommonLV),
|
|
Info, E->getCommon()))
|
|
Info, E->getCommon()))
|
|
return false;
|
|
return false;
|
|
|
|
|
|
@@ -6255,11 +6574,11 @@ public:
|
|
if (Info.checkingForUndefinedBehavior())
|
|
if (Info.checkingForUndefinedBehavior())
|
|
return Error(E);
|
|
return Error(E);
|
|
|
|
|
|
- BlockScopeRAII Scope(Info);
|
|
|
|
const CompoundStmt *CS = E->getSubStmt();
|
|
const CompoundStmt *CS = E->getSubStmt();
|
|
if (CS->body_empty())
|
|
if (CS->body_empty())
|
|
return true;
|
|
return true;
|
|
|
|
|
|
|
|
+ BlockScopeRAII Scope(Info);
|
|
for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
|
|
for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
|
|
BE = CS->body_end();
|
|
BE = CS->body_end();
|
|
/**/; ++BI) {
|
|
/**/; ++BI) {
|
|
@@ -6270,7 +6589,7 @@ public:
|
|
diag::note_constexpr_stmt_expr_unsupported);
|
|
diag::note_constexpr_stmt_expr_unsupported);
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
- return this->Visit(FinalExpr);
|
|
|
|
|
|
+ return this->Visit(FinalExpr) && Scope.destroy();
|
|
}
|
|
}
|
|
|
|
|
|
APValue ReturnValue;
|
|
APValue ReturnValue;
|
|
@@ -6619,8 +6938,8 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
|
|
*Value = APValue();
|
|
*Value = APValue();
|
|
Result.set(E);
|
|
Result.set(E);
|
|
} else {
|
|
} else {
|
|
- Value = &createTemporary(E, E->getStorageDuration() == SD_Automatic, Result,
|
|
|
|
- *Info.CurrentCall);
|
|
|
|
|
|
+ Value = &Info.CurrentCall->createTemporary(
|
|
|
|
+ E, E->getType(), E->getStorageDuration() == SD_Automatic, Result);
|
|
}
|
|
}
|
|
|
|
|
|
QualType Type = Inner->getType();
|
|
QualType Type = Inner->getType();
|
|
@@ -7152,8 +7471,8 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|
if (!evaluateLValue(SubExpr, Result))
|
|
if (!evaluateLValue(SubExpr, Result))
|
|
return false;
|
|
return false;
|
|
} else {
|
|
} else {
|
|
- APValue &Value = createTemporary(SubExpr, false, Result,
|
|
|
|
- *Info.CurrentCall);
|
|
|
|
|
|
+ APValue &Value = Info.CurrentCall->createTemporary(
|
|
|
|
+ SubExpr, SubExpr->getType(), false, Result);
|
|
if (!EvaluateInPlace(Value, Info, Result, SubExpr))
|
|
if (!EvaluateInPlace(Value, Info, Result, SubExpr))
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
@@ -7705,6 +8024,12 @@ namespace {
|
|
bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
|
|
bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
|
|
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
|
|
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
|
|
|
|
|
|
|
|
+ // Temporaries are registered when created, so we don't care about
|
|
|
|
+ // CXXBindTemporaryExpr.
|
|
|
|
+ bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *E) {
|
|
|
|
+ return Visit(E->getSubExpr());
|
|
|
|
+ }
|
|
|
|
+
|
|
bool VisitBinCmp(const BinaryOperator *E);
|
|
bool VisitBinCmp(const BinaryOperator *E);
|
|
};
|
|
};
|
|
}
|
|
}
|
|
@@ -8104,7 +8429,8 @@ public:
|
|
|
|
|
|
/// Visit an expression which constructs the value of this temporary.
|
|
/// Visit an expression which constructs the value of this temporary.
|
|
bool VisitConstructExpr(const Expr *E) {
|
|
bool VisitConstructExpr(const Expr *E) {
|
|
- APValue &Value = createTemporary(E, false, Result, *Info.CurrentCall);
|
|
|
|
|
|
+ APValue &Value =
|
|
|
|
+ Info.CurrentCall->createTemporary(E, E->getType(), false, Result);
|
|
return EvaluateInPlace(Value, Info, Result, E);
|
|
return EvaluateInPlace(Value, Info, Result, E);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -8464,8 +8790,12 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
|
}
|
|
}
|
|
|
|
|
|
bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) {
|
|
bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) {
|
|
|
|
+ LValue CommonLV;
|
|
if (E->getCommonExpr() &&
|
|
if (E->getCommonExpr() &&
|
|
- !Evaluate(Info.CurrentCall->createTemporary(E->getCommonExpr(), false),
|
|
|
|
|
|
+ !Evaluate(Info.CurrentCall->createTemporary(
|
|
|
|
+ E->getCommonExpr(),
|
|
|
|
+ getStorageType(Info.Ctx, E->getCommonExpr()), false,
|
|
|
|
+ CommonLV),
|
|
Info, E->getCommonExpr()->getSourceExpr()))
|
|
Info, E->getCommonExpr()->getSourceExpr()))
|
|
return false;
|
|
return false;
|
|
|
|
|
|
@@ -12124,13 +12454,14 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
|
|
return true;
|
|
return true;
|
|
} else if (T->isArrayType()) {
|
|
} else if (T->isArrayType()) {
|
|
LValue LV;
|
|
LValue LV;
|
|
- APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall);
|
|
|
|
|
|
+ APValue &Value =
|
|
|
|
+ Info.CurrentCall->createTemporary(E, T, false, LV);
|
|
if (!EvaluateArray(E, LV, Value, Info))
|
|
if (!EvaluateArray(E, LV, Value, Info))
|
|
return false;
|
|
return false;
|
|
Result = Value;
|
|
Result = Value;
|
|
} else if (T->isRecordType()) {
|
|
} else if (T->isRecordType()) {
|
|
LValue LV;
|
|
LValue LV;
|
|
- APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall);
|
|
|
|
|
|
+ APValue &Value = Info.CurrentCall->createTemporary(E, T, false, LV);
|
|
if (!EvaluateRecord(E, LV, Value, Info))
|
|
if (!EvaluateRecord(E, LV, Value, Info))
|
|
return false;
|
|
return false;
|
|
Result = Value;
|
|
Result = Value;
|
|
@@ -12144,7 +12475,7 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
|
|
QualType Unqual = T.getAtomicUnqualifiedType();
|
|
QualType Unqual = T.getAtomicUnqualifiedType();
|
|
if (Unqual->isArrayType() || Unqual->isRecordType()) {
|
|
if (Unqual->isArrayType() || Unqual->isRecordType()) {
|
|
LValue LV;
|
|
LValue LV;
|
|
- APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall);
|
|
|
|
|
|
+ APValue &Value = Info.CurrentCall->createTemporary(E, Unqual, false, LV);
|
|
if (!EvaluateAtomic(E, &LV, Value, Info))
|
|
if (!EvaluateAtomic(E, &LV, Value, Info))
|
|
return false;
|
|
return false;
|
|
} else {
|
|
} else {
|
|
@@ -12372,7 +12703,8 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx,
|
|
EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold);
|
|
EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold);
|
|
Info.InConstantContext = InConstantContext;
|
|
Info.InConstantContext = InConstantContext;
|
|
LValue LV;
|
|
LValue LV;
|
|
- if (!EvaluateLValue(this, LV, Info) || Result.HasSideEffects ||
|
|
|
|
|
|
+ if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() ||
|
|
|
|
+ Result.HasSideEffects ||
|
|
!CheckLValueConstantExpression(Info, getExprLoc(),
|
|
!CheckLValueConstantExpression(Info, getExprLoc(),
|
|
Ctx.getLValueReferenceType(getType()), LV,
|
|
Ctx.getLValueReferenceType(getType()), LV,
|
|
Expr::EvaluateForCodeGen))
|
|
Expr::EvaluateForCodeGen))
|
|
@@ -12394,6 +12726,9 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage,
|
|
if (!::Evaluate(Result.Val, Info, this))
|
|
if (!::Evaluate(Result.Val, Info, this))
|
|
return false;
|
|
return false;
|
|
|
|
|
|
|
|
+ if (!Info.discardCleanups())
|
|
|
|
+ llvm_unreachable("Unhandled cleanup; missing full expression marker?");
|
|
|
|
+
|
|
return CheckConstantExpression(Info, getExprLoc(), getType(), Result.Val,
|
|
return CheckConstantExpression(Info, getExprLoc(), getType(), Result.Val,
|
|
Usage);
|
|
Usage);
|
|
}
|
|
}
|
|
@@ -12457,6 +12792,13 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
|
|
EStatus.HasSideEffects)
|
|
EStatus.HasSideEffects)
|
|
return false;
|
|
return false;
|
|
|
|
|
|
|
|
+ // At this point, any lifetime-extended temporaries are completely
|
|
|
|
+ // initialized.
|
|
|
|
+ Info.performLifetimeExtension();
|
|
|
|
+
|
|
|
|
+ if (!Info.discardCleanups())
|
|
|
|
+ llvm_unreachable("Unhandled cleanup; missing full expression marker?");
|
|
|
|
+
|
|
return CheckConstantExpression(Info, DeclLoc, DeclTy, Value);
|
|
return CheckConstantExpression(Info, DeclLoc, DeclTy, Value);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -13063,7 +13405,11 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result,
|
|
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
|
|
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
|
|
|
|
|
|
APValue Scratch;
|
|
APValue Scratch;
|
|
- bool IsConstExpr = ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch);
|
|
|
|
|
|
+ bool IsConstExpr =
|
|
|
|
+ ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) &&
|
|
|
|
+ // FIXME: We don't produce a diagnostic for this, but the callers that
|
|
|
|
+ // call us on arbitrary full-expressions should generally not care.
|
|
|
|
+ Info.discardCleanups();
|
|
|
|
|
|
if (!Diags.empty()) {
|
|
if (!Diags.empty()) {
|
|
IsConstExpr = false;
|
|
IsConstExpr = false;
|
|
@@ -13115,7 +13461,8 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
|
|
// Build fake call to Callee.
|
|
// Build fake call to Callee.
|
|
CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr,
|
|
CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr,
|
|
ArgValues.data());
|
|
ArgValues.data());
|
|
- return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects;
|
|
|
|
|
|
+ return Evaluate(Value, Info, this) && Info.discardCleanups() &&
|
|
|
|
+ !Info.EvalStatus.HasSideEffects;
|
|
}
|
|
}
|
|
|
|
|
|
bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
|
|
bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
|