From 0503b9d80dd3a85527b6d47a3007c519aca746d1 Mon Sep 17 00:00:00 2001 From: Istvan Romai Date: Fri, 12 Jul 2024 13:29:57 +0200 Subject: [PATCH] [ArkTS frontend] Implement union array types Changed array type handling to create union typed array if initializer contains different types of elements Added tests Issue: #IACNQH Internal issue: #17961 Signed-off-by: Istvan Romai Change-Id: I68827dab2cdfcb062be4084cd758886dfb6f778e --- ets2panda/checker/ETSchecker.h | 2 +- ets2panda/checker/ets/helpers.cpp | 45 +- ...wrong-union-array-assignment-expected.txt} | 475 ++++++++++++------ .../ets/wrong-union-array-assignment.sts | 25 + ets2panda/test/runtime/ets/UnionArray.ets | 41 ++ 5 files changed, 392 insertions(+), 196 deletions(-) rename ets2panda/test/{compiler/ets/inferTypeOfArrayNegative1-expected.txt => parser/ets/wrong-union-array-assignment-expected.txt} (52%) create mode 100644 ets2panda/test/parser/ets/wrong-union-array-assignment.sts create mode 100644 ets2panda/test/runtime/ets/UnionArray.ets diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 51bd4b1f22..019def3912 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -667,7 +667,7 @@ public: // Static invoke void CheckInvokeMethodsLegitimacy(ETSObjectType *classType); - checker::Type *CheckArrayElements(ir::Identifier *ident, ir::ArrayExpression *init); + checker::Type *CheckArrayElements(ir::ArrayExpression *init); void ResolveReturnStatement(checker::Type *funcReturnType, checker::Type *argumentType, ir::ScriptFunction *containingFunc, ir::ReturnStatement *st); diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 6fde4aa632..5d1603ae2d 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -537,37 +537,24 @@ void ETSChecker::ResolveReturnStatement(checker::Type *funcReturnType, checker:: } } -checker::Type *ETSChecker::CheckArrayElements(ir::Identifier *ident, ir::ArrayExpression *init) +checker::Type *ETSChecker::CheckArrayElements(ir::ArrayExpression *init) { - ArenaVector elements = init->AsArrayExpression()->Elements(); - checker::Type *annotationType = nullptr; - if (elements.empty()) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - annotationType = Allocator()->New(GlobalETSObjectType()); - } else { - auto type = elements[0]->Check(this); - auto const primType = ETSBuiltinTypeAsPrimitiveType(type); - for (auto element : elements) { - auto const eType = element->Check(this); - auto const primEType = ETSBuiltinTypeAsPrimitiveType(eType); - if (primEType != nullptr && primType != nullptr && - primEType->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && - primType->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { - type = GlobalDoubleType(); - } else if (IsTypeIdenticalTo(type, eType)) { - continue; - } else if (type->IsETSEnumType() && eType->IsETSEnumType() && - type->AsETSEnumType()->IsSameEnumType(eType->AsETSEnumType())) { - continue; - } else { - // NOTE: Create union type when implemented here - ThrowTypeError({"Union type is not implemented yet!"}, ident->Start()); - } - } + ArenaVector elementTypes(Allocator()->Adapter()); + for (auto e : init->AsArrayExpression()->Elements()) { + elementTypes.push_back(e->Check(this)); + } + + if (elementTypes.empty()) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - annotationType = Allocator()->New(type); + return Allocator()->New(GlobalETSObjectType()); } - return annotationType; + auto const isNumeric = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); }; + auto const elementType = std::all_of(elementTypes.begin(), elementTypes.end(), isNumeric) + ? GlobalDoubleType() + : CreateETSUnionType(std::move(elementTypes)); + + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return Allocator()->New(elementType); } void ETSChecker::InferAliasLambdaType(ir::TypeNode *localTypeAnnotation, ir::ArrowFunctionExpression *init) @@ -641,7 +628,7 @@ void ETSChecker::CheckInit(ir::Identifier *ident, ir::TypeNode *typeAnnotation, { if (typeAnnotation == nullptr) { if (init->IsArrayExpression()) { - annotationType = CheckArrayElements(ident, init->AsArrayExpression()); + annotationType = CheckArrayElements(init->AsArrayExpression()); bindingVar->SetTsType(annotationType); } diff --git a/ets2panda/test/compiler/ets/inferTypeOfArrayNegative1-expected.txt b/ets2panda/test/parser/ets/wrong-union-array-assignment-expected.txt similarity index 52% rename from ets2panda/test/compiler/ets/inferTypeOfArrayNegative1-expected.txt rename to ets2panda/test/parser/ets/wrong-union-array-assignment-expected.txt index 6417db5f5d..e65c5ae097 100644 --- a/ets2panda/test/compiler/ets/inferTypeOfArrayNegative1-expected.txt +++ b/ets2panda/test/parser/ets/wrong-union-array-assignment-expected.txt @@ -6,16 +6,16 @@ "definition": { "id": { "type": "Identifier", - "name": "ETSGLOBAL", + "name": "Bad", "decorators": [], "loc": { "start": { - "line": 1, - "column": 1 + "line": 16, + "column": 7 }, "end": { - "line": 1, - "column": 1 + "line": 16, + "column": 10 } } }, @@ -26,7 +26,7 @@ "type": "MethodDefinition", "key": { "type": "Identifier", - "name": "main", + "name": "constructor", "decorators": [], "loc": { "start": { @@ -39,9 +39,8 @@ } } }, - "kind": "method", - "accessibility": "public", - "static": true, + "kind": "constructor", + "static": false, "optional": false, "computed": false, "value": { @@ -50,7 +49,7 @@ "type": "ScriptFunction", "id": { "type": "Identifier", - "name": "main", + "name": "constructor", "decorators": [], "loc": { "start": { @@ -107,15 +106,59 @@ "decorators": [], "loc": { "start": { - "line": 1, - "column": 1 + "line": 19, + "column": 2 }, "end": { - "line": 1, - "column": 1 + "line": 19, + "column": 2 } } + } + ], + "loc": { + "start": { + "line": 17, + "column": 1 }, + "end": { + "line": 19, + "column": 2 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 1 + }, + "end": { + "line": 19, + "column": 2 + } + } + }, + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "ETSGLOBAL", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "superClass": null, + "implements": [], + "body": [ { "type": "MethodDefinition", "key": { @@ -163,107 +206,7 @@ "params": [], "body": { "type": "BlockStatement", - "statements": [ - { - "type": "ExpressionStatement", - "expression": { - "type": "AssignmentExpression", - "operator": "=", - "left": { - "type": "Identifier", - "name": "a", - "decorators": [], - "loc": { - "start": { - "line": 16, - "column": 5 - }, - "end": { - "line": 16, - "column": 6 - } - } - }, - "right": { - "type": "ArrayExpression", - "elements": [ - { - "type": "StringLiteral", - "value": "a", - "loc": { - "start": { - "line": 16, - "column": 10 - }, - "end": { - "line": 16, - "column": 13 - } - } - }, - { - "type": "NumberLiteral", - "value": 1, - "loc": { - "start": { - "line": 16, - "column": 15 - }, - "end": { - "line": 16, - "column": 16 - } - } - }, - { - "type": "NumberLiteral", - "value": 3.14, - "loc": { - "start": { - "line": 16, - "column": 18 - }, - "end": { - "line": 16, - "column": 22 - } - } - } - ], - "loc": { - "start": { - "line": 16, - "column": 9 - }, - "end": { - "line": 16, - "column": 23 - } - } - }, - "loc": { - "start": { - "line": 16, - "column": 5 - }, - "end": { - "line": 16, - "column": 23 - } - } - }, - "loc": { - "start": { - "line": 16, - "column": 5 - }, - "end": { - "line": 16, - "column": 23 - } - } - } - ], + "statements": [], "loc": { "start": { "line": 1, @@ -311,95 +254,295 @@ } }, { - "type": "ClassProperty", + "type": "MethodDefinition", "key": { "type": "Identifier", - "name": "a", + "name": "main", "decorators": [], "loc": { "start": { - "line": 16, - "column": 5 + "line": 21, + "column": 10 }, "end": { - "line": 16, - "column": 6 + "line": 21, + "column": 14 } } }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, "value": { - "type": "ArrayExpression", - "elements": [ - { - "type": "StringLiteral", - "value": "a", + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "main", + "decorators": [], "loc": { "start": { - "line": 16, + "line": 21, "column": 10 }, "end": { - "line": 16, - "column": 13 + "line": 21, + "column": 14 } } }, - { - "type": "NumberLiteral", - "value": 1, - "loc": { - "start": { - "line": 16, - "column": 15 + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "array", + "decorators": [], + "loc": { + "start": { + "line": 23, + "column": 9 + }, + "end": { + "line": 23, + "column": 14 + } + } + }, + "init": { + "type": "ArrayExpression", + "elements": [ + { + "type": "NumberLiteral", + "value": 1, + "loc": { + "start": { + "line": 23, + "column": 18 + }, + "end": { + "line": 23, + "column": 19 + } + } + }, + { + "type": "NumberLiteral", + "value": 2.3, + "loc": { + "start": { + "line": 23, + "column": 21 + }, + "end": { + "line": 23, + "column": 24 + } + } + }, + { + "type": "StringLiteral", + "value": "string", + "loc": { + "start": { + "line": 23, + "column": 26 + }, + "end": { + "line": 23, + "column": 34 + } + } + } + ], + "loc": { + "start": { + "line": 23, + "column": 17 + }, + "end": { + "line": 23, + "column": 35 + } + } + }, + "loc": { + "start": { + "line": 23, + "column": 9 + }, + "end": { + "line": 23, + "column": 35 + } + } + } + ], + "kind": "let", + "loc": { + "start": { + "line": 23, + "column": 5 + }, + "end": { + "line": 23, + "column": 36 + } + } }, - "end": { - "line": 16, - "column": 16 + { + "type": "ExpressionStatement", + "expression": { + "type": "AssignmentExpression", + "operator": "=", + "left": { + "type": "Identifier", + "name": "array", + "decorators": [], + "loc": { + "start": { + "line": 24, + "column": 5 + }, + "end": { + "line": 24, + "column": 10 + } + } + }, + "right": { + "type": "ETSNewClassInstanceExpression", + "typeReference": { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "Bad", + "decorators": [], + "loc": { + "start": { + "line": 24, + "column": 17 + }, + "end": { + "line": 24, + "column": 20 + } + } + }, + "loc": { + "start": { + "line": 24, + "column": 17 + }, + "end": { + "line": 24, + "column": 21 + } + } + }, + "loc": { + "start": { + "line": 24, + "column": 17 + }, + "end": { + "line": 24, + "column": 21 + } + } + }, + "arguments": [], + "loc": { + "start": { + "line": 24, + "column": 13 + }, + "end": { + "line": 24, + "column": 23 + } + } + }, + "loc": { + "start": { + "line": 24, + "column": 5 + }, + "end": { + "line": 24, + "column": 23 + } + } + }, + "loc": { + "start": { + "line": 24, + "column": 5 + }, + "end": { + "line": 24, + "column": 23 + } + } } - } - }, - { - "type": "NumberLiteral", - "value": 3.14, + ], "loc": { "start": { - "line": 16, - "column": 18 + "line": 22, + "column": 1 }, "end": { - "line": 16, - "column": 22 + "line": 25, + "column": 2 } } + }, + "loc": { + "start": { + "line": 21, + "column": 14 + }, + "end": { + "line": 25, + "column": 2 + } } - ], + }, "loc": { "start": { - "line": 16, - "column": 9 + "line": 21, + "column": 14 }, "end": { - "line": 16, - "column": 23 + "line": 25, + "column": 2 } } }, - "accessibility": "public", - "static": true, - "readonly": false, - "declare": false, - "optional": false, - "computed": false, - "definite": false, + "overloads": [], "decorators": [], "loc": { "start": { - "line": 16, - "column": 5 + "line": 21, + "column": 1 }, "end": { - "line": 16, - "column": 23 + "line": 25, + "column": 2 } } } @@ -433,9 +576,9 @@ "column": 1 }, "end": { - "line": 17, + "line": 26, "column": 1 } } } -TypeError: Union type is not implemented yet! [inferTypeOfArrayNegative1.sts:16:5] +TypeError: Type 'Bad' cannot be assigned to type '(Int|Double|String)[]' [wrong-union-array-assignment.ets:24:13] diff --git a/ets2panda/test/parser/ets/wrong-union-array-assignment.sts b/ets2panda/test/parser/ets/wrong-union-array-assignment.sts new file mode 100644 index 0000000000..b1f586f33a --- /dev/null +++ b/ets2panda/test/parser/ets/wrong-union-array-assignment.sts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Bad +{ + +} + +function main() +{ + let array = [1, 2.3, "string"]; + array = new Bad(); +} diff --git a/ets2panda/test/runtime/ets/UnionArray.ets b/ets2panda/test/runtime/ets/UnionArray.ets new file mode 100644 index 0000000000..66aaa42b54 --- /dev/null +++ b/ets2panda/test/runtime/ets/UnionArray.ets @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Item { + public value: int; + + constructor(v: int) + { + this.value = v; + } +} + +function main() { + let objectArray = []; + let boolArray = [true, false, true]; + let intArray = [1, 2, 99, 100]; + let doubleArray = [1, -2, 1.2, 9.9]; + let stringArray = ["one", "two", "three"]; + let classArray = [new Item(1), new Item(2), new Item(3)]; + let unionArray = [true, 9.99, new Item(9), "success"]; + + assert(objectArray instanceof Object[]); + assert(boolArray instanceof Boolean[]); + assert(intArray instanceof number[]); + assert(doubleArray instanceof number[]); + assert(stringArray instanceof string[]); + assert(classArray instanceof Item[]); + assert(unionArray instanceof (Boolean|Double|Item|String)[]); +} -- Gitee