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"
|
2013-10-16 20:26:31 +00:00
|
|
|
#include "kernel/builtin.h"
|
2013-12-18 00:35:39 +00:00
|
|
|
#include "kernel/update_expr.h"
|
2013-10-18 16:58:12 +00:00
|
|
|
#include "library/type_inferer.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
|
|
|
|
level m_M; // universe M
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Collect metavars in \c c */
|
|
|
|
name_list collect_mvars(unification_constraint const & c) {
|
|
|
|
buffer<name> r;
|
|
|
|
auto f = [&](expr const & m, unsigned) {
|
|
|
|
if (is_metavar(m))
|
|
|
|
r.push_back(metavar_name(m));
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
for_each_fn<decltype(f)> proc(f);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
std::sort(r.begin(), r.end(), name_quick_cmp());
|
|
|
|
auto new_end = std::unique(r.begin(), r.end());
|
|
|
|
r.resize(r.size() + r.end() - new_end);
|
|
|
|
return to_list(r.begin(), r.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-10-22 16:42:06 +00:00
|
|
|
\brief Return (f x_{num_vars - 1} ... x_0)
|
|
|
|
*/
|
|
|
|
expr mk_app_vars(expr const & f, unsigned num_vars) {
|
|
|
|
buffer<expr> args;
|
|
|
|
args.push_back(f);
|
|
|
|
unsigned i = num_vars;
|
|
|
|
while (i > 0) {
|
|
|
|
--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
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\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)) {
|
|
|
|
m_conflict = justification(new unification_failure_justification(c));
|
|
|
|
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)) {
|
2013-10-23 20:42:14 +00:00
|
|
|
m_conflict = justification(new unification_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)) {
|
|
|
|
m_conflict = justification(new unification_failure_justification(c));
|
|
|
|
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()) {
|
2013-12-12 00:11:17 +00:00
|
|
|
if (!has_free_var(b, me.s(), me.s() + me.n(), m_state.m_menv)) {
|
2013-10-22 23:39:22 +00:00
|
|
|
// Case 3
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new normalize_justification(c));
|
2013-10-22 23:39:22 +00:00
|
|
|
expr new_a = pop_meta_context(a);
|
2013-12-12 00:11:17 +00:00
|
|
|
expr new_b = lower_free_vars(b, me.s() + me.n(), me.n(), m_state.m_menv);
|
2013-12-06 12:51:07 +00:00
|
|
|
context new_ctx = ctx.remove(me.s(), me.n());
|
2013-10-22 23:39:22 +00:00
|
|
|
if (!is_lhs)
|
|
|
|
swap(new_a, new_b);
|
2013-10-23 20:42:14 +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]
|
2013-10-23 20:42:14 +00:00
|
|
|
m_conflict = justification(new unification_failure_justification(c));
|
2013-10-22 23:39:22 +00:00
|
|
|
return Failed;
|
|
|
|
}
|
2013-12-06 12:51:07 +00:00
|
|
|
} else if (me.is_inst() && is_proposition(b, ctx) && !is_proposition(me.v(), ctx)) {
|
|
|
|
// If b is a proposition, and the value in the local context is not,
|
|
|
|
// we ignore it, and create new constraint without the head of the local context.
|
|
|
|
// This is a little bit hackish. We do it because we want to preserve
|
|
|
|
// the structure of the proposition. This is similar to the trick used
|
|
|
|
// in the assign(a, b, c) branch above.
|
|
|
|
justification new_jst(new normalize_justification(c));
|
|
|
|
expr new_a = pop_meta_context(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
|
|
|
expr new_b = lift_free_vars(b, me.s(), 1, m_state.m_menv);
|
2013-12-06 12:51:07 +00:00
|
|
|
context new_ctx = ctx.insert_at(me.s(), g_x_name, Type()); // insert a dummy at position s
|
|
|
|
if (!is_lhs)
|
|
|
|
swap(new_a, new_b);
|
|
|
|
push_new_constraint(is_eq(c), new_ctx, new_a, new_b, new_jst);
|
|
|
|
return Processed;
|
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
|
|
|
void process_eq(context const & ctx, expr & a) {
|
|
|
|
if (is_eq(a) && m_use_normalizer) {
|
|
|
|
a = normalize(ctx, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
process_eq(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
|
|
|
}
|
|
|
|
|
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
|
|
|
/**
|
|
|
|
\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) {
|
2013-11-26 19:30:18 +00:00
|
|
|
return is_type(a) || is_metavar(a) || is_bool(a) || (is_pi(a) && is_actual_lower(abst_body(a)));
|
2013-10-27 18:17:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\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)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief See \c process_simple_ho_match
|
|
|
|
*/
|
|
|
|
bool is_simple_ho_match(context const & ctx, expr const & a, expr const & b, bool is_lhs, unification_constraint const & c) {
|
2013-12-17 01:13:31 +00:00
|
|
|
return is_meta_app(a) && !has_local_context(arg(a, 0)) && are_args_vars(ctx, a) && closed(b) &&
|
2013-10-27 18:17:03 +00:00
|
|
|
(is_eq(c) || (is_lhs && !is_actual_upper(b)) || (!is_lhs && !is_actual_lower(b)));
|
|
|
|
}
|
|
|
|
|
2013-12-16 05:48:55 +00:00
|
|
|
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-21 23:45:14 +00:00
|
|
|
/**
|
|
|
|
\brief Return true iff ctx |- a == b is a "simple" higher-order matching constraint. By simple, we mean
|
|
|
|
a constraint of the form
|
|
|
|
ctx |- (?m x) == c
|
|
|
|
The constraint is solved by assigning ?m to (fun (x : T), c).
|
|
|
|
*/
|
|
|
|
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
|
2013-10-27 18:17:03 +00:00
|
|
|
if (is_simple_ho_match(ctx, a, b, is_lhs, c)) {
|
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;
|
|
|
|
}
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new destruct_justification(c));
|
2013-10-22 20:45:04 +00:00
|
|
|
expr s = mk_lambda(types, b);
|
2013-10-21 23:45:14 +00:00
|
|
|
if (!is_lhs)
|
|
|
|
swap(m, s);
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_eq_constraint(ctx, m, s, new_jst);
|
2013-10-21 23:45:14 +00:00
|
|
|
return true;
|
|
|
|
} 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_eq(b)) {
|
|
|
|
// Imitation for equality
|
2013-12-11 20:35:32 +00:00
|
|
|
// Assign f_a <- fun (x_1 : T_1) ... (x_{num_a} : T_{num_a}), (h_1 x_1 ... x_{num_a}) = (h_2 x_1 ... x_{num_a})
|
|
|
|
// New constraints (h_1 a_1 ... a_{num_a}) == eq_lhs(b)
|
|
|
|
// (h_2 a_1 ... a_{num_a}) == eq_rhs(b)
|
2013-12-17 18:56:20 +00:00
|
|
|
expr h_1 = new_state.m_menv->mk_metavar(ctx);
|
|
|
|
expr h_2 = new_state.m_menv->mk_metavar(ctx);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, update_app(a, 0, h_1), eq_lhs(b), new_assumption);
|
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, update_app(a, 0, h_2), eq_rhs(b), new_assumption);
|
2013-12-17 18:56:20 +00:00
|
|
|
imitation = mk_lambda(arg_types, mk_eq(mk_app_vars(add_lift(h_1, 0, num_a - 1), num_a - 1), mk_app_vars(add_lift(h_2, 0, num_a - 1), num_a - 1)));
|
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);
|
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, new_ctx,
|
2013-12-17 18:56:20 +00:00
|
|
|
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
|
|
|
/**
|
|
|
|
\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",
|
|
|
|
Case 1) ?m[...] == #i and t == b
|
|
|
|
Case 2) imitate b
|
|
|
|
*/
|
|
|
|
bool process_metavar_inst(expr const & a, expr const & b, bool is_lhs, unification_constraint const & c) {
|
2013-12-14 20:25:00 +00:00
|
|
|
if (is_metavar_inst(a) && !is_metavar_inst(b) && !is_meta_app(b) &&
|
|
|
|
(is_eq(c) || (is_lhs && !is_actual_upper(b)) || (!is_lhs && !is_actual_lower(b)))) {
|
2013-10-22 23:39:22 +00:00
|
|
|
context const & ctx = get_context(c);
|
|
|
|
local_context lctx = metavar_lctx(a);
|
|
|
|
unsigned i = head(lctx).s();
|
|
|
|
expr t = head(lctx).v();
|
2013-12-11 01:23:22 +00:00
|
|
|
|
|
|
|
metavar_env & menv = m_state.m_menv;
|
|
|
|
buffer<unification_constraint> ucs;
|
2013-12-13 01:47:11 +00:00
|
|
|
expr b_type = m_type_inferer(b, ctx, menv, ucs);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(ucs);
|
2013-12-11 01:23:22 +00:00
|
|
|
context ctx_with_i = ctx.insert_at(i, g_x_name, b_type); // must add entry for variable #i in the context
|
|
|
|
|
2013-10-22 23:39:22 +00:00
|
|
|
std::unique_ptr<generic_case_split> new_cs(new generic_case_split(c, m_state));
|
|
|
|
{
|
|
|
|
// Case 1
|
|
|
|
state new_state(m_state);
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_assumption = mk_assumption();
|
2013-10-22 23:39:22 +00:00
|
|
|
// add ?m[...] == #1
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx_with_i, pop_meta_context(a), mk_var(i), new_assumption);
|
2013-10-22 23:39:22 +00:00
|
|
|
// add t == b (t << b)
|
|
|
|
expr new_a = t;
|
|
|
|
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);
|
2013-10-22 23:39:22 +00:00
|
|
|
new_cs->push_back(new_state, new_assumption);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// Case 2
|
|
|
|
state new_state(m_state);
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_assumption = mk_assumption();
|
2013-10-22 23:39:22 +00:00
|
|
|
expr imitation;
|
|
|
|
if (is_app(b)) {
|
|
|
|
// Imitation for applications b == f(s_1, ..., s_k)
|
|
|
|
// mname <- f(?h_1, ..., ?h_k)
|
|
|
|
expr f_b = arg(b, 0);
|
|
|
|
unsigned num_b = num_args(b);
|
|
|
|
buffer<expr> imitation_args;
|
|
|
|
imitation_args.push_back(f_b);
|
2013-12-12 02:06:10 +00:00
|
|
|
for (unsigned j = 1; j < num_b; j++) {
|
2013-12-13 01:47:11 +00:00
|
|
|
expr h_j = new_state.m_menv->mk_metavar(ctx);
|
2013-12-12 02:06:10 +00:00
|
|
|
imitation_args.push_back(h_j);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, h_j, lift_free_vars(arg(b, j), i, 1, new_state.m_menv), new_assumption);
|
2013-10-23 19:01:39 +00:00
|
|
|
}
|
2013-10-26 21:46:12 +00:00
|
|
|
imitation = mk_app(imitation_args);
|
2013-10-22 23:39:22 +00:00
|
|
|
} else if (is_eq(b)) {
|
|
|
|
// Imitation for equality b == Eq(s1, s2)
|
|
|
|
// mname <- Eq(?h_1, ?h_2)
|
2013-12-13 01:47:11 +00:00
|
|
|
expr h_1 = new_state.m_menv->mk_metavar(ctx);
|
|
|
|
expr h_2 = new_state.m_menv->mk_metavar(ctx);
|
2013-10-22 23:39:22 +00:00
|
|
|
imitation = mk_eq(h_1, h_2);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, h_1, lift_free_vars(eq_lhs(b), i, 1, new_state.m_menv), new_assumption);
|
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, h_2, lift_free_vars(eq_rhs(b), i, 1, new_state.m_menv), new_assumption);
|
2013-10-22 23:39:22 +00:00
|
|
|
} else if (is_abstraction(b)) {
|
|
|
|
// Lambdas and Pis
|
|
|
|
// Imitation for Lambdas and Pis, b == Fun(x:T) B
|
2013-10-23 19:01:39 +00:00
|
|
|
// mname <- Fun (x:?h_1) ?h_2
|
2013-12-13 01:47:11 +00:00
|
|
|
expr h_1 = new_state.m_menv->mk_metavar(ctx);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx, h_1, lift_free_vars(abst_domain(b), i, 1, new_state.m_menv),
|
|
|
|
new_assumption);
|
2013-10-23 19:01:39 +00:00
|
|
|
context new_ctx = extend(ctx, abst_name(b), abst_domain(b));
|
2013-12-13 01:47:11 +00:00
|
|
|
expr h_2 = new_state.m_menv->mk_metavar(new_ctx);
|
2013-12-12 03:41:24 +00:00
|
|
|
imitation = update_abstraction(b, h_1, h_2);
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, new_ctx, h_2, lift_free_vars(abst_body(b), i+1, 1, new_state.m_menv),
|
|
|
|
new_assumption);
|
2013-10-22 23:39:22 +00:00
|
|
|
} else {
|
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 = lift_free_vars(b, i, 1, new_state.m_menv);
|
2013-10-22 23:39:22 +00:00
|
|
|
}
|
2013-12-15 07:23:57 +00:00
|
|
|
push_new_eq_constraint(new_state.m_active_cnstrs, ctx_with_i, pop_meta_context(a), imitation, new_assumption);
|
2013-10-22 23:39:22 +00:00
|
|
|
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;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
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)) {
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(c);
|
2013-12-12 03:41:24 +00:00
|
|
|
// 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
|
2013-12-13 01:47:11 +00:00
|
|
|
context ctx_a = m_state.m_menv->get_context(metavar_name(a));
|
|
|
|
expr h_1 = m_state.m_menv->mk_metavar(ctx_a);
|
|
|
|
expr h_2 = m_state.m_menv->mk_metavar(extend(ctx_a, abst_name(b), abst_domain(b)));
|
2013-10-23 19:01:39 +00:00
|
|
|
expr imitation = update_abstraction(b, h_1, h_2);
|
|
|
|
expr ma = mk_metavar(metavar_name(a));
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new imitation_justification(c));
|
2013-12-12 03:41:24 +00:00
|
|
|
push_new_constraint(true, ctx_a, ma, imitation, new_jst);
|
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)) {
|
2013-10-22 20:45:04 +00:00
|
|
|
expr choices[5] = { Bool, Type(), Type(level() + 1), TypeM, TypeU };
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(mk_choice_constraint(get_context(c), b, 5, 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)) {
|
|
|
|
expr choices[2] = { a, Type(ty_level(a) + 1) };
|
|
|
|
push_active(mk_choice_constraint(get_context(c), b, 2, choices, new_jst));
|
|
|
|
return true;
|
|
|
|
} else if (m_env->is_ge(ty_level(a), m_M)) {
|
|
|
|
expr choices[3] = { a, Type(ty_level(a) + 1), TypeU };
|
|
|
|
push_active(mk_choice_constraint(get_context(c), b, 3, choices, new_jst));
|
|
|
|
return true;
|
2013-10-22 20:45:04 +00:00
|
|
|
} else {
|
2013-12-21 11:58:39 +00:00
|
|
|
expr choices[4] = { a, Type(ty_level(a) + 1), TypeM, 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) {
|
|
|
|
expr choices[5] = { TypeU, TypeM, Type(level() + 1), Type(), Bool };
|
2013-12-15 07:23:57 +00:00
|
|
|
push_active(mk_choice_constraint(get_context(c), a, 5, choices, new_jst));
|
2013-12-09 02:11:35 +00:00
|
|
|
return true;
|
2013-10-23 19:01:39 +00:00
|
|
|
} else if (b == TypeM) {
|
|
|
|
expr choices[4] = { TypeM, Type(level() + 1), Type(), 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 {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (process_assigned_metavar(c, a, true) || process_assigned_metavar(c, b, false))
|
2013-10-16 00:32:02 +00:00
|
|
|
return true;
|
|
|
|
|
2013-10-24 23:44:05 +00:00
|
|
|
if (m_assume_injectivity && is_app(a) && is_app(b) && num_args(a) == num_args(b) && arg(a, 0) == arg(b, 0)) {
|
|
|
|
// 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; }
|
|
|
|
|
|
|
|
if (normalize_head(a, b, c)) { return true; }
|
|
|
|
|
2013-10-25 18:19:17 +00:00
|
|
|
if (!eq) {
|
2013-10-27 18:17:03 +00:00
|
|
|
// TODO(Leo): use is_actual_lower and is_actual_upper
|
|
|
|
|
2013-10-25 18:19:17 +00:00
|
|
|
// Try to assign convertability constraints.
|
|
|
|
if (!is_type(b) && !is_meta(b) && is_metavar(a) && !is_assigned(a) && !has_local_context(a)) {
|
|
|
|
// We can assign a <- b at this point IF b is not (Type lvl) or Metavariable
|
|
|
|
lean_assert(!has_metavar(b, a));
|
2013-12-14 20:25:00 +00:00
|
|
|
return assign(a, b, c, true);
|
2013-10-25 18:19:17 +00:00
|
|
|
}
|
|
|
|
if (!is_type(a) && !is_meta(a) && a != Bool && is_metavar(b) && !is_assigned(b) && !has_local_context(b)) {
|
|
|
|
// We can assign b <- a at this point IF a is not (Type lvl) or Metavariable or Bool.
|
|
|
|
lean_assert(!has_metavar(a, b));
|
2013-12-14 20:25:00 +00:00
|
|
|
return assign(b, a, c, false);
|
2013-10-25 18:19:17 +00:00
|
|
|
}
|
|
|
|
}
|
2013-10-16 20:26:31 +00:00
|
|
|
|
2013-10-27 18:17:03 +00:00
|
|
|
// TODO(Leo): normalize pi domain... to make sure we are not missing solutions in process_simple_ho_match
|
|
|
|
|
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 {
|
2013-10-23 20:42:14 +00:00
|
|
|
m_conflict = justification(new unification_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 {
|
2013-10-23 20:42:14 +00:00
|
|
|
m_conflict = justification(new unification_failure_justification(c));
|
2013-10-16 20:26:31 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
case expr_kind::Eq: {
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new destruct_justification(c));
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_eq_constraint(ctx, eq_lhs(a), eq_lhs(b), new_jst);
|
|
|
|
push_new_eq_constraint(ctx, eq_rhs(a), eq_rhs(b), new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case expr_kind::Pi: {
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new destruct_justification(c));
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_eq_constraint(ctx, abst_domain(a), abst_domain(b), new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
context new_ctx = extend(ctx, abst_name(a), abst_domain(a));
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_constraint(eq, new_ctx, abst_body(a), abst_body(b), new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case expr_kind::Lambda: {
|
2013-10-23 20:42:14 +00:00
|
|
|
justification new_jst(new destruct_justification(c));
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_eq_constraint(ctx, abst_domain(a), abst_domain(b), new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
context new_ctx = extend(ctx, abst_name(a), abst_domain(a));
|
2013-12-17 00:41:20 +00:00
|
|
|
push_new_eq_constraint(new_ctx, abst_body(a), abst_body(b), new_jst);
|
2013-10-16 20:26:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
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 {
|
2013-10-29 10:01:17 +00:00
|
|
|
m_conflict = justification(new unification_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";
|
2013-10-23 20:42:14 +00:00
|
|
|
m_conflict = justification(new unification_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;
|
|
|
|
}
|
|
|
|
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-11-26 19:30:18 +00:00
|
|
|
if (is_bool(lhs2))
|
2013-10-25 02:27:53 +00:00
|
|
|
lhs2 = 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;
|
|
|
|
} else if (lhs2 == 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), 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))) {
|
2013-10-23 20:42:14 +00:00
|
|
|
m_conflict = justification(new unification_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(),
|
|
|
|
s.m_failed_justifications.data()));
|
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(),
|
|
|
|
s.m_failed_justifications.data()));
|
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(),
|
|
|
|
s.m_failed_justifications.data()));
|
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");
|
|
|
|
m_M = m_env->get_uvar("M");
|
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();
|
|
|
|
}
|
|
|
|
}
|