Author: Morris Hafner
Date: 2026-10-24T13:40:08+02:00
New Revision: 04b14262efc4b0c31c5571f5cd354e5b413d6f4c

URL: 
https://github.com/llvm/llvm-project/commit/04b14262efc4b0c31c5571f5cd354e5b413d6f4c
DIFF: 
https://github.com/llvm/llvm-project/commit/04b14262efc4b0c31c5571f5cd354e5b413d6f4c.diff

LOG: [CIR] Fix temporary cleanup boundaries for asm and coroutines (#234664)

- Destroy asm operand temporaries at the end of the asm statement.
- Defer resume-expression temporary cleanups to the end of the enclosing
full-expression
- Clean up array-element default-argument temporaries before
constructing the next element, preserving lifetime extension.
- Keep conditional cleanup scopes open until the end of their owning
full-expression.

Note that this overlaps with #234303. I will rebase either depending on
which one gets merged first.

Assisted-By: Claude Opus 5.5

Added: 
    

Modified: 
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/CodeGen/CIRGenAsm.cpp
    clang/lib/CIR/CodeGen/CIRGenClass.cpp
    clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
    clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
    clang/lib/CIR/CodeGen/CIRGenDecl.cpp
    clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.h
    clang/test/CIR/CodeGen/array-ctor.cpp
    clang/test/CIR/CodeGen/cleanup-conditional-eh.cpp
    clang/test/CIR/CodeGen/cleanup-conditional-with-wrapper.cpp
    clang/test/CIR/CodeGen/cleanup-conditional.cpp
    clang/test/CIR/CodeGen/cleanup-scope-tmp.cpp
    clang/test/CIR/CodeGenCoroutines/coro-agg.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 7beb06541f7b64..ed7c2b9cfc92a1 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -119,7 +119,6 @@ struct MissingFeatures {
   static bool noUniqueAddressLayout() { return false; }
 
   // Coroutines
-  static bool coroAwaitFullExprCleanups() { return false; }
   static bool coroOutsideFrameMD() { return false; }
   static bool coroGetRetObjManager() { return false; };
   static bool coroUnwindRegion() { return false; };

diff  --git a/clang/lib/CIR/CodeGen/CIRGenAsm.cpp 
b/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
index 31b5ff07ecf641..c3c676bd742c12 100644
--- a/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
@@ -261,6 +261,8 @@ static void emitAsmStores(CIRGenFunction &cgf, const 
AsmStmt &s,
 }
 
 mlir::LogicalResult CIRGenFunction::emitAsmStmt(const AsmStmt &s) {
+  FullExprCleanupScope cleanups(*this, &s);
+
   // Assemble the final asm string.
   std::string asmString = s.generateAsmString(getContext());
   SourceLocation srcLoc = s.getAsmLoc();

diff  --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index bcf301a0b5ae20..738f5464672435 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -790,7 +790,8 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
     // destruction of every temporary created in a default argument expression
     // is sequenced before the construction of the next array element, if any.
     {
-      RunCleanupsScope scope(*this);
+      FullExprCleanupScope scope(*this, e);
+      scope.deferLifetimeExtendedCleanups();
       emitCXXConstructorCall(ctor, Ctor_Complete,
                              /*ForVirtualBase=*/false,
                              /*Delegating=*/false, currAVS, e);

diff  --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index 98fb9502ef0169..63e9dc24acbcfe 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -20,7 +20,7 @@
 #include "CIRGenFunction.h"
 
 #include "clang/AST/RecursiveASTVisitor.h"
-#include "clang/CIR/MissingFeatures.h"
+#include "llvm/Support/SaveAndRestore.h"
 
 using namespace clang;
 using namespace clang::CIRGen;
@@ -31,14 +31,39 @@ namespace {
 class ConditionalEvaluationFinder
     : public RecursiveASTVisitor<ConditionalEvaluationFinder> {
   bool foundConditional = false;
+  bool inResumeExpr = false;
+
+  bool traverseSuspendExpr(CoroutineSuspendExpr *e) {
+    return TraverseStmt(e->getCommonExpr()) && traverseResumeExpr(e);
+  }
+
+  /// Traverse the resume expression of \p e, except for the binding of its
+  /// result, which emitSuspendExpression destroys after the cir.await.
+  bool traverseResumeExpr(CoroutineSuspendExpr *e) {
+    Expr *resume = e->getResumeExpr();
+    if (auto *bind = dyn_cast_if_present<CXXBindTemporaryExpr>(resume))
+      resume = bind->getSubExpr();
+    llvm::SaveAndRestore inResume(inResumeExpr, true);
+    return TraverseStmt(resume);
+  }
 
 public:
+  static bool needsCleanupScope(const Stmt *s) {
+    ConditionalEvaluationFinder finder;
+    finder.TraverseStmt(const_cast<Stmt *>(s));
+    return finder.foundConditional;
+  }
+
+  static bool resumeExprDefersCleanups(const CoroutineSuspendExpr &s) {
+    ConditionalEvaluationFinder finder;
+    finder.traverseResumeExpr(const_cast<CoroutineSuspendExpr *>(&s));
+    return finder.foundConditional;
+  }
+
   // Default arguments and default member initializers can contain conditional
   // temporaries whose cleanups belong to the enclosing full-expression.
   bool shouldVisitImplicitCode() const { return true; }
 
-  bool found() const { return foundConditional; }
-
   bool VisitAbstractConditionalOperator(AbstractConditionalOperator *) {
     foundConditional = true;
     return false;
@@ -74,19 +99,31 @@ class ConditionalEvaluationFinder
   bool TraverseBlockExpr(BlockExpr *) { return true; }
   bool TraverseStmtExpr(StmtExpr *) { return true; }
 
-  // Skip over the implicit call to await_resume(). This requires cleanup
-  // scopes for await full-expressions which don't exist yet.
-  bool TraverseCoawaitExpr(CoawaitExpr *e) {
-    assert(!cir::MissingFeatures::coroAwaitFullExprCleanups());
-    return TraverseStmt(e->getOperand());
+  bool TraverseGCCAsmStmt(GCCAsmStmt *s) {
+    for (Stmt *operand : s->children())
+      if (!TraverseStmt(operand))
+        return false;
+    return true;
   }
-  bool TraverseDependentCoawaitExpr(DependentCoawaitExpr *e) {
-    assert(!cir::MissingFeatures::coroAwaitFullExprCleanups());
-    return TraverseStmt(e->getOperand());
+
+  // Traverse the common and resume expressions, whose temporaries belong to
+  // the enclosing full-expression. Ready and suspend have their own cleanup
+  // boundaries. The resume result's cleanup is handled separately.
+  bool TraverseCoawaitExpr(CoawaitExpr *e) { return traverseSuspendExpr(e); }
+  bool TraverseCoyieldExpr(CoyieldExpr *e) { return traverseSuspendExpr(e); }
+
+  bool VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *) {
+    if (!inResumeExpr)
+      return true;
+    foundConditional = true;
+    return false;
   }
-  bool TraverseCoyieldExpr(CoyieldExpr *e) {
-    assert(!cir::MissingFeatures::coroAwaitFullExprCleanups());
-    return TraverseStmt(e->getOperand());
+
+  bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *e) {
+    if (!inResumeExpr || !e->getType().isDestructedType())
+      return true;
+    foundConditional = true;
+    return false;
   }
 };
 } // namespace
@@ -95,6 +132,10 @@ class ConditionalEvaluationFinder
 // CIRGenFunction cleanup related
 
//===----------------------------------------------------------------------===//
 
+bool CIRGenFunction::resumeExprDefersCleanups(const CoroutineSuspendExpr &s) {
+  return ConditionalEvaluationFinder::resumeExprDefersCleanups(s);
+}
+
 /// Emits all the code to cause the given temporary to be cleaned up.
 void CIRGenFunction::emitCXXTemporary(const CXXTemporary *temporary,
                                       QualType tempType, Address ptr) {
@@ -143,16 +184,16 @@ void CIRGenFunction::initFullExprCleanupWithFlag(Address 
activeFlag) {
 }
 
 CIRGenFunction::FullExprCleanupScope::FullExprCleanupScope(CIRGenFunction &cgf,
-                                                           const Expr *subExpr)
+                                                           const Stmt *s)
     : cgf(cgf), cleanups(cgf), scope(nullptr),
       oldFullExprCleanupScope(cgf.currentFullExprCleanupScope),
+      oldOutermostConditional(cgf.outermostConditional),
+      oldHiddenOutermostConditional(cgf.hiddenOutermostConditional),
       deferredCleanupStackSize(cgf.deferredConditionalCleanupStack.size()),
       conditionalScopeDepth(cgf.conditionalCleanupScopes.size()) {
 
-  assert(subExpr && "ExprWithCleanups always has a sub-expression");
-  ConditionalEvaluationFinder finder;
-  finder.TraverseStmt(const_cast<Expr *>(subExpr));
-  if (finder.found()) {
+  assert(s && "expected an AST root for a cleanup boundary");
+  if (ConditionalEvaluationFinder::needsCleanupScope(s)) {
     mlir::Location loc = cgf.builder.getUnknownLoc();
     cir::CleanupKind cleanupKind = cgf.getLangOpts().Exceptions
                                        ? cir::CleanupKind::All
@@ -169,6 +210,13 @@ 
CIRGenFunction::FullExprCleanupScope::FullExprCleanupScope(CIRGenFunction &cgf,
   // Conditional cleanup scopes are only opened while a full-expression scope
   // is active to close them.
   cgf.currentFullExprCleanupScope = scope;
+
+  // This full-expression owns its cleanups, even inside a conditional branch.
+  // Hide the outer conditional to keep those cleanups here, but remember it
+  // for lifetime-extended temporaries whose cleanups belong to an outer scope.
+  if (cgf.outermostConditional)
+    cgf.hiddenOutermostConditional = cgf.outermostConditional;
+  cgf.outermostConditional = nullptr;
 }
 
 /// If the alloca that backs \p addr is currently nested inside the body
@@ -303,6 +351,8 @@ void CIRGenFunction::FullExprCleanupScope::exit(
   exited = true;
 
   cgf.currentFullExprCleanupScope = oldFullExprCleanupScope;
+  cgf.outermostConditional = oldOutermostConditional;
+  cgf.hiddenOutermostConditional = oldHiddenOutermostConditional;
 
   size_t oldSize = deferredCleanupStackSize;
 
@@ -599,7 +649,16 @@ static void setupCleanupBlockDeactivation(CIRGenFunction 
&cgf,
   builder.createFlagStore(loc, false, var.getPointer());
 }
 
-/// Deactive a cleanup that was created in an active state.
+static bool enclosesOpenConditionalCleanupScope(CIRGenFunction &cgf,
+                                                EHCleanupScope &scope) {
+  cir::CleanupScopeOp op = scope.getCleanupScopeOp();
+  return op &&
+         llvm::any_of(cgf.conditionalCleanupScopes, [&](const auto &condScope) 
{
+           return op->isProperAncestor(condScope.scope);
+         });
+}
+
+/// Deactivate a cleanup that was created in an active state.
 void CIRGenFunction::deactivateCleanupBlock(EHScopeStack::stable_iterator c,
                                             mlir::Operation *dominatingIP) {
   assert(c != ehStack.stable_end() && "deactivating bottom of stack?");
@@ -607,9 +666,11 @@ void 
CIRGenFunction::deactivateCleanupBlock(EHScopeStack::stable_iterator c,
   assert(scope.isActive() && "double deactivation");
 
   // If it's the top of the stack, just pop it, but do so only if it belongs
-  // to the current RunCleanupsScope.
+  // to the current RunCleanupsScope. If a conditional opened a cleanup scope
+  // keep it open till the end of the full-expression.
   if (c == ehStack.stable_begin() &&
-      currentCleanupStackDepth.strictlyEncloses(c)) {
+      currentCleanupStackDepth.strictlyEncloses(c) &&
+      !enclosesOpenConditionalCleanupScope(*this, scope)) {
     popCleanupBlock(/*forDeactivation=*/true);
     return;
   }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 538db6747cf0ba..f0421572773b11 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -714,6 +714,22 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData 
&coro,
   // throw.
   const bool resumeInTry = isResumeExprInTryCatch(coro, kind, s);
 
+  // Resume-expression temporaries live until the enclosing full-expression
+  // ends. Defer their cleanups and guard them with construction flags, since
+  // destroying the coroutine while suspended skips their construction.
+  // The initial-suspend try/catch cleans up its temporaries in the try block.
+  std::optional<CIRGenFunction::ConditionalEvaluation> resumeEval;
+  const bool resumeDefersCleanups =
+      !resumeInTry && CIRGenFunction::resumeExprDefersCleanups(s);
+  if (resumeDefersCleanups && cgf.currentFullExprCleanupScope)
+    resumeEval.emplace(cgf, cgf.getLoc(s.getSourceRange()));
+  else if (resumeDefersCleanups)
+    cgf.cgm.errorNYI(s.getSourceRange(),
+                     "resume expression cleanups without a full-expression "
+                     "cleanup scope");
+  [[maybe_unused]] EHScopeStack::stable_iterator ehDepth =
+      cgf.ehStack.stable_begin();
+
   // If the await_resume() result needs a destructor, take over its
   // destruction, unless the destination owns it. The try/catch path destroys
   // the result itself.
@@ -766,6 +782,8 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro,
       },
       /*resumeBuilder=*/
       [&](mlir::OpBuilder &b, mlir::Location loc) {
+        if (resumeEval)
+          resumeEval->beginEvaluation();
         if (resumeInTry) {
           // we are basically just emitting:
           // resumeEh = false;
@@ -796,24 +814,27 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData 
&coro,
 
           struct resumeEmitter final : CIRGenFunction::cxxTryBodyEmitter {
             const CXXTryStmt &tryStmt;
+            const Expr *resumeExpr;
             mlir::Location loc;
             mlir::Value resumeEHVar;
-            resumeEmitter(const CXXTryStmt &tryStmt, mlir::Location loc,
-                          Address resumeEHVar)
-                : tryStmt(tryStmt), loc(loc),
+            resumeEmitter(const CXXTryStmt &tryStmt, const Expr *resumeExpr,
+                          mlir::Location loc, Address resumeEHVar)
+                : tryStmt(tryStmt), resumeExpr(resumeExpr), loc(loc),
                   resumeEHVar(resumeEHVar.getPointer()) {}
 
             mlir::LogicalResult operator()(CIRGenFunction &cgf) override {
-              CIRGenFunction::RunCleanupsScope resumeCleanups(cgf);
+              // Destroy resume temporaries in the try block before marking
+              // resumption successful, so destructor exceptions are caught 
too.
+              CIRGenFunction::FullExprCleanupScope fullExpr(cgf, resumeExpr);
               mlir::LogicalResult res =
                   cgf.emitStmt(tryStmt.getTryBlock(), 
/*useCurrentScope=*/true);
-              resumeCleanups.forceCleanup();
+              fullExpr.exit();
               cgf.getBuilder().createFlagStore(loc, true, resumeEHVar);
               return res;
             }
 
             ~resumeEmitter() override = default;
-          } emitter{*tryStmt, mlirLoc, coro.resumeEHVar};
+          } emitter{*tryStmt, s.getResumeExpr(), mlirLoc, coro.resumeEHVar};
 
           awaitBuild = cgf.emitCXXTryStmt(*tryStmt, emitter);
           // We are not supposed to obtain the value from init suspend
@@ -852,10 +873,17 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData 
&coro,
           }
         }
 
+        if (resumeEval)
+          resumeEval->endEvaluation();
+
         // Returns control back to parent.
         cir::YieldOp::create(builder, loc);
       });
 
+  assert((cgf.ehStack.stable_begin() == ehDepth ||
+          (resumeDefersCleanups && !resumeEval)) &&
+         "a resume expression cleanup was pushed in the resume region");
+
   // Push the destructor right after the cir.await, where the result starts to
   // exist, and not around it: destroying the coroutine at the suspend point
   // would destroy the not yet constructed result. The cleanup runs at the end

diff  --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp 
b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 33c3a753cecd50..f74d7b643debb7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -1332,7 +1332,7 @@ void CIRGenFunction::pushDestroyAndDeferDeactivation(
 void CIRGenFunction::pushLifetimeExtendedDestroy(
     CleanupKind cleanupKind, Address addr, QualType type, Destroyer *destroyer,
     bool useEHCleanupForArray, const ValueDecl *extendingDecl) {
-  if (isInConditionalBranch()) {
+  if (isInConditionalBranch() || hiddenOutermostConditional) {
     cgm.errorNYI("conditional lifetime-extended destroy");
     return;
   }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 7c5ce166edb059..ac186a5b58a38b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -869,10 +869,18 @@ void AggExprEmitter::emitArrayInit(Address destPtr, 
cir::ArrayType arrayTy,
           CIRGenFunction::ArrayInitLoopExprScope loopExprScope(
               cgf, setArrayInitLoopExprScope, idx);
 
-          if (arrayFiller)
+          // C++17 [class.temporary]p5:
+          // "when a default constructor is called to initialize an element of
+          // an array with no corresponding initializer [...] the destruction
+          // of every temporary created in a default argument is sequenced
+          // before the construction of the next array element, if any."
+          if (arrayFiller) {
+            CIRGenFunction::FullExprCleanupScope scope(cgf, arrayFiller);
+            scope.deferLifetimeExtendedCleanups();
             emitInitializationToLValue(arrayFiller, elementLV);
-          else
+          } else {
             emitNullInitializationToLValue(loc, elementLV);
+          }
 
           // Advance pointer and store them to temporary variable
           cir::ConstantOp one = builder.getConstInt(

diff  --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 218b97ecb40356..396c10ec9001a5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1361,6 +1361,12 @@ class CIRGenFunction : public CIRGenTypeCache {
     EHScopeStack::stable_iterator cleanupStackDepth, oldCleanupStackDepth;
     size_t lifetimeExtendedCleanupStackSize;
     CleanupDeactivationScope deactivateCleanups;
+    bool deferLifetimeExtended = false;
+
+    size_t oldLifetimeExtendedSize() const {
+      return deferLifetimeExtended ? cgf.lifetimeExtendedCleanupStack.size()
+                                   : lifetimeExtendedCleanupStackSize;
+    }
 
   protected:
     bool performCleanup;
@@ -1441,8 +1447,7 @@ class CIRGenFunction : public CIRGenTypeCache {
       // pop cir.cleanup.scope ops); they already dominate the post-scope
       // insertion point on their own.
       if (didSpillAny) {
-        cgf.popCleanupBlocks(cleanupStackDepth,
-                             lifetimeExtendedCleanupStackSize);
+        cgf.popCleanupBlocks(cleanupStackDepth, oldLifetimeExtendedSize());
 
         // Reload the spilled values now that all cleanup popping (and
         // promotion of any lifetime-extended cleanups onto the EH stack) is
@@ -1453,8 +1458,8 @@ class CIRGenFunction : public CIRGenTypeCache {
           *valPtr = cgf.builder.createLoad(valPtr->getLoc(), addr);
         }
       } else {
-        cgf.popCleanupBlocks(cleanupStackDepth,
-                             lifetimeExtendedCleanupStackSize, valuesToReload);
+        cgf.popCleanupBlocks(cleanupStackDepth, oldLifetimeExtendedSize(),
+                             valuesToReload);
       }
 
       performCleanup = false;
@@ -1479,7 +1484,7 @@ class CIRGenFunction : public CIRGenTypeCache {
       assert(performCleanup && "Already forced cleanup");
       assert(deactivateCleanups.deactivated &&
              "forceCleanupExceptLifetimeExtended() must be called first");
-      cgf.popCleanupBlocks(cleanupStackDepth, 
lifetimeExtendedCleanupStackSize);
+      cgf.popCleanupBlocks(cleanupStackDepth, oldLifetimeExtendedSize());
       performCleanup = false;
       cgf.currentCleanupStackDepth = oldCleanupStackDepth;
     }
@@ -1489,25 +1494,43 @@ class CIRGenFunction : public CIRGenTypeCache {
     bool hasPendingCleanups() const {
       return cgf.ehStack.stable_begin() != cleanupStackDepth;
     }
+
+    /// Leave the cleanups of lifetime-extended temporaries created in this
+    /// scope to the enclosing scope instead of pushing them onto the EH stack
+    /// when this scope ends. This is for a scope that ends in the body of a
+    /// loop, such as the construction of an array element: the cleanup scope
+    /// of such a temporary ends with that of the variable that extends its
+    /// lifetime, after the loop.
+    void deferLifetimeExtendedCleanups() { deferLifetimeExtended = true; }
   };
 
   // Cleanup stack depth of the RunCleanupsScope that was pushed most recently.
   EHScopeStack::stable_iterator currentCleanupStackDepth = 
ehStack.stable_end();
 
+  class ConditionalEvaluation;
+
   class FullExprCleanupScope {
     CIRGenFunction &cgf;
     RunCleanupsScope cleanups;
     cir::CleanupScopeOp scope;
     cir::CleanupScopeOp oldFullExprCleanupScope;
+    ConditionalEvaluation *oldOutermostConditional;
+    ConditionalEvaluation *oldHiddenOutermostConditional;
     size_t deferredCleanupStackSize;
     size_t conditionalScopeDepth;
     bool exited = false;
 
   public:
-    FullExprCleanupScope(CIRGenFunction &cgf, const Expr *subExpr);
+    /// \p s is the AST root for one cleanup boundary: a full-expression, an
+    /// asm statement, or the construction of one array element.
+    FullExprCleanupScope(CIRGenFunction &cgf, const Stmt *s);
 
     void exit(ArrayRef<mlir::Value *> valuesToReload = {});
 
+    void deferLifetimeExtendedCleanups() {
+      cleanups.deferLifetimeExtendedCleanups();
+    }
+
     ~FullExprCleanupScope() {
       if (!exited)
         exit();
@@ -1518,6 +1541,11 @@ class CIRGenFunction : public CIRGenTypeCache {
     void operator=(const FullExprCleanupScope &) = delete;
   };
 
+  /// Whether the resume expression of \p s contains a construct that defers
+  /// cleanups to the end of the enclosing full-expression: a temporary with a
+  /// destructor, other than the result of await_resume(), or a conditional.
+  static bool resumeExprDefersCleanups(const CoroutineSuspendExpr &s);
+
   /// Captures cleanups for variables whose lifetime ends in a region owned by
   /// an enclosing op rather than at the end of a cir.cleanup.scope, so that
   /// they can be emitted into that region later. Examples for uses are:
@@ -2713,6 +2741,13 @@ class CIRGenFunction : public CIRGenTypeCache {
   // we know if a temporary should be destroyed conditionally.
   ConditionalEvaluation *outermostConditional = nullptr;
 
+  // The outermost conditional that a FullExprCleanupScope in a conditional
+  // branch hides from isInConditionalBranch(), if any. The cleanups of that
+  // full-expression run at its end, so they are not conditional, but a
+  // temporary whose lifetime is extended beyond it is still created
+  // conditionally.
+  ConditionalEvaluation *hiddenOutermostConditional = nullptr;
+
   /// An RAII object to record that we're evaluating a statement
   /// expression.
   class StmtExprEvaluation {
@@ -2722,15 +2757,19 @@ class CIRGenFunction : public CIRGenTypeCache {
     /// statement expression aren't conditional just because the
     /// StmtExpr is.
     ConditionalEvaluation *savedOutermostConditional;
+    ConditionalEvaluation *savedHiddenOutermostConditional;
 
   public:
     StmtExprEvaluation(CIRGenFunction &cgf)
-        : cgf(cgf), savedOutermostConditional(cgf.outermostConditional) {
+        : cgf(cgf), savedOutermostConditional(cgf.outermostConditional),
+          savedHiddenOutermostConditional(cgf.hiddenOutermostConditional) {
       cgf.outermostConditional = nullptr;
+      cgf.hiddenOutermostConditional = nullptr;
     }
 
     ~StmtExprEvaluation() {
       cgf.outermostConditional = savedOutermostConditional;
+      cgf.hiddenOutermostConditional = savedHiddenOutermostConditional;
     }
   };
 

diff  --git a/clang/test/CIR/CodeGen/array-ctor.cpp 
b/clang/test/CIR/CodeGen/array-ctor.cpp
index 0c2e9652bdccb6..872b2dda8e2cf7 100644
--- a/clang/test/CIR/CodeGen/array-ctor.cpp
+++ b/clang/test/CIR/CodeGen/array-ctor.cpp
@@ -1,10 +1,12 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o -  
2>&1 | FileCheck --check-prefixes=CIR-BEFORE-LPP %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -emit-cir %s -o %t.cir
-// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-emit-llvm %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-linux-gnu -fclangir 
-fcxx-exceptions -fexceptions -emit-cir %s -o %t.eh.cir
+// RUN: FileCheck --input-file=%t.eh.cir %s --check-prefixes=TEMP,TEMP-EH
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-linux-gnu 
-Wno-unused-value -fclangir -emit-cir -mmlir 
--mlir-print-ir-before=cir-lowering-prepare %s -o -  2>&1 | FileCheck 
--check-prefixes=CIR-BEFORE-LPP %s
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-linux-gnu 
-Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefixes=CIR,TEMP,TEMP-NOEH
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-linux-gnu 
-Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s 
--check-prefixes=LLVM,TEMP-IR,TEMP-LLVM
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-linux-gnu 
-Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefixes=OGCG,TEMP-IR
 
 struct S {
     S();
@@ -345,3 +347,182 @@ void Temp2InArray() {
 // OGCG-NEXT:  call void @_ZN5Temp2D1Ev({{.*}}[[TMP]])
 // OGCG:       %[[DTOR_ELT:.*]] = getelementptr inbounds %struct.CausesTemp2, 
ptr %{{.*}}, i64 -1
 // OGCG-NEXT:  call void @_ZN11CausesTemp2D1Ev(ptr {{.*}}%[[DTOR_ELT]])
+
+// Constructor default arguments are destroyed before the next array element.
+// Filler and capture-copy loops have the same boundary. Lifetime-extended
+// temporaries retain their enclosing cleanup ownership.
+
+struct ArgTemp {
+  ArgTemp(int);
+  ~ArgTemp();
+};
+
+extern "C" bool flag();
+extern "C" int g(const ArgTemp &);
+extern "C" void use(int);
+
+struct E {
+  E(const ArgTemp & = ArgTemp(1));
+  ~E();
+};
+
+struct F {
+  F(int = flag() ? g(ArgTemp(2)) : 0);
+  ~F();
+};
+
+extern "C" int take(F *);
+
+// TEMP-LABEL: cir.func {{.*}} @new_in_conditional(
+// TEMP: %[[TMP:.*]] = cir.alloca "ref.tmp0"
+// TEMP: %[[FLAG:.*]] = cir.alloca "cleanup.cond"
+// TEMP: cir.ternary(
+// TEMP: cir.do {
+// TEMP: cir.call @flag()
+// TEMP: %[[FALSE:.*]] = cir.const #false
+// TEMP-NEXT: cir.store %[[FALSE]], %[[FLAG]]
+// TEMP: cir.ternary(
+// TEMP: cir.call @_ZN7ArgTempC1Ei(%[[TMP]],
+// TEMP-NEXT: %[[TRUE:.*]] = cir.const #true
+// TEMP-NEXT: cir.store %[[TRUE]], %[[FLAG]]
+// TEMP: cir.call @_ZN1FC1Ei(
+// TEMP-NOEH: } cleanup normal {
+// TEMP-EH: } cleanup all {
+// TEMP: %[[LIVE:.*]] = cir.load {{.*}} %[[FLAG]]
+// TEMP-NEXT: cir.if %[[LIVE]] {
+// TEMP-NEXT: cir.call @_ZN7ArgTempD1Ev(%[[TMP]])
+// TEMP: } while {
+// TEMP: cir.call @take(
+// TEMP: cir.call @use(
+
+// TEMP-IR-LABEL: define {{.*}} void @new_in_conditional(
+// TEMP-IR: call void @_ZN7ArgTempC1Ei(ptr {{.*}} %[[TMP:[^,]+]], i32 {{.*}} 2)
+// TEMP-IR: call void @_ZN1FC1Ei(
+// TEMP-IR: call void @_ZN7ArgTempD1Ev(ptr {{.*}} %[[TMP]])
+// TEMP-IR: call i32 @take(
+// TEMP-IR: call void @use(
+
+extern "C" void new_in_conditional(bool b) { use(b ? take(new F[2]) : 0); }
+
+struct E3 {
+  E e[3];
+};
+
+extern "C" int take3(const E3 &);
+
+// TEMP-LABEL: cir.func {{.*}} @filler_in_conditional(
+// TEMP: %[[EXPLICIT:.*]] = cir.alloca "ref.tmp1"
+// TEMP: cir.ternary(
+// TEMP: cir.call @_ZN7ArgTempC1Ei(%[[EXPLICIT]],
+// TEMP: cir.do {
+// TEMP: cir.call @_ZN7ArgTempC1Ei(%[[TMP:[^,]+]],
+// TEMP: cir.cleanup.scope {
+// TEMP: cir.call @_ZN1EC1ERK7ArgTemp(
+// TEMP-NOEH: } cleanup normal {
+// TEMP-EH: } cleanup all {
+// TEMP-NEXT: cir.call @_ZN7ArgTempD1Ev(%[[TMP]])
+// TEMP: } while {
+// TEMP: cir.call @take3(
+// TEMP: cir.call @use(
+// TEMP: cir.call @_ZN2E3D1Ev(
+// TEMP: cir.call @_ZN7ArgTempD1Ev(%[[EXPLICIT]])
+
+// TEMP-IR-LABEL: define {{.*}} void @filler_in_conditional(
+// TEMP-IR: call void @_ZN7ArgTempC1Ei(ptr {{.*}} %[[EXPLICIT:[^,]+]], i32 
{{.*}} 5)
+// TEMP-IR: call void @_ZN7ArgTempC1Ei(ptr {{.*}} %[[TMP:[^,]+]], i32 {{.*}} 1)
+// TEMP-IR: call void @_ZN1EC1ERK7ArgTemp(
+// TEMP-IR: call void @_ZN7ArgTempD1Ev(ptr {{.*}} %[[TMP]])
+// TEMP-IR: call i32 @take3(
+// TEMP-IR: call void @use(
+// TEMP-IR: call void @_ZN2E3D1Ev(
+// TEMP-IR: call void @_ZN7ArgTempD1Ev(ptr {{.*}} %[[EXPLICIT]])
+
+extern "C" void filler_in_conditional(bool b) { use(b ? 
take3(E3{{E(ArgTemp(5))}}) : 0); }
+
+struct C {
+  C();
+  C(const C &, const ArgTemp & = ArgTemp(3));
+  ~C();
+};
+
+// TEMP-LABEL: cir.func {{.*}} @capture(
+// TEMP: %[[TMP:.*]] = cir.alloca "ref.tmp0"
+// TEMP: cir.do {
+// TEMP: cir.call @_ZN7ArgTempC1Ei(%[[TMP]],
+// TEMP: cir.cleanup.scope {
+// TEMP: cir.call @_ZN1CC1ERKS_RK7ArgTemp(
+// TEMP-NOEH: } cleanup normal {
+// TEMP-EH: } cleanup all {
+// TEMP-NEXT: cir.call @_ZN7ArgTempD1Ev(%[[TMP]])
+// TEMP: } while {
+
+// TEMP-IR-LABEL: define {{.*}} void @capture(
+// TEMP-IR: call void @_ZN7ArgTempC1Ei(ptr {{.*}} %[[TMP:.*]], i32 {{.*}} 3)
+// TEMP-IR: call void @_ZN1CC1ERKS_RK7ArgTemp(
+// TEMP-IR: call void @_ZN7ArgTempD1Ev(ptr {{.*}} %[[TMP]])
+
+extern "C" void capture() {
+  C arr[2];
+  (void)[arr] {};
+}
+
+struct R {
+  const ArgTemp &s = ArgTemp(4);
+  int x;
+};
+
+extern "C" void use_r(R *);
+
+// TEMP-LABEL: cir.func {{.*}} @extended_member_filler(
+// TEMP: %[[TMP:.*]] = cir.alloca "ref.tmp0"
+// TEMP: cir.do {
+// TEMP: cir.call @_ZN7ArgTempC1Ei(%[[TMP]],
+// TEMP: } while {
+// TEMP: cir.call @use_r(
+// TEMP-NOEH: } cleanup normal {
+// TEMP-EH: } cleanup all {
+// TEMP-NEXT: cir.call @_ZN7ArgTempD1Ev(%[[TMP]])
+
+// TEMP-LLVM-LABEL: define {{.*}} void @extended_member_filler(
+// TEMP-LLVM: call void @_ZN7ArgTempC1Ei(ptr {{.*}} %[[TMP:.*]], i32 {{.*}} 4)
+// TEMP-LLVM-NOT: call void @_ZN7ArgTempD1Ev
+// TEMP-LLVM: call void @use_r(
+// TEMP-LLVM: call void @_ZN7ArgTempD1Ev(ptr {{.*}} %[[TMP]])
+
+extern "C" void extended_member_filler() {
+  R a[2] = {};
+  use_r(a);
+}
+
+struct Range {
+  int *begin();
+  int *end();
+};
+
+extern "C" Range take_range(E *);
+
+// TEMP-LABEL: cir.func {{.*}} @extended_default_arg(
+// TEMP: %[[TMP:.*]] = cir.alloca "ref.tmp1"
+// TEMP: cir.do {
+// TEMP: cir.call @_ZN7ArgTempC1Ei(%[[TMP]],
+// TEMP: cir.call @_ZN1EC1ERK7ArgTemp(
+// TEMP: } while {
+// TEMP: cir.call @take_range(
+// TEMP: cir.for : cond {
+// TEMP: cir.call @use(
+// TEMP: } step {
+// TEMP-NOEH: } cleanup normal {
+// TEMP-EH: } cleanup all {
+// TEMP-NEXT: cir.call @_ZN7ArgTempD1Ev(%[[TMP]])
+
+// TEMP-LLVM-LABEL: define {{.*}} void @extended_default_arg(
+// TEMP-LLVM: call void @_ZN7ArgTempC1Ei(ptr {{.*}} %[[TMP:.*]], i32 {{.*}} 1)
+// TEMP-LLVM: call void @take_range(
+// TEMP-LLVM-NOT: call void @_ZN7ArgTempD1Ev
+// TEMP-LLVM: call void @use(
+// TEMP-LLVM: call void @_ZN7ArgTempD1Ev(ptr {{.*}} %[[TMP]])
+
+extern "C" void extended_default_arg() {
+  for (int i : take_range(new E[2]))
+    use(i);
+}

diff  --git a/clang/test/CIR/CodeGen/cleanup-conditional-eh.cpp 
b/clang/test/CIR/CodeGen/cleanup-conditional-eh.cpp
index 1423f087240d2f..f66dc5872d6d46 100644
--- a/clang/test/CIR/CodeGen/cleanup-conditional-eh.cpp
+++ b/clang/test/CIR/CodeGen/cleanup-conditional-eh.cpp
@@ -766,3 +766,338 @@ void test_flag_cleared_before_cond_cleanup() {
 // OGCG:   call void @_ZN7PayloadD1Ev(ptr {{.*}} %[[AGG_TMP]])
 // OGCG:   call void @_ZN5GuardD1Ev(ptr {{.*}} %[[REF_TMP]])
 // OGCG:   resume { ptr, i32 }
+
+// The members that an aggregate initialization has constructed so far are
+// destroyed when a later member initializer is left early, here by a return
+// in a statement expression in an operand of a conditional operator. The
+// member cleanups are deactivated at the end of the initialization, while the
+// cleanup scope that the conditional operator opened for its operands is still
+// open: the flag that guards the cleanup of the first member is cleared inside
+// that scope.
+
+struct Obj {
+  char c;
+  Obj(char);
+  ~Obj();
+};
+
+struct Pair {
+  Obj a;
+  Obj b;
+};
+
+Obj copy(const Obj &);
+Obj make(bool);
+
+void struct_ternary(bool b, bool c) {
+  Pair p{Obj{'a'}, b ? ({
+                     if (c)
+                       return;
+                     Obj{'b'};
+                   })
+                     : Obj{'c'}};
+}
+
+// The cleanup scope of the conditional operator is empty and canonicalized
+// away.
+//
+// CIR-LABEL: cir.func {{.*}} @_Z14struct_ternarybb(
+// CIR:         %[[P:.*]] = cir.alloca "p"
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive"
+// CIR:         %[[A:.*]] = cir.get_member %[[P]][0] {name = "a"}
+// CIR:         cir.call @_ZN3ObjC1Ec(%[[A]],
+// CIR-NEXT:    %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT:    cir.store %[[TRUE]], %[[ACTIVE]]
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR:           cir.if
+// CIR:             cir.if
+// CIR-NEXT:          cir.return
+// CIR:           } else {
+// CIR:             cir.call @_ZN3ObjC1Ec(
+// CIR-NEXT:      } loc(
+// CIR-NEXT:      %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:      cir.store %[[FALSE]], %[[ACTIVE]]
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup all {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]]
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZN3ObjD1Ev(%[[A]])
+// CIR:         cir.call @_ZN4PairD1Ev(%[[P]])
+
+// A logical operator opens such a scope too.
+
+void logical_and(bool b, bool c) {
+  Pair p{Obj{'a'}, make(b && ({
+                          if (c)
+                            return;
+                          true;
+                        }))};
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z11logical_andbb(
+// CIR:         %[[P:.*]] = cir.alloca "p"
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive"
+// CIR:         %[[A:.*]] = cir.get_member %[[P]][0] {name = "a"}
+// CIR:         cir.store %{{.*}}, %[[ACTIVE]]
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR:           cir.ternary
+// CIR:             cir.return
+// CIR:           cir.call @_Z4makeb(
+// CIR-NEXT:      %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:      cir.store %[[FALSE]], %[[ACTIVE]]
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup all {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]]
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZN3ObjD1Ev(%[[A]])
+
+// The cleanup of an initialized member or init-capture, or the operator delete
+// or partial-array cleanup of a new-expression, is deactivated once the object
+// is initialized.
+// If an operand of a later ?:, && or || in the same initializer created a
+// temporary, the cleanup scope for that temporary is open in the body of the
+// deactivated cleanup's scope. It has to stay open until the end of the
+// full-expression, so the deactivated cleanup gets an active flag instead of
+// ending its scope early.
+
+int h(const Obj &);
+int g();
+void use(const Pair &, int);
+
+void aggregate(bool b) {
+  use(Pair{Obj{'a'}, b ? copy(Obj{'t'}) : Obj{'c'}}, g());
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z9aggregateb(
+// CIR:         %[[PAIR:.*]] = cir.alloca "ref.tmp0" {{.*}} : 
!cir.ptr<!rec_Pair>
+// CIR:         %[[TMP:.*]] = cir.alloca "ref.tmp1" {{.*}} : !cir.ptr<!rec_Obj>
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive" {{.*}} : 
!cir.ptr<!cir.bool>
+// CIR:         %[[A:.*]] = cir.get_member %[[PAIR]][0] {name = "a"}
+// CIR:         cir.call @_ZN3ObjC1Ec(%[[A]], %{{.*}})
+// CIR-NEXT:    %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT:    cir.store %[[TRUE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR:           cir.cleanup.scope {
+// CIR:             cir.if %{{.*}} {
+// CIR:             } else {
+// CIR-NEXT:          %[[C:.*]] = cir.const #cir.int<99> : !s8i
+// CIR-NEXT:          cir.call @_ZN3ObjC1Ec(%{{.*}}, %[[C]]){{.*}}
+// CIR-NEXT:        }
+// CIR-NEXT:        %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:        cir.store %[[FALSE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:        cir.cleanup.scope {
+// CIR-NEXT:          %[[G:.*]] = cir.call @_Z1gv()
+// CIR-NEXT:          cir.call @_Z3useRK4Pairi(%[[PAIR]], %[[G]])
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        } cleanup all {
+// CIR-NEXT:          cir.call @_ZN4PairD1Ev(%[[PAIR]])
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      } cleanup all {
+// CIR-NEXT:        %[[IS_COND:.*]] = cir.load
+// CIR-NEXT:        cir.if %[[IS_COND]] {
+// CIR-NEXT:          cir.call @_ZN3ObjD1Ev(%[[TMP]]){{.*}}
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      }
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup all {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZN3ObjD1Ev(%[[A]])
+
+// LLVM-LABEL: define {{.*}} void @_Z9aggregateb(
+// LLVM:         call void @_ZN3ObjC1Ec(ptr {{.*}} %[[A:.*]], i8 {{.*}} 97)
+// LLVM:         invoke void @_ZN3ObjC1Ec(ptr {{.*}} %[[TMP:.*]], i8 {{.*}} 
116)
+// LLVM:         invoke void @_Z4copyRK3Obj(
+// LLVM:         invoke {{.*}} i32 @_Z1gv()
+// LLVM:         invoke void @_Z3useRK4Pairi(
+// LLVM:         call void @_ZN4PairD1Ev(
+// LLVM:         call void @_ZN3ObjD1Ev(ptr {{.*}} %[[TMP]])
+
+// OGCG-LABEL: define {{.*}} void @_Z9aggregateb(
+// OGCG:         call void @_ZN3ObjC1Ec(ptr {{.*}} %[[A:.*]], i8 {{.*}} 97)
+// OGCG:         invoke void @_ZN3ObjC1Ec(ptr {{.*}} %[[TMP:.*]], i8 {{.*}} 
116)
+// OGCG:         invoke void @_Z4copyRK3Obj(
+// OGCG:         invoke {{.*}} i32 @_Z1gv()
+// OGCG:         invoke void @_Z3useRK4Pairi(
+// OGCG:         call void @_ZN4PairD1Ev(
+// OGCG:         call void @_ZN3ObjD1Ev(ptr {{.*}} %[[TMP]])
+
+struct Agg {
+  Obj o;
+  int i;
+};
+
+void use_array(Agg *, int);
+
+void array_new(bool b) {
+  use_array(new Agg[2]{{Obj{'a'}, b ? h(Obj{'t'}) : 0}, {Obj{'c'}, 2}}, g());
+}
+
+// The member of the first element is deactivated with a flag, which keeps it
+// on the EH stack above the partial-array cleanup until the full-expression
+// ends.
+// CIR-LABEL: cir.func {{.*}} @_Z9array_newb(
+// CIR:         %[[RESULT:.*]] = cir.alloca "__new_result"
+// CIR:         %[[TMP:.*]] = cir.alloca "ref.tmp0" {{.*}} : !cir.ptr<!rec_Obj>
+// CIR:         %[[COND:.*]] = cir.alloca "cleanup.cond" {{.*}} : 
!cir.ptr<!cir.bool>
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive" {{.*}} : 
!cir.ptr<!cir.bool>
+// CIR:         %[[ARRAY:.*]] = cir.cast bitcast %{{.*}} : !cir.ptr<!void> -> 
!cir.ptr<!rec_Agg>
+// CIR:         %[[A:.*]] = cir.get_member %[[ARRAY]][0] {name = "o"}
+// CIR:         cir.call @_ZN3ObjC1Ec(%[[A]], %{{.*}})
+// CIR-NEXT:    %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT:    cir.store %[[TRUE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR:           cir.cleanup.scope {
+// CIR:             cir.ternary
+// CIR:               cir.call @_ZN3ObjC1Ec(%[[TMP]], %{{.*}})
+// CIR:             %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:        cir.store %[[FALSE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR:             %[[C:.*]] = cir.const #cir.int<99> : !s8i
+// CIR-NEXT:        cir.call @_ZN3ObjC1Ec(%{{.*}}, %[[C]])
+// CIR:             %[[NEW_ARRAY:.*]] = cir.load {{.*}} %[[RESULT]]
+// CIR-NEXT:        %[[G:.*]] = cir.call @_Z1gv()
+// CIR-NEXT:        cir.call @_Z9use_arrayP3Aggi(%[[NEW_ARRAY]], %[[G]])
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      } cleanup all {
+// CIR-NEXT:        %[[IS_COND:.*]] = cir.load {{.*}} %[[COND]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CIR-NEXT:        cir.if %[[IS_COND]] {
+// CIR-NEXT:          cir.call @_ZN3ObjD1Ev(%[[TMP]]){{.*}}
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      }
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup all {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZN3ObjD1Ev(%[[A]]){{.*}}
+
+// LLVM-LABEL: define {{.*}} void @_Z9array_newb(
+// LLVM:         invoke void @_ZN3ObjC1Ec(ptr {{.*}} %[[TMP:.*]], i8 {{.*}} 
116)
+// LLVM:         invoke void @_ZN3ObjC1Ec(ptr {{.*}}, i8 {{.*}} 99)
+// LLVM:         invoke {{.*}} i32 @_Z1gv()
+// LLVM:         invoke void @_Z9use_arrayP3Aggi(
+// LLVM:         call void @_ZN3ObjD1Ev(ptr {{.*}} %[[TMP]])
+
+// OGCG-LABEL: define {{.*}} void @_Z9array_newb(
+// OGCG:         invoke void @_ZN3ObjC1Ec(ptr {{.*}} %[[TMP:.*]], i8 {{.*}} 
116)
+// OGCG:         invoke void @_ZN3ObjC1Ec(ptr {{.*}}, i8 {{.*}} 99)
+// OGCG:         invoke {{.*}} i32 @_Z1gv()
+// OGCG:         invoke void @_Z9use_arrayP3Aggi(
+// OGCG:         call void @_ZN3ObjD1Ev(ptr {{.*}} %[[TMP]])
+
+struct Foo {
+  Foo(int);
+};
+
+void use_new(Foo *, int);
+
+void new_expr(bool b) { use_new(new Foo(b ? h(Obj{'x'}) : 0), g()); }
+
+// Without exceptions there is no operator delete cleanup to deactivate.
+// CIR-LABEL: cir.func {{.*}} @_Z8new_exprb(
+// CIR:         %[[RESULT:.*]] = cir.alloca "__new_result"
+// CIR:         %[[TMP:.*]] = cir.alloca "ref.tmp0" {{.*}} : !cir.ptr<!rec_Obj>
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive" {{.*}} : 
!cir.ptr<!cir.bool>
+// CIR:         %[[PTR:.*]] = cir.call @_Znwm(
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR-NEXT:      %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT:      cir.store %[[TRUE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR:           cir.cleanup.scope {
+// CIR:             cir.ternary
+// CIR:             cir.call @_ZN3FooC1Ei(
+// CIR-NEXT:        %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:        cir.store %[[FALSE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:        %[[FOO:.*]] = cir.load {{.*}} %[[RESULT]]
+// CIR-NEXT:        %[[G:.*]] = cir.call @_Z1gv()
+// CIR-NEXT:        cir.call @_Z7use_newP3Fooi(%[[FOO]], %[[G]])
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      } cleanup all {
+// CIR-NEXT:        %[[IS_COND:.*]] = cir.load
+// CIR-NEXT:        cir.if %[[IS_COND]] {
+// CIR-NEXT:          cir.call @_ZN3ObjD1Ev(%[[TMP]]){{.*}}
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      }
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup eh {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZdlPvm(%[[PTR]], %{{.*}})
+
+// LLVM-LABEL: define {{.*}} void @_Z8new_exprb(
+// LLVM:         invoke void @_ZN3ObjC1Ec(ptr {{.*}} %[[TMP:.*]], i8 {{.*}} 
120)
+// LLVM:         invoke void @_ZN3FooC1Ei(
+// LLVM:         invoke {{.*}} i32 @_Z1gv()
+// LLVM:         invoke void @_Z7use_newP3Fooi(
+// LLVM:         call void @_ZN3ObjD1Ev(ptr {{.*}} %[[TMP]])
+
+// OGCG-LABEL: define {{.*}} void @_Z8new_exprb(
+// OGCG:         invoke void @_ZN3ObjC1Ec(ptr {{.*}} %[[TMP:.*]], i8 {{.*}} 
120)
+// OGCG:         invoke void @_ZN3FooC1Ei(
+// OGCG:         invoke {{.*}} i32 @_Z1gv()
+// OGCG:         invoke void @_Z7use_newP3Fooi(
+// OGCG:         call void @_ZN3ObjD1Ev(ptr {{.*}} %[[TMP]])
+
+void use_int(int, int);
+
+void init_capture(bool b) {
+  use_int([o = Obj{'a'}, i = b ? h(Obj{'t'}) : 0] { return i; }(), g());
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z12init_captureb(
+// CIR:         %[[LAMBDA:.*]] = cir.alloca "ref.tmp0"
+// CIR:         %[[TMP:.*]] = cir.alloca "ref.tmp1" {{.*}} : !cir.ptr<!rec_Obj>
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive" {{.*}} : 
!cir.ptr<!cir.bool>
+// CIR:         %[[O:.*]] = cir.get_member %[[LAMBDA]][0] {name = "o"}
+// CIR:         cir.call @_ZN3ObjC1Ec(%[[O]], %{{.*}})
+// CIR-NEXT:    %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT:    cir.store %[[TRUE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR:           cir.cleanup.scope {
+// CIR:             cir.ternary
+// CIR:             cir.store {{.*}} : !s32i, !cir.ptr<!s32i>
+// CIR-NEXT:        %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:        cir.store %[[FALSE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:        cir.cleanup.scope {
+// CIR-NEXT:          %[[I:.*]] = cir.call 
@_ZZ12init_capturebENK3$_{{[0-9]+}}clEv(%[[LAMBDA]])
+// CIR-NEXT:          %[[G:.*]] = cir.call @_Z1gv()
+// CIR-NEXT:          cir.call @_Z7use_intii(%[[I]], %[[G]])
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        } cleanup all {
+// CIR-NEXT:          cir.call 
@_ZZ12init_capturebEN3$_{{[0-9]+}}D1Ev(%[[LAMBDA]])
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      } cleanup all {
+// CIR-NEXT:        %[[IS_COND:.*]] = cir.load
+// CIR-NEXT:        cir.if %[[IS_COND]] {
+// CIR-NEXT:          cir.call @_ZN3ObjD1Ev(%[[TMP]]){{.*}}
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      }
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup all {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZN3ObjD1Ev(%[[O]])
+
+// LLVM-LABEL: define {{.*}} void @_Z12init_captureb(
+// LLVM:         invoke void @_ZN3ObjC1Ec(ptr {{.*}} %[[TMP:.*]], i8 {{.*}} 
116)
+// LLVM:         invoke {{.*}} i32 @"_ZZ12init_capturebENK3$_{{[0-9]+}}clEv"(
+// LLVM:         invoke {{.*}} i32 @_Z1gv()
+// LLVM:         invoke void @_Z7use_intii(
+// LLVM:         call void @"_ZZ12init_capturebEN3$_{{[0-9]+}}D1Ev"(
+// LLVM:         call void @_ZN3ObjD1Ev(ptr {{.*}} %[[TMP]])
+// LLVM-LABEL: define internal {{.*}} 
@"_ZZ12init_capturebENK3$_{{[0-9]+}}clEv"(
+
+// OGCG-LABEL: define {{.*}} void @_Z12init_captureb(
+// OGCG:         invoke void @_ZN3ObjC1Ec(ptr {{.*}} %[[TMP:.*]], i8 {{.*}} 
116)
+// OGCG:         invoke {{.*}} i32 @"_ZZ12init_capturebENK3$_{{[0-9]+}}clEv"(
+// OGCG:         invoke {{.*}} i32 @_Z1gv()
+// OGCG:         invoke void @_Z7use_intii(
+// OGCG:         call void @"_ZZ12init_capturebEN3$_{{[0-9]+}}D1Ev"(
+// OGCG:         call void @_ZN3ObjD1Ev(ptr {{.*}} %[[TMP]])
+// OGCG-LABEL: define internal {{.*}} 
@"_ZZ12init_capturebENK3$_{{[0-9]+}}clEv"(

diff  --git a/clang/test/CIR/CodeGen/cleanup-conditional-with-wrapper.cpp 
b/clang/test/CIR/CodeGen/cleanup-conditional-with-wrapper.cpp
index 1d3ac68bc5a8fa..0dd8c4e517f6a6 100644
--- a/clang/test/CIR/CodeGen/cleanup-conditional-with-wrapper.cpp
+++ b/clang/test/CIR/CodeGen/cleanup-conditional-with-wrapper.cpp
@@ -1,9 +1,15 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
-// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
-// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-fcxx-exceptions -fexceptions -emit-cir %s -o %t.eh.cir
+// RUN: FileCheck --input-file=%t.eh.cir %s --check-prefix=DEACT
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-fcxx-exceptions -fexceptions -emit-llvm %s -o %t.eh.ll
+// RUN: FileCheck --input-file=%t.eh.ll %s --check-prefix=DEACT-IR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-fcxx-exceptions -fexceptions -emit-llvm %s -o %t.ogcg.eh.ll
+// RUN: FileCheck --input-file=%t.ogcg.eh.ll %s --check-prefix=DEACT-IR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefixes=CIR,DEACT
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefixes=LLVM,DEACT-IR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefixes=OGCG,DEACT-IR
 
 struct Base { ~Base(); };
 
@@ -349,3 +355,46 @@ void makeEntry() {
 // OGCG: [[CLEANUP_DONE]]:
 // OGCG:   call void @_ZN4IterD1Ev(ptr {{.*}} %[[REF_TMP]])
 // OGCG:   ret void
+
+// A non-conditional array initializer leaves its temporary alive until the
+// enclosing full-expression ends.
+
+struct Obj {
+  char c;
+  Obj(char);
+  ~Obj();
+};
+
+extern "C" int h(const Obj &);
+extern "C" int g();
+
+struct Agg {
+  Obj o;
+  int i;
+};
+
+extern "C" void use_array(Agg *, int);
+
+// DEACT-LABEL: cir.func {{.*}} @array_new_temporary(
+// DEACT: %[[TMP:.*]] = cir.alloca "ref.tmp0"
+// DEACT: cir.call @_ZN3ObjC1Ec(
+// DEACT-NEXT: %[[TRUE:.*]] = cir.const #true
+// DEACT-NEXT: cir.store %[[TRUE]], %[[ACTIVE:.*]]
+// DEACT: cir.call @h(%[[TMP]])
+// DEACT: %[[FALSE:.*]] = cir.const #false
+// DEACT-NEXT: cir.store %[[FALSE]], %[[ACTIVE]]
+// DEACT-NOT: cir.call @_ZN3ObjD1Ev(%[[TMP]])
+// DEACT: cir.call @g()
+// DEACT-NEXT: cir.call @use_array(
+// DEACT: cir.call @_ZN3ObjD1Ev(%[[TMP]])
+
+// DEACT-IR-LABEL: define {{.*}} void @array_new_temporary(
+// DEACT-IR: {{(call|invoke)}} void @_ZN3ObjC1Ec(ptr {{.*}} %[[TMP:.*]], i8 
{{.*}} 116)
+// DEACT-IR-NOT: call void @_ZN3ObjD1Ev(ptr {{.*}} %[[TMP]])
+// DEACT-IR: {{(call|invoke)}} i32 @g()
+// DEACT-IR: {{(call|invoke)}} void @use_array(
+// DEACT-IR: call void @_ZN3ObjD1Ev(ptr {{.*}} %[[TMP]])
+
+extern "C" void array_new_temporary() {
+  use_array(new Agg[2]{{Obj{'a'}, h(Obj{'t'})}, {Obj{'c'}, 2}}, g());
+}

diff  --git a/clang/test/CIR/CodeGen/cleanup-conditional.cpp 
b/clang/test/CIR/CodeGen/cleanup-conditional.cpp
index 1f86f1d1cd4831..b9df861d17e4f3 100644
--- a/clang/test/CIR/CodeGen/cleanup-conditional.cpp
+++ b/clang/test/CIR/CodeGen/cleanup-conditional.cpp
@@ -1356,3 +1356,284 @@ void lambda_init_capture() {
 // OGCG:       [[DTOR]]:
 // OGCG-NEXT:    call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
 // OGCG:         call void @"_ZZ19lambda_init_capturevENK3$_0clEv"(
+
+// The members that an aggregate initialization has constructed so far are
+// destroyed when a later member initializer is left early, here by a return
+// in a statement expression in an operand of a conditional operator. The
+// member cleanups are deactivated at the end of the initialization, while the
+// cleanup scope that the conditional operator opened for its operands is still
+// open: the flag that guards the cleanup of the first member is cleared inside
+// that scope.
+
+struct Obj {
+  char c;
+  Obj(char);
+  ~Obj();
+};
+
+struct Pair {
+  Obj a;
+  Obj b;
+};
+
+Obj copy(const Obj &);
+Obj make(bool);
+
+void struct_ternary(bool b, bool c) {
+  Pair p{Obj{'a'}, b ? ({
+                     if (c)
+                       return;
+                     Obj{'b'};
+                   })
+                     : Obj{'c'}};
+}
+
+// The cleanup scope of the conditional operator is empty and canonicalized
+// away.
+//
+// CIR-LABEL: cir.func {{.*}} @_Z14struct_ternarybb(
+// CIR:         %[[P:.*]] = cir.alloca "p"
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive"
+// CIR:         %[[A:.*]] = cir.get_member %[[P]][0] {name = "a"}
+// CIR:         cir.call @_ZN3ObjC1Ec(%[[A]],
+// CIR-NEXT:    %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT:    cir.store %[[TRUE]], %[[ACTIVE]]
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR:           cir.if
+// CIR:             cir.if
+// CIR-NEXT:          cir.return
+// CIR:           } else {
+// CIR:             cir.call @_ZN3ObjC1Ec(
+// CIR-NEXT:      } loc(
+// CIR-NEXT:      %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:      cir.store %[[FALSE]], %[[ACTIVE]]
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup normal {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]]
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZN3ObjD1Ev(%[[A]])
+// CIR:         cir.call @_ZN4PairD1Ev(%[[P]])
+
+// The return leaves through the cleanup, which destroys the first member.
+// Completing the initialization clears the flag.
+//
+// LLVMCIR-LABEL: define dso_local void @_Z14struct_ternarybb(
+// LLVMCIR:         %[[A:.*]] = getelementptr inbounds nuw %struct.Pair, ptr 
%{{.*}}, i32 0, i32 0
+// LLVMCIR-NEXT:    call void @_ZN3ObjC1Ec(ptr {{.*}} %[[A]], i8 noundef 
signext 97)
+// LLVMCIR-NEXT:    store i8 1, ptr %[[ACTIVE:[0-9]+]]
+// LLVMCIR:         store i32 1, ptr %[[SLOT:[0-9]+]]
+// LLVMCIR-NEXT:    br label %[[CLEANUP:[0-9]+]]
+// LLVMCIR:         call void @_ZN3ObjC1Ec(ptr {{.*}}, i8 noundef signext 98)
+// LLVMCIR:         call void @_ZN3ObjC1Ec(ptr {{.*}}, i8 noundef signext 99)
+// LLVMCIR-NEXT:    br label %[[END:[0-9]+]]
+// LLVMCIR:       [[END]]:
+// LLVMCIR-NEXT:    store i8 0, ptr %[[ACTIVE]]
+// LLVMCIR-NEXT:    store i32 0, ptr %[[SLOT]]
+// LLVMCIR-NEXT:    br label %[[CLEANUP]]
+// LLVMCIR:       [[CLEANUP]]:
+// LLVMCIR-NEXT:    %[[FLAG:.*]] = load i8, ptr %[[ACTIVE]]
+// LLVMCIR-NEXT:    %[[IS_ACTIVE:.*]] = trunc i8 %[[FLAG]] to i1
+// LLVMCIR-NEXT:    br i1 %[[IS_ACTIVE]], label %[[DESTROY:[0-9]+]], label 
%{{.*}}
+// LLVMCIR:       [[DESTROY]]:
+// LLVMCIR-NEXT:    call void @_ZN3ObjD1Ev(ptr {{.*}} %[[A]])
+// LLVMCIR:         call void @_ZN4PairD1Ev(
+// LLVMCIR:         ret void
+
+// OGCG-LABEL: define dso_local void @_Z14struct_ternarybb(
+// OGCG:         %[[A:.*]] = getelementptr inbounds nuw %struct.Pair, ptr %p, 
i32 0, i32 0
+// OGCG:       if.then:
+// OGCG-NEXT:    call void @_ZN3ObjD1Ev(ptr {{.*}} %[[A]])
+// OGCG-NEXT:    br label %return
+// OGCG:       cond.end:
+// OGCG-NEXT:    call void @_ZN4PairD1Ev(
+
+// A logical operator opens such a scope too.
+
+void logical_and(bool b, bool c) {
+  Pair p{Obj{'a'}, make(b && ({
+                          if (c)
+                            return;
+                          true;
+                        }))};
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z11logical_andbb(
+// CIR:         %[[P:.*]] = cir.alloca "p"
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive"
+// CIR:         %[[A:.*]] = cir.get_member %[[P]][0] {name = "a"}
+// CIR:         cir.store %{{.*}}, %[[ACTIVE]]
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR:           cir.ternary
+// CIR:             cir.return
+// CIR:           cir.call @_Z4makeb(
+// CIR-NEXT:      %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:      cir.store %[[FALSE]], %[[ACTIVE]]
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup normal {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]]
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZN3ObjD1Ev(%[[A]])
+
+// LLVMCIR-LABEL: define dso_local void @_Z11logical_andbb(
+// LLVMCIR:         call void @_Z4makeb(
+// LLVMCIR-NEXT:    store i8 0, ptr %[[ACTIVE:[0-9]+]]
+// LLVMCIR:         load i8, ptr %[[ACTIVE]]
+// LLVMCIR:         call void @_ZN3ObjD1Ev(
+
+// OGCG-LABEL: define dso_local void @_Z11logical_andbb(
+// OGCG:       if.then:
+// OGCG-NEXT:    call void @_ZN3ObjD1Ev(
+
+// The cleanup of an initialized member or init-capture, or the operator delete
+// or partial-array cleanup of a new-expression, is deactivated once the object
+// is initialized.
+// If an operand of a later ?:, && or || in the same initializer created a
+// temporary, the cleanup scope for that temporary is open in the body of the
+// deactivated cleanup's scope. It has to stay open until the end of the
+// full-expression, so the deactivated cleanup gets an active flag instead of
+// ending its scope early.
+
+int h(const Obj &);
+int g();
+void use(const Pair &, int);
+
+void aggregate(bool b) {
+  use(Pair{Obj{'a'}, b ? copy(Obj{'t'}) : Obj{'c'}}, g());
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z9aggregateb(
+// CIR:         %[[PAIR:.*]] = cir.alloca "ref.tmp0" {{.*}} : 
!cir.ptr<!rec_Pair>
+// CIR:         %[[TMP:.*]] = cir.alloca "ref.tmp1" {{.*}} : !cir.ptr<!rec_Obj>
+// CIR:         %[[COND:.*]] = cir.alloca "cleanup.cond" {{.*}} : 
!cir.ptr<!cir.bool>
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive" {{.*}} : 
!cir.ptr<!cir.bool>
+// CIR:         %[[A:.*]] = cir.get_member %[[PAIR]][0] {name = "a"}
+// CIR:         cir.call @_ZN3ObjC1Ec(%[[A]], %{{.*}})
+// CIR-NEXT:    %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT:    cir.store %[[TRUE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR:           cir.cleanup.scope {
+// CIR:             cir.if %{{.*}} {
+// CIR:               cir.call @_ZN3ObjC1Ec(%[[TMP]], %{{.*}})
+// CIR:               cir.call @_Z4copyRK3Obj(%{{.*}}, %[[TMP]])
+// CIR:             } else {
+// CIR-NEXT:          %[[C:.*]] = cir.const #cir.int<99> : !s8i
+// CIR-NEXT:          cir.call @_ZN3ObjC1Ec(%{{.*}}, %[[C]]){{.*}}
+// CIR-NEXT:        }
+// CIR-NEXT:        %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:        cir.store %[[FALSE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:        cir.cleanup.scope {
+// CIR-NEXT:          %[[G:.*]] = cir.call @_Z1gv()
+// CIR-NEXT:          cir.call @_Z3useRK4Pairi(%[[PAIR]], %[[G]])
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        } cleanup normal {
+// CIR-NEXT:          cir.call @_ZN4PairD1Ev(%[[PAIR]])
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      } cleanup normal {
+// CIR-NEXT:        %[[IS_COND:.*]] = cir.load {{.*}} %[[COND]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CIR-NEXT:        cir.if %[[IS_COND]] {
+// CIR-NEXT:          cir.call @_ZN3ObjD1Ev(%[[TMP]]){{.*}}
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      }
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup normal {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZN3ObjD1Ev(%[[A]]){{.*}}
+// CIR-NEXT:      }
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    }
+// CIR-NEXT:    cir.return
+
+struct Agg {
+  Obj o;
+  int i;
+};
+
+void use_array(Agg *, int);
+
+void array_new(bool b) {
+  use_array(new Agg[2]{{Obj{'a'}, b ? h(Obj{'t'}) : 0}, {Obj{'c'}, 2}}, g());
+}
+
+// The member of the first element is deactivated with a flag, which keeps it
+// on the EH stack above the partial-array cleanup. Without exceptions, that
+// cleanup is EH-only and therefore neither a normal nor an EH cleanup, so its
+// deactivation needs no flag.
+// CIR-LABEL: cir.func {{.*}} @_Z9array_newb(
+// CIR:         %[[TMP:.*]] = cir.alloca "ref.tmp0" {{.*}} : !cir.ptr<!rec_Obj>
+// CIR:         %[[COND:.*]] = cir.alloca "cleanup.cond" {{.*}} : 
!cir.ptr<!cir.bool>
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive" {{.*}} : 
!cir.ptr<!cir.bool>
+// CIR:         %[[ARRAY:.*]] = cir.cast bitcast %{{.*}} : !cir.ptr<!void> -> 
!cir.ptr<!rec_Agg>
+// CIR:         %[[A:.*]] = cir.get_member %[[ARRAY]][0] {name = "o"}
+// CIR:         cir.call @_ZN3ObjC1Ec(%[[A]], %{{.*}})
+// CIR-NEXT:    %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT:    cir.store %[[TRUE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR:           cir.cleanup.scope {
+// CIR:             cir.ternary
+// CIR:               cir.call @_ZN3ObjC1Ec(%[[TMP]], %{{.*}})
+// CIR:             %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:        cir.store %[[FALSE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR:             %[[C:.*]] = cir.const #cir.int<99> : !s8i
+// CIR-NEXT:        cir.call @_ZN3ObjC1Ec(%{{.*}}, %[[C]])
+// CIR:             %[[G:.*]] = cir.call @_Z1gv()
+// CIR-NEXT:        cir.call @_Z9use_arrayP3Aggi(%[[ARRAY]], %[[G]])
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      } cleanup normal {
+// CIR-NEXT:        %[[IS_COND:.*]] = cir.load {{.*}} %[[COND]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CIR-NEXT:        cir.if %[[IS_COND]] {
+// CIR-NEXT:          cir.call @_ZN3ObjD1Ev(%[[TMP]]){{.*}}
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      }
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup normal {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZN3ObjD1Ev(%[[A]]){{.*}}
+
+void use_int(int, int);
+
+void init_capture(bool b) {
+  use_int([o = Obj{'a'}, i = b ? h(Obj{'t'}) : 0] { return i; }(), g());
+}
+
+// CIR-LABEL: cir.func {{.*}} @_Z12init_captureb(
+// CIR:         %[[LAMBDA:.*]] = cir.alloca "ref.tmp0"
+// CIR:         %[[TMP:.*]] = cir.alloca "ref.tmp1" {{.*}} : !cir.ptr<!rec_Obj>
+// CIR:         %[[ACTIVE:.*]] = cir.alloca "cleanup.isactive" {{.*}} : 
!cir.ptr<!cir.bool>
+// CIR:         %[[O:.*]] = cir.get_member %[[LAMBDA]][0] {name = "o"}
+// CIR:         cir.call @_ZN3ObjC1Ec(%[[O]], %{{.*}})
+// CIR-NEXT:    %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT:    cir.store %[[TRUE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:    cir.cleanup.scope {
+// CIR:           cir.cleanup.scope {
+// CIR:             cir.ternary
+// CIR:             cir.store {{.*}} : !s32i, !cir.ptr<!s32i>
+// CIR-NEXT:        %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:        cir.store %[[FALSE]], %[[ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CIR-NEXT:        cir.cleanup.scope {
+// CIR-NEXT:          %[[I:.*]] = cir.call 
@_ZZ12init_capturebENK3$_{{[0-9]+}}clEv(%[[LAMBDA]])
+// CIR-NEXT:          %[[G:.*]] = cir.call @_Z1gv()
+// CIR-NEXT:          cir.call @_Z7use_intii(%[[I]], %[[G]])
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        } cleanup normal {
+// CIR-NEXT:          cir.call 
@_ZZ12init_capturebEN3$_{{[0-9]+}}D1Ev(%[[LAMBDA]])
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      } cleanup normal {
+// CIR-NEXT:        %[[IS_COND:.*]] = cir.load
+// CIR-NEXT:        cir.if %[[IS_COND]] {
+// CIR-NEXT:          cir.call @_ZN3ObjD1Ev(%[[TMP]]){{.*}}
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-NEXT:      }
+// CIR-NEXT:      cir.yield
+// CIR-NEXT:    } cleanup normal {
+// CIR-NEXT:      %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CIR-NEXT:      cir.if %[[IS_ACTIVE]] {
+// CIR-NEXT:        cir.call @_ZN3ObjD1Ev(%[[O]])

diff  --git a/clang/test/CIR/CodeGen/cleanup-scope-tmp.cpp 
b/clang/test/CIR/CodeGen/cleanup-scope-tmp.cpp
index 5f5c3f67c136ca..e89ad0e10b41d2 100644
--- a/clang/test/CIR/CodeGen/cleanup-scope-tmp.cpp
+++ b/clang/test/CIR/CodeGen/cleanup-scope-tmp.cpp
@@ -1,5 +1,11 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-Wno-unused-value -fclangir -emit-cir %s -o %t.cir
-// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: FileCheck --input-file=%t.cir %s --check-prefixes=CIR,ASM
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-fcxx-exceptions -fexceptions -emit-cir %s -o %t.eh.cir
+// RUN: FileCheck --input-file=%t.eh.cir %s --check-prefix=ASM
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=ASM-IR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=ASM-IR
 
 struct StructWithDestructor {
   ~StructWithDestructor();
@@ -29,3 +35,91 @@ void cleanup_scope_with_body_and_cleanup() {
 // CIR:   cir.call @_ZN20StructWithDestructorD1Ev(%0) nothrow : 
(!cir.ptr<!rec_StructWithDestructor> {{.*}}) -> ()
 // CIR:   cir.yield
 // CIR: }
+
+// The operands of an asm statement are not wrapped in an ExprWithCleanups.
+// Their temporaries are destroyed at the end of the asm statement.
+
+struct S {
+  S(int);
+  ~S();
+};
+
+extern "C" {
+
+int g(const S &);
+void use(int);
+
+void asm_plain() {
+  asm volatile("" : : "r"(g(S(1))));
+  use(0);
+}
+
+// ASM-LABEL: cir.func {{.*}} @asm_plain(
+// ASM:         %[[TMP:.*]] = cir.alloca "ref.tmp0" {{.*}} : !cir.ptr<!rec_S>
+// ASM:         cir.call @_ZN1SC1Ei(%[[TMP]],
+// ASM-NEXT:    cir.cleanup.scope {
+// ASM-NEXT:      %[[G:.*]] = cir.call @g(%[[TMP]])
+// ASM-NEXT:      cir.asm(
+// ASM:             in = [%[[G]] : !s32i],
+// ASM:           cir.yield
+// ASM-NEXT:    } cleanup {{(normal|all)}} {
+// ASM-NEXT:      cir.call @_ZN1SD1Ev(%[[TMP]])
+// ASM-NEXT:      cir.yield
+// ASM-NEXT:    }
+// ASM-NEXT:    %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// ASM-NEXT:    cir.call @use(%[[ZERO]])
+
+// ASM-IR-LABEL: define {{.*}} void @asm_plain(
+// ASM-IR:         call void @_ZN1SC1Ei(ptr {{.*}} %[[TMP:.*]], i32 {{.*}} 1)
+// ASM-IR:         call void asm sideeffect
+// ASM-IR:         call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
+// ASM-IR:         call void @use(i32 {{.*}} 0)
+
+// The cleanup scope of the enclosing full-expression is open, but the asm
+// statement in the statement expression needs its own.
+void asm_in_statement_expression(bool b) {
+  use(g(S(3)) + (b ? 1 : 0) + ({
+        int y = 0;
+        asm volatile("" : "+r"(y) : "r"(b ? g(S(4)) : 0));
+        y;
+      }));
+}
+
+// ASM-LABEL: cir.func {{.*}} @asm_in_statement_expression(
+// ASM:         %[[TMP3:.*]] = cir.alloca "ref.tmp0" {{.*}} : !cir.ptr<!rec_S>
+// ASM:         %[[TMP4:.*]] = cir.alloca "ref.tmp1" {{.*}} : !cir.ptr<!rec_S>
+// ASM:         %[[ACTIVE:.*]] = cir.alloca "cleanup.cond" {{.*}} : 
!cir.ptr<!cir.bool>
+// ASM:         cir.scope {
+// ASM:           cir.cleanup.scope {
+// ASM-NEXT:        %[[FALSE:.*]] = cir.const #false
+// ASM-NEXT:        cir.store %[[FALSE]], %[[ACTIVE]]
+// ASM-NEXT:        %[[ARG:.*]] = cir.ternary(%{{.*}}, true {
+// ASM:               cir.call @_ZN1SC1Ei(%[[TMP4]],
+// ASM-NEXT:          %[[TRUE:.*]] = cir.const #true
+// ASM-NEXT:          cir.store %[[TRUE]], %[[ACTIVE]]
+// ASM:               cir.call @g(%[[TMP4]])
+// ASM:             }) : (!cir.bool) -> !s32i
+// ASM-NEXT:        cir.asm(
+// ASM:               in = [%[[ARG]] : !s32i],
+// ASM:             cir.yield
+// ASM-NEXT:      } cleanup {{(normal|all)}} {
+// ASM-NEXT:        %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]]
+// ASM-NEXT:        cir.if %[[IS_ACTIVE]] {
+// ASM-NEXT:          cir.call @_ZN1SD1Ev(%[[TMP4]]){{.*}}
+// ASM-NEXT:        }
+// ASM-NEXT:        cir.yield
+// ASM-NEXT:      }
+// ASM:         }
+// ASM:         cir.call @use(
+// ASM:       } cleanup {{(normal|all)}} {
+// ASM-NEXT:    cir.call @_ZN1SD1Ev(%[[TMP3]])
+
+// ASM-IR-LABEL: define {{.*}} void @asm_in_statement_expression(
+// ASM-IR:         call void @_ZN1SC1Ei(ptr {{.*}} %[[TMP3:.*]], i32 {{.*}} 3)
+// ASM-IR:         call void @_ZN1SC1Ei(ptr {{.*}} %[[TMP4:.*]], i32 {{.*}} 4)
+// ASM-IR:         call i32 asm sideeffect
+// ASM-IR:         call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP4]])
+// ASM-IR:         call void @use(
+// ASM-IR:         call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP3]])
+
+} // extern "C"

diff  --git a/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp 
b/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp
index 47791d72627716..3c09be504168f5 100644
--- a/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp
+++ b/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp
@@ -1,11 +1,11 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-Wno-coroutine-missing-unhandled-exception -emit-cir %s -o %t.cir
-// RUN: FileCheck --input-file=%t.cir %s 
-check-prefixes=CIR,CIR-COMMON,CIR-NOEH
+// RUN: FileCheck --input-file=%t.cir %s 
-check-prefixes=CIR,CIR-COMMON,CIR-NOEH,RESUME,RESUME-NOEH
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-fcxx-exceptions -fexceptions -Wno-coroutine-missing-unhandled-exception 
-emit-cir %s -o %t.eh.cir
-// RUN: FileCheck --input-file=%t.eh.cir %s -check-prefixes=CIR-EH,CIR-COMMON
+// RUN: FileCheck --input-file=%t.eh.cir %s 
-check-prefixes=CIR-EH,CIR-COMMON,RESUME,RESUME-EH
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm 
-disable-llvm-passes -Wno-coroutine-missing-unhandled-exception %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=OGCG,RESUME-IR
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-fcxx-exceptions -fexceptions -emit-llvm -disable-llvm-passes 
-Wno-coroutine-missing-unhandled-exception %s -o %t.eh.ll
-// RUN: FileCheck --input-file=%t.eh.ll %s -check-prefix=OGCG-EH
+// RUN: FileCheck --input-file=%t.eh.ll %s -check-prefixes=OGCG-EH,RESUME-IR
 
 #include "Inputs/coroutine.h"
 
@@ -589,3 +589,429 @@ ThrowingDtorInitTask throwing_dtor_initial_resume() {
 // OGCG-EH-NEXT:      to label %[[CONT:.*]] unwind label
 // OGCG-EH:       [[CONT]]:
 // OGCG-EH-NEXT:    store i1 false, ptr %[[RESUME_EH]], align 1
+
+// Resume-expression temporaries survive through the enclosing full-expression.
+// Construction guards exclude paths that destroy the coroutine while 
suspended;
+// array elements and ready/suspend calls retain their own cleanup boundaries.
+
+struct S {
+  S(int);
+  ~S();
+};
+
+struct Agg {
+  S s;
+  int i;
+};
+
+extern "C" bool resume_flag();
+extern "C" int resume_arg(const S &);
+extern "C" int resume_next();
+extern "C" void resume_use(int);
+extern "C" void resume_use_pair(int, int);
+extern "C" void resume_use_result(NT);
+extern "C" void resume_use_aggregate(const Agg &, int);
+extern "C" void mark();
+
+struct DA {
+  bool await_ready();
+  void await_suspend(std::coroutine_handle<>);
+  int await_resume(const S & = S(1));
+};
+
+struct DV {
+  bool await_ready();
+  void await_suspend(std::coroutine_handle<>);
+  int await_resume(S = S(2));
+};
+
+struct DCC {
+  bool await_ready();
+  void await_suspend(std::coroutine_handle<>);
+  int await_resume(int = resume_flag() ? resume_arg(S(1)) : 0);
+};
+
+struct RA {
+  bool await_ready();
+  void await_suspend(std::coroutine_handle<>);
+  NT await_resume(const S & = S(1));
+};
+
+struct PL {
+  bool await_ready();
+  void await_suspend(std::coroutine_handle<>);
+  int await_resume();
+};
+
+struct Task {
+  struct promise_type {
+    Task get_return_object();
+    std::suspend_never initial_suspend();
+    std::suspend_never final_suspend() noexcept;
+    void return_void();
+    void unhandled_exception();
+    DA yield_value(int);
+  };
+};
+
+// RESUME-LABEL: cir.func coroutine {{.*}} @defarg_condtemp(
+// RESUME: %[[TMP:[^,]+]] = cir.alloca "ref.tmp{{[0-9]+}}" {{.*}} : 
!cir.ptr<!rec_S>
+// RESUME: %[[FLAG:.*]] = cir.alloca "cleanup.cond"
+// RESUME: %[[FALSE:.*]] = cir.const #false
+// RESUME-NEXT: cir.store %[[FALSE]], %[[FLAG]]
+// RESUME: cir.await(user, ready : {
+// RESUME: }, resume : {
+// RESUME: cir.ternary(
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP]],
+// RESUME-NEXT: %[[TRUE:.*]] = cir.const #true
+// RESUME-NEXT: cir.store %[[TRUE]], %[[FLAG]]
+// RESUME: cir.call @_ZN3DCC12await_resumeEi(
+// RESUME: },)
+// RESUME: cir.call @resume_use(
+// RESUME-NOEH: } cleanup normal {
+// RESUME-EH: } cleanup all {
+// RESUME: %[[LIVE:.*]] = cir.load {{.*}} %[[FLAG]]
+// RESUME-NEXT: cir.if %[[LIVE]] {
+// RESUME-NEXT: cir.call @_ZN1SD1Ev(%[[TMP]])
+
+extern "C" Task defarg_condtemp() {
+  resume_use(co_await DCC{});
+}
+
+// Cover reference and value arguments across two suspension points. Destroy
+// them in reverse order at full-expression end, before the next statement.
+// Construction flags exclude paths that destroy the coroutine while suspended.
+// RESUME-LABEL: cir.func coroutine {{.*}} @two_awaits(
+// RESUME-DAG: %[[TMP1:[^,]+]] = cir.alloca "ref.tmp{{[0-9]+}}" {{.*}} : 
!cir.ptr<!rec_S>
+// RESUME-DAG: %[[FLAG1:.*]] = cir.alloca "cleanup.cond"
+// RESUME-DAG: %[[TMP2:[^,]+]] = cir.alloca "agg.tmp{{[0-9]+}}" {{.*}} : 
!cir.ptr<!rec_S>
+// RESUME-DAG: %[[FLAG2:.*]] = cir.alloca "cleanup.cond"
+// RESUME: %[[FALSE1:.*]] = cir.const #false
+// RESUME-NEXT: cir.store %[[FALSE1]], %[[FLAG1]]
+// RESUME: cir.await(user, ready : {
+// RESUME: }, resume : {
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP1]],
+// RESUME-NEXT: %[[TRUE1:.*]] = cir.const #true
+// RESUME-NEXT: cir.store %[[TRUE1]], %[[FLAG1]]
+// RESUME: cir.call @_ZN2DA12await_resumeERK1S(
+// RESUME: },)
+// RESUME: %[[FALSE2:.*]] = cir.const #false
+// RESUME-NEXT: cir.store %[[FALSE2]], %[[FLAG2]]
+// RESUME: cir.await(user, ready : {
+// RESUME: }, resume : {
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP2]],
+// RESUME-NEXT: %[[TRUE2:.*]] = cir.const #true
+// RESUME-NEXT: cir.store %[[TRUE2]], %[[FLAG2]]
+// RESUME: cir.call @_ZN2DV12await_resumeE1S(
+// RESUME: },)
+// RESUME: cir.call @resume_use_pair(
+// RESUME-NOEH: } cleanup normal {
+// RESUME-EH: } cleanup all {
+// RESUME: %[[LIVE2:.*]] = cir.load {{.*}} %[[FLAG2]]
+// RESUME-NEXT: cir.if %[[LIVE2]] {
+// RESUME-NEXT: cir.call @_ZN1SD1Ev(%[[TMP2]])
+// RESUME-NOEH: } cleanup normal {
+// RESUME-EH: } cleanup all {
+// RESUME: %[[LIVE1:.*]] = cir.load {{.*}} %[[FLAG1]]
+// RESUME-NEXT: cir.if %[[LIVE1]] {
+// RESUME-NEXT: cir.call @_ZN1SD1Ev(%[[TMP1]])
+// RESUME: cir.call @mark()
+
+// RESUME-IR-LABEL: define {{.*}} void @two_awaits(
+// RESUME-IR: await.ready:
+// RESUME-IR: {{(call|invoke)}} void @_ZN1SC1Ei(ptr {{.*}} %[[TMP1:[^,]+]], 
i32 {{.*}} 1)
+// RESUME-IR: {{(call|invoke)}} {{.*}} i32 @_ZN2DA12await_resumeERK1S(
+// RESUME-IR: {{(call|invoke)}} void @_ZN1SC1Ei(ptr {{.*}} %[[TMP2:[^,]+]], 
i32 {{.*}} 2)
+// RESUME-IR: {{(call|invoke)}} void @resume_use_pair(
+// RESUME-IR: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP2]])
+// RESUME-IR: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP1]])
+// RESUME-IR: {{(call|invoke)}} void @mark()
+
+extern "C" Task two_awaits() {
+  resume_use_pair(co_await DA{}, co_await DV{});
+  mark();
+}
+
+// RESUME-LABEL: cir.func coroutine {{.*}} @class_result(
+// RESUME: cir.await(user, ready : {
+// RESUME: cir.call @_ZN2RA12await_resumeERK1S(
+// RESUME: },)
+// RESUME: cir.call @resume_use_result(
+// RESUME: cir.call @_ZN2NTD1Ev(
+// RESUME: cir.call @_ZN1SD1Ev(
+
+// RESUME-IR-LABEL: define {{.*}} void @class_result(
+// RESUME-IR: {{(call|invoke)}} void @_ZN2RA12await_resumeERK1S(
+// RESUME-IR: {{(call|invoke)}} void @resume_use_result(
+// RESUME-IR: call void @_ZN2NTD1Ev(
+// RESUME-IR: call void @_ZN1SD1Ev(
+
+extern "C" Task class_result() {
+  resume_use_result(co_await RA{});
+}
+
+// RESUME-LABEL: cir.func coroutine {{.*}} @in_conditional(
+// RESUME: %[[FLAG:.*]] = cir.alloca "cleanup.cond"
+// RESUME: %[[FALSE:.*]] = cir.const #false
+// RESUME-NEXT: cir.store %[[FALSE]], %[[FLAG]]
+// RESUME: cir.ternary(
+// RESUME: cir.await(user, ready : {
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP:[^,]+]],
+// RESUME: },)
+// RESUME: cir.call @resume_use(
+// RESUME: %[[LIVE:.*]] = cir.load {{.*}} %[[FLAG]]
+// RESUME-NEXT: cir.if %[[LIVE]] {
+// RESUME-NEXT: cir.call @_ZN1SD1Ev(%[[TMP]])
+
+extern "C" Task in_conditional() {
+  resume_use(resume_flag() ? co_await DA{} : 0);
+}
+
+// RESUME-LABEL: cir.func coroutine {{.*}} @agg_init(
+// RESUME: cir.call @_ZN1SC1Ei(
+// RESUME-NEXT: %[[TRUE:.*]] = cir.const #true
+// RESUME-NEXT: cir.store %[[TRUE]], %[[ACTIVE:.*]]
+// RESUME: cir.await(user, ready : {
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP:[^,]+]],
+// RESUME: cir.call @_ZN2DA12await_resumeERK1S(
+// RESUME: },)
+// RESUME: %[[FALSE:.*]] = cir.const #false
+// RESUME-NEXT: cir.store %[[FALSE]], %[[ACTIVE]]
+// RESUME-NOT: cir.call @_ZN1SD1Ev(%[[TMP]])
+// RESUME: cir.call @resume_next()
+// RESUME: cir.call @resume_use_aggregate(
+// RESUME: cir.call @_ZN3AggD1Ev(
+// RESUME: cir.call @_ZN1SD1Ev(%[[TMP]])
+
+// RESUME-IR-LABEL: define {{.*}} void @agg_init(
+// RESUME-IR: {{(call|invoke)}} {{.*}} i32 @_ZN2DA12await_resumeERK1S(
+// RESUME-IR: {{(call|invoke)}} i32 @resume_next()
+// RESUME-IR: {{(call|invoke)}} void @resume_use_aggregate(
+// RESUME-IR: call void @_ZN3AggD1Ev(
+// RESUME-IR: call void @_ZN1SD1Ev(
+
+extern "C" Task agg_init() {
+  resume_use_aggregate(Agg{S(0), co_await DA{}}, resume_next());
+}
+
+// An unrelated conditional must not create a resume cleanup guard.
+// RESUME-LABEL: cir.func coroutine {{.*}} @agg_plain_resume(
+// RESUME-NOT: cir.alloca "cleanup.cond"
+// RESUME: cir.await(user, ready : {
+// RESUME: cir.call @_ZN2PL12await_resumeEv(
+// RESUME: },)
+// RESUME: cir.call @resume_use_aggregate(
+// RESUME: cir.call @_ZN3AggD1Ev(
+// RESUME: cir.call @mark()
+
+extern "C" Task agg_plain_resume() {
+  resume_use_aggregate(Agg{S(0), co_await PL{}}, resume_flag() ? 1 : 2);
+  mark();
+}
+
+struct E {
+  E(const S & = S(3));
+  ~E();
+};
+
+struct Holder {
+  E e[2];
+  int k;
+};
+
+extern "C" int take(E *);
+extern "C" int take2(Holder *);
+
+struct AN {
+  bool await_ready();
+  void await_suspend(std::coroutine_handle<>);
+  int await_resume(int = take(new E[2]) + take2(new Holder{}));
+};
+
+// Array-element boundaries still apply inside a deferred resume expression.
+// RESUME-LABEL: cir.func coroutine {{.*}} @array_elements(
+// RESUME: cir.await(user, ready : {
+// RESUME: }, resume : {
+// RESUME: cir.do {
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP1:[^,]+]],
+// RESUME: cir.call @_ZN1EC1ERK1S(
+// RESUME-NOEH: } cleanup normal {
+// RESUME-EH: } cleanup all {
+// RESUME-NEXT: cir.call @_ZN1SD1Ev(%[[TMP1]])
+// RESUME: } while {
+// RESUME: cir.call @take(
+// RESUME: cir.do {
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP2:[^,]+]],
+// RESUME: cir.call @_ZN1EC1ERK1S(
+// RESUME-NOEH: } cleanup normal {
+// RESUME-EH: } cleanup all {
+// RESUME-NEXT: cir.call @_ZN1SD1Ev(%[[TMP2]])
+// RESUME: } while {
+// RESUME: cir.call @take2(
+// RESUME: cir.call @_ZN2AN12await_resumeEi(
+// RESUME: },)
+// RESUME: cir.call @resume_use(
+
+extern "C" Task array_elements() {
+  resume_use(co_await AN{});
+  mark();
+}
+
+// RESUME-LABEL: cir.func coroutine {{.*}} @asm_operand(
+// RESUME: cir.await(user, ready : {
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP:[^,]+]],
+// RESUME: },)
+// RESUME: cir.asm(
+// RESUME: cir.call @_ZN1SD1Ev(%[[TMP]])
+// RESUME: cir.call @mark()
+
+extern "C" Task asm_operand() {
+  asm volatile("" : : "r"(co_await DA{}));
+  mark();
+}
+
+// RESUME-LABEL: cir.func coroutine {{.*}} @coyield_arg(
+// RESUME: cir.await(yield, ready : {
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP:[^,]+]],
+// RESUME: },)
+// RESUME: cir.call @resume_use(
+// RESUME: cir.call @_ZN1SD1Ev(%[[TMP]])
+
+extern "C" Task coyield_arg() {
+  resume_use(co_yield 1);
+}
+
+struct TransformTask {
+  struct promise_type {
+    TransformTask get_return_object();
+    std::suspend_never initial_suspend();
+    std::suspend_never final_suspend() noexcept;
+    void return_void();
+    void unhandled_exception();
+    PL await_transform(int, int = resume_flag() ? resume_arg(S(5)) : 0);
+  };
+};
+
+// RESUME-LABEL: cir.func coroutine {{.*}} @transform_condtemp(
+// RESUME: %[[FLAG:.*]] = cir.alloca "cleanup.cond"
+// RESUME: cir.ternary(
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP:[^,]+]],
+// RESUME: cir.call @_ZN13TransformTask12promise_type15await_transformEii(
+// RESUME: cir.await(user, ready : {
+// RESUME: },)
+// RESUME: cir.call @resume_use(
+// RESUME: %[[LIVE:.*]] = cir.load {{.*}} %[[FLAG]]
+// RESUME-NEXT: cir.if %[[LIVE]] {
+// RESUME-NEXT: cir.call @_ZN1SD1Ev(%[[TMP]])
+
+extern "C" TransformTask transform_condtemp() {
+  resume_use(co_await 1);
+}
+
+struct NoexceptInitAwaiter {
+  bool await_ready();
+  void await_suspend(std::coroutine_handle<>);
+  void await_resume(const S & = S(1)) noexcept;
+};
+
+struct NoexceptFinalAwaiter {
+  bool await_ready() noexcept;
+  void await_suspend(std::coroutine_handle<>) noexcept;
+  void await_resume(const S & = S(2)) noexcept;
+};
+
+struct DefaultInitTask {
+  struct promise_type {
+    DefaultInitTask get_return_object();
+    NoexceptInitAwaiter initial_suspend();
+    NoexceptFinalAwaiter final_suspend() noexcept;
+    void return_void();
+    void unhandled_exception();
+  };
+};
+
+// RESUME-LABEL: cir.func coroutine {{.*}} @init_final_noexcept(
+// RESUME-NOT: resume.eh
+// RESUME: cir.await(init, ready : {
+// RESUME: cir.call @_ZN1SC1Ei(%[[INIT_TMP:[^,]+]],
+// RESUME: cir.call @_ZN19NoexceptInitAwaiter12await_resumeERK1S(
+// RESUME: },)
+// RESUME: cir.call @_ZN1SD1Ev(%[[INIT_TMP]])
+// RESUME: }, body : {
+// RESUME: cir.call @mark()
+// RESUME: }, finalSuspend : {
+// RESUME: cir.call @_ZN1SC1Ei(%[[FINAL_TMP:[^,]+]],
+// RESUME: cir.call @_ZN20NoexceptFinalAwaiter12await_resumeERK1S(
+// RESUME: },)
+// RESUME: cir.call @_ZN1SD1Ev(%[[FINAL_TMP]])
+
+extern "C" DefaultInitTask init_final_noexcept() {
+  mark();
+  co_return;
+}
+
+struct InitAwaiter {
+  bool await_ready();
+  void await_suspend(std::coroutine_handle<>);
+  void await_resume(const S & = S(1), int = resume_flag() ? resume_arg(S(2)) : 
0);
+};
+
+struct InitTask {
+  struct promise_type {
+    InitTask get_return_object();
+    InitAwaiter initial_suspend();
+    std::suspend_never final_suspend() noexcept;
+    void return_void();
+    void unhandled_exception();
+  };
+};
+
+// RESUME-LABEL: cir.func coroutine {{.*}} @init_try(
+// RESUME-EH: %[[RESUMED:.*]] = cir.alloca "resume.eh"
+// RESUME: cir.await(init, ready : {
+// RESUME-EH: cir.try {
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP1:[^,]+]],
+// RESUME: cir.ternary(
+// RESUME: cir.call @_ZN1SC1Ei(%[[TMP2:[^,]+]],
+// RESUME: cir.call @_ZN11InitAwaiter12await_resumeERK1Si(
+// RESUME: cir.call @_ZN1SD1Ev(%[[TMP2]])
+// RESUME: cir.call @_ZN1SD1Ev(%[[TMP1]])
+// RESUME-EH-NEXT: cir.yield
+// RESUME-EH-NEXT: }
+// RESUME-EH-NEXT: %[[TRUE:.*]] = cir.const #true
+// RESUME-EH-NEXT: cir.store %[[TRUE]], %[[RESUMED]]
+// RESUME-EH: } catch all
+// RESUME: }, body : {
+// RESUME: cir.call @mark()
+
+extern "C" InitTask init_try() {
+  mark();
+  co_return;
+}
+
+struct ReadySuspendArgs {
+  bool await_ready(const S & = S(8));
+  void await_suspend(std::coroutine_handle<>, const S & = S(9));
+  int await_resume();
+};
+
+// Ready and suspend arguments end before the next coroutine phase.
+// RESUME-LABEL: cir.func coroutine {{.*}} @ready_suspend_in_conditional(
+// RESUME: cir.ternary(
+// RESUME: cir.await(user, ready : {
+// RESUME: cir.call @_ZN1SC1Ei(%[[READY:[^,]+]],
+// RESUME: cir.call @_ZN16ReadySuspendArgs11await_readyERK1S(
+// RESUME-NOEH: } cleanup normal {
+// RESUME-EH: } cleanup all {
+// RESUME-NEXT: cir.call @_ZN1SD1Ev(%[[READY]])
+// RESUME: }, suspend : {
+// RESUME: cir.call @_ZN1SC1Ei(%[[SUSPEND:[^,]+]],
+// RESUME: cir.call 
@_ZN16ReadySuspendArgs13await_suspendESt16coroutine_handleIvERK1S(
+// RESUME-NOEH: } cleanup normal {
+// RESUME-EH: } cleanup all {
+// RESUME-NEXT: cir.call @_ZN1SD1Ev(%[[SUSPEND]])
+// RESUME: cir.coro.suspend_point
+// RESUME: }, resume : {
+// RESUME: cir.call @_ZN16ReadySuspendArgs12await_resumeEv(
+
+extern "C" Task ready_suspend_in_conditional(bool b) { resume_use(b ? co_await 
ReadySuspendArgs{} : 0); }


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to