ComparisonCategories.cpp 7.3 KB

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