1 /**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_RELATION_H
17 #define ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_RELATION_H
18
19 #include "lexer/token/sourceLocation.h"
20 #include "lexer/token/tokenType.h"
21 #include "util/ustring.h"
22 #include "util/enumbitops.h"
23
24 #include "macros.h"
25
26 #include <unordered_map>
27 #include <variant>
28
29 namespace panda::es2panda::ir {
30 class Expression;
31 } // namespace panda::es2panda::ir
32
33 namespace panda::es2panda::checker {
34 class Signature;
35 class IndexInfo;
36 class Type;
37 class Checker;
38
39 enum class TypeRelationFlag : uint32_t {
40 NONE = 0U,
41 NARROWING = 1U << 0U,
42 WIDENING = 1U << 1U,
43 BOXING = 1U << 2U,
44 UNBOXING = 1U << 3U,
45 CAPTURE = 1U << 4U,
46 STRING = 1U << 5U,
47 VALUE_SET = 1U << 6U,
48 UNCHECKED = 1U << 7U,
49 NO_THROW = 1U << 8U,
50 SELF_REFERENCE = 1U << 9U,
51 NO_RETURN_TYPE_CHECK = 1U << 10U,
52 DIRECT_RETURN = 1U << 11U,
53 NO_WIDENING = 1U << 12U,
54 NO_BOXING = 1U << 13U,
55 NO_UNBOXING = 1U << 14U,
56 ONLY_CHECK_WIDENING = 1U << 15U,
57 ONLY_CHECK_BOXING_UNBOXING = 1U << 16U,
58 IN_ASSIGNMENT_CONTEXT = 1U << 17U,
59 IN_CASTING_CONTEXT = 1U << 18U,
60 UNCHECKED_CAST = 1U << 19U,
61 IGNORE_TYPE_PARAMETERS = 1U << 20U,
62 CHECK_PROXY = 1U << 21U,
63 NO_CHECK_TRAILING_LAMBDA = 1U << 23U,
64 NO_THROW_GENERIC_TYPEALIAS = 1U << 24U,
65
66 ASSIGNMENT_CONTEXT = WIDENING | BOXING | UNBOXING,
67 CASTING_CONTEXT = NARROWING | WIDENING | BOXING | UNBOXING | UNCHECKED_CAST,
68 };
69
70 enum class RelationResult { TRUE, FALSE, UNKNOWN, MAYBE, CACHE_MISS, ERROR };
71
72 enum class RelationType { COMPARABLE, ASSIGNABLE, IDENTICAL, UNCHECKED_CASTABLE, SUPERTYPE };
73
DEFINE_BITOPS(TypeRelationFlag)74 DEFINE_BITOPS(TypeRelationFlag)
75
76 class RelationKey {
77 public:
78 uint64_t sourceId;
79 uint64_t targetId;
80 };
81
82 class RelationKeyHasher {
83 public:
operator()84 size_t operator()(const RelationKey &key) const noexcept
85 {
86 return static_cast<size_t>(key.sourceId ^ key.targetId);
87 }
88 };
89
90 class RelationKeyComparator {
91 public:
operator()92 bool operator()(const RelationKey &lhs, const RelationKey &rhs) const
93 {
94 return lhs.sourceId == rhs.sourceId && lhs.targetId == rhs.targetId;
95 }
96 };
97
98 class RelationEntry {
99 public:
100 RelationResult result;
101 RelationType type;
102 };
103
104 using RelationMap = std::unordered_map<RelationKey, RelationEntry, RelationKeyHasher, RelationKeyComparator>;
105
106 class RelationHolder {
107 public:
108 RelationMap cached;
109 RelationType type {};
110 };
111
112 class AsSrc {
113 public:
AsSrc(const Type * type)114 explicit AsSrc(const Type *type) : type_(const_cast<Type *>(type)) {}
115
GetType()116 const Type *GetType() const
117 {
118 return type_;
119 }
120
121 private:
122 Type *type_;
123 };
124
125 using TypeErrorMessageElement =
126 std::variant<const Type *, AsSrc, char *, util::StringView, lexer::TokenType, size_t, const Signature *>;
127
128 class TypeRelation {
129 public:
TypeRelation(Checker * checker)130 explicit TypeRelation(Checker *checker)
131 : checker_(checker), result_(RelationResult::FALSE), instantiationRecursionMap_(Allocator()->Adapter())
132 {
133 }
134
IsTrue()135 bool IsTrue() const
136 {
137 return result_ == RelationResult::TRUE;
138 }
139
IsError()140 bool IsError() const
141 {
142 return result_ == RelationResult::ERROR;
143 }
144
ApplyNarrowing()145 bool ApplyNarrowing() const
146 {
147 return (flags_ & TypeRelationFlag::NARROWING) != 0;
148 }
149
ApplyWidening()150 bool ApplyWidening() const
151 {
152 return (flags_ & TypeRelationFlag::WIDENING) != 0;
153 }
154
ApplyBoxing()155 bool ApplyBoxing() const
156 {
157 return (flags_ & TypeRelationFlag::BOXING) != 0;
158 }
159
ApplyUnboxing()160 bool ApplyUnboxing() const
161 {
162 return (flags_ & TypeRelationFlag::UNBOXING) != 0;
163 }
164
NoReturnTypeCheck()165 bool NoReturnTypeCheck() const
166 {
167 return (flags_ & TypeRelationFlag::NO_RETURN_TYPE_CHECK) != 0;
168 }
169
DirectReturn()170 bool DirectReturn() const
171 {
172 return (flags_ & TypeRelationFlag::DIRECT_RETURN) != 0;
173 }
174
InAssignmentContext()175 bool InAssignmentContext() const
176 {
177 return (flags_ & TypeRelationFlag::IN_ASSIGNMENT_CONTEXT) != 0;
178 }
179
OnlyCheckWidening()180 bool OnlyCheckWidening() const
181 {
182 return (flags_ & TypeRelationFlag::ONLY_CHECK_WIDENING) != 0;
183 }
184
OnlyCheckBoxingUnboxing()185 bool OnlyCheckBoxingUnboxing() const
186 {
187 return (flags_ & TypeRelationFlag::ONLY_CHECK_BOXING_UNBOXING) != 0;
188 }
189
IgnoreTypeParameters()190 bool IgnoreTypeParameters() const
191 {
192 return (flags_ & TypeRelationFlag::IGNORE_TYPE_PARAMETERS) != 0;
193 }
194
InCastingContext()195 [[nodiscard]] bool InCastingContext() const noexcept
196 {
197 return (flags_ & TypeRelationFlag::IN_CASTING_CONTEXT) != 0;
198 }
199
UncheckedCast()200 [[nodiscard]] bool UncheckedCast() const noexcept
201 {
202 return (flags_ & TypeRelationFlag::UNCHECKED_CAST) != 0;
203 }
204
NoThrowGenericTypeAlias()205 [[nodiscard]] bool NoThrowGenericTypeAlias() const noexcept
206 {
207 return (flags_ & TypeRelationFlag::NO_THROW_GENERIC_TYPEALIAS) != 0;
208 }
209
GetChecker()210 const Checker *GetChecker() const
211 {
212 return checker_;
213 }
214
GetNode()215 ir::Expression *GetNode() const
216 {
217 return node_;
218 }
219
GetChecker()220 Checker *GetChecker()
221 {
222 return checker_;
223 }
224
IncreaseTypeRecursionCount(Type * const type)225 void IncreaseTypeRecursionCount(Type *const type)
226 {
227 if (const auto foundType = instantiationRecursionMap_.find(type);
228 foundType != instantiationRecursionMap_.end()) {
229 foundType->second += 1;
230 return;
231 }
232
233 instantiationRecursionMap_.insert({type, 1});
234 }
235
TypeInstantiationPossible(Type * const type)236 bool TypeInstantiationPossible(Type *const type)
237 {
238 // This limitation makes sure that no type can be instantiated in infinite recursion. When declaring generic
239 // classes with recursive types, so the generic class itself, we need to allow 2 depth of recursion, to make it
240 // possible to reference the correct types of it's members and methods. 2 is possibly enough, because if we
241 // chain expressions, every one of them will be rechecked separately, thus allowing another 2 recursion.
242 constexpr auto MAX_RECURSIVE_TYPE_INST = 2;
243 const auto foundType = instantiationRecursionMap_.find(type);
244 return foundType == instantiationRecursionMap_.end() ? true : (foundType->second < MAX_RECURSIVE_TYPE_INST);
245 }
246
DecreaseTypeRecursionCount(Type * const type)247 void DecreaseTypeRecursionCount(Type *const type)
248 {
249 const auto foundType = instantiationRecursionMap_.find(type);
250 if (foundType == instantiationRecursionMap_.end()) {
251 return;
252 }
253
254 if (foundType->second > 1) {
255 foundType->second -= 1;
256 return;
257 }
258
259 instantiationRecursionMap_.erase(type);
260 }
261
262 bool IsIdenticalTo(Type *source, Type *target);
263 bool IsIdenticalTo(Signature *source, Signature *target);
264 bool IsIdenticalTo(IndexInfo *source, IndexInfo *target);
265 bool IsAssignableTo(Type *source, Type *target);
266 bool IsComparableTo(Type *source, Type *target);
267 bool IsCastableTo(Type *source, Type *target);
268 bool IsSupertypeOf(Type *super, Type *sub);
269 void RaiseError(const std::string &errMsg, const lexer::SourcePosition &loc) const;
270 void RaiseError(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &loc) const;
271
Result(bool res)272 bool Result(bool res)
273 {
274 result_ = res ? RelationResult::TRUE : RelationResult::FALSE;
275 return res;
276 }
277
Result(RelationResult res)278 void Result(RelationResult res)
279 {
280 result_ = res;
281 }
282
SetNode(ir::Expression * node)283 void SetNode(ir::Expression *node)
284 {
285 node_ = node;
286 }
287
SetFlags(TypeRelationFlag flags)288 void SetFlags(TypeRelationFlag flags)
289 {
290 flags_ = flags;
291 }
292
RemoveFlags(TypeRelationFlag flags)293 void RemoveFlags(TypeRelationFlag flags)
294 {
295 flags_ &= ~flags;
296 }
297
298 ArenaAllocator *Allocator();
299
300 friend class SavedTypeRelationFlagsContext;
301
302 private:
303 RelationResult CacheLookup(const Type *source, const Type *target, const RelationHolder &holder,
304 RelationType type) const;
305
306 Checker *checker_;
307 RelationResult result_ {};
308 TypeRelationFlag flags_ {};
309 ir::Expression *node_ {};
310 ArenaMap<checker::Type *, int8_t> instantiationRecursionMap_;
311 };
312 class SavedTypeRelationFlagsContext {
313 public:
SavedTypeRelationFlagsContext(TypeRelation * relation,TypeRelationFlag newFlag)314 explicit SavedTypeRelationFlagsContext(TypeRelation *relation, TypeRelationFlag newFlag)
315 : relation_(relation), prev_(relation->flags_)
316 {
317 relation_->flags_ = newFlag;
318 }
319
320 NO_COPY_SEMANTIC(SavedTypeRelationFlagsContext);
321 DEFAULT_MOVE_SEMANTIC(SavedTypeRelationFlagsContext);
322
~SavedTypeRelationFlagsContext()323 ~SavedTypeRelationFlagsContext()
324 {
325 relation_->flags_ = prev_;
326 }
327
328 private:
329 TypeRelation *relation_;
330 TypeRelationFlag prev_;
331 };
332 } // namespace panda::es2panda::checker
333
334 #endif /* TYPESCRIPT_TYPES_TYPE_RELATION_H */
335