214 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- 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/Decl.h"
 | |
| #include "clang/AST/DeclCXX.h"
 | |
| #include "clang/AST/Type.h"
 | |
| #include "llvm/ADT/SmallVector.h"
 | |
| 
 | |
| using namespace clang;
 | |
| 
 | |
| 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 llvm::None;
 | |
| }
 | |
| 
 | |
| bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
 | |
|   assert(VD && "must have var decl");
 | |
|   if (!VD->checkInitIsICE())
 | |
|     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 retreiving 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;
 | |
| }
 |