diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 596f03163ccfa9da9069f87a7683f7359b2a5526..0b2aea75e833615ae29f6e290f1ef61261ccb3c3 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 d40f60c4f49d5841cc4005e2f7a4ad4be8e0bbe6..13535b54741df96a0b0ce639a0cb420abf71d6cd 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 d8a9998848ce9a4f5d6124d41c6587f385abb866..7fa918d29f33169ac413fa9586d44a8884b087cf 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 bf7351956a7b3bae812a360c9c28f6a777633fae..6d6ff354876263cca9636e8da9367292bb816629 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 3aa4109c2eb8eb2d86c89482000a32026599fdd0..1a4e21f9c008188cb398ae404e8ae2fd49cc005b 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 2a5443ae232284fc548176857faf987d2efdb0ab..590933c12cfb47d1f349442a3c55f4f1f0ab4a89 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 ce5dd58750cf2af73e27ba167dafc524205d481a..7bc3073fa99699b07bb7776f61009ed4978b4ee7 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 b3ced7b7d372663b02d01071966e28c29a3ef118..45a8f4572dba15b88d5f7a1ab17eb0b87fcd2a89 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 39c4be1b5982acc392442724bc58a9d76b98126a..ca737861c64f9724abba26770d0d344eca08fa22 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 c41af2dbd818e3aa0df7266e0b115847fb411979..d6d6f8d31ef9bbcd9561f74156a754f8b4c08e52 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 b8da863c9b340d571fb4daa2bd1bab4c91eeea8f..0a08103b12581125cca8c529c0258ad4c527a94b 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 8b29b6e855f4fd3acb58d88e3409dcc3758e3a40..cd5c6132b7ddd15abccc6417af70529a3f61bf15 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 ea80a961c377bad8c0774150bebde324fc61cc1b..9a05c8c8a4858041a84dad47d13da894bd7ab59f 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 2ed4c6b430b254b5237d17726eaa2200711f2c37..44554a79318bf4220ad4707090962e127bef49b2 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 553abd66e2047e601d4e4440b62db65fc8eadec5..8c6286b224ae7e62cdfe93f1f63a6dccfa3e4e90 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 1649138262b54c8c8462130c35a4ea3919216d61..703b787001542f50361ba684fac4df9074685646 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 515b663e3878f586e60cc62e7affae3d28cbe31c..1242f6b19de7160f9430ba20c4e2cb9ce8dbb092 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 1c8c6c9a98910a1e00c65c7f932c68aaca91f26f..455dd2d88d200e389dfed511b5cdf7166324079a 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 f2aecd19fbb5122d9facfc69dc153844cedf8252..9edc873e8b07eb51b391f670b6a2faab38463876 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 eeb2ba008ddce15c0f48884f6998036577db8aa8..fb2c13818d5ef57e8969aa3723ad40f109a58749 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 c267defa3c8f07b568c3720c739da8bc9d218139..3769508cc5bd19774461eb40a287e81a8f69a06d 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 d9b87e49a24d5bef65de3d0ed9572f21a58fd5ae..52793689c9b76084dfc5ac556f7a9a53ef7d1319 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 1a315b36d603d3e67d3e199af08e5407844a2eae..bab24a6004948d01bf0f6addf7646ed19ba30234 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 d6b776d64c800deb2cbf8058eadc24cf26fc107c..c564a74fbacedbe0136fd81e6d2e195fb13a7416 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 ace95542ceaa04a6f103ad55cebd1e61ff3ffa0d..277adc4569ee4383574b292bb75a23ea3e085b21 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 1b27c6e7c72cecc11817b206a67f133b115be5de..651e28c1be6e338eb3b4a6cc27caf8a688eb0bc2 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 518a42441bc1d0493b3a90a4a549052fdf48b3b9..615584de9bd757e51f27bd59846b05d7bb972205 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 b5a4244e54630cac84ef1aa5d1c4be03e601ed8b..7b94a5311209335cc121aade18da8c1fcaf2d8d4 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 0000000000000000000000000000000000000000..59a2a4a1be645b4482aeab1f8b4f048e1b439cb9 --- /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 0000000000000000000000000000000000000000..eb1978e8d64d03011ffa14443c8b8607c8559850 --- /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 5ced79c41120e511b5f411031ffe5a045c8275b0..be45282d5f95b09d4a98d5cc6a18a04096a96d88 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 090981812fedbf7f10a5ff7e16016de06995cc1d..61a6b8568e82040ac0fe3ebbed737ee8846d6c40 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 d3f83e9d37094cd16ce4d49ef9be620eae5bd5ae..f4b3a3f481c57f4e5d67fdf8dd194011c62b0c37 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 feea58ce1294a994cceccb60c8811fb08999593c..5f01ae82252c6d2fcf311bbdfc2804c5f010a98c 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 a856a49bdbc8561f4e98e87e5be6a1b936804281..84e3ba9cfef6fdf49983f38bf38c92e203b2e5b3 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 96572db0715cf5f6e9e7c0f052a6897e2f8b3612..f83427ac2b331cc0dbf8f0d8a3bb7cd985a14623 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 09d7df240ab32e6551008de170ebbfdcd00e30b0..68b87145d087479cf9bf5863cf77377215bdbdc9 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 (expr->IsDynamicCall()) { + 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; } @@ -1630,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/ETSCompiler.h b/ets2panda/compiler/core/ETSCompiler.h index d09df63fd6c3d2e54a7fcc92214db24f5469d466..868ce81e497e15dbcb51d9a6dd65191d2249dc58 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/ETSCompilerUnrechable.cpp b/ets2panda/compiler/core/ETSCompilerUnrechable.cpp index 44669b1241826c6c824fbe63ccac691226ab45b4..4536b5cebfb6b2f03acb5665834293cd501dc59e 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/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 149c12d4d57963671b7f818166bbcc4ac098b78d..420e085a6ae7c518ec5705221ed4773042776c32 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 6712e10b91f0bc63dc89d21b7b79f458c137d21b..b10b95c99983c82877bc029b27a7dcfc09c0845d 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 81ff05cfe2b47612f29a5eb7a8a7bb1523c9432a..81a59f631085f223531026484b2d4ea169cc0513 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 4edd9d31b25ace531738391744a03630fe79e6ea..3b2d6ab2cb95ea3b15b6cf122ed312e6df8e2000 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 0000000000000000000000000000000000000000..3265cba95d6d43d4f681c217823e10767dbaad37 --- /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 0000000000000000000000000000000000000000..78af90c0132bd0801131f58f9434f32a50d34512 --- /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 0000000000000000000000000000000000000000..60474ea17d4e7fc1f688b266e9492f8bbbdb1029 --- /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 0000000000000000000000000000000000000000..e4a09c4ab960a0760129e82eb57b03cb9ff406df --- /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 c88ab3572b51a831e15f60d1bffe3e22bae19a02..613bc116b0905d38573cedf0df424b35d0ec732b 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()); @@ -287,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(); @@ -389,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()) { diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index c62bcac5c1bd15b538287363c64fe95f302ff04a..bc3dd4b3cabadb141d2fd17d16fa82158dcd8528 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; } diff --git a/ets2panda/compiler/lowering/ets/localClassLowering.cpp b/ets2panda/compiler/lowering/ets/localClassLowering.cpp index febb95ab47b37460f58f61183d64b182f764a3a8..0fb500e0aa30c04854140737609e60b415da42ea 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 5e42c84c84564887e1c949a85bfee3f1d3ce2633..9c81f38d7afaf078b630f44c2a37e39ffe42659b 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 d0f415917df8b09997b5b5bff31639751f60d695..0fe7a0ca644d3b0929df9ff7275e1d5e7f24b87e 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 94ce1794274081033d3b74167141e6e6fd066f7f..ea6d19e5b14ca04f4821b78faecdbf2e8105c143 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 f0435fb4c077f40af0e1a63b859c33c148c85619..9fe4183e9b9155b496bb2aed6acacf199cee2546 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 2f60dd17e0840b7eb6c1fa74b467109a2daca2ec..1962e5bed306d4bed77fb7adc7148ff94d68443a 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 c321ed36b8b664e093983492e3613f3e40d3ed51..72613d7ef058231e1f0366e513b8a17ab42d97db 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 b81f32d845bb531fd67da6b7b361147546dab0aa..a4c4e1336a99c85f07a286b5c5d047885ba99b37 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 7cfa3092069efbe16315bc78b51cf61b007ef06d..490ec79695636c4e36be77fad20d79d7480d53ce 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 924f0807e2ca4679749200e5e85237b169b96595..00ce9884b8aef3d7d204227d6dcca331e5d2e659 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 fd71f32cd3f1a783f038df8cb781551345b4e36d..d1a6f415bbd0019cdd85d0163919c7b11460fb3c 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 7986198c729d4d33902ce54239119b9df91e4c72..5fa3e352f72220d40c63001f1c55e23f25c3e205 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 9dbef072a275a2e41a81acddb471b649f2ec868a..9d567258d33d1eea1328e8c7e24ce7897ae025eb 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 9fe657e9302608ebbc02c8b032041c5127466597..40af6ab2b6e6690d97ee297a0d4452ca6e3f6c9b 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 a7bfa507bb79d431683688d41d9312edb523bc4b..73f0c121ea19bccc243330f2d5cd2a07f2f829de 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()->PropVar()->HasFlag(varbinder::VariableFlags::DYNAMIC)); +} + } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/expressions/callExpression.h b/ets2panda/ir/expressions/callExpression.h index 1dc57dca97b45e1b324e62b97ec9c816223d7600..53e6a1a108987d0cbcb20784c69b94fa134d887c 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 2ea5a0d8472cd3c0e10310e1f093841c896e51ff..f05a574cbb9bd0a6368a5ab346438ee3fb87a291 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 4906dc35d2e626cb0f2167e051c71fd7247dcb8f..4c72e652feabf12e3d22dd1f36bbfe9ed09b86f7 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 57b8f7d2ae2cd662db0504c73723e943cb43585f..d71e15963da81831f4cd353f0965ad13624d7a35 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 d4df74d45d740538637ee4e4c4ff8bf89eeb1ca6..77f06d2da7ae35876a82c166d868c86a8942a97d 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 2a7ea152f1d0c42814411b6665fbd1e30fd2b4d0..ed35b3c354a6237bc87952c3c4c406b0476eca94 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 c29543942cadcf88358a2ac9db0e08e8f34a4169..800b10165fc2715122f22081f4a0248f352c9b17 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 c84184cf5fed4406a6915b5ecb8eca0d3f138e9f..78467d144993ce0601bc57922ef60949dc454cb1 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 0000000000000000000000000000000000000000..b41cb7c83f96c685476aeab6d72e7298d17fdc7e --- /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 0000000000000000000000000000000000000000..a84f106ba09527349772b7706023600746075916 --- /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 53fb2a8dcf1e4258b5c3b2826a3a882c931fe8df..1f8e1c6d59eeda4e7e5b64eae587cccaa79588d5 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 79259abbadaa30314635362e73d5b21792a83a71..69f1fbdee00b0144e41b12514131388ff45479b0 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 568a3b73128a4625368f0bb392f21c0762ebe352..a575f677733f6f67708fdfe539d2520a980183fd 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 7cf04669276b7b647c1a6d4a086c986641ad2d86..f69485a96a3bd1c8769499725e802888133841bb 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 9e011a215919850377801f2c805284ab4e50a03d..0537c7a067a2338a05096304ddcaa3332e2b64bb 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 1177e71a3099a354ea472b0b41beac43ee24fe73..5ec081c2e68cbf1a6b70d29f88dbd94a571e2f68 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,