From 92e5104cab265ab8836757b4bc6e50e4281297c4 Mon Sep 17 00:00:00 2001 From: Daniel Kofanov Date: Tue, 8 Jul 2025 23:39:58 +0300 Subject: [PATCH 1/2] tmp Change-Id: I541d973225fe11ec4b15709b364c8970a6f054e1 --- ets2panda/checker/ETSAnalyzer.cpp | 163 ++++++++++-------- ets2panda/checker/ETSAnalyzerHelpers.cpp | 37 ++-- ets2panda/checker/ETSchecker.cpp | 3 +- ets2panda/checker/ETSchecker.h | 1 - ets2panda/checker/TSAnalyzer.cpp | 13 +- ets2panda/checker/checker.h | 70 +++++--- ets2panda/checker/checkerContext.h | 30 +--- ets2panda/checker/ets/arithmetic.cpp | 6 - ets2panda/checker/ets/function_helpers.h | 3 +- ets2panda/checker/ets/object.cpp | 124 ++++++------- ets2panda/checker/ets/typeCheckingHelpers.cpp | 15 +- ets2panda/checker/ets/typeRelationContext.cpp | 7 - ets2panda/checker/ts/function.cpp | 10 +- ets2panda/checker/ts/helpers.cpp | 2 +- ets2panda/compiler/core/ETSCompiler.cpp | 3 +- .../lowering/ets/asyncMethodLowering.cpp | 3 +- .../compiler/lowering/ets/boxingForLocals.cpp | 6 +- .../lowering/ets/declareOverloadLowering.cpp | 2 +- .../lowering/ets/enumPostCheckLowering.cpp | 7 +- .../ets/extensionAccessorLowering.cpp | 5 +- .../lowering/ets/genericBridgesLowering.cpp | 14 +- .../compiler/lowering/ets/lambdaLowering.cpp | 12 +- .../lowering/ets/objectLiteralLowering.cpp | 2 +- .../compiler/lowering/ets/opAssignment.cpp | 4 +- .../compiler/lowering/ets/recordLowering.cpp | 2 +- ets2panda/compiler/lowering/util.cpp | 91 +++++++--- ets2panda/ir/expressions/arrayExpression.cpp | 4 +- ets2panda/ir/expressions/objectExpression.cpp | 4 +- ets2panda/ir/statements/forOfStatement.cpp | 2 +- .../ast/parser/ets/InvalidExpressions.ets | 1 - .../test/parser/ets/instanceof-expected.txt | 1 - ets2panda/util/helpers.cpp | 26 --- ets2panda/util/helpers.h | 1 - 33 files changed, 340 insertions(+), 334 deletions(-) diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index c4d2946155..2454b5fe44 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -15,6 +15,7 @@ #include "ETSAnalyzer.h" +#include "checker.h" #include "checker/ETSchecker.h" #include "generated/diagnostic.h" #include "checker/types/globalTypesHolder.h" @@ -117,13 +118,7 @@ checker::Type *ETSAnalyzer::Check(ir::ClassProperty *st) const st->TypeAnnotation()->Check(checker); } - checker::SavedCheckerContext savedContext(checker, checker->Context().Status(), - checker->Context().ContainingClass(), - checker->Context().ContainingSignature()); - - if (st->IsStatic()) { - checker->AddStatus(checker::CheckerStatus::IN_STATIC_CONTEXT); - } + NarrowedContext savedContext {checker, st->IsStatic() ? CheckerStatus::IN_STATIC_CONTEXT : CheckerStatus::NO_OPTS}; checker::Type *propertyType = checker->CheckVariableDeclaration(st->Id(), st->TypeAnnotation(), st->Value(), st->Modifiers()); @@ -156,9 +151,7 @@ checker::Type *ETSAnalyzer::Check(ir::ClassStaticBlock *st) const st->SetTsType(checker->BuildMethodType(func)); } checker::ScopeContext scopeCtx(checker, func->Scope()); - checker::SavedCheckerContext savedContext(checker, checker->Context().Status(), - checker->Context().ContainingClass()); - checker->AddStatus(checker::CheckerStatus::IN_STATIC_BLOCK | checker::CheckerStatus::IN_STATIC_CONTEXT); + NarrowedContext savedContext {checker, CheckerStatus::IN_STATIC_BLOCK | CheckerStatus::IN_STATIC_CONTEXT}; func->Body()->Check(checker); return st->TsType(); } @@ -204,11 +197,33 @@ static checker::Type *CheckMethodDefinitionHelper(ETSChecker *checker, ir::Metho return node->TsType(); } -static bool IsInitializerBlockTransfer(std::string_view str) -{ - auto prefix = compiler::Signatures::INITIALIZER_BLOCK_INIT; - return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0; -} +struct EnterMethodContext : private NarrowedContext { + EnterMethodContext(ETSChecker *checker, const ir::ScriptFunction *scriptFunc) + : NarrowedContext {checker, FlagsFrom(scriptFunc)} + { + } + +private: + static CheckerStatus FlagsFrom(const ir::ScriptFunction *scriptFunc) + { + CheckerStatus flags {}; + ES2PANDA_ASSERT(!(scriptFunc->IsGetter() && scriptFunc->IsSetter())); + if (scriptFunc->IsGetter() || scriptFunc->IsSetter()) { + flags |= scriptFunc->IsGetter() ? CheckerStatus::IN_GETTER : CheckerStatus::IN_SETTER; + } + if (IsInitializerBlockTransfer(scriptFunc)) { + flags |= CheckerStatus::IN_STATIC_BLOCK; + } + return flags; + } + + static bool IsInitializerBlockTransfer(const ir::ScriptFunction *scriptFunc) + { + auto str = scriptFunc->Id()->Name().Utf8(); + auto prefix = compiler::Signatures::INITIALIZER_BLOCK_INIT; + return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0; + } +}; checker::Type *ETSAnalyzer::Check(ir::MethodDefinition *node) const { @@ -233,12 +248,6 @@ checker::Type *ETSAnalyzer::Check(ir::MethodDefinition *node) const return ReturnTypeForStatement(node); } - ES2PANDA_ASSERT(!(scriptFunc->IsGetter() && scriptFunc->IsSetter())); - if (scriptFunc->IsGetter() || scriptFunc->IsSetter()) { - auto status = scriptFunc->IsGetter() ? CheckerStatus::IN_GETTER : CheckerStatus::IN_SETTER; - checker->AddStatus(status); - } - // NOTE: aszilagyi. make it correctly check for open function not have body if (!scriptFunc->HasBody() && !(node->IsAbstract() || node->IsNative() || node->IsDeclare() || checker->HasStatus(checker::CheckerStatus::IN_INTERFACE))) { @@ -255,9 +264,7 @@ checker::Type *ETSAnalyzer::Check(ir::MethodDefinition *node) const node->SetTsType(checker->BuildMethodSignature(node)); } - if (IsInitializerBlockTransfer(scriptFunc->Id()->Name().Utf8())) { - checker->AddStatus(CheckerStatus::IN_STATIC_BLOCK); - } + EnterMethodContext(checker, scriptFunc); this->CheckMethodModifiers(node); HandleNativeAndAsyncMethods(checker, node); @@ -1018,6 +1025,43 @@ void TryInferPreferredType(ir::ArrowFunctionExpression *expr, checker::Type *pre } } +struct EnterLambdaContext : private NarrowedContext { + EnterLambdaContext(ETSChecker *checker, ir::ArrowFunctionExpression *expr) + : NarrowedContext {checker, CheckerStatus::IN_LAMBDA, CaptureContainingClass(checker, expr)} + { + if (!expr->Parent()->IsCallExpression() || expr->Function()->IsAsyncFunc()) { + checker->Context().ClearSmartCasts(); + } + checker->Context().SetContainingLambda(expr); + } + +private: + static const ETSObjectType *CaptureContainingClass(ETSChecker *checker, const ir::ArrowFunctionExpression *expr) + { + if (checker->HasStatus(checker::CheckerStatus::IN_EXTENSION_METHOD) && !expr->Function()->HasReceiver()) { + /* + example code: + ``` + class A { + prop:number + } + function method(this: A) { + let a = () => { + console.log(this.prop) + } + } + ``` + here the enclosing class of arrow function should be Class A + */ + return checker->Scope() + ->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS) + .variable->TsType() + ->AsETSObjectType(); + } + return checker->Context().ContainingClass(); + } +}; + checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const { ETSChecker *checker = GetETSChecker(); @@ -1026,36 +1070,7 @@ checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const return expr->TsType(); } checker::ScopeContext scopeCtx(checker, expr->Function()->Scope()); - - if (checker->HasStatus(checker::CheckerStatus::IN_EXTENSION_METHOD) && !expr->Function()->HasReceiver()) { - /* - example code: - ``` - class A { - prop:number - } - function method(this: A) { - let a = () => { - console.log(this.prop) - } - } - ``` - here the enclosing class of arrow function should be Class A - */ - checker->Context().SetContainingClass( - checker->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable->TsType()->AsETSObjectType()); - } - - auto lambdaSavedSmartCasts = checker->Context().CloneSmartCasts(); - checker::SavedCheckerContext savedContext(checker, checker->Context().Status(), - checker->Context().ContainingClass()); - - if (expr->Parent()->IsCallExpression() && !expr->Function()->IsAsyncFunc()) { - checker->Context().RestoreSmartCasts(lambdaSavedSmartCasts); - } - - checker->AddStatus(checker::CheckerStatus::IN_LAMBDA); - checker->Context().SetContainingLambda(expr); + EnterLambdaContext savedContext(checker, expr); auto preferredType = expr->GetPreferredType(); if (preferredType != nullptr) { @@ -1574,22 +1589,27 @@ static void CheckCallee(ETSChecker *checker, ir::CallExpression *expr) } // Restore CheckerContext of the owner class if we want to perform checking -static checker::SavedCheckerContext ReconstructOwnerClassContext(ETSChecker *checker, ETSObjectType *owner) -{ - if (owner == nullptr) { - return SavedCheckerContext(checker, CheckerStatus::NO_OPTS, nullptr); +struct ReconstructOwnerClassContext : private EmptyContext { + ReconstructOwnerClassContext(ETSChecker *checker, const ETSObjectType *owner) + : EmptyContext {checker, StatusFrom(owner), owner} + { } - ES2PANDA_ASSERT(!owner->HasObjectFlag(ETSObjectFlags::ENUM)); - CheckerStatus const status = - (owner->HasObjectFlag(ETSObjectFlags::CLASS) ? CheckerStatus::IN_CLASS : CheckerStatus::IN_INTERFACE) | - (owner->HasObjectFlag(ETSObjectFlags::ABSTRACT) ? CheckerStatus::IN_ABSTRACT : CheckerStatus::NO_OPTS) | - (owner->HasObjectFlag(ETSObjectFlags::INNER) ? CheckerStatus::INNER_CLASS : CheckerStatus::NO_OPTS) | - (owner->GetDeclNode()->IsClassDefinition() && owner->GetDeclNode()->AsClassDefinition()->IsLocal() - ? CheckerStatus::IN_LOCAL_CLASS - : CheckerStatus::NO_OPTS); - return SavedCheckerContext(checker, status, owner); -} +private: + static CheckerStatus StatusFrom(const ETSObjectType *owner) + { + if (owner == nullptr) { + return CheckerStatus::NO_OPTS; + } + ES2PANDA_ASSERT(!owner->HasObjectFlag(ETSObjectFlags::ENUM)); + return (owner->HasObjectFlag(ETSObjectFlags::CLASS) ? CheckerStatus::IN_CLASS : CheckerStatus::IN_INTERFACE) | + (owner->HasObjectFlag(ETSObjectFlags::ABSTRACT) ? CheckerStatus::IN_ABSTRACT : CheckerStatus::NO_OPTS) | + (owner->HasObjectFlag(ETSObjectFlags::INNER) ? CheckerStatus::INNER_CLASS : CheckerStatus::NO_OPTS) | + (owner->GetDeclNode()->IsClassDefinition() && owner->GetDeclNode()->AsClassDefinition()->IsLocal() + ? CheckerStatus::IN_LOCAL_CLASS + : CheckerStatus::NO_OPTS); + } +}; checker::Type *ETSAnalyzer::GetCallExpressionReturnType(ir::CallExpression *expr, checker::Type *calleeType) const { @@ -1616,8 +1636,7 @@ checker::Type *ETSAnalyzer::GetCallExpressionReturnType(ir::CallExpression *expr return checker->GlobalTypeError(); } - auto owner = const_cast(util::Helpers::GetContainingObjectType(signature->Function())); - SavedCheckerContext savedCtx(ReconstructOwnerClassContext(checker, owner)); + ReconstructOwnerClassContext savedCtx {checker, util::Helpers::GetContainingObjectType(signature->Function())}; ir::AstNode *methodDef = signature->Function(); while (!methodDef->IsMethodDefinition()) { @@ -3761,7 +3780,7 @@ checker::Type *ETSAnalyzer::Check(ir::TSInterfaceDeclaration *st) const st->SetTsType(stmtType); checker::ScopeContext scopeCtx(checker, st->Scope()); - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::IN_INTERFACE, interfaceType); + EmptyContext savedContext(checker, CheckerStatus::IN_INTERFACE, interfaceType); for (auto *it : st->Body()->Body()) { it->Check(checker); @@ -3885,7 +3904,7 @@ checker::Type *ETSAnalyzer::Check(ir::TSQualifiedName *expr) const checker::Type *ETSAnalyzer::Check(ir::TSTypeAliasDeclaration *st) const { ETSChecker *checker = GetETSChecker(); - auto checkerContext = SavedCheckerContext(checker, CheckerStatus::NO_OPTS, checker->Context().ContainingClass()); + EmptyContext savedContext {checker, {}, checker->Context().ContainingClass()}; checker->CheckAnnotations(st->Annotations()); diff --git a/ets2panda/checker/ETSAnalyzerHelpers.cpp b/ets2panda/checker/ETSAnalyzerHelpers.cpp index 021f906c1a..7bfce993b1 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.cpp +++ b/ets2panda/checker/ETSAnalyzerHelpers.cpp @@ -186,6 +186,26 @@ static void CheckNativeConstructorBody(ETSChecker *checker, ir::MethodDefinition } } +struct EnterMethodBodyContext : private NarrowedContext { + EnterMethodBodyContext(ETSChecker *checker, ir::MethodDefinition *method) + : NarrowedContext{checker, FlagsFrom(method, checker->Context().ContainingClass())} { + checker->Context().SetContainingSignature(checker->GetSignatureFromMethodDefinition(method)); + } + +private: + static CheckerStatus FlagsFrom(ir::MethodDefinition *method, const ETSObjectType *cls) { + CheckerStatus flags {}; + if (method->IsStatic() && !method->IsConstructor() && !cls->HasObjectFlag(checker::ETSObjectFlags::GLOBAL)) { + flags |= CheckerStatus::IN_STATIC_CONTEXT; + } + + if (method->IsConstructor()) { + flags |= CheckerStatus::IN_CONSTRUCTOR; + } + return flags; + } +}; + void DoBodyTypeChecking(ETSChecker *checker, ir::MethodDefinition *node, ir::ScriptFunction *scriptFunc) { if (scriptFunc->HasBody()) { @@ -197,19 +217,8 @@ void DoBodyTypeChecking(ETSChecker *checker, ir::MethodDefinition *node, ir::Scr return; } - checker::ScopeContext scopeCtx(checker, scriptFunc->Scope()); - checker::SavedCheckerContext savedContext(checker, checker->Context().Status(), - checker->Context().ContainingClass()); - checker->Context().SetContainingSignature(checker->GetSignatureFromMethodDefinition(node)); - - if (node->IsStatic() && !node->IsConstructor() && - !checker->Context().ContainingClass()->HasObjectFlag(checker::ETSObjectFlags::GLOBAL)) { - checker->AddStatus(checker::CheckerStatus::IN_STATIC_CONTEXT); - } - - if (node->IsConstructor()) { - checker->AddStatus(checker::CheckerStatus::IN_CONSTRUCTOR); - } + ScopeContext scopeCtx(checker, scriptFunc->Scope()); + EnterMethodBodyContext savedContext(checker, node); if (node->IsExtensionMethod()) { CheckExtensionMethod(checker, scriptFunc, node); @@ -234,8 +243,6 @@ void DoBodyTypeChecking(ETSChecker *checker, ir::MethodDefinition *node, ir::Scr } } } - - checker->Context().SetContainingSignature(nullptr); } void ComposeAsyncImplFuncReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc) diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index abf418beae..98808e574e 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -380,8 +380,7 @@ void ETSChecker::CheckProgram(parser::Program *program, bool runAnalysis) varbinder::RecordTableContext recordTableCtx(VarBinder()->AsETSBinder(), extProg); VarBinder()->AsETSBinder()->SetProgram(extProg); VarBinder()->AsETSBinder()->ResetTopScope(extProg->GlobalScope()); - checker::SavedCheckerContext savedContext(this, Context().Status(), Context().ContainingClass()); - AddStatus(checker::CheckerStatus::IN_EXTERNAL); + NarrowedContext savedContext {this, CheckerStatus::IN_EXTERNAL}; CheckProgram(extProg, VarBinder()->IsGenStdLib() || extProg->IsGenAbcForExternal()); VarBinder()->AsETSBinder()->SetProgram(savedProgram2); VarBinder()->AsETSBinder()->ResetTopScope(savedProgram2->GlobalScope()); diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 62c5776c6b..c183797ca5 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -251,7 +251,6 @@ public: void AddImplementedSignature(std::vector *implementedSignatures, varbinder::LocalVariable *function, ETSFunctionType *it); void CheckInnerClassMembers(const ETSObjectType *classType); - void CheckLocalClass(ir::ClassDefinition *classDef, CheckerStatus &checkerStatus); void CheckClassDefinition(ir::ClassDefinition *classDef); void CheckClassElement(ir::ClassDefinition *classDef); void CheckClassAnnotations(ir::ClassDefinition *classDef); diff --git a/ets2panda/checker/TSAnalyzer.cpp b/ets2panda/checker/TSAnalyzer.cpp index cfaad3f534..cca60bf63d 100644 --- a/ets2panda/checker/TSAnalyzer.cpp +++ b/ets2panda/checker/TSAnalyzer.cpp @@ -15,6 +15,7 @@ #include "TSAnalyzer.h" +#include "checker.h" #include "checker/TSchecker.h" #include "checker/ts/destructuringContext.h" @@ -378,7 +379,7 @@ checker::Type *TSAnalyzer::Check(ir::AssignmentExpression *expr) const { TSChecker *checker = GetTSChecker(); if (expr->Left()->IsArrayPattern()) { - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + EmptyContext savedContext(checker, CheckerStatus::FORCE_TUPLE); auto destructuringContext = checker::ArrayDestructuringContext({checker, expr->Left(), true, true, nullptr, expr->Right()}); destructuringContext.Start(); @@ -386,7 +387,7 @@ checker::Type *TSAnalyzer::Check(ir::AssignmentExpression *expr) const } if (expr->Left()->IsObjectPattern()) { - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + EmptyContext savedContext(checker, CheckerStatus::FORCE_TUPLE); auto destructuringContext = checker::ObjectDestructuringContext({checker, expr->Left(), true, true, nullptr, expr->Right()}); destructuringContext.Start(); @@ -1377,7 +1378,7 @@ checker::Type *TSAnalyzer::Check(ir::VariableDeclarator *st) const } if (st->Id()->IsArrayPattern()) { - auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + EmptyContext savedContext(checker, CheckerStatus::FORCE_TUPLE); checker::ArrayDestructuringContext({checker, st->Id(), false, st->Id()->AsArrayPattern()->TypeAnnotation() == nullptr, st->Id()->AsArrayPattern()->TypeAnnotation(), st->Init()}) @@ -1388,7 +1389,7 @@ checker::Type *TSAnalyzer::Check(ir::VariableDeclarator *st) const } ES2PANDA_ASSERT(st->Id()->IsObjectPattern()); - auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + EmptyContext savedContext(checker, CheckerStatus::FORCE_TUPLE); checker::ObjectDestructuringContext({checker, st->Id(), false, st->Id()->AsObjectPattern()->TypeAnnotation() == nullptr, st->Id()->AsObjectPattern()->TypeAnnotation(), st->Init()}) @@ -1475,7 +1476,7 @@ checker::Type *TSAnalyzer::Check(ir::TSAsExpression *expr) const { TSChecker *checker = GetTSChecker(); if (expr->IsConst()) { - auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::IN_CONST_CONTEXT); + EmptyContext context(checker, CheckerStatus::IN_CONST_CONTEXT); checker::Type *exprType = expr->Expr()->Check(checker); if (!IsValidConstAssertionArgument(checker, expr->Expr())) { @@ -1488,7 +1489,7 @@ checker::Type *TSAnalyzer::Check(ir::TSAsExpression *expr) const return exprType; } - auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::NO_OPTS); + EmptyContext context(checker, CheckerStatus::NO_OPTS); expr->TypeAnnotation()->Check(checker); checker::Type *exprType = checker->GetBaseTypeOfLiteralType(expr->Expr()->Check(checker)); diff --git a/ets2panda/checker/checker.h b/ets2panda/checker/checker.h index 04d1c37692..54dddebc9c 100644 --- a/ets2panda/checker/checker.h +++ b/ets2panda/checker/checker.h @@ -93,12 +93,13 @@ public: void RemoveStatus(CheckerStatus status) noexcept { - context_.Status() &= ~status; + ES2PANDA_ASSERT(!IsETSChecker()); // Flags should be erased via SavedCheckerContext. + context_.SetStatus(static_cast(context_.Status() & ~status)); } void AddStatus(CheckerStatus status) noexcept { - context_.Status() |= status; + context_.SetStatus(static_cast(context_.Status() | status)); } [[nodiscard]] TypeRelation *Relation() const noexcept @@ -212,6 +213,7 @@ public: friend class ScopeContext; friend class TypeStackElement; friend class NamedTypeStackElement; + template friend class SavedCheckerContext; friend class NamedTypeStackElement; @@ -390,43 +392,61 @@ private: parser::Program *prevProgram_; }; -class SavedCheckerContext { -public: - explicit SavedCheckerContext(Checker *checker, CheckerStatus newStatus) - : SavedCheckerContext(checker, newStatus, nullptr) - { - } - - explicit SavedCheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass) - : SavedCheckerContext(checker, newStatus, containingClass, nullptr) - { - } - - explicit SavedCheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass, - Signature *containingSignature) +template +struct SavedCheckerContext { +protected: + SavedCheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass) : checker_(checker), prev_(checker->context_) { - const bool inExternal = checker->HasStatus(CheckerStatus::IN_EXTERNAL); - checker_->context_ = CheckerContext(checker, newStatus, containingClass, containingSignature); - if (inExternal) { - // handled here instead of at call sites to make things more foolproof - checker_->context_.Status() |= CheckerStatus::IN_EXTERNAL; + if constexpr (CTX_BEING_NARROWED) { + // Check new context is narrower (i.e. has more flags). + ES2PANDA_ASSERT(All(newStatus, checker_->context_.Status())); + ES2PANDA_ASSERT((containingClass != nullptr) || prev_.ContainingClass() == nullptr); + checker_->context_.SetStatus(newStatus); + checker_->context_.SetContainingClass(containingClass); + } else { + // Implicitly copy 'sticky' flags: + newStatus |= static_cast(prev_.Status() & STICKY_FLAGS); + checker_->context_ = CheckerContext(checker, newStatus, containingClass); } } - NO_COPY_SEMANTIC(SavedCheckerContext); - DEFAULT_MOVE_SEMANTIC(SavedCheckerContext); - ~SavedCheckerContext() { - checker_->context_ = prev_; + checker_->context_ = std::move(prev_); } + NO_COPY_SEMANTIC(SavedCheckerContext); + NO_MOVE_SEMANTIC(SavedCheckerContext); + +private: + static constexpr auto STICKY_FLAGS = CheckerStatus::BUILTINS_INITIALIZED; + private: Checker *checker_; CheckerContext prev_; }; +struct NarrowedContext : private SavedCheckerContext<> { + NarrowedContext(Checker *checker, CheckerStatus flagsExtension) + : NarrowedContext(checker, flagsExtension, checker->Context().ContainingClass()) + { + } + + NarrowedContext(Checker *checker, CheckerStatus flagsExtension, const ETSObjectType *containingClass) + : SavedCheckerContext(checker, checker->Context().Status() | flagsExtension, containingClass) + { + } +}; + +struct EmptyContext : private SavedCheckerContext { + EmptyContext(Checker *checker, CheckerStatus flags = CheckerStatus::NO_OPTS, + const ETSObjectType *containingClass = nullptr) + : SavedCheckerContext(checker, flags, containingClass) + { + } +}; + // VerifiedType is used to check return type from Expresssion not equal nullptr class VerifiedType { public: diff --git a/ets2panda/checker/checkerContext.h b/ets2panda/checker/checkerContext.h index aa5e6b26fc..e7015182c6 100644 --- a/ets2panda/checker/checkerContext.h +++ b/ets2panda/checker/checkerContext.h @@ -64,7 +64,7 @@ enum class CheckerStatus : uint32_t { IGNORE_VISIBILITY = 1U << 14U, IN_EXTENSION_METHOD = 1U << 15U, IN_LOCAL_CLASS = 1U << 16U, - IN_INSTANCEOF_CONTEXT = 1U << 17U, + IN_INSTANCEOF_CONTEXT = 1U << 17U, // NOTE(dkofanov): Deprecated, need to be removed. IN_TEST_EXPRESSION = 1U << 18U, IN_LOOP = 1U << 19U, MEET_RETURN = 1U << 20U, @@ -160,9 +160,9 @@ public: return containingSignature_; } - [[nodiscard]] CheckerStatus &Status() noexcept + void SetStatus(CheckerStatus newStatus) { - return status_; + status_ = newStatus; } void SetContainingSignature(Signature *containingSignature) noexcept @@ -170,7 +170,7 @@ public: containingSignature_ = containingSignature; } - void SetContainingClass(ETSObjectType *containingClass) noexcept + void SetContainingClass(const ETSObjectType *containingClass) noexcept { containingClass_ = containingClass; } @@ -265,7 +265,6 @@ public: void OnBreakStatement(ir::BreakStatement const *breakStatement); void AddBreakSmartCasts(ir::Statement const *targetStatement, SmartCastArray &&smartCasts); void CombineBreakSmartCasts(ir::Statement const *targetStatement); - friend class SavedCheckerContextStatus; private: Checker *parent_; @@ -293,27 +292,6 @@ private: void CheckAssignments(ir::AstNode const *node, ReassignedVariableMap &changedVariables) const noexcept; }; -class SavedCheckerContextStatus final { -public: - explicit SavedCheckerContextStatus(CheckerContext *context, CheckerStatus status) - : context_(context), storedStatus_(status), preStatus_(context_->status_) - { - context_->status_ = context_->status_ | storedStatus_; - } - - ~SavedCheckerContextStatus() - { - context_->status_ = preStatus_; - } - - NO_COPY_SEMANTIC(SavedCheckerContextStatus); - DEFAULT_MOVE_SEMANTIC(SavedCheckerContextStatus); - -private: - CheckerContext *context_; - CheckerStatus storedStatus_; - CheckerStatus preStatus_; -}; } // namespace ark::es2panda::checker #endif diff --git a/ets2panda/checker/ets/arithmetic.cpp b/ets2panda/checker/ets/arithmetic.cpp index cbe2c38d4e..bb9aedbd3e 100644 --- a/ets2panda/checker/ets/arithmetic.cpp +++ b/ets2panda/checker/ets/arithmetic.cpp @@ -825,8 +825,6 @@ std::tuple ETSChecker::CheckBinaryOperatorInstanceOf(lexer::Sour } checker::Type *opType = GlobalETSObjectType(); - RemoveStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT); - return {GlobalETSBooleanBuiltinType(), opType}; } @@ -1194,10 +1192,6 @@ std::tuple ETSChecker::CheckBinaryOperator(ir::Expression *left, return {rightType, rightType}; } - if (operationType == lexer::TokenType::KEYW_INSTANCEOF) { - AddStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT); - } - Context().CheckTestSmartCastCondition(operationType); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) checker::Type *rightType = right->Check(this); diff --git a/ets2panda/checker/ets/function_helpers.h b/ets2panda/checker/ets/function_helpers.h index 7c22e2f83b..203dca52a1 100644 --- a/ets2panda/checker/ets/function_helpers.h +++ b/ets2panda/checker/ets/function_helpers.h @@ -75,7 +75,7 @@ static void InferUntilFail(Signature const *const signature, const ArenaVectorAddStatus(checker::CheckerStatus::IN_TYPE_INFER); + NarrowedContext ctx {checker, checker::CheckerStatus::IN_TYPE_INFER}; // some ets lib files require type infer from arg index 0,1,... , not fit to build graph while (anyChange && substitution->size() < sigParams.size()) { anyChange = false; @@ -113,7 +113,6 @@ static void InferUntilFail(Signature const *const signature, const ArenaVectorRemoveStatus(checker::CheckerStatus::IN_TYPE_INFER); } static std::optional BuildImplicitSubstitutionForArguments(ETSChecker *checker, Signature *signature, diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index 7cfe16d58e..3b4f631373 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -475,13 +475,9 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(interfaceType) : interfaceType; } - // Save before we mess with savedContext. - bool builtinsInitialized = HasStatus(CheckerStatus::BUILTINS_INITIALIZED); - auto *enclosingClass = Context().ContainingClass(); interfaceType->SetEnclosingType(enclosingClass); - CheckerStatus newStatus = CheckerStatus::IN_INTERFACE; - auto savedContext = checker::SavedCheckerContext(this, newStatus, interfaceType); + EmptyContext savedContext {this, CheckerStatus::IN_INTERFACE, interfaceType}; ConstraintCheckScope ctScope(this); if (interfaceDecl->TypeParams() != nullptr) { interfaceType->AddTypeFlag(TypeFlag::GENERIC); @@ -495,7 +491,7 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte // Skip this check if the builtins are not initialized. // They will be initialized in different order, // and it is possible that the FunctionType interface is not yet created. - if (builtinsInitialized) { + if (HasStatus(CheckerStatus::BUILTINS_INITIALIZED)) { CheckInterfaceFunctions(interfaceType); } @@ -542,14 +538,13 @@ Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) auto *enclosingClass = Context().ContainingClass(); classType->SetEnclosingType(enclosingClass); - CheckerStatus newStatus = CheckerStatus::IN_CLASS; + auto newFlags = CheckerStatus::IN_CLASS; if (classDef->IsInner()) { - newStatus |= CheckerStatus::INNER_CLASS; + newFlags |= CheckerStatus::INNER_CLASS; classType->AddObjectFlag(checker::ETSObjectFlags::INNER); } - - auto savedContext = checker::SavedCheckerContext(this, newStatus, classType); + EmptyContext savedContext {this, newFlags, classType}; if (!classType->HasObjectFlag(ETSObjectFlags::RESOLVED_SUPER)) { GetSuperType(classType); @@ -686,24 +681,19 @@ void ETSChecker::ResolveDeclaredMembersOfObject(const Type *type) if (objectType->IsGeneric() && objectType != objectType->GetOriginalBaseType()) { const auto *baseType = objectType->GetOriginalBaseType(); auto *baseDeclNode = baseType->GetDeclNode(); - checker::CheckerStatus baseStatus = baseDeclNode->IsTSInterfaceDeclaration() - ? checker::CheckerStatus::IN_INTERFACE - : checker::CheckerStatus::IN_CLASS; - auto baseScope = baseDeclNode->IsTSInterfaceDeclaration() ? baseDeclNode->AsTSInterfaceDeclaration()->Scope() - : baseDeclNode->AsClassDefinition()->Scope(); - auto savedContext = checker::SavedCheckerContext(this, baseStatus, baseType); - checker::ScopeContext scopeCtx(this, baseScope); + const bool isInterface = baseDeclNode->IsTSInterfaceDeclaration(); + EmptyContext savedContext {this, isInterface ? CheckerStatus::IN_INTERFACE : CheckerStatus::IN_CLASS, + baseType}; + ScopeContext scopeCtx(this, isInterface ? baseDeclNode->AsTSInterfaceDeclaration()->Scope() + : baseDeclNode->AsClassDefinition()->Scope()); ResolveDeclaredMembersOfObject(baseType); return; } - checker::CheckerStatus status = - declNode->IsTSInterfaceDeclaration() ? checker::CheckerStatus::IN_INTERFACE : checker::CheckerStatus::IN_CLASS; - auto *scope = declNode->IsTSInterfaceDeclaration() ? declNode->AsTSInterfaceDeclaration()->Scope() - : declNode->AsClassDefinition()->Scope(); - auto savedContext = checker::SavedCheckerContext(this, status, objectType); - checker::ScopeContext scopeCtx(this, scope); - + const bool isInterface = declNode->IsTSInterfaceDeclaration(); + auto *scope = isInterface ? declNode->AsTSInterfaceDeclaration()->Scope() : declNode->AsClassDefinition()->Scope(); + EmptyContext savedContext(this, isInterface ? CheckerStatus::IN_INTERFACE : CheckerStatus::IN_CLASS, objectType); + ScopeContext scopeCtx(this, scope); ResolveDeclaredDeclsOfObject(this, objectType, scope->AsClassScope()); ResolveDeclaredFieldsOfObject(this, objectType, scope->AsClassScope()); ResolveDeclaredMethodsOfObject(this, objectType, scope->AsClassScope()); @@ -1219,23 +1209,56 @@ void ETSChecker::AddImplementedSignature(std::vector *implementedSi } } -void ETSChecker::CheckLocalClass(ir::ClassDefinition *classDef, CheckerStatus &checkerStatus) -{ - if (!classDef->IsLocal()) { - return; +struct EnterClassBodyContext : private EmptyContext { + EnterClassBodyContext(ETSChecker *checker, ir::ClassDefinition *classDef, ETSObjectType *classType) + : EmptyContext(checker, SetupContextFlags(checker, classDef, classType), classType) + { } - checkerStatus |= CheckerStatus::IN_LOCAL_CLASS; - if (!classDef->Parent()->Parent()->IsBlockStatement()) { - LogError(diagnostic::LOCAL_CLASS_INVALID_CTX, {}, classDef->Start()); + +private: + CheckerStatus SetupContextFlags(ETSChecker *checker, ir::ClassDefinition *classDef, ETSObjectType *classType) + { + if (checker->Context().ContainingClass() != classType) { + classType->SetEnclosingType(checker->Context().ContainingClass()); + } + + auto flags = CheckerStatus::IN_CLASS; + if (classDef->IsInner()) { + flags |= CheckerStatus::INNER_CLASS; + classType->AddObjectFlag(ETSObjectFlags::INNER); + } + if (classDef->IsModule()) { + classType->AddObjectFlag(ETSObjectFlags::GLOBAL); + } else if (classDef->IsLocal()) { + flags |= CheckerStatus::IN_LOCAL_CLASS; + CheckLocalClass(checker, classDef); + } + + if (classDef->IsAbstract()) { + flags |= CheckerStatus::IN_ABSTRACT; + classType->AddObjectFlag(ETSObjectFlags::ABSTRACT); + } + + if (classDef->IsStatic() && !classType->HasObjectFlag(ETSObjectFlags::GLOBAL)) { + flags |= CheckerStatus::IN_STATIC_CONTEXT; + } + return flags; } - // NOTE(dkofanov): Related to spec 17.9.3 expecting CTE (native methods in local classes). - // Actually, if I'm not mistaken, the only reason to forbid this is problematic binding of native method to the - // mangled local class method, which is not really a reason for such restrictions. The spec should be revisited in - // the future. - if (classDef->HasNativeMethod()) { - LogError(diagnostic::LOCAL_CLASS_NATIVE_METHOD, {classDef->Ident()->Name()}, classDef->Start()); + + static void CheckLocalClass(ETSChecker *checker, ir::ClassDefinition *classDef) + { + if (!classDef->Parent()->Parent()->IsBlockStatement()) { + checker->LogError(diagnostic::LOCAL_CLASS_INVALID_CTX, {}, classDef->Start()); + } + // NOTE(dkofanov): Related to spec 17.9.3 expecting CTE (native methods in local classes). + // It seems that the only reason to forbid this is problematic binding of native method to the + // mangled local class method, which is not really a reason for such restrictions. The spec should be revisited + // in the future. + if (classDef->HasNativeMethod()) { + checker->LogError(diagnostic::LOCAL_CLASS_NATIVE_METHOD, {classDef->Ident()->Name()}, classDef->Start()); + } } -} +}; // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic void ETSChecker::CheckClassDefinition(ir::ClassDefinition *classDef) @@ -1256,33 +1279,10 @@ void ETSChecker::CheckClassDefinition(ir::ClassDefinition *classDef) classType->SuperType()->GetDeclNode()->Check(this); } - auto newStatus = checker::CheckerStatus::IN_CLASS; - if (Context().ContainingClass() != classType) { - classType->SetEnclosingType(Context().ContainingClass()); - } - - if (classDef->IsInner()) { - newStatus |= CheckerStatus::INNER_CLASS; - classType->AddObjectFlag(checker::ETSObjectFlags::INNER); - } - - classDef->IsModule() ? classType->AddObjectFlag(checker::ETSObjectFlags::GLOBAL) - : CheckLocalClass(classDef, newStatus); - + EnterClassBodyContext savedContext {this, classDef, classType}; checker::ScopeContext scopeCtx(this, classDef->Scope()); - auto savedContext = SavedCheckerContext(this, newStatus, classType); ResolveDeclaredMembersOfObject(classType); - - if (classDef->IsAbstract()) { - AddStatus(checker::CheckerStatus::IN_ABSTRACT); - classType->AddObjectFlag(checker::ETSObjectFlags::ABSTRACT); - } - - if (classDef->IsStatic() && !Context().ContainingClass()->HasObjectFlag(ETSObjectFlags::GLOBAL)) { - AddStatus(checker::CheckerStatus::IN_STATIC_CONTEXT); - } - // NOTE(gogabr): temporary, until we have proper bridges, see #16485 // Don't check overriding for synthetic functional classes. if ((static_cast(classDef)->Modifiers() & ir::ModifierFlags::FUNCTIONAL) == 0) { diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index f2a5fd239c..4ef305f00a 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -619,7 +619,7 @@ Type *ETSChecker::GetTypeOfVariable(varbinder::Variable *const var) return var->TsType(); } - checker::SavedCheckerContext savedContext(this, CheckerStatus::NO_OPTS); + EmptyContext savedContext(this); checker::ScopeContext scopeCtx(this, var->GetScope()); IterateInVariableContext(var); @@ -829,14 +829,11 @@ Type *ETSChecker::GetTypeFromInterfaceReference(varbinder::Variable *var) return var->TsType(); } - CheckerStatus status = CheckerStatus::IN_STATIC_CONTEXT; - status &= this->Context().Status(); - this->Context().Status() &= ~CheckerStatus::IN_STATIC_CONTEXT; + // Reconstruct context to check the interface. Should be more precise. + EmptyContext savedCtx {this}; auto *interfaceType = BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration()); var->SetTsType(interfaceType); - - this->Context().Status() |= status; return interfaceType; } @@ -848,13 +845,11 @@ Type *ETSChecker::GetTypeFromClassReference(varbinder::Variable *var) auto classDef = var->Declaration()->Node()->AsClassDefinition(); - CheckerStatus status = CheckerStatus::IN_STATIC_CONTEXT; - status &= this->Context().Status(); - this->Context().Status() &= ~CheckerStatus::IN_STATIC_CONTEXT; + // Reconstruct context to check the class. Should be more precise. + EmptyContext savedCtx {this}; auto *classType = BuildBasicClassProperties(classDef); var->SetTsType(classType); - this->Context().Status() |= status; return classType; } diff --git a/ets2panda/checker/ets/typeRelationContext.cpp b/ets2panda/checker/ets/typeRelationContext.cpp index 80bfff4d4e..3ed864aa61 100644 --- a/ets2panda/checker/ets/typeRelationContext.cpp +++ b/ets2panda/checker/ets/typeRelationContext.cpp @@ -45,13 +45,6 @@ bool AssignmentContext::ValidateArrayTypeInitializerByElement(TypeRelation *rela bool InstantiationContext::ValidateTypeArguments(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs, const lexer::SourcePosition &pos) { - if (checker_->HasStatus(CheckerStatus::IN_INSTANCEOF_CONTEXT)) { - if (typeArgs != nullptr) { - checker_->LogDiagnostic(diagnostic::INSTANCEOF_ERASED, {type->Name()}, pos); - } - result_ = type; - return true; - } if (!checker_->CheckNumberOfTypeArguments(type, typeArgs, pos)) { result_ = checker_->GlobalTypeError(); return true; diff --git a/ets2panda/checker/ts/function.cpp b/ets2panda/checker/ts/function.cpp index e4419c3f8c..fe1e3bba60 100644 --- a/ets2panda/checker/ts/function.cpp +++ b/ets2panda/checker/ts/function.cpp @@ -225,7 +225,7 @@ ReturnedVariable TSChecker::CheckFunctionAssignmentPatternParameter(ir::Assignme Type *paramType = nullptr; std::stringstream ss; - auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE | CheckerStatus::IN_PARAMETER); + EmptyContext savedContext {this, CheckerStatus::FORCE_TUPLE | CheckerStatus::IN_PARAMETER}; if (param->Left()->IsArrayPattern()) { ir::ArrayExpression *arrayPattern = param->Left()->AsArrayPattern(); @@ -278,7 +278,7 @@ std::tuple TSCheck } case ir::AstNodeType::OBJECT_PATTERN: { ES2PANDA_ASSERT(param->Argument()->IsObjectPattern()); - auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE); + EmptyContext savedContext {this, CheckerStatus::FORCE_TUPLE}; auto destructuringContext = ObjectDestructuringContext({this, param->Argument(), false, false, nullptr, nullptr}); destructuringContext.SetInferredType(restType); @@ -287,7 +287,7 @@ std::tuple TSCheck return {nullptr, nullptr, false}; } case ir::AstNodeType::ARRAY_PATTERN: { - auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE); + EmptyContext savedContext {this, CheckerStatus::FORCE_TUPLE}; auto destructuringContext = ArrayDestructuringContext({this, param->Argument(), false, false, nullptr, nullptr}); destructuringContext.SetInferredType(restType); @@ -311,7 +311,7 @@ std::tuple TSCheck varbinder::Scope::CreateVar(Allocator(), pn.View(), varbinder::VariableFlags::NONE, param); if (param->TypeAnnotation() != nullptr) { - auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE); + NarrowedContext savedContext {this, CheckerStatus::FORCE_TUPLE}; auto destructuringContext = ArrayDestructuringContext({this, param->AsArrayPattern(), false, false, param->TypeAnnotation(), nullptr}); destructuringContext.Start(); @@ -333,7 +333,7 @@ std::tuple TSCheck varbinder::Scope::CreateVar(Allocator(), pn.View(), varbinder::VariableFlags::NONE, param); if (param->TypeAnnotation() != nullptr) { - auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE); + EmptyContext savedContext {this, CheckerStatus::FORCE_TUPLE}; auto destructuringContext = ObjectDestructuringContext( {this, param->AsObjectPattern(), false, false, param->TypeAnnotation(), nullptr}); destructuringContext.Start(); diff --git a/ets2panda/checker/ts/helpers.cpp b/ets2panda/checker/ts/helpers.cpp index 9c2455f558..141a7717b2 100644 --- a/ets2panda/checker/ts/helpers.cpp +++ b/ets2panda/checker/ts/helpers.cpp @@ -322,7 +322,7 @@ Type *TSChecker::GetUnaryResultType(Type *operandType) void TSChecker::ElaborateElementwise(Type *targetType, ir::Expression *sourceNode, const lexer::SourcePosition &pos) { - auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE | CheckerStatus::KEEP_LITERAL_TYPE); + EmptyContext savedContext {this, CheckerStatus::FORCE_TUPLE | CheckerStatus::KEEP_LITERAL_TYPE}; Type *sourceType = CheckTypeCached(sourceNode); diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 8992e44632..42dde06274 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -22,6 +22,7 @@ #include "compiler/core/targetTypeContext.h" #include "compiler/core/vReg.h" #include "compiler/function/functionBuilder.h" +#include "compiler/lowering/util.h" #include "checker/ETSchecker.h" #include "checker/types/ets/etsTupleType.h" #include "ETSGen-inl.h" @@ -687,7 +688,7 @@ bool IsCastCall(checker::Signature *signature) ES2PANDA_ASSERT(signature->HasSignatureFlag(checker::SignatureFlags::STATIC)); auto *func = signature->Function(); return (func->Parent()->Parent()->IsMethodDefinition() && IsCastCallName(func->Id()->Name()) && - util::Helpers::ContainingClass(func)->AsETSObjectType()->IsBoxedPrimitive() && + ContainingClass(func)->IsBoxedPrimitive() && (signature->Params().size() == 1) && signature->Params()[0]->TsType()->IsETSPrimitiveType()); } diff --git a/ets2panda/compiler/lowering/ets/asyncMethodLowering.cpp b/ets2panda/compiler/lowering/ets/asyncMethodLowering.cpp index 4d403512f6..5036caacbc 100644 --- a/ets2panda/compiler/lowering/ets/asyncMethodLowering.cpp +++ b/ets2panda/compiler/lowering/ets/asyncMethodLowering.cpp @@ -189,8 +189,7 @@ void HandleMethod(checker::ETSChecker *checker, ir::MethodDefinition *node) void UpdateClassDefintion(checker::ETSChecker *checker, ir::ClassDefinition *classDef) { - checker::SavedCheckerContext savedContext(checker, checker->Context().Status(), - classDef->TsType()->AsETSObjectType()); + checker::NarrowedContext savedContext(checker, {}, classDef->TsType()->AsETSObjectType()); for (auto *it : classDef->Body()) { if (it->IsMethodDefinition()) { HandleMethod(checker, it->AsMethodDefinition()); diff --git a/ets2panda/compiler/lowering/ets/boxingForLocals.cpp b/ets2panda/compiler/lowering/ets/boxingForLocals.cpp index ce8e088aa6..0368fc34de 100644 --- a/ets2panda/compiler/lowering/ets/boxingForLocals.cpp +++ b/ets2panda/compiler/lowering/ets/boxingForLocals.cpp @@ -168,7 +168,7 @@ static void HandleFunctionParam(public_lib::Context *ctx, ir::ETSParameterExpres bodyStmts.insert(bodyStmts.begin(), newDeclaration); auto lexScope = varbinder::LexicalScope::Enter(varBinder, scope); - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::NO_OPTS); + checker::EmptyContext savedContext {checker}; auto scopeContext = checker::ScopeContext(checker, scope); newDeclaration->Check(checker); @@ -221,7 +221,7 @@ static ir::AstNode *HandleVariableDeclarator(public_lib::Context *ctx, ir::Varia scope->InsertBinding(newVar->Name(), newVar); auto lexScope = varbinder::LexicalScope::Enter(varBinder, scope); - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::NO_OPTS); + checker::EmptyContext savedContext {checker}; auto scopeContext = checker::ScopeContext(checker, scope); newDeclarator->Check(checker); @@ -303,7 +303,7 @@ static ir::AstNode *HandleAssignment(public_lib::Context *ctx, ir::AssignmentExp // NOTE(gogabr) -- The `get` and `set` properties remain without variable; this is OK for the current checker, but // may need adjustment later. auto lexScope = varbinder::LexicalScope::Enter(varBinder, scope); - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::NO_OPTS); + checker::EmptyContext savedContext {checker}; auto scopeContext = checker::ScopeContext(checker, scope); varBinder->ResolveReferencesForScopeWithContext(res, scope); diff --git a/ets2panda/compiler/lowering/ets/declareOverloadLowering.cpp b/ets2panda/compiler/lowering/ets/declareOverloadLowering.cpp index 434636afec..08f3b2456f 100644 --- a/ets2panda/compiler/lowering/ets/declareOverloadLowering.cpp +++ b/ets2panda/compiler/lowering/ets/declareOverloadLowering.cpp @@ -87,7 +87,7 @@ void BuildOverloadHelperFunction(public_lib::Context *ctx, ir::MethodDefinition helperOverload->Function()->ClearFlag((ir::ScriptFunctionFlags::OVERLOAD)); helperOverload->SetParent(method); - checker::SavedCheckerContext scc(checker, checker->Context().Status(), method->Function()->Signature()->Owner()); + checker::NarrowedContext scc(checker, {}, method->Function()->Signature()->Owner()); Rebind(ctx->phaseManager, varBinder, helperOverload); helperOverload->Function()->AddFlag((ir::ScriptFunctionFlags::OVERLOAD)); auto funcScope = helperOverload->Function()->Scope(); diff --git a/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp b/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp index abd8401d58..b2e830942d 100644 --- a/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp +++ b/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp @@ -158,8 +158,8 @@ static ir::CallExpression *CreateCallInstanceEnumExpression(public_lib::Context auto lexScope = varbinder::LexicalScope::Enter(varBinder, nearestScope); varBinder->ResolveReferencesForScopeWithContext(callExpr, nearestScope); - auto checkerCtx = checker::SavedCheckerContext(checker, checker::CheckerStatus::IN_CLASS, - calleeClass->Definition()->TsType()->AsETSObjectType()); + checker::EmptyContext checkerCtx(checker, checker::CheckerStatus::IN_CLASS, + calleeClass->Definition()->TsType()->AsETSObjectType()); auto scopeCtx = checker::ScopeContext(checker, nearestScope); callExpr->Check(checker); @@ -401,8 +401,7 @@ bool EnumPostCheckLoweringPhase::PerformForModule(public_lib::Context *ctx, pars node->AsExpression()->SetTsType(nullptr); // force recheck } auto *parentClass = util::Helpers::FindAncestorGivenByType(node, ir::AstNodeType::CLASS_DEFINITION); - checker::SavedCheckerContext savedContext(checker_, checker_->Context().Status(), - parentClass->AsClassDefinition()->TsType()->AsETSObjectType()); + checker::NarrowedContext savedContext(checker_, {}, parentClass->AsClassDefinition()->TsType()->AsETSObjectType()); node->RemoveAstNodeFlags(ir::AstNodeFlags::RECHECK); node->Check(checker_); if (node->IsExpression() && node->AsExpression()->TsType() != nullptr && diff --git a/ets2panda/compiler/lowering/ets/extensionAccessorLowering.cpp b/ets2panda/compiler/lowering/ets/extensionAccessorLowering.cpp index 0ec34677b1..811c0afe0d 100644 --- a/ets2panda/compiler/lowering/ets/extensionAccessorLowering.cpp +++ b/ets2panda/compiler/lowering/ets/extensionAccessorLowering.cpp @@ -61,8 +61,7 @@ static void SwitchType(ir::MemberExpression *expr) static void TryHandleExtensionAccessor(checker::ETSChecker *checker, ir::MemberExpression *expr) { - checker::SavedCheckerContextStatus ccStatusHelper(&checker->Context(), - checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK); + checker::NarrowedContext ccStatusHelper(checker, checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK); SwitchType(expr); auto oldParent = expr->Parent(); if (IsAssignExprExtensionSetter(oldParent) && expr == oldParent->AsAssignmentExpression()->Left()) { @@ -86,7 +85,7 @@ static void TryHandleExtensionAccessor(checker::ETSChecker *checker, ir::MemberE callExpr->AsCallExpression()->Arguments().emplace_back(rightExpr); if (!ResolveAssignmentExpressionToExtensionAccessorCall(assignExpr, expr)) { return; - }; + } CheckLoweredNode(checker->VarBinder()->AsETSBinder(), checker, callExpr); return; } diff --git a/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp b/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp index 1e2d1d47ff..1cdc06e680 100644 --- a/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp +++ b/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp @@ -101,11 +101,10 @@ void GenericBridgesPhase::AddGenericBridge(ir::ClassDefinition const *const clas varBinder->AsETSBinder()->ResolveReferencesForScopeWithContext(bridgeMethod, scope); auto *checker = context_->GetChecker()->AsETSChecker(); - auto const checkerCtx = - checker::SavedCheckerContext(checker, - checker::CheckerStatus::IN_CLASS | checker::CheckerStatus::IGNORE_VISIBILITY | - checker::CheckerStatus::IN_BRIDGE_TEST, - classDefinition->TsType()->AsETSObjectType()); + checker::EmptyContext checkerCtx = {checker, + checker::CheckerStatus::IN_CLASS | checker::CheckerStatus::IGNORE_VISIBILITY | + checker::CheckerStatus::IN_BRIDGE_TEST, + classDefinition->TsType()->AsETSObjectType()}; auto scopeCtx = checker::ScopeContext(checker, scope); // Note: we need to create and set function/method type here because the general method `BuildMethodSignature(...)` @@ -137,9 +136,8 @@ void GenericBridgesPhase::ProcessScriptFunction(ir::ClassDefinition const *const auto const overrides = [checker, relation, classDefinition](checker::Signature const *source, checker::Signature const *target) -> bool { - checker::SavedCheckerContext const checkerCtx( - checker, checker->Context().Status() | checker::CheckerStatus::IN_BRIDGE_TEST, - classDefinition->TsType()->AsETSObjectType()); + checker::NarrowedContext const checkerCtx(checker, checker::CheckerStatus::IN_BRIDGE_TEST, + classDefinition->TsType()->AsETSObjectType()); checker::SavedTypeRelationFlagsContext const savedFlags(relation, checker::TypeRelationFlag::BRIDGE_CHECK); return relation->SignatureIsSupertypeOf(const_cast(source), const_cast(target)); diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index 17cfcc6a9a..4a3a44012b 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -309,8 +309,8 @@ static ir::MethodDefinition *SetUpCalleeMethod(public_lib::Context *ctx, LambdaI varbinder::BoundContext bctx {varBinder->GetRecordTable(), calleeClass->Definition(), true}; varBinder->ResolveReferencesForScopeWithContext(func, funcScope); - auto checkerCtx = checker::SavedCheckerContext(ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, - calleeClass->Definition()->TsType()->AsETSObjectType()); + checker::EmptyContext checkerCtx {ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, + calleeClass->Definition()->TsType()->AsETSObjectType()}; method->Check(ctx->GetChecker()->AsETSChecker()); return method; @@ -958,8 +958,8 @@ static ir::ETSNewClassInstanceExpression *CreateConstructorCall(public_lib::Cont checker::Type *objectType = info->calleeClass != nullptr ? info->calleeClass->Definition()->TsType() : info->calleeInterface->TsType(); - auto checkerCtx = checker::SavedCheckerContext(ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, - objectType->AsETSObjectType()); + checker::EmptyContext checkerCtx {ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, + objectType->AsETSObjectType()}; auto scopeCtx = checker::ScopeContext(ctx->GetChecker(), nearestScope); newExpr->Check(checker); @@ -1060,8 +1060,8 @@ static ir::ArrowFunctionExpression *CreateWrappingLambda(public_lib::Context *ct auto [enclosingClass, _] = FindEnclosingClassAndFunction(parent); - auto checkerCtx = checker::SavedCheckerContext(ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, - enclosingClass->Definition()->TsType()->AsETSObjectType()); + checker::EmptyContext checkerCtx {ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, + enclosingClass->Definition()->TsType()->AsETSObjectType()}; auto scopeCtx = checker::ScopeContext(ctx->GetChecker(), nearestScope); lambda->Check(ctx->GetChecker()->AsETSChecker()); diff --git a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp index 8e0fbcfb05..2be2ef2bcd 100644 --- a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp @@ -287,7 +287,7 @@ static ir::AstNode *HandleObjectLiteralLowering(public_lib::Context *ctx, ir::Ob AllowRequiredTypeInstantiation(loweringResult); - checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; + checker::EmptyContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; loweringResult->Check(checker); return loweringResult; diff --git a/ets2panda/compiler/lowering/ets/opAssignment.cpp b/ets2panda/compiler/lowering/ets/opAssignment.cpp index d323d0006f..75b975ef74 100644 --- a/ets2panda/compiler/lowering/ets/opAssignment.cpp +++ b/ets2panda/compiler/lowering/ets/opAssignment.cpp @@ -295,7 +295,7 @@ ir::AstNode *HandleOpAssignment(public_lib::Context *ctx, ir::AssignmentExpressi InitScopesPhaseETS::RunExternalNode(loweringResult, ctx->parserProgram->VarBinder()); checker->VarBinder()->AsETSBinder()->ResolveReferencesForScopeWithContext(loweringResult, scope); - checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(assignment)}; + checker::EmptyContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(assignment)}; checker::ScopeContext sc {checker, scope}; loweringResult->Check(checker); @@ -403,7 +403,7 @@ static ir::AstNode *HandleUpdate(public_lib::Context *ctx, ir::UpdateExpression auto *checker = ctx->GetChecker()->AsETSChecker(); auto expressionCtx = varbinder::LexicalScope::Enter(checker->VarBinder(), scope); - checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(upd)}; + checker::EmptyContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(upd)}; checker::ScopeContext sc {checker, scope}; loweringResult->SetParent(upd->Parent()); diff --git a/ets2panda/compiler/lowering/ets/recordLowering.cpp b/ets2panda/compiler/lowering/ets/recordLowering.cpp index 32c30d0353..b85fdc718c 100644 --- a/ets2panda/compiler/lowering/ets/recordLowering.cpp +++ b/ets2panda/compiler/lowering/ets/recordLowering.cpp @@ -215,7 +215,7 @@ ir::Expression *RecordLowering::UpdateObjectExpression(ir::ObjectExpression *exp CheckLiteralsCompleteness(keySet, expr, ctx); auto *const scope = NearestScope(expr); - checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; + checker::EmptyContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; auto expressionCtx = varbinder::LexicalScope::Enter(checker->VarBinder(), scope); // Create Block Expression diff --git a/ets2panda/compiler/lowering/util.cpp b/ets2panda/compiler/lowering/util.cpp index a175fe9007..bf0c909ee1 100644 --- a/ets2panda/compiler/lowering/util.cpp +++ b/ets2panda/compiler/lowering/util.cpp @@ -235,6 +235,30 @@ void HandleExternalProgram(varbinder::ETSBinder *newVarbinder, parser::Program * } } +struct ReconstructContextForRecheck : private checker::EmptyContext { + ReconstructContextForRecheck(checker::ETSChecker *checker, ir::AstNode *node) : + ReconstructContextForRecheck{checker, util::Helpers::GetContainingClassDefinition(node)} {} + +private: + ReconstructContextForRecheck(checker::ETSChecker *checker, const ir::ClassDefinition *containingClass) : + EmptyContext{checker, StatusFrom(checker, containingClass), TypeFrom(containingClass)} { + } + + static checker::CheckerStatus StatusFrom(checker::ETSChecker *checker, const ir::ClassDefinition *containingClass) + { + ES2PANDA_ASSERT((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) == 0); + // NOTE(gogabr): should determine checker status more finely. + return (containingClass == nullptr) ? checker::CheckerStatus::NO_OPTS : checker::CheckerStatus::IN_CLASS; + } + + static const checker::ETSObjectType *TypeFrom(const ir::ClassDefinition *containingClass) { + return containingClass == nullptr ? nullptr + : containingClass->TsType()->IsGradualType() + ? containingClass->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : containingClass->TsType()->AsETSObjectType(); + } +}; + // Rerun varbinder and checker on the node. void Recheck(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node) @@ -278,14 +302,7 @@ void Recheck(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, checke auto *scope = Rebind(phaseManager, varBinder, node); - // NOTE(gogabr: should determine checker status more finely. - auto *containingClass = ContainingClass(node); - checker::CheckerStatus newStatus = - (containingClass == nullptr) ? checker::CheckerStatus::NO_OPTS : checker::CheckerStatus::IN_CLASS; - 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); + ReconstructContextForRecheck checkerCtx {checker, node}; auto scopeCtx = checker::ScopeContext(checker, scope); node->Check(checker); @@ -344,6 +361,43 @@ void BindLoweredNode(varbinder::ETSBinder *varBinder, ir::AstNode *node) varBinder->ResolveReferencesForScopeWithContext(node, scope); } +struct ReconstructContextForLoweredNodeChecking : private checker::EmptyContext { + ReconstructContextForLoweredNodeChecking(checker::ETSChecker *checker, const ir::AstNode *node) : + ReconstructContextForLoweredNodeChecking{checker, util::Helpers::GetContainingClassDefinition(node)} {} + +private: + ReconstructContextForLoweredNodeChecking(checker::ETSChecker *checker, const ir::ClassDefinition *containingClass) : + EmptyContext{checker, StatusFrom(checker, containingClass), TypeFrom(containingClass)} { + } + + static checker::CheckerStatus StatusFrom(checker::ETSChecker *checker, const ir::ClassDefinition *containingClass) + { + checker::CheckerStatus newStatus {}; + if (containingClass != nullptr) { + if (containingClass->IsAbstract()) { + newStatus |= checker::CheckerStatus::IN_ABSTRACT; + } else { + newStatus |= checker::CheckerStatus::IN_CLASS; + } + } + + // This flag shouldn't be so special. + if ((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) { + newStatus |= checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK; + } + return newStatus; + } + + static const checker::ETSObjectType *TypeFrom(const ir::ClassDefinition *containingClass) { + return containingClass == nullptr ? nullptr + : containingClass->TsType()->IsGradualType() + ? containingClass->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : containingClass->TsType()->AsETSObjectType(); + } + + +}; + // Note: run varbinder and checker on the new node generated in lowering phases (without ClearTypesVariablesAndScopes) void CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node) { @@ -352,26 +406,7 @@ void CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *chec auto *scope = NearestScope(node); varBinder->ResolveReferencesForScopeWithContext(node, scope); - checker::CheckerStatus newStatus = checker::CheckerStatus::NO_OPTS; - auto *containingClass = util::Helpers::GetContainingClassDefinition(node); - - if (containingClass != nullptr) { - if (containingClass->IsAbstract()) { - newStatus = checker::CheckerStatus::IN_ABSTRACT; - } else { - newStatus = checker::CheckerStatus::IN_CLASS; - } - } - - if ((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) { - newStatus |= checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK; - } - - auto classType = containingClass == nullptr ? nullptr - : containingClass->TsType()->IsGradualType() - ? containingClass->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() - : containingClass->TsType()->AsETSObjectType(); - auto checkerCtx = checker::SavedCheckerContext(checker, newStatus, classType); + ReconstructContextForLoweredNodeChecking checkerCtx {checker, node}; auto scopeCtx = checker::ScopeContext(checker, scope); node->Check(checker); diff --git a/ets2panda/ir/expressions/arrayExpression.cpp b/ets2panda/ir/expressions/arrayExpression.cpp index 3e0a753d33..a5af8414c0 100644 --- a/ets2panda/ir/expressions/arrayExpression.cpp +++ b/ets2panda/ir/expressions/arrayExpression.cpp @@ -243,14 +243,14 @@ checker::Type *CheckAssignmentPattern(Expression *it, checker::TSChecker *checke bindingVar->SetTsType(initializerType); elementType = initializerType; } else if (assignmentPattern->Left()->IsArrayPattern()) { - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + checker::EmptyContext savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; auto destructuringContext = checker::ArrayDestructuringContext( {checker, assignmentPattern->Left()->AsArrayPattern(), false, true, nullptr, assignmentPattern->Right()}); destructuringContext.Start(); elementType = destructuringContext.InferredType(); } else { ES2PANDA_ASSERT(assignmentPattern->Left()->IsObjectPattern()); - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + checker::EmptyContext savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; auto destructuringContext = checker::ObjectDestructuringContext( {checker, assignmentPattern->Left()->AsObjectPattern(), false, true, nullptr, assignmentPattern->Right()}); destructuringContext.Start(); diff --git a/ets2panda/ir/expressions/objectExpression.cpp b/ets2panda/ir/expressions/objectExpression.cpp index 062a325899..d87a7b06b2 100644 --- a/ets2panda/ir/expressions/objectExpression.cpp +++ b/ets2panda/ir/expressions/objectExpression.cpp @@ -363,7 +363,7 @@ bool ObjectExpression::CheckAssignmentPattern(Property *prop, varbinder::Variabl } if (assignmentPattern->Left()->IsArrayPattern()) { - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + checker::EmptyContext savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; auto destructuringContext = checker::ArrayDestructuringContext( {checker, assignmentPattern->Left()->AsArrayPattern(), false, true, nullptr, assignmentPattern->Right()}); @@ -378,7 +378,7 @@ bool ObjectExpression::CheckAssignmentPattern(Property *prop, varbinder::Variabl } ES2PANDA_ASSERT(assignmentPattern->Left()->IsObjectPattern()); - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + checker::EmptyContext savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; auto destructuringContext = checker::ObjectDestructuringContext( {checker, assignmentPattern->Left()->AsObjectPattern(), false, true, nullptr, assignmentPattern->Right()}); diff --git a/ets2panda/ir/statements/forOfStatement.cpp b/ets2panda/ir/statements/forOfStatement.cpp index e6183c6b5c..a223e61714 100644 --- a/ets2panda/ir/statements/forOfStatement.cpp +++ b/ets2panda/ir/statements/forOfStatement.cpp @@ -210,7 +210,7 @@ bool ForOfStatement::CheckReturnTypeOfIteratorMethod(checker::ETSChecker *checke signature->Function()->HasBody() && signature->Function()->Body()->IsBlockStatement()) { for (auto *const it : signature->Function()->Body()->AsBlockStatement()->Statements()) { if (it->IsReturnStatement()) { - checker::SavedCheckerContext savedContext(checker, checker::CheckerStatus::IN_CLASS, sourceType); + checker::EmptyContext savedContext(checker, checker::CheckerStatus::IN_CLASS, sourceType); it->AsReturnStatement()->Check(checker); break; } diff --git a/ets2panda/test/ast/parser/ets/InvalidExpressions.ets b/ets2panda/test/ast/parser/ets/InvalidExpressions.ets index 4a96a1fe0b..20a153dd1d 100644 --- a/ets2panda/test/ast/parser/ets/InvalidExpressions.ets +++ b/ets2panda/test/ast/parser/ets/InvalidExpressions.ets @@ -71,7 +71,6 @@ function f7(a: (b: int = 0) => int): void { /* @@? 18:36 Error SyntaxError: Unexpected token ';'. */ /* @@? 18:36 Error SyntaxError: Unexpected token, expected ')'. */ /* @@? 19:12 Error TypeError: Bad operand type, the types of the operands must be same type. */ -/* @@? 19:25 Error Warning: Type parameter is erased from type 'A' when used in instanceof expression. */ /* @@? 22:23 Error SyntaxError: Not enable default value with default undefined. */ /* @@? 24:10 Error TypeError: Only abstract or native methods can't have body. */ /* @@? 24:22 Error SyntaxError: You didn't set the value. */ diff --git a/ets2panda/test/parser/ets/instanceof-expected.txt b/ets2panda/test/parser/ets/instanceof-expected.txt index 965bcf0e21..dd32d224fd 100644 --- a/ets2panda/test/parser/ets/instanceof-expected.txt +++ b/ets2panda/test/parser/ets/instanceof-expected.txt @@ -1052,4 +1052,3 @@ } } } -Warning: Type parameter is erased from type 'Array' when used in instanceof expression. [instanceof.ets:1:1] diff --git a/ets2panda/util/helpers.cpp b/ets2panda/util/helpers.cpp index 9bde843fb2..034a5c6050 100644 --- a/ets2panda/util/helpers.cpp +++ b/ets2panda/util/helpers.cpp @@ -718,37 +718,11 @@ varbinder::Scope *Helpers::NearestScope(const ir::AstNode *ast) return ast == nullptr ? nullptr : ast->Scope(); } -checker::ETSObjectType const *Helpers::ContainingClass(const ir::AstNode *ast) -{ - while (ast != nullptr && !ast->IsClassDefinition()) { - ast = ast->Parent(); - } - - return ast == nullptr ? nullptr : ast->AsClassDefinition()->TsType()->AsETSObjectType(); -} - bool CheckTypeRelation(checker::ETSChecker *checker, checker::Type *super, checker::Type *sub) { return checker->Relation()->IsSupertypeOf(super, sub); } -void Helpers::CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node) -{ - auto *scope = util::Helpers::NearestScope(node); - varBinder->ResolveReferencesForScopeWithContext(node, scope); - - auto *containingClass = ContainingClass(node); - checker::CheckerStatus newStatus = - (containingClass == nullptr) ? checker::CheckerStatus::NO_OPTS : checker::CheckerStatus::IN_CLASS; - 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); - auto scopeCtx = checker::ScopeContext(checker, scope); - - node->Check(checker); -} - bool Helpers::IsNumericGlobalBuiltIn(checker::Type *type, checker::ETSChecker *checker) { return CheckTypeRelation(checker, type, checker->GetGlobalTypesHolder()->GlobalIntegerBuiltinType()) || diff --git a/ets2panda/util/helpers.h b/ets2panda/util/helpers.h index f8255dc477..138ca04a3c 100644 --- a/ets2panda/util/helpers.h +++ b/ets2panda/util/helpers.h @@ -165,7 +165,6 @@ public: static bool IsGlobalVar(const ark::es2panda::varbinder::Variable *var); static varbinder::Scope *NearestScope(const ir::AstNode *ast); - static checker::ETSObjectType const *ContainingClass(const ir::AstNode *ast); // Note: run varbinder and checker on the new node generated in lowering phases (without // ClearTypesVariablesAndScopes) static void CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node); -- Gitee From 02b33cce8040e614fb1632691059fd211b4f19c1 Mon Sep 17 00:00:00 2001 From: Daniel Kofanov Date: Wed, 9 Jul 2025 13:26:10 +0300 Subject: [PATCH 2/2] tmp2 Change-Id: I94a7179411123ee7b0c3096fc02e47a643e5f5d6 --- ets2panda/checker/ETSAnalyzer.cpp | 47 ++++--- ets2panda/checker/ETSAnalyzerHelpers.cpp | 20 +-- ets2panda/checker/ETSAnalyzerHelpers.h | 1 - ets2panda/checker/ETSchecker.cpp | 2 +- ets2panda/checker/TSAnalyzer.cpp | 12 +- ets2panda/checker/checker.cpp | 16 +++ ets2panda/checker/checker.h | 78 ++++++++--- ets2panda/checker/checkerContext.h | 5 +- ets2panda/checker/ets/arithmetic.cpp | 3 + ets2panda/checker/ets/function_helpers.h | 2 +- ets2panda/checker/ets/object.cpp | 18 +-- ets2panda/checker/ets/typeCheckingHelpers.cpp | 6 +- ets2panda/checker/ets/typeRelationContext.cpp | 4 + ets2panda/checker/ts/function.cpp | 10 +- ets2panda/checker/ts/helpers.cpp | 2 +- .../lowering/ets/asyncMethodLowering.cpp | 2 +- .../compiler/lowering/ets/boxingForLocals.cpp | 6 +- .../lowering/ets/declareOverloadLowering.cpp | 2 +- .../lowering/ets/enumPostCheckLowering.cpp | 4 +- .../ets/extensionAccessorLowering.cpp | 2 +- .../lowering/ets/genericBridgesLowering.cpp | 13 +- .../compiler/lowering/ets/lambdaLowering.cpp | 12 +- .../lowering/ets/objectLiteralLowering.cpp | 2 +- .../compiler/lowering/ets/opAssignment.cpp | 4 +- .../compiler/lowering/ets/recordLowering.cpp | 2 +- ets2panda/compiler/lowering/util.cpp | 132 ++++++++++-------- ets2panda/ir/expressions/arrayExpression.cpp | 4 +- ets2panda/ir/expressions/objectExpression.cpp | 4 +- ets2panda/ir/statements/forOfStatement.cpp | 2 +- 29 files changed, 252 insertions(+), 165 deletions(-) diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 2454b5fe44..9449f0d178 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -118,7 +118,8 @@ checker::Type *ETSAnalyzer::Check(ir::ClassProperty *st) const st->TypeAnnotation()->Check(checker); } - NarrowedContext savedContext {checker, st->IsStatic() ? CheckerStatus::IN_STATIC_CONTEXT : CheckerStatus::NO_OPTS}; + Guard savedContext {checker, + st->IsStatic() ? CheckerStatus::IN_STATIC_CONTEXT : CheckerStatus::NO_OPTS}; checker::Type *propertyType = checker->CheckVariableDeclaration(st->Id(), st->TypeAnnotation(), st->Value(), st->Modifiers()); @@ -151,7 +152,7 @@ checker::Type *ETSAnalyzer::Check(ir::ClassStaticBlock *st) const st->SetTsType(checker->BuildMethodType(func)); } checker::ScopeContext scopeCtx(checker, func->Scope()); - NarrowedContext savedContext {checker, CheckerStatus::IN_STATIC_BLOCK | CheckerStatus::IN_STATIC_CONTEXT}; + Guard savedContext {checker, CheckerStatus::IN_STATIC_BLOCK | CheckerStatus::IN_STATIC_CONTEXT}; func->Body()->Check(checker); return st->TsType(); } @@ -197,8 +198,8 @@ static checker::Type *CheckMethodDefinitionHelper(ETSChecker *checker, ir::Metho return node->TsType(); } -struct EnterMethodContext : private NarrowedContext { - EnterMethodContext(ETSChecker *checker, const ir::ScriptFunction *scriptFunc) +struct MethodContext : private NarrowedContext { + MethodContext(ETSChecker *checker, const ir::ScriptFunction *scriptFunc) : NarrowedContext {checker, FlagsFrom(scriptFunc)} { } @@ -225,6 +226,16 @@ private: } }; +static bool CheckReturnTypeNecessity(ir::MethodDefinition *node) +{ + bool needReturnType = true; + auto *scriptFunc = node->Function(); + needReturnType &= (node->IsNative() || node->IsDeclare()); + needReturnType &= !node->IsConstructor(); + needReturnType &= !scriptFunc->IsSetter(); + return needReturnType; +} + checker::Type *ETSAnalyzer::Check(ir::MethodDefinition *node) const { ETSChecker *checker = GetETSChecker(); @@ -248,6 +259,8 @@ checker::Type *ETSAnalyzer::Check(ir::MethodDefinition *node) const return ReturnTypeForStatement(node); } + MethodContext(checker, scriptFunc); + // NOTE: aszilagyi. make it correctly check for open function not have body if (!scriptFunc->HasBody() && !(node->IsAbstract() || node->IsNative() || node->IsDeclare() || checker->HasStatus(checker::CheckerStatus::IN_INTERFACE))) { @@ -264,8 +277,6 @@ checker::Type *ETSAnalyzer::Check(ir::MethodDefinition *node) const node->SetTsType(checker->BuildMethodSignature(node)); } - EnterMethodContext(checker, scriptFunc); - this->CheckMethodModifiers(node); HandleNativeAndAsyncMethods(checker, node); DoBodyTypeChecking(checker, node, scriptFunc); @@ -1025,8 +1036,11 @@ void TryInferPreferredType(ir::ArrowFunctionExpression *expr, checker::Type *pre } } -struct EnterLambdaContext : private NarrowedContext { - EnterLambdaContext(ETSChecker *checker, ir::ArrowFunctionExpression *expr) +class LambdaContext : private NarrowedContext { + template + friend class Guard; + + LambdaContext(ETSChecker *checker, ir::ArrowFunctionExpression *expr) : NarrowedContext {checker, CheckerStatus::IN_LAMBDA, CaptureContainingClass(checker, expr)} { if (!expr->Parent()->IsCallExpression() || expr->Function()->IsAsyncFunc()) { @@ -1035,7 +1049,6 @@ struct EnterLambdaContext : private NarrowedContext { checker->Context().SetContainingLambda(expr); } -private: static const ETSObjectType *CaptureContainingClass(ETSChecker *checker, const ir::ArrowFunctionExpression *expr) { if (checker->HasStatus(checker::CheckerStatus::IN_EXTENSION_METHOD) && !expr->Function()->HasReceiver()) { @@ -1070,7 +1083,7 @@ checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const return expr->TsType(); } checker::ScopeContext scopeCtx(checker, expr->Function()->Scope()); - EnterLambdaContext savedContext(checker, expr); + Guard savedContext(checker, expr); auto preferredType = expr->GetPreferredType(); if (preferredType != nullptr) { @@ -1589,13 +1602,15 @@ static void CheckCallee(ETSChecker *checker, ir::CallExpression *expr) } // Restore CheckerContext of the owner class if we want to perform checking -struct ReconstructOwnerClassContext : private EmptyContext { - ReconstructOwnerClassContext(ETSChecker *checker, const ETSObjectType *owner) +class OwnerClassContext : private EmptyContext { + template + friend class Guard; + + OwnerClassContext(ETSChecker *checker, const ETSObjectType *owner) : EmptyContext {checker, StatusFrom(owner), owner} { } -private: static CheckerStatus StatusFrom(const ETSObjectType *owner) { if (owner == nullptr) { @@ -1636,7 +1651,7 @@ checker::Type *ETSAnalyzer::GetCallExpressionReturnType(ir::CallExpression *expr return checker->GlobalTypeError(); } - ReconstructOwnerClassContext savedCtx {checker, util::Helpers::GetContainingObjectType(signature->Function())}; + Guard savedCtx {checker, util::Helpers::GetContainingObjectType(signature->Function())}; ir::AstNode *methodDef = signature->Function(); while (!methodDef->IsMethodDefinition()) { @@ -3780,7 +3795,7 @@ checker::Type *ETSAnalyzer::Check(ir::TSInterfaceDeclaration *st) const st->SetTsType(stmtType); checker::ScopeContext scopeCtx(checker, st->Scope()); - EmptyContext savedContext(checker, CheckerStatus::IN_INTERFACE, interfaceType); + Guard savedContext(checker, CheckerStatus::IN_INTERFACE, interfaceType); for (auto *it : st->Body()->Body()) { it->Check(checker); @@ -3904,7 +3919,7 @@ checker::Type *ETSAnalyzer::Check(ir::TSQualifiedName *expr) const checker::Type *ETSAnalyzer::Check(ir::TSTypeAliasDeclaration *st) const { ETSChecker *checker = GetETSChecker(); - EmptyContext savedContext {checker, {}, checker->Context().ContainingClass()}; + Guard savedContext {checker}; checker->CheckAnnotations(st->Annotations()); diff --git a/ets2panda/checker/ETSAnalyzerHelpers.cpp b/ets2panda/checker/ETSAnalyzerHelpers.cpp index 7bfce993b1..dfe7976c00 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.cpp +++ b/ets2panda/checker/ETSAnalyzerHelpers.cpp @@ -186,13 +186,15 @@ static void CheckNativeConstructorBody(ETSChecker *checker, ir::MethodDefinition } } -struct EnterMethodBodyContext : private NarrowedContext { - EnterMethodBodyContext(ETSChecker *checker, ir::MethodDefinition *method) +class MethodBodyContext : private NarrowedContext { + template + friend class Guard; + + MethodBodyContext(ETSChecker *checker, ir::MethodDefinition *method) : NarrowedContext{checker, FlagsFrom(method, checker->Context().ContainingClass())} { checker->Context().SetContainingSignature(checker->GetSignatureFromMethodDefinition(method)); } -private: static CheckerStatus FlagsFrom(ir::MethodDefinition *method, const ETSObjectType *cls) { CheckerStatus flags {}; if (method->IsStatic() && !method->IsConstructor() && !cls->HasObjectFlag(checker::ETSObjectFlags::GLOBAL)) { @@ -218,7 +220,7 @@ void DoBodyTypeChecking(ETSChecker *checker, ir::MethodDefinition *node, ir::Scr } ScopeContext scopeCtx(checker, scriptFunc->Scope()); - EnterMethodBodyContext savedContext(checker, node); + Guard savedContext(checker, node); if (node->IsExtensionMethod()) { CheckExtensionMethod(checker, scriptFunc, node); @@ -774,16 +776,6 @@ checker::Type *ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction * return funcReturnType; } -bool CheckReturnTypeNecessity(ir::MethodDefinition *node) -{ - bool needReturnType = true; - auto *scriptFunc = node->Function(); - needReturnType &= (node->IsNative() || node->IsDeclare()); - needReturnType &= !node->IsConstructor(); - needReturnType &= !scriptFunc->IsSetter(); - return needReturnType; -} - void CheckAllConstPropertyInitialized(checker::ETSChecker *checker, ir::ETSModule *pkg) { auto globalDecl = std::find_if(pkg->Statements().begin(), pkg->Statements().end(), [](ir::AstNode *node) { diff --git a/ets2panda/checker/ETSAnalyzerHelpers.h b/ets2panda/checker/ETSAnalyzerHelpers.h index 37ac4ce4f2..a8f74b9936 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.h +++ b/ets2panda/checker/ETSAnalyzerHelpers.h @@ -61,7 +61,6 @@ bool IsArrayExpressionValidInitializerForType(ETSChecker *checker, const Type *a void CastPossibleTupleOnRHS(ETSChecker *checker, ir::AssignmentExpression *expr); checker::Type *ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containingFunc, ir::ReturnStatement *st, ir::Expression *stArgument); -bool CheckReturnTypeNecessity(ir::MethodDefinition *node); void CheckAllConstPropertyInitialized(checker::ETSChecker *checker, ir::ETSModule *pkg); diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index 98808e574e..57e76da983 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -380,7 +380,7 @@ void ETSChecker::CheckProgram(parser::Program *program, bool runAnalysis) varbinder::RecordTableContext recordTableCtx(VarBinder()->AsETSBinder(), extProg); VarBinder()->AsETSBinder()->SetProgram(extProg); VarBinder()->AsETSBinder()->ResetTopScope(extProg->GlobalScope()); - NarrowedContext savedContext {this, CheckerStatus::IN_EXTERNAL}; + Guard savedContext {this, CheckerStatus::IN_EXTERNAL}; CheckProgram(extProg, VarBinder()->IsGenStdLib() || extProg->IsGenAbcForExternal()); VarBinder()->AsETSBinder()->SetProgram(savedProgram2); VarBinder()->AsETSBinder()->ResetTopScope(savedProgram2->GlobalScope()); diff --git a/ets2panda/checker/TSAnalyzer.cpp b/ets2panda/checker/TSAnalyzer.cpp index cca60bf63d..f33087a9ba 100644 --- a/ets2panda/checker/TSAnalyzer.cpp +++ b/ets2panda/checker/TSAnalyzer.cpp @@ -379,7 +379,7 @@ checker::Type *TSAnalyzer::Check(ir::AssignmentExpression *expr) const { TSChecker *checker = GetTSChecker(); if (expr->Left()->IsArrayPattern()) { - EmptyContext savedContext(checker, CheckerStatus::FORCE_TUPLE); + Guard savedContext(checker, CheckerStatus::FORCE_TUPLE); auto destructuringContext = checker::ArrayDestructuringContext({checker, expr->Left(), true, true, nullptr, expr->Right()}); destructuringContext.Start(); @@ -387,7 +387,7 @@ checker::Type *TSAnalyzer::Check(ir::AssignmentExpression *expr) const } if (expr->Left()->IsObjectPattern()) { - EmptyContext savedContext(checker, CheckerStatus::FORCE_TUPLE); + Guard savedContext(checker, CheckerStatus::FORCE_TUPLE); auto destructuringContext = checker::ObjectDestructuringContext({checker, expr->Left(), true, true, nullptr, expr->Right()}); destructuringContext.Start(); @@ -1378,7 +1378,7 @@ checker::Type *TSAnalyzer::Check(ir::VariableDeclarator *st) const } if (st->Id()->IsArrayPattern()) { - EmptyContext savedContext(checker, CheckerStatus::FORCE_TUPLE); + Guard savedContext(checker, CheckerStatus::FORCE_TUPLE); checker::ArrayDestructuringContext({checker, st->Id(), false, st->Id()->AsArrayPattern()->TypeAnnotation() == nullptr, st->Id()->AsArrayPattern()->TypeAnnotation(), st->Init()}) @@ -1389,7 +1389,7 @@ checker::Type *TSAnalyzer::Check(ir::VariableDeclarator *st) const } ES2PANDA_ASSERT(st->Id()->IsObjectPattern()); - EmptyContext savedContext(checker, CheckerStatus::FORCE_TUPLE); + Guard savedContext(checker, CheckerStatus::FORCE_TUPLE); checker::ObjectDestructuringContext({checker, st->Id(), false, st->Id()->AsObjectPattern()->TypeAnnotation() == nullptr, st->Id()->AsObjectPattern()->TypeAnnotation(), st->Init()}) @@ -1476,7 +1476,7 @@ checker::Type *TSAnalyzer::Check(ir::TSAsExpression *expr) const { TSChecker *checker = GetTSChecker(); if (expr->IsConst()) { - EmptyContext context(checker, CheckerStatus::IN_CONST_CONTEXT); + Guard context(checker, CheckerStatus::IN_CONST_CONTEXT); checker::Type *exprType = expr->Expr()->Check(checker); if (!IsValidConstAssertionArgument(checker, expr->Expr())) { @@ -1489,7 +1489,7 @@ checker::Type *TSAnalyzer::Check(ir::TSAsExpression *expr) const return exprType; } - EmptyContext context(checker, CheckerStatus::NO_OPTS); + Guard context(checker, CheckerStatus::NO_OPTS); expr->TypeAnnotation()->Check(checker); checker::Type *exprType = checker->GetBaseTypeOfLiteralType(expr->Expr()->Check(checker)); diff --git a/ets2panda/checker/checker.cpp b/ets2panda/checker/checker.cpp index ab4e17a2e0..e6c5ba931e 100644 --- a/ets2panda/checker/checker.cpp +++ b/ets2panda/checker/checker.cpp @@ -20,6 +20,22 @@ #include "checker/types/ts/unionType.h" namespace ark::es2panda::checker { + +template <> +SavedCheckerContext::~SavedCheckerContext() +{ + SmartCastTestMap accumulatedTestSmartCasts(actual_->testSmartCasts_.get_allocator()); + accumulatedTestSmartCasts = std::move(actual_->testSmartCasts_); + *actual_ = std::move(prev_); + actual_->testSmartCasts_ = std::move(accumulatedTestSmartCasts); +} + +template <> +SavedCheckerContext::~SavedCheckerContext() +{ + *actual_ = std::move(prev_); +} + Checker::Checker(ThreadSafeArenaAllocator *allocator, util::DiagnosticEngine &diagnosticEngine, ThreadSafeArenaAllocator *programAllocator) : allocator_(allocator), diff --git a/ets2panda/checker/checker.h b/ets2panda/checker/checker.h index 54dddebc9c..2520cd7475 100644 --- a/ets2panda/checker/checker.h +++ b/ets2panda/checker/checker.h @@ -394,27 +394,32 @@ private: template struct SavedCheckerContext { -protected: - SavedCheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass) - : checker_(checker), prev_(checker->context_) +public: + struct ManagableContextArgs { + static constexpr bool BEING_NARROWED = CTX_BEING_NARROWED; + Checker *checker_; + CheckerStatus flags_; + const ETSObjectType *containingClass_; + }; + +private: + SavedCheckerContext(ManagableContextArgs args) + : actual_(&args.checker_->context_), prev_(args.checker_->context_) { if constexpr (CTX_BEING_NARROWED) { // Check new context is narrower (i.e. has more flags). - ES2PANDA_ASSERT(All(newStatus, checker_->context_.Status())); - ES2PANDA_ASSERT((containingClass != nullptr) || prev_.ContainingClass() == nullptr); - checker_->context_.SetStatus(newStatus); - checker_->context_.SetContainingClass(containingClass); + ES2PANDA_ASSERT(All(args.flags_, actual_->Status())); + ES2PANDA_ASSERT((args.containingClass_ != nullptr) || prev_.ContainingClass() == nullptr); + actual_->SetStatus(args.flags_); + actual_->SetContainingClass(args.containingClass_); } else { // Implicitly copy 'sticky' flags: - newStatus |= static_cast(prev_.Status() & STICKY_FLAGS); - checker_->context_ = CheckerContext(checker, newStatus, containingClass); + args.flags_ |= static_cast(prev_.Status() & STICKY_FLAGS); + *actual_ = CheckerContext(args.checker_, args.flags_, args.containingClass_); } } - ~SavedCheckerContext() - { - checker_->context_ = std::move(prev_); - } + ~SavedCheckerContext(); NO_COPY_SEMANTIC(SavedCheckerContext); NO_MOVE_SEMANTIC(SavedCheckerContext); @@ -423,28 +428,65 @@ private: static constexpr auto STICKY_FLAGS = CheckerStatus::BUILTINS_INITIALIZED; private: - Checker *checker_; + CheckerContext *actual_; CheckerContext prev_; + + template + friend class Guard; }; -struct NarrowedContext : private SavedCheckerContext<> { + +class NarrowedContext : private SavedCheckerContext::ManagableContextArgs { + template + friend class Guard; + NarrowedContext(Checker *checker, CheckerStatus flagsExtension) : NarrowedContext(checker, flagsExtension, checker->Context().ContainingClass()) { } + NarrowedContext(Checker *checker, const ETSObjectType *containingClass) + : NarrowedContext(checker, {}, containingClass) + { + } + NarrowedContext(Checker *checker, CheckerStatus flagsExtension, const ETSObjectType *containingClass) - : SavedCheckerContext(checker, checker->Context().Status() | flagsExtension, containingClass) + : ManagableContextArgs{checker, checker->Context().Status() | flagsExtension, containingClass} { } + + // A list of custom 'NarrowedContext's: + friend class MethodContext; + friend class MethodBodyContext; + friend class LambdaContext; + }; -struct EmptyContext : private SavedCheckerContext { +class EmptyContext : private SavedCheckerContext::ManagableContextArgs { + template + friend class Guard; + EmptyContext(Checker *checker, CheckerStatus flags = CheckerStatus::NO_OPTS, const ETSObjectType *containingClass = nullptr) - : SavedCheckerContext(checker, flags, containingClass) + : ManagableContextArgs{checker, flags, containingClass} { } + + // A list of custom 'EmptyContext's: + friend class ClassBodyContext; + friend class OwnerClassContext; + friend class NodeRecheckContext; + friend class LoweredNodeCheckContext; +}; + +template +class Guard { +public: + template + [[nodiscard]] Guard(Args &&...args) : ctx_{GuardCtor{std::forward(args)...}} {} + +private: + SavedCheckerContext ctx_; }; // VerifiedType is used to check return type from Expresssion not equal nullptr diff --git a/ets2panda/checker/checkerContext.h b/ets2panda/checker/checkerContext.h index e7015182c6..99b5fc2963 100644 --- a/ets2panda/checker/checkerContext.h +++ b/ets2panda/checker/checkerContext.h @@ -64,7 +64,7 @@ enum class CheckerStatus : uint32_t { IGNORE_VISIBILITY = 1U << 14U, IN_EXTENSION_METHOD = 1U << 15U, IN_LOCAL_CLASS = 1U << 16U, - IN_INSTANCEOF_CONTEXT = 1U << 17U, // NOTE(dkofanov): Deprecated, need to be removed. + IN_INSTANCEOF_CONTEXT = 1U << 17U, IN_TEST_EXPRESSION = 1U << 18U, IN_LOOP = 1U << 19U, MEET_RETURN = 1U << 20U, @@ -290,6 +290,9 @@ private: [[nodiscard]] std::optional ResolveSmartCastTypes(); [[nodiscard]] bool CheckTestOrSmartCastCondition(SmartCastTuple const &types); void CheckAssignments(ir::AstNode const *node, ReassignedVariableMap &changedVariables) const noexcept; + + template + friend class SavedCheckerContext; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/arithmetic.cpp b/ets2panda/checker/ets/arithmetic.cpp index bb9aedbd3e..cb3f30ef3e 100644 --- a/ets2panda/checker/ets/arithmetic.cpp +++ b/ets2panda/checker/ets/arithmetic.cpp @@ -1192,6 +1192,9 @@ std::tuple ETSChecker::CheckBinaryOperator(ir::Expression *left, return {rightType, rightType}; } + Guard savedContext { + this, operationType == lexer::TokenType::KEYW_INSTANCEOF ? CheckerStatus::IN_INSTANCEOF_CONTEXT : CheckerStatus::NO_OPTS}; + Context().CheckTestSmartCastCondition(operationType); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) checker::Type *rightType = right->Check(this); diff --git a/ets2panda/checker/ets/function_helpers.h b/ets2panda/checker/ets/function_helpers.h index 203dca52a1..6fd605f79e 100644 --- a/ets2panda/checker/ets/function_helpers.h +++ b/ets2panda/checker/ets/function_helpers.h @@ -75,7 +75,7 @@ static void InferUntilFail(Signature const *const signature, const ArenaVector ctx {checker, checker::CheckerStatus::IN_TYPE_INFER}; // some ets lib files require type infer from arg index 0,1,... , not fit to build graph while (anyChange && substitution->size() < sigParams.size()) { anyChange = false; diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index 3b4f631373..cf4d23a94e 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -477,7 +477,7 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte auto *enclosingClass = Context().ContainingClass(); interfaceType->SetEnclosingType(enclosingClass); - EmptyContext savedContext {this, CheckerStatus::IN_INTERFACE, interfaceType}; + Guard savedContext {this, CheckerStatus::IN_INTERFACE, interfaceType}; ConstraintCheckScope ctScope(this); if (interfaceDecl->TypeParams() != nullptr) { interfaceType->AddTypeFlag(TypeFlag::GENERIC); @@ -544,7 +544,7 @@ Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) newFlags |= CheckerStatus::INNER_CLASS; classType->AddObjectFlag(checker::ETSObjectFlags::INNER); } - EmptyContext savedContext {this, newFlags, classType}; + Guard savedContext {this, newFlags, classType}; if (!classType->HasObjectFlag(ETSObjectFlags::RESOLVED_SUPER)) { GetSuperType(classType); @@ -682,7 +682,7 @@ void ETSChecker::ResolveDeclaredMembersOfObject(const Type *type) const auto *baseType = objectType->GetOriginalBaseType(); auto *baseDeclNode = baseType->GetDeclNode(); const bool isInterface = baseDeclNode->IsTSInterfaceDeclaration(); - EmptyContext savedContext {this, isInterface ? CheckerStatus::IN_INTERFACE : CheckerStatus::IN_CLASS, + Guard savedContext {this, isInterface ? CheckerStatus::IN_INTERFACE : CheckerStatus::IN_CLASS, baseType}; ScopeContext scopeCtx(this, isInterface ? baseDeclNode->AsTSInterfaceDeclaration()->Scope() : baseDeclNode->AsClassDefinition()->Scope()); @@ -692,7 +692,7 @@ void ETSChecker::ResolveDeclaredMembersOfObject(const Type *type) const bool isInterface = declNode->IsTSInterfaceDeclaration(); auto *scope = isInterface ? declNode->AsTSInterfaceDeclaration()->Scope() : declNode->AsClassDefinition()->Scope(); - EmptyContext savedContext(this, isInterface ? CheckerStatus::IN_INTERFACE : CheckerStatus::IN_CLASS, objectType); + Guard savedContext(this, isInterface ? CheckerStatus::IN_INTERFACE : CheckerStatus::IN_CLASS, objectType); ScopeContext scopeCtx(this, scope); ResolveDeclaredDeclsOfObject(this, objectType, scope->AsClassScope()); ResolveDeclaredFieldsOfObject(this, objectType, scope->AsClassScope()); @@ -1209,13 +1209,15 @@ void ETSChecker::AddImplementedSignature(std::vector *implementedSi } } -struct EnterClassBodyContext : private EmptyContext { - EnterClassBodyContext(ETSChecker *checker, ir::ClassDefinition *classDef, ETSObjectType *classType) +class ClassBodyContext : private EmptyContext { + template + friend class Guard; + + ClassBodyContext(ETSChecker *checker, ir::ClassDefinition *classDef, ETSObjectType *classType) : EmptyContext(checker, SetupContextFlags(checker, classDef, classType), classType) { } -private: CheckerStatus SetupContextFlags(ETSChecker *checker, ir::ClassDefinition *classDef, ETSObjectType *classType) { if (checker->Context().ContainingClass() != classType) { @@ -1279,7 +1281,7 @@ void ETSChecker::CheckClassDefinition(ir::ClassDefinition *classDef) classType->SuperType()->GetDeclNode()->Check(this); } - EnterClassBodyContext savedContext {this, classDef, classType}; + Guard savedContext {this, classDef, classType}; checker::ScopeContext scopeCtx(this, classDef->Scope()); ResolveDeclaredMembersOfObject(classType); diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index 4ef305f00a..85fbbd4db8 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -619,7 +619,7 @@ Type *ETSChecker::GetTypeOfVariable(varbinder::Variable *const var) return var->TsType(); } - EmptyContext savedContext(this); + Guard savedContext(this); checker::ScopeContext scopeCtx(this, var->GetScope()); IterateInVariableContext(var); @@ -830,7 +830,7 @@ Type *ETSChecker::GetTypeFromInterfaceReference(varbinder::Variable *var) } // Reconstruct context to check the interface. Should be more precise. - EmptyContext savedCtx {this}; + Guard savedCtx {this}; auto *interfaceType = BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration()); var->SetTsType(interfaceType); @@ -846,7 +846,7 @@ Type *ETSChecker::GetTypeFromClassReference(varbinder::Variable *var) auto classDef = var->Declaration()->Node()->AsClassDefinition(); // Reconstruct context to check the class. Should be more precise. - EmptyContext savedCtx {this}; + Guard savedCtx {this}; auto *classType = BuildBasicClassProperties(classDef); var->SetTsType(classType); diff --git a/ets2panda/checker/ets/typeRelationContext.cpp b/ets2panda/checker/ets/typeRelationContext.cpp index 3ed864aa61..d4a004d8f9 100644 --- a/ets2panda/checker/ets/typeRelationContext.cpp +++ b/ets2panda/checker/ets/typeRelationContext.cpp @@ -45,6 +45,10 @@ bool AssignmentContext::ValidateArrayTypeInitializerByElement(TypeRelation *rela bool InstantiationContext::ValidateTypeArguments(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs, const lexer::SourcePosition &pos) { + if (checker_->HasStatus(CheckerStatus::IN_INSTANCEOF_CONTEXT) && (typeArgs == nullptr)) { + result_ = type; + return true; + } if (!checker_->CheckNumberOfTypeArguments(type, typeArgs, pos)) { result_ = checker_->GlobalTypeError(); return true; diff --git a/ets2panda/checker/ts/function.cpp b/ets2panda/checker/ts/function.cpp index fe1e3bba60..a0c2829446 100644 --- a/ets2panda/checker/ts/function.cpp +++ b/ets2panda/checker/ts/function.cpp @@ -225,7 +225,7 @@ ReturnedVariable TSChecker::CheckFunctionAssignmentPatternParameter(ir::Assignme Type *paramType = nullptr; std::stringstream ss; - EmptyContext savedContext {this, CheckerStatus::FORCE_TUPLE | CheckerStatus::IN_PARAMETER}; + Guard savedContext {this, CheckerStatus::FORCE_TUPLE | CheckerStatus::IN_PARAMETER}; if (param->Left()->IsArrayPattern()) { ir::ArrayExpression *arrayPattern = param->Left()->AsArrayPattern(); @@ -278,7 +278,7 @@ std::tuple TSCheck } case ir::AstNodeType::OBJECT_PATTERN: { ES2PANDA_ASSERT(param->Argument()->IsObjectPattern()); - EmptyContext savedContext {this, CheckerStatus::FORCE_TUPLE}; + Guard savedContext {this, CheckerStatus::FORCE_TUPLE}; auto destructuringContext = ObjectDestructuringContext({this, param->Argument(), false, false, nullptr, nullptr}); destructuringContext.SetInferredType(restType); @@ -287,7 +287,7 @@ std::tuple TSCheck return {nullptr, nullptr, false}; } case ir::AstNodeType::ARRAY_PATTERN: { - EmptyContext savedContext {this, CheckerStatus::FORCE_TUPLE}; + Guard savedContext {this, CheckerStatus::FORCE_TUPLE}; auto destructuringContext = ArrayDestructuringContext({this, param->Argument(), false, false, nullptr, nullptr}); destructuringContext.SetInferredType(restType); @@ -311,7 +311,7 @@ std::tuple TSCheck varbinder::Scope::CreateVar(Allocator(), pn.View(), varbinder::VariableFlags::NONE, param); if (param->TypeAnnotation() != nullptr) { - NarrowedContext savedContext {this, CheckerStatus::FORCE_TUPLE}; + Guard savedContext {this, CheckerStatus::FORCE_TUPLE}; auto destructuringContext = ArrayDestructuringContext({this, param->AsArrayPattern(), false, false, param->TypeAnnotation(), nullptr}); destructuringContext.Start(); @@ -333,7 +333,7 @@ std::tuple TSCheck varbinder::Scope::CreateVar(Allocator(), pn.View(), varbinder::VariableFlags::NONE, param); if (param->TypeAnnotation() != nullptr) { - EmptyContext savedContext {this, CheckerStatus::FORCE_TUPLE}; + Guard savedContext {this, CheckerStatus::FORCE_TUPLE}; auto destructuringContext = ObjectDestructuringContext( {this, param->AsObjectPattern(), false, false, param->TypeAnnotation(), nullptr}); destructuringContext.Start(); diff --git a/ets2panda/checker/ts/helpers.cpp b/ets2panda/checker/ts/helpers.cpp index 141a7717b2..8c0522a997 100644 --- a/ets2panda/checker/ts/helpers.cpp +++ b/ets2panda/checker/ts/helpers.cpp @@ -322,7 +322,7 @@ Type *TSChecker::GetUnaryResultType(Type *operandType) void TSChecker::ElaborateElementwise(Type *targetType, ir::Expression *sourceNode, const lexer::SourcePosition &pos) { - EmptyContext savedContext {this, CheckerStatus::FORCE_TUPLE | CheckerStatus::KEEP_LITERAL_TYPE}; + Guard savedContext {this, CheckerStatus::FORCE_TUPLE | CheckerStatus::KEEP_LITERAL_TYPE}; Type *sourceType = CheckTypeCached(sourceNode); diff --git a/ets2panda/compiler/lowering/ets/asyncMethodLowering.cpp b/ets2panda/compiler/lowering/ets/asyncMethodLowering.cpp index 5036caacbc..2d509dee9c 100644 --- a/ets2panda/compiler/lowering/ets/asyncMethodLowering.cpp +++ b/ets2panda/compiler/lowering/ets/asyncMethodLowering.cpp @@ -189,7 +189,7 @@ void HandleMethod(checker::ETSChecker *checker, ir::MethodDefinition *node) void UpdateClassDefintion(checker::ETSChecker *checker, ir::ClassDefinition *classDef) { - checker::NarrowedContext savedContext(checker, {}, classDef->TsType()->AsETSObjectType()); + checker::Guard savedContext(checker, classDef->TsType()->AsETSObjectType()); for (auto *it : classDef->Body()) { if (it->IsMethodDefinition()) { HandleMethod(checker, it->AsMethodDefinition()); diff --git a/ets2panda/compiler/lowering/ets/boxingForLocals.cpp b/ets2panda/compiler/lowering/ets/boxingForLocals.cpp index 0368fc34de..066ccf067e 100644 --- a/ets2panda/compiler/lowering/ets/boxingForLocals.cpp +++ b/ets2panda/compiler/lowering/ets/boxingForLocals.cpp @@ -168,7 +168,7 @@ static void HandleFunctionParam(public_lib::Context *ctx, ir::ETSParameterExpres bodyStmts.insert(bodyStmts.begin(), newDeclaration); auto lexScope = varbinder::LexicalScope::Enter(varBinder, scope); - checker::EmptyContext savedContext {checker}; + checker::Guard savedContext {checker}; auto scopeContext = checker::ScopeContext(checker, scope); newDeclaration->Check(checker); @@ -221,7 +221,7 @@ static ir::AstNode *HandleVariableDeclarator(public_lib::Context *ctx, ir::Varia scope->InsertBinding(newVar->Name(), newVar); auto lexScope = varbinder::LexicalScope::Enter(varBinder, scope); - checker::EmptyContext savedContext {checker}; + checker::Guard savedContext {checker}; auto scopeContext = checker::ScopeContext(checker, scope); newDeclarator->Check(checker); @@ -303,7 +303,7 @@ static ir::AstNode *HandleAssignment(public_lib::Context *ctx, ir::AssignmentExp // NOTE(gogabr) -- The `get` and `set` properties remain without variable; this is OK for the current checker, but // may need adjustment later. auto lexScope = varbinder::LexicalScope::Enter(varBinder, scope); - checker::EmptyContext savedContext {checker}; + checker::Guard savedContext {checker}; auto scopeContext = checker::ScopeContext(checker, scope); varBinder->ResolveReferencesForScopeWithContext(res, scope); diff --git a/ets2panda/compiler/lowering/ets/declareOverloadLowering.cpp b/ets2panda/compiler/lowering/ets/declareOverloadLowering.cpp index 08f3b2456f..591753fc81 100644 --- a/ets2panda/compiler/lowering/ets/declareOverloadLowering.cpp +++ b/ets2panda/compiler/lowering/ets/declareOverloadLowering.cpp @@ -87,7 +87,7 @@ void BuildOverloadHelperFunction(public_lib::Context *ctx, ir::MethodDefinition helperOverload->Function()->ClearFlag((ir::ScriptFunctionFlags::OVERLOAD)); helperOverload->SetParent(method); - checker::NarrowedContext scc(checker, {}, method->Function()->Signature()->Owner()); + checker::Guard scc(checker, method->Function()->Signature()->Owner()); Rebind(ctx->phaseManager, varBinder, helperOverload); helperOverload->Function()->AddFlag((ir::ScriptFunctionFlags::OVERLOAD)); auto funcScope = helperOverload->Function()->Scope(); diff --git a/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp b/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp index b2e830942d..2187b204d3 100644 --- a/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp +++ b/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp @@ -158,7 +158,7 @@ static ir::CallExpression *CreateCallInstanceEnumExpression(public_lib::Context auto lexScope = varbinder::LexicalScope::Enter(varBinder, nearestScope); varBinder->ResolveReferencesForScopeWithContext(callExpr, nearestScope); - checker::EmptyContext checkerCtx(checker, checker::CheckerStatus::IN_CLASS, + checker::Guard checkerCtx(checker, checker::CheckerStatus::IN_CLASS, calleeClass->Definition()->TsType()->AsETSObjectType()); auto scopeCtx = checker::ScopeContext(checker, nearestScope); @@ -401,7 +401,7 @@ bool EnumPostCheckLoweringPhase::PerformForModule(public_lib::Context *ctx, pars node->AsExpression()->SetTsType(nullptr); // force recheck } auto *parentClass = util::Helpers::FindAncestorGivenByType(node, ir::AstNodeType::CLASS_DEFINITION); - checker::NarrowedContext savedContext(checker_, {}, parentClass->AsClassDefinition()->TsType()->AsETSObjectType()); + checker::Guard savedContext(checker_, parentClass->AsClassDefinition()->TsType()->AsETSObjectType()); node->RemoveAstNodeFlags(ir::AstNodeFlags::RECHECK); node->Check(checker_); if (node->IsExpression() && node->AsExpression()->TsType() != nullptr && diff --git a/ets2panda/compiler/lowering/ets/extensionAccessorLowering.cpp b/ets2panda/compiler/lowering/ets/extensionAccessorLowering.cpp index 811c0afe0d..c11dd4a138 100644 --- a/ets2panda/compiler/lowering/ets/extensionAccessorLowering.cpp +++ b/ets2panda/compiler/lowering/ets/extensionAccessorLowering.cpp @@ -61,7 +61,7 @@ static void SwitchType(ir::MemberExpression *expr) static void TryHandleExtensionAccessor(checker::ETSChecker *checker, ir::MemberExpression *expr) { - checker::NarrowedContext ccStatusHelper(checker, checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK); + checker::Guard ccStatusHelper(checker, checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK); SwitchType(expr); auto oldParent = expr->Parent(); if (IsAssignExprExtensionSetter(oldParent) && expr == oldParent->AsAssignmentExpression()->Left()) { diff --git a/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp b/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp index 1cdc06e680..70fcf2dcc3 100644 --- a/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp +++ b/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp @@ -101,10 +101,11 @@ void GenericBridgesPhase::AddGenericBridge(ir::ClassDefinition const *const clas varBinder->AsETSBinder()->ResolveReferencesForScopeWithContext(bridgeMethod, scope); auto *checker = context_->GetChecker()->AsETSChecker(); - checker::EmptyContext checkerCtx = {checker, - checker::CheckerStatus::IN_CLASS | checker::CheckerStatus::IGNORE_VISIBILITY | - checker::CheckerStatus::IN_BRIDGE_TEST, - classDefinition->TsType()->AsETSObjectType()}; + checker::Guard checkerCtx {checker, + checker::CheckerStatus::IN_CLASS | + checker::CheckerStatus::IGNORE_VISIBILITY | + checker::CheckerStatus::IN_BRIDGE_TEST, + classDefinition->TsType()->AsETSObjectType()}; auto scopeCtx = checker::ScopeContext(checker, scope); // Note: we need to create and set function/method type here because the general method `BuildMethodSignature(...)` @@ -136,8 +137,8 @@ void GenericBridgesPhase::ProcessScriptFunction(ir::ClassDefinition const *const auto const overrides = [checker, relation, classDefinition](checker::Signature const *source, checker::Signature const *target) -> bool { - checker::NarrowedContext const checkerCtx(checker, checker::CheckerStatus::IN_BRIDGE_TEST, - classDefinition->TsType()->AsETSObjectType()); + checker::Guard const checkerCtx(checker, checker::CheckerStatus::IN_BRIDGE_TEST, + classDefinition->TsType()->AsETSObjectType()); checker::SavedTypeRelationFlagsContext const savedFlags(relation, checker::TypeRelationFlag::BRIDGE_CHECK); return relation->SignatureIsSupertypeOf(const_cast(source), const_cast(target)); diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index 4a3a44012b..eaedefd753 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -309,8 +309,8 @@ static ir::MethodDefinition *SetUpCalleeMethod(public_lib::Context *ctx, LambdaI varbinder::BoundContext bctx {varBinder->GetRecordTable(), calleeClass->Definition(), true}; varBinder->ResolveReferencesForScopeWithContext(func, funcScope); - checker::EmptyContext checkerCtx {ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, - calleeClass->Definition()->TsType()->AsETSObjectType()}; + checker::Guard checkerCtx {ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, + calleeClass->Definition()->TsType()->AsETSObjectType()}; method->Check(ctx->GetChecker()->AsETSChecker()); return method; @@ -958,8 +958,8 @@ static ir::ETSNewClassInstanceExpression *CreateConstructorCall(public_lib::Cont checker::Type *objectType = info->calleeClass != nullptr ? info->calleeClass->Definition()->TsType() : info->calleeInterface->TsType(); - checker::EmptyContext checkerCtx {ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, - objectType->AsETSObjectType()}; + checker::Guard checkerCtx {ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, + objectType->AsETSObjectType()}; auto scopeCtx = checker::ScopeContext(ctx->GetChecker(), nearestScope); newExpr->Check(checker); @@ -1060,8 +1060,8 @@ static ir::ArrowFunctionExpression *CreateWrappingLambda(public_lib::Context *ct auto [enclosingClass, _] = FindEnclosingClassAndFunction(parent); - checker::EmptyContext checkerCtx {ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, - enclosingClass->Definition()->TsType()->AsETSObjectType()}; + checker::Guard checkerCtx {ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, + enclosingClass->Definition()->TsType()->AsETSObjectType()}; auto scopeCtx = checker::ScopeContext(ctx->GetChecker(), nearestScope); lambda->Check(ctx->GetChecker()->AsETSChecker()); diff --git a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp index 2be2ef2bcd..6152acff75 100644 --- a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp @@ -287,7 +287,7 @@ static ir::AstNode *HandleObjectLiteralLowering(public_lib::Context *ctx, ir::Ob AllowRequiredTypeInstantiation(loweringResult); - checker::EmptyContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; + checker::Guard scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; loweringResult->Check(checker); return loweringResult; diff --git a/ets2panda/compiler/lowering/ets/opAssignment.cpp b/ets2panda/compiler/lowering/ets/opAssignment.cpp index 75b975ef74..60e2ed695c 100644 --- a/ets2panda/compiler/lowering/ets/opAssignment.cpp +++ b/ets2panda/compiler/lowering/ets/opAssignment.cpp @@ -295,7 +295,7 @@ ir::AstNode *HandleOpAssignment(public_lib::Context *ctx, ir::AssignmentExpressi InitScopesPhaseETS::RunExternalNode(loweringResult, ctx->parserProgram->VarBinder()); checker->VarBinder()->AsETSBinder()->ResolveReferencesForScopeWithContext(loweringResult, scope); - checker::EmptyContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(assignment)}; + checker::Guard scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(assignment)}; checker::ScopeContext sc {checker, scope}; loweringResult->Check(checker); @@ -403,7 +403,7 @@ static ir::AstNode *HandleUpdate(public_lib::Context *ctx, ir::UpdateExpression auto *checker = ctx->GetChecker()->AsETSChecker(); auto expressionCtx = varbinder::LexicalScope::Enter(checker->VarBinder(), scope); - checker::EmptyContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(upd)}; + checker::Guard scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(upd)}; checker::ScopeContext sc {checker, scope}; loweringResult->SetParent(upd->Parent()); diff --git a/ets2panda/compiler/lowering/ets/recordLowering.cpp b/ets2panda/compiler/lowering/ets/recordLowering.cpp index b85fdc718c..30e72bcf64 100644 --- a/ets2panda/compiler/lowering/ets/recordLowering.cpp +++ b/ets2panda/compiler/lowering/ets/recordLowering.cpp @@ -215,7 +215,7 @@ ir::Expression *RecordLowering::UpdateObjectExpression(ir::ObjectExpression *exp CheckLiteralsCompleteness(keySet, expr, ctx); auto *const scope = NearestScope(expr); - checker::EmptyContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; + checker::Guard scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; auto expressionCtx = varbinder::LexicalScope::Enter(checker->VarBinder(), scope); // Create Block Expression diff --git a/ets2panda/compiler/lowering/util.cpp b/ets2panda/compiler/lowering/util.cpp index bf0c909ee1..b7c33b14be 100644 --- a/ets2panda/compiler/lowering/util.cpp +++ b/ets2panda/compiler/lowering/util.cpp @@ -21,6 +21,75 @@ #include "checker/ETSAnalyzer.h" #include "checker/types/gradualType.h" + +namespace ark::es2panda::checker { +class NodeRecheckContext : private EmptyContext { + template + friend class Guard; + + NodeRecheckContext(ETSChecker *checker, ir::AstNode *node) : + NodeRecheckContext{checker, util::Helpers::GetContainingClassDefinition(node)} {} + + NodeRecheckContext(ETSChecker *checker, const ir::ClassDefinition *containingClass) : + EmptyContext{checker, StatusFrom(checker, containingClass), TypeFrom(containingClass)} { + } + + static CheckerStatus StatusFrom(ETSChecker *checker, const ir::ClassDefinition *containingClass) + { + ES2PANDA_ASSERT((checker->Context().Status() & CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) == 0); + // NOTE(gogabr): should determine checker status more finely. + return (containingClass == nullptr) ? CheckerStatus::NO_OPTS : CheckerStatus::IN_CLASS; + } + + static const ETSObjectType *TypeFrom(const ir::ClassDefinition *containingClass) { + return containingClass == nullptr ? nullptr + : containingClass->TsType()->IsGradualType() + ? containingClass->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : containingClass->TsType()->AsETSObjectType(); + } +}; + +class LoweredNodeCheckContext : private EmptyContext { + template + friend class Guard; + + LoweredNodeCheckContext(ETSChecker *checker, const ir::AstNode *node) : + LoweredNodeCheckContext{checker, util::Helpers::GetContainingClassDefinition(node)} {} + + LoweredNodeCheckContext(ETSChecker *checker, const ir::ClassDefinition *containingClass) : + EmptyContext{checker, StatusFrom(checker, containingClass), TypeFrom(containingClass)} { + } + + static CheckerStatus StatusFrom(ETSChecker *checker, const ir::ClassDefinition *containingClass) + { + CheckerStatus newStatus {}; + if (containingClass != nullptr) { + if (containingClass->IsAbstract()) { + newStatus |= CheckerStatus::IN_ABSTRACT; + } else { + newStatus |= CheckerStatus::IN_CLASS; + } + } + + // This flag shouldn't be so special. + if ((checker->Context().Status() & CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) { + newStatus |= CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK; + } + return newStatus; + } + + static const ETSObjectType *TypeFrom(const ir::ClassDefinition *containingClass) { + return containingClass == nullptr ? nullptr + : containingClass->TsType()->IsGradualType() + ? containingClass->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : containingClass->TsType()->AsETSObjectType(); + } + + +}; + +} // namespace ark::es2panda::checker + namespace ark::es2panda::compiler { bool HasGlobalClassParent(const ir::AstNode *node) @@ -235,29 +304,7 @@ void HandleExternalProgram(varbinder::ETSBinder *newVarbinder, parser::Program * } } -struct ReconstructContextForRecheck : private checker::EmptyContext { - ReconstructContextForRecheck(checker::ETSChecker *checker, ir::AstNode *node) : - ReconstructContextForRecheck{checker, util::Helpers::GetContainingClassDefinition(node)} {} -private: - ReconstructContextForRecheck(checker::ETSChecker *checker, const ir::ClassDefinition *containingClass) : - EmptyContext{checker, StatusFrom(checker, containingClass), TypeFrom(containingClass)} { - } - - static checker::CheckerStatus StatusFrom(checker::ETSChecker *checker, const ir::ClassDefinition *containingClass) - { - ES2PANDA_ASSERT((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) == 0); - // NOTE(gogabr): should determine checker status more finely. - return (containingClass == nullptr) ? checker::CheckerStatus::NO_OPTS : checker::CheckerStatus::IN_CLASS; - } - - static const checker::ETSObjectType *TypeFrom(const ir::ClassDefinition *containingClass) { - return containingClass == nullptr ? nullptr - : containingClass->TsType()->IsGradualType() - ? containingClass->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() - : containingClass->TsType()->AsETSObjectType(); - } -}; // Rerun varbinder and checker on the node. void Recheck(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, @@ -302,7 +349,7 @@ void Recheck(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, checke auto *scope = Rebind(phaseManager, varBinder, node); - ReconstructContextForRecheck checkerCtx {checker, node}; + checker::Guard checkerCtx {checker, node}; auto scopeCtx = checker::ScopeContext(checker, scope); node->Check(checker); @@ -361,43 +408,6 @@ void BindLoweredNode(varbinder::ETSBinder *varBinder, ir::AstNode *node) varBinder->ResolveReferencesForScopeWithContext(node, scope); } -struct ReconstructContextForLoweredNodeChecking : private checker::EmptyContext { - ReconstructContextForLoweredNodeChecking(checker::ETSChecker *checker, const ir::AstNode *node) : - ReconstructContextForLoweredNodeChecking{checker, util::Helpers::GetContainingClassDefinition(node)} {} - -private: - ReconstructContextForLoweredNodeChecking(checker::ETSChecker *checker, const ir::ClassDefinition *containingClass) : - EmptyContext{checker, StatusFrom(checker, containingClass), TypeFrom(containingClass)} { - } - - static checker::CheckerStatus StatusFrom(checker::ETSChecker *checker, const ir::ClassDefinition *containingClass) - { - checker::CheckerStatus newStatus {}; - if (containingClass != nullptr) { - if (containingClass->IsAbstract()) { - newStatus |= checker::CheckerStatus::IN_ABSTRACT; - } else { - newStatus |= checker::CheckerStatus::IN_CLASS; - } - } - - // This flag shouldn't be so special. - if ((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) { - newStatus |= checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK; - } - return newStatus; - } - - static const checker::ETSObjectType *TypeFrom(const ir::ClassDefinition *containingClass) { - return containingClass == nullptr ? nullptr - : containingClass->TsType()->IsGradualType() - ? containingClass->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() - : containingClass->TsType()->AsETSObjectType(); - } - - -}; - // Note: run varbinder and checker on the new node generated in lowering phases (without ClearTypesVariablesAndScopes) void CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node) { @@ -406,7 +416,7 @@ void CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *chec auto *scope = NearestScope(node); varBinder->ResolveReferencesForScopeWithContext(node, scope); - ReconstructContextForLoweredNodeChecking checkerCtx {checker, node}; + checker::Guard checkerCtx {checker, node}; auto scopeCtx = checker::ScopeContext(checker, scope); node->Check(checker); diff --git a/ets2panda/ir/expressions/arrayExpression.cpp b/ets2panda/ir/expressions/arrayExpression.cpp index a5af8414c0..c06102b595 100644 --- a/ets2panda/ir/expressions/arrayExpression.cpp +++ b/ets2panda/ir/expressions/arrayExpression.cpp @@ -243,14 +243,14 @@ checker::Type *CheckAssignmentPattern(Expression *it, checker::TSChecker *checke bindingVar->SetTsType(initializerType); elementType = initializerType; } else if (assignmentPattern->Left()->IsArrayPattern()) { - checker::EmptyContext savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; + checker::Guard savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; auto destructuringContext = checker::ArrayDestructuringContext( {checker, assignmentPattern->Left()->AsArrayPattern(), false, true, nullptr, assignmentPattern->Right()}); destructuringContext.Start(); elementType = destructuringContext.InferredType(); } else { ES2PANDA_ASSERT(assignmentPattern->Left()->IsObjectPattern()); - checker::EmptyContext savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; + checker::Guard savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; auto destructuringContext = checker::ObjectDestructuringContext( {checker, assignmentPattern->Left()->AsObjectPattern(), false, true, nullptr, assignmentPattern->Right()}); destructuringContext.Start(); diff --git a/ets2panda/ir/expressions/objectExpression.cpp b/ets2panda/ir/expressions/objectExpression.cpp index d87a7b06b2..c8bf9d6d87 100644 --- a/ets2panda/ir/expressions/objectExpression.cpp +++ b/ets2panda/ir/expressions/objectExpression.cpp @@ -363,7 +363,7 @@ bool ObjectExpression::CheckAssignmentPattern(Property *prop, varbinder::Variabl } if (assignmentPattern->Left()->IsArrayPattern()) { - checker::EmptyContext savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; + checker::Guard savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; auto destructuringContext = checker::ArrayDestructuringContext( {checker, assignmentPattern->Left()->AsArrayPattern(), false, true, nullptr, assignmentPattern->Right()}); @@ -378,7 +378,7 @@ bool ObjectExpression::CheckAssignmentPattern(Property *prop, varbinder::Variabl } ES2PANDA_ASSERT(assignmentPattern->Left()->IsObjectPattern()); - checker::EmptyContext savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; + checker::Guard savedContext {checker, checker::CheckerStatus::FORCE_TUPLE}; auto destructuringContext = checker::ObjectDestructuringContext( {checker, assignmentPattern->Left()->AsObjectPattern(), false, true, nullptr, assignmentPattern->Right()}); diff --git a/ets2panda/ir/statements/forOfStatement.cpp b/ets2panda/ir/statements/forOfStatement.cpp index a223e61714..fb364f9d60 100644 --- a/ets2panda/ir/statements/forOfStatement.cpp +++ b/ets2panda/ir/statements/forOfStatement.cpp @@ -210,7 +210,7 @@ bool ForOfStatement::CheckReturnTypeOfIteratorMethod(checker::ETSChecker *checke signature->Function()->HasBody() && signature->Function()->Body()->IsBlockStatement()) { for (auto *const it : signature->Function()->Body()->AsBlockStatement()->Statements()) { if (it->IsReturnStatement()) { - checker::EmptyContext savedContext(checker, checker::CheckerStatus::IN_CLASS, sourceType); + checker::Guard savedContext(checker, checker::CheckerStatus::IN_CLASS, sourceType); it->AsReturnStatement()->Check(checker); break; } -- Gitee