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