xref: /llvm-project/clang/lib/AST/ComparisonCategories.cpp (revision 597f56f309815bcda9f38dd6040d7c7e0e520547)
10683c0e6SEric Fiselier //===- ComparisonCategories.cpp - Three Way Comparison Data -----*- C++ -*-===//
20683c0e6SEric Fiselier //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60683c0e6SEric Fiselier //
70683c0e6SEric Fiselier //===----------------------------------------------------------------------===//
80683c0e6SEric Fiselier //
90683c0e6SEric Fiselier //  This file defines the Comparison Category enum and data types, which
100683c0e6SEric Fiselier //  store the types and expressions needed to support operator<=>
110683c0e6SEric Fiselier //
120683c0e6SEric Fiselier //===----------------------------------------------------------------------===//
130683c0e6SEric Fiselier 
140683c0e6SEric Fiselier #include "clang/AST/ComparisonCategories.h"
15b36c19bcSReid Kleckner #include "clang/AST/ASTContext.h"
160683c0e6SEric Fiselier #include "clang/AST/Decl.h"
170683c0e6SEric Fiselier #include "clang/AST/DeclCXX.h"
180683c0e6SEric Fiselier #include "clang/AST/Type.h"
190683c0e6SEric Fiselier #include "llvm/ADT/SmallVector.h"
20a1580d7bSKazu Hirata #include <optional>
210683c0e6SEric Fiselier 
220683c0e6SEric Fiselier using namespace clang;
230683c0e6SEric Fiselier 
246ad0788cSKazu Hirata std::optional<ComparisonCategoryType>
getComparisonCategoryForBuiltinCmp(QualType T)2568009c24SRichard Smith clang::getComparisonCategoryForBuiltinCmp(QualType T) {
2668009c24SRichard Smith   using CCT = ComparisonCategoryType;
2768009c24SRichard Smith 
2868009c24SRichard Smith   if (T->isIntegralOrEnumerationType())
2968009c24SRichard Smith     return CCT::StrongOrdering;
3068009c24SRichard Smith 
31f495de43SRichard Smith   if (T->isRealFloatingType())
3268009c24SRichard Smith     return CCT::PartialOrdering;
3368009c24SRichard Smith 
3468009c24SRichard Smith   // C++2a [expr.spaceship]p8: If the composite pointer type is an object
3568009c24SRichard Smith   // pointer type, p <=> q is of type std::strong_ordering.
3668009c24SRichard Smith   // Note: this assumes neither operand is a null pointer constant.
37f495de43SRichard Smith   if (T->isObjectPointerType())
3868009c24SRichard Smith     return CCT::StrongOrdering;
3968009c24SRichard Smith 
4068009c24SRichard Smith   // TODO: Extend support for operator<=> to ObjC types.
41e31564afSKazu Hirata   return std::nullopt;
4268009c24SRichard Smith }
4368009c24SRichard Smith 
hasValidIntValue() const44c5fb8580SEric Fiselier bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
45c5fb8580SEric Fiselier   assert(VD && "must have var decl");
46e56e7bd4SZequan Wu   if (!VD->isUsableInConstantExpressions(VD->getASTContext()))
47c5fb8580SEric Fiselier     return false;
48c5fb8580SEric Fiselier 
49c5fb8580SEric Fiselier   // Before we attempt to get the value of the first field, ensure that we
50c5fb8580SEric Fiselier   // actually have one (and only one) field.
51*597f56f3STimm Bäder   const auto *Record = VD->getType()->getAsCXXRecordDecl();
52c5fb8580SEric Fiselier   if (std::distance(Record->field_begin(), Record->field_end()) != 1 ||
53c5fb8580SEric Fiselier       !Record->field_begin()->getType()->isIntegralOrEnumerationType())
54c5fb8580SEric Fiselier     return false;
55c5fb8580SEric Fiselier 
56c5fb8580SEric Fiselier   return true;
57c5fb8580SEric Fiselier }
58c5fb8580SEric Fiselier 
590683c0e6SEric Fiselier /// Attempt to determine the integer value used to represent the comparison
600683c0e6SEric Fiselier /// category result by evaluating the initializer for the specified VarDecl as
6160ab6861SNico Weber /// a constant expression and retrieving the value of the class's first
620683c0e6SEric Fiselier /// (and only) field.
630683c0e6SEric Fiselier ///
640683c0e6SEric Fiselier /// Note: The STL types are expected to have the form:
650683c0e6SEric Fiselier ///    struct X { T value; };
660683c0e6SEric Fiselier /// where T is an integral or enumeration type.
getIntValue() const67c5fb8580SEric Fiselier llvm::APSInt ComparisonCategoryInfo::ValueInfo::getIntValue() const {
68c5fb8580SEric Fiselier   assert(hasValidIntValue() && "must have a valid value");
69c5fb8580SEric Fiselier   return VD->evaluateValue()->getStructField(0).getInt();
700683c0e6SEric Fiselier }
710683c0e6SEric Fiselier 
lookupValueInfo(ComparisonCategoryResult ValueKind) const720683c0e6SEric Fiselier ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
730683c0e6SEric Fiselier     ComparisonCategoryResult ValueKind) const {
740683c0e6SEric Fiselier   // Check if we already have a cache entry for this value.
750683c0e6SEric Fiselier   auto It = llvm::find_if(
760683c0e6SEric Fiselier       Objects, [&](ValueInfo const &Info) { return Info.Kind == ValueKind; });
77c5fb8580SEric Fiselier   if (It != Objects.end())
78c5fb8580SEric Fiselier     return &(*It);
790683c0e6SEric Fiselier 
800683c0e6SEric Fiselier   // We don't have a cached result. Lookup the variable declaration and create
810683c0e6SEric Fiselier   // a new entry representing it.
820683c0e6SEric Fiselier   DeclContextLookupResult Lookup = Record->getCanonicalDecl()->lookup(
830683c0e6SEric Fiselier       &Ctx.Idents.get(ComparisonCategories::getResultString(ValueKind)));
8456bba012SRichard Smith   if (Lookup.empty() || !isa<VarDecl>(Lookup.front()))
850683c0e6SEric Fiselier     return nullptr;
860683c0e6SEric Fiselier   Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));
87c5fb8580SEric Fiselier   return &Objects.back();
880683c0e6SEric Fiselier }
890683c0e6SEric Fiselier 
lookupStdNamespace(const ASTContext & Ctx,NamespaceDecl * & StdNS)900683c0e6SEric Fiselier static const NamespaceDecl *lookupStdNamespace(const ASTContext &Ctx,
910683c0e6SEric Fiselier                                                NamespaceDecl *&StdNS) {
920683c0e6SEric Fiselier   if (!StdNS) {
930683c0e6SEric Fiselier     DeclContextLookupResult Lookup =
940683c0e6SEric Fiselier         Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get("std"));
9556bba012SRichard Smith     if (!Lookup.empty())
960683c0e6SEric Fiselier       StdNS = dyn_cast<NamespaceDecl>(Lookup.front());
970683c0e6SEric Fiselier   }
980683c0e6SEric Fiselier   return StdNS;
990683c0e6SEric Fiselier }
1000683c0e6SEric Fiselier 
lookupCXXRecordDecl(const ASTContext & Ctx,const NamespaceDecl * StdNS,ComparisonCategoryType Kind)101*597f56f3STimm Bäder static const CXXRecordDecl *lookupCXXRecordDecl(const ASTContext &Ctx,
1020683c0e6SEric Fiselier                                                 const NamespaceDecl *StdNS,
1030683c0e6SEric Fiselier                                                 ComparisonCategoryType Kind) {
1040683c0e6SEric Fiselier   StringRef Name = ComparisonCategories::getCategoryString(Kind);
1050683c0e6SEric Fiselier   DeclContextLookupResult Lookup = StdNS->lookup(&Ctx.Idents.get(Name));
10656bba012SRichard Smith   if (!Lookup.empty())
107*597f56f3STimm Bäder     if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Lookup.front()))
1080683c0e6SEric Fiselier       return RD;
1090683c0e6SEric Fiselier   return nullptr;
1100683c0e6SEric Fiselier }
1110683c0e6SEric Fiselier 
1120683c0e6SEric Fiselier const ComparisonCategoryInfo *
lookupInfo(ComparisonCategoryType Kind) const1130683c0e6SEric Fiselier ComparisonCategories::lookupInfo(ComparisonCategoryType Kind) const {
1140683c0e6SEric Fiselier   auto It = Data.find(static_cast<char>(Kind));
1150683c0e6SEric Fiselier   if (It != Data.end())
1160683c0e6SEric Fiselier     return &It->second;
1170683c0e6SEric Fiselier 
1180683c0e6SEric Fiselier   if (const NamespaceDecl *NS = lookupStdNamespace(Ctx, StdNS))
119*597f56f3STimm Bäder     if (const CXXRecordDecl *RD = lookupCXXRecordDecl(Ctx, NS, Kind))
1200683c0e6SEric Fiselier       return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
1210683c0e6SEric Fiselier 
1220683c0e6SEric Fiselier   return nullptr;
1230683c0e6SEric Fiselier }
1240683c0e6SEric Fiselier 
1250683c0e6SEric Fiselier const ComparisonCategoryInfo *
lookupInfoForType(QualType Ty) const1260683c0e6SEric Fiselier ComparisonCategories::lookupInfoForType(QualType Ty) const {
1270683c0e6SEric Fiselier   assert(!Ty.isNull() && "type must be non-null");
1280683c0e6SEric Fiselier   using CCT = ComparisonCategoryType;
129*597f56f3STimm Bäder   const auto *RD = Ty->getAsCXXRecordDecl();
1300683c0e6SEric Fiselier   if (!RD)
1310683c0e6SEric Fiselier     return nullptr;
1320683c0e6SEric Fiselier 
1330683c0e6SEric Fiselier   // Check to see if we have information for the specified type cached.
1340683c0e6SEric Fiselier   const auto *CanonRD = RD->getCanonicalDecl();
135*597f56f3STimm Bäder   for (const auto &KV : Data) {
1360683c0e6SEric Fiselier     const ComparisonCategoryInfo &Info = KV.second;
1370683c0e6SEric Fiselier     if (CanonRD == Info.Record->getCanonicalDecl())
1380683c0e6SEric Fiselier       return &Info;
1390683c0e6SEric Fiselier   }
1400683c0e6SEric Fiselier 
1410683c0e6SEric Fiselier   if (!RD->getEnclosingNamespaceContext()->isStdNamespace())
1420683c0e6SEric Fiselier     return nullptr;
1430683c0e6SEric Fiselier 
1440683c0e6SEric Fiselier   // If not, check to see if the decl names a type in namespace std with a name
1450683c0e6SEric Fiselier   // matching one of the comparison category types.
1460683c0e6SEric Fiselier   for (unsigned I = static_cast<unsigned>(CCT::First),
1470683c0e6SEric Fiselier                 End = static_cast<unsigned>(CCT::Last);
1480683c0e6SEric Fiselier        I <= End; ++I) {
1490683c0e6SEric Fiselier     CCT Kind = static_cast<CCT>(I);
1500683c0e6SEric Fiselier 
1510683c0e6SEric Fiselier     // We've found the comparison category type. Build a new cache entry for
1520683c0e6SEric Fiselier     // it.
1530683c0e6SEric Fiselier     if (getCategoryString(Kind) == RD->getName())
1540683c0e6SEric Fiselier       return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
1550683c0e6SEric Fiselier   }
1560683c0e6SEric Fiselier 
1570683c0e6SEric Fiselier   // We've found nothing. This isn't a comparison category type.
1580683c0e6SEric Fiselier   return nullptr;
1590683c0e6SEric Fiselier }
1600683c0e6SEric Fiselier 
getInfoForType(QualType Ty) const1610683c0e6SEric Fiselier const ComparisonCategoryInfo &ComparisonCategories::getInfoForType(QualType Ty) const {
1620683c0e6SEric Fiselier   const ComparisonCategoryInfo *Info = lookupInfoForType(Ty);
1630683c0e6SEric Fiselier   assert(Info && "info for comparison category not found");
1640683c0e6SEric Fiselier   return *Info;
1650683c0e6SEric Fiselier }
1660683c0e6SEric Fiselier 
getType() const1670683c0e6SEric Fiselier QualType ComparisonCategoryInfo::getType() const {
1680683c0e6SEric Fiselier   assert(Record);
1690683c0e6SEric Fiselier   return QualType(Record->getTypeForDecl(), 0);
1700683c0e6SEric Fiselier }
1710683c0e6SEric Fiselier 
getCategoryString(ComparisonCategoryType Kind)1720683c0e6SEric Fiselier StringRef ComparisonCategories::getCategoryString(ComparisonCategoryType Kind) {
1730683c0e6SEric Fiselier   using CCKT = ComparisonCategoryType;
1740683c0e6SEric Fiselier   switch (Kind) {
1750683c0e6SEric Fiselier   case CCKT::PartialOrdering:
1760683c0e6SEric Fiselier     return "partial_ordering";
1770683c0e6SEric Fiselier   case CCKT::WeakOrdering:
1780683c0e6SEric Fiselier     return "weak_ordering";
1790683c0e6SEric Fiselier   case CCKT::StrongOrdering:
1800683c0e6SEric Fiselier     return "strong_ordering";
1810683c0e6SEric Fiselier   }
1820683c0e6SEric Fiselier   llvm_unreachable("unhandled cases in switch");
1830683c0e6SEric Fiselier }
1840683c0e6SEric Fiselier 
getResultString(ComparisonCategoryResult Kind)1850683c0e6SEric Fiselier StringRef ComparisonCategories::getResultString(ComparisonCategoryResult Kind) {
1860683c0e6SEric Fiselier   using CCVT = ComparisonCategoryResult;
1870683c0e6SEric Fiselier   switch (Kind) {
1880683c0e6SEric Fiselier   case CCVT::Equal:
1890683c0e6SEric Fiselier     return "equal";
1900683c0e6SEric Fiselier   case CCVT::Equivalent:
1910683c0e6SEric Fiselier     return "equivalent";
1920683c0e6SEric Fiselier   case CCVT::Less:
1930683c0e6SEric Fiselier     return "less";
1940683c0e6SEric Fiselier   case CCVT::Greater:
1950683c0e6SEric Fiselier     return "greater";
1960683c0e6SEric Fiselier   case CCVT::Unordered:
1970683c0e6SEric Fiselier     return "unordered";
1980683c0e6SEric Fiselier   }
1990683c0e6SEric Fiselier   llvm_unreachable("unhandled case in switch");
2000683c0e6SEric Fiselier }
2010683c0e6SEric Fiselier 
2020683c0e6SEric Fiselier std::vector<ComparisonCategoryResult>
getPossibleResultsForType(ComparisonCategoryType Type)2030683c0e6SEric Fiselier ComparisonCategories::getPossibleResultsForType(ComparisonCategoryType Type) {
2040683c0e6SEric Fiselier   using CCT = ComparisonCategoryType;
2050683c0e6SEric Fiselier   using CCR = ComparisonCategoryResult;
2060683c0e6SEric Fiselier   std::vector<CCR> Values;
207dbd11297SRichard Smith   Values.reserve(4);
208f495de43SRichard Smith   bool IsStrong = Type == CCT::StrongOrdering;
209dbd11297SRichard Smith   Values.push_back(IsStrong ? CCR::Equal : CCR::Equivalent);
2100683c0e6SEric Fiselier   Values.push_back(CCR::Less);
2110683c0e6SEric Fiselier   Values.push_back(CCR::Greater);
2120683c0e6SEric Fiselier   if (Type == CCT::PartialOrdering)
2130683c0e6SEric Fiselier     Values.push_back(CCR::Unordered);
2140683c0e6SEric Fiselier   return Values;
2150683c0e6SEric Fiselier }
216