From 50f0c3803a193847f44b6574e51269d1d133d1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= Date: Sun, 25 Jun 2023 00:17:52 +0200 Subject: [PATCH] Merge simplify & typecheck [wip] --- .clang-tidy | 4 +- codon/parser/ast/expr.cpp | 10 +- codon/parser/ast/expr.h | 3 + codon/parser/ast/stmt.h | 2 +- codon/parser/ast/types/link.cpp | 15 +- codon/parser/ast/types/static.cpp | 10 +- codon/parser/ast/types/traits.cpp | 2 +- codon/parser/cache.cpp | 11 +- codon/parser/cache.h | 39 +- codon/parser/common.h | 6 + codon/parser/visitors/simplify/access.cpp | 2 +- codon/parser/visitors/typecheck/access.cpp | 227 ++++++++--- codon/parser/visitors/typecheck/assign.cpp | 189 +++------ codon/parser/visitors/typecheck/basic.cpp | 2 +- codon/parser/visitors/typecheck/call.cpp | 71 ++-- codon/parser/visitors/typecheck/class.cpp | 317 +++++++------- codon/parser/visitors/typecheck/ctx.cpp | 385 ++++++++---------- codon/parser/visitors/typecheck/ctx.h | 118 +++--- codon/parser/visitors/typecheck/error.cpp | 14 +- codon/parser/visitors/typecheck/function.cpp | 194 ++++----- codon/parser/visitors/typecheck/import.cpp | 55 ++- codon/parser/visitors/typecheck/infer.cpp | 129 +++--- codon/parser/visitors/typecheck/loops.cpp | 31 +- codon/parser/visitors/typecheck/op.cpp | 10 +- codon/parser/visitors/typecheck/typecheck.cpp | 201 +++++---- codon/parser/visitors/typecheck/typecheck.h | 40 +- stdlib/internal/__init__.codon | 16 +- stdlib/internal/core.codon | 13 +- stdlib/internal/internal.codon | 11 +- stdlib/internal/python.codon | 8 + stdlib/internal/types/array.codon | 7 + stdlib/internal/types/complex.codon | 8 + stdlib/internal/types/error.codon | 7 - stdlib/internal/types/float.codon | 4 - stdlib/internal/types/int.codon | 4 - stdlib/internal/types/optional.codon | 8 + stdlib/internal/types/ptr.codon | 6 - 37 files changed, 1147 insertions(+), 1032 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 42f02682..bb139f4c 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -4,4 +4,6 @@ WarningsAsErrors: false HeaderFilterRegex: '(build/.+)|(codon/util/.+)' AnalyzeTemporaryDtors: false FormatStyle: llvm -... +CheckOptions: + - key: cppcoreguidelines-macro-usage.CheckCapsOnly + value: '1' diff --git a/codon/parser/ast/expr.cpp b/codon/parser/ast/expr.cpp index 4ed0deb1..49f515e9 100644 --- a/codon/parser/ast/expr.cpp +++ b/codon/parser/ast/expr.cpp @@ -11,7 +11,14 @@ #include "codon/parser/visitors/visitor.h" #define ACCEPT_IMPL(T, X) \ - ExprPtr T::clone() const { return std::make_shared(*this); } \ + ExprPtr T::clone() const { \ + auto e = std::make_shared(*this); \ + e->type = nullptr; \ + e->done = false; \ + e->attributes = 0; \ + return e; \ + } \ + ExprPtr T::full_clone() const { return std::make_shared(*this); } \ void T::accept(X &visitor) { visitor.visit(this); } using fmt::format; @@ -126,6 +133,7 @@ IntExpr::IntExpr(const std::string &value, std::string suffix) std::make_unique(std::stoull(this->value.substr(2), nullptr, 2)); else intValue = std::make_unique(std::stoull(this->value, nullptr, 0)); + staticValue = StaticValue(*intValue); } catch (std::out_of_range &) { intValue = nullptr; } diff --git a/codon/parser/ast/expr.h b/codon/parser/ast/expr.h index a16459fb..e47095a4 100644 --- a/codon/parser/ast/expr.h +++ b/codon/parser/ast/expr.h @@ -15,6 +15,7 @@ namespace codon::ast { #define ACCEPT(X) \ ExprPtr clone() const override; \ + ExprPtr full_clone() const override; \ void accept(X &visitor) override // Forward declarations @@ -95,6 +96,8 @@ public: void validate() const; /// Deep copy a node. virtual std::shared_ptr clone() const = 0; + /// Deep copy a node; preserve types/attributes! + virtual std::shared_ptr full_clone() const = 0; /// Accept an AST visitor. virtual void accept(ASTVisitor &visitor) = 0; diff --git a/codon/parser/ast/stmt.h b/codon/parser/ast/stmt.h index 9298b12a..22cb1f3a 100644 --- a/codon/parser/ast/stmt.h +++ b/codon/parser/ast/stmt.h @@ -30,7 +30,7 @@ struct FunctionStmt; * A Seq AST statement. * Each AST statement is intended to be instantiated as a shared_ptr. */ -struct Stmt : public codon::SrcObject { +struct Stmt : public codon::SrcObject, public std::enable_shared_from_this { using base_type = Stmt; /// Flag that indicates if all types in a statement are inferred (i.e. if a diff --git a/codon/parser/ast/types/link.cpp b/codon/parser/ast/types/link.cpp index c2ab722a..fc836106 100644 --- a/codon/parser/ast/types/link.cpp +++ b/codon/parser/ast/types/link.cpp @@ -36,8 +36,12 @@ int LinkType::unify(Type *typ, Unification *undo) { return -1; } else { // Case 3: Unbound unification - if (isStaticType() != typ->isStaticType()) - return -1; + if (isStaticType() != typ->isStaticType()) { + if (!isStaticType()) + isStatic = typ->isStaticType(); + else + return -1; + } if (auto ts = typ->getStatic()) { if (ts->expr->getId()) return unify(ts->generics[0].type.get(), undo); @@ -154,11 +158,12 @@ bool LinkType::isInstantiated() const { return kind == Link && type->isInstantia std::string LinkType::debugString(char mode) const { if (kind == Unbound || kind == Generic) { if (mode == 2) { - return fmt::format("{}{}{}", kind == Unbound ? '?' : '#', id, + return fmt::format("{}{}{}{}", genericName.empty() ? "" : genericName + ":", + kind == Unbound ? '?' : '#', id, trait ? ":" + trait->debugString(mode) : ""); - } - if (trait) + } else if (trait) { return trait->debugString(mode); + } return (genericName.empty() ? (mode ? "?" : "") : genericName); } return type->debugString(mode); diff --git a/codon/parser/ast/types/static.cpp b/codon/parser/ast/types/static.cpp index 73946213..3b4a332b 100644 --- a/codon/parser/ast/types/static.cpp +++ b/codon/parser/ast/types/static.cpp @@ -137,12 +137,11 @@ std::string StaticType::realizedName() const { StaticValue StaticType::evaluate() const { if (expr->staticValue.evaluated) return expr->staticValue; - cache->typeCtx->addBlock(); + auto ctx = std::make_shared(cache); for (auto &g : generics) - cache->typeCtx->addType(g.name, g.name, getSrcInfo(), g.type); - auto en = TypecheckVisitor(cache->typeCtx).transform(expr->clone()); + ctx->addType(g.name, g.name, g.type); + auto en = TypecheckVisitor(ctx).transform(expr->clone()); seqassert(en->isStatic() && en->staticValue.evaluated, "{} cannot be evaluated", en); - cache->typeCtx->popBlock(); return en->staticValue; } @@ -157,8 +156,7 @@ void StaticType::parseExpr(const ExprPtr &e, std::unordered_set &se : genTyp->getStatic()->generics.empty() ? 0 : genTyp->getStatic()->generics[0].id; - generics.emplace_back(ei->value, - cache->typeCtx->cache->reverseIdentifierLookup[ei->value], + generics.emplace_back(ei->value, cache->reverseIdentifierLookup[ei->value], genTyp, id); seen.insert(ei->value); } diff --git a/codon/parser/ast/types/traits.cpp b/codon/parser/ast/types/traits.cpp index 7c07301c..010f713d 100644 --- a/codon/parser/ast/types/traits.cpp +++ b/codon/parser/ast/types/traits.cpp @@ -190,7 +190,7 @@ TypePtr TypeTrait::instantiate(int atLevel, int *unboundCount, } std::string TypeTrait::debugString(char mode) const { - return fmt::format("Trait[{}]", type->debugString(mode)); + return fmt::format("Trait[{}]", type->getClass() ? type->getClass()->name : "-"); } } // namespace codon::ast::types diff --git a/codon/parser/cache.cpp b/codon/parser/cache.cpp index 1c58c72a..efcc1558 100644 --- a/codon/parser/cache.cpp +++ b/codon/parser/cache.cpp @@ -16,7 +16,9 @@ namespace codon::ast { -Cache::Cache(std::string argv0) : argv0(std::move(argv0)) {} +Cache::Cache(std::string argv0) : argv0(std::move(argv0)) { + typeCtx = std::make_shared(this, ".root"); +} std::string Cache::getTemporaryVar(const std::string &prefix, char sigil) { return fmt::format("{}{}_{}", sigil ? fmt::format("{}_", sigil) : "", prefix, @@ -59,17 +61,17 @@ std::string Cache::getContent(const SrcInfo &info) { types::ClassTypePtr Cache::findClass(const std::string &name) const { auto f = typeCtx->find(name); - if (f && f->kind == TypecheckItem::Type) + if (f && f->isType()) return f->type->getClass(); return nullptr; } types::FuncTypePtr Cache::findFunction(const std::string &name) const { auto f = typeCtx->find(name); - if (f && f->type && f->kind == TypecheckItem::Func) + if (f && f->type && f->isFunc()) return f->type->getFunc(); f = typeCtx->find(name + ":0"); - if (f && f->type && f->kind == TypecheckItem::Func) + if (f && f->type && f->isFunc()) return f->type->getFunc(); return nullptr; } @@ -79,6 +81,7 @@ types::FuncTypePtr Cache::findMethod(types::ClassType *typ, const std::string &m auto e = std::make_shared(typ->name); e->type = typ->getClass(); seqassertn(e->type, "not a class"); + auto f = TypecheckVisitor(typeCtx).findBestMethod(e->type->getClass(), member, args); return f; } diff --git a/codon/parser/cache.h b/codon/parser/cache.h index 7c467478..81196a66 100644 --- a/codon/parser/cache.h +++ b/codon/parser/cache.h @@ -64,7 +64,9 @@ struct Cache : public std::enable_shared_from_this { int varCount = 0; /// Holds module import data. - struct Import { + struct Module { + /// Relative module name (e.g., `foo.bar`) + std::string name; /// Absolute filename of an import. std::string filename; /// Import typechecking context. @@ -73,8 +75,6 @@ struct Cache : public std::enable_shared_from_this { std::string importVar; /// File content (line:col indexable) std::vector content; - /// Relative module name (e.g., `foo.bar`) - std::string moduleName; }; /// Absolute path of seqc executable (if available). @@ -85,8 +85,9 @@ struct Cache : public std::enable_shared_from_this { ir::Module *module = nullptr; /// Table of imported files that maps an absolute filename to a Import structure. - /// By convention, the key of the Codon's standard library is "". - std::unordered_map imports; + /// By convention, the key of the Codon's standard library is ":stdlib:", + /// and the main module is "". + std::unordered_map imports; /// Set of unique (canonical) global identifiers for marking such variables as global /// in code-generation step and in JIT. @@ -94,10 +95,13 @@ struct Cache : public std::enable_shared_from_this { /// Stores class data for each class (type) in the source code. struct Class { + /// Module information + std::string module; + /// Generic (unrealized) class template AST. - std::shared_ptr ast; + std::shared_ptr ast = nullptr; /// Non-simplified AST. Used for base class instantiation. - std::shared_ptr originalAst; + std::shared_ptr originalAst = nullptr; /// Class method lookup table. Each non-canonical name points /// to a root function name of a corresponding method. @@ -155,10 +159,7 @@ struct Cache : public std::enable_shared_from_this { /// List of statically inherited classes. std::vector staticParentClasses; - /// Module information - std::string module; - - Class() : ast(nullptr), originalAst(nullptr), rtti(false) {} + bool hasRTTI() const { return rtti; } }; /// Class lookup table that maps a canonical class identifier to the corresponding /// Class instance. @@ -166,10 +167,12 @@ struct Cache : public std::enable_shared_from_this { size_t classRealizationCnt = 0; struct Function { + /// Module information + std::string module; /// Generic (unrealized) function template AST. - std::shared_ptr ast; + std::shared_ptr ast = nullptr; /// Non-simplified AST. - std::shared_ptr origAst; + std::shared_ptr origAst = nullptr; /// A function realization. struct FunctionRealization { @@ -186,15 +189,10 @@ struct Cache : public std::enable_shared_from_this { std::unordered_map> realizations; /// Unrealized function type. - types::FuncTypePtr type; + types::FuncTypePtr type = nullptr; - /// Module information - std::string rootName = ""; + std::string rootName; bool isToplevel = false; - - Function() - : ast(nullptr), origAst(nullptr), type(nullptr), rootName(""), - isToplevel(false) {} }; /// Function lookup table that maps a canonical function identifier to the /// corresponding Function instance. @@ -229,7 +227,6 @@ struct Cache : public std::enable_shared_from_this { bool isJit = false; int jitCell = 0; - std::unordered_map> replacements; std::unordered_map generatedTuples; std::vector errors; diff --git a/codon/parser/common.h b/codon/parser/common.h index d9854482..b2cf22e1 100644 --- a/codon/parser/common.h +++ b/codon/parser/common.h @@ -104,6 +104,12 @@ const V *in(const std::unordered_map &m, const U &item) { auto f = m.find(item); return f != m.end() ? &(f->second) : nullptr; } +/// @return True if an item is found in an unordered_map m. +template +V *in(std::unordered_map &m, const U &item) { + auto f = m.find(item); + return f != m.end() ? &(f->second) : nullptr; +} /// @return vector c transformed by the function f. template auto vmap(const std::vector &c, F &&f) { std::vector::type> ret; diff --git a/codon/parser/visitors/simplify/access.cpp b/codon/parser/visitors/simplify/access.cpp index aec276a1..5304ba61 100644 --- a/codon/parser/visitors/simplify/access.cpp +++ b/codon/parser/visitors/simplify/access.cpp @@ -266,7 +266,7 @@ SimplifyVisitor::getImport(const std::vector &chain) { } if (itemName.empty()) E(Error::IMPORT_NO_NAME, getSrcInfo(), chain[importEnd], - ctx->cache->imports[importName].moduleName); + ctx->cache->imports[importName].name); importEnd = itemEnd; } return {importEnd, val}; diff --git a/codon/parser/visitors/typecheck/access.cpp b/codon/parser/visitors/typecheck/access.cpp index 404c7c11..e5717a75 100644 --- a/codon/parser/visitors/typecheck/access.cpp +++ b/codon/parser/visitors/typecheck/access.cpp @@ -25,14 +25,20 @@ void TypecheckVisitor::visit(IdExpr *expr) { if (isTuple(expr->value)) generateTuple(std::stoi(expr->value.substr(sizeof(TYPE_TUPLE) - 1))); - auto val = ctx->findDominatingBinding(expr->value, this); - + auto val = findDominatingBinding(expr->value, ctx.get()); if (!val && ctx->getBase()->pyCaptures) { ctx->getBase()->pyCaptures->insert(expr->value); resultExpr = N(N("__pyenv__"), N(expr->value)); return; } else if (!val) { - E(Error::ID_NOT_FOUND, expr, expr->value); + if (in(ctx->cache->overloads, expr->value)) + val = ctx->forceFind(getDispatch(expr->value)->ast->name); + if (!val) { + ctx->dump(); + // LOG("================================================================="); + // ctx->cache->typeCtx->dump(); + E(Error::ID_NOT_FOUND, expr, expr->value); + } } // If we are accessing an outside variable, capture it or raise an error @@ -54,7 +60,6 @@ void TypecheckVisitor::visit(IdExpr *expr) { // Replace the variable with its canonical name expr->value = val->canonicalName; - val->references.push_back(expr->shared_from_this()); // Mark global as "seen" to prevent later creation of local variables // with the same name. Example: @@ -99,8 +104,6 @@ void TypecheckVisitor::visit(IdExpr *expr) { } } - // todo)) handle overloads [each overloaded fn is basically a new FnOverload object] - // Set up type unify(expr->type, ctx->instantiate(val->type)); if (val->type->isStaticType()) { @@ -111,10 +114,11 @@ void TypecheckVisitor::visit(IdExpr *expr) { expr->toString()); if (s && s->expr->staticValue.evaluated) { // Replace the identifier with static expression - if (s->expr->staticValue.type == StaticValue::STRING) + if (s->expr->staticValue.type == StaticValue::STRING) { resultExpr = transform(N(s->expr->staticValue.getString())); - else + } else { resultExpr = transform(N(s->expr->staticValue.getInt())); + } } return; } @@ -135,46 +139,101 @@ void TypecheckVisitor::visit(IdExpr *expr) { /// `python.foo` -> internal.python._get_identifier("foo") /// Other cases are handled during the type checking. /// See @c transformDot for details. -void TypecheckVisitor::visit(DotExpr *expr) { - if (!expr->type) { - // First flatten the imports: - // transform Dot(Dot(a, b), c...) to {a, b, c, ...} - std::vector chain; - Expr *root = expr; - for (; root->getDot(); root = root->getDot()->expr.get()) - chain.push_back(root->getDot()->member); +void TypecheckVisitor::visit(DotExpr *expr) { resultExpr = transformDot(expr); } - if (auto id = root->getId()) { - // Case: a.bar.baz - chain.push_back(id->value); - std::reverse(chain.begin(), chain.end()); - auto [pos, val] = getImport(chain); - - if (!val) { - seqassert(ctx->getBase()->pyCaptures, "unexpected py capture"); - ctx->getBase()->pyCaptures->insert(chain[0]); - resultExpr = N(N("__pyenv__"), N(chain[0])); - } else if (val->getModule() == "std.python") { - resultExpr = transform(N( - N(N(N("internal"), "python"), "_get_identifier"), - N(chain[pos++]))); - } else if (val->getModule() == ctx->getModule() && pos == 1) { - resultExpr = transform(N(chain[0]), true); - } else { - resultExpr = N(val->canonicalName); - if (val->isType() && pos == chain.size()) - resultExpr->markType(); - } - while (pos < chain.size()) - resultExpr = N(resultExpr, chain[pos++]); - resultExpr = transformDot(resultExpr->getDot()); - } else { - transform(expr->expr, true); - resultExpr = transformDot(expr); - } - } else { - resultExpr = transformDot(expr); +/// Get an item from the context. Perform domination analysis for accessing items +/// defined in the conditional blocks (i.e., Python scoping). +TypeContext::Item TypecheckVisitor::findDominatingBinding(const std::string &name, + TypeContext *ctx) { + auto it = ctx->find_all(name); + if (!it) { + return ctx->find(name); + } else if (ctx->isCanonicalName(name)) { + return *(it->begin()); } + seqassert(!it->empty(), "corrupted TypecheckContext ({})", name); + + // The item is found. Let's see is it accessible now. + + std::string canonicalName; + auto lastGood = it->begin(); + bool isOutside = (*lastGood)->getBaseName() != ctx->getBaseName(); + int prefix = int(ctx->scope.blocks.size()); + // Iterate through all bindings with the given name and find the closest binding that + // dominates the current scope. + for (auto i = it->begin(); i != it->end(); i++) { + // Find the longest block prefix between the binding and the current scope. + int p = std::min(prefix, int((*i)->scope.size())); + while (p >= 0 && (*i)->scope[p - 1] != ctx->scope.blocks[p - 1]) + p--; + // We reached the toplevel. Break. + if (p < 0) + break; + // We went outside the function scope. Break. + if (!isOutside && (*i)->getBaseName() != ctx->getBaseName()) + break; + prefix = p; + lastGood = i; + // The binding completely dominates the current scope. Break. + if ((*i)->scope.size() <= ctx->scope.blocks.size() && + (*i)->scope.back() == ctx->scope.blocks[(*i)->scope.size() - 1]) + break; + } + seqassert(lastGood != it->end(), "corrupted scoping ({})", name); + if (lastGood != it->begin() && !(*lastGood)->isVar()) + E(Error::CLASS_INVALID_BIND, getSrcInfo(), name); + + bool hasUsed = false; + types::TypePtr type = nullptr; + if ((*lastGood)->scope.size() == prefix) { + // The current scope is dominated by a binding. Use that binding. + canonicalName = (*lastGood)->canonicalName; + type = (*lastGood)->type; + } else { + // The current scope is potentially reachable by multiple bindings that are + // not dominated by a common binding. Create such binding in the scope that + // dominates (covers) all of them. + canonicalName = ctx->generateCanonicalName(name); + auto item = std::make_shared( + canonicalName, (*lastGood)->baseName, (*lastGood)->moduleName, + ctx->getUnbound(getSrcInfo()), + std::vector(ctx->scope.blocks.begin(), + ctx->scope.blocks.begin() + prefix)); + item->accessChecked = {(*lastGood)->scope}; + type = item->type; + lastGood = it->insert(++lastGood, item); + // Make sure to prepend a binding declaration: `var` and `var__used__ = False` + // to the dominating scope. + ctx->scope.stmts[ctx->scope.blocks[prefix - 1]].push_back( + N(N(N(canonicalName), nullptr, nullptr), + N(N(fmt::format("{}.__used__", canonicalName)), + N(false), nullptr))); + + // Reached the toplevel? Register the binding as global. + if (prefix == 1) { + ctx->cache->addGlobal(canonicalName); + ctx->cache->addGlobal(fmt::format("{}.__used__", canonicalName)); + } + hasUsed = true; + } + // Remove all bindings after the dominant binding. + 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. + + ctx->getBase()->replacements[(*i)->canonicalName] = {canonicalName, hasUsed}; + ctx->getBase()->replacements[format("{}.__used__", (*i)->canonicalName)] = { + format("{}.__used__", canonicalName), false}; + seqassert((*i)->canonicalName != canonicalName, "invalid replacement at {}: {}", + getSrcInfo(), canonicalName); + ctx->removeFromTopStack(name); + } + it->erase(it->begin(), lastGood); + return it->front(); } /// Access identifiers from outside of the current function/class scope. @@ -219,7 +278,7 @@ bool TypecheckVisitor::checkCapture(const TypeContext::Item &val) { // Case: a global variable that has not been marked with `global` statement if (val->isVar() && val->getBaseName().empty() && val->scope.size() == 1) { - val->noShadow = true; + val->canShadow = false; if (!val->isStatic()) ctx->cache->addGlobal(val->canonicalName); return false; @@ -247,11 +306,11 @@ bool TypecheckVisitor::checkCapture(const TypeContext::Item &val) { // Add newly generated argument to the context std::shared_ptr newVal = nullptr; if (val->isType()) - newVal = ctx->addType(ctx->cache->rev(val->canonicalName), newName, getSrcInfo()); + newVal = ctx->addType(ctx->cache->rev(val->canonicalName), newName, val->type); else - newVal = ctx->addVar(ctx->cache->rev(val->canonicalName), newName, getSrcInfo()); + newVal = ctx->addVar(ctx->cache->rev(val->canonicalName), newName, val->type); newVal->baseName = ctx->getBaseName(); - newVal->noShadow = true; // todo)) needed here? remove noshadow on fn boundaries? + newVal->canShadow = false; // todo)) needed here? remove noshadow on fn boundaries? newVal->scope = ctx->getBase()->scope; return true; } @@ -272,9 +331,11 @@ TypecheckVisitor::getImport(const std::vector &chain) { // (e.g., `a.b.c.d` -> `a.b.c` if there is `import a.b.c`) TypeContext::Item val = nullptr; for (auto i = chain.size(); i-- > 0;) { - val = ctx->find(join(chain, "/", 0, i + 1)); - if (val && val->isImport()) { - importName = val->importPath, importEnd = i + 1; + auto name = join(chain, "/", 0, i + 1); + val = ctx->find(name); + if (val && val->type->is("Import") && name != "Import") { + importName = getClassStaticStr(val->type->getClass()); + importEnd = i + 1; break; } } @@ -289,12 +350,16 @@ TypecheckVisitor::getImport(const std::vector &chain) { if (fctx->getModule() == "std.python" && importEnd < chain.size()) { // Special case: importing from Python. // Fake TypecheckItem that indicates std.python access - val = std::make_shared(TypecheckItem::Var, "", "", - fctx->getModule(), std::vector{}); + val = std::make_shared("", "", fctx->getModule(), + fctx->getUnbound()); return {importEnd, val}; } else { val = fctx->find(join(chain, ".", importEnd, i + 1)); - if (val && (importName.empty() || val->isType() || !val->isConditional())) { + bool isOverload = val && val->isFunc() && + in(ctx->cache->overloads, val->canonicalName) && + ctx->cache->overloads[val->canonicalName].size() > 1; + if (val && !isOverload && + (importName.empty() || val->isType() || !val->isConditional())) { itemName = val->canonicalName, itemEnd = i + 1; break; } @@ -305,9 +370,10 @@ TypecheckVisitor::getImport(const std::vector &chain) { return {1, nullptr}; E(Error::IMPORT_NO_MODULE, getSrcInfo(), chain[importEnd]); } - if (itemName.empty()) + if (itemName.empty()) { E(Error::IMPORT_NO_NAME, getSrcInfo(), chain[importEnd], - ctx->cache->imports[importName].moduleName); + ctx->cache->imports[importName].name); + } importEnd = itemEnd; } return {importEnd, val}; @@ -351,7 +417,7 @@ types::FuncTypePtr TypecheckVisitor::getDispatch(const std::string &fn) { auto baseType = getFuncTypeBase(2); auto typ = std::make_shared(baseType, ast.get()); typ = std::static_pointer_cast(typ->generalize(ctx->typecheckLevel - 1)); - ctx->addFunc(name, name, getSrcInfo(), typ); + ctx->addFunc(name, name, typ); overloads.insert(overloads.begin(), name); ctx->cache->functions[name].ast = ast; @@ -376,12 +442,49 @@ types::FuncTypePtr TypecheckVisitor::getDispatch(const std::string &fn) { /// See @c getClassMember and @c getBestOverload ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, std::vector *args) { + // First flatten the imports: + // transform Dot(Dot(a, b), c...) to {a, b, c, ...} + std::vector chain; + Expr *root = expr; + for (; root->getDot(); root = root->getDot()->expr.get()) + chain.push_back(root->getDot()->member); + + ExprPtr nexpr = expr->shared_from_this(); + if (auto id = root->getId()) { + // Case: a.bar.baz + chain.push_back(id->value); + std::reverse(chain.begin(), chain.end()); + auto [pos, val] = getImport(chain); + if (!val) { + seqassert(ctx->getBase()->pyCaptures, "unexpected py capture"); + ctx->getBase()->pyCaptures->insert(chain[0]); + nexpr = N(N("__pyenv__"), N(chain[0])); + } else if (val->getModule() == "std.python") { + nexpr = transform(N( + N(N(N("internal"), "python"), "_get_identifier"), + N(chain[pos++]))); + } else if (val->getModule() == ctx->getModule() && pos == 1) { + nexpr = transform(N(chain[0]), true); + } else { + nexpr = N(val->canonicalName); + if (val->isType() && pos == chain.size()) + nexpr->markType(); + } + while (pos < chain.size()) + nexpr = N(nexpr, chain[pos++]); + } + if (!nexpr->getDot()) { + return transform(nexpr); + } else { + expr->expr = nexpr->getDot()->expr; + expr->member = nexpr->getDot()->member; + } + // Special case: obj.__class__ if (expr->member == "__class__") { /// TODO: prevent cls.__class__ and type(cls) return transformType(NT(NT("type"), expr->expr)); } - transform(expr->expr); // Special case: fn.__name__ @@ -455,7 +558,7 @@ ExprPtr TypecheckVisitor::transformDot(DotExpr *expr, // ) auto e = N( fnType, - N(N(N("__internal__.class_get_rtti_vtable:0"), + N(N(N("__internal__.class_get_rtti_vtable"), expr->expr), N(vid))); return transform(e); @@ -569,7 +672,7 @@ ExprPtr TypecheckVisitor::getClassMember(DotExpr *expr, // Case: transform `union.m` to `__internal__.get_union_method(union, "m", ...)` if (typ->getUnion()) { return transform(N( - N("__internal__.get_union_method:0"), + N("__internal__.get_union_method"), std::vector{{"union", expr->expr}, {"method", N(expr->member)}, {"", N(EllipsisExpr::PARTIAL)}})); diff --git a/codon/parser/visitors/typecheck/assign.cpp b/codon/parser/visitors/typecheck/assign.cpp index ebb164f7..ef79d38c 100644 --- a/codon/parser/visitors/typecheck/assign.cpp +++ b/codon/parser/visitors/typecheck/assign.cpp @@ -48,125 +48,18 @@ void TypecheckVisitor::visit(AssignExpr *expr) { /// See @c transformAssignment and @c unpackAssignments for more details. /// See @c wrapExpr for more examples. void TypecheckVisitor::visit(AssignStmt *stmt) { - std::vector stmts; if (stmt->rhs && stmt->rhs->getBinary() && stmt->rhs->getBinary()->inPlace) { // Update case: a += b seqassert(!stmt->type, "invalid AssignStmt {}", stmt->toString()); - resultStmt = transform(transformAssignment(stmt->lhs, stmt->rhs, nullptr, true)); - } else if (stmt->type) { - // Type case: `a: T = b, c` (no unpacking) - resultStmt = transform(transformAssignment(stmt->lhs, stmt->rhs, stmt->type)); - } else if (!stmt->lhs->getId()) { + resultStmt = transformAssignment(stmt->lhs, stmt->rhs, nullptr, true); + } else if (!stmt->type && !stmt->lhs->getId()) { // Normal case + std::vector stmts; unpackAssignments(stmt->lhs, stmt->rhs, stmts); resultStmt = transform(N(stmts)); } else { - auto assign = transformAssignment(stmt->lhs, stmt->rhs, stmt->type); - - // Update statements are handled by @c visitUpdate - if (stmt->isUpdate()) { - transformUpdate(stmt); - return; - } - - seqassert(stmt->lhs->getId(), "invalid AssignStmt {}", stmt->lhs); - std::string lhs = stmt->lhs->getId()->value; - - // Special case: this assignment has been dominated and is not a true assignment but - // an update of the dominating binding. - if (auto changed = in(ctx->cache->replacements, lhs)) { - while (auto s = in(ctx->cache->replacements, lhs)) - lhs = changed->first, changed = s; - if (stmt->rhs && changed->second) { - // Mark the dominating binding as used: `var.__used__ = True` - auto u = N(N(fmt::format("{}.__used__", lhs)), - N(true)); - u->setUpdate(); - prependStmts->push_back(transform(u)); - } else if (changed->second && !stmt->rhs) { - // This assignment was a declaration only. Just mark the dominating binding as - // used: `var.__used__ = True` - stmt->lhs = N(fmt::format("{}.__used__", lhs)); - stmt->rhs = N(true); - } - seqassert(stmt->rhs, "bad domination statement: '{}'", stmt->toString()); - // Change this to the update and follow the update logic - stmt->setUpdate(); - transformUpdate(stmt); - return; - } - - transform(stmt->rhs); - transformType(stmt->type); - if (!stmt->rhs) { - // Forward declarations (e.g., dominating bindings, C imports etc.). - // The type is unknown and will be deduced later - unify(stmt->lhs->type, ctx->getUnbound(stmt->lhs->getSrcInfo())); - if (stmt->type) { - unify(stmt->lhs->type, - ctx->instantiate(stmt->type->getSrcInfo(), stmt->type->getType())); - } - ctx->addVar(lhs, lhs, getSrcInfo(), stmt->lhs->type); - if (realize(stmt->lhs->type)) - stmt->setDone(); - } else if (stmt->type && stmt->type->getType()->isStaticType()) { - // Static assignments (e.g., `x: Static[int] = 5`) - if (!stmt->rhs->isStatic()) - E(Error::EXPECTED_STATIC, stmt->rhs); - seqassert(stmt->rhs->staticValue.evaluated, "static not evaluated"); - unify(stmt->lhs->type, - unify(stmt->type->type, Type::makeStatic(ctx->cache, stmt->rhs))); - auto val = ctx->addVar(lhs, lhs, getSrcInfo(), stmt->lhs->type); - if (in(ctx->cache->globals, lhs)) { - // Make globals always visible! - ctx->addToplevel(lhs, val); - } - if (realize(stmt->lhs->type)) - stmt->setDone(); - } else { - // Normal assignments - unify(stmt->lhs->type, ctx->getUnbound()); - if (stmt->type) { - unify(stmt->lhs->type, - ctx->instantiate(stmt->type->getSrcInfo(), stmt->type->getType())); - } - // Check if we can wrap the expression (e.g., `a: float = 3` -> `a = float(3)`) - if (wrapExpr(stmt->rhs, stmt->lhs->getType())) - unify(stmt->lhs->type, stmt->rhs->type); - auto type = stmt->lhs->getType(); - auto kind = TypecheckItem::Var; - if (stmt->rhs->isType()) - kind = TypecheckItem::Type; - else if (type->getFunc()) - kind = TypecheckItem::Func; - // Generalize non-variable types. That way we can support cases like: - // `a = foo(x, ...); a(1); a('s')` - auto val = std::make_shared(kind, ctx->getBaseName(), lhs, - ctx->getModule(), ctx->scope.blocks); - val->setSrcInfo(getSrcInfo()); - val->type = - kind != TypecheckItem::Var ? type->generalize(ctx->typecheckLevel - 1) : type; - if (in(ctx->cache->globals, lhs)) { - // Make globals always visible! - ctx->addToplevel(lhs, val); - if (kind != TypecheckItem::Var) - ctx->cache->globals.erase(lhs); - } else if (startswith(ctx->getRealizationBase()->name, "._import_") && - kind == TypecheckItem::Type) { - // Make import toplevel type aliases (e.g., `a = Ptr[byte]`) visible - ctx->addToplevel(lhs, val); - } else { - ctx->add(lhs, val); - } - - if (stmt->lhs->getId() && kind != TypecheckItem::Var) { - // Special case: type/function renames - stmt->rhs->type = nullptr; - stmt->setDone(); - } else if (stmt->rhs->isDone() && realize(stmt->lhs->type)) { - stmt->setDone(); - } - } + // Type case: `a: T = b, c` (no unpacking); all other (invalid) cases + resultStmt = transformAssignment(stmt->lhs, stmt->rhs, stmt->type); } } @@ -218,11 +111,12 @@ StmtPtr TypecheckVisitor::transformAssignment(ExprPtr lhs, ExprPtr rhs, ExprPtr transform(dot->expr, true); // If we are deducing class members, check if we can deduce a member from this // assignment - auto deduced = ctx->getClassBase() ? ctx->getClassBase()->deducedMembers : nullptr; - if (deduced && dot->expr->isId(ctx->getBase()->selfName) && - !in(*deduced, dot->member)) - deduced->push_back(dot->member); - return N(dot->expr, dot->member, transform(rhs)); + // todo)) deduction! + // auto deduced = ctx->getClassBase() ? ctx->getClassBase()->deducedMembers : + // nullptr; if (deduced && dot->expr->isId(ctx->getBase()->selfName) && + // !in(*deduced, dot->member)) + // deduced->push_back(dot->member); + return transform(N(dot->expr, dot->member, transform(rhs))); } // Case: a (: t) = b @@ -243,15 +137,16 @@ StmtPtr TypecheckVisitor::transformAssignment(ExprPtr lhs, ExprPtr rhs, ExprPtr auto val = ctx->find(e->value); // Make sure that existing values that cannot be shadowed (e.g. imported globals) are // only updated - mustExist |= val && val->noShadow && !ctx->isOuter(val); + mustExist |= val && !val->canShadow && !ctx->isOuter(val); if (mustExist) { - val = ctx->findDominatingBinding(e->value, this); + val = findDominatingBinding(e->value, ctx.get()); if (val && val->isVar() && !ctx->isOuter(val)) { - auto s = N(transform(lhs, false), transform(rhs)); + auto s = N(lhs, rhs); if (ctx->getBase()->attributes && ctx->getBase()->attributes->has(Attr::Atomic)) s->setAtomicUpdate(); else s->setUpdate(); + transformUpdate(s.get()); return s; } else { E(Error::ASSIGN_LOCAL_REFERENCE, e, e->value); @@ -264,23 +159,55 @@ StmtPtr TypecheckVisitor::transformAssignment(ExprPtr lhs, ExprPtr rhs, ExprPtr // Generate new canonical variable name for this assignment and add it to the context auto canonical = ctx->generateCanonicalName(e->value); auto assign = N(N(canonical), rhs, type); - val = nullptr; - if (rhs && rhs->isType()) { - val = ctx->addType(e->value, canonical, lhs->getSrcInfo()); - } else { - val = ctx->addVar(e->value, canonical, lhs->getSrcInfo()); - if (auto st = getStaticGeneric(type.get())) - val->staticType = st; - if (ctx->avoidDomination) - val->avoidDomination = true; + unify(assign->lhs->type, ctx->getUnbound(assign->lhs->getSrcInfo())); + if (assign->type) { + unify(assign->lhs->type, + ctx->instantiate(assign->type->getSrcInfo(), assign->type->getType())); } + val = std::make_shared(canonical, ctx->getBaseName(), ctx->getModule(), + assign->lhs->type, ctx->scope.blocks); + val->setSrcInfo(getSrcInfo()); + if (auto st = getStaticGeneric(assign->type.get())) + val->staticType = st; + if (ctx->avoidDomination) + val->avoidDomination = true; + ctx->Context::add(e->value, val); + ctx->addAlwaysVisible(val); + LOG("added ass/{}: {}", val->isVar() ? "v" : (val->isFunc() ? "f" : "t"), + val->canonicalName); + + if (assign->rhs && assign->type && assign->type->getType()->isStaticType()) { + // Static assignments (e.g., `x: Static[int] = 5`) + if (!assign->rhs->isStatic()) + E(Error::EXPECTED_STATIC, assign->rhs); + seqassert(assign->rhs->staticValue.evaluated, "static not evaluated"); + unify(assign->lhs->type, + unify(assign->type->type, Type::makeStatic(ctx->cache, assign->rhs))); + } else if (assign->rhs) { + // Check if we can wrap the expression (e.g., `a: float = 3` -> `a = float(3)`) + if (wrapExpr(assign->rhs, assign->lhs->getType())) + unify(assign->lhs->type, assign->rhs->type); + if (rhs->isType()) + val->type = val->type->getClass(); + auto type = assign->lhs->getType(); + // Generalize non-variable types. That way we can support cases like: + // `a = foo(x, ...); a(1); a('s')` + if (!val->isVar()) + val->type = val->type->generalize(ctx->typecheckLevel - 1); + + // todo)) if (in(ctx->cache->globals, lhs)) { + } + + if ((!assign->rhs || assign->rhs->isDone()) && realize(assign->lhs->type)) { + assign->setDone(); + } + // Clean up seen tags if shadowing a name ctx->getBase()->seenGlobalIdentifiers.erase(e->value); - // Register all toplevel variables as global in JIT mode bool isGlobal = (ctx->cache->isJit && val->isGlobal() && !val->isGeneric()) || (canonical == VAR_ARGV); - if (isGlobal && !val->isGeneric()) + if (isGlobal && val->isVar()) ctx->cache->addGlobal(canonical); return assign; diff --git a/codon/parser/visitors/typecheck/basic.cpp b/codon/parser/visitors/typecheck/basic.cpp index a319b95a..66c29bd1 100644 --- a/codon/parser/visitors/typecheck/basic.cpp +++ b/codon/parser/visitors/typecheck/basic.cpp @@ -19,7 +19,7 @@ void TypecheckVisitor::visit(NoneExpr *expr) { if (realize(expr->type)) { // Realize the appropriate `Optional.__new__` for the translation stage auto cls = expr->type->getClass(); - auto f = ctx->forceFind(TYPE_OPTIONAL ".__new__:0")->type; + auto f = ctx->forceFind(TYPE_OPTIONAL ".__new__")->type; auto t = realize(ctx->instantiate(f, cls)->getFunc()); expr->setDone(); } diff --git a/codon/parser/visitors/typecheck/call.cpp b/codon/parser/visitors/typecheck/call.cpp index 3cb10021..4ae04af6 100644 --- a/codon/parser/visitors/typecheck/call.cpp +++ b/codon/parser/visitors/typecheck/call.cpp @@ -337,7 +337,7 @@ ExprPtr TypecheckVisitor::callReorderArguments(FuncTypePtr calleeFn, CallExpr *e } ExprPtr e = N(extra); e->setAttr(ExprAttr::StarArgument); - if (!expr->expr->isId("hasattr:0")) + if (!expr->expr->isId("hasattr")) e = transform(e); if (partial) { part.args = e; @@ -570,7 +570,8 @@ std::pair TypecheckVisitor::transformSpecialCall(CallExpr *expr) if (!expr->expr->getId()) return {false, nullptr}; auto val = expr->expr->getId()->value; - if (val == "tuple") { + if (val == "tuple" && expr->args.size() == 1 && + CAST(expr->args.front().value, GeneratorExpr)) { return {true, transformTupleGenerator(expr)}; } else if (val == "std.collections.namedtuple") { return {true, transformNamedTuple(expr)}; @@ -578,11 +579,11 @@ std::pair TypecheckVisitor::transformSpecialCall(CallExpr *expr) return {true, transformFunctoolsPartial(expr)}; } else if (val == "superf") { return {true, transformSuperF(expr)}; - } else if (val == "super:0") { + } else if (val == "super") { return {true, transformSuper()}; } else if (val == "__ptr__") { return {true, transformPtr(expr)}; - } else if (val == "__array__.__new__:0") { + } else if (val == "__array__.__new__") { return {true, transformArray(expr)}; } else if (val == "isinstance") { return {true, transformIsInstance(expr)}; @@ -594,7 +595,7 @@ std::pair TypecheckVisitor::transformSpecialCall(CallExpr *expr) return {true, transformGetAttr(expr)}; } else if (val == "setattr") { return {true, transformSetAttr(expr)}; - } else if (val == "type.__new__:0") { + } else if (val == "type.__new__") { return {true, transformTypeFn(expr)}; } else if (val == "compile_error") { return {true, transformCompileError(expr)}; @@ -605,6 +606,7 @@ std::pair TypecheckVisitor::transformSpecialCall(CallExpr *expr) } else if (val == "std.internal.static.static_print") { return {false, transformStaticPrintFn(expr)}; } else if (val == "__has_rtti__") { + LOG("- rtti has {}", getSrcInfo()); return {true, transformHasRttiFn(expr)}; } else { return transformInternalStaticFn(expr); @@ -627,12 +629,12 @@ ExprPtr TypecheckVisitor::transformTupleGenerator(CallExpr *expr) { ctx->enterConditionalBlock(); ctx->getBase()->loops.push_back({"", ctx->scope.blocks, {}}); if (auto i = var->getId()) { - ctx->addVar(i->value, ctx->generateCanonicalName(i->value), var->getSrcInfo()); + ctx->addVar(i->value, ctx->generateCanonicalName(i->value), ctx->getUnbound()); var = transform(var); ex = transform(ex); } else { std::string varName = ctx->cache->getTemporaryVar("for"); - ctx->addVar(varName, varName, var->getSrcInfo()); + ctx->addVar(varName, varName, ctx->getUnbound()); var = N(varName); auto head = transform(N(clone(g->loops[0].vars), clone(var))); ex = N(head, transform(ex)); @@ -640,7 +642,7 @@ ExprPtr TypecheckVisitor::transformTupleGenerator(CallExpr *expr) { ctx->leaveConditionalBlock(); // Dominate loop variables for (auto &var : ctx->getBase()->getLoop()->seenVars) - ctx->findDominatingBinding(var, this); + findDominatingBinding(var, ctx.get()); ctx->getBase()->loops.pop_back(); return N( GeneratorExpr::TupleGenerator, ex, @@ -705,7 +707,7 @@ ExprPtr TypecheckVisitor::transformFunctoolsPartial(CallExpr *expr) { /// cls.foo()``` /// prints "foo 1" followed by "foo 2" ExprPtr TypecheckVisitor::transformSuperF(CallExpr *expr) { - auto func = ctx->getRealizationBase()->type->getFunc(); + auto func = ctx->getBase()->type->getFunc(); // Find list of matching superf methods std::vector supers; @@ -740,9 +742,9 @@ ExprPtr TypecheckVisitor::transformSuperF(CallExpr *expr) { /// to the first inherited type. /// TODO: only an empty super() is currently supported. ExprPtr TypecheckVisitor::transformSuper() { - if (!ctx->getRealizationBase()->type) + if (!ctx->getBase()->type) E(Error::CALL_SUPER_PARENT, getSrcInfo()); - auto funcTyp = ctx->getRealizationBase()->type->getFunc(); + auto funcTyp = ctx->getBase()->type->getFunc(); if (!funcTyp || !funcTyp->ast->hasAttr(Attr::Method)) E(Error::CALL_SUPER_PARENT, getSrcInfo()); if (funcTyp->getArgTypes().empty()) @@ -791,7 +793,7 @@ ExprPtr TypecheckVisitor::transformSuper() { ExprPtr TypecheckVisitor::transformPtr(CallExpr *expr) { auto id = expr->args[0].value->getId(); auto val = id ? ctx->find(id->value) : nullptr; - if (!val || val->kind != TypecheckItem::Var) + if (!val || !val->isVar()) E(Error::CALL_PTR_VAR, expr->args[0]); transform(expr->args[0].value); @@ -859,12 +861,12 @@ ExprPtr TypecheckVisitor::transformIsInstance(CallExpr *expr) { if (tag == -1) return transform(N(false)); return transform(N( - N(N("__internal__.union_get_tag:0"), expr->args[0].value), + N(N("__internal__.union_get_tag"), expr->args[0].value), "==", N(tag))); } else if (typExpr->type->is("pyobj") && !typExpr->isType()) { if (typ->is("pyobj")) { expr->staticValue.type = StaticValue::NOT_STATIC; - return transform(N(N("std.internal.python._isinstance:0"), + return transform(N(N("std.internal.python._isinstance"), expr->args[0].value, expr->args[1].value)); } else { return transform(N(false)); @@ -923,7 +925,7 @@ ExprPtr TypecheckVisitor::transformHasAttr(CallExpr *expr) { ->evaluate() .getString(); std::vector> args{{"", typ}}; - if (expr->expr->isId("hasattr:0")) { + if (expr->expr->isId("hasattr")) { // Case: the first hasattr overload allows passing argument types via *args auto tup = expr->args[1].value->getTuple(); seqassert(tup, "not a tuple"); @@ -933,7 +935,6 @@ ExprPtr TypecheckVisitor::transformHasAttr(CallExpr *expr) { return nullptr; args.emplace_back("", a->getType()); } - auto kwtup = expr->args[2].value->origExpr->getCall(); seqassert(expr->args[2].value->origExpr && expr->args[2].value->origExpr->getCall(), "expected call: {}", expr->args[2].value->origExpr); auto kw = expr->args[2].value->origExpr->getCall(); @@ -1075,7 +1076,7 @@ ExprPtr TypecheckVisitor::transformHasRttiFn(CallExpr *expr) { return nullptr; auto c = in(ctx->cache->classes, t->name); seqassert(c, "bad class {}", t->name); - return transform(N(const_cast(c)->rtti)); + return transform(N(c->hasRTTI())); } // Transform internal.static calls @@ -1269,33 +1270,37 @@ std::vector TypecheckVisitor::getSuperTypes(const ClassTypePtr &cl /// Find all generics on which a function depends on and add them to the current /// context. void TypecheckVisitor::addFunctionGenerics(const FuncType *t) { + auto addT = [&](const std::string &name, const types::TypePtr &type) { + TypeContext::Item v = nullptr; + if (auto c = type->getClass()) { + v = ctx->addType(ctx->cache->rev(name), name, c); + } else { + v = ctx->addType(ctx->cache->rev(name), name, type); + v->generic = true; + } + // LOG(" <=> {} :: {} ({}) / {}", type->debugString(2), ctx->cache->rev(name), name, + // v->isType()); + ctx->add(name, v); + }; for (auto parent = t->funcParent; parent;) { if (auto f = parent->getFunc()) { // Add parent function generics - for (auto &g : f->funcGenerics) { - // LOG(" -> {} := {}", g.name, g.type->debugString(true)); - ctx->addType(g.name, g.name, getSrcInfo(), g.type); - } + for (auto &g : f->funcGenerics) + addT(g.name, g.type); parent = f->funcParent; } else { // Add parent class generics seqassert(parent->getClass(), "not a class: {}", parent); - for (auto &g : parent->getClass()->generics) { - // LOG(" => {} := {}", g.name, g.type->debugString(true)); - ctx->addType(g.name, g.name, getSrcInfo(), g.type); - } - for (auto &g : parent->getClass()->hiddenGenerics) { - // LOG(" :> {} := {}", g.name, g.type->debugString(true)); - ctx->addType(g.name, g.name, getSrcInfo(), g.type); - } + for (auto &g : parent->getClass()->generics) + addT(g.name, g.type); + for (auto &g : parent->getClass()->hiddenGenerics) + addT(g.name, g.type); break; } } // Add function generics - for (auto &g : t->funcGenerics) { - // LOG(" >> {} := {}", g.name, g.type->debugString(true)); - ctx->addType(g.name, g.name, getSrcInfo(), g.type); - } + for (auto &g : t->funcGenerics) + addT(g.name, g.type); } /// Generate a partial type `Partial.N` for a given function. diff --git a/codon/parser/visitors/typecheck/class.cpp b/codon/parser/visitors/typecheck/class.cpp index bff1a7a1..e97ffd64 100644 --- a/codon/parser/visitors/typecheck/class.cpp +++ b/codon/parser/visitors/typecheck/class.cpp @@ -25,13 +25,14 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { std::vector &argsToParse = stmt->args; // classItem will be added later when the scope is different - auto classItem = std::make_shared(TypecheckItem::Type, "", "", - ctx->getModule(), ctx->scope.blocks); + auto classItem = std::make_shared("", "", ctx->getModule(), nullptr, + ctx->scope.blocks); classItem->setSrcInfo(stmt->getSrcInfo()); types::ClassTypePtr typ = nullptr; if (!stmt->attributes.has(Attr::Extend)) { classItem->canonicalName = canonicalName = - ctx->generateCanonicalName(name, !stmt->attributes.has(Attr::Internal)); + ctx->generateCanonicalName(name, !stmt->attributes.has(Attr::Internal), + /* noSuffix*/ stmt->attributes.has(Attr::Internal)); typ = Type::makeType(ctx->cache, canonicalName, name, stmt->isRecord())->getClass(); if (stmt->isRecord() && stmt->hasAttr("__notuple__")) @@ -50,6 +51,9 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { if (!stmt->attributes.has(Attr::Tuple)) { ctx->add(name, classItem); ctx->addAlwaysVisible(classItem); + // LOG("added typ/{}: {}", + // classItem->isVar() ? "v" : (classItem->isFunc() ? "f" : "t"), + // classItem->canonicalName); } } else { // Find the canonical name and AST of the class that is to be extended @@ -59,6 +63,7 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { if (!val || !val->isType()) E(Error::CLASS_ID_NOT_FOUND, getSrcInfo(), name); canonicalName = val->canonicalName; + typ = val->type->getClass(); const auto &astIter = ctx->cache->classes.find(canonicalName); if (astIter == ctx->cache->classes.end()) { E(Error::CLASS_ID_NOT_FOUND, getSrcInfo(), name); @@ -74,36 +79,38 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { try { // Add the class base TypeContext::BaseGuard br(ctx.get(), canonicalName); + ctx->getBase()->type = typ; // Parse and add class generics std::vector args; std::pair autoDeducedInit{nullptr, nullptr}; if (stmt->attributes.has("deduce") && args.empty()) { + // todo)) do this // Auto-detect generics and fields - autoDeducedInit = autoDeduceMembers(stmt, args); + // autoDeducedInit = autoDeduceMembers(stmt, args); + } else if (stmt->attributes.has(Attr::Extend)) { + for (auto &a : argsToParse) { + if (a.status != Param::Generic) + continue; + auto val = ctx->forceFind(a.name); + auto generic = ctx->instantiate(val->type); + generic->getUnbound()->id = val->type->getLink()->id; + ctx->addType(ctx->cache->rev(val->canonicalName), val->canonicalName, generic) + ->generic = true; + } } else { // Add all generics before parent classes, fields and methods for (auto &a : argsToParse) { if (a.status != Param::Generic) continue; - std::string genName, varName; - if (stmt->attributes.has(Attr::Extend)) - varName = a.name, genName = ctx->cache->rev(a.name); - else - varName = ctx->generateCanonicalName(a.name), genName = a.name; + auto varName = ctx->generateCanonicalName(a.name), genName = a.name; auto generic = ctx->getUnbound(); auto typId = generic->id; - generic->getLink()->genericName = ctx->cache->rev(a.name); + generic->getLink()->genericName = genName; if (a.defaultValue) { auto defType = transformType(clone(a.defaultValue)); - if (a.status == Param::Generic) { - generic->defaultType = defType->type; - } else { - // Hidden generics can be outright replaced (e.g., `T=int`). - // Unify them immediately. - unify(defType->type, generic); - } + generic->defaultType = defType->type; } if (auto ti = CAST(a.type, InstantiateExpr)) { // Parse TraitVar @@ -114,17 +121,16 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { else generic->getLink()->trait = std::make_shared(l); } - if (auto st = getStaticGeneric(a.type.get())) { - generic->isStatic = true; - auto val = ctx->addVar(genName, varName, a.type->getSrcInfo(), generic); + generic->isStatic = st; + auto val = ctx->addVar(genName, varName, generic); val->generic = true; val->staticType = st; } else { - ctx->addType(genName, varName, a.type->getSrcInfo(), generic)->generic = true; + ctx->addType(genName, varName, generic)->generic = true; } - ClassType::Generic g{a.name, ctx->cache->rev(a.name), - generic->generalize(ctx->typecheckLevel), typId}; + ClassType::Generic g{varName, genName, generic->generalize(ctx->typecheckLevel), + typId}; if (a.status == Param::Generic) { typ->generics.push_back(g); } else { @@ -132,9 +138,6 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { } args.emplace_back(varName, transformType(clone(a.type), false), transformType(clone(a.defaultValue), false), a.status); - if (!stmt->attributes.has(Attr::Extend) && a.status == Param::Normal) - ctx->cache->classes[canonicalName].fields.push_back( - Cache::Class::ClassField{varName, nullptr, canonicalName}); } } @@ -157,12 +160,12 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { std::vector staticBaseASTs, baseASTs; if (!stmt->attributes.has(Attr::Extend)) { staticBaseASTs = parseBaseClasses(stmt->staticBaseClasses, args, stmt->attributes, - canonicalName); + canonicalName, nullptr, typ); if (ctx->cache->isJit && !stmt->baseClasses.empty()) E(Error::CUSTOM, stmt->baseClasses[0], "inheritance is not yet supported in JIT mode"); parseBaseClasses(stmt->baseClasses, args, stmt->attributes, canonicalName, - transformedTypeAst); + transformedTypeAst, typ); } // A ClassStmt will be separated into class variable assignments, method-free @@ -172,23 +175,25 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { // Collect class fields for (auto &a : argsToParse) { if (a.status == Param::Normal) { - if (!ClassStmt::isClassVar(a)) { - args.emplace_back(a.name, transformType(clone(a.type), false), - transform(clone(a.defaultValue), true)); - if (!stmt->attributes.has(Attr::Extend)) { - ctx->cache->classes[canonicalName].fields.push_back( - Cache::Class::ClassField{a.name, nullptr, canonicalName}); - } - } else if (!stmt->attributes.has(Attr::Extend)) { + if (ClassStmt::isClassVar(a)) { // Handle class variables. Transform them later to allow self-references auto name = format("{}.{}", canonicalName, a.name); - prependStmts->push_back(N(N(name), nullptr, nullptr)); - ctx->cache->addGlobal(name); + // prependStmts->push_back(N(N(name), nullptr, nullptr)); + // ctx->cache->addGlobal(name); auto assign = N(N(name), a.defaultValue, a.type ? a.type->getIndex()->index : nullptr); - assign->setUpdate(); varStmts.push_back(assign); ctx->cache->classes[canonicalName].classVars[a.name] = name; + } else if (!stmt->attributes.has(Attr::Extend)) { + std::string varName = a.name; + // stmt->attributes.has(Attr::Extend) + // ? a.name + // : ctx->generateCanonicalName(a.name); + args.emplace_back(varName, transformType(clone(a.type), false), + transform(clone(a.defaultValue), true)); + LOG(" -> {}", varName); + ctx->cache->classes[canonicalName].fields.push_back(Cache::Class::ClassField{ + varName, args.back().type->getType(), canonicalName}); } } } @@ -196,48 +201,28 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { // ASTs for member arguments to be used for populating magic methods std::vector memberArgs; for (auto &a : args) { - if (a.status == Param::Normal) + if (a.status == Param::Normal) { memberArgs.push_back(a.clone()); + } } // Handle class members - ctx->typecheckLevel++; // to avoid unifying generics early - auto &fields = ctx->cache->classes[stmt->name].fields; - for (auto ai = 0, aj = 0; ai < stmt->args.size(); ai++) - if (stmt->args[ai].status == Param::Normal) { - fields[aj].type = transformType(stmt->args[ai].type) - ->getType() - ->generalize(ctx->typecheckLevel - 1); - fields[aj].type->setSrcInfo(stmt->args[ai].type->getSrcInfo()); - if (stmt->isRecord()) - typ->getRecord()->args.push_back(fields[aj].type); - aj++; - } - ctx->typecheckLevel--; - - // Handle MRO - for (auto &m : ctx->cache->classes[stmt->name].mro) { - m = transformType(m); - } - - // Generalize generics and remove them from the context - for (const auto &g : args) - if (g.status != Param::Normal) { - auto generic = ctx->forceFind(g.name)->type; - if (g.status == Param::Generic) { - // Generalize generics. Hidden generics are linked to the class generics so - // ignore them - seqassert(generic && generic->getLink() && - generic->getLink()->kind != types::LinkType::Link, - "generic has been unified"); - generic->getLink()->kind = LinkType::Generic; + if (!stmt->attributes.has(Attr::Extend)) { + ctx->typecheckLevel++; // to avoid unifying generics early + auto &fields = ctx->cache->classes[canonicalName].fields; + for (auto ai = 0, aj = 0; ai < stmt->args.size(); ai++) + if (stmt->args[ai].status == Param::Normal && + !ClassStmt::isClassVar(stmt->args[ai])) { + fields[aj].type = transformType(stmt->args[ai].type) + ->getType() + ->generalize(ctx->typecheckLevel - 1); + fields[aj].type->setSrcInfo(stmt->args[ai].type->getSrcInfo()); + if (stmt->isRecord()) + typ->getRecord()->args.push_back(fields[aj].type); + aj++; } - ctx->remove(g.name); - } - // Debug information - LOG_REALIZE("[class] {} -> {}", stmt->name, typ); - for (auto &m : ctx->cache->classes[stmt->name].fields) - LOG_REALIZE(" - member: {}: {}", m.name, m.type); + ctx->typecheckLevel--; + } // Parse class members (arguments) and methods if (!stmt->attributes.has(Attr::Extend)) { @@ -246,15 +231,23 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { // Ensure that class binding does not shadow anything. // Class bindings cannot be dominated either auto v = ctx->find(name); - if (v && v->noShadow) + if (v && !v->canShadow) E(Error::CLASS_INVALID_BIND, stmt, name); ctx->add(name, classItem); ctx->addAlwaysVisible(classItem); + // LOG("added typ/{}: {}", + // classItem->isVar() ? "v" : (classItem->isFunc() ? "f" : "t"), + // classItem->canonicalName); } // Create a cached AST. - stmt->attributes.module = - format("{}{}", ctx->moduleName.status == ImportFile::STDLIB ? "std::" : "::", - ctx->moduleName.module); + stmt->attributes.module = ctx->moduleName.status == ImportFile::STDLIB + ? STDLIB_IMPORT + : ctx->moduleName.path; + ; + // format( + // "{}{}", + // ctx->moduleName.status == ImportFile::STDLIB ? "std::" : "::", + // ctx->moduleName.module); ctx->cache->classes[canonicalName].ast = N(canonicalName, args, N(), stmt->attributes); ctx->cache->classes[canonicalName].ast->baseClasses = stmt->baseClasses; @@ -263,6 +256,11 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { ctx->cache->classes[canonicalName].ast->validate(); ctx->cache->classes[canonicalName].module = ctx->getModule(); + // Handle MRO + for (auto &m : ctx->cache->classes[canonicalName].mro) { + m = transformType(m); + } + // Codegen default magic methods for (auto &m : stmt->attributes.magics) { fnStmts.push_back(transform( @@ -335,6 +333,28 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { } } } + + // Generalize generics and remove them from the context + for (const auto &g : args) + if (g.status != Param::Normal) { + auto generic = ctx->forceFind(g.name)->type; + if (g.status == Param::Generic) { + // Generalize generics. Hidden generics are linked to the class generics so + // ignore them + seqassert(generic && generic->getLink() && + generic->getLink()->kind != types::LinkType::Link, + "generic has been unified"); + generic->getLink()->kind = LinkType::Generic; + } + ctx->remove(g.name); + } + // Debug information + LOG("[class] {} -> {:D} / {}", canonicalName, typ, + ctx->cache->classes[canonicalName].fields.size()); + for (auto &m : ctx->cache->classes[canonicalName].fields) + LOG(" - member: {}: {:D}", m.name, m.type); + for (auto &m : ctx->cache->classes[canonicalName].methods) + LOG(" - method: {}: {}", m.first, m.second); } catch (const exc::ParserException &) { if (!stmt->attributes.has(Attr::Tuple)) ctx->remove(name); @@ -348,16 +368,18 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { if (!stmt->attributes.has(Attr::Extend)) { auto c = ctx->cache->classes[canonicalName].ast; seqassert(c, "not a class AST for {}", canonicalName); + c->setDone(); clsStmts.push_back(c); } clsStmts.insert(clsStmts.end(), fnStmts.begin(), fnStmts.end()); for (auto &a : varStmts) { // Transform class variables here to allow self-references - if (auto assign = a->getAssign()) { - transform(assign->rhs); - transformType(assign->type); - } + transform(a); + // if (auto assign = a->getAssign()) { + // transform(assign->rhs); + // transformType(assign->type); + // } clsStmts.push_back(a); } resultStmt = N(clsStmts); @@ -368,9 +390,11 @@ void TypecheckVisitor::visit(ClassStmt *stmt) { /// @param args Class fields that are to be updated with base classes' fields. /// @param typeAst Transformed AST for base class type (e.g., `A[T]`). /// Only set when dealing with dynamic polymorphism. -std::vector TypecheckVisitor::parseBaseClasses( - std::vector &baseClasses, std::vector &args, const Attr &attr, - const std::string &canonicalName, const ExprPtr &typeAst) { +std::vector +TypecheckVisitor::parseBaseClasses(std::vector &baseClasses, + std::vector &args, const Attr &attr, + const std::string &canonicalName, + const ExprPtr &typeAst, types::ClassTypePtr &typ) { std::vector asts; // MAJOR TODO: fix MRO it to work with generic classes (maybe replacements? IDK...) @@ -392,7 +416,7 @@ std::vector TypecheckVisitor::parseBaseClasses( } } - auto cachedCls = const_cast(in(ctx->cache->classes, name)); + Cache::Class *cachedCls = in(ctx->cache->classes, name); if (!cachedCls) E(Error::CLASS_ID_NOT_FOUND, getSrcInfo(), ctx->cache->rev(name)); asts.push_back(cachedCls->ast.get()); @@ -418,6 +442,9 @@ std::vector TypecheckVisitor::parseBaseClasses( nGenerics += a.status == Param::Generic; int si = 0; for (auto &a : asts.back()->args) { + if (a.status == Param::Normal) + continue; + if (a.status == Param::Generic) { if (si == subs.size()) E(Error::GENERICS_MISMATCH, cls, ctx->cache->rev(asts.back()->name), @@ -427,14 +454,40 @@ std::vector TypecheckVisitor::parseBaseClasses( } else if (a.status == Param::HiddenGeneric) { args.emplace_back(a); } - if (a.status != Param::Normal) { - if (auto st = getStaticGeneric(a.type.get())) { - auto val = ctx->addVar(a.name, a.name, a.type->getSrcInfo()); - val->generic = true; - val->staticType = st; - } else { - ctx->addType(a.name, a.name, a.type->getSrcInfo())->generic = true; - } + + auto generic = ctx->getUnbound(); + auto typId = generic->id; + generic->getLink()->genericName = ctx->cache->rev(a.name); + if (args.back().defaultValue) { + auto defType = transformType(clone(args.back().defaultValue)); + // Hidden generics can be outright replaced (e.g., `T=int`). + // Unify them immediately. + unify(defType->type, generic); + } + if (auto ti = CAST(a.type, InstantiateExpr)) { + // Parse TraitVar + seqassert(ti->typeExpr->isId(TYPE_TYPEVAR), "not a TypeVar instantiation"); + auto l = transformType(ti->typeParams[0])->type; + if (l->getLink() && l->getLink()->trait) + generic->getLink()->trait = l->getLink()->trait; + else + generic->getLink()->trait = std::make_shared(l); + } + + if (auto st = getStaticGeneric(a.type.get())) { + generic->isStatic = st; + auto val = ctx->addVar(a.name, a.name, generic); + val->generic = true; + val->staticType = st; + } else { + ctx->addType(a.name, a.name, generic)->generic = true; + } + ClassType::Generic g{a.name, a.name, generic->generalize(ctx->typecheckLevel), + typId}; + if (a.status == Param::Generic) { + typ->generics.push_back(g); + } else { + typ->hiddenGenerics.push_back(g); } } if (si != subs.size()) @@ -455,9 +508,10 @@ std::vector TypecheckVisitor::parseBaseClasses( seqassert(ctx->cache->classes[ast->name].fields[ai].name == a.name, "bad class fields: {} vs {}", ctx->cache->classes[ast->name].fields[ai].name, a.name); - args.emplace_back(name, a.type, a.defaultValue); + args.emplace_back(name, transformType(a.type), transform(a.defaultValue)); ctx->cache->classes[canonicalName].fields.push_back(Cache::Class::ClassField{ - name, nullptr, ctx->cache->classes[ast->name].fields[ai].baseClass}); + name, args.back().type->getType(), + ctx->cache->classes[ast->name].fields[ai].baseClass}); ai++; } } @@ -495,27 +549,29 @@ TypecheckVisitor::autoDeduceMembers(ClassStmt *stmt, std::vector &args) { for (const auto &sp : getClassMethods(stmt->suite)) if (sp && sp->getFunction()) { auto f = sp->getFunction(); - if (f->name == "__init__" && !f->args.empty() && f->args[0].name == "self") { - // Set up deducedMembers that will be populated during AssignStmt evaluation - ctx->getBase()->deducedMembers = std::make_shared>(); - auto transformed = transform(sp); - transformed->getFunction()->attributes.set(Attr::RealizeWithoutSelf); - ctx->cache->functions[transformed->getFunction()->name].ast->attributes.set( - Attr::RealizeWithoutSelf); - int i = 0; - // Once done, add arguments - for (auto &m : *(ctx->getBase()->deducedMembers)) { - auto varName = ctx->generateCanonicalName(format("T{}", ++i)); - auto memberName = ctx->cache->rev(varName); - ctx->addType(memberName, varName, stmt->getSrcInfo())->generic = true; - args.emplace_back(varName, N("type"), nullptr, Param::Generic); - args.emplace_back(m, N(varName)); - ctx->cache->classes[stmt->name].fields.push_back( - Cache::Class::ClassField{m, nullptr, stmt->name}); - } - ctx->getBase()->deducedMembers = nullptr; - return {transformed, f}; - } + // todo)) do this + // if (f->name == "__init__" && !f->args.empty() && f->args[0].name == "self") { + // // Set up deducedMembers that will be populated during AssignStmt evaluation + // ctx->getBase()->deducedMembers = + // std::make_shared>(); auto transformed = + // transform(sp); + // transformed->getFunction()->attributes.set(Attr::RealizeWithoutSelf); + // ctx->cache->functions[transformed->getFunction()->name].ast->attributes.set( + // Attr::RealizeWithoutSelf); + // int i = 0; + // // Once done, add arguments + // for (auto &m : *(ctx->getBase()->deducedMembers)) { + // auto varName = ctx->generateCanonicalName(format("T{}", ++i)); + // auto memberName = ctx->cache->rev(varName); + // ctx->addType(memberName, varName, stmt->getSrcInfo())->generic = true; + // args.emplace_back(varName, N("type"), nullptr, Param::Generic); + // args.emplace_back(m, N(varName)); + // ctx->cache->classes[canonicalName].fields.push_back( + // Cache::Class::ClassField{m, nullptr, canonicalName}); + // } + // ctx->getBase()->deducedMembers = nullptr; + // return {transformed, f}; + // } } return {nullptr, nullptr}; } @@ -594,8 +650,9 @@ StmtPtr TypecheckVisitor::codegenMagic(const std::string &op, const ExprPtr &typ attr.set("autogenerated"); std::vector args; + args.reserve(allArgs.size()); for (auto &a : allArgs) - args.push_back(a); + args.push_back(a.clone()); if (op == "new") { ret = typExpr->clone(); @@ -621,7 +678,7 @@ StmtPtr TypecheckVisitor::codegenMagic(const std::string &op, const ExprPtr &typ a.defaultValue ? clone(a.defaultValue) : N(clone(a.type))); } - } else if (op == "raw") { + } else if (op == "raw" || op == "dict") { // Classes: def __raw__(self: T) fargs.emplace_back("self", typExpr->clone()); stmts.emplace_back(N(N(NS(op), I("self")))); @@ -651,7 +708,7 @@ StmtPtr TypecheckVisitor::codegenMagic(const std::string &op, const ExprPtr &typ fargs.emplace_back("obj", typExpr->clone()); ret = I("bool"); stmts.emplace_back(N(N(NS(op), I("self"), I("obj")))); - } else if (op == "hash") { + } else if (op == "hash" || op == "len") { // def __hash__(self: T) -> int fargs.emplace_back("self", typExpr->clone()); ret = I("int"); @@ -661,26 +718,16 @@ StmtPtr TypecheckVisitor::codegenMagic(const std::string &op, const ExprPtr &typ fargs.emplace_back("self", typExpr->clone()); fargs.emplace_back("dest", N(I("Ptr"), I("byte"))); stmts.emplace_back(N(N(NS(op), I("self"), I("dest")))); - } else if (op == "unpickle") { + } else if (op == "unpickle" || op == "from_py") { // def __unpickle__(src: Ptr[byte]) -> T fargs.emplace_back("src", N(I("Ptr"), I("byte"))); ret = typExpr->clone(); stmts.emplace_back(N(N(NS(op), I("src"), typExpr->clone()))); - } else if (op == "len") { - // def __len__(self: T) -> int - fargs.emplace_back("self", typExpr->clone()); - ret = I("int"); - stmts.emplace_back(N(N(NS(op), I("self")))); } else if (op == "to_py") { // def __to_py__(self: T) -> Ptr[byte] fargs.emplace_back("self", typExpr->clone()); ret = N(I("Ptr"), I("byte")); stmts.emplace_back(N(N(NS(op), I("self")))); - } else if (op == "from_py") { - // def __from_py__(src: Ptr[byte]) -> T - fargs.emplace_back("src", N(I("Ptr"), I("byte"))); - ret = typExpr->clone(); - stmts.emplace_back(N(N(NS(op), I("src"), typExpr->clone()))); } else if (op == "to_gpu") { // def __to_gpu__(self: T, cache) -> T fargs.emplace_back("self", typExpr->clone()); @@ -702,10 +749,6 @@ StmtPtr TypecheckVisitor::codegenMagic(const std::string &op, const ExprPtr &typ fargs.emplace_back("self", typExpr->clone()); ret = I("str"); stmts.emplace_back(N(N(NS(op), I("self")))); - } else if (op == "dict") { - // def __dict__(self: T) - fargs.emplace_back("self", typExpr->clone()); - stmts.emplace_back(N(N(NS(op), I("self")))); } else if (op == "add") { // def __add__(self, obj) fargs.emplace_back("self", typExpr->clone()); diff --git a/codon/parser/visitors/typecheck/ctx.cpp b/codon/parser/visitors/typecheck/ctx.cpp index 6c4677ee..1e003ed3 100644 --- a/codon/parser/visitors/typecheck/ctx.cpp +++ b/codon/parser/visitors/typecheck/ctx.cpp @@ -17,84 +17,84 @@ using namespace codon::error; namespace codon::ast { -TypecheckItem::TypecheckItem(TypecheckItem::Kind kind, std::string baseName, - std::string canonicalName, std::string moduleName, - std::vector scope, std::string importPath, - types::TypePtr type) - : kind(kind), baseName(std::move(baseName)), - canonicalName(std::move(canonicalName)), moduleName(std::move(moduleName)), - scope(std::move(scope)), importPath(std::move(importPath)), - type(std::move(type)) {} +TypecheckItem::TypecheckItem(std::string canonicalName, std::string baseName, + std::string moduleName, types::TypePtr type, + std::vector scope) + : canonicalName(std::move(canonicalName)), baseName(std::move(baseName)), + moduleName(std::move(moduleName)), type(std::move(type)), + scope(std::move(scope)) {} TypeContext::TypeContext(Cache *cache, std::string filename) : Context(std::move(filename)), cache(cache) { - bases.emplace_back(""); + bases.emplace_back(); scope.blocks.emplace_back(scope.counter = 0); - realizationBases.emplace_back(); pushSrcInfo(cache->generateSrcInfo()); // Always have srcInfo() around } -TypeContext::Base::Base(std::string name, Attr *attributes) - : name(std::move(name)), attributes(attributes) {} - void TypeContext::add(const std::string &name, const TypeContext::Item &var) { auto v = find(name); - if (v && v->noShadow) + if (v && !v->canShadow) E(Error::ID_INVALID_BIND, getSrcInfo(), name); Context::add(name, var); } TypeContext::Item TypeContext::addVar(const std::string &name, const std::string &canonicalName, - const SrcInfo &srcInfo, - const types::TypePtr &type) { + const types::TypePtr &type, + const SrcInfo &srcInfo) { seqassert(!canonicalName.empty(), "empty canonical name for '{}'", name); - auto t = std::make_shared(TypecheckItem::Var, getBaseName(), - canonicalName, getModule(), scope.blocks); + seqassert(type->getLink(), "bad var"); + auto t = std::make_shared(canonicalName, getBaseName(), getModule(), + type, scope.blocks); t->setSrcInfo(srcInfo); - t->type = type; Context::add(name, t); - Context::add(canonicalName, t); + addAlwaysVisible(t); + // LOG("added var/{}: {}", t->isVar() ? "v" : (t->isFunc() ? "f" : "t"), + // canonicalName); return t; } TypeContext::Item TypeContext::addType(const std::string &name, const std::string &canonicalName, - const SrcInfo &srcInfo, - const types::TypePtr &type) { + const types::TypePtr &type, + const SrcInfo &srcInfo) { seqassert(!canonicalName.empty(), "empty canonical name for '{}'", name); - auto t = std::make_shared(TypecheckItem::Type, getBaseName(), - canonicalName, getModule(), scope.blocks); + // seqassert(type->getClass(), "bad type"); + auto t = std::make_shared(canonicalName, getBaseName(), getModule(), + type, scope.blocks); t->setSrcInfo(srcInfo); - t->type = type; Context::add(name, t); - Context::add(canonicalName, t); + addAlwaysVisible(t); + // LOG("added typ/{}: {}", t->isVar() ? "v" : (t->isFunc() ? "f" : "t"), + // canonicalName); return t; } TypeContext::Item TypeContext::addFunc(const std::string &name, const std::string &canonicalName, - const SrcInfo &srcInfo, - const types::TypePtr &type) { + const types::TypePtr &type, + const SrcInfo &srcInfo) { seqassert(!canonicalName.empty(), "empty canonical name for '{}'", name); - auto t = std::make_shared(TypecheckItem::Func, getBaseName(), - canonicalName, getModule(), scope.blocks); + seqassert(type->getFunc(), "bad func"); + auto t = std::make_shared(canonicalName, getBaseName(), getModule(), + type, scope.blocks); t->setSrcInfo(srcInfo); - t->type = type; Context::add(name, t); - Context::add(canonicalName, t); + addAlwaysVisible(t); + // LOG("added fun/{}: {}", t->isVar() ? "v" : (t->isFunc() ? "f" : "t"), + // canonicalName); return t; } TypeContext::Item TypeContext::addAlwaysVisible(const TypeContext::Item &item) { - auto i = std::make_shared(item->kind, item->baseName, - item->canonicalName, item->moduleName, - std::vector{0}, item->importPath); - auto stdlib = cache->imports[STDLIB_IMPORT].ctx; - if (!stdlib->find(i->canonicalName)) { - stdlib->add(i->canonicalName, i); + if (!cache->typeCtx->Context::find(item->canonicalName)) { + cache->typeCtx->add(item->canonicalName, item); + + // Realizations etc. + if (!in(cache->reverseIdentifierLookup, item->canonicalName)) + cache->reverseIdentifierLookup[item->canonicalName] = item->canonicalName; } - return i; + return item; } TypeContext::Item TypeContext::find(const std::string &name) const { @@ -106,7 +106,12 @@ TypeContext::Item TypeContext::find(const std::string &name) const { // Note: the standard library items cannot be dominated. auto stdlib = cache->imports[STDLIB_IMPORT].ctx; if (stdlib.get() != this) - t = stdlib->find(name); + t = stdlib->Context::find(name); + + // Maybe we are looking for a canonical identifier? + if (!t && cache->typeCtx.get() != this) + t = cache->typeCtx->Context::find(name); + return t; } @@ -116,158 +121,6 @@ TypeContext::Item TypeContext::forceFind(const std::string &name) const { return f; } -TypeContext::Item TypeContext::findDominatingBinding(const std::string &name, - TypecheckVisitor *tv) { - auto it = map.find(name); - if (it == map.end()) { - return find(name); - } else if (isCanonicalName(name)) { - return *(it->second.begin()); - } - seqassert(!it->second.empty(), "corrupted TypecheckContext ({})", name); - - // The item is found. Let's see is it accessible now. - - std::string canonicalName; - auto lastGood = it->second.begin(); - bool isOutside = (*lastGood)->getBaseName() != getBaseName(); - int prefix = int(scope.blocks.size()); - // Iterate through all bindings with the given name and find the closest binding that - // dominates the current scope. - for (auto i = it->second.begin(); i != it->second.end(); i++) { - // Find the longest block prefix between the binding and the current scope. - int p = std::min(prefix, int((*i)->scope.size())); - while (p >= 0 && (*i)->scope[p - 1] != scope.blocks[p - 1]) - p--; - // We reached the toplevel. Break. - if (p < 0) - break; - // We went outside the function scope. Break. - if (!isOutside && (*i)->getBaseName() != getBaseName()) - break; - prefix = p; - lastGood = i; - // The binding completely dominates the current scope. Break. - if ((*i)->scope.size() <= scope.blocks.size() && - (*i)->scope.back() == scope.blocks[(*i)->scope.size() - 1]) - break; - } - seqassert(lastGood != it->second.end(), "corrupted scoping ({})", name); - if (lastGood != it->second.begin() && !(*lastGood)->isVar()) - E(Error::CLASS_INVALID_BIND, getSrcInfo(), name); - - bool hasUsed = false; - types::TypePtr type = nullptr; - if ((*lastGood)->scope.size() == prefix) { - // The current scope is dominated by a binding. Use that binding. - canonicalName = (*lastGood)->canonicalName; - type = (*lastGood)->type; - } else { - // The current scope is potentially reachable by multiple bindings that are - // not dominated by a common binding. Create such binding in the scope that - // dominates (covers) all of them. - canonicalName = generateCanonicalName(name); - auto item = std::make_shared( - (*lastGood)->kind, (*lastGood)->baseName, canonicalName, - (*lastGood)->moduleName, - std::vector(scope.blocks.begin(), scope.blocks.begin() + prefix), - (*lastGood)->importPath); - item->accessChecked = {(*lastGood)->scope}; - type = item->type = getUnbound(getSrcInfo()); - lastGood = it->second.insert(++lastGood, item); - // Make sure to prepend a binding declaration: `var` and `var__used__ = False` - // to the dominating scope. - getBase()->preamble.push_back(tv->N( - tv->transform(tv->N(canonicalName)), nullptr, nullptr)); - getBase()->preamble.push_back(tv->N( - tv->transform(tv->N(fmt::format("{}.__used__", canonicalName))), - tv->transform(tv->N(false)), nullptr)); - - // Reached the toplevel? Register the binding as global. - if (prefix == 1) { - cache->addGlobal(canonicalName); - cache->addGlobal(fmt::format("{}.__used__", canonicalName)); - } - hasUsed = true; - } - // Remove all bindings after the dominant binding. - for (auto i = it->second.begin(); i != it->second.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. - - seqassert((*i)->canonicalName != canonicalName, "invalid replacement at {}: {}", - getSrcInfo(), canonicalName); - for (auto &ref : (*i)->references) { - ref->getId()->value = canonicalName; - tv->unify(type, ref->type); - } - - auto update = tv->N(tv->N(format("{}.__used__", canonicalName)), - tv->N(true)); - update->setUpdate(); - if (auto a = (*i)->root->getAssign()) { - a->lhs->getId()->value = canonicalName; - tv->unify(type, a->lhs->getType()); - if (hasUsed) { - if (a->preamble) { - a->preamble->getAssign()->lhs->getId()->value = update->lhs->getId()->value; - } else { - a->preamble = tv->transform(update); - } - } - } else if (auto ts = dynamic_cast((*i)->root)) { - for (auto &c : ts->catches) - if (c.var == (*i)->canonicalName) { - c.var = canonicalName; - c.exc->setAttr(ExprAttr::Dominated); - tv->unify(type, c.exc->getType()); - if (hasUsed) { - seqassert(c.suite->getSuite(), "not a Suite"); - if (c.suite->getSuite() && !c.suite->getSuite()->stmts.empty() && - c.suite->getSuite()->stmts[0]->getAssign() && - c.suite->getSuite()->stmts[0]->getAssign()->lhs->isId( - format("{}.__used__", (*i)->canonicalName))) { - c.suite->getSuite()->stmts[0]->getAssign()->lhs->getId()->value = - update->lhs->getId()->value; - } else { - c.suite->getSuite()->stmts.insert(c.suite->getSuite()->stmts.begin(), - tv->transform(update)); - } - } - } - } else if (auto fs = dynamic_cast((*i)->root)) { - fs->var->getId()->value = canonicalName; - fs->var->setAttr(ExprAttr::Dominated); - tv->unify(type, fs->var->getType()); - if (hasUsed) { - seqassert(fs->suite->getSuite(), "not a Suite"); - if (fs->suite->getSuite() && !fs->suite->getSuite()->stmts.empty() && - fs->suite->getSuite()->stmts[0]->getAssign() && - fs->suite->getSuite()->stmts[0]->getAssign()->lhs->isId( - format("{}.__used__", (*i)->canonicalName))) { - fs->suite->getSuite()->stmts[0]->getAssign()->lhs->getId()->value = - update->lhs->getId()->value; - } else { - fs->suite->getSuite()->stmts.insert(fs->suite->getSuite()->stmts.begin(), - tv->transform(update)); - } - } - } else { - seqassert(false, "bad identifier root: '{}'", canonicalName); - } - - auto it = std::find(stack.front().begin(), stack.front().end(), name); - if (it != stack.front().end()) - stack.front().erase(it); - } - it->second.erase(it->second.begin(), lastGood); - return it->second.front(); -} - /// Getters and setters std::string TypeContext::getBaseName() const { return bases.back().name; } @@ -287,20 +140,23 @@ bool TypeContext::isCanonicalName(const std::string &name) const { } std::string TypeContext::generateCanonicalName(const std::string &name, - bool includeBase, bool zeroId) const { + bool includeBase, bool noSuffix) const { std::string newName = name; bool alreadyGenerated = name.find('.') != std::string::npos; if (includeBase && !alreadyGenerated) { std::string base = getBaseName(); if (base.empty()) base = getModule(); - if (base == "std.internal.core") + if (base == "std.internal.core") { + noSuffix = true; base = ""; + } newName = (base.empty() ? "" : (base + ".")) + newName; } auto num = cache->identifierCount[newName]++; - newName = format("{}.{}", newName, num); - if (name != newName && !zeroId) + if (!noSuffix && !alreadyGenerated) + newName = format("{}.{}", newName, num); + if (name != newName) cache->identifierCount[newName]++; cache->reverseIdentifierLookup[newName] = name; return newName; @@ -308,7 +164,106 @@ std::string TypeContext::generateCanonicalName(const std::string &name, void TypeContext::enterConditionalBlock() { scope.blocks.push_back(++scope.counter); } -void TypeContext::leaveConditionalBlock(std::vector *stmts) { +ExprPtr NameVisitor::transform(const std::shared_ptr &expr) { + NameVisitor v(tv); + if (expr) + expr->accept(v); + return v.resultExpr ? v.resultExpr : expr; +} +ExprPtr NameVisitor::transform(std::shared_ptr &expr) { + NameVisitor v(tv); + if (expr) + expr->accept(v); + if (v.resultExpr) + expr = v.resultExpr; + return expr; +} +StmtPtr NameVisitor::transform(const std::shared_ptr &stmt) { + NameVisitor v(tv); + if (stmt) + stmt->accept(v); + return v.resultStmt ? v.resultStmt : stmt; +} +StmtPtr NameVisitor::transform(std::shared_ptr &stmt) { + NameVisitor v(tv); + if (stmt) + stmt->accept(v); + if (v.resultExpr) + stmt = v.resultStmt; + return stmt; +} +void NameVisitor::visit(IdExpr *expr) { + while (auto s = in(tv->getCtx()->getBase()->replacements, expr->value)) { + expr->value = s->first; + tv->unify(expr->type, tv->getCtx()->forceFind(s->first)->type); + } +} +void NameVisitor::visit(AssignStmt *stmt) { + seqassert(stmt->lhs->getId(), "invalid AssignStmt {}", stmt->lhs); + std::string lhs = stmt->lhs->getId()->value; + if (auto changed = in(tv->getCtx()->getBase()->replacements, lhs)) { + while (auto s = in(tv->getCtx()->getBase()->replacements, lhs)) + lhs = changed->first, changed = s; + if (stmt->rhs && changed->second) { + // Mark the dominating binding as used: `var.__used__ = True` + auto u = + N(N(fmt::format("{}.__used__", lhs)), N(true)); + u->setUpdate(); + stmt->setUpdate(); + resultStmt = N(u, stmt->shared_from_this()); + } else if (changed->second && !stmt->rhs) { + // This assignment was a declaration only. + // Just mark the dominating binding as used: `var.__used__ = True` + stmt->lhs = N(fmt::format("{}.__used__", lhs)); + stmt->rhs = N(true); + stmt->setUpdate(); + } + seqassert(stmt->rhs, "bad domination statement: '{}'", stmt->toString()); + } +} +void NameVisitor::visit(TryStmt *stmt) { + for (auto &c : stmt->catches) { + if (!c.var.empty()) { + // Handle dominated except bindings + auto changed = in(tv->getCtx()->getBase()->replacements, c.var); + while (auto s = in(tv->getCtx()->getBase()->replacements, c.var)) + c.var = s->first, changed = s; + if (changed && changed->second) { + auto update = + N(N(format("{}.__used__", c.var)), N(true)); + update->setUpdate(); + c.suite = N(update, c.suite); + } + if (changed) + c.exc->setAttr(ExprAttr::Dominated); + } + } +} +void NameVisitor::visit(ForStmt *stmt) { + auto var = stmt->var->getId(); + seqassert(var, "corrupt for variable: {}", stmt->var); + auto changed = in(tv->getCtx()->getBase()->replacements, var->value); + while (auto s = in(tv->getCtx()->getBase()->replacements, var->value)) + var->value = s->first, changed = s; + if (changed && changed->second) { + auto u = + N(N(format("{}.__used__", var->value)), N(true)); + u->setUpdate(); + stmt->suite = N(u, stmt->suite); + } + if (changed) + var->setAttr(ExprAttr::Dominated); +} + +void TypeContext::leaveConditionalBlock(std::vector *stmts, + TypecheckVisitor *tv) { + if (stmts && in(scope.stmts, scope.blocks.back())) { + stmts->insert(stmts->begin(), scope.stmts[scope.blocks.back()].begin(), + scope.stmts[scope.blocks.back()].end()); + NameVisitor nv(tv); + for (auto &s : *stmts) + nv.transform(s); + } scope.blocks.pop_back(); } @@ -334,17 +289,13 @@ TypeContext::Base *TypeContext::getClassBase() { return nullptr; } -TypeContext::RealizationBase *TypeContext::getRealizationBase() { - return &(realizationBases.back()); -} - -size_t TypeContext::getRealizationDepth() const { return realizationBases.size(); } +size_t TypeContext::getRealizationDepth() const { return bases.size(); } std::string TypeContext::getRealizationStackName() const { - if (realizationBases.empty()) + if (bases.empty()) return ""; std::vector s; - for (auto &b : realizationBases) + for (auto &b : bases) if (b.type) s.push_back(b.type->realizedName()); return join(s, ":"); @@ -577,21 +528,31 @@ int TypeContext::reorderNamedArgs(types::FuncType *func, void TypeContext::dump(int pad) { auto ordered = std::map(map.begin(), map.end()); - LOG("base: {}", getRealizationStackName()); + LOG("current module: {} ({})", moduleName.module, moduleName.path); + LOG("current base: {} / {}", getRealizationStackName(), getBase()->name); for (auto &i : ordered) { std::string s; auto t = i.second.front(); - LOG("{}{:.<25} {}", std::string(size_t(pad) * 2, ' '), i.first, t->type); + LOG("{}{:.<25}", std::string(size_t(pad) * 2, ' '), i.first); + LOG(" ... kind: {}", t->isType() * 100 + t->isFunc() * 10 + t->isVar()); + LOG(" ... canonical: {}", t->canonicalName); + LOG(" ... base: {}", t->baseName); + LOG(" ... module: {}", t->moduleName); + LOG(" ... type: {}", t->type ? t->type->debugString(2) : ""); + LOG(" ... scope: {}", t->scope); + LOG(" ... access: {}", t->accessChecked); + LOG(" ... shdw/dom: {} / {}", t->canShadow, t->avoidDomination); + LOG(" ... gnrc/sttc: {} / {}", t->generic, int(t->staticType)); } } std::string TypeContext::debugInfo() { - return fmt::format("[{}:i{}@{}]", getRealizationBase()->name, - getRealizationBase()->iteration, getSrcInfo()); + return fmt::format("[{}:i{}@{}]", getBase()->name, getBase()->iteration, + getSrcInfo()); } std::shared_ptr, std::vector>> -TypeContext::getFunctionArgs(types::TypePtr t) { +TypeContext::getFunctionArgs(const types::TypePtr &t) { if (!t->getFunc()) return nullptr; auto fn = t->getFunc(); @@ -604,7 +565,7 @@ TypeContext::getFunctionArgs(types::TypePtr t) { return ret; } -std::shared_ptr TypeContext::getStaticString(types::TypePtr t) { +std::shared_ptr TypeContext::getStaticString(const types::TypePtr &t) { if (auto s = t->getStatic()) { auto r = s->evaluate(); if (r.type == StaticValue::STRING) @@ -613,7 +574,7 @@ std::shared_ptr TypeContext::getStaticString(types::TypePtr t) { return nullptr; } -std::shared_ptr TypeContext::getStaticInt(types::TypePtr t) { +std::shared_ptr TypeContext::getStaticInt(const types::TypePtr &t) { if (auto s = t->getStatic()) { auto r = s->evaluate(); if (r.type == StaticValue::INT) @@ -622,7 +583,7 @@ std::shared_ptr TypeContext::getStaticInt(types::TypePtr t) { return nullptr; } -types::FuncTypePtr TypeContext::extractFunction(types::TypePtr t) { +types::FuncTypePtr TypeContext::extractFunction(const types::TypePtr &t) { if (auto f = t->getFunc()) return f; if (auto p = t->getPartial()) diff --git a/codon/parser/visitors/typecheck/ctx.h b/codon/parser/visitors/typecheck/ctx.h index 17b01589..62fbb655 100644 --- a/codon/parser/visitors/typecheck/ctx.h +++ b/codon/parser/visitors/typecheck/ctx.h @@ -23,49 +23,42 @@ class TypecheckVisitor; * Can be either a function, a class (type), or a variable. */ struct TypecheckItem : public SrcObject { - /// Identifier kind - enum Kind { Func, Type, Var } kind; - - /// Base name (e.g., foo.bar.baz) - std::string baseName; /// Unique identifier (canonical name) std::string canonicalName; + /// Base name (e.g., foo.bar.baz) + std::string baseName; /// Full module name std::string moduleName; + /// Type + types::TypePtr type = nullptr; + /// Full base scope information - std::vector scope; - /// Non-empty string if a variable is import variable - std::string importPath; + std::vector scope = {0}; /// List of scopes where the identifier is accessible /// without __used__ check std::vector> accessChecked; /// Set if an identifier cannot be shadowed /// (e.g., global-marked variables) - bool noShadow = false; - /// Set if an identifier is a class or a function generic - bool generic = false; - /// Set if an identifier is a static variable. - char staticType = 0; + bool canShadow = true; /// Set if an identifier should not be dominated /// (e.g., a loop variable in a comprehension). bool avoidDomination = false; - std::list references; - Stmt *root = nullptr; + /// Set if an identifier is a class or a function generic + bool generic = false; + /// Set if an identifier is a static variable. + char staticType = 0; - /// Type - types::TypePtr type = nullptr; - - TypecheckItem(Kind, std::string, std::string, std::string, std::vector = {}, - std::string = "", types::TypePtr = nullptr); + TypecheckItem(std::string, std::string, std::string, types::TypePtr, + std::vector = {0}); /* Convenience getters */ std::string getBaseName() const { return baseName; } std::string getModule() const { return moduleName; } - bool isVar() const { return kind == Var; } - bool isFunc() const { return kind == Func; } - bool isType() const { return kind == Type; } - bool isImport() const { return !importPath.empty(); } + bool isVar() const { return type->getLink() != nullptr && !generic; } + bool isFunc() const { return type->getFunc() != nullptr; } + bool isType() const { return !isFunc() && !isVar(); } + bool isGlobal() const { return scope.size() == 1 && baseName.empty(); } /// True if an identifier is within a conditional block /// (i.e., a block that might not be executed during the runtime) @@ -100,15 +93,25 @@ struct TypeContext : public Context { struct Base { /// Canonical name of a function or a class that owns this base. std::string name; + /// Function type + types::TypePtr type = nullptr; + /// The return type of currently realized function + types::TypePtr returnType = nullptr; + /// Typechecking iteration + int iteration = 0; /// Tracks function attributes (e.g. if it has @atomic or @test attributes). /// Only set for functions. - Attr *attributes; - /// Set if the base is class base and if class is marked with @deduce. - /// Stores the list of class fields in the order of traversal. - std::shared_ptr> deducedMembers = nullptr; - /// Canonical name of `self` parameter that is used to deduce class fields - /// (e.g., self in self.foo). - std::string selfName; + Attr *attributes = nullptr; + + struct { + /// Set if the base is class base and if class is marked with @deduce. + /// Stores the list of class fields in the order of traversal. + std::shared_ptr> deducedMembers = nullptr; + /// Canonical name of `self` parameter that is used to deduce class fields + /// (e.g., self in self.foo). + std::string selfName; + } deduce; + /// Map of captured identifiers (i.e., identifiers not defined in a function). /// Captured (canonical) identifiers are mapped to the new canonical names /// (representing the canonical function argument names that are appended to the @@ -123,8 +126,6 @@ struct TypeContext : public Context { /// Scope that defines the base. std::vector scope; - std::vector preamble; - /// Set of seen global identifiers used to prevent later creation of local variables /// with the same name. std::unordered_map seenGlobalIdentifiers; @@ -142,8 +143,9 @@ struct TypeContext : public Context { }; std::vector loops; + std::unordered_map> replacements; + public: - explicit Base(std::string name, Attr *attributes = nullptr); Loop *getLoop() { return loops.empty() ? nullptr : &(loops.back()); } bool isType() const { return attributes == nullptr; } }; @@ -153,7 +155,8 @@ struct TypeContext : public Context { struct BaseGuard { TypeContext *holder; BaseGuard(TypeContext *holder, const std::string &name) : holder(holder) { - holder->bases.emplace_back(Base(name)); + holder->bases.emplace_back(); + holder->bases.back().name = name; holder->bases.back().scope = holder->scope.blocks; holder->addBlock(); } @@ -163,10 +166,10 @@ struct TypeContext : public Context { } }; - /// Set if the standard library is currently being loaded. - bool isStdlibLoading = false; /// Current module. The default module is named `__main__`. ImportFile moduleName = {ImportFile::PACKAGE, "", ""}; + /// Set if the standard library is currently being loaded. + bool isStdlibLoading = false; /// Tracks if we are in a dependent part of a short-circuiting expression (e.g. b in a /// and b) to disallow assignment expressions there. bool isConditionalExpr = false; @@ -176,21 +179,6 @@ struct TypeContext : public Context { /// Set if all assignments should not be dominated later on. bool avoidDomination = false; - /// A realization base definition. Each function realization defines a new base scope. - /// Used to properly realize enclosed functions and to prevent mess with mutually - /// recursive enclosed functions. - struct RealizationBase { - /// Function name - std::string name; - /// Function type - types::TypePtr type = nullptr; - /// The return type of currently realized function - types::TypePtr returnType = nullptr; - /// Typechecking iteration - int iteration = 0; - }; - std::vector realizationBases; - /// The current type-checking level (for type instantiation and generalization). int typecheckLevel = 0; std::set pendingDefaults; @@ -215,13 +203,11 @@ public: void add(const std::string &name, const Item &var) override; /// Convenience method for adding an object to the context. Item addVar(const std::string &name, const std::string &canonicalName, - const SrcInfo &srcInfo = SrcInfo(), const types::TypePtr &type = nullptr); + const types::TypePtr &type, const SrcInfo &srcInfo = SrcInfo()); Item addType(const std::string &name, const std::string &canonicalName, - const SrcInfo &srcInfo = SrcInfo(), - const types::TypePtr &type = nullptr); + const types::TypePtr &type, const SrcInfo &srcInfo = SrcInfo()); Item addFunc(const std::string &name, const std::string &canonicalName, - const SrcInfo &srcInfo = SrcInfo(), - const types::TypePtr &type = nullptr); + const types::TypePtr &type, const SrcInfo &srcInfo = SrcInfo()); /// Add the item to the standard library module, thus ensuring its visibility from all /// modules. Item addAlwaysVisible(const Item &item); @@ -231,9 +217,6 @@ public: /// Get an item that exists in the context. If the item does not exist, assertion is /// raised. Item forceFind(const std::string &name) const; - /// Get an item from the context. Perform domination analysis for accessing items - /// defined in the conditional blocks (i.e., Python scoping). - Item findDominatingBinding(const std::string &name, TypecheckVisitor *); /// Return a canonical name of the current base. /// An empty string represents the toplevel base. @@ -247,11 +230,12 @@ public: void enterConditionalBlock(); /// Leave a conditional block. Populate stmts (if set) with the declarations of /// newly added identifiers that dominate the children blocks. - void leaveConditionalBlock(std::vector *stmts = nullptr); + void leaveConditionalBlock(std::vector *stmts = nullptr, + TypecheckVisitor *t = nullptr); /// Generate a unique identifier (name) for a given string. std::string generateCanonicalName(const std::string &name, bool includeBase = false, - bool zeroId = false) const; + bool noSuffix = false) const; /// True if we are at the toplevel. bool isGlobal() const; /// True if we are within a conditional block. @@ -277,8 +261,6 @@ public: public: /// Get the current realization depth (i.e., the number of nested realizations). size_t getRealizationDepth() const; - /// Get the current base. - RealizationBase *getRealizationBase(); /// Get the name of the current realization stack (e.g., `fn1:fn2:...`). std::string getRealizationStackName() const; @@ -343,10 +325,10 @@ private: public: std::shared_ptr, std::vector>> - getFunctionArgs(types::TypePtr t); - std::shared_ptr getStaticString(types::TypePtr t); - std::shared_ptr getStaticInt(types::TypePtr t); - types::FuncTypePtr extractFunction(types::TypePtr t); + getFunctionArgs(const types::TypePtr &); + std::shared_ptr getStaticString(const types::TypePtr &); + std::shared_ptr getStaticInt(const types::TypePtr &); + types::FuncTypePtr extractFunction(const types::TypePtr &); }; } // namespace codon::ast diff --git a/codon/parser/visitors/typecheck/error.cpp b/codon/parser/visitors/typecheck/error.cpp index 98e31b7d..cde525a8 100644 --- a/codon/parser/visitors/typecheck/error.cpp +++ b/codon/parser/visitors/typecheck/error.cpp @@ -64,7 +64,7 @@ void TypecheckVisitor::visit(TryStmt *stmt) { ctx->enterConditionalBlock(); if (!c.var.empty()) { c.var = ctx->generateCanonicalName(c.var); - ctx->addVar(ctx->cache->rev(c.var), c.var, c.suite->getSrcInfo()); + ctx->addVar(ctx->cache->rev(c.var), c.var, ctx->getUnbound()); } transform(c.exc); if (c.exc && c.exc->type->is("pyobj")) { @@ -92,8 +92,7 @@ void TypecheckVisitor::visit(TryStmt *stmt) { transformType(c.exc); if (!c.var.empty()) { // Handle dominated except bindings - auto val = ctx->addVar(c.var, c.var, getSrcInfo(), c.exc->getType()); - val->root = stmt; + auto val = ctx->addVar(c.var, c.var, c.exc->getType()); unify(val->type, c.exc->getType()); } ctx->blockLevel++; @@ -110,7 +109,7 @@ void TypecheckVisitor::visit(TryStmt *stmt) { pyCatchStmt->suite->getSuite()->stmts.push_back(N(nullptr)); TryStmt::Catch c{pyVar, transformType(exc), pyCatchStmt}; - auto val = ctx->addVar(pyVar, pyVar, getSrcInfo(), c.exc->getType()); + auto val = ctx->addVar(pyVar, pyVar, c.exc->getType()); unify(val->type, c.exc->getType()); ctx->blockLevel++; transform(c.suite); @@ -142,12 +141,11 @@ void TypecheckVisitor::visit(ThrowStmt *stmt) { transform(stmt->expr); if (!(stmt->expr->getCall() && - stmt->expr->getCall()->expr->isId("__internal__.set_header:0"))) { + stmt->expr->getCall()->expr->isId("__internal__.set_header"))) { stmt->expr = transform(N( N(N("__internal__"), "set_header"), stmt->expr, - N(ctx->getRealizationBase()->name), - N(stmt->getSrcInfo().file), N(stmt->getSrcInfo().line), - N(stmt->getSrcInfo().col))); + N(ctx->getBase()->name), N(stmt->getSrcInfo().file), + N(stmt->getSrcInfo().line), N(stmt->getSrcInfo().col))); } if (stmt->expr->isDone()) stmt->setDone(); diff --git a/codon/parser/visitors/typecheck/function.cpp b/codon/parser/visitors/typecheck/function.cpp index d57017b4..7cc9bde7 100644 --- a/codon/parser/visitors/typecheck/function.cpp +++ b/codon/parser/visitors/typecheck/function.cpp @@ -22,7 +22,7 @@ void TypecheckVisitor::visit(YieldExpr *expr) { E(Error::FN_OUTSIDE_ERROR, expr, "yield"); unify(expr->type, ctx->getUnbound()); - unify(ctx->getRealizationBase()->returnType, + unify(ctx->getBase()->returnType, ctx->instantiateGeneric(ctx->forceFind("Generator")->type, {expr->type})); if (realize(expr->type)) expr->setDone(); @@ -48,19 +48,19 @@ void TypecheckVisitor::visit(ReturnStmt *stmt) { if (transform(stmt->expr)) { // Wrap expression to match the return type - if (!ctx->getRealizationBase()->returnType->getUnbound()) - if (!wrapExpr(stmt->expr, ctx->getRealizationBase()->returnType)) { + if (!ctx->getBase()->returnType->getUnbound()) + if (!wrapExpr(stmt->expr, ctx->getBase()->returnType)) { return; } // Special case: partialize functions if we are returning them if (stmt->expr->getType()->getFunc() && - !(ctx->getRealizationBase()->returnType->getClass() && - ctx->getRealizationBase()->returnType->is("Function"))) { + !(ctx->getBase()->returnType->getClass() && + ctx->getBase()->returnType->is("Function"))) { stmt->expr = partializeFunction(stmt->expr->type->getFunc()); } - unify(ctx->getRealizationBase()->returnType, stmt->expr->type); + unify(ctx->getBase()->returnType, stmt->expr->type); } else { // Just set the expr for the translation stage. However, do not unify the return // type! This might be a `return` in a generator. @@ -82,7 +82,7 @@ void TypecheckVisitor::visit(YieldStmt *stmt) { E(Error::FN_OUTSIDE_ERROR, stmt, "yield"); stmt->expr = transform(stmt->expr ? stmt->expr : N(N("NoneType"))); - unify(ctx->getRealizationBase()->returnType, + unify(ctx->getBase()->returnType, ctx->instantiateGeneric(ctx->forceFind("Generator")->type, {stmt->expr->type})); if (stmt->expr->isDone()) @@ -104,7 +104,7 @@ void TypecheckVisitor::visit(GlobalStmt *stmt) { E(Error::FN_OUTSIDE_ERROR, stmt, stmt->nonLocal ? "nonlocal" : "global"); // Dominate the binding - auto val = ctx->findDominatingBinding(stmt->var, this); + auto val = findDominatingBinding(stmt->var, ctx.get()); if (!val || !val->isVar()) E(Error::ID_NOT_FOUND, stmt, stmt->var); if (val->getBaseName() == ctx->getBaseName()) @@ -121,10 +121,10 @@ void TypecheckVisitor::visit(GlobalStmt *stmt) { // Register as global if needed ctx->cache->addGlobal(val->canonicalName); - val = ctx->addVar(stmt->var, val->canonicalName, stmt->getSrcInfo()); + val = ctx->addVar(stmt->var, val->canonicalName, val->type); val->baseName = ctx->getBaseName(); // Globals/nonlocals cannot be shadowed in children scopes (as in Python) - val->noShadow = true; + val->canShadow = false; // Erase the statement resultStmt = N(); } @@ -139,9 +139,6 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { return; } - // Function should be constructed only once - stmt->setDone(); - // Parse attributes for (auto i = stmt->decorators.size(); i-- > 0;) { auto [isAttr, attrName] = getDecorator(stmt->decorators[i]); @@ -168,24 +165,28 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { if (auto c = ctx->find(stmt->name)) { if (c->isFunc() && c->getModule() == ctx->getModule() && c->getBaseName() == ctx->getBaseName()) - rootName = c->canonicalName; + rootName = ctx->cache->functions[c->canonicalName].rootName; } } if (rootName.empty()) - rootName = ctx->generateCanonicalName(stmt->name, true); + rootName = ctx->generateCanonicalName(stmt->name, true, isClassMember); // Append overload number to the name - auto canonicalName = - format("{}:{}", rootName, ctx->cache->overloads[rootName].size()); + auto canonicalName = rootName; + if (!ctx->cache->overloads[rootName].empty()) + canonicalName += format(":{}", ctx->cache->overloads[rootName].size()); ctx->cache->reverseIdentifierLookup[canonicalName] = stmt->name; - // Ensure that function binding does not shadow anything. - // Function bindings cannot be dominated either - if (!isClassMember) { - auto funcVal = ctx->find(stmt->name); - if (funcVal && funcVal->noShadow) + if (isClassMember) { + // Set the enclosing class name + stmt->attributes.parentClass = ctx->getBase()->name; + // Add the method to the class' method list + ctx->cache->classes[ctx->getBase()->name].methods[stmt->name] = rootName; + } else { + // Ensure that function binding does not shadow anything. + // Function bindings cannot be dominated either + auto funcVal = ctx->find(stmt->name); + if (funcVal && !funcVal->canShadow) E(Error::CLASS_INVALID_BIND, stmt, stmt->name); - funcVal = ctx->addFunc(stmt->name, rootName, stmt->getSrcInfo()); - ctx->addAlwaysVisible(funcVal); } std::vector args; @@ -208,7 +209,7 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { // Mark as method if the first argument is self if (isClassMember && stmt->attributes.has(Attr::HasSelf) && a.name == "self") { - ctx->getBase()->selfName = name; + // ctx->getBase()->selfName = name; stmt->attributes.set(Attr::Method); } @@ -237,46 +238,52 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { // Generic and static types auto generic = ctx->getUnbound(); auto typId = generic->getLink()->id; - generic->genericName = ctx->cache->rev(a.name); - if (a.defaultValue) { - auto defType = transformType(clone(a.defaultValue)); - generic->defaultType = defType->type; - } + generic->genericName = varName; if (auto st = getStaticGeneric(a.type.get())) { - auto val = ctx->addVar(varName, name, stmt->getSrcInfo(), generic); + auto val = ctx->addVar(varName, name, generic); val->generic = true; val->staticType = st; - generic->isStatic = true; + generic->isStatic = st; + if (a.defaultValue) { + auto defType = transform(clone(a.defaultValue)); + generic->defaultType = defType->type; + } } else { - auto val = ctx->addType(varName, name, stmt->getSrcInfo(), generic); + auto val = ctx->addType(varName, name, generic); val->generic = true; + if (a.defaultValue) { + auto defType = transformType(clone(a.defaultValue)); + generic->defaultType = defType->type; + } } - explicits.emplace_back(a.name, ctx->cache->rev(a.name), - generic->generalize(ctx->typecheckLevel), typId); + explicits.emplace_back(name, varName, generic->generalize(ctx->typecheckLevel), + typId); } } // Prepare list of all generic types - std::vector generics; ClassTypePtr parentClass = nullptr; if (isClassMember && stmt->attributes.has(Attr::Method)) { // Get class generics (e.g., T for `class Cls[T]: def foo:`) - auto parentClassAST = ctx->cache->classes[stmt->attributes.parentClass].ast.get(); + // auto parentClassAST = + // ctx->cache->classes[stmt->attributes.parentClass].ast.get(); parentClass = ctx->forceFind(stmt->attributes.parentClass)->type->getClass(); parentClass = parentClass->instantiate(ctx->typecheckLevel - 1, nullptr, nullptr) ->getClass(); - seqassert(parentClass, "parent class not set"); - for (int i = 0, j = 0, k = 0; i < parentClassAST->args.size(); i++) { - if (parentClassAST->args[i].status != Param::Normal) { - generics.push_back(parentClassAST->args[i].status == Param::Generic - ? parentClass->generics[j++].type - : parentClass->hiddenGenerics[k++].type); - ctx->addType(parentClassAST->args[i].name, parentClassAST->args[i].name, - getSrcInfo(), generics.back()); - } - } + // seqassert(parentClass, "parent class not set"); + // for (int i = 0, j = 0, k = 0; i < parentClassAST->args.size(); i++) { + // if (parentClassAST->args[i].status != Param::Normal) { + // generics.push_back(parentClassAST->args[i].status == Param::Generic + // ? parentClass->generics[j++].type + // : parentClass->hiddenGenerics[k++].type); + // ctx->addType(parentClassAST->args[i].name, parentClassAST->args[i].name, + // generics.back()) + // ->generic = true; + // } + // } } // Add function generics + std::vector generics; for (const auto &i : explicits) generics.push_back(ctx->find(i.name)->type); @@ -284,11 +291,6 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { // Base type: `Function[[args,...], ret]` baseType = getFuncTypeBase(stmt->args.size() - explicits.size()); ctx->typecheckLevel++; - if (stmt->ret) { - unify(baseType->generics[1].type, transformType(stmt->ret)->getType()); - } else { - generics.push_back(unify(baseType->generics[1].type, ctx->getUnbound())); - } // Parse arguments to the context. Needs to be done after adding generics // to support cases like `foo(a: T, T: type)` @@ -306,36 +308,42 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { std::string canName = stmt->args[ai].name; trimStars(canName); if (!stmt->args[ai].type) { - if (parentClass && ai == 0 && ctx->cache->rev(stmt->args[ai].name) == "self") { + if (parentClass && ai == 0 && stmt->args[ai].name == "self") { // Special case: self in methods unify(argType->args[aj], parentClass); } else { unify(argType->args[aj], ctx->getUnbound()); + generics.push_back(argType->args[aj]); } - generics.push_back(argType->args[aj++]); } else if (startswith(stmt->args[ai].name, "*")) { // Special case: `*args: type` and `**kwargs: type`. Do not add this type to the // signature (as the real type is `Tuple[type, ...]`); it will be used during // call typechecking unify(argType->args[aj], ctx->getUnbound()); - generics.push_back(argType->args[aj++]); + generics.push_back(argType->args[aj]); } else { unify(argType->args[aj], transformType(stmt->args[ai].type)->getType()); - generics.push_back(argType->args[aj++]); + // generics.push_back(argType->args[aj++]); } - ctx->addVar(ctx->cache->rev(canName), canName, stmt->getSrcInfo(), - argType->args[aj]); + aj++; + // ctx->addVar(ctx->cache->rev(canName), canName, argType->args[aj]); } ctx->typecheckLevel--; // Parse the return type ret = transformType(stmt->ret, false); + if (ret) { + unify(baseType->generics[1].type, ret->getType()); + } else { + generics.push_back(unify(baseType->generics[1].type, ctx->getUnbound())); + } // Generalize generics and remove them from the context for (const auto &g : generics) { for (auto &u : g->getUnbounds()) - if (u->getUnbound()) + if (u->getUnbound()) { u->getUnbound()->kind = LinkType::Generic; + } } // Parse function body @@ -349,45 +357,56 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { ctx->getBase()->captures = &captures; if (stmt->attributes.has("std.internal.attributes.pycapture")) ctx->getBase()->pyCaptures = &pyCaptures; + suite = clone(stmt->suite); // suite = SimplifyVisitor(ctx, // preamble).transformConditionalScope(stmt->suite); } } } - stmt->attributes.module = - format("{}{}", ctx->moduleName.status == ImportFile::STDLIB ? "std::" : "::", - ctx->moduleName.module); + stmt->attributes.module = ctx->moduleName.path; + // format( + // "{}{}", ctx->moduleName.status == ImportFile::STDLIB ? "std::" : + // "::", ctx->moduleName.module); ctx->cache->overloads[rootName].push_back(canonicalName); + // Make function AST and cache it for later realization + auto f = N(canonicalName, ret, args, suite, stmt->attributes); + ctx->cache->functions[canonicalName].module = ctx->getModule(); + ctx->cache->functions[canonicalName].ast = f; + ctx->cache->functions[canonicalName].origAst = + std::static_pointer_cast(stmt->clone()); + ctx->cache->functions[canonicalName].isToplevel = + ctx->getModule().empty() && ctx->isGlobal(); + ctx->cache->functions[canonicalName].rootName = rootName; + f->setDone(); + // Construct the type auto funcTyp = std::make_shared( - baseType, ctx->cache->functions[stmt->name].ast.get(), explicits); - + baseType, ctx->cache->functions[canonicalName].ast.get(), explicits); funcTyp->setSrcInfo(getSrcInfo()); if (isClassMember && stmt->attributes.has(Attr::Method)) { funcTyp->funcParent = ctx->find(stmt->attributes.parentClass)->type; } funcTyp = std::static_pointer_cast( funcTyp->generalize(ctx->typecheckLevel)); + ctx->cache->functions[canonicalName].type = funcTyp; + ctx->addFunc(stmt->name, canonicalName, funcTyp); + if (isClassMember) + ctx->remove(stmt->name); // Special method handling if (isClassMember) { - // Set the enclosing class name - stmt->attributes.parentClass = ctx->getBase()->name; - // Add the method to the class' method list - ctx->cache->classes[ctx->getBase()->name].methods[stmt->name] = rootName; - auto m = ctx->cache->getMethod(ctx->find(stmt->attributes.parentClass)->type->getClass(), - ctx->cache->rev(stmt->name)); + ctx->cache->rev(canonicalName)); bool found = false; for (auto &i : ctx->cache->overloads[m]) - if (i == stmt->name) { + if (i == canonicalName) { ctx->cache->functions[i].type = funcTyp; found = true; break; } - seqassert(found, "cannot find matching class method for {}", stmt->name); + seqassert(found, "cannot find matching class method for {}", canonicalName); } else { // Hack so that we can later use same helpers for class overloads ctx->cache->classes[".toplevel"].methods[stmt->name] = rootName; @@ -410,22 +429,6 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { // args.push_back(kw); // partialArgs.emplace_back("", N(EllipsisExpr::PARTIAL)); // } - // Make function AST and cache it for later realization - auto f = N(canonicalName, ret, args, suite, stmt->attributes); - ctx->cache->functions[canonicalName].ast = f; - ctx->cache->functions[canonicalName].origAst = - std::static_pointer_cast(stmt->clone()); - ctx->cache->functions[canonicalName].isToplevel = - ctx->getModule().empty() && ctx->isGlobal(); - ctx->cache->functions[canonicalName].rootName = rootName; - - // Update the visited table - // Functions should always be visible, so add them to the toplevel - auto val = std::make_shared(TypecheckItem::Func, ctx->getBaseName(), - stmt->name, ctx->getModule()); - val->type = funcTyp; - ctx->addToplevel(stmt->name, val); - ctx->cache->functions[stmt->name].type = funcTyp; // Ensure that functions with @C, @force_realize, and @export attributes can be // realized @@ -436,13 +439,13 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { } // Debug information - LOG_REALIZE("[stmt] added func {}: {}", stmt->name, funcTyp); + LOG("[stmt] added func {}: {}", canonicalName, funcTyp->debugString(2)); // Expression to be used if function binding is modified by captures or decorators ExprPtr finalExpr = nullptr; // If there are captures, replace `fn` with `fn(cap1=cap1, cap2=cap2, ...)` // if (!captures.empty()) { - // finalExpr = N(N(stmt->name), partialArgs); + // finalExpr = N(N(canonicalName), partialArgs); // // Add updated self reference in case function is recursive! // auto pa = partialArgs; // for (auto &a : pa) { @@ -463,13 +466,13 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) { E(Error::FN_NO_DECORATORS, stmt->decorators[i]); // Replace each decorator with `decorator(finalExpr)` in the reverse order finalExpr = N(stmt->decorators[i], - finalExpr ? finalExpr : N(stmt->name)); + finalExpr ? finalExpr : N(canonicalName)); } } if (finalExpr) { resultStmt = - N(f, transform(N(N(stmt->name), finalExpr))); + N(f, transform(N(N(canonicalName), finalExpr))); } else { resultStmt = f; } @@ -603,11 +606,8 @@ std::pair TypecheckVisitor::getDecorator(const ExprPtr &e) { if (id && id->getId()) { auto ci = ctx->find(id->getId()->value); if (ci && ci->isFunc()) { - if (ctx->cache->overloads[ci->canonicalName].size() == 1) { - return {ctx->cache->functions[ctx->cache->overloads[ci->canonicalName][0]] - .ast->attributes.isAttribute, - ci->canonicalName}; - } + return {ctx->cache->functions[ci->canonicalName].ast->attributes.isAttribute, + ci->canonicalName}; } } return {false, ""}; diff --git a/codon/parser/visitors/typecheck/import.cpp b/codon/parser/visitors/typecheck/import.cpp index 8e200af9..ce462453 100644 --- a/codon/parser/visitors/typecheck/import.cpp +++ b/codon/parser/visitors/typecheck/import.cpp @@ -71,15 +71,12 @@ void TypecheckVisitor::visit(ImportStmt *stmt) { if (!stmt->what) { // Case: import foo auto name = stmt->as.empty() ? path : stmt->as; - auto var = importVar + "_var"; - // Construct `import_var = Import([module], [path])` (for printing imports etc.) + // Construct `import_var = Import([path], [module])` (for printing imports etc.) resultStmt = N( - resultStmt, transform(N(N(var), - N(N("Import"), - N(file->module), - N(file->path)), - N("Import")))); - ctx->addVar(name, var, stmt->getSrcInfo())->importPath = file->path; + resultStmt, + transform(N( + N(name), N(N("Import"), N(file->path), + N(file->module))))); } else if (stmt->what->isId("*")) { // Case: from foo import * seqassert(stmt->as.empty(), "renamed star-import"); @@ -91,7 +88,7 @@ void TypecheckVisitor::visit(ImportStmt *stmt) { // `__` while the standard library is being loaded auto c = i.second.front(); if (c->isConditional() && i.first.find('.') == std::string::npos) { - c = import.ctx->findDominatingBinding(i.first, this); + c = findDominatingBinding(i.first, import.ctx.get()); } // Imports should ignore noShadow property ctx->Context::add(i.first, c); @@ -106,14 +103,11 @@ void TypecheckVisitor::visit(ImportStmt *stmt) { if (!c) E(Error::IMPORT_NO_NAME, i, i->value, file->module); if (c->isConditional()) - c = import.ctx->findDominatingBinding(i->value, this); + c = findDominatingBinding(i->value, import.ctx.get()); // Imports should ignore noShadow property ctx->Context::add(stmt->as.empty() ? i->value : stmt->as, c); } - - if (!resultStmt) { - resultStmt = N(); // erase it - } + resultStmt = transform(!resultStmt ? N() : resultStmt); // erase it } /// Transform special `from C` and `from python` imports. @@ -204,9 +198,10 @@ StmtPtr TypecheckVisitor::transformCImport(const std::string &name, StmtPtr TypecheckVisitor::transformCVarImport(const std::string &name, const Expr *type, const std::string &altName) { auto canonical = ctx->generateCanonicalName(name); - auto val = ctx->addVar(altName.empty() ? name : altName, canonical); - val->noShadow = true; - auto s = N(N(canonical), nullptr, transformType(type->clone())); + auto typ = transformType(type->clone()); + auto val = ctx->addVar(altName.empty() ? name : altName, canonical, typ->type); + val->canShadow = false; + auto s = N(N(canonical), nullptr, typ); s->lhs->setAttr(ExprAttr::ExternVar); return s; } @@ -313,18 +308,17 @@ StmtPtr TypecheckVisitor::transformNewImport(const ImportFile &file) { auto ictx = std::make_shared(ctx->cache, file.path); ictx->isStdlibLoading = ctx->isStdlibLoading; ictx->moduleName = file; - auto import = ctx->cache->imports.insert({file.path, {file.path, ictx}}).first; - import->second.moduleName = file.module; + auto import = + ctx->cache->imports.insert({file.path, {file.module, file.path, ictx}}).first; // __name__ = [import name] - StmtPtr n = - N(N("__name__"), N(ictx->moduleName.module)); - if (ictx->moduleName.module == "internal.core") { + StmtPtr n = N(N("__name__"), N(file.module)); + if (file.module == "internal.core") { // str is not defined when loading internal.core; __name__ is not needed anyway n = nullptr; } n = N(n, parseFile(ctx->cache, file.path)); - n = TypecheckVisitor(ictx).transform(n); + n = TypecheckVisitor(ictx, preamble).transform(n); if (!ctx->cache->errors.empty()) throw exc::ParserException(); // Add comment to the top of import for easier dump inspection @@ -341,8 +335,8 @@ StmtPtr TypecheckVisitor::transformNewImport(const ImportFile &file) { std::string importDoneVar; // `import_[I]_done = False` (set to True upon successful import) - ctx->cache->imports[MAIN_IMPORT].ctx->bases[0].preamble.push_back(N( - N(importDoneVar = importVar + "_done"), N(false))); + preamble->push_back(N(N(importDoneVar = importVar + "_done"), + N(false))); ctx->cache->addGlobal(importDoneVar); // Wrap all imported top-level statements into a function. @@ -371,12 +365,11 @@ StmtPtr TypecheckVisitor::transformNewImport(const ImportFile &file) { } // Create import function manually with ForceRealize - ctx->cache->functions[importVar + ":0"].ast = - N(importVar + ":0", nullptr, std::vector{}, - N(stmts), Attr({Attr::ForceRealize})); - ctx->cache->imports[MAIN_IMPORT].ctx->bases[0].preamble.push_back( - ctx->cache->functions[importVar + ":0"].ast->clone()); - ctx->cache->overloads[importVar].push_back(importVar + ":0"); + ctx->cache->functions[importVar].ast = + N(importVar, nullptr, std::vector{}, N(stmts), + Attr({Attr::ForceRealize})); + preamble->push_back(ctx->cache->functions[importVar].ast->clone()); + ctx->cache->overloads[importVar].push_back(importVar); } return nullptr; } diff --git a/codon/parser/visitors/typecheck/infer.cpp b/codon/parser/visitors/typecheck/infer.cpp index b3ea9553..d6015617 100644 --- a/codon/parser/visitors/typecheck/infer.cpp +++ b/codon/parser/visitors/typecheck/infer.cpp @@ -48,13 +48,11 @@ StmtPtr TypecheckVisitor::inferTypes(StmtPtr result, bool isToplevel) { if (!result) return nullptr; - for (ctx->getRealizationBase()->iteration = 1;; - ctx->getRealizationBase()->iteration++) { - LOG_TYPECHECK("[iter] {} :: {}", ctx->getRealizationBase()->name, - ctx->getRealizationBase()->iteration); - if (ctx->getRealizationBase()->iteration >= MAX_TYPECHECK_ITER) + 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->getRealizationBase()->name)); + ctx->cache->rev(ctx->getBase()->name)); // Keep iterating until: // (1) success: the statement is marked as done; or @@ -65,12 +63,12 @@ StmtPtr TypecheckVisitor::inferTypes(StmtPtr result, bool isToplevel) { ctx->changedNodes = 0; auto returnEarly = ctx->returnEarly; ctx->returnEarly = false; - TypecheckVisitor(ctx).transform(result); + TypecheckVisitor(ctx, preamble).transform(result); std::swap(ctx->changedNodes, changedNodes); std::swap(ctx->returnEarly, returnEarly); ctx->typecheckLevel--; - if (ctx->getRealizationBase()->iteration == 1 && isToplevel) { + if (ctx->getBase()->iteration == 1 && isToplevel) { // Realize all @force_realize functions for (auto &f : ctx->cache->functions) { auto &attr = f.second.ast->attributes; @@ -94,8 +92,8 @@ StmtPtr TypecheckVisitor::inferTypes(StmtPtr result, bool isToplevel) { // their default values and then run another round to see if anything changed. bool anotherRound = false; // Special case: return type might have default as well (e.g., Union) - if (ctx->getRealizationBase()->returnType) - ctx->pendingDefaults.insert(ctx->getRealizationBase()->returnType); + if (ctx->getBase()->returnType) + ctx->pendingDefaults.insert(ctx->getBase()->returnType); for (auto &unbound : ctx->pendingDefaults) { if (auto tu = unbound->getUnion()) { // Seal all dynamic unions after the iteration is over @@ -225,10 +223,9 @@ types::TypePtr TypecheckVisitor::realizeType(types::ClassType *type) { LOG_REALIZE("[realize] ty {} -> {}", realized->name, realized->realizedTypeName()); // Realizations should always be visible, so add them to the toplevel - auto val = std::make_shared( - TypecheckItem::Type, "", realized->realizedTypeName(), ctx->getModule()); - val->type = realized; - ctx->addToplevel(realized->realizedTypeName(), val); + auto val = std::make_shared(realized->realizedTypeName(), "", + ctx->getModule(), realized); + ctx->addAlwaysVisible(val); auto realization = ctx->cache->classes[realized->name].realizations[realized->realizedTypeName()] = std::make_shared(); @@ -250,10 +247,12 @@ types::TypePtr TypecheckVisitor::realizeType(types::ClassType *type) { std::map memberInfo; // needed for IR for (auto &field : ctx->cache->classes[realized->name].fields) { auto ftyp = ctx->instantiate(field.type, realized); - if (!realize(ftyp)) - E(Error::TYPE_CANNOT_REALIZE_ATTR, getSrcInfo(), field.name, - ftyp->prettyString()); - LOG_REALIZE("- member: {} -> {}: {}", field.name, field.type, ftyp); + if (!realize(ftyp)) { + realize(ftyp); + E(Error::TYPE_CANNOT_REALIZE_ATTR, getSrcInfo(), ctx->cache->rev(field.name), + realized->prettyString()); + } + // LOG_REALIZE("- member: {} -> {}: {}", field.name, field.type, ftyp); realization->fields.emplace_back(field.name, ftyp); names.emplace_back(field.name); typeArgs.emplace_back(makeIRType(ftyp->getClass().get())); @@ -272,10 +271,9 @@ types::TypePtr TypecheckVisitor::realizeType(types::ClassType *type) { // Fix for partial types if (auto p = type->getPartial()) { auto pt = std::make_shared(realized->getRecord(), p->func, p->known); - auto val = std::make_shared(TypecheckItem::Type, "", - pt->realizedName(), ctx->getModule()); - val->type = pt; - ctx->addToplevel(pt->realizedName(), val); + auto val = + std::make_shared(pt->realizedName(), "", ctx->getModule(), pt); + ctx->addAlwaysVisible(val); ctx->cache->classes[pt->name].realizations[pt->realizedName()] = ctx->cache->classes[realized->name].realizations[realized->realizedTypeName()]; } @@ -291,24 +289,31 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) } } + seqassert(in(ctx->cache->imports, type->ast->attributes.module) != nullptr, + "bad module: '{}'", type->ast->attributes.module); + auto &imp = ctx->cache->imports[type->ast->attributes.module]; + auto oldCtx = this->ctx; + this->ctx = imp.ctx; + // LOG("=> {}", ctx->moduleName.module, ctx->moduleName.path); + if (ctx->getRealizationDepth() > MAX_REALIZATION_DEPTH) { E(Error::MAX_REALIZATION, getSrcInfo(), ctx->cache->rev(type->ast->name)); } - LOG_REALIZE("[realize] fn {} -> {} : base {} ; depth = {}", type->ast->name, - type->realizedName(), ctx->getRealizationStackName(), - ctx->getRealizationDepth()); getLogger().level++; ctx->addBlock(); ctx->typecheckLevel++; // Find function parents - ctx->realizationBases.push_back( - {type->ast->name, type->getFunc(), type->getRetType()}); + ctx->bases.push_back({type->ast->name, type->getFunc(), type->getRetType()}); + LOG("[realize] fn {} -> {} : base {} ; depth = {} ; ctx-base: {}", type->ast->name, + type->realizedName(), ctx->getRealizationStackName(), ctx->getRealizationDepth(), + ctx->getBaseName()); // Clone the generic AST that is to be realized auto ast = generateSpecialAst(type); addFunctionGenerics(type); + ctx->getBase()->attributes = &(ast->attributes); // Internal functions have no AST that can be realized bool hasAst = ast->suite && !ast->attributes.has(Attr::Internal); @@ -317,8 +322,8 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) if (ast->args[i].status == Param::Normal) { std::string varName = ast->args[i].name; trimStars(varName); - ctx->addVar(varName, varName, getSrcInfo(), - std::make_shared(type->getArgTypes()[j++])); + auto v = ctx->addVar(ctx->cache->rev(varName), varName, + std::make_shared(type->getArgTypes()[j++])); } // Populate realization table in advance to support recursive realizations @@ -332,9 +337,8 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) // Realizations should always be visible, so add them to the toplevel auto val = - std::make_shared(TypecheckItem::Func, "", key, ctx->getModule()); - val->type = type->getFunc(); - ctx->addToplevel(key, val); + std::make_shared(key, "", ctx->getModule(), type->getFunc()); + ctx->addAlwaysVisible(val); if (hasAst) { auto oldBlockLevel = ctx->blockLevel; @@ -348,13 +352,15 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) // Lambda typecheck failures are "ignored" as they are treated as statements, // not functions. // TODO: generalize this further. - // LOG("{}", ast->suite->toString(2)); + LOG("[error=>] {}", ast->suite->toString(2)); + // inferTypes(ast->suite, ctx); error("cannot typecheck the program"); } - ctx->realizationBases.pop_back(); + ctx->bases.pop_back(); ctx->popBlock(); ctx->typecheckLevel--; getLogger().level--; + this->ctx = oldCtx; return nullptr; // inference must be delayed } @@ -362,6 +368,7 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) // function has no return statement if (!ast->ret && type->getRetType()->getUnbound()) unify(type->getRetType(), ctx->forceFind("NoneType")->type); + // LOG("-> {} {}", key, ret->toString(2)); } // Realize the return type auto ret = realize(type->getRetType()); @@ -387,14 +394,14 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) } if (force) realizations[type->realizedName()]->ast = r->ast; - val = std::make_shared(TypecheckItem::Func, "", type->realizedName(), - ctx->getModule()); - val->type = type->getFunc(); - ctx->addToplevel(type->realizedName(), val); - ctx->realizationBases.pop_back(); + val = std::make_shared(type->realizedName(), "", ctx->getModule(), + type->getFunc()); + ctx->addAlwaysVisible(val); + ctx->bases.pop_back(); ctx->popBlock(); ctx->typecheckLevel--; getLogger().level--; + this->ctx = oldCtx; return type->getFunc(); } @@ -403,7 +410,9 @@ types::TypePtr TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) /// Intended to be called once the typechecking is done. /// TODO: add JIT compatibility. StmtPtr TypecheckVisitor::prepareVTables() { - auto rep = "__internal__.class_populate_vtables:0"; // see internal.codon + auto rep = "__internal__.class_populate_vtables"; // see internal.codon + if (!in(ctx->cache->functions, rep)) + return nullptr; auto &initFn = ctx->cache->functions[rep]; auto suite = N(); for (auto &[_, cls] : ctx->cache->classes) { @@ -417,7 +426,7 @@ StmtPtr TypecheckVisitor::prepareVTables() { continue; // __internal__.class_set_rtti_vtable(real.ID, size, real.type) suite->stmts.push_back(N( - N(N("__internal__.class_set_rtti_vtable:0"), + N(N("__internal__.class_set_rtti_vtable"), N(real->id), N(vtSz + 2), NT(r)))); // LOG("[poly] {} -> {}", r, real->id); vtSz = 0; @@ -431,7 +440,7 @@ StmtPtr TypecheckVisitor::prepareVTables() { // p[real.ID].__setitem__(f.ID, Function[](f).__raw__()) LOG_REALIZE("[poly] vtable[{}][{}] = {}", real->id, vtSz + id, fn); suite->stmts.push_back(N(N( - N("__internal__.class_set_rtti_vtable_fn:0"), + N("__internal__.class_set_rtti_vtable_fn"), N(real->id), N(vtSz + id), N(N( N( @@ -457,7 +466,7 @@ StmtPtr TypecheckVisitor::prepareVTables() { typ->ast = initFn.ast.get(); realizeFunc(typ.get(), true); - auto &initDist = ctx->cache->functions["__internal__.class_base_derived_dist:0"]; + auto &initDist = ctx->cache->functions["__internal__.class_base_derived_dist"]; // def class_base_derived_dist(B, D): // return Tuple[].__elemsize__ auto oldAst = initDist.ast; @@ -583,7 +592,7 @@ size_t TypecheckVisitor::getRealizationID(types::ClassType *cp, types::FuncType nullptr); std::vector callArgs; callArgs.emplace_back( - N(N("__internal__.class_base_to_derived:0"), + N(N("__internal__.class_base_to_derived"), N(fp->ast->args[0].name), N(cp->realizedName()), N(real->type->realizedName()))); for (size_t i = 1; i < args.size(); i++) @@ -782,11 +791,11 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { auto ast = std::dynamic_pointer_cast( clone(ctx->cache->functions[type->ast->name].ast)); - if (ast->hasAttr("autogenerated") && endswith(ast->name, ".__iter__:0") && + if (ast->hasAttr("autogenerated") && endswith(ast->name, ".__iter__") && type->getArgTypes()[0]->getHeterogenousTuple()) { // Special case: do not realize auto-generated heterogenous __iter__ E(Error::EXPECTED_TYPE, getSrcInfo(), "iterable"); - } else if (ast->hasAttr("autogenerated") && endswith(ast->name, ".__getitem__:0") && + } else if (ast->hasAttr("autogenerated") && endswith(ast->name, ".__getitem__") && type->getArgTypes()[0]->getHeterogenousTuple()) { // Special case: do not realize auto-generated heterogenous __getitem__ E(Error::EXPECTED_TYPE, getSrcInfo(), "iterable"); @@ -814,15 +823,15 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { ll.push_back(format("ret {{}} %{}", as.size())); items[0] = N(N(combine2(ll, "\n"))); ast->suite = N(items); - } else if (startswith(ast->name, "Union.__new__:0")) { + } else if (startswith(ast->name, "Union.__new__")) { auto unionType = type->funcParent->getUnion(); seqassert(unionType, "expected union, got {}", type->funcParent); StmtPtr suite = N(N( - N("__internal__.new_union:0"), N(type->ast->args[0].name), + N("__internal__.new_union"), N(type->ast->args[0].name), N(unionType->realizedTypeName()))); ast->suite = suite; - } else if (startswith(ast->name, "__internal__.new_union:0")) { + } else if (startswith(ast->name, "__internal__.new_union")) { // Special case: __internal__.new_union // def __internal__.new_union(value, U[T0, ..., TN]): // if isinstance(value, T0): @@ -842,7 +851,7 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { suite->stmts.push_back(N( N(N("isinstance"), N(objVar), NT(t->realizedName())), - N(N(N("__internal__.union_make:0"), + N(N(N("__internal__.union_make"), N(tag), N(objVar), N(unionType->realizedTypeName()))))); // Check for Union[T] @@ -852,8 +861,8 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { NT(NT("Union"), std::vector{NT(t->realizedName())})), N( - N(N("__internal__.union_make:0"), N(tag), - N(N("__internal__.get_union:0"), + N(N("__internal__.union_make"), N(tag), + N(N("__internal__.get_union"), N(objVar), NT(t->realizedName())), N(unionType->realizedTypeName()))))); tag++; @@ -861,7 +870,7 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { suite->stmts.push_back(N(N( N("compile_error"), N("invalid union constructor")))); ast->suite = suite; - } else if (startswith(ast->name, "__internal__.get_union:0")) { + } else if (startswith(ast->name, "__internal__.get_union")) { // Special case: __internal__.get_union // def __internal__.new_union(union: Union[T0,...,TN], T): // if __internal__.union_get_tag(union) == 0: @@ -878,10 +887,10 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { for (const auto &t : unionTypes) { if (t->realizedName() == targetType->realizedName()) { suite->stmts.push_back(N( - N(N(N("__internal__.union_get_tag:0"), + N(N(N("__internal__.union_get_tag"), N(selfVar)), "==", N(tag)), - N(N(N("__internal__.union_get_data:0"), + N(N(N("__internal__.union_get_data"), N(selfVar), NT(t->realizedName()))))); } @@ -891,7 +900,7 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { N(N(N("std.internal.types.error.TypeError"), N("invalid union getter")))); ast->suite = suite; - } else if (startswith(ast->name, "__internal__._get_union_method:0")) { + } else if (startswith(ast->name, "__internal__._get_union_method")) { // def __internal__._get_union_method(union: Union[T0,...,TN], method, *args, **kw): // if __internal__.union_get_tag(union) == 0: // return __internal__.union_get_data(union, T0).method(*args, **kw) @@ -907,7 +916,7 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { int tag = 0; for (auto &t : unionTypes) { auto callee = - N(N(N("__internal__.union_get_data:0"), + N(N(N("__internal__.union_get_data"), N(selfVar), NT(t->realizedName())), fnName); auto args = N(N(ast->args[2].name.substr(1))); @@ -919,7 +928,7 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { suite->stmts.push_back(N( N( check, "&&", - N(N(N("__internal__.union_get_tag:0"), + N(N(N("__internal__.union_get_tag"), N(selfVar)), "==", N(tag))), N(N(N(callee, args, kwargs))))); @@ -931,7 +940,7 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { // suite->stmts.push_back(N(N())); unify(type->getRetType(), ctx->instantiate(ctx->forceFind("Union")->type)); ast->suite = suite; - } else if (startswith(ast->name, "__internal__.get_union_first:0")) { + } else if (startswith(ast->name, "__internal__.get_union_first")) { // def __internal__.get_union_first(union: Union[T0]): // return __internal__.union_get_data(union, T0) auto unionType = type->getArgTypes()[0]->getUnion(); @@ -939,7 +948,7 @@ TypecheckVisitor::generateSpecialAst(types::FuncType *type) { auto selfVar = ast->args[0].name; auto suite = N(N( - N(N("__internal__.union_get_data:0"), N(selfVar), + N(N("__internal__.union_get_data"), N(selfVar), NT(unionTypes[0]->realizedName())))); ast->suite = suite; } diff --git a/codon/parser/visitors/typecheck/loops.cpp b/codon/parser/visitors/typecheck/loops.cpp index 9a69535d..a921e38f 100644 --- a/codon/parser/visitors/typecheck/loops.cpp +++ b/codon/parser/visitors/typecheck/loops.cpp @@ -87,7 +87,7 @@ void TypecheckVisitor::visit(WhileStmt *stmt) { ctx->leaveConditionalBlock(); // Dominate loop variables for (auto &var : ctx->getBase()->getLoop()->seenVars) { - ctx->findDominatingBinding(var, this); + findDominatingBinding(var, ctx.get()); } ctx->getBase()->loops.pop_back(); @@ -140,15 +140,16 @@ void TypecheckVisitor::visit(ForStmt *stmt) { ctx->enterConditionalBlock(); ctx->getBase()->loops.push_back({breakVar, ctx->scope.blocks, {}}); std::string varName; + TypeContext::Item val = nullptr; if (auto i = stmt->var->getId()) { - auto val = ctx->addVar(i->value, varName = ctx->generateCanonicalName(i->value), - stmt->var->getSrcInfo()); + val = ctx->addVar(i->value, varName = ctx->generateCanonicalName(i->value), + ctx->getUnbound()); val->avoidDomination = ctx->avoidDomination; transform(stmt->var); stmt->suite = N(stmt->suite); } else { varName = ctx->cache->getTemporaryVar("for"); - auto val = ctx->addVar(varName, varName, stmt->var->getSrcInfo()); + val = ctx->addVar(varName, varName, ctx->getUnbound()); auto var = N(varName); std::vector stmts; // Add for_var = [for variables] @@ -162,9 +163,6 @@ void TypecheckVisitor::visit(ForStmt *stmt) { seqassert(var, "corrupt for variable: {}", stmt->var); // Unify iterator variable and the iterator type - auto val = ctx->addVar(var->value, var->value, getSrcInfo(), - ctx->getUnbound(stmt->var->getSrcInfo())); - val->root = stmt; if (iterType && iterType->name != "Generator") E(Error::EXPECTED_GENERATOR, stmt->iter); unify(stmt->var->type, @@ -181,13 +179,12 @@ void TypecheckVisitor::visit(ForStmt *stmt) { resultStmt = N(assign, N(*stmt), N(transform(N(breakVar)), transformConditionalScope(stmt->elseSuite))); - val->root = resultStmt->getSuite()->stmts[1].get(); } ctx->leaveConditionalBlock(&(stmt->suite->getSuite()->stmts)); // Dominate loop variables for (auto &var : ctx->getBase()->getLoop()->seenVars) - ctx->findDominatingBinding(var, this); + findDominatingBinding(var, ctx.get()); ctx->getBase()->loops.pop_back(); if (stmt->iter->isDone() && stmt->suite->isDone()) @@ -341,7 +338,7 @@ TypecheckVisitor::transformStaticLoopCall( auto stmt = N(N(vars[0]), nullptr, nullptr); std::vector> block; - if (startswith(fn->value, "statictuple:0")) { + if (startswith(fn->value, "statictuple")) { auto &args = iter->getCall()->args[0].value->getCall()->args; if (vars.size() != 1) error("expected one item"); @@ -356,7 +353,7 @@ TypecheckVisitor::transformStaticLoopCall( } block.push_back(wrap(stmt->clone())); } - } else if (fn && startswith(fn->value, "std.internal.types.range.staticrange:0")) { + } else if (fn && startswith(fn->value, "std.internal.types.range.staticrange")) { if (vars.size() != 1) error("expected one item"); auto st = @@ -367,7 +364,7 @@ TypecheckVisitor::transformStaticLoopCall( fn->type->getFunc()->funcGenerics[2].type->getStatic()->evaluate().getInt(); if (abs(st - ed) / abs(step) > MAX_STATIC_ITER) E(Error::STATIC_RANGE_BOUNDS, fn, MAX_STATIC_ITER, abs(st - ed) / abs(step)); - for (int i = st; step > 0 ? i < ed : i > ed; i += step) { + for (int64_t i = st; step > 0 ? i < ed : i > ed; i += step) { stmt->rhs = N(i); stmt->type = NT(N("Static"), N("int")); block.push_back(wrap(stmt->clone())); @@ -379,7 +376,7 @@ TypecheckVisitor::transformStaticLoopCall( fn->type->getFunc()->funcGenerics[0].type->getStatic()->evaluate().getInt(); if (ed > MAX_STATIC_ITER) E(Error::STATIC_RANGE_BOUNDS, fn, MAX_STATIC_ITER, ed); - for (int i = 0; i < ed; i++) { + for (int64_t i = 0; i < ed; i++) { stmt->rhs = N(i); stmt->type = NT(N("Static"), N("int")); block.push_back(wrap(stmt->clone())); @@ -402,8 +399,8 @@ TypecheckVisitor::transformStaticLoopCall( if (typ->getHeterogenousTuple()) { auto &ast = ctx->cache->functions[method].ast; if (ast->hasAttr("autogenerated") && - (endswith(ast->name, ".__iter__:0") || - endswith(ast->name, ".__getitem__:0"))) { + (endswith(ast->name, ".__iter__") || + endswith(ast->name, ".__getitem__"))) { // ignore __getitem__ and other heterogenuous methods continue; } @@ -436,7 +433,7 @@ TypecheckVisitor::transformStaticLoopCall( } else { error("bad call to staticenumerate"); } - } else if (fn && startswith(fn->value, "std.internal.internal.vars:0")) { + } else if (fn && startswith(fn->value, "std.internal.internal.vars")) { if (auto fna = ctx->getFunctionArgs(fn->type)) { auto [generics, args] = *fna; @@ -467,7 +464,7 @@ TypecheckVisitor::transformStaticLoopCall( } else { error("bad call to vars"); } - } else if (fn && startswith(fn->value, "std.internal.static.vars_types:0")) { + } else if (fn && startswith(fn->value, "std.internal.static.vars_types")) { if (auto fna = ctx->getFunctionArgs(fn->type)) { auto [generics, args] = *fna; diff --git a/codon/parser/visitors/typecheck/op.cpp b/codon/parser/visitors/typecheck/op.cpp index bcfc5481..7ad513f4 100644 --- a/codon/parser/visitors/typecheck/op.cpp +++ b/codon/parser/visitors/typecheck/op.cpp @@ -369,6 +369,7 @@ void TypecheckVisitor::visit(InstantiateExpr *expr) { TypePtr t = nullptr; if (expr->typeParams[i]->isStatic()) { t = Type::makeStatic(ctx->cache, expr->typeParams[i]); + t = ctx->instantiate(t); } else { if (expr->typeParams[i]->getNone()) // `None` -> `NoneType` transformType(expr->typeParams[i]); @@ -458,13 +459,14 @@ ExprPtr TypecheckVisitor::evaluateStaticUnary(UnaryExpr *expr) { } /// Division and modulus implementations. -std::pair divMod(const std::shared_ptr &ctx, int a, int b) { +std::pair divMod(const std::shared_ptr &ctx, int64_t a, + int64_t b) { if (!b) E(Error::STATIC_DIV_ZERO, ctx->getSrcInfo()); if (ctx->cache->pythonCompat) { // Use Python implementation. - int d = a / b; - int m = a - d * b; + int64_t d = a / b; + int64_t m = a - d * b; if (m && ((b ^ m) < 0)) { m += b; d -= 1; @@ -800,7 +802,7 @@ TypecheckVisitor::transformStaticTupleIndex(const ClassTypePtr &tuple, auto classItem = in(ctx->cache->classes, tuple->name); seqassert(classItem, "cannot find class '{}'", tuple->name); - auto sz = classItem->fields.size(); + auto sz = int64_t(classItem->fields.size()); int64_t start = 0, stop = sz, step = 1; if (getInt(&start, index)) { // Case: `tuple[int]` diff --git a/codon/parser/visitors/typecheck/typecheck.cpp b/codon/parser/visitors/typecheck/typecheck.cpp index eb01ad98..aceb65a5 100644 --- a/codon/parser/visitors/typecheck/typecheck.cpp +++ b/codon/parser/visitors/typecheck/typecheck.cpp @@ -30,95 +30,43 @@ StmtPtr TypecheckVisitor::apply( Cache *cache, const StmtPtr &node, const std::string &file, const std::unordered_map &defines, const std::unordered_map &earlyDefines, bool barebones) { - auto preamble = std::vector(); + auto preamble = std::make_shared>(); seqassertn(cache->module, "cache's module is not set"); -#define N std::make_shared // Load standard library if it has not been loaded - if (!in(cache->imports, STDLIB_IMPORT)) { - // Load the internal.__init__ - auto stdlib = std::make_shared(cache, STDLIB_IMPORT); - auto stdlibPath = - getImportFile(cache->argv0, STDLIB_INTERNAL_MODULE, "", true, cache->module0); - const std::string initFile = "__init__.codon"; - if (!stdlibPath || !endswith(stdlibPath->path, initFile)) - E(Error::COMPILER_NO_STDLIB); - - /// Use __init_test__ for faster testing (e.g., #%% name,barebones) - /// TODO: get rid of it one day... - if (barebones) { - stdlibPath->path = - stdlibPath->path.substr(0, stdlibPath->path.size() - initFile.size()) + - "__init_test__.codon"; - } - stdlib->setFilename(stdlibPath->path); - cache->imports[STDLIB_IMPORT] = {stdlibPath->path, stdlib}; - stdlib->isStdlibLoading = true; - stdlib->moduleName = {ImportFile::STDLIB, stdlibPath->path, "__init__"}; - // Load the standard library - stdlib->setFilename(stdlibPath->path); - // Core definitions - auto core = TypecheckVisitor(stdlib).transform( - parseCode(stdlib->cache, stdlibPath->path, "from internal.core import *")); - preamble.insert(preamble.end(), stdlib->getBase()->preamble.begin(), - stdlib->getBase()->preamble.end()); - stdlib->getBase()->preamble.clear(); - preamble.push_back(core); - for (auto &d : earlyDefines) { - // Load early compile-time defines (for standard library) - auto def = TypecheckVisitor(stdlib).transform( - N(N(d.first), N(d.second), - N(N("Static"), N("int")))); - preamble.insert(preamble.end(), stdlib->getBase()->preamble.begin(), - stdlib->getBase()->preamble.end()); - stdlib->getBase()->preamble.clear(); - preamble.push_back(def); - } - auto std = - TypecheckVisitor(stdlib).transform(parseFile(stdlib->cache, stdlibPath->path)); - preamble.insert(preamble.end(), stdlib->getBase()->preamble.begin(), - stdlib->getBase()->preamble.end()); - stdlib->getBase()->preamble.clear(); - preamble.push_back(std); - stdlib->isStdlibLoading = false; - } + if (!in(cache->imports, STDLIB_IMPORT)) + loadStdLibrary(cache, preamble, earlyDefines, barebones); // Set up the context and the cache auto ctx = std::make_shared(cache, file); - cache->imports[file].filename = file; - cache->imports[file].ctx = ctx; - cache->imports[MAIN_IMPORT] = {file, ctx}; + cache->imports[file] = cache->imports[MAIN_IMPORT] = {MAIN_IMPORT, file, ctx}; ctx->setFilename(file); ctx->moduleName = {ImportFile::PACKAGE, file, MODULE_MAIN}; - if (!cache->typeCtx) - cache->typeCtx = std::make_shared(cache); // Prepare the code - auto suite = N(); - suite->stmts.push_back(N(".toplevel", std::vector{}, nullptr, - std::vector{N(Attr::Internal)})); + auto tv = TypecheckVisitor(ctx, preamble); + auto suite = tv.N(); + suite->stmts.push_back( + tv.N(".toplevel", std::vector{}, nullptr, + std::vector{tv.N(Attr::Internal)})); + // Load compile-time defines (e.g., codon run -DFOO=1 ...) for (auto &d : defines) { - // Load compile-time defines (e.g., codon run -DFOO=1 ...) suite->stmts.push_back( - N(N(d.first), N(d.second), - N(N("Static"), N("int")))); + tv.N(tv.N(d.first), tv.N(d.second), + tv.N(tv.N("Static"), tv.N("int")))); } // Set up __name__ suite->stmts.push_back( - N(N("__name__"), N(MODULE_MAIN))); + tv.N(tv.N("__name__"), tv.N(MODULE_MAIN))); suite->stmts.push_back(node); - auto v = TypecheckVisitor(ctx); - auto n = v.inferTypes(suite, true); + auto n = tv.inferTypes(suite, true); if (!n) { - v.error("cannot typecheck the program"); + tv.error("cannot typecheck the program"); } - suite = N(); - suite->stmts.push_back(N(preamble)); - suite->stmts.insert(suite->stmts.end(), ctx->getBase()->preamble.begin(), - ctx->getBase()->preamble.end()); - ctx->getBase()->preamble.clear(); + suite = tv.N(); + suite->stmts.push_back(tv.N(*preamble)); // Add dominated assignment declarations if (in(ctx->scope.stmts, ctx->scope.blocks.back())) @@ -127,8 +75,7 @@ StmtPtr TypecheckVisitor::apply( ctx->scope.stmts[ctx->scope.blocks.back()].end()); suite->stmts.push_back(n); if (n->getSuite()) - v.prepareVTables(); -#undef N + tv.prepareVTables(); if (!ctx->cache->errors.empty()) throw exc::ParserException(); @@ -136,23 +83,75 @@ StmtPtr TypecheckVisitor::apply( return suite; } +void TypecheckVisitor::loadStdLibrary( + Cache *cache, const std::shared_ptr> &preamble, + const std::unordered_map &earlyDefines, bool barebones) { + // Load the internal.__init__ + auto stdlib = std::make_shared(cache, STDLIB_IMPORT); + auto stdlibPath = + getImportFile(cache->argv0, STDLIB_INTERNAL_MODULE, "", true, cache->module0); + const std::string initFile = "__init__.codon"; + if (!stdlibPath || !endswith(stdlibPath->path, initFile)) + E(Error::COMPILER_NO_STDLIB); + + /// Use __init_test__ for faster testing (e.g., #%% name,barebones) + /// TODO: get rid of it one day... + if (barebones) { + stdlibPath->path = + stdlibPath->path.substr(0, stdlibPath->path.size() - initFile.size()) + + "__init_test__.codon"; + } + stdlib->setFilename(stdlibPath->path); + cache->imports[stdlibPath->path] = + cache->imports[STDLIB_IMPORT] = {STDLIB_IMPORT, stdlibPath->path, stdlib}; + + // Load the standard library + stdlib->isStdlibLoading = true; + stdlib->moduleName = {ImportFile::STDLIB, stdlibPath->path, "__init__"}; + stdlib->setFilename(stdlibPath->path); + + // 1. Core definitions + auto core = TypecheckVisitor(stdlib, preamble) + .transform(parseCode(stdlib->cache, stdlibPath->path, + "from internal.core import *")); + preamble->push_back(core); + LOG("core done"); + + // 2. Load early compile-time defines (for standard library) + for (auto &d : earlyDefines) { + auto tv = TypecheckVisitor(stdlib, preamble); + auto def = tv.transform( + tv.N(tv.N(d.first), tv.N(d.second), + tv.N(tv.N("Static"), tv.N("int")))); + preamble->push_back(def); + } + LOG("defs done"); + + // 3. Load stdlib + auto std = TypecheckVisitor(stdlib, preamble) + .transform(parseFile(stdlib->cache, stdlibPath->path)); + preamble->push_back(std); + stdlib->isStdlibLoading = false; + LOG("stdlib done"); +} + /// Simplify an AST node. Assumes that the standard library is loaded. StmtPtr TypecheckVisitor::apply(const std::shared_ptr &ctx, const StmtPtr &node, const std::string &file) { auto oldFilename = ctx->getFilename(); ctx->setFilename(file); - auto v = TypecheckVisitor(ctx); - auto n = v.inferTypes(node, true); + auto preamble = std::make_shared>(); + auto tv = TypecheckVisitor(ctx, preamble); + auto n = tv.inferTypes(node, true); ctx->setFilename(oldFilename); if (!n) { - v.error("cannot typecheck the program"); + tv.error("cannot typecheck the program"); } if (!ctx->cache->errors.empty()) { throw exc::ParserException(); } - auto suite = std::make_shared(ctx->getBase()->preamble); - ctx->getBase()->preamble.clear(); + auto suite = std::make_shared(*preamble); suite->stmts.push_back(n); return suite; } @@ -160,14 +159,16 @@ StmtPtr TypecheckVisitor::apply(const std::shared_ptr &ctx, /**************************************************************************************/ TypecheckVisitor::TypecheckVisitor(std::shared_ptr ctx, + const std::shared_ptr> &pre, const std::shared_ptr> &stmts) : ctx(std::move(ctx)) { + preamble = pre ? pre : std::make_shared>(); prependStmts = stmts ? stmts : std::make_shared>(); } /**************************************************************************************/ -ExprPtr TypecheckVisitor::transform(ExprPtr &expr) { return transform(expr); } +ExprPtr TypecheckVisitor::transform(ExprPtr &expr) { return transform(expr, true); } /// Transform an expression node. ExprPtr TypecheckVisitor::transform(ExprPtr &expr, bool allowTypes) { @@ -178,7 +179,7 @@ ExprPtr TypecheckVisitor::transform(ExprPtr &expr, bool allowTypes) { unify(expr->type, ctx->getUnbound()); auto typ = expr->type; if (!expr->done) { - TypecheckVisitor v(ctx, prependStmts); + TypecheckVisitor v(ctx, preamble, prependStmts); v.setSrcInfo(expr->getSrcInfo()); ctx->pushSrcInfo(expr->getSrcInfo()); expr->accept(v); @@ -236,7 +237,7 @@ StmtPtr TypecheckVisitor::transform(StmtPtr &stmt) { if (!stmt || stmt->done) return stmt; - TypecheckVisitor v(ctx); + TypecheckVisitor v(ctx, preamble); v.setSrcInfo(stmt->getSrcInfo()); ctx->pushSrcInfo(stmt->getSrcInfo()); stmt->accept(v); @@ -254,8 +255,8 @@ StmtPtr TypecheckVisitor::transform(StmtPtr &stmt) { } if (stmt->done) ctx->changedNodes++; - // LOG_TYPECHECK("[stmt] {}: {}{}", getSrcInfo(), stmt, stmt->isDone() ? "[done]" : - // ""); + // LOG("[stmt] {}: {} {}", getSrcInfo(), split(stmt->toString(1), '\n').front(), + // stmt->isDone() ? "[done]" : ""); return stmt; } @@ -534,7 +535,7 @@ bool TypecheckVisitor::wrapExpr(ExprPtr &expr, const TypePtr &expectedType, expr = transform(N(expr, N(EllipsisExpr::PARTIAL))); else expr = transform(N( - N("__internal__.class_ctr:0"), + N("__internal__.class_ctr"), std::vector{{"T", expr}, {"", N(EllipsisExpr::PARTIAL)}})); } @@ -576,7 +577,7 @@ bool TypecheckVisitor::wrapExpr(ExprPtr &expr, const TypePtr &expectedType, !expectedClass->getUnion()) { // Extract union types via __internal__.get_union if (auto t = realize(expectedClass)) { - expr = transform(N(N("__internal__.get_union:0"), expr, + expr = transform(N(N("__internal__.get_union"), expr, N(t->realizedName()))); } else { return false; @@ -587,7 +588,7 @@ bool TypecheckVisitor::wrapExpr(ExprPtr &expr, const TypePtr &expectedType, expectedClass->getUnion()->addType(exprClass); if (auto t = realize(expectedClass)) { if (expectedClass->unify(exprClass.get(), nullptr) == -1) - expr = transform(N(N("__internal__.new_union:0"), expr, + expr = transform(N(N("__internal__.new_union"), expr, NT(t->realizedName()))); } else { return false; @@ -656,4 +657,36 @@ TypecheckVisitor::unpackTupleTypes(ExprPtr expr) { return ret; } +TypePtr TypecheckVisitor::getClassGeneric(const types::ClassTypePtr &cls, int idx) { + seqassert(idx < cls->generics.size(), "bad generic"); + return cls->generics[idx].type; +} +std::string TypecheckVisitor::getClassStaticStr(const types::ClassTypePtr &cls, + int idx) { + int i = 0; + for (auto &g : cls->generics) { + if (g.type->getStatic() && + g.type->getStatic()->expr->staticValue.type == StaticValue::STRING) { + if (i++ == idx) { + return g.type->getStatic()->evaluate().getString(); + } + } + } + seqassert(false, "bad string static generic"); + return ""; +} +int64_t TypecheckVisitor::getClassStaticInt(const types::ClassTypePtr &cls, int idx) { + int i = 0; + for (auto &g : cls->generics) { + if (g.type->getStatic() && + g.type->getStatic()->expr->staticValue.type == StaticValue::INT) { + if (i++ == idx) { + return g.type->getStatic()->evaluate().getInt(); + } + } + } + seqassert(false, "bad int static generic"); + return -1; +} + } // namespace codon::ast diff --git a/codon/parser/visitors/typecheck/typecheck.h b/codon/parser/visitors/typecheck/typecheck.h index 31feac68..e6bd2a4b 100644 --- a/codon/parser/visitors/typecheck/typecheck.h +++ b/codon/parser/visitors/typecheck/typecheck.h @@ -26,7 +26,8 @@ class TypecheckVisitor : public CallbackASTVisitor { /// Shared simplification context. std::shared_ptr ctx; /// Statements to prepend before the current statement. - std::shared_ptr> prependStmts; + std::shared_ptr> prependStmts = nullptr; + std::shared_ptr> preamble = nullptr; /// Each new expression is stored here (as @c visit does not return anything) and /// later returned by a @c transform call. @@ -45,9 +46,15 @@ public: static StmtPtr apply(const std::shared_ptr &cache, const StmtPtr &node, const std::string &file = ""); +private: + static void loadStdLibrary(Cache *, const std::shared_ptr> &, + const std::unordered_map &, + bool); + public: explicit TypecheckVisitor( std::shared_ptr ctx, + const std::shared_ptr> &preamble = nullptr, const std::shared_ptr> &stmts = nullptr); public: // Convenience transformators @@ -89,6 +96,7 @@ private: // Node typechecking rules /* Identifier access expressions (access.cpp) */ void visit(IdExpr *) override; + TypeContext::Item findDominatingBinding(const std::string &, TypeContext *); bool checkCapture(const TypeContext::Item &); void visit(DotExpr *) override; std::pair getImport(const std::vector &); @@ -235,8 +243,8 @@ private: // Node typechecking rules void visit(ClassStmt *) override; std::vector parseBaseClasses(std::vector &, std::vector &, const Attr &, - const std::string &, - const ExprPtr & = nullptr); + const std::string &, const ExprPtr &, + types::ClassTypePtr &); std::pair autoDeduceMembers(ClassStmt *, std::vector &); std::vector getClassMethods(const StmtPtr &s); @@ -254,13 +262,15 @@ private: // Node typechecking rules void visit(CommentStmt *stmt) override; void visit(CustomStmt *) override; -private: +public: /* Type inference (infer.cpp) */ types::TypePtr unify(types::TypePtr &a, const types::TypePtr &b); types::TypePtr unify(types::TypePtr &&a, const types::TypePtr &b) { auto x = a; return unify(x, b); } + +private: StmtPtr inferTypes(StmtPtr, bool isToplevel = false); types::TypePtr realize(types::TypePtr); types::TypePtr realizeFunc(types::FuncType *, bool = false); @@ -271,6 +281,10 @@ private: codon::ir::Func * makeIRFunction(const std::shared_ptr &); + types::TypePtr getClassGeneric(const types::ClassTypePtr &, int = 0); + std::string getClassStaticStr(const types::ClassTypePtr &, int = 0); + int64_t getClassStaticInt(const types::ClassTypePtr &, int = 0); + private: types::FuncTypePtr findBestMethod(const types::ClassTypePtr &typ, const std::string &member, @@ -293,6 +307,7 @@ private: public: bool isTuple(const std::string &s) const { return startswith(s, TYPE_TUPLE); } + std::shared_ptr getCtx() const { return ctx; } friend class Cache; friend class TypeContext; @@ -308,4 +323,21 @@ private: // Helpers const std::function(StmtPtr)> &); }; +class NameVisitor : public CallbackASTVisitor { + TypecheckVisitor *tv; + ExprPtr resultExpr = nullptr; + StmtPtr resultStmt = nullptr; + +public: + NameVisitor(TypecheckVisitor *tv) : tv(tv) {} + ExprPtr transform(const std::shared_ptr &expr) override; + ExprPtr transform(std::shared_ptr &expr) override; + StmtPtr transform(const std::shared_ptr &stmt) override; + StmtPtr transform(std::shared_ptr &stmt) override; + void visit(IdExpr *expr) override; + void visit(AssignStmt *stmt) override; + void visit(TryStmt *stmt) override; + void visit(ForStmt *stmt) override; +}; + } // namespace codon::ast diff --git a/stdlib/internal/__init__.codon b/stdlib/internal/__init__.codon index 21b15a76..5bc75f2b 100644 --- a/stdlib/internal/__init__.codon +++ b/stdlib/internal/__init__.codon @@ -4,6 +4,7 @@ from internal.attributes import * from internal.static import static_print as __static_print__ + from internal.types.ptr import * from internal.types.str import * from internal.types.int import * @@ -33,19 +34,20 @@ from internal.types.collections.tuple import * import internal.c_stubs as _C from internal.format import * from internal.builtin import * + from internal.builtin import _jit_display from internal.str import * from internal.sort import sorted -from openmp import Ident as __OMPIdent, for_par -from gpu import _gpu_loop_outline_template +# from openmp import Ident as __OMPIdent, for_par +# from gpu import _gpu_loop_outline_template from internal.file import File, gzFile, open, gzopen from pickle import pickle, unpickle from internal.dlopen import dlsym as _dlsym -import internal.python +# import internal.python -if __py_numerics__: - import internal.pynumerics -if __py_extension__: - internal.python.ensure_initialized() +# if __py_numerics__: +# import internal.pynumerics +# if __py_extension__: +# internal.python.ensure_initialized() diff --git a/stdlib/internal/core.codon b/stdlib/internal/core.codon index f6dd2c5a..0741e6bd 100644 --- a/stdlib/internal/core.codon +++ b/stdlib/internal/core.codon @@ -7,10 +7,6 @@ class __internal__: class __magic__: pass -@__internal__ -class __magic__: - pass - @tuple @__internal__ @__notuple__ @@ -163,6 +159,15 @@ class __array__: def __new__(sz: Static[int]) -> Array[T]: pass +@dataclass(init=True) +@tuple +@__internal__ +class Import: + path: Static[str] + name: str + def __new__(P: Static[str], name: str) -> Import[P]: + return (name, ) + def __ptr__(var): pass diff --git a/stdlib/internal/internal.codon b/stdlib/internal/internal.codon index 79013118..318278ce 100644 --- a/stdlib/internal/internal.codon +++ b/stdlib/internal/internal.codon @@ -260,11 +260,6 @@ class __internal__: if msg: raise OSError(prefix + msg) - @pure - @llvm - def opt_tuple_new(T: type) -> Optional[T]: - ret { i1, {=T} } { i1 false, {=T} undef } - @pure @llvm def opt_ref_new(T: type) -> Optional[T]: @@ -630,12 +625,8 @@ class __magic__: return slf.__repr__() -@dataclass(init=True) -@tuple +@extend class Import: - name: str - file: str - def __repr__(self) -> str: return f"" diff --git a/stdlib/internal/python.codon b/stdlib/internal/python.codon index 35ff9630..ee477c75 100644 --- a/stdlib/internal/python.codon +++ b/stdlib/internal/python.codon @@ -1948,3 +1948,11 @@ class _PyWrap: if obj.head.pytype != pytype: _conversion_error(T.__name__) return obj.data + + +class PyError(Static[Exception]): + pytype: pyobj + + def __init__(self, message: str, pytype: pyobj = pyobj(cobj(), steal=True)): + super().__init__("PyError", message) + self.pytype = pytype diff --git a/stdlib/internal/types/array.codon b/stdlib/internal/types/array.codon index 3d8e4be6..7fd95af7 100644 --- a/stdlib/internal/types/array.codon +++ b/stdlib/internal/types/array.codon @@ -39,3 +39,10 @@ class Array: return (e - s, self.ptr + s) array = Array + +# Forward declarations +@dataclass(init=False) +class List: + len: int + arr: Array[T] + T: type diff --git a/stdlib/internal/types/complex.codon b/stdlib/internal/types/complex.codon index ba4bbedd..031ce7a7 100644 --- a/stdlib/internal/types/complex.codon +++ b/stdlib/internal/types/complex.codon @@ -287,6 +287,9 @@ class int: @extend class float: + def __complex__(self) -> complex: + return complex(self, 0.0) + def __suffix_j__(x: float) -> complex: return complex(0, x) @@ -566,3 +569,8 @@ class complex64: declare float @llvm.log.f32(float) %y = call float @llvm.log.f32(float %x) ret float %y + +@extend +class int: + def __complex__(self) -> complex: + return complex(float(self), 0.0) diff --git a/stdlib/internal/types/error.codon b/stdlib/internal/types/error.codon index a0f8ea90..8d33a1ab 100644 --- a/stdlib/internal/types/error.codon +++ b/stdlib/internal/types/error.codon @@ -85,13 +85,6 @@ class CError(Static[Exception]): super().__init__("CError", message) self.python_type = self.__class__._pytype -class PyError(Static[Exception]): - pytype: pyobj - - def __init__(self, message: str, pytype: pyobj = pyobj(cobj(), steal=True)): - super().__init__("PyError", message) - self.pytype = pytype - class TypeError(Static[Exception]): _pytype: ClassVar[cobj] = cobj() def __init__(self, message: str = ""): diff --git a/stdlib/internal/types/float.codon b/stdlib/internal/types/float.codon index fdbb25f4..b72c804b 100644 --- a/stdlib/internal/types/float.codon +++ b/stdlib/internal/types/float.codon @@ -2,7 +2,6 @@ from internal.attributes import commutative from internal.gc import alloc_atomic, free -from internal.types.complex import complex @extend class float: @@ -41,9 +40,6 @@ class float: %1 = zext i1 %0 to i8 ret i8 %1 - def __complex__(self) -> complex: - return complex(self, 0.0) - def __pos__(self) -> float: return self diff --git a/stdlib/internal/types/int.codon b/stdlib/internal/types/int.codon index 8602bfe1..1ba1d98f 100644 --- a/stdlib/internal/types/int.codon +++ b/stdlib/internal/types/int.codon @@ -1,7 +1,6 @@ # Copyright (C) 2022-2023 Exaloop Inc. from internal.attributes import commutative, associative, distributive -from internal.types.complex import complex @extend class int: @@ -29,9 +28,6 @@ class int: %tmp = sitofp i64 %self to double ret double %tmp - def __complex__(self) -> complex: - return complex(float(self), 0.0) - def __index__(self) -> int: return self diff --git a/stdlib/internal/types/optional.codon b/stdlib/internal/types/optional.codon index 0af011e5..ea61fd17 100644 --- a/stdlib/internal/types/optional.codon +++ b/stdlib/internal/types/optional.codon @@ -1,5 +1,13 @@ # Copyright (C) 2022-2023 Exaloop Inc. +@extend +class __internal__: + @pure + @llvm + def opt_tuple_new(T: type) -> Optional[T]: + ret { i1, {=T} } { i1 false, {=T} undef } + + @extend class Optional: def __new__() -> Optional[T]: diff --git a/stdlib/internal/types/ptr.codon b/stdlib/internal/types/ptr.codon index 16a48fd3..c19c9ae2 100644 --- a/stdlib/internal/types/ptr.codon +++ b/stdlib/internal/types/ptr.codon @@ -190,12 +190,6 @@ class Ptr: ptr = Ptr Jar = Ptr[byte] -# Forward declarations -class List: - len: int - arr: Array[T] - T: type - @extend class NoneType: def __new__() -> NoneType: