From 17619b5f080ecb92b8d8d96647e86f058ae64d3e Mon Sep 17 00:00:00 2001 From: daizihan Date: Wed, 4 Jun 2025 20:17:02 +0800 Subject: [PATCH 1/4] Support gradula type and any type in bc Description: 1. support gradual type, all the types except escompat & arkts1.2 type in the dynamic Declaration file are treated as gradual type. fe type checking is enabled. 2. add lowering to transfrom gradual type to any type, and emit any type related bc Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICE0RF?from=project-issue Signed-off-by: xuxinjie4 Signed-off-by: daizihan --- ets2panda/BUILD.gn | 3 + ets2panda/CMakeLists.txt | 3 + ets2panda/ast_verifier/helpers.cpp | 59 ++-- .../invariants/importExportAccessValid.cpp | 7 - ets2panda/checker/ETSAnalyzer.cpp | 116 ++++--- ets2panda/checker/ETSAnalyzerHelpers.cpp | 6 +- ets2panda/checker/ETSchecker.cpp | 20 +- ets2panda/checker/ETSchecker.h | 12 +- ets2panda/checker/checker.cpp | 5 + ets2panda/checker/checker.h | 2 + ets2panda/checker/checkerContext.cpp | 2 +- ets2panda/checker/ets/assignAnalyzer.cpp | 4 + ets2panda/checker/ets/function.cpp | 81 +++-- ets2panda/checker/ets/function_helpers.h | 17 +- ets2panda/checker/ets/helpers.cpp | 54 ++- ets2panda/checker/ets/object.cpp | 66 ++-- ets2panda/checker/ets/typeCheckingHelpers.cpp | 24 +- ets2panda/checker/ets/typeCreation.cpp | 78 ++++- ets2panda/checker/ets/typeRelationContext.cpp | 16 +- ets2panda/checker/ets/typeRelationContext.h | 4 + ets2panda/checker/ets/utilityTypeHandlers.cpp | 10 +- ets2panda/checker/types/ets/etsAnyType.cpp | 2 +- ets2panda/checker/types/ets/etsAnyType.h | 12 + .../checker/types/ets/etsFunctionType.cpp | 4 +- ets2panda/checker/types/ets/etsObjectType.cpp | 8 +- .../types/ets/etsObjectTypeConstants.h | 1 + .../checker/types/ets/etsTypeParameter.cpp | 5 +- ets2panda/checker/types/ets/etsUnionType.cpp | 2 +- ets2panda/checker/types/gradualType.cpp | 97 ++++++ ets2panda/checker/types/gradualType.h | 89 +++++ ets2panda/checker/types/signature.cpp | 2 +- ets2panda/checker/types/type.cpp | 10 + ets2panda/checker/types/type.h | 3 + ets2panda/checker/types/typeFlag.h | 5 +- ets2panda/checker/types/typeMapping.h | 1 + ets2panda/compiler/base/lreference.cpp | 23 +- ets2panda/compiler/core/ETSCompiler.cpp | 93 +++++- ets2panda/compiler/core/ETSCompiler.h | 2 + ets2panda/compiler/core/ETSGen.cpp | 69 ++++ ets2panda/compiler/core/ETSGen.h | 101 +++++- ets2panda/compiler/core/ETSemitter.cpp | 13 +- ets2panda/compiler/core/ETSfunction.cpp | 6 +- .../compiler/lowering/ets/dynamicImport.cpp | 250 ++++++++++++++ .../compiler/lowering/ets/dynamicImport.h | 35 ++ .../lowering/ets/gradualTypeNarrowing.cpp | 141 ++++++++ .../lowering/ets/gradualTypeNarrowing.h | 43 +++ .../ets/interfaceObjectLiteralLowering.cpp | 10 +- .../compiler/lowering/ets/lambdaLowering.cpp | 9 +- .../lowering/ets/localClassLowering.cpp | 2 +- .../lowering/ets/objectIndexAccess.cpp | 24 +- .../lowering/ets/objectLiteralLowering.cpp | 6 +- .../ets/optionalArgumentsLowering.cpp | 13 +- .../lowering/ets/restArgsLowering.cpp | 4 +- .../compiler/lowering/ets/unboxLowering.cpp | 34 +- ets2panda/compiler/lowering/phase.cpp | 4 + ets2panda/compiler/lowering/util.cpp | 9 +- ets2panda/compiler/scripts/signatures.yaml | 2 + ets2panda/declgen_ets2ts/declgenEts2Ts.cpp | 17 +- ets2panda/ir/base/classDefinition.h | 11 + ets2panda/ir/ets/etsTypeReference.cpp | 21 +- ets2panda/ir/ets/etsTypeReferencePart.cpp | 20 +- ets2panda/ir/ets/etsTypeReferencePart.h | 1 + ets2panda/ir/expressions/callExpression.cpp | 6 + ets2panda/ir/expressions/callExpression.h | 2 + ets2panda/ir/expressions/memberExpression.cpp | 32 ++ ets2panda/ir/expressions/memberExpression.h | 1 + ets2panda/ir/ts/tsTypeReference.cpp | 17 +- ets2panda/lsp/src/isolated_declaration.cpp | 2 +- ets2panda/parser/ETSparser.cpp | 1 + ets2panda/parser/program/program.h | 20 ++ ets2panda/test/unit/CMakeLists.txt | 1 + .../test/unit/any_ins_test/CMakeLists.txt | 17 + .../test/unit/any_ins_test/any_ins_test.cpp | 312 ++++++++++++++++++ ets2panda/test/utils/asm_test.cpp | 2 +- ets2panda/test/utils/checker_test.h | 95 ++++++ ets2panda/util/diagnostic/semantic.yaml | 6 +- ets2panda/util/importPathManager.cpp | 2 +- ets2panda/util/importPathManager.h | 2 + ets2panda/varbinder/variableFlags.h | 1 + 79 files changed, 2014 insertions(+), 298 deletions(-) create mode 100644 ets2panda/checker/types/gradualType.cpp create mode 100644 ets2panda/checker/types/gradualType.h create mode 100644 ets2panda/compiler/lowering/ets/dynamicImport.cpp create mode 100644 ets2panda/compiler/lowering/ets/dynamicImport.h create mode 100644 ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp create mode 100644 ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h create mode 100644 ets2panda/test/unit/any_ins_test/CMakeLists.txt create mode 100644 ets2panda/test/unit/any_ins_test/any_ins_test.cpp diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 596f03163c..0b2aea75e8 100644 --- a/ets2panda/BUILD.gn +++ b/ets2panda/BUILD.gn @@ -124,6 +124,7 @@ libes2panda_sources = [ "checker/types/ets/etsAnyType.cpp", "checker/types/ets/wildcardType.cpp", "checker/types/globalTypesHolder.cpp", + "checker/types/gradualType.cpp", "checker/types/signature.cpp", "checker/types/ts/anyType.cpp", "checker/types/ts/arrayType.cpp", @@ -218,11 +219,13 @@ libes2panda_sources = [ "compiler/lowering/ets/expressionLambdaLowering.cpp", "compiler/lowering/ets/extensionAccessorLowering.cpp", "compiler/lowering/ets/genericBridgesLowering.cpp", + "compiler/lowering/ets/gradualTypeNarrowing.cpp", "compiler/lowering/ets/insertOptionalParametersAnnotation.cpp", "compiler/lowering/ets/interfaceObjectLiteralLowering.cpp", "compiler/lowering/ets/interfacePropertyDeclarations.cpp", "compiler/lowering/ets/lateInitialization.cpp", "compiler/lowering/ets/lambdaLowering.cpp", + "compiler/lowering/ets/dynamicImport.cpp", "compiler/lowering/ets/localClassLowering.cpp", "compiler/lowering/ets/objectIndexAccess.cpp", "compiler/lowering/ets/objectIterator.cpp", diff --git a/ets2panda/CMakeLists.txt b/ets2panda/CMakeLists.txt index d40f60c4f4..13535b5474 100644 --- a/ets2panda/CMakeLists.txt +++ b/ets2panda/CMakeLists.txt @@ -276,6 +276,7 @@ set(ES2PANDA_LIB_SRC compiler/lowering/ets/expressionLambdaLowering.cpp compiler/lowering/ets/extensionAccessorLowering.cpp compiler/lowering/ets/genericBridgesLowering.cpp + compiler/lowering/ets/gradualTypeNarrowing.cpp compiler/lowering/ets/arrayLiteralLowering.cpp compiler/lowering/ets/boxingForLocals.cpp compiler/lowering/ets/capturedVariables.cpp @@ -288,6 +289,7 @@ set(ES2PANDA_LIB_SRC compiler/lowering/ets/exportAnonymousConst.cpp compiler/lowering/ets/lateInitialization.cpp compiler/lowering/ets/lambdaLowering.cpp + compiler/lowering/ets/dynamicImport.cpp compiler/lowering/ets/restTupleLowering.cpp compiler/lowering/ets/spreadLowering.cpp compiler/lowering/ets/localClassLowering.cpp @@ -574,6 +576,7 @@ set(ES2PANDA_LIB_SRC checker/types/type.cpp checker/types/typeRelation.cpp checker/types/globalTypesHolder.cpp + checker/types/gradualType.cpp checker/types/ets/byteType.cpp checker/types/ets/charType.cpp checker/types/ets/doubleType.cpp diff --git a/ets2panda/ast_verifier/helpers.cpp b/ets2panda/ast_verifier/helpers.cpp index d8a9998848..7fa918d29f 100644 --- a/ets2panda/ast_verifier/helpers.cpp +++ b/ets2panda/ast_verifier/helpers.cpp @@ -19,6 +19,7 @@ #include "checker/types/type.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsUnionType.h" +#include "checker/types/gradualType.h" #include "ir/statements/blockStatement.h" #include "ir/ets/etsModule.h" #include "parser/program/program.h" @@ -50,17 +51,17 @@ bool IsBooleanType(const ir::AstNode *ast) } auto typedAst = static_cast(ast); - + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); if (typedAst->TsType() == nullptr) { return false; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BOOLEAN_LIKE); + return type->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) || type->HasTypeFlag(checker::TypeFlag::BOOLEAN_LIKE); } bool IsValidTypeForBinaryOp(const ir::AstNode *ast, bool isBitwise) @@ -74,6 +75,8 @@ bool IsValidTypeForBinaryOp(const ir::AstNode *ast, bool isBitwise) } auto typedAst = static_cast(ast); + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); if (typedAst->TsType() == nullptr) { return false; @@ -83,20 +86,19 @@ bool IsValidTypeForBinaryOp(const ir::AstNode *ast, bool isBitwise) return isBitwise; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) && - typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BIGINT)) { + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) && + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BIGINT)) { return true; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE) && - !typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE) && + !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL); + return type->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || + type->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) || type->HasTypeFlag(checker::TypeFlag::BIGINT) || + type->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL); } bool IsStringType(const ir::AstNode *ast) @@ -110,17 +112,19 @@ bool IsStringType(const ir::AstNode *ast) } auto typedAst = static_cast(ast); + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); - if (typedAst->TsType() == nullptr) { + if (type == nullptr) { return false; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::STRING) || - typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_STRING); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::STRING) || + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_STRING); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::STRING_LIKE); + return type->HasTypeFlag(checker::TypeFlag::STRING_LIKE); } bool IsVisibleInternalNode(const ir::AstNode *ast, const ir::AstNode *objTypeDeclNode) @@ -154,7 +158,8 @@ const checker::Type *GetClassDefinitionType(const ir::AstNode *ast) return nullptr; } auto *classDefinition = tmpNode->AsClassDefinition(); - return classDefinition->TsType(); + return classDefinition->TsType()->IsGradualType() ? classDefinition->TsType()->AsGradualType()->GetBaseType() + : classDefinition->TsType(); } const checker::Type *GetTSInterfaceDeclarationType(const ir::AstNode *ast) @@ -167,7 +172,9 @@ const checker::Type *GetTSInterfaceDeclarationType(const ir::AstNode *ast) return nullptr; } auto *tsInterfaceDeclaration = tmpNode->AsTSInterfaceDeclaration(); - return tsInterfaceDeclaration->TsType(); + return tsInterfaceDeclaration->TsType()->IsGradualType() + ? tsInterfaceDeclaration->TsType()->AsGradualType()->GetBaseType() + : tsInterfaceDeclaration->TsType(); } bool ValidateMethodAccessForClass(const ir::AstNode *ast, const ir::AstNode *ownerSignDeclNode, @@ -266,10 +273,12 @@ bool ValidateVariableAccess(const varbinder::LocalVariable *propVar, const ir::M if (auto objType = const_cast(ast)->Object()->TsType(); objType->IsETSUnionType()) { bool res = true; for (auto type : objType->AsETSUnionType()->ConstituentTypes()) { - const_cast(ast)->SetObjectType(type->AsETSObjectType()); + const_cast(ast)->SetObjectType( + type->MaybeBaseTypeOfGradualType()->AsETSObjectType()); // Just to skip enclosing if clause checking whether object tsType is ETSUnionType in subsequent recursive // call - const_cast(ast)->Object()->SetTsType(type->AsETSObjectType()); + const_cast(ast)->Object()->SetTsType( + type->MaybeBaseTypeOfGradualType()->AsETSObjectType()); } const_cast(ast)->SetObjectType(ast->ObjType()); const_cast(ast)->Object()->SetTsType(objType); @@ -323,8 +332,8 @@ bool ValidateMethodAccess(const ir::MemberExpression *memberExpression, const ir } if (memberObjType->HasObjectFlag(checker::ETSObjectFlags::RESOLVED_SUPER) && memberObjType->SuperType() != nullptr && - memberObjType->SuperType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE | - checker::ETSObjectFlags::GLOBAL)) { + memberObjType->SuperType()->MaybeBaseTypeOfGradualType()->AsETSObjectType()->HasObjectFlag( + checker::ETSObjectFlags::BUILTIN_TYPE | checker::ETSObjectFlags::GLOBAL)) { return true; } const auto *memberObjTypeDeclNode = memberObjType->GetDeclNode(); diff --git a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp index bf7351956a..6d6ff35487 100644 --- a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp +++ b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp @@ -78,13 +78,6 @@ bool ImportExportAccessValid::InvariantImportExportMethod(const std::unordered_s const ir::AstNode *callExpr, util::StringView name) { auto *signature = callExpr->AsCallExpression()->Signature(); - if (signature == nullptr || signature->Owner() == nullptr) { - // NOTE(vpukhov): Add a synthetic owner for dynamic signatures - ES2PANDA_ASSERT( - callExpr->AsCallExpression()->Callee()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG)); - return true; - } - if (signature != nullptr && varCallee->Declaration() != nullptr && varCallee->Declaration()->Node() != nullptr && !IsContainedIn(varCallee->Declaration()->Node(), signature->Owner()->GetDeclNode()) && varCallee->Declaration()->Node() != signature->Owner()->GetDeclNode()) { diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 3aa4109c2e..1a4e21f9c0 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -19,10 +19,13 @@ #include "generated/diagnostic.h" #include "checker/types/globalTypesHolder.h" #include "checker/types/ets/etsTupleType.h" +#include "checker/types/gradualType.h" #include "evaluate/scopedDebugInfoPlugin.h" #include "types/signature.h" #include "compiler/lowering/ets/setJumpTarget.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" +#include "types/type.h" +#include "types/typeFlag.h" #include "util/es2pandaMacros.h" #include @@ -171,7 +174,7 @@ static void HandleNativeAndAsyncMethods(ETSChecker *checker, ir::MethodDefinitio if (util::Helpers::IsAsyncMethod(node)) { if (scriptFunc->ReturnTypeAnnotation() != nullptr) { - auto *asyncFuncReturnType = scriptFunc->Signature()->ReturnType(); + auto *asyncFuncReturnType = scriptFunc->Signature()->ReturnType()->MaybeBaseTypeOfGradualType(); if (!asyncFuncReturnType->IsETSObjectType() || asyncFuncReturnType->AsETSObjectType()->GetOriginalBaseType() != checker->GlobalBuiltinPromiseType()) { @@ -318,7 +321,8 @@ checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const } ETSChecker *checker = GetETSChecker(); - Type *exprType = expr->AsSpreadElement()->Argument()->Check(checker); + auto type = expr->AsSpreadElement()->Argument()->Check(checker); + Type *exprType = type->MaybeBaseTypeOfGradualType(); if (exprType->IsETSResizableArrayType()) { return expr->SetTsType(exprType->AsETSObjectType()->TypeArguments().front()); @@ -332,7 +336,7 @@ checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const return checker->InvalidateType(expr); } - checker::Type *const elementType = exprType->IsETSTupleType() ? exprType : checker->GetElementTypeOfArray(exprType); + checker::Type *const elementType = exprType->IsETSTupleType() ? type : checker->GetElementTypeOfArray(exprType); return expr->SetTsType(elementType); } @@ -389,7 +393,8 @@ checker::Type *ETSAnalyzer::Check(ir::ETSFunctionType *node) const signature->SetOwner(checker->Context().ContainingClass()); - return node->SetTsType(checker->CreateETSArrowType(signature)); + auto arrowType = checker->CreateETSArrowType(signature); + return node->SetTsType(arrowType); } template >> @@ -398,7 +403,7 @@ static bool CheckArrayElementType(ETSChecker *checker, T *newArrayInstanceExpr) ES2PANDA_ASSERT(checker != nullptr); ES2PANDA_ASSERT(newArrayInstanceExpr != nullptr); - checker::Type *elementType = newArrayInstanceExpr->TypeReference()->GetType(checker); + checker::Type *elementType = newArrayInstanceExpr->TypeReference()->GetType(checker)->MaybeBaseTypeOfGradualType(); if (elementType->IsETSPrimitiveType()) { return true; } @@ -467,7 +472,8 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewArrayInstanceExpression *expr) const static checker::Type *CheckInstantiatedNewType(ETSChecker *checker, ir::ETSNewClassInstanceExpression *expr) { - checker::Type *calleeType = expr->GetTypeRef()->Check(checker); + checker::Type *res = expr->GetTypeRef()->Check(checker); + auto calleeType = res->MaybeBaseTypeOfGradualType(); if (calleeType->IsTypeError()) { return checker->InvalidateType(expr->GetTypeRef()); } @@ -498,7 +504,7 @@ static checker::Type *CheckInstantiatedNewType(ETSChecker *checker, ir::ETSNewCl return checker->GlobalTypeError(); } - return calleeType; + return res; } checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const @@ -511,8 +517,8 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const if (calleeType->IsTypeError()) { return checker->InvalidateType(expr); } - auto *calleeObj = calleeType->AsETSObjectType(); - expr->SetTsType(calleeObj); + auto *calleeObj = calleeType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); + expr->SetTsType(calleeType); auto *signature = checker->ResolveConstructExpression(calleeObj, expr->GetArguments(), expr->Start()); @@ -618,11 +624,12 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNonNullishTypeNode *node) const return node->TsType(); } ETSChecker *checker = GetETSChecker(); - checker::Type *originalType = node->GetTypeNode()->Check(checker); + auto type = node->GetTypeNode()->Check(checker); + checker::Type *originalType = type->MaybeBaseTypeOfGradualType(); if (!originalType->IsETSTypeParameter()) { checker->LogError(diagnostic::ILLEGAL_NON_NULLISH_TYPE, {}, node->GetTypeNode()->Start()); } - return node->SetTsType(checker->GetNonNullishType(originalType)); + return node->SetTsType(checker->GetNonNullishType(type)); } checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSNullType *node) const @@ -670,7 +677,7 @@ static void AddSpreadElementTypes(ETSChecker *checker, ir::SpreadElement *const return; } - Type *const spreadArgumentType = element->Argument()->TsType(); + Type *const spreadArgumentType = element->Argument()->TsType()->MaybeBaseTypeOfGradualType(); if (spreadArgumentType->IsETSTupleType()) { for (Type *type : spreadArgumentType->AsETSTupleType()->GetTupleTypesList()) { @@ -997,7 +1004,7 @@ checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const checker->Context().SetContainingSignature(nullptr); if (expr->Function()->IsAsyncFunc()) { - auto *retType = signature->ReturnType(); + auto *retType = signature->ReturnType()->MaybeBaseTypeOfGradualType(); if (!retType->IsETSObjectType() || retType->AsETSObjectType()->GetOriginalBaseType() != checker->GlobalBuiltinPromiseType()) { checker->LogError(diagnostic::ASYNC_DOESNT_PROMISE, {}, expr->Function()->Start()); @@ -1078,7 +1085,8 @@ checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const checker->WarnForEndlessLoopInGetterSetter(expr->Left()->AsMemberExpression()); } - const auto leftType = expr->Left()->Check(checker); + const auto res = expr->Left()->Check(checker); + const auto leftType = res->MaybeBaseTypeOfGradualType(); if (IsInvalidArrayMemberAssignment(expr, checker)) { expr->SetTsType(checker->GlobalTypeError()); @@ -1105,13 +1113,14 @@ checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const auto [rightType, relationNode] = CheckAssignmentExprOperatorType(expr, leftType); if (rightType->IsTypeError()) { - return expr->SetTsType(leftType); + return expr->SetTsType(res); } CastPossibleTupleOnRHS(checker, expr); checker::Type *smartType = rightType; - if (!leftType->IsTypeError()) { + if (!leftType->IsTypeError() && !(leftType->IsETSObjectType() && + leftType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::LAZY_IMPORT_OBJECT))) { if (const auto ctx = checker::AssignmentContext(checker->Relation(), relationNode, rightType, leftType, expr->Right()->Start(), {{diagnostic::INVALID_ASSIGNMNENT, {rightType, leftType}}}); @@ -1136,6 +1145,13 @@ static checker::Type *HandleSubstitution(ETSChecker *checker, ir::AssignmentExpr checker->TryInferTypeForLambdaTypeAlias(expr->Right()->AsArrowFunctionExpression(), preferredType->AsETSFunctionType()); } + } else if (expr->Right()->IsObjectExpression()) { + if (leftType->IsETSObjectType() && leftType->IsGradualType() && + (leftType->HasTypeFlag(TypeFlag::READONLY) || + leftType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::REQUIRED))) { + checker->LogError(diagnostic::DYMANIC_INIT_WITH_OBJEXPR, {leftType}, expr->Right()->Start()); + } + expr->Right()->AsObjectExpression()->SetPreferredType(leftType); } else { expr->Right()->SetPreferredType(leftType); } @@ -1183,6 +1199,9 @@ std::tuple ETSAnalyzer::CheckAssignmentExprOperatorTyp static bool IsPromiseType(checker::Type *type, ETSChecker *checker) { + if (type->IsGradualType()) { + return IsPromiseType(type->AsGradualType()->GetBaseType(), checker); + } return type->IsETSObjectType() && type->AsETSObjectType()->GetOriginalBaseType() == checker->GlobalBuiltinPromiseType(); } @@ -1198,7 +1217,8 @@ checker::Type *ETSAnalyzer::Check(ir::AwaitExpression *expr) const ArenaVector awaitedTypes(checker->ProgramAllocator()->Adapter()); if (argType->IsETSUnionType()) { - for (Type *type : argType->AsETSUnionType()->ConstituentTypes()) { + for (Type *ctype : argType->AsETSUnionType()->ConstituentTypes()) { + auto type = ctype->MaybeBaseTypeOfGradualType(); if (!IsPromiseType(type, checker)) { return checker->TypeError(expr, diagnostic::AWAITED_NOT_PROMISE, expr->Argument()->Start()); } @@ -1221,6 +1241,9 @@ checker::Type *ETSAnalyzer::Check(ir::AwaitExpression *expr) const checker::Type *ETSAnalyzer::UnwrapPromiseType(checker::Type *type) const { + if (type->IsGradualType()) { + return UnwrapPromiseType(type->AsGradualType()->GetBaseType()); + } ETSChecker *checker = GetETSChecker(); checker::Type *promiseType = checker->GlobalBuiltinPromiseType(); while (type->IsETSObjectType() && type->AsETSObjectType()->GetOriginalBaseType() == promiseType) { @@ -1358,7 +1381,7 @@ checker::Signature *ETSAnalyzer::ResolveSignature(ETSChecker *checker, ir::CallE static ETSObjectType *GetCallExpressionCalleeObject(ETSChecker *checker, ir::CallExpression *expr, Type *calleeType) { if (expr->IsETSConstructorCall()) { - return calleeType->AsETSObjectType(); + return calleeType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); } auto callee = expr->Callee(); if (callee->IsMemberExpression()) { @@ -1422,9 +1445,14 @@ static void CheckCallee(ETSChecker *checker, ir::CallExpression *expr) { checker->CheckNonNullish(expr->Callee()); if (expr->Callee()->IsMemberExpression() && expr->Callee()->AsMemberExpression()->Object() != nullptr && - expr->Callee()->AsMemberExpression()->Object()->TsType()->IsETSObjectType() && - expr->Callee()->AsMemberExpression()->Object()->TsType()->AsETSObjectType()->HasObjectFlag( - ETSObjectFlags::READONLY)) { + expr->Callee()->AsMemberExpression()->Object()->TsType()->MaybeBaseTypeOfGradualType()->IsETSObjectType() && + expr->Callee() + ->AsMemberExpression() + ->Object() + ->TsType() + ->MaybeBaseTypeOfGradualType() + ->AsETSObjectType() + ->HasObjectFlag(ETSObjectFlags::READONLY)) { checker->LogError(diagnostic::READONLY_CALL, {}, expr->Start()); expr->SetTsType(checker->GlobalTypeError()); } @@ -1635,7 +1663,9 @@ checker::Type *ETSAnalyzer::Check(ir::ConditionalExpression *expr) const static Type *TransformTypeForMethodReference(ETSChecker *checker, ir::Expression *const use, Type *type) { ES2PANDA_ASSERT(use->IsIdentifier() || use->IsMemberExpression()); - if (!type->IsETSMethodType()) { + if (!type->IsETSMethodType() || + (use->IsMemberExpression() && use->AsMemberExpression()->PropVar() != nullptr && + use->AsMemberExpression()->PropVar()->HasFlag(varbinder::VariableFlags::DYNAMIC))) { return type; } auto const getUseSite = [use]() { @@ -1671,7 +1701,8 @@ static Type *TransformTypeForMethodReference(ETSChecker *checker, ir::Expression checker->LogError(diagnostic::OVERLOADED_METHOD_AS_VALUE, getUseSite()); return checker->GlobalTypeError(); } - return functionType->MethodToArrow(checker); + checker::Type *funcType = functionType->MethodToArrow(checker); + return funcType; } checker::Type *ETSAnalyzer::Check(ir::Identifier *expr) const @@ -1742,6 +1773,10 @@ checker::Type *ETSAnalyzer::ResolveMemberExpressionByBaseType(ETSChecker *checke return checker->InvalidateType(expr); } + if (baseType->IsGradualType()) { + return ResolveMemberExpressionByBaseType(checker, baseType->AsGradualType()->GetBaseType(), expr); + } + if (baseType->IsETSArrayType()) { if (expr->Property()->AsIdentifier()->Name().Is("length")) { return expr->AdjustType(checker, checker->GlobalIntBuiltinType()); @@ -1760,8 +1795,9 @@ checker::Type *ETSAnalyzer::ResolveMemberExpressionByBaseType(ETSChecker *checke if (baseType->IsETSObjectType()) { checker->ETSObjectTypeDeclNode(checker, baseType->AsETSObjectType()); - return expr->SetTsType(TransformTypeForMethodReference( - checker, expr, expr->SetAndAdjustType(checker, baseType->AsETSObjectType()))); + auto res = TransformTypeForMethodReference(checker, expr, + expr->SetAndAdjustType(checker, baseType->AsETSObjectType())); + return expr->SetTsType(res); } if (baseType->IsETSUnionType()) { @@ -1780,8 +1816,7 @@ checker::Type *ETSAnalyzer::ResolveMemberExpressionByBaseType(ETSChecker *checke "toFloat", "toDouble", }}; - auto method = expr->Property()->AsIdentifier()->Name().Utf8(); - auto res = std::find(castMethods.begin(), castMethods.end(), method); + auto res = std::find(castMethods.begin(), castMethods.end(), expr->Property()->AsIdentifier()->Name().Utf8()); if (res != castMethods.end()) { auto type = checker->MaybeBoxType(baseType); expr->SetAstNodeFlags(ir::AstNodeFlags::TMP_CONVERT_PRIMITIVE_CAST_METHOD_CALL); @@ -1851,7 +1886,7 @@ checker::Type *ETSAnalyzer::CheckDynamic(ir::ObjectExpression *expr) const static bool ValidatePreferredType(ETSChecker *checker, ir::ObjectExpression *expr) { - auto preferredType = expr->PreferredType(); + auto preferredType = expr->PreferredType()->MaybeBaseTypeOfGradualType(); if (preferredType == nullptr) { checker->LogError(diagnostic::CLASS_COMPOSITE_UNKNOWN_TYPE, {}, expr->Start()); return false; @@ -2166,8 +2201,9 @@ checker::ETSObjectType *ResolveUnionObjectTypeForObjectLiteral(ETSChecker *check std::vector candidateObjectTypes; // Phase 1: Gather all ETSObjectTypes from the union for (auto *constituentType : unionType->ConstituentTypes()) { - if (constituentType->IsETSObjectType()) { - candidateObjectTypes.push_back(constituentType->AsETSObjectType()); + auto type = constituentType->MaybeBaseTypeOfGradualType(); + if (type->IsETSObjectType()) { + candidateObjectTypes.push_back(type->AsETSObjectType()); } } @@ -3010,7 +3046,7 @@ checker::Type *ETSAnalyzer::Check(ir::ForOfStatement *const st) const // NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point. auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st, std::nullopt); - checker::Type *const exprType = st->Right()->Check(checker); + checker::Type *const exprType = st->Right()->Check(checker)->MaybeBaseTypeOfGradualType(); if (exprType == nullptr) { checker->LogError(diagnostic::FOROF_CANT_INFER_SOURCE, {}, st->Right()->Start()); return checker->GlobalTypeError(); @@ -3176,15 +3212,16 @@ bool ETSAnalyzer::CheckInferredFunctionReturnType(ir::ReturnStatement *st, ir::S // Case when function's return type is defined explicitly: if (st->argument_ == nullptr) { - if (!funcReturnType->IsETSVoidType() && funcReturnType != checker->GlobalVoidType() && - !funcReturnType->IsETSAsyncFuncReturnType()) { + if (!funcReturnType->MaybeBaseTypeOfGradualType()->IsETSVoidType() && + funcReturnType != checker->GlobalVoidType() && + !funcReturnType->MaybeBaseTypeOfGradualType()->IsETSAsyncFuncReturnType()) { checker->LogError(diagnostic::RETURN_WITHOUT_VALUE, {}, st->Start()); return false; } funcReturnType = checker->GlobalVoidType(); } else { const auto name = containingFunc->Scope()->InternalName().Mutf8(); - if (!CheckArgumentVoidType(funcReturnType, checker, name, st)) { + if (!CheckArgumentVoidType(funcReturnType->MaybeBaseTypeOfGradualType(), checker, name, st)) { return false; } @@ -3196,7 +3233,7 @@ bool ETSAnalyzer::CheckInferredFunctionReturnType(ir::ReturnStatement *st, ir::S } checker::Type *argumentType = st->argument_->Check(checker); - return CheckReturnType(checker, funcReturnType, argumentType, st->argument_, containingFunc); + return CheckReturnType(checker, funcReturnType->MaybeBaseTypeOfGradualType(), argumentType, st->argument_, containingFunc); } return true; } @@ -3549,13 +3586,14 @@ checker::Type *ETSAnalyzer::Check(ir::TSInterfaceDeclaration *st) const return st->SetTsType(stmtType); } - auto *interfaceType = stmtType->AsETSObjectType(); + auto *interfaceType = stmtType->IsGradualType() ? stmtType->AsGradualType()->GetBaseType()->AsETSObjectType() + : stmtType->AsETSObjectType(); checker->CheckInterfaceAnnotations(st); interfaceType->SetSuperType(checker->GlobalETSObjectType()); checker->CheckInvokeMethodsLegitimacy(interfaceType); - st->SetTsType(interfaceType); + st->SetTsType(stmtType); checker::ScopeContext scopeCtx(checker, st->Scope()); auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::IN_INTERFACE, interfaceType); @@ -3597,7 +3635,7 @@ checker::Type *ETSAnalyzer::Check(ir::TSNonNullExpression *expr) const checker::Type *ETSAnalyzer::Check(ir::TSQualifiedName *expr) const { ETSChecker *checker = GetETSChecker(); - checker::Type *baseType = expr->Left()->Check(checker); + checker::Type *baseType = expr->Left()->Check(checker)->MaybeBaseTypeOfGradualType(); if (baseType->IsETSObjectType()) { // clang-format off auto searchName = expr->Right()->Name(); @@ -3607,7 +3645,7 @@ checker::Type *ETSAnalyzer::Check(ir::TSQualifiedName *expr) const searchName = expr->Right()->Name(); } varbinder::Variable *prop = - baseType->AsETSObjectType()->GetProperty(searchName, checker::PropertySearchFlags::SEARCH_DECL); + baseType->AsETSObjectType()->GetProperty(searchName, PropertySearchFlags::SEARCH_DECL); // NOTE(dslynko): in debugger evaluation mode must lazily generate module's properties here. if (prop == nullptr) { checker->LogError(diagnostic::NONEXISTENT_TYPE, {expr->Right()->Name()}, expr->Right()->Start()); diff --git a/ets2panda/checker/ETSAnalyzerHelpers.cpp b/ets2panda/checker/ETSAnalyzerHelpers.cpp index 2a5443ae23..590933c12c 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.cpp +++ b/ets2panda/checker/ETSAnalyzerHelpers.cpp @@ -306,7 +306,7 @@ static bool HasIteratorInterface(ETSObjectType const *const objectType) void CheckIteratorMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc, const lexer::SourcePosition &position, const std::string &methodName) { - const auto *returnType = scriptFunc->Signature()->ReturnType(); + const auto *returnType = scriptFunc->Signature()->ReturnType()->MaybeBaseTypeOfGradualType(); if (returnType == nullptr) { checker->LogError(diagnostic::MISSING_RETURN_TYPE_2, {util::StringView(methodName)}, position); @@ -522,7 +522,7 @@ void SetTsTypeForUnaryExpression(ETSChecker *checker, ir::UnaryExpression *expr, switch (expr->OperatorType()) { case lexer::TokenType::PUNCTUATOR_MINUS: case lexer::TokenType::PUNCTUATOR_PLUS: { - if (operandType == nullptr || !operandType->IsETSObjectType() || + if (operandType == nullptr || !operandType->MaybeBaseTypeOfGradualType()->IsETSObjectType() || !operandType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::CONVERTIBLE_TO_NUMERIC)) { checker->LogError(diagnostic::OPERAND_NOT_NUMERIC, {}, expr->Argument()->Start()); expr->SetTsType(checker->GlobalTypeError()); @@ -533,7 +533,7 @@ void SetTsTypeForUnaryExpression(ETSChecker *checker, ir::UnaryExpression *expr, break; } case lexer::TokenType::PUNCTUATOR_TILDE: { - if (operandType == nullptr || !operandType->IsETSObjectType() || + if (operandType == nullptr || !operandType->MaybeBaseTypeOfGradualType()->IsETSObjectType() || !operandType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::CONVERTIBLE_TO_NUMERIC)) { expr->SetTsType(checker->GlobalTypeError()); break; diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index ce5dd58750..7bc3073fa9 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -25,6 +25,8 @@ #include "ir/expressions/callExpression.h" #include "ir/ts/tsInterfaceDeclaration.h" #include "ir/statements/blockStatement.h" +#include "types/type.h" +#include "utils/arena_containers.h" #include "varbinder/ETSBinder.h" #include "parser/program/program.h" #include "checker/ets/aliveAnalyzer.h" @@ -34,6 +36,7 @@ #include "ir/base/scriptFunction.h" #include "util/helpers.h" #include "evaluate/scopedDebugInfoPlugin.h" +#include "checker/types/ets/etsTupleType.h" namespace ark::es2panda::checker { @@ -309,6 +312,14 @@ void ETSChecker::InitializeBuiltin(varbinder::Variable *var, const util::StringV GetGlobalTypesHolder()->InitializeBuiltin(name, type); } +void ETSChecker::InitialGradualTypes() +{ + ArenaUnorderedMap jsMap {ProgramAllocator()->Adapter()}; + gradualTypes_.insert({Language(Language::Id::JS), jsMap}); + ArenaUnorderedMap etsMap {ProgramAllocator()->Adapter()}; + gradualTypes_.insert({Language(Language::Id::ETS), etsMap}); +} + bool ETSChecker::StartChecker(varbinder::VarBinder *varbinder, const util::Options &options) { if (options.IsParseOnly()) { @@ -316,6 +327,7 @@ bool ETSChecker::StartChecker(varbinder::VarBinder *varbinder, const util::Optio } auto *etsBinder = varbinder->AsETSBinder(); + InitialGradualTypes(); InitializeBuiltins(etsBinder); bool isEvalMode = (debugInfoPlugin_ != nullptr); @@ -418,16 +430,14 @@ bool ETSChecker::IsClassStaticMethod(checker::ETSObjectType *objType, checker::S [[nodiscard]] TypeFlag ETSChecker::TypeKind(const Type *const type) noexcept { - // These types were not present in the ETS_TYPE list. Some of them are omited intentionally, other are just bugs + // These types were not present in the ETS_TYPE list. Some of them are omitted intentionally, other are just bugs static constexpr auto TO_CLEAR = TypeFlag::CONSTANT | TypeFlag::GENERIC | TypeFlag::ETS_INT_ENUM | TypeFlag::ETS_STRING_ENUM | TypeFlag::READONLY | TypeFlag::BIGINT_LITERAL | TypeFlag::ETS_TYPE_ALIAS | TypeFlag::TYPE_ERROR; - // Bugs: these types do not appear as a valid TypeKind, as the TypeKind has more then one bit set - [[maybe_unused]] static constexpr auto NOT_A_TYPE_KIND = TypeFlag::ETS_DYNAMIC_FLAG; - auto res = static_cast(type->TypeFlags() & ~(TO_CLEAR)); - ES2PANDA_ASSERT_POS(res == TypeFlag::NONE || helpers::math::IsPowerOfTwo(res & ~(NOT_A_TYPE_KIND)), + ES2PANDA_ASSERT_POS(res == TypeFlag::NONE || + helpers::math::IsPowerOfTwo(static_cast>(res)), ark::es2panda::GetPositionForDiagnostic()); return res; } diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index b3ced7b7d3..45a8f4572d 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -26,6 +26,7 @@ #include "checker/types/ets/types.h" #include "checker/resolveResult.h" #include "ir/visitor/AstVisitor.h" +#include "types/type.h" #include "util/helpers.h" namespace ark::es2panda::varbinder { @@ -79,6 +80,7 @@ using ConstraintCheckRecord = std::tuple *, const Subs using MaybeDiagnosticInfo = std::optional>; using AstNodePtr = ir::AstNode *; +using TypePtr = Type *; class ETSChecker final : public Checker { public: @@ -87,6 +89,7 @@ public: // NOLINTNEXTLINE(readability-redundant-member-init) : Checker(allocator, diagnosticEngine, programAllocator), arrayTypes_(Allocator()->Adapter()), + gradualTypes_(Allocator()->Adapter()), pendingConstraintCheckRecords_(Allocator()->Adapter()), objectInstantiationMap_(Allocator()->Adapter()), invokeToArrowSignatures_(Allocator()->Adapter()), @@ -184,6 +187,7 @@ public: void InitializeBuiltins(varbinder::ETSBinder *varbinder); void InitializeBuiltin(varbinder::Variable *var, const util::StringView &name); + void InitialGradualTypes(); bool StartChecker([[maybe_unused]] varbinder::VarBinder *varbinder, const util::Options &options) override; Type *CheckTypeCached(ir::Expression *expr) override; void ResolveStructuredTypeMembers([[maybe_unused]] Type *type) override {}; @@ -218,7 +222,7 @@ public: bool ValidateTupleIndexFromEtsObject(const ETSTupleType *const tuple, ir::MemberExpression *expr); ETSObjectType *CheckThisOrSuperAccess(ir::Expression *node, ETSObjectType *classType, std::string_view msg); void CreateTypeForClassOrInterfaceTypeParameters(ETSObjectType *type); - ETSTypeParameter *SetUpParameterType(ir::TSTypeParameter *param); + Type *SetUpParameterType(ir::TSTypeParameter *param); void GetInterfacesOfClass(ETSObjectType *type, ArenaVector &interfaces); void CheckIfOverrideIsValidInInterface(ETSObjectType *classType, Signature *sig, Signature *sigFunc); void CheckFunctionRedeclarationInInterface(ETSObjectType *classType, ArenaVector &similarSignatures, @@ -311,6 +315,8 @@ public: ETSResizableArrayType *CreateETSMultiDimResizableArrayType(Type *element, size_t dimSize); ETSResizableArrayType *CreateETSResizableArrayType(Type *element); ETSArrayType *CreateETSArrayType(Type *elementType, bool isCachePolluting = false); + Type *GetGlobalGradualType(Type *baseTypeM, Language lang = Language(Language::Id::JS)) const; + Type *CreateGradualType(Type *baseType, const Language lang, bool recursive = true); Type *CreateETSUnionType(Span constituentTypes); template Type *CreateETSUnionType(Type *const (&arr)[N]) // NOLINT(modernize-avoid-c-arrays) @@ -328,6 +334,7 @@ public: ETSTypeAliasType *CreateETSTypeAliasType(util::StringView name, const ir::AstNode *declNode, bool isRecursive = false); ETSFunctionType *CreateETSArrowType(Signature *signature); + ETSAnyType *CreateETSAnyType(bool transformFromGradual); ETSFunctionType *CreateETSMethodType(util::StringView name, ArenaVector &&signatures); ETSExtensionFuncHelperType *CreateETSExtensionFuncHelperType(ETSFunctionType *classMethodType, ETSFunctionType *extensionFunctionType); @@ -562,6 +569,7 @@ public: const util::StringView &importPath); void SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjType, const util::StringView &importPath, ir::ETSImportDeclaration *importDecl = nullptr); + parser::Program *SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder, const util::StringView &importPath); void SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType); Type *GetReferencedTypeFromBase(Type *baseType, ir::Expression *name); Type *GetReferencedTypeBase(ir::Expression *name); @@ -1010,6 +1018,7 @@ private: void SetUpTypeParameterConstraint(ir::TSTypeParameter *param); ETSObjectType *UpdateGlobalType(ETSObjectType *objType, util::StringView name); + void WrapTypeNode(ir::AstNode *node); void CheckProgram(parser::Program *program, bool runAnalysis = false); void CheckWarnings(parser::Program *program, const util::Options &options); @@ -1053,6 +1062,7 @@ private: std::unordered_set &typeAliases); ArrayMap arrayTypes_; + ArenaUnorderedMap> gradualTypes_; ArenaVector pendingConstraintCheckRecords_; ObjectInstantiationMap objectInstantiationMap_; FunctionSignatureMap invokeToArrowSignatures_; diff --git a/ets2panda/checker/checker.cpp b/ets2panda/checker/checker.cpp index 39c4be1b59..ca737861c6 100644 --- a/ets2panda/checker/checker.cpp +++ b/ets2panda/checker/checker.cpp @@ -164,6 +164,11 @@ bool Checker::IsAnyError() return DiagnosticEngine().IsAnyError(); } +bool Checker::IsDeclForDynamicStaticInterop() const +{ + return Program()->IsDeclForDynamicStaticInterop(); +} + ScopeContext::ScopeContext(Checker *checker, varbinder::Scope *newScope) : checker_(checker), prevScope_(checker_->scope_), prevProgram_(checker_->Program()) { diff --git a/ets2panda/checker/checker.h b/ets2panda/checker/checker.h index c41af2dbd8..d6d6f8d31e 100644 --- a/ets2panda/checker/checker.h +++ b/ets2panda/checker/checker.h @@ -239,6 +239,8 @@ public: return programAllocator_ == nullptr ? allocator_ : programAllocator_; } + bool IsDeclForDynamicStaticInterop() const; + protected: parser::Program *Program() const; void SetProgram(parser::Program *program); diff --git a/ets2panda/checker/checkerContext.cpp b/ets2panda/checker/checkerContext.cpp index b8da863c9b..0a08103b12 100644 --- a/ets2panda/checker/checkerContext.cpp +++ b/ets2panda/checker/checkerContext.cpp @@ -359,7 +359,7 @@ void CheckerContext::CheckBinarySmartCastCondition(ir::BinaryExpression *const b // let x = new C<...> // if (x instanceof C && x.fld instanceof ... const auto variable = binaryExpression->Left()->AsIdentifier()->Variable(); - auto type = binaryExpression->Right()->TsType(); + auto type = binaryExpression->Right()->TsType()->MaybeBaseTypeOfGradualType(); auto smartIt = smartCasts_.find(variable); // NOTE(pantos) Handle union types e.g. C|C|... if (type->HasTypeFlag(TypeFlag::GENERIC) && smartIt != smartCasts_.end() && type->IsETSObjectType() && diff --git a/ets2panda/checker/ets/assignAnalyzer.cpp b/ets2panda/checker/ets/assignAnalyzer.cpp index 8b29b6e855..cd5c6132b7 100644 --- a/ets2panda/checker/ets/assignAnalyzer.cpp +++ b/ets2panda/checker/ets/assignAnalyzer.cpp @@ -57,6 +57,7 @@ #include "varbinder/scope.h" #include "varbinder/declaration.h" #include "checker/ETSchecker.h" +#include "checker/types/gradualType.h" #include "ir/base/catchClause.h" #include "parser/program/program.h" #include "checker/types/ts/objectType.h" @@ -1348,6 +1349,9 @@ const ir::AstNode *AssignAnalyzer::GetDeclaringNode(const ir::AstNode *node) static bool IsDefaultValueType(const Type *type, bool isNonReadonlyField) { + if (type->IsGradualType()) { + return IsDefaultValueType(type->AsGradualType()->GetBaseType(), isNonReadonlyField); + } if (type == nullptr) { return false; } diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index ea80a961c3..9a05c8c8a4 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -23,6 +23,7 @@ #include "checker/ets/typeRelationContext.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" #include "checker/types/ets/etsObjectType.h" +#include "checker/types/gradualType.h" #include "compiler/lowering/scopesInit/scopesInitPhase.h" #include "ir/base/catchClause.h" #include "ir/base/classDefinition.h" @@ -82,54 +83,59 @@ bool ETSChecker::IsCompatibleTypeArgument(ETSTypeParameter *typeParam, Type *typ bool ETSChecker::EnhanceSubstitutionForReadonly(const ArenaVector &typeParams, ETSReadonlyType *paramType, Type *argumentType, Substitution *substitution) { - return EnhanceSubstitutionForType(typeParams, paramType->GetUnderlying(), GetReadonlyType(argumentType), - substitution); + return EnhanceSubstitutionForType(typeParams, paramType->GetUnderlying()->MaybeBaseTypeOfGradualType(), + GetReadonlyType(argumentType), substitution); } /* A very rough and imprecise partial type inference */ bool ETSChecker::EnhanceSubstitutionForType(const ArenaVector &typeParams, Type *paramType, Type *argumentType, Substitution *substitution) { - if (argumentType->IsETSPrimitiveType()) { - argumentType = MaybeBoxInRelation(argumentType); + auto ptype = paramType->MaybeBaseTypeOfGradualType(); + auto argType = argumentType->MaybeBaseTypeOfGradualType(); + if (argType->IsETSPrimitiveType()) { + argType = MaybeBoxInRelation(argType); } - if (paramType->IsETSTypeParameter()) { - auto *const tparam = paramType->AsETSTypeParameter(); + if (ptype->IsETSTypeParameter()) { + auto *const tparam = ptype->AsETSTypeParameter(); auto *const originalTparam = tparam->GetOriginal(); - if (std::find(typeParams.begin(), typeParams.end(), originalTparam) != typeParams.end() && + if (std::find_if(typeParams.begin(), typeParams.end(), + [ptype](checker::Type *typeParam) { + return typeParam->MaybeBaseTypeOfGradualType() == ptype; + }) != typeParams.end() && substitution->count(originalTparam) == 0) { - if (!IsReferenceType(argumentType)) { - LogError(diagnostic::INFERENCE_TYPE_INCOMPAT, {tparam, argumentType}, tparam->GetDeclNode()->Start()); + if (!IsReferenceType(argType)) { + LogError(diagnostic::INFERENCE_TYPE_INCOMPAT, {tparam, argType}, tparam->GetDeclNode()->Start()); return false; } // #23068 substitution happens before the constraint check, should be restored - EmplaceSubstituted(substitution, originalTparam, argumentType); - return IsCompatibleTypeArgument(tparam, argumentType, substitution); + EmplaceSubstituted(substitution, originalTparam, argType); + return IsCompatibleTypeArgument(tparam, argType, substitution); } } - if (paramType->IsETSFunctionType()) { - return EnhanceSubstitutionForFunction(typeParams, paramType->AsETSFunctionType(), argumentType, substitution); + if (ptype->IsETSFunctionType()) { + return EnhanceSubstitutionForFunction(typeParams, ptype->AsETSFunctionType(), argType, substitution); } - if (paramType->IsETSReadonlyType()) { - return EnhanceSubstitutionForReadonly(typeParams, paramType->AsETSReadonlyType(), argumentType, substitution); + if (ptype->IsETSReadonlyType()) { + return EnhanceSubstitutionForReadonly(typeParams, ptype->AsETSReadonlyType(), argType, substitution); } - if (paramType->IsETSUnionType()) { - return EnhanceSubstitutionForUnion(typeParams, paramType->AsETSUnionType(), argumentType, substitution); + if (ptype->IsETSUnionType()) { + return EnhanceSubstitutionForUnion(typeParams, ptype->AsETSUnionType(), argType, substitution); } - if (paramType->IsETSResizableArrayType()) { - return EnhanceSubstitutionForResizableArray(typeParams, paramType->AsETSResizableArrayType(), argumentType, + if (ptype->IsETSResizableArrayType()) { + return EnhanceSubstitutionForResizableArray(typeParams, ptype->AsETSResizableArrayType(), argType, substitution); } - if (paramType->IsETSObjectType()) { - return EnhanceSubstitutionForObject(typeParams, paramType->AsETSObjectType(), argumentType, substitution); + if (ptype->IsETSObjectType()) { + return EnhanceSubstitutionForObject(typeParams, ptype->AsETSObjectType(), argType, substitution); } - if (paramType->IsETSArrayType()) { - return EnhanceSubstitutionForArray(typeParams, paramType->AsETSArrayType(), argumentType, substitution); + if (ptype->IsETSArrayType()) { + return EnhanceSubstitutionForArray(typeParams, ptype->AsETSArrayType(), argType, substitution); } - if (paramType->IsETSFunctionType()) { - return EnhanceSubstitutionForFunction(typeParams, paramType->AsETSFunctionType(), argumentType, substitution); + if (ptype->IsETSFunctionType()) { + return EnhanceSubstitutionForFunction(typeParams, ptype->AsETSFunctionType(), argType, substitution); } return true; @@ -152,7 +158,8 @@ bool ETSChecker::EnhanceSubstitutionForUnion(const ArenaVector &typePara if (!argumentType->IsETSUnionType()) { bool foundValid = false; for (Type *ctype : paramUn->ConstituentTypes()) { - foundValid |= ValidateTypeSubstitution(typeParams, ctype, argumentType, substitution); + foundValid |= + ValidateTypeSubstitution(typeParams, ctype->MaybeBaseTypeOfGradualType(), argumentType, substitution); } return foundValid; } @@ -433,7 +440,8 @@ bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, auto &argument = arguments[index]; // #22952: infer optional parameter heuristics - auto const paramType = GetNonNullishType(substitutedSig->Params()[index]->TsType()); + auto const paramType = + GetNonNullishType(substitutedSig->Params()[index]->TsType())->MaybeBaseTypeOfGradualType(); if (argument->IsObjectExpression()) { if (!paramType->IsETSObjectType()) { return false; @@ -955,8 +963,9 @@ Signature *ETSChecker::FindMostSpecificSignature(const ArenaVector static Type *GetParameterTypeOrRestAtIdx(checker::ETSChecker *checker, Signature *sig, const size_t idx) { - return idx < sig->ArgCount() ? sig->Params().at(idx)->TsType() - : checker->GetElementTypeOfArray(sig->RestVar()->TsType()); + return idx < sig->ArgCount() + ? sig->Params().at(idx)->TsType()->MaybeBaseTypeOfGradualType() + : checker->GetElementTypeOfArray(sig->RestVar()->TsType())->MaybeBaseTypeOfGradualType(); } static void InitMostSpecificType(TypeRelation *relation, const ArenaVector &signatures, @@ -1095,8 +1104,9 @@ void ETSChecker::CollectSuitableSignaturesForTypeInference( } for (auto *sig : signatures) { - if (paramIdx >= sig->Params().size() || !sig->Params().at(paramIdx)->TsType()->IsETSObjectType() || - !sig->Params().at(paramIdx)->TsType()->AsETSObjectType()->IsGlobalETSObjectType()) { + auto paramType = sig->Params().at(paramIdx)->TsType()->MaybeBaseTypeOfGradualType(); + if (paramIdx >= sig->Params().size() || !paramType->IsETSObjectType() || + !paramType->AsETSObjectType()->IsGlobalETSObjectType()) { bestSignaturesForParameter.insert({paramIdx, sig}); } } @@ -1817,10 +1827,11 @@ Signature *ETSChecker::AdjustForTypeParameters(Signature *source, Signature *tar } auto *substitution = NewSubstitution(); for (size_t ix = 0; ix < sourceTypeParams.size(); ix++) { - if (!targetTypeParams[ix]->IsETSTypeParameter()) { + if (!targetTypeParams[ix]->MaybeBaseTypeOfGradualType()->IsETSTypeParameter()) { continue; } - EmplaceSubstituted(substitution, targetTypeParams[ix]->AsETSTypeParameter(), sourceTypeParams[ix]); + EmplaceSubstituted(substitution, targetTypeParams[ix]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(), + sourceTypeParams[ix]); } return target->Substitute(Relation(), substitution); } @@ -1877,8 +1888,8 @@ bool CheckTypeParameterConstraints(ArenaVector typeParamList1, ArenaVect return false; } for (size_t i = 0; i < typeParamList1.size(); i++) { - auto c1 = typeParamList1[i]->AsETSTypeParameter()->GetConstraintType(); - auto c2 = typeParamList2[i]->AsETSTypeParameter()->GetConstraintType(); + auto c1 = typeParamList1[i]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter()->GetConstraintType(); + auto c2 = typeParamList2[i]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter()->GetConstraintType(); if (!relation->IsSupertypeOf(c1, c2)) { // contravariance check return false; } diff --git a/ets2panda/checker/ets/function_helpers.h b/ets2panda/checker/ets/function_helpers.h index 2ed4c6b430..44554a7931 100644 --- a/ets2panda/checker/ets/function_helpers.h +++ b/ets2panda/checker/ets/function_helpers.h @@ -127,7 +127,7 @@ static const Substitution *BuildImplicitSubstitutionForArguments(ETSChecker *che if (substitution->size() != sigParams.size()) { for (const auto typeParam : sigParams) { - auto newTypeParam = typeParam->AsETSTypeParameter(); + auto newTypeParam = typeParam->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(); if (auto it = substitution->find(newTypeParam); it != substitution->cend()) { continue; } @@ -165,18 +165,21 @@ static const Substitution *BuildExplicitSubstitutionForArguments(ETSChecker *che for (size_t ix = 0; ix < params.size(); ++ix) { instArgs.push_back(MaybeBoxedType(checker, params[ix]->GetType(checker), params[ix])); if (ix < sigParams.size()) { - checker->EmplaceSubstituted(constraintsSubstitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]); + checker->EmplaceSubstituted(constraintsSubstitution, + sigParams[ix]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(), + instArgs[ix]); } } for (size_t ix = instArgs.size(); ix < sigParams.size(); ++ix) { - auto *dflt = sigParams[ix]->AsETSTypeParameter()->GetDefaultType(); + auto typeParam = sigParams[ix]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(); + auto *dflt = typeParam->GetDefaultType(); if (dflt == nullptr) { break; } dflt = dflt->Substitute(checker->Relation(), constraintsSubstitution); instArgs.push_back(dflt); - checker->EmplaceSubstituted(constraintsSubstitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]); + checker->EmplaceSubstituted(constraintsSubstitution, typeParam, instArgs[ix]); } if (sigParams.size() != instArgs.size()) { if ((flags & TypeRelationFlag::NO_THROW) == static_cast>(0U)) { @@ -186,11 +189,11 @@ static const Substitution *BuildExplicitSubstitutionForArguments(ETSChecker *che } for (size_t ix = 0; ix < sigParams.size(); ix++) { - if (!checker->IsCompatibleTypeArgument(sigParams[ix]->AsETSTypeParameter(), instArgs[ix], - constraintsSubstitution)) { + auto typeParam = sigParams[ix]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(); + if (!checker->IsCompatibleTypeArgument(typeParam, instArgs[ix], constraintsSubstitution)) { return nullptr; } - checker->EmplaceSubstituted(substitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]); + checker->EmplaceSubstituted(substitution, typeParam, instArgs[ix]); } return substitution; } diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 553abd66e2..8c6286b224 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include #include "checker/ETSchecker.h" #include "checker/types/globalTypesHolder.h" @@ -1588,7 +1589,7 @@ Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypePa for (std::size_t idx = 0; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) { auto *typeAliasTypeName = typeAliasNode->TypeParams()->Params().at(idx)->Name(); - auto *typeAliasType = typeAliasTypeName->Variable()->TsType(); + auto *typeAliasType = typeAliasTypeName->Variable()->TsType()->MaybeBaseTypeOfGradualType(); if (!typeAliasType->IsETSTypeParameter()) { continue; } @@ -1686,8 +1687,8 @@ util::StringView ETSChecker::FindPropNameForNamespaceImport(const util::StringVi } // Helps to prevent searching for the imported file among external sources if it is the entry program -static parser::Program *SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder, - const util::StringView &importPath) +parser::Program *ETSChecker::SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder, + const util::StringView &importPath) { if (importPath.Is(etsBinder->GetGlobalRecordTable()->Program()->AbsoluteName().Mutf8())) { return etsBinder->GetGlobalRecordTable()->Program(); @@ -1708,21 +1709,37 @@ void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjT program->SetASTChecked(); program->Ast()->Check(this); } + if (program->IsDeclForDynamicStaticInterop()) { + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + } else { + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath); + + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath); + + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath); + + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + } } void ETSChecker::SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType) @@ -1747,6 +1764,10 @@ Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name) return name->Check(this); } + if (name->IsMemberExpression()) { + return name->Check(this); + } + ES2PANDA_ASSERT(name->IsIdentifier()); auto *const var = name->AsIdentifier()->Variable(); @@ -1792,6 +1813,7 @@ checker::Type *ETSChecker::GetElementTypeOfArray(checker::Type *type) if (type->IsTypeError()) { return GlobalTypeError(); } + if (type->IsETSArrayType()) { return type->AsETSArrayType()->ElementType(); } @@ -2403,7 +2425,8 @@ bool ETSChecker::CheckNumberOfTypeArguments(ETSObjectType *const type, ir::TSTyp } size_t minimumTypeArgs = std::count_if(typeParams.begin(), typeParams.end(), [](Type *param) { - return param->IsETSTypeParameter() && param->AsETSTypeParameter()->GetDefaultType() == nullptr; + return param->MaybeBaseTypeOfGradualType()->IsETSTypeParameter() && + param->MaybeBaseTypeOfGradualType()->AsETSTypeParameter()->GetDefaultType() == nullptr; }); if (typeArgs == nullptr && minimumTypeArgs > 0) { LogError(diagnostic::GENERIC_WITHOUT_TYPE_PARAMS, {type}, pos); @@ -3051,11 +3074,12 @@ ir::CallExpression *ETSChecker::CreateExtensionAccessorCall(ETSChecker *checker, void ETSChecker::CheckTypeParameterVariance(ir::ClassDefinition *classDef) { + auto classType = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); if (classDef->TypeParams() == nullptr) { return; } - Context().SetContainingClass(classDef->TsType()->AsETSObjectType()); + Context().SetContainingClass(classType); auto checkVariance = [this](VarianceFlag varianceFlag, ir::Expression *expression, Type *type) { Relation()->Result(RelationResult::TRUE); Relation()->SetNode(expression); diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index 1649138262..703b787001 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -19,6 +19,7 @@ #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsTupleType.h" #include "checker/types/ets/etsPartialTypeParameter.h" +#include "checker/types/gradualType.h" #include "compiler/lowering/phase.h" #include "ir/base/classDefinition.h" #include "ir/base/classElement.h" @@ -155,7 +156,7 @@ bool ETSChecker::ComputeSuperType(ETSObjectType *type) return false; } - Type *superType = classDef->Super()->AsTypeNode()->GetType(this); + auto *superType = classDef->Super()->AsTypeNode()->GetType(this)->MaybeBaseTypeOfGradualType()->AsETSObjectType(); if (superType == nullptr) { return true; } @@ -179,7 +180,7 @@ bool ETSChecker::ComputeSuperType(ETSObjectType *type) if (GetSuperType(superObj) == nullptr) { superObj = GlobalETSObjectType(); } - type->SetSuperType(superObj); + type->SetSuperType(superType); type->AddObjectFlag(ETSObjectFlags::RESOLVED_SUPER); return true; } @@ -187,30 +188,32 @@ bool ETSChecker::ComputeSuperType(ETSObjectType *type) void ETSChecker::ValidateImplementedInterface(ETSObjectType *type, Type *interface, std::unordered_set *extendsSet, const lexer::SourcePosition &pos) { - if (interface->IsETSObjectType() && interface->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::CLASS)) { + auto interfaceType = interface->MaybeBaseTypeOfGradualType(); + if (interfaceType->IsETSObjectType() && interfaceType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::CLASS)) { LogError(diagnostic::INTERFACE_EXTENDS_CLASS, {}, pos); return; } - if (!interface->IsETSObjectType() || !interface->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::INTERFACE)) { + if (!interfaceType->IsETSObjectType() || + !interfaceType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::INTERFACE)) { LogError(diagnostic::NOT_INTERFACE, {}, pos); return; } - if (!extendsSet->insert(interface).second) { + if (!extendsSet->insert(interfaceType).second) { LogError(diagnostic::REPEATED_INTERFACE, {}, pos); } - auto *baseType = GetOriginalBaseType(interface); - if (baseType != interface && !extendsSet->insert(baseType).second) { + auto *baseType = GetOriginalBaseType(interfaceType); + if (baseType != interfaceType && !extendsSet->insert(baseType).second) { LogError(diagnostic::CONFLICTING_GENERIC_INTERFACE_IMPLS, {baseType}, pos); } - GetInterfaces(interface->AsETSObjectType()); - auto *declNode = interface->AsETSObjectType()->GetDeclNode()->AsTSInterfaceDeclaration(); + GetInterfaces(interfaceType->AsETSObjectType()); + auto *declNode = interfaceType->AsETSObjectType()->GetDeclNode()->AsTSInterfaceDeclaration(); if (declNode->TsType() != nullptr && declNode->TsType()->IsTypeError()) { return; } - type->AddInterface(interface->AsETSObjectType()); + type->AddInterface(interfaceType->AsETSObjectType()); } void ETSChecker::GetInterfacesOfClass(ETSObjectType *type) @@ -362,7 +365,8 @@ bool ETSChecker::CheckTypeParameterConstraint(ir::TSTypeParameter *param, Type2T void ETSChecker::SetUpTypeParameterConstraint(ir::TSTypeParameter *const param) { - ETSTypeParameter *const paramType = param->Name()->Variable()->TsType()->AsETSTypeParameter(); + ETSTypeParameter *const paramType = + param->Name()->Variable()->TsType()->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(); auto const traverseReferenced = [this, scope = param->Parent()->AsTSTypeParameterDeclaration()->Scope()](ir::TypeNode *typeNode) { if (!typeNode->IsETSTypeReference()) { @@ -389,13 +393,13 @@ void ETSChecker::SetUpTypeParameterConstraint(ir::TSTypeParameter *const param) } } -ETSTypeParameter *ETSChecker::SetUpParameterType(ir::TSTypeParameter *const param) +Type *ETSChecker::SetUpParameterType(ir::TSTypeParameter *const param) { auto *const var = param->Name()->Variable(); ES2PANDA_ASSERT(var != nullptr); if (var->TsType() != nullptr) { - return var->TsType()->AsETSTypeParameter(); + return var->TsType(); } auto *const paramType = CreateTypeParameter(); @@ -406,8 +410,9 @@ ETSTypeParameter *ETSChecker::SetUpParameterType(ir::TSTypeParameter *const para // NOTE: #15026 recursive type parameter workaround paramType->SetConstraintType(GlobalETSAnyType()); - var->SetTsType(paramType); - return paramType; + auto res = IsDeclForDynamicStaticInterop() ? CreateGradualType(paramType, Program()->GetModuleLang()) : paramType; + var->SetTsType(res); + return res; } void ETSChecker::CreateTypeForClassOrInterfaceTypeParameters(ETSObjectType *type) @@ -437,12 +442,17 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte } checker::ETSObjectType *interfaceType {}; + checker::Type *type {}; if (var->TsType() == nullptr) { interfaceType = CreateETSObjectTypeOrBuiltin(interfaceDecl, checker::ETSObjectFlags::INTERFACE); interfaceType->SetVariable(var); - var->SetTsType(interfaceType); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(interfaceType, Program()->GetModuleLang()) + : interfaceType; + var->SetTsType(type); } else { interfaceType = var->TsType()->AsETSObjectType(); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(interfaceType, Program()->GetModuleLang()) + : interfaceType; } ConstraintCheckScope ctScope(this); @@ -462,7 +472,7 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte CheckInterfaceFunctions(interfaceType); } - return interfaceType; + return type; } Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) @@ -478,21 +488,26 @@ Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) } checker::ETSObjectType *classType {}; + checker::Type *type {}; if (var->TsType() == nullptr) { classType = CreateETSObjectTypeOrBuiltin(classDef, checker::ETSObjectFlags::CLASS); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(classType, Program()->GetModuleLang()) + : classType; classType->SetVariable(var); - var->SetTsType(classType); + var->SetTsType(type); if (classDef->IsAbstract()) { classType->AddObjectFlag(checker::ETSObjectFlags::ABSTRACT); } } else if (var->TsType()->IsETSObjectType()) { classType = var->TsType()->AsETSObjectType(); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(classType, Program()->GetModuleLang()) + : classType; } else { ES2PANDA_ASSERT(IsAnyError()); return GlobalTypeError(); } - classDef->SetTsType(classType); + classDef->SetTsType(type); ConstraintCheckScope ctScope(this); if (classDef->TypeParams() != nullptr) { @@ -516,7 +531,7 @@ Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) GetInterfaces(classType); } ctScope.TryCheckConstraints(); - return classType; + return type; } ETSObjectType *ETSChecker::BuildAnonymousClassProperties(ir::ClassDefinition *classDef, ETSObjectType *superType) @@ -620,6 +635,10 @@ static void ResolveDeclaredDeclsOfObject(ETSChecker *checker, const ETSObjectTyp void ETSChecker::ResolveDeclaredMembersOfObject(const Type *type) { + if (type->IsGradualType()) { + return ResolveDeclaredMembersOfObject(type->AsGradualType()->GetBaseType()); + } + if (!type->IsETSObjectType() || type->AsETSObjectType()->IsPropertiesInstantiated()) { return; } @@ -1190,7 +1209,7 @@ void ETSChecker::CheckClassDefinition(ir::ClassDefinition *classDef) return; } - auto *classType = classDef->TsType()->AsETSObjectType(); + auto *classType = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); if (classType->SuperType() != nullptr) { classType->SuperType()->GetDeclNode()->Check(this); } @@ -1523,7 +1542,7 @@ void ETSChecker::CheckInnerClassMembers(const ETSObjectType *classType) bool ETSChecker::ValidateArrayIndex(ir::Expression *const expr, bool relaxed) { - auto const expressionType = expr->Check(this); + auto const expressionType = expr->Check(this)->MaybeBaseTypeOfGradualType(); if (expressionType->IsTypeError()) { return false; } @@ -2187,6 +2206,9 @@ std::vector ETSChecker::ResolveMemberReference(const ir::Member } const auto *const targetRef = GetTargetRef(memberExpr); auto searchFlag = GetSearchFlags(memberExpr, targetRef); + if (target->HasObjectFlag(ETSObjectFlags::LAZY_IMPORT_OBJECT)) { + searchFlag |= PropertySearchFlags::SEARCH_INSTANCE; + } auto searchName = target->GetReExportAliasValue(memberExpr->Property()->AsIdentifier()->Name()); auto *prop = target->GetProperty(searchName, searchFlag); varbinder::Variable *const globalFunctionVar = ResolveInstanceExtension(memberExpr); diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index 515b663e38..1242f6b19d 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -16,6 +16,7 @@ #include "checker/checker.h" #include "checker/ets/wideningConverter.h" #include "checker/types/globalTypesHolder.h" +#include "checker/types/gradualType.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsPartialTypeParameter.h" #include "ir/base/catchClause.h" @@ -73,7 +74,7 @@ void ETSChecker::CheckTruthinessOfType(ir::Expression *expr) bool ETSChecker::CheckNonNullish(ir::Expression const *expr) { - if (!expr->TsType()->PossiblyETSNullish()) { + if (!expr->TsType()->PossiblyETSNullish() || expr->TsType()->IsETSAnyType()) { return true; } @@ -87,6 +88,9 @@ bool ETSChecker::CheckNonNullish(ir::Expression const *expr) Type *ETSChecker::GetNonNullishType(Type *type) { + if (type->IsGradualType()) { + return GetNonNullishType(type->AsGradualType()->GetBaseType()); + } if (type->DefinitelyNotETSNullish()) { return type; } @@ -94,7 +98,7 @@ Type *ETSChecker::GetNonNullishType(Type *type) return type; } if (type->IsETSTypeParameter()) { - return ProgramAllocator()->New(type->AsETSTypeParameter()); + return ProgramAllocator()->New(type->MaybeBaseTypeOfGradualType()->AsETSTypeParameter()); } if (type->IsETSPartialTypeParameter()) { return type->AsETSPartialTypeParameter()->GetUnderlying(); @@ -181,7 +185,7 @@ std::pair ETSChecker::RemoveNullishTypes(Type *type) if (type->IsETSTypeParameter()) { return {GetGlobalTypesHolder()->GlobalETSUnionUndefinedNull(), - ProgramAllocator()->New(type->AsETSTypeParameter())}; + ProgramAllocator()->New(type->MaybeBaseTypeOfGradualType()->AsETSTypeParameter())}; } if (type->IsETSUndefinedType() || type->IsETSNullType()) { @@ -192,7 +196,8 @@ std::pair ETSChecker::RemoveNullishTypes(Type *type) ArenaVector nullishTypes(ProgramAllocator()->Adapter()); ArenaVector notNullishTypes(ProgramAllocator()->Adapter()); - for (auto *constituentType : type->AsETSUnionType()->ConstituentTypes()) { + for (auto *ctype : type->AsETSUnionType()->ConstituentTypes()) { + auto constituentType = ctype->MaybeBaseTypeOfGradualType(); if (constituentType->IsETSUndefinedType() || constituentType->IsETSNullType()) { nullishTypes.push_back(constituentType); } else { @@ -214,6 +219,9 @@ std::pair ETSChecker::RemoveNullishTypes(Type *type) template static bool MatchConstituentOrConstraint(const Type *type, Pred const &pred, Trv const &trv) { + if (type->IsGradualType()) { + return MatchConstituentOrConstraint(type->AsGradualType()->GetBaseType(), pred, trv); + } auto const traverse = [&pred, &trv](const Type *ttype) { return MatchConstituentOrConstraint(ttype, pred, trv); }; @@ -335,7 +343,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::ETS_ENUM | TypeFlag::ETS_READONLY | TypeFlag::GRADUAL_TYPE; // Issues if (type->IsETSVoidType()) { // NOTE(vpukhov): #19701 void refactoring @@ -461,10 +469,10 @@ void ETSChecker::IterateInVariableContext(varbinder::Variable *const var) Type *containingClass {}; if (classDef->TsType() == nullptr) { - containingClass = BuildBasicClassProperties(classDef); - ResolveDeclaredMembersOfObject(containingClass->AsETSObjectType()); + containingClass = BuildBasicClassProperties(classDef)->MaybeBaseTypeOfGradualType(); + ResolveDeclaredMembersOfObject(containingClass); } else { - containingClass = classDef->TsType()->AsETSObjectType(); + containingClass = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); } ES2PANDA_ASSERT(classDef->TsType()); diff --git a/ets2panda/checker/ets/typeCreation.cpp b/ets2panda/checker/ets/typeCreation.cpp index 1c8c6c9a98..455dd2d88d 100644 --- a/ets2panda/checker/ets/typeCreation.cpp +++ b/ets2panda/checker/ets/typeCreation.cpp @@ -19,6 +19,7 @@ #include "checker/types/ets/etsEnumType.h" #include "checker/types/ets/etsResizableArrayType.h" #include "checker/types/globalTypesHolder.h" +#include "checker/types/gradualType.h" #include "checker/types/type.h" #include "ir/statements/annotationDeclaration.h" @@ -85,8 +86,8 @@ ETSResizableArrayType *ETSChecker::CreateETSMultiDimResizableArrayType(Type *ele for (size_t dim = 0; dim < dimSize; ++dim) { Substitution *tmpSubstitution = NewSubstitution(); - EmplaceSubstituted(tmpSubstitution, arrayType->TypeArguments()[0]->AsETSTypeParameter()->GetOriginal(), - MaybeBoxType(baseArrayType)); + auto typeParam = arrayType->TypeArguments()[0]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(); + EmplaceSubstituted(tmpSubstitution, typeParam, MaybeBoxType(baseArrayType)); baseArrayType = arrayType->Substitute(Relation(), tmpSubstitution); } return baseArrayType->AsETSResizableArrayType(); @@ -98,8 +99,8 @@ ETSResizableArrayType *ETSChecker::CreateETSResizableArrayType(Type *element) ES2PANDA_ASSERT(arrayType->TypeArguments().size() == 1U); Substitution *substitution = NewSubstitution(); - EmplaceSubstituted(substitution, arrayType->TypeArguments()[0]->AsETSTypeParameter()->GetOriginal(), - MaybeBoxType(element)); + auto typeParam = arrayType->TypeArguments()[0]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(); + EmplaceSubstituted(substitution, typeParam->GetOriginal(), MaybeBoxType(element)); return arrayType->Substitute(Relation(), substitution); } @@ -124,6 +125,41 @@ ETSArrayType *ETSChecker::CreateETSArrayType(Type *elementType, bool isCachePoll return arrayType; } +Type *ETSChecker::GetGlobalGradualType(Type *baseType, Language lang) const +{ + auto gradualMap = gradualTypes_.find(lang)->second; + auto res = gradualMap.find(baseType); + if (res != gradualMap.end()) { + return res->second; + } + return baseType; +} + +Type *ETSChecker::CreateGradualType(Type *baseType, const Language lang, bool recursive) +{ + // For built-in 1.2 types and pure static 1.2 types, gradual will be simplified to T. + ES2PANDA_ASSERT(baseType->IsETSObjectType() || baseType->IsETSTypeParameter()); + + if (!recursive && baseType->IsGradualType()) { + return baseType->AsGradualType(); + } + + auto &gradualMap = gradualTypes_.find(lang)->second; + + auto res = gradualMap.find(baseType); + if (res != gradualMap.end()) { + return res->second; + } + + auto *gradualType = ProgramAllocator()->New(baseType, lang); + + std::stringstream ss; + gradualType->ToAssemblerTypeWithRank(ss); + + gradualMap.insert({baseType, gradualType}); + return gradualType; +} + Type *ETSChecker::CreateETSUnionType(Span constituentTypes) { if (constituentTypes.empty()) { @@ -152,6 +188,11 @@ ETSFunctionType *ETSChecker::CreateETSArrowType(Signature *signature) return ProgramAllocator()->New(this, signature); } +ETSAnyType *ETSChecker::CreateETSAnyType(bool transformFromGradual) +{ + return ProgramAllocator()->New(transformFromGradual); +} + ETSFunctionType *ETSChecker::CreateETSMethodType(util::StringView name, ArenaVector &&signatures) { return ProgramAllocator()->New(this, name, std::move(signatures)); @@ -332,22 +373,24 @@ ETSObjectType *ETSChecker::CreateETSObjectTypeOrBuiltin(ir::AstNode *declNode, E ETSObjectType *ETSChecker::CreateETSObjectType(ir::AstNode *declNode, ETSObjectFlags flags) { auto const [name, internalName] = GetObjectTypeDeclNames(declNode); - + ETSObjectType *objectType = nullptr; if (declNode->IsClassDefinition() && (declNode->AsClassDefinition()->IsEnumTransformed())) { if (declNode->AsClassDefinition()->IsIntEnumTransformed()) { - return ProgramAllocator()->New(ProgramAllocator(), name, internalName, declNode, - Relation()); + objectType = + ProgramAllocator()->New(ProgramAllocator(), name, internalName, declNode, Relation()); + } else { + ES2PANDA_ASSERT(declNode->AsClassDefinition()->IsStringEnumTransformed()); + objectType = ProgramAllocator()->New(ProgramAllocator(), name, internalName, declNode, + Relation()); } - ES2PANDA_ASSERT(declNode->AsClassDefinition()->IsStringEnumTransformed()); - return ProgramAllocator()->New(ProgramAllocator(), name, internalName, declNode, Relation()); - } - - if (internalName == compiler::Signatures::BUILTIN_ARRAY) { - return ProgramAllocator()->New(ProgramAllocator(), name, - std::make_tuple(declNode, flags, Relation())); + } else if (internalName == compiler::Signatures::BUILTIN_ARRAY) { + objectType = ProgramAllocator()->New(ProgramAllocator(), name, + std::make_tuple(declNode, flags, Relation())); + } else { + objectType = ProgramAllocator()->New(ProgramAllocator(), name, internalName, + std::make_tuple(declNode, flags, Relation())); } - return ProgramAllocator()->New(ProgramAllocator(), name, internalName, - std::make_tuple(declNode, flags, Relation())); + return objectType; } std::tuple ETSChecker::CreateBuiltinArraySignatureInfo(const ETSArrayType *arrayType, @@ -403,7 +446,8 @@ ETSObjectType *ETSChecker::CreatePromiseOf(Type *type) ES2PANDA_ASSERT(promiseType->TypeArguments().size() == 1U); Substitution *substitution = NewSubstitution(); - EmplaceSubstituted(substitution, promiseType->TypeArguments()[0]->AsETSTypeParameter()->GetOriginal(), type); + auto typeParam = promiseType->TypeArguments()[0]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(); + EmplaceSubstituted(substitution, typeParam->GetOriginal(), type); return promiseType->Substitute(Relation(), substitution); } diff --git a/ets2panda/checker/ets/typeRelationContext.cpp b/ets2panda/checker/ets/typeRelationContext.cpp index f2aecd19fb..9edc873e8b 100644 --- a/ets2panda/checker/ets/typeRelationContext.cpp +++ b/ets2panda/checker/ets/typeRelationContext.cpp @@ -84,11 +84,11 @@ void InstantiationContext::InstantiateType(ETSObjectType *type, ir::TSTypeParame while (typeArgTypes.size() < type->TypeArguments().size()) { Type *defaultType = nullptr; - - if (type->TypeArguments().at(typeArgTypes.size())->IsETSTypeParameter()) { - defaultType = type->TypeArguments().at(typeArgTypes.size())->AsETSTypeParameter()->GetDefaultType(); + auto typeArg = type->TypeArguments().at(typeArgTypes.size())->MaybeBaseTypeOfGradualType(); + if (typeArg->IsETSTypeParameter()) { + defaultType = typeArg->AsETSTypeParameter()->GetDefaultType(); } else { - defaultType = type->TypeArguments().at(typeArgTypes.size()); + defaultType = typeArg; } if (defaultType != nullptr && !defaultType->IsTypeError()) { @@ -110,7 +110,8 @@ static void CheckInstantiationConstraints(ETSChecker *checker, ArenaVectorRelation(); - for (auto type : typeParams) { + for (auto paramType : typeParams) { + auto type = paramType->MaybeBaseTypeOfGradualType(); if (!type->IsETSTypeParameter()) { continue; } @@ -155,10 +156,11 @@ void InstantiationContext::InstantiateType(ETSObjectType *type, ArenaVectorNewSubstitution(); for (size_t idx = 0; idx < typeParams.size(); idx++) { - if (!typeParams[idx]->IsETSTypeParameter()) { + auto typeParam = typeParams[idx]->MaybeBaseTypeOfGradualType(); + if (!typeParam->IsETSTypeParameter()) { continue; } - checker_->EmplaceSubstituted(substitution, typeParams[idx]->AsETSTypeParameter(), typeArgTypes[idx]); + checker_->EmplaceSubstituted(substitution, typeParam->AsETSTypeParameter(), typeArgTypes[idx]); } ConstraintCheckScope ctScope(checker_); diff --git a/ets2panda/checker/ets/typeRelationContext.h b/ets2panda/checker/ets/typeRelationContext.h index eeb2ba008d..fb2c13818d 100644 --- a/ets2panda/checker/ets/typeRelationContext.h +++ b/ets2panda/checker/ets/typeRelationContext.h @@ -28,6 +28,8 @@ public: const lexer::SourcePosition &pos, std::optional diag = std::nullopt, TypeRelationFlag flags = TypeRelationFlag::NONE) { + source = source->MaybeBaseTypeOfGradualType(); + target = target->MaybeBaseTypeOfGradualType(); flags_ |= ((flags & TypeRelationFlag::NO_BOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::BOXING; flags_ |= ((flags & TypeRelationFlag::NO_UNBOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::UNBOXING; flags_ |= ((flags & TypeRelationFlag::NO_WIDENING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::WIDENING; @@ -88,6 +90,8 @@ public: const lexer::SourcePosition &pos, const std::optional &diag, TypeRelationFlag initialFlags = TypeRelationFlag::NONE) { + source = source->MaybeBaseTypeOfGradualType(); + target = target->MaybeBaseTypeOfGradualType(); flags_ |= ((initialFlags & TypeRelationFlag::NO_BOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::BOXING; flags_ |= diff --git a/ets2panda/checker/ets/utilityTypeHandlers.cpp b/ets2panda/checker/ets/utilityTypeHandlers.cpp index c267defa3c..3769508cc5 100644 --- a/ets2panda/checker/ets/utilityTypeHandlers.cpp +++ b/ets2panda/checker/ets/utilityTypeHandlers.cpp @@ -23,6 +23,8 @@ #include "varbinder/ETSBinder.h" #include "checker/types/ets/etsPartialTypeParameter.h" +#include + namespace ark::es2panda::checker { std::optional ETSChecker::GetUtilityTypeTypeParamNode( @@ -123,6 +125,10 @@ Type *ETSChecker::CreatePartialType(Type *const typeToBePartial) return typeToBePartial; } + if (typeToBePartial->IsGradualType()) { + return CreatePartialType(typeToBePartial->AsGradualType()->GetBaseType()); + } + if (typeToBePartial->IsETSTypeParameter()) { return CreatePartialTypeParameter(typeToBePartial->AsETSTypeParameter()); } @@ -388,8 +394,8 @@ ir::TSTypeParameterInstantiation *ETSChecker::CreateNewSuperPartialRefTypeParams !originRefParams[ix]->AsETSTypeReference()->Part()->TsType()->IsETSTypeParameter()) { continue; } - auto it = likeSubstitution->find( - originRefParams[ix]->AsETSTypeReference()->Part()->TsType()->AsETSTypeParameter()->GetDeclNode()); + auto type = originRefParams[ix]->AsETSTypeReference()->Part()->TsType(); + auto it = likeSubstitution->find(type->MaybeBaseTypeOfGradualType()->AsETSTypeParameter()->GetDeclNode()); if (it != likeSubstitution->end()) { auto *typeParamRefPart = // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) diff --git a/ets2panda/checker/types/ets/etsAnyType.cpp b/ets2panda/checker/types/ets/etsAnyType.cpp index d9b87e49a2..52793689c9 100644 --- a/ets2panda/checker/types/ets/etsAnyType.cpp +++ b/ets2panda/checker/types/ets/etsAnyType.cpp @@ -22,7 +22,7 @@ namespace ark::es2panda::checker { void ETSAnyType::Identical(TypeRelation *relation, Type *other) { - relation->Result(other->IsAnyType()); + relation->Result(other->IsETSAnyType()); } void ETSAnyType::AssignmentTarget(TypeRelation *relation, Type *source) diff --git a/ets2panda/checker/types/ets/etsAnyType.h b/ets2panda/checker/types/ets/etsAnyType.h index 1a315b36d6..bab24a6004 100644 --- a/ets2panda/checker/types/ets/etsAnyType.h +++ b/ets2panda/checker/types/ets/etsAnyType.h @@ -22,6 +22,10 @@ namespace ark::es2panda::checker { class ETSAnyType : public Type { public: ETSAnyType() : Type(TypeFlag::ETS_ANY) {} + explicit ETSAnyType(bool isTransformedFromGradualType) + : Type(TypeFlag::ETS_ANY), isTransformedFromGradualType_(isTransformedFromGradualType) + { + } void Identical(TypeRelation *relation, Type *other) override; void AssignmentTarget(TypeRelation *relation, Type *source) override; @@ -38,6 +42,14 @@ public: TypeFacts GetTypeFacts() const override; Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + bool IsTransformedFromGradualType() const + { + return isTransformedFromGradualType_; + } + +private: + bool isTransformedFromGradualType_ = false; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsFunctionType.cpp b/ets2panda/checker/types/ets/etsFunctionType.cpp index d6b776d64c..c564a74fba 100644 --- a/ets2panda/checker/types/ets/etsFunctionType.cpp +++ b/ets2panda/checker/types/ets/etsFunctionType.cpp @@ -102,10 +102,10 @@ static ETSObjectType *FunctionTypeToFunctionalInterfaceType(ETSChecker *checker, auto *substitution = checker->NewSubstitution(); for (size_t i = 0; i < arity; i++) { - substitution->emplace(funcIface->TypeArguments()[i]->AsETSTypeParameter(), + substitution->emplace(funcIface->TypeArguments()[i]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(), checker->MaybeBoxType(signature->Params()[i]->TsType())); } - substitution->emplace(funcIface->TypeArguments()[arity]->AsETSTypeParameter(), + substitution->emplace(funcIface->TypeArguments()[arity]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(), checker->MaybeBoxType(signature->ReturnType())); auto result = funcIface->Substitute(checker->Relation(), substitution, true, isExtensionHack); diff --git a/ets2panda/checker/types/ets/etsObjectType.cpp b/ets2panda/checker/types/ets/etsObjectType.cpp index ace95542ce..277adc4569 100644 --- a/ets2panda/checker/types/ets/etsObjectType.cpp +++ b/ets2panda/checker/types/ets/etsObjectType.cpp @@ -235,7 +235,6 @@ varbinder::LocalVariable *ETSObjectType::CreateSyntheticVarFromEverySignature(ut for (auto &s : signatureSet) { funcType->AddCallSignature(s.second); } - res->SetTsType(funcType); funcType->SetVariable(res); @@ -1088,7 +1087,9 @@ static Substitution *ComputeEffectiveSubstitution(TypeRelation *const relation, auto *effectiveSubstitution = checker->NewSubstitution(); for (size_t ix = 0; ix < baseTypeParams.size(); ix++) { - checker->EmplaceSubstituted(effectiveSubstitution, baseTypeParams[ix]->AsETSTypeParameter(), newTypeArgs[ix]); + checker->EmplaceSubstituted(effectiveSubstitution, + baseTypeParams[ix]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(), + newTypeArgs[ix]); } return effectiveSubstitution; @@ -1285,7 +1286,8 @@ ETSObjectType *ETSObjectType::SubstituteArguments(TypeRelation *relation, ArenaV ES2PANDA_ASSERT(typeArguments_.size() == arguments.size()); for (size_t ix = 0; ix < typeArguments_.size(); ix++) { - substitution->emplace(typeArguments_[ix]->AsETSTypeParameter(), checker->MaybeBoxType(arguments[ix])); + substitution->emplace(typeArguments_[ix]->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(), + checker->MaybeBoxType(arguments[ix]->MaybeBaseTypeOfGradualType())); } return Substitute(relation, substitution); diff --git a/ets2panda/checker/types/ets/etsObjectTypeConstants.h b/ets2panda/checker/types/ets/etsObjectTypeConstants.h index 1b27c6e7c7..651e28c1be 100644 --- a/ets2panda/checker/types/ets/etsObjectTypeConstants.h +++ b/ets2panda/checker/types/ets/etsObjectTypeConstants.h @@ -61,6 +61,7 @@ enum class ETSObjectFlags : std::uint64_t { EXTENSION_FUNCTION = 1ULL << 35U, FUNCTIONAL_REFERENCE = 1ULL << 36U, + LAZY_IMPORT_OBJECT = 1ULL << 37U, ENUM_OBJECT = INT_ENUM_OBJECT | STRING_ENUM_OBJECT, diff --git a/ets2panda/checker/types/ets/etsTypeParameter.cpp b/ets2panda/checker/types/ets/etsTypeParameter.cpp index 518a42441b..615584de9b 100644 --- a/ets2panda/checker/types/ets/etsTypeParameter.cpp +++ b/ets2panda/checker/types/ets/etsTypeParameter.cpp @@ -95,7 +95,8 @@ void ETSTypeParameter::CheckVarianceRecursively([[maybe_unused]] TypeRelation *r } auto classTypeParameters = relation->GetChecker()->Context().ContainingClass()->TypeArguments(); if (std::all_of(classTypeParameters.begin(), classTypeParameters.end(), [this](Type *param) { - return this->GetDeclNode()->Name()->Name() != param->AsETSTypeParameter()->Name(); + return this->GetDeclNode()->Name()->Name() != + param->MaybeBaseTypeOfGradualType()->AsETSTypeParameter()->Name(); })) { return; } @@ -159,7 +160,7 @@ void ETSTypeParameter::ToDebugInfoType(std::stringstream &ss) const ETSTypeParameter *ETSTypeParameter::GetOriginal() const noexcept { - return GetDeclNode()->Name()->Variable()->TsType()->AsETSTypeParameter(); + return GetDeclNode()->Name()->Variable()->TsType()->MaybeBaseTypeOfGradualType()->AsETSTypeParameter(); } util::StringView const &ETSTypeParameter::Name() const noexcept diff --git a/ets2panda/checker/types/ets/etsUnionType.cpp b/ets2panda/checker/types/ets/etsUnionType.cpp index b5a4244e54..7b94a53112 100644 --- a/ets2panda/checker/types/ets/etsUnionType.cpp +++ b/ets2panda/checker/types/ets/etsUnionType.cpp @@ -359,7 +359,7 @@ bool ETSUnionType::ExtractType(checker::ETSChecker *checker, checker::Type *sour bool rc = false; auto it = unionTypes.cbegin(); while (it != unionTypes.cend()) { - auto *constituentType = *it; + auto *constituentType = (*it)->MaybeBaseTypeOfGradualType(); // Because 'instanceof' expression does not check for type parameters, then for generic types we should // consider that expressions like 'SomeType' and 'SomeType' are identical for smart casting. // We also have to pass through all the union to process cases like 'C|A|B|C|undefined` diff --git a/ets2panda/checker/types/gradualType.cpp b/ets2panda/checker/types/gradualType.cpp new file mode 100644 index 0000000000..59a2a4a1be --- /dev/null +++ b/ets2panda/checker/types/gradualType.cpp @@ -0,0 +1,97 @@ +/* + * 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 "gradualType.h" + +#include "checker/ETSchecker.h" +#include "checker/ets/conversion.h" + +namespace ark::es2panda::checker { +void GradualType::Identical(TypeRelation *relation, Type *other) +{ + baseType_->Identical(relation, other); +} + +void GradualType::AssignmentTarget(TypeRelation *relation, Type *source) +{ + baseType_->AssignmentTarget(relation, source); +} + +bool GradualType::AssignmentSource(TypeRelation *relation, Type *target) +{ + return baseType_->AssignmentSource(relation, target); +} + +void GradualType::Compare(TypeRelation *relation, Type *other) +{ + return baseType_->Compare(relation, other); +} + +void GradualType::Cast(TypeRelation *relation, Type *target) +{ + return baseType_->Cast(relation, target); +} + +void GradualType::CastTarget(TypeRelation *relation, Type *source) +{ + return baseType_->CastTarget(relation, source); +} + +void GradualType::IsSubtypeOf(TypeRelation *relation, Type *target) +{ + return baseType_->IsSubtypeOf(relation, target); +} + +void GradualType::IsSupertypeOf(TypeRelation *relation, Type *source) +{ + return baseType_->IsSupertypeOf(relation, source); +} + +void GradualType::ToString(std::stringstream &ss, [[maybe_unused]] bool precise) const +{ + baseType_->ToString(ss); +} + +Type *GradualType::Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) +{ + return baseType_->Instantiate(allocator, relation, globalTypes); +} + +Type *GradualType::Substitute(TypeRelation *relation, const Substitution *substitution) +{ + return baseType_->Substitute(relation, substitution); +} + +void GradualType::ToAssemblerType(std::stringstream &ss) const +{ + baseType_->ToAssemblerType(ss); +} + +void GradualType::ToDebugInfoType(std::stringstream &ss) const +{ + baseType_->ToDebugInfoType(ss); +} + +void GradualType::ToAssemblerTypeWithRank(std::stringstream &ss) const +{ + baseType_->ToAssemblerTypeWithRank(ss); +} + +void GradualType::CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) +{ + // The type of array should be Invariant + relation->CheckVarianceRecursively(baseType_, varianceFlag); +} +} // namespace ark::es2panda::checker \ No newline at end of file diff --git a/ets2panda/checker/types/gradualType.h b/ets2panda/checker/types/gradualType.h new file mode 100644 index 0000000000..eb1978e8d6 --- /dev/null +++ b/ets2panda/checker/types/gradualType.h @@ -0,0 +1,89 @@ +/* + * 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_GRADUAL_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_GRADUAL_TYPE_H + +#include "checker/types/type.h" +#include "ir/astNode.h" + +namespace ark::es2panda::checker { +class GradualType : public Type { +public: + explicit GradualType(checker::Type *baseType) + : Type(TypeFlag::GRADUAL_TYPE), baseType_(baseType), lang_(es2panda::Language(Language::Id::ETS)) + { + } + + explicit GradualType(checker::Type *baseType, Language lang) + : Type(TypeFlag::GRADUAL_TYPE), baseType_(baseType), lang_(lang) + { + } + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource(TypeRelation *relation, Type *target) override; + void Compare(TypeRelation *relation, Type *other) override; + void Cast(TypeRelation *relation, Type *target) override; + void CastTarget(TypeRelation *relation, Type *source) override; + void IsSubtypeOf(TypeRelation *relation, Type *target) override; + void IsSupertypeOf(TypeRelation *relation, Type *source) override; + void ToString(std::stringstream &ss, bool precise) const override; + void ToAssemblerType(std::stringstream &ss) const override; + void ToDebugInfoType(std::stringstream &ss) const override; + void ToAssemblerTypeWithRank(std::stringstream &ss) const override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + Type *Substitute(TypeRelation *relation, const Substitution *substitution) override; + void CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) override; + + const Type *GetBaseType() const + { + auto baseType = baseType_; + while (baseType->IsGradualType()) { + baseType = baseType->AsGradualType()->BaseType(); + } + return baseType; + } + + Type *GetBaseType() + { + auto baseType = baseType_; + while (baseType->IsGradualType()) { + baseType = baseType->AsGradualType()->BaseType(); + } + return baseType; + } + + Type *BaseType() + { + return baseType_; + } + + Type *BaseType() const + { + return baseType_; + } + + es2panda::Language Language() const + { + return lang_; + } + +private: + Type *baseType_; + es2panda::Language lang_; +}; +} // namespace ark::es2panda::checker + +#endif \ No newline at end of file diff --git a/ets2panda/checker/types/signature.cpp b/ets2panda/checker/types/signature.cpp index 5ced79c411..be45282d5f 100644 --- a/ets2panda/checker/types/signature.cpp +++ b/ets2panda/checker/types/signature.cpp @@ -112,7 +112,7 @@ Signature *Signature::Copy(ArenaAllocator *allocator, TypeRelation *relation, Gl SignatureInfo *copiedInfo = allocator->New(signatureInfo_, allocator); for (size_t idx = 0U; idx < signatureInfo_->params.size(); ++idx) { - auto *const paramType = signatureInfo_->params[idx]->TsType(); + auto *const paramType = signatureInfo_->params[idx]->TsType()->MaybeBaseTypeOfGradualType(); if (paramType->HasTypeFlag(TypeFlag::GENERIC) && paramType->IsETSObjectType()) { copiedInfo->params[idx]->SetTsType(paramType->Instantiate(allocator, relation, globalTypes)); auto originalTypeArgs = paramType->AsETSObjectType()->GetOriginalBaseType()->TypeArguments(); diff --git a/ets2panda/checker/types/type.cpp b/ets2panda/checker/types/type.cpp index 090981812f..61a6b8568e 100644 --- a/ets2panda/checker/types/type.cpp +++ b/ets2panda/checker/types/type.cpp @@ -18,6 +18,7 @@ #include "checker/types/typeFlag.h" #include "checker/types/typeRelation.h" #include "checker/types/ets/etsObjectType.h" +#include "checker/types/gradualType.h" #include "checker/checker.h" namespace ark::es2panda::checker { @@ -161,4 +162,13 @@ bool IsTypeError(Type const *tp) return tp != nullptr && tp->IsTypeError(); } +Type *Type::MaybeBaseTypeOfGradualType() +{ + auto res = this; + while (res->IsGradualType()) { + res = res->AsGradualType()->GetBaseType(); + } + return res; +} + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/type.h b/ets2panda/checker/types/type.h index d3f83e9d37..f4b3a3f481 100644 --- a/ets2panda/checker/types/type.h +++ b/ets2panda/checker/types/type.h @@ -33,6 +33,7 @@ class ETSAsyncFuncReturnType; class ETSChecker; class ETSTypeParameter; class ETSEnumType; +class GradualType; // CC-OFFNXT(G.PRE.02) name part // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -160,6 +161,8 @@ public: return HasTypeFlag(checker::TypeFlag::CONSTANT); } + Type *MaybeBaseTypeOfGradualType(); + TypeFlag TypeFlags() const { return typeFlags_; diff --git a/ets2panda/checker/types/typeFlag.h b/ets2panda/checker/types/typeFlag.h index feea58ce12..5f01ae8225 100644 --- a/ets2panda/checker/types/typeFlag.h +++ b/ets2panda/checker/types/typeFlag.h @@ -69,11 +69,10 @@ enum class TypeFlag : uint64_t { ETS_ARRAY = 1ULL << 41ULL, // ETS array type WILDCARD = 1ULL << 42ULL, // new A() ETS_TYPE_PARAMETER = 1ULL << 43ULL, // ETS type parameter - ETS_TYPE_REFERENCE = 1ULL << 44ULL, // ETS type reference GENERIC = 1ULL << 45ULL, // ETS Generic ETS_INT_ENUM = 1ULL << 46ULL, // ETS Enum ETS_STRING_ENUM = 1ULL << 47ULL, // ETS string-type Enumeration - ETS_DYNAMIC_FLAG = 1ULL << 48ULL, // ETS Dynamic flag + GRADUAL_TYPE = 1ULL << 48ULL, // gradual type GETTER = 1ULL << 49ULL, // ETS Getter SETTER = 1ULL << 50ULL, // ETS Setter ETS_EXTENSION_FUNC_HELPER = 1ULL << 51ULL, // ETS Extension Function Helper @@ -90,8 +89,6 @@ enum class TypeFlag : uint64_t { ETS_ANY = 1ULL << 22ULL, // ETS any, the value was *stolen* from the CONDITIONAL type kind ETS_NEVER = 1ULL << 62ULL, // ETS never ETS_METHOD = 1ULL << 63ULL, // ETS method (or function in module) (possibly overloaded) - ETS_DYNAMIC_TYPE = ETS_OBJECT | ETS_DYNAMIC_FLAG, - ETS_DYNAMIC_FUNCTION_TYPE = FUNCTION | ETS_DYNAMIC_FLAG, ETS_INTEGRAL_NUMERIC = BYTE | SHORT | INT | LONG, ETS_FLOATING_POINT = FLOAT | DOUBLE, ETS_NUMERIC = ETS_INTEGRAL_NUMERIC | ETS_FLOATING_POINT, diff --git a/ets2panda/checker/types/typeMapping.h b/ets2panda/checker/types/typeMapping.h index a856a49bdb..84e3ba9cfe 100644 --- a/ets2panda/checker/types/typeMapping.h +++ b/ets2panda/checker/types/typeMapping.h @@ -71,6 +71,7 @@ _(TypeFlag::ETS_TUPLE, ETSTupleType) \ _(TypeFlag::ETS_PARTIAL_TYPE_PARAMETER, ETSPartialTypeParameter) \ _(TypeFlag::TYPE_ERROR, TypeError) \ + _(TypeFlag::GRADUAL_TYPE, GradualType) \ _(TypeFlag::ETS_TYPE_ALIAS, ETSTypeAliasType) #define OBJECT_TYPE_MAPPING(_) \ diff --git a/ets2panda/compiler/base/lreference.cpp b/ets2panda/compiler/base/lreference.cpp index 96572db071..f83427ac2b 100644 --- a/ets2panda/compiler/base/lreference.cpp +++ b/ets2panda/compiler/base/lreference.cpp @@ -183,7 +183,8 @@ ETSLReference::ETSLReference(CodeGen *cg, const ir::AstNode *node, ReferenceKind const auto *memberExpr = Node()->AsMemberExpression(); staticObjRef_ = memberExpr->Object()->TsType(); - if (!memberExpr->IsComputed() && etsg_->Checker()->IsVariableStatic(memberExpr->PropVar())) { + if (!memberExpr->IsComputed() && memberExpr->PropVar() != nullptr && + etsg_->Checker()->IsVariableStatic(memberExpr->PropVar())) { return; } @@ -196,7 +197,8 @@ ETSLReference::ETSLReference(CodeGen *cg, const ir::AstNode *node, ReferenceKind TargetTypeContext pttctx(etsg_, memberExpr->Property()->TsType()); memberExpr->Property()->Compile(etsg_); etsg_->ApplyConversion(memberExpr->Property()); - ES2PANDA_ASSERT(etsg_->GetAccumulatorType()->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL)); + ES2PANDA_ASSERT(memberExpr->Object()->TsType()->IsETSAnyType() || + etsg_->GetAccumulatorType()->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL)); propReg_ = etsg_->AllocReg(); etsg_->StoreAccumulator(node, propReg_); } @@ -280,6 +282,15 @@ void ETSLReference::SetValueComputed(const ir::MemberExpression *memberExpr) con { const auto *const objectType = memberExpr->Object()->TsType(); + if (objectType->IsETSAnyType()) { + if (memberExpr->Property()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { + etsg_->StoreByIndexAny(memberExpr, baseReg_, propReg_); + } else { + etsg_->StoreByValueAny(memberExpr, baseReg_, propReg_); + } + return; + } + if (objectType->IsETSTupleType()) { ES2PANDA_ASSERT(memberExpr->GetTupleIndexValue().has_value()); @@ -325,6 +336,7 @@ void ETSLReference::SetValue() const const auto *const memberExpr = Node()->AsMemberExpression(); const auto *const memberExprTsType = memberExpr->TsType(); + auto const *objectType = memberExpr->Object()->TsType(); if (!memberExpr->IsIgnoreBox()) { etsg_->ApplyConversion(Node(), memberExprTsType); @@ -335,6 +347,11 @@ void ETSLReference::SetValue() const return; } + if (objectType->IsETSAnyType()) { + etsg_->StorePropertyByNameAny(memberExpr, baseReg_, memberExpr->Property()->AsIdentifier()->Name()); + return; + } + if (memberExpr->PropVar()->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { SetValueGetterSetter(memberExpr); return; @@ -349,8 +366,6 @@ void ETSLReference::SetValue() const return; } - auto const *objectType = memberExpr->Object()->TsType(); - if (objectType->IsETSUnionType()) { etsg_->StorePropertyByName(Node(), baseReg_, checker::ETSChecker::FormNamedAccessMetadata(memberExpr->PropVar())); diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 09d7df240a..e2a57c66ae 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -19,9 +19,15 @@ #include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/switchBuilder.h" +#include "compiler/core/targetTypeContext.h" +#include "compiler/core/vReg.h" +#include "compiler/function/functionBuilder.h" #include "checker/ETSchecker.h" #include "checker/types/ets/etsTupleType.h" #include "ETSGen-inl.h" +#include "generated/signatures.h" +#include "util/es2pandaMacros.h" +#include "varbinder/ETSBinder.h" namespace ark::es2panda::compiler { @@ -242,9 +248,17 @@ static void GetSizeInForOf(compiler::ETSGen *etsg, checker::Type const *const ex void ETSCompiler::Compile(const ir::ETSNewClassInstanceExpression *expr) const { ETSGen *etsg = GetETSGen(); - ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr); - etsg->InitObject(expr, expr->signature_, expr->GetArguments()); + if (expr->TsType()->IsETSAnyType()) { + compiler::RegScope rs(etsg); + auto objReg = etsg->AllocReg(); + expr->GetTypeRef()->Compile(etsg); + etsg->StoreAccumulator(expr->GetTypeRef(), objReg); + etsg->CallAnyNew(expr, expr->GetArguments(), objReg); + } else { + ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr); + etsg->InitObject(expr, expr->signature_, expr->GetArguments()); + } etsg->SetAccumulatorType(expr->TsType()); } @@ -476,6 +490,17 @@ static void CompileLogical(compiler::ETSGen *etsg, const ir::BinaryExpression *e etsg->SetAccumulatorType(expr->TsType()); } +static void CompileAnyInstanceOf(compiler::ETSGen *etsg, const VReg lhs, const ir::Expression *expr) +{ + RegScope rs(etsg); + VReg objReg = etsg->AllocReg(); + expr->Compile(etsg); + etsg->StoreAccumulator(expr, objReg); + etsg->LoadAccumulator(expr, lhs); + etsg->EmitAnyIsInstance(expr, objReg); + etsg->SetAccumulatorType(etsg->Checker()->GlobalETSBooleanType()); +} + static void CompileInstanceof(compiler::ETSGen *etsg, const ir::BinaryExpression *expr) { ES2PANDA_ASSERT(expr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF); @@ -487,7 +512,11 @@ static void CompileInstanceof(compiler::ETSGen *etsg, const ir::BinaryExpression etsg->ApplyConversionAndStoreAccumulator(expr->Left(), lhs, expr->OperationType()); auto target = expr->Right()->TsType(); - etsg->IsInstance(expr, lhs, target); + if (target->IsETSAnyType() && target->AsETSAnyType()->IsTransformedFromGradualType()) { + CompileAnyInstanceOf(etsg, lhs, expr->Right()); + } else { + etsg->IsInstance(expr, lhs, target); + } ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), expr->TsType())); } @@ -662,6 +691,26 @@ bool IsCastCall(checker::Signature *signature) (signature->Params().size() == 1) && signature->Params()[0]->TsType()->IsETSPrimitiveType()); } +void ETSCompiler::CompileAny(const ir::CallExpression *expr, const ir::Expression *callee, + compiler::VReg &calleeReg) const +{ + ETSGen *etsg = GetETSGen(); + auto memberExpr = callee->AsMemberExpression(); + memberExpr->Object()->Compile(etsg); + compiler::VReg objReg = etsg->AllocReg(); + etsg->StoreAccumulator(expr, objReg); + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + if (expr->Signature()->Function() != nullptr && expr->Signature()->Function()->IsStatic()) { + etsg->LoadPropertyByNameAny(memberExpr, objReg, memberExpr->Property()->AsIdentifier()->Name()); + etsg->StoreAccumulator(expr, calleeReg); + etsg->CallAny(callee->AsMemberExpression()->Object(), expr->Arguments(), calleeReg); + } else { + etsg->CallAnyThis(expr, memberExpr->Property()->AsIdentifier(), expr->Arguments(), objReg); + } + auto returnType = expr->Signature()->ReturnType(); + etsg->EmitAnyCheckCast(expr, returnType); +} + void ETSCompiler::EmitCall(const ir::CallExpression *expr, compiler::VReg &calleeReg, checker::Signature *signature) const { @@ -713,7 +762,9 @@ void ETSCompiler::Compile(const ir::CallExpression *expr) const ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr, signature); - if (callee->IsIdentifier()) { + if (callee->IsMemberExpression() && callee->AsMemberExpression()->Object()->TsType()->IsETSAnyType()) { + CompileAny(expr, callee, calleeReg); + } else if (callee->IsIdentifier()) { if (!isStatic) { etsg->LoadThis(expr); etsg->StoreAccumulator(expr, calleeReg); @@ -785,6 +836,17 @@ void ETSCompiler::Compile(const ir::Identifier *expr) const etsg->SetAccumulatorType(smartType); } +static void LoadETSAnyTypeFromMemberExpr(compiler::ETSGen *etsg, const ir::MemberExpression *expr, + compiler::VReg objReg) +{ + if (expr->Property()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { + etsg->LoadByIndexAny(expr, objReg); + } else { + etsg->LoadByValueAny(expr, objReg); + } + etsg->EmitAnyCheckCast(expr, expr->TsType()); +} + bool ETSCompiler::CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpression *expr) { if (!expr->IsComputed()) { @@ -810,6 +872,8 @@ bool ETSCompiler::CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpres auto indexValue = *expr->GetTupleIndexValue(); auto *tupleElementType = objectType->AsETSTupleType()->GetTypeAtIndex(indexValue); etsg->LoadTupleElement(expr, objReg, tupleElementType, indexValue); + } else if (objectType->IsETSAnyType()) { + LoadETSAnyTypeFromMemberExpr(etsg, expr, objReg); } else { ES2PANDA_ASSERT(objectType->IsETSArrayType()); etsg->LoadArrayElement(expr, objReg); @@ -822,6 +886,23 @@ bool ETSCompiler::CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpres return true; } +bool ETSCompiler::CompileAny(compiler::ETSGen *etsg, const ir::MemberExpression *expr) const +{ + if (!etsg->Checker()->GetApparentType(expr->Object()->TsType())->IsETSAnyType()) { + return false; + } + auto ottctx = compiler::TargetTypeContext(etsg, expr->Object()->TsType()); + etsg->CompileAndCheck(expr->Object()); + + compiler::VReg objReg = etsg->AllocReg(); + etsg->StoreAccumulator(expr, objReg); + + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + etsg->LoadPropertyByNameAny(expr, objReg, expr->Property()->AsIdentifier()->Name()); + etsg->EmitAnyCheckCast(expr, expr->TsType()); + return true; +} + void ETSCompiler::Compile(const ir::MemberExpression *expr) const { ETSGen *etsg = GetETSGen(); @@ -832,6 +913,10 @@ void ETSCompiler::Compile(const ir::MemberExpression *expr) const return; } + if (CompileAny(etsg, expr)) { + return; + } + if (HandleArrayTypeLengthProperty(expr, etsg)) { return; } diff --git a/ets2panda/compiler/core/ETSCompiler.h b/ets2panda/compiler/core/ETSCompiler.h index d09df63fd6..868ce81e49 100644 --- a/ets2panda/compiler/core/ETSCompiler.h +++ b/ets2panda/compiler/core/ETSCompiler.h @@ -35,6 +35,7 @@ public: private: void GetDynamicNameParts(const ir::CallExpression *expr, ArenaVector &parts) const; + void CompileAny(const ir::CallExpression *expr, const ir::Expression *callee, compiler::VReg &calleeReg) const; void CompileCastPrimitives(const ir::Expression *expr, checker::Type const *targetType) const; void CompileCast(const ir::TSAsExpression *expr, checker::Type const *targetType) const; void EmitCall(const ir::CallExpression *expr, compiler::VReg &calleeReg, checker::Signature *signature) const; @@ -44,6 +45,7 @@ private: void CompileTupleCreation(const ir::ArrayExpression *tupleInitializer) const; static bool CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpression *expr); + bool CompileAny(compiler::ETSGen *etsg, const ir::MemberExpression *expr) const; ETSGen *GetETSGen() const; }; diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 149c12d4d5..420e085a6a 100644 --- a/ets2panda/compiler/core/ETSGen.cpp +++ b/ets2panda/compiler/core/ETSGen.cpp @@ -15,6 +15,8 @@ #include "ETSGen-inl.h" +#include "compiler/core/regScope.h" +#include "generated/isa.h" #include "generated/signatures.h" #include "ir/base/scriptFunction.h" #include "ir/base/classDefinition.h" @@ -372,6 +374,22 @@ void ETSGen::StoreProperty(const ir::AstNode *const node, const checker::Type *p } } +void ETSGen::StorePropertyByNameAny(const ir::AstNode *const node, const VReg objReg, const util::StringView &fullName) +{ + ES2PANDA_ASSERT(node->IsMemberExpression() && + Checker()->GetApparentType(node->AsMemberExpression()->Object()->TsType())->IsETSAnyType()); + Ra().Emit(node, objReg, fullName); + SetAccumulatorType(node->AsMemberExpression()->TsType()); +} + +void ETSGen::LoadPropertyByNameAny(const ir::AstNode *const node, const VReg objReg, const util::StringView &fullName) +{ + ES2PANDA_ASSERT(node->IsMemberExpression() && + Checker()->GetApparentType(node->AsMemberExpression()->Object()->TsType())->IsETSAnyType()); + Ra().Emit(node, objReg, fullName); + SetAccumulatorType(node->AsMemberExpression()->TsType()); +} + void ETSGen::LoadProperty(const ir::AstNode *const node, const checker::Type *propType, const VReg objReg, const util::StringView &fullName) { @@ -425,6 +443,48 @@ void ETSGen::LoadPropertyByName([[maybe_unused]] const ir::AstNode *const node, #endif // PANDA_WITH_ETS } +void ETSGen::StoreByIndexAny(const ir::MemberExpression *node, VReg objectReg, VReg index) +{ + RegScope rs(this); + + // Store property by index + Ra().Emit(node, objectReg, index); + SetAccumulatorType(Checker()->GlobalVoidType()); +} + +void ETSGen::LoadByIndexAny(const ir::MemberExpression *node, VReg objectReg) +{ + RegScope rs(this); + + VReg indexReg = AllocReg(); + StoreAccumulator(node, indexReg); + + // Get property by index + Ra().Emit(node, objectReg); + SetAccumulatorType(node->TsType()); +} + +void ETSGen::StoreByValueAny(const ir::MemberExpression *node, VReg objectReg, VReg value) +{ + RegScope rs(this); + + // Store property by value + Ra().Emit(node, objectReg, value); + SetAccumulatorType(Checker()->GlobalVoidType()); +} + +void ETSGen::LoadByValueAny(const ir::MemberExpression *node, VReg objectReg) +{ + RegScope rs(this); + + VReg valueReg = AllocReg(); + StoreAccumulator(node, valueReg); + + // Get property by value + Ra().Emit(node, objectReg, valueReg); + SetAccumulatorType(node->TsType()); +} + void ETSGen::CallRangeFillUndefined(const ir::AstNode *const node, checker::Signature *const signature, const VReg thisReg) { @@ -649,6 +709,15 @@ void ETSGen::InternalCheckCast(const ir::AstNode *node, const es2panda::checker: SetAccumulatorType(target); } +// Handle checkcast for interop if it is 1.2 type. +void ETSGen::EmitAnyCheckCast(const ir::AstNode *node, const checker::Type *target) +{ + if (!target->IsETSAnyType() && + (target->IsETSObjectType() || target->IsETSArrayType() || target->IsETSTupleType())) { + InternalCheckCast(node, target); + } +} + // optimized specialization for object and [] targets void ETSGen::CheckedReferenceNarrowingObject(const ir::AstNode *node, const checker::Type *target) { diff --git a/ets2panda/compiler/core/ETSGen.h b/ets2panda/compiler/core/ETSGen.h index 6712e10b91..b10b95c999 100644 --- a/ets2panda/compiler/core/ETSGen.h +++ b/ets2panda/compiler/core/ETSGen.h @@ -21,6 +21,7 @@ #include "compiler/core/ETSfunction.h" #include "compiler/core/targetTypeContext.h" #include "checker/ETSchecker.h" +#include "ir/expressions/identifier.h" #include "util/helpers.h" #include @@ -69,6 +70,15 @@ public: void LoadProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &fullName); + void StoreByIndexAny(const ir::MemberExpression *node, VReg objectReg, VReg index); + void LoadByIndexAny(const ir::MemberExpression *node, VReg objectReg); + + void StoreByValueAny(const ir::MemberExpression *node, VReg objectReg, VReg value); + void LoadByValueAny(const ir::MemberExpression *node, VReg objectReg); + + void StorePropertyByNameAny(const ir::AstNode *const node, const VReg objReg, const util::StringView &fullName); + void LoadPropertyByNameAny(const ir::AstNode *const node, const VReg objReg, const util::StringView &fullName); + void StorePropertyByName(const ir::AstNode *node, VReg objReg, checker::ETSChecker::NamedAccessMeta const &fieldMeta); void LoadPropertyByName(const ir::AstNode *node, VReg objReg, @@ -269,6 +279,7 @@ public: void InternalIsInstance(const ir::AstNode *node, const checker::Type *target); void InternalCheckCast(const ir::AstNode *node, const checker::Type *target); + void EmitAnyCheckCast(const ir::AstNode *node, const checker::Type *target); void CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target); void GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target); @@ -311,6 +322,11 @@ public: #endif // PANDA_WITH_ETS } + void EmitAnyIsInstance(const ir::AstNode *node, VReg objReg) + { + Sa().Emit(node, objReg); + } + void CallExact(const ir::AstNode *node, checker::Signature *signature, const ArenaVector &arguments) { @@ -414,6 +430,22 @@ public: CallDynamicImpl(data, param3, signature, arguments); } + void CallAnyNew(const ir::AstNode *const node, const ArenaVector &arguments, const VReg athis) + { + CallAnyImpl(node, arguments, athis); + } + + void CallAnyThis(const ir::AstNode *const node, const ir::Identifier *ident, + const ArenaVector &arguments, const VReg athis) + { + CallAnyImpl(node, ident, arguments, athis); + } + + void CallAny(const ir::AstNode *const node, const ArenaVector &arguments, const VReg athis) + { + CallAnyImpl(node, arguments, athis); + } + // until a lowering for implicit super is available void CallRangeFillUndefined(const ir::AstNode *const node, checker::Signature *const signature, const VReg thisReg); @@ -555,7 +587,7 @@ private: template void BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs); -// NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty) +// NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty, modernize-loop-convert) #define COMPILE_ARG(idx) \ ES2PANDA_ASSERT((idx) < arguments.size()); \ ES2PANDA_ASSERT((idx) < signature->Params().size() || signature->RestVar() != nullptr); \ @@ -734,7 +766,72 @@ private: } #undef COMPILE_ARG - // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty) + +#define COMPILE_ANY_ARG(idx) \ + ES2PANDA_ASSERT((idx) < arguments.size()); \ + auto *param##idx = arguments[idx]; \ + auto *paramType##idx = param##idx->TsType(); \ + auto ttctx##idx = TargetTypeContext(this, paramType##idx); \ + arguments[idx]->Compile(this); \ + VReg arg##idx = AllocReg(); \ + ApplyConversion(arguments[idx], nullptr); \ + ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx) + + template + void CallAnyImpl(const ir::AstNode *node, const ir::Identifier *ident, + const ArenaVector &arguments, const VReg athis) + { + ES2PANDA_ASSERT(ident != nullptr); + RegScope rs(this); + + switch (arguments.size()) { + case 0U: { + Ra().Emit(node, ident->Name(), athis); + break; + } + case 1U: { + COMPILE_ANY_ARG(0); + Ra().Emit(node, ident->Name(), athis, arg0); + break; + } + default: { + VReg argStart = NextReg(); + for (size_t idx = 0; idx < arguments.size(); idx++) { + COMPILE_ANY_ARG(idx); + } + + Rra().Emit(node, argStart, arguments.size(), ident->Name(), athis, argStart, arguments.size()); + } + } + } + + template + void CallAnyImpl(const ir::AstNode *node, const ArenaVector &arguments, const VReg athis) + { + RegScope rs(this); + + switch (arguments.size()) { + case 0U: { + Ra().Emit(node, athis); + break; + } + case 1U: { + COMPILE_ANY_ARG(0); + Ra().Emit(node, athis, arg0); + break; + } + default: { + VReg argStart = NextReg(); + for (size_t idx = 0; idx < arguments.size(); idx++) { + COMPILE_ANY_ARG(idx); + } + + Rra().Emit(node, argStart, arguments.size(), athis, argStart, arguments.size()); + } + } + } +#undef COMPILE_ANY_ARG + // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty, modernize-loop-convert) void ToBinaryResult(const ir::AstNode *node, Label *ifFalse); diff --git a/ets2panda/compiler/core/ETSemitter.cpp b/ets2panda/compiler/core/ETSemitter.cpp index 81ff05cfe2..81a59f6310 100644 --- a/ets2panda/compiler/core/ETSemitter.cpp +++ b/ets2panda/compiler/core/ETSemitter.cpp @@ -36,6 +36,7 @@ #include "checker/types/signature.h" #include "checker/ETSchecker.h" #include "checker/types/type.h" +#include "checker/types/gradualType.h" #include "checker/types/ets/types.h" #include "checker/types/ets/etsPartialTypeParameter.h" #include "public/public.h" @@ -94,6 +95,9 @@ static uint32_t TranslateModifierFlags(ir::ModifierFlags modifierFlags) static pandasm::Type PandasmTypeWithRank(checker::Type const *type, uint32_t rank = 0) { + if (type->IsGradualType()) { + return PandasmTypeWithRank(type->AsGradualType()->GetBaseType()); + } if (type->IsETSTypeParameter()) { return PandasmTypeWithRank(type->AsETSTypeParameter()->GetConstraintType()); } @@ -506,7 +510,9 @@ void ETSEmitter::GenGlobalArrayRecord(const checker::ETSArrayType *arrayType, ch void ETSEmitter::GenInterfaceRecord(const ir::TSInterfaceDeclaration *interfaceDecl, bool external) { - auto *baseType = interfaceDecl->TsType()->AsETSObjectType(); + auto *baseType = interfaceDecl->TsType()->IsGradualType() + ? interfaceDecl->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : interfaceDecl->TsType()->AsETSObjectType(); auto interfaceRecord = pandasm::Record(interfaceDecl->InternalName().Mutf8(), Program()->lang); uint32_t accessFlags = ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE; @@ -603,8 +609,9 @@ void ETSEmitter::GenClassRecord(const ir::ClassDefinition *classDef, bool extern uint32_t accessFlags = GetAccessFlags(classDef); classRecord.metadata->SetAccessFlags(accessFlags); classRecord.sourceFile = std::string {Context()->parserProgram->VarBinder()->Program()->RelativeFilePath()}; - - auto *baseType = classDef->TsType()->AsETSObjectType(); + auto *baseType = classDef->TsType()->IsGradualType() + ? classDef->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : classDef->TsType()->AsETSObjectType(); GenClassInheritedFields(baseType, classRecord); for (const auto *prop : classDef->Body()) { if (!prop->IsClassProperty()) { diff --git a/ets2panda/compiler/core/ETSfunction.cpp b/ets2panda/compiler/core/ETSfunction.cpp index 4edd9d31b2..3b2d6ab2cb 100644 --- a/ets2panda/compiler/core/ETSfunction.cpp +++ b/ets2panda/compiler/core/ETSfunction.cpp @@ -33,14 +33,16 @@ #include "ir/ts/tsEnumDeclaration.h" #include "ir/ts/tsEnumMember.h" #include "checker/types/ets/types.h" - +#include "checker/types/gradualType.h" namespace ark::es2panda::compiler { // #22952: this should have been done in lowering void ETSFunction::CallImplicitCtor(ETSGen *etsg) { RegScope rs(etsg); - auto *superType = etsg->ContainingObjectType()->SuperType(); + auto *type = etsg->ContainingObjectType()->SuperType(); + auto superType = + type->IsGradualType() ? type->AsGradualType()->GetBaseType()->AsETSObjectType() : type->AsETSObjectType(); if (superType == nullptr) { etsg->CallExact(etsg->RootNode(), Signatures::BUILTIN_OBJECT_CTOR, etsg->GetThisReg()); diff --git a/ets2panda/compiler/lowering/ets/dynamicImport.cpp b/ets2panda/compiler/lowering/ets/dynamicImport.cpp new file mode 100644 index 0000000000..3265cba95d --- /dev/null +++ b/ets2panda/compiler/lowering/ets/dynamicImport.cpp @@ -0,0 +1,250 @@ +/** + * 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 "dynamicImport.h" +#include +#include +#include "checker/ETSchecker.h" +#include "ir/astNode.h" + +#include "compiler/lowering/util.h" +#include "compiler/lowering/scopesInit/scopesInitPhase.h" + +namespace ark::es2panda::compiler { + +using AstNodePtr = ir::AstNode *; +static constexpr std::string_view LAZY_IMPORT_OBJECT_SUFFIX = "%%lazyImportObject-"; +static constexpr std::string_view FIELD_NAME = "value"; + +ir::ClassDeclaration *GetOrCreateLazyImportObjectClass(ark::ArenaAllocator *allocator, + ir::ETSImportDeclaration *importDecl, parser::Program *program) +{ + auto checker = program->Checker()->AsETSChecker(); + auto globalClass = program->GlobalClass(); + auto varbinder = checker->VarBinder()->AsETSBinder(); + auto sourceProgram = checker->SelectEntryOrExternalProgram(varbinder, importDecl->DeclPath()); + + const std::string classNameStr = std::string(LAZY_IMPORT_OBJECT_SUFFIX) + sourceProgram->ModuleName().Mutf8(); + const util::UString className(classNameStr, allocator); + const auto nameView = className.View().Mutf8(); + + auto &classBody = globalClass->BodyForUpdate(); + auto classIt = std::find_if(classBody.begin(), classBody.end(), [&nameView](ir::AstNode *node) { + if (!node->IsClassDeclaration()) + return false; + return node->AsClassDeclaration()->Definition()->Ident()->Name().Mutf8() == nameView; + }); + if (classIt != classBody.end()) { + return (*classIt)->AsClassDeclaration(); + } + + auto *ident = allocator->New(className.View(), allocator); + auto *classDef = util::NodeAllocator::ForceSetParent( + allocator, allocator, ident, ir::ClassDefinitionModifiers::CLASS_DECL, ir::ModifierFlags::ABSTRACT, + Language(Language::Id::ETS)); + + classDef->SetLazyImportObjectClass(); + return util::NodeAllocator::ForceSetParent(allocator, classDef, allocator); +} + +static void AddImportInitializationStatement(public_lib::Context *ctx, ir::ETSImportDeclaration *import, + ArenaVector *statements, util::StringView className) +{ + auto allocator = ctx->GetChecker()->AsETSChecker()->ProgramAllocator(); + const auto builtin = compiler::Signatures::Dynamic::LoadModuleBuiltin(import->Language()); + auto [builtinClassName, builtinMethodName] = util::Helpers::SplitSignature(builtin); + + auto *classId = allocator->New(builtinClassName, allocator); + auto *methodId = allocator->New(builtinMethodName, allocator); + auto *callee = util::NodeAllocator::ForceSetParent( + allocator, classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + util::StringView ohmUrl = util::UString(import->OhmUrl(), allocator).View(); + if (ohmUrl.Empty()) { + ohmUrl = import->ResolvedSource(); + if (ark::os::file::File::IsRegularFile(ohmUrl.Mutf8())) { + ohmUrl = util::UString(ark::os::RemoveExtension(ohmUrl.Mutf8()), allocator).View(); + } + } + + ArenaVector callParams(allocator->Adapter()); + callParams.push_back(allocator->New(ohmUrl)); + + auto *loadCall = util::NodeAllocator::ForceSetParent(allocator, callee, std::move(callParams), + nullptr, false); + auto *moduleClassId = allocator->New(className, allocator); + auto *fieldId = allocator->New(FIELD_NAME, allocator); + auto *property = util::NodeAllocator::ForceSetParent( + allocator, moduleClassId, fieldId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + moduleClassId->SetParent(property); + fieldId->SetParent(property); + + auto *initializer = util::NodeAllocator::ForceSetParent( + allocator, property, loadCall, lexer::TokenType::PUNCTUATOR_SUBSTITUTION); + + statements->push_back(util::NodeAllocator::ForceSetParent(allocator, initializer)); +} + +checker::Type *CreateModuleObjectType(public_lib::Context *ctx, ir::ETSImportDeclaration *importDecl) +{ + auto checker = ctx->GetChecker()->AsETSChecker(); + auto varbinder = static_cast(checker->VarBinder()->AsETSBinder()); + auto allocator = checker->ProgramAllocator(); + + const auto importPath = importDecl->DeclPath(); + auto program = checker->SelectEntryOrExternalProgram(varbinder, importPath); + if (program == nullptr) { + return checker->GlobalTypeError(); + } + + const auto moduleName = program->ModuleName(); + const auto internalNameStr = std::string(moduleName.Mutf8()) + .append(compiler::Signatures::METHOD_SEPARATOR) + .append(compiler::Signatures::ETS_GLOBAL); + const util::UString internalName(internalNameStr, allocator); + + auto *moduleObjectType = allocator->New( + allocator, moduleName, internalName.View(), + std::make_tuple(program->GlobalClass(), checker::ETSObjectFlags::CLASS, checker->Relation())); + + auto *rootDecl = allocator->New(moduleName); + auto *rootVar = allocator->New(rootDecl, varbinder::VariableFlags::NONE); + rootVar->SetTsType(moduleObjectType); + checker->SetPropertiesForModuleObject(moduleObjectType, importPath, nullptr); + + return moduleObjectType; +} + +static void BuildLazyImportObject(public_lib::Context *ctx, ir::ETSImportDeclaration *importDecl, + parser::Program *program, + ArenaUnorderedMap &moduleMap, + ArenaUnorderedMap &varMap) +{ + auto checker = ctx->GetChecker()->AsETSChecker(); + auto varBinder = checker->VarBinder()->AsETSBinder(); + auto allocator = checker->ProgramAllocator(); + + auto declProgram = checker->SelectEntryOrExternalProgram(varBinder, importDecl->DeclPath()); + if (!declProgram->IsDeclForDynamicStaticInterop()) { + return; + } + + auto *classDecl = GetOrCreateLazyImportObjectClass(allocator, importDecl, program); + for (auto specifier : importDecl->Specifiers()) { + auto var = specifier->AsImportSpecifier()->Imported()->Variable(); + var->AddFlag(varbinder::VariableFlags::DYNAMIC); + varMap.insert({var, classDecl->Definition()}); + } + + const auto className = classDecl->Definition()->Ident()->Name(); + if (declProgram->IsASTChecked()) { + checker->SetPropertiesForModuleObject(moduleMap.find(className)->second, importDecl->DeclPath(), nullptr); + return; + } + + auto *objType = CreateModuleObjectType(ctx, importDecl)->AsETSObjectType(); + moduleMap.insert({className, objType}); + + objType->AddObjectFlag(checker::ETSObjectFlags::LAZY_IMPORT_OBJECT); + auto moduleType = checker->CreateGradualType(objType, Language::Id::JS); + + auto parser = ctx->parser->AsETSParser(); + auto *typeAnnotation = allocator->New(moduleType, allocator); + auto *classProp = parser->CreateFormattedClassFieldDefinition(std::string {FIELD_NAME} + ": @@T1", typeAnnotation) + ->AsClassProperty(); + typeAnnotation->SetParent(classProp); + classProp->AddModifier(ir::ModifierFlags::CONST | ir::ModifierFlags::STATIC); + + classDecl->Definition()->EmplaceBody(classProp); + classProp->SetParent(classDecl->Definition()); + + auto initializer = checker->CreateClassStaticInitializer( + [ctx, importDecl, className](ArenaVector *statements, + [[maybe_unused]] ArenaVector *params) { + AddImportInitializationStatement(ctx, importDecl, statements, className); + }); + + classDecl->Definition()->EmplaceBody(initializer); + initializer->SetParent(classDecl->Definition()); + + for (auto specifier : importDecl->Specifiers()) { + varMap.insert({specifier->AsImportSpecifier()->Imported()->Variable(), classDecl->Definition()}); + } + + program->GlobalClass()->EmplaceBody(classDecl); + classDecl->SetParent(program->GlobalClass()); + + auto lexScope = varbinder::LexicalScope::Enter(varBinder, program->GlobalClassScope()); + InitScopesPhaseETS::RunExternalNode(classDecl, varBinder); + varBinder->ResolveReferencesForScopeWithContext(classDecl, varBinder->TopScope()); + classDecl->Check(checker); +} + +static AstNodePtr TransformIdentifier(ir::Identifier *ident, public_lib::Context *ctx, + const ArenaUnorderedMap &varMap) +{ + auto checker = ctx->GetChecker()->AsETSChecker(); + auto varBinder = checker->VarBinder()->AsETSBinder(); + auto allocator = checker->ProgramAllocator(); + + const auto parent = ident->Parent(); + if (parent->IsImportSpecifier() || parent->IsScriptFunction() || parent->IsMethodDefinition()) { + return ident; + } + + auto varIt = varMap.find(ident->Variable()); + if (varIt == varMap.end()) { + return ident; + } + + auto *leftId = allocator->New(varIt->second->Ident()->Name(), allocator); + auto *rightId = allocator->New(FIELD_NAME, allocator); + + auto *expr = util::NodeAllocator::ForceSetParent( + allocator, leftId, rightId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + auto *memberExpr = util::NodeAllocator::ForceSetParent( + allocator, expr, ident, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + memberExpr->SetParent(parent); + CheckLoweredNode(varBinder, checker, memberExpr); + + return memberExpr; +} + +bool DynamicImport::PerformForModule(public_lib::Context *ctx, parser::Program *program) +{ + auto allocator = ctx->Allocator(); + ArenaUnorderedMap varMap {allocator->Adapter()}; + ArenaUnorderedMap moduleMap {allocator->Adapter()}; + + auto dynamicImports = program->VarBinder()->AsETSBinder()->DynamicImports(); + for (auto *importDecl : dynamicImports) { + BuildLazyImportObject(ctx, importDecl, program, moduleMap, varMap); + } + + program->Ast()->TransformChildrenRecursively( + [ctx, &varMap](ir::AstNode *node) -> AstNodePtr { + if (node->IsIdentifier() && node->AsIdentifier()->Variable() != nullptr) { + return TransformIdentifier(node->AsIdentifier(), ctx, varMap); + } + return node; + }, + Name()); + + return true; +} +} // namespace ark::es2panda::compiler \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/dynamicImport.h b/ets2panda/compiler/lowering/ets/dynamicImport.h new file mode 100644 index 0000000000..78af90c013 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/dynamicImport.h @@ -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. + */ + +#ifndef ES2PANDA_COMPILER_LOWERING_LAZY_IMPORT_OBJECT_H +#define ES2PANDA_COMPILER_LOWERING_LAZY_IMPORT_OBJECT_H + +#include "compiler/lowering/phase.h" +#include "utils/arena_containers.h" + +namespace ark::es2panda::compiler { + +class DynamicImport : public PhaseForBodies { +public: + std::string_view Name() const override + { + return "DynamicImport"; + } + + bool PerformForModule(public_lib::Context *ctx, parser::Program *program) override; +}; +} // namespace ark::es2panda::compiler + +#endif // ES2PANDA_COMPILER_LOWERING_TYPE_FOR_LOWERING_H \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp new file mode 100644 index 0000000000..60474ea17d --- /dev/null +++ b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp @@ -0,0 +1,141 @@ +/* + * 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 "gradualTypeNarrowing.h" + +#include "checker/ETSchecker.h" +#include "checker/types/ets/etsTupleType.h" +#include "es2panda.h" +#include "ir/astNode.h" +#include "ir/opaqueTypeNode.h" +#include "ir/typed.h" +#include "util/language.h" + +namespace ark::es2panda::compiler { + +checker::Type *GradualTypeNarrowing::TransformType(checker::Type *type, + const std::function &func) +{ + if (type->IsETSFunctionType()) { + auto funcType = type->AsETSFunctionType(); + for (auto sig : funcType->CallSignaturesOfMethodOrArrow()) { + sig->SetReturnType(TransformType(sig->ReturnType(), func)); + for (auto var : sig->Params()) { + var->SetTsType(TransformType(var->TsType(), func)); + } + + if (sig->RestVar() != nullptr) { + sig->RestVar()->SetTsType(TransformType(sig->RestVar()->TsType(), func)); + } + } + } + + if (type->IsETSUnionType()) { + auto unionType = type->AsETSUnionType(); + ArenaVector types {checker_->ProgramAllocator()->Adapter()}; + for (auto ctype : unionType->ConstituentTypes()) { + types.push_back(TransformType(ctype, func)); + } + type = checker_->CreateETSUnionType(std::move(types)); + } + + if (type->IsETSArrayType()) { + auto arrayType = type->AsETSArrayType(); + arrayType->SetElementType(TransformType(arrayType->ElementType(), func)); + } + + if (type->IsETSResizableArrayType()) { + auto arrayType = type->AsETSResizableArrayType(); + arrayType->SetElementType(TransformType(arrayType->ElementType(), func)); + } + + if (type->IsETSTupleType()) { + auto tupleType = type->AsETSTupleType(); + ArenaVector types {checker_->ProgramAllocator()->Adapter()}; + for (auto ctype : tupleType->GetTupleTypesList()) { + types.push_back(TransformType(ctype, func)); + } + type = checker_->ProgramAllocator()->New(checker_, std::move(types)); + } + + // if (type->IsETSTypeParameter()) { + // return type->AsETSTypeParameter()->GetConstraintType(); + // } + + return func(type); +} + +void GradualTypeNarrowing::NarrowGradualType(ir::AstNode *node) +{ + auto typedNode = node->AsTyped(); + auto typeTransformFunc = [this](checker::Type *type) -> checker::TypePtr { + if (type->IsGradualType()) { + return this->checker_->CreateETSAnyType(true); + } + return type; + }; + + if (typedNode->TsType() != nullptr) { + typedNode->SetTsType(TransformType(typedNode->TsType(), typeTransformFunc)); + } + + auto var = node->Variable(); + if (var != nullptr && var->TsType() != nullptr) { + var->SetTsType(TransformType(var->TsType(), typeTransformFunc)); + } +} + +ir::AstNode *GradualTypeNarrowing::ProcessGradualTypeNode(ir::ETSTypeReference *node) +{ + auto type = node->GetType(checker_); + if (!type->IsGradualType()) { + return node; + } + + // Only narrow explicit type annotation of gradual to T or to Any + if (!node->Part()->Name()->IsIdentifier() || + !(node->Part()->Name()->AsIdentifier()->Name() == compiler::Signatures::GRADUAL_TYPE_NAME)) { + return node; + } + auto loweredNode = context_->AllocNode(checker_->GlobalETSAnyType(), context_->Allocator()); + loweredNode->SetRange(node->Range()); + loweredNode->SetParent(node->Parent()); + loweredNode->SetTsType(checker_->CreateETSAnyType(true)); + return loweredNode; +} + +bool GradualTypeNarrowing::PerformForModule(public_lib::Context *ctx, parser::Program *program) +{ + context_ = ctx; + checker_ = ctx->GetChecker()->AsETSChecker(); + + program->Ast()->TransformChildrenRecursively( + // CC-OFFNXT(G.FMT.14-CPP) project code style + [this](ir::AstNode *ast) -> checker::AstNodePtr { + if (ast->IsETSTypeReference()) { + return ProcessGradualTypeNode(ast->AsETSTypeReference()); + } + return ast; + }, + Name()); + + program->Ast()->IterateRecursively([this](ir::AstNode *ast) { + if (ast->IsTyped()) { + NarrowGradualType(ast); + } + }); + return true; +} +} // namespace ark::es2panda::compiler \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h new file mode 100644 index 0000000000..e4a09c4ab9 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h @@ -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. + */ + +#ifndef ES2PANDA_GRADUAL_TYPE_NARROWING_H +#define ES2PANDA_GRADUAL_TYPE_NARROWING_H + +#include "compiler/lowering/phase.h" +#include "ir/typeNode.h" + +namespace ark::es2panda::compiler { + +class GradualTypeNarrowing : public PhaseForBodies { +public: + std::string_view Name() const override + { + return "GradualTypeNarrowing"; + } + + bool PerformForModule(public_lib::Context *ctx, parser::Program *program) override; + +private: + checker::Type *TransformType(checker::Type *type, const std::function &func); + void NarrowGradualType(ir::AstNode *node); + ir::AstNode *ProcessGradualTypeNode(ir::ETSTypeReference *node); + + public_lib::Context *context_ {nullptr}; + checker::ETSChecker *checker_ {nullptr}; +}; +} // namespace ark::es2panda::compiler + +#endif \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp index c88ab3572b..09f44143e9 100644 --- a/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp @@ -35,15 +35,13 @@ std::string_view InterfaceObjectLiteralLowering::Name() const static inline bool IsInterfaceType(const checker::Type *type) { return type != nullptr && type->IsETSObjectType() && - type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::INTERFACE) && - !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC); + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::INTERFACE) && !type->IsGradualType(); } static inline bool IsAbstractClassType(const checker::Type *type) { return type != nullptr && type->IsETSObjectType() && - type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT) && - !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC); + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT) && !type->IsGradualType(); } static ir::AstNode *CreateAnonClassImplCtor(public_lib::Context *ctx, ArenaVector &readonlyFields) @@ -187,7 +185,7 @@ static void FillAnonClassBody(public_lib::Context *ctx, ArenaVectorBody()->Body(), readonlyFields, interfaceType); - for (auto *extendedIface : ifaceNode->TsType()->AsETSObjectType()->Interfaces()) { + for (auto *extendedIface : ifaceNode->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType()->Interfaces()) { FillAnonClassBody(ctx, classBody, extendedIface->GetDeclNode()->AsTSInterfaceDeclaration(), readonlyFields, extendedIface); } @@ -232,7 +230,7 @@ static void GenerateAnonClassTypeFromInterface(public_lib::Context *ctx, ir::TSI auto *classDecl = checker->BuildClass(anonClassName.View(), classBodyBuilder); RefineSourceRanges(classDecl); auto *classDef = classDecl->Definition(); - auto *classType = classDef->TsType()->AsETSObjectType(); + auto *classType = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); classDef->SetAnonymousModifier(); classDecl->SetRange(ifaceNode->Range()); diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index c62bcac5c1..9c4d84677b 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -1143,6 +1143,9 @@ static bool IsFunctionOrMethodCall(checker::ETSChecker *checker, ir::CallExpress static ir::AstNode *InsertInvokeCall(public_lib::Context *ctx, ir::CallExpression *call) { + if (call->IsDynamicCall()) { + return call; + } auto *allocator = ctx->allocator; auto *checker = ctx->GetChecker()->AsETSChecker(); auto *varBinder = checker->VarBinder()->AsETSBinder(); @@ -1235,14 +1238,16 @@ static ir::AstNode *BuildLambdaClassWhenNeeded(public_lib::Context *ctx, ir::Ast // We are running this lowering only for ETS files // so it is correct to pass ETS extension here to isReference() if (id->IsReference(ScriptExtension::ETS) && id->TsType() != nullptr && id->TsType()->IsETSFunctionType() && - !IsInCalleePosition(id) && !IsEnumFunctionCall(id) && IsValidFunctionDeclVar(var)) { + !id->Variable()->HasFlag(varbinder::VariableFlags::DYNAMIC) && !IsInCalleePosition(id) && + !IsEnumFunctionCall(id) && IsValidFunctionDeclVar(var)) { return ConvertFunctionReference(ctx, id); } } if (node->IsMemberExpression()) { auto *mexpr = node->AsMemberExpression(); if (mexpr->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS && mexpr->TsType() != nullptr && - mexpr->TsType()->IsETSFunctionType() && mexpr->Object()->TsType()->IsETSObjectType()) { + !mexpr->TsType()->IsGradualType() && mexpr->TsType()->IsETSFunctionType() && + mexpr->Object()->TsType()->IsETSObjectType()) { ES2PANDA_ASSERT(mexpr->Property()->IsIdentifier()); auto *var = mexpr->Object()->TsType()->AsETSObjectType()->GetProperty( mexpr->Property()->AsIdentifier()->Name(), diff --git a/ets2panda/compiler/lowering/ets/localClassLowering.cpp b/ets2panda/compiler/lowering/ets/localClassLowering.cpp index febb95ab47..0fb500e0aa 100644 --- a/ets2panda/compiler/lowering/ets/localClassLowering.cpp +++ b/ets2panda/compiler/lowering/ets/localClassLowering.cpp @@ -267,7 +267,7 @@ bool LocalClassConstructionPhase::PerformForModule(public_lib::Context *ctx, par program->Ast()->IterateRecursivelyPostorder([&](ir::AstNode *ast) { if (ast->IsETSNewClassInstanceExpression()) { auto *newExpr = ast->AsETSNewClassInstanceExpression(); - checker::Type *calleeType = newExpr->GetTypeRef()->Check(checker); + checker::Type *calleeType = newExpr->GetTypeRef()->Check(checker)->MaybeBaseTypeOfGradualType(); ES2PANDA_ASSERT(calleeType->IsETSObjectType()); auto *calleeObj = calleeType->AsETSObjectType(); diff --git a/ets2panda/compiler/lowering/ets/objectIndexAccess.cpp b/ets2panda/compiler/lowering/ets/objectIndexAccess.cpp index 5e42c84c84..9c81f38d7a 100644 --- a/ets2panda/compiler/lowering/ets/objectIndexAccess.cpp +++ b/ets2panda/compiler/lowering/ets/objectIndexAccess.cpp @@ -24,6 +24,7 @@ #include "objectIndexAccess.h" #include "checker/ETSchecker.h" +#include "checker/types/typeFlag.h" #include "compiler/lowering/util.h" #include "parser/ETSparser.h" #include "util/options.h" @@ -110,10 +111,11 @@ bool ObjectIndexLowering::PerformForModule(public_lib::Context *ctx, parser::Pro program->Ast()->TransformChildrenRecursively( // CC-OFFNXT(G.FMT.14-CPP) project code style [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * { - if (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->Left()->IsMemberExpression() && - ast->AsAssignmentExpression()->Left()->AsMemberExpression()->Kind() == - ir::MemberExpressionKind::ELEMENT_ACCESS) { - if (ast->AsAssignmentExpression()->Left()->AsMemberExpression()->ObjType() != nullptr) { + if (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->Left()->IsMemberExpression()) { + auto memberExpr = ast->AsAssignmentExpression()->Left()->AsMemberExpression(); + if (memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS && + memberExpr->AsMemberExpression()->ObjType() != nullptr && + !memberExpr->Object()->TsType()->IsGradualType()) { return ProcessIndexSetAccess(parser, checker, ast->AsAssignmentExpression()); } } @@ -124,9 +126,10 @@ bool ObjectIndexLowering::PerformForModule(public_lib::Context *ctx, parser::Pro program->Ast()->TransformChildrenRecursively( // CC-OFFNXT(G.FMT.14-CPP) project code style [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * { - if (ast->IsMemberExpression() && - ast->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) { - if (ast->AsMemberExpression()->ObjType() != nullptr) { + if (ast->IsMemberExpression()) { + auto memberExpr = ast->AsMemberExpression(); + if (memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS && + memberExpr->ObjType() != nullptr && !memberExpr->Object()->TsType()->IsGradualType()) { return ProcessIndexGetAccess(parser, checker, ast->AsMemberExpression()); } } @@ -141,9 +144,10 @@ bool ObjectIndexLowering::PostconditionForModule([[maybe_unused]] public_lib::Co const parser::Program *program) { return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) { - if (ast->IsMemberExpression() && - ast->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) { - if (auto const *const objectType = ast->AsMemberExpression()->ObjType(); objectType != nullptr) { + if (ast->IsMemberExpression()) { + auto memberExpr = ast->AsMemberExpression(); + if (memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS && memberExpr->ObjType() != nullptr && + !memberExpr->Object()->TsType()->IsGradualType()) { return true; } } diff --git a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp index d0f415917d..0fe7a0ca64 100644 --- a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp @@ -297,8 +297,7 @@ bool ObjectLiteralLowering::PerformForModule(public_lib::Context *ctx, parser::P // Skip processing invalid and dynamic objects if (ast->IsObjectExpression()) { auto *exprType = ast->AsObjectExpression()->TsType(); - if (exprType != nullptr && exprType->IsETSObjectType() && - !exprType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC)) { + if (exprType != nullptr && exprType->IsETSObjectType()) { return HandleObjectLiteralLowering(ctx, ast->AsObjectExpression()); } } @@ -314,8 +313,7 @@ bool ObjectLiteralLowering::PostconditionForModule([[maybe_unused]] public_lib:: { // In all object literal contexts (except dynamic) a substitution should take place return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) -> bool { - return ast->IsObjectExpression() && ast->AsObjectExpression()->TsType()->IsETSObjectType() && - !ast->AsObjectExpression()->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC); + return ast->IsObjectExpression() && ast->AsObjectExpression()->TsType()->IsETSObjectType(); }); } diff --git a/ets2panda/compiler/lowering/ets/optionalArgumentsLowering.cpp b/ets2panda/compiler/lowering/ets/optionalArgumentsLowering.cpp index 94ce179427..ea6d19e5b1 100644 --- a/ets2panda/compiler/lowering/ets/optionalArgumentsLowering.cpp +++ b/ets2panda/compiler/lowering/ets/optionalArgumentsLowering.cpp @@ -43,13 +43,6 @@ static void TransformArguments(public_lib::Context *ctx, ir::Expression *callLik return; } ES2PANDA_ASSERT(signature->ArgCount() >= signature->MinArgCount()); - if (arguments.size() < signature->MinArgCount()) { // #22952: workaround for dynamic types - auto callee = callLike->IsCallExpression() ? callLike->AsCallExpression()->Callee() - : callLike->AsETSNewClassInstanceExpression()->GetTypeRef(); - if (callee->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG)) { - return; - } - } ES2PANDA_ASSERT((callLike->IsCallExpression() && callLike->AsCallExpression()->IsTrailingCall()) || arguments.size() >= signature->MinArgCount()); @@ -70,12 +63,14 @@ bool OptionalArgumentsLowering::PerformForModule(public_lib::Context *ctx, parse program->Ast()->TransformChildrenRecursivelyPreorder( // CC-OFFNXT(G.FMT.14-CPP) project code style [ctx](ir::AstNode *const node) -> ir::AstNode * { - if (node->IsCallExpression()) { + if (node->IsCallExpression() && !node->AsCallExpression()->IsDynamicCall()) { auto callExpr = node->AsCallExpression(); callExpr->IsTrailingCall() ? TransformArgumentsForTrailingLambda(ctx, callExpr->AsCallExpression(), callExpr->Signature()) : TransformArguments(ctx, callExpr, callExpr->Signature(), callExpr->Arguments()); - } else if (node->IsETSNewClassInstanceExpression()) { + } else if (node->IsETSNewClassInstanceExpression() && + node->AsETSNewClassInstanceExpression()->GetSignature()->Function()->Language() != + Language::Id::ETS) { auto newExpr = node->AsETSNewClassInstanceExpression(); TransformArguments(ctx, newExpr, newExpr->GetSignature(), newExpr->GetArguments()); } diff --git a/ets2panda/compiler/lowering/ets/restArgsLowering.cpp b/ets2panda/compiler/lowering/ets/restArgsLowering.cpp index f0435fb4c0..9fe4183e9b 100644 --- a/ets2panda/compiler/lowering/ets/restArgsLowering.cpp +++ b/ets2panda/compiler/lowering/ets/restArgsLowering.cpp @@ -200,7 +200,9 @@ ir::CallExpression *RestArgsLowering::TransformCallExpressionWithRestArgs(ir::Ca public_lib::Context *context) { checker::Type *calleeType = callExpr->Callee()->TsType(); - if (calleeType == nullptr || calleeType->IsETSArrowType()) { + if (calleeType == nullptr || calleeType->IsETSArrowType() || + (callExpr->Callee()->IsMemberExpression() && callExpr->Callee()->AsMemberExpression()->PropVar() != nullptr && + callExpr->Callee()->AsMemberExpression()->PropVar()->HasFlag(varbinder::VariableFlags::DYNAMIC))) { return callExpr; } diff --git a/ets2panda/compiler/lowering/ets/unboxLowering.cpp b/ets2panda/compiler/lowering/ets/unboxLowering.cpp index 2f60dd17e0..1962e5bed3 100644 --- a/ets2panda/compiler/lowering/ets/unboxLowering.cpp +++ b/ets2panda/compiler/lowering/ets/unboxLowering.cpp @@ -17,6 +17,7 @@ #include "ir/visitor/IterateAstVisitor.h" #include "checker/ETSchecker.h" #include "checker/types/ets/etsTupleType.h" +#include "checker/types/typeFlag.h" #include "checker/types/globalTypesHolder.h" #include "compiler/lowering/util.h" @@ -241,7 +242,7 @@ static void NormalizeAllTypes(UnboxContext *uctx, ir::AstNode *ast) return child; } auto typeNodeType = child->AsExpression()->AsTypeNode()->GetType(uctx->checker); - if (typeNodeType == nullptr) { + if (typeNodeType == nullptr || typeNodeType->IsGradualType()) { return child; } auto r = uctx->allocator->New(NormalizeType(uctx, typeNodeType), @@ -318,12 +319,20 @@ static void HandleScriptFunctionHeader(UnboxContext *uctx, ir::ScriptFunction *f uctx->varbinder->BuildFunctionName(func); } -static void HandleClassProperty(UnboxContext *uctx, ir::ClassProperty *prop) +static void HandleClassProperty(UnboxContext *uctx, ir::ClassProperty *prop, bool forceUnbox = false) { auto *propType = prop->TsType(); if (propType == nullptr) { propType = prop->Key()->Variable()->TsType(); } + // Primitive Types from JS should be Boxed, but in case of annotation, it should be unboxed. + ir::AstNode *node = prop; + while (node != nullptr && !node->IsETSModule()) { + node = node->Parent(); + } + if (node != nullptr && node->AsETSModule()->Program()->IsDeclForDynamicStaticInterop() && !forceUnbox) { + return; + } ES2PANDA_ASSERT(propType != nullptr); if (IsUnboxingApplicable(propType) && prop->Key()->IsIdentifier()) { auto *unboxedType = MaybeRecursivelyUnboxType(uctx, propType); @@ -860,9 +869,8 @@ struct UnboxVisitor : public ir::visitor::EmptyAstVisitor { // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP, G.FUD.05) solid logic void VisitCallExpression(ir::CallExpression *call) override { - auto *func = call->Signature()->Function(); - if (func == nullptr) { - // some lambda call, all arguments and return type need to be boxed + if (!call->Signature()->HasFunction() || call->Signature()->Function()->Language() == Language::Id::JS) { + // some lambda call and dynamic call to js, all arguments and return type need to be boxed // NOLINTNEXTLINE(modernize-loop-convert) for (size_t i = 0; i < call->Arguments().size(); i++) { auto *arg = call->Arguments()[i]; @@ -871,6 +879,8 @@ struct UnboxVisitor : public ir::visitor::EmptyAstVisitor { return; } + auto *func = call->Signature()->Function(); + HandleDeclarationNode(uctx_, func); // NOLINTNEXTLINE(modernize-loop-convert) for (size_t i = 0; i < call->Arguments().size(); i++) { @@ -931,6 +941,16 @@ struct UnboxVisitor : public ir::visitor::EmptyAstVisitor { void VisitETSNewClassInstanceExpression(ir::ETSNewClassInstanceExpression *call) override { auto *func = call->GetSignature()->Function(); + if (func == nullptr || func->Language() == Language::Id::JS) { + // For dynamic call to js, all arguments and return type need to be boxed + // NOLINTNEXTLINE(modernize-loop-convert) + for (size_t i = 0; i < call->GetArguments().size(); i++) { + auto *arg = call->GetArguments()[i]; + call->GetArguments()[i] = AdjustType(uctx_, arg, uctx_->checker->MaybeBoxType(arg->TsType())); + } + return; + } + HandleDeclarationNode(uctx_, func); for (size_t i = 0; i < call->GetArguments().size(); i++) { @@ -1172,7 +1192,7 @@ struct UnboxVisitor : public ir::visitor::EmptyAstVisitor { if (mexpr->Property()->Variable()->Declaration() != nullptr && mexpr->Property()->Variable()->Declaration()->Node() != nullptr && mexpr->Property()->Variable()->Declaration()->Node()->IsTyped() && - mexpr->Property()->Variable()->Declaration()->Node()->AsTyped()->TsType() != nullptr) { + !mexpr->Object()->TsType()->IsGradualType()) { HandleDeclarationNode(uctx_, mexpr->Property()->Variable()->Declaration()->Node()); propType = mexpr->Property()->Variable()->Declaration()->Node()->AsTyped()->TsType(); } else if (mexpr->Property()->Variable()->TsType() != nullptr) { @@ -1463,7 +1483,7 @@ static void VisitExternalPrograms(UnboxVisitor *visitor, parser::Program *progra auto annotationIterator = [visitor](auto *child) { if (child->IsClassProperty()) { auto prop = child->AsClassProperty(); - HandleClassProperty(visitor->uctx_, prop); + HandleClassProperty(visitor->uctx_, prop, true); if (prop->Value() != nullptr) { ES2PANDA_ASSERT(prop->Value()->IsLiteral() || prop->Value()->IsArrayExpression() || (prop->Value()->IsTyped() && prop->Value()->AsTyped()->TsType()->IsETSEnumType())); diff --git a/ets2panda/compiler/lowering/phase.cpp b/ets2panda/compiler/lowering/phase.cpp index c321ed36b8..72613d7ef0 100644 --- a/ets2panda/compiler/lowering/phase.cpp +++ b/ets2panda/compiler/lowering/phase.cpp @@ -39,10 +39,12 @@ #include "compiler/lowering/ets/expressionLambdaLowering.h" #include "compiler/lowering/ets/extensionAccessorLowering.h" #include "compiler/lowering/ets/genericBridgesLowering.h" +#include "compiler/lowering/ets/gradualTypeNarrowing.h" #include "compiler/lowering/ets/insertOptionalParametersAnnotation.h" #include "compiler/lowering/ets/interfaceObjectLiteralLowering.h" #include "compiler/lowering/ets/interfacePropertyDeclarations.h" #include "compiler/lowering/ets/lambdaLowering.h" +#include "compiler/lowering/ets/dynamicImport.h" #include "compiler/lowering/ets/localClassLowering.h" #include "compiler/lowering/ets/objectIndexAccess.h" #include "compiler/lowering/ets/objectIterator.h" @@ -124,6 +126,7 @@ std::vector GetETSPhaseList() // pluginsAfterCheck has to go right after checkerPhase, nothing should be between them new PluginPhase {g_pluginsAfterCheck, ES2PANDA_STATE_CHECKED, &util::Plugin::AfterCheck}, // new ConvertPrimitiveCastMethodCall, + new DynamicImport, new AnnotationCopyPostLowering, new AsyncMethodLowering, new DeclareOverloadLowering, @@ -153,6 +156,7 @@ std::vector GetETSPhaseList() new TypeFromLowering, new PrimitiveConversionPhase, new UnboxPhase, + new GradualTypeNarrowing, // pluginsAfterLowerings has to come at the very end, nothing should go after it new PluginPhase{g_pluginsAfterLowering, ES2PANDA_STATE_LOWERED, &util::Plugin::AfterLowerings}, diff --git a/ets2panda/compiler/lowering/util.cpp b/ets2panda/compiler/lowering/util.cpp index b81f32d845..a4c4e1336a 100644 --- a/ets2panda/compiler/lowering/util.cpp +++ b/ets2panda/compiler/lowering/util.cpp @@ -19,6 +19,7 @@ #include "ir/expressions/identifier.h" #include "checker/checker.h" #include "checker/ETSAnalyzer.h" +#include "checker/types/gradualType.h" namespace ark::es2panda::compiler { @@ -365,8 +366,12 @@ void CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *chec if ((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) { newStatus |= checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK; } - auto checkerCtx = checker::SavedCheckerContext( - checker, newStatus, containingClass != nullptr ? containingClass->TsType()->AsETSObjectType() : nullptr); + + auto classType = containingClass == nullptr ? nullptr + : containingClass->TsType()->IsGradualType() + ? containingClass->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : containingClass->TsType()->AsETSObjectType(); + auto checkerCtx = checker::SavedCheckerContext(checker, newStatus, classType); auto scopeCtx = checker::ScopeContext(checker, scope); node->Check(checker); diff --git a/ets2panda/compiler/scripts/signatures.yaml b/ets2panda/compiler/scripts/signatures.yaml index 7cfa309206..490ec79695 100644 --- a/ets2panda/compiler/scripts/signatures.yaml +++ b/ets2panda/compiler/scripts/signatures.yaml @@ -157,6 +157,8 @@ defines: ref: REQUIRED_TYPE_NAME - name: 'Array' ref: ARRAY + - name: 'gradual' + ref: GRADUAL_TYPE_NAME - name: '' ref: PROPERTY - name: 'Any' diff --git a/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp b/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp index 924f0807e2..00ce9884b8 100644 --- a/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp +++ b/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp @@ -93,11 +93,10 @@ void TSDeclGen::ProcessTypeAliasDependencies(const ir::TSTypeAliasDeclaration *t if (!parent->IsExported() && !parent->IsDefaultExported()) { return; } - if (typeFlag == checker::TypeFlag::ETS_OBJECT || typeFlag == checker::TypeFlag::ETS_DYNAMIC_TYPE) { + if (typeFlag == checker::TypeFlag::ETS_OBJECT) { auto objectType = aliasedType->AsETSObjectType(); if (objectType->IsETSStringType() || objectType->IsETSBigIntType() || objectType->IsETSUnboxableObject() || - objectType->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL) || - objectType->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC)) { + objectType->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL)) { return; } auto typeName = objectType->Name(); @@ -210,7 +209,7 @@ void TSDeclGen::ProcessClassMethodDependencies(const ir::MethodDefinition *metho void TSDeclGen::AddSuperType(const ir::Expression *super) { const auto superType = checker::ETSChecker::ETSType(super->TsType()); - if (superType == checker::TypeFlag::ETS_OBJECT || superType == checker::TypeFlag::ETS_DYNAMIC_TYPE) { + if (superType == checker::TypeFlag::ETS_OBJECT) { auto objectType = super->TsType()->AsETSObjectType(); AddObjectDependencies(objectType->Name()); } @@ -219,7 +218,7 @@ void TSDeclGen::AddSuperType(const ir::Expression *super) void TSDeclGen::AddSuperType(const checker::Type *tsType) { const auto superType = checker::ETSChecker::ETSType(tsType); - if (superType == checker::TypeFlag::ETS_OBJECT || superType == checker::TypeFlag::ETS_DYNAMIC_TYPE) { + if (superType == checker::TypeFlag::ETS_OBJECT) { auto objectType = tsType->AsETSObjectType(); AddObjectDependencies(objectType->Name()); } @@ -230,8 +229,7 @@ void TSDeclGen::ProcessInterfacesDependencies(const ArenaVectorName()); } }, @@ -420,7 +418,6 @@ bool TSDeclGen::HandleETSSpecificTypes(const checker::Type *checkerType) return true; case checker::TypeFlag::ETS_OBJECT: - case checker::TypeFlag::ETS_DYNAMIC_TYPE: return HandleObjectType(checkerType); case checker::TypeFlag::ETS_ARRAY: @@ -779,10 +776,6 @@ bool TSDeclGen::HandleSpecificObjectTypes(const checker::ETSObjectType *objectTy GenType(invoke); return true; } - if (objectType->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC)) { - OutDts("any"); - return true; - } return false; } diff --git a/ets2panda/ir/base/classDefinition.h b/ets2panda/ir/base/classDefinition.h index fd71f32cd3..d1a6f415bb 100644 --- a/ets2panda/ir/base/classDefinition.h +++ b/ets2panda/ir/base/classDefinition.h @@ -59,6 +59,7 @@ enum class ClassDefinitionModifiers : uint32_t { INT_ENUM_TRANSFORMED = 1U << 15U, FROM_STRUCT = 1U << 16U, FUNCTIONAL_REFERENCE = 1U << 17U, + LAZY_IMPORT_OBJECT_CLASS = 1U << 18U, DECLARATION_ID_REQUIRED = DECLARATION | ID_REQUIRED, ETS_MODULE = NAMESPACE_TRANSFORMED | GLOBAL }; @@ -272,6 +273,11 @@ public: return (Modifiers() & ClassDefinitionModifiers::NAMESPACE_TRANSFORMED) != 0; } + [[nodiscard]] bool IsLazyImportObjectClass() const noexcept + { + return (Modifiers() & ClassDefinitionModifiers::LAZY_IMPORT_OBJECT_CLASS) != 0; + } + [[nodiscard]] bool IsFromStruct() const noexcept { return (Modifiers() & ClassDefinitionModifiers::FROM_STRUCT) != 0; @@ -312,6 +318,11 @@ public: AddClassModifiers(ClassDefinitionModifiers::NAMESPACE_TRANSFORMED); } + void SetLazyImportObjectClass() noexcept + { + AddClassModifiers(ClassDefinitionModifiers::LAZY_IMPORT_OBJECT_CLASS); + } + void SetFromStructModifier() noexcept { AddClassModifiers(ClassDefinitionModifiers::FROM_STRUCT); diff --git a/ets2panda/ir/ets/etsTypeReference.cpp b/ets2panda/ir/ets/etsTypeReference.cpp index 7986198c72..5fa3e352f7 100644 --- a/ets2panda/ir/ets/etsTypeReference.cpp +++ b/ets2panda/ir/ets/etsTypeReference.cpp @@ -61,13 +61,24 @@ ir::Identifier *ETSTypeReference::BaseName() const return baseName->AsIdentifier(); } - ir::TSQualifiedName *nameIter = baseName->AsTSQualifiedName(); - - while (nameIter->Left()->IsTSQualifiedName()) { - nameIter = nameIter->Left()->AsTSQualifiedName(); + if (baseName->IsIdentifier()) { + return baseName->AsIdentifier(); } + if (baseName->IsTSQualifiedName()) { + ir::TSQualifiedName *iter = baseName->AsTSQualifiedName(); - return nameIter->Left()->AsIdentifier(); + while (iter->Left()->IsTSQualifiedName()) { + iter = iter->Left()->AsTSQualifiedName(); + } + return iter->Left()->AsIdentifier(); + } else { + ir::MemberExpression *iter = baseName->AsMemberExpression(); + + while (iter->Property()->IsMemberExpression()) { + iter = iter->Property()->AsMemberExpression(); + } + return iter->Property()->AsIdentifier(); + } } void ETSTypeReference::Dump(ir::AstDumper *dumper) const diff --git a/ets2panda/ir/ets/etsTypeReferencePart.cpp b/ets2panda/ir/ets/etsTypeReferencePart.cpp index 9dbef072a2..9d567258d3 100644 --- a/ets2panda/ir/ets/etsTypeReferencePart.cpp +++ b/ets2panda/ir/ets/etsTypeReferencePart.cpp @@ -207,6 +207,18 @@ checker::Type *ETSTypeReferencePart::HandleInternalTypes(checker::ETSChecker *co return nullptr; } +checker::Type *ETSTypeReferencePart::HandlerResultType(checker::ETSChecker *checker, checker::Type *baseType) +{ + if (baseType->IsGradualType()) { + return checker->GetGlobalGradualType(HandlerResultType(checker, baseType->MaybeBaseTypeOfGradualType())); + } + if (baseType->IsETSObjectType()) { + checker::InstantiationContext ctx(checker, baseType->AsETSObjectType(), TypeParams(), Start()); + return ctx.Result(); + } else { + return baseType; + } +} checker::Type *ETSTypeReferencePart::GetType(checker::ETSChecker *checker) { @@ -226,14 +238,8 @@ checker::Type *ETSTypeReferencePart::GetType(checker::ETSChecker *checker) if (TsType() == nullptr) { checker::Type *baseType = checker->GetReferencedTypeBase(name); - ES2PANDA_ASSERT(baseType != nullptr); - if (baseType->IsETSObjectType()) { - checker::InstantiationContext ctx(checker, baseType->AsETSObjectType(), TypeParams(), Start()); - SetTsType(ctx.Result()); - } else { - SetTsType(baseType); - } + SetTsType(HandlerResultType(checker, baseType)); } } else { checker::Type *baseType = Previous()->GetType(checker); diff --git a/ets2panda/ir/ets/etsTypeReferencePart.h b/ets2panda/ir/ets/etsTypeReferencePart.h index 9fe657e930..40af6ab2b6 100644 --- a/ets2panda/ir/ets/etsTypeReferencePart.h +++ b/ets2panda/ir/ets/etsTypeReferencePart.h @@ -95,6 +95,7 @@ public: void Compile(compiler::ETSGen *etsg) const override; checker::Type *Check(checker::TSChecker *checker) override; checker::VerifiedType Check(checker::ETSChecker *checker) override; + checker::Type *HandlerResultType(checker::ETSChecker *checker, checker::Type *baseType); checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; ir::Identifier *GetIdent(); diff --git a/ets2panda/ir/expressions/callExpression.cpp b/ets2panda/ir/expressions/callExpression.cpp index a7bfa507bb..967781ab97 100644 --- a/ets2panda/ir/expressions/callExpression.cpp +++ b/ets2panda/ir/expressions/callExpression.cpp @@ -176,4 +176,10 @@ bool CallExpression::IsExtensionAccessorCall() return (Signature() != nullptr) && (Signature()->Function()->IsExtensionAccessor()); } +bool CallExpression::IsDynamicCall() const noexcept +{ + return (Callee()->IsMemberExpression() && Callee()->AsMemberExpression()->PropVar() != nullptr && + Callee()->AsMemberExpression()->Object()->TsType()->IsGradualType()); +} + } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/expressions/callExpression.h b/ets2panda/ir/expressions/callExpression.h index 1dc57dca97..53e6a1a108 100644 --- a/ets2panda/ir/expressions/callExpression.h +++ b/ets2panda/ir/expressions/callExpression.h @@ -164,6 +164,8 @@ public: return callee_->IsThisExpression() || callee_->IsSuperExpression(); } + bool IsDynamicCall() const noexcept; + [[nodiscard]] CallExpression *Clone(ArenaAllocator *allocator, AstNode *parent) override; void TransformChildren(const NodeTransformer &cb, std::string_view transformationName) override; diff --git a/ets2panda/ir/expressions/memberExpression.cpp b/ets2panda/ir/expressions/memberExpression.cpp index 2ea5a0d847..f05a574cbb 100644 --- a/ets2panda/ir/expressions/memberExpression.cpp +++ b/ets2panda/ir/expressions/memberExpression.cpp @@ -21,6 +21,7 @@ #include "compiler/core/ETSGen.h" #include "compiler/core/pandagen.h" #include "util/diagnostic.h" +#include "util/es2pandaMacros.h" namespace ark::es2panda::ir { MemberExpression::MemberExpression([[maybe_unused]] Tag const tag, MemberExpression const &other, @@ -445,8 +446,39 @@ static void CastTupleElementFromClassMemberType(checker::ETSChecker *checker, tupleElementAccessor->Start(), checker::TypeRelationFlag::NO_THROW}); } +checker::Type *MemberExpression::HandleComputedInGradualType(checker::ETSChecker *checker, checker::Type *baseType) +{ + property_->Check(checker); + if (baseType->IsETSObjectType()) { + util::StringView searchName; + if (property_->IsLiteral()) { + searchName = util::StringView {property_->AsLiteral()->ToString()}; + } + auto found = baseType->AsETSObjectType()->GetProperty(searchName, checker::PropertySearchFlags::SEARCH_ALL); + if (found == nullptr) { + // Try to find indexer method + checker::Type *indexType = CheckIndexAccessMethod(checker); + if (indexType != nullptr) { + return indexType; + } + checker->LogError(diagnostic::PROPERTY_NONEXISTENT, {searchName, baseType->AsETSObjectType()->Name()}, + property_->Start()); + return nullptr; + } + return found->TsType(); + } + ES2PANDA_UNREACHABLE(); + return nullptr; +} + checker::Type *MemberExpression::CheckComputed(checker::ETSChecker *checker, checker::Type *baseType) { + if (baseType->IsGradualType()) { + auto objType = baseType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); + SetObjectType(objType); + return HandleComputedInGradualType(checker, objType); + } + if (baseType->IsETSArrayType()) { auto *dflt = baseType->AsETSArrayType()->ElementType(); if (!checker->ValidateArrayIndex(property_)) { diff --git a/ets2panda/ir/expressions/memberExpression.h b/ets2panda/ir/expressions/memberExpression.h index 4906dc35d2..4c72e652fe 100644 --- a/ets2panda/ir/expressions/memberExpression.h +++ b/ets2panda/ir/expressions/memberExpression.h @@ -251,6 +251,7 @@ private: checker::Type *TraverseUnionMember(checker::ETSChecker *checker, checker::ETSUnionType *unionType); bool CheckArrayIndexValue(checker::ETSChecker *checker) const; + checker::Type *HandleComputedInGradualType(checker::ETSChecker *checker, checker::Type *baseType); checker::Type *CheckIndexAccessMethod(checker::ETSChecker *checker); checker::Type *ResolveReturnTypeFromSignature(checker::ETSChecker *checker, bool isSetter, ArenaVector &arguments, diff --git a/ets2panda/ir/ts/tsTypeReference.cpp b/ets2panda/ir/ts/tsTypeReference.cpp index 57b8f7d2ae..d71e15963d 100644 --- a/ets2panda/ir/ts/tsTypeReference.cpp +++ b/ets2panda/ir/ts/tsTypeReference.cpp @@ -91,14 +91,21 @@ ir::Identifier *TSTypeReference::BaseName() const if (typeName_->IsIdentifier()) { return typeName_->AsIdentifier(); } + if (typeName_->IsTSQualifiedName()) { + ir::TSQualifiedName *iter = typeName_->AsTSQualifiedName(); - ir::TSQualifiedName *iter = typeName_->AsTSQualifiedName(); + while (iter->Left()->IsTSQualifiedName()) { + iter = iter->Left()->AsTSQualifiedName(); + } + return iter->Left()->AsIdentifier(); + } else { + ir::MemberExpression *iter = typeName_->AsMemberExpression(); - while (iter->Left()->IsTSQualifiedName()) { - iter = iter->Left()->AsTSQualifiedName(); + while (iter->Property()->IsMemberExpression()) { + iter = iter->Property()->AsMemberExpression(); + } + return iter->Property()->AsIdentifier(); } - - return iter->Left()->AsIdentifier(); } checker::Type *TSTypeReference::Check([[maybe_unused]] checker::TSChecker *checker) diff --git a/ets2panda/lsp/src/isolated_declaration.cpp b/ets2panda/lsp/src/isolated_declaration.cpp index d4df74d45d..77f06d2da7 100644 --- a/ets2panda/lsp/src/isolated_declaration.cpp +++ b/ets2panda/lsp/src/isolated_declaration.cpp @@ -214,7 +214,7 @@ std::optional HandleETSSpecificTypes(const checker::Type *checkerTy return checkerType->ToString(); case checker::TypeFlag::ETS_OBJECT: - case checker::TypeFlag::ETS_DYNAMIC_TYPE: + case checker::TypeFlag::GRADUAL_TYPE: return HandleObjectType(checkerType, checker); case checker::TypeFlag::ETS_ARRAY: diff --git a/ets2panda/parser/ETSparser.cpp b/ets2panda/parser/ETSparser.cpp index 2a7ea152f1..ed35b3c354 100644 --- a/ets2panda/parser/ETSparser.cpp +++ b/ets2panda/parser/ETSparser.cpp @@ -282,6 +282,7 @@ void ETSParser::ParseParseListElement(const util::ImportPathManager::ParseInfo & auto src = importData.HasSpecifiedDeclPath() ? importData.declPath : importData.resolvedSource; SourceFile sf {src, extSrc->View().Utf8(), importData.resolvedSource, false, importData.HasSpecifiedDeclPath()}; parser::Program *newProg = ParseSource(sf); + newProg->SetModuleLang(importData.lang); if (!importData.IsImplicitPackageImported() || newProg->IsPackage()) { AddDirectImportsToDirectExternalSources(directImportsFromMainSource, newProg); // don't insert the separate modules into the programs, when we collect implicit package imports diff --git a/ets2panda/parser/program/program.h b/ets2panda/parser/program/program.h index c29543942c..800b10165f 100644 --- a/ets2panda/parser/program/program.h +++ b/ets2panda/parser/program/program.h @@ -276,6 +276,26 @@ public: return moduleInfo_.kind == util::ModuleKind::PACKAGE; } + bool IsDeclForDynamicStaticInterop() const + { + return moduleInfo_.isDeclForDynamicStaticInterop; + } + + Language GetModuleLang() const + { + return moduleInfo_.lang; + } + + Language GetModuleLang() + { + return moduleInfo_.lang; + } + + void SetModuleLang(Language lang) + { + moduleInfo_.lang = lang; + } + void SetFlag(ProgramFlags flag); bool GetFlag(ProgramFlags flag) const; void SetASTChecked(); diff --git a/ets2panda/test/unit/CMakeLists.txt b/ets2panda/test/unit/CMakeLists.txt index c84184cf5f..78467d1449 100644 --- a/ets2panda/test/unit/CMakeLists.txt +++ b/ets2panda/test/unit/CMakeLists.txt @@ -27,6 +27,7 @@ add_subdirectory(arktsconfig-parser) add_subdirectory(annotations) add_subdirectory(lsp) add_subdirectory(relative_path) +add_subdirectory(any_ins_test) ets2panda_add_gtest(es2panda_astdumper_tests CPP_SOURCES ast_dumper_test.cpp diff --git a/ets2panda/test/unit/any_ins_test/CMakeLists.txt b/ets2panda/test/unit/any_ins_test/CMakeLists.txt new file mode 100644 index 0000000000..b41cb7c83f --- /dev/null +++ b/ets2panda/test/unit/any_ins_test/CMakeLists.txt @@ -0,0 +1,17 @@ +# 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. + + +ets2panda_add_gtest(any_ins_test + CPP_SOURCES any_ins_test.cpp +) diff --git a/ets2panda/test/unit/any_ins_test/any_ins_test.cpp b/ets2panda/test/unit/any_ins_test/any_ins_test.cpp new file mode 100644 index 0000000000..a84f106ba0 --- /dev/null +++ b/ets2panda/test/unit/any_ins_test/any_ins_test.cpp @@ -0,0 +1,312 @@ +/* + * 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 +#include +#include +#include +#include "assembly-function.h" +#include "assembly-ins.h" +#include "assembly-parser.h" +#include "assembly-program.h" +#include "test/utils/checker_test.h" + +namespace ark::es2panda::compiler::test { + +class AnyInsTest : public ::test::utils::CheckerTest { +public: + AnyInsTest() = default; + + ~AnyInsTest() override = default; + + void AssertInstructions(const pandasm::Function &func, const std::vector &expectedInstructions) + { + size_t index = 0; + size_t expectedInsIndex = 0; + while (index < func.ins.size()) { + if (expectedInsIndex >= expectedInstructions.size()) { + break; + } + if (func.ins.at(index).opcode == expectedInstructions[expectedInsIndex]) { + expectedInsIndex++; + } + index++; + } + ASSERT_EQ(expectedInsIndex, expectedInstructions.size()) + << "Expected instructions order do not match actual instructions in function: " << func.name; + } + + void AssertFunctionInstructions(pandasm::Program *program, const std::string &functionName, + const std::vector &expectedInstructions) + { + const auto &functionTable = program->functionStaticTable; + + auto found = functionTable.find(functionName); + ASSERT_NE(found, functionTable.end()) << "Function not found: " << functionName; + + AssertInstructions(found->second, expectedInstructions); + } + +private: + NO_COPY_SEMANTIC(AnyInsTest); + NO_MOVE_SEMANTIC(AnyInsTest); +}; + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_01) +{ + std::string_view text = R"( + // Assume this is dynamic class + export declare class X { + x: X + + constructor() + constructor(x: X) + constructor(p1: number, p2: string, p3: string) + } + + function foo() { + let x: X = new X() + x = new X(x) + x = new X(1, "", "") + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType() && tstype->AsETSObjectType()->Name() != "String") { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = { + pandasm::Opcode::ANY_CALL_NEW_0, pandasm::Opcode::ANY_CALL_NEW_SHORT, pandasm::Opcode::ANY_CALL_NEW_RANGE}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_02) +{ + std::string_view text = R"( + // Assume this is dynamic class + export declare class X { + x: X + + constructor() + } + + function foo() { + let x: X = new X() + x.x = new X() + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType() && tstype->AsETSObjectType()->Name() != "String") { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ANY_CALL_NEW_0, pandasm::Opcode::ANY_CALL_NEW_0, + pandasm::Opcode::ANY_STBYNAME}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_03) +{ + std::string_view text = R"( + // Assume this is dynamic class + export declare class X { + constructor() + } + + function foo() { + let x: X = new X() + x instanceof X + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType() && tstype->AsETSObjectType()->Name() != "String") { + astInClass->AsTyped()->SetTsType(Checker()->CreateETSAnyType(true)); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ANY_CALL_NEW_0, pandasm::Opcode::ANY_ISINSTANCE}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_04) +{ + std::string_view text = R"( + // Assume this is dynamic class + export declare class X { + constructor() + + foo(): void + foo(x: X): void + foo(p1: number, p2: string, p3: string): void + } + + function bar() { + let x : X = new X() + x.foo() + x.foo(x) + x.foo(1, "", "") + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType() && tstype->AsETSObjectType()->Name() != "String") { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + if (tstype != nullptr && tstype->IsETSFunctionType() && tstype->AsETSFunctionType()->Name() == "foo") { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ANY_CALL_NEW_0, pandasm::Opcode::ANY_CALL_THIS_0, + pandasm::Opcode::ANY_CALL_THIS_SHORT, + pandasm::Opcode::ANY_CALL_THIS_RANGE}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.bar:void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_05) +{ + std::string_view text = R"( + // Assume this is dynamic class + export declare class X { + s: string + } + + class Y { + s: string = "" + } + + function foo(v: X | Y) { + v.s + if (v instanceof X) { + v as X == v + } + if (v instanceof Y) { + v as Y == v + } + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType() && tstype->AsETSObjectType()->Name() != "Y") { + astInClass->AsTyped()->SetTsType(Checker()->CreateETSAnyType(true)); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ANY_ISINSTANCE, pandasm::Opcode::ISINSTANCE}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:std.core.Object;void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_06) +{ + std::string_view text = R"( + export declare class X { + } + + function foo() { + let x = new X() + x == x + x === x + x ? true : false + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType()) { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ETS_EQUALS, pandasm::Opcode::ETS_STRICTEQUALS}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:void;", expectedOps); +} + +} // namespace ark::es2panda::compiler::test \ No newline at end of file diff --git a/ets2panda/test/utils/asm_test.cpp b/ets2panda/test/utils/asm_test.cpp index 53fb2a8dcf..1f8e1c6d59 100644 --- a/ets2panda/test/utils/asm_test.cpp +++ b/ets2panda/test/utils/asm_test.cpp @@ -298,7 +298,7 @@ void AsmTest::SetCurrentProgram(std::string_view src) std::unique_ptr AsmTest::GetCurrentProgram(std::string_view src) { - static constexpr std::string_view FILE_NAME = "annotation.ets"; + static constexpr std::string_view FILE_NAME = "dummy.ets"; std::array args = {"../../../../../bin/es2panda", "--ets-unnamed"}; // NOLINT(modernize-avoid-c-arrays) diff --git a/ets2panda/test/utils/checker_test.h b/ets2panda/test/utils/checker_test.h index 79259abbad..69f1fbdee0 100644 --- a/ets2panda/test/utils/checker_test.h +++ b/ets2panda/test/utils/checker_test.h @@ -20,7 +20,9 @@ #include "compiler/lowering/phase.h" #include "panda_executable_path_getter.h" #include "compiler/core/regSpiller.h" +#include "compiler/core/ETSCompiler.h" #include "compiler/core/ETSemitter.h" +#include "compiler/core/ETSGen.h" #include "checker/ETSAnalyzer.h" #include "ir/astNode.h" #include "util/options.h" @@ -84,6 +86,37 @@ public: compiler_alias::ETSEmitter>(&es2pandaPathPtr, fileName, src, &checker_, &program_); } + template + std::unique_ptr RunCheckerWithCustomFunc(std::string_view fileName, std::string_view src, + CustomFunc customFunc) + { + auto es2pandaPathPtr = es2pandaPath_.c_str(); + ASSERT(es2pandaPathPtr); + + return std::unique_ptr( + InitializeCheckerWithCustomFunc(&es2pandaPathPtr, fileName, src, + &checker_, &program_, customFunc)); + } + + template + static plib_alias::Context::CodeGenCb MakeCompileJob() + { + return [](plib_alias::Context *context, varbinder_alias::FunctionScope *scope, + compiler_alias::ProgramElement *programElement) -> void { + RegSpiller regSpiller; + ark::ArenaAllocator allocator(ark::SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + AstCompiler astcompiler; + compiler_alias::SetPhaseManager(context->phaseManager); + CodeGen cg(&allocator, ®Spiller, context, std::make_tuple(scope, programElement, &astcompiler)); + FunctionEmitter funcEmitter(&cg, programElement); + funcEmitter.Generate(); + }; + } + template void InitializeChecker(char const *const *argv, std::string_view fileName, std::string_view src, @@ -137,6 +170,68 @@ public: } } } + + template + ark::pandasm::Program *InitializeCheckerWithCustomFunc(char const *const *argv, std::string_view fileName, + std::string_view src, checker_alias::ETSChecker *checker, + parser_alias::Program *program, CustomFunc customFunc) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto options = std::make_unique(argv[0], diagnosticEngine_); + if (!options->Parse(ark::Span(argv, 1))) { + return nullptr; + } + + ark::Logger::ComponentMask mask {}; + mask.set(ark::Logger::Component::ES2PANDA); + ark::Logger::InitializeStdLogging(options->LogLevel(), mask); + + ark::es2panda::Compiler compiler(options->GetExtension(), options->GetThread()); + ark::es2panda::SourceFile input(fileName, src, options->IsModule()); + compiler_alias::CompilationUnit unit {input, *options, 0, options->GetExtension(), diagnosticEngine_}; + auto parser = Parser(program, unit.options, diagnosticEngine_, + static_cast(unit.rawParserStatus)); + auto analyzer = Analyzer(checker); + checker->SetAnalyzer(&analyzer); + + auto *varbinder = program->VarBinder(); + varbinder->SetProgram(program); + + varbinder->SetContext(publicContext_.get()); + + auto emitter = Emitter(publicContext_.get()); + auto phaseManager = compiler_alias::PhaseManager(publicContext_.get(), unit.ext, allocator_.get()); + + auto config = plib_alias::ConfigImpl {}; + publicContext_->config = &config; + publicContext_->config->options = &unit.options; + publicContext_->sourceFile = &unit.input; + publicContext_->allocator = allocator_.get(); + publicContext_->parser = &parser; + parser.SetContext(publicContext_.get()); + publicContext_->parserProgram = program; + publicContext_->PushChecker(checker); + publicContext_->PushAnalyzer(publicContext_->GetChecker()->GetAnalyzer()); + publicContext_->emitter = &emitter; + publicContext_->diagnosticEngine = &diagnosticEngine_; + publicContext_->phaseManager = &phaseManager; + publicContext_->GetChecker()->Initialize(varbinder); + parser.ParseScript(unit.input, + unit.options.GetCompilationMode() == ark::es2panda::CompilationMode::GEN_STD_LIB); + while (auto phase = publicContext_->phaseManager->NextPhase()) { + if (!phase->Apply(publicContext_.get(), program)) { + return nullptr; + } + } + + // Run custom logic in here to modify ast + Program()->Ast()->IterateRecursively(customFunc); + + publicContext_->codeGenCb = MakeCompileJob(); + ark::es2panda::compiler::CompilerImpl compilerImpl(options->GetThread(), {}); + return compilerImpl.Emit(publicContext_.get()); + } NO_COPY_SEMANTIC(CheckerTest); NO_MOVE_SEMANTIC(CheckerTest); diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index 568a3b7312..a575f67773 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -1496,5 +1496,9 @@ semantic: message: "Class field '{}' defined by the parent class is not accessible in the child class via super." - name: INTERFACE_EXTENDS_CLASS - id: 373 + id: 378 message: "Interfaces cannot extend classes, only other interfaces." + +- name: DYMANIC_INIT_WITH_OBJEXPR + id: 379 + message: "Dymanic Type {} cannot be initialize with an object expression" diff --git a/ets2panda/util/importPathManager.cpp b/ets2panda/util/importPathManager.cpp index 7cf0466927..f69485a96a 100644 --- a/ets2panda/util/importPathManager.cpp +++ b/ets2panda/util/importPathManager.cpp @@ -66,7 +66,7 @@ ImportPathManager::ImportMetadata ImportPathManager::GatherImportMetadata(parser // NOTE(dkofanov): The code below expresses the idea of 'dynamicPaths' defining separated, virtual file system. // Probably, paths of common imports should be isolated from the host fs as well, being resolved by 'ModuleInfo' // instead of 'AbsoluteName'. - isDynamic_ = program->ModuleInfo().isDeclForDynamicStaticInterop; + isDynamic_ = program->IsDeclForDynamicStaticInterop(); auto curModulePath = isDynamic_ ? program->ModuleInfo().moduleName : program->AbsoluteName(); auto [resolvedImportPath, resolvedIsDynamic] = ResolvePath(curModulePath.Utf8(), importPath); if (resolvedImportPath.empty()) { diff --git a/ets2panda/util/importPathManager.h b/ets2panda/util/importPathManager.h index 9e011a2159..0537c7a067 100644 --- a/ets2panda/util/importPathManager.h +++ b/ets2panda/util/importPathManager.h @@ -16,6 +16,7 @@ #ifndef ES2PANDA_UTIL_IMPORT_PATH_MANAGER_H #define ES2PANDA_UTIL_IMPORT_PATH_MANAGER_H +#include "language.h" #if defined PANDA_TARGET_MOBILE #define USE_UNIX_SYSCALL #endif @@ -68,6 +69,7 @@ struct ModuleInfo { // 'Program::MaybeTransformToDeclarationModule'. bool isDeclForDynamicStaticInterop {}; // NOLINTEND(misc-non-private-member-variables-in-classes) + Language lang = Language(Language::Id::ETS); }; class ImportPathManager { diff --git a/ets2panda/varbinder/variableFlags.h b/ets2panda/varbinder/variableFlags.h index 1177e71a30..5ec081c2e6 100644 --- a/ets2panda/varbinder/variableFlags.h +++ b/ets2panda/varbinder/variableFlags.h @@ -165,6 +165,7 @@ enum class VariableFlags : uint64_t { ANNOTATIONUSAGE = 1ULL << 34ULL, NAMESPACE = 1ULL << 35ULL, INIT_IN_STATIC_BLOCK = 1ULL << 36ULL, + DYNAMIC = 1ULL << 37ULL, HOIST_VAR = HOIST | VAR, CLASS_OR_INTERFACE = CLASS | INTERFACE, -- Gitee From 06e4d64e4b69936b6f5799541f63194cc1e1ae1e Mon Sep 17 00:00:00 2001 From: xuxinjie4 Date: Thu, 19 Jun 2025 15:01:49 +0800 Subject: [PATCH 2/4] Fix bug Signed-off-by: xuxinjie4 --- .../compiler/lowering/ets/interfaceObjectLiteralLowering.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp index 09f44143e9..613bc116b0 100644 --- a/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp @@ -285,7 +285,7 @@ static void GenerateAnonClassTypeFromAbstractClass(public_lib::Context *ctx, ir: auto *classDecl = checker->BuildClass(anonClassName.View(), classBodyBuilder); RefineSourceRanges(classDecl); auto *classDef = classDecl->Definition(); - auto *classType = classDef->TsType()->AsETSObjectType(); + auto *classType = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); classDecl->SetRange(abstractClassNode->Range()); classDef->SetAnonymousModifier(); @@ -387,7 +387,7 @@ static bool CheckInterfaceShouldGenerateAnonClass(ir::TSInterfaceDeclaration *in static bool CheckAbstractClassShouldGenerateAnonClass(ir::ClassDefinition *classDef) { - auto constructorSigs = classDef->TsType()->AsETSObjectType()->ConstructSignatures(); + auto constructorSigs = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType()->ConstructSignatures(); if (auto res = std::find_if(constructorSigs.cbegin(), constructorSigs.cend(), [](checker::Signature *sig) -> bool { return sig->MinArgCount() == 0; }); res == constructorSigs.cend()) { -- Gitee From d4f201df85a0a631542cddc7810fbc8038d46ce8 Mon Sep 17 00:00:00 2001 From: xuxinjie4 Date: Thu, 19 Jun 2025 17:40:48 +0800 Subject: [PATCH 3/4] Fix lambda bug Signed-off-by: xuxinjie4 --- ets2panda/compiler/core/ETSCompiler.cpp | 13 +++++++++++++ ets2panda/compiler/core/ETSCompilerUnrechable.cpp | 5 ----- ets2panda/compiler/lowering/ets/lambdaLowering.cpp | 9 ++------- ets2panda/ir/expressions/callExpression.cpp | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index e2a57c66ae..0929407230 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -1715,4 +1715,17 @@ void ETSCompiler::Compile(const ir::TSNonNullExpression *expr) const void ETSCompiler::Compile([[maybe_unused]] const ir::TSTypeAliasDeclaration *st) const {} +void ETSCompiler::Compile(const ir::TSQualifiedName *expr) const +{ + ES2PANDA_ASSERT(expr->Left()->IsMemberExpression()); + + ETSGen *etsg = GetETSGen(); + expr->Left()->Compile(etsg); + + compiler::VReg objReg = etsg->AllocReg(); + etsg->StoreAccumulator(expr->Left(), objReg); + etsg->LoadPropertyByNameAny(expr->Left(), objReg, expr->Right()->AsIdentifier()->Name()); + etsg->EmitAnyCheckCast(expr, expr->Right()->AsIdentifier()->Variable()->TsType()); +} + } // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/core/ETSCompilerUnrechable.cpp b/ets2panda/compiler/core/ETSCompilerUnrechable.cpp index 44669b1241..4536b5cebf 100644 --- a/ets2panda/compiler/core/ETSCompilerUnrechable.cpp +++ b/ets2panda/compiler/core/ETSCompilerUnrechable.cpp @@ -419,11 +419,6 @@ void ETSCompiler::Compile([[maybe_unused]] const ir::TSParenthesizedType *node) ES2PANDA_UNREACHABLE(); } -void ETSCompiler::Compile([[maybe_unused]] const ir::TSQualifiedName *expr) const -{ - ES2PANDA_UNREACHABLE(); -} - void ETSCompiler::Compile([[maybe_unused]] const ir::TSStringKeyword *node) const { ES2PANDA_UNREACHABLE(); diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index 9c4d84677b..c62bcac5c1 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -1143,9 +1143,6 @@ static bool IsFunctionOrMethodCall(checker::ETSChecker *checker, ir::CallExpress static ir::AstNode *InsertInvokeCall(public_lib::Context *ctx, ir::CallExpression *call) { - if (call->IsDynamicCall()) { - return call; - } auto *allocator = ctx->allocator; auto *checker = ctx->GetChecker()->AsETSChecker(); auto *varBinder = checker->VarBinder()->AsETSBinder(); @@ -1238,16 +1235,14 @@ static ir::AstNode *BuildLambdaClassWhenNeeded(public_lib::Context *ctx, ir::Ast // We are running this lowering only for ETS files // so it is correct to pass ETS extension here to isReference() if (id->IsReference(ScriptExtension::ETS) && id->TsType() != nullptr && id->TsType()->IsETSFunctionType() && - !id->Variable()->HasFlag(varbinder::VariableFlags::DYNAMIC) && !IsInCalleePosition(id) && - !IsEnumFunctionCall(id) && IsValidFunctionDeclVar(var)) { + !IsInCalleePosition(id) && !IsEnumFunctionCall(id) && IsValidFunctionDeclVar(var)) { return ConvertFunctionReference(ctx, id); } } if (node->IsMemberExpression()) { auto *mexpr = node->AsMemberExpression(); if (mexpr->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS && mexpr->TsType() != nullptr && - !mexpr->TsType()->IsGradualType() && mexpr->TsType()->IsETSFunctionType() && - mexpr->Object()->TsType()->IsETSObjectType()) { + mexpr->TsType()->IsETSFunctionType() && mexpr->Object()->TsType()->IsETSObjectType()) { ES2PANDA_ASSERT(mexpr->Property()->IsIdentifier()); auto *var = mexpr->Object()->TsType()->AsETSObjectType()->GetProperty( mexpr->Property()->AsIdentifier()->Name(), diff --git a/ets2panda/ir/expressions/callExpression.cpp b/ets2panda/ir/expressions/callExpression.cpp index 967781ab97..73f0c121ea 100644 --- a/ets2panda/ir/expressions/callExpression.cpp +++ b/ets2panda/ir/expressions/callExpression.cpp @@ -179,7 +179,7 @@ bool CallExpression::IsExtensionAccessorCall() bool CallExpression::IsDynamicCall() const noexcept { return (Callee()->IsMemberExpression() && Callee()->AsMemberExpression()->PropVar() != nullptr && - Callee()->AsMemberExpression()->Object()->TsType()->IsGradualType()); + Callee()->AsMemberExpression()->PropVar()->HasFlag(varbinder::VariableFlags::DYNAMIC)); } } // namespace ark::es2panda::ir -- Gitee From b12c32bfc423f4fd46549e55c90791c9209dd067 Mon Sep 17 00:00:00 2001 From: xuxinjie4 Date: Thu, 19 Jun 2025 19:56:59 +0800 Subject: [PATCH 4/4] Fix bug Signed-off-by: xuxinjie4 --- ets2panda/compiler/core/ETSCompiler.cpp | 2 +- ets2panda/compiler/lowering/ets/lambdaLowering.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 0929407230..68b87145d0 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -762,7 +762,7 @@ void ETSCompiler::Compile(const ir::CallExpression *expr) const ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr, signature); - if (callee->IsMemberExpression() && callee->AsMemberExpression()->Object()->TsType()->IsETSAnyType()) { + if (expr->IsDynamicCall()) { CompileAny(expr, callee, calleeReg); } else if (callee->IsIdentifier()) { if (!isStatic) { diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index c62bcac5c1..bc3dd4b3ca 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -941,8 +941,9 @@ static ir::ETSNewClassInstanceExpression *CreateConstructorCall(public_lib::Cont auto lexScope = varbinder::LexicalScope::Enter(varBinder, nearestScope); varBinder->ResolveReferencesForScopeWithContext(newExpr, nearestScope); - auto checkerCtx = checker::SavedCheckerContext(ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, - info->calleeClass->Definition()->TsType()->AsETSObjectType()); + auto checkerCtx = checker::SavedCheckerContext( + ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, + info->calleeClass->Definition()->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType()); auto scopeCtx = checker::ScopeContext(ctx->GetChecker(), nearestScope); newExpr->Check(checker); @@ -1103,7 +1104,8 @@ static ir::AstNode *ConvertFunctionReference(public_lib::Context *ctx, ir::Expre ES2PANDA_ASSERT(funcRef->TsType()->IsETSArrowType()); auto *lambdaClass = CreateLambdaClass(ctx, funcRef->TsType()->AsETSFunctionType(), method, &info); auto *constructorCall = CreateConstructorCall(ctx, funcRef, lambdaClass, &info); - constructorCall->TsType()->AsETSObjectType()->AddObjectFlag(checker::ETSObjectFlags::FUNCTIONAL_REFERENCE); + constructorCall->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType()->AddObjectFlag( + checker::ETSObjectFlags::FUNCTIONAL_REFERENCE); return constructorCall; } -- Gitee