[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