mirror of https://github.com/exaloop/codon.git
New error handling via llvm::Error [wip]
parent
ecaa8d906e
commit
3446d5e58f
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
|
||||
UnionType *getUnion() override { return this; }
|
||||
|
||||
void addType(Type *);
|
||||
bool addType(Type *);
|
||||
void seal();
|
||||
std::vector<Type *> getRealizationTypes();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)`
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 += '}';
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue