From 30d21de5c58d2d765d068119b9982aa8c580540c Mon Sep 17 00:00:00 2001 From: zmw Date: Fri, 29 Aug 2025 11:32:14 +0800 Subject: [PATCH] Fix lambda local var type nullptr Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICVDRE Description: Fix lambda local var type nullptr Signed-off-by: zmw --- ets2panda/checker/ETSAnalyzer.cpp | 68 ++++++++++++------- ets2panda/checker/ets/arithmetic.cpp | 3 + ets2panda/checker/ets/function.cpp | 32 ++++++++- .../ets/lambda_type_infer_async_1.ets | 22 ++++++ .../ets/lambda_type_infer_async_2.ets | 25 +++++++ .../ets/lambda_type_infer_async_3.ets | 22 ++++++ .../ets/lambda_type_infer_async_4.ets | 22 ++++++ .../runtime/ets/catch_lambda_type_infer.ets | 27 ++++++++ .../runtime/ets/then_lambda_type_infer.ets | 20 ++++++ 9 files changed, 214 insertions(+), 27 deletions(-) create mode 100644 ets2panda/test/ast/compiler/ets/lambda_type_infer_async_1.ets create mode 100644 ets2panda/test/ast/compiler/ets/lambda_type_infer_async_2.ets create mode 100644 ets2panda/test/ast/compiler/ets/lambda_type_infer_async_3.ets create mode 100644 ets2panda/test/ast/compiler/ets/lambda_type_infer_async_4.ets create mode 100644 ets2panda/test/runtime/ets/catch_lambda_type_infer.ets create mode 100644 ets2panda/test/runtime/ets/then_lambda_type_infer.ets diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 339fa4192a..74057fe15d 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -2850,9 +2850,12 @@ checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::TypeofExpression *expr) c return expr->TsType(); } - expr->Argument()->Check(checker); - expr->SetTsType(ComputeTypeOfType(checker, expr->Argument()->TsType())); - return expr->TsType(); + auto argType = expr->Argument()->Check(checker); + if (argType->IsTypeError()) { + return expr->SetTsType(checker->GlobalTypeError()); + } + + return expr->SetTsType(ComputeTypeOfType(checker, argType)); } checker::Type *ETSAnalyzer::Check(ir::UnaryExpression *expr) const @@ -3867,6 +3870,38 @@ checker::Type *ETSAnalyzer::Check(ir::TSArrayType *node) const return node->TsType(); } +static bool CheckTSAsExpressionInvalidCast(ir::TSAsExpression *expr, checker::Type *sourceType, + checker::Type *targetType, ETSChecker *checker) +{ + if (sourceType->DefinitelyETSNullish() && !targetType->PossiblyETSNullish()) { + expr->SetTsType(checker->TypeError(expr, diagnostic::NULLISH_CAST_TO_NONNULLISH, expr->Start())); + return false; + } + + if (expr->Expr()->IsLiteral() && sourceType->IsBuiltinNumeric() && targetType->IsETSTypeParameter()) { + expr->SetTsType(checker->TypeError(expr, diagnostic::INVALID_CAST, + {sourceType->ToString(), targetType->ToString()}, expr->Expr()->Start())); + return false; + } + + if (expr->Expr()->IsLiteral() && sourceType->IsBuiltinNumeric() && targetType->IsETSUnionType()) { + bool allAreTypeParams = true; + for (auto *sub : targetType->AsETSUnionType()->ConstituentTypes()) { + if (!sub->IsETSTypeParameter()) { + allAreTypeParams = false; + } + } + if (allAreTypeParams) { + expr->SetTsType(checker->TypeError(expr, diagnostic::INVALID_CAST, + {sourceType->ToString(), targetType->ToString()}, + expr->Expr()->Start())); + return false; + } + } + + return true; +} + checker::Type *ETSAnalyzer::Check(ir::TSAsExpression *expr) const { ETSChecker *checker = GetETSChecker(); @@ -3882,30 +3917,13 @@ checker::Type *ETSAnalyzer::Check(ir::TSAsExpression *expr) const expr->Expr()->SetPreferredType(targetType); auto const sourceType = expr->Expr()->Check(checker); - FORWARD_TYPE_ERROR(checker, sourceType, expr); - - if (sourceType->DefinitelyETSNullish() && !targetType->PossiblyETSNullish()) { - return expr->SetTsType(checker->TypeError(expr, diagnostic::NULLISH_CAST_TO_NONNULLISH, expr->Start())); - } - - if (expr->Expr()->IsLiteral() && sourceType->IsBuiltinNumeric() && targetType->IsETSTypeParameter()) { - checker->LogError(diagnostic::INVALID_CAST, {sourceType->ToString(), targetType->ToString()}, - expr->Expr()->Start()); - return checker->InvalidateType(expr); + if (sourceType->IsTypeError() && checker->HasStatus(checker::CheckerStatus::IN_TYPE_INFER)) { + return expr->SetTsType(checker->GlobalTypeError()); } + FORWARD_TYPE_ERROR(checker, sourceType, expr); - if (expr->Expr()->IsLiteral() && sourceType->IsBuiltinNumeric() && targetType->IsETSUnionType()) { - bool allAreTypeParams = true; - for (auto *sub : targetType->AsETSUnionType()->ConstituentTypes()) { - if (!sub->IsETSTypeParameter()) { - allAreTypeParams = false; - } - } - if (allAreTypeParams) { - checker->LogError(diagnostic::INVALID_CAST, {sourceType->ToString(), targetType->ToString()}, - expr->Expr()->Start()); - return checker->InvalidateType(expr); - } + if (!CheckTSAsExpressionInvalidCast(expr, sourceType, targetType, checker)) { + return expr->TsType(); } const checker::CastingContext ctx( diff --git a/ets2panda/checker/ets/arithmetic.cpp b/ets2panda/checker/ets/arithmetic.cpp index 0415b8a2ed..0c158fc3cb 100644 --- a/ets2panda/checker/ets/arithmetic.cpp +++ b/ets2panda/checker/ets/arithmetic.cpp @@ -900,6 +900,9 @@ Type *ETSChecker::CheckBinaryOperatorNullishCoalescing(ir::Expression *left, ir: { auto *leftType = left->TsType(); leftType = GetNonNullishType(leftType); + if (leftType->IsTypeError() && HasStatus(checker::CheckerStatus::IN_TYPE_INFER)) { + return GlobalTypeError(); + } ERROR_TYPE_CHECK(this, leftType, return GlobalTypeError()); diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index 6f65e600f6..23ea6d54f4 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -210,6 +210,7 @@ bool ETSChecker::ProcessUntypedParameter(ir::AstNode *declNode, size_t paramInde varbinder::Variable *argParam = argSig->Params()[paramIndex]; argParam->SetTsType(inferredType); paramExpr->Ident()->SetTsType(inferredType); + paramExpr->Ident()->Variable()->SetTsType(inferredType); return true; } @@ -233,7 +234,31 @@ static void RemoveInvalidTypeMarkers(ir::AstNode *node) noexcept doNode(node); } -static void ResetInferredNode(ETSChecker *checker) +static void ResetInferredTypeInArrowBody(ir::AstNode *body, ETSChecker *checker, + std::unordered_set &inferredVarSet) +{ + std::function doNode = [&](ir::AstNode *node) { + if (node->IsIdentifier()) { + auto *id = node->AsIdentifier(); + if (inferredVarSet.count(id->Variable()) == 0U) { + return; + } + + id->Check(checker); + if (auto *parent = id->Parent(); parent->IsMemberExpression()) { + parent->AsMemberExpression()->Check(checker); + } + } + if (node->IsVariableDeclarator()) { + auto *id = node->AsVariableDeclarator()->Id(); + inferredVarSet.emplace(id->Variable()); + node->Check(checker); + } + }; + body->IterateRecursively(doNode); +} + +static void ResetInferredNode(ETSChecker *checker, std::unordered_set &inferredVarSet) { auto relation = checker->Relation(); auto resetFuncState = [](ir::ArrowFunctionExpression *expr) { @@ -252,6 +277,7 @@ static void ResetInferredNode(ETSChecker *checker) relation->SetNode(nullptr); RemoveInvalidTypeMarkers(arrowFunc); + ResetInferredTypeInArrowBody(arrowFunc->Function()->Body(), checker, inferredVarSet); resetFuncState(arrowFunc); arrowFunc->Check(checker); } @@ -277,15 +303,17 @@ bool ETSChecker::EnhanceSubstitutionForFunction(const ArenaVector &typeP bool res = true; const size_t commonArity = std::min(argSig->ArgCount(), paramSig->ArgCount()); + std::unordered_set inferredVarSet; for (size_t idx = 0; idx < commonArity; idx++) { auto *declNode = argSig->Params()[idx]->Declaration()->Node(); if (ProcessUntypedParameter(declNode, idx, paramSig, argSig, substitution)) { + inferredVarSet.emplace(argSig->Params()[idx]); continue; } res &= enhance(paramSig->Params()[idx]->TsType(), argSig->Params()[idx]->TsType()); } - ResetInferredNode(this); + ResetInferredNode(this, inferredVarSet); if (argSig->HasRestParameter() && paramSig->HasRestParameter()) { res &= enhance(paramSig->RestVar()->TsType(), argSig->RestVar()->TsType()); diff --git a/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_1.ets b/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_1.ets new file mode 100644 index 0000000000..ee105ffb91 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_1.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +const p = Promise.resolve(undefined); + +p.then(obj => { + const result = typeof obj?.prop; +}); + +/* @@? 19:27 Error TypeError: Value is possibly nullish. */ diff --git a/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_2.ets b/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_2.ets new file mode 100644 index 0000000000..0ee1d1d128 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_2.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. + */ + +function main() { + const p = Promise.resolve(null); + + const result = await p.then(obj => { + const value = obj?.prop ?? "default"; + return typeof value; + }); +} + +/* @@? 20:23 Error TypeError: Value is possibly nullish. */ diff --git a/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_3.ets b/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_3.ets new file mode 100644 index 0000000000..5283c804c9 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_3.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +const p = Promise.resolve(undefined); + +p.then(obj => { + const result = typeof obj.prop; +}); + +/* @@? 19:27 Error TypeError: Value is possibly nullish. */ diff --git a/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_4.ets b/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_4.ets new file mode 100644 index 0000000000..c7cadbc577 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/lambda_type_infer_async_4.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +const p = Promise.resolve('hello'); + +p.then(obj => { + const result = typeof obj.prop; +}); + +/* @@? 19:31 Error TypeError: Property 'prop' does not exist on type 'String' */ diff --git a/ets2panda/test/runtime/ets/catch_lambda_type_infer.ets b/ets2panda/test/runtime/ets/catch_lambda_type_infer.ets new file mode 100644 index 0000000000..17f2f757d8 --- /dev/null +++ b/ets2panda/test/runtime/ets/catch_lambda_type_infer.ets @@ -0,0 +1,27 @@ +/* + * 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. + */ + +async function startTest(): Promise { + throw new Error("test error"); +} + +function main() { + startTest().catch((input) => { + const err = input as Error; + const msg = err.message || 'Unknown error'; + arktest.assertEQ(msg, 'test error'); + return msg; + }); +} diff --git a/ets2panda/test/runtime/ets/then_lambda_type_infer.ets b/ets2panda/test/runtime/ets/then_lambda_type_infer.ets new file mode 100644 index 0000000000..d2054ef008 --- /dev/null +++ b/ets2panda/test/runtime/ets/then_lambda_type_infer.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +const p = Promise.resolve('hello'); + +p.then(obj => { + arktest.assertEQ(obj, 'hello'); +}); -- Gitee