Reapply "[Clang][Sema] Refactor collection of multi-level template argument lists (#106585, #111173)" (#111852)

This patch reapplies #111173, fixing a bug when instantiating dependent
expressions that name a member template that is later explicitly
specialized for a class specialization that is implicitly instantiated.

The bug is addressed by adding the `hasMemberSpecialization` function,
which return `true` if _any_ redeclaration is a member specialization.
This is then used when determining the instantiation pattern for a
specialization of a template, and when collecting template arguments for
a specialization of a template.
This commit is contained in:
Krystian Stasiowski 2024-10-11 12:08:06 -06:00 committed by GitHub
parent 48bda00b28
commit 2bb3d3a3f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 1075 additions and 713 deletions

View File

@ -502,6 +502,9 @@ Bug Fixes to C++ Support
in certain friend declarations. (#GH93099)
- Clang now instantiates the correct lambda call operator when a lambda's class type is
merged across modules. (#GH110401)
- 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)
- Fix a crash when parsing a pseudo destructor involving an invalid type. (#GH111460)
- Fixed an assertion failure when invoking recovery call expressions with explicit attributes
and undeclared templates. (#GH107047, #GH49093)

View File

@ -781,15 +781,11 @@ protected:
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 @@ protected:
};
/// 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,22 @@ public:
/// template<> template<typename T>
/// struct X<int>::Inner { /* ... */ };
/// \endcode
bool isMemberSpecialization() const {
return getCommonPtr()->InstantiatedFromMember.getInt();
bool isMemberSpecialization() const { return Common.getInt(); }
/// Determines whether any redeclaration of this template was
/// a specialization of a member template.
bool hasMemberSpecialization() const {
for (const auto *D : redecls()) {
if (D->isMemberSpecialization())
return true;
}
return false;
}
/// 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 +913,12 @@ public:
/// 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 +1997,8 @@ public:
/// 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 +2010,8 @@ public:
/// 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 +2199,22 @@ public:
/// struct X<int>::Inner<T*> { /* ... */ };
/// \endcode
bool isMemberSpecialization() const {
const auto *First =
cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
return First->InstantiatedFromMember.getInt();
return InstantiatedFromMember.getInt();
}
/// Determines whether any redeclaration of this this class template partial
/// specialization was a specialization of a member partial specialization.
bool hasMemberSpecialization() const {
for (const auto *D : redecls()) {
if (cast<ClassTemplatePartialSpecializationDecl>(D)
->isMemberSpecialization())
return true;
}
return false;
}
/// 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 +2284,6 @@ protected:
return static_cast<Common *>(RedeclarableTemplateDecl::getCommonPtr());
}
void setCommonPtr(Common *C) {
RedeclarableTemplateDecl::Common = C;
}
public:
friend class ASTDeclReader;
@ -2754,6 +2766,8 @@ public:
/// 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 +2779,8 @@ public:
/// 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 +2965,23 @@ public:
/// 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();
}
/// Determines whether any redeclaration of this this variable template
/// partial specialization was a specialization of a member partial
/// specialization.
bool hasMemberSpecialization() const {
for (const auto *D : redecls()) {
if (cast<VarTemplatePartialSpecializationDecl>(D)
->isMemberSpecialization())
return true;
}
return false;
}
/// 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;

View File

@ -11327,9 +11327,9 @@ public:
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.
@ -11368,7 +11368,8 @@ public:
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
@ -13009,28 +13010,14 @@ public:
/// 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.

View File

@ -2704,21 +2704,21 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const {
if (isTemplateInstantiation(VDTemplSpec->getTemplateSpecializationKind())) {
auto From = VDTemplSpec->getInstantiatedFrom();
if (auto *VTD = From.dyn_cast<VarTemplateDecl *>()) {
while (!VTD->isMemberSpecialization()) {
auto *NewVTD = VTD->getInstantiatedFromMemberTemplate();
if (!NewVTD)
while (!VTD->hasMemberSpecialization()) {
if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate())
VTD = NewVTD;
else
break;
VTD = NewVTD;
}
return getDefinitionOrSelf(VTD->getTemplatedDecl());
}
if (auto *VTPSD =
From.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
while (!VTPSD->isMemberSpecialization()) {
auto *NewVTPSD = VTPSD->getInstantiatedFromMember();
if (!NewVTPSD)
while (!VTPSD->hasMemberSpecialization()) {
if (auto *NewVTPSD = VTPSD->getInstantiatedFromMember())
VTPSD = NewVTPSD;
else
break;
VTPSD = NewVTPSD;
}
return getDefinitionOrSelf<VarDecl>(VTPSD);
}
@ -2727,15 +2727,14 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const {
// If this is the pattern of a variable template, find where it was
// instantiated from. FIXME: Is this necessary?
if (VarTemplateDecl *VarTemplate = VD->getDescribedVarTemplate()) {
while (!VarTemplate->isMemberSpecialization()) {
auto *NewVT = VarTemplate->getInstantiatedFromMemberTemplate();
if (!NewVT)
if (VarTemplateDecl *VTD = VD->getDescribedVarTemplate()) {
while (!VTD->hasMemberSpecialization()) {
if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate())
VTD = NewVTD;
else
break;
VarTemplate = NewVT;
}
return getDefinitionOrSelf(VarTemplate->getTemplatedDecl());
return getDefinitionOrSelf(VTD->getTemplatedDecl());
}
if (VD == this)
@ -4150,11 +4149,11 @@ FunctionDecl::getTemplateInstantiationPattern(bool ForDefinition) const {
if (FunctionTemplateDecl *Primary = getPrimaryTemplate()) {
// If we hit a point where the user provided a specialization of this
// template, we're done looking.
while (!ForDefinition || !Primary->isMemberSpecialization()) {
auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate();
if (!NewPrimary)
while (!ForDefinition || !Primary->hasMemberSpecialization()) {
if (auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate())
Primary = NewPrimary;
else
break;
Primary = NewPrimary;
}
return getDefinitionOrSelf(Primary->getTemplatedDecl());

View File

@ -2023,19 +2023,21 @@ const CXXRecordDecl *CXXRecordDecl::getTemplateInstantiationPattern() const {
if (auto *TD = dyn_cast<ClassTemplateSpecializationDecl>(this)) {
auto From = TD->getInstantiatedFrom();
if (auto *CTD = From.dyn_cast<ClassTemplateDecl *>()) {
while (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate()) {
if (NewCTD->isMemberSpecialization())
while (!CTD->hasMemberSpecialization()) {
if (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate())
CTD = NewCTD;
else
break;
CTD = NewCTD;
}
return GetDefinitionOrSelf(CTD->getTemplatedDecl());
}
if (auto *CTPSD =
From.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
while (auto *NewCTPSD = CTPSD->getInstantiatedFromMember()) {
if (NewCTPSD->isMemberSpecialization())
while (!CTPSD->hasMemberSpecialization()) {
if (auto *NewCTPSD = CTPSD->getInstantiatedFromMemberTemplate())
CTPSD = NewCTPSD;
else
break;
CTPSD = NewCTPSD;
}
return GetDefinitionOrSelf(CTPSD);
}

View File

@ -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 {
@ -463,19 +463,17 @@ void FunctionTemplateDecl::addSpecialization(
}
void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
using Base = RedeclarableTemplateDecl;
// 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 +483,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 +491,7 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
assert(ThisCommon->Specializations.size() == 0 &&
"Can't merge incompatible declarations!");
Base::Common = PrevCommon;
setCommonPtr(PrevCommon);
}
//===----------------------------------------------------------------------===//

View File

@ -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);
@ -834,7 +834,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;
@ -910,15 +909,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 differ 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();
}
@ -957,8 +954,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;
@ -1068,16 +1064,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))
@ -1524,7 +1520,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,
@ -1805,8 +1800,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) {

View File

@ -4510,10 +4510,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:
@ -7663,7 +7663,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());
@ -7682,6 +7682,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
@ -8059,12 +8063,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();
}
}
@ -9871,6 +9869,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) {
@ -12028,10 +12028,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()) {
@ -17093,8 +17090,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.

View File

@ -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.

View File

@ -9953,7 +9953,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
auto SynthesizeAggrGuide = [&](InitListExpr *ListInit) {
auto *Pattern = Template;
while (Pattern->getInstantiatedFromMemberTemplate()) {
if (Pattern->isMemberSpecialization())
if (Pattern->hasMemberSpecialization())
break;
Pattern = Pattern->getInstantiatedFromMemberTemplate();
}

View File

@ -1795,8 +1795,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 &&
@ -1984,19 +1985,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,
@ -2011,30 +1999,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
@ -2046,23 +2010,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.
@ -2089,10 +2036,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.
@ -2105,7 +2050,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);
@ -2120,12 +2068,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())
@ -2135,8 +2077,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);
@ -4133,7 +4129,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.");
@ -4251,17 +4248,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
@ -5776,9 +5772,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()))) {
@ -8467,15 +8461,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.
@ -8485,6 +8476,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()) {
@ -8496,11 +8492,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
@ -9100,8 +9091,8 @@ bool Sema::CheckFunctionTemplateSpecialization(
TemplateDeductionInfo Info(FailedCandidates.getLocation());
FunctionDecl *Specialization = nullptr;
if (TemplateDeductionResult TDK = DeduceTemplateArguments(
cast<FunctionTemplateDecl>(FunTmpl->getFirstDecl()),
ExplicitTemplateArgs ? &Args : nullptr, FT, Specialization, Info);
FunTmpl, ExplicitTemplateArgs ? &Args : nullptr, FT,
Specialization, Info);
TDK != TemplateDeductionResult::Success) {
// Template argument deduction failed; record why it failed, so
// that we can provide nifty diagnostics.
@ -11299,8 +11290,8 @@ private:
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);
}
}

View File

@ -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(),

View File

@ -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())

View File

@ -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,396 @@ 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);
struct TemplateInstantiationArgumentCollecter
: DeclVisitor<TemplateInstantiationArgumentCollecter, Decl *> {
Sema &S;
MultiLevelTemplateArgumentList &Result;
std::optional<ArrayRef<TemplateArgument>> Innermost;
bool RelativeToPrimary;
bool ForConstraintInstantiation;
// We're done when we hit an explicit specialization.
if (VarTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization &&
!isa<VarTemplatePartialSpecializationDecl>(VarTemplSpec))
return Response::Done();
TemplateInstantiationArgumentCollecter(
Sema &S, MultiLevelTemplateArgumentList &Result,
std::optional<ArrayRef<TemplateArgument>> Innermost,
bool RelativeToPrimary, bool ForConstraintInstantiation)
: S(S), Result(Result), Innermost(Innermost),
RelativeToPrimary(RelativeToPrimary),
ForConstraintInstantiation(ForConstraintInstantiation) {}
// 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();
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()); }
void AddInnermostTemplateArguments(const Decl *D) {
assert(Innermost);
Result.addOuterTemplateArguments(const_cast<Decl *>(D), *Innermost,
/*Final=*/false);
Innermost.reset();
}
void AddOuterTemplateArguments(const Decl *D, ArrayRef<TemplateArgument> Args,
bool Final) {
Result.addOuterTemplateArguments(const_cast<Decl *>(D), Args, Final);
}
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();
}
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);
}
Decl *VisitVarTemplateDecl(VarTemplateDecl *VTD) {
assert(
(ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
"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);
}
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);
}
Decl *VisitClassTemplateDecl(ClassTemplateDecl *CTD) {
assert(
(ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
"outer template not instantiated?");
if (Innermost)
AddInnermostTemplateArguments(CTD);
else if (ForConstraintInstantiation)
AddOuterTemplateArguments(CTD, CTD->getInjectedTemplateArgs(),
/*Final=*/false);
if (CTD->isMemberSpecialization())
return Done();
if (CTD->getFriendObjectKind())
return ChangeDecl(CTD->getLexicalDeclContext());
return UseNextDecl(CTD);
}
Decl *VisitClassTemplatePartialSpecializationDecl(
ClassTemplatePartialSpecializationDecl *CTPSD) {
assert(
(ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
"outer template not instantiated?");
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 (Innermost)
AddInnermostTemplateArguments(TATD);
else if (ForConstraintInstantiation)
AddOuterTemplateArguments(TATD, TATD->getInjectedTemplateArgs(),
/*Final=*/false);
return UseNextDecl(TATD);
}
Decl *VisitConceptDecl(ConceptDecl *CD) {
assert(
(ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
"outer template not instantiated?");
if (Innermost)
AddInnermostTemplateArguments(CD);
return UseNextDecl(CD);
}
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()->hasMemberSpecialization())
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);
}
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);
// 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();
if (CTSD->getSpecializationKind() == TSK_ExplicitSpecialization)
return Done();
if (!SkipForSpecialization)
Result.addOuterTemplateArguments(
const_cast<ClassTemplateSpecializationDecl *>(ClassTemplSpec),
ClassTemplSpec->getTemplateInstantiationArgs().asArray(),
/*Final=*/false);
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(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());
}
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);
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()) {
assert(
(ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
"Outer template not instantiated?");
}
// 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());
}
if (ForConstraintInstantiation && Function->getFriendObjectKind())
return Response::ChangeDecl(Function->getLexicalDeclContext());
return Response::UseNextDecl(Function);
}
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(
TSTy->getTemplateName().getAsTemplateDecl(), Arguments,
/*Final=*/false);
}
}
NNS = NNS->getPrefix();
assert(CTSD->getSpecializedTemplate() && "No class template?");
llvm::PointerUnion<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *>
Specialized = CTSD->getSpecializedTemplateOrPartial();
if (auto *CTPSD =
Specialized.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
if (CTPSD->hasMemberSpecialization())
return Done();
} else {
auto *CTD = Specialized.get<ClassTemplateDecl *>();
if (CTD->hasMemberSpecialization())
return Done();
}
return UseNextDecl(CTSD);
}
return Response::ChangeDecl(FTD->getLexicalDeclContext());
}
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);
Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
MultiLevelTemplateArgumentList &Result,
ASTContext &Context,
bool ForConstraintInstantiation) {
if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) {
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);
}
// We're done when we hit an explicit specialization.
if (VTSD->getSpecializationKind() == TSK_ExplicitSpecialization)
return Done();
if (const MemberSpecializationInfo *MSInfo =
Rec->getMemberSpecializationInfo())
if (MSInfo->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
return Response::Done();
if (Innermost)
AddInnermostTemplateArguments(VTSD);
else
AddOuterTemplateArguments(VTSD,
VTSD->getTemplateInstantiationArgs().asArray(),
/*Final=*/false);
bool IsFriend = Rec->getFriendObjectKind() ||
(Rec->getDescribedClassTemplate() &&
Rec->getDescribedClassTemplate()->getFriendObjectKind());
if (ForConstraintInstantiation && IsFriend &&
Rec->getNonTransparentDeclContext()->isFileContext()) {
return Response::ChangeDecl(Rec->getLexicalDeclContext());
}
// 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());
}
// 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->hasMemberSpecialization())
return Done();
} else {
auto *VTD = Specialized.get<VarTemplateDecl *>();
if (VTD->hasMemberSpecialization())
return Done();
}
return UseNextDecl(VTSD);
}
return Response::UseNextDecl(Rec);
}
Decl *VisitImplicitConceptSpecializationDecl(
ImplicitConceptSpecializationDecl *ICSD) {
AddOuterTemplateArguments(ICSD, ICSD->getTemplateArguments(),
/*Final=*/false);
return UseNextDecl(ICSD);
}
Response HandleImplicitConceptSpecializationDecl(
const ImplicitConceptSpecializationDecl *CSD,
MultiLevelTemplateArgumentList &Result) {
Result.addOuterTemplateArguments(
const_cast<ImplicitConceptSpecializationDecl *>(CSD),
CSD->getTemplateArguments(),
/*Final=*/false);
return Response::UseNextDecl(CSD);
}
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;
}
@ -1673,10 +1663,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())
@ -1721,8 +1709,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());
}
@ -4128,31 +4116,25 @@ getPatternForClassTemplateSpecialization(
CXXRecordDecl *Pattern = nullptr;
Specialized = ClassTemplateSpec->getSpecializedTemplateOrPartial();
if (auto *PartialSpec =
Specialized.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
// Instantiate using the best class template partial specialization.
while (PartialSpec->getInstantiatedFromMember()) {
// If we've found an explicit specialization of this class template,
// stop here and use that as the pattern.
if (PartialSpec->isMemberSpecialization())
if (auto *CTD = Specialized.dyn_cast<ClassTemplateDecl *>()) {
while (!CTD->hasMemberSpecialization()) {
if (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate())
CTD = NewCTD;
else
break;
PartialSpec = PartialSpec->getInstantiatedFromMember();
}
Pattern = PartialSpec;
} else {
ClassTemplateDecl *Template = ClassTemplateSpec->getSpecializedTemplate();
while (Template->getInstantiatedFromMemberTemplate()) {
// If we've found an explicit specialization of this class template,
// stop here and use that as the pattern.
if (Template->isMemberSpecialization())
Pattern = CTD->getTemplatedDecl();
} else if (auto *CTPSD =
Specialized
.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
while (!CTPSD->hasMemberSpecialization()) {
if (auto *NewCTPSD = CTPSD->getInstantiatedFromMemberTemplate())
CTPSD = NewCTPSD;
else
break;
Template = Template->getInstantiatedFromMemberTemplate();
}
Pattern = Template->getTemplatedDecl();
Pattern = CTPSD;
}
return Pattern;
}

View File

@ -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"
@ -4688,6 +4689,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
@ -4705,12 +4736,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;
@ -4751,7 +4780,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,
@ -5199,8 +5228,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.

View File

@ -10003,7 +10003,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();

View File

@ -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.

View File

@ -1713,14 +1713,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());
@ -1806,11 +1805,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;
}
@ -1874,12 +1872,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;
}

View File

@ -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&>;

View File

@ -0,0 +1,228 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
namespace Undefined {
template<typename T>
struct A {
template<typename U>
static constexpr int f(); // expected-note {{declared here}}
template<typename U>
static const int x; // expected-note {{declared here}}
template<typename U>
static const int x<U*>; // expected-note {{declared here}}
template<typename U>
struct B; // expected-note {{template is declared here}}
template<typename U>
struct B<U*>; // expected-note {{template is declared here}}
};
template<>
template<typename U>
constexpr int A<short>::f() {
return A<long>::f<U>();
}
template<>
template<typename U>
constexpr int A<short>::x = A<long>::x<U>;
template<>
template<typename U>
constexpr int A<short>::x<U*> = A<long>::x<U*>;
template<>
template<typename U>
struct A<short>::B<U*> {
static constexpr int y = A<long>::B<U*>::y;
};
template<>
template<typename U>
struct A<short>::B {
static constexpr int y = A<long>::B<U>::y;
};
template<>
template<typename U>
constexpr int A<long>::f() {
return 1;
}
template<>
template<typename U>
constexpr int A<long>::x = 1;
template<>
template<typename U>
constexpr int A<long>::x<U*> = 2;
template<>
template<typename U>
struct A<long>::B {
static constexpr int y = 1;
};
template<>
template<typename U>
struct A<long>::B<U*> {
static constexpr int y = 2;
};
static_assert(A<int>::f<int>() == 0); // expected-error {{static assertion expression is not an integral constant expression}}
// expected-note@-1 {{undefined function 'f<int>' cannot be used in a constant expression}}
static_assert(A<int>::x<int> == 0); // expected-error {{static assertion expression is not an integral constant expression}}
// expected-note@-1 {{initializer of 'x<int>' is unknown}}
static_assert(A<int>::x<int*> == 0); // expected-error {{static assertion expression is not an integral constant expression}}
// expected-note@-1 {{initializer of 'x<int *>' is unknown}}
static_assert(A<int>::B<int>::y == 0); // expected-error {{implicit instantiation of undefined template 'Undefined::A<int>::B<int>'}}
static_assert(A<int>::B<int*>::y == 0); // expected-error {{implicit instantiation of undefined template 'Undefined::A<int>::B<int *>'}}
static_assert(A<short>::f<int>() == 1);
static_assert(A<short>::x<int> == 1);
static_assert(A<short>::x<int*> == 2);
static_assert(A<short>::B<int>::y == 1);
static_assert(A<short>::B<int*>::y == 2);
} // namespace Undefined
namespace Defined {
template<typename T>
struct A {
template<typename U>
static constexpr int f() {
return 0;
};
template<typename U>
static const int x = 0;
template<typename U>
static const int x<U*> = 0;
template<typename U>
struct B {
static constexpr int y = 0;
};
template<typename U>
struct B<U*> {
static constexpr int y = 0;
};
};
template<>
template<typename U>
constexpr int A<short>::f() {
return A<long>::f<U>();
}
template<>
template<typename U>
constexpr int A<short>::x = A<long>::x<U>;
template<>
template<typename U>
constexpr int A<short>::x<U*> = A<long>::x<U*>;
template<>
template<typename U>
struct A<short>::B<U*> {
static constexpr int y = A<long>::B<U*>::y;
};
template<>
template<typename U>
struct A<short>::B {
static constexpr int y = A<long>::B<U>::y;
};
template<>
template<typename U>
constexpr int A<long>::f() {
return 1;
}
template<>
template<typename U>
constexpr int A<long>::x = 1;
template<>
template<typename U>
constexpr int A<long>::x<U*> = 2;
template<>
template<typename U>
struct A<long>::B {
static constexpr int y = 1;
};
template<>
template<typename U>
struct A<long>::B<U*> {
static constexpr int y = 2;
};
static_assert(A<int>::f<int>() == 0);
static_assert(A<int>::x<int> == 0);
static_assert(A<int>::x<int*> == 0);
static_assert(A<int>::B<int>::y == 0);
static_assert(A<int>::B<int*>::y == 0);
static_assert(A<short>::f<int>() == 1);
static_assert(A<short>::x<int> == 1);
static_assert(A<short>::x<int*> == 2);
static_assert(A<short>::B<int>::y == 1);
static_assert(A<short>::B<int*>::y == 2);
} // namespace Defined
namespace Dependent {
template<int I>
struct A {
template<int J>
static constexpr int f();
template<int J>
static const int x;
template<int J>
struct B;
};
template<>
template<int J>
constexpr int A<0>::f() {
return A<1>::f<J>();
}
template<>
template<int J>
constexpr int A<1>::f() {
return J;
}
template<>
template<int J>
constexpr int A<0>::x = A<1>::x<J>;
template<>
template<int J>
constexpr int A<1>::x = J;
template<>
template<int J>
struct A<0>::B {
static constexpr int y = A<1>::B<J>::y;
};
template<>
template<int J>
struct A<1>::B {
static constexpr int y = J;
};
static_assert(A<0>::f<2>() == 2);
static_assert(A<0>::x<2> == 2);
static_assert(A<0>::B<2>::y == 2);
} // namespace Dependent

View File

@ -199,9 +199,7 @@ namespace hidden_specializations {
cls<char*> uk4; // expected-error 1+{{partial specialization of 'cls<T *>' must be imported}} expected-error 1+{{definition of}}
cls<void>::nested_cls unk1; // expected-error 1+{{explicit specialization of 'nested_cls' must be imported}} expected-error 1+{{definition of}}
cls<void>::nested_cls_t<int> unk2; // expected-error 1+{{explicit specialization of 'nested_cls_t' must be imported}} expected-error 1+{{definition of}}
// expected-error@cxx-templates-unimported.h:29 {{explicit specialization of 'nested_cls_t' must be imported}}
// expected-note@-2 {{in evaluation of exception specification}}
cls<void>::nested_cls_t<char> unk3; // expected-error 1+{{explicit specialization of 'nested_cls_t' must be imported}}
cls<void>::nested_cls_t<char> unk3; // expected-error 1+{{explicit specialization of 'nested_cls_t' must be imported}} expected-error 1+{{definition of}}
// For enums, uses that would trigger instantiations of definitions are not
// allowed.