refactor(kernel/type_checker): remove "global" constraint buffer from type_checker, and use constraint_seq instead

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2014-08-19 22:31:26 -07:00
parent 4cf3d32e0c
commit 9588336c15
44 changed files with 945 additions and 965 deletions

View file

@ -221,6 +221,9 @@ list_induction_on s
theorem mem_concat (x : T) (s t : list T) : x ∈ s ++ t ↔ x ∈ s x ∈ t theorem mem_concat (x : T) (s t : list T) : x ∈ s ++ t ↔ x ∈ s x ∈ t
:= iff_intro (mem_concat_imp_or _ _ _) (mem_or_imp_concat _ _ _) := iff_intro (mem_concat_imp_or _ _ _) (mem_or_imp_concat _ _ _)
section
set_option unifier.expensive true -- TODO(Leo): remove after we add delta-split step
#erase_cache mem_split
theorem mem_split (x : T) (l : list T) : x ∈ l → ∃s t : list T, l = s ++ (x :: t) := theorem mem_split (x : T) (l : list T) : x ∈ l → ∃s t : list T, l = s ++ (x :: t) :=
list_induction_on l list_induction_on l
(take H : x ∈ nil, false_elim _ (iff_elim_left (mem_nil x) H)) (take H : x ∈ nil, false_elim _ (iff_elim_left (mem_nil x) H))
@ -235,8 +238,9 @@ list_induction_on l
obtain s (H2 : ∃t : list T, l = s ++ (x :: t)), from IH H1, obtain s (H2 : ∃t : list T, l = s ++ (x :: t)), from IH H1,
obtain t (H3 : l = s ++ (x :: t)), from H2, obtain t (H3 : l = s ++ (x :: t)), from H2,
have H4 : y :: l = (y :: s) ++ (x :: t), have H4 : y :: l = (y :: s) ++ (x :: t),
from trans (subst H3 (refl (y :: l))) (cons_concat _ _ _), from subst H3 (refl (y :: l)),
exists_intro _ (exists_intro _ H4))) exists_intro _ (exists_intro _ H4)))
end
-- Find -- Find
-- ---- -- ----

View file

@ -32,6 +32,7 @@ or_elim u_def
(assume Hp : p, or_inr Hp)) (assume Hp : p, or_inr Hp))
(assume Hp : p, or_inr Hp) (assume Hp : p, or_inr Hp)
set_option unifier.expensive true
lemma p_implies_uv [private] : p → u = v := lemma p_implies_uv [private] : p → u = v :=
assume Hp : p, assume Hp : p,
have Hpred : (λ x, x = true p) = (λ x, x = false p), from have Hpred : (λ x, x = true p) = (λ x, x = false p), from

View file

@ -248,10 +248,6 @@ add_subdirectory(kernel/record)
set(LEAN_LIBS ${LEAN_LIBS} record) set(LEAN_LIBS ${LEAN_LIBS} record)
add_subdirectory(library) add_subdirectory(library)
set(LEAN_LIBS ${LEAN_LIBS} library) set(LEAN_LIBS ${LEAN_LIBS} library)
# add_subdirectory(library/rewriter)
# set(LEAN_LIBS ${LEAN_LIBS} rewriter)
# add_subdirectory(library/simplifier)
# set(LEAN_LIBS ${LEAN_LIBS} simplifier)
add_subdirectory(library/tactic) add_subdirectory(library/tactic)
set(LEAN_LIBS ${LEAN_LIBS} tactic) set(LEAN_LIBS ${LEAN_LIBS} tactic)
add_subdirectory(library/error_handling) add_subdirectory(library/error_handling)

View file

@ -96,7 +96,7 @@ environment check_cmd(parser & p) {
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(), true); 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)).first;
auto reg = p.regular_stream(); auto reg = p.regular_stream();
formatter const & fmt = reg.get_formatter(); formatter const & fmt = reg.get_formatter();
options opts = p.ios().get_options(); options opts = p.ios().get_options();

View file

@ -301,7 +301,6 @@ class elaborator {
context m_context; // current local context: a list of local constants context m_context; // current local context: a list of local constants
context m_full_context; // superset of m_context, it also contains non-contextual locals. context m_full_context; // superset of m_context, it also contains non-contextual locals.
constraint_vect m_constraints; // constraints that must be solved for the elaborated term to be type correct.
local_tactic_hints m_local_tactic_hints; // mapping from metavariable name ?m to tactic expression that should be used to solve it. local_tactic_hints m_local_tactic_hints; // mapping from metavariable name ?m to tactic expression that should be used to solve it.
// this mapping is populated by the 'by tactic-expr' expression. // this mapping is populated by the 'by tactic-expr' expression.
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
@ -315,35 +314,17 @@ class elaborator {
scope_ctx(elaborator & e):m_scope1(e.m_context), m_scope2(e.m_full_context) {} scope_ctx(elaborator & e):m_scope1(e.m_context), m_scope2(e.m_full_context) {}
}; };
/** \brief Auxiliary object for creating backtracking points, and replacing the local scopes. /** \brief Auxiliary object for creating backtracking points, and replacing the local scopes. */
\remark A new scope can only be created when m_constraints is empty.
*/
struct new_scope { struct new_scope {
elaborator & m_main; elaborator & m_main;
context::scope_replace m_context_scope; context::scope_replace m_context_scope;
context::scope_replace m_full_context_scope; context::scope_replace m_full_context_scope;
new_scope(elaborator & e, list<expr> const & ctx, list<expr> const & full_ctx): new_scope(elaborator & e, list<expr> const & ctx, list<expr> const & full_ctx):
m_main(e), m_context_scope(e.m_context, ctx), m_full_context_scope(e.m_full_context, full_ctx) { m_main(e), m_context_scope(e.m_context, ctx), m_full_context_scope(e.m_full_context, full_ctx) {
lean_assert(m_main.m_constraints.empty());
m_main.m_tc[0]->push();
m_main.m_tc[1]->push();
}
~new_scope() {
m_main.m_tc[0]->pop();
m_main.m_tc[1]->pop();
m_main.m_constraints.clear();
lean_assert(m_main.m_constraints.empty());
} }
~new_scope() {}
}; };
/* \brief Move all constraints generated by the type checker to the buffer m_constraints. */
void consume_tc_cnstrs() {
for (unsigned i = 0; i < 2; i++)
while (auto c = m_tc[i]->next_cnstr())
m_constraints.push_back(*c);
}
struct choice_elaborator { struct choice_elaborator {
bool m_ignore_failure; bool m_ignore_failure;
choice_elaborator(bool ignore_failure = false):m_ignore_failure(ignore_failure) {} choice_elaborator(bool ignore_failure = false):m_ignore_failure(ignore_failure) {}
@ -379,11 +360,10 @@ class elaborator {
m_elab.save_identifier_info(f); m_elab.save_identifier_info(f);
try { try {
new_scope s(m_elab, m_ctx, m_full_ctx); new_scope s(m_elab, m_ctx, m_full_ctx);
expr r = m_elab.visit(c); pair<expr, constraint_seq> rcs = m_elab.visit(c);
m_elab.consume_tc_cnstrs(); expr r = rcs.first;
list<constraint> cs = to_list(m_elab.m_constraints.begin(), m_elab.m_constraints.end()); constraint_seq cs = mk_eq_cnstr(m_mvar, r, justification(), m_relax_main_opaque) + rcs.second;
cs = cons(mk_eq_cnstr(m_mvar, r, justification(), m_relax_main_opaque), cs); return optional<constraints>(cs.to_list());
return optional<constraints>(cs);
} catch (exception &) {} } catch (exception &) {}
} }
return optional<constraints>(); return optional<constraints>();
@ -444,13 +424,11 @@ class elaborator {
} }
try { try {
new_scope s(m_elab, m_ctx, m_full_ctx); new_scope s(m_elab, m_ctx, m_full_ctx);
expr r = m_elab.visit(pre); // use elaborator to create metavariables, levels, etc. pair<expr, constraint_seq> rcs = m_elab.visit(pre); // use elaborator to create metavariables, levels, etc.
m_elab.consume_tc_cnstrs(); expr r = rcs.first;
for (auto & c : m_elab.m_constraints) buffer<constraint> cs;
c = update_justification(c, mk_composite1(m_jst, c.get_justification())); to_buffer(rcs.second, m_jst, cs);
list<constraint> cs = to_list(m_elab.m_constraints.begin(), m_elab.m_constraints.end()); return optional<constraints>(cons(mk_eq_cnstr(m_meta, r, m_jst, m_relax_main_opaque), to_list(cs.begin(), cs.end())));
cs = cons(mk_eq_cnstr(m_meta, r, m_jst, m_relax_main_opaque), cs);
return optional<constraints>(cs);
} catch (exception &) { } catch (exception &) {
return optional<constraints>(); return optional<constraints>();
} }
@ -539,31 +517,53 @@ public:
return ::lean::mk_local(m_ngen.next(), n, t, bi); return ::lean::mk_local(m_ngen.next(), n, t, bi);
} }
expr infer_type(expr const & e) { pair<expr, constraint_seq> infer_type(expr const & e) { return m_tc[m_relax_main_opaque]->infer(e); }
lean_assert(closed(e)); pair<expr, constraint_seq> whnf(expr const & e) { return m_tc[m_relax_main_opaque]->whnf(e); }
return m_tc[m_relax_main_opaque]->infer(e); expr infer_type(expr const & e, constraint_seq & s) { return m_tc[m_relax_main_opaque]->infer(e, s); }
expr whnf(expr const & e, constraint_seq & s) { return m_tc[m_relax_main_opaque]->whnf(e, s); }
static expr save_tag(expr && e, tag g) { e.set_tag(g); return e; }
expr mk_app(expr const & f, expr const & a, tag g) { return save_tag(::lean::mk_app(f, a), g); }
/** \brief Store the pair (pos(e), type(r)) in the info_data if m_info_manager is available. */
void save_info_data(expr const & e, expr const & r) {
if (!m_noinfo && infom() && pip() && (is_constant(e) || is_local(e) || is_placeholder(e))) {
if (auto p = pip()->get_pos_info(e)) {
expr t = m_tc[m_relax_main_opaque]->infer(r).first;
m_pre_info_data.add_type_info(p->first, p->second, t);
}
}
} }
expr whnf(expr const & e) { /** \brief Auxiliary function for saving information about which overloaded identifier was used by the elaborator. */
return m_tc[m_relax_main_opaque]->whnf(e); void save_identifier_info(expr const & f) {
if (!m_noinfo && infom() && pip() && is_constant(f)) {
if (auto p = pip()->get_pos_info(f))
m_pre_info_data.add_identifier_info(p->first, p->second, const_name(f));
}
} }
/** \brief Clear constraint buffer \c m_constraints */ /** \brief Store actual term that was synthesized for an explicit placeholders */
void clear_constraints() { void save_synth_data(expr const & e, expr const & r) {
m_constraints.clear(); if (!m_noinfo && infom() && pip() && is_placeholder(e)) {
if (auto p = pip()->get_pos_info(e))
m_pre_info_data.add_synth_info(p->first, p->second, r);
}
} }
void add_cnstr(constraint const & c) { void save_placeholder_info(expr const & e, expr const & r) {
m_constraints.push_back(c); if (is_explicit_placeholder(e)) {
save_info_data(e, r);
save_synth_data(e, r);
}
} }
static expr save_tag(expr && e, tag g) { void copy_info_to_manager(substitution s) {
e.set_tag(g); if (!infom())
return e; return;
} m_pre_info_data.instantiate(s);
infom()->merge(m_pre_info_data);
expr mk_app(expr const & f, expr const & a, tag g) { m_pre_info_data.clear();
return save_tag(::lean::mk_app(f, a), g);
} }
list<name> get_class_instances(expr const & type) { list<name> get_class_instances(expr const & type) {
@ -598,7 +598,7 @@ public:
return mk_justification(m, [=](formatter const & fmt, substitution const & subst) { return mk_justification(m, [=](formatter const & fmt, substitution const & subst) {
substitution tmp(subst); substitution tmp(subst);
expr new_m = instantiate_meta(m, tmp); expr new_m = instantiate_meta(m, tmp);
expr new_type = type_checker(_env).infer(new_m); expr new_type = type_checker(_env).infer(new_m).first;
proof_state ps(goals(goal(new_m, new_type)), substitution(), name_generator("dontcare")); proof_state ps(goals(goal(new_m, new_type)), substitution(), name_generator("dontcare"));
return format({format("failed to synthesize placeholder"), line(), ps.pp(fmt)}); return format({format("failed to synthesize placeholder"), line(), ps.pp(fmt)});
}); });
@ -607,7 +607,7 @@ public:
/** \brief Create a metavariable, and attach choice constraint for generating /** \brief Create a metavariable, and attach choice constraint for generating
solutions using class-instances and tactic-hints. solutions using class-instances and tactic-hints.
*/ */
expr mk_placeholder_meta(optional<expr> const & type, tag g, bool is_strict = false) { expr mk_placeholder_meta(optional<expr> const & type, tag g, bool is_strict, constraint_seq & cs) {
expr m = m_context.mk_meta(type, g); expr m = m_context.mk_meta(type, g);
list<expr> ctx = m_context.get_data(); list<expr> ctx = m_context.get_data();
list<expr> full_ctx = m_full_context.get_data(); list<expr> full_ctx = m_full_context.get_data();
@ -639,7 +639,7 @@ public:
j, ignore_failure, m_relax_main_opaque)); j, ignore_failure, m_relax_main_opaque));
} }
}; };
add_cnstr(mk_choice_cnstr(m, choice_fn, to_delay_factor(cnstr_group::ClassInstance), false, j, m_relax_main_opaque)); cs += mk_choice_cnstr(m, choice_fn, to_delay_factor(cnstr_group::ClassInstance), false, j, m_relax_main_opaque);
return m; return m;
} }
@ -653,38 +653,31 @@ public:
return none_expr(); return none_expr();
} }
void save_placeholder_info(expr const & e, expr const & r) { expr visit_expecting_type(expr const & e, constraint_seq & cs) {
if (is_explicit_placeholder(e)) {
save_info_data(e, r);
save_synth_data(e, r);
}
}
expr visit_expecting_type(expr const & e) {
if (is_placeholder(e) && !placeholder_type(e)) { if (is_placeholder(e) && !placeholder_type(e)) {
expr r = m_context.mk_type_meta(e.get_tag()); expr r = m_context.mk_type_meta(e.get_tag());
save_placeholder_info(e, r); save_placeholder_info(e, r);
return r; return r;
} else { } else {
return visit(e); return visit(e, cs);
} }
} }
expr visit_expecting_type_of(expr const & e, expr const & t) { expr visit_expecting_type_of(expr const & e, expr const & t, constraint_seq & cs) {
if (is_placeholder(e) && !placeholder_type(e)) { if (is_placeholder(e) && !placeholder_type(e)) {
expr r = mk_placeholder_meta(some_expr(t), e.get_tag(), is_strict_placeholder(e)); expr r = mk_placeholder_meta(some_expr(t), e.get_tag(), is_strict_placeholder(e), cs);
save_placeholder_info(e, r); save_placeholder_info(e, r);
return r; return r;
} else if (is_choice(e)) { } else if (is_choice(e)) {
return visit_choice(e, some_expr(t)); return visit_choice(e, some_expr(t), cs);
} else if (is_by(e)) { } else if (is_by(e)) {
return visit_by(e, some_expr(t)); return visit_by(e, some_expr(t), cs);
} else { } else {
return visit(e); return visit(e, cs);
} }
} }
expr visit_choice(expr const & e, optional<expr> const & t) { expr visit_choice(expr const & e, optional<expr> const & t, constraint_seq & cs) {
lean_assert(is_choice(e)); lean_assert(is_choice(e));
// Possible optimization: try to lookahead and discard some of the alternatives. // Possible optimization: try to lookahead and discard some of the alternatives.
expr m = m_full_context.mk_meta(t, e.get_tag()); expr m = m_full_context.mk_meta(t, e.get_tag());
@ -695,13 +688,13 @@ public:
return choose(std::make_shared<choice_expr_elaborator>(*this, mvar, e, ctx, full_ctx, relax)); return choose(std::make_shared<choice_expr_elaborator>(*this, mvar, e, ctx, full_ctx, 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), true, j, m_relax_main_opaque)); cs += mk_choice_cnstr(m, fn, to_delay_factor(cnstr_group::Basic), true, j, m_relax_main_opaque);
return m; return m;
} }
expr visit_by(expr const & e, optional<expr> const & t) { expr visit_by(expr const & e, optional<expr> const & t, constraint_seq & cs) {
lean_assert(is_by(e)); lean_assert(is_by(e));
expr tac = visit(get_by_arg(e)); expr tac = visit(get_by_arg(e), cs);
expr m = m_context.mk_meta(t, e.get_tag()); expr m = m_context.mk_meta(t, e.get_tag());
m_local_tactic_hints.insert(mlocal_name(get_app_fn(m)), tac); m_local_tactic_hints.insert(mlocal_name(get_app_fn(m)), tac);
return m; return m;
@ -711,15 +704,17 @@ public:
The result is a pair <tt>new_f, f_type</tt>, where new_f is the new value for \c f, The result is a pair <tt>new_f, f_type</tt>, where new_f is the new value for \c f,
and \c f_type is its type (and a Pi-expression) and \c f_type is its type (and a Pi-expression)
*/ */
pair<expr, expr> ensure_fun(expr f) { pair<expr, expr> ensure_fun(expr f, constraint_seq & cs) {
expr f_type = infer_type(f); expr f_type = infer_type(f, cs);
if (!is_pi(f_type)) if (!is_pi(f_type))
f_type = whnf(f_type); f_type = whnf(f_type, cs);
if (!is_pi(f_type) && has_metavar(f_type)) { if (!is_pi(f_type) && has_metavar(f_type)) {
f_type = whnf(f_type); constraint_seq saved_cs = cs;
f_type = whnf(f_type, cs);
if (!is_pi(f_type) && is_meta(f_type)) { if (!is_pi(f_type) && is_meta(f_type)) {
cs = saved_cs;
// let type checker add constraint // let type checker add constraint
f_type = m_tc[m_relax_main_opaque]->ensure_pi(f_type, f); f_type = m_tc[m_relax_main_opaque]->ensure_pi(f_type, f, cs);
} }
} }
if (!is_pi(f_type)) { if (!is_pi(f_type)) {
@ -727,7 +722,7 @@ public:
optional<expr> c = get_coercion_to_fun(env(), f_type); optional<expr> c = get_coercion_to_fun(env(), f_type);
if (c) { if (c) {
f = mk_app(*c, f, f.get_tag()); f = mk_app(*c, f, f.get_tag());
f_type = infer_type(f); f_type = infer_type(f, cs);
lean_assert(is_pi(f_type)); lean_assert(is_pi(f_type));
} else { } else {
throw_kernel_exception(env(), f, [=](formatter const & fmt) { return pp_function_expected(fmt, f); }); throw_kernel_exception(env(), f, [=](formatter const & fmt) { return pp_function_expected(fmt, f); });
@ -738,18 +733,18 @@ public:
} }
bool has_coercions_from(expr const & a_type) { bool has_coercions_from(expr const & a_type) {
expr const & a_cls = get_app_fn(whnf(a_type)); expr const & a_cls = get_app_fn(whnf(a_type).first);
return is_constant(a_cls) && ::lean::has_coercions_from(env(), const_name(a_cls)); return is_constant(a_cls) && ::lean::has_coercions_from(env(), const_name(a_cls));
} }
bool has_coercions_to(expr const & d_type) { bool has_coercions_to(expr const & d_type) {
expr const & d_cls = get_app_fn(whnf(d_type)); expr const & d_cls = get_app_fn(whnf(d_type).first);
return is_constant(d_cls) && ::lean::has_coercions_to(env(), const_name(d_cls)); return is_constant(d_cls) && ::lean::has_coercions_to(env(), const_name(d_cls));
} }
expr apply_coercion(expr const & a, expr a_type, expr d_type) { expr apply_coercion(expr const & a, expr a_type, expr d_type) {
a_type = whnf(a_type); a_type = whnf(a_type).first;
d_type = whnf(d_type); d_type = whnf(d_type).first;
expr const & d_cls = get_app_fn(d_type); expr const & d_cls = get_app_fn(d_type);
if (is_constant(d_cls)) { if (is_constant(d_cls)) {
if (auto c = get_coercion(env(), a_type, const_name(d_cls))) if (auto c = get_coercion(env(), a_type, const_name(d_cls)))
@ -781,7 +776,7 @@ public:
return lazy_list<constraints>(constraints(mk_eq_cnstr(mvar, a, justification(), relax))); return lazy_list<constraints>(constraints(mk_eq_cnstr(mvar, a, justification(), relax)));
} }
} }
buffer<constraint> cs; constraint_seq cs;
new_a_type = tc.whnf(new_a_type, cs); new_a_type = tc.whnf(new_a_type, cs);
if (is_meta(d_type)) { if (is_meta(d_type)) {
// case-split // case-split
@ -789,17 +784,15 @@ public:
get_user_coercions(env(), new_a_type, alts); get_user_coercions(env(), new_a_type, alts);
buffer<constraints> r; buffer<constraints> r;
// first alternative: no coercion // first alternative: no coercion
cs.push_back(mk_eq_cnstr(mvar, a, justification(), relax)); constraint_seq cs1 = cs + mk_eq_cnstr(mvar, a, justification(), relax);
r.push_back(to_list(cs.begin(), cs.end())); r.push_back(cs1.to_list());
cs.pop_back();
unsigned i = alts.size(); unsigned i = alts.size();
while (i > 0) { while (i > 0) {
--i; --i;
auto const & t = alts[i]; auto const & t = alts[i];
expr new_a = mk_app(std::get<1>(t), a, a.get_tag()); expr new_a = mk_app(std::get<1>(t), a, a.get_tag());
cs.push_back(mk_eq_cnstr(mvar, new_a, new_a_type_jst, relax)); constraint_seq csi = cs + mk_eq_cnstr(mvar, new_a, new_a_type_jst, relax);
r.push_back(to_list(cs.begin(), cs.end())); r.push_back(csi.to_list());
cs.pop_back();
} }
return to_lazy(to_list(r.begin(), r.end())); return to_lazy(to_list(r.begin(), r.end()));
} else { } else {
@ -810,49 +803,52 @@ public:
if (auto c = get_coercion(env(), new_a_type, const_name(d_cls))) if (auto c = get_coercion(env(), new_a_type, const_name(d_cls)))
new_a = mk_app(*c, a, a.get_tag()); new_a = mk_app(*c, a, a.get_tag());
} }
cs.push_back(mk_eq_cnstr(mvar, new_a, new_a_type_jst, relax)); cs += mk_eq_cnstr(mvar, new_a, new_a_type_jst, relax);
return lazy_list<constraints>(to_list(cs.begin(), cs.end())); return lazy_list<constraints>(cs.to_list());
} }
}; };
return mk_choice_cnstr(m, choice_fn, delay_factor, true, j, m_relax_main_opaque); return mk_choice_cnstr(m, choice_fn, delay_factor, true, j, m_relax_main_opaque);
} }
/** \brief Given a term <tt>a : a_type</tt>, and an expected type generate a metavariable with a delayed coercion. */ /** \brief Given a term <tt>a : a_type</tt>, and an expected type generate a metavariable with a delayed coercion. */
expr mk_delayed_coercion(expr const & a, expr const & a_type, expr const & expected_type, justification const & j) { pair<expr, constraint_seq> mk_delayed_coercion(expr const & a, expr const & a_type, expr const & expected_type, justification const & j) {
expr m = m_full_context.mk_meta(some_expr(expected_type), a.get_tag()); expr m = m_full_context.mk_meta(some_expr(expected_type), a.get_tag());
add_cnstr(mk_delayed_coercion_cnstr(m, a, a_type, j, to_delay_factor(cnstr_group::Basic))); return to_ecs(m, mk_delayed_coercion_cnstr(m, a, a_type, j, to_delay_factor(cnstr_group::Basic)));
return m;
} }
/** \brief Given a term <tt>a : a_type</tt>, ensure it has type \c expected_type. Apply coercions if needed /** \brief Given a term <tt>a : a_type</tt>, ensure it has type \c expected_type. Apply coercions if needed
\remark relax == true affects how opaque definitions in the main module are treated. \remark relax == true affects how opaque definitions in the main module are treated.
*/ */
expr ensure_type(expr const & a, expr const & a_type, expr const & expected_type, justification const & j, bool relax) { pair<expr, constraint_seq> ensure_has_type(expr const & a, expr const & a_type, expr const & expected_type,
justification const & j, bool relax) {
if (is_meta(expected_type) && has_coercions_from(a_type)) { if (is_meta(expected_type) && has_coercions_from(a_type)) {
return mk_delayed_coercion(a, a_type, expected_type, j); return mk_delayed_coercion(a, a_type, expected_type, j);
} else if (is_meta(a_type) && has_coercions_to(expected_type)) { } else if (is_meta(a_type) && has_coercions_to(expected_type)) {
return mk_delayed_coercion(a, a_type, expected_type, j); return mk_delayed_coercion(a, a_type, expected_type, j);
} else if (m_tc[relax]->is_def_eq(a_type, expected_type, j)) { } else {
return a; auto dcs = m_tc[relax]->is_def_eq(a_type, expected_type, j);
if (dcs.first) {
return to_ecs(a, dcs.second);
} else { } else {
expr new_a = apply_coercion(a, a_type, expected_type); expr new_a = apply_coercion(a, a_type, expected_type);
bool coercion_worked = false; bool coercion_worked = false;
constraint_seq cs;
if (!is_eqp(a, new_a)) { if (!is_eqp(a, new_a)) {
expr new_a_type = infer_type(new_a); expr new_a_type = infer_type(new_a, cs);
coercion_worked = m_tc[relax]->is_def_eq(new_a_type, expected_type, j); coercion_worked = m_tc[relax]->is_def_eq(new_a_type, expected_type, j, cs);
} }
if (coercion_worked) { if (coercion_worked) {
return new_a; return to_ecs(new_a, cs);
} else if (has_metavar(a_type) || has_metavar(expected_type)) { } else if (has_metavar(a_type) || has_metavar(expected_type)) {
// rely on unification hints to solve this constraint // rely on unification hints to solve this constraint
add_cnstr(mk_eq_cnstr(a_type, expected_type, j, relax)); return to_ecs(a, mk_eq_cnstr(a_type, expected_type, j, relax));
return a;
} else { } else {
throw unifier_exception(j, substitution()); throw unifier_exception(j, substitution());
} }
} }
} }
}
bool is_choice_app(expr const & e) { bool is_choice_app(expr const & e) {
expr const & f = get_app_fn(e); expr const & f = get_app_fn(e);
@ -862,7 +858,7 @@ public:
/** \brief Process ((choice f_1 ... f_n) a_1 ... a_k) as /** \brief Process ((choice f_1 ... f_n) a_1 ... a_k) as
(choice (f_1 a_1 ... a_k) ... (f_n a_1 ... a_k)) (choice (f_1 a_1 ... a_k) ... (f_n a_1 ... a_k))
*/ */
expr visit_choice_app(expr const & e) { expr visit_choice_app(expr const & e, constraint_seq & cs) {
buffer<expr> args; buffer<expr> args;
expr f = get_app_rev_args(e, args); expr f = get_app_rev_args(e, args);
bool expl = is_explicit(f); bool expl = is_explicit(f);
@ -877,15 +873,16 @@ public:
f_i = copy_tag(f_i, mk_explicit(f_i)); f_i = copy_tag(f_i, mk_explicit(f_i));
new_choices.push_back(mk_rev_app(f_i, args)); new_choices.push_back(mk_rev_app(f_i, args));
} }
return visit_choice(copy_tag(e, mk_choice(new_choices.size(), new_choices.data())), none_expr()); return visit_choice(copy_tag(e, mk_choice(new_choices.size(), new_choices.data())), none_expr(), cs);
} }
expr visit_app(expr const & e) { expr visit_app(expr const & e, constraint_seq & cs) {
if (is_choice_app(e)) if (is_choice_app(e))
return visit_choice_app(e); return visit_choice_app(e, cs);
constraint_seq f_cs;
bool expl = is_explicit(get_app_fn(e)); bool expl = is_explicit(get_app_fn(e));
expr f = visit(app_fn(e)); expr f = visit(app_fn(e), f_cs);
auto f_t = ensure_fun(f); auto f_t = ensure_fun(f, f_cs);
f = f_t.first; f = f_t.first;
expr f_type = f_t.second; expr f_type = f_t.second;
lean_assert(is_pi(f_type)); lean_assert(is_pi(f_type));
@ -893,9 +890,10 @@ public:
bool first = true; bool first = true;
while (binding_info(f_type).is_strict_implicit() || (!first && binding_info(f_type).is_implicit())) { while (binding_info(f_type).is_strict_implicit() || (!first && binding_info(f_type).is_implicit())) {
tag g = f.get_tag(); tag g = f.get_tag();
expr imp_arg = mk_placeholder_meta(some_expr(binding_domain(f_type)), g); bool is_strict = false;
expr imp_arg = mk_placeholder_meta(some_expr(binding_domain(f_type)), g, is_strict, f_cs);
f = mk_app(f, imp_arg, g); f = mk_app(f, imp_arg, g);
auto f_t = ensure_fun(f); auto f_t = ensure_fun(f, f_cs);
f = f_t.first; f = f_t.first;
f_type = f_t.second; f_type = f_t.second;
first = false; first = false;
@ -905,18 +903,21 @@ public:
save_info_data(get_app_fn(e), f); save_info_data(get_app_fn(e), f);
} }
} }
constraint_seq a_cs;
expr d_type = binding_domain(f_type); expr d_type = binding_domain(f_type);
expr a = visit_expecting_type_of(app_arg(e), d_type); expr a = visit_expecting_type_of(app_arg(e), d_type, a_cs);
expr a_type = infer_type(a); expr a_type = infer_type(a, a_cs);
expr r = mk_app(f, a, e.get_tag()); expr r = mk_app(f, a, e.get_tag());
justification j = mk_app_justification(r, a, d_type, a_type); justification j = mk_app_justification(r, a, d_type, a_type);
expr new_a = ensure_type(a, a_type, d_type, j, m_relax_main_opaque); auto new_a_cs = ensure_has_type(a, a_type, d_type, j, m_relax_main_opaque);
expr new_a = new_a_cs.first;
cs += f_cs + new_a_cs.second + a_cs;
return update_app(r, app_fn(r), new_a); return update_app(r, app_fn(r), new_a);
} }
expr visit_placeholder(expr const & e) { expr visit_placeholder(expr const & e, constraint_seq & cs) {
expr r = mk_placeholder_meta(placeholder_type(e), e.get_tag(), is_strict_placeholder(e)); expr r = mk_placeholder_meta(placeholder_type(e), e.get_tag(), is_strict_placeholder(e), cs);
save_placeholder_info(e, r); save_placeholder_info(e, r);
return r; return r;
} }
@ -934,7 +935,7 @@ public:
return update_sort(e, replace_univ_placeholder(sort_level(e))); return update_sort(e, replace_univ_placeholder(sort_level(e)));
} }
expr visit_macro(expr const & e) { expr visit_macro(expr const & e, constraint_seq & cs) {
if (is_as_is(e)) { if (is_as_is(e)) {
return get_as_is_arg(e); return get_as_is_arg(e);
} else { } else {
@ -942,37 +943,11 @@ public:
// Perhaps, we should throw error. // Perhaps, we should throw error.
buffer<expr> args; buffer<expr> args;
for (unsigned i = 0; i < macro_num_args(e); i++) for (unsigned i = 0; i < macro_num_args(e); i++)
args.push_back(visit(macro_arg(e, i))); args.push_back(visit(macro_arg(e, i), cs));
return update_macro(e, args.size(), args.data()); return update_macro(e, args.size(), args.data());
} }
} }
/** \brief Store the pair (pos(e), type(r)) in the info_data if m_info_manager is available. */
void save_info_data(expr const & e, expr const & r) {
if (!m_noinfo && infom() && pip() && (is_constant(e) || is_local(e) || is_placeholder(e))) {
if (auto p = pip()->get_pos_info(e)) {
type_checker::scope scope(*m_tc[m_relax_main_opaque]);
expr t = m_tc[m_relax_main_opaque]->infer(r);
m_pre_info_data.add_type_info(p->first, p->second, t);
}
}
}
void save_identifier_info(expr const & f) {
if (!m_noinfo && infom() && pip() && is_constant(f)) {
if (auto p = pip()->get_pos_info(f))
m_pre_info_data.add_identifier_info(p->first, p->second, const_name(f));
}
}
void save_synth_data(expr const & e, expr const & r) {
if (!m_noinfo && infom() && pip() && is_placeholder(e)) {
if (auto p = pip()->get_pos_info(e)) {
m_pre_info_data.add_synth_info(p->first, p->second, r);
}
}
}
expr visit_constant(expr const & e) { expr visit_constant(expr const & e) {
declaration d = env().get(const_name(e)); declaration d = env().get(const_name(e));
buffer<level> ls; buffer<level> ls;
@ -991,20 +966,20 @@ public:
} }
/** \brief Make sure \c e is a type. If it is not, then try to apply coercions. */ /** \brief Make sure \c e is a type. If it is not, then try to apply coercions. */
expr ensure_type(expr const & e) { expr ensure_type(expr const & e, constraint_seq & cs) {
expr t = infer_type(e); expr t = infer_type(e, cs);
if (is_sort(t)) if (is_sort(t))
return e; return e;
t = whnf(t); t = whnf(t, cs);
if (is_sort(t)) if (is_sort(t))
return e; return e;
if (has_metavar(t)) { if (has_metavar(t)) {
t = whnf(t); t = whnf(t, cs);
if (is_sort(t)) if (is_sort(t))
return e; return e;
if (is_meta(t)) { if (is_meta(t)) {
// let type checker add constraint // let type checker add constraint
m_tc[m_relax_main_opaque]->ensure_sort(t, e); m_tc[m_relax_main_opaque]->ensure_sort(t, e, cs);
return e; return e;
} }
} }
@ -1040,14 +1015,14 @@ public:
}); });
} }
expr visit_binding(expr e, expr_kind k) { expr visit_binding(expr e, expr_kind k, constraint_seq & cs) {
scope_ctx scope(*this); scope_ctx scope(*this);
buffer<expr> ds, ls, es; buffer<expr> ds, ls, es;
while (e.kind() == k) { while (e.kind() == k) {
es.push_back(e); es.push_back(e);
expr d = binding_domain(e); expr d = binding_domain(e);
d = instantiate_rev_locals(d, ls.size(), ls.data()); d = instantiate_rev_locals(d, ls.size(), ls.data());
d = ensure_type(visit_expecting_type(d)); d = ensure_type(visit_expecting_type(d, cs), cs);
ds.push_back(d); ds.push_back(d);
expr l = mk_local(binding_name(e), d, binding_info(e)); expr l = mk_local(binding_name(e), d, binding_info(e));
if (binding_info(e).is_contextual()) if (binding_info(e).is_contextual())
@ -1058,7 +1033,7 @@ public:
} }
lean_assert(ls.size() == es.size() && ls.size() == ds.size()); lean_assert(ls.size() == es.size() && ls.size() == ds.size());
e = instantiate_rev_locals(e, ls.size(), ls.data()); e = instantiate_rev_locals(e, ls.size(), ls.data());
e = (k == expr_kind::Pi) ? ensure_type(visit_expecting_type(e)) : visit(e); e = (k == expr_kind::Pi) ? ensure_type(visit_expecting_type(e, cs), cs) : visit(e, cs);
e = abstract_locals(e, ls.size(), ls.data()); e = abstract_locals(e, ls.size(), ls.data());
unsigned i = ls.size(); unsigned i = ls.size();
while (i > 0) { while (i > 0) {
@ -1067,19 +1042,19 @@ public:
} }
return e; return e;
} }
expr visit_pi(expr const & e) { return visit_binding(e, expr_kind::Pi); } expr visit_pi(expr const & e, constraint_seq & cs) { return visit_binding(e, expr_kind::Pi, cs); }
expr visit_lambda(expr const & e) { return visit_binding(e, expr_kind::Lambda); } expr visit_lambda(expr const & e, constraint_seq & cs) { return visit_binding(e, expr_kind::Lambda, cs); }
expr visit_core(expr const & e) { expr visit_core(expr const & e, constraint_seq & cs) {
if (is_placeholder(e)) { if (is_placeholder(e)) {
return visit_placeholder(e); return visit_placeholder(e, cs);
} else if (is_choice(e)) { } else if (is_choice(e)) {
return visit_choice(e, none_expr()); return visit_choice(e, none_expr(), cs);
} else if (is_by(e)) { } else if (is_by(e)) {
return visit_by(e, none_expr()); return visit_by(e, none_expr(), cs);
} else if (is_noinfo(e)) { } else if (is_noinfo(e)) {
flet<bool> let(m_noinfo, true); flet<bool> let(m_noinfo, true);
return visit(get_annotation_arg(e)); return visit(get_annotation_arg(e), cs);
} else { } else {
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Local: return e; case expr_kind::Local: return e;
@ -1087,53 +1062,59 @@ public:
case expr_kind::Sort: return visit_sort(e); case expr_kind::Sort: return visit_sort(e);
case expr_kind::Var: lean_unreachable(); // LCOV_EXCL_LINE case expr_kind::Var: lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::Constant: return visit_constant(e); case expr_kind::Constant: return visit_constant(e);
case expr_kind::Macro: return visit_macro(e); case expr_kind::Macro: return visit_macro(e, cs);
case expr_kind::Lambda: return visit_lambda(e); case expr_kind::Lambda: return visit_lambda(e, cs);
case expr_kind::Pi: return visit_pi(e); case expr_kind::Pi: return visit_pi(e, cs);
case expr_kind::App: return visit_app(e); case expr_kind::App: return visit_app(e, cs);
} }
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
} }
} }
expr visit(expr const & e) { pair<expr, constraint_seq> visit(expr const & e) {
expr r; expr r;
expr b = e; expr b = e;
constraint_seq cs;
if (is_explicit(e)) { if (is_explicit(e)) {
b = get_explicit_arg(e); b = get_explicit_arg(e);
r = visit_core(get_explicit_arg(e)); r = visit_core(get_explicit_arg(e), cs);
} else if (is_explicit(get_app_fn(e))) { } else if (is_explicit(get_app_fn(e))) {
r = visit_core(e); r = visit_core(e, cs);
} else { } else {
if (is_implicit(e)) { if (is_implicit(e)) {
r = get_implicit_arg(e); r = get_implicit_arg(e);
if (is_explicit(r)) r = get_explicit_arg(r); if (is_explicit(r)) r = get_explicit_arg(r);
b = r; b = r;
r = visit_core(r); r = visit_core(r, cs);
} else { } else {
r = visit_core(e); r = visit_core(e, cs);
} }
if (!is_lambda(r)) { if (!is_lambda(r)) {
tag g = e.get_tag(); tag g = e.get_tag();
expr r_type = whnf(infer_type(r)); expr r_type = whnf(infer_type(r, cs), cs);
expr imp_arg; expr imp_arg;
bool is_strict = false;
while (is_pi(r_type) && binding_info(r_type).is_implicit()) { while (is_pi(r_type) && binding_info(r_type).is_implicit()) {
imp_arg = mk_placeholder_meta(some_expr(binding_domain(r_type)), g); imp_arg = mk_placeholder_meta(some_expr(binding_domain(r_type)), g, is_strict, cs);
r = mk_app(r, imp_arg, g); r = mk_app(r, imp_arg, g);
r_type = whnf(instantiate(binding_body(r_type), imp_arg)); r_type = whnf(instantiate(binding_body(r_type), imp_arg), cs);
} }
} }
} }
save_info_data(b, r); save_info_data(b, r);
return r; return mk_pair(r, cs);
} }
lazy_list<substitution> solve() { expr visit(expr const & e, constraint_seq & cs) {
consume_tc_cnstrs(); auto r = visit(e);
buffer<constraint> cs; cs += r.second;
cs.append(m_constraints); return r.first;
m_constraints.clear(); }
return unify(env(), cs.size(), cs.data(), m_ngen.mk_child(), true, ios().get_options());
lazy_list<substitution> solve(constraint_seq const & cs) {
buffer<constraint> tmp;
cs.linearize(tmp);
return unify(env(), tmp.size(), tmp.data(), m_ngen.mk_child(), true, ios().get_options());
} }
static void collect_metavars(expr const & e, buffer<expr> & mvars) { static void collect_metavars(expr const & e, buffer<expr> & mvars) {
@ -1245,7 +1226,8 @@ public:
if (!meta) if (!meta)
return; return;
meta = instantiate_meta(*meta, subst); meta = instantiate_meta(*meta, subst);
expr type = m_tc[m_relax_main_opaque]->infer(*meta); // TODO(Leo): we are discarding constraints here
expr type = m_tc[m_relax_main_opaque]->infer(*meta).first;
// 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());
@ -1279,7 +1261,7 @@ public:
return has_metavar(e); return has_metavar(e);
if (auto it = m_mvar2meta.find(mlocal_name(e))) { if (auto it = m_mvar2meta.find(mlocal_name(e))) {
expr meta = tmp_s.instantiate(*it); expr meta = tmp_s.instantiate(*it);
expr meta_type = tmp_s.instantiate(type_checker(env()).infer(meta)); expr meta_type = tmp_s.instantiate(type_checker(env()).infer(meta).first);
goal g(meta, meta_type); goal g(meta, meta_type);
display_unsolved_proof_state(e, proof_state(goals(g), substitution(), m_ngen), display_unsolved_proof_state(e, proof_state(goals(g), substitution(), m_ngen),
"don't know how to synthesize it"); "don't know how to synthesize it");
@ -1306,20 +1288,13 @@ 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()));
} }
void copy_info_to_manager(substitution s) {
if (!infom())
return;
m_pre_info_data.instantiate(s);
infom()->merge(m_pre_info_data);
m_pre_info_data.clear();
}
std::tuple<expr, level_param_names> operator()(expr const & e, bool _ensure_type, bool relax_main_opaque) { 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(env())); flet<bool> set_relax(m_relax_main_opaque, relax_main_opaque && !get_hide_main_opaque(env()));
expr r = visit(e); constraint_seq cs;
expr r = visit(e, cs);
if (_ensure_type) if (_ensure_type)
r = ensure_type(r); r = ensure_type(r, cs);
auto p = solve().pull(); auto p = solve(cs).pull();
lean_assert(p); lean_assert(p);
substitution s = p->first; substitution s = p->first;
auto result = apply(s, r); auto result = apply(s, r);
@ -1329,17 +1304,21 @@ public:
std::tuple<expr, expr, level_param_names> operator()(expr const & t, expr const & v, name const & n, bool is_opaque) { 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)); constraint_seq t_cs;
expr r_t = ensure_type(visit(t, t_cs), t_cs);
// Opaque definitions in the main module may treat other opaque definitions (in the main module) as transparent. // 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(env())); flet<bool> set_relax(m_relax_main_opaque, is_opaque && !get_hide_main_opaque(env()));
expr r_v = visit(v); constraint_seq v_cs;
expr r_v_type = infer_type(r_v); expr r_v = visit(v, v_cs);
expr r_v_type = infer_type(r_v, v_cs);
justification j = mk_justification(r_v, [=](formatter const & fmt, substitution const & subst) { justification j = mk_justification(r_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));
}); });
r_v = ensure_type(r_v, r_v_type, r_t, j, is_opaque); pair<expr, constraint_seq> r_v_cs = ensure_has_type(r_v, r_v_type, r_t, j, is_opaque);
auto p = solve().pull(); r_v = r_v_cs.first;
constraint_seq cs = t_cs + r_v_cs.second + v_cs;
auto p = solve(cs).pull();
lean_assert(p); lean_assert(p);
substitution s = p->first; substitution s = p->first;
name_set univ_params = collect_univ_params(r_v, collect_univ_params(r_t)); name_set univ_params = collect_univ_params(r_v, collect_univ_params(r_t));

View file

@ -174,9 +174,9 @@ struct inductive_cmd_fn {
/** \brief Return the universe level of the given type, if it is not a sort, then raise an exception. */ /** \brief Return the universe level of the given type, if it is not a sort, then raise an exception. */
level get_datatype_result_level(expr d_type) { level get_datatype_result_level(expr d_type) {
d_type = m_tc->whnf(d_type); d_type = m_tc->whnf(d_type).first;
while (is_pi(d_type)) { while (is_pi(d_type)) {
d_type = m_tc->whnf(binding_body(d_type)); d_type = m_tc->whnf(binding_body(d_type)).first;
} }
if (!is_sort(d_type)) if (!is_sort(d_type))
throw_error(sstream() << "invalid inductive datatype, resultant type is not a sort"); throw_error(sstream() << "invalid inductive datatype, resultant type is not a sort");
@ -185,7 +185,7 @@ struct inductive_cmd_fn {
/** \brief Update the result sort of the given type */ /** \brief Update the result sort of the given type */
expr update_result_sort(expr t, level const & l) { expr update_result_sort(expr t, level const & l) {
t = m_tc->whnf(t); t = m_tc->whnf(t).first;
if (is_pi(t)) { if (is_pi(t)) {
return update_binding(t, binding_domain(t), update_result_sort(binding_body(t), l)); return update_binding(t, binding_domain(t), update_result_sort(binding_body(t), l));
} else if (is_sort(t)) { } else if (is_sort(t)) {
@ -215,7 +215,7 @@ struct inductive_cmd_fn {
/** \brief Check if the parameters of \c d_type and \c first_d_type are equal. */ /** \brief Check if the parameters of \c d_type and \c first_d_type are equal. */
void check_params(expr d_type, expr first_d_type) { void check_params(expr d_type, expr first_d_type) {
for (unsigned i = 0; i < m_num_params; i++) { for (unsigned i = 0; i < m_num_params; i++) {
if (!m_tc->is_def_eq(binding_domain(d_type), binding_domain(first_d_type))) if (!m_tc->is_def_eq(binding_domain(d_type), binding_domain(first_d_type)).first)
throw_error(sstream() << "invalid parameter #" << (i+1) << " in mutually recursive inductive declaration, " throw_error(sstream() << "invalid parameter #" << (i+1) << " in mutually recursive inductive declaration, "
<< "all inductive types must have equivalent parameters"); << "all inductive types must have equivalent parameters");
expr l = mk_local_for(d_type); expr l = mk_local_for(d_type);
@ -418,7 +418,7 @@ struct inductive_cmd_fn {
unsigned i = 0; unsigned i = 0;
while (is_pi(intro_type)) { while (is_pi(intro_type)) {
if (i >= m_num_params) { if (i >= m_num_params) {
expr s = m_tc->ensure_type(binding_domain(intro_type)); expr s = m_tc->ensure_type(binding_domain(intro_type)).first;
level l = sort_level(s); level l = sort_level(s);
if (l == m_u) { if (l == m_u) {
// ignore, this is the auxiliary level // ignore, this is the auxiliary level

View file

@ -131,7 +131,7 @@ bool pretty_fn::is_implicit(expr const & f) {
if (m_implict) if (m_implict)
return false; // showing implicit arguments return false; // showing implicit arguments
try { try {
binder_info bi = binding_info(m_tc.ensure_pi(m_tc.infer(f))); binder_info bi = binding_info(m_tc.ensure_pi(m_tc.infer(f).first).first);
return bi.is_implicit() || bi.is_strict_implicit(); return bi.is_implicit() || bi.is_strict_implicit();
} catch (...) { } catch (...) {
return false; return false;
@ -140,7 +140,7 @@ bool pretty_fn::is_implicit(expr const & f) {
bool pretty_fn::is_prop(expr const & e) { bool pretty_fn::is_prop(expr const & e) {
try { try {
return m_env.impredicative() && m_tc.is_prop(e); return m_env.impredicative() && m_tc.is_prop(e).first;
} catch (...) { } catch (...) {
return false; return false;
} }

View file

@ -66,7 +66,7 @@ typedef scoped_ext<pq_config> proof_qed_ext;
static void check_valid_tactic(environment const & env, expr const & pre_tac) { static void check_valid_tactic(environment const & env, expr const & pre_tac) {
type_checker tc(env); type_checker tc(env);
if (!tc.is_def_eq(tc.infer(pre_tac), get_tactic_type())) if (!tc.is_def_eq(tc.infer(pre_tac).first, get_tactic_type()).first)
throw exception("invalid proof-qed pre-tactic update, argument is not a tactic"); throw exception("invalid proof-qed pre-tactic update, argument is not a tactic");
} }

View file

@ -239,7 +239,7 @@ struct structure_cmd_fn {
unsigned i = 0; unsigned i = 0;
while (is_pi(intro_type)) { while (is_pi(intro_type)) {
if (i >= num_params) { if (i >= num_params) {
expr s = tc->ensure_type(binding_domain(intro_type)); expr s = tc->ensure_type(binding_domain(intro_type)).first;
level l = sort_level(s); level l = sort_level(s);
if (l == m_u) { if (l == m_u) {
// ignore, this is the auxiliary level // ignore, this is the auxiliary level

View file

@ -5,7 +5,11 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura Author: Leonardo de Moura
*/ */
#include "util/rc.h" #include "util/rc.h"
#include "kernel/expr.h"
#include "kernel/justification.h"
#include "kernel/metavar.h"
#include "kernel/constraint.h" #include "kernel/constraint.h"
namespace lean { namespace lean {
struct constraint_cell { struct constraint_cell {
void dealloc(); void dealloc();
@ -104,6 +108,12 @@ constraint update_justification(constraint const & c, justification const & j) {
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
} }
void to_buffer(constraint_seq const & cs, justification const & j, buffer<constraint> & r) {
return cs.for_each([&](constraint const & c) {
r.push_back(update_justification(c, mk_composite1(c.get_justification(), j)));
});
}
std::ostream & operator<<(std::ostream & out, constraint const & c) { std::ostream & operator<<(std::ostream & out, constraint const & c) {
switch (c.kind()) { switch (c.kind()) {
case constraint_kind::Eq: case constraint_kind::Eq:

View file

@ -9,11 +9,13 @@ Author: Leonardo de Moura
#include "util/lazy_list.h" #include "util/lazy_list.h"
#include "util/list.h" #include "util/list.h"
#include "util/name_generator.h" #include "util/name_generator.h"
#include "kernel/expr.h" #include "util/sequence.h"
#include "kernel/justification.h" #include "kernel/level.h"
#include "kernel/metavar.h"
namespace lean { namespace lean {
class expr;
class justification;
class substitution;
/** /**
\brief The lean kernel type checker produces two kinds of constraints: \brief The lean kernel type checker produces two kinds of constraints:
@ -117,6 +119,11 @@ unsigned cnstr_delay_factor(constraint const & c);
/** \brief Return true iff the given choice constraints owns the right to assign the metavariable in \c c. */ /** \brief Return true iff the given choice constraints owns the right to assign the metavariable in \c c. */
bool cnstr_is_owner(constraint const & c); bool cnstr_is_owner(constraint const & c);
typedef sequence<constraint> constraint_seq;
inline constraint_seq empty_cs() { return constraint_seq(); }
/** \brief Copy constraints in cs to r, and append justification j to them. */
void to_buffer(constraint_seq const & cs, justification const & j, buffer<constraint> & r);
/** \brief Printer for debugging purposes */ /** \brief Printer for debugging purposes */
std::ostream & operator<<(std::ostream & out, constraint const & c); std::ostream & operator<<(std::ostream & out, constraint const & c);
} }

View file

@ -43,7 +43,8 @@ bool is_opaque(declaration const & d, name_set const & extra_opaque, optional<mo
} }
/** \brief Auxiliary method for \c is_delta */ /** \brief Auxiliary method for \c is_delta */
static optional<declaration> is_delta_core(environment const & env, expr const & e, name_set const & extra_opaque, optional<module_idx> const & mod_idx) { static optional<declaration> is_delta_core(environment const & env, expr const & e, name_set const & extra_opaque,
optional<module_idx> const & mod_idx) {
if (is_constant(e)) { if (is_constant(e)) {
if (auto d = env.find(const_name(e))) if (auto d = env.find(const_name(e)))
if (d->is_definition() && !is_opaque(*d, extra_opaque, mod_idx)) if (d->is_definition() && !is_opaque(*d, extra_opaque, mod_idx))
@ -66,14 +67,18 @@ optional<declaration> is_delta(environment const & env, expr const & e, name_set
} }
static no_delayed_justification g_no_delayed_jst; static no_delayed_justification g_no_delayed_jst;
bool converter::is_def_eq(expr const & t, expr const & s, type_checker & c) { pair<bool, constraint_seq> converter::is_def_eq(expr const & t, expr const & s, type_checker & c) {
return is_def_eq(t, s, c, g_no_delayed_jst); return is_def_eq(t, s, c, g_no_delayed_jst);
} }
/** \brief Do nothing converter */ /** \brief Do nothing converter */
struct dummy_converter : public converter { struct dummy_converter : public converter {
virtual expr whnf(expr const & e, type_checker &) { return e; } virtual pair<expr, constraint_seq> whnf(expr const & e, type_checker &) {
virtual bool is_def_eq(expr const &, expr const &, type_checker &, delayed_justification &) { return true; } return mk_pair(e, constraint_seq());
}
virtual pair<bool, constraint_seq> is_def_eq(expr const &, expr const &, type_checker &, delayed_justification &) {
return mk_pair(true, constraint_seq());
}
virtual optional<module_idx> get_module_idx() const { return optional<module_idx>(); } virtual optional<module_idx> get_module_idx() const { return optional<module_idx>(); }
}; };
@ -82,8 +87,7 @@ std::unique_ptr<converter> mk_dummy_converter() {
} }
name converter::mk_fresh_name(type_checker & tc) { return tc.mk_fresh_name(); } name converter::mk_fresh_name(type_checker & tc) { return tc.mk_fresh_name(); }
expr converter::infer_type(type_checker & tc, expr const & e) { return tc.infer_type(e); } pair<expr, constraint_seq> converter::infer_type(type_checker & tc, expr const & e) { return tc.infer_type(e); }
void converter::add_cnstr(type_checker & tc, constraint const & c) { return tc.add_cnstr(c); }
extension_context & converter::get_extension(type_checker & tc) { return tc.get_extension(); } extension_context & converter::get_extension(type_checker & tc) { return tc.get_extension(); }
static expr g_dont_care(Const("dontcare")); static expr g_dont_care(Const("dontcare"));
@ -93,7 +97,7 @@ struct default_converter : public converter {
bool m_memoize; bool m_memoize;
name_set m_extra_opaque; name_set m_extra_opaque;
expr_struct_map<expr> m_whnf_core_cache; expr_struct_map<expr> m_whnf_core_cache;
expr_struct_map<expr> m_whnf_cache; expr_struct_map<pair<expr, constraint_seq>> m_whnf_cache;
default_converter(environment const & env, optional<module_idx> mod_idx, bool memoize, name_set const & extra_opaque): default_converter(environment const & env, optional<module_idx> mod_idx, bool memoize, name_set const & extra_opaque):
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) {
@ -109,10 +113,19 @@ struct default_converter : public converter {
} }
/** \brief Apply normalizer extensions to \c e. */ /** \brief Apply normalizer extensions to \c e. */
optional<expr> norm_ext(expr const & e, type_checker & c) { optional<pair<expr, constraint_seq>> norm_ext(expr const & e, type_checker & c) {
return m_env.norm_ext()(e, get_extension(c)); return m_env.norm_ext()(e, get_extension(c));
} }
optional<expr> d_norm_ext(expr const & e, type_checker & c, constraint_seq & cs) {
if (auto r = norm_ext(e, c)) {
cs = cs + r->second;
return some_expr(r->first);
} else {
return none_expr();
}
}
/** \brief Return true if \c e may be reduced later after metavariables are instantiated. */ /** \brief Return true if \c e may be reduced later after metavariables are instantiated. */
bool may_reduce_later(expr const & e, type_checker & c) { bool may_reduce_later(expr const & e, type_checker & c) {
return m_env.norm_ext().may_reduce_later(e, get_extension(c)); return m_env.norm_ext().may_reduce_later(e, get_extension(c));
@ -260,11 +273,11 @@ struct default_converter : public converter {
} }
/** \brief Put expression \c t in weak head normal form */ /** \brief Put expression \c t in weak head normal form */
virtual expr whnf(expr const & e_prime, type_checker & c) { virtual pair<expr, constraint_seq> whnf(expr const & e_prime, type_checker & c) {
// Do not cache easy cases // Do not cache easy cases
switch (e_prime.kind()) { switch (e_prime.kind()) {
case expr_kind::Var: case expr_kind::Sort: case expr_kind::Meta: case expr_kind::Local: case expr_kind::Pi: case expr_kind::Var: case expr_kind::Sort: case expr_kind::Meta: case expr_kind::Local: case expr_kind::Pi:
return e_prime; return to_ecs(e_prime);
case expr_kind::Lambda: case expr_kind::Macro: case expr_kind::App: case expr_kind::Constant: case expr_kind::Lambda: case expr_kind::Macro: case expr_kind::App: case expr_kind::Constant:
break; break;
} }
@ -278,19 +291,30 @@ struct default_converter : public converter {
} }
expr t = e; expr t = e;
constraint_seq cs;
while (true) { while (true) {
expr t1 = whnf_core(t, 0, c); expr t1 = whnf_core(t, 0, c);
auto new_t = norm_ext(t1, c); if (auto new_t = d_norm_ext(t1, c, cs)) {
if (new_t) {
t = *new_t; t = *new_t;
} else { } else {
auto r = mk_pair(t1, cs);
if (m_memoize) if (m_memoize)
m_whnf_cache.insert(mk_pair(e, t1)); m_whnf_cache.insert(mk_pair(e, r));
return t1; return r;
} }
} }
} }
expr whnf(expr const & e_prime, type_checker & c, constraint_seq & cs) {
auto r = whnf(e_prime, c);
cs = cs + r.second;
return r.first;
}
pair<bool, constraint_seq> to_bcs(bool b) { return mk_pair(b, constraint_seq()); }
pair<bool, constraint_seq> to_bcs(bool b, constraint const & c) { return mk_pair(b, constraint_seq(c)); }
pair<bool, constraint_seq> to_bcs(bool b, constraint_seq const & cs) { return mk_pair(b, cs); }
/** /**
\brief Given lambda/Pi expressions \c t and \c s, return true iff \c t is def eq to \c s. \brief Given lambda/Pi expressions \c t and \c s, return true iff \c t is def eq to \c s.
@ -300,7 +324,7 @@ struct default_converter : public converter {
and and
body(t) is definitionally equal to body(s) body(t) is definitionally equal to body(s)
*/ */
bool is_def_eq_binding(expr t, expr s, type_checker & c, delayed_justification & jst) { bool is_def_eq_binding(expr t, expr s, type_checker & c, delayed_justification & jst, constraint_seq & cs) {
lean_assert(t.kind() == s.kind()); lean_assert(t.kind() == s.kind());
lean_assert(is_binding(t)); lean_assert(is_binding(t));
expr_kind k = t.kind(); expr_kind k = t.kind();
@ -310,7 +334,7 @@ struct default_converter : public converter {
if (binding_domain(t) != binding_domain(s)) { if (binding_domain(t) != binding_domain(s)) {
var_s_type = instantiate_rev(binding_domain(s), subst.size(), subst.data()); var_s_type = instantiate_rev(binding_domain(s), subst.size(), subst.data());
expr var_t_type = instantiate_rev(binding_domain(t), subst.size(), subst.data()); expr var_t_type = instantiate_rev(binding_domain(t), subst.size(), subst.data());
if (!is_def_eq(var_t_type, *var_s_type, c, jst)) if (!is_def_eq(var_t_type, *var_s_type, c, jst, cs))
return false; return false;
} }
if (!closed(binding_body(t)) || !closed(binding_body(s))) { if (!closed(binding_body(t)) || !closed(binding_body(s))) {
@ -325,44 +349,53 @@ struct default_converter : public converter {
s = binding_body(s); s = binding_body(s);
} while (t.kind() == k && s.kind() == k); } while (t.kind() == k && s.kind() == k);
return is_def_eq(instantiate_rev(t, subst.size(), subst.data()), return is_def_eq(instantiate_rev(t, subst.size(), subst.data()),
instantiate_rev(s, subst.size(), subst.data()), c, jst); instantiate_rev(s, subst.size(), subst.data()), c, jst, cs);
} }
bool is_def_eq(level const & l1, level const & l2, type_checker & c, delayed_justification & jst) { bool is_def_eq(level const & l1, level const & l2, delayed_justification & jst, constraint_seq & cs) {
if (is_equivalent(l1, l2)) { if (is_equivalent(l1, l2)) {
return true; return true;
} else if (has_meta(l1) || has_meta(l2)) { } else if (has_meta(l1) || has_meta(l2)) {
add_cnstr(c, mk_level_eq_cnstr(l1, l2, jst.get())); cs = cs + constraint_seq(mk_level_eq_cnstr(l1, l2, jst.get()));
return true; return true;
} else { } else {
return false; return false;
} }
} }
bool is_def_eq(levels const & ls1, levels const & ls2, type_checker & c, delayed_justification & jst) { bool is_def_eq(levels const & ls1, levels const & ls2, type_checker & c, delayed_justification & jst, constraint_seq & cs) {
if (is_nil(ls1) && is_nil(ls2)) if (is_nil(ls1) && is_nil(ls2)) {
return true; return true;
else if (!is_nil(ls1) && !is_nil(ls2)) } else if (!is_nil(ls1) && !is_nil(ls2)) {
return is_def_eq(head(ls1), head(ls2), c, jst) && is_def_eq(tail(ls1), tail(ls2), c, jst); return
else is_def_eq(head(ls1), head(ls2), jst, cs) &&
is_def_eq(tail(ls1), tail(ls2), c, jst, cs);
} else {
return false; return false;
} }
}
static pair<lbool, constraint_seq> to_lbcs(lbool l) { return mk_pair(l, constraint_seq()); }
static pair<lbool, constraint_seq> to_lbcs(lbool l, constraint const & c) { return mk_pair(l, constraint_seq(c)); }
static pair<lbool, constraint_seq> to_lbcs(pair<bool, constraint_seq> const & bcs) {
return mk_pair(to_lbool(bcs.first), bcs.second);
}
/** \brief This is an auxiliary method for is_def_eq. It handles the "easy cases". */ /** \brief This is an auxiliary method for is_def_eq. It handles the "easy cases". */
lbool quick_is_def_eq(expr const & t, expr const & s, type_checker & c, delayed_justification & jst) { lbool quick_is_def_eq(expr const & t, expr const & s, type_checker & c, delayed_justification & jst, constraint_seq & cs) {
if (t == s) if (t == s)
return l_true; // t and s are structurally equal return l_true; // t and s are structurally equal
if (is_meta(t) || is_meta(s)) { if (is_meta(t) || is_meta(s)) {
// if t or s is a metavariable (or the application of a metavariable), then add constraint // if t or s is a metavariable (or the application of a metavariable), then add constraint
add_cnstr(c, mk_eq_cnstr(t, s, jst.get())); cs = cs + constraint_seq(mk_eq_cnstr(t, s, jst.get()));
return l_true; return l_true;
} }
if (t.kind() == s.kind()) { if (t.kind() == s.kind()) {
switch (t.kind()) { switch (t.kind()) {
case expr_kind::Lambda: case expr_kind::Pi: case expr_kind::Lambda: case expr_kind::Pi:
return to_lbool(is_def_eq_binding(t, s, c, jst)); return to_lbool(is_def_eq_binding(t, s, c, jst, cs));
case expr_kind::Sort: case expr_kind::Sort:
return to_lbool(is_def_eq(sort_level(t), sort_level(s), c, jst)); return to_lbool(is_def_eq(sort_level(t), sort_level(s), c, jst, cs));
case expr_kind::Meta: case expr_kind::Meta:
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::Var: case expr_kind::Local: case expr_kind::App: case expr_kind::Var: case expr_kind::Local: case expr_kind::App:
@ -378,9 +411,9 @@ struct default_converter : public converter {
\brief Return true if arguments of \c t are definitionally equal to arguments of \c s. \brief Return true if arguments of \c t are definitionally equal to arguments of \c s.
This method is used to implement an optimization in the method \c is_def_eq. This method is used to implement an optimization in the method \c is_def_eq.
*/ */
bool is_def_eq_args(expr t, expr s, type_checker & c, delayed_justification & jst) { bool is_def_eq_args(expr t, expr s, type_checker & c, delayed_justification & jst, constraint_seq & cs) {
while (is_app(t) && is_app(s)) { while (is_app(t) && is_app(s)) {
if (!is_def_eq(app_arg(t), app_arg(s), c, jst)) if (!is_def_eq(app_arg(t), app_arg(s), c, jst, cs))
return false; return false;
t = app_fn(t); t = app_fn(t);
s = app_fn(s); s = app_fn(s);
@ -395,33 +428,47 @@ struct default_converter : public converter {
} }
/** \brief Try to solve (fun (x : A), B) =?= s by trying eta-expansion on s */ /** \brief Try to solve (fun (x : A), B) =?= s by trying eta-expansion on s */
bool try_eta_expansion(expr const & t, expr const & s, type_checker & c, delayed_justification & jst) { bool try_eta_expansion(expr const & t, expr const & s, type_checker & c, delayed_justification & jst, constraint_seq & cs) {
if (is_lambda(t) && !is_lambda(s)) { if (is_lambda(t) && !is_lambda(s)) {
type_checker::scope scope(c); auto tcs = infer_type(c, s);
expr s_type = whnf(infer_type(c, s), c); auto wcs = whnf(tcs.first, c);
expr s_type = wcs.first;
if (!is_pi(s_type)) if (!is_pi(s_type))
return false; return false;
expr new_s = mk_lambda(binding_name(s_type), binding_domain(s_type), mk_app(s, Var(0)), binding_info(s_type)); expr new_s = mk_lambda(binding_name(s_type), binding_domain(s_type), mk_app(s, Var(0)), binding_info(s_type));
bool r = is_def_eq(t, new_s, c, jst); auto dcs = is_def_eq(t, new_s, c, jst);
if (r) scope.keep(); if (!dcs.first)
return r; return false;
cs = cs + dcs.second + wcs.second + tcs.second;
return true;
} else {
return false;
}
}
bool is_def_eq(expr const & t, expr const & s, type_checker & c, delayed_justification & jst, constraint_seq & cs) {
auto bcs = is_def_eq(t, s, c, jst);
if (bcs.first) {
cs = cs + bcs.second;
return true;
} else { } else {
return false; return false;
} }
} }
/** Return true iff t is definitionally equal to s. */ /** Return true iff t is definitionally equal to s. */
virtual bool is_def_eq(expr const & t, expr const & s, type_checker & c, delayed_justification & jst) { virtual pair<bool, constraint_seq> is_def_eq(expr const & t, expr const & s, type_checker & c, delayed_justification & jst) {
check_system("is_definitionally_equal"); check_system("is_definitionally_equal");
lbool r = quick_is_def_eq(t, s, c, jst); constraint_seq cs;
if (r != l_undef) return r == l_true; lbool r = quick_is_def_eq(t, s, c, jst, cs);
if (r != l_undef) return to_bcs(r == l_true, cs);
// apply whnf (without using delta-reduction or normalizer extensions) // apply whnf (without using delta-reduction or normalizer extensions)
expr t_n = whnf_core(t, c); expr t_n = whnf_core(t, c);
expr s_n = whnf_core(s, c); expr s_n = whnf_core(s, c);
if (!is_eqp(t_n, t) || !is_eqp(s_n, s)) { if (!is_eqp(t_n, t) || !is_eqp(s_n, s)) {
r = quick_is_def_eq(t_n, s_n, c, jst); r = quick_is_def_eq(t_n, s_n, c, jst, cs);
if (r != l_undef) return r == l_true; if (r != l_undef) return to_bcs(r == l_true, cs);
} }
// lazy delta-reduction and then normalizer extensions // lazy delta-reduction and then normalizer extensions
@ -447,6 +494,7 @@ struct default_converter : public converter {
if (has_expr_metavar(t_n) || has_expr_metavar(s_n)) { if (has_expr_metavar(t_n) || has_expr_metavar(s_n)) {
// We let the unifier deal with cases such as // We let the unifier deal with cases such as
// (f ...) =?= (f ...) // (f ...) =?= (f ...)
// when t_n or s_n contains metavariables
break; break;
} else { } else {
// Optimization: // Optimization:
@ -454,41 +502,37 @@ struct default_converter : public converter {
// If they are, then t_n and s_n must be definitionally equal, and we can // If they are, then t_n and s_n must be definitionally equal, and we can
// skip the delta-reduction step. // skip the delta-reduction step.
// If the flag use_conv_opt() is not true, then we skip this optimization // If the flag use_conv_opt() is not true, then we skip this optimization
if (!is_opaque(*d_t) && d_t->use_conv_opt()) { if (!is_opaque(*d_t) && d_t->use_conv_opt() &&
type_checker::scope scope(c); is_def_eq_args(t_n, s_n, c, jst, cs))
if (is_def_eq_args(t_n, s_n, c, jst)) { return to_bcs(true, cs);
scope.keep();
return true;
}
}
} }
} }
t_n = whnf_core(unfold_names(t_n, d_t->get_weight() - 1), c); t_n = whnf_core(unfold_names(t_n, d_t->get_weight() - 1), c);
s_n = whnf_core(unfold_names(s_n, d_s->get_weight() - 1), c); s_n = whnf_core(unfold_names(s_n, d_s->get_weight() - 1), c);
} }
r = quick_is_def_eq(t_n, s_n, c, jst); r = quick_is_def_eq(t_n, s_n, c, jst, cs);
if (r != l_undef) return r == l_true; if (r != l_undef) return to_bcs(r == l_true, cs);
} }
// try normalizer extensions // try normalizer extensions
auto new_t_n = norm_ext(t_n, c); auto new_t_n = d_norm_ext(t_n, c, cs);
auto new_s_n = norm_ext(s_n, c); auto new_s_n = d_norm_ext(s_n, c, cs);
if (!new_t_n && !new_s_n) if (!new_t_n && !new_s_n)
break; // t_n and s_n are in weak head normal form break; // t_n and s_n are in weak head normal form
if (new_t_n) if (new_t_n)
t_n = whnf_core(*new_t_n, c); t_n = whnf_core(*new_t_n, c);
if (new_s_n) if (new_s_n)
s_n = whnf_core(*new_s_n, c); s_n = whnf_core(*new_s_n, c);
r = quick_is_def_eq(t_n, s_n, c, jst); r = quick_is_def_eq(t_n, s_n, c, jst, cs);
if (r != l_undef) return r == l_true; if (r != l_undef) return to_bcs(r == l_true, cs);
} }
if (is_constant(t_n) && is_constant(s_n) && const_name(t_n) == const_name(s_n) && if (is_constant(t_n) && is_constant(s_n) && const_name(t_n) == const_name(s_n) &&
is_def_eq(const_levels(t_n), const_levels(s_n), c, jst)) is_def_eq(const_levels(t_n), const_levels(s_n), c, jst, cs))
return true; return to_bcs(true, cs);
if (is_local(t_n) && is_local(s_n) && mlocal_name(t_n) == mlocal_name(s_n) && if (is_local(t_n) && is_local(s_n) && mlocal_name(t_n) == mlocal_name(s_n) &&
is_def_eq(mlocal_type(t_n), mlocal_type(s_n), c, jst)) is_def_eq(mlocal_type(t_n), mlocal_type(s_n), c, jst, cs))
return true; return to_bcs(true, cs);
optional<declaration> d_t, d_s; optional<declaration> d_t, d_s;
bool delay_check = false; bool delay_check = false;
@ -503,66 +547,69 @@ struct default_converter : public converter {
// At this point, t_n and s_n are in weak head normal form (modulo meta-variables and proof irrelevance) // At this point, t_n and s_n are in weak head normal form (modulo meta-variables and proof irrelevance)
if (!delay_check && is_app(t_n) && is_app(s_n)) { if (!delay_check && is_app(t_n) && is_app(s_n)) {
type_checker::scope scope(c);
buffer<expr> t_args; buffer<expr> t_args;
buffer<expr> s_args; buffer<expr> s_args;
expr t_fn = get_app_args(t_n, t_args); expr t_fn = get_app_args(t_n, t_args);
expr s_fn = get_app_args(s_n, s_args); expr s_fn = get_app_args(s_n, s_args);
if (is_def_eq(t_fn, s_fn, c, jst) && t_args.size() == s_args.size()) { constraint_seq cs_prime = cs;
if (is_def_eq(t_fn, s_fn, c, jst, cs_prime) && t_args.size() == s_args.size()) {
unsigned i = 0; unsigned i = 0;
for (; i < t_args.size(); i++) { for (; i < t_args.size(); i++) {
if (!is_def_eq(t_args[i], s_args[i], c, jst)) if (!is_def_eq(t_args[i], s_args[i], c, jst, cs_prime))
break; break;
} }
if (i == t_args.size()) { if (i == t_args.size()) {
scope.keep(); return to_bcs(true, cs_prime);
return true;
} }
} }
} }
if (try_eta_expansion(t_n, s_n, c, jst) || if (try_eta_expansion(t_n, s_n, c, jst, cs) ||
try_eta_expansion(s_n, t_n, c, jst)) try_eta_expansion(s_n, t_n, c, jst, cs))
return true; return to_bcs(true, cs);
if (m_env.prop_proof_irrel()) { if (m_env.prop_proof_irrel()) {
// Proof irrelevance support for Prop (aka Type.{0}) // Proof irrelevance support for Prop (aka Type.{0})
type_checker::scope scope(c); auto tcs = infer_type(c, t);
expr t_type = infer_type(c, t); expr t_type = tcs.first;
if (is_prop(t_type, c) && is_def_eq(t_type, infer_type(c, s), c, jst)) { auto pcs = is_prop(t_type, c);
scope.keep(); if (pcs.first) {
return true; auto scs = infer_type(c, s);
auto dcs = is_def_eq(t_type, scs.first, c, jst);
if (dcs.first)
return to_bcs(true, dcs.second + scs.second + pcs.second + tcs.second);
} }
} }
list<name> const & cls_proof_irrel = m_env.cls_proof_irrel(); list<name> const & cls_proof_irrel = m_env.cls_proof_irrel();
if (!is_nil(cls_proof_irrel)) { if (!is_nil(cls_proof_irrel)) {
// Proof irrelevance support for classes // Proof irrelevance support for classes
type_checker::scope scope(c); auto tcs = infer_type(c, t);
expr t_type = whnf(infer_type(c, t), c); auto wcs = whnf(tcs.first, c);
if (std::any_of(cls_proof_irrel.begin(), cls_proof_irrel.end(), expr t_type = wcs.first;
[&](name const & cls_name) { return is_app_of(t_type, cls_name); }) && if (std::any_of(cls_proof_irrel.begin(), cls_proof_irrel.end(), [&](name const & cls_name) { return is_app_of(t_type, cls_name); })) {
is_def_eq(t_type, infer_type(c, s), c, jst)) { auto ccs = infer_type(c, s);
scope.keep(); auto cs_prime = tcs.second + wcs.second + ccs.second;
return true; if (is_def_eq(t_type, ccs.first, c, jst, cs_prime))
return to_bcs(true, cs_prime);
} }
} }
if (may_reduce_later(t_n, c) || may_reduce_later(s_n, c)) { if (may_reduce_later(t_n, c) || may_reduce_later(s_n, c) || delay_check) {
add_cnstr(c, mk_eq_cnstr(t_n, s_n, jst.get())); cs = cs + constraint_seq(mk_eq_cnstr(t_n, s_n, jst.get()));
return true; return to_bcs(true, cs);
} }
if (delay_check) { return to_bcs(false);
add_cnstr(c, mk_eq_cnstr(t_n, s_n, jst.get()));
return true;
} }
return false; pair<bool, constraint_seq> is_prop(expr const & e, type_checker & c) {
} auto tcs = infer_type(c, e);
auto wcs = whnf(tcs.first, c);
bool is_prop(expr const & e, type_checker & c) { if (wcs.first == Prop)
return whnf(infer_type(c, e), c) == Prop; return to_bcs(true, wcs.second + tcs.second);
else
return to_bcs(false);
} }
virtual optional<module_idx> get_module_idx() const { virtual optional<module_idx> get_module_idx() const {

View file

@ -13,15 +13,14 @@ class type_checker;
class converter { class converter {
protected: protected:
name mk_fresh_name(type_checker & tc); name mk_fresh_name(type_checker & tc);
expr infer_type(type_checker & tc, expr const & e); pair<expr, constraint_seq> infer_type(type_checker & tc, expr const & e);
void add_cnstr(type_checker & tc, constraint const & c);
extension_context & get_extension(type_checker & tc); extension_context & get_extension(type_checker & tc);
public: public:
virtual ~converter() {} virtual ~converter() {}
virtual expr whnf(expr const & e, type_checker & c) = 0; virtual pair<expr, constraint_seq> 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 pair<bool, constraint_seq> 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; virtual optional<module_idx> get_module_idx() const = 0;
bool is_def_eq(expr const & t, expr const & s, type_checker & c); pair<bool, constraint_seq> is_def_eq(expr const & t, expr const & s, type_checker & c);
}; };
std::unique_ptr<converter> mk_dummy_converter(); std::unique_ptr<converter> mk_dummy_converter();

View file

@ -358,7 +358,9 @@ public:
macro_definition & operator=(macro_definition && s); macro_definition & operator=(macro_definition && s);
name get_name() const { return m_ptr->get_name(); } name get_name() const { return m_ptr->get_name(); }
expr get_type(expr const & m, expr const * arg_types, extension_context & ctx) const { return m_ptr->get_type(m, arg_types, ctx); } expr get_type(expr const & m, expr const * arg_types, extension_context & ctx) const {
return m_ptr->get_type(m, arg_types, ctx);
}
optional<expr> expand(expr const & m, extension_context & ctx) const { return m_ptr->expand(m, ctx); } optional<expr> expand(expr const & m, extension_context & ctx) const { return m_ptr->expand(m, ctx); }
optional<expr> expand1(expr const & m, extension_context & ctx) const { return m_ptr->expand1(m, ctx); } optional<expr> expand1(expr const & m, extension_context & ctx) const { return m_ptr->expand1(m, ctx); }
unsigned trust_level() const { return m_ptr->trust_level(); } unsigned trust_level() const { return m_ptr->trust_level(); }

View file

@ -6,10 +6,9 @@ Author: Leonardo de Moura
*/ */
#pragma once #pragma once
#include "util/name.h" #include "util/name.h"
#include "kernel/constraint.h"
namespace lean { namespace lean {
class expr;
class constraint;
class environment; class environment;
class delayed_justification; class delayed_justification;
@ -26,10 +25,9 @@ class extension_context {
public: public:
virtual ~extension_context() {} virtual ~extension_context() {}
virtual environment const & env() const = 0; virtual environment const & env() const = 0;
virtual expr whnf(expr const & e) = 0; virtual pair<expr, constraint_seq> whnf(expr const & e) = 0;
virtual bool is_def_eq(expr const & e1, expr const & e2, delayed_justification & j) = 0; virtual pair<bool, constraint_seq> is_def_eq(expr const & e1, expr const & e2, delayed_justification & j) = 0;
virtual expr infer_type(expr const & e) = 0; virtual pair<expr, constraint_seq> infer_type(expr const & e) = 0;
virtual name mk_fresh_name() = 0; virtual name mk_fresh_name() = 0;
virtual void add_cnstr(constraint const & c) = 0;
}; };
} }

View file

@ -215,6 +215,9 @@ struct add_inductive_fn {
} }
type_checker & tc() { return *(m_tc.get()); } type_checker & tc() { return *(m_tc.get()); }
bool is_def_eq(expr const & t, expr const & s) { return tc().is_def_eq(t, s).first; }
expr whnf(expr const & e) { return tc().whnf(e).first; }
expr ensure_type(expr const & e) { return tc().ensure_type(e).first; }
/** \brief Return a fresh name. */ /** \brief Return a fresh name. */
name mk_fresh_name() { return m_ngen.next(); } name mk_fresh_name() { return m_ngen.next(); }
@ -246,7 +249,7 @@ struct add_inductive_fn {
m_param_consts.push_back(l); m_param_consts.push_back(l);
t = instantiate(binding_body(t), l); t = instantiate(binding_body(t), l);
} else { } else {
if (!tc().is_def_eq(binding_domain(t), get_param_type(i))) if (!is_def_eq(binding_domain(t), get_param_type(i)))
throw kernel_exception(m_env, "parameters of all inductive datatypes must match"); throw kernel_exception(m_env, "parameters of all inductive datatypes must match");
t = instantiate(binding_body(t), m_param_consts[i]); t = instantiate(binding_body(t), m_param_consts[i]);
} }
@ -258,7 +261,7 @@ struct add_inductive_fn {
} }
if (i != m_num_params) if (i != m_num_params)
throw kernel_exception(m_env, "number of parameters mismatch in inductive datatype declaration"); throw kernel_exception(m_env, "number of parameters mismatch in inductive datatype declaration");
t = tc().ensure_sort(t); t = tc().ensure_sort(t).first;
if (m_env.impredicative()) { if (m_env.impredicative()) {
// if the environment is impredicative, then the resultant universe is 0 (Prop), // if the environment is impredicative, then the resultant universe is 0 (Prop),
// or is never zero (under any parameter assignment). // or is never zero (under any parameter assignment).
@ -301,7 +304,7 @@ struct add_inductive_fn {
bool is_valid_it_app(expr const & t, unsigned d_idx) { bool is_valid_it_app(expr const & t, unsigned d_idx) {
buffer<expr> args; buffer<expr> args;
expr I = get_app_args(t, args); expr I = get_app_args(t, args);
if (!tc().is_def_eq(I, m_it_consts[d_idx]) || args.size() != m_it_num_args[d_idx]) if (!is_def_eq(I, m_it_consts[d_idx]) || args.size() != m_it_num_args[d_idx])
return false; return false;
for (unsigned i = 0; i < m_num_params; i++) { for (unsigned i = 0; i < m_num_params; i++) {
if (m_param_consts[i] != args[i]) if (m_param_consts[i] != args[i])
@ -336,15 +339,15 @@ struct add_inductive_fn {
Return none otherwise. Return none otherwise.
*/ */
optional<unsigned> is_rec_argument(expr t) { optional<unsigned> is_rec_argument(expr t) {
t = tc().whnf(t); t = whnf(t);
while (is_pi(t)) while (is_pi(t))
t = tc().whnf(instantiate(binding_body(t), mk_local_for(t))); t = whnf(instantiate(binding_body(t), mk_local_for(t)));
return is_valid_it_app(t); return is_valid_it_app(t);
} }
/** \brief Check if \c t contains only positive occurrences of the inductive datatypes being declared. */ /** \brief Check if \c t contains only positive occurrences of the inductive datatypes being declared. */
void check_positivity(expr t, name const & intro_name, int arg_idx) { void check_positivity(expr t, name const & intro_name, int arg_idx) {
t = tc().whnf(t); t = whnf(t);
if (!has_it_occ(t)) { if (!has_it_occ(t)) {
// nonrecursive argument // nonrecursive argument
} else if (is_pi(t)) { } else if (is_pi(t)) {
@ -373,12 +376,12 @@ struct add_inductive_fn {
bool found_rec = false; bool found_rec = false;
while (is_pi(t)) { while (is_pi(t)) {
if (i < m_num_params) { if (i < m_num_params) {
if (!tc().is_def_eq(binding_domain(t), get_param_type(i))) if (!is_def_eq(binding_domain(t), get_param_type(i)))
throw kernel_exception(m_env, sstream() << "arg #" << (i + 1) << " of '" << n << "' " throw kernel_exception(m_env, sstream() << "arg #" << (i + 1) << " of '" << n << "' "
<< "does not match inductive datatypes parameters'"); << "does not match inductive datatypes parameters'");
t = instantiate(binding_body(t), m_param_consts[i]); t = instantiate(binding_body(t), m_param_consts[i]);
} else { } else {
expr s = tc().ensure_type(binding_domain(t)); expr s = ensure_type(binding_domain(t));
// the sort is ok IF // the sort is ok IF
// 1- its level is <= inductive datatype level, OR // 1- its level is <= inductive datatype level, OR
// 2- m_env is impredicative and inductive datatype is at level 0 // 2- m_env is impredicative and inductive datatype is at level 0
@ -457,7 +460,7 @@ struct add_inductive_fn {
unsigned i = 0; unsigned i = 0;
while (is_pi(t)) { while (is_pi(t)) {
if (i >= m_num_params) { if (i >= m_num_params) {
expr s = tc().ensure_type(binding_domain(t)); expr s = ensure_type(binding_domain(t));
if (!is_zero(sort_level(s))) if (!is_zero(sort_level(s)))
return true; return true;
} }
@ -569,12 +572,12 @@ struct add_inductive_fn {
// populate v using u // populate v using u
for (unsigned i = 0; i < u.size(); i++) { for (unsigned i = 0; i < u.size(); i++) {
expr u_i = u[i]; expr u_i = u[i];
expr u_i_ty = tc().whnf(mlocal_type(u_i)); expr u_i_ty = whnf(mlocal_type(u_i));
buffer<expr> xs; buffer<expr> xs;
while (is_pi(u_i_ty)) { while (is_pi(u_i_ty)) {
expr x = mk_local_for(u_i_ty); expr x = mk_local_for(u_i_ty);
xs.push_back(x); xs.push_back(x);
u_i_ty = tc().whnf(instantiate(binding_body(u_i_ty), x)); u_i_ty = whnf(instantiate(binding_body(u_i_ty), x));
} }
buffer<expr> it_indices; buffer<expr> it_indices;
unsigned it_idx = get_I_indices(u_i_ty, it_indices); unsigned it_idx = get_I_indices(u_i_ty, it_indices);
@ -707,12 +710,12 @@ struct add_inductive_fn {
if (m_dep_elim) { if (m_dep_elim) {
for (unsigned i = 0; i < u.size(); i++) { for (unsigned i = 0; i < u.size(); i++) {
expr u_i = u[i]; expr u_i = u[i];
expr u_i_ty = tc().whnf(mlocal_type(u_i)); expr u_i_ty = whnf(mlocal_type(u_i));
buffer<expr> xs; buffer<expr> xs;
while (is_pi(u_i_ty)) { while (is_pi(u_i_ty)) {
expr x = mk_local_for(u_i_ty); expr x = mk_local_for(u_i_ty);
xs.push_back(x); xs.push_back(x);
u_i_ty = tc().whnf(instantiate(binding_body(u_i_ty), x)); u_i_ty = whnf(instantiate(binding_body(u_i_ty), x));
} }
buffer<expr> it_indices; buffer<expr> it_indices;
unsigned it_idx = get_I_indices(u_i_ty, it_indices); unsigned it_idx = get_I_indices(u_i_ty, it_indices);
@ -758,46 +761,50 @@ bool inductive_normalizer_extension::supports(name const & feature) const {
return feature == g_inductive_extension; return feature == g_inductive_extension;
} }
optional<expr> inductive_normalizer_extension::operator()(expr const & e, extension_context & ctx) const { optional<pair<expr, constraint_seq>> inductive_normalizer_extension::operator()(expr const & e, extension_context & ctx) const {
// Reduce terms \c e of the form // Reduce terms \c e of the form
// elim_k A C e p[A,b] (intro_k_i A b u) // elim_k A C e p[A,b] (intro_k_i A b u)
inductive_env_ext const & ext = get_extension(ctx.env()); inductive_env_ext const & ext = get_extension(ctx.env());
expr const & elim_fn = get_app_fn(e); expr const & elim_fn = get_app_fn(e);
if (!is_constant(elim_fn)) if (!is_constant(elim_fn))
return none_expr(); return none_ecs();
auto it1 = ext.m_elim_info.find(const_name(elim_fn)); auto it1 = ext.m_elim_info.find(const_name(elim_fn));
if (!it1) if (!it1)
return none_expr(); // it is not an eliminator return none_ecs(); // it is not an eliminator
buffer<expr> elim_args; buffer<expr> elim_args;
get_app_args(e, elim_args); get_app_args(e, elim_args);
unsigned major_idx = it1->m_num_ACe + it1->m_num_indices; unsigned major_idx = it1->m_num_ACe + it1->m_num_indices;
if (elim_args.size() < major_idx + 1) if (elim_args.size() < major_idx + 1)
return none_expr(); // major premise is missing return none_ecs(); // major premise is missing
expr intro_app = ctx.whnf(elim_args[major_idx]); auto intro_app_cs = ctx.whnf(elim_args[major_idx]);
expr intro_app = intro_app_cs.first;
constraint_seq cs = intro_app_cs.second;
expr const & intro_fn = get_app_fn(intro_app); expr const & intro_fn = get_app_fn(intro_app);
// Last argument must be a constant and an application of a constant. // Last argument must be a constant and an application of a constant.
if (!is_constant(intro_fn)) if (!is_constant(intro_fn))
return none_expr(); return none_ecs();
// Check if intro_fn is an introduction rule matching elim_fn // Check if intro_fn is an introduction rule matching elim_fn
auto it2 = ext.m_comp_rules.find(const_name(intro_fn)); auto it2 = ext.m_comp_rules.find(const_name(intro_fn));
if (!it2 || it2->m_elim_name != const_name(elim_fn)) if (!it2 || it2->m_elim_name != const_name(elim_fn))
return none_expr(); return none_ecs();
buffer<expr> intro_args; buffer<expr> intro_args;
get_app_args(intro_app, intro_args); get_app_args(intro_app, intro_args);
// Check intro num_args // Check intro num_args
if (intro_args.size() != it1->m_num_params + it2->m_num_bu) if (intro_args.size() != it1->m_num_params + it2->m_num_bu)
return none_expr(); return none_ecs();
if (it1->m_num_params > 0) { if (it1->m_num_params > 0) {
// Global parameters of elim and intro be definitionally equal // Global parameters of elim and intro be definitionally equal
simple_delayed_justification jst([=]() { return mk_justification("elim/intro global parameters must match", some_expr(e)); }); simple_delayed_justification jst([=]() { return mk_justification("elim/intro global parameters must match", some_expr(e)); });
for (unsigned i = 0; i < it1->m_num_params; i++) { for (unsigned i = 0; i < it1->m_num_params; i++) {
if (!ctx.is_def_eq(elim_args[i], intro_args[i], jst)) auto dcs = ctx.is_def_eq(elim_args[i], intro_args[i], jst);
return none_expr(); if (!dcs.first)
return none_ecs();
cs = cs + dcs.second;
} }
} }
// Number of universe levels must match. // Number of universe levels must match.
if (length(const_levels(elim_fn)) != length(it1->m_level_names)) if (length(const_levels(elim_fn)) != length(it1->m_level_names))
return none_expr(); return none_ecs();
buffer<expr> ACebu; buffer<expr> ACebu;
for (unsigned i = 0; i < it1->m_num_ACe; i++) for (unsigned i = 0; i < it1->m_num_ACe; i++)
ACebu.push_back(elim_args[i]); ACebu.push_back(elim_args[i]);
@ -810,7 +817,7 @@ optional<expr> inductive_normalizer_extension::operator()(expr const & e, extens
unsigned num_args = elim_args.size() - major_idx - 1; unsigned num_args = elim_args.size() - major_idx - 1;
r = mk_app(r, num_args, elim_args.data() + major_idx + 1); r = mk_app(r, num_args, elim_args.data() + major_idx + 1);
} }
return some_expr(r); return some_ecs(r, cs);
} }
template<typename Ctx> template<typename Ctx>
@ -827,7 +834,7 @@ bool is_elim_meta_app_core(Ctx & ctx, expr const & e) {
unsigned major_idx = it1->m_num_ACe + it1->m_num_indices; unsigned major_idx = it1->m_num_ACe + it1->m_num_indices;
if (elim_args.size() < major_idx + 1) if (elim_args.size() < major_idx + 1)
return false; return false;
expr intro_app = ctx.whnf(elim_args[major_idx]); expr intro_app = ctx.whnf(elim_args[major_idx]).first;
return has_expr_metavar_strict(intro_app); return has_expr_metavar_strict(intro_app);
} }

View file

@ -16,7 +16,7 @@ namespace inductive {
/** \brief Normalizer extension for applying inductive datatype computational rules. */ /** \brief Normalizer extension for applying inductive datatype computational rules. */
class inductive_normalizer_extension : public normalizer_extension { class inductive_normalizer_extension : public normalizer_extension {
public: public:
virtual optional<expr> operator()(expr const & e, extension_context & ctx) const; virtual optional<pair<expr, constraint_seq>> operator()(expr const & e, extension_context & ctx) const;
virtual bool may_reduce_later(expr const & e, extension_context & ctx) const; virtual bool may_reduce_later(expr const & e, extension_context & ctx) const;
virtual bool supports(name const & feature) const; virtual bool supports(name const & feature) const;
}; };

View file

@ -9,7 +9,9 @@ Author: Leonardo de Moura
namespace lean { namespace lean {
class id_normalizer_extension : public normalizer_extension { class id_normalizer_extension : public normalizer_extension {
public: public:
virtual optional<expr> operator()(expr const &, extension_context &) const { return none_expr(); } virtual optional<pair<expr, constraint_seq>> operator()(expr const &, extension_context &) const {
return optional<pair<expr, constraint_seq>>();
}
virtual bool may_reduce_later(expr const &, extension_context &) const { return false; } virtual bool may_reduce_later(expr const &, extension_context &) const { return false; }
virtual bool supports(name const &) const { return false; } virtual bool supports(name const &) const { return false; }
}; };
@ -25,7 +27,7 @@ public:
comp_normalizer_extension(std::unique_ptr<normalizer_extension> && ext1, std::unique_ptr<normalizer_extension> && ext2): comp_normalizer_extension(std::unique_ptr<normalizer_extension> && ext1, std::unique_ptr<normalizer_extension> && ext2):
m_ext1(std::move(ext1)), m_ext2(std::move(ext2)) {} m_ext1(std::move(ext1)), m_ext2(std::move(ext2)) {}
virtual optional<expr> operator()(expr const & e, extension_context & ctx) const { virtual optional<pair<expr, constraint_seq>> operator()(expr const & e, extension_context & ctx) const {
if (auto r = (*m_ext1)(e, ctx)) if (auto r = (*m_ext1)(e, ctx))
return r; return r;
else else

View file

@ -17,7 +17,7 @@ namespace lean {
class normalizer_extension { class normalizer_extension {
public: public:
virtual ~normalizer_extension() {} virtual ~normalizer_extension() {}
virtual optional<expr> operator()(expr const & e, extension_context & ctx) const = 0; virtual optional<pair<expr, constraint_seq>> operator()(expr const & e, extension_context & ctx) const = 0;
/** \brief Return true if the extension may reduce \c e after metavariables are instantiated. */ /** \brief Return true if the extension may reduce \c e after metavariables are instantiated. */
virtual bool may_reduce_later(expr const & e, extension_context & ctx) const = 0; virtual bool may_reduce_later(expr const & e, extension_context & ctx) const = 0;
/** \brief Return true iff the extension supports a feature with the given name, /** \brief Return true iff the extension supports a feature with the given name,
@ -25,6 +25,11 @@ public:
virtual bool supports(name const & feature) const = 0; virtual bool supports(name const & feature) const = 0;
}; };
inline optional<pair<expr, constraint_seq>> none_ecs() { return optional<pair<expr, constraint_seq>>(); }
inline optional<pair<expr, constraint_seq>> some_ecs(expr const & e, constraint_seq const & cs) {
return optional<pair<expr, constraint_seq>>(e, cs);
}
/** \brief Create the do-nothing normalizer extension */ /** \brief Create the do-nothing normalizer extension */
std::unique_ptr<normalizer_extension> mk_id_normalizer_extension(); std::unique_ptr<normalizer_extension> mk_id_normalizer_extension();

View file

@ -52,8 +52,8 @@ environment add_record(environment const & env, level_param_names const & level_
return add_record_fn(env, level_params, rec_name, rec_type, intro_name, intro_type)(); return add_record_fn(env, level_params, rec_name, rec_type, intro_name, intro_type)();
} }
optional<expr> record_normalizer_extension::operator()(expr const &, extension_context &) const { optional<pair<expr, constraint_seq>> record_normalizer_extension::operator()(expr const &, extension_context &) const {
return optional<expr>(); return none_ecs();
} }
bool record_normalizer_extension::may_reduce_later(expr const &, extension_context &) const { bool record_normalizer_extension::may_reduce_later(expr const &, extension_context &) const {

View file

@ -27,7 +27,7 @@ environment add_record(environment const & env,
/** \brief Normalizer extension for applying record computational rules. */ /** \brief Normalizer extension for applying record computational rules. */
class record_normalizer_extension : public normalizer_extension { class record_normalizer_extension : public normalizer_extension {
public: public:
virtual optional<expr> operator()(expr const & e, extension_context & ctx) const; virtual optional<pair<expr, constraint_seq>> operator()(expr const & e, extension_context & ctx) const;
virtual bool may_reduce_later(expr const & e, extension_context & ctx) const; virtual bool may_reduce_later(expr const & e, extension_context & ctx) const;
virtual bool supports(name const & feature) const; virtual bool supports(name const & feature) const;
}; };

View file

@ -71,21 +71,6 @@ expr mk_pi_for(name_generator & ngen, expr const & meta) {
return mk_pi(ngen.next(), A, B); return mk_pi(ngen.next(), A, B);
} }
type_checker::scope::scope(type_checker & tc):
m_tc(tc), m_keep(false) {
m_tc.push();
}
type_checker::scope::~scope() {
if (m_keep)
m_tc.keep();
else
m_tc.pop();
}
void type_checker::scope::keep() {
m_keep = true;
}
optional<expr> type_checker::expand_macro(expr const & m) { optional<expr> type_checker::expand_macro(expr const & m) {
lean_assert(is_macro(m)); lean_assert(is_macro(m));
return macro_def(m).expand(m, m_tc_ctx); return macro_def(m).expand(m, m_tc_ctx);
@ -100,25 +85,10 @@ pair<expr, expr> type_checker::open_binding_body(expr const & e) {
return mk_pair(instantiate(binding_body(e), local), local); return mk_pair(instantiate(binding_body(e), local), local);
} }
/** \brief Add given constraint using m_add_cnstr_fn. */
void type_checker::add_cnstr(constraint const & c) {
m_cs.push_back(c);
}
constraint type_checker::mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j) { 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())); return ::lean::mk_eq_cnstr(lhs, rhs, j, static_cast<bool>(m_conv->get_module_idx()));
} }
optional<constraint> type_checker::next_cnstr() {
if (m_cs_qhead < m_cs.size()) {
constraint c = m_cs[m_cs_qhead];
m_cs_qhead++;
return optional<constraint>(c);
} else {
return optional<constraint>();
}
}
/** /**
\brief Make sure \c e "is" a sort, and return the corresponding sort. \brief Make sure \c e "is" a sort, and return the corresponding sort.
If \c e is not a sort, then the whnf procedure is invoked. Then, there are If \c e is not a sort, then the whnf procedure is invoked. Then, there are
@ -128,39 +98,37 @@ optional<constraint> type_checker::next_cnstr() {
\remark \c s is used to extract position (line number information) when an \remark \c s is used to extract position (line number information) when an
error message is produced error message is produced
*/ */
expr type_checker::ensure_sort_core(expr e, expr const & s) { pair<expr, constraint_seq> type_checker::ensure_sort_core(expr e, expr const & s) {
if (is_sort(e)) if (is_sort(e))
return e; return to_ecs(e);
e = whnf(e); auto ecs = whnf(e);
if (is_sort(e)) { if (is_sort(ecs.first)) {
return e; return ecs;
} else if (is_meta(e)) { } else if (is_meta(ecs.first)) {
expr r = mk_sort(mk_meta_univ(m_gen.next())); expr r = mk_sort(mk_meta_univ(m_gen.next()));
justification j = mk_justification(s, justification j = mk_justification(s,
[=](formatter const & fmt, substitution const & subst) { [=](formatter const & fmt, substitution const & subst) {
return pp_type_expected(fmt, substitution(subst).instantiate(s)); return pp_type_expected(fmt, substitution(subst).instantiate(s));
}); });
add_cnstr(mk_eq_cnstr(e, r, j)); return to_ecs(r, mk_eq_cnstr(ecs.first, r, j), ecs.second);
return r;
} else { } else {
throw_kernel_exception(m_env, s, [=](formatter const & fmt) { return pp_type_expected(fmt, s); }); throw_kernel_exception(m_env, s, [=](formatter const & fmt) { return pp_type_expected(fmt, s); });
} }
} }
/** \brief Similar to \c ensure_sort, but makes sure \c e "is" a Pi. */ /** \brief Similar to \c ensure_sort, but makes sure \c e "is" a Pi. */
expr type_checker::ensure_pi_core(expr e, expr const & s) { pair<expr, constraint_seq> type_checker::ensure_pi_core(expr e, expr const & s) {
if (is_pi(e)) if (is_pi(e))
return e; return to_ecs(e);
e = whnf(e); auto ecs = whnf(e);
if (is_pi(e)) { if (is_pi(ecs.first)) {
return e; return ecs;
} else if (is_meta(e)) { } else if (is_meta(ecs.first)) {
expr r = mk_pi_for(m_gen, e); expr r = mk_pi_for(m_gen, ecs.first);
justification j = mk_justification(s, [=](formatter const & fmt, substitution const & subst) { justification j = mk_justification(s, [=](formatter const & fmt, substitution const & subst) {
return pp_function_expected(fmt, substitution(subst).instantiate(s)); return pp_function_expected(fmt, substitution(subst).instantiate(s));
}); });
add_cnstr(mk_eq_cnstr(e, r, j)); return to_ecs(r, mk_eq_cnstr(ecs.first, r, j), ecs.second);
return r;
} else { } else {
throw_kernel_exception(m_env, s, [=](formatter const & fmt) { return pp_function_expected(fmt, s); }); throw_kernel_exception(m_env, s, [=](formatter const & fmt) { return pp_function_expected(fmt, s); });
} }
@ -217,26 +185,34 @@ expr type_checker::infer_constant(expr const & e, bool infer_only) {
return instantiate_type_univ_params(d, ls); return instantiate_type_univ_params(d, ls);
} }
expr type_checker::infer_macro(expr const & e, bool infer_only) { pair<expr, constraint_seq> type_checker::infer_macro(expr const & e, bool infer_only) {
buffer<expr> arg_types; buffer<expr> arg_types;
for (unsigned i = 0; i < macro_num_args(e); i++) constraint_seq cs;
arg_types.push_back(infer_type_core(macro_arg(e, i), infer_only)); for (unsigned i = 0; i < macro_num_args(e); i++) {
expr r = macro_def(e).get_type(e, arg_types.data(), m_tc_ctx); arg_types.push_back(infer_type_core(macro_arg(e, i), infer_only, cs));
if (!infer_only && macro_def(e).trust_level() >= m_env.trust_lvl()) { }
auto def = macro_def(e);
expr t = def.get_type(e, arg_types.data(), m_tc_ctx);
if (!infer_only && def.trust_level() >= m_env.trust_lvl()) {
optional<expr> m = expand_macro(e); optional<expr> m = expand_macro(e);
if (!m) if (!m)
throw_kernel_exception(m_env, "failed to expand macro", e); throw_kernel_exception(m_env, "failed to expand macro", e);
expr t = infer_type_core(*m, infer_only); pair<expr, constraint_seq> tmcs = infer_type_core(*m, infer_only);
cs = cs + tmcs.second;
simple_delayed_justification jst([=]() { return mk_macro_jst(e); }); simple_delayed_justification jst([=]() { return mk_macro_jst(e); });
if (!is_def_eq(r, t, jst)) pair<bool, constraint_seq> bcs = is_def_eq(t, tmcs.first, jst);
if (!bcs.first)
throw_kernel_exception(m_env, g_macro_error_msg, e); throw_kernel_exception(m_env, g_macro_error_msg, e);
return mk_pair(t, bcs.second + cs);
} else {
return mk_pair(t, cs);
} }
return r;
} }
expr type_checker::infer_lambda(expr const & _e, bool infer_only) { pair<expr, constraint_seq> type_checker::infer_lambda(expr const & _e, bool infer_only) {
buffer<expr> es, ds, ls; buffer<expr> es, ds, ls;
expr e = _e; expr e = _e;
constraint_seq cs;
while (is_lambda(e)) { while (is_lambda(e)) {
es.push_back(e); es.push_back(e);
ds.push_back(binding_domain(e)); ds.push_back(binding_domain(e));
@ -244,59 +220,76 @@ expr type_checker::infer_lambda(expr const & _e, bool infer_only) {
expr l = mk_local(m_gen.next(), binding_name(e), d, binding_info(e)); expr l = mk_local(m_gen.next(), binding_name(e), d, binding_info(e));
ls.push_back(l); ls.push_back(l);
if (!infer_only) { if (!infer_only) {
expr t = infer_type_core(d, infer_only); pair<expr, constraint_seq> dtcs = infer_type_core(d, infer_only);
ensure_sort_core(t, d); pair<expr, constraint_seq> scs = ensure_sort_core(dtcs.first, d);
cs = cs + scs.second + dtcs.second;
} }
e = binding_body(e); e = binding_body(e);
} }
expr r = infer_type_core(instantiate_rev(e, ls.size(), ls.data()), infer_only); pair<expr, constraint_seq> rcs = infer_type_core(instantiate_rev(e, ls.size(), ls.data()), infer_only);
r = abstract_locals(r, ls.size(), ls.data()); cs = cs + rcs.second;
expr r = abstract_locals(rcs.first, ls.size(), ls.data());
unsigned i = es.size(); unsigned i = es.size();
while (i > 0) { while (i > 0) {
--i; --i;
r = mk_pi(binding_name(es[i]), ds[i], r, binding_info(es[i])); r = mk_pi(binding_name(es[i]), ds[i], r, binding_info(es[i]));
} }
return r; return mk_pair(r, cs);
} }
expr type_checker::infer_pi(expr const & _e, bool infer_only) { pair<expr, constraint_seq> type_checker::infer_pi(expr const & _e, bool infer_only) {
buffer<expr> ls; buffer<expr> ls;
buffer<level> us; buffer<level> us;
expr e = _e; expr e = _e;
constraint_seq cs;
while (is_pi(e)) { while (is_pi(e)) {
expr d = instantiate_rev(binding_domain(e), ls.size(), ls.data()); expr d = instantiate_rev(binding_domain(e), ls.size(), ls.data());
expr t1 = ensure_sort_core(infer_type_core(d, infer_only), d); pair<expr, constraint_seq> dtcs = infer_type_core(d, infer_only);
pair<expr, constraint_seq> scs = ensure_sort_core(dtcs.first, d);
cs = cs + scs.second + dtcs.second;
expr t1 = scs.first;
us.push_back(sort_level(t1)); us.push_back(sort_level(t1));
expr l = mk_local(m_gen.next(), binding_name(e), d, binding_info(e)); expr l = mk_local(m_gen.next(), binding_name(e), d, binding_info(e));
ls.push_back(l); ls.push_back(l);
e = binding_body(e); e = binding_body(e);
} }
e = instantiate_rev(e, ls.size(), ls.data()); e = instantiate_rev(e, ls.size(), ls.data());
level r = sort_level(ensure_sort_core(infer_type_core(e, infer_only), e)); pair<expr, constraint_seq> etcs = infer_type_core(e, infer_only);
pair<expr, constraint_seq> scs = ensure_sort_core(etcs.first, e);
cs = cs + scs.second + etcs.second;
level r = sort_level(scs.first);
unsigned i = ls.size(); unsigned i = ls.size();
while (i > 0) { while (i > 0) {
--i; --i;
r = m_env.impredicative() ? mk_imax(us[i], r) : mk_max(us[i], r); r = m_env.impredicative() ? mk_imax(us[i], r) : mk_max(us[i], r);
} }
return mk_sort(r); return mk_pair(mk_sort(r), cs);
} }
expr type_checker::infer_app(expr const & e, bool infer_only) { pair<expr, constraint_seq> type_checker::infer_app(expr const & e, bool infer_only) {
if (!infer_only) { if (!infer_only) {
expr f_type = ensure_pi_core(infer_type_core(app_fn(e), infer_only), e); pair<expr, constraint_seq> ftcs = infer_type_core(app_fn(e), infer_only);
expr a_type = infer_type_core(app_arg(e), infer_only); pair<expr, constraint_seq> pics = ensure_pi_core(ftcs.first, e);
expr f_type = pics.first;
pair<expr, constraint_seq> acs = infer_type_core(app_arg(e), infer_only);
expr a_type = acs.first;
app_delayed_justification jst(e, app_arg(e), f_type, a_type); app_delayed_justification jst(e, app_arg(e), f_type, a_type);
if (!is_def_eq(a_type, binding_domain(f_type), jst)) { expr d_type = binding_domain(pics.first);
pair<bool, constraint_seq> dcs = is_def_eq(a_type, d_type, jst);
if (!dcs.first) {
throw_kernel_exception(m_env, e, throw_kernel_exception(m_env, e,
[=](formatter const & fmt) { [=](formatter const & fmt) {
return pp_app_type_mismatch(fmt, e, app_arg(e), binding_domain(f_type), a_type); return pp_app_type_mismatch(fmt, e, app_arg(e), d_type, a_type);
}); });
} }
return instantiate(binding_body(f_type), app_arg(e)); return mk_pair(instantiate(binding_body(pics.first), app_arg(e)),
pics.second + ftcs.second + dcs.second + acs.second);
} else { } else {
buffer<expr> args; buffer<expr> args;
expr const & f = get_app_args(e, args); expr const & f = get_app_args(e, args);
expr f_type = infer_type_core(f, true); pair<expr, constraint_seq> ftcs = infer_type_core(f, true);
expr f_type = ftcs.first;
constraint_seq cs = ftcs.second;
unsigned j = 0; unsigned j = 0;
unsigned nargs = args.size(); unsigned nargs = args.size();
for (unsigned i = 0; i < nargs; i++) { for (unsigned i = 0; i < nargs; i++) {
@ -304,22 +297,30 @@ expr type_checker::infer_app(expr const & e, bool infer_only) {
f_type = binding_body(f_type); f_type = binding_body(f_type);
} else { } else {
f_type = instantiate_rev(f_type, i-j, args.data()+j); f_type = instantiate_rev(f_type, i-j, args.data()+j);
f_type = ensure_pi_core(f_type, e); pair<expr, constraint_seq> pics = ensure_pi_core(f_type, e);
f_type = pics.first;
cs = pics.second + cs;
f_type = binding_body(f_type); f_type = binding_body(f_type);
j = i; j = i;
} }
} }
expr r = instantiate_rev(f_type, nargs-j, args.data()+j); expr r = instantiate_rev(f_type, nargs-j, args.data()+j);
return r; return mk_pair(r, cs);
} }
} }
expr type_checker::infer_type_core(expr const & e, bool infer_only, constraint_seq & cs) {
auto r = infer_type_core(e, infer_only);
cs = cs + r.second;
return r.first;
}
/** /**
\brief Return type of expression \c e, if \c infer_only is false, then it also check whether \c e is type correct or not. \brief Return type of expression \c e, if \c infer_only is false, then it also check whether \c e is type correct or not.
\pre closed(e) \pre closed(e)
*/ */
expr type_checker::infer_type_core(expr const & e, bool infer_only) { pair<expr, constraint_seq> type_checker::infer_type_core(expr const & e, bool infer_only) {
if (is_var(e)) if (is_var(e))
throw_kernel_exception(m_env, "type checker does not support free variables, replace them with local constants before invoking it", e); throw_kernel_exception(m_env, "type checker does not support free variables, replace them with local constants before invoking it", e);
@ -332,16 +333,16 @@ expr type_checker::infer_type_core(expr const & e, bool infer_only) {
return it->second; return it->second;
} }
expr r; pair<expr, constraint_seq> r;
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Local: case expr_kind::Meta: r = mlocal_type(e); break; case expr_kind::Local: case expr_kind::Meta: r.first = mlocal_type(e); break;
case expr_kind::Var: case expr_kind::Var:
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::Sort: case expr_kind::Sort:
if (!infer_only) check_level(sort_level(e), e); if (!infer_only) check_level(sort_level(e), e);
r = mk_sort(mk_succ(sort_level(e))); r.first = mk_sort(mk_succ(sort_level(e)));
break; break;
case expr_kind::Constant: r = infer_constant(e, infer_only); break; case expr_kind::Constant: r.first = infer_constant(e, infer_only); break;
case expr_kind::Macro: r = infer_macro(e, infer_only); break; case expr_kind::Macro: r = infer_macro(e, infer_only); break;
case expr_kind::Lambda: r = infer_lambda(e, infer_only); break; case expr_kind::Lambda: r = infer_lambda(e, infer_only); break;
case expr_kind::Pi: r = infer_pi(e, infer_only); break; case expr_kind::Pi: r = infer_pi(e, infer_only); break;
@ -354,151 +355,63 @@ expr type_checker::infer_type_core(expr const & e, bool infer_only) {
return r; return r;
} }
expr type_checker::infer_type(expr const & e) { pair<expr, constraint_seq> type_checker::infer_type(expr const & e) {
scope mk_scope(*this); return infer_type_core(e, true);
expr r = infer_type_core(e, true);
mk_scope.keep();
return r;
} }
void type_checker::copy_constraints(unsigned qhead, buffer<constraint> & new_cnstrs) { pair<expr, constraint_seq> type_checker::check(expr const & e, level_param_names const & ps) {
for (unsigned i = qhead; i < m_cs.size(); i++)
new_cnstrs.push_back(m_cs[i]);
}
expr type_checker::infer(expr const & e, buffer<constraint> & new_cnstrs) {
scope mk_scope(*this);
unsigned cs_qhead = m_cs.size();
expr r = infer_type_core(e, true);
copy_constraints(cs_qhead, new_cnstrs);
return r;
}
expr type_checker::check(expr const & e, level_param_names const & ps) {
scope mk_scope(*this);
flet<level_param_names const *> updt(m_params, &ps); flet<level_param_names const *> updt(m_params, &ps);
expr r = infer_type_core(e, false); return infer_type_core(e, false);
mk_scope.keep();
return r;
} }
expr type_checker::check(expr const & e, buffer<constraint> & new_cnstrs) { pair<expr, constraint_seq> type_checker::ensure_sort(expr const & e, expr const & s) {
scope mk_scope(*this); return ensure_sort_core(e, s);
unsigned cs_qhead = m_cs.size();
expr r = infer_type_core(e, false);
copy_constraints(cs_qhead, new_cnstrs);
return r;
} }
expr type_checker::ensure_sort(expr const & e, expr const & s) { pair<expr, constraint_seq> type_checker::ensure_pi(expr const & e, expr const & s) {
scope mk_scope(*this); return ensure_pi_core(e, s);
expr r = ensure_sort_core(e, s);
mk_scope.keep();
return r;
}
expr type_checker::ensure_pi(expr const & e, expr const & s) {
scope mk_scope(*this);
expr r = ensure_pi_core(e, s);
mk_scope.keep();
return r;
} }
/** \brief Return true iff \c t and \c s are definitionally equal */ /** \brief Return true iff \c t and \c s are definitionally equal */
bool type_checker::is_def_eq(expr const & t, expr const & s, delayed_justification & jst) { pair<bool, constraint_seq> type_checker::is_def_eq(expr const & t, expr const & s, delayed_justification & jst) {
scope mk_scope(*this); return m_conv->is_def_eq(t, s, *this, jst);
bool r = m_conv->is_def_eq(t, s, *this, jst);
if (r) mk_scope.keep();
return r;
} }
bool type_checker::is_def_eq(expr const & t, expr const & s) { pair<bool, constraint_seq> type_checker::is_def_eq(expr const & t, expr const & s) {
scope mk_scope(*this); return m_conv->is_def_eq(t, s, *this);
bool r = m_conv->is_def_eq(t, s, *this);
if (r) mk_scope.keep();
return r;
} }
bool type_checker::is_def_eq(expr const & t, expr const & s, justification const & j) { pair<bool, constraint_seq> type_checker::is_def_eq(expr const & t, expr const & s, justification const & j) {
as_delayed_justification djst(j); as_delayed_justification djst(j);
return is_def_eq(t, s, djst); return is_def_eq(t, s, djst);
} }
bool type_checker::is_def_eq(expr const & t, expr const & s, justification const & j, buffer<constraint> & new_cnstrs) { pair<bool, constraint_seq> type_checker::is_def_eq_types(expr const & t, expr const & s, justification const & j) {
unsigned cs_qhead = m_cs.size(); auto tcs1 = infer_type_core(t, true);
scope mk_scope(*this); auto tcs2 = infer_type_core(s, true);
as_delayed_justification djst(j); as_delayed_justification djst(j);
if (m_conv->is_def_eq(t, s, *this, djst)) { auto bcs = m_conv->is_def_eq(tcs1.first, tcs2.first, *this, djst);
copy_constraints(cs_qhead, new_cnstrs); return mk_pair(bcs.first, bcs.first ? bcs.second + tcs1.second + tcs2.second : constraint_seq());
return true;
} else {
return false;
}
}
bool type_checker::is_def_eq_types(expr const & t, expr const & s, justification const & j, buffer<constraint> & new_cnstrs) {
scope mk_scope(*this);
unsigned cs_qhead = m_cs.size();
expr r1 = infer_type_core(t, true);
expr r2 = infer_type_core(s, true);
as_delayed_justification djst(j);
if (m_conv->is_def_eq(r1, r2, *this, djst)) {
copy_constraints(cs_qhead, new_cnstrs);
return true;
} else {
return false;
}
} }
/** \brief Return true iff \c e is a proposition */ /** \brief Return true iff \c e is a proposition */
bool type_checker::is_prop(expr const & e) { pair<bool, constraint_seq> type_checker::is_prop(expr const & e) {
scope mk_scope(*this); auto tcs = infer_type(e);
bool r = whnf(infer_type(e)) == Prop; auto wtcs = whnf(tcs.first);
if (r) mk_scope.keep(); bool r = wtcs.first == Prop;
return r; if (r)
return mk_pair(true, tcs.second + wtcs.second);
else
return mk_pair(false, constraint_seq());
} }
expr type_checker::whnf(expr const & t) { pair<expr, constraint_seq> type_checker::whnf(expr const & t) {
return m_conv->whnf(t, *this); return m_conv->whnf(t, *this);
} }
expr type_checker::whnf(expr const & t, buffer<constraint> & new_cnstrs) {
scope mk_scope(*this);
unsigned cs_qhead = m_cs.size();
expr r = m_conv->whnf(t, *this);
copy_constraints(cs_qhead, new_cnstrs);
return r;
}
void type_checker::push() {
m_infer_type_cache[0].push();
m_infer_type_cache[1].push();
m_trail.emplace_back(m_cs.size(), m_cs_qhead);
}
void type_checker::pop() {
m_infer_type_cache[0].pop();
m_infer_type_cache[1].pop();
m_cs.shrink(m_trail.back().first);
m_cs_qhead = m_trail.back().second;
m_trail.pop_back();
}
void type_checker::keep() {
m_infer_type_cache[0].keep();
m_infer_type_cache[1].keep();
m_trail.pop_back();
}
unsigned type_checker::num_scopes() const {
lean_assert(m_infer_type_cache[0].num_scopes() == m_infer_type_cache[1].num_scopes());
return m_infer_type_cache[0].num_scopes();
}
type_checker::type_checker(environment const & env, name_generator const & g, std::unique_ptr<converter> && conv, bool memoize): type_checker::type_checker(environment const & env, name_generator const & g, std::unique_ptr<converter> && conv, bool memoize):
m_env(env), m_gen(g), m_conv(std::move(conv)), m_tc_ctx(*this), m_env(env), m_gen(g), m_conv(std::move(conv)), m_tc_ctx(*this),
m_memoize(memoize), m_params(nullptr) { m_memoize(memoize), m_params(nullptr) {
m_cs_qhead = 0;
} }
static name g_tmp_prefix = name::mk_internal_unique_name(); static name g_tmp_prefix = name::mk_internal_unique_name();
@ -547,15 +460,15 @@ certified_declaration check(environment const & env, declaration const & d, name
check_name(env, d.get_name()); check_name(env, d.get_name());
check_duplicated_params(env, d); check_duplicated_params(env, d);
type_checker checker1(env, g, mk_default_converter(env, optional<module_idx>(), memoize, extra_opaque)); type_checker checker1(env, g, mk_default_converter(env, optional<module_idx>(), memoize, extra_opaque));
expr sort = checker1.check(d.get_type(), d.get_univ_params()); expr sort = checker1.check(d.get_type(), d.get_univ_params()).first;
checker1.ensure_sort(sort, d.get_type()); checker1.ensure_sort(sort, d.get_type());
if (d.is_definition()) { if (d.is_definition()) {
optional<module_idx> midx; optional<module_idx> midx;
if (d.is_opaque()) if (d.is_opaque())
midx = optional<module_idx>(d.get_module_idx()); midx = optional<module_idx>(d.get_module_idx());
type_checker checker2(env, g, mk_default_converter(env, midx, memoize, extra_opaque)); type_checker checker2(env, g, mk_default_converter(env, midx, memoize, extra_opaque));
expr val_type = checker2.check(d.get_value(), d.get_univ_params()); expr val_type = checker2.check(d.get_value(), d.get_univ_params()).first;
if (!checker2.is_def_eq(val_type, d.get_type())) { if (!checker2.is_def_eq(val_type, d.get_type()).first) {
throw_kernel_exception(env, d.get_value(), [=](formatter const & fmt) { throw_kernel_exception(env, d.get_value(), [=](formatter const & fmt) {
return pp_def_type_mismatch(fmt, d.get_name(), d.get_type(), val_type); return pp_def_type_mismatch(fmt, d.get_name(), d.get_type(), val_type);
}); });

View file

@ -10,13 +10,21 @@ Author: Leonardo de Moura
#include <algorithm> #include <algorithm>
#include "util/name_generator.h" #include "util/name_generator.h"
#include "util/name_set.h" #include "util/name_set.h"
#include "util/scoped_map.h"
#include "kernel/environment.h" #include "kernel/environment.h"
#include "kernel/constraint.h" #include "kernel/constraint.h"
#include "kernel/justification.h"
#include "kernel/converter.h" #include "kernel/converter.h"
#include "kernel/expr_maps.h"
namespace lean { namespace lean {
inline pair<expr, constraint_seq> to_ecs(expr const & e) { return mk_pair(e, empty_cs()); }
inline pair<expr, constraint_seq> to_ecs(expr const & e, constraint const & c, constraint_seq const & cs) {
return mk_pair(e, constraint_seq(constraint_seq(c), cs));
}
inline pair<expr, constraint_seq> to_ecs(expr const & e, constraint const & c) { return mk_pair(e, constraint_seq(c)); }
inline pair<expr, constraint_seq> to_ecs(expr const & e, constraint_seq const & cs) { return mk_pair(e, cs); }
/** \brief Given \c type of the form <tt>(Pi ctx, r)</tt>, return <tt>(Pi ctx, new_range)</tt> */ /** \brief Given \c type of the form <tt>(Pi ctx, r)</tt>, return <tt>(Pi ctx, new_range)</tt> */
expr replace_range(expr const & type, expr const & new_range); expr replace_range(expr const & type, expr const & new_range);
@ -56,7 +64,7 @@ expr mk_pi_for(name_generator & ngen, expr const & meta);
The type checker produces constraints, and they are sent to the constraint handler. The type checker produces constraints, and they are sent to the constraint handler.
*/ */
class type_checker { class type_checker {
typedef scoped_map<expr, expr, expr_hash, is_bi_equal_proc> cache; typedef expr_bi_struct_map<pair<expr, constraint_seq>> cache;
/** \brief Interface type_checker <-> macro & normalizer_extension */ /** \brief Interface type_checker <-> macro & normalizer_extension */
class type_checker_context : public extension_context { class type_checker_context : public extension_context {
@ -64,11 +72,12 @@ class type_checker {
public: public:
type_checker_context(type_checker & tc):m_tc(tc) {} type_checker_context(type_checker & tc):m_tc(tc) {}
virtual environment const & env() const { return m_tc.m_env; } virtual environment const & env() const { return m_tc.m_env; }
virtual expr whnf(expr const & e) { return m_tc.whnf(e); } virtual pair<expr, constraint_seq> whnf(expr const & e) { return m_tc.whnf(e); }
virtual bool is_def_eq(expr const & e1, expr const & e2, delayed_justification & j) { return m_tc.is_def_eq(e1, e2, j); } virtual pair<bool, constraint_seq> is_def_eq(expr const & e1, expr const & e2, delayed_justification & j) {
virtual expr infer_type(expr const & e) { return m_tc.infer_type(e); } return m_tc.is_def_eq(e1, e2, j);
}
virtual pair<expr, constraint_seq> infer_type(expr const & e) { return m_tc.infer_type(e); }
virtual name mk_fresh_name() { return m_tc.m_gen.next(); } virtual name mk_fresh_name() { return m_tc.m_gen.next(); }
virtual void add_cnstr(constraint const & c) { m_tc.add_cnstr(c); }
}; };
environment m_env; environment m_env;
@ -83,27 +92,24 @@ class type_checker {
bool m_memoize; bool m_memoize;
// temp flag // temp flag
level_param_names const * m_params; level_param_names const * m_params;
buffer<constraint> m_cs; // temporary cache of constraints
unsigned m_cs_qhead;
buffer<pair<unsigned, unsigned>> m_trail;
friend class converter; // allow converter to access the following methods friend class converter; // allow converter to access the following methods
name mk_fresh_name() { return m_gen.next(); } name mk_fresh_name() { return m_gen.next(); }
optional<expr> expand_macro(expr const & m); optional<expr> expand_macro(expr const & m);
pair<expr, expr> open_binding_body(expr const & e); pair<expr, expr> open_binding_body(expr const & e);
void add_cnstr(constraint const & c); pair<expr, constraint_seq> ensure_sort_core(expr e, expr const & s);
expr ensure_sort_core(expr e, expr const & s); pair<expr, constraint_seq> ensure_pi_core(expr e, expr const & s);
expr ensure_pi_core(expr e, expr const & s);
justification mk_macro_jst(expr const & e); justification mk_macro_jst(expr const & e);
void check_level(level const & l, expr const & s); void check_level(level const & l, expr const & s);
expr infer_constant(expr const & e, bool infer_only); expr infer_constant(expr const & e, bool infer_only);
expr infer_macro(expr const & e, bool infer_only); pair<expr, constraint_seq> infer_macro(expr const & e, bool infer_only);
expr infer_lambda(expr const & e, bool infer_only); pair<expr, constraint_seq> infer_lambda(expr const & e, bool infer_only);
expr infer_pi(expr const & e, bool infer_only); pair<expr, constraint_seq> infer_pi(expr const & e, bool infer_only);
expr infer_app(expr const & e, bool infer_only); pair<expr, constraint_seq> infer_app(expr const & e, bool infer_only);
expr infer_type_core(expr const & e, bool infer_only); pair<expr, constraint_seq> infer_type_core(expr const & e, bool infer_only);
expr infer_type(expr const & e); pair<expr, constraint_seq> infer_type(expr const & e);
void copy_constraints(unsigned qhead, buffer<constraint> & new_cnstrs); expr infer_type_core(expr const & e, bool infer_only, constraint_seq & cs);
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); constraint mk_eq_cnstr(expr const & lhs, expr const & rhs, justification const & j);
public: public:
@ -130,71 +136,69 @@ public:
type is correct. type is correct.
Throw an exception if a type error is found. Throw an exception if a type error is found.
The result is meaningful only if the constraints sent to the The result is meaningful only if the generated constraints can be solved.
constraint handler can be solved.
*/ */
expr infer(expr const & t) { return infer_type(t); } pair<expr, constraint_seq> infer(expr const & t) { return infer_type(t); }
/** \brief Infer \c t type and copy constraints associated with type inference to \c new_cnstrs */
expr infer(expr const & t, buffer<constraint> & new_cnstrs);
/** /**
\brief Type check the given expression, and return the type of \c t. \brief Type check the given expression, and return the type of \c t.
Throw an exception if a type error is found. Throw an exception if a type error is found.
The result is meaningful only if the constraints sent to the The result is meaningful only if the generated constraints can be solved.
constraint handler can be solved.
*/ */
expr check(expr const & t, level_param_names const & ps = level_param_names()); pair<expr, constraint_seq> check(expr const & t, level_param_names const & ps = level_param_names());
expr check(expr const & t, buffer<constraint> & new_cnstrs);
/** \brief Return true iff t is definitionally equal to s. */ /** \brief Return true iff t is definitionally equal to s. */
bool is_def_eq(expr const & t, expr const & s); pair<bool, constraint_seq> is_def_eq(expr const & t, expr const & s);
bool is_def_eq(expr const & t, expr const & s, justification const & j); pair<bool, constraint_seq> is_def_eq(expr const & t, expr const & s, justification const & j);
bool is_def_eq(expr const & t, expr const & s, delayed_justification & jst); pair<bool, constraint_seq> is_def_eq(expr const & t, expr const & s, delayed_justification & jst);
/** \brief Return true iff \c t and \c s are (may be) definitionally equal (module constraints) /** \brief Return true iff types of \c t and \c s are (may be) definitionally equal (modulo constraints) */
New constraints associated with test are store in \c new_cnstrs. pair<bool, constraint_seq> is_def_eq_types(expr const & t, expr const & s, justification const & j);
*/
bool is_def_eq(expr const & t, expr const & s, justification const & j, buffer<constraint> & new_cnstrs);
/** \brief Return true iff types of \c t and \c s are (may be) definitionally equal (modulo constraints)
New constraints associated with test are store in \c new_cnstrs.
*/
bool is_def_eq_types(expr const & t, expr const & s, justification const & j, buffer<constraint> & new_cnstrs);
/** \brief Return true iff t is a proposition. */ /** \brief Return true iff t is a proposition. */
bool is_prop(expr const & t); pair<bool, constraint_seq> is_prop(expr const & t);
/** \brief Return the weak head normal form of \c t. */ /** \brief Return the weak head normal form of \c t. */
expr whnf(expr const & t); pair<expr, constraint_seq> whnf(expr const & t);
/** \brief Similar to the previous method, but it also returns the new constraints created in the process. */
expr whnf(expr const & t, buffer<constraint> & new_cnstrs);
/** \brief Return a Pi if \c t is convertible to a Pi type. Throw an exception otherwise. /** \brief Return a Pi if \c t is convertible to a Pi type. Throw an exception otherwise.
The argument \c s is used when reporting errors */ The argument \c s is used when reporting errors */
expr ensure_pi(expr const & t, expr const & s); pair<expr, constraint_seq> ensure_pi(expr const & t, expr const & s);
expr ensure_pi(expr const & t) { return ensure_pi(t, t); } pair<expr, constraint_seq> ensure_pi(expr const & t) { return ensure_pi(t, t); }
/** \brief Mare sure type of \c e is a Pi, and return it. Throw an exception otherwise. */ /** \brief Mare sure type of \c e is a Pi, and return it. Throw an exception otherwise. */
expr ensure_fun(expr const & e) { return ensure_pi(infer(e), e); } pair<expr, constraint_seq> ensure_fun(expr const & e) {
auto tcs = infer(e);
auto pics = ensure_pi(tcs.first, e);
return mk_pair(pics.first, pics.second + tcs.second);
}
/** \brief Return a Sort if \c t is convertible to Sort. Throw an exception otherwise. /** \brief Return a Sort if \c t is convertible to Sort. Throw an exception otherwise.
The argument \c s is used when reporting errors. */ The argument \c s is used when reporting errors. */
expr ensure_sort(expr const & t, expr const & s); pair<expr, constraint_seq> ensure_sort(expr const & t, expr const & s);
/** \brief Return a Sort if \c t is convertible to Sort. Throw an exception otherwise. */ /** \brief Return a Sort if \c t is convertible to Sort. Throw an exception otherwise. */
expr ensure_sort(expr const & t) { return ensure_sort(t, t); } pair<expr, constraint_seq> ensure_sort(expr const & t) { return ensure_sort(t, t); }
/** \brief Mare sure type of \c e is a sort, and return it. Throw an exception otherwise. */ /** \brief Mare sure type of \c e is a sort, and return it. Throw an exception otherwise. */
expr ensure_type(expr const & e) { return ensure_sort(infer(e), e); } pair<expr, constraint_seq> ensure_type(expr const & e) {
auto tcs = infer(e);
auto scs = ensure_sort(tcs.first, e);
return mk_pair(scs.first, scs.second + tcs.second);
}
/** \brief Return the number of backtracking points. */ expr whnf(expr const & e, constraint_seq & cs) { auto r = whnf(e); cs += r.second; return r.first; }
unsigned num_scopes() const; expr infer(expr const & e, constraint_seq & cs) { auto r = infer(e); cs += r.second; return r.first; }
/** \brief Consume next constraint in the produced constraint queue */ expr ensure_pi(expr const & e, constraint_seq & cs) { auto r = ensure_pi(e); cs += r.second; return r.first; }
optional<constraint> next_cnstr(); expr ensure_pi(expr const & e, expr const & s, constraint_seq & cs) { auto r = ensure_pi(e, s); cs += r.second; return r.first; }
expr ensure_sort(expr const & t, expr const & s, constraint_seq & cs) { auto r = ensure_sort(t, s); cs += r.second; return r.first; }
void push(); bool is_def_eq(expr const & t, expr const & s, justification const & j, constraint_seq & cs) {
void pop(); auto r = is_def_eq(t, s, j);
void keep(); if (r.first)
cs = r.second + cs;
return r.first;
}
class scope { bool is_def_eq_types(expr const & t, expr const & s, justification const & j, constraint_seq & cs) {
type_checker & m_tc; auto r = is_def_eq_types(t, s, j);
bool m_keep; if (r.first)
public: cs = r.second + cs;
scope(type_checker & tc); return r.first;
~scope(); }
void keep();
};
}; };
typedef std::shared_ptr<type_checker> type_checker_ref; typedef std::shared_ptr<type_checker> type_checker_ref;

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, bool relax) const { constraint_seq cs, 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);
@ -55,12 +55,10 @@ class inductive_unifier_plugin_cell : public unifier_plugin_cell {
lean_assert(has_expr_metavar_strict(meta)); lean_assert(has_expr_metavar_strict(meta));
buffer<expr> margs; buffer<expr> margs;
expr const & m = get_app_args(meta, margs); expr const & m = get_app_args(meta, margs);
expr mtype = tc.infer(m, tc_cnstr_buffer); expr mtype = tc.infer(m, cs);
lean_assert(!tc.next_cnstr());
unsigned buff_sz = tc_cnstr_buffer.size();
buffer<constraints> alts; buffer<constraints> alts;
for (auto const & intro : inductive::inductive_decl_intros(decl)) { for (auto const & intro : inductive::inductive_decl_intros(decl)) {
tc_cnstr_buffer.shrink(buff_sz); constraint_seq cs_intro = cs;
name const & intro_name = inductive::intro_rule_name(intro); name const & intro_name = inductive::intro_rule_name(intro);
declaration intro_decl = env.get(intro_name); declaration intro_decl = env.get(intro_name);
levels intro_lvls; levels intro_lvls;
@ -72,33 +70,30 @@ class inductive_unifier_plugin_cell : public unifier_plugin_cell {
} }
expr intro_fn = mk_constant(inductive::intro_rule_name(intro), intro_lvls); expr intro_fn = mk_constant(inductive::intro_rule_name(intro), intro_lvls);
expr hint = intro_fn; expr hint = intro_fn;
expr intro_type = tc.whnf(inductive::intro_rule_type(intro), tc_cnstr_buffer); expr intro_type = tc.whnf(inductive::intro_rule_type(intro), cs_intro);
while (is_pi(intro_type)) { while (is_pi(intro_type)) {
hint = mk_app(hint, mk_app(mk_aux_metavar_for(ngen, mtype), margs)); hint = mk_app(hint, mk_app(mk_aux_metavar_for(ngen, mtype), margs));
intro_type = tc.whnf(binding_body(intro_type), tc_cnstr_buffer); intro_type = tc.whnf(binding_body(intro_type), cs_intro);
lean_assert(!tc.next_cnstr());
} }
constraint c1 = mk_eq_cnstr(meta, hint, j, relax); constraint c1 = mk_eq_cnstr(meta, hint, j, relax);
args[major_idx] = hint; args[major_idx] = hint;
lean_assert(!tc.next_cnstr()); expr reduce_elim = tc.whnf(mk_app(elim, args), cs_intro);
expr reduce_elim = tc.whnf(mk_app(elim, args), tc_cnstr_buffer);
lean_assert(!tc.next_cnstr());
constraint c2 = mk_eq_cnstr(reduce_elim, t, j, relax); constraint c2 = mk_eq_cnstr(reduce_elim, t, j, relax);
list<constraint> tc_cnstrs = to_list(tc_cnstr_buffer.begin(), tc_cnstr_buffer.end()); cs_intro = constraint_seq(c1) + constraint_seq(c2) + cs_intro;
alts.push_back(cons(c1, cons(c2, tc_cnstrs))); buffer<constraint> cs_buffer;
cs_intro.linearize(cs_buffer);
alts.push_back(to_list(cs_buffer.begin(), cs_buffer.end()));
} }
lean_assert(!tc.next_cnstr());
return to_lazy(to_list(alts.begin(), alts.end())); return to_lazy(to_list(alts.begin(), alts.end()));
} }
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, bool relax) 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; auto dcs = tc.is_def_eq_types(lhs, rhs, j);
lean_assert(!tc.next_cnstr()); if (!dcs.first)
if (!tc.is_def_eq_types(lhs, rhs, j, tc_cnstr_buffer))
return lazy_list<constraints>(); return lazy_list<constraints>();
lean_assert(!tc.next_cnstr()); constraint_seq cs = dcs.second;
buffer<expr> args; buffer<expr> args;
expr const & elim = get_app_args(lhs, args); expr const & elim = get_app_args(lhs, args);
environment const & env = tc.env(); environment const & env = tc.env();
@ -106,7 +101,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, relax); return add_elim_meta_cnstrs(tc, ngen, d, elim, args, rhs, j, cs, relax);
} }
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
} }

View file

@ -951,6 +951,19 @@ static void open_formatter(lua_State * L) {
SET_GLOBAL_FUN(set_formatter_factory, "set_formatter_factory"); SET_GLOBAL_FUN(set_formatter_factory, "set_formatter_factory");
} }
// Helper function for push pair expr, constraint_seq
int push_ecs(lua_State * L, pair<expr, constraint_seq> const & p) {
push_expr(L, p.first);
push_constraint_seq(L, p.second);
return 2;
}
int push_bcs(lua_State * L, pair<bool, constraint_seq> const & p) {
push_boolean(L, p.first);
push_constraint_seq(L, p.second);
return 2;
}
// Environment_id // Environment_id
DECL_UDATA(environment_id) DECL_UDATA(environment_id)
static int environment_id_descendant(lua_State * L) { return push_boolean(L, to_environment_id(L, 1).is_descendant(to_environment_id(L, 2))); } static int environment_id_descendant(lua_State * L) { return push_boolean(L, to_environment_id(L, 1).is_descendant(to_environment_id(L, 2))); }
@ -1032,10 +1045,10 @@ static int mk_environment(lua_State * L) { return push_environment(L, mk_en
static int mk_hott_environment(lua_State * L) { return push_environment(L, mk_hott_environment(get_trust_lvl(L, 1))); } static int mk_hott_environment(lua_State * L) { return push_environment(L, mk_hott_environment(get_trust_lvl(L, 1))); }
static int environment_forget(lua_State * L) { return push_environment(L, to_environment(L, 1).forget()); } static int environment_forget(lua_State * L) { return push_environment(L, to_environment(L, 1).forget()); }
static int environment_whnf(lua_State * L) { return push_expr(L, type_checker(to_environment(L, 1)).whnf(to_expr(L, 2))); } static int environment_whnf(lua_State * L) { return push_ecs(L, type_checker(to_environment(L, 1)).whnf(to_expr(L, 2))); }
static int environment_normalize(lua_State * L) { return push_expr(L, normalize(to_environment(L, 1), to_expr(L, 2))); } static int environment_normalize(lua_State * L) { return push_expr(L, normalize(to_environment(L, 1), to_expr(L, 2))); }
static int environment_infer_type(lua_State * L) { return push_expr(L, type_checker(to_environment(L, 1)).infer(to_expr(L, 2))); } static int environment_infer_type(lua_State * L) { return push_ecs(L, type_checker(to_environment(L, 1)).infer(to_expr(L, 2))); }
static int environment_type_check(lua_State * L) { return push_expr(L, type_checker(to_environment(L, 1)).check(to_expr(L, 2))); } static int environment_type_check(lua_State * L) { return push_ecs(L, type_checker(to_environment(L, 1)).check(to_expr(L, 2))); }
static int environment_for_each_decl(lua_State * L) { static int environment_for_each_decl(lua_State * L) {
environment const & env = to_environment(L, 1); environment const & env = to_environment(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION); // user-fun luaL_checktype(L, 2, LUA_TFUNCTION); // user-fun
@ -1577,12 +1590,57 @@ static const struct luaL_Reg constraint_m[] = {
{0, 0} {0, 0}
}; };
// Constraint sequences
DECL_UDATA(constraint_seq)
static int constraint_seq_mk(lua_State * L) {
unsigned nargs = lua_gettop(L);
constraint_seq cs;
for (unsigned i = 0; i < nargs; i++) {
cs += to_constraint(L, i);
}
return push_constraint_seq(L, cs);
}
static int constraint_seq_concat(lua_State * L) {
if (is_constraint_seq(L, 1) && is_constraint(L, 2))
return push_constraint_seq(L, to_constraint_seq(L, 1) + to_constraint(L, 2));
else
return push_constraint_seq(L, to_constraint_seq(L, 1) + to_constraint_seq(L, 2));
}
static int constraint_seq_linearize(lua_State * L) {
buffer<constraint> tmp;
to_constraint_seq(L, 1).linearize(tmp);
lua_newtable(L);
int i = 1;
for (constraint const & c : tmp) {
push_constraint(L, c);
lua_rawseti(L, -2, i);
i++;
}
return 1;
}
static const struct luaL_Reg constraint_seq_m[] = {
{"__gc", constraint_seq_gc},
{"__concat", constraint_seq_concat},
{"concat", constraint_seq_concat},
{"linearize", constraint_seq_linearize},
{0, 0}
};
static void open_constraint(lua_State * L) { static void open_constraint(lua_State * L) {
luaL_newmetatable(L, constraint_mt); luaL_newmetatable(L, constraint_mt);
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "__index");
setfuncs(L, constraint_m, 0); setfuncs(L, constraint_m, 0);
luaL_newmetatable(L, constraint_seq_mt);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
setfuncs(L, constraint_seq_m, 0);
SET_GLOBAL_FUN(constraint_pred, "is_constraint"); SET_GLOBAL_FUN(constraint_pred, "is_constraint");
SET_GLOBAL_FUN(mk_eq_cnstr, "mk_eq_cnstr"); SET_GLOBAL_FUN(mk_eq_cnstr, "mk_eq_cnstr");
SET_GLOBAL_FUN(mk_level_eq_cnstr, "mk_level_eq_cnstr"); SET_GLOBAL_FUN(mk_level_eq_cnstr, "mk_level_eq_cnstr");
@ -1593,6 +1651,9 @@ static void open_constraint(lua_State * L) {
SET_ENUM("LevelEq", constraint_kind::LevelEq); SET_ENUM("LevelEq", constraint_kind::LevelEq);
SET_ENUM("Choice", constraint_kind::Choice); SET_ENUM("Choice", constraint_kind::Choice);
lua_setglobal(L, "constraint_kind"); lua_setglobal(L, "constraint_kind");
SET_GLOBAL_FUN(constraint_seq_pred, "is_constraint_seq");
SET_GLOBAL_FUN(constraint_seq_mk, "constraint_seq");
} }
// Substitution // Substitution
@ -1787,44 +1848,30 @@ static int mk_type_checker(lua_State * L) {
return push_type_checker_ref(L, t); return push_type_checker_ref(L, t);
} }
} }
static int type_checker_whnf(lua_State * L) { return push_expr(L, to_type_checker_ref(L, 1)->whnf(to_expr(L, 2))); } static int type_checker_whnf(lua_State * L) { return push_ecs(L, to_type_checker_ref(L, 1)->whnf(to_expr(L, 2))); }
static int type_checker_ensure_pi(lua_State * L) { static int type_checker_ensure_pi(lua_State * L) {
if (lua_gettop(L) == 2) if (lua_gettop(L) == 2)
return push_expr(L, to_type_checker_ref(L, 1)->ensure_pi(to_expr(L, 2))); return push_ecs(L, to_type_checker_ref(L, 1)->ensure_pi(to_expr(L, 2)));
else else
return push_expr(L, to_type_checker_ref(L, 1)->ensure_pi(to_expr(L, 2), to_expr(L, 3))); return push_ecs(L, to_type_checker_ref(L, 1)->ensure_pi(to_expr(L, 2), to_expr(L, 3)));
} }
static int type_checker_ensure_sort(lua_State * L) { static int type_checker_ensure_sort(lua_State * L) {
if (lua_gettop(L) == 2) if (lua_gettop(L) == 2)
return push_expr(L, to_type_checker_ref(L, 1)->ensure_sort(to_expr(L, 2))); return push_ecs(L, to_type_checker_ref(L, 1)->ensure_sort(to_expr(L, 2)));
else else
return push_expr(L, to_type_checker_ref(L, 1)->ensure_sort(to_expr(L, 2), to_expr(L, 3))); return push_ecs(L, to_type_checker_ref(L, 1)->ensure_sort(to_expr(L, 2), to_expr(L, 3)));
} }
static int type_checker_check(lua_State * L) { static int type_checker_check(lua_State * L) {
int nargs = lua_gettop(L); int nargs = lua_gettop(L);
if (nargs <= 2) if (nargs <= 2)
return push_expr(L, to_type_checker_ref(L, 1)->check(to_expr(L, 2), level_param_names())); return push_ecs(L, to_type_checker_ref(L, 1)->check(to_expr(L, 2), level_param_names()));
else else
return push_expr(L, to_type_checker_ref(L, 1)->check(to_expr(L, 2), to_level_param_names(L, 3))); return push_ecs(L, to_type_checker_ref(L, 1)->check(to_expr(L, 2), to_level_param_names(L, 3)));
} }
static int type_checker_infer(lua_State * L) { return push_expr(L, to_type_checker_ref(L, 1)->infer(to_expr(L, 2))); } static int type_checker_infer(lua_State * L) { return push_ecs(L, to_type_checker_ref(L, 1)->infer(to_expr(L, 2))); }
static int type_checker_is_def_eq(lua_State * L) { return push_boolean(L, to_type_checker_ref(L, 1)->is_def_eq(to_expr(L, 2), to_expr(L, 3))); }
static int type_checker_is_prop(lua_State * L) { return push_boolean(L, to_type_checker_ref(L, 1)->is_prop(to_expr(L, 2))); } static int type_checker_is_def_eq(lua_State * L) { return push_bcs(L, to_type_checker_ref(L, 1)->is_def_eq(to_expr(L, 2), to_expr(L, 3))); }
static int type_checker_push(lua_State * L) { to_type_checker_ref(L, 1)->push(); return 0; } static int type_checker_is_prop(lua_State * L) { return push_bcs(L, to_type_checker_ref(L, 1)->is_prop(to_expr(L, 2))); }
static int type_checker_pop(lua_State * L) {
if (to_type_checker_ref(L, 1)->num_scopes() == 0)
throw exception("invalid pop method, type_checker does not have backtracking points");
to_type_checker_ref(L, 1)->pop();
return 0;
}
static int type_checker_keep(lua_State * L) {
if (to_type_checker_ref(L, 1)->num_scopes() == 0)
throw exception("invalid pop method, type_checker does not have backtracking points");
to_type_checker_ref(L, 1)->keep();
return 0;
}
static int type_checker_num_scopes(lua_State * L) { return push_integer(L, to_type_checker_ref(L, 1)->num_scopes()); }
static int type_checker_next_cnstr(lua_State * L) { return push_optional_constraint(L, to_type_checker_ref(L, 1)->next_cnstr()); }
static name g_tmp_prefix = name::mk_internal_unique_name(); static name g_tmp_prefix = name::mk_internal_unique_name();
@ -1851,11 +1898,6 @@ static const struct luaL_Reg type_checker_ref_m[] = {
{"infer", safe_function<type_checker_infer>}, {"infer", safe_function<type_checker_infer>},
{"is_def_eq", safe_function<type_checker_is_def_eq>}, {"is_def_eq", safe_function<type_checker_is_def_eq>},
{"is_prop", safe_function<type_checker_is_prop>}, {"is_prop", safe_function<type_checker_is_prop>},
{"push", safe_function<type_checker_push>},
{"pop", safe_function<type_checker_pop>},
{"keep", safe_function<type_checker_keep>},
{"next_cnstr", safe_function<type_checker_next_cnstr>},
{"num_scopes", safe_function<type_checker_num_scopes>},
{0, 0} {0, 0}
}; };

View file

@ -20,6 +20,7 @@ UDATA_DEFS(environment)
UDATA_DEFS(substitution) UDATA_DEFS(substitution)
UDATA_DEFS(justification) UDATA_DEFS(justification)
UDATA_DEFS(constraint) UDATA_DEFS(constraint)
UDATA_DEFS_CORE(constraint_seq)
UDATA_DEFS(substitution) UDATA_DEFS(substitution)
UDATA_DEFS(io_state) UDATA_DEFS(io_state)
UDATA_DEFS_CORE(type_checker_ref) UDATA_DEFS_CORE(type_checker_ref)

View file

@ -347,10 +347,10 @@ bool match(expr const & p, expr const & t, buffer<optional<expr>> & esubst, buff
match_plugin mk_whnf_match_plugin(std::shared_ptr<type_checker> const & tc) { match_plugin mk_whnf_match_plugin(std::shared_ptr<type_checker> const & tc) {
return [=](expr const & p, expr const & t, match_context & ctx) { // NOLINT return [=](expr const & p, expr const & t, match_context & ctx) { // NOLINT
try { try {
buffer<constraint> cs; constraint_seq cs;
expr p1 = tc->whnf(p, cs); expr p1 = tc->whnf(p, cs);
expr t1 = tc->whnf(t, cs); expr t1 = tc->whnf(t, cs);
return cs.empty() && (p1 != p || t1 != t) && ctx.match(p1, t1); return !cs && (p1 != p || t1 != t) && ctx.match(p1, t1);
} catch (exception&) { } catch (exception&) {
return false; return false;
} }

View file

@ -32,7 +32,7 @@ class normalize_fn {
} }
expr normalize(expr e) { expr normalize(expr e) {
e = m_tc.whnf(e); e = m_tc.whnf(e).first;
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Var: case expr_kind::Constant: case expr_kind::Sort: case expr_kind::Var: case expr_kind::Constant: case expr_kind::Sort:
case expr_kind::Meta: case expr_kind::Local: case expr_kind::Macro: case expr_kind::Meta: case expr_kind::Local: case expr_kind::Macro:

View file

@ -20,11 +20,11 @@ bool has_num_decls(environment const & env) {
try { try {
type_checker tc(env); type_checker tc(env);
return return
tc.infer(g_zero) == g_num && tc.infer(g_zero).first == g_num &&
tc.infer(g_pos) == mk_arrow(g_pos_num, g_num) && tc.infer(g_pos).first == mk_arrow(g_pos_num, g_num) &&
tc.infer(g_one) == g_pos_num && tc.infer(g_one).first == g_pos_num &&
tc.infer(g_bit0) == mk_arrow(g_pos_num, g_pos_num) && tc.infer(g_bit0).first == mk_arrow(g_pos_num, g_pos_num) &&
tc.infer(g_bit1) == mk_arrow(g_pos_num, g_pos_num); tc.infer(g_bit1).first == mk_arrow(g_pos_num, g_pos_num);
} catch (...) { } catch (...) {
return false; return false;
} }

View file

@ -84,7 +84,24 @@ public:
// Begin of resolve_macro get_type implementation // Begin of resolve_macro get_type implementation
// This section of code is trusted when the environment has trust_level == 1 // This section of code is trusted when the environment has trust_level == 1
bool is_def_eq(expr const & l1, expr const & l2, extension_context & ctx) const { return ctx.is_def_eq(l1, l2, jst()); } bool is_def_eq(expr const & l1, expr const & l2, extension_context & ctx) const {
auto r = ctx.is_def_eq(l1, l2, jst());
return r.first && !r.second;
}
expr whnf(expr const & e, extension_context & ctx) const {
auto r = ctx.whnf(e);
if (r.second)
throw_kernel_exception(ctx.env(), "invalid resolve macro, constraints were generated while computing whnf", e);
return r.first;
}
expr infer_type(expr const & e, extension_context & ctx) const {
auto r = ctx.infer_type(e);
if (r.second)
throw_kernel_exception(ctx.env(), "invalid resolve macro, constraints were generated while inferring type", e);
return r.first;
}
/** \brief Return true if \c ls already contains a literal that is definitionally equal to \c l */ /** \brief Return true if \c ls already contains a literal that is definitionally equal to \c l */
bool already_contains(expr const & l, buffer<expr> const & ls, extension_context & ctx) const { bool already_contains(expr const & l, buffer<expr> const & ls, extension_context & ctx) const {
@ -109,7 +126,7 @@ public:
if (is_or(cls, lhs, rhs)) { if (is_or(cls, lhs, rhs)) {
return collect(lhs, rhs, l, R, ctx); return collect(lhs, rhs, l, R, ctx);
} else { } else {
cls = ctx.whnf(cls); cls = whnf(cls, ctx);
if (is_or(cls, lhs, rhs)) { if (is_or(cls, lhs, rhs)) {
return collect(lhs, rhs, l, R, ctx); return collect(lhs, rhs, l, R, ctx);
} else if (is_def_eq(cls, l, ctx)) { } else if (is_def_eq(cls, l, ctx)) {
@ -125,8 +142,8 @@ public:
virtual expr get_type(expr const & m, expr const * arg_types, extension_context & ctx) const { virtual expr get_type(expr const & m, expr const * arg_types, extension_context & ctx) const {
environment const & env = ctx.env(); environment const & env = ctx.env();
check_num_args(env, m); check_num_args(env, m);
expr l = ctx.whnf(macro_arg(m, 0)); expr l = whnf(macro_arg(m, 0), ctx);
expr not_l = ctx.whnf(g_not(l)); expr not_l = whnf(g_not(l), ctx);
expr C1 = arg_types[1]; expr C1 = arg_types[1];
expr C2 = arg_types[2]; expr C2 = arg_types[2];
buffer<expr> R; // resolvent buffer<expr> R; // resolvent
@ -148,12 +165,12 @@ public:
virtual optional<expr> expand(expr const & m, extension_context & ctx) const { virtual optional<expr> expand(expr const & m, extension_context & ctx) const {
environment const & env = ctx.env(); environment const & env = ctx.env();
check_num_args(env, m); check_num_args(env, m);
expr l = ctx.whnf(macro_arg(m, 0)); expr l = whnf(macro_arg(m, 0), ctx);
expr not_l = ctx.whnf(g_not(l)); expr not_l = whnf(g_not(l), ctx);
expr H1 = macro_arg(m, 1); expr H1 = macro_arg(m, 1);
expr H2 = macro_arg(m, 2); expr H2 = macro_arg(m, 2);
expr C1 = ctx.infer_type(H1); expr C1 = infer_type(H1, ctx);
expr C2 = ctx.infer_type(H2); expr C2 = infer_type(H2, ctx);
expr arg_types[3] = { expr() /* get_type() does not use first argument */, C1, C2 }; expr arg_types[3] = { expr() /* get_type() does not use first argument */, C1, C2 };
expr R = get_type(m, arg_types, ctx); expr R = get_type(m, arg_types, ctx);
return some_expr(mk_or_elim_tree1(l, not_l, C1, H1, C2, H2, R, ctx)); return some_expr(mk_or_elim_tree1(l, not_l, C1, H1, C2, H2, R, ctx));
@ -198,7 +215,7 @@ public:
if (is_or(C1, lhs, rhs)) { if (is_or(C1, lhs, rhs)) {
return mk_or_elim_tree1(l, not_l, lhs, rhs, H1, C2, H2, R, ctx); return mk_or_elim_tree1(l, not_l, lhs, rhs, H1, C2, H2, R, ctx);
} else { } else {
C1 = ctx.whnf(C1); C1 = whnf(C1, ctx);
if (is_or(C1, lhs, rhs)) { if (is_or(C1, lhs, rhs)) {
return mk_or_elim_tree1(l, not_l, lhs, rhs, H1, C2, H2, R, ctx); return mk_or_elim_tree1(l, not_l, lhs, rhs, H1, C2, H2, R, ctx);
} else if (is_def_eq(C1, l, ctx)) { } else if (is_def_eq(C1, l, ctx)) {
@ -243,7 +260,7 @@ public:
if (is_or(C2, lhs, rhs)) { if (is_or(C2, lhs, rhs)) {
return mk_or_elim_tree2(l, H, not_l, lhs, rhs, H2, R, ctx); return mk_or_elim_tree2(l, H, not_l, lhs, rhs, H2, R, ctx);
} else { } else {
C2 = ctx.whnf(C2); C2 = whnf(C2, ctx);
if (is_or(C2, lhs, rhs)) { if (is_or(C2, lhs, rhs)) {
return mk_or_elim_tree2(l, H, not_l, lhs, rhs, H2, R, ctx); return mk_or_elim_tree2(l, H, not_l, lhs, rhs, H2, R, ctx);
} else if (is_def_eq(C2, not_l, ctx)) { } else if (is_def_eq(C2, not_l, ctx)) {

View file

@ -101,11 +101,11 @@ bool has_string_decls(environment const & env) {
try { try {
type_checker tc(env); type_checker tc(env);
return return
tc.infer(g_ff) == g_bool && tc.infer(g_ff).first == g_bool &&
tc.infer(g_tt) == g_bool && tc.infer(g_tt).first == g_bool &&
tc.infer(g_ascii) == g_bool >> (g_bool >> (g_bool >> (g_bool >> (g_bool >> (g_bool >> (g_bool >> (g_bool >> g_char))))))) && tc.infer(g_ascii).first == g_bool >> (g_bool >> (g_bool >> (g_bool >> (g_bool >> (g_bool >> (g_bool >> (g_bool >> g_char))))))) &&
tc.infer(g_empty) == g_string && tc.infer(g_empty).first == g_string &&
tc.infer(g_str) == g_char >> (g_string >> g_string); tc.infer(g_str).first == g_char >> (g_string >> g_string);
} catch (...) { } catch (...) {
return false; return false;
} }

View file

@ -44,7 +44,7 @@ bool collect_simple_metas(expr const & e, buffer<expr> & result) {
unsigned get_expect_num_args(type_checker & tc, expr e) { unsigned get_expect_num_args(type_checker & tc, expr e) {
unsigned r = 0; unsigned r = 0;
while (true) { while (true) {
e = tc.whnf(e); e = tc.whnf(e).first;
if (!is_pi(e)) if (!is_pi(e))
return r; return r;
e = binding_body(e); e = binding_body(e);
@ -93,6 +93,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 relax_main_opaque) { bool add_meta, bool add_subgoals, bool relax_main_opaque) {
// TODO(Leo): we are ignoring constraints produces by type checker
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();
@ -102,7 +103,7 @@ proof_state_seq apply_tactic_core(environment const & env, io_state const & ios,
goals tail_gs = tail(gs); goals tail_gs = tail(gs);
expr t = g.get_type(); expr t = g.get_type();
expr e = _e; expr e = _e;
expr e_t = tc.infer(e); expr e_t = tc.infer(e).first;
buffer<expr> metas; buffer<expr> metas;
collect_simple_meta(e, metas); collect_simple_meta(e, metas);
if (add_meta) { if (add_meta) {
@ -111,7 +112,7 @@ proof_state_seq apply_tactic_core(environment const & env, io_state const & ios,
if (num_t > num_e_t) if (num_t > num_e_t)
return proof_state_seq(); // no hope to unify then return proof_state_seq(); // no hope to unify then
for (unsigned i = 0; i < num_e_t - num_t; i++) { for (unsigned i = 0; i < num_e_t - num_t; i++) {
e_t = tc.whnf(e_t); e_t = tc.whnf(e_t).first;
expr meta = g.mk_meta(ngen.next(), binding_domain(e_t)); expr meta = g.mk_meta(ngen.next(), binding_domain(e_t));
e = mk_app(e, meta); e = mk_app(e, meta);
e_t = instantiate(binding_body(e_t), meta); e_t = instantiate(binding_body(e_t), meta);
@ -139,7 +140,7 @@ proof_state_seq apply_tactic_core(environment const & env, io_state const & ios,
unsigned i = metas.size(); unsigned i = metas.size();
while (i > 0) { while (i > 0) {
--i; --i;
new_gs = cons(goal(metas[i], new_subst.instantiate_all(tc.infer(metas[i]))), new_gs); new_gs = cons(goal(metas[i], new_subst.instantiate_all(tc.infer(metas[i]).first)), new_gs);
} }
} }
return proof_state(new_gs, new_subst, new_ngen); return proof_state(new_gs, new_subst, new_ngen);

View file

@ -45,10 +45,10 @@ bool has_tactic_decls(environment const & env) {
try { try {
type_checker tc(env); type_checker tc(env);
return return
tc.infer(g_builtin_tac) == g_tac_type && tc.infer(g_builtin_tac).first == g_tac_type &&
tc.infer(g_and_then_tac_fn) == g_tac_type >> (g_tac_type >> g_tac_type) && tc.infer(g_and_then_tac_fn).first == g_tac_type >> (g_tac_type >> g_tac_type) &&
tc.infer(g_or_else_tac_fn) == g_tac_type >> (g_tac_type >> g_tac_type) && tc.infer(g_or_else_tac_fn).first == g_tac_type >> (g_tac_type >> g_tac_type) &&
tc.infer(g_repeat_tac_fn) == g_tac_type >> g_tac_type; tc.infer(g_repeat_tac_fn).first == g_tac_type >> g_tac_type;
} catch (...) { } catch (...) {
return false; return false;
} }
@ -69,7 +69,7 @@ static bool is_builtin_tactic(expr const & v) {
} }
tactic expr_to_tactic(type_checker & tc, expr e, pos_info_provider const * p) { tactic expr_to_tactic(type_checker & tc, expr e, pos_info_provider const * p) {
e = tc.whnf(e); e = tc.whnf(e).first;
expr f = get_app_fn(e); expr f = get_app_fn(e);
if (!is_constant(f)) if (!is_constant(f))
throw_failed(e); throw_failed(e);
@ -165,7 +165,7 @@ register_unary_num_tac::register_unary_num_tac(name const & n, std::function<tac
tactic t = expr_to_tactic(tc, args[0], p); tactic t = expr_to_tactic(tc, args[0], p);
optional<mpz> k = to_num(args[1]); optional<mpz> k = to_num(args[1]);
if (!k) if (!k)
k = to_num(tc.whnf(args[1])); k = to_num(tc.whnf(args[1]).first);
if (!k) if (!k)
throw expr_to_tactic_exception(e, "invalid tactic, second argument must be a numeral"); throw expr_to_tactic_exception(e, "invalid tactic, second argument must be a numeral");
if (!k->is_unsigned_int()) if (!k->is_unsigned_int())
@ -199,7 +199,7 @@ static register_tac reg_trace(name(g_tac, "trace"), [](type_checker & tc, expr c
throw expr_to_tactic_exception(e, "invalid trace tactic, argument expected"); throw expr_to_tactic_exception(e, "invalid trace tactic, argument expected");
if (auto str = to_string(args[0])) if (auto str = to_string(args[0]))
return trace_tactic(*str); return trace_tactic(*str);
else if (auto str = to_string(tc.whnf(args[0]))) else if (auto str = to_string(tc.whnf(args[0]).first))
return trace_tactic(*str); return trace_tactic(*str);
else else
throw expr_to_tactic_exception(e, "invalid trace tactic, string value expected"); throw expr_to_tactic_exception(e, "invalid trace tactic, string value expected");
@ -224,7 +224,7 @@ static register_unary_num_tac reg_try_for(name(g_tac, "try_for"), [](tactic cons
static register_tac reg_fixpoint(g_fixpoint_name, [](type_checker & tc, expr const & e, pos_info_provider const *) { static register_tac reg_fixpoint(g_fixpoint_name, [](type_checker & tc, expr const & e, pos_info_provider const *) {
if (!is_constant(app_fn(e))) if (!is_constant(app_fn(e)))
throw expr_to_tactic_exception(e, "invalid fixpoint tactic, it must have one argument"); throw expr_to_tactic_exception(e, "invalid fixpoint tactic, it must have one argument");
expr r = tc.whnf(mk_app(app_arg(e), e)); expr r = tc.whnf(mk_app(app_arg(e), e)).first;
return fixpoint(r); return fixpoint(r);
}); });

View file

@ -86,7 +86,7 @@ bool goal::validate_locals() const {
bool goal::validate(environment const & env) const { bool goal::validate(environment const & env) const {
if (validate_locals()) { if (validate_locals()) {
type_checker tc(env); type_checker tc(env);
return tc.is_def_eq(tc.check(m_meta), m_type); return tc.is_def_eq(tc.check(m_meta).first, m_type).first;
} else { } else {
return false; return false;
} }

View file

@ -10,6 +10,7 @@ Author: Leonardo de Moura
#include "util/lua.h" #include "util/lua.h"
#include "util/optional.h" #include "util/optional.h"
#include "util/name_set.h" #include "util/name_set.h"
#include "kernel/metavar.h"
#include "library/tactic/goal.h" #include "library/tactic/goal.h"
namespace lean { namespace lean {

View file

@ -248,9 +248,11 @@ tactic exact_tactic(expr const & _e) {
goals const & gs = s.get_goals(); goals const & gs = s.get_goals();
goal const & g = head(gs); goal const & g = head(gs);
expr e = subst.instantiate(_e); expr e = subst.instantiate(_e);
expr e_t = subst.instantiate(tc.infer(e)); auto e_t_cs = tc.infer(e);
expr e_t = subst.instantiate(e_t_cs.first);
expr t = subst.instantiate(g.get_type()); expr t = subst.instantiate(g.get_type());
if (tc.is_def_eq(e_t, t) && !tc.next_cnstr()) { auto dcs = tc.is_def_eq(e_t, t);
if (dcs.first && !dcs.second && !e_t_cs.second) {
expr new_p = g.abstract(e); expr new_p = g.abstract(e);
check_has_no_local(new_p, _e, "exact"); check_has_no_local(new_p, _e, "exact");
subst.assign(g.get_name(), new_p); subst.assign(g.get_name(), new_p);

View file

@ -310,17 +310,11 @@ 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_owned_map(u.m_owned_map), m_pattern(u.m_pattern) { m_mvar_occs(u.m_mvar_occs), m_owned_map(u.m_owned_map), m_pattern(u.m_pattern) {
u.m_next_assumption_idx++; u.m_next_assumption_idx++;
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[0]->pop(); // restore type checker state
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;
@ -438,54 +432,76 @@ struct unifier_fn {
\remark If relax is true then opaque definitions from the main module are treated as transparent. \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) { 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)) { auto dcs = m_tc[relax]->is_def_eq(t1, t2, j);
return true; if (!dcs.first) {
} else {
// std::cout << "conflict: " << t1 << " =?= " << t2 << "\n"; // std::cout << "conflict: " << t1 << " =?= " << t2 << "\n";
set_conflict(j); set_conflict(j);
return false; return false;
} else {
return process_constraints(dcs.second);
} }
} }
/** \brief Process the given constraints. Return true iff no conflict was detected. */
bool process_constraints(constraint_seq const & cs) {
return cs.all_of([&](constraint const & c) { return process_constraint(c); });
}
bool process_constraints(buffer<constraint> const & cs) {
for (auto const & c : cs) {
if (!process_constraint(c))
return false;
}
return true;
}
/** \brief Process constraints in \c cs, and append justification \c j to them. */
bool process_constraints(constraint_seq const & cs, justification const & j) {
return cs.all_of([&](constraint const & c) {
return process_constraint(update_justification(c, mk_composite1(c.get_justification(), j)));
});
}
template<typename Constraints>
bool process_constraints(Constraints const & cs, justification const & j) {
for (auto const & c : cs) {
if (!process_constraint(update_justification(c, mk_composite1(c.get_justification(), j))))
return false;
}
return true;
}
/** \brief Put \c e in weak head normal form. /** \brief Put \c e in weak head normal form.
\remark If relax is true then opaque definitions from the main module are treated as transparent. \remark If relax is true then opaque definitions from the main module are treated as transparent.
\remark Constraints generated in the process are stored in \c cs. The justification \c j is composed with them. \remark Constraints generated in the process are stored in \c cs.
*/ */
expr whnf(expr const & e, justification const & j, bool relax, buffer<constraint> & cs) { expr whnf(expr const & e, bool relax, constraint_seq & cs) {
unsigned cs_sz = cs.size(); return m_tc[relax]->whnf(e, cs);
expr r = m_tc[relax]->whnf(e, cs);
for (unsigned i = cs_sz; i < cs.size(); i++)
cs[i] = update_justification(cs[i], mk_composite1(j, cs[i].get_justification()));
return r;
}
/** \brief Process the given constraints. Return true iff no conflict was detected. */
bool process_constraints(buffer<constraint> & cs) {
for (auto const & c : cs)
if (!process_constraint(c))
return false;
return true;
} }
/** \brief Infer \c e type. /** \brief Infer \c e type.
\remark Return none if an exception was throw when inferring the type. \remark Return none if an exception was throw when inferring the type.
\remark If relax is true then opaque definitions from the main module are treated as transparent. \remark If relax is true then opaque definitions from the main module are treated as transparent.
\remark Constraints generated in the process are stored in \c cs. The justification \c j is composed with them. \remark Constraints generated in the process are stored in \c cs.
*/ */
optional<expr> infer(expr const & e, justification const & j, bool relax, buffer<constraint> & cs) { optional<expr> infer(expr const & e, bool relax, constraint_seq & cs) {
try { try {
unsigned cs_sz = cs.size(); return some_expr(m_tc[relax]->infer(e, cs));
expr r = m_tc[relax]->infer(e, cs);
for (unsigned i = cs_sz; i < cs.size(); i++)
cs[i] = update_justification(cs[i], mk_composite1(j, cs[i].get_justification()));
return some_expr(r);
} catch (exception &) { } catch (exception &) {
return none_expr(); return none_expr();
} }
} }
expr whnf(expr const & e, justification const & j, bool relax, buffer<constraint> & cs) {
constraint_seq _cs;
expr r = whnf(e, relax, _cs);
to_buffer(_cs, j, cs);
return r;
}
justification mk_assign_justification(expr const & m, expr const & m_type, expr const & v_type, justification const & j) { justification mk_assign_justification(expr const & m, expr const & m_type, expr const & v_type, justification const & j) {
auto r = j.get_main_expr(); auto r = j.get_main_expr();
if (!r) r = m; if (!r) r = m;
@ -524,28 +540,11 @@ struct unifier_fn {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(!in_conflict()); lean_assert(!in_conflict());
m_subst.assign(m, v, j); m_subst.assign(m, v, j);
#if 0 constraint_seq cs;
expr m_type = mlocal_type(m); auto lhs_type = infer(lhs, relax, cs);
expr v_type; auto rhs_type = infer(rhs, relax, cs);
buffer<constraint> cs;
if (auto type = infer(v, j, relax, cs)) {
v_type = *type;
if (!process_constraints(cs))
return false;
} else {
set_conflict(j);
return false;
}
lean_assert(!in_conflict());
justification new_j = mk_assign_justification(m, m_type, v_type, j);
if (!is_def_eq(m_type, v_type, new_j, relax))
return false;
#else
buffer<constraint> cs;
auto lhs_type = infer(lhs, j, relax, cs);
auto rhs_type = infer(rhs, j, relax, cs);
if (lhs_type && rhs_type) { if (lhs_type && rhs_type) {
if (!process_constraints(cs)) if (!process_constraints(cs, j))
return false; return false;
justification new_j = mk_assign_justification(m, *lhs_type, *rhs_type, j); justification new_j = mk_assign_justification(m, *lhs_type, *rhs_type, j);
if (!is_def_eq(*lhs_type, *rhs_type, new_j, relax)) if (!is_def_eq(*lhs_type, *rhs_type, new_j, relax))
@ -554,7 +553,6 @@ struct unifier_fn {
set_conflict(j); set_conflict(j);
return false; return false;
} }
#endif
auto it = m_mvar_occs.find(mlocal_name(m)); auto it = m_mvar_occs.find(mlocal_name(m));
if (it) { if (it) {
cnstr_idx_set s = *it; cnstr_idx_set s = *it;
@ -943,8 +941,6 @@ struct unifier_fn {
} }
void pop_case_split() { void pop_case_split() {
m_tc[0]->pop();
m_tc[1]->pop();
m_case_splits.pop_back(); m_case_splits.pop_back();
} }
@ -974,13 +970,6 @@ struct unifier_fn {
return optional<substitution>(); return optional<substitution>();
} }
/** \brief Process constraints in \c cs, and append justification \c j to them. */
bool process_constraints(constraints const & cs, justification const & j) {
for (constraint const & c : cs)
process_constraint(update_justification(c, mk_composite1(c.get_justification(), j)));
return !in_conflict();
}
bool next_lazy_constraints_case_split(lazy_constraints_case_split & cs) { bool next_lazy_constraints_case_split(lazy_constraints_case_split & cs) {
auto r = cs.m_tail.pull(); auto r = cs.m_tail.pull();
if (r) { if (r) {
@ -1029,29 +1018,30 @@ struct unifier_fn {
if (!is_constant(f_lhs) || !is_constant(f_rhs) || const_name(f_lhs) != const_name(f_rhs)) if (!is_constant(f_lhs) || !is_constant(f_rhs) || const_name(f_lhs) != const_name(f_rhs))
return lazy_list<constraints>(); return lazy_list<constraints>();
justification const & j = c.get_justification(); justification const & j = c.get_justification();
buffer<constraint> cs; constraint_seq cs;
bool relax = relax_main_opaque(c); bool relax = relax_main_opaque(c);
lean_assert(!m_tc[relax]->next_cnstr()); auto fcs = m_tc[relax]->is_def_eq(f_lhs, f_rhs, j);
if (!m_tc[relax]->is_def_eq(f_lhs, f_rhs, j, cs)) if (!fcs.first)
return lazy_list<constraints>(); return lazy_list<constraints>();
cs = fcs.second;
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[relax]->next_cnstr()); for (unsigned i = 0; i < args_lhs.size(); i++) {
for (unsigned i = 0; i < args_lhs.size(); i++) auto acs = m_tc[relax]->is_def_eq(args_lhs[i], args_rhs[i], j);
if (!m_tc[relax]->is_def_eq(args_lhs[i], args_rhs[i], j, cs)) if (!acs.first)
return lazy_list<constraints>(); return lazy_list<constraints>();
return lazy_list<constraints>(to_list(cs.begin(), cs.end())); cs = acs.second + cs;
}
return lazy_list<constraints>(cs.to_list());
} }
bool process_plugin_constraint(constraint const & c) { bool process_plugin_constraint(constraint const & c) {
bool relax = relax_main_opaque(c); bool relax = relax_main_opaque(c);
lean_assert(!is_choice_cnstr(c)); lean_assert(!is_choice_cnstr(c));
lean_assert(!m_tc[relax]->next_cnstr());
lazy_list<constraints> alts = m_plugin->solve(*m_tc[relax], c, m_ngen.mk_child()); lazy_list<constraints> alts = m_plugin->solve(*m_tc[relax], c, m_ngen.mk_child());
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());
} }
@ -1068,8 +1058,8 @@ struct unifier_fn {
expr m_type; expr m_type;
bool relax = relax_main_opaque(c); bool relax = relax_main_opaque(c);
buffer<constraint> cs; constraint_seq cs;
if (auto type = infer(m, c.get_justification(), relax, cs)) { if (auto type = infer(m, relax, cs)) {
m_type = *type; m_type = *type;
if (!process_constraints(cs)) if (!process_constraints(cs))
return false; return false;
@ -1128,11 +1118,11 @@ struct unifier_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); bool relax = relax_main_opaque(c);
buffer<constraint> cs2; auto dcs = m_tc[relax]->is_def_eq(t, s, j);
if (m_tc[relax]->is_def_eq(t, s, j, cs2)) { if (dcs.first) {
// 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, dcs.second.to_list())));
} }
// process first case // process first case
@ -1231,26 +1221,20 @@ struct unifier_fn {
return true; return true;
} }
/** \brief Copy pending constraints in u.m_tc[relax] to cs and append justification j to them */
void copy_pending_constraints(buffer<constraint> & cs) {
while (auto c = u.m_tc[relax]->next_cnstr())
cs.push_back(update_justification(*c, mk_composite1(c->get_justification(), j)));
}
/** \see ensure_sufficient_args */ /** \see ensure_sufficient_args */
expr ensure_sufficient_args_core(expr mtype, unsigned i) { expr ensure_sufficient_args_core(expr mtype, unsigned i, constraint_seq & cs) {
if (i == margs.size()) if (i == margs.size())
return mtype; return mtype;
mtype = u.m_tc[relax]->ensure_pi(mtype); mtype = u.m_tc[relax]->ensure_pi(mtype, cs);
expr local = u.mk_local_for(mtype); expr local = u.mk_local_for(mtype);
expr body = instantiate(binding_body(mtype), local); expr body = instantiate(binding_body(mtype), local);
return Pi(local, ensure_sufficient_args_core(body, i+1)); return Pi(local, ensure_sufficient_args_core(body, i+1, cs));
} }
/** \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.
*/ */
expr ensure_sufficient_args(expr const & mtype, buffer<constraint> & cs) { expr ensure_sufficient_args(expr const & mtype, constraint_seq & cs) {
expr t = mtype; expr t = mtype;
unsigned num = 0; unsigned num = 0;
while (is_pi(t)) { while (is_pi(t)) {
@ -1259,12 +1243,7 @@ struct unifier_fn {
} }
if (num == margs.size()) if (num == margs.size())
return mtype; return mtype;
lean_assert(!u.m_tc[relax]->next_cnstr()); // make sure there are no pending constraints return ensure_sufficient_args_core(mtype, 0, cs);
// We must create a scope to make sure no constraints "leak" into the current state.
type_checker::scope scope(*u.m_tc[relax]);
auto new_mtype = ensure_sufficient_args_core(mtype, 0);
copy_pending_constraints(cs);
return new_mtype;
} }
/** /**
@ -1278,10 +1257,10 @@ struct unifier_fn {
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; constraint_seq cs;
auto new_mtype = ensure_sufficient_args(mtype, cs); expr new_mtype = ensure_sufficient_args(mtype, cs);
cs.push_back(mk_eq_cnstr(m, mk_lambda_for(new_mtype, rhs), j, relax)); cs = cs + mk_eq_cnstr(m, mk_lambda_for(new_mtype, rhs), j, relax);
alts.push_back(to_list(cs.begin(), cs.end())); alts.push_back(cs.to_list());
} }
/** /**
@ -1302,15 +1281,15 @@ struct unifier_fn {
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; constraint_seq cs;
auto new_mtype = ensure_sufficient_args(mtype, cs); auto new_mtype = ensure_sufficient_args(mtype, cs);
// 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 (u.m_tc[relax]->is_def_eq_types(marg, rhs, j, cs) && if (u.m_tc[relax]->is_def_eq_types(marg, rhs, j, cs) &&
u.m_tc[relax]->is_def_eq(marg, rhs, j, cs)) { u.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, relax)); cs = cs + mk_eq_cnstr(m, v, j, relax);
alts.push_back(to_list(cs.begin(), cs.end())); alts.push_back(cs.to_list());
} }
} }
@ -1344,11 +1323,11 @@ struct unifier_fn {
mk_simple_nonlocal_projection(i); mk_simple_nonlocal_projection(i);
} 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; constraint_seq cs;
auto new_mtype = ensure_sufficient_args(mtype, cs); auto new_mtype = ensure_sufficient_args(mtype, 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, relax)); cs = cs + mk_eq_cnstr(m, v, j, relax);
alts.push_back(to_list(cs.begin(), cs.end())); alts.push_back(cs.to_list());
} }
} }
} }
@ -1386,30 +1365,6 @@ struct unifier_fn {
return v; return v;
} }
/** \brief Check if term \c e (produced by an imitation step) is
type correct, and store generated constraints in \c cs.
Include \c j in all generated constraints */
bool check_imitation(expr e, buffer<constraint> & cs) {
buffer<expr> ls;
while (is_lambda(e)) {
expr d = instantiate_rev(binding_domain(e), ls.size(), ls.data());
expr l = mk_local(u.m_ngen.next(), binding_name(e), d, binding_info(e));
ls.push_back(l);
e = binding_body(e);
}
e = instantiate_rev(e, ls.size(), ls.data());;
try {
buffer<constraint> aux;
u.m_tc[relax]->check(e, aux);
for (auto c : aux) {
cs.push_back(update_justification(c, mk_composite1(j, c.get_justification())));
}
return true;
} catch (exception&) {
return false;
}
}
void mk_app_projections() { void mk_app_projections() {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_app(rhs)); lean_assert(is_app(rhs));
@ -1433,7 +1388,7 @@ struct unifier_fn {
/** \brief Create the local context \c locals for the imitiation step. /** \brief Create the local context \c locals for the imitiation step.
*/ */
void mk_local_context(buffer<expr> & locals, buffer<constraint> & cs) { void mk_local_context(buffer<expr> & locals, constraint_seq & cs) {
expr mtype = mlocal_type(m); expr mtype = mlocal_type(m);
unsigned nargs = margs.size(); unsigned nargs = margs.size();
mtype = ensure_sufficient_args(mtype, cs); mtype = ensure_sufficient_args(mtype, cs);
@ -1456,7 +1411,7 @@ struct unifier_fn {
} }
expr mk_imitiation_arg(expr const & arg, expr const & type, buffer<expr> const & locals, expr mk_imitiation_arg(expr const & arg, expr const & type, buffer<expr> const & locals,
buffer<constraint> & cs) { constraint_seq & cs) {
if (!has_meta_args() && is_local(arg) && contains_local(arg, locals)) { if (!has_meta_args() && is_local(arg) && contains_local(arg, locals)) {
return arg; return arg;
} else { } else {
@ -1464,39 +1419,36 @@ struct unifier_fn {
if (context_check(type, locals)) { if (context_check(type, locals)) {
expr maux = mk_metavar(u.m_ngen.next(), Pi(locals, type)); expr maux = mk_metavar(u.m_ngen.next(), Pi(locals, type));
// std::cout << " >> " << maux << " : " << mlocal_type(maux) << "\n"; // std::cout << " >> " << maux << " : " << mlocal_type(maux) << "\n";
cs.push_back(mk_eq_cnstr(mk_app(maux, margs), arg, j, relax)); cs = mk_eq_cnstr(mk_app(maux, margs), arg, j, relax) + cs;
return mk_app(maux, locals); return mk_app(maux, locals);
} else { } else {
expr maux_type = mk_metavar(u.m_ngen.next(), Pi(locals, mk_sort(mk_meta_univ(u.m_ngen.next())))); expr maux_type = mk_metavar(u.m_ngen.next(), Pi(locals, mk_sort(mk_meta_univ(u.m_ngen.next()))));
expr maux = mk_metavar(u.m_ngen.next(), Pi(locals, mk_app(maux_type, locals))); expr maux = mk_metavar(u.m_ngen.next(), Pi(locals, mk_app(maux_type, locals)));
cs.push_back(mk_eq_cnstr(mk_app(maux, margs), arg, j, relax)); cs = mk_eq_cnstr(mk_app(maux, margs), arg, j, relax) + cs;
return mk_app(maux, locals); return mk_app(maux, locals);
} }
} }
} }
void mk_app_imitation_core(expr const & f, buffer<expr> const & locals, buffer<constraint> & cs) { void mk_app_imitation_core(expr const & f, buffer<expr> const & locals, constraint_seq & cs) {
buffer<expr> rargs; buffer<expr> rargs;
get_app_args(rhs, rargs); get_app_args(rhs, rargs);
buffer<expr> sargs; buffer<expr> sargs;
try { try {
// create a scope to make sure no constraints "leak" into the current state expr f_type = u.m_tc[relax]->infer(f, cs);
type_checker::scope scope(*u.m_tc[relax]);
expr f_type = u.m_tc[relax]->infer(f);
for (expr const & rarg : rargs) { for (expr const & rarg : rargs) {
f_type = u.m_tc[relax]->ensure_pi(f_type); f_type = u.m_tc[relax]->ensure_pi(f_type, cs);
expr d_type = binding_domain(f_type); expr d_type = binding_domain(f_type);
expr sarg = mk_imitiation_arg(rarg, d_type, locals, cs); expr sarg = mk_imitiation_arg(rarg, d_type, locals, cs);
sargs.push_back(sarg); sargs.push_back(sarg);
f_type = instantiate(binding_body(f_type), sarg); f_type = instantiate(binding_body(f_type), sarg);
} }
copy_pending_constraints(cs);
} catch (exception&) {} } catch (exception&) {}
expr v = Fun(locals, mk_app(f, sargs)); expr v = Fun(locals, mk_app(f, sargs));
// std::cout << " >> app imitation, v: " << v << "\n"; // std::cout << " >> app imitation, v: " << v << "\n";
lean_assert(!has_local(v)); lean_assert(!has_local(v));
cs.push_back(mk_eq_cnstr(m, v, j, relax)); cs = cs + mk_eq_cnstr(m, v, j, relax);
alts.push_back(to_list(cs.begin(), cs.end())); alts.push_back(cs.to_list());
} }
/** /**
@ -1519,22 +1471,20 @@ struct unifier_fn {
void mk_app_imitation() { void mk_app_imitation() {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_app(rhs)); lean_assert(is_app(rhs));
lean_assert(!u.m_tc[relax]->next_cnstr()); // make sure there are no pending constraints
buffer<expr> locals; buffer<expr> locals;
buffer<constraint> cs; constraint_seq cs;
flet<justification> let(j, j); // save j value flet<justification> let(j, j); // save j value
mk_local_context(locals, cs); mk_local_context(locals, cs);
lean_assert(margs.size() == locals.size()); lean_assert(margs.size() == locals.size());
expr const & f = get_app_fn(rhs); expr const & f = get_app_fn(rhs);
lean_assert(is_constant(f) || is_local(f)); lean_assert(is_constant(f) || is_local(f));
if (is_local(f)) { if (is_local(f)) {
unsigned cs_sz = cs.size();
unsigned i = margs.size(); unsigned i = margs.size();
while (i > 0) { while (i > 0) {
--i; --i;
if (is_local(margs[i]) && mlocal_name(margs[i]) == mlocal_name(f)) { if (is_local(margs[i]) && mlocal_name(margs[i]) == mlocal_name(f)) {
cs.shrink(cs_sz); constraint_seq new_cs = cs;
mk_app_imitation_core(locals[i], locals, cs); mk_app_imitation_core(locals[i], locals, new_cs);
} }
} }
} else { } else {
@ -1556,30 +1506,27 @@ struct unifier_fn {
void mk_bindings_imitation() { void mk_bindings_imitation() {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_binding(rhs)); lean_assert(is_binding(rhs));
lean_assert(!u.m_tc[relax]->next_cnstr()); // make sure there are no pending constraints constraint_seq cs;
buffer<constraint> cs;
buffer<expr> locals; buffer<expr> locals;
flet<justification> let(j, j); // save j value flet<justification> let(j, j); // save j value
mk_local_context(locals, cs); mk_local_context(locals, cs);
lean_assert(margs.size() == locals.size()); lean_assert(margs.size() == locals.size());
try { try {
// create a scope to make sure no constraints "leak" into the current state // create a scope to make sure no constraints "leak" into the current state
type_checker::scope scope(*u.m_tc[relax]);
expr rhs_A = binding_domain(rhs); expr rhs_A = binding_domain(rhs);
expr A_type = u.m_tc[relax]->infer(rhs_A); expr A_type = u.m_tc[relax]->infer(rhs_A, cs);
expr A = mk_imitiation_arg(rhs_A, A_type, locals, cs); expr A = mk_imitiation_arg(rhs_A, A_type, locals, cs);
expr local = mk_local(u.m_ngen.next(), binding_name(rhs), A, binding_info(rhs)); expr local = mk_local(u.m_ngen.next(), binding_name(rhs), A, binding_info(rhs));
locals.push_back(local); locals.push_back(local);
margs.push_back(local); margs.push_back(local);
expr rhs_B = instantiate(binding_body(rhs), local); expr rhs_B = instantiate(binding_body(rhs), local);
expr B_type = u.m_tc[relax]->infer(rhs_B); expr B_type = u.m_tc[relax]->infer(rhs_B, cs);
expr B = mk_imitiation_arg(rhs_B, B_type, locals, cs); expr B = mk_imitiation_arg(rhs_B, B_type, locals, cs);
expr binding = is_pi(rhs) ? Pi(local, B) : Fun(local, B); expr binding = is_pi(rhs) ? Pi(local, B) : Fun(local, B);
locals.pop_back(); locals.pop_back();
expr v = Fun(locals, binding); expr v = Fun(locals, binding);
copy_pending_constraints(cs); cs = cs + mk_eq_cnstr(m, v, j, relax);
cs.push_back(mk_eq_cnstr(m, v, j, relax)); alts.push_back(cs.to_list());
alts.push_back(to_list(cs.begin(), cs.end()));
} catch (exception&) {} } catch (exception&) {}
margs.pop_back(); margs.pop_back();
} }
@ -1596,24 +1543,27 @@ 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() { void mk_macro_imitation() {
// TODO(Leo): use same approach used in mk_app_imitation
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_macro(rhs)); lean_assert(is_macro(rhs));
buffer<constraint> cs; constraint_seq cs;
expr mtype = mlocal_type(m); expr mtype = mlocal_type(m);
mtype = ensure_sufficient_args(mtype, cs); mtype = ensure_sufficient_args(mtype, cs);
// 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(u.m_ngen, mtype); expr maux = mk_aux_metavar_for(u.m_ngen, mtype);
cs.push_back(mk_eq_cnstr(mk_app(maux, margs), macro_arg(rhs, i), j, relax)); cs = mk_eq_cnstr(mk_app(maux, margs), macro_arg(rhs, i), j, relax) + cs;
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);
if (check_imitation(v, cs)) { // if (check_imitation(v, cs)) {
cs.push_back(mk_eq_cnstr(m, v, j, relax)); // 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()));
} // }
cs = cs + mk_eq_cnstr(m, v, j, relax);
alts.push_back(cs.to_list());
} }
public: public:
@ -1689,10 +1639,10 @@ struct unifier_fn {
if (is_app(rhs)) { if (is_app(rhs)) {
expr const & f = get_app_fn(rhs); expr const & f = get_app_fn(rhs);
if (!is_local(f) && !is_constant(f)) { if (!is_local(f) && !is_constant(f)) {
buffer<constraint> cs; constraint_seq cs;
expr new_rhs = whnf(rhs, j, relax, cs); expr new_rhs = whnf(rhs, relax, cs);
lean_assert(new_rhs != rhs); lean_assert(new_rhs != rhs);
if (!process_constraints(cs)) if (!process_constraints(cs, j))
return false; return false;
return is_def_eq(lhs, new_rhs, j, relax); return is_def_eq(lhs, new_rhs, j, relax);
} }
@ -1770,20 +1720,6 @@ struct unifier_fn {
return true; return true;
} }
void consume_tc_cnstrs() {
while (true) {
if (in_conflict()) {
return;
} else if (auto c = m_tc[0]->next_cnstr()) {
process_constraint(*c);
} else if (auto c = m_tc[1]->next_cnstr()) {
process_constraint(*c);
} else {
break;
}
}
}
/** /**
\brief Process the following constraints \brief Process the following constraints
1. (max l1 l2) =?= 0 OR 1. (max l1 l2) =?= 0 OR
@ -1857,8 +1793,6 @@ struct unifier_fn {
/** \brief Process the next constraint in the constraint queue m_cnstrs */ /** \brief Process the next constraint in the constraint queue m_cnstrs */
bool process_next() { bool process_next() {
lean_assert(!m_cnstrs.empty()); lean_assert(!m_cnstrs.empty());
lean_assert(!m_tc[0]->next_cnstr());
lean_assert(!m_tc[1]->next_cnstr());
auto const * p = m_cnstrs.min(); auto const * p = m_cnstrs.min();
unsigned cidx = p->second; unsigned cidx = p->second;
if (!m_expensive && cidx >= get_group_first_index(cnstr_group::ClassInstance)) if (!m_expensive && cidx >= get_group_first_index(cnstr_group::ClassInstance))
@ -1871,8 +1805,6 @@ 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[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)
@ -1933,7 +1865,6 @@ struct unifier_fn {
return optional<substitution>(); return optional<substitution>();
} }
while (true) { while (true) {
consume_tc_cnstrs();
if (!in_conflict()) { if (!in_conflict()) {
if (m_cnstrs.empty()) if (m_cnstrs.empty())
break; break;
@ -1942,8 +1873,6 @@ struct unifier_fn {
if (in_conflict() && !resolve_conflict()) if (in_conflict() && !resolve_conflict())
return failure(); return failure();
} }
lean_assert(!m_tc[0]->next_cnstr());
lean_assert(!m_tc[1]->next_cnstr());
lean_assert(!in_conflict()); lean_assert(!in_conflict());
lean_assert(m_cnstrs.empty()); lean_assert(m_cnstrs.empty());
substitution s = m_subst; substitution s = m_subst;
@ -1983,11 +1912,13 @@ lazy_list<substitution> unify(environment const & env, expr const & lhs, expr co
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, expensive); auto u = std::make_shared<unifier_fn>(env, 0, nullptr, ngen, new_s, false, max_steps, expensive);
if (!u->m_tc[relax]->is_def_eq(_lhs, _rhs)) constraint_seq cs;
if (!u->m_tc[relax]->is_def_eq(_lhs, _rhs, justification(), cs) || !u->process_constraints(cs)) {
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,
bool relax, substitution const & s, options const & o) { bool relax, substitution const & s, options const & o) {

View file

@ -67,12 +67,12 @@ static void tst1() {
expr c = mk_local("c", Prop); expr c = mk_local("c", Prop);
expr id = Const("id"); expr id = Const("id");
type_checker checker(env3, name_generator("tmp")); type_checker checker(env3, name_generator("tmp"));
lean_assert(checker.check(id(Prop)) == Prop >> Prop); lean_assert(checker.check(id(Prop)).first == Prop >> Prop);
lean_assert(checker.whnf(id(Prop, c)) == c); lean_assert(checker.whnf(id(Prop, c)).first == c);
lean_assert(checker.whnf(id(Prop, id(Prop, id(Prop, c)))) == c); lean_assert(checker.whnf(id(Prop, id(Prop, id(Prop, c)))).first == c);
type_checker checker2(env2, name_generator("tmp")); type_checker checker2(env2, name_generator("tmp"));
lean_assert(checker2.whnf(id(Prop, id(Prop, id(Prop, c)))) == id(Prop, id(Prop, id(Prop, c)))); lean_assert(checker2.whnf(id(Prop, id(Prop, id(Prop, c)))).first == id(Prop, id(Prop, id(Prop, c))));
} }
static void tst2() { static void tst2() {
@ -99,34 +99,34 @@ static void tst2() {
expr c1 = mk_local("c1", Prop); expr c1 = mk_local("c1", Prop);
expr c2 = mk_local("c2", Prop); expr c2 = mk_local("c2", Prop);
expr id = Const("id"); expr id = Const("id");
std::cout << checker.whnf(f3(c1, c2)) << "\n"; std::cout << checker.whnf(f3(c1, c2)).first << "\n";
lean_assert_eq(env.find(name(base, 98))->get_weight(), 98); lean_assert_eq(env.find(name(base, 98))->get_weight(), 98);
lean_assert(checker.is_def_eq(f98(c1, c2), f97(f97(c1, c2), f97(c2, c1)))); lean_assert(checker.is_def_eq(f98(c1, c2), f97(f97(c1, c2), f97(c2, c1))).first);
lean_assert(checker.is_def_eq(f98(c1, id(Prop, id(Prop, c2))), f97(f97(c1, id(Prop, c2)), f97(c2, c1)))); lean_assert(checker.is_def_eq(f98(c1, id(Prop, id(Prop, c2))), f97(f97(c1, id(Prop, c2)), f97(c2, c1))).first);
name_set s; name_set s;
s.insert(name(base, 96)); s.insert(name(base, 96));
type_checker checker2(env, name_generator("tmp"), mk_default_converter(env, optional<module_idx>(), true, s)); type_checker checker2(env, name_generator("tmp"), mk_default_converter(env, optional<module_idx>(), true, s));
lean_assert_eq(checker2.whnf(f98(c1, c2)), lean_assert_eq(checker2.whnf(f98(c1, c2)).first,
f96(f96(f97(c1, c2), f97(c2, c1)), f96(f97(c2, c1), f97(c1, c2)))); f96(f96(f97(c1, c2), f97(c2, c1)), f96(f97(c2, c1), f97(c1, c2))));
} }
class normalizer_extension_tst : public normalizer_extension { class normalizer_extension_tst : public normalizer_extension {
public: public:
virtual optional<expr> operator()(expr const & e, extension_context & ctx) const { virtual optional<pair<expr, constraint_seq>> operator()(expr const & e, extension_context & ctx) const {
if (!is_app(e)) if (!is_app(e))
return none_expr(); return optional<pair<expr, constraint_seq>>();
expr const & f = app_fn(e); expr const & f = app_fn(e);
expr const & a = app_arg(e); expr const & a = app_arg(e);
if (!is_constant(f) || const_name(f) != name("proj1")) if (!is_constant(f) || const_name(f) != name("proj1"))
return none_expr(); return optional<pair<expr, constraint_seq>>();
expr a_n = ctx.whnf(a); expr a_n = ctx.whnf(a).first;
if (!is_app(a_n) || !is_app(app_fn(a_n)) || !is_constant(app_fn(app_fn(a_n)))) if (!is_app(a_n) || !is_app(app_fn(a_n)) || !is_constant(app_fn(app_fn(a_n))))
return none_expr(); return optional<pair<expr, constraint_seq>>();
expr const & mk = app_fn(app_fn(a_n)); expr const & mk = app_fn(app_fn(a_n));
if (const_name(mk) != name("mk")) if (const_name(mk) != name("mk"))
return none_expr(); return optional<pair<expr, constraint_seq>>();
// In a real implementation, we must check if proj1 and mk were defined in the environment. // In a real implementation, we must check if proj1 and mk were defined in the environment.
return some_expr(app_arg(app_fn(a_n))); return optional<pair<expr, constraint_seq>>(app_arg(app_fn(a_n)), constraint_seq());
} }
virtual bool may_reduce_later(expr const &, extension_context &) const { return false; } virtual bool may_reduce_later(expr const &, extension_context &) const { return false; }
virtual bool supports(name const &) const { return false; } virtual bool supports(name const &) const { return false; }
@ -145,7 +145,7 @@ static void tst3() {
expr a = Const("a"); expr a = Const("a");
expr b = Const("b"); expr b = Const("b");
type_checker checker(env, name_generator("tmp")); type_checker checker(env, name_generator("tmp"));
lean_assert_eq(checker.whnf(proj1(proj1(mk(id(A, mk(a, b)), b)))), a); lean_assert_eq(checker.whnf(proj1(proj1(mk(id(A, mk(a, b)), b)))).first, a);
} }
class dummy_ext : public environment_extension {}; class dummy_ext : public environment_extension {};

View file

@ -10,6 +10,7 @@ Author: Leonardo de Moura
#include "util/buffer.h" #include "util/buffer.h"
#include "util/optional.h" #include "util/optional.h"
#include "util/memory_pool.h" #include "util/memory_pool.h"
#include "util/list.h"
namespace lean { namespace lean {
/** \brief Sequence datastructure with O(1) concatenation operation */ /** \brief Sequence datastructure with O(1) concatenation operation */
@ -76,9 +77,13 @@ public:
friend bool is_eqp(sequence const & s1, sequence const & s2) { return s1.m_node.raw() == s2.m_node.raw(); } friend bool is_eqp(sequence const & s1, sequence const & s2) { return s1.m_node.raw() == s2.m_node.raw(); }
friend sequence operator+(sequence const & s1, sequence const & s2) { return sequence(s1, s2); } friend sequence operator+(sequence const & s1, sequence const & s2) { return sequence(s1, s2); }
friend sequence operator+(sequence const & s, T const & v) { return s + sequence(v); }
friend sequence operator+(T const & v, sequence const & s) { return sequence(v) + s; }
sequence & operator+=(T const & v) { *this = *this + v; return *this; }
sequence & operator+=(sequence const & s) { *this = *this + s; return *this; }
/** \brief Store sequence elements in \c r */ template<typename F>
void linearize(buffer<T> & r) const { bool all_of(F && f) const {
buffer<cell const *> todo; buffer<cell const *> todo;
if (m_node) todo.push_back(m_node.raw()); if (m_node) todo.push_back(m_node.raw());
while (!todo.empty()) { while (!todo.empty()) {
@ -88,9 +93,23 @@ public:
todo.push_back(static_cast<join_cell const *>(c)->m_second.raw()); todo.push_back(static_cast<join_cell const *>(c)->m_second.raw());
todo.push_back(static_cast<join_cell const *>(c)->m_first.raw()); todo.push_back(static_cast<join_cell const *>(c)->m_first.raw());
} else { } else {
r.push_back(static_cast<elem_cell const *>(c)->m_value); if (!f(static_cast<elem_cell const *>(c)->m_value))
return false;
} }
} }
return true;
}
template<typename F>
void for_each(F && f) const { all_of([&](T const & v) { f(v); return true; }); }
/** \brief Store sequence elements in \c r */
void linearize(buffer<T> & r) const { for_each([&](T const & v) { r.push_back(v); }); }
list<T> to_list() const {
buffer<T> tmp;
linearize(tmp);
return ::lean::to_list(tmp.begin(), tmp.end());
} }
}; };

View file

@ -41,8 +41,11 @@ assert(not env:is_descendant(env2))
local tc2 = type_checker(env2) local tc2 = type_checker(env2)
id_u = Const("id", {mk_global_univ("u")}) id_u = Const("id", {mk_global_univ("u")})
print(tc2:check(id_u)) print(tc2:check(id_u))
print(tc2:check(tc2:check(id_u))) local tmp = tc2:check(id_u)
print(tc2:check(tc2:check(tc2:check(id_u)))) print(tc2:check(tmp))
local tmp1 = tc2:check(id_u)
local tmp2 = tc2:check(tmp1)
print(tc2:check(tmp2))
env2 = add_decl(env2, mk_var_decl("a", Type)) env2 = add_decl(env2, mk_var_decl("a", Type))
local tc2 = type_checker(env2) local tc2 = type_checker(env2)
local a = Const("a") local a = Const("a")

View file

@ -21,6 +21,3 @@ print("check(t): ")
print(tc2:check(t)) print(tc2:check(t))
print("check(t2): ") print("check(t2): ")
print(tc2:check(t2)) print(tc2:check(t2))
assert(tc2:next_cnstr())
assert(tc2:next_cnstr())
assert(not tc2:next_cnstr())

View file

@ -10,7 +10,6 @@ assert(tc:is_prop(Const("C")))
assert(not tc:is_prop(Const("T"))) assert(not tc:is_prop(Const("T")))
assert(not tc:is_prop(Const("B2"))) assert(not tc:is_prop(Const("B2")))
print(tc:check(mk_lambda("x", mk_metavar("m", mk_metavar("t", mk_sort(mk_meta_univ("l")))), Var(0)))) print(tc:check(mk_lambda("x", mk_metavar("m", mk_metavar("t", mk_sort(mk_meta_univ("l")))), Var(0))))
assert(tc:next_cnstr())
print(tc:ensure_sort(Const("B2"))) print(tc:ensure_sort(Const("B2")))
assert(not pcall(function() assert(not pcall(function()
print(tc:ensure_sort(Const("A"))) print(tc:ensure_sort(Const("A")))

View file

@ -8,17 +8,16 @@ local a = Const("a")
local m1 = mk_metavar("m1", mk_metavar("m2", mk_sort(mk_meta_univ("l")))) local m1 = mk_metavar("m1", mk_metavar("m2", mk_sort(mk_meta_univ("l"))))
local ngen = name_generator("tst") local ngen = name_generator("tst")
local tc = type_checker(env, ngen) local tc = type_checker(env, ngen)
assert(tc:num_scopes() == 0)
tc:push() function test_check(e)
assert(tc:num_scopes() == 1) t, cs = tc:check(e)
print(tc:check(f(m1))) print(tostring(e) .. " : " .. tostring(t))
assert(tc:next_cnstr()) cs = cs:linearize()
assert(not tc:next_cnstr()) for i = 1, #cs do
print(tc:check(f(f(m1)))) print(" >> " .. tostring(cs[i]))
assert(not tc:next_cnstr()) -- New constraint is not generated end
tc:pop() -- forget that we checked f(m1) end
print(tc:check(f(m1)))
-- constraint is generated again test_check(f(m1))
assert(tc:next_cnstr()) test_check(f(f(m1)))
assert(not tc:next_cnstr()) test_check(f(m1))
check_error(function() tc:pop() end)

View file

@ -34,27 +34,26 @@ local ng = name_generator("foo")
local tc = type_checker(env, ng) local tc = type_checker(env, ng)
local m1 = mk_metavar("m1", Prop) local m1 = mk_metavar("m1", Prop)
print("before is_def_eq") print("before is_def_eq")
assert(not tc:next_cnstr())
local tc = type_checker(env, ng) local tc = type_checker(env, ng)
assert(tc:is_def_eq(foo_intro(m1, q(a), q(a), Ax1), foo_intro(q(a), q(a), q(a), Ax2))) r, cs = tc:is_def_eq(foo_intro(m1, q(a), q(a), Ax1), foo_intro(q(a), q(a), q(a), Ax2))
local c = tc:next_cnstr() assert(r)
assert(c) cs = cs:linearize()
assert(not tc:next_cnstr()) assert(#cs == 1)
assert(c:lhs() == m1) assert(cs[1]:lhs() == m1)
assert(c:rhs() == q(a)) assert(cs[1]:rhs() == q(a))
local tc = type_checker(env, ng) local tc = type_checker(env, ng)
assert(not tc:next_cnstr())
print(tostring(foo_intro) .. " : " .. tostring(tc:check(foo_intro))) print(tostring(foo_intro) .. " : " .. tostring(tc:check(foo_intro)))
print(tostring(foo_intro2) .. " : " .. tostring(tc:check(foo_intro2))) print(tostring(foo_intro2) .. " : " .. tostring(tc:check(foo_intro2)))
assert(tc:is_def_eq(foo_intro, foo_intro2)) assert(tc:is_def_eq(foo_intro, foo_intro2))
print("before is_def_eq2") print("before is_def_eq2")
assert(tc:is_def_eq(foo_intro(m1, q(a), q(b), Ax1), foo_intro2(q(a), q(a), q(a), Ax2))) local r, cs = tc:is_def_eq(foo_intro(m1, q(a), q(b), Ax1), foo_intro2(q(a), q(a), q(a), Ax2))
assert(not tc:next_cnstr()) assert(r)
cs = cs:linearize()
assert(#cs == 0)
local tc = type_checker(env, ng) local tc = type_checker(env, ng)
print("before failure") print("before failure")
assert(not pcall(function() print(tc:check(and_intro(m1, q(a), Ax1, Ax3))) end)) assert(not pcall(function() print(tc:check(and_intro(m1, q(a), Ax1, Ax3))) end))
assert(not tc:next_cnstr())
print("before success") print("before success")
print(tc:check(and_intro(m1, q(a), Ax1, Ax2))) local t, cs = tc:check(and_intro(m1, q(a), Ax1, Ax2))
assert(tc:next_cnstr()) cs = cs:linearize()
assert(not tc:next_cnstr()) assert(#cs == 1)