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 30namespace ark::es2panda::compiler { 31class TupleUpdateConverter { 32public: 33 TupleUpdateConverter(checker::ETSChecker *const checker, ir::UpdateExpression *const update) 34 : checker_(checker), update_(update) 35 { 36 } 37 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 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 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 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 125private: 126 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 137 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 162static 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 215static 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 263bool 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 293bool 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