[clang] c82f797 - [Clang][Sema] Rebuild template parameters for out-of-line template definitions and partial specializations (#104030)

via cfe-commits cfe-commits at lists.llvm.org
Thu Aug 22 06:22:37 PDT 2024


Author: Krystian Stasiowski
Date: 2024-08-22T09:22:33-04:00
New Revision: c82f7976ae20a7c76904415eae1964bab78f1a04

URL: https://github.com/llvm/llvm-project/commit/c82f7976ae20a7c76904415eae1964bab78f1a04
DIFF: https://github.com/llvm/llvm-project/commit/c82f7976ae20a7c76904415eae1964bab78f1a04.diff

LOG: [Clang][Sema] Rebuild template parameters for out-of-line template definitions and partial specializations (#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.

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaTemplate.cpp
    clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bb47350f76b308..12a924acc14331 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -294,6 +294,8 @@ Bug Fixes to C++ Support
 - Correctly check constraints of explicit instantiations of member functions. (#GH46029)
 - Fixed an assertion failure about a constraint of a friend function template references to a value with greater
   template depth than the friend function template. (#GH98258)
+- Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context
+  of the current instantiation in all cases.
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 503e93f9257137..b0ccbbe34b70c3 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7502,6 +7502,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 992565701d40ca..f8f41d0bafffc3 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -8089,13 +8089,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;
@@ -8117,6 +8118,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;
 
@@ -8282,9 +8289,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()) {
@@ -8306,8 +8312,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..4ec41521f9a3b1 100644
--- a/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
@@ -1,9 +1,8 @@
-// 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;
-  
+
   template <class U> struct B {
     static T twice(U value) {
       return (cond ? value + value : value);
@@ -35,3 +34,111 @@ 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 inline 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>
+  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>
+  int A<int>::x<U&, V> = 0; // expected-note {{previous definition is here}}
+
+  template<>
+  template<typename U, A<int>::B V>
+  int A<int>::x<U&, V> = 0; // expected-error {{redefinition of 'x<U &, V>'}}
+
+  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>'}}
+}


        


More information about the cfe-commits mailing list