#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;
if (T->isObjectPointerType())
return CCT::StrongOrdering;
return std::nullopt;
}
bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
assert(VD && "must have var decl");
if (!VD->isUsableInConstantExpressions(VD->getASTContext()))
return false;
const auto *Record = VD->getType()->getAsCXXRecordDecl();
if (std::distance(Record->field_begin(), Record->field_end()) != 1 ||
!Record->field_begin()->getType()->isIntegralOrEnumerationType())
return false;
return true;
}
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 {
auto It = llvm::find_if(
Objects, [&](ValueInfo const &Info) { return Info.Kind == ValueKind; });
if (It != Objects.end())
return &(*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 const 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 (const 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 (const 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;
const auto *RD = Ty->getAsCXXRecordDecl();
if (!RD)
return nullptr;
const auto *CanonRD = RD->getCanonicalDecl();
for (const auto &KV : Data) {
const ComparisonCategoryInfo &Info = KV.second;
if (CanonRD == Info.Record->getCanonicalDecl())
return &Info;
}
if (!RD->getEnclosingNamespaceContext()->isStdNamespace())
return nullptr;
for (unsigned I = static_cast<unsigned>(CCT::First),
End = static_cast<unsigned>(CCT::Last);
I <= End; ++I) {
CCT Kind = static_cast<CCT>(I);
if (getCategoryString(Kind) == RD->getName())
return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
}
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;
}