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 |
15 | #include "clang/AST/ASTConcept.h" |
16 | #include "clang/AST/ASTContext.h" |
17 | #include "clang/AST/DeclTemplate.h" |
18 | #include "clang/AST/Expr.h" |
19 | #include "clang/Basic/SourceLocation.h" |
20 | #include "llvm/ADT/FoldingSet.h" |
21 | #include "llvm/ADT/PointerUnion.h" |
22 | #include "llvm/ADT/STLFunctionalExtras.h" |
23 | #include "llvm/ADT/SmallVector.h" |
24 | #include <optional> |
25 | #include <utility> |
26 | |
27 | namespace clang { |
28 | class Sema; |
29 | |
30 | enum { ConstraintAlignment = 8 }; |
31 | |
32 | struct alignas(ConstraintAlignment) AtomicConstraint { |
33 | const Expr *ConstraintExpr; |
34 | const NamedDecl *ConstraintDecl; |
35 | std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping; |
36 | |
37 | AtomicConstraint(const Expr *ConstraintExpr, const NamedDecl *ConstraintDecl) |
38 | : ConstraintExpr(ConstraintExpr), ConstraintDecl(ConstraintDecl) {}; |
39 | |
40 | bool hasMatchingParameterMapping(ASTContext &C, |
41 | const AtomicConstraint &Other) const { |
42 | if (!ParameterMapping != !Other.ParameterMapping) |
43 | return false; |
44 | if (!ParameterMapping) |
45 | return true; |
46 | if (ParameterMapping->size() != Other.ParameterMapping->size()) |
47 | return false; |
48 | |
49 | for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) { |
50 | llvm::FoldingSetNodeID IDA, IDB; |
51 | C.getCanonicalTemplateArgument(Arg: (*ParameterMapping)[I].getArgument()) |
52 | .Profile(ID&: IDA, Context: C); |
53 | C.getCanonicalTemplateArgument(Arg: (*Other.ParameterMapping)[I].getArgument()) |
54 | .Profile(ID&: IDB, Context: C); |
55 | if (IDA != IDB) |
56 | return false; |
57 | } |
58 | return true; |
59 | } |
60 | }; |
61 | |
62 | struct alignas(ConstraintAlignment) NormalizedConstraintPair; |
63 | struct alignas(ConstraintAlignment) FoldExpandedConstraint; |
64 | |
65 | /// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is |
66 | /// either an atomic constraint, a conjunction of normalized constraints or a |
67 | /// disjunction of normalized constraints. |
68 | struct NormalizedConstraint { |
69 | friend class Sema; |
70 | |
71 | enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction }; |
72 | |
73 | using CompoundConstraint = llvm::PointerIntPair<NormalizedConstraintPair *, 1, |
74 | CompoundConstraintKind>; |
75 | |
76 | llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *, |
77 | CompoundConstraint> |
78 | Constraint; |
79 | |
80 | NormalizedConstraint(AtomicConstraint *C): Constraint{C} { }; |
81 | NormalizedConstraint(FoldExpandedConstraint *C) : Constraint{C} {}; |
82 | |
83 | NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS, |
84 | NormalizedConstraint RHS, CompoundConstraintKind Kind); |
85 | |
86 | NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other); |
87 | NormalizedConstraint(NormalizedConstraint &&Other): |
88 | Constraint(Other.Constraint) { |
89 | Other.Constraint = nullptr; |
90 | } |
91 | NormalizedConstraint &operator=(const NormalizedConstraint &Other) = delete; |
92 | NormalizedConstraint &operator=(NormalizedConstraint &&Other) { |
93 | if (&Other != this) { |
94 | NormalizedConstraint Temp(std::move(Other)); |
95 | std::swap(a&: Constraint, b&: Temp.Constraint); |
96 | } |
97 | return *this; |
98 | } |
99 | |
100 | bool isAtomic() const { return llvm::isa<AtomicConstraint *>(Val: Constraint); } |
101 | bool isFoldExpanded() const { |
102 | return llvm::isa<FoldExpandedConstraint *>(Val: Constraint); |
103 | } |
104 | bool isCompound() const { return llvm::isa<CompoundConstraint>(Val: Constraint); } |
105 | |
106 | CompoundConstraintKind getCompoundKind() const; |
107 | |
108 | NormalizedConstraint &getLHS() const; |
109 | NormalizedConstraint &getRHS() const; |
110 | |
111 | AtomicConstraint *getAtomicConstraint() const; |
112 | |
113 | FoldExpandedConstraint *getFoldExpandedConstraint() const; |
114 | |
115 | private: |
116 | static std::optional<NormalizedConstraint> |
117 | fromAssociatedConstraints(Sema &S, const NamedDecl *D, |
118 | ArrayRef<AssociatedConstraint> ACs); |
119 | static std::optional<NormalizedConstraint> |
120 | fromConstraintExpr(Sema &S, const NamedDecl *D, const Expr *E); |
121 | }; |
122 | |
123 | struct alignas(ConstraintAlignment) NormalizedConstraintPair { |
124 | NormalizedConstraint LHS, RHS; |
125 | }; |
126 | |
127 | struct alignas(ConstraintAlignment) FoldExpandedConstraint { |
128 | enum class FoldOperatorKind { And, Or } Kind; |
129 | NormalizedConstraint Constraint; |
130 | const Expr *Pattern; |
131 | |
132 | FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C, |
133 | const Expr *Pattern) |
134 | : Kind(K), Constraint(std::move(C)), Pattern(Pattern) {}; |
135 | |
136 | static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A, |
137 | const FoldExpandedConstraint &B); |
138 | }; |
139 | |
140 | const NormalizedConstraint *getNormalizedAssociatedConstraints( |
141 | Sema &S, const NamedDecl *ConstrainedDecl, |
142 | ArrayRef<AssociatedConstraint> AssociatedConstraints); |
143 | |
144 | /// \brief SubsumptionChecker establishes subsumption |
145 | /// between two set of constraints. |
146 | class SubsumptionChecker { |
147 | public: |
148 | using SubsumptionCallable = llvm::function_ref<bool( |
149 | const AtomicConstraint &, const AtomicConstraint &)>; |
150 | |
151 | SubsumptionChecker(Sema &SemaRef, SubsumptionCallable Callable = {}); |
152 | |
153 | std::optional<bool> Subsumes(const NamedDecl *DP, |
154 | ArrayRef<AssociatedConstraint> P, |
155 | const NamedDecl *DQ, |
156 | ArrayRef<AssociatedConstraint> Q); |
157 | |
158 | bool Subsumes(const NormalizedConstraint *P, const NormalizedConstraint *Q); |
159 | |
160 | private: |
161 | Sema &SemaRef; |
162 | SubsumptionCallable Callable; |
163 | |
164 | // Each Literal has a unique value that is enough to establish |
165 | // its identity. |
166 | // Some constraints (fold expended) require special subsumption |
167 | // handling logic beyond comparing values, so we store a flag |
168 | // to let us quickly dispatch to each kind of variable. |
169 | struct Literal { |
170 | enum Kind { Atomic, FoldExpanded }; |
171 | |
172 | unsigned Value : 16; |
173 | LLVM_PREFERRED_TYPE(Kind) |
174 | unsigned Kind : 1; |
175 | |
176 | bool operator==(const Literal &Other) const { return Value == Other.Value; } |
177 | bool operator<(const Literal &Other) const { return Value < Other.Value; } |
178 | }; |
179 | using Clause = llvm::SmallVector<Literal>; |
180 | using Formula = llvm::SmallVector<Clause, 5>; |
181 | |
182 | struct CNFFormula : Formula { |
183 | static constexpr auto Kind = NormalizedConstraint::CCK_Conjunction; |
184 | using Formula::Formula; |
185 | }; |
186 | struct DNFFormula : Formula { |
187 | static constexpr auto Kind = NormalizedConstraint::CCK_Disjunction; |
188 | using Formula::Formula; |
189 | }; |
190 | |
191 | struct MappedAtomicConstraint { |
192 | AtomicConstraint *Constraint; |
193 | Literal ID; |
194 | }; |
195 | |
196 | struct FoldExpendedConstraintKey { |
197 | FoldExpandedConstraint::FoldOperatorKind Kind; |
198 | AtomicConstraint *Constraint; |
199 | Literal ID; |
200 | }; |
201 | |
202 | llvm::DenseMap<const Expr *, llvm::SmallDenseMap<llvm::FoldingSetNodeID, |
203 | MappedAtomicConstraint>> |
204 | AtomicMap; |
205 | |
206 | llvm::DenseMap<const Expr *, std::vector<FoldExpendedConstraintKey>> FoldMap; |
207 | |
208 | // A map from a literal to a corresponding associated constraint. |
209 | // We do not have enough bits left for a pointer union here :( |
210 | llvm::DenseMap<uint16_t, void *> ReverseMap; |
211 | |
212 | // Fold expanded constraints ask us to recursively establish subsumption. |
213 | // This caches the result. |
214 | llvm::SmallDenseMap< |
215 | std::pair<const FoldExpandedConstraint *, const FoldExpandedConstraint *>, |
216 | bool> |
217 | FoldSubsumptionCache; |
218 | |
219 | // Each <atomic, fold expanded constraint> is represented as a single ID. |
220 | // This is intentionally kept small we can't handle a large number of |
221 | // constraints anyway. |
222 | uint16_t NextID; |
223 | |
224 | bool Subsumes(const DNFFormula &P, const CNFFormula &Q); |
225 | bool Subsumes(Literal A, Literal B); |
226 | bool Subsumes(const FoldExpandedConstraint *A, |
227 | const FoldExpandedConstraint *B); |
228 | bool DNFSubsumes(const Clause &P, const Clause &Q); |
229 | |
230 | CNFFormula CNF(const NormalizedConstraint &C); |
231 | DNFFormula DNF(const NormalizedConstraint &C); |
232 | |
233 | template <typename FormulaType> |
234 | FormulaType Normalize(const NormalizedConstraint &C); |
235 | void AddUniqueClauseToFormula(Formula &F, Clause C); |
236 | |
237 | Literal find(AtomicConstraint *); |
238 | Literal find(FoldExpandedConstraint *); |
239 | |
240 | uint16_t getNewLiteralId(); |
241 | }; |
242 | |
243 | } // clang |
244 | |
245 | #endif // LLVM_CLANG_SEMA_SEMACONCEPT_H |
246 | |