Coverage Report

Created: 2021-09-21 08:58

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/AST/ComparisonCategories.cpp
Line
Count
Source (jump to first uncovered line)
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
21
using namespace clang;
22
23
Optional<ComparisonCategoryType>
24
443
clang::getComparisonCategoryForBuiltinCmp(QualType T) {
25
443
  using CCT = ComparisonCategoryType;
26
27
443
  if (T->isIntegralOrEnumerationType())
28
383
    return CCT::StrongOrdering;
29
30
60
  if (T->isRealFloatingType())
31
27
    return CCT::PartialOrdering;
32
33
  // C++2a [expr.spaceship]p8: If the composite pointer type is an object
34
  // pointer type, p <=> q is of type std::strong_ordering.
35
  // Note: this assumes neither operand is a null pointer constant.
36
33
  if (T->isObjectPointerType())
37
17
    return CCT::StrongOrdering;
38
39
  // TODO: Extend support for operator<=> to ObjC types.
40
16
  return llvm::None;
41
33
}
42
43
565
bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
44
565
  assert(VD && "must have var decl");
45
565
  if (!VD->isUsableInConstantExpressions(VD->getASTContext()))
46
0
    return false;
47
48
  // Before we attempt to get the value of the first field, ensure that we
49
  // actually have one (and only one) field.
50
565
  auto *Record = VD->getType()->getAsCXXRecordDecl();
51
565
  if (std::distance(Record->field_begin(), Record->field_end()) != 1 ||
52
565
      !Record->field_begin()->getType()->isIntegralOrEnumerationType())
53
0
    return false;
54
55
565
  return true;
56
565
}
57
58
/// Attempt to determine the integer value used to represent the comparison
59
/// category result by evaluating the initializer for the specified VarDecl as
60
/// a constant expression and retrieving the value of the class's first
61
/// (and only) field.
62
///
63
/// Note: The STL types are expected to have the form:
64
///    struct X { T value; };
65
/// where T is an integral or enumeration type.
66
40
llvm::APSInt ComparisonCategoryInfo::ValueInfo::getIntValue() const {
67
40
  assert(hasValidIntValue() && "must have a valid value");
68
0
  return VD->evaluateValue()->getStructField(0).getInt();
69
40
}
70
71
ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
72
583
    ComparisonCategoryResult ValueKind) const {
73
  // Check if we already have a cache entry for this value.
74
583
  auto It = llvm::find_if(
75
788
      Objects, [&](ValueInfo const &Info) { return Info.Kind == ValueKind; });
76
583
  if (It != Objects.end())
77
365
    return &(*It);
78
79
  // We don't have a cached result. Lookup the variable declaration and create
80
  // a new entry representing it.
81
218
  DeclContextLookupResult Lookup = Record->getCanonicalDecl()->lookup(
82
218
      &Ctx.Idents.get(ComparisonCategories::getResultString(ValueKind)));
83
218
  if (Lookup.empty() || 
!isa<VarDecl>(Lookup.front())217
)
84
1
    return nullptr;
85
217
  Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));
86
217
  return &Objects.back();
87
218
}
88
89
static const NamespaceDecl *lookupStdNamespace(const ASTContext &Ctx,
90
70
                                               NamespaceDecl *&StdNS) {
91
70
  if (!StdNS) {
92
63
    DeclContextLookupResult Lookup =
93
63
        Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get("std"));
94
63
    if (!Lookup.empty())
95
57
      StdNS = dyn_cast<NamespaceDecl>(Lookup.front());
96
63
  }
97
70
  return StdNS;
98
70
}
99
100
static CXXRecordDecl *lookupCXXRecordDecl(const ASTContext &Ctx,
101
                                          const NamespaceDecl *StdNS,
102
64
                                          ComparisonCategoryType Kind) {
103
64
  StringRef Name = ComparisonCategories::getCategoryString(Kind);
104
64
  DeclContextLookupResult Lookup = StdNS->lookup(&Ctx.Idents.get(Name));
105
64
  if (!Lookup.empty())
106
64
    if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Lookup.front()))
107
64
      return RD;
108
0
  return nullptr;
109
64
}
110
111
const ComparisonCategoryInfo *
112
502
ComparisonCategories::lookupInfo(ComparisonCategoryType Kind) const {
113
502
  auto It = Data.find(static_cast<char>(Kind));
114
502
  if (It != Data.end())
115
432
    return &It->second;
116
117
70
  if (const NamespaceDecl *NS = lookupStdNamespace(Ctx, StdNS))
118
64
    if (CXXRecordDecl *RD = lookupCXXRecordDecl(Ctx, NS, Kind))
119
64
      return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
120
121
6
  return nullptr;
122
70
}
123
124
const ComparisonCategoryInfo *
125
380
ComparisonCategories::lookupInfoForType(QualType Ty) const {
126
380
  assert(!Ty.isNull() && "type must be non-null");
127
0
  using CCT = ComparisonCategoryType;
128
380
  auto *RD = Ty->getAsCXXRecordDecl();
129
380
  if (!RD)
130
10
    return nullptr;
131
132
  // Check to see if we have information for the specified type cached.
133
370
  const auto *CanonRD = RD->getCanonicalDecl();
134
434
  for (auto &KV : Data) {
135
434
    const ComparisonCategoryInfo &Info = KV.second;
136
434
    if (CanonRD == Info.Record->getCanonicalDecl())
137
358
      return &Info;
138
434
  }
139
140
12
  if (!RD->getEnclosingNamespaceContext()->isStdNamespace())
141
3
    return nullptr;
142
143
  // If not, check to see if the decl names a type in namespace std with a name
144
  // matching one of the comparison category types.
145
9
  for (unsigned I = static_cast<unsigned>(CCT::First),
146
9
                End = static_cast<unsigned>(CCT::Last);
147
23
       I <= End; 
++I14
) {
148
23
    CCT Kind = static_cast<CCT>(I);
149
150
    // We've found the comparison category type. Build a new cache entry for
151
    // it.
152
23
    if (getCategoryString(Kind) == RD->getName())
153
9
      return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
154
23
  }
155
156
  // We've found nothing. This isn't a comparison category type.
157
0
  return nullptr;
158
9
}
159
160
294
const ComparisonCategoryInfo &ComparisonCategories::getInfoForType(QualType Ty) const {
161
294
  const ComparisonCategoryInfo *Info = lookupInfoForType(Ty);
162
294
  assert(Info && "info for comparison category not found");
163
0
  return *Info;
164
294
}
165
166
1.05k
QualType ComparisonCategoryInfo::getType() const {
167
1.05k
  assert(Record);
168
0
  return QualType(Record->getTypeForDecl(), 0);
169
1.05k
}
170
171
93
StringRef ComparisonCategories::getCategoryString(ComparisonCategoryType Kind) {
172
93
  using CCKT = ComparisonCategoryType;
173
93
  switch (Kind) {
174
17
  case CCKT::PartialOrdering:
175
17
    return "partial_ordering";
176
8
  case CCKT::WeakOrdering:
177
8
    return "weak_ordering";
178
68
  case CCKT::StrongOrdering:
179
68
    return "strong_ordering";
180
93
  }
181
0
  llvm_unreachable("unhandled cases in switch");
182
0
}
183
184
424
StringRef ComparisonCategories::getResultString(ComparisonCategoryResult Kind) {
185
424
  using CCVT = ComparisonCategoryResult;
186
424
  switch (Kind) {
187
122
  case CCVT::Equal:
188
122
    return "equal";
189
18
  case CCVT::Equivalent:
190
18
    return "equivalent";
191
136
  case CCVT::Less:
192
136
    return "less";
193
135
  case CCVT::Greater:
194
135
    return "greater";
195
13
  case CCVT::Unordered:
196
13
    return "unordered";
197
424
  }
198
0
  llvm_unreachable("unhandled case in switch");
199
0
}
200
201
std::vector<ComparisonCategoryResult>
202
68
ComparisonCategories::getPossibleResultsForType(ComparisonCategoryType Type) {
203
68
  using CCT = ComparisonCategoryType;
204
68
  using CCR = ComparisonCategoryResult;
205
68
  std::vector<CCR> Values;
206
68
  Values.reserve(4);
207
68
  bool IsStrong = Type == CCT::StrongOrdering;
208
68
  Values.push_back(IsStrong ? 
CCR::Equal60
:
CCR::Equivalent8
);
209
68
  Values.push_back(CCR::Less);
210
68
  Values.push_back(CCR::Greater);
211
68
  if (Type == CCT::PartialOrdering)
212
7
    Values.push_back(CCR::Unordered);
213
68
  return Values;
214
68
}