refactor(library/unifier): group flex_rigid case related methods in a functional object

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2014-08-08 16:44:59 -07:00
parent 49070895d1
commit 9d13f634f3

View file

@ -1163,6 +1163,25 @@ struct unifier_fn {
return is_eq_cnstr(c) && is_meta(cnstr_lhs_expr(c)) && is_meta(cnstr_rhs_expr(c)); return is_eq_cnstr(c) && is_meta(cnstr_lhs_expr(c)) && is_meta(cnstr_rhs_expr(c));
} }
/** \brief Append the auxiliary constraints \c aux to each alternative in \c alts */
void append_auxiliary_constraints(buffer<constraints> & alts, list<constraint> const & aux) {
if (is_nil(aux))
return;
for (constraints & cs : alts)
cs = append(aux, cs);
}
/** \brief Auxiliary functional object for implementing process_flex_rigid_core */
class flex_rigid_core_fn {
unifier_fn & u;
expr const & lhs;
buffer<expr> margs;
expr const & m;
expr const & rhs;
justification const & j;
bool relax;
buffer<constraints> & alts; // result: alternatives
/** /**
\brief Given t \brief Given t
@ -1172,7 +1191,7 @@ struct unifier_fn {
\remark v has free variables. \remark v has free variables.
*/ */
expr mk_lambda_for(expr const & t, expr const & v) { static expr mk_lambda_for(expr const & t, expr const & v) {
if (is_pi(t)) { if (is_pi(t)) {
return mk_lambda(binding_name(t), binding_domain(t), mk_lambda_for(binding_body(t), v), binding_info(t)); return mk_lambda(binding_name(t), binding_domain(t), mk_lambda_for(binding_body(t), v), binding_info(t));
} else { } else {
@ -1180,22 +1199,26 @@ struct unifier_fn {
} }
} }
/** \see ensure_sufficient_args */ /** \brief Copy pending constraints in u.m_tc[relax] to cs and append justification j to them */
expr ensure_sufficient_args_core(expr mtype, unsigned i, buffer<expr> const & margs, bool relax) { void copy_pending_constraints(buffer<constraint> & cs) {
if (i == margs.size()) while (auto c = u.m_tc[relax]->next_cnstr())
return mtype; cs.push_back(update_justification(*c, mk_composite1(c->get_justification(), j)));
mtype = m_tc[relax]->ensure_pi(mtype);
expr local = mk_local_for(mtype);
expr body = instantiate(binding_body(mtype), local);
return Pi(local, ensure_sufficient_args_core(body, i+1, margs, relax));
} }
/** /** \see ensure_sufficient_args */
\brief Make sure mtype is a Pi of size at least margs.size(). expr ensure_sufficient_args_core(expr mtype, unsigned i) {
if (i == margs.size())
return mtype;
mtype = u.m_tc[relax]->ensure_pi(mtype);
expr local = u.mk_local_for(mtype);
expr body = instantiate(binding_body(mtype), local);
return Pi(local, ensure_sufficient_args_core(body, i+1));
}
/** \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<expr> const & margs, buffer<constraint> & cs, expr ensure_sufficient_args(expr const & mtype, buffer<constraint> & cs) {
justification const & j, bool relax) {
expr t = mtype; expr t = mtype;
unsigned num = 0; unsigned num = 0;
while (is_pi(t)) { while (is_pi(t)) {
@ -1204,15 +1227,99 @@ struct unifier_fn {
} }
if (num == margs.size()) if (num == margs.size())
return mtype; return mtype;
lean_assert(!m_tc[relax]->next_cnstr()); // make sure there are no pending constraints lean_assert(!u.m_tc[relax]->next_cnstr()); // make sure there are no pending constraints
// We must create a scope to make sure no constraints "leak" into the current state. // We must create a scope to make sure no constraints "leak" into the current state.
type_checker::scope scope(*m_tc[relax]); type_checker::scope scope(*u.m_tc[relax]);
auto new_mtype = ensure_sufficient_args_core(mtype, 0, margs, relax); auto new_mtype = ensure_sufficient_args_core(mtype, 0);
while (auto c = m_tc[relax]->next_cnstr()) copy_pending_constraints(cs);
cs.push_back(update_justification(*c, mk_composite1(c->get_justification(), j)));
return new_mtype; return new_mtype;
} }
/**
\brief Given
m := a metavariable ?m
rhs := sort, constant
Then solve (?m a_1 ... a_k) =?= rhs, by returning the constraint
?m =?= fun (x1 ... x_k), rhs
*/
void mk_simple_imitation() {
lean_assert(is_metavar(m));
lean_assert(is_sort(rhs) || is_constant(rhs));
expr const & mtype = mlocal_type(m);
buffer<constraint> cs;
cs.push_back(mk_eq_cnstr(m, mk_lambda_for(mtype, rhs), j, relax));
alts.push_back(to_list(cs.begin(), cs.end()));
}
/**
Given,
m := a metavariable ?m
margs := [a_1 ... a_k]
We say a unification problem (?m a_1 ... a_k) =?= rhs uses "simple nonlocal i-th projection" when
1) rhs is not a local constant
2) is_def_eq(a_i, rhs) does not fail
In this case, we add
a_i =?= rhs
?m =?= fun x_1 ... x_k, x_i
to alts as a possible solution.
*/
void mk_simple_nonlocal_projection(unsigned i) {
expr const & mtype = mlocal_type(m);
unsigned vidx = margs.size() - i - 1;
expr const & marg = margs[i];
buffer<constraint> 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.
// The unifier assumes the eq constraints are reduced.
if (u.m_tc[relax]->is_def_eq_types(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));
cs.push_back(mk_eq_cnstr(m, v, j, relax));
alts.push_back(to_list(cs.begin(), cs.end()));
}
}
/**
Given,
m := a metavariable ?m
margs := [a_1 ... a_k]
We say a unification problem (?m a_1 ... a_k) =?= rhs uses "simple projections" when
If (rhs and a_i are *not* local constants) OR (rhs is a local constant and a_i is a metavariable application),
then we add the constraints
a_i =?= rhs
?m =?= fun x_1 ... x_k, x_i
to alts as a possible solution.
If rhs is a local constant and a_i == rhs, then we add the constraint
?m =?= fun x_1 ... x_k, x_i
to alts as a possible solution when a_i is the same local constant or a metavariable application
*/
void mk_simple_projections() {
lean_assert(is_metavar(m));
lean_assert(!is_meta(rhs));
expr const & mtype = mlocal_type(m);
unsigned i = margs.size();
while (i > 0) {
unsigned vidx = margs.size() - i;
--i;
expr const & marg = margs[i];
if ((!is_local(marg) && !is_local(rhs)) || (is_meta(marg) && is_local(rhs))) {
// if rhs is not local, then we only add projections for the nonlocal arguments of lhs
mk_simple_nonlocal_projection(i);
} 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
buffer<constraint> cs;
auto new_mtype = ensure_sufficient_args(mtype, cs);
expr v = mk_lambda_for(new_mtype, mk_var(vidx));
cs.push_back(mk_eq_cnstr(m, v, j, relax));
alts.push_back(to_list(cs.begin(), cs.end()));
}
}
}
/** /**
\see mk_flex_rigid_app_cnstrs \see mk_flex_rigid_app_cnstrs
When using "imitation" for solving a constraint When using "imitation" for solving a constraint
@ -1229,7 +1336,7 @@ struct unifier_fn {
Result: none if it is not an easy argument, and variable #k-i-1 if it is easy. Result: none if it is not an easy argument, and variable #k-i-1 if it is easy.
The variable is the "solution". The variable is the "solution".
*/ */
optional<expr> is_easy_flex_rigid_arg(buffer<expr> const & margs, expr const & arg) { optional<expr> is_easy_flex_rigid_arg(expr const & arg) {
if (!is_local(arg)) if (!is_local(arg))
return none_expr(); return none_expr();
optional<expr> v; optional<expr> v;
@ -1249,18 +1356,18 @@ struct unifier_fn {
/** \brief Check if term \c e (produced by an imitation step) is /** \brief Check if term \c e (produced by an imitation step) is
type correct, and store generated constraints in \c cs. type correct, and store generated constraints in \c cs.
Include \c j in all generated constraints */ Include \c j in all generated constraints */
bool check_imitation(expr e, justification const & j, bool relax, buffer<constraint> & cs) { bool check_imitation(expr e, buffer<constraint> & cs) {
buffer<expr> ls; buffer<expr> ls;
while (is_lambda(e)) { while (is_lambda(e)) {
expr d = instantiate_rev(binding_domain(e), ls.size(), ls.data()); expr d = instantiate_rev(binding_domain(e), ls.size(), ls.data());
expr l = mk_local(m_ngen.next(), binding_name(e), d, binding_info(e)); expr l = mk_local(u.m_ngen.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());;
try { try {
buffer<constraint> aux; buffer<constraint> aux;
m_tc[relax]->check(e, aux); u.m_tc[relax]->check(e, aux);
for (auto c : aux) { for (auto c : aux) {
cs.push_back(update_justification(c, mk_composite1(j, c.get_justification()))); cs.push_back(update_justification(c, mk_composite1(j, c.get_justification())));
} }
@ -1288,29 +1395,28 @@ struct unifier_fn {
- g (if g is a constant), OR - g (if g is a constant), OR
- variable (if g is a local constant equal to a_i) - variable (if g is a local constant equal to a_i)
*/ */
void mk_flex_rigid_app_cnstrs(expr const & m, buffer<expr> const & margs, expr const & f, expr const & rhs, justification const & j, void mk_flex_rigid_app_cnstrs(expr const & f) {
buffer<constraints> & alts, bool relax) {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_app(rhs)); lean_assert(is_app(rhs));
lean_assert(is_constant(f) || is_var(f)); lean_assert(is_constant(f) || is_var(f));
buffer<constraint> cs; buffer<constraint> cs;
expr mtype = mlocal_type(m); expr mtype = mlocal_type(m);
mtype = ensure_sufficient_args(mtype, margs, cs, j, relax); mtype = ensure_sufficient_args(mtype, cs);
buffer<expr> rargs; buffer<expr> rargs;
get_app_args(rhs, rargs); get_app_args(rhs, rargs);
buffer<expr> sargs; buffer<expr> sargs;
for (expr const & rarg : rargs) { for (expr const & rarg : rargs) {
if (auto v = is_easy_flex_rigid_arg(margs, rarg)) { if (auto v = is_easy_flex_rigid_arg(rarg)) {
sargs.push_back(*v); sargs.push_back(*v);
} else { } else {
expr maux = mk_aux_metavar_for(m_ngen, mtype); expr maux = mk_aux_metavar_for(u.m_ngen, mtype);
cs.push_back(mk_eq_cnstr(mk_app(maux, margs), rarg, j, relax)); cs.push_back(mk_eq_cnstr(mk_app(maux, margs), rarg, j, relax));
sargs.push_back(mk_app_vars(maux, margs.size())); sargs.push_back(mk_app_vars(maux, margs.size()));
} }
} }
expr v = mk_app(f, sargs); expr v = mk_app(f, sargs);
v = mk_lambda_for(mtype, v); v = mk_lambda_for(mtype, v);
if (check_imitation(v, j, relax, 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()));
} }
@ -1327,45 +1433,28 @@ struct unifier_fn {
?m =?= fun (x_1 ... x_k), fun/Pi (y : ?m_1 x_1 ... x_k), ?m_2 x_1 ... x_k y ?m =?= fun (x_1 ... x_k), fun/Pi (y : ?m_1 x_1 ... x_k), ?m_2 x_1 ... x_k y
where l is a fresh local constant. where l is a fresh local constant.
*/ */
void mk_bindings_imitation(expr const & m, buffer<expr> const & margs, expr const & rhs, justification const & j, void mk_bindings_imitation() {
buffer<constraints> & alts, bool relax) {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_binding(rhs)); lean_assert(is_binding(rhs));
buffer<constraint> cs; buffer<constraint> cs;
expr mtype = mlocal_type(m); expr mtype = mlocal_type(m);
mtype = ensure_sufficient_args(mtype, margs, cs, j, relax); mtype = ensure_sufficient_args(mtype, cs);
expr maux1 = mk_aux_metavar_for(m_ngen, mtype); expr maux1 = mk_aux_metavar_for(u.m_ngen, mtype);
cs.push_back(mk_eq_cnstr(mk_app(maux1, margs), binding_domain(rhs), j, relax)); cs.push_back(mk_eq_cnstr(mk_app(maux1, margs), binding_domain(rhs), j, relax));
expr dontcare; expr dontcare;
expr tmp_pi = mk_pi(binding_name(rhs), mk_app_vars(maux1, margs.size()), dontcare); // trick for "extending" the context expr tmp_pi = mk_pi(binding_name(rhs), mk_app_vars(maux1, margs.size()), dontcare); // trick for "extending" the context
expr mtype2 = replace_range(mtype, tmp_pi); // trick for "extending" the context expr mtype2 = replace_range(mtype, tmp_pi); // trick for "extending" the context
expr maux2 = mk_aux_metavar_for(m_ngen, mtype2); expr maux2 = mk_aux_metavar_for(u.m_ngen, mtype2);
expr new_local = mk_local_for(rhs); expr new_local = u.mk_local_for(rhs);
cs.push_back(mk_eq_cnstr(mk_app(mk_app(maux2, margs), new_local), instantiate(binding_body(rhs), new_local), j, relax)); cs.push_back(mk_eq_cnstr(mk_app(mk_app(maux2, margs), new_local), instantiate(binding_body(rhs), new_local), j, relax));
expr v = update_binding(rhs, mk_app_vars(maux1, margs.size()), mk_app_vars(maux2, margs.size() + 1)); expr v = update_binding(rhs, mk_app_vars(maux1, margs.size()), mk_app_vars(maux2, margs.size() + 1));
v = mk_lambda_for(mtype, v); v = mk_lambda_for(mtype, v);
if (check_imitation(v, j, relax, 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()));
} }
} }
/**
\brief Given
m := a metavariable ?m
rhs := sort, constant
Then solve (?m a_1 ... a_k) =?= rhs, by returning the constraint
?m =?= fun (x1 ... x_k), rhs
*/
void mk_simple_imitation(expr const & m, expr const & rhs, justification const & j, buffer<constraints> & alts, bool relax) {
lean_assert(is_metavar(m));
lean_assert(is_sort(rhs) || is_constant(rhs));
expr const & mtype = mlocal_type(m);
buffer<constraint> cs;
cs.push_back(mk_eq_cnstr(m, mk_lambda_for(mtype, rhs), j, relax));
alts.push_back(to_list(cs.begin(), cs.end()));
}
/** /**
\brief Given \brief Given
m := a metavariable ?m m := a metavariable ?m
@ -1377,131 +1466,53 @@ struct unifier_fn {
(?m_n a_1 ... a_k) =?= b_n (?m_n a_1 ... a_k) =?= b_n
?m =?= fun (x_1 ... x_k), M((?m_1 x_1 ... x_k) ... (?m_n x_1 ... x_k)) ?m =?= fun (x_1 ... x_k), M((?m_1 x_1 ... x_k) ... (?m_n x_1 ... x_k))
*/ */
void mk_macro_imitation(expr const & m, buffer<expr> const & margs, expr const & rhs, justification const & j, void mk_macro_imitation() {
buffer<constraints> & alts, bool relax) {
lean_assert(is_metavar(m)); lean_assert(is_metavar(m));
lean_assert(is_macro(rhs)); lean_assert(is_macro(rhs));
buffer<constraint> cs; buffer<constraint> cs;
expr mtype = mlocal_type(m); expr mtype = mlocal_type(m);
mtype = ensure_sufficient_args(mtype, margs, cs, j, relax); 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(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.push_back(mk_eq_cnstr(mk_app(maux, margs), macro_arg(rhs, i), j, relax));
sargs.push_back(mk_app_vars(maux, margs.size())); sargs.push_back(mk_app_vars(maux, margs.size()));
} }
expr v = mk_macro(macro_def(rhs), sargs.size(), sargs.data()); expr v = mk_macro(macro_def(rhs), sargs.size(), sargs.data());
v = mk_lambda_for(mtype, v); v = mk_lambda_for(mtype, v);
if (check_imitation(v, j, relax, 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()));
} }
} }
/** public:
Given, flex_rigid_core_fn(unifier_fn & _u, expr const & _lhs, expr const & _rhs,
m := a metavariable ?m justification const & _j, bool _relax, buffer<constraints> & _alts):
margs := [a_1 ... a_k] u(_u), lhs(_lhs), m(get_app_args(lhs, margs)), rhs(_rhs), j(_j), relax(_relax), alts(_alts) {}
We say a unification problem (?m a_1 ... a_k) =?= rhs uses "simple nonlocal i-th projection" when
1) rhs is not a local constant void operator()() {
2) is_def_eq(a_i, rhs) does not fail
In this case, we add
a_i =?= rhs
?m =?= fun x_1 ... x_k, x_i
to alts as a possible solution.
*/
void mk_simple_nonlocal_projection(expr const & m, buffer<expr> const & margs, unsigned i, expr const & rhs, justification const & j,
buffer<constraints> & alts, bool relax) {
expr const & mtype = mlocal_type(m);
unsigned vidx = margs.size() - i - 1;
expr const & marg = margs[i];
buffer<constraint> cs;
auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j, relax);
// Remark: we should not use mk_eq_cnstr(marg, rhs, j) since is_def_eq may be able to reduce them.
// The unifier assumes the eq constraints are reduced.
if (m_tc[relax]->is_def_eq_types(marg, rhs, j, cs) &&
m_tc[relax]->is_def_eq(marg, rhs, j, cs)) {
expr v = mk_lambda_for(new_mtype, mk_var(vidx));
cs.push_back(mk_eq_cnstr(m, v, j, relax));
alts.push_back(to_list(cs.begin(), cs.end()));
}
}
/**
Given,
m := a metavariable ?m
margs := [a_1 ... a_k]
We say a unification problem (?m a_1 ... a_k) =?= rhs uses "simple projections" when
If (rhs and a_i are *not* local constants) OR (rhs is a local constant and a_i is a metavariable application),
then we add the constraints
a_i =?= rhs
?m =?= fun x_1 ... x_k, x_i
to alts as a possible solution.
If rhs is a local constant and a_i == rhs, then we add the constraint
?m =?= fun x_1 ... x_k, x_i
to alts as a possible solution when a_i is the same local constant or a metavariable application
*/
void mk_simple_projections(expr const & m, buffer<expr> const & margs, expr const & rhs, justification const & j,
buffer<constraints> & alts, bool relax) {
lean_assert(is_metavar(m));
lean_assert(!is_meta(rhs));
expr const & mtype = mlocal_type(m);
unsigned i = margs.size();
while (i > 0) {
unsigned vidx = margs.size() - i;
--i;
expr const & marg = margs[i];
if ((!is_local(marg) && !is_local(rhs)) || (is_meta(marg) && is_local(rhs))) {
// if rhs is not local, then we only add projections for the nonlocal arguments of lhs
mk_simple_nonlocal_projection(m, margs, i, rhs, j, alts, relax);
} else if (is_local(marg) && is_local(rhs) && mlocal_name(marg) == mlocal_name(rhs)) {
// if the argument is local, and rhs is equal to it, then we also add a projection
buffer<constraint> cs;
auto new_mtype = ensure_sufficient_args(mtype, margs, cs, j, relax);
expr v = mk_lambda_for(new_mtype, mk_var(vidx));
cs.push_back(mk_eq_cnstr(m, v, j, relax));
alts.push_back(to_list(cs.begin(), cs.end()));
}
}
}
/** \brief Append the auxiliary constraints \c aux to each alternative in \c alts */
void append_auxiliary_constraints(buffer<constraints> & alts, list<constraint> const & aux) {
if (is_nil(aux))
return;
for (constraints & cs : alts)
cs = append(aux, cs);
}
void process_flex_rigid_core(expr const & lhs, expr const & rhs, justification const & j, bool relax, buffer<constraints> & alts) {
buffer<expr> margs;
expr m = get_app_args(lhs, margs);
switch (rhs.kind()) { switch (rhs.kind()) {
case expr_kind::Var: case expr_kind::Meta: case expr_kind::Var: case expr_kind::Meta:
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::Local: case expr_kind::Local:
mk_simple_projections(m, margs, rhs, j, alts, relax); mk_simple_projections();
break; break;
case expr_kind::Sort: case expr_kind::Constant: case expr_kind::Sort: case expr_kind::Constant:
if (!m_pattern) if (!u.m_pattern)
mk_simple_projections(m, margs, rhs, j, alts, relax); mk_simple_projections();
mk_simple_imitation(m, rhs, j, alts, relax); mk_simple_imitation();
break; break;
case expr_kind::Pi: case expr_kind::Lambda: case expr_kind::Pi: case expr_kind::Lambda:
if (!m_pattern) if (!u.m_pattern)
mk_simple_projections(m, margs, rhs, j, alts, relax); mk_simple_projections();
mk_bindings_imitation(m, margs, rhs, j, alts, relax); mk_bindings_imitation();
break; break;
case expr_kind::Macro: case expr_kind::Macro:
if (!m_pattern) if (!u.m_pattern)
mk_simple_projections(m, margs, rhs, j, alts, relax); mk_simple_projections();
mk_macro_imitation(m, margs, rhs, j, alts, relax); mk_macro_imitation();
break; break;
case expr_kind::App: { case expr_kind::App: {
expr const & f = get_app_fn(rhs); expr const & f = get_app_fn(rhs);
@ -1512,19 +1523,25 @@ struct unifier_fn {
--i; --i;
expr const & marg = margs[i]; expr const & marg = margs[i];
if (is_local(marg) && mlocal_name(marg) == mlocal_name(f)) if (is_local(marg) && mlocal_name(marg) == mlocal_name(f))
mk_flex_rigid_app_cnstrs(m, margs, mk_var(vidx), rhs, j, alts, relax); mk_flex_rigid_app_cnstrs(mk_var(vidx));
else if (!m_pattern) else if (!u.m_pattern)
mk_simple_nonlocal_projection(m, margs, i, rhs, j, alts, relax); mk_simple_nonlocal_projection(i);
} }
} else { } else {
lean_assert(is_constant(f)); lean_assert(is_constant(f));
if (!m_pattern) if (!u.m_pattern)
mk_simple_projections(m, margs, rhs, j, alts, relax); mk_simple_projections();
mk_flex_rigid_app_cnstrs(m, margs, f, rhs, j, alts, relax); mk_flex_rigid_app_cnstrs(f);
} }
break; break;
}} }}
} }
};
void process_flex_rigid_core(expr const & lhs, expr const & rhs, justification const & j, bool relax,
buffer<constraints> & alts) {
flex_rigid_core_fn(*this, lhs, rhs, j, relax, alts)();
}
/** \brief When lhs is an application (f ...), make sure that if any argument that is reducible to a /** \brief When lhs is an application (f ...), make sure that if any argument that is reducible to a
local constant is replaced with a local constant. local constant is replaced with a local constant.