llvm-project

Форк
0
/
ComparisonCategories.cpp 
215 строк · 7.3 Кб
1
//===- ComparisonCategories.cpp - Three Way Comparison Data -----*- C++ -*-===//
2
//
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
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
//  This file defines the Comparison Category enum and data types, which
10
//  store the types and expressions needed to support operator<=>
11
//
12
//===----------------------------------------------------------------------===//
13

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"
20
#include <optional>
21

22
using namespace clang;
23

24
std::optional<ComparisonCategoryType>
25
clang::getComparisonCategoryForBuiltinCmp(QualType T) {
26
  using CCT = ComparisonCategoryType;
27

28
  if (T->isIntegralOrEnumerationType())
29
    return CCT::StrongOrdering;
30

31
  if (T->isRealFloatingType())
32
    return CCT::PartialOrdering;
33

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;
39

40
  // TODO: Extend support for operator<=> to ObjC types.
41
  return std::nullopt;
42
}
43

44
bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
45
  assert(VD && "must have var decl");
46
  if (!VD->isUsableInConstantExpressions(VD->getASTContext()))
47
    return false;
48

49
  // Before we attempt to get the value of the first field, ensure that we
50
  // actually have one (and only one) field.
51
  const auto *Record = VD->getType()->getAsCXXRecordDecl();
52
  if (std::distance(Record->field_begin(), Record->field_end()) != 1 ||
53
      !Record->field_begin()->getType()->isIntegralOrEnumerationType())
54
    return false;
55

56
  return true;
57
}
58

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
62
/// (and only) field.
63
///
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();
70
}
71

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())
78
    return &(*It);
79

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()))
85
    return nullptr;
86
  Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));
87
  return &Objects.back();
88
}
89

90
static const NamespaceDecl *lookupStdNamespace(const ASTContext &Ctx,
91
                                               NamespaceDecl *&StdNS) {
92
  if (!StdNS) {
93
    DeclContextLookupResult Lookup =
94
        Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get("std"));
95
    if (!Lookup.empty())
96
      StdNS = dyn_cast<NamespaceDecl>(Lookup.front());
97
  }
98
  return StdNS;
99
}
100

101
static const 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));
106
  if (!Lookup.empty())
107
    if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Lookup.front()))
108
      return RD;
109
  return nullptr;
110
}
111

112
const ComparisonCategoryInfo *
113
ComparisonCategories::lookupInfo(ComparisonCategoryType Kind) const {
114
  auto It = Data.find(static_cast<char>(Kind));
115
  if (It != Data.end())
116
    return &It->second;
117

118
  if (const NamespaceDecl *NS = lookupStdNamespace(Ctx, StdNS))
119
    if (const CXXRecordDecl *RD = lookupCXXRecordDecl(Ctx, NS, Kind))
120
      return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
121

122
  return nullptr;
123
}
124

125
const ComparisonCategoryInfo *
126
ComparisonCategories::lookupInfoForType(QualType Ty) const {
127
  assert(!Ty.isNull() && "type must be non-null");
128
  using CCT = ComparisonCategoryType;
129
  const auto *RD = Ty->getAsCXXRecordDecl();
130
  if (!RD)
131
    return nullptr;
132

133
  // Check to see if we have information for the specified type cached.
134
  const auto *CanonRD = RD->getCanonicalDecl();
135
  for (const auto &KV : Data) {
136
    const ComparisonCategoryInfo &Info = KV.second;
137
    if (CanonRD == Info.Record->getCanonicalDecl())
138
      return &Info;
139
  }
140

141
  if (!RD->getEnclosingNamespaceContext()->isStdNamespace())
142
    return nullptr;
143

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);
148
       I <= End; ++I) {
149
    CCT Kind = static_cast<CCT>(I);
150

151
    // We've found the comparison category type. Build a new cache entry for
152
    // it.
153
    if (getCategoryString(Kind) == RD->getName())
154
      return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
155
  }
156

157
  // We've found nothing. This isn't a comparison category type.
158
  return nullptr;
159
}
160

161
const ComparisonCategoryInfo &ComparisonCategories::getInfoForType(QualType Ty) const {
162
  const ComparisonCategoryInfo *Info = lookupInfoForType(Ty);
163
  assert(Info && "info for comparison category not found");
164
  return *Info;
165
}
166

167
QualType ComparisonCategoryInfo::getType() const {
168
  assert(Record);
169
  return QualType(Record->getTypeForDecl(), 0);
170
}
171

172
StringRef ComparisonCategories::getCategoryString(ComparisonCategoryType Kind) {
173
  using CCKT = ComparisonCategoryType;
174
  switch (Kind) {
175
  case CCKT::PartialOrdering:
176
    return "partial_ordering";
177
  case CCKT::WeakOrdering:
178
    return "weak_ordering";
179
  case CCKT::StrongOrdering:
180
    return "strong_ordering";
181
  }
182
  llvm_unreachable("unhandled cases in switch");
183
}
184

185
StringRef ComparisonCategories::getResultString(ComparisonCategoryResult Kind) {
186
  using CCVT = ComparisonCategoryResult;
187
  switch (Kind) {
188
  case CCVT::Equal:
189
    return "equal";
190
  case CCVT::Equivalent:
191
    return "equivalent";
192
  case CCVT::Less:
193
    return "less";
194
  case CCVT::Greater:
195
    return "greater";
196
  case CCVT::Unordered:
197
    return "unordered";
198
  }
199
  llvm_unreachable("unhandled case in switch");
200
}
201

202
std::vector<ComparisonCategoryResult>
203
ComparisonCategories::getPossibleResultsForType(ComparisonCategoryType Type) {
204
  using CCT = ComparisonCategoryType;
205
  using CCR = ComparisonCategoryResult;
206
  std::vector<CCR> Values;
207
  Values.reserve(4);
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);
214
  return Values;
215
}
216

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.