1 //===- ComparisonCategories.cpp - Three Way Comparison Data -----*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines the Comparison Category enum and data types, which
10 // store the types and expressions needed to support operator<=>
12 //===----------------------------------------------------------------------===//
14 #include "clang/AST/ComparisonCategories.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/Decl.h"
17 #include "clang/AST/DeclCXX.h"
18 #include "clang/AST/Type.h"
19 #include "llvm/ADT/SmallVector.h"
22 using namespace clang
;
24 std::optional
<ComparisonCategoryType
>
25 clang::getComparisonCategoryForBuiltinCmp(QualType T
) {
26 using CCT
= ComparisonCategoryType
;
28 if (T
->isIntegralOrEnumerationType())
29 return CCT::StrongOrdering
;
31 if (T
->isRealFloatingType())
32 return CCT::PartialOrdering
;
34 // C++2a [expr.spaceship]p8: If the composite pointer type is an object
35 // pointer type, p <=> q is of type std::strong_ordering.
36 // Note: this assumes neither operand is a null pointer constant.
37 if (T
->isObjectPointerType())
38 return CCT::StrongOrdering
;
40 // TODO: Extend support for operator<=> to ObjC types.
44 bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
45 assert(VD
&& "must have var decl");
46 if (!VD
->isUsableInConstantExpressions(VD
->getASTContext()))
49 // Before we attempt to get the value of the first field, ensure that we
50 // actually have one (and only one) field.
51 auto *Record
= VD
->getType()->getAsCXXRecordDecl();
52 if (std::distance(Record
->field_begin(), Record
->field_end()) != 1 ||
53 !Record
->field_begin()->getType()->isIntegralOrEnumerationType())
59 /// Attempt to determine the integer value used to represent the comparison
60 /// category result by evaluating the initializer for the specified VarDecl as
61 /// a constant expression and retrieving the value of the class's first
64 /// Note: The STL types are expected to have the form:
65 /// struct X { T value; };
66 /// where T is an integral or enumeration type.
67 llvm::APSInt
ComparisonCategoryInfo::ValueInfo::getIntValue() const {
68 assert(hasValidIntValue() && "must have a valid value");
69 return VD
->evaluateValue()->getStructField(0).getInt();
72 ComparisonCategoryInfo::ValueInfo
*ComparisonCategoryInfo::lookupValueInfo(
73 ComparisonCategoryResult ValueKind
) const {
74 // Check if we already have a cache entry for this value.
75 auto It
= llvm::find_if(
76 Objects
, [&](ValueInfo
const &Info
) { return Info
.Kind
== ValueKind
; });
77 if (It
!= Objects
.end())
80 // We don't have a cached result. Lookup the variable declaration and create
81 // a new entry representing it.
82 DeclContextLookupResult Lookup
= Record
->getCanonicalDecl()->lookup(
83 &Ctx
.Idents
.get(ComparisonCategories::getResultString(ValueKind
)));
84 if (Lookup
.empty() || !isa
<VarDecl
>(Lookup
.front()))
86 Objects
.emplace_back(ValueKind
, cast
<VarDecl
>(Lookup
.front()));
87 return &Objects
.back();
90 static const NamespaceDecl
*lookupStdNamespace(const ASTContext
&Ctx
,
91 NamespaceDecl
*&StdNS
) {
93 DeclContextLookupResult Lookup
=
94 Ctx
.getTranslationUnitDecl()->lookup(&Ctx
.Idents
.get("std"));
96 StdNS
= dyn_cast
<NamespaceDecl
>(Lookup
.front());
101 static CXXRecordDecl
*lookupCXXRecordDecl(const ASTContext
&Ctx
,
102 const NamespaceDecl
*StdNS
,
103 ComparisonCategoryType Kind
) {
104 StringRef Name
= ComparisonCategories::getCategoryString(Kind
);
105 DeclContextLookupResult Lookup
= StdNS
->lookup(&Ctx
.Idents
.get(Name
));
107 if (CXXRecordDecl
*RD
= dyn_cast
<CXXRecordDecl
>(Lookup
.front()))
112 const ComparisonCategoryInfo
*
113 ComparisonCategories::lookupInfo(ComparisonCategoryType Kind
) const {
114 auto It
= Data
.find(static_cast<char>(Kind
));
115 if (It
!= Data
.end())
118 if (const NamespaceDecl
*NS
= lookupStdNamespace(Ctx
, StdNS
))
119 if (CXXRecordDecl
*RD
= lookupCXXRecordDecl(Ctx
, NS
, Kind
))
120 return &Data
.try_emplace((char)Kind
, Ctx
, RD
, Kind
).first
->second
;
125 const ComparisonCategoryInfo
*
126 ComparisonCategories::lookupInfoForType(QualType Ty
) const {
127 assert(!Ty
.isNull() && "type must be non-null");
128 using CCT
= ComparisonCategoryType
;
129 auto *RD
= Ty
->getAsCXXRecordDecl();
133 // Check to see if we have information for the specified type cached.
134 const auto *CanonRD
= RD
->getCanonicalDecl();
135 for (auto &KV
: Data
) {
136 const ComparisonCategoryInfo
&Info
= KV
.second
;
137 if (CanonRD
== Info
.Record
->getCanonicalDecl())
141 if (!RD
->getEnclosingNamespaceContext()->isStdNamespace())
144 // If not, check to see if the decl names a type in namespace std with a name
145 // matching one of the comparison category types.
146 for (unsigned I
= static_cast<unsigned>(CCT::First
),
147 End
= static_cast<unsigned>(CCT::Last
);
149 CCT Kind
= static_cast<CCT
>(I
);
151 // We've found the comparison category type. Build a new cache entry for
153 if (getCategoryString(Kind
) == RD
->getName())
154 return &Data
.try_emplace((char)Kind
, Ctx
, RD
, Kind
).first
->second
;
157 // We've found nothing. This isn't a comparison category type.
161 const ComparisonCategoryInfo
&ComparisonCategories::getInfoForType(QualType Ty
) const {
162 const ComparisonCategoryInfo
*Info
= lookupInfoForType(Ty
);
163 assert(Info
&& "info for comparison category not found");
167 QualType
ComparisonCategoryInfo::getType() const {
169 return QualType(Record
->getTypeForDecl(), 0);
172 StringRef
ComparisonCategories::getCategoryString(ComparisonCategoryType Kind
) {
173 using CCKT
= ComparisonCategoryType
;
175 case CCKT::PartialOrdering
:
176 return "partial_ordering";
177 case CCKT::WeakOrdering
:
178 return "weak_ordering";
179 case CCKT::StrongOrdering
:
180 return "strong_ordering";
182 llvm_unreachable("unhandled cases in switch");
185 StringRef
ComparisonCategories::getResultString(ComparisonCategoryResult Kind
) {
186 using CCVT
= ComparisonCategoryResult
;
190 case CCVT::Equivalent
:
196 case CCVT::Unordered
:
199 llvm_unreachable("unhandled case in switch");
202 std::vector
<ComparisonCategoryResult
>
203 ComparisonCategories::getPossibleResultsForType(ComparisonCategoryType Type
) {
204 using CCT
= ComparisonCategoryType
;
205 using CCR
= ComparisonCategoryResult
;
206 std::vector
<CCR
> Values
;
208 bool IsStrong
= Type
== CCT::StrongOrdering
;
209 Values
.push_back(IsStrong
? CCR::Equal
: CCR::Equivalent
);
210 Values
.push_back(CCR::Less
);
211 Values
.push_back(CCR::Greater
);
212 if (Type
== CCT::PartialOrdering
)
213 Values
.push_back(CCR::Unordered
);