diff --git a/modcc/error.hpp b/modcc/error.hpp index e66fc45478faa8dde356e37b7e51d7b0460f000c..919d124884ce64b28a328e4bfd04b6df49e747fd 100644 --- a/modcc/error.hpp +++ b/modcc/error.hpp @@ -71,6 +71,3 @@ public: private: error_entry error_info_; }; - - - diff --git a/modcc/expression.hpp b/modcc/expression.hpp index da885630f2080290de61ca1b8c209f14282b0126..d369e7a99e9138aab7d80fc1b3da5a048d06842b 100644 --- a/modcc/expression.hpp +++ b/modcc/expression.hpp @@ -18,6 +18,7 @@ class Visitor; class ARB_LIBMODCC_API Expression; +struct ARB_LIBMODCC_API ErrorExpression; class ARB_LIBMODCC_API CallExpression; class ARB_LIBMODCC_API BlockExpression; class ARB_LIBMODCC_API IfExpression; @@ -209,6 +210,20 @@ protected: scope_ptr scope_; }; +struct ARB_LIBMODCC_API ErrorExpression : public Expression { + explicit ErrorExpression(Location location): Expression(location) + {} + + std::string to_string() const override { + return "Error" + error_string_; + } + + void accept(Visitor *) override { + throw compiler_exception{"Attempted to visit error expression.", location()}; + } +}; + + class ARB_LIBMODCC_API Symbol : public Expression { public : Symbol(Location loc, std::string name, symbolKind kind) diff --git a/modcc/modcc.cpp b/modcc/modcc.cpp index 6e9b51ba7fbe3478b2899d5c2eba5ef65a557340..3eea843b0c550f0dfc5a98537d479394fb9dadd5 100644 --- a/modcc/modcc.cpp +++ b/modcc/modcc.cpp @@ -25,12 +25,12 @@ using std::cerr; // Options and option parsing: int report_error(const std::string& message) { - cerr << red("error: ") << message << "\n"; + cerr << red("error trace:\n") << message << "\n"; return 1; } int report_ice(const std::string& message) { - cerr << red("internal compiler error: ") << message << "\n" + cerr << red("internal compiler error:\n") << message << "\n" << "\nPlease report this error to the modcc developers.\n"; return 1; } @@ -223,6 +223,7 @@ int main(int argc, char **argv) { emit_header("semantic analysis"); m.semantic(); if (m.has_warning()) { + cerr << yellow("Warnings:\n"); cerr << m.warning_string() << "\n"; } if (m.has_error()) { diff --git a/modcc/module.cpp b/modcc/module.cpp index 6d6a56a0dd1b980e0b486d0b48407d8135b8bddf..7dafa6ed0d65df27ada23dfb0f516a96e0478559 100644 --- a/modcc/module.cpp +++ b/modcc/module.cpp @@ -119,7 +119,7 @@ std::string Module::error_string() const { std::string str; for (const error_entry& entry: errors()) { if (!str.empty()) str += '\n'; - str += red("error "); + str += red(" * "); str += white(pprintf("%:% ", source_name(), entry.location)); str += entry.message; } @@ -130,7 +130,7 @@ std::string Module::warning_string() const { std::string str; for (auto& entry: warnings()) { if (!str.empty()) str += '\n'; - str += purple("warning "); + str += purple(" * "); str += white(pprintf("%:% ", source_name(), entry.location)); str += entry.message; } diff --git a/modcc/solvers.cpp b/modcc/solvers.cpp index 1d31e55d8fe70e4573efed3390b0b389ebf477d0..23d89a41e12c713963014adfba3b9dbbfcae7905 100644 --- a/modcc/solvers.cpp +++ b/modcc/solvers.cpp @@ -41,7 +41,13 @@ void CnexpSolverVisitor::visit(AssignmentExpression *e) { } auto s = deriv->name(); + linear_test_result r = linear_test(rhs, dvars_); + if (r.has_error()) { + append_errors(r.errors()); + error({"CNExp: Could not determine linearity, maybe use a different solver?", loc}); + return; + } if (!r.monolinear(s)) { error({"System not diagonal linear for cnexp", loc}); @@ -52,7 +58,6 @@ void CnexpSolverVisitor::visit(AssignmentExpression *e) { if (!coef || is_zero(coef)) { // s' = b becomes s = s + b*dt; use b_ as a local variable for // the constant term b. - auto local_b_term = make_unique_local_assign(scope, r.constant.get(), "b_"); statements_.push_back(std::move(local_b_term.local_decl)); statements_.push_back(std::move(local_b_term.assignment)); diff --git a/modcc/symdiff.cpp b/modcc/symdiff.cpp index 4946a1b8108c4ce3f33a405a712be82269019c8e..1b4028eba54bd5254cec2da49fe217fbeaa7458f 100644 --- a/modcc/symdiff.cpp +++ b/modcc/symdiff.cpp @@ -562,7 +562,17 @@ ARB_LIBMODCC_API expression_ptr symbolic_pdiff(Expression* e, const std::string& SymPDiffVisitor pdiff_visitor(id); e->accept(&pdiff_visitor); - if (pdiff_visitor.has_error()) return nullptr; + if (pdiff_visitor.has_error()) { + std::string errors, sep = ""; + + for (const auto& error: pdiff_visitor.errors()) { + errors += sep + error.message; + sep = "\n"; + } + auto res = std::make_unique<ErrorExpression>(e->location()); + res->error(errors); + return res; + } return constant_simplify(pdiff_visitor.result()); } @@ -666,17 +676,20 @@ ARB_LIBMODCC_API linear_test_result linear_test(Expression* e, const std::vector result.constant = e->clone(); for (const auto& id: vars) { auto coef = symbolic_pdiff(e, id); - if (!coef) { - return linear_test_result{}; + if (coef->has_error()) { + auto res = linear_test_result{}; + res.error({coef->error_message(), loc}); + return res; } + if (!coef) return linear_test_result{}; if (!is_zero(coef)) result.coef[id] = std::move(coef); - result.constant = substitute(result.constant, id, zero()); } ConstantSimplifyVisitor csimp_visitor; result.constant->accept(&csimp_visitor); result.constant = csimp_visitor.result(); + if (result.constant.get() == nullptr) throw compiler_exception{"Linear test: simplification of the constant term failed.", loc}; // linearity test: take second order derivatives, test against zero. result.is_linear = true; diff --git a/modcc/symdiff.hpp b/modcc/symdiff.hpp index cb58ffae803c36d37a4bef86e2b6974b9424badf..b43874898138ece6abe9d389302e81d2210b1908 100644 --- a/modcc/symdiff.hpp +++ b/modcc/symdiff.hpp @@ -81,7 +81,7 @@ inline expression_ptr substitute(const expression_ptr& e, const substitute_map& // Linearity testing -struct linear_test_result { +struct linear_test_result: public error_stack { bool is_linear = false; bool is_homogeneous = false; expression_ptr constant; diff --git a/test/unit-modcc/mod_files/bug-1893-bad.mod b/test/unit-modcc/mod_files/bug-1893-bad.mod new file mode 100644 index 0000000000000000000000000000000000000000..fde7f171b32138e6f1979897bff0fcc27fa4ac08 --- /dev/null +++ b/test/unit-modcc/mod_files/bug-1893-bad.mod @@ -0,0 +1,28 @@ +NEURON { + POINT_PROCESS bug_1893 +} + +INITIAL { + c = 0 + rho = 0 + theta_p = 0 +} + +STATE { + c + rho + theta_p +} + +PARAMETER { + tau_c = 150 (ms) +} + +BREAKPOINT { + SOLVE state METHOD cnexp +} + +DERIVATIVE state { + c' = -c/tau_c + rho' = (c - theta_p) > 0 +} diff --git a/test/unit-modcc/mod_files/bug-1893.mod b/test/unit-modcc/mod_files/bug-1893.mod new file mode 100644 index 0000000000000000000000000000000000000000..14cfd5e5dc3fba55d97516e3ec1b72b2a8d5bc70 --- /dev/null +++ b/test/unit-modcc/mod_files/bug-1893.mod @@ -0,0 +1,28 @@ +NEURON { + POINT_PROCESS bug_1893 +} + +INITIAL { + c = 0 + rho = 0 + theta_p = 0 +} + +STATE { + c + rho + theta_p +} + +PARAMETER { + tau_c = 150 (ms) +} + +BREAKPOINT { + SOLVE state METHOD sparse +} + +DERIVATIVE state { + c' = -c/tau_c + rho' = (c - theta_p) > 0 +} diff --git a/test/unit-modcc/test_module.cpp b/test/unit-modcc/test_module.cpp index 34ff5b4d9ba4d129410d4b9310a4e847dca7368f..9e9a1db0a78ec14848a48f6aa8740e571db3583f 100644 --- a/test/unit-modcc/test_module.cpp +++ b/test/unit-modcc/test_module.cpp @@ -122,3 +122,24 @@ TEST(Module, read_write_ion) { EXPECT_TRUE(p.parse()); EXPECT_TRUE(m.semantic()); } + +// Regression test in #1893 we found that the solver segfaults when handed a +// naked comparison statement. +TEST(Module, solver_bug_1893) { + { + Module m(io::read_all(DATADIR "/mod_files/bug-1893.mod"), "bug-1893.mod"); + EXPECT_NE(m.buffer().size(), 0); + + Parser p(m, false); + EXPECT_TRUE(p.parse()); + EXPECT_TRUE(m.semantic()); + } + { + Module m(io::read_all(DATADIR "/mod_files/bug-1893-bad.mod"), "bug-1893.mod"); + EXPECT_NE(m.buffer().size(), 0); + + Parser p(m, false); + EXPECT_TRUE(p.parse()); + EXPECT_FALSE(m.semantic()); + } +}