diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 4498536bb1f300d01c8b1a2ba89667e544d50c3f..dc696a768f7e650527df03cf336d79322d3393f0 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -862,14 +862,17 @@ static bool CheckElement(ETSChecker *checker, Type *const preferredType, auto *const compareType = tupleType->GetTypeAtIndex(idx); if (compareType == nullptr) { - checker->LogError(diagnostic::TUPLE_SIZE_MISMATCH, {tupleType->GetTupleSize()}, currentElement->Start()); + checker->PossiblyLogError(currentElement, diagnostic::TUPLE_SIZE_MISMATCH, {tupleType->GetTupleSize()}, + currentElement->Start()); + return false; } auto ctx = AssignmentContext(checker->Relation(), currentElement, elementType, compareType, currentElement->Start(), std::nullopt, TypeRelationFlag::NO_THROW); if (!ctx.IsAssignable()) { - checker->LogError(diagnostic::TUPLE_UNASSIGNABLE_ARRAY, {idx}, currentElement->Start()); + checker->PossiblyLogError(currentElement, diagnostic::TUPLE_UNASSIGNABLE_ARRAY, {idx}, + currentElement->Start()); return false; } @@ -886,8 +889,8 @@ static bool CheckElement(ETSChecker *checker, Type *const preferredType, auto ctx = AssignmentContext(checker->Relation(), currentElement, elementType, targetType, currentElement->Start(), {}, TypeRelationFlag::NO_THROW); if (!ctx.IsAssignable()) { - checker->LogError(diagnostic::ARRAY_ELEMENT_INIT_TYPE_INCOMPAT, {idx, elementType, targetType}, - currentElement->Start()); + checker->PossiblyLogError(currentElement, diagnostic::ARRAY_ELEMENT_INIT_TYPE_INCOMPAT, + {idx, elementType, targetType}, currentElement->Start()); return false; } @@ -1067,7 +1070,7 @@ checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const } if (!IsArrayExpressionValidInitializerForType(checker, preferredType)) { - checker->LogError(diagnostic::UNEXPECTED_ARRAY, {expr->PreferredType()}, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::UNEXPECTED_ARRAY, {expr->PreferredType()}, expr->Start()); return checker->InvalidateType(expr); } @@ -1080,7 +1083,8 @@ checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const } if (preferredType == nullptr) { - return checker->TypeError(expr, diagnostic::UNRESOLVABLE_ARRAY, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::UNRESOLVABLE_ARRAY, expr->Start()); + return checker->InvalidateType(expr); } if (!ValidArrayExprSizeForTupleSize(checker, preferredType, expr) || @@ -2167,7 +2171,7 @@ static bool ValidatePreferredType(ETSChecker *checker, ir::ObjectExpression *exp { auto preferredType = expr->PreferredType(); if (preferredType == nullptr) { - checker->LogError(diagnostic::CLASS_COMPOSITE_UNKNOWN_TYPE, {}, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::CLASS_COMPOSITE_UNKNOWN_TYPE, {}, expr->Start()); return false; } @@ -2177,7 +2181,7 @@ static bool ValidatePreferredType(ETSChecker *checker, ir::ObjectExpression *exp } if (!preferredType->IsETSObjectType()) { - checker->LogError(diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {preferredType}, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {preferredType}, expr->Start()); return false; } @@ -2509,11 +2513,13 @@ checker::ETSObjectType *ResolveUnionObjectTypeForObjectLiteral(ETSChecker *check } if (matchingObjectTypes.empty()) { // No candidate ETSObjectType from the union matched all properties - checker->LogError(diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {expr->PreferredType()}, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {expr->PreferredType()}, + expr->Start()); return nullptr; } // Ambiguous - checker->LogError(diagnostic::AMBIGUOUS_REFERENCE, {expr->PreferredType()->ToString()}, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::AMBIGUOUS_REFERENCE, {expr->PreferredType()->ToString()}, + expr->Start()); return nullptr; } @@ -2567,7 +2573,7 @@ checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const } if (expr->PreferredType() == nullptr) { - checker->LogError(diagnostic::CLASS_COMPOSITE_UNKNOWN_TYPE, {}, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::CLASS_COMPOSITE_UNKNOWN_TYPE, {}, expr->Start()); expr->SetTsType(checker->GlobalTypeError()); return expr->TsType(); } @@ -2581,7 +2587,8 @@ checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const if (objType == nullptr) { if (!expr->PreferredType()->IsETSUnionType()) { - checker->LogError(diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {expr->PreferredType()}, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {expr->PreferredType()}, + expr->Start()); } expr->SetTsType(checker->GlobalTypeError()); return expr->TsType(); @@ -2600,8 +2607,8 @@ checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const // If we reach here, objType is a class. It must have a parameterless constructor if (!HasParameterlessConstructor(objType, checker, expr->Start())) { - expr->SetTsType(checker->TypeError(expr, diagnostic::NO_PARAMLESS_CTOR, {objType->Name()}, expr->Start())); - return expr->TsType(); + checker->PossiblyLogError(expr, diagnostic::NO_PARAMLESS_CTOR, {objType->Name()}, expr->Start()); + return checker->InvalidateType(expr); } CheckObjectExprProps(expr, objType, @@ -2642,6 +2649,60 @@ void ETSAnalyzer::CollectNonOptionalProperty(const ETSObjectType *objType, } } +static std::optional GetNameForProperty(ETSChecker *checker, const ir::ObjectExpression *expr, + ir::Expression *propExpr, const ir::Expression *key) +{ + if (key->IsStringLiteral()) { + return std::make_optional(key->AsStringLiteral()->Str()); + } + + if (key->IsIdentifier()) { + return std::make_optional(key->AsIdentifier()->Name()); + } + + checker->PossiblyLogError(propExpr, diagnostic::CLASS_COMPOSITE_INVALID_KEY, {}, expr->Start()); + propExpr->SetTsType(checker->GlobalTypeError()); + return std::nullopt; +} + +bool ETSAnalyzer::IsPropertyAssignable(ir::Expression *propExpr, ir::Expression *key, ir::Expression *value, + varbinder::LocalVariable *lv, const util::StringView &pname) const +{ + ETSChecker *checker = GetETSChecker(); + + auto *propType = checker->GetTypeOfVariable(lv); + if (propType->IsETSMethodType()) { + checker->PossiblyLogError(propExpr, diagnostic::OBJECT_LITERAL_METHOD_KEY, {}, propExpr->Start()); + propExpr->SetTsType(checker->GlobalTypeError()); + return false; + } + + if (auto *setterType = GetSetterType(lv, checker); setterType != nullptr) { + propType = setterType; + } + + value->SetPreferredType(propType); + propExpr->SetTsType(propType); + key->SetTsType(propType); + value->SetTsType(value->Check(checker)); + + const auto assignmentCtxFlags = + propExpr->HasAstNodeFlags(ir::AstNodeFlags::NO_THROW) ? TypeRelationFlag::NO_THROW : TypeRelationFlag::NONE; + const bool isPropAssignable = + // CC-OFFNXT(G.FMT.06-CPP) project code style + checker::AssignmentContext(checker->Relation(), value, value->TsType(), propType, value->Start(), + // CC-OFFNXT(G.FMT.06-CPP) project code style + {{diagnostic::PROP_INCOMPAT, {value->TsType(), propType, pname}}}, + assignmentCtxFlags) + .IsAssignable(); + if (!isPropAssignable && propExpr->HasAstNodeFlags(ir::AstNodeFlags::NO_THROW)) { + propExpr->SetTsType(checker->GlobalTypeError()); + return false; + } + + return true; +} + void ETSAnalyzer::CheckObjectExprPropsHelper(const ir::ObjectExpression *expr, checker::ETSObjectType *objType, checker::PropertySearchFlags searchFlags, std::unordered_map &properties) const @@ -2649,49 +2710,39 @@ void ETSAnalyzer::CheckObjectExprPropsHelper(const ir::ObjectExpression *expr, c ETSChecker *checker = GetETSChecker(); for (ir::Expression *propExpr : expr->Properties()) { if (!propExpr->IsProperty()) { - checker->LogError(diagnostic::OBJECT_LITERAL_NOT_KV, {}, expr->Start()); + checker->PossiblyLogError(propExpr, diagnostic::OBJECT_LITERAL_NOT_KV, {}, expr->Start()); + propExpr->SetTsType(checker->GlobalTypeError()); return; } + ir::Expression *key = propExpr->AsProperty()->Key(); ir::Expression *value = propExpr->AsProperty()->Value(); - util::StringView pname; - if (key->IsStringLiteral()) { - pname = key->AsStringLiteral()->Str(); - } else if (key->IsIdentifier()) { - pname = key->AsIdentifier()->Name(); - } else { - checker->LogError(diagnostic::CLASS_COMPOSITE_INVALID_KEY, {}, expr->Start()); + std::optional possibleKey = GetNameForProperty(checker, expr, propExpr, key); + if (!possibleKey.has_value()) { return; } + + const util::StringView pname = possibleKey.value(); + varbinder::LocalVariable *lv = objType->GetProperty(pname, searchFlags); if (lv == nullptr) { - checker->LogError(diagnostic::UNDEFINED_PROPERTY, {objType->Name(), pname}, propExpr->Start()); + checker->PossiblyLogError(propExpr, diagnostic::UNDEFINED_PROPERTY, {objType->Name(), pname}, + propExpr->Start()); + propExpr->SetTsType(checker->GlobalTypeError()); return; } + checker->ValidatePropertyAccess(lv, objType, propExpr->Start()); if (key->IsIdentifier()) { key->AsIdentifier()->SetVariable(lv); } - auto *propType = checker->GetTypeOfVariable(lv); - if (propType->IsETSMethodType()) { - checker->LogError(diagnostic::OBJECT_LITERAL_METHOD_KEY, {}, propExpr->Start()); - return; - } - - if (auto setterType = GetSetterType(lv, checker); setterType != nullptr) { - propType = setterType; + if (!IsPropertyAssignable(propExpr, key, value, lv, pname)) { + continue; } - value->SetPreferredType(propType); - propExpr->SetTsType(propType); - key->SetTsType(propType); - value->SetTsType(value->Check(checker)); - - checker::AssignmentContext(checker->Relation(), value, value->TsType(), propType, value->Start(), - {{diagnostic::PROP_INCOMPAT, {value->TsType(), propType, pname}}}); if (properties.find(pname) != properties.end()) { properties.erase(pname); } @@ -2705,7 +2756,7 @@ void ETSAnalyzer::CheckObjectExprProps(const ir::ObjectExpression *expr, ETSChecker *checker = GetETSChecker(); checker::ETSObjectType *objType = objectTypeForProperties; if (objType->IsGlobalETSObjectType() && !expr->Properties().empty()) { - checker->LogError(diagnostic::ERROR_ARKTS_NO_UNTYPED_OBJ_LITERALS, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::ERROR_ARKTS_NO_UNTYPED_OBJ_LITERALS, expr->Start()); } std::unordered_map propertyWithNonOptionalType; @@ -2717,10 +2768,11 @@ void ETSAnalyzer::CheckObjectExprProps(const ir::ObjectExpression *expr, for (const auto &[propName, ownerType] : propertyWithNonOptionalType) { if (objType == ownerType) { - checker->LogError(diagnostic::OBJECT_LITERAL_NON_OPTIONAL_PROP_LOST, {propName, objType}, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::OBJECT_LITERAL_NON_OPTIONAL_PROP_LOST, {propName, objType}, + expr->Start()); } else { - checker->LogError(diagnostic::OBJECT_LITERAL_NON_OPTIONAL_PROP_OF_SUPER_LOST, - {propName, ownerType, objType}, expr->Start()); + checker->PossiblyLogError(expr, diagnostic::OBJECT_LITERAL_NON_OPTIONAL_PROP_OF_SUPER_LOST, + {propName, ownerType, objType}, expr->Start()); } } diff --git a/ets2panda/checker/ETSAnalyzer.h b/ets2panda/checker/ETSAnalyzer.h index 3bac2af23338f69798704c8f3226e26c3bf49455..54602320177941a1a9dfd1505973eeee4cfdb80a 100644 --- a/ets2panda/checker/ETSAnalyzer.h +++ b/ets2panda/checker/ETSAnalyzer.h @@ -41,6 +41,8 @@ public: void GetUnionPreferredType(ir::Expression *expr, Type *originalType) const; void CollectNonOptionalProperty(const ETSObjectType *objType, std::unordered_map &props) const; + bool IsPropertyAssignable(ir::Expression *propExpr, ir::Expression *key, ir::Expression *value, + varbinder::LocalVariable *lv, const util::StringView &pname) const; void CheckObjectExprPropsHelper(const ir::ObjectExpression *expr, checker::ETSObjectType *objType, checker::PropertySearchFlags searchFlags, std::unordered_map &properties) const; diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 621a001d0b3e89e9c4912388f6d2b08e55743dad..9e803cd0cdbb6cf27eec72c0cab7c62f51081aef 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -513,7 +513,7 @@ public: Signature *ResolvePotentialTrailingLambda(ir::CallExpression *callExpr, ArenaVector const &signatures, ArenaVector &arguments); bool SetPreferredTypeBeforeValidate(Signature *substitutedSig, ir::Expression *argument, size_t index, - TypeRelationFlag flags, const std::vector &argTypeInferenceRequired); + const std::vector &argTypeInferenceRequired); // CC-OFFNXT(G.FUN.01-CPP) solid logic Signature *ValidateSignatures(ArenaVector &signatures, @@ -748,6 +748,7 @@ public: util::StringView GetHashFromFunctionType(ir::ETSFunctionType *type); static ETSObjectType *GetOriginalBaseType(Type *object); void SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression *expr, Type *annotationType); + bool CheckIfPreferredTypeIsValidForArrayExpression(ir::ArrayExpression *arrayExpr); bool IsExtensionETSFunctionType(const checker::Type *type); bool IsExtensionAccessorFunctionType(const checker::Type *type); bool IsArrayExprSizeValidForTuple(const ir::ArrayExpression *arrayExpr, const ETSTupleType *tuple); diff --git a/ets2panda/checker/checker.cpp b/ets2panda/checker/checker.cpp index ab4e17a2e079d1182cf78b6a0e32ecc6eedbaa1d..6c304f0b1fe50f685958d80b85d17d4a1141c67f 100644 --- a/ets2panda/checker/checker.cpp +++ b/ets2panda/checker/checker.cpp @@ -49,6 +49,23 @@ void Checker::LogError(const diagnostic::DiagnosticKind &diagnostic, const lexer LogError(diagnostic, {}, pos); } +// NOTE (smartin): these 'PossiblyLogError' logging functions will be removed when the overload resolution is reworked +void Checker::PossiblyLogError(const ir::AstNode *expr, const diagnostic::DiagnosticKind &diagnostic, + const util::DiagnosticMessageParams &diagnosticParams, const lexer::SourcePosition &pos) +{ + if (expr->HasAstNodeFlags(ir::AstNodeFlags::NO_THROW)) { + return; + } + + diagnosticEngine_.LogDiagnostic(diagnostic, diagnosticParams, pos); +} + +void Checker::PossiblyLogError(const ir::AstNode *expr, const diagnostic::DiagnosticKind &diagnostic, + const lexer::SourcePosition &pos) +{ + PossiblyLogError(expr, diagnostic, {}, pos); +} + void Checker::LogTypeError(std::string_view message, const lexer::SourcePosition &pos) { diagnosticEngine_.LogSemanticError(message, pos); diff --git a/ets2panda/checker/checker.h b/ets2panda/checker/checker.h index d7466fb0d418ece598f7f6319d2b61f1e64a4e9a..195151ab3b9a3c0a652d58faca86efe8d6a3deaf 100644 --- a/ets2panda/checker/checker.h +++ b/ets2panda/checker/checker.h @@ -183,6 +183,10 @@ public: void LogError(const diagnostic::DiagnosticKind &diagnostic, const util::DiagnosticMessageParams &diagnosticParams, const lexer::SourcePosition &pos); void LogError(const diagnostic::DiagnosticKind &diagnostic, const lexer::SourcePosition &pos); + void PossiblyLogError(const ir::AstNode *expr, const diagnostic::DiagnosticKind &diagnostic, + const util::DiagnosticMessageParams &diagnosticParams, const lexer::SourcePosition &pos); + void PossiblyLogError(const ir::AstNode *expr, const diagnostic::DiagnosticKind &diagnostic, + const lexer::SourcePosition &pos); void LogTypeError(std::string_view message, const lexer::SourcePosition &pos); void LogTypeError(const util::DiagnosticMessageParams &list, const lexer::SourcePosition &pos); void LogDiagnostic(const diagnostic::DiagnosticKind &kind, const util::DiagnosticMessageParams &list, @@ -484,42 +488,6 @@ private: Type *type_ {}; }; -class SignatureMatchContext { -public: - explicit SignatureMatchContext(Checker *checker, util::DiagnosticType diagnosticKind, bool isLogError = true) - : diagnosticEngine_(checker->DiagnosticEngine()), - diagnosticCheckpoint_(), - diagnosticKind_(diagnosticKind), - isLogError_(isLogError) - { - diagnosticCheckpoint_ = diagnosticEngine_.Save(); - } - - bool ValidSignatureMatchStatus() - { - std::array diagnosticCheckpoint = diagnosticEngine_.Save(); - return diagnosticCheckpoint_[diagnosticKind_] == diagnosticCheckpoint[diagnosticKind_]; - } - - ~SignatureMatchContext() - { - if (isLogError_) { - return; - } - - diagnosticEngine_.Rollback(diagnosticCheckpoint_); - } - - NO_COPY_SEMANTIC(SignatureMatchContext); - NO_MOVE_SEMANTIC(SignatureMatchContext); - -private: - util::DiagnosticEngine &diagnosticEngine_; - std::array diagnosticCheckpoint_; - util::DiagnosticType diagnosticKind_; - bool isLogError_; -}; - class SignatureCollectContext { public: explicit SignatureCollectContext(Checker *checker, varbinder::LocalVariable *overloadDeclaration, diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index 2643ee7f1b29e25283309e2b118bf9162aa1d565..84bfc3bb3553edf9e7f3b5ff7640a282188c0714 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -468,13 +468,13 @@ static void ClearPreferredTypeForArray(checker::ETSChecker *checker, ir::Express { if (argument->IsArrayExpression()) { // fixed array and resizeable array will cause problem here, so clear it. - argument->AsArrayExpression()->CleanCheckInformation(); + argument->CleanCheckInformation(); argument->AsArrayExpression()->SetPreferredTypeBasedOnFuncParam(checker, paramType, flags); } else if (argument->IsETSNewArrayInstanceExpression()) { - argument->AsETSNewArrayInstanceExpression()->CleanCheckInformation(); + argument->CleanCheckInformation(); argument->AsETSNewArrayInstanceExpression()->SetPreferredTypeBasedOnFuncParam(checker, paramType, flags); } else if (argument->IsETSNewMultiDimArrayInstanceExpression()) { - argument->AsETSNewMultiDimArrayInstanceExpression()->CleanCheckInformation(); + argument->CleanCheckInformation(); argument->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredTypeBasedOnFuncParam(checker, paramType, flags); } else { @@ -631,7 +631,14 @@ bool ETSChecker::ValidateSignatureInvocationContext(Signature *substitutedSig, i { Type *targetType = substitutedSig->Params()[index]->TsType(); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + // NOTE (smartin): remove these flag hacks after the overload resolution is completely reworked + if ((flags & TypeRelationFlag::NO_THROW) != 0) { + argument->IterateRecursively([](ir::AstNode *node) { node->AddAstNodeFlags(ir::AstNodeFlags::NO_THROW); }); + } Type *argumentType = argument->Check(this); + if ((flags & TypeRelationFlag::NO_THROW) != 0) { + argument->IterateRecursively([](ir::AstNode *node) { node->RemoveAstNodeFlags(ir::AstNodeFlags::NO_THROW); }); + } flags |= (TypeRelationFlag::ONLY_CHECK_WIDENING); @@ -2768,10 +2775,6 @@ Signature *ETSChecker::ValidateOrderSignature( return nullptr; } - // When process first match, if current signature is not matched, do not log TypeError - SignatureMatchContext signatureMatchContext(this, util::DiagnosticType::SEMANTIC, - (flags & TypeRelationFlag::NO_THROW) == 0); - size_t const argCount = arguments.size(); auto const hasRestParameter = signature->RestVar() != nullptr; size_t compareCount = argCount; @@ -2780,8 +2783,11 @@ Signature *ETSChecker::ValidateOrderSignature( compareCount = compareCount - 1; } + const bool throwError = (flags & TypeRelationFlag::NO_THROW) == 0; if (compareCount < signature->MinArgCount() || (argCount > signature->ArgCount() && !hasRestParameter)) { - LogError(diagnostic::PARAM_COUNT_MISMATCH, {signature->MinArgCount(), argCount}, pos); + if (throwError) { + LogError(diagnostic::PARAM_COUNT_MISMATCH, {signature->MinArgCount(), argCount}, pos); + } return nullptr; } @@ -2792,8 +2798,7 @@ Signature *ETSChecker::ValidateOrderSignature( auto count = std::min(signature->ArgCount(), argCount); // Check all required formal parameter(s) first // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - if (!ValidateOrderSignatureRequiredParams(signature, arguments, flags, argTypeInferenceRequired) || - !signatureMatchContext.ValidSignatureMatchStatus()) { + if (!ValidateOrderSignatureRequiredParams(signature, arguments, flags, argTypeInferenceRequired)) { return nullptr; } @@ -2801,8 +2806,8 @@ Signature *ETSChecker::ValidateOrderSignature( if (!hasRestParameter || (count >= argCount && !signature->RestVar()->TsType()->IsETSTupleType())) { return signature; } - if (!ValidateSignatureRestParams(signature, arguments, flags, true, unique) || - !signatureMatchContext.ValidSignatureMatchStatus()) { + + if (!ValidateSignatureRestParams(signature, arguments, flags, true, unique)) { return nullptr; } @@ -2810,12 +2815,17 @@ Signature *ETSChecker::ValidateOrderSignature( } bool ETSChecker::SetPreferredTypeBeforeValidate(Signature *substitutedSig, ir::Expression *argument, size_t index, - TypeRelationFlag flags, const std::vector &argTypeInferenceRequired) { auto const paramType = GetNonNullishType(substitutedSig->Params()[index]->TsType()); if (argument->IsObjectExpression()) { - argument->AsObjectExpression()->SetPreferredType(paramType); + if (!paramType->IsETSObjectType()) { + return false; + } + if (paramType->AsETSObjectType()->IsBoxedPrimitive()) { + return false; + } + argument->SetPreferredType(paramType); } if (argument->IsMemberExpression()) { @@ -2836,18 +2846,6 @@ bool ETSChecker::SetPreferredTypeBeforeValidate(Signature *substitutedSig, ir::E return CheckLambdaInfer(param->TypeAnnotation(), argument->AsArrowFunctionExpression(), paramType); } - if (argument->IsArrayExpression()) { - argument->AsArrayExpression()->SetPreferredTypeBasedOnFuncParam(this, paramType, flags); - } - - if (argument->IsETSNewArrayInstanceExpression()) { - argument->AsETSNewArrayInstanceExpression()->SetPreferredTypeBasedOnFuncParam(this, paramType, flags); - } - - if (argument->IsETSNewMultiDimArrayInstanceExpression()) { - argument->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredTypeBasedOnFuncParam(this, paramType, flags); - } - return true; } @@ -2864,19 +2862,27 @@ bool ETSChecker::ValidateOrderSignatureRequiredParams(Signature *substitutedSig, } commonArity = commonArity - 1; } + bool throwError = (flags & TypeRelationFlag::NO_THROW) == 0; for (size_t index = 0; index < commonArity; ++index) { - auto &argument = arguments[index]; - if (!SetPreferredTypeBeforeValidate(substitutedSig, argument, index, flags, argTypeInferenceRequired)) { + const auto &argument = arguments[index]; + auto *const paramType = GetNonNullishType(substitutedSig->Params()[index]->TsType()); + if (!SetPreferredTypeBeforeValidate(substitutedSig, argument, index, argTypeInferenceRequired)) { return false; } if (argument->IsSpreadElement()) { - LogError(diagnostic::SPREAD_ONTO_SINGLE_PARAM, {}, argument->Start()); + if (throwError) { + LogError(diagnostic::SPREAD_ONTO_SINGLE_PARAM, {}, argument->Start()); + } return false; } + ClearPreferredTypeForArray(this, argument, paramType, flags, false); + if (argument->IsIdentifier() && IsInvalidArgumentAsIdentifier(Scope(), argument->AsIdentifier())) { - LogError(diagnostic::ARG_IS_CLASS_ID, {}, argument->Start()); + if (throwError) { + LogError(diagnostic::ARG_IS_CLASS_ID, {}, argument->Start()); + } return false; } @@ -2910,9 +2916,43 @@ bool ETSChecker::ValidateOrderSignatureInvocationContext(Signature *substitutedS std::size_t index, TypeRelationFlag flags) { Type *targetType = substitutedSig->Params()[index]->TsType(); + // NOTE (smartin): remove these flag hacks after the overload resolution is completely reworked + if ((flags & TypeRelationFlag::NO_THROW) != 0) { + argument->AddAstNodeFlags(ir::AstNodeFlags::NO_THROW); + argument->IterateRecursively([](ir::AstNode *node) { node->AddAstNodeFlags(ir::AstNodeFlags::NO_THROW); }); + } + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) Type *argumentType = argument->Check(this); + if ((flags & TypeRelationFlag::NO_THROW) != 0) { + argument->RemoveAstNodeFlags(ir::AstNodeFlags::NO_THROW); + argument->IterateRecursively([](ir::AstNode *node) { node->RemoveAstNodeFlags(ir::AstNodeFlags::NO_THROW); }); + } + + bool isAnyErroneousNodeInArgument = false; + argument->IterateRecursively([this, &isAnyErroneousNodeInArgument](ir::AstNode *node) { + if (node->IsExpression() && node->AsExpression()->TsType() == GlobalTypeError()) { + isAnyErroneousNodeInArgument = true; + return; + } + }); + if (isAnyErroneousNodeInArgument || argument->TsType()->IsTypeError()) { + // Some checks invalidate the type of argument node (if the type is erroneous), but won't log an error into + // diagnostics. This is needed as overload signatures try to match a signature one after another, and a not + // matching signature doesn't necessarily indicate wrong code (as an overload later in the overload list may + // match with the argument type). This can happen with expressions that needs their type inferred (eg. array + // expressions, object literals ...). When we set a 'preferred type' to a non-matching type to the expression + // (eg. we set wrong arity tuple), we don't want to throw an error during the check of the expression (as said, + // because another overload may set the correct 'preferred type' for it). Type error type is assignable to + // anything (so the invocation ctx check on next line will pass), but we don't want to allow a call with + // incorrect arguments. In this case a signature with bad parameter type and argument with 'ErrorType' will be + // chosen, which may cause errors in later phases (as a phase terminates if an error happened). + // This solution is brittle (but better than deleting all diagnostics thrown during argument check), so when + // overload resolution will be reworked, it'll need to be deleted. + return false; + } + flags |= TypeRelationFlag::ONLY_CHECK_WIDENING; auto const invocationCtx = diff --git a/ets2panda/checker/ets/utilityTypeHandlers.cpp b/ets2panda/checker/ets/utilityTypeHandlers.cpp index 8b9f73630b7fad2c31ef2f667db6383b3e79f0fa..28b54ba8e628efc71c518224abf5d145858c264d 100644 --- a/ets2panda/checker/ets/utilityTypeHandlers.cpp +++ b/ets2panda/checker/ets/utilityTypeHandlers.cpp @@ -1266,8 +1266,8 @@ void ETSChecker::ValidateObjectLiteralForRequiredType(const ETSObjectType *const auto fieldname = method->AsMethodDefinition()->Key()->AsIdentifier()->Name(); if (!initObjExprContainsField(fieldname)) { - LogError(diagnostic::REQUIRED_PROP_MISSING_INIT, {fieldname, requiredType->Name()}, - initObjExpr->Start()); + PossiblyLogError(initObjExpr, diagnostic::REQUIRED_PROP_MISSING_INIT, {fieldname, requiredType->Name()}, + initObjExpr->Start()); } } @@ -1276,7 +1276,8 @@ void ETSChecker::ValidateObjectLiteralForRequiredType(const ETSObjectType *const for (const auto &[propName, _] : requiredType->InstanceFields()) { if (!initObjExprContainsField(propName)) { - LogError(diagnostic::REQUIRED_PROP_MISSING_INIT, {propName, requiredType->Name()}, initObjExpr->Start()); + PossiblyLogError(initObjExpr, diagnostic::REQUIRED_PROP_MISSING_INIT, {propName, requiredType->Name()}, + initObjExpr->Start()); } } } diff --git a/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp b/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp index 3551988cce23a1d604b5355a6d853d6917efacb1..9c851d057b7dd4a6a10f5a68f92abecd090ded86 100644 --- a/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp +++ b/ets2panda/compiler/lowering/ets/enumPostCheckLowering.cpp @@ -434,7 +434,7 @@ ir::AstNode *EnumPostCheckLoweringPhase::BuildEnumCasting(ir::AstNode *const nod return node; } return GenerateEnumCasting(node->AsTSAsExpression(), castFlag); -}; +} bool EnumPostCheckLoweringPhase::PerformForModule(public_lib::Context *ctx, parser::Program *program) { diff --git a/ets2panda/ir/astNodeFlags.h b/ets2panda/ir/astNodeFlags.h index 2d26cce87b2c72eb18c004d3abefbb41fab060e4..3af64114400f043b6711345c294d34aa0af202ad 100644 --- a/ets2panda/ir/astNodeFlags.h +++ b/ets2panda/ir/astNodeFlags.h @@ -37,6 +37,8 @@ enum class AstNodeFlags : uint16_t { TMP_CONVERT_PRIMITIVE_CAST_METHOD_CALL = 1U << 8U, // Moved out of the ir::Expression IS_GROUPED = 1U << 9U, + // Used for overload resolution. Remove after its rework. + NO_THROW = 1U << 10U, /* do not introduce new flags. all the existing to be removed */ }; diff --git a/ets2panda/parser/program/DeclarationCache.cpp b/ets2panda/parser/program/DeclarationCache.cpp index 017abdb7698331e70dc3d7435eb8e4c8e755437a..82084353bb19d3f48c403acf4b5c6374650884d9 100644 --- a/ets2panda/parser/program/DeclarationCache.cpp +++ b/ets2panda/parser/program/DeclarationCache.cpp @@ -25,7 +25,7 @@ UniqueSpinMutex::~UniqueSpinMutex() { // Atomic with relaxed order reason: read of field ES2PANDA_ASSERT(spin_.load(std::memory_order_relaxed) == LOCK_OFF); -}; +} // CC-OFFNXT(G.NAM.03-CPP) project code style void UniqueSpinMutex::lock() diff --git a/ets2panda/test/ast/compiler/ets/ObjectLiteral_neg_1.ets b/ets2panda/test/ast/compiler/ets/ObjectLiteral_neg_1.ets index 0cae5d18a7c057823b44ef1ea811dea80572ef69..1c920e4fb59df5027c704d147cab88a1166abc5c 100644 --- a/ets2panda/test/ast/compiler/ets/ObjectLiteral_neg_1.ets +++ b/ets2panda/test/ast/compiler/ets/ObjectLiteral_neg_1.ets @@ -38,3 +38,4 @@ const router:RouterT = { /* @@? 32:5 Error TypeError: Class or interface methods cannot be initialized within an object literal. */ +/* @@? 33:5 Error TypeError: Class or interface methods cannot be initialized within an object literal. */ diff --git a/ets2panda/test/ast/compiler/ets/class_without_closing_parentheses.ets b/ets2panda/test/ast/compiler/ets/class_without_closing_parentheses.ets index 2926286b817fccaa6a16213dcdb5454341873c23..dcbc5a7762530c920008ad1aded203db7df9db26 100644 --- a/ets2panda/test/ast/compiler/ets/class_without_closing_parentheses.ets +++ b/ets2panda/test/ast/compiler/ets/class_without_closing_parentheses.ets @@ -36,5 +36,5 @@ export default class TreeMap implements ReadonlyTreeMap { /* @@? 19:24 Error SyntaxError: Namespace is allowed only at the top level or inside a namespace. */ /* @@? 21:15 Error TypeError: Variable 'buffer' has already been declared. */ /* @@? 21:15 Error TypeError: Merging declarations is not supported, please keep all definitions of classes, interfaces and enums compact in the codebase! */ -/* @@? 24:32 Error TypeError: Indexed access is not supported for such expression type. */ -/* @@? 26:20 Error TypeError: Namespace 'buffer' cannot be used as a type. */ +/* @@? 21:24 Error TypeError: No matching construct signature for ArrayBuffer(length) */ +/* @@? 21:40 Error TypeError: Unresolved reference length */ diff --git a/ets2panda/test/ast/compiler/ets/null_pointer_error1.ets b/ets2panda/test/ast/compiler/ets/null_pointer_error1.ets index 51edfbcad59292d9da1513c8cca2d09fd6dfbab6..f94cf496e9582473afff1080d9219a79e287a476 100644 --- a/ets2panda/test/ast/compiler/ets/null_pointer_error1.ets +++ b/ets2panda/test/ast/compiler/ets/null_pointer_error1.ets @@ -23,4 +23,4 @@ function mustCallArgSize() { }); } -/* @@? 19:5 Error TypeError: No matching call signature for push((() => void)) */ +/* @@? 20:9 Error TypeError: Unresolved reference xf */ diff --git a/ets2panda/test/ast/parser/ets/overload_function_match_neg.ets b/ets2panda/test/ast/parser/ets/overload_function_match_neg.ets index 29b285ee8de457bb2963a4208ac60f6ca8f56640..5637a371b9e2774bc91ddc726be5021dde861eb0 100644 --- a/ets2panda/test/ast/parser/ets/overload_function_match_neg.ets +++ b/ets2panda/test/ast/parser/ets/overload_function_match_neg.ets @@ -27,12 +27,8 @@ class DummyArray { let props: DummyArray = new DummyArray() -/* @@ label1 */props.push( - /* @@ label2 */new A('eaw1', () => { - let s: string = /* @@ label3 */1 - }) -) + props.push(new A('eaw1', () => { + let s: string = /* @@ label3 */1 + })) -/* @@@ label1 Error TypeError: No matching call signature for push(...) */ -/* @@@ label2 Error TypeError: No matching construct signature for A("eaw1", (() => void)) */ /* @@@ label3 Error TypeError: Type 'Int' cannot be assigned to type 'String' */