Path: blob/main/contrib/llvm-project/clang/lib/AST/ComparisonCategories.cpp
35260 views
//===- ComparisonCategories.cpp - Three Way Comparison Data -----*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines the Comparison Category enum and data types, which9// store the types and expressions needed to support operator<=>10//11//===----------------------------------------------------------------------===//1213#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>2021using namespace clang;2223std::optional<ComparisonCategoryType>24clang::getComparisonCategoryForBuiltinCmp(QualType T) {25using CCT = ComparisonCategoryType;2627if (T->isIntegralOrEnumerationType())28return CCT::StrongOrdering;2930if (T->isRealFloatingType())31return CCT::PartialOrdering;3233// C++2a [expr.spaceship]p8: If the composite pointer type is an object34// pointer type, p <=> q is of type std::strong_ordering.35// Note: this assumes neither operand is a null pointer constant.36if (T->isObjectPointerType())37return CCT::StrongOrdering;3839// TODO: Extend support for operator<=> to ObjC types.40return std::nullopt;41}4243bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {44assert(VD && "must have var decl");45if (!VD->isUsableInConstantExpressions(VD->getASTContext()))46return false;4748// Before we attempt to get the value of the first field, ensure that we49// actually have one (and only one) field.50const auto *Record = VD->getType()->getAsCXXRecordDecl();51if (std::distance(Record->field_begin(), Record->field_end()) != 1 ||52!Record->field_begin()->getType()->isIntegralOrEnumerationType())53return false;5455return true;56}5758/// Attempt to determine the integer value used to represent the comparison59/// category result by evaluating the initializer for the specified VarDecl as60/// a constant expression and retrieving the value of the class's first61/// (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.66llvm::APSInt ComparisonCategoryInfo::ValueInfo::getIntValue() const {67assert(hasValidIntValue() && "must have a valid value");68return VD->evaluateValue()->getStructField(0).getInt();69}7071ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(72ComparisonCategoryResult ValueKind) const {73// Check if we already have a cache entry for this value.74auto It = llvm::find_if(75Objects, [&](ValueInfo const &Info) { return Info.Kind == ValueKind; });76if (It != Objects.end())77return &(*It);7879// We don't have a cached result. Lookup the variable declaration and create80// a new entry representing it.81DeclContextLookupResult Lookup = Record->getCanonicalDecl()->lookup(82&Ctx.Idents.get(ComparisonCategories::getResultString(ValueKind)));83if (Lookup.empty() || !isa<VarDecl>(Lookup.front()))84return nullptr;85Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));86return &Objects.back();87}8889static const NamespaceDecl *lookupStdNamespace(const ASTContext &Ctx,90NamespaceDecl *&StdNS) {91if (!StdNS) {92DeclContextLookupResult Lookup =93Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get("std"));94if (!Lookup.empty())95StdNS = dyn_cast<NamespaceDecl>(Lookup.front());96}97return StdNS;98}99100static const CXXRecordDecl *lookupCXXRecordDecl(const ASTContext &Ctx,101const NamespaceDecl *StdNS,102ComparisonCategoryType Kind) {103StringRef Name = ComparisonCategories::getCategoryString(Kind);104DeclContextLookupResult Lookup = StdNS->lookup(&Ctx.Idents.get(Name));105if (!Lookup.empty())106if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Lookup.front()))107return RD;108return nullptr;109}110111const ComparisonCategoryInfo *112ComparisonCategories::lookupInfo(ComparisonCategoryType Kind) const {113auto It = Data.find(static_cast<char>(Kind));114if (It != Data.end())115return &It->second;116117if (const NamespaceDecl *NS = lookupStdNamespace(Ctx, StdNS))118if (const CXXRecordDecl *RD = lookupCXXRecordDecl(Ctx, NS, Kind))119return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;120121return nullptr;122}123124const ComparisonCategoryInfo *125ComparisonCategories::lookupInfoForType(QualType Ty) const {126assert(!Ty.isNull() && "type must be non-null");127using CCT = ComparisonCategoryType;128const auto *RD = Ty->getAsCXXRecordDecl();129if (!RD)130return nullptr;131132// Check to see if we have information for the specified type cached.133const auto *CanonRD = RD->getCanonicalDecl();134for (const auto &KV : Data) {135const ComparisonCategoryInfo &Info = KV.second;136if (CanonRD == Info.Record->getCanonicalDecl())137return &Info;138}139140if (!RD->getEnclosingNamespaceContext()->isStdNamespace())141return nullptr;142143// If not, check to see if the decl names a type in namespace std with a name144// matching one of the comparison category types.145for (unsigned I = static_cast<unsigned>(CCT::First),146End = static_cast<unsigned>(CCT::Last);147I <= End; ++I) {148CCT Kind = static_cast<CCT>(I);149150// We've found the comparison category type. Build a new cache entry for151// it.152if (getCategoryString(Kind) == RD->getName())153return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;154}155156// We've found nothing. This isn't a comparison category type.157return nullptr;158}159160const ComparisonCategoryInfo &ComparisonCategories::getInfoForType(QualType Ty) const {161const ComparisonCategoryInfo *Info = lookupInfoForType(Ty);162assert(Info && "info for comparison category not found");163return *Info;164}165166QualType ComparisonCategoryInfo::getType() const {167assert(Record);168return QualType(Record->getTypeForDecl(), 0);169}170171StringRef ComparisonCategories::getCategoryString(ComparisonCategoryType Kind) {172using CCKT = ComparisonCategoryType;173switch (Kind) {174case CCKT::PartialOrdering:175return "partial_ordering";176case CCKT::WeakOrdering:177return "weak_ordering";178case CCKT::StrongOrdering:179return "strong_ordering";180}181llvm_unreachable("unhandled cases in switch");182}183184StringRef ComparisonCategories::getResultString(ComparisonCategoryResult Kind) {185using CCVT = ComparisonCategoryResult;186switch (Kind) {187case CCVT::Equal:188return "equal";189case CCVT::Equivalent:190return "equivalent";191case CCVT::Less:192return "less";193case CCVT::Greater:194return "greater";195case CCVT::Unordered:196return "unordered";197}198llvm_unreachable("unhandled case in switch");199}200201std::vector<ComparisonCategoryResult>202ComparisonCategories::getPossibleResultsForType(ComparisonCategoryType Type) {203using CCT = ComparisonCategoryType;204using CCR = ComparisonCategoryResult;205std::vector<CCR> Values;206Values.reserve(4);207bool IsStrong = Type == CCT::StrongOrdering;208Values.push_back(IsStrong ? CCR::Equal : CCR::Equivalent);209Values.push_back(CCR::Less);210Values.push_back(CCR::Greater);211if (Type == CCT::PartialOrdering)212Values.push_back(CCR::Unordered);213return Values;214}215216217