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/4] [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/4] [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/4] 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/4] 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
