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