mirror of https://github.com/exaloop/codon.git
Fix assign tests
parent
6321a03868
commit
99973373e0
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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<AssignStmt>(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<ExprStmt>(inPlaceExpr);
|
||||
auto s = N<ExprStmt>(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<bool, ExprPtr> 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<CallExpr>(N<IdExpr>(method->ast->name),
|
||||
N<CallExpr>(N<IdExpr>("__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<CallExpr>(N<IdExpr>(method->ast->name),
|
||||
N<CallExpr>(N<IdExpr>("__ptr__"), stmt->lhs),
|
||||
call->args[1].value))};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<SuiteStmt>();
|
||||
}
|
||||
|
@ -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<std::string, TypeContext::Item> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -67,25 +67,78 @@ StmtPtr ScopingVisitor::transform(std::shared_ptr<Stmt> &stmt) {
|
|||
return stmt;
|
||||
}
|
||||
|
||||
void ScopingVisitor::switchToUpdate(std::shared_ptr<SrcObject> 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<SuiteStmt>(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<AssignStmt>(N<IdExpr>(used), N<BoolExpr>(true), nullptr,
|
||||
AssignStmt::UpdateMode::Update));
|
||||
}
|
||||
} else {
|
||||
s->stmts.clear();
|
||||
}
|
||||
} else if (auto f = std::dynamic_pointer_cast<ForStmt>(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<SuiteStmt>(N<AssignStmt>(N<IdExpr>(used), N<BoolExpr>(true),
|
||||
nullptr, AssignStmt::UpdateMode::Update),
|
||||
f->suite);
|
||||
}
|
||||
}
|
||||
} else if (auto f = std::dynamic_pointer_cast<TryStmt::Catch>(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<SuiteStmt>(N<AssignStmt>(N<IdExpr>(used), N<BoolExpr>(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<SrcObject> &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<int>{ctx->scope[0].id};
|
||||
auto b = N<AssignStmt>(N<IdExpr>(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<int>{ctx->scope[0].id};
|
||||
auto b = N<AssignStmt>(N<IdExpr>(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<SuiteStmt>(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<AssignStmt>(N<IdExpr>(used), N<BoolExpr>(true), nullptr,
|
||||
AssignStmt::UpdateMode::Update));
|
||||
}
|
||||
} else {
|
||||
s->stmts.clear();
|
||||
}
|
||||
} else if (auto f = std::dynamic_pointer_cast<ForStmt>(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<SuiteStmt>(N<AssignStmt>(N<IdExpr>(used), N<BoolExpr>(true), nullptr,
|
||||
AssignStmt::UpdateMode::Update),
|
||||
f->suite);
|
||||
}
|
||||
}
|
||||
} else if (auto f = std::dynamic_pointer_cast<TryStmt::Catch>(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<SuiteStmt>(N<AssignStmt>(N<IdExpr>(used), N<BoolExpr>(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);
|
||||
|
|
|
@ -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<StmtPtr> &);
|
||||
void visit(DelStmt *) override;
|
||||
|
@ -425,6 +425,7 @@ public:
|
|||
|
||||
void transformBlock(StmtPtr &s);
|
||||
ExprPtr makeAnonFn(std::vector<StmtPtr>, const std::vector<std::string> & = {});
|
||||
void switchToUpdate(std::shared_ptr<SrcObject> binding, const std::string &, bool);
|
||||
};
|
||||
|
||||
} // namespace codon::ast
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue