2014-09-10 23:07:41 +00:00
|
|
|
/*
|
|
|
|
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
|
|
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
|
|
|
|
Author: Leonardo de Moura
|
|
|
|
*/
|
|
|
|
#include "kernel/type_checker.h"
|
|
|
|
#include "kernel/metavar.h"
|
|
|
|
#include "kernel/constraint.h"
|
2015-05-30 23:44:26 +00:00
|
|
|
#include "kernel/instantiate.h"
|
2015-05-30 21:45:14 +00:00
|
|
|
#include "kernel/abstract.h"
|
2014-09-10 23:07:41 +00:00
|
|
|
#include "library/coercion.h"
|
|
|
|
#include "library/unifier.h"
|
2014-12-19 22:29:32 +00:00
|
|
|
#include "library/choice_iterator.h"
|
2014-09-10 23:07:41 +00:00
|
|
|
#include "frontends/lean/coercion_elaborator.h"
|
|
|
|
|
|
|
|
namespace lean {
|
2014-09-14 19:01:14 +00:00
|
|
|
coercion_elaborator::coercion_elaborator(coercion_info_manager & info, expr const & arg,
|
|
|
|
list<constraints> const & choices, list<expr> const & coes,
|
|
|
|
bool use_id):
|
|
|
|
m_info(info), m_arg(arg), m_id(use_id), m_choices(choices), m_coercions(coes) {
|
2014-09-17 01:59:51 +00:00
|
|
|
lean_assert(!use_id || length(m_coercions) + 1 == length(m_choices));
|
|
|
|
lean_assert(use_id || length(m_coercions) == length(m_choices));
|
2014-09-14 19:01:14 +00:00
|
|
|
}
|
2014-09-10 23:07:41 +00:00
|
|
|
|
2015-05-30 21:45:14 +00:00
|
|
|
list<expr> get_coercions_from_to(type_checker & from_tc, type_checker & to_tc,
|
2015-05-20 23:06:20 +00:00
|
|
|
expr const & from_type, expr const & to_type, constraint_seq & cs) {
|
2014-09-20 16:00:10 +00:00
|
|
|
constraint_seq new_cs;
|
2015-05-20 23:06:20 +00:00
|
|
|
environment const & env = to_tc.env();
|
2015-05-30 21:45:14 +00:00
|
|
|
expr whnf_from_type = from_tc.whnf(from_type, new_cs);
|
|
|
|
expr whnf_to_type = to_tc.whnf(to_type, new_cs);
|
2015-05-30 23:44:26 +00:00
|
|
|
if (is_pi(whnf_from_type)) {
|
2015-05-30 21:45:14 +00:00
|
|
|
// Try to lift coercions.
|
|
|
|
// The idea is to convert a coercion from A to B, into a coercion from D->A to D->B
|
2015-05-30 23:44:26 +00:00
|
|
|
if (!is_pi(whnf_to_type))
|
2015-05-30 21:45:14 +00:00
|
|
|
return list<expr>(); // failed
|
|
|
|
if (!from_tc.is_def_eq(binding_domain(whnf_from_type), binding_domain(whnf_to_type), justification(), new_cs))
|
|
|
|
return list<expr>(); // failed, the domains must be definitionally equal
|
2015-05-30 23:44:26 +00:00
|
|
|
expr x = mk_local(from_tc.mk_fresh_name(), "x", binding_domain(whnf_from_type), binder_info());
|
|
|
|
expr A = instantiate(binding_body(whnf_from_type), x);
|
|
|
|
expr B = instantiate(binding_body(whnf_to_type), x);
|
|
|
|
list<expr> coe = get_coercions_from_to(from_tc, to_tc, A, B, new_cs);
|
2015-05-30 21:45:14 +00:00
|
|
|
if (coe) {
|
|
|
|
cs += new_cs;
|
|
|
|
// Remark: each coercion c in coe is a function from A to B
|
|
|
|
// We create a new list: (fun (f : D -> A) (x : D), c (f x))
|
|
|
|
expr f = mk_local(from_tc.mk_fresh_name(), "f", whnf_from_type, binder_info());
|
|
|
|
expr fx = mk_app(f, x);
|
|
|
|
return map(coe, [&](expr const & c) { return Fun(f, Fun(x, mk_app(c, fx))); });
|
|
|
|
} else {
|
|
|
|
return list<expr>();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
expr const & fn = get_app_fn(whnf_to_type);
|
|
|
|
list<expr> r;
|
|
|
|
if (is_constant(fn)) {
|
|
|
|
r = get_coercions(env, whnf_from_type, const_name(fn));
|
|
|
|
} else if (is_pi(whnf_to_type)) {
|
|
|
|
r = get_coercions_to_fun(env, whnf_from_type);
|
|
|
|
} else if (is_sort(whnf_to_type)) {
|
|
|
|
r = get_coercions_to_sort(env, whnf_from_type);
|
|
|
|
}
|
|
|
|
if (r)
|
|
|
|
cs += new_cs;
|
|
|
|
return r;
|
2014-09-20 16:00:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-14 19:01:14 +00:00
|
|
|
optional<constraints> coercion_elaborator::next() {
|
|
|
|
if (!m_choices)
|
|
|
|
return optional<constraints>();
|
|
|
|
if (m_id) {
|
|
|
|
m_id = false;
|
|
|
|
m_info.erase_coercion_info(m_arg);
|
|
|
|
} else if (m_coercions) {
|
|
|
|
expr c = head(m_coercions);
|
|
|
|
m_coercions = tail(m_coercions);
|
|
|
|
m_info.save_coercion_info(m_arg, mk_app(c, m_arg));
|
2014-09-10 23:07:41 +00:00
|
|
|
}
|
2014-09-14 19:01:14 +00:00
|
|
|
auto r = head(m_choices);
|
|
|
|
m_choices = tail(m_choices);
|
|
|
|
return optional<constraints>(r);
|
|
|
|
}
|
2014-09-10 23:07:41 +00:00
|
|
|
|
2015-05-30 23:44:26 +00:00
|
|
|
bool is_pi_meta(expr const & e) {
|
|
|
|
if (is_pi(e)) {
|
|
|
|
return is_pi_meta(binding_body(e));
|
|
|
|
} else {
|
|
|
|
return is_meta(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-25 15:48:31 +00:00
|
|
|
/** \brief Given a term <tt>a : a_type</tt>, and a metavariable \c m, creates a constraint
|
|
|
|
that considers coercions from a_type to the type assigned to \c m. */
|
2015-05-20 23:06:20 +00:00
|
|
|
constraint mk_coercion_cnstr(type_checker & from_tc, type_checker & to_tc, coercion_info_manager & infom,
|
2014-09-10 23:07:41 +00:00
|
|
|
expr const & m, expr const & a, expr const & a_type,
|
2015-05-08 21:36:38 +00:00
|
|
|
justification const & j, unsigned delay_factor) {
|
2015-05-20 23:06:20 +00:00
|
|
|
auto choice_fn = [=, &from_tc, &to_tc, &infom](expr const & meta, expr const & d_type, substitution const & s,
|
2015-05-21 21:32:36 +00:00
|
|
|
name_generator && /* ngen */) {
|
2014-09-10 23:07:41 +00:00
|
|
|
expr new_a_type;
|
|
|
|
justification new_a_type_jst;
|
|
|
|
if (is_meta(a_type)) {
|
|
|
|
auto p = substitution(s).instantiate_metavars(a_type);
|
|
|
|
new_a_type = p.first;
|
|
|
|
new_a_type_jst = p.second;
|
|
|
|
} else {
|
|
|
|
new_a_type = a_type;
|
|
|
|
}
|
|
|
|
if (is_meta(new_a_type)) {
|
|
|
|
if (delay_factor < to_delay_factor(cnstr_group::DelayedChoice)) {
|
|
|
|
// postpone...
|
2015-05-20 23:06:20 +00:00
|
|
|
return lazy_list<constraints>(constraints(mk_coercion_cnstr(from_tc, to_tc, infom, m, a, a_type, justification(),
|
2015-05-08 21:36:38 +00:00
|
|
|
delay_factor+1)));
|
2014-09-10 23:07:41 +00:00
|
|
|
} else {
|
|
|
|
// giveup...
|
2015-05-08 21:36:38 +00:00
|
|
|
return lazy_list<constraints>(constraints(mk_eq_cnstr(meta, a, justification())));
|
2014-09-10 23:07:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
constraint_seq cs;
|
2015-05-20 23:06:20 +00:00
|
|
|
new_a_type = from_tc.whnf(new_a_type, cs);
|
2015-05-30 23:44:26 +00:00
|
|
|
if (is_pi_meta(d_type)) {
|
2014-09-10 23:07:41 +00:00
|
|
|
// case-split
|
2015-05-30 23:44:26 +00:00
|
|
|
buffer<expr> locals;
|
|
|
|
expr it_from = new_a_type;
|
|
|
|
expr it_to = d_type;
|
|
|
|
while (is_pi(it_from) && is_pi(it_to)) {
|
|
|
|
expr dom_from = binding_domain(it_from);
|
|
|
|
expr dom_to = binding_domain(it_to);
|
|
|
|
if (!from_tc.is_def_eq(dom_from, dom_to, justification(), cs))
|
|
|
|
return lazy_list<constraints>();
|
|
|
|
expr local = mk_local(from_tc.mk_fresh_name(), binding_name(it_from), dom_from, binder_info());
|
|
|
|
locals.push_back(local);
|
|
|
|
it_from = instantiate(binding_body(it_from), local);
|
|
|
|
it_to = instantiate(binding_body(it_to), local);
|
|
|
|
}
|
2014-09-20 16:00:10 +00:00
|
|
|
buffer<std::tuple<coercion_class, expr, expr>> alts;
|
2015-05-30 23:44:26 +00:00
|
|
|
get_coercions_from(from_tc.env(), it_from, alts);
|
|
|
|
expr fn_a;
|
|
|
|
if (!locals.empty())
|
|
|
|
fn_a = mk_local(from_tc.mk_fresh_name(), "f", new_a_type, binder_info());
|
2014-09-10 23:07:41 +00:00
|
|
|
buffer<constraints> choices;
|
|
|
|
buffer<expr> coes;
|
|
|
|
// first alternative: no coercion
|
2015-05-08 21:36:38 +00:00
|
|
|
constraint_seq cs1 = cs + mk_eq_cnstr(meta, a, justification());
|
2014-09-10 23:07:41 +00:00
|
|
|
choices.push_back(cs1.to_list());
|
|
|
|
unsigned i = alts.size();
|
|
|
|
while (i > 0) {
|
|
|
|
--i;
|
|
|
|
auto const & t = alts[i];
|
2015-05-30 23:44:26 +00:00
|
|
|
expr coe = std::get<1>(t);
|
|
|
|
if (!locals.empty())
|
|
|
|
coe = Fun(fn_a, Fun(locals, mk_app(coe, mk_app(fn_a, locals))));
|
2014-09-10 23:07:41 +00:00
|
|
|
expr new_a = copy_tag(a, mk_app(coe, a));
|
|
|
|
coes.push_back(coe);
|
2015-05-08 21:36:38 +00:00
|
|
|
constraint_seq csi = cs + mk_eq_cnstr(meta, new_a, new_a_type_jst);
|
2014-09-10 23:07:41 +00:00
|
|
|
choices.push_back(csi.to_list());
|
|
|
|
}
|
2014-09-14 19:01:14 +00:00
|
|
|
return choose(std::make_shared<coercion_elaborator>(infom, meta,
|
2014-09-10 23:07:41 +00:00
|
|
|
to_list(choices.begin(), choices.end()),
|
|
|
|
to_list(coes.begin(), coes.end())));
|
|
|
|
} else {
|
2015-05-20 23:06:20 +00:00
|
|
|
list<expr> coes = get_coercions_from_to(from_tc, to_tc, new_a_type, d_type, cs);
|
2014-09-20 16:00:10 +00:00
|
|
|
if (is_nil(coes)) {
|
2014-09-14 19:01:14 +00:00
|
|
|
expr new_a = a;
|
|
|
|
infom.erase_coercion_info(a);
|
2015-05-08 21:36:38 +00:00
|
|
|
cs += mk_eq_cnstr(meta, new_a, new_a_type_jst);
|
2014-09-14 19:01:14 +00:00
|
|
|
return lazy_list<constraints>(cs.to_list());
|
2014-09-20 16:00:10 +00:00
|
|
|
} else if (is_nil(tail(coes))) {
|
|
|
|
expr new_a = copy_tag(a, mk_app(head(coes), a));
|
|
|
|
infom.save_coercion_info(a, new_a);
|
2015-05-08 21:36:38 +00:00
|
|
|
cs += mk_eq_cnstr(meta, new_a, new_a_type_jst);
|
2014-09-20 16:00:10 +00:00
|
|
|
return lazy_list<constraints>(cs.to_list());
|
|
|
|
} else {
|
|
|
|
list<constraints> choices = map2<constraints>(coes, [&](expr const & coe) {
|
|
|
|
expr new_a = copy_tag(a, mk_app(coe, a));
|
2015-05-08 21:36:38 +00:00
|
|
|
constraint c = mk_eq_cnstr(meta, new_a, new_a_type_jst);
|
2014-09-20 16:00:10 +00:00
|
|
|
return (cs + c).to_list();
|
|
|
|
});
|
|
|
|
return choose(std::make_shared<coercion_elaborator>(infom, meta, choices, coes, false));
|
2014-09-10 23:07:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2015-05-08 21:36:38 +00:00
|
|
|
return mk_choice_cnstr(m, choice_fn, delay_factor, true, j);
|
2014-09-10 23:07:41 +00:00
|
|
|
}
|
|
|
|
}
|