[llvm-branch-commits] [clang] release/22.x: [clang] Don't use `VarDecl` of local variables as `ManglingContextDecl` for lambdas (#179035) (PR #184288)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Mar 2 19:39:56 PST 2026
Jan =?utf-8?q?Kokemüller?= <jan.kokemueller at gmail.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/184288 at github.com>
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-modules
Author: None (llvmbot)
<details>
<summary>Changes</summary>
Backport 772b15b3be153b1d2df910057af17926ea227243 765c4e6e8fb25ca999bc19654b5f324df62879ad
Requested by: @<!-- -->ChuanqiXu9
---
Full diff: https://github.com/llvm/llvm-project/pull/184288.diff
3 Files Affected:
- (modified) clang/lib/Sema/SemaLambda.cpp (+46-32)
- (added) clang/test/Modules/pr177385.cppm (+165)
- (added) clang/test/Modules/pr178893.cppm (+29)
``````````diff
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index e1b1cd3e04946..be7f73dbc267f 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -261,20 +261,6 @@ Sema::createLambdaClosureType(SourceRange IntroducerRange, TypeSourceInfo *Info,
return Class;
}
-/// Determine whether the given context is or is enclosed in an inline
-/// function.
-static bool isInInlineFunction(const DeclContext *DC) {
- while (!DC->isFileContext()) {
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
- if (FD->isInlined())
- return true;
-
- DC = DC->getLexicalParent();
- }
-
- return false;
-}
-
std::tuple<MangleNumberingContext *, Decl *>
Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
// Compute the context for allocating mangling numbers in the current
@@ -287,31 +273,33 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
DataMember,
InlineVariable,
TemplatedVariable,
+ ExternallyVisibleVariableInModulePurview,
Concept,
- NonInlineInModulePurview
} Kind = Normal;
bool IsInNonspecializedTemplate =
inTemplateInstantiation() || CurContext->isDependentContext();
+ // Checks if a VarDecl or FunctionDecl is from a module purview and externally
+ // visible. These Decls should be treated as "inline" for the purpose of
+ // mangling in the code below.
+ //
+ // See discussion in https://github.com/itanium-cxx-abi/cxx-abi/issues/186
+ //
+ // zygoloid:
+ // Yeah, I think the only cases left where lambdas don't need a
+ // mangling are when they have (effectively) internal linkage or
+ // appear in a non-inline function in a non-module translation unit.
+ static constexpr auto IsExternallyVisibleInModulePurview =
+ [](const NamedDecl *ND) -> bool {
+ return (ND->isInNamedModule() || ND->isFromGlobalModule()) &&
+ ND->isExternallyVisible();
+ };
+
// Default arguments of member function parameters that appear in a class
// definition, as well as the initializers of data members, receive special
// treatment. Identify them.
Kind = [&]() {
- if (auto *ND = dyn_cast<NamedDecl>(ManglingContextDecl ? ManglingContextDecl
- : cast<Decl>(DC))) {
- // See discussion in https://github.com/itanium-cxx-abi/cxx-abi/issues/186
- //
- // zygoloid:
- // Yeah, I think the only cases left where lambdas don't need a
- // mangling are when they have (effectively) internal linkage or appear
- // in a non-inline function in a non-module translation unit.
- Module *M = ND->getOwningModule();
- if (M && M->getTopLevelModule()->isNamedModuleUnit() &&
- ND->isExternallyVisible())
- return NonInlineInModulePurview;
- }
-
if (!ManglingContextDecl)
return Normal;
@@ -324,6 +312,9 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
if (Var->getMostRecentDecl()->isInline())
return InlineVariable;
+ if (IsExternallyVisibleInModulePurview(Var))
+ return ExternallyVisibleVariableInModulePurview;
+
if (Var->getDeclContext()->isRecord() && IsInNonspecializedTemplate)
return TemplatedVariable;
@@ -343,15 +334,35 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
return Normal;
}();
- // Itanium ABI [5.1.7]:
+ // Determine whether the given context is or is enclosed in a function that
+ // requires Decl's inside to be mangled, so either:
+ // - an inline function
+ // - or a function in a module purview that is externally visible
+ static constexpr auto IsInFunctionThatRequiresMangling =
+ [](const DeclContext *DC) -> bool {
+ while (!DC->isFileContext()) {
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
+ if (FD->isInlined() || IsExternallyVisibleInModulePurview(FD))
+ return true;
+
+ DC = DC->getLexicalParent();
+ }
+
+ return false;
+ };
+
+ // Itanium ABI [5.1.8]:
// In the following contexts [...] the one-definition rule requires closure
// types in different translation units to "correspond":
switch (Kind) {
case Normal: {
// -- the bodies of inline or templated functions
+ // -- the bodies of externally visible functions in a module purview
+ // (note: this is not yet part of the Itanium ABI, see the linked Github
+ // discussion above)
if ((IsInNonspecializedTemplate &&
!(ManglingContextDecl && isa<ParmVarDecl>(ManglingContextDecl))) ||
- isInInlineFunction(CurContext)) {
+ IsInFunctionThatRequiresMangling(CurContext)) {
while (auto *CD = dyn_cast<CapturedDecl>(DC))
DC = CD->getParent();
return std::make_tuple(&Context.getManglingNumberContext(DC), nullptr);
@@ -360,7 +371,6 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
return std::make_tuple(nullptr, nullptr);
}
- case NonInlineInModulePurview:
case Concept:
// Concept definitions aren't code generated and thus aren't mangled,
// however the ManglingContextDecl is important for the purposes of
@@ -371,8 +381,12 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
case DefaultArgument:
// -- default arguments appearing in class definitions
case InlineVariable:
+ case ExternallyVisibleVariableInModulePurview:
case TemplatedVariable:
// -- the initializers of inline or templated variables
+ // -- the initializers of externally visible variables in a module purview
+ // (note: this is not yet part of the Itanium ABI, see the linked Github
+ // discussion above)
return std::make_tuple(
&Context.getManglingNumberContext(ASTContext::NeedExtraManglingDecl,
ManglingContextDecl),
diff --git a/clang/test/Modules/pr177385.cppm b/clang/test/Modules/pr177385.cppm
new file mode 100644
index 0000000000000..02852ce3b6fe4
--- /dev/null
+++ b/clang/test/Modules/pr177385.cppm
@@ -0,0 +1,165 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+// RUN: mkdir %t/tmp
+
+// RUN: %clang_cc1 -std=c++26 %t/std.cppm -emit-module-interface -o %t/std.pcm
+// RUN: %clang_cc1 -std=c++26 %t/optional.cppm -emit-module-interface -o %t/optional.pcm \
+// RUN: -fmodule-file=std=%t/std.pcm
+// RUN: %clang_cc1 -std=c++26 %t/date.format.cppm -emit-module-interface -o %t/date.format.pcm \
+// RUN: -fmodule-file=std=%t/std.pcm \
+// RUN: -fmodule-file=bopt.optional=%t/optional.pcm
+// RUN: %clang_cc1 -std=c++26 %t/annotated.cppm -emit-module-interface -o %t/annotated.pcm \
+// RUN: -fmodule-file=std=%t/std.pcm \
+// RUN: -fmodule-file=bopt.optional=%t/optional.pcm \
+// RUN: -fmodule-file=ldgr:date.format=%t/date.format.pcm
+// RUN: %clang_cc1 -std=c++26 %t/annotated.format.cppm -emit-module-interface -o %t/annotated.format.pcm \
+// RUN: -fmodule-file=std=%t/std.pcm \
+// RUN: -fmodule-file=bopt.optional=%t/optional.pcm \
+// RUN: -fmodule-file=ldgr:date.format=%t/date.format.pcm \
+// RUN: -fmodule-file=ldgr:annotated=%t/annotated.pcm
+// RUN: %clang_cc1 -std=c++26 %t/date.test.cppm -emit-module-interface -o %t/date.test.pcm \
+// RUN: -fmodule-file=std=%t/std.pcm \
+// RUN: -fmodule-file=bopt.optional=%t/optional.pcm \
+// RUN: -fmodule-file=ldgr:date.format=%t/date.format.pcm \
+// RUN: -fmodule-file=ldgr:annotated=%t/annotated.pcm \
+// RUN: -fmodule-file=ldgr:annotated.format=%t/annotated.format.pcm
+// RUN: %clang_cc1 -std=c++26 -w %t/annotated.test.cpp -fsyntax-only -verify \
+// RUN: -fmodule-file=std=%t/std.pcm \
+// RUN: -fmodule-file=bopt.optional=%t/optional.pcm \
+// RUN: -fmodule-file=ldgr:date.format=%t/date.format.pcm \
+// RUN: -fmodule-file=ldgr:annotated=%t/annotated.pcm \
+// RUN: -fmodule-file=ldgr:annotated.format=%t/annotated.format.pcm \
+// RUN: -fmodule-file=ldgr:date.testlib=%t/date.test.pcm
+
+//--- std.cppm
+module;
+namespace std {
+inline namespace __1 {
+template <class _Tp> _Tp forward(_Tp);
+template <class... _Args>
+void __invoke(_Args... __args) noexcept(
+ noexcept(__builtin_invoke(forward(__args)...)));
+using string = char;
+struct in_place_t {
+} in_place;
+template <typename...> struct __traits;
+struct Trans_NS___visitation___base {
+ template <class _Visitor, class... _Vs>
+ static void __visit_alt(_Visitor, _Vs... __vs) {
+ __make_fmatrix<_Visitor, decltype(__vs)...>;
+ }
+ struct __dispatcher {
+ template <class _Fp, class... _Vs> static void __dispatch(_Vs... __vs) {
+ _Fp __f;
+ __invoke(__f, __vs...);
+ }
+ };
+ template <class _Fp, class... _Vs> static void __make_dispatch() {
+ __dispatcher::__dispatch<_Fp, _Vs...>;
+ }
+ template <class _Fp, class... _Vs> static void __make_fmatrix_impl() {
+ __make_dispatch<_Fp, _Vs...>;
+ }
+ template <class _Fp, class... _Vs> static void __make_fmatrix() {
+ __make_fmatrix_impl<_Fp, _Vs...>;
+ }
+};
+template <class> class __dtor;
+template <class... _Types> struct __dtor<__traits<_Types...>> {
+ ~__dtor() {
+ Trans_NS___visitation___base::__visit_alt([](auto) {}, this);
+ }
+};
+struct __move_constructor : __dtor<__traits<>> {};
+struct __assignment : __move_constructor {};
+struct __impl : __assignment {};
+template <class> struct variant {
+ __impl __impl_;
+};
+}
+}
+export module std;
+export namespace std {
+using std::forward;
+using std::in_place;
+using std::in_place_t;
+using std::string;
+using std::variant;
+}
+
+//--- optional.cppm
+export module bopt.optional;
+import std;
+namespace bopt {
+template <class T> struct wrapper {
+ template <class Func> wrapper(int, Func f) : value(std::forward(f)()) {}
+ T value;
+};
+template <class T> struct optional_base {
+ struct union_t {
+ T value_;
+ };
+ struct repr {
+ template <class OtherValue>
+ repr(OtherValue) : un_{0, [] { return union_t{}; }} {}
+ wrapper<union_t> un_;
+ };
+ template <class... Args>
+ optional_base(std::in_place_t, Args... args) : repr_{args...} {}
+ repr repr_;
+};
+export template <class T> struct optional : optional_base<T> {
+ optional();
+ template <class U> optional(U) : optional_base<T>{std::in_place, 0} {}
+ template <class U> constexpr void operator=(U) {
+ [] {};
+ }
+};
+}
+
+//--- date.format.cppm
+module ldgr:date.format;
+import bopt.optional;
+namespace ldgr {
+struct date_format {
+ template <class S> static int parse(S s) { bopt::optional<int> year{s}; }
+};
+}
+
+//--- annotated.cppm
+module ldgr:annotated;
+import std;
+import bopt.optional;
+struct lot_annotation {
+ using valuation_expr_t = std::variant<std::string>;
+ bopt::optional<valuation_expr_t> valuation_expr_;
+};
+struct annotated_amount {
+ lot_annotation lot_annotation_;
+};
+
+//--- annotated.format.cppm
+module ldgr:annotated.format;
+import :date.format;
+import :annotated;
+namespace ldgr {
+struct annotated_format {
+ static annotated_amount parse_default() {
+ bopt::optional<int> date;
+ date = date_format::parse(0);
+ }
+};
+}
+
+//--- date.test.cppm
+module ldgr:date.testlib;
+import :date.format;
+void date_from_string() { ldgr::date_format::parse(0); }
+
+//--- annotated.test.cpp
+// expected-no-diagnostics
+module ldgr:annotatedtest;
+import :annotated.format;
+import :date.testlib;
+int main() { ldgr::annotated_format::parse_default(); }
diff --git a/clang/test/Modules/pr178893.cppm b/clang/test/Modules/pr178893.cppm
new file mode 100644
index 0000000000000..e58e183d82aba
--- /dev/null
+++ b/clang/test/Modules/pr178893.cppm
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -xc++ -emit-llvm -o - %s -w | FileCheck %s
+
+// CHECK-LABEL: define {{.*}}@_ZZN8PR178893W3mod6format5parseEPiENKUlvE_clEv
+// CHECK-LABEL: define {{.*}}@_ZZN8PR178893W3mod6format5parseEPiENKUlvE0_clEv
+
+export module mod;
+
+namespace PR178893 {
+ struct format {
+ static inline int parse(int* i)
+ {
+ int number;
+ number = [&]() -> int { return i[0]; }();
+
+ volatile bool b = true;
+ if (b) {
+ auto identifier = [&]() -> int { return i[1]; }();
+ return identifier;
+ }
+
+ return number;
+ }
+ };
+
+ int test_format() {
+ int n[2] = {1, 0};
+ return format::parse(n);
+ }
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/184288
More information about the llvm-branch-commits
mailing list