Beispiel #1
0
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();
    }
}
Beispiel #2
0
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);
}
Beispiel #3
0
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;
}
Beispiel #4
0
// 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;
    }
}
Beispiel #5
0
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();
    }
}
Beispiel #8
0
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;
}
Beispiel #9
0
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);
}
Beispiel #12
0
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;
    }
}
Beispiel #14
0
 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);
     }
 }
Beispiel #15
0
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;
    }
}
Beispiel #17
0
    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);
    }
Beispiel #18
0
/**
    \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);
}
Beispiel #19
0
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
}
Beispiel #20
0
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);
}
Beispiel #21
0
 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));
 }
Beispiel #22
0
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);
}
Beispiel #24
0
bool is_meta(expr const & e) {
    return is_metavar(get_app_fn(e));
}
Beispiel #25
0
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;
}