https://github.com/zyn0217 updated 
https://github.com/llvm/llvm-project/pull/139436

>From 1756fbcd874097fdea256c2c5986810a011eafed Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Sun, 11 May 2025 12:54:12 +0800
Subject: [PATCH 1/3] [Clang] Stop looking for DC from dependent friend
 specializations

Since 346077aa, we began using the primary template's lexical
DeclContext for template arguments in order to properly instantiate
a friend definition.

There is a missed peculiar case, as in a friend template is specialized
within a dependent context. In this scenario, the primary template is
not a definition, whereas the specialization is. So the primary
template's DeclContext doesn't provide any meaningful for instantiation.
---
 clang/docs/ReleaseNotes.rst                   |  2 +
 clang/lib/Sema/SemaDeclCXX.cpp                |  2 +-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 12 +++---
 clang/test/SemaTemplate/GH55509.cpp           | 38 +++++++++++++++++++
 4 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 1d0896f585fb4..c75ad0be8a8e6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -576,6 +576,8 @@ Bug Fixes in This Version
   ``#include`` directive. (#GH138094)
 - Fixed a crash during constant evaluation involving invalid lambda captures
   (#GH138832)
+- Fixed a crash when instantiating an invalid dependent friend template 
specialization.
+  (#GH139052)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index cbccb567e2adf..d915448d0feb1 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18740,7 +18740,7 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, 
Declarator &D,
         // a template-id, the function name is not unqualified because these is
         // no name. While the wording requires some reading in-between the
         // lines, GCC, MSVC, and EDG all consider a friend function
-        // specialization definitions // to be de facto explicit specialization
+        // specialization definitions to be de facto explicit specialization
         // and diagnose them as such.
       } else if (isTemplateId) {
         Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 08b3a423d1526..a8c3b28f8d185 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5751,14 +5751,16 @@ void Sema::InstantiateFunctionDefinition(SourceLocation 
PointOfInstantiation,
     RebuildTypeSourceInfoForDefaultSpecialMembers();
     SetDeclDefaulted(Function, PatternDecl->getLocation());
   } else {
-    NamedDecl *ND = Function;
-    DeclContext *DC = ND->getLexicalDeclContext();
+    DeclContext *DC = Function->getLexicalDeclContext();
     std::optional<ArrayRef<TemplateArgument>> Innermost;
-    if (auto *Primary = Function->getPrimaryTemplate();
-        Primary &&
+    bool NeedDCFromPrimaryTemplate =
         !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function) &&
         Function->getTemplateSpecializationKind() !=
-            TSK_ExplicitSpecialization) {
+            TSK_ExplicitSpecialization &&
+        !PatternDecl->getDependentSpecializationInfo();
+
+    if (auto *Primary = Function->getPrimaryTemplate();
+        Primary && NeedDCFromPrimaryTemplate) {
       auto It = llvm::find_if(Primary->redecls(),
                               [](const RedeclarableTemplateDecl *RTD) {
                                 return cast<FunctionTemplateDecl>(RTD)
diff --git a/clang/test/SemaTemplate/GH55509.cpp 
b/clang/test/SemaTemplate/GH55509.cpp
index 773a84305a0cd..f421e5a8fc237 100644
--- a/clang/test/SemaTemplate/GH55509.cpp
+++ b/clang/test/SemaTemplate/GH55509.cpp
@@ -110,3 +110,41 @@ namespace regression2 {
   }
   template void A<void>::f<long>();
 } // namespace regression2
+
+namespace GH139226 {
+
+struct FakeStream {};
+
+template <typename T>
+class BinaryTree;
+
+template <typename T>
+FakeStream& operator<<(FakeStream& os, BinaryTree<T>& b); // #1
+
+template <typename T>
+FakeStream& operator>>(FakeStream& os, BinaryTree<T>& b) {
+  return os;
+}
+
+template <typename T>
+struct BinaryTree {
+  T* root{};
+  // This template is described using a DependentSpecializationInfo, and its 
instantiations
+  // are tracked with TSK_ImplicitInstantiation kind.
+  // The primary template is declared at #1.
+  friend FakeStream& operator<< <T>(FakeStream& os, BinaryTree&) {
+    // expected-error@-1 {{friend function specialization cannot be defined}}
+    return os;
+  }
+
+  friend FakeStream& operator>> <T>(FakeStream& os, BinaryTree&);
+};
+
+void foo() {
+  FakeStream fakeout;
+  BinaryTree<int> a{};
+  fakeout << a;
+  fakeout >> a;
+}
+
+}

>From 62b507fb93d28ffd4a8720568ed9cd264563874b Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Tue, 13 May 2025 14:41:32 +0800
Subject: [PATCH 2/3] Convert those to TSK_ExplicitSpecializations

---
 clang/lib/Sema/SemaTemplate.cpp                | 11 +++++++++--
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 10 ++++------
 clang/test/SemaTemplate/GH55509.cpp            |  5 +----
 3 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 2ba69c87d95e3..9193ac5c620a6 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -9380,7 +9380,10 @@ bool Sema::CheckFunctionTemplateSpecialization(
 
   // Mark the prior declaration as an explicit specialization, so that later
   // clients know that this is an explicit specialization.
-  if (!isFriend) {
+  // A dependent friend specializations which have definitions should be 
treated
+  // as explicit specializations, despite being invalid.
+  if (FunctionDecl *InstFrom = FD->getInstantiatedFromMemberFunction();
+      !isFriend || (InstFrom && InstFrom->getDependentSpecializationInfo())) {
     // Since explicit specializations do not inherit '=delete' from their
     // primary function template - check if the 'specialization' that was
     // implicitly generated (during template argument deduction for partial
@@ -11367,7 +11370,11 @@ class ExplicitSpecializationVisibilityChecker {
   template<typename SpecDecl>
   void checkImpl(SpecDecl *Spec) {
     bool IsHiddenExplicitSpecialization = false;
-    if (Spec->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {
+    TemplateSpecializationKind SpecKind = 
Spec->getTemplateSpecializationKind();
+    if constexpr (std::is_same_v<SpecDecl, FunctionDecl>) {
+      SpecKind = Spec->getTemplateSpecializationKindForInstantiation();
+    }
+    if (SpecKind == TSK_ExplicitSpecialization) {
       IsHiddenExplicitSpecialization = Spec->getMemberSpecializationInfo()
                                            ? !CheckMemberSpecialization(Spec)
                                            : 
!CheckExplicitSpecialization(Spec);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index a8c3b28f8d185..dba7bfeb80895 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5753,14 +5753,12 @@ void Sema::InstantiateFunctionDefinition(SourceLocation 
PointOfInstantiation,
   } else {
     DeclContext *DC = Function->getLexicalDeclContext();
     std::optional<ArrayRef<TemplateArgument>> Innermost;
-    bool NeedDCFromPrimaryTemplate =
-        !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function) &&
-        Function->getTemplateSpecializationKind() !=
-            TSK_ExplicitSpecialization &&
-        !PatternDecl->getDependentSpecializationInfo();
 
     if (auto *Primary = Function->getPrimaryTemplate();
-        Primary && NeedDCFromPrimaryTemplate) {
+        Primary &&
+        !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function) &&
+        Function->getTemplateSpecializationKind() !=
+            TSK_ExplicitSpecialization) {
       auto It = llvm::find_if(Primary->redecls(),
                               [](const RedeclarableTemplateDecl *RTD) {
                                 return cast<FunctionTemplateDecl>(RTD)
diff --git a/clang/test/SemaTemplate/GH55509.cpp 
b/clang/test/SemaTemplate/GH55509.cpp
index f421e5a8fc237..b1ba8e513356d 100644
--- a/clang/test/SemaTemplate/GH55509.cpp
+++ b/clang/test/SemaTemplate/GH55509.cpp
@@ -119,7 +119,7 @@ template <typename T>
 class BinaryTree;
 
 template <typename T>
-FakeStream& operator<<(FakeStream& os, BinaryTree<T>& b); // #1
+FakeStream& operator<<(FakeStream& os, BinaryTree<T>& b);
 
 template <typename T>
 FakeStream& operator>>(FakeStream& os, BinaryTree<T>& b) {
@@ -129,9 +129,6 @@ FakeStream& operator>>(FakeStream& os, BinaryTree<T>& b) {
 template <typename T>
 struct BinaryTree {
   T* root{};
-  // This template is described using a DependentSpecializationInfo, and its 
instantiations
-  // are tracked with TSK_ImplicitInstantiation kind.
-  // The primary template is declared at #1.
   friend FakeStream& operator<< <T>(FakeStream& os, BinaryTree&) {
     // expected-error@-1 {{friend function specialization cannot be defined}}
     return os;

>From 6c0a909bb04396f24025f1b94b62a446b10d81ec Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7...@gmail.com>
Date: Wed, 14 May 2025 10:53:08 +0800
Subject: [PATCH 3/3] Fix some comments; remove whitespace changes

---
 clang/lib/Sema/SemaTemplate.cpp                | 9 +++++----
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 1 -
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index f44f35c7053d1..486414ea84861 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -9383,8 +9383,8 @@ bool Sema::CheckFunctionTemplateSpecialization(
 
   // Mark the prior declaration as an explicit specialization, so that later
   // clients know that this is an explicit specialization.
-  // A dependent friend specializations which have definitions should be 
treated
-  // as explicit specializations, despite being invalid.
+  // A dependent friend specialization which has a definition should be treated
+  // as explicit specialization, despite being invalid.
   if (FunctionDecl *InstFrom = FD->getInstantiatedFromMemberFunction();
       !isFriend || (InstFrom && InstFrom->getDependentSpecializationInfo())) {
     // Since explicit specializations do not inherit '=delete' from their
@@ -11374,9 +11374,10 @@ class ExplicitSpecializationVisibilityChecker {
   void checkImpl(SpecDecl *Spec) {
     bool IsHiddenExplicitSpecialization = false;
     TemplateSpecializationKind SpecKind = 
Spec->getTemplateSpecializationKind();
-    if constexpr (std::is_same_v<SpecDecl, FunctionDecl>) {
+    // Some invalid friend declarations are written as specializations but are
+    // instantiated implicitly.
+    if constexpr (std::is_same_v<SpecDecl, FunctionDecl>)
       SpecKind = Spec->getTemplateSpecializationKindForInstantiation();
-    }
     if (SpecKind == TSK_ExplicitSpecialization) {
       IsHiddenExplicitSpecialization = Spec->getMemberSpecializationInfo()
                                            ? !CheckMemberSpecialization(Spec)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 0de9e29b37b69..d4f99c1fa16f6 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5758,7 +5758,6 @@ void Sema::InstantiateFunctionDefinition(SourceLocation 
PointOfInstantiation,
   } else {
     DeclContext *DC = Function->getLexicalDeclContext();
     std::optional<ArrayRef<TemplateArgument>> Innermost;
-
     if (auto *Primary = Function->getPrimaryTemplate();
         Primary &&
         !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function) &&

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

Reply via email to