26
26
namespace clang {
27
27
class Sema ;
28
28
29
- struct AtomicConstraint {
29
+ enum { ConstraintAlignment = 8 };
30
+
31
+ struct alignas (ConstraintAlignment) AtomicConstraint {
30
32
const Expr *ConstraintExpr;
31
33
std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping;
32
34
@@ -75,6 +77,28 @@ struct AtomicConstraint {
75
77
}
76
78
};
77
79
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
+
78
102
// / \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
79
103
// / either an atomic constraint, a conjunction of normalized constraints or a
80
104
// / disjunction of normalized constraints.
@@ -83,30 +107,20 @@ struct NormalizedConstraint {
83
107
84
108
enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };
85
109
86
- using CompoundConstraint = llvm::PointerIntPair<
87
- std::pair<NormalizedConstraint, NormalizedConstraint> *, 1 ,
88
- CompoundConstraintKind>;
110
+ using CompoundConstraint = llvm::PointerIntPair<NormalizedConstraintPair *, 1 ,
111
+ CompoundConstraintKind>;
89
112
90
- llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint;
113
+ llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
114
+ CompoundConstraint>
115
+ Constraint;
91
116
92
117
NormalizedConstraint (AtomicConstraint *C): Constraint{C} { };
118
+ NormalizedConstraint (FoldExpandedConstraint *C) : Constraint{C} {};
119
+
93
120
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);
110
124
NormalizedConstraint (NormalizedConstraint &&Other):
111
125
Constraint (Other.Constraint) {
112
126
Other.Constraint = nullptr ;
@@ -120,36 +134,149 @@ struct NormalizedConstraint {
120
134
return *this ;
121
135
}
122
136
123
- CompoundConstraintKind getCompoundKind () const {
124
- assert (!isAtomic () && " getCompoundKind called on atomic constraint." );
125
- return Constraint.get <CompoundConstraint>().getInt ();
126
- }
127
-
128
137
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 *>();
133
140
}
141
+ bool isCompound () const { return Constraint.is <CompoundConstraint>(); }
134
142
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 () ;
138
146
}
139
147
148
+ NormalizedConstraint &getLHS () const ;
149
+ NormalizedConstraint &getRHS () const ;
150
+
140
151
AtomicConstraint *getAtomicConstraint () const {
141
152
assert (isAtomic () &&
142
153
" getAtomicConstraint called on non-atomic constraint." );
143
154
return Constraint.get <AtomicConstraint *>();
144
155
}
145
156
157
+ FoldExpandedConstraint *getFoldExpandedConstraint () const {
158
+ assert (isFoldExpanded () &&
159
+ " getFoldExpandedConstraint called on non-fold-expanded constraint." );
160
+ return Constraint.get <FoldExpandedConstraint *>();
161
+ }
162
+
146
163
private:
147
164
static std::optional<NormalizedConstraint>
148
165
fromConstraintExprs (Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
149
166
static std::optional<NormalizedConstraint>
150
167
fromConstraintExpr (Sema &S, NamedDecl *D, const Expr *E);
151
168
};
152
169
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
+
153
280
} // clang
154
281
155
282
#endif // LLVM_CLANG_SEMA_SEMACONCEPT_H
0 commit comments