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" 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 20 using namespace clang; 21 22 bool 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 37 /// Attempt to determine the integer value used to represent the comparison 38 /// category result by evaluating the initializer for the specified VarDecl as 39 /// a constant expression and retreiving the value of the class's first 40 /// (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. 45 llvm::APSInt ComparisonCategoryInfo::ValueInfo::getIntValue() const { 46 assert(hasValidIntValue() && "must have a valid value"); 47 return VD->evaluateValue()->getStructField(0).getInt(); 48 } 49 50 ComparisonCategoryInfo::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; }); 55 if (It != Objects.end()) 56 return &(*It); 57 58 // We don't have a cached result. Lookup the variable declaration and create 59 // a new entry representing it. 60 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(); 66 } 67 68 static 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 79 static 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 90 const ComparisonCategoryInfo * 91 ComparisonCategories::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 103 const ComparisonCategoryInfo * 104 ComparisonCategories::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 139 const 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 145 QualType ComparisonCategoryInfo::getType() const { 146 assert(Record); 147 return QualType(Record->getTypeForDecl(), 0); 148 } 149 150 StringRef 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 167 StringRef 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 188 std::vector<ComparisonCategoryResult> 189 ComparisonCategories::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 } 211