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 "tupleLowering.h"
17
18 #include "checker/ETSchecker.h"
19 #include "checker/types/ets/etsTupleType.h"
20 #include "compiler/lowering/util.h"
21 #include "ir/expressions/assignmentExpression.h"
22 #include "ir/expressions/identifier.h"
23 #include "ir/expressions/memberExpression.h"
24 #include "ir/expressions/sequenceExpression.h"
25 #include "ir/expressions/updateExpression.h"
26 #include "ir/opaqueTypeNode.h"
27 #include "ir/statements/blockStatement.h"
28 #include "ir/ts/tsAsExpression.h"
29
30 namespace ark::es2panda::compiler {
31 class TupleUpdateConverter {
32 public:
TupleUpdateConverter(checker::ETSChecker *const checker, ir::UpdateExpression *const update)33 TupleUpdateConverter(checker::ETSChecker *const checker, ir::UpdateExpression *const update)
34 : checker_(checker), update_(update)
35 {
36 }
37
CheckUpdateArgument()38 std::optional<checker::Type *const> CheckUpdateArgument()
39 {
40 auto *const argument = update_->Argument();
41 const bool isArgumentMemberExpression = argument->IsMemberExpression();
42 auto *const argumentType =
43 isArgumentMemberExpression ? argument->AsMemberExpression()->Object()->TsType() : nullptr;
44
45 if ((argumentType == nullptr) || (!argumentType->IsETSTupleType())) {
46 return std::nullopt;
47 }
48 return {argumentType};
49 }
50
SetArgumentType(checker::Type *const argumentType)51 checker::Type *SetArgumentType(checker::Type *const argumentType)
52 {
53 auto *const savedType = argument_->TsType();
54 argument_->SetTsType(argumentType->AsETSTupleType()->ElementType());
55 return savedType;
56 }
57
ComputeTypes(checker::Type *const argumentType)58 void ComputeTypes(checker::Type *const argumentType)
59 {
60 tupleTypeAtIdx_ = argumentType->AsETSTupleType()->GetTypeAtIndex(
61
62 // After the checker, we are guranteed that the index is correct.
63 *checker_->GetTupleElementAccessValue(argument_->AsMemberExpression()->Property()->TsType(),
64 argument_->AsMemberExpression()->Property()->Start()));
65
66 tupleElementTypeNode_ = checker_->AllocNode<ir::OpaqueTypeNode>(argumentType->AsETSTupleType()->ElementType());
67 tupleTypeAtIdxNode_ = checker_->AllocNode<ir::OpaqueTypeNode>(tupleTypeAtIdx_);
68 }
69
GenerateExpressions()70 ArenaVector<ir::Expression *> GenerateExpressions()
71 {
72 // Clone argument of update expression (conversion flag might be added to it, so we need to duplicate it to not
73 // make
74 // conversions on 'line 3', that belongs to 'line 1' )
75 auto [memberExpr, argumentClone] = CloneArgument(argument_);
76 // --------------
77
78 // Generate temporary symbols
79 auto [gensym, tmpVar] = GenerateSymbol(tupleTypeAtIdx_);
80 auto [gensym2, tmpVar2] = GenerateSymbol(tupleTypeAtIdx_);
81 // --------------
82 // make node: let gensym = tuple[n] as <tuple type at index n>;
83 auto *const gensymTsAs = checker_->AllocNode<ir::TSAsExpression>(argumentClone, tupleTypeAtIdxNode_, false);
84 auto *const tupleAsType = checker_->AllocNode<ir::AssignmentExpression>(
85 gensym, gensymTsAs, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
86 // --------------
87
88 // make node: let gensym2 = (gensym)++;
89 auto *identClone = gensym->Clone(checker_->Allocator(), nullptr);
90 identClone->SetTsType(tmpVar->TsType());
91 auto *gensymUpdate =
92 checker_->AllocNode<ir::UpdateExpression>(identClone, update_->OperatorType(), update_->IsPrefix());
93 auto *const gensym2Assignment = checker_->AllocNode<ir::AssignmentExpression>(
94 gensym2, gensymUpdate, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
95 // --------------
96
97 // make node: tuple[n] = (gensym as <tuple type at index n>) as <tuple element_type>;
98 identClone = gensym->Clone(checker_->Allocator(), nullptr);
99 identClone->SetTsType(tmpVar->TsType());
100 auto *gensymAs = checker_->AllocNode<ir::TSAsExpression>(
101 identClone, tupleTypeAtIdxNode_->Clone(checker_->Allocator(), nullptr), false);
102 auto *gensymAsTupleTypeAtIdx = checker_->AllocNode<ir::TSAsExpression>(gensymAs, tupleElementTypeNode_, false);
103 auto *const tupleAssignment = checker_->AllocNode<ir::AssignmentExpression>(
104 argument_, gensymAsTupleTypeAtIdx, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
105 // --------------
106
107 // make node: gensym2 as <tuple type at index n>;
108 identClone = gensym2->Clone(checker_->Allocator(), nullptr);
109 identClone->SetTsType(tmpVar2->TsType());
110 auto *const finalTupleNode = checker_->AllocNode<ir::TSAsExpression>(
111 identClone, tupleTypeAtIdxNode_->Clone(checker_->Allocator(), nullptr), false);
112 // --------------
113
114 // Construct sequence expression order
115 ArenaVector<ir::Expression *> expressionList(checker_->Allocator()->Adapter());
116 expressionList.push_back(tupleAsType);
117 expressionList.push_back(gensym2Assignment);
118 expressionList.push_back(tupleAssignment);
119 expressionList.push_back(finalTupleNode);
120 // --------------
121
122 return expressionList;
123 }
124
125 private:
GenerateSymbol(checker::Type *const type) const126 std::tuple<ir::Identifier *, varbinder::LocalVariable *const> GenerateSymbol(checker::Type *const type) const
127 {
128 auto *gensym = Gensym(checker_->Allocator());
129 auto *const tmpVar = NearestScope(update_)->AddDecl<varbinder::LetDecl, varbinder::LocalVariable>(
130 checker_->Allocator(), gensym->Name(), varbinder::VariableFlags::LOCAL);
131 tmpVar->SetTsType(type);
132 gensym->SetVariable(tmpVar);
133 gensym->SetTsType(tmpVar->TsType());
134 return std::make_tuple(gensym, tmpVar);
135 }
136
CloneArgument( ir::Expression *const argument) const137 std::tuple<ir::MemberExpression *const, ir::MemberExpression *const> CloneArgument(
138 ir::Expression *const argument) const
139 {
140 auto *const memberExpr = argument->AsMemberExpression();
141 auto *const argumentClone = memberExpr->Clone(checker_->Allocator(), memberExpr->Parent());
142 argumentClone->Object()->SetTsType(memberExpr->Object()->TsType());
143 if (argumentClone->Object()->IsIdentifier()) {
144 argumentClone->Object()->AsIdentifier()->SetVariable(memberExpr->Object()->AsIdentifier()->Variable());
145 }
146 argumentClone->Property()->SetTsType(memberExpr->Property()->TsType());
147 if (argumentClone->Property()->IsIdentifier()) {
148 argumentClone->Property()->AsIdentifier()->SetVariable(memberExpr->Property()->AsIdentifier()->Variable());
149 }
150 argumentClone->SetTsType(memberExpr->TsType());
151 return std::make_tuple(memberExpr, argumentClone);
152 };
153
154 checker::ETSChecker *const checker_;
155 ir::UpdateExpression *const update_;
156 ir::Expression *const argument_ {nullptr};
157 checker::Type *tupleTypeAtIdx_ {nullptr};
158 ir::OpaqueTypeNode *tupleElementTypeNode_ {nullptr};
159 ir::OpaqueTypeNode *tupleTypeAtIdxNode_ {nullptr};
160 };
161
ConvertTupleUpdate(checker::ETSChecker *const checker, ir::UpdateExpression *const update)162 static ir::Expression *ConvertTupleUpdate(checker::ETSChecker *const checker, ir::UpdateExpression *const update)
163 {
164 // Converts `tuple[n]++` to
165 // ```
166 // let gensym = tuple[n] as <tuple type at index n>; // line 1
167 // let gensym2 = (gensym)++; // line 2
168 // tuple[n] = (gensym as <tuple type at index n>) as <tuple element_type>; // line 3
169 // gensym2 as <tuple type at index n>; // line 4
170 // ```
171 // Notes:
172 // ---
173 // Because we can modify only 1 expression in the lowering (we don't want to add statements to the enclosing block),
174 // the expressions will be in a wrapper SequenceExpression
175 // ---
176 // At line 3 the double as expression is needed. If we simply write `gensym as <tuple type at index n>`, then a
177 // boxing flag may be put on the `gensym` identifier node. It'll be boxed in 'line 2' instead of 'line 3', which
178 // cause error. If we put another as expression inside (which won't do any conversion, because the type of `gensym`
179 // is already <tuple type at index n>), the boxing flag will be on the as expression, instead of the identifier, so
180 // the identifier node won't be unboxed at 'line 2'.
181
182 auto converter = TupleUpdateConverter {checker, update};
183
184 // Check if argument of update expression is tuple
185 auto const argumentType = converter.CheckUpdateArgument();
186 if (!argumentType) {
187 return update;
188 }
189 // --------------
190
191 // Set tuple type to Object (because we'll need implicit boxing)
192 auto *const savedType = converter.SetArgumentType(*argumentType);
193 // --------------
194
195 // Compute necessary types and OpaqueTypeNodes
196 converter.ComputeTypes(*argumentType);
197 // --------------
198
199 auto expressions = converter.GenerateExpressions();
200
201 // Check the new sequence expression
202 auto *const sequenceExpr = checker->AllocNode<ir::SequenceExpression>(std::move(expressions));
203 sequenceExpr->SetParent(update->Parent());
204 sequenceExpr->Check(checker);
205 // --------------
206
207 // Set back TsType of argument (not necessarily needed now, but there can be a phase later, that need to get the
208 // right type of it)
209 [[maybe_unused]] auto _ = converter.SetArgumentType(savedType);
210 // --------------
211
212 return sequenceExpr;
213 }
214
ConvertTupleAssignment(checker::ETSChecker *const checker, ir::AssignmentExpression *const assignment)215 static ir::AssignmentExpression *ConvertTupleAssignment(checker::ETSChecker *const checker,
216 ir::AssignmentExpression *const assignment)
217 {
218 // Converts `tuple[n] = variable;` to
219 // `tuple[n] = ((variable as <tuple type at index n>) as <tuple element_type>)`
220 // This lowering is necessary to handle `an unboxing conversion followed by a widening primitive
221 // conversion`, eg. when `tuple[n]` has type of `int`, and assignment::right_ has type of `Short`. Because every
222 // type is stored as the LUB type in the tuple (which can be Object), then the following conversions need to be done
223 // for this case: Short->short->int->Int->Object which can't be made implicitly, hence lowering is needed
224
225 // Check if the left side of an assignment expression is a tuple element access
226 auto *const left = assignment->Left();
227 auto *const leftObjectType = left->AsMemberExpression()->Object()->TsType();
228
229 if ((leftObjectType == nullptr) || (!leftObjectType->IsETSTupleType())) {
230 return assignment;
231 }
232 // --------------
233
234 // Set tuple type to <tuple element_type> (because we may need implicit boxing)
235 auto *const savedLeftType = left->TsType();
236 left->SetTsType(leftObjectType->AsETSTupleType()->ElementType());
237 // --------------
238
239 // Compute necessary types and OpaqueTypeNodes
240 auto *const elementTypeTypeNode =
241 checker->AllocNode<ir::OpaqueTypeNode>(leftObjectType->AsETSTupleType()->ElementType());
242 auto *const tupleTypeAtIdxTypeNode = checker->AllocNode<ir::OpaqueTypeNode>(savedLeftType);
243 // --------------
244
245 // make node: tuple[n] = ((variable as <tuple type at index n>) as <tuple element_type>)
246 auto *const tsAsExpressionLeft =
247 checker->AllocNode<ir::TSAsExpression>(assignment->Right(), tupleTypeAtIdxTypeNode, false);
248
249 auto *const tsAsExpression = checker->AllocNode<ir::TSAsExpression>(tsAsExpressionLeft, elementTypeTypeNode, false);
250 auto *const newAssignment =
251 checker->AllocNode<ir::AssignmentExpression>(left, tsAsExpression, assignment->OperatorType());
252 // --------------
253
254 // Check the new assignment
255 newAssignment->SetParent(assignment->Parent());
256 newAssignment->Check(checker);
257 left->SetTsType(savedLeftType);
258 // --------------
259
260 return newAssignment;
261 }
262
Perform(public_lib::Context *const ctx, parser::Program *const program)263 bool TupleLowering::Perform(public_lib::Context *const ctx, parser::Program *const program)
264 {
265 for (const auto &[_, ext_programs] : program->ExternalSources()) {
266 (void)_;
267 for (auto *const extProg : ext_programs) {
268 Perform(ctx, extProg);
269 }
270 }
271
272 checker::ETSChecker *const checker = ctx->checker->AsETSChecker();
273
274 program->Ast()->TransformChildrenRecursively(
275 [checker](ir::AstNode *const ast) -> ir::AstNode * {
276 // Check if node is an 'assignment expression', with a member expression on the left (potentially tuple)
277 if (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->Left()->IsMemberExpression()) {
278 return ConvertTupleAssignment(checker, ast->AsAssignmentExpression());
279 }
280
281 // Check if node is an 'update expression', with a member expression as an argument (potentially tuple)
282 if (ast->IsUpdateExpression() && ast->AsUpdateExpression()->Argument()->IsMemberExpression()) {
283 return ConvertTupleUpdate(checker, ast->AsUpdateExpression());
284 }
285
286 return ast;
287 },
288 Name());
289
290 return true;
291 }
292
Postcondition(public_lib::Context *const ctx, const parser::Program *const program)293 bool TupleLowering::Postcondition(public_lib::Context *const ctx, const parser::Program *const program)
294 {
295 for (const auto &[_, ext_programs] : program->ExternalSources()) {
296 (void)_;
297 for (const auto *const extProg : ext_programs) {
298 if (!Postcondition(ctx, extProg)) {
299 return false;
300 }
301 }
302 }
303
304 return !program->Ast()->IsAnyChild([](const ir::AstNode *const ast) {
305 const bool isLeftMemberExpr =
306 ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->Left()->IsMemberExpression();
307 const bool isLeftTuple =
308 isLeftMemberExpr
309 ? (ast->AsAssignmentExpression()->Left()->AsMemberExpression()->TsType() != nullptr) &&
310 ast->AsAssignmentExpression()->Left()->AsMemberExpression()->TsType()->IsETSTupleType()
311 : false;
312 // Check if there is an 'assignment expression' with a 'member expression' on it's left, which is a tuple. If
313 // yes, then the right hand side must be a type of the element type.
314 return isLeftMemberExpr && isLeftTuple &&
315 (ast->AsAssignmentExpression()->Right()->TsType() ==
316 ast->AsAssignmentExpression()->Left()->AsMemberExpression()->TsType()->AsETSTupleType()->ElementType());
317 });
318 }
319
320 } // namespace ark::es2panda::compiler
321