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