* @file
*
* This file implements typecheck apis for function CallExpr.
*/
#include "TypeCheckerImpl.h"
#include <algorithm>
#include <tuple>
#include "Desugar/DesugarInTypeCheck.h"
#include "DiagSuppressor.h"
#include "Diags.h"
#include "JoinAndMeet.h"
#include "LocalTypeArgumentSynthesis.h"
#include "TypeCheckUtil.h"
#include "ExtraScopes.h"
#include "cangjie/AST/Clone.h"
#include "cangjie/AST/Create.h"
#include "cangjie/AST/Match.h"
#include "cangjie/AST/RecoverDesugar.h"
#include "cangjie/AST/Utils.h"
#include "cangjie/Basic/DiagnosticEngine.h"
#include "cangjie/Basic/Match.h"
#include "cangjie/Frontend/CompilerInstance.h"
#include "cangjie/Utils/CheckUtils.h"
#include "cangjie/Utils/Macros.h"
#include "cangjie/Utils/Utils.h"
using namespace Cangjie;
using namespace Sema;
using namespace TypeCheckUtil;
using namespace AST;
namespace {
Ptr<Ty> GetInoutTargetTy(const OwnedPtr<FuncArg>& arg)
{
if (Ty::IsTyCorrect(arg->expr->GetTy())) {
return arg->expr->GetTy();
}
CJC_ASSERT(arg->GetTy()->IsPointer() && arg->GetTy()->typeArgs.size() == 1);
return arg->GetTy()->typeArgs[0];
}
std::optional<size_t> CheckNameArgInParams(DiagnosticEngine& diag, const FuncArg& arg, const std::string& argName,
const FuncParamList& list, const std::vector<bool>& marks)
{
for (size_t j = 0; j < list.params.size(); ++j) {
CJC_NULLPTR_CHECK(list.params[j]);
if (list.params[j]->identifier != argName) {
continue;
}
if (!list.params[j]->isNamedParam) {
diag.Diagnose(arg, DiagKind::sema_invalid_named_arguments, argName);
return {};
}
if (marks[j]) {
diag.Diagnose(arg, DiagKind::sema_multiple_named_argument, argName);
return {};
}
return {j};
}
diag.Diagnose(arg, DiagKind::sema_unknown_named_argument, argName);
return {};
}
void SortCallArgumentByParamOrder(const FuncDecl& fd, CallExpr& ce, std::vector<Ptr<AST::FuncArg>>& args)
{
bool namedArgFound = false;
size_t pos = 0;
for (auto& arg : ce.args) {
CJC_NULLPTR_CHECK(arg->expr);
arg->expr->SetTy(arg->withInout ? GetInoutTargetTy(arg) : arg->GetTy());
auto argName = GetArgName(fd, *arg);
if (argName.empty()) {
if (namedArgFound) {
break;
}
if (pos < args.size()) {
args[pos] = arg.get();
} else {
args.push_back(arg.get());
}
++pos;
continue;
}
namedArgFound = true;
for (size_t j = 0; j < fd.funcBody->paramLists[0]->params.size(); ++j) {
if (fd.funcBody->paramLists[0]->params[j]->identifier == argName) {
args[j] = arg.get();
break;
}
}
}
}
* Update callExpr's baseExpr's target and binding named funcArg with funcParam for LSP.
* NOTE: This function should only be used if the 'fd' matches the callExpr.
*/
void UpdateCallTargetsForLSP(const CallExpr& ce, FuncDecl& fd)
{
ReplaceTarget(ce.baseFunc.get(), &fd);
CJC_ASSERT(fd.funcBody && !fd.funcBody->paramLists.empty());
std::unordered_map<std::string, Ptr<FuncParam>> namedParams;
for (auto& param : fd.funcBody->paramLists[0]->params) {
if (param->isNamedParam) {
namedParams.emplace(param->identifier, param.get());
}
}
for (auto& arg : ce.args) {
if (!arg->name.Empty() && namedParams.count(arg->name) != 0) {
ReplaceTarget(arg.get(), namedParams[arg->name]);
}
}
}
void ProcessParamToArg(TypeManager& tyMgr, ArgumentTypeUnit& atu)
{
size_t argSize = atu.argTys.size();
for (size_t t = 0; t < argSize; ++t) {
auto& argTy = atu.argTys[t];
auto& paramTy = atu.tysInArgOrder[t];
CJC_ASSERT(argTy && paramTy);
paramTy = tyMgr.InstOf(paramTy);
if (argTy->IsInteger() && paramTy->IsNumeric()) {
argTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_IDEAL_INT);
} else if (argTy->IsFloating() && paramTy->IsNumeric()) {
argTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_IDEAL_FLOAT);
}
}
}
bool IsSuperCall(const CallExpr& ce)
{
if (ce.baseFunc->astKind == ASTKind::REF_EXPR) {
auto re = StaticAs<ASTKind::REF_EXPR>(ce.baseFunc.get());
return re->isSuper;
} else if (ce.baseFunc->astKind == ASTKind::MEMBER_ACCESS) {
auto ma = StaticAs<ASTKind::MEMBER_ACCESS>(ce.baseFunc.get());
if (ma->baseExpr == nullptr || ma->baseExpr->astKind != ASTKind::REF_EXPR) {
return false;
}
auto reBase = StaticAs<ASTKind::REF_EXPR>(ma->baseExpr.get());
return reBase->isSuper;
}
return false;
}
bool IsPossibleEnumConstructor(const std::vector<Ptr<FuncDecl>>& candidates, const Expr& baseExpr)
{
if (baseExpr.astKind != ASTKind::REF_EXPR) {
return false;
}
auto& re = static_cast<const RefExpr&>(baseExpr);
if (!candidates.empty() && Is<EnumDecl>(candidates.front()->outerDecl)) {
for (auto& target : re.ref.targets) {
if (target->GetTy() == nullptr || target->TyKind() != TypeKind::TYPE_FUNC) {
return true;
}
}
}
return false;
}
bool IsInGenericStructDecl(const FuncDecl& fd)
{
return fd.outerDecl && fd.outerDecl->IsNominalDecl() && fd.outerDecl->generic;
}
std::vector<Ptr<Ty>> GetAllGenericTys(const Expr& expr, const FuncDecl& fd)
{
std::vector<Ptr<Ty>> result;
std::vector<Ptr<Generic>> generics;
if (fd.funcBody->generic) {
generics.push_back(fd.funcBody->generic.get());
}
if (IsInGenericStructDecl(fd)) {
bool isStaticMemberAccess = (expr.astKind == ASTKind::MEMBER_ACCESS) && fd.TestAttr(Attribute::STATIC);
if (isStaticMemberAccess || IsClassOrEnumConstructor(fd)) {
generics.push_back(fd.outerDecl->generic.get());
}
}
for (auto& generic : generics) {
for (auto& it : generic->typeParameters) {
result.push_back(it->GetTy());
}
}
return result;
}
SUPPRESS_WARNING("-Wunused-function")
int64_t CalculateEnumCtorScopeLevel(const ASTContext& ctx, const CallExpr& ce, const FuncDecl& fd)
{
if (fd.TestAttr(Attribute::IMPORTED)) {
return 0;
}
if (IsNode1ScopeVisibleForNode2(fd, ce)) {
return static_cast<int64_t>(fd.scopeLevel);
}
Symbol* symOfCurStruct = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ce.symbol->scopeName);
if (symOfCurStruct && symOfCurStruct->node && symOfCurStruct->node->astKind == ASTKind::EXTEND_DECL) {
auto ed = RawStaticCast<ExtendDecl*>(symOfCurStruct->node);
if (ed->extendedType && fd.outerDecl && Ty::GetDeclPtrOfTy(ed->extendedType->GetTy()) == fd.outerDecl) {
return static_cast<int64_t>(fd.scopeLevel);
}
}
return 0;
}
UNSUPPRESS_WARNING()
bool CompareParamTys(TypeManager& tyMgr, const std::vector<Ptr<AST::Ty>>& iTys, const std::vector<Ptr<AST::Ty>>& jTys)
{
if (iTys.size() != jTys.size()) {
return false;
}
size_t argSize = iTys.size();
for (size_t t = 0; t < argSize; ++t) {
if (iTys[t]->IsNumeric() && jTys[t]->IsNumeric()) {
ComparisonRes res = CompareIntAndFloat(*iTys[t], *jTys[t]);
if (res == ComparisonRes::GT) {
return false;
} else {
continue;
}
}
if (!tyMgr.IsSubtype(iTys[t], jTys[t])) {
return false;
}
}
return true;
}
std::vector<SubstPack> ResolveTypeMappings(
TypeManager& tyMgr, const std::vector<SubstPack>& typeMappings, const FuncDecl& fd)
{
CJC_ASSERT(fd.GetTy() && fd.TyKind() == TypeKind::TYPE_FUNC);
auto mappingSize = typeMappings.size();
std::vector<bool> matchMark(mappingSize, true);
for (size_t i = 0; i < mappingSize; ++i) {
if (!matchMark[i]) {
continue;
}
for (size_t j = i + 1; j < mappingSize; ++j) {
if (!matchMark[j]) {
continue;
}
auto iTy = DynamicCast<FuncTy*>(tyMgr.ApplySubstPack(fd.GetTy(), typeMappings[i]));
auto jTy = DynamicCast<FuncTy*>(tyMgr.ApplySubstPack(fd.GetTy(), typeMappings[j]));
if (iTy == jTy) {
matchMark[j] = false;
continue;
}
CJC_NULLPTR_CHECK(iTy);
CJC_NULLPTR_CHECK(jTy);
auto iToJ = CompareParamTys(tyMgr, iTy->paramTys, jTy->paramTys);
auto jToI = CompareParamTys(tyMgr, jTy->paramTys, iTy->paramTys);
if (iToJ && !jToI) {
matchMark[j] = false;
} else if (!iToJ && jToI) {
matchMark[i] = false;
break;
}
}
}
std::vector<SubstPack> resMappings;
for (size_t i = 0; i < matchMark.size(); ++i) {
if (matchMark[i]) {
resMappings.push_back(typeMappings[i]);
}
}
return resMappings;
}
void DiagnoseForMultiMapping(DiagnosticEngine& diag, const CallExpr& ce, const FuncDecl& fd,
const std::vector<SubstPack>& typeMappings, const std::vector<SubstPack>& resMappings)
{
bool shouldDiag = std::none_of(ce.args.cbegin(), ce.args.cend(), [](const OwnedPtr<FuncArg>& farg) {
return farg && farg->expr && farg->expr->GetTy() && farg->expr->GetTy()->IsInvalid();
});
if (!shouldDiag) {
return;
}
if (resMappings.empty()) {
diag.Diagnose(ce, DiagKind::sema_parameters_and_arguments_mismatch);
} else if (resMappings.size() > typeMappings.size()) {
diag.Diagnose(ce, DiagKind::sema_generic_ambiguous_method_match_in_upper_bounds, fd.identifier.Val());
} else {
diag.Diagnose(ce, DiagKind::sema_ambiguous_arg_type);
}
}
bool IsInterfaceFuncWithSameSignature(TypeManager& tyMgr, const FunctionMatchingUnit& i, const FunctionMatchingUnit& j)
{
bool bothInInterface = i.fd.outerDecl && i.fd.outerDecl->astKind == ASTKind::INTERFACE_DECL && j.fd.outerDecl &&
j.fd.outerDecl->astKind == ASTKind::INTERFACE_DECL && i.fd.TestAttr(Attribute::ABSTRACT) &&
j.fd.TestAttr(Attribute::ABSTRACT);
if (bothInInterface) {
auto iTy = tyMgr.ApplySubstPack(i.fd.GetTy(), i.typeMapping);
auto jTy = tyMgr.ApplySubstPack(j.fd.GetTy(), j.typeMapping);
return iTy == jTy;
}
return false;
}
bool IsFuncDeclPossibleVariadic(const FuncDecl& fd)
{
if (fd.TestAttr(Attribute::ENUM_CONSTRUCTOR)) {
return false;
}
if (fd.identifier != "()" && fd.identifier != "[]" && IsFieldOperator(fd.identifier)) {
return false;
}
CJC_ASSERT(fd.funcBody && !fd.funcBody->paramLists.empty());
auto& params = fd.funcBody->paramLists.front()->params;
auto iter = std::find_if(params.crbegin(), params.crend(), [](auto& param) {
CJC_NULLPTR_CHECK(param);
return !param->isNamedParam;
});
return iter != params.crend() && Ty::IsTyCorrect((*iter)->GetTy()) && (*iter)->GetTy()->IsStructArray();
}
size_t GetPositionalParamSize(const FuncDecl& fd)
{
CJC_ASSERT(fd.funcBody && !fd.funcBody->paramLists.empty());
auto& params = fd.funcBody->paramLists.front()->params;
size_t namedSize = 0;
for (auto iter = params.crbegin(); iter < params.crend(); ++iter) {
CJC_NULLPTR_CHECK(*iter);
if ((*iter)->isNamedParam) {
namedSize++;
} else {
break;
}
}
return params.size() - namedSize;
}
size_t GetInitializedlNameParamSize(const FuncDecl& fd)
{
CJC_ASSERT(fd.funcBody && !fd.funcBody->paramLists.empty());
auto& params = fd.funcBody->paramLists.front()->params;
size_t result = 0;
for (auto iter = params.crbegin(); iter < params.crend(); ++iter) {
CJC_NULLPTR_CHECK(*iter);
if ((*iter)->isNamedParam) {
result += (*iter)->TestAttr(Attribute::HAS_INITIAL);
} else {
break;
}
}
return result;
}
size_t GetPositionalArgSize(const CallExpr& ce)
{
size_t count = 0;
for (auto& arg : ce.args) {
if (!arg->name.Empty()) {
break;
}
count++;
}
return count;
}
bool HasSamePositionalArgsSize(const FuncDecl& fd, const CallExpr& ce)
{
size_t positionalParamSize = GetPositionalParamSize(fd);
size_t positionalArgSize = GetPositionalArgSize(ce);
if (!ce.args.empty() && ce.args.back()->TestAttr(Attribute::IMPLICIT_ADD) &&
!fd.funcBody->paramLists[0]->params.empty() &&
fd.funcBody->paramLists[0]->params.back()->isNamedParam && positionalArgSize == ce.args.size()) {
--positionalArgSize;
}
return positionalParamSize == positionalArgSize;
}
bool IsCallSourceExprVariadic(const CallExpr& ce)
{
return ce.sourceExpr && ce.sourceExpr->astKind == ASTKind::CALL_EXPR &&
StaticAs<ASTKind::CALL_EXPR>(ce.sourceExpr)->callKind == CallKind::CALL_VARIADIC_FUNCTION;
}
bool AreFuncDeclAndCallExprFullfillVariadicInequality(const FuncDecl& fd, const CallExpr& ce)
{
size_t positionalArgSize = GetPositionalArgSize(ce);
size_t positionalParamSize = GetPositionalParamSize(fd);
return positionalArgSize + 1 >= positionalParamSize;
}
bool IsPossibleVariadicFunction(const FuncDecl& fd, const CallExpr& ce)
{
return !IsCallSourceExprVariadic(ce) && IsFuncDeclPossibleVariadic(fd) &&
AreFuncDeclAndCallExprFullfillVariadicInequality(fd, ce);
}
bool IsPossibleVariadicFunction(const std::vector<Ptr<FuncDecl>>& candidates, const CallExpr& ce)
{
return Utils::In(candidates, [&ce](Ptr<const FuncDecl> fd) {
return fd != nullptr && !IsCallSourceExprVariadic(ce) && IsFuncDeclPossibleVariadic(*fd) &&
AreFuncDeclAndCallExprFullfillVariadicInequality(*fd, ce);
});
}
bool IsPossibleVariadicFunction(const FuncTy& funcTy, const CallExpr& ce)
{
if (funcTy.hasVariableLenArg || funcTy.paramTys.empty() || IsCallSourceExprVariadic(ce) ||
GetPositionalArgSize(ce) + 1 < funcTy.paramTys.size()) {
return false;
}
return Ty::IsTyCorrect(funcTy.paramTys.back()) && funcTy.paramTys.back()->IsStructArray();
}
inline Ptr<Ty> GetRealNonOptionTy(Ptr<Ty> ty)
{
if (ty->IsCoreOptionType()) {
return GetRealNonOptionTy(ty->typeArgs[0]);
}
return ty;
}
std::vector<Ptr<FuncDecl>> SyntaxFilterCandidates(const CallExpr& ce, const std::vector<Ptr<FuncDecl>>& candidates)
{
std::vector<Ptr<FuncDecl>> definitelyMismatched;
std::vector<Ptr<FuncDecl>> mismatched;
std::vector<Ptr<FuncDecl>> matched;
for (auto fd : candidates) {
bool mismatch = false;
bool definitelyMismatch = false;
auto paramTys = GetParamTys(*fd);
for (size_t i = 0; i < ce.args.size(); i++) {
auto lamArg = DynamicCast<LambdaExpr>(ce.args[i]->expr.get());
if (!lamArg) {
continue;
} else if (ce.args[i]->TestAttr(Attribute::IMPLICIT_ADD)) {
definitelyMismatch = paramTys.empty() || !paramTys.back()->IsFunc();
break;
} else if (ce.args[i]->name.Empty() && paramTys.size() <= i) {
definitelyMismatch = true;
break;
}
Ptr<Ty> paramTy = nullptr;
if (ce.args[i]->name.Empty()) {
paramTy = GetRealNonOptionTy(paramTys[i]);
} else if (auto res = GetParamTyAccordingToArgName(*fd, ce.args[i]->name)) {
paramTy = GetRealNonOptionTy(res.value().first);
}
if (!paramTy ||
!Utils::In(paramTy->kind, {TypeKind::TYPE_FUNC, TypeKind::TYPE_ANY, TypeKind::TYPE_GENERICS})) {
mismatch = true;
}
}
definitelyMismatch
? definitelyMismatched.emplace_back(fd)
: (mismatch ? mismatched.emplace_back(fd) : matched.emplace_back(fd));
}
return matched.empty() ? (mismatched.empty() ? definitelyMismatched : mismatched) : matched;
}
bool ChkArgsOrder(DiagnosticEngine& diag, const CallExpr& ce)
{
bool foundNamedArg = false;
for (auto& arg : ce.args) {
if (foundNamedArg) {
if (arg->name.Empty()) {
diag.DiagnoseRefactor(DiagKindRefactor::sema_unordered_arguments, *arg);
return false;
}
} else if (!arg->name.Empty()) {
foundNamedArg = true;
}
}
return true;
}
std::map<size_t, std::vector<Ptr<FuncDecl>>> FuncDeclsGroupByFixedPositionalArity(
const std::vector<Ptr<FuncDecl>>& candidates, const CallExpr& ce)
{
std::map<size_t, std::vector<Ptr<FuncDecl>>> fixedPositionalArityToGroup;
for (auto fd : candidates) {
CJC_NULLPTR_CHECK(fd);
if (!fd || !IsPossibleVariadicFunction(*fd, ce)) {
continue;
}
size_t fixedPositionalArity = GetPositionalParamSize(*fd) - 1;
const auto iter = fixedPositionalArityToGroup.find(fixedPositionalArity);
if (iter != fixedPositionalArityToGroup.cend()) {
iter->second.emplace_back(fd);
} else {
std::vector<Ptr<FuncDecl>> group = {fd};
fixedPositionalArityToGroup.emplace(fixedPositionalArity, group);
}
}
return fixedPositionalArityToGroup;
}
inline bool IsSearchTargetInMemberAccessUpperBound(const CallExpr& ce)
{
if (auto ma = DynamicCast<MemberAccess*>(ce.baseFunc.get())) {
return ma->isExposedAccess;
}
return false;
}
std::pair<bool, TypeSubst> CheckAndObtainTypeMappingBetweenInterfaceAndExtend(
const ExtendDecl& ed, const InterfaceDecl& id)
{
bool flag = false;
TypeSubst typeMapping;
for (auto& super : ed.inheritedTypes) {
if (!super->GetTy()->IsInterface()) {
continue;
}
auto superTy = RawStaticCast<InterfaceTy*>(super->GetTy());
if (&id != Ty::GetDeclPtrOfTy(superTy)) {
continue;
}
typeMapping = TypeCheckUtil::GenerateTypeMapping(id, superTy->typeArgs);
if (typeMapping.empty()) {
continue;
}
flag = true;
break;
}
return {flag, typeMapping};
}
void RemoveNonAnnotationCandidates(std::vector<Ptr<FuncDecl>>& candidates)
{
for (auto iter = candidates.begin(); iter != candidates.end();) {
CJC_NULLPTR_CHECK(*iter);
bool isCustomAnnotation = IsInstanceConstructor(**iter) && (*iter)->TestAttr(Attribute::IN_CLASSLIKE) &&
(*iter)->outerDecl && (*iter)->outerDecl->TestAttr(Attribute::IS_ANNOTATION);
if (isCustomAnnotation) {
++iter;
} else {
iter = candidates.erase(iter);
}
}
}
inline bool IsFunctionalType(const Ty& ty)
{
if (auto genericTy = DynamicCast<GenericsTy>(&ty)) {
return std::any_of(
genericTy->upperBounds.cbegin(), genericTy->upperBounds.cend(), [](auto ty) { return ty->IsFunc(); });
}
return ty.IsFunc();
}
bool CheckUseTrailingClosureWithFunctionType(
DiagnosticEngine& diag, FuncArg& arg, const Ty& originalTy, const FuncDecl& candidate)
{
if (!arg.TestAttr(Attribute::IMPLICIT_ADD) || IsFunctionalType(originalTy)) {
return true;
}
arg.SetTy(TypeManager::GetInvalidTy());
CJC_NULLPTR_CHECK(arg.expr);
arg.expr->SetTy(TypeManager::GetInvalidTy());
std::string message =
originalTy.IsGeneric() ? "generic type without upper bound of function type" : "non-function type";
auto diagBuilder = diag.DiagnoseRefactor(
DiagKindRefactor::sema_trailing_lambda_cannot_used_for_non_function, arg, message);
diagBuilder.AddMainHintArguments(originalTy.String());
diagBuilder.AddNote(candidate, MakeRangeForDeclIdentifier(candidate), "found candidate");
return false;
}
void FilterOverriden(
TypeManager& tm, std::vector<OwnedPtr<FunctionMatchingUnit>>& candidates, std::vector<bool>& targetMark)
{
for (size_t i = 0; i < candidates.size(); ++i) {
if (!candidates[i]->fd.outerDecl) {
return;
}
auto instOuter1 = tm.ApplySubstPack(candidates[i]->fd.outerDecl->GetTy(), candidates[i]->typeMapping);
if (!Ty::IsTyCorrect(instOuter1)) {
return;
}
auto instFuncTy1 =
StaticCast<FuncTy*>(tm.ApplySubstPack(candidates[i]->fd.GetTy(), candidates[i]->typeMapping));
auto& funcBody1 = candidates[i]->fd.funcBody;
for (size_t j = 0; j < candidates.size(); ++j) {
if (!candidates[j]->fd.outerDecl || i == j) {
continue;
}
auto instOuter2 = tm.ApplySubstPack(candidates[j]->fd.outerDecl->GetTy(), candidates[j]->typeMapping);
if (!Ty::IsTyCorrect(instOuter2) || instOuter1 == instOuter2) {
continue;
}
auto instFuncTy2 =
StaticCast<FuncTy*>(tm.ApplySubstPack(candidates[j]->fd.GetTy(), candidates[j]->typeMapping));
auto& funcBody2 = candidates[j]->fd.funcBody;
auto sameSig = instFuncTy1 == instFuncTy2;
sameSig = sameSig && ((funcBody1->generic == nullptr) == (funcBody2->generic == nullptr));
if (sameSig && funcBody1->generic) {
sameSig = funcBody1->generic->typeParameters.size() == funcBody2->generic->typeParameters.size();
auto mappingOf2Func = GenerateTypeMappingBetweenFuncs(tm, candidates[i]->fd, candidates[j]->fd);
auto subFuncs = tm.GetInstantiatedTys(candidates[i]->fd.GetTy(), mappingOf2Func);
sameSig = sameSig && Utils::In(candidates[j]->fd.GetTy(), subFuncs);
}
if (tm.IsSubtype(instOuter1, instOuter2) && sameSig) {
targetMark[j] = false;
break;
}
}
}
}
}
bool TypeChecker::TypeCheckerImpl::CheckArgsWithParamName(const CallExpr& ce, const FuncDecl& fd)
{
std::vector<bool> hasTy;
CJC_ASSERT(fd.funcBody && !fd.funcBody->paramLists.empty());
for (auto& param : fd.funcBody->paramLists[0]->params) {
hasTy.push_back(param->TestAttr(Attribute::HAS_INITIAL));
}
size_t funcDeclParamListLen = fd.funcBody->paramLists[0]->params.size();
std::vector<bool> marks(funcDeclParamListLen, false);
bool namedArgFound = false;
size_t pos = 0;
for (size_t i = 0; i < ce.args.size(); ++i) {
CJC_NULLPTR_CHECK(ce.args[i]);
size_t index = 0;
std::string argName = GetArgName(fd, *ce.args[i]);
if (argName.empty()) {
if (namedArgFound) {
diag.DiagnoseRefactor(DiagKindRefactor::sema_unordered_arguments, *ce.args[i]);
return false;
}
if (pos < funcDeclParamListLen && fd.funcBody->paramLists[0]->params[pos]->isNamedParam) {
DiagNeedNamedArgument(diag, ce, fd, pos, i);
return false;
}
index = pos;
++pos;
} else {
auto res = CheckNameArgInParams(diag, *ce.args[i], argName, *fd.funcBody->paramLists[0], marks);
if (res.has_value()) {
index = res.value();
} else {
return false;
}
namedArgFound = true;
}
if (index < funcDeclParamListLen) {
marks[index] = true;
hasTy[index] = true;
}
}
for (auto it : std::as_const(hasTy)) {
if (it) {
continue;
}
if (ce.sourceExpr == nullptr) {
DiagWrongNumberOfArguments(diag, ce, fd);
}
return false;
}
return true;
}
bool TypeChecker::TypeCheckerImpl::IsGenericCall(const ASTContext& ctx, const CallExpr& ce, const FuncDecl& fd) const
{
bool hasGeneric = fd.TestAttr(Attribute::GENERIC) || (fd.GetTy() && fd.GetTy()->HasGeneric());
if (hasGeneric) {
return true;
}
if (!ce.baseFunc->GetTypeArgs().empty()) {
return true;
}
if (IsSuperCall(ce) && fd.funcBody->parentClassLike && fd.funcBody->parentClassLike->generic) {
return true;
}
if (ce.baseFunc->astKind == ASTKind::MEMBER_ACCESS) {
auto ma = StaticAs<ASTKind::MEMBER_ACCESS>(ce.baseFunc.get());
auto baseType = ma->baseExpr ? ma->baseExpr->GetTy() : nullptr;
if (!Ty::IsTyCorrect(baseType)) {
return false;
}
auto decl = Ty::GetDeclPtrOfTy(baseType);
if (decl && decl->TestAttr(Attribute::GENERIC) &&
(decl->astKind != ASTKind::ENUM_DECL || fd.TestAttr(Attribute::ENUM_CONSTRUCTOR) ||
fd.TestAttr(Attribute::STATIC))) {
return true;
}
if (IsInGenericStructDecl(fd)) {
return true;
}
}
if (ce.baseFunc->astKind == ASTKind::REF_EXPR) {
auto sym1 = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ce.scopeName);
auto sym2 = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, fd.scopeName);
if (sym1 != sym2 && sym2 != nullptr && Ty::IsTyCorrect(sym2->node->GetTy()) &&
!sym2->node->GetTy()->typeArgs.empty()) {
return true;
}
}
return false;
}
bool TypeChecker::TypeCheckerImpl::CompareFuncCandidates(
FunctionMatchingUnit& i, FunctionMatchingUnit& j, const CallExpr& ce)
{
size_t argSize = i.tysInArgOrder.size();
bool isGeneric = GetCurrentGeneric(j.fd, ce) != nullptr;
if (isGeneric && Utils::In(j.tysInArgOrder, [](auto ty) { return ty->HasGeneric(); })) {
InstCtxScope ic(*this);
ic.SetRefDeclSimple(j.fd, ce);
ArgumentTypeUnit atu(i.tysInArgOrder, j.tysInArgOrder, {});
ProcessParamToArg(typeManager, atu);
auto argPack = LocTyArgSynArgPack{GetTyVarsToSolve(typeManager.GetInstMapping()),
atu.argTys, atu.tysInArgOrder,
{}, TypeManager::GetInvalidTy(), TypeManager::GetInvalidTy(), {}};
auto optSubst = LocalTypeArgumentSynthesis(typeManager, argPack, {}).SynthesizeTypeArguments();
if (!optSubst.has_value()) {
return false;
}
for (size_t t = 0; t < argSize; ++t) {
if (!i.tysInArgOrder[t]->IsNumeric() || !j.tysInArgOrder[t]->IsNumeric()) {
continue;
}
ComparisonRes res = CompareIntAndFloat(*i.tysInArgOrder[t], *j.tysInArgOrder[t]);
if (res == ComparisonRes::GT) {
return false;
}
}
return true;
}
if (i.fd.outerDecl && j.fd.outerDecl) {
bool sameNumberOfParams = j.fd.GetTy()->typeArgs.size() == i.fd.GetTy()->typeArgs.size();
if (sameNumberOfParams && typeManager.IsFuncParameterTypesIdentical(i.tysInArgOrder, j.tysInArgOrder)) {
bool isJImplementable =
j.fd.outerDecl->astKind == ASTKind::INTERFACE_DECL || j.fd.TestAttr(Attribute::ABSTRACT);
bool isITheImplementation =
i.fd.outerDecl->astKind != ASTKind::INTERFACE_DECL && !i.fd.TestAttr(Attribute::ABSTRACT);
return isJImplementable && isITheImplementation;
}
}
return CompareParamTys(typeManager, i.tysInArgOrder, j.tysInArgOrder);
}
std::vector<size_t> TypeChecker::TypeCheckerImpl::ResolveOverload(
std::vector<OwnedPtr<FunctionMatchingUnit>>& candidates, const CallExpr& ce)
{
auto targetNum = candidates.size();
std::vector<bool> targetMark(targetNum, true);
FilterOverriden(typeManager, candidates, targetMark);
for (size_t i = 0; i < targetNum; ++i) {
if (!targetMark[i]) {
continue;
}
for (size_t j = i + 1; j < targetNum; ++j) {
if (!targetMark[j]) {
continue;
}
auto iToJ = CompareFuncCandidates(*candidates[i], *candidates[j], ce);
auto jToI = CompareFuncCandidates(*candidates[j], *candidates[i], ce);
if (iToJ && !jToI) {
targetMark[j] = false;
} else if (!iToJ && jToI) {
targetMark[i] = false;
break;
}
if (IsInterfaceFuncWithSameSignature(typeManager, *candidates[i], *candidates[j])) {
targetMark[i] = false;
break;
}
}
}
std::vector<size_t> results;
for (size_t i = 0; i < targetMark.size(); ++i) {
if (targetMark[i]) {
results.push_back(i);
}
}
return results;
}
void TypeChecker::TypeCheckerImpl::InstantiatePartOfTheGenericParameters(
std::vector<OwnedPtr<FunctionMatchingUnit>>& candidates)
{
for (auto& candidate : candidates) {
if (candidate->typeMapping.u2i.empty()) {
continue;
}
if (IsClassOrEnumConstructor(candidate->fd)) {
continue;
}
SubstPack outerDeclTypeMapping = candidate->typeMapping;
CJC_NULLPTR_CHECK(candidate->fd.funcBody);
auto generic = candidate->fd.funcBody->generic.get();
if (generic) {
for (auto& it : generic->typeParameters) {
outerDeclTypeMapping.u2i.erase(StaticCast<GenericsTy*>(it->GetTy()));
}
if (outerDeclTypeMapping.u2i.empty()) {
continue;
}
}
for (auto& it : candidate->tysInArgOrder) {
it = typeManager.ApplySubstPack(it, outerDeclTypeMapping);
}
}
}
void TypeChecker::TypeCheckerImpl::GenerateTypeMappingForBaseExpr(const Expr& baseExpr, MultiTypeSubst& typeMapping)
{
if (baseExpr.astKind != ASTKind::MEMBER_ACCESS) {
return;
}
auto& ma = static_cast<const MemberAccess&>(baseExpr);
if (!Ty::IsTyCorrect(ma.baseExpr->GetTy())) {
return;
}
CJC_ASSERT(!ma.baseExpr->GetTy()->HasIntersectionTy());
if (IsThisOrSuper(*ma.baseExpr)) {
typeManager.GenerateGenericMapping(typeMapping, *ma.baseExpr->GetTy());
return;
}
auto baseDecl = GetRealTarget(ma.baseExpr.get(), ma.baseExpr->GetTarget());
if (baseDecl && baseDecl->astKind == ASTKind::PACKAGE_DECL) {
return;
}
auto maTarget = ma.GetTarget();
bool typeDeclMemberAccess = baseDecl && maTarget && maTarget->outerDecl &&
(baseDecl->IsTypeDecl() || baseDecl->TestAttr(Attribute::ENUM_CONSTRUCTOR));
if (typeDeclMemberAccess) {
CJC_NULLPTR_CHECK(maTarget->outerDecl->GetTy());
auto promoteMapping = promotion.GetPromoteTypeMapping(*ma.baseExpr->GetTy(), *maTarget->outerDecl->GetTy());
auto baseTypeArgs = ma.baseExpr->GetTypeArgs();
std::unordered_set<Ptr<Ty>> definedTys;
std::for_each(
baseTypeArgs.begin(), baseTypeArgs.end(), [&definedTys](auto type) { definedTys.emplace(type->GetTy()); });
auto genericTys = GetAllGenericTys(baseDecl->GetTy());
for (auto it = promoteMapping.begin(); it != promoteMapping.end(); ++it) {
Utils::EraseIf(it->second,
[&genericTys, &definedTys](auto ty) { return genericTys.count(ty) != 0 && definedTys.count(ty) == 0; });
}
MergeMultiTypeSubsts(typeMapping, promoteMapping);
} else {
typeManager.GenerateGenericMapping(typeMapping, *ma.baseExpr->GetTy());
}
}
TypeSubst TypeChecker::TypeCheckerImpl::GenerateTypeMappingByTyArgs(
const std::vector<Ptr<Type>>& typeArgs, const Generic& generic) const
{
TypeSubst typeMapping;
auto typeArgsSize = typeArgs.size();
if (generic.typeParameters.size() != typeArgsSize) {
return typeMapping;
}
for (size_t i = 0; i < typeArgsSize; ++i) {
if (!generic.typeParameters[i]) {
continue;
}
Ptr<Ty> typeArgTy = TypeManager::GetInvalidTy();
if (auto type = typeArgs[i]; type) {
if (type->GetTy() && type->GetTy()->HasIntersectionTy()) {
continue;
}
typeArgTy = type->GetTy();
}
if (Ty::IsTyCorrect(generic.typeParameters[i]->GetTy()) && Ty::IsTyCorrect(typeArgTy)) {
typeMapping.emplace(StaticCast<GenericsTy*>(generic.typeParameters[i]->GetTy()), typeArgTy);
}
}
return typeMapping;
}
namespace {
void ReduceSubstPack(SubstPack& maps, TypeManager& tyMgr)
{
for (auto& [tv, insts] : maps.inst) {
std::set<Ptr<Ty>> instsOneStep = insts;
std::set<Ptr<Ty>> instsFinal;
for (auto ty : instsOneStep) {
instsFinal.merge(tyMgr.GetInstantiatedTys(ty, maps.inst));
}
insts = instsFinal;
}
}
void FilterAndReduceSubstPacks(std::vector<SubstPack>& mapsList, TypeManager& tyMgr)
{
std::vector<SubstPack> filtered;
for (auto& maps : mapsList) {
if (!HasTyVarsToSolve(maps)) {
filtered.push_back(maps);
}
}
for (auto& maps : filtered) {
ReduceSubstPack(maps, tyMgr);
}
mapsList = filtered;
}
}
void TypeChecker::TypeCheckerImpl::FilterTypeMappings(
const Expr& expr, FuncDecl& fd, std::vector<MultiTypeSubst>& typeMappings)
{
auto genericParams = StaticToTyVars(GetAllGenericTys(expr, fd));
for (auto curTyParam : genericParams) {
for (auto it = typeMappings.begin(); it != typeMappings.end();) {
auto mapping = *it;
if (curTyParam->IsGeneric() && mapping.find(curTyParam) == mapping.end()) {
it = typeMappings.erase(it);
} else {
++it;
}
}
}
auto tyVars = ::GetAllGenericTys(fd.GetTy());
CJC_ASSERT(fd.funcBody);
if (fd.funcBody->generic) {
for (auto& it : fd.funcBody->generic->typeParameters) {
tyVars.emplace(it->GetTy());
}
}
if (IsFuncReturnThisType(fd)) {
auto declOfThisType = GetDeclOfThisType(expr);
if (declOfThisType) {
tyVars.merge(::GetAllGenericTys(declOfThisType->GetTy()));
}
} else if (auto ma = DynamicCast<const MemberAccess*>(&expr); ma && ma->baseExpr) {
if (fd.outerDecl && (IsGenericUpperBoundCall(*ma, fd) || fd.GetTy()->HasQuestTy())) {
tyVars.merge(::GetAllGenericTys(fd.outerDecl->GetTy()));
}
auto baseTarget = TypeCheckUtil::GetRealTarget(ma->baseExpr->GetTarget());
bool isNameAccessNeedInfer = baseTarget && baseTarget->IsTypeDecl() && ma->baseExpr->GetTypeArgs().empty();
if (isNameAccessNeedInfer) {
tyVars.merge(::GetAllGenericTys(baseTarget->GetTy()));
}
}
for (auto& mapping : typeMappings) {
mapping = ReduceMultiTypeSubst(typeManager, StaticToTyVars(tyVars), mapping);
}
if (typeMappings.empty()) {
typeMappings.emplace_back(MultiTypeSubst{});
}
}
bool TypeChecker::TypeCheckerImpl::CheckGenericCallCompatible(
ASTContext& ctx, FunctionCandidate& candidate, SubstPack& typeMapping, Ptr<Ty> targetRet)
{
auto& ce = candidate.ce;
auto& fd = candidate.fd;
auto typeMappings = GenerateTypeMappingForCall(ctx, candidate, targetRet);
FilterAndReduceSubstPacks(typeMappings, typeManager);
if (typeMappings.empty()) {
return false;
}
CJC_ASSERT(ce.baseFunc);
std::vector<Ptr<Ty>> paramTysInArgOrder = GetParamTysInArgsOrder(typeManager, ce, fd);
if (paramTysInArgOrder.size() != ce.args.size()) {
return false;
}
std::vector<SubstPack> resMappings;
for (const auto& mts : typeMappings) {
std::set<Ptr<Ty>> usedTys = {fd.GetTy()};
usedTys.merge(GetGenericParamsForCall(ce, fd));
auto mappings = ExpandMultiTypeSubst(mts, usedTys);
resMappings.insert(resMappings.end(), mappings.begin(), mappings.end());
}
size_t matchedCnt = 0;
for (auto mapping = resMappings.begin(); mapping != resMappings.end();) {
auto ds = DiagSuppressor(diag);
bool matched = true;
size_t currentCnt = 0;
for (size_t i = 0; i < paramTysInArgOrder.size(); ++i) {
auto paramTy = paramTysInArgOrder[i];
if (paramTy && paramTy->HasGeneric()) {
paramTy = typeManager.ApplySubstPack(paramTy, *mapping);
}
if (!CheckWithCache(ctx, paramTy, ce.args[i].get())) {
matched = false;
break;
}
if (!CheckUseTrailingClosureWithFunctionType(diag, *ce.args[i], *paramTysInArgOrder[i], fd)) {
matched = false;
break;
}
currentCnt++;
}
matchedCnt = currentCnt > matchedCnt ? currentCnt : matchedCnt;
if (matched || resMappings.size() == 1) {
ds.ReportDiag();
}
mapping = matched ? mapping + 1 : resMappings.erase(mapping);
}
candidate.stat.matchedArgs = static_cast<uint32_t>(matchedCnt);
resMappings = ResolveTypeMappings(typeManager, resMappings, fd);
if (resMappings.size() == 1) {
typeMapping = resMappings.front();
return CheckCandidateConstrains(ce, fd, typeMapping);
}
DiagnoseForMultiMapping(diag, ce, fd, typeMappings, resMappings);
return false;
}
bool TypeChecker::TypeCheckerImpl::CheckAndGetMappingForTypeDecl(
const Expr& baseExpr, const Decl& typeDecl, const Decl& targetDecl, const SubstPack& typeMapping)
{
MultiTypeSubst mts;
MultiTypeSubst mapping;
if (baseExpr.GetTy() && targetDecl.GetTy()) {
mapping = promotion.GetPromoteTypeMapping(*baseExpr.GetTy(), *targetDecl.GetTy());
}
for (auto it : std::as_const(mapping)) {
if (!it.first->IsGeneric()) {
continue;
}
for (auto ty : it.second) {
if (ty->IsGeneric()) {
mts[RawStaticCast<GenericsTy*>(ty)].emplace(typeManager.ApplySubstPack(it.first, typeMapping));
}
}
}
bool valid = true;
std::string diagMessage;
for (auto it : std::as_const(mts)) {
if (it.second.size() > 1) {
diagMessage += (diagMessage.empty() ? "" : std::string(", ")) + "'" + it.first->String() + "' to '" +
Ty::GetTypesToStr(it.second, "', '") + "'";
valid = false;
}
}
if (!valid) {
auto diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_type_inconsistent, baseExpr,
MakeRange(baseExpr.begin, baseExpr.end), typeDecl.GetTy()->String());
diagBuilder.AddNote(diagMessage);
}
return valid;
}
bool TypeChecker::TypeCheckerImpl::CheckCandidateConstrains(
const CallExpr& ce, const FuncDecl& fd, const SubstPack& typeMapping)
{
auto userDefinedTypeArgs = ce.baseFunc->GetTypeArgs();
if (!NeedFurtherInstantiation(userDefinedTypeArgs) && fd.TestAttr(Attribute::GENERIC) &&
!CheckGenericDeclInstantiation(&fd, userDefinedTypeArgs, *ce.baseFunc)) {
return false;
}
bool ignored = !fd.outerDecl || !fd.outerDecl->IsNominalDecl() || !Ty::IsTyCorrect(fd.outerDecl->GetTy());
if (ignored) {
return true;
}
Ptr<Decl> structDecl = fd.outerDecl;
if (auto ma = DynamicCast<MemberAccess*>(ce.baseFunc.get());
ma && ma->baseExpr && Ty::IsTyCorrect(ma->baseExpr->GetTy())) {
Ptr<Ty> baseTy = ma->baseExpr->GetTy();
if (auto res = typeManager.GetExtendDeclByInterface(*baseTy, *structDecl->GetTy())) {
structDecl = *res;
}
if (!structDecl->GetGeneric()) {
return true;
}
auto typeDecl = Ty::GetDeclPtrOfTy<InheritableDecl>(baseTy);
auto typeArgs = ma->baseExpr->GetTypeArgs();
if (!NeedFurtherInstantiation(typeArgs) && structDecl == typeDecl) {
return CheckGenericDeclInstantiation(structDecl, typeArgs, *ma->baseExpr);
}
bool needCheckTypeInconsistency = typeDecl && typeDecl->TestAttr(Attribute::GENERIC) && typeDecl != structDecl;
if (needCheckTypeInconsistency) {
if (!CheckAndGetMappingForTypeDecl(*ma->baseExpr, *typeDecl, *structDecl, typeMapping)) {
return false;
}
}
if (structDecl->astKind == ASTKind::EXTEND_DECL && typeDecl) {
baseTy = ReplaceWithGenericTyInInheritableDecl(baseTy, *structDecl, *typeDecl);
}
auto prRes = promotion.Promote(*baseTy, *structDecl->GetTy());
if (prRes.empty()) {
return true;
}
auto instTy = typeManager.ApplySubstPack(*prRes.begin(), typeMapping);
return Ty::IsTyCorrect(instTy) && CheckGenericDeclInstantiation(structDecl, instTy->typeArgs, *ma->baseExpr);
} else if (IsClassOrEnumConstructor(fd)) {
if (!NeedFurtherInstantiation(userDefinedTypeArgs)) {
return CheckGenericDeclInstantiation(structDecl, userDefinedTypeArgs, *ce.baseFunc);
}
}
return true;
}
bool TypeChecker::TypeCheckerImpl::CheckCallCompatible(ASTContext& ctx, FunctionCandidate& candidate)
{
auto& ce = candidate.ce;
auto& fd = candidate.fd;
std::vector<Ptr<Ty>> paramTysInArgsOrder = GetParamTysInArgsOrder(typeManager, ce, fd);
if (paramTysInArgsOrder.size() != ce.args.size()) {
return false;
}
for (size_t i = 0; i < paramTysInArgsOrder.size(); ++i) {
ce.args[i]->Clear();
if (!CheckWithCache(ctx, paramTysInArgsOrder[i], ce.args[i].get())) {
if (ce.args[i]->ShouldDiagnose() && !CanSkipDiag(*ce.args[i])) {
DiagMismatchedTypes(diag, *ce.args[i], *paramTysInArgsOrder[i]);
}
return false;
}
if (!CheckUseTrailingClosureWithFunctionType(diag, *ce.args[i], *paramTysInArgsOrder[i], fd)) {
return false;
}
if (fd.TestAttr(Attribute::C) && ce.args[i]->GetTy() && ce.args[i]->GetTy()->IsUnit()) {
diag.Diagnose(*ce.args[i], DiagKind::sema_unit_cannot_as_cfunc_arg);
return false;
}
candidate.stat.matchedArgs++;
if (fd.TestAttr(Attribute::FOREIGN) && fd.hasVariableLenArg && i >= fd.funcBody->paramLists[0]->params.size()) {
continue;
}
if (auto st = DynamicCast<StructTy*>(ce.args[i]->GetTy());
st && st->decl->TestAttr(Attribute::C) && st != paramTysInArgsOrder[i]) {
diag.Diagnose(*ce.args[i], DiagKind::sema_cstruct_cannot_autobox, paramTysInArgsOrder[i]->String());
return false;
}
}
return true;
}
void TypeChecker::TypeCheckerImpl::FillEnumTypeArgumentsTy(
const Decl& ctorDecl, const SubstPack& typeMapping, MemberAccess& ma)
{
auto ed = As<ASTKind::ENUM_DECL>(ctorDecl.outerDecl);
if (!ed || !ed->generic) {
return;
}
if (auto ref = DynamicCast<NameReferenceExpr*>(ma.baseExpr.get())) {
if (ref->typeArguments.empty()) {
ref->instTys.clear();
}
if (ref->instTys.empty()) {
for (auto& typeParam : ed->generic->typeParameters) {
(void)ref->instTys.emplace_back(
typeManager.ApplySubstPack(StaticCast<GenericsTy*>(typeParam->GetTy()), typeMapping));
}
}
}
ma.baseExpr->SetTy(typeManager.ApplySubstPack(ed->GetTy(), typeMapping));
auto baseTypeArgs = ma.baseExpr->GetTypeArgs();
bool enumCallNoTyArgs = ma.typeArguments.empty() && baseTypeArgs.empty();
if (enumCallNoTyArgs) {
ma.SetTy(typeManager.ApplySubstPack(ma.GetTy(), typeMapping));
}
}
void TypeChecker::TypeCheckerImpl::FillTypeArgumentsTy(const FuncDecl& fd, const CallExpr& ce, SubstPack& typeMapping)
{
auto generic = GetCurrentGeneric(fd, ce);
if (auto ref = DynamicCast<NameReferenceExpr*>(ce.baseFunc.get()); ref && generic) {
if (ref->typeArguments.empty()) {
ref->instTys.clear();
}
if (ref->instTys.empty()) {
for (auto& typeParam : generic->typeParameters) {
(void)ref->instTys.emplace_back(
typeManager.ApplySubstPack(StaticCast<GenericsTy*>(typeParam->GetTy()), typeMapping));
}
}
}
auto ma = As<ASTKind::MEMBER_ACCESS>(ce.baseFunc.get());
if (!ma || !fd.outerDecl) {
return;
}
if (fd.TestAttr(Attribute::ENUM_CONSTRUCTOR)) {
FillEnumTypeArgumentsTy(fd, typeMapping, *ma);
return;
}
enum A<T>{
a
public func b(x:T){0}
}
let v = A.a.b(1) // <-- inference here
*/
if (auto ma2 = DynamicCast<MemberAccess*>(ma->baseExpr.get());
ma2 && ma2->target && ma2->target->TestAttr(Attribute::ENUM_CONSTRUCTOR)) {
FillEnumTypeArgumentsTy(*ma2->target, typeMapping, *ma2);
}
auto baseDecl = TypeCheckUtil::GetRealTarget(ma->baseExpr->GetTarget());
auto ref = DynamicCast<NameReferenceExpr*>(ma->baseExpr.get());
auto baseGeneric = baseDecl ? baseDecl->GetGeneric() : nullptr;
bool noNeedArguments = !ref || !baseDecl || !baseGeneric;
if (noNeedArguments) {
return;
}
bool typeAccessOmitTypeArgs =
(fd.TestAttr(Attribute::STATIC) || baseDecl->TestAttr(Attribute::ENUM_CONSTRUCTOR)) && ref->instTys.empty();
if (!typeAccessOmitTypeArgs) {
return;
}
if (baseDecl != fd.outerDecl) {
TypeSubst mapping;
auto outerDeclTy = typeManager.GetInstantiatedTy(fd.outerDecl->GetTy(), typeMapping.u2i);
if (baseDecl->GetTy() && fd.outerDecl->GetTy()) {
mapping = MultiTypeSubstToTypeSubst(promotion.GetDowngradeTypeMapping(*baseDecl->GetTy(), *outerDeclTy));
}
typeManager.PackMapping(typeMapping, mapping);
}
for (auto& typeParameter : baseGeneric->typeParameters) {
(void)ref->instTys.emplace_back(
typeManager.ApplySubstPack(StaticCast<GenericsTy*>(typeParameter->GetTy()), typeMapping));
}
ma->baseExpr->SetTy(typeManager.ApplySubstPack(ma->baseExpr->GetTy(), typeMapping));
}
std::vector<Ptr<FuncDecl>> TypeChecker::TypeCheckerImpl::UpdateFuncGenericType(
ASTContext& ctx, FunctionMatchingUnit& fmu, CallExpr& ce)
{
auto funcTy = RawStaticCast<FuncTy*>(fmu.fd.GetTy());
ReplaceIdealTypeInSubstPack(fmu.typeMapping);
if (!ce.desugarArgs.has_value()) {
return {};
}
for (size_t i = 0; i < ce.desugarArgs.value().size(); ++i) {
auto& arg = ce.desugarArgs.value()[i];
if (!arg->expr || !arg->expr->GetTy()) {
continue;
}
if (arg->expr->GetTy()->HasIdealTy()) {
return {};
}
}
if (auto ref = DynamicCast<NameReferenceExpr*>(ce.baseFunc.get())) {
ref->matchedParentTy = typeManager.ApplySubstPack(ref->matchedParentTy, fmu.typeMapping);
}
if (auto ma = DynamicCast<MemberAccess*>(ce.baseFunc.get()); ma && ma->matchedParentTy) {
if (ma->matchedParentTy->HasGeneric() && !ma->baseExpr->GetTy()->IsGeneric()) {
auto prTys = promotion.Promote(*ma->baseExpr->GetTy(), *ma->matchedParentTy);
auto updatedTy = prTys.empty() ? TypeManager::GetInvalidTy() : *prTys.begin();
if (Ty::IsTyCorrect(updatedTy)) {
ma->matchedParentTy = updatedTy;
}
}
}
ce.baseFunc->SetTy(typeManager.ApplySubstPack(funcTy, fmu.typeMapping));
auto rawTy = GetCallTy(ctx, ce, fmu.fd);
ce.SetTy(typeManager.ApplySubstPack(rawTy, fmu.typeMapping));
FillTypeArgumentsTy(fmu.fd, ce, fmu.typeMapping);
return {&fmu.fd};
}
void TypeChecker::TypeCheckerImpl::ReplaceIdealTypeInSubstPack(SubstPack& maps)
{
for (auto& [tv, tys] : maps.inst) {
auto tys0 = tys;
tys.clear();
for (auto& ty0 : tys0) {
Ptr<Ty> ty1 = typeManager.ReplaceIdealTy(ty0);
tys.insert(ty1);
}
}
}
OwnedPtr<FunctionMatchingUnit> TypeChecker::TypeCheckerImpl::CheckCandidate(
ASTContext& ctx, FunctionCandidate& candidate, Ptr<Ty> targetRet, SubstPack& typeMapping)
{
auto& ce = candidate.ce;
auto& fd = candidate.fd;
bool wrongNormalArgSize = !fd.hasVariableLenArg &&
fd.funcBody->paramLists.front()->params.size() != ce.args.size() && !HasSamePositionalArgsSize(fd, ce);
if (wrongNormalArgSize) {
DiagWrongNumberOfArguments(diag, ce, fd);
return nullptr;
}
candidate.stat.argNameValid = CheckArgsWithParamName(ce, fd);
std::vector<Ptr<Ty>> paramTyInArgOrder = GetParamTysInArgsOrder(typeManager, ce, fd);
if (paramTyInArgOrder.size() != ce.args.size()) {
return nullptr;
}
if (IsGenericCall(ctx, candidate.ce, candidate.fd)) {
if (!CheckGenericCallCompatible(ctx, candidate, typeMapping, targetRet)) {
return nullptr;
}
} else if (!CheckCallCompatible(ctx, candidate)) {
return nullptr;
}
if (NeedSynOnUsed(fd)) {
Synthesize({ctx, SynPos::NONE}, &fd);
}
if (!Ty::IsTyCorrect(fd.GetTy()) || !fd.GetTy()->IsFunc()) {
return nullptr;
}
if (targetRet) {
auto retTy = GetCallTy(ctx, candidate.ce, fd);
DynamicBindingThisType(*ce.baseFunc, fd, typeMapping);
auto instRet = typeManager.ApplySubstPack(retTy, typeMapping);
if (!instRet->HasIdealTy() && !typeManager.IsSubtype(instRet, targetRet)) {
if (!ce.sourceExpr) {
DiagMismatchedTypesWithFoundTy(diag, ce, *targetRet, *instRet);
}
return nullptr;
}
}
return candidate.stat.argNameValid ? MakeOwned<FunctionMatchingUnit>(fd, paramTyInArgOrder, typeMapping) : nullptr;
}
std::vector<Ptr<FuncDecl>> TypeChecker::TypeCheckerImpl::ReorderCallArgument(
ASTContext& ctx, FunctionMatchingUnit& fmu, CallExpr& ce)
{
std::vector<Ptr<FuncArg>> args;
auto defaultArgs = std::vector<OwnedPtr<FuncArg>>();
for (auto& param : fmu.fd.funcBody->paramLists[0]->params) {
if (param->TestAttr(Attribute::HAS_INITIAL)) {
auto arg = CreateFuncArgForOptional(*param);
if (!fmu.fd.TestAttr(Attribute::TOOL_ADD)) {
arg->EnableAttr(Attribute::HAS_INITIAL);
} else if (Ty::IsTyCorrect(arg->GetTy()) && !Ty::IsTyCorrect(arg->expr->GetTy())) {
auto ret = Check(ctx, arg->GetTy(), arg->expr.get());
CJC_ASSERT(ret);
}
SpreadInstantiationTy(*arg, fmu.typeMapping);
args.push_back(arg.get());
defaultArgs.push_back(std::move(arg));
} else {
args.push_back(nullptr);
}
}
SortCallArgumentByParamOrder(fmu.fd, ce, args);
ce.desugarArgs = args;
ce.defaultArgs = std::move(defaultArgs);
defaultArgs.clear();
auto funcTy = RawStaticCast<FuncTy*>(fmu.fd.GetTy());
if (IsGenericCall(ctx, ce, fmu.fd)) {
return UpdateFuncGenericType(ctx, fmu, ce);
}
for (size_t i = 0; i < funcTy->paramTys.size(); ++i) {
auto& arg = ce.desugarArgs.value()[i];
if (arg->TestAttr(Attribute::HAS_INITIAL)) {
continue;
}
if (arg->GetTy() && arg->GetTy()->IsIdeal()) {
return {};
}
}
ce.baseFunc->SetTy(funcTy);
ce.SetTy(GetCallTy(ctx, ce, fmu.fd));
return {&fmu.fd};
}
Ty* TypeChecker::TypeCheckerImpl::GetCallTy(ASTContext& ctx, const CallExpr& ce, const FuncDecl& target) const
{
auto targetRetTy = StaticCast<FuncTy>(target.GetTy())->retTy;
auto ret = targetRetTy;
if (Is<ClassThisTy>(targetRetTy)) {
if (auto ma = DynamicCast<MemberAccess>(&*ce.baseFunc)) {
if (target.TestAttr(Attribute::STATIC)) {
return target.outerDecl->GetTy();
}
return ma->baseExpr->GetTy();
}
auto sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ce.scopeName);
CJC_ASSERT(sym->node);
if (auto classTy = DynamicCast<ClassTy>(sym->node->GetTy())) {
return typeManager.GetClassThisTy(*classTy->decl, classTy->typeArgs);
}
return sym->node->GetTy();
}
return ret;
}
TypeChecker::TypeCheckerImpl::FuncTyPair TypeChecker::TypeCheckerImpl::CollectValidFuncTys(
ASTContext& ctx, std::vector<Ptr<FuncDecl>>& funcs, Expr& expr, Ptr<FuncTy> targetTy)
{
FilterShadowedFunc(funcs);
std::vector<std::tuple<Ptr<AST::FuncDecl>, Ptr<AST::Ty>, TypeSubst>> candidates;
auto typeArgs = expr.GetTypeArgs();
bool genericIgnored = false;
auto ds = DiagSuppressor(diag);
for (auto fd : funcs) {
if (fd == nullptr || !Ty::IsTyCorrect(fd->GetTy()) || !fd->GetTy()->IsFunc()) {
continue;
}
SubstPack mts;
if (auto generic = fd->GetGeneric()) {
genericIgnored = genericIgnored || typeArgs.empty();
if (typeArgs.empty() || !CheckGenericDeclInstantiation(fd, typeArgs, expr)) {
continue;
}
typeManager.PackMapping(mts, GenerateTypeMappingByTyArgs(typeArgs, *generic));
}
ReplaceTarget(&expr, fd);
MergeSubstPack(mts, GenerateGenericTypeMapping(ctx, expr));
std::vector<MultiTypeSubst> typeMappings{typeManager.ZipSubstPack(mts)};
FilterTypeMappings(expr, *fd, typeMappings);
CJC_ASSERT(!typeMappings.empty());
std::set<Ptr<Ty>> usedTys = {fd->GetTy()};
usedTys.merge(GetGenericParamsForDecl(*fd));
std::set<TypeSubst> resMappings = ExpandMultiTypeSubst(typeMappings[0], usedTys);
auto thisCd = DynamicCast<ClassDecl*>(GetDeclOfThisType(expr));
bool hasThisRet = thisCd && Ty::IsTyCorrect(thisCd->GetTy()) && IsFuncReturnThisType(*fd);
auto genericParams = GetAllGenericTys(expr, *fd);
for (auto& mapping : resMappings) {
if (!genericParams.empty() && mapping.empty()) {
genericIgnored = true;
break;
}
auto instTy = typeManager.GetInstantiatedTy(fd->GetTy(), mapping);
if (targetTy && !typeManager.IsFuncParametersSubtype(*RawStaticCast<FuncTy*>(instTy), *targetTy)) {
continue;
}
if (NeedSynOnUsed(*fd) && Synthesize({ctx, SynPos::NONE}, fd) &&
(!Ty::IsTyCorrect(fd->GetTy()) || fd->GetTy()->HasQuestTy())) {
DiagUnableToInferReturnType(diag, *fd, expr);
break;
}
instTy = typeManager.GetInstantiatedTy(fd->GetTy(), mapping);
CJC_ASSERT(instTy->IsFunc());
if (hasThisRet) {
auto realThisTy = typeManager.GetInstantiatedTy(thisCd->GetTy(), mapping);
instTy = typeManager.GetFunctionTy(RawStaticCast<FuncTy*>(instTy)->paramTys, realThisTy);
}
candidates.emplace_back(std::make_tuple(fd, instTy, mapping));
}
}
return {genericIgnored, candidates};
}
bool TypeChecker::TypeCheckerImpl::HasBaseOfPlaceholderTy(ASTContext& ctx, Ptr<Node> n)
{
if (!n || typeManager.GetUnsolvedTyVars().empty()) {
return false;
}
if (auto ma = DynamicCast<MemberAccess*>(n)) {
SetIsNotAlone(*ma->baseExpr);
if (HasBaseOfPlaceholderTy(ctx, ma->baseExpr)) {
return true;
}
}
bool wasOK = Ty::IsTyCorrect(n->GetTy());
if (Ty::IsInitialTy(n->GetTy())) {
DiagSuppressor ds(diag);
ctx.targetTypeMap[n] = TypeManager::GetQuestTy();
SynthesizeWithCache({ctx, SynPos::EXPR_ARG}, n);
ctx.targetTypeMap[n] = nullptr;
}
if (n->GetTy()->IsPlaceholder()) {
return true;
}
if (!wasOK) {
n->Clear();
}
return false;
}
std::vector<std::set<Ptr<Ty>>> TypeChecker::TypeCheckerImpl::GetArgTyPossibilities(ASTContext& ctx, CallExpr& ce)
{
std::vector<std::set<Ptr<Ty>>> ceArgs;
for (auto& arg : ce.args) {
std::vector<Ptr<Ty>> argCandidates;
if (!arg->expr) {
continue;
}
if (!arg->expr->IsReferenceExpr() || HasBaseOfPlaceholderTy(ctx, arg->expr)) {
argCandidates.emplace_back(nullptr);
ceArgs.emplace_back(Utils::VecToSet(argCandidates));
continue;
}
ctx.targetTypeMap[arg->expr.get()] = TypeManager::GetQuestTy();
SynthesizeWithCache({ctx, SynPos::EXPR_ARG}, arg.get());
ctx.targetTypeMap[arg->expr.get()] = nullptr;
auto targets = GetFuncTargets(*arg->expr);
if (!Ty::IsTyCorrect(arg->GetTy()) || targets.empty()) {
argCandidates.emplace_back(arg->GetTy());
ceArgs.emplace_back(Utils::VecToSet(argCandidates));
continue;
}
auto [genericIgnored, candidates] = CollectValidFuncTys(ctx, targets, *arg->expr);
for (auto [_, ty, __] : candidates) {
argCandidates.emplace_back(ty);
}
if (argCandidates.empty()) {
if (genericIgnored) {
diag.Diagnose(*arg, DiagKind::sema_generic_type_without_type_argument);
return {};
}
argCandidates.emplace_back(arg->GetTy());
}
ceArgs.emplace_back(Utils::VecToSet(argCandidates));
}
return ceArgs;
}
std::vector<std::vector<Ptr<Ty>>> TypeChecker::TypeCheckerImpl::GetArgsCombination(ASTContext& ctx, CallExpr& ce)
{
std::vector<std::set<Ptr<Ty>>> ceArgs = GetArgTyPossibilities(ctx, ce);
if (ceArgs.empty()) {
return {};
}
std::vector<std::vector<Ptr<Ty>>> combinations;
std::function<void(std::vector<Ptr<Ty>>&)> fillCombination;
size_t numArgs = ceArgs.size();
fillCombination = [&fillCombination, &ceArgs, &numArgs, &combinations](std::vector<Ptr<Ty>>& argSet) {
size_t cur = argSet.size();
for (auto& it : ceArgs[cur]) {
argSet.emplace_back(it);
if (cur + 1 == numArgs) {
combinations.emplace_back(argSet);
} else {
fillCombination(argSet);
}
argSet.pop_back();
}
};
std::vector<Ptr<Ty>> argSet;
fillCombination(argSet);
return combinations;
}
std::vector<Ptr<FuncDecl>> TypeChecker::TypeCheckerImpl::CheckFunctionMatch(
ASTContext& ctx, FunctionCandidate& candidate, Ptr<Ty> target, SubstPack& typeMapping)
{
auto matched = CheckCandidate(ctx, candidate, target, typeMapping);
if (!matched) {
return {};
}
if (candidate.fd.outerDecl && candidate.fd.outerDecl->astKind == ASTKind::INTERFACE_DECL) {
if (auto ref = DynamicCast<NameReferenceExpr*>(candidate.ce.baseFunc.get())) {
ref->matchedParentTy = candidate.fd.outerDecl->GetTy();
}
}
ReInferCallArgs(ctx, candidate.ce, *matched);
return ReorderCallArgument(ctx, *matched, candidate.ce);
}
void TypeChecker::TypeCheckerImpl::RemoveShadowedFunc(
const FuncDecl& fd, int64_t currentLevel, int64_t targetLevel, std::vector<Ptr<FuncDecl>>& funcs)
{
if (targetLevel >= currentLevel) {
return;
}
auto funcTy = DynamicCast<FuncTy*>(fd.GetTy());
auto paramTys = funcTy ? funcTy->paramTys : GetParamTys(fd);
auto isFuncSignatureSame = [this, &fd, ¶mTys](auto& target) -> bool {
if (fd.TestAttr(Attribute::GENERIC) != target->TestAttr(Attribute::GENERIC)) {
return false;
}
auto targetTy = DynamicCast<FuncTy*>(target->GetTy());
auto targetParamTys = targetTy ? targetTy->paramTys : GetParamTys(*target);
return typeManager.IsFuncParameterTypesIdentical(targetParamTys, paramTys);
};
funcs.erase(std::remove_if(funcs.begin(), funcs.end(), isFuncSignatureSame), funcs.end());
}
void TypeChecker::TypeCheckerImpl::FilterShadowedFunc(std::vector<Ptr<FuncDecl>>& candidates)
{
auto calScopeLevel = [](auto fd) {
return fd->TestAttr(Attribute::IMPORTED) && fd->TestAttr(Attribute::GLOBAL)
? -1
: (fd->TestAttr(Attribute::ENUM_CONSTRUCTOR) ? 0 : static_cast<int64_t>(fd->scopeLevel));
};
std::unordered_map<int64_t, std::vector<Ptr<FuncDecl>>> levelMap;
auto checkPriority = [&levelMap, &calScopeLevel](auto& fd) {
if (fd == nullptr) {
return;
}
int64_t scopeLevel = calScopeLevel(fd);
auto found = levelMap.find(scopeLevel);
if (found == levelMap.end()) {
std::vector<Ptr<FuncDecl>> set{fd};
levelMap.emplace(std::make_pair(scopeLevel, set));
} else {
found->second.emplace_back(fd);
}
};
std::for_each(candidates.begin(), candidates.end(), checkPriority);
for (auto& [i, fdSet] : levelMap) {
int64_t level = i;
for (auto& fd : fdSet) {
for (auto& it : levelMap) {
auto scopeLevel = it.first;
auto& funcs = it.second;
RemoveShadowedFunc(*fd, level, scopeLevel, funcs);
}
}
}
Utils::EraseIf(
candidates, [&levelMap, &calScopeLevel](auto fd) { return fd && !Utils::In(fd, levelMap[calScopeLevel(fd)]); });
}
void TypeChecker::TypeCheckerImpl::FilterExtendImplAbstractFunc(std::vector<Ptr<FuncDecl>>& candidates)
{
std::set<Ptr<FuncDecl>> implementedOrOverridenFuncs;
for (auto& fd1 : candidates) {
if (!(fd1->TestAttr(Attribute::ABSTRACT) && Ty::IsTyCorrect(fd1->GetTy()) && fd1->outerDecl &&
fd1->outerDecl->astKind == ASTKind::INTERFACE_DECL)) {
continue;
}
auto id = StaticAs<ASTKind::INTERFACE_DECL>(fd1->outerDecl);
for (auto it2 = candidates.begin(); it2 != candidates.end(); ++it2) {
auto fd2 = *it2;
if (!(Ty::IsTyCorrect(fd2->GetTy()) && fd2->outerDecl && fd2->outerDecl->astKind == ASTKind::EXTEND_DECL)) {
continue;
}
auto ed = StaticAs<ASTKind::EXTEND_DECL>(fd2->outerDecl);
auto [flag, typeMapping] = CheckAndObtainTypeMappingBetweenInterfaceAndExtend(*ed, *id);
if (!flag) {
continue;
}
auto funcTy1 = StaticCast<FuncTy*>(typeManager.GetInstantiatedTy(fd1->GetTy(), typeMapping));
auto funcTy2 = StaticCast<FuncTy*>(fd2->GetTy());
if (typeManager.IsFuncParameterTypesIdentical(funcTy1->paramTys, funcTy2->paramTys) &&
typeManager.IsSubtype(funcTy2->retTy, funcTy1->retTy)) {
implementedOrOverridenFuncs.insert(fd1);
}
}
}
if (implementedOrOverridenFuncs.empty()) {
return;
}
for (auto it = candidates.begin(); it != candidates.end();) {
if (implementedOrOverridenFuncs.find(*it) != implementedOrOverridenFuncs.end()) {
it = candidates.erase(it);
} else {
++it;
}
}
}
void TypeChecker::TypeCheckerImpl::FilterIncompatibleCandidatesForCall(
const CallExpr& ce, std::vector<Ptr<FuncDecl>>& candidates)
{
if (candidates.empty()) {
return;
}
Ptr<FuncDecl> badFd = candidates.front();
auto notMatch = [this, &badFd, &ce](const Ptr<FuncDecl> fd) {
CJC_ASSERT(fd && fd->funcBody && !fd->funcBody->paramLists.empty());
if (fd->hasVariableLenArg) {
return false;
}
if (IsPossibleVariadicFunction(*fd, ce)) {
return false;
}
auto ds = DiagSuppressor(diag);
if (HasSamePositionalArgsSize(*fd, ce) && CheckArgsWithParamName(ce, *fd)) {
return false;
}
badFd = fd;
return true;
};
candidates = SyntaxFilterCandidates(ce, candidates);
candidates.erase(std::remove_if(candidates.begin(), candidates.end(), notMatch), candidates.end());
if (!candidates.empty()) {
return;
}
size_t initializedParamSize = GetInitializedlNameParamSize(*badFd);
size_t paramsSize = badFd->funcBody->paramLists.front()->params.size();
if ((ce.args.size() >= paramsSize - initializedParamSize) && (ce.args.size() <= paramsSize)) {
CheckArgsWithParamName(ce, *badFd);
} else {
DiagWrongNumberOfArguments(diag, ce, *badFd);
}
}
std::vector<Ptr<FuncDecl>> TypeChecker::TypeCheckerImpl::FilterCandidates(
const ASTContext& ctx, const std::vector<Ptr<FuncDecl>>& inCandidates, const CallExpr& ce)
{
if (inCandidates.empty()) {
return inCandidates;
}
std::vector<Ptr<FuncDecl>> candidates = inCandidates;
auto isInvalidAndInvisible = [this, &ctx, &ce](Ptr<const FuncDecl> fd) {
bool invalid = !fd || !fd->funcBody || fd->funcBody->paramLists.empty();
if (invalid) {
return true;
}
Symbol* sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ce.baseFunc->scopeName);
return !IsLegalAccess(sym, *fd, *ce.baseFunc, importManager, typeManager);
};
candidates.erase(std::remove_if(candidates.begin(), candidates.end(), isInvalidAndInvisible), candidates.end());
FilterIncompatibleCandidatesForCall(ce, candidates);
auto paramInvalid = [](Ptr<FuncDecl> fd) {
auto funcTy = DynamicCast<FuncTy*>(fd->GetTy());
if (!Ty::IsTyCorrect(funcTy)) {
return true;
}
if (funcTy->paramTys.size() != fd->funcBody->paramLists[0]->params.size()) {
return true;
}
return false;
};
candidates.erase(std::remove_if(candidates.begin(), candidates.end(), paramInvalid), candidates.end());
FilterShadowedFunc(candidates);
FilterExtendImplAbstractFunc(candidates);
return candidates;
}
std::vector<Ptr<FuncDecl>> TypeChecker::TypeCheckerImpl::FilterCandidatesWithReExport(
const ASTContext& ctx, const std::vector<Ptr<FuncDecl>>& inCandidates, const CallExpr& ce)
{
if (inCandidates.empty()) {
return {};
}
auto first = inCandidates.begin();
while (first != inCandidates.end()) {
if (*first == nullptr) {
break;
}
++first;
}
if (first == inCandidates.end()) {
return FilterCandidates(ctx, inCandidates, ce);
}
std::vector<Ptr<FuncDecl>> reExportFuncTargets;
std::vector<Ptr<FuncDecl>> importedFuncTargets;
std::copy(inCandidates.begin(), first, std::back_inserter(reExportFuncTargets));
std::copy(++first, inCandidates.end(), std::back_inserter(importedFuncTargets));
importedFuncTargets = FilterCandidates(ctx, importedFuncTargets, ce);
reExportFuncTargets = FilterCandidates(ctx, reExportFuncTargets, ce);
if (!(importedFuncTargets.empty() && reExportFuncTargets.empty())) {
std::copy(importedFuncTargets.begin(), importedFuncTargets.end(), std::back_inserter(reExportFuncTargets));
return reExportFuncTargets;
}
if (importedFuncTargets.empty()) {
return reExportFuncTargets;
}
return importedFuncTargets;
}
using FuncCompT = std::function<bool(Ptr<FuncDecl> const, Ptr<FuncDecl> const)>;
std::vector<Ptr<FuncDecl>> TypeChecker::TypeCheckerImpl::GetOrderedCandidates(const ASTContext& ctx, const CallExpr& ce,
std::vector<Ptr<FuncDecl>>& candidates, std::unordered_map<Ptr<FuncDecl>, int64_t>& fdScopeLevelMap) const
{
std::vector<Ptr<FuncDecl>> reExportAndCurrentPkgFuncTargets;
std::vector<Ptr<FuncDecl>> importedFuncTargets;
for (Ptr<FuncDecl> funcDecl : candidates) {
CJC_NULLPTR_CHECK(funcDecl);
if (funcDecl->isReExportedOrRenamed) {
reExportAndCurrentPkgFuncTargets.emplace_back(funcDecl);
} else {
importedFuncTargets.emplace_back(funcDecl);
}
}
bool reExport = true;
FuncCompT scopeLevelCompare = [&ctx, &ce, &reExport, &fdScopeLevelMap](auto fd1, auto fd2) -> bool {
auto scopeCal = [&ctx, &ce, &reExport](const FuncDecl& fd) -> int64_t {
if (!fd.IsSamePackage(ce) && fd.TestAttr(Attribute::GLOBAL) &&
(!fd.outerDecl || fd.TestAttr(Attribute::ENUM_CONSTRUCTOR))) {
return reExport ? 0 : -1;
}
if (fd.TestAttr(Attribute::ENUM_CONSTRUCTOR)) {
return CalculateEnumCtorScopeLevel(ctx, ce, fd);
}
return static_cast<int64_t>(fd.scopeLevel);
};
int64_t scopeLevel1 = scopeCal(*fd1);
fdScopeLevelMap[fd1] = scopeLevel1;
int64_t scopeLevel2 = scopeCal(*fd2);
fdScopeLevelMap[fd2] = scopeLevel2;
if (scopeLevel1 != scopeLevel2) {
return scopeLevel1 > scopeLevel2;
} else if (fd1->begin.fileID != fd2->begin.fileID) {
return fd1->begin.fileID < fd2->begin.fileID;
} else {
return fd1->begin < fd2->begin;
}
};
size_t reExportSize = reExportAndCurrentPkgFuncTargets.size();
std::sort(reExportAndCurrentPkgFuncTargets.begin(), reExportAndCurrentPkgFuncTargets.end(), scopeLevelCompare);
reExport = false;
if (importedFuncTargets.size() != 1) {
std::sort(importedFuncTargets.begin(), importedFuncTargets.end(), scopeLevelCompare);
} else {
fdScopeLevelMap[importedFuncTargets[0]] =
importedFuncTargets[0]->TestAttr(Attribute::ENUM_CONSTRUCTOR) ? 0 : -1;
}
std::copy(
importedFuncTargets.begin(), importedFuncTargets.end(), std::back_inserter(reExportAndCurrentPkgFuncTargets));
FuncCompT cmpScope = [&fdScopeLevelMap](auto fd1, auto fd2) { return fdScopeLevelMap[fd1] > fdScopeLevelMap[fd2]; };
std::inplace_merge(reExportAndCurrentPkgFuncTargets.begin(),
std::next(reExportAndCurrentPkgFuncTargets.begin(), static_cast<long>(reExportSize)),
reExportAndCurrentPkgFuncTargets.end(), cmpScope);
return reExportAndCurrentPkgFuncTargets;
}
std::vector<Ptr<FuncDecl>> TypeChecker::TypeCheckerImpl::MatchFunctionForCall(
ASTContext& ctx, std::vector<Ptr<FuncDecl>>& candidates, CallExpr& ce, Ptr<Ty> target, SubstPack& typeMapping)
{
TyVarScope ts(typeManager);
std::vector<std::vector<Ptr<Ty>>> argCombinations = GetArgsCombination(ctx, ce);
if (!ce.args.empty() && argCombinations.empty()) {
return {};
}
auto filterSum = [this, &ce](std::vector<Ptr<FuncDecl>> ret) {
if (auto ma = DynamicCast<MemberAccess*>(ce.baseFunc.get())) {
if (ma->baseExpr->GetTy()->IsPlaceholder() && ret.size() == 1) {
FilterSumUpperbound(*ma, *RawStaticCast<GenericsTy*>(ma->baseExpr->GetTy()), *ret[0]);
}
}
return ret;
};
if (candidates.size() == 1) {
FunctionCandidate candidate{*candidates[0], ce, argCombinations};
return filterSum(CheckFunctionMatch(ctx, candidate, target, typeMapping));
}
std::unordered_map<Ptr<FuncDecl>, int64_t> fdScopeLevelMap;
auto orderedCandidates = GetOrderedCandidates(ctx, ce, candidates, fdScopeLevelMap);
std::vector<OwnedPtr<FunctionMatchingUnit>> legals;
std::vector<FunctionMatchingUnit> illegals;
int64_t preScopeLevel = fdScopeLevelMap[orderedCandidates[0]];
auto cs = PData::CommitScope(typeManager.constraints);
for (size_t i = 0; i < orderedCandidates.size(); ++i) {
auto ds = DiagSuppressor(diag);
auto curScopeLevel = fdScopeLevelMap[orderedCandidates[i]];
if (curScopeLevel != preScopeLevel && !legals.empty()) {
break;
}
FunctionCandidate candidate{*orderedCandidates[i], ce, argCombinations};
auto ret = CheckCandidate(ctx, candidate, target, typeMapping);
if (!ret || (!legals.empty() && legals.back()->id == static_cast<int64_t>(i))) {
illegals.emplace_back(*orderedCandidates[i], std::vector<Ptr<AST::Ty>>(), SubstPack());
illegals.back().diags = std::make_pair(ds.GetSuppressedDiag(), candidate.stat);
PData::Reset(typeManager.constraints);
continue;
}
ret->ver = PData::Stash<Constraint, CstVersionID>(typeManager.constraints);
ret->diags = std::make_pair(ds.GetSuppressedDiag(), candidate.stat);
ret->id = static_cast<int64_t>(i);
preScopeLevel = curScopeLevel;
legals.emplace_back(std::move(ret));
}
return filterSum(CheckMatchResult(ctx, ce, legals, illegals));
}
void TypeChecker::TypeCheckerImpl::ReInferCallArgs(
ASTContext& ctx, const CallExpr& ce, const FunctionMatchingUnit& legal)
{
for (size_t i = 0; i < legal.tysInArgOrder.size(); ++i) {
auto instTy = typeManager.ApplySubstPack(legal.tysInArgOrder[i], legal.typeMapping);
(void)CheckWithEffectiveCache(ctx, instTy, ce.args[i].get(), false);
}
}
void TypeChecker::TypeCheckerImpl::RecoverCallArgs(
ASTContext& ctx, const CallExpr& ce, const std::vector<Ptr<Ty>>& argsTys)
{
if (argsTys.empty() || argsTys.size() != ce.args.size()) {
return;
}
if (ce.resolvedFunction) {
UpdateCallTargetsForLSP(ce, *ce.resolvedFunction);
}
for (size_t i = 0; i < argsTys.size(); ++i) {
(void)CheckWithEffectiveCache(ctx, argsTys[i], ce.args[i].get(), false);
}
}
namespace {
* Choose the reasonable set of diagnoses to report again when their is not matching function.
* @param ce The callExpr need to be resolved.
* @param diagnoses The vector of diagnostics of all function candidates and their count of matched arguments.
*/
void CheckEmptyMatchResult(DiagnosticEngine& diag, CallExpr& ce,
std::vector<FunctionMatchingUnit>& illegals)
{
if (illegals.empty()) {
return;
}
std::vector<std::vector<Diagnostic>> diags;
uint32_t maxMatched = 0;
bool hasValid = false;
for (size_t i = 0; i < illegals.size(); ++i) {
auto [diagInfo, stat] = illegals[i].diags;
if (stat.matchedArgs > maxMatched) {
maxMatched = stat.matchedArgs;
diags.clear();
hasValid = stat.argNameValid;
diags.emplace_back(diagInfo);
} else if (stat.matchedArgs == maxMatched) {
if (stat.argNameValid && !hasValid) {
hasValid = true;
diags.clear();
}
if (stat.argNameValid == hasValid) {
diags.emplace_back(diagInfo);
}
}
}
auto hasCallIrrelatedDiag = [&ce](const std::vector<Diagnostic>& diags) {
return std::any_of(diags.begin(), diags.end(), [&ce](const auto& info) {
return info.start != ce.begin && std::all_of(ce.args.begin(), ce.args.end(), [&info](const auto& arg) {
auto pos = info.start.IsZero() ? info.mainHint.range.begin : info.start;
return !pos.IsZero() && pos != arg->begin;
});
});
};
size_t idx = 0;
for (size_t i = 0; i < diags.size(); ++i) {
if (hasCallIrrelatedDiag(diags[i])) {
idx = i;
break;
}
}
std::for_each(diags[idx].begin(), diags[idx].end(), [&diag](auto info) { diag.Diagnose(info); });
}
}
std::vector<Ptr<FuncDecl>> TypeChecker::TypeCheckerImpl::CheckMatchResult(ASTContext& ctx, CallExpr& ce,
std::vector<OwnedPtr<FunctionMatchingUnit>>& legals, std::vector<FunctionMatchingUnit>& illegals)
{
if (legals.empty()) {
CheckEmptyMatchResult(diag, ce, illegals);
return {};
}
uint64_t id;
if (legals.size() == 1) {
id = 0;
} else {
std::vector<Ptr<FuncDecl>> result;
std::for_each(legals.begin(), legals.end(), [&result](auto& it) { result.emplace_back(&it->fd); });
auto hasEnum = std::find_if(
result.begin(), result.end(), [](auto& fd) { return fd->TestAttr(Attribute::ENUM_CONSTRUCTOR); });
if (hasEnum != result.end()) {
return result;
}
InstantiatePartOfTheGenericParameters(legals);
auto indexRes = ResolveOverload(legals, ce);
if (indexRes.size() == 1) {
id = indexRes[0];
} else {
return (indexRes.size() > 1) ? result : std::vector<Ptr<FuncDecl>>();
}
}
bool funcHasError = false;
PData::Apply(typeManager.constraints, legals[id]->ver);
for (const Diagnostic& funcDiag : legals[id]->diags.first) {
if (funcDiag.diagSeverity == DiagSeverity::DS_ERROR) {
funcHasError = true;
}
diag.Diagnose(funcDiag);
}
if (funcHasError) {
return {};
}
if (legals[id]->fd.outerDecl && legals[id]->fd.outerDecl->astKind == ASTKind::INTERFACE_DECL) {
if (auto ref = DynamicCast<NameReferenceExpr*>(ce.baseFunc.get())) {
ref->matchedParentTy = legals[id]->fd.outerDecl->GetTy();
}
}
ReInferCallArgs(ctx, ce, *legals[id]);
auto ret = ReorderCallArgument(ctx, *legals[id], ce);
return ret;
}
static std::optional<std::vector<Ptr<Ty>>> CheckCallArgs(
DiagnosticEngine& diag, TypeManager& tyMgr, CallExpr& ce, FuncTy& funcTy)
{
auto paramTys = funcTy.paramTys;
if (funcTy.hasVariableLenArg && ce.args.size() > funcTy.paramTys.size()) {
size_t varargsSize = ce.args.size() - funcTy.paramTys.size();
for (size_t i = 0; i < varargsSize; i++) {
paramTys.push_back(funcTy.isC ? tyMgr.GetCTypeTy() : tyMgr.GetAnyTy());
}
}
if (paramTys.size() != ce.args.size()) {
DiagWrongNumberOfArguments(diag, ce, paramTys);
return std::nullopt;
}
for (auto& arg : ce.args) {
if (!arg->name.Empty()) {
diag.Diagnose(*arg, DiagKind::sema_unsupport_named_argument);
}
}
return paramTys;
}
bool TypeChecker::TypeCheckerImpl::CheckFuncPtrCall(ASTContext& ctx, Ptr<Ty> target, CallExpr& ce, FuncTy& funcTy)
{
if (IsValidCFuncConstructorCall(ce)) {
return true;
}
CJC_NULLPTR_CHECK(ce.baseFunc);
auto baseTarget = ce.baseFunc->GetTarget();
if (baseTarget && baseTarget->astKind == ASTKind::TYPE_ALIAS_DECL) {
auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_called_object, *ce.baseFunc);
builder.AddNote("cannot call a type alias for a function type");
return false;
}
auto paramTysOption = CheckCallArgs(diag, typeManager, ce, funcTy);
if (!paramTysOption.has_value()) {
return false;
}
auto paramTys = paramTysOption.value();
SubstPack typeMapping;
if (funcTy.HasGeneric()) {
typeMapping = GenerateGenericTypeMapping(ctx, *ce.baseFunc);
}
for (size_t i = 0; i < paramTys.size(); ++i) {
auto param = typeManager.ApplySubstPack(paramTys[i], typeMapping);
if (!param) {
return false;
}
if (!Check(ctx, param, ce.args[i].get())) {
return false;
}
}
for (size_t i = 0; i < paramTys.size(); ++i) {
auto param = typeManager.ApplySubstPack(paramTys[i], typeMapping);
if (!ce.args[i]->expr || !ce.args[i]->GetTy()->HasIdealTy()) {
continue;
}
(void)Check(ctx, param, ce.args[i]->expr.get());
if (ce.args[i]->expr->GetTy()->HasIdealTy()) {
if (ce.ShouldDiagnose()) {
diag.Diagnose(ce, DiagKind::sema_parameters_and_arguments_mismatch);
}
return false;
}
ce.args[i]->SetTy(ce.args[i]->expr->GetTy());
}
ce.SetTy(typeManager.ApplySubstPack(funcTy.retTy, typeMapping));
if (target && !typeManager.IsSubtype(ce.GetTy(), target)) {
DiagMismatchedTypes(diag, ce, *target);
return false;
} else {
ce.callKind = CallKind::CALL_FUNCTION_PTR;
return true;
}
}
Ptr<Ty> TypeChecker::TypeCheckerImpl::SynCallExpr(ASTContext& ctx, CallExpr& ce)
{
if (!ChkCallExpr(ctx, nullptr, ce)) {
return TypeManager::GetInvalidTy();
}
return ce.GetTy();
}
Ptr<Ty> TypeChecker::TypeCheckerImpl::SynTrailingClosure(ASTContext& ctx, TrailingClosureExpr& tc)
{
if (tc.desugarExpr) {
tc.SetTy(Synthesize({ctx, SynPos::EXPR_ARG}, tc.desugarExpr.get()));
} else {
tc.SetTy(TypeManager::GetInvalidTy());
}
return tc.GetTy();
}
bool TypeChecker::TypeCheckerImpl::ChkTrailingClosureExpr(ASTContext& ctx, Ty& target, TrailingClosureExpr& tc)
{
if (tc.desugarExpr) {
if (Check(ctx, &target, tc.desugarExpr.get())) {
tc.SetTy(tc.desugarExpr->GetTy());
return true;
}
tc.desugarExpr->SetTy(TypeManager::GetInvalidTy());
}
tc.SetTy(TypeManager::GetInvalidTy());
return false;
}
bool TypeChecker::TypeCheckerImpl::GetCallBaseCandidates(
const ASTContext& ctx, const CallExpr& ce, Expr& expr, Ptr<Decl>& target, std::vector<Ptr<FuncDecl>>& candidates)
{
target = GetRealTarget(&expr, expr.GetTarget());
if (target->TestAttr(Attribute::COMMON) && target->specificImplementation) {
target = target->specificImplementation;
}
CJC_NULLPTR_CHECK(target);
if (IsBuiltinTypeAlias(*target) || target->IsBuiltIn()) {
return false;
}
bool isFunctionCall = true;
if (IsInstanceConstructor(*target) && target->outerDecl) {
target = target->outerDecl;
}
if (target->IsStructOrClassDecl()) {
for (auto& member : target->GetMemberDeclPtrs()) {
CJC_NULLPTR_CHECK(member);
bool originalCtor =
IsInstanceConstructor(*member) && member->astKind == ASTKind::FUNC_DECL && member->identifier == "init";
if (originalCtor) {
(void)candidates.emplace_back(StaticCast<FuncDecl*>(member));
}
}
isFunctionCall = false;
} else {
candidates = GetFuncTargets(expr);
}
RemoveDuplicateElements(candidates);
if (ce.callKind == CallKind::CALL_ANNOTATION) {
RemoveNonAnnotationCandidates(candidates);
}
mpImpl->RemoveCommonCandidatesIfHasSpecific(candidates);
auto ds = DiagSuppressor(diag);
candidates = FilterCandidatesWithReExport(ctx, candidates, ce);
if (expr.astKind == ASTKind::REF_EXPR && candidates.empty() && target->astKind == ASTKind::PACKAGE_DECL) {
diag.DiagnoseRefactor(DiagKindRefactor::sema_cannot_ref_to_pkg_name, expr);
}
if (ds.HasError()) {
ds.ReportDiag();
return false;
}
ds.ReportDiag();
if (!candidates.empty() && isFunctionCall) {
target = candidates[0];
}
return true;
}
bool TypeChecker::TypeCheckerImpl::CheckRefConstructor(const ASTContext& ctx, const CallExpr& ce, const RefExpr& re)
{
if (re.isSuper || re.isThis) {
auto curFuncBody = GetCurFuncBody(ctx, re.scopeName);
bool insideCtor{false};
if (curFuncBody && curFuncBody->funcDecl && curFuncBody->funcDecl->TestAttr(Attribute::CONSTRUCTOR)) {
insideCtor = true;
}
if (!insideCtor) {
diag.Diagnose(ce, DiagKind::sema_invalid_this_call_outside_ctor, re.ref.identifier.Val());
return false;
}
}
return true;
}
bool TypeChecker::TypeCheckerImpl::ChkCallBaseRefExpr(
ASTContext& ctx, CallExpr& ce, Ptr<Decl>& target, std::vector<Ptr<FuncDecl>>& candidates)
{
CJC_NULLPTR_CHECK(ce.baseFunc);
CJC_ASSERT(ce.baseFunc->astKind == ASTKind::REF_EXPR);
auto re = StaticAs<ASTKind::REF_EXPR>(ce.baseFunc.get());
re->isAlone = false;
re->callOrPattern = &ce;
ctx.targetTypeMap[re] = ctx.targetTypeMap[&ce];
Synthesize({ctx, SynPos::EXPR_ARG}, re);
if (auto builtin = DynamicCast<BuiltInDecl>(re->ref.target); builtin && builtin->type == BuiltInType::CFUNC) {
return SynCFuncCall(ctx, ce);
}
if (!re->ref.target && re->ref.targets.empty()) {
return false;
} else {
return CheckRefConstructor(ctx, ce, *re) && GetCallBaseCandidates(ctx, ce, *re, target, candidates);
}
}
bool TypeChecker::TypeCheckerImpl::ChkCallBaseMemberAccess(
ASTContext& ctx, CallExpr& ce, Ptr<Decl>& target, std::vector<Ptr<FuncDecl>>& candidates)
{
auto ma = StaticAs<ASTKind::MEMBER_ACCESS>(ce.baseFunc.get());
ma->callOrPattern = &ce;
ma->isAlone = false;
ctx.targetTypeMap[ma->baseExpr] = TypeManager::GetQuestTy();
Synthesize({ctx, SynPos::EXPR_ARG}, ma);
ctx.targetTypeMap[ma->baseExpr] = nullptr;
if (ma->GetTy() && ma->GetTy()->IsNothing()) {
return true;
} else if (!ma->target && ma->targets.empty()) {
if (Is<VArrayTy>(ma->baseExpr->GetTy()) && ma->field == "size") {
diag.Diagnose(ce, DiagKind::sema_no_match_operator_function_call);
ma->SetTy(TypeManager::GetInvalidTy());
}
return false;
} else {
return GetCallBaseCandidates(ctx, ce, *ma, target, candidates);
}
}
namespace {
bool CanReachFuncCallByQuestable(Node& root)
{
bool canReachFuncCall = false;
Walker(&root, [&canReachFuncCall](Ptr<Node> n) {
if (Is<TrailingClosureExpr>(n) || Is<CallExpr>(n)) {
canReachFuncCall = true;
return VisitAction::STOP_NOW;
}
if (!n || !IsQuestableNode(*n)) {
return VisitAction::SKIP_CHILDREN;
} else {
return VisitAction::WALK_CHILDREN;
}
}).Walk();
return canReachFuncCall;
}
}
* Used to help type argument inference for curried function call,
* where the args are continuously given in a call chain like f(1)(2)(3).
* Expected return type for the entire call chain and the last argument's type
* are piggybacked to targetForBase.
* This procedure could be reached recursively if the baseFunc is yet another function call
* but not the first one.
*
* For example:
* Check(`f(1)(2)(3)`, Int64`)
* -> Check(`f(1)(2)`, (Int64)->Int64)
* -> Check(`f(1)`, (Int64)->(Int64)->Int64)
*
* If the target type or any argument's type is unknown, it is replaced by QuestTy.
* Since targetForBase is an overestimation with noCast set and possibly QuestTy,
* it can only pass down questable nodes to ensure it's not misused as the ty of any Node.
*/
bool TypeChecker::TypeCheckerImpl::ChkCurryCallBase(ASTContext& ctx, CallExpr& ce, Ptr<Ty>& targetRet)
{
std::vector<Ptr<Ty>> paramTys;
Ptr<Ty> retTy = targetRet ? targetRet : TypeManager::GetQuestTy();
{
auto ds = DiagSuppressor(diag);
for (auto& arg: ce.args) {
if (Ty::IsInitialTy(arg->GetTy())) {
SynthesizeWithCache({ctx, SynPos::EXPR_ARG}, arg);
}
if (Ty::IsTyCorrect(arg->GetTy())) {
paramTys.push_back(arg->GetTy());
} else {
paramTys.push_back(TypeManager::GetQuestTy());
}
}
}
auto targetForBase = typeManager.GetFunctionTy(paramTys, retTy, {false, false, false, true});
return Check(ctx, targetForBase, ce.baseFunc.get());
}
bool TypeChecker::TypeCheckerImpl::ChkCallBaseExpr(
ASTContext& ctx, CallExpr& ce, Ptr<Decl>& targetDecl, Ptr<Ty>& targetRet, std::vector<Ptr<FuncDecl>>& candidates)
{
auto setFuncArgsInvalidTy = [&ce]() {
ce.SetTy(TypeManager::GetInvalidTy());
std::for_each(ce.args.begin(), ce.args.end(), [](const OwnedPtr<FuncArg>& fa) {
CJC_NULLPTR_CHECK(fa->expr);
if (!Ty::IsTyCorrect(fa->expr->GetTy())) {
fa->expr->SetTy(TypeManager::GetInvalidTy());
}
fa->SetTy(TypeManager::GetInvalidTy());
});
};
if (ce.baseFunc->desugarExpr != nullptr && !Is<TrailingClosureExpr>(ce.baseFunc.get())) {
if (!Ty::IsTyCorrect(Synthesize({ctx, SynPos::EXPR_ARG}, ce.baseFunc.get()))) {
setFuncArgsInvalidTy();
return false;
}
return true;
}
bool isWellTyped = true;
auto cs = PData::CommitScope(typeManager.constraints);
switch (ce.baseFunc->astKind) {
case ASTKind::REF_EXPR:
isWellTyped = ChkCallBaseRefExpr(ctx, ce, targetDecl, candidates);
break;
case ASTKind::MEMBER_ACCESS:
isWellTyped = ChkCallBaseMemberAccess(ctx, ce, targetDecl, candidates);
break;
default:
bool mayCurry = false;
if (CanReachFuncCallByQuestable(*ce.baseFunc)) {
auto ds = DiagSuppressor(diag);
mayCurry = ChkCurryCallBase(ctx, ce, targetRet);
}
if (!mayCurry) {
PData::Reset(typeManager.constraints);
ce.baseFunc->Clear();
Synthesize({ctx, SynPos::EXPR_ARG}, ce.baseFunc.get());
}
break;
}
if (!isWellTyped && !Ty::IsTyCorrect(ce.baseFunc->GetTy())) {
setFuncArgsInvalidTy();
}
return isWellTyped;
}
void TypeChecker::TypeCheckerImpl::CheckMacroCall(ASTContext& ctx, Node& macroNode)
{
if (macroNode.TestAttr(Attribute::IS_CHECK_VISITED)) {
return;
}
macroNode.EnableAttr(Attribute::IS_CHECK_VISITED);
auto targetHandler = [&ctx, ¯oNode, this](const auto& kind) -> void {
auto me = static_cast<std::decay_t<decltype(kind)>*>(¯oNode);
if (me->invocation.target != nullptr) {
return;
}
if (ci->invocation.globalOptions.enableMacroInLSP && me->invocation.decl &&
!me->invocation.decl->TestAttr(Attribute::IS_CHECK_VISITED)) {
Synthesize({ctx, SynPos::NONE}, me->invocation.decl.get());
}
auto decls = importManager.GetImportedDeclsByName(*me->curFile, me->identifier);
if (decls.empty()) {
return;
}
Utils::EraseIf(decls, [](Ptr<const Decl> d) -> bool { return !d || !d->TestAttr(Attribute::MACRO_FUNC); });
for (const auto& it : decls) {
Ptr<FuncDecl> funcDecl{nullptr};
if (it->astKind == ASTKind::MACRO_DECL) {
auto macroDecl = RawStaticCast<MacroDecl*>(it);
funcDecl = macroDecl->desugarDecl.get();
} else {
funcDecl = StaticAs<ASTKind::FUNC_DECL>(it);
}
if (!funcDecl || !funcDecl->funcBody) {
continue;
}
if (funcDecl->funcBody && funcDecl->funcBody->paramLists.empty()) {
continue;
}
auto isCommonMacroCall = !me->invocation.HasAttr();
auto isCommonMacroDecl = funcDecl->funcBody->paramLists.front()->params.size() == MACRO_COMMON_ARGS;
if (isCommonMacroCall == isCommonMacroDecl) {
ReplaceTarget(me, funcDecl);
}
}
};
if (macroNode.astKind == ASTKind::MACRO_EXPAND_EXPR) {
targetHandler(MacroExpandExpr());
} else if (macroNode.astKind == ASTKind::MACRO_EXPAND_DECL) {
targetHandler(MacroExpandDecl());
} else if (macroNode.astKind == ASTKind::MACRO_EXPAND_PARAM) {
targetHandler(MacroExpandParam());
}
}
bool TypeChecker::TypeCheckerImpl::CheckCallKind(const CallExpr& ce, Ptr<Decl> decl, CallKind& type)
{
if (!decl) {
return false;
}
switch (decl->astKind) {
case ASTKind::INTERFACE_DECL:
diag.Diagnose(ce, DiagKind::sema_interface_can_not_be_instantiated, decl->identifier.Val());
break;
case ASTKind::CLASS_DECL:
case ASTKind::STRUCT_DECL: {
auto re = As<ASTKind::REF_EXPR>(ce.baseFunc.get());
bool isSuper = re && re->isSuper;
bool isThis = re && re->isThis;
auto cd = As<ASTKind::CLASS_DECL>(decl);
if (cd && cd->TestAttr(Attribute::ABSTRACT) && !isSuper && !isThis) {
diag.Diagnose(ce, DiagKind::sema_abstract_class_can_not_be_instantiated, cd->identifier.Val());
break;
}
if (isThis) {
type = CallKind::CALL_DECLARED_FUNCTION;
} else if (decl->astKind == ASTKind::CLASS_DECL) {
type = isSuper ? CallKind::CALL_SUPER_FUNCTION : CallKind::CALL_OBJECT_CREATION;
} else {
type = CallKind::CALL_STRUCT_CREATION;
}
break;
}
case ASTKind::FUNC_DECL: {
auto ma = As<ASTKind::MEMBER_ACCESS>(ce.baseFunc.get());
auto reBase = ma ? As<ASTKind::REF_EXPR>(ma->baseExpr.get()) : nullptr;
if (reBase && reBase->isSuper) {
type = CallKind::CALL_SUPER_FUNCTION;
} else {
type = decl->TestAttr(Attribute::INTRINSIC) ? CallKind::CALL_INTRINSIC_FUNCTION
: CallKind::CALL_DECLARED_FUNCTION;
}
break;
}
default:
return false;
}
return true;
}
DiagKind TypeChecker::TypeCheckerImpl::GetErrorKindForCall(const std::vector<Ptr<FuncDecl>>& candidatesBeforeCheck,
const std::vector<Ptr<FuncDecl>>& candidatesAfterCheck, const CallExpr& ce) const
{
bool hasEmpty = candidatesBeforeCheck.empty() || candidatesAfterCheck.empty();
auto target = ce.baseFunc ? ce.baseFunc->GetTarget() : nullptr;
if (hasEmpty) {
if (IsSearchTargetInMemberAccessUpperBound(ce)) {
return DiagKind::sema_generic_no_method_match_in_upper_bounds;
} else if (target && target->TestAttr(Attribute::ENUM_CONSTRUCTOR)) {
return DiagKind::sema_enum_constructor_type_not_match;
} else if (ce.callKind == CallKind::CALL_OBJECT_CREATION || ce.callKind == CallKind::CALL_STRUCT_CREATION) {
return DiagKind::sema_no_match_constructor;
} else {
return DiagKind::sema_no_match_function_declaration_for_call;
}
} else {
if (IsSearchTargetInMemberAccessUpperBound(ce)) {
return DiagKind::sema_generic_ambiguous_method_match_in_upper_bounds;
} else if (target && target->TestAttr(Attribute::ENUM_CONSTRUCTOR)) {
return DiagKind::sema_multiple_constructor_in_enum;
} else if (ce.callKind == CallKind::CALL_OBJECT_CREATION || ce.callKind == CallKind::CALL_STRUCT_CREATION) {
return DiagKind::sema_ambiguous_constructor_match;
} else {
return DiagKind::sema_ambiguous_match;
}
}
}
void TypeChecker::TypeCheckerImpl::DiagnoseForCall(const std::vector<Ptr<FuncDecl>>& candidatesBeforeCheck,
const std::vector<Ptr<FuncDecl>>& candidatesAfterCheck, CallExpr& ce, const Decl& decl)
{
if (ce.sourceExpr || ce.baseFunc->TestAttr(Attribute::MACRO_INVOKE_BODY)) {
return;
}
bool invalid = !candidatesBeforeCheck.empty() && candidatesAfterCheck.empty() &&
Ty::IsTyCorrect(ce.baseFunc->GetTy()) &&
Utils::In(ce.args, [](const auto& arg) { return !Ty::IsTyCorrect(arg->GetTy()); });
if (invalid) {
return;
}
bool isSuperCreation = ce.callKind == CallKind::CALL_SUPER_FUNCTION && decl.astKind != ASTKind::FUNC_DECL;
std::string identifier = isSuperCreation ? "super" : decl.identifier.Val();
Position identifierPos{ce.begin};
if (auto re = DynamicCast<RefExpr*>(ce.baseFunc.get()); re) {
identifier = re->ref.identifier;
} else if (auto memberAccess = DynamicCast<MemberAccess*>(ce.baseFunc.get());
memberAccess && !memberAccess->field.ZeroPos()) {
identifierPos = memberAccess->field.Begin();
}
DiagKind errorKind = GetErrorKindForCall(candidatesBeforeCheck, candidatesAfterCheck, ce);
if (candidatesBeforeCheck.empty()) {
ce.callKind = CallKind::CALL_INVALID;
auto builder = diag.Diagnose(ce, identifierPos, errorKind, identifier);
if (auto ma = DynamicCast<MemberAccess*>(ce.baseFunc.get())) {
RecommendImportForMemberAccess(typeManager, importManager, *ma, &builder);
}
return;
} else if (candidatesBeforeCheck.size() == 1) {
return;
}
bool hasEmpty = candidatesBeforeCheck.empty() || candidatesAfterCheck.empty();
std::vector<Ptr<FuncDecl>> candidates = hasEmpty ? candidatesBeforeCheck : candidatesAfterCheck;
DiagKind noteKind = hasEmpty ? DiagKind::sema_found_possible_candidate_decl : DiagKind::sema_found_candidate_decl;
std::vector<Ptr<Decl>> importedTargets;
DiagnosticBuilder diagInfo = diag.Diagnose(ce, identifierPos, errorKind, identifier);
for (auto& it : candidates) {
CJC_NULLPTR_CHECK(it);
if (it->TestAttr(Attribute::IMPORTED)) {
importedTargets.emplace_back(it);
} else {
diagInfo.AddNote(*it, it->identifier.Begin(), noteKind);
}
}
AddDiagNotesForImportedDecls(
diagInfo, importManager.GetImportsOfDecl(ce.curFile->curPackage->fullPackageName), importedTargets);
}
Ptr<Decl> TypeChecker::TypeCheckerImpl::GetDeclOfThisType(const Expr& expr) const
{
Ptr<Decl> decl = nullptr;
if (auto ma = DynamicCast<const MemberAccess*>(&expr); ma && ma->baseExpr) {
if (auto ct = DynamicCast<ClassTy*>(ma->baseExpr->GetTy()); ct) {
decl = ct->declPtr;
}
} else if (auto re = DynamicCast<const RefExpr*>(&expr); re) {
Ptr<Decl> baseFunc = re->GetTarget();
if (auto vd = DynamicCast<VarDecl*>(baseFunc);
vd && vd->initializer != nullptr && vd->initializer->astKind == ASTKind::MEMBER_ACCESS) {
Ptr<MemberAccess> maInitializer = RawStaticCast<MemberAccess*>(vd->initializer.get());
Ptr<Decl> baseTarget = maInitializer->baseExpr->GetTarget();
if (baseTarget != nullptr && baseTarget->GetTy() != nullptr && baseTarget->GetTy()->IsClass()) {
decl = RawStaticCast<ClassTy*>(baseTarget->GetTy())->declPtr;
}
} else {
CJC_NULLPTR_CHECK(expr.curFile);
auto ctx = ci->GetASTContextByPackage(expr.curFile->curPackage);
CJC_NULLPTR_CHECK(ctx);
auto outMostFunc = ScopeManager::GetOutMostSymbol(*ctx, SymbolKind::FUNC, expr.scopeName);
if (outMostFunc && StaticCast<FuncDecl*>(outMostFunc->node)->funcBody->parentClassLike) {
decl = RawStaticCast<FuncDecl*>(outMostFunc->node)->funcBody->parentClassLike;
}
}
}
return decl;
}
std::optional<Ptr<Ty>> TypeChecker::TypeCheckerImpl::DynamicBindingThisType(
Expr& baseExpr, const FuncDecl& fd, const SubstPack& typeMapping)
{
if (!IsFuncReturnThisType(fd)) {
return {};
}
auto funcTy = DynamicCast<FuncTy*>(
Ty::IsTyCorrect(baseExpr.GetTy()) && baseExpr.GetTy()->IsFunc() ? baseExpr.GetTy() : fd.GetTy());
if (funcTy == nullptr) {
return {};
}
auto declOfThisType = GetDeclOfThisType(baseExpr);
if (auto cd = DynamicCast<ClassDecl*>(declOfThisType); cd && Ty::IsTyCorrect(cd->GetTy())) {
auto instTy = typeManager.ApplySubstPack(typeManager.GetClassThisTy(*cd, cd->GetTy()->typeArgs), typeMapping);
baseExpr.SetTy(typeManager.GetFunctionTy(funcTy->paramTys, instTy));
return instTy;
} else if (auto ma = DynamicCast<MemberAccess*>(&baseExpr); ma && ma->baseExpr) {
if (auto gty = DynamicCast<GenericsTy*>(ma->baseExpr->GetTy()); gty) {
baseExpr.SetTy(typeManager.GetFunctionTy(funcTy->paramTys, gty));
return gty;
}
}
return {};
}
FunctionMatchingUnit* TypeChecker::TypeCheckerImpl::FindFuncWithMaxChildRetTy(
const CallExpr& ce, std::vector<std::unique_ptr<FunctionMatchingUnit>>& candidates)
{
if (candidates.empty()) {
return nullptr;
}
FunctionMatchingUnit* maxChildFmu = candidates[0].get();
for (size_t i = 1; i < candidates.size(); i++) {
auto ty1 = typeManager.ApplySubstPack(maxChildFmu->fd.GetTy(), maxChildFmu->typeMapping);
auto ty2 = typeManager.ApplySubstPack(candidates[i]->fd.GetTy(), candidates[i]->typeMapping);
if (ty1->IsInvalid() || ty2->IsInvalid()) {
continue;
}
auto meteRes = JoinAndMeet(typeManager, {ty1, ty2}).MeetAsVisibleTy();
Ptr<Ty> maxChildRetTy{};
auto [optErrs, metTy] = JoinAndMeet::SetMetType(maxChildRetTy, meteRes);
maxChildRetTy = metTy;
if (optErrs) {
(void)diag.Diagnose(ce, DiagKind::sema_diag_report_note_message, *optErrs);
};
if (maxChildRetTy->IsInvalid()) {
return nullptr;
} else if (maxChildRetTy == ty2 && ty1 != ty2) {
maxChildFmu = candidates[i].get();
} else {
continue;
}
}
return maxChildFmu;
}
bool TypeChecker::TypeCheckerImpl::PostCheckCallExpr(
const ASTContext& ctx, CallExpr& ce, FuncDecl& func, const SubstPack& typeMapping)
{
if (!func.TestAttr(Attribute::CONSTRUCTOR) && !CheckStaticCallNonStatic(ctx, ce, func)) {
ce.SetTy(TypeManager::GetInvalidTy());
ce.callKind = CallKind::CALL_INVALID;
return false;
}
if (func.TestAttr(Attribute::GENERIC)) {
auto typeArgs = ce.baseFunc->GetTypeArgs();
auto ref = DynamicCast<NameReferenceExpr*>(ce.baseFunc.get());
auto isGenericLegal = !(typeArgs.empty() && ref && ref->instTys.empty()) &&
CheckCallGenericDeclInstantiation(&func, typeArgs, *ce.baseFunc);
if (!isGenericLegal) {
ce.SetTy(TypeManager::GetInvalidTy());
ce.callKind = CallKind::CALL_INVALID;
return false;
}
}
ce.resolvedFunction = &func;
UpdateCallTargetsForLSP(ce, func);
DynamicBindingThisType(*ce.baseFunc, func, typeMapping);
if (!func.TestAttr(Attribute::FOREIGN) && !func.TestAttr(Attribute::C)) {
bool valid = true;
for (auto& fa : ce.args) {
if (fa->withInout) {
diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_can_only_used_in_cfunc_calling, *fa);
valid = false;
}
}
if (!valid) {
return false;
}
} else {
auto fa = std::find_if(
ce.args.begin(), ce.args.end(), [](auto& fa) { return Is<VArrayTy>(fa->GetTy()) && !fa->withInout; });
if (fa != ce.args.end()) {
(void)diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_mismatch, *fa->get(), VARRAY_NAME);
return false;
}
}
if (func.GetTy()->HasQuestTy()) {
DiagUnableToInferReturnType(diag, func, ce);
ce.SetTy(TypeManager::GetInvalidTy());
ce.callKind = CallKind::CALL_INVALID;
return false;
}
return true;
}
namespace {
Ptr<FuncTy> MakePlaceholderFuncTy(size_t arity, TypeManager& tyMgr)
{
std::vector<Ptr<Ty>> paramTys;
Ptr<Ty> retTy = tyMgr.AllocTyVar();
for (size_t i = 0; i < arity; i++) {
paramTys.push_back(tyMgr.AllocTyVar());
}
return tyMgr.GetFunctionTy(paramTys, retTy);
}
Ptr<FuncTy> TryCastingToFuncTy(Ptr<Ty> ty, size_t arity, TypeManager& tyMgr)
{
if (auto funcTy = DynamicCast<FuncTy*>(ty); funcTy) {
return funcTy;
}
if (auto genTy = DynamicCast<GenericsTy*>(ty); genTy) {
if (genTy->isPlaceholder) {
if (auto placeholderFuncTy = tyMgr.ConstrainByCtor(*genTy, *MakePlaceholderFuncTy(arity, tyMgr))) {
return StaticCast<FuncTy*>(placeholderFuncTy);
} else {
return nullptr;
}
}
auto allUpperBounds = genTy->upperBounds;
for (auto& upperBound : allUpperBounds) {
if (auto funcTy = DynamicCast<FuncTy*>(upperBound); funcTy) {
return funcTy;
}
}
}
return nullptr;
};
}
bool TypeChecker::TypeCheckerImpl::CheckNonNormalCall(ASTContext& ctx, Ptr<Ty> target, CallExpr& ce)
{
CJC_NULLPTR_CHECK(ce.baseFunc);
bool res = false;
if (auto funcTy = TryCastingToFuncTy(ce.baseFunc->GetTy(), ce.args.size(), typeManager)) {
auto maybeVariadic = IsPossibleVariadicFunction(*funcTy, ce);
std::vector<Diagnostic> diagnostics;
if (maybeVariadic) {
auto ds = DiagSuppressor(diag);
res = CheckFuncPtrCall(ctx, target, ce, *funcTy);
diagnostics = ds.GetSuppressedDiag();
} else {
res = CheckFuncPtrCall(ctx, target, ce, *funcTy);
}
if (!res && maybeVariadic) {
DesugarVariadicCallExpr(ctx, ce, funcTy->paramTys.size() - 1);
bool success = false;
{
auto ds = DiagSuppressor(diag);
success = CheckFuncPtrCall(ctx, target, *StaticAs<ASTKind::CALL_EXPR>(ce.desugarExpr.get()), *funcTy);
if (diagnostics.empty()) {
diagnostics = ds.GetSuppressedDiag();
}
}
if (success) {
ce.SetTy(ce.desugarExpr->GetTy());
res = true;
} else {
RecoverFromVariadicCallExpr(ce);
std::for_each(diagnostics.cbegin(), diagnostics.cend(), [this](auto info) { diag.Diagnose(info); });
ce.SetTy(TypeManager::GetInvalidTy());
res = false;
}
}
if (funcTy->IsCFunc()) {
auto fa = std::find_if(
ce.args.begin(), ce.args.end(), [](auto& fa) { return Is<VArrayTy>(fa->GetTy()) && !fa->withInout; });
if (fa != ce.args.end()) {
(void)diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_mismatch, *fa->get(), VARRAY_NAME);
res = false;
}
}
if (funcTy->isC && !ce.TestAttr(Attribute::UNSAFE) && !IsValidCFuncConstructorCall(ce)) {
diag.Diagnose(ce, DiagKind::sema_unsafe_function_invoke_failed);
res = false;
}
} else if (ChkFunctionCallExpr(ctx, target, ce)) {
res = true;
}
if (!res) {
ce.SetTy(TypeManager::GetInvalidTy());
}
return res;
}
void TypeChecker::TypeCheckerImpl::PostProcessForLSP(CallExpr& ce, const std::vector<Ptr<FuncDecl>>& result) const
{
if (result.size() > 1) {
UpdateCallTargetsForLSP(ce, *result[0]);
ce.resolvedFunction = result[0];
} else {
ReplaceTarget(ce.baseFunc.get(), nullptr);
ce.baseFunc->SetTy(TypeManager::GetInvalidTy());
}
}
bool TypeChecker::TypeCheckerImpl::ChkCallExpr(ASTContext& ctx, Ptr<Ty> target, CallExpr& ce)
{
if (ce.desugarExpr) {
return ChkDesugarExprOfCallExpr(ctx, target, ce);
}
if (!ce.baseFunc) {
return false;
}
bool prevCheckTrue =
ce.callKind != CallKind::CALL_INVALID && Ty::IsTyCorrect(ce.GetTy()) && typeManager.GetUnsolvedTyVars().empty();
std::vector<Ptr<Ty>> argsTys;
if (prevCheckTrue) {
std::transform(
ce.args.begin(), ce.args.end(), std::back_inserter(argsTys), [](auto& arg) { return arg->GetTy(); });
}
ce.SetTy(TypeManager::GetNonNullTy(ce.GetTy()));
Ptr<Decl> decl{nullptr};
std::vector<Ptr<FuncDecl>> candidates;
if (!ChkCallBaseExpr(ctx, ce, decl, target, candidates)) {
return ChkBuiltinCall(ctx, *TypeManager::GetNonNullTy(target), ce);
}
if (!Ty::IsTyCorrect(ce.baseFunc->GetTy())) {
return false;
}
if (ce.baseFunc->GetTy()->IsNothing()) {
return SynArgsOfNothingBaseExpr(ctx, ce);
}
if (ce.needCheckToTokens) {
CheckToTokensImpCallExpr(ce);
}
if (!CheckCallKind(ce, decl, ce.callKind)) {
return CheckNonNormalCall(ctx, target, ce);
}
if (ce.callKind == CallKind::CALL_INVALID) {
return false;
}
if (candidates.empty()) {
DiagnoseForCall(candidates, candidates, ce, *decl);
return false;
}
bool maybeEnumOverloadOP = IsPossibleEnumConstructor(candidates, *ce.baseFunc);
bool maybeVariadicFunction = IsPossibleVariadicFunction(candidates, ce);
bool maybeEnumOrVariadic = maybeEnumOverloadOP || maybeVariadicFunction;
SubstPack typeMapping;
std::vector<Ptr<FuncDecl>> result;
std::vector<Diagnostic> diagnostics;
PData::CommitScope cs(typeManager.constraints);
if (maybeEnumOrVariadic) {
auto ds = DiagSuppressor(diag);
result = MatchFunctionForCall(ctx, candidates, ce, target, typeMapping);
diagnostics = ds.GetSuppressedDiag();
} else {
result = MatchFunctionForCall(ctx, candidates, ce, target, typeMapping);
}
ce.SetTy(TypeManager::GetNonNullTy(ce.GetTy()));
if (result.size() == 1) {
return PostCheckCallExpr(ctx, ce, *result[0], typeMapping);
}
PData::Reset(typeManager.constraints);
ce.baseFunc->SetTy(maybeEnumOrVariadic ? TypeManager::GetInvalidTy() : ce.baseFunc->GetTy());
auto ret = (maybeEnumOverloadOP && ChkFunctionCallExpr(ctx, target, ce)) ||
(maybeVariadicFunction && result.empty() && ChkVariadicCallExpr(ctx, target, ce, candidates, diagnostics));
if (ret) {
return true;
}
if (diagnostics.empty()) {
DiagnoseForCall(candidates, result, ce, *decl);
}
RecoverCallArgs(ctx, ce, argsTys);
if (diag.GetDiagnoseStatus()) {
PostProcessForLSP(ce, result);
} else if (!prevCheckTrue) {
ce.callKind = CallKind::CALL_INVALID;
}
return false;
}
bool TypeChecker::TypeCheckerImpl::ChkDesugarExprOfCallExpr(ASTContext& ctx, Ptr<Ty> target, CallExpr& ce)
{
if (Ty::IsTyCorrect(ce.desugarExpr->GetTy())) {
ce.SetTy(ce.desugarExpr->GetTy());
return !target || typeManager.IsSubtype(ce.desugarExpr->GetTy(), target);
}
bool isWellTyped;
if (Ty::IsTyCorrect(target)) {
isWellTyped = Check(ctx, target, ce.desugarExpr.get());
} else {
isWellTyped = Ty::IsTyCorrect(Synthesize({ctx, SynPos::EXPR_ARG}, ce.desugarExpr.get()));
}
ce.SetTy(ce.desugarExpr->GetTy());
return isWellTyped && Ty::IsTyCorrect(ce.GetTy());
}
bool TypeChecker::TypeCheckerImpl::SynArgsOfNothingBaseExpr(ASTContext& ctx, CallExpr& ce)
{
bool isWellTyped = true;
std::for_each(ce.args.cbegin(), ce.args.cend(), [this, &ctx, &isWellTyped](auto& arg) {
isWellTyped = isWellTyped && Ty::IsTyCorrect(Synthesize({ctx, SynPos::EXPR_ARG}, arg.get()));
ReplaceIdealTy(*arg);
ReplaceIdealTy(*arg->expr);
});
ce.SetTy(isWellTyped ? RawStaticCast<Ty*>(TypeManager::GetNothingTy()) : TypeManager::GetInvalidTy());
return isWellTyped;
}
bool TypeChecker::TypeCheckerImpl::ChkFunctionCallExpr(ASTContext& ctx, Ptr<Ty> target, CallExpr& ce)
{
DesugarCallExpr(ctx, ce);
auto desugarCallExpr = StaticAs<ASTKind::CALL_EXPR>(ce.desugarExpr.get());
if (!ChkCallExpr(ctx, target, *desugarCallExpr)) {
RecoverToCallExpr(ce);
diag.Diagnose(ce, DiagKind::sema_no_match_operator_function_call);
ce.SetTy(TypeManager::GetInvalidTy());
return false;
}
ce.callKind = desugarCallExpr->callKind;
ce.SetTy(desugarCallExpr->GetTy());
return true;
}
bool TypeChecker::TypeCheckerImpl::ChkVariadicCallExpr(ASTContext& ctx, Ptr<Ty> target, CallExpr& ce,
const std::vector<Ptr<FuncDecl>>& candidates, std::vector<Diagnostic>& diagnostics)
{
if (!ChkArgsOrder(diag, ce)) {
return false;
}
std::vector<Ptr<FuncDecl>> results;
std::map<size_t, std::vector<Ptr<FuncDecl>>> fixedPositionalArityToGroup =
FuncDeclsGroupByFixedPositionalArity(candidates, ce);
for (auto& [fixedPositionalArity, group] : fixedPositionalArityToGroup) {
DesugarVariadicCallExpr(ctx, ce, fixedPositionalArity);
auto ds = DiagSuppressor(diag);
SubstPack typeMapping;
std::vector<Ptr<FuncDecl>> matchResults =
MatchFunctionForCall(ctx, group, *StaticAs<ASTKind::CALL_EXPR>(ce.desugarExpr.get()), target, typeMapping);
if (matchResults.size() == 1) {
results.emplace_back(matchResults.front());
}
if (diagnostics.empty()) {
diagnostics = ds.GetSuppressedDiag();
}
RecoverFromVariadicCallExpr(ce);
}
if (results.size() == 1) {
size_t fixedPositionalArity = GetPositionalParamSize(*results.front()) - 1;
DesugarVariadicCallExpr(ctx, ce, fixedPositionalArity);
SubstPack typeMapping;
std::vector<Ptr<FuncDecl>> matchResults = MatchFunctionForCall(
ctx, results, *StaticAs<ASTKind::CALL_EXPR>(ce.desugarExpr.get()), target, typeMapping);
CJC_ASSERT(matchResults.size() == 1);
bool ret = PostCheckCallExpr(
ctx, *StaticAs<ASTKind::CALL_EXPR>(ce.desugarExpr.get()), *matchResults.front(), typeMapping);
if (ret) {
ce.SetTy(ce.desugarExpr->GetTy());
ce.resolvedFunction = StaticAs<ASTKind::CALL_EXPR>(ce.desugarExpr.get())->resolvedFunction;
} else {
RecoverFromVariadicCallExpr(ce);
}
return ret;
} else if (results.empty()) {
std::for_each(diagnostics.cbegin(), diagnostics.cend(), [this](auto info) { diag.Diagnose(info); });
} else {
Ptr<Decl> decl = results.front();
CJC_NULLPTR_CHECK(decl);
if (decl->TestAttr(Attribute::CONSTRUCTOR) && decl->outerDecl) { decl = decl->outerDecl; }
DiagnoseForCall(candidates, results, ce, *decl);
}
ce.callKind = CallKind::CALL_INVALID;
return false;
}
void TypeChecker::TypeCheckerImpl::CheckToTokensImpCallExpr(const CallExpr& ce)
{
auto toTokensDecl = importManager.GetAstDecl<InterfaceDecl>("ToTokens");
if (ce.astKind != ASTKind::CALL_EXPR || !toTokensDecl) {
return;
}
auto ma = StaticAs<ASTKind::MEMBER_ACCESS>(ce.baseFunc.get());
auto be = ma->baseExpr.get();
if (be) {
auto prTys = promotion.Promote(*be->GetTy(), *toTokensDecl->GetTy());
auto ty = prTys.empty() ? TypeManager::GetInvalidTy() : *prTys.begin();
if (ty != toTokensDecl->GetTy()) {
diag.Diagnose(ce, DiagKind::sema_invalid_tokens_implementation, be->GetTy()->String());
}
}
}
bool TypeChecker::TypeCheckerImpl::CheckStaticCallNonStatic(
const ASTContext& ctx, const CallExpr& ce, const FuncDecl& result)
{
if (!result.outerDecl || !result.outerDecl->IsNominalDecl()) {
return true;
}
if (auto re = DynamicCast<RefExpr*>(ce.baseFunc.get()); re) {
return IsLegalAccessFromStaticFunc(ctx, *re, result);
}
return true;
}
void TypeChecker::TypeCheckerImpl::SpreadInstantiationTy(Node& node, const SubstPack& typeMapping)
{
if (typeMapping.u2i.empty() || !Ty::IsTyCorrect(node.GetTy())) {
return;
}
Walker walkerExpr(&node, [this, &typeMapping](Ptr<Node> node) -> VisitAction {
if (Ty::IsTyCorrect(node->GetTy())) {
node->SetTy(typeManager.ApplySubstPack(node->GetTy(), typeMapping));
return VisitAction::WALK_CHILDREN;
}
return VisitAction::WALK_CHILDREN;
});
walkerExpr.Walk();
}