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