Skip to content

[Clang] Stop changing DC when instantiating dependent friend specializations #139436

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 14, 2025
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -586,6 +586,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)
- Fixed a crash with an invalid member function parameter list with a default
argument which contains a pragma. (#GH113722)
- Fixed assertion failures when generating name lookup table in modules. (#GH61065, #GH134739)
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
12 changes: 10 additions & 2 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9383,7 +9383,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 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
// primary function template - check if the 'specialization' that was
// implicitly generated (during template argument deduction for partial
Expand Down Expand Up @@ -11370,7 +11373,12 @@ class ExplicitSpecializationVisibilityChecker {
template<typename SpecDecl>
void checkImpl(SpecDecl *Spec) {
bool IsHiddenExplicitSpecialization = false;
if (Spec->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {
TemplateSpecializationKind SpecKind = Spec->getTemplateSpecializationKind();
// 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)
: !CheckExplicitSpecialization(Spec);
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5756,8 +5756,7 @@ 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 &&
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;
}

}