2626namespace clang {
2727class Sema ;
2828
29- struct AtomicConstraint {
29+ enum { ConstraintAlignment = 8 };
30+
31+ struct alignas (ConstraintAlignment) AtomicConstraint {
3032 const Expr *ConstraintExpr;
3133 std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping;
3234
@@ -75,6 +77,28 @@ struct AtomicConstraint {
7577 }
7678};
7779
80+ struct alignas (ConstraintAlignment) FoldExpandedConstraint;
81+
82+ using NormalFormConstraint =
83+ llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *>;
84+ struct NormalizedConstraint ;
85+ using NormalForm =
86+ llvm::SmallVector<llvm::SmallVector<NormalFormConstraint, 2 >, 4 >;
87+
88+ // A constraint is in conjunctive normal form when it is a conjunction of
89+ // clauses where each clause is a disjunction of atomic constraints. For atomic
90+ // constraints A, B, and C, the constraint A ∧ (B ∨ C) is in conjunctive
91+ // normal form.
92+ NormalForm makeCNF (const NormalizedConstraint &Normalized);
93+
94+ // A constraint is in disjunctive normal form when it is a disjunction of
95+ // clauses where each clause is a conjunction of atomic constraints. For atomic
96+ // constraints A, B, and C, the disjunctive normal form of the constraint A
97+ // ∧ (B ∨ C) is (A ∧ B) ∨ (A ∧ C).
98+ NormalForm makeDNF (const NormalizedConstraint &Normalized);
99+
100+ struct alignas (ConstraintAlignment) NormalizedConstraintPair;
101+
78102// / \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
79103// / either an atomic constraint, a conjunction of normalized constraints or a
80104// / disjunction of normalized constraints.
@@ -83,30 +107,20 @@ struct NormalizedConstraint {
83107
84108 enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };
85109
86- using CompoundConstraint = llvm::PointerIntPair<
87- std::pair<NormalizedConstraint, NormalizedConstraint> *, 1 ,
88- CompoundConstraintKind>;
110+ using CompoundConstraint = llvm::PointerIntPair<NormalizedConstraintPair *, 1 ,
111+ CompoundConstraintKind>;
89112
90- llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint;
113+ llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
114+ CompoundConstraint>
115+ Constraint;
91116
92117 NormalizedConstraint (AtomicConstraint *C): Constraint{C} { };
118+ NormalizedConstraint (FoldExpandedConstraint *C) : Constraint{C} {};
119+
93120 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- }
121+ NormalizedConstraint RHS, CompoundConstraintKind Kind);
122+
123+ NormalizedConstraint (ASTContext &C, const NormalizedConstraint &Other);
110124 NormalizedConstraint (NormalizedConstraint &&Other):
111125 Constraint (Other.Constraint) {
112126 Other.Constraint = nullptr ;
@@ -120,36 +134,149 @@ struct NormalizedConstraint {
120134 return *this ;
121135 }
122136
123- CompoundConstraintKind getCompoundKind () const {
124- assert (!isAtomic () && " getCompoundKind called on atomic constraint." );
125- return Constraint.get <CompoundConstraint>().getInt ();
126- }
127-
128137 bool isAtomic () const { return Constraint.is <AtomicConstraint *>(); }
129-
130- NormalizedConstraint &getLHS () const {
131- assert (!isAtomic () && " getLHS called on atomic constraint." );
132- return Constraint.get <CompoundConstraint>().getPointer ()->first ;
138+ bool isFoldExpanded () const {
139+ return Constraint.is <FoldExpandedConstraint *>();
133140 }
141+ bool isCompound () const { return Constraint.is <CompoundConstraint>(); }
134142
135- NormalizedConstraint & getRHS () const {
136- assert (! isAtomic () && " getRHS called on atomic constraint." );
137- return Constraint.get <CompoundConstraint>().getPointer ()-> second ;
143+ CompoundConstraintKind getCompoundKind () const {
144+ assert (isCompound () && " getCompoundKind on a non-compound constraint. ." );
145+ return Constraint.get <CompoundConstraint>().getInt () ;
138146 }
139147
148+ NormalizedConstraint &getLHS () const ;
149+ NormalizedConstraint &getRHS () const ;
150+
140151 AtomicConstraint *getAtomicConstraint () const {
141152 assert (isAtomic () &&
142153 " getAtomicConstraint called on non-atomic constraint." );
143154 return Constraint.get <AtomicConstraint *>();
144155 }
145156
157+ FoldExpandedConstraint *getFoldExpandedConstraint () const {
158+ assert (isFoldExpanded () &&
159+ " getFoldExpandedConstraint called on non-fold-expanded constraint." );
160+ return Constraint.get <FoldExpandedConstraint *>();
161+ }
162+
146163private:
147164 static std::optional<NormalizedConstraint>
148165 fromConstraintExprs (Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
149166 static std::optional<NormalizedConstraint>
150167 fromConstraintExpr (Sema &S, NamedDecl *D, const Expr *E);
151168};
152169
170+ struct alignas (ConstraintAlignment) NormalizedConstraintPair {
171+ NormalizedConstraint LHS, RHS;
172+ };
173+
174+ struct alignas (ConstraintAlignment) 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