Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -2869,7 +2869,8 @@ Stmt *SubExpr; ExprWithCleanups(EmptyShell, unsigned NumObjects); - ExprWithCleanups(Expr *SubExpr, ArrayRef Objects); + ExprWithCleanups(Expr *SubExpr, bool CleanupsHaveSideEffects, + ArrayRef Objects); friend TrailingObjects; friend class ASTStmtReader; @@ -2879,6 +2880,7 @@ unsigned numObjects); static ExprWithCleanups *Create(const ASTContext &C, Expr *subexpr, + bool CleanupsHaveSideEffects, ArrayRef objects); ArrayRef getObjects() const { @@ -2895,6 +2897,9 @@ Expr *getSubExpr() { return cast(SubExpr); } const Expr *getSubExpr() const { return cast(SubExpr); } + bool cleanupsHaveSideEffects() const { + return ExprWithCleanupsBits.CleanupsHaveSideEffects; + } /// As with any mutator of the AST, be very careful /// when modifying an existing AST to preserve its invariants. Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -192,7 +192,10 @@ unsigned : NumExprBits; - unsigned NumObjects : 32 - NumExprBits; + // When false, it must not have side effects. + bool CleanupsHaveSideEffects : 1; + + unsigned NumObjects : 32 - 1 - NumExprBits; }; class PseudoObjectExprBitfields { Index: include/clang/Sema/CleanupInfo.h =================================================================== --- /dev/null +++ include/clang/Sema/CleanupInfo.h @@ -0,0 +1,47 @@ +//===--- CleanupInfo.cpp - Cleanup Control in Sema ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a set of operations on whether generating an +// ExprWithCleanups in a full expression. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_CLEANUP_INFO_H +#define LLVM_CLANG_SEMA_CLEANUP_INFO_H + +namespace clang { + +class CleanupInfo { + bool ExprNeedsCleanups = false; + bool CleanupsHaveSideEffects = false; + +public: + bool exprNeedsCleanups() const { return ExprNeedsCleanups; } + + bool cleanupsHaveSideEffects() const { return CleanupsHaveSideEffects; } + + void setExprNeedsCleanups(bool SideEffects) { + ExprNeedsCleanups = true; + CleanupsHaveSideEffects |= SideEffects; + } + + void reset() { + ExprNeedsCleanups = false; + CleanupsHaveSideEffects = false; + } + + void mergeFrom(CleanupInfo Rhs) { + ExprNeedsCleanups |= Rhs.ExprNeedsCleanups; + CleanupsHaveSideEffects |= Rhs.CleanupsHaveSideEffects; + } +}; + +} // end namespace clang + +#endif Index: include/clang/Sema/ScopeInfo.h =================================================================== --- include/clang/Sema/ScopeInfo.h +++ include/clang/Sema/ScopeInfo.h @@ -19,6 +19,7 @@ #include "clang/AST/Type.h" #include "clang/Basic/CapturedStmt.h" #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Sema/CleanupInfo.h" #include "clang/Sema/Ownership.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallSet.h" @@ -682,7 +683,7 @@ bool ExplicitParams; /// \brief Whether any of the capture expressions requires cleanups. - bool ExprNeedsCleanups; + CleanupInfo Cleanup; /// \brief Whether the lambda contains an unexpanded parameter pack. bool ContainsUnexpandedParameterPack; @@ -730,7 +731,7 @@ LambdaScopeInfo(DiagnosticsEngine &Diag) : CapturingScopeInfo(Diag, ImpCap_None), Lambda(nullptr), CallOperator(nullptr), NumExplicitCaptures(0), Mutable(false), - ExplicitParams(false), ExprNeedsCleanups(false), + ExplicitParams(false), Cleanup{}, ContainsUnexpandedParameterPack(false), AutoTemplateParameterDepth(0), GLTemplateParameterList(nullptr) { Kind = SK_Lambda; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -34,6 +34,7 @@ #include "clang/Basic/TemplateKinds.h" #include "clang/Basic/TypeTraits.h" #include "clang/Sema/AnalysisBasedWarnings.h" +#include "clang/Sema/CleanupInfo.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/IdentifierResolver.h" @@ -440,9 +441,8 @@ /// if Sema is already doing so, which would cause infinite recursions. bool IsBuildingRecoveryCallExpr; - /// ExprNeedsCleanups - True if the current evaluation context - /// requires cleanups to be run at its conclusion. - bool ExprNeedsCleanups; + /// Used to control the generation of ExprWithCleanups. + CleanupInfo Cleanup; /// ExprCleanupObjects - This is the stack of objects requiring /// cleanup that are created by the current full expression. The @@ -830,7 +830,7 @@ ExpressionEvaluationContext Context; /// \brief Whether the enclosing context needed a cleanup. - bool ParentNeedsCleanups; + CleanupInfo ParentCleanup; /// \brief Whether we are in a decltype expression. bool IsDecltype; @@ -871,10 +871,10 @@ ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context, unsigned NumCleanupObjects, - bool ParentNeedsCleanups, + CleanupInfo ParentCleanup, Decl *ManglingContextDecl, bool IsDecltype) - : Context(Context), ParentNeedsCleanups(ParentNeedsCleanups), + : Context(Context), ParentCleanup(ParentCleanup), IsDecltype(IsDecltype), NumCleanupObjects(NumCleanupObjects), NumTypos(0), ManglingContextDecl(ManglingContextDecl), MangleNumbering() { } @@ -4870,6 +4870,10 @@ Stmt *MaybeCreateStmtWithCleanups(Stmt *SubStmt); ExprResult MaybeCreateExprWithCleanups(ExprResult SubExpr); + MaterializeTemporaryExpr * + CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary, + bool BoundToLvalueReference); + ExprResult ActOnFinishFullExpr(Expr *Expr) { return ActOnFinishFullExpr(Expr, Expr ? Expr->getExprLoc() : SourceLocation()); Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -2890,7 +2890,6 @@ case CXXThrowExprClass: case CXXNewExprClass: case CXXDeleteExprClass: - case ExprWithCleanupsClass: case CoawaitExprClass: case CoyieldExprClass: // These always have a side-effect. @@ -2903,6 +2902,12 @@ return Finder.hasSideEffects(); } + case ExprWithCleanupsClass: + if (IncludePossibleEffects) + if (cast(this)->cleanupsHaveSideEffects()) + return true; + break; + case ParenExprClass: case ArraySubscriptExprClass: case OMPArraySectionExprClass: Index: lib/AST/ExprCXX.cpp =================================================================== --- lib/AST/ExprCXX.cpp +++ lib/AST/ExprCXX.cpp @@ -1039,6 +1039,7 @@ } ExprWithCleanups::ExprWithCleanups(Expr *subexpr, + bool CleanupsHaveSideEffects, ArrayRef objects) : Expr(ExprWithCleanupsClass, subexpr->getType(), subexpr->getValueKind(), subexpr->getObjectKind(), @@ -1046,16 +1047,19 @@ subexpr->isInstantiationDependent(), subexpr->containsUnexpandedParameterPack()), SubExpr(subexpr) { + ExprWithCleanupsBits.CleanupsHaveSideEffects = CleanupsHaveSideEffects; ExprWithCleanupsBits.NumObjects = objects.size(); for (unsigned i = 0, e = objects.size(); i != e; ++i) getTrailingObjects()[i] = objects[i]; } ExprWithCleanups *ExprWithCleanups::Create(const ASTContext &C, Expr *subexpr, + bool CleanupsHaveSideEffects, ArrayRef objects) { void *buffer = C.Allocate(totalSizeToAlloc(objects.size()), llvm::alignOf()); - return new (buffer) ExprWithCleanups(subexpr, objects); + return new (buffer) + ExprWithCleanups(subexpr, CleanupsHaveSideEffects, objects); } ExprWithCleanups::ExprWithCleanups(EmptyShell empty, unsigned numObjects) Index: lib/Analysis/Consumed.cpp =================================================================== --- lib/Analysis/Consumed.cpp +++ lib/Analysis/Consumed.cpp @@ -466,9 +466,15 @@ MapType PropagationMap; InfoEntry findInfo(const Expr *E) { + if (auto Cleanups = dyn_cast(E)) + if (!Cleanups->cleanupsHaveSideEffects()) + E = Cleanups->getSubExpr(); return PropagationMap.find(E->IgnoreParens()); } ConstInfoEntry findInfo(const Expr *E) const { + if (auto Cleanups = dyn_cast(E)) + if (!Cleanups->cleanupsHaveSideEffects()) + E = Cleanups->getSubExpr(); return PropagationMap.find(E->IgnoreParens()); } void insertInfo(const Expr *E, const PropagationInfo &PI) { Index: lib/CodeGen/CGCleanup.cpp =================================================================== --- lib/CodeGen/CGCleanup.cpp +++ lib/CodeGen/CGCleanup.cpp @@ -188,6 +188,7 @@ bool IsNormalCleanup = Kind & NormalCleanup; bool IsEHCleanup = Kind & EHCleanup; bool IsActive = !(Kind & InactiveCleanup); + bool IsLifetimeMarker = Kind & LifetimeMarker; EHCleanupScope *Scope = new (Buffer) EHCleanupScope(IsNormalCleanup, IsEHCleanup, @@ -200,6 +201,8 @@ InnermostNormalCleanup = stable_begin(); if (IsEHCleanup) InnermostEHScope = stable_begin(); + if (IsLifetimeMarker) + Scope->setLifetimeMarker(); return Scope->getCleanupBuffer(); } Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -528,19 +528,6 @@ CGF.EmitCall(FnInfo, CleanupFn, ReturnValueSlot(), Args); } }; - - /// A cleanup to call @llvm.lifetime.end. - class CallLifetimeEnd final : public EHScopeStack::Cleanup { - llvm::Value *Addr; - llvm::Value *Size; - public: - CallLifetimeEnd(Address addr, llvm::Value *size) - : Addr(addr.getPointer()), Size(size) {} - - void Emit(CodeGenFunction &CGF, Flags flags) override { - CGF.EmitLifetimeEnd(Size, Addr); - } - }; } // end anonymous namespace /// EmitAutoVarWithLifetime - Does the setup required for an automatic @@ -1394,13 +1381,10 @@ // Make sure we call @llvm.lifetime.end. This needs to happen // *last*, so the cleanup needs to be pushed *first*. - if (emission.useLifetimeMarkers()) { - EHStack.pushCleanup(NormalAndEHCleanup, + if (emission.useLifetimeMarkers()) + EHStack.pushCleanup(NormalEHLifetimeMarker, emission.getAllocatedAddress(), emission.getSizeForLifetimeMarkers()); - EHCleanupScope &cleanup = cast(*EHStack.begin()); - cleanup.setLifetimeMarker(); - } // Check the type for a cleanup. if (QualType::DestructionKind dtorKind = D.getType().isDestructedType()) Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// -#include "CodeGenFunction.h" #include "CGCXXABI.h" #include "CGCall.h" +#include "CGCleanup.h" #include "CGDebugInfo.h" #include "CGObjCRuntime.h" #include "CGOpenMPRuntime.h" #include "CGRecordLayout.h" +#include "CodeGenFunction.h" #include "CodeGenModule.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" @@ -423,7 +424,33 @@ EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true); } } else { + llvm::Value *Size = nullptr; + switch (M->getStorageDuration()) { + case SD_Automatic: + case SD_FullExpression: + Size = EmitLifetimeStart( + CGM.getDataLayout().getTypeAllocSize(Object.getElementType()), + Object.getPointer()); + break; + default: + break; + } EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true); + if (Size) { + switch (M->getStorageDuration()) { + case SD_Automatic: + pushCleanupAfterFullExpr(NormalEHLifetimeMarker, + Object, Size); + break; + case SD_FullExpression: + pushFullExprCleanup(NormalEHLifetimeMarker, Object, + Size); + break; + default: + llvm_unreachable("Storage duration should be either SD_Automatic or " + "SD_FullExpression"); + } + } } pushTemporaryCleanup(*this, M, E, Object); Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -764,6 +764,12 @@ return Visit(DIE->getExpr()); } + llvm::Constant *VisitExprWithCleanups(ExprWithCleanups *E) { + if (!E->cleanupsHaveSideEffects()) + return Visit(E->getSubExpr()); + return nullptr; + } + llvm::Constant *VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { return Visit(E->GetTemporaryExpr()); } Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -302,6 +302,19 @@ llvm::Instruction *CurrentFuncletPad = nullptr; + class CallLifetimeEnd final : public EHScopeStack::Cleanup { + llvm::Value *Addr; + llvm::Value *Size; + + public: + CallLifetimeEnd(Address addr, llvm::Value *size) + : Addr(addr.getPointer()), Size(size) {} + + void Emit(CodeGenFunction &CGF, Flags flags) override { + CGF.EmitLifetimeEnd(Size, Addr); + } + }; + /// Header for data within LifetimeExtendedCleanupStack. struct LifetimeExtendedCleanupHeader { /// The size of the following cleanup object. Index: lib/CodeGen/EHScopeStack.h =================================================================== --- lib/CodeGen/EHScopeStack.h +++ lib/CodeGen/EHScopeStack.h @@ -89,7 +89,10 @@ InactiveCleanup = 0x4, InactiveEHCleanup = EHCleanup | InactiveCleanup, InactiveNormalCleanup = NormalCleanup | InactiveCleanup, - InactiveNormalAndEHCleanup = NormalAndEHCleanup | InactiveCleanup + InactiveNormalAndEHCleanup = NormalAndEHCleanup | InactiveCleanup, + + LifetimeMarker = 0x8, + NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup, }; /// A stack of scopes which respond to exceptions, including cleanups Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -88,7 +88,7 @@ ConstSegStack(nullptr), CodeSegStack(nullptr), CurInitSeg(nullptr), VisContext(nullptr), IsBuildingRecoveryCallExpr(false), - ExprNeedsCleanups(false), LateTemplateParser(nullptr), + Cleanup{}, LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp), StdInitializerList(nullptr), CXXTypeInfoDecl(nullptr), MSVCGuidDecl(nullptr), @@ -124,7 +124,8 @@ // Tell diagnostics how to render things from the AST library. Diags.SetArgToStringFn(&FormatASTNodeDiagnosticArgument, &Context); - ExprEvalContexts.emplace_back(PotentiallyEvaluated, 0, false, nullptr, false); + ExprEvalContexts.emplace_back(PotentiallyEvaluated, 0, CleanupInfo{}, nullptr, + false); FunctionScopes.push_back(new FunctionScopeInfo(Diags)); Index: lib/Sema/SemaCast.cpp =================================================================== --- lib/Sema/SemaCast.cpp +++ lib/Sema/SemaCast.cpp @@ -641,8 +641,8 @@ // If we're dynamic_casting from a prvalue to an rvalue reference, we need // to materialize the prvalue before we bind the reference to it. if (SrcExpr.get()->isRValue()) - SrcExpr = new (Self.Context) MaterializeTemporaryExpr( - SrcType, SrcExpr.get(), /*IsLValueReference*/false); + SrcExpr = Self.CreateMaterializeTemporaryExpr( + SrcType, SrcExpr.get(), /*IsLValueReference*/ false); SrcPointee = SrcType; } @@ -1649,8 +1649,8 @@ if (NeedToMaterializeTemporary) // This is a const_cast from a class prvalue to an rvalue reference type. // Materialize a temporary to store the result of the conversion. - SrcExpr = new (Self.Context) MaterializeTemporaryExpr( - SrcType, SrcExpr.get(), /*IsLValueReference*/ false); + SrcExpr = Self.CreateMaterializeTemporaryExpr(SrcType, SrcExpr.get(), + /*IsLValueReference*/ false); return TC_Success; } Index: lib/Sema/SemaCoroutine.cpp =================================================================== --- lib/Sema/SemaCoroutine.cpp +++ lib/Sema/SemaCoroutine.cpp @@ -244,7 +244,7 @@ // If the expression is a temporary, materialize it as an lvalue so that we // can use it multiple times. if (E->getValueKind() == VK_RValue) - E = new (Context) MaterializeTemporaryExpr(E->getType(), E, true); + E = CreateMaterializeTemporaryExpr(E->getType(), E, true); // Build the await_ready, await_suspend, await_resume calls. ReadySuspendResumeResult RSS = buildCoawaitCalls(*this, Loc, E); @@ -311,7 +311,7 @@ // If the expression is a temporary, materialize it as an lvalue so that we // can use it multiple times. if (E->getValueKind() == VK_RValue) - E = new (Context) MaterializeTemporaryExpr(E->getType(), E, true); + E = CreateMaterializeTemporaryExpr(E->getType(), E, true); // Build the await_ready, await_suspend, await_resume calls. ReadySuspendResumeResult RSS = buildCoawaitCalls(*this, Loc, E); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -11641,7 +11641,7 @@ assert(ExprCleanupObjects.size() == ExprEvalContexts.back().NumCleanupObjects && "Leftover temporaries in function"); - assert(!ExprNeedsCleanups && "Unaccounted cleanups in function"); + assert(!Cleanup.exprNeedsCleanups() && "Unaccounted cleanups in function"); assert(MaybeODRUseExprs.empty() && "Leftover expressions for odr-use checking"); } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -712,7 +712,7 @@ // balance that. if (getLangOpts().ObjCAutoRefCount && E->getType().getObjCLifetime() == Qualifiers::OCL_Weak) - ExprNeedsCleanups = true; + Cleanup.setExprNeedsCleanups(true); ExprResult Res = ImplicitCastExpr::Create(Context, T, CK_LValueToRValue, E, nullptr, VK_RValue); @@ -4573,15 +4573,15 @@ // bound temporaries; see the comment in PR5810. // We don't need to do that with block decls, though, because // blocks in default argument expression can never capture anything. - if (isa(Param->getInit())) { + if (auto Init = dyn_cast(Param->getInit())) { // Set the "needs cleanups" bit regardless of whether there are // any explicit objects. - ExprNeedsCleanups = true; + Cleanup.setExprNeedsCleanups(Init->cleanupsHaveSideEffects()); // Append all the objects to the cleanup list. Right now, this // should always be a no-op, because blocks in default argument // expressions should never be able to capture anything. - assert(!cast(Param->getInit())->getNumObjects() && + assert(!Init->getNumObjects() && "default argument expression has capturing blocks?"); } @@ -5596,7 +5596,7 @@ E = ImplicitCastExpr::Create(Context, E.get()->getType(), CK_ARCExtendBlockObject, E.get(), /*base path*/ nullptr, VK_RValue); - ExprNeedsCleanups = true; + Cleanup.setExprNeedsCleanups(true); } /// Prepare a conversion of the given expression to an ObjC object @@ -10382,8 +10382,8 @@ if (sfinae) return QualType(); // Materialize the temporary as an lvalue so that we can take its address. - OrigOp = op = new (Context) - MaterializeTemporaryExpr(op->getType(), OrigOp.get(), true); + OrigOp = op = + CreateMaterializeTemporaryExpr(op->getType(), OrigOp.get(), true); } else if (isa(op)) { return Context.getPointerType(op->getType()); } else if (lval == Expr::LV_MemberFunction) { @@ -11596,7 +11596,8 @@ if (hasAnyUnrecoverableErrorsInThisFunction()) DiscardCleanupsInEvaluationContext(); - assert(!ExprNeedsCleanups && "cleanups within StmtExpr not correctly bound!"); + assert(!Cleanup.exprNeedsCleanups() && + "cleanups within StmtExpr not correctly bound!"); PopExpressionEvaluationContext(); // FIXME: there are a variety of strange constraints to enforce here, for @@ -12064,7 +12065,8 @@ // Leave the expression-evaluation context. if (hasAnyUnrecoverableErrorsInThisFunction()) DiscardCleanupsInEvaluationContext(); - assert(!ExprNeedsCleanups && "cleanups within block not correctly bound!"); + assert(!Cleanup.exprNeedsCleanups() && + "cleanups within block not correctly bound!"); PopExpressionEvaluationContext(); BlockScopeInfo *BSI = cast(FunctionScopes.back()); @@ -12155,7 +12157,7 @@ if (Result->getBlockDecl()->hasCaptures()) { // First, this expression has a new cleanup object. ExprCleanupObjects.push_back(Result->getBlockDecl()); - ExprNeedsCleanups = true; + Cleanup.setExprNeedsCleanups(true); // It also gets a branch-protected scope if any of the captured // variables needs destruction. @@ -12792,10 +12794,9 @@ Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext, Decl *LambdaContextDecl, bool IsDecltype) { - ExprEvalContexts.emplace_back(NewContext, ExprCleanupObjects.size(), - ExprNeedsCleanups, LambdaContextDecl, - IsDecltype); - ExprNeedsCleanups = false; + ExprEvalContexts.emplace_back(NewContext, ExprCleanupObjects.size(), Cleanup, + LambdaContextDecl, IsDecltype); + Cleanup.reset(); if (!MaybeODRUseExprs.empty()) std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs); } @@ -12846,12 +12847,12 @@ if (Rec.isUnevaluated() || Rec.Context == ConstantEvaluated) { ExprCleanupObjects.erase(ExprCleanupObjects.begin() + Rec.NumCleanupObjects, ExprCleanupObjects.end()); - ExprNeedsCleanups = Rec.ParentNeedsCleanups; + Cleanup = Rec.ParentCleanup; CleanupVarDeclMarking(); std::swap(MaybeODRUseExprs, Rec.SavedMaybeODRUseExprs); // Otherwise, merge the contexts together. } else { - ExprNeedsCleanups |= Rec.ParentNeedsCleanups; + Cleanup.mergeFrom(Rec.ParentCleanup); MaybeODRUseExprs.insert(Rec.SavedMaybeODRUseExprs.begin(), Rec.SavedMaybeODRUseExprs.end()); } @@ -12870,7 +12871,7 @@ ExprCleanupObjects.erase( ExprCleanupObjects.begin() + ExprEvalContexts.back().NumCleanupObjects, ExprCleanupObjects.end()); - ExprNeedsCleanups = false; + Cleanup.reset(); MaybeODRUseExprs.clear(); } Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -5549,7 +5549,7 @@ if (!ReturnsRetained && E->getType()->isObjCARCImplicitlyUnretainedType()) return E; - ExprNeedsCleanups = true; + Cleanup.setExprNeedsCleanups(true); CastKind ck = (ReturnsRetained ? CK_ARCConsumeObject : CK_ARCReclaimReturnedObject); @@ -5602,7 +5602,7 @@ return E; // We need a cleanup, but we don't need to remember the temporary. - ExprNeedsCleanups = true; + Cleanup.setExprNeedsCleanups(true); } CXXTemporary *Temp = CXXTemporary::Create(Context, Destructor); @@ -5629,14 +5629,16 @@ unsigned FirstCleanup = ExprEvalContexts.back().NumCleanupObjects; assert(ExprCleanupObjects.size() >= FirstCleanup); - assert(ExprNeedsCleanups || ExprCleanupObjects.size() == FirstCleanup); - if (!ExprNeedsCleanups) + assert(Cleanup.exprNeedsCleanups() || + ExprCleanupObjects.size() == FirstCleanup); + if (!Cleanup.exprNeedsCleanups()) return SubExpr; auto Cleanups = llvm::makeArrayRef(ExprCleanupObjects.begin() + FirstCleanup, ExprCleanupObjects.size() - FirstCleanup); - Expr *E = ExprWithCleanups::Create(Context, SubExpr, Cleanups); + auto *E = ExprWithCleanups::Create( + Context, SubExpr, Cleanup.cleanupsHaveSideEffects(), Cleanups); DiscardCleanupsInEvaluationContext(); return E; @@ -5647,7 +5649,7 @@ CleanupVarDeclMarking(); - if (!ExprNeedsCleanups) + if (!Cleanup.exprNeedsCleanups()) return SubStmt; // FIXME: In order to attach the temporaries, wrap the statement into @@ -5753,7 +5755,7 @@ return ExprError(); // We need a cleanup, but we don't need to remember the temporary. - ExprNeedsCleanups = true; + Cleanup.setExprNeedsCleanups(true); } // Possibly strip off the top CXXBindTemporaryExpr. Index: lib/Sema/SemaExprObjC.cpp =================================================================== --- lib/Sema/SemaExprObjC.cpp +++ lib/Sema/SemaExprObjC.cpp @@ -4067,7 +4067,7 @@ castExpr = ImplicitCastExpr::Create(Context, castExpr->getType(), CK_ARCConsumeObject, castExpr, nullptr, VK_RValue); - ExprNeedsCleanups = true; + Cleanup.setExprNeedsCleanups(true); return ACR_okay; } @@ -4310,7 +4310,7 @@ TSInfo, SubExpr); if (MustConsume) { - ExprNeedsCleanups = true; + Cleanup.setExprNeedsCleanups(true); Result = ImplicitCastExpr::Create(Context, T, CK_ARCConsumeObject, Result, nullptr, VK_RValue); } Index: lib/Sema/SemaInit.cpp =================================================================== --- lib/Sema/SemaInit.cpp +++ lib/Sema/SemaInit.cpp @@ -4807,8 +4807,8 @@ // If isWeakAccess to true, there will be an implicit // load which requires a cleanup. if (S.getLangOpts().ObjCAutoRefCount && isWeakAccess) - S.ExprNeedsCleanups = true; - + S.Cleanup.setExprNeedsCleanups(true); + if (iik == IIK_okay) return; S.Diag(src->getExprLoc(), diag::err_arc_nonlocal_writeback) @@ -6182,6 +6182,22 @@ } } +MaterializeTemporaryExpr * +Sema::CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary, + bool BoundToLvalueReference) { + auto MTE = new (Context) + MaterializeTemporaryExpr(T, Temporary, BoundToLvalueReference); + + // Order an ExprWithCleanups for lifetime marks. + // + // TODO: It'll be good to have a single place to check the access of the + // destructor and generate ExprWithCleanups for various uses. Currently these + // are done in both CreateMaterializeTemporaryExpr and MaybeBindToTemporary, + // but there may be a chance to merge them. + Cleanup.setExprNeedsCleanups(false); + return MTE; +} + ExprResult InitializationSequence::Perform(Sema &S, const InitializedEntity &Entity, @@ -6446,7 +6462,7 @@ return ExprError(); // Materialize the temporary into memory. - MaterializeTemporaryExpr *MTE = new (S.Context) MaterializeTemporaryExpr( + MaterializeTemporaryExpr *MTE = S.CreateMaterializeTemporaryExpr( Entity.getType().getNonReferenceType(), CurInit.get(), Entity.getType()->isLValueReferenceType()); @@ -6466,7 +6482,7 @@ MTE->getType()->isObjCLifetimeType()) || (MTE->getStorageDuration() == SD_Automatic && MTE->getType().isDestructedType())) - S.ExprNeedsCleanups = true; + S.Cleanup.setExprNeedsCleanups(true); CurInit = MTE; break; @@ -6858,9 +6874,9 @@ << CurInit.get()->getSourceRange(); // Materialize the temporary into memory. - MaterializeTemporaryExpr *MTE = new (S.Context) - MaterializeTemporaryExpr(CurInit.get()->getType(), CurInit.get(), - /*BoundToLvalueReference=*/false); + MaterializeTemporaryExpr *MTE = S.CreateMaterializeTemporaryExpr( + CurInit.get()->getType(), CurInit.get(), + /*BoundToLvalueReference=*/false); // Maybe lifetime-extend the array temporary's subobjects to match the // entity's lifetime. Index: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ lib/Sema/SemaLambda.cpp @@ -1500,7 +1500,7 @@ SourceRange IntroducerRange; bool ExplicitParams; bool ExplicitResultType; - bool LambdaExprNeedsCleanups; + CleanupInfo LambdaCleanup; bool ContainsUnexpandedParameterPack; SmallVector ArrayIndexVars; SmallVector ArrayIndexStarts; @@ -1510,7 +1510,7 @@ IntroducerRange = LSI->IntroducerRange; ExplicitParams = LSI->ExplicitParams; ExplicitResultType = !LSI->HasImplicitReturnType; - LambdaExprNeedsCleanups = LSI->ExprNeedsCleanups; + LambdaCleanup = LSI->Cleanup; ContainsUnexpandedParameterPack = LSI->ContainsUnexpandedParameterPack; CallOperator->setLexicalDeclContext(Class); @@ -1591,9 +1591,8 @@ CheckCompletedCXXClass(Class); } - if (LambdaExprNeedsCleanups) - ExprNeedsCleanups = true; - + Cleanup.mergeFrom(LambdaCleanup); + LambdaExpr *Lambda = LambdaExpr::Create(Context, Class, IntroducerRange, CaptureDefault, CaptureDefaultLoc, Captures, @@ -1714,7 +1713,7 @@ // Create the block literal expression. Expr *BuildBlock = new (Context) BlockExpr(Block, Conv->getConversionType()); ExprCleanupObjects.push_back(Block); - ExprNeedsCleanups = true; + Cleanup.setExprNeedsCleanups(true); return BuildBlock; } Index: lib/Sema/SemaOpenMP.cpp =================================================================== --- lib/Sema/SemaOpenMP.cpp +++ lib/Sema/SemaOpenMP.cpp @@ -3792,6 +3792,10 @@ } return true; } + if (auto *ExprTemp = dyn_cast(S)) + if (!ExprTemp->cleanupsHaveSideEffects()) + S = ExprTemp->getSubExpr(); + InitSrcRange = S->getSourceRange(); if (Expr *E = dyn_cast(S)) S = E->IgnoreParens(); @@ -3979,6 +3983,10 @@ SemaRef.Diag(DefaultLoc, diag::err_omp_loop_not_canonical_incr) << LCDecl; return true; } + if (auto *ExprTemp = dyn_cast(S)) + if (!ExprTemp->cleanupsHaveSideEffects()) + S = ExprTemp->getSubExpr(); + IncrementSrcRange = S->getSourceRange(); S = S->IgnoreParens(); if (auto UO = dyn_cast(S)) { Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -1518,6 +1518,10 @@ // variables Increment and DRE. bool ProcessIterationStmt(Sema &S, Stmt* Statement, bool &Increment, DeclRefExpr *&DRE) { + if (auto Cleanups = dyn_cast(Statement)) + if (!Cleanups->cleanupsHaveSideEffects()) + Statement = Cleanups->getSubExpr(); + if (UnaryOperator *UO = dyn_cast(Statement)) { switch (UO->getOpcode()) { default: return false; @@ -2472,6 +2476,10 @@ QualType VariableType = VD->getType(); + if (auto Cleanups = dyn_cast(InitExpr)) + if (!Cleanups->cleanupsHaveSideEffects()) + InitExpr = Cleanups->getSubExpr(); + const MaterializeTemporaryExpr *MTE = dyn_cast(InitExpr); Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -1448,6 +1448,7 @@ E->getTrailingObjects()[i] = ReadDeclAs(Record, Idx); + E->ExprWithCleanupsBits.CleanupsHaveSideEffects = Record[Idx++]; E->SubExpr = Reader.ReadSubExpr(); } Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -1430,7 +1430,8 @@ Record.push_back(E->getNumObjects()); for (unsigned i = 0, e = E->getNumObjects(); i != e; ++i) Record.AddDeclRef(E->getObject(i)); - + + Record.push_back(E->cleanupsHaveSideEffects()); Record.AddStmt(E->getSubExpr()); Code = serialization::EXPR_EXPR_WITH_CLEANUPS; } Index: test/CodeGen/temporary-lifetime.cpp =================================================================== --- /dev/null +++ test/CodeGen/temporary-lifetime.cpp @@ -0,0 +1,165 @@ +// RUN: %clang_cc1 %s -std=c++11 -O1 -DWITH_DTOR -triple x86_64 -emit-llvm -o - | FileCheck -check-prefix=CHECK-DTOR %s +// RUN: %clang_cc1 %s -std=c++11 -O1 -triple x86_64 -emit-llvm -o - | FileCheck -check-prefix=CHECK-NO-DTOR %s + +struct A { + A(); +#ifdef WITH_DTOR + ~A(); +#endif + char a[1024]; + operator bool() const; +}; + +template +void Foo(T &&); + +template +void Bar(T &&); + +template +T Baz(); + +void Test1() { + // CHECK-DTOR-LABEL: Test1 + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-DTOR: } + + // CHECK-NO-DTOR-LABEL: Test1 + // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]]) + // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]]) + // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-NO-DTOR: } + { + const A &a = A{}; + Foo(a); + } + { + const A &a = A{}; + Foo(a); + } +} + +void Test2() { + // CHECK-DTOR-LABEL: Test2 + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR1:[0-9]+]]) + // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR1:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR2:[0-9]+]]) + // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR2:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR2]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR2]]) + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR1]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR1]]) + // CHECK-DTOR: } + + // CHECK-NO-DTOR-LABEL: Test2 + // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR1:[0-9]+]]) + // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR1:[^ ]+]]) + // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR2:[0-9]+]]) + // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR2:[^ ]+]]) + // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR2]]) + // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR1]]) + // CHECK-NO-DTOR: } + const A &a = A{}; + Foo(a); + const A &b = A{}; + Foo(b); +} + +void Test3() { + // CHECK-DTOR-LABEL: Test3 + // CHECK-DTOR: entry: + // CHECK-DTOR: call void @llvm.lifetime.start + // CHECK-DTOR: call void @llvm.lifetime.start + // CHECK-DTOR: if.then: + // CHECK-DTOR: call void @llvm.lifetime.end + // CHECK-DTOR: cleanup{{.*}}: + // CHECK-DTOR: call void @llvm.lifetime.end + // CHECK-DTOR: cleanup{{.*}}: + // CHECK-DTOR: call void @llvm.lifetime.end + // CHECK-DTOR: } + const A &a = A{}; + if (const A &b = A(a)) { + Foo(b); + return; + } + Bar(a); +} + +void Test4() { + // CHECK-DTOR-LABEL: Test4 + // CHECK-DTOR: entry: + // CHECK-DTOR: call void @llvm.lifetime.start + // CHECK-DTOR: for.cond.cleanup: + // CHECK-DTOR: call void @llvm.lifetime.end + // CHECK-DTOR: for.body: + // CHECK-DTOR: } + for (const A &a = A{}; a;) { + Foo(a); + } +} + +int Test5() { + // CHECK-DTOR-LABEL: Test5 + // CHECK-DTOR: call void @llvm.lifetime.start + // CHECK-DTOR: call i32 @_Z3BazIiET_v() + // CHECK-DTOR: store + // CHECK-DTOR: call void @_Z3FooIRKiEvOT_ + // CHECK-DTOR: load + // CHECK-DTOR: call void @llvm.lifetime.end + // CHECK-DTOR: } + const int &a = Baz(); + Foo(a); + return a; +} + +void Test6() { + // CHECK-DTOR-LABEL: Test6 + // CHECK-DTOR: call void @llvm.lifetime.start(i64 {{[0-9]+}}, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call i32 @_Z3BazIiET_v() + // CHECK-DTOR: store + // CHECK-DTOR: call void @_Z3FooIiEvOT_ + // CHECK-DTOR: call void @llvm.lifetime.end(i64 {{[0-9]+}}, i8* %[[ADDR]]) + // CHECK-DTOR: call void @llvm.lifetime.start(i64 {{[0-9]+}}, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call i32 @_Z3BazIiET_v() + // CHECK-DTOR: store + // CHECK-DTOR: call void @_Z3FooIiEvOT_ + // CHECK-DTOR: call void @llvm.lifetime.end(i64 {{[0-9]+}}, i8* %[[ADDR]]) + // CHECK-DTOR: } + Foo(Baz()); + Foo(Baz()); +} + +void Test7() { + // CHECK-DTOR-LABEL: Test7 + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call void @_Z3BazI1AET_v({{.*}} %[[SLOT:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooI1AEvOT_({{.*}} %[[SLOT]]) + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[SLOT]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call void @_Z3BazI1AET_v({{.*}} %[[SLOT:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooI1AEvOT_({{.*}} %[[SLOT]]) + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[SLOT]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-DTOR: } + Foo(Baz()); + Foo(Baz()); +} Index: test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp =================================================================== --- test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp +++ test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 %s -// RUN: %clang_cc1 -std=c++11 -emit-llvm -O3 -disable-llvm-optzns %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 -check-prefix WIN32-LIFETIME %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 -check-prefix WIN32-O0 %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm -O3 -disable-llvm-optzns %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 -check-prefix WIN32-O3 -check-prefix WIN32-LIFETIME %s struct A { A(); @@ -95,40 +95,78 @@ return (cond ? TakesTwo((TakeRef(A()), A()), (TakeRef(A()), A())) : CouldThrow()); } -// WIN32-LABEL: define i32 @"\01?HasConditionalDeactivatedCleanups@@YAH_N@Z"{{.*}} { -// WIN32: alloca i1 -// WIN32: %[[arg1_cond:.*]] = alloca i1 +// WIN32-O0-LABEL: define i32 @"\01?HasConditionalDeactivatedCleanups@@YAH_N@Z"{{.*}} { +// WIN32-O0: alloca i1 +// WIN32-O0: %[[arg1_cond:.*]] = alloca i1 // Start all four cleanups as deactivated. -// WIN32: store i1 false -// WIN32: store i1 false -// WIN32: store i1 false -// WIN32: store i1 false -// WIN32: br i1 +// WIN32-O0: store i1 false +// WIN32-O0: store i1 false +// WIN32-O0: store i1 false +// WIN32-O0: store i1 false +// WIN32-O0: br i1 // True condition. -// WIN32: call x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" -// WIN32: store i1 true -// WIN32: invoke void @"\01?TakeRef@@YAXABUA@@@Z" -// WIN32: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" -// WIN32: store i1 true, i1* %[[arg1_cond]] -// WIN32: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" -// WIN32: store i1 true -// WIN32: invoke void @"\01?TakeRef@@YAXABUA@@@Z" -// WIN32: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" -// WIN32: store i1 true -// WIN32: store i1 false, i1* %[[arg1_cond]] -// WIN32: invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z" +// WIN32-O0: call x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O0: store i1 true +// WIN32-O0: invoke void @"\01?TakeRef@@YAXABUA@@@Z" +// WIN32-O0: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O0: store i1 true, i1* %[[arg1_cond]] +// WIN32-O0: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O0: store i1 true +// WIN32-O0: invoke void @"\01?TakeRef@@YAXABUA@@@Z" +// WIN32-O0: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O0: store i1 true +// WIN32-O0: store i1 false, i1* %[[arg1_cond]] +// WIN32-O0: invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z" // False condition. -// WIN32: invoke i32 @"\01?CouldThrow@@YAHXZ"() +// WIN32-O0: invoke i32 @"\01?CouldThrow@@YAHXZ"() // Two normal cleanups for TakeRef args. -// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) -// WIN32-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ" -// WIN32: ret i32 +// WIN32-O0: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) +// WIN32-O0-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ" +// WIN32-O0: ret i32 // // Somewhere in the landing pad soup, we conditionally destroy arg1. -// WIN32: %[[isactive:.*]] = load i1, i1* %[[arg1_cond]] -// WIN32: br i1 %[[isactive]] -// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) -// WIN32: } +// WIN32-O0: %[[isactive:.*]] = load i1, i1* %[[arg1_cond]] +// WIN32-O0: br i1 %[[isactive]] +// WIN32-O0: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) +// WIN32-O0: } + +// WIN32-O3-LABEL: define i32 @"\01?HasConditionalDeactivatedCleanups@@YAH_N@Z"{{.*}} { +// WIN32-O3: alloca i1 +// WIN32-O3: alloca i1 +// WIN32-O3: %[[arg1_cond:.*]] = alloca i1 +// Start all four cleanups as deactivated. +// WIN32-O3: store i1 false +// WIN32-O3: store i1 false +// WIN32-O3: store i1 false +// WIN32-O3: store i1 false +// WIN32-O3: store i1 false +// WIN32-O3: store i1 false +// WIN32-O3: br i1 +// True condition. +// WIN32-O3: call x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O3: store i1 true +// WIN32-O3: invoke void @"\01?TakeRef@@YAXABUA@@@Z" +// WIN32-O3: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O3: store i1 true, i1* %[[arg1_cond]] +// WIN32-O3: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O3: store i1 true +// WIN32-O3: invoke void @"\01?TakeRef@@YAXABUA@@@Z" +// WIN32-O3: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O3: store i1 true +// WIN32-O3: store i1 false, i1* %[[arg1_cond]] +// WIN32-O3: invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z" +// False condition. +// WIN32-O3: invoke i32 @"\01?CouldThrow@@YAHXZ"() +// Two normal cleanups for TakeRef args. +// WIN32-O3: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) +// WIN32-O3-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ" +// WIN32-O3: ret i32 +// +// Somewhere in the landing pad soup, we conditionally destroy arg1. +// WIN32-O3: %[[isactive:.*]] = load i1, i1* %[[arg1_cond]] +// WIN32-O3: br i1 %[[isactive]] +// WIN32-O3: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) +// WIN32-O3: } namespace crash_on_partial_destroy { struct A { Index: unittests/ASTMatchers/ASTMatchersNodeTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -999,8 +999,8 @@ EXPECT_TRUE(matches("struct Foo { ~Foo(); };" "const Foo f = Foo();", varDecl(hasInitializer(exprWithCleanups())))); - EXPECT_FALSE(matches("struct Foo { };" - "const Foo f = Foo();", + EXPECT_FALSE(matches("struct Foo { }; Foo a;" + "const Foo f = a;", varDecl(hasInitializer(exprWithCleanups())))); }