optional<expr> unfold_step(type_context & ctx, expr const & e, name_set const & to_unfold, bool unfold_reducible) { if (!unfold_reducible && to_unfold.empty()) return none_expr(); if (!is_app(e) && !is_constant(e)) return none_expr(); expr const & fn = get_app_fn(e); if (!is_constant(fn)) return none_expr(); name const & fn_name = const_name(fn); bool in_to_unfold = to_unfold.contains(const_name(fn)); if (!in_to_unfold && !unfold_reducible) return none_expr(); if (is_projection(ctx.env(), const_name(fn))) { if (in_to_unfold) { type_context::transparency_scope scope(ctx, transparency_mode::Instances); return ctx.reduce_projection(e); } else { return none_expr(); } } else if (in_to_unfold) { return unfold_term(ctx.env(), e); } else if (unfold_reducible && is_reducible(ctx.env(), fn_name)) { type_context::transparency_scope scope(ctx, transparency_mode::Reducible); return unfold_term(ctx.env(), e); } else { return none_expr(); } }
optional<pair<expr, constraint_seq>> projection_converter::reduce_projection(expr const & t) { projection_info const * info = is_projection(t); if (!info) return optional<pair<expr, constraint_seq>>(); buffer<expr> args; get_app_args(t, args); if (args.size() <= info->m_nparams) { return optional<pair<expr, constraint_seq>>(); } unsigned mkidx = info->m_nparams; expr const & mk = args[mkidx]; pair<expr, constraint_seq> new_mk_cs = whnf(mk); expr new_mk = new_mk_cs.first; expr const & new_mk_fn = get_app_fn(new_mk); if (!is_constant(new_mk_fn) || const_name(new_mk_fn) != info->m_constructor) { return optional<pair<expr, constraint_seq>>(); } buffer<expr> mk_args; get_app_args(new_mk, mk_args); unsigned i = info->m_nparams + info->m_i; if (i >= mk_args.size()) { return optional<pair<expr, constraint_seq>>(); } expr r = mk_args[i]; r = mk_app(r, args.size() - mkidx - 1, args.data() + mkidx + 1); return optional<pair<expr, constraint_seq>>(r, new_mk_cs.second); }
projection_info const * projection_converter::is_projection(expr const & e) const { expr const & f = get_app_fn(e); if (is_constant(f)) return m_proj_info.find(const_name(f)); else return nullptr; }
// Apply lazy delta-reduction and then normalizer extensions lbool projection_converter::reduce_def_eq(expr & t_n, expr & s_n, constraint_seq & cs) { while (true) { // first, keep applying lazy delta-reduction while applicable lbool r = lazy_delta_reduction(t_n, s_n, cs); if (r != l_undef) return r; auto p_t = reduce_projection(t_n); auto p_s = reduce_projection(s_n); if (p_t && p_s) { t_n = whnf_core(p_t->first); s_n = whnf_core(p_s->first); cs += p_t->second; cs += p_s->second; } else if (p_t || p_s) { expr const & f_t = get_app_fn(t_n); expr const & f_s = get_app_fn(s_n); if (is_constant(f_t) && is_constant(f_s) && const_name(f_t) == const_name(f_s) && (p_t || is_stuck(t_n, *m_tc)) && (p_s || is_stuck(s_n, *m_tc))) { // treat it as a delta-delta step return l_undef; } if (p_t) { t_n = whnf_core(p_t->first); cs += p_t->second; } else if (p_s) { s_n = whnf_core(p_s->first); cs += p_s->second; } else { lean_unreachable(); } } else { auto new_t_n = d_norm_ext(t_n, cs); auto new_s_n = d_norm_ext(s_n, cs); if (!new_t_n && !new_s_n) return l_undef; if (new_t_n) { t_n = whnf_core(*new_t_n); } if (new_s_n) { s_n = whnf_core(*new_s_n); } } r = quick_is_def_eq(t_n, s_n, cs); if (r != l_undef) return r; } }
expr revert(environment const & env, options const & opts, metavar_context & mctx, expr const & mvar, buffer<expr> & locals) { optional<metavar_decl> g = mctx.get_metavar_decl(mvar); lean_assert(g); type_context ctx = mk_type_context_for(env, opts, mctx, g->get_context(), transparency_mode::All); expr val = ctx.revert(locals, mvar); expr new_g = get_app_fn(val); mctx = ctx.mctx(); return new_g; }
static optional<head_index> to_head_index(expr type) { // TODO(dhs): we will want to filter this set, // because some constants are treated specially by this module // (e.g. [eq] and [not]) expr const & f = get_app_fn(type); if (is_constant(f) || is_local(f)) return optional<head_index>(head_index(f)); else return optional<head_index>(); }
action_result no_confusion_action(hypothesis_idx hidx) { try { state & s = curr_state(); app_builder & b = get_app_builder(); hypothesis const & h = s.get_hypothesis_decl(hidx); expr type = h.get_type(); expr lhs, rhs; if (!is_eq(type, lhs, rhs)) return action_result::failed(); lhs = whnf(lhs); rhs = whnf(rhs); optional<name> c1 = is_constructor_app(env(), lhs); optional<name> c2 = is_constructor_app(env(), rhs); if (!c1 || !c2) return action_result::failed(); expr A = whnf(infer_type(lhs)); expr I = get_app_fn(A); if (!is_constant(I) || !inductive::is_inductive_decl(env(), const_name(I))) return action_result::failed(); name nct_name(const_name(I), "no_confusion_type"); if (!env().find(nct_name)) return action_result::failed(); expr target = s.get_target(); expr nct = whnf(b.mk_app(nct_name, target, lhs, rhs)); if (c1 == c2) { if (!is_pi(nct)) return action_result::failed(); if (s.has_target_forward_deps(hidx)) { // TODO(Leo): we currently do not handle this case. // To avoid non-termination we remove the given hypothesis, if there // forward dependencies, we would also have to remove them. // Remark: this is a low priority refinement since it will not happen // very often in practice. return action_result::failed(); } unsigned num_params = *inductive::get_num_params(env(), const_name(I)); unsigned cnstr_arity = get_arity(env().get(*c1).get_type()); lean_assert(cnstr_arity >= num_params); unsigned num_new_eqs = cnstr_arity - num_params; s.push_proof_step(new no_confusion_proof_step_cell(const_name(I), target, h.get_self(), num_new_eqs)); s.set_target(binding_domain(nct)); s.del_hypothesis(hidx); trace_action("no_confusion"); return action_result::new_branch(); } else { name nc_name(const_name(I), "no_confusion"); expr pr = b.mk_app(nc_name, {target, lhs, rhs, h.get_self()}); trace_action("no_confusion"); return action_result::solved(pr); } } catch (app_builder_exception &) { return action_result::failed(); } }
static bool is_typeformer_app(buffer<name> const & typeformer_names, expr const & e) { expr const & fn = get_app_fn(e); if (!is_local(fn)) return false; unsigned r = 0; for (name const & n : typeformer_names) { if (mlocal_name(fn) == n) return true; r++; } return false; }
static optional<unsigned> is_typeformer_app(buffer<name> const & typeformer_names, expr const & e) { expr const & fn = get_app_fn(e); if (!is_local(fn)) return optional<unsigned>(); unsigned r = 0; for (name const & n : typeformer_names) { if (mlocal_name(fn) == n) return optional<unsigned>(r); r++; } return optional<unsigned>(); }
action_result grinder_intro_action() { grinder_branch_extension & ext = get_ext(); state & s = curr_state(); expr const & target = s.get_target(); expr const & f = get_app_fn(target); if (!is_constant(f)) return action_result::failed(); list<gexpr> const * lemmas = ext.m_intro_lemmas.find(head_index(f)); if (!lemmas) return action_result::failed(); return backward_cut_action(*lemmas); }
action_result grinder_elim_action(hypothesis_idx hidx) { grinder_branch_extension & ext = get_ext(); state & s = curr_state(); hypothesis const & h = s.get_hypothesis_decl(hidx); expr const & f = get_app_fn(h.get_type()); if (!is_constant(f)) return action_result::failed(); auto R = ext.m_elim_lemmas.find(const_name(f)); if (!R) return action_result::failed(); return recursor_action(hidx, *R); }
optional<name> defeq_canonizer::get_head_symbol(expr type) { type = m_ctx.whnf(type); expr const & fn = get_app_fn(type); if (is_constant(fn)) { return optional<name>(const_name(fn)); } else if (is_pi(type)) { type_context::tmp_locals locals(m_ctx); expr l = locals.push_local_from_binding(type); return get_head_symbol(instantiate(binding_body(type), l)); } else { return optional<name>(); } }
// Return true if \c e is convertible to a term of the form (h ...). // If the result is true, update \c e and \c cs. bool try_normalize_to_head(environment const & env, name const & h, expr & e, constraint_seq & cs) { type_checker_ptr tc = mk_type_checker(env, [=](name const & n) { return n == h; }); constraint_seq new_cs; expr new_e = tc->whnf(e, new_cs); expr const & fn = get_app_fn(new_e); if (is_constant(fn) && const_name(fn) == h) { e = new_e; cs += new_cs; return true; } else { return false; } }
expr pack(unsigned i, unsigned arity, buffer<expr> const & args, expr const & type) { lean_assert(arity > 0); if (i == arity - 1) { return args[i]; } else { lean_assert(is_constant(get_app_fn(type), get_psigma_name())); expr a = args[i]; expr A = app_arg(app_fn(type)); expr B = app_arg(type); lean_assert(is_lambda(B)); expr new_type = instantiate(binding_body(B), a); expr b = pack(i+1, arity, args, new_type); bool mask[2] = {true, true}; expr AB[2] = {A, B}; return mk_app(mk_app(m_ctx, get_psigma_mk_name(), 2, mask, AB), a, b); } }
list<list<name>> collect_choice_symbols(expr const & e) { buffer<list<name>> r; for_each(e, [&](expr const & e, unsigned) { if (is_choice(e)) { buffer<name> cs; for (unsigned i = 0; i < get_num_choices(e); i++) { expr const & c = get_app_fn(get_choice(e, i)); if (is_constant(c)) cs.push_back(const_name(c)); else if (is_local(c)) cs.push_back(mlocal_pp_name(c)); } if (cs.size() > 1) r.push_back(to_list(cs)); } return true; }); return to_list(r); }
list<expr> get_coercions_from_to(type_checker & from_tc, type_checker & to_tc, expr const & from_type, expr const & to_type, constraint_seq & cs, bool lift_coe) { constraint_seq new_cs; environment const & env = to_tc.env(); expr whnf_from_type = from_tc.whnf(from_type, new_cs); expr whnf_to_type = to_tc.whnf(to_type, new_cs); if (lift_coe && is_pi(whnf_from_type)) { // Try to lift coercions. // The idea is to convert a coercion from A to B, into a coercion from D->A to D->B if (!is_pi(whnf_to_type)) return list<expr>(); // failed if (!from_tc.is_def_eq(binding_domain(whnf_from_type), binding_domain(whnf_to_type), justification(), new_cs)) return list<expr>(); // failed, the domains must be definitionally equal expr x = mk_local(mk_fresh_name(), "x", binding_domain(whnf_from_type), binder_info()); expr A = instantiate(binding_body(whnf_from_type), x); expr B = instantiate(binding_body(whnf_to_type), x); list<expr> coe = get_coercions_from_to(from_tc, to_tc, A, B, new_cs, lift_coe); if (coe) { cs += new_cs; // Remark: each coercion c in coe is a function from A to B // We create a new list: (fun (f : D -> A) (x : D), c (f x)) expr f = mk_local(mk_fresh_name(), "f", whnf_from_type, binder_info()); expr fx = mk_app(f, x); return map(coe, [&](expr const & c) { return Fun(f, Fun(x, mk_app(c, fx))); }); } else { return list<expr>(); } } else { expr const & fn = get_app_fn(whnf_to_type); list<expr> r; if (is_constant(fn)) { r = get_coercions(env, whnf_from_type, const_name(fn)); } else if (is_pi(whnf_to_type)) { r = get_coercions_to_fun(env, whnf_from_type); } else if (is_sort(whnf_to_type)) { r = get_coercions_to_sort(env, whnf_from_type); } if (r) cs += new_cs; return r; } }
virtual expr visit_app(expr const & e) override { expr const & fn = get_app_fn(e); if (!is_constant(fn)) return default_visit_app(e); name const & n = const_name(fn); if (is_vm_builtin_function(n)) return default_visit_app(e); if (is_cases_on_recursor(env(), n) || is_nonrecursive_recursor(n)) return visit_cases_on_app(e); unsigned nargs = get_app_num_args(e); declaration d = env().get(n); if (!d.is_definition() || d.is_theorem()) return default_visit_app(e); expr v = d.get_value(); unsigned arity = 0; while (is_lambda(v)) { arity++; v = binding_body(v); } if (arity > nargs) { // not fully applied return default_visit_app(e); } if (is_inline(env(), n) || is_simple_application(v)) { if (auto r = unfold_term(env(), e)) return visit(*r); } /* TODO(Leo): this is not safe here. Reason: we may put recursors have have been eliminated in previous steps. We need to move this code to a different place, or make sure that we can recursors will be eliminated later if (auto r = ctx().reduce_projection(e)) { return visit(*r); } */ return default_visit_app(e); }
/** \brief Given a sequence metas: <tt>(?m_1 ...) (?m_2 ... ) ... (?m_k ...)</tt>, we say ?m_i is "redundant" if it occurs in the type of some ?m_j. This procedure removes from metas any redundant element. */ static void remove_redundant_metas(buffer<expr> & metas) { buffer<expr> mvars; for (expr const & m : metas) mvars.push_back(get_app_fn(m)); unsigned k = 0; for (unsigned i = 0; i < metas.size(); i++) { bool found = false; for (unsigned j = 0; j < metas.size(); j++) { if (j != i) { if (occurs(mvars[i], mlocal_type(mvars[j]))) { found = true; break; } } } if (!found) { metas[k] = metas[i]; k++; } } metas.shrink(k); }
bool light_lt_manager::is_lt(expr const & a, expr const & b) { if (is_eqp(a, b)) return false; unsigned wa = get_weight(a); unsigned wb = get_weight(b); if (wa < wb) return true; if (wa > wb) return false; if (is_constant(get_app_fn(a))) { unsigned const * light_arg = m_lrs.find(const_name(get_app_fn(a))); if (light_arg) { buffer<expr> args; get_app_args(a, args); if (args.size() > *light_arg) return is_lt(args[*light_arg], b); } } if (is_constant(get_app_fn(b))) { unsigned const * light_arg = m_lrs.find(const_name(get_app_fn(b))); if (light_arg) { buffer<expr> args; get_app_args(b, args); if (args.size() > *light_arg) return !is_lt(args[*light_arg], a); } } if (a.kind() != b.kind()) return a.kind() < b.kind(); if (a == b) return false; switch (a.kind()) { case expr_kind::Var: return var_idx(a) < var_idx(b); case expr_kind::Constant: if (const_name(a) != const_name(b)) return const_name(a) < const_name(b); else return ::lean::is_lt(const_levels(a), const_levels(b), false); case expr_kind::App: if (app_fn(a) != app_fn(b)) return is_lt(app_fn(a), app_fn(b)); else return is_lt(app_arg(a), app_arg(b)); case expr_kind::Lambda: case expr_kind::Pi: if (binding_domain(a) != binding_domain(b)) return is_lt(binding_domain(a), binding_domain(b)); else return is_lt(binding_body(a), binding_body(b)); case expr_kind::Sort: return ::lean::is_lt(sort_level(a), sort_level(b), false); case expr_kind::Local: case expr_kind::Meta: if (mlocal_name(a) != mlocal_name(b)) return mlocal_name(a) < mlocal_name(b); else return is_lt(mlocal_type(a), mlocal_type(b)); case expr_kind::Macro: if (macro_def(a) != macro_def(b)) return macro_def(a) < macro_def(b); if (macro_num_args(a) != macro_num_args(b)) return macro_num_args(a) < macro_num_args(b); for (unsigned i = 0; i < macro_num_args(a); i++) { if (macro_arg(a, i) != macro_arg(b, i)) return is_lt(macro_arg(a, i), macro_arg(b, i)); } return false; } lean_unreachable(); // LCOV_EXCL_LINE }
head_index::head_index(expr const & e) { expr const & f = get_app_fn(e); m_kind = f.kind(); if (is_constant(f)) m_const_name = const_name(f); }
bool is_relation(expr const & e) { if (!is_app(e)) return false; expr const & fn = get_app_fn(e); return is_constant(fn) && is_simp_relation(m_env, const_name(fn)); }
static proof_state_seq apply_tactic_core(environment const & env, io_state const & ios, proof_state const & s, expr const & _e, buffer<constraint> & cs, add_meta_kind add_meta, subgoals_action_kind subgoals_action, optional<unifier_kind> const & uk = optional<unifier_kind>()) { goals const & gs = s.get_goals(); if (empty(gs)) { throw_no_goal_if_enabled(s); return proof_state_seq(); } bool class_inst = get_apply_class_instance(ios.get_options()); name_generator ngen = s.get_ngen(); std::shared_ptr<type_checker> tc(mk_type_checker(env, ngen.mk_child())); goal g = head(gs); goals tail_gs = tail(gs); expr t = g.get_type(); expr e = _e; auto e_t_cs = tc->infer(e); e_t_cs.second.linearize(cs); expr e_t = e_t_cs.first; buffer<expr> metas; local_context ctx; bool initialized_ctx = false; unifier_config cfg(ios.get_options()); if (uk) cfg.m_kind = *uk; if (add_meta != DoNotAdd) { unsigned num_e_t = get_expect_num_args(*tc, e_t); if (add_meta == AddDiff) { unsigned num_t = get_expect_num_args(*tc, t); if (num_t <= num_e_t) num_e_t -= num_t; else num_e_t = 0; } else { lean_assert(add_meta == AddAll); } for (unsigned i = 0; i < num_e_t; i++) { auto e_t_cs = tc->whnf(e_t); e_t_cs.second.linearize(cs); e_t = e_t_cs.first; expr meta; if (class_inst && binding_info(e_t).is_inst_implicit()) { if (!initialized_ctx) { ctx = g.to_local_context(); initialized_ctx = true; } bool use_local_insts = true; bool is_strict = false; auto mc = mk_class_instance_elaborator( env, ios, ctx, ngen.next(), optional<name>(), use_local_insts, is_strict, some_expr(head_beta_reduce(binding_domain(e_t))), e.get_tag(), cfg, nullptr); meta = mc.first; cs.push_back(mc.second); } else { meta = g.mk_meta(ngen.next(), head_beta_reduce(binding_domain(e_t))); } e = mk_app(e, meta); e_t = instantiate(binding_body(e_t), meta); metas.push_back(meta); } } metavar_closure cls(t); cls.mk_constraints(s.get_subst(), justification()); pair<bool, constraint_seq> dcs = tc->is_def_eq(t, e_t); if (!dcs.first) { throw_tactic_exception_if_enabled(s, [=](formatter const & fmt) { format r = format("invalid 'apply' tactic, failed to unify"); r += pp_indent_expr(fmt, t); r += compose(line(), format("with")); r += pp_indent_expr(fmt, e_t); return r; }); return proof_state_seq(); } dcs.second.linearize(cs); unify_result_seq rseq = unify(env, cs.size(), cs.data(), ngen.mk_child(), s.get_subst(), cfg); list<expr> meta_lst = to_list(metas.begin(), metas.end()); return map2<proof_state>(rseq, [=](pair<substitution, constraints> const & p) -> proof_state { substitution const & subst = p.first; constraints const & postponed = p.second; name_generator new_ngen(ngen); substitution new_subst = subst; expr new_e = new_subst.instantiate_all(e); assign(new_subst, g, new_e); goals new_gs = tail_gs; if (subgoals_action != IgnoreSubgoals) { buffer<expr> metas; for (auto m : meta_lst) { if (!new_subst.is_assigned(get_app_fn(m))) metas.push_back(m); } if (subgoals_action == AddRevSubgoals) { for (unsigned i = 0; i < metas.size(); i++) new_gs = cons(goal(metas[i], new_subst.instantiate_all(tc->infer(metas[i]).first)), new_gs); } else { lean_assert(subgoals_action == AddSubgoals || subgoals_action == AddAllSubgoals); if (subgoals_action == AddSubgoals) remove_redundant_metas(metas); unsigned i = metas.size(); while (i > 0) { --i; new_gs = cons(goal(metas[i], new_subst.instantiate_all(tc->infer(metas[i]).first)), new_gs); } } } return proof_state(s, new_gs, new_subst, new_ngen, postponed); }); }
/** \brief Create a "choice" constraint that postpones the resolution of a calc proof step. By delaying it, we can perform quick fixes such as: - adding symmetry - adding ! - adding subst */ constraint mk_calc_proof_cnstr(environment const & env, options const & opts, old_local_context const & _ctx, expr const & m, expr const & _e, constraint_seq const & cs, unifier_config const & cfg, info_manager * im, update_type_info_fn const & fn) { justification j = mk_failed_to_synthesize_jst(env, m); auto choice_fn = [=](expr const & meta, expr const & _meta_type, substitution const & _s) { old_local_context ctx = _ctx; expr e = _e; substitution s = _s; expr meta_type = _meta_type; type_checker_ptr tc = mk_type_checker(env); constraint_seq new_cs = cs; expr e_type = tc->infer(e, new_cs); e_type = s.instantiate(e_type); tag g = e.get_tag(); bool calc_assistant = get_elaborator_calc_assistant(opts); if (calc_assistant) { // add '!' is needed while (is_norm_pi(*tc, e_type, new_cs)) { binder_info bi = binding_info(e_type); if (!bi.is_implicit() && !bi.is_inst_implicit()) { if (!has_free_var(binding_body(e_type), 0)) { // if the rest of the type does not reference argument, // then we also stop consuming arguments break; } } expr imp_arg = ctx.mk_meta(some_expr(binding_domain(e_type)), g); e = mk_app(e, imp_arg, g); e_type = instantiate(binding_body(e_type), imp_arg); } if (im) fn(e); } e_type = head_beta_reduce(e_type); expr const & meta_type_fn = get_app_fn(meta_type); expr const & e_type_fn = get_app_fn(e_type); if (is_constant(meta_type_fn) && (!is_constant(e_type_fn) || const_name(e_type_fn) != const_name(meta_type_fn))) { // try to make sure meta_type and e_type have the same head symbol if (!try_normalize_to_head(env, const_name(meta_type_fn), e_type, new_cs) && is_constant(e_type_fn)) { try_normalize_to_head(env, const_name(e_type_fn), meta_type, new_cs); } } auto try_alternative = [&](expr const & e, expr const & e_type, constraint_seq fcs, bool conservative) { justification new_j = mk_type_mismatch_jst(e, e_type, meta_type); if (!tc->is_def_eq(e_type, meta_type, new_j, fcs)) throw unifier_exception(new_j, s); buffer<constraint> cs_buffer; fcs.linearize(cs_buffer); metavar_closure cls(meta); cls.add(meta_type); cls.mk_constraints(s, j, cs_buffer); unifier_config new_cfg(cfg); new_cfg.m_discard = false; new_cfg.m_kind = conservative ? unifier_kind::Conservative : unifier_kind::Liberal; unify_result_seq seq = unify(env, cs_buffer.size(), cs_buffer.data(), substitution(), new_cfg); auto p = seq.pull(); lean_assert(p); substitution new_s = p->first.first; constraints postponed = map(p->first.second, [&](constraint const & c) { // we erase internal justifications return update_justification(c, j); }); expr new_e = new_s.instantiate(e); if (conservative && has_expr_metavar_relaxed(new_s.instantiate_all(e))) throw_elaborator_exception("solution contains metavariables", e); if (im) im->instantiate(new_s); constraints r = cls.mk_constraints(new_s, j); buffer<expr> locals; expr mvar = get_app_args(meta, locals); expr val = Fun(locals, new_e); r = cons(mk_eq_cnstr(mvar, val, j), r); return append(r, postponed); }; if (!get_elaborator_calc_assistant(opts)) { bool conservative = false; return try_alternative(e, e_type, new_cs, conservative); } else { // TODO(Leo): after we have the simplifier and rewriter tactic, we should revise // this code. It is "abusing" the higher-order unifier. { // Try the following possible intrepretations using a "conservative" unification procedure. // That is, we only unfold definitions marked as reducible. // Assume pr is the proof provided. // 1. pr bool conservative = true; try { return try_alternative(e, e_type, new_cs, conservative); } catch (exception & ex) {} // 2. eq.symm pr constraint_seq symm_cs = new_cs; auto symm = apply_symmetry(env, ctx, tc, e, e_type, symm_cs, g); if (symm) { try { return try_alternative(symm->first, symm->second, symm_cs, conservative); } catch (exception &) {} } // 3. subst pr (eq.refl lhs) constraint_seq subst_cs = new_cs; if (auto subst = apply_subst(env, ctx, tc, e, e_type, meta_type, subst_cs, g)) { try { return try_alternative(subst->first, subst->second, subst_cs, conservative); } catch (exception&) {} } // 4. subst (eq.symm pr) (eq.refl lhs) if (symm) { constraint_seq subst_cs = symm_cs; if (auto subst = apply_subst(env, ctx, tc, symm->first, symm->second, meta_type, subst_cs, g)) { try { return try_alternative(subst->first, subst->second, subst_cs, conservative); } catch (exception&) {} } } } { // Try the following possible insterpretations using the default unification procedure. // 1. pr bool conservative = false; std::unique_ptr<throwable> saved_ex; try { return try_alternative(e, e_type, new_cs, conservative); } catch (exception & ex) { saved_ex.reset(ex.clone()); } // 2. eq.symm pr constraint_seq symm_cs = new_cs; auto symm = apply_symmetry(env, ctx, tc, e, e_type, symm_cs, g); if (symm) { try { return try_alternative(symm->first, symm->second, symm_cs, conservative); } catch (exception &) {} } // We use the exception for the first alternative as the error message saved_ex->rethrow(); lean_unreachable(); } } }; bool owner = false; return mk_choice_cnstr(m, choice_fn, to_delay_factor(cnstr_group::Epilogue), owner, j); }
bool is_meta(expr const & e) { return is_metavar(get_app_fn(e)); }
bool is_const_app(expr const & e, name const & n, unsigned nargs) { expr const & f = get_app_fn(e); return is_constant(f) && const_name(f) == n && get_app_num_args(e) == nargs; }