Skip to content

Commit cdd71d6

Browse files
authored
[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).
1 parent 6d66ac5 commit cdd71d6

File tree

16 files changed

+763
-652
lines changed

16 files changed

+763
-652
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,9 @@ Bug Fixes to C++ Support
404404
- Fixed an assertion failure in debug mode, and potential crashes in release mode, when
405405
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.
406406
- Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326)
407+
- Clang now uses the correct set of template argument lists when comparing the constraints of
408+
out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of
409+
a class template. (#GH102320)
407410

408411
Bug Fixes to AST Handling
409412
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/DeclTemplate.h

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -781,15 +781,11 @@ class RedeclarableTemplateDecl : public TemplateDecl,
781781
EntryType *Entry, void *InsertPos);
782782

783783
struct CommonBase {
784-
CommonBase() : InstantiatedFromMember(nullptr, false) {}
784+
CommonBase() {}
785785

786786
/// The template from which this was most
787787
/// directly instantiated (or null).
788-
///
789-
/// The boolean value indicates whether this template
790-
/// was explicitly specialized.
791-
llvm::PointerIntPair<RedeclarableTemplateDecl*, 1, bool>
792-
InstantiatedFromMember;
788+
RedeclarableTemplateDecl *InstantiatedFromMember = nullptr;
793789

794790
/// If non-null, points to an array of specializations (including
795791
/// partial specializations) known only by their external declaration IDs.
@@ -809,14 +805,19 @@ class RedeclarableTemplateDecl : public TemplateDecl,
809805
};
810806

811807
/// Pointer to the common data shared by all declarations of this
812-
/// template.
813-
mutable CommonBase *Common = nullptr;
808+
/// template, and a flag indicating if the template is a member
809+
/// specialization.
810+
mutable llvm::PointerIntPair<CommonBase *, 1, bool> Common;
811+
812+
CommonBase *getCommonPtrInternal() const { return Common.getPointer(); }
814813

815814
/// Retrieves the "common" pointer shared by all (re-)declarations of
816815
/// the same template. Calling this routine may implicitly allocate memory
817816
/// for the common pointer.
818817
CommonBase *getCommonPtr() const;
819818

819+
void setCommonPtr(CommonBase *C) const { Common.setPointer(C); }
820+
820821
virtual CommonBase *newCommon(ASTContext &C) const = 0;
821822

822823
// Construct a template decl with name, parameters, and templated element.
@@ -857,15 +858,12 @@ class RedeclarableTemplateDecl : public TemplateDecl,
857858
/// template<> template<typename T>
858859
/// struct X<int>::Inner { /* ... */ };
859860
/// \endcode
860-
bool isMemberSpecialization() const {
861-
return getCommonPtr()->InstantiatedFromMember.getInt();
862-
}
861+
bool isMemberSpecialization() const { return Common.getInt(); }
863862

864863
/// Note that this member template is a specialization.
865864
void setMemberSpecialization() {
866-
assert(getCommonPtr()->InstantiatedFromMember.getPointer() &&
867-
"Only member templates can be member template specializations");
868-
getCommonPtr()->InstantiatedFromMember.setInt(true);
865+
assert(!isMemberSpecialization() && "already a member specialization");
866+
Common.setInt(true);
869867
}
870868

871869
/// Retrieve the member template from which this template was
@@ -905,12 +903,12 @@ class RedeclarableTemplateDecl : public TemplateDecl,
905903
/// void X<T>::f(T, U);
906904
/// \endcode
907905
RedeclarableTemplateDecl *getInstantiatedFromMemberTemplate() const {
908-
return getCommonPtr()->InstantiatedFromMember.getPointer();
906+
return getCommonPtr()->InstantiatedFromMember;
909907
}
910908

911909
void setInstantiatedFromMemberTemplate(RedeclarableTemplateDecl *TD) {
912-
assert(!getCommonPtr()->InstantiatedFromMember.getPointer());
913-
getCommonPtr()->InstantiatedFromMember.setPointer(TD);
910+
assert(!getCommonPtr()->InstantiatedFromMember);
911+
getCommonPtr()->InstantiatedFromMember = TD;
914912
}
915913

916914
/// Retrieve the "injected" template arguments that correspond to the
@@ -1989,6 +1987,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
19891987
/// template arguments have been deduced.
19901988
void setInstantiationOf(ClassTemplatePartialSpecializationDecl *PartialSpec,
19911989
const TemplateArgumentList *TemplateArgs) {
1990+
assert(!isa<ClassTemplatePartialSpecializationDecl>(this) &&
1991+
"A partial specialization cannot be instantiated from a template");
19921992
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() &&
19931993
"Already set to a class template partial specialization!");
19941994
auto *PS = new (getASTContext()) SpecializedPartialSpecialization();
@@ -2000,6 +2000,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
20002000
/// Note that this class template specialization is an instantiation
20012001
/// of the given class template.
20022002
void setInstantiationOf(ClassTemplateDecl *TemplDecl) {
2003+
assert(!isa<ClassTemplatePartialSpecializationDecl>(this) &&
2004+
"A partial specialization cannot be instantiated from a template");
20032005
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() &&
20042006
"Previously set to a class template partial specialization!");
20052007
SpecializedTemplate = TemplDecl;
@@ -2187,18 +2189,11 @@ class ClassTemplatePartialSpecializationDecl
21872189
/// struct X<int>::Inner<T*> { /* ... */ };
21882190
/// \endcode
21892191
bool isMemberSpecialization() const {
2190-
const auto *First =
2191-
cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
2192-
return First->InstantiatedFromMember.getInt();
2192+
return InstantiatedFromMember.getInt();
21932193
}
21942194

21952195
/// Note that this member template is a specialization.
2196-
void setMemberSpecialization() {
2197-
auto *First = cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
2198-
assert(First->InstantiatedFromMember.getPointer() &&
2199-
"Only member templates can be member template specializations");
2200-
return First->InstantiatedFromMember.setInt(true);
2201-
}
2196+
void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }
22022197

22032198
/// Retrieves the injected specialization type for this partial
22042199
/// specialization. This is not the same as the type-decl-type for
@@ -2268,10 +2263,6 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
22682263
return static_cast<Common *>(RedeclarableTemplateDecl::getCommonPtr());
22692264
}
22702265

2271-
void setCommonPtr(Common *C) {
2272-
RedeclarableTemplateDecl::Common = C;
2273-
}
2274-
22752266
public:
22762267

22772268
friend class ASTDeclReader;
@@ -2754,6 +2745,8 @@ class VarTemplateSpecializationDecl : public VarDecl,
27542745
/// template arguments have been deduced.
27552746
void setInstantiationOf(VarTemplatePartialSpecializationDecl *PartialSpec,
27562747
const TemplateArgumentList *TemplateArgs) {
2748+
assert(!isa<VarTemplatePartialSpecializationDecl>(this) &&
2749+
"A partial specialization cannot be instantiated from a template");
27572750
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() &&
27582751
"Already set to a variable template partial specialization!");
27592752
auto *PS = new (getASTContext()) SpecializedPartialSpecialization();
@@ -2765,6 +2758,8 @@ class VarTemplateSpecializationDecl : public VarDecl,
27652758
/// Note that this variable template specialization is an instantiation
27662759
/// of the given variable template.
27672760
void setInstantiationOf(VarTemplateDecl *TemplDecl) {
2761+
assert(!isa<VarTemplatePartialSpecializationDecl>(this) &&
2762+
"A partial specialization cannot be instantiated from a template");
27682763
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() &&
27692764
"Previously set to a variable template partial specialization!");
27702765
SpecializedTemplate = TemplDecl;
@@ -2949,18 +2944,11 @@ class VarTemplatePartialSpecializationDecl
29492944
/// U* X<int>::Inner<T*> = (T*)(0) + 1;
29502945
/// \endcode
29512946
bool isMemberSpecialization() const {
2952-
const auto *First =
2953-
cast<VarTemplatePartialSpecializationDecl>(getFirstDecl());
2954-
return First->InstantiatedFromMember.getInt();
2947+
return InstantiatedFromMember.getInt();
29552948
}
29562949

29572950
/// Note that this member template is a specialization.
2958-
void setMemberSpecialization() {
2959-
auto *First = cast<VarTemplatePartialSpecializationDecl>(getFirstDecl());
2960-
assert(First->InstantiatedFromMember.getPointer() &&
2961-
"Only member templates can be member template specializations");
2962-
return First->InstantiatedFromMember.setInt(true);
2963-
}
2951+
void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }
29642952

29652953
SourceRange getSourceRange() const override LLVM_READONLY;
29662954

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11389,9 +11389,9 @@ class Sema final : public SemaBase {
1138911389
CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
1139011390
const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams,
1139111391
AccessSpecifier AS, SourceLocation ModulePrivateLoc,
11392-
SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists,
11393-
TemplateParameterList **OuterTemplateParamLists,
11394-
SkipBodyInfo *SkipBody = nullptr);
11392+
SourceLocation FriendLoc,
11393+
ArrayRef<TemplateParameterList *> OuterTemplateParamLists,
11394+
bool IsMemberSpecialization, SkipBodyInfo *SkipBody = nullptr);
1139511395

1139611396
/// Translates template arguments as provided by the parser
1139711397
/// into template arguments used by semantic analysis.
@@ -11430,7 +11430,8 @@ class Sema final : public SemaBase {
1143011430
DeclResult ActOnVarTemplateSpecialization(
1143111431
Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous,
1143211432
SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
11433-
StorageClass SC, bool IsPartialSpecialization);
11433+
StorageClass SC, bool IsPartialSpecialization,
11434+
bool IsMemberSpecialization);
1143411435

1143511436
/// Get the specialization of the given variable template corresponding to
1143611437
/// the specified argument list, or a null-but-valid result if the arguments
@@ -13071,28 +13072,14 @@ class Sema final : public SemaBase {
1307113072
/// dealing with a specialization. This is only relevant for function
1307213073
/// template specializations.
1307313074
///
13074-
/// \param Pattern If non-NULL, indicates the pattern from which we will be
13075-
/// instantiating the definition of the given declaration, \p ND. This is
13076-
/// used to determine the proper set of template instantiation arguments for
13077-
/// friend function template specializations.
13078-
///
1307913075
/// \param ForConstraintInstantiation when collecting arguments,
1308013076
/// ForConstraintInstantiation indicates we should continue looking when
1308113077
/// encountering a lambda generic call operator, and continue looking for
1308213078
/// arguments on an enclosing class template.
13083-
///
13084-
/// \param SkipForSpecialization when specified, any template specializations
13085-
/// in a traversal would be ignored.
13086-
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
13087-
/// when encountering a specialized member function template, rather than
13088-
/// returning immediately.
1308913079
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
1309013080
const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false,
1309113081
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
13092-
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
13093-
bool ForConstraintInstantiation = false,
13094-
bool SkipForSpecialization = false,
13095-
bool ForDefaultArgumentSubstitution = false);
13082+
bool RelativeToPrimary = false, bool ForConstraintInstantiation = false);
1309613083

1309713084
/// RAII object to handle the state changes required to synthesize
1309813085
/// a function body.

clang/lib/AST/DeclTemplate.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -309,35 +309,35 @@ bool TemplateDecl::isTypeAlias() const {
309309
void RedeclarableTemplateDecl::anchor() {}
310310

311311
RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() const {
312-
if (Common)
313-
return Common;
312+
if (CommonBase *C = getCommonPtrInternal())
313+
return C;
314314

315315
// Walk the previous-declaration chain until we either find a declaration
316316
// with a common pointer or we run out of previous declarations.
317317
SmallVector<const RedeclarableTemplateDecl *, 2> PrevDecls;
318318
for (const RedeclarableTemplateDecl *Prev = getPreviousDecl(); Prev;
319319
Prev = Prev->getPreviousDecl()) {
320-
if (Prev->Common) {
321-
Common = Prev->Common;
320+
if (CommonBase *C = Prev->getCommonPtrInternal()) {
321+
setCommonPtr(C);
322322
break;
323323
}
324324

325325
PrevDecls.push_back(Prev);
326326
}
327327

328328
// If we never found a common pointer, allocate one now.
329-
if (!Common) {
329+
if (!getCommonPtrInternal()) {
330330
// FIXME: If any of the declarations is from an AST file, we probably
331331
// need an update record to add the common data.
332332

333-
Common = newCommon(getASTContext());
333+
setCommonPtr(newCommon(getASTContext()));
334334
}
335335

336336
// Update any previous declarations we saw with the common pointer.
337337
for (const RedeclarableTemplateDecl *Prev : PrevDecls)
338-
Prev->Common = Common;
338+
Prev->setCommonPtr(getCommonPtrInternal());
339339

340-
return Common;
340+
return getCommonPtrInternal();
341341
}
342342

343343
void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const {
@@ -467,15 +467,15 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
467467

468468
// If we haven't created a common pointer yet, then it can just be created
469469
// with the usual method.
470-
if (!Base::Common)
470+
if (!getCommonPtrInternal())
471471
return;
472472

473-
Common *ThisCommon = static_cast<Common *>(Base::Common);
473+
Common *ThisCommon = static_cast<Common *>(getCommonPtrInternal());
474474
Common *PrevCommon = nullptr;
475475
SmallVector<FunctionTemplateDecl *, 8> PreviousDecls;
476476
for (; Prev; Prev = Prev->getPreviousDecl()) {
477-
if (Prev->Base::Common) {
478-
PrevCommon = static_cast<Common *>(Prev->Base::Common);
477+
if (CommonBase *C = Prev->getCommonPtrInternal()) {
478+
PrevCommon = static_cast<Common *>(C);
479479
break;
480480
}
481481
PreviousDecls.push_back(Prev);
@@ -485,15 +485,15 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
485485
// use this common pointer.
486486
if (!PrevCommon) {
487487
for (auto *D : PreviousDecls)
488-
D->Base::Common = ThisCommon;
488+
D->setCommonPtr(ThisCommon);
489489
return;
490490
}
491491

492492
// Ensure we don't leak any important state.
493493
assert(ThisCommon->Specializations.size() == 0 &&
494494
"Can't merge incompatible declarations!");
495495

496-
Base::Common = PrevCommon;
496+
setCommonPtr(PrevCommon);
497497
}
498498

499499
//===----------------------------------------------------------------------===//

clang/lib/Sema/SemaConcept.cpp

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -585,8 +585,8 @@ static bool CheckConstraintSatisfaction(
585585

586586
ArrayRef<TemplateArgument> TemplateArgs =
587587
TemplateArgsLists.getNumSubstitutedLevels() > 0
588-
? TemplateArgsLists.getOutermost()
589-
: ArrayRef<TemplateArgument> {};
588+
? TemplateArgsLists.getInnermost()
589+
: ArrayRef<TemplateArgument>{};
590590
Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
591591
Sema::InstantiatingTemplate::ConstraintsCheck{},
592592
const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
@@ -833,7 +833,6 @@ Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
833833
getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(),
834834
/*Final=*/false, /*Innermost=*/std::nullopt,
835835
/*RelativeToPrimary=*/true,
836-
/*Pattern=*/nullptr,
837836
/*ForConstraintInstantiation=*/true);
838837
if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
839838
return std::nullopt;
@@ -909,15 +908,13 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
909908
// Figure out the to-translation-unit depth for this function declaration for
910909
// the purpose of seeing if they differ by constraints. This isn't the same as
911910
// getTemplateDepth, because it includes already instantiated parents.
912-
static unsigned
913-
CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
914-
bool SkipForSpecialization = false) {
911+
static unsigned CalculateTemplateDepthForConstraints(Sema &S,
912+
const NamedDecl *ND) {
915913
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
916914
ND, ND->getLexicalDeclContext(), /*Final=*/false,
917915
/*Innermost=*/std::nullopt,
918916
/*RelativeToPrimary=*/true,
919-
/*Pattern=*/nullptr,
920-
/*ForConstraintInstantiation=*/true, SkipForSpecialization);
917+
/*ForConstraintInstantiation=*/true);
921918
return MLTAL.getNumLevels();
922919
}
923920

@@ -956,8 +953,7 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
956953
DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false,
957954
/*Innermost=*/std::nullopt,
958955
/*RelativeToPrimary=*/true,
959-
/*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true,
960-
/*SkipForSpecialization*/ false);
956+
/*ForConstraintInstantiation=*/true);
961957

962958
if (MLTAL.getNumSubstitutedLevels() == 0)
963959
return ConstrExpr;
@@ -1063,16 +1059,16 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
10631059
bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) {
10641060
assert(FD->getFriendObjectKind() && "Must be a friend!");
10651061

1062+
FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate();
10661063
// The logic for non-templates is handled in ASTContext::isSameEntity, so we
10671064
// don't have to bother checking 'DependsOnEnclosingTemplate' for a
10681065
// non-function-template.
1069-
assert(FD->getDescribedFunctionTemplate() &&
1070-
"Non-function templates don't need to be checked");
1066+
assert(FTD && "Non-function templates don't need to be checked");
10711067

10721068
SmallVector<const Expr *, 3> ACs;
1073-
FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs);
1069+
FTD->getAssociatedConstraints(ACs);
10741070

1075-
unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD);
1071+
unsigned OldTemplateDepth = FTD->getTemplateParameters()->getDepth();
10761072
for (const Expr *Constraint : ACs)
10771073
if (ConstraintExpressionDependsOnEnclosingTemplate(FD, OldTemplateDepth,
10781074
Constraint))
@@ -1519,7 +1515,6 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
15191515
CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(),
15201516
/*Final=*/false, CSE->getTemplateArguments(),
15211517
/*RelativeToPrimary=*/true,
1522-
/*Pattern=*/nullptr,
15231518
/*ForConstraintInstantiation=*/true);
15241519

15251520
return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL,
@@ -1800,8 +1795,8 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
18001795
return false;
18011796
}
18021797

1803-
unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1, true);
1804-
unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2, true);
1798+
unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1);
1799+
unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2);
18051800

18061801
for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) {
18071802
if (Depth2 > Depth1) {

0 commit comments

Comments
 (0)