[clang] [Clang] Instantiate the correct lambda call operator (PR #110446)

via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 8 02:08:00 PDT 2024


https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/110446

>From a61f3e94d0636f4294f3be440d79b83889771800 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 30 Sep 2024 02:58:31 +0200
Subject: [PATCH 1/3] [Clang] Instantiate the correct lambda call operator

---
 clang/docs/ReleaseNotes.rst      |  1 +
 clang/lib/AST/DeclCXX.cpp        | 27 ++++++++++++++++++--
 clang/test/Modules/gh110401.cppm | 44 ++++++++++++++++++++++++++++++++
 3 files changed, 70 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/Modules/gh110401.cppm

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 14907e7db18de3..17a0c45d98c5c8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -426,6 +426,7 @@ Bug Fixes to C++ Support
 - Fixed an assertion failure in debug mode, and potential crashes in release mode, when
   diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.
 - Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326)
+- Clang now instantiates the correct lambda call operator when a lambda's class type is merged across modules. (#GH110401)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 01143391edab40..af9c644337c84d 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1631,9 +1631,32 @@ static bool allLookupResultsAreTheSame(const DeclContext::lookup_result &R) {
 static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) {
   if (!RD.isLambda()) return nullptr;
   DeclarationName Name =
-    RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
-  DeclContext::lookup_result Calls = RD.lookup(Name);
+      RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
+
+  // If we have multiple call operators, we might be in a situation
+  // where we merged this lambda with one from another module; in that
+  // case, return our method (instead of that of the other lambda).
+  //
+  // This avoids situations where, given two modules A and B, if we
+  // try to instantiate A's call operator in a function in B, anything
+  // in the call operator that relies on local decls in the surrounding
+  // function will crash because it tries to find A's decls, but we only
+  // instantiated B's:
+  //
+  //   template <typename>
+  //   void f() {
+  //     using T = int;      // We only instantiate B's version of this.
+  //     auto L = [](T) { }; // But A's call operator wants A's here.
+  //   }
+  //
+  // To mitigate this, search our own decls first.
+  // FIXME: This feels like a hack; is there a better way of doing this?
+  for (CXXMethodDecl *M : RD.methods())
+    if (M->getDeclName() == Name)
+      return M;
 
+  // Otherwise, do a full lookup to find external decls too.
+  DeclContext::lookup_result Calls = RD.lookup(Name);
   assert(!Calls.empty() && "Missing lambda call operator!");
   assert(allLookupResultsAreTheSame(Calls) &&
          "More than one lambda call operator!");
diff --git a/clang/test/Modules/gh110401.cppm b/clang/test/Modules/gh110401.cppm
new file mode 100644
index 00000000000000..6b335eb5ba9d55
--- /dev/null
+++ b/clang/test/Modules/gh110401.cppm
@@ -0,0 +1,44 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux -emit-module-interface %t/a.cppm -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux -emit-module-interface -fprebuilt-module-path=%t %t/b.cppm -o %t/B.pcm
+
+// Just check that this doesn't crash.
+
+//--- a.cppm
+module;
+
+template <typename _Visitor>
+void __do_visit(_Visitor &&__visitor) {
+  using _V0 = int;
+  [](_V0 __v) -> _V0 { return __v; } (1);
+}
+
+export module A;
+
+void g() {
+  struct Visitor { };
+  __do_visit(Visitor());
+}
+
+//--- b.cppm
+module;
+
+template <typename _Visitor>
+void __do_visit(_Visitor &&__visitor) {
+  using _V0 = int;
+
+  // Check that we instantiate this lambda's call operator in 'f' below
+  // instead of the one in 'a.cppm' here; otherwise, we won't find a
+  // corresponding instantiation of the using declaration above.
+  [](_V0 __v) -> _V0 { return __v; } (1);
+}
+
+export module B;
+import A;
+
+void f() {
+  __do_visit(1);
+}

>From d3b5acc0a43bf227d57b3d7f1c68acedb89e3123 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 30 Sep 2024 17:34:43 +0200
Subject: [PATCH 2/3] Walk redecl chain to find the right operator

---
 clang/lib/AST/DeclCXX.cpp | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index af9c644337c84d..da7a8ac0e5fe0f 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1633,6 +1633,11 @@ static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) {
   DeclarationName Name =
       RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
 
+  DeclContext::lookup_result Calls = RD.lookup(Name);
+  assert(!Calls.empty() && "Missing lambda call operator!");
+  assert(allLookupResultsAreTheSame(Calls) &&
+         "More than one lambda call operator!");
+
   // If we have multiple call operators, we might be in a situation
   // where we merged this lambda with one from another module; in that
   // case, return our method (instead of that of the other lambda).
@@ -1646,21 +1651,19 @@ static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) {
   //   template <typename>
   //   void f() {
   //     using T = int;      // We only instantiate B's version of this.
-  //     auto L = [](T) { }; // But A's call operator wants A's here.
+  //     auto L = [](T) { }; // But A's call operator would want A's here.
   //   }
   //
-  // To mitigate this, search our own decls first.
-  // FIXME: This feels like a hack; is there a better way of doing this?
-  for (CXXMethodDecl *M : RD.methods())
-    if (M->getDeclName() == Name)
-      return M;
+  // Walk the call operator’s redecl chain to find the one that belongs
+  // to this module.
+  Module *M = RD.getOwningModule();
+  for (Decl *D : Calls.front()->redecls()) {
+    auto *MD = cast<NamedDecl>(D);
+    if (MD->getOwningModule() == M)
+      return MD;
+  }
 
-  // Otherwise, do a full lookup to find external decls too.
-  DeclContext::lookup_result Calls = RD.lookup(Name);
-  assert(!Calls.empty() && "Missing lambda call operator!");
-  assert(allLookupResultsAreTheSame(Calls) &&
-         "More than one lambda call operator!");
-  return Calls.front();
+  llvm_unreachable("Couldn't find our call operator!");
 }
 
 FunctionTemplateDecl* CXXRecordDecl::getDependentLambdaCallOperator() const {

>From 1cc387a50b24237ce4b96b466a9d00ba8524b51f Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 8 Oct 2024 10:29:22 +0200
Subject: [PATCH 3/3] Add FIXME and TODO

---
 clang/lib/AST/DeclCXX.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index a522627b694fb3..1364ccc745ba01 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1638,7 +1638,7 @@ static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) {
   assert(allLookupResultsAreTheSame(Calls) &&
          "More than one lambda call operator!");
 
-  // If we have multiple call operators, we might be in a situation
+  // FIXME: If we have multiple call operators, we might be in a situation
   // where we merged this lambda with one from another module; in that
   // case, return our method (instead of that of the other lambda).
   //
@@ -1656,6 +1656,9 @@ static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) {
   //
   // Walk the call operator’s redecl chain to find the one that belongs
   // to this module.
+  //
+  // TODO: We need to fix this properly (see
+  // https://github.com/llvm/llvm-project/issues/90154).
   Module *M = RD.getOwningModule();
   for (Decl *D : Calls.front()->redecls()) {
     auto *MD = cast<NamedDecl>(D);



More information about the cfe-commits mailing list