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