https://github.com/YexuanXiao updated 
https://github.com/llvm/llvm-project/pull/179189

>From 8bf87ecd3e72b97d51d0e83fd550815e021a9e5d Mon Sep 17 00:00:00 2001
From: Yexuan Xiao <[email protected]>
Date: Mon, 2 Feb 2026 17:24:58 +0800
Subject: [PATCH 1/2] Resolve CWG2935, but if some functions are not marked as
 noexcept, the patch will block HALO

---
 clang/lib/CodeGen/CGCoroutine.cpp        |  52 +-
 clang/test/CodeGenCoroutines/cwg2935.cpp |  65 +++
 coroutine-cwg2935.cpp                    | 612 +++++++++++++++++++++++
 3 files changed, 724 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/CodeGenCoroutines/cwg2935.cpp
 create mode 100644 coroutine-cwg2935.cpp

diff --git a/clang/lib/CodeGen/CGCoroutine.cpp 
b/clang/lib/CodeGen/CGCoroutine.cpp
index 7282c42420657..cf5b9546995d6 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -134,24 +134,27 @@ static SmallString<32> buildSuspendPrefixStr(CGCoroData 
&Coro, AwaitKind Kind) {
 
 // Check if function can throw based on prototype noexcept, also works for
 // destructors which are implicitly noexcept but can be marked noexcept(false).
-static bool FunctionCanThrow(const FunctionDecl *D) {
+static bool FunctionCanThrow(const FunctionDecl *D, bool InitialSuspend) {
   const auto *Proto = D->getType()->getAs<FunctionProtoType>();
   if (!Proto) {
     // Function proto is not found, we conservatively assume throwing.
     return true;
   }
+  if (InitialSuspend && D->getIdentifier() &&
+      (D->getIdentifier()->getName() == "await_resume"))
+    return false;
   return !isNoexceptExceptionSpec(Proto->getExceptionSpecType()) ||
          Proto->canThrow() != CT_Cannot;
 }
 
-static bool StmtCanThrow(const Stmt *S) {
+static bool StmtCanThrow(const Stmt *S, bool InitialSuspend = false) {
   if (const auto *CE = dyn_cast<CallExpr>(S)) {
     const auto *Callee = CE->getDirectCallee();
     if (!Callee)
       // We don't have direct callee. Conservatively assume throwing.
       return true;
 
-    if (FunctionCanThrow(Callee))
+    if (FunctionCanThrow(Callee, InitialSuspend))
       return true;
 
     // Fall through to visit the children.
@@ -163,14 +166,14 @@ static bool StmtCanThrow(const Stmt *S) {
     // We need to mark entire statement as throwing if the destructor of the
     // temporary throws.
     const auto *Dtor = TE->getTemporary()->getDestructor();
-    if (FunctionCanThrow(Dtor))
+    if (FunctionCanThrow(Dtor, false))
       return true;
 
     // Fall through to visit the children.
   }
 
   for (const auto *child : S->children())
-    if (StmtCanThrow(child))
+    if (StmtCanThrow(child, InitialSuspend))
       return true;
 
   return false;
@@ -657,10 +660,19 @@ struct GetReturnObjectManager {
   bool DirectEmit = false;
 
   Address GroActiveFlag;
+  // Active flag used for DirectEmit return value cleanup. When the coroutine
+  // return value is directly emitted into the return slot, we need to run its
+  // destructor if an exception is thrown before initial-await-resume.
+  // DirectGroActiveFlag is unused when InitialSuspendCanThrow is false.
+  bool InitialSuspendCanThrow = false;
+  Address DirectGroActiveFlag;
+
   CodeGenFunction::AutoVarEmission GroEmission;
 
   GetReturnObjectManager(CodeGenFunction &CGF, const CoroutineBodyStmt &S)
       : CGF(CGF), Builder(CGF.Builder), S(S), 
GroActiveFlag(Address::invalid()),
+        InitialSuspendCanThrow(StmtCanThrow(S.getInitSuspendStmt(), true)),
+        DirectGroActiveFlag(Address::invalid()),
         GroEmission(CodeGenFunction::AutoVarEmission::invalid()) {
     // The call to get_­return_­object is sequenced before the call to
     // initial_­suspend and is invoked at most once, but there are caveats
@@ -768,6 +780,26 @@ struct GetReturnObjectManager {
         CGF.EmitAnyExprToMem(S.getReturnValue(), CGF.ReturnValue,
                              S.getReturnValue()->getType().getQualifiers(),
                              /*IsInit*/ true);
+        // If the return value has a non-trivial destructor, register a
+        // conditional cleanup that will destroy it if an exception is thrown
+        // before initial-await-resume. The cleanup is activated now and will
+        // be deactivated once initial_suspend completes normally.
+        if (InitialSuspendCanThrow) {
+          if (QualType::DestructionKind DtorKind =
+                  S.getReturnValue()->getType().isDestructedType();
+              DtorKind) {
+            // Create an active flag (initialize to true) for conditional
+            // cleanup. We are not necessarily in a conditional branch here so
+            // use a simple temp alloca instead of createCleanupActiveFlag().
+            auto ActiveFlag = CGF.CreateTempAlloca(
+                Builder.getInt1Ty(), CharUnits::One(), "direct.gro.active");
+            Builder.CreateStore(Builder.getTrue(), ActiveFlag);
+            CGF.pushDestroyAndDeferDeactivation(DtorKind, CGF.ReturnValue,
+                                                S.getReturnValue()->getType());
+            CGF.initFullExprCleanupWithFlag(ActiveFlag);
+            DirectGroActiveFlag = ActiveFlag;
+          }
+        }
       }
       return;
     }
@@ -782,6 +814,7 @@ struct GetReturnObjectManager {
     CGF.EmitAutoVarInit(GroEmission);
     Builder.CreateStore(Builder.getTrue(), GroActiveFlag);
   }
+
   // The GRO returns either when it is first suspended or when it completes
   // without ever being suspended. The EmitGroConv function evaluates these
   // conditions and perform the conversion if needed.
@@ -987,6 +1020,15 @@ void CodeGenFunction::EmitCoroutineBody(const 
CoroutineBodyStmt &S) {
     CurCoro.Data->CurrentAwaitKind = AwaitKind::Init;
     CurCoro.Data->ExceptionHandler = S.getExceptionHandler();
     EmitStmt(S.getInitSuspendStmt());
+
+    // If we emitted the return value directly into the return slot, the
+    // destructor cleanup we registered above should only be active while
+    // initial_suspend is in progress. After initial_suspend completes
+    // normally, deactivate the cleanup so the return value is not
+    // destroyed here.
+    if (GroManager.DirectEmit && GroManager.DirectGroActiveFlag.isValid())
+      Builder.CreateStore(Builder.getFalse(), GroManager.DirectGroActiveFlag);
+
     CurCoro.Data->FinalJD = getJumpDestInCurrentScope(FinalBB);
 
     CurCoro.Data->CurrentAwaitKind = AwaitKind::Normal;
diff --git a/clang/test/CodeGenCoroutines/cwg2935.cpp 
b/clang/test/CodeGenCoroutines/cwg2935.cpp
new file mode 100644
index 0000000000000..89057cd0f9f10
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/cwg2935.cpp
@@ -0,0 +1,65 @@
+// Verifies lifetime of __gro local variable
+// Verify that coroutine promise and allocated memory are freed up on 
exception.
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - 
%s -disable-llvm-passes | FileCheck %s
+
+#include "Inputs/coroutine.h"
+
+using namespace std;
+
+struct task {
+  struct promise_type {
+    task get_return_object();
+    suspend_always initial_suspend();
+    suspend_always final_suspend() noexcept;
+    void return_void();
+    void unhandled_exception();
+  };
+  ~task();
+};
+
+task f1() {
+  co_return;
+}
+// CHECK:       define dso_local void @_Z2f1v
+// CHECK:       entry:
+// CHECK-NEXT:    %result.ptr = alloca ptr, align 8
+// CHECK-NEXT:    %__promise = alloca %"struct.task::promise_type", align 1
+// CHECK-NEXT:    %direct.gro.active = alloca i1, align 1
+// CHECK:       coro.init:                                        ; preds = 
%coro.alloc, %entry
+// CHECK-NEXT:    %3 = phi ptr [ null, %entry ], [ %call, %coro.alloc ]
+// CHECK-NEXT:    %4 = call ptr @llvm.coro.begin(token %0, ptr %3)
+// CHECK-NEXT:    call void @llvm.lifetime.start.p{{.*}}(ptr %__promise) #2
+// CHECK-NEXT:    call void @_ZN4task12promise_type17get_return_objectEv(ptr 
dead_on_unwind writable sret(%struct.task) align 1 %agg.result, ptr noundef 
nonnull align 1 dereferenceable(1) %__promise)
+// CHECK-NEXT:    store i1 true, ptr %direct.gro.active, align 1
+// CHECK:       cleanup.cont:                                     ; preds = 
%cleanup
+// CHECK-NEXT:    store i1 false, ptr %direct.gro.active, align 1
+// CHECK-NEXT:    call void @_ZN4task12promise_type11return_voidEv(ptr noundef 
nonnull align 1 dereferenceable(1) %__promise)
+// CHECK-NEXT:    br label %coro.final
+// CHECK:       cleanup.cont{{.*}}:
+// CHECK-NEXT:    store i1 false, ptr %direct.gro.active, align 1
+// CHECK-NEXT:    br label %cleanup{{.*}}
+// CHECK:       cleanup.action:                                   ; preds = 
%cleanup{{.*}}
+// CHECK-NEXT:    call void @_ZN4taskD1Ev(ptr noundef nonnull align 1 
dereferenceable(1) %agg.result) #2
+// CHECK-NEXT:    br label %cleanup.done
+
+struct intial_awaiter_resume_throw : std::suspend_always {
+  void await_resume();
+};
+
+struct task_resume_throw {
+  struct promise_type {
+    task_resume_throw get_return_object();
+    suspend_always initial_suspend() noexcept;
+    suspend_always final_suspend() noexcept;
+    void return_void();
+    void unhandled_exception();
+  };
+  ~task_resume_throw();
+};
+
+task_resume_throw f2() {
+  co_return;
+}
+
+// CHECK:       define dso_local void @_Z2f2v
+// CHECK-NOT:     %direct.gro.active = alloca i1, align 1
\ No newline at end of file
diff --git a/coroutine-cwg2935.cpp b/coroutine-cwg2935.cpp
new file mode 100644
index 0000000000000..6f18c7dae1a28
--- /dev/null
+++ b/coroutine-cwg2935.cpp
@@ -0,0 +1,612 @@
+#include <array>
+#include <cassert>
+#include <coroutine>
+#include <cstdlib>
+
+enum check_points {
+  para_copy_ctor,
+  para_dtor,
+  promise_ctor,
+  promise_dtor,
+  get_return_obj,
+  task_ctor,
+  task_dtor,
+  init_suspend,
+  init_a_ready,
+  init_a_suspend,
+  init_a_resume,
+  awaiter_ctor,
+  awaiter_dtor,
+  return_v,
+  unhandled,
+  fin_suspend
+};
+
+using per_test_counts_type = std::array<int, fin_suspend + 1>;
+
+per_test_counts_type per_test_counts{};
+
+void record(check_points cp) {
+  // Each checkpoint's executions must be precisely recorded to prevent double
+  // free
+  ++per_test_counts[cp];
+}
+
+void clear() { per_test_counts = per_test_counts_type{}; }
+
+std::array<bool, fin_suspend + 1> checked_cond{};
+
+// Each test will throw an exception at a designated location. After the
+// coroutine is invoked, the execution status of all checkpoints will be
+// checked, and then switch to the next test. Before throwing an exception,
+// record the execution status first.
+void throw_c(check_points cp) {
+  record(cp);
+  // Once that point has been tested, it will not be tested again.
+  if (checked_cond[cp] == false) {
+    checked_cond[cp] = true;
+    throw 0;
+  }
+}
+
+std::size_t allocate_count = 0;
+
+void *operator new(std::size_t size) {
+  ++allocate_count;
+  // When the coroutine is invoked, memory allocation is the first operation
+  // performed
+  if (void *ptr = std::malloc(size)) {
+    return ptr;
+  }
+  std::abort();
+}
+
+void operator delete(void *ptr) noexcept {
+  // Deallocation is performed last
+  --allocate_count;
+  std::free(ptr);
+}
+
+struct copy_observer {
+private:
+  copy_observer() = default;
+
+public:
+  copy_observer(copy_observer const &) { throw_c(para_copy_ctor); }
+  ~copy_observer() { record(para_dtor); }
+
+  static copy_observer get() { return {}; }
+};
+
+const auto global_observer = copy_observer::get();
+
+namespace direct_emit {
+
+struct task {
+  task() { throw_c(task_ctor); }
+  ~task() { record(task_dtor); }
+  // In this test, the task should be constructed directly, without copy 
elision
+  task(task const &) = delete;
+  struct promise_type {
+    promise_type() { throw_c(promise_ctor); }
+    ~promise_type() { record(promise_dtor); }
+    promise_type(const promise_type &) = delete;
+    task get_return_object() {
+      throw_c(get_return_obj);
+      return {};
+    }
+    auto initial_suspend() {
+      throw_c(init_suspend);
+      struct initial_awaiter {
+        initial_awaiter() { throw_c(awaiter_ctor); }
+        ~initial_awaiter() { record(awaiter_dtor); }
+        initial_awaiter(const initial_awaiter &) = delete;
+        bool await_ready() {
+          throw_c(init_a_ready);
+          return false;
+        }
+        bool await_suspend(std::coroutine_handle<void>) {
+          throw_c(init_a_suspend);
+          return false;
+        }
+        void await_resume() {
+          // From this point onward, all exceptions are handled by
+          // `unhandled_exception` Since the defect of exceptions escaping from
+          // `unhandled_exception` remains unresolved (CWG2934), this test only
+          // covers the coroutine startup phase. Once CWG2934 is resolved,
+          // further tests can be added based on this one.
+          record(init_a_resume);
+        }
+      };
+
+      return initial_awaiter{};
+    }
+    void return_void() { record(return_v); }
+    void unhandled_exception() { record(unhandled); }
+    // Note that no exceptions may leak after final_suspend is called, 
otherwise
+    // the behavior is undefined
+    std::suspend_never final_suspend() noexcept {
+      record(fin_suspend);
+      return {};
+    }
+  };
+};
+
+task coro(copy_observer) { co_return; }
+
+void catch_coro() try { coro(global_observer); } catch (...) {
+}
+
+// Currently, the conditions at the eight potential exception-throwing points
+// need to be checked. More checkpoints can be added after CWG2934 is resolved.
+void test() {
+  per_test_counts_type e{};
+  allocate_count = 0;
+  catch_coro();
+  e = {
+      1, // para_copy_ctor
+      0, // para_dtor
+      0, // promise_ctor
+      0, // promise_dtor
+      0, // get_return_obj
+      0, // task_ctor
+      0, // task_dtor
+      0, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      0, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      0, // promise_dtor
+      0, // get_return_obj
+      0, // task_ctor
+      0, // task_dtor
+      0, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      0, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      0, // task_ctor
+      0, // task_dtor
+      0, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      0, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      1, // task_ctor
+      0, // task_dtor
+      0, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      0, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      1, // task_ctor
+      1, // task_dtor
+      1, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      0, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e ==
+         per_test_counts); // Clang currently fails starting from this line. If
+  // the code you modified causes tests above this line
+  // to fail, it indicates that you have broken the
+  // correct code and should start over from scratch.
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      1, // task_ctor
+      1, // task_dtor
+      1, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      1, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      1, // task_ctor
+      1, // task_dtor
+      1, // init_suspend
+      1, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      1, // awaiter_ctor
+      1, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      1, // task_ctor
+      1, // task_dtor
+      1, // init_suspend
+      1, // init_a_ready
+      1, // init_a_suspend
+      0, // init_a_resume
+      1, // awaiter_ctor
+      1, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  // Test for execution without exceptions
+  {
+    coro(global_observer);
+  }
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      1, // task_ctor
+      1, // task_dtor
+      1, // init_suspend
+      1, // init_a_ready
+      1, // init_a_suspend
+      1, // init_a_resume
+      1, // awaiter_ctor
+      1, // awaiter_dtor
+      1, // return_v,
+      0, // unhandled,
+      1, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  assert(allocate_count == 0);
+}
+
+} // namespace direct_emit
+
+namespace no_direct_emit {
+
+struct gro_tag_t {};
+
+struct task {
+  task(gro_tag_t) { throw_c(task_ctor); }
+  ~task() { record(task_dtor); }
+  // In this test, the task should be constructed directly, without copy 
elision
+  task(task const &) = delete;
+  struct promise_type {
+    promise_type() { throw_c(promise_ctor); }
+    ~promise_type() { record(promise_dtor); }
+    promise_type(const promise_type &) = delete;
+    gro_tag_t get_return_object() {
+      throw_c(get_return_obj);
+      return {};
+    }
+    auto initial_suspend() {
+      throw_c(init_suspend);
+      struct initial_awaiter {
+        initial_awaiter() { throw_c(awaiter_ctor); }
+        ~initial_awaiter() { record(awaiter_dtor); }
+        initial_awaiter(const initial_awaiter &) = delete;
+        bool await_ready() {
+          throw_c(init_a_ready);
+          return false;
+        }
+        bool await_suspend(std::coroutine_handle<void>) {
+          throw_c(init_a_suspend);
+          return false;
+        }
+        void await_resume() {
+          // From this point onward, all exceptions are handled by
+          // `unhandled_exception` Since the defect of exceptions escaping from
+          // `unhandled_exception` remains unresolved (CWG2934), this test only
+          // covers the coroutine startup phase. Once CWG2934 is resolved,
+          // further tests can be added based on this one.
+          record(init_a_resume);
+        }
+      };
+
+      return initial_awaiter{};
+    }
+    void return_void() { record(return_v); }
+    void unhandled_exception() { record(unhandled); }
+    // Note that no exceptions may leak after final_suspend is called, 
otherwise
+    // the behavior is undefined
+    std::suspend_never final_suspend() noexcept {
+      record(fin_suspend);
+      return {};
+    }
+  };
+};
+
+task coro(copy_observer) { co_return; }
+
+void catch_coro() try { coro(global_observer); } catch (...) {
+}
+
+// Currently, the conditions at the eight potential exception-throwing points
+// need to be checked. More checkpoints can be added after CWG2934 is resolved.
+void test() {
+  per_test_counts_type e{};
+  allocate_count = 0;
+  catch_coro();
+  e = {
+      1, // para_copy_ctor
+      0, // para_dtor
+      0, // promise_ctor
+      0, // promise_dtor
+      0, // get_return_obj
+      0, // task_ctor
+      0, // task_dtor
+      0, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      0, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      0, // promise_dtor
+      0, // get_return_obj
+      0, // task_ctor
+      0, // task_dtor
+      0, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      0, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      0, // task_ctor
+      0, // task_dtor
+      0, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      0, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      0, // task_ctor
+      0, // task_dtor
+      1, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      0, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      0, // task_ctor
+      0, // task_dtor
+      1, // init_suspend
+      0, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      1, // awaiter_ctor
+      0, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      0, // task_ctor
+      0, // task_dtor
+      1, // init_suspend
+      1, // init_a_ready
+      0, // init_a_suspend
+      0, // init_a_resume
+      1, // awaiter_ctor
+      1, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      0, // task_ctor
+      0, // task_dtor
+      1, // init_suspend
+      1, // init_a_ready
+      1, // init_a_suspend
+      0, // init_a_resume
+      1, // awaiter_ctor
+      1, // awaiter_dtor
+      0, // return_v,
+      0, // unhandled,
+      0, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  catch_coro();
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      1, // task_ctor
+      0, // task_dtor
+      1, // init_suspend
+      1, // init_a_ready
+      1, // init_a_suspend
+      1, // init_a_resume
+      1, // awaiter_ctor
+      1, // awaiter_dtor
+      1, // return_v,
+      0, // unhandled,
+      1, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  // Test for execution without exceptions
+  {
+    coro(global_observer);
+  }
+  e = {
+      2, // para_copy_ctor
+      2, // para_dtor
+      1, // promise_ctor
+      1, // promise_dtor
+      1, // get_return_obj
+      1, // task_ctor
+      1, // task_dtor
+      1, // init_suspend
+      1, // init_a_ready
+      1, // init_a_suspend
+      1, // init_a_resume
+      1, // awaiter_ctor
+      1, // awaiter_dtor
+      1, // return_v,
+      0, // unhandled,
+      1, // fin_suspend
+  };
+  assert(e == per_test_counts);
+  clear();
+  assert(allocate_count == 0);
+}
+
+} // namespace no_direct_emit
+
+int main() {
+  direct_emit::test();
+  // clear the global state that records the already thrown points
+  checked_cond = {};
+  no_direct_emit::test();
+}

>From 578203b0516c090aaff7c5652bcb2a673df1f9d5 Mon Sep 17 00:00:00 2001
From: Yexuan Xiao <[email protected]>
Date: Mon, 2 Feb 2026 18:03:06 +0800
Subject: [PATCH 2/2] Add noexcept to initial_suspend to enable HALO and ensure
 the tests pass

---
 clang/test/CodeGenCoroutines/coro-halo.cpp | 2 +-
 clang/test/CodeGenCoroutines/pr56919.cpp   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/test/CodeGenCoroutines/coro-halo.cpp 
b/clang/test/CodeGenCoroutines/coro-halo.cpp
index e75bedaf81fa2..915e0efa9bbea 100644
--- a/clang/test/CodeGenCoroutines/coro-halo.cpp
+++ b/clang/test/CodeGenCoroutines/coro-halo.cpp
@@ -13,7 +13,7 @@ template <typename T> struct generator {
       this->current_value = value;
       return {};
     }
-    std::suspend_always initial_suspend() { return {}; }
+    std::suspend_always initial_suspend() noexcept { return {}; }
     std::suspend_always final_suspend() noexcept { return {}; }
     generator get_return_object() { return generator{this}; };
     void unhandled_exception() {}
diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp 
b/clang/test/CodeGenCoroutines/pr56919.cpp
index baa8c27ce6649..84a27a302e607 100644
--- a/clang/test/CodeGenCoroutines/pr56919.cpp
+++ b/clang/test/CodeGenCoroutines/pr56919.cpp
@@ -30,7 +30,7 @@ class Task final {
 
     void unhandled_exception() {}
 
-    std::suspend_always initial_suspend() { return {}; }
+    std::suspend_always initial_suspend() noexcept { return {}; }
 
     auto await_transform(Task<void> co) {
       return await_transform(std::move(co.handle_.promise()));

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

Reply via email to