150
|
1 //===- ComparisonCategories.cpp - 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 #include "clang/AST/ComparisonCategories.h"
|
173
|
15 #include "clang/AST/ASTContext.h"
|
150
|
16 #include "clang/AST/Decl.h"
|
|
17 #include "clang/AST/DeclCXX.h"
|
|
18 #include "clang/AST/Type.h"
|
|
19 #include "llvm/ADT/SmallVector.h"
|
|
20
|
|
21 using namespace clang;
|
|
22
|
|
23 Optional<ComparisonCategoryType>
|
|
24 clang::getComparisonCategoryForBuiltinCmp(QualType T) {
|
|
25 using CCT = ComparisonCategoryType;
|
|
26
|
|
27 if (T->isIntegralOrEnumerationType())
|
|
28 return CCT::StrongOrdering;
|
|
29
|
|
30 if (T->isRealFloatingType())
|
|
31 return CCT::PartialOrdering;
|
|
32
|
|
33 // C++2a [expr.spaceship]p8: If the composite pointer type is an object
|
|
34 // pointer type, p <=> q is of type std::strong_ordering.
|
|
35 // Note: this assumes neither operand is a null pointer constant.
|
|
36 if (T->isObjectPointerType())
|
|
37 return CCT::StrongOrdering;
|
|
38
|
|
39 // TODO: Extend support for operator<=> to ObjC types.
|
|
40 return llvm::None;
|
|
41 }
|
|
42
|
|
43 bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
|
|
44 assert(VD && "must have var decl");
|
221
|
45 if (!VD->isUsableInConstantExpressions(VD->getASTContext()))
|
150
|
46 return false;
|
|
47
|
|
48 // Before we attempt to get the value of the first field, ensure that we
|
|
49 // actually have one (and only one) field.
|
|
50 auto *Record = VD->getType()->getAsCXXRecordDecl();
|
|
51 if (std::distance(Record->field_begin(), Record->field_end()) != 1 ||
|
|
52 !Record->field_begin()->getType()->isIntegralOrEnumerationType())
|
|
53 return false;
|
|
54
|
|
55 return true;
|
|
56 }
|
|
57
|
|
58 /// Attempt to determine the integer value used to represent the comparison
|
|
59 /// category result by evaluating the initializer for the specified VarDecl as
|
|
60 /// a constant expression and retreiving the value of the class's first
|
|
61 /// (and only) field.
|
|
62 ///
|
|
63 /// Note: The STL types are expected to have the form:
|
|
64 /// struct X { T value; };
|
|
65 /// where T is an integral or enumeration type.
|
|
66 llvm::APSInt ComparisonCategoryInfo::ValueInfo::getIntValue() const {
|
|
67 assert(hasValidIntValue() && "must have a valid value");
|
|
68 return VD->evaluateValue()->getStructField(0).getInt();
|
|
69 }
|
|
70
|
|
71 ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
|
|
72 ComparisonCategoryResult ValueKind) const {
|
|
73 // Check if we already have a cache entry for this value.
|
|
74 auto It = llvm::find_if(
|
|
75 Objects, [&](ValueInfo const &Info) { return Info.Kind == ValueKind; });
|
|
76 if (It != Objects.end())
|
|
77 return &(*It);
|
|
78
|
|
79 // We don't have a cached result. Lookup the variable declaration and create
|
|
80 // a new entry representing it.
|
|
81 DeclContextLookupResult Lookup = Record->getCanonicalDecl()->lookup(
|
|
82 &Ctx.Idents.get(ComparisonCategories::getResultString(ValueKind)));
|
|
83 if (Lookup.empty() || !isa<VarDecl>(Lookup.front()))
|
|
84 return nullptr;
|
|
85 Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));
|
|
86 return &Objects.back();
|
|
87 }
|
|
88
|
|
89 static const NamespaceDecl *lookupStdNamespace(const ASTContext &Ctx,
|
|
90 NamespaceDecl *&StdNS) {
|
|
91 if (!StdNS) {
|
|
92 DeclContextLookupResult Lookup =
|
|
93 Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get("std"));
|
|
94 if (!Lookup.empty())
|
|
95 StdNS = dyn_cast<NamespaceDecl>(Lookup.front());
|
|
96 }
|
|
97 return StdNS;
|
|
98 }
|
|
99
|
|
100 static CXXRecordDecl *lookupCXXRecordDecl(const ASTContext &Ctx,
|
|
101 const NamespaceDecl *StdNS,
|
|
102 ComparisonCategoryType Kind) {
|
|
103 StringRef Name = ComparisonCategories::getCategoryString(Kind);
|
|
104 DeclContextLookupResult Lookup = StdNS->lookup(&Ctx.Idents.get(Name));
|
|
105 if (!Lookup.empty())
|
|
106 if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Lookup.front()))
|
|
107 return RD;
|
|
108 return nullptr;
|
|
109 }
|
|
110
|
|
111 const ComparisonCategoryInfo *
|
|
112 ComparisonCategories::lookupInfo(ComparisonCategoryType Kind) const {
|
|
113 auto It = Data.find(static_cast<char>(Kind));
|
|
114 if (It != Data.end())
|
|
115 return &It->second;
|
|
116
|
|
117 if (const NamespaceDecl *NS = lookupStdNamespace(Ctx, StdNS))
|
|
118 if (CXXRecordDecl *RD = lookupCXXRecordDecl(Ctx, NS, Kind))
|
|
119 return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
|
|
120
|
|
121 return nullptr;
|
|
122 }
|
|
123
|
|
124 const ComparisonCategoryInfo *
|
|
125 ComparisonCategories::lookupInfoForType(QualType Ty) const {
|
|
126 assert(!Ty.isNull() && "type must be non-null");
|
|
127 using CCT = ComparisonCategoryType;
|
|
128 auto *RD = Ty->getAsCXXRecordDecl();
|
|
129 if (!RD)
|
|
130 return nullptr;
|
|
131
|
|
132 // Check to see if we have information for the specified type cached.
|
|
133 const auto *CanonRD = RD->getCanonicalDecl();
|
|
134 for (auto &KV : Data) {
|
|
135 const ComparisonCategoryInfo &Info = KV.second;
|
|
136 if (CanonRD == Info.Record->getCanonicalDecl())
|
|
137 return &Info;
|
|
138 }
|
|
139
|
|
140 if (!RD->getEnclosingNamespaceContext()->isStdNamespace())
|
|
141 return nullptr;
|
|
142
|
|
143 // If not, check to see if the decl names a type in namespace std with a name
|
|
144 // matching one of the comparison category types.
|
|
145 for (unsigned I = static_cast<unsigned>(CCT::First),
|
|
146 End = static_cast<unsigned>(CCT::Last);
|
|
147 I <= End; ++I) {
|
|
148 CCT Kind = static_cast<CCT>(I);
|
|
149
|
|
150 // We've found the comparison category type. Build a new cache entry for
|
|
151 // it.
|
|
152 if (getCategoryString(Kind) == RD->getName())
|
|
153 return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
|
|
154 }
|
|
155
|
|
156 // We've found nothing. This isn't a comparison category type.
|
|
157 return nullptr;
|
|
158 }
|
|
159
|
|
160 const ComparisonCategoryInfo &ComparisonCategories::getInfoForType(QualType Ty) const {
|
|
161 const ComparisonCategoryInfo *Info = lookupInfoForType(Ty);
|
|
162 assert(Info && "info for comparison category not found");
|
|
163 return *Info;
|
|
164 }
|
|
165
|
|
166 QualType ComparisonCategoryInfo::getType() const {
|
|
167 assert(Record);
|
|
168 return QualType(Record->getTypeForDecl(), 0);
|
|
169 }
|
|
170
|
|
171 StringRef ComparisonCategories::getCategoryString(ComparisonCategoryType Kind) {
|
|
172 using CCKT = ComparisonCategoryType;
|
|
173 switch (Kind) {
|
|
174 case CCKT::PartialOrdering:
|
|
175 return "partial_ordering";
|
|
176 case CCKT::WeakOrdering:
|
|
177 return "weak_ordering";
|
|
178 case CCKT::StrongOrdering:
|
|
179 return "strong_ordering";
|
|
180 }
|
|
181 llvm_unreachable("unhandled cases in switch");
|
|
182 }
|
|
183
|
|
184 StringRef ComparisonCategories::getResultString(ComparisonCategoryResult Kind) {
|
|
185 using CCVT = ComparisonCategoryResult;
|
|
186 switch (Kind) {
|
|
187 case CCVT::Equal:
|
|
188 return "equal";
|
|
189 case CCVT::Equivalent:
|
|
190 return "equivalent";
|
|
191 case CCVT::Less:
|
|
192 return "less";
|
|
193 case CCVT::Greater:
|
|
194 return "greater";
|
|
195 case CCVT::Unordered:
|
|
196 return "unordered";
|
|
197 }
|
|
198 llvm_unreachable("unhandled case in switch");
|
|
199 }
|
|
200
|
|
201 std::vector<ComparisonCategoryResult>
|
|
202 ComparisonCategories::getPossibleResultsForType(ComparisonCategoryType Type) {
|
|
203 using CCT = ComparisonCategoryType;
|
|
204 using CCR = ComparisonCategoryResult;
|
|
205 std::vector<CCR> Values;
|
|
206 Values.reserve(4);
|
|
207 bool IsStrong = Type == CCT::StrongOrdering;
|
|
208 Values.push_back(IsStrong ? CCR::Equal : CCR::Equivalent);
|
|
209 Values.push_back(CCR::Less);
|
|
210 Values.push_back(CCR::Greater);
|
|
211 if (Type == CCT::PartialOrdering)
|
|
212 Values.push_back(CCR::Unordered);
|
|
213 return Values;
|
|
214 }
|