Skip to content

Commit f01364e

Browse files
authored
[Clang] Instantiate the correct lambda call operator (#110446)
This is a fix for the following issue: when a lambda’s class type is merged across modules (e.g. because it is defined in a template in the GMF of some module `A`, and some other module `B` both imports `A` and has the same template in its GMF), then `getLambdaCallOperator()` might return the wrong operator (e.g. while compiling `B`, the lambda’s class type would be the one attached to `B`’s GMF, but the call operator ends up being the one attached to `A`’s GMF). This causes issues in situations where the call operator is in a template and accesses declarations in the surrounding context: when those declarations are instantated, a mapping is introduced from the original node in the template to that of the instantiation. If such an instantiation happens in `B`, and we then try to instantiate `A`’s call operator, any nodes in that call operator refer to declarations in the template in `A`, but the `LocalInstantiationScope` only contains mappings for declarations in `B`! This causes the following assertion (for godbolt links and more, see the issue below): ``` Assertion `isa<LabelDecl>(D) && "declaration not instantiated in this scope"' failed. ``` We now walk the redecl chain of the call operator to find the one that is in the same module as the record decl. This fixes #110401.
1 parent 235067b commit f01364e

File tree

3 files changed

+78
-3
lines changed

3 files changed

+78
-3
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,8 @@ Bug Fixes to C++ Support
479479
conformance of explicit instantiation behaviour with MSVC. (#GH111266)
480480
- Fixed a bug in constraint expression comparison where the ``sizeof...`` expression was not handled properly
481481
in certain friend declarations. (#GH93099)
482+
- Clang now instantiates the correct lambda call operator when a lambda's class type is
483+
merged across modules. (#GH110401)
482484

483485
Bug Fixes to AST Handling
484486
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/AST/DeclCXX.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,13 +1631,42 @@ static bool allLookupResultsAreTheSame(const DeclContext::lookup_result &R) {
16311631
static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) {
16321632
if (!RD.isLambda()) return nullptr;
16331633
DeclarationName Name =
1634-
RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
1635-
DeclContext::lookup_result Calls = RD.lookup(Name);
1634+
RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
16361635

1636+
DeclContext::lookup_result Calls = RD.lookup(Name);
16371637
assert(!Calls.empty() && "Missing lambda call operator!");
16381638
assert(allLookupResultsAreTheSame(Calls) &&
16391639
"More than one lambda call operator!");
1640-
return Calls.front();
1640+
1641+
// FIXME: If we have multiple call operators, we might be in a situation
1642+
// where we merged this lambda with one from another module; in that
1643+
// case, return our method (instead of that of the other lambda).
1644+
//
1645+
// This avoids situations where, given two modules A and B, if we
1646+
// try to instantiate A's call operator in a function in B, anything
1647+
// in the call operator that relies on local decls in the surrounding
1648+
// function will crash because it tries to find A's decls, but we only
1649+
// instantiated B's:
1650+
//
1651+
// template <typename>
1652+
// void f() {
1653+
// using T = int; // We only instantiate B's version of this.
1654+
// auto L = [](T) { }; // But A's call operator would want A's here.
1655+
// }
1656+
//
1657+
// Walk the call operator’s redecl chain to find the one that belongs
1658+
// to this module.
1659+
//
1660+
// TODO: We need to fix this properly (see
1661+
// https://github.com/llvm/llvm-project/issues/90154).
1662+
Module *M = RD.getOwningModule();
1663+
for (Decl *D : Calls.front()->redecls()) {
1664+
auto *MD = cast<NamedDecl>(D);
1665+
if (MD->getOwningModule() == M)
1666+
return MD;
1667+
}
1668+
1669+
llvm_unreachable("Couldn't find our call operator!");
16411670
}
16421671

16431672
FunctionTemplateDecl* CXXRecordDecl::getDependentLambdaCallOperator() const {

clang/test/Modules/gh110401.cppm

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux -emit-module-interface %t/a.cppm -o %t/A.pcm
6+
// 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
7+
8+
// Just check that this doesn't crash.
9+
10+
//--- a.cppm
11+
module;
12+
13+
template <typename _Visitor>
14+
void __do_visit(_Visitor &&__visitor) {
15+
using _V0 = int;
16+
[](_V0 __v) -> _V0 { return __v; } (1);
17+
}
18+
19+
export module A;
20+
21+
void g() {
22+
struct Visitor { };
23+
__do_visit(Visitor());
24+
}
25+
26+
//--- b.cppm
27+
module;
28+
29+
template <typename _Visitor>
30+
void __do_visit(_Visitor &&__visitor) {
31+
using _V0 = int;
32+
33+
// Check that we instantiate this lambda's call operator in 'f' below
34+
// instead of the one in 'a.cppm' here; otherwise, we won't find a
35+
// corresponding instantiation of the using declaration above.
36+
[](_V0 __v) -> _V0 { return __v; } (1);
37+
}
38+
39+
export module B;
40+
import A;
41+
42+
void f() {
43+
__do_visit(1);
44+
}

0 commit comments

Comments
 (0)