feat(frontends/lean/elaborator): add 'delayed coercions', add example demonstrating why the new feature is useful

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2014-06-26 18:39:23 -07:00
parent ed299c0914
commit ccce9d90a4
2 changed files with 83 additions and 32 deletions

View file

@ -338,15 +338,43 @@ public:
return mk_pair(f, f_type);
}
optional<expr> get_coercion(expr a_type, expr d_type, bool /* is_cast */) {
// TODO(Leo): handle casts, subtypes, ...
bool has_coercions_from(expr const & a_type) {
expr const & a_cls = get_app_fn(whnf(a_type));
return is_constant(a_cls) && ::lean::has_coercions_from(m_env, const_name(a_cls));
}
bool has_coercions_to(expr const & d_type) {
expr const & d_cls = get_app_fn(whnf(d_type));
return is_constant(d_cls) && ::lean::has_coercions_to(m_env, const_name(d_cls));
}
expr apply_coercion(expr const & a, expr a_type, expr d_type) {
a_type = whnf(a_type);
d_type = whnf(d_type);
expr const & d_cls = get_app_fn(d_type);
if (is_constant(d_cls))
return ::lean::get_coercion(m_env, a_type, const_name(d_cls));
else
return none_expr();
if (is_constant(d_cls)) {
if (auto c = get_coercion(m_env, a_type, const_name(d_cls)))
return mk_app(*c, a, a.get_tag());
}
return a;
}
/**
\brief Given an application \c e, where the expected type is d_type, and the argument type is a_type,
create a "delayed coercion". The idea is to create a choice constraint and postpone the coercion
search. We do that whenever d_type or a_type is a metavar application, and d_type or a_type is a coercion source/target.
*/
expr mk_delayed_coercion(expr const & e, expr const & d_type, expr const & a_type) {
expr a = app_arg(e);
expr m = mk_meta(some_expr(d_type), a.get_tag());
auto choice_fn = [=](expr const & new_d_type, substitution const & /* s */, name_generator const & /* ngen */) {
expr r = apply_coercion(a, a_type, new_d_type);
a_choice c(r, justification(), list<constraint>());
return lazy_list<a_choice>(c);
};
justification j = mk_app_justification(m_env, e, d_type, a_type);
add_cnstr(mk_choice_cnstr(m, choice_fn, false, j));
return update_app(e, app_fn(e), m);
}
expr visit_app(expr const & e) {
@ -365,22 +393,22 @@ public:
expr a = visit_expecting_type_of(app_arg(e), d_type);
expr a_type = instantiate_metavars(infer_type(a));
expr r = mk_app(f, a, e.get_tag());
if (is_meta(d_type) && has_coercions_from(a_type)) {
return mk_delayed_coercion(r, d_type, a_type);
} else if (is_meta(a_type) && has_coercions_to(d_type)) {
return mk_delayed_coercion(r, d_type, a_type);
} else {
app_delayed_justification j(m_env, r, f_type, a_type);
if (!m_tc.is_def_eq(a_type, d_type, j)) {
// try coercions
optional<expr> c = get_coercion(a_type, d_type, binding_info(f_type).is_cast());
expr new_a = apply_coercion(a, a_type, d_type);
bool coercion_worked = false;
expr new_a;
if (c) {
new_a = mk_app(*c, a, a.get_tag());
if (!is_eqp(a, new_a)) {
expr new_a_type = instantiate_metavars(infer_type(new_a));
coercion_worked = m_tc.is_def_eq(new_a_type, d_type, j);
} else {
coercion_worked = false;
}
if (coercion_worked) {
a = new_a;
r = mk_app(f, a, e.get_tag());
r = update_app(r, f, new_a);
} else {
if (has_metavar(a_type) || has_metavar(d_type)) {
// rely on unification hints to solve this constraint
@ -396,6 +424,7 @@ public:
}
return r;
}
}
expr visit_placeholder(expr const & e) {
return mk_meta(placeholder_type(e), e.get_tag());

22
tests/lean/run/e17.lean Normal file
View file

@ -0,0 +1,22 @@
inductive nat : Type :=
| zero : nat
| succ : nat → nat
inductive list (A : Type) : Type :=
| nil : list A
| cons : A → list A → list A
inductive int : Type :=
| of_nat : nat → int
| neg : nat → int
coercion of_nat
variables n m : nat
variables i j : int
check cons i (cons i nil)
check cons n (cons n nil)
check cons i (cons n nil)
check cons n (cons i nil)
check cons n (cons i (cons m (cons j nil)))