feat(frontends/lean/elaborator): discard partial solution during
class-instance resolution, use only tactic_hints associated with classes, enforce is_strict
This commit is contained in:
parent
318fec43a4
commit
c775da16ec
4 changed files with 70 additions and 53 deletions
|
@ -351,7 +351,7 @@ public:
|
|||
while (true) {
|
||||
lean_assert(is_pi(type));
|
||||
tag g = e.get_tag();
|
||||
bool is_strict = false;
|
||||
bool is_strict = true;
|
||||
expr imp_arg = mk_placeholder_meta(some_expr(binding_domain(type)), g, is_strict, cs);
|
||||
e = mk_app(e, imp_arg, g);
|
||||
type = instantiate(binding_body(type), imp_arg);
|
||||
|
@ -556,7 +556,7 @@ public:
|
|||
bool first = true;
|
||||
while (binding_info(f_type).is_strict_implicit() || (!first && binding_info(f_type).is_implicit())) {
|
||||
tag g = f.get_tag();
|
||||
bool is_strict = false;
|
||||
bool is_strict = true;
|
||||
expr imp_arg = mk_placeholder_meta(some_expr(binding_domain(f_type)), g, is_strict, f_cs);
|
||||
f = mk_app(f, imp_arg, g);
|
||||
auto f_t = ensure_fun(f, f_cs);
|
||||
|
@ -804,7 +804,7 @@ public:
|
|||
tag g = e.get_tag();
|
||||
expr r_type = whnf(infer_type(r, cs), cs);
|
||||
expr imp_arg;
|
||||
bool is_strict = false;
|
||||
bool is_strict = true;
|
||||
while (is_pi(r_type) && binding_info(r_type).is_implicit()) {
|
||||
imp_arg = mk_placeholder_meta(some_expr(binding_domain(r_type)), g, is_strict, cs);
|
||||
r = mk_app(r, imp_arg, g);
|
||||
|
|
|
@ -7,6 +7,7 @@ Author: Leonardo de Moura
|
|||
#include "util/lazy_list_fn.h"
|
||||
#include "util/flet.h"
|
||||
#include "kernel/instantiate.h"
|
||||
#include "kernel/for_each_fn.h"
|
||||
#include "kernel/abstract.h"
|
||||
#include "library/unifier.h"
|
||||
#include "library/reducible.h"
|
||||
|
@ -44,7 +45,7 @@ struct placeholder_context {
|
|||
};
|
||||
|
||||
pair<expr, constraint> mk_placeholder_elaborator(std::shared_ptr<placeholder_context> const & C,
|
||||
bool is_strict, optional<expr> const & type, tag g);
|
||||
optional<expr> const & type, tag g);
|
||||
|
||||
/** \brief Whenever the elaborator finds a placeholder '_' or introduces an
|
||||
implicit argument, it creates a metavariable \c ?m. It also creates a
|
||||
|
@ -86,8 +87,8 @@ struct placeholder_elaborator : public choice_iterator {
|
|||
expr const & meta, expr const & meta_type,
|
||||
list<expr> const & local_insts, list<name> const & instances,
|
||||
list<tactic_hint_entry> const & tacs,
|
||||
justification const & j, bool ignore_failure):
|
||||
choice_iterator(ignore_failure), m_C(C),
|
||||
justification const & j):
|
||||
choice_iterator(), m_C(C),
|
||||
m_meta(meta), m_meta_type(meta_type),
|
||||
m_local_instances(local_insts), m_instances(instances),
|
||||
m_tactics(tacs),
|
||||
|
@ -124,9 +125,7 @@ struct placeholder_elaborator : public choice_iterator {
|
|||
type = tc.whnf(type).first;
|
||||
if (!is_pi(type))
|
||||
break;
|
||||
bool is_strict = true;
|
||||
pair<expr, constraint> ac = mk_placeholder_elaborator(m_C, is_strict,
|
||||
some_expr(binding_domain(type)), g);
|
||||
pair<expr, constraint> ac = mk_placeholder_elaborator(m_C, some_expr(binding_domain(type)), g);
|
||||
expr arg = ac.first;
|
||||
cs.push_back(ac.second);
|
||||
r = mk_app(r, arg).set_tag(g);
|
||||
|
@ -205,7 +204,7 @@ struct placeholder_elaborator : public choice_iterator {
|
|||
};
|
||||
|
||||
|
||||
constraint mk_placeholder_cnstr(std::shared_ptr<placeholder_context> const & C, expr const & m, bool is_strict) {
|
||||
constraint mk_placeholder_cnstr(std::shared_ptr<placeholder_context> const & C, expr const & m) {
|
||||
environment const & env = C->env();
|
||||
justification j = mk_failed_to_synthesize_jst(env, m);
|
||||
auto choice_fn = [=](expr const & meta, expr const & meta_type, substitution const & s,
|
||||
|
@ -224,18 +223,10 @@ constraint mk_placeholder_cnstr(std::shared_ptr<placeholder_context> const & C,
|
|||
if (empty(local_insts) && empty(insts) && empty(tacs))
|
||||
return lazy_list<constraints>(); // nothing to be done
|
||||
// we are always strict with placeholders associated with classes
|
||||
bool ignore_failure = false;
|
||||
return choose(std::make_shared<placeholder_elaborator>(C, meta, meta_type, local_insts, insts, tacs,
|
||||
j, ignore_failure));
|
||||
} else if (s.is_assigned(mvar)) {
|
||||
// if the metavariable is assigned and it is not a class, then we just ignore it, and return
|
||||
// the an empty set of constraints.
|
||||
return lazy_list<constraints>(constraints());
|
||||
return choose(std::make_shared<placeholder_elaborator>(C, meta, meta_type, local_insts, insts, tacs, j));
|
||||
} else {
|
||||
list<tactic_hint_entry> tacs = get_tactic_hints(env);
|
||||
bool ignore_failure = !is_strict;
|
||||
return choose(std::make_shared<placeholder_elaborator>(C, meta, meta_type, list<expr>(), list<name>(),
|
||||
tacs, j, ignore_failure));
|
||||
// do nothing, type is not a class...
|
||||
return lazy_list<constraints>(constraints());
|
||||
}
|
||||
};
|
||||
bool owner = false;
|
||||
|
@ -245,52 +236,82 @@ constraint mk_placeholder_cnstr(std::shared_ptr<placeholder_context> const & C,
|
|||
}
|
||||
|
||||
pair<expr, constraint> mk_placeholder_elaborator(std::shared_ptr<placeholder_context> const & C,
|
||||
bool is_strict, optional<expr> const & type, tag g) {
|
||||
optional<expr> const & type, tag g) {
|
||||
expr m = C->m_ctx.mk_meta(C->m_ngen, type, g);
|
||||
constraint c = mk_placeholder_cnstr(C, m, is_strict);
|
||||
constraint c = mk_placeholder_cnstr(C, m);
|
||||
return mk_pair(m, c);
|
||||
}
|
||||
|
||||
/** \brief Similar to has_expr_metavar, but ignores metavariables occurring in the type
|
||||
of local constants */
|
||||
static bool has_expr_metavar_relaxed(expr const & e) {
|
||||
if (!has_expr_metavar(e))
|
||||
return false;
|
||||
bool found = false;
|
||||
for_each(e, [&](expr const & e, unsigned) {
|
||||
if (found || !has_expr_metavar(e))
|
||||
return false;
|
||||
if (is_metavar(e)) {
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
if (is_local(e))
|
||||
return false; // do not visit type
|
||||
return true;
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
constraint mk_placeholder_root_cnstr(std::shared_ptr<placeholder_context> const & C, expr const & m, bool is_strict,
|
||||
unifier_config const & cfg, unsigned delay_factor) {
|
||||
environment const & env = C->env();
|
||||
justification j = mk_failed_to_synthesize_jst(env, m);
|
||||
auto choice_fn = [=](expr const & meta, expr const & meta_type, substitution const & s,
|
||||
name_generator const & ngen) {
|
||||
if (has_expr_metavar(meta_type)) {
|
||||
if (has_expr_metavar_relaxed(meta_type)) {
|
||||
if (delay_factor < to_delay_factor(cnstr_group::ClassInstance)) {
|
||||
constraint delayed_c = mk_placeholder_root_cnstr(C, m, is_strict, cfg, delay_factor+1);
|
||||
return lazy_list<constraints>(constraints(delayed_c));
|
||||
}
|
||||
}
|
||||
expr const & mvar = get_app_fn(meta);
|
||||
if (!is_ext_class(C->tc(), meta_type) && s.is_assigned(mvar)) {
|
||||
// see mk_placeholder_cnstr
|
||||
if (!is_ext_class(C->tc(), meta_type)) {
|
||||
// do nothing, since type is not a class.
|
||||
return lazy_list<constraints>(constraints());
|
||||
}
|
||||
pair<expr, justification> mj = update_meta(meta, s);
|
||||
expr new_meta = mj.first;
|
||||
justification new_j = mj.second;
|
||||
constraint c = mk_placeholder_cnstr(C, new_meta, is_strict);
|
||||
constraint c = mk_placeholder_cnstr(C, new_meta);
|
||||
unifier_config new_cfg(cfg);
|
||||
new_cfg.m_discard = false;
|
||||
new_cfg.m_use_exceptions = false;
|
||||
unify_result_seq seq = unify(env, 1, &c, ngen, new_cfg);
|
||||
return map2<constraints>(seq, [=](pair<substitution, constraints> const & p) {
|
||||
unify_result_seq seq1 = unify(env, 1, &c, ngen, new_cfg);
|
||||
unify_result_seq seq2 = filter(seq1, [=](pair<substitution, constraints> const & p) {
|
||||
substitution new_s = p.first;
|
||||
if (!new_s.is_expr_assigned(mlocal_name(get_app_fn(new_meta))))
|
||||
return constraints();
|
||||
constraints postponed = map(p.second,
|
||||
[&](constraint const & c) {
|
||||
// we erase internal justifications
|
||||
return update_justification(c, new_j);
|
||||
});
|
||||
metavar_closure cls(new_meta);
|
||||
cls.add(meta_type);
|
||||
bool relax = C->m_relax;
|
||||
constraints cs = cls.mk_constraints(new_s, new_j, relax);
|
||||
return append(cs, postponed);
|
||||
expr result = new_s.instantiate(new_meta);
|
||||
// We only keep complete solution (modulo universe metavariables)
|
||||
return !has_expr_metavar_relaxed(result);
|
||||
});
|
||||
lazy_list<constraints> seq3 = map2<constraints>(seq2, [=](pair<substitution, constraints> const & p) {
|
||||
substitution new_s = p.first;
|
||||
// some constraints may have been postponed (example: universe level constraints)
|
||||
constraints postponed = map(p.second,
|
||||
[&](constraint const & c) {
|
||||
// we erase internal justifications
|
||||
return update_justification(c, new_j);
|
||||
});
|
||||
metavar_closure cls(new_meta);
|
||||
cls.add(meta_type);
|
||||
bool relax = C->m_relax;
|
||||
constraints cs = cls.mk_constraints(new_s, new_j, relax);
|
||||
return append(cs, postponed);
|
||||
});
|
||||
if (is_strict) {
|
||||
return seq3;
|
||||
} else {
|
||||
// make sure it does not fail by appending empty set of constraints
|
||||
return append(seq3, lazy_list<constraints>(constraints()));
|
||||
}
|
||||
};
|
||||
bool owner = false;
|
||||
bool relax = C->m_relax;
|
||||
|
|
|
@ -4,22 +4,15 @@ open tactic
|
|||
inductive inh (A : Type) : Type :=
|
||||
intro : A -> inh A
|
||||
|
||||
instance inh.intro
|
||||
|
||||
theorem inh_bool [instance] : inh Prop
|
||||
:= inh.intro true
|
||||
|
||||
theorem inh_fun [instance] {A B : Type} (H : inh B) : inh (A → B)
|
||||
:= inh.rec (λ b, inh.intro (λ a : A, b)) H
|
||||
|
||||
definition assump := eassumption; now
|
||||
|
||||
set_option elaborator.local_instances false
|
||||
tactic_hint [inh] assump
|
||||
tactic_hint assump
|
||||
|
||||
theorem tst {A B : Type} (H : inh B) : inh (A → B → B)
|
||||
|
||||
theorem T1 {A : Type} (a : A) : inh A
|
||||
theorem T1 {A : Type} (a : A) : inh A :=
|
||||
by repeat [apply @inh.intro | eassumption]
|
||||
|
||||
theorem T2 : inh Prop
|
||||
|
|
|
@ -9,7 +9,7 @@ instance inh.intro
|
|||
theorem inh_elim {A : Type} {B : Prop} (H1 : inh A) (H2 : A → B) : B
|
||||
:= inh.rec H2 H1
|
||||
|
||||
theorem inh_exists [instance] {A : Type} {P : A → Prop} (H : ∃x, P x) : inh A
|
||||
theorem inh_exists {A : Type} {P : A → Prop} (H : ∃x, P x) : inh A
|
||||
:= obtain w Hw, from H, inh.intro w
|
||||
|
||||
theorem inh_bool [instance] : inh Prop
|
||||
|
@ -26,7 +26,10 @@ tactic_hint assump
|
|||
|
||||
theorem tst {A B : Type} (H : inh B) : inh (A → B → B)
|
||||
|
||||
theorem T1 {A B C D : Type} {P : C → Prop} (a : A) (H1 : inh B) (H2 : ∃x, P x) : inh ((A → A) × B × (D → C) × Prop)
|
||||
theorem T1 {A B C D : Type} {P : C → Prop} (a : A) (H1 : inh B) (H2 : ∃x, P x) : inh ((A → A) × B × (D → C) × Prop) :=
|
||||
have h1 [visible] : inh A, from inh.intro a,
|
||||
have h2 [visible] : inh C, from inh_exists H2,
|
||||
_
|
||||
|
||||
(*
|
||||
print(get_env():find("T1"):value())
|
||||
|
|
Loading…
Reference in a new issue