1 //===- ComparisonCategories.h - Three Way Comparison Data -------*- C++ -*-===//
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 defines the Comparison Category enum and data types, which
10 // store the types and expressions needed to support operator<=>
11 //
12 //===----------------------------------------------------------------------===//
13
14 #ifndef LLVM_CLANG_AST_COMPARISONCATEGORIES_H
15 #define LLVM_CLANG_AST_COMPARISONCATEGORIES_H
16
17 #include "clang/Basic/LLVM.h"
18 #include "llvm/ADT/APSInt.h"
19 #include "llvm/ADT/DenseMap.h"
20 #include <array>
21 #include <cassert>
22 #include <optional>
23 #include <vector>
24
25 namespace llvm {
26 class StringRef;
27 class APSInt;
28 }
29
30 namespace clang {
31
32 class ASTContext;
33 class VarDecl;
34 class CXXRecordDecl;
35 class Sema;
36 class QualType;
37 class NamespaceDecl;
38
39 /// An enumeration representing the different comparison categories
40 /// types.
41 ///
42 /// C++2a [cmp.categories.pre] The types weak_equality, strong_equality,
43 /// partial_ordering, weak_ordering, and strong_ordering are collectively
44 /// termed the comparison category types.
45 enum class ComparisonCategoryType : unsigned char {
46 PartialOrdering,
47 WeakOrdering,
48 StrongOrdering,
49 First = PartialOrdering,
50 Last = StrongOrdering
51 };
52
53 /// Determine the common comparison type, as defined in C++2a
54 /// [class.spaceship]p4.
commonComparisonType(ComparisonCategoryType A,ComparisonCategoryType B)55 inline ComparisonCategoryType commonComparisonType(ComparisonCategoryType A,
56 ComparisonCategoryType B) {
57 return A < B ? A : B;
58 }
59
60 /// Get the comparison category that should be used when comparing values of
61 /// type \c T.
62 std::optional<ComparisonCategoryType>
63 getComparisonCategoryForBuiltinCmp(QualType T);
64
65 /// An enumeration representing the possible results of a three-way
66 /// comparison. These values map onto instances of comparison category types
67 /// defined in the standard library. e.g. 'std::strong_ordering::less'.
68 enum class ComparisonCategoryResult : unsigned char {
69 Equal,
70 Equivalent,
71 Less,
72 Greater,
73 Unordered,
74 Last = Unordered
75 };
76
77 class ComparisonCategoryInfo {
78 friend class ComparisonCategories;
79 friend class Sema;
80
81 public:
ComparisonCategoryInfo(const ASTContext & Ctx,CXXRecordDecl * RD,ComparisonCategoryType Kind)82 ComparisonCategoryInfo(const ASTContext &Ctx, CXXRecordDecl *RD,
83 ComparisonCategoryType Kind)
84 : Ctx(Ctx), Record(RD), Kind(Kind) {}
85
86 struct ValueInfo {
87 ComparisonCategoryResult Kind;
88 VarDecl *VD;
89
ValueInfoValueInfo90 ValueInfo(ComparisonCategoryResult Kind, VarDecl *VD)
91 : Kind(Kind), VD(VD) {}
92
93 /// True iff we've successfully evaluated the variable as a constant
94 /// expression and extracted its integer value.
95 bool hasValidIntValue() const;
96
97 /// Get the constant integer value used by this variable to represent
98 /// the comparison category result type.
99 llvm::APSInt getIntValue() const;
100 };
101 private:
102 const ASTContext &Ctx;
103
104 /// A map containing the comparison category result decls from the
105 /// standard library. The key is a value of ComparisonCategoryResult.
106 mutable llvm::SmallVector<
107 ValueInfo, static_cast<unsigned>(ComparisonCategoryResult::Last) + 1>
108 Objects;
109
110 /// Lookup the ValueInfo struct for the specified ValueKind. If the
111 /// VarDecl for the value cannot be found, nullptr is returned.
112 ///
113 /// If the ValueInfo does not have a valid integer value the variable
114 /// is evaluated as a constant expression to determine that value.
115 ValueInfo *lookupValueInfo(ComparisonCategoryResult ValueKind) const;
116
117 public:
118 /// The declaration for the comparison category type from the
119 /// standard library.
120 const CXXRecordDecl *Record = nullptr;
121
122 /// The Kind of the comparison category type
123 ComparisonCategoryType Kind;
124
125 public:
126 QualType getType() const;
127
getValueInfo(ComparisonCategoryResult ValueKind)128 const ValueInfo *getValueInfo(ComparisonCategoryResult ValueKind) const {
129 ValueInfo *Info = lookupValueInfo(ValueKind);
130 assert(Info &&
131 "comparison category does not contain the specified result kind");
132 assert(Info->hasValidIntValue() &&
133 "couldn't determine the integer constant for this value");
134 return Info;
135 }
136
137 /// True iff the comparison is "strong". i.e. it checks equality and
138 /// not equivalence.
isStrong()139 bool isStrong() const {
140 using CCK = ComparisonCategoryType;
141 return Kind == CCK::StrongOrdering;
142 }
143
144 /// True iff the comparison is not totally ordered.
isPartial()145 bool isPartial() const {
146 using CCK = ComparisonCategoryType;
147 return Kind == CCK::PartialOrdering;
148 }
149
150 /// Converts the specified result kind into the correct result kind
151 /// for this category. Specifically it lowers strong equality results to
152 /// weak equivalence if needed.
makeWeakResult(ComparisonCategoryResult Res)153 ComparisonCategoryResult makeWeakResult(ComparisonCategoryResult Res) const {
154 using CCR = ComparisonCategoryResult;
155 if (!isStrong() && Res == CCR::Equal)
156 return CCR::Equivalent;
157 return Res;
158 }
159
getEqualOrEquiv()160 const ValueInfo *getEqualOrEquiv() const {
161 return getValueInfo(makeWeakResult(ComparisonCategoryResult::Equal));
162 }
getLess()163 const ValueInfo *getLess() const {
164 return getValueInfo(ComparisonCategoryResult::Less);
165 }
getGreater()166 const ValueInfo *getGreater() const {
167 return getValueInfo(ComparisonCategoryResult::Greater);
168 }
getUnordered()169 const ValueInfo *getUnordered() const {
170 assert(isPartial());
171 return getValueInfo(ComparisonCategoryResult::Unordered);
172 }
173 };
174
175 class ComparisonCategories {
176 public:
177 static StringRef getCategoryString(ComparisonCategoryType Kind);
178 static StringRef getResultString(ComparisonCategoryResult Kind);
179
180 /// Return the list of results which are valid for the specified
181 /// comparison category type.
182 static std::vector<ComparisonCategoryResult>
183 getPossibleResultsForType(ComparisonCategoryType Type);
184
185 /// Return the comparison category information for the category
186 /// specified by 'Kind'.
getInfo(ComparisonCategoryType Kind)187 const ComparisonCategoryInfo &getInfo(ComparisonCategoryType Kind) const {
188 const ComparisonCategoryInfo *Result = lookupInfo(Kind);
189 assert(Result != nullptr &&
190 "information for specified comparison category has not been built");
191 return *Result;
192 }
193
194 /// Return the comparison category information as specified by
195 /// `getCategoryForType(Ty)`. If the information is not already cached,
196 /// the declaration is looked up and a cache entry is created.
197 /// NOTE: Lookup is expected to succeed. Use lookupInfo if failure is
198 /// possible.
199 const ComparisonCategoryInfo &getInfoForType(QualType Ty) const;
200
201 public:
202 /// Return the cached comparison category information for the
203 /// specified 'Kind'. If no cache entry is present the comparison category
204 /// type is looked up. If lookup fails nullptr is returned. Otherwise, a
205 /// new cache entry is created and returned
206 const ComparisonCategoryInfo *lookupInfo(ComparisonCategoryType Kind) const;
207
lookupInfo(ComparisonCategoryType Kind)208 ComparisonCategoryInfo *lookupInfo(ComparisonCategoryType Kind) {
209 const auto &This = *this;
210 return const_cast<ComparisonCategoryInfo *>(This.lookupInfo(Kind));
211 }
212
213 const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const;
214
215 private:
216 friend class ASTContext;
217
ComparisonCategories(const ASTContext & Ctx)218 explicit ComparisonCategories(const ASTContext &Ctx) : Ctx(Ctx) {}
219
220 const ASTContext &Ctx;
221
222 /// A map from the ComparisonCategoryType (represented as 'char') to the
223 /// cached information for the specified category.
224 mutable llvm::DenseMap<char, ComparisonCategoryInfo> Data;
225 mutable NamespaceDecl *StdNS = nullptr;
226 };
227
228 } // namespace clang
229
230 #endif
231