clang 20.0.0git
SemaConcept.h
Go to the documentation of this file.
1//===-- SemaConcept.h - Semantic Analysis for Constraints and Concepts ----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9// This file provides semantic analysis for C++ constraints and concepts.
10///
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_CLANG_SEMA_SEMACONCEPT_H
14#define LLVM_CLANG_SEMA_SEMACONCEPT_H
17#include "clang/AST/Expr.h"
20#include "llvm/ADT/PointerUnion.h"
21#include "llvm/ADT/SmallVector.h"
22#include <optional>
23#include <string>
24#include <utility>
25
26namespace clang {
27class Sema;
28
30
31struct alignas(ConstraintAlignment) AtomicConstraint {
34 std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping;
35
36 AtomicConstraint(const Expr *ConstraintExpr, NamedDecl *ConstraintDecl)
37 : ConstraintExpr(ConstraintExpr), ConstraintDecl(ConstraintDecl) {};
38
40 const AtomicConstraint &Other) const {
41 if (!ParameterMapping != !Other.ParameterMapping)
42 return false;
43 if (!ParameterMapping)
44 return true;
45 if (ParameterMapping->size() != Other.ParameterMapping->size())
46 return false;
47
48 for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) {
49 llvm::FoldingSetNodeID IDA, IDB;
50 C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
51 .Profile(IDA, C);
52 C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument())
53 .Profile(IDB, C);
54 if (IDA != IDB)
55 return false;
56 }
57 return true;
58 }
59
60 bool subsumes(ASTContext &C, const AtomicConstraint &Other) const {
61 // C++ [temp.constr.order] p2
62 // - an atomic constraint A subsumes another atomic constraint B
63 // if and only if the A and B are identical [...]
64 //
65 // C++ [temp.constr.atomic] p2
66 // Two atomic constraints are identical if they are formed from the
67 // same expression and the targets of the parameter mappings are
68 // equivalent according to the rules for expressions [...]
69
70 // We do not actually substitute the parameter mappings into the
71 // constraint expressions, therefore the constraint expressions are
72 // the originals, and comparing them will suffice.
73 if (ConstraintExpr != Other.ConstraintExpr)
74 return false;
75
76 // Check that the parameter lists are identical
77 return hasMatchingParameterMapping(C, Other);
78 }
79};
80
81struct alignas(ConstraintAlignment) FoldExpandedConstraint;
82
84 llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *>;
88
89// A constraint is in conjunctive normal form when it is a conjunction of
90// clauses where each clause is a disjunction of atomic constraints. For atomic
91// constraints A, B, and C, the constraint A  ∧ (B  ∨ C) is in conjunctive
92// normal form.
93NormalForm makeCNF(const NormalizedConstraint &Normalized);
94
95// A constraint is in disjunctive normal form when it is a disjunction of
96// clauses where each clause is a conjunction of atomic constraints. For atomic
97// constraints A, B, and C, the disjunctive normal form of the constraint A
98//  ∧ (B  ∨ C) is (A  ∧ B)  ∨ (A  ∧ C).
99NormalForm makeDNF(const NormalizedConstraint &Normalized);
100
101struct alignas(ConstraintAlignment) NormalizedConstraintPair;
102
103/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
104/// either an atomic constraint, a conjunction of normalized constraints or a
105/// disjunction of normalized constraints.
107 friend class Sema;
108
109 enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };
110
111 using CompoundConstraint = llvm::PointerIntPair<NormalizedConstraintPair *, 1,
113
114 llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
117
120
122 NormalizedConstraint RHS, CompoundConstraintKind Kind);
123
126 Constraint(Other.Constraint) {
127 Other.Constraint = nullptr;
128 }
131 if (&Other != this) {
132 NormalizedConstraint Temp(std::move(Other));
133 std::swap(Constraint, Temp.Constraint);
134 }
135 return *this;
136 }
137
138 bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); }
139 bool isFoldExpanded() const {
140 return Constraint.is<FoldExpandedConstraint *>();
141 }
142 bool isCompound() const { return Constraint.is<CompoundConstraint>(); }
143
145 assert(isCompound() && "getCompoundKind on a non-compound constraint..");
146 return Constraint.get<CompoundConstraint>().getInt();
147 }
148
149 NormalizedConstraint &getLHS() const;
150 NormalizedConstraint &getRHS() const;
151
153 assert(isAtomic() &&
154 "getAtomicConstraint called on non-atomic constraint.");
155 return Constraint.get<AtomicConstraint *>();
156 }
157
159 assert(isFoldExpanded() &&
160 "getFoldExpandedConstraint called on non-fold-expanded constraint.");
161 return Constraint.get<FoldExpandedConstraint *>();
162 }
163
164private:
165 static std::optional<NormalizedConstraint>
166 fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
167 static std::optional<NormalizedConstraint>
168 fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E);
169};
170
171struct alignas(ConstraintAlignment) NormalizedConstraintPair {
173};
174
175struct alignas(ConstraintAlignment) FoldExpandedConstraint {
176 enum class FoldOperatorKind { And, Or } Kind;
178 const Expr *Pattern;
179
181 const Expr *Pattern)
182 : Kind(K), Constraint(std::move(C)), Pattern(Pattern) {};
183
184 template <typename AtomicSubsumptionEvaluator>
185 bool subsumes(const FoldExpandedConstraint &Other,
186 const AtomicSubsumptionEvaluator &E) const;
187
188 static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A,
189 const FoldExpandedConstraint &B);
190};
191
192const NormalizedConstraint *getNormalizedAssociatedConstraints(
193 Sema &S, NamedDecl *ConstrainedDecl,
194 ArrayRef<const Expr *> AssociatedConstraints);
195
196template <typename AtomicSubsumptionEvaluator>
197bool subsumes(const NormalForm &PDNF, const NormalForm &QCNF,
198 const AtomicSubsumptionEvaluator &E) {
199 // C++ [temp.constr.order] p2
200 // Then, P subsumes Q if and only if, for every disjunctive clause Pi in the
201 // disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in
202 // the conjuctive normal form of Q, where [...]
203 for (const auto &Pi : PDNF) {
204 for (const auto &Qj : QCNF) {
205 // C++ [temp.constr.order] p2
206 // - [...] a disjunctive clause Pi subsumes a conjunctive clause Qj if
207 // and only if there exists an atomic constraint Pia in Pi for which
208 // there exists an atomic constraint, Qjb, in Qj such that Pia
209 // subsumes Qjb.
210 bool Found = false;
211 for (NormalFormConstraint Pia : Pi) {
212 for (NormalFormConstraint Qjb : Qj) {
213 if (Pia.is<FoldExpandedConstraint *>() &&
214 Qjb.is<FoldExpandedConstraint *>()) {
215 if (Pia.get<FoldExpandedConstraint *>()->subsumes(
216 *Qjb.get<FoldExpandedConstraint *>(), E)) {
217 Found = true;
218 break;
219 }
220 } else if (Pia.is<AtomicConstraint *>() &&
221 Qjb.is<AtomicConstraint *>()) {
222 if (E(*Pia.get<AtomicConstraint *>(),
223 *Qjb.get<AtomicConstraint *>())) {
224 Found = true;
225 break;
226 }
227 }
228 }
229 if (Found)
230 break;
231 }
232 if (!Found)
233 return false;
234 }
235 }
236 return true;
237}
238
239template <typename AtomicSubsumptionEvaluator>
241 ArrayRef<const Expr *> Q, bool &Subsumes,
242 const AtomicSubsumptionEvaluator &E) {
243 // C++ [temp.constr.order] p2
244 // In order to determine if a constraint P subsumes a constraint Q, P is
245 // transformed into disjunctive normal form, and Q is transformed into
246 // conjunctive normal form. [...]
247 const NormalizedConstraint *PNormalized =
249 if (!PNormalized)
250 return true;
251 NormalForm PDNF = makeDNF(*PNormalized);
252
253 const NormalizedConstraint *QNormalized =
255 if (!QNormalized)
256 return true;
257 NormalForm QCNF = makeCNF(*QNormalized);
258
259 Subsumes = subsumes(PDNF, QCNF, E);
260 return false;
261}
262
263template <typename AtomicSubsumptionEvaluator>
264bool FoldExpandedConstraint::subsumes(
265 const FoldExpandedConstraint &Other,
266 const AtomicSubsumptionEvaluator &E) const {
267
268 // [C++26] [temp.constr.order]
269 // a fold expanded constraint A subsumes another fold expanded constraint B if
270 // they are compatible for subsumption, have the same fold-operator, and the
271 // constraint of A subsumes that of B
272
273 if (Kind != Other.Kind || !AreCompatibleForSubsumption(*this, Other))
274 return false;
275
276 NormalForm PDNF = makeDNF(this->Constraint);
277 NormalForm QCNF = makeCNF(Other.Constraint);
278 return clang::subsumes(PDNF, QCNF, E);
279}
280
281} // clang
282
283#endif // LLVM_CLANG_SEMA_SEMACONCEPT_H
This file provides AST data structures related to concepts.
Defines the clang::ASTContext interface.
StringRef P
const Decl * D
enum clang::sema::@1653::IndirectLocalPathEntry::EntryKind Kind
Expr * E
Defines the C++ template declaration subclasses.
Defines the clang::SourceLocation class and associated facilities.
static const TemplateArgument & getArgument(const TemplateArgument &A)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:187
This represents one expression.
Definition: Expr.h:110
This represents a decl that may have a name.
Definition: Decl.h:249
Sema - This implements semantic analysis and AST building for C.
Definition: Sema.h:493
The JSON file list parser is used to communicate input to InstallAPI.
NormalForm makeCNF(const NormalizedConstraint &Normalized)
llvm::PointerUnion< AtomicConstraint *, FoldExpandedConstraint * > NormalFormConstraint
Definition: SemaConcept.h:84
NormalForm makeDNF(const NormalizedConstraint &Normalized)
bool subsumes(const NormalForm &PDNF, const NormalForm &QCNF, const AtomicSubsumptionEvaluator &E)
Definition: SemaConcept.h:197
const NormalizedConstraint * getNormalizedAssociatedConstraints(Sema &S, NamedDecl *ConstrainedDecl, ArrayRef< const Expr * > AssociatedConstraints)
@ ConstraintAlignment
Definition: SemaConcept.h:29
@ Other
Other implicit parameter.
bool subsumes(ASTContext &C, const AtomicConstraint &Other) const
Definition: SemaConcept.h:60
AtomicConstraint(const Expr *ConstraintExpr, NamedDecl *ConstraintDecl)
Definition: SemaConcept.h:36
NamedDecl * ConstraintDecl
Definition: SemaConcept.h:33
std::optional< ArrayRef< TemplateArgumentLoc > > ParameterMapping
Definition: SemaConcept.h:34
bool hasMatchingParameterMapping(ASTContext &C, const AtomicConstraint &Other) const
Definition: SemaConcept.h:39
const Expr * ConstraintExpr
Definition: SemaConcept.h:32
NormalizedConstraint Constraint
Definition: SemaConcept.h:177
bool subsumes(const FoldExpandedConstraint &Other, const AtomicSubsumptionEvaluator &E) const
Definition: SemaConcept.h:264
FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C, const Expr *Pattern)
Definition: SemaConcept.h:180
NormalizedConstraint LHS
Definition: SemaConcept.h:172
A normalized constraint, as defined in C++ [temp.constr.normal], is either an atomic constraint,...
Definition: SemaConcept.h:106
llvm::PointerUnion< AtomicConstraint *, FoldExpandedConstraint *, CompoundConstraint > Constraint
Definition: SemaConcept.h:116
llvm::PointerIntPair< NormalizedConstraintPair *, 1, CompoundConstraintKind > CompoundConstraint
Definition: SemaConcept.h:112
AtomicConstraint * getAtomicConstraint() const
Definition: SemaConcept.h:152
NormalizedConstraint(NormalizedConstraint &&Other)
Definition: SemaConcept.h:125
NormalizedConstraint(AtomicConstraint *C)
Definition: SemaConcept.h:118
NormalizedConstraint & operator=(const NormalizedConstraint &Other)=delete
NormalizedConstraint & operator=(NormalizedConstraint &&Other)
Definition: SemaConcept.h:130
CompoundConstraintKind getCompoundKind() const
Definition: SemaConcept.h:144
NormalizedConstraint(FoldExpandedConstraint *C)
Definition: SemaConcept.h:119
FoldExpandedConstraint * getFoldExpandedConstraint() const
Definition: SemaConcept.h:158