[clang] [Clang] Stop changing DC when instantiating dependent friend specializations (PR #139436)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Tue May 13 19:54:14 PDT 2025
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 <zyn7109 at 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 at -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 <zyn7109 at 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 at -1 {{friend function specialization cannot be defined}}
return os;
>From 6c0a909bb04396f24025f1b94b62a446b10d81ec Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at 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) &&
More information about the cfe-commits
mailing list