From adac82bdefa1adf0922a3cc0c50379d016951854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 10 Dec 2021 12:28:19 -0800 Subject: [PATCH 01/61] Backport seq-lang/seq@develop fixes --- codon/parser/ast/types.cpp | 12 ++-- codon/parser/peg/grammar.peg | 5 +- .../visitors/simplify/simplify_expr.cpp | 2 +- .../visitors/simplify/simplify_stmt.cpp | 4 +- .../visitors/typecheck/typecheck_ctx.cpp | 3 +- .../parser/visitors/typecheck/typecheck_ctx.h | 5 ++ .../visitors/typecheck/typecheck_expr.cpp | 66 +++++++++++++------ .../visitors/typecheck/typecheck_stmt.cpp | 20 ++++-- docs/sphinx/primer.rst | 7 +- stdlib/algorithms/pdqsort.codon | 12 ++-- stdlib/algorithms/qsort.codon | 12 ++-- stdlib/collections.codon | 3 + stdlib/heapq.codon | 20 +++--- stdlib/internal/gc.codon | 6 ++ stdlib/internal/python.codon | 10 +-- stdlib/internal/str.codon | 6 -- stdlib/internal/types/array.codon | 7 ++ stdlib/internal/types/bool.codon | 2 + stdlib/internal/types/byte.codon | 2 + stdlib/internal/types/collections/dict.codon | 3 + stdlib/internal/types/collections/list.codon | 3 + stdlib/internal/types/collections/set.codon | 3 + stdlib/internal/types/float.codon | 2 + stdlib/internal/types/int.codon | 2 + stdlib/internal/types/intn.codon | 4 ++ stdlib/internal/types/str.codon | 5 +- stdlib/itertools.codon | 4 +- test/parser/simplify_expr.codon | 9 ++- test/parser/simplify_stmt.codon | 20 +++++- test/parser/typecheck_expr.codon | 1 + test/parser/typecheck_stmt.codon | 16 +++++ test/parser/types.codon | 35 ++++++++-- 32 files changed, 227 insertions(+), 84 deletions(-) diff --git a/codon/parser/ast/types.cpp b/codon/parser/ast/types.cpp index e932851b..a08abed4 100644 --- a/codon/parser/ast/types.cpp +++ b/codon/parser/ast/types.cpp @@ -562,10 +562,12 @@ std::string PartialType::debugString(bool debug) const { std::vector as; int i, gi; for (i = 0, gi = 0; i < known.size(); i++) - if (!known[i]) - as.emplace_back("..."); - else - as.emplace_back(gs[gi++]); + if (!func->ast->args[i].generic) { + if (!known[i]) + as.emplace_back("..."); + else + as.emplace_back(gs[gi++]); + } return fmt::format("{}[{}]", !debug ? func->ast->name : func->debugString(debug), join(as, ",")); } @@ -756,7 +758,7 @@ int CallableTrait::unify(Type *typ, Unification *us) { if (args[0]->unify(pt->func->args[0].get(), us) == -1) return -1; for (int pi = 0, gi = 1; pi < pt->known.size(); pi++) - if (!pt->known[pi]) + if (!pt->known[pi] && !pt->func->ast->args[pi].generic) if (args[gi++]->unify(pt->func->args[pi + 1].get(), us) == -1) return -1; return 1; diff --git a/codon/parser/peg/grammar.peg b/codon/parser/peg/grammar.peg index 0f8cf6e2..ad292a2b 100644 --- a/codon/parser/peg/grammar.peg +++ b/codon/parser/peg/grammar.peg @@ -555,9 +555,8 @@ atom <- ast(LOC, ac(V0)), ast(LOC, ac(V1)) ); } - / FLOAT { - // Right now suffixes are _not_ supported - return ast(LOC, ac(V0), ""); + / FLOAT NAME? { + return ast(LOC, ac(V0), VS.size() > 1 ? ac(V1) : ""); } / INT NAME? { return ast(LOC, ac(V0), VS.size() > 1 ? ac(V1) : ""); diff --git a/codon/parser/visitors/simplify/simplify_expr.cpp b/codon/parser/visitors/simplify/simplify_expr.cpp index a7242651..68a998e6 100644 --- a/codon/parser/visitors/simplify/simplify_expr.cpp +++ b/codon/parser/visitors/simplify/simplify_expr.cpp @@ -728,7 +728,7 @@ ExprPtr SimplifyVisitor::transformFloat(const std::string &value, return expr; } } catch (std::out_of_range &) { - error("integer {} out of range", value); + error("float {} out of range", value); } /// Custom suffix sfx: use float.__suffix_sfx__(str) call. return transform(N(N("float", format("__suffix_{}__", suffix)), diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 53edd73c..a3158edc 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -786,8 +786,8 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { if (bcName.empty() || !in(ctx->cache->classes, bcName)) error(baseClass.get(), "invalid base class"); baseASTs.push_back(ctx->cache->classes[bcName].ast.get()); - if (baseASTs.back()->attributes.has(Attr::Tuple) != isRecord) - error("tuples cannot inherit reference classes (and vice versa)"); + if (!isRecord && baseASTs.back()->attributes.has(Attr::Tuple)) + error("reference classes cannot inherit by-value classes"); if (baseASTs.back()->attributes.has(Attr::Internal)) error("cannot inherit internal types"); int si = 0; diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.cpp b/codon/parser/visitors/typecheck/typecheck_ctx.cpp index ef2c0971..31297307 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.cpp +++ b/codon/parser/visitors/typecheck/typecheck_ctx.cpp @@ -16,7 +16,8 @@ namespace ast { TypeContext::TypeContext(Cache *cache) : Context(""), cache(move(cache)), typecheckLevel(0), - allowActivation(true), age(0), realizationDepth(0) { + allowActivation(true), age(0), realizationDepth(0), blockLevel(0), + returnEarly(false) { stack.push_front(std::vector()); bases.push_back({"", nullptr, nullptr}); } diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.h b/codon/parser/visitors/typecheck/typecheck_ctx.h index b0f6a597..9713ae64 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.h +++ b/codon/parser/visitors/typecheck/typecheck_ctx.h @@ -67,6 +67,11 @@ struct TypeContext : public Context { /// (e.g. class A: def __init__(a: A = A())). std::set defaultCallDepth; + /// Number of nested blocks (0 for toplevel) + int blockLevel; + /// True if early return is sounds (anything afterwards won't be typechecked) + bool returnEarly; + public: explicit TypeContext(Cache *cache); diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index a0c46863..8590aa1f 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -709,10 +709,20 @@ ExprPtr TypecheckVisitor::transformBinary(BinaryExpr *expr, bool isAtomic, if (method) swap(expr->lexpr, expr->rexpr); } - if (!method) - error("cannot find magic '{}' in {}", magic, lt->toString()); - - return transform(N(N(method->ast->name), expr->lexpr, expr->rexpr)); + if (method) { + return transform( + N(N(method->ast->name), expr->lexpr, expr->rexpr)); + } else if (lt->is("pyobj")) { + return transform(N(N(N(expr->lexpr, "_getattr"), + N(format("__{}__", magic))), + expr->rexpr)); + } else if (rt->is("pyobj")) { + return transform(N(N(N(expr->rexpr, "_getattr"), + N(format("__r{}__", magic))), + expr->lexpr)); + } + error("cannot find magic '{}' in {}", magic, lt->toString()); + return nullptr; } ExprPtr TypecheckVisitor::transformStaticTupleIndex(ClassType *tuple, ExprPtr &expr, @@ -1046,8 +1056,11 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in // c = t.__new__(); c.__init__(args); c ExprPtr var = N(ctx->cache->getTemporaryVar("v")); return transform(N( - N(clone(var), N(N(expr->expr, "__new__"))), - N(N(N(clone(var), "__init__"), expr->args)), + N( + N(clone(var), N(N(expr->expr, "__new__"))), + N(N(N("std.internal.gc.register_finalizer"), + clone(var))), + N(N(N(clone(var), "__init__"), expr->args))), clone(var))); } } else if (auto pc = callee->getPartial()) { @@ -1055,8 +1068,13 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in expr->expr = transform(N(N(clone(var), expr->expr), N(pc->func->ast->name))); calleeFn = expr->expr->type->getFunc(); - for (int i = 0, j = 0; i < calleeFn->ast->args.size(); i++) - known.push_back(calleeFn->ast->args[i].generic ? 0 : pc->known[j++]); + for (int i = 0, j = 0; i < pc->known.size(); i++) + if (pc->func->ast->args[i].generic) { + if (pc->known[i]) + unify(calleeFn->funcGenerics[j].type, pc->func->funcGenerics[j].type); + j++; + } + known = pc->known; seqassert(calleeFn, "not a function: {}", expr->expr->type->toString()); } else if (!callee->getFunc()) { // Case 3: callee is not a named function. Route it through a __call__ method. @@ -1070,6 +1088,7 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in int typeArgCount = 0; bool isPartial = false; int ellipsisStage = -1; + auto newMask = vector(calleeFn->ast->args.size(), 1); if (expr->ordered) args = expr->args; else @@ -1085,6 +1104,7 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in typeArgs.push_back(slots[si].empty() ? nullptr : expr->args[slots[si][0]].value); typeArgCount += typeArgs.back() != nullptr; + newMask[si] = slots[si].empty() ? 0 : 1; } else if (si == starArgIndex && !(partial && slots[si].empty())) { std::vector extra; for (auto &e : slots[si]) { @@ -1115,6 +1135,7 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in args.push_back({"", ex}); } else if (partial) { args.push_back({"", transform(N())}); + newMask[si] = 0; } else { auto es = calleeFn->ast->args[si].deflt->toString(); if (in(ctx->defaultCallDepth, es)) @@ -1207,7 +1228,7 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in // Handle default generics (calleeFn.g. foo[S, T=int]) only if all arguments were // unified. // TODO: remove once the proper partial handling of overloaded functions land - if (unificationsDone) + if (unificationsDone) { for (int i = 0, j = 0; i < calleeFn->ast->args.size(); i++) if (calleeFn->ast->args[i].generic) { if (calleeFn->ast->args[i].deflt && @@ -1222,6 +1243,7 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in } j++; } + } for (int si = 0; si < replacements.size(); si++) if (replacements[si]) { if (replacements[si]->getFunc()) @@ -1238,13 +1260,7 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in expr->done &= expr->expr->done; // Emit the final call. - std::vector newMask; - if (isPartial) - newMask = std::vector(calleeFn->args.size() - 1, 1); - for (int si = 0; si < calleeFn->args.size() - 1; si++) - if (args[si].value->getEllipsis() && !args[si].value->getEllipsis()->isPipeArg) - newMask[si] = 0; - if (!newMask.empty()) { + if (isPartial) { // Case 1: partial call. // Transform calleeFn(args...) to Partial.N.(args...). auto partialTypeName = generatePartialStub(newMask, calleeFn->getFunc().get()); @@ -1507,14 +1523,15 @@ std::string TypecheckVisitor::generateFunctionStub(int n) { std::string TypecheckVisitor::generatePartialStub(const std::vector &mask, types::FuncType *fn) { std::string strMask(mask.size(), '1'); + int tupleSize = 0; for (int i = 0; i < mask.size(); i++) if (!mask[i]) strMask[i] = '0'; - auto typeName = format(TYPE_PARTIAL "{}", strMask); - if (!ctx->find(typeName)) { - auto tupleSize = std::count_if(mask.begin(), mask.end(), [](char c) { return c; }); + else if (!fn->ast->args[i].generic) + tupleSize++; + auto typeName = format(TYPE_PARTIAL "{}.{}", strMask, fn->ast->name); + if (!ctx->find(typeName)) generateTupleStub(tupleSize, typeName, {}, false); - } return typeName; } @@ -1569,7 +1586,14 @@ void TypecheckVisitor::generateFnCall(int n) { ExprPtr TypecheckVisitor::partializeFunction(ExprPtr expr) { auto fn = expr->getType()->getFunc(); seqassert(fn, "not a function: {}", expr->getType()->toString()); - std::vector mask(fn->args.size() - 1, 0); + std::vector mask(fn->ast->args.size(), 0); + for (int i = 0, j = 0; i < fn->ast->args.size(); i++) + if (fn->ast->args[i].generic) { + // TODO: better detection of user-provided args...? + if (!fn->funcGenerics[j].type->getUnbound()) + mask[i] = 1; + j++; + } auto partialTypeName = generatePartialStub(mask, fn.get()); deactivateUnbounds(fn.get()); std::string var = ctx->cache->getTemporaryVar("partial"); diff --git a/codon/parser/visitors/typecheck/typecheck_stmt.cpp b/codon/parser/visitors/typecheck/typecheck_stmt.cpp index 307377b4..432857c0 100644 --- a/codon/parser/visitors/typecheck/typecheck_stmt.cpp +++ b/codon/parser/visitors/typecheck/typecheck_stmt.cpp @@ -49,11 +49,16 @@ void TypecheckVisitor::defaultVisit(Stmt *s) { void TypecheckVisitor::visit(SuiteStmt *stmt) { std::vector stmts; stmt->done = true; - for (auto &s : stmt->stmts) + ctx->blockLevel += int(stmt->ownBlock); + for (auto &s : stmt->stmts) { + if (ctx->returnEarly) + break; if (auto t = transform(s)) { stmts.push_back(t); stmt->done &= stmts.back()->done; } + } + ctx->blockLevel -= int(stmt->ownBlock); stmt->stmts = stmts; } @@ -205,16 +210,18 @@ void TypecheckVisitor::visit(ReturnStmt *stmt) { stmt->expr = transform(stmt->expr); if (stmt->expr) { auto &base = ctx->bases.back(); - wrapExpr(stmt->expr, base.returnType, nullptr); + if (!base.returnType->getUnbound()) + wrapExpr(stmt->expr, base.returnType, nullptr); if (stmt->expr->getType()->getFunc() && !(base.returnType->getClass() && startswith(base.returnType->getClass()->name, TYPE_FUNCTION))) stmt->expr = partializeFunction(stmt->expr); unify(base.returnType, stmt->expr->type); - auto retTyp = stmt->expr->getType()->getClass(); stmt->done = stmt->expr->done; } else { + if (ctx->blockLevel == 1) + ctx->returnEarly = true; stmt->done = true; } } @@ -307,7 +314,12 @@ void TypecheckVisitor::visit(IfStmt *stmt) { isTrue = !stmt->cond->staticValue.getString().empty(); else isTrue = stmt->cond->staticValue.getInt(); - resultStmt = transform(isTrue ? stmt->ifSuite : stmt->elseSuite); + resultStmt = isTrue ? stmt->ifSuite : stmt->elseSuite; + bool isOwn = // these blocks will not be a real owning blocks after inlining + resultStmt && resultStmt->getSuite() && resultStmt->getSuite()->ownBlock; + ctx->blockLevel -= isOwn; + resultStmt = transform(resultStmt); + ctx->blockLevel += isOwn; if (!resultStmt) resultStmt = transform(N()); return; diff --git a/docs/sphinx/primer.rst b/docs/sphinx/primer.rst index 637ebdd6..bd3736c2 100644 --- a/docs/sphinx/primer.rst +++ b/docs/sphinx/primer.rst @@ -811,7 +811,12 @@ You can also add methods to types: Type extensions ~~~~~~~~~~~~~~~ -Suppose you have a class that lacks a method or an operator that might be really useful. Codon provides an ``@extend`` annotation that allows you to add and modify methods of various types at compile time, including built-in types like ``int`` or ``str``. This allows much of the functionality of built-in types to be implemented in Codon as type extensions in the standard library. +Suppose you have a class that lacks a method or an operator that might be really useful. + +Codon provides an ``@extend`` annotation that allows programmers to add and modify +methods of various types at compile time, including built-in types like ``int`` or ``str``. +This actually allows much of the functionality of built-in types to be implemented in +Codon as type extensions in the standard library. .. code:: python diff --git a/stdlib/algorithms/pdqsort.codon b/stdlib/algorithms/pdqsort.codon index 9607893e..0c3c5756 100644 --- a/stdlib/algorithms/pdqsort.codon +++ b/stdlib/algorithms/pdqsort.codon @@ -35,7 +35,7 @@ def _partial_insertion_sort[S,T](arr: Array[T], begin: int, end: int, keyf: Call arr[sift] = arr[sift_1] sift -= 1 sift_1 -= 1 - if sift == begin or keyf(tmp) >= keyf(arr[sift_1]): + if sift == begin or not keyf(tmp) < keyf(arr[sift_1]): break arr[sift] = tmp @@ -52,7 +52,7 @@ def _partition_left[S,T](arr: Array[T], begin: int, end: int, keyf: Callable[[T] while True: last -= 1 - if keyf(pivot) >= keyf(arr[last]): + if not keyf(pivot) < keyf(arr[last]): break if (last + 1 == end): @@ -71,7 +71,7 @@ def _partition_left[S,T](arr: Array[T], begin: int, end: int, keyf: Callable[[T] arr[first], arr[last] = arr[last], arr[first] while True: last -= 1 - if keyf(pivot) >= keyf(arr[last]): + if not keyf(pivot) < keyf(arr[last]): break while True: first += 1 @@ -91,7 +91,7 @@ def _partition_right[S,T](arr: Array[T], begin: int, end: int, keyf: Callable[[T while True: first += 1 - if keyf(arr[first]) >= keyf(pivot): + if not keyf(arr[first]) < keyf(pivot): break if first - 1 == begin: @@ -115,7 +115,7 @@ def _partition_right[S,T](arr: Array[T], begin: int, end: int, keyf: Callable[[T while True: first += 1 - if keyf(arr[first]) >= keyf(pivot): + if not keyf(arr[first]) < keyf(pivot): break while True: @@ -155,7 +155,7 @@ def _pdq_sort[S,T](arr: Array[T], begin: int, end: int, keyf: Callable[[T], S], else: _sort3(arr, begin + size_2, begin, end - 1, keyf) - if not leftmost and keyf(arr[begin - 1]) >= keyf(arr[begin]): + if not leftmost and not keyf(arr[begin - 1]) < keyf(arr[begin]): begin = _partition_left(arr, begin, end, keyf) + 1 continue diff --git a/stdlib/algorithms/qsort.codon b/stdlib/algorithms/qsort.codon index 26c0a0b2..69ccf57f 100644 --- a/stdlib/algorithms/qsort.codon +++ b/stdlib/algorithms/qsort.codon @@ -1,6 +1,8 @@ def _med3[S,T](a: int, b: int, c: int, d: Array[T], k: Callable[[T], S]): - return ((b if (k(d[b]) < k(d[c])) else (c if k(d[a]) < k(d[c]) else a)) - if (k(d[a]) < k(d[b])) else (b if (k(d[b]) > k(d[c])) else (c if k(d[a]) > k(d[c]) else a))) + if k(d[a]) < k(d[b]): + return b if (k(d[b]) < k(d[c])) else (c if k(d[a]) < k(d[c]) else a) + else: + return b if not (k(d[b]) < k(d[c]) or k(d[b]) == k(d[c])) else (c if not (k(d[a]) < k(d[c]) or k(d[a]) == k(d[c])) else a) def _swap[T](i: int, j: int, a: Array[T]): a[i], a[j] = a[j], a[i] @@ -17,7 +19,7 @@ def _qsort[S,T](arr: Array[T], frm: int, cnt: int, keyf: Callable[[T], S]): i = frm + 1 while i < frm + cnt: j = i - while j > frm and keyf(arr[j - 1]) > keyf(arr[j]): + while j > frm and not (keyf(arr[j - 1]) < keyf(arr[j]) or keyf(arr[j - 1]) == keyf(arr[j])): _swap(j, j - 1, arr) j -= 1 i += 1 @@ -41,13 +43,13 @@ def _qsort[S,T](arr: Array[T], frm: int, cnt: int, keyf: Callable[[T], S]): d = c while True: - while b <= c and keyf(arr[b]) <= keyf(arr[frm]): + while b <= c and (keyf(arr[b]) < keyf(arr[frm]) or keyf(arr[b]) == keyf(arr[frm])): if keyf(arr[b]) == keyf(arr[frm]): _swap(a, b, arr) a += 1 b += 1 - while c >= b and keyf(arr[c]) >= keyf(arr[frm]): + while c >= b and not keyf(arr[c]) < keyf(arr[frm]): if keyf(arr[c]) == keyf(arr[frm]): _swap(c, d, arr) d -= 1 diff --git a/stdlib/collections.codon b/stdlib/collections.codon index 50097155..fc18a627 100644 --- a/stdlib/collections.codon +++ b/stdlib/collections.codon @@ -104,6 +104,9 @@ class deque[T]: return True return False + def __deepcopy__(self): + return deque(i.__deepcopy__() for i in self) + def __copy__(self): return deque[T](copy(self._arr), self._head, self._tail, self._maxlen) diff --git a/stdlib/heapq.codon b/stdlib/heapq.codon index 25e40d9b..6d6216d0 100644 --- a/stdlib/heapq.codon +++ b/stdlib/heapq.codon @@ -148,7 +148,7 @@ def nsmallest[T](n: int, iterable: Generator[T], key = Optional[int]()): Equivalent to: sorted(iterable, key=key)[:n] """ if n == 1: - v = List[T](1) + v = List(1) for a in iterable: if not v: v.append(a) @@ -165,7 +165,7 @@ def nsmallest[T](n: int, iterable: Generator[T], key = Optional[int]()): it = iter(iterable) # put the range(n) first so that zip() doesn't # consume one too many elements from the iterator - result = List[Tuple[T,int]](n) + result = List(n) done = False for i in range(n): if it.done(): @@ -174,7 +174,7 @@ def nsmallest[T](n: int, iterable: Generator[T], key = Optional[int]()): result.append((it.next(), i)) if not result: it.destroy() - return List[T](0) + return [] _heapify_max(result) top = result[0][0] order = n @@ -191,7 +191,7 @@ def nsmallest[T](n: int, iterable: Generator[T], key = Optional[int]()): else: # General case, slowest method it = iter(iterable) - result = List[Tuple[type(key(T())),int,T]](n) + result = List(n) done = False for i in range(n): if it.done(): @@ -201,7 +201,7 @@ def nsmallest[T](n: int, iterable: Generator[T], key = Optional[int]()): result.append((key(elem), i, elem)) if not result: it.destroy() - return List[T](0) + return [] _heapify_max(result) top = result[0][0] order = n @@ -222,7 +222,7 @@ def nlargest[T](n: int, iterable: Generator[T], key = Optional[int]()): Equivalent to: sorted(iterable, key=key, reverse=True)[:n] """ if n == 1: - v = List[T](1) + v = List(1) for a in iterable: if not v: v.append(a) @@ -237,7 +237,7 @@ def nlargest[T](n: int, iterable: Generator[T], key = Optional[int]()): # When key is none, use simpler decoration if isinstance(key, Optional): it = iter(iterable) - result = List[Tuple[T,int]](n) + result = List(n) done = False for i in range(0, -n, -1): if it.done(): @@ -246,7 +246,7 @@ def nlargest[T](n: int, iterable: Generator[T], key = Optional[int]()): result.append((it.next(), i)) if not result: it.destroy() - return List[T](0) + return [] heapify(result) top = result[0][0] order = -n @@ -263,7 +263,7 @@ def nlargest[T](n: int, iterable: Generator[T], key = Optional[int]()): else: # General case, slowest method it = iter(iterable) - result = List[Tuple[type(key(T())),int,T]](n) + result = List(n) done = False for i in range(0, -n, -1): if it.done(): @@ -272,7 +272,7 @@ def nlargest[T](n: int, iterable: Generator[T], key = Optional[int]()): elem = it.next() result.append((key(elem), i, elem)) if not result: - return List[T](0) + return [] heapify(result) top = result[0][0] order = -n diff --git a/stdlib/internal/gc.codon b/stdlib/internal/gc.codon index ec2fe228..ef752107 100644 --- a/stdlib/internal/gc.codon +++ b/stdlib/internal/gc.codon @@ -49,3 +49,9 @@ def clear_roots(): def exclude_static_roots(start: cobj, end: cobj): seq_gc_exclude_static_roots(start, end) + +def register_finalizer(p): + if hasattr(p, '__del__'): + def f(x: cobj, data: cobj, T: type): + Ptr[T](__ptr__(x).as_byte())[0].__del__() + seq_register_finalizer(p.__raw__(), f(T=type(p), ...).__raw__()) diff --git a/stdlib/internal/python.codon b/stdlib/internal/python.codon index c5614649..3301f318 100644 --- a/stdlib/internal/python.codon +++ b/stdlib/internal/python.codon @@ -132,12 +132,6 @@ class pyobj: def _getattr(self, name: str): return pyobj.exc_wrap(pyobj(PyObject_GetAttrString(self.p, name.c_str()))) - def __getitem__(self, t): - return self._getattr("__getitem__")(t) - - def __add__(self, t): - return self._getattr("__add__")(t) - def __setitem__(self, name: str, val: pyobj): return pyobj.exc_wrap(pyobj(PyObject_SetAttrString(self.p, name.c_str(), val.p))) @@ -232,7 +226,7 @@ class pyobj: ensure_initialized() PyRun_SimpleString(code.c_str()) - def get[T](self) -> T: + def get(self, T: type) -> T: return T.__from_py__(self) def none(): @@ -244,7 +238,7 @@ def none(): def py(x) -> pyobj: return x.__to_py__() -def get[T](x: pyobj) -> T: +def get(x: pyobj, T: type) -> T: return T.__from_py__(x) @extend diff --git a/stdlib/internal/str.codon b/stdlib/internal/str.codon index 2116ab9d..67a8dd67 100644 --- a/stdlib/internal/str.codon +++ b/stdlib/internal/str.codon @@ -106,12 +106,6 @@ class str: n += self.len return str(p, total) - def __copy__(self): - n = len(self) - p = cobj(n) - str.memcpy(p, self.ptr, n) - return str(p, n) - def _cmp(self, other: str): n = min(self.len, other.len) i = 0 diff --git a/stdlib/internal/types/array.codon b/stdlib/internal/types/array.codon index e052f5ce..834db41b 100644 --- a/stdlib/internal/types/array.codon +++ b/stdlib/internal/types/array.codon @@ -10,6 +10,13 @@ class Array: p = Ptr[T](self.len) str.memcpy(p.as_byte(), self.ptr.as_byte(), self.len * sizeof(T)) return (self.len, p) + def __deepcopy__(self) -> Array[T]: + p = Ptr[T](self.len) + i = 0 + while i < self.len: + p[i] = self.ptr[i].__deepcopy__() + i += 1 + return (self.len, p) def __len__(self) -> int: return self.len def __bool__(self) -> bool: diff --git a/stdlib/internal/types/bool.codon b/stdlib/internal/types/bool.codon index 81ba8c5f..de66f660 100644 --- a/stdlib/internal/types/bool.codon +++ b/stdlib/internal/types/bool.codon @@ -10,6 +10,8 @@ class bool: return "True" if self else "False" def __copy__(self) -> bool: return self + def __deepcopy__(self) -> bool: + return self def __bool__(self) -> bool: return self def __hash__(self): diff --git a/stdlib/internal/types/byte.codon b/stdlib/internal/types/byte.codon index fa22afc9..764c056b 100644 --- a/stdlib/internal/types/byte.codon +++ b/stdlib/internal/types/byte.codon @@ -21,6 +21,8 @@ class byte: ret i8 %0 def __copy__(self) -> byte: return self + def __deepcopy__(self) -> byte: + return self @pure @llvm def __bool__(self) -> bool: diff --git a/stdlib/internal/types/collections/dict.codon b/stdlib/internal/types/collections/dict.codon index fc7a177b..6a742fb0 100644 --- a/stdlib/internal/types/collections/dict.codon +++ b/stdlib/internal/types/collections/dict.codon @@ -113,6 +113,9 @@ class Dict[K,V]: str.memcpy(vals_copy.as_byte(), self._vals.as_byte(), n * gc.sizeof(V)) return Dict[K,V](n, self._size, self._n_occupied, self._upper_bound, flags_copy, keys_copy, vals_copy) + def __deepcopy__(self): + return {k.__deepcopy__(): v.__deepcopy__() for k, v in self.items()} + def __repr__(self): n = self.__len__() if n == 0: diff --git a/stdlib/internal/types/collections/list.codon b/stdlib/internal/types/collections/list.codon index d4ce27ba..dd490b10 100644 --- a/stdlib/internal/types/collections/list.codon +++ b/stdlib/internal/types/collections/list.codon @@ -173,6 +173,9 @@ class List: def __copy__(self): return List[T](self.arr.__copy__(), self.len) + def __deepcopy__(self): + return [l.__deepcopy__() for l in self] + def __iter__(self): i = 0 N = self.len diff --git a/stdlib/internal/types/collections/set.codon b/stdlib/internal/types/collections/set.codon index 8e26f965..c045fc78 100644 --- a/stdlib/internal/types/collections/set.codon +++ b/stdlib/internal/types/collections/set.codon @@ -119,6 +119,9 @@ class Set[K]: str.memcpy(keys_copy.as_byte(), self._keys.as_byte(), n * gc.sizeof(K)) return Set[K](n, self._size, self._n_occupied, self._upper_bound, flags_copy, keys_copy) + def __deepcopy__(self): + return {s.__deepcopy__() for s in self} + def __repr__(self): n = self.__len__() if n == 0: diff --git a/stdlib/internal/types/float.codon b/stdlib/internal/types/float.codon index 572a6fbf..54b358e0 100644 --- a/stdlib/internal/types/float.codon +++ b/stdlib/internal/types/float.codon @@ -17,6 +17,8 @@ class float: return s if s != "-nan" else "nan" def __copy__(self) -> float: return self + def __deepcopy__(self) -> float: + return self @pure @llvm def __int__(self) -> int: diff --git a/stdlib/internal/types/int.codon b/stdlib/internal/types/int.codon index 42002ff8..5e7be7db 100644 --- a/stdlib/internal/types/int.codon +++ b/stdlib/internal/types/int.codon @@ -31,6 +31,8 @@ class int: return seq_str_int(self) def __copy__(self) -> int: return self + def __deepcopy__(self) -> int: + return self def __hash__(self) -> int: return self @pure diff --git a/stdlib/internal/types/intn.codon b/stdlib/internal/types/intn.codon index 94c9a3d3..4ed993da 100644 --- a/stdlib/internal/types/intn.codon +++ b/stdlib/internal/types/intn.codon @@ -50,6 +50,8 @@ class Int: return int(self) def __copy__(self) -> Int[N]: return self + def __deepcopy__(self) -> Int[N]: + return self def __hash__(self) -> int: return int(self) @pure @@ -255,6 +257,8 @@ class UInt: return int(self) def __copy__(self) -> UInt[N]: return self + def __deepcopy__(self) -> UInt[N]: + return self def __hash__(self) -> int: return int(self) @pure diff --git a/stdlib/internal/types/str.codon b/stdlib/internal/types/str.codon index d28dff66..90c7be7e 100644 --- a/stdlib/internal/types/str.codon +++ b/stdlib/internal/types/str.codon @@ -23,7 +23,10 @@ class str: def __bool__(self) -> bool: return self.len != 0 def __copy__(self) -> str: - return self + n = len(self) + p = cobj(n) + str.memcpy(p, self.ptr, n) + return str(p, n) @llvm def memcpy(dest: Ptr[byte], src: Ptr[byte], len: int) -> void: declare void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest, i8* %src, i64 %len, i32 %align, i1 %isvolatile) diff --git a/stdlib/itertools.codon b/stdlib/itertools.codon index 9461c316..e3115bf7 100644 --- a/stdlib/itertools.codon +++ b/stdlib/itertools.codon @@ -284,7 +284,7 @@ def starmap(function, iterable): yield function(*args) @inline -def groupby[T](iterable: Generator[T], key: Callable[[T], S] = NoneType, S: type = NoneType): +def groupby(iterable, key = Optional[int]()): """ Make an iterator that returns consecutive keys and groups from the iterable. """ @@ -292,7 +292,7 @@ def groupby[T](iterable: Generator[T], key: Callable[[T], S] = NoneType, S: type group = [] for currvalue in iterable: - k = currvalue if isinstance(key, NoneType) else key(currvalue) + k = currvalue if isinstance(key, Optional) else key(currvalue) if currkey is None: currkey = k if k != ~currkey: diff --git a/test/parser/simplify_expr.codon b/test/parser/simplify_expr.codon index 6be91e16..9f5aa7b9 100644 --- a/test/parser/simplify_expr.codon +++ b/test/parser/simplify_expr.codon @@ -39,8 +39,13 @@ print 5.15 #: 5.15 print 2e2 #: 200 print 2.e-2 #: 0.02 -#%% float_error,barebones -print 5.__str__() #! syntax error +#%% float_suffix,barebones +@extend +class float: + def __suffix_zoo__(x: str): + return x+'_zoo' + +print 1.2e-2zoo #: 1.2e-2_zoo #%% string,barebones print 'kthxbai', "kthxbai" #: kthxbai kthxbai diff --git a/test/parser/simplify_stmt.codon b/test/parser/simplify_stmt.codon index b43a7988..b35ef4dc 100644 --- a/test/parser/simplify_stmt.codon +++ b/test/parser/simplify_stmt.codon @@ -851,6 +851,11 @@ print np.transpose(a) n = np.array([[1, 2], [3, 4]]) print n[0], n[0][0] + 1 #: [1 2] 2 +a = np.array([1,2,3]) +print(a + 1) #: [2 3 4] +print(a - 1) #: [0 1 2] +print(1 - a) #: [ 0 -1 -2] + #%% python_import_fn from python import re.split(str, str) -> List[str] as rs print rs(r'\W+', 'Words, words, words.') #: ['Words', 'words', 'words', ''] @@ -943,10 +948,23 @@ class defdict(Dict[str,float]): z = defdict() z[1.1] #! cannot unify float and str +#%% inherit_tuple,barebones +class Foo: + a: int + b: str + def __init__(self, a: int): + self.a, self.b = a, 'yoo' +@tuple +class FooTup(Foo): pass + +f = Foo(5) +print f.a, f.b #: 5 yoo +fp = FooTup(6, 's') +print fp #: (a: 6, b: s) #%% inherit_class_err_1,barebones class defdict(Array[int]): - pass #! tuples cannot inherit reference classes (and vice versa) + pass #! reference classes cannot inherit by-value classes #%% inherit_class_err_2,barebones @tuple diff --git a/test/parser/typecheck_expr.codon b/test/parser/typecheck_expr.codon index beefbfeb..dedc61de 100644 --- a/test/parser/typecheck_expr.codon +++ b/test/parser/typecheck_expr.codon @@ -441,6 +441,7 @@ def foo(x, *args, **kwargs): print x, args, kwargs p = foo(...) p(1, z=5) #: 1 () (z: 5) +p('s', zh=65) #: s () (zh: 65) q = p(zh=43, ...) q(1) #: 1 () (zh: 43) r = q(5, 38, ...) diff --git a/test/parser/typecheck_stmt.codon b/test/parser/typecheck_stmt.codon index 3e8ab0b8..8ffdfc75 100644 --- a/test/parser/typecheck_stmt.codon +++ b/test/parser/typecheck_stmt.codon @@ -282,3 +282,19 @@ class int: yield self self -= 1 print list((5).run_lola_run()) #: [5, 4, 3, 2, 1] + + +#%% early_return,barebones +def foo(x): + print x-1 + return + print len(x) +foo(5) #: 4 + +def foo(x): + if isinstance(x, int): + print x+1 + return + print len(x) +foo(1) #: 2 +foo('s') #: 1 diff --git a/test/parser/types.codon b/test/parser/types.codon index 53e14255..a90894cb 100644 --- a/test/parser/types.codon +++ b/test/parser/types.codon @@ -765,10 +765,7 @@ def bar[T](x): foo(bar, 1) #: 1 int #: bar[...] -foo(bar(T=int,...), 1) -#: 1 int -#: bar[...] -foo(bar(T=str,...), 's') +foo(bar(...), 's') #: s str #: bar[...] z = bar @@ -780,7 +777,16 @@ z(1, T=str) zz = bar(T=int,...) zz(1) #: 1 int -# zz('s') # TODO: zz = foo[int] is update stmt, messes up everything... :/ + +#%% forward_error,barebones +def foo(f, x): + f(x, type(x)) + print f.__class__ +def bar[T](x): + print x, T.__class__ +foo(bar(T=int,...), 1) +#! too many arguments for bar[T1,int] (expected maximum 2, got 2) +#! while realizing foo (arguments foo[bar[...],int]) #%% sort_partial def foo(x, y): @@ -1082,3 +1088,22 @@ def test(v: Optional[int]): print v.__class__ test(5) #: Optional[int] test(None) #: Optional[int] + +#%% methodcaller,barebones +def foo(): + def bar(a, b): + print 'bar', a, b + return bar +foo()(1, 2) #: bar 1 2 + +def methodcaller(foo: Static[str]): + def caller(foo: Static[str], obj, *args, **kwargs): + if isinstance(getattr(obj, foo)(*args, **kwargs), void): + getattr(obj, foo)(*args, **kwargs) + else: + return getattr(obj, foo)(*args, **kwargs) + return caller(foo=foo, ...) +v = [1] +methodcaller('append')(v, 42) +print v #: [1, 42] +print methodcaller('index')(v, 42) #: 1 From 721531c409ec80fac1476f28c2768aa23c1e2900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 10 Dec 2021 13:01:40 -0800 Subject: [PATCH 02/61] Backport seq-lang/seq@develop fixes --- codon/compiler/jit.cpp | 3 +++ codon/parser/visitors/typecheck/typecheck_expr.cpp | 2 +- codon/parser/visitors/typecheck/typecheck_infer.cpp | 6 ++++++ stdlib/internal/gc.codon | 3 --- stdlib/internal/types/str.codon | 2 +- test/parser/simplify_stmt.codon | 2 +- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/codon/compiler/jit.cpp b/codon/compiler/jit.cpp index fce6d8e5..6e082d8e 100644 --- a/codon/compiler/jit.cpp +++ b/codon/compiler/jit.cpp @@ -140,7 +140,10 @@ llvm::Expected JIT::exec(const std::string &code) { auto *cache = compiler->getCache(); auto typechecked = ast::TypecheckVisitor::apply(cache, simplified); std::vector globalNames; + for (auto &g : cache->globals) { + if (!g.second) globalNames.push_back(g.first); + } // add newly realized functions std::vector v; std::vector frs; diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 8590aa1f..4b0a619f 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1088,7 +1088,7 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in int typeArgCount = 0; bool isPartial = false; int ellipsisStage = -1; - auto newMask = vector(calleeFn->ast->args.size(), 1); + auto newMask = std::vector(calleeFn->ast->args.size(), 1); if (expr->ordered) args = expr->args; else diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index 329b51e2..2acb96ef 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -188,7 +188,13 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { StmtPtr realized = nullptr; if (!isInternal) { + auto oldBlockLevel = ctx->blockLevel; + auto oldReturnEarly = ctx->returnEarly; + ctx->blockLevel = 0; + ctx->returnEarly = false; realized = inferTypes(ast->suite, false, type->realizedName()).second; + ctx->blockLevel = oldBlockLevel; + ctx->returnEarly = oldReturnEarly; if (ast->attributes.has(Attr::LLVM)) { auto s = realized->getSuite(); for (int i = 1; i < s->stmts.size(); i++) { diff --git a/stdlib/internal/gc.codon b/stdlib/internal/gc.codon index ef752107..596f9210 100644 --- a/stdlib/internal/gc.codon +++ b/stdlib/internal/gc.codon @@ -35,9 +35,6 @@ def realloc(p: cobj, sz: int): def free(p: cobj): seq_free(p) -def register_finalizer(p: cobj, f: Function[[cobj, cobj], void]): - seq_register_finalizer(p, f.__raw__()) - def add_roots(start: cobj, end: cobj): seq_gc_add_roots(start, end) diff --git a/stdlib/internal/types/str.codon b/stdlib/internal/types/str.codon index 90c7be7e..229eca1c 100644 --- a/stdlib/internal/types/str.codon +++ b/stdlib/internal/types/str.codon @@ -23,7 +23,7 @@ class str: def __bool__(self) -> bool: return self.len != 0 def __copy__(self) -> str: - n = len(self) + n = self.len p = cobj(n) str.memcpy(p, self.ptr, n) return str(p, n) diff --git a/test/parser/simplify_stmt.codon b/test/parser/simplify_stmt.codon index b35ef4dc..3fe932bf 100644 --- a/test/parser/simplify_stmt.codon +++ b/test/parser/simplify_stmt.codon @@ -960,7 +960,7 @@ class FooTup(Foo): pass f = Foo(5) print f.a, f.b #: 5 yoo fp = FooTup(6, 's') -print fp #: (a: 6, b: s) +print fp #: (a: 6, b: 's') #%% inherit_class_err_1,barebones class defdict(Array[int]): From ab14cf9fc7dda8b6979b3a91c8337c2c3a56ce8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Sat, 11 Dec 2021 11:19:14 -0800 Subject: [PATCH 03/61] Select the last matching overload by default (remove scoring logic); Add dispatch stubs for partial overload support --- codon/parser/cache.cpp | 2 +- .../parser/visitors/simplify/simplify_ctx.cpp | 5 +- codon/parser/visitors/simplify/simplify_ctx.h | 4 +- .../visitors/simplify/simplify_stmt.cpp | 33 +++- codon/parser/visitors/typecheck/typecheck.cpp | 13 +- codon/parser/visitors/typecheck/typecheck.h | 14 +- .../visitors/typecheck/typecheck_ctx.cpp | 110 +---------- .../parser/visitors/typecheck/typecheck_ctx.h | 10 - .../visitors/typecheck/typecheck_expr.cpp | 101 ++++++++-- .../visitors/typecheck/typecheck_stmt.cpp | 10 +- stdlib/internal/types/bool.codon | 2 +- stdlib/internal/types/collections/list.codon | 24 +-- stdlib/internal/types/complex.codon | 84 ++++----- stdlib/internal/types/float.codon | 123 +++++++----- stdlib/internal/types/int.codon | 177 ++++++++++++------ stdlib/internal/types/intn.codon | 77 ++++++++ stdlib/internal/types/optional.codon | 7 + stdlib/internal/types/ptr.codon | 39 +++- stdlib/internal/types/str.codon | 19 +- stdlib/statistics.codon | 16 +- 20 files changed, 526 insertions(+), 344 deletions(-) diff --git a/codon/parser/cache.cpp b/codon/parser/cache.cpp index dedaaf9c..d5b89cd0 100644 --- a/codon/parser/cache.cpp +++ b/codon/parser/cache.cpp @@ -63,7 +63,7 @@ Cache::findMethod(types::ClassType *typ, const std::string &member, seqassert(e->type, "not a class"); int oldAge = typeCtx->age; typeCtx->age = 99999; - auto f = typeCtx->findBestMethod(e.get(), member, args); + auto f = TypecheckVisitor(typeCtx).findBestMethod(e.get(), member, args); typeCtx->age = oldAge; return f; } diff --git a/codon/parser/visitors/simplify/simplify_ctx.cpp b/codon/parser/visitors/simplify/simplify_ctx.cpp index 711eeb9c..307ac1c8 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.cpp +++ b/codon/parser/visitors/simplify/simplify_ctx.cpp @@ -61,7 +61,8 @@ std::string SimplifyContext::getBase() const { } std::string SimplifyContext::generateCanonicalName(const std::string &name, - bool includeBase) const { + bool includeBase, + bool zeroId) const { std::string newName = name; if (includeBase && name.find('.') == std::string::npos) { std::string base = getBase(); @@ -74,7 +75,7 @@ std::string SimplifyContext::generateCanonicalName(const std::string &name, newName = (base.empty() ? "" : (base + ".")) + newName; } auto num = cache->identifierCount[newName]++; - newName = num ? format("{}.{}", newName, num) : newName; + newName = num || zeroId ? format("{}.{}", newName, num) : newName; if (newName != name) cache->identifierCount[newName]++; cache->reverseIdentifierLookup[newName] = name; diff --git a/codon/parser/visitors/simplify/simplify_ctx.h b/codon/parser/visitors/simplify/simplify_ctx.h index ed5ad79a..11a628a1 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.h +++ b/codon/parser/visitors/simplify/simplify_ctx.h @@ -113,8 +113,8 @@ public: void dump() override { dump(0); } /// Generate a unique identifier (name) for a given string. - std::string generateCanonicalName(const std::string &name, - bool includeBase = false) const; + std::string generateCanonicalName(const std::string &name, bool includeBase = false, + bool zeroId = false) const; bool inFunction() const { return getLevel() && !bases.back().isType(); } bool inClass() const { return getLevel() && bases.back().isType(); } diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index a3158edc..26e3b25d 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -472,8 +472,25 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { return; } - auto canonicalName = ctx->generateCanonicalName(stmt->name, true); bool isClassMember = ctx->inClass(); + if (isClassMember && !endswith(stmt->name, ".dispatch") && + ctx->cache->classes[ctx->bases.back().name].methods[stmt->name].empty()) { + transform( + N(stmt->name + ".dispatch", nullptr, + std::vector{Param("*args")}, + N(N(N( + N(N(ctx->bases.back().name), stmt->name), + N(N("args"))))))); + } + auto func_name = stmt->name; + if (endswith(stmt->name, ".dispatch")) + func_name = func_name.substr(0, func_name.size() - 9); + auto canonicalName = ctx->generateCanonicalName( + func_name, true, isClassMember && !endswith(stmt->name, ".dispatch")); + if (endswith(stmt->name, ".dispatch")) { + canonicalName += ".dispatch"; + ctx->cache->reverseIdentifierLookup[canonicalName] = func_name; + } bool isEnclosedFunc = ctx->inFunction(); if (attr.has(Attr::ForceRealize) && (ctx->getLevel() || isClassMember)) @@ -483,7 +500,7 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { ctx->bases = std::vector(); if (!isClassMember) // Class members are added to class' method table - ctx->add(SimplifyItem::Func, stmt->name, canonicalName, ctx->isToplevel()); + ctx->add(SimplifyItem::Func, func_name, canonicalName, ctx->isToplevel()); if (isClassMember) ctx->bases.push_back(oldBases[0]); ctx->bases.emplace_back(SimplifyContext::Base{canonicalName}); // Add new base... @@ -602,7 +619,7 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { // ... set the enclosing class name... attr.parentClass = ctx->bases.back().name; // ... add the method to class' method list ... - ctx->cache->classes[ctx->bases.back().name].methods[stmt->name].push_back( + ctx->cache->classes[ctx->bases.back().name].methods[func_name].push_back( {canonicalName, nullptr, ctx->cache->age}); // ... and if the function references outer class variable (by definition a // generic), mark it as not static as it needs fully instantiated class to be @@ -637,21 +654,21 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { ExprPtr finalExpr; if (!captures.empty()) - finalExpr = N(N(stmt->name), partialArgs); + finalExpr = N(N(func_name), partialArgs); if (isClassMember && decorators.size()) error("decorators cannot be applied to class methods"); for (int j = int(decorators.size()) - 1; j >= 0; j--) { if (auto c = const_cast(decorators[j]->getCall())) { c->args.emplace(c->args.begin(), - CallExpr::Arg{"", finalExpr ? finalExpr : N(stmt->name)}); + CallExpr::Arg{"", finalExpr ? finalExpr : N(func_name)}); finalExpr = N(c->expr, c->args); } else { finalExpr = - N(decorators[j], finalExpr ? finalExpr : N(stmt->name)); + N(decorators[j], finalExpr ? finalExpr : N(func_name)); } } if (finalExpr) - resultStmt = transform(N(N(stmt->name), finalExpr)); + resultStmt = transform(N(N(func_name), finalExpr)); } void SimplifyVisitor::visit(ClassStmt *stmt) { @@ -941,7 +958,7 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { continue; auto subs = substitutions[ai]; auto newName = ctx->generateCanonicalName( - ctx->cache->reverseIdentifierLookup[f->name], true); + ctx->cache->reverseIdentifierLookup[f->name], true, true); auto nf = std::dynamic_pointer_cast(replace(sp, subs)); subs[nf->name] = N(newName); nf->name = newName; diff --git a/codon/parser/visitors/typecheck/typecheck.cpp b/codon/parser/visitors/typecheck/typecheck.cpp index 867d7da7..77dc0ba0 100644 --- a/codon/parser/visitors/typecheck/typecheck.cpp +++ b/codon/parser/visitors/typecheck/typecheck.cpp @@ -33,16 +33,21 @@ StmtPtr TypecheckVisitor::apply(Cache *cache, StmtPtr stmts) { return std::move(infer.second); } -TypePtr TypecheckVisitor::unify(TypePtr &a, const TypePtr &b) { +TypePtr TypecheckVisitor::unify(TypePtr &a, const TypePtr &b, bool undoOnSuccess) { if (!a) return a = b; seqassert(b, "rhs is nullptr"); types::Type::Unification undo; - if (a->unify(b.get(), &undo) >= 0) + if (a->unify(b.get(), &undo) >= 0) { + if (undoOnSuccess) + undo.undo(); return a; - undo.undo(); + } else { + undo.undo(); + } // LOG("{} / {}", a->debugString(true), b->debugString(true)); - a->unify(b.get(), &undo); + if (!undoOnSuccess) + a->unify(b.get(), &undo); error("cannot unify {} and {}", a->toString(), b->toString()); return nullptr; } diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index e8344461..fc75f4f4 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -283,9 +283,19 @@ private: void generateFnCall(int n); /// Make an empty partial call fn(...) for a function fn. ExprPtr partializeFunction(ExprPtr expr); + /// Picks the best method of a given expression that matches the given argument + /// types. Prefers methods whose signatures are closer to the given arguments: + /// e.g. foo(int) will match (int) better that a foo(T). + /// Also takes care of the Optional arguments. + /// If multiple equally good methods are found, return the first one. + /// Return nullptr if no methods were found. + types::FuncTypePtr + findBestMethod(const Expr *expr, const std::string &member, + const std::vector> &args); private: - types::TypePtr unify(types::TypePtr &a, const types::TypePtr &b); + types::TypePtr unify(types::TypePtr &a, const types::TypePtr &b, + bool undoOnSuccess = false); types::TypePtr realizeType(types::ClassType *typ); types::TypePtr realizeFunc(types::FuncType *typ); std::pair inferTypes(StmtPtr stmt, bool keepLast, @@ -293,7 +303,7 @@ private: codon::ir::types::Type *getLLVMType(const types::ClassType *t); bool wrapExpr(ExprPtr &expr, types::TypePtr expectedType, - const types::FuncTypePtr &callee); + const types::FuncTypePtr &callee, bool undoOnSuccess = false); int64_t translateIndex(int64_t idx, int64_t len, bool clamp = false); int64_t sliceAdjustIndices(int64_t length, int64_t *start, int64_t *stop, int64_t step); diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.cpp b/codon/parser/visitors/typecheck/typecheck_ctx.cpp index 31297307..d1c72784 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.cpp +++ b/codon/parser/visitors/typecheck/typecheck_ctx.cpp @@ -141,10 +141,12 @@ TypeContext::findMethod(const std::string &typeName, const std::string &method) if (m != cache->classes.end()) { auto t = m->second.methods.find(method); if (t != m->second.methods.end()) { + seqassert(!t->second.empty() && endswith(t->second[0].name, ".dispatch"), + "first method is not dispatch"); std::unordered_map signatureLoci; std::vector vv; - for (auto &mt : t->second) { - // LOG("{}::{} @ {} vs. {}", typeName, method, age, mt.age); + for (int mti = 1; mti < t->second.size(); mti++) { + auto &mt = t->second[mti]; if (mt.age <= age) { auto sig = cache->functions[mt.name].ast->signature(); auto it = signatureLoci.find(sig); @@ -177,110 +179,6 @@ types::TypePtr TypeContext::findMember(const std::string &typeName, return nullptr; } -types::FuncTypePtr TypeContext::findBestMethod( - const Expr *expr, const std::string &member, - const std::vector> &args, bool checkSingle) { - auto typ = expr->getType()->getClass(); - seqassert(typ, "not a class"); - auto methods = findMethod(typ->name, member); - if (methods.empty()) - return nullptr; - if (methods.size() == 1 && !checkSingle) // methods is not overloaded - return methods[0]; - - // Calculate the unification score for each available methods and pick the one with - // highest score. - std::vector> scores; - for (int mi = 0; mi < methods.size(); mi++) { - auto method = instantiate(expr, methods[mi], typ.get(), false)->getFunc(); - std::vector reordered; - std::vector callArgs; - for (auto &a : args) { - callArgs.push_back({a.first, std::make_shared()}); // dummy expression - callArgs.back().value->setType(a.second); - } - auto score = reorderNamedArgs( - method.get(), callArgs, - [&](int s, int k, const std::vector> &slots, bool _) { - for (int si = 0; si < slots.size(); si++) { - // Ignore *args, *kwargs and default arguments - reordered.emplace_back(si == s || si == k || slots[si].size() != 1 - ? nullptr - : args[slots[si][0]].second); - } - return 0; - }, - [](const std::string &) { return -1; }); - if (score == -1) - continue; - // Scoring system for each argument: - // Generics, traits and default arguments get a score of zero (lowest priority). - // Optional unwrap gets the score of 1. - // Optional wrap gets the score of 2. - // Successful unification gets the score of 3 (highest priority). - for (int ai = 0, mi = 1, gi = 0; ai < reordered.size(); ai++) { - auto argType = reordered[ai]; - if (!argType) - continue; - auto expectedType = method->ast->args[ai].generic ? method->generics[gi++].type - : method->args[mi++]; - auto expectedClass = expectedType->getClass(); - // Ignore traits, *args/**kwargs and default arguments. - if (expectedClass && expectedClass->name == "Generator") - continue; - // LOG("<~> {} {}", argType->toString(), expectedType->toString()); - auto argClass = argType->getClass(); - - types::Type::Unification undo; - int u = argType->unify(expectedType.get(), &undo); - undo.undo(); - if (u >= 0) { - score += u + 3; - continue; - } - if (!method->ast->args[ai].generic) { - // Unification failed: maybe we need to wrap an argument? - if (expectedClass && expectedClass->name == TYPE_OPTIONAL && argClass && - argClass->name != expectedClass->name) { - u = argType->unify(expectedClass->generics[0].type.get(), &undo); - undo.undo(); - if (u >= 0) { - score += u + 2; - continue; - } - } - // ... or unwrap it (less ideal)? - if (argClass && argClass->name == TYPE_OPTIONAL && expectedClass && - argClass->name != expectedClass->name) { - u = argClass->generics[0].type->unify(expectedType.get(), &undo); - undo.undo(); - if (u >= 0) { - score += u; - continue; - } - } - } - // This method cannot be selected, ignore it. - score = -1; - break; - } - // LOG("{} {} / {}", typ->toString(), method->toString(), score); - if (score >= 0) - scores.emplace_back(std::make_pair(score, mi)); - } - if (scores.empty()) - return nullptr; - // Get the best score. - sort(scores.begin(), scores.end(), std::greater<>()); - // LOG("Method: {}", methods[scores[0].second]->toString()); - // std::string x; - // for (auto &a : args) - // x += format("{}{},", a.first.empty() ? "" : a.first + ": ", - // a.second->toString()); - // LOG(" {} :: {} ( {} )", typ->toString(), member, x); - return methods[scores[0].second]; -} - int TypeContext::reorderNamedArgs(types::FuncType *func, const std::vector &args, ReorderDoneFn onDone, ReorderErrorFn onError, diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.h b/codon/parser/visitors/typecheck/typecheck_ctx.h index 9713ae64..b1961653 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.h +++ b/codon/parser/visitors/typecheck/typecheck_ctx.h @@ -127,16 +127,6 @@ public: types::TypePtr findMember(const std::string &typeName, const std::string &member) const; - /// Picks the best method of a given expression that matches the given argument - /// types. Prefers methods whose signatures are closer to the given arguments: - /// e.g. foo(int) will match (int) better that a foo(T). - /// Also takes care of the Optional arguments. - /// If multiple equally good methods are found, return the first one. - /// Return nullptr if no methods were found. - types::FuncTypePtr - findBestMethod(const Expr *expr, const std::string &member, - const std::vector> &args, - bool checkSingle = false); typedef std::function> &, bool)> ReorderDoneFn; diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 4b0a619f..b9201490 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -683,8 +683,8 @@ ExprPtr TypecheckVisitor::transformBinary(BinaryExpr *expr, bool isAtomic, if (isAtomic) { auto ptrlt = ctx->instantiateGeneric(expr->lexpr.get(), ctx->findInternal("Ptr"), {lt}); - method = ctx->findBestMethod(expr->lexpr.get(), format("__atomic_{}__", magic), - {{"", ptrlt}, {"", rt}}); + method = findBestMethod(expr->lexpr.get(), format("__atomic_{}__", magic), + {{"", ptrlt}, {"", rt}}); if (method) { expr->lexpr = N(expr->lexpr); if (noReturn) @@ -693,19 +693,19 @@ ExprPtr TypecheckVisitor::transformBinary(BinaryExpr *expr, bool isAtomic, } // Check if lt.__iop__(lt, rt) exists. if (!method && expr->inPlace) { - method = ctx->findBestMethod(expr->lexpr.get(), format("__i{}__", magic), - {{"", lt}, {"", rt}}); + method = findBestMethod(expr->lexpr.get(), format("__i{}__", magic), + {{"", lt}, {"", rt}}); if (method && noReturn) *noReturn = true; } // Check if lt.__op__(lt, rt) exists. if (!method) - method = ctx->findBestMethod(expr->lexpr.get(), format("__{}__", magic), - {{"", lt}, {"", rt}}); + method = findBestMethod(expr->lexpr.get(), format("__{}__", magic), + {{"", lt}, {"", rt}}); // Check if rt.__rop__(rt, lt) exists. if (!method) { - method = ctx->findBestMethod(expr->rexpr.get(), format("__r{}__", magic), - {{"", rt}, {"", lt}}); + method = findBestMethod(expr->rexpr.get(), format("__r{}__", magic), + {{"", rt}, {"", lt}}); if (method) swap(expr->lexpr, expr->rexpr); } @@ -873,8 +873,7 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, argTypes.emplace_back(make_pair("", typ)); // self variable for (const auto &a : *args) argTypes.emplace_back(make_pair(a.name, a.value->getType())); - if (auto bestMethod = - ctx->findBestMethod(expr->expr.get(), expr->member, argTypes)) { + if (auto bestMethod = findBestMethod(expr->expr.get(), expr->member, argTypes)) { ExprPtr e = N(bestMethod->ast->name); auto t = ctx->instantiate(expr, bestMethod, typ.get()); unify(e->type, t); @@ -906,7 +905,7 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, methodArgs.emplace_back(make_pair("", typ)); for (auto i = 1; i < oldType->generics.size(); i++) methodArgs.emplace_back(make_pair("", oldType->generics[i].type)); - bestMethod = ctx->findBestMethod(expr->expr.get(), expr->member, methodArgs); + bestMethod = findBestMethod(expr->expr.get(), expr->member, methodArgs); if (!bestMethod) { // Print a nice error message. std::vector nice; @@ -916,9 +915,11 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, typ->toString(), join(nice, ", ")); } } else { - // HACK: if we still have multiple valid methods, we just use the first one. - // TODO: handle this better (maybe hold these types until they can be selected?) - bestMethod = methods[0]; + auto m = ctx->cache->classes.find(typ->name); + auto t = m->second.methods.find(expr->member); + seqassert(!t->second.empty() && endswith(t->second[0].name, ".dispatch"), + "first method is not dispatch"); + bestMethod = t->second[0].type; } // Case 7: only one valid method remaining. Check if this is a class method or an @@ -1004,6 +1005,7 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in ai--; } else { // Case 3: Normal argument + // LOG("-> {}", expr->args[ai].value->toString()); expr->args[ai].value = transform(expr->args[ai].value, true); // Unbound inType might become a generator that will need to be extracted, so // don't unify it yet. @@ -1365,8 +1367,7 @@ std::pair TypecheckVisitor::transformSpecialCall(CallExpr *expr) bool exists = !ctx->findMethod(typ->getClass()->name, member).empty() || ctx->findMember(typ->getClass()->name, member); if (exists && args.size() > 1) - exists &= - ctx->findBestMethod(expr->args[0].value.get(), member, args, true) != nullptr; + exists &= findBestMethod(expr->args[0].value.get(), member, args) != nullptr; return {true, transform(N(exists))}; } else if (val == "compile_error") { expr->args[0].value = transform(expr->args[0].value); @@ -1609,8 +1610,72 @@ ExprPtr TypecheckVisitor::partializeFunction(ExprPtr expr) { return call; } +types::FuncTypePtr TypecheckVisitor::findBestMethod( + const Expr *expr, const std::string &member, + const std::vector> &args) { + auto typ = expr->getType()->getClass(); + seqassert(typ, "not a class"); + + // Pick the last method that accepts the given arguments. + auto methods = ctx->findMethod(typ->name, member); + // if (methods.size() == 1) + // return methods[0]; + types::FuncTypePtr method = nullptr; + for (int mi = int(methods.size()) - 1; mi >= 0; mi--) { + auto m = ctx->instantiate(expr, methods[mi], typ.get(), false)->getFunc(); + std::vector reordered; + std::vector callArgs; + for (auto &a : args) { + callArgs.push_back({a.first, std::make_shared()}); // dummy expression + callArgs.back().value->setType(a.second); + } + auto score = ctx->reorderNamedArgs( + m.get(), callArgs, + [&](int s, int k, const std::vector> &slots, bool _) { + for (int si = 0; si < slots.size(); si++) { + if (m->ast->args[si].generic) { + // Ignore type arguments + } else if (si == s || si == k || slots[si].size() != 1) { + // Ignore *args, *kwargs and default arguments + reordered.emplace_back(nullptr); + } else { + reordered.emplace_back(args[slots[si][0]].second); + } + } + return 0; + }, + [](const std::string &) { return -1; }); + for (int ai = 0, mi = 1, gi = 0; score != -1 && ai < reordered.size(); ai++) { + auto argType = reordered[ai]; + if (!argType) + continue; + auto expectTyp = + m->ast->args[ai].generic ? m->generics[gi++].type : m->args[mi++]; + try { + ExprPtr dummy = std::make_shared(""); + dummy->type = argType; + dummy->done = true; + wrapExpr(dummy, expectTyp, m, /*undoOnSuccess*/ true); + } catch (const exc::ParserException &) { + score = -1; + } + } + if (score != -1) { + // std::vector ar; + // for (auto &a: args) { + // if (a.first.empty()) ar.push_back(a.second->toString()); + // else ar.push_back(format("{}: {}", a.first, a.second->toString())); + // } + // LOG("- {} vs {}", m->toString(), join(ar, "; ")); + method = methods[mi]; + break; + } + } + return method; +} + bool TypecheckVisitor::wrapExpr(ExprPtr &expr, TypePtr expectedType, - const FuncTypePtr &callee) { + const FuncTypePtr &callee, bool undoOnSuccess) { auto expectedClass = expectedType->getClass(); auto exprClass = expr->getType()->getClass(); if (callee && expr->isType()) @@ -1637,7 +1702,7 @@ bool TypecheckVisitor::wrapExpr(ExprPtr &expr, TypePtr expectedType, // Case 7: wrap raw Seq functions into Partial(...) call for easy realization. expr = partializeFunction(expr); } - unify(expr->type, expectedType); + unify(expr->type, expectedType, undoOnSuccess); return true; } diff --git a/codon/parser/visitors/typecheck/typecheck_stmt.cpp b/codon/parser/visitors/typecheck/typecheck_stmt.cpp index 432857c0..5f08fb94 100644 --- a/codon/parser/visitors/typecheck/typecheck_stmt.cpp +++ b/codon/parser/visitors/typecheck/typecheck_stmt.cpp @@ -153,9 +153,9 @@ void TypecheckVisitor::visit(UpdateStmt *stmt) { ctx->instantiateGeneric(stmt->lhs.get(), ctx->findInternal("Ptr"), {lhsClass}); c->args[1].value = transform(c->args[1].value); auto rhsTyp = c->args[1].value->getType()->getClass(); - if (auto method = ctx->findBestMethod( - stmt->lhs.get(), format("__atomic_{}__", c->expr->getId()->value), - {{"", ptrTyp}, {"", rhsTyp}})) { + if (auto method = findBestMethod(stmt->lhs.get(), + format("__atomic_{}__", c->expr->getId()->value), + {{"", ptrTyp}, {"", rhsTyp}})) { resultStmt = transform(N(N( N(method->ast->name), N(stmt->lhs), c->args[1].value))); return; @@ -168,8 +168,8 @@ void TypecheckVisitor::visit(UpdateStmt *stmt) { if (stmt->isAtomic && lhsClass && rhsClass) { auto ptrType = ctx->instantiateGeneric(stmt->lhs.get(), ctx->findInternal("Ptr"), {lhsClass}); - if (auto m = ctx->findBestMethod(stmt->lhs.get(), "__atomic_xchg__", - {{"", ptrType}, {"", rhsClass}})) { + if (auto m = findBestMethod(stmt->lhs.get(), "__atomic_xchg__", + {{"", ptrType}, {"", rhsClass}})) { resultStmt = transform(N( N(N(m->ast->name), N(stmt->lhs), stmt->rhs))); return; diff --git a/stdlib/internal/types/bool.codon b/stdlib/internal/types/bool.codon index de66f660..6e54e3ec 100644 --- a/stdlib/internal/types/bool.codon +++ b/stdlib/internal/types/bool.codon @@ -4,7 +4,7 @@ from internal.attributes import commutative, associative class bool: def __new__() -> bool: return False - def __new__[T](what: T) -> bool: # lowest priority! + def __new__(what) -> bool: return what.__bool__() def __repr__(self) -> str: return "True" if self else "False" diff --git a/stdlib/internal/types/collections/list.codon b/stdlib/internal/types/collections/list.codon index dd490b10..364043d7 100644 --- a/stdlib/internal/types/collections/list.codon +++ b/stdlib/internal/types/collections/list.codon @@ -2,9 +2,9 @@ import internal.gc as gc @extend class List: - def __init__(self, arr: Array[T], len: int): - self.arr = arr - self.len = len + def __init__(self): + self.arr = Array[T](10) + self.len = 0 def __init__(self, it: Generator[T]): self.arr = Array[T](10) @@ -12,27 +12,27 @@ class List: for i in it: self.append(i) - def __init__(self, capacity: int): - self.arr = Array[T](capacity) - self.len = 0 - - def __init__(self): - self.arr = Array[T](10) - self.len = 0 - def __init__(self, other: List[T]): self.arr = Array[T](other.len) self.len = 0 for i in other: self.append(i) - # Dummy __init__ used for list comprehension optimization + def __init__(self, capacity: int): + self.arr = Array[T](capacity) + self.len = 0 + def __init__(self, dummy: bool, other): + """Dummy __init__ used for list comprehension optimization""" if hasattr(other, '__len__'): self.__init__(other.__len__()) else: self.__init__() + def __init__(self, arr: Array[T], len: int): + self.arr = arr + self.len = len + def __len__(self): return self.len diff --git a/stdlib/internal/types/complex.codon b/stdlib/internal/types/complex.codon index ef4502b4..7e4811c6 100644 --- a/stdlib/internal/types/complex.codon +++ b/stdlib/internal/types/complex.codon @@ -6,18 +6,12 @@ class complex: def __new__(): return complex(0.0, 0.0) - def __new__(real: int, imag: int): - return complex(float(real), float(imag)) - - def __new__(real: float, imag: int): - return complex(real, float(imag)) - - def __new__(real: int, imag: float): - return complex(float(real), imag) - def __new__(other): return other.__complex__() + def __new__(real, imag): + return complex(float(real), float(imag)) + def __complex__(self): return self @@ -42,6 +36,42 @@ class complex: def __hash__(self): return self.real.__hash__() + self.imag.__hash__()*1000003 + def __add__(self, other): + return self + complex(other) + + def __sub__(self, other): + return self - complex(other) + + def __mul__(self, other): + return self * complex(other) + + def __truediv__(self, other): + return self / complex(other) + + def __eq__(self, other): + return self == complex(other) + + def __ne__(self, other): + return self != complex(other) + + def __pow__(self, other): + return self ** complex(other) + + def __radd__(self, other): + return complex(other) + self + + def __rsub__(self, other): + return complex(other) - self + + def __rmul__(self, other): + return complex(other) * self + + def __rtruediv__(self, other): + return complex(other) / self + + def __rpow__(self, other): + return complex(other) ** self + def __add__(self, other: complex): return complex(self.real + other.real, self.imag + other.imag) @@ -160,42 +190,6 @@ class complex: phase += other.imag * log(vabs) return complex(len * cos(phase), len * sin(phase)) - def __add__(self, other): - return self + complex(other) - - def __sub__(self, other): - return self - complex(other) - - def __mul__(self, other): - return self * complex(other) - - def __truediv__(self, other): - return self / complex(other) - - def __eq__(self, other): - return self == complex(other) - - def __ne__(self, other): - return self != complex(other) - - def __pow__(self, other): - return self ** complex(other) - - def __radd__(self, other): - return complex(other) + self - - def __rsub__(self, other): - return complex(other) - self - - def __rmul__(self, other): - return complex(other) * self - - def __rtruediv__(self, other): - return complex(other) / self - - def __rpow__(self, other): - return complex(other) ** self - def __repr__(self): @pure @llvm diff --git a/stdlib/internal/types/float.codon b/stdlib/internal/types/float.codon index 54b358e0..68e59635 100644 --- a/stdlib/internal/types/float.codon +++ b/stdlib/internal/types/float.codon @@ -10,80 +10,105 @@ def seq_str_float(a: float) -> str: pass class float: def __new__() -> float: return 0.0 - def __new__[T](what: T): + + def __new__(what): return what.__float__() + + def __new__(s: str) -> float: + from C import strtod(cobj, Ptr[cobj]) -> float + buf = __array__[byte](32) + n = s.__len__() + need_dyn_alloc = (n >= buf.__len__()) + + p = alloc_atomic(n + 1) if need_dyn_alloc else buf.ptr + str.memcpy(p, s.ptr, n) + p[n] = byte(0) + + end = cobj() + result = strtod(p, __ptr__(end)) + + if need_dyn_alloc: + free(p) + + if end != p + n: + raise ValueError("could not convert string to float: " + s) + + return result + def __repr__(self) -> str: s = seq_str_float(self) return s if s != "-nan" else "nan" + def __copy__(self) -> float: return self + def __deepcopy__(self) -> float: return self + @pure @llvm def __int__(self) -> int: %0 = fptosi double %self to i64 ret i64 %0 + def __float__(self): return self + @pure @llvm def __bool__(self) -> bool: %0 = fcmp one double %self, 0.000000e+00 %1 = zext i1 %0 to i8 ret i8 %1 + def __complex__(self): return complex(self, 0.0) + def __pos__(self) -> float: return self + @pure @llvm def __neg__(self) -> float: %0 = fneg double %self ret double %0 + @pure @commutative @llvm def __add__(a: float, b: float) -> float: %tmp = fadd double %a, %b ret double %tmp - @commutative - def __add__(self, other: int) -> float: - return self.__add__(float(other)) + @pure @llvm def __sub__(a: float, b: float) -> float: %tmp = fsub double %a, %b ret double %tmp - def __sub__(self, other: int) -> float: - return self.__sub__(float(other)) + @pure @commutative @llvm def __mul__(a: float, b: float) -> float: %tmp = fmul double %a, %b ret double %tmp - @commutative - def __mul__(self, other: int) -> float: - return self.__mul__(float(other)) + def __floordiv__(self, other: float) -> float: return self.__truediv__(other).__floor__() - def __floordiv__(self, other: int) -> float: - return self.__floordiv__(float(other)) + + @pure @llvm def __truediv__(a: float, b: float) -> float: %tmp = fdiv double %a, %b ret double %tmp - def __truediv__(self, other: int) -> float: - return self.__truediv__(float(other)) + @pure @llvm def __mod__(a: float, b: float) -> float: %tmp = frem double %a, %b ret double %tmp - def __mod__(self, other: int) -> float: - return self.__mod__(float(other)) + def __divmod__(self, other: float): mod = self % other div = (self - mod) / other @@ -103,16 +128,14 @@ class float: floordiv = (0.0).copysign(self / other) return (floordiv, mod) - def __divmod__(self, other: int): - return self.__divmod__(float(other)) + @pure @llvm def __eq__(a: float, b: float) -> bool: %tmp = fcmp oeq double %a, %b %res = zext i1 %tmp to i8 ret i8 %res - def __eq__(self, other: int) -> bool: - return self.__eq__(float(other)) + @pure @llvm def __ne__(a: float, b: float) -> bool: @@ -120,174 +143,190 @@ class float: %tmp = fcmp one double %a, %b %res = zext i1 %tmp to i8 ret i8 %res - def __ne__(self, other: int) -> bool: - return self.__ne__(float(other)) + @pure @llvm def __lt__(a: float, b: float) -> bool: %tmp = fcmp olt double %a, %b %res = zext i1 %tmp to i8 ret i8 %res - def __lt__(self, other: int) -> bool: - return self.__lt__(float(other)) + @pure @llvm def __gt__(a: float, b: float) -> bool: %tmp = fcmp ogt double %a, %b %res = zext i1 %tmp to i8 ret i8 %res - def __gt__(self, other: int) -> bool: - return self.__gt__(float(other)) + @pure @llvm def __le__(a: float, b: float) -> bool: %tmp = fcmp ole double %a, %b %res = zext i1 %tmp to i8 ret i8 %res - def __le__(self, other: int) -> bool: - return self.__le__(float(other)) + @pure @llvm def __ge__(a: float, b: float) -> bool: %tmp = fcmp oge double %a, %b %res = zext i1 %tmp to i8 ret i8 %res - def __ge__(self, other: int) -> bool: - return self.__ge__(float(other)) + @pure @llvm def sqrt(a: float) -> float: declare double @llvm.sqrt.f64(double %a) %tmp = call double @llvm.sqrt.f64(double %a) ret double %tmp + @pure @llvm def sin(a: float) -> float: declare double @llvm.sin.f64(double %a) %tmp = call double @llvm.sin.f64(double %a) ret double %tmp + @pure @llvm def cos(a: float) -> float: declare double @llvm.cos.f64(double %a) %tmp = call double @llvm.cos.f64(double %a) ret double %tmp + @pure @llvm def exp(a: float) -> float: declare double @llvm.exp.f64(double %a) %tmp = call double @llvm.exp.f64(double %a) ret double %tmp + @pure @llvm def exp2(a: float) -> float: declare double @llvm.exp2.f64(double %a) %tmp = call double @llvm.exp2.f64(double %a) ret double %tmp + @pure @llvm def log(a: float) -> float: declare double @llvm.log.f64(double %a) %tmp = call double @llvm.log.f64(double %a) ret double %tmp + @pure @llvm def log10(a: float) -> float: declare double @llvm.log10.f64(double %a) %tmp = call double @llvm.log10.f64(double %a) ret double %tmp + @pure @llvm def log2(a: float) -> float: declare double @llvm.log2.f64(double %a) %tmp = call double @llvm.log2.f64(double %a) ret double %tmp + @pure @llvm def __abs__(a: float) -> float: declare double @llvm.fabs.f64(double %a) %tmp = call double @llvm.fabs.f64(double %a) ret double %tmp + @pure @llvm def __floor__(a: float) -> float: declare double @llvm.floor.f64(double %a) %tmp = call double @llvm.floor.f64(double %a) ret double %tmp + @pure @llvm def __ceil__(a: float) -> float: declare double @llvm.ceil.f64(double %a) %tmp = call double @llvm.ceil.f64(double %a) ret double %tmp + @pure @llvm def __trunc__(a: float) -> float: declare double @llvm.trunc.f64(double %a) %tmp = call double @llvm.trunc.f64(double %a) ret double %tmp + @pure @llvm def rint(a: float) -> float: declare double @llvm.rint.f64(double %a) %tmp = call double @llvm.rint.f64(double %a) ret double %tmp + @pure @llvm def nearbyint(a: float) -> float: declare double @llvm.nearbyint.f64(double %a) %tmp = call double @llvm.nearbyint.f64(double %a) ret double %tmp + @pure @llvm def __round__(a: float) -> float: declare double @llvm.round.f64(double %a) %tmp = call double @llvm.round.f64(double %a) ret double %tmp + @pure @llvm def __pow__(a: float, b: float) -> float: declare double @llvm.pow.f64(double %a, double %b) %tmp = call double @llvm.pow.f64(double %a, double %b) ret double %tmp - def __pow__(self, other: int) -> float: - return self.__pow__(float(other)) + @pure @llvm def min(a: float, b: float) -> float: declare double @llvm.minnum.f64(double %a, double %b) %tmp = call double @llvm.minnum.f64(double %a, double %b) ret double %tmp + @pure @llvm def max(a: float, b: float) -> float: declare double @llvm.maxnum.f64(double %a, double %b) %tmp = call double @llvm.maxnum.f64(double %a, double %b) ret double %tmp + @pure @llvm def copysign(a: float, b: float) -> float: declare double @llvm.copysign.f64(double %a, double %b) %tmp = call double @llvm.copysign.f64(double %a, double %b) ret double %tmp + @pure @llvm def fma(a: float, b: float, c: float) -> float: declare double @llvm.fma.f64(double %a, double %b, double %c) %tmp = call double @llvm.fma.f64(double %a, double %b, double %c) ret double %tmp + @llvm def __atomic_xchg__(d: Ptr[float], b: float) -> void: %tmp = atomicrmw xchg double* %d, double %b seq_cst ret void + @llvm def __atomic_add__(d: Ptr[float], b: float) -> float: %tmp = atomicrmw fadd double* %d, double %b seq_cst ret double %tmp + @llvm def __atomic_sub__(d: Ptr[float], b: float) -> float: %tmp = atomicrmw fsub double* %d, double %b seq_cst ret double %tmp + def __hash__(self): from C import frexp(float, Ptr[Int[32]]) -> float @@ -332,31 +371,13 @@ class float: x = -2 return x - def __new__(s: str) -> float: - from C import strtod(cobj, Ptr[cobj]) -> float - buf = __array__[byte](32) - n = s.__len__() - need_dyn_alloc = (n >= buf.__len__()) - - p = alloc_atomic(n + 1) if need_dyn_alloc else buf.ptr - str.memcpy(p, s.ptr, n) - p[n] = byte(0) - - end = cobj() - result = strtod(p, __ptr__(end)) - - if need_dyn_alloc: - free(p) - - if end != p + n: - raise ValueError("could not convert string to float: " + s) - - return result def __match__(self, i: float): return self == i + @property def real(self): return self + @property def imag(self): return 0.0 diff --git a/stdlib/internal/types/int.codon b/stdlib/internal/types/int.codon index 5e7be7db..8c2e8676 100644 --- a/stdlib/internal/types/int.codon +++ b/stdlib/internal/types/int.codon @@ -14,37 +14,56 @@ class int: @llvm def __new__() -> int: ret i64 0 - def __new__[T](what: T) -> int: # lowest priority! + + def __new__(what) -> int: return what.__int__() + + def __new__(s: str) -> int: + return int._from_str(s, 10) + + def __new__(s: str, base: int) -> int: + return int._from_str(s, base) + def __int__(self) -> int: return self + @pure @llvm def __float__(self) -> float: %tmp = sitofp i64 %self to double ret double %tmp + def __complex__(self): return complex(float(self), 0.0) + def __index__(self): return self + def __repr__(self) -> str: return seq_str_int(self) + def __copy__(self) -> int: return self + def __deepcopy__(self) -> int: return self + def __hash__(self) -> int: return self + @pure @llvm def __bool__(self) -> bool: %0 = icmp ne i64 %self, 0 %1 = zext i1 %0 to i8 ret i8 %1 + def __pos__(self) -> int: return self + def __neg__(self) -> int: return 0 - self + @pure @llvm def __abs__(self) -> int: @@ -52,23 +71,19 @@ class int: %1 = sub i64 0, %self %2 = select i1 %0, i64 %self, i64 %1 ret i64 %2 + @pure @llvm def __lshift__(self, other: int) -> int: %0 = shl i64 %self, %other ret i64 %0 + @pure @llvm def __rshift__(self, other: int) -> int: %0 = ashr i64 %self, %other ret i64 %0 - @pure - @commutative - @associative - @llvm - def __add__(self, b: int) -> int: - %tmp = add i64 %self, %b - ret i64 %tmp + @pure @commutative @llvm @@ -76,17 +91,36 @@ class int: %0 = sitofp i64 %self to double %1 = fadd double %0, %other ret double %1 + @pure + @commutative + @associative @llvm - def __sub__(self, b: int) -> int: - %tmp = sub i64 %self, %b + def __add__(self, b: int) -> int: + %tmp = add i64 %self, %b ret i64 %tmp + @pure @llvm def __sub__(self, other: float) -> float: %0 = sitofp i64 %self to double %1 = fsub double %0, %other ret double %1 + + @pure + @llvm + def __sub__(self, b: int) -> int: + %tmp = sub i64 %self, %b + ret i64 %tmp + + @pure + @commutative + @llvm + def __mul__(self, other: float) -> float: + %0 = sitofp i64 %self to double + %1 = fmul double %0, %other + ret double %1 + @pure @commutative @associative @@ -95,18 +129,7 @@ class int: def __mul__(self, b: int) -> int: %tmp = mul i64 %self, %b ret i64 %tmp - @pure - @commutative - @llvm - def __mul__(self, other: float) -> float: - %0 = sitofp i64 %self to double - %1 = fmul double %0, %other - ret double %1 - @pure - @llvm - def __floordiv__(self, b: int) -> int: - %tmp = sdiv i64 %self, %b - ret i64 %tmp + @pure @llvm def __floordiv__(self, other: float) -> float: @@ -115,6 +138,20 @@ class int: %1 = fdiv double %0, %other %2 = call double @llvm.floor.f64(double %1) ret double %2 + + @pure + @llvm + def __floordiv__(self, b: int) -> int: + %tmp = sdiv i64 %self, %b + ret i64 %tmp + + @pure + @llvm + def __truediv__(self, other: float) -> float: + %0 = sitofp i64 %self to double + %1 = fdiv double %0, %other + ret double %1 + @pure @llvm def __truediv__(self, other: int) -> float: @@ -122,23 +159,20 @@ class int: %1 = sitofp i64 %other to double %2 = fdiv double %0, %1 ret double %2 - @pure - @llvm - def __truediv__(self, other: float) -> float: - %0 = sitofp i64 %self to double - %1 = fdiv double %0, %other - ret double %1 - @pure - @llvm - def __mod__(a: int, b: int) -> int: - %tmp = srem i64 %a, %b - ret i64 %tmp + @pure @llvm def __mod__(self, other: float) -> float: %0 = sitofp i64 %self to double %1 = frem double %0, %other ret double %1 + + @pure + @llvm + def __mod__(a: int, b: int) -> int: + %tmp = srem i64 %a, %b + ret i64 %tmp + def __divmod__(self, other: int): d = self // other m = self - d*other @@ -146,11 +180,13 @@ class int: m += other d -= 1 return (d, m) + @pure @llvm def __invert__(a: int) -> int: %tmp = xor i64 %a, -1 ret i64 %tmp + @pure @commutative @associative @@ -158,6 +194,7 @@ class int: def __and__(a: int, b: int) -> int: %tmp = and i64 %a, %b ret i64 %tmp + @pure @commutative @associative @@ -165,6 +202,7 @@ class int: def __or__(a: int, b: int) -> int: %tmp = or i64 %a, %b ret i64 %tmp + @pure @commutative @associative @@ -172,42 +210,42 @@ class int: def __xor__(a: int, b: int) -> int: %tmp = xor i64 %a, %b ret i64 %tmp + @pure @llvm def __bitreverse__(a: int) -> int: declare i64 @llvm.bitreverse.i64(i64 %a) %tmp = call i64 @llvm.bitreverse.i64(i64 %a) ret i64 %tmp + @pure @llvm def __bswap__(a: int) -> int: declare i64 @llvm.bswap.i64(i64 %a) %tmp = call i64 @llvm.bswap.i64(i64 %a) ret i64 %tmp + @pure @llvm def __ctpop__(a: int) -> int: declare i64 @llvm.ctpop.i64(i64 %a) %tmp = call i64 @llvm.ctpop.i64(i64 %a) ret i64 %tmp + @pure @llvm def __ctlz__(a: int) -> int: declare i64 @llvm.ctlz.i64(i64 %a, i1 %is_zero_undef) %tmp = call i64 @llvm.ctlz.i64(i64 %a, i1 false) ret i64 %tmp + @pure @llvm def __cttz__(a: int) -> int: declare i64 @llvm.cttz.i64(i64 %a, i1 %is_zero_undef) %tmp = call i64 @llvm.cttz.i64(i64 %a, i1 false) ret i64 %tmp - @pure - @llvm - def __eq__(a: int, b: int) -> bool: - %tmp = icmp eq i64 %a, %b - %res = zext i1 %tmp to i8 - ret i8 %res + @pure @llvm def __eq__(self, b: float) -> bool: @@ -215,12 +253,14 @@ class int: %1 = fcmp oeq double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + @pure @llvm - def __ne__(a: int, b: int) -> bool: - %tmp = icmp ne i64 %a, %b + def __eq__(a: int, b: int) -> bool: + %tmp = icmp eq i64 %a, %b %res = zext i1 %tmp to i8 ret i8 %res + @pure @llvm def __ne__(self, b: float) -> bool: @@ -228,12 +268,14 @@ class int: %1 = fcmp one double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + @pure @llvm - def __lt__(a: int, b: int) -> bool: - %tmp = icmp slt i64 %a, %b + def __ne__(a: int, b: int) -> bool: + %tmp = icmp ne i64 %a, %b %res = zext i1 %tmp to i8 ret i8 %res + @pure @llvm def __lt__(self, b: float) -> bool: @@ -241,12 +283,14 @@ class int: %1 = fcmp olt double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + @pure @llvm - def __gt__(a: int, b: int) -> bool: - %tmp = icmp sgt i64 %a, %b + def __lt__(a: int, b: int) -> bool: + %tmp = icmp slt i64 %a, %b %res = zext i1 %tmp to i8 ret i8 %res + @pure @llvm def __gt__(self, b: float) -> bool: @@ -254,12 +298,14 @@ class int: %1 = fcmp ogt double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + @pure @llvm - def __le__(a: int, b: int) -> bool: - %tmp = icmp sle i64 %a, %b + def __gt__(a: int, b: int) -> bool: + %tmp = icmp sgt i64 %a, %b %res = zext i1 %tmp to i8 ret i8 %res + @pure @llvm def __le__(self, b: float) -> bool: @@ -267,12 +313,14 @@ class int: %1 = fcmp ole double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + @pure @llvm - def __ge__(a: int, b: int) -> bool: - %tmp = icmp sge i64 %a, %b + def __le__(a: int, b: int) -> bool: + %tmp = icmp sle i64 %a, %b %res = zext i1 %tmp to i8 ret i8 %res + @pure @llvm def __ge__(self, b: float) -> bool: @@ -280,10 +328,17 @@ class int: %1 = fcmp oge double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 - def __new__(s: str) -> int: - return int._from_str(s, 10) - def __new__(s: str, base: int) -> int: - return int._from_str(s, base) + + @pure + @llvm + def __ge__(a: int, b: int) -> bool: + %tmp = icmp sge i64 %a, %b + %res = zext i1 %tmp to i8 + ret i8 %res + + def __pow__(self, exp: float): + return float(self) ** exp + def __pow__(self, exp: int): if exp < 0: return 0 @@ -296,53 +351,65 @@ class int: break self *= self return result - def __pow__(self, exp: float): - return float(self) ** exp + def popcnt(self): return Int[64](self).popcnt() + @llvm def __atomic_xchg__(d: Ptr[int], b: int) -> void: %tmp = atomicrmw xchg i64* %d, i64 %b seq_cst ret void + @llvm def __atomic_add__(d: Ptr[int], b: int) -> int: %tmp = atomicrmw add i64* %d, i64 %b seq_cst ret i64 %tmp + @llvm def __atomic_sub__(d: Ptr[int], b: int) -> int: %tmp = atomicrmw sub i64* %d, i64 %b seq_cst ret i64 %tmp + @llvm def __atomic_and__(d: Ptr[int], b: int) -> int: %tmp = atomicrmw and i64* %d, i64 %b seq_cst ret i64 %tmp + @llvm def __atomic_nand__(d: Ptr[int], b: int) -> int: %tmp = atomicrmw nand i64* %d, i64 %b seq_cst ret i64 %tmp + @llvm def __atomic_or__(d: Ptr[int], b: int) -> int: %tmp = atomicrmw or i64* %d, i64 %b seq_cst ret i64 %tmp + @llvm def _atomic_xor(d: Ptr[int], b: int) -> int: %tmp = atomicrmw xor i64* %d, i64 %b seq_cst ret i64 %tmp + def __atomic_xor__(self, b: int) -> int: return int._atomic_xor(__ptr__(self), b) + @llvm def __atomic_min__(d: Ptr[int], b: int) -> int: %tmp = atomicrmw min i64* %d, i64 %b seq_cst ret i64 %tmp + @llvm def __atomic_max__(d: Ptr[int], b: int) -> int: %tmp = atomicrmw max i64* %d, i64 %b seq_cst ret i64 %tmp + def __match__(self, i: int): return self == i + @property def real(self): return self + @property def imag(self): return 0 diff --git a/stdlib/internal/types/intn.codon b/stdlib/internal/types/intn.codon index 4ed993da..99b774e7 100644 --- a/stdlib/internal/types/intn.codon +++ b/stdlib/internal/types/intn.codon @@ -10,9 +10,11 @@ class Int: def __new__() -> Int[N]: check_N(N) return Int[N](0) + def __new__(what: Int[N]) -> Int[N]: check_N(N) return what + def __new__(what: int) -> Int[N]: check_N(N) if N < 64: @@ -21,10 +23,12 @@ class Int: return what else: return __internal__.int_sext(what, 64, N) + @pure @llvm def __new__(what: UInt[N]) -> Int[N]: ret i{=N} %what + def __new__(what: str) -> Int[N]: check_N(N) ret = Int[N]() @@ -39,6 +43,7 @@ class Int: ret = ret * Int[N](10) + Int[N](int(what.ptr[i]) - 48) i += 1 return sign * ret + def __int__(self) -> int: if N > 64: return __internal__.int_trunc(self, N, 64) @@ -46,37 +51,47 @@ class Int: return self else: return __internal__.int_sext(self, N, 64) + def __index__(self): return int(self) + def __copy__(self) -> Int[N]: return self + def __deepcopy__(self) -> Int[N]: return self + def __hash__(self) -> int: return int(self) + @pure @llvm def __float__(self) -> float: %0 = sitofp i{=N} %self to double ret double %0 + @pure @llvm def __bool__(self) -> bool: %0 = icmp ne i{=N} %self, 0 %1 = zext i1 %0 to i8 ret i8 %1 + def __pos__(self) -> Int[N]: return self + @pure @llvm def __neg__(self) -> Int[N]: %0 = sub i{=N} 0, %self ret i{=N} %0 + @pure @llvm def __invert__(self) -> Int[N]: %0 = xor i{=N} %self, -1 ret i{=N} %0 + @pure @commutative @associative @@ -84,11 +99,13 @@ class Int: def __add__(self, other: Int[N]) -> Int[N]: %0 = add i{=N} %self, %other ret i{=N} %0 + @pure @llvm def __sub__(self, other: Int[N]) -> Int[N]: %0 = sub i{=N} %self, %other ret i{=N} %0 + @pure @commutative @associative @@ -97,11 +114,13 @@ class Int: def __mul__(self, other: Int[N]) -> Int[N]: %0 = mul i{=N} %self, %other ret i{=N} %0 + @pure @llvm def __floordiv__(self, other: Int[N]) -> Int[N]: %0 = sdiv i{=N} %self, %other ret i{=N} %0 + @pure @llvm def __truediv__(self, other: Int[N]) -> float: @@ -109,11 +128,13 @@ class Int: %1 = sitofp i{=N} %other to double %2 = fdiv double %0, %1 ret double %2 + @pure @llvm def __mod__(self, other: Int[N]) -> Int[N]: %0 = srem i{=N} %self, %other ret i{=N} %0 + def __divmod__(self, other: Int[N]): d = self // other m = self - d*other @@ -121,52 +142,61 @@ class Int: m += other d -= Int[N](1) return (d, m) + @pure @llvm def __lshift__(self, other: Int[N]) -> Int[N]: %0 = shl i{=N} %self, %other ret i{=N} %0 + @pure @llvm def __rshift__(self, other: Int[N]) -> Int[N]: %0 = ashr i{=N} %self, %other ret i{=N} %0 + @pure @llvm def __eq__(self, other: Int[N]) -> bool: %0 = icmp eq i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __ne__(self, other: Int[N]) -> bool: %0 = icmp ne i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __lt__(self, other: Int[N]) -> bool: %0 = icmp slt i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __gt__(self, other: Int[N]) -> bool: %0 = icmp sgt i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __le__(self, other: Int[N]) -> bool: %0 = icmp sle i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __ge__(self, other: Int[N]) -> bool: %0 = icmp sge i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @commutative @associative @@ -174,6 +204,7 @@ class Int: def __and__(self, other: Int[N]) -> Int[N]: %0 = and i{=N} %self, %other ret i{=N} %0 + @pure @commutative @associative @@ -181,6 +212,7 @@ class Int: def __or__(self, other: Int[N]) -> Int[N]: %0 = or i{=N} %self, %other ret i{=N} %0 + @pure @commutative @associative @@ -188,6 +220,7 @@ class Int: def __xor__(self, other: Int[N]) -> Int[N]: %0 = xor i{=N} %self, %other ret i{=N} %0 + @llvm def __pickle__(self, dest: Ptr[byte]) -> void: declare i32 @gzwrite(i8*, i8*, i32) @@ -198,6 +231,7 @@ class Int: %szi = ptrtoint i{=N}* %sz to i32 %2 = call i32 @gzwrite(i8* %dest, i8* %1, i32 %szi) ret void + @llvm def __unpickle__(src: Ptr[byte]) -> Int[N]: declare i32 @gzread(i8*, i8*, i32) @@ -208,18 +242,23 @@ class Int: %2 = call i32 @gzread(i8* %src, i8* %1, i32 %szi) %3 = load i{=N}, i{=N}* %0 ret i{=N} %3 + def __repr__(self) -> str: return str.cat(('Int[', seq_str_int(N), '](', seq_str_int(int(self)), ')')) + def __str__(self) -> str: return seq_str_int(int(self)) + @pure @llvm def _popcnt(self) -> Int[N]: declare i{=N} @llvm.ctpop.i{=N}(i{=N}) %0 = call i{=N} @llvm.ctpop.i{=N}(i{=N} %self) ret i{=N} %0 + def popcnt(self): return int(self._popcnt()) + def len() -> int: return N @@ -228,9 +267,11 @@ class UInt: def __new__() -> UInt[N]: check_N(N) return UInt[N](0) + def __new__(what: UInt[N]) -> UInt[N]: check_N(N) return what + def __new__(what: int) -> UInt[N]: check_N(N) if N < 64: @@ -239,13 +280,16 @@ class UInt: return UInt[N](Int[N](what)) else: return UInt[N](__internal__.int_zext(what, 64, N)) + @pure @llvm def __new__(what: Int[N]) -> UInt[N]: ret i{=N} %what + def __new__(what: str) -> UInt[N]: check_N(N) return UInt[N](Int[N](what)) + def __int__(self) -> int: if N > 64: return __internal__.int_trunc(self, N, 64) @@ -253,37 +297,47 @@ class UInt: return Int[64](self) else: return __internal__.int_zext(self, N, 64) + def __index__(self): return int(self) + def __copy__(self) -> UInt[N]: return self + def __deepcopy__(self) -> UInt[N]: return self + def __hash__(self) -> int: return int(self) + @pure @llvm def __float__(self) -> float: %0 = uitofp i{=N} %self to double ret double %0 + @pure @llvm def __bool__(self) -> bool: %0 = icmp ne i{=N} %self, 0 %1 = zext i1 %0 to i8 ret i8 %1 + def __pos__(self) -> UInt[N]: return self + @pure @llvm def __neg__(self) -> UInt[N]: %0 = sub i{=N} 0, %self ret i{=N} %0 + @pure @llvm def __invert__(self) -> UInt[N]: %0 = xor i{=N} %self, -1 ret i{=N} %0 + @pure @commutative @associative @@ -291,11 +345,13 @@ class UInt: def __add__(self, other: UInt[N]) -> UInt[N]: %0 = add i{=N} %self, %other ret i{=N} %0 + @pure @llvm def __sub__(self, other: UInt[N]) -> UInt[N]: %0 = sub i{=N} %self, %other ret i{=N} %0 + @pure @commutative @associative @@ -304,11 +360,13 @@ class UInt: def __mul__(self, other: UInt[N]) -> UInt[N]: %0 = mul i{=N} %self, %other ret i{=N} %0 + @pure @llvm def __floordiv__(self, other: UInt[N]) -> UInt[N]: %0 = udiv i{=N} %self, %other ret i{=N} %0 + @pure @llvm def __truediv__(self, other: UInt[N]) -> float: @@ -316,59 +374,70 @@ class UInt: %1 = uitofp i{=N} %other to double %2 = fdiv double %0, %1 ret double %2 + @pure @llvm def __mod__(self, other: UInt[N]) -> UInt[N]: %0 = urem i{=N} %self, %other ret i{=N} %0 + def __divmod__(self, other: UInt[N]): return (self // other, self % other) + @pure @llvm def __lshift__(self, other: UInt[N]) -> UInt[N]: %0 = shl i{=N} %self, %other ret i{=N} %0 + @pure @llvm def __rshift__(self, other: UInt[N]) -> UInt[N]: %0 = lshr i{=N} %self, %other ret i{=N} %0 + @pure @llvm def __eq__(self, other: UInt[N]) -> bool: %0 = icmp eq i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __ne__(self, other: UInt[N]) -> bool: %0 = icmp ne i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __lt__(self, other: UInt[N]) -> bool: %0 = icmp ult i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __gt__(self, other: UInt[N]) -> bool: %0 = icmp ugt i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __le__(self, other: UInt[N]) -> bool: %0 = icmp ule i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __ge__(self, other: UInt[N]) -> bool: %0 = icmp uge i{=N} %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @commutative @associative @@ -376,6 +445,7 @@ class UInt: def __and__(self, other: UInt[N]) -> UInt[N]: %0 = and i{=N} %self, %other ret i{=N} %0 + @pure @commutative @associative @@ -383,6 +453,7 @@ class UInt: def __or__(self, other: UInt[N]) -> UInt[N]: %0 = or i{=N} %self, %other ret i{=N} %0 + @pure @commutative @associative @@ -390,6 +461,7 @@ class UInt: def __xor__(self, other: UInt[N]) -> UInt[N]: %0 = xor i{=N} %self, %other ret i{=N} %0 + @llvm def __pickle__(self, dest: Ptr[byte]) -> void: declare i32 @gzwrite(i8*, i8*, i32) @@ -400,6 +472,7 @@ class UInt: %szi = ptrtoint i{=N}* %sz to i32 %2 = call i32 @gzwrite(i8* %dest, i8* %1, i32 %szi) ret void + @llvm def __unpickle__(src: Ptr[byte]) -> UInt[N]: declare i32 @gzread(i8*, i8*, i32) @@ -410,12 +483,16 @@ class UInt: %2 = call i32 @gzread(i8* %src, i8* %1, i32 %szi) %3 = load i{=N}, i{=N}* %0 ret i{=N} %3 + def __repr__(self) -> str: return str.cat(('UInt[', seq_str_int(N), '](', seq_str_uint(int(self)), ')')) + def __str__(self) -> str: return seq_str_uint(int(self)) + def popcnt(self): return int(Int[N](self)._popcnt()) + def len() -> int: return N diff --git a/stdlib/internal/types/optional.codon b/stdlib/internal/types/optional.codon index c9d668e8..022beaa8 100644 --- a/stdlib/internal/types/optional.codon +++ b/stdlib/internal/types/optional.codon @@ -5,29 +5,36 @@ class Optional: return __internal__.opt_tuple_new(T) else: return __internal__.opt_ref_new(T) + def __new__(what: T) -> Optional[T]: if isinstance(T, ByVal): return __internal__.opt_tuple_new_arg(what, T) else: return __internal__.opt_ref_new_arg(what, T) + def __bool__(self) -> bool: if isinstance(T, ByVal): return __internal__.opt_tuple_bool(self, T) else: return __internal__.opt_ref_bool(self, T) + def __invert__(self) -> T: if isinstance(T, ByVal): return __internal__.opt_tuple_invert(self, T) else: return __internal__.opt_ref_invert(self, T) + def __str__(self) -> str: return 'None' if not self else str(~self) + def __repr__(self) -> str: return 'None' if not self else (~self).__repr__() + def __is_optional__(self, other: Optional[T]): if (not self) or (not other): return (not self) and (not other) return self.__invert__() is other.__invert__() + optional = Optional def unwrap[T](opt: Optional[T]) -> T: diff --git a/stdlib/internal/types/ptr.codon b/stdlib/internal/types/ptr.codon index 0dfc8dc6..defdfb37 100644 --- a/stdlib/internal/types/ptr.codon +++ b/stdlib/internal/types/ptr.codon @@ -4,53 +4,63 @@ def seq_str_ptr(a: Ptr[byte]) -> str: pass @extend class Ptr: - @__internal__ - def __new__(sz: int) -> Ptr[T]: - pass @pure @llvm def __new__() -> Ptr[T]: ret {=T}* null + + @__internal__ + def __new__(sz: int) -> Ptr[T]: + pass + + @pure + @llvm + def __new__(other: Ptr[T]) -> Ptr[T]: + ret {=T}* %other + @pure @llvm def __new__(other: Ptr[byte]) -> Ptr[T]: %0 = bitcast i8* %other to {=T}* ret {=T}* %0 - @pure - @llvm - def __new__(other: Ptr[T]) -> Ptr[T]: - ret {=T}* %other + @pure @llvm def __int__(self) -> int: %0 = ptrtoint {=T}* %self to i64 ret i64 %0 + @pure @llvm def __copy__(self) -> Ptr[T]: ret {=T}* %self + @pure @llvm def __bool__(self) -> bool: %0 = icmp ne {=T}* %self, null %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __getitem__(self, index: int) -> T: %0 = getelementptr {=T}, {=T}* %self, i64 %index %1 = load {=T}, {=T}* %0 ret {=T} %1 + @llvm def __setitem__(self, index: int, what: T) -> void: %0 = getelementptr {=T}, {=T}* %self, i64 %index store {=T} %what, {=T}* %0 ret void + @pure @llvm def __add__(self, other: int) -> Ptr[T]: %0 = getelementptr {=T}, {=T}* %self, i64 %other ret {=T}* %0 + @pure @llvm def __sub__(self, other: Ptr[T]) -> int: @@ -59,90 +69,105 @@ class Ptr: %2 = sub i64 %0, %1 %3 = sdiv exact i64 %2, ptrtoint ({=T}* getelementptr ({=T}, {=T}* null, i32 1) to i64) ret i64 %3 + @pure @llvm def __eq__(self, other: Ptr[T]) -> bool: %0 = icmp eq {=T}* %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __ne__(self, other: Ptr[T]) -> bool: %0 = icmp ne {=T}* %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __lt__(self, other: Ptr[T]) -> bool: %0 = icmp slt {=T}* %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __gt__(self, other: Ptr[T]) -> bool: %0 = icmp sgt {=T}* %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __le__(self, other: Ptr[T]) -> bool: %0 = icmp sle {=T}* %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @pure @llvm def __ge__(self, other: Ptr[T]) -> bool: %0 = icmp sge {=T}* %self, %other %1 = zext i1 %0 to i8 ret i8 %1 + @llvm def __prefetch_r0__(self) -> void: declare void @llvm.prefetch(i8* nocapture readonly, i32, i32, i32) %0 = bitcast {=T}* %self to i8* call void @llvm.prefetch(i8* %0, i32 0, i32 0, i32 1) ret void + @llvm def __prefetch_r1__(self) -> void: declare void @llvm.prefetch(i8* nocapture readonly, i32, i32, i32) %0 = bitcast {=T}* %self to i8* call void @llvm.prefetch(i8* %0, i32 0, i32 1, i32 1) ret void + @llvm def __prefetch_r2__(self) -> void: declare void @llvm.prefetch(i8* nocapture readonly, i32, i32, i32) %0 = bitcast {=T}* %self to i8* call void @llvm.prefetch(i8* %0, i32 0, i32 2, i32 1) ret void + @llvm def __prefetch_r3__(self) -> void: declare void @llvm.prefetch(i8* nocapture readonly, i32, i32, i32) %0 = bitcast {=T}* %self to i8* call void @llvm.prefetch(i8* %0, i32 0, i32 3, i32 1) ret void + @llvm def __prefetch_w0__(self) -> void: declare void @llvm.prefetch(i8* nocapture readonly, i32, i32, i32) %0 = bitcast {=T}* %self to i8* call void @llvm.prefetch(i8* %0, i32 1, i32 0, i32 1) ret void + @llvm def __prefetch_w1__(self) -> void: declare void @llvm.prefetch(i8* nocapture readonly, i32, i32, i32) %0 = bitcast {=T}* %self to i8* call void @llvm.prefetch(i8* %0, i32 1, i32 1, i32 1) ret void + @llvm def __prefetch_w2__(self) -> void: declare void @llvm.prefetch(i8* nocapture readonly, i32, i32, i32) %0 = bitcast {=T}* %self to i8* call void @llvm.prefetch(i8* %0, i32 1, i32 2, i32 1) ret void + @llvm def __prefetch_w3__(self) -> void: declare void @llvm.prefetch(i8* nocapture readonly, i32, i32, i32) %0 = bitcast {=T}* %self to i8* call void @llvm.prefetch(i8* %0, i32 1, i32 3, i32 1) ret void + @pure @llvm def as_byte(self) -> Ptr[byte]: diff --git a/stdlib/internal/types/str.codon b/stdlib/internal/types/str.codon index 229eca1c..5cd02b67 100644 --- a/stdlib/internal/types/str.codon +++ b/stdlib/internal/types/str.codon @@ -7,41 +7,52 @@ class str: @__internal__ def __new__(l: int, p: Ptr[byte]) -> str: pass + def __new__(p: Ptr[byte], l: int) -> str: return str(l, p) + def __new__() -> str: return str(Ptr[byte](), 0) - def __new__[T](what: T) -> str: # lowest priority! + + def __new__(what) -> str: if hasattr(what, "__str__"): return what.__str__() else: return what.__repr__() + def __str__(what: str) -> str: return what + def __len__(self) -> int: return self.len + def __bool__(self) -> bool: return self.len != 0 + def __copy__(self) -> str: n = self.len p = cobj(n) str.memcpy(p, self.ptr, n) return str(p, n) + @llvm def memcpy(dest: Ptr[byte], src: Ptr[byte], len: int) -> void: declare void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest, i8* %src, i64 %len, i32 %align, i1 %isvolatile) call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dest, i8* %src, i64 %len, i32 0, i1 false) ret void + @llvm def memmove(dest: Ptr[byte], src: Ptr[byte], len: int) -> void: declare void @llvm.memmove.p0i8.p0i8.i64(i8* %dest, i8* %src, i64 %len, i32 %align, i1 %isvolatile) call void @llvm.memmove.p0i8.p0i8.i64(i8* %dest, i8* %src, i64 %len, i32 0, i1 false) ret void + @llvm def memset(dest: Ptr[byte], val: byte, len: int) -> void: declare void @llvm.memset.p0i8.i64(i8* %dest, i8 %val, i64 %len, i32 %align, i1 %isvolatile) call void @llvm.memset.p0i8.i64(i8* %dest, i8 %val, i64 %len, i32 0, i1 false) ret void + def __add__(self, other: str) -> str: len1 = self.len len2 = other.len @@ -50,17 +61,20 @@ class str: str.memcpy(p, self.ptr, len1) str.memcpy(p + len1, other.ptr, len2) return str(p, len3) + def c_str(self): n = self.__len__() p = cobj(n + 1) str.memcpy(p, self.ptr, n) p[n] = byte(0) return p + def from_ptr(t: cobj) -> str: n = strlen(t) p = Ptr[byte](n) str.memcpy(p, t, n) return str(p, n) + def __eq__(self, other: str): if self.len != other.len: return False @@ -70,10 +84,13 @@ class str: return False i += 1 return True + def __match__(self, other: str): return self.__eq__(other) + def __ne__(self, other: str): return not self.__eq__(other) + def cat(*args): total = 0 if staticlen(args) == 1 and hasattr(args[0], "__iter__") and hasattr(args[0], "__len__"): diff --git a/stdlib/statistics.codon b/stdlib/statistics.codon index d11622d3..0d3a440a 100644 --- a/stdlib/statistics.codon +++ b/stdlib/statistics.codon @@ -394,22 +394,10 @@ class NormalDist: self._mu = mu self._sigma = sigma - def __init__(self, mu: float, sigma: float): + def __init__(self, mu, sigma): self._init(float(mu), float(sigma)) - def __init__(self, mu: int, sigma: int): - self._init(float(mu), float(sigma)) - - def __init__(self, mu: float, sigma: int): - self._init(float(mu), float(sigma)) - - def __init__(self, mu: int, sigma: float): - self._init(float(mu), float(sigma)) - - def __init__(self, mu: float): - self._init(mu, 1.0) - - def __init__(self, mu: int): + def __init__(self, mu): self._init(float(mu), 1.0) def __init__(self): From a68e95fb2b8b5484fc25619ffab1ea2644f4d5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Sun, 12 Dec 2021 11:30:57 -0800 Subject: [PATCH 04/61] Select the last matching overload by default [wip] --- codon/compiler/compiler.cpp | 13 ++++++++++ codon/parser/peg/grammar.peg | 2 +- codon/parser/visitors/simplify/simplify.h | 3 +++ .../parser/visitors/simplify/simplify_ctx.cpp | 4 ++-- .../visitors/simplify/simplify_stmt.cpp | 21 ++++++++++------ .../visitors/typecheck/typecheck_ctx.cpp | 13 ++++------ .../visitors/typecheck/typecheck_expr.cpp | 24 +++++++++---------- stdlib/collections.codon | 2 ++ stdlib/internal/sort.codon | 8 +++---- stdlib/internal/types/array.codon | 11 +++++++++ stdlib/internal/types/ptr.codon | 5 ++++ test/parser/typecheck_expr.codon | 17 ++++++------- 12 files changed, 80 insertions(+), 43 deletions(-) diff --git a/codon/compiler/compiler.cpp b/codon/compiler/compiler.cpp index 55e8153f..2c920274 100644 --- a/codon/compiler/compiler.cpp +++ b/codon/compiler/compiler.cpp @@ -68,11 +68,24 @@ Compiler::parse(bool isCode, const std::string &file, const std::string &code, auto transformed = ast::SimplifyVisitor::apply(cache.get(), std::move(codeStmt), abspath, defines, (testFlags > 1)); t2.log(); + if (codon::getLogger().flags & codon::Logger::FLAG_USER) { + auto fo = fopen("_dump_simplify.sexp", "w"); + fmt::print(fo, "{}\n", transformed->toString(0)); + fclose(fo); + } Timer t3("typecheck"); auto typechecked = ast::TypecheckVisitor::apply(cache.get(), std::move(transformed)); t3.log(); + if (codon::getLogger().flags & codon::Logger::FLAG_USER) { + auto fo = fopen("_dump_typecheck.sexp", "w"); + fmt::print(fo, "{}\n", typechecked->toString(0)); + for (auto &f : cache->functions) + for (auto &r : f.second.realizations) + fmt::print(fo, "{}\n", r.second->ast->toString(0)); + fclose(fo); + } Timer t4("translate"); ast::TranslateVisitor::apply(cache.get(), std::move(typechecked)); diff --git a/codon/parser/peg/grammar.peg b/codon/parser/peg/grammar.peg index ad292a2b..656bff18 100644 --- a/codon/parser/peg/grammar.peg +++ b/codon/parser/peg/grammar.peg @@ -253,7 +253,7 @@ with_stmt <- 'with' SPACE (with_parens_item / with_item) _ ':' _ suite { with_parens_item <- '(' _ tlist(',', as_item) _ ')' { return VS; } with_item <- list(',', as_item) { return VS; } as_item <- - / expression SPACE 'as' SPACE star_target &(_ (',' / ')' / ':')) { + / expression SPACE 'as' SPACE id &(_ (',' / ')' / ':')) { return pair(ac_expr(V0), ac_expr(V1)); } / expression { return pair(ac_expr(V0), (ExprPtr)nullptr); } diff --git a/codon/parser/visitors/simplify/simplify.h b/codon/parser/visitors/simplify/simplify.h index 7060ad46..3c5838cd 100644 --- a/codon/parser/visitors/simplify/simplify.h +++ b/codon/parser/visitors/simplify/simplify.h @@ -488,6 +488,9 @@ private: // suite recursively, and assumes that each statement is either a function or a // doc-string. std::vector getClassMethods(const StmtPtr &s); + + // Generate dispatch method for partial overloaded calls. + void generateDispatch(const std::string &name); }; } // namespace ast diff --git a/codon/parser/visitors/simplify/simplify_ctx.cpp b/codon/parser/visitors/simplify/simplify_ctx.cpp index 307ac1c8..3a587995 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.cpp +++ b/codon/parser/visitors/simplify/simplify_ctx.cpp @@ -76,8 +76,8 @@ std::string SimplifyContext::generateCanonicalName(const std::string &name, } auto num = cache->identifierCount[newName]++; newName = num || zeroId ? format("{}.{}", newName, num) : newName; - if (newName != name) - cache->identifierCount[newName]++; + // if (newName != name) + // cache->identifierCount[newName]++; cache->reverseIdentifierLookup[newName] = name; return newName; } diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 26e3b25d..20dd5282 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -475,12 +475,7 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { bool isClassMember = ctx->inClass(); if (isClassMember && !endswith(stmt->name, ".dispatch") && ctx->cache->classes[ctx->bases.back().name].methods[stmt->name].empty()) { - transform( - N(stmt->name + ".dispatch", nullptr, - std::vector{Param("*args")}, - N(N(N( - N(N(ctx->bases.back().name), stmt->name), - N(N("args"))))))); + generateDispatch(stmt->name); } auto func_name = stmt->name; if (endswith(stmt->name, ".dispatch")) @@ -956,9 +951,13 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { if (auto f = sp->getFunction()) { if (f->attributes.has("autogenerated")) continue; + auto subs = substitutions[ai]; + if (ctx->cache->classes[ctx->bases.back().name] + .methods[ctx->cache->reverseIdentifierLookup[f->name]].empty()) + generateDispatch(ctx->cache->reverseIdentifierLookup[f->name]); auto newName = ctx->generateCanonicalName( - ctx->cache->reverseIdentifierLookup[f->name], true, true); + ctx->cache->reverseIdentifierLookup[f->name], true); auto nf = std::dynamic_pointer_cast(replace(sp, subs)); subs[nf->name] = N(newName); nf->name = newName; @@ -1764,5 +1763,13 @@ std::vector SimplifyVisitor::getClassMethods(const StmtPtr &s) { return v; } +void SimplifyVisitor::generateDispatch(const std::string &name) { + transform(N( + name + ".dispatch", nullptr, std::vector{Param("*args")}, + N( + N(N(N(N(ctx->bases.back().name), name), + N(N("args"))))))); +} + } // namespace ast } // namespace codon diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.cpp b/codon/parser/visitors/typecheck/typecheck_ctx.cpp index d1c72784..032d0bc9 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.cpp +++ b/codon/parser/visitors/typecheck/typecheck_ctx.cpp @@ -142,18 +142,15 @@ TypeContext::findMethod(const std::string &typeName, const std::string &method) auto t = m->second.methods.find(method); if (t != m->second.methods.end()) { seqassert(!t->second.empty() && endswith(t->second[0].name, ".dispatch"), - "first method is not dispatch"); - std::unordered_map signatureLoci; + "first method '{}' is not dispatch", t->second[0].name); + std::unordered_set signatureLoci; std::vector vv; - for (int mti = 1; mti < t->second.size(); mti++) { + for (int mti = int(t->second.size()) - 1; mti > 0; mti--) { auto &mt = t->second[mti]; if (mt.age <= age) { auto sig = cache->functions[mt.name].ast->signature(); - auto it = signatureLoci.find(sig); - if (it != signatureLoci.end()) - vv[it->second] = mt.type; - else { - signatureLoci[sig] = vv.size(); + if (!in(signatureLoci, sig)) { + signatureLoci.insert(sig); vv.emplace_back(mt.type); } } diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index b9201490..7320047a 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -731,7 +731,8 @@ ExprPtr TypecheckVisitor::transformStaticTupleIndex(ClassType *tuple, ExprPtr &e in(std::set{"Ptr", "pyobj", "str", "Array"}, tuple->name)) // Ptr, pyobj and str are internal types and have only one overloaded __getitem__ return nullptr; - if (ctx->cache->classes[tuple->name].methods["__getitem__"].size() != 1) + if (ctx->cache->classes[tuple->name].methods["__getitem__"].size() != 2) + // n.b.: there is dispatch as well // TODO: be smarter! there might be a compatible getitem? return nullptr; @@ -914,12 +915,14 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, error("cannot find a method '{}' in {} with arguments {}", expr->member, typ->toString(), join(nice, ", ")); } - } else { + } else if (methods.size() > 1) { auto m = ctx->cache->classes.find(typ->name); auto t = m->second.methods.find(expr->member); seqassert(!t->second.empty() && endswith(t->second[0].name, ".dispatch"), - "first method is not dispatch"); + "first method is not dispatch"); bestMethod = t->second[0].type; + } else { + bestMethod = methods[0]; } // Case 7: only one valid method remaining. Check if this is a class method or an @@ -1618,10 +1621,7 @@ types::FuncTypePtr TypecheckVisitor::findBestMethod( // Pick the last method that accepts the given arguments. auto methods = ctx->findMethod(typ->name, member); - // if (methods.size() == 1) - // return methods[0]; - types::FuncTypePtr method = nullptr; - for (int mi = int(methods.size()) - 1; mi >= 0; mi--) { + for (int mi = 0; mi < methods.size(); mi++) { auto m = ctx->instantiate(expr, methods[mi], typ.get(), false)->getFunc(); std::vector reordered; std::vector callArgs; @@ -1645,12 +1645,13 @@ types::FuncTypePtr TypecheckVisitor::findBestMethod( return 0; }, [](const std::string &) { return -1; }); + for (int ai = 0, mi = 1, gi = 0; score != -1 && ai < reordered.size(); ai++) { + auto expectTyp = + m->ast->args[ai].generic ? m->generics[gi++].type : m->args[mi++]; auto argType = reordered[ai]; if (!argType) continue; - auto expectTyp = - m->ast->args[ai].generic ? m->generics[gi++].type : m->args[mi++]; try { ExprPtr dummy = std::make_shared(""); dummy->type = argType; @@ -1667,11 +1668,10 @@ types::FuncTypePtr TypecheckVisitor::findBestMethod( // else ar.push_back(format("{}: {}", a.first, a.second->toString())); // } // LOG("- {} vs {}", m->toString(), join(ar, "; ")); - method = methods[mi]; - break; + return methods[mi]; } } - return method; + return nullptr; } bool TypecheckVisitor::wrapExpr(ExprPtr &expr, TypePtr expectedType, diff --git a/stdlib/collections.codon b/stdlib/collections.codon index fc18a627..b5a1a21a 100644 --- a/stdlib/collections.codon +++ b/stdlib/collections.codon @@ -339,10 +339,12 @@ class Counter[T](Dict[T,int]): result |= other return result + @extend class Dict: def __init__(self: Dict[K,int], other: Counter[K]): self._init_from(other) + def namedtuple(): # internal pass diff --git a/stdlib/internal/sort.codon b/stdlib/internal/sort.codon index fa46a459..5ca675cf 100644 --- a/stdlib/internal/sort.codon +++ b/stdlib/internal/sort.codon @@ -1,14 +1,14 @@ -from algorithms.timsort import tim_sort_inplace from algorithms.pdqsort import pdq_sort_inplace from algorithms.insertionsort import insertion_sort_inplace from algorithms.heapsort import heap_sort_inplace from algorithms.qsort import qsort_inplace -def sorted[T]( +def sorted( v: Generator[T], key = Optional[int](), algorithm: Optional[str] = None, - reverse: bool = False + reverse: bool = False, + T: type ): """ Return a sorted list of the elements in v @@ -27,8 +27,6 @@ def _sort_list(self, key, algorithm: str): insertion_sort_inplace(self, key) elif algorithm == 'heap': heap_sort_inplace(self, key) - #case 'tim': - # tim_sort_inplace(self, key) elif algorithm == 'quick': qsort_inplace(self, key) else: diff --git a/stdlib/internal/types/array.codon b/stdlib/internal/types/array.codon index 834db41b..6f883dd8 100644 --- a/stdlib/internal/types/array.codon +++ b/stdlib/internal/types/array.codon @@ -1,15 +1,19 @@ from internal.gc import sizeof + @extend class Array: def __new__(ptr: Ptr[T], sz: int) -> Array[T]: return (sz, ptr) + def __new__(sz: int) -> Array[T]: return (sz, Ptr[T](sz)) + def __copy__(self) -> Array[T]: p = Ptr[T](self.len) str.memcpy(p.as_byte(), self.ptr.as_byte(), self.len * sizeof(T)) return (self.len, p) + def __deepcopy__(self) -> Array[T]: p = Ptr[T](self.len) i = 0 @@ -17,14 +21,21 @@ class Array: p[i] = self.ptr[i].__deepcopy__() i += 1 return (self.len, p) + def __len__(self) -> int: return self.len + def __bool__(self) -> bool: return bool(self.len) + def __getitem__(self, index: int) -> T: return self.ptr[index] + def __setitem__(self, index: int, what: T): self.ptr[index] = what + def slice(self, s: int, e: int) -> Array[T]: return (e - s, self.ptr + s) + + array = Array diff --git a/stdlib/internal/types/ptr.codon b/stdlib/internal/types/ptr.codon index defdfb37..28b3a24a 100644 --- a/stdlib/internal/types/ptr.codon +++ b/stdlib/internal/types/ptr.codon @@ -176,20 +176,25 @@ class Ptr: def __repr__(self) -> str: return seq_str_ptr(self.as_byte()) + ptr = Ptr Jar = Ptr[byte] cobj = Ptr[byte] + # Forward declarations @__internal__ @tuple class Array[T]: len: int ptr: Ptr[T] + + class List[T]: arr: Array[T] len: int + @extend class NoneType: def __new__() -> NoneType: diff --git a/test/parser/typecheck_expr.codon b/test/parser/typecheck_expr.codon index dedc61de..b4376e3f 100644 --- a/test/parser/typecheck_expr.codon +++ b/test/parser/typecheck_expr.codon @@ -256,21 +256,22 @@ a = [5] a.foo #! cannot find 'foo' in List[int] #%% dot_case_6,barebones +# Did heavy changes to this testcase because +# of the automatic optional wraps/unwraps and promotions class Foo: - def bar(self, a: int): - print 'normal', a - def bar(self, a: Optional[int]): - print 'optional', a - def bar[T](self, a: Optional[T]): - print 'optional generic', a, a.__class__ def bar(self, a): print 'generic', a, a.__class__ + def bar(self, a: Optional[float]): + print 'optional', a + def bar(self, a: int): + print 'normal', a f = Foo() f.bar(1) #: normal 1 -f.bar(Optional(1)) #: optional 1 -f.bar(Optional('s')) #: optional generic s Optional[str] +f.bar(1.1) #: optional 1.1 +f.bar(Optional('s')) #: generic s Optional[str] f.bar('hehe') #: generic hehe str + #%% dot_case_6b,barebones class Foo: def bar(self, a, b): From 3d6090322db175465e4a3bfc3bb8757b815870fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Wed, 15 Dec 2021 11:44:17 -0800 Subject: [PATCH 05/61] Fix various bugs and update tests --- codon/parser/cache.cpp | 5 +- codon/parser/cache.h | 5 +- .../parser/visitors/simplify/simplify_ctx.cpp | 10 +- .../visitors/simplify/simplify_expr.cpp | 2 +- codon/parser/visitors/typecheck/typecheck.h | 7 +- .../visitors/typecheck/typecheck_ctx.cpp | 2 +- .../visitors/typecheck/typecheck_expr.cpp | 65 +++--- .../visitors/typecheck/typecheck_stmt.cpp | 6 +- codon/sir/module.cpp | 6 +- stdlib/internal/types/complex.codon | 8 +- test/parser/simplify_stmt.codon | 6 +- test/parser/typecheck_expr.codon | 2 +- test/parser/types.codon | 13 +- test/transform/folding.codon | 218 ++++++++++++------ 14 files changed, 224 insertions(+), 131 deletions(-) diff --git a/codon/parser/cache.cpp b/codon/parser/cache.cpp index d5b89cd0..4d774c1b 100644 --- a/codon/parser/cache.cpp +++ b/codon/parser/cache.cpp @@ -55,9 +55,8 @@ types::FuncTypePtr Cache::findFunction(const std::string &name) const { return nullptr; } -types::FuncTypePtr -Cache::findMethod(types::ClassType *typ, const std::string &member, - const std::vector> &args) { +types::FuncTypePtr Cache::findMethod(types::ClassType *typ, const std::string &member, + const std::vector &args) { auto e = std::make_shared(typ->name); e->type = typ->getClass(); seqassert(e->type, "not a class"); diff --git a/codon/parser/cache.h b/codon/parser/cache.h index 2c32c8bb..16db95cc 100644 --- a/codon/parser/cache.h +++ b/codon/parser/cache.h @@ -223,9 +223,8 @@ public: types::FuncTypePtr findFunction(const std::string &name) const; /// Find the class method in a given class type that best matches the given arguments. /// Returns an _uninstantiated_ type. - types::FuncTypePtr - findMethod(types::ClassType *typ, const std::string &member, - const std::vector> &args); + types::FuncTypePtr findMethod(types::ClassType *typ, const std::string &member, + const std::vector &args); /// Given a class type and the matching generic vector, instantiate the type and /// realize it. diff --git a/codon/parser/visitors/simplify/simplify_ctx.cpp b/codon/parser/visitors/simplify/simplify_ctx.cpp index 3a587995..3f87dec2 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.cpp +++ b/codon/parser/visitors/simplify/simplify_ctx.cpp @@ -64,7 +64,8 @@ std::string SimplifyContext::generateCanonicalName(const std::string &name, bool includeBase, bool zeroId) const { std::string newName = name; - if (includeBase && name.find('.') == std::string::npos) { + bool alreadyGenerated = name.find('.') != std::string::npos; + if (includeBase && !alreadyGenerated) { std::string base = getBase(); if (base.empty()) { base = moduleName.status == ImportFile::STDLIB ? "std." : ""; @@ -75,9 +76,10 @@ std::string SimplifyContext::generateCanonicalName(const std::string &name, newName = (base.empty() ? "" : (base + ".")) + newName; } auto num = cache->identifierCount[newName]++; - newName = num || zeroId ? format("{}.{}", newName, num) : newName; - // if (newName != name) - // cache->identifierCount[newName]++; + if (num) + newName = format("{}.{}", newName, num); + if (name != newName && !zeroId) + cache->identifierCount[newName]++; cache->reverseIdentifierLookup[newName] = name; return newName; } diff --git a/codon/parser/visitors/simplify/simplify_expr.cpp b/codon/parser/visitors/simplify/simplify_expr.cpp index 68a998e6..575dab70 100644 --- a/codon/parser/visitors/simplify/simplify_expr.cpp +++ b/codon/parser/visitors/simplify/simplify_expr.cpp @@ -617,7 +617,7 @@ void SimplifyVisitor::visit(DotExpr *expr) { auto s = join(chain, ".", importEnd, i + 1); val = fctx->find(s); // Make sure that we access only global imported variables. - if (val && (importName.empty() || val->isGlobal())) { + if (val && (importName.empty() || val->isType() || val->isGlobal())) { itemName = val->canonicalName; itemEnd = i + 1; if (!importName.empty()) diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index fc75f4f4..e96e3d33 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -289,9 +289,10 @@ private: /// Also takes care of the Optional arguments. /// If multiple equally good methods are found, return the first one. /// Return nullptr if no methods were found. - types::FuncTypePtr - findBestMethod(const Expr *expr, const std::string &member, - const std::vector> &args); + types::FuncTypePtr findBestMethod(const Expr *expr, const std::string &member, + const std::vector &args); + types::FuncTypePtr findBestMethod(const Expr *expr, const std::string &member, + const std::vector &args); private: types::TypePtr unify(types::TypePtr &a, const types::TypePtr &b, diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.cpp b/codon/parser/visitors/typecheck/typecheck_ctx.cpp index 032d0bc9..e463618e 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.cpp +++ b/codon/parser/visitors/typecheck/typecheck_ctx.cpp @@ -140,7 +140,7 @@ TypeContext::findMethod(const std::string &typeName, const std::string &method) auto m = cache->classes.find(typeName); if (m != cache->classes.end()) { auto t = m->second.methods.find(method); - if (t != m->second.methods.end()) { + if (t != m->second.methods.end() && !t->second.empty()) { seqassert(!t->second.empty() && endswith(t->second[0].name, ".dispatch"), "first method '{}' is not dispatch", t->second[0].name); std::unordered_set signatureLoci; diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 7320047a..09f63c99 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -683,8 +683,8 @@ ExprPtr TypecheckVisitor::transformBinary(BinaryExpr *expr, bool isAtomic, if (isAtomic) { auto ptrlt = ctx->instantiateGeneric(expr->lexpr.get(), ctx->findInternal("Ptr"), {lt}); - method = findBestMethod(expr->lexpr.get(), format("__atomic_{}__", magic), - {{"", ptrlt}, {"", rt}}); + method = + findBestMethod(expr->lexpr.get(), format("__atomic_{}__", magic), {ptrlt, rt}); if (method) { expr->lexpr = N(expr->lexpr); if (noReturn) @@ -693,19 +693,16 @@ ExprPtr TypecheckVisitor::transformBinary(BinaryExpr *expr, bool isAtomic, } // Check if lt.__iop__(lt, rt) exists. if (!method && expr->inPlace) { - method = findBestMethod(expr->lexpr.get(), format("__i{}__", magic), - {{"", lt}, {"", rt}}); + method = findBestMethod(expr->lexpr.get(), format("__i{}__", magic), {lt, rt}); if (method && noReturn) *noReturn = true; } // Check if lt.__op__(lt, rt) exists. if (!method) - method = findBestMethod(expr->lexpr.get(), format("__{}__", magic), - {{"", lt}, {"", rt}}); + method = findBestMethod(expr->lexpr.get(), format("__{}__", magic), {lt, rt}); // Check if rt.__rop__(rt, lt) exists. if (!method) { - method = findBestMethod(expr->rexpr.get(), format("__r{}__", magic), - {{"", rt}, {"", lt}}); + method = findBestMethod(expr->rexpr.get(), format("__r{}__", magic), {rt, lt}); if (method) swap(expr->lexpr, expr->rexpr); } @@ -868,12 +865,15 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, // If it exists, return a simple IdExpr with that method's name. // Append a "self" variable to the front if needed. if (args) { - std::vector> argTypes; + std::vector argTypes; bool isType = expr->expr->isType(); - if (!isType) - argTypes.emplace_back(make_pair("", typ)); // self variable + if (!isType) { + ExprPtr expr = N("self"); + expr->setType(typ); + argTypes.emplace_back(CallExpr::Arg{"", expr}); + } for (const auto &a : *args) - argTypes.emplace_back(make_pair(a.name, a.value->getType())); + argTypes.emplace_back(a); if (auto bestMethod = findBestMethod(expr->expr.get(), expr->member, argTypes)) { ExprPtr e = N(bestMethod->ast->name); auto t = ctx->instantiate(expr, bestMethod, typ.get()); @@ -891,7 +891,7 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, // No method was found, print a nice error message. std::vector nice; for (auto &t : argTypes) - nice.emplace_back(format("{} = {}", t.first, t.second->toString())); + nice.emplace_back(format("{} = {}", t.name, t.value->type->toString())); error("cannot find a method '{}' in {} with arguments {}", expr->member, typ->toString(), join(nice, ", ")); } @@ -901,17 +901,17 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, auto oldType = expr->getType() ? expr->getType()->getClass() : nullptr; if (methods.size() > 1 && oldType && oldType->getFunc()) { // If old type is already a function, use its arguments to pick the best call. - std::vector> methodArgs; + std::vector methodArgs; if (!expr->expr->isType()) // self argument - methodArgs.emplace_back(make_pair("", typ)); + methodArgs.emplace_back(typ); for (auto i = 1; i < oldType->generics.size(); i++) - methodArgs.emplace_back(make_pair("", oldType->generics[i].type)); + methodArgs.emplace_back(oldType->generics[i].type); bestMethod = findBestMethod(expr->expr.get(), expr->member, methodArgs); if (!bestMethod) { // Print a nice error message. std::vector nice; for (auto &t : methodArgs) - nice.emplace_back(format("{} = {}", t.first, t.second->toString())); + nice.emplace_back(format("{}", t->toString())); error("cannot find a method '{}' in {} with arguments {}", expr->member, typ->toString(), join(nice, ", ")); } @@ -1360,12 +1360,12 @@ std::pair TypecheckVisitor::transformSpecialCall(CallExpr *expr) if (!typ || !expr->args[1].value->staticValue.evaluated) return {true, nullptr}; auto member = expr->args[1].value->staticValue.getString(); - std::vector> args{{std::string(), typ}}; + std::vector args{typ}; for (int i = 2; i < expr->args.size(); i++) { expr->args[i].value = transformType(expr->args[i].value); if (!expr->args[i].value->getType()->getClass()) return {true, nullptr}; - args.push_back({std::string(), expr->args[i].value->getType()}); + args.push_back(expr->args[i].value->getType()); } bool exists = !ctx->findMethod(typ->getClass()->name, member).empty() || ctx->findMember(typ->getClass()->name, member); @@ -1613,9 +1613,20 @@ ExprPtr TypecheckVisitor::partializeFunction(ExprPtr expr) { return call; } -types::FuncTypePtr TypecheckVisitor::findBestMethod( - const Expr *expr, const std::string &member, - const std::vector> &args) { +types::FuncTypePtr +TypecheckVisitor::findBestMethod(const Expr *expr, const std::string &member, + const std::vector &args) { + std::vector callArgs; + for (auto &a : args) { + callArgs.push_back({"", std::make_shared()}); // dummy expression + callArgs.back().value->setType(a); + } + return findBestMethod(expr, member, callArgs); +} + +types::FuncTypePtr +TypecheckVisitor::findBestMethod(const Expr *expr, const std::string &member, + const std::vector &args) { auto typ = expr->getType()->getClass(); seqassert(typ, "not a class"); @@ -1624,13 +1635,8 @@ types::FuncTypePtr TypecheckVisitor::findBestMethod( for (int mi = 0; mi < methods.size(); mi++) { auto m = ctx->instantiate(expr, methods[mi], typ.get(), false)->getFunc(); std::vector reordered; - std::vector callArgs; - for (auto &a : args) { - callArgs.push_back({a.first, std::make_shared()}); // dummy expression - callArgs.back().value->setType(a.second); - } auto score = ctx->reorderNamedArgs( - m.get(), callArgs, + m.get(), args, [&](int s, int k, const std::vector> &slots, bool _) { for (int si = 0; si < slots.size(); si++) { if (m->ast->args[si].generic) { @@ -1639,13 +1645,12 @@ types::FuncTypePtr TypecheckVisitor::findBestMethod( // Ignore *args, *kwargs and default arguments reordered.emplace_back(nullptr); } else { - reordered.emplace_back(args[slots[si][0]].second); + reordered.emplace_back(args[slots[si][0]].value->type); } } return 0; }, [](const std::string &) { return -1; }); - for (int ai = 0, mi = 1, gi = 0; score != -1 && ai < reordered.size(); ai++) { auto expectTyp = m->ast->args[ai].generic ? m->generics[gi++].type : m->args[mi++]; diff --git a/codon/parser/visitors/typecheck/typecheck_stmt.cpp b/codon/parser/visitors/typecheck/typecheck_stmt.cpp index 5f08fb94..edbe2ad2 100644 --- a/codon/parser/visitors/typecheck/typecheck_stmt.cpp +++ b/codon/parser/visitors/typecheck/typecheck_stmt.cpp @@ -155,7 +155,7 @@ void TypecheckVisitor::visit(UpdateStmt *stmt) { auto rhsTyp = c->args[1].value->getType()->getClass(); if (auto method = findBestMethod(stmt->lhs.get(), format("__atomic_{}__", c->expr->getId()->value), - {{"", ptrTyp}, {"", rhsTyp}})) { + {ptrTyp, rhsTyp})) { resultStmt = transform(N(N( N(method->ast->name), N(stmt->lhs), c->args[1].value))); return; @@ -168,8 +168,8 @@ void TypecheckVisitor::visit(UpdateStmt *stmt) { if (stmt->isAtomic && lhsClass && rhsClass) { auto ptrType = ctx->instantiateGeneric(stmt->lhs.get(), ctx->findInternal("Ptr"), {lhsClass}); - if (auto m = findBestMethod(stmt->lhs.get(), "__atomic_xchg__", - {{"", ptrType}, {"", rhsClass}})) { + if (auto m = + findBestMethod(stmt->lhs.get(), "__atomic_xchg__", {ptrType, rhsClass})) { resultStmt = transform(N( N(N(m->ast->name), N(stmt->lhs), stmt->rhs))); return; diff --git a/codon/sir/module.cpp b/codon/sir/module.cpp index db42b406..e01e8808 100644 --- a/codon/sir/module.cpp +++ b/codon/sir/module.cpp @@ -22,12 +22,12 @@ translateGenerics(std::vector &generics) { return ret; } -std::vector> +std::vector generateDummyNames(std::vector &types) { - std::vector> ret; + std::vector ret; for (auto *t : types) { seqassert(t->getAstType(), "{} must have an ast type", *t); - ret.emplace_back("", t->getAstType()); + ret.emplace_back(t->getAstType()); } return ret; } diff --git a/stdlib/internal/types/complex.codon b/stdlib/internal/types/complex.codon index 7e4811c6..304a875a 100644 --- a/stdlib/internal/types/complex.codon +++ b/stdlib/internal/types/complex.codon @@ -3,14 +3,14 @@ class complex: real: float imag: float - def __new__(): - return complex(0.0, 0.0) + def __new__() -> complex: + return (0.0, 0.0) def __new__(other): return other.__complex__() - def __new__(real, imag): - return complex(float(real), float(imag)) + def __new__(real, imag) -> complex: + return (float(real), float(imag)) def __complex__(self): return self diff --git a/test/parser/simplify_stmt.codon b/test/parser/simplify_stmt.codon index 3fe932bf..28da102a 100644 --- a/test/parser/simplify_stmt.codon +++ b/test/parser/simplify_stmt.codon @@ -558,7 +558,7 @@ print f.foo() #: F class Foo: def foo(self): return 'F' -Foo.foo(1) #! cannot unify int and Foo +Foo.foo(1) #! cannot find a method 'foo' in Foo with arguments = int #%% function_nested,barebones def foo(v): @@ -941,12 +941,12 @@ print FooBarBaz[str]().foo() #: foo 0 print FooBarBaz[float]().bar() #: bar 0/float print FooBarBaz[str]().baz() #: baz! foo 0 bar /str -#%% inherit_class_2,barebones +#%% inherit_class_err_2,barebones class defdict(Dict[str,float]): def __init__(self, d: Dict[str, float]): self.__init__(d.items()) z = defdict() -z[1.1] #! cannot unify float and str +z[1.1] #! cannot find a method '__getitem__' in defdict with arguments = defdict, = float #%% inherit_tuple,barebones class Foo: diff --git a/test/parser/typecheck_expr.codon b/test/parser/typecheck_expr.codon index b4376e3f..faeb6497 100644 --- a/test/parser/typecheck_expr.codon +++ b/test/parser/typecheck_expr.codon @@ -306,7 +306,7 @@ class Foo: print 'foo' def method(self, a): print a -Foo().clsmethod() #! too many arguments for Foo.clsmethod (expected maximum 0, got 1) +Foo().clsmethod() #! cannot find a method 'clsmethod' in Foo with arguments = Foo #%% call,barebones def foo(a, b, c='hi'): diff --git a/test/parser/types.codon b/test/parser/types.codon index a90894cb..764a38a9 100644 --- a/test/parser/types.codon +++ b/test/parser/types.codon @@ -298,7 +298,7 @@ print h(list(map(lambda i: i-1, map(lambda i: i+2, range(5))))) #%% func_unify_error,barebones def foo(x:int): print x -z = 1 & foo #! cannot unify foo[...] and int +z = 1 & foo #! cannot find magic 'and' in int #%% void_error,barebones def foo(): @@ -515,7 +515,7 @@ class A[T]: def foo[W](t: V, u: V, v: V, w: W): return (t, u, v, w) -print A.B.C[bool].foo(W=str, ...).__class__ #: A.B.C.foo[bool,bool,bool,str,str] +print A.B.C[bool].foo(W=str, ...).__class__ #: A.B.C.foo.2[bool,bool,bool,str,str] print A.B.C.foo(1,1,1,True) #: (1, 1, 1, True) print A.B.C.foo('x', 'x', 'x', 'x') #: ('x', 'x', 'x', 'x') print A.B.C.foo('x', 'x', 'x', 'x') #: ('x', 'x', 'x', 'x') @@ -533,7 +533,8 @@ class A[T]: c: V def foo[W](t: V, u: V, v: V, w: W): return (t, u, v, w) -print A.B.C[str].foo(1,1,1,True) #! cannot unify int and str + +print A.B.C[str].foo(1,1,1,True) #! cannot find a method 'foo' in A.B.C[str] with arguments = int, = int, = int, = bool #%% nested_deep_class_error_2,barebones class A[T]: @@ -744,8 +745,8 @@ class X[T]: return (x+x, y+y) y = X[X[int]]() print y.__class__ #: X[X[int]] -print X[float].foo(U=int, ...).__class__ #: X.foo[X[float],float,int,int] -# print y.foo[float].__class__ +print X[float].foo(U=int, ...).__class__ #: X.foo.2[X[float],float,int,int] +# print y.foo.1[float].__class__ print X[int]().foo(1, 's') #: (2, 'ss') #%% class_partial_access,barebones @@ -753,7 +754,7 @@ class X[T]: def foo[U](self, x, y: U): return (x+x, y+y) y = X[X[int]]() -print y.foo(U=float,...).__class__ #: X.foo[X[X[int]],...,...] +print y.foo(U=float,...).__class__ #: X.foo.2[X[X[int]],...,...] print y.foo(1, 2.2, float) #: (2, 4.4) #%% forward,barebones diff --git a/test/transform/folding.codon b/test/transform/folding.codon index 5d5820ed..a7abb34b 100644 --- a/test/transform/folding.codon +++ b/test/transform/folding.codon @@ -10,60 +10,69 @@ class I: def __float__(self: int) -> float: %tmp = sitofp i64 %self to double ret double %tmp + @llvm def __bool__(self: int) -> bool: %0 = icmp ne i64 %self, 0 %1 = zext i1 %0 to i8 ret i8 %1 + def __pos__(self: int) -> int: return self + def __neg__(self: int) -> int: return I.__sub__(0, self) + @llvm def __abs__(self: int) -> int: %0 = icmp sgt i64 %self, 0 %1 = sub i64 0, %self %2 = select i1 %0, i64 %self, i64 %1 ret i64 %2 + @llvm def __lshift__(self: int, other: int) -> int: %0 = shl i64 %self, %other ret i64 %0 + @llvm def __rshift__(self: int, other: int) -> int: %0 = ashr i64 %self, %other ret i64 %0 - @llvm - def __add__(self: int, b: int) -> int: - %tmp = add i64 %self, %b - ret i64 %tmp + @llvm def __add__(self: int, other: float) -> float: %0 = sitofp i64 %self to double %1 = fadd double %0, %other ret double %1 + @llvm - def __sub__(self: int, b: int) -> int: - %tmp = sub i64 %self, %b + def __add__(self: int, b: int) -> int: + %tmp = add i64 %self, %b ret i64 %tmp + @llvm def __sub__(self: int, other: float) -> float: %0 = sitofp i64 %self to double %1 = fsub double %0, %other ret double %1 + @llvm - def __mul__(self: int, b: int) -> int: - %tmp = mul i64 %self, %b + def __sub__(self: int, b: int) -> int: + %tmp = sub i64 %self, %b ret i64 %tmp + @llvm def __mul__(self: int, other: float) -> float: %0 = sitofp i64 %self to double %1 = fmul double %0, %other ret double %1 + @llvm - def __floordiv__(self: int, b: int) -> int: - %tmp = sdiv i64 %self, %b + def __mul__(self: int, b: int) -> int: + %tmp = mul i64 %self, %b ret i64 %tmp + @llvm def __floordiv__(self: int, other: float) -> float: declare double @llvm.floor.f64(double) @@ -71,141 +80,177 @@ class I: %1 = fdiv double %0, %other %2 = call double @llvm.floor.f64(double %1) ret double %2 + + @llvm + def __floordiv__(self: int, b: int) -> int: + %tmp = sdiv i64 %self, %b + ret i64 %tmp + + @llvm + def __truediv__(self: int, other: float) -> float: + %0 = sitofp i64 %self to double + %1 = fdiv double %0, %other + ret double %1 + @llvm def __truediv__(self: int, other: int) -> float: %0 = sitofp i64 %self to double %1 = sitofp i64 %other to double %2 = fdiv double %0, %1 ret double %2 - @llvm - def __truediv__(self: int, other: float) -> float: - %0 = sitofp i64 %self to double - %1 = fdiv double %0, %other - ret double %1 - @llvm - def __mod__(a: int, b: int) -> int: - %tmp = srem i64 %a, %b - ret i64 %tmp + @llvm def __mod__(self: int, other: float) -> float: %0 = sitofp i64 %self to double %1 = frem double %0, %other ret double %1 + + @llvm + def __mod__(a: int, b: int) -> int: + %tmp = srem i64 %a, %b + ret i64 %tmp + @llvm def __invert__(a: int) -> int: %tmp = xor i64 %a, -1 ret i64 %tmp + @llvm def __and__(a: int, b: int) -> int: %tmp = and i64 %a, %b ret i64 %tmp + @llvm def __or__(a: int, b: int) -> int: %tmp = or i64 %a, %b ret i64 %tmp + @llvm def __xor__(a: int, b: int) -> int: %tmp = xor i64 %a, %b ret i64 %tmp + @llvm def __shr__(a: int, b: int) -> int: %tmp = ashr i64 %a, %b ret i64 %tmp + @llvm def __shl__(a: int, b: int) -> int: %tmp = shl i64 %a, %b ret i64 %tmp + @llvm def __bitreverse__(a: int) -> int: declare i64 @llvm.bitreverse.i64(i64 %a) %tmp = call i64 @llvm.bitreverse.i64(i64 %a) ret i64 %tmp + @llvm def __bswap__(a: int) -> int: declare i64 @llvm.bswap.i64(i64 %a) %tmp = call i64 @llvm.bswap.i64(i64 %a) ret i64 %tmp + @llvm def __ctpop__(a: int) -> int: declare i64 @llvm.ctpop.i64(i64 %a) %tmp = call i64 @llvm.ctpop.i64(i64 %a) ret i64 %tmp + @llvm def __ctlz__(a: int) -> int: declare i64 @llvm.ctlz.i64(i64 %a, i1 %is_zero_undef) %tmp = call i64 @llvm.ctlz.i64(i64 %a, i1 false) ret i64 %tmp + @llvm def __cttz__(a: int) -> int: declare i64 @llvm.cttz.i64(i64 %a, i1 %is_zero_undef) %tmp = call i64 @llvm.cttz.i64(i64 %a, i1 false) ret i64 %tmp - @llvm - def __eq__(a: int, b: int) -> bool: - %tmp = icmp eq i64 %a, %b - %res = zext i1 %tmp to i8 - ret i8 %res + @llvm def __eq__(self: int, b: float) -> bool: %0 = sitofp i64 %self to double %1 = fcmp oeq double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + @llvm - def __ne__(a: int, b: int) -> bool: - %tmp = icmp ne i64 %a, %b + def __eq__(a: int, b: int) -> bool: + %tmp = icmp eq i64 %a, %b %res = zext i1 %tmp to i8 ret i8 %res + @llvm def __ne__(self: int, b: float) -> bool: %0 = sitofp i64 %self to double %1 = fcmp one double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + @llvm - def __lt__(a: int, b: int) -> bool: - %tmp = icmp slt i64 %a, %b + def __ne__(a: int, b: int) -> bool: + %tmp = icmp ne i64 %a, %b %res = zext i1 %tmp to i8 ret i8 %res + @llvm def __lt__(self: int, b: float) -> bool: %0 = sitofp i64 %self to double %1 = fcmp olt double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + @llvm - def __gt__(a: int, b: int) -> bool: - %tmp = icmp sgt i64 %a, %b + def __lt__(a: int, b: int) -> bool: + %tmp = icmp slt i64 %a, %b %res = zext i1 %tmp to i8 ret i8 %res + @llvm def __gt__(self: int, b: float) -> bool: %0 = sitofp i64 %self to double %1 = fcmp ogt double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + @llvm - def __le__(a: int, b: int) -> bool: - %tmp = icmp sle i64 %a, %b + def __gt__(a: int, b: int) -> bool: + %tmp = icmp sgt i64 %a, %b %res = zext i1 %tmp to i8 ret i8 %res + @llvm def __le__(self: int, b: float) -> bool: %0 = sitofp i64 %self to double %1 = fcmp ole double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + @llvm - def __ge__(a: int, b: int) -> bool: - %tmp = icmp sge i64 %a, %b + def __le__(a: int, b: int) -> bool: + %tmp = icmp sle i64 %a, %b %res = zext i1 %tmp to i8 ret i8 %res + @llvm def __ge__(self: int, b: float) -> bool: %0 = sitofp i64 %self to double %1 = fcmp oge double %0, %b %2 = zext i1 %1 to i8 ret i8 %2 + + @llvm + def __ge__(a: int, b: int) -> bool: + %tmp = icmp sge i64 %a, %b + %res = zext i1 %tmp to i8 + ret i8 %res + + def __pow__(self: int, exp: float): + return float(self) ** exp + def __pow__(self: int, exp: int): if exp < 0: return 0 @@ -218,8 +263,6 @@ class I: break self *= self return result - def __pow__(self: int, exp: float): - return float(self) ** exp @extend class int: @@ -227,158 +270,197 @@ class int: global OP_COUNT OP_COUNT = inc(OP_COUNT) return self + def __float__(self) -> float: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__float__(self) + def __bool__(self) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__bool__(self) + def __pos__(self) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) return self + def __neg__(self) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__neg__(self) + def __lshift__(self, other: int) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__lshift__(self, other) + def __rshift__(self, other: int) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__rshift__(self, other) - def __add__(self, b: int) -> int: - global OP_COUNT - OP_COUNT = inc(OP_COUNT) - return I.__add__(self, b) + def __add__(self, other: float) -> float: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__add__(self, other) - def __sub__(self, b: int) -> int: + + def __add__(self, b: int) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__sub__(self, b) + return I.__add__(self, b) + def __sub__(self, other: float) -> float: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__sub__(self, other) - def __mul__(self, b: int) -> int: + + def __sub__(self, b: int) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__mul__(self, b) + return I.__sub__(self, b) + def __mul__(self, other: float) -> float: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__mul__(self, other) - def __floordiv__(self, b: int) -> int: + + def __mul__(self, b: int) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__floordiv__(self, b) + return I.__mul__(self, b) + def __floordiv__(self, other: float) -> float: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__floordiv__(self, other) - def __truediv__(self, other: int) -> float: + + def __floordiv__(self, b: int) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__truediv__(self, other) + return I.__floordiv__(self, b) + def __truediv__(self, other: float) -> float: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__truediv__(self, other) - def __mod__(self, b: int) -> int: + + def __truediv__(self, other: int) -> float: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__mod__(self, b) + return I.__truediv__(self, other) + def __mod__(self, other: float) -> float: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__mod__(self, other) + + def __mod__(self, b: int) -> int: + global OP_COUNT + OP_COUNT = inc(OP_COUNT) + return I.__mod__(self, b) + def __invert__(self) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__invert__(self) + def __and__(self, b: int) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__and__(self, b) + def __or__(self, b: int) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__or__(self, b) + def __xor__(self, b: int) -> int: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__xor__(self, b) - def __eq__(self, b: int) -> bool: - global OP_COUNT - OP_COUNT = inc(OP_COUNT) - return I.__eq__(self, b) + def __eq__(self, b: float) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__eq__(self, b) - def __ne__(self, b: int) -> bool: + + def __eq__(self, b: int) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__ne__(self, b) + return I.__eq__(self, b) + def __ne__(self, b: float) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__ne__(self, b) - def __lt__(self, b: int) -> bool: + + def __ne__(self, b: int) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__lt__(self, b) + return I.__ne__(self, b) + def __lt__(self, b: float) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__lt__(self, b) - def __gt__(self, b: int) -> bool: + + def __lt__(self, b: int) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__gt__(self, b) + return I.__lt__(self, b) + def __gt__(self, b: float) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__gt__(self, b) - def __le__(self, b: int) -> bool: + + def __gt__(self, b: int) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__le__(self, b) + return I.__gt__(self, b) + def __le__(self, b: float) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__le__(self, b) - def __ge__(self, b: int) -> bool: + + def __le__(self, b: int) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__ge__(self, b) + return I.__le__(self, b) + def __ge__(self, b: float) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__ge__(self, b) - def __pow__(self, exp: int): + + def __ge__(self, b: int) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) - return I.__pow__(self, exp) + return I.__ge__(self, b) + def __pow__(self, exp: float): global OP_COUNT OP_COUNT = inc(OP_COUNT) return I.__pow__(self, exp) + def __pow__(self, exp: int): + global OP_COUNT + OP_COUNT = inc(OP_COUNT) + return I.__pow__(self, exp) + + class F: @llvm def __int__(self: float) -> int: %0 = fptosi double %self to i64 ret i64 %0 + def __float__(self: float): return self + @llvm def __bool__(self: float) -> bool: %0 = fcmp one double %self, 0.000000e+00 @@ -391,10 +473,12 @@ class float: global OP_COUNT OP_COUNT = inc(OP_COUNT) return F.__int__(self) + def __float__(self) -> float: global OP_COUNT OP_COUNT = inc(OP_COUNT) return self + def __bool__(self) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) @@ -406,10 +490,12 @@ class bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) return 1 if self else 0 + def __float__(self) -> float: global OP_COUNT OP_COUNT = inc(OP_COUNT) return 1. if self else 0. + def __bool__(self) -> bool: global OP_COUNT OP_COUNT = inc(OP_COUNT) From acc96aa6eb99d5500c48af67c62f43c7a1f53141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Thu, 16 Dec 2021 13:23:25 -0800 Subject: [PATCH 06/61] Add support for partial functions with *args/**kwargs; Fix partial method dispatch --- codon/parser/ast/expr.h | 2 +- .../visitors/simplify/simplify_stmt.cpp | 12 +-- .../visitors/typecheck/typecheck_ctx.cpp | 14 +-- .../visitors/typecheck/typecheck_expr.cpp | 88 ++++++++++++++----- test/parser/typecheck_expr.codon | 84 +++++++++++++++--- 5 files changed, 158 insertions(+), 42 deletions(-) diff --git a/codon/parser/ast/expr.h b/codon/parser/ast/expr.h index e1c3be71..df5716c4 100644 --- a/codon/parser/ast/expr.h +++ b/codon/parser/ast/expr.h @@ -275,7 +275,7 @@ struct KeywordStarExpr : public Expr { struct TupleExpr : public Expr { std::vector items; - explicit TupleExpr(std::vector items); + explicit TupleExpr(std::vector items = {}); TupleExpr(const TupleExpr &expr); std::string toString() const override; diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 20dd5282..f736e081 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -954,7 +954,8 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { auto subs = substitutions[ai]; if (ctx->cache->classes[ctx->bases.back().name] - .methods[ctx->cache->reverseIdentifierLookup[f->name]].empty()) + .methods[ctx->cache->reverseIdentifierLookup[f->name]] + .empty()) generateDispatch(ctx->cache->reverseIdentifierLookup[f->name]); auto newName = ctx->generateCanonicalName( ctx->cache->reverseIdentifierLookup[f->name], true); @@ -1765,10 +1766,11 @@ std::vector SimplifyVisitor::getClassMethods(const StmtPtr &s) { void SimplifyVisitor::generateDispatch(const std::string &name) { transform(N( - name + ".dispatch", nullptr, std::vector{Param("*args")}, - N( - N(N(N(N(ctx->bases.back().name), name), - N(N("args"))))))); + name + ".dispatch", nullptr, + std::vector{Param("*args"), Param("**kwargs")}, + N(N(N( + N(N(ctx->bases.back().name), name), + N(N("args")), N(N("kwargs"))))))); } } // namespace ast diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.cpp b/codon/parser/visitors/typecheck/typecheck_ctx.cpp index e463618e..a84b02d9 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.cpp +++ b/codon/parser/visitors/typecheck/typecheck_ctx.cpp @@ -195,15 +195,17 @@ int TypeContext::reorderNamedArgs(types::FuncType *func, int starArgIndex = -1, kwstarArgIndex = -1; for (int i = 0; i < func->ast->args.size(); i++) { - if ((known.empty() || !known[i]) && startswith(func->ast->args[i].name, "**")) + // if (!known.empty() && known[i] && !partial) + // continue; + if (startswith(func->ast->args[i].name, "**")) kwstarArgIndex = i, score -= 2; - else if ((known.empty() || !known[i]) && startswith(func->ast->args[i].name, "*")) + else if (startswith(func->ast->args[i].name, "*")) starArgIndex = i, score -= 2; } - seqassert(known.empty() || starArgIndex == -1 || !known[starArgIndex], - "partial *args"); - seqassert(known.empty() || kwstarArgIndex == -1 || !known[kwstarArgIndex], - "partial **kwargs"); + // seqassert(known.empty() || starArgIndex == -1 || !known[starArgIndex], + // "partial *args"); + // seqassert(known.empty() || kwstarArgIndex == -1 || !known[kwstarArgIndex], + // "partial **kwargs"); // 1. Assign positional arguments to slots // Each slot contains a list of arg's indices diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 09f63c99..8be7976c 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -952,7 +952,8 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, if (bestMethod->ast->attributes.has(Attr::Property)) methodArgs.pop_back(); ExprPtr e = N(N(bestMethod->ast->name), methodArgs); - return transform(e, false, allowVoidExpr); + auto ex = transform(e, false, allowVoidExpr); + return ex; } } @@ -1094,6 +1095,18 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in bool isPartial = false; int ellipsisStage = -1; auto newMask = std::vector(calleeFn->ast->args.size(), 1); + auto getPartialArg = [&](int pi) { + auto id = transform(N(partialVar)); + ExprPtr it = N(pi); + // Manual call to transformStaticTupleIndex needed because otherwise + // IndexExpr routes this to InstantiateExpr. + auto ex = transformStaticTupleIndex(callee.get(), id, it); + seqassert(ex, "partial indexing failed"); + return ex; + }; + + ExprPtr partialStarArgs = nullptr; + ExprPtr partialKwstarArgs = nullptr; if (expr->ordered) args = expr->args; else @@ -1110,17 +1123,38 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in : expr->args[slots[si][0]].value); typeArgCount += typeArgs.back() != nullptr; newMask[si] = slots[si].empty() ? 0 : 1; - } else if (si == starArgIndex && !(partial && slots[si].empty())) { + } else if (si == starArgIndex) { std::vector extra; + if (!known.empty()) + extra.push_back(N(getPartialArg(-2))); for (auto &e : slots[si]) { extra.push_back(expr->args[e].value); if (extra.back()->getEllipsis()) ellipsisStage = args.size(); } - args.push_back({"", transform(N(extra))}); - } else if (si == kwstarArgIndex && !(partial && slots[si].empty())) { + auto e = transform(N(extra)); + if (partial) { + partialStarArgs = e; + args.push_back({"", transform(N())}); + newMask[si] = 0; + } else { + args.push_back({"", e}); + } + } else if (si == kwstarArgIndex) { std::vector names; std::vector values; + if (!known.empty()) { + auto e = getPartialArg(-1); + auto t = e->getType()->getRecord(); + seqassert(t && startswith(t->name, "KwTuple"), "{} not a kwtuple", + e->toString()); + auto &ff = ctx->cache->classes[t->name].fields; + for (int i = 0; i < t->getRecord()->args.size(); i++) { + names.emplace_back(ff[i].name); + values.emplace_back( + CallExpr::Arg{"", transform(N(clone(e), ff[i].name))}); + } + } for (auto &e : slots[si]) { names.emplace_back(expr->args[e].name); values.emplace_back(CallExpr::Arg{"", expr->args[e].value}); @@ -1128,16 +1162,17 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in ellipsisStage = args.size(); } auto kwName = generateTupleStub(names.size(), "KwTuple", names); - args.push_back({"", transform(N(N(kwName), values))}); + auto e = transform(N(N(kwName), values)); + if (partial) { + partialKwstarArgs = e; + args.push_back({"", transform(N())}); + newMask[si] = 0; + } else { + args.push_back({"", e}); + } } else if (slots[si].empty()) { if (!known.empty() && known[si]) { - // Manual call to transformStaticTupleIndex needed because otherwise - // IndexExpr routes this to InstantiateExpr. - auto id = transform(N(partialVar)); - ExprPtr it = N(pi++); - auto ex = transformStaticTupleIndex(callee.get(), id, it); - seqassert(ex, "partial indexing failed"); - args.push_back({"", ex}); + args.push_back({"", getPartialArg(pi++)}); } else if (partial) { args.push_back({"", transform(N())}); newMask[si] = 0; @@ -1165,6 +1200,12 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in if (isPartial) { deactivateUnbounds(expr->args.back().value->getType().get()); expr->args.pop_back(); + if (!partialStarArgs) + partialStarArgs = transform(N()); + if (!partialKwstarArgs) { + auto kwName = generateTupleStub(0, "KwTuple", {}); + partialKwstarArgs = transform(N(N(kwName))); + } } // Typecheck given arguments with the expected (signature) types. @@ -1257,11 +1298,12 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in deactivateUnbounds(pt->func.get()); calleeFn->generics[si + 1].type = calleeFn->args[si + 1] = replacements[si]; } - if (auto rt = realize(calleeFn)) { - unify(rt, std::static_pointer_cast(calleeFn)); - expr->expr = transform(expr->expr); + if (!isPartial) { + if (auto rt = realize(calleeFn)) { + unify(rt, std::static_pointer_cast(calleeFn)); + expr->expr = transform(expr->expr); + } } - expr->done &= expr->expr->done; // Emit the final call. @@ -1274,6 +1316,8 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in for (auto &r : args) if (!r.value->getEllipsis()) newArgs.push_back(r.value); + newArgs.push_back(partialStarArgs); + newArgs.push_back(partialKwstarArgs); std::string var = ctx->cache->getTemporaryVar("partial"); ExprPtr call = nullptr; @@ -1535,7 +1579,8 @@ std::string TypecheckVisitor::generatePartialStub(const std::vector &mask, tupleSize++; auto typeName = format(TYPE_PARTIAL "{}.{}", strMask, fn->ast->name); if (!ctx->find(typeName)) - generateTupleStub(tupleSize, typeName, {}, false); + // 2 for .starArgs and .kwstarArgs (empty tuples if fn does not have them) + generateTupleStub(tupleSize + 2, typeName, {}, false); return typeName; } @@ -1601,9 +1646,12 @@ ExprPtr TypecheckVisitor::partializeFunction(ExprPtr expr) { auto partialTypeName = generatePartialStub(mask, fn.get()); deactivateUnbounds(fn.get()); std::string var = ctx->cache->getTemporaryVar("partial"); - ExprPtr call = N( - N(N(var), N(N(partialTypeName))), - N(var)); + auto kwName = generateTupleStub(0, "KwTuple", {}); + ExprPtr call = + N(N(N(var), + N(N(partialTypeName), N(), + N(N(kwName)))), + N(var)); call = transform(call, false, allowVoidExpr); seqassert(call->type->getRecord() && startswith(call->type->getRecord()->name, partialTypeName) && diff --git a/test/parser/typecheck_expr.codon b/test/parser/typecheck_expr.codon index faeb6497..1403319e 100644 --- a/test/parser/typecheck_expr.codon +++ b/test/parser/typecheck_expr.codon @@ -447,16 +447,7 @@ q = p(zh=43, ...) q(1) #: 1 () (zh: 43) r = q(5, 38, ...) r() #: 5 (38) (zh: 43) - -#%% call_partial_star_error,barebones -def foo(x, *args, **kwargs): - print x, args, kwargs -p = foo(...) -p(1, z=5) -q = p(zh=43, ...) -q(1) -r = q(5, 38, ...) -r(1, a=1) #! too many arguments for foo[T1,T2,T3] (expected maximum 3, got 2) +r(1, a=1) #: 5 (38, 1) (zh: 43, a: 1) #%% call_kwargs,barebones def kwhatever(**kwargs): @@ -504,6 +495,79 @@ foo(*(1,2)) #: (1, 2) () foo(3, f) #: (3, (x: 6, y: True)) () foo(k = 3, **f) #: () (k: 3, x: 6, y: True) +#%% call_partial_args_kwargs,barebones +def foo(*args): + print(args) +a = foo(1, 2, ...) +b = a(3, 4, ...) +c = b(5, ...) +c('zooooo') +#: (1, 2, 3, 4, 5, 'zooooo') + +def fox(*args, **kwargs): + print(args, kwargs) +xa = fox(1, 2, x=5, ...) +xb = xa(3, 4, q=6, ...) +xc = xb(5, ...) +xd = xc(z=5.1, ...) +xd('zooooo', w='lele') +#: (1, 2, 3, 4, 5, 'zooooo') (x: 5, q: 6, z: 5.1, w: 'lele') + +class Foo: + i: int + def __str__(self): + return f'#{self.i}' + def foo(self, a): + return f'{self}:generic' + def foo(self, a: float): + return f'{self}:float' + def foo(self, a: int): + return f'{self}:int' +f = Foo(4) + +def pacman(x, f): + print f(x, '5') + print f(x, 2.1) + print f(x, 4) +pacman(f, Foo.foo) +#: #4:generic +#: #4:float +#: #4:int + +def macman(f): + print f('5') + print f(2.1) + print f(4) +macman(f.foo) +#: #4:generic +#: #4:float +#: #4:int + +class Fox: + i: int + def __str__(self): + return f'#{self.i}' + def foo(self, a, b): + return f'{self}:generic b={b}' + def foo(self, a: float, c): + return f'{self}:float, c={c}' + def foo(self, a: int): + return f'{self}:int' + def foo(self, a: int, z, q): + return f'{self}:int z={z} q={q}' +ff = Fox(5) +def maxman(f): + print f('5', b=1) + print f(2.1, 3) + print f(4) + print f(5, 1, q=3) +maxman(ff.foo) +#: #5:generic b=1 +#: #5:float, c=3 +#: #5:int +#: #5:int z=1 q=3 + + #%% call_static,barebones print isinstance(1, int), isinstance(2.2, float), isinstance(3, bool) #: True True False From 2d1894333cb5e6a47139b262f7c77a4047a001f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Thu, 16 Dec 2021 13:24:09 -0800 Subject: [PATCH 07/61] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 84b29947..b9c5927a 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ Thumbs.db extra/jupyter/share/jupyter/kernels/codon/kernel.json scratch.* +_* From ec76298ab59d2ae9c6b03912a6490a106bc70f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Thu, 16 Dec 2021 13:56:37 -0800 Subject: [PATCH 08/61] Fix grammar to allow variable names that have reserved word as a prefix --- codon/parser/peg/grammar.peg | 8 ++++---- codon/parser/visitors/simplify/simplify_expr.cpp | 14 ++++++++++++-- test/parser/simplify_stmt.codon | 13 +++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/codon/parser/peg/grammar.peg b/codon/parser/peg/grammar.peg index 656bff18..92f22344 100644 --- a/codon/parser/peg/grammar.peg +++ b/codon/parser/peg/grammar.peg @@ -61,12 +61,12 @@ small_stmt <- / 'break' &(SPACE / ';' / EOL) { return any(ast(LOC)); } / 'continue' &(SPACE / ';' / EOL) { return any(ast(LOC)); } / global_stmt - / yield_stmt + / yield_stmt &(SPACE / ';' / EOL) / assert_stmt / del_stmt - / return_stmt - / raise_stmt - / print_stmt + / return_stmt &(SPACE / ';' / EOL) + / raise_stmt &(SPACE / ';' / EOL) + / print_stmt / import_stmt / expressions &(_ ';' / _ EOL) { return any(ast(LOC, ac_expr(V0))); } / NAME SPACE expressions { diff --git a/codon/parser/visitors/simplify/simplify_expr.cpp b/codon/parser/visitors/simplify/simplify_expr.cpp index 575dab70..6d670d16 100644 --- a/codon/parser/visitors/simplify/simplify_expr.cpp +++ b/codon/parser/visitors/simplify/simplify_expr.cpp @@ -385,12 +385,22 @@ void SimplifyVisitor::visit(IndexExpr *expr) { } // IndexExpr[i1, ..., iN] is internally stored as IndexExpr[TupleExpr[i1, ..., iN]] // for N > 1, so make sure to check that case. + + std::vector it; if (auto t = index->getTuple()) for (auto &i : t->items) - it.push_back(transform(i, true)); + it.push_back(i); else - it.push_back(transform(index, true)); + it.push_back(index); + for (auto &i: it) { + if (auto es = i->getStar()) + i = N(transform(es->what)); + else if (auto ek = CAST(i, KeywordStarExpr)) + i = N(transform(ek->what)); + else + i = transform(i, true); + } if (e->isType()) { resultExpr = N(e, it); resultExpr->markType(); diff --git a/test/parser/simplify_stmt.codon b/test/parser/simplify_stmt.codon index 28da102a..6de479b8 100644 --- a/test/parser/simplify_stmt.codon +++ b/test/parser/simplify_stmt.codon @@ -982,3 +982,16 @@ class Bar: x: float class FooBar(Foo, Bar): pass #! 'x' declared twice + +#%% keyword_prefix,barebones +def foo(return_, pass_, yield_, break_, continue_, print_, assert_): + return_.append(1) + pass_.append(2) + yield_.append(3) + break_.append(4) + continue_.append(5) + print_.append(6) + assert_.append(7) + return return_, pass_, yield_, break_, continue_, print_, assert_ +print foo([1], [1], [1], [1], [1], [1], [1]) +#: ([1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7]) \ No newline at end of file From 2d6167be7e123a70625b428bc0737fab8c1a5c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 17 Dec 2021 09:23:50 -0800 Subject: [PATCH 09/61] Add support for super() call --- codon/parser/visitors/simplify/simplify.cpp | 3 +- codon/parser/visitors/typecheck/typecheck.h | 5 ++ .../visitors/typecheck/typecheck_ctx.cpp | 30 ++++++---- .../parser/visitors/typecheck/typecheck_ctx.h | 6 +- .../visitors/typecheck/typecheck_expr.cpp | 60 +++++++++++++++++-- .../visitors/typecheck/typecheck_infer.cpp | 10 +++- 6 files changed, 94 insertions(+), 20 deletions(-) diff --git a/codon/parser/visitors/simplify/simplify.cpp b/codon/parser/visitors/simplify/simplify.cpp index ef171ba4..0b1343c9 100644 --- a/codon/parser/visitors/simplify/simplify.cpp +++ b/codon/parser/visitors/simplify/simplify.cpp @@ -88,8 +88,9 @@ SimplifyVisitor::apply(Cache *cache, const StmtPtr &node, const std::string &fil } // Reserve the following static identifiers. for (auto name : {"staticlen", "compile_error", "isinstance", "hasattr", "type", - "TypeVar", "Callable", "argv"}) + "TypeVar", "Callable", "argv", "super"}) stdlib->generateCanonicalName(name); + stdlib->add(SimplifyItem::Var, "super", "super", true); // This code must be placed in a preamble (these are not POD types but are // referenced by the various preamble Function.N and Tuple.N stubs) diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index e96e3d33..97b77ac0 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -293,6 +293,11 @@ private: const std::vector &args); types::FuncTypePtr findBestMethod(const Expr *expr, const std::string &member, const std::vector &args); + std::vector findSuperMethods(const types::FuncTypePtr &func); + std::vector + findMatchingMethods(types::ClassType *typ, + const std::vector &methods, + const std::vector &args); private: types::TypePtr unify(types::TypePtr &a, const types::TypePtr &b, diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.cpp b/codon/parser/visitors/typecheck/typecheck_ctx.cpp index a84b02d9..2e5ba2f7 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.cpp +++ b/codon/parser/visitors/typecheck/typecheck_ctx.cpp @@ -103,19 +103,20 @@ types::TypePtr TypeContext::instantiate(const Expr *expr, types::TypePtr type, if (auto l = i.second->getLink()) { if (l->kind != types::LinkType::Unbound) continue; - i.second->setSrcInfo(expr->getSrcInfo()); + if (expr) + i.second->setSrcInfo(expr->getSrcInfo()); if (activeUnbounds.find(i.second) == activeUnbounds.end()) { LOG_TYPECHECK("[ub] #{} -> {} (during inst of {}): {} ({})", i.first, i.second->debugString(true), type->debugString(true), - expr->toString(), activate); + expr ? expr->toString() : "", activate); if (activate && allowActivation) - activeUnbounds[i.second] = - format("{} of {} in {}", l->genericName.empty() ? "?" : l->genericName, - type->toString(), cache->getContent(expr->getSrcInfo())); + activeUnbounds[i.second] = format( + "{} of {} in {}", l->genericName.empty() ? "?" : l->genericName, + type->toString(), expr ? cache->getContent(expr->getSrcInfo()) : ""); } } } - LOG_TYPECHECK("[inst] {} -> {}", expr->toString(), t->debugString(true)); + LOG_TYPECHECK("[inst] {} -> {}", expr ? expr->toString() : "", t->debugString(true)); return t; } @@ -135,8 +136,9 @@ TypeContext::instantiateGeneric(const Expr *expr, types::TypePtr root, return instantiate(expr, root, g.get()); } -std::vector -TypeContext::findMethod(const std::string &typeName, const std::string &method) const { +std::vector TypeContext::findMethod(const std::string &typeName, + const std::string &method, + bool hideShadowed) const { auto m = cache->classes.find(typeName); if (m != cache->classes.end()) { auto t = m->second.methods.find(method); @@ -148,9 +150,13 @@ TypeContext::findMethod(const std::string &typeName, const std::string &method) for (int mti = int(t->second.size()) - 1; mti > 0; mti--) { auto &mt = t->second[mti]; if (mt.age <= age) { - auto sig = cache->functions[mt.name].ast->signature(); - if (!in(signatureLoci, sig)) { - signatureLoci.insert(sig); + if (hideShadowed) { + auto sig = cache->functions[mt.name].ast->signature(); + if (!in(signatureLoci, sig)) { + signatureLoci.insert(sig); + vv.emplace_back(mt.type); + } + } else { vv.emplace_back(mt.type); } } @@ -196,7 +202,7 @@ int TypeContext::reorderNamedArgs(types::FuncType *func, int starArgIndex = -1, kwstarArgIndex = -1; for (int i = 0; i < func->ast->args.size(); i++) { // if (!known.empty() && known[i] && !partial) - // continue; + // continue; if (startswith(func->ast->args[i].name, "**")) kwstarArgIndex = i, score -= 2; else if (startswith(func->ast->args[i].name, "*")) diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.h b/codon/parser/visitors/typecheck/typecheck_ctx.h index b1961653..e6093f46 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.h +++ b/codon/parser/visitors/typecheck/typecheck_ctx.h @@ -48,6 +48,8 @@ struct TypeContext : public Context { /// Map of locally realized types and functions. std::unordered_map> visitedAsts; + /// List of functions that can be accessed via super() + std::vector supers; }; std::vector bases; @@ -121,13 +123,13 @@ public: /// Returns the list of generic methods that correspond to typeName.method. std::vector findMethod(const std::string &typeName, - const std::string &method) const; + const std::string &method, + bool hideShadowed = true) const; /// Returns the generic type of typeName.member, if it exists (nullptr otherwise). /// Special cases: __elemsize__ and __atomic__. types::TypePtr findMember(const std::string &typeName, const std::string &member) const; - typedef std::function> &, bool)> ReorderDoneFn; typedef std::function ReorderErrorFn; diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 8be7976c..0a390879 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -1026,6 +1027,20 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in seenNames.insert(i.name); } + if (expr->expr->isId("super")) { + if (ctx->bases.back().supers.empty()) + error("no matching super methods are available"); + auto parentCls = ctx->bases.back().type->getFunc()->funcParent; + auto m = + findMatchingMethods(parentCls ? CAST(parentCls, types::ClassType) : nullptr, + ctx->bases.back().supers, expr->args); + if (m.empty()) + error("no matching super methods are available"); + // LOG("found {} <- {}", ctx->bases.back().type->getFunc()->toString(), m[0]->toString()); + ExprPtr e = N(N(m[0]->ast->name), expr->args); + return transform(e, false, true); + } + // Intercept dot-callees (e.g. expr.foo). Needed in order to select a proper // overload for magic methods and to avoid dealing with partial calls // (a non-intercepted object DotExpr (e.g. expr.foo) will get transformed into a @@ -1677,11 +1692,48 @@ TypecheckVisitor::findBestMethod(const Expr *expr, const std::string &member, const std::vector &args) { auto typ = expr->getType()->getClass(); seqassert(typ, "not a class"); + auto methods = ctx->findMethod(typ->name, member, false); + auto m = findMatchingMethods(typ.get(), methods, args); + return m.empty() ? nullptr : m[0]; +} +std::vector +TypecheckVisitor::findSuperMethods(const types::FuncTypePtr &func) { + if (func->ast->attributes.parentClass.empty() || + endswith(func->ast->name, ".dispatch")) + return {}; + auto p = ctx->find(func->ast->attributes.parentClass)->type; + if (!p || !p->getClass()) + return {}; + + auto methodName = ctx->cache->reverseIdentifierLookup[func->ast->name]; + auto m = ctx->cache->classes.find(p->getClass()->name); + std::vector result; + if (m != ctx->cache->classes.end()) { + auto t = m->second.methods.find(methodName); + if (t != m->second.methods.end() && !t->second.empty()) { + seqassert(!t->second.empty() && endswith(t->second[0].name, ".dispatch"), + "first method '{}' is not dispatch", t->second[0].name); + for (int mti = 1; mti < t->second.size(); mti++) { + auto &mt = t->second[mti]; + if (mt.type->ast->name == func->ast->name) + break; + result.emplace_back(mt.type); + } + } + } + std::reverse(result.begin(), result.end()); + return result; +} + +std::vector +TypecheckVisitor::findMatchingMethods(types::ClassType *typ, + const std::vector &methods, + const std::vector &args) { // Pick the last method that accepts the given arguments. - auto methods = ctx->findMethod(typ->name, member); + std::vector results; for (int mi = 0; mi < methods.size(); mi++) { - auto m = ctx->instantiate(expr, methods[mi], typ.get(), false)->getFunc(); + auto m = ctx->instantiate(nullptr, methods[mi], typ, false)->getFunc(); std::vector reordered; auto score = ctx->reorderNamedArgs( m.get(), args, @@ -1721,10 +1773,10 @@ TypecheckVisitor::findBestMethod(const Expr *expr, const std::string &member, // else ar.push_back(format("{}: {}", a.first, a.second->toString())); // } // LOG("- {} vs {}", m->toString(), join(ar, "; ")); - return methods[mi]; + results.push_back(methods[mi]); } } - return nullptr; + return results; } bool TypecheckVisitor::wrapExpr(ExprPtr &expr, TypePtr expectedType, diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index 2acb96ef..eef9924b 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -154,7 +154,15 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { ctx->realizationDepth++; ctx->addBlock(); ctx->typecheckLevel++; - ctx->bases.push_back({type->ast->name, type->getFunc(), type->args[0]}); + + // Find parents! + ctx->bases.push_back({type->ast->name, type->getFunc(), type->args[0], + {}, findSuperMethods(type->getFunc())}); + // if (startswith(type->ast->name, "Foo")) { + // LOG(": {}", type->toString()); + // for (auto &s: ctx->bases.back().supers) + // LOG(" - {}", s->toString()); + // } auto clonedAst = ctx->cache->functions[type->ast->name].ast->clone(); auto *ast = (FunctionStmt *)clonedAst.get(); addFunctionGenerics(type); From 58664374c7486b8ee3fc8275e49b70a7959aa7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 17 Dec 2021 10:10:40 -0800 Subject: [PATCH 10/61] Add super() tests; Allow static inheritance to inherit @extend methods --- .../visitors/simplify/simplify_stmt.cpp | 17 ++++--- test/parser/typecheck_stmt.codon | 44 +++++++++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index f736e081..3c4b9386 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -946,9 +946,11 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { suite->stmts.push_back(preamble->functions.back()); } } - for (int ai = 0; ai < baseASTs.size(); ai++) - for (auto sp : getClassMethods(baseASTs[ai]->suite)) - if (auto f = sp->getFunction()) { + for (int ai = 0; ai < baseASTs.size(); ai++) { + // FUNCS + for (auto &mm : ctx->cache->classes[baseASTs[ai]->name].methods) + for (auto &mf : mm.second) { + auto f = ctx->cache->functions[mf.name].ast; if (f->attributes.has("autogenerated")) continue; @@ -959,14 +961,15 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { generateDispatch(ctx->cache->reverseIdentifierLookup[f->name]); auto newName = ctx->generateCanonicalName( ctx->cache->reverseIdentifierLookup[f->name], true); - auto nf = std::dynamic_pointer_cast(replace(sp, subs)); + auto nf = std::dynamic_pointer_cast( + replace(std::static_pointer_cast(f), subs)); subs[nf->name] = N(newName); nf->name = newName; suite->stmts.push_back(nf); nf->attributes.parentClass = ctx->bases.back().name; // check original ast... - if (nf->attributes.has(".changedSelf")) + if (nf->attributes.has(".changedSelf")) // replace self type with new class nf->args[0].type = transformType(ctx->bases.back().ast); preamble->functions.push_back(clone(nf)); ctx->cache->functions[newName].ast = nf; @@ -974,6 +977,7 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { .methods[ctx->cache->reverseIdentifierLookup[f->name]] .push_back({newName, nullptr, ctx->cache->age}); } + } for (auto sp : getClassMethods(stmt->suite)) if (sp && !sp->getClass()) { transform(sp); @@ -1770,7 +1774,8 @@ void SimplifyVisitor::generateDispatch(const std::string &name) { std::vector{Param("*args"), Param("**kwargs")}, N(N(N( N(N(ctx->bases.back().name), name), - N(N("args")), N(N("kwargs"))))))); + N(N("args")), N(N("kwargs"))))), + Attr({"autogenerated"}))); } } // namespace ast diff --git a/test/parser/typecheck_stmt.codon b/test/parser/typecheck_stmt.codon index 8ffdfc75..ace31779 100644 --- a/test/parser/typecheck_stmt.codon +++ b/test/parser/typecheck_stmt.codon @@ -298,3 +298,47 @@ def foo(x): print len(x) foo(1) #: 2 foo('s') #: 1 + +#%% super,barebones +class Foo: + def foo(a): + # super(a) + print 'foo-1', a + def foo(a: int): + super(a) + print 'foo-2', a + def foo(a: str): + super(a) + print 'foo-3', a + def foo(a): + super(a) + print 'foo-4', a +Foo.foo(1) +#: foo-1 1 +#: foo-2 1 +#: foo-4 1 + +class Bear: + def woof(x): + return f'bear woof {x}' +@extend +class Bear: + def woof(x): + return super(x) + f' bear w--f {x}' +print Bear.woof('!') +#: bear woof ! bear w--f ! + +class PolarBear(Bear): + def woof(): + return 'polar ' + super('@') +print PolarBear.woof() +#: polar bear woof @ bear w--f @ + +#%% super_error,barebones +class Foo: + def foo(a): + super(a) + print 'foo-1', a +Foo.foo(1) +#! no matching super methods are available +#! while realizing Foo.foo.2 From fa7278e616940d7c4978592ef9fee96b37986a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Tue, 28 Dec 2021 20:58:20 -0800 Subject: [PATCH 11/61] Support for overloaded functions [wip; base logic done] --- codon/parser/cache.cpp | 3 + codon/parser/cache.h | 30 +++-- codon/parser/peg/peg.cpp | 2 - .../parser/visitors/simplify/simplify_ctx.cpp | 22 ++-- codon/parser/visitors/simplify/simplify_ctx.h | 9 +- .../visitors/simplify/simplify_stmt.cpp | 120 +++++++++--------- codon/parser/visitors/translate/translate.cpp | 3 +- codon/parser/visitors/typecheck/typecheck.h | 2 + .../visitors/typecheck/typecheck_ctx.cpp | 19 +-- .../visitors/typecheck/typecheck_expr.cpp | 80 +++++++++--- .../visitors/typecheck/typecheck_stmt.cpp | 14 +- stdlib/internal/str.codon | 1 + test/parser/simplify_stmt.codon | 2 +- test/parser/typecheck_expr.codon | 12 +- test/parser/typecheck_stmt.codon | 12 +- test/parser/types.codon | 75 ++++++----- 16 files changed, 241 insertions(+), 165 deletions(-) diff --git a/codon/parser/cache.cpp b/codon/parser/cache.cpp index 4d774c1b..e94ca102 100644 --- a/codon/parser/cache.cpp +++ b/codon/parser/cache.cpp @@ -50,6 +50,9 @@ types::ClassTypePtr Cache::findClass(const std::string &name) const { types::FuncTypePtr Cache::findFunction(const std::string &name) const { auto f = typeCtx->find(name); + if (f && f->type && f->kind == TypecheckItem::Func) + return f->type->getFunc(); + f = typeCtx->find(name + ":0"); if (f && f->type && f->kind == TypecheckItem::Func) return f->type->getFunc(); return nullptr; diff --git a/codon/parser/cache.h b/codon/parser/cache.h index 16db95cc..7adcb4c8 100644 --- a/codon/parser/cache.h +++ b/codon/parser/cache.h @@ -106,19 +106,9 @@ struct Cache : public std::enable_shared_from_this { /// Non-simplified AST. Used for base class instantiation. std::shared_ptr originalAst; - /// A class function method. - struct ClassMethod { - /// Canonical name of a method (e.g. __init__.1). - std::string name; - /// A corresponding generic function type. - types::FuncTypePtr type; - /// Method age (how many class extension were seen before a method definition). - /// Used to prevent the usage of a method before it was defined in the code. - int age; - }; - /// Class method lookup table. Each name points to a list of ClassMethod instances - /// that share the same method name (a list because methods can be overloaded). - std::unordered_map> methods; + /// Class method lookup table. Each non-canonical name points + /// to a root function name of a corresponding method. + std::unordered_map methods; /// A class field (member). struct ClassField { @@ -177,6 +167,20 @@ struct Cache : public std::enable_shared_from_this { /// corresponding Function instance. std::unordered_map functions; + + struct Overload { + /// Canonical name of an overload (e.g. Foo.__init__.1). + std::string name; + /// Overload age (how many class extension were seen before a method definition). + /// Used to prevent the usage of an overload before it was defined in the code. + /// TODO: I have no recollection of how this was supposed to work. Most likely + /// it does not work at all... + int age; + }; + /// Maps a "root" name of each function to the list of names of the function + /// overloads. + std::unordered_map> overloads; + /// Pointer to the later contexts needed for IR API access. std::shared_ptr typeCtx; std::shared_ptr codegenCtx; diff --git a/codon/parser/peg/peg.cpp b/codon/parser/peg/peg.cpp index f8b8dcc9..80ef9f49 100644 --- a/codon/parser/peg/peg.cpp +++ b/codon/parser/peg/peg.cpp @@ -52,8 +52,6 @@ std::shared_ptr initParser() { template T parseCode(Cache *cache, const std::string &file, std::string code, int line_offset, int col_offset, const std::string &rule) { - TIME("peg"); - // Initialize if (!grammar) grammar = initParser(); diff --git a/codon/parser/visitors/simplify/simplify_ctx.cpp b/codon/parser/visitors/simplify/simplify_ctx.cpp index 3f87dec2..517518db 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.cpp +++ b/codon/parser/visitors/simplify/simplify_ctx.cpp @@ -14,8 +14,9 @@ namespace codon { namespace ast { SimplifyItem::SimplifyItem(Kind k, std::string base, std::string canonicalName, - bool global) - : kind(k), base(move(base)), canonicalName(move(canonicalName)), global(global) {} + bool global, std::string moduleName) + : kind(k), base(move(base)), canonicalName(move(canonicalName)), global(global), + moduleName(move(moduleName)) {} SimplifyContext::SimplifyContext(std::string filename, Cache *cache) : Context(move(filename)), cache(move(cache)), @@ -31,6 +32,7 @@ std::shared_ptr SimplifyContext::add(SimplifyItem::Kind kind, bool global) { seqassert(!canonicalName.empty(), "empty canonical name for '{}'", name); auto t = std::make_shared(kind, getBase(), canonicalName, global); + t->moduleName = getModule(); Context::add(name, t); Context::add(canonicalName, t); return t; @@ -60,6 +62,14 @@ std::string SimplifyContext::getBase() const { return bases.back().name; } +std::string SimplifyContext::getModule() const { + std::string base = moduleName.status == ImportFile::STDLIB ? "std." : ""; + base += moduleName.module; + if (startswith(base, "__main__")) + base = base.substr(8); + return base; +} + std::string SimplifyContext::generateCanonicalName(const std::string &name, bool includeBase, bool zeroId) const { @@ -67,12 +77,8 @@ std::string SimplifyContext::generateCanonicalName(const std::string &name, bool alreadyGenerated = name.find('.') != std::string::npos; if (includeBase && !alreadyGenerated) { std::string base = getBase(); - if (base.empty()) { - base = moduleName.status == ImportFile::STDLIB ? "std." : ""; - base += moduleName.module; - if (startswith(base, "__main__")) - base = base.substr(8); - } + if (base.empty()) + base = getModule(); newName = (base.empty() ? "" : (base + ".")) + newName; } auto num = cache->identifierCount[newName]++; diff --git a/codon/parser/visitors/simplify/simplify_ctx.h b/codon/parser/visitors/simplify/simplify_ctx.h index 11a628a1..468ded2a 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.h +++ b/codon/parser/visitors/simplify/simplify_ctx.h @@ -32,13 +32,16 @@ struct SimplifyItem { bool global; /// Non-empty string if a variable is import variable std::string importPath; + /// Full module name + std::string moduleName; public: - SimplifyItem(Kind k, std::string base, std::string canonicalName, - bool global = false); + SimplifyItem(Kind k, std::string base, std::string canonicalName, bool global = false, + std::string moduleName = ""); /// Convenience getters. std::string getBase() const { return base; } + std::string getModule() const { return moduleName; } bool isGlobal() const { return global; } bool isVar() const { return kind == Var; } bool isFunc() const { return kind == Func; } @@ -107,6 +110,8 @@ public: /// Return a canonical name of the top-most base, or an empty string if this is a /// top-level base. std::string getBase() const; + /// Return the current module. + std::string getModule() const; /// Return the current base nesting level (note: bases, not blocks). int getLevel() const { return bases.size(); } /// Pretty-print the current context state. diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 3c4b9386..7e9f2c3b 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -437,28 +437,30 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { if (stmt->decorators.size() != 1) error("__attribute__ cannot be mixed with other decorators"); attr.isAttribute = true; - } else if (d->isId(Attr::LLVM)) + } else if (d->isId(Attr::LLVM)) { attr.set(Attr::LLVM); - else if (d->isId(Attr::Python)) + } else if (d->isId(Attr::Python)) { attr.set(Attr::Python); - else if (d->isId(Attr::Internal)) + } else if (d->isId(Attr::Internal)) { attr.set(Attr::Internal); - else if (d->isId(Attr::Atomic)) + } else if (d->isId(Attr::Atomic)) { attr.set(Attr::Atomic); - else if (d->isId(Attr::Property)) + } else if (d->isId(Attr::Property)) { attr.set(Attr::Property); - else if (d->isId(Attr::ForceRealize)) + } else if (d->isId(Attr::ForceRealize)) { attr.set(Attr::ForceRealize); - else { + } else { // Let's check if this is a attribute auto dt = transform(clone(d)); if (dt && dt->getId()) { auto ci = ctx->find(dt->getId()->value); if (ci && ci->kind == SimplifyItem::Func) { - if (ctx->cache->functions[ci->canonicalName].ast->attributes.isAttribute) { - attr.set(ci->canonicalName); - continue; - } + if (ctx->cache->overloads[ci->canonicalName].size() == 1) + if (ctx->cache->functions[ctx->cache->overloads[ci->canonicalName][0].name] + .ast->attributes.isAttribute) { + attr.set(ci->canonicalName); + continue; + } } } decorators.emplace_back(clone(d)); @@ -473,19 +475,22 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { } bool isClassMember = ctx->inClass(); - if (isClassMember && !endswith(stmt->name, ".dispatch") && - ctx->cache->classes[ctx->bases.back().name].methods[stmt->name].empty()) { - generateDispatch(stmt->name); - } - auto func_name = stmt->name; - if (endswith(stmt->name, ".dispatch")) - func_name = func_name.substr(0, func_name.size() - 9); - auto canonicalName = ctx->generateCanonicalName( - func_name, true, isClassMember && !endswith(stmt->name, ".dispatch")); - if (endswith(stmt->name, ".dispatch")) { - canonicalName += ".dispatch"; - ctx->cache->reverseIdentifierLookup[canonicalName] = func_name; + std::string rootName; + if (isClassMember) { + auto &m = ctx->cache->classes[ctx->bases.back().name].methods; + auto i = m.find(stmt->name); + if (i != m.end()) + rootName = i->second; + } else if (auto c = ctx->find(stmt->name)) { + if (c->isFunc() && c->getModule() == ctx->getModule() && + c->getBase() == ctx->getBase()) + rootName = c->canonicalName; } + if (rootName.empty()) + rootName = ctx->generateCanonicalName(stmt->name, true); + auto canonicalName = + format("{}:{}", rootName, ctx->cache->overloads[rootName].size()); + ctx->cache->reverseIdentifierLookup[canonicalName] = stmt->name; bool isEnclosedFunc = ctx->inFunction(); if (attr.has(Attr::ForceRealize) && (ctx->getLevel() || isClassMember)) @@ -495,7 +500,7 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { ctx->bases = std::vector(); if (!isClassMember) // Class members are added to class' method table - ctx->add(SimplifyItem::Func, func_name, canonicalName, ctx->isToplevel()); + ctx->add(SimplifyItem::Func, stmt->name, rootName, ctx->isToplevel()); if (isClassMember) ctx->bases.push_back(oldBases[0]); ctx->bases.emplace_back(SimplifyContext::Base{canonicalName}); // Add new base... @@ -614,8 +619,7 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { // ... set the enclosing class name... attr.parentClass = ctx->bases.back().name; // ... add the method to class' method list ... - ctx->cache->classes[ctx->bases.back().name].methods[func_name].push_back( - {canonicalName, nullptr, ctx->cache->age}); + ctx->cache->classes[ctx->bases.back().name].methods[stmt->name] = rootName; // ... and if the function references outer class variable (by definition a // generic), mark it as not static as it needs fully instantiated class to be // realized. For example, in class A[T]: def foo(): pass, A.foo() can be realized @@ -624,6 +628,7 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { if (isMethod) attr.set(Attr::Method); } + ctx->cache->overloads[rootName].push_back({canonicalName, ctx->cache->age}); std::vector partialArgs; if (!captures.empty()) { @@ -649,21 +654,21 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { ExprPtr finalExpr; if (!captures.empty()) - finalExpr = N(N(func_name), partialArgs); + finalExpr = N(N(stmt->name), partialArgs); if (isClassMember && decorators.size()) error("decorators cannot be applied to class methods"); for (int j = int(decorators.size()) - 1; j >= 0; j--) { if (auto c = const_cast(decorators[j]->getCall())) { c->args.emplace(c->args.begin(), - CallExpr::Arg{"", finalExpr ? finalExpr : N(func_name)}); + CallExpr::Arg{"", finalExpr ? finalExpr : N(stmt->name)}); finalExpr = N(c->expr, c->args); } else { finalExpr = - N(decorators[j], finalExpr ? finalExpr : N(func_name)); + N(decorators[j], finalExpr ? finalExpr : N(stmt->name)); } } if (finalExpr) - resultStmt = transform(N(N(func_name), finalExpr)); + resultStmt = transform(N(N(stmt->name), finalExpr)); } void SimplifyVisitor::visit(ClassStmt *stmt) { @@ -744,6 +749,7 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { ClassStmt *originalAST = nullptr; auto classItem = std::make_shared(SimplifyItem::Type, "", "", ctx->isToplevel()); + classItem->moduleName = ctx->getModule(); if (!extension) { classItem->canonicalName = canonicalName = ctx->generateCanonicalName(name, !attr.has(Attr::Internal)); @@ -949,22 +955,29 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { for (int ai = 0; ai < baseASTs.size(); ai++) { // FUNCS for (auto &mm : ctx->cache->classes[baseASTs[ai]->name].methods) - for (auto &mf : mm.second) { + for (auto &mf : ctx->cache->overloads[mm.second]) { auto f = ctx->cache->functions[mf.name].ast; if (f->attributes.has("autogenerated")) continue; auto subs = substitutions[ai]; - if (ctx->cache->classes[ctx->bases.back().name] - .methods[ctx->cache->reverseIdentifierLookup[f->name]] - .empty()) - generateDispatch(ctx->cache->reverseIdentifierLookup[f->name]); - auto newName = ctx->generateCanonicalName( - ctx->cache->reverseIdentifierLookup[f->name], true); + + std::string rootName; + auto &mts = ctx->cache->classes[ctx->bases.back().name].methods; + auto it = mts.find(ctx->cache->reverseIdentifierLookup[f->name]); + if (it != mts.end()) + rootName = it->second; + else + rootName = ctx->generateCanonicalName( + ctx->cache->reverseIdentifierLookup[f->name], true); + auto newCanonicalName = + format("{}:{}", rootName, ctx->cache->overloads[rootName].size()); + ctx->cache->reverseIdentifierLookup[newCanonicalName] = + ctx->cache->reverseIdentifierLookup[f->name]; auto nf = std::dynamic_pointer_cast( replace(std::static_pointer_cast(f), subs)); - subs[nf->name] = N(newName); - nf->name = newName; + subs[nf->name] = N(newCanonicalName); + nf->name = newCanonicalName; suite->stmts.push_back(nf); nf->attributes.parentClass = ctx->bases.back().name; @@ -972,10 +985,10 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { if (nf->attributes.has(".changedSelf")) // replace self type with new class nf->args[0].type = transformType(ctx->bases.back().ast); preamble->functions.push_back(clone(nf)); - ctx->cache->functions[newName].ast = nf; + ctx->cache->overloads[rootName].push_back({newCanonicalName, ctx->cache->age}); + ctx->cache->functions[newCanonicalName].ast = nf; ctx->cache->classes[ctx->bases.back().name] - .methods[ctx->cache->reverseIdentifierLookup[f->name]] - .push_back({newName, nullptr, ctx->cache->age}); + .methods[ctx->cache->reverseIdentifierLookup[f->name]] = rootName; } } for (auto sp : getClassMethods(stmt->suite)) @@ -1248,8 +1261,10 @@ StmtPtr SimplifyVisitor::transformCImport(const std::string &name, auto f = N(name, ret ? ret->clone() : N("void"), fnArgs, nullptr, attr); StmtPtr tf = transform(f); // Already in the preamble - if (!altName.empty()) + if (!altName.empty()) { ctx->add(altName, ctx->find(name)); + ctx->remove(name); + } return tf; } @@ -1392,10 +1407,11 @@ void SimplifyVisitor::transformNewImport(const ImportFile &file) { stmts[0] = N(); // Add a def import(): ... manually to the cache and to the preamble (it won't be // transformed here!). - ctx->cache->functions[importVar].ast = - N(importVar, nullptr, std::vector{}, N(stmts), - Attr({Attr::ForceRealize})); - preamble->functions.push_back(ctx->cache->functions[importVar].ast->clone()); + ctx->cache->overloads[importVar].push_back({importVar, ctx->cache->age}); + ctx->cache->functions[importVar + ":0"].ast = + N(importVar + ":0", nullptr, std::vector{}, + N(stmts), Attr({Attr::ForceRealize})); + preamble->functions.push_back(ctx->cache->functions[importVar + ":0"].ast->clone()); ; } } @@ -1768,15 +1784,5 @@ std::vector SimplifyVisitor::getClassMethods(const StmtPtr &s) { return v; } -void SimplifyVisitor::generateDispatch(const std::string &name) { - transform(N( - name + ".dispatch", nullptr, - std::vector{Param("*args"), Param("**kwargs")}, - N(N(N( - N(N(ctx->bases.back().name), name), - N(N("args")), N(N("kwargs"))))), - Attr({"autogenerated"}))); -} - } // namespace ast } // namespace codon diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index 492211d6..7e0ba953 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -296,7 +296,8 @@ void TranslateVisitor::visit(ForStmt *stmt) { auto c = stmt->decorator->getCall(); seqassert(c, "for par is not a call: {}", stmt->decorator->toString()); auto fc = c->expr->getType()->getFunc(); - seqassert(fc && fc->ast->name == "std.openmp.for_par", "for par is not a function"); + seqassert(fc && fc->ast->name == "std.openmp.for_par:0", + "for par is not a function"); auto schedule = fc->funcGenerics[0].type->getStatic()->expr->staticValue.getString(); bool ordered = fc->funcGenerics[1].type->getStatic()->expr->staticValue.getInt(); diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index 97b77ac0..f8013fc6 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -313,6 +313,8 @@ private: int64_t translateIndex(int64_t idx, int64_t len, bool clamp = false); int64_t sliceAdjustIndices(int64_t length, int64_t *start, int64_t *stop, int64_t step); + types::FuncTypePtr findDispatch(const std::string &fn); + std::string getRootName(const std::string &name); friend struct Cache; }; diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.cpp b/codon/parser/visitors/typecheck/typecheck_ctx.cpp index 2e5ba2f7..aac6eb51 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.cpp +++ b/codon/parser/visitors/typecheck/typecheck_ctx.cpp @@ -142,22 +142,23 @@ std::vector TypeContext::findMethod(const std::string &typeN auto m = cache->classes.find(typeName); if (m != cache->classes.end()) { auto t = m->second.methods.find(method); - if (t != m->second.methods.end() && !t->second.empty()) { - seqassert(!t->second.empty() && endswith(t->second[0].name, ".dispatch"), - "first method '{}' is not dispatch", t->second[0].name); + if (t != m->second.methods.end()) { + auto mt = cache->overloads[t->second]; std::unordered_set signatureLoci; std::vector vv; - for (int mti = int(t->second.size()) - 1; mti > 0; mti--) { - auto &mt = t->second[mti]; - if (mt.age <= age) { + for (int mti = int(mt.size()) - 1; mti >= 0; mti--) { + auto &m = mt[mti]; + if (endswith(m.name, ":dispatch")) + continue; + if (m.age <= age) { if (hideShadowed) { - auto sig = cache->functions[mt.name].ast->signature(); + auto sig = cache->functions[m.name].ast->signature(); if (!in(signatureLoci, sig)) { signatureLoci.insert(sig); - vv.emplace_back(mt.type); + vv.emplace_back(cache->functions[m.name].type); } } else { - vv.emplace_back(mt.type); + vv.emplace_back(cache->functions[m.name].type); } } } diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 0a390879..78d4171c 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -105,6 +105,9 @@ void TypecheckVisitor::visit(IdExpr *expr) { return; } auto val = ctx->find(expr->value); + if (!val) { + val = ctx->find(expr->value + ":0"); // is it function?! + } seqassert(val, "cannot find IdExpr '{}' ({})", expr->value, expr->getSrcInfo()); auto t = ctx->instantiate(expr, val->type); @@ -725,14 +728,17 @@ ExprPtr TypecheckVisitor::transformBinary(BinaryExpr *expr, bool isAtomic, ExprPtr TypecheckVisitor::transformStaticTupleIndex(ClassType *tuple, ExprPtr &expr, ExprPtr &index) { - if (!tuple->getRecord() || - in(std::set{"Ptr", "pyobj", "str", "Array"}, tuple->name)) + if (!tuple->getRecord()) + return nullptr; + if (!startswith(tuple->name, TYPE_TUPLE) && !startswith(tuple->name, TYPE_PARTIAL)) + // in(std::set{"Ptr", "pyobj", "str", "Array"}, tuple->name)) // Ptr, pyobj and str are internal types and have only one overloaded __getitem__ return nullptr; - if (ctx->cache->classes[tuple->name].methods["__getitem__"].size() != 2) - // n.b.: there is dispatch as well - // TODO: be smarter! there might be a compatible getitem? - return nullptr; + // if (in(ctx->cache->classes[tuple->name].methods, "__getitem__")) { + // ctx->cache->overloads[ctx->cache->classes[tuple->name].methods["__getitem__"]] + // .size() != 1) + // return nullptr; + // } // Extract a static integer value from a compatible expression. auto getInt = [&](int64_t *o, const ExprPtr &e) { @@ -919,9 +925,7 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, } else if (methods.size() > 1) { auto m = ctx->cache->classes.find(typ->name); auto t = m->second.methods.find(expr->member); - seqassert(!t->second.empty() && endswith(t->second[0].name, ".dispatch"), - "first method is not dispatch"); - bestMethod = t->second[0].type; + bestMethod = findDispatch(t->second); } else { bestMethod = methods[0]; } @@ -1036,7 +1040,8 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in ctx->bases.back().supers, expr->args); if (m.empty()) error("no matching super methods are available"); - // LOG("found {} <- {}", ctx->bases.back().type->getFunc()->toString(), m[0]->toString()); + // LOG("found {} <- {}", ctx->bases.back().type->getFunc()->toString(), + // m[0]->toString()); ExprPtr e = N(N(m[0]->ast->name), expr->args); return transform(e, false, true); } @@ -1700,7 +1705,7 @@ TypecheckVisitor::findBestMethod(const Expr *expr, const std::string &member, std::vector TypecheckVisitor::findSuperMethods(const types::FuncTypePtr &func) { if (func->ast->attributes.parentClass.empty() || - endswith(func->ast->name, ".dispatch")) + endswith(func->ast->name, ":dispatch")) return {}; auto p = ctx->find(func->ast->attributes.parentClass)->type; if (!p || !p->getClass()) @@ -1711,14 +1716,13 @@ TypecheckVisitor::findSuperMethods(const types::FuncTypePtr &func) { std::vector result; if (m != ctx->cache->classes.end()) { auto t = m->second.methods.find(methodName); - if (t != m->second.methods.end() && !t->second.empty()) { - seqassert(!t->second.empty() && endswith(t->second[0].name, ".dispatch"), - "first method '{}' is not dispatch", t->second[0].name); - for (int mti = 1; mti < t->second.size(); mti++) { - auto &mt = t->second[mti]; - if (mt.type->ast->name == func->ast->name) + if (t != m->second.methods.end()) { + for (auto &m : ctx->cache->overloads[t->second]) { + if (endswith(m.name, ":dispatch")) + continue; + if (m.name == func->ast->name) break; - result.emplace_back(mt.type); + result.emplace_back(ctx->cache->functions[m.name].type); } } } @@ -1860,5 +1864,45 @@ int64_t TypecheckVisitor::sliceAdjustIndices(int64_t length, int64_t *start, return 0; } +types::FuncTypePtr TypecheckVisitor::findDispatch(const std::string &fn) { + for (auto &m : ctx->cache->overloads[fn]) + if (endswith(ctx->cache->functions[m.name].ast->name, ":dispatch")) + return ctx->cache->functions[m.name].type; + + // Generate dispatch and return it! + auto name = fn + ":dispatch"; + + ExprPtr root; + auto a = ctx->cache->functions[ctx->cache->overloads[fn][0].name].ast; + if (!a->attributes.parentClass.empty()) + root = N(N(a->attributes.parentClass), + ctx->cache->reverseIdentifierLookup[fn]); + else + root = N(fn); + root = N(root, N(N("args")), + N(N("kwargs"))); + auto ast = N( + name, nullptr, std::vector{Param("*args"), Param("**kwargs")}, + N(N( + N(N("isinstance"), root->clone(), N("void")), + N(root->clone()), N(root))), + Attr({"autogenerated"})); + ctx->cache->reverseIdentifierLookup[name] = ctx->cache->reverseIdentifierLookup[fn]; + + auto baseType = + ctx->instantiate(N(name).get(), ctx->find(generateFunctionStub(2))->type, + nullptr, false) + ->getRecord(); + auto typ = std::make_shared(baseType, ast.get()); + typ = std::static_pointer_cast(typ->generalize(ctx->typecheckLevel)); + ctx->add(TypecheckItem::Func, name, typ); + + ctx->cache->overloads[fn].insert(ctx->cache->overloads[fn].begin(), {name, 0}); + ctx->cache->functions[name].ast = ast; + ctx->cache->functions[name].type = typ; + prependStmts->push_back(ast); + return typ; +} + } // namespace ast } // namespace codon diff --git a/codon/parser/visitors/typecheck/typecheck_stmt.cpp b/codon/parser/visitors/typecheck/typecheck_stmt.cpp index edbe2ad2..42ece717 100644 --- a/codon/parser/visitors/typecheck/typecheck_stmt.cpp +++ b/codon/parser/visitors/typecheck/typecheck_stmt.cpp @@ -474,12 +474,12 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { typ = std::static_pointer_cast(typ->generalize(ctx->typecheckLevel)); // Check if this is a class method; if so, update the class method lookup table. if (isClassMember) { - auto &methods = ctx->cache->classes[attr.parentClass] - .methods[ctx->cache->reverseIdentifierLookup[stmt->name]]; + auto m = ctx->cache->classes[attr.parentClass] + .methods[ctx->cache->reverseIdentifierLookup[stmt->name]]; bool found = false; - for (auto &i : methods) + for (auto &i : ctx->cache->overloads[m]) if (i.name == stmt->name) { - i.type = typ; + ctx->cache->functions[i.name].type = typ; found = true; break; } @@ -570,5 +570,11 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { stmt->done = true; } +std::string TypecheckVisitor::getRootName(const std::string &name) { + auto p = name.rfind(':'); + seqassert(p != std::string::npos, ": not found in {}", name); + return name.substr(0, p); +} + } // namespace ast } // namespace codon diff --git a/stdlib/internal/str.codon b/stdlib/internal/str.codon index 67a8dd67..ad062b5a 100644 --- a/stdlib/internal/str.codon +++ b/stdlib/internal/str.codon @@ -44,6 +44,7 @@ class str: if c == '\n': d = "\\n" elif c == '\r': d = "\\r" elif c == '\t': d = "\\t" + elif c == '\a': d = "\\a" elif c == '\\': d = "\\\\" elif c == q: d = qe else: diff --git a/test/parser/simplify_stmt.codon b/test/parser/simplify_stmt.codon index 6de479b8..d849b7dd 100644 --- a/test/parser/simplify_stmt.codon +++ b/test/parser/simplify_stmt.codon @@ -538,7 +538,7 @@ def foo() -> int: a{=a} foo() #! not a type or static expression -#! while realizing foo (arguments foo) +#! while realizing foo:0 (arguments foo:0) #%% function_llvm_err_4,barebones a = 5 diff --git a/test/parser/typecheck_expr.codon b/test/parser/typecheck_expr.codon index 1403319e..ce02e25b 100644 --- a/test/parser/typecheck_expr.codon +++ b/test/parser/typecheck_expr.codon @@ -374,7 +374,7 @@ def foo(i, j, k): return i + j + k print foo(1.1, 2.2, 3.3) #: 6.6 p = foo(6, ...) -print p.__class__ #: foo[int,...,...] +print p.__class__ #: foo:0[int,...,...] print p(2, 1) #: 9 print p(k=3, j=6) #: 15 q = p(k=1, ...) @@ -390,11 +390,11 @@ print 42 |> add_two #: 44 def moo(a, b, c=3): print a, b, c m = moo(b=2, ...) -print m.__class__ #: moo[...,int,...] +print m.__class__ #: moo:0[...,int,...] m('s', 1.1) #: s 2 1.1 # # n = m(c=2.2, ...) -print n.__class__ #: moo[...,int,float] +print n.__class__ #: moo:0[...,int,float] n('x') #: x 2 2.2 print n('y').__class__ #: void @@ -403,11 +403,11 @@ def ff(a, b, c): print ff(1.1, 2, True).__class__ #: Tuple[float,int,bool] print ff(1.1, ...)(2, True).__class__ #: Tuple[float,int,bool] y = ff(1.1, ...)(c=True, ...) -print y.__class__ #: ff[float,...,bool] +print y.__class__ #: ff:0[float,...,bool] print ff(1.1, ...)(2, ...)(True).__class__ #: Tuple[float,int,bool] print y('hei').__class__ #: Tuple[float,str,bool] z = ff(1.1, ...)(c='s', ...) -print z.__class__ #: ff[float,...,str] +print z.__class__ #: ff:0[float,...,str] #%% call_arguments_partial,barebones def doo[R, T](a: Callable[[T], R], b: Generator[T], c: Optional[T], d: T): @@ -432,7 +432,7 @@ l = [1] def adder(a, b): return a+b doo(b=l, d=Optional(5), c=l[0], a=adder(b=4, ...)) #: int int -#: adder[.. Generator[int] +#: adder:0[ Generator[int] #: 5 #: 1 Optional[int] #: 5 int diff --git a/test/parser/typecheck_stmt.codon b/test/parser/typecheck_stmt.codon index ace31779..a3099c5c 100644 --- a/test/parser/typecheck_stmt.codon +++ b/test/parser/typecheck_stmt.codon @@ -252,7 +252,7 @@ try: except MyError: print "my" except OSError as o: - print "os", o._hdr[0], len(o._hdr[1]), o._hdr[3][-20:], o._hdr[4] + print "os", o._hdr.typename, len(o._hdr.msg), o._hdr.file[-20:], o._hdr.line #: os std.internal.types.error.OSError 9 typecheck_stmt.codon 249 finally: print "whoa" #: whoa @@ -263,7 +263,7 @@ def foo(): try: foo() except MyError as e: - print e._hdr[0], e._hdr[1] #: MyError foo! + print e._hdr.typename, e._hdr.msg #: MyError foo! #%% throw_error,barebones raise 'hello' #! cannot throw non-exception (first object member must be of type ExcHeader) @@ -291,13 +291,13 @@ def foo(x): print len(x) foo(5) #: 4 -def foo(x): +def foo2(x): if isinstance(x, int): print x+1 return print len(x) -foo(1) #: 2 -foo('s') #: 1 +foo2(1) #: 2 +foo2('s') #: 1 #%% super,barebones class Foo: @@ -341,4 +341,4 @@ class Foo: print 'foo-1', a Foo.foo(1) #! no matching super methods are available -#! while realizing Foo.foo.2 +#! while realizing Foo.foo:0 diff --git a/test/parser/types.codon b/test/parser/types.codon index 764a38a9..5302905a 100644 --- a/test/parser/types.codon +++ b/test/parser/types.codon @@ -199,10 +199,10 @@ def f[T](x: T) -> T: print f(1.2).__class__ #: float print f('s').__class__ #: str -def f[T](x: T): - return f(x - 1, T) if x else 1 -print f(1) #: 1 -print f(1.1).__class__ #: int +def f2[T](x: T): + return f2(x - 1, T) if x else 1 +print f2(1) #: 1 +print f2(1.1).__class__ #: int #%% recursive_error,barebones @@ -215,7 +215,7 @@ def rec3(x, y): #- ('a, 'b) -> 'b return y rec3(1, 's') #! cannot unify str and int -#! while realizing rec3 (arguments rec3[int,str]) +#! while realizing rec3:0 (arguments rec3:0[int,str]) #%% instantiate_function_2,barebones def fx[T](x: T) -> T: @@ -447,13 +447,13 @@ def f(x): return g(x) print f(5), f('s') #: 5 s -def f[U](x: U, y): +def f2[U](x: U, y): def g[T, U](x: T, y: U): return (x, y) return g(y, x) x, y = 1, 'haha' -print f(x, y).__class__ #: Tuple[str,int] -print f('aa', 1.1, U=str).__class__ #: Tuple[float,str] +print f2(x, y).__class__ #: Tuple[str,int] +print f2('aa', 1.1, U=str).__class__ #: Tuple[float,str] #%% nested_fn_generic_error,barebones def f[U](x: U, y): # ('u, 'a) -> tuple['a, 'u] @@ -464,7 +464,7 @@ print f(1.1, 1, int).__class__ #! cannot unify float and int #%% fn_realization,barebones def ff[T](x: T, y: tuple[T]): - print ff(T=str,...).__class__ #: ff[str,Tuple[str],str] + print ff(T=str,...).__class__ #: ff:0[str,Tuple[str],str] return x x = ff(1, (1,)) print x, x.__class__ #: 1 int @@ -474,7 +474,7 @@ def fg[T](x:T): def g[T](y): z = T() return z - print fg(T=str,...).__class__ #: fg[str,str] + print fg(T=str,...).__class__ #: fg:0[str,str] print g(1, T).__class__ #: int fg(1) print fg(1).__class__ #: void @@ -515,7 +515,7 @@ class A[T]: def foo[W](t: V, u: V, v: V, w: W): return (t, u, v, w) -print A.B.C[bool].foo(W=str, ...).__class__ #: A.B.C.foo.2[bool,bool,bool,str,str] +print A.B.C[bool].foo(W=str, ...).__class__ #: A.B.C.foo:0[bool,bool,bool,str,str] print A.B.C.foo(1,1,1,True) #: (1, 1, 1, True) print A.B.C.foo('x', 'x', 'x', 'x') #: ('x', 'x', 'x', 'x') print A.B.C.foo('x', 'x', 'x', 'x') #: ('x', 'x', 'x', 'x') @@ -734,10 +734,10 @@ def test(name, sort, key): def foo(l, f): return [f(i) for i in l] test('hi', foo, lambda x: x+1) #: hi [2, 3, 4, 5] -# TODO -# def foof(l: List[int], x, f: Callable[[int], int]): -# return [f(i)+x for i in l] -# test('qsort', foof(..., 3, ...)) + +def foof(l: List[int], x, f: Callable[[int], int]): + return [f(i)+x for i in l] +test('qsort', foof(x=3, ...), lambda x: x+1) #: qsort [5, 6, 7, 8] #%% class_fn_access,barebones class X[T]: @@ -745,8 +745,7 @@ class X[T]: return (x+x, y+y) y = X[X[int]]() print y.__class__ #: X[X[int]] -print X[float].foo(U=int, ...).__class__ #: X.foo.2[X[float],float,int,int] -# print y.foo.1[float].__class__ +print X[float].foo(U=int, ...).__class__ #: X.foo:0[X[float],float,int,int] print X[int]().foo(1, 's') #: (2, 'ss') #%% class_partial_access,barebones @@ -754,7 +753,7 @@ class X[T]: def foo[U](self, x, y: U): return (x+x, y+y) y = X[X[int]]() -print y.foo(U=float,...).__class__ #: X.foo.2[X[X[int]],...,...] +print y.foo(U=float,...).__class__ #: X.foo:0[X[X[int]],...,...] print y.foo(1, 2.2, float) #: (2, 4.4) #%% forward,barebones @@ -765,10 +764,10 @@ def bar[T](x): print x, T.__class__ foo(bar, 1) #: 1 int -#: bar[...] +#: bar:0[...] foo(bar(...), 's') #: s str -#: bar[...] +#: bar:0[...] z = bar z('s', int) #: s int @@ -786,8 +785,8 @@ def foo(f, x): def bar[T](x): print x, T.__class__ foo(bar(T=int,...), 1) -#! too many arguments for bar[T1,int] (expected maximum 2, got 2) -#! while realizing foo (arguments foo[bar[...],int]) +#! too many arguments for bar:0[T1,int] (expected maximum 2, got 2) +#! while realizing foo:0 (arguments foo:0[bar:0[...],int]) #%% sort_partial def foo(x, y): @@ -806,16 +805,16 @@ def frec(x, y): return grec(x, y) if bl(y) else 2 print frec(1, 2).__class__, frec('s', 1).__class__ #! expression with void type -#! while realizing frec (arguments frec[int,int]) +#! while realizing frec:0 (arguments frec:0[int,int]) #%% return_fn,barebones def retfn(a): def inner(b, *args, **kwargs): print a, b, args, kwargs - print inner.__class__ #: retfn.inner[...,...,int,...] + print inner.__class__ #: retfn:0.inner:0[...,...,int,...] return inner(15, ...) f = retfn(1) -print f.__class__ #: retfn.inner[int,...,int,...] +print f.__class__ #: retfn:0.inner:0[int,...,int,...] f(2,3,foo='bar') #: 1 15 (2, 3) (foo: 'bar') #%% decorator_manual,barebones @@ -823,7 +822,7 @@ def foo(x, *args, **kwargs): print x, args, kwargs return 1 def dec(fn, a): - print 'decorating', fn.__class__ #: decorating foo[...,...,...] + print 'decorating', fn.__class__ #: decorating foo:0[...,...,...] def inner(*args, **kwargs): print 'decorator', args, kwargs #: decorator (5.5, 's') (z: True) return fn(a, *args, **kwargs) @@ -846,7 +845,7 @@ def dec(fn, a): return inner ff = dec(foo, 10) print ff(5.5, 's', z=True) -#: decorating foo[...,...,...] +#: decorating foo:0[...,...,...] #: decorator (5.5, 's') (z: True) #: 10 (5.5, 's') (z: True) #: 1 @@ -856,7 +855,7 @@ def zoo(e, b, *args): return f'zoo: {e}, {b}, {args}' print zoo(2, 3) print zoo('s', 3) -#: decorating zoo[...,...,...] +#: decorating zoo:0[...,...,...] #: decorator (2, 3) () #: zoo: 5, 2, (3) #: decorator ('s', 3) () @@ -869,9 +868,9 @@ def mydecorator(func): print("after") return inner @mydecorator -def foo(): +def foo2(): print("foo") -foo() +foo2() #: before #: foo #: after @@ -891,7 +890,7 @@ def factorial(num): return n factorial(10) #: 3628800 -#: time needed for factorial[...] is 3628799 +#: time needed for factorial:0[...] is 3628799 def dx1(func): def inner(): @@ -921,9 +920,9 @@ def dy2(func): return inner @dy1 @dy2 -def num(a, b): +def num2(a, b): return a+b -print(num(10, 20)) #: 3600 +print(num2(10, 20)) #: 3600 #%% hetero_iter,barebones e = (1, 2, 3, 'foo', 5, 'bar', 6) @@ -970,14 +969,14 @@ def tee(iterable, n=2): return list(gen(d) for d in deques) it = [1,2,3,4] a, b = tee(it) #! cannot typecheck the program -#! while realizing tee (arguments tee[List[int],int]) +#! while realizing tee:0 (arguments tee:0[List[int],int]) #%% new_syntax,barebones def foo[T,U](x: type, y, z: Static[int] = 10): print T.__class__, U.__class__, x.__class__, y.__class__, Int[z+1].__class__ return List[x]() -print foo(T=int,U=str,...).__class__ #: foo[T1,x,z,int,str] -print foo(T=int,U=str,z=5,x=bool,...).__class__ #: foo[T1,bool,5,int,str] +print foo(T=int,U=str,...).__class__ #: foo:0[T1,x,z,int,str] +print foo(T=int,U=str,z=5,x=bool,...).__class__ #: foo:0[T1,bool,5,int,str] print foo(float,3,T=int,U=str,z=5).__class__ #: List[float] foo(float,1,10,str,int) #: str int float int Int[11] @@ -993,11 +992,11 @@ print Foo[5,int,float,6].__class__ #: Foo[5,int,float,6] print Foo(1.1, 10i32, [False], 10u66).__class__ #: Foo[66,bool,float,32] -def foo[N: Static[int]](): +def foo2[N: Static[int]](): print Int[N].__class__, N x: Static[int] = 5 y: Static[int] = 105 - x * 2 -foo(y-x) #: Int[90] 90 +foo2(y-x) #: Int[90] 90 if 1.1+2.2 > 0: x: Static[int] = 88 From 5bca6e47a99da2bf48fb3dc2d1170039b7260690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Wed, 29 Dec 2021 09:56:07 -0800 Subject: [PATCH 12/61] Support for overloaded functions --- .../visitors/simplify/simplify_stmt.cpp | 2 +- codon/parser/visitors/typecheck/typecheck.h | 2 + .../visitors/typecheck/typecheck_expr.cpp | 41 ++++++++++++++++++- stdlib/internal/str.codon | 1 - test/parser/types.codon | 25 +++++++++++ 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 7e9f2c3b..9664e99b 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -1407,7 +1407,7 @@ void SimplifyVisitor::transformNewImport(const ImportFile &file) { stmts[0] = N(); // Add a def import(): ... manually to the cache and to the preamble (it won't be // transformed here!). - ctx->cache->overloads[importVar].push_back({importVar, ctx->cache->age}); + ctx->cache->overloads[importVar].push_back({importVar + ":0", ctx->cache->age}); ctx->cache->functions[importVar + ":0"].ast = N(importVar + ":0", nullptr, std::vector{}, N(stmts), Attr({Attr::ForceRealize})); diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index f8013fc6..92777c51 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -293,6 +293,8 @@ private: const std::vector &args); types::FuncTypePtr findBestMethod(const Expr *expr, const std::string &member, const std::vector &args); + types::FuncTypePtr findBestMethod(const std::string &fn, + const std::vector &args); std::vector findSuperMethods(const types::FuncTypePtr &func); std::vector findMatchingMethods(types::ClassType *typ, diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 78d4171c..4c0bd65b 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -106,7 +106,15 @@ void TypecheckVisitor::visit(IdExpr *expr) { } auto val = ctx->find(expr->value); if (!val) { - val = ctx->find(expr->value + ":0"); // is it function?! + auto i = ctx->cache->overloads.find(expr->value); + if (i != ctx->cache->overloads.end()) { + if (i->second.size() == 1) { + val = ctx->find(i->second[0].name); + } else { + auto d = findDispatch(expr->value); + val = ctx->find(d->ast->name); + } + } } seqassert(val, "cannot find IdExpr '{}' ({})", expr->value, expr->getSrcInfo()); @@ -1062,6 +1070,24 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in if (auto ed = const_cast((*lhs)->getDot())) { if (auto edt = transformDot(ed, &expr->args)) *lhs = edt; + } else if (auto ei = const_cast((*lhs)->getId())) { + // check if this is an overloaded function? + auto i = ctx->cache->overloads.find(ei->value); + if (i != ctx->cache->overloads.end() && i->second.size() != 1) { + if (auto bestMethod = findBestMethod(ei->value, expr->args)) { + ExprPtr e = N(bestMethod->ast->name); + auto t = ctx->instantiate(expr, bestMethod); + unify(e->type, t); + unify(ei->type, e->type); + *lhs = e; + } else { + std::vector nice; + for (auto &t : expr->args) + nice.emplace_back(format("{} = {}", t.name, t.value->type->toString())); + error("cannot find an overload '{}' with arguments {}", ei->value, + join(nice, ", ")); + } + } } expr->expr = transform(expr->expr, true); @@ -1702,6 +1728,18 @@ TypecheckVisitor::findBestMethod(const Expr *expr, const std::string &member, return m.empty() ? nullptr : m[0]; } +types::FuncTypePtr +TypecheckVisitor::findBestMethod(const std::string &fn, + const std::vector &args) { + std::vector methods; + for (auto &m : ctx->cache->overloads[fn]) + if (!endswith(m.name, ":dispatch")) + methods.push_back(ctx->cache->functions[m.name].type); + std::reverse(methods.begin(), methods.end()); + auto m = findMatchingMethods(nullptr, methods, args); + return m.empty() ? nullptr : m[0]; +} + std::vector TypecheckVisitor::findSuperMethods(const types::FuncTypePtr &func) { if (func->ast->attributes.parentClass.empty() || @@ -1901,6 +1939,7 @@ types::FuncTypePtr TypecheckVisitor::findDispatch(const std::string &fn) { ctx->cache->functions[name].ast = ast; ctx->cache->functions[name].type = typ; prependStmts->push_back(ast); + // LOG("dispatch: {}", ast->toString(1)); return typ; } diff --git a/stdlib/internal/str.codon b/stdlib/internal/str.codon index ad062b5a..67a8dd67 100644 --- a/stdlib/internal/str.codon +++ b/stdlib/internal/str.codon @@ -44,7 +44,6 @@ class str: if c == '\n': d = "\\n" elif c == '\r': d = "\\r" elif c == '\t': d = "\\t" - elif c == '\a': d = "\\a" elif c == '\\': d = "\\\\" elif c == q: d = qe else: diff --git a/test/parser/types.codon b/test/parser/types.codon index 5302905a..cd36f184 100644 --- a/test/parser/types.codon +++ b/test/parser/types.codon @@ -1107,3 +1107,28 @@ v = [1] methodcaller('append')(v, 42) print v #: [1, 42] print methodcaller('index')(v, 42) #: 1 + + +#%% fn_overloads,barebones +def foo(x): + return 1, x + +def foo(x, y): + def foo(x, y): + return f'{x}_{y}' + return 2, foo(x, y) + +def foo(x): + if x == '': + return 3, 0 + return 3, 1 + foo(x[1:])[1] + +print foo('hi') #: (3, 2) +print foo('hi', 1) #: (2, 'hi_1') + +#%% fn_overloads_error,barebones +def foo(x): + return 1, x +def foo(x, y): + return 2, x, y +foo('hooooooooy!', 1, 2) #! cannot find an overload 'foo' with arguments = str, = int, = int From 682b97676dbc261f368c9104b9cfb9e365734466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Wed, 29 Dec 2021 13:11:56 -0800 Subject: [PATCH 13/61] Add mimetype support [wip] --- CMakeLists.txt | 4 +-- extra/jupyter/{src/codon.cpp => jupyter.cpp} | 33 ++++++++++++++++++-- extra/jupyter/{src/codon.h => jupyter.h} | 0 3 files changed, 33 insertions(+), 4 deletions(-) rename extra/jupyter/{src/codon.cpp => jupyter.cpp} (84%) rename extra/jupyter/{src/codon.h => jupyter.h} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa87c24c..02e5c11c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,7 +209,7 @@ set(CODON_HPPFILES codon/util/toml++/toml_node.h codon/util/toml++/toml_parser.hpp codon/util/toml++/toml_utf8_streams.h - extra/jupyter/src/codon.h) + extra/jupyter/jupyter.h) set(CODON_CPPFILES codon/compiler/compiler.cpp codon/compiler/debug_listener.cpp @@ -288,7 +288,7 @@ set(CODON_CPPFILES codon/sir/var.cpp codon/util/common.cpp codon/util/fmt/format.cpp - extra/jupyter/src/codon.cpp) + extra/jupyter/jupyter.cpp) add_library(codonc SHARED ${CODON_HPPFILES}) target_sources(codonc PRIVATE ${CODON_CPPFILES} codon_rules.cpp omp_rules.cpp) if(CODON_JUPYTER) diff --git a/extra/jupyter/src/codon.cpp b/extra/jupyter/jupyter.cpp similarity index 84% rename from extra/jupyter/src/codon.cpp rename to extra/jupyter/jupyter.cpp index 9f788b6d..1397b1ee 100644 --- a/extra/jupyter/src/codon.cpp +++ b/extra/jupyter/jupyter.cpp @@ -1,9 +1,11 @@ -#include "codon.h" +#include "jupyter.h" #ifdef CODON_JUPYTER +#include #include #include #include +#include #include #include #include @@ -50,8 +52,35 @@ nl::json CodonJupyter::execute_request_impl(int execution_counter, const string ast::join(backtrace, " \n")); }); if (failed.empty()) { + std::string msg = *result; nl::json pub_data; - pub_data["text/plain"] = *result; + if (ast::startswith(msg, "\x00\x00__codon/mime__\x00")) { + std::string mime = ""; + int i = 17; + for (; i < msg.size() && msg[i]; i++) + mime += msg[i]; + if (i < msg.size() && !msg[i]) { + i += 1; + } else { + mime = "text/plain"; + i = 0; + } + + std::string out; + out.reserve(msg.size() * 1.5); + for (; i < msg.size(); i++) { + uint8_t c = msg[i]; + if (c <= 127) { + out.push_back(c); + } else { + out.push_back((c >> 6) | 0xC0); + out.push_back((c & 0x3F) | 0x80); + } + } + pub_data[mime] = out; + } else { + pub_data["text/plain"] = msg; + } publish_execution_result(execution_counter, move(pub_data), nl::json::object()); return nl::json{{"status", "ok"}, {"payload", nl::json::array()}, diff --git a/extra/jupyter/src/codon.h b/extra/jupyter/jupyter.h similarity index 100% rename from extra/jupyter/src/codon.h rename to extra/jupyter/jupyter.h From b29635216c9b6e261bac9a573c7969a7a0e01143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Wed, 29 Dec 2021 17:07:49 -0800 Subject: [PATCH 14/61] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b9c5927a..3bc55219 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ Thumbs.db extra/jupyter/share/jupyter/kernels/codon/kernel.json scratch.* _* +.ipynb_checkpoints From d6ace332b8653705e31c2dbee4a6c3d54795883f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Wed, 29 Dec 2021 17:58:22 -0800 Subject: [PATCH 15/61] Fix partial dots --- .../visitors/typecheck/typecheck_expr.cpp | 76 ++++++++++--------- test/parser/types.codon | 3 +- test/stdlib/datetime_test.codon | 8 +- 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 9947a651..3007302c 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1053,38 +1053,43 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in return transform(e, false, true); } - // Intercept dot-callees (e.g. expr.foo). Needed in order to select a proper - // overload for magic methods and to avoid dealing with partial calls - // (a non-intercepted object DotExpr (e.g. expr.foo) will get transformed into a - // partial call). - ExprPtr *lhs = &expr->expr; - // Make sure to check for instantiation DotExpr (e.g. a.b[T]) as well. - if (auto ei = const_cast(expr->expr->getIndex())) { - // A potential function instantiation - lhs = &ei->expr; - } else if (auto eii = CAST(expr->expr, InstantiateExpr)) { - // Real instantiation - lhs = &eii->typeExpr; - } - if (auto ed = const_cast((*lhs)->getDot())) { - if (auto edt = transformDot(ed, &expr->args)) - *lhs = edt; - } else if (auto ei = const_cast((*lhs)->getId())) { - // check if this is an overloaded function? - auto i = ctx->cache->overloads.find(ei->value); - if (i != ctx->cache->overloads.end() && i->second.size() != 1) { - if (auto bestMethod = findBestMethod(ei->value, expr->args)) { - ExprPtr e = N(bestMethod->ast->name); - auto t = ctx->instantiate(expr, bestMethod); - unify(e->type, t); - unify(ei->type, e->type); - *lhs = e; - } else { - std::vector nice; - for (auto &t : expr->args) - nice.emplace_back(format("{} = {}", t.name, t.value->type->toString())); - error("cannot find an overload '{}' with arguments {}", ei->value, - join(nice, ", ")); + bool isPartial = !expr->args.empty() && expr->args.back().value->getEllipsis() && + !expr->args.back().value->getEllipsis()->isPipeArg && + expr->args.back().name.empty(); + if (!isPartial) { + // Intercept dot-callees (e.g. expr.foo). Needed in order to select a proper + // overload for magic methods and to avoid dealing with partial calls + // (a non-intercepted object DotExpr (e.g. expr.foo) will get transformed into a + // partial call). + ExprPtr *lhs = &expr->expr; + // Make sure to check for instantiation DotExpr (e.g. a.b[T]) as well. + if (auto ei = const_cast(expr->expr->getIndex())) { + // A potential function instantiation + lhs = &ei->expr; + } else if (auto eii = CAST(expr->expr, InstantiateExpr)) { + // Real instantiation + lhs = &eii->typeExpr; + } + if (auto ed = const_cast((*lhs)->getDot())) { + if (auto edt = transformDot(ed, &expr->args)) + *lhs = edt; + } else if (auto ei = const_cast((*lhs)->getId())) { + // check if this is an overloaded function? + auto i = ctx->cache->overloads.find(ei->value); + if (i != ctx->cache->overloads.end() && i->second.size() != 1) { + if (auto bestMethod = findBestMethod(ei->value, expr->args)) { + ExprPtr e = N(bestMethod->ast->name); + auto t = ctx->instantiate(expr, bestMethod); + unify(e->type, t); + unify(ei->type, e->type); + *lhs = e; + } else { + std::vector nice; + for (auto &t : expr->args) + nice.emplace_back(format("{} = {}", t.name, t.value->type->toString())); + error("cannot find an overload '{}' with arguments {}", ei->value, + join(nice, ", ")); + } } } } @@ -1137,7 +1142,7 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in std::vector args; std::vector typeArgs; int typeArgCount = 0; - bool isPartial = false; + // bool isPartial = false; int ellipsisStage = -1; auto newMask = std::vector(calleeFn->ast->args.size(), 1); auto getPartialArg = [&](int pi) { @@ -1159,7 +1164,6 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in calleeFn.get(), expr->args, [&](int starArgIndex, int kwstarArgIndex, const std::vector> &slots, bool partial) { - isPartial = partial; ctx->addBlock(); // add generics for default arguments. addFunctionGenerics(calleeFn->getFunc().get()); for (int si = 0, pi = 0; si < slots.size(); si++) { @@ -1272,8 +1276,8 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in // Special case: function instantiation if (isPartial && typeArgCount && typeArgCount == expr->args.size()) { for (auto &a : args) { - seqassert(a.value->getEllipsis(), "expected ellipsis"); - deactivateUnbounds(a.value->getType().get()); + if (a.value->getEllipsis()) + deactivateUnbounds(a.value->getType().get()); } auto e = transform(expr->expr); unify(expr->type, e->getType()); diff --git a/test/parser/types.codon b/test/parser/types.codon index 024c3fe3..82b21a66 100644 --- a/test/parser/types.codon +++ b/test/parser/types.codon @@ -753,7 +753,8 @@ class X[T]: def foo[U](self, x, y: U): return (x+x, y+y) y = X[X[int]]() -print y.foo(U=float,...).__class__ #: X.foo:0[X[X[int]],...,...] +# TODO: should this even be the case? +# print y.foo(U=float,...).__class__ -> X.foo:0[X[X[int]],...,...] print y.foo(1, 2.2, float) #: (2, 4.4) #%% forward,barebones diff --git a/test/stdlib/datetime_test.codon b/test/stdlib/datetime_test.codon index b17fb392..8c94cb2e 100644 --- a/test/stdlib/datetime_test.codon +++ b/test/stdlib/datetime_test.codon @@ -708,10 +708,10 @@ class TestDate[theclass](TestCase): iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split())) L = [] for i in range(400): - d = self.theclass(2000+i, 12, 31) - d1 = self.theclass(1600+i, 12, 31) - self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:]) - if d.isocalendar()[1] == 53: + d = self.theclass(2000+i, 12, 31).isocalendar() + d1 = self.theclass(1600+i, 12, 31).isocalendar() + self.assertEqual((d.week, d.weekday), (d1.week, d1.weekday)) + if d.week == 53: L.append(i) self.assertEqual(L, iso_long_years) From 0f38fc647320d85a2d73fad55aaac6747ee675b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Tue, 4 Jan 2022 10:13:59 -0800 Subject: [PATCH 16/61] Jupiter display fixes --- extra/jupyter/jupyter.cpp | 38 +++++++++++++++++------------------ stdlib/internal/builtin.codon | 13 +++++++----- stdlib/internal/python.codon | 10 +++++++++ 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/extra/jupyter/jupyter.cpp b/extra/jupyter/jupyter.cpp index 1397b1ee..cf912666 100644 --- a/extra/jupyter/jupyter.cpp +++ b/extra/jupyter/jupyter.cpp @@ -52,34 +52,34 @@ nl::json CodonJupyter::execute_request_impl(int execution_counter, const string ast::join(backtrace, " \n")); }); if (failed.empty()) { - std::string msg = *result; + std::string out = *result; + // std::string out; + // for (int i = 0; i < msg.size(); i++) { + // uint8_t c = msg[i]; + // if (c <= 127) { + // out.push_back(c); + // } else { + // out.push_back((c >> 6) | 0xC0); + // out.push_back((c & 0x3F) | 0x80); + // } + // } + nl::json pub_data; - if (ast::startswith(msg, "\x00\x00__codon/mime__\x00")) { + if (ast::startswith(out, "\x00\x00__codon/mime__\x00")) { std::string mime = ""; int i = 17; - for (; i < msg.size() && msg[i]; i++) - mime += msg[i]; - if (i < msg.size() && !msg[i]) { + for (; i < out.size() && out[i]; i++) + mime += out[i]; + if (i < out.size() && !out[i]) { i += 1; } else { mime = "text/plain"; i = 0; } - - std::string out; - out.reserve(msg.size() * 1.5); - for (; i < msg.size(); i++) { - uint8_t c = msg[i]; - if (c <= 127) { - out.push_back(c); - } else { - out.push_back((c >> 6) | 0xC0); - out.push_back((c & 0x3F) | 0x80); - } - } - pub_data[mime] = out; + pub_data[mime] = out.substr(i); + LOG("> {}: {}", mime, out.substr(i)); } else { - pub_data["text/plain"] = msg; + pub_data["text/plain"] = out; } publish_execution_result(execution_counter, move(pub_data), nl::json::object()); return nl::json{{"status", "ok"}, diff --git a/stdlib/internal/builtin.codon b/stdlib/internal/builtin.codon index e8e24044..9b0b86f3 100644 --- a/stdlib/internal/builtin.codon +++ b/stdlib/internal/builtin.codon @@ -331,10 +331,13 @@ class int: return result -def _jit_display(x, s: Static[str]): - if hasattr(x, "__repr_pretty__") and s == "jupyter": - print x.__repr_pretty__() +def _jit_display(x, s: Static[str], bundle: Set[str] = set()): + if hasattr(x, "_repr_mimebundle_") and s == "jupyter": + d = x._repr_mimebundle_(bundle) + # TODO: pick appropriate mime + mime = next(d.keys()) # just pick first + print(f"\x00\x00__codon/mime__\x00{mime}\x00{d[mime]}", end='') elif hasattr(x, "__repr__"): - print x.__repr__() + print(x.__repr__(), end='') elif hasattr(x, "__str__"): - print x.__str__() + print(x.__str__(), end='') diff --git a/stdlib/internal/python.codon b/stdlib/internal/python.codon index c5614649..35118315 100644 --- a/stdlib/internal/python.codon +++ b/stdlib/internal/python.codon @@ -235,6 +235,16 @@ class pyobj: def get[T](self) -> T: return T.__from_py__(self) + def _repr_mimebundle_(self, bundle = Set[str]()): + # p = PyObject_GetAttrString(self.p, '_repr_mimebundle_'.c_str()) + # if p != cobj(): + # return Dict[str, str].__from_py__(self._getattr("_repr_mimebundle_").__call__()) + # else: + p = PyObject_GetAttrString(self.p, '_repr_html_'.c_str()) + if p != cobj(): + return {'text/html': str.__from_py__(self._getattr("_repr_html_").__call__())} + return {'text/plain': self.__repr__()} + def none(): raise NotImplementedError() From 5dcc51a667728ba308ba89ab1083b0a9bea61339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Tue, 4 Jan 2022 19:39:53 -0800 Subject: [PATCH 17/61] Add class auto-deduction support --- codon/parser/ast/stmt.cpp | 2 + codon/parser/ast/stmt.h | 1 + codon/parser/ast/types.cpp | 14 ++-- .../parser/visitors/simplify/simplify_ctx.cpp | 3 +- codon/parser/visitors/simplify/simplify_ctx.h | 3 + .../visitors/simplify/simplify_stmt.cpp | 65 +++++++++++++++++-- .../visitors/typecheck/typecheck_infer.cpp | 20 ++++-- stdlib/internal/attributes.codon | 4 ++ 8 files changed, 95 insertions(+), 17 deletions(-) diff --git a/codon/parser/ast/stmt.cpp b/codon/parser/ast/stmt.cpp index 119825aa..86f0ad64 100644 --- a/codon/parser/ast/stmt.cpp +++ b/codon/parser/ast/stmt.cpp @@ -285,6 +285,8 @@ const std::string Attr::Atomic = "atomic"; const std::string Attr::Property = "property"; const std::string Attr::Internal = "__internal__"; const std::string Attr::ForceRealize = "__force__"; +const std::string Attr::RealizeWithoutSelf = + "std.internal.attributes.realize_without_self"; const std::string Attr::C = "std.internal.attributes.C"; const std::string Attr::CVarArg = ".__vararg__"; const std::string Attr::Method = ".__method__"; diff --git a/codon/parser/ast/stmt.h b/codon/parser/ast/stmt.h index 23fde86b..cae7f850 100644 --- a/codon/parser/ast/stmt.h +++ b/codon/parser/ast/stmt.h @@ -398,6 +398,7 @@ struct Attr { // Internal attributes const static std::string Internal; const static std::string ForceRealize; + const static std::string RealizeWithoutSelf; // Compiler-generated attributes const static std::string C; const static std::string CVarArg; diff --git a/codon/parser/ast/types.cpp b/codon/parser/ast/types.cpp index a08abed4..b3b2134a 100644 --- a/codon/parser/ast/types.cpp +++ b/codon/parser/ast/types.cpp @@ -476,12 +476,18 @@ std::vector FuncType::getUnbounds() const { } bool FuncType::canRealize() const { // Important: return type does not have to be realized. - for (int ai = 1; ai < args.size(); ai++) + + bool force = ast->hasAttr(Attr::RealizeWithoutSelf); + + int ai = 1 + force; + for (; ai < args.size(); ai++) if (!args[ai]->getFunc() && !args[ai]->canRealize()) return false; - return std::all_of(funcGenerics.begin(), funcGenerics.end(), - [](auto &a) { return !a.type || a.type->canRealize(); }) && - (!funcParent || funcParent->canRealize()); + bool generics = std::all_of(funcGenerics.begin(), funcGenerics.end(), + [](auto &a) { return !a.type || a.type->canRealize(); }); + if (!force) + generics &= (!funcParent || funcParent->canRealize()); + return generics; } bool FuncType::isInstantiated() const { TypePtr removed = nullptr; diff --git a/codon/parser/visitors/simplify/simplify_ctx.cpp b/codon/parser/visitors/simplify/simplify_ctx.cpp index 517518db..7587a6c2 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.cpp +++ b/codon/parser/visitors/simplify/simplify_ctx.cpp @@ -24,7 +24,8 @@ SimplifyContext::SimplifyContext(std::string filename, Cache *cache) allowTypeOf(true), substitutions(nullptr) {} SimplifyContext::Base::Base(std::string name, std::shared_ptr ast, int attributes) - : name(move(name)), ast(move(ast)), attributes(attributes) {} + : name(move(name)), ast(move(ast)), attributes(attributes), + deducedMembers(nullptr), selfName() {} std::shared_ptr SimplifyContext::add(SimplifyItem::Kind kind, const std::string &name, diff --git a/codon/parser/visitors/simplify/simplify_ctx.h b/codon/parser/visitors/simplify/simplify_ctx.h index 468ded2a..e525ff24 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.h +++ b/codon/parser/visitors/simplify/simplify_ctx.h @@ -67,6 +67,9 @@ struct SimplifyContext : public Context { /// Tracks function attributes (e.g. if it has @atomic or @test attributes). int attributes; + std::shared_ptr> deducedMembers; + std::string selfName; + explicit Base(std::string name, ExprPtr ast = nullptr, int attributes = 0); bool isType() const { return ast != nullptr; } }; diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 9664e99b..1d0a8823 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -504,6 +504,8 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { if (isClassMember) ctx->bases.push_back(oldBases[0]); ctx->bases.emplace_back(SimplifyContext::Base{canonicalName}); // Add new base... + if (isClassMember && ctx->bases[0].deducedMembers) + ctx->bases.back().deducedMembers = ctx->bases[0].deducedMembers; ctx->addBlock(); // ... and a block! // Set atomic flag if @atomic attribute is present. if (attr.has(Attr::Atomic)) @@ -540,9 +542,13 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { error("non-default argument '{}' after a default argument", varName); defaultsStarted |= bool(a.deflt); + + auto name = ctx->generateCanonicalName(varName); + auto typeAst = a.type; if (!typeAst && isClassMember && ia == 0 && a.name == "self") { typeAst = ctx->bases[ctx->bases.size() - 2].ast; + ctx->bases.back().selfName = name; attr.set(".changedSelf"); } @@ -556,7 +562,6 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { } // First add all generics! - auto name = ctx->generateCanonicalName(varName); args.emplace_back( Param{std::string(stars, '*') + name, typeAst, a.deflt, a.generic}); if (a.generic) { @@ -676,12 +681,15 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { Attr attr = stmt->attributes; std::vector hasMagic(10, 2); hasMagic[Init] = hasMagic[Pickle] = 1; + bool deduce = false; // @tuple(init=, repr=, eq=, order=, hash=, pickle=, container=, python=, add=, // internal=...) // @dataclass(...) // @extend for (auto &d : stmt->decorators) { - if (auto c = d->getCall()) { + if (d->isId("__deduce__")) { + deduce = true; + } else if (auto c = d->getCall()) { if (c->expr->isId(Attr::Tuple)) attr.set(Attr::Tuple); else if (!c->expr->isId("dataclass")) @@ -858,6 +866,45 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { } argSubstitutions.push_back(substitutions.size() - 1); } + + // Auto-detect fields + StmtPtr autoDeducedInit = nullptr; + Stmt *firstInit = nullptr; + if (deduce && args.empty() && !extension) { + // LOG("deducing {}", stmt->name); + for (auto sp : getClassMethods(stmt->suite)) + if (sp && sp->getFunction()) { + firstInit = sp.get(); + auto f = sp->getFunction(); + if (f->name == "__init__" && f->args.size() >= 1 && f->args[0].name == "self") { + ctx->bases.back().deducedMembers = + std::make_shared>(); + transform(sp); + autoDeducedInit = preamble->functions.back(); + std::dynamic_pointer_cast(autoDeducedInit) + ->attributes.set(Attr::RealizeWithoutSelf); + ctx->cache->functions[autoDeducedInit->getFunction()->name] + .ast->attributes.set(Attr::RealizeWithoutSelf); + + int i = 0; + for (auto &m : *(ctx->bases.back().deducedMembers)) { + auto varName = ctx->generateCanonicalName(format("T{}", ++i)); + auto name = ctx->cache->reverseIdentifierLookup[varName]; + ctx->add(SimplifyItem::Type, name, varName, true); + genAst.push_back(N(varName)); + args.emplace_back(Param{varName, N("type"), nullptr, true}); + argSubstitutions.push_back(substitutions.size() - 1); + + ctx->cache->classes[canonicalName].fields.push_back({m, nullptr}); + args.emplace_back(Param{m, N(varName), nullptr}); + argSubstitutions.push_back(substitutions.size() - 1); + // LOG("deduction: {}: {} <-> {}", stmt->name, m, name); + } + ctx->bases.back().deducedMembers = nullptr; + break; + } + } + } if (!genAst.empty()) ctx->bases.back().ast = std::make_shared(N(name), N(genAst)); @@ -919,7 +966,7 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { magics = {"len", "hash"}; else magics = {"new", "raw"}; - if (hasMagic[Init]) + if (hasMagic[Init] && !firstInit) magics.emplace_back(isRecord ? "new" : "init"); if (hasMagic[Eq]) for (auto &i : {"eq", "ne"}) @@ -953,7 +1000,6 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { } } for (int ai = 0; ai < baseASTs.size(); ai++) { - // FUNCS for (auto &mm : ctx->cache->classes[baseASTs[ai]->name].methods) for (auto &mf : ctx->cache->overloads[mm.second]) { auto f = ctx->cache->functions[mf.name].ast; @@ -993,6 +1039,8 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { } for (auto sp : getClassMethods(stmt->suite)) if (sp && !sp->getClass()) { + if (firstInit && firstInit == sp.get()) + continue; transform(sp); suite->stmts.push_back(preamble->functions.back()); } @@ -1040,7 +1088,14 @@ StmtPtr SimplifyVisitor::transformAssignment(const ExprPtr &lhs, const ExprPtr & clone(ei->index), rhs->clone()))); } else if (auto ed = lhs->getDot()) { seqassert(!type, "unexpected type annotation"); - return N(transform(ed->expr), ed->member, transform(rhs, false)); + auto l = transform(ed->expr); + if (ctx->bases.back().deducedMembers && l->isId(ctx->bases.back().selfName)) { + if (std::find(ctx->bases.back().deducedMembers->begin(), + ctx->bases.back().deducedMembers->end(), + ed->member) == ctx->bases.back().deducedMembers->end()) + ctx->bases.back().deducedMembers->push_back(ed->member); + } + return N(l, ed->member, transform(rhs, false)); } else if (auto e = lhs->getId()) { ExprPtr t = transformType(type, false); if (!shadow && !t) { diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index eef9924b..8e8e27fc 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -156,8 +156,11 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { ctx->typecheckLevel++; // Find parents! - ctx->bases.push_back({type->ast->name, type->getFunc(), type->args[0], - {}, findSuperMethods(type->getFunc())}); + ctx->bases.push_back({type->ast->name, + type->getFunc(), + type->args[0], + {}, + findSuperMethods(type->getFunc())}); // if (startswith(type->ast->name, "Foo")) { // LOG(": {}", type->toString()); // for (auto &s: ctx->bases.back().supers) @@ -174,13 +177,16 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { if (!isInternal) for (int i = 0, j = 1; i < ast->args.size(); i++) if (!ast->args[i].generic) { - seqassert(type->args[j] && type->args[j]->getUnbounds().empty(), - "unbound argument {}", type->args[j]->toString()); std::string varName = ast->args[i].name; trimStars(varName); ctx->add(TypecheckItem::Var, varName, - std::make_shared( - type->args[j++]->generalize(ctx->typecheckLevel))); + std::make_shared(type->args[j++])); + // N.B. this used to be: + // seqassert(type->args[j] && type->args[j]->getUnbounds().empty(), + // "unbound argument {}", type->args[j]->toString()); + // type->args[j++]->generalize(ctx->typecheckLevel) + // no idea why... most likely an old artefact, BUT if seq or sequre + // fail with weird type errors try returning this and see if it works } // Need to populate realization table in advance to make recursive functions @@ -220,7 +226,7 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { // Realize the return type. if (auto t = realize(type->args[0])) unify(type->args[0], t); - LOG_REALIZE("done with {} / {}", type->realizedName(), oldKey); + LOG_REALIZE("... done with {} / {}", type->realizedName(), oldKey); // Create and store IR node and a realized AST to be used // during the code generation. diff --git a/stdlib/internal/attributes.codon b/stdlib/internal/attributes.codon index 464424da..bab803b8 100644 --- a/stdlib/internal/attributes.codon +++ b/stdlib/internal/attributes.codon @@ -38,3 +38,7 @@ def distributive(): def C(): pass +@__attribute__ +def realize_without_self(): + pass + From d238ee4656324d0bc74193638138ccf8e6c398ad Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Fri, 7 Jan 2022 11:06:09 -0500 Subject: [PATCH 18/61] clang-format --- codon/parser/ast/expr.cpp | 5 ++--- codon/parser/visitors/simplify/simplify_expr.cpp | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/codon/parser/ast/expr.cpp b/codon/parser/ast/expr.cpp index ec5768a3..0f3e3dd2 100644 --- a/codon/parser/ast/expr.cpp +++ b/codon/parser/ast/expr.cpp @@ -46,9 +46,8 @@ std::string StaticValue::toString() const { return ""; if (!evaluated) return type == StaticValue::STRING ? "str" : "int"; - return type == StaticValue::STRING - ? "'" + escape(std::get(value)) + "'" - : std::to_string(std::get(value)); + return type == StaticValue::STRING ? "'" + escape(std::get(value)) + "'" + : std::to_string(std::get(value)); } int64_t StaticValue::getInt() const { seqassert(type == StaticValue::INT, "not an int"); diff --git a/codon/parser/visitors/simplify/simplify_expr.cpp b/codon/parser/visitors/simplify/simplify_expr.cpp index 053e384b..d850ee7e 100644 --- a/codon/parser/visitors/simplify/simplify_expr.cpp +++ b/codon/parser/visitors/simplify/simplify_expr.cpp @@ -716,8 +716,8 @@ ExprPtr SimplifyVisitor::transformInt(const std::string &value, } /// Custom suffix sfx: use int.__suffix_sfx__(str) call. /// NOTE: you cannot neither use binary (0bXXX) format here. - return transform(N(N("int", format("__suffix_{}__", suffix)), - N(val))); + return transform( + N(N("int", format("__suffix_{}__", suffix)), N(val))); } ExprPtr SimplifyVisitor::transformFloat(const std::string &value, From f35656e59c853d0d4036c89e27fe500fcaa71ec2 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Fri, 7 Jan 2022 12:52:17 -0500 Subject: [PATCH 19/61] Fix LLVM JIT issues --- codon/compiler/compiler.cpp | 22 +++++++++++++++++++--- codon/compiler/compiler.h | 14 +++++++++++++- codon/compiler/jit.cpp | 3 +-- codon/sir/llvm/llvisitor.cpp | 20 ++++++++++++-------- codon/sir/llvm/llvisitor.h | 2 +- codon/sir/transform/manager.cpp | 29 ++++++++++++++++++++++------- codon/sir/transform/manager.h | 14 +++----------- 7 files changed, 71 insertions(+), 33 deletions(-) diff --git a/codon/compiler/compiler.cpp b/codon/compiler/compiler.cpp index 55e8153f..505c298b 100644 --- a/codon/compiler/compiler.cpp +++ b/codon/compiler/compiler.cpp @@ -11,13 +11,29 @@ #include "codon/parser/visitors/typecheck/typecheck.h" namespace codon { +namespace { +ir::transform::PassManager::Init getPassManagerInit(Compiler::Mode mode, bool isTest) { + using ir::transform::PassManager; + switch (mode) { + case Compiler::Mode::DEBUG: + return isTest ? PassManager::Init::RELEASE : PassManager::Init::DEBUG; + case Compiler::Mode::RELEASE: + return PassManager::Init::RELEASE; + case Compiler::Mode::JIT: + return PassManager::Init::JIT; + default: + return PassManager::Init::EMPTY; + } +} +} // namespace -Compiler::Compiler(const std::string &argv0, bool debug, +Compiler::Compiler(const std::string &argv0, Compiler::Mode mode, const std::vector &disabledPasses, bool isTest) - : argv0(argv0), debug(debug), input(), plm(std::make_unique()), + : argv0(argv0), debug(mode == Mode::DEBUG), input(), + plm(std::make_unique()), cache(std::make_unique(argv0)), module(std::make_unique()), - pm(std::make_unique(debug && !isTest, + pm(std::make_unique(getPassManagerInit(mode, isTest), disabledPasses)), llvisitor(std::make_unique()) { cache->module = module.get(); diff --git a/codon/compiler/compiler.h b/codon/compiler/compiler.h index 35a30d69..8d0a8001 100644 --- a/codon/compiler/compiler.h +++ b/codon/compiler/compiler.h @@ -15,6 +15,13 @@ namespace codon { class Compiler { +public: + enum Mode { + DEBUG, + RELEASE, + JIT, + }; + private: std::string argv0; bool debug; @@ -30,9 +37,14 @@ private: const std::unordered_map &defines); public: - Compiler(const std::string &argv0, bool debug = false, + Compiler(const std::string &argv0, Mode mode, const std::vector &disabledPasses = {}, bool isTest = false); + explicit Compiler(const std::string &argv0, bool debug = false, + const std::vector &disabledPasses = {}, + bool isTest = false) + : Compiler(argv0, debug ? Mode::DEBUG : Mode::RELEASE, disabledPasses, isTest) {} + std::string getInput() const { return input; } PluginManager *getPluginManager() const { return plm.get(); } ast::Cache *getCache() const { return cache.get(); } diff --git a/codon/compiler/jit.cpp b/codon/compiler/jit.cpp index 6e082d8e..38f197b1 100644 --- a/codon/compiler/jit.cpp +++ b/codon/compiler/jit.cpp @@ -18,7 +18,7 @@ const std::string JIT_FILENAME = ""; } // namespace JIT::JIT(const std::string &argv0, const std::string &mode) - : compiler(std::make_unique(argv0, /*debug=*/true)), mode(mode) { + : compiler(std::make_unique(argv0, Compiler::Mode::JIT)), mode(mode) { if (auto e = Engine::create()) { engine = std::move(e.get()); } else { @@ -108,7 +108,6 @@ llvm::Expected JIT::exec(const std::string &code) { auto sctx = cache->imports[MAIN_IMPORT].ctx; auto preamble = std::make_shared(); - ast::Cache bCache = *cache; ast::SimplifyContext bSimplify = *sctx; ast::TypeContext bType = *(cache->typeCtx); diff --git a/codon/sir/llvm/llvisitor.cpp b/codon/sir/llvm/llvisitor.cpp index cd0cf2ee..1b877e9b 100644 --- a/codon/sir/llvm/llvisitor.cpp +++ b/codon/sir/llvm/llvisitor.cpp @@ -88,8 +88,7 @@ void LLVMVisitor::registerGlobal(const Var *var) { return; if (auto *f = cast(var)) { - makeLLVMFunction(f); - insertFunc(f, func); + insertFunc(f, makeLLVMFunction(f)); } else { llvm::Type *llvmType = getLLVMType(var->getType()); if (llvmType->isVoidTy()) { @@ -145,6 +144,7 @@ llvm::Value *LLVMVisitor::getVar(const Var *var) { } } else { registerGlobal(var); + it = vars.find(var->getId()); return it->second; } } @@ -177,6 +177,7 @@ llvm::Function *LLVMVisitor::getFunc(const Func *func) { } } else { registerGlobal(func); + it = funcs.find(func->getId()); return it->second; } } @@ -575,8 +576,7 @@ void LLVMVisitor::visit(const Module *x) { } const Func *main = x->getMainFunc(); - makeLLVMFunction(main); - llvm::FunctionCallee realMain = func; + llvm::FunctionCallee realMain = makeLLVMFunction(main); process(main); setDebugInfoForNode(nullptr); @@ -712,12 +712,15 @@ llvm::DISubprogram *LLVMVisitor::getDISubprogramForFunc(const Func *x) { return subprogram; } -void LLVMVisitor::makeLLVMFunction(const Func *x) { +llvm::Function *LLVMVisitor::makeLLVMFunction(const Func *x) { // process LLVM functions in full immediately if (auto *llvmFunc = cast(x)) { + auto *oldFunc = func; process(llvmFunc); setDebugInfoForNode(nullptr); - return; + auto *newFunc = func; + func = oldFunc; + return newFunc; } auto *funcType = cast(x->getType()); @@ -730,11 +733,12 @@ void LLVMVisitor::makeLLVMFunction(const Func *x) { auto *llvmFuncType = llvm::FunctionType::get(returnType, argTypes, funcType->isVariadic()); const std::string functionName = getNameForFunction(x); - func = llvm::cast( + auto *f = llvm::cast( M->getOrInsertFunction(functionName, llvmFuncType).getCallee()); if (!cast(x)) { - func->setSubprogram(getDISubprogramForFunc(x)); + f->setSubprogram(getDISubprogramForFunc(x)); } + return f; } void LLVMVisitor::makeYield(llvm::Value *value, bool finalYield) { diff --git a/codon/sir/llvm/llvisitor.h b/codon/sir/llvm/llvisitor.h index 0191daa2..2a29e7c4 100644 --- a/codon/sir/llvm/llvisitor.h +++ b/codon/sir/llvm/llvisitor.h @@ -167,7 +167,7 @@ private: // General function helpers llvm::Value *call(llvm::FunctionCallee callee, llvm::ArrayRef args); - void makeLLVMFunction(const Func *); + llvm::Function *makeLLVMFunction(const Func *); void makeYield(llvm::Value *value = nullptr, bool finalYield = false); std::string buildLLVMCodeString(const LLVMFunc *); void callStage(const PipelineFlow::Stage *stage); diff --git a/codon/sir/transform/manager.cpp b/codon/sir/transform/manager.cpp index 8e5d12bb..a3fd51a4 100644 --- a/codon/sir/transform/manager.cpp +++ b/codon/sir/transform/manager.cpp @@ -139,12 +139,18 @@ void PassManager::invalidate(const std::string &key) { } } -void PassManager::registerStandardPasses(bool debug) { - if (debug) { +void PassManager::registerStandardPasses(PassManager::Init init) { + switch (init) { + case Init::EMPTY: + break; + case Init::DEBUG: { registerPass(std::make_unique()); registerPass(std::make_unique()); registerPass(std::make_unique()); - } else { + break; + } + case Init::RELEASE: + case Init::JIT: { // Pythonic registerPass(std::make_unique()); registerPass(std::make_unique()); @@ -174,10 +180,19 @@ void PassManager::registerStandardPasses(bool debug) { // parallel registerPass(std::make_unique()); - registerPass(std::make_unique(seKey2, rdKey, globalKey, - /*runGlobalDemoton=*/true), - /*insertBefore=*/"", {seKey2, rdKey, globalKey}, - {seKey2, rdKey, cfgKey, globalKey}); + if (init != Init::JIT) { + // Don't demote globals in JIT mode, since they might be used later + // by another user input. + registerPass( + std::make_unique(seKey2, rdKey, globalKey, + /*runGlobalDemoton=*/true), + /*insertBefore=*/"", {seKey2, rdKey, globalKey}, + {seKey2, rdKey, cfgKey, globalKey}); + } + break; + } + default: + seqassert(false, "unknown PassManager init value"); } } diff --git a/codon/sir/transform/manager.h b/codon/sir/transform/manager.h index 4a4f5317..5a231794 100644 --- a/codon/sir/transform/manager.h +++ b/codon/sir/transform/manager.h @@ -95,6 +95,7 @@ public: EMPTY, DEBUG, RELEASE, + JIT, }; static const int PASS_IT_MAX; @@ -102,16 +103,7 @@ public: explicit PassManager(Init init, std::vector disabled = {}) : km(), passes(), analyses(), executionOrder(), results(), disabled(std::move(disabled)) { - switch (init) { - case Init::EMPTY: - break; - case Init::DEBUG: - registerStandardPasses(true); - break; - case Init::RELEASE: - registerStandardPasses(false); - break; - } + registerStandardPasses(init); } explicit PassManager(bool debug = false, std::vector disabled = {}) @@ -156,7 +148,7 @@ public: private: void runPass(Module *module, const std::string &name); - void registerStandardPasses(bool debug = false); + void registerStandardPasses(Init init); void runAnalysis(Module *module, const std::string &name); void invalidate(const std::string &key); }; From cb0a6ea443362137586d5d3c88cf0bcd9461bc65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 7 Jan 2022 17:27:51 -0800 Subject: [PATCH 20/61] Rename function overload 'super' to 'superf' --- codon/parser/visitors/simplify/simplify.cpp | 3 ++- .../visitors/typecheck/typecheck_expr.cpp | 6 +++--- test/parser/typecheck_stmt.codon | 20 +++++++++---------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/codon/parser/visitors/simplify/simplify.cpp b/codon/parser/visitors/simplify/simplify.cpp index 0b1343c9..4dca7099 100644 --- a/codon/parser/visitors/simplify/simplify.cpp +++ b/codon/parser/visitors/simplify/simplify.cpp @@ -88,9 +88,10 @@ SimplifyVisitor::apply(Cache *cache, const StmtPtr &node, const std::string &fil } // Reserve the following static identifiers. for (auto name : {"staticlen", "compile_error", "isinstance", "hasattr", "type", - "TypeVar", "Callable", "argv", "super"}) + "TypeVar", "Callable", "argv", "super", "superf", "fn"}) stdlib->generateCanonicalName(name); stdlib->add(SimplifyItem::Var, "super", "super", true); + stdlib->add(SimplifyItem::Var, "superf", "superf", true); // This code must be placed in a preamble (these are not POD types but are // referenced by the various preamble Function.N and Tuple.N stubs) diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 3007302c..bc547469 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1038,15 +1038,15 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in seenNames.insert(i.name); } - if (expr->expr->isId("super")) { + if (expr->expr->isId("superf")) { if (ctx->bases.back().supers.empty()) - error("no matching super methods are available"); + error("no matching superf methods are available"); auto parentCls = ctx->bases.back().type->getFunc()->funcParent; auto m = findMatchingMethods(parentCls ? CAST(parentCls, types::ClassType) : nullptr, ctx->bases.back().supers, expr->args); if (m.empty()) - error("no matching super methods are available"); + error("no matching superf methods are available"); // LOG("found {} <- {}", ctx->bases.back().type->getFunc()->toString(), // m[0]->toString()); ExprPtr e = N(N(m[0]->ast->name), expr->args); diff --git a/test/parser/typecheck_stmt.codon b/test/parser/typecheck_stmt.codon index a3099c5c..1baa0fcd 100644 --- a/test/parser/typecheck_stmt.codon +++ b/test/parser/typecheck_stmt.codon @@ -299,19 +299,19 @@ def foo2(x): foo2(1) #: 2 foo2('s') #: 1 -#%% super,barebones +#%% superf,barebones class Foo: def foo(a): - # super(a) + # superf(a) print 'foo-1', a def foo(a: int): - super(a) + superf(a) print 'foo-2', a def foo(a: str): - super(a) + superf(a) print 'foo-3', a def foo(a): - super(a) + superf(a) print 'foo-4', a Foo.foo(1) #: foo-1 1 @@ -324,21 +324,21 @@ class Bear: @extend class Bear: def woof(x): - return super(x) + f' bear w--f {x}' + return superf(x) + f' bear w--f {x}' print Bear.woof('!') #: bear woof ! bear w--f ! class PolarBear(Bear): def woof(): - return 'polar ' + super('@') + return 'polar ' + superf('@') print PolarBear.woof() #: polar bear woof @ bear w--f @ -#%% super_error,barebones +#%% superf_error,barebones class Foo: def foo(a): - super(a) + superf(a) print 'foo-1', a Foo.foo(1) -#! no matching super methods are available +#! no matching superf methods are available #! while realizing Foo.foo:0 From 5672cebe1c70527b6b2a7d22ff9def5514fe77f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 7 Jan 2022 18:26:14 -0800 Subject: [PATCH 21/61] Add support for super() --- codon/parser/cache.h | 2 + .../visitors/simplify/simplify_stmt.cpp | 5 ++ codon/parser/visitors/typecheck/typecheck.h | 2 + .../visitors/typecheck/typecheck_expr.cpp | 48 +++++++++++++++++++ stdlib/internal/internal.codon | 6 +++ 5 files changed, 63 insertions(+) diff --git a/codon/parser/cache.h b/codon/parser/cache.h index 7adcb4c8..bef3629d 100644 --- a/codon/parser/cache.h +++ b/codon/parser/cache.h @@ -134,6 +134,8 @@ struct Cache : public std::enable_shared_from_this { /// ClassRealization instance. std::unordered_map> realizations; + std::vector> parentClasses; + Class() : ast(nullptr), originalAst(nullptr) {} }; /// Class lookup table that maps a canonical class identifier to the corresponding diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 9664e99b..b7cb13ec 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -788,6 +788,7 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { std::vector> substitutions; std::vector argSubstitutions; std::unordered_set seenMembers; + std::vector baseASTsFields; for (auto &baseClass : stmt->baseClasses) { std::string bcName; std::vector subs; @@ -828,6 +829,7 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { if (!extension) ctx->cache->classes[canonicalName].fields.push_back({a.name, nullptr}); } + baseASTsFields.push_back(args.size()); } // Add generics, if any, to the context. @@ -909,6 +911,9 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { ctx->moduleName.module); ctx->cache->classes[canonicalName].ast = N(canonicalName, args, N(), attr); + for (int i = 0; i < baseASTs.size(); i++) + ctx->cache->classes[canonicalName].parentClasses.push_back( + {baseASTs[i]->name, baseASTsFields[i]}); std::vector fns; ExprPtr codeType = ctx->bases.back().ast->clone(); std::vector magics{}; diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index 92777c51..a8bfca92 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -301,6 +301,8 @@ private: const std::vector &methods, const std::vector &args); + ExprPtr transformSuper(const CallExpr *expr); + private: types::TypePtr unify(types::TypePtr &a, const types::TypePtr &b, bool undoOnSuccess = false); diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index bc547469..0213aaa9 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1052,6 +1052,8 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in ExprPtr e = N(N(m[0]->ast->name), expr->args); return transform(e, false, true); } + if (expr->expr->isId("super")) + return transformSuper(expr); bool isPartial = !expr->args.empty() && expr->args.back().value->getEllipsis() && !expr->args.back().value->getEllipsis()->isPipeArg && @@ -1946,5 +1948,51 @@ types::FuncTypePtr TypecheckVisitor::findDispatch(const std::string &fn) { return typ; } +ExprPtr TypecheckVisitor::transformSuper(const CallExpr *expr) { + // For now, we just support casting to the _FIRST_ overload (i.e. empty super()) + if (!expr->args.empty()) + error("super does not take arguments"); + + if (ctx->bases.empty()) + error("no parent classes available"); + auto fptyp = ctx->bases.back().type->getFunc(); + if (!fptyp || fptyp->ast->hasAttr(Attr::Method)) + error("no parent classes available"); + ClassTypePtr typ = fptyp->args[1]->getClass(); + auto &cands = ctx->cache->classes[typ->name].parentClasses; + if (cands.empty()) + error("no parent classes available"); + if (typ->getRecord()) + error("cannot use super on tuple types"); + + // find parent typ + // unify top N args with parent typ args + // realize & do bitcast + // call bitcast() . method + + auto name = cands[0].first; + int fields = cands[0].second; + auto val = ctx->find(name); + seqassert(val, "cannot find '{}'", name); + auto ftyp = ctx->instantiate(expr, val->type)->getClass(); + + for (int i = 0; i < fields; i++) { + auto t = ctx->cache->classes[typ->name].fields[i].type; + t = ctx->instantiate(expr, t, typ.get()); + + auto ft = ctx->cache->classes[name].fields[i].type; + ft = ctx->instantiate(expr, ft, ftyp.get()); + unify(t, ft); + } + + ExprPtr typExpr = N(name); + typExpr->setType(ftyp); + auto self = fptyp->ast->args[0].name; + ExprPtr e = transform( + N(N(N("__internal__"), "to_class_ptr"), + N(N(N(self), "__raw__")), typExpr)); + return e; +} + } // namespace ast } // namespace codon diff --git a/stdlib/internal/internal.codon b/stdlib/internal/internal.codon index f07c7e50..ba66d154 100644 --- a/stdlib/internal/internal.codon +++ b/stdlib/internal/internal.codon @@ -125,6 +125,12 @@ class __internal__: def opt_ref_invert[T](what: Optional[T]) -> T: ret i8* %what + @pure + @llvm + def to_class_ptr[T](ptr: Ptr[byte]) -> T: + %0 = bitcast i8* %ptr to {=T} + ret {=T} %0 + def raw_type_str(p: Ptr[byte], name: str) -> str: pstr = p.__repr__() # '<[name] at [pstr]>' From 3920a16cdd140c41273ed99c7e98ab217c3dfba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 7 Jan 2022 18:33:51 -0800 Subject: [PATCH 22/61] Add tests for super() --- .../visitors/typecheck/typecheck_expr.cpp | 2 +- test/parser/typecheck_expr.codon | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 0213aaa9..3ea805c2 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1953,7 +1953,7 @@ ExprPtr TypecheckVisitor::transformSuper(const CallExpr *expr) { if (!expr->args.empty()) error("super does not take arguments"); - if (ctx->bases.empty()) + if (ctx->bases.empty() || !ctx->bases.back().type) error("no parent classes available"); auto fptyp = ctx->bases.back().type->getFunc(); if (!fptyp || fptyp->ast->hasAttr(Attr::Method)) diff --git a/test/parser/typecheck_expr.codon b/test/parser/typecheck_expr.codon index ce02e25b..d2c99d1a 100644 --- a/test/parser/typecheck_expr.codon +++ b/test/parser/typecheck_expr.codon @@ -679,3 +679,33 @@ def foo(x: Callable[[1,2], 3]): pass #! unexpected static type #%% static_unify_2,barebones def foo(x: List[1]): pass #! cannot unify T and 1 + +#%% super,barebones +class A[T]: + a: T + def __init__(self, t: T): + self.a = t + def foo(self): + return f'A:{self.a}' +class B(A[str]): + b: int + def __init__(self): + super().__init__('s') + self.b = 6 + def baz(self): + return f'{super().foo()}::{self.b}' +b = B() +print b.foo() #: A:s +print b.baz() #: A:s::6 + + +#%% super_error,barebones +class A: + def __init__(self): + super().__init__() +a = A() +#! no parent classes available +#! while realizing A.__init__:1 (arguments A.__init__:1[A]) + +#%% super_error_2,barebones +super().foo(1) #! no parent classes available From 65bc56eb214039a820af6a21ff8ff261eeb1b75f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Sun, 9 Jan 2022 10:15:02 -0800 Subject: [PATCH 23/61] Add tuple_offsetof --- stdlib/internal/internal.codon | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/stdlib/internal/internal.codon b/stdlib/internal/internal.codon index ba66d154..dfcc1de2 100644 --- a/stdlib/internal/internal.codon +++ b/stdlib/internal/internal.codon @@ -131,6 +131,17 @@ class __internal__: %0 = bitcast i8* %ptr to {=T} ret {=T} %0 + def _tuple_offsetof(x, field: Static[int]): + @llvm + def _llvm_offsetof(T: type, idx: Static[int], TE: type) -> int: + %a = alloca {=T} + %b = getelementptr inbounds {=T}, {=T}* %a, i64 0, i32 {=idx} + %base = ptrtoint {=T}* %a to i64 + %elem = ptrtoint {=TE}* %b to i64 + %offset = sub i64 %elem, %base + ret i64 %offset + return _llvm_offsetof(type(x), field, type(x[field])) + def raw_type_str(p: Ptr[byte], name: str) -> str: pstr = p.__repr__() # '<[name] at [pstr]>' From 9dde5be88fa2b6863e7830ceff6d9c617aade3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Tue, 11 Jan 2022 11:04:03 -0800 Subject: [PATCH 24/61] Add tuple support for super() --- codon/parser/cache.h | 3 +- codon/parser/visitors/simplify/simplify.cpp | 2 +- .../visitors/typecheck/typecheck_expr.cpp | 46 ++++++++++++------- stdlib/internal/internal.codon | 1 + 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/codon/parser/cache.h b/codon/parser/cache.h index bef3629d..63f7161f 100644 --- a/codon/parser/cache.h +++ b/codon/parser/cache.h @@ -133,7 +133,8 @@ struct Cache : public std::enable_shared_from_this { /// Realization lookup table that maps a realized class name to the corresponding /// ClassRealization instance. std::unordered_map> realizations; - + /// List of inherited class. We also keep the number of fields each of inherited + /// class. std::vector> parentClasses; Class() : ast(nullptr), originalAst(nullptr) {} diff --git a/codon/parser/visitors/simplify/simplify.cpp b/codon/parser/visitors/simplify/simplify.cpp index 4dca7099..6c149a94 100644 --- a/codon/parser/visitors/simplify/simplify.cpp +++ b/codon/parser/visitors/simplify/simplify.cpp @@ -88,7 +88,7 @@ SimplifyVisitor::apply(Cache *cache, const StmtPtr &node, const std::string &fil } // Reserve the following static identifiers. for (auto name : {"staticlen", "compile_error", "isinstance", "hasattr", "type", - "TypeVar", "Callable", "argv", "super", "superf", "fn"}) + "TypeVar", "Callable", "argv", "super", "superf"}) stdlib->generateCanonicalName(name); stdlib->add(SimplifyItem::Var, "super", "super", true); stdlib->add(SimplifyItem::Var, "superf", "superf", true); diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 3ea805c2..168431de 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1958,12 +1958,14 @@ ExprPtr TypecheckVisitor::transformSuper(const CallExpr *expr) { auto fptyp = ctx->bases.back().type->getFunc(); if (!fptyp || fptyp->ast->hasAttr(Attr::Method)) error("no parent classes available"); + if (fptyp->args.size() < 2) + error("no parent classes available"); ClassTypePtr typ = fptyp->args[1]->getClass(); auto &cands = ctx->cache->classes[typ->name].parentClasses; if (cands.empty()) error("no parent classes available"); - if (typ->getRecord()) - error("cannot use super on tuple types"); + // if (typ->getRecord()) + // error("cannot use super on tuple types"); // find parent typ // unify top N args with parent typ args @@ -1976,22 +1978,34 @@ ExprPtr TypecheckVisitor::transformSuper(const CallExpr *expr) { seqassert(val, "cannot find '{}'", name); auto ftyp = ctx->instantiate(expr, val->type)->getClass(); - for (int i = 0; i < fields; i++) { - auto t = ctx->cache->classes[typ->name].fields[i].type; - t = ctx->instantiate(expr, t, typ.get()); + if (typ->getRecord()) { + std::vector members; + for (int i = 0; i < fields; i++) + members.push_back(N(N(fptyp->ast->args[0].name), + ctx->cache->classes[typ->name].fields[i].name)); + ExprPtr e = transform( + N(N(format(TYPE_TUPLE "{}", members.size())), members)); + unify(e->type, ftyp); + e->type = ftyp; + return e; + } else { + for (int i = 0; i < fields; i++) { + auto t = ctx->cache->classes[typ->name].fields[i].type; + t = ctx->instantiate(expr, t, typ.get()); - auto ft = ctx->cache->classes[name].fields[i].type; - ft = ctx->instantiate(expr, ft, ftyp.get()); - unify(t, ft); + auto ft = ctx->cache->classes[name].fields[i].type; + ft = ctx->instantiate(expr, ft, ftyp.get()); + unify(t, ft); + } + + ExprPtr typExpr = N(name); + typExpr->setType(ftyp); + auto self = fptyp->ast->args[0].name; + ExprPtr e = transform( + N(N(N("__internal__"), "to_class_ptr"), + N(N(N(self), "__raw__")), typExpr)); + return e; } - - ExprPtr typExpr = N(name); - typExpr->setType(ftyp); - auto self = fptyp->ast->args[0].name; - ExprPtr e = transform( - N(N(N("__internal__"), "to_class_ptr"), - N(N(N(self), "__raw__")), typExpr)); - return e; } } // namespace ast diff --git a/stdlib/internal/internal.codon b/stdlib/internal/internal.codon index dfcc1de2..cfe19211 100644 --- a/stdlib/internal/internal.codon +++ b/stdlib/internal/internal.codon @@ -131,6 +131,7 @@ class __internal__: %0 = bitcast i8* %ptr to {=T} ret {=T} %0 + @pure def _tuple_offsetof(x, field: Static[int]): @llvm def _llvm_offsetof(T: type, idx: Static[int], TE: type) -> int: From 47bea7017bb4029c174c3d4071adace520eb5f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Tue, 11 Jan 2022 11:49:12 -0800 Subject: [PATCH 25/61] Add isinstance support for inherited classes; Fix review issues --- .../visitors/simplify/simplify_stmt.cpp | 1 + codon/parser/visitors/typecheck/typecheck.h | 2 + .../visitors/typecheck/typecheck_expr.cpp | 39 +++++++++- test/parser/typecheck_expr.codon | 74 +++++++++++++++++++ 4 files changed, 113 insertions(+), 3 deletions(-) diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index b7cb13ec..178fc12a 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -544,6 +544,7 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { if (!typeAst && isClassMember && ia == 0 && a.name == "self") { typeAst = ctx->bases[ctx->bases.size() - 2].ast; attr.set(".changedSelf"); + attr.set(Attr::Method); } if (attr.has(Attr::C)) { diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index a8bfca92..91e0aa74 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -302,6 +302,8 @@ private: const std::vector &args); ExprPtr transformSuper(const CallExpr *expr); + std::vector getSuperTypes(const types::ClassTypePtr &cls); + private: types::TypePtr unify(types::TypePtr &a, const types::TypePtr &b, diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 168431de..811f28a6 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1423,8 +1423,15 @@ std::pair TypecheckVisitor::transformSpecialCall(CallExpr *expr) expr->args[1].value = transformType(expr->args[1].value, /*disableActivation*/ true); auto t = expr->args[1].value->type; - auto unifyOK = typ->unify(t.get(), nullptr) >= 0; - return {true, transform(N(unifyOK))}; + auto hierarchy = getSuperTypes(typ->getClass()); + + for (auto &tx: hierarchy) { + auto unifyOK = tx->unify(t.get(), nullptr) >= 0; + if (unifyOK) { + return {true, transform(N(true))}; + } + } + return {true, transform(N(false))}; } } } else if (val == "staticlen") { @@ -1956,7 +1963,7 @@ ExprPtr TypecheckVisitor::transformSuper(const CallExpr *expr) { if (ctx->bases.empty() || !ctx->bases.back().type) error("no parent classes available"); auto fptyp = ctx->bases.back().type->getFunc(); - if (!fptyp || fptyp->ast->hasAttr(Attr::Method)) + if (!fptyp || !fptyp->ast->hasAttr(Attr::Method)) error("no parent classes available"); if (fptyp->args.size() < 2) error("no parent classes available"); @@ -2008,5 +2015,31 @@ ExprPtr TypecheckVisitor::transformSuper(const CallExpr *expr) { } } +std::vector TypecheckVisitor::getSuperTypes(const ClassTypePtr &cls) { + std::vector result; + if (!cls) + return result; + result.push_back(cls); + int start = 0; + for (auto &cand: ctx->cache->classes[cls->name].parentClasses) { + auto name = cand.first; + int fields = cand.second; + auto val = ctx->find(name); + seqassert(val, "cannot find '{}'", name); + auto ftyp = ctx->instantiate(nullptr, val->type)->getClass(); + for (int i = start; i < fields; i++) { + auto t = ctx->cache->classes[cls->name].fields[i].type; + t = ctx->instantiate(nullptr, t, cls.get()); + auto ft = ctx->cache->classes[name].fields[i].type; + ft = ctx->instantiate(nullptr, ft, ftyp.get()); + unify(t, ft); + } + start += fields; + for (auto &t: getSuperTypes(ftyp)) + result.push_back(t); + } + return result; +} + } // namespace ast } // namespace codon diff --git a/test/parser/typecheck_expr.codon b/test/parser/typecheck_expr.codon index d2c99d1a..f28f1b87 100644 --- a/test/parser/typecheck_expr.codon +++ b/test/parser/typecheck_expr.codon @@ -600,6 +600,30 @@ print hasattr(int, "__getitem__") print hasattr([1, 2], "__getitem__", str) #: False +#%% isinstance_inheritance,barebones +class AX[T]: + a: T + def __init__(self, a: T): + self.a = a +class Side: + def __init__(self): + pass +class BX[T,U](AX[T], Side): + b: U + def __init__(self, a: T, b: U): + super().__init__(a) + self.b = b +class CX[T,U](BX[T,U]): + c: int + def __init__(self, a: T, b: U): + super().__init__(a, b) + self.c = 1 +c = CX('a', False) +print isinstance(c, CX), isinstance(c, BX), isinstance(c, AX), isinstance(c, Side) +#: True True True True +print isinstance(c, BX[str, bool]), isinstance(c, BX[str, str]), isinstance(c, AX[int]) +#: True False False + #%% staticlen_err,barebones print staticlen([1, 2]) #! List[int] is not a tuple type @@ -698,6 +722,56 @@ b = B() print b.foo() #: A:s print b.baz() #: A:s::6 +class AX[T]: + a: T + def __init__(self, a: T): + self.a = a + def foo(self): + return f'[AX:{self.a}]' +class BX[T,U](AX[T]): + b: U + def __init__(self, a: T, b: U): + print super().__class__ + super().__init__(a) + self.b = b + def foo(self): + return f'[BX:{super().foo()}:{self.b}]' +class CX[T,U](BX[T,U]): + c: int + def __init__(self, a: T, b: U): + print super().__class__ + super().__init__(a, b) + self.c = 1 + def foo(self): + return f'CX:{super().foo()}:{self.c}' +c = CX('a', False) +print c.__class__, c.foo() +#: BX[str,bool] +#: AX[str] +#: CX[str,bool] CX:[BX:[AX:a]:False]:1 + + +#%% super_tuple,barebones +@tuple +class A[T]: + a: T + x: int + def __new__(a: T) -> A[T]: + return (a, 1) + def foo(self): + return f'A:{self.a}' +@tuple +class B(A[str]): + b: int + def __new__() -> B: + return (*(A('s')), 6) + def baz(self): + return f'{super().foo()}::{self.b}' + +b = B() +print b.foo() #: A:s +print b.baz() #: A:s::6 + #%% super_error,barebones class A: From 6ee84e09b95f1313e5fe8cee1feb41e4125f8d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Sat, 15 Jan 2022 11:39:24 -0800 Subject: [PATCH 26/61] Fix tests --- codon/parser/visitors/simplify/simplify_stmt.cpp | 6 +++--- codon/parser/visitors/typecheck/typecheck_infer.cpp | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 823301e8..0af91181 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -506,7 +506,7 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { ctx->bases.emplace_back(SimplifyContext::Base{canonicalName}); // Add new base... if (isClassMember && ctx->bases[0].deducedMembers) ctx->bases.back().deducedMembers = ctx->bases[0].deducedMembers; - ctx->addBlock(); // ... and a block! + ctx->addBlock(); // ... and a block! // Set atomic flag if @atomic attribute is present. if (attr.has(Attr::Atomic)) ctx->bases.back().attributes |= FLAG_ATOMIC; @@ -542,7 +542,6 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { error("non-default argument '{}' after a default argument", varName); defaultsStarted |= bool(a.deflt); - auto name = ctx->generateCanonicalName(varName); auto typeAst = a.type; @@ -1095,7 +1094,8 @@ StmtPtr SimplifyVisitor::transformAssignment(const ExprPtr &lhs, const ExprPtr & } else if (auto ed = lhs->getDot()) { seqassert(!type, "unexpected type annotation"); auto l = transform(ed->expr); - if (ctx->bases.back().deducedMembers && l->isId(ctx->bases.back().selfName)) { + if (ctx->bases.size() && ctx->bases.back().deducedMembers && + l->isId(ctx->bases.back().selfName)) { if (std::find(ctx->bases.back().deducedMembers->begin(), ctx->bases.back().deducedMembers->end(), ed->member) == ctx->bases.back().deducedMembers->end()) diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index dd9b27ca..4e5b1d92 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -158,11 +158,6 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { // Find parents! ctx->bases.push_back({type->ast->name, type->getFunc(), type->args[0], {}, findSuperMethods(type->getFunc())}); - // if (startswith(type->ast->name, "Foo")) { - // LOG(": {}", type->toString()); - // for (auto &s: ctx->bases.back().supers) - // LOG(" - {}", s->toString()); - // } auto clonedAst = ctx->cache->functions[type->ast->name].ast->clone(); auto *ast = (FunctionStmt *)clonedAst.get(); addFunctionGenerics(type); From 84237247a25ed6842cdd899b29a5362afc07bca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Sat, 15 Jan 2022 18:30:01 -0800 Subject: [PATCH 27/61] Fix Callable[] and None --- codon/parser/visitors/simplify/simplify_expr.cpp | 4 ++-- codon/parser/visitors/simplify/simplify_stmt.cpp | 9 ++++++++- codon/parser/visitors/typecheck/typecheck_expr.cpp | 2 ++ codon/parser/visitors/typecheck/typecheck_infer.cpp | 4 +++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/codon/parser/visitors/simplify/simplify_expr.cpp b/codon/parser/visitors/simplify/simplify_expr.cpp index 5f5299d5..ecd53ee6 100644 --- a/codon/parser/visitors/simplify/simplify_expr.cpp +++ b/codon/parser/visitors/simplify/simplify_expr.cpp @@ -308,10 +308,10 @@ void SimplifyVisitor::visit(UnaryExpr *expr) { void SimplifyVisitor::visit(BinaryExpr *expr) { auto lhs = (startswith(expr->op, "is") && expr->lexpr->getNone()) ? clone(expr->lexpr) - : transform(expr->lexpr); + : transform(expr->lexpr, startswith(expr->op, "is")); auto rhs = (startswith(expr->op, "is") && expr->rexpr->getNone()) ? clone(expr->rexpr) - : transform(expr->rexpr, false, + : transform(expr->rexpr, startswith(expr->op, "is"), /*allowAssign*/ expr->op != "&&" && expr->op != "||"); resultExpr = N(lhs, expr->op, rhs, expr->inPlace); } diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 0af91181..7fef1ad6 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -562,8 +562,15 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { } // First add all generics! + auto deflt = a.deflt; + if (typeAst && typeAst->getIndex() && typeAst->getIndex()->expr->isId("Callable") && + deflt && deflt->getNone()) + deflt = N(N("NoneType")); + if (typeAst && (typeAst->isId("type") || typeAst->isId("TypeVar")) && deflt && + deflt->getNone()) + deflt = N("NoneType"); args.emplace_back( - Param{std::string(stars, '*') + name, typeAst, a.deflt, a.generic}); + Param{std::string(stars, '*') + name, typeAst, deflt, a.generic}); if (a.generic) { if (a.type->getIndex() && a.type->getIndex()->expr->isId("Static")) ctx->add(SimplifyItem::Var, varName, name); diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 811f28a6..16f00d64 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -645,6 +645,8 @@ ExprPtr TypecheckVisitor::transformBinary(BinaryExpr *expr, bool isAtomic, // Check if this is a "a is None" expression. If so, ... if (expr->op == "is" && expr->rexpr->getNone()) { + if (expr->lexpr->getType()->getClass()->name == "NoneType") + return transform(N(true)); if (expr->lexpr->getType()->getClass()->name != TYPE_OPTIONAL) // ... return False if lhs is not an Optional... return transform(N(false)); diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index 4e5b1d92..f4c2a10f 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -403,8 +403,10 @@ std::pair TypecheckVisitor::inferTypes(StmtPtr result, bool keepLa ir::types::Type *TypecheckVisitor::getLLVMType(const types::ClassType *t) { auto realizedName = t->realizedTypeName(); + if (!in(ctx->cache->classes[t->name].realizations, realizedName)) + realizeType(const_cast(t)); if (auto l = ctx->cache->classes[t->name].realizations[realizedName]->ir) - return l; + return l; auto getLLVM = [&](const TypePtr &tt) { auto t = tt->getClass(); seqassert(t && in(ctx->cache->classes[t->name].realizations, t->realizedTypeName()), From de678930d85a9a45d9e949c064d44282a53c5c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Tue, 18 Jan 2022 21:19:52 -0800 Subject: [PATCH 28/61] Add Matplotlib plot support --- codon/app/main.cpp | 6 +++-- codon/sir/analyze/dataflow/cfg.h | 1 + codon/sir/util/operator.h | 6 +++-- stdlib/internal/builtin.codon | 2 +- stdlib/internal/python.codon | 46 +++++++++++++++++++++++++------- 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/codon/app/main.cpp b/codon/app/main.cpp index ae830ccf..e3fcb0b8 100644 --- a/codon/app/main.cpp +++ b/codon/app/main.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -226,17 +227,18 @@ int jitMode(const std::vector &args) { llvm::cantFail(jit.init()); fmt::print(">>> Codon JIT v{} <<<\n", CODON_VERSION); std::string code; + // std::ifstream qq("scratch.codon"); for (std::string line; std::getline(std::cin, line);) { if (line != "#%%") { code += line + "\n"; } else { - fmt::print("{}\n\n[done]\n\n", jitExec(&jit, code)); + fmt::print("{}[done]\n", jitExec(&jit, code)); code = ""; fflush(stdout); } } if (!code.empty()) - fmt::print("{}\n\n[done]\n\n", jitExec(&jit, code)); + fmt::print("{}[done]\n", jitExec(&jit, code)); return EXIT_SUCCESS; } diff --git a/codon/sir/analyze/dataflow/cfg.h b/codon/sir/analyze/dataflow/cfg.h index 3627d32f..1294067d 100644 --- a/codon/sir/analyze/dataflow/cfg.h +++ b/codon/sir/analyze/dataflow/cfg.h @@ -481,6 +481,7 @@ public: void visit(const dsl::CustomInstr *v) override; template void process(const NodeType *v) { + if (!v) return; if (seenIds.find(v->getId()) != seenIds.end()) return; seenIds.insert(v->getId()); diff --git a/codon/sir/util/operator.h b/codon/sir/util/operator.h index 40ec34d5..41a6c81e 100644 --- a/codon/sir/util/operator.h +++ b/codon/sir/util/operator.h @@ -68,8 +68,10 @@ public: } void visit(BodiedFunc *f) override { - seen.insert(f->getBody()->getId()); - process(f->getBody()); + if (f->getBody()) { + seen.insert(f->getBody()->getId()); + process(f->getBody()); + } } LAMBDA_VISIT(VarValue); diff --git a/stdlib/internal/builtin.codon b/stdlib/internal/builtin.codon index b318dcb2..5e1498fb 100644 --- a/stdlib/internal/builtin.codon +++ b/stdlib/internal/builtin.codon @@ -331,7 +331,7 @@ class int: return result -def _jit_display(x, s: Static[str], bundle: Set[str] = set()): +def _jit_display(x, s: Static[str], bundle: Set[str] = Set[str]()): if hasattr(x, "_repr_mimebundle_") and s == "jupyter": d = x._repr_mimebundle_(bundle) # TODO: pick appropriate mime diff --git a/stdlib/internal/python.codon b/stdlib/internal/python.codon index 06b60233..95ef05c1 100644 --- a/stdlib/internal/python.codon +++ b/stdlib/internal/python.codon @@ -35,9 +35,32 @@ PyDict_SetItem = Function[[cobj, cobj, cobj], cobj](cobj()) PyDict_Next = Function[[cobj, Ptr[int], Ptr[cobj], Ptr[cobj]], int](cobj()) PyObject_GetIter = Function[[cobj], cobj](cobj()) PyIter_Next = Function[[cobj], cobj](cobj()) +PyObject_HasAttrString = Function[[cobj, cobj], int](cobj()) +PyImport_AddModule = Function[[cobj], cobj](cobj()) _PY_MODULE_CACHE = Dict[str, pyobj]() +_PY_INIT = """ +import io + +cls = None +try: + import matplotlib.figure + cls = matplotlib.figure.Figure +except ModuleNotFoundError: + pass + +def __codon_repr__(fig): + if cls and isinstance(fig, cls): + stream = io.StringIO() + fig.savefig(stream, format="svg") + return 'image/svg+xml', stream.getvalue() + elif hasattr(fig, "_repr_html_"): + return 'text/html', fig._repr_html_() + else: + return 'text/plain', fig.__repr__() +""" + _PY_INITIALIZED = False def init(): global _PY_INITIALIZED @@ -115,8 +138,14 @@ def init(): PyObject_GetIter = dlsym(hnd, "PyObject_GetIter") global PyIter_Next PyIter_Next = dlsym(hnd, "PyIter_Next") + global PyObject_HasAttrString + PyObject_HasAttrString = dlsym(hnd, "PyObject_HasAttrString") + global PyImport_AddModule + PyImport_AddModule = dlsym(hnd, "PyImport_AddModule") + Py_Initialize() + PyRun_SimpleString(_PY_INIT.c_str()) _PY_INITIALIZED = True def ensure_initialized(): @@ -229,20 +258,19 @@ class pyobj: def get(self, T: type) -> T: return T.__from_py__(self) + def _main_module(): + m = PyImport_AddModule("__main__".c_str()) + return pyobj(m) + def _repr_mimebundle_(self, bundle = Set[str]()): - # p = PyObject_GetAttrString(self.p, '_repr_mimebundle_'.c_str()) - # if p != cobj(): - # return Dict[str, str].__from_py__(self._getattr("_repr_mimebundle_").__call__()) - # else: - p = PyObject_GetAttrString(self.p, '_repr_html_'.c_str()) - if p != cobj(): - return {'text/html': str.__from_py__(self._getattr("_repr_html_").__call__())} - return {'text/plain': self.__repr__()} + fn = pyobj._main_module()._getattr("__codon_repr__") + assert fn.p != cobj(), "cannot find python.__codon_repr__" + mime, txt = fn.__call__(self).get(Tuple[str, str]) + return {mime: txt} def none(): raise NotImplementedError() - # Type conversions def py(x) -> pyobj: From c3c48b9c6d4020fca11feeb91b832c553121a3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Tue, 18 Jan 2022 21:57:38 -0800 Subject: [PATCH 29/61] Add Matplotlib plot support --- stdlib/internal/python.codon | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/internal/python.codon b/stdlib/internal/python.codon index 95ef05c1..40eb311a 100644 --- a/stdlib/internal/python.codon +++ b/stdlib/internal/python.codon @@ -44,9 +44,11 @@ _PY_INIT = """ import io cls = None +cls2 = None try: import matplotlib.figure cls = matplotlib.figure.Figure + cls2 = matplotlib.lines.Line2D except ModuleNotFoundError: pass @@ -55,6 +57,10 @@ def __codon_repr__(fig): stream = io.StringIO() fig.savefig(stream, format="svg") return 'image/svg+xml', stream.getvalue() + elif cls2 and isinstance(fig, cls2): + stream = io.StringIO() + fig.savefig(stream, format="svg") + return 'image/svg+xml', stream.getvalue() elif hasattr(fig, "_repr_html_"): return 'text/html', fig._repr_html_() else: From 6f4e24fb006ac37aee1d48ade6c56627c783669a Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Wed, 19 Jan 2022 11:21:40 -0500 Subject: [PATCH 30/61] Update new global detection and processing in JIT --- codon/compiler/jit.cpp | 30 ++++-------------------------- codon/compiler/jit.h | 3 +-- codon/sir/llvm/llvisitor.cpp | 19 +++++++++++++++++++ codon/sir/llvm/llvisitor.h | 5 +++++ 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/codon/compiler/jit.cpp b/codon/compiler/jit.cpp index 38f197b1..4f3b01e7 100644 --- a/codon/compiler/jit.cpp +++ b/codon/compiler/jit.cpp @@ -58,23 +58,14 @@ llvm::Error JIT::init() { return llvm::Error::success(); } -llvm::Expected JIT::run(const ir::Func *input, - const std::vector &newGlobals) { +llvm::Expected JIT::run(const ir::Func *input) { auto *module = compiler->getModule(); auto *pm = compiler->getPassManager(); auto *llvisitor = compiler->getLLVMVisitor(); pm->run(module); const std::string name = ir::LLVMVisitor::getNameForFunction(input); - llvisitor->registerGlobal(input); - for (auto *var : newGlobals) { - llvisitor->registerGlobal(var); - } - for (auto *var : newGlobals) { - if (auto *func = ir::cast(var)) - func->accept(*llvisitor); - } - input->accept(*llvisitor); + llvisitor->processNewGlobals(module); auto pair = llvisitor->takeModule(); if (auto err = engine->addModule({std::move(pair.first), std::move(pair.second)})) @@ -138,11 +129,7 @@ llvm::Expected JIT::exec(const std::string &code) { auto *cache = compiler->getCache(); auto typechecked = ast::TypecheckVisitor::apply(cache, simplified); - std::vector globalNames; - for (auto &g : cache->globals) { - if (!g.second) - globalNames.push_back(g.first); - } + // add newly realized functions std::vector v; std::vector frs; @@ -155,16 +142,7 @@ llvm::Expected JIT::exec(const std::string &code) { ast::TranslateVisitor::apply(cache, std::make_shared(v, false)); cache->jitCell++; - std::vector globalVars; - for (auto &g : globalNames) { - seqassert(cache->globals[g], "JIT global {} not set", g); - globalVars.push_back(cache->globals[g]); - } - for (auto &i : frs) { - seqassert(*i, "JIT fn not set"); - globalVars.push_back(*i); - } - return run(func, globalVars); + return run(func); } catch (const exc::ParserException &e) { *cache = bCache; *(cache->imports[MAIN_IMPORT].ctx) = bSimplify; diff --git a/codon/compiler/jit.h b/codon/compiler/jit.h index b781299d..ab3e42a7 100644 --- a/codon/compiler/jit.h +++ b/codon/compiler/jit.h @@ -28,8 +28,7 @@ public: Engine *getEngine() const { return engine.get(); } llvm::Error init(); - llvm::Expected run(const ir::Func *input, - const std::vector &newGlobals = {}); + llvm::Expected run(const ir::Func *input); llvm::Expected exec(const std::string &code); }; diff --git a/codon/sir/llvm/llvisitor.cpp b/codon/sir/llvm/llvisitor.cpp index 1b877e9b..d108a810 100644 --- a/codon/sir/llvm/llvisitor.cpp +++ b/codon/sir/llvm/llvisitor.cpp @@ -113,6 +113,25 @@ void LLVMVisitor::registerGlobal(const Var *var) { } } +void LLVMVisitor::processNewGlobals(Module *module) { + std::vector newFuncs; + for (auto *var : *module) { + if (!var->isGlobal()) + continue; + auto id = var->getId(); + auto *func = cast(var); + bool isNewFunc = (func && funcs.find(id) == funcs.end()); + if (isNewFunc || (!func && vars.find(id) == vars.end())) + registerGlobal(var); + if (isNewFunc) + newFuncs.push_back(func); + } + + for (auto *func : newFuncs) { + func->accept(*this); + } +} + llvm::Value *LLVMVisitor::getVar(const Var *var) { auto it = vars.find(var->getId()); if (db.jit && var->isGlobal()) { diff --git a/codon/sir/llvm/llvisitor.h b/codon/sir/llvm/llvisitor.h index 2a29e7c4..4f28252b 100644 --- a/codon/sir/llvm/llvisitor.h +++ b/codon/sir/llvm/llvisitor.h @@ -275,6 +275,11 @@ public: /// @param var the global variable (or function) to register void registerGlobal(const Var *var); + /// Processes new globals that were not previously + /// compiled. Used in JIT mode. + /// @param module the IR module + void processNewGlobals(Module *module); + /// Returns the default LLVM linkage type for the module. /// @return LLVM linkage type llvm::GlobalValue::LinkageTypes getDefaultLinkage(); From f51a53e2fe0ccf89a7545ac1a73bf583ba6474ec Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Thu, 20 Jan 2022 11:22:32 -0500 Subject: [PATCH 31/61] Inline JIT functions --- codon/app/main.cpp | 12 ++++-- codon/compiler/jit.cpp | 14 +++++-- codon/sir/llvm/llvisitor.cpp | 80 +++++++++++++++++++++++------------- codon/sir/llvm/llvisitor.h | 8 +--- 4 files changed, 74 insertions(+), 40 deletions(-) diff --git a/codon/app/main.cpp b/codon/app/main.cpp index e3fcb0b8..da32bfa8 100644 --- a/codon/app/main.cpp +++ b/codon/app/main.cpp @@ -56,6 +56,12 @@ void display(const codon::error::ParserErrorInfo &e) { } } +void initLogFlags(const llvm::cl::opt &log) { + codon::getLogger().parse(log); + if (auto *d = getenv("CODON_DEBUG")) + codon::getLogger().parse(std::string(d)); +} + enum BuildKind { LLVM, Bitcode, Object, Executable, Detect }; enum OptMode { Debug, Release }; } // namespace @@ -103,9 +109,7 @@ std::unique_ptr processSource(const std::vector & llvm::cl::opt log("log", llvm::cl::desc("Enable given log streams")); llvm::cl::ParseCommandLineOptions(args.size(), args.data()); - codon::getLogger().parse(log); - if (auto *d = getenv("CODON_DEBUG")) - codon::getLogger().parse(std::string(d)); + initLogFlags(log); auto &exts = supportedExtensions(); if (input != "-" && std::find_if(exts.begin(), exts.end(), [&](auto &ext) { @@ -207,7 +211,9 @@ std::string jitExec(codon::jit::JIT *jit, const std::string &code) { int jitMode(const std::vector &args) { llvm::cl::list plugins("plugin", llvm::cl::desc("Load specified plugin")); + llvm::cl::opt log("log", llvm::cl::desc("Enable given log streams")); llvm::cl::ParseCommandLineOptions(args.size(), args.data()); + initLogFlags(log); codon::jit::JIT jit(args[0]); // load plugins diff --git a/codon/compiler/jit.cpp b/codon/compiler/jit.cpp index 4f3b01e7..881e5a82 100644 --- a/codon/compiler/jit.cpp +++ b/codon/compiler/jit.cpp @@ -44,7 +44,7 @@ llvm::Error JIT::init() { pm->run(module); module->accept(*llvisitor); - auto pair = llvisitor->takeModule(); + auto pair = llvisitor->takeModule(module); if (auto err = engine->addModule({std::move(pair.first), std::move(pair.second)})) return err; @@ -62,12 +62,18 @@ llvm::Expected JIT::run(const ir::Func *input) { auto *module = compiler->getModule(); auto *pm = compiler->getPassManager(); auto *llvisitor = compiler->getLLVMVisitor(); + + Timer t1("jit/ir"); pm->run(module); + t1.log(); const std::string name = ir::LLVMVisitor::getNameForFunction(input); - llvisitor->processNewGlobals(module); - auto pair = llvisitor->takeModule(); + Timer t2("jit/llvm"); + auto pair = llvisitor->takeModule(module); + t2.log(); + + Timer t3("jit/engine"); if (auto err = engine->addModule({std::move(pair.first), std::move(pair.second)})) return std::move(err); @@ -76,6 +82,8 @@ llvm::Expected JIT::run(const ir::Func *input) { return std::move(err); auto *repl = (InputFunc *)func->getAddress(); + t3.log(); + try { (*repl)(); } catch (const JITError &e) { diff --git a/codon/sir/llvm/llvisitor.cpp b/codon/sir/llvm/llvisitor.cpp index d108a810..269094ea 100644 --- a/codon/sir/llvm/llvisitor.cpp +++ b/codon/sir/llvm/llvisitor.cpp @@ -113,25 +113,6 @@ void LLVMVisitor::registerGlobal(const Var *var) { } } -void LLVMVisitor::processNewGlobals(Module *module) { - std::vector newFuncs; - for (auto *var : *module) { - if (!var->isGlobal()) - continue; - auto id = var->getId(); - auto *func = cast(var); - bool isNewFunc = (func && funcs.find(id) == funcs.end()); - if (isNewFunc || (!func && vars.find(id) == vars.end())) - registerGlobal(var); - if (isNewFunc) - newFuncs.push_back(func); - } - - for (auto *func : newFuncs) { - func->accept(*this); - } -} - llvm::Value *LLVMVisitor::getVar(const Var *var) { auto it = vars.find(var->getId()); if (db.jit && var->isGlobal()) { @@ -230,7 +211,34 @@ std::unique_ptr LLVMVisitor::makeModule(llvm::LLVMContext &context } std::pair, std::unique_ptr> -LLVMVisitor::takeModule(const SrcInfo *src) { +LLVMVisitor::takeModule(Module *module, const SrcInfo *src) { + // process any new functions or globals + if (module) { + std::unordered_set funcsToProcess; + for (auto *var : *module) { + auto id = var->getId(); + if (auto *func = cast(var)) { + if (funcs.find(id) != funcs.end()) + continue; + else + funcsToProcess.insert(id); + } else { + if (vars.find(id) != vars.end()) + continue; + } + + registerGlobal(var); + } + + for (auto *var : *module) { + if (auto *func = cast(var)) { + if (funcsToProcess.find(func->getId()) != funcsToProcess.end()) { + process(func); + } + } + } + } + db.builder->finalize(); auto currentContext = std::move(context); auto currentModule = std::move(M); @@ -240,10 +248,25 @@ LLVMVisitor::takeModule(const SrcInfo *src) { func = nullptr; block = nullptr; value = nullptr; - for (auto &it : vars) - it.second = nullptr; - for (auto &it : funcs) - it.second = nullptr; + + for (auto it = funcs.begin(); it != funcs.end();) { + if (it->second && it->second->hasPrivateLinkage()) { + it = funcs.erase(it); + } else { + it->second = nullptr; + ++it; + } + } + + for (auto it = vars.begin(); it != vars.end();) { + if (it->second && !llvm::isa(it->second)) { + it = vars.erase(it); + } else { + it->second = nullptr; + ++it; + } + } + coro.reset(); loops.clear(); trycatch.clear(); @@ -824,7 +847,7 @@ void LLVMVisitor::visit(const InternalFunc *x) { auto *funcType = cast(x->getType()); std::vector argTypes(funcType->begin(), funcType->end()); - func->setLinkage(getDefaultLinkage()); + func->setLinkage(llvm::GlobalValue::PrivateLinkage); func->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline); std::vector args; for (auto it = func->arg_begin(); it != func->arg_end(); ++it) { @@ -987,7 +1010,7 @@ void LLVMVisitor::visit(const LLVMFunc *x) { seqassert(!fail, "linking failed"); func = M->getFunction(getNameForFunction(x)); seqassert(func, "function not linked in"); - func->setLinkage(getDefaultLinkage()); + func->setLinkage(llvm::GlobalValue::PrivateLinkage); func->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline); func->setSubprogram(getDISubprogramForFunc(x)); @@ -1011,10 +1034,11 @@ void LLVMVisitor::visit(const BodiedFunc *x) { setDebugInfoForNode(x); auto *fnAttributes = x->getAttribute(); - if (fnAttributes && fnAttributes->has("std.internal.attributes.export")) { + if (x->isJIT() || + (fnAttributes && fnAttributes->has("std.internal.attributes.export"))) { func->setLinkage(llvm::GlobalValue::ExternalLinkage); } else { - func->setLinkage(getDefaultLinkage()); + func->setLinkage(llvm::GlobalValue::PrivateLinkage); } if (fnAttributes && fnAttributes->has("std.internal.attributes.inline")) { func->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline); diff --git a/codon/sir/llvm/llvisitor.h b/codon/sir/llvm/llvisitor.h index 4f28252b..ab20fcc6 100644 --- a/codon/sir/llvm/llvisitor.h +++ b/codon/sir/llvm/llvisitor.h @@ -275,11 +275,6 @@ public: /// @param var the global variable (or function) to register void registerGlobal(const Var *var); - /// Processes new globals that were not previously - /// compiled. Used in JIT mode. - /// @param module the IR module - void processNewGlobals(Module *module); - /// Returns the default LLVM linkage type for the module. /// @return LLVM linkage type llvm::GlobalValue::LinkageTypes getDefaultLinkage(); @@ -295,10 +290,11 @@ public: /// Returns the current module/LLVM context and replaces them /// with new, fresh ones. References to variables or functions /// from the old module will be included as "external". + /// @param module the IR module /// @param src source information for the new module /// @return the current module/context, replaced internally std::pair, std::unique_ptr> - takeModule(const SrcInfo *src = nullptr); + takeModule(Module *module, const SrcInfo *src = nullptr); /// Sets current debug info based on a given node. /// @param node the node whose debug info to use From 8dd7d2e0eaef766f8296de8d8ac4e52deb2dcaeb Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Tue, 25 Jan 2022 16:10:34 -0500 Subject: [PATCH 32/61] Add new attributes --- codon/sir/attribute.cpp | 111 ++++++++++++++++++++++++++++++++++++++- codon/sir/attribute.h | 109 ++++++++++++++++++++++++++++++++++---- codon/sir/util/cloning.h | 4 +- 3 files changed, 212 insertions(+), 12 deletions(-) diff --git a/codon/sir/attribute.cpp b/codon/sir/attribute.cpp index b27541e5..cec29267 100644 --- a/codon/sir/attribute.cpp +++ b/codon/sir/attribute.cpp @@ -1,5 +1,8 @@ -#include "value.h" +#include "attribute.h" +#include "codon/sir/func.h" +#include "codon/sir/util/cloning.h" +#include "codon/sir/value.h" #include "codon/util/fmt/ostream.h" namespace codon { @@ -36,5 +39,111 @@ std::ostream &MemberAttribute::doFormat(std::ostream &os) const { const std::string SrcInfoAttribute::AttributeName = "srcInfoAttribute"; +const std::string ListLiteralAttribute::AttributeName = "listLiteralAttribute"; + +std::unique_ptr ListLiteralAttribute::clone(util::CloneVisitor &cv) const { + std::vector elementsCloned; + for (auto *val : elements) + elementsCloned.push_back(cv.clone(val)); + return std::make_unique(elementsCloned); +} + +std::unique_ptr +ListLiteralAttribute::forceClone(util::CloneVisitor &cv) const { + std::vector elementsCloned; + for (auto *val : elements) + elementsCloned.push_back(cv.forceClone(val)); + return std::make_unique(elementsCloned); +} + +std::ostream &ListLiteralAttribute::doFormat(std::ostream &os) const { + std::vector strings; + for (auto *val : elements) + strings.push_back(fmt::format(FMT_STRING("{}"), *val)); + fmt::print(os, FMT_STRING("[{}]"), fmt::join(strings.begin(), strings.end(), ",")); + return os; +} + +const std::string SetLiteralAttribute::AttributeName = "setLiteralAttribute"; + +std::unique_ptr SetLiteralAttribute::clone(util::CloneVisitor &cv) const { + std::vector elementsCloned; + for (auto *val : elements) + elementsCloned.push_back(cv.clone(val)); + return std::make_unique(elementsCloned); +} + +std::unique_ptr +SetLiteralAttribute::forceClone(util::CloneVisitor &cv) const { + std::vector elementsCloned; + for (auto *val : elements) + elementsCloned.push_back(cv.forceClone(val)); + return std::make_unique(elementsCloned); +} + +std::ostream &SetLiteralAttribute::doFormat(std::ostream &os) const { + std::vector strings; + for (auto *val : elements) + strings.push_back(fmt::format(FMT_STRING("{}"), *val)); + fmt::print(os, FMT_STRING("set([{}])"), + fmt::join(strings.begin(), strings.end(), ",")); + return os; +} + +const std::string DictLiteralAttribute::AttributeName = "dictLiteralAttribute"; + +std::unique_ptr DictLiteralAttribute::clone(util::CloneVisitor &cv) const { + std::vector elementsCloned; + for (auto &val : elements) + elementsCloned.push_back({cv.clone(val.key), cv.clone(val.value)}); + return std::make_unique(elementsCloned); +} + +std::unique_ptr +DictLiteralAttribute::forceClone(util::CloneVisitor &cv) const { + std::vector elementsCloned; + for (auto &val : elements) + elementsCloned.push_back({cv.forceClone(val.key), cv.forceClone(val.value)}); + return std::make_unique(elementsCloned); +} + +std::ostream &DictLiteralAttribute::doFormat(std::ostream &os) const { + std::vector strings; + for (auto &val : elements) + strings.push_back(fmt::format(FMT_STRING("{}:{}"), *val.key, *val.value)); + fmt::print(os, FMT_STRING("dict([{}])"), + fmt::join(strings.begin(), strings.end(), ",")); + return os; +} + +const std::string PartialFunctionAttribute::AttributeName = "partialFunctionAttribute"; + +std::unique_ptr +PartialFunctionAttribute::clone(util::CloneVisitor &cv) const { + std::vector argsCloned; + for (auto *val : args) + argsCloned.push_back(cv.clone(val)); + return std::make_unique(cast(cv.clone(func)), + argsCloned); +} + +std::unique_ptr +PartialFunctionAttribute::forceClone(util::CloneVisitor &cv) const { + std::vector argsCloned; + for (auto *val : args) + argsCloned.push_back(cv.forceClone(val)); + return std::make_unique(cast(cv.forceClone(func)), + argsCloned); +} + +std::ostream &PartialFunctionAttribute::doFormat(std::ostream &os) const { + std::vector strings; + for (auto *val : args) + strings.push_back(val ? fmt::format(FMT_STRING("{}"), *val) : "..."); + fmt::print(os, FMT_STRING("{}({})"), func->getName(), + fmt::join(strings.begin(), strings.end(), ",")); + return os; +} + } // namespace ir } // namespace codon diff --git a/codon/sir/attribute.h b/codon/sir/attribute.h index 48beb5b7..9c4c0429 100644 --- a/codon/sir/attribute.h +++ b/codon/sir/attribute.h @@ -14,6 +14,13 @@ namespace codon { namespace ir { +class Func; +class Value; + +namespace util { +class CloneVisitor; +} + /// Base for SIR attributes. struct Attribute { virtual ~Attribute() noexcept = default; @@ -26,14 +33,15 @@ struct Attribute { } /// @return a clone of the attribute - std::unique_ptr clone() const { - return std::unique_ptr(doClone()); + virtual std::unique_ptr clone(util::CloneVisitor &cv) const = 0; + + /// @return a clone of the attribute + virtual std::unique_ptr forceClone(util::CloneVisitor &cv) const { + return clone(cv); } private: virtual std::ostream &doFormat(std::ostream &os) const = 0; - - virtual Attribute *doClone() const = 0; }; /// Attribute containing SrcInfo @@ -48,10 +56,12 @@ struct SrcInfoAttribute : public Attribute { /// @param info the source info explicit SrcInfoAttribute(codon::SrcInfo info) : info(std::move(info)) {} + std::unique_ptr clone(util::CloneVisitor &cv) const override { + return std::make_unique(*this); + } + private: std::ostream &doFormat(std::ostream &os) const override { return os << info; } - - Attribute *doClone() const override { return new SrcInfoAttribute(*this); } }; /// Attribute containing function information @@ -76,10 +86,12 @@ struct KeyValueAttribute : public Attribute { /// string if none std::string get(const std::string &key) const; + std::unique_ptr clone(util::CloneVisitor &cv) const override { + return std::make_unique(*this); + } + private: std::ostream &doFormat(std::ostream &os) const override; - - Attribute *doClone() const override { return new KeyValueAttribute(*this); } }; /// Attribute containing type member information @@ -95,10 +107,89 @@ struct MemberAttribute : public Attribute { explicit MemberAttribute(std::map memberSrcInfo) : memberSrcInfo(std::move(memberSrcInfo)) {} + std::unique_ptr clone(util::CloneVisitor &cv) const override { + return std::make_unique(*this); + } + private: std::ostream &doFormat(std::ostream &os) const override; +}; - Attribute *doClone() const override { return new MemberAttribute(*this); } +/// Attribute attached to IR structures corresponding to list literals +struct ListLiteralAttribute : public Attribute { + static const std::string AttributeName; + + /// values contained in list literal + std::vector elements; + + explicit ListLiteralAttribute(std::vector elements) + : elements(std::move(elements)) {} + + std::unique_ptr clone(util::CloneVisitor &cv) const override; + std::unique_ptr forceClone(util::CloneVisitor &cv) const override; + +private: + std::ostream &doFormat(std::ostream &os) const override; +}; + +/// Attribute attached to IR structures corresponding to set literals +struct SetLiteralAttribute : public Attribute { + static const std::string AttributeName; + + /// values contained in set literal + std::vector elements; + + explicit SetLiteralAttribute(std::vector elements) + : elements(std::move(elements)) {} + + std::unique_ptr clone(util::CloneVisitor &cv) const override; + std::unique_ptr forceClone(util::CloneVisitor &cv) const override; + +private: + std::ostream &doFormat(std::ostream &os) const override; +}; + +/// Attribute attached to IR structures corresponding to dict literals +struct DictLiteralAttribute : public Attribute { + struct KeyValuePair { + Value *key; + Value *value; + }; + + static const std::string AttributeName; + + /// keys and values contained in dict literal + std::vector elements; + + explicit DictLiteralAttribute(std::vector elements) + : elements(std::move(elements)) {} + + std::unique_ptr clone(util::CloneVisitor &cv) const override; + std::unique_ptr forceClone(util::CloneVisitor &cv) const override; + +private: + std::ostream &doFormat(std::ostream &os) const override; +}; + +/// Attribute attached to IR structures corresponding to partial functions +struct PartialFunctionAttribute : public Attribute { + static const std::string AttributeName; + + /// function being called + Func *func; + + /// partial arguments, or null if none + /// e.g. "f(a, ..., b)" has elements [a, null, b] + std::vector args; + + PartialFunctionAttribute(Func *func, std::vector args) + : func(func), args(std::move(args)) {} + + std::unique_ptr clone(util::CloneVisitor &cv) const override; + std::unique_ptr forceClone(util::CloneVisitor &cv) const override; + +private: + std::ostream &doFormat(std::ostream &os) const override; }; } // namespace ir diff --git a/codon/sir/util/cloning.h b/codon/sir/util/cloning.h index c62ce17f..e3aaf01f 100644 --- a/codon/sir/util/cloning.h +++ b/codon/sir/util/cloning.h @@ -82,7 +82,7 @@ public: for (auto it = other->attributes_begin(); it != other->attributes_end(); ++it) { const auto *attr = other->getAttribute(*it); if (attr->needsClone()) { - ctx[id]->setAttribute(attr->clone(), *it); + ctx[id]->setAttribute(attr->clone(*this), *it); } } } @@ -125,7 +125,7 @@ public: for (auto it = other->attributes_begin(); it != other->attributes_end(); ++it) { const auto *attr = other->getAttribute(*it); if (attr->needsClone()) { - ctx[id]->setAttribute(attr->clone(), *it); + ctx[id]->setAttribute(attr->forceClone(*this), *it); } } } From fd06afaa6190de8a02b6816df86a51f127913f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Tue, 25 Jan 2022 13:17:37 -0800 Subject: [PATCH 33/61] Fix linux build [wip] --- CMakeLists.txt | 2 +- cmake/deps.cmake | 7 ++-- codon/compiler/compiler.cpp | 4 +- codon/dsl/plugins.cpp | 4 +- codon/parser/common.cpp | 3 +- codon/parser/visitors/doc/doc.cpp | 4 +- codon/parser/visitors/translate/translate.cpp | 3 +- codon/util/common.h | 4 +- stdlib/internal/python.codon | 41 +++++++++++-------- 9 files changed, 37 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 02e5c11c..7038888f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -334,7 +334,7 @@ llvm_map_components_to_libnames(LLVM_LIBS if(APPLE) target_link_libraries(codonc PRIVATE ${LLVM_LIBS} dl codonrt) else() - target_link_libraries(codonc PRIVATE ${STATIC_LIBCPP} ${LLVM_LIBS} dl codonrt) + target_link_libraries(codonc PRIVATE ${STATIC_LIBCPP} ${LLVM_LIBS} dl codonrt stdc++fs) endif() # Gather headers diff --git a/cmake/deps.cmake b/cmake/deps.cmake index dfa45958..8f253292 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -144,16 +144,17 @@ if(CODON_JUPYTER) CPMAddPackage( NAME json GITHUB_REPOSITORY "nlohmann/json" - VERSION 3.10.4) + VERSION 3.10.1) CPMAddPackage( NAME xeus GITHUB_REPOSITORY "jupyter-xeus/xeus" VERSION 2.2.0 GIT_TAG 2.2.0 - PATCH_COMMAND sed -i bak "s/-Wunused-parameter -Wextra -Wreorder//g" CMakeLists.txt + PATCH_COMMAND sed -ibak "s/-Wunused-parameter -Wextra -Wreorder//g" CMakeLists.txt OPTIONS "BUILD_EXAMPLES OFF" "XEUS_BUILD_SHARED_LIBS OFF" - "XEUS_STATIC_DEPENDENCIES ON") + "XEUS_STATIC_DEPENDENCIES ON" + "CMAKE_POSITION_INDEPENDENT_CODE ON") if (xeus_ADDED) install(TARGETS nlohmann_json EXPORT xeus-targets) endif() diff --git a/codon/compiler/compiler.cpp b/codon/compiler/compiler.cpp index f922a4c0..90637586 100644 --- a/codon/compiler/compiler.cpp +++ b/codon/compiler/compiler.cpp @@ -1,7 +1,5 @@ #include "compiler.h" -#include - #include "codon/parser/cache.h" #include "codon/parser/peg/peg.h" #include "codon/parser/visitors/doc/doc.h" @@ -67,7 +65,7 @@ Compiler::parse(bool isCode, const std::string &file, const std::string &code, const std::unordered_map &defines) { input = file; std::string abspath = - (file != "-") ? std::filesystem::absolute(std::filesystem::path(file)).string() + (file != "-") ? std::experimental::filesystem::absolute(std::experimental::filesystem::path(file)).string() : file; try { Timer t1("parse"); diff --git a/codon/dsl/plugins.cpp b/codon/dsl/plugins.cpp index 07c1fb97..fe82b968 100644 --- a/codon/dsl/plugins.cpp +++ b/codon/dsl/plugins.cpp @@ -1,7 +1,7 @@ #include "plugins.h" #include -#include +#include #include "codon/parser/common.h" #include "codon/util/common.h" @@ -17,7 +17,7 @@ llvm::Expected pluginError(const std::string &msg) { typedef std::unique_ptr LoadFunc(); } // namespace -namespace fs = std::filesystem; +namespace fs = std::experimental::filesystem; llvm::Expected PluginManager::load(const std::string &path) { #if __APPLE__ diff --git a/codon/parser/common.cpp b/codon/parser/common.cpp index bb64649c..3846b202 100644 --- a/codon/parser/common.cpp +++ b/codon/parser/common.cpp @@ -1,6 +1,5 @@ #include "common.h" -#include #include #include @@ -195,7 +194,7 @@ std::string executable_path(const char *argv0) { std::string executable_path(const char *argv0) { return std::string(argv0); } #endif -namespace fs = std::filesystem; +namespace fs = std::experimental::filesystem; namespace { void addPath(std::vector &paths, const fs::path &path) { diff --git a/codon/parser/visitors/doc/doc.cpp b/codon/parser/visitors/doc/doc.cpp index e5dc470b..e67ac1e4 100644 --- a/codon/parser/visitors/doc/doc.cpp +++ b/codon/parser/visitors/doc/doc.cpp @@ -1,6 +1,6 @@ #include "doc.h" -#include +#include #include #include #include @@ -116,7 +116,7 @@ std::shared_ptr DocVisitor::apply(const std::string &argv0, auto ctx = std::make_shared(shared); for (auto &f : files) { - auto path = std::filesystem::canonical(std::filesystem::path(f)).string(); + auto path = std::experimental::filesystem::canonical(std::experimental::filesystem::path(f)).string(); ctx->setFilename(path); ast = ast::parseFile(shared->cache, path); // LOG("parsing {}", f); diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index 7e0ba953..65b5e604 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -1,6 +1,5 @@ #include "translate.h" -#include #include #include #include @@ -36,7 +35,7 @@ ir::Func *TranslateVisitor::apply(Cache *cache, StmtPtr stmts) { } else { main = cast(cache->module->getMainFunc()); auto path = - std::filesystem::canonical(std::filesystem::path(cache->module0)).string(); + std::experimental::filesystem::canonical(std::experimental::filesystem::path(cache->module0)).string(); main->setSrcInfo({path, 0, 0, 0}); } diff --git a/codon/util/common.h b/codon/util/common.h index 780ae25c..80661490 100644 --- a/codon/util/common.h +++ b/codon/util/common.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include @@ -123,7 +123,7 @@ struct SrcInfo { SrcInfo() : SrcInfo("", 0, 0, 0) {} friend std::ostream &operator<<(std::ostream &out, const codon::SrcInfo &src) { - out << std::filesystem::path(src.file).filename() << ":" << src.line << ":" + out << std::experimental::filesystem::path(src.file).filename() << ":" << src.line << ":" << src.col; return out; } diff --git a/stdlib/internal/python.codon b/stdlib/internal/python.codon index 40eb311a..e0d6142a 100644 --- a/stdlib/internal/python.codon +++ b/stdlib/internal/python.codon @@ -43,28 +43,33 @@ _PY_MODULE_CACHE = Dict[str, pyobj]() _PY_INIT = """ import io -cls = None -cls2 = None +clsf = None +clsa = None +plt = None try: - import matplotlib.figure - cls = matplotlib.figure.Figure - cls2 = matplotlib.lines.Line2D + import matplotlib.figure + import matplotlib.pyplot + plt = matplotlib.pyplot + clsf = matplotlib.figure.Figure + clsa = matplotlib.artist.Artist except ModuleNotFoundError: - pass + pass def __codon_repr__(fig): - if cls and isinstance(fig, cls): - stream = io.StringIO() - fig.savefig(stream, format="svg") - return 'image/svg+xml', stream.getvalue() - elif cls2 and isinstance(fig, cls2): - stream = io.StringIO() - fig.savefig(stream, format="svg") - return 'image/svg+xml', stream.getvalue() - elif hasattr(fig, "_repr_html_"): - return 'text/html', fig._repr_html_() - else: - return 'text/plain', fig.__repr__() + if clsf and isinstance(fig, clsf): + stream = io.StringIO() + fig.savefig(stream, format="svg") + return 'image/svg+xml', stream.getvalue() + elif clsa and isinstance(fig, list) and all( + isinstance(i, clsa) for i in fig + ): + stream = io.StringIO() + plt.gcf().savefig(stream, format="svg") + return 'image/svg+xml', stream.getvalue() + elif hasattr(fig, "_repr_html_"): + return 'text/html', fig._repr_html_() + else: + return 'text/plain', fig.__repr__() """ _PY_INITIALIZED = False From 4b2dfaf28f62a680f5f7c27a4261a5d97713e1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Tue, 25 Jan 2022 18:55:00 -0800 Subject: [PATCH 34/61] Fix partial handling in IR --- codon/compiler/compiler.cpp | 18 ++++- codon/parser/ast/expr.cpp | 5 +- codon/parser/ast/expr.h | 6 ++ codon/parser/ast/stmt.cpp | 1 + codon/parser/cache.h | 3 + .../visitors/simplify/simplify_expr.cpp | 17 ++++- codon/parser/visitors/translate/translate.cpp | 4 + .../visitors/typecheck/typecheck_expr.cpp | 75 ++++++++++--------- .../visitors/typecheck/typecheck_stmt.cpp | 7 ++ 9 files changed, 94 insertions(+), 42 deletions(-) diff --git a/codon/compiler/compiler.cpp b/codon/compiler/compiler.cpp index 2c920274..0a0d3836 100644 --- a/codon/compiler/compiler.cpp +++ b/codon/compiler/compiler.cpp @@ -9,6 +9,9 @@ #include "codon/parser/visitors/simplify/simplify.h" #include "codon/parser/visitors/translate/translate.h" #include "codon/parser/visitors/typecheck/typecheck.h" +#include "codon/sir/util/operator.h" +#include "codon/sir/util/irtools.h" + namespace codon { @@ -110,9 +113,22 @@ Compiler::parseCode(const std::string &file, const std::string &code, int startL const std::unordered_map &defines) { return parse(/*isCode=*/true, file, code, startLine, testFlags, defines); } - +struct DummyOp : public codon::ir::util::Operator { + void handle(codon::ir::CallInstr *x) override { + auto *M = x->getModule(); + auto *func = codon::ir::util::getFunc(x->getCallee()); + if (!func || func->getUnmangledName() != "foo") + return; + auto fn = M->getOrRealizeFunc("bar", {x->front()->getType()}, {}); + seqassert(fn, "did not succeed"); + auto result = codon::ir::util::call(fn, {x->front()}); + x->replaceAll(result); + } +}; llvm::Error Compiler::compile() { pm->run(module.get()); + auto d = DummyOp(); + module->accept(d); llvisitor->visit(module.get()); return llvm::Error::success(); } diff --git a/codon/parser/ast/expr.cpp b/codon/parser/ast/expr.cpp index 0f3e3dd2..c4765443 100644 --- a/codon/parser/ast/expr.cpp +++ b/codon/parser/ast/expr.cpp @@ -397,11 +397,14 @@ StmtExpr::StmtExpr(std::shared_ptr stmt, std::shared_ptr stmt2, stmts.push_back(std::move(stmt2)); } StmtExpr::StmtExpr(const StmtExpr &expr) - : Expr(expr), stmts(ast::clone(expr.stmts)), expr(ast::clone(expr.expr)) {} + : Expr(expr), stmts(ast::clone(expr.stmts)), expr(ast::clone(expr.expr)), + attributes(expr.attributes) {} std::string StmtExpr::toString() const { return wrapType(format("stmt-expr ({}) {}", combine(stmts, " "), expr->toString())); } ACCEPT_IMPL(StmtExpr, ASTVisitor); +bool StmtExpr::hasAttr(const std::string &attr) const { return in(attributes, attr); } +void StmtExpr::setAttr(const std::string &attr) { attributes.insert(attr); } PtrExpr::PtrExpr(ExprPtr expr) : Expr(), expr(std::move(expr)) {} PtrExpr::PtrExpr(const PtrExpr &expr) : Expr(expr), expr(ast::clone(expr.expr)) {} diff --git a/codon/parser/ast/expr.h b/codon/parser/ast/expr.h index df5716c4..2a9dd015 100644 --- a/codon/parser/ast/expr.h +++ b/codon/parser/ast/expr.h @@ -600,6 +600,8 @@ struct RangeExpr : public Expr { struct StmtExpr : public Expr { std::vector> stmts; ExprPtr expr; + /// Set of attributes. + std::set attributes; StmtExpr(std::vector> stmts, ExprPtr expr); StmtExpr(std::shared_ptr stmt, ExprPtr expr); @@ -610,6 +612,10 @@ struct StmtExpr : public Expr { ACCEPT(ASTVisitor); const StmtExpr *getStmtExpr() const override { return this; } + + /// Attribute helpers + bool hasAttr(const std::string &attr) const; + void setAttr(const std::string &attr); }; /// Pointer expression (__ptr__(expr)). diff --git a/codon/parser/ast/stmt.cpp b/codon/parser/ast/stmt.cpp index 119825aa..f7bd2e57 100644 --- a/codon/parser/ast/stmt.cpp +++ b/codon/parser/ast/stmt.cpp @@ -42,6 +42,7 @@ std::string SuiteStmt::toString(int indent) const { } ACCEPT_IMPL(SuiteStmt, ASTVisitor); void SuiteStmt::flatten(StmtPtr s, std::vector &stmts) { + // WARNING: does not preserve attributes! if (!s) return; auto suite = const_cast(s->getSuite()); diff --git a/codon/parser/cache.h b/codon/parser/cache.h index 6b0ccbb7..feb234bf 100644 --- a/codon/parser/cache.h +++ b/codon/parser/cache.h @@ -188,6 +188,9 @@ struct Cache : public std::enable_shared_from_this { std::shared_ptr codegenCtx; /// Set of function realizations that are to be translated to IR. std::set> pendingRealizations; + /// Mapping of partial record names to function pointers and corresponding masks. + std::unordered_map>> + partials; /// Custom operators std::unordered_map(N(N(clone(var), "append"), clone(it))))); } } - resultExpr = N(stmts, transform(var)); + auto e = N(stmts, transform(var)); + e->setAttr(ir::ListLiteralAttribute::AttributeName); + resultExpr = e; ctx->popBlock(); } @@ -207,7 +210,9 @@ void SimplifyVisitor::visit(SetExpr *expr) { stmts.push_back(transform( N(N(N(clone(var), "add"), clone(it))))); } - resultExpr = N(stmts, transform(var)); + auto e = N(stmts, transform(var)); + e->setAttr(ir::SetLiteralAttribute::AttributeName); + resultExpr = e; ctx->popBlock(); } @@ -229,7 +234,9 @@ void SimplifyVisitor::visit(DictExpr *expr) { stmts.push_back(transform(N(N( N(clone(var), "__setitem__"), clone(it.key), clone(it.value))))); } - resultExpr = N(stmts, transform(var)); + auto e = N(stmts, transform(var)); + e->setAttr(ir::DictLiteralAttribute::AttributeName); + resultExpr = e; ctx->popBlock(); } @@ -688,7 +695,9 @@ void SimplifyVisitor::visit(StmtExpr *expr) { for (auto &s : expr->stmts) stmts.emplace_back(transform(s)); auto e = transform(expr->expr); - resultExpr = N(stmts, e); + auto s = N(stmts, e); + s->attributes = expr->attributes; + resultExpr = s; } /**************************************************************************************/ diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index 7e0ba953..3436a316 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -209,6 +209,10 @@ void TranslateVisitor::visit(StmtExpr *expr) { transform(s); ctx->popSeries(); result = make(expr, bodySeries, transform(expr->expr)); + for (auto &a: expr->attributes) { + // if (a == ir::ListLiteralAttribute::AttributeName) + // result->setAttribute(ir::ListLiteralAttribute); + } } /************************************************************************************/ diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index f893af60..7883cfbe 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -10,6 +10,7 @@ #include "codon/parser/common.h" #include "codon/parser/visitors/simplify/simplify.h" #include "codon/parser/visitors/typecheck/typecheck.h" +#include "codon/sir/attribute.h" using fmt::format; @@ -739,14 +740,7 @@ ExprPtr TypecheckVisitor::transformStaticTupleIndex(ClassType *tuple, ExprPtr &e if (!tuple->getRecord()) return nullptr; if (!startswith(tuple->name, TYPE_TUPLE) && !startswith(tuple->name, TYPE_PARTIAL)) - // in(std::set{"Ptr", "pyobj", "str", "Array"}, tuple->name)) - // Ptr, pyobj and str are internal types and have only one overloaded __getitem__ return nullptr; - // if (in(ctx->cache->classes[tuple->name].methods, "__getitem__")) { - // ctx->cache->overloads[ctx->cache->classes[tuple->name].methods["__getitem__"]] - // .size() != 1) - // return nullptr; - // } // Extract a static integer value from a compatible expression. auto getInt = [&](int64_t *o, const ExprPtr &e) { @@ -1121,30 +1115,32 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in N(N(N(clone(var), "__init__"), expr->args))), clone(var))); } - } else if (auto pc = callee->getPartial()) { - ExprPtr var = N(partialVar = ctx->cache->getTemporaryVar("pt")); - expr->expr = transform(N(N(clone(var), expr->expr), - N(pc->func->ast->name))); - calleeFn = expr->expr->type->getFunc(); - for (int i = 0, j = 0; i < pc->known.size(); i++) - if (pc->func->ast->args[i].generic) { - if (pc->known[i]) - unify(calleeFn->funcGenerics[j].type, pc->func->funcGenerics[j].type); - j++; - } - known = pc->known; - seqassert(calleeFn, "not a function: {}", expr->expr->type->toString()); - } else if (!callee->getFunc()) { - // Case 3: callee is not a named function. Route it through a __call__ method. - ExprPtr newCall = N(N(expr->expr, "__call__"), expr->args); - return transform(newCall, false, allowVoidExpr); + } else { + auto pc = callee->getPartial(); + if (pc) { + ExprPtr var = N(partialVar = ctx->cache->getTemporaryVar("pt")); + expr->expr = transform(N(N(clone(var), expr->expr), + N(pc->func->ast->name))); + calleeFn = expr->expr->type->getFunc(); + for (int i = 0, j = 0; i < pc->known.size(); i++) + if (pc->func->ast->args[i].generic) { + if (pc->known[i]) + unify(calleeFn->funcGenerics[j].type, pc->func->funcGenerics[j].type); + j++; + } + known = pc->known; + seqassert(calleeFn, "not a function: {}", expr->expr->type->toString()); + } else if (!callee->getFunc()) { + // Case 3: callee is not a named function. Route it through a __call__ method. + ExprPtr newCall = N(N(expr->expr, "__call__"), expr->args); + return transform(newCall, false, allowVoidExpr); + } } // Handle named and default arguments std::vector args; std::vector typeArgs; int typeArgCount = 0; - // bool isPartial = false; int ellipsisStage = -1; auto newMask = std::vector(calleeFn->ast->args.size(), 1); auto getPartialArg = [&](int pi) { @@ -1383,12 +1379,14 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in N(N(partialTypeName), newArgs)), N(var)); } + const_cast(call->getStmtExpr()) + ->setAttr(ir::PartialFunctionAttribute::AttributeName); call = transform(call, false, allowVoidExpr); - seqassert(call->type->getRecord() && - startswith(call->type->getRecord()->name, partialTypeName) && - !call->type->getPartial(), - "bad partial transformation"); - call->type = N(call->type->getRecord(), calleeFn, newMask); + // seqassert(call->type->getRecord() && + // startswith(call->type->getRecord()->name, partialTypeName) && + // !call->type->getPartial(), + // "bad partial transformation"); + // call->type = N(call->type->getRecord(), calleeFn, newMask); return call; } else { // Case 2. Normal function call. @@ -1636,9 +1634,12 @@ std::string TypecheckVisitor::generatePartialStub(const std::vector &mask, else if (!fn->ast->args[i].generic) tupleSize++; auto typeName = format(TYPE_PARTIAL "{}.{}", strMask, fn->ast->name); - if (!ctx->find(typeName)) + if (!ctx->find(typeName)) { + ctx->cache->partials[typeName] = { + std::static_pointer_cast(fn->shared_from_this()), mask}; // 2 for .starArgs and .kwstarArgs (empty tuples if fn does not have them) generateTupleStub(tupleSize + 2, typeName, {}, false); + } return typeName; } @@ -1710,12 +1711,14 @@ ExprPtr TypecheckVisitor::partializeFunction(ExprPtr expr) { N(N(partialTypeName), N(), N(N(kwName)))), N(var)); + const_cast(call->getStmtExpr()) + ->setAttr(ir::PartialFunctionAttribute::AttributeName); call = transform(call, false, allowVoidExpr); - seqassert(call->type->getRecord() && - startswith(call->type->getRecord()->name, partialTypeName) && - !call->type->getPartial(), - "bad partial transformation"); - call->type = N(call->type->getRecord(), fn, mask); + // seqassert(call->type->getRecord() && + // startswith(call->type->getRecord()->name, partialTypeName) && + // !call->type->getPartial(), + // "bad partial transformation"); + // call->type = N(call->type->getRecord(), fn, mask); return call; } diff --git a/codon/parser/visitors/typecheck/typecheck_stmt.cpp b/codon/parser/visitors/typecheck/typecheck_stmt.cpp index 42ece717..c7aa05e9 100644 --- a/codon/parser/visitors/typecheck/typecheck_stmt.cpp +++ b/codon/parser/visitors/typecheck/typecheck_stmt.cpp @@ -508,6 +508,13 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { else typ = std::make_shared( stmt->name, ctx->cache->reverseIdentifierLookup[stmt->name]); + if (stmt->isRecord() && startswith(stmt->name, TYPE_PARTIAL)) { + seqassert(in(ctx->cache->partials, stmt->name), + "invalid partial initialization: {}", stmt->name); + typ = std::make_shared(typ->getRecord(), + ctx->cache->partials[stmt->name].first, + ctx->cache->partials[stmt->name].second); + } typ->setSrcInfo(stmt->getSrcInfo()); ctx->add(TypecheckItem::Type, stmt->name, typ); ctx->bases[0].visitedAsts[stmt->name] = {TypecheckItem::Type, typ}; From 858cc31ed018f7d9c15a803f7ef29c0ad4738e7a Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Fri, 28 Jan 2022 10:12:42 -0500 Subject: [PATCH 35/61] clang-format --- codon/compiler/compiler.cpp | 3 +-- codon/parser/ast/expr.h | 2 +- codon/parser/visitors/translate/translate.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/codon/compiler/compiler.cpp b/codon/compiler/compiler.cpp index 0a0d3836..9ac9349a 100644 --- a/codon/compiler/compiler.cpp +++ b/codon/compiler/compiler.cpp @@ -9,9 +9,8 @@ #include "codon/parser/visitors/simplify/simplify.h" #include "codon/parser/visitors/translate/translate.h" #include "codon/parser/visitors/typecheck/typecheck.h" -#include "codon/sir/util/operator.h" #include "codon/sir/util/irtools.h" - +#include "codon/sir/util/operator.h" namespace codon { diff --git a/codon/parser/ast/expr.h b/codon/parser/ast/expr.h index 2a9dd015..220d56f7 100644 --- a/codon/parser/ast/expr.h +++ b/codon/parser/ast/expr.h @@ -612,7 +612,7 @@ struct StmtExpr : public Expr { ACCEPT(ASTVisitor); const StmtExpr *getStmtExpr() const override { return this; } - + /// Attribute helpers bool hasAttr(const std::string &attr) const; void setAttr(const std::string &attr); diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index 3436a316..f1346ce1 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -209,7 +209,7 @@ void TranslateVisitor::visit(StmtExpr *expr) { transform(s); ctx->popSeries(); result = make(expr, bodySeries, transform(expr->expr)); - for (auto &a: expr->attributes) { + for (auto &a : expr->attributes) { // if (a == ir::ListLiteralAttribute::AttributeName) // result->setAttribute(ir::ListLiteralAttribute); } From c8c8a0edf779a29c2276c8c82616c21819ae82f0 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Fri, 28 Jan 2022 10:13:34 -0500 Subject: [PATCH 36/61] Add TupleLiteralAttribute --- codon/sir/attribute.cpp | 25 +++++++++++++++++++++++++ codon/sir/attribute.h | 17 +++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/codon/sir/attribute.cpp b/codon/sir/attribute.cpp index cec29267..d131c993 100644 --- a/codon/sir/attribute.cpp +++ b/codon/sir/attribute.cpp @@ -39,6 +39,31 @@ std::ostream &MemberAttribute::doFormat(std::ostream &os) const { const std::string SrcInfoAttribute::AttributeName = "srcInfoAttribute"; +const std::string TupleLiteralAttribute::AttributeName = "tupleLiteralAttribute"; + +std::unique_ptr TupleLiteralAttribute::clone(util::CloneVisitor &cv) const { + std::vector elementsCloned; + for (auto *val : elements) + elementsCloned.push_back(cv.clone(val)); + return std::make_unique(elementsCloned); +} + +std::unique_ptr +TupleLiteralAttribute::forceClone(util::CloneVisitor &cv) const { + std::vector elementsCloned; + for (auto *val : elements) + elementsCloned.push_back(cv.forceClone(val)); + return std::make_unique(elementsCloned); +} + +std::ostream &TupleLiteralAttribute::doFormat(std::ostream &os) const { + std::vector strings; + for (auto *val : elements) + strings.push_back(fmt::format(FMT_STRING("{}"), *val)); + fmt::print(os, FMT_STRING("({})"), fmt::join(strings.begin(), strings.end(), ",")); + return os; +} + const std::string ListLiteralAttribute::AttributeName = "listLiteralAttribute"; std::unique_ptr ListLiteralAttribute::clone(util::CloneVisitor &cv) const { diff --git a/codon/sir/attribute.h b/codon/sir/attribute.h index 9c4c0429..cf34d072 100644 --- a/codon/sir/attribute.h +++ b/codon/sir/attribute.h @@ -115,6 +115,23 @@ private: std::ostream &doFormat(std::ostream &os) const override; }; +/// Attribute attached to IR structures corresponding to tuple literals +struct TupleLiteralAttribute : public Attribute { + static const std::string AttributeName; + + /// values contained in tuple literal + std::vector elements; + + explicit TupleLiteralAttribute(std::vector elements) + : elements(std::move(elements)) {} + + std::unique_ptr clone(util::CloneVisitor &cv) const override; + std::unique_ptr forceClone(util::CloneVisitor &cv) const override; + +private: + std::ostream &doFormat(std::ostream &os) const override; +}; + /// Attribute attached to IR structures corresponding to list literals struct ListLiteralAttribute : public Attribute { static const std::string AttributeName; From 69552b15553d70fc870bea96660ce28296569a64 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Fri, 28 Jan 2022 15:07:11 -0500 Subject: [PATCH 37/61] Remove dummy op --- codon/compiler/compiler.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/codon/compiler/compiler.cpp b/codon/compiler/compiler.cpp index 9ac9349a..cbb62c07 100644 --- a/codon/compiler/compiler.cpp +++ b/codon/compiler/compiler.cpp @@ -112,22 +112,9 @@ Compiler::parseCode(const std::string &file, const std::string &code, int startL const std::unordered_map &defines) { return parse(/*isCode=*/true, file, code, startLine, testFlags, defines); } -struct DummyOp : public codon::ir::util::Operator { - void handle(codon::ir::CallInstr *x) override { - auto *M = x->getModule(); - auto *func = codon::ir::util::getFunc(x->getCallee()); - if (!func || func->getUnmangledName() != "foo") - return; - auto fn = M->getOrRealizeFunc("bar", {x->front()->getType()}, {}); - seqassert(fn, "did not succeed"); - auto result = codon::ir::util::call(fn, {x->front()}); - x->replaceAll(result); - } -}; + llvm::Error Compiler::compile() { pm->run(module.get()); - auto d = DummyOp(); - module->accept(d); llvisitor->visit(module.get()); return llvm::Error::success(); } From 80480f87d59ef30c1568749b91bcb5589f12e573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Sun, 30 Jan 2022 10:42:57 -0800 Subject: [PATCH 38/61] Fix partialization [wip] --- codon/compiler/compiler.cpp | 2 - codon/parser/ast/stmt.cpp | 1 - codon/parser/ast/types.cpp | 8 +- codon/parser/visitors/typecheck/typecheck.cpp | 5 ++ .../visitors/typecheck/typecheck_expr.cpp | 76 ++++++++++--------- .../visitors/typecheck/typecheck_infer.cpp | 17 +++-- .../visitors/typecheck/typecheck_stmt.cpp | 7 -- 7 files changed, 62 insertions(+), 54 deletions(-) diff --git a/codon/compiler/compiler.cpp b/codon/compiler/compiler.cpp index cbb62c07..2c920274 100644 --- a/codon/compiler/compiler.cpp +++ b/codon/compiler/compiler.cpp @@ -9,8 +9,6 @@ #include "codon/parser/visitors/simplify/simplify.h" #include "codon/parser/visitors/translate/translate.h" #include "codon/parser/visitors/typecheck/typecheck.h" -#include "codon/sir/util/irtools.h" -#include "codon/sir/util/operator.h" namespace codon { diff --git a/codon/parser/ast/stmt.cpp b/codon/parser/ast/stmt.cpp index e3440b83..86f0ad64 100644 --- a/codon/parser/ast/stmt.cpp +++ b/codon/parser/ast/stmt.cpp @@ -42,7 +42,6 @@ std::string SuiteStmt::toString(int indent) const { } ACCEPT_IMPL(SuiteStmt, ASTVisitor); void SuiteStmt::flatten(StmtPtr s, std::vector &stmts) { - // WARNING: does not preserve attributes! if (!s) return; auto suite = const_cast(s->getSuite()); diff --git a/codon/parser/ast/types.cpp b/codon/parser/ast/types.cpp index b3b2134a..0371c7fc 100644 --- a/codon/parser/ast/types.cpp +++ b/codon/parser/ast/types.cpp @@ -551,13 +551,17 @@ int PartialType::unify(Type *typ, Unification *us) { TypePtr PartialType::generalize(int atLevel) { return std::make_shared( std::static_pointer_cast(this->RecordType::generalize(atLevel)), func, + /*func->generalize(0)->getFunc(), */ known); } TypePtr PartialType::instantiate(int atLevel, int *unboundCount, std::unordered_map *cache) { + auto rec = std::static_pointer_cast( + this->RecordType::instantiate(atLevel, unboundCount, cache)); + // Do not track function unbounds! + // auto tempCache = cache ? *cache : std::unordered_map(); return std::make_shared( - std::static_pointer_cast( - this->RecordType::instantiate(atLevel, unboundCount, cache)), + rec, // func->instantiate(atLevel, unboundCount, &tempCache)->getFunc(), func, known); } std::string PartialType::debugString(bool debug) const { diff --git a/codon/parser/visitors/typecheck/typecheck.cpp b/codon/parser/visitors/typecheck/typecheck.cpp index 77dc0ba0..2f0555dd 100644 --- a/codon/parser/visitors/typecheck/typecheck.cpp +++ b/codon/parser/visitors/typecheck/typecheck.cpp @@ -48,6 +48,11 @@ TypePtr TypecheckVisitor::unify(TypePtr &a, const TypePtr &b, bool undoOnSuccess // LOG("{} / {}", a->debugString(true), b->debugString(true)); if (!undoOnSuccess) a->unify(b.get(), &undo); + // if (format("cannot unify {} and {}", a->toString(), b->toString()) == + // "cannot unify ._lambda_82:0[...] and T2") { + // LOG("cannot unify {} and {}", a->debugString(1), b->debugString(1)); + // a->unify(b.get(), &undo); + // } error("cannot unify {} and {}", a->toString(), b->toString()); return nullptr; } diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 899dc4cc..4b53320e 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1117,26 +1117,23 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in N(N(N(clone(var), "__init__"), expr->args))), clone(var))); } - } else { - auto pc = callee->getPartial(); - if (pc) { - ExprPtr var = N(partialVar = ctx->cache->getTemporaryVar("pt")); - expr->expr = transform(N(N(clone(var), expr->expr), - N(pc->func->ast->name))); - calleeFn = expr->expr->type->getFunc(); - for (int i = 0, j = 0; i < pc->known.size(); i++) - if (pc->func->ast->args[i].generic) { - if (pc->known[i]) - unify(calleeFn->funcGenerics[j].type, pc->func->funcGenerics[j].type); - j++; - } - known = pc->known; - seqassert(calleeFn, "not a function: {}", expr->expr->type->toString()); - } else if (!callee->getFunc()) { - // Case 3: callee is not a named function. Route it through a __call__ method. - ExprPtr newCall = N(N(expr->expr, "__call__"), expr->args); - return transform(newCall, false, allowVoidExpr); - } + } else if (auto pc = callee->getPartial()) { + ExprPtr var = N(partialVar = ctx->cache->getTemporaryVar("pt")); + expr->expr = transform(N(N(clone(var), expr->expr), + N(pc->func->ast->name))); + calleeFn = expr->expr->type->getFunc(); + for (int i = 0, j = 0; i < pc->known.size(); i++) + if (pc->func->ast->args[i].generic) { + if (pc->known[i]) + unify(calleeFn->funcGenerics[j].type, pc->func->funcGenerics[j].type); + j++; + } + known = pc->known; + seqassert(calleeFn, "not a function: {}", expr->expr->type->toString()); + } else if (!callee->getFunc()) { + // Case 3: callee is not a named function. Route it through a __call__ method. + ExprPtr newCall = N(N(expr->expr, "__call__"), expr->args); + return transform(newCall, false, allowVoidExpr); } // Handle named and default arguments @@ -1384,16 +1381,27 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in const_cast(call->getStmtExpr()) ->setAttr(ir::PartialFunctionAttribute::AttributeName); call = transform(call, false, allowVoidExpr); - // seqassert(call->type->getRecord() && - // startswith(call->type->getRecord()->name, partialTypeName) && - // !call->type->getPartial(), - // "bad partial transformation"); - // call->type = N(call->type->getRecord(), calleeFn, newMask); + seqassert(call->type->getPartial(), "expected partial type"); return call; } else { // Case 2. Normal function call. expr->args = args; unify(expr->type, calleeFn->args[0]); // function return type + + // HACK: Intercept Partial.__new__ and replace it with partial type + // TODO: needs cleaner logic for this. Maybe just use normal record type + // and just track partialized function args, not the whole function as it's done now. + // Major caveat: needs rewiring of the function generic partialization logic. + if (startswith(calleeFn->ast->name, TYPE_PARTIAL) && + endswith(calleeFn->ast->name, ".__new__:0")) { + seqassert(expr->type->getRecord(), "expected a partial record"); + LOG("partial constructor"); + auto r = expr->type->getRecord(); + // TODO: maybe + expr->type = std::make_shared(r, ctx->cache->partials[r->name].first, + ctx->cache->partials[r->name].second); + } + LOG("-- {} :: {}", ctx->getBase(), calleeFn->debugString(1)); return nullptr; } } @@ -1629,19 +1637,20 @@ std::string TypecheckVisitor::generateFunctionStub(int n) { std::string TypecheckVisitor::generatePartialStub(const std::vector &mask, types::FuncType *fn) { std::string strMask(mask.size(), '1'); - int tupleSize = 0; + int tupleSize = 0, genericSize = 0; for (int i = 0; i < mask.size(); i++) if (!mask[i]) strMask[i] = '0'; else if (!fn->ast->args[i].generic) tupleSize++; - auto typeName = format(TYPE_PARTIAL "{}.{}", strMask, fn->ast->name); + else + genericSize++; + auto typeName = format(TYPE_PARTIAL "{}.{}", strMask, fn->toString()); if (!ctx->find(typeName)) { - ctx->cache->partials[typeName] = { - std::static_pointer_cast(fn->shared_from_this()), mask}; - // 2 for .starArgs and .kwstarArgs (empty tuples if fn does not have them) + ctx->cache->partials[typeName] = {fn->generalize(0)->getFunc(), mask}; generateTupleStub(tupleSize + 2, typeName, {}, false); } + LOG("[p] {} -> {}", typeName, ctx->cache->partials[typeName].first->debugString(1)); return typeName; } @@ -1716,11 +1725,8 @@ ExprPtr TypecheckVisitor::partializeFunction(ExprPtr expr) { const_cast(call->getStmtExpr()) ->setAttr(ir::PartialFunctionAttribute::AttributeName); call = transform(call, false, allowVoidExpr); - // seqassert(call->type->getRecord() && - // startswith(call->type->getRecord()->name, partialTypeName) && - // !call->type->getPartial(), - // "bad partial transformation"); - // call->type = N(call->type->getRecord(), fn, mask); + LOG("-- {} / {}", ctx->getBase(), call->type->debugString(1)); + seqassert(call->type->getPartial(), "expected partial type"); return call; } diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index f4c2a10f..26f219eb 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -35,9 +35,8 @@ types::TypePtr TypecheckVisitor::realize(types::TypePtr typ) { if (auto rt = realize(p->func)) unify(rt, p->func); return std::make_shared(t->getRecord(), p->func, p->known); - } else { - return t; } + return t; } else { return nullptr; } @@ -117,8 +116,9 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { try { auto it = ctx->cache->functions[type->ast->name].realizations.find(type->realizedName()); - if (it != ctx->cache->functions[type->ast->name].realizations.end()) + if (it != ctx->cache->functions[type->ast->name].realizations.end()) { return it->second->type; + } // Set up bases. Ensure that we have proper parent bases even during a realization // of mutually recursive functions. @@ -156,8 +156,11 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { ctx->typecheckLevel++; // Find parents! - ctx->bases.push_back({type->ast->name, type->getFunc(), type->args[0], - {}, findSuperMethods(type->getFunc())}); + ctx->bases.push_back({type->ast->name, + type->getFunc(), + type->args[0], + {}, + findSuperMethods(type->getFunc())}); auto clonedAst = ctx->cache->functions[type->ast->name].ast->clone(); auto *ast = (FunctionStmt *)clonedAst.get(); addFunctionGenerics(type); @@ -404,9 +407,9 @@ std::pair TypecheckVisitor::inferTypes(StmtPtr result, bool keepLa ir::types::Type *TypecheckVisitor::getLLVMType(const types::ClassType *t) { auto realizedName = t->realizedTypeName(); if (!in(ctx->cache->classes[t->name].realizations, realizedName)) - realizeType(const_cast(t)); + realizeType(const_cast(t)); if (auto l = ctx->cache->classes[t->name].realizations[realizedName]->ir) - return l; + return l; auto getLLVM = [&](const TypePtr &tt) { auto t = tt->getClass(); seqassert(t && in(ctx->cache->classes[t->name].realizations, t->realizedTypeName()), diff --git a/codon/parser/visitors/typecheck/typecheck_stmt.cpp b/codon/parser/visitors/typecheck/typecheck_stmt.cpp index c7aa05e9..42ece717 100644 --- a/codon/parser/visitors/typecheck/typecheck_stmt.cpp +++ b/codon/parser/visitors/typecheck/typecheck_stmt.cpp @@ -508,13 +508,6 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { else typ = std::make_shared( stmt->name, ctx->cache->reverseIdentifierLookup[stmt->name]); - if (stmt->isRecord() && startswith(stmt->name, TYPE_PARTIAL)) { - seqassert(in(ctx->cache->partials, stmt->name), - "invalid partial initialization: {}", stmt->name); - typ = std::make_shared(typ->getRecord(), - ctx->cache->partials[stmt->name].first, - ctx->cache->partials[stmt->name].second); - } typ->setSrcInfo(stmt->getSrcInfo()); ctx->add(TypecheckItem::Type, stmt->name, typ); ctx->bases[0].visitedAsts[stmt->name] = {TypecheckItem::Type, typ}; From 363caec2ac8ddc6f3ac1157b43bc57b6a5f98e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Sun, 30 Jan 2022 18:32:31 -0800 Subject: [PATCH 39/61] Fix partialization --- codon/parser/ast/types.cpp | 40 +++++++++++-------- codon/parser/ast/types.h | 3 ++ codon/parser/visitors/typecheck/typecheck.cpp | 1 + .../visitors/typecheck/typecheck_expr.cpp | 18 ++++----- .../visitors/typecheck/typecheck_infer.cpp | 4 +- 5 files changed, 37 insertions(+), 29 deletions(-) diff --git a/codon/parser/ast/types.cpp b/codon/parser/ast/types.cpp index 0371c7fc..7a3244a0 100644 --- a/codon/parser/ast/types.cpp +++ b/codon/parser/ast/types.cpp @@ -539,30 +539,25 @@ PartialType::PartialType(const std::shared_ptr &baseType, : RecordType(*baseType), func(move(func)), known(move(known)) {} int PartialType::unify(Type *typ, Unification *us) { int s1 = 0, s; - if (auto tc = typ->getPartial()) { - // Check names. - if ((s = func->unify(tc->func.get(), us)) == -1) - return -1; - s1 += s; - } + // if (auto tc = typ->getPartial()) { + // // Check names. + // if ((s = func->unify(tc->func.get(), us)) == -1) + // return -1; + // s1 += s; + // } s = this->RecordType::unify(typ, us); return s == -1 ? s : s1 + s; } TypePtr PartialType::generalize(int atLevel) { return std::make_shared( std::static_pointer_cast(this->RecordType::generalize(atLevel)), func, - /*func->generalize(0)->getFunc(), */ known); } TypePtr PartialType::instantiate(int atLevel, int *unboundCount, std::unordered_map *cache) { auto rec = std::static_pointer_cast( this->RecordType::instantiate(atLevel, unboundCount, cache)); - // Do not track function unbounds! - // auto tempCache = cache ? *cache : std::unordered_map(); - return std::make_shared( - rec, // func->instantiate(atLevel, unboundCount, &tempCache)->getFunc(), - func, known); + return std::make_shared(rec, func, known); } std::string PartialType::debugString(bool debug) const { std::vector gs; @@ -583,7 +578,7 @@ std::string PartialType::debugString(bool debug) const { } std::string PartialType::realizedName() const { std::vector gs; - gs.push_back(func->realizedName()); + // gs.push_back(func->realizedName()); for (auto &a : generics) if (!a.name.empty()) gs.push_back(a.type->realizedName()); @@ -765,12 +760,23 @@ int CallableTrait::unify(Type *typ, Unification *us) { zeros.emplace_back(pi - 9); if (zeros.size() + 1 != args.size()) return -1; - if (args[0]->unify(pt->func->args[0].get(), us) == -1) - return -1; + + int ic = 0; + std::unordered_map c; + auto pf = pt->func->instantiate(0, &ic, &c)->getFunc(); + // For partial functions, we just check can we unify without actually performing + // unification for (int pi = 0, gi = 1; pi < pt->known.size(); pi++) - if (!pt->known[pi] && !pt->func->ast->args[pi].generic) - if (args[gi++]->unify(pt->func->args[pi + 1].get(), us) == -1) + if (!pt->known[pi] && !pf->ast->args[pi].generic) + if (args[gi++]->unify(pf->args[pi + 1].get(), us) == -1) return -1; + if (us && us->realizator && pf->canRealize()) { + // Realize if possible to allow deduction of return type [and possible unification!] + auto rf = us->realizator->realize(pf); + pf->unify(rf.get(), us); + } + if (args[0]->unify(pf->args[0].get(), us) == -1) + return -1; return 1; } } else if (auto tl = typ->getLink()) { diff --git a/codon/parser/ast/types.h b/codon/parser/ast/types.h index 065e21b0..08c6e6f9 100644 --- a/codon/parser/ast/types.h +++ b/codon/parser/ast/types.h @@ -14,6 +14,7 @@ struct Expr; struct StaticValue; struct FunctionStmt; struct TypeContext; +class TypecheckVisitor; namespace types { @@ -43,6 +44,8 @@ struct Type : public codon::SrcObject, public std::enable_shared_from_this std::vector> leveled; /// List of assigned traits. std::vector traits; + /// Pointer to a TypecheckVisitor to support realization function types. + TypecheckVisitor *realizator = nullptr; public: /// Undo the unification step. diff --git a/codon/parser/visitors/typecheck/typecheck.cpp b/codon/parser/visitors/typecheck/typecheck.cpp index 2f0555dd..16a6dda7 100644 --- a/codon/parser/visitors/typecheck/typecheck.cpp +++ b/codon/parser/visitors/typecheck/typecheck.cpp @@ -38,6 +38,7 @@ TypePtr TypecheckVisitor::unify(TypePtr &a, const TypePtr &b, bool undoOnSuccess return a = b; seqassert(b, "rhs is nullptr"); types::Type::Unification undo; + undo.realizator = this; if (a->unify(b.get(), &undo) >= 0) { if (undoOnSuccess) undo.undo(); diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 4b53320e..683ac510 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1122,10 +1122,12 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in expr->expr = transform(N(N(clone(var), expr->expr), N(pc->func->ast->name))); calleeFn = expr->expr->type->getFunc(); + // Fill in generics for (int i = 0, j = 0; i < pc->known.size(); i++) if (pc->func->ast->args[i].generic) { if (pc->known[i]) - unify(calleeFn->funcGenerics[j].type, pc->func->funcGenerics[j].type); + unify(calleeFn->funcGenerics[j].type, + ctx->instantiate(expr, pc->func->funcGenerics[j].type)); j++; } known = pc->known; @@ -1340,8 +1342,6 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in if (replacements[si]) { if (replacements[si]->getFunc()) deactivateUnbounds(replacements[si].get()); - if (auto pt = replacements[si]->getPartial()) - deactivateUnbounds(pt->func.get()); calleeFn->generics[si + 1].type = calleeFn->args[si + 1] = replacements[si]; } if (!isPartial) { @@ -1390,18 +1390,15 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in // HACK: Intercept Partial.__new__ and replace it with partial type // TODO: needs cleaner logic for this. Maybe just use normal record type - // and just track partialized function args, not the whole function as it's done now. - // Major caveat: needs rewiring of the function generic partialization logic. + // and just track partialized function args, not the whole function as it's done + // now. Major caveat: needs rewiring of the function generic partialization logic. if (startswith(calleeFn->ast->name, TYPE_PARTIAL) && endswith(calleeFn->ast->name, ".__new__:0")) { seqassert(expr->type->getRecord(), "expected a partial record"); - LOG("partial constructor"); auto r = expr->type->getRecord(); - // TODO: maybe expr->type = std::make_shared(r, ctx->cache->partials[r->name].first, ctx->cache->partials[r->name].second); } - LOG("-- {} :: {}", ctx->getBase(), calleeFn->debugString(1)); return nullptr; } } @@ -1650,7 +1647,7 @@ std::string TypecheckVisitor::generatePartialStub(const std::vector &mask, ctx->cache->partials[typeName] = {fn->generalize(0)->getFunc(), mask}; generateTupleStub(tupleSize + 2, typeName, {}, false); } - LOG("[p] {} -> {}", typeName, ctx->cache->partials[typeName].first->debugString(1)); + // LOG("[p] {} -> {}", typeName, ctx->cache->partials[typeName].first->debugString(1)); return typeName; } @@ -1725,7 +1722,6 @@ ExprPtr TypecheckVisitor::partializeFunction(ExprPtr expr) { const_cast(call->getStmtExpr()) ->setAttr(ir::PartialFunctionAttribute::AttributeName); call = transform(call, false, allowVoidExpr); - LOG("-- {} / {}", ctx->getBase(), call->type->debugString(1)); seqassert(call->type->getPartial(), "expected partial type"); return call; } @@ -1872,6 +1868,8 @@ bool TypecheckVisitor::wrapExpr(ExprPtr &expr, TypePtr expectedType, // Case 7: wrap raw Seq functions into Partial(...) call for easy realization. expr = partializeFunction(expr); } + + // Special case: unify(expr->type, expectedType, undoOnSuccess); return true; } diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index 26f219eb..bf804274 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -32,8 +32,8 @@ types::TypePtr TypecheckVisitor::realize(types::TypePtr typ) { } else if (auto c = typ->getClass()) { auto t = realizeType(c.get()); if (auto p = typ->getPartial()) { - if (auto rt = realize(p->func)) - unify(rt, p->func); + // if (auto rt = realize(p->func)) + // unify(rt, p->func); return std::make_shared(t->getRecord(), p->func, p->known); } return t; From bbdd51cf9e2b3313e26f367d409b454ab3eb87bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Mon, 31 Jan 2022 10:33:32 -0800 Subject: [PATCH 40/61] Fix ctx->age in realizeFunction --- codon/parser/cache.cpp | 8 ++++++-- .../visitors/typecheck/typecheck_expr.cpp | 17 +++++++++-------- .../visitors/typecheck/typecheck_stmt.cpp | 7 +++++++ codon/sir/module.cpp | 3 ++- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/codon/parser/cache.cpp b/codon/parser/cache.cpp index e94ca102..1af811eb 100644 --- a/codon/parser/cache.cpp +++ b/codon/parser/cache.cpp @@ -111,14 +111,18 @@ ir::Func *Cache::realizeFunction(types::FuncTypePtr type, } } } + int oldAge = typeCtx->age; + typeCtx->age = 99999; auto tv = TypecheckVisitor(typeCtx); + ir::Func *f = nullptr; if (auto rtv = tv.realize(type)) { auto pr = pendingRealizations; // copy it as it might be modified for (auto &fn : pr) TranslateVisitor(codegenCtx).transform(functions[fn.first].ast->clone()); - return functions[rtv->getFunc()->ast->name].realizations[rtv->realizedName()]->ir; + f = functions[rtv->getFunc()->ast->name].realizations[rtv->realizedName()]->ir; } - return nullptr; + typeCtx->age = oldAge; + return f; } ir::types::Type *Cache::makeTuple(const std::vector &types) { diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 683ac510..7625dc38 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -867,7 +867,8 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, N(N(expr->expr, "_getattr"), N(expr->member))); } else { // For debugging purposes: - ctx->findMethod(typ->name, expr->member); + if (expr->member == "ticker") + ctx->findMethod(typ->name, expr->member); error("cannot find '{}' in {}", expr->member, typ->toString()); } } @@ -1392,13 +1393,13 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in // TODO: needs cleaner logic for this. Maybe just use normal record type // and just track partialized function args, not the whole function as it's done // now. Major caveat: needs rewiring of the function generic partialization logic. - if (startswith(calleeFn->ast->name, TYPE_PARTIAL) && - endswith(calleeFn->ast->name, ".__new__:0")) { - seqassert(expr->type->getRecord(), "expected a partial record"); - auto r = expr->type->getRecord(); - expr->type = std::make_shared(r, ctx->cache->partials[r->name].first, - ctx->cache->partials[r->name].second); - } + // if (startswith(calleeFn->ast->name, TYPE_PARTIAL) && + // endswith(calleeFn->ast->name, ".__new__:0")) { + // seqassert(expr->type->getRecord(), "expected a partial record"); + // auto r = expr->type->getRecord(); + // expr->type = std::make_shared(r, ctx->cache->partials[r->name].first, + // ctx->cache->partials[r->name].second); + // } return nullptr; } } diff --git a/codon/parser/visitors/typecheck/typecheck_stmt.cpp b/codon/parser/visitors/typecheck/typecheck_stmt.cpp index 42ece717..c7aa05e9 100644 --- a/codon/parser/visitors/typecheck/typecheck_stmt.cpp +++ b/codon/parser/visitors/typecheck/typecheck_stmt.cpp @@ -508,6 +508,13 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { else typ = std::make_shared( stmt->name, ctx->cache->reverseIdentifierLookup[stmt->name]); + if (stmt->isRecord() && startswith(stmt->name, TYPE_PARTIAL)) { + seqassert(in(ctx->cache->partials, stmt->name), + "invalid partial initialization: {}", stmt->name); + typ = std::make_shared(typ->getRecord(), + ctx->cache->partials[stmt->name].first, + ctx->cache->partials[stmt->name].second); + } typ->setSrcInfo(stmt->getSrcInfo()); ctx->add(TypecheckItem::Type, stmt->name, typ); ctx->bases[0].visitedAsts[stmt->name] = {TypecheckItem::Type, typ}; diff --git a/codon/sir/module.cpp b/codon/sir/module.cpp index e01e8808..b5036f9a 100644 --- a/codon/sir/module.cpp +++ b/codon/sir/module.cpp @@ -142,7 +142,8 @@ Func *Module::getOrRealizeFunc(const std::string &funcName, try { return cache->realizeFunction(func, arg, gens); } catch (const exc::ParserException &e) { - LOG_IR("getOrRealizeFunc parser error: {}", e.what()); + for (int i = 0; i < e.messages.size(); i++) + LOG_IR("getOrRealizeFunc parser error at {}: {}", e.locations[i], e.messages[i]); return nullptr; } } From 2bf3709d335ebaa77baffd56fe035eac72a65c29 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Mon, 31 Jan 2022 18:38:59 -0500 Subject: [PATCH 41/61] Fix Asan flags --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa87c24c..809fc806 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,8 +74,8 @@ else() -Wl,--no-whole-archive) endif() if(ASAN) - target_compile_options(codonrt PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address") - target_link_libraries(codonrt PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address") + target_compile_options(codonrt PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address" "-fsanitize-recover=address") + target_link_libraries(codonrt PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address" "-fsanitize-recover=address") endif() add_custom_command(TARGET codonrt POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${CMAKE_BINARY_DIR}) @@ -297,8 +297,8 @@ if(CODON_JUPYTER) target_link_libraries(codonc PRIVATE xeus-static) endif() if(ASAN) - target_compile_options(codonc PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address") - target_link_libraries(codonc PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address") + target_compile_options(codonc PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address" "-fsanitize-recover=address") + target_link_libraries(codonc PRIVATE "-fno-omit-frame-pointer" "-fsanitize=address" "-fsanitize-recover=address") endif() if(CMAKE_BUILD_TYPE MATCHES Debug) set_source_files_properties(codon_rules.cpp codon/parser/peg/peg.cpp PROPERTIES COMPILE_FLAGS "-O2") From f0a21fa3ef20a7dc67a5b27bf36b6ce2e3ca2005 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Tue, 1 Feb 2022 11:15:22 -0500 Subject: [PATCH 42/61] clang-format --- codon/parser/ast/types.cpp | 3 ++- codon/parser/visitors/simplify/simplify_ctx.cpp | 4 ++-- codon/parser/visitors/simplify/simplify_stmt.cpp | 3 +-- codon/parser/visitors/typecheck/typecheck_expr.cpp | 6 ++++-- codon/parser/visitors/typecheck/typecheck_infer.cpp | 4 ++-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/codon/parser/ast/types.cpp b/codon/parser/ast/types.cpp index 7a3244a0..09fc633c 100644 --- a/codon/parser/ast/types.cpp +++ b/codon/parser/ast/types.cpp @@ -771,7 +771,8 @@ int CallableTrait::unify(Type *typ, Unification *us) { if (args[gi++]->unify(pf->args[pi + 1].get(), us) == -1) return -1; if (us && us->realizator && pf->canRealize()) { - // Realize if possible to allow deduction of return type [and possible unification!] + // Realize if possible to allow deduction of return type [and possible + // unification!] auto rf = us->realizator->realize(pf); pf->unify(rf.get(), us); } diff --git a/codon/parser/visitors/simplify/simplify_ctx.cpp b/codon/parser/visitors/simplify/simplify_ctx.cpp index 7587a6c2..0ddf3fda 100644 --- a/codon/parser/visitors/simplify/simplify_ctx.cpp +++ b/codon/parser/visitors/simplify/simplify_ctx.cpp @@ -24,8 +24,8 @@ SimplifyContext::SimplifyContext(std::string filename, Cache *cache) allowTypeOf(true), substitutions(nullptr) {} SimplifyContext::Base::Base(std::string name, std::shared_ptr ast, int attributes) - : name(move(name)), ast(move(ast)), attributes(attributes), - deducedMembers(nullptr), selfName() {} + : name(move(name)), ast(move(ast)), attributes(attributes), deducedMembers(nullptr), + selfName() {} std::shared_ptr SimplifyContext::add(SimplifyItem::Kind kind, const std::string &name, diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 7fef1ad6..d1e420ce 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -569,8 +569,7 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { if (typeAst && (typeAst->isId("type") || typeAst->isId("TypeVar")) && deflt && deflt->getNone()) deflt = N("NoneType"); - args.emplace_back( - Param{std::string(stars, '*') + name, typeAst, deflt, a.generic}); + args.emplace_back(Param{std::string(stars, '*') + name, typeAst, deflt, a.generic}); if (a.generic) { if (a.type->getIndex() && a.type->getIndex()->expr->isId("Static")) ctx->add(SimplifyItem::Var, varName, name); diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 7625dc38..bf32a655 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1397,7 +1397,8 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in // endswith(calleeFn->ast->name, ".__new__:0")) { // seqassert(expr->type->getRecord(), "expected a partial record"); // auto r = expr->type->getRecord(); - // expr->type = std::make_shared(r, ctx->cache->partials[r->name].first, + // expr->type = std::make_shared(r, + // ctx->cache->partials[r->name].first, // ctx->cache->partials[r->name].second); // } return nullptr; @@ -1648,7 +1649,8 @@ std::string TypecheckVisitor::generatePartialStub(const std::vector &mask, ctx->cache->partials[typeName] = {fn->generalize(0)->getFunc(), mask}; generateTupleStub(tupleSize + 2, typeName, {}, false); } - // LOG("[p] {} -> {}", typeName, ctx->cache->partials[typeName].first->debugString(1)); + // LOG("[p] {} -> {}", typeName, + // ctx->cache->partials[typeName].first->debugString(1)); return typeName; } diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index bf804274..b970dd40 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -32,8 +32,8 @@ types::TypePtr TypecheckVisitor::realize(types::TypePtr typ) { } else if (auto c = typ->getClass()) { auto t = realizeType(c.get()); if (auto p = typ->getPartial()) { - // if (auto rt = realize(p->func)) - // unify(rt, p->func); + // if (auto rt = realize(p->func)) + // unify(rt, p->func); return std::make_shared(t->getRecord(), p->func, p->known); } return t; From 6b4f29a3f2f1e12fed72075443b70eff39fdc63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Tue, 1 Feb 2022 12:39:50 -0800 Subject: [PATCH 43/61] Fix memory leaks --- codon/parser/ast/types.cpp | 6 +++--- codon/parser/ast/types.h | 6 +++--- codon/parser/visitors/translate/translate.cpp | 4 ++-- codon/parser/visitors/typecheck/typecheck_infer.cpp | 2 ++ codon/sir/llvm/llvisitor.cpp | 2 ++ 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/codon/parser/ast/types.cpp b/codon/parser/ast/types.cpp index 7a3244a0..4e52b0c6 100644 --- a/codon/parser/ast/types.cpp +++ b/codon/parser/ast/types.cpp @@ -100,7 +100,7 @@ int LinkType::unify(Type *typ, Unification *undo) { if (undo) { LOG_TYPECHECK("[unify] {} := {}", id, typ->debugString(true)); // Link current type to typ and ensure that this modification is recorded in undo. - undo->linked.push_back(this); + undo->linked.push_back(getLink()); kind = Link; seqassert(!typ->getLink() || typ->getLink()->kind != Unbound || typ->getLink()->id <= id, @@ -108,7 +108,7 @@ int LinkType::unify(Type *typ, Unification *undo) { type = typ->follow(); if (auto t = type->getLink()) if (trait && t->kind == Unbound && !t->trait) { - undo->traits.push_back(t.get()); + undo->traits.push_back(t->getLink()); t->trait = trait; } } @@ -205,7 +205,7 @@ bool LinkType::occurs(Type *typ, Type::Unification *undo) { if (tl->trait && occurs(tl->trait.get(), undo)) return true; if (undo && tl->level > level) { - undo->leveled.emplace_back(make_pair(tl.get(), tl->level)); + undo->leveled.emplace_back(make_pair(tl, tl->level)); tl->level = level; } return false; diff --git a/codon/parser/ast/types.h b/codon/parser/ast/types.h index 08c6e6f9..465c93eb 100644 --- a/codon/parser/ast/types.h +++ b/codon/parser/ast/types.h @@ -39,11 +39,11 @@ struct Type : public codon::SrcObject, public std::enable_shared_from_this /// Needed because the unify() is destructive. struct Unification { /// List of unbound types that have been changed. - std::vector linked; + std::vector> linked; /// List of unbound types whose level has been changed. - std::vector> leveled; + std::vector, int>> leveled; /// List of assigned traits. - std::vector traits; + std::vector> traits; /// Pointer to a TypecheckVisitor to support realization function types. TypecheckVisitor *realizator = nullptr; diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index f1346ce1..33779418 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -438,7 +438,7 @@ void TranslateVisitor::transformFunction(types::FuncType *type, FunctionStmt *as func->setAttribute(std::make_unique(attr)); for (int i = 0; i < names.size(); i++) func->getArgVar(names[i])->setSrcInfo(ast->args[indices[i]].getSrcInfo()); - func->setUnmangledName(ctx->cache->reverseIdentifierLookup[type->ast->name]); + // func->setUnmangledName(ctx->cache->reverseIdentifierLookup[type->ast->name]); if (!ast->attributes.has(Attr::C) && !ast->attributes.has(Attr::Internal)) { ctx->addBlock(); for (auto i = 0; i < names.size(); i++) @@ -515,7 +515,7 @@ void TranslateVisitor::transformLLVMFunction(types::FuncType *type, FunctionStmt f->setLLVMBody(join(lines, "\n")); f->setLLVMDeclarations(declare); f->setLLVMLiterals(literals); - func->setUnmangledName(ctx->cache->reverseIdentifierLookup[type->ast->name]); + // func->setUnmangledName(ctx->cache->reverseIdentifierLookup[type->ast->name]); } } // namespace ast diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index bf804274..95e50d2d 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -237,6 +237,7 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { } else { r->ir = ctx->cache->module->Nr(type->realizedName()); } + r->ir->setUnmangledName(ctx->cache->reverseIdentifierLookup[type->ast->name]); auto parent = type->funcParent; if (!ast->attributes.parentClass.empty() && @@ -284,6 +285,7 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { irType->setAstType(type->getFunc()); r->ir->realize(irType, names); + // LOG("-> {}", *(r->ir)); ctx->cache->functions[type->ast->name].realizations[type->realizedName()] = r; } else { ctx->cache->functions[type->ast->name].realizations[oldKey] = diff --git a/codon/sir/llvm/llvisitor.cpp b/codon/sir/llvm/llvisitor.cpp index cd0cf2ee..cbc431fd 100644 --- a/codon/sir/llvm/llvisitor.cpp +++ b/codon/sir/llvm/llvisitor.cpp @@ -874,6 +874,8 @@ void LLVMVisitor::visit(const InternalFunc *x) { } } + if (!result) + internalFuncMatchesIgnoreArgs("__new__", x); seqassert(result, "internal function {} not found", *x); B->CreateRet(result); } From b72f54acc37c5af3d288525e610cfb6fdd2f9123 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Wed, 16 Feb 2022 17:06:44 -0500 Subject: [PATCH 44/61] Update IR attributes --- codon/sir/attribute.cpp | 32 ++++++++++++++++---------------- codon/sir/attribute.h | 20 ++++++++++++++------ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/codon/sir/attribute.cpp b/codon/sir/attribute.cpp index d131c993..445ef10a 100644 --- a/codon/sir/attribute.cpp +++ b/codon/sir/attribute.cpp @@ -67,24 +67,24 @@ std::ostream &TupleLiteralAttribute::doFormat(std::ostream &os) const { const std::string ListLiteralAttribute::AttributeName = "listLiteralAttribute"; std::unique_ptr ListLiteralAttribute::clone(util::CloneVisitor &cv) const { - std::vector elementsCloned; - for (auto *val : elements) - elementsCloned.push_back(cv.clone(val)); + std::vector elementsCloned; + for (auto &e : elements) + elementsCloned.push_back({cv.clone(e.value), e.star}); return std::make_unique(elementsCloned); } std::unique_ptr ListLiteralAttribute::forceClone(util::CloneVisitor &cv) const { - std::vector elementsCloned; - for (auto *val : elements) - elementsCloned.push_back(cv.forceClone(val)); + std::vector elementsCloned; + for (auto &e : elements) + elementsCloned.push_back({cv.forceClone(e.value), e.star}); return std::make_unique(elementsCloned); } std::ostream &ListLiteralAttribute::doFormat(std::ostream &os) const { std::vector strings; - for (auto *val : elements) - strings.push_back(fmt::format(FMT_STRING("{}"), *val)); + for (auto &e : elements) + strings.push_back(fmt::format(FMT_STRING("{}{}"), e.star ? "*" : "", *e.value)); fmt::print(os, FMT_STRING("[{}]"), fmt::join(strings.begin(), strings.end(), ",")); return os; } @@ -92,24 +92,24 @@ std::ostream &ListLiteralAttribute::doFormat(std::ostream &os) const { const std::string SetLiteralAttribute::AttributeName = "setLiteralAttribute"; std::unique_ptr SetLiteralAttribute::clone(util::CloneVisitor &cv) const { - std::vector elementsCloned; - for (auto *val : elements) - elementsCloned.push_back(cv.clone(val)); + std::vector elementsCloned; + for (auto &e : elements) + elementsCloned.push_back({cv.clone(e.value), e.star}); return std::make_unique(elementsCloned); } std::unique_ptr SetLiteralAttribute::forceClone(util::CloneVisitor &cv) const { - std::vector elementsCloned; - for (auto *val : elements) - elementsCloned.push_back(cv.forceClone(val)); + std::vector elementsCloned; + for (auto &e : elements) + elementsCloned.push_back({cv.forceClone(e.value), e.star}); return std::make_unique(elementsCloned); } std::ostream &SetLiteralAttribute::doFormat(std::ostream &os) const { std::vector strings; - for (auto *val : elements) - strings.push_back(fmt::format(FMT_STRING("{}"), *val)); + for (auto &e : elements) + strings.push_back(fmt::format(FMT_STRING("{}{}"), e.star ? "*" : "", *e.value)); fmt::print(os, FMT_STRING("set([{}])"), fmt::join(strings.begin(), strings.end(), ",")); return os; diff --git a/codon/sir/attribute.h b/codon/sir/attribute.h index cf34d072..2c79ed81 100644 --- a/codon/sir/attribute.h +++ b/codon/sir/attribute.h @@ -132,14 +132,22 @@ private: std::ostream &doFormat(std::ostream &os) const override; }; +/// Information about an element in a collection literal +struct LiteralElement { + /// the element value + Value *value; + /// true if preceded by "*", as in "[*x]" + bool star; +}; + /// Attribute attached to IR structures corresponding to list literals struct ListLiteralAttribute : public Attribute { static const std::string AttributeName; - /// values contained in list literal - std::vector elements; + /// elements contained in list literal + std::vector elements; - explicit ListLiteralAttribute(std::vector elements) + explicit ListLiteralAttribute(std::vector elements) : elements(std::move(elements)) {} std::unique_ptr clone(util::CloneVisitor &cv) const override; @@ -153,10 +161,10 @@ private: struct SetLiteralAttribute : public Attribute { static const std::string AttributeName; - /// values contained in set literal - std::vector elements; + /// elements contained in set literal + std::vector elements; - explicit SetLiteralAttribute(std::vector elements) + explicit SetLiteralAttribute(std::vector elements) : elements(std::move(elements)) {} std::unique_ptr clone(util::CloneVisitor &cv) const override; From aaf5e03e565df27ca2e961a51f48cd44c89d6009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Thu, 17 Feb 2022 19:03:07 -0800 Subject: [PATCH 45/61] Fix Linux undefined behaviour; Update CMake for g++ --- CMakeLists.txt | 12 ++++++++++-- codon/app/main.cpp | 3 ++- codon/parser/visitors/simplify/simplify_expr.cpp | 6 +++--- codon/parser/visitors/simplify/simplify_stmt.cpp | 11 +++++++---- codon/parser/visitors/translate/translate.cpp | 6 ++++-- codon/parser/visitors/translate/translate_ctx.cpp | 4 ++-- test/parser/simplify_stmt.codon | 2 ++ 7 files changed, 30 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 809fc806..30e407da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,16 @@ if(CODON_JUPYTER) endif() set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden -pedantic -Wno-return-type-c-linkage -Wno-gnu-zero-variadic-macro-arguments") -set(CMAKE_CXX_FLAGS_DEBUG "-g -fno-limit-debug-info") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -fvisibility-inlines-hidden -Wno-return-type-c-linkage -Wno-gnu-zero-variadic-macro-arguments") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type") +endif() +set(CMAKE_CXX_FLAGS_DEBUG "-g") +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-limit-debug-info") +endif() set(CMAKE_CXX_FLAGS_RELEASE "-O3") include_directories(.) diff --git a/codon/app/main.cpp b/codon/app/main.cpp index ae830ccf..de3da5c9 100644 --- a/codon/app/main.cpp +++ b/codon/app/main.cpp @@ -84,10 +84,11 @@ std::unique_ptr processSource(const std::vector & bool standalone) { llvm::cl::opt input(llvm::cl::Positional, llvm::cl::desc(""), llvm::cl::init("-")); + auto regs = llvm::cl::getRegisteredOptions(); llvm::cl::opt optMode( llvm::cl::desc("optimization mode"), llvm::cl::values( - clEnumValN(Debug, "debug", + clEnumValN(Debug, regs.find("debug") != regs.end() ? "default" : "debug", "Turn off compiler optimizations and show backtraces"), clEnumValN(Release, "release", "Turn on compiler optimizations and disable debug info")), diff --git a/codon/parser/visitors/simplify/simplify_expr.cpp b/codon/parser/visitors/simplify/simplify_expr.cpp index ae2bf528..f17bec5a 100644 --- a/codon/parser/visitors/simplify/simplify_expr.cpp +++ b/codon/parser/visitors/simplify/simplify_expr.cpp @@ -500,9 +500,9 @@ void SimplifyVisitor::visit(CallExpr *expr) { ctx->add(SimplifyItem::Var, varName, varName); var = N(varName); ctx->addBlock(); // prevent tmp vars from being toplevel vars - ex = N( - transform(N(clone(g->loops[0].vars), clone(var), nullptr, true)), - transform(ex)); + auto head = + transform(N(clone(g->loops[0].vars), clone(var), nullptr, true)); + ex = N(head, transform(ex)); ctx->popBlock(); } std::vector body; diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index d1e420ce..ff09cf15 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -180,7 +180,9 @@ void SimplifyVisitor::visit(WhileStmt *stmt) { transform(N(N(breakVar), N(true), nullptr, true)); } ctx->loops.push_back(breakVar); // needed for transforming break in loop..else blocks - StmtPtr whileStmt = N(transform(cond), transform(stmt->suite)); + + cond = transform(cond); + StmtPtr whileStmt = N(cond, transform(stmt->suite)); ctx->loops.pop_back(); if (stmt->elseSuite && stmt->elseSuite->firstInBlock()) { resultStmt = @@ -232,7 +234,8 @@ void SimplifyVisitor::visit(ForStmt *stmt) { ctx->addBlock(); if (auto i = stmt->var->getId()) { ctx->add(SimplifyItem::Var, i->value, ctx->generateCanonicalName(i->value)); - forStmt = N(transform(stmt->var), clone(iter), transform(stmt->suite), + auto var = transform(stmt->var); + forStmt = N(var, clone(iter), transform(stmt->suite), nullptr, decorator, ompArgs); } else { std::string varName = ctx->cache->getTemporaryVar("for"); @@ -259,8 +262,8 @@ void SimplifyVisitor::visit(ForStmt *stmt) { void SimplifyVisitor::visit(IfStmt *stmt) { seqassert(stmt->cond, "invalid if statement"); - resultStmt = N(transform(stmt->cond), transform(stmt->ifSuite), - transform(stmt->elseSuite)); + auto cond = transform(stmt->cond); + resultStmt = N(cond, transform(stmt->ifSuite), transform(stmt->elseSuite)); } void SimplifyVisitor::visit(MatchStmt *stmt) { diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index f1346ce1..a244c6de 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -91,8 +91,10 @@ void TranslateVisitor::visit(IdExpr *expr) { } void TranslateVisitor::visit(IfExpr *expr) { - result = make(expr, transform(expr->cond), transform(expr->ifexpr), - transform(expr->elsexpr)); + auto cond = transform(expr->cond); + auto ifexpr = transform(expr->ifexpr); + auto elsexpr = transform(expr->elsexpr); + result = make(expr, cond, ifexpr, elsexpr); } void TranslateVisitor::visit(CallExpr *expr) { diff --git a/codon/parser/visitors/translate/translate_ctx.cpp b/codon/parser/visitors/translate/translate_ctx.cpp index 690c0b84..7d7589c2 100644 --- a/codon/parser/visitors/translate/translate_ctx.cpp +++ b/codon/parser/visitors/translate/translate_ctx.cpp @@ -21,14 +21,14 @@ std::shared_ptr TranslateContext::find(const std::string &name) c return t; std::shared_ptr ret = nullptr; auto tt = cache->typeCtx->find(name); - if (tt->isType() && tt->type->canRealize()) { + if (tt && tt->isType() && tt->type->canRealize()) { ret = std::make_shared(TranslateItem::Type, bases[0]); seqassert(in(cache->classes, tt->type->getClass()->name) && in(cache->classes[tt->type->getClass()->name].realizations, name), "cannot find type realization {}", name); ret->handle.type = cache->classes[tt->type->getClass()->name].realizations[name]->ir; - } else if (tt->type->getFunc() && tt->type->canRealize()) { + } else if (tt && tt->type->getFunc() && tt->type->canRealize()) { ret = std::make_shared(TranslateItem::Func, bases[0]); seqassert( in(cache->functions, tt->type->getFunc()->ast->name) && diff --git a/test/parser/simplify_stmt.codon b/test/parser/simplify_stmt.codon index d849b7dd..28791469 100644 --- a/test/parser/simplify_stmt.codon +++ b/test/parser/simplify_stmt.codon @@ -381,6 +381,8 @@ print log(5.5) #: 1.70475 #%% import_c_dylib,barebones from internal.dlopen import dlext RT = "./libcodonrt." + dlext() +if RT[-3:] == ".so": + RT = "build/" + RT[2:] from C import RT.seq_str_int(int) -> str as sp print sp(65) #: 65 From 2506cb849e97782475d00f00c718c31539181584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 18 Feb 2022 17:27:24 -0800 Subject: [PATCH 46/61] Use @overload for top-level function overloads; Partial function bugfixes --- cmake/deps.cmake | 7 +++++-- codon/parser/ast/stmt.cpp | 1 + codon/parser/ast/stmt.h | 1 + codon/parser/ast/types.cpp | 10 +++++----- codon/parser/ast/types.h | 9 ++++++--- codon/parser/cache.cpp | 1 + codon/parser/visitors/simplify/simplify_stmt.cpp | 15 ++++++++------- .../parser/visitors/typecheck/typecheck_infer.cpp | 5 ++++- stdlib/internal/attributes.codon | 3 +++ test/parser/types.codon | 12 ++++++++++++ 10 files changed, 46 insertions(+), 18 deletions(-) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index dfa45958..ccbf1abd 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -69,6 +69,9 @@ CPMAddPackage( GITHUB_REPOSITORY "llvm-mirror/openmp" VERSION 9.0 GIT_TAG release_90 + # GITHUB_REPOSITORY "exaloop/openmp" + # VERSION 12.0.1 + # GIT_TAG v12.0.1 OPTIONS "OPENMP_ENABLE_LIBOMPTARGET OFF" "OPENMP_STANDALONE_BUILD ON") @@ -125,8 +128,8 @@ if(CODON_JUPYTER) NAME libzmq VERSION 4.3.4 URL https://github.com/zeromq/libzmq/releases/download/v4.3.4/zeromq-4.3.4.tar.gz - OPTIONS "WITH_PERF_TOOL OFF" - "ZMQ_BUILD_TESTS OFF" + OPTIONS "WITH_PERF_TOOL OFF" + "ZMQ_BUILD_TESTS OFF" "ENABLE_CPACK OFF" "BUILD_SHARED ON" "WITH_LIBSODIUM OFF") diff --git a/codon/parser/ast/stmt.cpp b/codon/parser/ast/stmt.cpp index 86f0ad64..45245dae 100644 --- a/codon/parser/ast/stmt.cpp +++ b/codon/parser/ast/stmt.cpp @@ -294,6 +294,7 @@ const std::string Attr::Capture = ".__capture__"; const std::string Attr::Extend = "extend"; const std::string Attr::Tuple = "tuple"; const std::string Attr::Test = "std.internal.attributes.test"; +const std::string Attr::Overload = "std.internal.attributes.overload"; FunctionStmt::FunctionStmt(std::string name, ExprPtr ret, std::vector args, StmtPtr suite, Attr attributes, diff --git a/codon/parser/ast/stmt.h b/codon/parser/ast/stmt.h index cae7f850..c4155f3a 100644 --- a/codon/parser/ast/stmt.h +++ b/codon/parser/ast/stmt.h @@ -409,6 +409,7 @@ struct Attr { const static std::string Tuple; // Standard library attributes const static std::string Test; + const static std::string Overload; // Function module std::string module; // Parent class (set for methods only) diff --git a/codon/parser/ast/types.cpp b/codon/parser/ast/types.cpp index 60ca85b3..e481ccb6 100644 --- a/codon/parser/ast/types.cpp +++ b/codon/parser/ast/types.cpp @@ -100,7 +100,7 @@ int LinkType::unify(Type *typ, Unification *undo) { if (undo) { LOG_TYPECHECK("[unify] {} := {}", id, typ->debugString(true)); // Link current type to typ and ensure that this modification is recorded in undo. - undo->linked.push_back(getLink()); + undo->linked.push_back(this); kind = Link; seqassert(!typ->getLink() || typ->getLink()->kind != Unbound || typ->getLink()->id <= id, @@ -108,7 +108,7 @@ int LinkType::unify(Type *typ, Unification *undo) { type = typ->follow(); if (auto t = type->getLink()) if (trait && t->kind == Unbound && !t->trait) { - undo->traits.push_back(t->getLink()); + undo->traits.push_back(t.get()); t->trait = trait; } } @@ -185,7 +185,7 @@ std::string LinkType::debugString(bool debug) const { // fmt::format("{}->{}", id, type->debugString(debug)); } std::string LinkType::realizedName() const { - if (kind == Unbound) + if (kind == Unbound || kind == Generic) return "?"; seqassert(kind == Link, "unexpected generic link"); return type->realizedName(); @@ -205,7 +205,7 @@ bool LinkType::occurs(Type *typ, Type::Unification *undo) { if (tl->trait && occurs(tl->trait.get(), undo)) return true; if (undo && tl->level > level) { - undo->leveled.emplace_back(make_pair(tl, tl->level)); + undo->leveled.emplace_back(make_pair(tl.get(), tl->level)); tl->level = level; } return false; @@ -578,7 +578,7 @@ std::string PartialType::debugString(bool debug) const { } std::string PartialType::realizedName() const { std::vector gs; - // gs.push_back(func->realizedName()); + gs.push_back(func->ast->name); for (auto &a : generics) if (!a.name.empty()) gs.push_back(a.type->realizedName()); diff --git a/codon/parser/ast/types.h b/codon/parser/ast/types.h index 465c93eb..94f30263 100644 --- a/codon/parser/ast/types.h +++ b/codon/parser/ast/types.h @@ -39,13 +39,16 @@ struct Type : public codon::SrcObject, public std::enable_shared_from_this /// Needed because the unify() is destructive. struct Unification { /// List of unbound types that have been changed. - std::vector> linked; + std::vector linked; /// List of unbound types whose level has been changed. - std::vector, int>> leveled; + std::vector> leveled; /// List of assigned traits. - std::vector> traits; + std::vector traits; /// Pointer to a TypecheckVisitor to support realization function types. TypecheckVisitor *realizator = nullptr; + /// List of pointers that are owned by unification process + /// (to avoid memory issues with undoing). + std::vector> ownedTypes; public: /// Undo the unification step. diff --git a/codon/parser/cache.cpp b/codon/parser/cache.cpp index 1af811eb..f660cea1 100644 --- a/codon/parser/cache.cpp +++ b/codon/parser/cache.cpp @@ -111,6 +111,7 @@ ir::Func *Cache::realizeFunction(types::FuncTypePtr type, } } } + // LOG("--> realizing {}", type->debugString(1)); int oldAge = typeCtx->age; typeCtx->age = 99999; auto tv = TypecheckVisitor(typeCtx); diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index ff09cf15..8f884708 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -235,8 +235,8 @@ void SimplifyVisitor::visit(ForStmt *stmt) { if (auto i = stmt->var->getId()) { ctx->add(SimplifyItem::Var, i->value, ctx->generateCanonicalName(i->value)); auto var = transform(stmt->var); - forStmt = N(var, clone(iter), transform(stmt->suite), - nullptr, decorator, ompArgs); + forStmt = N(var, clone(iter), transform(stmt->suite), nullptr, decorator, + ompArgs); } else { std::string varName = ctx->cache->getTemporaryVar("for"); ctx->add(SimplifyItem::Var, varName, varName); @@ -476,7 +476,7 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { // TODO: error on decorators return; } - + bool overload = attr.has(Attr::Overload); bool isClassMember = ctx->inClass(); std::string rootName; if (isClassMember) { @@ -484,10 +484,11 @@ void SimplifyVisitor::visit(FunctionStmt *stmt) { auto i = m.find(stmt->name); if (i != m.end()) rootName = i->second; - } else if (auto c = ctx->find(stmt->name)) { - if (c->isFunc() && c->getModule() == ctx->getModule() && - c->getBase() == ctx->getBase()) - rootName = c->canonicalName; + } else if (overload) { + if (auto c = ctx->find(stmt->name)) + if (c->isFunc() && c->getModule() == ctx->getModule() && + c->getBase() == ctx->getBase()) + rootName = c->canonicalName; } if (rootName.empty()) rootName = ctx->generateCanonicalName(stmt->name, true); diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index d3db1f3a..7e77e1f4 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -150,6 +150,7 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { LOG_REALIZE("[realize] fn {} -> {} : base {} ; depth = {}", type->ast->name, type->realizedName(), ctx->getBase(), depth); { + // Timer trx(fmt::format("fn {}", type->realizedName())); getLogger().level++; ctx->realizationDepth++; ctx->addBlock(); @@ -221,7 +222,9 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { // Realize the return type. if (auto t = realize(type->args[0])) unify(type->args[0], t); - LOG_REALIZE("... done with {} / {}", type->realizedName(), oldKey); + LOG_REALIZE("[realize] done with {} / {} =>{}", type->realizedName(), oldKey, + time); + // trx.log(); // Create and store IR node and a realized AST to be used // during the code generation. diff --git a/stdlib/internal/attributes.codon b/stdlib/internal/attributes.codon index bab803b8..19ddeea2 100644 --- a/stdlib/internal/attributes.codon +++ b/stdlib/internal/attributes.codon @@ -42,3 +42,6 @@ def C(): def realize_without_self(): pass +@__attribute__ +def overload(): + pass diff --git a/test/parser/types.codon b/test/parser/types.codon index 82b21a66..8f7f2f2d 100644 --- a/test/parser/types.codon +++ b/test/parser/types.codon @@ -1113,11 +1113,13 @@ print methodcaller('index')(v, 42) #: 1 def foo(x): return 1, x +@overload def foo(x, y): def foo(x, y): return f'{x}_{y}' return 2, foo(x, y) +@overload def foo(x): if x == '': return 3, 0 @@ -1126,9 +1128,19 @@ def foo(x): print foo('hi') #: (3, 2) print foo('hi', 1) #: (2, 'hi_1') +#%% fn_shadow,barebones +def foo(x): + return 1, x +print foo('hi') #: (1, 'hi') + +def foo(x): + return 2, x +print foo('hi') #: (2, 'hi') + #%% fn_overloads_error,barebones def foo(x): return 1, x +@overload def foo(x, y): return 2, x, y foo('hooooooooy!', 1, 2) #! cannot find an overload 'foo' with arguments = str, = int, = int From d59ec14cc64c304c4f0a4b0e3424c80236c6d4a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 18 Feb 2022 17:34:23 -0800 Subject: [PATCH 47/61] @deduce tests --- .../visitors/simplify/simplify_stmt.cpp | 2 +- test/parser/simplify_stmt.codon | 25 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 8f884708..40c350c4 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -697,7 +697,7 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { // @dataclass(...) // @extend for (auto &d : stmt->decorators) { - if (d->isId("__deduce__")) { + if (d->isId("deduce")) { deduce = true; } else if (auto c = d->getCall()) { if (c->expr->isId(Attr::Tuple)) diff --git a/test/parser/simplify_stmt.codon b/test/parser/simplify_stmt.codon index 28791469..598827d2 100644 --- a/test/parser/simplify_stmt.codon +++ b/test/parser/simplify_stmt.codon @@ -996,4 +996,27 @@ def foo(return_, pass_, yield_, break_, continue_, print_, assert_): assert_.append(7) return return_, pass_, yield_, break_, continue_, print_, assert_ print foo([1], [1], [1], [1], [1], [1], [1]) -#: ([1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7]) \ No newline at end of file +#: ([1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7]) + + +#%% class_deduce,barebones +@deduce +class Foo: + def __init__(self, x): + self.x = [x] + self.y = 1, x + +f = Foo(1) +print(f.x, f.y, f.__class__) #: [1] (1, 1) Foo[List[int],Tuple[int,int]] + +f: Foo = Foo('s') +print(f.x, f.y, f.__class__) #: ['s'] (1, 's') Foo[List[str],Tuple[int,str]] + +@deduce +class Bar: + def __init__(self, y): + self.y = Foo(y) + +b = Bar(3.1) +print(b.y.x, b.__class__) #: [3.1] Bar[Foo[List[float],Tuple[int,float]]] + From f2ed69af9e5bc1b998bb4e831a1bc5964e76f609 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Sat, 19 Feb 2022 14:30:44 -0500 Subject: [PATCH 48/61] Update dict attribute --- codon/sir/attribute.cpp | 15 +++++++++++---- codon/sir/attribute.h | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/codon/sir/attribute.cpp b/codon/sir/attribute.cpp index 445ef10a..45433a54 100644 --- a/codon/sir/attribute.cpp +++ b/codon/sir/attribute.cpp @@ -120,7 +120,8 @@ const std::string DictLiteralAttribute::AttributeName = "dictLiteralAttribute"; std::unique_ptr DictLiteralAttribute::clone(util::CloneVisitor &cv) const { std::vector elementsCloned; for (auto &val : elements) - elementsCloned.push_back({cv.clone(val.key), cv.clone(val.value)}); + elementsCloned.push_back( + {cv.clone(val.key), val.value ? cv.clone(val.value) : nullptr}); return std::make_unique(elementsCloned); } @@ -128,14 +129,20 @@ std::unique_ptr DictLiteralAttribute::forceClone(util::CloneVisitor &cv) const { std::vector elementsCloned; for (auto &val : elements) - elementsCloned.push_back({cv.forceClone(val.key), cv.forceClone(val.value)}); + elementsCloned.push_back( + {cv.forceClone(val.key), val.value ? cv.forceClone(val.value) : nullptr}); return std::make_unique(elementsCloned); } std::ostream &DictLiteralAttribute::doFormat(std::ostream &os) const { std::vector strings; - for (auto &val : elements) - strings.push_back(fmt::format(FMT_STRING("{}:{}"), *val.key, *val.value)); + for (auto &val : elements) { + if (val.value) { + strings.push_back(fmt::format(FMT_STRING("{}:{}"), *val.key, *val.value)); + } else { + strings.push_back(fmt::format(FMT_STRING("**{}"), *val.key)); + } + } fmt::print(os, FMT_STRING("dict([{}])"), fmt::join(strings.begin(), strings.end(), ",")); return os; diff --git a/codon/sir/attribute.h b/codon/sir/attribute.h index 2c79ed81..f86edfa5 100644 --- a/codon/sir/attribute.h +++ b/codon/sir/attribute.h @@ -177,7 +177,9 @@ private: /// Attribute attached to IR structures corresponding to dict literals struct DictLiteralAttribute : public Attribute { struct KeyValuePair { + /// the key in the literal Value *key; + /// the value in the literal, or null if key is being star-unpacked Value *value; }; From 354e7f80fecee9bf2faddcaf7a8c4e10e87e6481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Sat, 19 Feb 2022 12:39:24 -0800 Subject: [PATCH 49/61] Add and fix IR attributes; Fix Counter test --- codon/parser/ast/expr.cpp | 9 +-- codon/parser/ast/expr.h | 23 ++++-- .../visitors/simplify/simplify_expr.cpp | 41 +++++++--- codon/parser/visitors/translate/translate.cpp | 79 +++++++++++++++++-- .../parser/visitors/translate/translate_ctx.h | 2 + .../visitors/typecheck/typecheck_expr.cpp | 20 +++-- stdlib/collections.codon | 3 + 7 files changed, 142 insertions(+), 35 deletions(-) diff --git a/codon/parser/ast/expr.cpp b/codon/parser/ast/expr.cpp index c4765443..b9e87b8c 100644 --- a/codon/parser/ast/expr.cpp +++ b/codon/parser/ast/expr.cpp @@ -18,7 +18,7 @@ namespace ast { Expr::Expr() : type(nullptr), isTypeExpr(false), staticValue(StaticValue::NOT_STATIC), - done(false) {} + done(false), attributes(0) {} types::TypePtr Expr::getType() const { return type; } void Expr::setType(types::TypePtr t) { this->type = std::move(t); } bool Expr::isType() const { return isTypeExpr; } @@ -31,6 +31,8 @@ std::string Expr::wrapType(const std::string &sexpr) const { done ? "*" : ""); } bool Expr::isStatic() const { return staticValue.type != StaticValue::NOT_STATIC; } +bool Expr::hasAttr(int attr) const { return (attributes & (1 << attr)); } +void Expr::setAttr(int attr) { attributes |= (1 << attr); } StaticValue::StaticValue(StaticValue::Type t) : value(), type(t), evaluated(false) {} StaticValue::StaticValue(int64_t i) : value(i), type(INT), evaluated(true) {} @@ -397,14 +399,11 @@ StmtExpr::StmtExpr(std::shared_ptr stmt, std::shared_ptr stmt2, stmts.push_back(std::move(stmt2)); } StmtExpr::StmtExpr(const StmtExpr &expr) - : Expr(expr), stmts(ast::clone(expr.stmts)), expr(ast::clone(expr.expr)), - attributes(expr.attributes) {} + : Expr(expr), stmts(ast::clone(expr.stmts)), expr(ast::clone(expr.expr)) {} std::string StmtExpr::toString() const { return wrapType(format("stmt-expr ({}) {}", combine(stmts, " "), expr->toString())); } ACCEPT_IMPL(StmtExpr, ASTVisitor); -bool StmtExpr::hasAttr(const std::string &attr) const { return in(attributes, attr); } -void StmtExpr::setAttr(const std::string &attr) { attributes.insert(attr); } PtrExpr::PtrExpr(ExprPtr expr) : Expr(), expr(std::move(expr)) {} PtrExpr::PtrExpr(const PtrExpr &expr) : Expr(expr), expr(ast::clone(expr.expr)) {} diff --git a/codon/parser/ast/expr.h b/codon/parser/ast/expr.h index 220d56f7..a9682a89 100644 --- a/codon/parser/ast/expr.h +++ b/codon/parser/ast/expr.h @@ -78,6 +78,9 @@ struct Expr : public codon::SrcObject { /// type-checking procedure was successful). bool done; + /// Set of attributes. + int attributes; + public: Expr(); Expr(const Expr &expr) = default; @@ -124,6 +127,10 @@ public: virtual const TupleExpr *getTuple() const { return nullptr; } virtual const UnaryExpr *getUnary() const { return nullptr; } + /// Attribute helpers + bool hasAttr(int attr) const; + void setAttr(int attr); + protected: /// Add a type to S-expression string. std::string wrapType(const std::string &sexpr) const; @@ -600,8 +607,6 @@ struct RangeExpr : public Expr { struct StmtExpr : public Expr { std::vector> stmts; ExprPtr expr; - /// Set of attributes. - std::set attributes; StmtExpr(std::vector> stmts, ExprPtr expr); StmtExpr(std::shared_ptr stmt, ExprPtr expr); @@ -612,10 +617,6 @@ struct StmtExpr : public Expr { ACCEPT(ASTVisitor); const StmtExpr *getStmtExpr() const override { return this; } - - /// Attribute helpers - bool hasAttr(const std::string &attr) const; - void setAttr(const std::string &attr); }; /// Pointer expression (__ptr__(expr)). @@ -672,5 +673,15 @@ struct StackAllocExpr : Expr { #undef ACCEPT +enum ExprAttr { + SequenceItem, + StarSequenceItem, + List, + Set, + Dict, + Partial, + __LAST__ +}; + } // namespace ast } // namespace codon diff --git a/codon/parser/visitors/simplify/simplify_expr.cpp b/codon/parser/visitors/simplify/simplify_expr.cpp index f17bec5a..eccc0784 100644 --- a/codon/parser/visitors/simplify/simplify_expr.cpp +++ b/codon/parser/visitors/simplify/simplify_expr.cpp @@ -9,7 +9,6 @@ #include "codon/parser/common.h" #include "codon/parser/peg/peg.h" #include "codon/parser/visitors/simplify/simplify.h" -#include "codon/sir/attribute.h" using fmt::format; @@ -32,6 +31,8 @@ ExprPtr SimplifyVisitor::transform(const ExprPtr &expr, bool allowTypes, ctx->canAssign = oldAssign; if (!allowTypes && v.resultExpr && v.resultExpr->isType()) error("unexpected type expression"); + if (v.resultExpr) + v.resultExpr->attributes |= expr->attributes; return v.resultExpr; } @@ -180,16 +181,20 @@ void SimplifyVisitor::visit(ListExpr *expr) { for (const auto &it : expr->items) { if (auto star = it->getStar()) { ExprPtr forVar = N(ctx->cache->getTemporaryVar("it")); + auto st = star->what->clone(); + st->setAttr(ExprAttr::StarSequenceItem); stmts.push_back(transform(N( - clone(forVar), star->what->clone(), + clone(forVar), st, N(N(N(clone(var), "append"), clone(forVar)))))); } else { - stmts.push_back(transform( - N(N(N(clone(var), "append"), clone(it))))); + auto st = clone(it); + st->setAttr(ExprAttr::SequenceItem); + stmts.push_back( + transform(N(N(N(clone(var), "append"), st)))); } } auto e = N(stmts, transform(var)); - e->setAttr(ir::ListLiteralAttribute::AttributeName); + e->setAttr(ExprAttr::List); resultExpr = e; ctx->popBlock(); } @@ -203,15 +208,19 @@ void SimplifyVisitor::visit(SetExpr *expr) { for (auto &it : expr->items) if (auto star = it->getStar()) { ExprPtr forVar = N(ctx->cache->getTemporaryVar("it")); + auto st = star->what->clone(); + st->setAttr(ExprAttr::StarSequenceItem); stmts.push_back(transform(N( - clone(forVar), star->what->clone(), + clone(forVar), st, N(N(N(clone(var), "add"), clone(forVar)))))); } else { - stmts.push_back(transform( - N(N(N(clone(var), "add"), clone(it))))); + auto st = clone(it); + st->setAttr(ExprAttr::SequenceItem); + stmts.push_back( + transform(N(N(N(clone(var), "add"), st)))); } auto e = N(stmts, transform(var)); - e->setAttr(ir::SetLiteralAttribute::AttributeName); + e->setAttr(ExprAttr::Set); resultExpr = e; ctx->popBlock(); } @@ -225,17 +234,23 @@ void SimplifyVisitor::visit(DictExpr *expr) { for (auto &it : expr->items) if (auto star = CAST(it.value, KeywordStarExpr)) { ExprPtr forVar = N(ctx->cache->getTemporaryVar("it")); + auto st = star->what->clone(); + st->setAttr(ExprAttr::StarSequenceItem); stmts.push_back(transform(N( - clone(forVar), N(N(star->what->clone(), "items")), + clone(forVar), N(N(st, "items")), N(N(N(clone(var), "__setitem__"), N(clone(forVar), N(0)), N(clone(forVar), N(1))))))); } else { - stmts.push_back(transform(N(N( - N(clone(var), "__setitem__"), clone(it.key), clone(it.value))))); + auto k = clone(it.key); + k->setAttr(ExprAttr::SequenceItem); + auto v = clone(it.value); + v->setAttr(ExprAttr::SequenceItem); + stmts.push_back(transform( + N(N(N(clone(var), "__setitem__"), k, v)))); } auto e = N(stmts, transform(var)); - e->setAttr(ir::DictLiteralAttribute::AttributeName); + e->setAttr(ExprAttr::Dict); resultExpr = e; ctx->popBlock(); } diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index ebeee5bb..7a3cf985 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -57,8 +57,81 @@ ir::Func *TranslateVisitor::apply(Cache *cache, StmtPtr stmts) { ir::Value *TranslateVisitor::transform(const ExprPtr &expr) { TranslateVisitor v(ctx); v.setSrcInfo(expr->getSrcInfo()); + + types::PartialType *p = nullptr; + if (expr->attributes) { + if (expr->hasAttr(ExprAttr::List) || expr->hasAttr(ExprAttr::Set) || + expr->hasAttr(ExprAttr::Dict) || expr->hasAttr(ExprAttr::Partial)) { + ctx->seqItems.push_back(std::vector>()); + } + if (expr->hasAttr(ExprAttr::Partial)) + p = expr->type->getPartial().get(); + // LOG("{} {}: {}", std::string(ctx->seqItems.size(), ' '), expr->attributes, expr->toString()); + } + expr->accept(v); - return v.result; + ir::Value *ir = v.result; + + if (expr->attributes) { + if (expr->hasAttr(ExprAttr::List) || expr->hasAttr(ExprAttr::Set)) { + std::vector v; + for (auto &p : ctx->seqItems.back()) { + seqassert(p.first <= ExprAttr::StarSequenceItem, "invalid list/set element"); + v.push_back( + ir::LiteralElement{p.second, p.first == ExprAttr::StarSequenceItem}); + } + if (expr->hasAttr(ExprAttr::List)) + ir->setAttribute(std::make_unique(v)); + else + ir->setAttribute(std::make_unique(v)); + ctx->seqItems.pop_back(); + } + if (expr->hasAttr(ExprAttr::Dict)) { + std::vector v; + LOG("{} {}", expr->toString(), expr->getSrcInfo()); + for (int pi = 0; pi < ctx->seqItems.back().size(); pi++) { + auto &p = ctx->seqItems.back()[pi]; + if (p.first == ExprAttr::StarSequenceItem) { + v.push_back({p.second, nullptr}); + } else { + seqassert(p.first == ExprAttr::SequenceItem && + pi + 1 < ctx->seqItems.back().size() && + ctx->seqItems.back()[pi + 1].first == ExprAttr::SequenceItem, + "invalid dict element"); + v.push_back({p.second, ctx->seqItems.back()[pi + 1].second}); + pi++; + } + } + ir->setAttribute(std::make_unique(v)); + ctx->seqItems.pop_back(); + } + if (expr->hasAttr(ExprAttr::Partial)) { + std::vector v; + seqassert(p, "invalid partial element"); + int j = 0; + for (int i = 0; i < p->known.size(); i++) { + if (p->known[i] && !p->func->ast->args[i].generic) { + seqassert(j < ctx->seqItems.back().size() && + ctx->seqItems.back()[j].first == ExprAttr::SequenceItem, + "invalid partial element"); + v.push_back(ctx->seqItems.back()[j++].second); + } else if (!p->func->ast->args[i].generic) { + v.push_back({nullptr}); + } + } + // seqassert(j == ctx->seqItems.back().size(), "invalid partial element"); + ir->setAttribute(std::make_unique(nullptr, v)); + ctx->seqItems.pop_back(); + } + if (expr->hasAttr(ExprAttr::SequenceItem)) { + ctx->seqItems.back().push_back({ExprAttr::SequenceItem, ir}); + } + if (expr->hasAttr(ExprAttr::StarSequenceItem)) { + ctx->seqItems.back().push_back({ExprAttr::StarSequenceItem, ir}); + } + } + + return ir; } void TranslateVisitor::defaultVisit(Expr *n) { @@ -211,10 +284,6 @@ void TranslateVisitor::visit(StmtExpr *expr) { transform(s); ctx->popSeries(); result = make(expr, bodySeries, transform(expr->expr)); - for (auto &a : expr->attributes) { - // if (a == ir::ListLiteralAttribute::AttributeName) - // result->setAttribute(ir::ListLiteralAttribute); - } } /************************************************************************************/ diff --git a/codon/parser/visitors/translate/translate_ctx.h b/codon/parser/visitors/translate/translate_ctx.h index 6d182492..12b34704 100644 --- a/codon/parser/visitors/translate/translate_ctx.h +++ b/codon/parser/visitors/translate/translate_ctx.h @@ -50,6 +50,8 @@ struct TranslateContext : public Context { std::vector bases; /// Stack of IR series (blocks). std::vector series; + /// Stack of sequence items for attribute initialization. + std::vector>> seqItems; public: TranslateContext(Cache *cache); diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index bf32a655..73c7bcc8 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -36,8 +36,10 @@ ExprPtr TypecheckVisitor::transform(ExprPtr &expr, bool allowTypes, bool allowVo ctx->allowActivation = false; v.setSrcInfo(expr->getSrcInfo()); expr->accept(v); - if (v.resultExpr) + if (v.resultExpr) { + v.resultExpr->attributes |= expr->attributes; expr = v.resultExpr; + } seqassert(expr->type, "type not set for {}", expr->toString()); unify(typ, expr->type); if (disableActivation) @@ -1246,6 +1248,8 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in return -1; }, known); + bool hasPartialArgs = partialStarArgs != nullptr, + hasPartialKwargs = partialKwstarArgs != nullptr; if (isPartial) { deactivateUnbounds(expr->args.back().value->getType().get()); expr->args.pop_back(); @@ -1361,10 +1365,16 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in deactivateUnbounds(calleeFn.get()); std::vector newArgs; for (auto &r : args) - if (!r.value->getEllipsis()) + if (!r.value->getEllipsis()) { newArgs.push_back(r.value); + newArgs.back()->setAttr(ExprAttr::SequenceItem); + } newArgs.push_back(partialStarArgs); + if (hasPartialArgs) + newArgs.back()->setAttr(ExprAttr::SequenceItem); newArgs.push_back(partialKwstarArgs); + if (hasPartialKwargs) + newArgs.back()->setAttr(ExprAttr::SequenceItem); std::string var = ctx->cache->getTemporaryVar("partial"); ExprPtr call = nullptr; @@ -1379,8 +1389,7 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in N(N(partialTypeName), newArgs)), N(var)); } - const_cast(call->getStmtExpr()) - ->setAttr(ir::PartialFunctionAttribute::AttributeName); + call->setAttr(ExprAttr::Partial); call = transform(call, false, allowVoidExpr); seqassert(call->type->getPartial(), "expected partial type"); return call; @@ -1722,8 +1731,7 @@ ExprPtr TypecheckVisitor::partializeFunction(ExprPtr expr) { N(N(partialTypeName), N(), N(N(kwName)))), N(var)); - const_cast(call->getStmtExpr()) - ->setAttr(ir::PartialFunctionAttribute::AttributeName); + call->setAttr(ExprAttr::Partial); call = transform(call, false, allowVoidExpr); seqassert(call->type->getPartial(), "expected partial type"); return call; diff --git a/stdlib/collections.codon b/stdlib/collections.codon index b5a1a21a..75162ac4 100644 --- a/stdlib/collections.codon +++ b/stdlib/collections.codon @@ -339,6 +339,9 @@ class Counter[T](Dict[T,int]): result |= other return result + def __dict_do_op_throws__[F, Z](self, key: T, other: Z, op: F): + self.__dict_do_op__(key, other, 0, op) + @extend class Dict: From 685c42981d309e4859aa078055208981c0d46f63 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Tue, 22 Feb 2022 10:16:30 -0500 Subject: [PATCH 50/61] Remove invalid check --- codon/sir/llvm/llvisitor.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/codon/sir/llvm/llvisitor.cpp b/codon/sir/llvm/llvisitor.cpp index cbc431fd..cd0cf2ee 100644 --- a/codon/sir/llvm/llvisitor.cpp +++ b/codon/sir/llvm/llvisitor.cpp @@ -874,8 +874,6 @@ void LLVMVisitor::visit(const InternalFunc *x) { } } - if (!result) - internalFuncMatchesIgnoreArgs("__new__", x); seqassert(result, "internal function {} not found", *x); B->CreateRet(result); } From 4452367c1fb32acb8ec00b34d08f140a066a09c9 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Tue, 22 Feb 2022 10:17:50 -0500 Subject: [PATCH 51/61] Update OpenMP --- cmake/deps.cmake | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index ccbf1abd..aa093bbe 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -66,13 +66,11 @@ if(bdwgc_ADDED) endif() CPMAddPackage( - GITHUB_REPOSITORY "llvm-mirror/openmp" - VERSION 9.0 - GIT_TAG release_90 - # GITHUB_REPOSITORY "exaloop/openmp" - # VERSION 12.0.1 - # GIT_TAG v12.0.1 - OPTIONS "OPENMP_ENABLE_LIBOMPTARGET OFF" + NAME openmp + GITHUB_REPOSITORY "exaloop/openmp" + VERSION 13.0.0-fb9fc3c + OPTIONS "CMAKE_BUILD_TYPE Release" + "OPENMP_ENABLE_LIBOMPTARGET OFF" "OPENMP_STANDALONE_BUILD ON") CPMAddPackage( From bba16437c707ed334321d4c7d0912d2b8dd1865e Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Wed, 23 Feb 2022 16:20:33 -0500 Subject: [PATCH 52/61] Store function name in PartialFunctionAttribute --- codon/sir/attribute.cpp | 8 +++----- codon/sir/attribute.h | 8 ++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/codon/sir/attribute.cpp b/codon/sir/attribute.cpp index 45433a54..075d6b4a 100644 --- a/codon/sir/attribute.cpp +++ b/codon/sir/attribute.cpp @@ -155,8 +155,7 @@ PartialFunctionAttribute::clone(util::CloneVisitor &cv) const { std::vector argsCloned; for (auto *val : args) argsCloned.push_back(cv.clone(val)); - return std::make_unique(cast(cv.clone(func)), - argsCloned); + return std::make_unique(name, argsCloned); } std::unique_ptr @@ -164,15 +163,14 @@ PartialFunctionAttribute::forceClone(util::CloneVisitor &cv) const { std::vector argsCloned; for (auto *val : args) argsCloned.push_back(cv.forceClone(val)); - return std::make_unique(cast(cv.forceClone(func)), - argsCloned); + return std::make_unique(name, argsCloned); } std::ostream &PartialFunctionAttribute::doFormat(std::ostream &os) const { std::vector strings; for (auto *val : args) strings.push_back(val ? fmt::format(FMT_STRING("{}"), *val) : "..."); - fmt::print(os, FMT_STRING("{}({})"), func->getName(), + fmt::print(os, FMT_STRING("{}({})"), name, fmt::join(strings.begin(), strings.end(), ",")); return os; } diff --git a/codon/sir/attribute.h b/codon/sir/attribute.h index f86edfa5..eb2e945f 100644 --- a/codon/sir/attribute.h +++ b/codon/sir/attribute.h @@ -202,15 +202,15 @@ private: struct PartialFunctionAttribute : public Attribute { static const std::string AttributeName; - /// function being called - Func *func; + /// base name of the function being used in the partial + std::string name; /// partial arguments, or null if none /// e.g. "f(a, ..., b)" has elements [a, null, b] std::vector args; - PartialFunctionAttribute(Func *func, std::vector args) - : func(func), args(std::move(args)) {} + PartialFunctionAttribute(const std::string &name, std::vector args) + : name(name), args(std::move(args)) {} std::unique_ptr clone(util::CloneVisitor &cv) const override; std::unique_ptr forceClone(util::CloneVisitor &cv) const override; From 49ca0845cfe4b6ec26fadc596f82e7ffa81e1087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Wed, 23 Feb 2022 17:40:59 -0800 Subject: [PATCH 53/61] CR --- codon/parser/ast/types.cpp | 10 +--------- codon/parser/visitors/translate/translate.cpp | 4 ---- codon/parser/visitors/typecheck/typecheck_expr.cpp | 2 -- codon/sir/transform/cleanup/canonical.cpp | 2 +- 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/codon/parser/ast/types.cpp b/codon/parser/ast/types.cpp index e481ccb6..9aa156b4 100644 --- a/codon/parser/ast/types.cpp +++ b/codon/parser/ast/types.cpp @@ -538,15 +538,7 @@ PartialType::PartialType(const std::shared_ptr &baseType, std::shared_ptr func, std::vector known) : RecordType(*baseType), func(move(func)), known(move(known)) {} int PartialType::unify(Type *typ, Unification *us) { - int s1 = 0, s; - // if (auto tc = typ->getPartial()) { - // // Check names. - // if ((s = func->unify(tc->func.get(), us)) == -1) - // return -1; - // s1 += s; - // } - s = this->RecordType::unify(typ, us); - return s == -1 ? s : s1 + s; + return this->RecordType::unify(typ, us); } TypePtr PartialType::generalize(int atLevel) { return std::make_shared( diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index ebeee5bb..3de6f57e 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -211,10 +211,6 @@ void TranslateVisitor::visit(StmtExpr *expr) { transform(s); ctx->popSeries(); result = make(expr, bodySeries, transform(expr->expr)); - for (auto &a : expr->attributes) { - // if (a == ir::ListLiteralAttribute::AttributeName) - // result->setAttribute(ir::ListLiteralAttribute); - } } /************************************************************************************/ diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index bf32a655..e3a20139 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -1649,8 +1649,6 @@ std::string TypecheckVisitor::generatePartialStub(const std::vector &mask, ctx->cache->partials[typeName] = {fn->generalize(0)->getFunc(), mask}; generateTupleStub(tupleSize + 2, typeName, {}, false); } - // LOG("[p] {} -> {}", typeName, - // ctx->cache->partials[typeName].first->debugString(1)); return typeName; } diff --git a/codon/sir/transform/cleanup/canonical.cpp b/codon/sir/transform/cleanup/canonical.cpp index c0b962b3..6cf2da47 100644 --- a/codon/sir/transform/cleanup/canonical.cpp +++ b/codon/sir/transform/cleanup/canonical.cpp @@ -280,7 +280,7 @@ struct CanonConstSub : public RewriteRule { Value *newCall = nullptr; if (util::isConst(rhs)) { auto c = util::getConst(rhs); - if (c != -(static_cast(1) << 63)) // ensure no overflow + if (c != -(1ull << 63)) // ensure no overflow newCall = *lhs + *(M->getInt(-c)); } else if (util::isConst(rhs)) { auto c = util::getConst(rhs); From a8bc79b3a28ca002f9d32c642292fd21a07b5b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Wed, 23 Feb 2022 18:08:06 -0800 Subject: [PATCH 54/61] Fix PartialAttr function name --- codon/parser/visitors/translate/translate.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index 7a3cf985..362a9d6d 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -66,7 +66,8 @@ ir::Value *TranslateVisitor::transform(const ExprPtr &expr) { } if (expr->hasAttr(ExprAttr::Partial)) p = expr->type->getPartial().get(); - // LOG("{} {}: {}", std::string(ctx->seqItems.size(), ' '), expr->attributes, expr->toString()); + // LOG("{} {}: {}", std::string(ctx->seqItems.size(), ' '), expr->attributes, + // expr->toString()); } expr->accept(v); @@ -119,8 +120,8 @@ ir::Value *TranslateVisitor::transform(const ExprPtr &expr) { v.push_back({nullptr}); } } - // seqassert(j == ctx->seqItems.back().size(), "invalid partial element"); - ir->setAttribute(std::make_unique(nullptr, v)); + ir->setAttribute( + std::make_unique(p->func->ast->name, v)); ctx->seqItems.pop_back(); } if (expr->hasAttr(ExprAttr::SequenceItem)) { From 56cd0711f79d91d5e36a3d624d77d06cbb796e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Thu, 24 Feb 2022 11:43:51 -0800 Subject: [PATCH 55/61] Move std::filesystem to llvm::sys::fs --- CMakeLists.txt | 2 +- codon/app/main.cpp | 1 - codon/compiler/compiler.cpp | 4 +- codon/dsl/plugins.cpp | 49 ++++++++------- codon/parser/common.cpp | 59 ++++++++++++------- codon/parser/common.h | 3 + codon/parser/visitors/doc/doc.cpp | 3 +- codon/parser/visitors/translate/translate.cpp | 3 +- codon/util/common.h | 4 +- extra/jupyter/jupyter.cpp | 11 ---- 10 files changed, 76 insertions(+), 63 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7038888f..02e5c11c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -334,7 +334,7 @@ llvm_map_components_to_libnames(LLVM_LIBS if(APPLE) target_link_libraries(codonc PRIVATE ${LLVM_LIBS} dl codonrt) else() - target_link_libraries(codonc PRIVATE ${STATIC_LIBCPP} ${LLVM_LIBS} dl codonrt stdc++fs) + target_link_libraries(codonc PRIVATE ${STATIC_LIBCPP} ${LLVM_LIBS} dl codonrt) endif() # Gather headers diff --git a/codon/app/main.cpp b/codon/app/main.cpp index da32bfa8..4597bb4c 100644 --- a/codon/app/main.cpp +++ b/codon/app/main.cpp @@ -233,7 +233,6 @@ int jitMode(const std::vector &args) { llvm::cantFail(jit.init()); fmt::print(">>> Codon JIT v{} <<<\n", CODON_VERSION); std::string code; - // std::ifstream qq("scratch.codon"); for (std::string line; std::getline(std::cin, line);) { if (line != "#%%") { code += line + "\n"; diff --git a/codon/compiler/compiler.cpp b/codon/compiler/compiler.cpp index 90637586..f781b14f 100644 --- a/codon/compiler/compiler.cpp +++ b/codon/compiler/compiler.cpp @@ -64,9 +64,7 @@ Compiler::parse(bool isCode, const std::string &file, const std::string &code, int startLine, int testFlags, const std::unordered_map &defines) { input = file; - std::string abspath = - (file != "-") ? std::experimental::filesystem::absolute(std::experimental::filesystem::path(file)).string() - : file; + std::string abspath = (file != "-") ? ast::getAbsolutePath(file) : file; try { Timer t1("parse"); ast::StmtPtr codeStmt = isCode diff --git a/codon/dsl/plugins.cpp b/codon/dsl/plugins.cpp index fe82b968..b259dadf 100644 --- a/codon/dsl/plugins.cpp +++ b/codon/dsl/plugins.cpp @@ -1,12 +1,14 @@ #include "plugins.h" #include -#include #include "codon/parser/common.h" #include "codon/util/common.h" #include "codon/util/semver/semver.h" #include "codon/util/toml++/toml.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Filesystem.h" +#include "llvm/Support/Path.h" namespace codon { namespace { @@ -17,8 +19,6 @@ llvm::Expected pluginError(const std::string &msg) { typedef std::unique_ptr LoadFunc(); } // namespace -namespace fs = std::experimental::filesystem; - llvm::Expected PluginManager::load(const std::string &path) { #if __APPLE__ const std::string libExt = "dylib"; @@ -27,35 +27,42 @@ llvm::Expected PluginManager::load(const std::string &path) { #endif const std::string config = "plugin.toml"; - fs::path tomlPath = fs::path(path) / config; - if (!fs::exists(tomlPath)) { + + llvm::SmallString<128> tomlPath(path); + llvm::sys::path::append(tomlPath, config); + if (!llvm::sys::fs::exists(tomlPath)) { // try default install path - if (auto *homeDir = std::getenv("HOME")) - tomlPath = fs::path(homeDir) / ".codon/plugins" / path / config; + if (auto *homeDir = std::getenv("HOME")) { + tomlPath = homeDir; + llvm::sys::path::append(tomlPath, ".codon", "plugins", path, config); + } } toml::parse_result tml; try { - tml = toml::parse_file(tomlPath.string()); + tml = toml::parse_file(tomlPath.str()); } catch (const toml::parse_error &e) { return pluginError( - fmt::format("[toml::parse_file(\"{}\")] {}", tomlPath.string(), e.what())); + fmt::format("[toml::parse_file(\"{}\")] {}", tomlPath.str(), e.what())); } auto about = tml["about"]; auto library = tml["library"]; std::string cppLib = library["cpp"].value_or(""); - std::string dylibPath; - if (!cppLib.empty()) - dylibPath = fs::path(tomlPath) - .replace_filename(library["cpp"].value_or("lib")) - .replace_extension(libExt) - .string(); + llvm::SmallString<128> dylibPath; + if (!cppLib.empty()) { + dylibPath = llvm::sys::path::parent_path(tomlPath); + auto fn = std::string(library["cpp"].value_or("lib")) + "." + libExt; + llvm::sys::path::append(dylibPath, fn); + } std::string codonLib = library["codon"].value_or(""); std::string stdlibPath; - if (!codonLib.empty()) - stdlibPath = fs::path(tomlPath).replace_filename(codonLib).string(); + if (!codonLib.empty()) { + llvm::SmallString<128> p = llvm::sys::path::parent_path(tomlPath.str()); + llvm::sys::path::append(p, codonLib); + stdlibPath = p.str(); + } DSL::Info info = {about["name"].value_or(""), about["description"].value_or(""), about["version"].value_or(""), about["url"].value_or(""), @@ -80,13 +87,13 @@ llvm::Expected PluginManager::load(const std::string &path) { &libLoadErrorMsg); if (!handle.isValid()) return pluginError(fmt::format( - "[llvm::sys::DynamicLibrary::getPermanentLibrary(\"{}\", ...)] {}", dylibPath, - libLoadErrorMsg)); + "[llvm::sys::DynamicLibrary::getPermanentLibrary(\"{}\", ...)] {}", + dylibPath.str(), libLoadErrorMsg)); auto *entry = (LoadFunc *)handle.getAddressOfSymbol("load"); if (!entry) - return pluginError( - fmt::format("could not find 'load' in plugin shared library: {}", dylibPath)); + return pluginError(fmt::format( + "could not find 'load' in plugin shared library: {}", dylibPath.str())); auto dsl = (*entry)(); plugins.push_back(std::make_unique(std::move(dsl), info, handle)); diff --git a/codon/parser/common.cpp b/codon/parser/common.cpp index 3846b202..e0e574a8 100644 --- a/codon/parser/common.cpp +++ b/codon/parser/common.cpp @@ -5,6 +5,8 @@ #include "codon/parser/common.h" #include "codon/util/fmt/format.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" namespace codon { namespace ast { @@ -194,28 +196,29 @@ std::string executable_path(const char *argv0) { std::string executable_path(const char *argv0) { return std::string(argv0); } #endif -namespace fs = std::experimental::filesystem; - namespace { -void addPath(std::vector &paths, const fs::path &path) { - if (fs::exists(path)) - paths.push_back(fs::canonical(path)); + +void addPath(std::vector &paths, const std::string &path) { + if (llvm::sys::fs::exists(path)) + paths.push_back(getAbsolutePath(path)); } -std::vector getStdLibPaths(const std::string &argv0, - const std::vector &plugins) { - std::vector paths; +std::vector getStdLibPaths(const std::string &argv0, + const std::vector &plugins) { + std::vector paths; if (auto c = getenv("CODON_PATH")) { - addPath(paths, fs::path(std::string(c))); + addPath(paths, c); } if (!argv0.empty()) { - auto base = fs::path(executable_path(argv0.c_str())); + auto base = executable_path(argv0.c_str()); for (auto loci : {"../lib/codon/stdlib", "../stdlib", "stdlib"}) { - addPath(paths, base.parent_path() / loci); + auto path = llvm::SmallString<128>(llvm::sys::path::parent_path(base)); + llvm::sys::path::append(path, loci); + addPath(paths, std::string(path)); } } for (auto &path : plugins) { - addPath(paths, fs::path(path)); + addPath(paths, path); } return paths; } @@ -243,28 +246,44 @@ ImportFile getRoot(const std::string argv0, const std::vector &plug } } // namespace +std::string getAbsolutePath(const std::string &path) { + llvm::SmallString<128> p(path); + llvm::sys::fs::make_absolute(p); + return std::string(p); +} + std::shared_ptr getImportFile(const std::string &argv0, const std::string &what, const std::string &relativeTo, bool forceStdlib, const std::string &module0, const std::vector &plugins) { - std::vector paths; + std::vector paths; if (what != "") { - auto parentRelativeTo = fs::path(relativeTo).parent_path(); + auto parentRelativeTo = llvm::sys::path::parent_path(relativeTo); if (!forceStdlib) { - addPath(paths, (parentRelativeTo / what).replace_extension("codon")); - addPath(paths, parentRelativeTo / what / "__init__.codon"); + auto path = llvm::SmallString<128>(parentRelativeTo); + llvm::sys::path::append(path, what); + llvm::sys::path::replace_extension(path, "codon"); + addPath(paths, std::string(path)); + path = llvm::SmallString<128>(parentRelativeTo); + llvm::sys::path::append(path, what, "__init__.codon"); + addPath(paths, std::string(path)); } } for (auto &p : getStdLibPaths(argv0, plugins)) { - addPath(paths, (p / what).replace_extension("codon")); - addPath(paths, p / what / "__init__.codon"); + auto path = llvm::SmallString<128>(p); + llvm::sys::path::append(path, what); + llvm::sys::path::replace_extension(path, "codon"); + addPath(paths, std::string(path)); + path = llvm::SmallString<128>(p); + llvm::sys::path::append(path, what, "__init__.codon"); + addPath(paths, std::string(path)); } - auto module0Root = fs::path(module0).parent_path().string(); + auto module0Root = llvm::sys::path::parent_path(module0).str(); return paths.empty() ? nullptr : std::make_shared( - getRoot(argv0, plugins, module0Root, paths[0].string())); + getRoot(argv0, plugins, module0Root, paths[0])); } } // namespace ast diff --git a/codon/parser/common.h b/codon/parser/common.h index 8c1be3a5..568ab875 100644 --- a/codon/parser/common.h +++ b/codon/parser/common.h @@ -167,6 +167,9 @@ template std::vector clone_nop(const std::vector &t) { /// Path utilities +/// @return The absolute path of a given path. +std::string getAbsolutePath(const std::string &path); + /// Detect a absolute path of the current executable (whose argv0 is known). /// @return Absolute executable path or argv0 if one cannot be found. std::string executable_path(const char *argv0); diff --git a/codon/parser/visitors/doc/doc.cpp b/codon/parser/visitors/doc/doc.cpp index e67ac1e4..4c9825f6 100644 --- a/codon/parser/visitors/doc/doc.cpp +++ b/codon/parser/visitors/doc/doc.cpp @@ -1,6 +1,5 @@ #include "doc.h" -#include #include #include #include @@ -116,7 +115,7 @@ std::shared_ptr DocVisitor::apply(const std::string &argv0, auto ctx = std::make_shared(shared); for (auto &f : files) { - auto path = std::experimental::filesystem::canonical(std::experimental::filesystem::path(f)).string(); + auto path = getAbsolutePath(f); ctx->setFilename(path); ast = ast::parseFile(shared->cache, path); // LOG("parsing {}", f); diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index 65b5e604..2f4bb937 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -34,8 +34,7 @@ ir::Func *TranslateVisitor::apply(Cache *cache, StmtPtr stmts) { main->setJIT(); } else { main = cast(cache->module->getMainFunc()); - auto path = - std::experimental::filesystem::canonical(std::experimental::filesystem::path(cache->module0)).string(); + auto path = getAbsolutePath(cache->module0); main->setSrcInfo({path, 0, 0, 0}); } diff --git a/codon/util/common.h b/codon/util/common.h index 80661490..73fdc851 100644 --- a/codon/util/common.h +++ b/codon/util/common.h @@ -1,7 +1,7 @@ #pragma once +#include "llvm/Support/Path.h" #include -#include #include #include @@ -123,7 +123,7 @@ struct SrcInfo { SrcInfo() : SrcInfo("", 0, 0, 0) {} friend std::ostream &operator<<(std::ostream &out, const codon::SrcInfo &src) { - out << std::experimental::filesystem::path(src.file).filename() << ":" << src.line << ":" + out << llvm::sys::path::filename(src.file).str() << ":" << src.line << ":" << src.col; return out; } diff --git a/extra/jupyter/jupyter.cpp b/extra/jupyter/jupyter.cpp index cb3f0332..eb068f28 100644 --- a/extra/jupyter/jupyter.cpp +++ b/extra/jupyter/jupyter.cpp @@ -53,17 +53,6 @@ nl::json CodonJupyter::execute_request_impl(int execution_counter, const string }); if (failed.empty()) { std::string out = *result; - // std::string out; - // for (int i = 0; i < msg.size(); i++) { - // uint8_t c = msg[i]; - // if (c <= 127) { - // out.push_back(c); - // } else { - // out.push_back((c >> 6) | 0xC0); - // out.push_back((c & 0x3F) | 0x80); - // } - // } - nl::json pub_data; if (ast::startswith(out, "\x00\x00__codon/mime__\x00")) { std::string mime = ""; From 4bfab60ac641860a86836e110e2807b3b0717101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 25 Feb 2022 09:19:35 -0800 Subject: [PATCH 56/61] Fix getAbsolutePath (use POSIX realpath) --- codon/parser/common.cpp | 9 ++++++--- codon/parser/common.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/codon/parser/common.cpp b/codon/parser/common.cpp index e0e574a8..34e9a519 100644 --- a/codon/parser/common.cpp +++ b/codon/parser/common.cpp @@ -247,9 +247,12 @@ ImportFile getRoot(const std::string argv0, const std::vector &plug } // namespace std::string getAbsolutePath(const std::string &path) { - llvm::SmallString<128> p(path); - llvm::sys::fs::make_absolute(p); - return std::string(p); + char *c = realpath(path.c_str(), nullptr); + if (!c) + return path; + std::string result(c); + free(c); + return result; } std::shared_ptr getImportFile(const std::string &argv0, diff --git a/codon/parser/common.h b/codon/parser/common.h index 568ab875..4978e64b 100644 --- a/codon/parser/common.h +++ b/codon/parser/common.h @@ -167,7 +167,7 @@ template std::vector clone_nop(const std::vector &t) { /// Path utilities -/// @return The absolute path of a given path. +/// @return The absolute canonical path of a given path. std::string getAbsolutePath(const std::string &path); /// Detect a absolute path of the current executable (whose argv0 is known). From c8b757859ad25edd311fba750674bffd7191e529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Fri, 25 Feb 2022 18:25:29 -0800 Subject: [PATCH 57/61] Fix OpenMP 12+ issue --- cmake/deps.cmake | 2 +- codon/dsl/plugins.cpp | 2 +- codon/runtime/lib.cpp | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index 7ea5ddfe..201314fc 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -68,7 +68,7 @@ endif() CPMAddPackage( NAME openmp GITHUB_REPOSITORY "exaloop/openmp" - VERSION 13.0.0-fb9fc3c + VERSION 12.0.1 OPTIONS "CMAKE_BUILD_TYPE Release" "OPENMP_ENABLE_LIBOMPTARGET OFF" "OPENMP_STANDALONE_BUILD ON") diff --git a/codon/dsl/plugins.cpp b/codon/dsl/plugins.cpp index b259dadf..897aab68 100644 --- a/codon/dsl/plugins.cpp +++ b/codon/dsl/plugins.cpp @@ -7,7 +7,7 @@ #include "codon/util/semver/semver.h" #include "codon/util/toml++/toml.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Support/Filesystem.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" namespace codon { diff --git a/codon/runtime/lib.cpp b/codon/runtime/lib.cpp index 258c6a17..2aa7ebfd 100644 --- a/codon/runtime/lib.cpp +++ b/codon/runtime/lib.cpp @@ -54,6 +54,9 @@ SEQ_FUNC void seq_init(int flags) { GC_INIT(); GC_set_warn_proc(GC_ignore_warn_proc); GC_allow_register_threads(); + // Disable libopenmp 12+'s hidden helper threads that + // cause segfaults with omp_critical in Codon + setenv("LIBOMP_NUM_HIDDEN_HELPER_THREADS", "0", true); // equivalent to: #pragma omp parallel { register_thread } __kmpc_fork_call(&dummy_loc, 0, (kmpc_micro)register_thread); seq_exc_init(); From f40d27836bc10abfbe8b31b28f763f77b1af8002 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Sun, 27 Feb 2022 07:52:28 -0500 Subject: [PATCH 58/61] clang-format --- codon/parser/ast/expr.h | 10 +--------- codon/parser/visitors/translate/translate_ctx.h | 2 +- codon/sir/analyze/dataflow/cfg.h | 3 ++- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/codon/parser/ast/expr.h b/codon/parser/ast/expr.h index a9682a89..b4f8a11c 100644 --- a/codon/parser/ast/expr.h +++ b/codon/parser/ast/expr.h @@ -673,15 +673,7 @@ struct StackAllocExpr : Expr { #undef ACCEPT -enum ExprAttr { - SequenceItem, - StarSequenceItem, - List, - Set, - Dict, - Partial, - __LAST__ -}; +enum ExprAttr { SequenceItem, StarSequenceItem, List, Set, Dict, Partial, __LAST__ }; } // namespace ast } // namespace codon diff --git a/codon/parser/visitors/translate/translate_ctx.h b/codon/parser/visitors/translate/translate_ctx.h index 12b34704..49c0e5b5 100644 --- a/codon/parser/visitors/translate/translate_ctx.h +++ b/codon/parser/visitors/translate/translate_ctx.h @@ -51,7 +51,7 @@ struct TranslateContext : public Context { /// Stack of IR series (blocks). std::vector series; /// Stack of sequence items for attribute initialization. - std::vector>> seqItems; + std::vector>> seqItems; public: TranslateContext(Cache *cache); diff --git a/codon/sir/analyze/dataflow/cfg.h b/codon/sir/analyze/dataflow/cfg.h index 1294067d..9946b259 100644 --- a/codon/sir/analyze/dataflow/cfg.h +++ b/codon/sir/analyze/dataflow/cfg.h @@ -481,7 +481,8 @@ public: void visit(const dsl::CustomInstr *v) override; template void process(const NodeType *v) { - if (!v) return; + if (!v) + return; if (seenIds.find(v->getId()) != seenIds.end()) return; seenIds.insert(v->getId()); From a3be465ae5082f72b99e9196da78a4a0ad331df7 Mon Sep 17 00:00:00 2001 From: "A. R. Shajii" Date: Sun, 27 Feb 2022 07:52:43 -0500 Subject: [PATCH 59/61] Use patched OpenMP --- cmake/deps.cmake | 2 +- codon/runtime/lib.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/cmake/deps.cmake b/cmake/deps.cmake index 201314fc..31a3efde 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -68,7 +68,7 @@ endif() CPMAddPackage( NAME openmp GITHUB_REPOSITORY "exaloop/openmp" - VERSION 12.0.1 + VERSION 13.0.0-patch1 OPTIONS "CMAKE_BUILD_TYPE Release" "OPENMP_ENABLE_LIBOMPTARGET OFF" "OPENMP_STANDALONE_BUILD ON") diff --git a/codon/runtime/lib.cpp b/codon/runtime/lib.cpp index 2aa7ebfd..258c6a17 100644 --- a/codon/runtime/lib.cpp +++ b/codon/runtime/lib.cpp @@ -54,9 +54,6 @@ SEQ_FUNC void seq_init(int flags) { GC_INIT(); GC_set_warn_proc(GC_ignore_warn_proc); GC_allow_register_threads(); - // Disable libopenmp 12+'s hidden helper threads that - // cause segfaults with omp_critical in Codon - setenv("LIBOMP_NUM_HIDDEN_HELPER_THREADS", "0", true); // equivalent to: #pragma omp parallel { register_thread } __kmpc_fork_call(&dummy_loc, 0, (kmpc_micro)register_thread); seq_exc_init(); From 3938b46a9a6a9ccf9bc1d7c7285466f18c2e5f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Sun, 27 Feb 2022 08:46:15 -0800 Subject: [PATCH 60/61] Fix stray print issue --- codon/parser/visitors/translate/translate.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index 67aabde4..9123b44e 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -87,7 +87,6 @@ ir::Value *TranslateVisitor::transform(const ExprPtr &expr) { } if (expr->hasAttr(ExprAttr::Dict)) { std::vector v; - LOG("{} {}", expr->toString(), expr->getSrcInfo()); for (int pi = 0; pi < ctx->seqItems.back().size(); pi++) { auto &p = ctx->seqItems.back()[pi]; if (p.first == ExprAttr::StarSequenceItem) { From ef51677acf0d161a263ec65c0245ea88323c16d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Mon, 28 Feb 2022 09:37:39 -0800 Subject: [PATCH 61/61] Remove debug comments [skip ci] --- codon/parser/cache.cpp | 1 - codon/parser/peg/peg.cpp | 3 +- codon/parser/visitors/doc/doc.cpp | 1 - codon/parser/visitors/format/format.cpp | 9 ------ codon/parser/visitors/format/format.h | 3 -- .../visitors/simplify/simplify_stmt.cpp | 5 +--- codon/parser/visitors/translate/translate.cpp | 3 -- codon/parser/visitors/typecheck/typecheck.cpp | 6 ---- .../visitors/typecheck/typecheck_ctx.cpp | 10 ++----- .../visitors/typecheck/typecheck_expr.cpp | 28 +------------------ .../visitors/typecheck/typecheck_infer.cpp | 1 - 11 files changed, 5 insertions(+), 65 deletions(-) diff --git a/codon/parser/cache.cpp b/codon/parser/cache.cpp index f660cea1..1af811eb 100644 --- a/codon/parser/cache.cpp +++ b/codon/parser/cache.cpp @@ -111,7 +111,6 @@ ir::Func *Cache::realizeFunction(types::FuncTypePtr type, } } } - // LOG("--> realizing {}", type->debugString(1)); int oldAge = typeCtx->age; typeCtx->age = 99999; auto tv = TypecheckVisitor(typeCtx); diff --git a/codon/parser/peg/peg.cpp b/codon/parser/peg/peg.cpp index 80ef9f49..48a02d27 100644 --- a/codon/parser/peg/peg.cpp +++ b/codon/parser/peg/peg.cpp @@ -109,9 +109,8 @@ StmtPtr parseFile(Cache *cache, const std::string &file) { cache->imports[file].content = lines; auto result = parseCode(cache, file, code); + // For debugging purposes: // LOG("peg/{} := {}", file, result ? result->toString(0) : ""); - // throw; - // LOG("fmt := {}", FormatVisitor::apply(result)); return result; } diff --git a/codon/parser/visitors/doc/doc.cpp b/codon/parser/visitors/doc/doc.cpp index 4c9825f6..264502b1 100644 --- a/codon/parser/visitors/doc/doc.cpp +++ b/codon/parser/visitors/doc/doc.cpp @@ -118,7 +118,6 @@ std::shared_ptr DocVisitor::apply(const std::string &argv0, auto path = getAbsolutePath(f); ctx->setFilename(path); ast = ast::parseFile(shared->cache, path); - // LOG("parsing {}", f); DocVisitor(ctx).transformModule(std::move(ast)); } diff --git a/codon/parser/visitors/format/format.cpp b/codon/parser/visitors/format/format.cpp index 434f0d00..3fbcd4fa 100644 --- a/codon/parser/visitors/format/format.cpp +++ b/codon/parser/visitors/format/format.cpp @@ -406,15 +406,6 @@ void FormatVisitor::visit(FunctionStmt *fstmt) { } void FormatVisitor::visit(ClassStmt *stmt) { - // if (cache && - // cache->realizationAsts.find(fstmt->name) != cache->realizationAsts.end()) { - // fstmt = (const FunctionStmt *)(cache->realizationAsts[fstmt->name].get()); - // } else if (cache) { - // for (auto &real : cache->realizations[fstmt->name]) - // result += simplify(cache->realizationAsts[real.first]); - // return; - // } - std::vector attrs; if (!stmt->attributes.has(Attr::Extend)) diff --git a/codon/parser/visitors/format/format.h b/codon/parser/visitors/format/format.h index 2f0ffd02..e58ce6d2 100644 --- a/codon/parser/visitors/format/format.h +++ b/codon/parser/visitors/format/format.h @@ -30,9 +30,6 @@ class FormatVisitor : public CallbackASTVisitor { private: template std::string renderExpr(T &&t, Ts &&...args) { std::string s; - // if (renderType) - // s += fmt::format("{}{}{}", typeStart, - // t->getType() ? t->getType()->toString() : "-", typeEnd); return fmt::format("{}{}{}{}{}{}", exprStart, s, nodeStart, fmt::format(args...), nodeEnd, exprEnd); } diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 40c350c4..c27dffb7 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -883,7 +883,6 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { StmtPtr autoDeducedInit = nullptr; Stmt *firstInit = nullptr; if (deduce && args.empty() && !extension) { - // LOG("deducing {}", stmt->name); for (auto sp : getClassMethods(stmt->suite)) if (sp && sp->getFunction()) { firstInit = sp.get(); @@ -910,7 +909,6 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { ctx->cache->classes[canonicalName].fields.push_back({m, nullptr}); args.emplace_back(Param{m, N(varName), nullptr}); argSubstitutions.push_back(substitutions.size() - 1); - // LOG("deduction: {}: {} <-> {}", stmt->name, m, name); } ctx->bases.back().deducedMembers = nullptr; break; @@ -1069,8 +1067,7 @@ void SimplifyVisitor::visit(ClassStmt *stmt) { seqassert(c, "not a class AST for {}", canonicalName); preamble->globals.push_back(c->clone()); c->suite = clone(suite); - // if (stmt->baseClasses.size()) - // LOG("{} -> {}", stmt->name, c->toString(0)); + } stmts[0] = N(canonicalName, std::vector{}, N(), Attr({Attr::Extend}), std::vector{}, diff --git a/codon/parser/visitors/translate/translate.cpp b/codon/parser/visitors/translate/translate.cpp index 9123b44e..055add0f 100644 --- a/codon/parser/visitors/translate/translate.cpp +++ b/codon/parser/visitors/translate/translate.cpp @@ -64,8 +64,6 @@ ir::Value *TranslateVisitor::transform(const ExprPtr &expr) { } if (expr->hasAttr(ExprAttr::Partial)) p = expr->type->getPartial().get(); - // LOG("{} {}: {}", std::string(ctx->seqItems.size(), ' '), expr->attributes, - // expr->toString()); } expr->accept(v); @@ -501,7 +499,6 @@ void TranslateVisitor::transformFunction(types::FuncType *type, FunctionStmt *as std::map attr; attr[".module"] = ast->attributes.module; for (auto &a : ast->attributes.customAttr) { - // LOG("{} -> {}", ast->name, a); attr[a] = ""; } func->setAttribute(std::make_unique(attr)); diff --git a/codon/parser/visitors/typecheck/typecheck.cpp b/codon/parser/visitors/typecheck/typecheck.cpp index 16a6dda7..315da1a6 100644 --- a/codon/parser/visitors/typecheck/typecheck.cpp +++ b/codon/parser/visitors/typecheck/typecheck.cpp @@ -46,14 +46,8 @@ TypePtr TypecheckVisitor::unify(TypePtr &a, const TypePtr &b, bool undoOnSuccess } else { undo.undo(); } - // LOG("{} / {}", a->debugString(true), b->debugString(true)); if (!undoOnSuccess) a->unify(b.get(), &undo); - // if (format("cannot unify {} and {}", a->toString(), b->toString()) == - // "cannot unify ._lambda_82:0[...] and T2") { - // LOG("cannot unify {} and {}", a->debugString(1), b->debugString(1)); - // a->unify(b.get(), &undo); - // } error("cannot unify {} and {}", a->toString(), b->toString()); return nullptr; } diff --git a/codon/parser/visitors/typecheck/typecheck_ctx.cpp b/codon/parser/visitors/typecheck/typecheck_ctx.cpp index aac6eb51..4863cbc2 100644 --- a/codon/parser/visitors/typecheck/typecheck_ctx.cpp +++ b/codon/parser/visitors/typecheck/typecheck_ctx.cpp @@ -78,8 +78,8 @@ std::shared_ptr TypeContext::addUnbound(const Expr *expr, int level, bool setActive, char staticType) { auto t = std::make_shared( types::LinkType::Unbound, cache->unboundCount++, level, nullptr, staticType); - // if (t->id == 7815) - // LOG("debug"); + // Keep it for debugging purposes: + // if (t->id == 7815) LOG("debug"); t->setSrcInfo(expr->getSrcInfo()); LOG_TYPECHECK("[ub] new {}: {} ({})", t->debugString(true), expr->toString(), setActive); @@ -202,17 +202,11 @@ int TypeContext::reorderNamedArgs(types::FuncType *func, int starArgIndex = -1, kwstarArgIndex = -1; for (int i = 0; i < func->ast->args.size(); i++) { - // if (!known.empty() && known[i] && !partial) - // continue; if (startswith(func->ast->args[i].name, "**")) kwstarArgIndex = i, score -= 2; else if (startswith(func->ast->args[i].name, "*")) starArgIndex = i, score -= 2; } - // seqassert(known.empty() || starArgIndex == -1 || !known[starArgIndex], - // "partial *args"); - // seqassert(known.empty() || kwstarArgIndex == -1 || !known[kwstarArgIndex], - // "partial **kwargs"); // 1. Assign positional arguments to slots // Each slot contains a list of arg's indices diff --git a/codon/parser/visitors/typecheck/typecheck_expr.cpp b/codon/parser/visitors/typecheck/typecheck_expr.cpp index 232f0ee9..3337c691 100644 --- a/codon/parser/visitors/typecheck/typecheck_expr.cpp +++ b/codon/parser/visitors/typecheck/typecheck_expr.cpp @@ -868,9 +868,7 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, return transform( N(N(expr->expr, "_getattr"), N(expr->member))); } else { - // For debugging purposes: - if (expr->member == "ticker") - ctx->findMethod(typ->name, expr->member); + // For debugging purposes: ctx->findMethod(typ->name, expr->member); error("cannot find '{}' in {}", expr->member, typ->toString()); } } @@ -1020,7 +1018,6 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in ai--; } else { // Case 3: Normal argument - // LOG("-> {}", expr->args[ai].value->toString()); expr->args[ai].value = transform(expr->args[ai].value, true); // Unbound inType might become a generator that will need to be extracted, so // don't unify it yet. @@ -1046,8 +1043,6 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in ctx->bases.back().supers, expr->args); if (m.empty()) error("no matching superf methods are available"); - // LOG("found {} <- {}", ctx->bases.back().type->getFunc()->toString(), - // m[0]->toString()); ExprPtr e = N(N(m[0]->ast->name), expr->args); return transform(e, false, true); } @@ -1285,7 +1280,6 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in } auto e = transform(expr->expr); unify(expr->type, e->getType()); - // LOG("-- {} / {}", e->toString(), e->type->debugString(true)); return e; } @@ -1397,19 +1391,6 @@ ExprPtr TypecheckVisitor::transformCall(CallExpr *expr, const types::TypePtr &in // Case 2. Normal function call. expr->args = args; unify(expr->type, calleeFn->args[0]); // function return type - - // HACK: Intercept Partial.__new__ and replace it with partial type - // TODO: needs cleaner logic for this. Maybe just use normal record type - // and just track partialized function args, not the whole function as it's done - // now. Major caveat: needs rewiring of the function generic partialization logic. - // if (startswith(calleeFn->ast->name, TYPE_PARTIAL) && - // endswith(calleeFn->ast->name, ".__new__:0")) { - // seqassert(expr->type->getRecord(), "expected a partial record"); - // auto r = expr->type->getRecord(); - // expr->type = std::make_shared(r, - // ctx->cache->partials[r->name].first, - // ctx->cache->partials[r->name].second); - // } return nullptr; } } @@ -1837,12 +1818,6 @@ TypecheckVisitor::findMatchingMethods(types::ClassType *typ, } } if (score != -1) { - // std::vector ar; - // for (auto &a: args) { - // if (a.first.empty()) ar.push_back(a.second->toString()); - // else ar.push_back(format("{}: {}", a.first, a.second->toString())); - // } - // LOG("- {} vs {}", m->toString(), join(ar, "; ")); results.push_back(methods[mi]); } } @@ -1969,7 +1944,6 @@ types::FuncTypePtr TypecheckVisitor::findDispatch(const std::string &fn) { ctx->cache->functions[name].ast = ast; ctx->cache->functions[name].type = typ; prependStmts->push_back(ast); - // LOG("dispatch: {}", ast->toString(1)); return typ; } diff --git a/codon/parser/visitors/typecheck/typecheck_infer.cpp b/codon/parser/visitors/typecheck/typecheck_infer.cpp index 7e77e1f4..879783b6 100644 --- a/codon/parser/visitors/typecheck/typecheck_infer.cpp +++ b/codon/parser/visitors/typecheck/typecheck_infer.cpp @@ -288,7 +288,6 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type) { irType->setAstType(type->getFunc()); r->ir->realize(irType, names); - // LOG("-> {}", *(r->ir)); ctx->cache->functions[type->ast->name].realizations[type->realizedName()] = r; } else { ctx->cache->functions[type->ast->name].realizations[oldKey] =