feat(library/coercion): new coercion manager

closes #668
This commit is contained in:
Leonardo de Moura 2015-07-01 16:32:34 -07:00
parent fe26c37fcb
commit 4ae9f3ea81
12 changed files with 266 additions and 502 deletions

View file

@ -72,11 +72,13 @@ calc
a div b = -((-a - 1) div b + 1) : div_of_neg_of_pos Ha Hb a div b = -((-a - 1) div b + 1) : div_of_neg_of_pos Ha Hb
... < 0 : neg_neg_of_pos H2 ... < 0 : neg_neg_of_pos H2
set_option pp.coercions true
theorem zero_div (b : ) : 0 div b = 0 := theorem zero_div (b : ) : 0 div b = 0 :=
calc calc
0 div b = sign b * (#nat 0 div (nat_abs b)) : rfl 0 div b = sign b * (#nat 0 div (nat_abs b)) : rfl
... = sign b * 0 : nat.zero_div ... = sign b * (0:nat) : nat.zero_div
... = 0 : mul_zero ... = 0 : mul_zero
theorem div_zero (a : ) : a div 0 = 0 := theorem div_zero (a : ) : a div 0 = 0 :=
by rewrite [↑divide, sign_zero, zero_mul] by rewrite [↑divide, sign_zero, zero_mul]
@ -98,7 +100,7 @@ int.cases_on a
assume H : m < n, assume H : m < n,
calc calc
m div n = #nat m div n : of_nat_div m div n = #nat m div n : of_nat_div
... = 0 : nat.div_eq_zero_of_lt (lt_of_of_nat_lt_of_nat H)) ... = (0:nat) : nat.div_eq_zero_of_lt (lt_of_of_nat_lt_of_nat H))
(take n, (take n,
assume H : m < -[1+n], assume H : m < -[1+n],
have H1 : ¬(m < -[1+n]), from dec_trivial, have H1 : ¬(m < -[1+n]), from dec_trivial,

View file

@ -54,15 +54,15 @@ static void print_coercions(parser & p, optional<name> const & C) {
opts = opts.update(get_pp_coercions_option_name(), true); opts = opts.update(get_pp_coercions_option_name(), true);
io_state_stream out = p.regular_stream().update_options(opts); io_state_stream out = p.regular_stream().update_options(opts);
char const * arrow = get_pp_unicode(opts) ? "" : ">->"; char const * arrow = get_pp_unicode(opts) ? "" : ">->";
for_each_coercion_user(env, [&](name const & C1, name const & D, expr const & c, level_param_names const &, unsigned) { for_each_coercion_user(env, [&](name const & C1, name const & c, name const & D) {
if (!C || *C == C1) if (!C || *C == C1)
out << C1 << " " << arrow << " " << D << " : " << c << endl; out << C1 << " " << arrow << " " << D << " : " << c << endl;
}); });
for_each_coercion_sort(env, [&](name const & C1, expr const & c, level_param_names const &, unsigned) { for_each_coercion_sort(env, [&](name const & C1, name const & c) {
if (!C || *C == C1) if (!C || *C == C1)
out << C1 << " " << arrow << " [sort-class] : " << c << endl; out << C1 << " " << arrow << " [sort-class] : " << c << endl;
}); });
for_each_coercion_fun(env, [&](name const & C1, expr const & c, level_param_names const &, unsigned) { for_each_coercion_fun(env, [&](name const & C1, name const & c) {
if (!C || *C == C1) if (!C || *C == C1)
out << C1 << " " << arrow << " [fun-class] : " << c << endl; out << C1 << " " << arrow << " [fun-class] : " << c << endl;
}); });
@ -960,20 +960,6 @@ static environment init_hits_cmd(parser & p) {
return module::declare_hits(p.env()); return module::declare_hits(p.env());
} }
static environment compose_cmd(parser & p) {
name g = p.check_constant_next("invalid #compose command, constant expected");
name f = p.check_constant_next("invalid #compose command, constant expected");
optional<name> gf;
if (p.curr_is_token(get_arrow_tk())) {
p.next();
name id = p.check_id_next("invalid #compose command, identifier expected");
gf = get_namespace(p.env()) + id;
}
auto r = compose(p.env(), g, f, gf);
p.regular_stream() << ">> " << r.second << "\n";
return r.first;
}
void init_cmd_table(cmd_table & r) { void init_cmd_table(cmd_table & r) {
add_cmd(r, cmd_info("open", "create aliases for declarations, and use objects defined in other namespaces", add_cmd(r, cmd_info("open", "create aliases for declarations, and use objects defined in other namespaces",
open_cmd)); open_cmd));
@ -997,7 +983,6 @@ void init_cmd_table(cmd_table & r) {
add_cmd(r, cmd_info("#erase_cache", "erase cached definition (for debugging purposes)", erase_cache_cmd)); add_cmd(r, cmd_info("#erase_cache", "erase cached definition (for debugging purposes)", erase_cache_cmd));
add_cmd(r, cmd_info("#projections", "generate projections for inductive datatype (for debugging purposes)", projections_cmd)); add_cmd(r, cmd_info("#projections", "generate projections for inductive datatype (for debugging purposes)", projections_cmd));
add_cmd(r, cmd_info("#telescope_eq", "(for debugging purposes)", telescope_eq_cmd)); add_cmd(r, cmd_info("#telescope_eq", "(for debugging purposes)", telescope_eq_cmd));
add_cmd(r, cmd_info("#compose", "(for debugging purposes)", compose_cmd));
register_decl_cmds(r); register_decl_cmds(r);
register_inductive_cmd(r); register_inductive_cmd(r);
register_structure_cmd(r); register_structure_cmd(r);

View file

@ -181,7 +181,7 @@ environment decl_attributes::apply(environment env, io_state const & ios, name c
} }
} }
if (m_is_coercion) if (m_is_coercion)
env = add_coercion(env, d, ios, m_persistent); env = add_coercion(env, ios, d, m_persistent);
auto decl = env.find(d); auto decl = env.find(d);
if (decl && decl->is_definition()) { if (decl && decl->is_definition()) {
if (m_is_reducible) if (m_is_reducible)

View file

@ -818,7 +818,7 @@ struct structure_cmd_fn {
save_def_info(coercion_name); save_def_info(coercion_name);
add_alias(coercion_name); add_alias(coercion_name);
if (!m_private_parents[i]) { if (!m_private_parents[i]) {
m_env = add_coercion(m_env, coercion_name, m_p.ios()); m_env = add_coercion(m_env, m_p.ios(), coercion_name);
if (m_modifiers.is_class() && is_class(m_env, parent_name)) { if (m_modifiers.is_class() && is_class(m_env, parent_name)) {
// if both are classes, then we also mark coercion_name as an instance // if both are classes, then we also mark coercion_name as an instance
m_env = add_trans_instance(m_env, coercion_name); m_env = add_trans_instance(m_env, coercion_name);

View file

@ -120,7 +120,7 @@ void init_token_table(token_table & t) {
"add_begin_end_tactic", "set_begin_end_tactic", "instance", "class", "add_begin_end_tactic", "set_begin_end_tactic", "instance", "class",
"multiple_instances", "find_decl", "attribute", "persistent", "multiple_instances", "find_decl", "attribute", "persistent",
"include", "omit", "migrate", "init_quotient", "init_hits", "#erase_cache", "#projections", "#telescope_eq", "include", "omit", "migrate", "init_quotient", "init_hits", "#erase_cache", "#projections", "#telescope_eq",
"#compose", nullptr}; nullptr};
pair<char const *, char const *> aliases[] = pair<char const *, char const *> aliases[] =
{{g_lambda_unicode, "fun"}, {"forall", "Pi"}, {g_forall_unicode, "Pi"}, {g_pi_unicode, "Pi"}, {{g_lambda_unicode, "fun"}, {"forall", "Pi"}, {g_forall_unicode, "Pi"}, {g_pi_unicode, "Pi"},

View file

@ -9,121 +9,219 @@ Author: Leonardo de Moura
#include "util/rb_map.h" #include "util/rb_map.h"
#include "util/sstream.h" #include "util/sstream.h"
#include "kernel/instantiate.h" #include "kernel/instantiate.h"
#include "library/tc_multigraph.h"
#include "library/coercion.h" #include "library/coercion.h"
#include "library/reducible.h"
#include "library/protected.h"
#include "library/module.h" #include "library/module.h"
#include "library/kernel_serializer.h" #include "library/kernel_serializer.h"
#include "library/kernel_bindings.h"
#include "library/scoped_ext.h" #include "library/scoped_ext.h"
namespace lean { namespace lean {
enum class coercion_class_kind { User, Sort, Fun };
/**
\brief A coercion is a mapping between classes.
We support three kinds of classes: User, Sort, Function.
*/
class coercion_class {
name m_name; // relevant only if m_kind == User
coercion_class(name const & n): m_name(n) {}
public:
coercion_class();
static coercion_class mk_user(name n);
static coercion_class mk_sort();
static coercion_class mk_fun();
friend bool operator==(coercion_class const & c1, coercion_class const & c2) { return c1.m_name == c2.m_name; }
friend bool operator!=(coercion_class const & c1, coercion_class const & c2) { return c1.m_name != c2.m_name; }
coercion_class_kind kind() const;
name get_name() const { return m_name; }
};
static name * g_fun = nullptr; static name * g_fun = nullptr;
static name * g_sort = nullptr; static name * g_sort = nullptr;
coercion_class::coercion_class():m_name(*g_sort) {} struct coercion_entry {
name m_from;
coercion_class coercion_class::mk_user(name n) { return coercion_class(n); } name m_coe;
coercion_class coercion_class::mk_sort() { return coercion_class(*g_sort); } unsigned m_num_args;
coercion_class coercion_class::mk_fun() { return coercion_class(*g_fun); } name m_to;
coercion_entry() {}
coercion_class_kind coercion_class::kind() const { coercion_entry(name const & from, name const & coe, unsigned num, name const & to):
if (m_name == *g_sort) return coercion_class_kind::Sort; m_from(from), m_coe(coe), m_num_args(num), m_to(to) {}
else if (m_name == *g_fun) return coercion_class_kind::Fun;
else return coercion_class_kind::User;
}
std::ostream & operator<<(std::ostream & out, coercion_class const & cls) {
switch (cls.kind()) {
case coercion_class_kind::User: out << cls.get_name(); break;
case coercion_class_kind::Sort: out << "Sort-class"; break;
case coercion_class_kind::Fun: out << "Function-class"; break;
}
return out;
}
struct coercion_class_cmp_fn {
int operator()(coercion_class const & c1, coercion_class const & c2) const {
return quick_cmp(c1.get_name(), c2.get_name());
}
};
struct coercion_info {
expr m_fun;
expr m_fun_type;
level_param_names m_level_params;
unsigned m_num_args;
coercion_class m_to;
coercion_info() {}
coercion_info(expr const & f, expr const & f_type, level_param_names const & ls, unsigned num, coercion_class const & cls):
m_fun(f), m_fun_type(f_type), m_level_params(ls), m_num_args(num), m_to(cls) {}
}; };
struct coercion_state { struct coercion_state {
name_map<list<coercion_info>> m_coercion_info; tc_multigraph m_graph;
// m_from and m_to contain "direct" coercions name_map<pair<name, unsigned>> m_coercions; // map coercion -> (from-class, num-args)
typedef std::tuple<coercion_class, expr, expr> from_data;
name_map<list<from_data>> m_from; // map user-class -> list of (class, coercion-fun)
rb_map<coercion_class, list<name>, coercion_class_cmp_fn> m_to;
name_map<pair<name, unsigned>> m_coercions; // map coercion -> (from-class, num-args) void add1(environment const & env, name const & from, name const & coe, unsigned num, name const & to) {
m_coercions.insert(coe, mk_pair(from, num));
template<typename F> m_graph.add1(env, from, coe, to);
void for_each_info(name const & from, coercion_class const & to, F && f) {
auto it = m_coercion_info.find(from);
lean_assert(it);
for (coercion_info info : *it) {
if (info.m_to == to) {
f(info);
}
}
} }
void update_from_to(type_checker & tc, name const & C, coercion_class const & D, coercion_state():m_graph("coercion") {}
expr const & f, expr const & f_type, io_state const & ios) { };
auto it1 = m_from.find(C);
if (!it1) { static name * g_class_name = nullptr;
m_from.insert(C, to_list(from_data(D, f, f_type))); static std::string * g_key = nullptr;
} else {
coercion_class D_it; expr f_it, f_type_it; struct coercion_config {
auto it = it1->begin(); typedef coercion_state state;
auto end = it1->end(); typedef coercion_entry entry;
for (; it != end; ++it) { static void add_entry(environment const & env, io_state const &, state & s, entry const & e) {
std::tie(D_it, f_it, f_type_it) = *it; s.add1(env, e.m_from, e.m_coe, e.m_num_args, e.m_to);
if (D_it == D && tc.is_def_eq(f_type_it, f_type).first) }
break; static name const & get_class_name() {
} return *g_class_name;
if (it == end) }
m_from.insert(C, cons(from_data(D, f, f_type), *it1)); static std::string const & get_serialization_key() {
else if (std::get<1>(*it) != f) return *g_key;
ios.get_diagnostic_channel() << "replacing the coercion from '" << C << "' to '" << D << "'\n"; }
} static void write_entry(serializer & s, entry const & e) {
auto it2 = m_to.find(D); s << e.m_from << e.m_coe << e.m_num_args << e.m_to;
if (!it2) }
m_to.insert(D, to_list(C)); static entry read_entry(deserializer & d) {
else if (std::find(it2->begin(), it2->end(), C) == it2->end()) entry e;
m_to.insert(D, cons(C, *it2)); d >> e.m_from >> e.m_coe >> e.m_num_args >> e.m_to;
return e;
}
static optional<unsigned> get_fingerprint(entry const & e) {
return some(e.m_coe.hash());
} }
}; };
template class scoped_ext<coercion_config>;
typedef scoped_ext<coercion_config> coercion_ext;
void initialize_coercion() {
name p = name::mk_internal_unique_name();
g_fun = new name(p, "Fun");
g_sort = new name(p, "Sort");
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;
delete g_fun;
delete g_sort;
}
optional<pair<name, unsigned>> is_coercion(environment const & env, name const & f) {
coercion_state const & ext = coercion_ext::get_state(env);
if (auto it = ext.m_coercions.find(f))
return optional<pair<name, unsigned>>(*it);
else
return optional<pair<name, unsigned>>();
}
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));
}
bool has_coercions_to(environment const & env, name const & D) {
coercion_state const & ext = coercion_ext::get_state(env);
return !is_nil(ext.m_graph.get_predecessors(D));
}
bool has_coercions_to_sort(environment const & env) {
coercion_state const & ext = coercion_ext::get_state(env);
return !is_nil(ext.m_graph.get_predecessors(*g_sort));
}
bool has_coercions_to_fun(environment const & env) {
coercion_state const & ext = coercion_ext::get_state(env);
return !is_nil(ext.m_graph.get_predecessors(*g_fun));
}
bool has_coercions_from(environment const & env, name const & C) {
coercion_state const & ext = coercion_ext::get_state(env);
return !is_nil(ext.m_graph.get_successors(C));
}
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;
coercion_state const & ext = coercion_ext::get_state(env);
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;
}
static list<expr> get_coercions_core(environment const & env, expr const & C, name const & D) {
buffer<expr> args;
expr const & C_fn = get_app_rev_args(C, args);
if (!is_constant(C_fn))
return list<expr>();
coercion_state const & ext = coercion_ext::get_state(env);
buffer<expr> r;
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;
}
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()));
}
return to_list(r);
}
list<expr> get_coercions(environment const & env, expr const & C, name const & D) {
return get_coercions_core(env, C, D);
}
list<expr> get_coercions_to_sort(environment const & env, expr const & C) {
return get_coercions_core(env, C, *g_sort);
}
list<expr> get_coercions_to_fun(environment const & env, expr const & C) {
return get_coercions_core(env, C, *g_fun);
}
bool get_coercions_from(environment const & env, expr const & C, buffer<expr> & result) {
buffer<expr> args;
expr const & C_fn = get_app_rev_args(C, args);
if (!is_constant(C_fn))
return false;
coercion_state const & ext = coercion_ext::get_state(env);
bool r = false;
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;
}
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;
}
return r;
}
void for_each_coercion_user(environment const & env, coercion_user_fn const & f) {
tc_multigraph const & g = coercion_ext::get_state(env).m_graph;
g.for_each(f);
}
void for_each_coercion_sort(environment const & env, coercion_sort_fn const & f) {
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);
});
}
void for_each_coercion_fun(environment const & env, coercion_fun_fn const & f) {
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);
});
}
static void check_pi(name const & f, expr const & t) { static void check_pi(name const & f, expr const & t) {
if (!is_pi(t)) if (!is_pi(t))
throw exception(sstream() << "invalid coercion, '" << f << "' is not function"); throw exception(sstream() << "invalid coercion, '" << f << "' is not function");
@ -162,160 +260,59 @@ static bool check_levels(levels ls, level_param_names ps) {
return is_nil(ls) && is_nil(ps); return is_nil(ls) && is_nil(ps);
} }
optional<coercion_class> type_to_coercion_class(expr const & t) { static optional<name> type_to_coercion_class(expr const & t) {
if (is_sort(t)) { if (is_sort(t)) {
return optional<coercion_class>(coercion_class::mk_sort()); return optional<name>(*g_sort);
} else if (is_pi(t)) { } else if (is_pi(t)) {
return optional<coercion_class>(coercion_class::mk_fun()); return optional<name>(*g_fun);
} else { } else {
expr const & C = get_app_fn(t); expr const & C = get_app_fn(t);
if (is_constant(C)) if (is_constant(C))
return optional<coercion_class>(coercion_class::mk_user(const_name(C))); return optional<name>(const_name(C));
else else
return optional<coercion_class>(); return optional<name>();
} }
} }
typedef std::tuple<name, coercion_class, expr> arrow; static bool is_user_class(name const & cls) {
typedef list<arrow> arrows; return cls != *g_fun && cls != *g_sort;
static bool contains(type_checker & tc, arrows const & as, name const & C, coercion_class const & D, expr const & f_type) {
name C_it; coercion_class D_it; expr f_type_it;
for (arrow const & a : as) {
std::tie(C_it, D_it, f_type_it) = a;
if (C == C_it && D == D_it && tc.is_def_eq(f_type_it, f_type).first)
return true;
}
return false;
}
static arrows insert(arrows const & a, name const & C, coercion_class const & D, expr const & f_type) {
return arrows(arrow(C, D, f_type), a);
} }
struct add_coercion_fn { static unsigned get_num_args(environment const & env, tc_edge const & new_coe) {
type_checker m_tc; declaration const & d = env.get(new_coe.m_cnst);
coercion_state m_state; unsigned num = 0;
arrows m_visited; buffer<expr> args;
io_state const & m_ios; expr t = d.get_type();
while (true) {
void add_coercion_trans(name const & C, if (!is_pi(t))
level_param_names const & f_level_params, expr const & f, expr const & f_type, unsigned f_num_args, return num;
level_param_names const & g_level_params, expr g, expr const & g_type, unsigned g_num_args, expr fn = get_app_fn(binding_domain(t));
coercion_class const & g_class) { if (is_constant(fn) && const_name(fn) == new_coe.m_from)
expr t = f_type; return num;
buffer<expr> args; t = binding_body(t);
for (unsigned i = 0; i <= f_num_args; i++) num++;
args.push_back(mk_var(i));
expr f_app = apply_beta(f, args.size(), args.data());
buffer<name> f_arg_names;
buffer<expr> f_arg_types;
while (is_pi(t)) {
f_arg_names.push_back(binding_name(t));
f_arg_types.push_back(binding_domain(t));
t = binding_body(t);
}
expr D_app = t;
buffer<expr> gf_args;
gf_args.push_back(f_app);
expr D_cnst = get_app_rev_args(D_app, gf_args);
if (gf_args.size() != g_num_args + 1)
return;
if (length(const_levels(D_cnst)) != length(g_level_params))
return;
// C >-> D >-> E
g = instantiate_univ_params(g, g_level_params, const_levels(D_cnst));
expr gf = apply_beta(g, gf_args.size(), gf_args.data());
expr gf_type = g_type;
while (is_pi(gf_type))
gf_type = binding_body(gf_type);
gf_type = instantiate(instantiate_univ_params(gf_type, g_level_params,
const_levels(D_cnst)), gf_args.size(), gf_args.data());
unsigned i = f_arg_types.size();
while (i > 0) {
--i;
gf = mk_lambda(f_arg_names[i], f_arg_types[i], gf);
gf_type = mk_pi(f_arg_names[i], f_arg_types[i], gf_type);
}
add_coercion(C, gf, gf_type, f_level_params, f_num_args, g_class);
} }
}
void add_coercion_trans_to(name const & C, expr const & f, expr const & f_type, static environment add_coercion_core(environment const & env,
level_param_names const & ls, unsigned num_args, coercion_class const & cls) { name const & from, name const & coe, unsigned num_args, name const & to,
// apply transitivity using ext.m_to bool persistent) {
coercion_class C_cls = coercion_class::mk_user(C); coercion_state st = coercion_ext::get_state(env);
auto it1 = m_state.m_to.find(C_cls); pair<environment, list<tc_edge>> new_env_coes = st.m_graph.add(env, from, coe, to);
if (!it1) environment new_env = new_env_coes.first;
return; new_env = coercion_ext::add_entry(new_env, get_dummy_ios(), coercion_entry(from, coe, num_args, to), persistent);
for (name const & B : *it1) { for (tc_edge const & new_coe : new_env_coes.second) {
m_state.for_each_info(B, C_cls, [&](coercion_info const & info) { unsigned nargs = get_num_args(new_env, new_coe);
// B >-> C >-> D new_env = coercion_ext::add_entry(new_env, get_dummy_ios(),
add_coercion_trans(B, info.m_level_params, info.m_fun, info.m_fun_type, info.m_num_args, coercion_entry(new_coe.m_from, new_coe.m_cnst, nargs, new_coe.m_to), persistent);
ls, f, f_type, num_args, cls); 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;
}
void add_coercion_trans_from(name const & C, expr const & f, expr const & f_type, static environment add_coercion(environment const & env, io_state const & ios,
level_param_names const & ls, unsigned num_args, coercion_class const & cls) { name const & f, name const & C, bool persistent) {
// apply transitivity using ext.m_from
if (cls.kind() != coercion_class_kind::User)
return; // nothing to do Sort and Fun classes are terminal
name const & D = cls.get_name();
auto it = m_state.m_from.find(D);
if (!it)
return;
for (auto const & p : *it) {
coercion_class E = std::get<0>(p);
m_state.for_each_info(D, E, [&](coercion_info const & info) {
// C >-> D >-> E
add_coercion_trans(C, ls, f, f_type, num_args,
info.m_level_params, info.m_fun, info.m_fun_type, info.m_num_args, info.m_to);
});
}
}
void add_coercion_core(name const & C, expr const & f, expr const & f_type,
level_param_names const & ls, unsigned num_args, coercion_class const & cls) {
auto it = m_state.m_coercion_info.find(C);
if (!it) {
list<coercion_info> infos(coercion_info(f, f_type, ls, num_args, cls));
m_state.m_coercion_info.insert(C, infos);
} else {
list<coercion_info> infos = *it;
infos = filter(infos, [&](coercion_info const & info) {
return info.m_to != cls || !m_tc.is_def_eq(info.m_fun_type, f_type).first;
});
infos = cons(coercion_info(f, f_type, ls, num_args, cls), infos);
m_state.m_coercion_info.insert(C, infos);
}
if (is_constant(f))
m_state.m_coercions.insert(const_name(f), mk_pair(C, num_args));
}
void add_coercion(name const & C, expr const & f, expr const & f_type,
level_param_names const & ls, unsigned num_args, coercion_class const & cls) {
if (contains(m_tc, m_visited, C, cls, f_type))
return;
if (cls.kind() == coercion_class_kind::User && cls.get_name() == C)
return;
m_visited = insert(m_visited, C, cls, f_type);
add_coercion_core(C, f, f_type, ls, num_args, cls);
add_coercion_trans_to(C, f, f_type, ls, num_args, cls);
add_coercion_trans_from(C, f, f_type, ls, num_args, cls);
}
add_coercion_fn(environment const & env, coercion_state const & s, io_state const & ios):
m_tc(env), m_state(s), m_ios(ios) {}
coercion_state operator()(name const & C, expr const & f, expr const & f_type,
level_param_names const & ls, unsigned num_args, coercion_class const & cls) {
add_coercion(C, f, f_type, ls, num_args, cls);
m_state.update_from_to(m_tc, C, cls, f, f_type, m_ios);
return m_state;
}
};
coercion_state add_coercion(environment const & env, io_state const & ios, coercion_state const & st,
name const & f, name const & C) {
declaration d = env.get(f); declaration d = env.get(f);
unsigned num = 0; unsigned num = 0;
buffer<expr> args; buffer<expr> args;
@ -329,13 +326,13 @@ coercion_state add_coercion(environment const & env, io_state const & ios, coerc
num == args.size() && num == args.size() &&
check_var_args(args) && check_var_args(args) &&
check_levels(const_levels(C_fn), d.get_univ_params())) { check_levels(const_levels(C_fn), d.get_univ_params())) {
expr fn = mk_constant(f, const_levels(C_fn)); optional<name> cls = type_to_coercion_class(binding_body(t));
optional<coercion_class> cls = type_to_coercion_class(binding_body(t));
if (!cls) if (!cls)
throw exception(sstream() << "invalid coercion, '" << f << "' cannot be used as a coercion from '" << C << "'"); throw exception(sstream() << "invalid coercion, '" << f << "' cannot be used as a coercion from '"
else if (cls->kind() == coercion_class_kind::User && cls->get_name() == C) << C << "'");
else if (is_user_class(*cls) && *cls == C)
throw exception(sstream() << "invalid coercion, '" << f << "' is a coercion from '" << C << "' to itself"); throw exception(sstream() << "invalid coercion, '" << f << "' is a coercion from '" << C << "' to itself");
return add_coercion_fn(env, st, ios)(C, fn, d.get_type(), d.get_univ_params(), num, *cls); return add_coercion_core(env, C, f, num, *cls, persistent);
} }
t = binding_body(t); t = binding_body(t);
num++; num++;
@ -343,60 +340,7 @@ coercion_state add_coercion(environment const & env, io_state const & ios, coerc
} }
} }
static name * g_class_name = nullptr; environment add_coercion(environment const & env, io_state const & ios, name const & f, bool persistent) {
static std::string * g_key = nullptr;
typedef pair<name, name> coercion_entry;
struct coercion_config {
typedef coercion_state state;
typedef coercion_entry entry;
static void add_entry(environment const & env, io_state const & ios, state & s, entry const & e) {
s = add_coercion(env, ios, s, e.first, e.second);
}
static name const & get_class_name() {
return *g_class_name;
}
static std::string const & get_serialization_key() {
return *g_key;
}
static void write_entry(serializer & s, entry const & e) {
s << e.first << e.second;
}
static entry read_entry(deserializer & d) {
entry e;
d >> e.first >> e.second;
return e;
}
static optional<unsigned> get_fingerprint(entry const & e) {
return some(hash(e.first.hash(), e.second.hash()));
}
};
template class scoped_ext<coercion_config>;
typedef scoped_ext<coercion_config> coercion_ext;
void initialize_coercion() {
name p = name::mk_internal_unique_name();
g_fun = new name(p, "Fun");
g_sort = new name(p, "Sort");
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;
delete g_fun;
delete g_sort;
}
environment add_coercion(environment const & env, name const & f, name const & C, io_state const & ios, bool persistent) {
return coercion_ext::add_entry(env, ios, coercion_entry(f, C), persistent);
}
environment add_coercion(environment const & env, name const & f, io_state const & ios, bool persistent) {
declaration d = env.get(f); declaration d = env.get(f);
expr t = d.get_type(); expr t = d.get_type();
check_pi(f, t); check_pi(f, t);
@ -414,10 +358,10 @@ environment add_coercion(environment const & env, name const & f, io_state const
--i; --i;
if (i == 0) { if (i == 0) {
// last alternative // last alternative
return add_coercion(env, f, Cs[i], ios, persistent); return add_coercion(env, ios, f, Cs[i], persistent);
} else { } else {
try { try {
return add_coercion(env, f, Cs[i], ios, persistent); return add_coercion(env, ios, f, Cs[i], persistent);
} catch (exception &) { } catch (exception &) {
// failed, keep trying... // failed, keep trying...
} }
@ -425,142 +369,4 @@ environment add_coercion(environment const & env, name const & f, io_state const
} }
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
} }
optional<pair<name, unsigned>> is_coercion(environment const & env, name const & f) {
coercion_state const & ext = coercion_ext::get_state(env);
if (auto it = ext.m_coercions.find(f))
return optional<pair<name, unsigned>>(*it);
else
return optional<pair<name, unsigned>>();
}
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));
}
bool has_coercions_to(environment const & env, name const & D) {
coercion_state const & ext = coercion_ext::get_state(env);
auto it = ext.m_to.find(coercion_class::mk_user(D));
return it && !is_nil(*it);
}
bool has_coercions_to_sort(environment const & env) {
coercion_state const & ext = coercion_ext::get_state(env);
auto it = ext.m_to.find(coercion_class::mk_sort());
return it && !is_nil(*it);
}
bool has_coercions_to_fun(environment const & env) {
coercion_state const & ext = coercion_ext::get_state(env);
auto it = ext.m_to.find(coercion_class::mk_fun());
return it && !is_nil(*it);
}
bool has_coercions_from(environment const & env, name const & C) {
coercion_state const & ext = coercion_ext::get_state(env);
return ext.m_coercion_info.contains(C);
}
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;
coercion_state const & ext = coercion_ext::get_state(env);
auto it = ext.m_coercion_info.find(const_name(C_fn));
if (!it)
return false;
list<coercion_info> const & cs = *it;
return
head(cs).m_num_args == get_app_num_args(C) &&
length(head(cs).m_level_params) == length(const_levels(C_fn));
}
list<expr> get_coercions(environment const & env, expr const & C, coercion_class const & D) {
buffer<expr> args;
expr const & C_fn = get_app_rev_args(C, args);
if (!is_constant(C_fn))
return list<expr>();
coercion_state const & ext = coercion_ext::get_state(env);
auto it = ext.m_coercion_info.find(const_name(C_fn));
if (!it)
return list<expr>();
buffer<expr> r;
for (coercion_info const & info : *it) {
if (info.m_to == D && info.m_num_args == args.size() && length(info.m_level_params) == length(const_levels(C_fn))) {
expr f = instantiate_univ_params(info.m_fun, info.m_level_params, const_levels(C_fn));
r.push_back(apply_beta(f, args.size(), args.data()));
}
}
return to_list(r.begin(), r.end());
}
list<expr> get_coercions(environment const & env, expr const & C, name const & D) {
return get_coercions(env, C, coercion_class::mk_user(D));
}
list<expr> get_coercions_to_sort(environment const & env, expr const & C) {
return get_coercions(env, C, coercion_class::mk_sort());
}
list<expr> get_coercions_to_fun(environment const & env, expr const & C) {
return get_coercions(env, C, coercion_class::mk_fun());
}
bool get_coercions_from(environment const & env, expr const & C, buffer<expr> & result) {
buffer<expr> args;
expr const & C_fn = get_app_rev_args(C, args);
if (!is_constant(C_fn))
return false;
coercion_state const & ext = coercion_ext::get_state(env);
auto it = ext.m_coercion_info.find(const_name(C_fn));
if (!it)
return false;
bool r = false;
for (coercion_info const & info : *it) {
if (info.m_num_args == args.size() &&
length(info.m_level_params) == length(const_levels(C_fn))) {
expr f = instantiate_univ_params(info.m_fun, info.m_level_params, const_levels(C_fn));
expr c = apply_beta(f, args.size(), args.data());
expr t = instantiate_univ_params(info.m_fun_type, info.m_level_params, const_levels(C_fn));
for (unsigned i = 0; i < args.size(); i++) t = binding_body(t);
t = instantiate(t, args.size(), args.data());
result.push_back(c);
r = true;
}
}
return r;
}
template<typename F>
void for_each_coercion(environment const & env, F && f) {
coercion_state const & ext = coercion_ext::get_state(env);
ext.m_coercion_info.for_each([&](name const & C, list<coercion_info> const & infos) {
for (auto const & info : infos) {
f(C, info);
}
});
}
void for_each_coercion_user(environment const & env, coercion_user_fn const & f) {
for_each_coercion(env, [&](name const & C, coercion_info const & info) {
if (info.m_to.kind() == coercion_class_kind::User)
f(C, info.m_to.get_name(), info.m_fun, info.m_level_params, info.m_num_args);
});
}
void for_each_coercion_sort(environment const & env, coercion_sort_fn const & f) {
for_each_coercion(env, [&](name const & C, coercion_info const & info) {
if (info.m_to.kind() == coercion_class_kind::Sort)
f(C, info.m_fun, info.m_level_params, info.m_num_args);
});
}
void for_each_coercion_fun(environment const & env, coercion_fun_fn const & f) {
for_each_coercion(env, [&](name const & C, coercion_info const & info) {
if (info.m_to.kind() == coercion_class_kind::Fun)
f(C, info.m_fun, info.m_level_params, info.m_num_args);
});
}
} }

View file

@ -38,7 +38,7 @@ namespace lean {
\remark if persistent == true, then coercion is saved in .olean files \remark if persistent == true, then coercion is saved in .olean files
*/ */
environment add_coercion(environment const & env, name const & f, io_state const & ios, bool persistent = true); environment add_coercion(environment const & env, io_state const & ios, name const & f, bool persistent = true);
/** \brief If \c f is a coercion, then return the name of the 'from-class' and the number of /** \brief If \c f is a coercion, then return the name of the 'from-class' and the number of
class parameters. class parameters.
*/ */
@ -67,9 +67,9 @@ list<expr> get_coercions_to_fun(environment const & env, expr const & C);
*/ */
bool get_coercions_from(environment const & env, expr const & C, buffer<expr> & result); bool get_coercions_from(environment const & env, expr const & C, buffer<expr> & result);
typedef std::function<void(name const &, name const &, expr const &, level_param_names const &, unsigned)> coercion_user_fn; typedef std::function<void(name const &, name const &, name const &)> coercion_user_fn;
typedef std::function<void(name const &, expr const &, level_param_names const &, unsigned)> coercion_sort_fn; typedef std::function<void(name const &, name const &)> coercion_sort_fn;
typedef std::function<void(name const &, expr const &, level_param_names const &, unsigned)> coercion_fun_fn; typedef std::function<void(name const &, name const &)> coercion_fun_fn;
void for_each_coercion_user(environment const & env, coercion_user_fn const & f); void for_each_coercion_user(environment const & env, coercion_user_fn const & f);
void for_each_coercion_sort(environment const & env, coercion_sort_fn const & f); void for_each_coercion_sort(environment const & env, coercion_sort_fn const & f);
void for_each_coercion_fun(environment const & env, coercion_fun_fn const & f); void for_each_coercion_fun(environment const & env, coercion_fun_fn const & f);

View file

@ -186,4 +186,12 @@ list<name> tc_multigraph::get_predecessors(name const & c) const {
else else
return list<name>(); return list<name>();
} }
void tc_multigraph::for_each(std::function<void(name const &, name const &, name const &)> const & fn) const {
m_successors.for_each([&](name const & from, list<pair<name, name>> const & succs) {
for (pair<name, name> const & p : succs) {
fn(from, p.first, p.second);
}
});
}
} }

View file

@ -33,5 +33,6 @@ public:
bool is_node(name const & c) const; bool is_node(name const & c) const;
list<pair<name, name>> get_successors(name const & c) const; list<pair<name, name>> get_successors(name const & c) const;
list<name> get_predecessors(name const & c) const; list<name> get_predecessors(name const & c) const;
void for_each(std::function<void(name const &, name const &, name const &)> const & fn) const;
}; };
} }

View file

@ -1,24 +0,0 @@
import data.real data.vector data.list
open nat int rat
#compose int.of_nat nat.of_num → num_to_int
#compose int.of_nat nat.of_num → num_to_int_2
set_option pp.all true
print num_to_int
check num_to_int
check num_to_int_2 -- Error
constant to_list {A : Type} {n : nat} : vector A n → list A
constant to_vector {A : Type} (l : list A) : vector A (list.length l)
constant matrix.{l} : Type.{l} → nat → nat → Type.{l}
constant to_matrix {A : Type} {n : nat} : vector A n → matrix A n 1
#compose list.length to_list → vec_len
#compose to_matrix to_vector → list_to_matrix
#compose to_matrix to_vector → list_to_matrix_2
print vec_len
print list_to_matrix
check list_to_matrix_2 -- Error

View file

@ -1,14 +0,0 @@
>> num_to_int
>> num_to_int
definition num_to_int : num → int :=
λ (n : num), int.of_nat (nat.of_num n)
num_to_int : num → int
comp1.lean:11:6: error: unknown identifier 'num_to_int_2'
>> vec_len
>> list_to_matrix
>> list_to_matrix
definition vec_len : Π {A : Type.{l_1}} {n : nat}, vector.{l_1} A n → nat :=
λ {A : Type.{l_1}} {n : nat} (a : vector.{l_1} A n), @list.length.{l_1} A (@to_list.{l_1} A n a)
definition list_to_matrix : Π {A : Type.{l_1}} (l : list.{l_1} A), matrix.{l_1} A (@list.length.{l_1} A l) (nat.of_num 1) :=
λ {A : Type.{l_1}} (l : list.{l_1} A), @to_matrix.{l_1} A (@list.length.{l_1} A l) (@to_vector.{l_1} A l)
comp1.lean:24:6: error: unknown identifier 'list_to_matrix_2'

View file

@ -3,4 +3,4 @@ f a b : A
nat ↣ bool : foo nat ↣ bool : foo
bla : num bla : num
local_notation_bug.lean:22:8: error: invalid expression local_notation_bug.lean:22:8: error: invalid expression
num ↣ nat : of_num num ↣ nat : nat.of_num