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/ETSchecker.h"
19 #include "checker/types/ets/etsTupleType.h"
20 #include "ir/astDump.h"
21
22 namespace panda::es2panda::ir {
23
TransformChildren(const NodeTransformer & cb)24 void ETSTuple::TransformChildren([[maybe_unused]] const NodeTransformer &cb)
25 {
26 for (auto *&it : GetTupleTypeAnnotationsList()) {
27 it = static_cast<TypeNode *>(cb(it));
28 }
29
30 if (HasSpreadType()) {
31 cb(spreadType_);
32 }
33 }
34
Iterate(const NodeTraverser & cb) const35 void ETSTuple::Iterate([[maybe_unused]] const NodeTraverser &cb) const
36 {
37 for (auto *const it : GetTupleTypeAnnotationsList()) {
38 cb(it);
39 }
40
41 if (HasSpreadType()) {
42 cb(spreadType_);
43 }
44 }
45
Dump(ir::AstDumper * const dumper) const46 void ETSTuple::Dump(ir::AstDumper *const dumper) const
47 {
48 dumper->Add({{"type", "ETSTuple"},
49 {"types", AstDumper::Optional(typeAnnotationList_)},
50 {"spreadType", AstDumper::Nullish(spreadType_)}});
51 }
52
Dump(ir::SrcDumper * const dumper) const53 void ETSTuple::Dump(ir::SrcDumper *const dumper) const
54 {
55 dumper->Add("[");
56 for (const auto *const typeAnnot : typeAnnotationList_) {
57 typeAnnot->Dump(dumper);
58 if ((typeAnnot != typeAnnotationList_.back()) || (spreadType_ != nullptr)) {
59 dumper->Add(", ");
60 }
61 }
62 if (spreadType_ != nullptr) {
63 dumper->Add("...");
64 spreadType_->Dump(dumper);
65 }
66 dumper->Add("]");
67 }
68
Compile(compiler::PandaGen * const pg) const69 void ETSTuple::Compile([[maybe_unused]] compiler::PandaGen *const pg) const {}
Compile(compiler::ETSGen * const etsg) const70 void ETSTuple::Compile([[maybe_unused]] compiler::ETSGen *const etsg) const {}
71
Check(checker::TSChecker * const checker)72 checker::Type *ETSTuple::Check([[maybe_unused]] checker::TSChecker *const checker)
73 {
74 return nullptr;
75 }
76
Check(checker::ETSChecker * const checker)77 checker::Type *ETSTuple::Check([[maybe_unused]] checker::ETSChecker *const checker)
78 {
79 return GetType(checker);
80 }
81
SetNullUndefinedFlags(std::pair<bool,bool> & containsNullOrUndefined,const checker::Type * const type)82 void ETSTuple::SetNullUndefinedFlags(std::pair<bool, bool> &containsNullOrUndefined, const checker::Type *const type)
83 {
84 if (type->HasTypeFlag(checker::TypeFlag::NULLISH)) {
85 containsNullOrUndefined.first = true;
86 }
87
88 if (type->HasTypeFlag(checker::TypeFlag::UNDEFINED)) {
89 containsNullOrUndefined.second = true;
90 }
91 }
92
CalculateLUBForTuple(checker::ETSChecker * const checker,ArenaVector<checker::Type * > & typeList,checker::Type * const spreadType)93 checker::Type *ETSTuple::CalculateLUBForTuple(checker::ETSChecker *const checker,
94 ArenaVector<checker::Type *> &typeList, checker::Type *const spreadType)
95 {
96 if (typeList.empty()) {
97 return spreadType == nullptr ? checker->GlobalETSObjectType() : spreadType;
98 }
99
100 std::pair<bool, bool> containsNullOrUndefined = {false, false};
101
102 bool allElementsAreSame =
103 std::all_of(typeList.begin(), typeList.end(),
104 [this, &checker, &typeList, &containsNullOrUndefined](checker::Type *const element) {
105 SetNullUndefinedFlags(containsNullOrUndefined, element);
106 return checker->Relation()->IsIdenticalTo(typeList[0], element);
107 });
108
109 if (spreadType != nullptr) {
110 SetNullUndefinedFlags(containsNullOrUndefined, spreadType);
111 allElementsAreSame = allElementsAreSame && checker->Relation()->IsIdenticalTo(typeList[0], spreadType);
112 }
113
114 // If only one type present in the tuple, that will be the holder array type. If any two not identical types
115 // present, primitives will be boxed, and LUB is calculated for all of them.
116 // That makes it possible to assign eg. `[int, int, ...int[]]` tuple type to `int[]` array type. Because a `short[]`
117 // array already isn't assignable to `int[]` array, that preserve that the `[int, short, ...int[]]` tuple type's
118 // element type will be calculated to `Object[]`, which is not assignable to `int[]` array either.
119 if (allElementsAreSame) {
120 return typeList[0];
121 }
122
123 auto *const savedRelationNode = checker->Relation()->GetNode();
124 checker->Relation()->SetNode(this);
125
126 auto getBoxedTypeOrType = [&checker](checker::Type *const type) {
127 auto *const boxedType = checker->PrimitiveTypeAsETSBuiltinType(type);
128 return boxedType == nullptr ? type : boxedType;
129 };
130
131 checker::Type *lubType = getBoxedTypeOrType(typeList[0]);
132
133 for (std::size_t idx = 1; idx < typeList.size(); ++idx) {
134 lubType = checker->FindLeastUpperBound(lubType, getBoxedTypeOrType(typeList[idx]));
135 }
136
137 if (spreadType != nullptr) {
138 lubType = checker->FindLeastUpperBound(lubType, getBoxedTypeOrType(spreadType));
139 }
140
141 const auto nullishUndefinedFlags =
142 (containsNullOrUndefined.first ? checker::TypeFlag::NULLISH | checker::TypeFlag::NULL_TYPE
143 : checker::TypeFlag::NONE) |
144 (containsNullOrUndefined.second ? checker::TypeFlag::UNDEFINED : checker::TypeFlag::NONE);
145
146 if (nullishUndefinedFlags != checker::TypeFlag::NONE) {
147 lubType = checker->CreateNullishType(lubType, nullishUndefinedFlags, checker->Allocator(), checker->Relation(),
148 checker->GetGlobalTypesHolder());
149 }
150
151 checker->Relation()->SetNode(savedRelationNode);
152
153 return lubType;
154 }
155
GetType(checker::ETSChecker * const checker)156 checker::Type *ETSTuple::GetType(checker::ETSChecker *const checker)
157 {
158 if (TsType() != nullptr) {
159 return TsType();
160 }
161
162 ArenaVector<checker::Type *> typeList(checker->Allocator()->Adapter());
163
164 for (auto *const typeAnnotation : GetTupleTypeAnnotationsList()) {
165 auto *const checkedType = checker->GetTypeFromTypeAnnotation(typeAnnotation);
166 typeList.emplace_back(checkedType);
167 }
168
169 if (HasSpreadType()) {
170 ASSERT(spreadType_->IsTSArrayType());
171 auto *const arrayType = spreadType_->GetType(checker);
172 ASSERT(arrayType->IsETSArrayType());
173 spreadType_->SetTsType(arrayType->AsETSArrayType()->ElementType());
174 }
175
176 auto *const spreadElementType = spreadType_ != nullptr ? spreadType_->TsType() : nullptr;
177
178 auto *const tupleType = checker->Allocator()->New<checker::ETSTupleType>(
179 typeList, CalculateLUBForTuple(checker, typeList, spreadElementType), spreadElementType);
180
181 SetTsType(tupleType);
182 return TsType();
183 }
184
185 } // namespace panda::es2panda::ir
186