Skip to content

Commit 7d1e283

Browse files
authored
[Clang][Sema] Ignore previous partial specializations of member templates explicitly specialized for an implicitly instantiated class template specialization (#113464)
Consider the following: ``` template<typename T> struct A { template<typename U> struct B { static constexpr int x = 0; // #1 }; template<typename U> struct B<U*> { static constexpr int x = 1; // #2 }; }; template<> template<typename U> struct A<long>::B { static constexpr int x = 2; // #3 }; static_assert(A<short>::B<int>::y == 0); // uses #1 static_assert(A<short>::B<int*>::y == 1); // uses #2 static_assert(A<long>::B<int>::y == 2); // uses #3 static_assert(A<long>::B<int*>::y == 2); // uses #3 ``` According to [temp.spec.partial.member] p2: > If the primary member template is explicitly specialized for a given (implicit) specialization of the enclosing class template, the partial specializations of the member template are ignored for this specialization of the enclosing class template. If a partial specialization of the member template is explicitly specialized for a given (implicit) specialization of the enclosing class template, the primary member template and its other partial specializations are still considered for this specialization of the enclosing class template. The example above fails to compile because we currently don't implement [temp.spec.partial.member] p2. This patch implements the wording, fixing #51051.
1 parent e989e31 commit 7d1e283

File tree

4 files changed

+133
-8
lines changed

4 files changed

+133
-8
lines changed

clang/docs/ReleaseNotes.rst

+2
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,8 @@ Bug Fixes to C++ Support
574574
(#GH95854).
575575
- Fixed an assertion failure when evaluating an invalid expression in an array initializer. (#GH112140)
576576
- Fixed an assertion failure in range calculations for conditional throw expressions. (#GH111854)
577+
- Clang now correctly ignores previous partial specializations of member templates explicitly specialized for
578+
an implicitly instantiated class template specialization. (#GH51051)
577579

578580
Bug Fixes to AST Handling
579581
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaTemplate.cpp

+14-2
Original file line numberDiff line numberDiff line change
@@ -4381,8 +4381,20 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
43814381
SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
43824382
Template->getPartialSpecializations(PartialSpecs);
43834383

4384-
for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) {
4385-
VarTemplatePartialSpecializationDecl *Partial = PartialSpecs[I];
4384+
for (VarTemplatePartialSpecializationDecl *Partial : PartialSpecs) {
4385+
// C++ [temp.spec.partial.member]p2:
4386+
// If the primary member template is explicitly specialized for a given
4387+
// (implicit) specialization of the enclosing class template, the partial
4388+
// specializations of the member template are ignored for this
4389+
// specialization of the enclosing class template. If a partial
4390+
// specialization of the member template is explicitly specialized for a
4391+
// given (implicit) specialization of the enclosing class template, the
4392+
// primary member template and its other partial specializations are still
4393+
// considered for this specialization of the enclosing class template.
4394+
if (Template->getMostRecentDecl()->isMemberSpecialization() &&
4395+
!Partial->getMostRecentDecl()->isMemberSpecialization())
4396+
continue;
4397+
43864398
TemplateDeductionInfo Info(FailedCandidates.getLocation());
43874399

43884400
if (TemplateDeductionResult Result =

clang/lib/Sema/SemaTemplateInstantiate.cpp

+32-6
Original file line numberDiff line numberDiff line change
@@ -3978,11 +3978,24 @@ bool Sema::usesPartialOrExplicitSpecialization(
39783978
return true;
39793979

39803980
SmallVector<ClassTemplatePartialSpecializationDecl *, 4> PartialSpecs;
3981-
ClassTemplateSpec->getSpecializedTemplate()
3982-
->getPartialSpecializations(PartialSpecs);
3983-
for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) {
3981+
ClassTemplateDecl *CTD = ClassTemplateSpec->getSpecializedTemplate();
3982+
CTD->getPartialSpecializations(PartialSpecs);
3983+
for (ClassTemplatePartialSpecializationDecl *CTPSD : PartialSpecs) {
3984+
// C++ [temp.spec.partial.member]p2:
3985+
// If the primary member template is explicitly specialized for a given
3986+
// (implicit) specialization of the enclosing class template, the partial
3987+
// specializations of the member template are ignored for this
3988+
// specialization of the enclosing class template. If a partial
3989+
// specialization of the member template is explicitly specialized for a
3990+
// given (implicit) specialization of the enclosing class template, the
3991+
// primary member template and its other partial specializations are still
3992+
// considered for this specialization of the enclosing class template.
3993+
if (CTD->getMostRecentDecl()->isMemberSpecialization() &&
3994+
!CTPSD->getMostRecentDecl()->isMemberSpecialization())
3995+
continue;
3996+
39843997
TemplateDeductionInfo Info(Loc);
3985-
if (DeduceTemplateArguments(PartialSpecs[I],
3998+
if (DeduceTemplateArguments(CTPSD,
39863999
ClassTemplateSpec->getTemplateArgs().asArray(),
39874000
Info) == TemplateDeductionResult::Success)
39884001
return true;
@@ -4025,8 +4038,21 @@ getPatternForClassTemplateSpecialization(
40254038
SmallVector<ClassTemplatePartialSpecializationDecl *, 4> PartialSpecs;
40264039
Template->getPartialSpecializations(PartialSpecs);
40274040
TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation);
4028-
for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) {
4029-
ClassTemplatePartialSpecializationDecl *Partial = PartialSpecs[I];
4041+
for (ClassTemplatePartialSpecializationDecl *Partial : PartialSpecs) {
4042+
// C++ [temp.spec.partial.member]p2:
4043+
// If the primary member template is explicitly specialized for a given
4044+
// (implicit) specialization of the enclosing class template, the
4045+
// partial specializations of the member template are ignored for this
4046+
// specialization of the enclosing class template. If a partial
4047+
// specialization of the member template is explicitly specialized for a
4048+
// given (implicit) specialization of the enclosing class template, the
4049+
// primary member template and its other partial specializations are
4050+
// still considered for this specialization of the enclosing class
4051+
// template.
4052+
if (Template->getMostRecentDecl()->isMemberSpecialization() &&
4053+
!Partial->getMostRecentDecl()->isMemberSpecialization())
4054+
continue;
4055+
40304056
TemplateDeductionInfo Info(FailedCandidates.getLocation());
40314057
if (TemplateDeductionResult Result = S.DeduceTemplateArguments(
40324058
Partial, ClassTemplateSpec->getTemplateArgs().asArray(), Info);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
2+
// expected-no-diagnostics
3+
4+
template<typename T>
5+
struct A {
6+
template<typename U>
7+
struct B {
8+
static constexpr int y = 0;
9+
};
10+
11+
template<typename U>
12+
struct B<U*> {
13+
static constexpr int y = 1;
14+
};
15+
16+
template<typename U>
17+
static constexpr int x = 0;
18+
19+
template<typename U>
20+
static constexpr int x<U*> = 1;
21+
};
22+
23+
template<typename T>
24+
template<typename U>
25+
struct A<T>::B<U[]> {
26+
static constexpr int y = 2;
27+
};
28+
29+
template<typename T>
30+
template<typename U>
31+
constexpr int A<T>::x<U[]> = 2;
32+
33+
static_assert(A<short>::B<int>::y == 0);
34+
static_assert(A<short>::B<int*>::y == 1);
35+
static_assert(A<short>::B<int[]>::y == 2);
36+
static_assert(A<short>::x<int> == 0);
37+
static_assert(A<short>::x<int*> == 1);
38+
static_assert(A<short>::x<int[]> == 2);
39+
40+
template<>
41+
template<typename U>
42+
struct A<int>::B {
43+
static constexpr int y = 3;
44+
};
45+
46+
template<>
47+
template<typename U>
48+
struct A<int>::B<U&> {
49+
static constexpr int y = 4;
50+
};
51+
52+
template<>
53+
template<typename U>
54+
struct A<long>::B<U&> {
55+
static constexpr int y = 5;
56+
};
57+
58+
template<>
59+
template<typename U>
60+
constexpr int A<int>::x = 3;
61+
62+
template<>
63+
template<typename U>
64+
constexpr int A<int>::x<U&> = 4;
65+
66+
template<>
67+
template<typename U>
68+
constexpr int A<long>::x<U&> = 5;
69+
70+
static_assert(A<int>::B<int>::y == 3);
71+
static_assert(A<int>::B<int*>::y == 3);
72+
static_assert(A<int>::B<int[]>::y == 3);
73+
static_assert(A<int>::B<int&>::y == 4);
74+
static_assert(A<int>::x<int> == 3);
75+
static_assert(A<int>::x<int*> == 3);
76+
static_assert(A<int>::x<int[]> == 3);
77+
static_assert(A<int>::x<int&> == 4);
78+
static_assert(A<long>::B<int>::y == 0);
79+
static_assert(A<long>::B<int*>::y == 1);
80+
static_assert(A<long>::B<int[]>::y == 2);
81+
static_assert(A<long>::B<int&>::y == 5);
82+
static_assert(A<long>::x<int> == 0);
83+
static_assert(A<long>::x<int*> == 1);
84+
static_assert(A<long>::x<int[]> == 2);
85+
static_assert(A<long>::x<int&> == 5);

0 commit comments

Comments
 (0)