fix(library/unifier): use depth-first search strategy for solving class-instance constraints
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
030eec1d06
commit
1436212a34
4 changed files with 63 additions and 9 deletions
|
@ -637,7 +637,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::DelayedChoice2), false, j, m_relax_main_opaque));
|
add_cnstr(mk_choice_cnstr(m, choice_fn, to_delay_factor(cnstr_group::ClassInstance), false, j, m_relax_main_opaque));
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,7 +762,7 @@ public:
|
||||||
auto choice_fn = [=](expr const & mvar, expr const & new_d_type, substitution const & /* s */, name_generator const & /* ngen */) {
|
auto choice_fn = [=](expr const & mvar, expr const & new_d_type, substitution const & /* s */, name_generator const & /* ngen */) {
|
||||||
// Remark: we want the coercions solved before we start discarding FlexFlex constraints. So, we use PreFlexFlex as a max cap
|
// Remark: we want the coercions solved before we start discarding FlexFlex constraints. So, we use PreFlexFlex as a max cap
|
||||||
// for delaying coercions.
|
// for delaying coercions.
|
||||||
if (is_meta(new_d_type) && delay_factor < to_delay_factor(cnstr_group::DelayedChoice1)) {
|
if (is_meta(new_d_type) && delay_factor < to_delay_factor(cnstr_group::DelayedChoice)) {
|
||||||
// The type is still unknown, delay the constraint even more.
|
// The type is still unknown, delay the constraint even more.
|
||||||
return lazy_list<constraints>(constraints(mk_delayed_coercion_cnstr(m, a, a_type, justification(), delay_factor+1)));
|
return lazy_list<constraints>(constraints(mk_delayed_coercion_cnstr(m, a, a_type, justification(), delay_factor+1)));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,6 +7,7 @@ Author: Leonardo de Moura
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <limits>
|
||||||
#include "util/interrupt.h"
|
#include "util/interrupt.h"
|
||||||
#include "util/luaref.h"
|
#include "util/luaref.h"
|
||||||
#include "util/lazy_list_fn.h"
|
#include "util/lazy_list_fn.h"
|
||||||
|
@ -212,6 +213,15 @@ static unsigned g_cnstr_group_first_index[g_num_groups] = { 0, g_group_size, 2*g
|
||||||
static unsigned get_group_first_index(cnstr_group g) {
|
static unsigned get_group_first_index(cnstr_group g) {
|
||||||
return g_cnstr_group_first_index[static_cast<unsigned>(g)];
|
return g_cnstr_group_first_index[static_cast<unsigned>(g)];
|
||||||
}
|
}
|
||||||
|
static unsigned get_group_last_index(cnstr_group g) {
|
||||||
|
unsigned g_idx = static_cast<unsigned>(g);
|
||||||
|
if (g_idx + 1 < g_num_groups) {
|
||||||
|
lean_assert(g_cnstr_group_first_index[g_idx+1] != 0);
|
||||||
|
return g_cnstr_group_first_index[g_idx+1]-1;
|
||||||
|
} else {
|
||||||
|
return std::numeric_limits<unsigned>::max();
|
||||||
|
}
|
||||||
|
}
|
||||||
static cnstr_group to_cnstr_group(unsigned d) {
|
static cnstr_group to_cnstr_group(unsigned d) {
|
||||||
if (d >= g_num_groups)
|
if (d >= g_num_groups)
|
||||||
d = g_num_groups-1;
|
d = g_num_groups-1;
|
||||||
|
@ -411,7 +421,13 @@ struct unifier_fn {
|
||||||
|
|
||||||
/** \brief Add constraint to the constraint queue */
|
/** \brief Add constraint to the constraint queue */
|
||||||
unsigned add_cnstr(constraint const & c, cnstr_group g) {
|
unsigned add_cnstr(constraint const & c, cnstr_group g) {
|
||||||
unsigned cidx = m_next_cidx + get_group_first_index(g);
|
unsigned cidx;
|
||||||
|
if (g == cnstr_group::ClassInstance) {
|
||||||
|
// we use a stack discipline for solving class instances
|
||||||
|
cidx = get_group_last_index(g) - m_next_cidx;
|
||||||
|
} else {
|
||||||
|
cidx = m_next_cidx + get_group_first_index(g);
|
||||||
|
}
|
||||||
m_cnstrs.insert(cnstr(c, cidx));
|
m_cnstrs.insert(cnstr(c, cidx));
|
||||||
m_next_cidx++;
|
m_next_cidx++;
|
||||||
return cidx;
|
return cidx;
|
||||||
|
@ -1845,8 +1861,8 @@ struct unifier_fn {
|
||||||
lean_assert(!m_tc[1]->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::DelayedChoice2))
|
if (!m_expensive && cidx >= get_group_first_index(cnstr_group::ClassInstance))
|
||||||
m_pattern = true; // use only higher-order (pattern) matching after we start processing MaxDelayed (aka class-instance constraints)
|
m_pattern = true; // use only higher-order (pattern) matching after we start processing class-instance constraints
|
||||||
constraint c = p->first;
|
constraint c = p->first;
|
||||||
// std::cout << "process_next: " << c << "\n";
|
// std::cout << "process_next: " << c << "\n";
|
||||||
m_cnstrs.erase_min();
|
m_cnstrs.erase_min();
|
||||||
|
|
|
@ -50,7 +50,7 @@ lazy_list<substitution> unify(environment const & env, expr const & lhs, expr co
|
||||||
bool relax_main_opaque, substitution const & s, options const & o);
|
bool relax_main_opaque, substitution const & s, options const & o);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The unifier divides the constraints in 6 groups: Simple, Basic, FlexRigid, PluginDelayed, DelayedChoice1, DelayedChoice2, FlexFlex, MaxDelayed
|
The unifier divides the constraints in 8 groups: Simple, Basic, FlexRigid, PluginDelayed, DelayedChoice, ClassInstance, FlexFlex, MaxDelayed
|
||||||
|
|
||||||
1) Simple: constraints that never create case-splits. Example: pattern matching constraints (?M l_1 ... l_n) =?= t.
|
1) Simple: constraints that never create case-splits. Example: pattern matching constraints (?M l_1 ... l_n) =?= t.
|
||||||
The are not even inserted in the constraint priority queue.
|
The are not even inserted in the constraint priority queue.
|
||||||
|
@ -64,9 +64,9 @@ lazy_list<substitution> unify(environment const & env, expr const & lhs, expr co
|
||||||
where elim is an eliminator/recursor and intro is an introduction/constructor.
|
where elim is an eliminator/recursor and intro is an introduction/constructor.
|
||||||
This constraints are delayed because after ?m is assigned we may be able to reduce them.
|
This constraints are delayed because after ?m is assigned we may be able to reduce them.
|
||||||
|
|
||||||
5) DelayedChoice1: for delayed choice constraints (we use this group for the maximally delayed coercions constraints).
|
5) DelayedChoice: for delayed choice constraints (we use this group for the maximally delayed coercions constraints).
|
||||||
|
|
||||||
6) DelayedChoice2: for delayed choice constraints (we use this group for class-instance).
|
6) ClassInstance: for delayed choice constraints (we use this group for class-instance).
|
||||||
|
|
||||||
7) FlexFlex: (?m1 ...) =?= (?m2 ...) we don't try to solve this constraint, we delay them and hope the other
|
7) FlexFlex: (?m1 ...) =?= (?m2 ...) we don't try to solve this constraint, we delay them and hope the other
|
||||||
ones instantiate ?m1 or ?m2. If this kind of constraint is the next to be processed in the queue, then
|
ones instantiate ?m1 or ?m2. If this kind of constraint is the next to be processed in the queue, then
|
||||||
|
@ -74,7 +74,7 @@ lazy_list<substitution> unify(environment const & env, expr const & lhs, expr co
|
||||||
|
|
||||||
8) MaxDelayed: maximally delayed constraint group
|
8) MaxDelayed: maximally delayed constraint group
|
||||||
*/
|
*/
|
||||||
enum class cnstr_group { Basic = 0, FlexRigid, PluginDelayed, DelayedChoice1, DelayedChoice2, FlexFlex, MaxDelayed };
|
enum class cnstr_group { Basic = 0, FlexRigid, PluginDelayed, DelayedChoice, ClassInstance, FlexFlex, MaxDelayed };
|
||||||
inline unsigned to_delay_factor(cnstr_group g) { return static_cast<unsigned>(g); }
|
inline unsigned to_delay_factor(cnstr_group g) { return static_cast<unsigned>(g); }
|
||||||
|
|
||||||
class unifier_exception : public exception {
|
class unifier_exception : public exception {
|
||||||
|
|
38
tests/lean/run/rel.lean
Normal file
38
tests/lean/run/rel.lean
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import logic struc.relation
|
||||||
|
using relation
|
||||||
|
|
||||||
|
namespace is_equivalence
|
||||||
|
|
||||||
|
inductive class {T : Type} (R : T → T → Type) : Prop :=
|
||||||
|
| mk : is_reflexive.class R → is_symmetric.class R → is_transitive.class R → class R
|
||||||
|
|
||||||
|
theorem is_reflexive {T : Type} {R : T → T → Type} {C : class R} : is_reflexive.class R :=
|
||||||
|
class_rec (λx y z, x) C
|
||||||
|
|
||||||
|
theorem is_symmetric {T : Type} {R : T → T → Type} {C : class R} : is_symmetric.class R :=
|
||||||
|
class_rec (λx y z, y) C
|
||||||
|
|
||||||
|
theorem is_transitive {T : Type} {R : T → T → Type} {C : class R} : is_transitive.class R :=
|
||||||
|
class_rec (λx y z, z) C
|
||||||
|
|
||||||
|
end is_equivalence
|
||||||
|
|
||||||
|
instance is_equivalence.is_reflexive
|
||||||
|
instance is_equivalence.is_symmetric
|
||||||
|
instance is_equivalence.is_transitive
|
||||||
|
|
||||||
|
theorem and_inhabited_left {a : Prop} (b : Prop) (Ha : a) : a ∧ b ↔ b :=
|
||||||
|
iff_intro (take Hab, and_elim_right Hab) (take Hb, and_intro Ha Hb)
|
||||||
|
|
||||||
|
theorem test (a b c : Prop) (P : Prop → Prop) (H1 : a ↔ b) (H2 : c ∧ a) : c ∧ b :=
|
||||||
|
gensubst.subst H1 H2
|
||||||
|
|
||||||
|
theorem test2 (Q R S : Prop) (H3 : R ↔ Q) (H1 : S) : Q ↔ (S ∧ Q) :=
|
||||||
|
relation.operations.symm (and_inhabited_left Q H1)
|
||||||
|
|
||||||
|
theorem test3 (Q R S : Prop) (H3 : R ↔ Q) (H1 : S) : R ↔ (S ∧ Q) :=
|
||||||
|
gensubst.subst (test2 Q R S H3 H1) H3
|
||||||
|
|
||||||
|
-- the composition of test2' and test3' fails
|
||||||
|
theorem test4 (Q R S : Prop) (H3 : R ↔ Q) (H1 : S) : R ↔ (S ∧ Q) :=
|
||||||
|
gensubst.subst (relation.operations.symm (and_inhabited_left Q H1)) H3
|
Loading…
Reference in a new issue