From 5485c7021aca771f782c2fc1bc5e913dbed0ddc8 Mon Sep 17 00:00:00 2001 From: Matheus Izvekov Date: Tue, 19 Aug 2025 14:32:08 -0300 Subject: [PATCH] [clang] fix redecl chain assumption when checking linkage consistency (#153996) In C++, it can be assumed the same linkage will be computed for all redeclarations of an entity, and we have assertions to check this. However, the linkage for a declaration can be requested in the middle of deserealization, and at this point the redecl chain is not well formed, as computation of the most recent declaration is deferred. This patch makes that assertion work even in such conditions. This fixes a regression introduced in https://github.com/llvm/llvm-project/pull/147835, which was never released, so there are no release notes for this. Fixes #153933 --- clang/lib/AST/Decl.cpp | 17 ++++++++++------- clang/test/Modules/GH153933.cpp | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 clang/test/Modules/GH153933.cpp diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 12fe5516883e..4507f415ce60 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1604,17 +1604,20 @@ LinkageInfo LinkageComputer::getLVForDecl(const NamedDecl *D, // We have just computed the linkage for this decl. By induction we know // that all other computed linkages match, check that the one we just // computed also does. - NamedDecl *Old = nullptr; - for (auto *I : D->redecls()) { - auto *T = cast(I); - if (T == D) + // We can't assume the redecl chain is well formed at this point, + // so keep track of already visited declarations. + for (llvm::SmallPtrSet AlreadyVisited{D}; /**/; /**/) { + D = cast(const_cast(D)->getNextRedeclarationImpl()); + if (!AlreadyVisited.insert(D).second) + break; + if (D->isInvalidDecl()) continue; - if (!T->isInvalidDecl() && T->hasCachedLinkage()) { - Old = T; + if (auto OldLinkage = D->getCachedLinkage(); + OldLinkage != Linkage::Invalid) { + assert(LV.getLinkage() == OldLinkage); break; } } - assert(!Old || Old->getCachedLinkage() == D->getCachedLinkage()); #endif return LV; diff --git a/clang/test/Modules/GH153933.cpp b/clang/test/Modules/GH153933.cpp new file mode 100644 index 000000000000..41184c6b0060 --- /dev/null +++ b/clang/test/Modules/GH153933.cpp @@ -0,0 +1,23 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/B.cppm -emit-module-interface -o %t/B.pcm +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fprebuilt-module-path=%t %t/C.cpp + +//--- A.hpp +template struct A {}; +template struct B { + virtual A v() { return {}; } +}; +B x; + +//--- B.cppm +module; +#include "A.hpp" +export module B; +using ::x; + +//--- C.cpp +#include "A.hpp" +import B;