1578 lines
55 KiB
C++
1578 lines
55 KiB
C++
/*
|
|
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
Author: Leonardo de Moura
|
|
*/
|
|
#include <string>
|
|
#include <vector>
|
|
#include "util/sstream.h"
|
|
#include "kernel/for_each_fn.h"
|
|
#include "kernel/find_fn.h"
|
|
#include "kernel/replace_fn.h"
|
|
#include "kernel/instantiate.h"
|
|
#include "kernel/abstract.h"
|
|
#include "kernel/type_checker.h"
|
|
#include "library/replace_visitor.h"
|
|
#include "library/util.h"
|
|
#include "library/defeq_simp_lemmas.h"
|
|
#include "library/defeq_simplifier.h"
|
|
#include "library/trace.h"
|
|
#include "library/reducible.h"
|
|
#include "library/class.h"
|
|
#include "library/constants.h"
|
|
#include "library/type_context.h"
|
|
#include "library/relation_manager.h"
|
|
#include "library/congr_lemma_manager.h"
|
|
#include "library/abstract_expr_manager.h"
|
|
#include "library/proof_irrel_expr_manager.h"
|
|
#include "library/light_lt_manager.h"
|
|
#include "library/projection.h"
|
|
#include "library/scoped_ext.h"
|
|
#include "library/tactic/goal.h"
|
|
#include "library/blast/state.h"
|
|
#include "library/blast/blast.h"
|
|
#include "library/blast/proof_expr.h"
|
|
#include "library/blast/blast_exception.h"
|
|
#include "library/blast/choice_point.h"
|
|
#include "library/blast/congruence_closure.h"
|
|
#include "library/blast/trace.h"
|
|
#include "library/blast/options.h"
|
|
#include "library/blast/strategies/portfolio.h"
|
|
#include "library/blast/simplifier/simp_lemmas.h"
|
|
|
|
namespace lean {
|
|
namespace blast {
|
|
static name * g_ref_prefix = nullptr;
|
|
static expr * g_dummy_type = nullptr; // dummy type for href/mref
|
|
|
|
class imp_extension_manager {
|
|
std::vector<pair<ext_state_maker &, unsigned> > m_entries;
|
|
public:
|
|
std::vector<pair<ext_state_maker &, unsigned> > const & get_entries() { return m_entries; }
|
|
|
|
unsigned register_imp_extension(ext_state_maker & state_maker) {
|
|
unsigned state_id = m_entries.size();
|
|
unsigned ext_id = register_branch_extension(new imp_extension(state_id));
|
|
m_entries.emplace_back(state_maker, ext_id);
|
|
return state_id;
|
|
}
|
|
};
|
|
|
|
static imp_extension_manager * g_imp_extension_manager = nullptr;
|
|
static imp_extension_manager & get_imp_extension_manager() {
|
|
return *g_imp_extension_manager;
|
|
}
|
|
|
|
struct imp_extension_entry {
|
|
std::unique_ptr<imp_extension_state> m_ext_state;
|
|
unsigned m_ext_id;
|
|
imp_extension * m_ext_of_ext_state;
|
|
imp_extension_entry(imp_extension_state * ext_state, unsigned ext_id, imp_extension * ext_of_ext_state):
|
|
m_ext_state(ext_state), m_ext_id(ext_id), m_ext_of_ext_state(ext_of_ext_state) {}
|
|
};
|
|
|
|
unsigned proof_irrel_hash(expr const & e);
|
|
bool proof_irrel_is_equal(expr const & e1, expr const & e2);
|
|
|
|
class tmp_tctx_pool : public tmp_type_context_pool {
|
|
public:
|
|
virtual tmp_type_context * mk_tmp_type_context() override;
|
|
virtual void recycle_tmp_type_context(tmp_type_context * tmp_tctx) override;
|
|
};
|
|
|
|
class blastenv {
|
|
friend class scope_assignment;
|
|
friend class scope_unfold_macro_pred;
|
|
typedef std::vector<tmp_type_context *> tmp_type_context_pool;
|
|
typedef std::unique_ptr<tmp_type_context> tmp_type_context_ptr;
|
|
typedef std::vector<imp_extension_entry> imp_extension_entries;
|
|
|
|
environment m_env;
|
|
io_state m_ios;
|
|
/* blast may use different strategies.
|
|
We use m_buffer_ios to store messages describing failed attempts.
|
|
These messages are reported to the user only if none of the strategies have worked.
|
|
We dump the content of the diagnostic channel into an blast_exception. */
|
|
io_state m_buffer_ios;
|
|
unsigned m_next_uref_idx{0};
|
|
unsigned m_next_mref_idx{0};
|
|
unsigned m_next_href_idx{0};
|
|
unsigned m_next_choice_idx{0};
|
|
unsigned m_next_split_idx{0};
|
|
list<expr> m_initial_context; // for setting type_context local instances
|
|
name_set m_lemma_hints;
|
|
name_set m_unfold_hints;
|
|
name_map<level> m_uvar2uref; // map global universe metavariables to blast uref's
|
|
name_map<pair<expr, expr>> m_mvar2meta_mref; // map global metavariables to blast mref's
|
|
name_predicate m_not_reducible_pred;
|
|
name_predicate m_class_pred;
|
|
name_predicate m_instance_pred;
|
|
name_map<projection_info> m_projection_info;
|
|
is_relation_pred m_is_relation_pred;
|
|
state m_curr_state; // current state
|
|
tmp_type_context_pool m_tmp_ctx_pool;
|
|
tmp_type_context_ptr m_tmp_ctx; // for app_builder and congr_lemma_manager
|
|
app_builder m_app_builder;
|
|
fun_info_manager m_fun_info_manager;
|
|
congr_lemma_manager m_congr_lemma_manager;
|
|
abstract_expr_manager m_abstract_expr_manager;
|
|
proof_irrel_expr_manager m_proof_irrel_expr_manager;
|
|
light_lt_manager m_light_lt_manager;
|
|
imp_extension_entries m_imp_extension_entries;
|
|
relation_info_getter m_rel_getter;
|
|
refl_info_getter m_refl_getter;
|
|
symm_info_getter m_symm_getter;
|
|
trans_info_getter m_trans_getter;
|
|
unfold_macro_pred m_unfold_macro_pred;
|
|
bool m_classical{false};
|
|
|
|
bool is_extra_opaque(name const & n) const {
|
|
// TODO(Leo, Daniel): should we force 'not' to be always opaque?
|
|
// If we do that, we can remove the whnf-trick from unit_propagate.
|
|
// We can also avoid the `is_pi(type) && !is_prop(type)`
|
|
if (n == get_ne_name())
|
|
return false;
|
|
return
|
|
(m_not_reducible_pred(n) ||
|
|
m_projection_info.contains(n));
|
|
}
|
|
|
|
class tctx : public type_context {
|
|
blastenv & m_benv;
|
|
std::vector<state::assignment_snapshot> m_stack;
|
|
public:
|
|
tctx(blastenv & benv):
|
|
type_context(benv.m_env, benv.m_ios.get_options()),
|
|
m_benv(benv) {}
|
|
|
|
virtual bool is_extra_opaque(name const & n) const override {
|
|
return m_benv.is_extra_opaque(n);
|
|
}
|
|
|
|
virtual bool should_unfold_macro(expr const & e) const override {
|
|
return m_benv.m_unfold_macro_pred(e);
|
|
}
|
|
|
|
virtual bool is_uvar(level const & l) const override {
|
|
return blast::is_uref(l);
|
|
}
|
|
|
|
virtual bool is_mvar(expr const & e) const override {
|
|
return blast::is_mref(e);
|
|
}
|
|
|
|
virtual optional<level> get_assignment(level const & u) const override {
|
|
if (auto v = m_benv.m_curr_state.get_uref_assignment(u))
|
|
return some_level(*v);
|
|
else
|
|
return none_level();
|
|
}
|
|
|
|
virtual optional<expr> get_assignment(expr const & m) const override {
|
|
if (auto v = m_benv.m_curr_state.get_mref_assignment(m))
|
|
return some_expr(*v);
|
|
else
|
|
return none_expr();
|
|
}
|
|
|
|
virtual void update_assignment(level const & u, level const & v) override {
|
|
m_benv.m_curr_state.assign_uref(u, v);
|
|
}
|
|
|
|
virtual void update_assignment(expr const & m, expr const & v) override {
|
|
m_benv.m_curr_state.assign_mref(m, v);
|
|
}
|
|
|
|
bool check_href_core(metavar_decl const & d, expr const & h, hypothesis_idx_set & visited) {
|
|
lean_assert(is_href(h));
|
|
lean_assert(!d.contains_href(h));
|
|
if (visited.contains(href_index(h)))
|
|
return true;
|
|
visited.insert(href_index(h));
|
|
state & s = m_benv.m_curr_state;
|
|
hypothesis const & h_decl = s.get_hypothesis_decl(h);
|
|
if (h_decl.is_assumption())
|
|
return false;
|
|
return !find(*h_decl.get_value(), [&](expr const & e, unsigned) {
|
|
return is_href(e) && !d.contains_href(e) && !check_href_core(d, e, visited);
|
|
});
|
|
}
|
|
|
|
bool check_href(metavar_decl const & d, expr const & h) {
|
|
lean_assert(is_href(h));
|
|
if (d.contains_href(h))
|
|
return true;
|
|
hypothesis_idx_set visited;
|
|
return check_href_core(d, h, visited);
|
|
}
|
|
|
|
virtual bool validate_assignment(expr const & m, buffer<expr> const & locals, expr const & v) override {
|
|
// We must check
|
|
// 1. All href in new_v are in the context of m.
|
|
// 2. The context of any (unassigned) mref in new_v must be a subset of the context of m.
|
|
// If it is not we force it to be.
|
|
// 3. Any local constant occurring in new_v occurs in locals
|
|
// 4. m does not occur in new_v
|
|
state & s = m_benv.m_curr_state;
|
|
metavar_decl const * d = s.get_metavar_decl(m);
|
|
lean_assert(d);
|
|
bool ok = true;
|
|
for_each(v, [&](expr const & e, unsigned) {
|
|
if (!ok)
|
|
return false; // stop search
|
|
if (is_href(e)) {
|
|
if (!check_href(*d, e)) {
|
|
ok = false; // failed 1
|
|
return false;
|
|
}
|
|
} else if (is_local(e)) {
|
|
if (std::all_of(locals.begin(), locals.end(), [&](expr const & a) {
|
|
return mlocal_name(a) != mlocal_name(e); })) {
|
|
ok = false; // failed 3
|
|
return false;
|
|
}
|
|
} else if (is_mref(e)) {
|
|
if (m == e) {
|
|
ok = false; // failed 4
|
|
return false;
|
|
}
|
|
s.restrict_mref_context_using(e, m); // enforce 2
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
return ok;
|
|
}
|
|
|
|
/** \brief Return the type of a local constant (local or not).
|
|
\remark This method allows the customer to store the type of local constants
|
|
in a different place. */
|
|
virtual expr infer_local(expr const & e) const override {
|
|
if (is_href(e)) {
|
|
state const & s = m_benv.m_curr_state;
|
|
hypothesis const & h = s.get_hypothesis_decl(e);
|
|
return h.get_type();
|
|
} else {
|
|
return mlocal_type(e);
|
|
}
|
|
}
|
|
|
|
virtual expr infer_metavar(expr const & m) const override {
|
|
// Remark: we do not tolerate external meta-variables here.
|
|
if (is_mref(m)) {
|
|
state const & s = m_benv.m_curr_state;
|
|
metavar_decl const * d = s.get_metavar_decl(m);
|
|
lean_assert(d);
|
|
return d->get_type();
|
|
} else {
|
|
return mlocal_type(m);
|
|
}
|
|
}
|
|
|
|
virtual level mk_uvar() override {
|
|
return mk_fresh_uref();
|
|
}
|
|
|
|
virtual expr mk_mvar(expr const & type) override {
|
|
return m_benv.m_curr_state.mk_metavar(type);
|
|
}
|
|
|
|
virtual void push_core() override {
|
|
m_stack.push_back(m_benv.m_curr_state.save_assignment());
|
|
}
|
|
|
|
virtual void pop_core() override {
|
|
m_benv.m_curr_state.restore_assignment(m_stack.back());
|
|
m_stack.pop_back();
|
|
}
|
|
|
|
virtual unsigned get_num_check_points() const override {
|
|
return m_stack.size();
|
|
}
|
|
|
|
virtual void commit() override {
|
|
m_stack.pop_back();
|
|
}
|
|
|
|
virtual name get_local_pp_name(expr const & e) const override {
|
|
if (is_href(e)) {
|
|
state const & s = m_benv.m_curr_state;
|
|
hypothesis const & h = s.get_hypothesis_decl(e);
|
|
return h.get_name();
|
|
} else {
|
|
return local_pp_name(e);
|
|
}
|
|
}
|
|
};
|
|
|
|
class to_blast_expr_fn : public replace_visitor {
|
|
type_checker m_tc;
|
|
state & m_state;
|
|
// We map each metavariable to a metavariable application and the mref associated with it.
|
|
name_map<level> & m_uvar2uref;
|
|
name_map<pair<expr, expr>> & m_mvar2meta_mref;
|
|
name_map<expr> & m_local2href;
|
|
|
|
level to_blast_level(level const & l) {
|
|
level lhs;
|
|
switch (l.kind()) {
|
|
case level_kind::Succ: return mk_succ(to_blast_level(succ_of(l)));
|
|
case level_kind::Zero: return mk_level_zero();
|
|
case level_kind::Param: return mk_param_univ(param_id(l));
|
|
case level_kind::Global: return mk_global_univ(global_id(l));
|
|
case level_kind::Max:
|
|
lhs = to_blast_level(max_lhs(l));
|
|
return mk_max(lhs, to_blast_level(max_rhs(l)));
|
|
case level_kind::IMax:
|
|
lhs = to_blast_level(imax_lhs(l));
|
|
return mk_imax(lhs, to_blast_level(imax_rhs(l)));
|
|
case level_kind::Meta:
|
|
if (auto r = m_uvar2uref.find(meta_id(l))) {
|
|
return *r;
|
|
} else {
|
|
level uref = mk_fresh_uref();
|
|
m_uvar2uref.insert(meta_id(l), uref);
|
|
return uref;
|
|
}
|
|
}
|
|
lean_unreachable();
|
|
}
|
|
|
|
virtual expr visit_sort(expr const & e) override {
|
|
return mk_sort(to_blast_level(sort_level(e)));
|
|
}
|
|
|
|
virtual expr visit_macro(expr const & e) override {
|
|
buffer<expr> new_args;
|
|
for (unsigned i = 0; i < macro_num_args(e); i++) {
|
|
new_args.push_back(visit(macro_arg(e, i)));
|
|
}
|
|
return mk_macro(macro_def(e), new_args.size(), new_args.data());
|
|
}
|
|
|
|
virtual expr visit_constant(expr const & e) override {
|
|
levels new_ls = map(const_levels(e), [&](level const & l) { return to_blast_level(l); });
|
|
return mk_constant(const_name(e), new_ls);
|
|
}
|
|
|
|
virtual expr visit_var(expr const & e) override {
|
|
return mk_var(var_idx(e));
|
|
}
|
|
|
|
void throw_unsupported_metavar_occ(expr const & e) {
|
|
// TODO(Leo): improve error message
|
|
throw blast_exception("'blast' tactic failed, goal contains a "
|
|
"meta-variable application that is not supported", e);
|
|
}
|
|
|
|
expr mk_mref_app(expr const & mref, unsigned nargs, expr const * args) {
|
|
lean_assert(is_mref(mref));
|
|
buffer<expr> new_args;
|
|
for (unsigned i = 0; i < nargs; i++) {
|
|
new_args.push_back(visit(args[i]));
|
|
}
|
|
return mk_app(mref, new_args.size(), new_args.data());
|
|
}
|
|
|
|
expr visit_meta_app(expr const & e) {
|
|
lean_assert(is_meta(e));
|
|
buffer<expr> args;
|
|
expr const & mvar = get_app_args(e, args);
|
|
if (pair<expr, expr> const * meta_mref = m_mvar2meta_mref.find(mlocal_name(mvar))) {
|
|
lean_assert(is_meta(meta_mref->first));
|
|
lean_assert(is_mref(meta_mref->second));
|
|
buffer<expr> decl_args;
|
|
get_app_args(meta_mref->first, decl_args);
|
|
if (decl_args.size() > args.size())
|
|
throw_unsupported_metavar_occ(e);
|
|
// Make sure the the current metavariable application prefix matches the one
|
|
// found before.
|
|
for (unsigned i = 0; i < decl_args.size(); i++) {
|
|
if (is_local(decl_args[i])) {
|
|
if (!is_local(args[i]) || mlocal_name(args[i]) != mlocal_name(decl_args[i]))
|
|
throw_unsupported_metavar_occ(e);
|
|
} else if (decl_args[i] != args[i]) {
|
|
throw_unsupported_metavar_occ(e);
|
|
}
|
|
}
|
|
return mk_mref_app(meta_mref->second, args.size() - decl_args.size(), args.data() + decl_args.size());
|
|
} else {
|
|
unsigned i = 0;
|
|
hypothesis_idx_buffer ctx;
|
|
// Find prefix that contains only closed terms.
|
|
for (; i < args.size(); i++) {
|
|
if (!closed(args[i]))
|
|
break;
|
|
if (!is_local(args[i])) {
|
|
// Ignore arguments that are not local constants.
|
|
// In the blast tactic we only support higher-order patterns.
|
|
continue;
|
|
}
|
|
expr const & l = args[i];
|
|
if (!std::all_of(args.begin(), args.begin() + i,
|
|
[&](expr const & prev) { return mlocal_name(prev) != mlocal_name(l); })) {
|
|
// Local has already been processed
|
|
continue;
|
|
}
|
|
auto href = m_local2href.find(mlocal_name(l));
|
|
if (!href) {
|
|
// One of the arguments is a local constant that is not in m_local2href
|
|
throw_unsupported_metavar_occ(e);
|
|
}
|
|
ctx.push_back(href_index(*href));
|
|
}
|
|
unsigned prefix_sz = i;
|
|
expr aux = e;
|
|
for (; i < args.size(); i++)
|
|
aux = app_fn(aux);
|
|
lean_assert(is_meta(aux));
|
|
expr type = visit(m_tc.infer(aux).first);
|
|
expr mref = m_state.mk_metavar(ctx, type);
|
|
m_mvar2meta_mref.insert(mlocal_name(mvar), mk_pair(aux, mref));
|
|
return mk_mref_app(mref, args.size() - prefix_sz, args.data() + prefix_sz);
|
|
}
|
|
}
|
|
|
|
virtual expr visit_meta(expr const & e) override {
|
|
return visit_meta_app(e);
|
|
}
|
|
|
|
virtual expr visit_local(expr const & e) override {
|
|
if (auto r = m_local2href.find(mlocal_name(e)))
|
|
return * r;
|
|
else
|
|
throw blast_exception("blast tactic failed, ill-formed input goal", e);
|
|
}
|
|
|
|
virtual expr visit_app(expr const & e) override {
|
|
if (is_meta(e)) {
|
|
return visit_meta_app(e);
|
|
} else {
|
|
expr f = visit(app_fn(e));
|
|
return mk_app(f, visit(app_arg(e)));
|
|
}
|
|
}
|
|
|
|
virtual expr visit_lambda(expr const & e) override {
|
|
expr d = visit(binding_domain(e));
|
|
return mk_lambda(binding_name(e), d, visit(binding_body(e)), binding_info(e));
|
|
}
|
|
|
|
virtual expr visit_pi(expr const & e) override {
|
|
expr d = visit(binding_domain(e));
|
|
return mk_pi(binding_name(e), d, visit(binding_body(e)), binding_info(e));
|
|
}
|
|
|
|
virtual expr visit_let(expr const & e) override {
|
|
expr t = visit(let_type(e));
|
|
expr v = visit(let_value(e));
|
|
return mk_let(let_name(e), t, v, visit(let_body(e)));
|
|
}
|
|
|
|
public:
|
|
to_blast_expr_fn(environment const & env, state & s,
|
|
name_map<level> & uvar2uref, name_map<pair<expr, expr>> & mvar2meta_mref,
|
|
name_map<expr> & local2href):
|
|
m_tc(env), m_state(s), m_uvar2uref(uvar2uref), m_mvar2meta_mref(mvar2meta_mref), m_local2href(local2href) {}
|
|
};
|
|
|
|
void init_curr_state(goal const & g) {
|
|
state & s = curr_state();
|
|
name_map<expr> local2href;
|
|
to_blast_expr_fn to_blast_expr(m_env, s, m_uvar2uref, m_mvar2meta_mref, local2href);
|
|
buffer<expr> hs;
|
|
g.get_hyps(hs);
|
|
for (expr const & h : hs) {
|
|
lean_assert(is_local(h));
|
|
if (!local_info(h).is_rec()) {
|
|
/*
|
|
We do not add auxiliary locals used to compile recursive equations.
|
|
The problem is that blast doesn't know when it is safe to use this kind of hypothesis.
|
|
For example: suppose we are defining the following function using recursive equations and blast.
|
|
|
|
lemma comm : ∀ a b : nat, a + b = b + a
|
|
| a 0 := by simp
|
|
| a (succ n) := by simp
|
|
|
|
Both goals will contain the (rec) hypothesis
|
|
|
|
comm : ∀ a b : nat, a + b = b + a
|
|
|
|
If we the recursive equation is being compiled using structural recursion, then we can only apply
|
|
'comm' to strucuturall smaller terms. If we are using well-founded recursion, then we need the well-founded relation.
|
|
Blast does not have access to this information. We address this issue by simply ignoring this kind of
|
|
auxiliary rec hypothesis.
|
|
|
|
Of course, this workaround forces the user to provide a valid induction hypothesis.
|
|
Example:
|
|
|
|
lemma comm : ∀ a b : nat, a + b = b + a
|
|
| a 0 := by simp
|
|
| a (succ n) :=
|
|
assert a + n = n + a, from !comm,
|
|
by simp
|
|
|
|
In this simple example, we can simply ask blast to apply the recursor automatically for use.
|
|
|
|
lemma comm : ∀ a b : nat, a + b = b + a :=
|
|
by rec_simp
|
|
|
|
However, this is not always possible. Sometimes, we will be defining a complex function using recursive equations.
|
|
The definition may contain nested proofs that we may want to discharge using blast.
|
|
*/
|
|
expr new_type = normalize(to_blast_expr(mlocal_type(h)));
|
|
expr href = s.mk_hypothesis(local_pp_name(h), new_type, h);
|
|
local2href.insert(mlocal_name(h), href);
|
|
}
|
|
}
|
|
expr new_target = normalize(to_blast_expr(g.get_type()));
|
|
s.set_target(new_target);
|
|
lean_assert(s.check_invariant());
|
|
}
|
|
|
|
tctx m_tctx;
|
|
|
|
/* Normalizing instances */
|
|
normalizer m_normalizer;
|
|
|
|
struct inst_key {
|
|
expr m_e;
|
|
unsigned m_hash;
|
|
|
|
inst_key(expr const & e):
|
|
m_e(e), m_hash(blast::proof_irrel_hash(e)) { }
|
|
};
|
|
|
|
struct inst_key_hash_fn {
|
|
unsigned operator()(inst_key const & k) const { return k.m_hash; }
|
|
};
|
|
|
|
struct inst_key_eq_fn {
|
|
bool operator()(inst_key const & k1, inst_key const & k2) const {
|
|
return blast::proof_irrel_is_equal(k1.m_e, k2.m_e);
|
|
}
|
|
};
|
|
|
|
typedef std::unordered_map<inst_key, expr, inst_key_hash_fn, inst_key_eq_fn> inst_cache;
|
|
inst_cache m_inst_nf_to_cf;
|
|
expr_map<expr> m_inst_to_nf;
|
|
|
|
expr_map<expr> m_norm_cache; // normalization cache
|
|
|
|
|
|
void save_initial_context() {
|
|
hypothesis_idx_buffer hidxs;
|
|
m_curr_state.get_sorted_hypotheses(hidxs);
|
|
buffer<expr> ctx;
|
|
for (unsigned hidx : hidxs) {
|
|
ctx.push_back(mk_href(hidx));
|
|
}
|
|
m_initial_context = to_list(ctx);
|
|
for (auto ctx : m_tmp_ctx_pool) delete ctx;
|
|
m_tmp_ctx_pool.clear();
|
|
}
|
|
|
|
name_map<level> mk_uref2uvar() const {
|
|
name_map<level> r;
|
|
m_uvar2uref.for_each([&](name const & uvar_id, level const & uref) {
|
|
lean_assert(is_uref(uref));
|
|
r.insert(meta_id(uref), mk_meta_univ(uvar_id));
|
|
});
|
|
return r;
|
|
}
|
|
|
|
name_map<expr> mk_mref2meta() const {
|
|
name_map<expr> r;
|
|
m_mvar2meta_mref.for_each([&](name const &, pair<expr, expr> const & p) {
|
|
lean_assert(is_mref(p.second));
|
|
r.insert(mlocal_name(p.second), p.first);
|
|
});
|
|
return r;
|
|
}
|
|
|
|
level restore_uvars(level const & l, name_map<level> const & uref2uvar) const {
|
|
return replace(l, [&](level const & l) {
|
|
if (is_meta(l)) {
|
|
if (auto uvar = uref2uvar.find(meta_id(l)))
|
|
return some_level(*uvar);
|
|
}
|
|
return none_level();
|
|
});
|
|
}
|
|
|
|
levels restore_uvars(levels const & ls, name_map<level> const & uref2uvar) const {
|
|
return map(ls, [&](level const & l) { return restore_uvars(l, uref2uvar); });
|
|
}
|
|
|
|
/* Convert uref's and mref's back into tactic metavariables */
|
|
expr restore_uvars_mvars(expr const & e, name_map<level> const & uref2uvar, name_map<expr> const & mref2meta) const {
|
|
return replace(e, [&](expr const & e, unsigned) {
|
|
if (is_mref(e)) {
|
|
if (auto m = mref2meta.find(mlocal_name(e))) {
|
|
return some_expr(*m);
|
|
} else {
|
|
throw blast_exception(sstream() << "blast tactic failed, resultant proof still contains internal meta-variables");
|
|
}
|
|
} else if (is_sort(e)) {
|
|
return some_expr(update_sort(e, restore_uvars(sort_level(e), uref2uvar)));
|
|
} else if (is_constant(e)) {
|
|
return some_expr(update_constant(e, restore_uvars(const_levels(e), uref2uvar)));
|
|
} else {
|
|
return none_expr();
|
|
}
|
|
});
|
|
}
|
|
|
|
level to_tactic_univ(level const & l, name_map<level> const & uref2uvar) {
|
|
return restore_uvars(m_curr_state.instantiate_urefs(l), uref2uvar);
|
|
}
|
|
|
|
expr to_tactic_expr(expr const & pr, name_map<level> const & uref2uvar, name_map<expr> const & mref2meta) {
|
|
// When a proof is found we must
|
|
// 1- Remove all occurrences of href's from pr
|
|
expr pr1 = unfold_hypotheses_ge(m_curr_state, pr, 0);
|
|
// 2- Replace mrefs with their assignments,
|
|
// and convert unassigned meta-variables back into
|
|
// tactic meta-variables.
|
|
expr pr2 = m_curr_state.instantiate_urefs_mrefs(pr1);
|
|
return restore_uvars_mvars(pr2, uref2uvar, mref2meta);
|
|
}
|
|
|
|
|
|
/* The external tactic meta-variables that have been instantiated
|
|
by blast must also be communicated back to the tactic framework. */
|
|
constraint_seq mk_cnstrs_for_assignments(name_map<level> const & uref2uvar, name_map<expr> const & mref2meta) {
|
|
constraint_seq r;
|
|
justification j = mk_justification("assigned by blast");
|
|
m_uvar2uref.for_each([&](name const & uvar_id, level const & uref) {
|
|
lean_assert(is_uref(uref));
|
|
if (auto v = m_curr_state.get_uref_assignment(uref)) {
|
|
r += mk_level_eq_cnstr(mk_meta_univ(uvar_id), to_tactic_univ(*v, uref2uvar), j);
|
|
}
|
|
});
|
|
m_mvar2meta_mref.for_each([&](name const &, pair<expr, expr> const & p) {
|
|
lean_assert(is_mref(p.second));
|
|
if (auto v = m_curr_state.get_mref_assignment(p.second)) {
|
|
r += mk_eq_cnstr(p.first, to_tactic_expr(*v, uref2uvar, mref2meta), j);
|
|
}
|
|
});
|
|
return r;
|
|
}
|
|
|
|
pair<expr, constraint_seq> to_tactic_proof(expr const & pr) {
|
|
name_map<level> uref2uvar = mk_uref2uvar();
|
|
name_map<expr> mref2meta = mk_mref2meta();
|
|
return mk_pair(to_tactic_expr(pr, uref2uvar, mref2meta), mk_cnstrs_for_assignments(uref2uvar, mref2meta));
|
|
}
|
|
|
|
public:
|
|
blastenv(environment const & env, io_state const & ios, list<name> const & ls, list<name> const & ds):
|
|
m_env(env), m_ios(ios), m_buffer_ios(ios),
|
|
m_lemma_hints(to_name_set(ls)), m_unfold_hints(to_name_set(ds)),
|
|
m_not_reducible_pred(mk_not_reducible_pred(env)),
|
|
m_class_pred(mk_class_pred(env)),
|
|
m_instance_pred(mk_instance_pred(env)),
|
|
m_is_relation_pred(mk_is_relation_pred(env)),
|
|
m_tmp_ctx(mk_tmp_type_context()),
|
|
m_app_builder(*m_tmp_ctx),
|
|
m_fun_info_manager(*m_tmp_ctx),
|
|
m_congr_lemma_manager(m_app_builder, m_fun_info_manager),
|
|
m_abstract_expr_manager(m_congr_lemma_manager),
|
|
m_proof_irrel_expr_manager(m_fun_info_manager),
|
|
m_light_lt_manager(env),
|
|
m_rel_getter(mk_relation_info_getter(env)),
|
|
m_refl_getter(mk_refl_info_getter(env)),
|
|
m_symm_getter(mk_symm_info_getter(env)),
|
|
m_trans_getter(mk_trans_info_getter(env)),
|
|
m_unfold_macro_pred([](expr const &) { return true; }),
|
|
m_tctx(*this),
|
|
m_normalizer(m_tctx) {
|
|
m_buffer_ios.set_diagnostic_channel(std::shared_ptr<output_channel>(new string_output_channel()));
|
|
clear_choice_points();
|
|
}
|
|
|
|
~blastenv() {
|
|
finalize_imp_extension_entries();
|
|
for (auto ctx : m_tmp_ctx_pool)
|
|
delete ctx;
|
|
}
|
|
|
|
void init_classical_flag() {
|
|
if (is_standard(env())) {
|
|
expr p = m_tmp_ctx->mk_tmp_local(mk_Prop());
|
|
expr dec_p = mk_app(mk_constant(get_decidable_name()), p);
|
|
if (m_tmp_ctx->mk_class_instance(dec_p)) {
|
|
m_classical = true;
|
|
}
|
|
m_tmp_ctx->clear_cache();
|
|
}
|
|
}
|
|
|
|
bool classical() { return m_classical; }
|
|
|
|
void init_state(goal const & g) {
|
|
init_curr_state(g);
|
|
init_imp_extension_entries();
|
|
save_initial_context();
|
|
m_tctx.set_local_instances(m_initial_context);
|
|
m_tmp_ctx->set_local_instances(m_initial_context);
|
|
init_classical_flag();
|
|
}
|
|
|
|
pair<expr, constraint_seq> operator()(goal const & g) {
|
|
init_state(g);
|
|
if (auto r = apply_strategy()) {
|
|
return to_tactic_proof(*r);
|
|
} else {
|
|
string_output_channel & channel = static_cast<string_output_channel &>(m_buffer_ios.get_diagnostic_channel());
|
|
std::string buffer = channel.str();
|
|
if (buffer.empty()) {
|
|
throw blast_exception(sstream() << " blast tactic failed");
|
|
} else {
|
|
throw blast_exception(sstream() << " blast tactic failed\n" << buffer << "-------");
|
|
}
|
|
}
|
|
}
|
|
|
|
environment const & get_env() const { return m_env; }
|
|
|
|
io_state const & get_ios() const { return m_ios; }
|
|
|
|
io_state const & get_buffer_ios() const { return m_buffer_ios; }
|
|
|
|
state & get_curr_state() { return m_curr_state; }
|
|
|
|
bool is_reducible(name const & n) const {
|
|
if (m_not_reducible_pred(n))
|
|
return false;
|
|
return !m_projection_info.contains(n);
|
|
}
|
|
|
|
projection_info const * get_projection_info(name const & n) const {
|
|
return m_projection_info.find(n);
|
|
}
|
|
|
|
unfold_macro_pred get_unfold_macro_pred() const {
|
|
return m_unfold_macro_pred;
|
|
}
|
|
|
|
expr mk_fresh_local(expr const & type, binder_info const & bi) {
|
|
return m_tctx.mk_tmp_local(type, bi);
|
|
}
|
|
bool is_fresh_local(expr const & e) const {
|
|
return m_tctx.is_tmp_local(e);
|
|
}
|
|
expr whnf(expr const & e) { return m_tctx.whnf(e); }
|
|
expr relaxed_whnf(expr const & e) { return m_tctx.relaxed_whnf(e); }
|
|
expr infer_type(expr const & e) { return m_tctx.infer(e); }
|
|
bool is_prop(expr const & e) { return m_tctx.is_prop(e); }
|
|
bool is_def_eq(expr const & e1, expr const & e2) { return m_tctx.is_def_eq(e1, e2); }
|
|
optional<expr> mk_class_instance(expr const & e) {
|
|
m_tmp_ctx->clear();
|
|
return m_tmp_ctx->mk_class_instance(e);
|
|
}
|
|
optional<expr> mk_subsingleton_instance(expr const & type) {
|
|
m_tmp_ctx->clear();
|
|
return m_tmp_ctx->mk_subsingleton_instance(type);
|
|
}
|
|
|
|
tmp_type_context * mk_tmp_type_context();
|
|
|
|
void recycle_tmp_type_context(tmp_type_context * ctx) {
|
|
lean_assert(ctx);
|
|
ctx->clear();
|
|
m_tmp_ctx_pool.push_back(ctx);
|
|
}
|
|
|
|
optional<congr_lemma> mk_congr_lemma_for_simp(expr const & fn, unsigned num_args) {
|
|
return m_congr_lemma_manager.mk_congr_simp(fn, num_args);
|
|
}
|
|
|
|
optional<congr_lemma> mk_congr_lemma_for_simp(expr const & fn) {
|
|
return m_congr_lemma_manager.mk_congr_simp(fn);
|
|
}
|
|
|
|
optional<congr_lemma> mk_specialized_congr_lemma_for_simp(expr const & fn) {
|
|
return m_congr_lemma_manager.mk_specialized_congr_simp(fn);
|
|
}
|
|
|
|
optional<congr_lemma> mk_congr_lemma(expr const & fn, unsigned num_args) {
|
|
return m_congr_lemma_manager.mk_congr(fn, num_args);
|
|
}
|
|
|
|
optional<congr_lemma> mk_congr_lemma(expr const & fn) {
|
|
return m_congr_lemma_manager.mk_congr(fn);
|
|
}
|
|
|
|
optional<congr_lemma> mk_hcongr_lemma(expr const & fn, unsigned num_args) {
|
|
return m_congr_lemma_manager.mk_hcongr(fn, num_args);
|
|
}
|
|
|
|
optional<congr_lemma> mk_specialized_congr_lemma(expr const & a) {
|
|
return m_congr_lemma_manager.mk_specialized_congr(a);
|
|
}
|
|
|
|
optional<congr_lemma> mk_rel_iff_congr(expr const & fn) {
|
|
return m_congr_lemma_manager.mk_rel_iff_congr(fn);
|
|
}
|
|
|
|
optional<congr_lemma> mk_rel_eq_congr(expr const & fn) {
|
|
return m_congr_lemma_manager.mk_rel_eq_congr(fn);
|
|
}
|
|
|
|
fun_info get_fun_info(expr const & fn) {
|
|
return m_fun_info_manager.get(fn);
|
|
}
|
|
|
|
fun_info get_fun_info(expr const & fn, unsigned nargs) {
|
|
return m_fun_info_manager.get(fn, nargs);
|
|
}
|
|
|
|
fun_info get_specialized_fun_info(expr const & a) {
|
|
return m_fun_info_manager.get_specialized(a);
|
|
}
|
|
|
|
unsigned get_specialization_prefix_size(expr const & fn, unsigned nargs) {
|
|
return m_fun_info_manager.get_specialization_prefix_size(fn, nargs);
|
|
}
|
|
|
|
unsigned abstract_hash(expr const & e) {
|
|
return m_abstract_expr_manager.hash(e);
|
|
}
|
|
|
|
unsigned proof_irrel_hash(expr const & e) {
|
|
return m_proof_irrel_expr_manager.hash(e);
|
|
}
|
|
|
|
void init_imp_extension_entries() {
|
|
for (auto & p : get_imp_extension_manager().get_entries()) {
|
|
branch_extension & b_ext = curr_state().get_extension(p.second);
|
|
b_ext.inc_ref();
|
|
m_imp_extension_entries.emplace_back(p.first(), p.second, static_cast<imp_extension*>(&b_ext));
|
|
}
|
|
}
|
|
|
|
void finalize_imp_extension_entries() {
|
|
for (auto & e : m_imp_extension_entries) {
|
|
e.m_ext_of_ext_state->dec_ref();
|
|
}
|
|
}
|
|
|
|
void get_ext_path(imp_extension * _imp_ext, buffer<imp_extension*> & path) {
|
|
imp_extension * imp_ext = _imp_ext;
|
|
while (imp_ext != nullptr) {
|
|
path.push_back(imp_ext);
|
|
imp_ext = imp_ext->get_parent();
|
|
}
|
|
}
|
|
|
|
imp_extension_state & get_imp_extension_state(unsigned state_id) {
|
|
lean_assert(state_id < m_imp_extension_entries.size());
|
|
imp_extension_entry & e = m_imp_extension_entries[state_id];
|
|
imp_extension_state * ext_state = e.m_ext_state.get();
|
|
imp_extension * ext_of_curr_state = static_cast<imp_extension*>(&curr_state().get_extension(e.m_ext_id));
|
|
lean_assert(e.m_ext_of_ext_state);
|
|
imp_extension * ext_of_ext_state = e.m_ext_of_ext_state;
|
|
|
|
buffer<imp_extension*> curr_state_path, ext_state_path;
|
|
get_ext_path(ext_of_curr_state, curr_state_path);
|
|
get_ext_path(ext_of_ext_state, ext_state_path);
|
|
|
|
int i_curr = curr_state_path.size();
|
|
int i_ext = ext_state_path.size();
|
|
|
|
while (true) {
|
|
if (curr_state_path[--i_curr] != ext_state_path[--i_ext]) break;
|
|
if (i_curr == 0 || i_ext == 0) break;
|
|
}
|
|
|
|
while (i_ext >= 0) ext_state->undo_actions(ext_state_path[i_ext--]);
|
|
int j_curr = 0;
|
|
while (j_curr <= i_curr) ext_state->replay_actions(curr_state_path[j_curr++]);
|
|
|
|
ext_of_curr_state->inc_ref();
|
|
ext_of_ext_state->dec_ref();
|
|
e.m_ext_of_ext_state = ext_of_curr_state;
|
|
return *ext_state;
|
|
}
|
|
|
|
bool abstract_is_equal(expr const & e1, expr const & e2) {
|
|
return m_abstract_expr_manager.is_equal(e1, e2);
|
|
}
|
|
|
|
bool proof_irrel_is_equal(expr const & e1, expr const & e2) {
|
|
return m_proof_irrel_expr_manager.is_equal(e1, e2);
|
|
}
|
|
|
|
bool is_light_lt(expr const & e1, expr const & e2) {
|
|
return m_light_lt_manager.is_lt(e1, e2);
|
|
}
|
|
|
|
/** \brief Convert an external expression into a blast expression
|
|
It converts meta-variables to blast meta-variables, and ensures the expressions
|
|
are maximally shared.
|
|
\remark This procedure should only be used for debugging purposes. */
|
|
expr internalize(expr const & e) {
|
|
name_map<expr> local2href;
|
|
return to_blast_expr_fn(m_env, m_curr_state, m_uvar2uref, m_mvar2meta_mref, local2href)(e);
|
|
}
|
|
|
|
app_builder & get_app_builder() {
|
|
return m_app_builder;
|
|
}
|
|
|
|
type_context & get_type_context() {
|
|
return m_tctx;
|
|
}
|
|
|
|
expr normalize_instance(expr const & inst) {
|
|
auto it1 = m_inst_to_nf.find(inst);
|
|
expr inst_nf;
|
|
if (it1 != m_inst_to_nf.end()) {
|
|
inst_nf = it1->second;
|
|
} else {
|
|
inst_nf = m_normalizer(inst);
|
|
m_inst_to_nf.insert(mk_pair(inst, inst_nf));
|
|
}
|
|
|
|
auto it2 = m_inst_nf_to_cf.find(inst_nf);
|
|
expr inst_cf;
|
|
if (it2 != m_inst_nf_to_cf.end()) {
|
|
inst_cf = it2->second;
|
|
} else {
|
|
inst_cf = inst;
|
|
m_inst_nf_to_cf.insert(mk_pair(inst_nf, inst_cf));
|
|
}
|
|
lean_trace(name({"debug", "blast", "inst_cache"}),
|
|
tout() << "\n" << inst << "\n==>\n" << inst_nf << "\n==>\n" << inst_cf << "\n";);
|
|
return inst_cf;
|
|
}
|
|
|
|
expr normalize_instances(expr const & e) {
|
|
// TODO(Leo, Dan): This procedure is traversing the expression \c e ignoring sharing.
|
|
// That is, it traverses a DAG as a Tree. This may generate serious performance prooblems.
|
|
expr b, l, d;
|
|
switch (e.kind()) {
|
|
case expr_kind::Constant:
|
|
case expr_kind::Local:
|
|
case expr_kind::Meta:
|
|
case expr_kind::Sort:
|
|
case expr_kind::Var:
|
|
case expr_kind::Macro:
|
|
return e;
|
|
case expr_kind::Lambda:
|
|
case expr_kind::Pi:
|
|
d = normalize_instances(binding_domain(e));
|
|
l = mk_fresh_local(d, binding_info(e));
|
|
b = abstract(normalize_instances(instantiate(binding_body(e), l)), l);
|
|
return update_binding(e, d, b);
|
|
case expr_kind::Let:
|
|
return normalize_instances(instantiate(let_body(e), let_value(e)));
|
|
case expr_kind::App:
|
|
buffer<expr> args;
|
|
expr const & f = get_app_args(e, args);
|
|
unsigned prefix_sz = get_specialization_prefix_size(f, args.size());
|
|
expr new_f = e;
|
|
unsigned rest_sz = args.size() - prefix_sz;
|
|
for (unsigned i = 0; i < rest_sz; i++)
|
|
new_f = app_fn(new_f);
|
|
fun_info info = get_fun_info(new_f, rest_sz);
|
|
lean_assert(length(info.get_params_info()) == rest_sz);
|
|
unsigned i = prefix_sz;
|
|
for_each(info.get_params_info(), [&](param_info const & p_info) {
|
|
if (p_info.is_inst_implicit()) {
|
|
args[i] = normalize_instance(args[i]);
|
|
} else {
|
|
args[i] = normalize_instances(args[i]);
|
|
}
|
|
i++;
|
|
});
|
|
return mk_app(f, args);
|
|
}
|
|
lean_unreachable();
|
|
}
|
|
|
|
expr normalize(expr const & e) {
|
|
auto it = m_norm_cache.find(e);
|
|
if (it != m_norm_cache.end()) return it->second;
|
|
tmp_tctx_pool pool;
|
|
expr r = normalize_instances(defeq_simplify(pool, m_ios.get_options(), get_defeq_simp_lemmas(m_env), e));
|
|
m_norm_cache.insert(mk_pair(e, r));
|
|
return r;
|
|
}
|
|
|
|
bool is_relation_app(expr const & e, name & rop, expr & lhs, expr & rhs) {
|
|
return m_is_relation_pred(e, rop, lhs, rhs);
|
|
}
|
|
|
|
bool is_reflexive(name const & rop) const {
|
|
return static_cast<bool>(m_refl_getter(rop));
|
|
}
|
|
|
|
bool is_symmetric(name const & rop) const {
|
|
return static_cast<bool>(m_symm_getter(rop));
|
|
}
|
|
|
|
bool is_transitive(name const & rop) const {
|
|
return static_cast<bool>(m_trans_getter(rop, rop));
|
|
}
|
|
|
|
bool is_equivalence_relation_app(expr const & e, name & rop, expr & lhs, expr & rhs) {
|
|
return is_relation_app(e, rop, lhs, rhs) && is_reflexive(rop) && is_symmetric(rop) && is_transitive(rop);
|
|
}
|
|
|
|
optional<relation_info> get_relation_info(name const & rop) const {
|
|
return m_rel_getter(rop);
|
|
}
|
|
|
|
unsigned mk_uref_idx() {
|
|
unsigned r = m_next_uref_idx;
|
|
m_next_uref_idx++;
|
|
return r;
|
|
}
|
|
|
|
unsigned mk_mref_idx() {
|
|
unsigned r = m_next_mref_idx;
|
|
m_next_mref_idx++;
|
|
return r;
|
|
}
|
|
|
|
unsigned mk_href_idx() {
|
|
unsigned r = m_next_href_idx;
|
|
m_next_href_idx++;
|
|
return r;
|
|
}
|
|
|
|
unsigned mk_choice_point_idx() {
|
|
unsigned r = m_next_choice_idx;
|
|
m_next_choice_idx++;
|
|
return r;
|
|
}
|
|
|
|
unsigned mk_split_idx() {
|
|
unsigned r = m_next_split_idx;
|
|
m_next_split_idx++;
|
|
return r;
|
|
}
|
|
};
|
|
|
|
LEAN_THREAD_PTR(blastenv, g_blastenv);
|
|
struct scope_blastenv {
|
|
blastenv * m_prev_blastenv;
|
|
public:
|
|
scope_blastenv(blastenv & c):m_prev_blastenv(g_blastenv) { g_blastenv = &c; }
|
|
~scope_blastenv() { g_blastenv = m_prev_blastenv; }
|
|
};
|
|
|
|
level mk_uref(unsigned idx) {
|
|
return lean::mk_meta_univ(name(*g_ref_prefix, idx));
|
|
}
|
|
|
|
bool is_uref(level const & l) {
|
|
return is_meta(l) && meta_id(l).is_numeral();
|
|
}
|
|
|
|
unsigned uref_index(level const & l) {
|
|
lean_assert(is_uref(l));
|
|
return meta_id(l).get_numeral();
|
|
}
|
|
|
|
expr mk_href(unsigned idx) {
|
|
return lean::mk_local(name(*g_ref_prefix, idx), *g_dummy_type);
|
|
}
|
|
|
|
bool is_href(expr const & e) {
|
|
return lean::is_local(e) && mlocal_type(e) == *g_dummy_type;
|
|
}
|
|
|
|
expr mk_mref(unsigned idx) {
|
|
return mk_metavar(name(*g_ref_prefix, idx), *g_dummy_type);
|
|
}
|
|
|
|
bool is_mref(expr const & e) {
|
|
return is_metavar(e) && mlocal_type(e) == *g_dummy_type;
|
|
}
|
|
|
|
unsigned mref_index(expr const & e) {
|
|
lean_assert(is_mref(e));
|
|
return mlocal_name(e).get_numeral();
|
|
}
|
|
|
|
unsigned href_index(expr const & e) {
|
|
lean_assert(is_href(e));
|
|
return mlocal_name(e).get_numeral();
|
|
}
|
|
|
|
bool has_href(expr const & e) {
|
|
return lean::has_local(e);
|
|
}
|
|
|
|
bool has_mref(expr const & e) {
|
|
return lean::has_expr_metavar(e);
|
|
}
|
|
|
|
unsigned mk_uref_idx() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_uref_idx();
|
|
}
|
|
|
|
unsigned mk_mref_idx() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_mref_idx();
|
|
}
|
|
|
|
unsigned mk_href_idx() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_href_idx();
|
|
}
|
|
|
|
environment const & env() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_env();
|
|
}
|
|
|
|
io_state const & ios() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_ios();
|
|
}
|
|
|
|
type_context & get_type_context() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_type_context();
|
|
}
|
|
|
|
app_builder & get_app_builder() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_app_builder();
|
|
}
|
|
|
|
state & curr_state() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_curr_state();
|
|
}
|
|
|
|
bool is_reducible(name const & n) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->is_reducible(n);
|
|
}
|
|
|
|
projection_info const * get_projection_info(name const & n) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_projection_info(n);
|
|
}
|
|
|
|
bool is_relation_app(expr const & e, name & rop, expr & lhs, expr & rhs) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->is_relation_app(e, rop, lhs, rhs);
|
|
}
|
|
|
|
bool is_relation_app(expr const & e) {
|
|
name rop; expr lhs, rhs;
|
|
return is_relation_app(e, rop, lhs, rhs);
|
|
}
|
|
|
|
bool is_reflexive(name const & rop) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->is_reflexive(rop);
|
|
}
|
|
|
|
bool is_symmetric(name const & rop) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->is_symmetric(rop);
|
|
}
|
|
|
|
bool is_transitive(name const & rop) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->is_transitive(rop);
|
|
}
|
|
|
|
bool is_equivalence_relation_app(expr const & e, name & rop, expr & lhs, expr & rhs) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->is_equivalence_relation_app(e, rop, lhs, rhs);
|
|
}
|
|
|
|
optional<relation_info> get_relation_info(name const & rop) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_relation_info(rop);
|
|
}
|
|
|
|
expr whnf(expr const & e) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->whnf(e);
|
|
}
|
|
|
|
expr relaxed_whnf(expr const & e) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->relaxed_whnf(e);
|
|
}
|
|
|
|
expr infer_type(expr const & e) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->infer_type(e);
|
|
}
|
|
|
|
expr normalize(expr const & e) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->normalize(e);
|
|
}
|
|
|
|
bool is_prop(expr const & e) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->is_prop(e);
|
|
}
|
|
|
|
bool is_def_eq(expr const & e1, expr const & e2) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->is_def_eq(e1, e2);
|
|
}
|
|
|
|
optional<expr> mk_class_instance(expr const & e) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_class_instance(e);
|
|
}
|
|
|
|
optional<expr> mk_subsingleton_instance(expr const & type) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_subsingleton_instance(type);
|
|
}
|
|
|
|
unfold_macro_pred get_unfold_macro_pred() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_unfold_macro_pred();
|
|
}
|
|
|
|
expr mk_fresh_local(expr const & type, binder_info const & bi) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_fresh_local(type, bi);
|
|
}
|
|
|
|
bool is_fresh_local(expr const & e) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->is_fresh_local(e);
|
|
}
|
|
|
|
optional<congr_lemma> mk_congr_lemma_for_simp(expr const & fn, unsigned num_args) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_congr_lemma_for_simp(fn, num_args);
|
|
}
|
|
|
|
optional<congr_lemma> mk_congr_lemma_for_simp(expr const & fn) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_congr_lemma_for_simp(fn);
|
|
}
|
|
|
|
optional<congr_lemma> mk_specialized_congr_lemma_for_simp(expr const & a) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_specialized_congr_lemma_for_simp(a);
|
|
}
|
|
|
|
optional<congr_lemma> mk_congr_lemma(expr const & fn, unsigned num_args) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_congr_lemma(fn, num_args);
|
|
}
|
|
|
|
optional<congr_lemma> mk_congr_lemma(expr const & fn) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_congr_lemma(fn);
|
|
}
|
|
|
|
optional<congr_lemma> mk_hcongr_lemma(expr const & fn, unsigned num_args) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_hcongr_lemma(fn, num_args);
|
|
}
|
|
|
|
optional<congr_lemma> mk_specialized_congr_lemma(expr const & a) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_specialized_congr_lemma(a);
|
|
}
|
|
|
|
optional<congr_lemma> mk_rel_iff_congr(expr const & fn) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_rel_iff_congr(fn);
|
|
}
|
|
|
|
optional<congr_lemma> mk_rel_eq_congr(expr const & fn) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_rel_eq_congr(fn);
|
|
}
|
|
|
|
fun_info get_fun_info(expr const & fn) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_fun_info(fn);
|
|
}
|
|
|
|
fun_info get_fun_info(expr const & fn, unsigned nargs) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_fun_info(fn, nargs);
|
|
}
|
|
|
|
fun_info get_specialized_fun_info(expr const & a) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_specialized_fun_info(a);
|
|
}
|
|
|
|
unsigned get_specialization_prefix_size(expr const & fn, unsigned nargs) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_specialization_prefix_size(fn, nargs);
|
|
}
|
|
|
|
unsigned abstract_hash(expr const & e) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->abstract_hash(e);
|
|
}
|
|
|
|
unsigned proof_irrel_hash(expr const & e) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->proof_irrel_hash(e);
|
|
}
|
|
|
|
unsigned register_imp_extension(std::function<imp_extension_state*()> & ext_state_maker) {
|
|
return get_imp_extension_manager().register_imp_extension(ext_state_maker);
|
|
}
|
|
|
|
imp_extension_state & get_imp_extension_state(unsigned state_id) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->get_imp_extension_state(state_id);
|
|
}
|
|
|
|
bool abstract_is_equal(expr const & e1, expr const & e2) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->abstract_is_equal(e1, e2);
|
|
}
|
|
|
|
bool proof_irrel_is_equal(expr const & e1, expr const & e2) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->proof_irrel_is_equal(e1, e2);
|
|
}
|
|
|
|
bool is_light_lt(expr const & e1, expr const & e2) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->is_light_lt(e1, e2);
|
|
}
|
|
|
|
bool classical() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->classical();
|
|
}
|
|
|
|
unsigned mk_choice_point_idx() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_choice_point_idx();
|
|
}
|
|
|
|
unsigned mk_split_idx() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_split_idx();
|
|
}
|
|
|
|
void display_curr_state() {
|
|
curr_state().display(env(), ios());
|
|
display("\n");
|
|
}
|
|
|
|
void display_expr(expr const & e) {
|
|
ios().get_diagnostic_channel() << e << "\n";
|
|
}
|
|
|
|
void display(char const * msg) {
|
|
ios().get_diagnostic_channel() << msg;
|
|
}
|
|
|
|
void display(sstream const & msg) {
|
|
ios().get_diagnostic_channel() << msg.str();
|
|
}
|
|
|
|
void display_at_buffer(sstream const & msg) {
|
|
lean_assert(g_blastenv);
|
|
g_blastenv->get_buffer_ios().get_diagnostic_channel() << msg.str();
|
|
}
|
|
|
|
void display_curr_state_at_buffer(bool include_inactive) {
|
|
lean_assert(g_blastenv);
|
|
curr_state().display(g_blastenv->get_env(), g_blastenv->get_buffer_ios(), include_inactive);
|
|
}
|
|
|
|
scope_assignment::scope_assignment():m_keep(false) {
|
|
lean_assert(g_blastenv);
|
|
g_blastenv->m_tctx.push();
|
|
}
|
|
|
|
scope_assignment::~scope_assignment() {
|
|
if (m_keep)
|
|
g_blastenv->m_tctx.commit();
|
|
else
|
|
g_blastenv->m_tctx.pop();
|
|
}
|
|
|
|
void scope_assignment::commit() {
|
|
m_keep = true;
|
|
}
|
|
|
|
scope_unfold_macro_pred::scope_unfold_macro_pred(unfold_macro_pred const & pred):
|
|
m_old_pred(g_blastenv->m_unfold_macro_pred) {
|
|
g_blastenv->m_unfold_macro_pred = pred;
|
|
g_blastenv->m_norm_cache.clear(); // TODO(Leo): check if we need better solution
|
|
}
|
|
|
|
scope_unfold_macro_pred::~scope_unfold_macro_pred() {
|
|
g_blastenv->m_unfold_macro_pred = m_old_pred;
|
|
g_blastenv->m_norm_cache.clear();
|
|
}
|
|
|
|
struct scope_debug::imp {
|
|
scoped_expr_caching m_scope1;
|
|
blastenv m_benv;
|
|
scope_blastenv m_scope2;
|
|
scope_congruence_closure m_scope3;
|
|
scope_config m_scope4;
|
|
scope_simp m_scope5;
|
|
imp(environment const & env, io_state const & ios):
|
|
m_scope1(true),
|
|
m_benv(env, ios, list<name>(), list<name>()),
|
|
m_scope2(m_benv),
|
|
m_scope4(ios.get_options()) {
|
|
expr aux_mvar = mk_metavar("dummy_mvar", mk_true());
|
|
goal aux_g(aux_mvar, mlocal_type(aux_mvar));
|
|
m_benv.init_state(aux_g);
|
|
}
|
|
};
|
|
|
|
scope_debug::scope_debug(environment const & env, io_state const & ios):
|
|
m_imp(new imp(env, ios)) {
|
|
}
|
|
|
|
scope_debug::~scope_debug() {}
|
|
|
|
/** \brief We need to redefine infer_local and infer_metavar, because the types of hypotheses
|
|
and blast meta-variables are stored in the blast state */
|
|
class tmp_tctx : public tmp_type_context {
|
|
public:
|
|
tmp_tctx(environment const & env, options const & o):
|
|
tmp_type_context(env, o) {}
|
|
|
|
virtual bool should_unfold_macro(expr const & e) const override {
|
|
return get_unfold_macro_pred()(e);
|
|
}
|
|
|
|
/** \brief Return the type of a local constant (local or not).
|
|
\remark This method allows the customer to store the type of local constants
|
|
in a different place. */
|
|
virtual expr infer_local(expr const & e) const {
|
|
state const & s = curr_state();
|
|
if (is_href(e)) {
|
|
hypothesis const & h = s.get_hypothesis_decl(e);
|
|
return h.get_type();
|
|
} else {
|
|
return mlocal_type(e);
|
|
}
|
|
}
|
|
|
|
virtual expr infer_metavar(expr const & m) const {
|
|
if (is_mref(m)) {
|
|
state const & s = curr_state();
|
|
metavar_decl const * d = s.get_metavar_decl(m);
|
|
lean_assert(d);
|
|
return d->get_type();
|
|
} else {
|
|
// The type of external meta-variables is encoded in the usual way.
|
|
// In temporary type_context objects, we may have temporary meta-variables
|
|
// created by external modules (e.g., simplifier and app_builder).
|
|
return mlocal_type(m);
|
|
}
|
|
}
|
|
};
|
|
|
|
tmp_type_context * blastenv::mk_tmp_type_context() {
|
|
tmp_type_context * r;
|
|
if (m_tmp_ctx_pool.empty()) {
|
|
r = new tmp_tctx(m_env, m_ios.get_options());
|
|
// Design decision: in the blast tactic, we only consider the instances that were
|
|
// available in initial goal provided to the blast tactic.
|
|
// So, we only need to setup the local instances when we create a new (temporary) type context.
|
|
// This is important since whenever we set the local instances the cache in at type context is
|
|
// invalidated.
|
|
r->set_local_instances(m_initial_context);
|
|
} else {
|
|
r = m_tmp_ctx_pool.back();
|
|
m_tmp_ctx_pool.pop_back();
|
|
}
|
|
return r;
|
|
}
|
|
|
|
tmp_type_context * tmp_tctx_pool::mk_tmp_type_context() {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->mk_tmp_type_context();
|
|
}
|
|
|
|
void tmp_tctx_pool::recycle_tmp_type_context(tmp_type_context * tmp_tctx) {
|
|
lean_assert(g_blastenv);
|
|
g_blastenv->recycle_tmp_type_context(tmp_tctx);
|
|
}
|
|
|
|
blast_tmp_type_context::blast_tmp_type_context(unsigned num_umeta, unsigned num_emeta) {
|
|
lean_assert(g_blastenv);
|
|
m_ctx = g_blastenv->mk_tmp_type_context();
|
|
m_ctx->clear();
|
|
m_ctx->set_next_uvar_idx(num_umeta);
|
|
m_ctx->set_next_mvar_idx(num_emeta);
|
|
}
|
|
|
|
blast_tmp_type_context::blast_tmp_type_context() {
|
|
lean_assert(g_blastenv);
|
|
m_ctx = g_blastenv->mk_tmp_type_context();
|
|
}
|
|
|
|
blast_tmp_type_context::~blast_tmp_type_context() {
|
|
g_blastenv->recycle_tmp_type_context(m_ctx);
|
|
}
|
|
|
|
expr internalize(expr const & e) {
|
|
lean_assert(g_blastenv);
|
|
return g_blastenv->internalize(e);
|
|
}
|
|
}
|
|
pair<expr, constraint_seq> blast_goal(environment const & env, io_state const & ios, list<name> const & ls, list<name> const & ds,
|
|
goal const & g) {
|
|
scoped_expr_caching scope1(true);
|
|
blast::blastenv b(env, ios, ls, ds);
|
|
blast::scope_blastenv scope2(b);
|
|
blast::scope_congruence_closure scope3;
|
|
blast::scope_config scope4(ios.get_options());
|
|
scope_trace_env scope5(env, ios, blast::get_type_context());
|
|
blast::scope_simp scope6;
|
|
return b(g);
|
|
}
|
|
void initialize_blast() {
|
|
register_trace_class("blast");
|
|
register_trace_class(name{"blast_detailed"});
|
|
register_trace_class(name{"blast", "event"});
|
|
register_trace_class(name{"blast", "state"});
|
|
register_trace_class(name{"blast", "action"});
|
|
register_trace_class(name{"blast", "search"});
|
|
register_trace_class(name{"blast", "deadend"});
|
|
register_trace_class(name{"debug", "blast"});
|
|
register_trace_class(name{"debug", "blast", "inst_cache"});
|
|
|
|
register_trace_class_alias("app_builder", name({"blast", "event"}));
|
|
register_trace_class_alias(name({"simplifier", "failure"}), name({"blast", "event"}));
|
|
register_trace_class_alias("fun_info", name({"blast", "event"}));
|
|
|
|
register_trace_class_alias(name({"cc", "propagation"}), "blast");
|
|
|
|
register_trace_class_alias("blast", "blast_detailed");
|
|
register_trace_class_alias("app_builder", "blast_detailed");
|
|
register_trace_class_alias("fun_info", "blast_detailed");
|
|
register_trace_class_alias(name({"simplifier", "failure"}), "blast_detailed");
|
|
register_trace_class_alias(name({"cc", "merge"}), "blast_detailed");
|
|
|
|
blast::g_ref_prefix = new name(name::mk_internal_unique_name());
|
|
blast::g_imp_extension_manager = new blast::imp_extension_manager();
|
|
blast::g_dummy_type = new expr(mk_constant(*blast::g_ref_prefix));
|
|
}
|
|
void finalize_blast() {
|
|
delete blast::g_imp_extension_manager;
|
|
delete blast::g_ref_prefix;
|
|
delete blast::g_dummy_type;
|
|
}
|
|
}
|