• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 namespace ark::es2panda::ir {
25 class Expression;
26 }  // namespace ark::es2panda::ir
27 
28 namespace ark::es2panda::checker {
29 class Signature;
30 class IndexInfo;
31 class Type;
32 class Checker;
33 
34 using ENUMBITOPS_OPERATORS;
35 
36 enum class TypeRelationFlag : uint32_t {
37     NONE = 0U,
38     NARROWING = 1U << 0U,
39     WIDENING = 1U << 1U,
40     BOXING = 1U << 2U,
41     UNBOXING = 1U << 3U,
42     CAPTURE = 1U << 4U,
43     STRING = 1U << 5U,
44     VALUE_SET = 1U << 6U,
45     UNCHECKED = 1U << 7U,
46     NO_THROW = 1U << 8U,
47     SELF_REFERENCE = 1U << 9U,
48     NO_RETURN_TYPE_CHECK = 1U << 10U,
49     DIRECT_RETURN = 1U << 11U,
50     NO_WIDENING = 1U << 12U,
51     NO_BOXING = 1U << 13U,
52     NO_UNBOXING = 1U << 14U,
53     ONLY_CHECK_WIDENING = 1U << 15U,
54     ONLY_CHECK_BOXING_UNBOXING = 1U << 16U,
55     IN_ASSIGNMENT_CONTEXT = 1U << 17U,
56     IN_CASTING_CONTEXT = 1U << 18U,
57     UNCHECKED_CAST = 1U << 19U,
58     IGNORE_TYPE_PARAMETERS = 1U << 20U,
59     CHECK_PROXY = 1U << 21U,
60     NO_CHECK_TRAILING_LAMBDA = 1U << 23U,
61     NO_THROW_GENERIC_TYPEALIAS = 1U << 24U,
62     OVERRIDING_CONTEXT = 1U << 25U,
63     IGNORE_REST_PARAM = 1U << 26U,
64     STRING_TO_CHAR = 1U << 27U,
65 
66     ASSIGNMENT_CONTEXT = WIDENING | BOXING | UNBOXING,
67     BRIDGE_CHECK = OVERRIDING_CONTEXT | IGNORE_TYPE_PARAMETERS | NO_RETURN_TYPE_CHECK,
68     CASTING_CONTEXT = NARROWING | WIDENING | BOXING | UNBOXING | UNCHECKED_CAST,
69 };
70 
71 enum class RelationResult { TRUE, FALSE, UNKNOWN, MAYBE, CACHE_MISS, ERROR };
72 
73 enum class RelationType { COMPARABLE, ASSIGNABLE, IDENTICAL, UNCHECKED_CASTABLE, SUPERTYPE };
74 
75 }  // namespace ark::es2panda::checker
76 
77 template <>
78 struct enumbitops::IsAllowedType<ark::es2panda::checker::TypeRelationFlag> : std::true_type {
79 };
80 
81 namespace ark::es2panda::checker {
82 
83 class RelationKey {
84 public:
85     uint64_t sourceId;
86     uint64_t targetId;
87 };
88 
89 class RelationKeyHasher {
90 public:
91     size_t operator()(const RelationKey &key) const noexcept
92     {
93         return static_cast<size_t>(key.sourceId ^ key.targetId);
94     }
95 };
96 
97 class RelationKeyComparator {
98 public:
99     bool operator()(const RelationKey &lhs, const RelationKey &rhs) const
100     {
101         return lhs.sourceId == rhs.sourceId && lhs.targetId == rhs.targetId;
102     }
103 };
104 
105 class RelationEntry {
106 public:
107     RelationResult result;
108     RelationType type;
109 };
110 
111 using RelationMap = std::unordered_map<RelationKey, RelationEntry, RelationKeyHasher, RelationKeyComparator>;
112 
113 class RelationHolder {
114 public:
115     RelationMap cached;
116     RelationType type {};
117 };
118 
119 class AsSrc {
120 public:
121     explicit AsSrc(const Type *type) : type_(const_cast<Type *>(type)) {}
122 
123     const Type *GetType() const
124     {
125         return type_;
126     }
127 
128 private:
129     Type *type_;
130 };
131 
132 using TypeErrorMessageElement =
133     std::variant<const Type *, AsSrc, char *, util::StringView, lexer::TokenType, size_t, const Signature *>;
134 
135 class TypeRelation {
136 public:
137     explicit TypeRelation(Checker *checker)
138         : checker_(checker), result_(RelationResult::FALSE), instantiationRecursionMap_(Allocator()->Adapter())
139     {
140     }
141 
142     bool IsTrue() const
143     {
144         return result_ == RelationResult::TRUE;
145     }
146 
147     bool IsError() const
148     {
149         return result_ == RelationResult::ERROR;
150     }
151 
152     bool ApplyNarrowing() const
153     {
154         return (flags_ & TypeRelationFlag::NARROWING) != 0;
155     }
156 
157     bool ApplyWidening() const
158     {
159         return (flags_ & TypeRelationFlag::WIDENING) != 0;
160     }
161 
162     bool ApplyBoxing() const
163     {
164         return (flags_ & TypeRelationFlag::BOXING) != 0;
165     }
166 
167     bool ApplyUnboxing() const
168     {
169         return (flags_ & TypeRelationFlag::UNBOXING) != 0;
170     }
171 
172     bool ApplyStringToChar() const
173     {
174         return (flags_ & TypeRelationFlag::STRING_TO_CHAR) != 0;
175     }
176 
177     bool NoReturnTypeCheck() const
178     {
179         return (flags_ & TypeRelationFlag::NO_RETURN_TYPE_CHECK) != 0;
180     }
181 
182     bool DirectReturn() const
183     {
184         return (flags_ & TypeRelationFlag::DIRECT_RETURN) != 0;
185     }
186 
187     bool InAssignmentContext() const
188     {
189         return (flags_ & TypeRelationFlag::IN_ASSIGNMENT_CONTEXT) != 0;
190     }
191 
192     bool OnlyCheckWidening() const
193     {
194         return (flags_ & TypeRelationFlag::ONLY_CHECK_WIDENING) != 0;
195     }
196 
197     bool OnlyCheckBoxingUnboxing() const
198     {
199         return (flags_ & TypeRelationFlag::ONLY_CHECK_BOXING_UNBOXING) != 0;
200     }
201 
202     bool IgnoreTypeParameters() const
203     {
204         return (flags_ & TypeRelationFlag::IGNORE_TYPE_PARAMETERS) != 0;
205     }
206 
207     [[nodiscard]] bool InCastingContext() const noexcept
208     {
209         return (flags_ & TypeRelationFlag::IN_CASTING_CONTEXT) != 0;
210     }
211 
212     [[nodiscard]] bool UncheckedCast() const noexcept
213     {
214         return (flags_ & TypeRelationFlag::UNCHECKED_CAST) != 0;
215     }
216 
217     [[nodiscard]] bool NoThrow() const noexcept
218     {
219         return (flags_ & TypeRelationFlag::NO_THROW) != 0;
220     }
221 
222     [[nodiscard]] bool NoThrowGenericTypeAlias() const noexcept
223     {
224         return (flags_ & TypeRelationFlag::NO_THROW_GENERIC_TYPEALIAS) != 0;
225     }
226 
227     [[nodiscard]] bool IsOverridingCheck() const noexcept
228     {
229         return (flags_ & TypeRelationFlag::OVERRIDING_CONTEXT) != 0;
230     }
231 
232     [[nodiscard]] bool IsBridgeCheck() const noexcept
233     {
234         return (flags_ & TypeRelationFlag::BRIDGE_CHECK) == helpers::ToUnderlying(TypeRelationFlag::BRIDGE_CHECK);
235     }
236 
237     [[nodiscard]] TypeRelationFlag GetTypeRelationFlags() const noexcept
238     {
239         return flags_;
240     }
241 
242     const Checker *GetChecker() const
243     {
244         return checker_;
245     }
246 
247     ir::Expression *GetNode() const
248     {
249         return node_;
250     }
251 
252     Checker *GetChecker()
253     {
254         return checker_;
255     }
256 
257     void IncreaseTypeRecursionCount(Type *const type)
258     {
259         if (const auto foundType = instantiationRecursionMap_.find(type);
260             foundType != instantiationRecursionMap_.end()) {
261             foundType->second += 1;
262             return;
263         }
264 
265         instantiationRecursionMap_.insert({type, 1});
266     }
267 
268     bool IsAtTypeDepthLimit(Type *const type)
269     {
270         // This limitation makes sure that no type can be instantiated in infinite recursion. When declaring generic
271         // classes with recursive types, so the generic class itself, we need to allow 2 depth of recursion, to make it
272         // possible to reference the correct types of it's members and methods. 2 is possibly enough, because if we
273         // chain expressions, every one of them will be rechecked separately, thus allowing another 2 recursion.
274         constexpr auto MAX_RECURSIVE_TYPE_INST = 2;
275         const auto foundType = instantiationRecursionMap_.find(type);
276         return foundType == instantiationRecursionMap_.end() ? true : (foundType->second < MAX_RECURSIVE_TYPE_INST);
277     }
278 
279     void DecreaseTypeRecursionCount(Type *const type)
280     {
281         const auto foundType = instantiationRecursionMap_.find(type);
282         if (foundType == instantiationRecursionMap_.end()) {
283             return;
284         }
285 
286         if (foundType->second > 1) {
287             foundType->second -= 1;
288             return;
289         }
290 
291         instantiationRecursionMap_.erase(type);
292     }
293 
294     //  NOTE: special overloading to be used mainly in ETSCompiler where types and nodes are 'const'.
295     //  Unfortunately now we cannot have only a single method with 'const Types *' because it affects
296     //  a lot of non-const references... :(((
297     bool IsIdenticalTo(Type const *source, Type const *target)
298     {
299         return IsIdenticalTo(const_cast<Type *>(source), const_cast<Type *>(target));
300     }
301     bool IsIdenticalTo(Type *source, Type *target);
302     bool IsIdenticalTo(IndexInfo *source, IndexInfo *target);
303     bool IsCompatibleTo(Signature *source, Signature *target);
304     bool IsAssignableTo(Type *source, Type *target);
305     bool IsComparableTo(Type *source, Type *target);
306     bool IsCastableTo(Type *const source, Type *const target);
307     bool IsSupertypeOf(Type const *super, Type const *sub)
308     {
309         return IsSupertypeOf(const_cast<Type *>(super), const_cast<Type *>(sub));
310     }
311     bool IsSupertypeOf(Type *super, Type *sub);
312     void RaiseError(const std::string &errMsg, const lexer::SourcePosition &loc) const;
313     void RaiseError(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &loc) const;
314     void LogError(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &loc) const;
315 
316     bool Result(bool res)
317     {
318         result_ = res ? RelationResult::TRUE : RelationResult::FALSE;
319         return res;
320     }
321 
322     void Result(RelationResult res)
323     {
324         result_ = res;
325     }
326 
327     void SetNode(ir::Expression *node)
328     {
329         node_ = node;
330     }
331 
332     void SetFlags(TypeRelationFlag flags)
333     {
334         flags_ = flags;
335     }
336 
337     void RemoveFlags(TypeRelationFlag flags)
338     {
339         flags_ &= ~flags;
340     }
341 
342     ArenaAllocator *Allocator();
343 
344     friend class SavedTypeRelationFlagsContext;
345 
346 private:
347     RelationResult CacheLookup(const Type *source, const Type *target, const RelationHolder &holder,
348                                RelationType type) const;
349 
350     Checker *checker_;
351     RelationResult result_ {};
352     TypeRelationFlag flags_ {};
353     ir::Expression *node_ {};
354     ArenaMap<checker::Type *, int8_t> instantiationRecursionMap_;
355 };
356 class SavedTypeRelationFlagsContext {
357 public:
358     explicit SavedTypeRelationFlagsContext(TypeRelation *relation, TypeRelationFlag newFlag)
359         : relation_(relation), prev_(relation->flags_)
360     {
361         relation_->flags_ = newFlag;
362     }
363 
364     NO_COPY_SEMANTIC(SavedTypeRelationFlagsContext);
365     DEFAULT_MOVE_SEMANTIC(SavedTypeRelationFlagsContext);
366 
367     ~SavedTypeRelationFlagsContext()
368     {
369         relation_->flags_ = prev_;
370     }
371 
372 private:
373     TypeRelation *relation_;
374     TypeRelationFlag prev_;
375 };
376 }  // namespace ark::es2panda::checker
377 
378 #endif /* TYPESCRIPT_TYPES_TYPE_RELATION_H */
379