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