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