Skip to content

Commit c82f797

Browse files
authored
[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.
1 parent d163935 commit c82f797

File tree

4 files changed

+131
-10
lines changed

4 files changed

+131
-10
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ Bug Fixes to C++ Support
294294
- Correctly check constraints of explicit instantiations of member functions. (#GH46029)
295295
- Fixed an assertion failure about a constraint of a friend function template references to a value with greater
296296
template depth than the friend function template. (#GH98258)
297+
- Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context
298+
of the current instantiation in all cases.
297299

298300
Bug Fixes to AST Handling
299301
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaDecl.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7502,6 +7502,12 @@ NamedDecl *Sema::ActOnVariableDeclarator(
75027502
/*never a friend*/ false, IsMemberSpecialization, Invalid);
75037503

75047504
if (TemplateParams) {
7505+
if (DC->isDependentContext()) {
7506+
ContextRAII SavedContext(*this, DC);
7507+
if (RebuildTemplateParamsInCurrentInstantiation(TemplateParams))
7508+
Invalid = true;
7509+
}
7510+
75057511
if (!TemplateParams->size() &&
75067512
D.getName().getKind() != UnqualifiedIdKind::IK_TemplateId) {
75077513
// There is an extraneous 'template<>' for this variable. Complain

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8089,13 +8089,14 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
80898089
return true;
80908090
}
80918091

8092+
DeclContext *DC = ClassTemplate->getDeclContext();
8093+
80928094
bool isMemberSpecialization = false;
80938095
bool isPartialSpecialization = false;
80948096

80958097
if (SS.isSet()) {
80968098
if (TUK != TagUseKind::Reference && TUK != TagUseKind::Friend &&
8097-
diagnoseQualifiedDeclaration(SS, ClassTemplate->getDeclContext(),
8098-
ClassTemplate->getDeclName(),
8099+
diagnoseQualifiedDeclaration(SS, DC, ClassTemplate->getDeclName(),
80998100
TemplateNameLoc, &TemplateId,
81008101
/*IsMemberSpecialization=*/false))
81018102
return true;
@@ -8117,6 +8118,12 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
81178118
if (TemplateParams && CheckTemplateDeclScope(S, TemplateParams))
81188119
return true;
81198120

8121+
if (TemplateParams && DC->isDependentContext()) {
8122+
ContextRAII SavedContext(*this, DC);
8123+
if (RebuildTemplateParamsInCurrentInstantiation(TemplateParams))
8124+
return true;
8125+
}
8126+
81208127
if (TemplateParams && TemplateParams->size() > 0) {
81218128
isPartialSpecialization = true;
81228129

@@ -8282,9 +8289,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
82828289
= cast_or_null<ClassTemplatePartialSpecializationDecl>(PrevDecl);
82838290
ClassTemplatePartialSpecializationDecl *Partial =
82848291
ClassTemplatePartialSpecializationDecl::Create(
8285-
Context, Kind, ClassTemplate->getDeclContext(), KWLoc,
8286-
TemplateNameLoc, TemplateParams, ClassTemplate, CanonicalConverted,
8287-
CanonType, PrevPartial);
8292+
Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams,
8293+
ClassTemplate, CanonicalConverted, CanonType, PrevPartial);
82888294
Partial->setTemplateArgsAsWritten(TemplateArgs);
82898295
SetNestedNameSpecifier(*this, Partial, SS);
82908296
if (TemplateParameterLists.size() > 1 && SS.isSet()) {
@@ -8306,8 +8312,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
83068312
// Create a new class template specialization declaration node for
83078313
// this explicit specialization or friend declaration.
83088314
Specialization = ClassTemplateSpecializationDecl::Create(
8309-
Context, Kind, ClassTemplate->getDeclContext(), KWLoc, TemplateNameLoc,
8310-
ClassTemplate, CanonicalConverted, PrevDecl);
8315+
Context, Kind, DC, KWLoc, TemplateNameLoc, ClassTemplate,
8316+
CanonicalConverted, PrevDecl);
83118317
Specialization->setTemplateArgsAsWritten(TemplateArgs);
83128318
SetNestedNameSpecifier(*this, Specialization, SS);
83138319
if (TemplateParameterLists.size() > 0) {

clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
// RUN: %clang_cc1 -fsyntax-only -verify %s
2-
// expected-no-diagnostics
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
32

43
template <class T> struct A {
54
static T cond;
6-
5+
76
template <class U> struct B {
87
static T twice(U value) {
98
return (cond ? value + value : value);
@@ -35,3 +34,111 @@ namespace PR6376 {
3534

3635
Z<float, int> z0;
3736
}
37+
38+
namespace OutOfLine {
39+
template<typename T>
40+
struct A {
41+
struct B { };
42+
43+
template<typename U, B V>
44+
void f();
45+
46+
template<typename U, B V>
47+
void g() { } // expected-note {{previous definition is here}}
48+
49+
template<typename U, B V>
50+
static int x;
51+
52+
template<typename U, B V>
53+
static int x<U*, V>;
54+
55+
template<typename U, B V>
56+
static inline int x<U&, V> = 0; // expected-note {{previous definition is here}}
57+
58+
template<typename U, B V>
59+
struct C;
60+
61+
template<typename U, B V>
62+
struct C<U*, V>;
63+
64+
template<typename U, B V>
65+
struct C<U&, V> { }; // expected-note {{previous definition is here}}
66+
};
67+
68+
template<typename T>
69+
template<typename U, typename A<T>::B V>
70+
void A<T>::f() { }
71+
72+
template<typename T>
73+
template<typename U, typename A<T>::B V>
74+
void A<T>::g() { } // expected-error {{redefinition of 'g'}}
75+
76+
template<typename T>
77+
template<typename U, typename A<T>::B V>
78+
int A<T>::x = 0;
79+
80+
template<typename T>
81+
template<typename U, typename A<T>::B V>
82+
int A<T>::x<U*, V> = 0;
83+
84+
template<typename T>
85+
template<typename U, typename A<T>::B V>
86+
int A<T>::x<U&, V> = 0; // expected-error {{redefinition of 'x<U &, V>'}}
87+
88+
template<typename T>
89+
template<typename U, typename A<T>::B V>
90+
struct A<T>::C { };
91+
92+
template<typename T>
93+
template<typename U, typename A<T>::B V>
94+
struct A<T>::C<U*, V> { };
95+
96+
template<typename T>
97+
template<typename U, typename A<T>::B V>
98+
struct A<T>::C<U&, V> { }; // expected-error {{redefinition of 'C<U &, V>'}}
99+
100+
// FIXME: Crashes when parsing the non-type template parameter prior to C++20
101+
template<>
102+
template<typename U, A<int>::B V>
103+
void A<int>::f() { }
104+
105+
template<>
106+
template<typename U, A<int>::B V>
107+
void A<int>::g() { } // expected-note {{previous definition is here}}
108+
109+
template<>
110+
template<typename U, A<int>::B V>
111+
void A<int>::g() { } // expected-error {{redefinition of 'g'}}
112+
113+
template<>
114+
template<typename U, A<int>::B V>
115+
int A<int>::x = 0;
116+
117+
template<>
118+
template<typename U, A<int>::B V>
119+
int A<int>::x<U*, V> = 0;
120+
121+
template<>
122+
template<typename U, A<int>::B V>
123+
int A<int>::x<U&, V> = 0; // expected-note {{previous definition is here}}
124+
125+
template<>
126+
template<typename U, A<int>::B V>
127+
int A<int>::x<U&, V> = 0; // expected-error {{redefinition of 'x<U &, V>'}}
128+
129+
template<>
130+
template<typename U, A<int>::B V>
131+
struct A<int>::C { };
132+
133+
template<>
134+
template<typename U, A<int>::B V>
135+
struct A<int>::C<U*, V> { };
136+
137+
template<>
138+
template<typename U, A<int>::B V>
139+
struct A<int>::C<U&, V> { }; // expected-note {{previous definition is here}}
140+
141+
template<>
142+
template<typename U, A<int>::B V>
143+
struct A<int>::C<U&, V> { }; // expected-error {{redefinition of 'C<U &, V>'}}
144+
}

0 commit comments

Comments
 (0)