diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index a35397d0b8c46ba7ecf41bf291460bc34c09848c..3ddb14664f61941fca09872e70ce710dddee9191 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -979,6 +979,75 @@ static Type *GetAppropriatePreferredType(Type *originalType, std::functionPreferredType(); + node->SetPreferredType(preferElem); + checker::Type *t = node->Check(checker); + node->SetPreferredType(oldPref); + return t; +} + +static Type *SelectArrayPreferredTypeForLiteral(ETSChecker *checker, ir::ArrayExpression *arrayLiteral, + Type *contextualType) +{ + ES2PANDA_ASSERT(checker != nullptr); + ES2PANDA_ASSERT(arrayLiteral != nullptr); + ES2PANDA_ASSERT(contextualType != nullptr && contextualType->IsETSUnionType()); + + auto &alts = contextualType->AsETSUnionType()->ConstituentTypes(); + + for (auto *el : arrayLiteral->Elements()) { + ES2PANDA_ASSERT(el != nullptr); + if (el->IsSpreadElement() || el->IsBrokenExpression()) { + return nullptr; + } + } + + Type *selected = nullptr; + Type *selectedElem = nullptr; + + for (Type *candidate : alts) { + if (!candidate->IsETSArrayType() && !candidate->IsETSResizableArrayType()) { + continue; + } + + Type *candElem = checker->GetElementTypeOfArray(candidate); + ES2PANDA_ASSERT(candElem != nullptr); + + bool ok = true; + for (auto *el : arrayLiteral->Elements()) { + Type *elTy = CheckElemUnder(checker, el, candElem); + if (elTy == nullptr || !checker->Relation()->IsAssignableTo(elTy, candElem)) { + ok = false; + break; + } + } + if (!ok) { + continue; + } + + if (selected == nullptr) { + selected = candidate; + selectedElem = candElem; + continue; + } + + auto *relation = checker->Relation(); + const bool candMoreSpecific = relation->IsSupertypeOf(selectedElem, candElem); + const bool selMoreSpecific = relation->IsSupertypeOf(candElem, selectedElem); + if (candMoreSpecific && !selMoreSpecific) { + selected = candidate; + selectedElem = candElem; + } else if (!candMoreSpecific && !selMoreSpecific && !relation->IsIdenticalTo(selected, candidate)) { + return nullptr; + } + } + + return selected; +} + checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const { ETSChecker *checker = GetETSChecker(); @@ -988,6 +1057,13 @@ checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const auto *preferredType = GetAppropriatePreferredType(expr->PreferredType(), &Type::IsAnyETSArrayOrTupleType); + if (expr->PreferredType() != nullptr && expr->PreferredType()->IsETSUnionType()) { + if (auto *picked = SelectArrayPreferredTypeForLiteral(checker, expr, expr->PreferredType())) { + preferredType = picked; + expr->SetPreferredType(preferredType); + } + } + if (preferredType != nullptr && preferredType->IsETSReadonlyArrayType()) { const auto elementType = preferredType->AsETSObjectType()->TypeArguments().front(); preferredType = checker->CreateETSResizableArrayType(elementType); diff --git a/ets2panda/test/ast/compiler/ets/FixedArray/union_subtyping_neg.ets b/ets2panda/test/ast/compiler/ets/FixedArray/union_subtyping_neg.ets new file mode 100644 index 0000000000000000000000000000000000000000..ee093b8b105916362453ed31d7f5934951e419bd --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/FixedArray/union_subtyping_neg.ets @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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 A {} +class B extends A {} +class C {} + +let ac: FixedArray | FixedArray = [new A(), new B()] +let b: FixedArray = [new B(), new B()] + +let cOnly: FixedArray = b + +/* @@? 23:28 Error TypeError: Type 'FixedArray' cannot be assigned to type 'FixedArray' */ diff --git a/ets2panda/test/ast/compiler/ets/FixedArray/union_subtyping_pos_1.ets b/ets2panda/test/ast/compiler/ets/FixedArray/union_subtyping_pos_1.ets new file mode 100644 index 0000000000000000000000000000000000000000..d754b99b1e4d57023add7b093906eb043b842256 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/FixedArray/union_subtyping_pos_1.ets @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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 A {} +class B extends A {} +class C {} + +let ac: FixedArray | FixedArray = [new A(), new B()] +let b: FixedArray = [new B(), new B()] + +function assertFixedArrayOfA(u: FixedArray | FixedArray) { + if (u instanceof FixedArray) { + arktest.assertTrue(u.length === 2) + for (let i = 0; i < u.length; i++) { + arktest.assertTrue(u[i] instanceof A) + } + return + } + arktest.assertTrue(false) +} + +function main() { + ac = b + assertFixedArrayOfA(ac) +} diff --git a/ets2panda/test/ast/compiler/ets/FixedArray/union_subtyping_pos_2.ets b/ets2panda/test/ast/compiler/ets/FixedArray/union_subtyping_pos_2.ets new file mode 100644 index 0000000000000000000000000000000000000000..9a6cc4f3afc8e88502ab2650a55ff36993999bba --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/FixedArray/union_subtyping_pos_2.ets @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 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. + */ + + +function f(arg: FixedArray | Array) { + if (arg instanceof FixedArray) { + arktest.assertTrue(arg.length === 2) + let sum = 0 + for (let i = 0; i < arg.length; i++) { + sum += arg[i] + } + arktest.assertTrue(sum === 3) + return + } + + arktest.assertTrue(arg.length === 2) + let cat = "" + for (let i = 0; i < arg.length; i++) { + cat = cat + arg[i] + } + arktest.assertTrue(cat === "3344") +} + +function main() { + f([1, 2]) + f(["33", "44"]) +}