summaryrefslogtreecommitdiffstats
path: root/clang/lib/AST/ExprConstant.cpp
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2019-09-23 03:48:44 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2019-09-23 03:48:44 +0000
commit457226e02a6e8533eaaa864a3fd7c8eeccd2bf58 (patch)
tree616a28265a6d458d6dc25c56825fc04f65d5fc17 /clang/lib/AST/ExprConstant.cpp
parentda4a4707d2ed7add4a054b06f26ed547fd1d221d (diff)
downloadbcm5719-llvm-457226e02a6e8533eaaa864a3fd7c8eeccd2bf58.tar.gz
bcm5719-llvm-457226e02a6e8533eaaa864a3fd7c8eeccd2bf58.zip
For P0784R7: add support for constexpr destructors, and call them as
appropriate during constant evaluation. Note that the evaluator is sometimes invoked on incomplete expressions. In such cases, if an object is constructed but we never reach the point where it would be destroyed (and it has non-trivial destruction), we treat the expression as having an unmodeled side-effect. llvm-svn: 372538
Diffstat (limited to 'clang/lib/AST/ExprConstant.cpp')
-rw-r--r--clang/lib/AST/ExprConstant.cpp549
1 files changed, 448 insertions, 101 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4e371d7ca95..fbec3ae582b 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -134,6 +134,14 @@ namespace {
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.
static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) {
const FunctionDecl *Callee = CE->getDirectCallee();
@@ -572,7 +580,13 @@ namespace {
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;
@@ -596,18 +610,33 @@ namespace {
CallStackFrame &Frame;
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.
class Cleanup {
llvm::PointerIntPair<APValue*, 1, bool> Value;
+ APValue::LValueBase Base;
+ QualType T;
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(); }
- void endLifetime() {
+ bool endLifetime(EvalInfo &Info, bool RunDestructors) {
+ if (RunDestructors && T.isDestructedType())
+ return HandleDestructorCall(Info, Base, *Value.getPointer(), T);
*Value.getPointer() = APValue();
+ return true;
+ }
+
+ bool hasSideEffect() {
+ return T.isDestructedType();
}
};
@@ -623,7 +652,13 @@ namespace {
return llvm::hash_combine(Obj.Base, Obj.Path);
}
};
- enum class ConstructionPhase { None, Bases, AfterBases };
+ enum class ConstructionPhase {
+ None,
+ Bases,
+ AfterBases,
+ Destroying,
+ DestroyingBases
+ };
}
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
- isEvaluatingConstructor(APValue::LValueBase Base,
- ArrayRef<APValue::LValuePathEntry> Path) {
+ isEvaluatingCtorDtor(APValue::LValueBase Base,
+ ArrayRef<APValue::LValuePathEntry> Path) {
return ObjectsUnderConstruction.lookup({Base, Path});
}
@@ -811,6 +866,10 @@ namespace {
HasFoldFailureDiagnostic(false), InConstantContext(false),
EvalMode(Mode) {}
+ ~EvalInfo() {
+ discardCleanups();
+ }
+
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
EvaluatingDecl = Base;
EvaluatingDeclValue = &Value;
@@ -858,6 +917,25 @@ namespace {
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:
interp::Frame *getCurrentFrame() override { return CurrentCall; }
const interp::Frame *getBottomFrame() const override { return &BottomFrame; }
@@ -1101,29 +1179,37 @@ namespace {
// temporaries created in different iterations of a loop.
Info.CurrentCall->pushTempVersion();
}
+ bool destroy(bool RunDestructors = true) {
+ bool OK = cleanup(Info, RunDestructors, OldStackSize);
+ OldStackSize = -1U;
+ return OK;
+ }
~ScopeRAII() {
+ if (OldStackSize != -1U)
+ destroy(false);
// Body moved to a static method to encourage the compiler to inline away
// instances of this class.
- cleanup(Info, OldStackSize);
Info.CurrentCall->popTempVersion();
}
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;
@@ -1183,15 +1269,6 @@ CallStackFrame::~CallStackFrame() {
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) {
return AK == AK_Read || AK == AK_ReadObjectRepresentation;
}
@@ -1544,15 +1621,6 @@ static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result,
// 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
/// preserving its value (by extending by up to one bit as needed).
static void negateAsSigned(APSInt &Int) {
@@ -1563,6 +1631,18 @@ static void negateAsSigned(APSInt &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.
void CallStackFrame::describe(raw_ostream &Out) {
unsigned ArgIndex = 0;
@@ -2858,12 +2938,12 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
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
- // construction.
+ // {con,de}struction.
if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) &&
ObjType->isRecordType() &&
- Info.isEvaluatingConstructor(
+ Info.isEvaluatingCtorDtor(
Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(),
Sub.Entries.begin() + I)) !=
ConstructionPhase::None) {
@@ -3938,7 +4018,8 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
return true;
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();
if (!InitE) {
@@ -3980,7 +4061,9 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
FullExpressionRAII Scope(Info);
if (CondDecl && !EvaluateDecl(Info, CondDecl))
return false;
- return EvaluateAsBooleanCondition(Cond, Result, Info);
+ if (!EvaluateAsBooleanCondition(Cond, Result, Info))
+ return false;
+ return Scope.destroy();
}
namespace {
@@ -4016,7 +4099,12 @@ static EvalStmtResult EvaluateLoopBody(StmtResult &Result, EvalInfo &Info,
const Stmt *Body,
const SwitchCase *Case = nullptr) {
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:
return ESR_Succeeded;
case ESR_Succeeded:
@@ -4038,17 +4126,23 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
// Evaluate the switch condition.
APSInt Value;
{
- FullExpressionRAII Scope(Info);
if (const Stmt *Init = SS->getInit()) {
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;
+ }
}
+
+ FullExpressionRAII CondScope(Info);
if (SS->getConditionVariable() &&
!EvaluateDecl(Info, SS->getConditionVariable()))
return ESR_Failed;
if (!EvaluateInteger(SS->getCond(), Value, Info))
return ESR_Failed;
+ if (!CondScope.destroy())
+ return ESR_Failed;
}
// Find the switch case corresponding to the value of the condition.
@@ -4072,10 +4166,14 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
}
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.
- 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:
return ESR_Succeeded;
case ESR_Succeeded:
@@ -4143,9 +4241,19 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
// (The same is true for 'for' statements.)
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 EvaluateStmt(Result, Info, IS->getElse(), Case);
+ if (ESR != ESR_CaseNotFound)
+ return Scope.destroy() ? ESR : ESR_Failed;
+ return ESR_CaseNotFound;
}
case Stmt::WhileStmtClass: {
@@ -4176,7 +4284,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
return ESR;
if (FS->getInc()) {
FullExpressionRAII IncScope(Info);
- if (!EvaluateIgnoredValue(Info, FS->getInc()))
+ if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy())
return ESR_Failed;
}
break;
@@ -4209,8 +4317,10 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
if (const Expr *E = dyn_cast<Expr>(S)) {
// Don't bother evaluating beyond an expression-statement which couldn't
// be evaluated.
+ // FIXME: Do we need the FullExpressionRAII object here?
+ // VisitExprWithCleanups should create one when necessary.
FullExpressionRAII Scope(Info);
- if (!EvaluateIgnoredValue(Info, E))
+ if (!EvaluateIgnoredValue(Info, E) || !Scope.destroy())
return ESR_Failed;
return ESR_Succeeded;
}
@@ -4228,6 +4338,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
FullExpressionRAII Scope(Info);
if (!EvaluateDecl(Info, D) && !Info.noteFailure())
return ESR_Failed;
+ if (!Scope.destroy())
+ return ESR_Failed;
}
return ESR_Succeeded;
}
@@ -4240,7 +4352,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr)
: Evaluate(Result.Value, Info, RetExpr)))
return ESR_Failed;
- return ESR_Returned;
+ return Scope.destroy() ? ESR_Returned : ESR_Failed;
}
case Stmt::CompoundStmtClass: {
@@ -4251,10 +4363,15 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
EvalStmtResult ESR = EvaluateStmt(Result, Info, BI, Case);
if (ESR == ESR_Succeeded)
Case = nullptr;
- else if (ESR != ESR_CaseNotFound)
+ else if (ESR != ESR_CaseNotFound) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ return ESR_Failed;
return ESR;
+ }
}
- return Case ? ESR_CaseNotFound : ESR_Succeeded;
+ if (Case)
+ return ESR_CaseNotFound;
+ return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
}
case Stmt::IfStmtClass: {
@@ -4264,8 +4381,11 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
BlockScopeRAII Scope(Info);
if (const Stmt *Init = IS->getInit()) {
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;
+ }
}
bool 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()) {
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_Succeeded;
+ return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
}
case Stmt::WhileStmtClass: {
@@ -4291,8 +4414,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
break;
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;
+ }
+ if (!Scope.destroy())
+ return ESR_Failed;
}
return ESR_Succeeded;
}
@@ -4307,7 +4435,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
Case = nullptr;
FullExpressionRAII CondScope(Info);
- if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info))
+ if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info) ||
+ !CondScope.destroy())
return ESR_Failed;
} while (Continue);
return ESR_Succeeded;
@@ -4315,14 +4444,17 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
case Stmt::ForStmtClass: {
const ForStmt *FS = cast<ForStmt>(S);
- BlockScopeRAII Scope(Info);
+ BlockScopeRAII ForScope(Info);
if (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;
+ }
}
while (true) {
- BlockScopeRAII Scope(Info);
+ BlockScopeRAII IterScope(Info);
bool Continue = true;
if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(),
FS->getCond(), Continue))
@@ -4331,16 +4463,22 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
break;
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;
+ }
if (FS->getInc()) {
FullExpressionRAII IncScope(Info);
- if (!EvaluateIgnoredValue(Info, FS->getInc()))
+ if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy())
return ESR_Failed;
}
+
+ if (!IterScope.destroy())
+ return ESR_Failed;
}
- return ESR_Succeeded;
+ return ForScope.destroy() ? ESR_Succeeded : ESR_Failed;
}
case Stmt::CXXForRangeStmtClass: {
@@ -4350,22 +4488,34 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
// Evaluate the init-statement if present.
if (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;
+ }
}
// Initialize the __range variable.
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;
+ }
// Create the __begin and __end iterators.
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;
+ }
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;
+ }
while (true) {
// Condition: __begin != __end.
@@ -4381,20 +4531,29 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
// User's variable declaration, initialized by *__begin.
BlockScopeRAII InnerScope(Info);
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;
+ }
// Loop body.
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;
+ }
// Increment: ++__begin
if (!EvaluateIgnoredValue(Info, FS->getInc()))
return ESR_Failed;
+
+ if (!InnerScope.destroy())
+ return ESR_Failed;
}
- return ESR_Succeeded;
+ return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
}
case Stmt::SwitchStmtClass:
@@ -4619,16 +4778,19 @@ static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E,
ArrayRef<APValue::LValuePathEntry> Path = This.Designator.Entries;
for (unsigned PathLength = This.Designator.MostDerivedPathLength;
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:
- // 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;
case ConstructionPhase::None:
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),
PathLength};
}
@@ -5117,7 +5279,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
CXXConstructorDecl::init_const_iterator I = Definition->init_begin();
{
FullExpressionRAII InitScope(Info);
- if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()))
+ if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()) ||
+ !InitScope.destroy())
return false;
}
return EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed;
@@ -5280,7 +5443,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
}
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,
@@ -5295,6 +5459,158 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
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
//===----------------------------------------------------------------------===//
@@ -5905,10 +6221,10 @@ public:
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) {
CCEDiag(E, diag::note_constexpr_invalid_cast) << 0;
@@ -5948,7 +6264,10 @@ public:
bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
// Evaluate and cache the common expression. We treat it as a temporary,
// 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()))
return false;
@@ -6255,11 +6574,11 @@ public:
if (Info.checkingForUndefinedBehavior())
return Error(E);
- BlockScopeRAII Scope(Info);
const CompoundStmt *CS = E->getSubStmt();
if (CS->body_empty())
return true;
+ BlockScopeRAII Scope(Info);
for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
BE = CS->body_end();
/**/; ++BI) {
@@ -6270,7 +6589,7 @@ public:
diag::note_constexpr_stmt_expr_unsupported);
return false;
}
- return this->Visit(FinalExpr);
+ return this->Visit(FinalExpr) && Scope.destroy();
}
APValue ReturnValue;
@@ -6619,8 +6938,8 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
*Value = APValue();
Result.set(E);
} 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();
@@ -7152,8 +7471,8 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
if (!evaluateLValue(SubExpr, Result))
return false;
} 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))
return false;
}
@@ -7705,6 +8024,12 @@ namespace {
bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
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);
};
}
@@ -8104,7 +8429,8 @@ public:
/// Visit an expression which constructs the value of this temporary.
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);
}
@@ -8464,8 +8790,12 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
}
bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) {
+ LValue CommonLV;
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()))
return false;
@@ -12124,13 +12454,14 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
return true;
} else if (T->isArrayType()) {
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))
return false;
Result = Value;
} else if (T->isRecordType()) {
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))
return false;
Result = Value;
@@ -12144,7 +12475,7 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
QualType Unqual = T.getAtomicUnqualifiedType();
if (Unqual->isArrayType() || Unqual->isRecordType()) {
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))
return false;
} else {
@@ -12372,7 +12703,8 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx,
EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold);
Info.InConstantContext = InConstantContext;
LValue LV;
- if (!EvaluateLValue(this, LV, Info) || Result.HasSideEffects ||
+ if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() ||
+ Result.HasSideEffects ||
!CheckLValueConstantExpression(Info, getExprLoc(),
Ctx.getLValueReferenceType(getType()), LV,
Expr::EvaluateForCodeGen))
@@ -12394,6 +12726,9 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage,
if (!::Evaluate(Result.Val, Info, this))
return false;
+ if (!Info.discardCleanups())
+ llvm_unreachable("Unhandled cleanup; missing full expression marker?");
+
return CheckConstantExpression(Info, getExprLoc(), getType(), Result.Val,
Usage);
}
@@ -12457,6 +12792,13 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
EStatus.HasSideEffects)
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);
}
@@ -13063,7 +13405,11 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result,
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
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()) {
IsConstExpr = false;
@@ -13115,7 +13461,8 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
// Build fake call to Callee.
CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr,
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,
OpenPOWER on IntegriCloud