New error handling via llvm::Error [wip]

typecheck-v2
Ibrahim Numanagić 2024-12-07 11:49:53 -08:00
parent ecaa8d906e
commit 3446d5e58f
38 changed files with 1009 additions and 970 deletions

View File

@ -64,16 +64,18 @@ std::string makeOutputFilename(const std::string &filename,
void display(const codon::error::ParserErrorInfo &e) {
using codon::MessageGroupPos;
for (auto &group : e) {
for (auto &group : e.getErrors()) {
int i = 0;
for (auto &msg : group) {
MessageGroupPos pos = MessageGroupPos::NONE;
if (&msg == &group.front()) {
if (i == 0) {
pos = MessageGroupPos::HEAD;
} else if (&msg == &group.back()) {
} else if (i == group.size() - 1) {
pos = MessageGroupPos::LAST;
} else {
pos = MessageGroupPos::MID;
}
i++;
codon::compilationError(msg.getMessage(), msg.getFile(), msg.getLine(),
msg.getColumn(), msg.getLength(), msg.getErrorCode(),
/*terminate=*/false, pos);

View File

@ -173,9 +173,10 @@ Func *Module::getOrRealizeMethod(types::Type *parent, const std::string &methodN
return cache->realizeFunction(method, translateArgs(cache, args),
translateGenerics(cache, generics), cls);
} catch (const exc::ParserException &e) {
for (int i = 0; i < e.messages.size(); i++)
LOG_IR("getOrRealizeMethod parser error at {}: {}", e.locations[i],
e.messages[i]);
for (auto &trace : e.getErrors())
for (auto &msg : trace)
LOG_IR("getOrRealizeMethod parser error at {}: {}", msg.getSrcInfo(),
msg.getMessage());
return nullptr;
}
}
@ -196,8 +197,10 @@ Func *Module::getOrRealizeFunc(const std::string &funcName,
try {
return cache->realizeFunction(func, arg, gens);
} catch (const exc::ParserException &e) {
for (int i = 0; i < e.messages.size(); i++)
LOG("getOrRealizeFunc parser error at {}: {}", e.locations[i], e.messages[i]);
for (auto &trace : e.getErrors())
for (auto &msg : trace)
LOG("getOrRealizeFunc parser error at {}: {}", msg.getSrcInfo(),
msg.getMessage());
return nullptr;
}
}
@ -213,8 +216,10 @@ types::Type *Module::getOrRealizeType(const std::string &typeName,
try {
return cache->realizeType(type, translateGenerics(cache, generics));
} catch (const exc::ParserException &e) {
for (int i = 0; i < e.messages.size(); i++)
LOG_IR("getOrRealizeType parser error at {}: {}", e.locations[i], e.messages[i]);
for (auto &trace : e.getErrors())
for (auto &msg : trace)
LOG_IR("getOrRealizeType parser error at {}: {}", msg.getSrcInfo(),
msg.getMessage());
return nullptr;
}
}

View File

@ -73,16 +73,18 @@ Compiler::parse(bool isCode, const std::string &file, const std::string &code,
input = file;
std::string abspath = (file != "-") ? ast::getAbsolutePath(file) : file;
try {
ast::Stmt *codeStmt = isCode ? ast::parseCode(cache.get(), abspath, code, startLine)
: ast::parseFile(cache.get(), abspath);
auto nodeOrErr = isCode ? ast::parseCode(cache.get(), abspath, code, startLine)
: ast::parseFile(cache.get(), abspath);
if (!nodeOrErr)
throw exc::ParserException(nodeOrErr.takeError());
auto codeStmt = *nodeOrErr;
cache->module0 = file;
Timer t2("typecheck");
t2.logged = true;
auto typechecked =
ast::TypecheckVisitor::apply(cache.get(), std::move(codeStmt), abspath, defines,
getEarlyDefines(), (testFlags > 1));
auto typechecked = ast::TypecheckVisitor::apply(
cache.get(), codeStmt, abspath, defines, getEarlyDefines(), (testFlags > 1));
LOG_TIME("[T] parse = {:.1f}", totalPeg);
LOG_TIME("[T] typecheck = {:.1f}", t2.elapsed() - totalPeg);
@ -115,24 +117,7 @@ Compiler::parse(bool isCode, const std::string &file, const std::string &code,
ast::TranslateVisitor::apply(cache.get(), std::move(typechecked));
t4.log();
} catch (const exc::ParserException &exc) {
std::vector<error::Message> messages;
if (exc.messages.empty()) {
const int MAX_ERRORS = 5;
int ei = 0;
for (auto &e : cache->errors) {
for (unsigned i = 0; i < e.messages.size(); i++) {
if (!e.messages[i].empty())
messages.emplace_back(e.messages[i], e.locations[i].file,
e.locations[i].line, e.locations[i].col,
e.locations[i].len, e.errorCode);
}
if (ei++ > MAX_ERRORS)
break;
}
return llvm::make_error<error::ParserErrorInfo>(messages);
} else {
return llvm::make_error<error::ParserErrorInfo>(exc);
}
return llvm::make_error<error::ParserErrorInfo>(exc.getErrors());
}
module->setSrcInfo({abspath, 0, 0, 0});
if (codon::getLogger().flags & codon::Logger::FLAG_USER) {
@ -181,8 +166,8 @@ llvm::Expected<std::string> Compiler::docgen(const std::vector<std::string> &fil
try {
auto j = ast::DocVisitor::apply(argv0, files);
return j->toString();
} catch (exc::ParserException &e) {
return llvm::make_error<error::ParserErrorInfo>(e);
} catch (exc::ParserException &exc) {
return llvm::make_error<error::ParserErrorInfo>(exc.getErrors());
}
}

View File

@ -26,15 +26,16 @@ char PluginErrorInfo::ID = 0;
char IOErrorInfo::ID = 0;
void raise_error(const char *format) { throw exc::ParserException(format); }
void raise_error(int e, const ::codon::SrcInfo &info, const char *format) {
throw exc::ParserException(e, format, info);
}
void raise_error(int e, const ::codon::SrcInfo &info, const std::string &format) {
throw exc::ParserException(e, format, info);
}
void E(llvm::Error &&error) { throw exc::ParserException(std::move(error)); }
} // namespace error
namespace exc {
ParserException::ParserException(llvm::Error &&e) noexcept : std::runtime_error("") {
llvm::handleAllErrors(std::move(e), [this](const error::ParserErrorInfo &e) {
errors = e.getErrors();
});
}
} // namespace exc
} // namespace codon

View File

@ -12,153 +12,6 @@
namespace codon {
namespace error {
class Message {
private:
std::string msg;
std::string file;
int line = 0;
int col = 0;
int len = 0;
int errorCode = -1;
public:
explicit Message(const std::string &msg, const std::string &file = "", int line = 0,
int col = 0, int len = 0, int errorCode = -1)
: msg(msg), file(file), line(line), col(col), len(len), errorCode(-1) {}
std::string getMessage() const { return msg; }
std::string getFile() const { return file; }
int getLine() const { return line; }
int getColumn() const { return col; }
int getLength() const { return len; }
int getErrorCode() const { return errorCode; }
void log(llvm::raw_ostream &out) const {
if (!getFile().empty()) {
out << getFile();
if (getLine() != 0) {
out << ":" << getLine();
if (getColumn() != 0) {
out << ":" << getColumn();
}
}
out << ": ";
}
out << getMessage();
}
};
class ParserErrorInfo : public llvm::ErrorInfo<ParserErrorInfo> {
private:
std::vector<std::vector<Message>> messages;
public:
explicit ParserErrorInfo(const std::vector<Message> &m) : messages() {
for (auto &msg : m) {
messages.push_back({msg});
}
}
explicit ParserErrorInfo(const exc::ParserException &e) : messages() {
std::vector<Message> group;
for (unsigned i = 0; i < e.messages.size(); i++) {
if (!e.messages[i].empty())
group.emplace_back(e.messages[i], e.locations[i].file, e.locations[i].line,
e.locations[i].col, e.locations[i].len);
}
messages.push_back(group);
}
auto begin() { return messages.begin(); }
auto end() { return messages.end(); }
auto begin() const { return messages.begin(); }
auto end() const { return messages.end(); }
void log(llvm::raw_ostream &out) const override {
for (auto &group : messages) {
for (auto &msg : group) {
msg.log(out);
out << "\n";
}
}
}
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
static char ID;
};
class RuntimeErrorInfo : public llvm::ErrorInfo<RuntimeErrorInfo> {
private:
std::string output;
std::string type;
Message message;
std::vector<std::string> backtrace;
public:
RuntimeErrorInfo(const std::string &output, const std::string &type,
const std::string &msg, const std::string &file = "", int line = 0,
int col = 0, std::vector<std::string> backtrace = {})
: output(output), type(type), message(msg, file, line, col),
backtrace(std::move(backtrace)) {}
std::string getOutput() const { return output; }
std::string getType() const { return type; }
std::string getMessage() const { return message.getMessage(); }
std::string getFile() const { return message.getFile(); }
int getLine() const { return message.getLine(); }
int getColumn() const { return message.getColumn(); }
std::vector<std::string> getBacktrace() const { return backtrace; }
void log(llvm::raw_ostream &out) const override {
out << type << ": ";
message.log(out);
}
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
static char ID;
};
class PluginErrorInfo : public llvm::ErrorInfo<PluginErrorInfo> {
private:
std::string message;
public:
explicit PluginErrorInfo(const std::string &message) : message(message) {}
std::string getMessage() const { return message; }
void log(llvm::raw_ostream &out) const override { out << message; }
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
static char ID;
};
class IOErrorInfo : public llvm::ErrorInfo<IOErrorInfo> {
private:
std::string message;
public:
explicit IOErrorInfo(const std::string &message) : message(message) {}
std::string getMessage() const { return message; }
void log(llvm::raw_ostream &out) const override { out << message; }
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
static char ID;
};
enum Error {
CALL_NAME_ORDER,
CALL_NAME_STAR,
@ -261,6 +114,111 @@ enum Error {
__END__
};
class ParserErrorInfo : public llvm::ErrorInfo<ParserErrorInfo> {
private:
ParserErrors errors;
public:
static char ID;
public:
explicit ParserErrorInfo(const ErrorMessage &msg) : errors(msg) {}
explicit ParserErrorInfo(const std::vector<ErrorMessage> &msgs) : errors(msgs) {}
explicit ParserErrorInfo(const ParserErrors &errors) : errors(errors) {}
template <class... TA>
ParserErrorInfo(error::Error e, const codon::SrcInfo &o = codon::SrcInfo(), const TA &...args) {
auto msg = Emsg(e, args...);
errors = ParserErrors(ErrorMessage(msg, o, (int)e));
}
const ParserErrors &getErrors() const { return errors; }
ParserErrors &getErrors() { return errors; }
void log(llvm::raw_ostream &out) const override {
for (const auto &trace : errors) {
for (const auto &msg : trace.getMessages()) {
msg.log(out);
out << "\n";
}
}
}
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
};
class RuntimeErrorInfo : public llvm::ErrorInfo<RuntimeErrorInfo> {
private:
std::string output;
std::string type;
ErrorMessage message;
std::vector<std::string> backtrace;
public:
RuntimeErrorInfo(const std::string &output, const std::string &type,
const std::string &msg, const std::string &file = "", int line = 0,
int col = 0, std::vector<std::string> backtrace = {})
: output(output), type(type), message(msg, file, line, col),
backtrace(std::move(backtrace)) {}
std::string getOutput() const { return output; }
std::string getType() const { return type; }
std::string getMessage() const { return message.getMessage(); }
std::string getFile() const { return message.getFile(); }
int getLine() const { return message.getLine(); }
int getColumn() const { return message.getColumn(); }
std::vector<std::string> getBacktrace() const { return backtrace; }
void log(llvm::raw_ostream &out) const override {
out << type << ": ";
message.log(out);
}
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
static char ID;
};
class PluginErrorInfo : public llvm::ErrorInfo<PluginErrorInfo> {
private:
std::string message;
public:
explicit PluginErrorInfo(const std::string &message) : message(message) {}
std::string getMessage() const { return message; }
void log(llvm::raw_ostream &out) const override { out << message; }
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
static char ID;
};
class IOErrorInfo : public llvm::ErrorInfo<IOErrorInfo> {
private:
std::string message;
public:
explicit IOErrorInfo(const std::string &message) : message(message) {}
std::string getMessage() const { return message; }
void log(llvm::raw_ostream &out) const override { out << message; }
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
static char ID;
};
template <class... TA> std::string Emsg(Error e, const TA &...args) {
switch (e) {
/// Validations
@ -489,17 +447,14 @@ template <class... TA> std::string Emsg(Error e, const TA &...args) {
}
}
/// Raise a parsing error.
void raise_error(const char *format);
/// Raise a parsing error at a source location p.
void raise_error(int e, const codon::SrcInfo &info, const char *format);
void raise_error(int e, const codon::SrcInfo &info, const std::string &format);
template <class... TA>
void E(Error e, const codon::SrcInfo &o = codon::SrcInfo(), const TA &...args) {
auto msg = Emsg(e, args...);
raise_error((int)e, o, msg);
auto err = ParserErrors(ErrorMessage(msg, o, (int)e));
throw exc::ParserException(err);
}
void E(llvm::Error &&error);
} // namespace error
} // namespace codon

View File

@ -90,11 +90,16 @@ llvm::Expected<ir::Func *> JIT::compile(const std::string &code,
ast::TypeContext bType = *(cache->typeCtx);
ast::TranslateContext bTranslate = *(cache->codegenCtx);
try {
ast::Stmt *node = ast::parseCode(cache, file.empty() ? JIT_FILENAME : file, code,
/*startLine=*/line);
auto nodeOrErr = ast::parseCode(cache, file.empty() ? JIT_FILENAME : file, code,
/*startLine=*/line);
if (!nodeOrErr)
throw exc::ParserException(nodeOrErr.takeError());
auto *node = *nodeOrErr;
ast::Stmt **e = &node;
while (auto se = ast::cast<ast::SuiteStmt>(*e)) {
if (se->empty()) break;
if (se->empty())
break;
e = &se->back();
}
if (e)
@ -104,11 +109,12 @@ llvm::Expected<ir::Func *> JIT::compile(const std::string &code,
cache->N<ast::StringExpr>(mode)));
}
auto tv = ast::TypecheckVisitor(sctx, preamble);
ast::ScopingVisitor::apply(sctx->cache, node);
if (auto err = ast::ScopingVisitor::apply(sctx->cache, node))
throw exc::ParserException(std::move(err));
node = tv.transform(node);
if (!cache->errors.empty())
throw exc::ParserException();
throw exc::ParserException(cache->errors);
auto typechecked = cache->N<ast::SuiteStmt>();
for (auto &s : *preamble)
typechecked->addStmt(s);
@ -128,18 +134,6 @@ llvm::Expected<ir::Func *> JIT::compile(const std::string &code,
return func;
} catch (const exc::ParserException &exc) {
std::vector<error::Message> messages;
if (exc.messages.empty()) {
for (auto &e : cache->errors) {
for (unsigned i = 0; i < e.messages.size(); i++) {
if (!e.messages[i].empty())
messages.emplace_back(e.messages[i], e.locations[i].file,
e.locations[i].line, e.locations[i].col,
e.locations[i].len, e.errorCode);
}
}
}
for (auto &f : cache->functions)
for (auto &r : f.second.realizations)
if (!(in(bCache.functions, f.first) &&
@ -153,10 +147,7 @@ llvm::Expected<ir::Func *> JIT::compile(const std::string &code,
*(cache->typeCtx) = bType;
*(cache->codegenCtx) = bTranslate;
if (exc.messages.empty())
return llvm::make_error<error::ParserErrorInfo>(messages);
else
return llvm::make_error<error::ParserErrorInfo>(exc);
return llvm::make_error<error::ParserErrorInfo>(exc.getErrors());
}
}

View File

@ -21,6 +21,10 @@ const std::string Attr::HiddenFromUser = "__hidden__";
const std::string Attr::ForceRealize = "__force__";
const std::string Attr::RealizeWithoutSelf =
"std.internal.attributes.realize_without_self.0:0";
const std::string Attr::ParentCallExpr = "parentCallExpr";
const std::string Attr::TupleCall = "tupleFn";
const std::string Attr::Validated = "validated";
const std::string Attr::AutoGenerated = "autogenerated";
const std::string Attr::CVarArg = ".__vararg__";
const std::string Attr::Method = ".__method__";
@ -36,6 +40,8 @@ const std::string Attr::ClassNoTuple = "__notuple__";
const std::string Attr::Test = "std.internal.attributes.test.0:0";
const std::string Attr::Overload = "overload:0";
const std::string Attr::Export = "std.internal.attributes.export.0:0";
const std::string Attr::Inline = "std.internal.attributes.inline.0:0";
const std::string Attr::NoArgReorder = "std.internal.attributes.no_arg_reorder.0:0";
const std::string Attr::ClassMagic = "classMagic";
const std::string Attr::ExprSequenceItem = "exprSequenceItem";

View File

@ -26,6 +26,10 @@ struct Attr {
const static std::string HiddenFromUser;
const static std::string ForceRealize;
const static std::string RealizeWithoutSelf; // not internal
const static std::string ParentCallExpr;
const static std::string TupleCall;
const static std::string Validated;
const static std::string AutoGenerated;
// Compiler-generated attributes
const static std::string CVarArg;
const static std::string Method;
@ -41,6 +45,8 @@ struct Attr {
const static std::string Test;
const static std::string Overload;
const static std::string Export;
const static std::string Inline;
const static std::string NoArgReorder;
// Expression-related attributes
const static std::string ClassMagic;
const static std::string ExprSequenceItem;

View File

@ -2,6 +2,7 @@
#pragma once
#include "llvm/Support/Error.h"
#include <stdexcept>
#include <string>
#include <vector>
@ -28,6 +29,95 @@ struct SrcInfo {
bool operator==(const SrcInfo &src) const;
};
class ErrorMessage {
private:
std::string msg;
SrcInfo loc;
int errorCode = -1;
public:
explicit ErrorMessage(const std::string &msg, const SrcInfo &loc = SrcInfo(),
int errorCode = -1)
: msg(msg), loc(loc), errorCode(-1) {}
explicit ErrorMessage(const std::string &msg, const std::string &file = "",
int line = 0, int col = 0, int len = 0, int errorCode = -1)
: msg(msg), loc(file, line, col, len), errorCode(-1) {}
std::string getMessage() const { return msg; }
std::string getFile() const { return loc.file; }
int getLine() const { return loc.line; }
int getColumn() const { return loc.col; }
int getLength() const { return loc.len; }
int getErrorCode() const { return errorCode; }
SrcInfo getSrcInfo() const { return loc; }
void setSrcInfo(const SrcInfo &s) { loc = s; }
void log(llvm::raw_ostream &out) const {
if (!getFile().empty()) {
out << getFile();
if (getLine() != 0) {
out << ":" << getLine();
if (getColumn() != 0) {
out << ":" << getColumn();
}
}
out << ": ";
}
out << getMessage();
}
};
struct ParserErrors {
struct Backtrace {
std::vector<ErrorMessage> trace;
const std::vector<ErrorMessage> &getMessages() const { return trace; }
auto begin() const { return trace.begin(); }
auto front() const { return trace.front(); }
auto front() { return trace.front(); }
auto end() const { return trace.end(); }
auto back() { return trace.back(); }
auto back() const { return trace.back(); }
auto size() const { return trace.size(); }
void addMessage(const std::string &msg, const SrcInfo &info = SrcInfo()) {
trace.emplace_back(msg, info);
}
};
std::vector<Backtrace> errors;
ParserErrors() {}
ParserErrors(const ErrorMessage &msg) : errors{Backtrace{{msg}}} {}
ParserErrors(const std::string &msg, const SrcInfo &info)
: ParserErrors({msg, info}) {}
ParserErrors(const std::string &msg) : ParserErrors(msg, {}) {}
ParserErrors(const ParserErrors &e) : errors(e.errors) {}
ParserErrors(const std::vector<ErrorMessage> &m) : ParserErrors() {
for (auto &msg : m)
errors.push_back(Backtrace{{msg}});
}
auto begin() { return errors.begin(); }
auto end() { return errors.end(); }
auto begin() const { return errors.begin(); }
auto end() const { return errors.end(); }
auto empty() const { return errors.empty(); }
auto size() const { return errors.size(); }
void append(const ParserErrors &e) {
errors.insert(errors.end(), e.errors.begin(), e.errors.end());
}
Backtrace &getLast() {
assert(!empty() && "empty error trace");
return errors.back();
}
/// Add an error message to the current backtrace
void addError(const std::vector<ErrorMessage> &trace) { errors.push_back({trace}); }
std::string getMessage() const {
if (empty())
return "";
return errors.front().trace.front().getMessage();
}
};
} // namespace codon
namespace codon::exc {
@ -37,38 +127,16 @@ namespace codon::exc {
* Used for parsing, transformation and type-checking errors.
*/
class ParserException : public std::runtime_error {
public:
/// These vectors (stacks) store an error stack-trace.
std::vector<SrcInfo> locations;
std::vector<std::string> messages;
int errorCode = -1;
ParserErrors errors;
public:
ParserException(int errorCode, const std::string &msg, const SrcInfo &info) noexcept
: std::runtime_error(msg), errorCode(errorCode) {
messages.push_back(msg);
locations.push_back(info);
}
ParserException() noexcept : std::runtime_error("") {}
ParserException(int errorCode, const std::string &msg) noexcept
: ParserException(errorCode, msg, {}) {}
explicit ParserException(const std::string &msg) noexcept
: ParserException(-1, msg, {}) {}
ParserException(const ParserException &e) noexcept
: std::runtime_error(e), locations(e.locations), messages(e.messages),
errorCode(e.errorCode) {}
ParserException(const ParserErrors &errors) noexcept
: std::runtime_error(errors.getMessage()), errors(errors) {}
ParserException(llvm::Error &&e) noexcept;
/// Add an error message to the current stack trace
void trackRealize(const std::string &msg, const SrcInfo &info) {
locations.push_back(info);
messages.push_back("during the realization of " + msg);
}
/// Add an error message to the current stack trace
void track(const std::string &msg, const SrcInfo &info) {
locations.push_back(info);
messages.push_back(msg);
}
ParserErrors getErrors() const { return errors; }
};
} // namespace codon::exc

View File

@ -39,7 +39,6 @@ Expr::Expr(const Expr &expr, bool clean) : Expr(expr) {
done = false;
}
}
void Expr::validate() const {}
types::ClassType *Expr::getClassType() const {
return type ? type->getClass() : nullptr;
}
@ -51,13 +50,13 @@ std::string Expr::wrapType(const std::string &sexpr) const {
type && !done ? format(" #:type \"{}\"", type->debugString(2)) : "");
return s;
}
Expr *Expr::operator<<(types::Type *t) {
seqassert(type, "lhs is nullptr");
if ((*type) << t) {
E(Error::TYPE_UNIFY, getSrcInfo(), type->prettyString(), t->prettyString());
}
return this;
}
// llvm::Expected<Expr> *Expr::operator<<(types::Type *t) {
// seqassert(type, "lhs is nullptr");
// if ((*type) << t) {
// E(Error::TYPE_UNIFY, getSrcInfo(), type->prettyString(), t->prettyString());
// }
// return this;
// }
Param::Param(std::string name, Expr *type, Expr *defaultValue, int status)
: name(std::move(name)), type(type), defaultValue(defaultValue) {
@ -162,9 +161,7 @@ std::string FloatExpr::toString(int) const {
}
StringExpr::StringExpr(std::vector<StringExpr::String> s)
: AcceptorExtend(), strings(std::move(s)) {
unpack();
}
: AcceptorExtend(), strings(std::move(s)) {}
StringExpr::StringExpr(std::string value, std::string prefix)
: StringExpr(std::vector<StringExpr::String>{{value, prefix}}) {}
StringExpr::StringExpr(const StringExpr &expr, bool clean)
@ -186,60 +183,6 @@ std::string StringExpr::getValue() const {
bool StringExpr::isSimple() const {
return strings.size() == 1 && strings[0].prefix.empty();
}
void StringExpr::unpack() {
std::vector<String> exprs;
for (auto &p : strings) {
if (p.prefix == "f" || p.prefix == "F") {
/// Transform an F-string
for (auto pf : unpackFString(p.value)) {
if (pf.prefix.empty() && !exprs.empty() && exprs.back().prefix.empty()) {
exprs.back().value += pf.value;
} else {
exprs.emplace_back(pf);
}
}
} else if (!p.prefix.empty()) {
exprs.emplace_back(p);
} else if (!exprs.empty() && exprs.back().prefix.empty()) {
exprs.back().value += p.value;
} else {
exprs.emplace_back(p);
}
}
strings = exprs;
}
std::vector<StringExpr::String>
StringExpr::unpackFString(const std::string &value) const {
// Strings to be concatenated
std::vector<StringExpr::String> items;
int braceCount = 0, braceStart = 0;
for (int i = 0; i < value.size(); i++) {
if (value[i] == '{') {
if (braceStart < i)
items.emplace_back(value.substr(braceStart, i - braceStart));
if (!braceCount)
braceStart = i + 1;
braceCount++;
} else if (value[i] == '}') {
braceCount--;
if (!braceCount) {
std::string code = value.substr(braceStart, i - braceStart);
auto offset = getSrcInfo();
offset.col += i;
items.emplace_back(code, "#f");
items.back().setSrcInfo(offset);
}
braceStart = i + 1;
}
}
if (braceCount > 0)
E(Error::STR_FSTRING_BALANCE_EXTRA, getSrcInfo());
if (braceCount < 0)
E(Error::STR_FSTRING_BALANCE_MISSING, getSrcInfo());
if (braceStart != value.size())
items.emplace_back(value.substr(braceStart, value.size() - braceStart));
return items;
}
IdExpr::IdExpr(std::string value) : AcceptorExtend(), value(std::move(value)) {}
IdExpr::IdExpr(const IdExpr &expr, bool clean)
@ -447,7 +390,6 @@ PipeExpr::PipeExpr(std::vector<Pipe> items)
PipeExpr::PipeExpr(const PipeExpr &expr, bool clean)
: AcceptorExtend(expr, clean), Items(ast::clone(expr.items, clean)),
inTypes(expr.inTypes) {}
void PipeExpr::validate() const {}
std::string PipeExpr::toString(int indent) const {
std::vector<std::string> s;
for (auto &i : items)
@ -483,28 +425,12 @@ CallExpr::CallExpr(const CallExpr &expr, bool clean)
CallExpr::CallExpr(Expr *expr, std::vector<CallArg> args)
: AcceptorExtend(), Items(std::move(args)), expr(expr), ordered(false),
partial(false) {
validate();
}
CallExpr::CallExpr(Expr *expr, std::vector<Expr *> args)
: AcceptorExtend(), Items({}), expr(expr), ordered(false), partial(false) {
for (auto a : args)
if (a)
items.emplace_back("", a);
validate();
}
void CallExpr::validate() const {
bool namesStarted = false, foundEllipsis = false;
for (auto &a : *this) {
if (a.name.empty() && namesStarted &&
!(cast<KeywordStarExpr>(a.value) || cast<EllipsisExpr>(a.value)))
E(Error::CALL_NAME_ORDER, a.value);
if (!a.name.empty() && (cast<StarExpr>(a.value) || cast<KeywordStarExpr>(a.value)))
E(Error::CALL_NAME_STAR, a.value);
if (cast<EllipsisExpr>(a.value) && foundEllipsis)
E(Error::CALL_ELLIPSIS, a.value);
foundEllipsis |= bool(cast<EllipsisExpr>(a.value));
namesStarted |= !a.name.empty();
}
}
std::string CallExpr::toString(int indent) const {
std::vector<std::string> s;

View File

@ -52,8 +52,6 @@ struct Expr : public AcceptorExtend<Expr, ASTNode> {
void setDone() { done = true; }
Expr *getOrigExpr() const { return origExpr; }
void setOrigExpr(Expr *orig) { origExpr = orig; }
/// Validate a node. Throw ParseASTException if a node is not valid.
void validate() const;
static const char NodeId;
SERIALIZE(Expr, BASE(ASTNode), /*type,*/ done, origExpr);
@ -211,12 +209,6 @@ struct StringExpr : public AcceptorExtend<StringExpr, Expr> {
private:
std::vector<String> strings;
void unpack();
/// Split a Python-like f-string into a list:
/// `f"foo {x+1} bar"` -> `["foo ", str(x+1), " bar"]
/// Supports "{x=}" specifier (that prints the raw expression as well):
/// `f"{x+1=}"` -> `["x+1=", str(x+1)]`
std::vector<String> unpackFString(const std::string &) const;
auto begin() { return strings.begin(); }
auto end() { return strings.end(); }
@ -430,8 +422,6 @@ private:
/// Output type of a "prefix" pipe ending at the index position.
/// Example: for a |> b |> c, inTypes[1] is typeof(a |> b).
std::vector<types::TypePtr> inTypes;
void validate() const;
};
/// Index expression (expr[index]).
@ -489,8 +479,6 @@ private:
bool ordered;
/// True if the call is partial
bool partial = false;
void validate() const;
};
/// Dot (access) expression (expr.member).

View File

@ -96,89 +96,6 @@ std::string AssignStmt::toString(int indent) const {
type ? format(" #:type {}", type->toString(indent)) : "");
}
/// Unpack an assignment expression `lhs = rhs` into a list of simple assignment
/// expressions (e.g., `a = b`, `a.x = b`, or `a[x] = b`).
/// Handle Python unpacking rules.
/// @example
/// `(a, b) = c` -> `a = c[0]; b = c[1]`
/// `a, b = c` -> `a = c[0]; b = c[1]`
/// `[a, *x, b] = c` -> `a = c[0]; x = c[1:-1]; b = c[-1]`.
/// Non-trivial right-hand expressions are first stored in a temporary variable.
/// @example
/// `a, b = c, d + foo()` -> `assign = (c, d + foo); a = assign[0]; b = assign[1]`.
/// Each assignment is unpacked recursively to allow cases like `a, (b, c) = d`.
Stmt *AssignStmt::unpack() const {
std::vector<Expr *> leftSide;
if (auto et = cast<TupleExpr>(lhs)) {
// Case: (a, b) = ...
for (auto *i : *et)
leftSide.push_back(i);
} else if (auto el = cast<ListExpr>(lhs)) {
// Case: [a, b] = ...
for (auto *i : *el)
leftSide.push_back(i);
} else {
// Case: simple assignment (a = b, a.x = b, or a[x] = b)
return cache->NS<AssignStmt>(this, lhs, rhs, type);
}
// Prepare the right-side expression
auto srcPos = rhs;
SuiteStmt *block = cache->NS<SuiteStmt>(this);
auto rhs = this->rhs;
if (!cast<IdExpr>(rhs)) {
// Store any non-trivial right-side expression into a variable
auto var = cache->getTemporaryVar("assign");
rhs = cache->NS<IdExpr>(this->rhs, var);
block->addStmt(cache->NS<AssignStmt>(this, rhs, ast::clone(this->rhs)));
}
// Process assignments until the fist StarExpr (if any)
size_t st = 0;
for (; st < leftSide.size(); st++) {
if (cast<StarExpr>(leftSide[st]))
break;
// Transformation: `leftSide_st = rhs[st]` where `st` is static integer
auto rightSide =
cache->NS<IndexExpr>(rhs, ast::clone(rhs), cache->NS<IntExpr>(rhs, st));
// Recursively process the assignment because of cases like `(a, (b, c)) = d)`
auto aa = AssignStmt(leftSide[st], rightSide);
aa.cache = cache;
auto ns = aa.unpack();
block->addStmt(ns);
}
// Process StarExpr (if any) and the assignments that follow it
if (st < leftSide.size() && cast<StarExpr>(leftSide[st])) {
// StarExpr becomes SliceExpr (e.g., `b` in `(a, *b, c) = d` becomes `d[1:-2]`)
auto rightSide = cache->NS<IndexExpr>(
rhs, ast::clone(rhs),
cache->NS<SliceExpr>(rhs, cache->NS<IntExpr>(rhs, st),
// this slice is either [st:] or [st:-lhs_len + st + 1]
leftSide.size() == st + 1
? nullptr
: cache->NS<IntExpr>(rhs, -leftSide.size() + st + 1),
nullptr));
auto aa = AssignStmt(cast<StarExpr>(leftSide[st])->getExpr(), rightSide);
aa.cache = cache;
auto ns = aa.unpack();
block->addStmt(ns);
st += 1;
// Process remaining assignments. They will use negative indices (-1, -2 etc.)
// because we do not know how big is StarExpr
for (; st < leftSide.size(); st++) {
if (cast<StarExpr>(leftSide[st]))
E(Error::ASSIGN_MULTI_STAR, leftSide[st]);
rightSide = cache->NS<IndexExpr>(
rhs, ast::clone(rhs), cache->NS<IntExpr>(rhs, -int(leftSide.size() - st)));
auto aa = AssignStmt(leftSide[st], rightSide);
aa.cache = cache;
auto ns = aa.unpack();
block->addStmt(ns);
}
}
return block;
}
DelStmt::DelStmt(Expr *expr) : AcceptorExtend(), expr(expr) {}
DelStmt::DelStmt(const DelStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}
@ -319,7 +236,6 @@ ImportStmt::ImportStmt(Expr *from, Expr *what, std::vector<Param> args, Expr *re
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) {
validate();
}
ImportStmt::ImportStmt(const ImportStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), from(ast::clone(stmt.from, clean)),
@ -337,25 +253,6 @@ std::string ImportStmt::toString(int indent) const {
va.empty() ? "" : format(" #:args ({})", join(va)),
ret ? format(" #:ret {}", ret->toString(indent)) : "");
}
void ImportStmt::validate() const {
if (from) {
Expr *e = from;
while (auto d = cast<DotExpr>(e))
e = d->getExpr();
if (!isId(from, "C") && !isId(from, "python")) {
if (!cast<IdExpr>(e))
E(Error::IMPORT_IDENTIFIER, e);
if (!args.empty())
E(Error::IMPORT_FN, args[0]);
if (ret)
E(Error::IMPORT_FN, ret);
if (what && !cast<IdExpr>(what))
E(Error::IMPORT_IDENTIFIER, what);
}
if (!isFunction && !args.empty())
E(Error::IMPORT_FN, args[0]);
}
}
ExceptStmt::ExceptStmt(const std::string &var, Expr *exc, Stmt *suite)
: var(var), exc(exc), suite(SuiteStmt::wrap(suite)) {}
@ -413,7 +310,6 @@ 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)) {
parseDecorators();
}
FunctionStmt::FunctionStmt(const FunctionStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)),
@ -438,93 +334,12 @@ std::string FunctionStmt::toString(int indent) const {
suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)
: "(suite)");
}
void FunctionStmt::validate() const {
if (!ret && (hasAttribute(Attr::LLVM) || hasAttribute(Attr::C)))
E(Error::FN_LLVM, getSrcInfo());
std::unordered_set<std::string> seenArgs;
bool defaultsStarted = false, hasStarArg = false, hasKwArg = false;
for (size_t ia = 0; ia < size(); ia++) {
auto &a = items[ia];
auto [stars, n] = a.getNameWithStars();
if (stars == 2) {
if (hasKwArg)
E(Error::FN_MULTIPLE_ARGS, a);
if (a.defaultValue)
E(Error::FN_DEFAULT_STARARG, a.defaultValue);
if (ia != size() - 1)
E(Error::FN_LAST_KWARG, a);
hasKwArg = true;
} else if (stars == 1) {
if (hasStarArg)
E(Error::FN_MULTIPLE_ARGS, a);
if (a.defaultValue)
E(Error::FN_DEFAULT_STARARG, a.defaultValue);
hasStarArg = true;
}
if (in(seenArgs, n))
E(Error::FN_ARG_TWICE, a, n);
seenArgs.insert(n);
if (!a.defaultValue && defaultsStarted && !stars && a.isValue())
E(Error::FN_DEFAULT, a, n);
defaultsStarted |= bool(a.defaultValue);
if (hasAttribute(Attr::C)) {
if (a.defaultValue)
E(Error::FN_C_DEFAULT, a.defaultValue, n);
if (stars != 1 && !a.type)
E(Error::FN_C_TYPE, a, n);
}
}
}
std::string FunctionStmt::signature() const {
std::vector<std::string> s;
for (auto &a : items)
s.push_back(a.type ? a.type->toString() : "-");
return format("{}", join(s, ":"));
}
void FunctionStmt::parseDecorators() {
std::vector<Expr *> newDecorators;
for (auto &d : decorators) {
if (isId(d, Attr::Attribute)) {
if (decorators.size() != 1)
E(Error::FN_SINGLE_DECORATOR, decorators[1], Attr::Attribute);
setAttribute(Attr::Attribute);
} else if (isId(d, Attr::LLVM)) {
setAttribute(Attr::LLVM);
} else if (isId(d, Attr::Python)) {
if (decorators.size() != 1)
E(Error::FN_SINGLE_DECORATOR, decorators[1], Attr::Python);
setAttribute(Attr::Python);
} else if (isId(d, Attr::Internal)) {
setAttribute(Attr::Internal);
} else if (isId(d, Attr::HiddenFromUser)) {
setAttribute(Attr::HiddenFromUser);
} else if (isId(d, Attr::Atomic)) {
setAttribute(Attr::Atomic);
} else if (isId(d, Attr::Property)) {
setAttribute(Attr::Property);
} else if (isId(d, Attr::StaticMethod)) {
setAttribute(Attr::StaticMethod);
} else if (isId(d, Attr::ForceRealize)) {
setAttribute(Attr::ForceRealize);
} else if (isId(d, Attr::C)) {
setAttribute(Attr::C);
} else {
newDecorators.emplace_back(d);
}
}
if (hasAttribute(Attr::C)) {
for (auto &a : items) {
if (a.name.size() > 1 && a.name[0] == '*' && a.name[1] != '*')
setAttribute(Attr::CVarArg);
}
}
if (!items.empty() && !items[0].type && items[0].name == "self") {
setAttribute(Attr::HasSelf);
}
decorators = newDecorators;
validate();
}
size_t FunctionStmt::getStarArgs() const {
size_t i = 0;
while (i < items.size()) {
@ -616,7 +431,6 @@ ClassStmt::ClassStmt(std::string name, std::vector<Param> args, Stmt *suite,
this->baseClasses.push_back(b);
}
}
parseDecorators();
}
ClassStmt::ClassStmt(const ClassStmt &stmt, bool clean)
: AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)),
@ -646,122 +460,7 @@ std::string ClassStmt::toString(int indent) const {
suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)
: "(suite)");
}
void ClassStmt::validate() const {
std::unordered_set<std::string> seen;
if (hasAttribute(Attr::Extend) && !items.empty())
E(Error::CLASS_EXTENSION, items[0]);
if (hasAttribute(Attr::Extend) && !(baseClasses.empty() && staticBaseClasses.empty()))
E(Error::CLASS_EXTENSION,
baseClasses.empty() ? staticBaseClasses[0] : baseClasses[0]);
for (auto &a : items) {
if (!a.type && !a.defaultValue)
E(Error::CLASS_MISSING_TYPE, a, a.name);
if (in(seen, a.name))
E(Error::CLASS_ARG_TWICE, a, a.name);
seen.insert(a.name);
}
}
bool ClassStmt::isRecord() const { return hasAttribute(Attr::Tuple); }
void ClassStmt::parseDecorators() {
// @tuple(init=, repr=, eq=, order=, hash=, pickle=, container=, python=, add=,
// internal=...)
// @dataclass(...)
// @extend
std::map<std::string, bool> tupleMagics = {
{"new", true}, {"repr", false}, {"hash", false},
{"eq", false}, {"ne", false}, {"lt", false},
{"le", false}, {"gt", false}, {"ge", false},
{"pickle", true}, {"unpickle", true}, {"to_py", false},
{"from_py", false}, {"iter", false}, {"getitem", false},
{"len", false}, {"to_gpu", false}, {"from_gpu", false},
{"from_gpu_new", false}, {"tuplesize", true}};
for (auto &d : decorators) {
if (isId(d, "deduce")) {
setAttribute(Attr::ClassDeduce);
} else if (isId(d, "__notuple__")) {
setAttribute(Attr::ClassNoTuple);
} else if (isId(d, "dataclass")) {
} else if (auto c = cast<CallExpr>(d)) {
if (isId(c->getExpr(), Attr::Tuple)) {
setAttribute(Attr::Tuple);
for (auto &m : tupleMagics)
m.second = true;
} else if (!isId(c->getExpr(), "dataclass")) {
E(Error::CLASS_BAD_DECORATOR, c->getExpr());
} else if (hasAttribute(Attr::Tuple)) {
E(Error::CLASS_CONFLICT_DECORATOR, c, "dataclass", Attr::Tuple);
}
for (const auto &a : *c) {
auto b = cast<BoolExpr>(a);
if (!b)
E(Error::CLASS_NONSTATIC_DECORATOR, a);
char val = char(b->getValue());
if (a.getName() == "init") {
tupleMagics["new"] = val;
} else if (a.getName() == "repr") {
tupleMagics["repr"] = val;
} else if (a.getName() == "eq") {
tupleMagics["eq"] = tupleMagics["ne"] = val;
} else if (a.getName() == "order") {
tupleMagics["lt"] = tupleMagics["le"] = tupleMagics["gt"] =
tupleMagics["ge"] = val;
} else if (a.getName() == "hash") {
tupleMagics["hash"] = val;
} else if (a.getName() == "pickle") {
tupleMagics["pickle"] = tupleMagics["unpickle"] = val;
} else if (a.getName() == "python") {
tupleMagics["to_py"] = tupleMagics["from_py"] = val;
} else if (a.getName() == "gpu") {
tupleMagics["to_gpu"] = tupleMagics["from_gpu"] =
tupleMagics["from_gpu_new"] = val;
} else if (a.getName() == "container") {
tupleMagics["iter"] = tupleMagics["getitem"] = val;
} else {
E(Error::CLASS_BAD_DECORATOR_ARG, a);
}
}
} else if (isId(d, Attr::Tuple)) {
if (hasAttribute(Attr::Tuple))
E(Error::CLASS_MULTIPLE_DECORATORS, d, Attr::Tuple);
setAttribute(Attr::Tuple);
for (auto &m : tupleMagics) {
m.second = true;
}
} else if (isId(d, Attr::Extend)) {
setAttribute(Attr::Extend);
if (decorators.size() != 1)
E(Error::CLASS_SINGLE_DECORATOR, decorators[decorators[0] == d], Attr::Extend);
} else if (isId(d, Attr::Internal)) {
setAttribute(Attr::Internal);
} else {
E(Error::CLASS_BAD_DECORATOR, d);
}
}
if (hasAttribute(Attr::ClassDeduce))
tupleMagics["new"] = false;
if (!hasAttribute(Attr::Tuple)) {
tupleMagics["init"] = tupleMagics["new"];
tupleMagics["new"] = tupleMagics["raw"] = true;
tupleMagics["len"] = false;
}
tupleMagics["dict"] = true;
// Internal classes do not get any auto-generated members.
std::vector<std::string> magics;
if (!hasAttribute(Attr::Internal)) {
for (auto &m : tupleMagics)
if (m.second) {
if (m.first == "new")
magics.insert(magics.begin(), m.first);
else
magics.push_back(m.first);
}
}
setAttribute(Attr::ClassMagic, std::make_unique<ir::StringListAttribute>(magics));
validate();
}
bool ClassStmt::isClassVar(const Param &p) {
if (!p.defaultValue)
return false;

View File

@ -47,8 +47,6 @@ struct Stmt : public AcceptorExtend<Stmt, ASTNode> {
/// @return the first statement in a suite; if a statement is not a suite, returns the
/// statement itself
virtual Stmt *firstInBlock() { return this; }
/// Validate a node. Throw ParseASTException if a node is not valid.
void validate() const;
static const char NodeId;
SERIALIZE(Stmt, BASE(ASTNode), done);
@ -133,7 +131,6 @@ struct AssignStmt : public AcceptorExtend<AssignStmt, Stmt> {
bool isAtomicUpdate() const { return update == UpdateAtomic; }
void setUpdate() { update = Update; }
void setAtomicUpdate() { update = UpdateAtomic; }
Stmt *unpack() const;
ACCEPT(AssignStmt, ASTVisitor, lhs, rhs, type, update);
@ -376,7 +373,6 @@ struct ImportStmt : public AcceptorExtend<ImportStmt, Stmt> {
Expr *getReturnType() const { return ret; }
const std::vector<Param> &getArgs() const { return args; }
bool isCVar() const { return !isFunction; }
void validate() const;
ACCEPT(ImportStmt, ASTVisitor, from, what, as, dots, args, ret, isFunction);
@ -494,8 +490,6 @@ struct FunctionStmt : public AcceptorExtend<FunctionStmt, Stmt>, Items<Param> {
std::string signature() const;
size_t getStarArgs() const;
size_t getKwStarArgs() const;
void validate() const;
void parseDecorators();
std::string getDocstr() const;
std::unordered_set<std::string> getNonInferrableGenerics() const;
@ -532,8 +526,6 @@ struct ClassStmt : public AcceptorExtend<ClassStmt, Stmt>, Items<Param> {
/// @return true if a class is a tuple-like record (e.g. has a "@tuple" attribute)
bool isRecord() const;
std::string getDocstr() const;
void validate() const;
void parseDecorators();
static bool isClassVar(const Param &p);

View File

@ -101,22 +101,24 @@ std::string UnionType::realizedName() const {
return ClassType::realizedName();
}
void UnionType::addType(Type *typ) {
bool UnionType::addType(Type *typ) {
seqassert(!isSealed(), "union already sealed");
if (this == typ)
return;
return true;
if (auto tu = typ->getUnion()) {
if (tu->isSealed()) {
for (auto &t : tu->generics[0].type->getClass()->generics)
addType(t.type.get());
if (!addType(t.type.get()))
return false;
} else {
for (auto &t : tu->pendingTypes) {
if (t->getLink() && t->getLink()->kind == LinkType::Unbound)
break;
else
addType(t.get());
else if (!addType(t.get()))
return false;
}
}
return true;
} else {
// Find first pending generic to which we can attach this!
Unification us;
@ -124,10 +126,10 @@ void UnionType::addType(Type *typ) {
if (auto l = t->getLink()) {
if (l->kind == LinkType::Unbound) {
t->unify(typ, &us);
return;
return true;
}
}
E(error::Error::UNION_TOO_BIG, this, pendingTypes.size());
return false;
}
}

View File

@ -34,7 +34,7 @@ public:
UnionType *getUnion() override { return this; }
void addType(Type *);
bool addType(Type *);
void seal();
std::vector<Type *> getRealizationTypes();
};

View File

@ -170,9 +170,11 @@ ir::types::Type *Cache::makeUnion(const std::vector<types::TypePtr> &types) {
}
void Cache::parseCode(const std::string &code) {
auto node = ast::parseCode(this, "<internal>", code, /*startLine=*/0);
auto nodeOrErr = ast::parseCode(this, "<internal>", code, /*startLine=*/0);
if (nodeOrErr)
throw exc::ParserException(nodeOrErr.takeError());
auto sctx = imports[MAIN_IMPORT].ctx;
node = ast::TypecheckVisitor::apply(sctx, node);
auto node = ast::TypecheckVisitor::apply(sctx, *nodeOrErr);
ast::TranslateVisitor(codegenCtx).translateStmts(node);
}

View File

@ -33,6 +33,7 @@
#define VAR_CLASS_TOPLEVEL ":toplevel"
#define VAR_ARGV "__argv__"
#define MAX_ERRORS 5
#define MAX_TUPLE 2048
#define MAX_INT_WIDTH 10000
#define MAX_REALIZATION_DEPTH 200
@ -255,7 +256,7 @@ struct Cache {
std::unordered_map<std::string, int> generatedTuples;
std::vector<std::vector<std::string>> generatedTupleNames = {{}};
std::vector<exc::ParserException> errors;
ParserErrors errors;
/// Set if Codon operates in Python compatibility mode (e.g., with Python numerics)
bool pythonCompat = false;

View File

@ -114,18 +114,14 @@ assignment <-
/ (star_targets _ (!'==' '=') _)+ star_expressions !(_ '=') {
vector<Stmt*> stmts;
for (int i = int(VS.size()) - 2; i >= 0; i--) {
auto a = AssignStmt(ac_expr(VS[i]), ac_expr(VS[i + 1]));
a.setSrcInfo(LOC);
a.cache = CTX.cache;
stmts.push_back(a.unpack());
auto a = asts(Assign, LOC, ac_expr(VS[i]), ac_expr(VS[i + 1]));
stmts.push_back(a);
}
return asts(Suite, LOC, std::move(stmts));
}
/ star_expression _ augassign '=' ^ _ star_expressions {
auto a = AssignStmt(ac_expr(V0), aste(Binary, LOC, clone(ac_expr(V0)), ac<string>(V1), ac_expr(V2), true));
a.setSrcInfo(LOC);
a.cache = CTX.cache;
return a.unpack();
auto a = asts(Assign, LOC, ac_expr(V0), aste(Binary, LOC, clone(ac_expr(V0)), ac<string>(V1), ac_expr(V2), true));
return a;
}
augassign <- <
'+' / '-' / '**' / '*' / '@' / '//' / '/' / '%' / '&' / '|' / '^' / '<<' / '>>'
@ -320,7 +316,6 @@ function <-
auto fn = (FunctionStmt*)(ac_stmt(V1));
fn->setDecorators(ac<vector<Expr*>>(V0));
fn->setSuite(SuiteStmt::wrap(asts(Expr, LOC, aste(String, LOC, ac<string>(V2)))));
fn->parseDecorators();
return (Stmt*)fn;
}
/ decorators? function_def _ suite {
@ -328,7 +323,6 @@ function <-
if (VS.size() > 2)
fn->setDecorators(ac<vector<Expr*>>(V0));
fn->setSuite(SuiteStmt::wrap(ac_stmt(VS.size() > 2 ? V2 : V1)));
fn->parseDecorators();
return (Stmt*)fn;
}
extern <- (empty_line* EXTERNDENT (!EOL .)* EOL empty_line*)+ {
@ -395,7 +389,6 @@ class <- decorators? class_def {
if (VS.size() == 2) {
auto fn = ac_stmt(V1);
cast<ClassStmt>(fn)->setDecorators(ac<vector<Expr*>>(V0));
cast<ClassStmt>(fn)->parseDecorators();
return fn;
}
return ac_stmt(V0);

View File

@ -53,15 +53,16 @@ std::shared_ptr<peg::Grammar> initParser() {
}
template <typename T>
T parseCode(Cache *cache, const std::string &file, const std::string &code,
int line_offset, int col_offset, const std::string &rule) {
llvm::Expected<T> parseCode(Cache *cache, const std::string &file,
const std::string &code, int line_offset, int col_offset,
const std::string &rule) {
Timer t("");
t.logged = true;
// Initialize
if (!grammar)
grammar = initParser();
std::vector<std::tuple<size_t, size_t, std::string>> errors;
std::vector<ErrorMessage> errors;
auto log = [&](size_t line, size_t col, const std::string &msg, const std::string &) {
size_t ed = msg.size();
if (startswith(msg, "syntax error, unexpected")) {
@ -69,7 +70,7 @@ T parseCode(Cache *cache, const std::string &file, const std::string &code,
if (i != std::string::npos)
ed = i;
}
errors.emplace_back(line, col, msg.substr(0, ed));
errors.emplace_back(msg.substr(0, ed), file, line, col);
};
T result;
auto ctx = std::make_any<ParseContext>(cache, 0, line_offset, col_offset);
@ -79,33 +80,27 @@ T parseCode(Cache *cache, const std::string &file, const std::string &code,
if (!ret)
r.error_info.output_log(log, code.c_str(), code.size());
totalPeg += t.elapsed();
exc::ParserException ex;
if (!errors.empty()) {
for (auto &e : errors)
ex.track(fmt::format("{}", std::get<2>(e)),
SrcInfo(file, std::get<0>(e), std::get<1>(e), 0));
throw ex;
return T();
}
if (!errors.empty())
return llvm::make_error<error::ParserErrorInfo>(errors);
return result;
}
Stmt *parseCode(Cache *cache, const std::string &file, const std::string &code,
int line_offset) {
llvm::Expected<Stmt *> parseCode(Cache *cache, const std::string &file,
const std::string &code, int line_offset) {
return parseCode<Stmt *>(cache, file, code + "\n", line_offset, 0, "program");
}
std::pair<Expr *, StringExpr::FormatSpec>
llvm::Expected<std::pair<Expr *, StringExpr::FormatSpec>>
parseExpr(Cache *cache, const std::string &code, const codon::SrcInfo &offset) {
auto newCode = code;
ltrim(newCode);
rtrim(newCode);
auto e = parseCode<std::pair<Expr *, StringExpr::FormatSpec>>(
return parseCode<std::pair<Expr *, StringExpr::FormatSpec>>(
cache, offset.file, newCode, offset.line, offset.col, "fstring");
return e;
}
Stmt *parseFile(Cache *cache, const std::string &file) {
llvm::Expected<Stmt *> parseFile(Cache *cache, const std::string &file) {
std::vector<std::string> lines;
std::string code;
if (file == "-") {
@ -116,7 +111,8 @@ Stmt *parseFile(Cache *cache, const std::string &file) {
} else {
std::ifstream fin(file);
if (!fin)
E(error::Error::COMPILER_NO_FILE, SrcInfo(), file);
return llvm::make_error<error::ParserErrorInfo>(error::Error::COMPILER_NO_FILE,
SrcInfo(), file);
for (std::string line; getline(fin, line);) {
lines.push_back(line);
code += line + "\n";
@ -143,14 +139,14 @@ std::shared_ptr<peg::Grammar> initOpenMPParser() {
return g;
}
std::vector<CallArg> parseOpenMP(Cache *cache, const std::string &code,
const codon::SrcInfo &loc) {
llvm::Expected<std::vector<CallArg>> parseOpenMP(Cache *cache, const std::string &code,
const codon::SrcInfo &loc) {
if (!ompGrammar)
ompGrammar = initOpenMPParser();
std::vector<std::tuple<size_t, size_t, std::string>> errors;
std::vector<ErrorMessage> errors;
auto log = [&](size_t line, size_t col, const std::string &msg, const std::string &) {
errors.emplace_back(line, col, msg);
errors.emplace_back(fmt::format("openmp: {}", msg), loc.file, loc.line, loc.col);
};
std::vector<CallArg> result;
auto ctx = std::make_any<ParseContext>(cache, 0, 0, 0);
@ -159,11 +155,8 @@ std::vector<CallArg> parseOpenMP(Cache *cache, const std::string &code,
auto ret = r.ret && r.len == code.size();
if (!ret)
r.error_info.output_log(log, code.c_str(), code.size());
exc::ParserException ex;
if (!errors.empty()) {
ex.track(fmt::format("openmp {}", std::get<2>(errors[0])), loc);
throw ex;
}
if (!errors.empty())
return llvm::make_error<error::ParserErrorInfo>(errors);
return result;
}

View File

@ -12,21 +12,19 @@
namespace codon::ast {
/// Parse a Seq code block with the appropriate file and position offsets.
Stmt *parseCode(Cache *cache, const std::string &file, const std::string &code,
int line_offset = 0);
llvm::Expected<Stmt *> parseCode(Cache *cache, const std::string &file,
const std::string &code, int line_offset = 0);
/// Parse a Seq code expression.
/// @return pair of Expr * and a format specification
/// (empty if not available).
std::pair<Expr *, StringExpr::FormatSpec>
llvm::Expected<std::pair<Expr *, StringExpr::FormatSpec>>
parseExpr(Cache *cache, const std::string &code, const codon::SrcInfo &offset);
/// Parse a Seq file.
Stmt *parseFile(Cache *cache, const std::string &file);
llvm::Expected<Stmt *> parseFile(Cache *cache, const std::string &file);
/// Parse a OpenMP clause.
std::vector<CallArg> parseOpenMP(Cache *cache, const std::string &code,
const codon::SrcInfo &loc);
llvm::Expected<std::vector<CallArg>> parseOpenMP(Cache *cache, const std::string &code,
const codon::SrcInfo &loc);
} // namespace codon::ast

View File

@ -17,6 +17,7 @@ using fmt::format;
namespace codon::ast {
using namespace error;
using namespace matcher;
// clang-format off
@ -99,24 +100,30 @@ std::shared_ptr<json> DocVisitor::apply(const std::string &argv0,
shared->j = std::make_shared<json>();
auto stdlib = getImportFile(argv0, STDLIB_INTERNAL_MODULE, "", true, "");
auto ast = ast::parseFile(shared->cache, stdlib->path);
auto core =
auto astOrErr = ast::parseFile(shared->cache, stdlib->path);
if (!astOrErr)
throw exc::ParserException(astOrErr.takeError());
auto coreOrErr =
ast::parseCode(shared->cache, stdlib->path, "from internal.core import *");
if (!coreOrErr)
throw exc::ParserException(coreOrErr.takeError());
shared->modules[""]->setFilename(stdlib->path);
shared->modules[""]->add("__py_numerics__", std::make_shared<int>(shared->itemID++));
shared->modules[""]->add("__py_extension__", std::make_shared<int>(shared->itemID++));
shared->modules[""]->add("__debug__", std::make_shared<int>(shared->itemID++));
shared->modules[""]->add("__apple__", std::make_shared<int>(shared->itemID++));
DocVisitor(shared->modules[""]).transformModule(std::move(core));
DocVisitor(shared->modules[""]).transformModule(std::move(ast));
DocVisitor(shared->modules[""]).transformModule(*coreOrErr);
DocVisitor(shared->modules[""]).transformModule(*astOrErr);
auto ctx = std::make_shared<DocContext>(shared);
for (auto &f : files) {
auto path = getAbsolutePath(f);
ctx->setFilename(path);
LOG("-> parsing {}", path);
auto ast = ast::parseFile(shared->cache, path);
DocVisitor(ctx).transformModule(std::move(ast));
auto astOrErr = ast::parseFile(shared->cache, path);
if (!astOrErr)
throw exc::ParserException(astOrErr.takeError());
DocVisitor(ctx).transformModule(*astOrErr);
}
shared->cache = nullptr;
@ -206,7 +213,7 @@ void DocVisitor::visit(IntExpr *expr) {
void DocVisitor::visit(IdExpr *expr) {
auto i = ctx->find(expr->getValue());
if (!i)
error("unknown identifier {}", expr->getValue());
E(Error::CUSTOM, expr->getSrcInfo(), "unknown identifier {}", expr->getValue());
resultExpr = std::make_shared<json>(*i ? std::to_string(*i) : expr->getValue());
}
@ -391,7 +398,7 @@ void DocVisitor::visit(ImportStmt *stmt) {
}
if (!cast<IdExpr>(e) || !stmt->getArgs().empty() || stmt->getReturnType() ||
(stmt->getWhat() && !cast<IdExpr>(stmt->getWhat())))
error("invalid import statement");
E(Error::CUSTOM, stmt->getSrcInfo(), "invalid import statement");
// We have an empty stmt->from in "from .. import".
if (!cast<IdExpr>(e)->getValue().empty())
dirs.push_back(cast<IdExpr>(e)->getValue());
@ -399,7 +406,7 @@ void DocVisitor::visit(ImportStmt *stmt) {
auto ee = cast<IdExpr>(e);
if (!ee || !stmt->getArgs().empty() || stmt->getReturnType() ||
(stmt->getWhat() && !cast<IdExpr>(stmt->getWhat())))
error("invalid import statement");
E(Error::CUSTOM, stmt->getSrcInfo(), "invalid import statement");
// We have an empty stmt->from in "from .. import".
if (!ee->getValue().empty())
dirs.push_back(ee->getValue());
@ -413,7 +420,7 @@ void DocVisitor::visit(ImportStmt *stmt) {
// Fetch the import!
auto file = getImportFile(ctx->shared->argv0, path, ctx->getFilename());
if (!file)
error(stmt, "cannot locate import '{}'", path);
E(Error::CUSTOM, stmt->getSrcInfo(), "cannot locate import '{}'", path);
auto ictx = ctx;
auto it = ctx->shared->modules.find(file->path);
@ -421,8 +428,10 @@ void DocVisitor::visit(ImportStmt *stmt) {
ctx->shared->modules[file->path] = ictx = std::make_shared<DocContext>(ctx->shared);
ictx->setFilename(file->path);
LOG("=> parsing {}", file->path);
auto tmp = parseFile(ctx->shared->cache, file->path);
DocVisitor(ictx).transformModule(std::move(tmp));
auto tmpOrErr = parseFile(ctx->shared->cache, file->path);
if (!tmpOrErr)
throw exc::ParserException(tmpOrErr.takeError());
DocVisitor(ictx).transformModule(*tmpOrErr);
} else {
ictx = it->second;
}
@ -440,7 +449,8 @@ void DocVisitor::visit(ImportStmt *stmt) {
if (auto c = ictx->find(i->getValue()))
ctx->add(stmt->getAs().empty() ? i->getValue() : stmt->getAs(), c);
else
error(stmt, "symbol '{}' not found in {}", i->getValue(), file->path);
E(Error::CUSTOM, stmt->getSrcInfo(), "symbol '{}' not found in {}", i->getValue(),
file->path);
}
}

View File

@ -63,8 +63,8 @@ public:
return fmt::format("{}{}{}", t.header, t.transform(stmt), t.footer);
}
void defaultVisit(Expr *e) override { error("cannot format {}", *e); }
void defaultVisit(Stmt *e) override { error("cannot format {}", *e); }
void defaultVisit(Expr *e) override { seqassertn(false, "cannot format {}", *e); }
void defaultVisit(Stmt *e) override { seqassertn(false, "cannot format {}", *e); }
public:
void visit(NoneExpr *) override;

View File

@ -11,13 +11,24 @@
#include "codon/parser/visitors/scoping/scoping.h"
#include <fmt/format.h>
#define CHECK(x) \
{ \
if (!(x)) \
return; \
}
#define STOP_ERROR(...) \
do { \
addError(__VA_ARGS__); \
return; \
} while (0)
using fmt::format;
using namespace codon::error;
using namespace codon::matcher;
namespace codon::ast {
void ScopingVisitor::apply(Cache *cache, Stmt *s) {
llvm::Error ScopingVisitor::apply(Cache *cache, Stmt *s) {
auto c = std::make_shared<ScopingVisitor::Context>();
c->cache = cache;
c->functionScope = nullptr;
@ -25,60 +36,72 @@ void ScopingVisitor::apply(Cache *cache, Stmt *s) {
v.ctx = c;
ConditionalBlock cb(c.get(), s, 0);
v.transform(s);
if (!v.transform(s))
return llvm::make_error<ParserErrorInfo>(v.errors);
v.processChildCaptures();
return llvm::Error::success();
}
void ScopingVisitor::transform(Expr *expr) {
bool ScopingVisitor::transform(Expr *expr) {
ScopingVisitor v(*this);
if (expr) {
setSrcInfo(expr->getSrcInfo());
expr->accept(v);
if (!canContinue())
return false;
}
return true;
}
void ScopingVisitor::transform(Stmt *stmt) {
bool ScopingVisitor::transform(Stmt *stmt) {
ScopingVisitor v(*this);
if (stmt) {
setSrcInfo(stmt->getSrcInfo());
stmt->accept(v);
if (!canContinue())
return false;
}
return true;
}
void ScopingVisitor::transformScope(Expr *e) {
bool ScopingVisitor::transformScope(Expr *e) {
if (e) {
ConditionalBlock c(ctx.get(), nullptr);
transform(e);
return transform(e);
}
return true;
}
void ScopingVisitor::transformScope(Stmt *s) {
bool ScopingVisitor::transformScope(Stmt *s) {
if (s) {
ConditionalBlock c(ctx.get(), s);
transform(s);
return transform(s);
}
return true;
}
void ScopingVisitor::transformAdding(Expr *e, ASTNode *root) {
seqassert(e, "bad call to transformAdding");
bool ScopingVisitor::transformAdding(Expr *e, ASTNode *root) {
if (cast<IndexExpr>(e)) {
transform(e);
return transform(e);
} else if (auto de = cast<DotExpr>(e)) {
transform(e);
if (!transform(e))
return false;
if (!ctx->classDeduce.first.empty() &&
match(de->getExpr(), M<IdExpr>(ctx->classDeduce.first)))
ctx->classDeduce.second.insert(de->getMember());
return true;
} else if (cast<ListExpr>(e) || cast<TupleExpr>(e) || cast<IdExpr>(e)) {
SetInScope s1(&(ctx->adding), true);
SetInScope s2(&(ctx->root), root);
transform(e);
return transform(e);
} else {
E(Error::ASSIGN_INVALID, e);
seqassert(e, "bad call to transformAdding");
addError(Error::ASSIGN_INVALID, e);
return false;
}
}
void ScopingVisitor::visit(IdExpr *expr) {
// LOG("-------> {} {} {}", expr->getSrcInfo(), *expr, ctx->getScope());
if (ctx->adding && ctx->tempScope)
ctx->renames.back()[expr->getValue()] =
ctx->cache->getTemporaryVar(expr->getValue());
@ -87,44 +110,104 @@ void ScopingVisitor::visit(IdExpr *expr) {
expr->setValue(*v);
break;
}
if (visitName(expr->getValue(), ctx->adding, ctx->root, expr->getSrcInfo())) {
if (visitName(expr->getValue(), ctx->adding, ctx->root, expr->getSrcInfo()))
expr->setAttribute(Attr::ExprDominatedUndefCheck);
}
}
void ScopingVisitor::visit(StringExpr *expr) {
for (auto &s : *expr)
if (s.prefix == "#f") {
auto e = expr->getSrcInfo();
e.line += s.getSrcInfo().col;
e.col += s.getSrcInfo().col;
auto [expr, format] = parseExpr(ctx->cache, s.value, e);
s.expr = expr;
s.format = format;
transform(s.expr);
std::vector<StringExpr::String> exprs;
for (auto &p : *expr) {
if (p.prefix == "f" || p.prefix == "F") {
/// Transform an F-string
auto fstr = unpackFString(p.value);
if (!canContinue())
return;
for (auto pf : fstr) {
if (pf.prefix.empty() && !exprs.empty() && exprs.back().prefix.empty()) {
exprs.back().value += pf.value;
} else {
exprs.emplace_back(pf);
}
}
} else if (!p.prefix.empty()) {
exprs.emplace_back(p);
} else if (!exprs.empty() && exprs.back().prefix.empty()) {
exprs.back().value += p.value;
} else {
exprs.emplace_back(p);
}
}
expr->strings = exprs;
}
/// Split a Python-like f-string into a list:
/// `f"foo {x+1} bar"` -> `["foo ", str(x+1), " bar"]
/// Supports "{x=}" specifier (that prints the raw expression as well):
/// `f"{x+1=}"` -> `["x+1=", str(x+1)]`
std::vector<StringExpr::String>
ScopingVisitor::unpackFString(const std::string &value) {
// Strings to be concatenated
std::vector<StringExpr::String> items;
int braceCount = 0, braceStart = 0;
for (int i = 0; i < value.size(); i++) {
if (value[i] == '{') {
if (braceStart < i)
items.emplace_back(value.substr(braceStart, i - braceStart));
if (!braceCount)
braceStart = i + 1;
braceCount++;
} else if (value[i] == '}') {
braceCount--;
if (!braceCount) {
std::string code = value.substr(braceStart, i - braceStart);
auto offset = getSrcInfo();
offset.col += i;
items.emplace_back(code, "#f");
items.back().setSrcInfo(offset);
auto val = parseExpr(ctx->cache, code, offset);
if (!val) {
addError(val.takeError());
} else {
items.back().expr = val->first;
if (!transform(items.back().expr))
return items;
items.back().format = val->second;
}
}
braceStart = i + 1;
}
}
if (braceCount > 0)
addError(Error::STR_FSTRING_BALANCE_EXTRA, getSrcInfo());
else if (braceCount < 0)
addError(Error::STR_FSTRING_BALANCE_MISSING, getSrcInfo());
if (braceStart != value.size())
items.emplace_back(value.substr(braceStart, value.size() - braceStart));
return items;
}
void ScopingVisitor::visit(GeneratorExpr *expr) {
SetInScope s(&(ctx->tempScope), true);
ctx->renames.emplace_back();
transform(expr->getFinalSuite());
CHECK(transform(expr->getFinalSuite()));
ctx->renames.pop_back();
}
void ScopingVisitor::visit(IfExpr *expr) {
transform(expr->getCond());
transformScope(expr->getIf());
transformScope(expr->getElse());
CHECK(transform(expr->getCond()));
CHECK(transformScope(expr->getIf()));
CHECK(transformScope(expr->getElse()));
}
void ScopingVisitor::visit(BinaryExpr *expr) {
transform(expr->getLhs());
if (expr->getOp() == "&&" || expr->getOp() == "||")
transformScope(expr->getRhs());
else
transform(expr->getRhs());
CHECK(transform(expr->getLhs()));
if (expr->getOp() == "&&" || expr->getOp() == "||") {
CHECK(transformScope(expr->getRhs()));
} else {
CHECK(transform(expr->getRhs()));
}
}
void ScopingVisitor::visit(AssignExpr *expr) {
@ -132,8 +215,8 @@ void ScopingVisitor::visit(AssignExpr *expr) {
"only simple assignment expression are supported");
SetInScope s(&(ctx->tempScope), false);
transform(expr->getExpr());
transformAdding(expr->getVar(), expr);
CHECK(transform(expr->getExpr()));
CHECK(transformAdding(expr->getVar(), expr));
}
void ScopingVisitor::visit(LambdaExpr *expr) {
@ -154,6 +237,10 @@ void ScopingVisitor::visit(LambdaExpr *expr) {
v.transform(expr->getExpr());
v.processChildCaptures();
c->scope.pop_back();
if (v.hasErrors())
errors.append(v.errors);
if (!canContinue())
return;
auto b = std::make_unique<BindingsAttribute>();
b->captures = c->captures;
@ -165,101 +252,116 @@ void ScopingVisitor::visit(LambdaExpr *expr) {
}
// todo)) Globals/nonlocals cannot be shadowed in children scopes (as in Python)
// val->canShadow = false;
void ScopingVisitor::visit(AssignStmt *stmt) {
// seqassert(stmt->lhs->getId(), "assignment not unpacked");
transform(stmt->getRhs());
transform(stmt->getTypeExpr());
transformAdding(stmt->getLhs(), stmt);
CHECK(transform(stmt->getRhs()));
CHECK(transform(stmt->getTypeExpr()));
CHECK(transformAdding(stmt->getLhs(), stmt));
}
void ScopingVisitor::visit(IfStmt *stmt) {
transform(stmt->getCond());
transformScope(stmt->getIf());
transformScope(stmt->getElse());
CHECK(transform(stmt->getCond()));
CHECK(transformScope(stmt->getIf()));
CHECK(transformScope(stmt->getElse()));
}
void ScopingVisitor::visit(MatchStmt *stmt) {
transform(stmt->getExpr());
CHECK(transform(stmt->getExpr()));
for (auto &m : *stmt) {
transform(m.getPattern());
transform(m.getGuard());
transformScope(m.getSuite());
CHECK(transform(m.getPattern()));
CHECK(transform(m.getGuard()));
CHECK(transformScope(m.getSuite()));
}
}
void ScopingVisitor::visit(WhileStmt *stmt) {
transform(stmt->getCond());
CHECK(transform(stmt->getCond()));
std::unordered_set<std::string> seen;
{
ConditionalBlock c(ctx.get(), stmt->getSuite());
ctx->scope.back().seenVars = std::make_unique<std::unordered_set<std::string>>();
transform(stmt->getSuite());
CHECK(transform(stmt->getSuite()));
seen = *(ctx->scope.back().seenVars);
}
for (auto &var : seen)
findDominatingBinding(var);
transformScope(stmt->getElse());
CHECK(transformScope(stmt->getElse()));
}
void ScopingVisitor::visit(ForStmt *stmt) {
transform(stmt->getIter());
transform(stmt->getDecorator());
CHECK(transform(stmt->getIter()));
CHECK(transform(stmt->getDecorator()));
for (auto &a : stmt->ompArgs)
transform(a.value);
CHECK(transform(a.value));
std::unordered_set<std::string> seen;
std::unordered_set<std::string> seen, seenDef;
{
ConditionalBlock c(ctx.get(), stmt->getSuite());
if (!cast<IdExpr>(stmt->getVar())) {
auto var = N<IdExpr>(ctx->cache->getTemporaryVar("for"));
auto e = N<AssignStmt>(clone(stmt->getVar()), clone(var));
stmt->suite = N<SuiteStmt>(e->unpack(), stmt->getSuite());
stmt->var = var;
}
transformAdding(stmt->var, stmt);
ctx->scope.back().seenVars = std::make_unique<std::unordered_set<std::string>>();
transform(stmt->getSuite());
CHECK(transformAdding(stmt->var, stmt));
seenDef = *(ctx->scope.back().seenVars);
ctx->scope.back().seenVars = std::make_unique<std::unordered_set<std::string>>();
CHECK(transform(stmt->getSuite()));
seen = *(ctx->scope.back().seenVars);
}
for (auto &var : seen)
if (var != cast<IdExpr>(stmt->getVar())->getValue())
if (!in(seenDef, var))
findDominatingBinding(var);
transformScope(stmt->getElse());
CHECK(transformScope(stmt->getElse()));
}
void ScopingVisitor::visit(ImportStmt *stmt) {
// Validate
if (stmt->getFrom()) {
Expr *e = stmt->getFrom();
while (auto d = cast<DotExpr>(e))
e = d->getExpr();
if (!isId(stmt->getFrom(), "C") && !isId(stmt->getFrom(), "python")) {
if (!cast<IdExpr>(e))
STOP_ERROR(Error::IMPORT_IDENTIFIER, e);
if (!stmt->getArgs().empty())
STOP_ERROR(Error::IMPORT_FN, stmt->getArgs().front().getSrcInfo());
if (stmt->getReturnType())
STOP_ERROR(Error::IMPORT_FN, stmt->getReturnType());
if (stmt->getWhat() && !cast<IdExpr>(stmt->getWhat()))
STOP_ERROR(Error::IMPORT_IDENTIFIER, stmt->getWhat());
}
if (stmt->isCVar() && !stmt->getArgs().empty())
STOP_ERROR(Error::IMPORT_FN, stmt->getArgs().front().getSrcInfo());
}
if (ctx->functionScope && stmt->getWhat() && isId(stmt->getWhat(), "*"))
E(error::Error::IMPORT_STAR, stmt);
STOP_ERROR(error::Error::IMPORT_STAR, stmt);
return;
// dylib C imports
if (stmt->getFrom() && isId(stmt->getFrom(), "C") && cast<DotExpr>(stmt->getWhat()))
transform(cast<DotExpr>(stmt->getWhat())->getExpr());
CHECK(transform(cast<DotExpr>(stmt->getWhat())->getExpr()));
if (stmt->getAs().empty()) {
if (stmt->getWhat())
transformAdding(stmt->getWhat(), stmt);
else
transformAdding(stmt->getFrom(), stmt);
if (stmt->getWhat()) {
CHECK(transformAdding(stmt->getWhat(), stmt));
} else {
CHECK(transformAdding(stmt->getFrom(), stmt));
}
} else {
visitName(stmt->getAs(), true, stmt, stmt->getSrcInfo());
}
for (const auto &a : stmt->getArgs()) {
transform(a.type);
transform(a.defaultValue);
CHECK(transform(a.type));
CHECK(transform(a.defaultValue));
}
transform(stmt->getReturnType());
CHECK(transform(stmt->getReturnType()));
}
void ScopingVisitor::visit(TryStmt *stmt) {
transformScope(stmt->getSuite());
CHECK(transformScope(stmt->getSuite()));
for (auto *a : *stmt) {
transform(a->getException());
CHECK(transform(a->getException()));
ConditionalBlock c(ctx.get(), a->getSuite());
if (!a->getVar().empty()) {
auto newName = ctx->cache->getTemporaryVar(a->getVar());
@ -267,24 +369,25 @@ void ScopingVisitor::visit(TryStmt *stmt) {
a->var = newName;
visitName(a->getVar(), true, a, a->getException()->getSrcInfo());
}
transform(a->getSuite());
CHECK(transform(a->getSuite()));
if (!a->getVar().empty())
ctx->renames.pop_back();
}
transform(stmt->getFinally());
CHECK(transform(stmt->getFinally()));
}
void ScopingVisitor::visit(DelStmt *stmt) {
/// TODO
transform(stmt->getExpr());
CHECK(transform(stmt->getExpr()));
}
/// Process `global` statements. Remove them upon completion.
void ScopingVisitor::visit(GlobalStmt *stmt) {
if (!ctx->functionScope)
E(Error::FN_OUTSIDE_ERROR, stmt, stmt->isNonLocal() ? "nonlocal" : "global");
STOP_ERROR(Error::FN_OUTSIDE_ERROR, stmt,
stmt->isNonLocal() ? "nonlocal" : "global");
if (in(ctx->map, stmt->getVar()) || in(ctx->captures, stmt->getVar()))
E(Error::FN_GLOBAL_ASSIGNED, stmt, stmt->getVar());
STOP_ERROR(Error::FN_GLOBAL_ASSIGNED, stmt, stmt->getVar());
visitName(stmt->getVar(), true, stmt, stmt->getSrcInfo());
ctx->captures[stmt->getVar()] = stmt->isNonLocal()
@ -295,7 +398,7 @@ void ScopingVisitor::visit(GlobalStmt *stmt) {
void ScopingVisitor::visit(YieldStmt *stmt) {
if (ctx->functionScope)
ctx->functionScope->setAttribute(Attr::IsGenerator);
transform(stmt->getExpr());
CHECK(transform(stmt->getExpr()));
}
void ScopingVisitor::visit(YieldExpr *expr) {
@ -304,6 +407,87 @@ void ScopingVisitor::visit(YieldExpr *expr) {
}
void ScopingVisitor::visit(FunctionStmt *stmt) {
// Validate
std::vector<Expr *> newDecorators;
for (auto &d : stmt->getDecorators()) {
if (isId(d, Attr::Attribute)) {
if (stmt->getDecorators().size() != 1)
STOP_ERROR(Error::FN_SINGLE_DECORATOR, stmt->getDecorators()[1],
Attr::Attribute);
stmt->setAttribute(Attr::Attribute);
} else if (isId(d, Attr::LLVM)) {
stmt->setAttribute(Attr::LLVM);
} else if (isId(d, Attr::Python)) {
if (stmt->getDecorators().size() != 1)
STOP_ERROR(Error::FN_SINGLE_DECORATOR, stmt->getDecorators()[1], Attr::Python);
stmt->setAttribute(Attr::Python);
} else if (isId(d, Attr::Internal)) {
stmt->setAttribute(Attr::Internal);
} else if (isId(d, Attr::HiddenFromUser)) {
stmt->setAttribute(Attr::HiddenFromUser);
} else if (isId(d, Attr::Atomic)) {
stmt->setAttribute(Attr::Atomic);
} else if (isId(d, Attr::Property)) {
stmt->setAttribute(Attr::Property);
} else if (isId(d, Attr::StaticMethod)) {
stmt->setAttribute(Attr::StaticMethod);
} else if (isId(d, Attr::ForceRealize)) {
stmt->setAttribute(Attr::ForceRealize);
} else if (isId(d, Attr::C)) {
stmt->setAttribute(Attr::C);
} else {
newDecorators.emplace_back(d);
}
}
if (stmt->hasAttribute(Attr::C)) {
for (auto &a : *stmt) {
if (a.getName().size() > 1 && a.getName()[0] == '*' && a.getName()[1] != '*')
stmt->setAttribute(Attr::CVarArg);
}
}
if (!stmt->empty() && !stmt->front().getType() && stmt->front().getName() == "self") {
stmt->setAttribute(Attr::HasSelf);
}
stmt->setDecorators(newDecorators);
if (!stmt->getReturn() &&
(stmt->hasAttribute(Attr::LLVM) || stmt->hasAttribute(Attr::C)))
STOP_ERROR(Error::FN_LLVM, getSrcInfo());
// Set attributes
std::unordered_set<std::string> seenArgs;
bool defaultsStarted = false, hasStarArg = false, hasKwArg = false;
for (size_t ia = 0; ia < stmt->size(); ia++) {
auto &a = (*stmt)[ia];
auto [stars, n] = a.getNameWithStars();
if (stars == 2) {
if (hasKwArg)
STOP_ERROR(Error::FN_MULTIPLE_ARGS, a.getSrcInfo());
if (a.getDefault())
STOP_ERROR(Error::FN_DEFAULT_STARARG, a.getDefault());
if (ia != stmt->size() - 1)
STOP_ERROR(Error::FN_LAST_KWARG, a.getSrcInfo());
hasKwArg = true;
} else if (stars == 1) {
if (hasStarArg)
STOP_ERROR(Error::FN_MULTIPLE_ARGS, a.getSrcInfo());
if (a.getDefault())
STOP_ERROR(Error::FN_DEFAULT_STARARG, a.getDefault());
hasStarArg = true;
}
if (in(seenArgs, n))
STOP_ERROR(Error::FN_ARG_TWICE, a.getSrcInfo(), n);
seenArgs.insert(n);
if (!a.getDefault() && defaultsStarted && !stars && a.isValue())
STOP_ERROR(Error::FN_DEFAULT, a.getSrcInfo(), n);
defaultsStarted |= bool(a.getDefault());
if (stmt->hasAttribute(Attr::C)) {
if (a.getDefault())
STOP_ERROR(Error::FN_C_DEFAULT, a.getDefault(), n);
if (stars != 1 && !a.getType())
STOP_ERROR(Error::FN_C_TYPE, a.getSrcInfo(), n);
}
}
bool isOverload = false;
for (auto &d : stmt->getDecorators())
if (isId(d, "overload")) {
@ -326,7 +510,7 @@ void ScopingVisitor::visit(FunctionStmt *stmt) {
auto [_, n] = a.getNameWithStars();
v.visitName(n, true, stmt, a.getSrcInfo());
if (a.defaultValue)
transform(a.defaultValue);
CHECK(transform(a.defaultValue));
}
c->scope.pop_back();
@ -334,6 +518,10 @@ void ScopingVisitor::visit(FunctionStmt *stmt) {
v.transform(stmt->getSuite());
v.processChildCaptures();
c->scope.pop_back();
if (v.hasErrors())
errors.append(v.errors);
if (!canContinue())
return;
auto b = std::make_unique<BindingsAttribute>();
b->captures = c->captures;
@ -354,14 +542,132 @@ void ScopingVisitor::visit(FunctionStmt *stmt) {
void ScopingVisitor::visit(WithStmt *stmt) {
ConditionalBlock c(ctx.get(), stmt->getSuite());
for (size_t i = 0; i < stmt->size(); i++) {
transform((*stmt)[i]);
CHECK(transform((*stmt)[i]));
if (!stmt->getVars()[i].empty())
visitName(stmt->getVars()[i], true, stmt, stmt->getSrcInfo());
}
transform(stmt->getSuite());
CHECK(transform(stmt->getSuite()));
}
void ScopingVisitor::visit(ClassStmt *stmt) {
// @tuple(init=, repr=, eq=, order=, hash=, pickle=, container=, python=, add=,
// internal=...)
// @dataclass(...)
// @extend
std::map<std::string, bool> tupleMagics = {
{"new", true}, {"repr", false}, {"hash", false},
{"eq", false}, {"ne", false}, {"lt", false},
{"le", false}, {"gt", false}, {"ge", false},
{"pickle", true}, {"unpickle", true}, {"to_py", false},
{"from_py", false}, {"iter", false}, {"getitem", false},
{"len", false}, {"to_gpu", false}, {"from_gpu", false},
{"from_gpu_new", false}, {"tuplesize", true}};
for (auto &d : stmt->getDecorators()) {
if (isId(d, "deduce")) {
stmt->setAttribute(Attr::ClassDeduce);
} else if (isId(d, "__notuple__")) {
stmt->setAttribute(Attr::ClassNoTuple);
} else if (isId(d, "dataclass")) {
} else if (auto c = cast<CallExpr>(d)) {
if (isId(c->getExpr(), Attr::Tuple)) {
stmt->setAttribute(Attr::Tuple);
for (auto &m : tupleMagics)
m.second = true;
} else if (!isId(c->getExpr(), "dataclass")) {
STOP_ERROR(Error::CLASS_BAD_DECORATOR, c->getExpr());
} else if (stmt->hasAttribute(Attr::Tuple)) {
STOP_ERROR(Error::CLASS_CONFLICT_DECORATOR, c, "dataclass", Attr::Tuple);
}
for (const auto &a : *c) {
auto b = cast<BoolExpr>(a);
if (!b)
STOP_ERROR(Error::CLASS_NONSTATIC_DECORATOR, a.getSrcInfo());
char val = char(b->getValue());
if (a.getName() == "init") {
tupleMagics["new"] = val;
} else if (a.getName() == "repr") {
tupleMagics["repr"] = val;
} else if (a.getName() == "eq") {
tupleMagics["eq"] = tupleMagics["ne"] = val;
} else if (a.getName() == "order") {
tupleMagics["lt"] = tupleMagics["le"] = tupleMagics["gt"] =
tupleMagics["ge"] = val;
} else if (a.getName() == "hash") {
tupleMagics["hash"] = val;
} else if (a.getName() == "pickle") {
tupleMagics["pickle"] = tupleMagics["unpickle"] = val;
} else if (a.getName() == "python") {
tupleMagics["to_py"] = tupleMagics["from_py"] = val;
} else if (a.getName() == "gpu") {
tupleMagics["to_gpu"] = tupleMagics["from_gpu"] =
tupleMagics["from_gpu_new"] = val;
} else if (a.getName() == "container") {
tupleMagics["iter"] = tupleMagics["getitem"] = val;
} else {
STOP_ERROR(Error::CLASS_BAD_DECORATOR_ARG, a.getSrcInfo());
}
}
} else if (isId(d, Attr::Tuple)) {
if (stmt->hasAttribute(Attr::Tuple))
STOP_ERROR(Error::CLASS_MULTIPLE_DECORATORS, d, Attr::Tuple);
stmt->setAttribute(Attr::Tuple);
for (auto &m : tupleMagics) {
m.second = true;
}
} else if (isId(d, Attr::Extend)) {
stmt->setAttribute(Attr::Extend);
if (stmt->getDecorators().size() != 1) {
STOP_ERROR(
Error::CLASS_SINGLE_DECORATOR,
stmt->getDecorators()[stmt->getDecorators().front() == d]->getSrcInfo(),
Attr::Extend);
}
} else if (isId(d, Attr::Internal)) {
stmt->setAttribute(Attr::Internal);
} else {
STOP_ERROR(Error::CLASS_BAD_DECORATOR, d);
}
}
if (stmt->hasAttribute(Attr::ClassDeduce))
tupleMagics["new"] = false;
if (!stmt->hasAttribute(Attr::Tuple)) {
tupleMagics["init"] = tupleMagics["new"];
tupleMagics["new"] = tupleMagics["raw"] = true;
tupleMagics["len"] = false;
}
tupleMagics["dict"] = true;
// Internal classes do not get any auto-generated members.
std::vector<std::string> magics;
if (!stmt->hasAttribute(Attr::Internal)) {
for (auto &m : tupleMagics)
if (m.second) {
if (m.first == "new")
magics.insert(magics.begin(), m.first);
else
magics.push_back(m.first);
}
}
stmt->setAttribute(Attr::ClassMagic,
std::make_unique<ir::StringListAttribute>(magics));
std::unordered_set<std::string> seen;
if (stmt->hasAttribute(Attr::Extend) && !stmt->empty())
STOP_ERROR(Error::CLASS_EXTENSION, stmt->front().getSrcInfo());
if (stmt->hasAttribute(Attr::Extend) &&
!(stmt->getBaseClasses().empty() && stmt->getStaticBaseClasses().empty())) {
STOP_ERROR(Error::CLASS_EXTENSION, stmt->getBaseClasses().empty()
? stmt->getStaticBaseClasses().front()
: stmt->getBaseClasses().front());
}
for (auto &a : *stmt) {
if (!a.getType() && !a.getDefault())
STOP_ERROR(Error::CLASS_MISSING_TYPE, a.getSrcInfo(), a.getName());
if (in(seen, a.getName()))
STOP_ERROR(Error::CLASS_ARG_TWICE, a.getSrcInfo(), a.getName());
seen.insert(a.getName());
}
if (stmt->hasAttribute(Attr::Extend))
visitName(stmt->getName());
else
@ -380,13 +686,15 @@ void ScopingVisitor::visit(ClassStmt *stmt) {
}
v.transform(stmt->getSuite());
c->scope.pop_back();
if (v.hasErrors())
errors.append(v.errors);
if (!canContinue())
return;
// for (auto &d : stmt->decorators)
// transform(d);
for (auto &d : stmt->getBaseClasses())
transform(d);
CHECK(transform(d));
for (auto &d : stmt->getStaticBaseClasses())
transform(d);
CHECK(transform(d));
}
void ScopingVisitor::processChildCaptures() {
@ -396,7 +704,6 @@ void ScopingVisitor::processChildCaptures() {
continue;
}
if (!findDominatingBinding(n.first)) {
// LOG("-> add: {} / {}", ctx->functionScope ? ctx->functionScope->name : "-", n);
ctx->captures.insert(n); // propagate!
}
}
@ -404,7 +711,6 @@ void ScopingVisitor::processChildCaptures() {
void ScopingVisitor::switchToUpdate(ASTNode *binding, const std::string &name,
bool gotUsedVar) {
// LOG("switch: {} {} : {}", name, gotUsedVar, binding ? binding->toString(0) : "-");
if (binding && binding->hasAttribute(Attr::Bindings)) {
binding->getAttribute<BindingsAttribute>(Attr::Bindings)->bindings.erase(name);
}
@ -414,9 +720,9 @@ void ScopingVisitor::switchToUpdate(ASTNode *binding, const std::string &name,
binding->setAttribute(gotUsedVar ? Attr::ExprDominatedUsed : Attr::ExprDominated);
}
if (cast<FunctionStmt>(binding))
E(error::Error::ID_INVALID_BIND, binding, name);
if (cast<ClassStmt>(binding))
E(error::Error::ID_INVALID_BIND, binding, name);
STOP_ERROR(error::Error::ID_INVALID_BIND, binding, name);
else if (cast<ClassStmt>(binding))
STOP_ERROR(error::Error::ID_INVALID_BIND, binding, name);
}
bool ScopingVisitor::visitName(const std::string &name, bool adding, ASTNode *root,
@ -425,9 +731,10 @@ bool ScopingVisitor::visitName(const std::string &name, bool adding, ASTNode *ro
return false;
if (adding) {
if (auto p = in(ctx->captures, name)) {
if (*p == BindingsAttribute::CaptureType::Read)
E(error::Error::ASSIGN_LOCAL_REFERENCE, ctx->firstSeen[name], name, src);
else if (root) { // global, nonlocal
if (*p == BindingsAttribute::CaptureType::Read) {
addError(error::Error::ASSIGN_LOCAL_REFERENCE, ctx->firstSeen[name], name, src);
return false;
} else if (root) { // global, nonlocal
switchToUpdate(root, name, false);
}
} else {
@ -441,15 +748,11 @@ bool ScopingVisitor::visitName(const std::string &name, bool adding, ASTNode *ro
ctx->scope.front()
.suite->getAttribute<BindingsAttribute>(Attr::Bindings)
->bindings[name] = false;
// LOG("-> promote {} / F", name);
auto newItem = ScopingVisitor::Context::Item(src, newScope, nullptr);
ctx->map[name].push_back(newItem);
// LOG("add: {} | {} := {} {}", src, name, newScope, "-");
}
}
ctx->map[name].emplace_front(src, ctx->getScope(), root);
// LOG("add: {} | {} := {} {}", src, name, ctx->getScope(),
// root ? root->toString(-1) : "-");
}
} else {
if (!in(ctx->firstSeen, name))
@ -478,8 +781,6 @@ bool ScopingVisitor::visitName(const std::string &name, bool adding, ASTNode *ro
}
// Variable binding check for variables that are defined within conditional blocks
// LOG("{} : var {}: {} vs {}", getSrcInfo(), name, val->accessChecked,
// ctx->getScope());
if (!val->accessChecked.empty()) {
bool checked = false;
for (size_t ai = val->accessChecked.size(); ai-- > 0;) {
@ -573,10 +874,6 @@ ScopingVisitor::findDominatingBinding(const std::string &name, bool allowShadow)
auto newItem = ScopingVisitor::Context::Item(
getSrcInfo(), newScope, ctx->scope[si].suite, {lastGood->scope});
lastGood = it->insert(++lastGood, newItem);
// LOG("-> {}: promote {} / T from {} to {} | {}",
// getSrcInfo(),
// name, ctx->getScope(),
// lastGood->scope, lastGood->accessChecked);
gotUsedVar = true;
break;
}

View File

@ -34,7 +34,7 @@ private:
std::ostream &doFormat(std::ostream &os) const override { return os << "Bindings"; }
};
class ScopingVisitor : public CallbackASTVisitor<void, void> {
class ScopingVisitor : public CallbackASTVisitor<bool, bool> {
struct Context {
/// A pointer to the shared cache.
Cache *cache;
@ -112,34 +112,55 @@ class ScopingVisitor : public CallbackASTVisitor<void, void> {
};
public:
static void apply(Cache *, Stmt *s);
void transform(Expr *expr) override;
void transform(Stmt *stmt) override;
ParserErrors errors;
bool hasErrors() const { return !errors.empty(); }
bool canContinue() const { return errors.size() <= MAX_ERRORS; }
template <class... TA>
void addError(error::Error e, const SrcInfo &o, const TA &...args) {
auto msg =
ErrorMessage(error::Emsg(e, args...), o.file, o.line, o.col, o.len, int(e));
errors.addError({msg});
}
template <class... TA> void addError(error::Error e, ASTNode *o, const TA &...args) {
this->addError(e, o->getSrcInfo(), args...);
}
void addError(llvm::Error &&e) {
llvm::handleAllErrors(std::move(e), [this](const error::ParserErrorInfo &e) {
this->errors.append(e.getErrors());
});
}
static llvm::Error apply(Cache *, Stmt *s);
bool transform(Expr *expr) override;
bool transform(Stmt *stmt) override;
// Can error!
bool visitName(const std::string &name, bool = false, ASTNode * = nullptr,
const SrcInfo & = SrcInfo());
void transformAdding(Expr *e, ASTNode *);
void transformScope(Expr *);
void transformScope(Stmt *);
bool transformAdding(Expr *e, ASTNode *);
bool transformScope(Expr *);
bool transformScope(Stmt *);
void visit(IdExpr *) override;
void visit(StringExpr *) override;
void visit(IdExpr *) override;
void visit(GeneratorExpr *) override;
void visit(AssignExpr *) override;
void visit(LambdaExpr *) override;
void visit(IfExpr *) override;
void visit(BinaryExpr *) override;
void visit(LambdaExpr *) override;
void visit(YieldExpr *) override;
void visit(AssignExpr *) override;
void visit(AssignStmt *) override;
void visit(IfStmt *) override;
void visit(MatchStmt *) override;
void visit(DelStmt *) override;
void visit(YieldStmt *) override;
void visit(WhileStmt *) override;
void visit(ForStmt *) override;
void visit(IfStmt *) override;
void visit(MatchStmt *) override;
void visit(ImportStmt *) override;
void visit(DelStmt *) override;
void visit(TryStmt *) override;
void visit(GlobalStmt *) override;
void visit(YieldStmt *) override;
void visit(FunctionStmt *) override;
void visit(ClassStmt *) override;
void visit(WithStmt *) override;
@ -148,6 +169,8 @@ public:
void processChildCaptures();
void switchToUpdate(ASTNode *binding, const std::string &, bool);
std::vector<StringExpr::String> unpackFString(const std::string &value);
template <typename Tn, typename... Ts> Tn *N(Ts &&...args) {
Tn *t = ctx->cache->N<Tn>(std::forward<Ts>(args)...);
t->setSrcInfo(getSrcInfo());

View File

@ -83,7 +83,7 @@ void TypecheckVisitor::visit(DotExpr *expr) {
// Check if this is being called from CallExpr (e.g., foo.bar(...))
// and mark it as such (to inline useless partial expression)
CallExpr *parentCall = cast<CallExpr>(ctx->getParentNode());
if (parentCall && !parentCall->hasAttribute("CallExpr"))
if (parentCall && !parentCall->hasAttribute(Attr::ParentCallExpr))
parentCall = nullptr;
// Flatten imports:
@ -366,7 +366,8 @@ TypecheckVisitor::getImport(const std::vector<std::string> &chain) {
getRootModulePath(), getPluginImportPaths());
if (file) { // auto-load support
Stmt *s = N<SuiteStmt>(N<ImportStmt>(N<IdExpr>(chain[importEnd]), nullptr));
ScopingVisitor::apply(ctx->cache, s);
if (auto err = ScopingVisitor::apply(ctx->cache, s))
throw exc::ParserException(std::move(err));
s = TypecheckVisitor(import->ctx, preamble).transform(s);
prependStmts->push_back(s);
return getImport(chain);
@ -412,7 +413,7 @@ types::FuncType *TypecheckVisitor::getDispatch(const std::string &fn) {
auto ast = N<FunctionStmt>(name, nullptr,
std::vector<Param>{Param("*" + nar), Param("**" + nkw)},
N<SuiteStmt>(N<ReturnStmt>(root)));
ast->setAttribute("autogenerated");
ast->setAttribute(Attr::AutoGenerated);
ast->setAttribute(Attr::Module, ctx->moduleName.path);
ctx->cache->reverseIdentifierLookup[name] = getUnmangledName(fn);

View File

@ -30,6 +30,11 @@ 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) {
if (cast<TupleExpr>(stmt->lhs) || cast<ListExpr>(stmt->lhs)) {
resultStmt = transform(unpackAssignment(stmt->lhs, stmt->rhs));
return;
}
bool mustUpdate = stmt->isUpdate() || stmt->isAtomicUpdate();
mustUpdate |= stmt->hasAttribute(Attr::ExprDominated);
mustUpdate |= stmt->hasAttribute(Attr::ExprDominatedUsed);
@ -82,6 +87,80 @@ void TypecheckVisitor::visit(DelStmt *stmt) {
}
}
/// Unpack an assignment expression `lhs = rhs` into a list of simple assignment
/// expressions (e.g., `a = b`, `a.x = b`, or `a[x] = b`).
/// Handle Python unpacking rules.
/// @example
/// `(a, b) = c` -> `a = c[0]; b = c[1]`
/// `a, b = c` -> `a = c[0]; b = c[1]`
/// `[a, *x, b] = c` -> `a = c[0]; x = c[1:-1]; b = c[-1]`.
/// Non-trivial right-hand expressions are first stored in a temporary variable.
/// @example
/// `a, b = c, d + foo()` -> `assign = (c, d + foo); a = assign[0]; b = assign[1]`.
/// Each assignment is unpacked recursively to allow cases like `a, (b, c) = d`.
Stmt *TypecheckVisitor::unpackAssignment(Expr *lhs, Expr *rhs) {
std::vector<Expr *> leftSide;
if (auto et = cast<TupleExpr>(lhs)) {
// Case: (a, b) = ...
for (auto *i : *et)
leftSide.push_back(i);
} else if (auto el = cast<ListExpr>(lhs)) {
// Case: [a, b] = ...
for (auto *i : *el)
leftSide.push_back(i);
}
// Prepare the right-side expression
auto oldSrcInfo = getSrcInfo();
setSrcInfo(rhs->getSrcInfo());
auto srcPos = rhs;
SuiteStmt *block = N<SuiteStmt>();
if (!cast<IdExpr>(rhs)) {
// Store any non-trivial right-side expression into a variable
auto var = getTemporaryVar("assign");
auto newRhs = N<IdExpr>(var);
block->addStmt(N<AssignStmt>(newRhs, ast::clone(rhs)));
rhs = newRhs;
}
// Process assignments until the fist StarExpr (if any)
size_t st = 0;
for (; st < leftSide.size(); st++) {
if (cast<StarExpr>(leftSide[st]))
break;
// Transformation: `leftSide_st = rhs[st]` where `st` is static integer
auto rightSide = N<IndexExpr>(ast::clone(rhs), N<IntExpr>(st));
// Recursively process the assignment because of cases like `(a, (b, c)) = d)`
auto ns = unpackAssignment(leftSide[st], rightSide);
block->addStmt(ns);
}
// Process StarExpr (if any) and the assignments that follow it
if (st < leftSide.size() && cast<StarExpr>(leftSide[st])) {
// StarExpr becomes SliceExpr (e.g., `b` in `(a, *b, c) = d` becomes `d[1:-2]`)
auto rightSide = N<IndexExpr>(
ast::clone(rhs),
N<SliceExpr>(N<IntExpr>(st),
// this slice is either [st:] or [st:-lhs_len + st + 1]
leftSide.size() == st + 1 ? nullptr
: N<IntExpr>(-leftSide.size() + st + 1),
nullptr));
auto ns = unpackAssignment(cast<StarExpr>(leftSide[st])->getExpr(), rightSide);
block->addStmt(ns);
st += 1;
// Process remaining assignments. They will use negative indices (-1, -2 etc.)
// because we do not know how big is StarExpr
for (; st < leftSide.size(); st++) {
if (cast<StarExpr>(leftSide[st]))
E(Error::ASSIGN_MULTI_STAR, leftSide[st]->getSrcInfo());
rightSide = N<IndexExpr>(ast::clone(rhs), N<IntExpr>(-int(leftSide.size() - st)));
auto ns = unpackAssignment(leftSide[st], rightSide);
block->addStmt(ns);
}
}
setSrcInfo(oldSrcInfo);
return block;
}
/// Transform simple assignments.
/// @example
/// `a[x] = b` -> `a.__setitem__(x, b)`

View File

@ -60,21 +60,23 @@ void TypecheckVisitor::visit(EllipsisExpr *expr) {
void TypecheckVisitor::visit(CallExpr *expr) {
auto orig = expr->toString(0);
if (match(expr->getExpr(), M<IdExpr>("tuple")) && expr->size() == 1)
expr->setAttribute("TupleFn");
expr->setAttribute(Attr::TupleCall);
validateCall(expr);
// Check if this call is partial call
PartialCallData part;
expr->setAttribute("CallExpr");
expr->setAttribute(Attr::ParentCallExpr);
expr->expr = transform(expr->getExpr());
expr->eraseAttribute("CallExpr");
expr->eraseAttribute(Attr::ParentCallExpr);
if (isUnbound(expr->getExpr()))
return; // delay
auto [calleeFn, newExpr] = getCalleeFn(expr, part);
// Transform `tuple(i for i in tup)` into a GeneratorExpr that will be handled during
// the type checking.
if (!calleeFn && expr->hasAttribute("TupleFn")) {
if (!calleeFn && expr->hasAttribute(Attr::TupleCall)) {
if (cast<GeneratorExpr>(expr->begin()->getExpr())) {
auto g = cast<GeneratorExpr>(expr->begin()->getExpr());
if (!g || g->kind != GeneratorExpr::Generator || g->loopCount() != 1)
@ -244,6 +246,24 @@ void TypecheckVisitor::visit(CallExpr *expr) {
}
}
void TypecheckVisitor::validateCall(CallExpr *expr) {
if (expr->hasAttribute(Attr::Validated))
return;
bool namesStarted = false, foundEllipsis = false;
for (auto &a : *expr) {
if (a.name.empty() && namesStarted &&
!(cast<KeywordStarExpr>(a.value) || cast<EllipsisExpr>(a.value)))
E(Error::CALL_NAME_ORDER, a.value);
if (!a.name.empty() && (cast<StarExpr>(a.value) || cast<KeywordStarExpr>(a.value)))
E(Error::CALL_NAME_STAR, a.value);
if (cast<EllipsisExpr>(a.value) && foundEllipsis)
E(Error::CALL_ELLIPSIS, a.value);
foundEllipsis |= bool(cast<EllipsisExpr>(a.value));
namesStarted |= !a.name.empty();
}
expr->setAttribute(Attr::Validated);
}
/// Transform call arguments. Expand *args and **kwargs to the list of @c CallArg
/// objects.
/// @return false if expansion could not be completed; true otherwise
@ -346,10 +366,10 @@ TypecheckVisitor::getCalleeFn(CallExpr *expr, PartialCallData &part) {
return {nullptr, nullptr};
auto clsName = typ->name;
if (typ->isRecord()) {
if (expr->hasAttribute("TupleFn")) {
if (expr->hasAttribute(Attr::TupleCall)) {
if (extractType(expr->getExpr())->is(TYPE_TUPLE))
return {nullptr, nullptr};
expr->eraseAttribute("TupleFn");
expr->eraseAttribute(Attr::TupleCall);
}
// Case: tuple constructor. Transform to: `T.__new__(args)`
auto e =
@ -419,7 +439,7 @@ TypecheckVisitor::getCalleeFn(CallExpr *expr, PartialCallData &part) {
/// `foo(1, 2, baz=3, baf=4)` -> `foo(a=1, baz=2, args=(3, ), kwargs=KwArgs(baf=4))`
Expr *TypecheckVisitor::callReorderArguments(FuncType *calleeFn, CallExpr *expr,
PartialCallData &part) {
if (calleeFn->ast->hasAttribute("std.internal.attributes.no_arg_reorder.0:0"))
if (calleeFn->ast->hasAttribute(Attr::NoArgReorder))
return nullptr;
std::vector<CallArg> args; // stores ordered and processed arguments
@ -546,7 +566,7 @@ Expr *TypecheckVisitor::callReorderArguments(FuncType *calleeFn, CallExpr *expr,
reorderNamedArgs(
calleeFn, expr->items, reorderFn,
[&](error::Error e, const SrcInfo &o, const std::string &errorMsg) {
error::raise_error(e, o, errorMsg);
E(e, o, errorMsg);
return -1;
},
part.known);
@ -585,7 +605,7 @@ Expr *TypecheckVisitor::callReorderArguments(FuncType *calleeFn, CallExpr *expr,
} else {
if (isUnbound(gen.getType()) && !(*calleeFn->ast)[si].getDefault() &&
!partial && in(niGenerics, gen.name)) {
error("generic '{}' not provided", gen.niceName);
E(Error::CUSTOM, getSrcInfo(), "generic '{}' not provided", gen.niceName);
}
}
}

View File

@ -253,7 +253,6 @@ void TypecheckVisitor::visit(ClassStmt *stmt) {
cls.ast->baseClasses = stmt->baseClasses;
for (auto &b : staticBaseASTs)
cls.staticParentClasses.emplace_back(b->getClass()->name);
cls.ast->validate();
cls.module = ctx->moduleName.path;
// Codegen default magic methods
@ -269,7 +268,7 @@ void TypecheckVisitor::visit(ClassStmt *stmt) {
for (auto &mf : getOverloads(mm.second)) {
const auto &fp = getFunction(mf);
auto f = fp->origAst;
if (f && !f->hasAttribute("autogenerated")) {
if (f && !f->hasAttribute(Attr::AutoGenerated)) {
fnStmts.push_back(
cast<FunctionStmt>(withClassGenerics(base->getClass(), [&]() {
auto cf = clean_clone(f);
@ -755,7 +754,7 @@ types::ClassType *TypecheckVisitor::generateTuple(size_t n, bool generateNew) {
ext->setAttribute(Attr::Extend);
ext = N<SuiteStmt>(ext);
ScopingVisitor::apply(ctx->cache, ext);
llvm::cantFail(ScopingVisitor::apply(ctx->cache, ext));
auto rctx = getImport(STDLIB_IMPORT)->ctx;
auto oldBases = rctx->bases;
rctx->bases.clear();

View File

@ -29,7 +29,8 @@ void TypecheckVisitor::visit(LambdaExpr *expr) {
params.emplace_back(s);
Stmt *f = N<FunctionStmt>(name, nullptr, params,
N<SuiteStmt>(N<ReturnStmt>(expr->getExpr())));
ScopingVisitor::apply(ctx->cache, N<SuiteStmt>(f));
if (auto err = ScopingVisitor::apply(ctx->cache, N<SuiteStmt>(f)))
throw exc::ParserException(std::move(err));
f = transform(f);
if (auto a = expr->getAttribute(Attr::Bindings))
f->setAttribute(Attr::Bindings, a->clone());
@ -557,7 +558,10 @@ Stmt *TypecheckVisitor::transformLLVMDefinition(Stmt *codeStmt) {
std::string exprCode = code.substr(braceStart, i - braceStart);
auto offset = getSrcInfo();
offset.col += i;
auto expr = transform(parseExpr(ctx->cache, exprCode, offset).first, true);
auto exprOrErr = parseExpr(ctx->cache, exprCode, offset);
if (!exprOrErr)
throw exc::ParserException(exprOrErr.takeError());
auto expr = transform(exprOrErr->first, true);
items.push_back(N<ExprStmt>(expr));
braceStart = i + 1;
finalCode += '}';

View File

@ -357,13 +357,16 @@ Stmt *TypecheckVisitor::transformNewImport(const ImportFile &file) {
getImport(STDLIB_IMPORT)->ctx->addToplevel(importVar, val);
registerGlobal(val->getName());
}
n = N<SuiteStmt>(n, parseFile(ctx->cache, file.path));
auto nodeOrErr = parseFile(ctx->cache, file.path);
if (!nodeOrErr)
throw exc::ParserException(nodeOrErr.takeError());
n = N<SuiteStmt>(n, *nodeOrErr);
auto tv = TypecheckVisitor(ictx, preamble);
ScopingVisitor::apply(ctx->cache, n);
// n = tv.transform(n);
if (auto err = ScopingVisitor::apply(ctx->cache, n))
throw exc::ParserException(std::move(err));
if (!ctx->cache->errors.empty())
throw exc::ParserException();
throw exc::ParserException(ctx->cache->errors);
// Add comment to the top of import for easier dump inspection
auto comment = N<CommentStmt>(format("import: {} at {}", file.module, file.path));
auto suite = N<SuiteStmt>(comment, n);

View File

@ -51,9 +51,9 @@ Stmt *TypecheckVisitor::inferTypes(Stmt *result, bool isToplevel) {
LOG_TYPECHECK("[iter] {} :: {}", ctx->getBase()->name, ctx->getBase()->iteration);
if (ctx->getBase()->iteration >= MAX_TYPECHECK_ITER) {
LOG("[error=>] {}", result->toString(2));
error(result, "cannot typecheck '{}' in reasonable time",
ctx->getBase()->name.empty() ? "toplevel"
: getUnmangledName(ctx->getBase()->name));
E(Error::CUSTOM, result, "cannot typecheck '{}' in reasonable time",
ctx->getBase()->name.empty() ? "toplevel"
: getUnmangledName(ctx->getBase()->name));
}
// Keep iterating until:
@ -92,7 +92,8 @@ Stmt *TypecheckVisitor::inferTypes(Stmt *result, bool isToplevel) {
if (!tu->isSealed()) {
if (tu->pendingTypes[0]->getLink() &&
tu->pendingTypes[0]->getLink()->kind == LinkType::Unbound) {
tu->addType(getStdLibType("NoneType"));
auto r = tu->addType(getStdLibType("NoneType"));
seqassert(r, "cannot add type to union {}", tu->debugString(2));
tu->seal();
}
}
@ -160,12 +161,13 @@ types::Type *TypecheckVisitor::realize(types::Type *typ) {
auto t = realizeType(c);
return t;
}
} catch (exc::ParserException &e) {
if (e.errorCode == Error::MAX_REALIZATION)
} catch (exc::ParserException &exc) {
auto &bt = exc.getErrors().getLast();
if (bt.front().getErrorCode() == Error::MAX_REALIZATION)
throw;
if (auto f = typ->getFunc()) {
if (f->ast->hasAttribute(Attr::HiddenFromUser)) {
e.locations.back() = getSrcInfo();
bt.back().setSrcInfo(getSrcInfo());
} else {
std::vector<std::string> args;
for (size_t i = 0, ai = 0, gi = 0; i < f->ast->size(); i++) {
@ -188,10 +190,12 @@ types::Type *TypecheckVisitor::realize(types::Type *typ) {
name = getUnmangledName(f->ast->name);
name_args = fmt::format("({})", fmt::join(args, ", "));
}
e.trackRealize(fmt::format("{}{}", name, name_args), getSrcInfo());
bt.addMessage(fmt::format("during the realization of {}{}", name, name_args),
getSrcInfo());
}
} else {
e.trackRealize(typ->prettyString(), getSrcInfo());
bt.addMessage(fmt::format("during the realization of {}", typ->prettyString()),
getSrcInfo());
}
throw;
}
@ -415,7 +419,7 @@ types::Type *TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) {
// Lambda typecheck failures are "ignored" as they are treated as statements,
// not functions.
// TODO: generalize this further.
error("cannot typecheck the program [1]");
E(Error::CUSTOM, getSrcInfo(), "cannot typecheck the program [1]");
}
this->ctx = oldCtx;
return nullptr; // inference must be delayed

View File

@ -116,6 +116,14 @@ void TypecheckVisitor::visit(ForStmt *stmt) {
if (!iterType)
return; // wait until the iterator is known
// Replace for (i, j) in ... { ... } with for tmp in ...: { i, j = tmp ; ... }
if (!cast<IdExpr>(stmt->getVar())) {
auto var = N<IdExpr>(ctx->cache->getTemporaryVar("for"));
auto ns = unpackAssignment(stmt->getVar(), var);
stmt->suite = N<SuiteStmt>(ns, stmt->getSuite());
stmt->var = var;
}
auto [delay, staticLoop] = transformStaticForLoop(stmt);
if (delay)
return;
@ -201,8 +209,12 @@ Expr *TypecheckVisitor::transformForDecorator(Expr *decorator) {
for (auto &a : *c) {
if (a.getName() == "openmp" ||
(a.getName().empty() && openmp.empty() && cast<StringExpr>(a.getExpr()))) {
omp = parseOpenMP(ctx->cache, cast<StringExpr>(a.getExpr())->getValue(),
a.value->getSrcInfo());
auto ompOrErr =
parseOpenMP(ctx->cache, cast<StringExpr>(a.getExpr())->getValue(),
a.value->getSrcInfo());
if (!ompOrErr)
throw exc::ParserException(ompOrErr.takeError());
omp = *ompOrErr;
} else {
args.emplace_back(a.getName(), transform(a.getExpr()));
}

View File

@ -398,10 +398,13 @@ void TypecheckVisitor::visit(InstantiateExpr *expr) {
if (!isTypeExpr((*expr)[i]))
E(Error::EXPECTED_TYPE, (*expr)[i], "type");
}
if (isUnion)
typ->getUnion()->addType(t.get());
else
if (isUnion) {
if (!typ->getUnion()->addType(t.get()))
E(error::Error::UNION_TOO_BIG, (*expr)[i],
typ->getUnion()->pendingTypes.size());
} else {
unify(t.get(), generics[i].getType());
}
}
if (isUnion) {
typ->getUnion()->seal();

View File

@ -159,7 +159,7 @@ FunctionStmt *TypecheckVisitor::generateThunkAST(FuncType *fp, ClassType *base,
auto thunkAst = N<FunctionStmt>(
thunkName, nullptr, fnArgs,
N<SuiteStmt>(N<ReturnStmt>(N<CallExpr>(N<IdExpr>(m->ast->name), callArgs))));
thunkAst->setAttribute("std.internal.attributes.inline.0:0");
thunkAst->setAttribute(Attr::Inline);
return cast<FunctionStmt>(transform(thunkAst));
}
@ -278,7 +278,7 @@ SuiteStmt *TypecheckVisitor::generateUnionTagAST(FuncType *type) {
SuiteStmt *TypecheckVisitor::generateNamedKeysAST(FuncType *type) {
auto n = getIntLiteral(extractFuncGeneric(type));
if (n < 0 || n >= ctx->cache->generatedTupleNames.size())
error("bad namedkeys index");
E(Error::CUSTOM, getSrcInfo(), "bad namedkeys index");
std::vector<Expr *> s;
for (auto &k : ctx->cache->generatedTupleNames[n])
s.push_back(N<StringExpr>(k));
@ -304,11 +304,11 @@ SuiteStmt *TypecheckVisitor::generateTupleMulAST(FuncType *type) {
SuiteStmt *TypecheckVisitor::generateSpecialAst(types::FuncType *type) {
// Clone the generic AST that is to be realized
auto ast = type->ast;
if (ast->hasAttribute("autogenerated") && endswith(ast->name, ".__iter__") &&
if (ast->hasAttribute(Attr::AutoGenerated) && endswith(ast->name, ".__iter__") &&
extractFuncArgType(type, 0)->getHeterogenousTuple()) {
// Special case: do not realize auto-generated heterogenous __iter__
E(Error::EXPECTED_TYPE, getSrcInfo(), "iterable");
} else if (ast->hasAttribute("autogenerated") &&
} else if (ast->hasAttribute(Attr::AutoGenerated) &&
endswith(ast->name, ".__getitem__") &&
extractFuncArgType(type, 0)->getHeterogenousTuple()) {
// Special case: do not realize auto-generated heterogenous __getitem__
@ -799,8 +799,8 @@ Expr *TypecheckVisitor::transformStaticFnCanCall(CallExpr *expr) {
Expr *TypecheckVisitor::transformStaticFnArgHasType(CallExpr *expr) {
auto fn = extractFunction(expr->begin()->getExpr()->getType());
if (!fn)
error("expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
E(Error::CUSTOM, getSrcInfo(), "expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
auto idx = extractFuncGeneric(expr->getExpr()->getType())->getIntStatic();
seqassert(idx, "expected a static integer");
return transform(N<BoolExpr>(idx->value >= 0 && idx->value < fn->size() &&
@ -810,20 +810,20 @@ Expr *TypecheckVisitor::transformStaticFnArgHasType(CallExpr *expr) {
Expr *TypecheckVisitor::transformStaticFnArgGetType(CallExpr *expr) {
auto fn = extractFunction(expr->begin()->getExpr()->getType());
if (!fn)
error("expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
E(Error::CUSTOM, getSrcInfo(), "expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
auto idx = extractFuncGeneric(expr->getExpr()->getType())->getIntStatic();
seqassert(idx, "expected a static integer");
if (idx->value < 0 || idx->value >= fn->size() || !(*fn)[idx->value]->canRealize())
error("argument does not have type");
E(Error::CUSTOM, getSrcInfo(), "argument does not have type");
return transform(N<IdExpr>((*fn)[idx->value]->realizedName()));
}
Expr *TypecheckVisitor::transformStaticFnArgs(CallExpr *expr) {
auto fn = extractFunction(expr->begin()->value->getType());
if (!fn)
error("expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
E(Error::CUSTOM, getSrcInfo(), "expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
std::vector<Expr *> v;
v.reserve(fn->ast->size());
for (const auto &a : *fn->ast) {
@ -837,24 +837,24 @@ Expr *TypecheckVisitor::transformStaticFnArgs(CallExpr *expr) {
Expr *TypecheckVisitor::transformStaticFnHasDefault(CallExpr *expr) {
auto fn = extractFunction(expr->begin()->getExpr()->getType());
if (!fn)
error("expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
E(Error::CUSTOM, getSrcInfo(), "expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
auto idx = extractFuncGeneric(expr->getExpr()->getType())->getIntStatic();
seqassert(idx, "expected a static integer");
if (idx->value < 0 || idx->value >= fn->ast->size())
error("argument out of bounds");
E(Error::CUSTOM, getSrcInfo(), "argument out of bounds");
return transform(N<BoolExpr>((*fn->ast)[idx->value].getDefault() != nullptr));
}
Expr *TypecheckVisitor::transformStaticFnGetDefault(CallExpr *expr) {
auto fn = extractFunction(expr->begin()->getExpr()->getType());
if (!fn)
error("expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
E(Error::CUSTOM, getSrcInfo(), "expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
auto idx = extractFuncGeneric(expr->getExpr()->getType())->getIntStatic();
seqassert(idx, "expected a static integer");
if (idx->value < 0 || idx->value >= fn->ast->size())
error("argument out of bounds");
E(Error::CUSTOM, getSrcInfo(), "argument out of bounds");
return transform((*fn->ast)[idx->value].getDefault());
}
@ -865,8 +865,8 @@ Expr *TypecheckVisitor::transformStaticFnWrapCallArgs(CallExpr *expr) {
auto fn = extractFunction(expr->begin()->getExpr()->getType());
if (!fn)
error("expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
E(Error::CUSTOM, getSrcInfo(), "expected a function, got '{}'",
expr->begin()->getExpr()->getType()->prettyString());
std::vector<CallArg> callArgs;
if (auto tup = cast<TupleExpr>((*expr)[1].getExpr()->getOrigExpr())) {
@ -924,7 +924,7 @@ Expr *TypecheckVisitor::transformStaticTupleType(CallExpr *expr) {
types::TypePtr typ = nullptr;
auto f = getClassFields(t);
if (n < 0 || n >= f.size())
error("invalid index");
E(Error::CUSTOM, getSrcInfo(), "invalid index");
auto rt = realize(instantiateType(f[n].getType(), t));
return transform(N<IdExpr>(rt->realizedName()));
}
@ -936,7 +936,7 @@ TypecheckVisitor::populateStaticTupleLoop(Expr *iter,
auto stmt = N<AssignStmt>(N<IdExpr>(vars[0]), nullptr, nullptr);
auto call = cast<CallExpr>(cast<CallExpr>(iter)->front());
if (vars.size() != 1)
error("expected one item");
E(Error::CUSTOM, getSrcInfo(), "expected one item");
for (auto &a : *call) {
stmt->rhs = transform(clean_clone(a.value));
if (auto st = stmt->rhs->getType()->getStatic()) {
@ -953,7 +953,7 @@ std::vector<Stmt *>
TypecheckVisitor::populateSimpleStaticRangeLoop(Expr *iter,
const std::vector<std::string> &vars) {
if (vars.size() != 1)
error("expected one item");
E(Error::CUSTOM, getSrcInfo(), "expected one item");
auto fn =
cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;
auto stmt = N<AssignStmt>(N<IdExpr>(vars[0]), nullptr, nullptr);
@ -973,7 +973,7 @@ std::vector<Stmt *>
TypecheckVisitor::populateStaticRangeLoop(Expr *iter,
const std::vector<std::string> &vars) {
if (vars.size() != 1)
error("expected one item");
E(Error::CUSTOM, getSrcInfo(), "expected one item");
auto fn =
cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;
auto stmt = N<AssignStmt>(N<IdExpr>(vars[0]), nullptr, nullptr);
@ -996,7 +996,7 @@ std::vector<Stmt *>
TypecheckVisitor::populateStaticFnOverloadsLoop(Expr *iter,
const std::vector<std::string> &vars) {
if (vars.size() != 1)
error("expected one item");
E(Error::CUSTOM, getSrcInfo(), "expected one item");
auto fn =
cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;
auto stmt = N<AssignStmt>(N<IdExpr>(vars[0]), nullptr, nullptr);
@ -1012,7 +1012,7 @@ TypecheckVisitor::populateStaticFnOverloadsLoop(Expr *iter,
if (isDispatch(method) || !cfn->type)
continue;
if (typ->getHeterogenousTuple()) {
if (cfn->ast->hasAttribute("autogenerated") &&
if (cfn->ast->hasAttribute(Attr::AutoGenerated) &&
(endswith(cfn->ast->name, ".__iter__") ||
endswith(cfn->ast->name, ".__getitem__"))) {
// ignore __getitem__ and other heterogenuous methods
@ -1030,7 +1030,7 @@ std::vector<Stmt *>
TypecheckVisitor::populateStaticEnumerateLoop(Expr *iter,
const std::vector<std::string> &vars) {
if (vars.size() != 2)
error("expected two items");
E(Error::CUSTOM, getSrcInfo(), "expected two items");
auto fn =
cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;
auto stmt = N<AssignStmt>(N<IdExpr>(vars[0]), nullptr, nullptr);
@ -1047,7 +1047,7 @@ TypecheckVisitor::populateStaticEnumerateLoop(Expr *iter,
block.push_back(b);
}
} else {
error("staticenumerate needs a tuple");
E(Error::CUSTOM, getSrcInfo(), "staticenumerate needs a tuple");
}
return block;
}
@ -1059,9 +1059,9 @@ TypecheckVisitor::populateStaticVarsLoop(Expr *iter,
cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;
bool withIdx = getBoolLiteral(extractFuncGeneric(fn->getType()));
if (!withIdx && vars.size() != 2)
error("expected two items");
E(Error::CUSTOM, getSrcInfo(), "expected two items");
else if (withIdx && vars.size() != 3)
error("expected three items");
E(Error::CUSTOM, getSrcInfo(), "expected three items");
std::vector<Stmt *> block;
auto typ = extractFuncArgType(fn->getType())->getClass();
size_t idx = 0;
@ -1092,9 +1092,9 @@ TypecheckVisitor::populateStaticVarTypesLoop(Expr *iter,
auto typ = realize(extractFuncGeneric(fn->getType(), 0)->getClass());
bool withIdx = getBoolLiteral(extractFuncGeneric(fn->getType(), 1));
if (!withIdx && vars.size() != 1)
error("expected one item");
E(Error::CUSTOM, getSrcInfo(), "expected one item");
else if (withIdx && vars.size() != 2)
error("expected two items");
E(Error::CUSTOM, getSrcInfo(), "expected two items");
seqassert(typ, "vars_types expects a realizable type, got '{}' instead",
*(extractFuncGeneric(fn->getType(), 0)));

View File

@ -63,11 +63,12 @@ Stmt *TypecheckVisitor::apply(
tv.N<AssignStmt>(tv.N<IdExpr>("__name__"), tv.N<StringExpr>(MODULE_MAIN)));
stmts.push_back(node);
ScopingVisitor::apply(cache, suite);
if (auto err = ScopingVisitor::apply(cache, suite))
throw exc::ParserException(std::move(err));
auto n = tv.inferTypes(suite, true);
if (!n) {
LOG("[error=>] {}", suite->toString(2));
tv.error("cannot typecheck the program");
E(Error::CUSTOM, suite, "cannot typecheck the program");
}
suite = tv.N<SuiteStmt>();
@ -82,7 +83,7 @@ Stmt *TypecheckVisitor::apply(
tv.prepareVTables();
if (!ctx->cache->errors.empty())
throw exc::ParserException();
throw exc::ParserException(ctx->cache->errors);
return suite;
}
@ -116,8 +117,13 @@ void TypecheckVisitor::loadStdLibrary(
// 1. Core definitions
cache->classes[VAR_CLASS_TOPLEVEL] = Cache::Class();
auto core = parseCode(stdlib->cache, stdlibPath->path, "from internal.core import *");
ScopingVisitor::apply(stdlib->cache, core);
auto coreOrErr =
parseCode(stdlib->cache, stdlibPath->path, "from internal.core import *");
if (!coreOrErr)
throw exc::ParserException(coreOrErr.takeError());
auto *core = *coreOrErr;
if (auto err = ScopingVisitor::apply(stdlib->cache, core))
throw exc::ParserException(std::move(err));
auto tv = TypecheckVisitor(stdlib, preamble);
core = tv.inferTypes(core, true);
preamble->push_back(core);
@ -133,8 +139,12 @@ void TypecheckVisitor::loadStdLibrary(
}
// 3. Load stdlib
auto std = parseFile(stdlib->cache, stdlibPath->path);
ScopingVisitor::apply(stdlib->cache, std);
auto stdOrErr = parseFile(stdlib->cache, stdlibPath->path);
if (!stdOrErr)
throw exc::ParserException(stdOrErr.takeError());
auto std = *stdOrErr;
if (auto err = ScopingVisitor::apply(stdlib->cache, std))
throw exc::ParserException(std::move(err));
tv = TypecheckVisitor(stdlib, preamble);
std = tv.inferTypes(std, true);
preamble->push_back(std);
@ -150,12 +160,10 @@ Stmt *TypecheckVisitor::apply(const std::shared_ptr<TypeContext> &ctx, Stmt *nod
auto tv = TypecheckVisitor(ctx, preamble);
auto n = tv.inferTypes(node, true);
ctx->setFilename(oldFilename);
if (!n) {
tv.error("cannot typecheck the program");
}
if (!ctx->cache->errors.empty()) {
throw exc::ParserException();
}
if (!n)
E(Error::CUSTOM, tv.getSrcInfo(), "cannot typecheck the program");
if (!ctx->cache->errors.empty())
throw exc::ParserException(ctx->cache->errors);
auto suite = ctx->cache->N<SuiteStmt>(*preamble);
suite->addStmt(n);
@ -687,7 +695,9 @@ TypecheckVisitor::canWrapExpr(Type *exprType, Type *expectedType, FuncType *call
} else if (exprClass && expectedClass && expectedClass->getUnion()) {
// Make union types via __internal__.new_union
if (!expectedClass->getUnion()->isSealed()) {
expectedClass->getUnion()->addType(exprClass);
if (!expectedClass->getUnion()->addType(exprClass))
E(error::Error::UNION_TOO_BIG, expectedClass->getSrcInfo(),
expectedClass->getUnion()->pendingTypes.size());
}
if (auto t = realize(expectedClass)) {
if (expectedClass->unify(exprClass, nullptr) == -1) {
@ -1116,7 +1126,7 @@ types::TypePtr TypecheckVisitor::instantiateType(const SrcInfo &srcInfo,
// look for generics [todo: speed-up!]
std::unordered_set<std::string> generics;
for (auto &g : ft->funcGenerics)
generics.insert(getUnmangledName(g.name));
generics.insert(getUnmangledName(g.name));
for (auto parent = ft->funcParent; parent;) {
if (auto f = parent->getFunc()) {
for (auto &g : f->funcGenerics)
@ -1431,7 +1441,7 @@ ir::PyType TypecheckVisitor::cythonizeClass(const std::string &name) {
for (const auto &[n, ofnn] : methods) {
auto canonicalName = getOverloads(ofnn).back();
auto fn = getFunction(canonicalName);
if (getOverloads(ofnn).size() == 1 && fn->ast->hasAttribute("autogenerated"))
if (getOverloads(ofnn).size() == 1 && fn->ast->hasAttribute(Attr::AutoGenerated))
continue;
auto fna = fn->ast;
bool isMethod = fna->hasAttribute(Attr::Method);

View File

@ -133,6 +133,7 @@ private: // Node typechecking rules
void visit(KeywordStarExpr *) override;
void visit(EllipsisExpr *) override;
void visit(CallExpr *) override;
void validateCall(CallExpr *expr);
bool transformCallArgs(CallExpr *);
std::pair<std::shared_ptr<types::FuncType>, Expr *> getCalleeFn(CallExpr *,
PartialCallData &);
@ -144,6 +145,7 @@ private: // Node typechecking rules
/* Assignments (assign.cpp) */
void visit(AssignExpr *) override;
void visit(AssignStmt *) override;
Stmt *unpackAssignment(Expr *lhs, Expr *rhs);
Stmt *transformUpdate(AssignStmt *);
Stmt *transformAssignment(AssignStmt *, bool = false);
void visit(DelStmt *) override;

View File

@ -101,47 +101,6 @@ struct CallbackASTVisitor : public ASTVisitor, public SrcObject {
return r;
}
// /// Convenience method that constructs a clone of a node.
// template <typename Tn> auto N(const Tn &ptr) { return std::make_shared<Tn>(ptr); }
// /// Convenience method that constructs a node.
// /// @param s source location.
// template <typename Tn, typename... Ts> auto N(codon::SrcInfo s, Ts &&...args) {
// auto t = std::make_shared<Tn>(std::forward<Ts>(args)...);
// t->setSrcInfo(s);
// return t;
// }
// /// Convenience method that constructs a node with the visitor's source location.
// template <typename Tn, typename... Ts> auto N(Ts &&...args) {
// auto t = std::make_shared<Tn>(std::forward<Ts>(args)...);
// t->setSrcInfo(getSrcInfo());
// return t;
// }
// template <typename Tn, typename Tt, typename... Ts>
// auto NT(const Tt &tt, Ts &&...args) {
// auto t = std::make_shared<Tn>(std::forward<Ts>(args)...);
// t->setSrcInfo(getSrcInfo());
// t->setType(tt);
// return t;
// }
/// Convenience method that raises an error at the current source location.
template <typename... TArgs> void error(const char *format, TArgs &&...args) {
error::raise_error(-1, getSrcInfo(), fmt::format(format, args...).c_str());
}
/// Convenience method that raises an error at the source location of p.
template <typename T, typename... TArgs>
void error(const T &p, const char *format, TArgs &&...args) {
error::raise_error(-1, p->getSrcInfo(), fmt::format(format, args...).c_str());
}
/// Convenience method that raises an internal error.
template <typename T, typename... TArgs>
void internalError(const char *format, TArgs &&...args) {
throw exc::ParserException(
fmt::format("INTERNAL: {}", fmt::format(format, args...), getSrcInfo()));
}
public:
void visit(NoneExpr *expr) override {}
void visit(BoolExpr *expr) override {}

View File

@ -279,7 +279,7 @@ public:
? compiler->parseFile(file, testFlags)
: compiler->parseCode(file, code, startLine, testFlags),
[](const error::ParserErrorInfo &e) {
for (auto &group : e) {
for (auto &group : e.getErrors()) {
for (auto &msg : group) {
getLogger().level = 0;
printf("%s\n", msg.getMessage().c_str());