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; // llvm#2
  };
};

template<>
template<typename U>
struct A<long>::B {
  static constexpr int x = 2; // llvm#3
};

static_assert(A<short>::B<int>::y == 0); // uses #1
static_assert(A<short>::B<int*>::y == 1); // uses llvm#2

static_assert(A<long>::B<int>::y == 2); // uses llvm#3
static_assert(A<long>::B<int*>::y == 2); // uses llvm#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.
  • Loading branch information
sdkrystian authored Oct 30, 2024
1 parent e989e31 commit 7d1e283
Show file tree
Hide file tree
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
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
16 changes: 14 additions & 2 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
38 changes: 32 additions & 6 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
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.