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