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