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