[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