diff --git a/src/library/unifier.cpp b/src/library/unifier.cpp
index dcdb5a76c..4e65242ac 100644
--- a/src/library/unifier.cpp
+++ b/src/library/unifier.cpp
@@ -1163,314 +1163,6 @@ struct unifier_fn {
return is_eq_cnstr(c) && is_meta(cnstr_lhs_expr(c)) && is_meta(cnstr_rhs_expr(c));
}
-
- /**
- \brief Given t
- Pi (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), B[x_1, ..., x_n]
- return
- fun (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), v[x_1, ... x_n]
-
- \remark v has free variables.
- */
- expr mk_lambda_for(expr const & t, expr const & v) {
- if (is_pi(t)) {
- return mk_lambda(binding_name(t), binding_domain(t), mk_lambda_for(binding_body(t), v), binding_info(t));
- } else {
- return v;
- }
- }
-
- /** \see ensure_sufficient_args */
- expr ensure_sufficient_args_core(expr mtype, unsigned i, buffer const & margs, bool relax) {
- if (i == margs.size())
- return mtype;
- 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));
- }
-
- /**
- \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.
- */
- expr ensure_sufficient_args(expr const & mtype, buffer const & margs, buffer & cs,
- justification const & j, bool relax) {
- expr t = mtype;
- unsigned num = 0;
- while (is_pi(t)) {
- num++;
- t = binding_body(t);
- }
- if (num == margs.size())
- return mtype;
- lean_assert(!m_tc[relax]->next_cnstr()); // make sure there are no pending constraints
- // We must create a scope to make sure no constraints "leak" into the current state.
- type_checker::scope scope(*m_tc[relax]);
- auto new_mtype = ensure_sufficient_args_core(mtype, 0, margs, relax);
- while (auto c = m_tc[relax]->next_cnstr())
- cs.push_back(update_justification(*c, mk_composite1(c->get_justification(), j)));
- return new_mtype;
- }
-
- /**
- \see mk_flex_rigid_app_cnstrs
- When using "imitation" for solving a constraint
- ?m l_1 ... l_k =?= f a_1 ... a_n
- We say argument a_i is "easy" if
- 1) it is a local constant
- 2) there is only one l_j equal to a_i.
- 3) none of the l_j's is of the form (?m ...)
- In our experiments, the vast majority (> 2/3 of all cases) of the arguments are easy.
-
- margs contains l_1 ... l_k
- arg is the argument we are testing
-
- Result: none if it is not an easy argument, and variable #k-i-1 if it is easy.
- The variable is the "solution".
- */
- optional is_easy_flex_rigid_arg(buffer const & margs, expr const & arg) {
- if (!is_local(arg))
- return none_expr();
- optional v;
- unsigned num_margs = margs.size();
- for (unsigned j = 0; j < num_margs; j++) {
- if (is_meta(margs[j]))
- return none_expr();
- if (is_local(margs[j]) && mlocal_name(arg) == mlocal_name(margs[j])) {
- if (v)
- return none_expr(); // failed, there is more than one possibility
- v = mk_var(num_margs - j - 1);
- }
- }
- 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, justification const & j, bool relax, buffer & cs) {
- buffer ls;
- while (is_lambda(e)) {
- 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));
- ls.push_back(l);
- e = binding_body(e);
- }
- e = instantiate_rev(e, ls.size(), ls.data());;
- try {
- buffer aux;
- 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;
- }
- }
-
- /**
- \brief Given
- m := a metavariable ?m
- margs := [a_1 ... a_k]
- rhs := (g b_1 ... b_n)
- Then create the constraints
- (?m_1 a_1 ... a_k) =?= b_1
- ...
- (?m_n a_1 ... a_k) =?= b_n
- ?m =?= fun (x_1 ... x_k), f (?m_1 x_1 ... x_k) ... (?m_n x_1 ... x_k)
-
- Remark: we try to minimize the number of constraints (?m_i a_1 ... a_k) =?= b_i by detecting "easy" cases
- that can be solved immediately. See \c is_easy_flex_rigid_arg
-
- Remark: The term f is:
- - g (if g is a constant), OR
- - variable (if g is a local constant equal to a_i)
- */
- void mk_flex_rigid_app_cnstrs(expr const & m, buffer const & margs, expr const & f, expr const & rhs, justification const & j,
- buffer & alts, bool relax) {
- lean_assert(is_metavar(m));
- lean_assert(is_app(rhs));
- lean_assert(is_constant(f) || is_var(f));
- buffer cs;
- expr mtype = mlocal_type(m);
- mtype = ensure_sufficient_args(mtype, margs, cs, j, relax);
- buffer rargs;
- get_app_args(rhs, rargs);
- buffer sargs;
- for (expr const & rarg : rargs) {
- if (auto v = is_easy_flex_rigid_arg(margs, rarg)) {
- sargs.push_back(*v);
- } else {
- expr maux = mk_aux_metavar_for(m_ngen, mtype);
- cs.push_back(mk_eq_cnstr(mk_app(maux, margs), rarg, j, relax));
- sargs.push_back(mk_app_vars(maux, margs.size()));
- }
- }
- expr v = mk_app(f, sargs);
- v = mk_lambda_for(mtype, v);
- if (check_imitation(v, j, relax, cs)) {
- cs.push_back(mk_eq_cnstr(m, v, j, relax));
- alts.push_back(to_list(cs.begin(), cs.end()));
- }
- }
-
- /**
- \brief Given
- m := a metavariable ?m
- margs := [a_1 ... a_k]
- rhs := (fun/Pi (y : A), B y)
- Then create the constraints
- (?m_1 a_1 ... a_k) =?= A
- (?m_2 a_1 ... a_k l) =?= B l
- ?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.
- */
- void mk_bindings_imitation(expr const & m, buffer const & margs, expr const & rhs, justification const & j,
- buffer & alts, bool relax) {
- lean_assert(is_metavar(m));
- lean_assert(is_binding(rhs));
- buffer cs;
- expr mtype = mlocal_type(m);
- mtype = ensure_sufficient_args(mtype, margs, cs, j, relax);
- expr maux1 = mk_aux_metavar_for(m_ngen, mtype);
- cs.push_back(mk_eq_cnstr(mk_app(maux1, margs), binding_domain(rhs), j, relax));
- expr dontcare;
- 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 maux2 = mk_aux_metavar_for(m_ngen, mtype2);
- expr new_local = mk_local_for(rhs);
- cs.push_back(mk_eq_cnstr(mk_app(mk_app(maux2, margs), new_local), instantiate(binding_body(rhs), new_local), j, relax));
- expr v = update_binding(rhs, mk_app_vars(maux1, margs.size()), mk_app_vars(maux2, margs.size() + 1));
- v = mk_lambda_for(mtype, v);
- if (check_imitation(v, j, relax, cs)) {
- cs.push_back(mk_eq_cnstr(m, v, j, relax));
- 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 & alts, bool relax) {
- lean_assert(is_metavar(m));
- lean_assert(is_sort(rhs) || is_constant(rhs));
- expr const & mtype = mlocal_type(m);
- buffer 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
- m := a metavariable ?m
- margs := [a_1 ... a_k]
- rhs := M(b_1 ... b_n) where M is a macro with arguments b_1 ... b_n
- Then create the constraints
- (?m_1 a_1 ... a_k) =?= b_1
- ...
- (?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))
- */
- void mk_macro_imitation(expr const & m, buffer const & margs, expr const & rhs, justification const & j,
- buffer & alts, bool relax) {
- lean_assert(is_metavar(m));
- lean_assert(is_macro(rhs));
- buffer cs;
- expr mtype = mlocal_type(m);
- mtype = ensure_sufficient_args(mtype, margs, cs, j, relax);
- // create an auxiliary metavariable for each macro argument
- buffer sargs;
- for (unsigned i = 0; i < macro_num_args(rhs); i++) {
- expr maux = mk_aux_metavar_for(m_ngen, mtype);
- 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()));
- }
- expr v = mk_macro(macro_def(rhs), sargs.size(), sargs.data());
- v = mk_lambda_for(mtype, v);
- if (check_imitation(v, j, relax, cs)) {
- 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 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(expr const & m, buffer const & margs, unsigned i, expr const & rhs, justification const & j,
- buffer & alts, bool relax) {
- expr const & mtype = mlocal_type(m);
- unsigned vidx = margs.size() - i - 1;
- expr const & marg = margs[i];
- buffer 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 const & margs, expr const & rhs, justification const & j,
- buffer & 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 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 & alts, list const & aux) {
if (is_nil(aux))
@@ -1479,51 +1171,376 @@ struct unifier_fn {
cs = append(aux, cs);
}
- void process_flex_rigid_core(expr const & lhs, expr const & rhs, justification const & j, bool relax, buffer & alts) {
- buffer margs;
- expr m = get_app_args(lhs, margs);
- switch (rhs.kind()) {
- case expr_kind::Var: case expr_kind::Meta:
- lean_unreachable(); // LCOV_EXCL_LINE
- case expr_kind::Local:
- mk_simple_projections(m, margs, rhs, j, alts, relax);
- break;
- case expr_kind::Sort: case expr_kind::Constant:
- if (!m_pattern)
- mk_simple_projections(m, margs, rhs, j, alts, relax);
- mk_simple_imitation(m, rhs, j, alts, relax);
- break;
- case expr_kind::Pi: case expr_kind::Lambda:
- if (!m_pattern)
- mk_simple_projections(m, margs, rhs, j, alts, relax);
- mk_bindings_imitation(m, margs, rhs, j, alts, relax);
- break;
- case expr_kind::Macro:
- if (!m_pattern)
- mk_simple_projections(m, margs, rhs, j, alts, relax);
- mk_macro_imitation(m, margs, rhs, j, alts, relax);
- break;
- case expr_kind::App: {
- expr const & f = get_app_fn(rhs);
- if (is_local(f)) {
- unsigned i = margs.size();
- while (i > 0) {
- unsigned vidx = margs.size() - i;
- --i;
- expr const & marg = margs[i];
- if (is_local(marg) && mlocal_name(marg) == mlocal_name(f))
- mk_flex_rigid_app_cnstrs(m, margs, mk_var(vidx), rhs, j, alts, relax);
- else if (!m_pattern)
- mk_simple_nonlocal_projection(m, margs, i, rhs, j, alts, relax);
- }
+ /** \brief Auxiliary functional object for implementing process_flex_rigid_core */
+ class flex_rigid_core_fn {
+ unifier_fn & u;
+ expr const & lhs;
+ buffer margs;
+ expr const & m;
+ expr const & rhs;
+ justification const & j;
+ bool relax;
+
+ buffer & alts; // result: alternatives
+
+ /**
+ \brief Given t
+ Pi (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), B[x_1, ..., x_n]
+ return
+ fun (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), v[x_1, ... x_n]
+
+ \remark v has free variables.
+ */
+ static expr mk_lambda_for(expr const & t, expr const & v) {
+ if (is_pi(t)) {
+ return mk_lambda(binding_name(t), binding_domain(t), mk_lambda_for(binding_body(t), v), binding_info(t));
} else {
- lean_assert(is_constant(f));
- if (!m_pattern)
- mk_simple_projections(m, margs, rhs, j, alts, relax);
- mk_flex_rigid_app_cnstrs(m, margs, f, rhs, j, alts, relax);
+ return v;
}
- break;
- }}
+ }
+
+ /** \brief Copy pending constraints in u.m_tc[relax] to cs and append justification j to them */
+ void copy_pending_constraints(buffer & 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 */
+ 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.
+ */
+ expr ensure_sufficient_args(expr const & mtype, buffer & cs) {
+ expr t = mtype;
+ unsigned num = 0;
+ while (is_pi(t)) {
+ num++;
+ t = binding_body(t);
+ }
+ if (num == margs.size())
+ return mtype;
+ 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.
+ type_checker::scope scope(*u.m_tc[relax]);
+ auto new_mtype = ensure_sufficient_args_core(mtype, 0);
+ copy_pending_constraints(cs);
+ 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 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 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 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
+ When using "imitation" for solving a constraint
+ ?m l_1 ... l_k =?= f a_1 ... a_n
+ We say argument a_i is "easy" if
+ 1) it is a local constant
+ 2) there is only one l_j equal to a_i.
+ 3) none of the l_j's is of the form (?m ...)
+ In our experiments, the vast majority (> 2/3 of all cases) of the arguments are easy.
+
+ margs contains l_1 ... l_k
+ arg is the argument we are testing
+
+ Result: none if it is not an easy argument, and variable #k-i-1 if it is easy.
+ The variable is the "solution".
+ */
+ optional is_easy_flex_rigid_arg(expr const & arg) {
+ if (!is_local(arg))
+ return none_expr();
+ optional v;
+ unsigned num_margs = margs.size();
+ for (unsigned j = 0; j < num_margs; j++) {
+ if (is_meta(margs[j]))
+ return none_expr();
+ if (is_local(margs[j]) && mlocal_name(arg) == mlocal_name(margs[j])) {
+ if (v)
+ return none_expr(); // failed, there is more than one possibility
+ v = mk_var(num_margs - j - 1);
+ }
+ }
+ 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 & cs) {
+ buffer 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 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;
+ }
+ }
+
+ /**
+ \brief Given
+ m := a metavariable ?m
+ margs := [a_1 ... a_k]
+ rhs := (g b_1 ... b_n)
+ Then create the constraints
+ (?m_1 a_1 ... a_k) =?= b_1
+ ...
+ (?m_n a_1 ... a_k) =?= b_n
+ ?m =?= fun (x_1 ... x_k), f (?m_1 x_1 ... x_k) ... (?m_n x_1 ... x_k)
+
+ Remark: we try to minimize the number of constraints (?m_i a_1 ... a_k) =?= b_i by detecting "easy" cases
+ that can be solved immediately. See \c is_easy_flex_rigid_arg
+
+ Remark: The term f is:
+ - g (if g is a constant), OR
+ - variable (if g is a local constant equal to a_i)
+ */
+ void mk_flex_rigid_app_cnstrs(expr const & f) {
+ lean_assert(is_metavar(m));
+ lean_assert(is_app(rhs));
+ lean_assert(is_constant(f) || is_var(f));
+ buffer cs;
+ expr mtype = mlocal_type(m);
+ mtype = ensure_sufficient_args(mtype, cs);
+ buffer rargs;
+ get_app_args(rhs, rargs);
+ buffer sargs;
+ for (expr const & rarg : rargs) {
+ if (auto v = is_easy_flex_rigid_arg(rarg)) {
+ sargs.push_back(*v);
+ } else {
+ expr maux = mk_aux_metavar_for(u.m_ngen, mtype);
+ cs.push_back(mk_eq_cnstr(mk_app(maux, margs), rarg, j, relax));
+ sargs.push_back(mk_app_vars(maux, margs.size()));
+ }
+ }
+ expr v = mk_app(f, sargs);
+ v = mk_lambda_for(mtype, v);
+ if (check_imitation(v, cs)) {
+ cs.push_back(mk_eq_cnstr(m, v, j, relax));
+ alts.push_back(to_list(cs.begin(), cs.end()));
+ }
+ }
+
+ /**
+ \brief Given
+ m := a metavariable ?m
+ margs := [a_1 ... a_k]
+ rhs := (fun/Pi (y : A), B y)
+ Then create the constraints
+ (?m_1 a_1 ... a_k) =?= A
+ (?m_2 a_1 ... a_k l) =?= B l
+ ?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.
+ */
+ void mk_bindings_imitation() {
+ lean_assert(is_metavar(m));
+ lean_assert(is_binding(rhs));
+ buffer cs;
+ expr mtype = mlocal_type(m);
+ mtype = ensure_sufficient_args(mtype, cs);
+ 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));
+ expr dontcare;
+ 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 maux2 = mk_aux_metavar_for(u.m_ngen, mtype2);
+ 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));
+ expr v = update_binding(rhs, mk_app_vars(maux1, margs.size()), mk_app_vars(maux2, margs.size() + 1));
+ v = mk_lambda_for(mtype, v);
+ if (check_imitation(v, cs)) {
+ cs.push_back(mk_eq_cnstr(m, v, j, relax));
+ alts.push_back(to_list(cs.begin(), cs.end()));
+ }
+ }
+
+ /**
+ \brief Given
+ m := a metavariable ?m
+ margs := [a_1 ... a_k]
+ rhs := M(b_1 ... b_n) where M is a macro with arguments b_1 ... b_n
+ Then create the constraints
+ (?m_1 a_1 ... a_k) =?= b_1
+ ...
+ (?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))
+ */
+ void mk_macro_imitation() {
+ lean_assert(is_metavar(m));
+ lean_assert(is_macro(rhs));
+ buffer cs;
+ expr mtype = mlocal_type(m);
+ mtype = ensure_sufficient_args(mtype, cs);
+ // create an auxiliary metavariable for each macro argument
+ buffer sargs;
+ for (unsigned i = 0; i < macro_num_args(rhs); i++) {
+ 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));
+ sargs.push_back(mk_app_vars(maux, margs.size()));
+ }
+ expr v = mk_macro(macro_def(rhs), sargs.size(), sargs.data());
+ v = mk_lambda_for(mtype, v);
+ if (check_imitation(v, cs)) {
+ cs.push_back(mk_eq_cnstr(m, v, j, relax));
+ alts.push_back(to_list(cs.begin(), cs.end()));
+ }
+ }
+
+ public:
+ flex_rigid_core_fn(unifier_fn & _u, expr const & _lhs, expr const & _rhs,
+ justification const & _j, bool _relax, buffer & _alts):
+ u(_u), lhs(_lhs), m(get_app_args(lhs, margs)), rhs(_rhs), j(_j), relax(_relax), alts(_alts) {}
+
+ void operator()() {
+ switch (rhs.kind()) {
+ case expr_kind::Var: case expr_kind::Meta:
+ lean_unreachable(); // LCOV_EXCL_LINE
+ case expr_kind::Local:
+ mk_simple_projections();
+ break;
+ case expr_kind::Sort: case expr_kind::Constant:
+ if (!u.m_pattern)
+ mk_simple_projections();
+ mk_simple_imitation();
+ break;
+ case expr_kind::Pi: case expr_kind::Lambda:
+ if (!u.m_pattern)
+ mk_simple_projections();
+ mk_bindings_imitation();
+ break;
+ case expr_kind::Macro:
+ if (!u.m_pattern)
+ mk_simple_projections();
+ mk_macro_imitation();
+ break;
+ case expr_kind::App: {
+ expr const & f = get_app_fn(rhs);
+ if (is_local(f)) {
+ unsigned i = margs.size();
+ while (i > 0) {
+ unsigned vidx = margs.size() - i;
+ --i;
+ expr const & marg = margs[i];
+ if (is_local(marg) && mlocal_name(marg) == mlocal_name(f))
+ mk_flex_rigid_app_cnstrs(mk_var(vidx));
+ else if (!u.m_pattern)
+ mk_simple_nonlocal_projection(i);
+ }
+ } else {
+ lean_assert(is_constant(f));
+ if (!u.m_pattern)
+ mk_simple_projections();
+ mk_flex_rigid_app_cnstrs(f);
+ }
+ break;
+ }}
+ }
+ };
+
+ void process_flex_rigid_core(expr const & lhs, expr const & rhs, justification const & j, bool relax,
+ buffer & 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