2013-10-03 22:02:07 +00:00
|
|
|
/*
|
|
|
|
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
|
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
|
|
|
|
Author: Leonardo de Moura
|
|
|
|
*/
|
2013-10-16 00:32:02 +00:00
|
|
|
#include <memory>
|
|
|
|
#include <vector>
|
2013-10-16 20:26:31 +00:00
|
|
|
#include <utility>
|
2013-12-15 07:23:57 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include "util/list.h"
|
|
|
|
#include "util/splay_tree.h"
|
2013-11-13 05:42:22 +00:00
|
|
|
#include "util/interrupt.h"
|
2013-12-15 07:23:57 +00:00
|
|
|
#include "kernel/for_each_fn.h"
|
2013-10-14 15:16:23 +00:00
|
|
|
#include "kernel/formatter.h"
|
2013-10-16 20:26:31 +00:00
|
|
|
#include "kernel/free_vars.h"
|
|
|
|
#include "kernel/normalizer.h"
|
|
|
|
#include "kernel/instantiate.h"
|
2013-12-03 20:40:52 +00:00
|
|
|
#include "kernel/replace_fn.h"
|
2014-01-16 04:06:29 +00:00
|
|
|
#include "kernel/kernel.h"
|
2013-12-22 19:51:38 +00:00
|
|
|
#include "kernel/type_checker.h"
|
2013-12-18 00:35:39 +00:00
|
|
|
#include "kernel/update_expr.h"
|
2014-01-02 21:37:50 +00:00
|
|
|
#include "library/printer.h"
|
2014-01-16 10:05:09 +00:00
|
|
|
#include "library/equality.h"
|
2013-10-03 22:02:07 +00:00
|
|
|
#include "library/elaborator/elaborator.h"
|
2013-10-23 20:42:14 +00:00
|
|
|
#include "library/elaborator/elaborator_justification.h"
|
2013-10-03 22:02:07 +00:00
|
|
|
|
|
|
|
namespace lean {
|
2013-10-16 00:32:02 +00:00
|
|
|
static name g_x_name("x");
|
|
|
|
|
2013-10-03 22:02:07 +00:00
|
|
|
class elaborator::imp {
|
2013-12-15 07:23:57 +00:00
|
|
|
typedef splay_tree<name, name_cmp> name_set;
|
|
|
|
typedef list<unification_constraint> cnstr_list;
|
|
|
|
typedef list<name> name_list;
|
|
|
|
typedef list<std::pair<unification_constraint, name_list>> delayed_cnstr_list;
|
2013-10-14 15:16:23 +00:00
|
|
|
|
|
|
|
struct state {
|
2013-12-15 07:23:57 +00:00
|
|
|
metavar_env m_menv;
|
|
|
|
cnstr_list m_active_cnstrs;
|
|
|
|
delayed_cnstr_list m_delayed_cnstrs;
|
|
|
|
name_set m_recently_assigned; // recently assigned metavars
|
2013-10-16 00:32:02 +00:00
|
|
|
state(metavar_env const & menv, unsigned num_cnstrs, unification_constraint const * cnstrs):
|
2013-12-13 01:47:11 +00:00
|
|
|
m_menv(menv.copy()),
|
2013-12-15 07:23:57 +00:00
|
|
|
m_active_cnstrs(to_list(cnstrs, cnstrs + num_cnstrs)) {
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
2013-12-13 01:47:11 +00:00
|
|
|
|
|
|
|
state(state const & other):
|
|
|
|
m_menv(other.m_menv.copy()),
|
2013-12-15 07:23:57 +00:00
|
|
|
m_active_cnstrs(other.m_active_cnstrs),
|
|
|
|
m_delayed_cnstrs(other.m_delayed_cnstrs),
|
|
|
|
m_recently_assigned(other.m_recently_assigned) {
|
2013-12-13 01:47:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
state & operator=(state const & other) {
|
|
|
|
m_menv = other.m_menv.copy();
|
2013-12-15 07:23:57 +00:00
|
|
|
m_active_cnstrs = other.m_active_cnstrs;
|
|
|
|
m_delayed_cnstrs = other.m_delayed_cnstrs;
|
|
|
|
m_recently_assigned = other.m_recently_assigned;
|
2013-12-13 01:47:11 +00:00
|
|
|
return *this;
|
|
|
|
}
|
2013-10-16 00:32:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Base class for case splits performed by the elaborator.
|
|
|
|
*/
|
|
|
|
struct case_split {
|
2013-10-23 20:42:14 +00:00
|
|
|
justification m_curr_assumption; // object used to justify current split
|
|
|
|
state m_prev_state;
|
|
|
|
std::vector<justification> m_failed_justifications; // justifications for failed branches
|
2013-10-16 00:32:02 +00:00
|
|
|
|
|
|
|
case_split(state const & prev_state):m_prev_state(prev_state) {}
|
|
|
|
virtual ~case_split() {}
|
|
|
|
|
|
|
|
virtual bool next(imp & owner) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Case-split object for choice constraints.
|
|
|
|
*/
|
|
|
|
struct choice_case_split : public case_split {
|
|
|
|
unsigned m_idx;
|
|
|
|
unification_constraint m_choice;
|
|
|
|
|
|
|
|
choice_case_split(unification_constraint const & c, state const & prev_state):
|
|
|
|
case_split(prev_state),
|
|
|
|
m_idx(0),
|
|
|
|
m_choice(c) {
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~choice_case_split() {}
|
|
|
|
|
|
|
|
virtual bool next(imp & owner) {
|
|
|
|
return owner.next_choice_case(*this);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2013-10-22 20:45:04 +00:00
|
|
|
\brief General purpose case split object
|
2013-10-16 00:32:02 +00:00
|
|
|
*/
|
2013-10-22 20:45:04 +00:00
|
|
|
struct generic_case_split : public case_split {
|
2013-10-23 20:42:14 +00:00
|
|
|
unification_constraint m_constraint;
|
|
|
|
unsigned m_idx; // current alternative
|
|
|
|
std::vector<state> m_states; // alternatives
|
|
|
|
std::vector<justification> m_assumptions; // assumption for each alternative
|
2013-10-16 00:32:02 +00:00
|
|
|
|
2013-10-22 20:45:04 +00:00
|
|
|
generic_case_split(unification_constraint const & cnstr, state const & prev_state):
|
2013-10-16 00:32:02 +00:00
|
|
|
case_split(prev_state),
|
|
|
|
m_constraint(cnstr),
|
2013-10-22 16:42:06 +00:00
|
|
|
m_idx(0) {
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
|
2013-10-22 20:45:04 +00:00
|
|
|
virtual ~generic_case_split() {}
|
2013-10-16 00:32:02 +00:00
|
|
|
|
|
|
|
virtual bool next(imp & owner) {
|
2013-10-22 20:45:04 +00:00
|
|
|
return owner.next_generic_case(*this);
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
2013-10-22 16:42:06 +00:00
|
|
|
|
2013-10-23 20:42:14 +00:00
|
|
|
void push_back(state const & s, justification const & tr) {
|
2013-10-22 16:42:06 +00:00
|
|
|
m_states.push_back(s);
|
|
|
|
m_assumptions.push_back(tr);
|
|
|
|
}
|
2013-10-16 00:32:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct plugin_case_split : public case_split {
|
|
|
|
unification_constraint m_constraint;
|
|
|
|
std::unique_ptr<elaborator_plugin::result> m_alternatives;
|
|
|
|
|
|
|
|
plugin_case_split(unification_constraint const & cnstr, std::unique_ptr<elaborator_plugin::result> & r, state const & prev_state):
|
|
|
|
case_split(prev_state),
|
|
|
|
m_constraint(cnstr),
|
|
|
|
m_alternatives(std::move(r)) {
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~plugin_case_split() {}
|
|
|
|
|
|
|
|
virtual bool next(imp & owner) {
|
|
|
|
return owner.next_plugin_case(*this);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-12-13 00:33:31 +00:00
|
|
|
ro_environment m_env;
|
2013-10-18 16:58:12 +00:00
|
|
|
type_inferer m_type_inferer;
|
2013-10-16 20:26:31 +00:00
|
|
|
normalizer m_normalizer;
|
2013-10-16 00:32:02 +00:00
|
|
|
state m_state;
|
|
|
|
std::vector<std::unique_ptr<case_split>> m_case_splits;
|
|
|
|
std::shared_ptr<elaborator_plugin> m_plugin;
|
|
|
|
unsigned m_next_id;
|
2013-10-23 20:42:14 +00:00
|
|
|
justification m_conflict;
|
2013-10-23 00:49:37 +00:00
|
|
|
bool m_first;
|
2013-12-21 11:58:39 +00:00
|
|
|
level m_U; // universe U used for builtin kernel axioms
|
2013-10-16 00:32:02 +00:00
|
|
|
|
|
|
|
// options
|
2013-10-23 20:42:14 +00:00
|
|
|
bool m_use_justifications;
|
2013-10-16 00:32:02 +00:00
|
|
|
bool m_use_normalizer;
|
2013-10-24 23:44:05 +00:00
|
|
|
bool m_assume_injectivity;
|
2013-10-16 00:32:02 +00:00
|
|
|
|
|
|
|
void set_options(options const &) {
|
2013-10-23 20:42:14 +00:00
|
|
|
m_use_justifications = true;
|
|
|
|
m_use_normalizer = true;
|
2013-10-24 23:44:05 +00:00
|
|
|
m_assume_injectivity = true;
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
|
2013-10-23 20:42:14 +00:00
|
|
|
justification mk_assumption() {
|
2013-10-16 00:32:02 +00:00
|
|
|
unsigned id = m_next_id;
|
|
|
|
m_next_id++;
|
2013-10-26 21:21:29 +00:00
|
|
|
return mk_assumption_justification(id);
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
|
2013-12-15 07:23:57 +00:00
|
|
|
void push_front(cnstr_list & clist, unification_constraint const & c) {
|
2013-12-14 20:25:00 +00:00
|
|
|
// std::cout << "PUSHING: "; display(std::cout, c); std::cout << "\n";
|
2013-12-15 07:23:57 +00:00
|
|
|
clist = cons(c, clist);
|
2013-12-14 20:25:00 +00:00
|
|
|
}
|
|
|
|
|
2013-12-15 07:23:57 +00:00
|
|
|
/** \brief Add given constraint to active list */
|
|
|
|
void push_active(unification_constraint const & c) {
|
|
|
|
push_front(m_state.m_active_cnstrs, c);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
2013-12-15 07:23:57 +00:00
|
|
|
/** \brief Push all contraints in the collection to the active list */
|
|
|
|
void push_active(buffer<unification_constraint> const & cs) {
|
|
|
|
for (auto const & c : cs)
|
|
|
|
push_active(c);
|
|
|
|
}
|
|
|
|
|
2014-01-03 00:08:32 +00:00
|
|
|
void collect_mvars(expr const & e, name_set & r) {
|
|
|
|
for_each(e, [&](expr const & m, unsigned) {
|
|
|
|
if (is_metavar(m) && !r.contains(metavar_name(m))) {
|
|
|
|
r.insert(metavar_name(m));
|
|
|
|
for (auto const & entry : metavar_lctx(m)) {
|
|
|
|
if (entry.is_inst())
|
|
|
|
collect_mvars(entry.v(), r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-12-15 07:23:57 +00:00
|
|
|
/** \brief Collect metavars in \c c */
|
|
|
|
name_list collect_mvars(unification_constraint const & c) {
|
2014-01-03 00:08:32 +00:00
|
|
|
name_set s;
|
|
|
|
auto proc = [&](expr const & e) { return collect_mvars(e, s); };
|
2013-12-15 07:23:57 +00:00
|
|
|
switch (c.kind()) {
|
|
|
|
case unification_constraint_kind::Eq:
|
|
|
|
proc(eq_lhs(c)); proc(eq_rhs(c)); break;
|
|
|
|
case unification_constraint_kind::Convertible:
|
|
|
|
proc(convertible_from(c)); proc(convertible_to(c)); break;
|
|
|
|
case unification_constraint_kind::Max:
|
|
|
|
proc(max_lhs1(c)); proc(max_lhs2(c)); proc(max_rhs(c)); break;
|
|
|
|
case unification_constraint_kind::Choice:
|
|
|
|
lean_unreachable(); // we never delay Choice
|
|
|
|
break;
|
|
|
|
}
|
2014-01-03 00:08:32 +00:00
|
|
|
return s.fold([](name const & n, name_list const & l) { return cons(n, l); }, name_list());
|
2013-12-15 07:23:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Add given constraint to the delayed list */
|
|
|
|
void push_delayed(unification_constraint const & c) {
|
|
|
|
m_state.m_delayed_cnstrs.emplace_front(c, collect_mvars(c));
|
2013-10-21 23:45:14 +00:00
|
|
|
}
|
|
|
|
|
2013-10-16 20:26:31 +00:00
|
|
|
/** \brief Return true iff \c m is an assigned metavariable in the current state */
|
|
|
|
bool is_assigned(expr const & m) const {
|
|
|
|
lean_assert(is_metavar(m));
|
2013-12-13 01:47:11 +00:00
|
|
|
return m_state.m_menv->is_assigned(m);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 09:39:52 +00:00
|
|
|
/** \brief Return the substitution (and justification) for an assigned metavariable */
|
|
|
|
std::pair<expr, justification> get_subst_jst(expr const & m) const {
|
2013-10-16 20:26:31 +00:00
|
|
|
lean_assert(is_metavar(m));
|
|
|
|
lean_assert(is_assigned(m));
|
2013-12-13 01:47:11 +00:00
|
|
|
return *(m_state.m_menv->get_subst_jst(m));
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Return true iff \c e contains the metavariable \c m.
|
|
|
|
The substitutions in the current state are taken into account.
|
|
|
|
*/
|
|
|
|
bool has_metavar(expr const & e, expr const & m) const {
|
2013-12-13 01:47:11 +00:00
|
|
|
return m_state.m_menv->has_metavar(e, m);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
2013-10-22 20:45:04 +00:00
|
|
|
static bool has_metavar(expr const & e) {
|
|
|
|
return ::lean::has_metavar(e);
|
|
|
|
}
|
|
|
|
|
2013-10-21 23:45:14 +00:00
|
|
|
/**
|
|
|
|
\brief Return true iff \c e contains an assigned metavariable in
|
|
|
|
the current state.
|
|
|
|
*/
|
|
|
|
bool has_assigned_metavar(expr const & e) const {
|
2013-12-13 01:47:11 +00:00
|
|
|
return m_state.m_menv->has_assigned_metavar(e);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Return true if \c a is of the form <tt>(?m ...)</tt> */
|
|
|
|
static bool is_meta_app(expr const & a) {
|
|
|
|
return is_app(a) && is_metavar(arg(a, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Return true iff \c a is a metavariable or if \c a is an application where the function is a metavariable */
|
|
|
|
static bool is_meta(expr const & a) {
|
|
|
|
return is_metavar(a) || is_meta_app(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Return true iff \c a is a metavariable and has a meta context. */
|
|
|
|
static bool is_metavar_with_context(expr const & a) {
|
|
|
|
return is_metavar(a) && has_local_context(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Return true if \c a is of the form <tt>(?m[...] ...)</tt> */
|
|
|
|
static bool is_meta_app_with_context(expr const & a) {
|
|
|
|
return is_meta_app(a) && has_local_context(arg(a, 0));
|
|
|
|
}
|
|
|
|
|
2013-11-29 17:14:54 +00:00
|
|
|
/** \brief Return true iff \c a is a proposition */
|
|
|
|
bool is_proposition(expr const & a, context const & ctx) {
|
2013-12-11 03:40:10 +00:00
|
|
|
if ((is_metavar(a)) ||
|
|
|
|
(is_app(a) && is_metavar(arg(a, 0))) ||
|
|
|
|
(is_abstraction(a) && (is_metavar(abst_domain(a)) || is_metavar(abst_body(a))))) {
|
2013-12-11 00:31:33 +00:00
|
|
|
// Avoid exception at m_type_inferer.
|
|
|
|
// Throw is expensive in C++.
|
|
|
|
return false;
|
|
|
|
}
|
2013-11-29 17:14:54 +00:00
|
|
|
try {
|
|
|
|
return m_type_inferer.is_proposition(a, ctx);
|
|
|
|
} catch (...) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-16 20:26:31 +00:00
|
|
|
static expr mk_lambda(name const & n, expr const & d, expr const & b) {
|
|
|
|
return ::lean::mk_lambda(n, d, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Create the term (fun (x_0 : types[0]) ... (x_{n-1} : types[n-1]) body)
|
|
|
|
*/
|
|
|
|
expr mk_lambda(buffer<expr> const & types, expr const & body) {
|
|
|
|
expr r = body;
|
|
|
|
unsigned i = types.size();
|
|
|
|
while (i > 0) {
|
|
|
|
--i;
|
2013-10-24 22:42:17 +00:00
|
|
|
r = mk_lambda(i == 0 ? g_x_name : name(g_x_name, i), types[i], r);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
\brief Return (f x_{num_vars + first - 1} ... x_first)
|
2013-10-22 16:42:06 +00:00
|
|
|
*/
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
expr mk_app_vars(expr const & f, unsigned num_vars, unsigned first = 0) {
|
2013-10-22 16:42:06 +00:00
|
|
|
buffer<expr> args;
|
|
|
|
args.push_back(f);
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
unsigned i = num_vars + first;
|
|
|
|
while (i > first) {
|
2013-10-22 16:42:06 +00:00
|
|
|
--i;
|
|
|
|
args.push_back(mk_var(i));
|
|
|
|
}
|
2013-10-26 21:46:12 +00:00
|
|
|
return mk_app(args);
|
2013-10-22 16:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-12-15 07:23:57 +00:00
|
|
|
\brief Push a new constraint to the active_cnstrs.
|
2013-10-22 16:42:06 +00:00
|
|
|
If \c is_eq is true, then a equality constraint is created, otherwise a convertability constraint is created.
|
|
|
|
*/
|
2013-12-15 07:23:57 +00:00
|
|
|
void push_new_constraint(cnstr_list & active_cnstrs, bool is_eq,
|
|
|
|
context const & new_ctx, expr const & new_a, expr const & new_b, justification const & new_jst) {
|
2013-12-17 00:41:20 +00:00
|
|
|
if (new_a == new_b)
|
|
|
|
return; // trivial constraint
|
2013-10-22 16:42:06 +00:00
|
|
|
if (is_eq)
|
2013-12-15 07:23:57 +00:00
|
|
|
push_front(active_cnstrs, mk_eq_constraint(new_ctx, new_a, new_b, new_jst));
|
2013-10-22 16:42:06 +00:00
|
|
|
else
|
2013-12-15 07:23:57 +00:00
|
|
|
push_front(active_cnstrs, mk_convertible_constraint(new_ctx, new_a, new_b, new_jst));
|
2013-10-22 16:42:06 +00:00
|
|
|
}
|
|
|
|
|
2013-10-31 23:27:36 +00:00
|
|
|
/**
|
2013-12-15 07:23:57 +00:00
|
|
|
\brief Push a new equality constraint <tt>new_ctx |- new_a == new_b</tt> into the active_cnstrs using
|
2013-10-31 23:27:36 +00:00
|
|
|
justification \c new_jst.
|
|
|
|
*/
|
2013-12-15 07:23:57 +00:00
|
|
|
void push_new_eq_constraint(cnstr_list & active_cnstrs,
|
|
|
|
context const & new_ctx, expr const & new_a, expr const & new_b, justification const & new_jst) {
|
|
|
|
push_new_constraint(active_cnstrs, true, new_ctx, new_a, new_b, new_jst);
|
|
|
|
}
|
|
|
|
|
|
|
|
void push_new_eq_constraint(context const & new_ctx, expr const & new_a, expr const & new_b, justification const & new_jst) {
|
|
|
|
push_new_eq_constraint(m_state.m_active_cnstrs, new_ctx, new_a, new_b, new_jst);
|
2013-10-22 16:42:06 +00:00
|
|
|
}
|
|
|
|
|
2013-10-22 23:39:22 +00:00
|
|
|
/**
|
2013-12-15 07:23:57 +00:00
|
|
|
\brief Auxiliary method for pushing a new constraint to the current active list.
|
2013-10-22 23:39:22 +00:00
|
|
|
If \c is_eq is true, then a equality constraint is created, otherwise a convertability constraint is created.
|
|
|
|
*/
|
2013-10-23 20:42:14 +00:00
|
|
|
void push_new_constraint(bool is_eq, context const & new_ctx, expr const & new_a, expr const & new_b, justification const & new_jst) {
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_constraint(m_state.m_active_cnstrs, is_eq, new_ctx, new_a, new_b, new_jst);
|
2013-10-22 23:39:22 +00:00
|
|
|
}
|
|
|
|
|
2013-10-22 16:42:06 +00:00
|
|
|
/**
|
2013-12-15 07:23:57 +00:00
|
|
|
\brief Auxiliary method for pushing a new constraint to the current active constraint list.
|
2013-10-16 20:26:31 +00:00
|
|
|
The new constraint is based on the constraint \c c. The constraint \c c may be a equality or convertability constraint.
|
2013-10-23 20:42:14 +00:00
|
|
|
The update is justified by \c new_jst.
|
2013-10-16 20:26:31 +00:00
|
|
|
*/
|
2013-10-23 20:42:14 +00:00
|
|
|
void push_updated_constraint(unification_constraint const & c, expr const & new_a, expr const & new_b, justification const & new_jst) {
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_constraint(m_state.m_active_cnstrs, is_eq(c), get_context(c), new_a, new_b, new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-12-15 07:23:57 +00:00
|
|
|
\brief Auxiliary method for pushing a new constraint to the current active constraint list.
|
2013-10-16 20:26:31 +00:00
|
|
|
The new constraint is based on the constraint \c c. The constraint \c c may be a equality or convertability constraint.
|
|
|
|
The flag \c is_lhs says if the left-hand-side or right-hand-side are being updated with \c new_a.
|
2013-10-23 20:42:14 +00:00
|
|
|
The update is justified by \c new_jst.
|
2013-10-16 20:26:31 +00:00
|
|
|
*/
|
2013-10-23 20:42:14 +00:00
|
|
|
void push_updated_constraint(unification_constraint const & c, bool is_lhs, expr const & new_a, justification const & new_jst) {
|
2013-10-16 20:26:31 +00:00
|
|
|
lean_assert(is_eq(c) || is_convertible(c));
|
|
|
|
if (is_eq(c)) {
|
|
|
|
if (is_lhs)
|
2013-12-17 00:41:20 +00:00
|
|
|
push_updated_constraint(c, new_a, eq_rhs(c), new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
else
|
2013-12-17 00:41:20 +00:00
|
|
|
push_updated_constraint(c, eq_lhs(c), new_a, new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
} else {
|
|
|
|
if (is_lhs)
|
2013-12-17 00:41:20 +00:00
|
|
|
push_updated_constraint(c, new_a, convertible_to(c), new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
else
|
2013-12-17 00:41:20 +00:00
|
|
|
push_updated_constraint(c, convertible_from(c), new_a, new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Auxiliary method for pushing a new constraint to the constraint queue.
|
|
|
|
The new constraint is obtained from \c c by one or more normalization steps that produce \c new_a and \c new_b
|
|
|
|
*/
|
|
|
|
void push_normalized_constraint(unification_constraint const & c, expr const & new_a, expr const & new_b) {
|
2013-10-23 20:42:14 +00:00
|
|
|
push_updated_constraint(c, new_a, new_b, justification(new normalize_justification(c)));
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
2014-01-04 01:13:10 +00:00
|
|
|
justification mk_failure_justification(unification_constraint const & c) {
|
|
|
|
return justification(new unification_failure_justification(c, m_state.m_menv.copy()));
|
|
|
|
}
|
|
|
|
|
2013-10-16 20:26:31 +00:00
|
|
|
/**
|
|
|
|
\brief Assign \c v to \c m with justification \c tr in the current state.
|
|
|
|
*/
|
2013-12-14 20:25:00 +00:00
|
|
|
bool assign(expr const & m, expr const & v, unification_constraint const & c, bool is_lhs) {
|
2013-10-16 20:26:31 +00:00
|
|
|
lean_assert(is_metavar(m));
|
2013-12-14 20:25:00 +00:00
|
|
|
if (instantiate_metavars(!is_lhs, v, c)) // make sure v does not have assigned metavars
|
|
|
|
return true;
|
2013-10-25 18:19:17 +00:00
|
|
|
context const & ctx = get_context(c);
|
|
|
|
justification jst(new assignment_justification(c));
|
2013-12-13 01:47:11 +00:00
|
|
|
metavar_env const & menv = m_state.m_menv;
|
2013-12-14 20:25:00 +00:00
|
|
|
if (!menv->assign(m, v, jst)) {
|
2014-01-04 01:13:10 +00:00
|
|
|
m_conflict = mk_failure_justification(c);
|
2013-12-14 20:25:00 +00:00
|
|
|
return false;
|
|
|
|
}
|
2013-12-13 01:47:11 +00:00
|
|
|
if (menv->has_type(m)) {
|
2013-10-21 23:45:14 +00:00
|
|
|
buffer<unification_constraint> ucs;
|
2013-12-13 01:47:11 +00:00
|
|
|
expr tv = m_type_inferer(v, ctx, menv, ucs);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(ucs);
|
2013-12-13 01:47:11 +00:00
|
|
|
justification new_jst(new typeof_mvar_justification(ctx, m, menv->get_type(m), tv, jst));
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(mk_convertible_constraint(ctx, tv, menv->get_type(m), new_jst));
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
2013-12-15 07:23:57 +00:00
|
|
|
m_state.m_recently_assigned.insert(metavar_name(m));
|
2013-12-14 20:25:00 +00:00
|
|
|
return true;
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
2013-10-16 00:32:02 +00:00
|
|
|
bool process(unification_constraint const & c) {
|
|
|
|
switch (c.kind()) {
|
|
|
|
case unification_constraint_kind::Eq: return process_eq(c);
|
|
|
|
case unification_constraint_kind::Convertible: return process_convertible(c);
|
|
|
|
case unification_constraint_kind::Max: return process_max(c);
|
|
|
|
case unification_constraint_kind::Choice: return process_choice(c);
|
|
|
|
}
|
2013-11-11 17:19:38 +00:00
|
|
|
lean_unreachable(); // LCOV_EXCL_LINE
|
2013-10-16 00:32:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool process_eq(unification_constraint const & c) {
|
|
|
|
return process_eq_convertible(get_context(c), eq_lhs(c), eq_rhs(c), c);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool process_convertible(unification_constraint const & c) {
|
|
|
|
return process_eq_convertible(get_context(c), convertible_from(c), convertible_to(c), c);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
2013-10-16 00:32:02 +00:00
|
|
|
|
2013-10-16 20:26:31 +00:00
|
|
|
/**
|
|
|
|
Process <tt>ctx |- a == b</tt> and <tt>ctx |- a << b</tt> when:
|
|
|
|
1- \c a is an assigned metavariable
|
|
|
|
2- \c a is a unassigned metavariable without a metavariable context.
|
|
|
|
3- \c a is a unassigned metavariable of the form <tt>?m[lift:s:n, ...]</tt>, and \c b does not have
|
|
|
|
a free variable in the range <tt>[s, s+n)</tt>.
|
|
|
|
4- \c a is an application of the form <tt>(?m ...)</tt> where ?m is an assigned metavariable.
|
|
|
|
*/
|
|
|
|
enum status { Processed, Failed, Continue };
|
2013-11-29 17:14:54 +00:00
|
|
|
status process_metavar(unification_constraint const & c, expr const & a, expr const & b, bool is_lhs) {
|
2013-12-06 12:51:07 +00:00
|
|
|
context const & ctx = get_context(c);
|
2013-12-20 09:33:25 +00:00
|
|
|
lean_assert(!(is_metavar(a) && is_assigned(a)));
|
|
|
|
lean_assert(!(is_metavar(b) && is_assigned(b)));
|
2013-10-16 20:26:31 +00:00
|
|
|
if (is_metavar(a)) {
|
2013-12-20 09:33:25 +00:00
|
|
|
if (!has_local_context(a)) {
|
2013-10-16 20:26:31 +00:00
|
|
|
// Case 2
|
|
|
|
if (has_metavar(b, a)) {
|
2014-01-04 01:13:10 +00:00
|
|
|
m_conflict = mk_failure_justification(c);
|
2013-10-16 20:26:31 +00:00
|
|
|
return Failed;
|
2013-12-06 12:51:07 +00:00
|
|
|
} else if (is_eq(c) || is_proposition(b, ctx)) {
|
2013-11-29 17:14:54 +00:00
|
|
|
// At this point, we only assign metavariables if the constraint is an equational constraint,
|
|
|
|
// or b is a proposition.
|
|
|
|
// It is important to handle propositions since we don't want to normalize them.
|
|
|
|
// The normalization process destroys the structure of the proposition.
|
2013-12-14 20:25:00 +00:00
|
|
|
return assign(a, b, c, is_lhs) ? Processed : Failed;
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
} else {
|
2013-12-15 03:47:27 +00:00
|
|
|
if (!is_metavar(b) && has_metavar(b, a)) {
|
2014-01-04 01:13:10 +00:00
|
|
|
m_conflict = mk_failure_justification(c);
|
2013-12-15 03:47:27 +00:00
|
|
|
return Failed;
|
|
|
|
}
|
2013-10-16 20:26:31 +00:00
|
|
|
local_entry const & me = head(metavar_lctx(a));
|
2013-10-22 23:39:22 +00:00
|
|
|
if (me.is_lift()) {
|
2014-01-01 11:02:41 +00:00
|
|
|
// 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.
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new normalize_justification(c));
|
2014-01-01 11:02:41 +00:00
|
|
|
expr new_a = pop_meta_context(a);
|
|
|
|
expr new_b = lower_free_vars(b, s + n, n, m_state.m_menv);
|
2013-10-22 23:39:22 +00:00
|
|
|
if (!is_lhs)
|
|
|
|
swap(new_a, new_b);
|
2014-01-01 11:02:41 +00:00
|
|
|
push_new_constraint(is_eq(c), *new_ctx, new_a, new_b, new_jst);
|
2013-10-22 23:39:22 +00:00
|
|
|
return Processed;
|
2013-12-17 18:56:20 +00:00
|
|
|
} else if (!has_metavar(b)) {
|
2013-10-22 23:39:22 +00:00
|
|
|
// Failure, there is no way to unify
|
2013-12-17 18:56:20 +00:00
|
|
|
// ?m[lift:s:n, ...] with a term that contains variables in [s, s+n]
|
2014-01-04 01:13:10 +00:00
|
|
|
m_conflict = mk_failure_justification(c);
|
2013-10-22 23:39:22 +00:00
|
|
|
return Failed;
|
|
|
|
}
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
}
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
|
2013-10-16 20:26:31 +00:00
|
|
|
if (is_app(a) && is_metavar(arg(a, 0)) && is_assigned(arg(a, 0))) {
|
|
|
|
// Case 4
|
2013-10-29 09:39:52 +00:00
|
|
|
auto s_j = get_subst_jst(arg(a, 0));
|
|
|
|
justification new_jst(new substitution_justification(c, s_j.second));
|
|
|
|
expr new_f = s_j.first;
|
2013-10-23 23:35:47 +00:00
|
|
|
expr new_a = update_app(a, 0, new_f);
|
2013-12-13 01:47:11 +00:00
|
|
|
if (m_state.m_menv->beta_reduce_metavar_application())
|
2013-10-23 23:35:47 +00:00
|
|
|
new_a = head_beta_reduce(new_a);
|
2013-10-23 20:42:14 +00:00
|
|
|
push_updated_constraint(c, is_lhs, new_a, new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
return Processed;
|
|
|
|
}
|
|
|
|
return Continue;
|
|
|
|
}
|
2013-10-16 00:32:02 +00:00
|
|
|
|
2013-10-23 20:42:14 +00:00
|
|
|
justification mk_subst_justification(unification_constraint const & c, buffer<justification> const & subst_justifications) {
|
|
|
|
if (subst_justifications.size() == 1) {
|
|
|
|
return justification(new substitution_justification(c, subst_justifications[0]));
|
2013-10-21 23:45:14 +00:00
|
|
|
} else {
|
2013-10-23 20:42:14 +00:00
|
|
|
return justification(new multi_substitution_justification(c, subst_justifications.size(), subst_justifications.data()));
|
2013-10-21 23:45:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-23 19:01:39 +00:00
|
|
|
/**
|
2013-10-23 20:42:14 +00:00
|
|
|
\brief Instantiate the assigned metavariables in \c a, and store the justifications
|
|
|
|
in \c jsts.
|
2013-10-23 19:01:39 +00:00
|
|
|
*/
|
2013-10-23 20:42:14 +00:00
|
|
|
expr instantiate_metavars(expr const & a, buffer<justification> & jsts) {
|
2013-10-23 19:01:39 +00:00
|
|
|
lean_assert(has_assigned_metavar(a));
|
2013-12-13 01:47:11 +00:00
|
|
|
return m_state.m_menv->instantiate_metavars(a, jsts);
|
2013-10-23 19:01:39 +00:00
|
|
|
}
|
|
|
|
|
2013-10-21 23:45:14 +00:00
|
|
|
/**
|
|
|
|
\brief Return true iff \c a contains instantiated variables. If this is the case,
|
|
|
|
then constraint \c c is updated with a new \c a s.t. all metavariables of \c a
|
|
|
|
are instantiated.
|
|
|
|
|
|
|
|
\remark if \c is_lhs is true, then we are considering the left-hand-side of the constraint \c c.
|
|
|
|
*/
|
|
|
|
bool instantiate_metavars(bool is_lhs, expr const & a, unification_constraint const & c) {
|
|
|
|
lean_assert(is_eq(c) || is_convertible(c));
|
|
|
|
lean_assert(!is_eq(c) || !is_lhs || is_eqp(eq_lhs(c), a));
|
|
|
|
lean_assert(!is_eq(c) || is_lhs || is_eqp(eq_rhs(c), a));
|
|
|
|
lean_assert(!is_convertible(c) || !is_lhs || is_eqp(convertible_from(c), a));
|
|
|
|
lean_assert(!is_convertible(c) || is_lhs || is_eqp(convertible_to(c), a));
|
|
|
|
if (has_assigned_metavar(a)) {
|
2013-10-23 20:42:14 +00:00
|
|
|
buffer<justification> jsts;
|
|
|
|
expr new_a = instantiate_metavars(a, jsts);
|
|
|
|
justification new_jst = mk_subst_justification(c, jsts);
|
|
|
|
push_updated_constraint(c, is_lhs, new_a, new_jst);
|
2013-10-21 23:45:14 +00:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2013-10-16 20:26:31 +00:00
|
|
|
|
|
|
|
/** \brief Unfold let-expression */
|
|
|
|
void process_let(expr & a) {
|
|
|
|
if (is_let(a))
|
feat(library/elaborator): provide the metavar_env to instantiate and lift_free_vars in the elaborator, it will minimize the number of local_entries needed
The modifications started at commit 1852c86948dd7da8a2bac224e3c7c0ee309db224 made a big difference. For example, before these changes test tests/lean/implicit7.lean generated complicated constraints such as:
[x : Type; a : ?M::29[inst:1 ?M::0[lift:0:1]] x] ⊢ Pi B : Type, (Pi _ : x, (Pi _ : (?M::35[inst:0 #0, inst:1 #2, inst:2 #4, inst:3 #6, inst:5 #5, inst:6 #7, inst:7 #9, inst:9 #9, inst:10 #11, inst:13 ?M::0[lift:0:13]] x a B _), (?M::36[inst:1 #1, inst:2 #3, inst:3 #5, inst:4 #7, inst:6 #6, inst:7 #8, inst:8 #10, inst:10 #10, inst:11 #12, inst:14 ?M::0[lift:0:14]] x a B _ _))) ≈
?M::22 x a
After the changes, only very simple constraints are generated. The most complicated one is:
[] ⊢ Pi a : ?M::0, (Pi B : Type, (Pi _ : ?M::0, (Pi _ : B, ?M::0))) ≈ Pi x : ?M::17, ?M::18
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2013-12-14 23:55:12 +00:00
|
|
|
a = instantiate(let_body(a), let_value(a), m_state.m_menv);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Replace variables by their definition if the context contains it. */
|
|
|
|
void process_var(context const & ctx, expr & a) {
|
|
|
|
if (is_var(a)) {
|
2013-12-11 17:54:54 +00:00
|
|
|
auto e = find(ctx, var_idx(a));
|
|
|
|
if (e && e->get_body())
|
2013-12-21 14:41:09 +00:00
|
|
|
a = lift_free_vars(*(e->get_body()), var_idx(a) + 1, m_state.m_menv);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expr normalize(context const & ctx, expr const & a) {
|
2013-12-14 23:41:50 +00:00
|
|
|
return m_normalizer(a, ctx, m_state.m_menv);
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void process_app(context const & ctx, expr & a) {
|
|
|
|
if (is_app(a)) {
|
|
|
|
expr f = arg(a, 0);
|
|
|
|
if (is_value(f) && m_use_normalizer) {
|
|
|
|
// if f is a semantic attachment, we keep normalizing children from
|
|
|
|
// left to right until the semantic attachment is applicable
|
|
|
|
buffer<expr> new_args;
|
|
|
|
new_args.append(num_args(a), &(arg(a, 0)));
|
|
|
|
bool modified = false;
|
|
|
|
expr r;
|
|
|
|
for (unsigned i = 1; i < new_args.size(); i++) {
|
|
|
|
expr const & curr = new_args[i];
|
|
|
|
expr new_curr = normalize(ctx, curr);
|
|
|
|
if (curr != new_curr) {
|
|
|
|
modified = true;
|
|
|
|
new_args[i] = new_curr;
|
2013-12-08 07:21:07 +00:00
|
|
|
if (optional<expr> r = to_value(f).normalize(new_args.size(), new_args.data())) {
|
|
|
|
a = *r;
|
2013-10-16 20:26:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-12-08 07:21:07 +00:00
|
|
|
if (optional<expr> r = to_value(f).normalize(new_args.size(), new_args.data())) {
|
|
|
|
a = *r;
|
2013-10-28 14:42:14 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-10-16 20:26:31 +00:00
|
|
|
if (modified) {
|
2013-10-26 21:46:12 +00:00
|
|
|
a = mk_app(new_args);
|
2013-10-16 20:26:31 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-10-16 00:32:02 +00:00
|
|
|
} else {
|
2013-10-16 20:26:31 +00:00
|
|
|
process_let(f);
|
|
|
|
process_var(ctx, f);
|
|
|
|
f = head_beta_reduce(f);
|
|
|
|
a = update_app(a, 0, f);
|
|
|
|
a = head_beta_reduce(a);
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
}
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
2013-10-16 00:32:02 +00:00
|
|
|
|
2013-10-16 20:26:31 +00:00
|
|
|
expr normalize_step(context const & ctx, expr const & a) {
|
|
|
|
expr new_a = a;
|
|
|
|
process_let(new_a);
|
|
|
|
process_var(ctx, new_a);
|
|
|
|
process_app(ctx, new_a);
|
|
|
|
return new_a;
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_const_weight(expr const & a) {
|
|
|
|
lean_assert(is_constant(a));
|
2013-12-13 00:33:31 +00:00
|
|
|
optional<object> obj = m_env->find_object(const_name(a));
|
refactor(kernel): add unfold_opaque flag to normalizer, modify how type checker uses the opaque flag, remove hidden_defs, and mark most builtin definitions as opaque
After this commit, in the type checker, when checking convertability, we first compute a normal form without expanding opaque terms.
If the terms are convertible, then we are done, and saved a lot of time by not expanding unnecessary definitions.
If they are not, instead of throwing an error, we try again expanding the opaque terms.
This seems to be the best of both worlds.
The opaque flag is a hint for the type checker, but it would never prevent us from type checking a valid term.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2013-12-20 20:47:36 +00:00
|
|
|
if (should_unfold(obj))
|
2013-12-08 22:37:38 +00:00
|
|
|
return obj->get_weight();
|
2013-10-16 20:26:31 +00:00
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Return a number >= 0 if \c a is a defined constant or the application of a defined constant.
|
|
|
|
In this case, the value is the weight of the definition.
|
|
|
|
*/
|
|
|
|
int get_unfolding_weight(expr const & a) {
|
|
|
|
if (is_constant(a))
|
|
|
|
return get_const_weight(a);
|
|
|
|
else if (is_app(a) && is_constant(arg(a, 0)))
|
|
|
|
return get_const_weight(arg(a, 0));
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
expr unfold(expr const & a) {
|
|
|
|
lean_assert(is_constant(a) || (is_app(a) && is_constant(arg(a, 0))));
|
|
|
|
if (is_constant(a)) {
|
2013-12-13 00:33:31 +00:00
|
|
|
lean_assert(m_env->find_object(const_name(a)));
|
|
|
|
return m_env->find_object(const_name(a))->get_value();
|
2013-10-16 20:26:31 +00:00
|
|
|
} else {
|
2013-12-13 00:33:31 +00:00
|
|
|
lean_assert(m_env->find_object(const_name(arg(a, 0))));
|
|
|
|
return update_app(a, 0, m_env->find_object(const_name(arg(a, 0)))->get_value());
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool normalize_head(expr a, expr b, unification_constraint const & c) {
|
|
|
|
context const & ctx = get_context(c);
|
|
|
|
bool modified = false;
|
|
|
|
while (true) {
|
2013-11-13 05:42:22 +00:00
|
|
|
check_interrupted();
|
2013-10-16 20:26:31 +00:00
|
|
|
expr new_a = normalize_step(ctx, a);
|
|
|
|
expr new_b = normalize_step(ctx, b);
|
|
|
|
if (new_a == a && new_b == b) {
|
|
|
|
int w_a = get_unfolding_weight(a);
|
|
|
|
int w_b = get_unfolding_weight(b);
|
|
|
|
if (w_a >= 0 || w_b >= 0) {
|
|
|
|
if (w_a >= w_b)
|
|
|
|
new_a = unfold(a);
|
|
|
|
if (w_b >= w_a)
|
|
|
|
new_b = unfold(b);
|
|
|
|
if (new_a == a && new_b == b)
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
modified = true;
|
|
|
|
a = new_a;
|
|
|
|
b = new_b;
|
|
|
|
if (a == b) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (modified) {
|
|
|
|
push_normalized_constraint(c, a, b);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-21 23:45:14 +00:00
|
|
|
/** \brief Return true iff the variable with id \c vidx has a body/definition in the context \c ctx. */
|
|
|
|
static bool has_body(context const & ctx, unsigned vidx) {
|
2013-12-11 17:54:54 +00:00
|
|
|
auto e = find(ctx, vidx);
|
|
|
|
return e && e->get_body();
|
2013-10-21 23:45:14 +00:00
|
|
|
}
|
|
|
|
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
/**
|
|
|
|
\brief Return true iff \c a may be an actual lower bound for a convertibility constraint.
|
|
|
|
That is, if the result is false, it means the convertability constraint is essentially
|
|
|
|
an equality constraint.
|
|
|
|
*/
|
|
|
|
bool is_actual_lower(expr const & a) {
|
|
|
|
return is_type(a) || is_metavar(a) || is_bool(a) || (is_pi(a) && is_actual_lower(abst_body(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Return true iff \c a may be an actual upper bound for a convertibility constraint.
|
|
|
|
That is, if the result is false, it means the convertability constraint is essentially
|
|
|
|
an equality constraint.
|
|
|
|
*/
|
|
|
|
bool is_actual_upper(expr const & a) {
|
|
|
|
return is_type(a) || is_metavar(a) || (is_pi(a) && is_actual_upper(abst_body(a)));
|
|
|
|
}
|
|
|
|
|
|
|
|
optional<expr> try_get_type(context const & ctx, expr const & e) {
|
|
|
|
try {
|
|
|
|
return some_expr(m_type_inferer(e, ctx));
|
|
|
|
} catch (...) {
|
|
|
|
return none_expr();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-22 20:45:04 +00:00
|
|
|
/**
|
|
|
|
\brief Return true iff all arguments of the application \c a are variables that do
|
|
|
|
not have a definition in \c ctx.
|
|
|
|
*/
|
|
|
|
static bool are_args_vars(context const & ctx, expr const & a) {
|
|
|
|
lean_assert(is_app(a));
|
|
|
|
for (unsigned i = 1; i < num_args(a); i++) {
|
|
|
|
if (!is_var(arg(a, i)))
|
|
|
|
return false;
|
|
|
|
if (has_body(ctx, var_idx(arg(a, i))))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-27 18:17:03 +00:00
|
|
|
/**
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
\brief are_args_vars(a) && all variables are distinct
|
2013-10-27 18:17:03 +00:00
|
|
|
*/
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
static bool are_args_distinct_vars(context const & ctx, expr const & a) {
|
|
|
|
if (!are_args_vars(ctx, a))
|
|
|
|
return false;
|
|
|
|
buffer<bool> found;
|
|
|
|
for (unsigned i = 1; i < num_args(a); i++) {
|
|
|
|
unsigned vidx = var_idx(arg(a, i));
|
|
|
|
if (vidx >= found.size())
|
|
|
|
found.resize(vidx+1, false);
|
|
|
|
if (found[vidx])
|
|
|
|
return false;
|
|
|
|
found[vidx] = true;
|
|
|
|
}
|
|
|
|
return true;
|
2013-10-27 18:17:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
\brief See \c process_simple_ho_match (case 1)
|
2013-10-27 18:17:03 +00:00
|
|
|
*/
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
bool is_simple_ho_match_closed(context const & ctx, expr const & a, expr const & b, bool is_lhs, unification_constraint const & c) {
|
|
|
|
return is_meta_app(a) && are_args_vars(ctx, a) && closed(b) &&
|
|
|
|
(is_eq(c) || (is_lhs && !is_actual_upper(b)) || (!is_lhs && !is_actual_lower(b)));
|
2013-10-27 18:17:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
\brief See \c process_simple_ho_match (case 2)
|
2013-10-27 18:17:03 +00:00
|
|
|
*/
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
bool is_simple_ho_match_app(context const & ctx, expr const & a, expr const & b, bool is_lhs, unification_constraint const & c) {
|
|
|
|
if (is_meta_app(a) && are_args_distinct_vars(ctx, a) && is_app(b) && !is_meta_app(b) &&
|
|
|
|
num_args(a) == num_args(b) &&
|
|
|
|
(is_eq(c) || (is_lhs && !is_actual_upper(b)) || (!is_lhs && !is_actual_lower(b)))) {
|
|
|
|
// check if a and b have the same arguments
|
|
|
|
for (unsigned i = 1; i < num_args(a); i++) {
|
|
|
|
if (arg(a, i) != arg(b, i))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// a is of the form (?m x1 ... xn) AND b is of the form (f x1 ... xn)
|
|
|
|
// f should not contain any xi
|
|
|
|
bool failed = false;
|
|
|
|
for_each(arg(b, 0), [&](expr const & e, unsigned offset) {
|
|
|
|
if (failed)
|
|
|
|
return false; // abort search
|
|
|
|
if (is_var(e)) {
|
|
|
|
unsigned vidx = var_idx(e);
|
|
|
|
if (vidx >= offset) {
|
|
|
|
vidx -= offset;
|
|
|
|
for (unsigned i = 1; i < num_args(a); i++) {
|
|
|
|
if (var_idx(arg(a, i)) == vidx) {
|
|
|
|
failed = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true; // continue search
|
|
|
|
});
|
|
|
|
return !failed;
|
|
|
|
} else {
|
|
|
|
return false;
|
2013-12-16 05:48:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-21 23:45:14 +00:00
|
|
|
/**
|
|
|
|
\brief Return true iff ctx |- a == b is a "simple" higher-order matching constraint. By simple, we mean
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
|
|
|
|
1) a constraint of the form
|
|
|
|
ctx |- (?m x1 ... xn) == c where c is closed
|
|
|
|
The constraint is solved by assigning ?m to (fun x1 ... xn, c).
|
|
|
|
The arguments may contain duplicate occurrences of variables.
|
|
|
|
|
|
|
|
|
|
|
|
2) a constraint of the form
|
|
|
|
ctx |- (?m x1 ... xn) == (f x1 ... xn) where f does not contain x1 ... xn, and x1 ... xn are distinct
|
|
|
|
The constraint is solved by assigning ?m to f OR ?m to (fun x1 ... xn, f x1 ... xn)
|
2013-10-21 23:45:14 +00:00
|
|
|
*/
|
|
|
|
bool process_simple_ho_match(context const & ctx, expr const & a, expr const & b, bool is_lhs, unification_constraint const & c) {
|
|
|
|
// Solve constraint of the form
|
|
|
|
// ctx |- (?m x) == c
|
|
|
|
// using imitation
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
bool eq_closed = is_simple_ho_match_closed(ctx, a, b, is_lhs, c);
|
|
|
|
bool eq_app = is_simple_ho_match_app(ctx, a, b, is_lhs, c);
|
|
|
|
if (eq_closed || eq_app) {
|
2013-10-21 23:45:14 +00:00
|
|
|
expr m = arg(a, 0);
|
2013-10-22 20:45:04 +00:00
|
|
|
buffer<expr> types;
|
2013-12-08 07:21:07 +00:00
|
|
|
for (unsigned i = 1; i < num_args(a); i++) {
|
2013-12-16 05:48:55 +00:00
|
|
|
optional<expr> d = try_get_type(ctx, arg(a, i));
|
2013-12-08 07:21:07 +00:00
|
|
|
if (d)
|
|
|
|
types.push_back(*d);
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
if (eq_closed) {
|
|
|
|
justification new_jst(new destruct_justification(c));
|
|
|
|
expr s = mk_lambda(types, b);
|
|
|
|
push_new_eq_constraint(ctx, m, s, new_jst);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
lean_assert(eq_app);
|
|
|
|
expr f = arg(b, 0);
|
|
|
|
std::unique_ptr<generic_case_split> new_cs(new generic_case_split(c, m_state));
|
|
|
|
{
|
|
|
|
// eta-expanded version: m = fun x1 ... xn, f x1 ... xn
|
|
|
|
unsigned n = types.size();
|
|
|
|
expr s = mk_lambda(types, mk_app_vars(lift_free_vars(f, 0, n), n));
|
|
|
|
state new_state(m_state);
|
|
|
|
justification new_assumption = mk_assumption();
|
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, m, s, new_assumption);
|
|
|
|
new_cs->push_back(new_state, new_assumption);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// m = f
|
|
|
|
state new_state(m_state);
|
|
|
|
justification new_assumption = mk_assumption();
|
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, m, f, new_assumption);
|
|
|
|
new_cs->push_back(new_state, new_assumption);
|
|
|
|
}
|
|
|
|
// add case split
|
|
|
|
bool r = new_cs->next(*this);
|
|
|
|
lean_assert(r);
|
|
|
|
m_case_splits.push_back(std::move(new_cs));
|
|
|
|
return r;
|
|
|
|
}
|
2013-10-21 23:45:14 +00:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-23 00:49:37 +00:00
|
|
|
/**
|
|
|
|
\brief Auxiliary method for \c process_meta_app. Add new case-splits to new_cs
|
|
|
|
*/
|
2013-12-15 07:23:57 +00:00
|
|
|
void process_meta_app_core(std::unique_ptr<generic_case_split> & new_cs, expr const & a, expr const & b, bool is_lhs,
|
|
|
|
unification_constraint const & c) {
|
2013-10-23 00:49:37 +00:00
|
|
|
lean_assert(is_meta_app(a));
|
|
|
|
context const & ctx = get_context(c);
|
|
|
|
metavar_env & menv = m_state.m_menv;
|
|
|
|
expr f_a = arg(a, 0);
|
|
|
|
lean_assert(is_metavar(f_a));
|
|
|
|
unsigned num_a = num_args(a);
|
|
|
|
buffer<expr> arg_types;
|
|
|
|
buffer<unification_constraint> ucs;
|
|
|
|
for (unsigned i = 1; i < num_a; i++) {
|
2013-12-13 01:47:11 +00:00
|
|
|
arg_types.push_back(m_type_inferer(arg(a, i), ctx, menv, ucs));
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(ucs);
|
2013-10-23 00:49:37 +00:00
|
|
|
}
|
|
|
|
// Add projections
|
|
|
|
for (unsigned i = 1; i < num_a; i++) {
|
|
|
|
// Assign f_a <- fun (x_1 : T_0) ... (x_{num_a-1} : T_{num_a-1}), x_i
|
|
|
|
state new_state(m_state);
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_assumption = mk_assumption();
|
2013-10-23 00:49:37 +00:00
|
|
|
expr proj = mk_lambda(arg_types, mk_var(num_a - i - 1));
|
|
|
|
expr new_a = arg(a, i);
|
|
|
|
expr new_b = b;
|
|
|
|
if (!is_lhs)
|
|
|
|
swap(new_a, new_b);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_constraint(new_state.m_active_cnstrs, is_eq(c), ctx, new_a, new_b, new_assumption);
|
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, f_a, proj, new_assumption);
|
2013-10-23 00:49:37 +00:00
|
|
|
new_cs->push_back(new_state, new_assumption);
|
|
|
|
}
|
|
|
|
// Add imitation
|
|
|
|
state new_state(m_state);
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_assumption = mk_assumption();
|
2013-10-23 00:49:37 +00:00
|
|
|
expr imitation;
|
2013-10-25 00:53:37 +00:00
|
|
|
lean_assert(arg_types.size() == num_a - 1);
|
2013-10-23 00:49:37 +00:00
|
|
|
if (is_app(b)) {
|
|
|
|
// Imitation for applications
|
|
|
|
expr f_b = arg(b, 0);
|
|
|
|
unsigned num_b = num_args(b);
|
2013-12-11 20:35:32 +00:00
|
|
|
// Assign f_a <- fun (x_1 : T_1) ... (x_{num_a} : T_{num_a}), f_b (h_1 x_1 ... x_{num_a}) ... (h_{num_b} x_1 ... x_{num_a})
|
|
|
|
// New constraints (h_i a_1 ... a_{num_a}) == arg(b, i)
|
2013-10-23 00:49:37 +00:00
|
|
|
buffer<expr> imitation_args; // arguments for the imitation
|
feat(library/elaborator): provide the metavar_env to instantiate and lift_free_vars in the elaborator, it will minimize the number of local_entries needed
The modifications started at commit 1852c86948dd7da8a2bac224e3c7c0ee309db224 made a big difference. For example, before these changes test tests/lean/implicit7.lean generated complicated constraints such as:
[x : Type; a : ?M::29[inst:1 ?M::0[lift:0:1]] x] ⊢ Pi B : Type, (Pi _ : x, (Pi _ : (?M::35[inst:0 #0, inst:1 #2, inst:2 #4, inst:3 #6, inst:5 #5, inst:6 #7, inst:7 #9, inst:9 #9, inst:10 #11, inst:13 ?M::0[lift:0:13]] x a B _), (?M::36[inst:1 #1, inst:2 #3, inst:3 #5, inst:4 #7, inst:6 #6, inst:7 #8, inst:8 #10, inst:10 #10, inst:11 #12, inst:14 ?M::0[lift:0:14]] x a B _ _))) ≈
?M::22 x a
After the changes, only very simple constraints are generated. The most complicated one is:
[] ⊢ Pi a : ?M::0, (Pi B : Type, (Pi _ : ?M::0, (Pi _ : B, ?M::0))) ≈ Pi x : ?M::17, ?M::18
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2013-12-14 23:55:12 +00:00
|
|
|
imitation_args.push_back(lift_free_vars(f_b, 0, num_a - 1, new_state.m_menv));
|
2013-10-23 00:49:37 +00:00
|
|
|
for (unsigned i = 1; i < num_b; i++) {
|
2013-12-17 18:56:20 +00:00
|
|
|
expr h_i = new_state.m_menv->mk_metavar(ctx);
|
|
|
|
imitation_args.push_back(mk_app_vars(add_lift(h_i, 0, num_a - 1), num_a - 1));
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, update_app(a, 0, h_i), arg(b, i), new_assumption);
|
2013-10-23 00:49:37 +00:00
|
|
|
}
|
2013-10-26 21:46:12 +00:00
|
|
|
imitation = mk_lambda(arg_types, mk_app(imitation_args));
|
2013-10-23 00:49:37 +00:00
|
|
|
} else if (is_abstraction(b)) {
|
|
|
|
// Imitation for lambdas and Pis
|
2013-12-11 20:35:32 +00:00
|
|
|
// Assign f_a <- fun (x_1 : T_1) ... (x_{num_a} : T_{num_a}),
|
|
|
|
// fun (x_b : (?h_1 x_1 ... x_{num_a})), (?h_2 x_1 ... x_{num_a} x_b)
|
|
|
|
// New constraints (h_1 a_1 ... a_{num_a}) == abst_domain(b)
|
|
|
|
// (h_2 a_1 ... a_{num_a} x_b) == abst_body(b)
|
2013-12-17 18:56:20 +00:00
|
|
|
expr h_1 = new_state.m_menv->mk_metavar(ctx);
|
2013-10-23 19:01:39 +00:00
|
|
|
context new_ctx = extend(ctx, abst_name(b), abst_domain(b));
|
2013-12-17 18:56:20 +00:00
|
|
|
expr h_2 = new_state.m_menv->mk_metavar(extend(ctx, abst_name(b), abst_domain(b)));
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, update_app(a, 0, h_1), abst_domain(b), new_assumption);
|
feat(library/elaborator): compensate the lack of eta-reduction (and eta-expanded normal forms) in the kernel normalizer
Before this commit, the elaborator was solving constraints of the form
ctx |- (?m x) == (f x)
as
?m <- (fun x : A, f x) where A is the domain of f.
In our kernel, the terms f and (fun x, f x) are not definitionally equal.
So, the solution above is not the only one. Another possible solution is
?m <- f
Depending of the circumstances we want ?m <- (fun x : A, f x) OR ?m <- f.
For example, when Lean is elaborating the eta-theorem in kernel.lean, the first solution should be used:
?m <- (fun x : A, f x)
When we are elaborating the axiom_of_choice theorem, we need to use the second one:
?m <- f
Of course, we can always provide the parameters explicitly and bypass the elaborator.
However, this goes against the idea that the elaborator can do mechanical steps for us.
This commit addresses this issue by creating a case-split
?m <- (fun x : A, f x)
OR
?m <- f
Another solution is to implement eta-expanded normal forms in the Kernel.
With this change, we were able to cleanup the following "hacks" in kernel.lean:
@eps_ax A (nonempty_ex_intro H) P w Hw
@axiom_of_choice A B P H
where we had to explicitly provided the implicit arguments
This commit also improves the imitation step for Pi-terms that are actually arrows.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2014-01-22 21:18:27 +00:00
|
|
|
if (is_arrow(b)) {
|
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, new_ctx,
|
|
|
|
update_app(lift_free_vars(a, 1), 0, h_2), abst_body(b), new_assumption);
|
|
|
|
imitation = mk_lambda(arg_types, update_abstraction(b, mk_app_vars(add_lift(h_1, 0, num_a - 1), num_a - 1), mk_app_vars(add_lift(h_2, 1, num_a - 1), num_a - 1, 1)));
|
|
|
|
} else {
|
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, new_ctx,
|
|
|
|
mk_app(update_app(lift_free_vars(a, 1), 0, h_2), mk_var(0)), abst_body(b), new_assumption);
|
|
|
|
imitation = mk_lambda(arg_types, update_abstraction(b, mk_app_vars(add_lift(h_1, 0, num_a - 1), num_a - 1), mk_app_vars(add_lift(h_2, 1, num_a - 1), num_a)));
|
|
|
|
}
|
2013-10-23 00:49:37 +00:00
|
|
|
} else {
|
|
|
|
// "Dumb imitation" aka the constant function
|
2013-12-11 20:35:32 +00:00
|
|
|
// Assign f_a <- fun (x_1 : T_1) ... (x_{num_a} : T_{num_a}), b
|
feat(library/elaborator): provide the metavar_env to instantiate and lift_free_vars in the elaborator, it will minimize the number of local_entries needed
The modifications started at commit 1852c86948dd7da8a2bac224e3c7c0ee309db224 made a big difference. For example, before these changes test tests/lean/implicit7.lean generated complicated constraints such as:
[x : Type; a : ?M::29[inst:1 ?M::0[lift:0:1]] x] ⊢ Pi B : Type, (Pi _ : x, (Pi _ : (?M::35[inst:0 #0, inst:1 #2, inst:2 #4, inst:3 #6, inst:5 #5, inst:6 #7, inst:7 #9, inst:9 #9, inst:10 #11, inst:13 ?M::0[lift:0:13]] x a B _), (?M::36[inst:1 #1, inst:2 #3, inst:3 #5, inst:4 #7, inst:6 #6, inst:7 #8, inst:8 #10, inst:10 #10, inst:11 #12, inst:14 ?M::0[lift:0:14]] x a B _ _))) ≈
?M::22 x a
After the changes, only very simple constraints are generated. The most complicated one is:
[] ⊢ Pi a : ?M::0, (Pi B : Type, (Pi _ : ?M::0, (Pi _ : B, ?M::0))) ≈ Pi x : ?M::17, ?M::18
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2013-12-14 23:55:12 +00:00
|
|
|
imitation = mk_lambda(arg_types, lift_free_vars(b, 0, num_a - 1, new_state.m_menv));
|
2013-10-23 00:49:37 +00:00
|
|
|
}
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, f_a, imitation, new_assumption);
|
2013-10-23 00:49:37 +00:00
|
|
|
new_cs->push_back(new_state, new_assumption);
|
|
|
|
}
|
|
|
|
|
2013-10-22 16:42:06 +00:00
|
|
|
/**
|
|
|
|
\brief Process a constraint <tt>ctx |- a = b</tt> where \c a is of the form <tt>(?m ...)</tt>.
|
|
|
|
We perform a "case split" using "projection" or "imitation". See Huet&Lang's paper on higher order matching
|
|
|
|
for further details.
|
|
|
|
*/
|
2013-12-16 17:38:57 +00:00
|
|
|
bool process_meta_app(expr const & a, expr const & b, bool is_lhs, unification_constraint const & c,
|
|
|
|
bool flex_flex = false, bool local_ctx = false) {
|
2013-12-15 07:23:57 +00:00
|
|
|
if (!is_meta_app(a))
|
2013-10-22 16:42:06 +00:00
|
|
|
return false;
|
2013-12-16 17:38:57 +00:00
|
|
|
if (!local_ctx && has_local_context(arg(a, 0)))
|
|
|
|
return false;
|
2013-12-15 07:23:57 +00:00
|
|
|
if (!flex_flex) {
|
|
|
|
if (is_meta_app(b))
|
|
|
|
return false;
|
|
|
|
if (is_abstraction(b) && is_meta_app(abst_domain(b)) && is_meta_app(abst_body(b)))
|
|
|
|
return false;
|
2013-10-22 16:42:06 +00:00
|
|
|
}
|
2013-12-15 07:23:57 +00:00
|
|
|
if (!(is_eq(c) || (is_lhs && !is_actual_upper(b)) || (!is_lhs && !is_actual_lower(b))))
|
|
|
|
return false;
|
|
|
|
std::unique_ptr<generic_case_split> new_cs(new generic_case_split(c, m_state));
|
|
|
|
process_meta_app_core(new_cs, a, b, is_lhs, c);
|
|
|
|
if (flex_flex && is_meta_app(b))
|
|
|
|
process_meta_app_core(new_cs, b, a, !is_lhs, c);
|
|
|
|
bool r = new_cs->next(*this);
|
|
|
|
lean_assert(r);
|
|
|
|
m_case_splits.push_back(std::move(new_cs));
|
|
|
|
return r;
|
2013-10-22 16:42:06 +00:00
|
|
|
}
|
|
|
|
|
2013-10-22 23:39:22 +00:00
|
|
|
/** \brief Return true if \c a is of the form ?m[inst:i t, ...] */
|
|
|
|
bool is_metavar_inst(expr const & a) const {
|
|
|
|
return is_metavar(a) && has_local_context(a) && head(metavar_lctx(a)).is_inst();
|
|
|
|
}
|
|
|
|
|
2013-10-23 19:01:39 +00:00
|
|
|
/** \brief Return true if \c a is of the form ?m[lift:s:n, ...] */
|
|
|
|
bool is_metavar_lift(expr const & a) const {
|
|
|
|
return is_metavar(a) && has_local_context(a) && head(metavar_lctx(a)).is_lift();
|
|
|
|
}
|
|
|
|
|
2013-10-22 23:39:22 +00:00
|
|
|
/**
|
2014-01-01 11:02:41 +00:00
|
|
|
\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}
|
2013-10-22 23:39:22 +00:00
|
|
|
*/
|
2014-01-01 11:02:41 +00:00
|
|
|
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);
|
|
|
|
}
|
2013-12-11 01:23:22 +00:00
|
|
|
|
2014-01-01 11:02:41 +00:00
|
|
|
/**
|
|
|
|
\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.
|
2013-10-22 23:39:22 +00:00
|
|
|
std::unique_ptr<generic_case_split> new_cs(new generic_case_split(c, m_state));
|
2014-01-01 11:02:41 +00:00
|
|
|
for (auto s : solutions) {
|
|
|
|
new_args[0] = s;
|
|
|
|
expr imitation = mk_app(new_args);
|
2013-10-22 23:39:22 +00:00
|
|
|
state new_state(m_state);
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_assumption = mk_assumption();
|
2014-01-01 11:02:41 +00:00
|
|
|
push_front(new_state.m_active_cnstrs, c);
|
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx_m, m, imitation, new_assumption);
|
2013-10-22 23:39:22 +00:00
|
|
|
new_cs->push_back(new_state, new_assumption);
|
|
|
|
}
|
2014-01-01 11:02:41 +00:00
|
|
|
lean_verify(new_cs->next(*this));
|
|
|
|
m_case_splits.push_back(std::move(new_cs));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\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",
|
2014-01-03 00:08:32 +00:00
|
|
|
Case 1) ?m[...] == #i and t == b (for constants, variables, values and Type)
|
2014-01-01 11:02:41 +00:00
|
|
|
Case 2) imitate b
|
|
|
|
*/
|
|
|
|
bool process_metavar_inst(expr const & a, expr const & b, bool is_lhs, unification_constraint const & c) {
|
2014-01-03 00:08:32 +00:00
|
|
|
// This method is miss some cases. In particular the local context of \c a contains metavariables.
|
|
|
|
//
|
|
|
|
// (f#2 #1) == ?M2[i:1 ?M1]
|
|
|
|
//
|
|
|
|
// A possible solution for this constraint is:
|
|
|
|
// ?M2 == #1
|
|
|
|
// ?M1 == f#2 #1
|
|
|
|
//
|
|
|
|
// TODO(Leo): consider the following alternative design: We do NOT use approximations, since it is quite
|
|
|
|
// hard to understand what happened when they do not work. Instead, we rely on user provided plugins
|
|
|
|
// for handling the nasty cases.
|
|
|
|
//
|
|
|
|
// TODO(Leo): another possible design is to inform the user where approximation was used.
|
2014-01-01 11:02:41 +00:00
|
|
|
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);
|
2014-01-03 00:08:32 +00:00
|
|
|
bool keep_c = local_context_has_metavar(metavar_lctx(a));
|
2014-01-01 11:02:41 +00:00
|
|
|
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;
|
2013-10-22 23:39:22 +00:00
|
|
|
} else {
|
2014-01-01 11:02:41 +00:00
|
|
|
// 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;
|
2013-10-22 23:39:22 +00:00
|
|
|
}
|
2014-01-01 11:02:41 +00:00
|
|
|
} 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;
|
2013-10-22 23:39:22 +00:00
|
|
|
}
|
|
|
|
}
|
2014-01-01 11:02:41 +00:00
|
|
|
return false;
|
2013-10-22 23:39:22 +00:00
|
|
|
}
|
|
|
|
|
2013-10-23 19:01:39 +00:00
|
|
|
/**
|
|
|
|
\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.
|
2014-01-01 11:02:41 +00:00
|
|
|
|
|
|
|
Remark: we can expand this method and support application and equality
|
2013-10-23 19:01:39 +00:00
|
|
|
*/
|
|
|
|
bool process_metavar_lift_abstraction(expr const & a, expr const & b, unification_constraint const & c) {
|
2013-12-12 03:41:24 +00:00
|
|
|
if (is_metavar_lift(a) && is_abstraction(b)) {
|
2014-01-01 11:02:41 +00:00
|
|
|
imitate_abstraction(a, b, c);
|
2013-10-23 19:01:39 +00:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-23 19:50:31 +00:00
|
|
|
/**
|
|
|
|
\brief Return true iff c is a constraint of the form <tt>ctx |- a << ?m</tt>, where \c a is Type or Bool
|
|
|
|
*/
|
|
|
|
bool is_lower(unification_constraint const & c) {
|
|
|
|
return
|
|
|
|
is_convertible(c) &&
|
2013-12-14 20:25:00 +00:00
|
|
|
(is_metavar(convertible_to(c)) || is_meta_app(convertible_to(c))) &&
|
2013-11-26 19:30:18 +00:00
|
|
|
(is_bool(convertible_from(c)) || is_type(convertible_from(c)));
|
2013-10-23 19:50:31 +00:00
|
|
|
}
|
|
|
|
|
2013-10-23 19:01:39 +00:00
|
|
|
/** \brief Process constraint of the form <tt>ctx |- a << ?m</tt>, where \c a is Type or Bool */
|
2013-10-22 20:45:04 +00:00
|
|
|
bool process_lower(expr const & a, expr const & b, unification_constraint const & c) {
|
2013-10-23 19:50:31 +00:00
|
|
|
if (is_lower(c)) {
|
2013-10-22 20:45:04 +00:00
|
|
|
// Remark: in principle, there are infinite number of choices.
|
|
|
|
// We approximate and only consider the most useful ones.
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new destruct_justification(c));
|
2013-11-26 19:30:18 +00:00
|
|
|
if (is_bool(a)) {
|
2014-01-17 02:06:25 +00:00
|
|
|
expr choices[3] = { Bool, Type(), TypeU };
|
|
|
|
push_active(mk_choice_constraint(get_context(c), b, 3, choices, new_jst));
|
2013-12-09 02:11:35 +00:00
|
|
|
return true;
|
2013-12-21 11:58:39 +00:00
|
|
|
} else if (m_env->is_ge(ty_level(a), m_U)) {
|
2014-01-17 02:06:25 +00:00
|
|
|
expr choices[2] = { a, Type(ty_level(a) + 1) };
|
|
|
|
push_active(mk_choice_constraint(get_context(c), b, 2, choices, new_jst));
|
2013-12-21 11:58:39 +00:00
|
|
|
return true;
|
2013-10-22 20:45:04 +00:00
|
|
|
} else {
|
2014-01-16 23:07:51 +00:00
|
|
|
expr choices[4] = { a, Type(ty_level(a) + 1), TypeU };
|
|
|
|
push_active(mk_choice_constraint(get_context(c), b, 4, choices, new_jst));
|
2013-12-09 02:11:35 +00:00
|
|
|
return true;
|
2013-10-22 20:45:04 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-23 19:50:31 +00:00
|
|
|
/**
|
|
|
|
\brief Return true if the current queue contains a constraint that satisfies the predicate p
|
|
|
|
*/
|
|
|
|
template<typename P>
|
|
|
|
bool has_constraint(P p) {
|
2013-12-15 07:23:57 +00:00
|
|
|
for (auto const & c : m_state.m_active_cnstrs) {
|
2013-10-23 19:50:31 +00:00
|
|
|
if (p(c))
|
|
|
|
return true;
|
|
|
|
}
|
2013-12-15 07:23:57 +00:00
|
|
|
for (auto const & c : m_state.m_delayed_cnstrs) {
|
|
|
|
if (p(c.first))
|
|
|
|
return true;
|
|
|
|
}
|
2013-10-23 19:50:31 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Return true iff the current queue has a max constraint of the form <tt>ctx |- max(L1, L2) == a</tt>.
|
|
|
|
|
|
|
|
\pre is_metavar(a)
|
|
|
|
*/
|
|
|
|
bool has_max_constraint(expr const & a) {
|
|
|
|
lean_assert(is_metavar(a));
|
|
|
|
return has_constraint([&](unification_constraint const & c) { return is_max(c) && max_rhs(c) == a; });
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Return true iff the current queue has a constraint that is a lower bound for \c a.
|
|
|
|
\pre is_metavar(a)
|
|
|
|
*/
|
|
|
|
bool has_lower(expr const & a) {
|
|
|
|
lean_assert(is_metavar(a));
|
|
|
|
return has_constraint([&](unification_constraint const & c) { return is_lower(c) && convertible_to(c) == a; });
|
|
|
|
}
|
|
|
|
|
2013-10-23 19:01:39 +00:00
|
|
|
/** \brief Process constraint of the form <tt>ctx |- ?m << b</tt>, where \c a is Type */
|
|
|
|
bool process_upper(expr const & a, expr const & b, unification_constraint const & c) {
|
2013-10-23 19:50:31 +00:00
|
|
|
if (is_convertible(c) && is_metavar(a) && is_type(b) && !has_max_constraint(a) && !has_lower(a)) {
|
2013-10-23 19:01:39 +00:00
|
|
|
// Remark: in principle, there are infinite number of choices.
|
|
|
|
// We approximate and only consider the most useful ones.
|
2013-10-23 19:50:31 +00:00
|
|
|
//
|
|
|
|
// Remark: we only consider \c a if the queue does not have a constraint
|
|
|
|
// of the form ctx |- max(L1, L2) == a.
|
|
|
|
// If it does, we don't need to guess. We wait \c a to be assigned
|
|
|
|
// and just check if the upper bound is satisfied.
|
|
|
|
//
|
|
|
|
// Remark: we also give preference to lower bounds
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new destruct_justification(c));
|
2013-10-23 19:01:39 +00:00
|
|
|
if (b == Type()) {
|
|
|
|
expr choices[2] = { Type(), Bool };
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(mk_choice_constraint(get_context(c), a, 2, choices, new_jst));
|
2013-12-09 02:11:35 +00:00
|
|
|
return true;
|
2013-10-23 19:01:39 +00:00
|
|
|
} else if (b == TypeU) {
|
2014-01-08 21:18:49 +00:00
|
|
|
expr choices[4] = { TypeU, Type(level() + 1), Type(), Bool };
|
|
|
|
push_active(mk_choice_constraint(get_context(c), a, 4, choices, new_jst));
|
2014-01-16 23:07:51 +00:00
|
|
|
return true;
|
2013-10-23 19:01:39 +00:00
|
|
|
} else {
|
|
|
|
level const & lvl = ty_level(b);
|
|
|
|
lean_assert(!lvl.is_bottom());
|
|
|
|
if (is_lift(lvl)) {
|
|
|
|
// If b is (Type L + k)
|
|
|
|
// make choices == { Type(L + k), Type(L + k - 1), ..., Type(L), Type, Bool }
|
|
|
|
buffer<expr> choices;
|
|
|
|
unsigned k = lift_offset(lvl);
|
|
|
|
level L = lift_of(lvl);
|
|
|
|
lean_assert(k > 0);
|
|
|
|
while (k > 0) {
|
|
|
|
choices.push_back(mk_type(L + k));
|
|
|
|
k--;
|
|
|
|
}
|
|
|
|
choices.push_back(mk_type(L));
|
|
|
|
if (!L.is_bottom())
|
|
|
|
choices.push_back(Type());
|
|
|
|
choices.push_back(Bool);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(mk_choice_constraint(get_context(c), a, choices.size(), choices.data(), new_jst));
|
2013-12-09 02:11:35 +00:00
|
|
|
return true;
|
2013-10-23 19:01:39 +00:00
|
|
|
} else if (is_uvar(lvl)) {
|
|
|
|
expr choices[4] = { Type(level() + 1), Type(), b, Bool };
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(mk_choice_constraint(get_context(c), a, 4, choices, new_jst));
|
2013-12-09 02:11:35 +00:00
|
|
|
return true;
|
2013-10-23 19:01:39 +00:00
|
|
|
} else {
|
|
|
|
lean_assert(is_max(lvl));
|
|
|
|
// TODO(Leo)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-20 09:33:25 +00:00
|
|
|
bool process_assigned_metavar(unification_constraint const & c, expr const & a, bool is_lhs) {
|
|
|
|
if (is_metavar(a) && is_assigned(a)) {
|
|
|
|
auto s_j = get_subst_jst(a);
|
|
|
|
justification new_jst(new substitution_justification(c, s_j.second));
|
|
|
|
push_updated_constraint(c, is_lhs, s_j.first, new_jst);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-08 20:10:46 +00:00
|
|
|
/**
|
|
|
|
\brief Resolve constraints of the form
|
|
|
|
|
|
|
|
ctx |- ?m << Pi(x : A, B) (param is_lhs is true)
|
|
|
|
and
|
|
|
|
ctx |- Pi(x : A, B) << ?m (param is_lhs is false)
|
|
|
|
|
|
|
|
where ?m is not assigned and does not have a local context.
|
|
|
|
|
|
|
|
We replace
|
|
|
|
ctx | ?m << Pi(x : A, B)
|
|
|
|
with
|
|
|
|
ctx |- ?m == Pi(x : A, ?m1)
|
|
|
|
ctx, x : A |- ?m1 << B
|
|
|
|
*/
|
|
|
|
void process_metavar_conv_pi(unification_constraint const & c, expr const & m, expr const & pi, bool is_lhs) {
|
|
|
|
lean_assert(!is_eq(c));
|
|
|
|
lean_assert(is_metavar(m) && !has_local_context(m));
|
|
|
|
lean_assert(!is_assigned(m));
|
|
|
|
lean_assert(is_pi(pi));
|
|
|
|
lean_assert(is_lhs || is_eqp(convertible_to(c), m));
|
|
|
|
lean_assert(!is_lhs || is_eqp(convertible_from(c), m));
|
|
|
|
context ctx = get_context(c);
|
|
|
|
context new_ctx = extend(ctx, abst_name(pi), abst_domain(pi));
|
|
|
|
expr m1 = m_state.m_menv->mk_metavar(new_ctx);
|
|
|
|
justification new_jst(new destruct_justification(c));
|
|
|
|
|
|
|
|
// Add ctx, x : A |- ?m1 << B when is_lhs == true,
|
|
|
|
// and ctx, x : A |- B << ?m1 when is_lhs == false
|
|
|
|
expr lhs = m1;
|
|
|
|
expr rhs = abst_body(pi);
|
|
|
|
if (!is_lhs)
|
|
|
|
swap(lhs, rhs);
|
|
|
|
push_new_constraint(false, new_ctx, lhs, rhs, new_jst);
|
|
|
|
|
|
|
|
// Add ctx |- ?m == Pi(x : A, ?m1)
|
|
|
|
push_new_eq_constraint(ctx, m, update_abstraction(pi, abst_domain(pi), m1), new_jst);
|
|
|
|
}
|
|
|
|
|
2014-01-23 02:03:08 +00:00
|
|
|
/**
|
|
|
|
\brief Return true iff \c a has not metavar and no free
|
|
|
|
variable that is assigned to a term containing metavariables in
|
|
|
|
ctx.
|
|
|
|
*/
|
|
|
|
bool has_no_metavar(context const & ctx, expr const & a) {
|
|
|
|
if (has_metavar(a))
|
|
|
|
return false;
|
|
|
|
bool found = false;
|
|
|
|
for_each(a, [&](expr const & e, unsigned offset) {
|
|
|
|
if (found) return false; // stop the search
|
|
|
|
if (is_var(e)) {
|
|
|
|
unsigned vidx = var_idx(e);
|
|
|
|
if (vidx >= offset) {
|
|
|
|
vidx -= offset;
|
|
|
|
auto entry = find(ctx, vidx);
|
|
|
|
if (entry && entry->get_body() && has_metavar(*entry->get_body())) {
|
|
|
|
found = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
return !found;
|
|
|
|
}
|
|
|
|
|
2013-10-16 20:26:31 +00:00
|
|
|
bool process_eq_convertible(context const & ctx, expr const & a, expr const & b, unification_constraint const & c) {
|
|
|
|
bool eq = is_eq(c);
|
2013-12-20 09:33:25 +00:00
|
|
|
if (a == b)
|
|
|
|
return true;
|
2014-01-23 02:03:08 +00:00
|
|
|
if (has_no_metavar(ctx, a) && has_no_metavar(ctx, b)) {
|
2014-01-09 22:06:23 +00:00
|
|
|
if (m_type_inferer.is_convertible(a, b, ctx)) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
m_conflict = mk_failure_justification(c);
|
|
|
|
return false;
|
2014-01-01 11:14:01 +00:00
|
|
|
}
|
|
|
|
}
|
2013-12-20 09:33:25 +00:00
|
|
|
|
|
|
|
if (process_assigned_metavar(c, a, true) || process_assigned_metavar(c, b, false))
|
2013-10-16 00:32:02 +00:00
|
|
|
return true;
|
|
|
|
|
2014-01-15 22:11:33 +00:00
|
|
|
if (m_assume_injectivity && is_app(a) && is_app(b) && num_args(a) == num_args(b) && arg(a, 0) == arg(b, 0) && !is_metavar(arg(a, 0))) {
|
2013-10-24 23:44:05 +00:00
|
|
|
// If m_assume_injectivity is true, we apply the following rule
|
|
|
|
// ctx |- (f a1 a2) == (f b1 b2)
|
|
|
|
// ===>
|
|
|
|
// ctx |- a1 == b1
|
|
|
|
// ctx |- a2 == b2
|
|
|
|
justification new_jst(new destruct_justification(c));
|
|
|
|
for (unsigned i = 1; i < num_args(a); i++)
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_eq_constraint(ctx, arg(a, i), arg(b, i), new_jst);
|
2013-10-24 23:44:05 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-16 20:26:31 +00:00
|
|
|
status r;
|
2013-11-29 17:14:54 +00:00
|
|
|
r = process_metavar(c, a, b, true);
|
2013-10-16 20:26:31 +00:00
|
|
|
if (r != Continue) { return r == Processed; }
|
2013-11-29 17:14:54 +00:00
|
|
|
r = process_metavar(c, b, a, false);
|
2013-10-16 20:26:31 +00:00
|
|
|
if (r != Continue) { return r == Processed; }
|
|
|
|
|
2014-01-16 10:05:09 +00:00
|
|
|
if (is_equality(a) && is_equality(b)) {
|
|
|
|
expr_pair p1 = get_equality_args(a);
|
|
|
|
expr_pair p2 = get_equality_args(b);
|
2014-01-02 05:32:07 +00:00
|
|
|
justification new_jst(new destruct_justification(c));
|
2014-01-16 23:07:51 +00:00
|
|
|
if (is_eq(a) && is_eq(b))
|
|
|
|
push_new_eq_constraint(ctx, arg(a, 1), arg(b, 1), new_jst);
|
2014-01-02 05:32:07 +00:00
|
|
|
push_new_eq_constraint(ctx, p1.first, p2.first, new_jst);
|
|
|
|
push_new_eq_constraint(ctx, p1.second, p2.second, new_jst);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a.kind() == b.kind()) {
|
|
|
|
switch (a.kind()) {
|
|
|
|
case expr_kind::Pi: {
|
|
|
|
justification new_jst(new destruct_justification(c));
|
|
|
|
push_new_eq_constraint(ctx, abst_domain(a), abst_domain(b), new_jst);
|
|
|
|
context new_ctx = extend(ctx, abst_name(a), abst_domain(a));
|
|
|
|
push_new_constraint(eq, new_ctx, abst_body(a), abst_body(b), new_jst);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case expr_kind::Lambda: {
|
|
|
|
justification new_jst(new destruct_justification(c));
|
|
|
|
push_new_eq_constraint(ctx, abst_domain(a), abst_domain(b), new_jst);
|
|
|
|
context new_ctx = extend(ctx, abst_name(a), abst_domain(a));
|
|
|
|
push_new_eq_constraint(new_ctx, abst_body(a), abst_body(b), new_jst);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-01 22:46:19 +00:00
|
|
|
if (!is_meta_app(a) && !is_meta_app(b) && normalize_head(a, b, c)) { return true; }
|
2013-10-16 20:26:31 +00:00
|
|
|
|
2013-10-25 18:19:17 +00:00
|
|
|
if (!eq) {
|
|
|
|
// Try to assign convertability constraints.
|
2014-01-08 20:10:46 +00:00
|
|
|
if (is_metavar(a) && !is_assigned(a) && !has_local_context(a)) {
|
|
|
|
if (is_pi(b)) {
|
|
|
|
process_metavar_conv_pi(c, a, b, true);
|
|
|
|
return true;
|
|
|
|
} else if (!is_type(b) && !is_meta(b)) {
|
|
|
|
// We can assign a <- b at this point IF b is not (Type lvl) or Metavariable
|
|
|
|
lean_assert(!has_metavar(b, a));
|
|
|
|
return assign(a, b, c, true);
|
|
|
|
}
|
2013-10-25 18:19:17 +00:00
|
|
|
}
|
2014-01-08 20:10:46 +00:00
|
|
|
|
|
|
|
if (is_metavar(b) && !is_assigned(b) && !has_local_context(b)) {
|
|
|
|
if (is_pi(a)) {
|
|
|
|
process_metavar_conv_pi(c, b, a, false);
|
|
|
|
return true;
|
|
|
|
} else if (!is_type(a) && !is_meta(a) && a != Bool) {
|
|
|
|
// We can assign b <- a at this point IF a is not (Type lvl) or Metavariable or Bool.
|
|
|
|
lean_assert(!has_metavar(a, b));
|
|
|
|
return assign(b, a, c, false);
|
|
|
|
}
|
2013-10-25 18:19:17 +00:00
|
|
|
}
|
|
|
|
}
|
2013-10-16 20:26:31 +00:00
|
|
|
|
2013-10-21 23:45:14 +00:00
|
|
|
if (process_simple_ho_match(ctx, a, b, true, c) ||
|
|
|
|
process_simple_ho_match(ctx, b, a, false, c))
|
|
|
|
return true;
|
|
|
|
|
2013-11-26 19:30:18 +00:00
|
|
|
if (!eq && is_bool(a) && is_type(b))
|
2013-10-22 20:45:04 +00:00
|
|
|
return true;
|
|
|
|
|
2013-10-16 20:26:31 +00:00
|
|
|
if (a.kind() == b.kind()) {
|
|
|
|
switch (a.kind()) {
|
|
|
|
case expr_kind::Constant: case expr_kind::Var: case expr_kind::Value:
|
2013-10-21 23:45:14 +00:00
|
|
|
if (a == b) {
|
|
|
|
return true;
|
|
|
|
} else {
|
2014-01-04 01:13:10 +00:00
|
|
|
m_conflict = mk_failure_justification(c);
|
2013-10-21 23:45:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
2013-10-16 20:26:31 +00:00
|
|
|
case expr_kind::Type:
|
2013-12-13 00:33:31 +00:00
|
|
|
if ((!eq && m_env->is_ge(ty_level(b), ty_level(a))) || (eq && a == b)) {
|
2013-10-16 20:26:31 +00:00
|
|
|
return true;
|
|
|
|
} else {
|
2014-01-04 01:13:10 +00:00
|
|
|
m_conflict = mk_failure_justification(c);
|
2013-10-16 20:26:31 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
case expr_kind::App:
|
|
|
|
if (!is_meta_app(a) && !is_meta_app(b)) {
|
|
|
|
if (num_args(a) == num_args(b)) {
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new destruct_justification(c));
|
2013-10-16 20:26:31 +00:00
|
|
|
for (unsigned i = 0; i < num_args(a); i++)
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_eq_constraint(ctx, arg(a, i), arg(b, i), new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
return true;
|
|
|
|
} else {
|
2014-01-04 01:13:10 +00:00
|
|
|
m_conflict = mk_failure_justification(c);
|
2013-10-16 20:26:31 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case expr_kind::Let:
|
2013-11-11 17:19:38 +00:00
|
|
|
lean_unreachable(); // LCOV_EXCL_LINE
|
2013-10-16 20:26:31 +00:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-21 23:45:14 +00:00
|
|
|
if (instantiate_metavars(true, a, c) ||
|
|
|
|
instantiate_metavars(false, b, c)) {
|
|
|
|
return true;
|
|
|
|
}
|
2013-10-16 20:26:31 +00:00
|
|
|
|
2013-12-06 21:04:26 +00:00
|
|
|
// If 'a' and 'b' have different kinds, and 'a' and 'b' are not metavariables,
|
|
|
|
// and 'a' and 'b' are not applications where the function contains metavariables,
|
|
|
|
// then it is not possible to unify 'a' and 'b'.
|
|
|
|
// We need the last condition because if 'a'/'b' are applications containing metavariables,
|
|
|
|
// then they can be reduced when the metavariable is assigned
|
|
|
|
// Here is an example:
|
|
|
|
// |- (?m Type) << Type
|
|
|
|
// If ?m is assigned to the identity function (fun x, x), then the constraint can be solved.
|
2013-12-15 07:23:57 +00:00
|
|
|
if (a.kind() != b.kind() && !is_metavar(a) && !is_metavar(b) &&
|
|
|
|
!(is_app(a) && has_metavar(arg(a, 0))) && !(is_app(b) && has_metavar(arg(b, 0)))) {
|
|
|
|
// std::cout << "CONFLICT: "; display(std::cout, c); std::cout << "\n";
|
2014-01-04 01:13:10 +00:00
|
|
|
m_conflict = mk_failure_justification(c);
|
2013-10-22 20:45:04 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-12-15 07:23:57 +00:00
|
|
|
push_delayed(c);
|
2013-10-16 00:32:02 +00:00
|
|
|
return true;
|
2013-10-16 20:26:31 +00:00
|
|
|
}
|
2013-10-16 00:32:02 +00:00
|
|
|
|
2013-10-23 19:01:39 +00:00
|
|
|
bool process_max(unification_constraint const & c) {
|
2013-10-25 02:27:53 +00:00
|
|
|
expr lhs1 = max_lhs1(c);
|
|
|
|
expr lhs2 = max_lhs2(c);
|
2013-10-23 19:01:39 +00:00
|
|
|
expr const & rhs = max_rhs(c);
|
2013-10-23 20:42:14 +00:00
|
|
|
buffer<justification> jsts;
|
2013-10-23 19:01:39 +00:00
|
|
|
bool modified = false;
|
|
|
|
expr new_lhs1 = lhs1;
|
|
|
|
expr new_lhs2 = lhs2;
|
|
|
|
expr new_rhs = rhs;
|
|
|
|
if (has_assigned_metavar(lhs1)) {
|
2013-10-23 20:42:14 +00:00
|
|
|
new_lhs1 = instantiate_metavars(lhs1, jsts);
|
2013-10-23 19:01:39 +00:00
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
if (has_assigned_metavar(lhs2)) {
|
2013-10-23 20:42:14 +00:00
|
|
|
new_lhs2 = instantiate_metavars(lhs2, jsts);
|
2013-10-23 19:01:39 +00:00
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
if (has_assigned_metavar(rhs)) {
|
2013-10-23 20:42:14 +00:00
|
|
|
new_rhs = instantiate_metavars(rhs, jsts);
|
2013-10-23 19:01:39 +00:00
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
if (modified) {
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst = mk_subst_justification(c, jsts);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(mk_max_constraint(get_context(c), new_lhs1, new_lhs2, new_rhs, new_jst));
|
2013-10-23 19:01:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-01-08 08:38:39 +00:00
|
|
|
if (is_bool(lhs2)) {
|
|
|
|
justification new_jst(new normalize_justification(c));
|
|
|
|
push_new_eq_constraint(get_context(c), lhs2, rhs, new_jst);
|
|
|
|
return true;
|
|
|
|
}
|
2013-10-23 19:01:39 +00:00
|
|
|
if (!is_metavar(lhs1) && !is_type(lhs1)) {
|
|
|
|
new_lhs1 = normalize(get_context(c), lhs1);
|
|
|
|
modified = (lhs1 != new_lhs1);
|
|
|
|
}
|
|
|
|
if (!is_metavar(lhs2) && !is_type(lhs2)) {
|
|
|
|
new_lhs2 = normalize(get_context(c), lhs2);
|
|
|
|
modified = (lhs2 != new_lhs2);
|
|
|
|
}
|
|
|
|
if (!is_metavar(rhs) && !is_type(rhs)) {
|
|
|
|
new_rhs = normalize(get_context(c), rhs);
|
|
|
|
modified = (rhs != new_rhs);
|
|
|
|
}
|
|
|
|
if (modified) {
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new normalize_justification(c));
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(mk_max_constraint(get_context(c), new_lhs1, new_lhs2, new_rhs, new_jst));
|
2013-10-23 19:01:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
2013-11-26 19:30:18 +00:00
|
|
|
if (is_bool(lhs1))
|
2013-10-25 02:27:53 +00:00
|
|
|
lhs1 = Type();
|
2013-10-23 19:01:39 +00:00
|
|
|
if (is_type(lhs1) && is_type(lhs2)) {
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new normalize_justification(c));
|
2013-10-23 19:01:39 +00:00
|
|
|
expr new_lhs = mk_type(max(ty_level(lhs1), ty_level(lhs2)));
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_eq_constraint(get_context(c), new_lhs, rhs, new_jst);
|
2013-10-23 19:01:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (lhs1 == rhs) {
|
|
|
|
// ctx |- max(lhs1, lhs2) == rhs
|
|
|
|
// ==> IF lhs1 = rhs
|
|
|
|
// ctx |- lhs2 << rhs
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new normalize_justification(c));
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(mk_convertible_constraint(get_context(c), lhs2, rhs, new_jst));
|
2013-10-23 19:01:39 +00:00
|
|
|
return true;
|
2014-01-08 20:10:46 +00:00
|
|
|
} else if (lhs2 == rhs && is_type(lhs2)) {
|
|
|
|
// ctx |- max(lhs1, lhs2) == rhs IF lhs2 is a Type
|
2013-10-23 19:01:39 +00:00
|
|
|
// ==> IF lhs1 = rhs
|
|
|
|
// ctx |- lhs2 << rhs
|
2014-01-08 20:10:46 +00:00
|
|
|
|
|
|
|
// Remark: this rule is not applicable when lhs2 == Bool.
|
|
|
|
// Recall that max is actually a constraint generated for a Pi(x : A, B)
|
|
|
|
// where lhs1 and lhs2 represent the types of A and B.
|
|
|
|
// If lhs2 == Bool, the type of Pi(x : A, B) is Bool, and the type
|
|
|
|
// of A (lhs1) can be as big as we want
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new normalize_justification(c));
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(mk_convertible_constraint(get_context(c), lhs1, rhs, new_jst));
|
2013-10-23 19:01:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((!has_metavar(lhs1) && !is_type(lhs1)) ||
|
|
|
|
(!has_metavar(lhs2) && !is_type(lhs2))) {
|
2014-01-04 01:13:10 +00:00
|
|
|
m_conflict = mk_failure_justification(c);
|
2013-10-23 19:01:39 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// std::cout << "Postponed: "; display(std::cout, c);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_delayed(c);
|
2013-10-16 20:26:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
2013-10-16 00:32:02 +00:00
|
|
|
|
2013-10-21 23:45:14 +00:00
|
|
|
bool process_choice(unification_constraint const & c) {
|
|
|
|
std::unique_ptr<case_split> new_cs(new choice_case_split(c, m_state));
|
|
|
|
bool r = new_cs->next(*this);
|
|
|
|
lean_assert(r);
|
|
|
|
m_case_splits.push_back(std::move(new_cs));
|
|
|
|
return r;
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void resolve_conflict() {
|
|
|
|
lean_assert(m_conflict);
|
2013-10-21 23:45:14 +00:00
|
|
|
|
2013-10-24 22:14:29 +00:00
|
|
|
// std::cout << "Resolve conflict, num case_splits: " << m_case_splits.size() << "\n";
|
|
|
|
// formatter fmt = mk_simple_formatter();
|
|
|
|
// std::cout << m_conflict.pp(fmt, options(), nullptr, true) << "\n";
|
2013-10-21 23:45:14 +00:00
|
|
|
|
2013-10-16 00:32:02 +00:00
|
|
|
while (!m_case_splits.empty()) {
|
|
|
|
std::unique_ptr<case_split> & d = m_case_splits.back();
|
2013-10-22 23:39:22 +00:00
|
|
|
// std::cout << "Assumption " << d->m_curr_assumption.pp(fmt, options(), nullptr, true) << "\n";
|
2013-10-16 00:32:02 +00:00
|
|
|
if (depends_on(m_conflict, d->m_curr_assumption)) {
|
2013-10-23 20:42:14 +00:00
|
|
|
d->m_failed_justifications.push_back(m_conflict);
|
2013-10-16 00:32:02 +00:00
|
|
|
if (d->next(*this)) {
|
2013-10-23 20:42:14 +00:00
|
|
|
m_conflict = justification();
|
2013-10-16 00:32:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_case_splits.pop_back();
|
|
|
|
}
|
|
|
|
throw elaborator_exception(m_conflict);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool next_choice_case(choice_case_split & s) {
|
|
|
|
unification_constraint & choice = s.m_choice;
|
|
|
|
unsigned idx = s.m_idx;
|
|
|
|
if (idx < choice_size(choice)) {
|
|
|
|
s.m_idx++;
|
|
|
|
s.m_curr_assumption = mk_assumption();
|
|
|
|
m_state = s.m_prev_state;
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_eq_constraint(get_context(choice), choice_mvar(choice), choice_ith(choice, idx), s.m_curr_assumption);
|
2013-10-16 00:32:02 +00:00
|
|
|
return true;
|
|
|
|
} else {
|
2013-12-15 07:23:57 +00:00
|
|
|
m_conflict = justification(new unification_failure_by_cases_justification(choice, s.m_failed_justifications.size(),
|
2014-01-04 01:13:10 +00:00
|
|
|
s.m_failed_justifications.data(),
|
|
|
|
s.m_prev_state.m_menv));
|
2013-10-16 00:32:02 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-22 20:45:04 +00:00
|
|
|
bool next_generic_case(generic_case_split & s) {
|
2013-10-22 16:42:06 +00:00
|
|
|
unsigned idx = s.m_idx;
|
|
|
|
unsigned sz = s.m_states.size();
|
|
|
|
if (idx < sz) {
|
|
|
|
s.m_idx++;
|
|
|
|
s.m_curr_assumption = s.m_assumptions[sz - idx - 1];
|
|
|
|
m_state = s.m_states[sz - idx - 1];
|
|
|
|
return true;
|
|
|
|
} else {
|
2013-12-15 07:23:57 +00:00
|
|
|
m_conflict = justification(new unification_failure_by_cases_justification(s.m_constraint, s.m_failed_justifications.size(),
|
2014-01-04 01:13:10 +00:00
|
|
|
s.m_failed_justifications.data(),
|
|
|
|
s.m_prev_state.m_menv));
|
2013-10-22 16:42:06 +00:00
|
|
|
return false;
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool next_plugin_case(plugin_case_split & s) {
|
|
|
|
try {
|
|
|
|
s.m_curr_assumption = mk_assumption();
|
|
|
|
std::pair<metavar_env, list<unification_constraint>> r = s.m_alternatives->next(s.m_curr_assumption);
|
2013-12-15 07:23:57 +00:00
|
|
|
m_state.m_active_cnstrs = s.m_prev_state.m_active_cnstrs;
|
|
|
|
m_state.m_delayed_cnstrs = s.m_prev_state.m_delayed_cnstrs;
|
|
|
|
m_state.m_recently_assigned = s.m_prev_state.m_recently_assigned;
|
|
|
|
m_state.m_menv = r.first;
|
2013-10-16 00:32:02 +00:00
|
|
|
for (auto c : r.second) {
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(c);
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch (exception & ex) {
|
2013-12-15 07:23:57 +00:00
|
|
|
m_conflict = justification(new unification_failure_by_cases_justification(s.m_constraint, s.m_failed_justifications.size(),
|
2014-01-04 01:13:10 +00:00
|
|
|
s.m_failed_justifications.data(),
|
|
|
|
s.m_prev_state.m_menv));
|
2013-10-16 00:32:02 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-15 07:23:57 +00:00
|
|
|
bool process_delayed() {
|
|
|
|
name_set const & recently_assigned = m_state.m_recently_assigned;
|
|
|
|
m_state.m_delayed_cnstrs =
|
|
|
|
filter(m_state.m_delayed_cnstrs,
|
|
|
|
[&](std::pair<unification_constraint, name_list> const & p) {
|
|
|
|
bool found = false;
|
|
|
|
for (auto const & m : p.second) {
|
|
|
|
if (recently_assigned.contains(m)) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found) {
|
|
|
|
push_active(p.first);
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
m_state.m_recently_assigned = name_set(); // reset
|
|
|
|
lean_assert(m_state.m_recently_assigned.empty());
|
|
|
|
if (!empty(m_state.m_active_cnstrs))
|
|
|
|
return true;
|
|
|
|
// second pass trying to apply process_meta_app
|
|
|
|
m_state.m_delayed_cnstrs =
|
|
|
|
remove_last(m_state.m_delayed_cnstrs,
|
|
|
|
[&](std::pair<unification_constraint, name_list> const & p) {
|
|
|
|
unification_constraint const & c = p.first;
|
|
|
|
if (is_eq(c) || is_convertible(c)) {
|
|
|
|
expr const & a = is_eq(c) ? eq_lhs(c) : convertible_from(c);
|
|
|
|
expr const & b = is_eq(c) ? eq_rhs(c) : convertible_to(c);
|
|
|
|
if ((process_meta_app(a, b, true, c) || process_meta_app(b, a, false, c))) {
|
|
|
|
// std::cout << "META_APP: "; display(std::cout, c); std::cout << "\n";
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
if (!empty(m_state.m_active_cnstrs))
|
|
|
|
return true;
|
|
|
|
// final pass trying expensive constraints
|
|
|
|
m_state.m_delayed_cnstrs =
|
|
|
|
remove_last(m_state.m_delayed_cnstrs,
|
|
|
|
[&](std::pair<unification_constraint, name_list> const & p) {
|
|
|
|
unification_constraint const & c = p.first;
|
|
|
|
if (is_eq(c) || is_convertible(c)) {
|
|
|
|
// std::cout << "EXPENSIVE: "; display(std::cout, c); std::cout << "\n";
|
|
|
|
expr const & a = is_eq(c) ? eq_lhs(c) : convertible_from(c);
|
|
|
|
expr const & b = is_eq(c) ? eq_rhs(c) : convertible_to(c);
|
|
|
|
if (process_lower(a, b, c) ||
|
|
|
|
process_upper(a, b, c) ||
|
|
|
|
process_metavar_inst(a, b, true, c) ||
|
|
|
|
process_metavar_inst(b, a, false, c) ||
|
|
|
|
process_metavar_lift_abstraction(a, b, c) ||
|
|
|
|
process_metavar_lift_abstraction(b, a, c) ||
|
2013-12-16 17:38:57 +00:00
|
|
|
process_meta_app(a, b, true, c, false, true) ||
|
|
|
|
process_meta_app(b, a, false, c, false, true) ||
|
2013-12-15 07:23:57 +00:00
|
|
|
process_meta_app(a, b, true, c, true)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
if (!empty(m_state.m_active_cnstrs))
|
|
|
|
return true;
|
|
|
|
// "approximated mode"
|
|
|
|
// change convertability into equality constraint
|
|
|
|
m_state.m_delayed_cnstrs =
|
|
|
|
remove_last(m_state.m_delayed_cnstrs,
|
|
|
|
[&](std::pair<unification_constraint, name_list> const & p) {
|
|
|
|
if (is_convertible(p.first)) {
|
|
|
|
unification_constraint const & c = p.first;
|
|
|
|
// std::cout << "CONVERTABILITY: "; display(std::cout, c); std::cout << "\n";
|
|
|
|
push_new_eq_constraint(get_context(c), convertible_from(c), convertible_to(c), get_justification(c));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
return !empty(m_state.m_active_cnstrs);
|
|
|
|
}
|
|
|
|
|
2013-10-03 22:02:07 +00:00
|
|
|
public:
|
2013-12-13 00:33:31 +00:00
|
|
|
imp(ro_environment const & env, metavar_env const & menv, unsigned num_cnstrs, unification_constraint const * cnstrs,
|
2013-12-10 23:55:46 +00:00
|
|
|
options const & opts, std::shared_ptr<elaborator_plugin> const & p):
|
2013-10-03 22:02:07 +00:00
|
|
|
m_env(env),
|
2013-10-18 16:58:12 +00:00
|
|
|
m_type_inferer(env),
|
2013-10-16 20:26:31 +00:00
|
|
|
m_normalizer(env),
|
2013-10-16 00:32:02 +00:00
|
|
|
m_state(menv, num_cnstrs, cnstrs),
|
2013-10-03 22:02:07 +00:00
|
|
|
m_plugin(p) {
|
2013-10-16 00:32:02 +00:00
|
|
|
set_options(opts);
|
|
|
|
m_next_id = 0;
|
2013-10-23 00:49:37 +00:00
|
|
|
m_first = true;
|
2013-12-21 11:58:39 +00:00
|
|
|
m_U = m_env->get_uvar("U");
|
2013-10-24 22:14:29 +00:00
|
|
|
// display(std::cout);
|
2013-10-03 22:02:07 +00:00
|
|
|
}
|
|
|
|
|
2013-10-27 18:02:29 +00:00
|
|
|
metavar_env next() {
|
2013-11-13 05:42:22 +00:00
|
|
|
check_interrupted();
|
2013-10-16 00:32:02 +00:00
|
|
|
if (m_conflict)
|
|
|
|
throw elaborator_exception(m_conflict);
|
|
|
|
if (!m_case_splits.empty()) {
|
2013-10-23 20:42:14 +00:00
|
|
|
buffer<justification> assumptions;
|
2013-10-16 00:32:02 +00:00
|
|
|
for (std::unique_ptr<case_split> const & cs : m_case_splits)
|
|
|
|
assumptions.push_back(cs->m_curr_assumption);
|
2013-10-23 20:42:14 +00:00
|
|
|
m_conflict = justification(new next_solution_justification(assumptions.size(), assumptions.data()));
|
2013-10-16 00:32:02 +00:00
|
|
|
resolve_conflict();
|
2013-10-23 00:49:37 +00:00
|
|
|
} else if (m_first) {
|
|
|
|
m_first = false;
|
|
|
|
} else {
|
|
|
|
// this is not the first run, and there are no case-splits
|
2013-10-23 20:42:14 +00:00
|
|
|
m_conflict = justification(new next_solution_justification(0, nullptr));
|
2013-10-23 00:49:37 +00:00
|
|
|
throw elaborator_exception(m_conflict);
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
while (true) {
|
2013-11-13 05:42:22 +00:00
|
|
|
check_interrupted();
|
2013-12-15 07:23:57 +00:00
|
|
|
cnstr_list & active_cnstrs = m_state.m_active_cnstrs;
|
|
|
|
if (!empty(active_cnstrs)) {
|
|
|
|
unification_constraint c = head(active_cnstrs);
|
|
|
|
m_state.m_active_cnstrs = tail(active_cnstrs);
|
|
|
|
// std::cout << "Processing, depth: " << m_case_splits.size() << " "; display(std::cout, c);
|
2013-10-16 00:32:02 +00:00
|
|
|
if (!process(c)) {
|
|
|
|
resolve_conflict();
|
|
|
|
}
|
2013-12-15 07:23:57 +00:00
|
|
|
} else if (!empty(m_state.m_delayed_cnstrs)) {
|
|
|
|
// std::cout << "PROCESSING DELAYED\n"; display(std::cout); std::cout << "\n\n";
|
|
|
|
if (!process_delayed()) {
|
|
|
|
// std::cout << "FAILED to solve\n";
|
|
|
|
// display(std::cout);
|
|
|
|
return m_state.m_menv;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return m_state.m_menv;
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
}
|
2013-10-03 22:02:07 +00:00
|
|
|
}
|
|
|
|
|
2013-10-16 20:26:31 +00:00
|
|
|
void display(std::ostream & out, unification_constraint const & c) const {
|
|
|
|
formatter fmt = mk_simple_formatter();
|
|
|
|
out << c.pp(fmt, options(), nullptr, false) << "\n";
|
2013-10-16 00:32:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void display(std::ostream & out) const {
|
2013-12-13 01:47:11 +00:00
|
|
|
m_state.m_menv->for_each_subst([&](name const & m, expr const & e) {
|
2013-10-16 00:32:02 +00:00
|
|
|
out << m << " <- " << e << "\n";
|
|
|
|
});
|
2013-12-15 07:23:57 +00:00
|
|
|
for (auto c : m_state.m_active_cnstrs)
|
2013-10-16 20:26:31 +00:00
|
|
|
display(out, c);
|
2013-12-15 07:23:57 +00:00
|
|
|
out << "Delayed constraints:\n";
|
|
|
|
for (auto c : m_state.m_delayed_cnstrs)
|
|
|
|
display(out, c.first);
|
2013-10-03 22:02:07 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-12-13 00:33:31 +00:00
|
|
|
elaborator::elaborator(ro_environment const & env,
|
2013-10-03 22:02:07 +00:00
|
|
|
metavar_env const & menv,
|
|
|
|
unsigned num_cnstrs,
|
|
|
|
unification_constraint const * cnstrs,
|
2013-10-16 00:32:02 +00:00
|
|
|
options const & opts,
|
2013-10-03 22:02:07 +00:00
|
|
|
std::shared_ptr<elaborator_plugin> const & p):
|
2013-12-10 23:55:46 +00:00
|
|
|
m_ptr(new imp(env, menv, num_cnstrs, cnstrs, opts, p)) {
|
2013-10-03 22:02:07 +00:00
|
|
|
}
|
|
|
|
|
2013-12-13 00:33:31 +00:00
|
|
|
elaborator::elaborator(ro_environment const & env,
|
2013-10-03 22:02:07 +00:00
|
|
|
metavar_env const & menv,
|
|
|
|
context const & ctx, expr const & lhs, expr const & rhs):
|
2013-10-23 20:42:14 +00:00
|
|
|
elaborator(env, menv, { mk_eq_constraint(ctx, lhs, rhs, justification()) }) {
|
2013-10-03 22:02:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
elaborator::~elaborator() {
|
|
|
|
}
|
|
|
|
|
2013-10-27 18:02:29 +00:00
|
|
|
metavar_env elaborator::next() {
|
2013-10-03 22:02:07 +00:00
|
|
|
return m_ptr->next();
|
|
|
|
}
|
|
|
|
}
|