• 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 #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