mirror of
https://github.com/exaloop/codon.git
synced 2025-06-03 15:03:52 +08:00
Add static reflection methods (setattr; internal.static.*); refactor PyExt to python.codon; handle errors and kwargs in PyExt
This commit is contained in:
parent
d0461d572f
commit
12d21ff5eb
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,6 +17,7 @@
|
||||
build/
|
||||
build_*/
|
||||
install/
|
||||
install_*/
|
||||
extra/python/src/jit.cpp
|
||||
extra/jupyter/build/
|
||||
|
||||
|
@ -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<Param> params;
|
||||
std::vector<ExprPtr> 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<CallExpr>(N<DotExpr>(fna->args[ai].type->clone(), "__from_py__"), po);
|
||||
} else {
|
||||
return N<CallExpr>(N<IdExpr>("pyobj"), po);
|
||||
}
|
||||
};
|
||||
|
||||
StmtPtr ret = nullptr;
|
||||
ExprPtr retType = N<IdExpr>("cobj");
|
||||
const int *p = nullptr;
|
||||
LOG("[py] {}: {} => {} ({})", isMethod ? "method" : "classm", name, isMethod,
|
||||
rev(canonicalName));
|
||||
if (isMethod && in(std::set<std::string>{"__abs__", "__pos__", "__neg__",
|
||||
"__invert__", "__int__", "__float__",
|
||||
"__index__", "__repr__", "__str__"},
|
||||
rev(canonicalName))) {
|
||||
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")}};
|
||||
ret = N<ReturnStmt>(N<CallExpr>(N<DotExpr>(
|
||||
N<CallExpr>(N<IdExpr>(canonicalName),
|
||||
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0)}),
|
||||
"__to_py__")));
|
||||
} else if (isMethod &&
|
||||
in(std::set<std::string>{"__len__", "__hash__"}, rev(canonicalName))) {
|
||||
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")}};
|
||||
ret = N<ReturnStmt>(
|
||||
N<CallExpr>(N<IdExpr>(canonicalName),
|
||||
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0)}));
|
||||
retType = N<IdExpr>("i64");
|
||||
} else if (isMethod && rev(canonicalName) == "__bool__") {
|
||||
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")}};
|
||||
ret = N<ReturnStmt>(N<CallExpr>(
|
||||
N<IdExpr>("i32"),
|
||||
N<CallExpr>(N<IdExpr>(canonicalName),
|
||||
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0)})));
|
||||
retType = N<IdExpr>("i32");
|
||||
} else if (isMethod && rev(canonicalName) == "__del__") {
|
||||
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")}};
|
||||
ret = N<ExprStmt>(N<CallExpr>(
|
||||
N<IdExpr>("i32"),
|
||||
N<CallExpr>(N<IdExpr>(canonicalName),
|
||||
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0)})));
|
||||
retType = N<IdExpr>("i32");
|
||||
} else if (isMethod && rev(canonicalName) == "__contains__") {
|
||||
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("args"), N<IdExpr>("cobj")}};
|
||||
ret = N<ReturnStmt>(N<CallExpr>(
|
||||
N<IdExpr>("i32"),
|
||||
N<CallExpr>(N<IdExpr>(canonicalName),
|
||||
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0),
|
||||
wrapArg(N<IdExpr>(params[1].name), 1)})));
|
||||
retType = N<IdExpr>("i32");
|
||||
} else if (isMethod && rev(canonicalName) == "__init__") {
|
||||
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("args"), N<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("kwargs"), N<IdExpr>("cobj")}};
|
||||
std::vector<ExprPtr> tupTypes;
|
||||
for (size_t ai = 1; ai < fna->args.size(); ai++)
|
||||
tupTypes.push_back(fna->args[ai].type ? fna->args[ai].type->clone()
|
||||
: N<IdExpr>("pyobj"));
|
||||
ExprPtr tup = N<InstantiateExpr>(
|
||||
N<IdExpr>(fmt::format("Tuple.N{}", tupTypes.size())), tupTypes);
|
||||
tup = N<CallExpr>(N<DotExpr>(tup, "__from_py__"), N<IdExpr>(params[1].name));
|
||||
ret = N<SuiteStmt>(N<ExprStmt>(N<CallExpr>(N<IdExpr>(canonicalName),
|
||||
wrapArg(N<IdExpr>(params[0].name), 0),
|
||||
N<StarExpr>(tup))),
|
||||
N<ReturnStmt>(N<CallExpr>(N<IdExpr>("i32"), N<IntExpr>(0))));
|
||||
retType = N<IdExpr>("i32");
|
||||
} else if (isMethod && rev(canonicalName) == "__call__") {
|
||||
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("args"), N<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("kwargs"), N<IdExpr>("cobj")}};
|
||||
std::vector<ExprPtr> tupTypes;
|
||||
for (size_t ai = 1; ai < fna->args.size(); ai++)
|
||||
tupTypes.push_back(fna->args[ai].type ? fna->args[ai].type->clone()
|
||||
: N<IdExpr>("pyobj"));
|
||||
ExprPtr tup = N<InstantiateExpr>(
|
||||
N<IdExpr>(fmt::format("Tuple.N{}", tupTypes.size())), tupTypes);
|
||||
tup = N<CallExpr>(N<DotExpr>(tup, "__from_py__"), N<IdExpr>(params[1].name));
|
||||
ret = N<ReturnStmt>(N<CallExpr>(N<DotExpr>(
|
||||
N<CallExpr>(N<IdExpr>(canonicalName), wrapArg(N<IdExpr>(params[0].name), 0),
|
||||
N<StarExpr>(tup)),
|
||||
"__to_py__")));
|
||||
} else if (isMethod && in(std::set<std::string>{"__lt__", "__le__", "__eq__",
|
||||
"__ne__", "__gt__", "__ge__"},
|
||||
rev(canonicalName))) {
|
||||
params = std::vector<Param>{
|
||||
Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("other"), N<IdExpr>("cobj")}};
|
||||
ret = N<ReturnStmt>(N<CallExpr>(N<DotExpr>(
|
||||
N<CallExpr>(N<IdExpr>(canonicalName),
|
||||
std::vector<ExprPtr>{wrapArg(N<IdExpr>(params[0].name), 0),
|
||||
wrapArg(N<IdExpr>(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<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("index"), N<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("value"), N<IdExpr>("cobj")}};
|
||||
retType = N<IdExpr>("i32");
|
||||
ret = N<SuiteStmt>(std::vector<StmtPtr>{
|
||||
N<IfStmt>(
|
||||
N<BinaryExpr>(N<CallExpr>(N<IdExpr>("hasattr"), N<IdExpr>(className),
|
||||
N<StringExpr>("__delitem__")),
|
||||
"&&",
|
||||
N<BinaryExpr>(N<IdExpr>(params[2].name),
|
||||
"==", N<CallExpr>(N<IdExpr>("cobj")))),
|
||||
N<ExprStmt>(N<CallExpr>(N<DotExpr>(N<IdExpr>(className), "__delitem__"),
|
||||
wrapArg(N<IdExpr>(params[0].name), 0),
|
||||
wrapArg(N<IdExpr>(params[1].name), 1))),
|
||||
N<ExprStmt>(N<CallExpr>(N<IdExpr>(canonicalName),
|
||||
wrapArg(N<IdExpr>(params[0].name), 0),
|
||||
wrapArg(N<IdExpr>(params[1].name), 1),
|
||||
wrapArg(N<IdExpr>(params[2].name), 2)))),
|
||||
N<ReturnStmt>(N<CallExpr>(N<IdExpr>("i32"), N<IntExpr>(0)))});
|
||||
} else if (isMethod && rev(canonicalName) == "__iter__") {
|
||||
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")}};
|
||||
ret = N<ReturnStmt>(
|
||||
N<CallExpr>(N<IdExpr>("std.internal.python._PyextIterWrap._init:0"),
|
||||
N<IdExpr>(params[0].name), N<IdExpr>(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<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("args"), N<IdExpr>("cobj")}};
|
||||
if (fna->args.size() > 1 + isMethod) {
|
||||
params.back().type = N<InstantiateExpr>(N<IdExpr>("Ptr"), params.back().type);
|
||||
params.push_back(Param{sctx->generateCanonicalName("nargs"), N<IdExpr>("int")});
|
||||
}
|
||||
ExprPtr po = N<IdExpr>(params[0].name);
|
||||
if (!className.empty())
|
||||
po = N<CallExpr>(N<DotExpr>(N<IdExpr>(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<IndexExpr>(N<IdExpr>(params[1].name), N<IntExpr>(ai - isMethod));
|
||||
if (fna->args[ai].type) {
|
||||
po =
|
||||
N<CallExpr>(N<DotExpr>(fna->args[ai].type->clone(), "__from_py__"), po);
|
||||
} else {
|
||||
po = N<CallExpr>(N<IdExpr>("pyobj"), po);
|
||||
}
|
||||
args.push_back(po);
|
||||
}
|
||||
} else if (fna->args.size() == 1 + isMethod) {
|
||||
ExprPtr po = N<IdExpr>(params[1].name);
|
||||
if (fna->args[isMethod].type) {
|
||||
po = N<CallExpr>(N<DotExpr>(fna->args[isMethod].type->clone(), "__from_py__"),
|
||||
po);
|
||||
} else {
|
||||
po = N<CallExpr>(N<IdExpr>("pyobj"), po);
|
||||
}
|
||||
args.push_back(po);
|
||||
}
|
||||
ret = N<ReturnStmt>(N<CallExpr>(
|
||||
N<DotExpr>(N<CallExpr>(N<IdExpr>(canonicalName), args), "__to_py__")));
|
||||
}
|
||||
auto stubName = sctx->generateCanonicalName(fmt::format("_py.{}", name));
|
||||
auto node = N<FunctionStmt>(stubName, retType, params, N<SuiteStmt>(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<Overload>{{node->name, 0}};
|
||||
return f;
|
||||
};
|
||||
|
||||
if (!pyModule)
|
||||
pyModule = std::make_shared<ir::PyModule>();
|
||||
@ -443,6 +251,23 @@ void Cache::populatePythonModule() {
|
||||
int oldAge = typeCtx->age;
|
||||
typeCtx->age = 99999;
|
||||
|
||||
auto realizeIR = [&](const types::FuncTypePtr &fn,
|
||||
const std::vector<types::TypePtr> &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<ReturnStmt>(N<CallExpr>(
|
||||
N<IdExpr>("__internal__.to_py:0"), N<IdExpr>(fna->args[0].name)));
|
||||
N<IdExpr>(pyWrap + ".wrap_to_py:0"), N<IdExpr>(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<ReturnStmt>(N<CallExpr>(N<IdExpr>("__internal__.from_py:0"),
|
||||
N<ReturnStmt>(N<CallExpr>(N<IdExpr>(pyWrap + ".wrap_from_py:0"),
|
||||
N<IdExpr>(fna->args[0].name), N<IdExpr>(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<types::TypePtr>{tc};
|
||||
if (!isMagic) {
|
||||
generics.push_back(std::make_shared<types::StaticType>(this, n));
|
||||
generics.push_back(std::make_shared<types::StaticType>(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<std::string>{"__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<types::StaticType>(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<Param> params{
|
||||
Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("closure"), N<IdExpr>("cobj")}};
|
||||
ExprPtr retType = N<IdExpr>("cobj");
|
||||
StmtPtr ret = N<ReturnStmt>(N<CallExpr>(
|
||||
N<DotExpr>(N<DotExpr>(N<CallExpr>(N<DotExpr>(N<IdExpr>(cn), "__from_py__"),
|
||||
N<IdExpr>(params[0].name)),
|
||||
mn),
|
||||
"__to_py__")));
|
||||
auto gstub = sctx->generateCanonicalName(fmt::format("_py._get_{}", mn));
|
||||
auto gnode = N<FunctionStmt>(gstub, retType, params, N<SuiteStmt>(ret),
|
||||
Attr({Attr::ForceRealize}));
|
||||
functions[gstub].ast = gnode;
|
||||
|
||||
params = {Param{sctx->generateCanonicalName("self"), N<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("what"), N<IdExpr>("cobj")},
|
||||
Param{sctx->generateCanonicalName("closure"), N<IdExpr>("cobj")}};
|
||||
retType = N<IdExpr>("i32");
|
||||
ret = N<SuiteStmt>(
|
||||
N<AssignMemberStmt>(
|
||||
N<CallExpr>(N<DotExpr>(N<IdExpr>(cn), "__from_py__"),
|
||||
N<IdExpr>(params[0].name)),
|
||||
mn,
|
||||
N<CallExpr>(N<DotExpr>(N<IdExpr>(mt->realizedName()), "__from_py__"),
|
||||
N<IdExpr>(params[1].name))),
|
||||
N<ReturnStmt>(N<CallExpr>(N<IdExpr>("i32"), N<IntExpr>(0))));
|
||||
auto sstub = sctx->generateCanonicalName(fmt::format("_py._set_{}", mn));
|
||||
auto snode = N<FunctionStmt>(sstub, retType, params, N<SuiteStmt>(ret),
|
||||
Attr({Attr::ForceRealize}));
|
||||
functions[sstub].ast = snode;
|
||||
|
||||
auto tv = TypecheckVisitor(typeCtx);
|
||||
auto tnode = tv.transform(N<SuiteStmt>(gnode, snode));
|
||||
seqassertn(tnode, "blah");
|
||||
for (auto &stub : std::vector<std::string>{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<types::TypePtr>{
|
||||
tc, std::make_shared<types::StaticType>(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<std::string>{"_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<types::TypePtr>{
|
||||
typeCtx->forceFind(".toplevel")->type,
|
||||
std::make_shared<types::StaticType>(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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -86,6 +86,8 @@ SimplifyVisitor::apply(Cache *cache, const StmtPtr &node, const std::string &fil
|
||||
|
||||
// Prepare the code
|
||||
auto suite = N<SuiteStmt>();
|
||||
suite->stmts.push_back(N<ClassStmt>(".toplevel", std::vector<Param>{}, nullptr,
|
||||
std::vector<ExprPtr>{N<IdExpr>(Attr::Internal)}));
|
||||
for (auto &d : defines) {
|
||||
// Load compile-time defines (e.g., codon run -DFOO=1 ...)
|
||||
suite->stmts.push_back(
|
||||
|
@ -166,6 +166,12 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr,
|
||||
return transform(N<StringExpr>(expr->expr->type->prettyString()));
|
||||
return nullptr;
|
||||
}
|
||||
// Special case: expr.__is_static__
|
||||
if (expr->member == "__is_static__") {
|
||||
if (expr->expr->isDone())
|
||||
return transform(N<BoolExpr>(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))
|
||||
|
@ -543,6 +543,8 @@ std::pair<bool, ExprPtr> 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<bool, ExprPtr> 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<DotExpr>(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<StmtExpr>(N<AssignMemberStmt>(expr->args[0].value,
|
||||
staticTyp->evaluate().getString(),
|
||||
expr->args[1].value),
|
||||
N<NoneExpr>()));
|
||||
}
|
||||
|
||||
/// 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<CallExpr::Arg> callArgs;
|
||||
for (auto &a : *inargs) {
|
||||
callArgs.push_back({a.first, std::make_shared<NoneExpr>()}); // dummy expression
|
||||
callArgs.back().value->setType(a.second);
|
||||
}
|
||||
for (auto &a : *kwargs) {
|
||||
callArgs.push_back({a.first, std::make_shared<NoneExpr>()}); // dummy expression
|
||||
callArgs.back().value->setType(a.second);
|
||||
}
|
||||
return transform(N<BoolExpr>(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<BoolExpr>(*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<IdExpr>(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<ExprPtr> v;
|
||||
for (size_t i = 0; i < fn->ast->args.size(); i++)
|
||||
v.push_back(N<TupleExpr>(std::vector<ExprPtr>{
|
||||
N<IntExpr>(i), N<StringExpr>(ctx->cache->rev(fn->ast->args[i].name))}));
|
||||
return transform(N<TupleExpr>(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<ClassTypePtr> TypecheckVisitor::getSuperTypes(const ClassTypePtr &cls) {
|
||||
|
@ -305,4 +305,44 @@ std::string TypeContext::debugInfo() {
|
||||
getRealizationBase()->iteration, getSrcInfo());
|
||||
}
|
||||
|
||||
std::shared_ptr<std::pair<std::vector<types::TypePtr>, std::vector<types::TypePtr>>>
|
||||
TypeContext::getFunctionArgs(types::TypePtr t) {
|
||||
if (!t->getFunc())
|
||||
return nullptr;
|
||||
auto fn = t->getFunc();
|
||||
auto ret = std::make_shared<
|
||||
std::pair<std::vector<types::TypePtr>, std::vector<types::TypePtr>>>();
|
||||
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<std::string> TypeContext::getStaticString(types::TypePtr t) {
|
||||
if (auto s = t->getStatic()) {
|
||||
auto r = s->evaluate();
|
||||
if (r.type == StaticValue::STRING)
|
||||
return std::make_shared<std::string>(r.getString());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<int64_t> TypeContext::getStaticInt(types::TypePtr t) {
|
||||
if (auto s = t->getStatic()) {
|
||||
auto r = s->evaluate();
|
||||
if (r.type == StaticValue::INT)
|
||||
return std::make_shared<int64_t>(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
|
||||
|
@ -157,6 +157,13 @@ private:
|
||||
void dump(int pad);
|
||||
/// Pretty-print the current realization context.
|
||||
std::string debugInfo();
|
||||
|
||||
public:
|
||||
std::shared_ptr<std::pair<std::vector<types::TypePtr>, std::vector<types::TypePtr>>>
|
||||
getFunctionArgs(types::TypePtr t);
|
||||
std::shared_ptr<std::string> getStaticString(types::TypePtr t);
|
||||
std::shared_ptr<int64_t> getStaticInt(types::TypePtr t);
|
||||
types::FuncTypePtr extractFunction(types::TypePtr t);
|
||||
};
|
||||
|
||||
} // namespace codon::ast
|
||||
|
@ -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<IndexExpr>(
|
||||
N<IdExpr>("Static"),
|
||||
N<IdExpr>(expr->staticValue.type == StaticValue::INT ? "int" : "str"));
|
||||
auto brk = N<BreakStmt>();
|
||||
brk->setDone(); // Avoid transforming this one to continue
|
||||
// var [: Static] := expr; suite...
|
||||
auto loop = N<WhileStmt>(N<IdExpr>(loopVar),
|
||||
N<SuiteStmt>(N<AssignStmt>(N<IdExpr>(var), expr->clone(),
|
||||
staticInt ? t : nullptr),
|
||||
clone(stmt->suite), brk));
|
||||
auto loop = N<WhileStmt>(
|
||||
N<IdExpr>(loopVar),
|
||||
N<SuiteStmt>(
|
||||
expr ? N<AssignStmt>(N<IdExpr>(var), expr->clone(),
|
||||
expr->isStatic()
|
||||
? NT<IndexExpr>(N<IdExpr>("Static"),
|
||||
N<IdExpr>(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<IntExpr>(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<IdExpr>(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<IntExpr>(i);
|
||||
suiteVec[0]->getAssign()->type =
|
||||
NT<IndexExpr>(NT<IdExpr>("Static"), NT<IdExpr>("int"));
|
||||
suiteVec[1]->getAssign()->rhs =
|
||||
N<StringExpr>(ctx->cache->rev(typ->ast->args[i].name));
|
||||
suiteVec[1]->getAssign()->type =
|
||||
NT<IndexExpr>(NT<IdExpr>("Static"), NT<IdExpr>("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<SuiteStmt>(N<AssignStmt>(N<IdExpr>(loopVar), N<BoolExpr>(true)),
|
||||
N<WhileStmt>(N<IdExpr>(loopVar), block)));
|
||||
ctx->blockLevel--;
|
||||
// LOG("-> {} :: {}", getSrcInfo(), loop->toString(2));
|
||||
return loop;
|
||||
}
|
||||
|
||||
|
@ -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<CallExpr::Arg> &args) {
|
||||
std::vector<std::pair<types::TypePtr, size_t>> reordered;
|
||||
auto score = ctx->reorderNamedArgs(
|
||||
fn.get(), args,
|
||||
[&](int s, int k, const std::vector<std::vector<int>> &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<IdExpr>("");
|
||||
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<types::FuncTypePtr>
|
||||
@ -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<std::pair<types::TypePtr, size_t>> reordered;
|
||||
auto score = ctx->reorderNamedArgs(
|
||||
method.get(), args,
|
||||
[&](int s, int k, const std::vector<std::vector<int>> &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<IdExpr>("");
|
||||
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<std::vector<std::pair<std::string, types::TypePtr>>>
|
||||
TypecheckVisitor::unpackTupleTypes(ExprPtr expr) {
|
||||
auto ret = std::make_shared<std::vector<std::pair<std::string, types::TypePtr>>>();
|
||||
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
|
||||
|
@ -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<types::ClassTypePtr> getSuperTypes(const types::ClassTypePtr &cls);
|
||||
void addFunctionGenerics(const types::FuncType *t);
|
||||
std::string generatePartialStub(const std::vector<char> &mask, types::FuncType *fn);
|
||||
@ -213,6 +215,7 @@ private:
|
||||
types::FuncTypePtr
|
||||
findBestMethod(const types::ClassTypePtr &typ, const std::string &member,
|
||||
const std::vector<std::pair<std::string, types::TypePtr>> &args);
|
||||
int canCall(const types::FuncTypePtr &, const std::vector<CallExpr::Arg> &);
|
||||
std::vector<types::FuncTypePtr>
|
||||
findMatchingMethods(const types::ClassTypePtr &typ,
|
||||
const std::vector<types::FuncTypePtr> &methods,
|
||||
@ -228,6 +231,10 @@ public:
|
||||
friend class Cache;
|
||||
friend class types::CallableTrait;
|
||||
friend class types::UnionType;
|
||||
|
||||
private: // Helpers
|
||||
std::shared_ptr<std::vector<std::pair<std::string, types::TypePtr>>>
|
||||
unpackTupleTypes(ExprPtr);
|
||||
};
|
||||
|
||||
} // namespace codon::ast
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
18
stdlib/internal/static.codon
Normal file
18
stdlib/internal/static.codon
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user