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 #include <numeric>
17 #include "etsUnionType.h"
18
19 #include "checker/ets/conversion.h"
20 #include "checker/types/globalTypesHolder.h"
21 #include "checker/ETSchecker.h"
22
23 namespace ark::es2panda::checker {
ToString(std::stringstream & ss,bool precise) const24 void ETSUnionType::ToString(std::stringstream &ss, bool precise) const
25 {
26 for (auto it = constituentTypes_.begin(); it != constituentTypes_.end(); it++) {
27 (*it)->ToString(ss, precise);
28 if (std::next(it) != constituentTypes_.end()) {
29 ss << "|";
30 }
31 }
32 }
33
ToAssemblerType(std::stringstream & ss) const34 void ETSUnionType::ToAssemblerType(std::stringstream &ss) const
35 {
36 assemblerLub_->ToAssemblerTypeWithRank(ss);
37 }
38
ToDebugInfoType(std::stringstream & ss) const39 void ETSUnionType::ToDebugInfoType(std::stringstream &ss) const
40 {
41 assemblerLub_->ToDebugInfoType(ss);
42 }
43
ETSUnionType(ETSChecker * checker,ArenaVector<Type * > && constituentTypes)44 ETSUnionType::ETSUnionType(ETSChecker *checker, ArenaVector<Type *> &&constituentTypes)
45 : Type(TypeFlag::ETS_UNION), constituentTypes_(std::move(constituentTypes))
46 {
47 ASSERT(constituentTypes_.size() > 1);
48 assemblerLub_ = ComputeAssemblerLUB(checker, this);
49 }
50
EachTypeRelatedToSomeType(TypeRelation * relation,ETSUnionType * source,ETSUnionType * target)51 bool ETSUnionType::EachTypeRelatedToSomeType(TypeRelation *relation, ETSUnionType *source, ETSUnionType *target)
52 {
53 return std::all_of(source->constituentTypes_.begin(), source->constituentTypes_.end(),
54 [relation, target](auto *s) { return TypeRelatedToSomeType(relation, s, target); });
55 }
56
TypeRelatedToSomeType(TypeRelation * relation,Type * source,ETSUnionType * target)57 bool ETSUnionType::TypeRelatedToSomeType(TypeRelation *relation, Type *source, ETSUnionType *target)
58 {
59 return std::any_of(target->constituentTypes_.begin(), target->constituentTypes_.end(),
60 [relation, source](auto *t) { return relation->IsIdenticalTo(source, t); });
61 }
62
63 // This function computes effective runtime representation of union type
ComputeAssemblerLUB(ETSChecker * checker,ETSUnionType * un)64 Type *ETSUnionType::ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un)
65 {
66 auto *const apparent = checker->GetApparentType(un);
67 if (!apparent->IsETSUnionType()) {
68 return apparent;
69 }
70 if (apparent != un) {
71 return apparent->AsETSUnionType()->assemblerLub_;
72 }
73 un = apparent->AsETSUnionType();
74
75 Type *lub = nullptr;
76 for (auto *t : un->ConstituentTypes()) {
77 if (t->IsTypeError()) {
78 return checker->GlobalTypeError();
79 }
80 // NOTE(vpukhov): #19701 void refactoring
81 ASSERT(t->IsETSReferenceType() || t->IsETSVoidType());
82 if (t->IsETSNullType() || lub == t) {
83 continue;
84 }
85 // NOTE(vpukhov): #19701 void refactoring
86 if (t->IsETSUndefinedType() || t->IsETSVoidType()) {
87 return checker->GetGlobalTypesHolder()->GlobalETSObjectType();
88 }
89 if (lub == nullptr) {
90 lub = t;
91 continue;
92 }
93 if (t->IsETSObjectType() && lub->IsETSObjectType()) {
94 lub = checker->GetClosestCommonAncestor(lub->AsETSObjectType(), t->AsETSObjectType());
95 } else if (t->IsETSArrayType() && lub->IsETSArrayType()) {
96 // NOTE: can compute "common(lub, t)[]"
97 return checker->GetGlobalTypesHolder()->GlobalETSObjectType();
98 } else {
99 return checker->GetGlobalTypesHolder()->GlobalETSObjectType();
100 }
101 }
102 return checker->GetNonConstantType(lub);
103 }
104
Identical(TypeRelation * relation,Type * other)105 void ETSUnionType::Identical(TypeRelation *relation, Type *other)
106 {
107 if (other->IsETSUnionType()) {
108 if (EachTypeRelatedToSomeType(relation, this, other->AsETSUnionType()) &&
109 EachTypeRelatedToSomeType(relation, other->AsETSUnionType(), this)) {
110 relation->Result(true);
111 return;
112 }
113 }
114
115 relation->Result(false);
116 }
117
AmbiguousUnionOperation(TypeRelation * relation)118 static void AmbiguousUnionOperation(TypeRelation *relation)
119 {
120 auto checker = relation->GetChecker()->AsETSChecker();
121 if (!relation->NoThrow()) {
122 checker->LogTypeError({"Ambiguous union type operation"}, relation->GetNode()->Start());
123 }
124 conversion::Forbidden(relation);
125 }
126
127 template <typename RelFN>
RelationTarget(TypeRelation * relation,Type * source,RelFN const & relFn)128 void ETSUnionType::RelationTarget(TypeRelation *relation, Type *source, RelFN const &relFn)
129 {
130 auto *const checker = relation->GetChecker()->AsETSChecker();
131 auto *const refsource = checker->MaybeBoxType(source);
132
133 relation->Result(false);
134
135 if (refsource != source && !relation->ApplyBoxing()) {
136 return;
137 }
138
139 if (std::any_of(constituentTypes_.begin(), constituentTypes_.end(),
140 [relation, refsource, relFn](auto *t) { return relFn(relation, refsource, t); })) {
141 if (refsource != source) {
142 relation->GetNode()->SetBoxingUnboxingFlags(checker->GetBoxingFlag(refsource));
143 }
144 relation->Result(true);
145 return;
146 }
147
148 if (refsource == source) {
149 relation->IsSupertypeOf(this, refsource);
150 return;
151 }
152
153 bool related = false;
154 for (auto *ct : ConstituentTypes()) {
155 if (relFn(relation, source, checker->MaybeUnboxType(ct))) {
156 if (related) {
157 AmbiguousUnionOperation(relation);
158 }
159 relation->GetNode()->SetBoxingUnboxingFlags(checker->GetBoxingFlag(ct));
160 related = true;
161 }
162 }
163
164 relation->Result(related);
165 }
166
AssignmentSource(TypeRelation * relation,Type * target)167 bool ETSUnionType::AssignmentSource(TypeRelation *relation, Type *target)
168 {
169 auto *const checker = relation->GetChecker()->AsETSChecker();
170 if (target->HasTypeFlag(TypeFlag::PRIMITIVE)) {
171 if (!relation->ApplyUnboxing()) {
172 return relation->Result(false);
173 }
174 relation->GetNode()->SetBoxingUnboxingFlags(
175 relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(checker->MaybeUnboxType(target)));
176 }
177
178 bool isAssignable = false;
179
180 if (!(target->IsETSObjectType() && target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) {
181 isAssignable = std::all_of(constituentTypes_.begin(), constituentTypes_.end(),
182 [relation, target](auto *t) { return relation->IsAssignableTo(t, target); });
183 } else {
184 for (auto it : constituentTypes_) {
185 if (!it->IsETSObjectType() || !it->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) {
186 isAssignable = false;
187 break;
188 }
189
190 if (relation->IsAssignableTo(it, target)) {
191 isAssignable = true;
192 }
193 }
194 }
195
196 return relation->Result(isAssignable);
197 }
198
AssignmentTarget(TypeRelation * relation,Type * source)199 void ETSUnionType::AssignmentTarget(TypeRelation *relation, Type *source)
200 {
201 auto const relFn = []([[maybe_unused]] TypeRelation *rel, [[maybe_unused]] Type *src, [[maybe_unused]] Type *tgt) {
202 return rel->IsAssignableTo(src, tgt);
203 };
204 RelationTarget(relation, source, relFn);
205 }
206
Cast(TypeRelation * relation,Type * target)207 void ETSUnionType::Cast(TypeRelation *relation, Type *target)
208 {
209 auto *const checker = relation->GetChecker()->AsETSChecker();
210
211 if (target->HasTypeFlag(TypeFlag::PRIMITIVE)) {
212 if (!relation->ApplyUnboxing()) {
213 relation->Result(false);
214 return;
215 }
216
217 relation->GetNode()->SetBoxingUnboxingFlags(
218 relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(checker->MaybeUnboxType(target)));
219 }
220
221 if (relation->InCastingContext()) {
222 relation->Result(std::any_of(constituentTypes_.begin(), constituentTypes_.end(),
223 [relation, target](auto *t) { return relation->IsCastableTo(t, target); }));
224 return;
225 }
226
227 relation->Result(std::all_of(constituentTypes_.begin(), constituentTypes_.end(),
228 [relation, target](auto *t) { return relation->IsCastableTo(t, target); }));
229 }
230
CastTarget(TypeRelation * relation,Type * source)231 void ETSUnionType::CastTarget(TypeRelation *relation, Type *source)
232 {
233 auto const relFn = []([[maybe_unused]] TypeRelation *rel, [[maybe_unused]] Type *src, [[maybe_unused]] Type *tgt) {
234 return rel->IsCastableTo(src, tgt);
235 };
236 RelationTarget(relation, source, relFn);
237 }
238
239 static auto constexpr ETS_NORMALIZABLE_NUMERIC = TypeFlag(TypeFlag::ETS_NUMERIC);
240
LargestNumeric(Type * t1,Type * t2)241 static Type *LargestNumeric(Type *t1, Type *t2)
242 {
243 static_assert(TypeFlag::DOUBLE > TypeFlag::FLOAT);
244 static_assert(TypeFlag::FLOAT > TypeFlag::LONG);
245 static_assert(TypeFlag::LONG > TypeFlag::INT);
246 static_assert(TypeFlag::INT > TypeFlag::SHORT);
247 static_assert(TypeFlag::SHORT > TypeFlag::BYTE);
248
249 auto v1 = t1->TypeFlags() & ETS_NORMALIZABLE_NUMERIC;
250 auto v2 = t2->TypeFlags() & ETS_NORMALIZABLE_NUMERIC;
251 ASSERT(helpers::math::IsPowerOfTwo(v1));
252 ASSERT(helpers::math::IsPowerOfTwo(v2));
253 return v1 > v2 ? t1 : t2;
254 }
255
TryMergeTypes(TypeRelation * relation,Type * const t1,Type * const t2)256 static std::optional<Type *> TryMergeTypes(TypeRelation *relation, Type *const t1, Type *const t2)
257 {
258 auto checker = relation->GetChecker()->AsETSChecker();
259 auto never = checker->GetGlobalTypesHolder()->GlobalETSNeverType();
260 if (relation->IsSupertypeOf(t1, t2) || t2 == never) {
261 return t1;
262 }
263 if (relation->IsSupertypeOf(t2, t1) || t1 == never) {
264 return t2;
265 }
266 // NOTE(vpukhov): numerics - clarification required
267 return std::nullopt;
268 }
269
LinearizeAndEraseIdentical(TypeRelation * relation,ArenaVector<Type * > & types)270 void ETSUnionType::LinearizeAndEraseIdentical(TypeRelation *relation, ArenaVector<Type *> &types)
271 {
272 auto *const checker = relation->GetChecker()->AsETSChecker();
273
274 // Linearize
275 size_t const initialSz = types.size();
276 for (size_t i = 0; i < initialSz; ++i) {
277 auto ct = types[i];
278 if (ct->IsETSUnionType()) {
279 auto const &otherTypes = ct->AsETSUnionType()->ConstituentTypes();
280 types.insert(types.end(), otherTypes.begin(), otherTypes.end());
281 types[i] = nullptr;
282 } else if (ct->IsNeverType()) {
283 types[i] = nullptr;
284 }
285 }
286 size_t insPos = 0;
287 for (size_t i = 0; i < types.size(); ++i) {
288 auto *const ct = types[i];
289 if (ct != nullptr) {
290 types[insPos++] = ct;
291 }
292 }
293 types.resize(insPos);
294
295 // Promote primitives
296 for (auto &ct : types) {
297 ct = checker->MaybeBoxType(ct);
298 }
299 // Reduce subtypes
300 for (auto cmpIt = types.begin(); cmpIt != types.end(); ++cmpIt) {
301 for (auto it = std::next(cmpIt); it != types.end();) {
302 auto merged = TryMergeTypes(relation, *cmpIt, *it);
303 if (!merged) {
304 ++it;
305 continue;
306 }
307
308 if (merged == *cmpIt) {
309 it = types.erase(it);
310 continue;
311 }
312
313 cmpIt = types.erase(cmpIt);
314 it = std::next(cmpIt);
315 }
316 }
317 }
318
NormalizeTypes(TypeRelation * relation,ArenaVector<Type * > & types)319 void ETSUnionType::NormalizeTypes(TypeRelation *relation, ArenaVector<Type *> &types)
320 {
321 if (types.size() == 1) {
322 return;
323 }
324 auto const isNumeric = [](auto *ct) { return ct->HasTypeFlag(ETS_NORMALIZABLE_NUMERIC); };
325 if (std::all_of(types.begin(), types.end(), isNumeric)) {
326 types[0] = std::accumulate(std::next(types.begin()), types.end(), types[0], LargestNumeric);
327 types.resize(1);
328 return;
329 }
330 LinearizeAndEraseIdentical(relation, types);
331 }
332
Instantiate(ArenaAllocator * allocator,TypeRelation * relation,GlobalTypesHolder * globalTypes)333 Type *ETSUnionType::Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes)
334 {
335 auto *const checker = relation->GetChecker()->AsETSChecker();
336 ArenaVector<Type *> copiedConstituents(allocator->Adapter());
337 for (auto *it : constituentTypes_) {
338 copiedConstituents.push_back(it->Instantiate(allocator, relation, globalTypes));
339 }
340 return checker->CreateETSUnionType(std::move(copiedConstituents));
341 }
342
Substitute(TypeRelation * relation,const Substitution * substitution)343 Type *ETSUnionType::Substitute(TypeRelation *relation, const Substitution *substitution)
344 {
345 auto *const checker = relation->GetChecker()->AsETSChecker();
346 ArenaVector<Type *> substitutedConstituents(checker->Allocator()->Adapter());
347 for (auto *ctype : constituentTypes_) {
348 substitutedConstituents.push_back(ctype->Substitute(relation, substitution));
349 }
350 return checker->CreateETSUnionType(std::move(substitutedConstituents));
351 }
352
IsSupertypeOf(TypeRelation * relation,Type * source)353 void ETSUnionType::IsSupertypeOf(TypeRelation *relation, Type *source)
354 {
355 for (auto const &ctype : ConstituentTypes()) {
356 if (relation->IsSupertypeOf(ctype, source)) {
357 return;
358 }
359 }
360 }
361
IsSubtypeOf(TypeRelation * relation,Type * target)362 void ETSUnionType::IsSubtypeOf(TypeRelation *relation, Type *target)
363 {
364 for (auto const &ctype : ConstituentTypes()) {
365 if (!relation->IsSupertypeOf(target, ctype)) {
366 return;
367 }
368 }
369 }
370
IsAssignableType(checker::Type * sourceType) const371 bool ETSUnionType::IsAssignableType(checker::Type *sourceType) const noexcept
372 {
373 if (sourceType->IsETSTypeParameter() || sourceType->IsTypeError()) {
374 return true;
375 }
376
377 if (sourceType->IsETSUnionType() || sourceType->IsETSArrayType() || sourceType->IsETSFunctionType()) {
378 return true;
379 }
380
381 return false;
382 }
383
384 // NOTE! When calling this method we assume that 'AssignmentTarget(...)' check was passes successfully,
385 // thus the required assignable type always exists.
GetAssignableType(checker::ETSChecker * checker,checker::Type * sourceType) const386 checker::Type *ETSUnionType::GetAssignableType(checker::ETSChecker *checker, checker::Type *sourceType) const noexcept
387 {
388 if (IsAssignableType(sourceType)) {
389 return sourceType;
390 }
391
392 auto *objectType = sourceType->IsETSObjectType() ? sourceType->AsETSObjectType() : nullptr;
393 if (objectType != nullptr && (!objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE) ||
394 objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_STRING))) {
395 // NOTE: here wo don't cast the actual type to possible base type using in the union, but use it as is!
396 return sourceType;
397 }
398
399 std::map<std::uint32_t, checker::Type *> numericTypes {};
400 bool const isBool = objectType != nullptr ? objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_BOOLEAN)
401 : sourceType->HasTypeFlag(TypeFlag::ETS_BOOLEAN);
402 bool const isChar = objectType != nullptr ? objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR)
403 : sourceType->HasTypeFlag(TypeFlag::CHAR);
404 if (checker::Type *assignableType = GetAssignableBuiltinType(checker, objectType, isBool, isChar, numericTypes);
405 assignableType != nullptr) {
406 return assignableType;
407 }
408
409 if (auto const sourceId =
410 objectType != nullptr ? ETSObjectType::GetPrecedence(checker, objectType) : Type::GetPrecedence(sourceType);
411 sourceId > 0U) {
412 for (auto const [id, type] : numericTypes) {
413 if (id >= sourceId) {
414 return type;
415 }
416 }
417 }
418
419 for (auto *constituentType : constituentTypes_) {
420 if (constituentType->IsETSObjectType() && constituentType->AsETSObjectType()->IsGlobalETSObjectType()) {
421 return constituentType;
422 }
423 }
424
425 return checker->GlobalTypeError();
426 }
427
GetAssignableBuiltinType(checker::ETSChecker * checker,checker::ETSObjectType * sourceType,bool const isBool,bool const isChar,std::map<std::uint32_t,checker::Type * > & numericTypes) const428 checker::Type *ETSUnionType::GetAssignableBuiltinType(
429 checker::ETSChecker *checker, checker::ETSObjectType *sourceType, bool const isBool, bool const isChar,
430 std::map<std::uint32_t, checker::Type *> &numericTypes) const noexcept
431 {
432 checker::Type *assignableType = nullptr;
433
434 for (auto *constituentType : constituentTypes_) {
435 if (!constituentType->IsETSObjectType()) {
436 continue;
437 }
438
439 auto *const type = constituentType->AsETSObjectType();
440 if (type->HasObjectFlag(ETSObjectFlags::BUILTIN_BOOLEAN)) {
441 if (isBool) {
442 assignableType = constituentType;
443 break;
444 }
445 } else if (type->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR)) {
446 if (isChar) {
447 assignableType = constituentType;
448 break;
449 }
450 } else if (auto const id = ETSObjectType::GetPrecedence(checker, type); id > 0U) {
451 numericTypes.emplace(id, constituentType);
452 } else if (assignableType == nullptr && sourceType != nullptr &&
453 checker->Relation()->IsSupertypeOf(type, sourceType)) {
454 assignableType = constituentType;
455 }
456 }
457
458 return assignableType;
459 }
460
ExtractType(checker::ETSChecker * checker,checker::ETSObjectType * sourceType,ArenaVector<Type * > & unionTypes)461 bool ETSUnionType::ExtractType(checker::ETSChecker *checker, checker::ETSObjectType *sourceType,
462 ArenaVector<Type *> &unionTypes) noexcept
463 {
464 std::map<std::uint32_t, ArenaVector<checker::Type *>::const_iterator> numericTypes {};
465 bool const isBool = sourceType->HasObjectFlag(ETSObjectFlags::BUILTIN_BOOLEAN);
466 bool const isChar = sourceType->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR);
467
468 bool rc = false;
469 auto it = unionTypes.cbegin();
470 while (it != unionTypes.cend()) {
471 auto *constituentType = *it;
472 // Because 'instanceof' expression does not check for type parameters, then for generic types we should
473 // consider that expressions like 'SomeType<U...>' and 'SomeType<T...>' are identical for smart casting.
474 // We also have to pass through all the union to process cases like 'C<T>|A|B|C<U>|undefined`
475 if (constituentType->HasTypeFlag(checker::TypeFlag::GENERIC)) {
476 constituentType = constituentType->Clone(checker);
477 constituentType->RemoveTypeFlag(checker::TypeFlag::GENERIC);
478 }
479
480 if (checker->Relation()->IsIdenticalTo(constituentType, sourceType)) {
481 rc = true;
482 it = unionTypes.erase(it);
483 continue;
484 }
485
486 if (checker->Relation()->IsSupertypeOf(constituentType, sourceType)) {
487 rc = true;
488 } else if (!rc && constituentType->IsETSObjectType()) {
489 auto *const objectType = (*it)->AsETSObjectType();
490 if (isBool && objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_BOOLEAN)) {
491 unionTypes.erase(it);
492 return true;
493 }
494
495 if (isChar && objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR)) {
496 unionTypes.erase(it);
497 return true;
498 }
499
500 if (auto const id = ETSObjectType::GetPrecedence(checker, objectType); id > 0U) {
501 numericTypes.emplace(id, it);
502 }
503 }
504
505 ++it;
506 }
507
508 if (rc) {
509 return true;
510 }
511
512 if (auto const sourceId = ETSObjectType::GetPrecedence(checker, sourceType); sourceId > 0U) {
513 for (auto const [id, it1] : numericTypes) {
514 if (id >= sourceId) {
515 unionTypes.erase(it1);
516 return true;
517 }
518 }
519 }
520
521 return false;
522 }
523
ExtractType(checker::ETSChecker * checker,checker::ETSArrayType * sourceType,ArenaVector<Type * > & unionTypes)524 bool ETSUnionType::ExtractType(checker::ETSChecker *checker, checker::ETSArrayType *sourceType,
525 ArenaVector<Type *> &unionTypes) noexcept
526 {
527 auto it = unionTypes.cbegin();
528 while (it != unionTypes.cend()) {
529 auto *const constituentType = *it;
530 if (constituentType != nullptr && constituentType->IsETSArrayType()) {
531 if (checker->Relation()->IsIdenticalTo(constituentType, sourceType)) {
532 unionTypes.erase(it);
533 return true;
534 }
535 if (checker->Relation()->IsSupertypeOf(constituentType, sourceType)) {
536 return true;
537 }
538 }
539 ++it;
540 }
541
542 for (auto const &constituentType : unionTypes) {
543 if (constituentType != nullptr && constituentType->IsETSObjectType() &&
544 constituentType->AsETSObjectType()->IsGlobalETSObjectType()) {
545 return true;
546 }
547 }
548
549 return false;
550 }
551
GetComplimentaryType(ETSChecker * const checker,checker::Type * sourceType)552 std::pair<checker::Type *, checker::Type *> ETSUnionType::GetComplimentaryType(ETSChecker *const checker,
553 checker::Type *sourceType)
554 {
555 ArenaVector<Type *> unionTypes(checker->Allocator()->Adapter());
556 for (auto *it : constituentTypes_) {
557 unionTypes.emplace_back(it);
558 }
559 bool ok = true;
560
561 if (sourceType->IsETSUnionType()) {
562 for (auto *const constituentType : sourceType->AsETSUnionType()->ConstituentTypes()) {
563 if (ok = ExtractType(checker, constituentType->AsETSObjectType(), unionTypes); !ok) {
564 break;
565 }
566 }
567 } else if (sourceType->IsETSArrayType()) {
568 ok = ExtractType(checker, sourceType->AsETSArrayType(), unionTypes);
569 } else {
570 // NOTE(vpukhov): #19701 void refactoring
571 if (sourceType->IsETSPrimitiveType() && !sourceType->IsETSVoidType()) {
572 sourceType = checker->MaybeBoxInRelation(sourceType);
573 } else if (sourceType->HasTypeFlag(checker::TypeFlag::GENERIC)) {
574 // Because 'instanceof' expression does not check for type parameters, then for generic types we should
575 // consider that expressions like 'SomeType<U>' and 'SomeType<T>' are identical for smart casting.
576 sourceType = sourceType->Clone(checker);
577 sourceType->RemoveTypeFlag(checker::TypeFlag::GENERIC);
578 }
579
580 if (sourceType->IsETSObjectType()) {
581 ok = ExtractType(checker, sourceType->AsETSObjectType(), unionTypes);
582 }
583 }
584
585 if (!ok) {
586 return std::make_pair(checker->GetGlobalTypesHolder()->GlobalNeverType(), this);
587 }
588
589 checker::Type *complimentaryType;
590 if (unionTypes.size() == 1U) {
591 complimentaryType = unionTypes.front();
592 } else {
593 complimentaryType = checker->CreateETSUnionType(std::move(unionTypes));
594 }
595
596 return std::make_pair(sourceType, complimentaryType);
597 }
598
FindTypeIsCastableToThis(ir::Expression * node,TypeRelation * relation,Type * source) const599 Type *ETSUnionType::FindTypeIsCastableToThis(ir::Expression *node, TypeRelation *relation, Type *source) const
600 {
601 ASSERT(node);
602 bool nodeWasSet = false;
603 if (relation->GetNode() == nullptr) {
604 nodeWasSet = true;
605 relation->SetNode(node);
606 }
607 // Prioritize object to object conversion
608 auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [relation, source](Type *target) {
609 relation->IsCastableTo(source, target);
610 return relation->IsTrue() && source->IsETSReferenceType() && target->IsETSReferenceType();
611 });
612 if (it != constituentTypes_.end()) {
613 if (nodeWasSet) {
614 relation->SetNode(nullptr);
615 }
616 return *it;
617 }
618 it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [relation, source](Type *target) {
619 relation->IsCastableTo(source, target);
620 return relation->IsTrue();
621 });
622 if (nodeWasSet) {
623 relation->SetNode(nullptr);
624 }
625 if (it != constituentTypes_.end()) {
626 return *it;
627 }
628 return nullptr;
629 }
630
FindTypeIsCastableToSomeType(ir::Expression * node,TypeRelation * relation,Type * target) const631 Type *ETSUnionType::FindTypeIsCastableToSomeType(ir::Expression *node, TypeRelation *relation, Type *target) const
632 {
633 ASSERT(node);
634 bool nodeWasSet = false;
635 if (relation->GetNode() == nullptr) {
636 nodeWasSet = true;
637 relation->SetNode(node);
638 relation->SetFlags(TypeRelationFlag::CASTING_CONTEXT);
639 }
640 auto isCastablePred = [](TypeRelation *r, Type *sourceType, Type *targetType) {
641 if (targetType->IsETSUnionType()) {
642 auto *foundTargetType = targetType->AsETSUnionType()->FindTypeIsCastableToThis(r->GetNode(), r, sourceType);
643 r->Result(foundTargetType != nullptr);
644 } else {
645 r->IsCastableTo(sourceType, targetType);
646 }
647 return r->IsTrue();
648 };
649 // Prioritize object to object conversion
650 auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(),
651 [relation, target, &isCastablePred](Type *source) {
652 return isCastablePred(relation, source, target) && source->IsETSReferenceType() &&
653 target->IsETSReferenceType();
654 }); // CC-OFF(G.FMT.02) project code style
655 if (it != constituentTypes_.end()) {
656 if (nodeWasSet) {
657 relation->SetNode(nullptr);
658 relation->RemoveFlags(TypeRelationFlag::CASTING_CONTEXT);
659 }
660 return *it;
661 }
662 it = std::find_if(
663 constituentTypes_.begin(), constituentTypes_.end(),
664 [relation, target, &isCastablePred](Type *source) { return isCastablePred(relation, source, target); });
665 if (nodeWasSet) {
666 relation->SetNode(nullptr);
667 relation->RemoveFlags(TypeRelationFlag::CASTING_CONTEXT);
668 }
669 if (it != constituentTypes_.end()) {
670 return *it;
671 }
672 return nullptr;
673 }
674
FindUnboxableType() const675 Type *ETSUnionType::FindUnboxableType() const
676 {
677 auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(),
678 [](Type *t) { return t->IsETSUnboxableObject(); });
679 if (it != constituentTypes_.end()) {
680 return *it;
681 }
682 return nullptr;
683 }
684
HasObjectType(ETSObjectFlags flag) const685 bool ETSUnionType::HasObjectType(ETSObjectFlags flag) const
686 {
687 auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [flag](Type *t) {
688 return t->IsETSObjectType() && t->AsETSObjectType()->HasObjectFlag(flag);
689 });
690 return it != constituentTypes_.end();
691 }
692
FindExactOrBoxedType(ETSChecker * checker,Type * const type) const693 Type *ETSUnionType::FindExactOrBoxedType(ETSChecker *checker, Type *const type) const
694 {
695 auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [checker, type](Type *ct) {
696 if (ct->IsETSUnboxableObject()) {
697 auto *const unboxedCt = checker->MaybeUnboxInRelation(ct);
698 return unboxedCt == type;
699 }
700 return ct == type;
701 });
702 if (it != constituentTypes_.end()) {
703 return *it;
704 }
705 return nullptr;
706 }
707
ResolveConditionExpr() const708 std::tuple<bool, bool> ETSUnionType::ResolveConditionExpr() const
709 {
710 if (PossiblyETSString()) {
711 return {false, false};
712 }
713 if (std::all_of(ConstituentTypes().begin(), ConstituentTypes().end(),
714 [](checker::Type const *ct) { return ct->DefinitelyETSNullish(); })) {
715 return {true, false};
716 }
717 // We have to test if union can contain builtin numerics or string types to infer "true"
718 return {false, false};
719 }
720
HasUndefinedType() const721 bool ETSUnionType::HasUndefinedType() const
722 {
723 for (const auto &type : constituentTypes_) {
724 if (type->IsETSUndefinedType()) {
725 return true;
726 }
727 }
728 return false;
729 }
730
HasType(Type * type) const731 bool ETSUnionType::HasType(Type *type) const
732 {
733 for (const auto &cType : constituentTypes_) {
734 if (cType == type) {
735 return true;
736 }
737 }
738 return false;
739 }
740
HasNullishType(const ETSChecker * checker) const741 bool ETSUnionType::HasNullishType(const ETSChecker *checker) const
742 {
743 return HasType(checker->GlobalETSNullType()) || HasType(checker->GlobalETSUndefinedType());
744 }
745
IsOverlapWith(TypeRelation * relation,Type * type)746 bool ETSUnionType::IsOverlapWith(TypeRelation *relation, Type *type)
747 {
748 // NOTE(aakmaev): replace this func with intersection type when it will be implemented
749 for (auto const &ct : ConstituentTypes()) {
750 if (type->IsETSUnionType() && type->AsETSUnionType()->IsOverlapWith(relation, ct)) {
751 return true;
752 }
753 if (relation->IsSupertypeOf(ct, type) || relation->IsSupertypeOf(type, ct)) {
754 return true;
755 }
756 }
757 return false;
758 }
759
GetNonConstantTypes(ETSChecker * checker,const ArenaVector<Type * > & types)760 ArenaVector<Type *> ETSUnionType::GetNonConstantTypes(ETSChecker *checker, const ArenaVector<Type *> &types)
761 {
762 ArenaVector<Type *> nonConstTypes(checker->Allocator()->Adapter());
763 for (const auto &ct : types) {
764 nonConstTypes.push_back(checker->GetNonConstantType(ct));
765 }
766 return nonConstTypes;
767 }
768
769 } // namespace ark::es2panda::checker
770