fix(library/elaborator): bug at method process_metavar_inst, add new test that exposed the bug
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
0d815b8594
commit
cbd1f98365
8 changed files with 344 additions and 106 deletions
|
@ -7,6 +7,8 @@ Author: Leonardo de Moura
|
|||
#include <utility>
|
||||
#include "util/exception.h"
|
||||
#include "kernel/context.h"
|
||||
#include "kernel/metavar.h"
|
||||
#include "kernel/free_vars.h"
|
||||
|
||||
namespace lean {
|
||||
context::context(std::initializer_list<std::pair<char const *, expr const &>> const & l) {
|
||||
|
@ -47,36 +49,56 @@ optional<context_entry> context::find(unsigned i) const {
|
|||
return optional<context_entry>();
|
||||
}
|
||||
|
||||
static list<context_entry> remove_core(list<context_entry> const & l, unsigned s, unsigned n) {
|
||||
list<context_entry> truncate_core(list<context_entry> const & l, unsigned s) {
|
||||
if (s == 0) {
|
||||
return list<context_entry>();
|
||||
} else {
|
||||
return cons(head(l), truncate_core(tail(l), s-1));
|
||||
}
|
||||
}
|
||||
|
||||
context context::truncate(unsigned s) const {
|
||||
return context(truncate_core(m_list, s));
|
||||
}
|
||||
|
||||
struct remove_no_applicable {};
|
||||
static list<context_entry> remove_core(list<context_entry> const & l, unsigned s, unsigned n, metavar_env const & menv) {
|
||||
if (l) {
|
||||
if (s == 0) {
|
||||
if (n > 0) {
|
||||
return remove_core(tail(l), 0, n-1);
|
||||
return remove_core(tail(l), 0, n-1, menv);
|
||||
} else {
|
||||
return l;
|
||||
}
|
||||
} else {
|
||||
return cons(head(l), remove_core(tail(l), s-1, n));
|
||||
if (has_free_var(head(l), s-1, s+n-1, menv))
|
||||
throw remove_no_applicable();
|
||||
return cons(lower_free_vars(head(l), s+n-1, n, menv), remove_core(tail(l), s-1, n, menv));
|
||||
}
|
||||
} else {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
|
||||
context context::remove(unsigned s, unsigned n) const {
|
||||
return context(remove_core(m_list, s, n));
|
||||
optional<context> context::remove(unsigned s, unsigned n, metavar_env const & menv) const {
|
||||
try {
|
||||
return some(context(remove_core(m_list, s, n, menv)));
|
||||
} catch (remove_no_applicable&) {
|
||||
return optional<context>();
|
||||
}
|
||||
}
|
||||
|
||||
static list<context_entry> insert_at_core(list<context_entry> const & l, unsigned i, name const & n, expr const & d) {
|
||||
static list<context_entry> insert_at_core(list<context_entry> const & l, unsigned i, name const & n, expr const & d,
|
||||
metavar_env const & menv) {
|
||||
if (i == 0) {
|
||||
return cons(context_entry(n, d), l);
|
||||
} else {
|
||||
lean_assert(l);
|
||||
return cons(head(l), insert_at_core(tail(l), i - 1, n, d));
|
||||
return cons(lift_free_vars(head(l), i-1, 1, menv), insert_at_core(tail(l), i-1, n, d, menv));
|
||||
}
|
||||
}
|
||||
|
||||
context context::insert_at(unsigned i, name const & n, expr const & d) const {
|
||||
return context(insert_at_core(m_list, i, n, d));
|
||||
context context::insert_at(unsigned i, name const & n, expr const & d, metavar_env const & menv) const {
|
||||
return context(insert_at_core(m_list, i, n, d, menv));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ public:
|
|||
friend bool operator!=(context_entry const & e1, context_entry const & e2) { return !(e1 == e2); }
|
||||
};
|
||||
|
||||
class metavar_env;
|
||||
|
||||
/**
|
||||
\brief A context is essentially a mapping from free-variables to types (and definition/body).
|
||||
*/
|
||||
|
@ -57,18 +59,37 @@ public:
|
|||
iterator begin() const { return m_list.begin(); }
|
||||
iterator end() const { return m_list.end(); }
|
||||
friend bool is_eqp(context const & c1, context const & c2) { return is_eqp(c1.m_list, c2.m_list); }
|
||||
/**
|
||||
\brief Return a new context where entries at positions >= s are removed.
|
||||
*/
|
||||
context truncate(unsigned s) const;
|
||||
/**
|
||||
\brief Return a new context where the entries at positions [s, s+n) were removed.
|
||||
|
||||
The free variables in entries [0, s) are lowered.
|
||||
That is, if this context is of the form
|
||||
[ce_m, ..., ce_{s+n}, ce_{s+n-1}, ..., ce_s, ce_{s-1}, ..., ce_0]
|
||||
Then, the resultant context is of the form
|
||||
[ce_m, ..., ce_{s+n}, lower(ce_{s-1}, n, n), ..., lower(ce_0, s+n-1, n)]
|
||||
|
||||
\pre size() >= s + n
|
||||
|
||||
If for some i in [0, s), has_free_var(ce_i, s - 1 - i, s + n - 1 - i), then return none.
|
||||
That is, the lower operations must be valid.
|
||||
*/
|
||||
context remove(unsigned s, unsigned n) const;
|
||||
optional<context> remove(unsigned s, unsigned n, metavar_env const & menv) const;
|
||||
/**
|
||||
\brief Return a new context where then entry n : d is inserted at position i.
|
||||
|
||||
The entries from [0, i) are lifted
|
||||
That is, if this context is of the form
|
||||
[ce_m, ..., ce_i, ce_{i-1}, ..., ce_0]
|
||||
Then, the resultant context is of the form
|
||||
[ce_m, ..., ce_i, n := v, lift(ce_{i-1}, 0, 1), ..., lift(ce_0, i-1, 1)]
|
||||
|
||||
\pre size() >= i
|
||||
*/
|
||||
context insert_at(unsigned i, name const & n, expr const & d) const;
|
||||
context insert_at(unsigned i, name const & n, expr const & d, metavar_env const & menv) const;
|
||||
friend bool operator==(context const & ctx1, context const & ctx2) { return ctx1.m_list == ctx2.m_list; }
|
||||
friend bool operator!=(context const & ctx1, context const & ctx2) { return !(ctx1 == ctx2); }
|
||||
};
|
||||
|
|
|
@ -333,6 +333,12 @@ bool has_free_var(expr const & e, unsigned i, optional<metavar_env> const & menv
|
|||
bool has_free_var(expr const & e, unsigned i, metavar_env const & menv) { return has_free_var(e, i, i+1, menv); }
|
||||
bool has_free_var(expr const & e, unsigned i) { return has_free_var(e, i, i+1); }
|
||||
|
||||
bool has_free_var(context_entry const & e, unsigned low, unsigned high, metavar_env const & menv) {
|
||||
auto d = e.get_domain();
|
||||
auto b = e.get_body();
|
||||
return (d && has_free_var(*d, low, high, menv)) || (b && has_free_var(*b, low, high, menv));
|
||||
}
|
||||
|
||||
expr lower_free_vars(expr const & e, unsigned s, unsigned d, optional<metavar_env> const & DEBUG_CODE(menv)) {
|
||||
lean_assert(s >= d);
|
||||
lean_assert(!has_free_var(e, s-d, s, menv));
|
||||
|
@ -351,6 +357,17 @@ expr lower_free_vars(expr const & e, unsigned d, optional<metavar_env> const & m
|
|||
expr lower_free_vars(expr const & e, unsigned d, metavar_env const & menv) { return lower_free_vars(e, d, d, menv); }
|
||||
expr lower_free_vars(expr const & e, unsigned d) { return lower_free_vars(e, d, d); }
|
||||
|
||||
context_entry lower_free_vars(context_entry const & e, unsigned s, unsigned d, metavar_env const & menv) {
|
||||
auto domain = e.get_domain();
|
||||
auto body = e.get_body();
|
||||
if (domain && body)
|
||||
return context_entry(e.get_name(), lower_free_vars(*domain, s, d, menv), lower_free_vars(*body, s, d, menv));
|
||||
else if (domain)
|
||||
return context_entry(e.get_name(), lower_free_vars(*domain, s, d, menv));
|
||||
else
|
||||
return context_entry(e.get_name(), none_expr(), lower_free_vars(*body, s, d, menv));
|
||||
}
|
||||
|
||||
expr lift_free_vars(expr const & e, unsigned s, unsigned d, optional<metavar_env> const & menv) {
|
||||
if (d == 0)
|
||||
return e;
|
||||
|
@ -369,4 +386,15 @@ expr lift_free_vars(expr const & e, unsigned s, unsigned d) { return lift_free_v
|
|||
expr lift_free_vars(expr const & e, unsigned d, optional<metavar_env> const & menv) { return lift_free_vars(e, 0, d, menv); }
|
||||
expr lift_free_vars(expr const & e, unsigned d) { return lift_free_vars(e, 0, d); }
|
||||
expr lift_free_vars(expr const & e, unsigned d, metavar_env const & menv) { return lift_free_vars(e, 0, d, menv); }
|
||||
|
||||
context_entry lift_free_vars(context_entry const & e, unsigned s, unsigned d, metavar_env const & menv) {
|
||||
auto domain = e.get_domain();
|
||||
auto body = e.get_body();
|
||||
if (domain && body)
|
||||
return context_entry(e.get_name(), lift_free_vars(*domain, s, d, menv), lift_free_vars(*body, s, d, menv));
|
||||
else if (domain)
|
||||
return context_entry(e.get_name(), lift_free_vars(*domain, s, d, menv));
|
||||
else
|
||||
return context_entry(e.get_name(), none_expr(), lift_free_vars(*body, s, d, menv));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,12 +54,14 @@ bool has_free_var(expr const & e, unsigned i, optional<metavar_env> const & menv
|
|||
bool has_free_var(expr const & e, unsigned i, metavar_env const & menv);
|
||||
bool has_free_var(expr const & e, unsigned i);
|
||||
|
||||
bool has_free_var(context_entry const & e, unsigned low, unsigned high, metavar_env const & menv);
|
||||
|
||||
/**
|
||||
\brief Lower the free variables >= s in \c e by \c d. That is, a free variable <tt>(var i)</tt> s.t.
|
||||
<tt>i >= s</tt> is mapped into <tt>(var i-d)</tt>.
|
||||
|
||||
\pre s >= d
|
||||
\pre !has_free_var(e, s-d, d, menv)
|
||||
\pre !has_free_var(e, s-d, s, menv)
|
||||
|
||||
\remark The parameter menv is only used for debugging purposes
|
||||
*/
|
||||
|
@ -70,6 +72,8 @@ expr lower_free_vars(expr const & e, unsigned d, optional<metavar_env> const & m
|
|||
expr lower_free_vars(expr const & e, unsigned d, metavar_env const & menv);
|
||||
expr lower_free_vars(expr const & e, unsigned d);
|
||||
|
||||
context_entry lower_free_vars(context_entry const & e, unsigned s, unsigned d, metavar_env const & menv);
|
||||
|
||||
/**
|
||||
\brief Lift free variables >= s in \c e by d.
|
||||
|
||||
|
@ -82,4 +86,6 @@ expr lift_free_vars(expr const & e, unsigned s, unsigned d, metavar_env const &
|
|||
expr lift_free_vars(expr const & e, unsigned d, optional<metavar_env> const & menv);
|
||||
expr lift_free_vars(expr const & e, unsigned d, metavar_env const & menv);
|
||||
expr lift_free_vars(expr const & e, unsigned d);
|
||||
|
||||
context_entry lift_free_vars(context_entry const & e, unsigned s, unsigned d, metavar_env const & menv);
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@ bool metavar_env_cell::assign(name const & m, expr const & t, justification cons
|
|||
if (e_ctx_size < extra) {
|
||||
failed = true;
|
||||
} else {
|
||||
it2->m_context = it2->m_context.remove(e_ctx_size - extra, extra);
|
||||
it2->m_context = it2->m_context.truncate(e_ctx_size - extra);
|
||||
lean_assert_le(free_var_range(e, metavar_env(this)), ctx_size + offset);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -462,15 +462,29 @@ class elaborator::imp {
|
|||
}
|
||||
local_entry const & me = head(metavar_lctx(a));
|
||||
if (me.is_lift()) {
|
||||
if (!has_free_var(b, me.s(), me.s() + me.n(), m_state.m_menv)) {
|
||||
// Case 3
|
||||
// Case 3
|
||||
// a is of the form ?m[lift:s:n]
|
||||
unsigned s = me.s();
|
||||
unsigned n = me.n();
|
||||
// Let ctx be of the form
|
||||
// [ce_{m-1}, ..., ce_{s+n}, ce_{s+n-1}, ..., ce_s, ce_{s-1}, ..., ce_0]
|
||||
// Then, we reduce
|
||||
// [ce_{m-1}, ..., ce_{s+n}, ce_{s+n-1}, ..., ce_s, ce_{s-1}, ..., ce_0] |- ?m[lift:s:n] == b
|
||||
// to
|
||||
// [ce_{m-1}, ..., ce_{s+n}, lower(ce_{s-1}, n, n), ..., lower(ce_0, s + n - 1, n)] |- ?m == lower(b, s + n, n)
|
||||
//
|
||||
// Remark: we have to check if the lower operations are applicable using the operation has_free_var.
|
||||
//
|
||||
if (!has_free_var(b, s, s + n, m_state.m_menv)) {
|
||||
optional<context> new_ctx = ctx.remove(s, n, m_state.m_menv); // method remove will lower the entries ce_0, ..., ce_{s-1}
|
||||
if (!new_ctx)
|
||||
return Continue; // rule is not applicable because we cannot lower the entries.
|
||||
justification new_jst(new normalize_justification(c));
|
||||
expr new_a = pop_meta_context(a);
|
||||
expr new_b = lower_free_vars(b, me.s() + me.n(), me.n(), m_state.m_menv);
|
||||
context new_ctx = ctx.remove(me.s(), me.n());
|
||||
expr new_a = pop_meta_context(a);
|
||||
expr new_b = lower_free_vars(b, s + n, n, m_state.m_menv);
|
||||
if (!is_lhs)
|
||||
swap(new_a, new_b);
|
||||
push_new_constraint(is_eq(c), new_ctx, new_a, new_b, new_jst);
|
||||
push_new_constraint(is_eq(c), *new_ctx, new_a, new_b, new_jst);
|
||||
return Processed;
|
||||
} else if (!has_metavar(b)) {
|
||||
// Failure, there is no way to unify
|
||||
|
@ -487,7 +501,7 @@ class elaborator::imp {
|
|||
justification new_jst(new normalize_justification(c));
|
||||
expr new_a = pop_meta_context(a);
|
||||
expr new_b = lift_free_vars(b, me.s(), 1, m_state.m_menv);
|
||||
context new_ctx = ctx.insert_at(me.s(), g_x_name, Type()); // insert a dummy at position s
|
||||
context new_ctx = ctx.insert_at(me.s(), g_x_name, Type(), m_state.m_menv); // insert a dummy at position s
|
||||
if (!is_lhs)
|
||||
swap(new_a, new_b);
|
||||
push_new_constraint(is_eq(c), new_ctx, new_a, new_b, new_jst);
|
||||
|
@ -901,6 +915,173 @@ class elaborator::imp {
|
|||
return is_metavar(a) && has_local_context(a) && head(metavar_lctx(a)).is_lift();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Collect possible solutions for ?m given a constraint of the form
|
||||
ctx |- ?m[lctx] == b
|
||||
where b is a Constant, Type, Value or Variable.
|
||||
|
||||
We only need the local context \c lctx and \c b for computing the set of possible solutions.
|
||||
The result is stored in \c solutions.
|
||||
|
||||
We may have more than one solution. Here is an example:
|
||||
|
||||
?m[inst:3:b, lift:1:1, inst:2:b] == b
|
||||
|
||||
The possible solutions is the set of solutions for
|
||||
1- ?m[lift:1:1, inst:2:b] == #3
|
||||
2- ?m[lift:1:1, inst:2:b] == b
|
||||
|
||||
The solutions for constraints 1 and 2 are the solutions for
|
||||
1.1- ?m[inst:2:b] == #2
|
||||
2.1- ?m[inst:2:b] == b
|
||||
|
||||
And 1.1 has two possible solutions
|
||||
1.1.1 ?m == #3
|
||||
1.1.2 ?m == b
|
||||
|
||||
And 2.1 has also two possible solutions
|
||||
2.1.1 ?m == #2
|
||||
2.1.2 ?m == b
|
||||
|
||||
Thus, the resulting set of solutions is {#3, b, #2}
|
||||
*/
|
||||
void collect_metavar_solutions(local_context const & lctx, expr const & b, buffer<expr> & solutions) {
|
||||
lean_assert(is_constant(b) || is_type(b) || is_value(b) || is_var(b));
|
||||
if (lctx) {
|
||||
local_entry const & e = head(lctx);
|
||||
if (e.is_inst()) {
|
||||
if (e.v() == b || has_metavar(e.v())) {
|
||||
// There is an extra possible solution #{e.s()}
|
||||
// If e.v() has metavariables, then it may become equal to b.
|
||||
collect_metavar_solutions(tail(lctx), mk_var(e.s()), solutions);
|
||||
}
|
||||
if (is_var(b) && var_idx(b) >= e.s()) {
|
||||
collect_metavar_solutions(tail(lctx), mk_var(var_idx(b) + 1), solutions);
|
||||
} else {
|
||||
collect_metavar_solutions(tail(lctx), b, solutions);
|
||||
}
|
||||
} else {
|
||||
lean_assert(e.is_lift());
|
||||
if (is_var(b) && var_idx(b) >= e.s() + e.n()) {
|
||||
collect_metavar_solutions(tail(lctx), mk_var(var_idx(b) - e.n()), solutions);
|
||||
} else {
|
||||
collect_metavar_solutions(tail(lctx), b, solutions);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lean_assert(length(lctx) == 0);
|
||||
if (std::find(solutions.begin(), solutions.end(), b) == solutions.end())
|
||||
solutions.push_back(b); // insert new solution
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if the local context contains a metavariable.
|
||||
*/
|
||||
bool local_context_has_metavar(local_context const & lctx) {
|
||||
for (auto const & e : lctx) {
|
||||
if (e.is_inst() && has_metavar(e.v()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Solve a constraint of the form
|
||||
ctx |- a == b
|
||||
where
|
||||
a is of the form ?m[...] i.e., metavariable with a non-empty local context.
|
||||
b is an abstraction
|
||||
|
||||
We solve the constraint by assigning a to an abstraction with fresh metavariables.
|
||||
*/
|
||||
void imitate_abstraction(expr const & a, expr const & b, unification_constraint const & c) {
|
||||
lean_assert(is_metavar(a));
|
||||
lean_assert(is_abstraction(b));
|
||||
lean_assert(!is_assigned(a));
|
||||
lean_assert(has_local_context(a));
|
||||
// imitate
|
||||
push_active(c);
|
||||
// a <- (fun x : ?h1, ?h2) or (Pi x : ?h1, ?h2)
|
||||
// ?h1 is in the same context where 'a' was defined
|
||||
// ?h2 is in the context of 'a' + domain of b
|
||||
expr m = mk_metavar(metavar_name(a));
|
||||
context ctx_m = m_state.m_menv->get_context(m);
|
||||
expr h_1 = m_state.m_menv->mk_metavar(ctx_m);
|
||||
expr h_2 = m_state.m_menv->mk_metavar(extend(ctx_m, abst_name(b), abst_domain(b)));
|
||||
expr imitation = update_abstraction(b, h_1, h_2);
|
||||
justification new_jst(new imitation_justification(c));
|
||||
push_new_constraint(true, ctx_m, m, imitation, new_jst);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Similar to imitate_abstraction, but b is an application, where the function
|
||||
is a Variable, Constant or Value.
|
||||
*/
|
||||
void imitate_application(expr const & a, expr const & b, unification_constraint const & c) {
|
||||
lean_assert(is_metavar(a));
|
||||
lean_assert(is_app(b) && (is_var(arg(b, 0)) || is_constant(arg(b, 0)) || is_value(arg(b, 0))));
|
||||
lean_assert(!is_assigned(a));
|
||||
lean_assert(has_local_context(a));
|
||||
// Create a fresh meta variable for each argument of b.
|
||||
// The new metavariables have the same context of a.
|
||||
expr m = mk_metavar(metavar_name(a));
|
||||
context ctx_m = m_state.m_menv->get_context(m);
|
||||
expr f_b = arg(b, 0);
|
||||
buffer<expr> new_args;
|
||||
new_args.push_back(expr()); // save space.
|
||||
unsigned num = num_args(b);
|
||||
for (unsigned i = 1; i < num; i++)
|
||||
new_args.push_back(m_state.m_menv->mk_metavar(ctx_m));
|
||||
// We may have many different imitations.
|
||||
local_context lctx = metavar_lctx(a);
|
||||
buffer<expr> solutions;
|
||||
collect_metavar_solutions(lctx, f_b, solutions);
|
||||
lean_assert(solutions.size() >= 1);
|
||||
if (solutions.size() == 1) {
|
||||
new_args[0] = solutions[0];
|
||||
expr imitation = mk_app(new_args);
|
||||
justification new_jst(new imitation_justification(c));
|
||||
push_active(c);
|
||||
push_new_constraint(true, ctx_m, m, imitation, new_jst);
|
||||
} else {
|
||||
// More than one solution. We must create a case-split.
|
||||
std::unique_ptr<generic_case_split> new_cs(new generic_case_split(c, m_state));
|
||||
for (auto s : solutions) {
|
||||
new_args[0] = s;
|
||||
expr imitation = mk_app(new_args);
|
||||
state new_state(m_state);
|
||||
justification new_assumption = mk_assumption();
|
||||
push_front(new_state.m_active_cnstrs, c);
|
||||
push_new_eq_constraint(new_state.m_active_cnstrs, ctx_m, m, imitation, new_assumption);
|
||||
new_cs->push_back(new_state, new_assumption);
|
||||
}
|
||||
lean_verify(new_cs->next(*this));
|
||||
m_case_splits.push_back(std::move(new_cs));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Similar to imitate_abstraction, but b is a heterogeneous equality.
|
||||
*/
|
||||
void imitate_equality(expr const & a, expr const & b, unification_constraint const & c) {
|
||||
lean_assert(is_metavar(a));
|
||||
lean_assert(is_eq(b));
|
||||
lean_assert(!is_assigned(a));
|
||||
lean_assert(has_local_context(a));
|
||||
// imitate
|
||||
push_active(c);
|
||||
// Create a fresh meta variable for the lhs and rhs of b.
|
||||
// The new metavariables have the same context of a.
|
||||
expr m = mk_metavar(metavar_name(a));
|
||||
context ctx_m = m_state.m_menv->get_context(m);
|
||||
expr h1 = m_state.m_menv->mk_metavar(ctx_m);
|
||||
expr h2 = m_state.m_menv->mk_metavar(ctx_m);
|
||||
expr imitation = mk_eq(h1, h2);
|
||||
justification new_jst(new imitation_justification(c));
|
||||
push_new_constraint(true, ctx_m, m, imitation, new_jst);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Process a constraint <tt>ctx |- a == b</tt> where \c a is of the form <tt>?m[(inst:i t), ...]</tt>.
|
||||
We perform a "case split",
|
||||
|
@ -908,105 +1089,62 @@ class elaborator::imp {
|
|||
Case 2) imitate b
|
||||
*/
|
||||
bool process_metavar_inst(expr const & a, expr const & b, bool is_lhs, unification_constraint const & c) {
|
||||
if (is_metavar_inst(a) && !is_metavar_inst(b) && !is_meta_app(b) &&
|
||||
(is_eq(c) || (is_lhs && !is_actual_upper(b)) || (!is_lhs && !is_actual_lower(b)))) {
|
||||
context const & ctx = get_context(c);
|
||||
local_context lctx = metavar_lctx(a);
|
||||
unsigned i = head(lctx).s();
|
||||
expr t = head(lctx).v();
|
||||
|
||||
metavar_env & menv = m_state.m_menv;
|
||||
buffer<unification_constraint> ucs;
|
||||
expr b_type = m_type_inferer(b, ctx, menv, ucs);
|
||||
push_active(ucs);
|
||||
context ctx_with_i = ctx.insert_at(i, g_x_name, b_type); // must add entry for variable #i in the context
|
||||
|
||||
std::unique_ptr<generic_case_split> new_cs(new generic_case_split(c, m_state));
|
||||
{
|
||||
// Case 1
|
||||
state new_state(m_state);
|
||||
justification new_assumption = mk_assumption();
|
||||
// add ?m[...] == #1
|
||||
push_new_eq_constraint(new_state.m_active_cnstrs, ctx_with_i, pop_meta_context(a), mk_var(i), new_assumption);
|
||||
// add t == b (t << b)
|
||||
expr new_a = t;
|
||||
expr new_b = b;
|
||||
if (!is_lhs)
|
||||
swap(new_a, new_b);
|
||||
push_new_constraint(new_state.m_active_cnstrs, is_eq(c), ctx, new_a, new_b, new_assumption);
|
||||
new_cs->push_back(new_state, new_assumption);
|
||||
}
|
||||
{
|
||||
// Case 2
|
||||
state new_state(m_state);
|
||||
justification new_assumption = mk_assumption();
|
||||
expr imitation;
|
||||
if (is_app(b)) {
|
||||
// Imitation for applications b == f(s_1, ..., s_k)
|
||||
// mname <- f(?h_1, ..., ?h_k)
|
||||
expr f_b = arg(b, 0);
|
||||
unsigned num_b = num_args(b);
|
||||
buffer<expr> imitation_args;
|
||||
imitation_args.push_back(f_b);
|
||||
for (unsigned j = 1; j < num_b; j++) {
|
||||
expr h_j = new_state.m_menv->mk_metavar(ctx);
|
||||
imitation_args.push_back(h_j);
|
||||
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, h_j, lift_free_vars(arg(b, j), i, 1, new_state.m_menv), new_assumption);
|
||||
}
|
||||
imitation = mk_app(imitation_args);
|
||||
} else if (is_eq(b)) {
|
||||
// Imitation for equality b == Eq(s1, s2)
|
||||
// mname <- Eq(?h_1, ?h_2)
|
||||
expr h_1 = new_state.m_menv->mk_metavar(ctx);
|
||||
expr h_2 = new_state.m_menv->mk_metavar(ctx);
|
||||
imitation = mk_eq(h_1, h_2);
|
||||
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, h_1, lift_free_vars(eq_lhs(b), i, 1, new_state.m_menv), new_assumption);
|
||||
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, h_2, lift_free_vars(eq_rhs(b), i, 1, new_state.m_menv), new_assumption);
|
||||
} else if (is_abstraction(b)) {
|
||||
// Lambdas and Pis
|
||||
// Imitation for Lambdas and Pis, b == Fun(x:T) B
|
||||
// mname <- Fun (x:?h_1) ?h_2
|
||||
expr h_1 = new_state.m_menv->mk_metavar(ctx);
|
||||
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, h_1, lift_free_vars(abst_domain(b), i, 1, new_state.m_menv),
|
||||
new_assumption);
|
||||
context new_ctx = extend(ctx, abst_name(b), abst_domain(b));
|
||||
expr h_2 = new_state.m_menv->mk_metavar(new_ctx);
|
||||
imitation = update_abstraction(b, h_1, h_2);
|
||||
push_new_eq_constraint(new_state.m_active_cnstrs, new_ctx, h_2, lift_free_vars(abst_body(b), i+1, 1, new_state.m_menv),
|
||||
new_assumption);
|
||||
if (is_metavar_inst(a) && (is_eq(c) || (is_lhs && !is_actual_upper(b)) || (!is_lhs && !is_actual_lower(b)))) {
|
||||
lean_assert(!is_assigned(a));
|
||||
if (is_constant(b) || is_type(b) || is_value(b) || is_var(b)) {
|
||||
local_context lctx = metavar_lctx(a);
|
||||
buffer<expr> solutions;
|
||||
collect_metavar_solutions(lctx, b, solutions);
|
||||
lean_assert(solutions.size() >= 1);
|
||||
bool keep_c = local_context_has_metavar(lctx);
|
||||
expr m = mk_metavar(metavar_name(a));
|
||||
context ctx_m = m_state.m_menv->get_context(m);
|
||||
if (solutions.size() == 1) {
|
||||
justification new_jst(new destruct_justification(c));
|
||||
if (keep_c)
|
||||
push_active(c);
|
||||
push_new_eq_constraint(ctx_m, m, solutions[0], new_jst);
|
||||
return true;
|
||||
} else {
|
||||
imitation = lift_free_vars(b, i, 1, new_state.m_menv);
|
||||
// More than one solution. We must create a case-split.
|
||||
std::unique_ptr<generic_case_split> new_cs(new generic_case_split(c, m_state));
|
||||
for (auto s : solutions) {
|
||||
state new_state(m_state);
|
||||
justification new_assumption = mk_assumption();
|
||||
if (keep_c)
|
||||
push_front(new_state.m_active_cnstrs, c);
|
||||
push_new_eq_constraint(new_state.m_active_cnstrs, ctx_m, m, s, new_assumption);
|
||||
new_cs->push_back(new_state, new_assumption);
|
||||
}
|
||||
bool r = new_cs->next(*this);
|
||||
lean_assert(r);
|
||||
m_case_splits.push_back(std::move(new_cs));
|
||||
return r;
|
||||
}
|
||||
push_new_eq_constraint(new_state.m_active_cnstrs, ctx_with_i, pop_meta_context(a), imitation, new_assumption);
|
||||
new_cs->push_back(new_state, new_assumption);
|
||||
} else if (is_lambda(b) || is_pi(b)) {
|
||||
imitate_abstraction(a, b, c);
|
||||
return true;
|
||||
} else if (is_app(b) && !has_metavar(arg(b, 0))) {
|
||||
imitate_application(a, b, c);
|
||||
return true;
|
||||
} else if (is_eq(b)) {
|
||||
imitate_equality(a, b, c);
|
||||
return true;
|
||||
}
|
||||
bool r = new_cs->next(*this);
|
||||
lean_assert(r);
|
||||
m_case_splits.push_back(std::move(new_cs));
|
||||
return r;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Process a constraint of the form <tt>ctx |- ?m[lift, ...] == b</tt> where \c b is an abstraction.
|
||||
That is, \c b is a Pi or Lambda. In both cases, ?m must have the same kind.
|
||||
We just add a new assignment that forces ?m to have the corresponding kind.
|
||||
|
||||
Remark: we can expand this method and support application and equality
|
||||
*/
|
||||
bool process_metavar_lift_abstraction(expr const & a, expr const & b, unification_constraint const & c) {
|
||||
if (is_metavar_lift(a) && is_abstraction(b)) {
|
||||
push_active(c);
|
||||
// a <- (fun x : ?h1, ?h2) or (Pi x : ?h1, ?h2)
|
||||
// ?h1 is in the same context where 'a' was defined
|
||||
// ?h2 is in the context of 'a' + domain of b
|
||||
context ctx_a = m_state.m_menv->get_context(metavar_name(a));
|
||||
expr h_1 = m_state.m_menv->mk_metavar(ctx_a);
|
||||
expr h_2 = m_state.m_menv->mk_metavar(extend(ctx_a, abst_name(b), abst_domain(b)));
|
||||
expr imitation = update_abstraction(b, h_1, h_2);
|
||||
expr ma = mk_metavar(metavar_name(a));
|
||||
justification new_jst(new imitation_justification(c));
|
||||
push_new_constraint(true, ctx_a, ma, imitation, new_jst);
|
||||
imitate_abstraction(a, b, c);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
12
tests/lean/exists7.lean
Normal file
12
tests/lean/exists7.lean
Normal file
|
@ -0,0 +1,12 @@
|
|||
SetOption pp::colors false
|
||||
Variable N : Type
|
||||
Variables a b c : N
|
||||
Variables P : N -> N -> N -> Bool
|
||||
|
||||
SetOpaque forall false.
|
||||
SetOpaque exists false.
|
||||
SetOpaque not false.
|
||||
|
||||
Theorem T1 (f : N -> N) (H : P (f a) b (f (f c))) : exists x y z, P x y z := ExistsIntro _ (ExistsIntro _ (ExistsIntro _ H))
|
||||
|
||||
Show Environment 1.
|
11
tests/lean/exists7.lean.expected.out
Normal file
11
tests/lean/exists7.lean.expected.out
Normal file
|
@ -0,0 +1,11 @@
|
|||
Set: pp::colors
|
||||
Set: pp::unicode
|
||||
Set: pp::colors
|
||||
Assumed: N
|
||||
Assumed: a
|
||||
Assumed: b
|
||||
Assumed: c
|
||||
Assumed: P
|
||||
Proved: T1
|
||||
Theorem T1 (f : N → N) (H : P (f a) b (f (f c))) : ∃ x y z : N, P x y z :=
|
||||
ExistsIntro (f a) (ExistsIntro b (ExistsIntro (f (f c)) H))
|
Loading…
Reference in a new issue