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