Track statement times to properly reference names in case of delayed typechecking; Static short-circuiting; Tiered delayed generic resolution

typecheck-v2
Ibrahim Numanagić 2025-01-18 21:30:37 -08:00
parent 04913cd405
commit 740815f2b8
28 changed files with 391 additions and 139 deletions

View File

@ -12,6 +12,8 @@ namespace ir {
const std::string StringValueAttribute::AttributeName = "svAttribute";
const std::string IntValueAttribute::AttributeName = "i64Attribute";
const std::string StringListAttribute::AttributeName = "slAttribute";
std::ostream &StringListAttribute::doFormat(std::ostream &os) const {

View File

@ -329,6 +329,23 @@ private:
std::ostream &doFormat(std::ostream &os) const override;
};
struct IntValueAttribute : public Attribute {
static const std::string AttributeName;
int64_t value;
IntValueAttribute() = default;
/// Constructs a IntValueAttribute.
explicit IntValueAttribute(int64_t value) : value(value) {}
std::unique_ptr<Attribute> clone() const override {
return std::make_unique<IntValueAttribute>(*this);
}
private:
std::ostream &doFormat(std::ostream &os) const override { return os << value; }
};
} // namespace ir
std::map<std::string, std::unique_ptr<ir::Attribute>>

View File

@ -161,6 +161,10 @@ public:
AttributeType *getAttribute(const std::string &key) {
return static_cast<AttributeType *>(getAttribute(key));
}
template <typename AttributeType>
const AttributeType *getAttribute(const std::string &key) const {
return static_cast<const AttributeType *>(getAttribute(key));
}
void eraseAttribute(const std::string &key) { attributes.erase(key); }
void cloneAttributesFrom(Node *n) { attributes = codon::clone(n->attributes); }

View File

@ -28,10 +28,7 @@ types::Type *Instr::doGetType() const { return getModule()->getNoneType(); }
const char AssignInstr::NodeId = 0;
AssignInstr::AssignInstr(Var *lhs, Value *rhs, std::string name)
: AcceptorExtend(std::move(name)), lhs(lhs), rhs(rhs) {
if (!lhs->getType())
LOG("->");
}
: AcceptorExtend(std::move(name)), lhs(lhs), rhs(rhs) {}
int AssignInstr::doReplaceUsedValue(id_t id, Value *newValue) {
if (rhs->getId() == id) {

View File

@ -16,6 +16,14 @@ SrcInfo::SrcInfo() : SrcInfo("", 0, 0, 0) {}
bool SrcInfo::operator==(const SrcInfo &src) const { return id == src.id; }
bool SrcInfo::operator<(const SrcInfo &src) const {
return std::tie(file, line, col) < std::tie(src.file, src.line, src.col);
}
bool SrcInfo::operator<=(const SrcInfo &src) const {
return std::tie(file, line, col) <= std::tie(src.file, src.line, src.col);
}
namespace error {
char ParserErrorInfo::ID = 0;

View File

@ -57,5 +57,6 @@ const std::string Attr::ExprOrderedCall = "exprOrderedCall";
const std::string Attr::ExprExternVar = "exprExternVar";
const std::string Attr::ExprDominatedUndefCheck = "exprDominatedUndefCheck";
const std::string Attr::ExprDominatedUsed = "exprDominatedUsed";
const std::string Attr::ExprTime = "exprTime";
} // namespace codon::ast

View File

@ -62,5 +62,6 @@ struct Attr {
const static std::string ExprExternVar;
const static std::string ExprDominatedUndefCheck;
const static std::string ExprDominatedUsed;
const static std::string ExprTime;
};
} // namespace codon::ast

View File

@ -27,6 +27,8 @@ struct SrcInfo {
SrcInfo();
SrcInfo(std::string file, int line, int col, int len);
bool operator==(const SrcInfo &src) const;
bool operator<(const SrcInfo &src) const;
bool operator<=(const SrcInfo &src) const;
};
class ErrorMessage {

View File

@ -55,6 +55,9 @@ struct ASTNode : public ir::Node {
void setAttribute(const std::string &key, const std::string &value) {
attributes[key] = std::make_unique<ir::StringValueAttribute>(value);
}
void setAttribute(const std::string &key, int64_t value) {
attributes[key] = std::make_unique<ir::IntValueAttribute>(value);
}
void setAttribute(const std::string &key) {
attributes[key] = std::make_unique<ir::Attribute>();
}

View File

@ -27,6 +27,11 @@ Stmt::Stmt(const Stmt &expr, bool clean) : AcceptorExtend(expr) {
if (clean)
done = false;
}
std::string Stmt::wrapStmt(const std::string &s) const {
// if (auto a = ir::Node::getAttribute<ir::IntValueAttribute>(Attr::ExprTime))
// return format("{}%%{}", s, a->value);
return s;
}
SuiteStmt::SuiteStmt(std::vector<Stmt *> stmts)
: AcceptorExtend(), Items(std::move(stmts)) {}
@ -44,7 +49,8 @@ std::string SuiteStmt::toString(int indent) const {
is.insert(findStar(is), "*");
s += (i ? pad : "") + is;
}
return format("({}suite{})", (isDone() ? "*" : ""), (s.empty() ? s : " " + pad + s));
return wrapStmt(
format("({}suite{})", (isDone() ? "*" : ""), (s.empty() ? s : " " + pad + s)));
}
void SuiteStmt::flatten() {
std::vector<Stmt *> ns;
@ -71,17 +77,17 @@ SuiteStmt *SuiteStmt::wrap(Stmt *s) {
}
BreakStmt::BreakStmt(const BreakStmt &stmt, bool clean) : AcceptorExtend(stmt, clean) {}
std::string BreakStmt::toString(int indent) const { return "(break)"; }
std::string BreakStmt::toString(int indent) const { return wrapStmt("(break)"); }
ContinueStmt::ContinueStmt(const ContinueStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean) {}
std::string ContinueStmt::toString(int indent) const { return "(continue)"; }
std::string ContinueStmt::toString(int indent) const { return wrapStmt("(continue)"); }
ExprStmt::ExprStmt(Expr *expr) : AcceptorExtend(), expr(expr) {}
ExprStmt::ExprStmt(const ExprStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}
std::string ExprStmt::toString(int indent) const {
return format("(expr {})", expr->toString(indent));
return wrapStmt(format("(expr {})", expr->toString(indent)));
}
AssignStmt::AssignStmt(Expr *lhs, Expr *rhs, Expr *type, UpdateMode update)
@ -91,16 +97,16 @@ AssignStmt::AssignStmt(const AssignStmt &stmt, bool clean)
rhs(ast::clone(stmt.rhs, clean)), type(ast::clone(stmt.type, clean)),
update(stmt.update) {}
std::string AssignStmt::toString(int indent) const {
return format("({} {}{}{})", update != Assign ? "update" : "assign",
lhs->toString(indent), rhs ? " " + rhs->toString(indent) : "",
type ? format(" #:type {}", type->toString(indent)) : "");
return wrapStmt(format("({} {}{}{})", update != Assign ? "update" : "assign",
lhs->toString(indent), rhs ? " " + rhs->toString(indent) : "",
type ? format(" #:type {}", type->toString(indent)) : ""));
}
DelStmt::DelStmt(Expr *expr) : AcceptorExtend(), expr(expr) {}
DelStmt::DelStmt(const DelStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}
std::string DelStmt::toString(int indent) const {
return format("(del {})", expr->toString(indent));
return wrapStmt(format("(del {})", expr->toString(indent)));
}
PrintStmt::PrintStmt(std::vector<Expr *> items, bool noNewline)
@ -109,21 +115,21 @@ PrintStmt::PrintStmt(const PrintStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)),
noNewline(stmt.noNewline) {}
std::string PrintStmt::toString(int indent) const {
return format("(print {}{})", noNewline ? "#:inline " : "", combine(items));
return wrapStmt(format("(print {}{})", noNewline ? "#:inline " : "", combine(items)));
}
ReturnStmt::ReturnStmt(Expr *expr) : AcceptorExtend(), expr(expr) {}
ReturnStmt::ReturnStmt(const ReturnStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}
std::string ReturnStmt::toString(int indent) const {
return expr ? format("(return {})", expr->toString(indent)) : "(return)";
return wrapStmt(expr ? format("(return {})", expr->toString(indent)) : "(return)");
}
YieldStmt::YieldStmt(Expr *expr) : AcceptorExtend(), expr(expr) {}
YieldStmt::YieldStmt(const YieldStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}
std::string YieldStmt::toString(int indent) const {
return expr ? format("(yield {})", expr->toString(indent)) : "(yield)";
return wrapStmt(expr ? format("(yield {})", expr->toString(indent)) : "(yield)");
}
AssertStmt::AssertStmt(Expr *expr, Expr *message)
@ -132,8 +138,8 @@ AssertStmt::AssertStmt(const AssertStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)),
message(ast::clone(stmt.message, clean)) {}
std::string AssertStmt::toString(int indent) const {
return format("(assert {}{})", expr->toString(indent),
message ? message->toString(indent) : "");
return wrapStmt(format("(assert {}{})", expr->toString(indent),
message ? message->toString(indent) : ""));
}
WhileStmt::WhileStmt(Expr *cond, Stmt *suite, Stmt *elseSuite)
@ -145,15 +151,17 @@ WhileStmt::WhileStmt(const WhileStmt &stmt, bool clean)
elseSuite(ast::clone(stmt.elseSuite, clean)) {}
std::string WhileStmt::toString(int indent) const {
if (indent == -1)
return format("(while {})", cond->toString(indent));
return wrapStmt(format("(while {})", cond->toString(indent)));
std::string pad = indent > 0 ? ("\n" + std::string(indent + INDENT_SIZE, ' ')) : " ";
if (elseSuite && elseSuite->firstInBlock())
return format("(while-else {}{}{}{}{})", cond->toString(indent), pad,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1), pad,
elseSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1));
else
return format("(while {}{}{})", cond->toString(indent), pad,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1));
if (elseSuite && elseSuite->firstInBlock()) {
return wrapStmt(
format("(while-else {}{}{}{}{})", cond->toString(indent), pad,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1), pad,
elseSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)));
} else {
return wrapStmt(format("(while {}{}{})", cond->toString(indent), pad,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)));
}
}
ForStmt::ForStmt(Expr *var, Expr *iter, Stmt *suite, Stmt *elseSuite, Expr *decorator,
@ -171,7 +179,7 @@ ForStmt::ForStmt(const ForStmt &stmt, bool clean)
std::string ForStmt::toString(int indent) const {
auto vs = var->toString(indent);
if (indent == -1)
return format("(for {} {})", vs, iter->toString(indent));
return wrapStmt(format("(for {} {})", vs, iter->toString(indent)));
std::string pad = indent > 0 ? ("\n" + std::string(indent + INDENT_SIZE, ' ')) : " ";
std::string attr;
@ -179,13 +187,15 @@ std::string ForStmt::toString(int indent) const {
attr += " " + decorator->toString(indent);
if (!attr.empty())
attr = " #:attr" + attr;
if (elseSuite && elseSuite->firstInBlock())
return format("(for-else {} {}{}{}{}{}{})", vs, iter->toString(indent), attr, pad,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1), pad,
elseSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1));
else
return format("(for {} {}{}{}{})", vs, iter->toString(indent), attr, pad,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1));
if (elseSuite && elseSuite->firstInBlock()) {
return wrapStmt(
format("(for-else {} {}{}{}{}{}{})", vs, iter->toString(indent), attr, pad,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1), pad,
elseSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)));
} else {
return wrapStmt(format("(for {} {}{}{}{})", vs, iter->toString(indent), attr, pad,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)));
}
}
IfStmt::IfStmt(Expr *cond, Stmt *ifSuite, Stmt *elseSuite)
@ -197,13 +207,13 @@ IfStmt::IfStmt(const IfStmt &stmt, bool clean)
elseSuite(ast::clone(stmt.elseSuite, clean)) {}
std::string IfStmt::toString(int indent) const {
if (indent == -1)
return format("(if {})", cond->toString(indent));
return wrapStmt(format("(if {})", cond->toString(indent)));
std::string pad = indent > 0 ? ("\n" + std::string(indent + INDENT_SIZE, ' ')) : " ";
return format("(if {}{}{}{})", cond->toString(indent), pad,
ifSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1),
elseSuite
? pad + elseSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)
: "");
return wrapStmt(format(
"(if {}{}{}{})", cond->toString(indent), pad,
ifSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1),
elseSuite ? pad + elseSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)
: ""));
}
MatchCase::MatchCase(Expr *pattern, Expr *guard, Stmt *suite)
@ -220,7 +230,7 @@ MatchStmt::MatchStmt(const MatchStmt &stmt, bool clean)
expr(ast::clone(stmt.expr, clean)) {}
std::string MatchStmt::toString(int indent) const {
if (indent == -1)
return format("(match {})", expr->toString(indent));
return wrapStmt(format("(match {})", expr->toString(indent)));
std::string pad = indent > 0 ? ("\n" + std::string(indent + INDENT_SIZE, ' ')) : " ";
std::string padExtra = indent > 0 ? std::string(INDENT_SIZE, ' ') : "";
std::vector<std::string> s;
@ -229,14 +239,13 @@ std::string MatchStmt::toString(int indent) const {
c.guard ? " #:guard " + c.guard->toString(indent) : "",
pad + padExtra,
c.suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1 * 2)));
return format("(match {}{}{})", expr->toString(indent), pad, join(s, pad));
return wrapStmt(format("(match {}{}{})", expr->toString(indent), pad, join(s, pad)));
}
ImportStmt::ImportStmt(Expr *from, Expr *what, std::vector<Param> args, Expr *ret,
std::string as, size_t dots, bool isFunction)
: AcceptorExtend(), from(from), what(what), as(std::move(as)), dots(dots),
args(std::move(args)), ret(ret), isFunction(isFunction) {
}
args(std::move(args)), ret(ret), isFunction(isFunction) {}
ImportStmt::ImportStmt(const ImportStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), from(ast::clone(stmt.from, clean)),
what(ast::clone(stmt.what, clean)), as(stmt.as), dots(stmt.dots),
@ -246,12 +255,12 @@ std::string ImportStmt::toString(int indent) const {
std::vector<std::string> va;
for (auto &a : args)
va.push_back(a.toString(indent));
return format("(import {}{}{}{}{}{})", from ? from->toString(indent) : "",
as.empty() ? "" : format(" #:as '{}", as),
what ? format(" #:what {}", what->toString(indent)) : "",
dots ? format(" #:dots {}", dots) : "",
va.empty() ? "" : format(" #:args ({})", join(va)),
ret ? format(" #:ret {}", ret->toString(indent)) : "");
return wrapStmt(format("(import {}{}{}{}{}{})", from ? from->toString(indent) : "",
as.empty() ? "" : format(" #:as '{}", as),
what ? format(" #:what {}", what->toString(indent)) : "",
dots ? format(" #:dots {}", dots) : "",
va.empty() ? "" : format(" #:args ({})", join(va)),
ret ? format(" #:ret {}", ret->toString(indent)) : ""));
}
ExceptStmt::ExceptStmt(const std::string &var, Expr *exc, Stmt *suite)
@ -262,9 +271,10 @@ ExceptStmt::ExceptStmt(const ExceptStmt &stmt, bool clean)
std::string ExceptStmt::toString(int indent) const {
std::string pad = indent > 0 ? ("\n" + std::string(indent + INDENT_SIZE, ' ')) : " ";
std::string padExtra = indent > 0 ? std::string(INDENT_SIZE, ' ') : "";
return format("(catch {}{}{}{})", !var.empty() ? format("#:var '{}", var) : "",
exc ? format(" #:exc {}", exc->toString(indent)) : "", pad + padExtra,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1 * 2));
return wrapStmt(
format("(catch {}{}{}{})", !var.empty() ? format("#:var '{}", var) : "",
exc ? format(" #:exc {}", exc->toString(indent)) : "", pad + padExtra,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1 * 2)));
}
TryStmt::TryStmt(Stmt *suite, std::vector<ExceptStmt *> excepts, Stmt *finally)
@ -275,17 +285,17 @@ TryStmt::TryStmt(const TryStmt &stmt, bool clean)
suite(ast::clone(stmt.suite, clean)), finally(ast::clone(stmt.finally, clean)) {}
std::string TryStmt::toString(int indent) const {
if (indent == -1)
return format("(try)");
return wrapStmt(format("(try)"));
std::string pad = indent > 0 ? ("\n" + std::string(indent + INDENT_SIZE, ' ')) : " ";
std::vector<std::string> s;
for (auto &i : items)
s.push_back(i->toString(indent));
return format(
return wrapStmt(format(
"(try{}{}{}{}{})", pad, suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1),
pad, join(s, pad),
finally ? format("{}{}", pad,
finally->toString(indent >= 0 ? indent + INDENT_SIZE : -1))
: "");
: ""));
}
ThrowStmt::ThrowStmt(Expr *expr, Expr *from, bool transformed)
@ -294,8 +304,8 @@ ThrowStmt::ThrowStmt(const ThrowStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)),
from(ast::clone(stmt.from, clean)), transformed(stmt.transformed) {}
std::string ThrowStmt::toString(int indent) const {
return format("(throw{}{})", expr ? " " + expr->toString(indent) : "",
from ? format(" :from {}", from->toString(indent)) : "");
return wrapStmt(format("(throw{}{})", expr ? " " + expr->toString(indent) : "",
from ? format(" :from {}", from->toString(indent)) : ""));
}
GlobalStmt::GlobalStmt(std::string var, bool nonLocal)
@ -303,14 +313,13 @@ GlobalStmt::GlobalStmt(std::string var, bool nonLocal)
GlobalStmt::GlobalStmt(const GlobalStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), var(stmt.var), nonLocal(stmt.nonLocal) {}
std::string GlobalStmt::toString(int indent) const {
return format("({} '{})", nonLocal ? "nonlocal" : "global", var);
return wrapStmt(format("({} '{})", nonLocal ? "nonlocal" : "global", var));
}
FunctionStmt::FunctionStmt(std::string name, Expr *ret, std::vector<Param> args,
Stmt *suite, std::vector<Expr *> decorators)
: AcceptorExtend(), Items(std::move(args)), name(std::move(name)), ret(ret),
suite(SuiteStmt::wrap(suite)), decorators(std::move(decorators)) {
}
suite(SuiteStmt::wrap(suite)), decorators(std::move(decorators)) {}
FunctionStmt::FunctionStmt(const FunctionStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)),
name(stmt.name), ret(ast::clone(stmt.ret, clean)),
@ -326,13 +335,13 @@ std::string FunctionStmt::toString(int indent) const {
if (a)
dec.push_back(format("(dec {})", a->toString(indent)));
if (indent == -1)
return format("(fn '{} ({}){})", name, join(as, " "),
ret ? " #:ret " + ret->toString(indent) : "");
return format("(fn '{} ({}){}{}{}{})", name, join(as, " "),
ret ? " #:ret " + ret->toString(indent) : "",
dec.empty() ? "" : format(" (dec {})", join(dec, " ")), pad,
suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)
: "(suite)");
return wrapStmt(format("(fn '{} ({}){})", name, join(as, " "),
ret ? " #:ret " + ret->toString(indent) : ""));
return wrapStmt(format(
"(fn '{} ({}){}{}{}{})", name, join(as, " "),
ret ? " #:ret " + ret->toString(indent) : "",
dec.empty() ? "" : format(" (dec {})", join(dec, " ")), pad,
suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1) : "(suite)"));
}
std::string FunctionStmt::signature() const {
std::vector<std::string> s;
@ -452,13 +461,13 @@ std::string ClassStmt::toString(int indent) const {
for (auto &a : decorators)
attr.push_back(format("(dec {})", a->toString(indent)));
if (indent == -1)
return format("(class '{} ({}))", name, as);
return format("(class '{}{}{}{}{}{})", name,
bases.empty() ? "" : format(" (bases {})", join(bases, " ")),
attr.empty() ? "" : format(" (attr {})", join(attr, " ")),
as.empty() ? as : pad + as, pad,
suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)
: "(suite)");
return wrapStmt(format("(class '{} ({}))", name, as));
return wrapStmt(format(
"(class '{}{}{}{}{}{})", name,
bases.empty() ? "" : format(" (bases {})", join(bases, " ")),
attr.empty() ? "" : format(" (attr {})", join(attr, " ")),
as.empty() ? as : pad + as, pad,
suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1) : "(suite)"));
}
bool ClassStmt::isRecord() const { return hasAttribute(Attr::Tuple); }
bool ClassStmt::isClassVar(const Param &p) {
@ -484,7 +493,7 @@ YieldFromStmt::YieldFromStmt(Expr *expr) : AcceptorExtend(), expr(std::move(expr
YieldFromStmt::YieldFromStmt(const YieldFromStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}
std::string YieldFromStmt::toString(int indent) const {
return format("(yield-from {})", expr->toString(indent));
return wrapStmt(format("(yield-from {})", expr->toString(indent)));
}
WithStmt::WithStmt(std::vector<Expr *> items, std::vector<std::string> vars,
@ -517,9 +526,9 @@ std::string WithStmt::toString(int indent) const {
: items[i]->toString(indent));
}
if (indent == -1)
return format("(with ({}))", join(as, " "));
return format("(with ({}){}{})", join(as, " "), pad,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1));
return wrapStmt(format("(with ({}))", join(as, " ")));
return wrapStmt(format("(with ({}){}{})", join(as, " "), pad,
suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)));
}
CustomStmt::CustomStmt(std::string keyword, Expr *expr, Stmt *suite)
@ -530,9 +539,10 @@ CustomStmt::CustomStmt(const CustomStmt &stmt, bool clean)
expr(ast::clone(stmt.expr, clean)), suite(ast::clone(stmt.suite, clean)) {}
std::string CustomStmt::toString(int indent) const {
std::string pad = indent > 0 ? ("\n" + std::string(indent + INDENT_SIZE, ' ')) : " ";
return format("(custom-{} {}{}{})", keyword,
expr ? format(" #:expr {}", expr->toString(indent)) : "", pad,
suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1) : "");
return wrapStmt(
format("(custom-{} {}{}{})", keyword,
expr ? format(" #:expr {}", expr->toString(indent)) : "", pad,
suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1) : ""));
}
AssignMemberStmt::AssignMemberStmt(Expr *lhs, std::string member, Expr *rhs)
@ -541,8 +551,8 @@ AssignMemberStmt::AssignMemberStmt(const AssignMemberStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), lhs(ast::clone(stmt.lhs, clean)),
member(stmt.member), rhs(ast::clone(stmt.rhs, clean)) {}
std::string AssignMemberStmt::toString(int indent) const {
return format("(assign-member {} {} {})", lhs->toString(indent), member,
rhs->toString(indent));
return wrapStmt(format("(assign-member {} {} {})", lhs->toString(indent), member,
rhs->toString(indent)));
}
CommentStmt::CommentStmt(std::string comment)
@ -550,7 +560,7 @@ CommentStmt::CommentStmt(std::string comment)
CommentStmt::CommentStmt(const CommentStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), comment(stmt.comment) {}
std::string CommentStmt::toString(int indent) const {
return format("(comment \"{}\")", comment);
return wrapStmt(format("(comment \"{}\")", comment));
}
const char Stmt::NodeId = 0;

View File

@ -51,6 +51,8 @@ struct Stmt : public AcceptorExtend<Stmt, ASTNode> {
static const char NodeId;
SERIALIZE(Stmt, BASE(ASTNode), done);
virtual std::string wrapStmt(const std::string &) const;
private:
/// Flag that indicates if all types in a statement are inferred (i.e. if a
/// type-checking procedure was successful).

View File

@ -18,7 +18,7 @@
}
#define STOP_ERROR(...) \
do { \
addError(__VA_ARGS__); \
addError(__VA_ARGS__); \
return; \
} while (0)
@ -66,6 +66,7 @@ bool ScopingVisitor::transform(Stmt *stmt) {
errors.append(v.errors);
if (!canContinue())
return false;
stmt->setAttribute(Attr::ExprTime, ++ctx->time);
}
return true;
}
@ -847,8 +848,6 @@ ScopingVisitor::findDominatingBinding(const std::string &name, bool allowShadow)
lastGood = i;
}
}
// if (commonScope != ctx->scope.size())
// LOG("==> {}: {} / {} vs {}", getSrcInfo(), name, ctx->getScope(), commonScope);
seqassert(lastGood != it->end(), "corrupted scoping ({})", name);
if (!allowShadow) { // go to the end
lastGood = it->end();

View File

@ -93,6 +93,9 @@ class ScopingVisitor : public CallbackASTVisitor<bool, bool> {
std::vector<std::unordered_map<std::string, std::string>> renames = {{}};
bool tempScope = false;
// Time to track positions of assignments and references to them.
int64_t time = 0;
};
std::shared_ptr<Context> ctx = nullptr;

View File

@ -24,7 +24,7 @@ using namespace types;
/// If the identifier of a generic is fully qualified, use its qualified name
/// (e.g., replace `Ptr` with `Ptr[byte]`).
void TypecheckVisitor::visit(IdExpr *expr) {
auto val = ctx->find(expr->getValue());
auto val = ctx->find(expr->getValue(), getTime());
if (!val) {
E(Error::ID_NOT_FOUND, expr, expr->getValue());
}
@ -61,7 +61,7 @@ void TypecheckVisitor::visit(IdExpr *expr) {
if (expr->hasAttribute(Attr::ExprDominatedUndefCheck)) {
auto controlVar =
fmt::format("{}{}", getUnmangledName(val->canonicalName), VAR_USED_SUFFIX);
if (ctx->find(controlVar)) {
if (ctx->find(controlVar, getTime())) {
auto checkStmt = N<ExprStmt>(N<CallExpr>(
N<DotExpr>(N<IdExpr>("__internal__"), "undef"), N<IdExpr>(controlVar),
N<StringExpr>(getUnmangledName(val->canonicalName))));
@ -295,7 +295,7 @@ TypecheckVisitor::getImport(const std::vector<std::string> &chain) {
TypeContext::Item val = nullptr, importVal = nullptr;
for (auto i = chain.size(); i-- > 0;) {
auto name = join(chain, "/", 0, i + 1);
val = ctx->find(name);
val = ctx->find(name, getTime());
if (val && val->type->is("Import") && startswith(val->getName(), "%_import_")) {
importName = getStrLiteral(val->type.get());
importEnd = i + 1;

View File

@ -202,7 +202,7 @@ Stmt *TypecheckVisitor::transformAssignment(AssignStmt *stmt, bool mustExist) {
if (!e)
E(Error::ASSIGN_INVALID, stmt->getLhs());
auto val = ctx->find(e->getValue());
auto val = ctx->find(e->getValue(), getTime());
// Make sure that existing values that cannot be shadowed are only updated
// mustExist |= val && !ctx->isOuter(val);
if (mustExist) {
@ -237,7 +237,7 @@ Stmt *TypecheckVisitor::transformAssignment(AssignStmt *stmt, bool mustExist) {
// static check)
assign->getLhs()->getType()->getLink()->defaultType =
getStdLibType("NoneType")->shared_from_this();
ctx->getBase()->pendingDefaults.insert(
ctx->getBase()->pendingDefaults[1].insert(
assign->getLhs()->getType()->shared_from_this());
}
if (stmt->getTypeExpr()) {
@ -248,6 +248,7 @@ Stmt *TypecheckVisitor::transformAssignment(AssignStmt *stmt, bool mustExist) {
val = std::make_shared<TypecheckItem>(canonical, ctx->getBaseName(), ctx->getModule(),
assign->getLhs()->getType()->shared_from_this(),
ctx->getScope());
val->time = getTime();
val->setSrcInfo(getSrcInfo());
ctx->add(e->getValue(), val);
ctx->addAlwaysVisible(val);

View File

@ -150,8 +150,10 @@ void TypecheckVisitor::visit(CallExpr *expr) {
? t.getExpr()->getClassType()->name
: t.getExpr()->getType()->prettyString()));
auto argsNice = fmt::format("({})", fmt::join(a, ", "));
E(Error::FN_NO_ATTR_ARGS, expr, getUnmangledName(calleeFn->getFuncName()),
argsNice);
auto name = getUnmangledName(calleeFn->getFuncName());
if (calleeFn->getParentType() && calleeFn->getParentType()->getClass())
name = format("{}.{}", calleeFn->getParentType()->getClass()->niceName, name);
E(Error::FN_NO_ATTR_ARGS, expr, name, argsNice);
}
}
@ -740,11 +742,11 @@ std::pair<bool, Expr *> TypecheckVisitor::transformSpecialCall(CallExpr *expr) {
return {true, transformPtr(expr)};
} else if (val == "__array__.__new__:0") {
return {true, transformArray(expr)};
} else if (val == "isinstance") {
} else if (val == "isinstance") { // static
return {true, transformIsInstance(expr)};
} else if (val == "staticlen") {
} else if (val == "staticlen") { // static
return {true, transformStaticLen(expr)};
} else if (val == "hasattr") {
} else if (val == "hasattr") { // static
return {true, transformHasAttr(expr)};
} else if (val == "getattr") {
return {true, transformGetAttr(expr)};
@ -758,21 +760,21 @@ std::pair<bool, Expr *> TypecheckVisitor::transformSpecialCall(CallExpr *expr) {
return {true, transformRealizedFn(expr)};
} else if (val == "std.internal.static.static_print.0") {
return {false, transformStaticPrintFn(expr)};
} else if (val == "__has_rtti__") {
} else if (val == "__has_rtti__") { // static
return {true, transformHasRttiFn(expr)};
} else if (val == "std.collections.namedtuple.0") {
return {true, transformNamedTuple(expr)};
} else if (val == "std.functools.partial.0:0") {
return {true, transformFunctoolsPartial(expr)};
} else if (val == "std.internal.static.fn_can_call.0") {
} else if (val == "std.internal.static.fn_can_call.0") { // static
return {true, transformStaticFnCanCall(expr)};
} else if (val == "std.internal.static.fn_arg_has_type.0") {
} else if (val == "std.internal.static.fn_arg_has_type.0") { // static
return {true, transformStaticFnArgHasType(expr)};
} else if (val == "std.internal.static.fn_arg_get_type.0") {
return {true, transformStaticFnArgGetType(expr)};
} else if (val == "std.internal.static.fn_args.0") {
return {true, transformStaticFnArgs(expr)};
} else if (val == "std.internal.static.fn_has_default.0") {
} else if (val == "std.internal.static.fn_has_default.0") { // static
return {true, transformStaticFnHasDefault(expr)};
} else if (val == "std.internal.static.fn_get_default.0") {
return {true, transformStaticFnGetDefault(expr)};

View File

@ -63,7 +63,7 @@ void TypecheckVisitor::visit(ClassStmt *stmt) {
// Find the canonical name and AST of the class that is to be extended
if (!ctx->isGlobal() || ctx->isConditional())
E(Error::EXPECTED_TOPLEVEL, getSrcInfo(), "class extension");
auto val = ctx->find(name);
auto val = ctx->find(name, getTime());
if (!val || !val->isType())
E(Error::CLASS_ID_NOT_FOUND, getSrcInfo(), name);
typ = val->getName() == TYPE_TYPE ? val->getType()->getClass()

View File

@ -121,6 +121,29 @@ TypeContext::Item TypeContext::find(const std::string &name) const {
return t;
}
TypeContext::Item TypeContext::find(const std::string &name, int64_t time) const {
auto it = map.find(name);
if (it != map.end()) {
for (auto &i : it->second) {
if (i->getBaseName() != getBaseName() || !time || i->getTime() <= time)
return i;
}
}
// Item is not found in the current module. Time to look in the standard library!
// Note: the standard library items cannot be dominated.
TypeContext::Item t = nullptr;
auto stdlib = cache->imports[STDLIB_IMPORT].ctx;
if (stdlib.get() != this)
t = stdlib->Context<TypecheckItem>::find(name);
// Maybe we are looking for a canonical identifier?
if (!t && cache->typeCtx.get() != this)
t = cache->typeCtx->Context<TypecheckItem>::find(name);
return t;
}
TypeContext::Item TypeContext::forceFind(const std::string &name) const {
auto f = find(name);
seqassert(f, "cannot find '{}'", name);

View File

@ -34,6 +34,10 @@ struct TypecheckItem : public SrcObject {
/// Full base scope information
std::vector<int> scope = {0};
/// Specifies at which time the name was added to the context.
/// Used to prevent using later definitions early (can happen in
/// advanced type checking iterations).
int64_t time = 0;
/// Set if an identifier is a class or a function generic
bool generic = false;
@ -57,6 +61,8 @@ struct TypecheckItem : public SrcObject {
types::Type *getType() const { return type.get(); }
std::string getName() const { return canonicalName; }
int64_t getTime() const { return time; }
};
/** Context class that tracks identifiers during the typechecking. **/
@ -136,7 +142,7 @@ struct TypeContext : public Context<TypecheckItem> {
};
std::vector<Loop> loops;
std::set<types::TypePtr> pendingDefaults;
std::map<int, std::set<types::TypePtr>> pendingDefaults;
public:
Loop *getLoop() { return loops.empty() ? nullptr : &(loops.back()); }
@ -181,6 +187,9 @@ struct TypeContext : public Context<TypecheckItem> {
/// Stack of static loop control variables (used to emulate goto statements).
std::vector<std::string> staticLoops = {};
/// Current statement time.
int64_t time;
public:
explicit TypeContext(Cache *cache, std::string filename = "");
@ -198,6 +207,9 @@ public:
/// Get an item from the context. If the item does not exist, nullptr is returned.
Item find(const std::string &name) const override;
/// Get an item from the context before given srcInfo. If the item does not exist,
/// nullptr is returned.
Item find(const std::string &name, int64_t time) const;
/// Get an item that exists in the context. If the item does not exist, assertion is
/// raised.
Item forceFind(const std::string &name) const;

View File

@ -31,6 +31,7 @@ void TypecheckVisitor::visit(LambdaExpr *expr) {
N<SuiteStmt>(N<ReturnStmt>(expr->getExpr())));
if (auto err = ScopingVisitor::apply(ctx->cache, N<SuiteStmt>(f)))
throw exc::ParserException(std::move(err));
f->setAttribute(Attr::ExprTime, getTime()); // to handle captures properly
f = transform(f);
if (auto a = expr->getAttribute(Attr::Bindings))
f->setAttribute(Attr::Bindings, a->clone());
@ -168,7 +169,7 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) {
rootName = *n;
} else if (stmt->hasAttribute(Attr::Overload)) {
// Case 2: function overload
if (auto c = ctx->find(stmt->getName())) {
if (auto c = ctx->find(stmt->getName(), getTime())) {
if (c->isFunc() && c->getModule() == ctx->getModule() &&
c->getBaseName() == ctx->getBaseName()) {
rootName = c->canonicalName;
@ -196,7 +197,7 @@ void TypecheckVisitor::visit(FunctionStmt *stmt) {
std::map<std::string, TypeContext::Item> captures;
if (auto b = stmt->getAttribute<BindingsAttribute>(Attr::Bindings))
for (auto &[c, t] : b->captures) {
if (auto v = ctx->find(c)) {
if (auto v = ctx->find(c, getTime())) {
if (t != BindingsAttribute::CaptureType::Global && !v->isGlobal()) {
bool parentClassGeneric =
ctx->bases.back().isType() && ctx->bases.back().name == v->getBaseName();
@ -581,7 +582,7 @@ std::pair<bool, std::string> TypecheckVisitor::getDecorator(Expr *e) {
auto dt = transform(clone(e));
auto id = cast<IdExpr>(cast<CallExpr>(dt) ? cast<CallExpr>(dt)->getExpr() : dt);
if (id) {
auto ci = ctx->find(id->getValue());
auto ci = ctx->find(id->getValue(), getTime());
if (ci && ci->isFunc()) {
auto fn = ci->getName();
auto f = getFunction(fn);

View File

@ -109,27 +109,35 @@ Stmt *TypecheckVisitor::inferTypes(Stmt *result, bool isToplevel) {
bool anotherRound = false;
// Special case: return type might have default as well (e.g., Union)
if (auto t = ctx->getBase()->returnType) {
ctx->getBase()->pendingDefaults.insert(t);
ctx->getBase()->pendingDefaults[0].insert(t);
}
for (auto &unbound : ctx->getBase()->pendingDefaults) {
if (auto tu = unbound->getUnion()) {
// Seal all dynamic unions after the iteration is over
if (!tu->isSealed()) {
tu->seal();
anotherRound = true;
}
} else if (auto u = unbound->getLink()) {
types::Type::Unification undo;
if (u->defaultType &&
u->unify(extractClassType(u->defaultType.get()), &undo) >= 0) {
anotherRound = true;
// First unify "explicit" generics (whose default type is explicit),
// then "implicit" ones (whose default type is compiler generated,
// e.g. compiler-generated variable placeholders with default NoneType)
for (auto &[level, unbounds] : ctx->getBase()->pendingDefaults) {
if (!unbounds.empty()) {
for (const auto &unbound : unbounds) {
if (auto tu = unbound->getUnion()) {
// Seal all dynamic unions after the iteration is over
if (!tu->isSealed()) {
tu->seal();
anotherRound = true;
}
} else if (auto u = unbound->getLink()) {
types::Type::Unification undo;
if (u->defaultType &&
u->unify(extractClassType(u->defaultType.get()), &undo) >= 0) {
anotherRound = true;
}
}
}
unbounds.clear();
if (anotherRound)
break;
}
}
ctx->getBase()->pendingDefaults.clear();
if (anotherRound)
continue;
// Nothing helps. Return nullptr.
return nullptr;
}
@ -356,11 +364,21 @@ types::Type *TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) {
E(Error::FN_GLOBAL_NOT_FOUND, getSrcInfo(), "global", c);
}
}
// Add self reference! TODO: maybe remove later when doing contexts?
// Add self [recursive] reference! TODO: maybe remove later when doing contexts?
auto pc = ast->getAttribute<ir::StringValueAttribute>(Attr::ParentClass);
if (!pc || pc->value.empty())
ctx->addFunc(getUnmangledName(ast->getName()), ast->getName(),
ctx->forceFind(ast->getName())->type);
if (!pc || pc->value.empty()) {
// Check if we already exist?
bool exists = false;
auto val = ctx->find(getUnmangledName(ast->getName()));
if (val && val->getType()->getFunc()) {
auto fn = getFunction(val->getType());
exists = fn->rootName == getFunction(type)->rootName;
}
if (!exists) {
ctx->addFunc(getUnmangledName(ast->getName()), ast->getName(),
ctx->forceFind(ast->getName())->type);
}
}
for (size_t i = 0, j = 0; hasAst && i < ast->size(); i++) {
if ((*ast)[i].isValue()) {
auto [_, varName] = (*ast)[i].getNameWithStars();
@ -407,7 +425,8 @@ types::Type *TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) {
// TODO: generalize this further.
for (size_t w = ctx->bases.size(); w-- > 0;)
if (ctx->bases[w].suite)
LOG("[error=> {}] {}", ctx->bases[w].type->debugString(2),
LOG("[error=> {}] {}",
ctx->bases[w].type ? ctx->bases[w].type->debugString(2) : "-",
ctx->bases[w].suite->toString(2));
}
if (!isImport) {

View File

@ -59,6 +59,50 @@ void TypecheckVisitor::visit(UnaryExpr *expr) {
/// Also evaluate static expressions. See @c evaluateStaticBinary for details.
void TypecheckVisitor::visit(BinaryExpr *expr) {
expr->lexpr = transform(expr->getLhs(), true);
// Static short-circuit
if (expr->getLhs()->getType()->isStaticType() && expr->op == "&&") {
if (auto tb = expr->getLhs()->getType()->getBoolStatic()) {
if (!tb->value) {
resultExpr = transform(N<BoolExpr>(false));
return;
}
} else if (auto ts = expr->getLhs()->getType()->getStrStatic()) {
if (ts->value.empty()) {
resultExpr = transform(N<BoolExpr>(false));
return;
}
} else if (auto ti = expr->getLhs()->getType()->getIntStatic()) {
if (!ti->value) {
resultExpr = transform(N<BoolExpr>(false));
return;
}
} else {
expr->getType()->getUnbound()->isStatic = 3;
return;
}
} else if (expr->getLhs()->getType()->isStaticType() && expr->op == "||") {
if (auto tb = expr->getLhs()->getType()->getBoolStatic()) {
if (tb->value) {
resultExpr = transform(N<BoolExpr>(true));
return;
}
} else if (auto ts = expr->getLhs()->getType()->getStrStatic()) {
if (!ts->value.empty()) {
resultExpr = transform(N<BoolExpr>(true));
return;
}
} else if (auto ti = expr->getLhs()->getType()->getIntStatic()) {
if (ti->value) {
resultExpr = transform(N<BoolExpr>(true));
return;
}
} else {
expr->getType()->getUnbound()->isStatic = 3;
return;
}
}
expr->rexpr = transform(expr->getRhs(), true);
static std::unordered_map<int, std::unordered_set<std::string>> staticOps = {

View File

@ -483,7 +483,7 @@ Expr *TypecheckVisitor::transformSuper() {
/// the argument is a variable binding.
Expr *TypecheckVisitor::transformPtr(CallExpr *expr) {
auto id = cast<IdExpr>(expr->begin()->getExpr());
auto val = id ? ctx->find(id->getValue()) : nullptr;
auto val = id ? ctx->find(id->getValue(), getTime()) : nullptr;
if (!val || !val->isVar())
E(Error::CALL_PTR_VAR, expr->begin()->getExpr());
@ -511,6 +511,9 @@ Expr *TypecheckVisitor::transformArray(CallExpr *expr) {
/// `isinstance(obj, ByVal)` is True if `type(obj)` is a tuple type
/// `isinstance(obj, ByRef)` is True if `type(obj)` is a reference type
Expr *TypecheckVisitor::transformIsInstance(CallExpr *expr) {
if (auto u = expr->getType()->getUnbound())
u->isStatic = 3;
expr->begin()->value = transform(expr->begin()->getExpr());
auto typ = expr->begin()->getExpr()->getClassType();
if (!typ || !typ->canRealize())
@ -582,6 +585,9 @@ Expr *TypecheckVisitor::transformIsInstance(CallExpr *expr) {
/// Transform staticlen method to a static integer expression. This method supports only
/// static strings and tuple types.
Expr *TypecheckVisitor::transformStaticLen(CallExpr *expr) {
if (auto u = expr->getType()->getUnbound())
u->isStatic = 1;
expr->begin()->value = transform(expr->begin()->getExpr());
auto typ = extractType(expr->begin()->getExpr());
@ -605,6 +611,9 @@ Expr *TypecheckVisitor::transformStaticLen(CallExpr *expr) {
/// This method also supports additional argument types that are used to check
/// for a matching overload (not available in Python).
Expr *TypecheckVisitor::transformHasAttr(CallExpr *expr) {
if (auto u = expr->getType()->getUnbound())
u->isStatic = 3;
auto typ = extractClassType((*expr)[0].getExpr());
if (!typ)
return nullptr;
@ -763,6 +772,9 @@ Expr *TypecheckVisitor::transformStaticPrintFn(CallExpr *expr) {
/// Transform __has_rtti__ to a static boolean that indicates RTTI status of a type.
Expr *TypecheckVisitor::transformHasRttiFn(CallExpr *expr) {
if (auto u = expr->getType()->getUnbound())
u->isStatic = 3;
auto t = extractFuncGeneric(expr->getExpr()->getType())->getClass();
if (!t)
return nullptr;
@ -771,6 +783,9 @@ Expr *TypecheckVisitor::transformHasRttiFn(CallExpr *expr) {
// Transform internal.static calls
Expr *TypecheckVisitor::transformStaticFnCanCall(CallExpr *expr) {
if (auto u = expr->getType()->getUnbound())
u->isStatic = 3;
auto typ = extractClassType((*expr)[0].getExpr());
if (!typ)
return nullptr;
@ -800,6 +815,9 @@ Expr *TypecheckVisitor::transformStaticFnCanCall(CallExpr *expr) {
}
Expr *TypecheckVisitor::transformStaticFnArgHasType(CallExpr *expr) {
if (auto u = expr->getType()->getUnbound())
u->isStatic = 3;
auto fn = extractFunction(expr->begin()->getExpr()->getType());
if (!fn)
E(Error::CUSTOM, getSrcInfo(), "expected a function, got '{}'",
@ -838,6 +856,9 @@ Expr *TypecheckVisitor::transformStaticFnArgs(CallExpr *expr) {
}
Expr *TypecheckVisitor::transformStaticFnHasDefault(CallExpr *expr) {
if (auto u = expr->getType()->getUnbound())
u->isStatic = 3;
auto fn = extractFunction(expr->begin()->getExpr()->getType());
if (!fn)
E(Error::CUSTOM, getSrcInfo(), "expected a function, got '{}'",

View File

@ -68,7 +68,7 @@ Stmt *TypecheckVisitor::apply(
auto n = tv.inferTypes(suite, true);
if (!n) {
LOG("[error=>] {}", suite->toString(2));
E(Error::CUSTOM, suite, "cannot typecheck the program");
E(Error::CUSTOM, suite->getSrcInfo(), "cannot typecheck the program");
}
suite = tv.N<SuiteStmt>();
@ -275,7 +275,14 @@ Stmt *TypecheckVisitor::transform(Stmt *stmt) {
LOG_TYPECHECK("> [{}] [{}:{}] {}", getSrcInfo(), ctx->getBaseName(),
ctx->getBase()->iteration, stmt->toString(-1));
ctx->pushNode(stmt);
int64_t time = 0;
if (auto a = stmt->getAttribute<ir::IntValueAttribute>(Attr::ExprTime))
time = a->value;
auto oldTime = ctx->time;
ctx->time = time;
stmt->accept(v);
ctx->time = oldTime;
ctx->popNode();
if (v.resultStmt)
stmt = v.resultStmt;
@ -546,10 +553,9 @@ bool TypecheckVisitor::wrapExpr(Expr **expr, Type *expectedType, FuncType *calle
auto [canWrap, newArgTyp, fn] = canWrapExpr((*expr)->getType(), expectedType, callee,
allowUnwrap, cast<EllipsisExpr>(*expr));
// TODO: get rid of this line one day!
if ((*expr)->getType()->getStatic() &&
if ((*expr)->getType()->isStaticType() &&
(!expectedType || !expectedType->isStaticType()))
(*expr)->setType(
(*expr)->getType()->getStatic()->getNonStaticType()->shared_from_this());
(*expr)->setType(getUnderlyingStaticType((*expr)->getType())->shared_from_this());
if (canWrap && fn)
*expr = transform(fn(*expr));
return canWrap;
@ -583,10 +589,14 @@ TypecheckVisitor::canWrapExpr(Type *exprType, Type *expectedType, FuncType *call
std::unordered_set<std::string> hints = {"Generator", "float", TYPE_OPTIONAL,
"pyobj"};
if (exprType->getStatic() && (!expectedType || !expectedType->isStaticType())) {
exprType = exprType->getStatic()->getNonStaticType();
exprClass = exprType->getClass();
if (!expectedType || !expectedType->isStaticType()) {
if (auto c = exprType->isStaticType()) {
exprType = getUnderlyingStaticType(exprType);
exprClass = exprType->getClass();
type = exprType->shared_from_this();
}
}
if (!exprClass && expectedClass && in(hints, expectedClass->name)) {
return {false, nullptr, nullptr}; // argument type not yet known.
}
@ -630,8 +640,10 @@ TypecheckVisitor::canWrapExpr(Type *exprType, Type *expectedType, FuncType *call
return N<CallExpr>(N<IdExpr>("pyobj"),
N<CallExpr>(N<DotExpr>(expr, "__to_py__")));
};
} else if (allowUnwrap && expectedClass && exprClass && exprClass->is("pyobj") &&
!exprClass->is(expectedClass->name)) { // unwrap pyobj
}
else if (allowUnwrap && expectedClass && exprClass && exprClass->is("pyobj") &&
!exprClass->is(expectedClass->name)) { // unwrap pyobj
if (findMethod(expectedClass, "__from_py__").empty())
return {false, nullptr, nullptr};
type = instantiateType(expectedClass);
@ -692,7 +704,9 @@ TypecheckVisitor::canWrapExpr(Type *exprType, Type *expectedType, FuncType *call
} else {
return {false, nullptr, nullptr};
}
} else if (exprClass && expectedClass && expectedClass->getUnion()) {
}
else if (exprClass && expectedClass && expectedClass->getUnion()) {
// Make union types via __internal__.new_union
if (!expectedClass->getUnion()->isSealed()) {
if (!expectedClass->getUnion()->addType(exprClass))
@ -1072,6 +1086,22 @@ bool TypecheckVisitor::isImportFn(const std::string &s) {
return startswith(s, "%_import_");
}
int64_t TypecheckVisitor::getTime() { return ctx->time; }
types::Type *TypecheckVisitor::getUnderlyingStaticType(types::Type *t) {
if (t->getStatic()) {
return t->getStatic()->getNonStaticType();
} else if (auto c = t->isStaticType()) {
if (c == 1)
return getStdLibType("int");
if (c == 2)
return getStdLibType("str");
if (c == 3)
return getStdLibType("bool");
}
return t;
}
std::shared_ptr<types::LinkType>
TypecheckVisitor::instantiateUnbound(const SrcInfo &srcInfo, int level) const {
auto typ = std::make_shared<types::LinkType>(
@ -1112,7 +1142,7 @@ types::TypePtr TypecheckVisitor::instantiateType(const SrcInfo &srcInfo,
if (auto l = i.second->getLink()) {
i.second->setSrcInfo(srcInfo);
if (l->defaultType) {
ctx->getBase()->pendingDefaults.insert(i.second);
ctx->getBase()->pendingDefaults[0].insert(i.second);
}
}
}
@ -1164,7 +1194,7 @@ types::TypePtr TypecheckVisitor::instantiateType(const SrcInfo &srcInfo,
}
if (t->getUnion() && !t->getUnion()->isSealed()) {
t->setSrcInfo(srcInfo);
ctx->getBase()->pendingDefaults.insert(t);
ctx->getBase()->pendingDefaults[0].insert(t);
}
return t;
}

View File

@ -346,6 +346,8 @@ public:
std::string getClassMethod(types::Type *typ, const std::string &member);
std::string getTemporaryVar(const std::string &s);
bool isImportFn(const std::string &s);
int64_t getTime();
types::Type *getUnderlyingStaticType(types::Type *t);
int64_t getIntLiteral(types::Type *t, size_t pos = 0);
bool getBoolLiteral(types::Type *t, size_t pos = 0);

View File

@ -431,6 +431,21 @@ fox(1, 2)
fox(1, 2, 3)
#: fox 1: 1 2 3
# Test whether recursive self references override overloads (they shouldn't)
def arange(start: int, stop: int, step: int):
return (start, stop, step)
@overload
def arange(stop: int):
return arange(0, stop, 1)
print(arange(0, 1, 2))
#: (0, 1, 2)
print(arange(12))
#: (0, 12, 1)
#%% fn_shadow,barebones
def foo(x):
return 1, x

View File

@ -421,6 +421,13 @@ foo2(s[10:50]) #: kl True
foo2(s[1:30:3]) #: behk True
foo2(s[::-1]) #: lkjihgfedcba True
#%% static_short_circuit,barebones
x = 3.14
if isinstance(x, List) and x.T is float:
print('is list')
else:
print('not list') #: not list
#%% partial_star_pipe_args,barebones
iter(['A', 'C']) |> print
#: A

View File

@ -40,3 +40,29 @@ def foo(x: List[1]): pass #! expected type expression
a = 5; b = 3
print a, b #: 5 3
#%% delayed_instantiation_correct_context,barebones
# Test timing of the statements; ensure that delayed blocks still
# use correct names.
def foo():
l = []
s = 1 # CH1
if isinstance(l, List[int]): # delay typechecking this block
print(s) #: 1
# if this is done badly, this print will print 's'
# or result in assertion error
print(s) #: 1
s = 's' # CH2
print(s) #: s
# instantiate l so that the block above
# is typechecked in the next iteration
l.append(1)
foo()
# check that this does not mess up comprehensions
# (where variable names are used BEFORE their declaration)
slice_prefixes = [(start, end)
for start, end in [(1, 2), (3, 4)]]
print(slice_prefixes) #: [(1, 2), (3, 4)]