Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
27 changes: 25 additions & 2 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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!");
Expand Down
44 changes: 44 additions & 0 deletions clang/test/Modules/gh110401.cppm
Original file line number Diff line number Diff line change
@@ -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);
}
Loading