diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 53040aa0f9074..b5c6fbd32cc91 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -160,6 +160,9 @@ Bug Fixes in This Version - Fixed missing warnings when doing bool-like conversions in C23 (`#79435 `_). +- Clang now accepts qualified partial/explicit specializations of variable templates that + are not nominable in the lookup context of the specialization. + Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 780a2f2d8ce27..7885cf6728b54 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8456,7 +8456,7 @@ class Sema final { SourceLocation RAngleLoc); DeclResult ActOnVarTemplateSpecialization( - Scope *S, Declarator &D, TypeSourceInfo *DI, + Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous, SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams, StorageClass SC, bool IsPartialSpecialization); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index fd1c47008d685..de6067ba13cbe 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7727,7 +7727,7 @@ NamedDecl *Sema::ActOnVariableDeclarator( ? TemplateParamLists[0]->getTemplateLoc() : SourceLocation(); DeclResult Res = ActOnVarTemplateSpecialization( - S, D, TInfo, TemplateKWLoc, TemplateParams, SC, + S, D, TInfo, Previous, TemplateKWLoc, TemplateParams, SC, IsPartialSpecialization); if (Res.isInvalid()) return nullptr; @@ -8070,8 +8070,8 @@ NamedDecl *Sema::ActOnVariableDeclarator( D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous)); } else { // If this is an explicit specialization of a static data member, check it. - if (IsMemberSpecialization && !NewVD->isInvalidDecl() && - CheckMemberSpecialization(NewVD, Previous)) + if (IsMemberSpecialization && !IsVariableTemplateSpecialization && + !NewVD->isInvalidDecl() && CheckMemberSpecialization(NewVD, Previous)) NewVD->setInvalidDecl(); // Merge the decl with the existing one if appropriate. @@ -8086,7 +8086,8 @@ NamedDecl *Sema::ActOnVariableDeclarator( Previous.clear(); NewVD->setInvalidDecl(); } - } else if (D.getCXXScopeSpec().isSet()) { + } else if (D.getCXXScopeSpec().isSet() && + !IsVariableTemplateSpecialization) { // No previous declaration in the qualifying scope. Diag(D.getIdentifierLoc(), diag::err_no_member) << Name << computeDeclContext(D.getCXXScopeSpec(), true) @@ -8094,7 +8095,7 @@ NamedDecl *Sema::ActOnVariableDeclarator( NewVD->setInvalidDecl(); } - if (!IsVariableTemplateSpecialization && !IsPlaceholderVariable) + if (!IsPlaceholderVariable) D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous)); // CheckVariableDeclaration will set NewVD as invalid if something is in diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 5616682e909aa..ed4201bc6b1f7 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4601,9 +4601,9 @@ void Sema::CheckDeductionGuideTemplate(FunctionTemplateDecl *TD) { } DeclResult Sema::ActOnVarTemplateSpecialization( - Scope *S, Declarator &D, TypeSourceInfo *DI, SourceLocation TemplateKWLoc, - TemplateParameterList *TemplateParams, StorageClass SC, - bool IsPartialSpecialization) { + Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous, + SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams, + StorageClass SC, bool IsPartialSpecialization) { // D must be variable template id. assert(D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId && "Variable template specialization is declared with a template id."); @@ -4783,17 +4783,12 @@ DeclResult Sema::ActOnVarTemplateSpecialization( // Note that this is an explicit specialization. Specialization->setSpecializationKind(TSK_ExplicitSpecialization); - if (PrevDecl) { - // Check that this isn't a redefinition of this specialization, - // merging with previous declarations. - LookupResult PrevSpec(*this, GetNameForDeclarator(D), LookupOrdinaryName, - forRedeclarationInCurContext()); - PrevSpec.addDecl(PrevDecl); - D.setRedeclaration(CheckVariableDeclaration(Specialization, PrevSpec)); - } else if (Specialization->isStaticDataMember() && - Specialization->isOutOfLine()) { + Previous.clear(); + if (PrevDecl) + Previous.addDecl(PrevDecl); + else if (Specialization->isStaticDataMember() && + Specialization->isOutOfLine()) Specialization->setAccess(VarTemplate->getAccess()); - } return Specialization; } diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.meaning.general/p3.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.meaning.general/p3.cpp new file mode 100644 index 0000000000000..262f7cabf8213 --- /dev/null +++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.meaning.general/p3.cpp @@ -0,0 +1,112 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +namespace N0 { + template + void f0(); + + template + int x0 = 0; + + template + class C0; +} +using namespace N0; + +template<> +void f0(); // expected-error {{no function template matches}} + +template<> +int x0; + +template<> +class C0; + +namespace N1 { + namespace N2 { + template + void f2(); + + template + int x2 = 0; + + template + class C2; + } + using namespace N2; +} + +template<> +void N1::f2(); // expected-error {{no function template matches}} + +template<> +int N1::x2; + +template<> +class N1::C2; + +namespace N3 { + namespace N4 { + template + void f4(); + + template + int x4 = 0; + + template + class C4; + } + using N4::f4; + using N4::x4; + using N4::C4; +} + +template<> +void N3::f4(); // expected-error {{no function template matches}} + +template<> +int N3::x4; + +template<> +class N3::C4; + +inline namespace N5 { + template + void f5(); + + template + int x5 = 0; + + template + class C5; +} + +template<> +void f5(); + +template<> +int x5; + +template<> +class C5; + +namespace N6 { + inline namespace N7 { + template + void f7(); + + template + int x7 = 0; + + template + class C7; + } +} + +template<> +void N6::f7(); + +template<> +int N6::x7; + +template<> +class N6::C7;