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