Skip to content

Commit 6ba9bbe

Browse files
committed
[clang] Concepts: support pack expansions for type constraints
This reverts an earlier attempt to support these expansions, which was limited to type arguments and which subverted the purpose of SubstTemplateTypeParmType. This propagates the ArgumentPackSubstitutionIndex along with the AssociatedConstraint, so that the pack expansion works, without needing any new transforms or otherwise any changes to the template instanntiation process. This keeps the tests from the reverted commits, and adds a few more showing the new solution also works for NTTPs. This patch is incomplete: * It is likely missing plumbing the ArgumentPackSubstitutionIndex into more places. * The Normalization cache is not adapted so it indexes on the ArgumentPackSubstitutionIndex as well. One new test is added, which in any case shows an ambiguous call, but if the normalization cache were corrected, the reason for ambighuity would change from subsumption both ways, to neither subsumes the other. I am not sure if the current language rules would allow for a test case where the pack index would break an ambiguity, this is left for future consideration. Fixes #131798
1 parent 28d2240 commit 6ba9bbe

21 files changed

+222
-115
lines changed

clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ static std::vector<FixItHint> handleReturnType(const FunctionDecl *Function,
356356
if (!TypeText)
357357
return {};
358358

359-
SmallVector<const Expr *, 3> ExistingConstraints;
359+
SmallVector<AssociatedConstraint, 3> ExistingConstraints;
360360
Function->getAssociatedConstraints(ExistingConstraints);
361361
if (!ExistingConstraints.empty()) {
362362
// FIXME - Support adding new constraints to existing ones. Do we need to
@@ -404,7 +404,7 @@ handleTrailingTemplateType(const FunctionTemplateDecl *FunctionTemplate,
404404
if (!ConditionText)
405405
return {};
406406

407-
SmallVector<const Expr *, 3> ExistingConstraints;
407+
SmallVector<AssociatedConstraint, 3> ExistingConstraints;
408408
Function->getAssociatedConstraints(ExistingConstraints);
409409
if (!ExistingConstraints.empty()) {
410410
// FIXME - Support adding new constraints to existing ones. Do we need to

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,9 @@ Bug Fixes to C++ Support
370370
- Clang now uses the parameter location for abbreviated function templates in ``extern "C"``. (#GH46386)
371371
- Clang will emit an error instead of crash when use co_await or co_yield in
372372
C++26 braced-init-list template parameter initialization. (#GH78426)
373+
- Improved fix for an issue with pack expansions of type constraints, where this
374+
now also works if the constraint has non-type or template template parameters.
375+
(#GH131798)
373376
- Fixes matching of nested template template parameters. (#GH130362)
374377
- Correctly diagnoses template template paramters which have a pack parameter
375378
not in the last position.

clang/include/clang/AST/ASTConcept.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,15 @@ class TypeConstraint {
229229
/// type-constraint.
230230
Expr *ImmediatelyDeclaredConstraint = nullptr;
231231
ConceptReference *ConceptRef;
232+
int ArgumentPackSubstitutionIndex;
232233

233234
public:
234235
TypeConstraint(ConceptReference *ConceptRef,
235-
Expr *ImmediatelyDeclaredConstraint)
236+
Expr *ImmediatelyDeclaredConstraint,
237+
int ArgumentPackSubstitutionIndex)
236238
: ImmediatelyDeclaredConstraint(ImmediatelyDeclaredConstraint),
237-
ConceptRef(ConceptRef) {}
239+
ConceptRef(ConceptRef),
240+
ArgumentPackSubstitutionIndex(ArgumentPackSubstitutionIndex) {}
238241

239242
/// \brief Get the immediately-declared constraint expression introduced by
240243
/// this type-constraint, that is - the constraint expression that is added to
@@ -245,6 +248,10 @@ class TypeConstraint {
245248

246249
ConceptReference *getConceptReference() const { return ConceptRef; }
247250

251+
int getArgumentPackSubstitutionIndex() const {
252+
return ArgumentPackSubstitutionIndex;
253+
}
254+
248255
// FIXME: Instead of using these concept related functions the callers should
249256
// directly work with the corresponding ConceptReference.
250257
ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }

clang/include/clang/AST/Decl.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ class UnresolvedSetImpl;
7878
class VarTemplateDecl;
7979
enum class ImplicitParamKind;
8080

81+
// Holds a constraint expression along with a pack expansion index, if
82+
// expanded.
83+
struct AssociatedConstraint {
84+
const Expr *ConstraintExpr;
85+
int ArgumentPackSubstitutionIndex;
86+
87+
explicit AssociatedConstraint(const Expr *ConstraintExpr,
88+
int ArgumentPackSubstitutionIndex = -1)
89+
: ConstraintExpr(ConstraintExpr),
90+
ArgumentPackSubstitutionIndex(ArgumentPackSubstitutionIndex) {}
91+
};
92+
8193
/// The top declaration context.
8294
class TranslationUnitDecl : public Decl,
8395
public DeclContext,
@@ -2631,9 +2643,10 @@ class FunctionDecl : public DeclaratorDecl,
26312643
///
26322644
/// Use this instead of getTrailingRequiresClause for concepts APIs that
26332645
/// accept an ArrayRef of constraint expressions.
2634-
void getAssociatedConstraints(SmallVectorImpl<const Expr *> &AC) const {
2646+
void
2647+
getAssociatedConstraints(SmallVectorImpl<AssociatedConstraint> &AC) const {
26352648
if (auto *TRC = getTrailingRequiresClause())
2636-
AC.push_back(TRC);
2649+
AC.emplace_back(TRC);
26372650
}
26382651

26392652
/// Get the message that indicates why this function was deleted.

clang/include/clang/AST/DeclTemplate.h

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ class TemplateParameterList final
195195
///
196196
/// The constraints in the resulting list are to be treated as if in a
197197
/// conjunction ("and").
198-
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const;
198+
void getAssociatedConstraints(
199+
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const;
199200

200201
bool hasAssociatedConstraints() const;
201202

@@ -422,7 +423,8 @@ class TemplateDecl : public NamedDecl {
422423
/// including constraint-expressions derived from the requires-clause,
423424
/// trailing requires-clause (for functions and methods) and constrained
424425
/// template parameters.
425-
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const;
426+
void getAssociatedConstraints(
427+
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const;
426428

427429
bool hasAssociatedConstraints() const;
428430

@@ -1341,7 +1343,8 @@ class TemplateTypeParmDecl final : public TypeDecl,
13411343
}
13421344

13431345
void setTypeConstraint(ConceptReference *CR,
1344-
Expr *ImmediatelyDeclaredConstraint);
1346+
Expr *ImmediatelyDeclaredConstraint,
1347+
int ArgumentPackSubstitutionIndex);
13451348

13461349
/// Determine whether this template parameter has a type-constraint.
13471350
bool hasTypeConstraint() const {
@@ -1353,9 +1356,11 @@ class TemplateTypeParmDecl final : public TypeDecl,
13531356
///
13541357
/// Use this instead of getTypeConstraint for concepts APIs that
13551358
/// accept an ArrayRef of constraint expressions.
1356-
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
1359+
void getAssociatedConstraints(
1360+
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const {
13571361
if (HasTypeConstraint)
1358-
AC.push_back(getTypeConstraint()->getImmediatelyDeclaredConstraint());
1362+
AC.emplace_back(getTypeConstraint()->getImmediatelyDeclaredConstraint(),
1363+
getTypeConstraint()->getArgumentPackSubstitutionIndex());
13591364
}
13601365

13611366
SourceRange getSourceRange() const override LLVM_READONLY;
@@ -1574,9 +1579,10 @@ class NonTypeTemplateParmDecl final
15741579
///
15751580
/// Use this instead of getPlaceholderImmediatelyDeclaredConstraint for
15761581
/// concepts APIs that accept an ArrayRef of constraint expressions.
1577-
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
1582+
void getAssociatedConstraints(
1583+
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const {
15781584
if (Expr *E = getPlaceholderTypeConstraint())
1579-
AC.push_back(E);
1585+
AC.emplace_back(E);
15801586
}
15811587

15821588
// Implement isa/cast/dyncast/etc.
@@ -2169,7 +2175,8 @@ class ClassTemplatePartialSpecializationDecl
21692175
///
21702176
/// The constraints in the resulting list are to be treated as if in a
21712177
/// conjunction ("and").
2172-
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
2178+
void getAssociatedConstraints(
2179+
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const {
21732180
TemplateParams->getAssociatedConstraints(AC);
21742181
}
21752182

@@ -2943,7 +2950,8 @@ class VarTemplatePartialSpecializationDecl
29432950
///
29442951
/// The constraints in the resulting list are to be treated as if in a
29452952
/// conjunction ("and").
2946-
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
2953+
void getAssociatedConstraints(
2954+
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const {
29472955
TemplateParams->getAssociatedConstraints(AC);
29482956
}
29492957

clang/include/clang/Sema/Sema.h

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14551,13 +14551,14 @@ class Sema final : public SemaBase {
1455114551
/// \returns true if an error occurred and satisfaction could not be checked,
1455214552
/// false otherwise.
1455314553
bool CheckConstraintSatisfaction(
14554-
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
14554+
const NamedDecl *Template,
14555+
ArrayRef<AssociatedConstraint> AssociatedConstraints,
1455514556
const MultiLevelTemplateArgumentList &TemplateArgLists,
1455614557
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
1455714558
llvm::SmallVector<Expr *, 4> Converted;
14558-
return CheckConstraintSatisfaction(Template, ConstraintExprs, Converted,
14559-
TemplateArgLists, TemplateIDRange,
14560-
Satisfaction);
14559+
return CheckConstraintSatisfaction(Template, AssociatedConstraints,
14560+
Converted, TemplateArgLists,
14561+
TemplateIDRange, Satisfaction);
1456114562
}
1456214563

1456314564
/// \brief Check whether the given list of constraint expressions are
@@ -14583,7 +14584,8 @@ class Sema final : public SemaBase {
1458314584
/// \returns true if an error occurred and satisfaction could not be checked,
1458414585
/// false otherwise.
1458514586
bool CheckConstraintSatisfaction(
14586-
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
14587+
const NamedDecl *Template,
14588+
ArrayRef<AssociatedConstraint> AssociatedConstraints,
1458714589
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
1458814590
const MultiLevelTemplateArgumentList &TemplateArgList,
1458914591
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
@@ -14661,7 +14663,7 @@ class Sema final : public SemaBase {
1466114663

1466214664
const NormalizedConstraint *getNormalizedAssociatedConstraints(
1466314665
const NamedDecl *ConstrainedDecl,
14664-
ArrayRef<const Expr *> AssociatedConstraints);
14666+
ArrayRef<AssociatedConstraint> AssociatedConstraints);
1466514667

1466614668
/// \brief Check whether the given declaration's associated constraints are
1466714669
/// at least as constrained than another declaration's according to the
@@ -14672,17 +14674,18 @@ class Sema final : public SemaBase {
1467214674
///
1467314675
/// \returns true if an error occurred, false otherwise.
1467414676
bool IsAtLeastAsConstrained(const NamedDecl *D1,
14675-
MutableArrayRef<const Expr *> AC1,
14677+
MutableArrayRef<AssociatedConstraint> AC1,
1467614678
const NamedDecl *D2,
14677-
MutableArrayRef<const Expr *> AC2, bool &Result);
14679+
MutableArrayRef<AssociatedConstraint> AC2,
14680+
bool &Result);
1467814681

1467914682
/// If D1 was not at least as constrained as D2, but would've been if a pair
1468014683
/// of atomic constraints involved had been declared in a concept and not
1468114684
/// repeated in two separate places in code.
1468214685
/// \returns true if such a diagnostic was emitted, false otherwise.
1468314686
bool MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
14684-
const NamedDecl *D1, ArrayRef<const Expr *> AC1, const NamedDecl *D2,
14685-
ArrayRef<const Expr *> AC2);
14687+
const NamedDecl *D1, ArrayRef<AssociatedConstraint> AC1,
14688+
const NamedDecl *D2, ArrayRef<AssociatedConstraint> AC2);
1468614689

1468714690
private:
1468814691
/// Caches pairs of template-like decls whose associated constraints were

clang/include/clang/Sema/SemaConcept.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ struct NormalizedConstraint {
114114

115115
private:
116116
static std::optional<NormalizedConstraint>
117-
fromConstraintExprs(Sema &S, const NamedDecl *D, ArrayRef<const Expr *> E);
117+
fromAssociatedConstraints(Sema &S, const NamedDecl *D,
118+
ArrayRef<AssociatedConstraint> ACs);
118119
static std::optional<NormalizedConstraint>
119120
fromConstraintExpr(Sema &S, const NamedDecl *D, const Expr *E);
120121
};
@@ -138,7 +139,7 @@ struct alignas(ConstraintAlignment) FoldExpandedConstraint {
138139

139140
const NormalizedConstraint *getNormalizedAssociatedConstraints(
140141
Sema &S, const NamedDecl *ConstrainedDecl,
141-
ArrayRef<const Expr *> AssociatedConstraints);
142+
ArrayRef<AssociatedConstraint> AssociatedConstraints);
142143

143144
/// \brief SubsumptionChecker establishes subsumption
144145
/// between two set of constraints.
@@ -149,8 +150,10 @@ class SubsumptionChecker {
149150

150151
SubsumptionChecker(Sema &SemaRef, SubsumptionCallable Callable = {});
151152

152-
std::optional<bool> Subsumes(const NamedDecl *DP, ArrayRef<const Expr *> P,
153-
const NamedDecl *DQ, ArrayRef<const Expr *> Q);
153+
std::optional<bool> Subsumes(const NamedDecl *DP,
154+
ArrayRef<AssociatedConstraint> P,
155+
const NamedDecl *DQ,
156+
ArrayRef<AssociatedConstraint> Q);
154157

155158
bool Subsumes(const NormalizedConstraint *P, const NormalizedConstraint *Q);
156159

clang/lib/AST/ASTImporter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5975,7 +5975,8 @@ ASTNodeImporter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
59755975
if (Err)
59765976
return std::move(Err);
59775977

5978-
ToD->setTypeConstraint(ToConceptRef, ToIDC);
5978+
ToD->setTypeConstraint(ToConceptRef, ToIDC,
5979+
TC->getArgumentPackSubstitutionIndex());
59795980
}
59805981

59815982
if (Error Err = importTemplateParameterDefaultArgument(D, ToD))

clang/lib/AST/DeclTemplate.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -223,20 +223,21 @@ static bool AdoptTemplateParameterList(TemplateParameterList *Params,
223223
return Invalid;
224224
}
225225

226-
void TemplateParameterList::
227-
getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
226+
void TemplateParameterList::getAssociatedConstraints(
227+
llvm::SmallVectorImpl<AssociatedConstraint> &ACs) const {
228228
if (HasConstrainedParameters)
229229
for (const NamedDecl *Param : *this) {
230230
if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {
231231
if (const auto *TC = TTP->getTypeConstraint())
232-
AC.push_back(TC->getImmediatelyDeclaredConstraint());
232+
ACs.emplace_back(TC->getImmediatelyDeclaredConstraint(),
233+
TC->getArgumentPackSubstitutionIndex());
233234
} else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
234235
if (const Expr *E = NTTP->getPlaceholderTypeConstraint())
235-
AC.push_back(E);
236+
ACs.emplace_back(E);
236237
}
237238
}
238239
if (HasRequiresClause)
239-
AC.push_back(getRequiresClause());
240+
ACs.emplace_back(getRequiresClause());
240241
}
241242

242243
bool TemplateParameterList::hasAssociatedConstraints() const {
@@ -286,12 +287,12 @@ TemplateDecl::TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L,
286287

287288
void TemplateDecl::anchor() {}
288289

289-
void TemplateDecl::
290-
getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
291-
TemplateParams->getAssociatedConstraints(AC);
290+
void TemplateDecl::getAssociatedConstraints(
291+
llvm::SmallVectorImpl<AssociatedConstraint> &ACs) const {
292+
TemplateParams->getAssociatedConstraints(ACs);
292293
if (auto *FD = dyn_cast_or_null<FunctionDecl>(getTemplatedDecl()))
293294
if (const Expr *TRC = FD->getTrailingRequiresClause())
294-
AC.push_back(TRC);
295+
ACs.emplace_back(TRC);
295296
}
296297

297298
bool TemplateDecl::hasAssociatedConstraints() const {
@@ -748,14 +749,15 @@ bool TemplateTypeParmDecl::isParameterPack() const {
748749
}
749750

750751
void TemplateTypeParmDecl::setTypeConstraint(
751-
ConceptReference *Loc, Expr *ImmediatelyDeclaredConstraint) {
752+
ConceptReference *Loc, Expr *ImmediatelyDeclaredConstraint,
753+
int ArgumentPackSubstitutionIndex) {
752754
assert(HasTypeConstraint &&
753755
"HasTypeConstraint=true must be passed at construction in order to "
754756
"call setTypeConstraint");
755757
assert(!TypeConstraintInitialized &&
756758
"TypeConstraint was already initialized!");
757-
new (getTrailingObjects<TypeConstraint>())
758-
TypeConstraint(Loc, ImmediatelyDeclaredConstraint);
759+
new (getTrailingObjects<TypeConstraint>()) TypeConstraint(
760+
Loc, ImmediatelyDeclaredConstraint, ArgumentPackSubstitutionIndex);
759761
TypeConstraintInitialized = true;
760762
}
761763

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5463,8 +5463,9 @@ class ConceptInfo {
54635463
// that T is attached to in order to gather the relevant constraints.
54645464
ConceptInfo(const TemplateTypeParmType &BaseType, Scope *S) {
54655465
auto *TemplatedEntity = getTemplatedEntity(BaseType.getDecl(), S);
5466-
for (const Expr *E : constraintsForTemplatedEntity(TemplatedEntity))
5467-
believe(E, &BaseType);
5466+
for (const AssociatedConstraint &AC :
5467+
constraintsForTemplatedEntity(TemplatedEntity))
5468+
believe(AC.ConstraintExpr, &BaseType);
54685469
}
54695470

54705471
std::vector<Member> members() {
@@ -5696,9 +5697,9 @@ class ConceptInfo {
56965697

56975698
// Gets all the type constraint expressions that might apply to the type
56985699
// variables associated with DC (as returned by getTemplatedEntity()).
5699-
static SmallVector<const Expr *, 1>
5700+
static SmallVector<AssociatedConstraint, 1>
57005701
constraintsForTemplatedEntity(DeclContext *DC) {
5701-
SmallVector<const Expr *, 1> Result;
5702+
SmallVector<AssociatedConstraint, 1> Result;
57025703
if (DC == nullptr)
57035704
return Result;
57045705
// Primary templates can have constraints.

0 commit comments

Comments
 (0)