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 <algorithm>
17
18 #include "etsUnionType.h"
19 #include "checker/ets/conversion.h"
20 #include "checker/types/globalTypesHolder.h"
21 #include "checker/ETSchecker.h"
22 #include "ir/astNode.h"
23
24 namespace panda::es2panda::checker {
ToString(std::stringstream & ss) const25 void ETSUnionType::ToString(std::stringstream &ss) const
26 {
27 for (auto it = constituentTypes_.begin(); it != constituentTypes_.end(); it++) {
28 (*it)->ToString(ss);
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 lubType_->ToAssemblerType(ss);
38 }
39
ToDebugInfoType(std::stringstream & ss) const40 void ETSUnionType::ToDebugInfoType(std::stringstream &ss) const
41 {
42 lubType_->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 ASSERT(constituentTypes_.size() > 1);
49 lubType_ = ComputeLUB(checker);
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
ComputeLUB(ETSChecker * checker) const64 Type *ETSUnionType::ComputeLUB(ETSChecker *checker) const
65 {
66 auto lub = constituentTypes_.front();
67 for (auto *t : constituentTypes_) {
68 if (!checker->IsReferenceType(t)) {
69 return checker->GetGlobalTypesHolder()->GlobalETSObjectType();
70 }
71 if (t->IsETSObjectType() && t->AsETSObjectType()->SuperType() == nullptr) {
72 return checker->GetGlobalTypesHolder()->GlobalETSObjectType();
73 }
74 lub = checker->FindLeastUpperBound(lub, t);
75 }
76 return lub;
77 }
78
Identical(TypeRelation * relation,Type * other)79 void ETSUnionType::Identical(TypeRelation *relation, Type *other)
80 {
81 if (other->IsETSUnionType()) {
82 if (EachTypeRelatedToSomeType(relation, this, other->AsETSUnionType()) &&
83 EachTypeRelatedToSomeType(relation, other->AsETSUnionType(), this)) {
84 relation->Result(true);
85 return;
86 }
87 }
88
89 relation->Result(false);
90 }
91
AssignmentSource(TypeRelation * relation,Type * target)92 bool ETSUnionType::AssignmentSource(TypeRelation *relation, Type *target)
93 {
94 for (auto *it : constituentTypes_) {
95 if (!relation->IsAssignableTo(it, target)) {
96 return false;
97 }
98 }
99
100 return relation->Result(true);
101 }
102
AssignmentTarget(TypeRelation * relation,Type * source)103 void ETSUnionType::AssignmentTarget(TypeRelation *relation, Type *source)
104 {
105 auto *const checker = relation->GetChecker()->AsETSChecker();
106 auto *const refSource =
107 source->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) ? checker->PrimitiveTypeAsETSBuiltinType(source) : source;
108 auto exactType = std::find_if(
109 constituentTypes_.begin(), constituentTypes_.end(), [checker, relation, source, refSource](Type *ct) {
110 if (ct == refSource && source->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) && ct->IsETSObjectType() &&
111 ct->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE)) {
112 relation->GetNode()->SetBoxingUnboxingFlags(checker->GetBoxingFlag(ct));
113 return relation->IsAssignableTo(refSource, ct);
114 }
115 return false;
116 });
117 if (exactType != constituentTypes_.end()) {
118 return;
119 }
120 size_t assignableCount = 0;
121 for (auto *it : constituentTypes_) {
122 if (relation->IsAssignableTo(refSource, it)) {
123 if (refSource != source) {
124 relation->IsAssignableTo(source, it);
125 ASSERT(relation->IsTrue());
126 }
127 ++assignableCount;
128 continue;
129 }
130 bool assignPrimitive = it->IsETSObjectType() &&
131 it->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE) &&
132 source->HasTypeFlag(TypeFlag::ETS_PRIMITIVE);
133 if (assignPrimitive && relation->IsAssignableTo(source, checker->ETSBuiltinTypeAsPrimitiveType(it))) {
134 Type *unboxedIt = checker->ETSBuiltinTypeAsPrimitiveType(it);
135 if (unboxedIt != source) {
136 relation->GetNode()->SetBoxingUnboxingFlags(checker->GetBoxingFlag(it));
137 source->Cast(relation, unboxedIt);
138 ASSERT(relation->IsTrue());
139 }
140 ++assignableCount;
141 }
142 }
143 if (assignableCount > 1) {
144 checker->ThrowTypeError({"Ambiguous assignment: after union normalization several types are assignable."},
145 relation->GetNode()->Start());
146 }
147 relation->Result(assignableCount != 0U);
148 }
149
LinearizeAndEraseIdentical(TypeRelation * relation,ArenaVector<Type * > & constituentTypes)150 void ETSUnionType::LinearizeAndEraseIdentical(TypeRelation *relation, ArenaVector<Type *> &constituentTypes)
151 {
152 auto *const checker = relation->GetChecker()->AsETSChecker();
153 // Firstly, make linearization
154 ArenaVector<Type *> copiedConstituents(checker->Allocator()->Adapter());
155 for (auto *ct : constituentTypes) {
156 if (ct->IsETSUnionType()) {
157 auto otherTypes = ct->AsETSUnionType()->ConstituentTypes();
158 copiedConstituents.insert(copiedConstituents.end(), otherTypes.begin(), otherTypes.end());
159 } else {
160 copiedConstituents.push_back(ct);
161 }
162 }
163 constituentTypes = copiedConstituents;
164 // Removing subtypes should be in the next iteration, especially needed for proper literals removal
165 auto checkSubtyping = [relation](Type *lhs, Type *rhs) {
166 if (lhs == rhs) {
167 return false;
168 }
169 relation->Result(false);
170 lhs->IsSupertypeOf(relation, rhs);
171 bool inheritanceRelation = relation->IsTrue();
172 rhs->IsSupertypeOf(relation, lhs);
173 inheritanceRelation = inheritanceRelation || relation->IsTrue();
174 return inheritanceRelation;
175 };
176 // Secondly, remove identical types
177 auto cmpIt = constituentTypes.begin();
178 while (cmpIt != constituentTypes.end()) {
179 auto it = std::next(cmpIt);
180 while (it != constituentTypes.end()) {
181 if (relation->IsIdenticalTo(*it, *cmpIt) && !checkSubtyping(*it, *cmpIt)) {
182 it = constituentTypes.erase(it);
183 } else {
184 ++it;
185 }
186 }
187 ++cmpIt;
188 }
189 }
190
NormalizeTypes(TypeRelation * relation,ArenaVector<Type * > & constituentTypes)191 void ETSUnionType::NormalizeTypes(TypeRelation *relation, ArenaVector<Type *> &constituentTypes)
192 {
193 auto *const checker = relation->GetChecker()->AsETSChecker();
194 auto etsObject = std::find(constituentTypes.begin(), constituentTypes.end(),
195 checker->GetGlobalTypesHolder()->GlobalETSObjectType());
196 if (etsObject != constituentTypes.end()) {
197 constituentTypes.clear();
198 constituentTypes.push_back(checker->GetGlobalTypesHolder()->GlobalETSObjectType());
199 return;
200 }
201 LinearizeAndEraseIdentical(relation, constituentTypes);
202 // Find number type to remove other numeric types
203 auto numberFound =
204 std::find_if(constituentTypes.begin(), constituentTypes.end(), [](Type *const ct) {
205 return ct->IsETSObjectType() && ct->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_DOUBLE);
206 }) != constituentTypes.end();
207 auto cmpIt = constituentTypes.begin();
208 while (cmpIt != constituentTypes.end()) {
209 auto newEnd = std::remove_if(
210 constituentTypes.begin(), constituentTypes.end(), [relation, checker, cmpIt, numberFound](Type *ct) {
211 bool bothConstants = (*cmpIt)->HasTypeFlag(TypeFlag::CONSTANT) && ct->HasTypeFlag(TypeFlag::CONSTANT);
212 relation->IsSupertypeOf((*cmpIt), ct);
213 bool removeSubtype = ct != *cmpIt && !bothConstants && relation->IsTrue();
214 bool removeNumeric = numberFound && ct->IsETSObjectType() &&
215 ct->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE) &&
216 !ct->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_DOUBLE) &&
217 !ct->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_BOOLEAN);
218 bool removeNever = ct == checker->GetGlobalTypesHolder()->GlobalBuiltinNeverType();
219 return removeSubtype || removeNumeric || removeNever;
220 });
221 if (newEnd != constituentTypes.end()) {
222 constituentTypes.erase(newEnd, constituentTypes.end());
223 cmpIt = constituentTypes.begin();
224 continue;
225 }
226 ++cmpIt;
227 }
228 }
229
Instantiate(ArenaAllocator * allocator,TypeRelation * relation,GlobalTypesHolder * globalTypes)230 Type *ETSUnionType::Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes)
231 {
232 ArenaVector<Type *> copiedConstituents(allocator->Adapter());
233
234 for (auto *it : constituentTypes_) {
235 copiedConstituents.push_back(it->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)
236 ? relation->GetChecker()->AsETSChecker()->PrimitiveTypeAsETSBuiltinType(it)
237 : it->Instantiate(allocator, relation, globalTypes));
238 }
239
240 ETSUnionType::NormalizeTypes(relation, copiedConstituents);
241 if (copiedConstituents.size() == 1) {
242 return copiedConstituents[0];
243 }
244
245 return allocator->New<ETSUnionType>(relation->GetChecker()->AsETSChecker(), std::move(copiedConstituents));
246 }
247
Substitute(TypeRelation * relation,const Substitution * substitution)248 Type *ETSUnionType::Substitute(TypeRelation *relation, const Substitution *substitution)
249 {
250 auto *const checker = relation->GetChecker()->AsETSChecker();
251 ArenaVector<Type *> substitutedConstituents(checker->Allocator()->Adapter());
252 for (auto *ctype : constituentTypes_) {
253 substitutedConstituents.push_back(ctype->Substitute(relation, substitution));
254 }
255 return checker->CreateETSUnionType(std::move(substitutedConstituents));
256 }
257
Cast(TypeRelation * relation,Type * target)258 void ETSUnionType::Cast(TypeRelation *relation, Type *target)
259 {
260 auto *const checker = relation->GetChecker()->AsETSChecker();
261 auto *const refTarget =
262 target->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE) ? checker->PrimitiveTypeAsETSBuiltinType(target) : target;
263 auto exactType =
264 std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [this, relation, refTarget](Type *src) {
265 if (src == refTarget && relation->IsCastableTo(src, refTarget)) {
266 GetLeastUpperBoundType()->Cast(relation, refTarget);
267 ASSERT(relation->IsTrue());
268 return true;
269 }
270 return false;
271 });
272 if (exactType != constituentTypes_.end()) {
273 return;
274 }
275 for (auto *source : constituentTypes_) {
276 if (relation->IsCastableTo(source, refTarget)) {
277 GetLeastUpperBoundType()->Cast(relation, refTarget);
278 ASSERT(relation->IsTrue());
279 if (refTarget != target) {
280 source->Cast(relation, target);
281 ASSERT(relation->IsTrue());
282 ASSERT(relation->GetNode()->GetBoxingUnboxingFlags() != ir::BoxingUnboxingFlags::NONE);
283 }
284 return;
285 }
286 bool castPrimitive = source->IsETSObjectType() &&
287 source->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE) &&
288 target->HasTypeFlag(TypeFlag::ETS_PRIMITIVE);
289 if (castPrimitive && relation->IsCastableTo(checker->ETSBuiltinTypeAsPrimitiveType(source), target)) {
290 ASSERT(relation->IsTrue());
291 return;
292 }
293 }
294
295 conversion::Forbidden(relation);
296 }
297
IsSupertypeOf(TypeRelation * relation,Type * source)298 void ETSUnionType::IsSupertypeOf(TypeRelation *relation, Type *source)
299 {
300 for (auto const &ctype : ConstituentTypes()) {
301 if (relation->IsSupertypeOf(ctype, source)) {
302 return;
303 }
304 }
305 }
306
IsSubtypeOf(TypeRelation * relation,Type * target)307 void ETSUnionType::IsSubtypeOf(TypeRelation *relation, Type *target)
308 {
309 for (auto const &ctype : ConstituentTypes()) {
310 if (!relation->IsSupertypeOf(target, ctype)) {
311 return;
312 }
313 }
314 }
315
CastTarget(TypeRelation * relation,Type * source)316 void ETSUnionType::CastTarget(TypeRelation *relation, Type *source)
317 {
318 Type *targetType = FindTypeIsCastableToThis(relation->GetNode(), relation, source);
319 if (targetType != nullptr) {
320 source->Cast(relation, targetType);
321 return;
322 }
323
324 conversion::Forbidden(relation);
325 }
326
FindTypeIsCastableToThis(ir::Expression * node,TypeRelation * relation,Type * source) const327 Type *ETSUnionType::FindTypeIsCastableToThis(ir::Expression *node, TypeRelation *relation, Type *source) const
328 {
329 ASSERT(node);
330 bool nodeWasSet = false;
331 if (relation->GetNode() == nullptr) {
332 nodeWasSet = true;
333 relation->SetNode(node);
334 }
335 // Prioritize object to object conversion
336 auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [relation, source](Type *target) {
337 relation->IsCastableTo(source, target);
338 return relation->IsTrue() && source->HasTypeFlag(TypeFlag::ETS_ARRAY_OR_OBJECT) &&
339 target->HasTypeFlag(TypeFlag::ETS_ARRAY_OR_OBJECT);
340 });
341 if (it != constituentTypes_.end()) {
342 if (nodeWasSet) {
343 relation->SetNode(nullptr);
344 }
345 return *it;
346 }
347 it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [relation, source](Type *target) {
348 relation->IsCastableTo(source, target);
349 return relation->IsTrue();
350 });
351 if (nodeWasSet) {
352 relation->SetNode(nullptr);
353 }
354 if (it != constituentTypes_.end()) {
355 return *it;
356 }
357 return nullptr;
358 }
359
FindTypeIsCastableToSomeType(ir::Expression * node,TypeRelation * relation,Type * target) const360 Type *ETSUnionType::FindTypeIsCastableToSomeType(ir::Expression *node, TypeRelation *relation, Type *target) const
361 {
362 ASSERT(node);
363 bool nodeWasSet = false;
364 if (relation->GetNode() == nullptr) {
365 nodeWasSet = true;
366 relation->SetNode(node);
367 relation->SetFlags(TypeRelationFlag::CASTING_CONTEXT);
368 }
369 auto isCastablePred = [](TypeRelation *r, Type *sourceType, Type *targetType) {
370 if (targetType->IsETSUnionType()) {
371 auto *foundTargetType = targetType->AsETSUnionType()->FindTypeIsCastableToThis(r->GetNode(), r, sourceType);
372 r->Result(foundTargetType != nullptr);
373 } else {
374 r->IsCastableTo(sourceType, targetType);
375 }
376 return r->IsTrue();
377 };
378 // Prioritize object to object conversion
379 auto it = std::find_if(
380 constituentTypes_.begin(), constituentTypes_.end(), [relation, target, &isCastablePred](Type *source) {
381 return isCastablePred(relation, source, target) && source->HasTypeFlag(TypeFlag::ETS_ARRAY_OR_OBJECT) &&
382 target->HasTypeFlag(TypeFlag::ETS_ARRAY_OR_OBJECT);
383 });
384 if (it != constituentTypes_.end()) {
385 if (nodeWasSet) {
386 relation->SetNode(nullptr);
387 relation->RemoveFlags(TypeRelationFlag::CASTING_CONTEXT);
388 }
389 return *it;
390 }
391 it = std::find_if(
392 constituentTypes_.begin(), constituentTypes_.end(),
393 [relation, target, &isCastablePred](Type *source) { return isCastablePred(relation, source, target); });
394 if (nodeWasSet) {
395 relation->SetNode(nullptr);
396 relation->RemoveFlags(TypeRelationFlag::CASTING_CONTEXT);
397 }
398 if (it != constituentTypes_.end()) {
399 return *it;
400 }
401 return nullptr;
402 }
403
FindUnboxableType() const404 Type *ETSUnionType::FindUnboxableType() const
405 {
406 auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(),
407 [](Type *t) { return t->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE); });
408 if (it != constituentTypes_.end()) {
409 return *it;
410 }
411 return nullptr;
412 }
413
HasObjectType(ETSObjectFlags flag) const414 bool ETSUnionType::HasObjectType(ETSObjectFlags flag) const
415 {
416 auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(),
417 [flag](Type *t) { return t->AsETSObjectType()->HasObjectFlag(flag); });
418 return it != constituentTypes_.end();
419 }
420
FindExactOrBoxedType(ETSChecker * checker,Type * const type) const421 Type *ETSUnionType::FindExactOrBoxedType(ETSChecker *checker, Type *const type) const
422 {
423 auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [checker, type](Type *ct) {
424 if (ct->IsETSObjectType() && ct->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE)) {
425 auto *const unboxedCt = checker->ETSBuiltinTypeAsPrimitiveType(ct);
426 return unboxedCt == type;
427 }
428 return ct == type;
429 });
430 if (it != constituentTypes_.end()) {
431 return *it;
432 }
433 return nullptr;
434 }
435
436 } // namespace panda::es2panda::checker
437