diff --git a/codon/parser/ast/stmt.cpp b/codon/parser/ast/stmt.cpp index 78653dff..0bcffd8c 100644 --- a/codon/parser/ast/stmt.cpp +++ b/codon/parser/ast/stmt.cpp @@ -646,6 +646,7 @@ void ClassStmt::parseDecorators() { attributes.customAttr.insert("deduce"); } else if (d->isId("__notuple__")) { attributes.customAttr.insert("__notuple__"); + } else if (d->isId("dataclass")) { } else if (auto c = d->getCall()) { if (c->expr->isId(Attr::Tuple)) { attributes.set(Attr::Tuple); diff --git a/codon/parser/visitors/typecheck/access.cpp b/codon/parser/visitors/typecheck/access.cpp index 248bfaa0..309b34a3 100644 --- a/codon/parser/visitors/typecheck/access.cpp +++ b/codon/parser/visitors/typecheck/access.cpp @@ -35,8 +35,8 @@ void TypecheckVisitor::visit(IdExpr *expr) { // ctx->getBase()->captures.insert(expr->value); if (!val) { ctx->dump(); - // LOG("================================================================="); - // ctx->cache->typeCtx->dump(); + LOG("================================================================="); + ctx->cache->typeCtx->dump(); E(Error::ID_NOT_FOUND, expr, expr->value); } if (expr->type->getUnbound() && in(ctx->cache->overloads, val->canonicalName)) diff --git a/codon/parser/visitors/typecheck/assign.cpp b/codon/parser/visitors/typecheck/assign.cpp index 2a0dd6e9..903f86dd 100644 --- a/codon/parser/visitors/typecheck/assign.cpp +++ b/codon/parser/visitors/typecheck/assign.cpp @@ -57,6 +57,7 @@ void TypecheckVisitor::visit(DelStmt *stmt) { if (ctx->getScope() != val->scope) E(Error::DEL_NOT_ALLOWED, ei, ei->value); ctx->remove(ei->value); + ctx->remove(ctx->cache->rev(ei->value)); } else { E(Error::DEL_INVALID, stmt); } @@ -99,13 +100,15 @@ StmtPtr TypecheckVisitor::transformAssignment(AssignStmt *stmt, bool mustExist) // Make sure that existing values that cannot be shadowed are only updated // mustExist |= val && !ctx->isOuter(val); if (mustExist) { - if (val && val->isVar() && !ctx->isOuter(val)) { + if (val && val->isVar() /*&& !ctx->isOuter(val)*/) { + // commented out: should be handled by namevisitor auto s = N(stmt->lhs, stmt->rhs); if (ctx->getBase()->attributes && ctx->getBase()->attributes->has(Attr::Atomic)) s->setAtomicUpdate(); else s->setUpdate(); - transformUpdate(s.get()); + if (auto u = transformUpdate(s.get())) + return u; return s; } else { E(Error::ASSIGN_LOCAL_REFERENCE, e, e->value, e->getSrcInfo()); @@ -172,7 +175,7 @@ StmtPtr TypecheckVisitor::transformAssignment(AssignStmt *stmt, bool mustExist) /// Transform binding updates. Special handling is done for atomic or in-place /// statements (e.g., `a += b`). /// See @c transformInplaceUpdate and @c wrapExpr for details. -void TypecheckVisitor::transformUpdate(AssignStmt *stmt) { +StmtPtr TypecheckVisitor::transformUpdate(AssignStmt *stmt) { transform(stmt->lhs); if (stmt->lhs->isStatic()) E(Error::ASSIGN_UNEXPECTED_STATIC, stmt->lhs); @@ -189,11 +192,12 @@ void TypecheckVisitor::transformUpdate(AssignStmt *stmt) { auto [inPlace, inPlaceExpr] = transformInplaceUpdate(stmt); if (inPlace) { if (inPlaceExpr) { - resultStmt = N(inPlaceExpr); + auto s = N(inPlaceExpr); if (inPlaceExpr->isDone()) - resultStmt->setDone(); + s->setDone(); + return s; } - return; + return nullptr; } transform(stmt->rhs); @@ -202,6 +206,7 @@ void TypecheckVisitor::transformUpdate(AssignStmt *stmt) { unify(stmt->rhs->type, stmt->lhs->type); if (stmt->rhs->done && realize(stmt->lhs->type)) stmt->setDone(); + return nullptr; } /// Typecheck instance member assignments (e.g., `a.b = c`) and handle optional @@ -282,19 +287,22 @@ std::pair TypecheckVisitor::transformInplaceUpdate(AssignStmt *st auto lhsClass = stmt->lhs->getType()->getClass(); auto call = stmt->rhs->getCall(); if (stmt->isAtomicUpdate() && call && stmt->lhs->getId() && - (call->expr->isId("min") || call->expr->isId("max")) && call->args.size() == 2 && - call->args[0].value->isId(std::string(stmt->lhs->getId()->value))) { - // `type(a).__atomic_min__(__ptr__(a), b)` - auto ptrTyp = ctx->instantiateGeneric(stmt->lhs->getSrcInfo(), - ctx->forceFind("Ptr")->type, {lhsClass}); - call->args[1].value = transform(call->args[1].value); - auto rhsTyp = call->args[1].value->getType()->getClass(); - if (auto method = findBestMethod( - lhsClass, format("__atomic_{}__", call->expr->getId()->value), - {ptrTyp, rhsTyp})) { - return {true, transform(N(N(method->ast->name), - N(N("__ptr__"), stmt->lhs), - call->args[1].value))}; + (call->expr->isId("min") || call->expr->isId("max")) && call->args.size() == 2) { + transform(call->args[0].value); + if (call->args[0].value->isId(std::string(stmt->lhs->getId()->value))) { + // `type(a).__atomic_min__(__ptr__(a), b)` + auto ptrTyp = ctx->instantiateGeneric(stmt->lhs->getSrcInfo(), + ctx->forceFind("Ptr")->type, {lhsClass}); + call->args[1].value = transform(call->args[1].value); + auto rhsTyp = call->args[1].value->getType()->getClass(); + if (auto method = findBestMethod( + lhsClass, format("__atomic_{}__", call->expr->getId()->value), + {ptrTyp, rhsTyp})) { + return {true, + transform(N(N(method->ast->name), + N(N("__ptr__"), stmt->lhs), + call->args[1].value))}; + } } } diff --git a/codon/parser/visitors/typecheck/function.cpp b/codon/parser/visitors/typecheck/function.cpp index b9765b04..728e58ff 100644 --- a/codon/parser/visitors/typecheck/function.cpp +++ b/codon/parser/visitors/typecheck/function.cpp @@ -89,29 +89,29 @@ void TypecheckVisitor::visit(YieldFromStmt *stmt) { /// Process `global` statements. Remove them upon completion. void TypecheckVisitor::visit(GlobalStmt *stmt) { - if (!ctx->inFunction()) - E(Error::FN_OUTSIDE_ERROR, stmt, stmt->nonLocal ? "nonlocal" : "global"); + // if (!ctx->inFunction()) + // E(Error::FN_OUTSIDE_ERROR, stmt, stmt->nonLocal ? "nonlocal" : "global"); - // Dominate the binding - auto val = ctx->find(stmt->var); - if (!val || !val->isVar()) - E(Error::ID_NOT_FOUND, stmt, stmt->var); - if (val->getBaseName() == ctx->getBaseName()) - E(Error::FN_GLOBAL_ASSIGNED, stmt, stmt->var); + // // Dominate the binding + // auto val = ctx->find(stmt->var); + // if (!val || !val->isVar()) + // E(Error::ID_NOT_FOUND, stmt, stmt->var); + // if (val->getBaseName() == ctx->getBaseName()) + // E(Error::FN_GLOBAL_ASSIGNED, stmt, stmt->var); - // Check global/nonlocal distinction - if (!stmt->nonLocal && !val->getBaseName().empty()) - E(Error::FN_GLOBAL_NOT_FOUND, stmt, "global", stmt->var); - else if (stmt->nonLocal && val->getBaseName().empty()) - E(Error::FN_GLOBAL_NOT_FOUND, stmt, "nonlocal", stmt->var); - seqassert(!val->canonicalName.empty(), "'{}' does not have a canonical name", - stmt->var); + // // Check global/nonlocal distinction + // if (!stmt->nonLocal && !val->getBaseName().empty()) + // E(Error::FN_GLOBAL_NOT_FOUND, stmt, "global", stmt->var); + // else if (stmt->nonLocal && val->getBaseName().empty()) + // E(Error::FN_GLOBAL_NOT_FOUND, stmt, "nonlocal", stmt->var); + // seqassert(!val->canonicalName.empty(), "'{}' does not have a canonical name", + // stmt->var); - // Register as global if needed - ctx->cache->addGlobal(val->canonicalName); + // // Register as global if needed + // ctx->cache->addGlobal(val->canonicalName); - val = ctx->addVar(stmt->var, val->canonicalName, val->type); - val->baseName = ctx->getBaseName(); + // val = ctx->addVar(stmt->var, val->canonicalName, val->type); + // val->baseName = ctx->getBaseName(); // Erase the statement resultStmt = N(); } @@ -179,9 +179,9 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { // Handle captures. Add additional argument to the function for every capture. // Make sure to account for **kwargs if present std::map captures; - for (auto &[c, _] : stmt->attributes.captures) { + for (auto &[c, t] : stmt->attributes.captures) { if (auto v = ctx->find(c)) { - if (!v->isGlobal() && !v->isGeneric()) { + if (t != Attr::CaptureType::Global && !v->isGlobal() && !v->isGeneric()) { captures[c] = v; } } diff --git a/codon/parser/visitors/typecheck/infer.cpp b/codon/parser/visitors/typecheck/infer.cpp index 4fbbac5a..768f3ec1 100644 --- a/codon/parser/visitors/typecheck/infer.cpp +++ b/codon/parser/visitors/typecheck/infer.cpp @@ -50,9 +50,10 @@ StmtPtr TypecheckVisitor::inferTypes(StmtPtr result, bool isToplevel) { for (ctx->getBase()->iteration = 1;; ctx->getBase()->iteration++) { LOG_TYPECHECK("[iter] {} :: {}", ctx->getBase()->name, ctx->getBase()->iteration); - if (ctx->getBase()->iteration >= MAX_TYPECHECK_ITER) - error(result, "cannot typecheck '{}' in reasonable time", - ctx->cache->rev(ctx->getBase()->name)); + if (ctx->getBase()->iteration >= MAX_TYPECHECK_ITER) { + LOG("[error=>] {}", result->toString(2)); + error(result, "cannot typecheck '{}' in reasonable time", ctx->getBase()->name); + } // Keep iterating until: // (1) success: the statement is marked as done; or diff --git a/codon/parser/visitors/typecheck/names.cpp b/codon/parser/visitors/typecheck/names.cpp index 8d5a8fa1..9218a46c 100644 --- a/codon/parser/visitors/typecheck/names.cpp +++ b/codon/parser/visitors/typecheck/names.cpp @@ -67,25 +67,78 @@ StmtPtr ScopingVisitor::transform(std::shared_ptr &stmt) { return stmt; } +void ScopingVisitor::switchToUpdate(std::shared_ptr binding, + const std::string &name, bool gotUsedVar) { + // These bindings (and their canonical identifiers) will be replaced by the + // dominating binding during the type checking pass. + auto used = format("{}.__used__", name); + if (auto s = std::dynamic_pointer_cast(binding)) { + seqassert(!s->stmts.empty() && s->stmts[0]->getAssign(), "bad suite"); + auto a = s->stmts[0]->getAssign(); + if (a->rhs) { + a->setUpdate(); + if (gotUsedVar && s->stmts.size() < 2) { + s->stmts.push_back(N(N(used), N(true), nullptr, + AssignStmt::UpdateMode::Update)); + } + } else { + s->stmts.clear(); + } + } else if (auto f = std::dynamic_pointer_cast(binding)) { + f->var->setAttr(ExprAttr::Dominated); + if (gotUsedVar) { + bool skip = false; + if (auto s = f->suite->firstInBlock()) + skip = s->getAssign() && s->getAssign()->lhs->isId(used); + if (!skip) { + f->suite = N(N(N(used), N(true), + nullptr, AssignStmt::UpdateMode::Update), + f->suite); + } + } + } else if (auto f = std::dynamic_pointer_cast(binding)) { + f->exc->setAttr(ExprAttr::Dominated); + if (gotUsedVar) { + bool skip = false; + if (auto s = f->suite->firstInBlock()) + skip = s->getAssign() && s->getAssign()->lhs->isId(used); + if (!skip) { + f->suite = N(N(N(used), N(true), + nullptr, AssignStmt::UpdateMode::Update), + f->suite); + } + } + } else if (binding) { + // class; function; func-arg; comprehension-arg; catch-name; import-name[anything + // really] + // todo)) generators?! + E(error::Error::ID_INVALID_BIND, binding, name); + } +} + void ScopingVisitor::visitName(const std::string &name, bool adding, const std::shared_ptr &root, const SrcInfo &src) { if (adding && ctx->inClass) return; if (adding) { - if (auto p = in(ctx->captures, name)) + if (auto p = in(ctx->captures, name)) { if (*p == Attr::CaptureType::Read) E(error::Error::ASSIGN_LOCAL_REFERENCE, ctx->firstSeen[name], name, src); - if (in(ctx->childCaptures, name) && ctx->functionScope) { - auto newScope = std::vector{ctx->scope[0].id}; - auto b = N(N(name), nullptr, nullptr); - auto newItem = ScopingVisitor::Context::Item(src, newScope, b); - ctx->scope.front().stmts.emplace_back(b); - ctx->map[name].push_back(newItem); + else if (root) // global, nonlocal + switchToUpdate(root, name, false); + } else { + if (in(ctx->childCaptures, name) && ctx->functionScope) { + auto newScope = std::vector{ctx->scope[0].id}; + auto b = N(N(name), nullptr, nullptr); + auto newItem = ScopingVisitor::Context::Item(src, newScope, b); + ctx->scope.front().stmts.emplace_back(b); + ctx->map[name].push_back(newItem); + } + ctx->map[name].emplace_front(src, ctx->getScope(), root); + if (!root) + ctx->temps.back().insert(name); } - ctx->map[name].emplace_front(src, ctx->getScope(), root); - if (!root) - ctx->temps.back().insert(name); } else { if (!in(ctx->firstSeen, name)) ctx->firstSeen[name] = src; @@ -220,56 +273,7 @@ ScopingVisitor::findDominatingBinding(const std::string &name, bool allowShadow) for (auto i = it->begin(); i != it->end(); i++) { if (i == lastGood) break; - // if (!(*i)->canDominate()) - // continue; - - // These bindings (and their canonical identifiers) will be replaced by the - // dominating binding during the type checking pass. - auto used = format("{}.__used__", name); - if (auto s = std::dynamic_pointer_cast(i->binding)) { - seqassert(!s->stmts.empty() && s->stmts[0]->getAssign(), "bad suite"); - auto a = s->stmts[0]->getAssign(); - if (a->rhs) { - a->setUpdate(); - if (gotUsedVar && s->stmts.size() < 2) { - s->stmts.push_back(N(N(used), N(true), nullptr, - AssignStmt::UpdateMode::Update)); - } - } else { - s->stmts.clear(); - } - } else if (auto f = std::dynamic_pointer_cast(i->binding)) { - f->var->setAttr(ExprAttr::Dominated); - if (gotUsedVar) { - bool skip = false; - if (auto s = f->suite->firstInBlock()) - skip = s->getAssign() && s->getAssign()->lhs->isId(used); - if (!skip) { - f->suite = - N(N(N(used), N(true), nullptr, - AssignStmt::UpdateMode::Update), - f->suite); - } - } - } else if (auto f = std::dynamic_pointer_cast(i->binding)) { - f->exc->setAttr(ExprAttr::Dominated); - if (gotUsedVar) { - bool skip = false; - if (auto s = f->suite->firstInBlock()) - skip = s->getAssign() && s->getAssign()->lhs->isId(used); - if (!skip) { - f->suite = - N(N(N(used), N(true), nullptr, - AssignStmt::UpdateMode::Update), - f->suite); - } - } - } else if (i->binding) { - // class; function; func-arg; comprehension-arg; catch-name; import-name[anything - // really] - // todo)) generators?! - E(error::Error::ID_INVALID_BIND, i->binding, name); - } + switchToUpdate(i->binding, name, gotUsedVar); } it->erase(it->begin(), lastGood); return &(*lastGood); diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index 2d899c70..24bb10d0 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -182,7 +182,7 @@ private: // Node typechecking rules /* Assignments (assign.cpp) */ void visit(AssignExpr *) override; void visit(AssignStmt *) override; - void transformUpdate(AssignStmt *); + StmtPtr transformUpdate(AssignStmt *); StmtPtr transformAssignment(AssignStmt *, bool = false); void unpackAssignments(const ExprPtr &, ExprPtr, std::vector &); void visit(DelStmt *) override; @@ -425,6 +425,7 @@ public: void transformBlock(StmtPtr &s); ExprPtr makeAnonFn(std::vector, const std::vector & = {}); + void switchToUpdate(std::shared_ptr binding, const std::string &, bool); }; } // namespace codon::ast diff --git a/stdlib/internal/__init_test__.codon b/stdlib/internal/__init_test__.codon index a4156f43..2e0cae1f 100644 --- a/stdlib/internal/__init_test__.codon +++ b/stdlib/internal/__init_test__.codon @@ -146,6 +146,9 @@ class str: def __repr__(self) -> str: return f"'{self}'" +set = Set +dict = Dict + from internal.builtin import * # from openmp import Ident as __OMPIdent, for_par