Skip to content

Commit 6db007a

Browse files
[Clang][Sema] Fix comparison of constraint expressions
This diff switches the approach to comparison of constraint expressions to the new one based on template args substitution. It continues the effort to fix our handling of out-of-line definitions of constrained templates. This is a recommit of 3a54022. Differential revision: https://reviews.llvm.org/D146178
1 parent e305dcc commit 6db007a

10 files changed

+518
-63
lines changed

clang/include/clang/AST/DeclTemplate.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2309,9 +2309,15 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
23092309
return static_cast<Common *>(RedeclarableTemplateDecl::getCommonPtr());
23102310
}
23112311

2312+
void setCommonPtr(Common *C) {
2313+
RedeclarableTemplateDecl::Common = C;
2314+
}
2315+
23122316
public:
2317+
23132318
friend class ASTDeclReader;
23142319
friend class ASTDeclWriter;
2320+
friend class TemplateDeclInstantiator;
23152321

23162322
/// Load any lazily-loaded specializations from the external source.
23172323
void LoadLazySpecializations() const;

clang/include/clang/Sema/Template.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,21 @@ enum class TemplateSubstitutionKind : char {
232232
/// Replaces the current 'innermost' level with the provided argument list.
233233
/// This is useful for type deduction cases where we need to get the entire
234234
/// list from the AST, but then add the deduced innermost list.
235-
void replaceInnermostTemplateArguments(ArgList Args) {
236-
assert(TemplateArgumentLists.size() > 0 && "Replacing in an empty list?");
237-
TemplateArgumentLists[0].Args = Args;
235+
void replaceInnermostTemplateArguments(Decl *AssociatedDecl, ArgList Args) {
236+
assert((!TemplateArgumentLists.empty() || NumRetainedOuterLevels) &&
237+
"Replacing in an empty list?");
238+
239+
if (!TemplateArgumentLists.empty()) {
240+
assert((TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() ||
241+
TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() ==
242+
AssociatedDecl) &&
243+
"Trying to change incorrect declaration?");
244+
TemplateArgumentLists[0].Args = Args;
245+
} else {
246+
--NumRetainedOuterLevels;
247+
TemplateArgumentLists.push_back(
248+
{{AssociatedDecl, /*Final=*/false}, Args});
249+
}
238250
}
239251

240252
/// Add an outermost level that we are not substituting. We have no

clang/lib/Sema/SemaConcept.cpp

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
722722
ND, /*Final=*/false, /*Innermost=*/nullptr, /*RelativeToPrimary=*/true,
723723
/*Pattern=*/nullptr,
724724
/*ForConstraintInstantiation=*/true, SkipForSpecialization);
725-
return MLTAL.getNumSubstitutedLevels();
725+
return MLTAL.getNumLevels();
726726
}
727727

728728
namespace {
@@ -753,27 +753,44 @@ namespace {
753753
};
754754
} // namespace
755755

756+
static const Expr *SubstituteConstraintExpression(Sema &S, const NamedDecl *ND,
757+
const Expr *ConstrExpr) {
758+
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
759+
ND, /*Final=*/false, /*Innermost=*/nullptr,
760+
/*RelativeToPrimary=*/true,
761+
/*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true,
762+
/*SkipForSpecialization*/ false);
763+
if (MLTAL.getNumSubstitutedLevels() == 0)
764+
return ConstrExpr;
765+
766+
Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/false);
767+
std::optional<Sema::CXXThisScopeRAII> ThisScope;
768+
if (auto *RD = dyn_cast<CXXRecordDecl>(ND->getDeclContext()))
769+
ThisScope.emplace(S, const_cast<CXXRecordDecl *>(RD), Qualifiers());
770+
ExprResult SubstConstr =
771+
S.SubstConstraintExpr(const_cast<clang::Expr *>(ConstrExpr), MLTAL);
772+
if (SFINAE.hasErrorOccurred() || !SubstConstr.isUsable())
773+
return nullptr;
774+
return SubstConstr.get();
775+
}
776+
756777
bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
757778
const Expr *OldConstr,
758779
const NamedDecl *New,
759780
const Expr *NewConstr) {
781+
if (OldConstr == NewConstr)
782+
return true;
760783
if (Old && New && Old != New) {
761-
unsigned Depth1 = CalculateTemplateDepthForConstraints(
762-
*this, Old);
763-
unsigned Depth2 = CalculateTemplateDepthForConstraints(
764-
*this, New);
765-
766-
// Adjust the 'shallowest' verison of this to increase the depth to match
767-
// the 'other'.
768-
if (Depth2 > Depth1) {
769-
OldConstr = AdjustConstraintDepth(*this, Depth2 - Depth1)
770-
.TransformExpr(const_cast<Expr *>(OldConstr))
771-
.get();
772-
} else if (Depth1 > Depth2) {
773-
NewConstr = AdjustConstraintDepth(*this, Depth1 - Depth2)
774-
.TransformExpr(const_cast<Expr *>(NewConstr))
775-
.get();
776-
}
784+
if (const Expr *SubstConstr =
785+
SubstituteConstraintExpression(*this, Old, OldConstr))
786+
OldConstr = SubstConstr;
787+
else
788+
return false;
789+
if (const Expr *SubstConstr =
790+
SubstituteConstraintExpression(*this, New, NewConstr))
791+
NewConstr = SubstConstr;
792+
else
793+
return false;
777794
}
778795

779796
llvm::FoldingSetNodeID ID1, ID2;

clang/lib/Sema/SemaOverload.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1297,7 +1297,7 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
12971297
// We check the return type and template parameter lists for function
12981298
// templates first; the remaining checks follow.
12991299
bool SameTemplateParameterList = TemplateParameterListsAreEqual(
1300-
NewTemplate->getTemplateParameters(),
1300+
NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate,
13011301
OldTemplate->getTemplateParameters(), false, TPL_TemplateMatch);
13021302
bool SameReturnType = Context.hasSameType(Old->getDeclaredReturnType(),
13031303
New->getDeclaredReturnType());

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2882,7 +2882,7 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template,
28822882
// not class-scope explicit specialization, so replace with Deduced Args
28832883
// instead of adding to inner-most.
28842884
if (NeedsReplacement)
2885-
MLTAL.replaceInnermostTemplateArguments(CanonicalDeducedArgs);
2885+
MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs);
28862886

28872887
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
28882888
Info.getLocation(),

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,14 @@ HandleDefaultTempArgIntoTempTempParam(const TemplateTemplateParmDecl *TTP,
133133
return Response::Done();
134134
}
135135

136+
Response HandlePartialClassTemplateSpec(
137+
const ClassTemplatePartialSpecializationDecl *PartialClassTemplSpec,
138+
MultiLevelTemplateArgumentList &Result, bool SkipForSpecialization) {
139+
if (!SkipForSpecialization)
140+
Result.addOuterRetainedLevels(PartialClassTemplSpec->getTemplateDepth());
141+
return Response::Done();
142+
}
143+
136144
// Add template arguments from a class template instantiation.
137145
Response
138146
HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec,
@@ -210,6 +218,21 @@ Response HandleFunction(const FunctionDecl *Function,
210218
return Response::UseNextDecl(Function);
211219
}
212220

221+
Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD,
222+
MultiLevelTemplateArgumentList &Result) {
223+
if (!isa<ClassTemplateSpecializationDecl>(FTD->getDeclContext())) {
224+
NestedNameSpecifier *NNS = FTD->getTemplatedDecl()->getQualifier();
225+
const Type *Ty;
226+
const TemplateSpecializationType *TSTy;
227+
if (NNS && (Ty = NNS->getAsType()) &&
228+
(TSTy = Ty->getAs<TemplateSpecializationType>()))
229+
Result.addOuterTemplateArguments(const_cast<FunctionTemplateDecl *>(FTD),
230+
TSTy->template_arguments(),
231+
/*Final=*/false);
232+
}
233+
return Response::ChangeDecl(FTD->getLexicalDeclContext());
234+
}
235+
213236
Response HandleRecordDecl(const CXXRecordDecl *Rec,
214237
MultiLevelTemplateArgumentList &Result,
215238
ASTContext &Context,
@@ -220,15 +243,10 @@ Response HandleRecordDecl(const CXXRecordDecl *Rec,
220243
"Outer template not instantiated?");
221244
if (ClassTemplate->isMemberSpecialization())
222245
return Response::Done();
223-
if (ForConstraintInstantiation) {
224-
QualType RecordType = Context.getTypeDeclType(Rec);
225-
QualType Injected = cast<InjectedClassNameType>(RecordType)
226-
->getInjectedSpecializationType();
227-
const auto *InjectedType = cast<TemplateSpecializationType>(Injected);
246+
if (ForConstraintInstantiation)
228247
Result.addOuterTemplateArguments(const_cast<CXXRecordDecl *>(Rec),
229-
InjectedType->template_arguments(),
248+
ClassTemplate->getInjectedTemplateArgs(),
230249
/*Final=*/false);
231-
}
232250
}
233251

234252
bool IsFriend = Rec->getFriendObjectKind() ||
@@ -296,18 +314,23 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
296314
// Accumulate the set of template argument lists in this structure.
297315
MultiLevelTemplateArgumentList Result;
298316

299-
if (Innermost)
317+
using namespace TemplateInstArgsHelpers;
318+
const Decl *CurDecl = ND;
319+
if (Innermost) {
300320
Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND),
301321
Innermost->asArray(), Final);
302-
303-
const Decl *CurDecl = ND;
322+
CurDecl = Response::UseNextDecl(ND).NextDecl;
323+
}
304324

305325
while (!CurDecl->isFileContextDecl()) {
306-
using namespace TemplateInstArgsHelpers;
307326
Response R;
308327
if (const auto *VarTemplSpec =
309328
dyn_cast<VarTemplateSpecializationDecl>(CurDecl)) {
310329
R = HandleVarTemplateSpec(VarTemplSpec, Result, SkipForSpecialization);
330+
} else if (const auto *PartialClassTemplSpec =
331+
dyn_cast<ClassTemplatePartialSpecializationDecl>(CurDecl)) {
332+
R = HandlePartialClassTemplateSpec(PartialClassTemplSpec, Result,
333+
SkipForSpecialization);
311334
} else if (const auto *ClassTemplSpec =
312335
dyn_cast<ClassTemplateSpecializationDecl>(CurDecl)) {
313336
R = HandleClassTemplateSpec(ClassTemplSpec, Result,
@@ -320,6 +343,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
320343
} else if (const auto *CSD =
321344
dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) {
322345
R = HandleImplicitConceptSpecializationDecl(CSD, Result);
346+
} else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(CurDecl)) {
347+
R = HandleFunctionTemplateDecl(FTD, Result);
323348
} else if (!isa<DeclContext>(CurDecl)) {
324349
R = Response::DontClearRelativeToPrimaryNextDecl(CurDecl);
325350
if (CurDecl->getDeclContext()->isTranslationUnit()) {

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,33 +1654,12 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
16541654
<< QualifierLoc.getSourceRange();
16551655
return nullptr;
16561656
}
1657-
1658-
if (PrevClassTemplate) {
1659-
const ClassTemplateDecl *MostRecentPrevCT =
1660-
PrevClassTemplate->getMostRecentDecl();
1661-
TemplateParameterList *PrevParams =
1662-
MostRecentPrevCT->getTemplateParameters();
1663-
1664-
// Make sure the parameter lists match.
1665-
if (!SemaRef.TemplateParameterListsAreEqual(
1666-
D->getTemplatedDecl(), InstParams,
1667-
MostRecentPrevCT->getTemplatedDecl(), PrevParams, true,
1668-
Sema::TPL_TemplateMatch))
1669-
return nullptr;
1670-
1671-
// Do some additional validation, then merge default arguments
1672-
// from the existing declarations.
1673-
if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
1674-
Sema::TPC_ClassTemplate))
1675-
return nullptr;
1676-
}
16771657
}
16781658

16791659
CXXRecordDecl *RecordInst = CXXRecordDecl::Create(
16801660
SemaRef.Context, Pattern->getTagKind(), DC, Pattern->getBeginLoc(),
16811661
Pattern->getLocation(), Pattern->getIdentifier(), PrevDecl,
16821662
/*DelayTypeCreation=*/true);
1683-
16841663
if (QualifierLoc)
16851664
RecordInst->setQualifierInfo(QualifierLoc);
16861665

@@ -1690,16 +1669,38 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
16901669
ClassTemplateDecl *Inst
16911670
= ClassTemplateDecl::Create(SemaRef.Context, DC, D->getLocation(),
16921671
D->getIdentifier(), InstParams, RecordInst);
1693-
assert(!(isFriend && Owner->isDependentContext()));
1694-
Inst->setPreviousDecl(PrevClassTemplate);
1695-
16961672
RecordInst->setDescribedClassTemplate(Inst);
16971673

16981674
if (isFriend) {
1699-
if (PrevClassTemplate)
1675+
assert(!Owner->isDependentContext());
1676+
Inst->setLexicalDeclContext(Owner);
1677+
RecordInst->setLexicalDeclContext(Owner);
1678+
1679+
if (PrevClassTemplate) {
1680+
Inst->setCommonPtr(PrevClassTemplate->getCommonPtr());
1681+
RecordInst->setTypeForDecl(
1682+
PrevClassTemplate->getTemplatedDecl()->getTypeForDecl());
1683+
const ClassTemplateDecl *MostRecentPrevCT =
1684+
PrevClassTemplate->getMostRecentDecl();
1685+
TemplateParameterList *PrevParams =
1686+
MostRecentPrevCT->getTemplateParameters();
1687+
1688+
// Make sure the parameter lists match.
1689+
if (!SemaRef.TemplateParameterListsAreEqual(
1690+
RecordInst, InstParams, MostRecentPrevCT->getTemplatedDecl(),
1691+
PrevParams, true, Sema::TPL_TemplateMatch))
1692+
return nullptr;
1693+
1694+
// Do some additional validation, then merge default arguments
1695+
// from the existing declarations.
1696+
if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
1697+
Sema::TPC_ClassTemplate))
1698+
return nullptr;
1699+
17001700
Inst->setAccess(PrevClassTemplate->getAccess());
1701-
else
1701+
} else {
17021702
Inst->setAccess(D->getAccess());
1703+
}
17031704

17041705
Inst->setObjectOfFriendDecl();
17051706
// TODO: do we want to track the instantiation progeny of this
@@ -1710,15 +1711,15 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
17101711
Inst->setInstantiatedFromMemberTemplate(D);
17111712
}
17121713

1714+
Inst->setPreviousDecl(PrevClassTemplate);
1715+
17131716
// Trigger creation of the type for the instantiation.
1714-
SemaRef.Context.getInjectedClassNameType(RecordInst,
1715-
Inst->getInjectedClassNameSpecialization());
1717+
SemaRef.Context.getInjectedClassNameType(
1718+
RecordInst, Inst->getInjectedClassNameSpecialization());
17161719

17171720
// Finish handling of friends.
17181721
if (isFriend) {
17191722
DC->makeDeclVisibleInContext(Inst);
1720-
Inst->setLexicalDeclContext(Owner);
1721-
RecordInst->setLexicalDeclContext(Owner);
17221723
return Inst;
17231724
}
17241725

clang/test/SemaTemplate/concepts-friends.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,3 +441,27 @@ namespace NTTP {
441441
templ_func<1>(u2);
442442
}
443443
}
444+
445+
446+
namespace FriendOfFriend {
447+
448+
template <typename>
449+
concept Concept = true;
450+
451+
template <Concept> class FriendOfBar;
452+
453+
template <Concept> class Bar {
454+
template <Concept> friend class FriendOfBar;
455+
};
456+
457+
Bar<void> BarInstance;
458+
459+
namespace internal {
460+
void FriendOfFoo(FriendOfBar<void>);
461+
}
462+
463+
template <Concept> class Foo {
464+
friend void internal::FriendOfFoo(FriendOfBar<void>);
465+
};
466+
467+
} // namespace FriendOfFriend

0 commit comments

Comments
 (0)