123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- //===- ComparisonCategories.cpp - Three Way Comparison Data -----*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file defines the Comparison Category enum and data types, which
- // store the types and expressions needed to support operator<=>
- //
- //===----------------------------------------------------------------------===//
- #include "clang/AST/ComparisonCategories.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/AST/Decl.h"
- #include "clang/AST/DeclCXX.h"
- #include "clang/AST/Type.h"
- #include "llvm/ADT/SmallVector.h"
- #include <optional>
- using namespace clang;
- std::optional<ComparisonCategoryType>
- clang::getComparisonCategoryForBuiltinCmp(QualType T) {
- using CCT = ComparisonCategoryType;
- if (T->isIntegralOrEnumerationType())
- return CCT::StrongOrdering;
- if (T->isRealFloatingType())
- return CCT::PartialOrdering;
- // C++2a [expr.spaceship]p8: If the composite pointer type is an object
- // pointer type, p <=> q is of type std::strong_ordering.
- // Note: this assumes neither operand is a null pointer constant.
- if (T->isObjectPointerType())
- return CCT::StrongOrdering;
- // TODO: Extend support for operator<=> to ObjC types.
- return std::nullopt;
- }
- bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
- assert(VD && "must have var decl");
- if (!VD->isUsableInConstantExpressions(VD->getASTContext()))
- return false;
- // Before we attempt to get the value of the first field, ensure that we
- // actually have one (and only one) field.
- auto *Record = VD->getType()->getAsCXXRecordDecl();
- if (std::distance(Record->field_begin(), Record->field_end()) != 1 ||
- !Record->field_begin()->getType()->isIntegralOrEnumerationType())
- return false;
- return true;
- }
- /// Attempt to determine the integer value used to represent the comparison
- /// category result by evaluating the initializer for the specified VarDecl as
- /// a constant expression and retrieving the value of the class's first
- /// (and only) field.
- ///
- /// Note: The STL types are expected to have the form:
- /// struct X { T value; };
- /// where T is an integral or enumeration type.
- llvm::APSInt ComparisonCategoryInfo::ValueInfo::getIntValue() const {
- assert(hasValidIntValue() && "must have a valid value");
- return VD->evaluateValue()->getStructField(0).getInt();
- }
- ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
- ComparisonCategoryResult ValueKind) const {
- // Check if we already have a cache entry for this value.
- auto It = llvm::find_if(
- Objects, [&](ValueInfo const &Info) { return Info.Kind == ValueKind; });
- if (It != Objects.end())
- return &(*It);
- // We don't have a cached result. Lookup the variable declaration and create
- // a new entry representing it.
- DeclContextLookupResult Lookup = Record->getCanonicalDecl()->lookup(
- &Ctx.Idents.get(ComparisonCategories::getResultString(ValueKind)));
- if (Lookup.empty() || !isa<VarDecl>(Lookup.front()))
- return nullptr;
- Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));
- return &Objects.back();
- }
- static const NamespaceDecl *lookupStdNamespace(const ASTContext &Ctx,
- NamespaceDecl *&StdNS) {
- if (!StdNS) {
- DeclContextLookupResult Lookup =
- Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get("std"));
- if (!Lookup.empty())
- StdNS = dyn_cast<NamespaceDecl>(Lookup.front());
- }
- return StdNS;
- }
- static CXXRecordDecl *lookupCXXRecordDecl(const ASTContext &Ctx,
- const NamespaceDecl *StdNS,
- ComparisonCategoryType Kind) {
- StringRef Name = ComparisonCategories::getCategoryString(Kind);
- DeclContextLookupResult Lookup = StdNS->lookup(&Ctx.Idents.get(Name));
- if (!Lookup.empty())
- if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Lookup.front()))
- return RD;
- return nullptr;
- }
- const ComparisonCategoryInfo *
- ComparisonCategories::lookupInfo(ComparisonCategoryType Kind) const {
- auto It = Data.find(static_cast<char>(Kind));
- if (It != Data.end())
- return &It->second;
- if (const NamespaceDecl *NS = lookupStdNamespace(Ctx, StdNS))
- if (CXXRecordDecl *RD = lookupCXXRecordDecl(Ctx, NS, Kind))
- return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
- return nullptr;
- }
- const ComparisonCategoryInfo *
- ComparisonCategories::lookupInfoForType(QualType Ty) const {
- assert(!Ty.isNull() && "type must be non-null");
- using CCT = ComparisonCategoryType;
- auto *RD = Ty->getAsCXXRecordDecl();
- if (!RD)
- return nullptr;
- // Check to see if we have information for the specified type cached.
- const auto *CanonRD = RD->getCanonicalDecl();
- for (auto &KV : Data) {
- const ComparisonCategoryInfo &Info = KV.second;
- if (CanonRD == Info.Record->getCanonicalDecl())
- return &Info;
- }
- if (!RD->getEnclosingNamespaceContext()->isStdNamespace())
- return nullptr;
- // If not, check to see if the decl names a type in namespace std with a name
- // matching one of the comparison category types.
- for (unsigned I = static_cast<unsigned>(CCT::First),
- End = static_cast<unsigned>(CCT::Last);
- I <= End; ++I) {
- CCT Kind = static_cast<CCT>(I);
- // We've found the comparison category type. Build a new cache entry for
- // it.
- if (getCategoryString(Kind) == RD->getName())
- return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
- }
- // We've found nothing. This isn't a comparison category type.
- return nullptr;
- }
- const ComparisonCategoryInfo &ComparisonCategories::getInfoForType(QualType Ty) const {
- const ComparisonCategoryInfo *Info = lookupInfoForType(Ty);
- assert(Info && "info for comparison category not found");
- return *Info;
- }
- QualType ComparisonCategoryInfo::getType() const {
- assert(Record);
- return QualType(Record->getTypeForDecl(), 0);
- }
- StringRef ComparisonCategories::getCategoryString(ComparisonCategoryType Kind) {
- using CCKT = ComparisonCategoryType;
- switch (Kind) {
- case CCKT::PartialOrdering:
- return "partial_ordering";
- case CCKT::WeakOrdering:
- return "weak_ordering";
- case CCKT::StrongOrdering:
- return "strong_ordering";
- }
- llvm_unreachable("unhandled cases in switch");
- }
- StringRef ComparisonCategories::getResultString(ComparisonCategoryResult Kind) {
- using CCVT = ComparisonCategoryResult;
- switch (Kind) {
- case CCVT::Equal:
- return "equal";
- case CCVT::Equivalent:
- return "equivalent";
- case CCVT::Less:
- return "less";
- case CCVT::Greater:
- return "greater";
- case CCVT::Unordered:
- return "unordered";
- }
- llvm_unreachable("unhandled case in switch");
- }
- std::vector<ComparisonCategoryResult>
- ComparisonCategories::getPossibleResultsForType(ComparisonCategoryType Type) {
- using CCT = ComparisonCategoryType;
- using CCR = ComparisonCategoryResult;
- std::vector<CCR> Values;
- Values.reserve(4);
- bool IsStrong = Type == CCT::StrongOrdering;
- Values.push_back(IsStrong ? CCR::Equal : CCR::Equivalent);
- Values.push_back(CCR::Less);
- Values.push_back(CCR::Greater);
- if (Type == CCT::PartialOrdering)
- Values.push_back(CCR::Unordered);
- return Values;
- }
|