https://github.com/ChuanqiXu9 created https://github.com/llvm/llvm-project/pull/218574
Close https://github.com/llvm/llvm-project/issues/217858 The crash triggers on the assertion: ```C++ assert(!DD.IsLambda && !MergeDD.IsLambda && "faked up lambda definition?"); ``` where DD.IsLambda is false and MergeDD.IsLambda is true. This is kinda surprising. But this turns out to be real for the example. ```C++ export template <class Callback> auto make_closure(Callback &cb) { return [&cb](auto &arg) noexcept(noexcept(cb(arg))) { cb(arg); }; } export template <class Callback, class Arg> void for_each(Callback &&cb, Arg &arg) noexcept(noexcept(make_closure(cb)(arg))) {} export template <class It> struct iterator { It cur; void operator++() noexcept(noexcept(for_each([](auto &i) { ++i; }, cur))) {} }; ``` Here the chained use of lambda in noexcept inside the lambda makes the loading ordering pretty uncommon and odd. And it makes the first loading lambda definition doesn't get its definition data but the later loaded redeclaration get it. As this falls into an assertion, the change itself is not expected to meet any regressions. >From 9578297f12fbeb776734b491181001e384d29068 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <[email protected]> Date: Tue, 25 Aug 2026 10:36:56 +0800 Subject: [PATCH] [C++20] [Modules] Merge lambda where the merge definition data is merged before the primary one Close https://github.com/llvm/llvm-project/issues/217858 The crash triggers on the assertion: ```C++ assert(!DD.IsLambda && !MergeDD.IsLambda && "faked up lambda definition?"); ``` where DD.IsLambda is false and MergeDD.IsLambda is true. This is kinda surprising. But this turns out to be real for the example. ```C++ export template <class Callback> auto make_closure(Callback &cb) { return [&cb](auto &arg) noexcept(noexcept(cb(arg))) { cb(arg); }; } export template <class Callback, class Arg> void for_each(Callback &&cb, Arg &arg) noexcept(noexcept(make_closure(cb)(arg))) {} export template <class It> struct iterator { It cur; void operator++() noexcept(noexcept(for_each([](auto &i) { ++i; }, cur))) {} }; ``` Here the chained use of lambda in noexcept inside the lambda makes the loading ordering pretty uncommon and odd. And it makes the first loading lambda definition doesn't get its definition data but the later loaded redeclaration get it. As this falls into an assertion, the change itself is not expected to meet any regressions. --- clang/lib/Serialization/ASTReaderDecl.cpp | 22 ++++++++- clang/test/Modules/pr217858.cppm | 56 +++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 clang/test/Modules/pr217858.cppm diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index e973b7ae71954..74e0106520011 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2103,7 +2103,27 @@ void ASTDeclMerger::MergeDefinitionData( PFDI->second == ASTReader::PendingFakeDefinitionKind::Fake) { // We faked up this definition data because we found a class for which we'd // not yet loaded the definition. Replace it with the real thing now. - assert(!DD.IsLambda && !MergeDD.IsLambda && "faked up lambda definition?"); + assert(!DD.IsLambda && "faked up lambda definition?"); + + // This is possible for some special loading ordering. See + // clang/test/Modules/pr217858.cppm for an example. + // + // LambdaDefinitionData is larger than DefinitionData, so it cannot replace + // the fake DefinitionData object in place. + if (MergeDD.IsLambda) { + auto *Def = DD.Definition; + MergeDD.Definition = Def; + // Unlike an instantiated class definition, + // whose update-record reader removes the fake entry after loading its + // lexical declarations, a lambda's definition is part of its declaration + // record and is fully loaded here. + Reader.PendingFakeDefinitionData.erase(PFDI); + for (auto *R = Reader.getMostRecentExistingDecl(Def); R; + R = R->getPreviousDecl()) + cast<CXXRecordDecl>(R)->DefinitionData = &MergeDD; + return; + } + PFDI->second = ASTReader::PendingFakeDefinitionKind::FakeLoaded; // Don't change which declaration is the definition; that is required diff --git a/clang/test/Modules/pr217858.cppm b/clang/test/Modules/pr217858.cppm new file mode 100644 index 0000000000000..040518bfec8ef --- /dev/null +++ b/clang/test/Modules/pr217858.cppm @@ -0,0 +1,56 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++23 -triple %itanium_abi_triple \ +// RUN: -emit-module-interface %t/lib.cppm -o %t/Lib.pcm +// RUN: %clang_cc1 -std=c++23 -triple %itanium_abi_triple \ +// RUN: -emit-module-interface %t/mod.cppm -o %t/Mod.pcm \ +// RUN: -fmodule-file=Lib=%t/Lib.pcm +// RUN: %clang_cc1 -std=c++23 -triple %itanium_abi_triple -emit-obj \ +// RUN: %t/main.cpp -o %t/main.o -fmodule-file=Lib=%t/Lib.pcm \ +// RUN: -fmodule-file=Mod=%t/Mod.pcm + +// Test again with reduced BMI. +// RUN: %clang_cc1 -std=c++23 -triple %itanium_abi_triple \ +// RUN: -emit-reduced-module-interface %t/lib.cppm -o %t/Lib.pcm +// RUN: %clang_cc1 -std=c++23 -triple %itanium_abi_triple \ +// RUN: -emit-reduced-module-interface %t/mod.cppm -o %t/Mod.pcm \ +// RUN: -fmodule-file=Lib=%t/Lib.pcm +// RUN: %clang_cc1 -std=c++23 -triple %itanium_abi_triple -emit-obj \ +// RUN: %t/main.cpp -o %t/main.o -fmodule-file=Lib=%t/Lib.pcm \ +// RUN: -fmodule-file=Mod=%t/Mod.pcm + +//--- lib.cppm +export module Lib; + +export template <class Callback> auto make_closure(Callback &callback) { + return [&callback](auto &arg) noexcept(noexcept(callback(arg))) { + callback(arg); + }; +} + +export template <class Callback, class Arg> +void for_each(Callback &&callback, Arg &arg) + noexcept(noexcept(make_closure(callback)(arg))) {} + +export template <class It> struct iterator { + It current; + void operator++() + noexcept(noexcept(for_each([](auto &it) { ++it; }, current))) {} +}; + +//--- mod.cppm +export module Mod; +import Lib; + +export inline int test() { + int data[4]{}; + iterator<int *> it{data}; + ++it; + return static_cast<int>(it.current - data); +} + +//--- main.cpp +import Mod; +int main() { return test(); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
