* Copyright (c) 2021-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cstddef>
#include "checker/types/type.h"
#include "checker/types/typeRelation.h"
#include "lexer/token/sourceLocation.h"
#include "util/ustring.h"
#include "checker/ETSAnalyzerHelpers.h"
#include "checker/ETSchecker.h"
#include "checker/ets/typeRelationContext.h"
#include "checker/types/ets/etsAwaitedType.h"
#include "checker/types/ets/etsObjectType.h"
#include "checker/types/ets/etsPartialTypeParameter.h"
#include "checker/types/ets/etsResizableArrayType.h"
#include "checker/types/ets/etsReturnTypeUtilityType.h"
#include "checker/types/ets/etsTupleType.h"
#include "checker/types/typeError.h"
#include "compiler/lowering/scopesInit/scopesInitPhase.h"
#include "generated/signatures.h"
#include "ir/base/catchClause.h"
#include "ir/base/classDefinition.h"
#include "ir/base/classProperty.h"
#include "ir/base/methodDefinition.h"
#include "ir/base/scriptFunction.h"
#include "ir/base/spreadElement.h"
#include "ir/ets/etsFunctionType.h"
#include "ir/ets/etsParameterExpression.h"
#include "ir/expressions/arrowFunctionExpression.h"
#include "ir/expressions/assignmentExpression.h"
#include "ir/expressions/callExpression.h"
#include "ir/expressions/functionExpression.h"
#include "ir/expressions/identifier.h"
#include "ir/expressions/memberExpression.h"
#include "ir/expressions/objectExpression.h"
#include "ir/statements/blockStatement.h"
#include "ir/statements/doWhileStatement.h"
#include "ir/statements/expressionStatement.h"
#include "ir/statements/forInStatement.h"
#include "ir/statements/forOfStatement.h"
#include "ir/statements/forUpdateStatement.h"
#include "ir/statements/returnStatement.h"
#include "ir/statements/switchStatement.h"
#include "ir/statements/whileStatement.h"
#include "ir/ts/tsTypeAliasDeclaration.h"
#include "ir/ts/tsTypeParameter.h"
#include "ir/ts/tsTypeParameterInstantiation.h"
#include "parser/program/program.h"
#include "util/helpers.h"
#include "util/nameMangler.h"
#include "varbinder/ETSBinder.h"
namespace ark::es2panda::checker {
bool ETSChecker::IsOverloadDeclaration(ir::Expression *expr)
{
while (expr->IsMemberExpression()) {
expr = expr->AsMemberExpression()->Property();
}
if (expr->IsIdentifier() && expr->AsIdentifier()->Variable() != nullptr) {
return expr->AsIdentifier()->Variable()->HasFlag(varbinder::VariableFlags::OVERLOAD);
}
return false;
}
static Signature *CheckDerivedStaticMethodCall(ETSChecker *checker, ir::CallExpression *expr, Signature *signature)
{
if (signature == nullptr || !signature->HasFunction() || !signature->Function()->IsStatic() ||
!expr->Callee()->IsMemberExpression()) {
return signature;
}
auto *memberExpr = expr->Callee()->AsMemberExpression();
if (!memberExpr->Property()->IsIdentifier()) {
return signature;
}
const auto *targetRef = checker->GetTargetRef(memberExpr);
if (!checker::IsClassStaticAccessTarget(targetRef)) {
return signature;
}
auto *targetType = memberExpr->ObjType();
if (targetType == nullptr || !targetType->IsETSObjectType()) {
return signature;
}
auto *owner = signature->Owner();
if (owner == nullptr || owner->IsSameBasedGeneric(checker->Relation(), targetType->AsETSObjectType())) {
return signature;
}
auto propertyName = memberExpr->Property()->AsIdentifier()->Name();
if (propertyName.Is(compiler::Signatures::STATIC_INSTANTIATE_METHOD) ||
propertyName.Is(compiler::Signatures::STATIC_INVOKE_METHOD)) {
return signature;
}
checker->LogDiagnostic(diagnostic::STATIC_PROP_INHERITANCE, {memberExpr->Property()->AsIdentifier()->Name(), owner},
memberExpr->Property()->Start());
return nullptr;
}
static Type *GetParamTypeForArgument(Signature const *signature, ETSChecker *checker, const SignatureInfo *sigInfo,
size_t argIndex)
{
auto maybeRestParam = argIndex >= signature->ArgCount();
if (!maybeRestParam) {
return sigInfo->params[argIndex]->TsType();
}
if (sigInfo->restVar == nullptr) {
return nullptr;
}
auto *restType = sigInfo->restVar->TsType();
if (util::Helpers::IsArrayType(restType)) {
return checker->GetElementTypeOfArray(restType);
}
return restType;
}
static Type *GetArgumentType(ETSChecker *checker, ir::Expression *arg)
{
if (arg->IsSpreadElement()) {
auto spreadArg = arg->AsSpreadElement()->Argument();
auto type = spreadArg->Check(checker);
if (type->IsETSTupleType()) {
return type;
}
if (util::Helpers::IsArrayType(type)) {
return checker->GetElementTypeOfArray(type);
}
return checker->GetElementTypeOfSpreadType(type);
}
return arg->Check(checker);
}
static std::pair<Type *, bool> GetArgumentTypeForInfer(ETSChecker *checker, ir::Expression *arg)
{
if (arg->IsArrowFunctionExpression()) {
InferMatchContext inferMatchContext(checker, util::DiagnosticType::SEMANTIC, arg->Range(), false);
auto *argType = GetArgumentType(checker, arg);
if (!inferMatchContext.ValidMatchStatus()) {
arg->CleanCheckInformation();
return {nullptr, true};
}
checker->Relation()->SetNode(arg);
return {argType, false};
}
return {GetArgumentType(checker, arg), false};
}
static Substitution RenameParameters(ETSChecker *checker, Signature *signature)
{
auto res = Substitution {};
for (auto *tp : signature->TypeParams()) {
auto *tpar = tp->AsETSTypeParameter();
auto *newTpar = tpar->Clone(checker)->AsETSTypeParameter();
newTpar->SetUnderInference();
res.emplace(tpar, newTpar);
}
for (auto *tp : signature->TypeParams()) {
auto *tpar = tp->AsETSTypeParameter();
auto *newTpar = res[tpar]->AsETSTypeParameter();
if (tpar->GetConstraintType() != nullptr) {
newTpar->SetConstraintType(tpar->GetConstraintType()->Substitute(checker->Relation(), &res));
}
if (tpar->GetDefaultType() != nullptr) {
newTpar->SetDefaultType(tpar->GetDefaultType()->Substitute(checker->Relation(), &res));
}
}
return res;
}
static Substitution ComposeTemporarySubstitution(ETSChecker *checker, Substitution *one, Substitution *two)
{
Substitution res {};
for (auto [tpar, val] : *one) {
auto nval = val->Substitute(checker->Relation(), two);
if (nval != val) {
res.emplace(tpar, nval);
}
}
return res;
}
static void InferUntilFail(Signature const *const signature, const ArenaVector<ir::Expression *> &arguments,
ETSChecker *checker, Substitution *substitution)
{
auto *sigInfo = signature->GetSignatureInfo();
auto &sigParams = signature->GetSignatureInfo()->typeParams;
ArenaVector<bool> inferStatus(checker->Allocator()->Adapter());
inferStatus.assign(arguments.size(), false);
bool anyChange = true;
size_t lastSubsititutionSize = 0;
checker->AddStatus(checker::CheckerStatus::IN_TYPE_INFER);
ES2PANDA_ASSERT(substitution != nullptr);
while (anyChange && substitution->size() < sigParams.size()) {
anyChange = false;
for (size_t ix = 0; ix < arguments.size(); ++ix) {
if (inferStatus[ix]) {
continue;
}
auto *arg = arguments[ix];
if (arg->IsObjectExpression()) {
continue;
}
Type *paramType = GetParamTypeForArgument(signature, checker, sigInfo, ix);
if (paramType == nullptr) {
continue;
}
if (!substitution->empty()) {
paramType = paramType->Substitute(checker->Relation(), substitution);
}
arg->SetPreferredType(paramType);
auto [argType, needContinue] = GetArgumentTypeForInfer(checker, arg);
if (needContinue) {
continue;
}
if (checker->EnhanceSubstitutionForType(sigInfo->typeParams, paramType, argType, substitution)) {
inferStatus[ix] = true;
}
if (lastSubsititutionSize != substitution->size()) {
lastSubsititutionSize = substitution->size();
anyChange = true;
}
}
}
checker->RemoveStatus(checker::CheckerStatus::IN_TYPE_INFER);
}
static bool IsCompatibleTypeArgument(ETSChecker *checker, ETSTypeParameter *typeParam, Type *typeArgument,
const Substitution *substitution);
static bool CheckTypeParamsConstraint(ETSChecker *checker, const Substitution *substitution,
const lexer::SourcePosition &pos)
{
for (auto it = substitution->begin(); it != substitution->end(); ++it) {
auto *constraintType = it->second;
auto *typeParameter = it->first->AsETSTypeParameter();
while (typeParameter->GetConstraintType() != nullptr &&
typeParameter->GetConstraintType()->IsETSTypeParameter()) {
typeParameter = typeParameter->GetConstraintType()->AsETSTypeParameter();
}
if (!IsCompatibleTypeArgument(checker, typeParameter, constraintType, substitution)) {
checker->LogError(diagnostic::TYPEARG_TYPEPARAM_SUBTYPING,
{constraintType, typeParameter->GetConstraintType()}, pos);
return false;
}
}
return true;
}
static std::optional<Substitution> BuildImplicitSubstitutionForArguments(ETSChecker *checker, Signature *signature,
const ArenaVector<ir::Expression *> &arguments,
const lexer::SourcePosition &pos)
{
auto substitution = Substitution {};
auto paramRenamer = RenameParameters(checker, signature);
auto sigRenamed = signature->Substitute(checker->Relation(), ¶mRenamer);
auto *sigInfo = sigRenamed->GetSignatureInfo();
auto &sigParams = sigInfo->typeParams;
InferUntilFail(sigRenamed, arguments, checker, &substitution);
auto sigArgs = signature->Params();
auto noNeverArg = std::all_of(sigArgs.begin(), sigArgs.end(),
[](varbinder::LocalVariable *param) { return !param->TsType()->IsETSNeverType(); });
if (substitution.size() == sigParams.size()) {
if (!CheckTypeParamsConstraint(checker, &substitution, pos)) {
return std::nullopt;
}
return ComposeTemporarySubstitution(checker, ¶mRenamer, &substitution);
}
for (const auto typeParam : sigParams) {
auto newTypeParam = typeParam->AsETSTypeParameter();
if (auto it = substitution.find(newTypeParam); it != substitution.cend()) {
continue;
}
if (newTypeParam->GetDefaultType() == nullptr) {
if (sigArgs.empty() || noNeverArg) {
checker->LogError(diagnostic::GENERIC_TYPE_PARAM_INFERENCE_FAILED,
{typeParam, signature->Function()->Id()->Name()}, pos);
}
continue;
}
auto dflt = newTypeParam->GetDefaultType()->Substitute(checker->Relation(), &substitution);
if (!checker->EnhanceSubstitutionForType(sigInfo->typeParams, newTypeParam, dflt, &substitution)) {
return std::nullopt;
}
}
if (substitution.size() != sigParams.size() &&
(signature->Function()->ReturnTypeAnnotation() == nullptr ||
sigRenamed->Function()->ReturnTypeAnnotation()->TsType() == nullptr || sigRenamed->ReturnType() == nullptr ||
!checker->EnhanceSubstitutionForType(sigInfo->typeParams,
sigRenamed->Function()->ReturnTypeAnnotation()->TsType(),
sigRenamed->ReturnType(), &substitution))) {
return std::nullopt;
}
if (!CheckTypeParamsConstraint(checker, &substitution, pos)) {
return std::nullopt;
}
return ComposeTemporarySubstitution(checker, ¶mRenamer, &substitution);
}
static std::optional<Substitution> BuildExplicitSubstitutionForArguments(ETSChecker *checker, Signature *signature,
const ArenaVector<ir::TypeNode *> ¶ms,
const lexer::SourcePosition &pos)
{
auto &sigParams = signature->GetSignatureInfo()->typeParams;
auto substitution = Substitution {};
auto constraintsSubstitution = Substitution {};
ArenaVector<Type *> instArgs {checker->Allocator()->Adapter()};
for (size_t ix = 0; ix < params.size(); ++ix) {
instArgs.push_back(params[ix]->GetType(checker));
if (ix < sigParams.size()) {
checker->EmplaceSubstituted(&constraintsSubstitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]);
}
}
for (size_t ix = instArgs.size(); ix < sigParams.size(); ++ix) {
auto typeParam = sigParams[ix]->AsETSTypeParameter();
auto *dflt = typeParam->GetDefaultType();
if (dflt == nullptr) {
break;
}
dflt = dflt->Substitute(checker->Relation(), &constraintsSubstitution);
instArgs.push_back(dflt);
checker->EmplaceSubstituted(&constraintsSubstitution, typeParam, instArgs[ix]);
}
if (sigParams.size() != instArgs.size()) {
checker->LogError(diagnostic::RTYPE_PARAM_COUNT_MISMATCH, {sigParams.size(), instArgs.size()}, pos);
return std::nullopt;
}
for (size_t ix = 0; ix < sigParams.size(); ix++) {
if (!IsCompatibleTypeArgument(checker, sigParams[ix]->AsETSTypeParameter(), instArgs[ix],
&constraintsSubstitution)) {
auto *constraintType = sigParams[ix]->AsETSTypeParameter()->GetConstraintType()->Substitute(
checker->Relation(), &constraintsSubstitution);
checker->LogError(diagnostic::TYPEARG_TYPEPARAM_SUBTYPING, {instArgs[ix], constraintType}, pos);
return std::nullopt;
}
checker->EmplaceSubstituted(&substitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]);
}
return substitution;
}
static Signature *MaybeSubstituteTypeParameters(
ETSChecker *checker, std::tuple<Signature *, const ir::TSTypeParameterInstantiation *, TypeRelationFlag, bool> info,
const ArenaVector<ir::Expression *> &arguments, const lexer::SourcePosition &pos)
{
auto [signature, typeArguments, flags, _] = info;
if (typeArguments == nullptr && signature->GetSignatureInfo()->typeParams.empty()) {
return signature;
}
const std::optional<Substitution> substitution =
(typeArguments != nullptr)
? BuildExplicitSubstitutionForArguments(checker, signature, typeArguments->Params(), pos)
: BuildImplicitSubstitutionForArguments(checker, signature, arguments, pos);
return (!substitution.has_value()) ? nullptr : signature->Substitute(checker->Relation(), &substitution.value());
}
static varbinder::Scope *NodeScope(ir::AstNode *ast)
{
if (ast->IsBlockStatement()) {
return ast->AsBlockStatement()->Scope();
}
if (ast->IsBlockExpression()) {
return ast->AsBlockExpression()->Scope();
}
if (ast->IsDoWhileStatement()) {
return ast->AsDoWhileStatement()->Scope();
}
if (ast->IsForInStatement()) {
return ast->AsForInStatement()->Scope();
}
if (ast->IsForOfStatement()) {
return ast->AsForOfStatement()->Scope();
}
if (ast->IsForUpdateStatement()) {
return ast->AsForUpdateStatement()->Scope();
}
if (ast->IsSwitchStatement()) {
return ast->AsSwitchStatement()->Scope();
}
if (ast->IsWhileStatement()) {
return ast->AsWhileStatement()->Scope();
}
if (ast->IsCatchClause()) {
return ast->AsCatchClause()->Scope();
}
if (ast->IsClassDefinition()) {
return ast->AsClassDefinition()->Scope();
}
if (ast->IsScriptFunction()) {
return ast->AsScriptFunction()->Scope()->ParamScope();
}
return nullptr;
}
static bool IsCompatibleTypeArgument(ETSChecker *checker, ETSTypeParameter *typeParam, Type *typeArgument,
const Substitution *substitution)
{
if (typeArgument->IsTypeError()) {
return true;
}
ES2PANDA_ASSERT(ETSChecker::IsReferenceType(typeArgument));
auto constraint = typeParam->GetConstraintType()->Substitute(checker->Relation(), substitution);
return checker->Relation()->IsSupertypeOf(constraint, typeArgument->Substitute(checker->Relation(), substitution));
}
static bool EnhanceSubstitutionForType(ETSChecker *checker, const ArenaVector<Type *> &typeParams, Type *paramType,
Type *argumentType, Substitution *substitution);
static bool EnhanceSubstitutionForReadonly(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
ETSReadonlyType *paramType, Type *argumentType, Substitution *substitution)
{
return EnhanceSubstitutionForType(checker, typeParams, paramType->GetUnderlying(),
checker->GetReadonlyType(argumentType), substitution);
}
static bool ValidateTypeSubstitution(ETSChecker *checker, const ArenaVector<Type *> &typeParams, Type *ctype,
Type *argumentType, Substitution *substitution)
{
if (!EnhanceSubstitutionForType(checker, typeParams, ctype, argumentType, substitution)) {
return false;
}
return !ctype->IsETSTypeParameter() ||
(substitution->count(ctype->AsETSTypeParameter()) > 0 &&
checker->Relation()->IsAssignableTo(argumentType, substitution->at(ctype->AsETSTypeParameter())));
}
static bool EnhanceSubstitutionForUnion(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
ETSUnionType *paramUn, Type *argumentType, Substitution *substitution)
{
if (!argumentType->IsETSUnionType()) {
bool foundValid = false;
for (Type *ctype : paramUn->ConstituentTypes()) {
foundValid |= ValidateTypeSubstitution(checker, typeParams, ctype, argumentType, substitution);
}
return foundValid;
}
auto *const argUn = argumentType->AsETSUnionType();
std::vector<Type *> paramWlist;
std::vector<Type *> argWlist;
bool isIdenticalUpToTypeParams = false;
for (auto *pc : paramUn->ConstituentTypes()) {
for (auto *ac : argUn->ConstituentTypes()) {
{
SavedTypeRelationFlagsContext savedFlagsCtx(checker->Relation(),
TypeRelationFlag::IGNORE_TYPE_PARAMETERS);
isIdenticalUpToTypeParams = checker->Relation()->IsIdenticalTo(pc, ac);
}
if (!isIdenticalUpToTypeParams) {
paramWlist.push_back(pc);
argWlist.push_back(ac);
continue;
}
if (!EnhanceSubstitutionForType(checker, typeParams, pc, ac, substitution)) {
return false;
}
}
}
auto *const newArg = checker->CreateETSUnionType(std::move(argWlist));
for (auto *pc : paramWlist) {
if (!EnhanceSubstitutionForType(checker, typeParams, pc, newArg, substitution)) {
return false;
}
}
return true;
}
static bool ProcessUntypedParameter(ETSChecker *checker, size_t paramIndex, Signature *paramSig, Signature *argSig,
Substitution *substitution)
{
auto declNode = argSig->Params()[paramIndex]->Declaration()->Node();
if (!declNode->IsETSParameterExpression() || !checker->HasStatus(CheckerStatus::IN_TYPE_INFER)) {
return false;
}
auto *paramExpr = declNode->AsETSParameterExpression();
if (paramExpr->Ident()->TypeAnnotation() != nullptr) {
return false;
}
Type *paramType = paramSig->Params()[paramIndex]->TsType();
Type *inferredType = paramType->Substitute(checker->Relation(), substitution);
varbinder::Variable *argParam = argSig->Params()[paramIndex];
argParam->SetTsType(inferredType);
paramExpr->Ident()->SetTsType(inferredType);
paramExpr->Ident()->Variable()->SetTsType(inferredType);
return true;
}
static void RemoveInvalidTypeMarkers(ir::AstNode *node) noexcept
{
std::function<void(ir::AstNode *)> doNode = [&](ir::AstNode *nn) {
if (nn->IsTyped() && !(nn->IsExpression() && nn->AsExpression()->IsTypeNode()) &&
nn->AsTyped()->TsType() != nullptr && nn->AsTyped()->TsType()->IsTypeError()) {
nn->AsTyped()->SetTsType(nullptr);
}
if (nn->IsIdentifier() && nn->AsIdentifier()->TsType() != nullptr &&
nn->AsIdentifier()->TsType()->IsTypeError()) {
nn->AsIdentifier()->SetVariable(nullptr);
}
if (!nn->IsETSTypeReference()) {
nn->Iterate([&](ir::AstNode *child) { doNode(child); });
}
};
doNode(node);
}
static void ResetInferredTypeInArrowBody(ir::AstNode *body, ETSChecker *checker,
std::unordered_set<varbinder::Variable *> &inferredVarSet)
{
checker::ScopeContext scopeCtx(checker, body->Parent()->Scope());
std::function<void(ir::AstNode *)> doNode = [&](ir::AstNode *node) {
if (node->IsIdentifier()) {
auto *id = node->AsIdentifier();
if (inferredVarSet.count(id->Variable()) == 0U) {
return;
}
ir::AstNode *checkNode = id;
while (checkNode->Parent()->IsTyped() && checkNode->Parent()->AsTyped()->TsType() == nullptr &&
checkNode->Parent() != body) {
checkNode = checkNode->Parent();
}
checkNode->Check(checker);
}
if (node->IsVariableDeclarator()) {
auto *id = node->AsVariableDeclarator()->Id();
inferredVarSet.emplace(id->Variable());
node->Check(checker);
}
};
body->IterateRecursively(doNode);
}
static void ResetInferredNode(ETSChecker *checker, std::unordered_set<varbinder::Variable *> &inferredVarSet)
{
auto relation = checker->Relation();
auto resetFuncState = [](ir::ArrowFunctionExpression *expr) {
auto *func = expr->Function();
func->SetSignature(nullptr);
func->ClearReturnStatements();
expr->SetTsType(nullptr);
};
const bool hasValidNode = relation->GetNode() != nullptr && relation->GetNode()->IsArrowFunctionExpression();
if (!checker->HasStatus(CheckerStatus::IN_TYPE_INFER) || !hasValidNode) {
return;
}
auto *arrowFunc = relation->GetNode()->AsArrowFunctionExpression();
relation->SetNode(nullptr);
RemoveInvalidTypeMarkers(arrowFunc);
ResetInferredTypeInArrowBody(arrowFunc->Function()->Body(), checker, inferredVarSet);
resetFuncState(arrowFunc);
arrowFunc->Check(checker);
}
static bool EnhanceSubstitutionForNonNullish(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
ETSNonNullishType *paramType, Type *argumentType,
Substitution *substitution)
{
if (argumentType->IsETSNonNullishType()) {
ES2PANDA_ASSERT(argumentType->AsETSNonNullishType()->GetUnderlying() != nullptr);
return EnhanceSubstitutionForType(checker, typeParams, paramType->GetUnderlying(),
argumentType->AsETSNonNullishType()->GetUnderlying(), substitution);
}
return EnhanceSubstitutionForType(checker, typeParams, paramType->GetUnderlying(), argumentType, substitution);
}
static bool EnhanceSubstitutionTypeParameter(ETSChecker *checker, ETSTypeParameter *paramType, Type *argumentType,
Substitution *substitution)
{
auto *const originalTparam = paramType->GetOriginal();
if (argumentType->IsETSTypeParameter() && argumentType->AsETSTypeParameter()->GetOriginal() == originalTparam) {
return false;
}
if (!ETSChecker::IsReferenceType(argumentType)) {
checker->LogError(diagnostic::INFERENCE_TYPE_INCOMPAT, {paramType, argumentType},
paramType->GetDeclNode()->Start());
return false;
}
auto trialSubstitution = *substitution;
checker->EmplaceSubstituted(&trialSubstitution, originalTparam, argumentType);
if (!IsCompatibleTypeArgument(checker, paramType, argumentType, &trialSubstitution)) {
return false;
}
checker->EmplaceSubstituted(substitution, originalTparam, argumentType);
return true;
}
static bool EnhanceSubstitutionForFunction(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
ETSFunctionType *paramType, Type *argumentType, Substitution *substitution)
{
auto const enhance = [checker, typeParams, substitution](Type *ptype, Type *atype) {
return EnhanceSubstitutionForType(checker, typeParams, ptype, atype, substitution);
};
if (!argumentType->IsETSFunctionType()) {
return true;
}
auto *paramSig = paramType->ArrowSignature();
auto *argSig = argumentType->AsETSFunctionType()->ArrowSignature();
if (paramSig->MinArgCount() < argSig->MinArgCount()) {
return false;
}
bool res = true;
const size_t commonArity = std::min(argSig->ArgCount(), paramSig->ArgCount());
std::unordered_set<varbinder::Variable *> inferredVarSet;
for (size_t idx = 0; idx < commonArity; idx++) {
auto *declNode = argSig->Params()[idx]->Declaration()->Node();
if (ProcessUntypedParameter(checker, idx, paramSig, argSig, substitution)) {
inferredVarSet.emplace(declNode->AsETSParameterExpression()->Ident()->Variable());
continue;
}
res &= enhance(paramSig->Params()[idx]->TsType(), argSig->Params()[idx]->TsType());
}
ResetInferredNode(checker, inferredVarSet);
if (argSig->HasRestParameter() && paramSig->HasRestParameter()) {
res &= enhance(paramSig->RestVar()->TsType(), argSig->RestVar()->TsType());
}
res &= enhance(paramSig->ReturnType(), argSig->ReturnType());
return res;
}
static bool EnhanceSubstitutionForAwaited(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
ETSAwaitedType *paramType, Type *argumentType, Substitution *substitution)
{
auto *argumentAwaitedType =
argumentType->IsETSAwaitedType() ? argumentType->AsETSAwaitedType()->GetUnderlying() : argumentType;
auto *paramAwaitedType = paramType->GetUnderlying();
return EnhanceSubstitutionForType(checker, typeParams, paramAwaitedType, argumentAwaitedType, substitution);
}
static bool EnhanceSubstitutionForPartialTypeParam(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
ETSPartialTypeParameter *paramType, Type *argumentType,
Substitution *substitution)
{
if (!argumentType->IsETSObjectType() || !argumentType->AsETSObjectType()->IsPartial()) {
return false;
}
ES2PANDA_ASSERT(argumentType->AsETSObjectType()->GetBaseType() != nullptr);
return EnhanceSubstitutionForType(checker, typeParams, paramType->GetUnderlying(),
argumentType->AsETSObjectType()->GetBaseType(), substitution);
}
static ETSObjectType *FindEnhanceTargetInSupertypes(ETSObjectType *object, ETSObjectType *base)
{
ES2PANDA_ASSERT(base == base->GetOriginalBaseType());
if (object->GetConstOriginalBaseType() == base) {
return object;
}
auto const traverse = [base](ETSObjectType *v) { return FindEnhanceTargetInSupertypes(v, base); };
for (auto itf : object->Interfaces()) {
auto res = traverse(itf);
if (res != nullptr) {
return res;
}
}
if (object->SuperType() != nullptr) {
auto res = traverse(object->SuperType());
if (res != nullptr) {
return res;
}
}
return nullptr;
}
static bool EnhanceSubstitutionForObject(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
ETSObjectType *paramType, Type *argumentType, Substitution *substitution)
{
auto const enhance = [checker, typeParams, substitution](Type *ptype, Type *atype) {
return EnhanceSubstitutionForType(checker, typeParams, ptype, atype, substitution);
};
if (!argumentType->IsETSObjectType()) {
return true;
}
auto enhanceType = FindEnhanceTargetInSupertypes(argumentType->AsETSObjectType(), paramType->GetOriginalBaseType());
if (enhanceType == nullptr) {
return true;
}
ES2PANDA_ASSERT(enhanceType->GetOriginalBaseType() == paramType->GetOriginalBaseType());
bool res = true;
for (size_t i = 0; i < enhanceType->TypeArguments().size(); i++) {
res &= enhance(paramType->TypeArguments()[i], enhanceType->TypeArguments()[i]);
}
return res;
}
static bool EnhanceSubstitutionForArray(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
ETSArrayType *const paramType, Type *const argumentType,
Substitution *const substitution)
{
auto *const elementType =
argumentType->IsETSArrayType() ? argumentType->AsETSArrayType()->ElementType() : argumentType;
return EnhanceSubstitutionForType(checker, typeParams, paramType->ElementType(), elementType, substitution);
}
static bool EnhanceSubstitutionForResizableArray(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
ETSResizableArrayType *const paramType, Type *const argumentType,
Substitution *const substitution)
{
auto *const elementType =
argumentType->IsETSResizableArrayType() ? argumentType->AsETSResizableArrayType()->ElementType() : argumentType;
return EnhanceSubstitutionForType(checker, typeParams, paramType->ElementType(), elementType, substitution);
}
static bool EnhanceSubstitutionForReturnTypeUtilityType(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
const ETSReturnTypeUtilityType *paramType, Type *argumentType,
Substitution *substitution)
{
auto *argumentReturnTypeType = argumentType->IsETSReturnTypeUtilityType()
? argumentType->AsETSReturnTypeUtilityType()->GetUnderlying()
: static_cast<Type *>(checker->GlobalBuiltinFunctionType());
Type *paramReturnTypeType = paramType->IsETSReturnTypeUtilityType()
? paramType->GetUnderlying()
: static_cast<Type *>(checker->GlobalBuiltinFunctionType());
return EnhanceSubstitutionForType(checker, typeParams, paramReturnTypeType, argumentReturnTypeType, substitution);
}
static bool EnhanceSubstitutionForUtilityType(ETSChecker *checker, const ArenaVector<Type *> &typeParams,
Type *paramType, Type *argumentType, Substitution *substitution)
{
if (paramType->IsETSReadonlyType()) {
return EnhanceSubstitutionForReadonly(checker, typeParams, paramType->AsETSReadonlyType(), argumentType,
substitution);
}
if (paramType->IsETSPartialTypeParameter()) {
return EnhanceSubstitutionForPartialTypeParam(checker, typeParams, paramType->AsETSPartialTypeParameter(),
argumentType, substitution);
}
if (paramType->IsETSAwaitedType()) {
return EnhanceSubstitutionForAwaited(checker, typeParams, paramType->AsETSAwaitedType(), argumentType,
substitution);
}
if (paramType->IsETSReturnTypeUtilityType()) {
return EnhanceSubstitutionForReturnTypeUtilityType(checker, typeParams, paramType->AsETSReturnTypeUtilityType(),
argumentType, substitution);
}
if (paramType->IsETSNonNullishType()) {
return EnhanceSubstitutionForNonNullish(checker, typeParams, paramType->AsETSNonNullishType(), argumentType,
substitution);
}
return true;
}
static bool EnhanceSubstitutionForType(ETSChecker *checker, const ArenaVector<Type *> &typeParams, Type *paramType,
Type *argumentType, Substitution *substitution)
{
ES2PANDA_ASSERT(argumentType != nullptr);
if (paramType->IsETSTypeParameter()) {
auto *const originalTparam = paramType->AsETSTypeParameter()->GetOriginal();
if (std::find(typeParams.begin(), typeParams.end(), originalTparam) != typeParams.end() &&
substitution->count(originalTparam) == 0) {
return EnhanceSubstitutionTypeParameter(checker, paramType->AsETSTypeParameter(), argumentType,
substitution);
}
}
if (paramType->IsETSFunctionType()) {
return EnhanceSubstitutionForFunction(checker, typeParams, paramType->AsETSFunctionType(), argumentType,
substitution);
}
if (paramType->IsETSUnionType()) {
return EnhanceSubstitutionForUnion(checker, typeParams, paramType->AsETSUnionType(), argumentType,
substitution);
}
if (paramType->IsETSResizableArrayType()) {
return EnhanceSubstitutionForResizableArray(checker, typeParams, paramType->AsETSResizableArrayType(),
argumentType, substitution);
}
if (paramType->IsETSObjectType()) {
return EnhanceSubstitutionForObject(checker, typeParams, paramType->AsETSObjectType(), argumentType,
substitution);
}
if (paramType->IsETSArrayType()) {
return EnhanceSubstitutionForArray(checker, typeParams, paramType->AsETSArrayType(), argumentType,
substitution);
}
return EnhanceSubstitutionForUtilityType(checker, typeParams, paramType, argumentType, substitution);
}
bool ETSChecker::EnhanceSubstitutionForType(const ArenaVector<Type *> &typeParams, Type *paramType, Type *argumentType,
Substitution *substitution)
{
return checker::EnhanceSubstitutionForType(this, typeParams, paramType, argumentType, substitution);
}
static bool CheckLambdaAssignable(ETSChecker *checker, ir::Expression *expr, Type *paramType,
ir::ScriptFunction *lambda);
static bool CheckLambdaAssignableUnion(ETSChecker *checker, ir::Expression *expr, ETSUnionType *paramType,
ir::ScriptFunction *lambda)
{
for (auto *type : paramType->ConstituentTypes()) {
if (CheckLambdaAssignable(checker, expr, type, lambda)) {
return true;
}
}
return false;
}
static bool CheckLambdaTypeParameter(ETSChecker *checker, ir::ScriptFunction *lambda)
{
if (lambda->Params().empty()) {
return true;
}
for (auto param : lambda->Params()) {
if (param->IsETSParameterExpression() &&
param->AsETSParameterExpression()->Ident()->TypeAnnotation() == nullptr &&
param->AsETSParameterExpression()->Ident()->TsType()->IsTypeError()) {
checker->LogError(diagnostic::INFER_FAILURE_FUNC_PARAM,
{param->AsETSParameterExpression()->Ident()->Name()}, param->Start());
return false;
}
}
return true;
}
static bool CheckLambdaAssignable(ETSChecker *checker, ir::Expression *expr, Type *paramType,
ir::ScriptFunction *lambda)
{
if (paramType->IsETSTypeAliasType()) {
paramType = paramType->AsETSTypeAliasType()->GetTargetType();
}
if (!paramType->IsETSArrowType()) {
if (paramType->IsETSUnionType()) {
return CheckLambdaAssignableUnion(checker, expr, paramType->AsETSUnionType(), lambda);
}
if (checker->Relation()->IsSupertypeOf(paramType, checker->GlobalBuiltinFunctionType())) {
ES2PANDA_ASSERT(lambda->Parent()->IsArrowFunctionExpression());
lambda->Parent()->AsArrowFunctionExpression()->SetPreferredType(nullptr);
lambda->Parent()->Check(checker);
return true;
}
return false;
}
ir::AstNode *typeAnn = expr->AsETSParameterExpression()->Ident()->TypeAnnotation();
if (typeAnn == nullptr) {
return false;
}
if (typeAnn->IsETSTypeReference() && !typeAnn->AsETSTypeReference()->TsType()->IsETSArrayType()) {
typeAnn = util::Helpers::DerefETSTypeReference(typeAnn);
}
if (typeAnn->IsTSTypeParameter() && !CheckLambdaTypeParameter(checker, lambda)) {
return false;
}
auto *calleeType = paramType->AsETSFunctionType();
const size_t restVarCount = calleeType->ArrowSignature()->RestVar() != nullptr ? 1 : 0;
return lambda->Params().size() <= calleeType->ArrowSignature()->Params().size() + restVarCount;
}
static bool CheckOptionalLambdaFunction(ETSChecker *checker, ir::Expression *argument, Signature *substitutedSig,
size_t index)
{
auto param = substitutedSig->Params()[index]->Declaration()->Node()->AsETSParameterExpression();
if (argument->IsArrowFunctionExpression()) {
auto *const arrowFuncExpr = argument->AsArrowFunctionExpression();
ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
return CheckLambdaAssignable(checker, param, substitutedSig->Params()[index]->TsType(), lambda);
}
return true;
}
static bool IsInvalidArgumentAsIdentifier(varbinder::Scope *scope, const ir::Identifier *identifier)
{
auto result = scope->Find(identifier->Name());
return result.variable != nullptr &&
(result.variable->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE_OR_ENUM |
varbinder::VariableFlags::TYPE_ALIAS));
}
static bool CheckArrowFunctionParamIfNeeded(ETSChecker *checker, Signature *substitutedSig,
const ArenaVector<ir::Expression *> &arguments, TypeRelationFlag flags)
{
if ((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0 && arguments.back()->IsArrowFunctionExpression()) {
ir::ScriptFunction *const lambda = arguments.back()->AsArrowFunctionExpression()->Function();
auto targetParm = substitutedSig->GetSignatureInfo()->params.back()->Declaration()->Node();
auto targetParmType = substitutedSig->GetSignatureInfo()->params.back()->TsType();
if (!CheckLambdaAssignable(checker, targetParm->AsETSParameterExpression(), targetParmType, lambda)) {
return false;
}
}
return true;
}
static bool HasTransferredTrailingLambda(const ArenaVector<ir::Expression *> &arguments)
{
return !arguments.empty() && arguments.back()->IsArrowFunctionExpression() &&
arguments.back()->AsArrowFunctionExpression()->Function()->IsTrailingLambda();
}
bool ValidateRestParameter(ETSChecker *checker, Signature *signature, const ArenaVector<ir::Expression *> &arguments,
const lexer::SourcePosition &pos, TypeRelationFlag flags)
{
size_t const argCount = arguments.size();
size_t compareCount = argCount;
auto const hasRestParameter = signature->HasRestParameter();
if ((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0 && !signature->Params().empty() &&
signature->Params().back()->Declaration()->Node()->AsETSParameterExpression()->IsOptional()) {
compareCount = compareCount - 1;
}
if (!hasRestParameter && argCount > 0 && arguments[argCount - 1]->IsSpreadElement()) {
checker->LogError(diagnostic::ERROR_ARKTS_SPREAD_ONLY_WITH_REST, {}, pos);
return false;
}
if (compareCount < signature->MinArgCount() || (argCount > signature->ArgCount() && !hasRestParameter)) {
checker->LogError(diagnostic::PARAM_COUNT_MISMATCH, {signature->MinArgCount(), argCount}, pos);
return false;
}
if (hasRestParameter &&
(((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0) || HasTransferredTrailingLambda(arguments))) {
return false;
}
return !(argCount > signature->ArgCount() && hasRestParameter &&
(flags & TypeRelationFlag::IGNORE_REST_PARAM) != 0);
}
static void InferTypeForNumberLiteral(ETSChecker *checker, ir::NumberLiteral *argumentLiteral, Type *paramType)
{
if (argumentLiteral->IsFolded() || argumentLiteral->IsNarrowingBlocked()) {
return;
}
argumentLiteral->SetTsType(nullptr);
argumentLiteral->SetPreferredType(paramType);
auto &number = argumentLiteral->AsNumberLiteral()->Number();
auto *typeRel = checker->Relation();
if (typeRel->IsSupertypeOf(checker->GlobalLongBuiltinType(), paramType)) {
number.TryNarrowTo<int64_t>();
} else if (typeRel->IsSupertypeOf(checker->GlobalIntBuiltinType(), paramType)) {
number.TryNarrowTo<int32_t>();
} else if (typeRel->IsSupertypeOf(checker->GlobalShortBuiltinType(), paramType)) {
number.TryNarrowTo<int16_t>();
} else if (typeRel->IsSupertypeOf(checker->GlobalByteBuiltinType(), paramType)) {
number.TryNarrowTo<int8_t>();
}
}
static bool SetPreferredTypeForArrayArgument(ETSChecker *checker, ir::ArrayExpression *arrayExpr,
Signature *substitutedSig);
static bool IsValidRestArgument(ETSChecker *checker, ir::Expression *const argument, Signature *const substitutedSig,
const TypeRelationFlag flags, const std::size_t index)
{
auto *restParamType = substitutedSig->RestVar()->TsType();
if (restParamType->IsETSTupleType()) {
return false;
}
if (argument->IsObjectExpression()) {
argument->SetPreferredType(checker->GetElementTypeOfArray(restParamType));
return true;
}
if (argument->IsArrayExpression()) {
if (!SetPreferredTypeForArrayArgument(checker, argument->AsArrayExpression(), substitutedSig)) {
return false;
}
}
const auto argumentType = argument->Check(checker);
auto targetType = checker->GetElementTypeOfArray(restParamType);
auto const invocationCtx = checker::InvocationContext(
checker->Relation(), argument, argumentType, targetType, argument->Start(),
{{diagnostic::REST_PARAM_INCOMPAT_AT, {argumentType, targetType, index + 1}}}, flags);
bool result = invocationCtx.IsInvocable();
if (!result && argument->IsArrayExpression()) {
checker->ModifyPreferredType(argument->AsArrayExpression(), nullptr);
}
return result;
}
static bool SetPreferredTypeForArrayArgument(ETSChecker *checker, ir::ArrayExpression *arrayExpr,
Signature *substitutedSig)
{
auto *const restVarType = substitutedSig->RestVar()->TsType();
if (!restVarType->IsETSArrayType() && !restVarType->IsETSResizableArrayType()) {
return true;
}
auto targetType = checker->GetElementTypeOfArray(restVarType);
if (targetType->IsETSTupleType()) {
auto *tupleType = targetType->AsETSTupleType();
if (tupleType->GetTupleSize() != arrayExpr->Elements().size()) {
return false;
}
}
arrayExpr->SetPreferredType(targetType);
return true;
}
static void PrepareComparisonTypes(ETSChecker *checker, Type **argumentType, Type **targetType)
{
if (!util::Helpers::IsArrayType(*argumentType) && !(*argumentType)->IsETSTupleType()) {
*argumentType = checker->GetElementTypeOfSpreadType(*argumentType);
if (util::Helpers::IsArrayType(*targetType)) {
*targetType = checker->GetElementTypeOfArray(*targetType);
}
}
while (util::Helpers::IsArrayType(*targetType) &&
(util::Helpers::IsArrayType(*argumentType) || (*argumentType)->IsETSTupleType())) {
if ((*argumentType)->IsETSTupleType()) {
*argumentType =
util::Helpers::CreateUnionOfTupleConstituentTypes(checker, (*argumentType)->AsETSTupleType());
} else {
*argumentType = checker->GetElementTypeOfArray(*argumentType);
}
*targetType = checker->GetElementTypeOfArray(*targetType);
}
}
static bool ValidateSpreadRestArgument(ETSChecker *checker, ir::Expression *argument, Signature *substitutedSig,
TypeRelationFlag flags, [[maybe_unused]] size_t index)
{
auto *const restArgument = argument->AsSpreadElement()->Argument();
Type *targetType = substitutedSig->RestVar()->TsType();
argument->SetPreferredType(targetType);
Type *argumentType = argument->Check(checker);
PrepareComparisonTypes(checker, &argumentType, &targetType);
auto const invocationCtx = checker::InvocationContext(checker->Relation(), restArgument, argumentType, targetType,
argument->Start(), {}, flags);
if (!invocationCtx.IsInvocable()) {
if (restArgument->IsArrayExpression()) {
checker->ModifyPreferredType(restArgument->AsArrayExpression(), nullptr);
argument->SetTsType(nullptr);
}
return false;
}
return true;
}
static bool SetPreferredTypeBeforeValidate(ETSChecker *checker, ir::Expression *argument, Type *paramType,
TypeRelationFlag flags, bool isRestParam = false);
static bool ValidateSignatureRestParams(ETSChecker *checker, Signature *substitutedSig,
const ArenaVector<ir::Expression *> &arguments, TypeRelationFlag flags)
{
size_t const argumentCount = arguments.size();
auto const commonArity = std::min(substitutedSig->ArgCount(), argumentCount);
auto restParamType = substitutedSig->RestVar()->TsType();
if (argumentCount == commonArity && restParamType->IsETSTupleType()) {
return false;
}
for (size_t index = commonArity; index < argumentCount; ++index) {
auto &argument = arguments[index];
auto preferredType =
restParamType->IsETSTupleType() ? restParamType : checker->GetElementTypeOfArray(restParamType);
if (!SetPreferredTypeBeforeValidate(checker, argument, preferredType, flags, true)) {
return false;
}
if (!argument->IsSpreadElement()) {
if (!IsValidRestArgument(checker, argument, substitutedSig, flags, index)) {
return false;
}
continue;
}
if (!ValidateSpreadRestArgument(checker, argument, substitutedSig, flags, index)) {
return false;
}
}
return true;
}
bool IsSignatureAccessible(Signature *sig, ETSObjectType *containingClass, TypeRelation *relation)
{
if (!sig->HasSignatureFlag(SignatureFlags::PUBLIC | SignatureFlags::PROTECTED | SignatureFlags::PRIVATE)) {
return true;
}
if (sig->HasSignatureFlag(SignatureFlags::PUBLIC) || sig->Owner() == containingClass ||
(sig->HasSignatureFlag(SignatureFlags::PROTECTED) && relation->IsSupertypeOf(sig->Owner(), containingClass))) {
return true;
}
return false;
}
static std::vector<bool> FindTypeInferenceArguments(const ArenaVector<ir::Expression *> &arguments)
{
std::vector<bool> argTypeInferenceRequired(arguments.size());
size_t index = 0;
auto isNeedInterLambda = [](ir::AstNode *arg) {
if (arg->IsArrowFunctionExpression()) {
ir::ScriptFunction *const lambda = arg->AsArrowFunctionExpression()->Function();
return ETSChecker::NeedTypeInference(lambda);
}
return false;
};
for (ir::Expression *arg : arguments) {
if (arg->IsArrayExpression()) {
if (arg->IsAnyChild(isNeedInterLambda)) {
argTypeInferenceRequired[index] = true;
}
} else {
argTypeInferenceRequired[index] = isNeedInterLambda(arg);
}
++index;
}
return argTypeInferenceRequired;
}
static bool CheckLambdaInfer(ETSChecker *checker, ir::AstNode *typeAnnotation,
ir::ArrowFunctionExpression *const arrowFuncExpr, Type *const subParameterType)
{
if (typeAnnotation->IsETSTypeReference()) {
typeAnnotation = util::Helpers::DerefETSTypeReference(typeAnnotation);
}
if (typeAnnotation->IsTSTypeParameter()) {
return true;
}
if (!typeAnnotation->IsETSFunctionType()) {
return false;
}
ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
auto calleeType = typeAnnotation->AsETSFunctionType();
checker->InferTypesForLambda(lambda, calleeType, subParameterType->AsETSFunctionType()->ArrowSignature());
return true;
}
static bool CheckLambdaTypeAnnotation(ETSChecker *checker, ir::ETSParameterExpression *param,
ir::ArrowFunctionExpression *const arrowFuncExpr, Type *const parameterType,
TypeRelationFlag flags)
{
ir::AstNode *typeAnnotation = param->Ident()->TypeAnnotation();
if (typeAnnotation->IsETSTypeReference()) {
typeAnnotation = util::Helpers::DerefETSTypeReference(typeAnnotation);
}
auto checkInvocable = [&arrowFuncExpr, ¶meterType, checker](TypeRelationFlag functionFlags) {
arrowFuncExpr->SetPreferredType(parameterType);
Type *const argumentType = arrowFuncExpr->Check(checker);
checker::InvocationContext invocationCtx(checker->Relation(), arrowFuncExpr, argumentType, parameterType,
arrowFuncExpr->Start(), std::nullopt, functionFlags);
return invocationCtx.IsInvocable();
};
if (!typeAnnotation->IsETSUnionType()) {
auto nonNullishParam = param->IsOptional() ? checker->GetNonNullishType(parameterType) : parameterType;
ES2PANDA_ASSERT(nonNullishParam != nullptr);
if (!nonNullishParam->IsETSFunctionType()) {
arrowFuncExpr->Check(checker);
return true;
}
return CheckLambdaInfer(checker, typeAnnotation, arrowFuncExpr, nonNullishParam) && checkInvocable(flags);
}
ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
std::vector<ir::TypeNode *> lambdaParamTypes {};
for (auto *const lambdaParam : lambda->Params()) {
lambdaParamTypes.emplace_back(lambdaParam->AsETSParameterExpression()->Ident()->TypeAnnotation());
}
auto *const lambdaReturnTypeAnnotation = lambda->ReturnTypeAnnotation();
if (!parameterType->IsETSUnionType() || parameterType->AsETSUnionType()->ConstituentTypes().size() !=
typeAnnotation->AsETSUnionType()->Types().size()) {
Type *const argumentType = arrowFuncExpr->Check(checker);
return checker->Relation()->IsSupertypeOf(parameterType, argumentType);
}
const auto typeAnnsOfUnion = typeAnnotation->AsETSUnionType()->Types();
const auto typeParamOfUnion = parameterType->AsETSUnionType()->ConstituentTypes();
for (size_t ix = 0; ix < typeAnnsOfUnion.size(); ++ix) {
auto *typeNode = typeAnnsOfUnion[ix];
auto *paramNode = typeParamOfUnion[ix];
if (CheckLambdaInfer(checker, typeNode, arrowFuncExpr, paramNode) && checkInvocable(flags)) {
return true;
}
for (std::size_t i = 0U; i < lambda->Params().size(); ++i) {
if (lambdaParamTypes[i] == nullptr) {
lambda->Params()[i]->AsETSParameterExpression()->Ident()->SetTsTypeAnnotation(nullptr);
}
}
if (lambdaReturnTypeAnnotation == nullptr) {
lambda->SetReturnTypeAnnotation(nullptr);
}
}
return false;
}
static bool ResolveLambdaArgumentType(ETSChecker *checker, Signature *signature, size_t paramPosition,
std::pair<ir::Expression *, size_t> argumentInfo,
TypeRelationFlag resolutionFlags)
{
auto [argument, argumentPosition] = argumentInfo;
if (!argument->IsArrowFunctionExpression()) {
return true;
}
auto arrowFuncExpr = argument->AsArrowFunctionExpression();
bool typeValid = true;
ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
if (!checker->NeedTypeInference(lambda) && !lambda->IsTrailingLambda()) {
return typeValid;
}
arrowFuncExpr->SetTsType(nullptr);
arrowFuncExpr->SetPreferredType(nullptr);
auto *const param =
signature->GetSignatureInfo()->params[paramPosition]->Declaration()->Node()->AsETSParameterExpression();
Type *const parameterType = signature->Params()[paramPosition]->TsType();
bool rc = CheckLambdaTypeAnnotation(checker, param, arrowFuncExpr, parameterType, resolutionFlags);
if (!rc) {
Type *const argumentType = arrowFuncExpr->Check(checker);
checker->LogError(diagnostic::TYPE_MISMATCH_AT_IDX, {argumentType, parameterType, argumentPosition + 1},
arrowFuncExpr->Start());
rc = false;
} else if ((lambda->Signature() != nullptr) && !lambda->HasReturnStatement() && !lambda->HasThrowStatement()) {
if (!AssignmentContext(
checker->Relation(), checker->ProgramAllocNode<ir::Identifier>(checker->ProgramAllocator()),
checker->GlobalETSUndefinedType(), lambda->Signature()->ReturnType(), lambda->Start(), std::nullopt,
checker::TypeRelationFlag::DIRECT_RETURN)
.IsAssignable()) {
checker->LogError(diagnostic::ARROW_TYPE_MISMATCH,
{checker->GlobalETSUndefinedType(), lambda->Signature()->ReturnType()},
lambda->Body()->Start());
rc = false;
}
}
typeValid &= rc;
return typeValid;
}
static bool TypeInference(ETSChecker *checker, Signature *signature, const ArenaVector<ir::Expression *> &arguments,
TypeRelationFlag inferenceFlags)
{
bool typeConsistent = true;
auto const argumentCount = arguments.size();
auto const minArity = std::min(signature->ArgCount(), argumentCount);
for (size_t idx = 0U; idx < minArity; ++idx) {
auto const &argument = arguments[idx];
if (idx == argumentCount - 1 && (inferenceFlags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0) {
continue;
}
const bool valid = ResolveLambdaArgumentType(checker, signature, idx, {argument, idx}, inferenceFlags);
typeConsistent &= valid;
}
return typeConsistent;
}
void ETSChecker::LogSignatureMismatch(ArenaVector<Signature *> const &signatures,
const ArenaVector<ir::Expression *> &arguments, const lexer::SourcePosition &pos,
std::string_view signatureKind)
{
if (!arguments.empty() && !signatures.empty()) {
std::string msg {};
auto someSignature = signatures[0];
if (someSignature->HasFunction()) {
if (someSignature->Function()->IsConstructor()) {
msg += util::Helpers::GetClassDefinition(someSignature->Function())->InternalName().Utf8();
} else {
msg += someSignature->Function()->Id()->Name().Utf8();
}
}
msg += '(';
for (std::size_t index = 0U; index < arguments.size(); ++index) {
auto const &argument = arguments[index];
Type const *const argumentType = argument->Check(this);
if (argumentType != nullptr && !ContainsTypeError(argumentType)) {
msg += argumentType->ToString();
} else {
msg += argument->ToString();
}
if (index == arguments.size() - 1U) {
msg += ')';
LogError(diagnostic::NO_MATCHING_SIG, {signatureKind, msg.c_str()}, pos);
return;
}
msg += ", ";
}
}
LogError(diagnostic::NO_MATCHING_SIG_2, {signatureKind}, pos);
}
static bool IsLastParameterLambdaWithReceiver(Signature const *sig)
{
auto const ¶ms = sig->Function()->Params();
return !params.empty() && (params.back()->AsETSParameterExpression()->TypeAnnotation() != nullptr) &&
params.back()->AsETSParameterExpression()->TypeAnnotation()->IsETSFunctionType() &&
params.back()->AsETSParameterExpression()->TypeAnnotation()->AsETSFunctionType()->IsExtensionFunction();
}
static bool TrailingLambdaTypeInference(ETSChecker *checker, Signature *signature,
const ArenaVector<ir::Expression *> &arguments)
{
if (arguments.empty() || signature->GetSignatureInfo()->params.empty()) {
return false;
}
ES2PANDA_ASSERT(arguments.back()->IsArrowFunctionExpression());
const size_t lastParamPos = signature->GetSignatureInfo()->params.size() - 1;
return ResolveLambdaArgumentType(checker, signature, lastParamPos, {arguments.back(), arguments.size() - 1},
TypeRelationFlag::NONE);
}
static ArenaVector<ir::Expression *> ExtendArgumentsWithFakeLamda(ETSChecker *checker, ir::CallExpression *callExpr,
const ArenaVector<ir::Expression *> &arguments);
static void TransformTraillingLambda(ETSChecker *checker, ir::CallExpression *callExpr, Signature *sig,
const ArenaVector<ir::Expression *> &arguments);
static void EnsureValidCurlyBrace(ETSChecker *checker, ir::CallExpression *callExpr);
Signature *ETSChecker::ResolveConstructExpression(ETSObjectType *type, ir::ETSNewClassInstanceExpression *expr)
{
const ArenaVector<ir::Expression *> &arguments = expr->GetArguments();
auto *var = type->GetProperty(compiler::Signatures::CONSTRUCTOR_NAME, PropertySearchFlags::SEARCH_STATIC_METHOD);
Signature *sig = nullptr;
if (var != nullptr && var->TsType()->IsETSFunctionType()) {
sig = MatchOrderSignatures(var->TsType()->AsETSFunctionType()->CallSignatures(), arguments, expr,
TypeRelationFlag::NONE, "construct");
} else {
sig = MatchOrderSignatures(type->ConstructSignatures(), arguments, expr, TypeRelationFlag::NONE, "construct");
}
return sig;
}
static bool CollectOverload(checker::ETSChecker *checker, ir::MethodDefinition *method, ETSFunctionType *funcType)
{
std::vector<ETSFunctionType *> overloads {};
for (ir::MethodDefinition *const currentFunc : method->Overloads()) {
currentFunc->Function()->Id()->SetVariable(currentFunc->Id()->Variable());
checker->BuildFunctionSignature(currentFunc->Function(), method->IsConstructor());
if (currentFunc->Function()->Signature() == nullptr) {
auto *methodId = method->Id();
ES2PANDA_ASSERT(methodId != nullptr);
methodId->Variable()->SetTsType(checker->GlobalTypeError());
return false;
}
auto *const overloadType = checker->BuildMethodType(currentFunc->Function());
checker->CheckIdenticalOverloads(funcType, overloadType, currentFunc);
if (currentFunc->TsType() == nullptr) {
currentFunc->SetTsType(overloadType);
}
auto overloadSig = currentFunc->Function()->Signature();
funcType->AddCallSignature(overloadSig);
overloads.push_back(overloadType);
}
return true;
}
checker::Type *ETSChecker::BuildMethodSignature(ir::MethodDefinition *method)
{
if (method->TsType() != nullptr) {
return method->TsType();
}
auto *methodId = method->Id();
method->Function()->Id()->SetVariable(methodId->Variable());
BuildFunctionSignature(method->Function(), method->IsConstructor());
if (method->Function()->Signature() == nullptr) {
return methodId->Variable()->SetTsType(GlobalTypeError());
}
auto *funcType = BuildMethodType(method->Function());
if (!CollectOverload(this, method, funcType)) {
return GlobalTypeError();
}
return methodId->Variable()->SetTsType(funcType);
}
static bool CheckRestParamOverload(Signature *funcSig, Signature *overloadSig, TypeRelation *relation)
{
if (std::abs(static_cast<int32_t>(funcSig->ArgCount() - overloadSig->ArgCount())) != 1) {
return true;
}
if (!relation->NoReturnTypeCheck() && !relation->IsIdenticalTo(funcSig->ReturnType(), overloadSig->ReturnType())) {
return true;
}
for (size_t idx = 0; idx < std::min(funcSig->ArgCount(), overloadSig->ArgCount()); ++idx) {
if (!relation->IsIdenticalTo(funcSig->Params()[idx]->TsType(), overloadSig->Params()[idx]->TsType())) {
return true;
}
}
auto isLastParamIdentical = [relation](Signature *withRest, Signature *withArray) {
if (withArray->Params().empty()) {
return false;
}
auto *lastParamType = withArray->Params().back()->TsType();
bool isArray = lastParamType->IsETSArrayType() || lastParamType->IsETSResizableArrayType();
return isArray && relation->IsIdenticalTo(lastParamType, withRest->RestVar()->TsType());
};
if ((funcSig->HasRestParameter() && isLastParamIdentical(funcSig, overloadSig)) ||
(overloadSig->HasRestParameter() && isLastParamIdentical(overloadSig, funcSig))) {
return false;
}
return true;
}
bool ETSChecker::CheckIdenticalOverloads(ETSFunctionType *func, ETSFunctionType *overload,
const ir::MethodDefinition *const currentFunc, bool omitSameAsm,
TypeRelationFlag relationFlags)
{
if (func->Name().Is(ERROR_LITERAL)) {
ES2PANDA_ASSERT(IsAnyError());
return false;
}
SavedTypeRelationFlagsContext savedFlagsCtx(Relation(), relationFlags);
auto *funcSig = func->CallSignatures()[0];
auto *overloadSig = overload->CallSignatures()[0];
Relation()->SignatureIsIdenticalTo(funcSig, overloadSig);
if (Relation()->IsTrue() && funcSig->GetSignatureInfo()->restVar == overloadSig->GetSignatureInfo()->restVar) {
LogError(diagnostic::FUNCTION_REDECL_BY_TYPE_SIG, {overload->Name().Utf8()}, currentFunc->Start());
return false;
}
if (funcSig->HasRestParameter() != overloadSig->HasRestParameter() &&
!CheckRestParamOverload(funcSig, overloadSig, Relation())) {
LogError(diagnostic::FUNCTION_REDECL_BY_TYPE_SIG, {overload->Name().Utf8()}, currentFunc->Start());
return false;
}
bool const returnTypeCheck = (relationFlags & TypeRelationFlag::NO_RETURN_TYPE_CHECK) ==
static_cast<std::underlying_type_t<TypeRelationFlag>>(0U);
if (!HasSameAssemblySignatures(func, overload, returnTypeCheck)) {
return false;
}
if (!omitSameAsm) {
LogError(diagnostic::FUNCTION_REDECL_BY_ASM_SIG, {func->Name().Utf8()}, currentFunc->Start());
return false;
}
return true;
}
Signature *ETSChecker::ComposeSignature(ir::ScriptFunction *func, SignatureInfo *signatureInfo, Type *returnType,
varbinder::Variable *nameVar)
{
auto *signature = CreateSignature(signatureInfo, returnType, func);
if (signature == nullptr) {
ES2PANDA_ASSERT(IsAnyError());
return nullptr;
}
signature->SetOwner(Context().ContainingClass());
signature->SetOwnerVar(nameVar);
const auto *returnTypeAnnotation = func->ReturnTypeAnnotation();
if (returnTypeAnnotation == nullptr && func->HasReturnStatement()) {
signature->AddSignatureFlag(SignatureFlags::NEED_RETURN_TYPE);
}
if (returnTypeAnnotation != nullptr && returnTypeAnnotation->IsTSThisType()) {
signature->AddSignatureFlag(SignatureFlags::THIS_RETURN_TYPE);
}
if (signature->Owner() != nullptr && signature->Owner()->GetDeclNode()->IsFinal()) {
signature->AddSignatureFlag(SignatureFlags::FINAL);
}
return signature;
}
Type *ETSChecker::ComposeReturnType(ir::TypeNode *typeAnnotation, bool isAsync)
{
if (typeAnnotation != nullptr) {
return GetTypeFromTypeAnnotation(typeAnnotation);
}
return isAsync ? CreatePromiseOf(GlobalETSUndefinedType()) : GlobalETSUndefinedType();
}
static varbinder::LocalVariable *SetupSignatureParameter(ir::ETSParameterExpression *param, Type *type)
{
auto *const variable = param->Ident()->Variable();
if (variable == nullptr) {
return nullptr;
}
param->Ident()->SetTsType(type);
variable->SetTsType(type);
return variable->AsLocalVariable();
}
static bool AppendSignatureInfoParam(ETSChecker *checker, SignatureInfo *sigInfo, ir::ETSParameterExpression *param)
{
auto variable = SetupSignatureParameter(param, [checker, param]() {
if (param->TypeAnnotation() != nullptr) {
auto type = checker->GetTypeFromTypeAnnotation(param->TypeAnnotation());
return param->IsOptional() ? checker->CreateETSUnionType({type, checker->GlobalETSUndefinedType()}) : type;
}
if (param->Ident()->TsType() != nullptr) {
return param->Ident()->TsType();
}
if (!param->Ident()->IsErrorPlaceHolder() && !checker->HasStatus(checker::CheckerStatus::IN_TYPE_INFER)) {
checker->LogError(diagnostic::INFER_FAILURE_FUNC_PARAM, {param->Ident()->Name()}, param->Start());
}
return checker->GlobalTypeError();
}());
if (variable == nullptr) {
return false;
}
if (param->IsRestParameter()) {
return true;
}
sigInfo->params.push_back(variable);
if (!param->IsOptional()) {
++sigInfo->minArgCount;
}
ERROR_SANITY_CHECK(
checker,
!param->IsOptional() || param->Ident()->TsType()->IsTypeError() ||
checker->Relation()->IsSupertypeOf(param->Ident()->TsType(), checker->GlobalETSUndefinedType()),
return false);
return true;
}
static void InitializeSignatureTypeParameters(ETSChecker *checker, SignatureInfo *signatureInfo,
ir::TSTypeParameterDeclaration *typeParams)
{
if (typeParams == nullptr) {
return;
}
auto [typeParamTypes, ok] = checker->CreateUnconstrainedTypeParameters(typeParams);
ES2PANDA_ASSERT(signatureInfo != nullptr);
signatureInfo->typeParams = std::move(typeParamTypes);
if (ok) {
ok = checker->PrecheckTypeParameterConstraintCycles(typeParams);
}
if (ok) {
checker->AssignTypeParameterConstraints(typeParams);
}
}
static bool AppendSignatureInfoParams(ETSChecker *checker, SignatureInfo *signatureInfo,
ArenaVector<ir::Expression *> const ¶ms)
{
for (auto *const paramExpr : params) {
if (!paramExpr->IsETSParameterExpression()) {
ES2PANDA_ASSERT(checker->IsAnyError());
return false;
}
auto *const param = paramExpr->AsETSParameterExpression();
checker->CheckAnnotations(param);
if (!AppendSignatureInfoParam(checker, signatureInfo, param)) {
ES2PANDA_ASSERT(checker->IsAnyError());
return false;
}
}
return true;
}
static Type *ResolveRestParameterType(ETSChecker *checker, ir::ETSParameterExpression *param)
{
Type *paramType = nullptr;
if (param->TypeAnnotation() != nullptr) {
paramType = checker->GetTypeFromTypeAnnotation(param->RestParameter()->TypeAnnotation());
} else if (param->Ident()->TsType() != nullptr) {
paramType = param->Ident()->TsType();
}
if (paramType == nullptr) {
ES2PANDA_ASSERT(checker->IsAnyError());
} else if (paramType->IsETSTypeParameter()) {
paramType = paramType->AsETSTypeParameter()->GetConstraintType();
}
return paramType;
}
static bool FinalizeRestParameter(ETSChecker *checker, SignatureInfo *signatureInfo,
ArenaVector<ir::Expression *> const ¶ms)
{
auto *const lastParam = params.empty() ? nullptr : params.back()->AsETSParameterExpression();
if (lastParam == nullptr || !lastParam->IsRestParameter()) {
return true;
}
Type *restParamType = ResolveRestParameterType(checker, lastParam);
if (restParamType == nullptr) {
return false;
}
ES2PANDA_ASSERT(restParamType != nullptr);
if (!restParamType->IsAnyETSArrayOrTupleType()) {
checker->LogError(diagnostic::ONLY_ARRAY_OR_TUPLE_FOR_REST, {}, lastParam->Start());
restParamType = checker->GlobalTypeError();
}
signatureInfo->restVar = SetupSignatureParameter(lastParam, restParamType);
ES2PANDA_ASSERT(signatureInfo->restVar != nullptr);
size_t nOpt =
std::count_if(signatureInfo->params.begin(), signatureInfo->params.end(), [](varbinder::LocalVariable *var) {
return var->Declaration()->Node()->AsETSParameterExpression()->IsOptional();
});
if (signatureInfo->restVar->TsType()->IsETSTupleType()) {
signatureInfo->minArgCount += nOpt;
}
return true;
}
SignatureInfo *ETSChecker::ComposeSignatureInfo(ir::TSTypeParameterDeclaration *typeParams,
ArenaVector<ir::Expression *> const ¶ms)
{
auto *const signatureInfo = CreateSignatureInfo();
InitializeSignatureTypeParameters(this, signatureInfo, typeParams);
if (!AppendSignatureInfoParams(this, signatureInfo, params)) {
return nullptr;
}
if (!FinalizeRestParameter(this, signatureInfo, params)) {
return nullptr;
}
return signatureInfo;
}
static void ValidateMainSignature(ETSChecker *checker, ir::ScriptFunction *func)
{
if (func->Params().size() >= 2U) {
checker->LogError(diagnostic::MAIN_INVALID_ARG_COUNT, {}, func->Start());
return;
}
if (func->Params().size() == 1) {
auto const *const param = func->Params()[0]->AsETSParameterExpression();
if (param->IsRestParameter()) {
checker->LogError(diagnostic::MAIN_WITH_REST, {}, param->Start());
}
const auto paramType = param->Variable()->TsType();
if (!paramType->IsETSArrayType() || !paramType->AsETSArrayType()->ElementType()->IsETSStringType()) {
checker->LogError(diagnostic::MAIN_PARAM_NOT_ARR_OF_STRING, {}, param->Start());
}
}
}
void ETSChecker::BuildFunctionSignature(ir::ScriptFunction *func, bool isConstructSig)
{
ES2PANDA_ASSERT(func != nullptr);
bool isArrow = func->IsArrow();
if (func->Signature() != nullptr && !isArrow) {
return;
}
auto *nameVar = isArrow ? nullptr : func->Id()->Variable();
auto funcName = nameVar == nullptr ? util::StringView() : nameVar->Name();
if (func->IsConstructor() && func->IsStatic()) {
LogError(diagnostic::INVALID_DECORATOR_CONSTRUCTOR, {}, func->Start());
return;
}
if ((func->IsConstructor() || !func->IsStatic()) && !func->IsArrow()) {
if (!func->Scope()->ParamScope()->Params().empty()) {
func->Scope()->ParamScope()->Params().front()->SetTsType(Context().ContainingClass());
}
}
auto *signatureInfo = ComposeSignatureInfo(func->TypeParams(), func->Params());
Type *returnType = func->GetPreferredReturnType() != nullptr
? func->GetPreferredReturnType()
: ComposeReturnType(func->ReturnTypeAnnotation(), func->IsAsyncFunc());
auto *signature = ComposeSignature(func, signatureInfo, returnType, nameVar);
if (signature == nullptr) {
ES2PANDA_ASSERT(IsAnyError());
return;
}
func->SetSignature(signature);
if (isConstructSig) {
signature->AddSignatureFlag(SignatureFlags::CONSTRUCT);
} else {
signature->AddSignatureFlag(SignatureFlags::CALL);
}
if (funcName.Is(compiler::Signatures::MAIN) &&
func->Scope()->Name().Utf8().find(compiler::Signatures::ETS_GLOBAL) != std::string::npos) {
func->AddFlag(ir::ScriptFunctionFlags::ENTRY_POINT);
}
if (func->IsEntryPoint()) {
ValidateMainSignature(this, func);
}
VarBinder()->AsETSBinder()->BuildFunctionName(func);
}
checker::ETSFunctionType *ETSChecker::BuildMethodType(ir::ScriptFunction *func)
{
ES2PANDA_ASSERT(!func->IsArrow());
ES2PANDA_ASSERT(func != nullptr);
auto *ident = func->Id();
ETSFunctionType *funcType =
CreateETSMethodType(ident->Name(), {{func->Signature()}, ProgramAllocator()->Adapter()});
funcType->SetVariable(ident->Variable());
return funcType;
}
static bool IsOverridableIn(Signature *signature)
{
if (signature->HasSignatureFlag(SignatureFlags::PRIVATE)) {
return false;
}
if (signature->HasSignatureFlag(SignatureFlags::PUBLIC)) {
return true;
}
return signature->HasSignatureFlag(SignatureFlags::PROTECTED);
}
static bool IsMethodOverridesOther(ETSChecker *checker, Signature *base, Signature *derived)
{
if (derived->Function()->IsConstructor()) {
return false;
}
if (base == derived) {
return true;
}
if (derived->HasSignatureFlag(SignatureFlags::STATIC) != base->HasSignatureFlag(SignatureFlags::STATIC)) {
return false;
}
if (IsOverridableIn(base)) {
SavedTypeRelationFlagsContext savedFlagsCtx(checker->Relation(), TypeRelationFlag::NO_RETURN_TYPE_CHECK |
TypeRelationFlag::OVERRIDING_CONTEXT);
if (checker->Relation()->SignatureIsSupertypeOf(base, derived)) {
if (derived->HasSignatureFlag(SignatureFlags::STATIC)) {
return false;
}
derived->Function()->SetOverride();
return true;
}
}
return false;
}
enum class OverrideErrorCode {
NO_ERROR,
OVERRIDDEN_FINAL,
INCOMPATIBLE_RETURN,
INCOMPATIBLE_TYPEPARAM,
OVERRIDDEN_WEAKER,
};
static OverrideErrorCode CheckOverride(ETSChecker *checker, Signature *signature, Signature *other)
{
if (other->HasSignatureFlag(SignatureFlags::STATIC)) {
ES2PANDA_ASSERT(signature->HasSignatureFlag(SignatureFlags::STATIC));
return OverrideErrorCode::NO_ERROR;
}
if (other->IsFinal()) {
return OverrideErrorCode::OVERRIDDEN_FINAL;
}
if (signature->ProtectionFlag() > other->ProtectionFlag()) {
return OverrideErrorCode::OVERRIDDEN_WEAKER;
}
if (!checker->IsReturnTypeSubstitutable(signature, other)) {
return OverrideErrorCode::INCOMPATIBLE_RETURN;
}
return OverrideErrorCode::NO_ERROR;
}
Signature *ETSChecker::AdjustForTypeParameters(Signature *source, Signature *target)
{
return AdjustForTypeParameters(source, target, Relation());
}
Signature *ETSChecker::AdjustForTypeParameters(Signature *source, Signature *target, TypeRelation *relation)
{
auto &sourceTypeParams = source->GetSignatureInfo()->typeParams;
auto &targetTypeParams = target->GetSignatureInfo()->typeParams;
if (sourceTypeParams.size() != targetTypeParams.size()) {
return nullptr;
}
if (sourceTypeParams.empty()) {
return target;
}
auto substitution = Substitution {};
for (size_t ix = 0; ix < sourceTypeParams.size(); ix++) {
if (!targetTypeParams[ix]->IsETSTypeParameter()) {
continue;
}
EmplaceSubstituted(&substitution, targetTypeParams[ix]->AsETSTypeParameter(), sourceTypeParams[ix]);
}
return target->Substitute(relation, &substitution);
}
static void ReportOverrideError(ETSChecker *checker, Signature *signature, Signature *overriddenSignature,
const OverrideErrorCode &errorCode)
{
switch (errorCode) {
case OverrideErrorCode::OVERRIDDEN_FINAL: {
checker->LogError(diagnostic::CANNOT_OVERRIDE_OVERRIDDEN_FINAL,
{signature->Function()->Id()->Name(), signature->ReturnType(), signature->Owner(),
overriddenSignature->Function()->Id()->Name(), overriddenSignature->ReturnType(),
overriddenSignature->Owner()},
signature->Function()->Start());
return;
}
case OverrideErrorCode::INCOMPATIBLE_RETURN: {
if (overriddenSignature->Owner()->HasObjectFlag(ETSObjectFlags::INTERFACE)) {
checker->LogError(diagnostic::CANNOT_IMPLEMENT_INCOMPATIBLE_RETURN,
{signature->Function()->Id()->Name(), signature->ReturnType(), signature->Owner(),
overriddenSignature->Function()->Id()->Name(), overriddenSignature->ReturnType(),
overriddenSignature->Owner()},
signature->Function()->Start());
return;
}
checker->LogError(diagnostic::CANNOT_OVERRIDE_INCOMPATIBLE_RETURN,
{signature->Function()->Id()->Name(), signature->ReturnType(), signature->Owner(),
overriddenSignature->Function()->Id()->Name(), overriddenSignature->ReturnType(),
overriddenSignature->Owner()},
signature->Function()->Start());
return;
}
case OverrideErrorCode::OVERRIDDEN_WEAKER: {
checker->LogError(diagnostic::CANNOT_OVERRIDE_OVERRIDDEN_WEAKER,
{signature->Function()->Id()->Name(), signature->ReturnType(), signature->Owner(),
overriddenSignature->Function()->Id()->Name(), overriddenSignature->ReturnType(),
overriddenSignature->Owner()},
signature->Function()->Start());
return;
}
case OverrideErrorCode::INCOMPATIBLE_TYPEPARAM: {
checker->LogError(diagnostic::CANNOT_OVERRIDE_INCOMPATIBLE_TYPEPARAM,
{signature->Function()->Id()->Name(), signature->ReturnType(), signature->Owner(),
overriddenSignature->Function()->Id()->Name(), overriddenSignature->ReturnType(),
overriddenSignature->Owner()},
signature->Function()->Start());
return;
}
default: {
ES2PANDA_UNREACHABLE();
}
}
}
static bool CheckOverride(ETSChecker *checker, Signature *signature, ETSObjectType *site)
{
PropertySearchFlags flags =
PropertySearchFlags::SEARCH_METHOD | PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION;
auto *target = site->GetProperty(signature->Function()->Id()->Name(), flags);
bool isOverridingAnySignature = false;
if (target == nullptr || target->TsType() == nullptr || target->TsType()->IsTypeError()) {
return isOverridingAnySignature;
}
for (auto *it : target->TsType()->AsETSFunctionType()->CallSignatures()) {
bool typeParamError = false;
if (!checker->Relation()->CheckTypeParameterConstraints(signature->TypeParams(), it->TypeParams())) {
typeParamError = true;
}
auto *itSubst = checker->AdjustForTypeParameters(signature, it);
if (itSubst == nullptr) {
continue;
}
if (itSubst->HasSignatureFlag(SignatureFlags::ABSTRACT) || site->HasObjectFlag(ETSObjectFlags::INTERFACE)) {
if ((itSubst->Function()->IsSetter() && !signature->Function()->IsSetter()) ||
(itSubst->Function()->IsGetter() && !signature->Function()->IsGetter())) {
continue;
}
}
if (!IsMethodOverridesOther(checker, itSubst, signature)) {
continue;
}
if (typeParamError) {
ReportOverrideError(checker, signature, it, OverrideErrorCode::INCOMPATIBLE_TYPEPARAM);
return false;
}
if (auto err = CheckOverride(checker, signature, itSubst); err != OverrideErrorCode::NO_ERROR) {
ReportOverrideError(checker, signature, it, err);
return false;
}
isOverridingAnySignature = true;
}
return isOverridingAnySignature;
}
static bool CheckInterfaceOverride(ETSChecker *const checker, ETSObjectType *const interface,
Signature *const signature)
{
bool isOverriding = CheckOverride(checker, signature, interface);
for (auto *const superInterface : interface->Interfaces()) {
isOverriding |= CheckInterfaceOverride(checker, superInterface, signature);
}
return isOverriding;
}
static bool HasMultipleOverridesOfSameSuperInterfaceMethod(ETSChecker *const checker, ETSObjectType *const interface,
Signature *const first, Signature *const second,
Signature **const conflictingSuperMethod)
{
PropertySearchFlags const flags =
PropertySearchFlags::SEARCH_METHOD | PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION;
auto *const target = interface->GetProperty(first->Function()->Id()->Name(), flags);
if (target == nullptr || target->TsType() == nullptr || target->TsType()->IsTypeError()) {
for (auto *const superInterface : interface->Interfaces()) {
if (HasMultipleOverridesOfSameSuperInterfaceMethod(checker, superInterface, first, second,
conflictingSuperMethod)) {
return true;
}
}
return false;
}
for (auto *const base : target->TsType()->AsETSFunctionType()->CallSignatures()) {
auto *const firstSubst = checker->AdjustForTypeParameters(first, base);
auto *const secondSubst = checker->AdjustForTypeParameters(second, base);
if (firstSubst == nullptr || secondSubst == nullptr) {
continue;
}
if (IsMethodOverridesOther(checker, firstSubst, first) &&
IsMethodOverridesOther(checker, secondSubst, second)) {
if (conflictingSuperMethod != nullptr) {
*conflictingSuperMethod = base;
}
return true;
}
}
for (auto *const superInterface : interface->Interfaces()) {
if (HasMultipleOverridesOfSameSuperInterfaceMethod(checker, superInterface, first, second,
conflictingSuperMethod)) {
return true;
}
}
return false;
}
static bool ShouldSkipOverrideConflictCandidate(Signature *const current, Signature *const other)
{
auto const currentPos = current->Function()->Start();
auto const otherPos = other->Function()->Start();
auto const sameProgram = currentPos.Program() == otherPos.Program();
auto const hasPositionInfo = currentPos.line != 0 && otherPos.line != 0;
if (sameProgram && hasPositionInfo) {
return (currentPos.line < otherPos.line) ||
(currentPos.line == otherPos.line && currentPos.index <= otherPos.index);
}
return other < current;
}
static Signature *FindConflictingSuperInterfaceMethod(ETSChecker *const checker, ETSObjectType *const owner,
Signature *const signature, Signature *const other)
{
Signature *conflictingSuperMethod = nullptr;
for (auto *const superInterface : owner->Interfaces()) {
if (HasMultipleOverridesOfSameSuperInterfaceMethod(checker, superInterface, signature, other,
&conflictingSuperMethod)) {
return conflictingSuperMethod != nullptr ? conflictingSuperMethod : other;
}
}
return nullptr;
}
static void ReportMultipleInterfaceOverrideConflict(ETSChecker *const checker, Signature *const signature,
Signature *const other, Signature *const conflictingSuperMethod)
{
checker->LogError(diagnostic::CONFLICTING_INTERFACE_OVERRIDE_OF_SAME_SUPER_METHOD,
{signature->Function()->Id()->Name(), signature, signature->Owner(), signature->Owner(),
other->Function()->Id()->Name(), other, other->Owner(),
conflictingSuperMethod->Function()->Id()->Name(), conflictingSuperMethod,
conflictingSuperMethod->Owner()},
signature->Function()->Start());
}
static bool CheckMultipleInterfaceOverrides(ETSChecker *const checker, Signature *const signature)
{
auto *const owner = signature->Owner();
ES2PANDA_ASSERT(owner != nullptr);
if (!owner->HasObjectFlag(ETSObjectFlags::INTERFACE) || owner->Interfaces().empty()) {
return false;
}
PropertySearchFlags const flags =
PropertySearchFlags::SEARCH_METHOD | PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION;
auto *const target = owner->GetProperty(signature->Function()->Id()->Name(), flags);
if (target == nullptr || target->TsType() == nullptr || target->TsType()->IsTypeError()) {
return false;
}
for (auto *const other : target->TsType()->AsETSFunctionType()->CallSignatures()) {
if (other == signature || other->Function()->IsStatic() != signature->Function()->IsStatic()) {
continue;
}
if (ShouldSkipOverrideConflictCandidate(signature, other)) {
continue;
}
auto *const conflictingSuperMethod = FindConflictingSuperInterfaceMethod(checker, owner, signature, other);
if (conflictingSuperMethod != nullptr) {
ReportMultipleInterfaceOverrideConflict(checker, signature, other, conflictingSuperMethod);
return true;
}
}
return false;
}
void ETSChecker::CheckOverride(Signature *signature)
{
ES2PANDA_ASSERT(signature != nullptr);
auto *owner = signature->Owner();
ES2PANDA_ASSERT(owner != nullptr);
bool isOverriding = false;
if (!owner->HasObjectFlag(ETSObjectFlags::CLASS | ETSObjectFlags::INTERFACE)) {
return;
}
if (CheckMultipleInterfaceOverrides(this, signature)) {
return;
}
for (auto *const interface : owner->Interfaces()) {
isOverriding |= CheckInterfaceOverride(this, interface, signature);
}
ETSObjectType *iter = owner->SuperType();
while (iter != nullptr) {
isOverriding |= checker::CheckOverride(this, signature, iter);
for (auto *const interface : iter->Interfaces()) {
isOverriding |= CheckInterfaceOverride(this, interface, signature);
}
iter = iter->SuperType();
}
lexer::SourcePosition ownerPos = signature->Owner()->GetDeclNode()->Start();
lexer::SourcePosition signaturePos = signature->Function()->Start();
lexer::SourcePosition pos = signaturePos.line == 0 && signaturePos.index == 0 ? ownerPos : signaturePos;
if (!isOverriding && signature->Function()->IsOverride()) {
LogError(diagnostic::OVERRIDE_DOESNT_OVERRIDE,
{signature->Function()->Id()->Name(), signature, signature->Owner()}, pos);
}
}
Signature *ETSChecker::GetSignatureFromMethodDefinition(const ir::MethodDefinition *methodDef)
{
if (methodDef->TsType()->IsTypeError()) {
return nullptr;
}
ES2PANDA_ASSERT_POS(methodDef->TsType() && methodDef->TsType()->IsETSFunctionType(), methodDef->Start());
for (auto *it : methodDef->TsType()->AsETSFunctionType()->CallSignatures()) {
if (it->Function() == methodDef->Function()) {
return it;
}
}
return nullptr;
}
static bool NeedToVerifySignatureVisibility(ETSChecker *checker, Signature *signature, const lexer::SourcePosition &pos)
{
if (signature == nullptr) {
checker->LogError(diagnostic::SIG_UNAVAILABLE, {}, pos);
return false;
}
return (checker->Context().Status() & CheckerStatus::IGNORE_VISIBILITY) == 0U &&
(signature->HasSignatureFlag(SignatureFlags::PRIVATE) ||
signature->HasSignatureFlag(SignatureFlags::PROTECTED));
}
void ETSChecker::ValidateSignatureAccessibility(ETSObjectType *callee, Signature *signature,
const lexer::SourcePosition &pos,
const MaybeDiagnosticInfo &maybeErrorInfo)
{
if (!NeedToVerifySignatureVisibility(this, signature, pos)) {
return;
}
const auto *declNode = callee->GetDeclNode();
auto *containingClass = Context().ContainingClass();
bool isContainingSignatureInherited = containingClass->IsSignatureInherited(signature);
ES2PANDA_ASSERT(declNode && (declNode->IsClassDefinition() || declNode->IsTSInterfaceDeclaration()));
if (declNode->IsTSInterfaceDeclaration()) {
if (containingClass == declNode->AsTSInterfaceDeclaration()->TsType() && isContainingSignatureInherited) {
return;
}
}
if (containingClass == declNode->AsClassDefinition()->TsType() && isContainingSignatureInherited) {
return;
}
bool isSignatureInherited = callee->IsSignatureInherited(signature);
const auto *currentOutermost = containingClass->OutermostClass();
if (!signature->HasSignatureFlag(SignatureFlags::PRIVATE) &&
((signature->HasSignatureFlag(SignatureFlags::PROTECTED) &&
(containingClass->IsDescendantOf(callee) || callee->IsDescendantOf(containingClass))) ||
(currentOutermost != nullptr && currentOutermost == callee->OutermostClass())) &&
isSignatureInherited) {
return;
}
if (signature->HasSignatureFlag(SignatureFlags::PRIVATE)) {
auto *signatureOwner = signature->Owner();
if (signatureOwner == containingClass && callee->IsDescendantOf(containingClass)) {
auto *containingSignature = Context().ContainingSignature();
if (containingSignature != nullptr && containingSignature->Function()->IsStatic()) {
return;
}
}
}
if (!maybeErrorInfo.has_value()) {
LogError(diagnostic::SIG_INVISIBLE, {signature->Function()->Id()->Name(), signature}, pos);
return;
}
const auto [diagnostic, diagnosticParams] = *maybeErrorInfo;
LogError(diagnostic, diagnosticParams, pos);
}
bool ETSChecker::IsReturnTypeSubstitutable(Signature *const s1, Signature *const s2)
{
if (s2->HasSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE)) {
s2->Function()->Parent()->Parent()->Check(this);
}
auto *const r1 = s1->ReturnType();
auto *const r2 = s2->ReturnType();
if (r1->IsETSPrimitiveType() || r2->IsETSPrimitiveType()) {
return Relation()->IsIdenticalTo(r2, r1);
}
auto const hasThisReturnType = [](Signature *s) {
auto *retAnn = s->Function()->ReturnTypeAnnotation();
return retAnn != nullptr && retAnn->IsTSThisType();
};
bool s1HasThisType = hasThisReturnType(s1);
bool s2HasThisType = hasThisReturnType(s2);
if (!s1HasThisType && s2HasThisType) {
return false;
}
ES2PANDA_ASSERT(IsReferenceType(r1));
return Relation()->IsSupertypeOf(r2, r1);
}
std::string ETSChecker::GetAsyncImplName(const util::StringView &name)
{
std::string newName =
util::NameMangler::GetInstance()->CreateMangledNameByTypeAndName(util::NameMangler::ASYNC, name);
return newName;
}
std::string ETSChecker::GetAsyncImplName(ir::MethodDefinition *asyncMethod)
{
ir::ScriptFunction *scriptFunc = asyncMethod->Function();
CHECK_NOT_NULL(scriptFunc);
ir::Identifier *asyncName = scriptFunc->Id();
ES2PANDA_ASSERT_POS(asyncName != nullptr, asyncMethod->Start());
return GetAsyncImplName(asyncName->Name());
}
ir::MethodDefinition *ETSChecker::CreateMethod(const util::StringView &name, ir::ModifierFlags modifiers,
ir::ScriptFunctionFlags flags, ArenaVector<ir::Expression *> &¶ms,
varbinder::FunctionParamScope *paramScope, ir::TypeNode *returnType,
ir::AstNode *body)
{
auto *nameId = ProgramAllocNode<ir::Identifier>(name, ProgramAllocator());
auto *scope = ProgramAllocator()->New<varbinder::FunctionScope>(ProgramAllocator(), paramScope);
auto *const func = ProgramAllocNode<ir::ScriptFunction>(
ProgramAllocator(), ir::ScriptFunction::ScriptFunctionData {
body, ir::FunctionSignature(nullptr, std::move(params), returnType), flags, modifiers});
ES2PANDA_ASSERT(func != nullptr);
func->SetScope(scope);
func->SetIdent(nameId);
if (body != nullptr && body->IsBlockStatement()) {
body->AsBlockStatement()->SetScope(scope);
}
ES2PANDA_ASSERT(scope != nullptr);
scope->BindNode(func);
paramScope->BindNode(func);
scope->BindParamScope(paramScope);
paramScope->BindFunctionScope(scope);
if (!func->IsStatic()) {
auto classDef = VarBinder()->GetScope()->AsClassScope()->Node()->AsClassDefinition();
VarBinder()->AsETSBinder()->AddFunctionThisParam(func);
func->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable->SetTsType(classDef->TsType());
}
auto *funcExpr = ProgramAllocNode<ir::FunctionExpression>(func);
auto *nameClone = nameId->Clone(ProgramAllocator(), nullptr);
auto *method = util::NodeAllocator::ForceSetParent<ir::MethodDefinition>(
ProgramAllocator(), ir::MethodDefinitionKind::METHOD, nameClone, funcExpr, modifiers, ProgramAllocator(),
false);
return method;
}
varbinder::FunctionParamScope *ETSChecker::CopyParams(
const ArenaVector<ir::Expression *> ¶ms, ArenaVector<ir::Expression *> &outParams,
ArenaUnorderedMap<varbinder::Variable *, varbinder::Variable *> *paramVarMap)
{
auto paramCtx = varbinder::LexicalScope<varbinder::FunctionParamScope>(VarBinder());
for (auto *const it : params) {
auto *const paramOld = it->AsETSParameterExpression();
auto *typeOld = paramOld->Clone(ProgramAllocator(), paramOld->Parent());
ES2PANDA_ASSERT(typeOld != nullptr);
auto *const paramNew = typeOld->AsETSParameterExpression();
varbinder::Variable *var = VarBinder()->AddParamDecl(paramNew);
Type *paramType = paramOld->Variable()->TsType();
var->SetTsType(paramType);
var->SetScope(paramCtx.GetScope());
paramNew->SetVariable(var);
paramNew->SetTsType(paramType);
if (auto *newTypeAnno = paramNew->TypeAnnotation(); newTypeAnno != nullptr) {
newTypeAnno->SetTsType(paramOld->TypeAnnotation()->TsType());
compiler::InitScopesPhaseETS::RunExternalNode(newTypeAnno, VarBinder()->AsETSBinder());
}
if (paramVarMap != nullptr) {
paramVarMap->insert({paramOld->Ident()->Variable(), var});
}
outParams.emplace_back(paramNew);
}
return paramCtx.GetScope();
}
void ETSChecker::ReplaceScope(ir::AstNode *root, ir::AstNode *oldNode, varbinder::Scope *newScope)
{
if (root == nullptr) {
return;
}
root->Iterate([this, oldNode, newScope](ir::AstNode *child) {
auto *scope = NodeScope(child);
if (scope != nullptr) {
while (scope->Parent() != nullptr && scope->Parent()->Node() != oldNode) {
scope = scope->Parent();
}
scope->SetParent(newScope);
} else {
ReplaceScope(child, oldNode, newScope);
}
});
}
static void MoveTrailingBlockToEnclosingBlockStatement(ir::CallExpression *callExpr)
{
if (callExpr == nullptr) {
return;
}
ir::AstNode *parent = callExpr->Parent();
ir::AstNode *current = callExpr;
while (parent != nullptr) {
if (!parent->IsBlockStatement()) {
current = parent;
parent = parent->Parent();
} else {
parent->AsBlockStatement()->AddTrailingBlock(current, callExpr->TrailingBlock());
callExpr->TrailingBlock()->SetParent(parent);
callExpr->SetTrailingBlock(nullptr);
break;
}
}
}
static ir::ScriptFunction *CreateLambdaFunction(ETSChecker *checker, ir::BlockStatement *trailingBlock, Signature *sig)
{
auto *funcParamScope = varbinder::LexicalScope<varbinder::FunctionParamScope>(checker->VarBinder()).GetScope();
auto paramCtx =
varbinder::LexicalScope<varbinder::FunctionParamScope>::Enter(checker->VarBinder(), funcParamScope, false);
auto funcCtx = varbinder::LexicalScope<varbinder::FunctionScope>(checker->VarBinder());
auto *funcScope = funcCtx.GetScope();
funcScope->BindParamScope(funcParamScope);
funcParamScope->BindFunctionScope(funcScope);
funcParamScope->SetParent(trailingBlock->Scope()->Parent());
for (auto [_, var] : trailingBlock->Scope()->Bindings()) {
(void)_;
if (var->GetScope() == trailingBlock->Scope()) {
var->SetScope(funcScope);
funcScope->InsertBinding(var->Name(), var);
}
}
ArenaVector<ir::Expression *> params(checker->ProgramAllocator()->Adapter());
ir::ScriptFunctionFlags flags = ir::ScriptFunctionFlags::ARROW;
bool trailingLambdaHasReceiver = false;
if (IsLastParameterLambdaWithReceiver(sig)) {
auto *actualLambdaType =
sig->Function()->Params().back()->AsETSParameterExpression()->TypeAnnotation()->AsETSFunctionType();
auto *receiverOfTrailingBlock =
actualLambdaType->Params()[0]->Clone(checker->ProgramAllocator(), nullptr)->AsExpression();
auto *receiverVar = receiverOfTrailingBlock->AsETSParameterExpression()->Ident()->Variable();
auto *receiverVarClone = checker->ProgramAllocator()->New<varbinder::LocalVariable>(receiverVar->Declaration(),
receiverVar->Flags());
receiverVarClone->SetTsType(receiverVar->TsType());
receiverVarClone->SetScope(funcParamScope);
funcScope->InsertBinding(receiverVarClone->Name(), receiverVarClone);
receiverOfTrailingBlock->AsETSParameterExpression()->Ident()->SetVariable(receiverVarClone);
params.emplace_back(receiverOfTrailingBlock);
trailingLambdaHasReceiver = true;
}
auto *funcNode = checker->ProgramAllocNode<ir::ScriptFunction>(
checker->ProgramAllocator(),
ir::ScriptFunction::ScriptFunctionData {
trailingBlock, ir::FunctionSignature(nullptr, std::move(params), nullptr, trailingLambdaHasReceiver),
flags});
funcNode->SetScope(funcScope);
funcScope->BindNode(funcNode);
funcParamScope->BindNode(funcNode);
trailingBlock->SetScope(funcScope);
return funcNode;
}
static void TransformTraillingLambda(ETSChecker *checker, ir::CallExpression *callExpr, Signature *sig,
const ArenaVector<ir::Expression *> &arguments)
{
auto *trailingBlock = callExpr->TrailingBlock();
ES2PANDA_ASSERT(trailingBlock != nullptr);
auto *funcNode = CreateLambdaFunction(checker, trailingBlock, sig);
funcNode->AddFlag(ir::ScriptFunctionFlags::TRAILING_LAMBDA);
checker->ReplaceScope(funcNode->Body(), trailingBlock, funcNode->Scope());
callExpr->SetTrailingBlock(nullptr);
auto *arrowFuncNode = checker->ProgramAllocNode<ir::ArrowFunctionExpression>(funcNode, checker->ProgramAllocator());
arrowFuncNode->SetRange(trailingBlock->Range());
arrowFuncNode->SetParent(callExpr);
auto missingArgs = sig->Params().size() - arguments.size();
for (size_t i = 0; i < missingArgs - 1; ++i) {
auto undefArg = checker->ProgramAllocator()->New<ir::UndefinedLiteral>();
ES2PANDA_ASSERT(undefArg != nullptr);
undefArg->SetTsType(checker->GlobalETSUndefinedType());
callExpr->Arguments().push_back(undefArg);
undefArg->SetParent(callExpr);
}
callExpr->Arguments().push_back(arrowFuncNode);
}
static ArenaVector<ir::Expression *> ExtendArgumentsWithFakeLamda(ETSChecker *checker, ir::CallExpression *callExpr,
const ArenaVector<ir::Expression *> &arguments)
{
auto funcCtx = varbinder::LexicalScope<varbinder::FunctionScope>(checker->VarBinder());
auto *funcScope = funcCtx.GetScope();
ArenaVector<ir::Expression *> params(checker->ProgramAllocator()->Adapter());
ArenaVector<ir::Statement *> statements(checker->ProgramAllocator()->Adapter());
auto *body = checker->ProgramAllocNode<ir::BlockStatement>(checker->ProgramAllocator(), std::move(statements));
ES2PANDA_ASSERT(body != nullptr);
body->SetScope(funcScope);
auto *funcNode = checker->ProgramAllocNode<ir::ScriptFunction>(
checker->ProgramAllocator(),
ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
ir::ScriptFunctionFlags::ARROW});
ES2PANDA_ASSERT(funcNode != nullptr);
funcNode->SetScope(funcScope);
funcScope->BindNode(funcNode);
auto *arrowFuncNode = checker->ProgramAllocNode<ir::ArrowFunctionExpression>(funcNode, checker->ProgramAllocator());
ES2PANDA_ASSERT(arrowFuncNode != nullptr);
arrowFuncNode->SetParent(callExpr);
ArenaVector<ir::Expression *> fakeArguments = arguments;
fakeArguments.push_back(arrowFuncNode);
return fakeArguments;
}
static void EnsureValidCurlyBrace(ETSChecker *checker, ir::CallExpression *callExpr)
{
if (callExpr->TrailingBlock() == nullptr) {
return;
}
if (callExpr->IsTrailingBlockInNewLine()) {
MoveTrailingBlockToEnclosingBlockStatement(callExpr);
return;
}
checker->LogError(diagnostic::NO_SUCH_SIG_WITH_TRAILING_LAMBDA, {}, callExpr->Start());
}
ETSObjectType *ETSChecker::GetCachedFunctionalInterface(ir::ETSFunctionType *type)
{
auto hash = GetHashFromFunctionType(type);
auto it = functionalInterfaceCache_.find(hash);
if (it == functionalInterfaceCache_.cend()) {
return nullptr;
}
return it->second;
}
void ETSChecker::CacheFunctionalInterface(ir::ETSFunctionType *type, ETSObjectType *ifaceType)
{
auto hash = GetHashFromFunctionType(type);
ES2PANDA_ASSERT(functionalInterfaceCache_.find(hash) == functionalInterfaceCache_.cend());
functionalInterfaceCache_.emplace(hash, ifaceType);
}
void ETSChecker::CollectReturnStatements(ir::AstNode *parent)
{
parent->Iterate([this](ir::AstNode *childNode) -> void {
if (childNode->IsScriptFunction()) {
return;
}
auto scope = Scope();
if (childNode->IsBlockStatement()) {
scope = childNode->AsBlockStatement()->Scope();
}
checker::ScopeContext scopeCtx(this, scope);
if (childNode->IsReturnStatement()) {
ir::ReturnStatement *returnStmt = childNode->AsReturnStatement();
returnStmt->Check(this);
}
CollectReturnStatements(childNode);
});
}
std::vector<ConstraintCheckRecord> &ETSChecker::PendingConstraintCheckRecords()
{
return pendingConstraintCheckRecords_;
}
size_t &ETSChecker::ConstraintCheckScopesCount()
{
return constraintCheckScopesCount_;
}
bool ETSChecker::HasSameAssemblySignature(Signature const *const sig1, Signature const *const sig2,
bool returnTypeCheck) noexcept
{
if (returnTypeCheck) {
if (sig1->ReturnType()->ToAssemblerTypeWithRank() != sig2->ReturnType()->ToAssemblerTypeWithRank()) {
return false;
}
}
if (sig1->ArgCount() != sig2->ArgCount()) {
return false;
}
for (size_t ix = 0U; ix < sig1->Params().size(); ++ix) {
if (sig1->Params()[ix]->TsType()->ToAssemblerTypeWithRank() !=
sig2->Params()[ix]->TsType()->ToAssemblerTypeWithRank()) {
return false;
}
}
auto *rv1 = sig1->RestVar();
auto *rv2 = sig2->RestVar();
if (rv1 == nullptr && rv2 == nullptr) {
return true;
}
if (rv1 == nullptr || rv2 == nullptr) {
return false;
}
return (rv1->TsType()->ToAssemblerTypeWithRank() == rv2->TsType()->ToAssemblerTypeWithRank());
}
bool ETSChecker::HasSameAssemblySignatures(ETSFunctionType const *const func1, ETSFunctionType const *const func2,
bool returnTypeCheck) noexcept
{
for (auto const *sig1 : func1->CallSignatures()) {
for (auto const *sig2 : func2->CallSignatures()) {
if (HasSameAssemblySignature(sig1, sig2, returnTypeCheck)) {
return true;
}
}
}
return false;
}
static Signature *ResolveTrailingLambda(ETSChecker *checker, ArenaVector<Signature *> &signatures,
ir::CallExpression *callExpr, const ArenaVector<ir::Expression *> &arguments,
bool reportError);
checker::Signature *FirstMatchSignatures(ETSChecker *checker, ArenaVector<Signature *> &signatures,
ir::CallExpression *expr)
{
return FirstMatchSignaturesWithArguments(checker, signatures, expr->Arguments(), expr);
}
checker::Signature *FirstMatchSignaturesWithArguments(ETSChecker *checker, ArenaVector<Signature *> &signatures,
const ArenaVector<ir::Expression *> &arguments,
ir::CallExpression *expr, bool reportError)
{
if (expr->TrailingBlock() != nullptr) {
return ResolveTrailingLambda(checker, signatures, expr, arguments, reportError);
}
auto typeRelationFlag =
checker->IsOverloadDeclaration(expr->Callee()) ? TypeRelationFlag::OVERLOADING_CONTEXT : TypeRelationFlag::NONE;
auto *signature =
checker->MatchOrderSignatures(signatures, arguments, expr, typeRelationFlag, reportError ? "call" : "");
signature = CheckDerivedStaticMethodCall(checker, expr, signature);
if (signature == nullptr) {
return nullptr;
}
UpdateDeclarationFromSignature(checker, expr, signature);
return signature;
}
static void CleanArgumentsInformation(const ArenaVector<ir::Expression *> &arguments)
{
if (arguments.empty()) {
return;
}
for (auto *argument : arguments) {
argument->CleanCheckInformation();
}
}
static void LogOverloadMismatch(ETSChecker *checker, util::StringView callName,
const ArenaVector<ir::Expression *> &arguments, const lexer::SourcePosition &pos,
std::string_view signatureKind);
static Signature *ValidateOrderSignature(
ETSChecker *checker, std::tuple<Signature *, const ir::TSTypeParameterInstantiation *, TypeRelationFlag, bool> info,
const ArenaVector<ir::Expression *> &arguments, const lexer::SourceRange &range,
const std::vector<bool> &argTypeInferenceRequired);
static util::StringView GetInvocationTargetName(const ir::Expression *expr)
{
if (expr->IsCallExpression() && expr->AsCallExpression()->Callee()->TsType() == nullptr) {
auto callee = expr->AsCallExpression()->Callee();
if (callee->IsMemberExpression()) {
auto prop = callee->AsMemberExpression()->Property();
ES2PANDA_ASSERT(prop->IsIdentifier());
return prop->AsIdentifier()->Name();
}
return util::StringView("");
}
if (expr->IsCallExpression() && expr->AsCallExpression()->Callee()->TsType()->IsETSFunctionType() &&
!expr->AsCallExpression()->Callee()->TsType()->IsETSArrowType()) {
return expr->AsCallExpression()->Callee()->TsType()->AsETSFunctionType()->Name();
}
if (expr->IsETSNewClassInstanceExpression() &&
expr->AsETSNewClassInstanceExpression()->TsType()->IsETSObjectType()) {
return expr->AsETSNewClassInstanceExpression()->TsType()->AsETSObjectType()->AssemblerName();
}
return util::StringView("");
}
struct MatchOrderFlags {
TypeRelationFlag validateFlags = TypeRelationFlag::NONE;
bool reportErrorsWhileChecking = false;
};
static SignatureMatchResult MatchOrderSignaturesImpl(ETSChecker *checker, ArenaVector<Signature *> &signatures,
const ArenaVector<ir::Expression *> &arguments,
const ir::Expression *expr, MatchOrderFlags flags)
{
SignatureMatchResult matchResult {};
const ir::TSTypeParameterInstantiation *typeArguments =
expr->IsCallExpression() ? expr->AsCallExpression()->TypeParams() : nullptr;
auto const signaturesNumber = signatures.size();
for (std::size_t i = 0U; i < signaturesNumber;) {
auto *sig = signatures[i++];
bool const reportErrorsWhileChecking = flags.reportErrorsWhileChecking && signaturesNumber == i;
std::vector<bool> argTypeInferenceRequired = FindTypeInferenceArguments(arguments);
if (matchResult.notVisibleSignature != nullptr &&
!IsSignatureAccessible(sig, checker->Context().ContainingClass(), checker->Relation())) {
continue;
}
auto *concreteSig = ValidateOrderSignature(
checker, std::make_tuple(sig, typeArguments, flags.validateFlags, reportErrorsWhileChecking), arguments,
expr->Range(), argTypeInferenceRequired);
if (concreteSig == nullptr) {
if (!reportErrorsWhileChecking) {
CleanArgumentsInformation(arguments);
}
continue;
}
if (matchResult.notVisibleSignature == nullptr &&
!IsSignatureAccessible(sig, checker->Context().ContainingClass(), checker->Relation())) {
if (!reportErrorsWhileChecking) {
CleanArgumentsInformation(arguments);
}
matchResult.notVisibleSignature = concreteSig;
} else {
matchResult.matchedSignature = concreteSig;
return matchResult;
}
};
return matchResult;
}
SignatureMatchResult ETSChecker::MatchOrderSignaturesWithResult(ArenaVector<Signature *> &signatures,
const ArenaVector<ir::Expression *> &arguments,
const ir::Expression *expr,
TypeRelationFlag validateFlags)
{
return MatchOrderSignaturesImpl(this, signatures, arguments, expr, {validateFlags, false});
}
Signature *ETSChecker::MatchOrderSignatures(ArenaVector<Signature *> &signatures,
const ArenaVector<ir::Expression *> &arguments, const ir::Expression *expr,
TypeRelationFlag validateFlags, std::string_view signatureKind)
{
auto result = MatchOrderSignaturesImpl(this, signatures, arguments, expr, {validateFlags, !signatureKind.empty()});
if (result.matchedSignature != nullptr) {
return result.matchedSignature;
}
const lexer::SourcePosition &pos = expr->Start();
if (!signatureKind.empty() && result.notVisibleSignature != nullptr) {
LogError(diagnostic::SIG_INVISIBLE,
{result.notVisibleSignature->Function()->Id()->Name(), result.notVisibleSignature}, pos);
}
if (!signatureKind.empty()) {
util::StringView name = GetInvocationTargetName(expr);
if (!name.Empty()) {
LogOverloadMismatch(this, name, arguments, pos, signatureKind);
} else {
LogSignatureMismatch(signatures, arguments, pos, signatureKind);
}
}
return nullptr;
}
static bool ValidateOrderSignatureRequiredParams(ETSChecker *checker, Signature *substitutedSig,
const ArenaVector<ir::Expression *> &arguments, TypeRelationFlag flags,
const std::vector<bool> &argTypeInferenceRequired);
static Signature *ValidateOrderSignature(
ETSChecker *checker, std::tuple<Signature *, const ir::TSTypeParameterInstantiation *, TypeRelationFlag, bool> info,
const ArenaVector<ir::Expression *> &arguments, const lexer::SourceRange &range,
const std::vector<bool> &argTypeInferenceRequired)
{
auto [baseSignature, typeArguments, flags, reportErrors] = info;
InferMatchContext signatureMatchContext(checker, util::DiagnosticType::SEMANTIC, range, reportErrors);
Signature *const signature = MaybeSubstituteTypeParameters(checker, info, arguments, range.start);
if (signature == nullptr) {
return nullptr;
}
size_t const argCount = arguments.size();
auto const hasRestParameter = signature->RestVar() != nullptr;
if (!ValidateRestParameter(checker, signature, arguments, range.start, flags)) {
return nullptr;
}
auto count = std::min(signature->ArgCount(), argCount);
if (!ValidateOrderSignatureRequiredParams(checker, signature, arguments, flags, argTypeInferenceRequired) ||
!signatureMatchContext.ValidMatchStatus()) {
return nullptr;
}
if (!hasRestParameter || (count >= argCount && !signature->RestVar()->TsType()->IsETSTupleType())) {
return signature;
}
if (!ValidateSignatureRestParams(checker, signature, arguments, flags) ||
!signatureMatchContext.ValidMatchStatus()) {
return nullptr;
}
return signature;
}
static bool SetPreferredTypeBeforeValidate(ETSChecker *checker, ir::Expression *argument, Type *paramType,
TypeRelationFlag flags, bool isRestParam)
{
if (argument->IsObjectExpression()) {
argument->AsObjectExpression()->SetPreferredType(paramType);
}
if (argument->IsMemberExpression()) {
checker->SetArrayPreferredTypeForNestedMemberExpressions(argument->AsMemberExpression(), paramType);
} else if (argument->IsSpreadElement() && !isRestParam) {
checker->LogError(diagnostic::SPREAD_ONTO_SINGLE_PARAM, {}, argument->Start());
return false;
} else if (argument->IsNumberLiteral()) {
InferTypeForNumberLiteral(checker, argument->AsNumberLiteral(), paramType);
}
if (argument->IsArrayExpression()) {
argument->AsArrayExpression()->SetPreferredTypeBasedOnFuncParam(checker, paramType, flags);
}
if (argument->IsETSNewArrayInstanceExpression()) {
argument->AsETSNewArrayInstanceExpression()->SetPreferredTypeBasedOnFuncParam(checker, paramType, flags);
}
if (argument->IsETSNewMultiDimArrayInstanceExpression()) {
argument->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredTypeBasedOnFuncParam(checker, paramType,
flags);
}
return true;
}
static bool ValidateOrderSignatureInvocationContext(ETSChecker *checker, Signature *substitutedSig,
ir::Expression *argument, std::size_t index,
TypeRelationFlag flags);
static bool ValidateTypeInferenceRequiredArgument(ETSChecker *checker, Signature *substitutedSig,
const ArenaVector<ir::Expression *> &arguments,
const std::tuple<ir::Expression *, Type *, ir::AstNode *> &argInfo,
TypeRelationFlag flags)
{
auto [argument, paramType, targetParm] = argInfo;
if (argument->IsArrowFunctionExpression()) {
ir::ScriptFunction *const lambda = argument->AsArrowFunctionExpression()->Function();
ERROR_SANITY_CHECK(checker, targetParm->IsETSParameterExpression(), return false);
return CheckLambdaAssignable(checker, targetParm->AsETSParameterExpression(), paramType, lambda) &&
TypeInference(checker, substitutedSig, arguments, flags);
}
if (!argument->IsArrayExpression() || !ContainsTypeError(paramType)) {
return true;
}
checker->LogError(diagnostic::INFER_FAILURE_FUNC_PARAM, {targetParm->AsETSParameterExpression()->Ident()->Name()},
argument->Start());
return false;
}
static bool ValidateOrderSignatureRequiredParams(ETSChecker *checker, Signature *substitutedSig,
const ArenaVector<ir::Expression *> &arguments, TypeRelationFlag flags,
const std::vector<bool> &argTypeInferenceRequired)
{
auto commonArity = std::min(arguments.size(), substitutedSig->ArgCount());
if ((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0) {
if (commonArity == 0) {
ES2PANDA_ASSERT(substitutedSig->GetSignatureInfo()->params.empty());
return false;
}
commonArity = commonArity - 1;
}
for (size_t index = 0; index < commonArity; ++index) {
auto &argument = arguments[index];
auto const paramType = checker->GetNonNullishType(substitutedSig->Params()[index]->TsType());
if (!SetPreferredTypeBeforeValidate(checker, argument, paramType, flags)) {
return false;
}
auto *targetParm = substitutedSig->GetSignatureInfo()->params[index]->Declaration()->Node();
if (argTypeInferenceRequired[index] &&
!ValidateTypeInferenceRequiredArgument(checker, substitutedSig, arguments,
std::make_tuple(argument, paramType, targetParm), flags)) {
return false;
}
if (argument->IsIdentifier() && IsInvalidArgumentAsIdentifier(checker->Scope(), argument->AsIdentifier())) {
checker->LogError(diagnostic::ARG_IS_CLASS_ID, {}, argument->Start());
return false;
}
if (!ValidateOrderSignatureInvocationContext(checker, substitutedSig, argument, index, flags)) {
return false;
}
}
return CheckArrowFunctionParamIfNeeded(checker, substitutedSig, arguments, flags);
}
static bool ValidateOrderSignatureInvocationContext(ETSChecker *checker, Signature *substitutedSig,
ir::Expression *argument, std::size_t index, TypeRelationFlag flags)
{
Type *targetType = substitutedSig->Params()[index]->TsType();
Type *argumentType = argument->Check(checker);
if ((flags & TypeRelationFlag::OVERLOADING_CONTEXT) == 0) {
InferMatchContext sctx {checker, util::DiagnosticType::SEMANTIC, argument->Range(), false};
if (!CheckOptionalLambdaFunction(checker, argument, substitutedSig, index)) {
return false;
}
}
auto const invocationCtx =
checker::InvocationContext(checker->Relation(), argument, argumentType, targetType, argument->Start(),
{{diagnostic::TYPE_MISMATCH_AT_IDX, {argumentType, targetType, index + 1}}}, flags);
return invocationCtx.IsInvocable();
}
static Signature *ResolvePotentialTrailingLambda(ETSChecker *checker, ir::CallExpression *callExpr,
ArenaVector<Signature *> const &signatures,
ArenaVector<ir::Expression *> &arguments);
static Signature *ResolveTrailingLambda(ETSChecker *checker, ArenaVector<Signature *> &signatures,
ir::CallExpression *callExpr,
const ArenaVector<ir::Expression *> &argumentsWithoutTrailingLambda,
bool reportError)
{
auto arguments = ExtendArgumentsWithFakeLamda(checker, callExpr, argumentsWithoutTrailingLambda);
auto sig = ResolvePotentialTrailingLambda(checker, callExpr, signatures, arguments);
sig = CheckDerivedStaticMethodCall(checker, callExpr, sig);
if (sig != nullptr) {
TransformTraillingLambda(checker, callExpr, sig, argumentsWithoutTrailingLambda);
TrailingLambdaTypeInference(checker, sig, callExpr->Arguments());
UpdateDeclarationFromSignature(checker, callExpr, sig);
callExpr->SetIsTrailingCall(true);
return sig;
}
sig = checker->MatchOrderSignatures(signatures, argumentsWithoutTrailingLambda, callExpr, TypeRelationFlag::NONE,
reportError ? "call" : "");
sig = CheckDerivedStaticMethodCall(checker, callExpr, sig);
if (sig != nullptr) {
EnsureValidCurlyBrace(checker, callExpr);
}
UpdateDeclarationFromSignature(checker, callExpr, sig);
return sig;
}
static bool SkipIfMismatchOnTrailingLambdaType(Signature *sig)
{
if (sig->Params().empty()) {
return false;
}
auto *lambdaType = sig->Params().back()->TsType();
if (lambdaType == nullptr || !lambdaType->IsETSArrowType()) {
return false;
}
auto *arrowSig = lambdaType->AsETSFunctionType()->ArrowSignature();
auto requiredCallerParams = arrowSig->MinArgCount();
if (arrowSig->IsExtensionFunction() && requiredCallerParams > 0) {
requiredCallerParams--;
}
return requiredCallerParams > 0;
}
static Signature *ResolvePotentialTrailingLambda(ETSChecker *checker, ir::CallExpression *callExpr,
ArenaVector<Signature *> const &signatures,
ArenaVector<ir::Expression *> &arguments)
{
auto *trailingLambda = arguments.back()->AsArrowFunctionExpression();
ArenaVector<Signature *> normalSig(checker->ProgramAllocator()->Adapter());
ArenaVector<Signature *> sigContainLambdaWithReceiverAsParam(checker->ProgramAllocator()->Adapter());
for (auto sig : signatures) {
if (!sig->HasFunction()) {
continue;
}
if (SkipIfMismatchOnTrailingLambdaType(sig)) {
continue;
}
if (!IsLastParameterLambdaWithReceiver(sig)) {
normalSig.emplace_back(sig);
continue;
}
auto *candidateFunctionType =
sig->Function()->Params().back()->AsETSParameterExpression()->TypeAnnotation()->AsETSFunctionType();
auto *currentReceiver = candidateFunctionType->Params()[0];
trailingLambda->Function()->EmplaceParams(currentReceiver);
sigContainLambdaWithReceiverAsParam.emplace_back(sig);
InferMatchContext sctx {checker, util::DiagnosticType::SEMANTIC, callExpr->Range(), false};
auto signature =
checker->MatchOrderSignatures(sigContainLambdaWithReceiverAsParam, arguments, callExpr,
TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA);
if (signature != nullptr && sctx.ValidMatchStatus()) {
return signature;
}
sigContainLambdaWithReceiverAsParam.clear();
trailingLambda->Function()->ClearParams();
}
InferMatchContext sctx {checker, util::DiagnosticType::SEMANTIC, callExpr->Range(), false};
auto signature =
checker->MatchOrderSignatures(normalSig, arguments, callExpr, TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA);
if (!sctx.ValidMatchStatus()) {
return nullptr;
}
return signature;
}
std::optional<Substitution> ETSChecker::CheckTypeParamsAndBuildSubstitutionIfValid(
Signature *signature, const ArenaVector<ir::TypeNode *> ¶ms, const lexer::SourcePosition &pos)
{
return BuildExplicitSubstitutionForArguments(this, signature, params, pos);
}
static void LogOverloadMismatch(ETSChecker *checker, util::StringView callName,
const ArenaVector<ir::Expression *> &arguments, const lexer::SourcePosition &pos,
std::string_view signatureKind)
{
std::string msg = callName.Mutf8();
msg += '(';
for (std::size_t index = 0U; index < arguments.size(); ++index) {
auto const &argument = arguments[index];
Type const *const argumentType = argument->Check(checker);
if (argumentType != nullptr && !ContainsTypeError(argumentType)) {
msg += argumentType->ToString();
} else {
msg += argument->ToString();
}
if (index != arguments.size() - 1U) {
msg += ", ";
}
}
msg += ')';
checker->LogError(diagnostic::NO_MATCHING_SIG, {signatureKind, msg.c_str()}, pos);
}
}