https://github.com/Serosh-commits updated https://github.com/llvm/llvm-project/pull/186398
>From b88d19cdd09a75427d6599415860f0778d0972ef Mon Sep 17 00:00:00 2001 From: Serosh-commits <[email protected]> Date: Fri, 13 Mar 2026 19:21:29 +0530 Subject: [PATCH 1/8] [Clang] Fix crash in friend definition Fix assertion failure in instantiation. --- clang/docs/ReleaseNotes.rst | 1 + clang/lib/Sema/SemaDeclCXX.cpp | 3 +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 5 ++++- clang/test/SemaCXX/gh185341.cpp | 17 +++++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 clang/test/SemaCXX/gh185341.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 613d87668be18..330c46b2eaaf3 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -243,6 +243,7 @@ Bug Fixes to Attribute Support Bug Fixes to C++ Support ^^^^^^^^^^^^^^^^^^^^^^^^ +- Fixed a crash when a function template is defined as a non-template friend with a global scope qualifier. (#GH185341) - Fixed a crash when instantiating ``requires`` expressions involving substitution failures in C++ concepts. (#GH176402) - Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639) - Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 5837ecd6b9163..d966a3c1a25fd 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18452,10 +18452,12 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, DB << SS.getScopeRep(); if (DC->isFileContext()) DB << FixItHint::CreateRemoval(SS.getRange()); + ND->setInvalidDecl(); // Friend function defined in a local class. } else if (FunctionContainingLocalClass) { Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class); + ND->setInvalidDecl(); // Per [basic.pre]p4, a template-id is not a name. Therefore, if we have // a template-id, the function name is not unqualified because these is @@ -18465,6 +18467,7 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, // and diagnose them as such. } else if (isTemplateId) { Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def); + ND->setInvalidDecl(); } } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index e74c41517ecbf..9e1416bac212c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5563,6 +5563,8 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, // corresponding declaration of the function. assert(ExistingDefn->isThisDeclarationInstantiatedFromAFriendDefinition()); Function = const_cast<FunctionDecl*>(ExistingDefn); + if (Function->isInvalidDecl()) + return; } #ifndef NDEBUG @@ -5888,7 +5890,8 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, Primary && !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function) && Function->getTemplateSpecializationKind() != - TSK_ExplicitSpecialization) { + TSK_ExplicitSpecialization && + !Function->isThisDeclarationInstantiatedFromAFriendDefinition()) { auto It = llvm::find_if(Primary->redecls(), [](const RedeclarableTemplateDecl *RTD) { return cast<FunctionTemplateDecl>(RTD) diff --git a/clang/test/SemaCXX/gh185341.cpp b/clang/test/SemaCXX/gh185341.cpp new file mode 100644 index 0000000000000..fb3ae259ccd6e --- /dev/null +++ b/clang/test/SemaCXX/gh185341.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s + + +template<class> +struct D; + +template<class T> +void foo(D<T>); + +template<class T> +struct D { + friend void ::foo(D) {} // expected-error {{friend function definition cannot be qualified with '::'}} +}; + +int main() { + foo(D<int>{}); +} \ No newline at end of file >From 7da88732745d507b874cbd53916b4ebf2119398a Mon Sep 17 00:00:00 2001 From: Serosh-commits <[email protected]> Date: Sat, 14 Mar 2026 00:16:57 +0530 Subject: [PATCH 2/8] [Clang] Fix crash in friend definition with recovery This fix addresses GH185341 by correctly handling qualified friend function definitions through stripping the qualifier for error recovery and ensuring template instantiation logic correctly identifies functions instantiated from friends. --- clang/lib/Sema/SemaDeclCXX.cpp | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index d966a3c1a25fd..78f804f229e25 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18247,6 +18247,12 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool isTemplateId = D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId; + if (D.isFunctionDefinition() && SS.isNotEmpty()) { + Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def) + << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange()); + SS.clear(); + } + // There are five cases here. // - There's no scope specifier and we're in a local class. Only look // for functions declared in the immediately-enclosing block scope. @@ -18441,21 +18447,7 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, // only if the class is a non-local class, and the function name is // unqualified. if (D.isFunctionDefinition()) { - // Qualified friend function definition. - if (SS.isNotEmpty()) { - // FIXME: We should only do this if the scope specifier names the - // innermost enclosing namespace; otherwise the fixit changes the - // meaning of the code. - SemaDiagnosticBuilder DB = - Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def); - - DB << SS.getScopeRep(); - if (DC->isFileContext()) - DB << FixItHint::CreateRemoval(SS.getRange()); - ND->setInvalidDecl(); - - // Friend function defined in a local class. - } else if (FunctionContainingLocalClass) { + if (FunctionContainingLocalClass) { Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class); ND->setInvalidDecl(); >From 239d334655af53a78b14bcf4e6012e6845e77ba3 Mon Sep 17 00:00:00 2001 From: Serosh-commits <[email protected]> Date: Sat, 14 Mar 2026 18:20:18 +0530 Subject: [PATCH 3/8] address feedback --- clang/lib/Sema/SemaDeclCXX.cpp | 11 ++++++++--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 +-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 78f804f229e25..f679f390cc752 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18245,14 +18245,16 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, LookupResult Previous(*this, NameInfo, LookupOrdinaryName, RedeclarationKind::ForExternalRedeclaration); - bool isTemplateId = D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId; - - if (D.isFunctionDefinition() && SS.isNotEmpty()) { + bool WasQualifiedFriendDef = D.isFunctionDefinition() && SS.isNotEmpty(); + if (WasQualifiedFriendDef) { Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def) << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange()); SS.clear(); + Previous.clear(); } + bool isTemplateId = D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId; + // There are five cases here. // - There's no scope specifier and we're in a local class. Only look // for functions declared in the immediately-enclosing block scope. @@ -18406,6 +18408,9 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, TemplateParams, AddToScope); if (!ND) return nullptr; + if (WasQualifiedFriendDef) + ND->setInvalidDecl(); + assert(ND->getLexicalDeclContext() == CurContext); // If we performed typo correction, we might have added a scope specifier diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 9e1416bac212c..cc37a98cd27ff 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5890,8 +5890,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, Primary && !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function) && Function->getTemplateSpecializationKind() != - TSK_ExplicitSpecialization && - !Function->isThisDeclarationInstantiatedFromAFriendDefinition()) { + TSK_ExplicitSpecialization) { auto It = llvm::find_if(Primary->redecls(), [](const RedeclarableTemplateDecl *RTD) { return cast<FunctionTemplateDecl>(RTD) >From 6d325c1ad99177f8b05dfe4f6d9a945fec2c332b Mon Sep 17 00:00:00 2001 From: Serosh-commits <[email protected]> Date: Sat, 14 Mar 2026 19:08:35 +0530 Subject: [PATCH 4/8] address feedback --- clang/lib/Sema/SemaDeclCXX.cpp | 2 -- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index f679f390cc752..6e462f272863c 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18454,7 +18454,6 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, if (D.isFunctionDefinition()) { if (FunctionContainingLocalClass) { Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class); - ND->setInvalidDecl(); // Per [basic.pre]p4, a template-id is not a name. Therefore, if we have // a template-id, the function name is not unqualified because these is @@ -18464,7 +18463,6 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, // and diagnose them as such. } else if (isTemplateId) { Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def); - ND->setInvalidDecl(); } } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index cc37a98cd27ff..e74c41517ecbf 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5563,8 +5563,6 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, // corresponding declaration of the function. assert(ExistingDefn->isThisDeclarationInstantiatedFromAFriendDefinition()); Function = const_cast<FunctionDecl*>(ExistingDefn); - if (Function->isInvalidDecl()) - return; } #ifndef NDEBUG >From 37edd087fe61b044231d47af5fd7d9bd09a9eab6 Mon Sep 17 00:00:00 2001 From: Serosh-commits <[email protected]> Date: Mon, 16 Mar 2026 02:09:44 +0530 Subject: [PATCH 5/8] nit --- clang/lib/Sema/SemaDeclCXX.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 6e462f272863c..693898ca4c31f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18408,9 +18408,6 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, TemplateParams, AddToScope); if (!ND) return nullptr; - if (WasQualifiedFriendDef) - ND->setInvalidDecl(); - assert(ND->getLexicalDeclContext() == CurContext); // If we performed typo correction, we might have added a scope specifier >From 50d0fe55997d73d2e0aee8e03e6312664fc5eb14 Mon Sep 17 00:00:00 2001 From: Serosh-commits <[email protected]> Date: Mon, 16 Mar 2026 04:47:41 +0530 Subject: [PATCH 6/8] ci fixes --- clang/lib/Sema/SemaDeclCXX.cpp | 16 ++++++++++------ clang/test/SemaCXX/gh185341.cpp | 3 +-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 693898ca4c31f..ccbc603cd8579 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18245,12 +18245,16 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, LookupResult Previous(*this, NameInfo, LookupOrdinaryName, RedeclarationKind::ForExternalRedeclaration); - bool WasQualifiedFriendDef = D.isFunctionDefinition() && SS.isNotEmpty(); - if (WasQualifiedFriendDef) { - Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def) - << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange()); - SS.clear(); - Previous.clear(); + if (D.isFunctionDefinition() && SS.isNotEmpty()) { + auto Kind = SS.getScopeRep()->getKind(); + if (Kind == NestedNameSpecifier::Global || + Kind == NestedNameSpecifier::Namespace || + Kind == NestedNameSpecifier::NamespaceAlias) { + Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def) + << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange()); + SS.clear(); + Previous.clear(); + } } bool isTemplateId = D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId; diff --git a/clang/test/SemaCXX/gh185341.cpp b/clang/test/SemaCXX/gh185341.cpp index fb3ae259ccd6e..d5de1331486e3 100644 --- a/clang/test/SemaCXX/gh185341.cpp +++ b/clang/test/SemaCXX/gh185341.cpp @@ -1,6 +1,5 @@ // RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s - template<class> struct D; @@ -14,4 +13,4 @@ struct D { int main() { foo(D<int>{}); -} \ No newline at end of file +} >From e9c7a70d5598456d295586ebd23d65491e7b0b66 Mon Sep 17 00:00:00 2001 From: Serosh <[email protected]> Date: Mon, 16 Mar 2026 08:26:32 +0530 Subject: [PATCH 7/8] Update SemaDeclCXX.cpp --- clang/lib/Sema/SemaDeclCXX.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 64a3a98195ab6..752b575de129b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18306,17 +18306,17 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, LookupResult Previous(*this, NameInfo, LookupOrdinaryName, RedeclarationKind::ForExternalRedeclaration); - if (D.isFunctionDefinition() && SS.isNotEmpty()) { - auto Kind = SS.getScopeRep()->getKind(); - if (Kind == NestedNameSpecifier::Global || - Kind == NestedNameSpecifier::Namespace || - Kind == NestedNameSpecifier::NamespaceAlias) { - Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def) - << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange()); - SS.clear(); - Previous.clear(); + if (D.isFunctionDefinition() && SS.isNotEmpty()) { + auto Kind = SS.getScopeRep().getKind(); + if (Kind == NestedNameSpecifier::Kind::Global || + Kind == NestedNameSpecifier::Kind::Namespace || + Kind == NestedNameSpecifier::Kind::NamespaceAlias) { + Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def) + << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange()); + SS.clear(); + Previous.clear(); } - } +} bool isTemplateId = D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId; >From 894324a26fa383aec9a6e54c8f758f1978c4c38c Mon Sep 17 00:00:00 2001 From: Serosh <[email protected]> Date: Mon, 16 Mar 2026 09:45:15 +0530 Subject: [PATCH 8/8] nit --- clang/lib/Sema/SemaDeclCXX.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 752b575de129b..96796b8afd24f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18306,18 +18306,17 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, LookupResult Previous(*this, NameInfo, LookupOrdinaryName, RedeclarationKind::ForExternalRedeclaration); - if (D.isFunctionDefinition() && SS.isNotEmpty()) { - auto Kind = SS.getScopeRep().getKind(); - if (Kind == NestedNameSpecifier::Kind::Global || - Kind == NestedNameSpecifier::Kind::Namespace || - Kind == NestedNameSpecifier::Kind::NamespaceAlias) { - Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def) - << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange()); - SS.clear(); - Previous.clear(); - } +if (D.isFunctionDefinition() && SS.isNotEmpty()) { + auto Kind = SS.getScopeRep().getKind(); + if (Kind == NestedNameSpecifier::Kind::Global || + Kind == NestedNameSpecifier::Kind::Namespace) { + Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def) + << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange()); + SS.clear(); + Previous.clear(); + } } - + bool isTemplateId = D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId; // There are five cases here. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
