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