From 12d21ff5eb0fb9be13f70e9c561c830530962f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Thu, 2 Mar 2023 21:59:42 -0800 Subject: [PATCH] Add static reflection methods (setattr; internal.static.*); refactor PyExt to python.codon; handle errors and kwargs in PyExt --- .gitignore | 1 + codon/parser/cache.cpp | 351 ++++-------------- codon/parser/visitors/simplify/function.cpp | 3 + codon/parser/visitors/simplify/import.cpp | 1 - codon/parser/visitors/simplify/simplify.cpp | 2 + codon/parser/visitors/typecheck/access.cpp | 6 + codon/parser/visitors/typecheck/call.cpp | 76 ++++ codon/parser/visitors/typecheck/ctx.cpp | 40 ++ codon/parser/visitors/typecheck/ctx.h | 7 + codon/parser/visitors/typecheck/loops.cpp | 71 +++- codon/parser/visitors/typecheck/typecheck.cpp | 150 +++++--- codon/parser/visitors/typecheck/typecheck.h | 7 + stdlib/internal/core.codon | 3 + stdlib/internal/python.codon | 261 ++++++++++--- stdlib/internal/static.codon | 18 + 15 files changed, 611 insertions(+), 386 deletions(-) create mode 100644 stdlib/internal/static.codon 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, std::vector>> +TypeContext::getFunctionArgs(types::TypePtr t) { + if (!t->getFunc()) + return nullptr; + auto fn = t->getFunc(); + auto ret = std::make_shared< + std::pair, std::vector>>(); + for (auto &t : fn->funcGenerics) + ret->first.push_back(t.type); + for (auto &t : fn->generics[0].type->getRecord()->args) + ret->second.push_back(t); + return ret; +} + +std::shared_ptr TypeContext::getStaticString(types::TypePtr t) { + if (auto s = t->getStatic()) { + auto r = s->evaluate(); + if (r.type == StaticValue::STRING) + return std::make_shared(r.getString()); + } + return nullptr; +} + +std::shared_ptr TypeContext::getStaticInt(types::TypePtr t) { + if (auto s = t->getStatic()) { + auto r = s->evaluate(); + if (r.type == StaticValue::INT) + return std::make_shared(r.getInt()); + } + return nullptr; +} + +types::FuncTypePtr TypeContext::extractFunction(types::TypePtr t) { + if (auto f = t->getFunc()) + return f; + if (auto p = t->getPartial()) + return p->func; + return nullptr; +} + } // namespace codon::ast diff --git a/codon/parser/visitors/typecheck/ctx.h b/codon/parser/visitors/typecheck/ctx.h index 06c085c4..8021c89d 100644 --- a/codon/parser/visitors/typecheck/ctx.h +++ b/codon/parser/visitors/typecheck/ctx.h @@ -157,6 +157,13 @@ private: void dump(int pad); /// Pretty-print the current realization context. std::string debugInfo(); + +public: + std::shared_ptr, std::vector>> + getFunctionArgs(types::TypePtr t); + std::shared_ptr getStaticString(types::TypePtr t); + std::shared_ptr getStaticInt(types::TypePtr t); + types::FuncTypePtr extractFunction(types::TypePtr t); }; } // namespace codon::ast diff --git a/codon/parser/visitors/typecheck/loops.cpp b/codon/parser/visitors/typecheck/loops.cpp index 479241da..8063b682 100644 --- a/codon/parser/visitors/typecheck/loops.cpp +++ b/codon/parser/visitors/typecheck/loops.cpp @@ -168,17 +168,22 @@ StmtPtr TypecheckVisitor::transformHeterogenousTupleFor(ForStmt *stmt) { StmtPtr TypecheckVisitor::transformStaticForLoop(ForStmt *stmt) { auto loopVar = ctx->cache->getTemporaryVar("loop"); auto fn = [&](const std::string &var, const ExprPtr &expr) { - bool staticInt = expr->isStatic(); - auto t = NT( - N("Static"), - N(expr->staticValue.type == StaticValue::INT ? "int" : "str")); auto brk = N(); brk->setDone(); // Avoid transforming this one to continue // var [: Static] := expr; suite... - auto loop = N(N(loopVar), - N(N(N(var), expr->clone(), - staticInt ? t : nullptr), - clone(stmt->suite), brk)); + auto loop = N( + N(loopVar), + N( + expr ? N(N(var), expr->clone(), + expr->isStatic() + ? NT(N("Static"), + N(expr->staticValue.type == + StaticValue::INT + ? "int" + : "str")) + : nullptr) + : nullptr, + clone(stmt->suite), brk)); loop->gotoVar = loopVar; return loop; }; @@ -212,6 +217,55 @@ StmtPtr TypecheckVisitor::transformStaticForLoop(ForStmt *stmt) { E(Error::STATIC_RANGE_BOUNDS, iter, MAX_STATIC_ITER, ed); for (int i = 0; i < ed; i++) block->stmts.push_back(fn(var, N(i))); + } else if (iter && startswith(iter->value, "std.internal.static.fn_overloads")) { + if (auto fna = ctx->getFunctionArgs(iter->type)) { + auto [generics, args] = *fna; + auto typ = generics[0]->getClass(); + auto name = ctx->getStaticString(generics[1]); + seqassert(name, "bad static string"); + if (auto n = in(ctx->cache->classes[typ->name].methods, *name)) { + for (auto &method : ctx->cache->overloads[*n]) { + if (endswith(method.name, ":dispatch") || + !ctx->cache->functions[method.name].type) + continue; + if (method.age <= ctx->age) + block->stmts.push_back(fn(var, N(method.name))); + } + } + } else { + error("bad call to fn_overloads"); + } + } else if (iter && startswith(iter->value, "std.internal.static.fn_args")) { + auto &suiteVec = stmt->suite->getSuite()->stmts; + int validI = 0; + for (; validI < suiteVec.size(); validI++) { + if (auto a = suiteVec[validI]->getAssign()) + if (auto i = suiteVec[0]->getAssign()->rhs->getIndex()) + if (i->expr->isId(var)) + continue; + break; + } + if (validI != 2) + error("fn_args needs two variables in for loop"); + + if (auto fna = ctx->getFunctionArgs(iter->type)) { + auto [generics, args] = *fna; + auto typ = ctx->extractFunction(args[0]); + if (!typ) + error("fn_args needs a function"); + for (size_t i = 0; i < typ->ast->args.size(); i++) { + suiteVec[0]->getAssign()->rhs = N(i); + suiteVec[0]->getAssign()->type = + NT(NT("Static"), NT("int")); + suiteVec[1]->getAssign()->rhs = + N(ctx->cache->rev(typ->ast->args[i].name)); + suiteVec[1]->getAssign()->type = + NT(NT("Static"), NT("str")); + block->stmts.push_back(fn("", nullptr)); + } + } else { + error("bad call to fn_args"); + } } else { return nullptr; } @@ -226,6 +280,7 @@ StmtPtr TypecheckVisitor::transformStaticForLoop(ForStmt *stmt) { transform(N(N(N(loopVar), N(true)), N(N(loopVar), block))); ctx->blockLevel--; + // LOG("-> {} :: {}", getSrcInfo(), loop->toString(2)); return loop; } diff --git a/codon/parser/visitors/typecheck/typecheck.cpp b/codon/parser/visitors/typecheck/typecheck.cpp index 9380d884..48bd526b 100644 --- a/codon/parser/visitors/typecheck/typecheck.cpp +++ b/codon/parser/visitors/typecheck/typecheck.cpp @@ -214,6 +214,70 @@ types::FuncTypePtr TypecheckVisitor::findBestMethod( return m.empty() ? nullptr : m[0]; } +/// Check if a function can be called with the given arguments. +/// See @c reorderNamedArgs for details. +int TypecheckVisitor::canCall(const types::FuncTypePtr &fn, + const std::vector &args) { + std::vector> reordered; + auto score = ctx->reorderNamedArgs( + fn.get(), args, + [&](int s, int k, const std::vector> &slots, bool _) { + for (int si = 0; si < slots.size(); si++) { + if (fn->ast->args[si].status == Param::Generic) { + if (slots[si].empty()) + reordered.push_back({nullptr, 0}); + else + reordered.push_back({args[slots[si][0]].value->type, slots[si][0]}); + } else if (si == s || si == k || slots[si].size() != 1) { + // Ignore *args, *kwargs and default arguments + reordered.push_back({nullptr, 0}); + } else { + reordered.push_back({args[slots[si][0]].value->type, slots[si][0]}); + } + } + return 0; + }, + [](error::Error, const SrcInfo &, const std::string &) { return -1; }); + for (int ai = 0, mai = 0, gi = 0; score != -1 && ai < reordered.size(); ai++) { + auto expectTyp = fn->ast->args[ai].status == Param::Normal + ? fn->getArgTypes()[mai++] + : fn->funcGenerics[gi++].type; + auto [argType, argTypeIdx] = reordered[ai]; + if (!argType) + continue; + if (fn->ast->args[ai].status != Param::Normal) { + // Check if this is a good generic! + if (expectTyp && expectTyp->isStaticType()) { + if (!args[argTypeIdx].value->isStatic()) { + score = -1; + break; + } else { + argType = Type::makeStatic(ctx->cache, args[argTypeIdx].value); + } + } else { + /// TODO: check if these are real types or if traits are satisfied + continue; + } + } + try { + ExprPtr dummy = std::make_shared(""); + dummy->type = argType; + dummy->setDone(); + wrapExpr(dummy, expectTyp, fn); + types::Type::Unification undo; + if (dummy->type->unify(expectTyp.get(), &undo) >= 0) { + undo.undo(); + } else { + score = -1; + } + } catch (const exc::ParserException &) { + // Ignore failed wraps + score = -1; + } + } + return score; +} + /// Select the best method among the provided methods given the list of arguments. /// See @c reorderNamedArgs for details. std::vector @@ -226,63 +290,7 @@ TypecheckVisitor::findMatchingMethods(const types::ClassTypePtr &typ, if (!mi) continue; // avoid overloads that have not been seen yet auto method = ctx->instantiate(mi, typ)->getFunc(); - std::vector> reordered; - auto score = ctx->reorderNamedArgs( - method.get(), args, - [&](int s, int k, const std::vector> &slots, bool _) { - for (int si = 0; si < slots.size(); si++) { - if (method->ast->args[si].status == Param::Generic) { - if (slots[si].empty()) - reordered.push_back({nullptr, 0}); - else - reordered.push_back({args[slots[si][0]].value->type, slots[si][0]}); - } else if (si == s || si == k || slots[si].size() != 1) { - // Ignore *args, *kwargs and default arguments - reordered.push_back({nullptr, 0}); - } else { - reordered.push_back({args[slots[si][0]].value->type, slots[si][0]}); - } - } - return 0; - }, - [](error::Error, const SrcInfo &, const std::string &) { return -1; }); - for (int ai = 0, mai = 0, gi = 0; score != -1 && ai < reordered.size(); ai++) { - auto expectTyp = method->ast->args[ai].status == Param::Normal - ? method->getArgTypes()[mai++] - : method->funcGenerics[gi++].type; - auto [argType, argTypeIdx] = reordered[ai]; - if (!argType) - continue; - if (method->ast->args[ai].status != Param::Normal) { - // Check if this is a good generic! - if (expectTyp && expectTyp->isStaticType()) { - if (!args[argTypeIdx].value->isStatic()) { - score = -1; - break; - } else { - argType = Type::makeStatic(ctx->cache, args[argTypeIdx].value); - } - } else { - /// TODO: check if these are real types or if traits are satisfied - continue; - } - } - try { - ExprPtr dummy = std::make_shared(""); - dummy->type = argType; - dummy->setDone(); - wrapExpr(dummy, expectTyp, method); - types::Type::Unification undo; - if (dummy->type->unify(expectTyp.get(), &undo) >= 0) { - undo.undo(); - } else { - score = -1; - } - } catch (const exc::ParserException &) { - // Ignore failed wraps - score = -1; - } - } + int score = canCall(method, args); if (score != -1) { results.push_back(mi); } @@ -418,4 +426,32 @@ ExprPtr TypecheckVisitor::castToSuperClass(ExprPtr expr, ClassTypePtr superTyp, dist, typExpr)); } +/// Unpack a Tuple or KwTuple expression into (name, type) vector. +/// Name is empty when handling Tuple; otherwise it matches names of KwTuple. +std::shared_ptr>> +TypecheckVisitor::unpackTupleTypes(ExprPtr expr) { + auto ret = std::make_shared>>(); + if (auto tup = expr->origExpr->getTuple()) { + for (auto &a : tup->items) { + transform(a); + if (!a->getType()->getClass()) + return nullptr; + ret->push_back({"", a->getType()}); + } + } else if (auto kw = expr->origExpr->getCall()) { // origExpr? + auto kwCls = in(ctx->cache->classes, expr->getType()->getClass()->name); + seqassert(kwCls, "cannot find {}", expr->getType()->getClass()->name); + for (size_t i = 0; i < kw->args.size(); i++) { + auto &a = kw->args[i].value; + transform(a); + if (!a->getType()->getClass()) + return nullptr; + ret->push_back({kwCls->fields[i].name, a->getType()}); + } + } else { + return nullptr; + } + return ret; +} + } // namespace codon::ast diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index 8b07c196..c56ad2da 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -139,11 +139,13 @@ private: // Node typechecking rules ExprPtr transformStaticLen(CallExpr *expr); ExprPtr transformHasAttr(CallExpr *expr); ExprPtr transformGetAttr(CallExpr *expr); + ExprPtr transformSetAttr(CallExpr *expr); ExprPtr transformCompileError(CallExpr *expr); ExprPtr transformTupleFn(CallExpr *expr); ExprPtr transformTypeFn(CallExpr *expr); ExprPtr transformRealizedFn(CallExpr *expr); ExprPtr transformStaticPrintFn(CallExpr *expr); + ExprPtr transformInternalStaticFn(CallExpr *expr); std::vector getSuperTypes(const types::ClassTypePtr &cls); void addFunctionGenerics(const types::FuncType *t); std::string generatePartialStub(const std::vector &mask, types::FuncType *fn); @@ -213,6 +215,7 @@ private: types::FuncTypePtr findBestMethod(const types::ClassTypePtr &typ, const std::string &member, const std::vector> &args); + int canCall(const types::FuncTypePtr &, const std::vector &); std::vector findMatchingMethods(const types::ClassTypePtr &typ, const std::vector &methods, @@ -228,6 +231,10 @@ public: friend class Cache; friend class types::CallableTrait; friend class types::UnionType; + +private: // Helpers + std::shared_ptr>> + unpackTupleTypes(ExprPtr); }; } // namespace codon::ast diff --git a/stdlib/internal/core.codon b/stdlib/internal/core.codon index b6b729a6..49c23e9e 100644 --- a/stdlib/internal/core.codon +++ b/stdlib/internal/core.codon @@ -178,6 +178,9 @@ def hasattr(obj, attr: Static[str]): def getattr(obj, attr: Static[str]): pass +def setattr(obj, attr: Static[str], what): + pass + def tuple(iterable): pass diff --git a/stdlib/internal/python.codon b/stdlib/internal/python.codon index c3496532..b7b4ce9f 100644 --- a/stdlib/internal/python.codon +++ b/stdlib/internal/python.codon @@ -1515,66 +1515,239 @@ __pyenv__: Optional[pyobj] = None def _____(): __pyenv__ # make it global! -@extend -class __internal__: - def py_type(T: type) -> Ptr[byte]: - return Ptr[byte]() +import internal.static as _S +class _PyWrap: + def _wrap(args, T: type, F: Static[str], map): + for fn in _S.fn_overloads(T, F): + if _S.fn_can_call(fn, *args): + try: + return map(fn, args) + except PyError: + pass + raise PyError("cannot dispatch " + F) - def to_py(o) -> Ptr[byte]: - pytype = __internal__.py_type(type(o)) - obj = Ptr[PyWrapper[type(o)]](alloc_uncollectable(sizeof(type(o)))) - obj[0] = PyWrapper(PyObject(1, pytype), o) - return obj.as_byte() + def _wrap_unary(obj: cobj, T: type, F: Static[str]) -> cobj: + return _PyWrap._wrap( + (pyobj(obj), ), T=T, F=F, + map=lambda f, a: f(*a).__to_py__() + ) + def wrap_magic_abs(obj: cobj, T: type): + return _PyWrap._wrap_unary(obj, T, "__abs__") + def wrap_magic_pos(obj: cobj, T: type): + return _PyWrap._wrap_unary(obj, T, "__pos__") + def wrap_magic_neg(obj: cobj, T: type): + return _PyWrap._wrap_unary(obj, T, "__neg__") + def wrap_magic_invert(obj: cobj, T: type): + return _PyWrap._wrap_unary(obj, T, "__invert__") + def wrap_magic_int(obj: cobj, T: type): + return _PyWrap._wrap_unary(obj, T, "__int__") + def wrap_magic_float(obj: cobj, T: type): + return _PyWrap._wrap_unary(obj, T, "__float__") + def wrap_magic_index(obj: cobj, T: type): + return _PyWrap._wrap_unary(obj, T, "__index__") + def wrap_magic_repr(obj: cobj, T: type): + return _PyWrap._wrap_unary(obj, T, "__repr__") + def wrap_magic_str(obj: cobj, T: type): + return _PyWrap._wrap_unary(obj, T, "__str__") - def from_py(o: Ptr[byte], T: type) -> T: - obj = Ptr[PyWrapper[T]](o)[0] - pytype = __internal__.py_type(T) - if obj.head.pytype != pytype: - raise TypeError("Python object has incompatible type") - return obj.data + def _wrap_hash(obj: cobj, T: type, F: Static[str]) -> i64: + return _PyWrap._wrap( + (pyobj(obj), ), T=T, F=F, + map=lambda f, a: f(*a) + ) + def wrap_magic_len(obj: cobj, T: type): + return _PyWrap._wrap_hash(obj, T, "__len__") + def wrap_magic_hash(obj: cobj, T: type): + return _PyWrap._wrap_hash(obj, T, "__hash__") - def cmp_py(obj: cobj, other: cobj, op: i32, C: type, name: Static[str]) -> cobj: + def wrap_magic_bool(obj: cobj, T: type) -> i32: + return _PyWrap._wrap( + (pyobj(obj), ), T=T, F="__bool__", + map=lambda f, a: i32(f(*a)) + ) + + def wrap_magic_del(obj: cobj, T: type): + _PyWrap._wrap( + (pyobj(obj), ), T=T, F="__del__", + map=lambda f, a: f(*a) + ) + + def wrap_magic_contains(obj: cobj, arg: cobj, T: type) -> i32: + return _PyWrap._wrap( + (pyobj(obj), pyobj(arg)), T=T, F="__contains__", + map=lambda f, a: i32(f(*a)) + ) + + def wrap_magic_init(obj: cobj, _args: cobj, _kwds: cobj, T: type) -> i32: + args = pyobj(_args) + kwds = pyobj(_kwds) + for fn in _S.fn_overloads(T, "__init__"): + try: + ai = -1 + # TODO: default values do not work + a = tuple( + kwds[n] if n in kwds else args[(ai := ai + 1)] + for _, n in _S.fn_args(fn) + ) + a = (pyobj(obj), *a) + if _S.fn_can_call(fn, *a): + fn(*a) + return i32(0) + except PyError: + pass + return i32(-1) + + def wrap_magic_call(obj: cobj, _args: cobj, _kwds: cobj, T: type) -> cobj: + args = pyobj(_args) + kwds = pyobj(_kwds) + for fn in _S.fn_overloads(T, "__call__"): + try: + ai = -1 + a = tuple( # TODO: default values do not work + kwds[n] if n in kwds else args[(ai := ai + 1)] + for _, n in _S.fn_args(fn) + ) + a = (pyobj(obj), *a) + if _S.fn_can_call(fn, *a): + return fn(*a).__to_py__() + except PyError: + pass + raise PyError("cannot dispatch __call__") + + def _wrap_cmp(obj: cobj, other: cobj, T: type, F: Static[str]) -> cobj: + return _PyWrap._wrap( + (pyobj(obj), pyobj(other)), T=T, F=F, + map=lambda f, a: f(*a).__to_py__() + ) + def wrap_magic_lt(obj: cobj, other: cobj, T: type): + return _PyWrap._wrap_cmp(obj, other, T, "__lt__") + def wrap_magic_le(obj: cobj, other: cobj, T: type): + return _PyWrap._wrap_cmp(obj, other, T, "__le__") + def wrap_magic_eq(obj: cobj, other: cobj, T: type): + return _PyWrap._wrap_cmp(obj, other, T, "__eq__") + def wrap_magic_ne(obj: cobj, other: cobj, T: type): + return _PyWrap._wrap_cmp(obj, other, T, "__ne__") + def wrap_magic_gt(obj: cobj, other: cobj, T: type): + return _PyWrap._wrap_cmp(obj, other, T, "__gt__") + def wrap_magic_ge(obj: cobj, other: cobj, T: type): + return _PyWrap._wrap_cmp(obj, other, T, "__ge__") + def wrap_cmp(obj: cobj, other: cobj, op: i32, C: type) -> cobj: if hasattr(C, "__lt__") and op == 0i32: - return getattr(C, name)(obj, other) + return _PyWrap.wrap_magic_lt(obj, other, C) elif hasattr(C, "__le__") and op == 1i32: - return getattr(C, name)(obj, other) + return _PyWrap.wrap_magic_le(obj, other, C) elif hasattr(C, "__eq__") and op == 2i32: - return getattr(C, name)(obj, other) + return _PyWrap.wrap_magic_eq(obj, other, C) elif hasattr(C, "__ne__") and op == 3i32: - return getattr(C, name)(obj, other) + return _PyWrap.wrap_magic_ne(obj, other, C) elif hasattr(C, "__gt__") and op == 4i32: - return getattr(C, name)(obj, other) + return _PyWrap.wrap_magic_gt(obj, other, C) elif hasattr(C, "__ge__") and op == 5i32: - return getattr(C, name)(obj, other) + return _PyWrap.wrap_magic_ge(obj, other, C) else: Py_IncRef(Py_NotImplemented) return Py_NotImplemented -class _PyextIterWrap: - _gen: cobj - T: type + def wrap_magic_setitem(obj: cobj, idx: cobj, val: cobj, T: type) -> i32: + if val == cobj(): + try: + if hasattr(T, "__delitem__"): + T.__delitem__(pyobj(obj), pyobj(idx)) + return 0 + except PyError: + pass + return -1 + try: + _PyWrap._wrap( + (pyobj(obj), pyobj(idx), pyobj(val)), T=T, F="__setitem__", + map=lambda f, a: f(*a).__to_py__() + ) + return 0 + except PyError: + return -1 - def _init(obj: cobj, T: type) -> cobj: - return _PyextIterWrap(T.__from_py__(obj)).__to_py__() + class IterWrap: + _gen: cobj + T: type - @realize_without_self - def __init__(self, it: T): - self._gen = it.__iter__().__raw__() + def _init(obj: cobj, T: type) -> cobj: + return _PyWrap.IterWrap(T.__from_py__(obj)).__to_py__() - def _iter(obj: cobj) -> cobj: - T # need separate fn for each instantiation - return obj + @realize_without_self + def __init__(self, it: T): + self._gen = it.__iter__().__raw__() - def _iternext(self: cobj) -> cobj: - pt = _PyextIterWrap[T].__from_py__(self) - gt = type(T().__iter__())(pt._gen) - if gt.done(): - return cobj() - else: - return gt.next().__to_py__() + def _iter(obj: cobj) -> cobj: + T # need separate fn for each instantiation + return obj - def __to_py__(self): - return __internal__.to_py(self) + def _iternext(self: cobj) -> cobj: + pt = _PyWrap.IterWrap[T].__from_py__(self) + gt = type(T().__iter__())(pt._gen) + if gt.done(): + return cobj() + else: + return gt.next().__to_py__() - def __from_py__(obj: cobj): - return __internal__.from_py(obj, _PyextIterWrap[T]) + def __to_py__(self): + return _PyWrap.wrap_to_py(self) + + def __from_py__(obj: cobj): + return _PyWrap.wrap_from_py(obj, _PyWrap.IterWrap[T]) + + def wrap_magic_iter(obj: cobj, T: type) -> cobj: + return _PyWrap.IterWrap._init(obj, T) + + def wrap_single(obj: cobj, arg: cobj, T: type, F: Static[str], method: Static[int]): + a = (pyobj(obj), pyobj(arg)) if method else (pyobj(arg),) + return _PyWrap._wrap( + a, T=T, F=F, + map=lambda f, a: f(*a).__to_py__() + ) + + def wrap_multiple(obj: cobj, args: Ptr[cobj], nargs: i32, T: type, F: Static[str], method: Static[int]): + def _err(): + raise PyError("argument mismatch") + return pyobj() + + a = (pyobj(obj), ) if method else () + for fn in _S.fn_overloads(T, F): + try: + ai = -1 + an = ( + pyobj(args[i]) if i < nargs else _err() + for i, _ in _S.fn_args(fn) + ) + if len(an) != nargs: + _err() + if _S.fn_can_call(fn, (*a, *an)): + return fn(*a, *an).__to_py__() + except PyError: + pass + PyError("cannot dispatch " + F) + + def wrap_get(obj: cobj, closure: cobj, T: type, S: Static[str]): + return getattr(T.__from_py__(obj), S).__to_py__() + def wrap_set(obj: cobj, what: cobj, closure: cobj, T: type, S: Static[str]) -> i32: + try: + t = T.__from_py__(obj) + setattr(t, S, type(getattr(t, S)).__from_py__(what)) + return i32(0) + except PyError: + return i32(-1) + + def py_type(T: type) -> cobj: + return cobj() + + def wrap_to_py(o) -> cobj: + pytype = _PyWrap.py_type(type(o)) + obj = Ptr[PyWrapper[type(o)]](alloc_uncollectable(sizeof(type(o)))) + obj[0] = PyWrapper(PyObject(1, pytype), o) + return obj.as_byte() + + def wrap_from_py(o: cobj, T: type) -> T: + obj = Ptr[PyWrapper[T]](o)[0] + pytype = _PyWrap.py_type(T) + if obj.head.pytype != pytype: + raise TypeError("Python object has incompatible type") + return obj.data diff --git a/stdlib/internal/static.codon b/stdlib/internal/static.codon new file mode 100644 index 00000000..b54b1ba3 --- /dev/null +++ b/stdlib/internal/static.codon @@ -0,0 +1,18 @@ + +def fn_overloads(T: type, F: Static[str]): + pass + +def fn_args(F): # function: (i, name) + pass + +def fn_arg_has_type(F, i: Static[int]): + pass + +def fn_arg_get_type(F, i: Static[int]): + pass + +def fn_can_call(F, *args, **kwargs): + pass + +def class_args(T: type): + pass