fix(*): make sure elaborator and type_checker use the same "rules" for treating opaque definitions

This is a big change because we have to store in constraints whether we can use the "relaxed" rules or not.
The "relaxed" case says that when type checking the value of an opaque definition we can treat other opaque definitions in the same module as transparent.

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2014-07-27 12:01:06 -07:00
parent 83d38674c9
commit faee08591f
29 changed files with 303 additions and 214 deletions

View file

@ -86,7 +86,7 @@ environment check_cmd(parser & p) {
level_param_names ls = to_level_param_names(collect_univ_params(e)); level_param_names ls = to_level_param_names(collect_univ_params(e));
level_param_names new_ls; level_param_names new_ls;
std::tie(e, new_ls) = p.elaborate_relaxed(e, ctx); std::tie(e, new_ls) = p.elaborate_relaxed(e, ctx);
auto tc = mk_type_checker_with_hints(p.env(), p.mk_ngen()); auto tc = mk_type_checker_with_hints(p.env(), p.mk_ngen(), true);
expr type = tc->check(e, append(ls, new_ls)); expr type = tc->check(e, append(ls, new_ls));
auto reg = p.regular_stream(); auto reg = p.regular_stream();
formatter const & fmt = reg.get_formatter(); formatter const & fmt = reg.get_formatter();
@ -270,7 +270,7 @@ environment opaque_hint_cmd(parser & p) {
if (p.curr_is_token(g_module)) { if (p.curr_is_token(g_module)) {
found = true; found = true;
p.next(); p.next();
env = set_main_module_opaque_defs(env, hiding); env = set_hide_main_opaque(env, hiding);
} else { } else {
name c = p.check_constant_next("invalid 'opaque_hint', constant expected"); name c = p.check_constant_next("invalid 'opaque_hint', constant expected");
found = true; found = true;

View file

@ -271,11 +271,11 @@ environment definition_cmd_core(parser & p, bool is_theorem, bool _is_opaque) {
std::tie(type, new_ls) = p.elaborate_type(type); std::tie(type, new_ls) = p.elaborate_type(type);
env = module::add(env, check(env, mk_axiom(real_n, append(ls, new_ls), type))); env = module::add(env, check(env, mk_axiom(real_n, append(ls, new_ls), type)));
} else { } else {
std::tie(type, value, new_ls) = p.elaborate_definition(n, type, value); std::tie(type, value, new_ls) = p.elaborate_definition(n, type, value, modifiers.m_is_opaque);
env = module::add(env, check(env, mk_theorem(real_n, append(ls, new_ls), type, value))); env = module::add(env, check(env, mk_theorem(real_n, append(ls, new_ls), type, value)));
} }
} else { } else {
std::tie(type, value, new_ls) = p.elaborate_definition(n, type, value); std::tie(type, value, new_ls) = p.elaborate_definition(n, type, value, modifiers.m_is_opaque);
env = module::add(env, check(env, mk_definition(env, real_n, append(ls, new_ls), type, value, modifiers.m_is_opaque))); env = module::add(env, check(env, mk_definition(env, real_n, append(ls, new_ls), type, value, modifiers.m_is_opaque)));
} }
if (real_n != n) if (real_n != n)

View file

@ -131,7 +131,7 @@ class elaborator {
local_decls<level> m_lls; local_decls<level> m_lls;
io_state m_ios; io_state m_ios;
name_generator m_ngen; name_generator m_ngen;
type_checker_ptr m_tc; type_checker_ptr m_tc[2];
substitution m_subst; substitution m_subst;
expr_map<expr> m_cache; // (pointer equality) cache for Type and Constants (this is a trick to make sure we get the expr_map<expr> m_cache; // (pointer equality) cache for Type and Constants (this is a trick to make sure we get the
// same universe metavariable for different occurrences of the same Type/Constant // same universe metavariable for different occurrences of the same Type/Constant
@ -148,6 +148,7 @@ class elaborator {
name_set m_displayed_errors; // set of metavariables that we already reported unsolved/unassigned name_set m_displayed_errors; // set of metavariables that we already reported unsolved/unassigned
bool m_check_unassigned; // if true if display error messages if elaborated term still contains metavariables bool m_check_unassigned; // if true if display error messages if elaborated term still contains metavariables
bool m_use_local_instances; // if true class-instance resolution will use the local context bool m_use_local_instances; // if true class-instance resolution will use the local context
bool m_relax_main_opaque; // if true, then treat opaque definitions from the main module as transparent
// Set m_ctx to ctx, and make sure m_ctx_buffer and m_ctx_domain_buffer reflect the contents of the new ctx // Set m_ctx to ctx, and make sure m_ctx_buffer and m_ctx_domain_buffer reflect the contents of the new ctx
void set_ctx(context const & ctx) { void set_ctx(context const & ctx) {
@ -173,12 +174,14 @@ class elaborator {
lean_assert(m_main.m_accumulated.is_none()); lean_assert(m_main.m_accumulated.is_none());
m_old_ctx = m_main.m_ctx; m_old_ctx = m_main.m_ctx;
m_main.set_ctx(ctx); m_main.set_ctx(ctx);
m_main.m_tc->push(); m_main.m_tc[0]->push();
m_main.m_tc[1]->push();
m_main.m_subst = s; m_main.m_subst = s;
} }
~scope() { ~scope() {
m_main.set_ctx(m_old_ctx); m_main.set_ctx(m_old_ctx);
m_main.m_tc->pop(); m_main.m_tc[0]->pop();
m_main.m_tc[1]->pop();
m_main.m_constraints.clear(); m_main.m_constraints.clear();
m_main.m_accumulated = justification(); m_main.m_accumulated = justification();
m_main.m_subst = substitution(); m_main.m_subst = substitution();
@ -189,8 +192,9 @@ class elaborator {
/* \brief Move all constraints generated by the type checker to the buffer m_constraints. */ /* \brief Move all constraints generated by the type checker to the buffer m_constraints. */
void consume_tc_cnstrs() { void consume_tc_cnstrs() {
while (auto c = m_tc->next_cnstr()) for (unsigned i = 0; i < 2; i++)
m_constraints.push_back(*c); while (auto c = m_tc[i]->next_cnstr())
m_constraints.push_back(*c);
} }
struct choice_elaborator { struct choice_elaborator {
@ -212,8 +216,9 @@ class elaborator {
context m_ctx; context m_ctx;
substitution m_subst; substitution m_subst;
unsigned m_idx; unsigned m_idx;
choice_expr_elaborator(elaborator & elab, expr const & mvar, expr const & c, context const & ctx, substitution const & s): bool m_relax_main_opaque;
m_elab(elab), m_mvar(mvar), m_choice(c), m_ctx(ctx), m_subst(s), m_idx(0) { choice_expr_elaborator(elaborator & elab, expr const & mvar, expr const & c, context const & ctx, substitution const & s, bool relax):
m_elab(elab), m_mvar(mvar), m_choice(c), m_ctx(ctx), m_subst(s), m_idx(0), m_relax_main_opaque(relax) {
} }
virtual optional<constraints> next() { virtual optional<constraints> next() {
@ -226,7 +231,7 @@ class elaborator {
justification j = m_elab.m_accumulated; justification j = m_elab.m_accumulated;
m_elab.consume_tc_cnstrs(); m_elab.consume_tc_cnstrs();
list<constraint> cs = to_list(m_elab.m_constraints.begin(), m_elab.m_constraints.end()); list<constraint> cs = to_list(m_elab.m_constraints.begin(), m_elab.m_constraints.end());
cs = cons(mk_eq_cnstr(m_mvar, r, j), cs); cs = cons(mk_eq_cnstr(m_mvar, r, j, m_relax_main_opaque), cs);
return optional<constraints>(cs); return optional<constraints>(cs);
} catch (exception &) {} } catch (exception &) {}
} }
@ -263,13 +268,14 @@ class elaborator {
context m_ctx; // local context for m_meta context m_ctx; // local context for m_meta
substitution m_subst; substitution m_subst;
justification m_jst; justification m_jst;
bool m_relax_main_opaque;
placeholder_elaborator(elaborator & elab, expr const & meta, expr const & meta_type, placeholder_elaborator(elaborator & elab, expr const & meta, expr const & meta_type,
list<expr> const & local_insts, list<name> const & instances, list<tactic_hint_entry> const & tacs, list<expr> const & local_insts, list<name> const & instances, list<tactic_hint_entry> const & tacs,
context const & ctx, substitution const & s, justification const & j, bool ignore_failure): context const & ctx, substitution const & s, justification const & j, bool ignore_failure, bool relax):
choice_elaborator(ignore_failure), choice_elaborator(ignore_failure),
m_elab(elab), m_meta(meta), m_meta_type(meta_type), m_local_instances(local_insts), m_instances(instances), m_elab(elab), m_meta(meta), m_meta_type(meta_type), m_local_instances(local_insts), m_instances(instances),
m_tactics(tacs), m_ctx(ctx), m_subst(s), m_jst(j) { m_tactics(tacs), m_ctx(ctx), m_subst(s), m_jst(j), m_relax_main_opaque(relax) {
collect_metavars(meta_type, m_mvars_in_meta_type); collect_metavars(meta_type, m_mvars_in_meta_type);
} }
@ -292,7 +298,7 @@ class elaborator {
for (auto & c : m_elab.m_constraints) for (auto & c : m_elab.m_constraints)
c = update_justification(c, mk_composite1(m_jst, c.get_justification())); c = update_justification(c, mk_composite1(m_jst, c.get_justification()));
list<constraint> cs = to_list(m_elab.m_constraints.begin(), m_elab.m_constraints.end()); list<constraint> cs = to_list(m_elab.m_constraints.begin(), m_elab.m_constraints.end());
cs = cons(mk_eq_cnstr(m_meta, r, mk_composite1(m_jst, j)), cs); cs = cons(mk_eq_cnstr(m_meta, r, mk_composite1(m_jst, j), m_relax_main_opaque), cs);
return optional<constraints>(cs); return optional<constraints>(cs);
} catch (exception &) { } catch (exception &) {
return optional<constraints>(); return optional<constraints>();
@ -307,9 +313,9 @@ class elaborator {
substitution subst = next->first.get_subst(); substitution subst = next->first.get_subst();
buffer<constraint> cs; buffer<constraint> cs;
expr const & mvar = get_app_fn(m_meta); expr const & mvar = get_app_fn(m_meta);
cs.push_back(mk_eq_cnstr(mvar, subst.instantiate(mvar), m_jst)); cs.push_back(mk_eq_cnstr(mvar, subst.instantiate(mvar), m_jst, m_relax_main_opaque));
for (auto const & mvar : m_mvars_in_meta_type) for (auto const & mvar : m_mvars_in_meta_type)
cs.push_back(mk_eq_cnstr(mvar, subst.instantiate(mvar), m_jst)); cs.push_back(mk_eq_cnstr(mvar, subst.instantiate(mvar), m_jst, m_relax_main_opaque));
return optional<constraints>(to_list(cs.begin(), cs.end())); return optional<constraints>(to_list(cs.begin(), cs.end()));
} }
return optional<constraints>(); return optional<constraints>();
@ -319,7 +325,7 @@ class elaborator {
while (!empty(m_local_instances)) { while (!empty(m_local_instances)) {
expr inst = head(m_local_instances); expr inst = head(m_local_instances);
m_local_instances = tail(m_local_instances); m_local_instances = tail(m_local_instances);
constraints cs(mk_eq_cnstr(m_meta, inst, m_jst)); constraints cs(mk_eq_cnstr(m_meta, inst, m_jst, m_relax_main_opaque));
return optional<constraints>(cs); return optional<constraints>(cs);
} }
while (!empty(m_instances)) { while (!empty(m_instances)) {
@ -362,8 +368,11 @@ public:
elaborator(environment const & env, local_decls<level> const & lls, list<expr> const & ctx, io_state const & ios, name_generator const & ngen, elaborator(environment const & env, local_decls<level> const & lls, list<expr> const & ctx, io_state const & ios, name_generator const & ngen,
pos_info_provider * pp, bool check_unassigned): pos_info_provider * pp, bool check_unassigned):
m_env(env), m_lls(lls), m_ios(ios), m_env(env), m_lls(lls), m_ios(ios),
m_ngen(ngen), m_tc(mk_type_checker_with_hints(env, m_ngen.mk_child())), m_ngen(ngen),
m_pos_provider(pp) { m_pos_provider(pp) {
m_relax_main_opaque = false;
m_tc[0] = mk_type_checker_with_hints(env, m_ngen.mk_child(), false);
m_tc[1] = mk_type_checker_with_hints(env, m_ngen.mk_child(), true);
m_check_unassigned = check_unassigned; m_check_unassigned = check_unassigned;
m_use_local_instances = get_elaborator_local_instances(ios.get_options()); m_use_local_instances = get_elaborator_local_instances(ios.get_options());
set_ctx(ctx); set_ctx(ctx);
@ -375,10 +384,11 @@ public:
expr infer_type(expr const & e) { expr infer_type(expr const & e) {
lean_assert(closed(e)); lean_assert(closed(e));
return m_tc->infer(e); } return m_tc[m_relax_main_opaque]->infer(e);
}
expr whnf(expr const & e) { expr whnf(expr const & e) {
return m_tc->whnf(e); return m_tc[m_relax_main_opaque]->whnf(e);
} }
/** \brief Clear constraint buffer \c m_constraints, and associated datastructures /** \brief Clear constraint buffer \c m_constraints, and associated datastructures
@ -593,7 +603,7 @@ public:
return lazy_list<constraints>(); // nothing to be done return lazy_list<constraints>(); // nothing to be done
bool ignore_failure = false; // we are always strict with placeholders associated with classes bool ignore_failure = false; // we are always strict with placeholders associated with classes
return choose(std::make_shared<placeholder_elaborator>(*this, meta, meta_type, local_insts, insts, tacs, ctx, s, j, return choose(std::make_shared<placeholder_elaborator>(*this, meta, meta_type, local_insts, insts, tacs, ctx, s, j,
ignore_failure)); ignore_failure, m_relax_main_opaque));
} else if (s.is_assigned(mvar)) { } else if (s.is_assigned(mvar)) {
// if the metavariable is assigned and it is not a class, then we just ignore it, and return // if the metavariable is assigned and it is not a class, then we just ignore it, and return
// the an empty set of constraints. // the an empty set of constraints.
@ -602,10 +612,10 @@ public:
list<tactic_hint_entry> tacs = get_tactic_hints(m_env); list<tactic_hint_entry> tacs = get_tactic_hints(m_env);
bool ignore_failure = !is_strict; bool ignore_failure = !is_strict;
return choose(std::make_shared<placeholder_elaborator>(*this, meta, meta_type, list<expr>(), list<name>(), tacs, ctx, s, j, return choose(std::make_shared<placeholder_elaborator>(*this, meta, meta_type, list<expr>(), list<name>(), tacs, ctx, s, j,
ignore_failure)); ignore_failure, m_relax_main_opaque));
} }
}; };
add_cnstr(mk_choice_cnstr(m, choice_fn, to_delay_factor(cnstr_group::MaxDelayed), j)); add_cnstr(mk_choice_cnstr(m, choice_fn, to_delay_factor(cnstr_group::MaxDelayed), j, m_relax_main_opaque));
return m; return m;
} }
@ -642,11 +652,12 @@ public:
// Possible optimization: try to lookahead and discard some of the alternatives. // Possible optimization: try to lookahead and discard some of the alternatives.
expr m = mk_meta(t, e.get_tag()); expr m = mk_meta(t, e.get_tag());
context ctx = m_ctx; context ctx = m_ctx;
bool relax = m_relax_main_opaque;
auto fn = [=](expr const & mvar, expr const & /* type */, substitution const & s, name_generator const & /* ngen */) { auto fn = [=](expr const & mvar, expr const & /* type */, substitution const & s, name_generator const & /* ngen */) {
return choose(std::make_shared<choice_expr_elaborator>(*this, mvar, e, ctx, s)); return choose(std::make_shared<choice_expr_elaborator>(*this, mvar, e, ctx, s, relax));
}; };
justification j = mk_justification("none of the overloads is applicable", some_expr(e)); justification j = mk_justification("none of the overloads is applicable", some_expr(e));
add_cnstr(mk_choice_cnstr(m, fn, to_delay_factor(cnstr_group::Basic), j)); add_cnstr(mk_choice_cnstr(m, fn, to_delay_factor(cnstr_group::Basic), j, m_relax_main_opaque));
return m; return m;
} }
@ -670,7 +681,7 @@ public:
f_type = whnf(instantiate_metavars(f_type)); f_type = whnf(instantiate_metavars(f_type));
if (!is_pi(f_type) && is_meta(f_type)) { if (!is_pi(f_type) && is_meta(f_type)) {
// let type checker add constraint // let type checker add constraint
f_type = m_tc->ensure_pi(f_type, f); f_type = m_tc[m_relax_main_opaque]->ensure_pi(f_type, f);
} }
} }
if (!is_pi(f_type)) { if (!is_pi(f_type)) {
@ -710,6 +721,7 @@ public:
} }
constraint mk_delayed_coercion_cnstr(expr const & m, expr const & a, expr const & a_type, justification const & j, unsigned delay_factor) { constraint mk_delayed_coercion_cnstr(expr const & m, expr const & a, expr const & a_type, justification const & j, unsigned delay_factor) {
bool relax = m_relax_main_opaque;
auto choice_fn = [=](expr const & mvar, expr const & new_d_type, substitution const & /* s */, name_generator const & /* ngen */) { auto choice_fn = [=](expr const & mvar, expr const & new_d_type, substitution const & /* s */, name_generator const & /* ngen */) {
// Remark: we want the coercions solved before we start discarding FlexFlex constraints. So, we use PreFlexFlex as a max cap // Remark: we want the coercions solved before we start discarding FlexFlex constraints. So, we use PreFlexFlex as a max cap
// for delaying coercions. // for delaying coercions.
@ -718,10 +730,10 @@ public:
return lazy_list<constraints>(constraints(mk_delayed_coercion_cnstr(m, a, a_type, justification(), delay_factor+1))); return lazy_list<constraints>(constraints(mk_delayed_coercion_cnstr(m, a, a_type, justification(), delay_factor+1)));
} else { } else {
expr r = apply_coercion(a, a_type, new_d_type); expr r = apply_coercion(a, a_type, new_d_type);
return lazy_list<constraints>(constraints(mk_eq_cnstr(mvar, r, justification()))); return lazy_list<constraints>(constraints(mk_eq_cnstr(mvar, r, justification(), relax)));
} }
}; };
return mk_choice_cnstr(m, choice_fn, delay_factor, j); return mk_choice_cnstr(m, choice_fn, delay_factor, j, m_relax_main_opaque);
} }
/** \brief Given an application \c e, where the expected type is d_type, and the argument type is a_type, /** \brief Given an application \c e, where the expected type is d_type, and the argument type is a_type,
@ -792,19 +804,19 @@ public:
return mk_delayed_coercion(r, d_type, a_type); return mk_delayed_coercion(r, d_type, a_type);
} else { } else {
app_delayed_justification j(r, f_type, a_type); app_delayed_justification j(r, f_type, a_type);
if (!m_tc->is_def_eq(a_type, d_type, j)) { if (!m_tc[m_relax_main_opaque]->is_def_eq(a_type, d_type, j)) {
expr new_a = apply_coercion(a, a_type, d_type); expr new_a = apply_coercion(a, a_type, d_type);
bool coercion_worked = false; bool coercion_worked = false;
if (!is_eqp(a, new_a)) { if (!is_eqp(a, new_a)) {
expr new_a_type = instantiate_metavars(infer_type(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); coercion_worked = m_tc[m_relax_main_opaque]->is_def_eq(new_a_type, d_type, j);
} }
if (coercion_worked) { if (coercion_worked) {
r = update_app(r, f, new_a); r = update_app(r, f, new_a);
} else { } else {
if (has_metavar(a_type) || has_metavar(d_type)) { if (has_metavar(a_type) || has_metavar(d_type)) {
// rely on unification hints to solve this constraint // rely on unification hints to solve this constraint
add_cnstr(mk_eq_cnstr(a_type, d_type, j.get())); add_cnstr(mk_eq_cnstr(a_type, d_type, j.get(), m_relax_main_opaque));
} else { } else {
throw_kernel_exception(m_env, r, [=](formatter const & fmt) { return pp_app_type_mismatch(fmt, e, d_type, a_type); }); throw_kernel_exception(m_env, r, [=](formatter const & fmt) { return pp_app_type_mismatch(fmt, e, d_type, a_type); });
} }
@ -888,7 +900,7 @@ public:
return e; return e;
if (is_meta(t)) { if (is_meta(t)) {
// let type checker add constraint // let type checker add constraint
m_tc->ensure_sort(t, e); m_tc[m_relax_main_opaque]->ensure_sort(t, e);
return e; return e;
} }
} }
@ -1112,7 +1124,7 @@ public:
if (!meta) if (!meta)
return; return;
meta = instantiate_meta(*meta, subst); meta = instantiate_meta(*meta, subst);
expr type = m_tc->infer(*meta); expr type = m_tc[m_relax_main_opaque]->infer(*meta);
// first solve unassigned metavariables in type // first solve unassigned metavariables in type
type = solve_unassigned_mvars(subst, type, visited); type = solve_unassigned_mvars(subst, type, visited);
proof_state ps(goals(goal(*meta, type)), subst, m_ngen.mk_child()); proof_state ps(goals(goal(*meta, type)), subst, m_ngen.mk_child());
@ -1173,7 +1185,8 @@ public:
return std::make_tuple(r, to_list(new_ps.begin(), new_ps.end())); return std::make_tuple(r, to_list(new_ps.begin(), new_ps.end()));
} }
std::tuple<expr, level_param_names> operator()(expr const & e, bool _ensure_type) { std::tuple<expr, level_param_names> operator()(expr const & e, bool _ensure_type, bool relax_main_opaque) {
flet<bool> set_relax(m_relax_main_opaque, relax_main_opaque && !get_hide_main_opaque(m_env));
expr r = visit(e); expr r = visit(e);
if (_ensure_type) if (_ensure_type)
r = ensure_type(r); r = ensure_type(r);
@ -1183,16 +1196,18 @@ public:
return apply(s, r); return apply(s, r);
} }
std::tuple<expr, expr, level_param_names> operator()(expr const & t, expr const & v, name const & n) { std::tuple<expr, expr, level_param_names> operator()(expr const & t, expr const & v, name const & n, bool is_opaque) {
lean_assert(!has_local(t)); lean_assert(!has_local(v)); lean_assert(!has_local(t)); lean_assert(!has_local(v));
expr r_t = ensure_type(visit(t)); expr r_t = ensure_type(visit(t));
// Opaque definitions in the main module may treat other opaque definitions (in the main module) as transparent.
flet<bool> set_relax(m_relax_main_opaque, is_opaque && !get_hide_main_opaque(m_env));
expr r_v = visit(v); expr r_v = visit(v);
expr r_v_type = infer_type(r_v); expr r_v_type = infer_type(r_v);
justification j = mk_justification(v, [=](formatter const & fmt, substitution const & subst) { justification j = mk_justification(v, [=](formatter const & fmt, substitution const & subst) {
substitution s(subst); substitution s(subst);
return pp_def_type_mismatch(fmt, n, s.instantiate(r_t), s.instantiate(r_v_type)); return pp_def_type_mismatch(fmt, n, s.instantiate(r_t), s.instantiate(r_v_type));
}); });
if (!m_tc->is_def_eq(r_v_type, r_t, j)) { if (!m_tc[is_opaque]->is_def_eq(r_v_type, r_t, j)) {
throw_kernel_exception(m_env, v, [=](formatter const & fmt) { return pp_def_type_mismatch(fmt, n, r_t, r_v_type); }); throw_kernel_exception(m_env, v, [=](formatter const & fmt) { return pp_def_type_mismatch(fmt, n, r_t, r_v_type); });
} }
auto p = solve().pull(); auto p = solve().pull();
@ -1209,13 +1224,13 @@ public:
static name g_tmp_prefix = name::mk_internal_unique_name(); static name g_tmp_prefix = name::mk_internal_unique_name();
std::tuple<expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, list<expr> const & ctx, std::tuple<expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, list<expr> const & ctx,
io_state const & ios, expr const & e, pos_info_provider * pp, bool check_unassigned, io_state const & ios, expr const & e, bool relax_main_opaque,
bool ensure_type) { pos_info_provider * pp, bool check_unassigned, bool ensure_type) {
return elaborator(env, lls, ctx, ios, name_generator(g_tmp_prefix), pp, check_unassigned)(e, ensure_type); return elaborator(env, lls, ctx, ios, name_generator(g_tmp_prefix), pp, check_unassigned)(e, ensure_type, relax_main_opaque);
} }
std::tuple<expr, expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, io_state const & ios, std::tuple<expr, expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, io_state const & ios,
name const & n, expr const & t, expr const & v, pos_info_provider * pp) { name const & n, expr const & t, expr const & v, bool is_opaque, pos_info_provider * pp) {
return elaborator(env, lls, list<expr>(), ios, name_generator(g_tmp_prefix), pp, true)(t, v, n); return elaborator(env, lls, list<expr>(), ios, name_generator(g_tmp_prefix), pp, true)(t, v, n, is_opaque);
} }
} }

View file

@ -14,9 +14,9 @@ Author: Leonardo de Moura
namespace lean { namespace lean {
std::tuple<expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, list<expr> const & ctx, std::tuple<expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, list<expr> const & ctx,
io_state const & ios, expr const & e, pos_info_provider * pp = nullptr, io_state const & ios, expr const & e, bool relax_main_opaque,
bool check_unassigned = true, bool ensure_type = false); pos_info_provider * pp = nullptr, bool check_unassigned = true, bool ensure_type = false);
std::tuple<expr, expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, std::tuple<expr, expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls,
io_state const & ios, name const & n, expr const & t, expr const & v, io_state const & ios, name const & n, expr const & t, expr const & v,
pos_info_provider * pp = nullptr); bool is_opaque, pos_info_provider * pp = nullptr);
} }

View file

@ -90,7 +90,7 @@ struct inductive_cmd_fn {
m_u = mk_global_univ(u_name); m_u = mk_global_univ(u_name);
m_infer_result_universe = false; m_infer_result_universe = false;
m_namespace = get_namespace(m_env); m_namespace = get_namespace(m_env);
m_tc = mk_type_checker_with_hints(m_env, m_p.mk_ngen()); m_tc = mk_type_checker_with_hints(m_env, m_p.mk_ngen(), false);
} }
[[ noreturn ]] void throw_error(char const * error_msg) { throw parser_error(error_msg, m_pos); } [[ noreturn ]] void throw_error(char const * error_msg) { throw parser_error(error_msg, m_pos); }
@ -402,7 +402,7 @@ struct inductive_cmd_fn {
std::tie(d_name, d_type, std::ignore) = d; std::tie(d_name, d_type, std::ignore) = d;
m_env = m_env.add(check(m_env, mk_var_decl(d_name, ls, d_type))); m_env = m_env.add(check(m_env, mk_var_decl(d_name, ls, d_type)));
} }
m_tc = mk_type_checker_with_hints(m_env, m_p.mk_ngen()); m_tc = mk_type_checker_with_hints(m_env, m_p.mk_ngen(), false);
} }
/** \brief Traverse the introduction rule type and collect the universes where non-parameters reside in \c r_lvls. /** \brief Traverse the introduction rule type and collect the universes where non-parameters reside in \c r_lvls.

View file

@ -485,28 +485,34 @@ level parser::parse_level(unsigned rbp) {
std::tuple<expr, level_param_names> parser::elaborate_relaxed(expr const & e, list<expr> const & ctx) { std::tuple<expr, level_param_names> parser::elaborate_relaxed(expr const & e, list<expr> const & ctx) {
parser_pos_provider pp = get_pos_provider(); parser_pos_provider pp = get_pos_provider();
return ::lean::elaborate(m_env, m_local_level_decls, ctx, m_ios, e, &pp, false); bool relax = true;
bool check_unassigned = false;
return ::lean::elaborate(m_env, m_local_level_decls, ctx, m_ios, e, relax, &pp, check_unassigned);
} }
std::tuple<expr, level_param_names> parser::elaborate_type(expr const & e, list<expr> const & ctx) { std::tuple<expr, level_param_names> parser::elaborate_type(expr const & e, list<expr> const & ctx) {
parser_pos_provider pp = get_pos_provider(); parser_pos_provider pp = get_pos_provider();
return ::lean::elaborate(m_env, m_local_level_decls, ctx, m_ios, e, &pp, true, true); bool relax = false;
bool ensure_type = true;
bool check_unassigned = true;
return ::lean::elaborate(m_env, m_local_level_decls, ctx, m_ios, e, relax, &pp, check_unassigned, ensure_type);
} }
std::tuple<expr, level_param_names> parser::elaborate_at(environment const & env, expr const & e) { std::tuple<expr, level_param_names> parser::elaborate_at(environment const & env, expr const & e) {
parser_pos_provider pp = get_pos_provider(); parser_pos_provider pp = get_pos_provider();
return ::lean::elaborate(env, m_local_level_decls, list<expr>(), m_ios, e, &pp); bool relax = false;
return ::lean::elaborate(env, m_local_level_decls, list<expr>(), m_ios, e, relax, &pp);
} }
std::tuple<expr, expr, level_param_names> parser::elaborate_definition(name const & n, expr const & t, expr const & v) { std::tuple<expr, expr, level_param_names> parser::elaborate_definition(name const & n, expr const & t, expr const & v, bool is_opaque) {
parser_pos_provider pp = get_pos_provider(); parser_pos_provider pp = get_pos_provider();
return ::lean::elaborate(m_env, m_local_level_decls, m_ios, n, t, v, &pp); return ::lean::elaborate(m_env, m_local_level_decls, m_ios, n, t, v, is_opaque, &pp);
} }
std::tuple<expr, expr, level_param_names> parser::elaborate_definition_at(environment const & env, local_level_decls const & lls, std::tuple<expr, expr, level_param_names> parser::elaborate_definition_at(environment const & env, local_level_decls const & lls,
name const & n, expr const & t, expr const & v) { name const & n, expr const & t, expr const & v, bool is_opaque) {
parser_pos_provider pp = get_pos_provider(); parser_pos_provider pp = get_pos_provider();
return ::lean::elaborate(env, lls, m_ios, n, t, v, &pp); return ::lean::elaborate(env, lls, m_ios, n, t, v, is_opaque, &pp);
} }
[[ noreturn ]] void throw_invalid_open_binder(pos_info const & pos) { [[ noreturn ]] void throw_invalid_open_binder(pos_info const & pos) {

View file

@ -262,9 +262,10 @@ public:
/** \brief Elaborate \c e (making sure the result does not have metavariables). */ /** \brief Elaborate \c e (making sure the result does not have metavariables). */
std::tuple<expr, level_param_names> elaborate(expr const & e) { return elaborate_at(m_env, e); } std::tuple<expr, level_param_names> elaborate(expr const & e) { return elaborate_at(m_env, e); }
/** \brief Elaborate the definition n : t := v */ /** \brief Elaborate the definition n : t := v */
std::tuple<expr, expr, level_param_names> elaborate_definition(name const & n, expr const & t, expr const & v); std::tuple<expr, expr, level_param_names> elaborate_definition(name const & n, expr const & t, expr const & v, bool is_opaque);
/** \brief Elaborate the definition n : t := v in the given environment*/ /** \brief Elaborate the definition n : t := v in the given environment*/
std::tuple<expr, expr, level_param_names> elaborate_definition_at(environment const & env, local_level_decls const & lls, name const & n, expr const & t, expr const & v); std::tuple<expr, expr, level_param_names> elaborate_definition_at(environment const & env, local_level_decls const & lls,
name const & n, expr const & t, expr const & v, bool is_opaque);
parser_pos_provider get_pos_provider() const { return parser_pos_provider(m_pos_table, get_stream_name(), m_last_cmd_pos); } parser_pos_provider get_pos_provider() const { return parser_pos_provider(m_pos_table, get_stream_name(), m_last_cmd_pos); }

View file

@ -16,7 +16,8 @@ void theorem_queue::add(environment const & env, name const & n, level_param_nam
m_queue.add([=]() { m_queue.add([=]() {
level_param_names new_ls; level_param_names new_ls;
expr type, value; expr type, value;
std::tie(type, value, new_ls) = m_parser.elaborate_definition_at(env, lls, n, t, v); bool is_opaque = true; // theorems are always opaque
std::tie(type, value, new_ls) = m_parser.elaborate_definition_at(env, lls, n, t, v, is_opaque);
return check(env, mk_theorem(n, append(ls, new_ls), type, value)); return check(env, mk_theorem(n, append(ls, new_ls), type, value));
}); });
} }

View file

@ -12,28 +12,29 @@ struct constraint_cell {
MK_LEAN_RC() MK_LEAN_RC()
constraint_kind m_kind; constraint_kind m_kind;
justification m_jst; justification m_jst;
constraint_cell(constraint_kind k, justification const & j):m_rc(1), m_kind(k), m_jst(j) {} bool m_relax_main_opaque;
constraint_cell(constraint_kind k, justification const & j, bool relax):m_rc(1), m_kind(k), m_jst(j), m_relax_main_opaque(relax) {}
}; };
struct eq_constraint_cell : public constraint_cell { struct eq_constraint_cell : public constraint_cell {
expr m_lhs; expr m_lhs;
expr m_rhs; expr m_rhs;
eq_constraint_cell(expr const & lhs, expr const & rhs, justification const & j): eq_constraint_cell(expr const & lhs, expr const & rhs, justification const & j, bool relax):
constraint_cell(constraint_kind::Eq, j), constraint_cell(constraint_kind::Eq, j, relax),
m_lhs(lhs), m_rhs(rhs) {} m_lhs(lhs), m_rhs(rhs) {}
}; };
struct level_constraint_cell : public constraint_cell { struct level_constraint_cell : public constraint_cell {
level m_lhs; level m_lhs;
level m_rhs; level m_rhs;
level_constraint_cell(level const & lhs, level const & rhs, justification const & j): level_constraint_cell(level const & lhs, level const & rhs, justification const & j):
constraint_cell(constraint_kind::LevelEq, j), constraint_cell(constraint_kind::LevelEq, j, false),
m_lhs(lhs), m_rhs(rhs) {} m_lhs(lhs), m_rhs(rhs) {}
}; };
struct choice_constraint_cell : public constraint_cell { struct choice_constraint_cell : public constraint_cell {
expr m_expr; expr m_expr;
choice_fn m_fn; choice_fn m_fn;
unsigned m_delay_factor; unsigned m_delay_factor;
choice_constraint_cell(expr const & e, choice_fn const & fn, unsigned delay_factor, justification const & j): choice_constraint_cell(expr const & e, choice_fn const & fn, unsigned delay_factor, justification const & j, bool relax):
constraint_cell(constraint_kind::Choice, j), constraint_cell(constraint_kind::Choice, j, relax),
m_expr(e), m_fn(fn), m_delay_factor(delay_factor) {} m_expr(e), m_fn(fn), m_delay_factor(delay_factor) {}
}; };
@ -57,19 +58,20 @@ constraint & constraint::operator=(constraint && c) { LEAN_MOVE_REF(c); }
constraint_kind constraint::kind() const { lean_assert(m_ptr); return m_ptr->m_kind; } constraint_kind constraint::kind() const { lean_assert(m_ptr); return m_ptr->m_kind; }
justification const & constraint::get_justification() const { lean_assert(m_ptr); return m_ptr->m_jst; } justification const & constraint::get_justification() const { lean_assert(m_ptr); return m_ptr->m_jst; }
constraint mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j) { constraint mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j, bool relax_main_opaque) {
return constraint(new eq_constraint_cell(lhs, rhs, j)); return constraint(new eq_constraint_cell(lhs, rhs, j, relax_main_opaque));
} }
constraint mk_level_eq_cnstr(level const & lhs, level const & rhs, justification const & j) { constraint mk_level_eq_cnstr(level const & lhs, level const & rhs, justification const & j) {
return constraint(new level_constraint_cell(lhs, rhs, j)); return constraint(new level_constraint_cell(lhs, rhs, j));
} }
constraint mk_choice_cnstr(expr const & m, choice_fn const & fn, unsigned delay_factor, justification const & j) { constraint mk_choice_cnstr(expr const & m, choice_fn const & fn, unsigned delay_factor, justification const & j, bool relax_main_opaque) {
lean_assert(is_meta(m)); lean_assert(is_meta(m));
return constraint(new choice_constraint_cell(m, fn, delay_factor, j)); return constraint(new choice_constraint_cell(m, fn, delay_factor, j, relax_main_opaque));
} }
expr const & cnstr_lhs_expr(constraint const & c) { lean_assert(is_eq_cnstr(c)); return static_cast<eq_constraint_cell*>(c.raw())->m_lhs; } expr const & cnstr_lhs_expr(constraint const & c) { lean_assert(is_eq_cnstr(c)); return static_cast<eq_constraint_cell*>(c.raw())->m_lhs; }
expr const & cnstr_rhs_expr(constraint const & c) { lean_assert(is_eq_cnstr(c)); return static_cast<eq_constraint_cell*>(c.raw())->m_rhs; } expr const & cnstr_rhs_expr(constraint const & c) { lean_assert(is_eq_cnstr(c)); return static_cast<eq_constraint_cell*>(c.raw())->m_rhs; }
bool relax_main_opaque(constraint const & c) { return c.raw()->m_relax_main_opaque; }
level const & cnstr_lhs_level(constraint const & c) { level const & cnstr_lhs_level(constraint const & c) {
lean_assert(is_level_eq_cnstr(c)); lean_assert(is_level_eq_cnstr(c));
return static_cast<level_constraint_cell*>(c.raw())->m_lhs; return static_cast<level_constraint_cell*>(c.raw())->m_lhs;
@ -89,11 +91,11 @@ unsigned cnstr_delay_factor(constraint const & c) {
constraint update_justification(constraint const & c, justification const & j) { constraint update_justification(constraint const & c, justification const & j) {
switch (c.kind()) { switch (c.kind()) {
case constraint_kind::Eq: case constraint_kind::Eq:
return mk_eq_cnstr(cnstr_lhs_expr(c), cnstr_rhs_expr(c), j); return mk_eq_cnstr(cnstr_lhs_expr(c), cnstr_rhs_expr(c), j, relax_main_opaque(c));
case constraint_kind::LevelEq: case constraint_kind::LevelEq:
return mk_level_eq_cnstr(cnstr_lhs_level(c), cnstr_rhs_level(c), j); return mk_level_eq_cnstr(cnstr_lhs_level(c), cnstr_rhs_level(c), j);
case constraint_kind::Choice: case constraint_kind::Choice:
return mk_choice_cnstr(cnstr_expr(c), cnstr_choice_fn(c), cnstr_delay_factor(c), j); return mk_choice_cnstr(cnstr_expr(c), cnstr_choice_fn(c), cnstr_delay_factor(c), j, relax_main_opaque(c));
} }
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
} }

View file

@ -65,9 +65,10 @@ public:
friend bool is_eqp(constraint const & c1, constraint const & c2) { return c1.m_ptr == c2.m_ptr; } friend bool is_eqp(constraint const & c1, constraint const & c2) { return c1.m_ptr == c2.m_ptr; }
friend void swap(constraint & l1, constraint & l2) { std::swap(l1, l2); } friend void swap(constraint & l1, constraint & l2) { std::swap(l1, l2); }
friend constraint mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j); friend constraint mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j, bool relax_main_opaque);
friend constraint mk_level_eq_cnstr(level const & lhs, level const & rhs, justification const & j); friend constraint mk_level_eq_cnstr(level const & lhs, level const & rhs, justification const & j);
friend constraint mk_choice_cnstr(expr const & m, choice_fn const & fn, unsigned delay_factor, justification const & j); friend constraint mk_choice_cnstr(expr const & m, choice_fn const & fn, unsigned delay_factor, justification const & j,
bool relax_main_opaque);
constraint_cell * raw() const { return m_ptr; } constraint_cell * raw() const { return m_ptr; }
}; };
@ -75,9 +76,13 @@ public:
inline bool operator==(constraint const & c1, constraint const & c2) { return c1.raw() == c2.raw(); } inline bool operator==(constraint const & c1, constraint const & c2) { return c1.raw() == c2.raw(); }
inline bool operator!=(constraint const & c1, constraint const & c2) { return !(c1 == c2); } inline bool operator!=(constraint const & c1, constraint const & c2) { return !(c1 == c2); }
constraint mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j); /**
\brief Create a unification constraint lhs =?= rhs
If \c relax_main_opaque is true, then opaque definitions from the main module are treated as transparent.
*/
constraint mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j, bool relax_main_opaque);
constraint mk_level_eq_cnstr(level const & lhs, level const & rhs, justification const & j); constraint mk_level_eq_cnstr(level const & lhs, level const & rhs, justification const & j);
constraint mk_choice_cnstr(expr const & m, choice_fn const & fn, unsigned delay_factor, justification const & j); constraint mk_choice_cnstr(expr const & m, choice_fn const & fn, unsigned delay_factor, justification const & j, bool relax_main_opaque);
inline bool is_eq_cnstr(constraint const & c) { return c.kind() == constraint_kind::Eq; } inline bool is_eq_cnstr(constraint const & c) { return c.kind() == constraint_kind::Eq; }
inline bool is_level_eq_cnstr(constraint const & c) { return c.kind() == constraint_kind::LevelEq; } inline bool is_level_eq_cnstr(constraint const & c) { return c.kind() == constraint_kind::LevelEq; }
@ -89,6 +94,8 @@ constraint update_justification(constraint const & c, justification const & j);
expr const & cnstr_lhs_expr(constraint const & c); expr const & cnstr_lhs_expr(constraint const & c);
/** \brief Return the rhs of an equality constraint. */ /** \brief Return the rhs of an equality constraint. */
expr const & cnstr_rhs_expr(constraint const & c); expr const & cnstr_rhs_expr(constraint const & c);
/** \brief Return true iff opaque definitions from the main module should be treated as transparent. */
bool relax_main_opaque(constraint const & c);
/** \brief Return the lhs of an level constraint. */ /** \brief Return the lhs of an level constraint. */
level const & cnstr_lhs_level(constraint const & c); level const & cnstr_lhs_level(constraint const & c);
/** \brief Return the rhs of an level constraint. */ /** \brief Return the rhs of an level constraint. */

View file

@ -74,6 +74,7 @@ bool converter::is_def_eq(expr const & t, expr const & s, type_checker & c) {
struct dummy_converter : public converter { struct dummy_converter : public converter {
virtual expr whnf(expr const & e, type_checker &) { return e; } virtual expr whnf(expr const & e, type_checker &) { return e; }
virtual bool is_def_eq(expr const &, expr const &, type_checker &, delayed_justification &) { return true; } virtual bool is_def_eq(expr const &, expr const &, type_checker &, delayed_justification &) { return true; }
virtual optional<module_idx> get_module_idx() const { return optional<module_idx>(); }
}; };
std::unique_ptr<converter> mk_dummy_converter() { std::unique_ptr<converter> mk_dummy_converter() {
@ -98,6 +99,10 @@ struct default_converter : public converter {
m_env(env), m_module_idx(mod_idx), m_memoize(memoize), m_extra_opaque(extra_opaque) { m_env(env), m_module_idx(mod_idx), m_memoize(memoize), m_extra_opaque(extra_opaque) {
} }
constraint mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j) {
return ::lean::mk_eq_cnstr(lhs, rhs, j, static_cast<bool>(m_module_idx));
}
optional<expr> expand_macro(expr const & m, type_checker & c) { optional<expr> expand_macro(expr const & m, type_checker & c) {
lean_assert(is_macro(m)); lean_assert(is_macro(m));
return macro_def(m).expand(m, get_extension(c)); return macro_def(m).expand(m, get_extension(c));
@ -552,6 +557,10 @@ struct default_converter : public converter {
bool is_prop(expr const & e, type_checker & c) { bool is_prop(expr const & e, type_checker & c) {
return whnf(infer_type(c, e), c) == Prop; return whnf(infer_type(c, e), c) == Prop;
} }
virtual optional<module_idx> get_module_idx() const {
return m_module_idx;
}
}; };
std::unique_ptr<converter> mk_default_converter(environment const & env, optional<module_idx> mod_idx, std::unique_ptr<converter> mk_default_converter(environment const & env, optional<module_idx> mod_idx,

View file

@ -20,6 +20,7 @@ public:
virtual ~converter() {} virtual ~converter() {}
virtual expr whnf(expr const & e, type_checker & c) = 0; virtual expr whnf(expr const & e, type_checker & c) = 0;
virtual bool is_def_eq(expr const & t, expr const & s, type_checker & c, delayed_justification & j) = 0; virtual bool is_def_eq(expr const & t, expr const & s, type_checker & c, delayed_justification & j) = 0;
virtual optional<module_idx> get_module_idx() const = 0;
bool is_def_eq(expr const & t, expr const & s, type_checker & c); bool is_def_eq(expr const & t, expr const & s, type_checker & c);
}; };

View file

@ -105,6 +105,10 @@ void type_checker::add_cnstr(constraint const & c) {
m_cs.push_back(c); m_cs.push_back(c);
} }
constraint type_checker::mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j) {
return ::lean::mk_eq_cnstr(lhs, rhs, j, static_cast<bool>(m_conv->get_module_idx()));
}
optional<constraint> type_checker::next_cnstr() { optional<constraint> type_checker::next_cnstr() {
if (m_cs_qhead < m_cs.size()) { if (m_cs_qhead < m_cs.size()) {
constraint c = m_cs[m_cs_qhead]; constraint c = m_cs[m_cs_qhead];

View file

@ -105,6 +105,7 @@ class type_checker {
expr infer_type(expr const & e); expr infer_type(expr const & e);
void copy_constraints(unsigned qhead, buffer<constraint> & new_cnstrs); void copy_constraints(unsigned qhead, buffer<constraint> & new_cnstrs);
extension_context & get_extension() { return m_tc_ctx; } extension_context & get_extension() { return m_tc_ctx; }
constraint mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j);
public: public:
/** /**
\brief Create a type checker for the given environment. The auxiliary names created by this \brief Create a type checker for the given environment. The auxiliary names created by this

View file

@ -45,7 +45,7 @@ class inductive_unifier_plugin_cell : public unifier_plugin_cell {
*/ */
lazy_list<constraints> add_elim_meta_cnstrs(type_checker & tc, name_generator ngen, inductive::inductive_decl const & decl, lazy_list<constraints> add_elim_meta_cnstrs(type_checker & tc, name_generator ngen, inductive::inductive_decl const & decl,
expr const & elim, buffer<expr> & args, expr const & t, justification const & j, expr const & elim, buffer<expr> & args, expr const & t, justification const & j,
buffer<constraint> & tc_cnstr_buffer) const { buffer<constraint> & tc_cnstr_buffer, bool relax) const {
lean_assert(is_constant(elim)); lean_assert(is_constant(elim));
environment const & env = tc.env(); environment const & env = tc.env();
levels elim_lvls = const_levels(elim); levels elim_lvls = const_levels(elim);
@ -78,12 +78,12 @@ class inductive_unifier_plugin_cell : public unifier_plugin_cell {
intro_type = tc.whnf(binding_body(intro_type), tc_cnstr_buffer); intro_type = tc.whnf(binding_body(intro_type), tc_cnstr_buffer);
lean_assert(!tc.next_cnstr()); lean_assert(!tc.next_cnstr());
} }
constraint c1 = mk_eq_cnstr(meta, hint, j); constraint c1 = mk_eq_cnstr(meta, hint, j, relax);
args[major_idx] = hint; args[major_idx] = hint;
lean_assert(!tc.next_cnstr()); lean_assert(!tc.next_cnstr());
expr reduce_elim = tc.whnf(mk_app(elim, args), tc_cnstr_buffer); expr reduce_elim = tc.whnf(mk_app(elim, args), tc_cnstr_buffer);
lean_assert(!tc.next_cnstr()); lean_assert(!tc.next_cnstr());
constraint c2 = mk_eq_cnstr(reduce_elim, t, j); constraint c2 = mk_eq_cnstr(reduce_elim, t, j, relax);
list<constraint> tc_cnstrs = to_list(tc_cnstr_buffer.begin(), tc_cnstr_buffer.end()); list<constraint> tc_cnstrs = to_list(tc_cnstr_buffer.begin(), tc_cnstr_buffer.end());
alts.push_back(cons(c1, cons(c2, tc_cnstrs))); alts.push_back(cons(c1, cons(c2, tc_cnstrs)));
} }
@ -92,7 +92,7 @@ class inductive_unifier_plugin_cell : public unifier_plugin_cell {
} }
lazy_list<constraints> process_elim_meta_core(type_checker & tc, name_generator const & ngen, lazy_list<constraints> process_elim_meta_core(type_checker & tc, name_generator const & ngen,
expr const & lhs, expr const & rhs, justification const & j) const { expr const & lhs, expr const & rhs, justification const & j, bool relax) const {
lean_assert(inductive::is_elim_meta_app(tc, lhs)); lean_assert(inductive::is_elim_meta_app(tc, lhs));
buffer<constraint> tc_cnstr_buffer; buffer<constraint> tc_cnstr_buffer;
lean_assert(!tc.next_cnstr()); lean_assert(!tc.next_cnstr());
@ -106,7 +106,7 @@ class inductive_unifier_plugin_cell : public unifier_plugin_cell {
auto decls = *inductive::is_inductive_decl(env, it_name); auto decls = *inductive::is_inductive_decl(env, it_name);
for (auto const & d : std::get<2>(decls)) { for (auto const & d : std::get<2>(decls)) {
if (inductive::inductive_decl_name(d) == it_name) if (inductive::inductive_decl_name(d) == it_name)
return add_elim_meta_cnstrs(tc, ngen, d, elim, args, rhs, j, tc_cnstr_buffer); return add_elim_meta_cnstrs(tc, ngen, d, elim, args, rhs, j, tc_cnstr_buffer, relax);
} }
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
} }
@ -122,10 +122,11 @@ public:
expr const & lhs = cnstr_lhs_expr(c); expr const & lhs = cnstr_lhs_expr(c);
expr const & rhs = cnstr_rhs_expr(c); expr const & rhs = cnstr_rhs_expr(c);
justification const & j = c.get_justification(); justification const & j = c.get_justification();
bool relax = relax_main_opaque(c);
if (inductive::is_elim_meta_app(tc, lhs)) if (inductive::is_elim_meta_app(tc, lhs))
return process_elim_meta_core(tc, ngen, lhs, rhs, j); return process_elim_meta_core(tc, ngen, lhs, rhs, j, relax);
else if (inductive::is_elim_meta_app(tc, rhs)) else if (inductive::is_elim_meta_app(tc, rhs))
return process_elim_meta_core(tc, ngen, rhs, lhs, j); return process_elim_meta_core(tc, ngen, rhs, lhs, j, relax);
else else
return lazy_list<constraints>(); return lazy_list<constraints>();
} }

View file

@ -1478,7 +1478,9 @@ static int constraint_tostring(lua_State * L) {
} }
static int mk_eq_cnstr(lua_State * L) { static int mk_eq_cnstr(lua_State * L) {
int nargs = lua_gettop(L); int nargs = lua_gettop(L);
return push_constraint(L, mk_eq_cnstr(to_expr(L, 1), to_expr(L, 2), nargs == 3 ? to_justification(L, 3) : justification())); return push_constraint(L, mk_eq_cnstr(to_expr(L, 1), to_expr(L, 2),
nargs >= 3 ? to_justification(L, 3) : justification(),
nargs >= 4 && lua_toboolean(L, 4)));
} }
static int mk_level_eq_cnstr(lua_State * L) { static int mk_level_eq_cnstr(lua_State * L) {
int nargs = lua_gettop(L); int nargs = lua_gettop(L);
@ -1508,7 +1510,7 @@ static choice_fn to_choice_fn(lua_State * L, int idx) {
if (is_constraint(L, -1)) if (is_constraint(L, -1))
r.push_back(constraints(to_constraint(L, -1))); r.push_back(constraints(to_constraint(L, -1)));
else if (is_expr(L, -1)) else if (is_expr(L, -1))
r.push_back(constraints(mk_eq_cnstr(mvar, to_expr(L, -1), justification()))); r.push_back(constraints(mk_eq_cnstr(mvar, to_expr(L, -1), justification(), false)));
else else
r.push_back(to_list_constraint_ext(L, -1)); r.push_back(to_list_constraint_ext(L, -1));
lua_pop(L, 1); lua_pop(L, 1);
@ -1526,13 +1528,15 @@ static int mk_choice_cnstr(lua_State * L) {
expr m = to_expr(L, 1); expr m = to_expr(L, 1);
choice_fn fn = to_choice_fn(L, 2); choice_fn fn = to_choice_fn(L, 2);
if (nargs == 2) if (nargs == 2)
return push_constraint(L, mk_choice_cnstr(m, fn, 0, justification())); return push_constraint(L, mk_choice_cnstr(m, fn, 0, justification(), false));
else if (nargs == 3 && is_justification(L, 3)) else if (nargs == 3 && is_justification(L, 3))
return push_constraint(L, mk_choice_cnstr(m, fn, 0, to_justification(L, 3))); return push_constraint(L, mk_choice_cnstr(m, fn, 0, to_justification(L, 3), false));
else if (nargs == 3) else if (nargs == 3)
return push_constraint(L, mk_choice_cnstr(m, fn, lua_tonumber(L, 3), justification())); return push_constraint(L, mk_choice_cnstr(m, fn, lua_tonumber(L, 3), justification(), false));
else if (nargs == 4)
return push_constraint(L, mk_choice_cnstr(m, fn, lua_tonumber(L, 3), to_justification(L, 4), false));
else else
return push_constraint(L, mk_choice_cnstr(m, fn, lua_tonumber(L, 3), to_justification(L, 4))); return push_constraint(L, mk_choice_cnstr(m, fn, lua_tonumber(L, 3), to_justification(L, 4), lua_toboolean(L, 5)));
} }
static int constraint_expr(lua_State * L) { static int constraint_expr(lua_State * L) {

View file

@ -45,15 +45,17 @@ environment expose_definition(environment const & env, name const & n) {
ext.m_extra_opaque.erase(n); ext.m_extra_opaque.erase(n);
return update(env, ext); return update(env, ext);
} }
environment set_main_module_opaque_defs(environment const & env, bool f) { environment set_hide_main_opaque(environment const & env, bool f) {
auto ext = get_extension(env); auto ext = get_extension(env);
ext.m_hide_module = f; ext.m_hide_module = f;
return update(env, ext); return update(env, ext);
} }
std::unique_ptr<type_checker> mk_type_checker_with_hints(environment const & env, name_generator const & ngen) { bool get_hide_main_opaque(environment const & env) {
return get_extension(env).m_hide_module;
}
std::unique_ptr<type_checker> mk_type_checker_with_hints(environment const & env, name_generator const & ngen, bool relax_main_opaque) {
auto const & ext = get_extension(env); auto const & ext = get_extension(env);
return std::unique_ptr<type_checker>(new type_checker(env, ngen, mk_default_converter(env, !ext.m_hide_module, return std::unique_ptr<type_checker>(new type_checker(env, ngen, mk_default_converter(env, !ext.m_hide_module && relax_main_opaque,
true, ext.m_extra_opaque))); true, ext.m_extra_opaque)));
} }
} }

View file

@ -22,12 +22,11 @@ namespace lean {
environment hide_definition(environment const & env, name const & n); environment hide_definition(environment const & env, name const & n);
/** \brief Undo the modification made with \c hide_definition. */ /** \brief Undo the modification made with \c hide_definition. */
environment expose_definition(environment const & env, name const & n); environment expose_definition(environment const & env, name const & n);
/** /** \brief By default, the elaborator/unifier will treat the opaque definitions of the current/main
\brief By default, the elaborator/unifier will treat the opaque module as transparent (when allowed). We can change this behavior using this function.
definitions of the current/main module as transparent. We can
change this behavior using this function.
*/ */
environment set_main_module_opaque_defs(environment const & env, bool f); environment set_hide_main_opaque(environment const & env, bool f);
bool get_hide_main_opaque(environment const & env);
/** \brief Create a type checker that takes the hints into account. */ /** \brief Create a type checker that takes the hints into account. */
std::unique_ptr<type_checker> mk_type_checker_with_hints(environment const & env, name_generator const & ngen); std::unique_ptr<type_checker> mk_type_checker_with_hints(environment const & env, name_generator const & ngen, bool relax_main_opaque);
} }

View file

@ -92,7 +92,7 @@ static void remove_redundant_metas(buffer<expr> & metas) {
} }
proof_state_seq apply_tactic_core(environment const & env, io_state const & ios, proof_state const & s, expr const & _e, proof_state_seq apply_tactic_core(environment const & env, io_state const & ios, proof_state const & s, expr const & _e,
bool add_meta, bool add_subgoals) { bool add_meta, bool add_subgoals, bool relax_main_opaque) {
goals const & gs = s.get_goals(); goals const & gs = s.get_goals();
if (empty(gs)) if (empty(gs))
return proof_state_seq(); return proof_state_seq();
@ -119,7 +119,7 @@ proof_state_seq apply_tactic_core(environment const & env, io_state const & ios,
} }
} }
list<expr> meta_lst = to_list(metas.begin(), metas.end()); list<expr> meta_lst = to_list(metas.begin(), metas.end());
lazy_list<substitution> substs = unify(env, t, e_t, ngen.mk_child(), s.get_subst(), ios.get_options()); lazy_list<substitution> substs = unify(env, t, e_t, ngen.mk_child(), relax_main_opaque, s.get_subst(), ios.get_options());
return map2<proof_state>(substs, [=](substitution const & subst) -> proof_state { return map2<proof_state>(substs, [=](substitution const & subst) -> proof_state {
name_generator new_ngen(ngen); name_generator new_ngen(ngen);
type_checker tc(env, new_ngen.mk_child()); type_checker tc(env, new_ngen.mk_child());
@ -182,21 +182,21 @@ expr refresh_univ_metavars(expr const & e, name_generator & ngen) {
} }
} }
tactic apply_tactic(expr const & e, bool refresh_univ_mvars) { tactic apply_tactic(expr const & e, bool relax_main_opaque, bool refresh_univ_mvars) {
return tactic([=](environment const & env, io_state const & ios, proof_state const & s) { return tactic([=](environment const & env, io_state const & ios, proof_state const & s) {
if (refresh_univ_mvars) { if (refresh_univ_mvars) {
name_generator ngen = s.get_ngen(); name_generator ngen = s.get_ngen();
substitution new_subst = s.get_subst(); substitution new_subst = s.get_subst();
expr new_e = refresh_univ_metavars(new_subst.instantiate_all(e), ngen); expr new_e = refresh_univ_metavars(new_subst.instantiate_all(e), ngen);
proof_state new_s(s.get_goals(), new_subst, ngen); proof_state new_s(s.get_goals(), new_subst, ngen);
return apply_tactic_core(env, ios, new_s, new_e, true, true); return apply_tactic_core(env, ios, new_s, new_e, true, true, relax_main_opaque);
} else { } else {
return apply_tactic_core(env, ios, s, e, true, true); return apply_tactic_core(env, ios, s, e, true, true, relax_main_opaque);
} }
}); });
} }
tactic eassumption_tactic() { tactic eassumption_tactic(bool relax_main_opaque) {
return tactic([=](environment const & env, io_state const & ios, proof_state const & s) { return tactic([=](environment const & env, io_state const & ios, proof_state const & s) {
goals const & gs = s.get_goals(); goals const & gs = s.get_goals();
if (empty(gs)) if (empty(gs))
@ -206,7 +206,7 @@ tactic eassumption_tactic() {
buffer<expr> hs; buffer<expr> hs;
get_app_args(g.get_meta(), hs); get_app_args(g.get_meta(), hs);
for (expr const & h : hs) { for (expr const & h : hs) {
r = append(r, apply_tactic_core(env, ios, s, h, false, false)); r = append(r, apply_tactic_core(env, ios, s, h, false, false, relax_main_opaque));
} }
return r; return r;
}); });

View file

@ -8,7 +8,7 @@ Author: Leonardo de Moura
#include "util/lua.h" #include "util/lua.h"
#include "library/tactic/tactic.h" #include "library/tactic/tactic.h"
namespace lean { namespace lean {
tactic apply_tactic(expr const & e, bool refresh_univ_mvars = true); tactic apply_tactic(expr const & e, bool relax_main_opaque = true, bool refresh_univ_mvars = true);
tactic eassumption_tactic(); tactic eassumption_tactic(bool relax_main_opaque = true);
void open_apply_tactic(lua_State * L); void open_apply_tactic(lua_State * L);
} }

View file

@ -209,7 +209,7 @@ unify_status unify_simple(substitution & s, constraint const & c) {
return unify_status::Unsupported; return unify_status::Unsupported;
} }
static constraint g_dont_care_cnstr = mk_eq_cnstr(expr(), expr(), justification()); static constraint g_dont_care_cnstr = mk_eq_cnstr(expr(), expr(), justification(), false);
static unsigned g_group_size = 1u << 29; static unsigned g_group_size = 1u << 29;
static unsigned g_cnstr_group_first_index[6] = { 0, g_group_size, 2*g_group_size, 3*g_group_size, 4*g_group_size, 5*g_group_size}; static unsigned g_cnstr_group_first_index[6] = { 0, g_group_size, 2*g_group_size, 3*g_group_size, 4*g_group_size, 5*g_group_size};
static unsigned get_group_first_index(cnstr_group g) { static unsigned get_group_first_index(cnstr_group g) {
@ -241,7 +241,7 @@ struct unifier_fn {
name_generator m_ngen; name_generator m_ngen;
substitution m_subst; substitution m_subst;
unifier_plugin m_plugin; unifier_plugin m_plugin;
type_checker_ptr m_tc; type_checker_ptr m_tc[2];
bool m_use_exception; //!< True if we should throw an exception when there are no more solutions. bool m_use_exception; //!< True if we should throw an exception when there are no more solutions.
unsigned m_max_steps; unsigned m_max_steps;
unsigned m_num_steps; unsigned m_num_steps;
@ -292,14 +292,17 @@ struct unifier_fn {
m_assumption_idx(u.m_next_assumption_idx), m_jst(j), m_subst(u.m_subst), m_cnstrs(u.m_cnstrs), m_assumption_idx(u.m_next_assumption_idx), m_jst(j), m_subst(u.m_subst), m_cnstrs(u.m_cnstrs),
m_mvar_occs(u.m_mvar_occs) { m_mvar_occs(u.m_mvar_occs) {
u.m_next_assumption_idx++; u.m_next_assumption_idx++;
u.m_tc->push(); u.m_tc[0]->push();
u.m_tc[1]->push();
} }
/** \brief Restore unifier's state with saved values, and update m_assumption_idx and m_failed_justifications. */ /** \brief Restore unifier's state with saved values, and update m_assumption_idx and m_failed_justifications. */
void restore_state(unifier_fn & u) { void restore_state(unifier_fn & u) {
lean_assert(u.in_conflict()); lean_assert(u.in_conflict());
u.m_tc->pop(); // restore type checker state u.m_tc[0]->pop(); // restore type checker state
u.m_tc->push(); u.m_tc[1]->pop(); // restore type checker state
u.m_tc[0]->push();
u.m_tc[1]->push();
u.m_subst = m_subst; u.m_subst = m_subst;
u.m_cnstrs = m_cnstrs; u.m_cnstrs = m_cnstrs;
u.m_mvar_occs = m_mvar_occs; u.m_mvar_occs = m_mvar_occs;
@ -333,8 +336,9 @@ struct unifier_fn {
name_generator const & ngen, substitution const & s, name_generator const & ngen, substitution const & s,
bool use_exception, unsigned max_steps): bool use_exception, unsigned max_steps):
m_env(env), m_ngen(ngen), m_subst(s), m_plugin(get_unifier_plugin(env)), m_env(env), m_ngen(ngen), m_subst(s), m_plugin(get_unifier_plugin(env)),
m_tc(mk_type_checker_with_hints(env, m_ngen.mk_child())),
m_use_exception(use_exception), m_max_steps(max_steps), m_num_steps(0) { m_use_exception(use_exception), m_max_steps(max_steps), m_num_steps(0) {
m_tc[0] = mk_type_checker_with_hints(env, m_ngen.mk_child(), false);
m_tc[1] = mk_type_checker_with_hints(env, m_ngen.mk_child(), true);
m_next_assumption_idx = 0; m_next_assumption_idx = 0;
m_next_cidx = 0; m_next_cidx = 0;
m_first = true; m_first = true;
@ -403,9 +407,12 @@ struct unifier_fn {
return cidx; return cidx;
} }
/** \brief Check if \c t1 and \c t2 are definitionally equal, if they are not, set a conflict with justification \c j */ /** \brief Check if \c t1 and \c t2 are definitionally equal, if they are not, set a conflict with justification \c j
bool is_def_eq(expr const & t1, expr const & t2, justification const & j) {
if (m_tc->is_def_eq(t1, t2, j)) { \remark If relax is true then opaque definitions from the main module are treated as transparent.
*/
bool is_def_eq(expr const & t1, expr const & t2, justification const & j, bool relax) {
if (m_tc[relax]->is_def_eq(t1, t2, j)) {
return true; return true;
} else { } else {
set_conflict(j); set_conflict(j);
@ -417,21 +424,23 @@ struct unifier_fn {
\brief Assign \c v to metavariable \c m with justification \c j. \brief Assign \c v to metavariable \c m with justification \c j.
The type of v and m are inferred, and is_def_eq is invoked. The type of v and m are inferred, and is_def_eq is invoked.
Any constraint that contains \c m is revisited. Any constraint that contains \c m is revisited.
\remark If relax is true then opaque definitions from the main module are treated as transparent.
*/ */
bool assign(expr const & m, expr const & v, justification const & j) { bool assign(expr const & m, expr const & v, justification const & j, bool relax) {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
m_subst.assign(m, v, j); m_subst.assign(m, v, j);
expr m_type = mlocal_type(m); expr m_type = mlocal_type(m);
expr v_type; expr v_type;
try { try {
v_type = m_tc->infer(v); v_type = m_tc[relax]->infer(v);
} catch (kernel_exception & e) { } catch (kernel_exception & e) {
set_conflict(j); set_conflict(j);
return false; return false;
} }
if (in_conflict()) if (in_conflict())
return false; return false;
if (!is_def_eq(m_type, v_type, j)) if (!is_def_eq(m_type, v_type, j, relax))
return false; return false;
auto it = m_mvar_occs.find(mlocal_name(m)); auto it = m_mvar_occs.find(mlocal_name(m));
if (it) { if (it) {
@ -463,7 +472,7 @@ struct unifier_fn {
The method returns \c Failed if \c rhs contains <tt>?m</tt>, or it contains a local constant not in <tt>{l_1, ..., l_n}</tt>. The method returns \c Failed if \c rhs contains <tt>?m</tt>, or it contains a local constant not in <tt>{l_1, ..., l_n}</tt>.
Otherwise, it returns \c Continue. Otherwise, it returns \c Continue.
*/ */
status process_metavar_eq(expr const & lhs, expr const & rhs, justification const & j) { status process_metavar_eq(expr const & lhs, expr const & rhs, justification const & j, bool relax) {
if (!is_meta(lhs)) if (!is_meta(lhs))
return Continue; return Continue;
buffer<expr> locals; buffer<expr> locals;
@ -478,7 +487,7 @@ struct unifier_fn {
return Continue; return Continue;
case l_true: case l_true:
lean_assert(!m_subst.is_assigned(*m)); lean_assert(!m_subst.is_assigned(*m));
if (assign(*m, lambda_abstract_locals(rhs, locals), j)) { if (assign(*m, lambda_abstract_locals(rhs, locals), j, relax)) {
return Solved; return Solved;
} else { } else {
return Failed; return Failed;
@ -509,7 +518,8 @@ struct unifier_fn {
expr rhs = rhs_jst.first; expr rhs = rhs_jst.first;
if (lhs != cnstr_lhs_expr(c) || rhs != cnstr_rhs_expr(c)) { if (lhs != cnstr_lhs_expr(c) || rhs != cnstr_rhs_expr(c)) {
return mk_pair(mk_eq_cnstr(lhs, rhs, return mk_pair(mk_eq_cnstr(lhs, rhs,
mk_composite1(mk_composite1(c.get_justification(), lhs_jst.second), rhs_jst.second)), mk_composite1(mk_composite1(c.get_justification(), lhs_jst.second), rhs_jst.second),
relax_main_opaque(c)),
true); true);
} }
} else if (is_level_eq_cnstr(c)) { } else if (is_level_eq_cnstr(c)) {
@ -530,19 +540,20 @@ struct unifier_fn {
expr const & lhs = cnstr_lhs_expr(c); expr const & lhs = cnstr_lhs_expr(c);
expr const & rhs = cnstr_rhs_expr(c); expr const & rhs = cnstr_rhs_expr(c);
justification const & jst = c.get_justification(); justification const & jst = c.get_justification();
bool relax = relax_main_opaque(c);
if (lhs == rhs) if (lhs == rhs)
return Solved; // trivial constraint return Solved; // trivial constraint
// Update justification using the justification of the instantiated metavariables // Update justification using the justification of the instantiated metavariables
if (!has_metavar(lhs) && !has_metavar(rhs)) { if (!has_metavar(lhs) && !has_metavar(rhs)) {
return is_def_eq(lhs, rhs, jst) ? Solved : Failed; return is_def_eq(lhs, rhs, jst, relax) ? Solved : Failed;
} }
// Handle higher-order pattern matching. // Handle higher-order pattern matching.
status st = process_metavar_eq(lhs, rhs, jst); status st = process_metavar_eq(lhs, rhs, jst, relax);
if (st != Continue) return st; if (st != Continue) return st;
st = process_metavar_eq(rhs, lhs, jst); st = process_metavar_eq(rhs, lhs, jst, relax);
if (st != Continue) return st; if (st != Continue) return st;
return Continue; return Continue;
@ -589,11 +600,11 @@ struct unifier_fn {
expr lhs = instantiate_meta(cnstr_lhs_expr(c), j); expr lhs = instantiate_meta(cnstr_lhs_expr(c), j);
expr rhs = instantiate_meta(cnstr_rhs_expr(c), j); expr rhs = instantiate_meta(cnstr_rhs_expr(c), j);
if (lhs != cnstr_lhs_expr(c) || rhs != cnstr_rhs_expr(c)) if (lhs != cnstr_lhs_expr(c) || rhs != cnstr_rhs_expr(c))
return is_def_eq(lhs, rhs, j) ? Solved : Failed; return is_def_eq(lhs, rhs, j, relax_main_opaque(c)) ? Solved : Failed;
lhs = instantiate_meta_args(lhs, j); lhs = instantiate_meta_args(lhs, j);
rhs = instantiate_meta_args(rhs, j); rhs = instantiate_meta_args(rhs, j);
if (lhs != cnstr_lhs_expr(c) || rhs != cnstr_rhs_expr(c)) if (lhs != cnstr_lhs_expr(c) || rhs != cnstr_rhs_expr(c))
return is_def_eq(lhs, rhs, j) ? Solved : Failed; return is_def_eq(lhs, rhs, j, relax_main_opaque(c)) ? Solved : Failed;
return Continue; return Continue;
} }
@ -608,6 +619,7 @@ struct unifier_fn {
expr const & lhs = cnstr_lhs_expr(c); expr const & lhs = cnstr_lhs_expr(c);
expr const & rhs = cnstr_rhs_expr(c); expr const & rhs = cnstr_rhs_expr(c);
bool relax = relax_main_opaque(c);
if (is_eq_deltas(lhs, rhs)) { if (is_eq_deltas(lhs, rhs)) {
// we need to create a backtracking point for this one // we need to create a backtracking point for this one
@ -617,7 +629,7 @@ struct unifier_fn {
unsigned cidx = add_cnstr(c, cnstr_group::FlexFlex); unsigned cidx = add_cnstr(c, cnstr_group::FlexFlex);
add_meta_occ(lhs, cidx); add_meta_occ(lhs, cidx);
add_meta_occ(rhs, cidx); add_meta_occ(rhs, cidx);
} else if (m_plugin->delay_constraint(*m_tc, c)) { } else if (m_plugin->delay_constraint(*m_tc[relax], c)) {
unsigned cidx = add_cnstr(c, cnstr_group::PluginDelayed); unsigned cidx = add_cnstr(c, cnstr_group::PluginDelayed);
add_meta_occs(lhs, cidx); add_meta_occs(lhs, cidx);
add_meta_occs(rhs, cidx); add_meta_occs(rhs, cidx);
@ -749,7 +761,8 @@ struct unifier_fn {
} }
void pop_case_split() { void pop_case_split() {
m_tc->pop(); m_tc[0]->pop();
m_tc[1]->pop();
m_case_splits.pop_back(); m_case_splits.pop_back();
} }
@ -835,26 +848,28 @@ struct unifier_fn {
return lazy_list<constraints>(); return lazy_list<constraints>();
justification const & j = c.get_justification(); justification const & j = c.get_justification();
buffer<constraint> cs; buffer<constraint> cs;
lean_assert(!m_tc->next_cnstr()); bool relax = relax_main_opaque(c);
if (!m_tc->is_def_eq(f_lhs, f_rhs, j, cs)) lean_assert(!m_tc[relax]->next_cnstr());
if (!m_tc[relax]->is_def_eq(f_lhs, f_rhs, j, cs))
return lazy_list<constraints>(); return lazy_list<constraints>();
buffer<expr> args_lhs, args_rhs; buffer<expr> args_lhs, args_rhs;
get_app_args(lhs, args_lhs); get_app_args(lhs, args_lhs);
get_app_args(rhs, args_rhs); get_app_args(rhs, args_rhs);
if (args_lhs.size() != args_rhs.size()) if (args_lhs.size() != args_rhs.size())
return lazy_list<constraints>(); return lazy_list<constraints>();
lean_assert(!m_tc->next_cnstr()); lean_assert(!m_tc[relax]->next_cnstr());
for (unsigned i = 0; i < args_lhs.size(); i++) for (unsigned i = 0; i < args_lhs.size(); i++)
if (!m_tc->is_def_eq(args_lhs[i], args_rhs[i], j, cs)) if (!m_tc[relax]->is_def_eq(args_lhs[i], args_rhs[i], j, cs))
return lazy_list<constraints>(); return lazy_list<constraints>();
return lazy_list<constraints>(to_list(cs.begin(), cs.end())); return lazy_list<constraints>(to_list(cs.begin(), cs.end()));
} }
bool process_plugin_constraint(constraint const & c) { bool process_plugin_constraint(constraint const & c) {
bool relax = relax_main_opaque(c);
lean_assert(!is_choice_cnstr(c)); lean_assert(!is_choice_cnstr(c));
lean_assert(!m_tc->next_cnstr()); lean_assert(!m_tc[relax]->next_cnstr());
lazy_list<constraints> alts = m_plugin->solve(*m_tc, c, m_ngen.mk_child()); lazy_list<constraints> alts = m_plugin->solve(*m_tc[relax], c, m_ngen.mk_child());
lean_assert(!m_tc->next_cnstr()); lean_assert(!m_tc[relax]->next_cnstr());
alts = append(alts, process_const_const_cnstr(c)); alts = append(alts, process_const_const_cnstr(c));
return process_lazy_constraints(alts, c.get_justification()); return process_lazy_constraints(alts, c.get_justification());
} }
@ -864,8 +879,9 @@ struct unifier_fn {
expr const & m = cnstr_expr(c); expr const & m = cnstr_expr(c);
choice_fn const & fn = cnstr_choice_fn(c); choice_fn const & fn = cnstr_choice_fn(c);
expr m_type; expr m_type;
bool relax = relax_main_opaque(c);
try { try {
m_type = m_tc->infer(m); m_type = m_tc[relax]->infer(m);
} catch (kernel_exception &) { } catch (kernel_exception &) {
set_conflict(c.get_justification()); set_conflict(c.get_justification());
return false; return false;
@ -920,8 +936,9 @@ struct unifier_fn {
expr rhs_fn_val = instantiate_univ_params(d.get_value(), d.get_univ_params(), const_levels(rhs_fn)); expr rhs_fn_val = instantiate_univ_params(d.get_value(), d.get_univ_params(), const_levels(rhs_fn));
expr t = apply_beta(lhs_fn_val, lhs_args.size(), lhs_args.data()); expr t = apply_beta(lhs_fn_val, lhs_args.size(), lhs_args.data());
expr s = apply_beta(rhs_fn_val, rhs_args.size(), rhs_args.data()); expr s = apply_beta(rhs_fn_val, rhs_args.size(), rhs_args.data());
bool relax = relax_main_opaque(c);
buffer<constraint> cs2; buffer<constraint> cs2;
if (m_tc->is_def_eq(t, s, j, cs2)) { if (m_tc[relax]->is_def_eq(t, s, j, cs2)) {
// create a case split // create a case split
a = mk_assumption_justification(m_next_assumption_idx); a = mk_assumption_justification(m_next_assumption_idx);
add_case_split(std::unique_ptr<case_split>(new simple_case_split(*this, j, to_list(cs2.begin(), cs2.end())))); add_case_split(std::unique_ptr<case_split>(new simple_case_split(*this, j, to_list(cs2.begin(), cs2.end()))));
@ -941,7 +958,7 @@ struct unifier_fn {
unsigned i = lhs_args.size(); unsigned i = lhs_args.size();
while (i > 0) { while (i > 0) {
--i; --i;
if (!is_def_eq(lhs_args[i], rhs_args[i], new_j)) if (!is_def_eq(lhs_args[i], rhs_args[i], new_j, relax))
return false; return false;
} }
return true; return true;
@ -979,19 +996,19 @@ struct unifier_fn {
} }
/** \see ensure_sufficient_args */ /** \see ensure_sufficient_args */
optional<expr> ensure_sufficient_args_core(expr mtype, unsigned i, buffer<expr> const & margs) { optional<expr> ensure_sufficient_args_core(expr mtype, unsigned i, buffer<expr> const & margs, bool relax) {
if (i == margs.size()) if (i == margs.size())
return some_expr(mtype); return some_expr(mtype);
mtype = m_tc->ensure_pi(mtype); mtype = m_tc[relax]->ensure_pi(mtype);
try { try {
if (!m_tc->is_def_eq(binding_domain(mtype), m_tc->infer(margs[i]))) if (!m_tc[relax]->is_def_eq(binding_domain(mtype), m_tc[relax]->infer(margs[i])))
return none_expr(); return none_expr();
} catch (kernel_exception &) { } catch (kernel_exception &) {
return none_expr(); return none_expr();
} }
expr local = mk_local_for(mtype); expr local = mk_local_for(mtype);
expr body = instantiate(binding_body(mtype), local); expr body = instantiate(binding_body(mtype), local);
auto new_body = ensure_sufficient_args_core(body, i+1, margs); auto new_body = ensure_sufficient_args_core(body, i+1, margs, relax);
if (!new_body) if (!new_body)
return none_expr(); return none_expr();
return some_expr(Pi(local, *new_body)); return some_expr(Pi(local, *new_body));
@ -1001,7 +1018,8 @@ struct unifier_fn {
\brief Make sure mtype is a Pi of size at least margs.size(). \brief Make sure mtype is a Pi of size at least margs.size().
If it is not, we use ensure_pi and (potentially) add new constaints to enforce it. If it is not, we use ensure_pi and (potentially) add new constaints to enforce it.
*/ */
optional<expr> ensure_sufficient_args(expr const & mtype, buffer<expr> const & margs, buffer<constraint> & cs, justification const & j) { optional<expr> ensure_sufficient_args(expr const & mtype, buffer<expr> const & margs, buffer<constraint> & cs, justification const & j,
bool relax) {
expr t = mtype; expr t = mtype;
unsigned num = 0; unsigned num = 0;
while (is_pi(t)) { while (is_pi(t)) {
@ -1010,13 +1028,13 @@ struct unifier_fn {
} }
if (num == margs.size()) if (num == margs.size())
return some_expr(mtype);; return some_expr(mtype);;
lean_assert(!m_tc->next_cnstr()); // make sure there are no pending constraints lean_assert(!m_tc[relax]->next_cnstr()); // make sure there are no pending constraints
// We must create a scope to make sure no constraints "leak" into the current state. // We must create a scope to make sure no constraints "leak" into the current state.
type_checker::scope scope(*m_tc); type_checker::scope scope(*m_tc[relax]);
auto new_mtype = ensure_sufficient_args_core(mtype, 0, margs); auto new_mtype = ensure_sufficient_args_core(mtype, 0, margs, relax);
if (!new_mtype) if (!new_mtype)
return none_expr(); return none_expr();
while (auto c = m_tc->next_cnstr()) while (auto c = m_tc[relax]->next_cnstr())
cs.push_back(update_justification(*c, mk_composite1(c->get_justification(), j))); cs.push_back(update_justification(*c, mk_composite1(c->get_justification(), j)));
return new_mtype; return new_mtype;
} }
@ -1073,13 +1091,13 @@ struct unifier_fn {
- variable (if g is a local constant equal to a_i) - variable (if g is a local constant equal to a_i)
*/ */
void mk_flex_rigid_app_cnstrs(expr const & m, buffer<expr> const & margs, expr const & f, expr const & rhs, justification const & j, void mk_flex_rigid_app_cnstrs(expr const & m, buffer<expr> const & margs, expr const & f, expr const & rhs, justification const & j,
buffer<constraints> & alts) { buffer<constraints> & alts, bool relax) {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_app(rhs)); lean_assert(is_app(rhs));
lean_assert(is_constant(f) || is_var(f)); lean_assert(is_constant(f) || is_var(f));
buffer<constraint> cs; buffer<constraint> cs;
expr mtype = mlocal_type(m); expr mtype = mlocal_type(m);
auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j); auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j, relax);
if (!new_mtype) return; if (!new_mtype) return;
mtype = *new_mtype; mtype = *new_mtype;
buffer<expr> rargs; buffer<expr> rargs;
@ -1090,13 +1108,13 @@ struct unifier_fn {
sargs.push_back(*v); sargs.push_back(*v);
} else { } else {
expr maux = mk_aux_metavar_for(m_ngen, mtype); expr maux = mk_aux_metavar_for(m_ngen, mtype);
cs.push_back(mk_eq_cnstr(mk_app(maux, margs), rarg, j)); cs.push_back(mk_eq_cnstr(mk_app(maux, margs), rarg, j, relax));
sargs.push_back(mk_app_vars(maux, margs.size())); sargs.push_back(mk_app_vars(maux, margs.size()));
} }
} }
expr v = mk_app(f, sargs); expr v = mk_app(f, sargs);
v = mk_lambda_for(mtype, v); v = mk_lambda_for(mtype, v);
cs.push_back(mk_eq_cnstr(m, v, j)); cs.push_back(mk_eq_cnstr(m, v, j, relax));
alts.push_back(to_list(cs.begin(), cs.end())); alts.push_back(to_list(cs.begin(), cs.end()));
} }
@ -1112,25 +1130,25 @@ struct unifier_fn {
where l is a fresh local constant. where l is a fresh local constant.
*/ */
void mk_bindings_imitation(expr const & m, buffer<expr> const & margs, expr const & rhs, justification const & j, void mk_bindings_imitation(expr const & m, buffer<expr> const & margs, expr const & rhs, justification const & j,
buffer<constraints> & alts) { buffer<constraints> & alts, bool relax) {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_binding(rhs)); lean_assert(is_binding(rhs));
buffer<constraint> cs; buffer<constraint> cs;
expr mtype = mlocal_type(m); expr mtype = mlocal_type(m);
auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j); auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j, relax);
if (!new_mtype) return; if (!new_mtype) return;
mtype = *new_mtype; mtype = *new_mtype;
expr maux1 = mk_aux_metavar_for(m_ngen, mtype); expr maux1 = mk_aux_metavar_for(m_ngen, mtype);
cs.push_back(mk_eq_cnstr(mk_app(maux1, margs), binding_domain(rhs), j)); cs.push_back(mk_eq_cnstr(mk_app(maux1, margs), binding_domain(rhs), j, relax));
expr dontcare; expr dontcare;
expr tmp_pi = mk_pi(binding_name(rhs), mk_app_vars(maux1, margs.size()), dontcare); // trick for "extending" the context expr tmp_pi = mk_pi(binding_name(rhs), mk_app_vars(maux1, margs.size()), dontcare); // trick for "extending" the context
expr mtype2 = replace_range(mtype, tmp_pi); // trick for "extending" the context expr mtype2 = replace_range(mtype, tmp_pi); // trick for "extending" the context
expr maux2 = mk_aux_metavar_for(m_ngen, mtype2); expr maux2 = mk_aux_metavar_for(m_ngen, mtype2);
expr new_local = mk_local_for(rhs); expr new_local = mk_local_for(rhs);
cs.push_back(mk_eq_cnstr(mk_app(mk_app(maux2, margs), new_local), instantiate(binding_body(rhs), new_local), j)); cs.push_back(mk_eq_cnstr(mk_app(mk_app(maux2, margs), new_local), instantiate(binding_body(rhs), new_local), j, relax));
expr v = update_binding(rhs, mk_app_vars(maux1, margs.size()), mk_app_vars(maux2, margs.size() + 1)); expr v = update_binding(rhs, mk_app_vars(maux1, margs.size()), mk_app_vars(maux2, margs.size() + 1));
v = mk_lambda_for(mtype, v); v = mk_lambda_for(mtype, v);
cs.push_back(mk_eq_cnstr(m, v, j)); cs.push_back(mk_eq_cnstr(m, v, j, relax));
alts.push_back(to_list(cs.begin(), cs.end())); alts.push_back(to_list(cs.begin(), cs.end()));
} }
@ -1141,12 +1159,12 @@ struct unifier_fn {
Then solve (?m a_1 ... a_k) =?= rhs, by returning the constraint Then solve (?m a_1 ... a_k) =?= rhs, by returning the constraint
?m =?= fun (x1 ... x_k), rhs ?m =?= fun (x1 ... x_k), rhs
*/ */
void mk_simple_imitation(expr const & m, expr const & rhs, justification const & j, buffer<constraints> & alts) { void mk_simple_imitation(expr const & m, expr const & rhs, justification const & j, buffer<constraints> & alts, bool relax) {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_sort(rhs) || is_constant(rhs)); lean_assert(is_sort(rhs) || is_constant(rhs));
expr const & mtype = mlocal_type(m); expr const & mtype = mlocal_type(m);
buffer<constraint> cs; buffer<constraint> cs;
cs.push_back(mk_eq_cnstr(m, mk_lambda_for(mtype, rhs), j)); cs.push_back(mk_eq_cnstr(m, mk_lambda_for(mtype, rhs), j, relax));
alts.push_back(to_list(cs.begin(), cs.end())); alts.push_back(to_list(cs.begin(), cs.end()));
} }
@ -1162,24 +1180,24 @@ struct unifier_fn {
?m =?= fun (x_1 ... x_k), M((?m_1 x_1 ... x_k) ... (?m_n x_1 ... x_k)) ?m =?= fun (x_1 ... x_k), M((?m_1 x_1 ... x_k) ... (?m_n x_1 ... x_k))
*/ */
void mk_macro_imitation(expr const & m, buffer<expr> const & margs, expr const & rhs, justification const & j, void mk_macro_imitation(expr const & m, buffer<expr> const & margs, expr const & rhs, justification const & j,
buffer<constraints> & alts) { buffer<constraints> & alts, bool relax) {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_macro(rhs)); lean_assert(is_macro(rhs));
buffer<constraint> cs; buffer<constraint> cs;
expr mtype = mlocal_type(m); expr mtype = mlocal_type(m);
auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j); auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j, relax);
if (!new_mtype) return; if (!new_mtype) return;
mtype = *new_mtype; mtype = *new_mtype;
// create an auxiliary metavariable for each macro argument // create an auxiliary metavariable for each macro argument
buffer<expr> sargs; buffer<expr> sargs;
for (unsigned i = 0; i < macro_num_args(rhs); i++) { for (unsigned i = 0; i < macro_num_args(rhs); i++) {
expr maux = mk_aux_metavar_for(m_ngen, mtype); expr maux = mk_aux_metavar_for(m_ngen, mtype);
cs.push_back(mk_eq_cnstr(mk_app(maux, margs), macro_arg(rhs, i), j)); cs.push_back(mk_eq_cnstr(mk_app(maux, margs), macro_arg(rhs, i), j, relax));
sargs.push_back(mk_app_vars(maux, margs.size())); sargs.push_back(mk_app_vars(maux, margs.size()));
} }
expr v = mk_macro(macro_def(rhs), sargs.size(), sargs.data()); expr v = mk_macro(macro_def(rhs), sargs.size(), sargs.data());
v = mk_lambda_for(mtype, v); v = mk_lambda_for(mtype, v);
cs.push_back(mk_eq_cnstr(m, v, j)); cs.push_back(mk_eq_cnstr(m, v, j, relax));
alts.push_back(to_list(cs.begin(), cs.end())); alts.push_back(to_list(cs.begin(), cs.end()));
} }
@ -1198,17 +1216,17 @@ struct unifier_fn {
to alts as a possible solution. to alts as a possible solution.
*/ */
void mk_simple_nonlocal_projection(expr const & m, buffer<expr> const & margs, unsigned i, expr const & rhs, justification const & j, void mk_simple_nonlocal_projection(expr const & m, buffer<expr> const & margs, unsigned i, expr const & rhs, justification const & j,
buffer<constraints> & alts) { buffer<constraints> & alts, bool relax) {
expr const & mtype = mlocal_type(m); expr const & mtype = mlocal_type(m);
unsigned vidx = margs.size() - i - 1; unsigned vidx = margs.size() - i - 1;
expr const & marg = margs[i]; expr const & marg = margs[i];
buffer<constraint> cs; buffer<constraint> cs;
if (auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j)) { if (auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j, relax)) {
// Remark: we should not use mk_eq_cnstr(marg, rhs, j) since is_def_eq may be able to reduce them. // Remark: we should not use mk_eq_cnstr(marg, rhs, j) since is_def_eq may be able to reduce them.
// The unifier assumes the eq constraints are reduced. // The unifier assumes the eq constraints are reduced.
if (m_tc->is_def_eq(marg, rhs, j, cs)) { if (m_tc[relax]->is_def_eq(marg, rhs, j, cs)) {
expr v = mk_lambda_for(*new_mtype, mk_var(vidx)); expr v = mk_lambda_for(*new_mtype, mk_var(vidx));
cs.push_back(mk_eq_cnstr(m, v, j)); cs.push_back(mk_eq_cnstr(m, v, j, relax));
alts.push_back(to_list(cs.begin(), cs.end())); alts.push_back(to_list(cs.begin(), cs.end()));
} }
} }
@ -1232,7 +1250,7 @@ struct unifier_fn {
*/ */
void mk_simple_projections(expr const & m, buffer<expr> const & margs, expr const & rhs, justification const & j, void mk_simple_projections(expr const & m, buffer<expr> const & margs, expr const & rhs, justification const & j,
buffer<constraints> & alts) { buffer<constraints> & alts, bool relax) {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(!is_meta(rhs)); lean_assert(!is_meta(rhs));
expr const & mtype = mlocal_type(m); expr const & mtype = mlocal_type(m);
@ -1243,13 +1261,13 @@ struct unifier_fn {
expr const & marg = margs[i]; expr const & marg = margs[i];
if ((!is_local(marg) && !is_local(rhs)) || (is_meta(marg) && is_local(rhs))) { if ((!is_local(marg) && !is_local(rhs)) || (is_meta(marg) && is_local(rhs))) {
// if rhs is not local, then we only add projections for the nonlocal arguments of lhs // if rhs is not local, then we only add projections for the nonlocal arguments of lhs
mk_simple_nonlocal_projection(m, margs, i, rhs, j, alts); mk_simple_nonlocal_projection(m, margs, i, rhs, j, alts, relax);
} else if (is_local(marg) && is_local(rhs) && mlocal_name(marg) == mlocal_name(rhs)) { } else if (is_local(marg) && is_local(rhs) && mlocal_name(marg) == mlocal_name(rhs)) {
// if the argument is local, and rhs is equal to it, then we also add a projection // if the argument is local, and rhs is equal to it, then we also add a projection
buffer<constraint> cs; buffer<constraint> cs;
if (auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j)) { if (auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j, relax)) {
expr v = mk_lambda_for(*new_mtype, mk_var(vidx)); expr v = mk_lambda_for(*new_mtype, mk_var(vidx));
cs.push_back(mk_eq_cnstr(m, v, j)); cs.push_back(mk_eq_cnstr(m, v, j, relax));
alts.push_back(to_list(cs.begin(), cs.end())); alts.push_back(to_list(cs.begin(), cs.end()));
} }
} }
@ -1257,7 +1275,7 @@ struct unifier_fn {
} }
/** \brief Process a flex rigid constraint */ /** \brief Process a flex rigid constraint */
bool process_flex_rigid(expr const & lhs, expr const & rhs, justification const & j) { bool process_flex_rigid(expr const & lhs, expr const & rhs, justification const & j, bool relax) {
lean_assert(is_meta(lhs)); lean_assert(is_meta(lhs));
lean_assert(!is_meta(rhs)); lean_assert(!is_meta(rhs));
buffer<expr> margs; buffer<expr> margs;
@ -1266,7 +1284,7 @@ struct unifier_fn {
// Make sure that if marg is reducible to a local constant, then it is replaced with it. // Make sure that if marg is reducible to a local constant, then it is replaced with it.
// We need that because of the optimization based on is_easy_flex_rigid_arg // We need that because of the optimization based on is_easy_flex_rigid_arg
if (!is_local(marg)) { if (!is_local(marg)) {
expr new_marg = m_tc->whnf(marg); expr new_marg = m_tc[relax]->whnf(marg);
if (is_local(new_marg)) if (is_local(new_marg))
marg = new_marg; marg = new_marg;
} }
@ -1276,19 +1294,19 @@ struct unifier_fn {
case expr_kind::Var: case expr_kind::Meta: case expr_kind::Var: case expr_kind::Meta:
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::Local: case expr_kind::Local:
mk_simple_projections(m, margs, rhs, j, alts); mk_simple_projections(m, margs, rhs, j, alts, relax);
break; break;
case expr_kind::Sort: case expr_kind::Constant: case expr_kind::Sort: case expr_kind::Constant:
mk_simple_projections(m, margs, rhs, j, alts); mk_simple_projections(m, margs, rhs, j, alts, relax);
mk_simple_imitation(m, rhs, j, alts); mk_simple_imitation(m, rhs, j, alts, relax);
break; break;
case expr_kind::Pi: case expr_kind::Lambda: case expr_kind::Pi: case expr_kind::Lambda:
mk_simple_projections(m, margs, rhs, j, alts); mk_simple_projections(m, margs, rhs, j, alts, relax);
mk_bindings_imitation(m, margs, rhs, j, alts); mk_bindings_imitation(m, margs, rhs, j, alts, relax);
break; break;
case expr_kind::Macro: case expr_kind::Macro:
mk_simple_projections(m, margs, rhs, j, alts); mk_simple_projections(m, margs, rhs, j, alts, relax);
mk_macro_imitation(m, margs, rhs, j, alts); mk_macro_imitation(m, margs, rhs, j, alts, relax);
break; break;
case expr_kind::App: { case expr_kind::App: {
expr const & f = get_app_fn(rhs); expr const & f = get_app_fn(rhs);
@ -1299,17 +1317,17 @@ struct unifier_fn {
--i; --i;
expr const & marg = margs[i]; expr const & marg = margs[i];
if (is_local(marg) && mlocal_name(marg) == mlocal_name(f)) if (is_local(marg) && mlocal_name(marg) == mlocal_name(f))
mk_flex_rigid_app_cnstrs(m, margs, mk_var(vidx), rhs, j, alts); mk_flex_rigid_app_cnstrs(m, margs, mk_var(vidx), rhs, j, alts, relax);
else else
mk_simple_nonlocal_projection(m, margs, i, rhs, j, alts); mk_simple_nonlocal_projection(m, margs, i, rhs, j, alts, relax);
} }
} else if (is_constant(f)) { } else if (is_constant(f)) {
mk_simple_projections(m, margs, rhs, j, alts); mk_simple_projections(m, margs, rhs, j, alts, relax);
mk_flex_rigid_app_cnstrs(m, margs, f, rhs, j, alts); mk_flex_rigid_app_cnstrs(m, margs, f, rhs, j, alts, relax);
} else { } else {
expr new_rhs = m_tc->whnf(rhs); expr new_rhs = m_tc[relax]->whnf(rhs);
lean_assert(new_rhs != rhs); lean_assert(new_rhs != rhs);
return is_def_eq(lhs, new_rhs, j); return is_def_eq(lhs, new_rhs, j, relax);
} }
break; break;
}} }}
@ -1330,12 +1348,13 @@ struct unifier_fn {
/** \brief Process a flex rigid constraint */ /** \brief Process a flex rigid constraint */
bool process_flex_rigid(constraint const & c) { bool process_flex_rigid(constraint const & c) {
lean_assert(is_flex_rigid(c)); lean_assert(is_flex_rigid(c));
expr lhs = cnstr_lhs_expr(c); expr lhs = cnstr_lhs_expr(c);
expr rhs = cnstr_rhs_expr(c); expr rhs = cnstr_rhs_expr(c);
bool relax = relax_main_opaque(c);
if (is_meta(lhs)) if (is_meta(lhs))
return process_flex_rigid(lhs, rhs, c.get_justification()); return process_flex_rigid(lhs, rhs, c.get_justification(), relax);
else else
return process_flex_rigid(rhs, lhs, c.get_justification()); return process_flex_rigid(rhs, lhs, c.get_justification(), relax);
} }
bool process_flex_flex(constraint const & c) { bool process_flex_flex(constraint const & c) {
@ -1358,18 +1377,20 @@ struct unifier_fn {
if (mlocal_name(lhs_args[i]) != mlocal_name(rhs_args[i])) if (mlocal_name(lhs_args[i]) != mlocal_name(rhs_args[i]))
break; break;
if (i == lhs_args.size()) if (i == lhs_args.size())
return assign(ml, mr, c.get_justification()); return assign(ml, mr, c.get_justification(), relax_main_opaque(c));
return true; return true;
} }
void consume_tc_cnstrs() { void consume_tc_cnstrs() {
while (true) { for (unsigned i = 0; i < 2; i++) {
if (in_conflict()) while (true) {
return; if (in_conflict())
if (auto c = m_tc->next_cnstr()) { return;
process_constraint(*c); if (auto c = m_tc[i]->next_cnstr()) {
} else { process_constraint(*c);
break; } else {
break;
}
} }
} }
} }
@ -1417,7 +1438,8 @@ struct unifier_fn {
} else { } else {
auto r = instantiate_metavars(c); auto r = instantiate_metavars(c);
c = r.first; c = r.first;
lean_assert(!m_tc->next_cnstr()); lean_assert(!m_tc[0]->next_cnstr());
lean_assert(!m_tc[1]->next_cnstr());
bool modified = r.second; bool modified = r.second;
if (is_level_eq_cnstr(c)) { if (is_level_eq_cnstr(c)) {
if (modified) if (modified)
@ -1432,7 +1454,7 @@ struct unifier_fn {
if (is_delta_cnstr(c)) { if (is_delta_cnstr(c)) {
return process_delta(c); return process_delta(c);
} else if (modified) { } else if (modified) {
return is_def_eq(cnstr_lhs_expr(c), cnstr_rhs_expr(c), c.get_justification()); return is_def_eq(cnstr_lhs_expr(c), cnstr_rhs_expr(c), c.get_justification(), relax_main_opaque(c));
} else if (is_flex_rigid(c)) { } else if (is_flex_rigid(c)) {
return process_flex_rigid(c); return process_flex_rigid(c);
} else if (is_flex_flex(c)) { } else if (is_flex_flex(c)) {
@ -1510,21 +1532,21 @@ lazy_list<substitution> unify(environment const & env, unsigned num_cs, constra
return unify(env, num_cs, cs, ngen, use_exception, get_unifier_max_steps(o)); return unify(env, num_cs, cs, ngen, use_exception, get_unifier_max_steps(o));
} }
lazy_list<substitution> unify(environment const & env, expr const & lhs, expr const & rhs, name_generator const & ngen, substitution const & s, lazy_list<substitution> unify(environment const & env, expr const & lhs, expr const & rhs, name_generator const & ngen,
unsigned max_steps) { bool relax, substitution const & s, unsigned max_steps) {
substitution new_s = s; substitution new_s = s;
expr _lhs = new_s.instantiate(lhs); expr _lhs = new_s.instantiate(lhs);
expr _rhs = new_s.instantiate(rhs); expr _rhs = new_s.instantiate(rhs);
auto u = std::make_shared<unifier_fn>(env, 0, nullptr, ngen, new_s, false, max_steps); auto u = std::make_shared<unifier_fn>(env, 0, nullptr, ngen, new_s, false, max_steps);
if (!u->m_tc->is_def_eq(_lhs, _rhs)) if (!u->m_tc[relax]->is_def_eq(_lhs, _rhs))
return lazy_list<substitution>(); return lazy_list<substitution>();
else else
return unify(u); return unify(u);
} }
lazy_list<substitution> unify(environment const & env, expr const & lhs, expr const & rhs, name_generator const & ngen, lazy_list<substitution> unify(environment const & env, expr const & lhs, expr const & rhs, name_generator const & ngen,
substitution const & s, options const & o) { bool relax, substitution const & s, options const & o) {
return unify(env, lhs, rhs, ngen, s, get_unifier_max_steps(o)); return unify(env, lhs, rhs, ngen, relax, s, get_unifier_max_steps(o));
} }
static int unify_simple(lua_State * L) { static int unify_simple(lua_State * L) {
@ -1646,10 +1668,12 @@ static int unify(lua_State * L) {
lazy_list<substitution> r; lazy_list<substitution> r;
environment const & env = to_environment(L, 1); environment const & env = to_environment(L, 1);
if (is_expr(L, 2)) { if (is_expr(L, 2)) {
if (nargs == 6) if (nargs == 7)
r = unify(env, to_expr(L, 2), to_expr(L, 3), to_name_generator(L, 4), to_substitution(L, 5), to_options(L, 6)); r = unify(env, to_expr(L, 2), to_expr(L, 3), to_name_generator(L, 4), lua_toboolean(L, 5), to_substitution(L, 6), to_options(L, 7));
else if (nargs == 6)
r = unify(env, to_expr(L, 2), to_expr(L, 3), to_name_generator(L, 4), lua_toboolean(L, 5), to_substitution(L, 6), options());
else else
r = unify(env, to_expr(L, 2), to_expr(L, 3), to_name_generator(L, 4), to_substitution(L, 5), options()); r = unify(env, to_expr(L, 2), to_expr(L, 3), to_name_generator(L, 4), lua_toboolean(L, 5));
} else { } else {
buffer<constraint> cs; buffer<constraint> cs;
to_constraint_buffer(L, 2, cs); to_constraint_buffer(L, 2, cs);

View file

@ -40,10 +40,10 @@ lazy_list<substitution> unify(environment const & env, unsigned num_cs, constrai
bool use_exception = true, unsigned max_steps = LEAN_DEFAULT_UNIFIER_MAX_STEPS); bool use_exception = true, unsigned max_steps = LEAN_DEFAULT_UNIFIER_MAX_STEPS);
lazy_list<substitution> unify(environment const & env, unsigned num_cs, constraint const * cs, name_generator const & ngen, lazy_list<substitution> unify(environment const & env, unsigned num_cs, constraint const * cs, name_generator const & ngen,
bool use_exception, options const & o); bool use_exception, options const & o);
lazy_list<substitution> unify(environment const & env, expr const & lhs, expr const & rhs, name_generator const & ngen, lazy_list<substitution> unify(environment const & env, expr const & lhs, expr const & rhs, name_generator const & ngen, bool relax_main_opaque,
substitution const & s = substitution(), unsigned max_steps = LEAN_DEFAULT_UNIFIER_MAX_STEPS); substitution const & s = substitution(), unsigned max_steps = LEAN_DEFAULT_UNIFIER_MAX_STEPS);
lazy_list<substitution> unify(environment const & env, expr const & lhs, expr const & rhs, name_generator const & ngen, substitution const & s, lazy_list<substitution> unify(environment const & env, expr const & lhs, expr const & rhs, name_generator const & ngen,
options const & o); bool relax_main_opaque, substitution const & s, options const & o);
/** /**
The unifier divides the constraints in 6 groups: Simple, Basic, FlexRigid, PluginDelayed, PreFlexFlex, FlexFlex, MaxDelayed The unifier divides the constraints in 6 groups: Simple, Basic, FlexRigid, PluginDelayed, PreFlexFlex, FlexFlex, MaxDelayed

View file

@ -18,7 +18,7 @@ static void tst1() {
expr m = mk_metavar("m", A); expr m = mk_metavar("m", A);
expr t1 = f(m, m); expr t1 = f(m, m);
expr t2 = f(a, b); expr t2 = f(a, b);
auto r = unify(env, t1, t2, ngen); auto r = unify(env, t1, t2, ngen, false);
lean_assert(!r.pull()); lean_assert(!r.pull());
} }

View file

@ -69,6 +69,7 @@ variable div : Π (x y : nat) {H : not_zero y}, nat
variables a b : nat variables a b : nat
set_option pp.implicit true
opaque_hint (hiding [module]) opaque_hint (hiding [module])
check div a (succ b) check div a (succ b)
check (λ H : not_zero b, div a b) check (λ H : not_zero b, div a b)

View file

@ -27,7 +27,7 @@ print(env:whnf(t(m)))
function test_unify(env, lhs, rhs, num_s) function test_unify(env, lhs, rhs, num_s)
print(tostring(lhs) .. " =?= " .. tostring(rhs) .. ", expected: " .. tostring(num_s)) print(tostring(lhs) .. " =?= " .. tostring(rhs) .. ", expected: " .. tostring(num_s))
local ss = unify(env, lhs, rhs, name_generator(), substitution(), options()) local ss = unify(env, lhs, rhs, name_generator(), true, substitution(), options())
local n = 0 local n = 0
for s in ss do for s in ss do
print("solution: ") print("solution: ")

View file

@ -30,7 +30,7 @@ print(env:whnf(t(m)))
function test_unify(env, lhs, rhs, num_s) function test_unify(env, lhs, rhs, num_s)
print(tostring(lhs) .. " =?= " .. tostring(rhs) .. ", expected: " .. tostring(num_s)) print(tostring(lhs) .. " =?= " .. tostring(rhs) .. ", expected: " .. tostring(num_s))
local ss = unify(env, lhs, rhs, name_generator(), substitution(), options()) local ss = unify(env, lhs, rhs, name_generator(), true, substitution(), options())
local n = 0 local n = 0
for s in ss do for s in ss do
print("solution: ") print("solution: ")

11
tests/lean/run/univ2.lean Normal file
View file

@ -0,0 +1,11 @@
import standard
hypothesis I : Type
definition F (X : Type) : Type := (X → Prop) → Prop
hypothesis unfold : I → F I
hypothesis fold : F I → I
hypothesis iso1 : ∀x, fold (unfold x) = x
variable sorry {A : Type} : A
theorem iso2 : ∀x, fold (unfold x) = x
:= sorry

View file

@ -1,6 +1,6 @@
function test_unify(env, lhs, rhs, num_s) function test_unify(env, lhs, rhs, num_s)
print(tostring(lhs) .. " =?= " .. tostring(rhs) .. ", expected: " .. tostring(num_s)) print(tostring(lhs) .. " =?= " .. tostring(rhs) .. ", expected: " .. tostring(num_s))
local ss = unify(env, lhs, rhs, name_generator(), substitution(), options()) local ss = unify(env, lhs, rhs, name_generator(), true, substitution(), options())
local n = 0 local n = 0
for s in ss do for s in ss do
print("solution: ") print("solution: ")

View file

@ -1,6 +1,6 @@
function test_unify(env, m, lhs, rhs, num_s) function test_unify(env, m, lhs, rhs, num_s)
print(tostring(lhs) .. " =?= " .. tostring(rhs) .. ", expected: " .. tostring(num_s)) print(tostring(lhs) .. " =?= " .. tostring(rhs) .. ", expected: " .. tostring(num_s))
local ss = unify(env, lhs, rhs, name_generator(), substitution(), options()) local ss = unify(env, lhs, rhs, name_generator(), true, substitution(), options())
local n = 0 local n = 0
for s in ss do for s in ss do
print("solution: " .. tostring(s:instantiate(m))) print("solution: " .. tostring(s:instantiate(m)))