From c2703188cd50149d4b0165a389662c0151c895f2 Mon Sep 17 00:00:00 2001 From: irfan-karatekin Date: Thu, 26 Jun 2025 04:44:36 +0300 Subject: [PATCH] Fail when array type cannot determined Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICH4IZ Signed-off-by: irfan-karatekin --- ets2panda/checker/ETSchecker.h | 3 + ets2panda/checker/ets/function.cpp | 139 ++++++++++++++---- ets2panda/checker/ets/helpers.cpp | 3 + .../ets/inferTypeOfArrayNegative2.ets | 1 + .../ets/inferTypeOfArray-expected.txt | 4 +- .../test/compiler/ets/inferTypeOfArray.ets | 3 + .../test/parser/ets/literals-expected.txt | 3 +- ets2panda/test/parser/ets/literals.ets | 1 + ets2panda/util/diagnostic/semantic.yaml | 4 + 9 files changed, 127 insertions(+), 34 deletions(-) diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index ec155ee9ae..ea77461f95 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -468,6 +468,9 @@ public: bool ValidateArgumentAsIdentifier(const ir::Identifier *identifier); bool IsValidRestArgument(ir::Expression *argument, Signature *substitutedSig, TypeRelationFlag flags, std::size_t index); + bool ValidateSpreadArgument(ir::SpreadElement *argument, Signature *substitutedSig, + const std::tuple &info); + bool HandleLambdaInference(ir::Expression *argument, Signature *substitutedSig, size_t index); bool SetPreferredTypeForArrayArgument(ir::ArrayExpression *arrayExpr, Signature *substitutedSig); bool ValidateSignatureRestParams(Signature *substitutedSig, const ArenaVector &arguments, TypeRelationFlag flags, bool reportError, bool unique); diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index c2c1ed6974..0e9caf4da8 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -426,6 +426,49 @@ static bool CheckArrowFunctionParamIfNeeded(ETSChecker *checker, Signature *subs return true; } +static bool ContainsEmptyArrayLiteral(ir::Expression *expr) +{ + if (!expr->IsArrayExpression()) { + return false; + } + auto *arrayExpr = expr->AsArrayExpression(); + if (arrayExpr->Elements().empty()) { + return true; // Found an empty array literal + } + // Recursively check each element + for (auto *element : arrayExpr->Elements()) { + if (ContainsEmptyArrayLiteral(element)) { + return true; + } + } + return false; +} + +static bool IsUnionOfMultipleArrayTypes(Type *type) +{ + if (!type->IsETSUnionType()) { + return false; + } + + int arrayTypeCount = 0; + for (auto *constituent : type->AsETSUnionType()->ConstituentTypes()) { + if (constituent->IsETSArrayType() || constituent->IsETSResizableArrayType()) { + arrayTypeCount++; + } + } + + return arrayTypeCount > 1; +} + +bool ETSChecker::HandleLambdaInference(ir::Expression *argument, Signature *substitutedSig, size_t index) +{ + // Note: If the signatures are from lambdas, then they have no `Function`. + ES2PANDA_ASSERT(argument->IsArrowFunctionExpression()); + ir::ScriptFunction *const lambda = argument->AsArrowFunctionExpression()->Function(); + auto targetParm = substitutedSig->GetSignatureInfo()->params[index]->Declaration()->Node(); + return CheckLambdaAssignable(targetParm->AsETSParameterExpression(), lambda); +} + // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP, G.FUD.05) solid logic bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, const ArenaVector &arguments, TypeRelationFlag flags, @@ -441,11 +484,15 @@ bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, // #22952: infer optional parameter heuristics auto const paramType = GetNonNullishType(substitutedSig->Params()[index]->TsType())->MaybeBaseTypeOfGradualType(); - if (argument->IsObjectExpression()) { - if (!paramType->IsETSObjectType()) { - return false; + if (argument->IsArrayExpression() && IsUnionOfMultipleArrayTypes(paramType) && + ContainsEmptyArrayLiteral(argument)) { + if (reportError) { + LogError(diagnostic::CANNOT_INFER_ARRAY, {}, argument->Start()); } - if (paramType->AsETSObjectType()->IsBoxedPrimitive()) { + return false; + } + if (argument->IsObjectExpression()) { + if ((!paramType->IsETSObjectType()) || paramType->AsETSObjectType()->IsBoxedPrimitive()) { return false; } argument->SetPreferredType(paramType); @@ -464,11 +511,7 @@ bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, } if (argTypeInferenceRequired[index]) { - ES2PANDA_ASSERT(argument->IsArrowFunctionExpression()); - // Note: If the signatures are from lambdas, then they have no `Function`. - ir::ScriptFunction *const lambda = argument->AsArrowFunctionExpression()->Function(); - auto targetParm = substitutedSig->GetSignatureInfo()->params[index]->Declaration()->Node(); - if (CheckLambdaAssignable(targetParm->AsETSParameterExpression(), lambda)) { + if (HandleLambdaInference(argument, substitutedSig, index)) { continue; } return false; @@ -511,13 +554,22 @@ bool ETSChecker::IsValidRestArgument(ir::Expression *const argument, Signature * { auto *restParamType = substitutedSig->RestVar()->TsType(); if (argument->IsObjectExpression()) { - argument->SetPreferredType(GetElementTypeOfArray(restParamType)); + if (restParamType->IsETSArrayType() || restParamType->IsETSResizableArrayType()) { + argument->SetPreferredType(GetElementTypeOfArray(restParamType)); + } // Object literals should be checked separately afterwards after call resolution return true; } // Set preferred type for array expressions before checking, similar to spread elements if (argument->IsArrayExpression()) { + if (restParamType->IsETSArrayType() || restParamType->IsETSResizableArrayType()) { + auto targetType = GetElementTypeOfArray(restParamType); + if (IsUnionOfMultipleArrayTypes(targetType) && ContainsEmptyArrayLiteral(argument)) { + LogError(diagnostic::CANNOT_INFER_ARRAY, {}, argument->Start()); + return false; + } + } if (!SetPreferredTypeForArrayArgument(argument->AsArrayExpression(), substitutedSig)) { return false; } @@ -570,6 +622,44 @@ bool ETSChecker::SetPreferredTypeForArrayArgument(ir::ArrayExpression *arrayExpr return true; } +bool ETSChecker::ValidateSpreadArgument(ir::SpreadElement *argument, Signature *substitutedSig, + const std::tuple &info) +{ + auto [flags, reportError, index, restCount] = info; + if (restCount > 1U) { + if (reportError) { + LogError(diagnostic::MULTIPLE_SPREADS, {}, argument->Start()); + } + return false; + } + + auto *const restArgument = argument->Argument(); + Type *targetType = substitutedSig->RestVar()->TsType(); + // backing out of check that results in a signature mismatch would be difficult + // so only attempt it if there is only one candidate signature + if (restArgument->IsArrayExpression()) { + if (IsUnionOfMultipleArrayTypes(targetType) && ContainsEmptyArrayLiteral(restArgument)) { + if (reportError) { + LogError(diagnostic::CANNOT_INFER_ARRAY, {}, restArgument->Start()); + } + return false; + } + restArgument->AsArrayExpression()->SetPreferredType(targetType); + } + auto const argumentType = restArgument->Check(this); + + auto const invocationCtx = checker::InvocationContext( + Relation(), restArgument, argumentType, substitutedSig->RestVar()->TsType(), argument->Start(), + {{diagnostic::REST_PARAM_INCOMPAT_AT, {argumentType, substitutedSig->RestVar()->TsType(), index + 1}}}, flags); + if (!invocationCtx.IsInvocable()) { + if (restArgument->IsArrayExpression()) { + ModifyPreferredType(restArgument->AsArrayExpression(), nullptr); + } + return false; + } + return true; +} + bool ETSChecker::ValidateSignatureRestParams(Signature *substitutedSig, const ArenaVector &arguments, TypeRelationFlag flags, bool reportError, [[maybe_unused]] const bool unique) @@ -581,6 +671,9 @@ bool ETSChecker::ValidateSignatureRestParams(Signature *substitutedSig, const Ar if (argumentCount == commonArity && substitutedSig->RestVar()->TsType()->IsETSTupleType()) { return false; } + + constexpr size_t INDEX_TUPLE_INDEX = 2; + auto spreadInfo = std::make_tuple(flags, reportError, 0, restCount); for (size_t index = commonArity; index < argumentCount; ++index) { auto &argument = arguments[index]; @@ -591,28 +684,8 @@ bool ETSChecker::ValidateSignatureRestParams(Signature *substitutedSig, const Ar continue; } - if (restCount > 1U) { - if (reportError) { - LogError(diagnostic::MULTIPLE_SPREADS, {}, argument->Start()); - } - return false; - } - - auto *const restArgument = argument->AsSpreadElement()->Argument(); - Type *targetType = substitutedSig->RestVar()->TsType(); - // backing out of check that results in a signature mismatch would be difficult - // so only attempt it if there is only one candidate signature - restArgument->SetPreferredType(targetType); - auto const argumentType = restArgument->Check(this); - - auto const invocationCtx = checker::InvocationContext( - Relation(), restArgument, argumentType, substitutedSig->RestVar()->TsType(), argument->Start(), - {{diagnostic::REST_PARAM_INCOMPAT_AT, {argumentType, substitutedSig->RestVar()->TsType(), index + 1}}}, - flags); - if (!invocationCtx.IsInvocable()) { - if (restArgument->IsArrayExpression()) { - ModifyPreferredType(restArgument->AsArrayExpression(), nullptr); - } + std::get(spreadInfo) = index; + if (!ValidateSpreadArgument(argument->AsSpreadElement(), substitutedSig, spreadInfo)) { return false; } } @@ -816,6 +889,8 @@ Signature *ETSChecker::GetMostSpecificSignature(ArenaVector &compat return nullptr; } + CheckObjectLiteralArguments(mostSpecificSignature, arguments); + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) if (!TypeInference(mostSpecificSignature, arguments, resolveFlags)) { return nullptr; diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 26ed9ad28c..7fcb3c321d 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -692,6 +692,9 @@ bool ETSChecker::CheckInit(ir::Identifier *ident, ir::TypeNode *typeAnnotation, if (typeAnnotation == nullptr) { if (init->IsArrayExpression()) { annotationType = CheckArrayElements(init->AsArrayExpression()); + if (init->AsArrayExpression()->Elements().empty()) { + LogError(diagnostic::CANNOT_INFER_ARRAY, {}, ident->Start()); + } } else if (init->IsETSNewArrayInstanceExpression()) { annotationType = init->AsETSNewArrayInstanceExpression()->TypeReference()->GetType(this); annotationType = CreateETSResizableArrayType(annotationType); diff --git a/ets2panda/test/ast/compiler/ets/inferTypeOfArrayNegative2.ets b/ets2panda/test/ast/compiler/ets/inferTypeOfArrayNegative2.ets index b0f2d51def..151309e0f8 100644 --- a/ets2panda/test/ast/compiler/ets/inferTypeOfArrayNegative2.ets +++ b/ets2panda/test/ast/compiler/ets/inferTypeOfArrayNegative2.ets @@ -18,4 +18,5 @@ a[0] = 1 a[1] = "1" let b = a[0] + a[1] +/* @@? 16:5 Error TypeError: Cannot infer type because array literal needs an explicit target typed */ /* @@? 19:9 Error TypeError: Bad operand type, the types of the operands must be numeric type, enum or String. */ diff --git a/ets2panda/test/compiler/ets/inferTypeOfArray-expected.txt b/ets2panda/test/compiler/ets/inferTypeOfArray-expected.txt index 760183394c..7dea8db1d3 100644 --- a/ets2panda/test/compiler/ets/inferTypeOfArray-expected.txt +++ b/ets2panda/test/compiler/ets/inferTypeOfArray-expected.txt @@ -1828,9 +1828,11 @@ "program": "inferTypeOfArray.ets" }, "end": { - "line": 25, + "line": 28, "column": 1, "program": "inferTypeOfArray.ets" } } } +TypeError: Cannot infer type because array literal needs an explicit target typed [inferTypeOfArray.ets:17:5] +TypeError: Cannot infer type because array literal needs an explicit target typed [inferTypeOfArray.ets:21:5] diff --git a/ets2panda/test/compiler/ets/inferTypeOfArray.ets b/ets2panda/test/compiler/ets/inferTypeOfArray.ets index ca4ed9c3b4..5578f487a5 100644 --- a/ets2panda/test/compiler/ets/inferTypeOfArray.ets +++ b/ets2panda/test/compiler/ets/inferTypeOfArray.ets @@ -22,3 +22,6 @@ let f = [] let g = d[2] + 1.2 f[0] = "1" f[1] = 1 + +/* @@? 17:5 Error TypeError: Cannot infer type because array literal needs an explicit target typed */ +/* @@? 21:5 Error TypeError: Cannot infer type because array literal needs an explicit target typed */ diff --git a/ets2panda/test/parser/ets/literals-expected.txt b/ets2panda/test/parser/ets/literals-expected.txt index 5dffbbbbf6..07338bd3e4 100644 --- a/ets2panda/test/parser/ets/literals-expected.txt +++ b/ets2panda/test/parser/ets/literals-expected.txt @@ -1594,9 +1594,10 @@ "program": "literals.ets" }, "end": { - "line": 40, + "line": 41, "column": 1, "program": "literals.ets" } } } +TypeError: Cannot infer type because array literal needs an explicit target typed [literals.ets:35:7] diff --git a/ets2panda/test/parser/ets/literals.ets b/ets2panda/test/parser/ets/literals.ets index bc1c123010..b53b16f63f 100644 --- a/ets2panda/test/parser/ets/literals.ets +++ b/ets2panda/test/parser/ets/literals.ets @@ -37,3 +37,4 @@ const lit19 = [1,2,3]; const lit20 = ["1","2","3"]; const lit21 = [1.0,2.0,3.0]; +/* @@? 35:7 Error TypeError: Cannot infer type because array literal needs an explicit target typed */ diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index 4c4ae2e28e..87ba0f4fa6 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -1514,3 +1514,7 @@ semantic: - name: DYMANIC_INIT_WITH_OBJEXPR id: 382 message: "Dymanic Type {} cannot be initialize with an object expression" + +- name: CANNOT_INFER_ARRAY + id: 383 + message: "Cannot infer type because array literal needs an explicit target typed" -- Gitee