diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index c4d2946155463dcfde7a2ba8e4219d59b333469d..9449f0d178a21af65966af4b4f9f714e492d451d 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,8 @@ 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); - } + 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()); @@ -156,9 +152,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); + Guard savedContext {checker, CheckerStatus::IN_STATIC_BLOCK | CheckerStatus::IN_STATIC_CONTEXT}; func->Body()->Check(checker); return st->TsType(); } @@ -204,10 +198,42 @@ static checker::Type *CheckMethodDefinitionHelper(ETSChecker *checker, ir::Metho return node->TsType(); } -static bool IsInitializerBlockTransfer(std::string_view str) +struct MethodContext : private NarrowedContext { + MethodContext(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; + } +}; + +static bool CheckReturnTypeNecessity(ir::MethodDefinition *node) { - auto prefix = compiler::Signatures::INITIALIZER_BLOCK_INIT; - return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0; + 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 @@ -233,11 +259,7 @@ 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); - } + MethodContext(checker, scriptFunc); // NOTE: aszilagyi. make it correctly check for open function not have body if (!scriptFunc->HasBody() && !(node->IsAbstract() || node->IsNative() || node->IsDeclare() || @@ -255,10 +277,6 @@ 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); - } - this->CheckMethodModifiers(node); HandleNativeAndAsyncMethods(checker, node); DoBodyTypeChecking(checker, node, scriptFunc); @@ -1018,6 +1036,45 @@ void TryInferPreferredType(ir::ArrowFunctionExpression *expr, checker::Type *pre } } +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()) { + checker->Context().ClearSmartCasts(); + } + checker->Context().SetContainingLambda(expr); + } + + 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 +1083,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); + Guard savedContext(checker, expr); auto preferredType = expr->GetPreferredType(); if (preferredType != nullptr) { @@ -1574,22 +1602,29 @@ 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); +class OwnerClassContext : private EmptyContext { + template + friend class Guard; + + OwnerClassContext(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); -} + 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 +1651,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)); + Guard savedCtx {checker, util::Helpers::GetContainingObjectType(signature->Function())}; ir::AstNode *methodDef = signature->Function(); while (!methodDef->IsMethodDefinition()) { @@ -3761,7 +3795,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); + Guard savedContext(checker, CheckerStatus::IN_INTERFACE, interfaceType); for (auto *it : st->Body()->Body()) { it->Check(checker); @@ -3885,7 +3919,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()); + Guard savedContext {checker}; checker->CheckAnnotations(st->Annotations()); diff --git a/ets2panda/checker/ETSAnalyzerHelpers.cpp b/ets2panda/checker/ETSAnalyzerHelpers.cpp index 021f906c1a022736ccdeeb8eacc88c1cdd89a774..dfe7976c00fea6c53d5fda5c2569f556ca742534 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.cpp +++ b/ets2panda/checker/ETSAnalyzerHelpers.cpp @@ -186,6 +186,28 @@ static void CheckNativeConstructorBody(ETSChecker *checker, ir::MethodDefinition } } +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)); + } + + 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 +219,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()); + Guard savedContext(checker, node); if (node->IsExtensionMethod()) { CheckExtensionMethod(checker, scriptFunc, node); @@ -234,8 +245,6 @@ void DoBodyTypeChecking(ETSChecker *checker, ir::MethodDefinition *node, ir::Scr } } } - - checker->Context().SetContainingSignature(nullptr); } void ComposeAsyncImplFuncReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc) @@ -767,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 37ac4ce4f2c8fa2188c44b75e7a5ad116a72b27a..a8f74b9936f9d317bbf2d52a49f09a358e214876 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 abf418beae8b15f7a9979144cac3a715ede08fe4..57e76da983174f9ab582313705412581e9a914d0 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); + 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/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 62c5776c6b25bac3bda30ddbb34a35413a27a800..c183797ca5107d40a32fd51c2498425514d8885e 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 cfaad3f534778bcc113cfa6f35b12ba77bdffdfb..f33087a9ba231ab1ff9cdc5acbea427ff860159a 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); + Guard 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); + Guard 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); + Guard 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); + Guard 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); + Guard 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); + 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 ab4e17a2e079d1182cf78b6a0e32ecc6eedbaa1d..e6c5ba931e0ac2024ccef9ba5d0e0021fcf1f1e1 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 04d1c376926c01e27f3afc0cb2bc3e37180a3439..2520cd74751b1b2c510ce43007f829b5b7999fbe 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,41 +392,101 @@ private: parser::Program *prevProgram_; }; -class SavedCheckerContext { +template +struct SavedCheckerContext { public: - explicit SavedCheckerContext(Checker *checker, CheckerStatus newStatus) - : SavedCheckerContext(checker, newStatus, nullptr) + 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(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: + args.flags_ |= static_cast(prev_.Status() & STICKY_FLAGS); + *actual_ = CheckerContext(args.checker_, args.flags_, args.containingClass_); + } + } + + ~SavedCheckerContext(); + + NO_COPY_SEMANTIC(SavedCheckerContext); + NO_MOVE_SEMANTIC(SavedCheckerContext); + +private: + static constexpr auto STICKY_FLAGS = CheckerStatus::BUILTINS_INITIALIZED; + +private: + CheckerContext *actual_; + CheckerContext prev_; + + template + friend class Guard; +}; + + +class NarrowedContext : private SavedCheckerContext::ManagableContextArgs { + template + friend class Guard; + + NarrowedContext(Checker *checker, CheckerStatus flagsExtension) + : NarrowedContext(checker, flagsExtension, checker->Context().ContainingClass()) { } - explicit SavedCheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass) - : SavedCheckerContext(checker, newStatus, containingClass, nullptr) + NarrowedContext(Checker *checker, const ETSObjectType *containingClass) + : NarrowedContext(checker, {}, containingClass) { } - explicit SavedCheckerContext(Checker *checker, CheckerStatus newStatus, const ETSObjectType *containingClass, - Signature *containingSignature) - : checker_(checker), prev_(checker->context_) + NarrowedContext(Checker *checker, CheckerStatus flagsExtension, const ETSObjectType *containingClass) + : ManagableContextArgs{checker, checker->Context().Status() | flagsExtension, containingClass} { - 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; - } } - NO_COPY_SEMANTIC(SavedCheckerContext); - DEFAULT_MOVE_SEMANTIC(SavedCheckerContext); + // A list of custom 'NarrowedContext's: + friend class MethodContext; + friend class MethodBodyContext; + friend class LambdaContext; + +}; - ~SavedCheckerContext() +class EmptyContext : private SavedCheckerContext::ManagableContextArgs { + template + friend class Guard; + + EmptyContext(Checker *checker, CheckerStatus flags = CheckerStatus::NO_OPTS, + const ETSObjectType *containingClass = nullptr) + : ManagableContextArgs{checker, flags, containingClass} { - checker_->context_ = prev_; } + // 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: - Checker *checker_; - CheckerContext prev_; + 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 aa5e6b26fcdbad5213fec9eecbe6e003013339d3..99b5fc29635d9fbe3af3e8427f11105911f06310 100644 --- a/ets2panda/checker/checkerContext.h +++ b/ets2panda/checker/checkerContext.h @@ -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_; @@ -291,29 +290,11 @@ private: [[nodiscard]] std::optional ResolveSmartCastTypes(); [[nodiscard]] bool CheckTestOrSmartCastCondition(SmartCastTuple const &types); 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_; + template + friend class SavedCheckerContext; }; + } // namespace ark::es2panda::checker #endif diff --git a/ets2panda/checker/ets/arithmetic.cpp b/ets2panda/checker/ets/arithmetic.cpp index cbe2c38d4ec82f65b2addbc0f7694aed02f89501..cb3f30ef3ea715084d90cbcc3db6c388b2ebc979 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,9 +1192,8 @@ std::tuple ETSChecker::CheckBinaryOperator(ir::Expression *left, return {rightType, rightType}; } - if (operationType == lexer::TokenType::KEYW_INSTANCEOF) { - AddStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT); - } + Guard savedContext { + this, operationType == lexer::TokenType::KEYW_INSTANCEOF ? CheckerStatus::IN_INSTANCEOF_CONTEXT : CheckerStatus::NO_OPTS}; Context().CheckTestSmartCastCondition(operationType); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) diff --git a/ets2panda/checker/ets/function_helpers.h b/ets2panda/checker/ets/function_helpers.h index 7c22e2f83b690b8c753b021b516bb43c0a0e93d7..6fd605f79e9bb907bb089b79e23a3eb31f458e3c 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); + Guard 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 7cfe16d58eb609267a1f9c2a2877defcbd7daa76..cf4d23a94e00337b1d4ae7653cf994f663620214 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); + Guard 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); + Guard 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(); + Guard 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(); + 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()); ResolveDeclaredMethodsOfObject(this, objectType, scope->AsClassScope()); @@ -1219,23 +1209,58 @@ void ETSChecker::AddImplementedSignature(std::vector *implementedSi } } -void ETSChecker::CheckLocalClass(ir::ClassDefinition *classDef, CheckerStatus &checkerStatus) -{ - if (!classDef->IsLocal()) { - return; +class ClassBodyContext : private EmptyContext { + template + friend class Guard; + + ClassBodyContext(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()); + + 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 +1281,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); - + Guard 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 f2a5fd239c3e1880de04b922ddbd820ecd817e54..85fbbd4db84d39bbf9fb0d59f92a70fd59532ea1 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); + Guard 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. + Guard 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. + Guard 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 80bfff4d4ebc492025a92f10c9e7d15cba978d00..d4a004d8f94ec559dc1313d642250df7e75eef64 100644 --- a/ets2panda/checker/ets/typeRelationContext.cpp +++ b/ets2panda/checker/ets/typeRelationContext.cpp @@ -45,10 +45,7 @@ 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); - } + if (checker_->HasStatus(CheckerStatus::IN_INSTANCEOF_CONTEXT) && (typeArgs == nullptr)) { result_ = type; return true; } diff --git a/ets2panda/checker/ts/function.cpp b/ets2panda/checker/ts/function.cpp index e4419c3f8c3e31d2890a36cf00d8249525e94ec9..a0c282944604fec3f9a98e2f4b8f1a49f7154f17 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); + 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()); - auto savedContext = SavedCheckerContext(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: { - auto savedContext = SavedCheckerContext(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) { - auto savedContext = SavedCheckerContext(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) { - auto savedContext = SavedCheckerContext(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 9c2455f558c2cf064c93663fee5cd0ffa05bf9b4..8c0522a997554b1e699486740a8ce525def5e964 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); + Guard 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 8992e4463266032d54ade146aee52b4d6f28f6a7..42dde06274fcb3af1746afb9d859fd1c476c35de 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 4d403512f6927810a123215069ba07837382fdc8..2d509dee9cbcfd06fca435b813f14373a23596a5 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::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 ce8e088aa65c7a970900983d64b253ff8fca10fa..066ccf067ecf685cf8c9305223342b55843d5683 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::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); - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::NO_OPTS); + 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); - auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::NO_OPTS); + 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 434636afec6c75ad013f04bc2915bf7d502f22cd..591753fc81f772b324795b6804a8d67043470840 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::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 abd8401d58e559c36a883568ff437cd53e8fd8ca..2187b204d32c49165904c168708f48c966a61e41 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::Guard 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::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 0ec34677b128076d59b37fbe0d7286057cef08c1..c11dd4a138cea004b025624893fbcf9df1309b92 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::Guard 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 1e2d1d47ff9d8033fe37cff06d61883a1c7e3e2d..70fcf2dcc3e363c357bc42cb1e6ed90e4e769680 100644 --- a/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp +++ b/ets2panda/compiler/lowering/ets/genericBridgesLowering.cpp @@ -101,11 +101,11 @@ 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::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(...)` @@ -137,9 +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::SavedCheckerContext const checkerCtx( - checker, checker->Context().Status() | 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 17cfcc6a9a491bbd098f469075495366c2a15e30..eaedefd753a3773fa846d92421cb25228a0ab58b 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::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(); - auto checkerCtx = checker::SavedCheckerContext(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); - auto checkerCtx = checker::SavedCheckerContext(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 8e0fbcfb05cb2aaa7afda6abd1486a510a2583bb..6152acff7532c34aed10c60657b8f1ff48ac398a 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::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 d323d0006fda050c51dd0e90e3e59a9ae8cd89be..60e2ed695c8490dddc2978cf7fe81555e2a16c92 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::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::SavedCheckerContext 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 32c30d0353d7a8b87fce58a359ffeb5afa2a3c5d..30e72bcf6454dad7c3f14102776678b2b02d62f0 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::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 a175fe9007f5c8c8a4c747efa0d1fcacfc8cf20d..b7c33b14be05004caa7e5cc956895d6a2d6165cc 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,6 +304,8 @@ void HandleExternalProgram(varbinder::ETSBinder *newVarbinder, parser::Program * } } + + // Rerun varbinder and checker on the node. void Recheck(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node) @@ -278,14 +349,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); + checker::Guard checkerCtx {checker, node}; auto scopeCtx = checker::ScopeContext(checker, scope); node->Check(checker); @@ -352,26 +416,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); + 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 3e0a753d33a91d0f811565bffbf976da5592a430..c06102b59508252a9574ad5bb47e107831e3a7f3 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::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()); - auto savedContext = checker::SavedCheckerContext(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 062a3258994e8c5c2b52c3e42a0aa3792b9170fd..c8bf9d6d872337840c278c58d901c98f87bf9712 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::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()); - auto savedContext = checker::SavedCheckerContext(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 e6183c6b5cfad5be1525fc8b541188ba63279762..fb364f9d60f1756841ddb17f01f3b903d3ad66be 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::Guard 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 4a96a1fe0bef86b137e37cc7ba6366f84e05f169..20a153dd1d237d9f2a989e93c8e1a83946b02236 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 965bcf0e21e7ce96e2fdef916165612044bdb2d4..dd32d224fd0e988d8d04c38411467132f29483d4 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 9bde843fb28a80800f05f813e8606e78d13f4a23..034a5c6050de4dde60d96c9bd0c6a091eeb8ba36 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 f8255dc4771fce090d54d04c4d4a7ce47d50d160..138ca04a3c83132f315698d567869a38fd9cf5ce 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);