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