[clang] cdd71d6 - [Clang][Sema] Refactor collection of multi-level template argument lists (#106585)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 20 11:57:44 PDT 2024
Author: Krystian Stasiowski
Date: 2024-09-20T14:57:40-04:00
New Revision: cdd71d61664b63ae57bdba9ee0d891f78ef79c07
URL: https://github.com/llvm/llvm-project/commit/cdd71d61664b63ae57bdba9ee0d891f78ef79c07
DIFF: https://github.com/llvm/llvm-project/commit/cdd71d61664b63ae57bdba9ee0d891f78ef79c07.diff
LOG: [Clang][Sema] Refactor collection of multi-level template argument lists (#106585)
Currently, clang rejects the following explicit specialization of `f`
due to the constraints not being equivalent:
```
template<typename T>
struct A
{
template<bool B>
void f() requires B;
};
template<>
template<bool B>
void A<int>::f() requires B { }
```
This happens because, in most cases, we do not set the flag indicating
whether a `RedeclarableTemplate` is an explicit specialization of a
member of an implicitly instantiated class template specialization until
_after_ we compare constraints for equivalence. This patch addresses the
issue (and a number of other issues) by:
- storing the flag indicating whether a declaration is a member
specialization on a per declaration basis, and
- significantly refactoring `Sema::getTemplateInstantiationArgs` so we
collect the right set of template argument in all cases.
Many of our declaration matching & constraint evaluation woes can be
traced back to bugs in `Sema::getTemplateInstantiationArgs`. This
change/refactor should fix a lot of them. It also paves the way for
fixing #101330 and #105462 per my suggestion in #102267 (which I have
implemented on top of this patch but will merge in a subsequent PR).
Added:
clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/DeclTemplate.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/DeclTemplate.cpp
clang/lib/Sema/SemaConcept.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/lib/Sema/SemaTemplateDeductionGuide.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Serialization/ASTReader.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0a1d0fd85e7ae0..f4535db7356194 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -404,6 +404,9 @@ Bug Fixes to C++ Support
- Fixed an assertion failure in debug mode, and potential crashes in release mode, when
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.
- Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326)
+- Clang now uses the correct set of template argument lists when comparing the constraints of
+ out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of
+ a class template. (#GH102320)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 687715a22e9fd3..05739f39d2a496 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -781,15 +781,11 @@ class RedeclarableTemplateDecl : public TemplateDecl,
EntryType *Entry, void *InsertPos);
struct CommonBase {
- CommonBase() : InstantiatedFromMember(nullptr, false) {}
+ CommonBase() {}
/// The template from which this was most
/// directly instantiated (or null).
- ///
- /// The boolean value indicates whether this template
- /// was explicitly specialized.
- llvm::PointerIntPair<RedeclarableTemplateDecl*, 1, bool>
- InstantiatedFromMember;
+ RedeclarableTemplateDecl *InstantiatedFromMember = nullptr;
/// If non-null, points to an array of specializations (including
/// partial specializations) known only by their external declaration IDs.
@@ -809,14 +805,19 @@ class RedeclarableTemplateDecl : public TemplateDecl,
};
/// Pointer to the common data shared by all declarations of this
- /// template.
- mutable CommonBase *Common = nullptr;
+ /// template, and a flag indicating if the template is a member
+ /// specialization.
+ mutable llvm::PointerIntPair<CommonBase *, 1, bool> Common;
+
+ CommonBase *getCommonPtrInternal() const { return Common.getPointer(); }
/// Retrieves the "common" pointer shared by all (re-)declarations of
/// the same template. Calling this routine may implicitly allocate memory
/// for the common pointer.
CommonBase *getCommonPtr() const;
+ void setCommonPtr(CommonBase *C) const { Common.setPointer(C); }
+
virtual CommonBase *newCommon(ASTContext &C) const = 0;
// Construct a template decl with name, parameters, and templated element.
@@ -857,15 +858,12 @@ class RedeclarableTemplateDecl : public TemplateDecl,
/// template<> template<typename T>
/// struct X<int>::Inner { /* ... */ };
/// \endcode
- bool isMemberSpecialization() const {
- return getCommonPtr()->InstantiatedFromMember.getInt();
- }
+ bool isMemberSpecialization() const { return Common.getInt(); }
/// Note that this member template is a specialization.
void setMemberSpecialization() {
- assert(getCommonPtr()->InstantiatedFromMember.getPointer() &&
- "Only member templates can be member template specializations");
- getCommonPtr()->InstantiatedFromMember.setInt(true);
+ assert(!isMemberSpecialization() && "already a member specialization");
+ Common.setInt(true);
}
/// Retrieve the member template from which this template was
@@ -905,12 +903,12 @@ class RedeclarableTemplateDecl : public TemplateDecl,
/// void X<T>::f(T, U);
/// \endcode
RedeclarableTemplateDecl *getInstantiatedFromMemberTemplate() const {
- return getCommonPtr()->InstantiatedFromMember.getPointer();
+ return getCommonPtr()->InstantiatedFromMember;
}
void setInstantiatedFromMemberTemplate(RedeclarableTemplateDecl *TD) {
- assert(!getCommonPtr()->InstantiatedFromMember.getPointer());
- getCommonPtr()->InstantiatedFromMember.setPointer(TD);
+ assert(!getCommonPtr()->InstantiatedFromMember);
+ getCommonPtr()->InstantiatedFromMember = TD;
}
/// Retrieve the "injected" template arguments that correspond to the
@@ -1989,6 +1987,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
/// template arguments have been deduced.
void setInstantiationOf(ClassTemplatePartialSpecializationDecl *PartialSpec,
const TemplateArgumentList *TemplateArgs) {
+ assert(!isa<ClassTemplatePartialSpecializationDecl>(this) &&
+ "A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() &&
"Already set to a class template partial specialization!");
auto *PS = new (getASTContext()) SpecializedPartialSpecialization();
@@ -2000,6 +2000,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
/// Note that this class template specialization is an instantiation
/// of the given class template.
void setInstantiationOf(ClassTemplateDecl *TemplDecl) {
+ assert(!isa<ClassTemplatePartialSpecializationDecl>(this) &&
+ "A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() &&
"Previously set to a class template partial specialization!");
SpecializedTemplate = TemplDecl;
@@ -2187,18 +2189,11 @@ class ClassTemplatePartialSpecializationDecl
/// struct X<int>::Inner<T*> { /* ... */ };
/// \endcode
bool isMemberSpecialization() const {
- const auto *First =
- cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
- return First->InstantiatedFromMember.getInt();
+ return InstantiatedFromMember.getInt();
}
/// Note that this member template is a specialization.
- void setMemberSpecialization() {
- auto *First = cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
- assert(First->InstantiatedFromMember.getPointer() &&
- "Only member templates can be member template specializations");
- return First->InstantiatedFromMember.setInt(true);
- }
+ void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }
/// Retrieves the injected specialization type for this partial
/// specialization. This is not the same as the type-decl-type for
@@ -2268,10 +2263,6 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
return static_cast<Common *>(RedeclarableTemplateDecl::getCommonPtr());
}
- void setCommonPtr(Common *C) {
- RedeclarableTemplateDecl::Common = C;
- }
-
public:
friend class ASTDeclReader;
@@ -2754,6 +2745,8 @@ class VarTemplateSpecializationDecl : public VarDecl,
/// template arguments have been deduced.
void setInstantiationOf(VarTemplatePartialSpecializationDecl *PartialSpec,
const TemplateArgumentList *TemplateArgs) {
+ assert(!isa<VarTemplatePartialSpecializationDecl>(this) &&
+ "A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() &&
"Already set to a variable template partial specialization!");
auto *PS = new (getASTContext()) SpecializedPartialSpecialization();
@@ -2765,6 +2758,8 @@ class VarTemplateSpecializationDecl : public VarDecl,
/// Note that this variable template specialization is an instantiation
/// of the given variable template.
void setInstantiationOf(VarTemplateDecl *TemplDecl) {
+ assert(!isa<VarTemplatePartialSpecializationDecl>(this) &&
+ "A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() &&
"Previously set to a variable template partial specialization!");
SpecializedTemplate = TemplDecl;
@@ -2949,18 +2944,11 @@ class VarTemplatePartialSpecializationDecl
/// U* X<int>::Inner<T*> = (T*)(0) + 1;
/// \endcode
bool isMemberSpecialization() const {
- const auto *First =
- cast<VarTemplatePartialSpecializationDecl>(getFirstDecl());
- return First->InstantiatedFromMember.getInt();
+ return InstantiatedFromMember.getInt();
}
/// Note that this member template is a specialization.
- void setMemberSpecialization() {
- auto *First = cast<VarTemplatePartialSpecializationDecl>(getFirstDecl());
- assert(First->InstantiatedFromMember.getPointer() &&
- "Only member templates can be member template specializations");
- return First->InstantiatedFromMember.setInt(true);
- }
+ void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }
SourceRange getSourceRange() const override LLVM_READONLY;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e1c3a99cfa167e..b86861ce7e8cfa 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11389,9 +11389,9 @@ class Sema final : public SemaBase {
CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams,
AccessSpecifier AS, SourceLocation ModulePrivateLoc,
- SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists,
- TemplateParameterList **OuterTemplateParamLists,
- SkipBodyInfo *SkipBody = nullptr);
+ SourceLocation FriendLoc,
+ ArrayRef<TemplateParameterList *> OuterTemplateParamLists,
+ bool IsMemberSpecialization, SkipBodyInfo *SkipBody = nullptr);
/// Translates template arguments as provided by the parser
/// into template arguments used by semantic analysis.
@@ -11430,7 +11430,8 @@ class Sema final : public SemaBase {
DeclResult ActOnVarTemplateSpecialization(
Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous,
SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
- StorageClass SC, bool IsPartialSpecialization);
+ StorageClass SC, bool IsPartialSpecialization,
+ bool IsMemberSpecialization);
/// Get the specialization of the given variable template corresponding to
/// the specified argument list, or a null-but-valid result if the arguments
@@ -13071,28 +13072,14 @@ class Sema final : public SemaBase {
/// dealing with a specialization. This is only relevant for function
/// template specializations.
///
- /// \param Pattern If non-NULL, indicates the pattern from which we will be
- /// instantiating the definition of the given declaration, \p ND. This is
- /// used to determine the proper set of template instantiation arguments for
- /// friend function template specializations.
- ///
/// \param ForConstraintInstantiation when collecting arguments,
/// ForConstraintInstantiation indicates we should continue looking when
/// encountering a lambda generic call operator, and continue looking for
/// arguments on an enclosing class template.
- ///
- /// \param SkipForSpecialization when specified, any template specializations
- /// in a traversal would be ignored.
- /// \param ForDefaultArgumentSubstitution indicates we should continue looking
- /// when encountering a specialized member function template, rather than
- /// returning immediately.
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false,
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
- bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
- bool ForConstraintInstantiation = false,
- bool SkipForSpecialization = false,
- bool ForDefaultArgumentSubstitution = false);
+ bool RelativeToPrimary = false, bool ForConstraintInstantiation = false);
/// RAII object to handle the state changes required to synthesize
/// a function body.
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 976b3a3e1ecedb..6f17baf4fc01ad 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -309,16 +309,16 @@ bool TemplateDecl::isTypeAlias() const {
void RedeclarableTemplateDecl::anchor() {}
RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() const {
- if (Common)
- return Common;
+ if (CommonBase *C = getCommonPtrInternal())
+ return C;
// Walk the previous-declaration chain until we either find a declaration
// with a common pointer or we run out of previous declarations.
SmallVector<const RedeclarableTemplateDecl *, 2> PrevDecls;
for (const RedeclarableTemplateDecl *Prev = getPreviousDecl(); Prev;
Prev = Prev->getPreviousDecl()) {
- if (Prev->Common) {
- Common = Prev->Common;
+ if (CommonBase *C = Prev->getCommonPtrInternal()) {
+ setCommonPtr(C);
break;
}
@@ -326,18 +326,18 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() c
}
// If we never found a common pointer, allocate one now.
- if (!Common) {
+ if (!getCommonPtrInternal()) {
// FIXME: If any of the declarations is from an AST file, we probably
// need an update record to add the common data.
- Common = newCommon(getASTContext());
+ setCommonPtr(newCommon(getASTContext()));
}
// Update any previous declarations we saw with the common pointer.
for (const RedeclarableTemplateDecl *Prev : PrevDecls)
- Prev->Common = Common;
+ Prev->setCommonPtr(getCommonPtrInternal());
- return Common;
+ return getCommonPtrInternal();
}
void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const {
@@ -467,15 +467,15 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
// If we haven't created a common pointer yet, then it can just be created
// with the usual method.
- if (!Base::Common)
+ if (!getCommonPtrInternal())
return;
- Common *ThisCommon = static_cast<Common *>(Base::Common);
+ Common *ThisCommon = static_cast<Common *>(getCommonPtrInternal());
Common *PrevCommon = nullptr;
SmallVector<FunctionTemplateDecl *, 8> PreviousDecls;
for (; Prev; Prev = Prev->getPreviousDecl()) {
- if (Prev->Base::Common) {
- PrevCommon = static_cast<Common *>(Prev->Base::Common);
+ if (CommonBase *C = Prev->getCommonPtrInternal()) {
+ PrevCommon = static_cast<Common *>(C);
break;
}
PreviousDecls.push_back(Prev);
@@ -485,7 +485,7 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
// use this common pointer.
if (!PrevCommon) {
for (auto *D : PreviousDecls)
- D->Base::Common = ThisCommon;
+ D->setCommonPtr(ThisCommon);
return;
}
@@ -493,7 +493,7 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
assert(ThisCommon->Specializations.size() == 0 &&
"Can't merge incompatible declarations!");
- Base::Common = PrevCommon;
+ setCommonPtr(PrevCommon);
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 6a1b32598bb4a6..8b83418c897f8d 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -585,8 +585,8 @@ static bool CheckConstraintSatisfaction(
ArrayRef<TemplateArgument> TemplateArgs =
TemplateArgsLists.getNumSubstitutedLevels() > 0
- ? TemplateArgsLists.getOutermost()
- : ArrayRef<TemplateArgument> {};
+ ? TemplateArgsLists.getInnermost()
+ : ArrayRef<TemplateArgument>{};
Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
Sema::InstantiatingTemplate::ConstraintsCheck{},
const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
@@ -833,7 +833,6 @@ Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(),
/*Final=*/false, /*Innermost=*/std::nullopt,
/*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
return std::nullopt;
@@ -909,15 +908,13 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
// Figure out the to-translation-unit depth for this function declaration for
// the purpose of seeing if they
diff er by constraints. This isn't the same as
// getTemplateDepth, because it includes already instantiated parents.
-static unsigned
-CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
- bool SkipForSpecialization = false) {
+static unsigned CalculateTemplateDepthForConstraints(Sema &S,
+ const NamedDecl *ND) {
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
ND, ND->getLexicalDeclContext(), /*Final=*/false,
/*Innermost=*/std::nullopt,
/*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
- /*ForConstraintInstantiation=*/true, SkipForSpecialization);
+ /*ForConstraintInstantiation=*/true);
return MLTAL.getNumLevels();
}
@@ -956,8 +953,7 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false,
/*Innermost=*/std::nullopt,
/*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true,
- /*SkipForSpecialization*/ false);
+ /*ForConstraintInstantiation=*/true);
if (MLTAL.getNumSubstitutedLevels() == 0)
return ConstrExpr;
@@ -1063,16 +1059,16 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) {
assert(FD->getFriendObjectKind() && "Must be a friend!");
+ FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate();
// The logic for non-templates is handled in ASTContext::isSameEntity, so we
// don't have to bother checking 'DependsOnEnclosingTemplate' for a
// non-function-template.
- assert(FD->getDescribedFunctionTemplate() &&
- "Non-function templates don't need to be checked");
+ assert(FTD && "Non-function templates don't need to be checked");
SmallVector<const Expr *, 3> ACs;
- FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs);
+ FTD->getAssociatedConstraints(ACs);
- unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD);
+ unsigned OldTemplateDepth = FTD->getTemplateParameters()->getDepth();
for (const Expr *Constraint : ACs)
if (ConstraintExpressionDependsOnEnclosingTemplate(FD, OldTemplateDepth,
Constraint))
@@ -1519,7 +1515,6 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(),
/*Final=*/false, CSE->getTemplateArguments(),
/*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL,
@@ -1800,8 +1795,8 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
return false;
}
- unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1, true);
- unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2, true);
+ unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1);
+ unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2);
for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) {
if (Depth2 > Depth1) {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 31bf50a32a83c3..de8805e15bc750 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -4505,10 +4505,10 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
adjustDeclContextForDeclaratorDecl(New, Old);
// Ensure the template parameters are compatible.
- if (NewTemplate &&
- !TemplateParameterListsAreEqual(NewTemplate->getTemplateParameters(),
- OldTemplate->getTemplateParameters(),
- /*Complain=*/true, TPL_TemplateMatch))
+ if (NewTemplate && !TemplateParameterListsAreEqual(
+ NewTemplate, NewTemplate->getTemplateParameters(),
+ OldTemplate, OldTemplate->getTemplateParameters(),
+ /*Complain=*/true, TPL_TemplateMatch))
return New->setInvalidDecl();
// C++ [class.mem]p1:
@@ -7655,7 +7655,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
: SourceLocation();
DeclResult Res = ActOnVarTemplateSpecialization(
S, D, TInfo, Previous, TemplateKWLoc, TemplateParams, SC,
- IsPartialSpecialization);
+ IsPartialSpecialization, IsMemberSpecialization);
if (Res.isInvalid())
return nullptr;
NewVD = cast<VarDecl>(Res.get());
@@ -7674,6 +7674,10 @@ NamedDecl *Sema::ActOnVariableDeclarator(
VarTemplateDecl::Create(Context, DC, D.getIdentifierLoc(), Name,
TemplateParams, NewVD);
NewVD->setDescribedVarTemplate(NewTemplate);
+ // If we are providing an explicit specialization of a static variable
+ // template, make a note of that.
+ if (IsMemberSpecialization)
+ NewTemplate->setMemberSpecialization();
}
// If this decl has an auto type in need of deduction, make a note of the
@@ -8049,12 +8053,6 @@ NamedDecl *Sema::ActOnVariableDeclarator(
? TPC_ClassTemplateMember
: TPC_VarTemplate))
NewVD->setInvalidDecl();
-
- // If we are providing an explicit specialization of a static variable
- // template, make a note of that.
- if (PrevVarTemplate &&
- PrevVarTemplate->getInstantiatedFromMemberTemplate())
- PrevVarTemplate->setMemberSpecialization();
}
}
@@ -9859,6 +9857,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
NewFD);
FunctionTemplate->setLexicalDeclContext(CurContext);
NewFD->setDescribedFunctionTemplate(FunctionTemplate);
+ if (isMemberSpecialization)
+ FunctionTemplate->setMemberSpecialization();
// For source fidelity, store the other template param lists.
if (TemplateParamLists.size() > 1) {
@@ -12005,10 +12005,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
// If this is an explicit specialization of a member that is a function
// template, mark it as a member specialization.
- if (IsMemberSpecialization &&
- NewTemplateDecl->getInstantiatedFromMemberTemplate()) {
- NewTemplateDecl->setMemberSpecialization();
- assert(OldTemplateDecl->isMemberSpecialization());
+ if (IsMemberSpecialization) {
// Explicit specializations of a member template do not inherit deleted
// status from the parent member template that they are specializing.
if (OldFD->isDeleted()) {
@@ -17067,8 +17064,8 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
DeclResult Result = CheckClassTemplate(
S, TagSpec, TUK, KWLoc, SS, Name, NameLoc, Attrs, TemplateParams,
AS, ModulePrivateLoc,
- /*FriendLoc*/ SourceLocation(), TemplateParameterLists.size() - 1,
- TemplateParameterLists.data(), SkipBody);
+ /*FriendLoc*/ SourceLocation(), TemplateParameterLists.drop_back(),
+ isMemberSpecialization, SkipBody);
return Result.get();
} else {
// The "template<>" header is extraneous.
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index d8cdfcf8c6ec05..31cd6802a39fa4 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17416,8 +17416,8 @@ DeclResult Sema::ActOnTemplatedFriendTag(
return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS,
Name, NameLoc, Attr, TemplateParams, AS_public,
/*ModulePrivateLoc=*/SourceLocation(),
- FriendLoc, TempParamLists.size() - 1,
- TempParamLists.data())
+ FriendLoc, TempParamLists.drop_back(),
+ IsMemberSpecialization)
.get();
} else {
// The "template<>" header is extraneous.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index b052afede2cd67..16f4542d785715 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1793,8 +1793,9 @@ DeclResult Sema::CheckClassTemplate(
CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams,
AccessSpecifier AS, SourceLocation ModulePrivateLoc,
- SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists,
- TemplateParameterList **OuterTemplateParamLists, SkipBodyInfo *SkipBody) {
+ SourceLocation FriendLoc,
+ ArrayRef<TemplateParameterList *> OuterTemplateParamLists,
+ bool IsMemberSpecialization, SkipBodyInfo *SkipBody) {
assert(TemplateParams && TemplateParams->size() > 0 &&
"No template parameters");
assert(TUK != TagUseKind::Reference &&
@@ -1982,19 +1983,6 @@ DeclResult Sema::CheckClassTemplate(
}
if (PrevClassTemplate) {
- // Ensure that the template parameter lists are compatible. Skip this check
- // for a friend in a dependent context: the template parameter list itself
- // could be dependent.
- if (!(TUK == TagUseKind::Friend && CurContext->isDependentContext()) &&
- !TemplateParameterListsAreEqual(
- TemplateCompareNewDeclInfo(SemanticContext ? SemanticContext
- : CurContext,
- CurContext, KWLoc),
- TemplateParams, PrevClassTemplate,
- PrevClassTemplate->getTemplateParameters(), /*Complain=*/true,
- TPL_TemplateMatch))
- return true;
-
// C++ [temp.class]p4:
// In a redeclaration, partial specialization, explicit
// specialization or explicit instantiation of a class template,
@@ -2009,30 +1997,6 @@ DeclResult Sema::CheckClassTemplate(
Diag(PrevRecordDecl->getLocation(), diag::note_previous_use);
Kind = PrevRecordDecl->getTagKind();
}
-
- // Check for redefinition of this class template.
- if (TUK == TagUseKind::Definition) {
- if (TagDecl *Def = PrevRecordDecl->getDefinition()) {
- // If we have a prior definition that is not visible, treat this as
- // simply making that previous definition visible.
- NamedDecl *Hidden = nullptr;
- if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) {
- SkipBody->ShouldSkip = true;
- SkipBody->Previous = Def;
- auto *Tmpl = cast<CXXRecordDecl>(Hidden)->getDescribedClassTemplate();
- assert(Tmpl && "original definition of a class template is not a "
- "class template?");
- makeMergedDefinitionVisible(Hidden);
- makeMergedDefinitionVisible(Tmpl);
- } else {
- Diag(NameLoc, diag::err_redefinition) << Name;
- Diag(Def->getLocation(), diag::note_previous_definition);
- // FIXME: Would it make sense to try to "forget" the previous
- // definition, as part of error recovery?
- return true;
- }
- }
- }
} else if (PrevDecl) {
// C++ [temp]p5:
// A class template shall not have the same name as any other
@@ -2044,23 +2008,6 @@ DeclResult Sema::CheckClassTemplate(
return true;
}
- // Check the template parameter list of this declaration, possibly
- // merging in the template parameter list from the previous class
- // template declaration. Skip this check for a friend in a dependent
- // context, because the template parameter list might be dependent.
- if (!(TUK == TagUseKind::Friend && CurContext->isDependentContext()) &&
- CheckTemplateParameterList(
- TemplateParams,
- PrevClassTemplate ? GetTemplateParameterList(PrevClassTemplate)
- : nullptr,
- (SS.isSet() && SemanticContext && SemanticContext->isRecord() &&
- SemanticContext->isDependentContext())
- ? TPC_ClassTemplateMember
- : TUK == TagUseKind::Friend ? TPC_FriendClassTemplate
- : TPC_ClassTemplate,
- SkipBody))
- Invalid = true;
-
if (SS.isSet()) {
// If the name of the template was qualified, we must be defining the
// template out-of-line.
@@ -2087,10 +2034,8 @@ DeclResult Sema::CheckClassTemplate(
PrevClassTemplate->getTemplatedDecl() : nullptr,
/*DelayTypeCreation=*/true);
SetNestedNameSpecifier(*this, NewClass, SS);
- if (NumOuterTemplateParamLists > 0)
- NewClass->setTemplateParameterListsInfo(
- Context,
- llvm::ArrayRef(OuterTemplateParamLists, NumOuterTemplateParamLists));
+ if (!OuterTemplateParamLists.empty())
+ NewClass->setTemplateParameterListsInfo(Context, OuterTemplateParamLists);
// Add alignment attributes if necessary; these attributes are checked when
// the ASTContext lays out the structure.
@@ -2103,7 +2048,10 @@ DeclResult Sema::CheckClassTemplate(
= ClassTemplateDecl::Create(Context, SemanticContext, NameLoc,
DeclarationName(Name), TemplateParams,
NewClass);
-
+ // If we are providing an explicit specialization of a member that is a
+ // class template, make a note of that.
+ if (IsMemberSpecialization)
+ NewTemplate->setMemberSpecialization();
if (ShouldAddRedecl)
NewTemplate->setPreviousDecl(PrevClassTemplate);
@@ -2118,12 +2066,6 @@ DeclResult Sema::CheckClassTemplate(
assert(T->isDependentType() && "Class template type is not dependent?");
(void)T;
- // If we are providing an explicit specialization of a member that is a
- // class template, make a note of that.
- if (PrevClassTemplate &&
- PrevClassTemplate->getInstantiatedFromMemberTemplate())
- PrevClassTemplate->setMemberSpecialization();
-
// Set the access specifier.
if (!Invalid && TUK != TagUseKind::Friend &&
NewTemplate->getDeclContext()->isRecord())
@@ -2133,8 +2075,62 @@ DeclResult Sema::CheckClassTemplate(
NewClass->setLexicalDeclContext(CurContext);
NewTemplate->setLexicalDeclContext(CurContext);
- if (TUK == TagUseKind::Definition && (!SkipBody || !SkipBody->ShouldSkip))
- NewClass->startDefinition();
+ // Ensure that the template parameter lists are compatible. Skip this check
+ // for a friend in a dependent context: the template parameter list itself
+ // could be dependent.
+ if (ShouldAddRedecl && PrevClassTemplate &&
+ !TemplateParameterListsAreEqual(
+ NewTemplate, TemplateParams, PrevClassTemplate,
+ PrevClassTemplate->getTemplateParameters(),
+ /*Complain=*/true, TPL_TemplateMatch))
+ return true;
+
+ // Check the template parameter list of this declaration, possibly
+ // merging in the template parameter list from the previous class
+ // template declaration. Skip this check for a friend in a dependent
+ // context, because the template parameter list might be dependent.
+ if (ShouldAddRedecl &&
+ CheckTemplateParameterList(
+ TemplateParams,
+ PrevClassTemplate ? PrevClassTemplate->getTemplateParameters()
+ : nullptr,
+ (SS.isSet() && SemanticContext && SemanticContext->isRecord() &&
+ SemanticContext->isDependentContext())
+ ? TPC_ClassTemplateMember
+ : TUK == TagUseKind::Friend ? TPC_FriendClassTemplate
+ : TPC_ClassTemplate,
+ SkipBody))
+ Invalid = true;
+
+ if (TUK == TagUseKind::Definition) {
+ if (PrevClassTemplate) {
+ // Check for redefinition of this class template.
+ if (TagDecl *Def =
+ PrevClassTemplate->getTemplatedDecl()->getDefinition()) {
+ // If we have a prior definition that is not visible, treat this as
+ // simply making that previous definition visible.
+ NamedDecl *Hidden = nullptr;
+ if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) {
+ SkipBody->ShouldSkip = true;
+ SkipBody->Previous = Def;
+ auto *Tmpl = cast<CXXRecordDecl>(Hidden)->getDescribedClassTemplate();
+ assert(Tmpl && "original definition of a class template is not a "
+ "class template?");
+ makeMergedDefinitionVisible(Hidden);
+ makeMergedDefinitionVisible(Tmpl);
+ } else {
+ Diag(NameLoc, diag::err_redefinition) << Name;
+ Diag(Def->getLocation(), diag::note_previous_definition);
+ // FIXME: Would it make sense to try to "forget" the previous
+ // definition, as part of error recovery?
+ return true;
+ }
+ }
+ }
+
+ if (!SkipBody || !SkipBody->ShouldSkip)
+ NewClass->startDefinition();
+ }
ProcessDeclAttributeList(S, NewClass, Attr);
ProcessAPINotes(NewClass);
@@ -3974,7 +3970,8 @@ void Sema::CheckDeductionGuideTemplate(FunctionTemplateDecl *TD) {
DeclResult Sema::ActOnVarTemplateSpecialization(
Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous,
SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
- StorageClass SC, bool IsPartialSpecialization) {
+ StorageClass SC, bool IsPartialSpecialization,
+ bool IsMemberSpecialization) {
// D must be variable template id.
assert(D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId &&
"Variable template specialization is declared with a template id.");
@@ -4092,17 +4089,16 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
Context, VarTemplate->getDeclContext(), TemplateKWLoc,
TemplateNameLoc, TemplateParams, VarTemplate, DI->getType(), DI, SC,
CanonicalConverted);
+ // If we are providing an explicit specialization of a member variable
+ // template specialization, make a note of that.
+ if (IsMemberSpecialization)
+ Partial->setMemberSpecialization();
Partial->setTemplateArgsAsWritten(TemplateArgs);
if (!PrevPartial)
VarTemplate->AddPartialSpecialization(Partial, InsertPos);
Specialization = Partial;
- // If we are providing an explicit specialization of a member variable
- // template specialization, make a note of that.
- if (PrevPartial && PrevPartial->getInstantiatedFromMember())
- PrevPartial->setMemberSpecialization();
-
CheckTemplatePartialSpecialization(Partial);
} else {
// Create a new class template specialization declaration node for
@@ -5617,9 +5613,7 @@ bool Sema::CheckTemplateArgumentList(
MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
Template, NewContext, /*Final=*/false, CanonicalConverted,
- /*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
- /*ForConceptInstantiation=*/true);
+ /*RelativeToPrimary=*/true, /*ForConceptInstantiation=*/true);
if (EnsureTemplateArgumentListConstraints(
Template, MLTAL,
SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) {
@@ -8308,15 +8302,12 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
Diag(TemplateNameLoc, diag::err_partial_spec_args_match_primary_template)
<< /*class template*/ 0 << (TUK == TagUseKind::Definition)
<< FixItHint::CreateRemoval(SourceRange(LAngleLoc, RAngleLoc));
- return CheckClassTemplate(S, TagSpec, TUK, KWLoc, SS,
- ClassTemplate->getIdentifier(),
- TemplateNameLoc,
- Attr,
- TemplateParams,
- AS_none, /*ModulePrivateLoc=*/SourceLocation(),
- /*FriendLoc*/SourceLocation(),
- TemplateParameterLists.size() - 1,
- TemplateParameterLists.data());
+ return CheckClassTemplate(
+ S, TagSpec, TUK, KWLoc, SS, ClassTemplate->getIdentifier(),
+ TemplateNameLoc, Attr, TemplateParams, AS_none,
+ /*ModulePrivateLoc=*/SourceLocation(),
+ /*FriendLoc*/ SourceLocation(), TemplateParameterLists.drop_back(),
+ isMemberSpecialization);
}
// Create a new class template partial specialization declaration node.
@@ -8326,6 +8317,11 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
ClassTemplatePartialSpecializationDecl::Create(
Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams,
ClassTemplate, CanonicalConverted, CanonType, PrevPartial);
+
+ // If we are providing an explicit specialization of a member class
+ // template specialization, make a note of that.
+ if (isMemberSpecialization)
+ Partial->setMemberSpecialization();
Partial->setTemplateArgsAsWritten(TemplateArgs);
SetNestedNameSpecifier(*this, Partial, SS);
if (TemplateParameterLists.size() > 1 && SS.isSet()) {
@@ -8337,11 +8333,6 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
ClassTemplate->AddPartialSpecialization(Partial, InsertPos);
Specialization = Partial;
- // If we are providing an explicit specialization of a member class
- // template specialization, make a note of that.
- if (PrevPartial && PrevPartial->getInstantiatedFromMember())
- PrevPartial->setMemberSpecialization();
-
CheckTemplatePartialSpecialization(Partial);
} else {
// Create a new class template specialization declaration node for
@@ -11139,8 +11130,8 @@ class ExplicitSpecializationVisibilityChecker {
template<typename TemplDecl>
void checkTemplate(TemplDecl *TD) {
- if (TD->isMemberSpecialization()) {
- if (!CheckMemberSpecialization(TD))
+ if (TD->getMostRecentDecl()->isMemberSpecialization()) {
+ if (!CheckMemberSpecialization(TD->getMostRecentDecl()))
diagnose(TD->getMostRecentDecl(), false);
}
}
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index cc095ae67ac400..ffe7225c9abd04 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3138,20 +3138,6 @@ template<>
struct IsPartialSpecialization<VarTemplatePartialSpecializationDecl> {
static constexpr bool value = true;
};
-template <typename TemplateDeclT>
-static bool DeducedArgsNeedReplacement(TemplateDeclT *Template) {
- return false;
-}
-template <>
-bool DeducedArgsNeedReplacement<VarTemplatePartialSpecializationDecl>(
- VarTemplatePartialSpecializationDecl *Spec) {
- return !Spec->isClassScopeExplicitSpecialization();
-}
-template <>
-bool DeducedArgsNeedReplacement<ClassTemplatePartialSpecializationDecl>(
- ClassTemplatePartialSpecializationDecl *Spec) {
- return !Spec->isClassScopeExplicitSpecialization();
-}
template <typename TemplateDeclT>
static TemplateDeductionResult
@@ -3162,23 +3148,10 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template,
llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
Template->getAssociatedConstraints(AssociatedConstraints);
- std::optional<ArrayRef<TemplateArgument>> Innermost;
- // If we don't need to replace the deduced template arguments,
- // we can add them immediately as the inner-most argument list.
- if (!DeducedArgsNeedReplacement(Template))
- Innermost = CanonicalDeducedArgs;
-
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
- Template, Template->getDeclContext(), /*Final=*/false, Innermost,
- /*RelativeToPrimary=*/true, /*Pattern=*/
- nullptr, /*ForConstraintInstantiation=*/true);
-
- // getTemplateInstantiationArgs picks up the non-deduced version of the
- // template args when this is a variable template partial specialization and
- // not class-scope explicit specialization, so replace with Deduced Args
- // instead of adding to inner-most.
- if (!Innermost)
- MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs);
+ Template, Template->getDeclContext(), /*Final=*/false,
+ /*Innermost=*/CanonicalDeducedArgs, /*RelativeToPrimary=*/true,
+ /*ForConstraintInstantiation=*/true);
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
Info.getLocation(),
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 545da21183c3c4..ca93c840f03215 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -765,7 +765,7 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
}
// Template arguments used to transform the template arguments in
// DeducedResults.
- SmallVector<TemplateArgument> TemplateArgsForBuildingRC(
+ SmallVector<TemplateArgument> InnerArgsForBuildingRC(
F->getTemplateParameters()->size());
// Transform the transformed template args
MultiLevelTemplateArgumentList Args;
@@ -778,33 +778,30 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
NamedDecl *TP = F->getTemplateParameters()->getParam(Index);
MultiLevelTemplateArgumentList Args;
Args.setKind(TemplateSubstitutionKind::Rewrite);
- Args.addOuterTemplateArguments(TemplateArgsForBuildingRC);
+ Args.addOuterTemplateArguments(InnerArgsForBuildingRC);
// Rebuild the template parameter with updated depth and index.
NamedDecl *NewParam =
transformTemplateParameter(SemaRef, F->getDeclContext(), TP, Args,
/*NewIndex=*/FirstUndeducedParamIdx,
getDepthAndIndex(TP).first + AdjustDepth);
FirstUndeducedParamIdx += 1;
- assert(TemplateArgsForBuildingRC[Index].isNull());
- TemplateArgsForBuildingRC[Index] =
- Context.getInjectedTemplateArg(NewParam);
+ assert(InnerArgsForBuildingRC[Index].isNull());
+ InnerArgsForBuildingRC[Index] = Context.getInjectedTemplateArg(NewParam);
continue;
}
TemplateArgumentLoc Input =
SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{});
TemplateArgumentLoc Output;
if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) {
- assert(TemplateArgsForBuildingRC[Index].isNull() &&
+ assert(InnerArgsForBuildingRC[Index].isNull() &&
"InstantiatedArgs must be null before setting");
- TemplateArgsForBuildingRC[Index] = Output.getArgument();
+ InnerArgsForBuildingRC[Index] = Output.getArgument();
}
}
- // A list of template arguments for transforming the require-clause of F.
- // It must contain the entire set of template argument lists.
- MultiLevelTemplateArgumentList ArgsForBuildingRC;
- ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite);
- ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC);
+ // A list of template arguments for transforming the require-clause using
+ // the transformed template arguments as the template argument list of F.
+ //
// For 2), if the underlying deduction guide F is nested in a class template,
// we need the entire template argument list, as the constraint AST in the
// require-clause of F remains completely uninstantiated.
@@ -827,25 +824,15 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
// - The occurrence of U in the function parameter is [depth:0, index:0]
// - The template parameter of U is [depth:0, index:0]
//
- // We add the outer template arguments which is [int] to the multi-level arg
- // list to ensure that the occurrence U in `C<U>` will be replaced with int
- // during the substitution.
- //
// NOTE: The underlying deduction guide F is instantiated -- either from an
// explicitly-written deduction guide member, or from a constructor.
- // getInstantiatedFromMemberTemplate() can only handle the former case, so we
- // check the DeclContext kind.
- if (F->getLexicalDeclContext()->getDeclKind() ==
- clang::Decl::ClassTemplateSpecialization) {
- auto OuterLevelArgs = SemaRef.getTemplateInstantiationArgs(
- F, F->getLexicalDeclContext(),
- /*Final=*/false, /*Innermost=*/std::nullopt,
- /*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
- /*ForConstraintInstantiation=*/true);
- for (auto It : OuterLevelArgs)
- ArgsForBuildingRC.addOuterTemplateArguments(It.Args);
- }
+ MultiLevelTemplateArgumentList ArgsForBuildingRC =
+ SemaRef.getTemplateInstantiationArgs(F, F->getLexicalDeclContext(),
+ /*Final=*/false,
+ /*Innermost=*/InnerArgsForBuildingRC,
+ /*RelativeToPrimary=*/true,
+ /*ForConstraintInstantiation=*/true);
+ ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite);
ExprResult E = SemaRef.SubstExpr(RC, ArgsForBuildingRC);
if (E.isInvalid())
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 55f38743e2768e..7481c700019dc8 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -52,38 +52,6 @@ using namespace sema;
//===----------------------------------------------------------------------===/
namespace {
-namespace TemplateInstArgsHelpers {
-struct Response {
- const Decl *NextDecl = nullptr;
- bool IsDone = false;
- bool ClearRelativeToPrimary = true;
- static Response Done() {
- Response R;
- R.IsDone = true;
- return R;
- }
- static Response ChangeDecl(const Decl *ND) {
- Response R;
- R.NextDecl = ND;
- return R;
- }
- static Response ChangeDecl(const DeclContext *Ctx) {
- Response R;
- R.NextDecl = Decl::castFromDeclContext(Ctx);
- return R;
- }
-
- static Response UseNextDecl(const Decl *CurDecl) {
- return ChangeDecl(CurDecl->getDeclContext());
- }
-
- static Response DontClearRelativeToPrimaryNextDecl(const Decl *CurDecl) {
- Response R = Response::UseNextDecl(CurDecl);
- R.ClearRelativeToPrimary = false;
- return R;
- }
-};
-
// Retrieve the primary template for a lambda call operator. It's
// unfortunate that we only have the mappings of call operators rather
// than lambda classes.
@@ -171,374 +139,397 @@ bool isLambdaEnclosedByTypeAliasDecl(
.TraverseType(Underlying);
}
-// Add template arguments from a variable template instantiation.
-Response
-HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec,
- MultiLevelTemplateArgumentList &Result,
- bool SkipForSpecialization) {
- // For a class-scope explicit specialization, there are no template arguments
- // at this level, but there may be enclosing template arguments.
- if (VarTemplSpec->isClassScopeExplicitSpecialization())
- return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec);
-
- // We're done when we hit an explicit specialization.
- if (VarTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization &&
- !isa<VarTemplatePartialSpecializationDecl>(VarTemplSpec))
- return Response::Done();
-
- // If this variable template specialization was instantiated from a
- // specialized member that is a variable template, we're done.
- assert(VarTemplSpec->getSpecializedTemplate() && "No variable template?");
- llvm::PointerUnion<VarTemplateDecl *, VarTemplatePartialSpecializationDecl *>
- Specialized = VarTemplSpec->getSpecializedTemplateOrPartial();
- if (VarTemplatePartialSpecializationDecl *Partial =
- Specialized.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
- if (!SkipForSpecialization)
- Result.addOuterTemplateArguments(
- Partial, VarTemplSpec->getTemplateInstantiationArgs().asArray(),
- /*Final=*/false);
- if (Partial->isMemberSpecialization())
- return Response::Done();
- } else {
- VarTemplateDecl *Tmpl = Specialized.get<VarTemplateDecl *>();
- if (!SkipForSpecialization)
- Result.addOuterTemplateArguments(
- Tmpl, VarTemplSpec->getTemplateInstantiationArgs().asArray(),
- /*Final=*/false);
- if (Tmpl->isMemberSpecialization())
- return Response::Done();
+struct TemplateInstantiationArgumentCollecter
+ : DeclVisitor<TemplateInstantiationArgumentCollecter, Decl *> {
+ Sema &S;
+ MultiLevelTemplateArgumentList &Result;
+ std::optional<ArrayRef<TemplateArgument>> Innermost;
+ bool RelativeToPrimary;
+ bool ForConstraintInstantiation;
+
+ TemplateInstantiationArgumentCollecter(
+ Sema &S, MultiLevelTemplateArgumentList &Result,
+ std::optional<ArrayRef<TemplateArgument>> Innermost,
+ bool RelativeToPrimary, bool ForConstraintInstantiation)
+ : S(S), Result(Result), Innermost(Innermost),
+ RelativeToPrimary(RelativeToPrimary),
+ ForConstraintInstantiation(ForConstraintInstantiation) {}
+
+ Decl *Done() { return nullptr; }
+
+ Decl *ChangeDecl(const Decl *D) {
+ RelativeToPrimary = false;
+ return const_cast<Decl *>(D);
}
- return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec);
-}
-// If we have a template template parameter with translation unit context,
-// then we're performing substitution into a default template argument of
-// this template template parameter before we've constructed the template
-// that will own this template template parameter. In this case, we
-// use empty template parameter lists for all of the outer templates
-// to avoid performing any substitutions.
-Response
-HandleDefaultTempArgIntoTempTempParam(const TemplateTemplateParmDecl *TTP,
- MultiLevelTemplateArgumentList &Result) {
- for (unsigned I = 0, N = TTP->getDepth() + 1; I != N; ++I)
- Result.addOuterTemplateArguments(std::nullopt);
- return Response::Done();
-}
+ Decl *ChangeDecl(const DeclContext *DC) {
+ return ChangeDecl(Decl::castFromDeclContext(DC));
+ }
-Response HandlePartialClassTemplateSpec(
- const ClassTemplatePartialSpecializationDecl *PartialClassTemplSpec,
- MultiLevelTemplateArgumentList &Result, bool SkipForSpecialization) {
- if (!SkipForSpecialization)
- Result.addOuterRetainedLevels(PartialClassTemplSpec->getTemplateDepth());
- return Response::Done();
-}
+ Decl *UseNextDecl(const Decl *D) { return ChangeDecl(D->getDeclContext()); }
-// Add template arguments from a class template instantiation.
-Response
-HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec,
- MultiLevelTemplateArgumentList &Result,
- bool SkipForSpecialization) {
- if (!ClassTemplSpec->isClassScopeExplicitSpecialization()) {
- // We're done when we hit an explicit specialization.
- if (ClassTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization &&
- !isa<ClassTemplatePartialSpecializationDecl>(ClassTemplSpec))
- return Response::Done();
+ void AddInnermostTemplateArguments(const Decl *D) {
+ assert(Innermost);
+ Result.addOuterTemplateArguments(const_cast<Decl *>(D), *Innermost,
+ /*Final=*/false);
+ Innermost.reset();
+ }
- if (!SkipForSpecialization)
- Result.addOuterTemplateArguments(
- const_cast<ClassTemplateSpecializationDecl *>(ClassTemplSpec),
- ClassTemplSpec->getTemplateInstantiationArgs().asArray(),
- /*Final=*/false);
+ void AddOuterTemplateArguments(const Decl *D, ArrayRef<TemplateArgument> Args,
+ bool Final) {
+ Result.addOuterTemplateArguments(const_cast<Decl *>(D), Args, Final);
+ }
- // If this class template specialization was instantiated from a
- // specialized member that is a class template, we're done.
- assert(ClassTemplSpec->getSpecializedTemplate() && "No class template?");
- if (ClassTemplSpec->getSpecializedTemplate()->isMemberSpecialization())
- return Response::Done();
-
- // If this was instantiated from a partial template specialization, we need
- // to get the next level of declaration context from the partial
- // specialization, as the ClassTemplateSpecializationDecl's
- // DeclContext/LexicalDeclContext will be for the primary template.
- if (auto *InstFromPartialTempl = ClassTemplSpec->getSpecializedTemplateOrPartial()
- .dyn_cast<ClassTemplatePartialSpecializationDecl *>())
- return Response::ChangeDecl(InstFromPartialTempl->getLexicalDeclContext());
+ Decl *VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *TTPD) {
+ if (Innermost)
+ AddInnermostTemplateArguments(TTPD);
+ else if (ForConstraintInstantiation)
+ AddOuterTemplateArguments(nullptr, std::nullopt, /*Final=*/false);
+
+ for (unsigned Depth = TTPD->getDepth() + 1; Depth--;)
+ AddOuterTemplateArguments(nullptr, std::nullopt, /*Final=*/false);
+
+ return Done();
}
- return Response::UseNextDecl(ClassTemplSpec);
-}
-Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
- MultiLevelTemplateArgumentList &Result,
- const FunctionDecl *Pattern, bool RelativeToPrimary,
- bool ForConstraintInstantiation,
- bool ForDefaultArgumentSubstitution) {
- // Add template arguments from a function template specialization.
- if (!RelativeToPrimary &&
- Function->getTemplateSpecializationKindForInstantiation() ==
- TSK_ExplicitSpecialization)
- return Response::Done();
-
- if (!RelativeToPrimary &&
- Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {
- // This is an implicit instantiation of an explicit specialization. We
- // don't get any template arguments from this function but might get
- // some from an enclosing template.
- return Response::UseNextDecl(Function);
- } else if (const TemplateArgumentList *TemplateArgs =
- Function->getTemplateSpecializationArgs()) {
- // Add the template arguments for this specialization.
- Result.addOuterTemplateArguments(const_cast<FunctionDecl *>(Function),
- TemplateArgs->asArray(),
- /*Final=*/false);
+ Decl *VisitFunctionTemplateDecl(FunctionTemplateDecl *FTD) {
+ assert(
+ (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
+ "outer template not instantiated?");
+
+ if (Innermost)
+ AddInnermostTemplateArguments(FTD);
+ else if (ForConstraintInstantiation)
+ AddOuterTemplateArguments(FTD, FTD->getInjectedTemplateArgs(),
+ /*Final=*/false);
+
+ if (FTD->isMemberSpecialization())
+ return Done();
+
+ if (FTD->getFriendObjectKind())
+ return ChangeDecl(FTD->getLexicalDeclContext());
+ return UseNextDecl(FTD);
+ }
- if (RelativeToPrimary &&
- (Function->getTemplateSpecializationKind() ==
- TSK_ExplicitSpecialization ||
- (Function->getFriendObjectKind() &&
- !Function->getPrimaryTemplate()->getFriendObjectKind())))
- return Response::UseNextDecl(Function);
-
- // If this function was instantiated from a specialized member that is
- // a function template, we're done.
- assert(Function->getPrimaryTemplate() && "No function template?");
- if (!ForDefaultArgumentSubstitution &&
- Function->getPrimaryTemplate()->isMemberSpecialization())
- return Response::Done();
-
- // If this function is a generic lambda specialization, we are done.
- if (!ForConstraintInstantiation &&
- isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
- return Response::Done();
-
- } else if (Function->getDescribedFunctionTemplate()) {
+ Decl *VisitVarTemplateDecl(VarTemplateDecl *VTD) {
assert(
(ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
- "Outer template not instantiated?");
+ "outer template not instantiated?");
+
+ if (Innermost)
+ AddInnermostTemplateArguments(VTD);
+ else if (ForConstraintInstantiation)
+ AddOuterTemplateArguments(VTD, VTD->getInjectedTemplateArgs(),
+ /*Final=*/false);
+
+ if (VTD->isMemberSpecialization())
+ return Done();
+
+ return UseNextDecl(VTD);
}
- // If this is a friend or local declaration and it declares an entity at
- // namespace scope, take arguments from its lexical parent
- // instead of its semantic parent, unless of course the pattern we're
- // instantiating actually comes from the file's context!
- if ((Function->getFriendObjectKind() || Function->isLocalExternDecl()) &&
- Function->getNonTransparentDeclContext()->isFileContext() &&
- (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) {
- return Response::ChangeDecl(Function->getLexicalDeclContext());
+
+ Decl *VisitVarTemplatePartialSpecializationDecl(
+ VarTemplatePartialSpecializationDecl *VTPSD) {
+ assert(
+ (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
+ "outer template not instantiated?");
+
+ if (Innermost)
+ AddInnermostTemplateArguments(VTPSD);
+ else if (ForConstraintInstantiation)
+ AddOuterTemplateArguments(VTPSD, VTPSD->getTemplateArgs().asArray(),
+ /*Final=*/false);
+
+ if (VTPSD->isMemberSpecialization())
+ return Done();
+
+ return UseNextDecl(VTPSD);
}
- if (ForConstraintInstantiation && Function->getFriendObjectKind())
- return Response::ChangeDecl(Function->getLexicalDeclContext());
- return Response::UseNextDecl(Function);
-}
+ Decl *VisitClassTemplateDecl(ClassTemplateDecl *CTD) {
+ assert(
+ (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
+ "outer template not instantiated?");
-Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD,
- MultiLevelTemplateArgumentList &Result) {
- if (!isa<ClassTemplateSpecializationDecl>(FTD->getDeclContext())) {
- Result.addOuterTemplateArguments(
- const_cast<FunctionTemplateDecl *>(FTD),
- const_cast<FunctionTemplateDecl *>(FTD)->getInjectedTemplateArgs(),
- /*Final=*/false);
-
- NestedNameSpecifier *NNS = FTD->getTemplatedDecl()->getQualifier();
-
- while (const Type *Ty = NNS ? NNS->getAsType() : nullptr) {
- if (NNS->isInstantiationDependent()) {
- if (const auto *TSTy = Ty->getAs<TemplateSpecializationType>()) {
- ArrayRef<TemplateArgument> Arguments = TSTy->template_arguments();
- // Prefer template arguments from the injected-class-type if possible.
- // For example,
- // ```cpp
- // template <class... Pack> struct S {
- // template <class T> void foo();
- // };
- // template <class... Pack> template <class T>
- // ^^^^^^^^^^^^^ InjectedTemplateArgs
- // They're of kind TemplateArgument::Pack, not of
- // TemplateArgument::Type.
- // void S<Pack...>::foo() {}
- // ^^^^^^^
- // TSTy->template_arguments() (which are of PackExpansionType)
- // ```
- // This meets the contract in
- // TreeTransform::TryExpandParameterPacks that the template arguments
- // for unexpanded parameters should be of a Pack kind.
- if (TSTy->isCurrentInstantiation()) {
- auto *RD = TSTy->getCanonicalTypeInternal()->getAsCXXRecordDecl();
- if (ClassTemplateDecl *CTD = RD->getDescribedClassTemplate())
- Arguments = CTD->getInjectedTemplateArgs();
- else if (auto *Specialization =
- dyn_cast<ClassTemplateSpecializationDecl>(RD))
- Arguments =
- Specialization->getTemplateInstantiationArgs().asArray();
- }
- Result.addOuterTemplateArguments(
- const_cast<FunctionTemplateDecl *>(FTD), Arguments,
- /*Final=*/false);
- }
- }
+ if (Innermost)
+ AddInnermostTemplateArguments(CTD);
+ else if (ForConstraintInstantiation)
+ AddOuterTemplateArguments(CTD, CTD->getInjectedTemplateArgs(),
+ /*Final=*/false);
- NNS = NNS->getPrefix();
- }
+ if (CTD->isMemberSpecialization())
+ return Done();
+
+ if (CTD->getFriendObjectKind())
+ return ChangeDecl(CTD->getLexicalDeclContext());
+ return UseNextDecl(CTD);
}
- return Response::ChangeDecl(FTD->getLexicalDeclContext());
-}
+ Decl *VisitClassTemplatePartialSpecializationDecl(
+ ClassTemplatePartialSpecializationDecl *CTPSD) {
+ assert(
+ (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
+ "outer template not instantiated?");
-Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
- MultiLevelTemplateArgumentList &Result,
- ASTContext &Context,
- bool ForConstraintInstantiation) {
- if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) {
+ if (Innermost)
+ AddInnermostTemplateArguments(CTPSD);
+ else if (ForConstraintInstantiation)
+ AddOuterTemplateArguments(CTPSD, CTPSD->getTemplateArgs().asArray(),
+ /*Final=*/false);
+
+ if (CTPSD->isMemberSpecialization())
+ return Done();
+
+ return UseNextDecl(CTPSD);
+ }
+
+ Decl *VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *TATD) {
assert(
(ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
- "Outer template not instantiated?");
- if (ClassTemplate->isMemberSpecialization())
- return Response::Done();
- if (ForConstraintInstantiation)
- Result.addOuterTemplateArguments(const_cast<CXXRecordDecl *>(Rec),
- ClassTemplate->getInjectedTemplateArgs(),
- /*Final=*/false);
+ "outer template not instantiated?");
+ if (Innermost)
+ AddInnermostTemplateArguments(TATD);
+ else if (ForConstraintInstantiation)
+ AddOuterTemplateArguments(TATD, TATD->getInjectedTemplateArgs(),
+ /*Final=*/false);
+
+ return UseNextDecl(TATD);
}
- if (const MemberSpecializationInfo *MSInfo =
- Rec->getMemberSpecializationInfo())
- if (MSInfo->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
- return Response::Done();
-
- bool IsFriend = Rec->getFriendObjectKind() ||
- (Rec->getDescribedClassTemplate() &&
- Rec->getDescribedClassTemplate()->getFriendObjectKind());
- if (ForConstraintInstantiation && IsFriend &&
- Rec->getNonTransparentDeclContext()->isFileContext()) {
- return Response::ChangeDecl(Rec->getLexicalDeclContext());
+ Decl *VisitConceptDecl(ConceptDecl *CD) {
+ assert(
+ (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
+ "outer template not instantiated?");
+ if (Innermost)
+ AddInnermostTemplateArguments(CD);
+
+ return UseNextDecl(CD);
}
- // This is to make sure we pick up the VarTemplateSpecializationDecl or the
- // TypeAliasTemplateDecl that this lambda is defined inside of.
- if (Rec->isLambda()) {
- if (const Decl *LCD = Rec->getLambdaContextDecl())
- return Response::ChangeDecl(LCD);
- // Retrieve the template arguments for a using alias declaration.
- // This is necessary for constraint checking, since we always keep
- // constraints relative to the primary template.
- if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef);
- ForConstraintInstantiation && TypeAlias) {
- if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(),
- TypeAlias.PrimaryTypeAliasDecl)) {
- Result.addOuterTemplateArguments(TypeAlias.Template,
- TypeAlias.AssociatedTemplateArguments,
- /*Final=*/false);
- // Visit the parent of the current type alias declaration rather than
- // the lambda thereof.
- // E.g., in the following example:
- // struct S {
- // template <class> using T = decltype([]<Concept> {} ());
- // };
- // void foo() {
- // S::T var;
- // }
- // The instantiated lambda expression (which we're visiting at 'var')
- // has a function DeclContext 'foo' rather than the Record DeclContext
- // S. This seems to be an oversight to me that we may want to set a
- // Sema Context from the CXXScopeSpec before substituting into T.
- return Response::ChangeDecl(TypeAlias.Template->getDeclContext());
+ Decl *VisitFunctionDecl(FunctionDecl *FD) {
+ assert(!FD->getDescribedFunctionTemplate() &&
+ "not for templated declarations");
+
+ if (!RelativeToPrimary) {
+ // Add template arguments from a function template specialization.
+ if (const MemberSpecializationInfo *MSI =
+ FD->getMemberSpecializationInfo();
+ MSI &&
+ MSI->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+ return Done();
+
+ // This is an implicit instantiation of an explicit specialization. We
+ // don't get any template arguments from this function but might get
+ // some from an enclosing template.
+ if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+ return UseNextDecl(FD);
+ }
+
+ if (const TemplateArgumentList *TemplateArgs =
+ FD->getTemplateSpecializationArgs()) {
+ // Add the template arguments for this specialization.
+ if (Innermost)
+ AddInnermostTemplateArguments(FD);
+ else
+ AddOuterTemplateArguments(FD, TemplateArgs->asArray(), /*Final=*/false);
+
+ if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization ||
+ (FD->getFriendObjectKind() &&
+ !FD->getPrimaryTemplate()->getFriendObjectKind()))
+ return UseNextDecl(FD);
+
+ // If this function was instantiated from a specialized member that is
+ // a function template, we're done.
+ assert(FD->getPrimaryTemplate() && "No function template?");
+ if (FD->getPrimaryTemplate()->isMemberSpecialization())
+ return Done();
+
+ // If this function is a generic lambda specialization, we are done.
+ if (!ForConstraintInstantiation &&
+ isGenericLambdaCallOperatorOrStaticInvokerSpecialization(FD))
+ return Done();
+ }
+
+ // If this is a friend or local declaration and it declares an entity at
+ // namespace scope, take arguments from its lexical parent
+ // instead of its semantic parent, unless of course the pattern we're
+ // instantiating actually comes from the file's context!
+ if ((FD->getFriendObjectKind() || FD->isLocalExternDecl()) &&
+ FD->getNonTransparentDeclContext()->isFileContext()) {
+ return ChangeDecl(FD->getLexicalDeclContext());
+ }
+
+ if (ForConstraintInstantiation && FD->getFriendObjectKind())
+ return ChangeDecl(FD->getLexicalDeclContext());
+ return UseNextDecl(FD);
+ }
+
+ Decl *VisitCXXRecordDecl(CXXRecordDecl *RD) {
+ assert(!RD->getDescribedClassTemplate() &&
+ "not for templated declarations");
+
+ if (const MemberSpecializationInfo *MSI = RD->getMemberSpecializationInfo();
+ MSI &&
+ MSI->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+ return Done();
+
+ if (ForConstraintInstantiation && RD->getFriendObjectKind() &&
+ RD->getNonTransparentDeclContext()->isFileContext()) {
+ return ChangeDecl(RD->getLexicalDeclContext());
+ }
+
+ // This is to make sure we pick up the VarTemplateSpecializationDecl or the
+ // TypeAliasTemplateDecl that this lambda is defined inside of.
+ if (RD->isLambda()) {
+ if (Decl *LCD = RD->getLambdaContextDecl())
+ return ChangeDecl(LCD);
+ // Retrieve the template arguments for a using alias declaration.
+ // This is necessary for constraint checking, since we always keep
+ // constraints relative to the primary template.
+ if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(S);
+ ForConstraintInstantiation && TypeAlias) {
+ if (isLambdaEnclosedByTypeAliasDecl(RD->getLambdaCallOperator(),
+ TypeAlias.PrimaryTypeAliasDecl)) {
+ AddOuterTemplateArguments(TypeAlias.Template,
+ TypeAlias.AssociatedTemplateArguments,
+ /*Final=*/false);
+ // Visit the parent of the current type alias declaration rather than
+ // the lambda thereof.
+ // E.g., in the following example:
+ // struct S {
+ // template <class> using T = decltype([]<Concept> {} ());
+ // };
+ // void foo() {
+ // S::T var;
+ // }
+ // The instantiated lambda expression (which we're visiting at 'var')
+ // has a function DeclContext 'foo' rather than the Record DeclContext
+ // S. This seems to be an oversight to me that we may want to set a
+ // Sema Context from the CXXScopeSpec before substituting into T.
+ return ChangeDecl(TypeAlias.Template->getDeclContext());
+ }
}
}
+
+ return UseNextDecl(RD);
}
- return Response::UseNextDecl(Rec);
-}
+ Decl *
+ VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *CTSD) {
+ // For a class-scope explicit specialization, there are no template
+ // arguments at this level, but there may be enclosing template arguments.
+ if (CTSD->isClassScopeExplicitSpecialization())
+ return UseNextDecl(CTSD);
-Response HandleImplicitConceptSpecializationDecl(
- const ImplicitConceptSpecializationDecl *CSD,
- MultiLevelTemplateArgumentList &Result) {
- Result.addOuterTemplateArguments(
- const_cast<ImplicitConceptSpecializationDecl *>(CSD),
- CSD->getTemplateArguments(),
- /*Final=*/false);
- return Response::UseNextDecl(CSD);
-}
+ // We're done when we hit an explicit specialization.
+ if (CTSD->getSpecializationKind() == TSK_ExplicitSpecialization)
+ return Done();
+
+ if (Innermost)
+ AddInnermostTemplateArguments(CTSD);
+ else
+ AddOuterTemplateArguments(CTSD,
+ CTSD->getTemplateInstantiationArgs().asArray(),
+ /*Final=*/false);
+
+ // If this class template specialization was instantiated from a
+ // specialized member that is a class template, we're done.
+ assert(CTSD->getSpecializedTemplate() && "No class template?");
+ llvm::PointerUnion<ClassTemplateDecl *,
+ ClassTemplatePartialSpecializationDecl *>
+ Specialized = CTSD->getSpecializedTemplateOrPartial();
+
+ if (auto *CTPSD =
+ Specialized.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
+ if (CTPSD->isMemberSpecialization())
+ return Done();
+ } else {
+ auto *CTD = Specialized.get<ClassTemplateDecl *>();
+ if (CTD->isMemberSpecialization())
+ return Done();
+ }
+ return UseNextDecl(CTSD);
+ }
+
+ Decl *
+ VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *VTSD) {
+ // For a class-scope explicit specialization, there are no template
+ // arguments at this level, but there may be enclosing template arguments.
+ if (VTSD->isClassScopeExplicitSpecialization())
+ return UseNextDecl(VTSD);
+
+ // We're done when we hit an explicit specialization.
+ if (VTSD->getSpecializationKind() == TSK_ExplicitSpecialization)
+ return Done();
+
+ if (Innermost)
+ AddInnermostTemplateArguments(VTSD);
+ else
+ AddOuterTemplateArguments(VTSD,
+ VTSD->getTemplateInstantiationArgs().asArray(),
+ /*Final=*/false);
+
+ // If this variable template specialization was instantiated from a
+ // specialized member that is a variable template, we're done.
+ assert(VTSD->getSpecializedTemplate() && "No variable template?");
+ llvm::PointerUnion<VarTemplateDecl *,
+ VarTemplatePartialSpecializationDecl *>
+ Specialized = VTSD->getSpecializedTemplateOrPartial();
+ if (auto *VTPSD =
+ Specialized.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
+ if (VTPSD->isMemberSpecialization())
+ return Done();
+ } else {
+ auto *VTD = Specialized.get<VarTemplateDecl *>();
+ if (VTD->isMemberSpecialization())
+ return Done();
+ }
+ return UseNextDecl(VTSD);
+ }
+
+ Decl *VisitImplicitConceptSpecializationDecl(
+ ImplicitConceptSpecializationDecl *ICSD) {
+ AddOuterTemplateArguments(ICSD, ICSD->getTemplateArguments(),
+ /*Final=*/false);
+ return UseNextDecl(ICSD);
+ }
+
+ Decl *VisitDecl(Decl *D) {
+ if (D->isFileContextDecl())
+ return Done();
+
+ if (isa<DeclContext>(D))
+ RelativeToPrimary = false;
+
+ return UseNextDecl(D);
+ }
+
+ Decl *Visit(Decl *D) {
+ if (TemplateDecl *TD = D->getDescribedTemplate())
+ D = TD;
+ return DeclVisitor::Visit(D);
+ }
+};
-Response HandleGenericDeclContext(const Decl *CurDecl) {
- return Response::UseNextDecl(CurDecl);
-}
-} // namespace TemplateInstArgsHelpers
} // namespace
MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
const NamedDecl *ND, const DeclContext *DC, bool Final,
std::optional<ArrayRef<TemplateArgument>> Innermost, bool RelativeToPrimary,
- const FunctionDecl *Pattern, bool ForConstraintInstantiation,
- bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) {
+ bool ForConstraintInstantiation) {
assert((ND || DC) && "Can't find arguments for a decl if one isn't provided");
// Accumulate the set of template argument lists in this structure.
MultiLevelTemplateArgumentList Result;
-
- using namespace TemplateInstArgsHelpers;
const Decl *CurDecl = ND;
if (!CurDecl)
CurDecl = Decl::castFromDeclContext(DC);
- if (Innermost) {
- Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND), *Innermost,
- Final);
- // Populate placeholder template arguments for TemplateTemplateParmDecls.
- // This is essential for the case e.g.
- //
- // template <class> concept Concept = false;
- // template <template <Concept C> class T> void foo(T<int>)
- //
- // where parameter C has a depth of 1 but the substituting argument `int`
- // has a depth of 0.
- if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl))
- HandleDefaultTempArgIntoTempTempParam(TTP, Result);
- CurDecl = Response::UseNextDecl(CurDecl).NextDecl;
- }
-
- while (!CurDecl->isFileContextDecl()) {
- Response R;
- if (const auto *VarTemplSpec =
- dyn_cast<VarTemplateSpecializationDecl>(CurDecl)) {
- R = HandleVarTemplateSpec(VarTemplSpec, Result, SkipForSpecialization);
- } else if (const auto *PartialClassTemplSpec =
- dyn_cast<ClassTemplatePartialSpecializationDecl>(CurDecl)) {
- R = HandlePartialClassTemplateSpec(PartialClassTemplSpec, Result,
- SkipForSpecialization);
- } else if (const auto *ClassTemplSpec =
- dyn_cast<ClassTemplateSpecializationDecl>(CurDecl)) {
- R = HandleClassTemplateSpec(ClassTemplSpec, Result,
- SkipForSpecialization);
- } else if (const auto *Function = dyn_cast<FunctionDecl>(CurDecl)) {
- R = HandleFunction(*this, Function, Result, Pattern, RelativeToPrimary,
- ForConstraintInstantiation,
- ForDefaultArgumentSubstitution);
- } else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
- R = HandleRecordDecl(*this, Rec, Result, Context,
- ForConstraintInstantiation);
- } else if (const auto *CSD =
- dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) {
- R = HandleImplicitConceptSpecializationDecl(CSD, Result);
- } else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(CurDecl)) {
- R = HandleFunctionTemplateDecl(FTD, Result);
- } else if (const auto *CTD = dyn_cast<ClassTemplateDecl>(CurDecl)) {
- R = Response::ChangeDecl(CTD->getLexicalDeclContext());
- } else if (!isa<DeclContext>(CurDecl)) {
- R = Response::DontClearRelativeToPrimaryNextDecl(CurDecl);
- if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl)) {
- R = HandleDefaultTempArgIntoTempTempParam(TTP, Result);
- }
- } else {
- R = HandleGenericDeclContext(CurDecl);
- }
-
- if (R.IsDone)
- return Result;
- if (R.ClearRelativeToPrimary)
- RelativeToPrimary = false;
- assert(R.NextDecl);
- CurDecl = R.NextDecl;
- }
-
+ TemplateInstantiationArgumentCollecter Collecter(
+ *this, Result, Innermost, RelativeToPrimary, ForConstraintInstantiation);
+ do {
+ CurDecl = Collecter.Visit(const_cast<Decl *>(CurDecl));
+ } while (CurDecl);
return Result;
}
@@ -1657,10 +1648,8 @@ namespace {
CXXRecordDecl::LambdaDependencyKind
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
- if (auto TypeAlias =
- TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
- getSema());
- TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl(
+ if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(getSema());
+ TypeAlias && isLambdaEnclosedByTypeAliasDecl(
LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) {
unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth();
if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
@@ -1698,8 +1687,8 @@ namespace {
// RecoveryExpr that wraps the uninstantiated default argument so
// that downstream diagnostics are omitted.
ExprResult ErrorResult = SemaRef.CreateRecoveryExpr(
- UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(),
- { UninstExpr }, UninstExpr->getType());
+ UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), {UninstExpr},
+ UninstExpr->getType());
if (ErrorResult.isUsable())
PVD->setDefaultArg(ErrorResult.get());
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e055c87e783813..73d374f4aa1b12 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -12,6 +12,7 @@
#include "TreeTransform.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTLambda.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclVisitor.h"
@@ -4686,6 +4687,36 @@ bool Sema::InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD,
ParmVarDecl *Param) {
assert(Param->hasUninstantiatedDefaultArg());
+ NamedDecl *Pattern = FD;
+ std::optional<ArrayRef<TemplateArgument>> Innermost;
+
+ // C++ [dcl.fct.default]p4
+ // For non-template functions, default arguments can be added in later
+ // declarations of a function that inhabit the same scope.
+ //
+ // C++ [dcl.fct.default]p6
+ // Except for member functions of templated classes, the default arguments
+ // in a member function definition that appears outside of the class
+ // definition are added to the set of default arguments provided by the
+ // member function declaration in the class definition; the program is
+ // ill-formed if a default constructor, copy or move constructor, or copy
+ // or move assignment operator is so declared. Default arguments for a
+ // member function of a templated class shall be specified on the initial
+ // declaration of the member function within the templated class.
+ //
+ // We need to collect the template arguments from the context of the function
+ // where the default argument was defined. For a specialization of a function
+ // template explicitly specialized for an implicit instantiation of a class
+ // template, that context is the (implicitly instantiated) declaration in the
+ // definition of the class template specialization.
+ if (FD->isCXXClassMember() &&
+ !isGenericLambdaCallOperatorOrStaticInvokerSpecialization(FD)) {
+ if (FunctionTemplateDecl *FTD = FD->getPrimaryTemplate()) {
+ Pattern = FTD->getFirstDecl();
+ Innermost = FD->getTemplateSpecializationArgs()->asArray();
+ }
+ }
+
// Instantiate the expression.
//
// FIXME: Pass in a correct Pattern argument, otherwise
@@ -4703,12 +4734,10 @@ bool Sema::InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD,
//
// template<typename T>
// A<T> Foo(int a = A<T>::FooImpl());
- MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
- FD, FD->getLexicalDeclContext(),
- /*Final=*/false, /*Innermost=*/std::nullopt,
- /*RelativeToPrimary=*/true, /*Pattern=*/nullptr,
- /*ForConstraintInstantiation=*/false, /*SkipForSpecialization=*/false,
- /*ForDefaultArgumentSubstitution=*/true);
+ MultiLevelTemplateArgumentList TemplateArgs =
+ getTemplateInstantiationArgs(Pattern, Pattern->getLexicalDeclContext(),
+ /*Final=*/false, Innermost,
+ /*RelativeToPrimary=*/true);
if (SubstDefaultArgument(CallLoc, Param, TemplateArgs, /*ForCallExpr*/ true))
return true;
@@ -4749,7 +4778,7 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
MultiLevelTemplateArgumentList TemplateArgs =
getTemplateInstantiationArgs(Decl, Decl->getLexicalDeclContext(),
/*Final=*/false, /*Innermost=*/std::nullopt,
- /*RelativeToPrimary*/ true);
+ /*RelativeToPrimary=*/true);
// FIXME: We can't use getTemplateInstantiationPattern(false) in general
// here, because for a non-defining friend declaration in a class template,
@@ -5187,8 +5216,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
SetDeclDefaulted(Function, PatternDecl->getLocation());
} else {
MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
- Function, Function->getLexicalDeclContext(), /*Final=*/false,
- /*Innermost=*/std::nullopt, false, PatternDecl);
+ Function, Function->getLexicalDeclContext());
// Substitute into the qualifier; we can get a substitution failure here
// through evil use of alias templates.
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 7efcc81e194d95..f0af40cc172145 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -9973,7 +9973,8 @@ void ASTReader::finishPendingActions() {
auto RTD = cast<RedeclarableTemplateDecl>(D)->getCanonicalDecl();
for (auto *R = getMostRecentExistingDecl(RTD); R; R = R->getPreviousDecl())
- cast<RedeclarableTemplateDecl>(R)->Common = RTD->Common;
+ cast<RedeclarableTemplateDecl>(R)->setCommonPtr(
+ RTD->getCommonPtrInternal());
}
PendingDefinitions.clear();
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 9272e23c7da3fc..321e65fd2b094f 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2416,11 +2416,13 @@ ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
// Make sure we've allocated the Common pointer first. We do this before
// VisitTemplateDecl so that getCommonPtr() can be used during initialization.
RedeclarableTemplateDecl *CanonD = D->getCanonicalDecl();
- if (!CanonD->Common) {
- CanonD->Common = CanonD->newCommon(Reader.getContext());
+ if (!CanonD->getCommonPtrInternal()) {
+ CanonD->setCommonPtr(CanonD->newCommon(Reader.getContext()));
Reader.PendingDefinitions.insert(CanonD);
}
- D->Common = CanonD->Common;
+ D->setCommonPtr(CanonD->getCommonPtrInternal());
+ if (Record.readInt())
+ D->setMemberSpecialization();
// If this is the first declaration of the template, fill in the information
// for the 'common' pointer.
@@ -2429,8 +2431,6 @@ ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
assert(RTD->getKind() == D->getKind() &&
"InstantiatedFromMemberTemplate kind mismatch");
D->setInstantiatedFromMemberTemplate(RTD);
- if (Record.readInt())
- D->setMemberSpecialization();
}
}
@@ -2562,12 +2562,12 @@ void ASTDeclReader::VisitClassTemplatePartialSpecializationDecl(
D->TemplateParams = Params;
RedeclarableResult Redecl = VisitClassTemplateSpecializationDeclImpl(D);
+ D->InstantiatedFromMember.setInt(Record.readInt());
// These are read/set from/to the first declaration.
if (ThisDeclID == Redecl.getFirstID()) {
D->InstantiatedFromMember.setPointer(
- readDeclAs<ClassTemplatePartialSpecializationDecl>());
- D->InstantiatedFromMember.setInt(Record.readInt());
+ readDeclAs<ClassTemplatePartialSpecializationDecl>());
}
}
@@ -2660,12 +2660,12 @@ void ASTDeclReader::VisitVarTemplatePartialSpecializationDecl(
D->TemplateParams = Params;
RedeclarableResult Redecl = VisitVarTemplateSpecializationDeclImpl(D);
+ D->InstantiatedFromMember.setInt(Record.readInt());
// These are read/set from/to the first declaration.
if (ThisDeclID == Redecl.getFirstID()) {
D->InstantiatedFromMember.setPointer(
readDeclAs<VarTemplatePartialSpecializationDecl>());
- D->InstantiatedFromMember.setInt(Record.readInt());
}
}
@@ -2888,7 +2888,7 @@ void ASTDeclReader::mergeRedeclarableTemplate(RedeclarableTemplateDecl *D,
// If we merged the template with a prior declaration chain, merge the
// common pointer.
// FIXME: Actually merge here, don't just overwrite.
- D->Common = D->getCanonicalDecl()->Common;
+ D->setCommonPtr(D->getCanonicalDecl()->getCommonPtrInternal());
}
/// "Cast" to type T, asserting if we don't have an implicit conversion.
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 555f6325da646b..020f82ef3ac47f 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1707,14 +1707,13 @@ void ASTDeclWriter::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) {
void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
VisitRedeclarable(D);
+ Record.push_back(D->isMemberSpecialization());
+
// Emit data to initialize CommonOrPrev before VisitTemplateDecl so that
// getCommonPtr() can be used while this is still initializing.
- if (D->isFirstDecl()) {
+ if (D->isFirstDecl())
// This declaration owns the 'common' pointer, so serialize that data now.
Record.AddDeclRef(D->getInstantiatedFromMemberTemplate());
- if (D->getInstantiatedFromMemberTemplate())
- Record.push_back(D->isMemberSpecialization());
- }
VisitTemplateDecl(D);
Record.push_back(D->getIdentifierNamespace());
@@ -1788,11 +1787,10 @@ void ASTDeclWriter::VisitClassTemplatePartialSpecializationDecl(
VisitClassTemplateSpecializationDecl(D);
+ Record.push_back(D->isMemberSpecialization());
// These are read/set from/to the first declaration.
- if (D->getPreviousDecl() == nullptr) {
+ if (D->isFirstDecl())
Record.AddDeclRef(D->getInstantiatedFromMember());
- Record.push_back(D->isMemberSpecialization());
- }
Code = serialization::DECL_CLASS_TEMPLATE_PARTIAL_SPECIALIZATION;
}
@@ -1856,12 +1854,11 @@ void ASTDeclWriter::VisitVarTemplatePartialSpecializationDecl(
Record.AddTemplateParameterList(D->getTemplateParameters());
VisitVarTemplateSpecializationDecl(D);
+ Record.push_back(D->isMemberSpecialization());
// These are read/set from/to the first declaration.
- if (D->getPreviousDecl() == nullptr) {
+ if (D->isFirstDecl())
Record.AddDeclRef(D->getInstantiatedFromMember());
- Record.push_back(D->isMemberSpecialization());
- }
Code = serialization::DECL_VAR_TEMPLATE_PARTIAL_SPECIALIZATION;
}
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp
new file mode 100644
index 00000000000000..70064f867e18e3
--- /dev/null
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp
@@ -0,0 +1,175 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+// expected-no-diagnostics
+
+template<typename T>
+concept D = true;
+
+template<typename T>
+struct A {
+ template<typename U, bool V>
+ void f() requires V;
+
+ template<>
+ void f<short, true>();
+
+ template<D U>
+ void g();
+
+ template<typename U, bool V> requires V
+ struct B;
+
+ template<typename U, bool V> requires V
+ struct B<U*, V>;
+
+ template<>
+ struct B<short, true>;
+
+ template<D U>
+ struct C;
+
+ template<D U>
+ struct C<U*>;
+
+ template<typename U, bool V> requires V
+ static int x;
+
+ template<typename U, bool V> requires V
+ static int x<U*, V>;
+
+ template<>
+ int x<short, true>;
+
+ template<D U>
+ static int y;
+
+ template<D U>
+ static int y<U*>;
+};
+
+template<typename T>
+template<typename U, bool V>
+void A<T>::f() requires V { }
+
+template<typename T>
+template<D U>
+void A<T>::g() { }
+
+template<typename T>
+template<typename U, bool V> requires V
+struct A<T>::B { };
+
+template<typename T>
+template<typename U, bool V> requires V
+struct A<T>::B<U*, V> { };
+
+template<typename T>
+template<typename U, bool V> requires V
+struct A<T>::B<U&, V> { };
+
+template<typename T>
+template<D U>
+struct A<T>::C { };
+
+template<typename T>
+template<D U>
+struct A<T>::C<U*> { };
+
+template<typename T>
+template<typename U, bool V> requires V
+int A<T>::x = 0;
+
+template<typename T>
+template<typename U, bool V> requires V
+int A<T>::x<U*, V> = 0;
+
+template<typename T>
+template<typename U, bool V> requires V
+int A<T>::x<U&, V> = 0;
+
+template<typename T>
+template<D U>
+int A<T>::y = 0;
+
+template<typename T>
+template<D U>
+int A<T>::y<U*> = 0;
+
+template<>
+template<typename U, bool V>
+void A<short>::f() requires V;
+
+template<>
+template<>
+void A<short>::f<int, true>();
+
+template<>
+template<>
+void A<void>::f<int, true>();
+
+template<>
+template<D U>
+void A<short>::g();
+
+template<>
+template<typename U, bool V> requires V
+struct A<int>::B;
+
+template<>
+template<>
+struct A<int>::B<int, true>;
+
+template<>
+template<>
+struct A<void>::B<int, true>;
+
+template<>
+template<typename U, bool V> requires V
+struct A<int>::B<U*, V>;
+
+template<>
+template<typename U, bool V> requires V
+struct A<int>::B<U&, V>;
+
+template<>
+template<D U>
+struct A<int>::C;
+
+template<>
+template<D U>
+struct A<int>::C<U*>;
+
+template<>
+template<D U>
+struct A<int>::C<U&>;
+
+template<>
+template<typename U, bool V> requires V
+int A<long>::x;
+
+template<>
+template<>
+int A<long>::x<int, true>;
+
+template<>
+template<>
+int A<void>::x<int, true>;
+
+template<>
+template<typename U, bool V> requires V
+int A<long>::x<U*, V>;
+
+template<>
+template<typename U, bool V> requires V
+int A<long>::x<U&, V>;
+
+template<>
+template<D U>
+int A<long>::y;
+
+template<>
+template<D U>
+int A<long>::y<U*>;
+
+template<>
+template<D U>
+int A<long>::y<U&>;
More information about the cfe-commits
mailing list