* @file
*
* This file implements the TypeChecker related classes.
*/
#include "TypeCheckerImpl.h"
#include <algorithm>
#include <memory>
#include <unordered_set>
#include "Collector.h"
#include "Desugar/DesugarInTypeCheck.h"
#include "DiagSuppressor.h"
#include "Diags.h"
#include "ExtraScopes.h"
#include "JoinAndMeet.h"
#include "NativeFFI/Java/AfterTypeCheck/InteropLibBridge.h"
#include "NativeFFI/Java/BeforeTypeCheck/PreTypeCheck.h"
#include "NativeFFI/ObjC/BeforeTypeCheck/Desugar.h"
#include "NativeFFI/ObjC/Utils/InteropLibBridge.h"
#include "Plugin/PluginCustomAnnoChecker.h"
#include "SearchSymbol.h"
#include "TypeCheckUtil.h"
#include "cangjie/AST/Clone.h"
#include "cangjie/AST/Create.h"
#include "cangjie/AST/Match.h"
#include "cangjie/AST/Node.h"
#include "cangjie/AST/Utils.h"
#include "cangjie/Basic/DiagnosticEngine.h"
#include "cangjie/Basic/Print.h"
#include "cangjie/Frontend/CompilerInstance.h"
#include "cangjie/Utils/CheckUtils.h"
#include "cangjie/Utils/Utils.h"
#include "cangjie/Utils/ProfileRecorder.h"
namespace Cangjie {
using namespace Sema;
using namespace TypeCheckUtil;
using namespace AST;
TypeChecker::TypeChecker(CompilerInstance* ci)
{
impl = std::make_unique<TypeCheckerImpl>(ci);
}
TypeChecker::~TypeChecker()
{
}
TypeChecker::TypeCheckerImpl::TypeCheckerImpl(CompilerInstance* ci)
: promotion(Promotion(*ci->typeManager)),
typeManager(*ci->typeManager),
ci(ci),
diag(ci->diag),
importManager(*ci->importManager),
backendType(ci->invocation.globalOptions.backend),
mpImpl(new MPTypeCheckerImpl(*ci))
{
}
TypeChecker::TypeCheckerImpl::~TypeCheckerImpl()
{
if (mpImpl) {
delete mpImpl;
mpImpl = nullptr;
}
}
bool TypeChecker::TypeCheckerImpl::CheckThisTypeOfFuncBody(const FuncBody& fb) const
{
CJC_ASSERT(fb.retType);
bool returnThis = fb.retType->astKind == ASTKind::THIS_TYPE ||
(fb.retType->astKind == ASTKind::REF_TYPE &&
RawStaticCast<RefType*>(fb.retType.get())->ref.identifier == "This");
if (returnThis && fb.funcDecl != nullptr && fb.funcDecl->TestAttr(Attribute::STATIC)) {
return false;
}
return true;
}
bool TypeChecker::TypeCheckerImpl::IsIndexAssignmentOperator(const AST::FuncDecl& fd) const
{
return fd.op == TokenKind::LSQUARE && fd.funcBody != nullptr && !fd.funcBody->paramLists.empty() &&
!fd.funcBody->paramLists[0]->params.empty() && fd.funcBody->paramLists[0]->params.back()->isNamedParam;
}
bool TypeChecker::TypeCheckerImpl::CheckBodyRetType(ASTContext& ctx, FuncBody& fb)
{
CJC_ASSERT(fb.retType);
CJC_ASSERT(fb.retType->GetTy());
if (fb.body == nullptr) {
if (fb.retType->GetTy()->HasQuestTy()) {
fb.retType->SetTy(TypeManager::GetInvalidTy());
return false;
}
return true;
}
if (fb.retType->GetTy()->IsQuest()) {
auto ret = Synthesize({ctx, SynPos::NONE}, fb.body.get());
if (fb.funcDecl == nullptr && !Ty::IsTyCorrect(ret)) {
fb.retType->SetTy(TypeManager::GetInvalidTy());
return false;
}
fb.retType->SetTy(CalcFuncRetTyFromBody(fb));
bool isWellTyped = Ty::IsTyCorrect(fb.retType->GetTy());
if (fb.retType->GetTy()->HasQuestTy()) {
isWellTyped = false;
fb.retType->SetTy(TypeManager::GetInvalidTy());
if (!CanSkipDiag(fb)) {
DiagUnableToInferReturnType(diag, fb);
}
}
if (isWellTyped) {
if (CheckReturnThisInFuncBody(fb)) {
ReplaceFuncRetTyWithThis(fb, fb.retType->GetTy());
} else if (auto ctt = DynamicCast<ClassThisTy*>(fb.retType->GetTy())) {
fb.retType->SetTy(ctt->declPtr->GetTy());
}
}
return isWellTyped;
} else {
bool isWellTyped = true;
if (fb.retType->GetTy()->IsUnit()) {
isWellTyped = Ty::IsTyCorrect(Synthesize({ctx, SynPos::UNUSED}, fb.body.get()));
} else if (NeedCheckBodyReturn(fb)) {
isWellTyped = Check(ctx, fb.retType->GetTy(), fb.body.get());
if (!isWellTyped && fb.body->body.empty()) {
DiagMismatchedTypes(diag, *fb.body, *fb.retType, "return type");
}
if (isWellTyped && Is<ClassThisTy>(fb.retType->GetTy())) {
auto node = fb.body->body.back().get();
while (true) {
if (auto e = DynamicCast<Expr>(node); e && e->desugarExpr) {
node = e->desugarExpr.get();
} else {
break;
}
}
if (auto k = node->astKind; k != ASTKind::CALL_EXPR && k != ASTKind::MEMBER_ACCESS &&
k != ASTKind::REF_EXPR && k != ASTKind::RETURN_EXPR && k != ASTKind::PAREN_EXPR) {
fb.body->SetTy(typeManager.GetThisRealTy(fb.body->GetTy()));
}
if (!CheckReturnThisInFuncBody(fb) && !fb.retType->GetTy()->IsNothing()) {
DiagMismatchedTypes(diag, *fb.body, *fb.retType, "return type");
isWellTyped = false;
}
}
}
return isWellTyped;
}
}
void TypeChecker::TypeCheckerImpl::ReplaceFuncRetTyWithThis(FuncBody& fb, Ptr<Ty> ty)
{
if (auto ct = DynamicCast<ClassTy*>(ty); ct) {
auto rt = MakeOwned<RefType>();
rt->curFile = fb.curFile;
rt->ref.target = ct->decl;
rt->ref.identifier = "This";
rt->SetTy(typeManager.GetClassThisTy(*ct->declPtr, ct->typeArgs));
rt->EnableAttr(Attribute::COMPILER_ADD);
if (fb.retType) {
CJC_ASSERT(fb.curFile);
fb.curFile->trashBin.emplace_back(std::move(fb.retType));
}
fb.retType = std::move(rt);
}
}
bool TypeChecker::TypeCheckerImpl::CheckFuncBody(ASTContext& ctx, FuncBody& fb)
{
if (fb.retType) {
Synthesize({ctx, SynPos::NONE}, fb.retType.get());
} else {
AddRetTypeNode(fb);
}
if (fb.funcDecl && fb.funcDecl->TestAttr(Attribute::CONSTRUCTOR)) {
CheckCtorFuncBody(ctx, fb);
return true;
}
if (!CheckThisTypeOfFuncBody(fb)) {
diag.Diagnose(*fb.retType, DiagKind::sema_invalid_position_of_this_type);
}
std::vector<Ptr<Ty>> paramTys;
if (!CheckNormalFuncBody(ctx, fb, paramTys)) {
return false;
}
return true;
}
void TypeChecker::TypeCheckerImpl::AddRetTypeNode(FuncBody& fb) const
{
if (!fb.retType) {
fb.retType = MakeOwned<RefType>();
}
fb.retType->EnableAttr(Attribute::COMPILER_ADD);
fb.retType->SetTy(TypeManager::GetQuestTy());
fb.retType->EnableAttr(Attribute::IS_CHECK_VISITED);
}
bool TypeChecker::TypeCheckerImpl::CheckNormalFuncBody(ASTContext& ctx, FuncBody& fb, std::vector<Ptr<Ty>>& paramTys)
{
if (fb.paramLists.empty()) {
return false;
}
CheckFuncParamList(ctx, *fb.paramLists[0]);
paramTys = GetFuncBodyParamTys(fb);
bool isCFFIBackend = IsUnsafeBackend(backendType);
bool isCFunc =
fb.TestAttr(Attribute::C) || (fb.funcDecl && fb.funcDecl->TestAttr(Attribute::FOREIGN) && isCFFIBackend);
bool hasVariableLenArg = (fb.funcDecl && fb.funcDecl->hasVariableLenArg) || fb.paramLists[0]->hasVariableLenArg;
if (!Ty::IsTyCorrect(fb.retType->GetTy())) {
if (!fb.TestAttr(Attribute::IS_CHECK_VISITED)) {
fb.EnableAttr(Attribute::IS_CHECK_VISITED);
Synthesize({ctx, SynPos::NONE}, fb.body.get());
}
fb.SetTy(typeManager.GetFunctionTy(paramTys, fb.retType->GetTy(), {isCFunc, false, hasVariableLenArg}));
return false;
}
auto funcTy = typeManager.GetFunctionTy(paramTys, fb.retType->GetTy(), {isCFunc, false, hasVariableLenArg});
if (fb.funcDecl) {
fb.funcDecl->SetTy(funcTy);
}
fb.SetTy(funcTy);
if (!CheckBodyRetType(ctx, fb)) {
fb.SetTy(typeManager.GetFunctionTy(paramTys, fb.retType->GetTy(), {isCFunc, false, hasVariableLenArg}));
return false;
}
if (isCFFIBackend) {
UnsafeCheck(fb);
}
funcTy = typeManager.GetFunctionTy(paramTys, fb.retType->GetTy(), {isCFunc, false, hasVariableLenArg});
if (fb.funcDecl) {
fb.funcDecl->SetTy(funcTy);
}
fb.SetTy(funcTy);
return true;
}
namespace {
* Add return type(Unit) for function
*/
void AddUnitType(const AST::FuncDecl& fd)
{
if (fd.funcBody == nullptr || fd.funcBody->retType) {
return;
}
OwnedPtr<PrimitiveType> type = MakeOwnedNode<PrimitiveType>();
type->str = "Unit";
type->kind = TypeKind::TYPE_UNIT;
if (!fd.funcBody->paramLists.empty()) {
type->begin = fd.funcBody->paramLists[0]->end;
type->end = fd.funcBody->paramLists[0]->end;
}
type->SetTy(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT));
fd.funcBody->retType = std::move(type);
fd.funcBody->retType->EnableAttr(Attribute::COMPILER_ADD);
}
* Add return type for property member decl
*/
void AddReturnTypeForPropMemDecl(PropDecl& pd)
{
for (auto& getter : pd.getters) {
CJC_NULLPTR_CHECK(getter);
CJC_NULLPTR_CHECK(getter->funcBody);
if (pd.type && !getter->funcBody->retType) {
getter->funcBody->retType = ASTCloner::Clone(pd.type.get());
}
}
for (auto& setter : pd.setters) {
if (!pd.type) {
continue;
}
CJC_NULLPTR_CHECK(setter);
CJC_NULLPTR_CHECK(setter->funcBody);
if (setter->funcBody->paramLists.empty() || setter->funcBody->paramLists[0]->params.empty()) {
return;
}
if (!setter->funcBody->paramLists[0]->params[0]->type) {
setter->funcBody->paramLists[0]->params[0]->type = ASTCloner::Clone(pd.type.get());
}
AddUnitType(*setter);
}
}
* Add Getter/Setter in the abstract property(no body) of Interface/Class.
*/
void AddSetterGetterInProp(const InheritableDecl& cld)
{
for (auto decl : cld.GetMemberDeclPtrs()) {
CJC_ASSERT(decl);
if (decl->astKind != ASTKind::PROP_DECL || decl->TestAttr(Attribute::IMPORTED) ||
decl->TestAttr(Attribute::FROM_COMMON_PART)) {
continue;
}
if (!decl->TestAttr(Attribute::ABSTRACT) && !IsCommonWithoutDefault(*decl)) {
continue;
}
auto propDecl = RawStaticCast<PropDecl*>(decl);
OwnedPtr<FuncDecl> getter = MakeOwnedNode<FuncDecl>();
getter->propDecl = propDecl;
getter->identifier = "get";
getter->isGetter = true;
getter->CloneAttrs(*propDecl);
getter->DisableAttr(Attribute::MUT);
getter->EnableAttr(Attribute::IMPLICIT_ADD);
getter->EnableAttr(Attribute::COMPILER_ADD);
if (cld.astKind == ASTKind::INTERFACE_DECL) {
getter->EnableAttr(Attribute::PUBLIC);
}
auto getFuncParamList = MakeOwnedNode<FuncParamList>();
OwnedPtr<FuncBody> getFuncBody = MakeOwnedNode<FuncBody>();
getFuncBody->paramLists.push_back(std::move(getFuncParamList));
getter->funcBody = std::move(getFuncBody);
propDecl->getters.emplace_back(std::move(getter));
if (!propDecl->isVar) {
continue;
}
OwnedPtr<FuncDecl> setter = MakeOwnedNode<FuncDecl>();
setter->propDecl = propDecl;
setter->isSetter = true;
setter->identifier = "set";
setter->CloneAttrs(*propDecl);
setter->DisableAttr(Attribute::MUT);
setter->EnableAttr(Attribute::IMPLICIT_ADD);
if (cld.astKind == ASTKind::INTERFACE_DECL) {
setter->EnableAttr(Attribute::PUBLIC);
}
auto setFuncParamList = MakeOwnedNode<FuncParamList>();
auto param = CreateFuncParam("set");
setFuncParamList->params.push_back(std::move(param));
OwnedPtr<FuncBody> setFuncBody = MakeOwnedNode<FuncBody>();
setFuncBody->paramLists.push_back(std::move(setFuncParamList));
setter->funcBody = std::move(setFuncBody);
propDecl->setters.emplace_back(std::move(setter));
}
}
constexpr std::string_view INOUT_TEMP_VALUE{"temporary value"};
constexpr std::string_view INOUT_IMMUTABLE{"is a immutable variable"};
}
bool TypeChecker::TypeCheckerImpl::CheckReturnThisInFuncBody(const FuncBody& fb) const
{
std::function<bool(Ptr<Expr>)> checkExprType = [&checkExprType](Ptr<Expr> expr) {
while (expr->desugarExpr) {
expr = expr->desugarExpr.get();
}
if (auto refExpr = DynamicCast<RefExpr*>(expr); refExpr) {
return refExpr->isThis;
} else if (auto ce = DynamicCast<CallExpr*>(expr); ce && ce->resolvedFunction) {
bool isMemberCall = Is<RefExpr>(ce->baseFunc.get());
if (auto ma = DynamicCast<MemberAccess*>(ce->baseFunc.get()); ma && ma->baseExpr) {
isMemberCall = IsThisOrSuper(*ma->baseExpr);
}
return isMemberCall && IsFuncReturnThisType(*ce->resolvedFunction);
} else if (auto paren = DynamicCast<ParenExpr>(expr)) {
return checkExprType(paren->expr);
}
bool returnThis = false;
auto visitor = [&returnThis, &checkExprType](Ptr<Node> n) {
CJC_ASSERT(n);
if (n->astKind != ASTKind::RETURN_EXPR) {
return VisitAction::WALK_CHILDREN;
}
auto re = RawStaticCast<ReturnExpr*>(n);
returnThis = checkExprType(re->expr.get());
return VisitAction::SKIP_CHILDREN;
};
Walker(expr, visitor).Walk();
return returnThis;
};
if (fb.body != nullptr && !fb.body->body.empty()) {
auto lastNode = fb.body->body.back().get();
if (auto expr = DynamicCast<Expr*>(lastNode); expr) {
while (expr->desugarExpr) {
expr = expr->desugarExpr.get();
}
return checkExprType(expr) && Is<ClassDecl>(fb.parentClassLike);
}
}
return false;
}
void TypeChecker::TypeCheckerImpl::CheckCtorFuncBody(ASTContext& ctx, FuncBody& fb)
{
CJC_ASSERT(fb.funcDecl);
if (fb.parentClassLike) {
if (auto attr = HasJavaAttr(*fb.parentClassLike); attr) {
fb.funcDecl->EnableAttr(attr.value());
}
}
Ptr<Ty> ctorTy = TypeManager::GetInvalidTy();
if (fb.funcDecl->TestAttr(Attribute::STATIC)) {
ctorTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT);
} else {
if (fb.parentStruct) {
ctorTy = fb.parentStruct->GetTy();
}
if (fb.parentClassLike) {
ctorTy = fb.parentClassLike->GetTy();
}
}
if (!Ty::IsTyCorrect(ctorTy) || fb.paramLists.empty()) {
return;
}
CheckFuncParamList(ctx, *fb.paramLists[0].get());
auto paramTys = GetFuncBodyParamTys(fb);
fb.SetTy(typeManager.GetFunctionTy(paramTys, ctorTy));
fb.funcDecl->SetTy(fb.GetTy());
fb.retType->SetTy(ctorTy);
Synthesize({ctx, SynPos::UNUSED}, fb.body.get());
}
void TypeChecker::TypeCheckerImpl::CheckFuncParamList(ASTContext& ctx, FuncParamList& fpl)
{
std::vector<Ptr<Ty>> paramTys;
if (fpl.params.empty()) {
fpl.SetTy(typeManager.GetTupleTy(paramTys));
return;
}
for (auto& param : fpl.params) {
CJC_NULLPTR_CHECK(param);
if (!Ty::IsTyCorrect(Synthesize({ctx, SynPos::NONE}, param.get()))) {
paramTys.push_back(TypeManager::GetInvalidTy());
continue;
}
Ptr<Ty> paramTy = param->GetTy();
paramTys.push_back(paramTy);
if (param->assignment) {
auto curNode = param->assignment.get();
auto expr = param->assignment.get();
while (expr != nullptr && expr->desugarExpr != nullptr) {
curNode = expr->desugarExpr.get();
expr = As<ASTKind::EXPR>(curNode);
}
(void)Check(ctx, paramTy, curNode);
if (Ty::IsTyCorrect(curNode->GetTy())) {
param->assignment->SetTy(curNode->GetTy());
}
}
}
fpl.SetTy(typeManager.GetTupleTy(paramTys));
}
bool TypeChecker::TypeCheckerImpl::ChkFuncArg(ASTContext& ctx, Ty& target, FuncArg& fa)
{
if (!fa.expr) {
fa.SetTy(TypeManager::GetInvalidTy());
return false;
}
if (fa.withInout) {
return ChkFuncArgWithInout(ctx, target, fa);
}
if (!Check(ctx, &target, fa.expr.get())) {
fa.SetTy(TypeManager::GetInvalidTy());
return false;
}
fa.SetTy(fa.expr->GetTy());
return true;
}
bool TypeChecker::TypeCheckerImpl::ChkFuncArgWithInout(ASTContext& ctx, Ty& target, FuncArg& fa)
{
auto realTarget = ⌖
auto exprTy = fa.expr->GetTy();
if (!Ty::IsTyCorrect(fa.expr->GetTy())) {
exprTy = Synthesize({ctx, SynPos::EXPR_ARG}, fa.expr.get());
}
auto argTy = DynamicCast<VArrayTy*>(exprTy);
auto ptrParamTy = DynamicCast<PointerTy*>(&target);
auto varrParamTy = DynamicCast<VArrayTy*>(&target);
if (argTy && ptrParamTy) {
realTarget = typeManager.GetVArrayTy(*ptrParamTy->typeArgs[0], argTy->size);
} else if (argTy && varrParamTy) {
realTarget = ⌖
} else if (ptrParamTy) {
realTarget = ptrParamTy->typeArgs[0];
}
if (!Ty::IsTyCorrect(exprTy) || !typeManager.IsSubtype(exprTy, realTarget)) {
if (Ty::IsTyCorrect(exprTy)) {
DiagMismatchedTypes(diag, *fa.expr, *realTarget);
}
fa.SetTy(TypeManager::GetInvalidTy());
return false;
}
if (!ChkInoutFuncArg(fa)) {
fa.SetTy(TypeManager::GetInvalidTy());
return false;
}
fa.SetTy(typeManager.GetPointerTy(Is<VArrayTy>(realTarget) ? realTarget->typeArgs[0].get() : realTarget));
return true;
}
bool TypeChecker::TypeCheckerImpl::ChkInoutFuncArg(const FuncArg& fa)
{
switch (fa.expr->astKind) {
case ASTKind::REF_EXPR:
return ChkInoutRefExpr(*StaticCast<RefExpr*>(fa.expr.get()));
case ASTKind::MEMBER_ACCESS:
return ChkInoutMemberAccess(*StaticCast<MemberAccess*>(fa.expr.get()));
case ASTKind::LIT_CONST_EXPR: {
auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, *fa.expr);
builder.AddMainHintArguments("literal");
return false;
}
default: {
auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, *fa.expr);
builder.AddMainHintArguments(std::string(INOUT_TEMP_VALUE));
return false;
}
}
}
bool TypeChecker::TypeCheckerImpl::ChkInoutRefExpr(RefExpr& re, bool isBase)
{
auto target = GetRealTarget(&re, re.GetTarget());
bool thisOrSuper = re.isThis || re.isSuper;
if (isBase && thisOrSuper) {
return true;
}
if (thisOrSuper || !target || !Utils::In(target->astKind, {ASTKind::VAR_DECL, ASTKind::FUNC_PARAM})) {
auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, re);
builder.AddMainHintArguments("not a variable");
return false;
}
auto vd = StaticAs<ASTKind::VAR_DECL>(target);
if (!vd->isVar) {
auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, re);
builder.AddMainHintArguments(std::string(INOUT_IMMUTABLE));
return false;
}
if (vd->outerDecl && vd->outerDecl->IsNominalDecl()) {
if (vd->outerDecl->astKind != ASTKind::STRUCT_DECL && !vd->TestAttr(Attribute::STATIC)) {
(void)diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_heap_variable, re);
return false;
}
}
return true;
}
* For member access a.n:
* - if a is VArray value and n is "size", it's desugar as a LitConstExpr, report error;
* - if a or n defined with 'let', report error;
* - if a is class object, report error.
*/
bool TypeChecker::TypeCheckerImpl::ChkInoutMemberAccess(const MemberAccess& ma)
{
if (ma.desugarExpr && ma.desugarExpr->astKind == ASTKind::LIT_CONST_EXPR) {
auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, ma);
builder.AddMainHintArguments(std::string(INOUT_IMMUTABLE));
return false;
}
if (!ma.target) {
return false;
}
bool meetContraints = true;
if (ma.target->astKind == ASTKind::VAR_DECL) {
auto vd = StaticAs<ASTKind::VAR_DECL>(ma.target);
if (!vd->isVar) {
auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, ma.field.Begin());
builder.AddMainHintArguments(std::string(INOUT_IMMUTABLE));
meetContraints = false;
}
if (vd->TestAttr(Attribute::STATIC)) {
return meetContraints;
}
} else {
auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, ma);
if (ma.target->astKind == ASTKind::FUNC_DECL) {
builder.AddMainHintArguments("function declaration");
} else {
builder.AddMainHintArguments(std::string(INOUT_TEMP_VALUE));
}
meetContraints = false;
}
auto& be = ma.baseExpr;
if (Ty::IsTyCorrect(be->GetTy()) && be->GetTy()->IsClassLike()) {
diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_heap_variable, *be);
return false;
}
if (be->astKind == ASTKind::REF_EXPR) {
return ChkInoutRefExpr(static_cast<RefExpr&>(*be), true) && meetContraints;
}
if (be->astKind == ASTKind::MEMBER_ACCESS) {
if (auto beTarget = GetRealTarget(be.get(), be->GetTarget());
beTarget && beTarget->astKind != ASTKind::PACKAGE_DECL) {
return ChkInoutMemberAccess(static_cast<MemberAccess&>(*be)) && meetContraints;
}
return false;
}
auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, *be);
builder.AddMainHintArguments(std::string(INOUT_TEMP_VALUE));
return false;
}
Ptr<Ty> TypeChecker::TypeCheckerImpl::SynFuncArg(ASTContext& ctx, FuncArg& fa)
{
if (fa.expr) {
Synthesize({ctx, SynPos::EXPR_ARG}, fa.expr.get());
if (fa.withInout) {
if (!Ty::IsTyCorrect(fa.expr->GetTy()) || !ChkInoutFuncArg(fa)) {
fa.SetTy(TypeManager::GetInvalidTy());
} else if (!Ty::IsMetCType(*fa.expr->GetTy())) {
diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_non_ctype, fa);
fa.SetTy(TypeManager::GetInvalidTy());
} else if (fa.expr->GetTy()->IsCString()) {
diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_cstring_or_zerosized, fa, "type 'CString'");
fa.SetTy(TypeManager::GetInvalidTy());
} else if (Is<VArrayTy>(fa.expr->GetTy())) {
fa.SetTy(typeManager.GetPointerTy(fa.expr->GetTy()->typeArgs[0]));
} else {
fa.SetTy(typeManager.GetPointerTy(fa.expr->GetTy()));
}
} else {
fa.SetTy(fa.expr->GetTy());
}
} else {
fa.SetTy(TypeManager::GetInvalidTy());
}
return fa.GetTy();
}
void TypeChecker::TypeCheckerImpl::SubstituteTypeArguments(
const TypeAliasDecl& tad, std::vector<OwnedPtr<Type>>& typeArguments, const TypeSubst& typeMapping)
{
if (!typeArguments.empty() && typeArguments[0]->TestAttr(Attribute::COMPILER_ADD)) {
return;
}
if (auto rt = DynamicCast<AST::RefType*>(tad.type.get())) {
SubstituteTypeArguments(typeArguments, *rt, typeMapping);
}
if (auto qt = DynamicCast<AST::QualifiedType*>(tad.type.get())) {
SubstituteTypeArguments(typeArguments, *qt, typeMapping);
}
}
namespace {
bool IsNodeDesugared(Ptr<const Node> node)
{
if (auto expr = DynamicCast<Expr>(node)) {
return expr->desugarExpr.get();
}
return false;
}
CacheKey GetCacheKeyForSyn(const ASTContext& ctx, Ptr<const Node> node)
{
auto it = ctx.targetTypeMap.find(node);
auto target = it != ctx.targetTypeMap.cend() ? it->second : nullptr;
return CacheKey{
.target = target, .isDesugared = IsNodeDesugared(node), .diagKey = DiagnosticCache::ExtractKey(ctx.diag)};
}
CacheKey GetCacheKeyForChk(const ASTContext& ctx, Ptr<const Node> node, Ptr<Ty> target)
{
return CacheKey{
.target = target, .isDesugared = IsNodeDesugared(node), .diagKey = DiagnosticCache::ExtractKey(ctx.diag)};
}
void RestoreCached(ASTContext& ctx, Ptr<Node> node, CacheEntry& cache, bool recoverDiag = true)
{
if (Ty::IsInitialTy(node->GetTy())) {
ctx.typeCheckCache[node].lastKey = {};
}
node->SetTy(cache.result);
RestoreTargets(*node, cache.targets);
if (recoverDiag) {
cache.diags.Restore(ctx.diag);
}
}
}
bool TypeChecker::TypeCheckerImpl::IsChecked(ASTContext& ctx, Node& node) const
{
auto decl = As<ASTKind::DECL>(&node);
auto type = As<ASTKind::TYPE>(&node);
bool visitedDecl = decl && (decl->TestAttr(Attribute::IS_CHECK_VISITED) || decl->TestAttr(Attribute::IMPORTED));
bool visitedExpr =
!decl && !type && ctx.typeCheckCache[&node].lastKey.has_value() && typeManager.GetUnsolvedTyVars().empty();
return Ty::IsTyCorrect(node.GetTy()) && node.TyKind() != AST::TypeKind::TYPE_QUEST &&
(visitedExpr || visitedDecl || (type && type->TestAttr(Attribute::IS_CHECK_VISITED)));
}
std::optional<Ptr<Ty>> TypeChecker::TypeCheckerImpl::PerformBasicChecksForSynthesize(
ASTContext& ctx, Ptr<Node> node) const
{
if (!node) {
return {TypeManager::GetInvalidTy()};
}
if (node->TestAttr(Attribute::IS_BROKEN)) {
node->SetTy(TypeManager::GetInvalidTy());
return {node->GetTy()};
}
if (IsChecked(ctx, *node)) {
return {node->GetTy()};
}
if (node->IsDecl() && !node->symbol) {
return {node->GetTy()};
}
return {};
}
Ptr<Ty> TypeChecker::TypeCheckerImpl::Synthesize(const CheckerContext& ctx, Ptr<Node> node)
{
if (auto res = PerformBasicChecksForSynthesize(ctx.Ctx(), node)) {
return *res;
}
ctx.Ctx().typeCheckCache[node].lastKey = GetCacheKeyForSyn(ctx.Ctx(), node);
ASTContext* curCtx = &ctx.Ctx();
if (ci->GetSourcePackages().size() > 1 && node->curFile) {
if (auto ctx1 = ci->GetASTContextByPackage(node->curFile->curPackage)) {
curCtx = ctx1;
}
}
CheckerContext newCtx{*curCtx, ctx.SynthPos()};
if (auto decl = DynamicCast<Decl*>(node)) {
auto stashDiagnoseStatus = diag.AutoStashDisableDiagnoseStatus();
CheckAnnotations(*curCtx, *decl);
}
switch (node->astKind) {
case ASTKind::FUNC_DECL: {
auto stashDiagnoseStatus = diag.AutoStashDisableDiagnoseStatus();
if (StaticAs<ASTKind::FUNC_DECL>(node)->ownerFunc) {
auto ds = DiagSuppressor(diag);
CheckFuncDecl(*curCtx, *StaticAs<ASTKind::FUNC_DECL>(node));
} else {
CheckFuncDecl(*curCtx, *StaticAs<ASTKind::FUNC_DECL>(node));
}
break;
}
case ASTKind::FUNC_BODY: {
(void)CheckFuncBody(*curCtx, *StaticAs<ASTKind::FUNC_BODY>(node));
break;
}
case ASTKind::BLOCK: {
node->SetTy(SynBlock(newCtx, *StaticAs<ASTKind::BLOCK>(node)));
break;
}
case ASTKind::FUNC_PARAM_LIST: {
CheckFuncParamList(*curCtx, *StaticAs<ASTKind::FUNC_PARAM_LIST>(node));
break;
}
case ASTKind::FUNC_PARAM: {
node->SetTy(SynFuncParam(*curCtx, *StaticAs<ASTKind::FUNC_PARAM>(node)));
break;
}
case ASTKind::FUNC_ARG: {
node->SetTy(SynFuncArg(*curCtx, *StaticAs<ASTKind::FUNC_ARG>(node)));
break;
}
case ASTKind::INC_OR_DEC_EXPR: {
node->SetTy(SynIncOrDecExpr(*curCtx, *StaticAs<ASTKind::INC_OR_DEC_EXPR>(node)));
break;
}
case ASTKind::PAREN_EXPR: {
node->SetTy(SynParenExpr(newCtx, *StaticAs<ASTKind::PAREN_EXPR>(node)));
break;
}
case ASTKind::LAMBDA_EXPR: {
node->SetTy(SynLamExpr(*curCtx, *StaticAs<ASTKind::LAMBDA_EXPR>(node)));
break;
}
case ASTKind::RETURN_EXPR: {
node->SetTy(SynReturnExpr(*curCtx, *StaticAs<ASTKind::RETURN_EXPR>(node)));
break;
}
case ASTKind::LIT_CONST_EXPR: {
node->SetTy(SynLitConstExpr(*curCtx, *StaticAs<ASTKind::LIT_CONST_EXPR>(node)));
break;
}
case ASTKind::WHILE_EXPR: {
node->SetTy(SynWhileExpr(*curCtx, *StaticAs<ASTKind::WHILE_EXPR>(node)));
break;
}
case ASTKind::DO_WHILE_EXPR: {
node->SetTy(SynDoWhileExpr(*curCtx, *StaticAs<ASTKind::DO_WHILE_EXPR>(node)));
break;
}
case ASTKind::FOR_IN_EXPR: {
node->SetTy(SynForInExpr(*curCtx, *StaticAs<ASTKind::FOR_IN_EXPR>(node)));
break;
}
case ASTKind::UNARY_EXPR: {
node->SetTy(SynUnaryExpr(*curCtx, *StaticAs<ASTKind::UNARY_EXPR>(node)));
break;
}
case ASTKind::BINARY_EXPR: {
node->SetTy(SynBinaryExpr(*curCtx, *StaticAs<ASTKind::BINARY_EXPR>(node)));
break;
}
case ASTKind::ASSIGN_EXPR: {
node->SetTy(SynAssignExpr(*curCtx, *StaticAs<ASTKind::ASSIGN_EXPR>(node)));
break;
}
case ASTKind::QUOTE_EXPR: {
node->SetTy(SynQuoteExpr(*curCtx, *StaticAs<ASTKind::QUOTE_EXPR>(node)));
break;
}
case ASTKind::IF_EXPR: {
node->SetTy(SynIfExpr(newCtx, *StaticAs<ASTKind::IF_EXPR>(node)));
break;
}
case ASTKind::TRY_EXPR: {
node->SetTy(SynTryExpr(*curCtx, *StaticAs<ASTKind::TRY_EXPR>(node)));
break;
}
case ASTKind::REF_EXPR: {
auto re = StaticAs<ASTKind::REF_EXPR>(node);
InferRefExpr(*curCtx, *re);
break;
}
case ASTKind::PRIMITIVE_TYPE_EXPR: {
auto te = StaticAs<ASTKind::PRIMITIVE_TYPE_EXPR>(node);
te->SetTy(TypeManager::GetPrimitiveTy(te->typeKind));
break;
}
case ASTKind::CALL_EXPR: {
node->SetTy(SynCallExpr(*curCtx, *StaticAs<ASTKind::CALL_EXPR>(node)));
break;
}
case ASTKind::TRAIL_CLOSURE_EXPR: {
node->SetTy(SynTrailingClosure(*curCtx, *StaticAs<ASTKind::TRAIL_CLOSURE_EXPR>(node)));
break;
}
case ASTKind::MEMBER_ACCESS: {
auto ma = StaticAs<ASTKind::MEMBER_ACCESS>(node);
InferMemberAccess(*curCtx, *ma);
break;
}
case ASTKind::TYPE_CONV_EXPR: {
node->SetTy(SynTypeConvExpr(*curCtx, *StaticAs<ASTKind::TYPE_CONV_EXPR>(node)));
break;
}
case ASTKind::IF_AVAILABLE_EXPR:
node->SetTy(SynIfAvailableExpr(*curCtx, StaticCast<IfAvailableExpr>(*node)));
break;
case ASTKind::ARRAY_LIT: {
node->SetTy(SynArrayLit(*curCtx, *StaticAs<ASTKind::ARRAY_LIT>(node)));
break;
}
case ASTKind::ARRAY_EXPR: {
auto ae = StaticAs<ASTKind::ARRAY_EXPR>(node);
node->SetTy(ae->isValueArray ? SynVArrayExpr(*curCtx, *ae) : SynArrayExpr(*curCtx, *ae));
break;
}
case ASTKind::POINTER_EXPR: {
node->SetTy(SynPointerExpr(*curCtx, *StaticAs<ASTKind::POINTER_EXPR>(node)));
break;
}
case ASTKind::MATCH_EXPR: {
node->SetTy(SynMatchExpr(*curCtx, *StaticAs<ASTKind::MATCH_EXPR>(node)));
break;
}
case ASTKind::IS_EXPR: {
node->SetTy(SynIsExpr(*curCtx, *StaticAs<ASTKind::IS_EXPR>(node)));
break;
}
case ASTKind::AS_EXPR: {
node->SetTy(SynAsExpr(*curCtx, *StaticAs<ASTKind::AS_EXPR>(node)));
break;
}
case ASTKind::OPTIONAL_CHAIN_EXPR: {
node->SetTy(SynOptionalChainExpr(newCtx, *StaticAs<ASTKind::OPTIONAL_CHAIN_EXPR>(node)));
break;
}
case ASTKind::ENUM_DECL: {
CheckEnumDecl(*curCtx, *StaticAs<ASTKind::ENUM_DECL>(node));
break;
}
case ASTKind::STRUCT_DECL: {
auto sd = StaticAs<ASTKind::STRUCT_DECL>(node);
CheckStructDecl(*curCtx, *sd);
break;
}
case ASTKind::CLASS_DECL: {
auto cd = StaticAs<ASTKind::CLASS_DECL>(node);
CheckClassDecl(*curCtx, *cd);
break;
}
case ASTKind::INTERFACE_DECL: {
auto id = StaticAs<ASTKind::INTERFACE_DECL>(node);
CheckInterfaceDecl(*curCtx, *id);
break;
}
case ASTKind::TUPLE_LIT: {
node->SetTy(SynTupleLit(*curCtx, *StaticAs<ASTKind::TUPLE_LIT>(node)));
break;
}
case ASTKind::JUMP_EXPR: {
node->SetTy(SynLoopControlExpr(*curCtx, *StaticAs<ASTKind::JUMP_EXPR>(node)));
break;
}
case ASTKind::THROW_EXPR: {
node->SetTy(SynThrowExpr(*curCtx, *StaticAs<ASTKind::THROW_EXPR>(node)));
break;
}
#ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND
case ASTKind::PERFORM_EXPR: {
node->SetTy(SynPerformExpr(*curCtx, *StaticAs<ASTKind::PERFORM_EXPR>(node)));
break;
}
case ASTKind::RESUME_EXPR: {
node->SetTy(SynResumeExpr(*curCtx, *StaticAs<ASTKind::RESUME_EXPR>(node)));
break;
}
#endif
case ASTKind::TYPE_ALIAS_DECL: {
CheckTypeAlias(*curCtx, *StaticAs<ASTKind::TYPE_ALIAS_DECL>(node));
break;
}
case ASTKind::SUBSCRIPT_EXPR: {
node->SetTy(SynSubscriptExpr(*curCtx, *StaticAs<ASTKind::SUBSCRIPT_EXPR>(node)));
break;
}
case ASTKind::RANGE_EXPR: {
node->SetTy(SynRangeExpr(*curCtx, *StaticAs<ASTKind::RANGE_EXPR>(node)));
break;
}
case ASTKind::TYPE:
case ASTKind::INVALID_TYPE:
case ASTKind::OPTION_TYPE:
case ASTKind::THIS_TYPE:
case ASTKind::PAREN_TYPE:
case ASTKind::VARRAY_TYPE:
case ASTKind::FUNC_TYPE:
case ASTKind::TUPLE_TYPE:
case ASTKind::PRIMITIVE_TYPE:
case ASTKind::REF_TYPE:
case ASTKind::QUALIFIED_TYPE: {
CheckReferenceTypeLegality(*curCtx, *StaticAs<ASTKind::TYPE>(node));
break;
}
case ASTKind::PROP_DECL: {
auto propDecl = StaticAs<ASTKind::PROP_DECL>(node);
CheckPropDecl(*curCtx, *propDecl);
break;
}
case ASTKind::VAR_WITH_PATTERN_DECL: {
auto vpd = StaticAs<ASTKind::VAR_WITH_PATTERN_DECL>(node);
CheckVarWithPatternDecl(*curCtx, *vpd);
break;
}
case ASTKind::VAR_DECL: {
if (IsGlobalOrMember(*node)) {
auto stashDiagnoseStatus = diag.AutoStashDisableDiagnoseStatus();
CheckVarDecl(*curCtx, *StaticAs<ASTKind::VAR_DECL>(node));
} else {
CheckVarDecl(*curCtx, *StaticAs<ASTKind::VAR_DECL>(node));
}
break;
}
case ASTKind::SPAWN_EXPR: {
node->SetTy(SynSpawnExpr(*curCtx, *StaticAs<ASTKind::SPAWN_EXPR>(node)));
break;
}
case ASTKind::SYNCHRONIZED_EXPR: {
node->SetTy(SynSyncExpr(*curCtx, *StaticAs<ASTKind::SYNCHRONIZED_EXPR>(node)));
break;
}
case ASTKind::EXTEND_DECL: {
CheckExtendDecl(*curCtx, *StaticAs<ASTKind::EXTEND_DECL>(node));
break;
}
case ASTKind::MACRO_EXPAND_EXPR:
case ASTKind::MACRO_EXPAND_PARAM:
case ASTKind::MACRO_EXPAND_DECL: {
CheckMacroCall(*curCtx, *node);
break;
}
case ASTKind::INVALID_EXPR: {
node->SetTy(TypeManager::GetInvalidTy());
break;
}
default: {
break;
}
}
CJC_ASSERT(!Ty::IsTyCorrect(node->GetTy()) || As<ASTKind::EXPR>(node) == nullptr ||
StaticAs<ASTKind::EXPR>(node)->desugarExpr == nullptr ||
node->GetTy() == StaticAs<ASTKind::EXPR>(node)->desugarExpr->GetTy());
node->SetTy(typeManager.TryGreedySubst(node->GetTy()));
return TypeManager::GetNonNullTy(node->GetTy());
}
std::optional<bool> TypeChecker::TypeCheckerImpl::PerformBasicChecksForCheck(
ASTContext& ctx, Ptr<Ty> target, Ptr<Node> node) const
{
if (!node) {
return {false};
}
if (!Ty::IsTyCorrect(target)) {
node->SetTy(TypeManager::GetInvalidTy());
return {false};
}
ctx.targetTypeMap[node] = target;
if (node->TestAttr(Attribute::IS_BROKEN)) {
node->SetTy(TypeManager::GetInvalidTy());
return {false};
}
if (target->HasQuestTy() && !IsQuestableNode(*node)) {
return {false};
}
return {};
}
bool TypeChecker::TypeCheckerImpl::Check(ASTContext& ctx, Ptr<Ty> target, Ptr<Node> node)
{
if (auto res = PerformBasicChecksForCheck(ctx, target, node)) {
return *res;
}
ctx.typeCheckCache[node].lastKey = GetCacheKeyForChk(ctx, node, target);
ASTContext* curCtx = &ctx;
if (ci->GetSourcePackages().size() > 1) {
if (auto decl = AST::As<ASTKind::DECL>(node); decl && decl->curFile) {
if (auto ctx1 = ci->GetASTContextByPackage(decl->curFile->curPackage)) {
curCtx = ctx1;
}
}
}
bool chkRet = false;
auto realTarget = typeManager.TryGreedySubst(target);
if (realTarget->IsPlaceholder() && !AcceptPlaceholderTarget(*node)) {
auto& cst = typeManager.constraints[RawStaticCast<GenericsTy*>(realTarget)];
Ptr<Ty> lub = nullptr;
if (!cst.ubs.empty()) {
auto meetRes = JoinAndMeet(typeManager, cst.ubs.raw(), typeManager.GetUnsolvedTyVars()).MeetAsVisibleTy();
if (std::holds_alternative<Ptr<Ty>>(meetRes)) {
lub = std::get<Ptr<Ty>>(meetRes);
}
}
if (lub) {
chkRet = Check(ctx, lub, node) && typeManager.IsSubtype(node->GetTy(), realTarget);
} else {
Synthesize({ctx, SynPos::NONE}, node);
ReplaceIdealTy(*node);
chkRet = typeManager.IsSubtype(node->GetTy(), realTarget);
}
} else {
switch (node->astKind) {
case ASTKind::IF_EXPR: {
chkRet = ChkIfExpr(*curCtx, *realTarget, *StaticAs<ASTKind::IF_EXPR>(node));
break;
}
case ASTKind::ASSIGN_EXPR: {
chkRet = ChkAssignExpr(*curCtx, *realTarget, *StaticAs<ASTKind::ASSIGN_EXPR>(node));
break;
}
case ASTKind::LIT_CONST_EXPR: {
auto lce = StaticAs<ASTKind::LIT_CONST_EXPR>(node);
chkRet = ChkLitConstExpr(*curCtx, *realTarget, *lce);
InitializeLitConstValue(*lce);
break;
}
case ASTKind::ARRAY_LIT: {
chkRet = ChkArrayLit(*curCtx, *realTarget, *StaticAs<ASTKind::ARRAY_LIT>(node));
break;
}
case ASTKind::ARRAY_EXPR: {
auto ae = StaticAs<ASTKind::ARRAY_EXPR>(node);
chkRet = ae->isValueArray ? ChkVArrayExpr(*curCtx, *realTarget, *ae)
: ChkArrayExpr(*curCtx, *realTarget, *ae);
break;
}
case ASTKind::POINTER_EXPR: {
chkRet = ChkPointerExpr(*curCtx, *realTarget, *StaticAs<ASTKind::POINTER_EXPR>(node));
break;
}
case ASTKind::TUPLE_LIT: {
chkRet = ChkTupleLit(*curCtx, *realTarget, *StaticAs<ASTKind::TUPLE_LIT>(node));
break;
}
case ASTKind::WHILE_EXPR: {
chkRet = ChkWhileExpr(*curCtx, *realTarget, *StaticAs<ASTKind::WHILE_EXPR>(node));
break;
}
case ASTKind::DO_WHILE_EXPR: {
chkRet = ChkDoWhileExpr(*curCtx, *realTarget, *StaticAs<ASTKind::DO_WHILE_EXPR>(node));
break;
}
case ASTKind::FOR_IN_EXPR: {
chkRet = ChkForInExpr(*curCtx, *realTarget, *StaticAs<ASTKind::FOR_IN_EXPR>(node));
break;
}
case ASTKind::RANGE_EXPR: {
chkRet = ChkRangeExpr(*curCtx, *realTarget, *StaticAs<ASTKind::RANGE_EXPR>(node));
break;
}
case ASTKind::PAREN_EXPR: {
chkRet = ChkParenExpr(*curCtx, *realTarget, *StaticAs<ASTKind::PAREN_EXPR>(node));
break;
}
case ASTKind::BINARY_EXPR: {
chkRet = ChkBinaryExpr(ctx, *realTarget, *StaticAs<ASTKind::BINARY_EXPR>(node));
break;
}
case ASTKind::INC_OR_DEC_EXPR: {
chkRet = ChkIncOrDecExpr(*curCtx, *realTarget, *StaticAs<ASTKind::INC_OR_DEC_EXPR>(node));
break;
}
case ASTKind::UNARY_EXPR: {
chkRet = ChkUnaryExpr(*curCtx, *realTarget, *StaticAs<ASTKind::UNARY_EXPR>(node));
break;
}
case ASTKind::TYPE_CONV_EXPR: {
chkRet = ChkTypeConvExpr(*curCtx, *realTarget, *StaticAs<ASTKind::TYPE_CONV_EXPR>(node));
break;
}
case ASTKind::IF_AVAILABLE_EXPR:
chkRet = ChkIfAvailableExpr(*curCtx, *realTarget, StaticCast<IfAvailableExpr>(*node));
break;
case ASTKind::JUMP_EXPR:
chkRet = ChkLoopControlExpr(*curCtx, *StaticAs<ASTKind::JUMP_EXPR>(node));
break;
case ASTKind::MATCH_EXPR: {
chkRet = ChkMatchExpr(*curCtx, *realTarget, *StaticAs<ASTKind::MATCH_EXPR>(node));
break;
}
case ASTKind::EXCEPT_TYPE_PATTERN:
case ASTKind::COMMAND_TYPE_PATTERN:
case ASTKind::WILDCARD_PATTERN:
case ASTKind::CONST_PATTERN:
case ASTKind::TYPE_PATTERN:
case ASTKind::VAR_PATTERN:
case ASTKind::TUPLE_PATTERN:
case ASTKind::ENUM_PATTERN:
case ASTKind::VAR_OR_ENUM_PATTERN: {
chkRet = ChkPattern(*curCtx, *realTarget, *StaticAs<ASTKind::PATTERN>(node));
break;
}
case ASTKind::BLOCK: {
chkRet = ChkBlock(*curCtx, *realTarget, *StaticAs<ASTKind::BLOCK>(node));
break;
}
case ASTKind::SUBSCRIPT_EXPR: {
chkRet = ChkSubscriptExpr(*curCtx, realTarget, *StaticAs<ASTKind::SUBSCRIPT_EXPR>(node));
break;
}
case ASTKind::MEMBER_ACCESS:
case ASTKind::REF_EXPR: {
chkRet = ChkRefExpr(*curCtx, *realTarget, *RawStaticCast<NameReferenceExpr*>(node));
break;
}
case ASTKind::CALL_EXPR: {
chkRet = ChkCallExpr(*curCtx, realTarget, *StaticAs<ASTKind::CALL_EXPR>(node));
break;
}
case ASTKind::TRAIL_CLOSURE_EXPR: {
chkRet = ChkTrailingClosureExpr(*curCtx, *realTarget, *StaticAs<ASTKind::TRAIL_CLOSURE_EXPR>(node));
break;
}
case ASTKind::TRY_EXPR: {
chkRet = ChkTryExpr(*curCtx, *realTarget, *StaticAs<ASTKind::TRY_EXPR>(node));
break;
}
case ASTKind::RETURN_EXPR: {
chkRet = ChkReturnExpr(*curCtx, *StaticAs<ASTKind::RETURN_EXPR>(node));
break;
}
case ASTKind::LAMBDA_EXPR: {
chkRet = ChkLamExpr(*curCtx, *realTarget, *StaticAs<ASTKind::LAMBDA_EXPR>(node));
break;
}
case ASTKind::QUOTE_EXPR: {
chkRet = ChkQuoteExpr(*curCtx, *realTarget, *StaticAs<ASTKind::QUOTE_EXPR>(node));
break;
}
case ASTKind::FUNC_PARAM: {
chkRet = ChkFuncParam(*curCtx, *realTarget, *StaticAs<ASTKind::FUNC_PARAM>(node));
break;
}
case ASTKind::FUNC_ARG: {
chkRet = ChkFuncArg(*curCtx, *realTarget, *StaticAs<ASTKind::FUNC_ARG>(node));
break;
}
case ASTKind::SPAWN_EXPR: {
chkRet = ChkSpawnExpr(*curCtx, *realTarget, *StaticAs<ASTKind::SPAWN_EXPR>(node));
break;
}
case ASTKind::SYNCHRONIZED_EXPR: {
chkRet = ChkSyncExpr(*curCtx, realTarget, *StaticAs<ASTKind::SYNCHRONIZED_EXPR>(node));
break;
}
case ASTKind::IS_EXPR: {
chkRet = ChkIsExpr(*curCtx, *realTarget, *StaticAs<ASTKind::IS_EXPR>(node));
break;
}
case ASTKind::AS_EXPR: {
chkRet = ChkAsExpr(*curCtx, *realTarget, *StaticAs<ASTKind::AS_EXPR>(node));
break;
}
case ASTKind::OPTIONAL_CHAIN_EXPR: {
chkRet = ChkOptionalChainExpr(*curCtx, *realTarget, *StaticAs<ASTKind::OPTIONAL_CHAIN_EXPR>(node));
break;
}
case ASTKind::MACRO_EXPAND_EXPR:
case ASTKind::MACRO_EXPAND_PARAM:
case ASTKind::MACRO_EXPAND_DECL: {
CheckMacroCall(*curCtx, *node);
break;
}
default: {
Synthesize({ctx, SynPos::NONE}, node);
ReplaceIdealTy(*node);
chkRet = typeManager.IsSubtype(node->GetTy(), realTarget);
break;
}
}
}
CJC_ASSERT(!Ty::IsTyCorrect(node->GetTy()) || As<ASTKind::EXPR>(node) == nullptr ||
StaticAs<ASTKind::EXPR>(node)->desugarExpr == nullptr ||
node->GetTy() == StaticAs<ASTKind::EXPR>(node)->desugarExpr->GetTy());
ctx.targetTypeMap[node] = nullptr;
node->SetTy(typeManager.TryGreedySubst(node->GetTy()));
return chkRet;
}
void TypeChecker::TypeCheckerImpl::CheckConstructor(ASTContext& ctx, const Decl& decl, FuncDecl& fd)
{
if (decl.astKind != ASTKind::CLASS_DECL && decl.astKind != ASTKind::STRUCT_DECL) {
return;
}
if (!fd.funcBody) {
return;
}
bool hasBrokenBody = fd.funcBody->generic && !fd.TestAttr(Attribute::STATIC) && !HasJavaAttr(decl);
if (hasBrokenBody) {
diag.Diagnose(fd, DiagKind::sema_forbid_generic_constructor, fd.identifier.Val());
}
if (fd.funcBody->paramLists.size() > 1) {
diag.Diagnose(*fd.funcBody->paramLists[0], DiagKind::sema_cannot_currying, "constructor");
hasBrokenBody = true;
}
if (hasBrokenBody) {
fd.funcBody->EnableAttr(Attribute::IS_BROKEN);
fd.EnableAttr(Attribute::HAS_BROKEN);
}
if (hasBrokenBody || !fd.funcBody->body || fd.funcBody->body->body.empty()) {
return;
}
Ptr<Node> firstExprOrDecl = fd.funcBody->body->body.front().get();
if (firstExprOrDecl == nullptr) {
return;
}
bool needEraseSuper{false};
if (auto ce = AST::As<ASTKind::CALL_EXPR>(firstExprOrDecl); ce) {
CheckConstructorSuper(*ce, decl, fd, needEraseSuper);
}
CheckCallsInConstructor(ctx, decl, fd, *firstExprOrDecl, needEraseSuper);
}
void TypeChecker::TypeCheckerImpl::CheckCallsInConstructor(
ASTContext& ctx, const Decl& decl, FuncDecl& fd, Node& firstExprOrDecl, bool needEraseSuper)
{
CJC_ASSERT(!fd.funcBody->body->body.empty());
auto firstExprOrDeclPtr = &firstExprOrDecl;
std::vector<Ptr<CallExpr>> calls;
Walker(fd.funcBody.get(), [&calls](auto node) {
if (auto ce = DynamicCast<CallExpr*>(node); ce) {
calls.emplace_back(ce);
}
return VisitAction::WALK_CHILDREN;
}).Walk();
for (auto ce : calls) {
if (ce->baseFunc == nullptr || ce->baseFunc->astKind != ASTKind::REF_EXPR) {
continue;
}
auto re = RawStaticCast<RefExpr*>(ce->baseFunc.get());
if (!re->isThis && !re->isSuper) {
continue;
}
if (re->isThis && fd.TestAttr(Attribute::PRIMARY_CONSTRUCTOR)) {
diag.Diagnose(*ce, DiagKind::sema_illegal_place_of_calling_this_primary_constructor);
} else if (ce != firstExprOrDeclPtr) {
Ptr<TrailingClosureExpr> tce = nullptr;
if (firstExprOrDeclPtr && firstExprOrDeclPtr->astKind == ASTKind::TRAIL_CLOSURE_EXPR) {
tce = RawStaticCast<TrailingClosureExpr*>(firstExprOrDeclPtr);
}
auto notIllegalPlace = !tce || (ce->TestAttr(Attribute::COMPILER_ADD) && ce != tce->desugarExpr.get());
if (notIllegalPlace) {
diag.Diagnose(*ce, DiagKind::sema_illegal_place_of_calling_this_or_super, re->ref.identifier.Val(),
decl.astKind == ASTKind::CLASS_DECL ? "class" : "struct", decl.identifier.Val());
}
}
auto needReset = needEraseSuper && re->isSuper && ce->TestAttr(Attribute::COMPILER_ADD) && ce->args.empty() &&
decl.astKind == ASTKind::CLASS_DECL && ce == fd.funcBody->body->body.front().get();
if (needReset) {
needEraseSuper = false;
auto it = fd.funcBody->body->body.begin();
ctx.DeleteInvertedIndexes(it->get());
it->reset();
fd.funcBody->body->body.erase(it);
firstExprOrDeclPtr = nullptr;
}
if (decl.astKind == ASTKind::STRUCT_DECL) {
fd.constructorCall = ConstructorCall::OTHER_INIT;
}
}
}
Ptr<Ty> TypeChecker::TypeCheckerImpl::SynthesizeWithCache(const CheckerContext& ctx, Ptr<Node> node)
{
CJC_NULLPTR_CHECK(node);
if (!typeManager.GetUnsolvedTyVars().empty()) {
return Synthesize(ctx, node);
}
CacheKey key = GetCacheKeyForSyn(ctx.Ctx(), node);
if (ctx.Ctx().typeCheckCache[node].synCache.count(key) != 0) {
auto& cache = ctx.Ctx().typeCheckCache[node].synCache[key];
RestoreCached(ctx.Ctx(), node, cache);
return cache.result;
} else {
return SynthesizeAndCache(ctx, node, key);
}
}
bool TypeChecker::TypeCheckerImpl::CheckWithCache(ASTContext& ctx, Ptr<Ty> target, Ptr<Node> node)
{
CJC_NULLPTR_CHECK(node);
if (!typeManager.GetUnsolvedTyVars().empty()) {
return Check(ctx, target, node);
}
CacheKey key = GetCacheKeyForChk(ctx, node, target);
if (ctx.typeCheckCache[node].chkCache.count(key) != 0) {
auto& cache = ctx.typeCheckCache[node].chkCache[key];
RestoreCached(ctx, node, cache);
return cache.successful;
} else {
return CheckAndCache(ctx, target, node, key);
}
}
Ptr<Ty> TypeChecker::TypeCheckerImpl::SynthesizeWithNegCache(const CheckerContext& ctx, Ptr<Node> node)
{
CJC_NULLPTR_CHECK(node);
if (!typeManager.GetUnsolvedTyVars().empty()) {
return Synthesize(ctx, node);
}
CacheKey key = GetCacheKeyForSyn(ctx.Ctx(), node);
if (ctx.Ctx().typeCheckCache[node].synCache.count(key) != 0 && !ctx.Ctx().typeCheckCache[node].synCache[key].successful) {
auto& cache = ctx.Ctx().typeCheckCache[node].synCache[key];
RestoreCached(ctx.Ctx(), node, cache);
return cache.result;
} else {
return SynthesizeAndCache(ctx, node, key);
}
}
bool TypeChecker::TypeCheckerImpl::CheckWithNegCache(ASTContext& ctx, Ptr<Ty> target, Ptr<Node> node)
{
CJC_NULLPTR_CHECK(node);
if (!typeManager.GetUnsolvedTyVars().empty()) {
return Check(ctx, target, node);
}
CacheKey key = GetCacheKeyForChk(ctx, node, target);
if (ctx.typeCheckCache[node].chkCache.count(key) != 0 && !ctx.typeCheckCache[node].chkCache[key].successful) {
auto& cache = ctx.typeCheckCache[node].chkCache[key];
RestoreCached(ctx, node, cache);
return false;
} else {
return CheckAndCache(ctx, target, node, key);
}
}
bool TypeChecker::TypeCheckerImpl::CheckWithEffectiveCache(
ASTContext& ctx, Ptr<Ty> target, Ptr<Node> node, bool recoverDiag)
{
if (!typeManager.GetUnsolvedTyVars().empty() || !node) {
return Check(ctx, target, node);
}
CacheKey key = GetCacheKeyForChk(ctx, node, target);
if (!Ty::IsInitialTy(node->GetTy())) {
if (ctx.typeCheckCache[node].lastKey && ctx.typeCheckCache[node].lastKey.value() == key) {
if (ctx.typeCheckCache[node].chkCache.count(key) != 0) {
auto& cache = ctx.typeCheckCache[node].chkCache[key];
RestoreCached(ctx, node, cache, recoverDiag);
return cache.successful;
} else {
return CheckAndCache(ctx, target, node, key);
}
}
}
node->Clear();
return CheckAndCache(ctx, target, node, key);
}
Ptr<Ty> TypeChecker::TypeCheckerImpl::SynthesizeWithEffectiveCache(const CheckerContext& ctx, Ptr<Node> node, bool recoverDiag)
{
if (!typeManager.GetUnsolvedTyVars().empty() || !node) {
return Synthesize(ctx, node);
}
CacheKey key = GetCacheKeyForSyn(ctx.Ctx(), node);
if (!Ty::IsInitialTy(node->GetTy())) {
if (ctx.Ctx().typeCheckCache[node].lastKey && ctx.Ctx().typeCheckCache[node].lastKey.value() == key) {
if (ctx.Ctx().typeCheckCache[node].synCache.count(key) != 0) {
auto& cache = ctx.Ctx().typeCheckCache[node].synCache[key];
RestoreCached(ctx.Ctx(), node, cache, recoverDiag);
return cache.result;
} else {
return SynthesizeAndCache(ctx, node, key);
}
}
}
return SynthesizeAndCache(ctx, node, key);
}
Ptr<AST::Ty> TypeChecker::TypeCheckerImpl::SynthesizeAndCache(
const CheckerContext& ctx, Ptr<AST::Node> node, const CacheKey& key)
{
DiagnosticCache dc;
dc.ToExclude(ctx.Ctx().diag);
auto ret = Synthesize(ctx, node);
dc.BackUp(ctx.Ctx().diag);
ctx.Ctx().typeCheckCache[node].synCache[key] = {.successful = ret && Ty::IsTyCorrect(ret) && dc.NoError(),
.result = ret,
.diags = std::move(dc),
.targets = CollectTargets(*node)};
return ret;
}
bool TypeChecker::TypeCheckerImpl::CheckAndCache(ASTContext& ctx, Ptr<Ty> target, Ptr<Node> node, const CacheKey& key)
{
DiagnosticCache dc;
dc.ToExclude(ctx.diag);
bool ret = Check(ctx, target, node);
dc.BackUp(ctx.diag);
ctx.typeCheckCache[node].chkCache[key] = {
.successful = ret, .result = node->GetTy(), .diags = std::move(dc), .targets = CollectTargets(*node)};
return ret;
}
namespace {
bool HasNonParamCtorForClass(const ClassDecl& classDecl)
{
for (auto& decl : classDecl.body->decls) {
auto fd = DynamicCast<FuncDecl*>(decl.get());
if (!fd || !IsInstanceConstructor(*fd)) {
continue;
}
if (fd->funcBody->paramLists.empty()) {
return false;
}
auto& params = fd->funcBody->paramLists[0]->params;
if (params.empty()) {
if (fd->TestAttr(Attribute::PRIVATE)) {
return false;
}
return true;
}
bool allHasDefaultValue{true};
for (auto& param : params) {
if (!(param->assignment)) {
allHasDefaultValue = false;
break;
}
}
if (allHasDefaultValue) {
if (fd->TestAttr(Attribute::PRIVATE)) {
continue;
}
return true;
}
}
return false;
}
}
void TypeChecker::TypeCheckerImpl::CheckConstructorSuper(
const CallExpr& ce, const Decl& decl, FuncDecl& fd, bool& needEraseSuper)
{
if (!ce.baseFunc || ce.baseFunc->astKind != ASTKind::REF_EXPR) {
return;
}
auto re = RawStaticCast<RefExpr*>(ce.baseFunc.get());
if (!re->isSuper || !ce.TestAttr(Attribute::COMPILER_ADD) || !ce.args.empty()) {
return;
}
if (decl.astKind != ASTKind::CLASS_DECL) {
return;
}
auto cd = RawStaticCast<const ClassDecl*>(&decl);
Ptr<ClassDecl> superCD = cd->GetSuperClassDecl();
needEraseSuper = superCD && !HasNonParamCtorForClass(*superCD);
if (!needEraseSuper) {
return;
}
auto& fdName = fd.TestAttr(Attribute::PRIMARY_CONSTRUCTOR) ? fd.identifierForLsp : fd.identifier.Val();
auto range =
fd.TestAttr(Attribute::IMPLICIT_ADD) ? MakeRange(decl.identifier) : MakeRange(fd.identifier.Begin(), fdName);
(void)diag.DiagnoseRefactor(DiagKindRefactor::sema_no_non_param_constructor_in_super_class, fd, range);
fd.constructorCall = ConstructorCall::NONE;
}
void TypeChecker::TypeCheckerImpl::CheckFinalizer(const FuncDecl& fd)
{
if (!fd.funcBody) {
return;
}
bool invalidGeneric =
fd.funcBody->generic && !fd.TestAttr(Attribute::STATIC) && fd.outerDecl && !HasJavaAttr(*fd.outerDecl);
if (invalidGeneric) {
diag.Diagnose(fd, DiagKind::sema_forbid_generic_finalizer, fd.identifier.Val());
}
if (!fd.funcBody->paramLists.empty()) {
if (fd.funcBody->paramLists.size() > 1) {
diag.Diagnose(*fd.funcBody->paramLists[0], DiagKind::sema_cannot_currying, "finalizer");
}
if (!fd.funcBody->paramLists[0]->params.empty()) {
diag.Diagnose(fd, DiagKind::sema_cannot_have_parameter, "finalizer");
}
}
}
void TypeChecker::TypeCheckerImpl::CheckPrimaryCtorForClassOrStruct(InheritableDecl& id)
{
if (id.TestAttr(Attribute::IMPORTED)) {
return;
}
bool primaryCtor = false;
bool hasDesugared = false;
Ptr<PrimaryCtorDecl> target = nullptr;
for (auto& decl : id.GetMemberDecls()) {
if (auto fd = DynamicCast<PrimaryCtorDecl*>(decl.get()); fd) {
if (primaryCtor) {
auto typeName = id.astKind == ASTKind::CLASS_DECL ? "class" : "struct";
diag.Diagnose(*fd, DiagKind::sema_multiple_primary_constructors, typeName, id.identifier.Val());
} else {
primaryCtor = true;
target = fd;
}
if (fd->funcBody && fd->funcBody->generic) {
diag.Diagnose(*decl, DiagKind::sema_forbid_generic_constructor, decl->identifier.Val());
}
if (fd->funcBody && fd->funcBody->paramLists.size() > 1) {
diag.Diagnose(*fd->funcBody->paramLists[0], DiagKind::sema_cannot_currying, "constructor");
}
}
if (decl->TestAttr(Attribute::PRIMARY_CONSTRUCTOR) && decl->astKind == ASTKind::FUNC_DECL) {
hasDesugared = true;
}
}
if (target != nullptr && !hasDesugared) {
DesugarPrimaryCtor(id, *target);
}
}
void TypeChecker::TypeCheckerImpl::TypeCheckCompositeBody(
ASTContext& ctx, const Decl& structDecl, const std::vector<OwnedPtr<Decl>>& body)
{
for (auto& decl : body) {
CJC_ASSERT(decl);
if (auto fd = DynamicCast<FuncDecl*>(decl.get()); fd) {
if (fd->TestAttr(Attribute::CONSTRUCTOR)) {
CheckConstructor(ctx, structDecl, *fd);
}
if (fd->IsFinalizer()) {
CheckFinalizer(*fd);
}
}
Synthesize({ctx, SynPos::NONE}, decl.get());
CheckCTypeMember(*decl);
}
}
void TypeChecker::TypeCheckerImpl::CheckJavaInteropLibImport(Decl& decl)
{
if (!Interop::Java::InteropLibBridge::IsInteropLibAccessible(importManager)) {
diag.DiagnoseRefactor(DiagKindRefactor::sema_java_mirror_interoplib_must_be_imported, decl);
decl.EnableAttr(Attribute::IS_BROKEN);
}
}
void TypeChecker::TypeCheckerImpl::CheckObjCInteropLibImport(Decl& decl)
{
if (!Interop::ObjC::InteropLibBridge::IsInteropLibAccessible(importManager)) {
diag.DiagnoseRefactor(DiagKindRefactor::sema_objc_mirror_interoplib_must_be_imported, decl);
decl.EnableAttr(Attribute::IS_BROKEN);
}
}
bool TypeChecker::TypeCheckerImpl::IsCapturedInCFuncLambda(const ASTContext& ctx, const AST::RefExpr& re) const
{
if (re.ref.target == nullptr || re.ref.target->fullPackageName != ctx.fullPackageName) {
return false;
}
auto fb = GetCurFuncBody(ctx, re.scopeName);
auto targetFb = GetCurFuncBody(ctx, re.ref.target->scopeName);
if (fb == nullptr || targetFb == nullptr || fb == targetFb || re.ref.target->TestAttr(Attribute::GLOBAL)) {
return false;
}
if (auto fty = DynamicCast<AST::FuncTy*>(fb->GetTy()); fty && fty->isC) {
return true;
}
if (!Is<AST::VarDecl*>(re.ref.target) && !Is<AST::FuncDecl*>(re.ref.target)) {
return false;
}
auto outerFb = GetCurFuncBody(ctx, ScopeManagerApi::GetParentScopeName(fb->scopeName));
while (outerFb != nullptr) {
if (outerFb == targetFb) {
return false;
}
if (auto fty = DynamicCast<AST::FuncTy*>(outerFb->GetTy()); fty && fty->isC) {
return true;
}
outerFb = GetCurFuncBody(ctx, ScopeManagerApi::GetParentScopeName(outerFb->scopeName));
}
return false;
}
void TypeChecker::TypeCheckerImpl::CheckLegalUseOfClosure(Expr& e, DiagKind kind, LambdaSource ls) const
{
if (e.TestAttr(Attribute::IS_BROKEN)) {
return;
}
if (auto target = DynamicCast<LambdaExpr*>(&e); target && target->funcBody) {
if (target->funcBody->captureKind == CaptureKind::CAPTURE_VAR) {
DiagUseClosureCaptureVarAlone(diag, e, ls);
} else if (target->funcBody->captureKind == CaptureKind::TRANSITIVE_CAPTURE) {
diag.Diagnose(e, kind, "lambda", "transitively", "lambda");
}
}
if (auto ref = DynamicCast<RefExpr*>(&e); ref) {
if (auto target = DynamicCast<FuncDecl*>(ref->ref.target); target) {
if (target->funcBody->captureKind == CaptureKind::CAPTURE_VAR) {
DiagUseClosureCaptureVarAlone(diag, e, ls);
} else if (target->funcBody->captureKind == CaptureKind::TRANSITIVE_CAPTURE) {
diag.Diagnose(e, kind, target->identifier.Val(), "transitively", target->identifier.Val());
}
}
}
}
void TypeChecker::TypeCheckerImpl::CheckLegalUseOfClosure(const ASTContext& ctx, Node& node, LambdaSource ls) const
{
if (auto vd = DynamicCast<VarDecl*>(&node); vd) {
if (vd->initializer) {
CheckLegalUseOfClosure(*vd->initializer, DiagKind::sema_func_capture_var_cannot_assign, ls);
}
} else if (auto re = DynamicCast<ReturnExpr*>(&node); re) {
if (re->expr) {
CheckLegalUseOfClosure(*re->expr, DiagKind::sema_func_capture_var_cannot_return, ls);
}
} else if (auto ce = DynamicCast<CallExpr*>(&node); ce) {
if (auto baseRe = DynamicCast<RefExpr*>(ce->baseFunc.get()); baseRe && IsCapturedInCFuncLambda(ctx, *baseRe)) {
diag.Diagnose(*baseRe, DiagKind::sema_cfunc_cannot_capture_var, baseRe->ref.identifier.Val());
}
for (auto& arg : ce->args) {
if (arg && arg->expr) {
CheckLegalUseOfClosure(*arg->expr, DiagKind::sema_func_capture_var_cannot_param, ls);
}
}
} else if (auto ref = DynamicCast<RefExpr*>(&node); ref && !ref->isBaseFunc) {
CheckLegalUseOfClosure(*ref, DiagKind::sema_func_capture_var_cannot_expr, ls);
if (auto refVd = DynamicCast<VarDecl*>(ref->ref.target); refVd) {
if (IsCapturedCStructOfClosure(*refVd)) {
diag.Diagnose(*ref, DiagKind::sema_func_capture_var_not_ctype);
}
}
if (IsCapturedInCFuncLambda(ctx, *ref)) {
diag.Diagnose(*ref, DiagKind::sema_cfunc_cannot_capture_var, ref->ref.identifier.Val());
}
} else if (auto lambda = DynamicCast<LambdaExpr*>(&node); lambda && !lambda->isBaseFunc) {
CheckLegalUseOfClosure(*lambda, DiagKind::sema_func_capture_var_cannot_expr, ls);
}
}
bool TypeChecker::TypeCheckerImpl::IsCapturedCStructOfClosure(const VarDecl& decl) const
{
return decl.TestAttr(Attribute::IS_CAPTURE) && decl.GetTy() && Ty::IsCTypeConstraint(*decl.GetTy());
}
void TypeChecker::TypeCheckerImpl::CheckCHIRClassDependencies()
{
auto objectDecl = importManager.GetImportedDecl(CORE_PACKAGE_NAME, "Object");
if (objectDecl == nullptr) {
diag.DiagnoseRefactor(DiagKindRefactor::sema_core_object_not_found_when_no_prelude, DEFAULT_POSITION);
}
}
namespace {
void MarkOverflow(Node& node)
{
Walker checkOverflowWalker(&node, nullptr, [](Ptr<Node> node) -> VisitAction {
switch (node->astKind) {
case ASTKind::LIT_CONST_EXPR: {
auto lce = StaticAs<ASTKind::LIT_CONST_EXPR>(node);
if (lce->GetTy() && lce->GetTy()->IsInteger()) {
auto primitiveTy = RawStaticCast<PrimitiveTy*>(lce->GetTy());
lce->constNumValue.asInt.SetOutOfRange(primitiveTy);
}
return VisitAction::WALK_CHILDREN;
}
default: {
return VisitAction::WALK_CHILDREN;
}
}
});
checkOverflowWalker.Walk();
}
void AddBuiltInArrayDecl(Package& pkg)
{
auto bid = MakeOwned<BuiltInDecl>(BuiltInType::ARRAY);
bid->identifier = RAW_ARRAY_NAME;
bid->generic = MakeOwned<Generic>();
auto gpd = MakeOwnedNode<GenericParamDecl>();
gpd->identifier = "T";
gpd->outerDecl = bid.get();
bid->generic->typeParameters.emplace_back(std::move(gpd));
bid->EnableAttr(Attribute::GLOBAL, Attribute::GENERIC);
CopyFileID(bid.get(), pkg.files[0].get());
pkg.files[0]->decls.emplace_back(std::move(bid));
}
void AddBuiltInPointerDecl(Package& pkg)
{
auto bid = MakeOwned<BuiltInDecl>(BuiltInType::POINTER);
bid->identifier = CPOINTER_NAME;
bid->generic = MakeOwned<Generic>();
auto gpd = MakeOwnedNode<GenericParamDecl>();
gpd->identifier = "T";
gpd->outerDecl = bid.get();
bid->generic->typeParameters.emplace_back(std::move(gpd));
bid->generic->genericConstraints.emplace_back(CreateConstraintForFFI(CTYPE_NAME));
bid->EnableAttr(Attribute::PUBLIC, Attribute::GLOBAL, Attribute::GENERIC);
CopyFileID(bid.get(), pkg.files[0].get());
pkg.files[0]->decls.emplace_back(std::move(bid));
}
void AddBuiltInCStringDecl(Package& pkg)
{
auto bid = MakeOwned<BuiltInDecl>(BuiltInType::CSTRING);
bid->identifier = CSTRING_NAME;
bid->generic = nullptr;
bid->EnableAttr(Attribute::PUBLIC, Attribute::GLOBAL);
CopyFileID(bid.get(), pkg.files[0].get());
pkg.files[0]->decls.emplace_back(std::move(bid));
}
void AddBuiltInVArrayDecl(Package& pkg)
{
auto bid = MakeOwned<BuiltInDecl>(BuiltInType::VARRAY);
bid->identifier = VARRAY_NAME;
bid->generic = MakeOwned<Generic>();
auto gpd = MakeOwnedNode<GenericParamDecl>();
gpd->identifier = "T";
gpd->outerDecl = bid.get();
bid->generic->typeParameters.emplace_back(std::move(gpd));
bid->EnableAttr(Attribute::GLOBAL);
bid->EnableAttr(Attribute::PUBLIC);
bid->EnableAttr(Attribute::GENERIC);
CopyFileID(bid.get(), pkg.files[0].get());
pkg.files[0]->decls.emplace_back(std::move(bid));
}
void AddBuiltinCFuncDecl(Package& pkg)
{
auto bid = MakeOwned<BuiltInDecl>(BuiltInType::CFUNC);
bid->identifier = CFUNC_NAME;
bid->generic = MakeOwned<Generic>();
auto gpd = MakeOwnedNode<GenericParamDecl>();
gpd->identifier = "T";
gpd->outerDecl = &*bid;
bid->generic->typeParameters.push_back(std::move(gpd));
bid->EnableAttr(Attribute::GLOBAL);
bid->EnableAttr(Attribute::PUBLIC);
bid->EnableAttr(Attribute::GENERIC);
CopyFileID(&*bid, &*pkg.files[0]);
pkg.files[0]->decls.push_back(std::move(bid));
}
void AddAttrForDefaultFuncParam(Package& pkg)
{
Utils::ProfileRecorder recorder("Post TypeCheck", "AddAttrForDefaultFuncParam");
Walker(&pkg, [](auto node) {
if (node->astKind != ASTKind::FUNC_DECL) {
return VisitAction::WALK_CHILDREN;
}
auto fd = StaticAs<ASTKind::FUNC_DECL>(node);
if (!fd->funcBody || fd->funcBody->paramLists.empty()) {
return VisitAction::SKIP_CHILDREN;
}
for (auto& param : fd->funcBody->paramLists[0]->params) {
if (param->desugarDecl) {
if (fd->TestAttr(Attribute::GLOBAL)) {
param->desugarDecl->EnableAttr(Attribute::GLOBAL);
}
}
}
return VisitAction::WALK_CHILDREN;
}).Walk();
}
void MarkImplicitUsedFunctions(const Package& pkg)
{
Utils::ProfileRecorder recorder("Post TypeCheck", "MarkImplicitUsedFunctions");
static const std::unordered_map<std::string, std::unordered_set<std::string>> SPECIAL_EXPORTED_FUNCS{
{CORE_PACKAGE_NAME,
{"arrayInitByCollection", "arrayInitByFunction", "composition", "handleException",
"createOverflowExceptionMsg", "createArithmeticExceptionMsg", "getCommandLineArgs"}},
{AST_PACKAGE_NAME,
{MACRO_OBJECT_NAME, "refreshTokensPosition", "refreshPos", "unsafePointerCastFromUint8Array", "transformTokens"}}};
auto found = SPECIAL_EXPORTED_FUNCS.find(pkg.fullPackageName);
if (found == SPECIAL_EXPORTED_FUNCS.end()) {
return;
}
IterateToplevelDecls(pkg, [&found](auto& decl) {
if (found->second.count(decl->identifier) != 0) {
decl->EnableAttr(Attribute::IMPLICIT_USED);
}
});
}
}
Ptr<Decl> TypeChecker::TypeCheckerImpl::GetImplementedTargetIfExist(
const ASTContext& ctx, const Ty& interfaceTy, Decl& target, const MultiTypeSubst& typeMapping)
{
auto targetInstanceTy = typeManager.GetBestInstantiatedTy(target.GetTy(), typeMapping);
auto id = Ty::GetDeclPtrOfTy<InheritableDecl>(&interfaceTy);
auto members = FieldLookup(ctx, id, target.identifier);
for (auto& member : members) {
bool isSameSignature = false;
if (member->IsFunc() && target.IsFunc()) {
if (!IsOverrideOrShadow(typeManager, *RawStaticCast<FuncDecl*>(member), static_cast<FuncDecl&>(target))) {
continue;
}
auto memberFuncTy = DynamicCast<FuncTy*>(member->GetTy());
auto targetFuncTy = DynamicCast<FuncTy*>(targetInstanceTy);
if (!Ty::IsTyCorrect(memberFuncTy) || !Ty::IsTyCorrect(targetFuncTy)) {
continue;
}
auto mts = typeMapping;
auto memberFuncDecl = DynamicCast<FuncDecl*>(member);
auto targetFuncDecl = DynamicCast<const FuncDecl*>(&target);
CJC_NULLPTR_CHECK(memberFuncDecl);
CJC_NULLPTR_CHECK(targetFuncDecl);
auto ts = GenerateTypeMappingBetweenFuncs(typeManager, *memberFuncDecl, *targetFuncDecl);
mts.merge(ts);
auto memberParamTys = memberFuncTy->paramTys;
auto targetParamTys = targetFuncTy->paramTys;
for (auto& it : memberParamTys) {
it = typeManager.GetBestInstantiatedTy(it, mts);
}
for (auto& it : targetParamTys) {
it = typeManager.GetBestInstantiatedTy(it, mts);
}
isSameSignature = typeManager.IsFuncParameterTypesIdentical(memberParamTys, targetParamTys);
} else if (member->astKind == ASTKind::PROP_DECL && target.astKind == ASTKind::PROP_DECL) {
if (!IsOverrideOrShadow(typeManager, *RawStaticCast<PropDecl*>(member), static_cast<PropDecl&>(target))) {
continue;
}
isSameSignature = member->GetTy() == targetInstanceTy;
}
if (isSameSignature && !member->TestAttr(Attribute::ABSTRACT)) {
return member;
}
}
return nullptr;
}
std::pair<bool, Ptr<RefExpr>> TypeChecker::TypeCheckerImpl::CheckInvokeTargetHasImpl(const ASTContext& ctx,
Ty& interfaceTy, Decl& decl, MultiTypeSubst& typeMapping, std::unordered_set<Ptr<AST::Decl>>& traversedDecls)
{
std::pair<bool, Ptr<RefExpr>> ret{false, nullptr};
auto preVisit = [this, &ret, &ctx, &interfaceTy, &typeMapping, &traversedDecls](Ptr<Node> node) -> VisitAction {
if (node->astKind != ASTKind::REF_EXPR) {
return AST::VisitAction::WALK_CHILDREN;
}
const auto re = RawStaticCast<RefExpr*>(node);
auto target = re->GetTarget();
if (target && target->TestAttr(Attribute::STATIC) && target->outerDecl && !target->outerDecl->IsFunc()) {
if (traversedDecls.find(target) != traversedDecls.end()) {
return VisitAction::SKIP_CHILDREN;
}
traversedDecls.emplace(target);
if (re->matchedParentTy && target->outerDecl->GetTy()) {
typeMapping.merge(promotion.GetPromoteTypeMapping(*re->matchedParentTy, *target->outerDecl->GetTy()));
}
auto newTarget = GetImplementedTargetIfExist(ctx, interfaceTy, *target, typeMapping);
if (newTarget == nullptr) {
ret.first = true;
ret.second = re;
return VisitAction::STOP_NOW;
}
ret = CheckInvokeTargetHasImpl(ctx, interfaceTy, *newTarget, typeMapping, traversedDecls);
if (ret.first) {
return VisitAction::STOP_NOW;
}
}
return AST::VisitAction::WALK_CHILDREN;
};
Walker walker(&decl, preVisit);
walker.Walk();
return ret;
}
void TypeChecker::TypeCheckForPackages(const std::vector<Ptr<Package>>& pkgs) const
{
impl->TypeCheckForPackages(pkgs);
}
std::vector<Ptr<ASTContext>> TypeChecker::TypeCheckerImpl::PreTypeCheck(const std::vector<Ptr<AST::Package>>& pkgs)
{
Utils::ProfileRecorder recorder("Semantic", "Pre TypeCheck");
std::vector<Ptr<ASTContext>> contexts;
std::for_each(pkgs.begin(), pkgs.end(), [this, &contexts](Ptr<Package> pkg) {
CJC_NULLPTR_CHECK(pkg);
if (auto ctx = ci->GetASTContextByPackage(pkg)) {
contexts.emplace_back(ctx);
}
});
Utils::ProfileRecorder::Start("Pre TypeCheck", "PrepareTypeCheck");
for (auto& ctx : contexts) {
PrepareTypeCheck(*ctx, *ctx->curPackage);
}
Utils::ProfileRecorder::Stop("Pre TypeCheck", "PrepareTypeCheck");
PreCheck(contexts);
Utils::ProfileRecorder::Start("Pre TypeCheck", "CollectDeclsWithMember");
for (auto pkg : pkgs) {
if (auto ctx = ci->GetASTContextByPackage(pkg)) {
CollectDeclsWithMember(pkg, *ctx);
}
}
Utils::ProfileRecorder::Stop("Pre TypeCheck", "CollectDeclsWithMember");
Utils::ProfileRecorder::Start("Pre TypeCheck", "MatchSpecificWithCommon");
for (auto pkg : pkgs) {
mpImpl->MatchSpecificWithCommon(*pkg);
mpImpl->CheckNotAllowedAnnotations(*pkg);
}
Utils::ProfileRecorder::Stop("Pre TypeCheck", "MatchSpecificWithCommon");
return contexts;
}
void TypeChecker::TypeCheckerImpl::PostTypeCheck(std::vector<Ptr<ASTContext>>& contexts)
{
Utils::ProfileRecorder recorder("Semantic", "Post TypeCheck");
for (auto& ctx : contexts) {
CheckOverflow(*ctx->curPackage);
CheckUnusedImportSpec(*ctx->curPackage);
CheckInstDupSuperInterfacesEntry(*ctx->curPackage);
CheckLegalityOfUsage(*ctx, *ctx->curPackage);
Utils::ProfileRecorder::Start("Post TypeCheck", "CheckCJMPRules");
mpImpl->CheckReturnAndVariableTypes(*ctx->curPackage);
mpImpl->ValidateMatchedAnnotationsAndModifiers(*ctx->curPackage);
Utils::ProfileRecorder::Stop("Post TypeCheck", "CheckCJMPRules");
AddAttrForDefaultFuncParam(*ctx->curPackage);
DesugarForPropDecl(*ctx->curPackage);
CheckConstEvaluation(*ctx->curPackage);
MarkImplicitUsedFunctions(*ctx->curPackage);
IterateToplevelDecls(*ctx->curPackage, [this](auto& decl) {
if (auto md = DynamicCast<MainDecl*>(decl.get()); md && md->desugarDecl) {
(void)mainFunctionMap[md->curFile].emplace(md->desugarDecl.get());
}
});
Utils::ProfileRecorder::Start("Post TypeCheck", "PluginCheck");
PluginCheck::PluginCustomAnnoChecker(*ci, diag, importManager).Check(*ctx->curPackage);
Utils::ProfileRecorder::Stop("Post TypeCheck", "PluginCheck");
}
CheckWhetherHasProgramEntry();
}
void TypeChecker::TypeCheckerImpl::PrepareTypeCheck(ASTContext& ctx, Package& pkg)
{
ctx.searcher->InvalidateCache();
CheckPrimaryCtorBeforeMerge(pkg);
mpImpl->PrepareTypeCheck4CJMP(pkg);
#ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND
Utils::ProfileRecorder::Start("PrepareTypeCheck", "InteropPrepare");
Cangjie::Native::FFI::Java::PrepareTypeCheck(pkg, importManager, typeManager);
Interop::ObjC::PrepareTypeCheck(pkg);
Utils::ProfileRecorder::Stop("PrepareTypeCheck", "InteropPrepare");
#endif
Utils::ProfileRecorder::Start("PrepareTypeCheck", "AddDefaultFuncAndBuiltinDecl");
AddDefaultFunction(pkg);
if (pkg.fullPackageName == CORE_PACKAGE_NAME && !pkg.files.empty() && !pkg.TestAttr(Attribute::IMPORTED)) {
AddBuiltInArrayDecl(pkg);
AddBuiltInVArrayDecl(pkg);
AddBuiltInPointerDecl(pkg);
AddBuiltinCFuncDecl(pkg);
AddBuiltInCStringDecl(pkg);
}
Utils::ProfileRecorder::Stop("PrepareTypeCheck", "AddDefaultFuncAndBuiltinDecl");
Utils::ProfileRecorder::Start("PrepareTypeCheck", "BuildSymbolTable");
Collector collector(scopeManager, ci->invocation.globalOptions.enableMacroInLSP);
collector.BuildSymbolTable(ctx, &pkg, ci->buildTrie);
Utils::ProfileRecorder::Stop("PrepareTypeCheck", "BuildSymbolTable");
Utils::ProfileRecorder::Start("PrepareTypeCheck", "MarkAndPrepare");
MarkOutermostBinaryExpressions(pkg);
AddCurFile(pkg);
MarkParamWithInitialValue(pkg);
Utils::ProfileRecorder::Stop("PrepareTypeCheck", "MarkAndPrepare");
SearchSymbol::WarmupCache(ctx);
}
void TypeChecker::TypeCheckerImpl::TypeCheckTopLevelDecl(ASTContext& ctx, Decl& decl)
{
TyVarScope ts(typeManager);
Synthesize({ctx, SynPos::NONE}, &decl);
MarkOverflow(decl);
}
void TypeChecker::TypeCheckerImpl::TypeCheckImportedGenericMember(ASTContext& ctx)
{
std::vector<Symbol*> syms = SearchSymbol::GetAllStructDecls(ctx);
for (auto sym : syms) {
CJC_ASSERT(sym && sym->node);
auto id = StaticCast<InheritableDecl*>(sym->node);
if (id->TestAttr(Attribute::GENERIC)) {
continue;
}
for (auto& member : id->GetMemberDecls()) {
if (member->TestAttr(Attribute::GENERIC)) {
Synthesize({ctx, SynPos::NONE}, member.get());
}
}
}
}
void TypeChecker::TypeCheckerImpl::TypeCheck(ASTContext& ctx, Package& pkg)
{
std::vector<Symbol*> syms = SearchSymbol::GetToplevelDecls(ctx);
for (auto sym : syms) {
CJC_ASSERT(sym && sym->node);
if (!Is<Decl*>(sym->node)) {
return;
}
auto decl = StaticAs<ASTKind::DECL>(sym->node);
TypeCheckTopLevelDecl(ctx, *decl);
}
for (auto& node : pkg.srcImportedNonGenericDecls) {
Synthesize({ctx, SynPos::NONE}, node);
}
if (pkg.TestAttr(Attribute::IMPORTED)) {
TypeCheckImportedGenericMember(ctx);
}
std::function<VisitAction(Ptr<Node>)> visitMacrocall = [this, &visitMacrocall, &ctx](
Ptr<Node> curNode) -> VisitAction {
if (curNode->astKind == ASTKind::FILE) {
auto file = StaticAs<ASTKind::FILE>(curNode);
for (auto& it : file->originalMacroCallNodes) {
Walker(it.get(), visitMacrocall).Walk();
}
}
if (curNode->IsMacroCallNode()) {
CheckMacroCall(ctx, *curNode);
}
return VisitAction::WALK_CHILDREN;
};
if (ci->invocation.globalOptions.enableMacroInLSP) {
for (auto& file : pkg.files) {
Walker(file.get(), visitMacrocall).Walk();
}
}
}
void TypeChecker::TypeCheckerImpl::CheckWhetherHasProgramEntry()
{
CJC_ASSERT(ci);
if (!ci->invocation.globalOptions.CompileExecutable() || ci->invocation.globalOptions.enableCompileTest ||
ci->invocation.frontendOptions.dumpAction == FrontendOptions::DumpAction::TYPE_CHECK ||
!mainFunctionMap.empty() || ci->invocation.globalOptions.compileCjd) {
return;
}
if (ci->srcPkgs.empty() || ci->srcPkgs[0] == nullptr || ci->srcPkgs[0]->files.empty()) {
return;
}
auto& file = ci->srcPkgs[0]->files[0];
if (file != nullptr) {
diag.Diagnose(*file, DiagKind::sema_missing_entry);
}
}
namespace {
bool HasCtorForTypeDecl(const InheritableDecl& id)
{
for (auto& decl : id.GetMemberDeclPtrs()) {
CJC_NULLPTR_CHECK(decl);
if (decl->TestAttr(Attribute::CONSTRUCTOR) && !decl->TestAttr(Attribute::STATIC)) {
return true;
}
}
return false;
}
}
VisitAction TypeChecker::TypeCheckerImpl::CheckDefaultParamFunc(StructDecl& sd) const
{
CJC_ASSERT(sd.body);
if (sd.body->TestAttr(Attribute::IS_BROKEN)) {
return VisitAction::SKIP_CHILDREN;
}
if (!HasCtorForTypeDecl(sd)) {
AddDefaultCtor(sd);
}
AddSetterGetterInProp(sd);
return VisitAction::WALK_CHILDREN;
}
VisitAction TypeChecker::TypeCheckerImpl::CheckDefaultParamFunc(ClassDecl& cd, const File& file) const
{
CJC_ASSERT(cd.body);
if (cd.body->TestAttr(Attribute::IS_BROKEN)) {
return VisitAction::SKIP_CHILDREN;
}
if (!HasCtorForTypeDecl(cd)) {
AddDefaultCtor(cd);
}
AddSetterGetterInProp(cd);
for (auto& decl : cd.body->decls) {
CJC_ASSERT(decl);
if (decl->astKind != ASTKind::FUNC_DECL) {
continue;
}
auto& fd = *RawStaticCast<FuncDecl*>(decl.get());
if (fd.funcBody == nullptr || fd.funcBody->body == nullptr || !IsInstanceConstructor(fd)) {
continue;
}
SetFuncDeclConstructorCall(fd);
if (fd.constructorCall == ConstructorCall::NONE) {
std::string_view fullPackageName;
if (file.curPackage) {
fullPackageName = file.curPackage->fullPackageName;
}
auto objPkgName = CORE_PACKAGE_NAME;
bool nonObjectClass = !(cd.identifier == OBJECT_NAME && fullPackageName == objPkgName);
if (nonObjectClass) {
AddDefaultSuperCall(*fd.funcBody);
fd.constructorCall = ConstructorCall::SUPER;
}
}
}
return VisitAction::WALK_CHILDREN;
}
void TypeChecker::TypeCheckerImpl::SetFuncDeclConstructorCall(FuncDecl& fd) const
{
CJC_ASSERT(fd.funcBody && fd.funcBody->body);
if (fd.funcBody->body->body.empty()) {
return;
}
Ptr<RefExpr> refExpr = nullptr;
if (auto ce = DynamicCast<CallExpr*>(fd.funcBody->body->body.begin()->get()); ce) {
if (auto re = DynamicCast<RefExpr*>(ce->baseFunc.get()); re) {
refExpr = re;
}
} else if (auto tce = DynamicCast<TrailingClosureExpr*>(fd.funcBody->body->body.begin()->get()); tce) {
if (auto callExpr = DynamicCast<CallExpr*>(tce->desugarExpr.get()); callExpr) {
if (auto re = DynamicCast<RefExpr*>(callExpr->baseFunc.get()); re) {
refExpr = re;
}
} else if (auto re = DynamicCast<RefExpr*>(tce->desugarExpr.get()); re) {
refExpr = re;
}
}
if (refExpr != nullptr) {
if (refExpr->isThis && !fd.TestAttr(Attribute::PRIMARY_CONSTRUCTOR)) {
fd.constructorCall = ConstructorCall::OTHER_INIT;
}
if (refExpr->isSuper) {
fd.constructorCall = ConstructorCall::SUPER;
}
}
}
VisitAction TypeChecker::TypeCheckerImpl::CheckDefaultParamFunc(const InterfaceDecl& ifd) const
{
CJC_ASSERT(ifd.body);
if (ifd.body->TestAttr(Attribute::IS_BROKEN)) {
return VisitAction::SKIP_CHILDREN;
}
AddSetterGetterInProp(ifd);
return VisitAction::WALK_CHILDREN;
}
VisitAction TypeChecker::TypeCheckerImpl::CheckDefaultParamFunc(const EnumDecl& ed) const
{
AddSetterGetterInProp(ed);
return VisitAction::WALK_CHILDREN;
}
void TypeChecker::TypeCheckerImpl::CheckDefaultParamFuncsEntry(File& file)
{
auto visitFunc = [&file, this](Ptr<Node> node) -> VisitAction {
switch (node->astKind) {
case ASTKind::FUNC_DECL: {
auto fd = StaticAs<ASTKind::FUNC_DECL>(node);
GetSingleParamFunc(*fd);
if (fd->IsFinalizer()) {
AddUnitType(*fd);
}
return VisitAction::WALK_CHILDREN;
}
case ASTKind::PROP_DECL: {
auto pd = StaticAs<ASTKind::PROP_DECL>(node);
AddReturnTypeForPropMemDecl(*pd);
return VisitAction::WALK_CHILDREN;
}
case ASTKind::STRUCT_DECL: {
auto sd = StaticAs<ASTKind::STRUCT_DECL>(node);
return CheckDefaultParamFunc(*sd);
}
case ASTKind::CLASS_DECL: {
auto cd = StaticAs<ASTKind::CLASS_DECL>(node);
return CheckDefaultParamFunc(*cd, file);
}
case ASTKind::INTERFACE_DECL: {
auto ifd = StaticAs<ASTKind::INTERFACE_DECL>(node);
return CheckDefaultParamFunc(*ifd);
}
case ASTKind::ENUM_DECL: {
auto ed = StaticAs<ASTKind::ENUM_DECL>(node);
return CheckDefaultParamFunc(*ed);
}
case ASTKind::EXTEND_DECL: {
auto ed = StaticAs<ASTKind::EXTEND_DECL>(node);
AddSetterGetterInProp(*ed);
return VisitAction::WALK_CHILDREN;
}
case ASTKind::FUNC_PARAM:
return VisitAction::SKIP_CHILDREN;
default:
return VisitAction::WALK_CHILDREN;
}
};
for (auto& decl : file.decls) {
Walker walker(decl.get(), visitFunc);
walker.Walk();
}
for (auto& decl : file.exportedInternalDecls) {
Walker(decl.get(), visitFunc).Walk();
}
if (ci->invocation.globalOptions.enableMacroInLSP) {
for (auto& node : file.originalMacroCallNodes) {
Walker(node.get(), visitFunc).Walk();
}
}
}
namespace {
#ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND
void CollectGenericParam(const FuncDecl& funcDecl, Ptr<FuncDecl> desugared)
{
if (!funcDecl.funcBody || !funcDecl.funcBody->generic || funcDecl.funcBody->generic->typeParameters.empty()) {
return;
}
OwnedPtr<Generic> generic = MakeOwnedNode<Generic>();
int typeParamIdx = 0;
std::map<std::string, std::string> orig2New;
for (auto& param : std::as_const(funcDecl.funcBody->generic->typeParameters)) {
OwnedPtr<GenericParamDecl> tp = MakeOwnedNode<GenericParamDecl>();
tp->identifier = param->identifier + "$" + std::to_string(typeParamIdx++);
tp->outerDecl = desugared;
orig2New.emplace(param->identifier, tp->identifier);
generic->typeParameters.emplace_back(std::move(tp));
}
for (auto& constraint : std::as_const(funcDecl.funcBody->generic->genericConstraints)) {
auto gc = MakeOwnedNode<GenericConstraint>();
CopyBasicInfo(constraint.get(), gc.get());
auto& constraintType = constraint->type;
auto rt = MakeOwnedNode<RefType>();
rt->ref.identifier = orig2New[constraintType->ref.identifier];
gc->type = std::move(rt);
for (auto& upperBound : std::as_const(constraint->upperBounds)) {
auto ub = ASTCloner::Clone(upperBound.get());
gc->upperBounds.emplace_back(std::move(ub));
}
generic->genericConstraints.emplace_back(std::move(gc));
}
desugared->funcBody->generic = std::move(generic);
auto replacesTypeParams = [&orig2New](Ptr<Node> node) {
if (auto rt = DynamicCast<RefType>(node)) {
auto found = orig2New.find(rt->ref.identifier);
if (found != orig2New.end()) {
rt->ref.identifier = found->second;
}
} else if (auto re = DynamicCast<RefExpr>(node)) {
auto found = orig2New.find(re->ref.identifier);
if (found != orig2New.end()) {
re->ref.identifier = found->second;
}
}
return VisitAction::WALK_CHILDREN;
};
Walker(desugared, replacesTypeParams).Walk();
desugared->EnableAttr(Attribute::GENERIC);
}
#endif
* *************** before desugar ****************
* class A<T> {
* func foo<R>(a: T, b!: T = a) {}
* }
* *************** after desugar ****************
* class A<T> {
* // chir will generic apply for 'b' when call 'foo', like 'foo<Int64>(a, b.0<Int64>(a))'
* func foo<R>(a: T, b: T) {}
* func b.1<R$0>(a: T) {
* return a
* }
* }
*/
OwnedPtr<FuncDecl> MakeDefaultParamFunction(
FuncParam& fp, FuncDecl& funcDecl, const std::vector<Ptr<FuncParam>>& funcParams)
{
fp.EnableAttr(Attribute::HAS_INITIAL);
OwnedPtr<FuncDecl> ret = MakeOwnedNode<FuncDecl>();
CopyBasicInfo(fp.assignment.get(), ret.get());
ret->isFrozen = funcDecl.isFrozen || funcDecl.HasAnno(AnnotationKind::FROZEN);
CopyNodeScopeInfo(&funcDecl, ret.get());
ret->identifier = fp.identifier + "." + std::to_string(funcParams.size());
OwnedPtr<FuncBody> funcBody = MakeOwnedNode<FuncBody>();
funcBody->body = MakeOwnedNode<Block>();
funcBody->paramLists.push_back(MakeOwnedNode<FuncParamList>());
std::vector<OwnedPtr<FuncParam>> params;
for (auto& param : funcParams) {
if (param == nullptr) {
continue;
}
params.emplace_back(CreateFuncParamForOptional(*param));
}
funcBody->paramLists[0]->params = std::move(params);
ret->funcBody = std::move(funcBody);
auto returnExpr = MakeOwnedNode<ReturnExpr>();
returnExpr->begin = fp.assignment->begin;
returnExpr->end = fp.assignment->end;
returnExpr->expr = ASTCloner::Clone(fp.assignment.get());
returnExpr->refFuncBody = ret->funcBody.get();
ret->funcBody->body->body.emplace_back(std::move(returnExpr));
ret->funcBody->retType = ASTCloner::Clone(fp.type.get());
fp.assignment->EnableAttr(Attribute::UNREACHABLE);
ret->funcBody->funcDecl = ret.get();
ret->EnableAttr(Attribute::HAS_INITIAL, Attribute::IMPLICIT_ADD, Attribute::NO_REFLECT_INFO);
ret->toBeCompiled = funcDecl.toBeCompiled;
ret->ownerFunc = &funcDecl;
ret->fullPackageName = funcDecl.fullPackageName;
ret->outerDecl = funcDecl.outerDecl;
if (funcDecl.TestAttr(Attribute::CONSTRUCTOR)) {
ret->EnableAttr(Attribute::STATIC);
}
if (funcDecl.TestAttr(Attribute::MUT)) {
ret->EnableAttr(Attribute::MUT);
}
#ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND
ret->EnableAttr(Attribute::PRIVATE);
CollectGenericParam(funcDecl, ret.get());
#endif
return ret;
}
}
void TypeChecker::TypeCheckerImpl::GetSingleParamFunc(Decl& decl)
{
auto fd = As<ASTKind::FUNC_DECL>(&decl);
bool notInherit = !fd || !fd->funcBody || fd->funcBody->paramLists.empty() || fd->TestAttr(Attribute::IMPORTED);
if (notInherit) {
return;
}
std::vector<Ptr<FuncParam>> funcParams;
bool isStatic = fd->TestAttr(Attribute::STATIC);
auto walkFunc = [isStatic, this](Ptr<Node> node) -> VisitAction {
CJC_ASSERT(node);
if (node->astKind == ASTKind::FUNC_DECL) {
if (isStatic) {
node->EnableAttr(Attribute::STATIC);
}
GetSingleParamFunc(*StaticAs<ASTKind::FUNC_DECL>(node));
}
return VisitAction::WALK_CHILDREN;
};
auto walkOrigin = [isStatic](Ptr<Node> node) -> VisitAction {
if (node->astKind == ASTKind::FUNC_DECL && isStatic) {
node->EnableAttr(Attribute::STATIC);
}
return VisitAction::WALK_CHILDREN;
};
for (auto& fp : fd->funcBody->paramLists[0]->params) {
if (fp && fp->assignment && !fp->TestAttr(Attribute::HAS_INITIAL)) {
if (fd->op != TokenKind::ILLEGAL || fd->TestAttr(Attribute::OPEN) || fd->TestAttr(Attribute::ABSTRACT) ||
fd->TestAttr(Attribute::DEFAULT)) {
DiagCannotHaveDefaultParam(diag, *fd, *fp);
return;
}
fp->desugarDecl = MakeDefaultParamFunction(*fp, *fd, funcParams);
Walker walker(fp->desugarDecl.get(), walkFunc);
walker.Walk();
Walker originWalker(fp->assignment.get(), walkOrigin);
originWalker.Walk();
MarkParamWithInitialValue(*fp->assignment);
}
funcParams.push_back(fp.get());
}
}
Ptr<Decl> TypeChecker::TypeCheckerImpl::GetDupInterfaceRecursively(const Node& triggerNode, Ty& interfaceTy,
const TypeSubst& instantiateMap, std::unordered_set<Ptr<InterfaceTy>>& res,
std::unordered_set<Ptr<ClassLikeDecl>>& passedClassLikeDecls)
{
auto insTy = typeManager.GetInstantiatedTy(&interfaceTy, instantiateMap);
if (!Ty::IsTyCorrect(insTy) || !insTy->IsInterface()) {
return nullptr;
}
auto ity = RawStaticCast<InterfaceTy*>(insTy);
auto insertRes = res.insert(ity);
if (!insertRes.second) {
return ity->declPtr;
}
CJC_NULLPTR_CHECK(ity->declPtr);
TypeSubst superInstantiateMap = GenerateTypeMapping(*ity->declPtr, ity->typeArgs);
if (superInstantiateMap.empty()) {
return nullptr;
}
return GetDupSuperInterface(triggerNode, *ity->declPtr, superInstantiateMap, passedClassLikeDecls);
}
Ptr<Decl> TypeChecker::TypeCheckerImpl::GetExtendDupSuperInterface(const Node& triggerNode, const InheritableDecl& decl,
const TypeSubst& instantiateMap, std::unordered_set<Ptr<InterfaceTy>>& res,
std::unordered_set<Ptr<ClassLikeDecl>>& passedClassLikeDecls)
{
if (!decl.TestAttr(Attribute::GENERIC) || !Ty::IsTyCorrect(decl.GetTy())) {
return nullptr;
}
auto extends = typeManager.GetDeclExtends(decl);
for (auto& extend : extends) {
TypeSubst extendInstMap = GenerateTypeMapping(*extend, decl.GetTy()->typeArgs);
if (extendInstMap.size() != instantiateMap.size()) {
continue;
}
extendInstMap.insert(instantiateMap.begin(), instantiateMap.end());
for (auto& interfaceType : extend->inheritedTypes) {
if (!Ty::IsTyCorrect(interfaceType->GetTy())) {
continue;
}
auto ret = GetDupInterfaceRecursively(
triggerNode, *interfaceType->GetTy(), extendInstMap, res, passedClassLikeDecls);
if (ret) {
return ret;
}
}
}
return nullptr;
}
Ptr<Decl> TypeChecker::TypeCheckerImpl::GetDupSuperInterface(const Node& triggerNode, InheritableDecl& decl,
const TypeSubst& instantiateMap, std::unordered_set<Ptr<ClassLikeDecl>>& passedClassLikeDecls, bool checkExtend)
{
if (decl.IsClassLikeDecl()) {
auto cld = RawStaticCast<ClassLikeDecl*>(&decl);
if (passedClassLikeDecls.find(cld) != passedClassLikeDecls.end()) {
return nullptr;
}
passedClassLikeDecls.insert(cld);
}
std::unordered_set<Ptr<InterfaceTy>> instInterfaceTys;
for (auto& interfaceType : decl.inheritedTypes) {
if (!Ty::IsTyCorrect(interfaceType->GetTy())) {
continue;
}
auto ret = GetDupInterfaceRecursively(
triggerNode, *interfaceType->GetTy(), instantiateMap, instInterfaceTys, passedClassLikeDecls);
if (ret) {
return ret;
}
}
if (checkExtend) {
return GetExtendDupSuperInterface(triggerNode, decl, instantiateMap, instInterfaceTys, passedClassLikeDecls);
} else {
return nullptr;
}
}
void TypeChecker::TypeCheckerImpl::CheckInstDupSuperInterfaces(
const Node& triggerNode, InheritableDecl& decl, const TypeSubst& instantiateMap, bool checkExtend)
{
std::unordered_set<Ptr<ClassLikeDecl>> passedClassLikeDecls;
auto interfaceDecl = GetDupSuperInterface(triggerNode, decl, instantiateMap, passedClassLikeDecls, checkExtend);
auto baseDecl = Ty::GetDeclPtrOfTy(decl.GetTy());
std::string name = baseDecl ? baseDecl->identifier.Val() : Ty::ToString(decl.GetTy());
if (interfaceDecl) {
diag.Diagnose(triggerNode, DiagKind::sema_inherit_duplicate_interface, DeclKindToString(decl), name,
interfaceDecl->identifier.Val());
}
}
VisitAction TypeChecker::TypeCheckerImpl::CheckInstDupSuperInterfaces(const Type& type)
{
if (!Ty::IsTyCorrect(type.GetTy())) {
return VisitAction::SKIP_CHILDREN;
}
auto typeTarget = TypeCheckUtil::GetRealTarget(type.GetTarget());
if (!typeTarget || !typeTarget->IsNominalDecl()) {
return VisitAction::WALK_CHILDREN;
}
TypeSubst typeMapping = GenerateTypeMapping(*typeTarget, type.GetTy()->typeArgs);
if (typeMapping.empty()) {
return VisitAction::SKIP_CHILDREN;
}
CheckInstDupSuperInterfaces(type, *StaticCast<InheritableDecl*>(typeTarget), typeMapping, false);
return VisitAction::WALK_CHILDREN;
}
VisitAction TypeChecker::TypeCheckerImpl::CheckInstDupSuperInterfaces(const Expr& expr)
{
if (!Ty::IsTyCorrect(expr.GetTy())) {
return VisitAction::WALK_CHILDREN;
}
auto target = TypeCheckUtil::GetRealTarget(expr.GetTarget());
auto instTys = TypeCheckUtil::GetInstanationTys(expr);
if (!target || instTys.empty()) {
return VisitAction::WALK_CHILDREN;
}
if (IsClassOrEnumConstructor(*target) && target->outerDecl) {
target = target->outerDecl;
}
if (!target->IsNominalDecl()) {
return VisitAction::WALK_CHILDREN;
}
TypeSubst instantiateMap = GenerateTypeMapping(*target, instTys);
if (instantiateMap.empty()) {
return VisitAction::SKIP_CHILDREN;
}
CheckInstDupSuperInterfaces(expr, *StaticCast<InheritableDecl*>(target), instantiateMap);
return VisitAction::WALK_CHILDREN;
}
void TypeChecker::TypeCheckerImpl::CheckInstDupSuperInterfacesEntry(Node& n)
{
Utils::ProfileRecorder recorder("Post TypeCheck", "CheckInstDupSuperInterfacesEntry");
std::function<VisitAction(Ptr<Node>)> visitor = [this, &visitor](Ptr<Node> n) {
switch (n->astKind) {
case ASTKind::PACKAGE: {
auto& pkg = *RawStaticCast<Package*>(n);
for (auto& it : pkg.files) {
Walker(it.get(), visitor).Walk();
}
return VisitAction::STOP_NOW;
}
case ASTKind::REF_TYPE:
case ASTKind::QUALIFIED_TYPE: {
return CheckInstDupSuperInterfaces(*RawStaticCast<Type*>(n));
}
case ASTKind::REF_EXPR:
case ASTKind::MEMBER_ACCESS: {
return CheckInstDupSuperInterfaces(*RawStaticCast<Expr*>(n));
}
default: {
return VisitAction::WALK_CHILDREN;
}
}
};
Walker walker(&n, visitor);
walker.Walk();
}
void TypeChecker::TypeCheckerImpl::CheckOverflow(Node& node)
{
Utils::ProfileRecorder recorder("Post TypeCheck", "CheckOverflow");
Walker walker(&node, nullptr, [this](Ptr<Node> node) -> VisitAction {
switch (node->astKind) {
case ASTKind::LIT_CONST_EXPR: {
auto& lce = *StaticAs<ASTKind::LIT_CONST_EXPR>(node);
if (!lce.desugarExpr && Ty::IsTyCorrect(lce.GetTy())) {
ChkLitConstExprRange(lce);
}
return VisitAction::WALK_CHILDREN;
}
default: {
return VisitAction::WALK_CHILDREN;
}
}
});
walker.Walk();
}
Ptr<Ty> TypeChecker::TypeCheckerImpl::CalcFuncRetTyFromBody(const FuncBody& fb)
{
if (!fb.body) {
return TypeManager::GetInvalidTy();
}
if (fb.body->body.empty()) {
return TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT);
}
auto& lastNode = fb.body->body.back();
Ptr<Ty> bodyTy = lastNode->IsDecl() ? TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT) : lastNode->GetTy();
Ptr<Ty> retTy = bodyTy;
std::set<Ptr<Ty>> retTys;
Walker(fb.body.get(), [&retTys](auto node) {
CJC_ASSERT(node);
if (node->astKind == ASTKind::FUNC_DECL || node->astKind == ASTKind::LAMBDA_EXPR) {
return VisitAction::SKIP_CHILDREN;
} else if (auto re = DynamicCast<ReturnExpr*>(node); re && re->expr) {
if (Ty::IsTyCorrect(re->expr->GetTy())) {
retTys.emplace(re->expr->GetTy());
}
}
return VisitAction::WALK_CHILDREN;
}).Walk();
CJC_NULLPTR_CHECK(fb.retType);
bool returnUnit = Ty::IsTyCorrect(fb.retType->GetTy()) && fb.retType->GetTy()->IsUnit();
if (returnUnit) {
if (retTys.empty()) {
return fb.retType->GetTy();
}
} else {
retTys.emplace(bodyTy);
}
if (Ty::IsTyCorrect(bodyTy)) {
auto joinAndMeet = JoinAndMeet(typeManager, retTys, {}, &importManager, fb.curFile);
auto joinRes = joinAndMeet.JoinAsVisibleTy();
auto [optErrs, joinedRet] = JoinAndMeet::SetJoinedType(retTy, joinRes);
retTy = joinedRet;
if (optErrs) {
auto builder = diag.Diagnose(*lastNode, DiagKind::sema_incompatible_func_body_and_return_type);
builder.AddNote(*optErrs);
}
return retTy;
} else {
return TypeManager::GetInvalidTy();
}
}
}