diff --git a/src/frontends/lean/elaborator.cpp b/src/frontends/lean/elaborator.cpp index e4208867e..39b996e4b 100644 --- a/src/frontends/lean/elaborator.cpp +++ b/src/frontends/lean/elaborator.cpp @@ -569,7 +569,8 @@ public: if (!infom()) return; m_pre_info_data.instantiate(s); - infom()->merge(m_pre_info_data); + bool overwrite = true; + infom()->merge(m_pre_info_data, overwrite); m_pre_info_data.clear(); } diff --git a/src/frontends/lean/info_manager.cpp b/src/frontends/lean/info_manager.cpp index ec63c4c1e..e51bf70f4 100644 --- a/src/frontends/lean/info_manager.cpp +++ b/src/frontends/lean/info_manager.cpp @@ -6,7 +6,9 @@ Author: Leonardo de Moura */ #include #include +#include #include "util/thread.h" +#include "kernel/environment.h" #include "library/choice.h" #include "frontends/lean/info_manager.h" #include "frontends/lean/pp_options.h" @@ -195,14 +197,36 @@ struct info_data_cmp { struct info_manager::imp { typedef rb_tree info_data_set; + /** \brief Saved environment + options for a given line and iteration + Whenever "lean server" starts processing a file again, we bump the iteration. + */ + struct env_info { + unsigned m_line; + unsigned m_iteration; + environment m_env; + options m_options; + env_info():m_line(0), m_iteration(0) {} + env_info(unsigned l, unsigned i, environment const & env, options const & o): + m_line(l), m_iteration(i), m_env(env), m_options(o) {} + friend bool operator<(env_info const & i1, env_info const & i2) { return i1.m_line < i2.m_line; } + }; mutex m_mutex; + bool m_block_updates; std::vector m_line_data; std::vector m_line_valid; - unsigned m_available_upto; + std::set m_env_info; + unsigned m_iteration; // current interation + unsigned m_processed_upto; - imp():m_available_upto(0) {} + imp():m_block_updates(false), m_iteration(0), m_processed_upto(0) {} + + void block_updates(bool f) { + lock_guard lc(m_mutex); + m_block_updates = f; + } void synch_line(unsigned l) { + lean_assert(!m_block_updates); lean_assert(l > 0); if (l >= m_line_data.size()) { m_line_data.resize(l+1); @@ -210,38 +234,68 @@ struct info_manager::imp { } } + void save_environment_options(unsigned l, environment const & env, options const & o) { + lock_guard lc(m_mutex); + if (m_block_updates) + return; + // erase all entries in m_env_info such that e.m_line <= l and e.m_iteration < m_iteration + auto it = m_env_info.begin(); + auto end = m_env_info.end(); + while (it != end) { + if (it->m_line > l) + break; + else if (it->m_line <= l && it->m_iteration < m_iteration) + it = m_env_info.erase(it); + else + ++it; + } + m_env_info.insert(env_info(l, m_iteration, env, o)); + } + void add_type_info(unsigned l, unsigned c, expr const & e) { lock_guard lc(m_mutex); + if (m_block_updates) + return; synch_line(l); m_line_data[l].insert(mk_type_info(c, e)); } void add_synth_info(unsigned l, unsigned c, expr const & e) { lock_guard lc(m_mutex); + if (m_block_updates) + return; synch_line(l); m_line_data[l].insert(mk_synth_info(c, e)); } void add_overload_info(unsigned l, unsigned c, expr const & e) { lock_guard lc(m_mutex); + if (m_block_updates) + return; synch_line(l); m_line_data[l].insert(mk_overload_info(c, e)); } void add_coercion_info(unsigned l, unsigned c, expr const & e) { lock_guard lc(m_mutex); + if (m_block_updates) + return; synch_line(l); m_line_data[l].insert(mk_coercion_info(c, e)); } void add_symbol_info(unsigned l, unsigned c, name const & s) { lock_guard lc(m_mutex); + if (m_block_updates) + return; synch_line(l); m_line_data[l].insert(mk_symbol_info(c, s)); } void add_identifier_info(unsigned l, unsigned c, name const & full_id) { lock_guard lc(m_mutex); + if (m_block_updates) + return; synch_line(l); m_line_data[l].insert(mk_identifier_info(c, full_id)); } @@ -256,6 +310,8 @@ struct info_manager::imp { void instantiate(substitution const & s) { lock_guard lc(m_mutex); + if (m_block_updates) + return; substitution tmp_s = s; unsigned sz = m_line_data.size(); for (unsigned i = 0; i < sz; i++) { @@ -263,25 +319,30 @@ struct info_manager::imp { } } - void merge(info_manager::imp const & m) { + void merge(info_manager::imp const & m, bool overwrite) { if (m.m_line_data.empty()) return; lock_guard lc(m_mutex); + if (m_block_updates) + return; unsigned sz = m.m_line_data.size(); for (unsigned i = 1; i < sz; i++) { info_data_set const & s = m.m_line_data[i]; s.for_each([&](info_data const & d) { synch_line(i); - m_line_data[i].insert(d); + if (overwrite || !m_line_data[i].contains(d)) + m_line_data[i].insert(d); }); } } void insert_line(unsigned l) { lock_guard lc(m_mutex); + if (m_block_updates) + return; synch_line(l); - if (m_available_upto > l - 1) - m_available_upto = l - 1; + if (m_processed_upto > l - 1) + m_processed_upto = l - 1; m_line_data.push_back(info_data_set()); unsigned i = m_line_data.size(); while (i > l) { @@ -294,6 +355,8 @@ struct info_manager::imp { void remove_line(unsigned l) { lock_guard lc(m_mutex); + if (m_block_updates) + return; lean_assert(l > 0); if (l >= m_line_data.size()) return; @@ -303,29 +366,28 @@ struct info_manager::imp { m_line_valid[i] = m_line_valid[i+1]; } m_line_data.pop_back(); - if (m_available_upto > l - 1) - m_available_upto = l - 1; + if (m_processed_upto > l - 1) + m_processed_upto = l - 1; } void invalidate_line(unsigned l) { lock_guard lc(m_mutex); + if (m_block_updates) + return; synch_line(l); - if (m_available_upto > l - 1) - m_available_upto = l - 1; + if (m_processed_upto > l - 1) + m_processed_upto = l - 1; m_line_data[l] = info_data_set(); m_line_valid[l] = false; } void commit_upto(unsigned l, bool valid) { lock_guard lc(m_mutex); - for (unsigned i = m_available_upto; i < l && i < m_line_valid.size(); i++) + if (m_block_updates) + return; + for (unsigned i = m_processed_upto; i < l && i < m_line_valid.size(); i++) m_line_valid[i] = valid; - m_available_upto = l; - } - - bool is_available(unsigned l) { - lock_guard lc(m_mutex); - return l < m_available_upto; + m_processed_upto = l; } bool is_invalidated(unsigned l) { @@ -335,18 +397,60 @@ struct info_manager::imp { return !m_line_valid[l]; } - void display(io_state_stream const & ios, unsigned l) { - lock_guard lc(m_mutex); - if (l >= m_line_data.size()) - return; - m_line_data[l].for_each([&](info_data const & d) { - d.display(ios, l); + void display_core(environment const & env, options const & o, io_state const & ios, unsigned line) { + io_state_stream out = regular(env, ios).update_options(o); + m_line_data[line].for_each([&](info_data const & d) { + d.display(out, line); }); } + void display(environment const & env, io_state const & ios, unsigned line) { + lock_guard lc(m_mutex); + if (line >= m_line_data.size() || m_line_data[line].empty()) { + regular(env, ios) << "-- NAY\n"; + return; + } + if (m_env_info.empty()) { + display_core(env, ios.get_options(), ios, line); + return; + } + auto it = m_env_info.begin(); + auto end = m_env_info.end(); + lean_assert(it != end); + if (it->m_line > line) { + display_core(env, ios.get_options(), ios, line); + return; + } + while (true) { + lean_assert(it->m_line <= line); + lean_assert(it != end); + auto next = it; + ++next; + if (next == end || next->m_line > line) { + display_core(it->m_env, it->m_options, ios, line); + return; + } + it = next; + } + } + + optional> get_final_env_opts() { + lock_guard lc(m_mutex); + if (m_env_info.empty()) { + return optional>(); + } else { + auto it = m_env_info.end(); + --it; + return optional>(mk_pair(it->m_env, it->m_options)); + } + } + void clear() { lock_guard lc(m_mutex); + if (m_block_updates) + return; m_line_data.clear(); + m_line_valid.clear(); } }; @@ -361,13 +465,20 @@ void info_manager::add_identifier_info(unsigned l, unsigned c, name const & full m_ptr->add_identifier_info(l, c, full_id); } void info_manager::instantiate(substitution const & s) { m_ptr->instantiate(s); } -void info_manager::merge(info_manager const & m) { m_ptr->merge(*m.m_ptr); } +void info_manager::merge(info_manager const & m, bool overwrite) { m_ptr->merge(*m.m_ptr, overwrite); } void info_manager::insert_line(unsigned l) { m_ptr->insert_line(l); } void info_manager::remove_line(unsigned l) { m_ptr->remove_line(l); } void info_manager::invalidate_line(unsigned l) { m_ptr->invalidate_line(l); } void info_manager::commit_upto(unsigned l, bool valid) { m_ptr->commit_upto(l, valid); } -bool info_manager::is_available(unsigned l) const { return m_ptr->is_available(l); } bool info_manager::is_invalidated(unsigned l) const { return m_ptr->is_invalidated(l); } -void info_manager::clear() { m_ptr->clear(); } -void info_manager::display(io_state_stream const & ios, unsigned line) { m_ptr->display(ios, line); } +void info_manager::save_environment_options(unsigned l, environment const & env, options const & o) { + m_ptr->save_environment_options(l, env, o); +} +void info_manager::clear() { m_ptr->clear(); } +optional> info_manager::get_final_env_opts() const { + return m_ptr->get_final_env_opts(); +} +void info_manager::display(environment const & env, io_state const & ios, unsigned line) { + m_ptr->display(env, ios, line); +} } diff --git a/src/frontends/lean/info_manager.h b/src/frontends/lean/info_manager.h index 11e827d19..697452ee2 100644 --- a/src/frontends/lean/info_manager.h +++ b/src/frontends/lean/info_manager.h @@ -25,14 +25,15 @@ public: void add_symbol_info(unsigned l, unsigned c, name const & n); void add_identifier_info(unsigned l, unsigned c, name const & full_id); void instantiate(substitution const & s); - void merge(info_manager const & m); + void merge(info_manager const & m, bool overwrite); void insert_line(unsigned l); void remove_line(unsigned l); void invalidate_line(unsigned l); void commit_upto(unsigned l, bool valid); - bool is_available(unsigned l) const; bool is_invalidated(unsigned l) const; + void save_environment_options(unsigned l, environment const & env, options const & o); + optional> get_final_env_opts() const; void clear(); - void display(io_state_stream const & ios, unsigned line); + void display(environment const & env, io_state const & ios, unsigned line); }; } diff --git a/src/frontends/lean/parser.cpp b/src/frontends/lean/parser.cpp index c42d4f7d0..85a2e17c3 100644 --- a/src/frontends/lean/parser.cpp +++ b/src/frontends/lean/parser.cpp @@ -1209,6 +1209,14 @@ void parser::parse_imports() { } } +void parser::commit_info(unsigned line) { + save_snapshot(); + if (m_info_manager) { + m_info_manager->save_environment_options(line, m_env, m_ios.get_options()); + m_info_manager->commit_upto(line, true); + } +} + bool parser::parse_commands() { // We disable hash-consing while parsing to make sure the pos-info are correct. scoped_expr_caching disable(false); @@ -1230,9 +1238,7 @@ bool parser::parse_commands() { switch (curr()) { case scanner::token_kind::CommandKeyword: parse_command(); - save_snapshot(); - if (m_info_manager) - m_info_manager->commit_upto(m_scanner.get_line(), true); + commit_info(m_scanner.get_line()); break; case scanner::token_kind::ScriptBlock: parse_script(); @@ -1260,9 +1266,7 @@ bool parser::parse_commands() { throw_parser_exception("invalid end of module, expecting 'end'", pos()); } } catch (interrupt_parser) {} - save_snapshot(); - if (m_info_manager) - m_info_manager->commit_upto(m_scanner.get_line()+1, true); + commit_info(m_scanner.get_line()+1); for (certified_declaration const & thm : m_theorem_queue.join()) { m_env.replace(thm); } @@ -1299,7 +1303,8 @@ void parser::save_snapshot() { void parser::save_pre_info_data() { // if elaborator failed, then m_pre_info_data contains type information before elaboration. if (m_info_manager) { - m_info_manager->merge(m_pre_info_manager); + bool overwrite = false; + m_info_manager->merge(m_pre_info_manager, overwrite); m_pre_info_manager.clear(); } } diff --git a/src/frontends/lean/parser.h b/src/frontends/lean/parser.h index 9c9b2aa6c..45e484b19 100644 --- a/src/frontends/lean/parser.h +++ b/src/frontends/lean/parser.h @@ -170,6 +170,7 @@ class parser { void save_type_info(expr const & e); void save_pre_info_data(); void save_identifier_info(pos_info const & p, name const & full_id); + void commit_info(unsigned line); elaborator_context mk_elaborator_context(pos_info_provider const & pp, bool check_unassigned = true); elaborator_context mk_elaborator_context(environment const & env, pos_info_provider const & pp); diff --git a/src/frontends/lean/server.cpp b/src/frontends/lean/server.cpp index 1ecb1b40b..d2b8a3478 100644 --- a/src/frontends/lean/server.cpp +++ b/src/frontends/lean/server.cpp @@ -256,31 +256,17 @@ void server::set_option(std::string const & line) { void server::show_info(unsigned linenum) { check_file(); - if (!m_file->m_info.is_available(linenum)) { - m_out << "-- BEGININFO\n-- NAY\n-- ENDINFO" << std::endl; - return; - } - unsigned i = m_file->find(linenum); - environment const & env = i == 0 ? m_env : m_file->m_snapshots[i-1].m_env; - options const & o = i == 0 ? m_ios.get_options() : m_file->m_snapshots[i-1].m_options; - scoped_updt_options updt(m_ios, o); - io_state_stream out(env, m_ios); - out << "-- BEGININFO" << endl; - m_file->m_info.display(out, linenum); - out << "-- ENDINFO" << endl; + m_out << "-- BEGININFO" << std::endl; + m_file->m_info.display(m_env, m_ios, linenum); + m_out << "-- ENDINFO" << std::endl; } -void server::eval(std::string const & line) { - if (!m_file->m_info.is_available(m_file->m_lines.size())) { - m_out << "-- BEGINEVAL\n-- NAY\n-- ENDEVAL" << std::endl; - return; - } - snapshot & s = !m_file || m_file->m_snapshots.empty() ? m_empty_snapshot : m_file->m_snapshots.back(); +void server::eval_core(environment const & env, options const & o, std::string const & line) { std::istringstream strm(line); - scoped_updt_options updt(m_ios, s.m_options); + scoped_updt_options updt(m_ios, o); m_out << "-- BEGINEVAL" << std::endl; try { - parser p(s.m_env, m_ios, strm, "EVAL_command", true, 1, s.m_lds, s.m_eds, 1); + parser p(env, m_ios, strm, "EVAL_command", true); p(); } catch (exception & ex) { m_out << ex.what() << std::endl; @@ -288,6 +274,16 @@ void server::eval(std::string const & line) { m_out << "-- ENDEVAL" << std::endl; } +void server::eval(std::string const & line) { + if (!m_file) { + eval_core(m_env, m_ios.get_options(), line); + } else if (auto p = m_file->m_info.get_final_env_opts()) { + eval_core(p->first, p->second, line); + } else { + eval_core(m_env, m_ios.get_options(), line); + } +} + bool server::operator()(std::istream & in) { for (std::string line; std::getline(in, line);) { try { diff --git a/src/frontends/lean/server.h b/src/frontends/lean/server.h index e3397acd5..3002ef9bb 100644 --- a/src/frontends/lean/server.h +++ b/src/frontends/lean/server.h @@ -53,6 +53,7 @@ class server { void show_info(unsigned linenum); void process_from(unsigned linenum); void set_option(std::string const & line); + void eval_core(environment const & env, options const & o, std::string const & line); void eval(std::string const & line); unsigned find(unsigned linenum); void read_line(std::istream & in, std::string & line); diff --git a/tests/lean/interactive/in2.input b/tests/lean/interactive/in2.input new file mode 100644 index 000000000..fe0b596c9 --- /dev/null +++ b/tests/lean/interactive/in2.input @@ -0,0 +1,10 @@ +EVAL +check Type.{1} +SET +pp.universes true +EVAL +check Type.{1} +VISIT simple.lean +WAIT +EVAL +check tst.foo diff --git a/tests/lean/interactive/in2.input.expected.out b/tests/lean/interactive/in2.input.expected.out new file mode 100644 index 000000000..36c751f4c --- /dev/null +++ b/tests/lean/interactive/in2.input.expected.out @@ -0,0 +1,11 @@ +-- BEGINEVAL +Type : Type +-- ENDEVAL +-- BEGINSET +-- ENDSET +-- BEGINEVAL +Type.{1} : Type.{2} +-- ENDEVAL +-- BEGINEVAL +tst.foo.{l_1 l_2} : ?M_1 → ?M_2 → ?M_1 +-- ENDEVAL