diff --git a/integration/program.cpp b/integration/program.cpp index 74674348..f54e57c4 100644 --- a/integration/program.cpp +++ b/integration/program.cpp @@ -110,7 +110,7 @@ class RunPythonProgram : public ::testing::Test auto lexer = Lexer::create(std::string(program), "_integration_dummy_.py"); parser::Parser p{ lexer }; - p.parse(); + ASSERT_TRUE(p.parse().is_ok()); p.module()->print_node(""); m_bytecode = compiler::compile(p.module(), {}, diff --git a/integration/run_python_tests.sh b/integration/run_python_tests.sh index e3418148..d7312a57 100755 --- a/integration/run_python_tests.sh +++ b/integration/run_python_tests.sh @@ -46,4 +46,24 @@ else echo $file "... PASSED!" fi +# A syntax error must exit non-zero and report the line the parser actually gave +# up on -- not line 1 -- with a caret under the offending token. +file=$SCRIPT_DIR/tests/expected_failures/syntax_error_reporting.py +output=$(timeout 10s $PYTHON_EXECUTABLE $file --gc-frequency $GC_FREQUENCY 2>&1) +if [ $? -eq 0 ]; then + echo $file "... FAILED! (expected a non-zero exit code)" + exit_code=1 +elif ! echo "$output" | grep -q '", line 4$'; then + echo $file "... FAILED! (expected the error on line 4, got: ${output})" + exit_code=1 +elif ! echo "$output" | grep -qF ' ^'; then + echo $file "... FAILED! (expected a caret under the ':', got: ${output})" + exit_code=1 +elif ! echo "$output" | grep -q '^SyntaxError: invalid syntax$'; then + echo $file "... FAILED! (expected a SyntaxError, got: ${output})" + exit_code=1 +else + echo $file "... PASSED!" +fi + exit $exit_code diff --git a/integration/tests/expected_failures/syntax_error_reporting.py b/integration/tests/expected_failures/syntax_error_reporting.py new file mode 100644 index 00000000..8273868f --- /dev/null +++ b/integration/tests/expected_failures/syntax_error_reporting.py @@ -0,0 +1,5 @@ +x = 1 +y = 2 + +def foo(: + pass diff --git a/src/ast/optimizers/Optimizers_tests.cpp b/src/ast/optimizers/Optimizers_tests.cpp index b580b41d..50939286 100644 --- a/src/ast/optimizers/Optimizers_tests.cpp +++ b/src/ast/optimizers/Optimizers_tests.cpp @@ -466,7 +466,7 @@ void assert_generates_ast(std::string_view program, parser::Parser p{ lexer }; const auto spdlog_level = spdlog::get_level(); spdlog::set_level(spdlog::level::debug); - p.parse(); + ASSERT(p.parse().is_ok()); spdlog::set_level(spdlog_level); if (lvl > compiler::OptimizationLevel::None) { diff --git a/src/executable/bytecode/BytecodeProgram_tests.cpp b/src/executable/bytecode/BytecodeProgram_tests.cpp index 258c3f8a..9c0c1602 100644 --- a/src/executable/bytecode/BytecodeProgram_tests.cpp +++ b/src/executable/bytecode/BytecodeProgram_tests.cpp @@ -14,7 +14,7 @@ std::shared_ptr generate_bytecode(std::string_view program) { auto lexer = Lexer::create(std::string(program), "_bytecode_program_tests_.py"); parser::Parser p{ lexer }; - p.parse(); + ASSERT(p.parse().is_ok()); auto module = p.module(); ASSERT(module); diff --git a/src/executable/bytecode/codegen/BytecodeGenerator_tests.cpp b/src/executable/bytecode/codegen/BytecodeGenerator_tests.cpp index 720875e8..eb1c6a8a 100644 --- a/src/executable/bytecode/codegen/BytecodeGenerator_tests.cpp +++ b/src/executable/bytecode/codegen/BytecodeGenerator_tests.cpp @@ -15,7 +15,7 @@ std::shared_ptr generate_bytecode(std::string_view program) { auto lexer = Lexer::create(std::string(program), "_bytecode_generator_tests_.py"); parser::Parser p{ lexer }; - p.parse(); + ASSERT(p.parse().is_ok()); auto module = p.module(); ASSERT(module); diff --git a/src/executable/bytecode/codegen/VariablesResolver_tests.cpp b/src/executable/bytecode/codegen/VariablesResolver_tests.cpp index 6c90f475..376b1556 100644 --- a/src/executable/bytecode/codegen/VariablesResolver_tests.cpp +++ b/src/executable/bytecode/codegen/VariablesResolver_tests.cpp @@ -13,7 +13,7 @@ VariablesResolver::VisibilityMap generate_resolver(std::string_view program) { auto lexer = Lexer::create(std::string(program), "_bytecode_generator_tests_.py"); parser::Parser p{ lexer }; - p.parse(); + ASSERT(p.parse().is_ok()); auto *module = as(p.module().get()); ASSERT(module); diff --git a/src/executable/llvm/LLVMGenerator_tests.cpp b/src/executable/llvm/LLVMGenerator_tests.cpp index 6001ef38..95b00aaa 100644 --- a/src/executable/llvm/LLVMGenerator_tests.cpp +++ b/src/executable/llvm/LLVMGenerator_tests.cpp @@ -14,7 +14,7 @@ std::shared_ptr generate_llvm_module(std::string_view program) { auto lexer = Lexer::create(std::string(program), "_llvm_backend_tests_.py"); parser::Parser p{ lexer }; - p.parse(); + ASSERT(p.parse().is_ok()); auto module = as(p.module()); ASSERT(module); diff --git a/src/parser/Parser.cpp b/src/parser/Parser.cpp index ffbeb032..06488d15 100644 --- a/src/parser/Parser.cpp +++ b/src/parser/Parser.cpp @@ -1,7 +1,8 @@ module; #include "core.hpp" -#include "spdlog/spdlog.h" +#include "runtime/SourceManager.hpp" +#include "spdlog/spdlog.h" #include @@ -133,8 +134,8 @@ template struct PatternV2 if constexpr (seeds_sentinel) { if (result.has_value()) { // Safe to hold across grow_lr: memo entries live in a deque. - auto *slot = p.memo_find(start_position, memo_rule_id); - ASSERT(slot); + const auto &slot = p.memo_find(start_position, memo_rule_id); + ASSERT(slot.has_value()); ASSERT(slot->has_value()); auto &value = *slot; if (std::holds_alternative(value->value) && std::get(value->value)) { @@ -212,7 +213,8 @@ template class PatternMa if (!t.has_value()) { return {}; } if constexpr (::detail::has_type{}) { - if (auto *slot = p.memo_find(original_token_position, memo_rule_id)) { + if (const auto &slot = p.memo_find(original_token_position, memo_rule_id); + slot.has_value()) { if (!slot->has_value()) { return {}; } auto &value = (*slot)->value; p.token_position() = (*slot)->position; @@ -708,6 +710,7 @@ struct SingleTokenPatternV2 : PatternV2> static std::optional matches_impl(Parser &p) { + p.observe_token(p.token_position()); if (SingleTokenPattern_>::match(p)) { const auto &t = p.lexer().peek_token(p.token_position()); return t.has_value() ? std::make_optional(ResultType{ *t }) : std::nullopt; @@ -7389,33 +7392,41 @@ struct FilePattern : PatternV2 } return p.module(); } - size_t idx = 0; - auto t = *p.lexer().peek_token(idx); - auto begin = t.start().pointer_to_program; - auto end = t.end().pointer_to_program; - const size_t row = t.start().row; - while (row == t.start().row) { - end = t.end().pointer_to_program; - idx++; - t = *p.lexer().peek_token(idx); - } - std::string line{ begin, end }; - spdlog::error("Syntax error on line {}: '{}'", row + 1, line); - // PARSER_ERROR(); return {}; } }; namespace parser { -void Parser::parse() +PyResult> Parser::parse() { auto result = PatternMatchV2::match(*this); if (result) { auto [module] = *result; m_module = std::move(module); - m_module->print_node(""); + return Ok(m_module); } - DEBUG_LOG("Parser return code: {}", result.has_value()); + std::size_t index = m_furthest_token; + std::optional token = m_lexer.peek_token(index); + while (!token.has_value() && index > 0) { token = m_lexer.peek_token(--index); } + + const auto &filename = m_lexer.filename(); + const auto &program = m_lexer.program(); + auto lineno = token.has_value() ? token->start().row + 1 : 1; + auto offset = token.has_value() ? token->start().column + 1 : 1; + const auto line_count = + std::max(static_cast(std::count(program.begin(), program.end(), '\n')) + + (program.empty() || program.back() == '\n' ? 0uz : 1uz), + 1uz); + if (lineno > line_count) { + lineno = line_count; + offset = SourceManager::the().line(filename, lineno).size() + 1; + } + const auto text = SourceManager::the().line(filename, lineno); + return Err(syntax_error("invalid syntax", + SyntaxErrorLocation{ .filename = filename, + .lineno = lineno, + .offset = offset, + .text = std::string{ text } })); } PyResult> Parser::parse_expression() diff --git a/src/parser/Parser.cppm b/src/parser/Parser.cppm index 79ed6a16..ee22cad7 100644 --- a/src/parser/Parser.cppm +++ b/src/parser/Parser.cppm @@ -12,6 +12,7 @@ class Parser std::shared_ptr m_module; Lexer &m_lexer; std::size_t m_token_position{ 0 }; + std::size_t m_furthest_token{ m_token_position }; public: struct CacheValue @@ -25,18 +26,20 @@ class Parser using MemoSlot = std::optional; - MemoSlot *memo_find(std::size_t position, std::uint16_t rule) + std::optional memo_find(std::size_t position, std::uint16_t rule) { - if (position >= m_memo_index.size()) { return nullptr; } + if (position >= m_memo_index.size()) { return std::nullopt; } for (const auto &[id, slot] : m_memo_index[position]) { - if (id == rule) { return &m_memo_pool[slot]; } + if (id == rule) { return m_memo_pool[slot]; } } - return nullptr; + return std::nullopt; } MemoSlot &memo_insert(std::size_t position, std::uint16_t rule) { - if (auto *existing = memo_find(position, rule)) { return *existing; } + if (const auto &existing = memo_find(position, rule); existing.has_value()) { + return existing.value(); + } if (position >= m_memo_index.size()) { m_memo_index.resize(position + 1); } m_memo_pool.emplace_back(); m_memo_index[position].emplace_back( @@ -65,8 +68,15 @@ class Parser const std::size_t &token_position() const { return m_token_position; } std::size_t &token_position() { return m_token_position; } + std::size_t furthest_token() const { return m_furthest_token; } + + void observe_token(std::size_t position) + { + m_furthest_token = std::max(m_furthest_token, position); + } + // parses a file - void parse(); + py::PyResult> parse(); // parses an expression used by the builtin `eval` function py::PyResult> parse_expression(); diff --git a/src/parser/Parser_tests.cpp b/src/parser/Parser_tests.cpp index 3f35a286..fa6796b8 100644 --- a/src/parser/Parser_tests.cpp +++ b/src/parser/Parser_tests.cpp @@ -1104,7 +1104,7 @@ void assert_generates_ast(std::string_view program, std::shared_ptr expe { auto lexer = Lexer::create(std::string(program), "_parser_test_.py"); parser::Parser p{ lexer }; - p.parse(); + ASSERT_TRUE(p.parse().is_ok()); ASSERT_TRUE(p.module()); const auto lvl = spdlog::get_level(); diff --git a/src/repl/repl.cpp b/src/repl/repl.cpp index 72e7910c..96a75ac3 100644 --- a/src/repl/repl.cpp +++ b/src/repl/repl.cpp @@ -89,16 +89,20 @@ int run_and_execute_script(size_t argc, std::cout << std::endl; } parser::Parser p{ lexer }; - p.parse(); + auto module_ = p.parse(); + if (module_.is_err()) { + std::cout << module_.unwrap_err()->format_traceback() << std::endl; + return EXIT_FAILURE; + } if (print_ast) { const auto lvl = spdlog::get_level(); spdlog::set_level(spdlog::level::debug); - p.module()->print_node(""); + module_.unwrap()->print_node(""); spdlog::set_level(lvl); } std::shared_ptr bytecode = compiler::compile( - p.module(), argv_vector, compiler::Backend::MLIR, compiler::OptimizationLevel::None); + module_.unwrap(), argv_vector, compiler::Backend::MLIR, compiler::OptimizationLevel::None); if (print_bytecode) { std::cout << "Generated bytecode: \n"; @@ -108,7 +112,7 @@ int run_and_execute_script(size_t argc, if (use_llvm) { #ifdef USE_LLVM auto llvm_code = codegen::LLVMGenerator::compile( - p.module(), argv_vector, compiler::OptimizationLevel::None); + module_.unwrap(), argv_vector, compiler::OptimizationLevel::None); if (!llvm_code) { std::cout << "Could not compile to LLVM IR\n"; } else { diff --git a/src/runtime/BaseException.cpp b/src/runtime/BaseException.cpp index 67b4baa8..dba104fc 100644 --- a/src/runtime/BaseException.cpp +++ b/src/runtime/BaseException.cpp @@ -107,9 +107,9 @@ std::string BaseException::to_string() const std::string BaseException::format_traceback() const { std::ostringstream out; - out << "Traceback (most recent call last):\n"; auto *tb = m_traceback; - while (tb) { + if (tb) { out << "Traceback (most recent call last):\n"; } + for (; tb; tb = tb->m_tb_next) { const auto &filename = tb->m_tb_frame->code()->m_filename; out << std::format(" File \"{}\", line {}, in {}\n", filename, @@ -118,12 +118,17 @@ std::string BaseException::format_traceback() const const auto source = SourceManager::the().line(filename, tb->m_tb_lineno); const auto trimmed = SourceManager::strip_leading_whitespace(source); if (!trimmed.empty()) { out << " " << trimmed << "\n"; } - tb = tb->m_tb_next; } - out << type()->name() << ": " << what() << "\n"; + out << format_exception_only(); return out.str(); } +std::string BaseException::format_exception_only() const +{ + return std::format("{}: {}\n", type()->name(), what()); +} + + PyResult BaseException::__repr__() const { std::string args_part; diff --git a/src/runtime/BaseException.cppm b/src/runtime/BaseException.cppm index 1b0ed0ad..085bc5f2 100644 --- a/src/runtime/BaseException.cppm +++ b/src/runtime/BaseException.cppm @@ -72,6 +72,8 @@ class BaseException : public PyBaseObject PyType *static_type() const override; static PyType *class_type(); + virtual std::string format_exception_only() const; + void visit_graph(Visitor &) override; }; diff --git a/src/runtime/SyntaxError.cpp b/src/runtime/SyntaxError.cpp index bfab0202..879afd14 100644 --- a/src/runtime/SyntaxError.cpp +++ b/src/runtime/SyntaxError.cpp @@ -1,25 +1,63 @@ module; #include "core.hpp" +#include + module py.runtime; import py.types; namespace py { +namespace { + constexpr std::string_view whitespace = " \t\n\v\f\r"; + + std::string_view strip(std::string_view s) + { + const auto first = s.find_first_not_of(whitespace); + if (first == std::string_view::npos) { return {}; } + return s.substr(first, s.find_last_not_of(whitespace) - first + 1); + } + + // `as` dereferences its argument, and every location attribute is null + // until an info tuple sets it. + template const T *attribute_as(PyObject *attribute) + { + return attribute ? as(attribute) : nullptr; + } +}// namespace + SyntaxError *SyntaxError::create(PyTuple *args) { auto &heap = VirtualMachine::the().heap(); return heap.allocate(args); } -BaseException *make_syntax_error(std::string &&message) +SyntaxError *SyntaxError::create(std::string message) { - auto msg = PyString::create(std::move(message)); - ASSERT(msg.is_ok()); - auto args_tuple = PyTuple::create(msg.unwrap()); - ASSERT(args_tuple.is_ok()); - return SyntaxError::create(args_tuple.unwrap()); + auto args = PyTuple::create(String{ std::move(message) }); + if (args.is_err()) { TODO(); } + auto &heap = VirtualMachine::the().heap(); + auto *error = heap.allocate(args.unwrap()); + auto result = error->__init__(args.unwrap(), nullptr); + ASSERT(result.is_ok()); + return error; +} + +SyntaxError *SyntaxError::create(std::string message, SyntaxErrorLocation location) +{ + auto info = PyTuple::create(String{ std::move(location.filename) }, + Number{ static_cast(location.lineno) }, + Number{ static_cast(location.offset) }, + String{ std::move(location.text) }); + if (info.is_err()) { TODO(); } + auto args = PyTuple::create(String{ std::move(message) }, info.unwrap()); + if (args.is_err()) { TODO(); } + auto &heap = VirtualMachine::the().heap(); + auto *error = heap.allocate(args.unwrap()); + auto result = error->__init__(args.unwrap(), nullptr); + ASSERT(result.is_ok()); + return error; } SyntaxError::SyntaxError(PyType *type) : Exception(type) {} @@ -30,23 +68,138 @@ SyntaxError::SyntaxError(PyTuple *args) : Exception(types::BuiltinTypes::the().s PyResult SyntaxError::__new__(const PyType *type, PyTuple *args, PyDict *kwargs) { ASSERT(type == types::syntax_error()); - ASSERT(!kwargs || kwargs->map().empty()); + if (kwargs && !kwargs->map().empty()) { + return Err(type_error("SyntaxError() takes no keyword arguments")); + } return Ok(SyntaxError::create(args)); } +PyResult SyntaxError::__init__(PyTuple *args, PyDict *kwargs) +{ + // `SyntaxError(msg, (filename, lineno, offset, text))` + if (kwargs && !kwargs->map().empty()) { + return Err(type_error("SyntaxError() takes no keyword arguments")); + } + m_args = args; + if (!args || args->size() == 0) { return Ok(1); } + auto msg = PyObject::from(args->elements()[0]); + if (msg.is_err()) { return Err(msg.unwrap_err()); } + m_msg = msg.unwrap(); + if (args->size() == 2) { + auto info = PyObject::from(args->elements()[1]); + if (info.is_err()) { return Err(info.unwrap_err()); } + std::vector info_args; + info_args.reserve(4); + if (auto result = from_iterable(info.unwrap(), std::inserter(info_args, info_args.begin())); + result.is_err()) { + return Err(result.unwrap_err()); + } + if (info_args.size() != 4) { return Err(index_error("tuple index out of range")); } + m_filename = PyObject::from(info_args[0]).unwrap(); + m_lineno = PyObject::from(info_args[1]).unwrap(); + m_offset = PyObject::from(info_args[2]).unwrap(); + m_text = PyObject::from(info_args[3]).unwrap(); + } + return Ok(1); +} + +PyResult SyntaxError::__str__() const +{ + std::string msg{ "None" }; + if (m_msg) { + auto str = m_msg->str(); + if (str.is_err()) { return str; } + msg = str.unwrap()->to_string(); + } + const auto *filename = attribute_as(m_filename); + const auto *lineno = attribute_as(m_lineno); + if (!filename && !lineno) { return PyString::create(msg); } + std::string basename; + if (filename) { + const auto &value = filename->value(); + const auto separator = value.find_last_of("/\\"); + basename = separator == std::string::npos ? value : value.substr(separator + 1); + } + if (filename && lineno) { + return PyString::create( + std::format("{} ({}, line {})", msg, basename, lineno->as_size_t())); + } + if (filename) { return PyString::create(std::format("{} ({})", msg, basename)); } + return PyString::create(std::format("{} (line {})", msg, lineno->as_size_t())); +} + +std::string SyntaxError::format_exception_only() const +{ + std::ostringstream out; + + const auto *lineno = attribute_as(m_lineno); + if (lineno) { + const auto *filename = attribute_as(m_filename); + out << std::format(" File \"{}\", line {}\n", + filename ? filename->value() : std::string{ "" }, + lineno->as_size_t()); + if (const auto *text = attribute_as(m_text)) { + const std::string_view line{ text->value() }; + if (const auto trimmed = strip(line); !trimmed.empty()) { + out << " " << trimmed << "\n"; + if (const auto *offset = attribute_as(m_offset)) { + const auto column = std::min(line.size(), offset->as_size_t()); + auto prefix = line.substr(0, column > 0 ? column - 1 : 0); + if (const auto first = prefix.find_first_not_of(whitespace); + first != std::string_view::npos) { + prefix.remove_prefix(first); + } else { + prefix = {}; + } + std::string caret; + caret.reserve(prefix.size() + 1); + for (const char c : prefix) { + caret += whitespace.find(c) != std::string_view::npos ? c : ' '; + } + caret += '^'; + out << " " << caret << "\n"; + } + } + } + } + + std::string msg{ "" }; + if (m_msg && m_msg != py_none()) { + if (auto str = m_msg->str(); str.is_ok()) { msg = str.unwrap()->to_string(); } + } + out << std::format("{}: {}\n", type()->name(), msg); + return out.str(); +} + PyType *SyntaxError::static_type() const { ASSERT(types::syntax_error()); return types::syntax_error(); } +void SyntaxError::visit_graph(Visitor &visitor) +{ + Exception::visit_graph(visitor); + if (m_msg) visitor.visit(*m_msg); + if (m_filename) visitor.visit(*m_filename); + if (m_text) visitor.visit(*m_text); + if (m_lineno) visitor.visit(*m_lineno); + if (m_offset) visitor.visit(*m_offset); +} + namespace { std::once_flag syntax_error_flag; std::unique_ptr register_syntax_error() { - return std::move(klass("SyntaxError", Exception::class_type()).type); + return std::move(klass("SyntaxError", Exception::class_type()) + .attr("msg", &SyntaxError::m_msg) + .attr("filename", &SyntaxError::m_filename) + .attr("lineno", &SyntaxError::m_lineno) + .attr("offset", &SyntaxError::m_offset) + .attr("text", &SyntaxError::m_text) + .type); } }// namespace diff --git a/src/runtime/SyntaxError.cppm b/src/runtime/SyntaxError.cppm index 7b0b3538..f60200d7 100644 --- a/src/runtime/SyntaxError.cppm +++ b/src/runtime/SyntaxError.cppm @@ -16,36 +16,66 @@ import std; export namespace py { class PyType; +// Where a syntax error happened, in CPython's (filename, lineno, offset, text) +// order -- the same order as the info tuple accepted by `SyntaxError(msg, info)`. +// `lineno` and `offset` are 1-based, and `offset` indexes into `text` (the source +// line), not into the file. +struct SyntaxErrorLocation +{ + std::string filename; + std::size_t lineno; + std::size_t offset; + std::string text; +}; + class SyntaxError : public Exception { friend class ::Heap; friend class py::detail::Allocator; - friend BaseException *make_syntax_error(std::string &&); + friend BaseException *syntax_error(std::string); + friend BaseException *syntax_error(std::string, SyntaxErrorLocation); + + public: + PyObject *m_msg{ nullptr }; + PyObject *m_filename{ nullptr }; + PyObject *m_text{ nullptr }; + PyObject *m_lineno{ nullptr }; + PyObject *m_offset{ nullptr }; private: SyntaxError(PyType *type); - SyntaxError(PyTuple *args); - static SyntaxError *create(PyTuple *args); + static SyntaxError *create(PyTuple *); + + static SyntaxError *create(std::string message); + + static SyntaxError *create(std::string message, SyntaxErrorLocation location); public: static PyResult __new__(const PyType *type, PyTuple *args, PyDict *kwargs); + PyResult __init__(PyTuple *args, PyDict *kwargs); + + PyResult __str__() const; + static std::function()> type_factory(); PyType *static_type() const override; + + std::string format_exception_only() const override; + + void visit_graph(Visitor &) override; }; -// Defined in SyntaxError.cpp. Keeping the PyString/PyTuple construction and the -// heap allocation out of the interface means this partition no longer has -// to materialise those types just to declare one exception class. -BaseException *make_syntax_error(std::string &&message); +inline BaseException *syntax_error(std::string message) +{ + return SyntaxError::create(std::move(message)); +} -template -inline BaseException *syntax_error(const std::string &message, Args &&...args) +inline BaseException *syntax_error(std::string message, SyntaxErrorLocation location) { - return make_syntax_error(std::vformat(message, std::make_format_args(args...))); + return SyntaxError::create(std::move(message), std::move(location)); } }// namespace py diff --git a/src/runtime/modules/BuiltinsModule.cpp b/src/runtime/modules/BuiltinsModule.cpp index 15154ad5..f1c70aa8 100644 --- a/src/runtime/modules/BuiltinsModule.cpp +++ b/src/runtime/modules/BuiltinsModule.cpp @@ -1098,9 +1098,10 @@ PyResult compile(const PyTuple *args, const PyDict *, Interpreter &) auto lexer = Lexer::create(source_str, filename_str); parser::Parser p{ lexer }; - p.parse(); + auto module_ = p.parse(); + if (module_.is_err()) { TODO(); } - std::shared_ptr bytecode = compiler::compile(p.module(), + std::shared_ptr bytecode = compiler::compile(module_.unwrap(), { filename_str }, compiler::Backend::MLIR, compiler::OptimizationLevel::None); diff --git a/src/utilities/freeze.cpp b/src/utilities/freeze.cpp index e2e0511d..dbfd5654 100644 --- a/src/utilities/freeze.cpp +++ b/src/utilities/freeze.cpp @@ -18,9 +18,13 @@ std::shared_ptr compile(const std::string &filename, std::vectorformat_traceback(); + return nullptr; + } return compiler::compile( - p.module(), argv, compiler::Backend::MLIR, compiler::OptimizationLevel::None); + module_.unwrap(), argv, compiler::Backend::MLIR, compiler::OptimizationLevel::None); } int freeze(size_t argc, char **argv, const std::string &output) @@ -34,6 +38,7 @@ int freeze(size_t argc, char **argv, const std::string &output) [[maybe_unused]] auto &vm = VirtualMachine::the(); auto bytecode = compile(filename, std::move(argv_vector)); + if (!bytecode) { return EXIT_FAILURE; } std::cout << bytecode->to_string() << "-----------------------------\n\n"; const auto bytes = bytecode->serialize();