https://github.com/ChuanqiXu9 created 
https://github.com/llvm/llvm-project/pull/219926

Close https://github.com/llvm/llvm-project/issues/219639

The root cause of the problem is the incorrect redecl chain.

A valid redecl chain should be a circle where each decl refers to the previous 
one and the first decl refers to the most recent decl (latest one). However, in 
the example, the redecl chain became to:

D2 -> D1 -> D1 ....

so that the range of `for (... : D->redecls())` never ends.

The real cause of the issue is we didn't merge correctly in the ASTReader. 
Previous code assumes about the most recent decl while the new code makes the 
behavior more clearly.

>From 039b1d2534310893bda9b2bc78ac0607699e5ea7 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <[email protected]>
Date: Mon, 31 Aug 2026 17:29:35 +0800
Subject: [PATCH] [C++20] [Modules] Correct the redecl chain

Close https://github.com/llvm/llvm-project/issues/219639

The root cause of the problem is the incorrect redecl chain.

A valid redecl chain should be a circle where each decl refers
to the previous one and the first decl refers to the most recent decl
(latest one). However, in the example, the redecl chain became to:

D2 -> D1 -> D1 ....

so that the range of `for (... : D->redecls())` never ends.

The real cause of the issue is we didn't merge correctly in the
ASTReader. Previous code assumes about the most recent decl while
the new code makes the behavior more clearly.
---
 clang/lib/Serialization/ASTReaderDecl.cpp | 15 ++++----
 clang/test/Modules/pr219639.cppm          | 45 +++++++++++++++++++++++
 2 files changed, 53 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/Modules/pr219639.cppm

diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp 
b/clang/lib/Serialization/ASTReaderDecl.cpp
index d27aa1dd64268..d4c47a81ed32f 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -4616,17 +4616,19 @@ void 
ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
 }
 
 void ASTReader::loadPendingDeclChain(Decl *FirstLocal, uint64_t LocalOffset) {
-  // Attach FirstLocal to the end of the decl chain.
   Decl *CanonDecl = FirstLocal->getCanonicalDecl();
+
+  Decl *MostRecent = ASTDeclReader::getMostRecentDecl(CanonDecl);
+  if (!MostRecent)
+    MostRecent = CanonDecl;
   if (FirstLocal != CanonDecl) {
-    Decl *PrevMostRecent = ASTDeclReader::getMostRecentDecl(CanonDecl);
-    ASTDeclReader::attachPreviousDecl(
-        *this, FirstLocal, PrevMostRecent ? PrevMostRecent : CanonDecl,
-        CanonDecl);
+    // Attach FirstLocal to the end of the decl chain.
+    ASTDeclReader::attachPreviousDecl(*this, FirstLocal, MostRecent, 
CanonDecl);
+    MostRecent = FirstLocal;
   }
 
   if (!LocalOffset) {
-    ASTDeclReader::attachLatestDecl(CanonDecl, FirstLocal);
+    ASTDeclReader::attachLatestDecl(CanonDecl, MostRecent);
     return;
   }
 
@@ -4658,7 +4660,6 @@ void ASTReader::loadPendingDeclChain(Decl *FirstLocal, 
uint64_t LocalOffset) {
 
   // FIXME: We have several different dispatches on decl kind here; maybe
   // we should instead generate one loop per kind and dispatch up-front?
-  Decl *MostRecent = FirstLocal;
   for (unsigned I = 0, N = Record.size(); I != N; ++I) {
     unsigned Idx = N - I - 1;
     auto *D = ReadDecl(*M, Record, Idx);
diff --git a/clang/test/Modules/pr219639.cppm b/clang/test/Modules/pr219639.cppm
new file mode 100644
index 0000000000000..bdc2f6441e92a
--- /dev/null
+++ b/clang/test/Modules/pr219639.cppm
@@ -0,0 +1,45 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/M.cppm -o %t/M.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fmodule-file=M=%t/M.pcm 
%t/B.cppm -o %t/B.pcm
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fmodule-file=M=%t/M.pcm 
-fmodule-file=B=%t/B.pcm %t/A.cppm
+//
+// Test again with reduced BMI
+// RUN: %clang_cc1 -std=c++20 -emit-reduced-module-interface %t/M.cppm -o 
%t/M.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-reduced-module-interface 
-fmodule-file=M=%t/M.pcm %t/B.cppm -o %t/B.pcm
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fmodule-file=M=%t/M.pcm 
-fmodule-file=B=%t/B.pcm %t/A.cppm
+
+//--- decls.h
+void f(int);
+
+namespace N {
+inline namespace I {
+void f();
+using ::f;
+} // namespace I
+} // namespace N
+
+//--- M.cppm
+module;
+#include "decls.h"
+
+export module M;
+
+export namespace N { using N::f; }
+export namespace N { using N::f; }
+
+//--- B.cppm
+export module B;
+
+import M;
+
+//--- A.cppm
+module;
+#include "decls.h"
+
+export module A;
+
+import B;
+
+export void test() { N::f(); }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to