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