Skip to content

Commit

Permalink
[Clang][Sema] Ignore previous partial specializations of member templ…
Browse files Browse the repository at this point in the history
…ates explicitly specialized for an implicitly instantiated class template specialization (llvm#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 llvm#51051.
sdkrystian authored Oct 30, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent e989e31 commit 7d1e283
Showing 4 changed files with 133 additions and 8 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
@@ -574,6 +574,8 @@ Bug Fixes to C++ Support
(#GH95854).
- Fixed an assertion failure when evaluating an invalid expression in an array initializer. (#GH112140)
- Fixed an assertion failure in range calculations for conditional throw expressions. (#GH111854)
- Clang now correctly ignores previous partial specializations of member templates explicitly specialized for
an implicitly instantiated class template specialization. (#GH51051)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
16 changes: 14 additions & 2 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
@@ -4381,8 +4381,20 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
Template->getPartialSpecializations(PartialSpecs);

for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) {
VarTemplatePartialSpecializationDecl *Partial = PartialSpecs[I];
for (VarTemplatePartialSpecializationDecl *Partial : PartialSpecs) {
// C++ [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.
if (Template->getMostRecentDecl()->isMemberSpecialization() &&
!Partial->getMostRecentDecl()->isMemberSpecialization())
continue;

TemplateDeductionInfo Info(FailedCandidates.getLocation());

if (TemplateDeductionResult Result =
38 changes: 32 additions & 6 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
@@ -3978,11 +3978,24 @@ bool Sema::usesPartialOrExplicitSpecialization(
return true;

SmallVector<ClassTemplatePartialSpecializationDecl *, 4> PartialSpecs;
ClassTemplateSpec->getSpecializedTemplate()
->getPartialSpecializations(PartialSpecs);
for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) {
ClassTemplateDecl *CTD = ClassTemplateSpec->getSpecializedTemplate();
CTD->getPartialSpecializations(PartialSpecs);
for (ClassTemplatePartialSpecializationDecl *CTPSD : PartialSpecs) {
// C++ [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.
if (CTD->getMostRecentDecl()->isMemberSpecialization() &&
!CTPSD->getMostRecentDecl()->isMemberSpecialization())
continue;

TemplateDeductionInfo Info(Loc);
if (DeduceTemplateArguments(PartialSpecs[I],
if (DeduceTemplateArguments(CTPSD,
ClassTemplateSpec->getTemplateArgs().asArray(),
Info) == TemplateDeductionResult::Success)
return true;
@@ -4025,8 +4038,21 @@ getPatternForClassTemplateSpecialization(
SmallVector<ClassTemplatePartialSpecializationDecl *, 4> PartialSpecs;
Template->getPartialSpecializations(PartialSpecs);
TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation);
for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) {
ClassTemplatePartialSpecializationDecl *Partial = PartialSpecs[I];
for (ClassTemplatePartialSpecializationDecl *Partial : PartialSpecs) {
// C++ [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.
if (Template->getMostRecentDecl()->isMemberSpecialization() &&
!Partial->getMostRecentDecl()->isMemberSpecialization())
continue;

TemplateDeductionInfo Info(FailedCandidates.getLocation());
if (TemplateDeductionResult Result = S.DeduceTemplateArguments(
Partial, ClassTemplateSpec->getTemplateArgs().asArray(), Info);
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
// expected-no-diagnostics

template<typename T>
struct A {
template<typename U>
struct B {
static constexpr int y = 0;
};

template<typename U>
struct B<U*> {
static constexpr int y = 1;
};

template<typename U>
static constexpr int x = 0;

template<typename U>
static constexpr int x<U*> = 1;
};

template<typename T>
template<typename U>
struct A<T>::B<U[]> {
static constexpr int y = 2;
};

template<typename T>
template<typename U>
constexpr int A<T>::x<U[]> = 2;

static_assert(A<short>::B<int>::y == 0);
static_assert(A<short>::B<int*>::y == 1);
static_assert(A<short>::B<int[]>::y == 2);
static_assert(A<short>::x<int> == 0);
static_assert(A<short>::x<int*> == 1);
static_assert(A<short>::x<int[]> == 2);

template<>
template<typename U>
struct A<int>::B {
static constexpr int y = 3;
};

template<>
template<typename U>
struct A<int>::B<U&> {
static constexpr int y = 4;
};

template<>
template<typename U>
struct A<long>::B<U&> {
static constexpr int y = 5;
};

template<>
template<typename U>
constexpr int A<int>::x = 3;

template<>
template<typename U>
constexpr int A<int>::x<U&> = 4;

template<>
template<typename U>
constexpr int A<long>::x<U&> = 5;

static_assert(A<int>::B<int>::y == 3);
static_assert(A<int>::B<int*>::y == 3);
static_assert(A<int>::B<int[]>::y == 3);
static_assert(A<int>::B<int&>::y == 4);
static_assert(A<int>::x<int> == 3);
static_assert(A<int>::x<int*> == 3);
static_assert(A<int>::x<int[]> == 3);
static_assert(A<int>::x<int&> == 4);
static_assert(A<long>::B<int>::y == 0);
static_assert(A<long>::B<int*>::y == 1);
static_assert(A<long>::B<int[]>::y == 2);
static_assert(A<long>::B<int&>::y == 5);
static_assert(A<long>::x<int> == 0);
static_assert(A<long>::x<int*> == 1);
static_assert(A<long>::x<int[]> == 2);
static_assert(A<long>::x<int&> == 5);

0 comments on commit 7d1e283

Please sign in to comment.