https://github.com/sdkrystian created 
https://github.com/llvm/llvm-project/pull/104030

We need to rebuild the template parameters of out-of-line 
definitions/specializations of member templates in the context of the current 
instantiation for the purposes of declaration matching. We already do this for 
function templates and class templates, but not variable templates, partial 
specializations of variable template, and partial specializations of class 
templates. This patch fixes the latter cases.

>From 0b99f8d40fa3e74021e496dfa5000fa51d972704 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkryst...@gmail.com>
Date: Wed, 14 Aug 2024 09:00:30 -0400
Subject: [PATCH] [Clang][Sema] Rebuild template parameters for out-of-line
 template definitions and partial specializations

---
 clang/lib/Sema/SemaDecl.cpp                   |   6 +
 clang/lib/Sema/SemaTemplate.cpp               |  20 ++--
 .../test/CXX/temp/temp.decls/temp.mem/p1.cpp  | 112 +++++++++++++++++-
 3 files changed, 129 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 641b180527da55..4490aed2645951 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7490,6 +7490,12 @@ NamedDecl *Sema::ActOnVariableDeclarator(
         /*never a friend*/ false, IsMemberSpecialization, Invalid);
 
     if (TemplateParams) {
+      if (DC->isDependentContext()) {
+        ContextRAII SavedContext(*this, DC);
+        if (RebuildTemplateParamsInCurrentInstantiation(TemplateParams))
+          Invalid = true;
+      }
+
       if (!TemplateParams->size() &&
           D.getName().getKind() != UnqualifiedIdKind::IK_TemplateId) {
         // There is an extraneous 'template<>' for this variable. Complain
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 29e7978ba5b1f8..78aea38cd35216 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -8086,13 +8086,14 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
     return true;
   }
 
+  DeclContext *DC = ClassTemplate->getDeclContext();
+
   bool isMemberSpecialization = false;
   bool isPartialSpecialization = false;
 
   if (SS.isSet()) {
     if (TUK != TagUseKind::Reference && TUK != TagUseKind::Friend &&
-        diagnoseQualifiedDeclaration(SS, ClassTemplate->getDeclContext(),
-                                     ClassTemplate->getDeclName(),
+        diagnoseQualifiedDeclaration(SS, DC, ClassTemplate->getDeclName(),
                                      TemplateNameLoc, &TemplateId,
                                      /*IsMemberSpecialization=*/false))
       return true;
@@ -8114,6 +8115,12 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
   if (TemplateParams && CheckTemplateDeclScope(S, TemplateParams))
     return true;
 
+  if (TemplateParams && DC->isDependentContext()) {
+    ContextRAII SavedContext(*this, DC);
+    if (RebuildTemplateParamsInCurrentInstantiation(TemplateParams))
+      return true;
+  }
+
   if (TemplateParams && TemplateParams->size() > 0) {
     isPartialSpecialization = true;
 
@@ -8279,9 +8286,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
       = cast_or_null<ClassTemplatePartialSpecializationDecl>(PrevDecl);
     ClassTemplatePartialSpecializationDecl *Partial =
         ClassTemplatePartialSpecializationDecl::Create(
-            Context, Kind, ClassTemplate->getDeclContext(), KWLoc,
-            TemplateNameLoc, TemplateParams, ClassTemplate, CanonicalConverted,
-            CanonType, PrevPartial);
+            Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams,
+            ClassTemplate, CanonicalConverted, CanonType, PrevPartial);
     Partial->setTemplateArgsAsWritten(TemplateArgs);
     SetNestedNameSpecifier(*this, Partial, SS);
     if (TemplateParameterLists.size() > 1 && SS.isSet()) {
@@ -8303,8 +8309,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
     // Create a new class template specialization declaration node for
     // this explicit specialization or friend declaration.
     Specialization = ClassTemplateSpecializationDecl::Create(
-        Context, Kind, ClassTemplate->getDeclContext(), KWLoc, TemplateNameLoc,
-        ClassTemplate, CanonicalConverted, PrevDecl);
+        Context, Kind, DC, KWLoc, TemplateNameLoc, ClassTemplate,
+        CanonicalConverted, PrevDecl);
     Specialization->setTemplateArgsAsWritten(TemplateArgs);
     SetNestedNameSpecifier(*this, Specialization, SS);
     if (TemplateParameterLists.size() > 0) {
diff --git a/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp 
b/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
index b48e145e1468db..64b1274419e35d 100644
--- a/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
@@ -1,5 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
-// expected-no-diagnostics
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
 
 template <class T> struct A {
   static T cond;
@@ -35,3 +34,112 @@ namespace PR6376 {
 
   Z<float, int> z0;
 }
+
+namespace OutOfLine {
+  template<typename T>
+  struct A {
+    struct B { };
+
+    template<typename U, B V>
+    void f();
+
+    template<typename U, B V>
+    void g() { } // expected-note {{previous definition is here}}
+
+    template<typename U, B V>
+    static int x;
+
+    template<typename U, B V>
+    static int x<U*, V>;
+
+    template<typename U, B V>
+    static constexpr int x<U&, V> = 0; // expected-note {{previous definition 
is here}}
+
+    template<typename U, B V>
+    struct C;
+
+    template<typename U, B V>
+    struct C<U*, V>;
+
+    template<typename U, B V>
+    struct C<U&, V> { }; // expected-note {{previous definition is here}}
+  };
+
+  template<typename T>
+  template<typename U, typename A<T>::B V>
+  void A<T>::f() { }
+
+  template<typename T>
+  template<typename U, typename A<T>::B V>
+  void A<T>::g() { } // expected-error {{redefinition of 'g'}}
+
+  template<typename T>
+  template<typename U, typename A<T>::B V>
+  int A<T>::x = 0;
+
+  template<typename T>
+  template<typename U, typename A<T>::B V>
+  int A<T>::x<U*, V> = 0;
+
+  template<typename T>
+  template<typename U, typename A<T>::B V>
+  constexpr int A<T>::x<U&, V> = 0; // expected-error {{redefinition of 'x<U 
&, V>'}}
+
+  template<typename T>
+  template<typename U, typename A<T>::B V>
+  struct A<T>::C { };
+
+  template<typename T>
+  template<typename U, typename A<T>::B V>
+  struct A<T>::C<U*, V> { };
+
+  template<typename T>
+  template<typename U, typename A<T>::B V>
+  struct A<T>::C<U&, V> { }; // expected-error {{redefinition of 'C<U &, V>'}}
+
+  // FIXME: Crashes when parsing the non-type template parameter prior to C++20
+  template<>
+  template<typename U, A<int>::B V>
+  void A<int>::f() { }
+
+  template<>
+  template<typename U, A<int>::B V>
+  void A<int>::g() { } // expected-note {{previous definition is here}}
+
+  template<>
+  template<typename U, A<int>::B V>
+  void A<int>::g() { } // expected-error {{redefinition of 'g'}}
+
+  template<>
+  template<typename U, A<int>::B V>
+  int A<int>::x = 0;
+
+  template<>
+  template<typename U, A<int>::B V>
+  int A<int>::x<U*, V> = 0;
+
+  template<>
+  template<typename U, A<int>::B V>
+  constexpr int A<int>::x<U&, V> = 0;
+
+  // FIXME: We should diagnose this redefinition!
+  template<>
+  template<typename U, A<int>::B V>
+  constexpr int A<int>::x<U&, V> = 0;
+
+  template<>
+  template<typename U, A<int>::B V>
+  struct A<int>::C { };
+
+  template<>
+  template<typename U, A<int>::B V>
+  struct A<int>::C<U*, V> { };
+
+  template<>
+  template<typename U, A<int>::B V>
+  struct A<int>::C<U&, V> { }; // expected-note {{previous definition is here}}
+
+  template<>
+  template<typename U, A<int>::B V>
+  struct A<int>::C<U&, V> { }; // expected-error {{redefinition of 'C<U &, 
V>'}}
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to