From d82822fa0ed505e8b8a3d27ec03fc182032587ca Mon Sep 17 00:00:00 2001 From: Klimentieva Date: Fri, 18 Jul 2025 19:41:49 +0300 Subject: [PATCH] Add Awaited utility type Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICLJFU Change-Id: I2525655528401267441238a1ff54d4f5b5f848b7 Signed-off-by: Klimentieva --- ets2panda/BUILD.gn | 1 + ets2panda/CMakeLists.txt | 1 + .../invariants/identifierHasVariable.cpp | 2 +- ets2panda/checker/ETSAnalyzer.cpp | 58 +--------- ets2panda/checker/ETSchecker.h | 7 ++ ets2panda/checker/ets/function.cpp | 27 ++++- ets2panda/checker/ets/object.cpp | 36 ++++-- ets2panda/checker/ets/typeCheckingHelpers.cpp | 2 +- ets2panda/checker/ets/utilityTypeHandlers.cpp | 89 +++++++++++++++ .../checker/types/ets/etsAwaitedType.cpp | 108 ++++++++++++++++++ ets2panda/checker/types/ets/etsAwaitedType.h | 63 ++++++++++ ets2panda/checker/types/typeFlag.h | 2 +- ets2panda/checker/types/typeMapping.h | 1 + ets2panda/compiler/scripts/signatures.yaml | 2 + ets2panda/ir/ets/etsTypeReferencePart.cpp | 3 +- .../ets/PromiseAllSettledRejectIgnored.ets | 59 ++++++++++ ets2panda/test/ast/compiler/ets/awaited_1.ets | 35 ++++++ ets2panda/test/ast/compiler/ets/awaited_2.ets | 28 +++++ ets2panda/test/ast/compiler/ets/awaited_3.ets | 29 +++++ ets2panda/test/ast/compiler/ets/awaited_4.ets | 31 +++++ .../ets/awaited_assignability_2_neg.ets | 21 ++++ .../ets/awaited_assignability_neg.ets | 22 ++++ .../ast/compiler/ets/awaited_utility_type.ets | 28 +++++ .../ast/compiler/ets/awaited_with_array.ets | 21 ++++ .../test/ast/compiler/ets/catch_error.ets | 43 +++++++ .../test/ast/compiler/ets/double_then.ets | 28 +++++ .../ast/compiler/ets/null_pointer_error.ets | 1 + ets2panda/test/ast/compiler/ets/readonly.ets | 22 ++++ ets2panda/test/ast/compiler/ets/then_ret.ets | 23 ++++ .../ast/parser/ets/await_argument_null.ets | 5 +- .../test/ast/parser/ets/await_object_bad.ets | 4 +- .../test/ast/parser/ets/await_promise_bad.ets | 4 +- .../parser/ets/dynmicImportUnimplemented.ets | 2 +- .../PromiseAllRejectIgnoredDeferred.ets | 47 ++++++++ .../ets/awaited_tests/awaited_parameter_1.ets | 26 +++++ .../ets/awaited_tests/awaited_return_1.ets | 27 +++++ .../ets/awaited_tests/awaited_type.ets | 28 +++++ ets2panda/util/diagnostic/semantic.yaml | 4 +- ets2panda/varbinder/ETSBinder.cpp | 2 +- 39 files changed, 860 insertions(+), 82 deletions(-) create mode 100644 ets2panda/checker/types/ets/etsAwaitedType.cpp create mode 100644 ets2panda/checker/types/ets/etsAwaitedType.h create mode 100644 ets2panda/test/ast/compiler/ets/PromiseAllSettledRejectIgnored.ets create mode 100644 ets2panda/test/ast/compiler/ets/awaited_1.ets create mode 100644 ets2panda/test/ast/compiler/ets/awaited_2.ets create mode 100644 ets2panda/test/ast/compiler/ets/awaited_3.ets create mode 100644 ets2panda/test/ast/compiler/ets/awaited_4.ets create mode 100644 ets2panda/test/ast/compiler/ets/awaited_assignability_2_neg.ets create mode 100644 ets2panda/test/ast/compiler/ets/awaited_assignability_neg.ets create mode 100644 ets2panda/test/ast/compiler/ets/awaited_utility_type.ets create mode 100644 ets2panda/test/ast/compiler/ets/awaited_with_array.ets create mode 100644 ets2panda/test/ast/compiler/ets/catch_error.ets create mode 100644 ets2panda/test/ast/compiler/ets/double_then.ets create mode 100644 ets2panda/test/ast/compiler/ets/readonly.ets create mode 100644 ets2panda/test/ast/compiler/ets/then_ret.ets create mode 100644 ets2panda/test/runtime/ets/awaited_tests/PromiseAllRejectIgnoredDeferred.ets create mode 100644 ets2panda/test/runtime/ets/awaited_tests/awaited_parameter_1.ets create mode 100644 ets2panda/test/runtime/ets/awaited_tests/awaited_return_1.ets create mode 100644 ets2panda/test/runtime/ets/awaited_tests/awaited_type.ets diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 62b92c6fff..af1b255b8b 100644 --- a/ets2panda/BUILD.gn +++ b/ets2panda/BUILD.gn @@ -127,6 +127,7 @@ libes2panda_sources = [ "checker/types/ets/etsObjectType.cpp", "checker/types/ets/etsPartialTypeParameter.cpp", "checker/types/ets/etsReadonlyType.cpp", + "checker/types/ets/etsAwaitedType.cpp", "checker/types/ets/etsResizableArrayType.cpp", "checker/types/ets/etsStringType.cpp", "checker/types/ets/etsTupleType.cpp", diff --git a/ets2panda/CMakeLists.txt b/ets2panda/CMakeLists.txt index 9ea3c3f73a..99db1133b5 100644 --- a/ets2panda/CMakeLists.txt +++ b/ets2panda/CMakeLists.txt @@ -606,6 +606,7 @@ set(ES2PANDA_LIB_SRC checker/types/ets/etsTypeAliasType.cpp checker/types/ets/etsTypeParameter.cpp checker/types/ets/etsPartialTypeParameter.cpp + checker/types/ets/etsAwaitedType.cpp checker/types/ets/etsUnionType.cpp checker/types/ets/etsVoidType.cpp checker/types/ets/wildcardType.cpp diff --git a/ets2panda/ast_verifier/invariants/identifierHasVariable.cpp b/ets2panda/ast_verifier/invariants/identifierHasVariable.cpp index 4eae5c316c..bdc95f22b1 100644 --- a/ets2panda/ast_verifier/invariants/identifierHasVariable.cpp +++ b/ets2panda/ast_verifier/invariants/identifierHasVariable.cpp @@ -75,7 +75,7 @@ private: // NOTE(mmartin): find a better solution to handle utility type resolution return name.Is(Signatures::PARTIAL_TYPE_NAME) || name.Is(Signatures::REQUIRED_TYPE_NAME) || name.Is(Signatures::READONLY_TYPE_NAME) || name.Is(Signatures::FIXED_ARRAY_TYPE_NAME) || - name.Is(compiler::Signatures::ANY_TYPE_NAME); + name.Is(compiler::Signatures::ANY_TYPE_NAME) || name.Is(compiler::Signatures::AWAITED_TYPE_NAME); } bool IsUnionMemberAccess() diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 3d35de95c8..e8052656d7 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -1351,12 +1351,6 @@ std::tuple ETSAnalyzer::CheckAssignmentExprOperatorTyp return {sourceType, relationNode}; } -static bool IsPromiseType(checker::Type *type, ETSChecker *checker) -{ - return type->IsETSObjectType() && - type->AsETSObjectType()->GetOriginalBaseType() == checker->GlobalBuiltinPromiseType(); -} - checker::Type *ETSAnalyzer::Check(ir::AwaitExpression *expr) const { ETSChecker *checker = GetETSChecker(); @@ -1364,60 +1358,10 @@ checker::Type *ETSAnalyzer::Check(ir::AwaitExpression *expr) const return expr->TsType(); } - checker::Type *argType = checker->GetApparentType(expr->argument_->Check(checker)); - ArenaVector awaitedTypes(checker->ProgramAllocator()->Adapter()); - - if (argType->IsETSUnionType()) { - for (Type *type : argType->AsETSUnionType()->ConstituentTypes()) { - if (!IsPromiseType(type, checker)) { - return checker->TypeError(expr, diagnostic::AWAITED_NOT_PROMISE, expr->Argument()->Start()); - } - - Type *typeArg = type->AsETSObjectType()->TypeArguments().at(0); - awaitedTypes.push_back(UnwrapPromiseType(typeArg)); - } - } else { - if (!IsPromiseType(argType, checker)) { - return checker->TypeError(expr, diagnostic::AWAITED_NOT_PROMISE, expr->Argument()->Start()); - } - - Type *typeArg = argType->AsETSObjectType()->TypeArguments().at(0); - awaitedTypes.push_back(UnwrapPromiseType(typeArg)); - } - - expr->SetTsType(argType->IsETSUnionType() ? checker->CreateETSUnionType(std::move(awaitedTypes)) : awaitedTypes[0]); + expr->SetTsType(checker->HandleAwaitExpression(expr->argument_->Check(checker), expr)); return expr->TsType(); } -checker::Type *ETSAnalyzer::UnwrapPromiseType(checker::Type *type) const -{ - ETSChecker *checker = GetETSChecker(); - checker::Type *promiseType = checker->GlobalBuiltinPromiseType(); - while (type->IsETSObjectType() && type->AsETSObjectType()->GetOriginalBaseType() == promiseType) { - type = type->AsETSObjectType()->TypeArguments().at(0); - } - if (!type->IsETSUnionType()) { - return type; - } - const auto &ctypes = type->AsETSUnionType()->ConstituentTypes(); - auto it = std::find_if(ctypes.begin(), ctypes.end(), [promiseType](checker::Type *t) { - return t == promiseType || (t->IsETSObjectType() && t->AsETSObjectType()->GetBaseType() == promiseType); - }); - if (it == ctypes.end()) { - return type; - } - ArenaVector newCTypes(ctypes); - do { - size_t index = it - ctypes.begin(); - newCTypes[index] = UnwrapPromiseType(ctypes[index]); - ++it; - it = std::find_if(it, ctypes.end(), [promiseType](checker::Type *t) { - return t == promiseType || t->AsETSObjectType()->GetBaseType() == promiseType; - }); - } while (it != ctypes.end()); - return checker->CreateETSUnionType(std::move(newCTypes)); -} - checker::Type *ETSAnalyzer::Check(ir::BinaryExpression *expr) const { if (expr->TsType() != nullptr) { diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 9494c61473..eec5c1edcf 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -452,6 +452,8 @@ public: Type *argumentType, Substitution *substitution); [[nodiscard]] bool EnhanceSubstitutionForFunction(const ArenaVector &typeParams, ETSFunctionType *paramType, Type *argumentType, Substitution *substitution); + [[nodiscard]] bool EnhanceSubstitutionForAwaited(const ArenaVector &typeParams, ETSAwaitedType *paramType, + Type *argumentType, Substitution *substitution); [[nodiscard]] bool EnhanceSubstitutionForUnion(const ArenaVector &typeParams, ETSUnionType *paramUn, Type *argumentType, Substitution *substitution); [[nodiscard]] bool EnhanceSubstitutionForArray(const ArenaVector &typeParams, ETSArrayType *paramType, @@ -798,6 +800,11 @@ public: // Readonly Type *GetReadonlyType(Type *type); void MakePropertiesReadonly(ETSObjectType *classType); + // Awaited + Type *HandleAwaitedUtilityType(Type *typeToBeAwaited); + Type *HandleAwaitExpression(Type *typeToBeAwaited, ir::AwaitExpression *expr); + Type *UnwrapPromiseType(checker::Type *type); + bool IsPromiseType(Type *type); // Required Type *HandleRequiredType(Type *typeToBeRequired); void MakePropertiesNonNullish(ETSObjectType *classType); diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index 6f65e600f6..e009490c79 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -21,7 +21,7 @@ #include "checker/ETSchecker.h" #include "checker/ets/function_helpers.h" #include "checker/ets/typeRelationContext.h" -#include "checker/types/ets/etsAsyncFuncReturnType.h" +#include "checker/types/ets/etsAwaitedType.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/gradualType.h" #include "checker/types/typeError.h" @@ -136,8 +136,8 @@ bool ETSChecker::EnhanceSubstitutionForType(const ArenaVector &typeParam if (paramType->IsETSArrayType()) { return EnhanceSubstitutionForArray(typeParams, paramType->AsETSArrayType(), argumentType, substitution); } - if (paramType->IsETSFunctionType()) { - return EnhanceSubstitutionForFunction(typeParams, paramType->AsETSFunctionType(), argumentType, substitution); + if (paramType->IsETSAwaitedType()) { + return EnhanceSubstitutionForAwaited(typeParams, paramType->AsETSAwaitedType(), argumentType, substitution); } return true; @@ -295,6 +295,27 @@ bool ETSChecker::EnhanceSubstitutionForFunction(const ArenaVector &typeP return res; } +bool ETSChecker::EnhanceSubstitutionForAwaited(const ArenaVector &typeParams, ETSAwaitedType *paramType, + Type *argumentType, Substitution *substitution) +{ + auto *argumentAwaitedType = + argumentType->IsETSAwaitedType() ? argumentType->AsETSAwaitedType()->GetUnderlying() : argumentType; + auto *paramAwaitedType = paramType->GetUnderlying(); + return EnhanceSubstitutionForType(typeParams, paramAwaitedType, argumentAwaitedType, substitution); +} + +bool ETSChecker::EnhanceSubstitutionForPartialTypeParam(const ArenaVector &typeParams, + ETSPartialTypeParameter *paramType, Type *argumentType, + Substitution *substitution) +{ + if (!argumentType->IsETSObjectType() || !argumentType->AsETSObjectType()->IsPartial()) { + return false; + } + ES2PANDA_ASSERT(argumentType->AsETSObjectType()->GetBaseType() != nullptr); + return EnhanceSubstitutionForType(typeParams, paramType->GetUnderlying(), + argumentType->AsETSObjectType()->GetBaseType(), substitution); +} + // Try to find the base type somewhere in object subtypes. Incomplete, yet safe static ETSObjectType *FindEnhanceTargetInSupertypes(ETSObjectType *object, ETSObjectType *base) { diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index 42bf284a93..56771f1df9 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -20,6 +20,7 @@ #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsTupleType.h" #include "checker/types/ets/etsPartialTypeParameter.h" +#include "checker/types/ets/etsAwaitedType.h" #include "checker/types/gradualType.h" #include "compiler/lowering/phase.h" #include "ir/base/classDefinition.h" @@ -151,7 +152,8 @@ static bool CheckObjectTypeAndSuperType(ETSChecker *checker, ETSObjectType *type auto *classDef = type->GetDeclNode()->AsClassDefinition(); auto cName = classDef->Ident()->Name(); if (cName == compiler::Signatures::PARTIAL_TYPE_NAME || cName == compiler::Signatures::READONLY_TYPE_NAME || - cName == compiler::Signatures::REQUIRED_TYPE_NAME || cName == compiler::Signatures::FIXED_ARRAY_TYPE_NAME) { + cName == compiler::Signatures::REQUIRED_TYPE_NAME || cName == compiler::Signatures::FIXED_ARRAY_TYPE_NAME || + cName == compiler::Signatures::AWAITED_TYPE_NAME) { checker->LogError(diagnostic::USING_RESERVED_NAME_AS_VARIABLE_OR_TYPE_NAME, {cName}, type->GetDeclNode()->Start()); type->SetSuperType(checker->GlobalETSObjectType()); @@ -2663,6 +2665,29 @@ void ETSChecker::AddElementsToModuleObject(ETSObjectType *moduleObj, const util: } } +static Type *GetApparentTypeUtilityTypes(checker::ETSChecker *checker, Type *type) +{ + if (type->IsETSReadonlyType()) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return checker->GetApparentType(type->AsETSReadonlyType()->GetUnderlying()->GetConstraintType()); + } + if (type->IsETSAwaitedType()) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return checker->GetApparentType(type->AsETSAwaitedType()->GetUnderlying()->GetConstraintType()); + } + if (type->IsETSPartialTypeParameter()) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return checker->CreatePartialType( + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + checker->GetApparentType(type->AsETSPartialTypeParameter()->GetUnderlying()->GetConstraintType())); + } + if (type->IsETSNonNullishType()) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return checker->GetApparentType(type->AsETSNonNullishType()->GetUnderlying()->GetConstraintType()); + } + return type; +} + // This function computes effective runtime view of type Type *ETSChecker::GetApparentType(Type *type) { @@ -2706,12 +2731,6 @@ Type *ETSChecker::GetApparentType(Type *type) // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) GetNonNullishType(GetApparentType(type->AsETSNonNullishType()->GetUnderlying()->GetConstraintType()))); } - if (type->IsETSPartialTypeParameter()) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return cached(CreatePartialType( - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - GetApparentType(type->AsETSPartialTypeParameter()->GetUnderlying()->GetConstraintType()))); - } if (type->IsETSArrayType()) { return cached(type); } @@ -2728,7 +2747,8 @@ Type *ETSChecker::GetApparentType(Type *type) } return cached(differ ? CreateETSUnionType(std::move(newConstituent)) : type); } - return cached(type); + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return cached(GetApparentTypeUtilityTypes(this, type)); } Type const *ETSChecker::GetApparentType(Type const *type) const diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index dd0a84f46e..3fe9bdd5b1 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -385,7 +385,7 @@ bool Type::IsETSMethodType() const TypeFlag::ETS_TYPE_PARAMETER | TypeFlag::WILDCARD | TypeFlag::ETS_NONNULLISH | TypeFlag::ETS_REQUIRED_TYPE_PARAMETER | TypeFlag::ETS_ANY | TypeFlag::ETS_NEVER | TypeFlag::ETS_UNION | TypeFlag::ETS_ARRAY | TypeFlag::FUNCTION | TypeFlag::ETS_PARTIAL_TYPE_PARAMETER | TypeFlag::ETS_TUPLE | - TypeFlag::ETS_ENUM | TypeFlag::ETS_READONLY | TypeFlag::GRADUAL_TYPE; + TypeFlag::ETS_ENUM | TypeFlag::ETS_READONLY | TypeFlag::GRADUAL_TYPE | TypeFlag::ETS_AWAITED; // Issues if (type->IsETSVoidType()) { // NOTE(vpukhov): #19701 void refactoring diff --git a/ets2panda/checker/ets/utilityTypeHandlers.cpp b/ets2panda/checker/ets/utilityTypeHandlers.cpp index 48967c2d7d..f51265fde3 100644 --- a/ets2panda/checker/ets/utilityTypeHandlers.cpp +++ b/ets2panda/checker/ets/utilityTypeHandlers.cpp @@ -22,6 +22,7 @@ #include "ir/expressions/literals/undefinedLiteral.h" #include "varbinder/ETSBinder.h" #include "checker/types/ets/etsPartialTypeParameter.h" +#include "checker/types/ets/etsAwaitedType.h" #include "util/nameMangler.h" #include @@ -93,10 +94,98 @@ Type *ETSChecker::HandleUtilityTypeParameterNode(const ir::TSTypeParameterInstan return HandleRequiredType(baseType); } + if (utilityType == compiler::Signatures::AWAITED_TYPE_NAME) { + return HandleAwaitedUtilityType(baseType); + } + LogError(diagnostic::UTILITY_TYPE_UNIMPLEMENTED, {}, typeParams->Start()); return baseType; } +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// Awaited utility type +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +bool ETSChecker::IsPromiseType(Type *type) +{ + if (type->IsETSUnionType()) { + for (Type *constituentType : type->AsETSUnionType()->ConstituentTypes()) { + if (!IsPromiseType(constituentType)) { + return false; + } + } + return true; + } + return type->IsETSObjectType() && type->AsETSObjectType()->GetOriginalBaseType() == GlobalBuiltinPromiseType(); +} + +Type *ETSChecker::UnwrapPromiseType(checker::Type *type) +{ + Type *promiseType = GlobalBuiltinPromiseType(); + while (type->IsETSObjectType() && type->AsETSObjectType()->GetOriginalBaseType() == promiseType) { + type = type->AsETSObjectType()->TypeArguments().at(0); + } + if (!type->IsETSUnionType()) { + return type; + } + const auto &ctypes = type->AsETSUnionType()->ConstituentTypes(); + auto it = std::find_if(ctypes.begin(), ctypes.end(), [promiseType](checker::Type *t) { + return t == promiseType || (t->IsETSObjectType() && t->AsETSObjectType()->GetBaseType() == promiseType); + }); + if (it == ctypes.end()) { + return type; + } + ArenaVector newCTypes(ctypes); + do { + size_t index = it - ctypes.begin(); + newCTypes[index] = UnwrapPromiseType(ctypes[index]); + ++it; + it = std::find_if(it, ctypes.end(), [promiseType](checker::Type *t) { + return t == promiseType || t->AsETSObjectType()->GetBaseType() == promiseType; + }); + } while (it != ctypes.end()); + return CreateETSUnionType(std::move(newCTypes)); +} + +Type *ETSChecker::HandleAwaitedUtilityType(Type *typeToBeAwaited) +{ + if (typeToBeAwaited->IsETSTypeParameter()) { + return ProgramAllocator()->New(typeToBeAwaited->AsETSTypeParameter()); + } + + if (typeToBeAwaited->IsETSUnionType()) { + ArenaVector awaitedTypes(ProgramAllocator()->Adapter()); + for (Type *type : typeToBeAwaited->AsETSUnionType()->ConstituentTypes()) { + Type *unwrapped = IsPromiseType(type) ? type->AsETSObjectType()->TypeArguments().at(0) : type; + if (unwrapped->IsETSTypeParameter()) { + unwrapped = HandleAwaitedUtilityType(unwrapped); + } + awaitedTypes.push_back(UnwrapPromiseType(unwrapped)); + } + return CreateETSUnionType(std::move(awaitedTypes)); + } + + if (IsPromiseType(typeToBeAwaited)) { + Type *typeArg = typeToBeAwaited->AsETSObjectType()->TypeArguments().at(0); + auto unwrappedType = UnwrapPromiseType(typeArg); + return unwrappedType->IsETSTypeParameter() ? HandleAwaitedUtilityType(unwrappedType) : unwrappedType; + } + + return typeToBeAwaited; +} + +Type *ETSChecker::HandleAwaitExpression(Type *typeToBeAwaited, ir::AwaitExpression *expr) +{ + Relation()->SetFlags(TypeRelationFlag::IGNORE_TYPE_PARAMETERS); + if (!typeToBeAwaited->IsETSTypeParameter() && + !Relation()->IsSupertypeOf(GlobalBuiltinPromiseType(), typeToBeAwaited)) { + LogError(diagnostic::AWAITED_TYPE_NOT_PROMISE, {typeToBeAwaited}, expr->Start()); + return typeToBeAwaited; + } + Relation()->RemoveFlags(TypeRelationFlag::IGNORE_TYPE_PARAMETERS); + + return HandleAwaitedUtilityType(typeToBeAwaited); +} + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Partial utility type // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ diff --git a/ets2panda/checker/types/ets/etsAwaitedType.cpp b/ets2panda/checker/types/ets/etsAwaitedType.cpp new file mode 100644 index 0000000000..0adb9a37cd --- /dev/null +++ b/ets2panda/checker/types/ets/etsAwaitedType.cpp @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#include "checker/ETSchecker.h" +#include "etsAwaitedType.h" + +namespace ark::es2panda::checker { +void ETSAwaitedType::ToString(std::stringstream &ss, bool precise) const +{ + ss << "Awaited<"; + GetUnderlying()->ToString(ss, precise); + ss << ">"; +} + +void ETSAwaitedType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsETSAwaitedType()) { + relation->IsIdenticalTo(GetUnderlying(), other->AsETSAwaitedType()->GetUnderlying()); + } +} + +bool ETSAwaitedType::AssignmentSource(TypeRelation *relation, Type *target) +{ + return relation->IsSupertypeOf(target, this); +} + +void ETSAwaitedType::AssignmentTarget(TypeRelation *relation, Type *source) +{ + relation->IsSupertypeOf(this, source); +} + +void ETSAwaitedType::Cast(TypeRelation *relation, Type *target) +{ + if (relation->IsSupertypeOf(target, this)) { + relation->RemoveFlags(TypeRelationFlag::UNCHECKED_CAST); + return; + } + relation->Result(relation->InCastingContext()); +} + +void ETSAwaitedType::CastTarget(TypeRelation *relation, Type *source) +{ + if (relation->IsSupertypeOf(this, source)) { + relation->RemoveFlags(TypeRelationFlag::UNCHECKED_CAST); + return; + } + relation->Result(relation->InCastingContext()); +} + +void ETSAwaitedType::IsSupertypeOf(TypeRelation *relation, [[maybe_unused]] Type *source) +{ + relation->Result(false); +} + +void ETSAwaitedType::IsSubtypeOf(TypeRelation *relation, Type *target) +{ + relation->Result(false); + if (target->IsETSAwaitedType()) { + relation->IsSupertypeOf(target->AsETSAwaitedType()->GetUnderlying(), GetUnderlying()); + } +} +ETSAwaitedType *ETSAwaitedType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, + [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return allocator->New( + GetUnderlying()->Instantiate(allocator, relation, globalTypes)->AsETSTypeParameter()); +} + +Type *ETSAwaitedType::Substitute([[maybe_unused]] TypeRelation *relation, const Substitution *substitution) +{ + auto *substituted = GetUnderlying()->Substitute(relation, substitution); + auto *checker = relation->GetChecker()->AsETSChecker(); + if (substituted == GetUnderlying()) { + return this; + } + + return checker->HandleAwaitedUtilityType(substituted); +} + +void ETSAwaitedType::ToAssemblerType(std::stringstream &ss) const +{ + GetUnderlying()->ToAssemblerTypeWithRank(ss); +} + +void ETSAwaitedType::ToDebugInfoType(std::stringstream &ss) const +{ + GetUnderlying()->ToDebugInfoType(ss); +} + +void ETSAwaitedType::CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) +{ + relation->CheckVarianceRecursively(GetUnderlying(), + relation->TransferVariant(varianceFlag, VarianceFlag::COVARIANT)); +} +} // namespace ark::es2panda::checker \ No newline at end of file diff --git a/ets2panda/checker/types/ets/etsAwaitedType.h b/ets2panda/checker/types/ets/etsAwaitedType.h new file mode 100644 index 0000000000..e5415da37c --- /dev/null +++ b/ets2panda/checker/types/ets/etsAwaitedType.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_ETS_AWAITED_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_AWAITED_TYPE_H + +#include "checker/types/ets/etsTypeParameter.h" +#include "checker/types/type.h" + +namespace ark::es2panda::checker { +class ETSAwaitedType : public Type { +public: + ETSAwaitedType() = delete; + ~ETSAwaitedType() override = default; + + NO_COPY_SEMANTIC(ETSAwaitedType); + NO_MOVE_SEMANTIC(ETSAwaitedType); + + explicit ETSAwaitedType(ETSTypeParameter *tparam) : Type(TypeFlag::ETS_AWAITED), tparam_(tparam) {} + + ETSTypeParameter *GetUnderlying() const + { + return tparam_; + } + + Type *Substitute(TypeRelation *relation, const Substitution *substitution) override; + + ETSAwaitedType *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, + GlobalTypesHolder *globalTypes) override; + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource(TypeRelation *relation, Type *target) override; + void Cast(TypeRelation *relation, Type *target) override; + void CastTarget(TypeRelation *relation, Type *source) override; + void IsSupertypeOf(TypeRelation *relation, Type *source) override; + void IsSubtypeOf(TypeRelation *relation, Type *target) override; + + void CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) override; + + void ToString(std::stringstream &ss, bool precise) const override; + void ToAssemblerType(std::stringstream &ss) const override; + void ToDebugInfoType(std::stringstream &ss) const override; + +private: + ETSTypeParameter *const tparam_; +}; + +} // namespace ark::es2panda::checker + +#endif diff --git a/ets2panda/checker/types/typeFlag.h b/ets2panda/checker/types/typeFlag.h index 5f01ae8225..0c8da35014 100644 --- a/ets2panda/checker/types/typeFlag.h +++ b/ets2panda/checker/types/typeFlag.h @@ -48,7 +48,7 @@ enum class TypeFlag : uint64_t { INTERSECTION = 1ULL << 19ULL, // x: a & b INDEX = 1ULL << 20ULL, // keyof x INDEX_ACCESS = 1ULL << 21ULL, // x[a] - TEMPLATE_LITERAL = 1ULL << 23ULL, // x: `hello ${World}` + ETS_AWAITED = 1ULL << 23ULL, // ETS Awaited type ANY = 1ULL << 24ULL, // x: any ARRAY = 1ULL << 25ULL, // x: number[] FUNCTION = 1ULL << 26ULL, // x: (a) => b diff --git a/ets2panda/checker/types/typeMapping.h b/ets2panda/checker/types/typeMapping.h index 84e3ba9cfe..0ab8b04c64 100644 --- a/ets2panda/checker/types/typeMapping.h +++ b/ets2panda/checker/types/typeMapping.h @@ -64,6 +64,7 @@ _(TypeFlag::ETS_TYPE_PARAMETER, ETSTypeParameter) \ _(TypeFlag::ETS_NONNULLISH, ETSNonNullishType) \ _(TypeFlag::ETS_READONLY, ETSReadonlyType) \ + _(TypeFlag::ETS_AWAITED, ETSAwaitedType) \ _(TypeFlag::ETS_INT_ENUM, ETSIntEnumType) \ _(TypeFlag::ETS_STRING_ENUM, ETSStringEnumType) \ _(TypeFlag::ETS_ENUM, ETSEnumType) \ diff --git a/ets2panda/compiler/scripts/signatures.yaml b/ets2panda/compiler/scripts/signatures.yaml index 8a03d4889a..67a7788047 100644 --- a/ets2panda/compiler/scripts/signatures.yaml +++ b/ets2panda/compiler/scripts/signatures.yaml @@ -154,6 +154,8 @@ defines: ref: READONLY_TYPE_NAME - name: 'Partial' ref: PARTIAL_TYPE_NAME + - name: 'Awaited' + ref: AWAITED_TYPE_NAME - name: 'FixedArray' ref: FIXED_ARRAY_TYPE_NAME - name: 'Required' diff --git a/ets2panda/ir/ets/etsTypeReferencePart.cpp b/ets2panda/ir/ets/etsTypeReferencePart.cpp index d1ccf9cb5b..2491ba33fd 100644 --- a/ets2panda/ir/ets/etsTypeReferencePart.cpp +++ b/ets2panda/ir/ets/etsTypeReferencePart.cpp @@ -161,7 +161,8 @@ static checker::Type *CheckPredefinedBuiltinTypes(checker::ETSChecker *const che } if (ident->Name() == compiler::Signatures::READONLY_TYPE_NAME || - ident->Name() == compiler::Signatures::REQUIRED_TYPE_NAME) { + ident->Name() == compiler::Signatures::REQUIRED_TYPE_NAME || + ident->Name() == compiler::Signatures::AWAITED_TYPE_NAME) { return checker->HandleUtilityTypeParameterNode(ref->TypeParams(), ident); } if (ident->Name() == compiler::Signatures::PARTIAL_TYPE_NAME) { diff --git a/ets2panda/test/ast/compiler/ets/PromiseAllSettledRejectIgnored.ets b/ets2panda/test/ast/compiler/ets/PromiseAllSettledRejectIgnored.ets new file mode 100644 index 0000000000..db53e7267e --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/PromiseAllSettledRejectIgnored.ets @@ -0,0 +1,59 @@ +/* + * 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 Fulfill implements PromiseLike { + then(onFulfill?: (value: string) => U | PromiseLike, _?: + (error: Error) => E | PromiseLike): PromiseLike> { + return Promise.resolve(onFulfill!('abc')); + } +} + +class Reject implements PromiseLike { + then(onFulfilled?: (value: string) => U | PromiseLike, onRejected?: + (error: Error) => E | PromiseLike): PromiseLike> { + onFulfilled!('def'); + return Promise.reject(onRejected!(new Error('xyz')) as Error); + } +} + +let thenable: PromiseLike[] = [new Fulfill(), new Reject()]; +Promise.allSettled(thenable).then((values: PromiseSettledResult[]): void => { + if (values.length != 2) { + console.log('Test failed. Expected a string array of length 2 but got length ' + values.length + '.'); + return; + } + if (values[0].status != PromiseStatus.fulfilled || values[1].status != PromiseStatus.fulfilled) { + console.log('Test failed. Expected all elements has status \'fulfilled\'.'); + return; + } + let v: string = (values[0] as PromiseFulfilledResult).value; + if (v != 'abc') { + console.log('Test failed. Expected the first elements has value \'abc\' but got ' + v + '.'); + return; + } + v = (values[1] as PromiseFulfilledResult).value; + if (v != 'def') { + console.log('Test failed. Expected the first elements has value \'abc\' but got ' + v + '.'); + return; + } + console.log('Test passed.'); +}, (error: Error): void => { + console.log('Test failed. The promise should not be rejected.'); +}).catch((e: Error): void => { + console.log('Test failed: ' + e); +}); + +/* @@? 19:16 Error TypeError: Type 'Promise' is not compatible with the enclosing method's return type 'PromiseLike|Awaited>' */ +/* @@? 27:16 Error TypeError: Type 'Promise' is not compatible with the enclosing method's return type 'PromiseLike|Awaited>' */ \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/awaited_1.ets b/ets2panda/test/ast/compiler/ets/awaited_1.ets new file mode 100644 index 0000000000..3ea926e1b4 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/awaited_1.ets @@ -0,0 +1,35 @@ +/* + * 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. + */ + +interface User { + age: Promise +} +class Myuser implements User { + age: Promise = delay(1) +} +const delay = (value: number): Promise => { + return Promise.resolve(value) +} +async function identity(arg: Type): Promise { + return Promise.resolve(arg); +} + +let user = new Myuser() +type user_name = Promise + +async function main() { + let awaited_user: number = await user.age + let aw: number = await identity(user.age) +} \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/awaited_2.ets b/ets2panda/test/ast/compiler/ets/awaited_2.ets new file mode 100644 index 0000000000..99fc80034e --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/awaited_2.ets @@ -0,0 +1,28 @@ +/* + * 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(x: Awaited) {} + +async function ff(x: Promise) { + f(await x) +} + +const asyncfunc = async() => { + return Promise.resolve(1.0) +} + +function main() { + let x = ff(asyncfunc()) +} \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/awaited_3.ets b/ets2panda/test/ast/compiler/ets/awaited_3.ets new file mode 100644 index 0000000000..34bdb6b7aa --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/awaited_3.ets @@ -0,0 +1,29 @@ +/* + * 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. + */ + +type PromisesArray = Promise[]; +type AwaitedArray = Awaited; + +async function B(a: T) : Promise> { + return await a +} + +async function main() { + let a: Promise = Promise.resolve("1") + let b: PromisesArray = [a] + console.log(b) + let c: AwaitedArray = await B(b) + console.log(c) +} \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/awaited_4.ets b/ets2panda/test/ast/compiler/ets/awaited_4.ets new file mode 100644 index 0000000000..122bb725e9 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/awaited_4.ets @@ -0,0 +1,31 @@ +/* + * 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 { + method_1(): Awaited { + console.log("1") + return "1" as Awaited + } +} + +class B extends A { + method_1(): Awaited { + return "1" as Awaited + } +} + +function ff1() { + let a: Awaited = "1" as Awaited +} \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/awaited_assignability_2_neg.ets b/ets2panda/test/ast/compiler/ets/awaited_assignability_2_neg.ets new file mode 100644 index 0000000000..eba08ed879 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/awaited_assignability_2_neg.ets @@ -0,0 +1,21 @@ +/* + * 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 aw(p: Promise, cb: (v: T) => void) { + let x: T = await p + cb(x) +} + +/* @@? 17:16 Error TypeError: Type 'Awaited' cannot be assigned to type 'T' */ \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/awaited_assignability_neg.ets b/ets2panda/test/ast/compiler/ets/awaited_assignability_neg.ets new file mode 100644 index 0000000000..680c93f328 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/awaited_assignability_neg.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. + */ + +async function aw(p: Promise, cb: (v: T) => void) { + let x: Awaited = await p + cb(x) +} + +/* @@? 18:5 Error TypeError: No matching call signature for (Awaited) */ +/* @@? 18:8 Error TypeError: Type 'Awaited' is not compatible with type 'T' at index 1 */ \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/awaited_utility_type.ets b/ets2panda/test/ast/compiler/ets/awaited_utility_type.ets new file mode 100644 index 0000000000..d9f5bc86dd --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/awaited_utility_type.ets @@ -0,0 +1,28 @@ +/* + * 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. + */ + +type A = Awaited // string +type B = Awaited>>> // number | boolean +type C = Awaited | boolean> // number | boolean +type D = Awaited // number | string + +type E = Awaited|Promise>>> +const results: E = [Promise.resolve(1.0)] // OK +let results_2 : E = [1.0] +let a: A = "a" +let valueB: B = true +let valueD: D = 123 + +/* @@? 23:22 Error TypeError: Array element at index 0 with type 'Double' is not compatible with the target array element type 'Promise|Promise' */ \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/awaited_with_array.ets b/ets2panda/test/ast/compiler/ets/awaited_with_array.ets new file mode 100644 index 0000000000..644cf02a40 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/awaited_with_array.ets @@ -0,0 +1,21 @@ +/* + * 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. + */ + +type PromisesArray = Promise[] +type AwaitedArray = Awaited + +const results: AwaitedArray = ["hello"] + +/* @@? 19:32 Error TypeError: Array element at index 0 with type '"hello"' is not compatible with the target array element type 'Promise' */ diff --git a/ets2panda/test/ast/compiler/ets/catch_error.ets b/ets2panda/test/ast/compiler/ets/catch_error.ets new file mode 100644 index 0000000000..069c102528 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/catch_error.ets @@ -0,0 +1,43 @@ +/* + * 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 asyncFunction = async () => { + return new Promise((resolve, reject) => { + setTimeout(() => { + reject(new Error()) + }, 1000) + }) +} + +async function main() { + const result : number = await asyncFunction() + .then((result) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(result + 1) + }, 500) + }) + }) + .then((result) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(result + 4) + }, 500) + }) + }).catch((): number => { + console.log("PromiseMultiAsyncOperationTest0300 test error") + return 0 + }) +} \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/double_then.ets b/ets2panda/test/ast/compiler/ets/double_then.ets new file mode 100644 index 0000000000..9f5ee761e8 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/double_then.ets @@ -0,0 +1,28 @@ +/* + * 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 asyncFunction = async () => { + return Promise.resolve(1.0) +}; + +async function main() { + const result : number = await asyncFunction() + .then((result) => { + return Promise.resolve(result + 2.0) + }) + .then((result) => { + return Promise.resolve(result + 3.0) + }) +} \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/null_pointer_error.ets b/ets2panda/test/ast/compiler/ets/null_pointer_error.ets index 289f308742..33034e04a0 100644 --- a/ets2panda/test/ast/compiler/ets/null_pointer_error.ets +++ b/ets2panda/test/ast/compiler/ets/null_pointer_error.ets @@ -25,3 +25,4 @@ export class AbstractDaoSession{ /* @@? 1:3 Error TypeError: 'ValueType' type does not exist. */ /* @@? 17:62 Error SyntaxError: Rest parameter should be either array or tuple type. */ /* @@? 19:17 Error TypeError: Cannot find type 'AbstractDao'. */ +/* @@? 20:16 Error TypeError: Type '*ERROR_TYPE*' can not be awaited, it is not a Promise. */ diff --git a/ets2panda/test/ast/compiler/ets/readonly.ets b/ets2panda/test/ast/compiler/ets/readonly.ets new file mode 100644 index 0000000000..1c547d70cc --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/readonly.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. + */ + +function f(x: Readonly) {} + +async function ff(x:T) { + f(a(x)) +} + +function a(x:T) : Readonly { return x } \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/then_ret.ets b/ets2panda/test/ast/compiler/ets/then_ret.ets new file mode 100644 index 0000000000..ccb9208140 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/then_ret.ets @@ -0,0 +1,23 @@ +/* + * 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 asyncFunction = async () => { + return Promise.resolve(1.0) +}; + +let thenRet : Promise = asyncFunction() + .then((result) => { + return Promise.resolve(result + 2.0) + }) \ No newline at end of file diff --git a/ets2panda/test/ast/parser/ets/await_argument_null.ets b/ets2panda/test/ast/parser/ets/await_argument_null.ets index 9b7a14c21a..80bafac120 100644 --- a/ets2panda/test/ast/parser/ets/await_argument_null.ets +++ b/ets2panda/test/ast/parser/ets/await_argument_null.ets @@ -14,9 +14,10 @@ */ async function foo(): Promise { - let obj: Object = await /* @@ label */null; + let obj: Object = /* @@ label */await null; return /* @@ label1 */null; } -/* @@@ label Error TypeError: 'await' expressions require Promise object as argument. */ +/* @@@ label Error TypeError: Type 'null' can not be awaited, it is not a Promise. */ +/* @@@ label Error TypeError: Type 'null' cannot be assigned to type 'Object' */ /* @@@ label1 Error TypeError: Type 'null' is not compatible with the enclosing method's return type 'Promise | Object' */ diff --git a/ets2panda/test/ast/parser/ets/await_object_bad.ets b/ets2panda/test/ast/parser/ets/await_object_bad.ets index 0b3bf805be..f7d0995413 100644 --- a/ets2panda/test/ast/parser/ets/await_object_bad.ets +++ b/ets2panda/test/ast/parser/ets/await_object_bad.ets @@ -14,9 +14,9 @@ */ async function foo(): Promise { - let obj: Object = await /* @@ label */5; + let obj: Object = /* @@ label */await 5; return /* @@ label1 */null; } -/* @@@ label Error TypeError: 'await' expressions require Promise object as argument. */ +/* @@@ label Error TypeError: Type 'Int' can not be awaited, it is not a Promise. */ /* @@@ label1 Error TypeError: Type 'null' is not compatible with the enclosing method's return type 'Promise | Object' */ diff --git a/ets2panda/test/ast/parser/ets/await_promise_bad.ets b/ets2panda/test/ast/parser/ets/await_promise_bad.ets index d0ee72abbd..e1d10cf959 100644 --- a/ets2panda/test/ast/parser/ets/await_promise_bad.ets +++ b/ets2panda/test/ast/parser/ets/await_promise_bad.ets @@ -14,9 +14,9 @@ */ async function foo(): Promise { - let obj: Object = await /* @@ label */new Object(); + let obj: Object = /* @@ label */await new Object(); return /* @@ label1 */null; } -/* @@@ label Error TypeError: 'await' expressions require Promise object as argument. */ +/* @@@ label Error TypeError: Type 'Object' can not be awaited, it is not a Promise. */ /* @@@ label1 Error TypeError: Type 'null' is not compatible with the enclosing method's return type 'Promise | Object' */ diff --git a/ets2panda/test/ast/parser/ets/dynmicImportUnimplemented.ets b/ets2panda/test/ast/parser/ets/dynmicImportUnimplemented.ets index 089ace2f22..e681815d55 100644 --- a/ets2panda/test/ast/parser/ets/dynmicImportUnimplemented.ets +++ b/ets2panda/test/ast/parser/ets/dynmicImportUnimplemented.ets @@ -28,4 +28,4 @@ function main() { } /* @@? 18:15 Error SyntaxError: Unexpected token 'import'. */ -/* @@? 18:15 Error TypeError: 'await' expressions require Promise object as argument. */ +/* @@? 18:9 Error TypeError: Type '*ERROR_TYPE*' can not be awaited, it is not a Promise. */ diff --git a/ets2panda/test/runtime/ets/awaited_tests/PromiseAllRejectIgnoredDeferred.ets b/ets2panda/test/runtime/ets/awaited_tests/PromiseAllRejectIgnoredDeferred.ets new file mode 100644 index 0000000000..e8093a5150 --- /dev/null +++ b/ets2panda/test/runtime/ets/awaited_tests/PromiseAllRejectIgnoredDeferred.ets @@ -0,0 +1,47 @@ +/* + * 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 Fulfill implements PromiseLike { + then(onFulfill?: (value: string) => U | PromiseLike, _?: + (error: Error) => E | PromiseLike): PromiseLike> { + return Promise.resolve().then((): U | PromiseLike => { + return onFulfill!('abc'); + }); + } +} + +class Reject implements PromiseLike { + then(onFulfilled?: (value: string) => U | PromiseLike, onRejected?: + (error: Error) => E | PromiseLike): PromiseLike> { + return Promise.resolve().then((): E | PromiseLike => { + onFulfilled!('def'); + return onRejected!(new Error('xyz')); + }); + } +} + +let thenable: PromiseLike[] = [new Fulfill(), new Reject()]; +Promise.all(thenable).then((values): void => { + if (values.length != 2) { + console.log('Test failed. Expected a string array of length 2 but got length ' + values.length + '.'); + return; + } + if (values[0] != 'abc' || values[1] != 'def') { + console.log('Test failed. The promise should be resolved by the specified values.'); + return; + } + console.log('Test passed.'); +}, (error: Error): void => { + console.log('Test failed. The promise should not be rejected.'); +}); diff --git a/ets2panda/test/runtime/ets/awaited_tests/awaited_parameter_1.ets b/ets2panda/test/runtime/ets/awaited_tests/awaited_parameter_1.ets new file mode 100644 index 0000000000..2c8a0edf41 --- /dev/null +++ b/ets2panda/test/runtime/ets/awaited_tests/awaited_parameter_1.ets @@ -0,0 +1,26 @@ +/* + * 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 foo(awaited: Awaited, expected: T) { + arktest.assertEQ(awaited, expected) +} + +function main() { + await foo(await new Promise((resolve) => { + setTimeout(() => { + resolve("hello") + }, 1000) +}), "hello") +} diff --git a/ets2panda/test/runtime/ets/awaited_tests/awaited_return_1.ets b/ets2panda/test/runtime/ets/awaited_tests/awaited_return_1.ets new file mode 100644 index 0000000000..988d4cce47 --- /dev/null +++ b/ets2panda/test/runtime/ets/awaited_tests/awaited_return_1.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. + */ + +function foo(): Awaited { + return await new Promise((resolve) => { + setTimeout(() => { + resolve("hello" as T) + }, 1000) +}) +} + +function main() { + let actual = foo() + arktest.assertEQ(actual, "hello") +} \ No newline at end of file diff --git a/ets2panda/test/runtime/ets/awaited_tests/awaited_type.ets b/ets2panda/test/runtime/ets/awaited_tests/awaited_type.ets new file mode 100644 index 0000000000..99fc80034e --- /dev/null +++ b/ets2panda/test/runtime/ets/awaited_tests/awaited_type.ets @@ -0,0 +1,28 @@ +/* + * 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(x: Awaited) {} + +async function ff(x: Promise) { + f(await x) +} + +const asyncfunc = async() => { + return Promise.resolve(1.0) +} + +function main() { + let x = ff(asyncfunc()) +} \ No newline at end of file diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index f76f0cf5ec..eb667041af 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -207,9 +207,9 @@ semantic: message: Return type of async function must be 'Promise'. code_fix_ids: [FixReturnTypeInAsyncFunction] -- name: AWAITED_NOT_PROMISE +- name: AWAITED_TYPE_NOT_PROMISE id: 303 - message: "'await' expressions require Promise object as argument." + message: "Type '{}' can not be awaited, it is not a Promise." - name: BINOP_DYN_UNIMPLEMENTED id: 112 diff --git a/ets2panda/varbinder/ETSBinder.cpp b/ets2panda/varbinder/ETSBinder.cpp index 9305148e05..6ddcd98c16 100644 --- a/ets2panda/varbinder/ETSBinder.cpp +++ b/ets2panda/varbinder/ETSBinder.cpp @@ -60,7 +60,7 @@ bool ETSBinder::IsSpecialName(const util::StringView &name) return name == compiler::Signatures::ANY_TYPE_NAME || name == compiler::Signatures::UNDEFINED || name == compiler::Signatures::NULL_LITERAL || name == compiler::Signatures::READONLY_TYPE_NAME || name == compiler::Signatures::PARTIAL_TYPE_NAME || name == compiler::Signatures::REQUIRED_TYPE_NAME || - name == compiler::Signatures::FIXED_ARRAY_TYPE_NAME; + name == compiler::Signatures::FIXED_ARRAY_TYPE_NAME || name == compiler::Signatures::AWAITED_TYPE_NAME; } bool ETSBinder::LookupInDebugInfoPlugin(ir::Identifier *ident) -- Gitee