diff --git a/.gitignore b/.gitignore
index a10c998a..d0cd885e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@
build/
build_*/
install/
+install_*/
extra/python/src/jit.cpp
extra/jupyter/build/
diff --git a/codon/parser/cache.cpp b/codon/parser/cache.cpp
index cbe290dd..a89da2cc 100644
--- a/codon/parser/cache.cpp
+++ b/codon/parser/cache.cpp
@@ -243,198 +243,6 @@ void Cache::populatePythonModule() {
LOG("[py] ====== module generation =======");
#define N std::make_shared
- auto sctx = imports[MAIN_IMPORT].ctx;
- auto getFn = [&](const std::string &canonicalName,
- const std::string &className = "") -> ir::Func * {
- auto fna = in(functions, canonicalName) ? functions[canonicalName].ast : nullptr;
- std::vector params;
- std::vector args;
-
- bool isMethod = className.empty() ? false : fna->hasAttr(Attr::Method);
- auto name =
- fmt::format("{}{}", className.empty() ? "" : className + ".", canonicalName);
-
- auto wrapArg = [&](ExprPtr po, int ai) {
- if (fna && fna->args[ai].type) {
- return N(N(fna->args[ai].type->clone(), "__from_py__"), po);
- } else {
- return N(N("pyobj"), po);
- }
- };
-
- StmtPtr ret = nullptr;
- ExprPtr retType = N("cobj");
- const int *p = nullptr;
- LOG("[py] {}: {} => {} ({})", isMethod ? "method" : "classm", name, isMethod,
- rev(canonicalName));
- if (isMethod && in(std::set{"__abs__", "__pos__", "__neg__",
- "__invert__", "__int__", "__float__",
- "__index__", "__repr__", "__str__"},
- rev(canonicalName))) {
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")}};
- ret = N(N(N(
- N(N(canonicalName),
- std::vector{wrapArg(N(params[0].name), 0)}),
- "__to_py__")));
- } else if (isMethod &&
- in(std::set{"__len__", "__hash__"}, rev(canonicalName))) {
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")}};
- ret = N(
- N(N(canonicalName),
- std::vector{wrapArg(N(params[0].name), 0)}));
- retType = N("i64");
- } else if (isMethod && rev(canonicalName) == "__bool__") {
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")}};
- ret = N(N(
- N("i32"),
- N(N(canonicalName),
- std::vector{wrapArg(N(params[0].name), 0)})));
- retType = N("i32");
- } else if (isMethod && rev(canonicalName) == "__del__") {
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")}};
- ret = N(N(
- N("i32"),
- N(N(canonicalName),
- std::vector{wrapArg(N(params[0].name), 0)})));
- retType = N("i32");
- } else if (isMethod && rev(canonicalName) == "__contains__") {
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")},
- Param{sctx->generateCanonicalName("args"), N("cobj")}};
- ret = N(N(
- N("i32"),
- N(N(canonicalName),
- std::vector{wrapArg(N(params[0].name), 0),
- wrapArg(N(params[1].name), 1)})));
- retType = N("i32");
- } else if (isMethod && rev(canonicalName) == "__init__") {
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")},
- Param{sctx->generateCanonicalName("args"), N("cobj")},
- Param{sctx->generateCanonicalName("kwargs"), N("cobj")}};
- std::vector tupTypes;
- for (size_t ai = 1; ai < fna->args.size(); ai++)
- tupTypes.push_back(fna->args[ai].type ? fna->args[ai].type->clone()
- : N("pyobj"));
- ExprPtr tup = N(
- N(fmt::format("Tuple.N{}", tupTypes.size())), tupTypes);
- tup = N(N(tup, "__from_py__"), N(params[1].name));
- ret = N(N(N(N(canonicalName),
- wrapArg(N(params[0].name), 0),
- N(tup))),
- N(N(N("i32"), N(0))));
- retType = N("i32");
- } else if (isMethod && rev(canonicalName) == "__call__") {
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")},
- Param{sctx->generateCanonicalName("args"), N("cobj")},
- Param{sctx->generateCanonicalName("kwargs"), N("cobj")}};
- std::vector tupTypes;
- for (size_t ai = 1; ai < fna->args.size(); ai++)
- tupTypes.push_back(fna->args[ai].type ? fna->args[ai].type->clone()
- : N("pyobj"));
- ExprPtr tup = N(
- N(fmt::format("Tuple.N{}", tupTypes.size())), tupTypes);
- tup = N(N(tup, "__from_py__"), N(params[1].name));
- ret = N(N(N(
- N(N(canonicalName), wrapArg(N(params[0].name), 0),
- N(tup)),
- "__to_py__")));
- } else if (isMethod && in(std::set{"__lt__", "__le__", "__eq__",
- "__ne__", "__gt__", "__ge__"},
- rev(canonicalName))) {
- params = std::vector{
- Param{sctx->generateCanonicalName("self"), N("cobj")},
- Param{sctx->generateCanonicalName("other"), N("cobj")}};
- ret = N(N(N(
- N(N(canonicalName),
- std::vector{wrapArg(N(params[0].name), 0),
- wrapArg(N(params[1].name), 1)}),
- "__to_py__")));
- } else if (isMethod && rev(canonicalName) == "__setitem__") {
- // TODO: return -1 if __delitem__ does not exist? right now it assumes it...
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")},
- Param{sctx->generateCanonicalName("index"), N("cobj")},
- Param{sctx->generateCanonicalName("value"), N("cobj")}};
- retType = N("i32");
- ret = N(std::vector{
- N(
- N(N(N("hasattr"), N(className),
- N("__delitem__")),
- "&&",
- N(N(params[2].name),
- "==", N(N("cobj")))),
- N(N(N(N(className), "__delitem__"),
- wrapArg(N(params[0].name), 0),
- wrapArg(N(params[1].name), 1))),
- N(N(N(canonicalName),
- wrapArg(N(params[0].name), 0),
- wrapArg(N(params[1].name), 1),
- wrapArg(N(params[2].name), 2)))),
- N(N(N("i32"), N(0)))});
- } else if (isMethod && rev(canonicalName) == "__iter__") {
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")}};
- ret = N(
- N(N("std.internal.python._PyextIterWrap._init:0"),
- N(params[0].name), N(className)));
- } else {
- // def wrapper(self: cobj, arg: cobj) -> cobj
- // def wrapper(self: cobj, args: Ptr[cobj], nargs: int) -> cobj
- // iter, iternext: todo
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")},
- Param{sctx->generateCanonicalName("args"), N("cobj")}};
- if (fna->args.size() > 1 + isMethod) {
- params.back().type = N(N("Ptr"), params.back().type);
- params.push_back(Param{sctx->generateCanonicalName("nargs"), N("int")});
- }
- ExprPtr po = N(params[0].name);
- if (!className.empty())
- po = N(N(N(className), "__from_py__"), po);
- if (isMethod)
- args.push_back(po);
- if (fna->args.size() > 1 + isMethod) {
- for (size_t ai = isMethod; ai < fna->args.size(); ai++) {
- ExprPtr po =
- N(N(params[1].name), N(ai - isMethod));
- if (fna->args[ai].type) {
- po =
- N(N(fna->args[ai].type->clone(), "__from_py__"), po);
- } else {
- po = N(N("pyobj"), po);
- }
- args.push_back(po);
- }
- } else if (fna->args.size() == 1 + isMethod) {
- ExprPtr po = N(params[1].name);
- if (fna->args[isMethod].type) {
- po = N(N(fna->args[isMethod].type->clone(), "__from_py__"),
- po);
- } else {
- po = N(N("pyobj"), po);
- }
- args.push_back(po);
- }
- ret = N(N(
- N(N(N(canonicalName), args), "__to_py__")));
- }
- auto stubName = sctx->generateCanonicalName(fmt::format("_py.{}", name));
- auto node = N(stubName, retType, params, N(ret),
- Attr({Attr::ForceRealize}));
- functions[node->name].ast = node;
- auto tv = TypecheckVisitor(typeCtx);
- auto tnode = tv.transform(node);
- // LOG("=> {}", tnode->toString(2));
- seqassertn(tnode, "blah");
- seqassertn(typeCtx->forceFind(stubName) && typeCtx->forceFind(stubName)->type,
- "bad type");
- auto rtv = tv.realize(typeCtx->forceFind(stubName)->type);
- seqassertn(rtv, "realization of {} failed", stubName);
-
- auto pr = pendingRealizations; // copy it as it might be modified
- for (auto &fn : pr)
- TranslateVisitor(codegenCtx).transform(functions[fn.first].ast->clone());
- auto f = functions[rtv->getFunc()->ast->name].realizations[rtv->realizedName()]->ir;
- classes[className].methods[node->name] = node->name; // to allow getattr()
- overloads[node->name] = std::vector{{node->name, 0}};
- return f;
- };
if (!pyModule)
pyModule = std::make_shared();
@@ -443,6 +251,23 @@ void Cache::populatePythonModule() {
int oldAge = typeCtx->age;
typeCtx->age = 99999;
+ auto realizeIR = [&](const types::FuncTypePtr &fn,
+ const std::vector &generics = {}) -> ir::Func * {
+ auto fnType = typeCtx->instantiate(fn);
+ types::Type::Unification u;
+ for (size_t i = 0; i < generics.size(); i++)
+ fnType->getFunc()->funcGenerics[i].type->unify(generics[i].get(), &u);
+ fnType = TypecheckVisitor(typeCtx).realize(fnType);
+ if (!fnType)
+ return nullptr;
+
+ auto pr = pendingRealizations; // copy it as it might be modified
+ for (auto &fn : pr)
+ TranslateVisitor(codegenCtx).transform(functions[fn.first].ast->clone());
+ return functions[fn->ast->name].realizations[fnType->realizedName()]->ir;
+ };
+
+ const std::string pyWrap = "std.internal.python._PyWrap";
for (const auto &[cn, c] : classes)
if (c.module.empty() && startswith(cn, "Pyx")) {
ir::PyType py{rev(cn), c.ast->getDocstr()};
@@ -453,12 +278,12 @@ void Cache::populatePythonModule() {
tc = TypecheckVisitor(typeCtx).realize(tc);
seqassertn(tc, "cannot realize '{}'", cn);
- // fix to_py / from_py
+ // 1. Replace to_py / from_py with _PyWrap.wrap_to_py/from_py
if (auto ofnn = in(c.methods, "__to_py__")) {
auto fnn = overloads[*ofnn].begin()->name; // default first overload!
auto &fna = functions[fnn].ast;
fna->getFunction()->suite = N(N(
- N("__internal__.to_py:0"), N(fna->args[0].name)));
+ N(pyWrap + ".wrap_to_py:0"), N(fna->args[0].name)));
} else {
compilationError(fmt::format("class '{}' has no __to_py__"), rev(cn));
}
@@ -466,7 +291,7 @@ void Cache::populatePythonModule() {
auto fnn = overloads[*ofnn].begin()->name; // default first overload!
auto &fna = functions[fnn].ast;
fna->getFunction()->suite =
- N(N(N("__internal__.from_py:0"),
+ N(N(N(pyWrap + ".wrap_from_py:0"),
N(fna->args[0].name), N(cn)));
} else {
compilationError(fmt::format("class '{}' has no __from_py__"), rev(cn));
@@ -488,23 +313,46 @@ void Cache::populatePythonModule() {
ir::util::call(functions[fnn].realizations.begin()->second->ir, args)));
}
}
-
- for (auto &[rn, r] : functions["__internal__.py_type:0"].realizations) {
+ for (auto &[rn, r] : functions[pyWrap + ".py_type:0"].realizations) {
if (r->type->funcGenerics[0].type->unify(tc.get(), nullptr) >= 0) {
py.typePtrHook = r->ir;
break;
}
}
+ // 2. Handle methods
auto methods = c.methods;
for (const auto &[n, ofnn] : methods) {
- auto fnn = overloads[ofnn].back().name; // last overload
- auto &fna = functions[fnn].ast;
- if (fna->hasAttr("autogenerated"))
+ auto canonicalName = overloads[ofnn].back().name;
+ if (overloads[ofnn].size() == 1 &&
+ functions[canonicalName].ast->hasAttr("autogenerated"))
continue;
- auto f = getFn(fnn, cn);
+
+ auto fna = functions[canonicalName].ast;
+ bool isMethod = fna->hasAttr(Attr::Method);
+ std::string call = pyWrap + ".wrap_single";
+ if (fna->args.size() - isMethod > 1)
+ call = pyWrap + ".wrap_multiple";
+ bool isMagic = false;
+ if (startswith(n, "__") && endswith(n, "__")) {
+ if (auto i = in(classes[pyWrap].methods,
+ "wrap_magic_" + n.substr(2, n.size() - 4))) {
+ call = *i;
+ isMagic = true;
+ }
+ }
+
+ auto fnName = call + ":0";
+ seqassertn(in(functions, fnName), "bad name");
+ auto generics = std::vector{tc};
+ if (!isMagic) {
+ generics.push_back(std::make_shared(this, n));
+ generics.push_back(std::make_shared(this, isMethod));
+ }
+ auto f = realizeIR(functions[fnName].type, generics);
if (!f)
continue;
+
if (n == "__repr__") {
py.repr = f;
} else if (n == "__add__") {
@@ -610,13 +458,8 @@ void Cache::populatePythonModule() {
if (in(std::set{"__lt__", "__le__", "__eq__", "__ne__", "__gt__",
"__ge__"},
m.name)) {
- auto f = realizeFunction(
- typeCtx->forceFind("__internal__.cmp_py:0")->type->getFunc(),
- {typeCtx->forceFind("cobj")->type, typeCtx->forceFind("cobj")->type,
- typeCtx->forceFind("cobj")->type, typeCtx->forceFind("i32")->type},
- {tc,
- std::make_shared(this, m.func->getUnmangledName())});
- py.cmp = f;
+ py.cmp = realizeIR(
+ typeCtx->forceFind(pyWrap + ".wrap_cmp:0")->type->getFunc(), {tc});
break;
}
}
@@ -626,75 +469,25 @@ void Cache::populatePythonModule() {
auto &r = c.realizations.begin()->second;
py.type = realizeType(r->type);
for (auto &[mn, mt] : r->fields) {
- // This will be handled later
- // py.members.push_back(ir::PyMember{mn, "",
- // mt->is("int") ?
- // ir::PyMember::Type::LONGLONG :
- // mt->is("float")
- // ? ir::PyMember::Type::DOUBLE
- // : ir::PyMember::Type::OBJECT,
- // true});
-
+ /// TODO: handle PyMember for tuples
// Generate getters & setters
- std::vector params{
- Param{sctx->generateCanonicalName("self"), N("cobj")},
- Param{sctx->generateCanonicalName("closure"), N("cobj")}};
- ExprPtr retType = N("cobj");
- StmtPtr ret = N(N(
- N(N(N(N(N(cn), "__from_py__"),
- N(params[0].name)),
- mn),
- "__to_py__")));
- auto gstub = sctx->generateCanonicalName(fmt::format("_py._get_{}", mn));
- auto gnode = N(gstub, retType, params, N(ret),
- Attr({Attr::ForceRealize}));
- functions[gstub].ast = gnode;
-
- params = {Param{sctx->generateCanonicalName("self"), N("cobj")},
- Param{sctx->generateCanonicalName("what"), N("cobj")},
- Param{sctx->generateCanonicalName("closure"), N("cobj")}};
- retType = N("i32");
- ret = N(
- N(
- N(N(N(cn), "__from_py__"),
- N(params[0].name)),
- mn,
- N(N(N(mt->realizedName()), "__from_py__"),
- N(params[1].name))),
- N(N(N("i32"), N(0))));
- auto sstub = sctx->generateCanonicalName(fmt::format("_py._set_{}", mn));
- auto snode = N(sstub, retType, params, N(ret),
- Attr({Attr::ForceRealize}));
- functions[sstub].ast = snode;
-
- auto tv = TypecheckVisitor(typeCtx);
- auto tnode = tv.transform(N(gnode, snode));
- seqassertn(tnode, "blah");
- for (auto &stub : std::vector{gstub, sstub}) {
- seqassertn(typeCtx->forceFind(stub) && typeCtx->forceFind(stub)->type,
- "bad type");
- auto rtv = tv.realize(typeCtx->forceFind(stub)->type);
- seqassertn(rtv, "realization of {} failed", stub);
- }
-
- auto pr = pendingRealizations; // copy it as it might be modified
- for (auto &fn : pr)
- TranslateVisitor(codegenCtx).transform(functions[fn.first].ast->clone());
-
- py.getset.push_back({mn, "", functions[gstub].realizations.begin()->second->ir,
- functions[sstub].realizations.begin()->second->ir});
- LOG("[py] {}: {}.{} => {}, {}", "member", cn, mn, gstub, sstub);
+ auto generics = std::vector{
+ tc, std::make_shared(this, mn)};
+ auto gf = realizeIR(functions[pyWrap + ".wrap_get:0"].type, generics);
+ auto sf = realizeIR(functions[pyWrap + ".wrap_set:0"].type, generics);
+ py.getset.push_back({mn, "", gf, sf});
+ LOG("[py] {}: {} . {}", "member", cn, mn);
}
pyModule->types.push_back(py);
}
// Handle __iternext__ wrappers
- auto cin = "std.internal.python._PyextIterWrap";
+ auto cin = "std.internal.python._PyWrap.IterWrap";
for (auto &[cn, cr] : classes[cin].realizations) {
LOG("[py] iterfn: {}", cn);
ir::PyType py{cn, ""};
auto tc = cr->type;
- for (auto &[rn, r] : functions["__internal__.py_type:0"].realizations) {
+ for (auto &[rn, r] : functions[pyWrap + ".py_type:0"].realizations) {
if (r->type->funcGenerics[0].type->unify(tc.get(), nullptr) >= 0) {
py.typePtrHook = r->ir;
break;
@@ -703,7 +496,7 @@ void Cache::populatePythonModule() {
auto &methods = classes[cin].methods;
for (auto &n : std::vector{"_iter", "_iternext"}) {
- auto fnn = overloads[methods[n]].begin()->name; // default first overload!
+ auto fnn = overloads[methods[n]].begin()->name;
auto &fna = functions[fnn];
auto ft = typeCtx->instantiate(fna.type, tc->getClass());
auto rtv = TypecheckVisitor(typeCtx).realize(ft);
@@ -721,14 +514,20 @@ void Cache::populatePythonModule() {
for (const auto &[fn, f] : functions)
if (f.isToplevel) {
- auto fnn = overloads[f.rootName].back().name; // last overload
- if (startswith(rev(fnn), "_"))
- continue;
- LOG("[py] functn {} => {}", rev(fn), fnn);
- auto ir = getFn(fnn);
- pyModule->functions.push_back(ir::PyFunction{rev(fn), f.ast->getDocstr(), ir,
- ir::PyFunction::Type::TOPLEVEL,
- int(f.ast->args.size())});
+ std::string call = pyWrap + ".wrap_single";
+ if (f.ast->args.size() > 1)
+ call = pyWrap + ".wrap_multiple";
+ auto fnName = call + ":0";
+ seqassertn(in(functions, fnName), "bad name");
+ LOG("<- {}", typeCtx->forceFind(".toplevel")->type);
+ auto generics = std::vector{
+ typeCtx->forceFind(".toplevel")->type,
+ std::make_shared(this, rev(f.ast->name))};
+ if (auto ir = realizeIR(functions[fnName].type, generics)) {
+ pyModule->functions.push_back(ir::PyFunction{rev(fn), f.ast->getDocstr(), ir,
+ ir::PyFunction::Type::TOPLEVEL,
+ int(f.ast->args.size())});
+ }
}
typeCtx->age = oldAge;
diff --git a/codon/parser/visitors/simplify/function.cpp b/codon/parser/visitors/simplify/function.cpp
index d33e5a8e..8454512b 100644
--- a/codon/parser/visitors/simplify/function.cpp
+++ b/codon/parser/visitors/simplify/function.cpp
@@ -257,6 +257,9 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) {
stmt->attributes.parentClass = ctx->getBase()->name;
// Add the method to the class' method list
ctx->cache->classes[ctx->getBase()->name].methods[stmt->name] = rootName;
+ } else {
+ // Hack so that we can later use same helpers for class overloads
+ ctx->cache->classes[".toplevel"].methods[stmt->name] = rootName;
}
// Handle captures. Add additional argument to the function for every capture.
diff --git a/codon/parser/visitors/simplify/import.cpp b/codon/parser/visitors/simplify/import.cpp
index 7505b837..53162a9d 100644
--- a/codon/parser/visitors/simplify/import.cpp
+++ b/codon/parser/visitors/simplify/import.cpp
@@ -90,7 +90,6 @@ void SimplifyVisitor::visit(ImportStmt *stmt) {
// `__` while the standard library is being loaded
auto c = i.second.front();
if (c->isConditional() && i.first.find('.') == std::string::npos) {
- LOG("-> fix {} :: {}", import.moduleName, i.first);
c = import.ctx->findDominatingBinding(i.first);
}
// Imports should ignore noShadow property
diff --git a/codon/parser/visitors/simplify/simplify.cpp b/codon/parser/visitors/simplify/simplify.cpp
index 5de40859..3f1acd70 100644
--- a/codon/parser/visitors/simplify/simplify.cpp
+++ b/codon/parser/visitors/simplify/simplify.cpp
@@ -86,6 +86,8 @@ SimplifyVisitor::apply(Cache *cache, const StmtPtr &node, const std::string &fil
// Prepare the code
auto suite = N();
+ suite->stmts.push_back(N(".toplevel", std::vector{}, nullptr,
+ std::vector{N(Attr::Internal)}));
for (auto &d : defines) {
// Load compile-time defines (e.g., codon run -DFOO=1 ...)
suite->stmts.push_back(
diff --git a/codon/parser/visitors/typecheck/access.cpp b/codon/parser/visitors/typecheck/access.cpp
index 9caa87d0..00a8983b 100644
--- a/codon/parser/visitors/typecheck/access.cpp
+++ b/codon/parser/visitors/typecheck/access.cpp
@@ -166,6 +166,12 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr,
return transform(N(expr->expr->type->prettyString()));
return nullptr;
}
+ // Special case: expr.__is_static__
+ if (expr->member == "__is_static__") {
+ if (expr->expr->isDone())
+ return transform(N(expr->expr->isStatic()));
+ return nullptr;
+ }
// Special case: cls.__vtable_id__
if (expr->expr->isType() && expr->member == "__vtable_id__") {
if (auto c = realize(expr->expr->type))
diff --git a/codon/parser/visitors/typecheck/call.cpp b/codon/parser/visitors/typecheck/call.cpp
index abade182..f85fa5eb 100644
--- a/codon/parser/visitors/typecheck/call.cpp
+++ b/codon/parser/visitors/typecheck/call.cpp
@@ -543,6 +543,8 @@ std::pair TypecheckVisitor::transformSpecialCall(CallExpr *expr)
return {true, transformHasAttr(expr)};
} else if (val == "getattr") {
return {true, transformGetAttr(expr)};
+ } else if (val == "setattr") {
+ return {true, transformSetAttr(expr)};
} else if (val == "type.__new__:0") {
return {true, transformTypeFn(expr)};
} else if (val == "compile_error") {
@@ -553,6 +555,8 @@ std::pair TypecheckVisitor::transformSpecialCall(CallExpr *expr)
return {true, transformRealizedFn(expr)};
} else if (val == "__static_print__") {
return {false, transformStaticPrintFn(expr)};
+ } else if (auto e = transformInternalStaticFn(expr)) {
+ return {true, e};
} else {
return {false, nullptr};
}
@@ -827,6 +831,18 @@ ExprPtr TypecheckVisitor::transformGetAttr(CallExpr *expr) {
return transform(N(expr->args[0].value, staticTyp->evaluate().getString()));
}
+/// Transform setattr method to a AssignMemberStmt.
+ExprPtr TypecheckVisitor::transformSetAttr(CallExpr *expr) {
+ auto funcTyp = expr->expr->type->getFunc();
+ auto staticTyp = funcTyp->funcGenerics[0].type->getStatic();
+ if (!staticTyp->canRealize())
+ return nullptr;
+ return transform(N(N(expr->args[0].value,
+ staticTyp->evaluate().getString(),
+ expr->args[1].value),
+ N()));
+}
+
/// Raise a compiler error.
ExprPtr TypecheckVisitor::transformCompileError(CallExpr *expr) {
auto funcTyp = expr->expr->type->getFunc();
@@ -894,6 +910,66 @@ ExprPtr TypecheckVisitor::transformStaticPrintFn(CallExpr *expr) {
return nullptr;
}
+// Transform internal.static calls
+ExprPtr TypecheckVisitor::transformInternalStaticFn(CallExpr *expr) {
+ if (expr->expr->isId("std.internal.static.fn_can_call")) {
+ expr->staticValue.type = StaticValue::INT;
+ auto typ = expr->args[0].value->getType()->getClass();
+ if (!typ)
+ return nullptr;
+
+ auto fn = expr->args[0].value->type->getFunc();
+ if (!fn)
+ error("expected a function, got '{}'", expr->args[0].value->type->prettyString());
+
+ auto inargs = unpackTupleTypes(expr->args[1].value);
+ auto kwargs = unpackTupleTypes(expr->args[2].value);
+ seqassert(inargs && kwargs, "bad call to fn_can_call");
+
+ std::vector callArgs;
+ for (auto &a : *inargs) {
+ callArgs.push_back({a.first, std::make_shared()}); // dummy expression
+ callArgs.back().value->setType(a.second);
+ }
+ for (auto &a : *kwargs) {
+ callArgs.push_back({a.first, std::make_shared()}); // dummy expression
+ callArgs.back().value->setType(a.second);
+ }
+ return transform(N(canCall(fn, callArgs) >= 0));
+ } else if (expr->expr->isId("std.internal.static.fn_arg_has_type")) {
+ expr->staticValue.type = StaticValue::INT;
+ auto fn = expr->args[0].value->type->getFunc();
+ if (!fn)
+ error("expected a function, got '{}'", expr->args[0].value->type->prettyString());
+ auto idx = ctx->getStaticInt(expr->expr->type->getFunc()->funcGenerics[0].type);
+ seqassert(idx, "expected a static integer");
+ auto &args = fn->getArgTypes();
+ return transform(
+ N(*idx >= 0 && *idx < args.size() && args[*idx]->canRealize()));
+ } else if (expr->expr->isId("std.internal.static.fn_arg_get_type")) {
+ auto fn = expr->args[0].value->type->getFunc();
+ if (!fn)
+ error("expected a function, got '{}'", expr->args[0].value->type->prettyString());
+ auto idx = ctx->getStaticInt(expr->expr->type->getFunc()->funcGenerics[0].type);
+ seqassert(idx, "expected a static integer");
+ auto &args = fn->getArgTypes();
+ if (*idx < 0 || *idx >= args.size() || !args[*idx]->canRealize())
+ error("argument does not have type");
+ return transform(NT(args[*idx]->realizedName()));
+ } else if (expr->expr->isId("std.internal.static.fn_args")) {
+ auto fn = expr->args[0].value->type->getFunc();
+ if (!fn)
+ error("expected a function, got '{}'", expr->args[0].value->type->prettyString());
+ std::vector v;
+ for (size_t i = 0; i < fn->ast->args.size(); i++)
+ v.push_back(N(std::vector{
+ N(i), N(ctx->cache->rev(fn->ast->args[i].name))}));
+ return transform(N(v));
+ } else {
+ return nullptr;
+ }
+}
+
/// Get the list that describes the inheritance hierarchy of a given type.
/// The first type in the list is the most recently inherited type.
std::vector TypecheckVisitor::getSuperTypes(const ClassTypePtr &cls) {
diff --git a/codon/parser/visitors/typecheck/ctx.cpp b/codon/parser/visitors/typecheck/ctx.cpp
index 97adfec5..76146085 100644
--- a/codon/parser/visitors/typecheck/ctx.cpp
+++ b/codon/parser/visitors/typecheck/ctx.cpp
@@ -305,4 +305,44 @@ std::string TypeContext::debugInfo() {
getRealizationBase()->iteration, getSrcInfo());
}
+std::shared_ptr