https://github.com/hahnjo updated 
https://github.com/llvm/llvm-project/pull/133057

>From e5f64e795f10a81a009b4562db0de692deb4c0f7 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <jonas.hahnf...@cern.ch>
Date: Tue, 25 Mar 2025 11:27:59 +0100
Subject: [PATCH 1/5] [Serialization] Hash inner template arguments

The code is applied from ODRHash::AddDecl with the reasoning given
in the comment, to reduce collisions. This was particularly visible
with STL types templated on std::pair where its template arguments
were not taken into account.
---
 .../lib/Serialization/TemplateArgumentHasher.cpp  | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/clang/lib/Serialization/TemplateArgumentHasher.cpp 
b/clang/lib/Serialization/TemplateArgumentHasher.cpp
index 3e8ffea78c2f1..d2aef73dec723 100644
--- a/clang/lib/Serialization/TemplateArgumentHasher.cpp
+++ b/clang/lib/Serialization/TemplateArgumentHasher.cpp
@@ -202,6 +202,21 @@ void TemplateArgumentHasher::AddDecl(const Decl *D) {
   }
 
   AddDeclarationName(ND->getDeclName());
+
+  // If this was a specialization we should take into account its template
+  // arguments. This helps to reduce collisions coming when visiting template
+  // specialization types (eg. when processing type template arguments).
+  ArrayRef<TemplateArgument> Args;
+  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D))
+    Args = CTSD->getTemplateArgs().asArray();
+  else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D))
+    Args = VTSD->getTemplateArgs().asArray();
+  else if (auto *FD = dyn_cast<FunctionDecl>(D))
+    if (FD->getTemplateSpecializationArgs())
+      Args = FD->getTemplateSpecializationArgs()->asArray();
+
+  for (auto &TA : Args)
+    AddTemplateArgument(TA);
 }
 
 void TemplateArgumentHasher::AddQualType(QualType T) {

>From e0dbf8e9c8ac320555181591ad4e30cb1ee5a926 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <jonas.hahnf...@cern.ch>
Date: Tue, 25 Mar 2025 11:31:23 +0100
Subject: [PATCH 2/5] [Serialization] Complete only needed partial
 specializations

It is unclear (to me) why this needs to be done "for safety", but
this change significantly improves the effectiveness of lazy loading.
---
 clang/lib/Serialization/ASTReader.cpp | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Serialization/ASTReader.cpp 
b/clang/lib/Serialization/ASTReader.cpp
index e8dddda584a9b..f7c4e1846393b 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -8089,14 +8089,8 @@ void ASTReader::CompleteRedeclChain(const Decl *D) {
     }
   }
 
-  if (Template) {
-    // For partitial specialization, load all the specializations for safety.
-    if (isa<ClassTemplatePartialSpecializationDecl,
-            VarTemplatePartialSpecializationDecl>(D))
-      Template->loadLazySpecializationsImpl();
-    else
-      Template->loadLazySpecializationsImpl(Args);
-  }
+  if (Template)
+    Template->loadLazySpecializationsImpl(Args);
 }
 
 CXXCtorInitializer **

>From 9325b153a46e272f2627a341097ff9527a8837b4 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <jonas.hahnf...@cern.ch>
Date: Tue, 25 Mar 2025 11:42:20 +0100
Subject: [PATCH 3/5] [Serialization] Load only needed partial specializations

Similar as the last commit, it is unclear why we need to load all
specializations, including non-partial ones, when we have a TPL.
---
 clang/lib/AST/DeclTemplate.cpp | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 8a6fb3391c13f..6b918c2d75abb 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -368,12 +368,6 @@ bool RedeclarableTemplateDecl::loadLazySpecializationsImpl(
   if (!ExternalSource)
     return false;
 
-  // If TPL is not null, it implies that we're loading specializations for
-  // partial templates. We need to load all specializations in such cases.
-  if (TPL)
-    return 
ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(),
-                                                       /*OnlyPartial=*/false);
-
   return ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(),
                                                      Args);
 }

>From 3d95698ac96a9db6e99b02741d998fcce2a98735 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <jonas.hahnf...@cern.ch>
Date: Tue, 25 Mar 2025 11:59:46 +0100
Subject: [PATCH 4/5] [Serialization] Remove bail-out logic in
 TemplateArgumentHasher

While it is correct to assign a single fixed hash to all template
arguments, it can reduce the effectiveness of lazy loading and is
not actually needed: we are allowed to ignore parts that cannot be
handled because they will be analogously ignored by all hashings.
---
 .../Serialization/TemplateArgumentHasher.cpp  | 36 ++-----------------
 1 file changed, 3 insertions(+), 33 deletions(-)

diff --git a/clang/lib/Serialization/TemplateArgumentHasher.cpp 
b/clang/lib/Serialization/TemplateArgumentHasher.cpp
index d2aef73dec723..70f963b63509c 100644
--- a/clang/lib/Serialization/TemplateArgumentHasher.cpp
+++ b/clang/lib/Serialization/TemplateArgumentHasher.cpp
@@ -22,17 +22,6 @@ using namespace clang;
 namespace {
 
 class TemplateArgumentHasher {
-  // If we bail out during the process of calculating hash values for
-  // template arguments for any reason. We're allowed to do it since
-  // TemplateArgumentHasher are only required to give the same hash value
-  // for the same template arguments, but not required to give different
-  // hash value for different template arguments.
-  //
-  // So in the worst case, it is still a valid implementation to give all
-  // inputs the same BailedOutValue as output.
-  bool BailedOut = false;
-  static constexpr unsigned BailedOutValue = 0x12345678;
-
   llvm::FoldingSetNodeID ID;
 
 public:
@@ -42,14 +31,7 @@ class TemplateArgumentHasher {
 
   void AddInteger(unsigned V) { ID.AddInteger(V); }
 
-  unsigned getValue() {
-    if (BailedOut)
-      return BailedOutValue;
-
-    return ID.computeStableHash();
-  }
-
-  void setBailedOut() { BailedOut = true; }
+  unsigned getValue() { return ID.computeStableHash(); }
 
   void AddType(const Type *T);
   void AddQualType(QualType T);
@@ -95,8 +77,7 @@ void 
TemplateArgumentHasher::AddTemplateArgument(TemplateArgument TA) {
   case TemplateArgument::Expression:
     // If we meet expression in template argument, it implies
     // that the template is still dependent. It is meaningless
-    // to get a stable hash for the template. Bail out simply.
-    BailedOut = true;
+    // to get a stable hash for the template.
     break;
   case TemplateArgument::Pack:
     AddInteger(TA.pack_size());
@@ -113,10 +94,9 @@ void TemplateArgumentHasher::AddStructuralValue(const 
APValue &Value) {
 
   // 'APValue::Profile' uses pointer values to make hash for LValue and
   // MemberPointer, but they differ from one compiler invocation to another.
-  // It may be difficult to handle such cases. Bail out simply.
+  // It may be difficult to handle such cases.
 
   if (Kind == APValue::LValue || Kind == APValue::MemberPointer) {
-    BailedOut = true;
     return;
   }
 
@@ -138,14 +118,11 @@ void TemplateArgumentHasher::AddTemplateName(TemplateName 
Name) {
   case TemplateName::DependentTemplate:
   case TemplateName::SubstTemplateTemplateParm:
   case TemplateName::SubstTemplateTemplateParmPack:
-    BailedOut = true;
     break;
   case TemplateName::UsingTemplate: {
     UsingShadowDecl *USD = Name.getAsUsingShadowDecl();
     if (USD)
       AddDecl(USD->getTargetDecl());
-    else
-      BailedOut = true;
     break;
   }
   case TemplateName::DeducedTemplate:
@@ -170,7 +147,6 @@ void 
TemplateArgumentHasher::AddDeclarationName(DeclarationName Name) {
   case DeclarationName::ObjCZeroArgSelector:
   case DeclarationName::ObjCOneArgSelector:
   case DeclarationName::ObjCMultiArgSelector:
-    BailedOut = true;
     break;
   case DeclarationName::CXXConstructorName:
   case DeclarationName::CXXDestructorName:
@@ -197,7 +173,6 @@ void 
TemplateArgumentHasher::AddDeclarationName(DeclarationName Name) {
 void TemplateArgumentHasher::AddDecl(const Decl *D) {
   const NamedDecl *ND = dyn_cast<NamedDecl>(D);
   if (!ND) {
-    BailedOut = true;
     return;
   }
 
@@ -221,7 +196,6 @@ void TemplateArgumentHasher::AddDecl(const Decl *D) {
 
 void TemplateArgumentHasher::AddQualType(QualType T) {
   if (T.isNull()) {
-    BailedOut = true;
     return;
   }
   SplitQualType split = T.split();
@@ -231,7 +205,6 @@ void TemplateArgumentHasher::AddQualType(QualType T) {
 
 // Process a Type pointer.  Add* methods call back into TemplateArgumentHasher
 // while Visit* methods process the relevant parts of the Type.
-// Any unhandled type will make the hash computation bail out.
 class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> {
   typedef TypeVisitor<TypeVisitorHelper> Inherited;
   llvm::FoldingSetNodeID &ID;
@@ -263,9 +236,6 @@ class TypeVisitorHelper : public 
TypeVisitor<TypeVisitorHelper> {
 
   void Visit(const Type *T) { Inherited::Visit(T); }
 
-  // Unhandled types. Bail out simply.
-  void VisitType(const Type *T) { Hash.setBailedOut(); }
-
   void VisitAdjustedType(const AdjustedType *T) {
     AddQualType(T->getOriginalType());
   }

>From 5b5e242027e18205240546f20738698118829500 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <jonas.hahnf...@cern.ch>
Date: Tue, 19 Aug 2025 14:25:13 +0200
Subject: [PATCH 5/5] [DO NOT MERGE] Add failing reproducer

---
 clang/test/Modules/pr133057.cpp | 143 ++++++++++++++++++++++++++++++++
 1 file changed, 143 insertions(+)
 create mode 100644 clang/test/Modules/pr133057.cpp

diff --git a/clang/test/Modules/pr133057.cpp b/clang/test/Modules/pr133057.cpp
new file mode 100644
index 0000000000000..b273fc318058e
--- /dev/null
+++ b/clang/test/Modules/pr133057.cpp
@@ -0,0 +1,143 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -xc++ -std=c++20 -emit-module -fmodule-name=hf 
-fno-cxx-modules -fmodules -fno-implicit-modules %t/CMO.cppmap -o %t/WI9.pcm
+// RUN: %clang_cc1 -xc++ -std=c++20 -emit-module -fmodule-name=g 
-fno-cxx-modules -fmodules -fno-implicit-modules -fmodule-file=%t/WI9.pcm 
%t/E6H.cppmap -o %t/4BK.pcm
+// RUN: %clang_cc1 -xc++ -std=c++20 -emit-module -fmodule-name=r 
-fno-cxx-modules -fmodules -fno-implicit-modules -fmodule-file=%t/WI9.pcm 
%t/HMT.cppmap -o %t/LUM.pcm
+// RUN: %clang_cc1 -xc++ -std=c++20 -emit-module -fmodule-name=q 
-fno-cxx-modules -fmodules -fno-implicit-modules -fmodule-file=%t/LUM.pcm 
-fmodule-file=%t/4BK.pcm %t/JOV.cppmap -o %t/9VX.pcm
+// RUN: %clang_cc1 -xc++ -std=c++20 -verify -fsyntax-only -fno-cxx-modules 
-fmodules -fno-implicit-modules -fmodule-file=%t/9VX.pcm %t/XFD.cc
+
+//--- 2OT.h
+#include "LQ1.h"
+
+namespace ciy {
+namespace xqk {
+template <typename>
+class vum {
+ public:
+  using sc = std::C::wmd;
+  friend bool operator==(vum, vum);
+};
+template <typename>
+class me {
+ public:
+  using vbh = vum<me>;
+  using sc = std::C::vy<vbh>::sc;
+  template <typename db>
+  operator db() { return {}; }
+};
+}  // namespace xqk
+template <typename vus>
+xqk::me<vus> uvo(std::C::wmd, vus);
+}  // namespace ciy
+
+class ua {
+  std::C::wmd kij() {
+    ciy::uvo(kij(), '-');
+    return {};
+  }
+};
+
+//--- 9KF.h
+#include "LQ1.h"
+#include "2OT.h"
+namespace {
+void al(std::C::wmd lou) { std::C::jv<std::C::wmd> yt = ciy::uvo(lou, '/'); }
+}  // namespace
+
+//--- CMO.cppmap
+module "hf" {
+header "LQ1.h"
+}
+
+
+//--- E6H.cppmap
+module "g" {
+export *
+header "2OT.h"
+}
+
+
+//--- HMT.cppmap
+module "r" {
+header "2OT.h"
+}
+
+
+//--- JOV.cppmap
+module "q" {
+header "9KF.h"
+}
+
+
+//--- LQ1.h
+namespace std {
+namespace C {
+template <class zd>
+struct vy : zd {};
+template <class ub>
+struct vy<ub*> {
+  typedef ub jz;
+};
+struct wmd {};
+template <class uo, class zt>
+void sk(uo k, zt gf) {
+  (void)(k != gf);
+}
+template <class uo>
+class fm {
+ public:
+  fm(uo);
+};
+template <class kj, class kju>
+bool operator==(kj, kju);
+template <class epn>
+void afm(epn) {
+  using yp = vy<epn>;
+  if (__is_trivially_copyable(yp)) {
+    sk(fm(epn()), nullptr);
+  }
+}
+template <class ub>
+class jv {
+ public:
+  constexpr void gq();
+  ub *nef;
+};
+template <class ub>
+constexpr void jv<ub>::gq() {
+    afm(nef);
+}
+}  // namespace C
+}  // namespace std
+namespace ciy {
+}  // namespace ciy
+
+//--- XFD.cc
+// expected-no-diagnostics
+#include "LQ1.h"
+#include "2OT.h"
+class wiy {
+ public:
+  std::C::wmd eyb();
+};
+template <typename wpa>
+void i(wpa fg) {
+  std::C::jv<std::C::wmd> zs;
+  zs = ciy::uvo(fg.eyb(), '\n');
+}
+namespace ciy {
+namespace xqk {
+struct sbv;
+std::C::jv<sbv> ns() {
+  std::C::jv<sbv> ubs;
+  ubs.gq();
+  return ubs;
+}
+}  // namespace xqk
+}  // namespace ciy
+void s() {
+  wiy fg;
+  i(fg);
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to