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