Skip to content

Commit 60bee9f

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. The associated GitHub issue: #61414 Test plan: 1/ ninja check-all 2/ bootstrapped Clang passes tests Differential revision: https://reviews.llvm.org/D146178
1 parent 3490345 commit 60bee9f

7 files changed

+234
-50
lines changed

clang/lib/Sema/SemaConcept.cpp

+35-16
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
260260
return SubstitutedAtomicExpr;
261261
}
262262

263+
if (SubstitutedAtomicExpr.get()->isValueDependent())
264+
return SubstitutedAtomicExpr;
265+
263266
EnterExpressionEvaluationContext ConstantEvaluated(
264267
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
265268
SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
@@ -752,27 +755,43 @@ namespace {
752755
};
753756
} // namespace
754757

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

778797
llvm::FoldingSetNodeID ID1, ID2;

clang/lib/Sema/SemaOverload.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1294,7 +1294,7 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
12941294
// We check the return type and template parameter lists for function
12951295
// templates first; the remaining checks follow.
12961296
bool SameTemplateParameterList = TemplateParameterListsAreEqual(
1297-
NewTemplate->getTemplateParameters(),
1297+
NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate,
12981298
OldTemplate->getTemplateParameters(), false, TPL_TemplateMatch);
12991299
bool SameReturnType = Context.hasSameType(Old->getDeclaredReturnType(),
13001300
New->getDeclaredReturnType());

clang/lib/Sema/SemaTemplateInstantiate.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ Response HandleFunction(const FunctionDecl *Function,
208208
return Response::UseNextDecl(Function);
209209
}
210210

211+
Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD) {
212+
return Response::ChangeDecl(FTD->getLexicalDeclContext());
213+
}
214+
211215
Response HandleRecordDecl(const CXXRecordDecl *Rec,
212216
MultiLevelTemplateArgumentList &Result,
213217
ASTContext &Context,
@@ -318,6 +322,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
318322
} else if (const auto *CSD =
319323
dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) {
320324
R = HandleImplicitConceptSpecializationDecl(CSD, Result);
325+
} else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(CurDecl)) {
326+
R = HandleFunctionTemplateDecl(FTD);
321327
} else if (!isa<DeclContext>(CurDecl)) {
322328
R = Response::DontClearRelativeToPrimaryNextDecl(CurDecl);
323329
if (CurDecl->getDeclContext()->isTranslationUnit()) {

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

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

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

@@ -1689,16 +1668,37 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
16891668
ClassTemplateDecl *Inst
16901669
= ClassTemplateDecl::Create(SemaRef.Context, DC, D->getLocation(),
16911670
D->getIdentifier(), InstParams, RecordInst);
1692-
assert(!(isFriend && Owner->isDependentContext()));
1693-
Inst->setPreviousDecl(PrevClassTemplate);
1694-
16951671
RecordInst->setDescribedClassTemplate(Inst);
16961672

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

17031703
Inst->setObjectOfFriendDecl();
17041704
// TODO: do we want to track the instantiation progeny of this
@@ -1709,15 +1709,15 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
17091709
Inst->setInstantiatedFromMemberTemplate(D);
17101710
}
17111711

1712+
Inst->setPreviousDecl(PrevClassTemplate);
1713+
17121714
// Trigger creation of the type for the instantiation.
1713-
SemaRef.Context.getInjectedClassNameType(RecordInst,
1714-
Inst->getInjectedClassNameSpecialization());
1715+
SemaRef.Context.getInjectedClassNameType(
1716+
RecordInst, Inst->getInjectedClassNameSpecialization());
17151717

17161718
// Finish handling of friends.
17171719
if (isFriend) {
17181720
DC->makeDeclVisibleInContext(Inst);
1719-
Inst->setLexicalDeclContext(Owner);
1720-
RecordInst->setLexicalDeclContext(Owner);
17211721
return Inst;
17221722
}
17231723

clang/test/SemaTemplate/concepts-out-of-line-def.cpp

+150
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,153 @@ static_assert(S<XY>::specialization("str") == SPECIALIZATION_CONCEPT);
127127
static_assert(S<int>::specialization("str") == SPECIALIZATION_REQUIRES);
128128

129129
} // namespace multiple_template_parameter_lists
130+
131+
static constexpr int CONSTRAINED_METHOD_1 = 1;
132+
static constexpr int CONSTRAINED_METHOD_2 = 2;
133+
134+
namespace constrained_members {
135+
136+
template <int>
137+
struct S {
138+
template <Concept C>
139+
static constexpr int constrained_method();
140+
};
141+
142+
template <>
143+
template <Concept C>
144+
constexpr int S<1>::constrained_method() { return CONSTRAINED_METHOD_1; }
145+
146+
template <>
147+
template <Concept C>
148+
constexpr int S<2>::constrained_method() { return CONSTRAINED_METHOD_2; }
149+
150+
static_assert(S<1>::constrained_method<XY>() == CONSTRAINED_METHOD_1);
151+
static_assert(S<2>::constrained_method<XY>() == CONSTRAINED_METHOD_2);
152+
153+
154+
template <class T1, class T2>
155+
concept ConceptT1T2 = true;
156+
157+
template<typename T3>
158+
struct S12 {
159+
template<ConceptT1T2<T3> T4>
160+
static constexpr int constrained_method();
161+
};
162+
163+
template<>
164+
template<ConceptT1T2<int> T5>
165+
constexpr int S12<int>::constrained_method() { return CONSTRAINED_METHOD_1; }
166+
167+
template<>
168+
template<ConceptT1T2<double> T5>
169+
constexpr int S12<double>::constrained_method() { return CONSTRAINED_METHOD_2; }
170+
171+
static_assert(S12<int>::constrained_method<XY>() == CONSTRAINED_METHOD_1);
172+
static_assert(S12<double>::constrained_method<XY>() == CONSTRAINED_METHOD_2);
173+
174+
} // namespace constrained members
175+
176+
namespace constrained_members_of_nested_types {
177+
178+
template <int>
179+
struct S {
180+
struct Inner0 {
181+
struct Inner1 {
182+
template <Concept C>
183+
static constexpr int constrained_method();
184+
};
185+
};
186+
};
187+
188+
template <>
189+
template <Concept C>
190+
constexpr int S<1>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_1; }
191+
192+
template <>
193+
template <Concept C>
194+
constexpr int S<2>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_2; }
195+
196+
static_assert(S<1>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_1);
197+
static_assert(S<2>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_2);
198+
199+
200+
template <class T1, class T2>
201+
concept ConceptT1T2 = true;
202+
203+
template<typename T3>
204+
struct S12 {
205+
struct Inner0 {
206+
struct Inner1 {
207+
template<ConceptT1T2<T3> T4>
208+
static constexpr int constrained_method();
209+
};
210+
};
211+
};
212+
213+
template<>
214+
template<ConceptT1T2<int> T5>
215+
constexpr int S12<int>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_1; }
216+
217+
template<>
218+
template<ConceptT1T2<double> T5>
219+
constexpr int S12<double>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_2; }
220+
221+
static_assert(S12<int>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_1);
222+
static_assert(S12<double>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_2);
223+
224+
} // namespace constrained_members_of_nested_types
225+
226+
namespace constrained_member_sfinae {
227+
228+
template<int N> struct S {
229+
template<class T>
230+
static constexpr int constrained_method() requires (sizeof(int[N * 1073741824 + 4]) == 16) {
231+
return CONSTRAINED_METHOD_1;
232+
}
233+
234+
template<class T>
235+
static constexpr int constrained_method() requires (sizeof(int[N]) == 16);
236+
};
237+
238+
template<>
239+
template<typename T>
240+
constexpr int S<4>::constrained_method() requires (sizeof(int[4]) == 16) {
241+
return CONSTRAINED_METHOD_2;
242+
}
243+
244+
// Verify that there is no amiguity in this case.
245+
static_assert(S<4>::constrained_method<double>() == CONSTRAINED_METHOD_2);
246+
247+
} // namespace constrained_member_sfinae
248+
249+
namespace requires_expression_references_members {
250+
251+
void accept1(int x);
252+
void accept2(XY xy);
253+
254+
template <class T> struct S {
255+
T Field = T();
256+
257+
constexpr int constrained_method()
258+
requires requires { accept1(Field); };
259+
260+
constexpr int constrained_method()
261+
requires requires { accept2(Field); };
262+
};
263+
264+
template <class T>
265+
constexpr int S<T>::constrained_method()
266+
requires requires { accept1(Field); } {
267+
return CONSTRAINED_METHOD_1;
268+
}
269+
270+
template <class T>
271+
constexpr int S<T>::constrained_method()
272+
requires requires { accept2(Field); } {
273+
return CONSTRAINED_METHOD_2;
274+
}
275+
276+
static_assert(S<int>().constrained_method() == CONSTRAINED_METHOD_1);
277+
static_assert(S<XY>().constrained_method() == CONSTRAINED_METHOD_2);
278+
279+
} // namespace requires_expression_references_members

clang/test/SemaTemplate/concepts.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -816,3 +816,12 @@ static_assert(Parent<int>::TakesBinary<int, 0>::i == 0);
816816
static_assert(Parent<int>::TakesBinary<int, 0ULL>::i == 0);
817817
}
818818

819+
namespace TemplateInsideNonTemplateClass {
820+
template<typename T, typename U> concept C = true;
821+
822+
template<typename T> auto L = []<C<T> U>() {};
823+
824+
struct Q {
825+
template<C<int> U> friend constexpr auto decltype(L<int>)::operator()() const;
826+
};
827+
} // namespace TemplateInsideNonTemplateClass

clang/www/cxx_status.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -947,9 +947,6 @@ <h2 id="cxx20">C++20 implementation status</h2>
947947
<tr>
948948
<td><a href="https://wg21.link/p1980r0">P1980R0</a></td>
949949
</tr>
950-
<tr> <!-- from Prague -->
951-
<td><a href="https://wg21.link/p2103r0">P2103R0</a></td>
952-
</tr>
953950
<tr> <!-- from February 2022 -->
954951
<td><a href="https://wg21.link/p2493r0">P2493R0</a></td>
955952
</tr>
@@ -961,6 +958,9 @@ <h2 id="cxx20">C++20 implementation status</h2>
961958
<td><a href="https://wg21.link/p2113r0">P2113R0</a></td>
962959
<td rowspan="1" class="full" align="center">Clang 16</td>
963960
</tr>
961+
<tr>
962+
<td><a href="https://wg21.link/p2103r0">P2103R0</a></td>
963+
</tr>
964964
<!-- Albuquerque papers -->
965965
<tr>
966966
<td>Range-based for statements with initializer</td>

0 commit comments

Comments
 (0)