Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 9 additions & 2 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5751,9 +5751,9 @@ 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 &&
!isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function) &&
Expand Down
35 changes: 35 additions & 0 deletions clang/test/SemaTemplate/GH55509.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,38 @@ 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);

template <typename T>
FakeStream& operator>>(FakeStream& os, BinaryTree<T>& b) {
return os;
}

template <typename T>
struct BinaryTree {
T* root{};
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;
}

}