1 /**
2  * Copyright (c) 2023-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 "etsTuple.h"
17 
18 #include "checker/types/ets/etsTupleType.h"
19 #include "ir/astDump.h"
20 
21 namespace ark::es2panda::ir {
22 
TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)23 void ETSTuple::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
24 {
25     for (auto *&it : GetTupleTypeAnnotationsList()) {
26         if (auto *transformedNode = cb(it); it != transformedNode) {
27             it->SetTransformedNode(transformationName, transformedNode);
28             it = static_cast<TypeNode *>(transformedNode);
29         }
30     }
31 
32     if (HasSpreadType()) {
33         if (auto *transformedNode = cb(spreadType_); spreadType_ != transformedNode) {
34             spreadType_->SetTransformedNode(transformationName, transformedNode);
35             spreadType_ = static_cast<TypeNode *>(transformedNode);
36         }
37     }
38 }
39 
Iterate(const NodeTraverser &cb) const40 void ETSTuple::Iterate(const NodeTraverser &cb) const
41 {
42     for (auto *const it : GetTupleTypeAnnotationsList()) {
43         cb(it);
44     }
45 
46     if (HasSpreadType()) {
47         cb(spreadType_);
48     }
49 }
50 
Dump(ir::AstDumper *const dumper) const51 void ETSTuple::Dump(ir::AstDumper *const dumper) const
52 {
53     dumper->Add({{"type", "ETSTuple"},
54                  {"types", AstDumper::Optional(typeAnnotationList_)},
55                  {"spreadType", AstDumper::Nullish(spreadType_)}});
56 }
57 
Dump(ir::SrcDumper *const dumper) const58 void ETSTuple::Dump(ir::SrcDumper *const dumper) const
59 {
60     dumper->Add("[");
61     for (const auto *const typeAnnot : typeAnnotationList_) {
62         typeAnnot->Dump(dumper);
63         if ((typeAnnot != typeAnnotationList_.back()) || (spreadType_ != nullptr)) {
64             dumper->Add(", ");
65         }
66     }
67     if (spreadType_ != nullptr) {
68         dumper->Add("...");
69         spreadType_->Dump(dumper);
70     }
71     dumper->Add("]");
72 }
73 
Compile([[maybe_unused]] compiler::PandaGen *const pg) const74 void ETSTuple::Compile([[maybe_unused]] compiler::PandaGen *const pg) const {}
Compile([[maybe_unused]] compiler::ETSGen *const etsg) const75 void ETSTuple::Compile([[maybe_unused]] compiler::ETSGen *const etsg) const {}
76 
Check([[maybe_unused]] checker::TSChecker *const checker)77 checker::Type *ETSTuple::Check([[maybe_unused]] checker::TSChecker *const checker)
78 {
79     return nullptr;
80 }
81 
Check([[maybe_unused]] checker::ETSChecker *const checker)82 checker::Type *ETSTuple::Check([[maybe_unused]] checker::ETSChecker *const checker)
83 {
84     return GetType(checker);
85 }
86 
CalculateLUBForTuple(checker::ETSChecker *const checker, ArenaVector<checker::Type *> &typeList, checker::Type **spreadTypePtr)87 checker::Type *ETSTuple::CalculateLUBForTuple(checker::ETSChecker *const checker,
88                                               ArenaVector<checker::Type *> &typeList, checker::Type **spreadTypePtr)
89 {
90     auto &spreadType = *spreadTypePtr;
91     if (typeList.empty()) {
92         return spreadType == nullptr ? checker->GlobalETSObjectType() : spreadType;
93     }
94 
95     bool allElementsAreSame = std::all_of(typeList.begin(), typeList.end(), [&checker, &typeList](auto *element) {
96         return checker->Relation()->IsIdenticalTo(typeList[0], element);
97     });
98 
99     if (spreadType != nullptr) {
100         allElementsAreSame = allElementsAreSame && checker->Relation()->IsIdenticalTo(typeList[0], spreadType);
101     }
102 
103     // If only one type present in the tuple, that will be the holder array type. If any two not identical types
104     // present, primitives will be boxed, and LUB is calculated for all of them.
105     // That makes it possible to assign eg. `[int, int, ...int[]]` tuple type to `int[]` array type. Because a
106     // `short[]` array already isn't assignable to `int[]` array, that preserve that the `[int, short, ...int[]]`
107     // tuple type's element type will be calculated to `Object[]`, which is not assignable to `int[]` array either.
108     if (allElementsAreSame) {
109         return typeList[0];
110     }
111     // Other case - promote element types
112     // NOTE(vpukhov): #15570 normalization happens or not?
113     std::for_each(typeList.begin(), typeList.end(), [checker](auto &t) { t = checker->MaybePromotedBuiltinType(t); });
114 
115     auto ctypes = typeList;
116     if (spreadType != nullptr) {
117         spreadType = checker->MaybePromotedBuiltinType(spreadType);
118         ctypes.push_back(spreadType);
119     }
120     return checker->CreateETSUnionType(std::move(ctypes));
121 }
122 
GetType(checker::ETSChecker *const checker)123 checker::Type *ETSTuple::GetType(checker::ETSChecker *const checker)
124 {
125     if (TsType() != nullptr) {
126         return TsType();
127     }
128 
129     ArenaVector<checker::Type *> typeList(checker->Allocator()->Adapter());
130 
131     for (auto *const typeAnnotation : GetTupleTypeAnnotationsList()) {
132         auto *const checkedType = typeAnnotation->GetType(checker);
133         typeList.emplace_back(checkedType);
134     }
135 
136     if (HasSpreadType()) {
137         ASSERT(spreadType_->IsTSArrayType());
138         auto *const arrayType = spreadType_->GetType(checker);
139         ASSERT(arrayType->IsETSArrayType());
140         spreadType_->SetTsType(arrayType->AsETSArrayType()->ElementType());
141     }
142 
143     auto *spreadElementType = spreadType_ != nullptr ? spreadType_->TsType() : nullptr;
144 
145     auto *const tupleType = checker->Allocator()->New<checker::ETSTupleType>(
146         typeList, CalculateLUBForTuple(checker, typeList, &spreadElementType), spreadElementType);
147 
148     SetTsType(tupleType);
149     return TsType();
150 }
151 
152 }  // namespace ark::es2panda::ir
153