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