https://github.com/hokein created 
https://github.com/llvm/llvm-project/pull/142467

This is a follow-up fix to https://github.com/llvm/llvm-project/pull/109167.

Previously, we stored a mapping between the enclosing function and the lambda 
class declaration. When loading the enclosing function, we also loaded the 
corresponding lambda class declaration. However, loading the lambda class 
declaration does not guarantee that its call operator (a `CXXMethodDecl`) is 
loaded as well. 
As a result, if the lambda call operator is later loaded from a different 
module, we can end up with a `DeclRefExpr` that refers to a `VarDecl` from a 
different module — leading to inconsistencies.

To fix this, we should ensure the lambda call operator itself is loaded.

Fixes #141582

>From 771bf5854b55287c69de606317f5fa47e49ca364 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein...@gmail.com>
Date: Mon, 2 Jun 2025 21:30:11 +0200
Subject: [PATCH] [clang][modules] Fix lambda and its enclosing function are
 not loaded from same module.

---
 clang/include/clang/Serialization/ASTReader.h |   2 +-
 clang/lib/Serialization/ASTWriterDecl.cpp     |   2 +-
 ...ash-instantiated-in-scope-cxx-modules6.cpp | 149 ++++++++++++++++++
 3 files changed, 151 insertions(+), 2 deletions(-)
 create mode 100644 
clang/test/Headers/crash-instantiated-in-scope-cxx-modules6.cpp

diff --git a/clang/include/clang/Serialization/ASTReader.h 
b/clang/include/clang/Serialization/ASTReader.h
index 57b0266af26bb..9ccef6a3f39eb 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -566,7 +566,7 @@ class ASTReader
   /// modules. It is used for the following cases:
   /// - Lambda inside a template function definition: The main declaration is
   ///   the enclosing function, and the related declarations are the lambda
-  ///   declarations.
+  ///   call operators.
   /// - Friend function defined inside a template CXXRecord declaration: The
   ///   main declaration is the enclosing record, and the related declarations
   ///   are the friend functions.
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp 
b/clang/lib/Serialization/ASTWriterDecl.cpp
index f3d260e8671b2..b16026c4ba898 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1655,7 +1655,7 @@ void ASTDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) {
     if (auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(D->getDeclContext());
         FD && isDefinitionInDependentContext(FD)) {
       Writer.RelatedDeclsMap[Writer.GetDeclRef(FD)].push_back(
-          Writer.GetDeclRef(D));
+          Writer.GetDeclRef(D->getLambdaCallOperator()));
     }
   } else {
     Record.push_back(CXXRecNotTemplate);
diff --git a/clang/test/Headers/crash-instantiated-in-scope-cxx-modules6.cpp 
b/clang/test/Headers/crash-instantiated-in-scope-cxx-modules6.cpp
new file mode 100644
index 0000000000000..077f69ea6598d
--- /dev/null
+++ b/clang/test/Headers/crash-instantiated-in-scope-cxx-modules6.cpp
@@ -0,0 +1,149 @@
+// RUN: rm -fR %t
+// RUN: split-file %s %t
+// RUN: cd %t
+// RUN: %clang_cc1 -std=c++20 -xc++ -emit-module -fmodules foo.cppmap 
-fmodule-name=foo -fmodule-name=foo -o foo.pcm
+// RUN: %clang_cc1 -std=c++20 -xc++ -emit-module -fmodules bar.cppmap 
-fmodule-name=bar -o bar.pcm
+// RUN: %clang_cc1 -std=c++20 -xc++ -emit-module -fmodules 
experiment_context.cppmap -fmodule-name=experiment_context 
-fmodule-file=foo.pcm -fmodule-file=bar.pcm -o experiment_context.pcm
+// RUN: %clang_cc1 -verify -std=c++20 -xc++ 
-fmodule-file=experiment_context.pcm experiment_context_test.cc -o 
experiment_context_test.o
+
+// https://github.com/llvm/llvm-project/issues/141582
+//--- bar.cppmap
+module "bar" {
+  export *
+  header "co.h"
+}
+
+//--- foo.cppmap
+module "foo" {
+  export *
+  header "co.h"
+}
+
+//--- experiment_context.cppmap
+module "experiment_context" {
+  export *
+  header "lazy.h"
+
+  use "foo"
+  use "bar"
+}
+
+//--- experiment_context_test.cc
+// expected-no-diagnostics
+#include "lazy.h"
+
+namespace c9 {
+
+template <typename T>
+void WaitForCoroutine() {
+  MakeCo<T>([]() -> Co<void> {
+    co_return;
+  });
+}
+
+void test() {
+  c9::WaitForCoroutine<void>();
+}
+}
+
+//--- lazy.h
+#pragma once
+
+#include "co.h"
+
+namespace c9 {
+template <typename T, typename F>
+Co<T> MakeCo(F f)
+{
+  co_return co_await f();
+}
+}
+
+inline c9::Co<void> DoNothing() { co_return; }
+
+
+//--- co.h
+#pragma  once
+namespace std {
+
+template <class _Ret, class... _Args>
+struct coroutine_traits {};
+
+template <typename Ret, typename... Args>
+  requires requires { typename Ret::promise_type; }
+struct coroutine_traits<Ret, Args...> {
+  using promise_type = typename Ret::promise_type;
+};
+
+template <typename Promise = void>
+struct coroutine_handle;
+
+template <>
+struct coroutine_handle<void> {};
+
+template <typename Promise = void>
+struct coroutine_handle : coroutine_handle<> {
+  static coroutine_handle from_address(void *addr);
+};
+
+struct suspend_always {
+  bool await_ready() noexcept;
+  void await_suspend(coroutine_handle<>) noexcept;
+  void await_resume() noexcept;
+};
+
+}  // namespace std
+
+namespace c9 {
+
+template <typename T>
+class Co;
+
+namespace internal {
+
+template <typename T>
+class CoroutinePromise {
+ public:
+  template <typename... Args>
+  explicit CoroutinePromise(Args&&... args) {
+    // Ensure that the 'dummy_color' VarDecl referenced by the inner 
DeclRefExpr
+    // is the same declaration as the one outside the lambda.
+    // This is guaranteed because both CoroutinePromise and the lambda's call 
operator
+    // (CXXMethodDecl) are loaded from the same module.
+    const int dummy_color = 1;
+    [&]{ (void)dummy_color; }();
+  }
+
+  ~CoroutinePromise();
+  void return_void();
+  auto get_return_object() {
+    return Co<T>();
+  }
+  void unhandled_exception();
+  std::suspend_always  initial_suspend();
+
+  struct result_t {
+    ~result_t();
+    bool await_ready() const noexcept;
+    void await_suspend(std::coroutine_handle<void>) noexcept;
+    void await_resume() const noexcept;
+  };
+
+  template <typename U>
+  result_t await_transform(Co<U> co);
+
+  std::suspend_always final_suspend() noexcept;
+};
+}  // namespace internal
+
+template <typename T>
+class Co {
+ public:
+  using promise_type = internal::CoroutinePromise<void>;
+};
+
+class CoIncomingModuleBase {
+ public:
+    Co<void> CoAfterFinish() { co_return; }
+};
+}  // namespace c9

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to