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