[clang] 6030416 - [C++20] [Modules] Add the abbrev number for coro-await-elidable calls

Chuanqi Xu via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 4 23:58:54 PDT 2025


Author: Chuanqi Xu
Date: 2025-09-05T14:58:05+08:00
New Revision: 60304161ce44c25dec7b87dd6a593d09eea5545a

URL: https://github.com/llvm/llvm-project/commit/60304161ce44c25dec7b87dd6a593d09eea5545a
DIFF: https://github.com/llvm/llvm-project/commit/60304161ce44c25dec7b87dd6a593d09eea5545a.diff

LOG: [C++20] [Modules] Add the abbrev number for coro-await-elidable calls

The root cause of the issue is that we forgot to update the abbrev
number correctly.

The test would crash before.

Added: 
    clang/test/Modules/coro-await-elidable.cppm

Modified: 
    clang/lib/Serialization/ASTWriterStmt.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index e36d83fe4559b..dda81557cb22f 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -979,7 +979,8 @@ void ASTStmtWriter::VisitCallExpr(CallExpr *E) {
     Record.push_back(E->getFPFeatures().getAsOpaqueInt());
 
   if (!E->hasStoredFPFeatures() && !static_cast<bool>(E->getADLCallKind()) &&
-      !E->usesMemberSyntax() && E->getStmtClass() == Stmt::CallExprClass)
+      !E->isCoroElideSafe() && !E->usesMemberSyntax() &&
+      E->getStmtClass() == Stmt::CallExprClass)
     AbbrevToUse = Writer.getCallExprAbbrev();
 
   Code = serialization::EXPR_CALL;
@@ -1712,7 +1713,8 @@ void ASTStmtWriter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
   Record.push_back(E->getOperator());
   Record.AddSourceLocation(E->BeginLoc);
 
-  if (!E->hasStoredFPFeatures() && !static_cast<bool>(E->getADLCallKind()))
+  if (!E->hasStoredFPFeatures() && !static_cast<bool>(E->getADLCallKind()) &&
+      !E->isCoroElideSafe() && !E->usesMemberSyntax())
     AbbrevToUse = Writer.getCXXOperatorCallExprAbbrev();
 
   Code = serialization::EXPR_CXX_OPERATOR_CALL;
@@ -1721,7 +1723,8 @@ void ASTStmtWriter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
 void ASTStmtWriter::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) {
   VisitCallExpr(E);
 
-  if (!E->hasStoredFPFeatures() && !static_cast<bool>(E->getADLCallKind()))
+  if (!E->hasStoredFPFeatures() && !static_cast<bool>(E->getADLCallKind()) &&
+      !E->isCoroElideSafe() && !E->usesMemberSyntax())
     AbbrevToUse = Writer.getCXXMemberCallExprAbbrev();
 
   Code = serialization::EXPR_CXX_MEMBER_CALL;

diff  --git a/clang/test/Modules/coro-await-elidable.cppm b/clang/test/Modules/coro-await-elidable.cppm
new file mode 100644
index 0000000000000..2d635c6efa88b
--- /dev/null
+++ b/clang/test/Modules/coro-await-elidable.cppm
@@ -0,0 +1,200 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 %t/task.cppm -I%t -emit-reduced-module-interface -o %t/task.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/user.cpp -verify -fsyntax-only
+
+//--- coroutine.h
+
+namespace std {
+
+template <typename R, typename...> struct coroutine_traits {
+    using promise_type = typename R::promise_type;
+};
+
+template <typename Promise = void> struct coroutine_handle;
+
+template <> struct coroutine_handle<void> {
+    static coroutine_handle from_address(void *addr) noexcept {
+    coroutine_handle me;
+    me.ptr = addr;
+    return me;
+    }
+    void operator()() { resume(); }
+    void *address() const noexcept { return ptr; }
+    void resume() const { __builtin_coro_resume(ptr); }
+    void destroy() const { __builtin_coro_destroy(ptr); }
+    bool done() const { return __builtin_coro_done(ptr); }
+    coroutine_handle &operator=(decltype(nullptr)) {
+    ptr = nullptr;
+    return *this;
+    }
+    coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
+    coroutine_handle() : ptr(nullptr) {}
+//  void reset() { ptr = nullptr; } // add to P0057?
+    explicit operator bool() const { return ptr; }
+
+protected:
+    void *ptr;
+};
+
+template <typename Promise> struct coroutine_handle : coroutine_handle<> {
+    using coroutine_handle<>::operator=;
+
+    static coroutine_handle from_address(void *addr) noexcept {
+    coroutine_handle me;
+    me.ptr = addr;
+    return me;
+    }
+
+    Promise &promise() const {
+    return *reinterpret_cast<Promise *>(
+        __builtin_coro_promise(ptr, alignof(Promise), false));
+    }
+    static coroutine_handle from_promise(Promise &promise) {
+    coroutine_handle p;
+    p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);
+    return p;
+    }
+};
+
+template <typename _PromiseT>
+bool operator==(coroutine_handle<_PromiseT> const &_Left,
+                coroutine_handle<_PromiseT> const &_Right) noexcept {
+    return _Left.address() == _Right.address();
+}
+
+template <typename _PromiseT>
+bool operator!=(coroutine_handle<_PromiseT> const &_Left,
+                coroutine_handle<_PromiseT> const &_Right) noexcept {
+    return !(_Left == _Right);
+}
+
+struct noop_coroutine_promise {};
+
+template <>
+struct coroutine_handle<noop_coroutine_promise> {
+    operator coroutine_handle<>() const noexcept {
+    return coroutine_handle<>::from_address(address());
+    }
+
+    constexpr explicit operator bool() const noexcept { return true; }
+    constexpr bool done() const noexcept { return false; }
+
+    constexpr void operator()() const noexcept {}
+    constexpr void resume() const noexcept {}
+    constexpr void destroy() const noexcept {}
+
+    noop_coroutine_promise &promise() const noexcept {
+    return *static_cast<noop_coroutine_promise *>(
+        __builtin_coro_promise(this->__handle_, alignof(noop_coroutine_promise), false));
+    }
+
+    constexpr void *address() const noexcept { return __handle_; }
+
+private:
+    friend coroutine_handle<noop_coroutine_promise> noop_coroutine() noexcept;
+
+    coroutine_handle() noexcept {
+    this->__handle_ = __builtin_coro_noop();
+    }
+
+    void *__handle_ = nullptr;
+};
+
+using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
+
+inline noop_coroutine_handle noop_coroutine() noexcept { return noop_coroutine_handle(); }
+
+struct suspend_always {
+    bool await_ready() noexcept { return false; }
+    void await_suspend(coroutine_handle<>) noexcept {}
+    void await_resume() noexcept {}
+};
+struct suspend_never {
+    bool await_ready() noexcept { return true; }
+    void await_suspend(coroutine_handle<>) noexcept {}
+    void await_resume() noexcept {}
+};
+
+} // namespace std
+
+//--- task.cppm
+module;
+#include "coroutine.h"
+export module task;
+export template <typename T>
+struct [[clang::coro_await_elidable]] Task {
+  struct promise_type {
+    struct FinalAwaiter {
+      bool await_ready() const noexcept { return false; }
+
+      template <typename P>
+      std::coroutine_handle<> await_suspend(std::coroutine_handle<P> coro) noexcept {
+        if (!coro)
+          return std::noop_coroutine();
+        return coro.promise().continuation;
+      }
+      void await_resume() noexcept {}
+    };
+
+    Task get_return_object() noexcept {
+      return std::coroutine_handle<promise_type>::from_promise(*this);
+    }
+
+    std::suspend_always initial_suspend() noexcept { return {}; }
+    FinalAwaiter final_suspend() noexcept { return {}; }
+    void unhandled_exception() noexcept {}
+    void return_value(T x) noexcept {
+      value = x;
+    }
+
+    std::coroutine_handle<> continuation;
+    T value;
+  };
+
+  Task(std::coroutine_handle<promise_type> handle) : handle(handle) {}
+  ~Task() {
+    if (handle)
+      handle.destroy();
+  }
+
+  struct Awaiter {
+    Awaiter(Task *t) : task(t) {}
+    bool await_ready() const noexcept { return false; }
+    void await_suspend(std::coroutine_handle<void> continuation) noexcept {}
+    T await_resume() noexcept {
+      return task->handle.promise().value;
+    }
+
+    Task *task;
+  };
+
+  auto operator co_await() {
+    return Awaiter{this};
+  }
+
+private:
+  std::coroutine_handle<promise_type> handle;
+};
+
+inline Task<int> callee() {
+    co_return 1;
+}
+
+export inline Task<int> elidable() {
+    co_return co_await callee();
+}
+
+namespace std {
+    export using std::coroutine_traits;
+    export using std::coroutine_handle;
+    export using std::suspend_always;
+}
+
+//--- user.cpp
+// expected-no-diagnostics
+import task;
+Task<int> test() {
+    co_return co_await elidable();
+}


        


More information about the cfe-commits mailing list