Skip to content

[Clang][Sema] Rebuild template parameters for out-of-line template definitions and partial specializations #104030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 13 additions & 7 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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()) {
Expand All @@ -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) {
Expand Down
113 changes: 110 additions & 3 deletions clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// 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;

template <class U> struct B {
static T twice(U value) {
return (cond ? value + value : value);
Expand Down Expand Up @@ -35,3 +34,111 @@ 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 inline 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>
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>
int A<int>::x<U&, V> = 0; // expected-note {{previous definition is here}}

template<>
template<typename U, A<int>::B V>
int A<int>::x<U&, V> = 0; // expected-error {{redefinition of 'x<U &, V>'}}

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>'}}
}
Loading