@@ -75,6 +75,26 @@ struct AtomicConstraint {
75
75
}
76
76
};
77
77
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
+
78
98
// / \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
79
99
// / either an atomic constraint, a conjunction of normalized constraints or a
80
100
// / disjunction of normalized constraints.
@@ -87,26 +107,17 @@ struct NormalizedConstraint {
87
107
std::pair<NormalizedConstraint, NormalizedConstraint> *, 1 ,
88
108
CompoundConstraintKind>;
89
109
90
- llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint;
110
+ llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
111
+ CompoundConstraint>
112
+ Constraint;
91
113
92
114
NormalizedConstraint (AtomicConstraint *C): Constraint{C} { };
115
+ NormalizedConstraint (FoldExpandedConstraint *C) : Constraint{C} {};
116
+
93
117
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);
110
121
NormalizedConstraint (NormalizedConstraint &&Other):
111
122
Constraint (Other.Constraint) {
112
123
Other.Constraint = nullptr ;
@@ -120,20 +131,24 @@ struct NormalizedConstraint {
120
131
return *this ;
121
132
}
122
133
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
+
123
140
CompoundConstraintKind getCompoundKind () const {
124
- assert (! isAtomic () && " getCompoundKind called on atomic constraint." );
141
+ assert (isCompound () && " getCompoundKind on a non-compound constraint. ." );
125
142
return Constraint.get <CompoundConstraint>().getInt ();
126
143
}
127
144
128
- bool isAtomic () const { return Constraint.is <AtomicConstraint *>(); }
129
-
130
145
NormalizedConstraint &getLHS () const {
131
- assert (! isAtomic () && " getLHS called on atomic constraint." );
146
+ assert (isCompound () && " getLHS called on a non-compound constraint." );
132
147
return Constraint.get <CompoundConstraint>().getPointer ()->first ;
133
148
}
134
149
135
150
NormalizedConstraint &getRHS () const {
136
- assert (! isAtomic () && " getRHS called on atomic constraint." );
151
+ assert (isCompound () && " getRHS called on a non-compound constraint." );
137
152
return Constraint.get <CompoundConstraint>().getPointer ()->second ;
138
153
}
139
154
@@ -143,13 +158,125 @@ struct NormalizedConstraint {
143
158
return Constraint.get <AtomicConstraint *>();
144
159
}
145
160
161
+ FoldExpandedConstraint *getFoldExpandedConstraint () const {
162
+ assert (isFoldExpanded () &&
163
+ " getFoldExpandedConstraint called on non-fold-expanded constraint." );
164
+ return Constraint.get <FoldExpandedConstraint *>();
165
+ }
166
+
146
167
private:
147
168
static std::optional<NormalizedConstraint>
148
169
fromConstraintExprs (Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
149
170
static std::optional<NormalizedConstraint>
150
171
fromConstraintExpr (Sema &S, NamedDecl *D, const Expr *E);
151
172
};
152
173
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
+
153
280
} // clang
154
281
155
282
#endif // LLVM_CLANG_SEMA_SEMACONCEPT_H
0 commit comments