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

Krystian Stasiowski via cfe-commits cfe-commits at lists.llvm.org
Thu Aug 22 06:13:27 PDT 2024


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

>From 14db4ba124a36ea778515fe0228ae959081f6d65 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 14 Aug 2024 09:00:30 -0400
Subject: [PATCH 1/3] [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 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..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>'}}
+}

>From cd6caceeb4cca754e92fd415cc5083824f0dc1f6 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 14 Aug 2024 14:55:04 -0400
Subject: [PATCH 2/3] [FOLD] use same type for variables in tests

---
 clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

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 64b1274419e35d..4ec41521f9a3b1 100644
--- a/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
@@ -2,7 +2,7 @@
 
 template <class T> struct A {
   static T cond;
-  
+
   template <class U> struct B {
     static T twice(U value) {
       return (cond ? value + value : value);
@@ -53,7 +53,7 @@ namespace OutOfLine {
     static int x<U*, V>;
 
     template<typename U, B V>
-    static constexpr int x<U&, V> = 0; // expected-note {{previous definition is here}}
+    static inline int x<U&, V> = 0; // expected-note {{previous definition is here}}
 
     template<typename U, B V>
     struct C;
@@ -83,7 +83,7 @@ namespace OutOfLine {
 
   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>'}}
+  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>
@@ -120,12 +120,11 @@ namespace OutOfLine {
 
   template<>
   template<typename U, A<int>::B V>
-  constexpr int A<int>::x<U&, V> = 0;
+  int A<int>::x<U&, V> = 0; // expected-note {{previous definition is here}}
 
-  // FIXME: We should diagnose this redefinition!
   template<>
   template<typename U, A<int>::B V>
-  constexpr int A<int>::x<U&, V> = 0;
+  int A<int>::x<U&, V> = 0; // expected-error {{redefinition of 'x<U &, V>'}}
 
   template<>
   template<typename U, A<int>::B V>

>From caa8f4ef079cc07e846a7bb133bc97d26944818d Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 22 Aug 2024 09:13:12 -0400
Subject: [PATCH 3/3] [FOLD] add release note

---
 clang/docs/ReleaseNotes.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5c156a9c073a9c..05c8352c29d7b3 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -292,6 +292,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
 ^^^^^^^^^^^^^^^^^^^^^^^^^



More information about the cfe-commits mailing list