[clang] [Clang][Sema] Correctly look up primary template for variable template specializations (PR #80359)
Krystian Stasiowski via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 1 14:49:58 PST 2024
https://github.com/sdkrystian created https://github.com/llvm/llvm-project/pull/80359
Consider the following:
```cpp
namespace N0 {
namespace N1 {
template<typename T>
int x1 = 0;
}
using namespace N1;
}
template<>
int N0::x1<int>;
```
According to [[dcl.meaning.general] p3.3](http://eel.is/c++draft/dcl.meaning.general#3.3):
> - If the _declarator_ declares an explicit instantiation or a partial or explicit specialization, the _declarator_ does not bind a name. If it declares a class member, the terminal name of the _declarator-id_ is not looked up; otherwise, **only those lookup results that are nominable in `S` are considered when identifying any function template specialization being declared**.
In particular, the requirement for lookup results to be nominal in the lookup context of the terminal name of the _declarator-id_ only applies to function template specializations -- not variable template specializations. We currently reject the above declaration. This patch makes it so the above specialization is (correctly) accepted.
>From d7eae1178766537b55a7f96839d50807a81806e5 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 1 Feb 2024 17:34:59 -0500
Subject: [PATCH] [Clang][Sema] Correctly look up primary template for variable
template specializations
---
clang/include/clang/Sema/Sema.h | 2 +-
clang/lib/Sema/SemaDecl.cpp | 11 +-
clang/lib/Sema/SemaTemplate.cpp | 21 ++--
.../dcl.meaning/dcl.meaning.general/p3.cpp | 112 ++++++++++++++++++
4 files changed, 127 insertions(+), 19 deletions(-)
create mode 100644 clang/test/CXX/dcl.decl/dcl.meaning/dcl.meaning.general/p3.cpp
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<typename T>
+ void f0();
+
+ template<typename T>
+ int x0 = 0;
+
+ template<typename T>
+ class C0;
+}
+using namespace N0;
+
+template<>
+void f0<int>(); // expected-error {{no function template matches}}
+
+template<>
+int x0<int>;
+
+template<>
+class C0<int>;
+
+namespace N1 {
+ namespace N2 {
+ template<typename T>
+ void f2();
+
+ template<typename T>
+ int x2 = 0;
+
+ template<typename T>
+ class C2;
+ }
+ using namespace N2;
+}
+
+template<>
+void N1::f2<int>(); // expected-error {{no function template matches}}
+
+template<>
+int N1::x2<int>;
+
+template<>
+class N1::C2<int>;
+
+namespace N3 {
+ namespace N4 {
+ template<typename T>
+ void f4();
+
+ template<typename T>
+ int x4 = 0;
+
+ template<typename T>
+ class C4;
+ }
+ using N4::f4;
+ using N4::x4;
+ using N4::C4;
+}
+
+template<>
+void N3::f4<int>(); // expected-error {{no function template matches}}
+
+template<>
+int N3::x4<int>;
+
+template<>
+class N3::C4<int>;
+
+inline namespace N5 {
+ template<typename T>
+ void f5();
+
+ template<typename T>
+ int x5 = 0;
+
+ template<typename T>
+ class C5;
+}
+
+template<>
+void f5<int>();
+
+template<>
+int x5<int>;
+
+template<>
+class C5<int>;
+
+namespace N6 {
+ inline namespace N7 {
+ template<typename T>
+ void f7();
+
+ template<typename T>
+ int x7 = 0;
+
+ template<typename T>
+ class C7;
+ }
+}
+
+template<>
+void N6::f7<int>();
+
+template<>
+int N6::x7<int>;
+
+template<>
+class N6::C7<int>;
More information about the cfe-commits
mailing list