blob: 70a78964b8a0ceda3b9cff90b32d764f337e87ea [file] [log] [blame]
//===- ComparisonCategories.h - 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<=>
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_COMPARISONCATEGORIES_H
#define LLVM_CLANG_AST_COMPARISONCATEGORIES_H
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/DenseMap.h"
#include <array>
#include <cassert>
namespace llvm {
class StringRef;
class APSInt;
}
namespace clang {
class ASTContext;
class VarDecl;
class CXXRecordDecl;
class Sema;
class QualType;
class NamespaceDecl;
/// An enumeration representing the different comparison categories
/// types.
///
/// C++2a [cmp.categories.pre] The types weak_equality, strong_equality,
/// partial_ordering, weak_ordering, and strong_ordering are collectively
/// termed the comparison category types.
enum class ComparisonCategoryType : unsigned char {
PartialOrdering,
WeakOrdering,
StrongOrdering,
First = PartialOrdering,
Last = StrongOrdering
};
/// Determine the common comparison type, as defined in C++2a
/// [class.spaceship]p4.
inline ComparisonCategoryType commonComparisonType(ComparisonCategoryType A,
ComparisonCategoryType B) {
return A < B ? A : B;
}
/// Get the comparison category that should be used when comparing values of
/// type \c T.
Optional<ComparisonCategoryType> getComparisonCategoryForBuiltinCmp(QualType T);
/// An enumeration representing the possible results of a three-way
/// comparison. These values map onto instances of comparison category types
/// defined in the standard library. e.g. 'std::strong_ordering::less'.
enum class ComparisonCategoryResult : unsigned char {
Equal,
Equivalent,
Less,
Greater,
Unordered,
Last = Unordered
};
class ComparisonCategoryInfo {
friend class ComparisonCategories;
friend class Sema;
public:
ComparisonCategoryInfo(const ASTContext &Ctx, CXXRecordDecl *RD,
ComparisonCategoryType Kind)
: Ctx(Ctx), Record(RD), Kind(Kind) {}
struct ValueInfo {
ComparisonCategoryResult Kind;
VarDecl *VD;
ValueInfo(ComparisonCategoryResult Kind, VarDecl *VD)
: Kind(Kind), VD(VD) {}
/// True iff we've successfully evaluated the variable as a constant
/// expression and extracted its integer value.
bool hasValidIntValue() const;
/// Get the constant integer value used by this variable to represent
/// the comparison category result type.
llvm::APSInt getIntValue() const;
};
private:
const ASTContext &Ctx;
/// A map containing the comparison category result decls from the
/// standard library. The key is a value of ComparisonCategoryResult.
mutable llvm::SmallVector<
ValueInfo, static_cast<unsigned>(ComparisonCategoryResult::Last) + 1>
Objects;
/// Lookup the ValueInfo struct for the specified ValueKind. If the
/// VarDecl for the value cannot be found, nullptr is returned.
///
/// If the ValueInfo does not have a valid integer value the variable
/// is evaluated as a constant expression to determine that value.
ValueInfo *lookupValueInfo(ComparisonCategoryResult ValueKind) const;
public:
/// The declaration for the comparison category type from the
/// standard library.
// FIXME: Make this const
CXXRecordDecl *Record = nullptr;
/// The Kind of the comparison category type
ComparisonCategoryType Kind;
public:
QualType getType() const;
const ValueInfo *getValueInfo(ComparisonCategoryResult ValueKind) const {
ValueInfo *Info = lookupValueInfo(ValueKind);
assert(Info &&
"comparison category does not contain the specified result kind");
assert(Info->hasValidIntValue() &&
"couldn't determine the integer constant for this value");
return Info;
}
/// True iff the comparison is "strong". i.e. it checks equality and
/// not equivalence.
bool isStrong() const {
using CCK = ComparisonCategoryType;
return Kind == CCK::StrongOrdering;
}
/// True iff the comparison is not totally ordered.
bool isPartial() const {
using CCK = ComparisonCategoryType;
return Kind == CCK::PartialOrdering;
}
/// Converts the specified result kind into the the correct result kind
/// for this category. Specifically it lowers strong equality results to
/// weak equivalence if needed.
ComparisonCategoryResult makeWeakResult(ComparisonCategoryResult Res) const {
using CCR = ComparisonCategoryResult;
if (!isStrong() && Res == CCR::Equal)
return CCR::Equivalent;
return Res;
}
const ValueInfo *getEqualOrEquiv() const {
return getValueInfo(makeWeakResult(ComparisonCategoryResult::Equal));
}
const ValueInfo *getLess() const {
return getValueInfo(ComparisonCategoryResult::Less);
}
const ValueInfo *getGreater() const {
return getValueInfo(ComparisonCategoryResult::Greater);
}
const ValueInfo *getUnordered() const {
assert(isPartial());
return getValueInfo(ComparisonCategoryResult::Unordered);
}
};
class ComparisonCategories {
public:
static StringRef getCategoryString(ComparisonCategoryType Kind);
static StringRef getResultString(ComparisonCategoryResult Kind);
/// Return the list of results which are valid for the specified
/// comparison category type.
static std::vector<ComparisonCategoryResult>
getPossibleResultsForType(ComparisonCategoryType Type);
/// Return the comparison category information for the category
/// specified by 'Kind'.
const ComparisonCategoryInfo &getInfo(ComparisonCategoryType Kind) const {
const ComparisonCategoryInfo *Result = lookupInfo(Kind);
assert(Result != nullptr &&
"information for specified comparison category has not been built");
return *Result;
}
/// Return the comparison category information as specified by
/// `getCategoryForType(Ty)`. If the information is not already cached,
/// the declaration is looked up and a cache entry is created.
/// NOTE: Lookup is expected to succeed. Use lookupInfo if failure is
/// possible.
const ComparisonCategoryInfo &getInfoForType(QualType Ty) const;
public:
/// Return the cached comparison category information for the
/// specified 'Kind'. If no cache entry is present the comparison category
/// type is looked up. If lookup fails nullptr is returned. Otherwise, a
/// new cache entry is created and returned
const ComparisonCategoryInfo *lookupInfo(ComparisonCategoryType Kind) const;
ComparisonCategoryInfo *lookupInfo(ComparisonCategoryType Kind) {
const auto &This = *this;
return const_cast<ComparisonCategoryInfo *>(This.lookupInfo(Kind));
}
const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const;
private:
friend class ASTContext;
explicit ComparisonCategories(const ASTContext &Ctx) : Ctx(Ctx) {}
const ASTContext &Ctx;
/// A map from the ComparisonCategoryType (represented as 'char') to the
/// cached information for the specified category.
mutable llvm::DenseMap<char, ComparisonCategoryInfo> Data;
mutable NamespaceDecl *StdNS = nullptr;
};
} // namespace clang
#endif