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 "etsTupleType.h"
17
18 #include "checker/ETSchecker.h"
19 #include "checker/ets/conversion.h"
20 #include "ir/ets/etsTuple.h"
21
22 namespace ark::es2panda::checker {
ToString(std::stringstream & ss,bool precise) const23 void ETSTupleType::ToString(std::stringstream &ss, bool precise) const
24 {
25 if (HasTypeFlag(TypeFlag::READONLY)) {
26 ss << "readonly ";
27 }
28
29 ss << "[";
30
31 for (auto it = typeList_.begin(); it != typeList_.end(); it++) {
32 (*it)->ToString(ss, precise);
33
34 if (std::next(it) != typeList_.end()) {
35 ss << ", ";
36 }
37 }
38
39 if (spreadType_ != nullptr) {
40 ss << ", ...";
41 spreadType_->ToString(ss, precise);
42 ss << "[]";
43 }
44
45 ss << "]";
46 }
47
GetTypeAtIndex(const TupleSizeType index) const48 Type *ETSTupleType::GetTypeAtIndex(const TupleSizeType index) const
49 {
50 return index >= GetTupleSize() ? GetSpreadType() : GetTupleTypesList().at(static_cast<std::size_t>(index));
51 }
52
Identical(TypeRelation * const relation,Type * const other)53 void ETSTupleType::Identical([[maybe_unused]] TypeRelation *const relation, Type *const other)
54 {
55 if (!other->IsETSTupleType()) {
56 return;
57 }
58
59 const auto *const otherTuple = other->AsETSTupleType();
60
61 if (GetMinTupleSize() != otherTuple->GetMinTupleSize()) {
62 return;
63 }
64
65 for (TupleSizeType idx = 0; idx < GetMinTupleSize(); ++idx) {
66 if (!relation->IsIdenticalTo(GetTypeAtIndex(idx), otherTuple->GetTypeAtIndex(idx))) {
67 relation->Result(false);
68 return;
69 }
70 }
71
72 if (HasSpreadType() != otherTuple->HasSpreadType()) {
73 relation->Result(false);
74 return;
75 }
76
77 relation->Result(true);
78 }
79
AssignmentSource(TypeRelation * const relation,Type * const target)80 bool ETSTupleType::AssignmentSource(TypeRelation *const relation, Type *const target)
81 {
82 if (!(target->IsETSTupleType() || target->IsETSArrayType())) {
83 return false;
84 }
85
86 if (!target->IsETSTupleType()) {
87 ASSERT(target->IsETSArrayType());
88 auto *const arrayTarget = target->AsETSArrayType();
89
90 const SavedTypeRelationFlagsContext savedFlagsCtx(
91 relation, TypeRelationFlag::NO_BOXING | TypeRelationFlag::NO_UNBOXING | TypeRelationFlag::NO_WIDENING);
92
93 relation->Result(relation->IsAssignableTo(ElementType(), arrayTarget->ElementType()));
94 }
95
96 return relation->IsTrue();
97 }
98
AssignmentTarget(TypeRelation * const relation,Type * const source)99 void ETSTupleType::AssignmentTarget(TypeRelation *const relation, Type *const source)
100 {
101 if (source->HasTypeFlag(TypeFlag::READONLY)) {
102 relation->Result(false);
103 return;
104 }
105
106 if (!(source->IsETSTupleType() || (source->IsETSArrayType() && HasSpreadType()))) {
107 return;
108 }
109
110 if (!source->IsETSTupleType()) {
111 ASSERT(source->IsETSArrayType());
112 auto *const arraySource = source->AsETSArrayType();
113
114 const SavedTypeRelationFlagsContext savedFlagsCtx(
115 relation, TypeRelationFlag::NO_BOXING | TypeRelationFlag::NO_UNBOXING | TypeRelationFlag::NO_WIDENING);
116
117 relation->Result(relation->IsAssignableTo(arraySource->ElementType(), ElementType()));
118 return;
119 }
120
121 const auto *const tupleSource = source->AsETSTupleType();
122
123 if (tupleSource->GetMinTupleSize() != GetMinTupleSize()) {
124 return;
125 }
126
127 for (int32_t idx = 0; idx < GetMinTupleSize(); ++idx) {
128 // because an array assignment to another array simply copies it's memory address, then it's not possible to
129 // make boxing/unboxing/widening for types. Only conversion allowed is reference widening, which won't generate
130 // bytecode for the conversion, same as for arrays.
131
132 const SavedTypeRelationFlagsContext savedFlagsCtx(
133 relation, TypeRelationFlag::NO_BOXING | TypeRelationFlag::NO_UNBOXING | TypeRelationFlag::NO_WIDENING);
134
135 if (!relation->IsIdenticalTo(GetTypeAtIndex(idx), tupleSource->GetTypeAtIndex(idx))) {
136 relation->Result(false);
137 return;
138 }
139 }
140
141 if (!HasSpreadType() && tupleSource->HasSpreadType()) {
142 relation->Result(false);
143 return;
144 }
145
146 relation->Result(true);
147 }
148
Substitute(TypeRelation * relation,const Substitution * substitution)149 Type *ETSTupleType::Substitute(TypeRelation *relation, const Substitution *substitution)
150 {
151 auto *const checker = relation->GetChecker()->AsETSChecker();
152 ArenaVector<Type *> newTypeList(checker->Allocator()->Adapter());
153
154 for (auto *const tupleTypeListElement : GetTupleTypesList()) {
155 newTypeList.emplace_back(tupleTypeListElement->Substitute(relation, substitution));
156 }
157
158 auto *newSpreadType = spreadType_ == nullptr ? nullptr : spreadType_->Substitute(relation, substitution);
159 auto *newElementType = ir::ETSTuple::CalculateLUBForTuple(checker, newTypeList, &newSpreadType);
160 return checker->Allocator()->New<ETSTupleType>(std::move(newTypeList), newElementType, newSpreadType);
161 }
162
Cast(TypeRelation * const relation,Type * const target)163 void ETSTupleType::Cast(TypeRelation *const relation, Type *const target)
164 {
165 // NOTE(mmartin): Might be not the correct casting rules, as these aren't defined yet
166
167 if (!(target->IsETSTupleType() || target->IsETSArrayType())) {
168 conversion::Forbidden(relation);
169 return;
170 }
171
172 if (target->IsETSArrayType() && (!target->IsETSTupleType())) {
173 auto *const arrayTarget = target->AsETSArrayType();
174
175 if (!arrayTarget->ElementType()->IsETSObjectType()) {
176 conversion::Forbidden(relation);
177 return;
178 }
179
180 const SavedTypeRelationFlagsContext savedFlagsCtx(
181 relation, TypeRelationFlag::NO_BOXING | TypeRelationFlag::NO_UNBOXING | TypeRelationFlag::NO_WIDENING);
182
183 const bool elementsAssignable =
184 std::all_of(GetTupleTypesList().begin(), GetTupleTypesList().end(),
185 [&relation, &arrayTarget](auto *const tupleTypeAtIdx) {
186 return relation->IsAssignableTo(tupleTypeAtIdx, arrayTarget->ElementType());
187 });
188
189 bool spreadAssignable = true;
190 if (HasSpreadType()) {
191 spreadAssignable = relation->IsAssignableTo(GetSpreadType(), arrayTarget->ElementType());
192 }
193
194 relation->Result(elementsAssignable && spreadAssignable);
195 return;
196 }
197
198 const auto *const tupleTarget = target->AsETSTupleType();
199
200 if (tupleTarget->GetTupleSize() != GetTupleSize()) {
201 return;
202 }
203
204 for (int32_t idx = 0; idx < GetTupleSize(); ++idx) {
205 const SavedTypeRelationFlagsContext savedFlagsCtx(
206 relation, TypeRelationFlag::NO_BOXING | TypeRelationFlag::NO_UNBOXING | TypeRelationFlag::NO_WIDENING);
207
208 if (!relation->IsAssignableTo(tupleTarget->GetTypeAtIndex(idx), GetTypeAtIndex(idx))) {
209 return;
210 }
211 }
212
213 relation->Result(true);
214 }
215
Instantiate(ArenaAllocator * allocator,TypeRelation * relation,GlobalTypesHolder * globalTypes)216 Type *ETSTupleType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation,
217 [[maybe_unused]] GlobalTypesHolder *globalTypes)
218 {
219 auto *tupleType = allocator->New<ETSTupleType>(
220 GetTupleTypesList(), ElementType()->Instantiate(allocator, relation, globalTypes), GetSpreadType());
221 tupleType->typeFlags_ = typeFlags_;
222 return tupleType;
223 }
224
225 } // namespace ark::es2panda::checker
226