feat(frontends/lean): add new option (elaborator.coercions) for disabling coercions
This commit is contained in:
parent
9bedbbb739
commit
fa3baed701
5 changed files with 105 additions and 39 deletions
|
@ -467,9 +467,12 @@ pair<expr, expr> elaborator::ensure_fun(expr f, constraint_seq & cs) {
|
||||||
erase_coercion_info(f);
|
erase_coercion_info(f);
|
||||||
} else {
|
} else {
|
||||||
cs = saved_cs;
|
cs = saved_cs;
|
||||||
f_type = m_coercion_from_tc->whnf(saved_f_type, cs);
|
list<expr> coes;
|
||||||
// try coercion to function-class
|
// try coercion to function-class IF coercions are enabled
|
||||||
list<expr> coes = get_coercions_to_fun(env(), f_type);
|
if (m_ctx.m_coercions) {
|
||||||
|
f_type = m_coercion_from_tc->whnf(saved_f_type, cs);
|
||||||
|
coes = get_coercions_to_fun(env(), f_type);
|
||||||
|
}
|
||||||
if (is_nil(coes)) {
|
if (is_nil(coes)) {
|
||||||
throw_kernel_exception(env(), f, [=](formatter const & fmt) { return pp_function_expected(fmt, f); });
|
throw_kernel_exception(env(), f, [=](formatter const & fmt) { return pp_function_expected(fmt, f); });
|
||||||
} else if (is_nil(tail(coes))) {
|
} else if (is_nil(tail(coes))) {
|
||||||
|
@ -513,6 +516,8 @@ pair<expr, expr> elaborator::ensure_fun(expr f, constraint_seq & cs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool elaborator::has_coercions_from(expr const & a_type, bool & lifted_coe) {
|
bool elaborator::has_coercions_from(expr const & a_type, bool & lifted_coe) {
|
||||||
|
if (!m_ctx.m_coercions)
|
||||||
|
return false;
|
||||||
try {
|
try {
|
||||||
expr a_cls = get_app_fn(m_coercion_from_tc->whnf(a_type).first);
|
expr a_cls = get_app_fn(m_coercion_from_tc->whnf(a_type).first);
|
||||||
if (m_ctx.m_lift_coercions) {
|
if (m_ctx.m_lift_coercions) {
|
||||||
|
@ -529,6 +534,8 @@ bool elaborator::has_coercions_from(expr const & a_type, bool & lifted_coe) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool elaborator::has_coercions_to(expr d_type) {
|
bool elaborator::has_coercions_to(expr d_type) {
|
||||||
|
if (!m_ctx.m_coercions)
|
||||||
|
return false;
|
||||||
try {
|
try {
|
||||||
d_type = m_coercion_to_tc->whnf(d_type).first;
|
d_type = m_coercion_to_tc->whnf(d_type).first;
|
||||||
expr const & fn = get_app_fn(d_type);
|
expr const & fn = get_app_fn(d_type);
|
||||||
|
@ -546,10 +553,15 @@ bool elaborator::has_coercions_to(expr d_type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pair<expr, constraint_seq> elaborator::apply_coercion(expr const & a, expr a_type, expr d_type, justification const & j) {
|
pair<expr, constraint_seq> elaborator::apply_coercion(expr const & a, expr a_type, expr d_type, justification const & j) {
|
||||||
|
if (!m_ctx.m_coercions) {
|
||||||
|
erase_coercion_info(a);
|
||||||
|
return to_ecs(a);
|
||||||
|
}
|
||||||
a_type = m_coercion_from_tc->whnf(a_type).first;
|
a_type = m_coercion_from_tc->whnf(a_type).first;
|
||||||
d_type = m_coercion_to_tc->whnf(d_type).first;
|
d_type = m_coercion_to_tc->whnf(d_type).first;
|
||||||
constraint_seq aux_cs;
|
constraint_seq aux_cs;
|
||||||
list<expr> coes = get_coercions_from_to(*m_coercion_from_tc, *m_coercion_to_tc, a_type, d_type, aux_cs, m_ctx.m_lift_coercions);
|
list<expr> coes = get_coercions_from_to(*m_coercion_from_tc, *m_coercion_to_tc, a_type,
|
||||||
|
d_type, aux_cs, m_ctx.m_lift_coercions);
|
||||||
if (is_nil(coes)) {
|
if (is_nil(coes)) {
|
||||||
erase_coercion_info(a);
|
erase_coercion_info(a);
|
||||||
return to_ecs(a);
|
return to_ecs(a);
|
||||||
|
@ -604,41 +616,45 @@ pair<expr, constraint_seq> elaborator::mk_delayed_coercion(
|
||||||
pair<expr, constraint_seq> elaborator::ensure_has_type_core(
|
pair<expr, constraint_seq> elaborator::ensure_has_type_core(
|
||||||
expr const & a, expr const & a_type, expr const & expected_type, bool use_expensive_coercions, justification const & j) {
|
expr const & a, expr const & a_type, expr const & expected_type, bool use_expensive_coercions, justification const & j) {
|
||||||
bool lifted_coe = false;
|
bool lifted_coe = false;
|
||||||
if (use_expensive_coercions &&
|
if (m_ctx.m_coercions) {
|
||||||
has_coercions_from(a_type, lifted_coe) && ((!lifted_coe && is_meta(expected_type)) || (lifted_coe && is_pi_meta(expected_type)))) {
|
if (use_expensive_coercions &&
|
||||||
return mk_delayed_coercion(a, a_type, expected_type, j);
|
has_coercions_from(a_type, lifted_coe) && ((!lifted_coe && is_meta(expected_type)) || (lifted_coe && is_pi_meta(expected_type)))) {
|
||||||
} else if (use_expensive_coercions && !m_in_equation_lhs && is_meta(a_type) && has_coercions_to(expected_type)) {
|
return mk_delayed_coercion(a, a_type, expected_type, j);
|
||||||
return mk_delayed_coercion(a, a_type, expected_type, j);
|
} else if (use_expensive_coercions && !m_in_equation_lhs && is_meta(a_type) && has_coercions_to(expected_type)) {
|
||||||
} else {
|
return mk_delayed_coercion(a, a_type, expected_type, j);
|
||||||
pair<bool, constraint_seq> dcs;
|
|
||||||
try {
|
|
||||||
dcs = m_tc->is_def_eq(a_type, expected_type, j);
|
|
||||||
} catch (exception&) {
|
|
||||||
dcs.first = false;
|
|
||||||
}
|
}
|
||||||
if (dcs.first) {
|
}
|
||||||
return to_ecs(a, dcs.second);
|
|
||||||
|
pair<bool, constraint_seq> dcs;
|
||||||
|
try {
|
||||||
|
dcs = m_tc->is_def_eq(a_type, expected_type, j);
|
||||||
|
} catch (exception&) {
|
||||||
|
dcs.first = false;
|
||||||
|
}
|
||||||
|
if (dcs.first) {
|
||||||
|
return to_ecs(a, dcs.second);
|
||||||
|
} else {
|
||||||
|
if (!m_ctx.m_coercions)
|
||||||
|
throw unifier_exception(j, substitution());
|
||||||
|
auto new_a_cs = apply_coercion(a, a_type, expected_type, j);
|
||||||
|
expr new_a = new_a_cs.first;
|
||||||
|
constraint_seq cs = new_a_cs.second;
|
||||||
|
bool coercion_worked = false;
|
||||||
|
if (!is_eqp(a, new_a)) {
|
||||||
|
expr new_a_type = infer_type(new_a, cs);
|
||||||
|
try {
|
||||||
|
coercion_worked = m_tc->is_def_eq(new_a_type, expected_type, j, cs);
|
||||||
|
} catch (exception&) {
|
||||||
|
coercion_worked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (coercion_worked) {
|
||||||
|
return to_ecs(new_a, cs);
|
||||||
|
} else if (has_metavar(a_type) || has_metavar(expected_type)) {
|
||||||
|
// rely on unification hints to solve this constraint
|
||||||
|
return to_ecs(a, mk_eq_cnstr(a_type, expected_type, j));
|
||||||
} else {
|
} else {
|
||||||
auto new_a_cs = apply_coercion(a, a_type, expected_type, j);
|
throw unifier_exception(j, substitution());
|
||||||
expr new_a = new_a_cs.first;
|
|
||||||
constraint_seq cs = new_a_cs.second;
|
|
||||||
bool coercion_worked = false;
|
|
||||||
if (!is_eqp(a, new_a)) {
|
|
||||||
expr new_a_type = infer_type(new_a, cs);
|
|
||||||
try {
|
|
||||||
coercion_worked = m_tc->is_def_eq(new_a_type, expected_type, j, cs);
|
|
||||||
} catch (exception&) {
|
|
||||||
coercion_worked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (coercion_worked) {
|
|
||||||
return to_ecs(new_a, cs);
|
|
||||||
} else if (has_metavar(a_type) || has_metavar(expected_type)) {
|
|
||||||
// rely on unification hints to solve this constraint
|
|
||||||
return to_ecs(a, mk_eq_cnstr(a_type, expected_type, j));
|
|
||||||
} else {
|
|
||||||
throw unifier_exception(j, substitution());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -819,8 +835,11 @@ expr elaborator::ensure_type(expr const & e, constraint_seq & cs) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cs = saved_cs;
|
cs = saved_cs;
|
||||||
t = m_coercion_from_tc->whnf(saved_t, cs);
|
list<expr> coes;
|
||||||
list<expr> coes = get_coercions_to_sort(env(), t);
|
if (m_ctx.m_coercions) {
|
||||||
|
t = m_coercion_from_tc->whnf(saved_t, cs);
|
||||||
|
coes = get_coercions_to_sort(env(), t);
|
||||||
|
}
|
||||||
if (is_nil(coes)) {
|
if (is_nil(coes)) {
|
||||||
throw_kernel_exception(env(), e, [=](formatter const & fmt) { return pp_type_expected(fmt, e); });
|
throw_kernel_exception(env(), e, [=](formatter const & fmt) { return pp_type_expected(fmt, e); });
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -28,6 +28,10 @@ Author: Leonardo de Moura
|
||||||
#define LEAN_DEFAULT_ELABORATOR_LIFT_COERCIONS true
|
#define LEAN_DEFAULT_ELABORATOR_LIFT_COERCIONS true
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef LEAN_DEFAULT_ELABORATOR_COERCIONS
|
||||||
|
#define LEAN_DEFAULT_ELABORATOR_COERCIONS true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
@ -37,6 +41,7 @@ static name * g_elaborator_ignore_instances = nullptr;
|
||||||
static name * g_elaborator_flycheck_goals = nullptr;
|
static name * g_elaborator_flycheck_goals = nullptr;
|
||||||
static name * g_elaborator_fail_missing_field = nullptr;
|
static name * g_elaborator_fail_missing_field = nullptr;
|
||||||
static name * g_elaborator_lift_coercions = nullptr;
|
static name * g_elaborator_lift_coercions = nullptr;
|
||||||
|
static name * g_elaborator_coercions = nullptr;
|
||||||
|
|
||||||
name const & get_elaborator_ignore_instances_name() {
|
name const & get_elaborator_ignore_instances_name() {
|
||||||
return *g_elaborator_ignore_instances;
|
return *g_elaborator_ignore_instances;
|
||||||
|
@ -62,6 +67,10 @@ bool get_elaborator_lift_coercions(options const & opts) {
|
||||||
return opts.get_bool(*g_elaborator_lift_coercions, LEAN_DEFAULT_ELABORATOR_LIFT_COERCIONS);
|
return opts.get_bool(*g_elaborator_lift_coercions, LEAN_DEFAULT_ELABORATOR_LIFT_COERCIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get_elaborator_coercions(options const & opts) {
|
||||||
|
return opts.get_bool(*g_elaborator_coercions, LEAN_DEFAULT_ELABORATOR_COERCIONS);
|
||||||
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|
||||||
elaborator_context::elaborator_context(environment const & env, io_state const & ios, local_decls<level> const & lls,
|
elaborator_context::elaborator_context(environment const & env, io_state const & ios, local_decls<level> const & lls,
|
||||||
|
@ -83,6 +92,7 @@ void elaborator_context::set_options(options const & opts) {
|
||||||
m_flycheck_goals = get_elaborator_flycheck_goals(opts);
|
m_flycheck_goals = get_elaborator_flycheck_goals(opts);
|
||||||
m_fail_missing_field = get_elaborator_fail_missing_field(opts);
|
m_fail_missing_field = get_elaborator_fail_missing_field(opts);
|
||||||
m_lift_coercions = get_elaborator_lift_coercions(opts);
|
m_lift_coercions = get_elaborator_lift_coercions(opts);
|
||||||
|
m_coercions = get_elaborator_coercions(opts);
|
||||||
|
|
||||||
if (has_show_goal(opts, m_show_goal_line, m_show_goal_col)) {
|
if (has_show_goal(opts, m_show_goal_line, m_show_goal_col)) {
|
||||||
m_show_goal_at = true;
|
m_show_goal_at = true;
|
||||||
|
@ -131,6 +141,7 @@ void initialize_elaborator_context() {
|
||||||
g_elaborator_flycheck_goals = new name{"elaborator", "flycheck_goals"};
|
g_elaborator_flycheck_goals = new name{"elaborator", "flycheck_goals"};
|
||||||
g_elaborator_fail_missing_field = new name{"elaborator", "fail_if_missing_field"};
|
g_elaborator_fail_missing_field = new name{"elaborator", "fail_if_missing_field"};
|
||||||
g_elaborator_lift_coercions = new name{"elaborator", "lift_coercions"};
|
g_elaborator_lift_coercions = new name{"elaborator", "lift_coercions"};
|
||||||
|
g_elaborator_coercions = new name{"elaborator", "coercions"};
|
||||||
register_bool_option(*g_elaborator_local_instances, LEAN_DEFAULT_ELABORATOR_LOCAL_INSTANCES,
|
register_bool_option(*g_elaborator_local_instances, LEAN_DEFAULT_ELABORATOR_LOCAL_INSTANCES,
|
||||||
"(elaborator) use local declarates as class instances");
|
"(elaborator) use local declarates as class instances");
|
||||||
register_bool_option(*g_elaborator_ignore_instances, LEAN_DEFAULT_ELABORATOR_IGNORE_INSTANCES,
|
register_bool_option(*g_elaborator_ignore_instances, LEAN_DEFAULT_ELABORATOR_IGNORE_INSTANCES,
|
||||||
|
@ -144,6 +155,8 @@ void initialize_elaborator_context() {
|
||||||
register_bool_option(*g_elaborator_lift_coercions, LEAN_DEFAULT_ELABORATOR_LIFT_COERCIONS,
|
register_bool_option(*g_elaborator_lift_coercions, LEAN_DEFAULT_ELABORATOR_LIFT_COERCIONS,
|
||||||
"(elaborator) if true, the elaborator will automatically lift coercions from A to B "
|
"(elaborator) if true, the elaborator will automatically lift coercions from A to B "
|
||||||
"into coercions from (C -> A) to (C -> B)");
|
"into coercions from (C -> A) to (C -> B)");
|
||||||
|
register_bool_option(*g_elaborator_coercions, LEAN_DEFAULT_ELABORATOR_COERCIONS,
|
||||||
|
"(elaborator) if true, the elaborator will automatically introduce coercions");
|
||||||
}
|
}
|
||||||
void finalize_elaborator_context() {
|
void finalize_elaborator_context() {
|
||||||
delete g_elaborator_local_instances;
|
delete g_elaborator_local_instances;
|
||||||
|
@ -151,5 +164,6 @@ void finalize_elaborator_context() {
|
||||||
delete g_elaborator_flycheck_goals;
|
delete g_elaborator_flycheck_goals;
|
||||||
delete g_elaborator_fail_missing_field;
|
delete g_elaborator_fail_missing_field;
|
||||||
delete g_elaborator_lift_coercions;
|
delete g_elaborator_lift_coercions;
|
||||||
|
delete g_elaborator_coercions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ class elaborator_context {
|
||||||
bool m_flycheck_goals;
|
bool m_flycheck_goals;
|
||||||
bool m_fail_missing_field;
|
bool m_fail_missing_field;
|
||||||
bool m_lift_coercions;
|
bool m_lift_coercions;
|
||||||
|
bool m_coercions;
|
||||||
friend class elaborator;
|
friend class elaborator;
|
||||||
|
|
||||||
bool m_show_goal_at;
|
bool m_show_goal_at;
|
||||||
|
|
20
tests/lean/coe2.lean
Normal file
20
tests/lean/coe2.lean
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import data.int
|
||||||
|
open nat int
|
||||||
|
|
||||||
|
variable f : int → int
|
||||||
|
variable a : nat
|
||||||
|
|
||||||
|
constant bv : nat → Type₁
|
||||||
|
attribute bv [coercion]
|
||||||
|
constant g : Π {n : nat}, bv n → bv n
|
||||||
|
|
||||||
|
set_option pp.all true
|
||||||
|
|
||||||
|
check f a
|
||||||
|
check fun x : a, g x
|
||||||
|
|
||||||
|
|
||||||
|
set_option elaborator.coercions false
|
||||||
|
|
||||||
|
check f a -- ERROR
|
||||||
|
check fun x : a, g x -- ERROR
|
12
tests/lean/coe2.lean.expected.out
Normal file
12
tests/lean/coe2.lean.expected.out
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
f (int.of_nat a) : int
|
||||||
|
λ (x : bv a), @g a x : bv a → bv a
|
||||||
|
coe2.lean:19:6: error: type mismatch at application
|
||||||
|
f a
|
||||||
|
term
|
||||||
|
a
|
||||||
|
has type
|
||||||
|
nat
|
||||||
|
but is expected to have type
|
||||||
|
int
|
||||||
|
coe2.lean:20:14: error: type expected at
|
||||||
|
a
|
Loading…
Reference in a new issue