Skip to content

Commit dc67030

Browse files
cor3ntinyuxuanchen1997
authored andcommitted
[Clang][C++26] Implement "Ordering of constraints involving fold expressions (#98160)
Implement https://isocpp.org/files/papers/P2963R3.pdf
1 parent 6968f43 commit dc67030

File tree

7 files changed

+842
-223
lines changed

7 files changed

+842
-223
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ C++2c Feature Support
278278

279279
- Implemented `P3144R2 Deleting a Pointer to an Incomplete Type Should be Ill-formed <https://wg21.link/P3144R2>`_.
280280

281+
- Implemented `P2963R3 Ordering of constraints involving fold expressions <https://wg21.link/P2963R3>`_.
282+
283+
281284
Resolutions to C++ Defect Reports
282285
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
283286
- Substitute template parameter pack, when it is not explicitly specified

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14078,6 +14078,11 @@ class Sema final : public SemaBase {
1407814078
const DeclarationNameInfo &NameInfo,
1407914079
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
1408014080

14081+
/// Collect the set of unexpanded parameter packs within the given
14082+
/// expression.
14083+
static void collectUnexpandedParameterPacks(
14084+
Expr *E, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
14085+
1408114086
/// Invoked when parsing a template argument followed by an
1408214087
/// ellipsis, which creates a pack expansion.
1408314088
///

clang/include/clang/Sema/SemaConcept.h

Lines changed: 149 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,26 @@ struct AtomicConstraint {
7575
}
7676
};
7777

78+
struct FoldExpandedConstraint;
79+
80+
using NormalFormConstraint =
81+
llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *>;
82+
struct NormalizedConstraint;
83+
using NormalForm =
84+
llvm::SmallVector<llvm::SmallVector<NormalFormConstraint, 2>, 4>;
85+
86+
// A constraint is in conjunctive normal form when it is a conjunction of
87+
// clauses where each clause is a disjunction of atomic constraints. For atomic
88+
// constraints A, B, and C, the constraint A  ∧ (B  ∨ C) is in conjunctive
89+
// normal form.
90+
NormalForm makeCNF(const NormalizedConstraint &Normalized);
91+
92+
// A constraint is in disjunctive normal form when it is a disjunction of
93+
// clauses where each clause is a conjunction of atomic constraints. For atomic
94+
// constraints A, B, and C, the disjunctive normal form of the constraint A
95+
//  ∧ (B  ∨ C) is (A  ∧ B)  ∨ (A  ∧ C).
96+
NormalForm makeDNF(const NormalizedConstraint &Normalized);
97+
7898
/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
7999
/// either an atomic constraint, a conjunction of normalized constraints or a
80100
/// disjunction of normalized constraints.
@@ -87,26 +107,17 @@ struct NormalizedConstraint {
87107
std::pair<NormalizedConstraint, NormalizedConstraint> *, 1,
88108
CompoundConstraintKind>;
89109

90-
llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint;
110+
llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
111+
CompoundConstraint>
112+
Constraint;
91113

92114
NormalizedConstraint(AtomicConstraint *C): Constraint{C} { };
115+
NormalizedConstraint(FoldExpandedConstraint *C) : Constraint{C} {};
116+
93117
NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS,
94-
NormalizedConstraint RHS, CompoundConstraintKind Kind)
95-
: Constraint{CompoundConstraint{
96-
new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
97-
std::move(LHS), std::move(RHS)}, Kind}} { };
98-
99-
NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other) {
100-
if (Other.isAtomic()) {
101-
Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint());
102-
} else {
103-
Constraint = CompoundConstraint(
104-
new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
105-
NormalizedConstraint(C, Other.getLHS()),
106-
NormalizedConstraint(C, Other.getRHS())},
107-
Other.getCompoundKind());
108-
}
109-
}
118+
NormalizedConstraint RHS, CompoundConstraintKind Kind);
119+
120+
NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other);
110121
NormalizedConstraint(NormalizedConstraint &&Other):
111122
Constraint(Other.Constraint) {
112123
Other.Constraint = nullptr;
@@ -120,20 +131,24 @@ struct NormalizedConstraint {
120131
return *this;
121132
}
122133

134+
bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); }
135+
bool isFoldExpanded() const {
136+
return Constraint.is<FoldExpandedConstraint *>();
137+
}
138+
bool isCompound() const { return Constraint.is<CompoundConstraint>(); }
139+
123140
CompoundConstraintKind getCompoundKind() const {
124-
assert(!isAtomic() && "getCompoundKind called on atomic constraint.");
141+
assert(isCompound() && "getCompoundKind on a non-compound constraint..");
125142
return Constraint.get<CompoundConstraint>().getInt();
126143
}
127144

128-
bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); }
129-
130145
NormalizedConstraint &getLHS() const {
131-
assert(!isAtomic() && "getLHS called on atomic constraint.");
146+
assert(isCompound() && "getLHS called on a non-compound constraint.");
132147
return Constraint.get<CompoundConstraint>().getPointer()->first;
133148
}
134149

135150
NormalizedConstraint &getRHS() const {
136-
assert(!isAtomic() && "getRHS called on atomic constraint.");
151+
assert(isCompound() && "getRHS called on a non-compound constraint.");
137152
return Constraint.get<CompoundConstraint>().getPointer()->second;
138153
}
139154

@@ -143,13 +158,125 @@ struct NormalizedConstraint {
143158
return Constraint.get<AtomicConstraint *>();
144159
}
145160

161+
FoldExpandedConstraint *getFoldExpandedConstraint() const {
162+
assert(isFoldExpanded() &&
163+
"getFoldExpandedConstraint called on non-fold-expanded constraint.");
164+
return Constraint.get<FoldExpandedConstraint *>();
165+
}
166+
146167
private:
147168
static std::optional<NormalizedConstraint>
148169
fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
149170
static std::optional<NormalizedConstraint>
150171
fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E);
151172
};
152173

174+
struct FoldExpandedConstraint {
175+
enum class FoldOperatorKind { And, Or } Kind;
176+
NormalizedConstraint Constraint;
177+
const Expr *Pattern;
178+
179+
FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C,
180+
const Expr *Pattern)
181+
: Kind(K), Constraint(std::move(C)), Pattern(Pattern) {};
182+
183+
template <typename AtomicSubsumptionEvaluator>
184+
bool subsumes(const FoldExpandedConstraint &Other,
185+
const AtomicSubsumptionEvaluator &E) const;
186+
187+
static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A,
188+
const FoldExpandedConstraint &B);
189+
};
190+
191+
const NormalizedConstraint *getNormalizedAssociatedConstraints(
192+
Sema &S, NamedDecl *ConstrainedDecl,
193+
ArrayRef<const Expr *> AssociatedConstraints);
194+
195+
template <typename AtomicSubsumptionEvaluator>
196+
bool subsumes(const NormalForm &PDNF, const NormalForm &QCNF,
197+
const AtomicSubsumptionEvaluator &E) {
198+
// C++ [temp.constr.order] p2
199+
// Then, P subsumes Q if and only if, for every disjunctive clause Pi in the
200+
// disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in
201+
// the conjuctive normal form of Q, where [...]
202+
for (const auto &Pi : PDNF) {
203+
for (const auto &Qj : QCNF) {
204+
// C++ [temp.constr.order] p2
205+
// - [...] a disjunctive clause Pi subsumes a conjunctive clause Qj if
206+
// and only if there exists an atomic constraint Pia in Pi for which
207+
// there exists an atomic constraint, Qjb, in Qj such that Pia
208+
// subsumes Qjb.
209+
bool Found = false;
210+
for (NormalFormConstraint Pia : Pi) {
211+
for (NormalFormConstraint Qjb : Qj) {
212+
if (Pia.is<FoldExpandedConstraint *>() &&
213+
Qjb.is<FoldExpandedConstraint *>()) {
214+
if (Pia.get<FoldExpandedConstraint *>()->subsumes(
215+
*Qjb.get<FoldExpandedConstraint *>(), E)) {
216+
Found = true;
217+
break;
218+
}
219+
} else if (Pia.is<AtomicConstraint *>() &&
220+
Qjb.is<AtomicConstraint *>()) {
221+
if (E(*Pia.get<AtomicConstraint *>(),
222+
*Qjb.get<AtomicConstraint *>())) {
223+
Found = true;
224+
break;
225+
}
226+
}
227+
}
228+
if (Found)
229+
break;
230+
}
231+
if (!Found)
232+
return false;
233+
}
234+
}
235+
return true;
236+
}
237+
238+
template <typename AtomicSubsumptionEvaluator>
239+
bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P, NamedDecl *DQ,
240+
ArrayRef<const Expr *> Q, bool &Subsumes,
241+
const AtomicSubsumptionEvaluator &E) {
242+
// C++ [temp.constr.order] p2
243+
// In order to determine if a constraint P subsumes a constraint Q, P is
244+
// transformed into disjunctive normal form, and Q is transformed into
245+
// conjunctive normal form. [...]
246+
const NormalizedConstraint *PNormalized =
247+
getNormalizedAssociatedConstraints(S, DP, P);
248+
if (!PNormalized)
249+
return true;
250+
NormalForm PDNF = makeDNF(*PNormalized);
251+
252+
const NormalizedConstraint *QNormalized =
253+
getNormalizedAssociatedConstraints(S, DQ, Q);
254+
if (!QNormalized)
255+
return true;
256+
NormalForm QCNF = makeCNF(*QNormalized);
257+
258+
Subsumes = subsumes(PDNF, QCNF, E);
259+
return false;
260+
}
261+
262+
template <typename AtomicSubsumptionEvaluator>
263+
bool FoldExpandedConstraint::subsumes(
264+
const FoldExpandedConstraint &Other,
265+
const AtomicSubsumptionEvaluator &E) const {
266+
267+
// [C++26] [temp.constr.order]
268+
// a fold expanded constraint A subsumes another fold expanded constraint B if
269+
// they are compatible for subsumption, have the same fold-operator, and the
270+
// constraint of A subsumes that of B
271+
272+
if (Kind != Other.Kind || !AreCompatibleForSubsumption(*this, Other))
273+
return false;
274+
275+
NormalForm PDNF = makeDNF(this->Constraint);
276+
NormalForm QCNF = makeCNF(Other.Constraint);
277+
return clang::subsumes(PDNF, QCNF, E);
278+
}
279+
153280
} // clang
154281

155282
#endif // LLVM_CLANG_SEMA_SEMACONCEPT_H

0 commit comments

Comments
 (0)