2014-05-24 17:45:00 +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 <utility>
|
|
|
|
#include <string>
|
|
|
|
#include "util/rb_map.h"
|
|
|
|
#include "util/sstream.h"
|
|
|
|
#include "kernel/instantiate.h"
|
2015-07-01 23:32:34 +00:00
|
|
|
#include "library/tc_multigraph.h"
|
2014-05-24 17:45:00 +00:00
|
|
|
#include "library/coercion.h"
|
2015-07-01 23:32:34 +00:00
|
|
|
#include "library/reducible.h"
|
|
|
|
#include "library/protected.h"
|
2014-05-24 17:45:00 +00:00
|
|
|
#include "library/module.h"
|
|
|
|
#include "library/kernel_serializer.h"
|
2014-06-13 02:33:02 +00:00
|
|
|
#include "library/scoped_ext.h"
|
2014-05-24 17:45:00 +00:00
|
|
|
|
|
|
|
namespace lean {
|
2015-07-01 21:29:23 +00:00
|
|
|
static name * g_fun = nullptr;
|
|
|
|
static name * g_sort = nullptr;
|
|
|
|
|
2015-07-01 23:32:34 +00:00
|
|
|
struct coercion_entry {
|
|
|
|
name m_from;
|
|
|
|
name m_coe;
|
|
|
|
unsigned m_num_args;
|
|
|
|
name m_to;
|
|
|
|
coercion_entry() {}
|
|
|
|
coercion_entry(name const & from, name const & coe, unsigned num, name const & to):
|
|
|
|
m_from(from), m_coe(coe), m_num_args(num), m_to(to) {}
|
2014-05-24 17:45:00 +00:00
|
|
|
};
|
|
|
|
|
2014-06-15 05:13:25 +00:00
|
|
|
struct coercion_state {
|
2015-07-01 23:32:34 +00:00
|
|
|
tc_multigraph m_graph;
|
|
|
|
name_map<pair<name, unsigned>> m_coercions; // map coercion -> (from-class, num-args)
|
2014-09-14 19:01:14 +00:00
|
|
|
|
2015-07-01 23:32:34 +00:00
|
|
|
void add1(environment const & env, name const & from, name const & coe, unsigned num, name const & to) {
|
|
|
|
m_coercions.insert(coe, mk_pair(from, num));
|
|
|
|
m_graph.add1(env, from, coe, to);
|
2014-05-25 16:49:26 +00:00
|
|
|
}
|
2014-05-24 17:45:00 +00:00
|
|
|
|
2015-07-01 23:32:34 +00:00
|
|
|
coercion_state():m_graph("coercion") {}
|
2014-05-25 18:08:49 +00:00
|
|
|
};
|
2014-05-24 17:45:00 +00:00
|
|
|
|
2014-09-23 00:30:29 +00:00
|
|
|
static name * g_class_name = nullptr;
|
|
|
|
static std::string * g_key = nullptr;
|
|
|
|
|
2014-06-13 02:33:02 +00:00
|
|
|
struct coercion_config {
|
|
|
|
typedef coercion_state state;
|
|
|
|
typedef coercion_entry entry;
|
2015-07-01 23:32:34 +00:00
|
|
|
static void add_entry(environment const & env, io_state const &, state & s, entry const & e) {
|
|
|
|
s.add1(env, e.m_from, e.m_coe, e.m_num_args, e.m_to);
|
2014-06-13 02:33:02 +00:00
|
|
|
}
|
|
|
|
static name const & get_class_name() {
|
2014-09-23 00:30:29 +00:00
|
|
|
return *g_class_name;
|
2014-06-13 02:33:02 +00:00
|
|
|
}
|
|
|
|
static std::string const & get_serialization_key() {
|
2014-09-23 00:30:29 +00:00
|
|
|
return *g_key;
|
2014-06-13 02:33:02 +00:00
|
|
|
}
|
|
|
|
static void write_entry(serializer & s, entry const & e) {
|
2015-07-01 23:32:34 +00:00
|
|
|
s << e.m_from << e.m_coe << e.m_num_args << e.m_to;
|
2014-06-13 02:33:02 +00:00
|
|
|
}
|
|
|
|
static entry read_entry(deserializer & d) {
|
|
|
|
entry e;
|
2015-07-01 23:32:34 +00:00
|
|
|
d >> e.m_from >> e.m_coe >> e.m_num_args >> e.m_to;
|
2014-06-13 02:33:02 +00:00
|
|
|
return e;
|
|
|
|
}
|
2014-09-30 01:26:53 +00:00
|
|
|
static optional<unsigned> get_fingerprint(entry const & e) {
|
2015-07-01 23:32:34 +00:00
|
|
|
return some(e.m_coe.hash());
|
2014-09-30 01:26:53 +00:00
|
|
|
}
|
2014-06-13 02:33:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template class scoped_ext<coercion_config>;
|
|
|
|
typedef scoped_ext<coercion_config> coercion_ext;
|
|
|
|
|
2014-09-23 00:30:29 +00:00
|
|
|
void initialize_coercion() {
|
2015-07-01 21:29:23 +00:00
|
|
|
name p = name::mk_internal_unique_name();
|
|
|
|
g_fun = new name(p, "Fun");
|
|
|
|
g_sort = new name(p, "Sort");
|
2014-09-23 00:30:29 +00:00
|
|
|
g_class_name = new name("coercions");
|
|
|
|
g_key = new std::string("coerce");
|
|
|
|
coercion_ext::initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void finalize_coercion() {
|
|
|
|
coercion_ext::finalize();
|
|
|
|
delete g_key;
|
|
|
|
delete g_class_name;
|
2015-07-01 21:29:23 +00:00
|
|
|
delete g_fun;
|
|
|
|
delete g_sort;
|
2014-09-23 00:30:29 +00:00
|
|
|
}
|
|
|
|
|
2014-09-09 19:46:55 +00:00
|
|
|
optional<pair<name, unsigned>> is_coercion(environment const & env, name const & f) {
|
2014-06-13 02:33:02 +00:00
|
|
|
coercion_state const & ext = coercion_ext::get_state(env);
|
2014-09-09 19:46:55 +00:00
|
|
|
if (auto it = ext.m_coercions.find(f))
|
|
|
|
return optional<pair<name, unsigned>>(*it);
|
|
|
|
else
|
|
|
|
return optional<pair<name, unsigned>>();
|
2014-05-25 15:38:50 +00:00
|
|
|
}
|
|
|
|
|
2014-09-09 19:46:55 +00:00
|
|
|
optional<pair<name, unsigned>> is_coercion(environment const & env, expr const & f) {
|
|
|
|
if (!is_constant(f))
|
|
|
|
return optional<pair<name, unsigned>>();
|
|
|
|
return is_coercion(env, const_name(f));
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
|
|
|
|
2014-06-27 01:38:27 +00:00
|
|
|
bool has_coercions_to(environment const & env, name const & D) {
|
|
|
|
coercion_state const & ext = coercion_ext::get_state(env);
|
2015-07-01 23:32:34 +00:00
|
|
|
return !is_nil(ext.m_graph.get_predecessors(D));
|
2014-06-27 01:38:27 +00:00
|
|
|
}
|
|
|
|
|
2014-09-20 16:00:10 +00:00
|
|
|
bool has_coercions_to_sort(environment const & env) {
|
|
|
|
coercion_state const & ext = coercion_ext::get_state(env);
|
2015-07-01 23:32:34 +00:00
|
|
|
return !is_nil(ext.m_graph.get_predecessors(*g_sort));
|
2014-09-20 16:00:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool has_coercions_to_fun(environment const & env) {
|
|
|
|
coercion_state const & ext = coercion_ext::get_state(env);
|
2015-07-01 23:32:34 +00:00
|
|
|
return !is_nil(ext.m_graph.get_predecessors(*g_fun));
|
2014-09-20 16:00:10 +00:00
|
|
|
}
|
|
|
|
|
2014-05-24 17:45:00 +00:00
|
|
|
bool has_coercions_from(environment const & env, name const & C) {
|
2014-06-13 02:33:02 +00:00
|
|
|
coercion_state const & ext = coercion_ext::get_state(env);
|
2015-07-01 23:32:34 +00:00
|
|
|
return !is_nil(ext.m_graph.get_successors(C));
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool has_coercions_from(environment const & env, expr const & C) {
|
|
|
|
expr const & C_fn = get_app_fn(C);
|
|
|
|
if (!is_constant(C_fn))
|
|
|
|
return false;
|
2014-06-13 02:33:02 +00:00
|
|
|
coercion_state const & ext = coercion_ext::get_state(env);
|
2015-07-01 23:32:34 +00:00
|
|
|
for (pair<name, name> const & coe_to : ext.m_graph.get_successors(const_name(C_fn))) {
|
|
|
|
name const & coe = coe_to.first;
|
|
|
|
if (auto it = ext.m_coercions.find(coe)) {
|
|
|
|
if (it->second == get_app_num_args(C))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 23:32:34 +00:00
|
|
|
static list<expr> get_coercions_core(environment const & env, expr const & C, name const & D) {
|
2014-05-24 17:45:00 +00:00
|
|
|
buffer<expr> args;
|
|
|
|
expr const & C_fn = get_app_rev_args(C, args);
|
|
|
|
if (!is_constant(C_fn))
|
2014-09-14 19:01:14 +00:00
|
|
|
return list<expr>();
|
2014-06-13 02:33:02 +00:00
|
|
|
coercion_state const & ext = coercion_ext::get_state(env);
|
2014-09-14 19:01:14 +00:00
|
|
|
buffer<expr> r;
|
2015-07-01 23:32:34 +00:00
|
|
|
for (pair<name, name> const & coe_to : ext.m_graph.get_successors(const_name(C_fn))) {
|
|
|
|
name const & coe = coe_to.first;
|
|
|
|
name const & to = coe_to.second;
|
|
|
|
if (to != D)
|
|
|
|
continue;
|
|
|
|
if (auto it = ext.m_coercions.find(coe)) {
|
|
|
|
if (it->second != args.size())
|
|
|
|
continue;
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
2015-07-01 23:32:34 +00:00
|
|
|
declaration const & coe_decl = env.get(coe);
|
|
|
|
if (coe_decl.get_num_univ_params() != length(const_levels(C_fn)))
|
|
|
|
continue;
|
|
|
|
expr f = mk_constant(coe, const_levels(C_fn));
|
|
|
|
r.push_back(mk_rev_app(f, args.size(), args.data()));
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
2015-07-01 23:32:34 +00:00
|
|
|
return to_list(r);
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 23:32:34 +00:00
|
|
|
|
2014-09-14 19:01:14 +00:00
|
|
|
list<expr> get_coercions(environment const & env, expr const & C, name const & D) {
|
2015-07-01 23:32:34 +00:00
|
|
|
return get_coercions_core(env, C, D);
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
|
|
|
|
2014-09-14 19:01:14 +00:00
|
|
|
list<expr> get_coercions_to_sort(environment const & env, expr const & C) {
|
2015-07-01 23:32:34 +00:00
|
|
|
return get_coercions_core(env, C, *g_sort);
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
|
|
|
|
2014-09-14 19:01:14 +00:00
|
|
|
list<expr> get_coercions_to_fun(environment const & env, expr const & C) {
|
2015-07-01 23:32:34 +00:00
|
|
|
return get_coercions_core(env, C, *g_fun);
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 21:40:12 +00:00
|
|
|
bool get_coercions_from(environment const & env, expr const & C, buffer<expr> & result) {
|
2014-05-24 17:45:00 +00:00
|
|
|
buffer<expr> args;
|
|
|
|
expr const & C_fn = get_app_rev_args(C, args);
|
|
|
|
if (!is_constant(C_fn))
|
|
|
|
return false;
|
2014-06-13 02:33:02 +00:00
|
|
|
coercion_state const & ext = coercion_ext::get_state(env);
|
2014-05-24 17:45:00 +00:00
|
|
|
bool r = false;
|
2015-07-01 23:32:34 +00:00
|
|
|
for (pair<name, name> const & coe_to : ext.m_graph.get_successors(const_name(C_fn))) {
|
|
|
|
name const & coe = coe_to.first;
|
|
|
|
if (auto it = ext.m_coercions.find(coe)) {
|
|
|
|
if (it->second != args.size())
|
|
|
|
continue;
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
2015-07-01 23:32:34 +00:00
|
|
|
declaration const & coe_decl = env.get(coe);
|
|
|
|
if (coe_decl.get_num_univ_params() != length(const_levels(C_fn)))
|
|
|
|
continue;
|
|
|
|
expr f = mk_constant(coe, const_levels(C_fn));
|
|
|
|
result.push_back(mk_rev_app(f, args.size(), args.data()));
|
|
|
|
r = true;
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
2014-05-25 13:05:31 +00:00
|
|
|
|
2014-09-14 15:52:46 +00:00
|
|
|
void for_each_coercion_user(environment const & env, coercion_user_fn const & f) {
|
2015-07-01 23:32:34 +00:00
|
|
|
tc_multigraph const & g = coercion_ext::get_state(env).m_graph;
|
|
|
|
g.for_each(f);
|
2014-05-25 14:24:58 +00:00
|
|
|
}
|
|
|
|
|
2014-09-14 15:52:46 +00:00
|
|
|
void for_each_coercion_sort(environment const & env, coercion_sort_fn const & f) {
|
2015-07-01 23:32:34 +00:00
|
|
|
tc_multigraph const & g = coercion_ext::get_state(env).m_graph;
|
|
|
|
g.for_each([&](name const & from, name const & coe, name const & to) {
|
|
|
|
if (to == *g_sort)
|
|
|
|
f(from, coe);
|
2014-05-25 14:24:58 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-09-14 15:52:46 +00:00
|
|
|
void for_each_coercion_fun(environment const & env, coercion_fun_fn const & f) {
|
2015-07-01 23:32:34 +00:00
|
|
|
tc_multigraph const & g = coercion_ext::get_state(env).m_graph;
|
|
|
|
g.for_each([&](name const & from, name const & coe, name const & to) {
|
|
|
|
if (to == *g_fun)
|
|
|
|
f(from, coe);
|
2014-05-25 14:24:58 +00:00
|
|
|
});
|
|
|
|
}
|
2015-07-01 23:32:34 +00:00
|
|
|
|
|
|
|
static void check_pi(name const & f, expr const & t) {
|
|
|
|
if (!is_pi(t))
|
|
|
|
throw exception(sstream() << "invalid coercion, '" << f << "' is not function");
|
|
|
|
}
|
|
|
|
|
|
|
|
// similar to check_pi, but produces a more informative message
|
|
|
|
static void check_valid_coercion(name const & f, expr const & t) {
|
|
|
|
if (!is_pi(t)) {
|
|
|
|
throw exception(sstream() << "invalid coercion, type of '" << f
|
|
|
|
<< "' does not match any of the allowed expected types for coercions\n"
|
|
|
|
<< " Pi (x_1 : A_1) ... (x_n : A_n) (y: C x_1 ... x_n), D t_1 ... t_m\n"
|
|
|
|
<< " Pi (x_1 : A_1) ... (x_n : A_n) (y: C x_1 ... x_n), Type\n"
|
|
|
|
<< " Pi (x_1 : A_1) ... (x_n : A_n) (y: C x_1 ... x_n), A -> B");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Return true iff args contains Var(0), Var(1), ..., Var(args.size() - 1) */
|
|
|
|
static bool check_var_args(buffer<expr> const & args) {
|
|
|
|
for (unsigned i = 0; i < args.size(); i++) {
|
|
|
|
if (!is_var(args[i]) || var_idx(args[i]) != i)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \brief Return true iff param_id(levels[i]) == level_params[i] */
|
|
|
|
static bool check_levels(levels ls, level_param_names ps) {
|
|
|
|
while (!is_nil(ls) && !is_nil(ps)) {
|
|
|
|
if (!is_param(head(ls)))
|
|
|
|
return false;
|
|
|
|
if (param_id(head(ls)) != head(ps))
|
|
|
|
return false;
|
|
|
|
ls = tail(ls);
|
|
|
|
ps = tail(ps);
|
|
|
|
}
|
|
|
|
return is_nil(ls) && is_nil(ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
static optional<name> type_to_coercion_class(expr const & t) {
|
|
|
|
if (is_sort(t)) {
|
|
|
|
return optional<name>(*g_sort);
|
|
|
|
} else if (is_pi(t)) {
|
|
|
|
return optional<name>(*g_fun);
|
|
|
|
} else {
|
|
|
|
expr const & C = get_app_fn(t);
|
|
|
|
if (is_constant(C))
|
|
|
|
return optional<name>(const_name(C));
|
|
|
|
else
|
|
|
|
return optional<name>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_user_class(name const & cls) {
|
|
|
|
return cls != *g_fun && cls != *g_sort;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned get_num_args(environment const & env, tc_edge const & new_coe) {
|
|
|
|
declaration const & d = env.get(new_coe.m_cnst);
|
|
|
|
unsigned num = 0;
|
|
|
|
buffer<expr> args;
|
|
|
|
expr t = d.get_type();
|
|
|
|
while (true) {
|
|
|
|
if (!is_pi(t))
|
|
|
|
return num;
|
|
|
|
expr fn = get_app_fn(binding_domain(t));
|
|
|
|
if (is_constant(fn) && const_name(fn) == new_coe.m_from)
|
|
|
|
return num;
|
|
|
|
t = binding_body(t);
|
|
|
|
num++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static environment add_coercion_core(environment const & env,
|
|
|
|
name const & from, name const & coe, unsigned num_args, name const & to,
|
|
|
|
bool persistent) {
|
|
|
|
coercion_state st = coercion_ext::get_state(env);
|
|
|
|
pair<environment, list<tc_edge>> new_env_coes = st.m_graph.add(env, from, coe, to);
|
|
|
|
environment new_env = new_env_coes.first;
|
|
|
|
new_env = coercion_ext::add_entry(new_env, get_dummy_ios(), coercion_entry(from, coe, num_args, to), persistent);
|
|
|
|
for (tc_edge const & new_coe : new_env_coes.second) {
|
|
|
|
unsigned nargs = get_num_args(new_env, new_coe);
|
|
|
|
new_env = coercion_ext::add_entry(new_env, get_dummy_ios(),
|
|
|
|
coercion_entry(new_coe.m_from, new_coe.m_cnst, nargs, new_coe.m_to), persistent);
|
|
|
|
new_env = set_reducible(new_env, new_coe.m_cnst, reducible_status::Reducible, persistent);
|
|
|
|
new_env = add_protected(new_env, new_coe.m_cnst);
|
|
|
|
}
|
|
|
|
return new_env;
|
|
|
|
}
|
|
|
|
|
2015-07-02 14:26:00 +00:00
|
|
|
static environment add_coercion(environment const & env, name const & f, name const & C, bool persistent) {
|
2015-07-01 23:32:34 +00:00
|
|
|
declaration d = env.get(f);
|
|
|
|
unsigned num = 0;
|
|
|
|
buffer<expr> args;
|
|
|
|
expr t = d.get_type();
|
|
|
|
check_pi(f, t);
|
|
|
|
while (true) {
|
|
|
|
args.clear();
|
|
|
|
expr const & C_fn = get_app_rev_args(binding_domain(t), args);
|
|
|
|
if (is_constant(C_fn) &&
|
|
|
|
const_name(C_fn) == C &&
|
|
|
|
num == args.size() &&
|
|
|
|
check_var_args(args) &&
|
|
|
|
check_levels(const_levels(C_fn), d.get_univ_params())) {
|
|
|
|
optional<name> cls = type_to_coercion_class(binding_body(t));
|
|
|
|
if (!cls)
|
|
|
|
throw exception(sstream() << "invalid coercion, '" << f << "' cannot be used as a coercion from '"
|
|
|
|
<< C << "'");
|
|
|
|
else if (is_user_class(*cls) && *cls == C)
|
|
|
|
throw exception(sstream() << "invalid coercion, '" << f << "' is a coercion from '" << C << "' to itself");
|
|
|
|
return add_coercion_core(env, C, f, num, *cls, persistent);
|
|
|
|
}
|
|
|
|
t = binding_body(t);
|
|
|
|
num++;
|
|
|
|
check_valid_coercion(f, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-02 14:26:00 +00:00
|
|
|
environment add_coercion(environment const & env, io_state const &, name const & f, bool persistent) {
|
2015-07-01 23:32:34 +00:00
|
|
|
declaration d = env.get(f);
|
|
|
|
expr t = d.get_type();
|
|
|
|
check_pi(f, t);
|
|
|
|
buffer<name> Cs; // possible Cs
|
|
|
|
while (is_pi(t)) {
|
|
|
|
expr d = get_app_fn(binding_domain(t));
|
|
|
|
if (is_constant(d))
|
|
|
|
Cs.push_back(const_name(d));
|
|
|
|
t = binding_body(t);
|
|
|
|
}
|
|
|
|
if (Cs.empty())
|
|
|
|
throw exception(sstream() << "invalid coercion, '" << f << "' cannot be used as a coercion");
|
|
|
|
unsigned i = Cs.size();
|
|
|
|
while (i > 0) {
|
|
|
|
--i;
|
|
|
|
if (i == 0) {
|
|
|
|
// last alternative
|
2015-07-02 14:26:00 +00:00
|
|
|
return add_coercion(env, f, Cs[i], persistent);
|
2015-07-01 23:32:34 +00:00
|
|
|
} else {
|
|
|
|
try {
|
2015-07-02 14:26:00 +00:00
|
|
|
return add_coercion(env, f, Cs[i], persistent);
|
2015-07-01 23:32:34 +00:00
|
|
|
} catch (exception &) {
|
|
|
|
// failed, keep trying...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lean_unreachable(); // LCOV_EXCL_LINE
|
|
|
|
}
|
2014-05-24 17:45:00 +00:00
|
|
|
}
|