Example #1
0
optional<expr> mk_class_instance(environment const & env, io_state const & ios, local_context const & ctx,
                                 name const & prefix, expr const & type, bool relax_opaque, bool use_local_instances,
                                 unifier_config const & cfg) {
    auto C = std::make_shared<class_instance_context>(env, ios, prefix, relax_opaque, use_local_instances);
    if (!is_ext_class(C->tc(), type))
        return none_expr();
    expr meta       = ctx.mk_meta(C->m_ngen, some_expr(type), type.get_tag());
    unsigned depth  = 0;
    constraint c    = mk_class_instance_cnstr(C, ctx, meta, depth);
    unifier_config new_cfg(cfg);
    new_cfg.m_discard        = true;
    new_cfg.m_use_exceptions = true;
    new_cfg.m_pattern        = true;
    new_cfg.m_kind           = C->m_conservative ? unifier_kind::VeryConservative : unifier_kind::Liberal;
    try {
        auto seq = unify(env, 1, &c, C->m_ngen.mk_child(), substitution(), new_cfg);
        while (true) {
            auto p = seq.pull();
            lean_assert(p);
            substitution s = p->first.first;
            expr r = s.instantiate_all(meta);
            if (!has_expr_metavar_relaxed(r))
                return some_expr(r);
            seq = p->second;
        }
    } catch (exception &) {
        return none_expr();
    }
}
Example #2
0
 expr collect(expr const & e) {
     return replace(e, [&](expr const & e, unsigned) {
             if (is_metavar(e)) {
                 name const & id = mlocal_name(e);
                 if (auto r = m_meta_to_param.find(id)) {
                     return some_expr(*r);
                 } else {
                     expr type  = m_ctx.infer(e);
                     expr x     = m_ctx.push_local("_x", type);
                     m_meta_to_param.insert(id, x);
                     m_meta_to_param_inv.insert(mlocal_name(x), e);
                     m_params.push_back(x);
                     return some_expr(x);
                 }
             } else if (is_local(e)) {
                 name const & id = mlocal_name(e);
                 if (!m_found_local.contains(id)) {
                     m_found_local.insert(id);
                     m_params.push_back(e);
                 }
             } else if (is_sort(e)) {
                 return some_expr(update_sort(e, collect(sort_level(e))));
             } else if (is_constant(e)) {
                 return some_expr(update_constant(e, collect(const_levels(e))));
             }
             return none_expr();
         });
 }
static optional<pair<expr, expr>> mk_op(environment const & env, old_local_context & ctx, type_checker_ptr & tc,
                                        name const & op, unsigned nunivs, unsigned nargs, std::initializer_list<expr> const & explicit_args,
                                        constraint_seq & cs, tag g) {
    levels lvls;
    for (unsigned i = 0; i < nunivs; i++)
        lvls = levels(mk_meta_univ(mk_fresh_name()), lvls);
    expr c = mk_constant(op, lvls);
    expr op_type = instantiate_type_univ_params(env.get(op), lvls);
    buffer<expr> args;
    for (unsigned i = 0; i < nargs; i++) {
        if (!is_pi(op_type))
            return optional<pair<expr, expr>>();
        expr arg = ctx.mk_meta(some_expr(binding_domain(op_type)), g);
        args.push_back(arg);
        op_type  = instantiate(binding_body(op_type), arg);
    }
    expr r = mk_app(c, args, g);
    for (expr const & explicit_arg : explicit_args) {
        if (!is_pi(op_type))
            return optional<pair<expr, expr>>();
        r = mk_app(r, explicit_arg);
        expr type = tc->infer(explicit_arg, cs);
        justification j = mk_app_justification(r, op_type, explicit_arg, type);
        if (!tc->is_def_eq(binding_domain(op_type), type, j, cs))
            return optional<pair<expr, expr>>();
        op_type  = instantiate(binding_body(op_type), explicit_arg);
    }
    return some(mk_pair(r, op_type));
}
Example #4
0
static int mk_expr_placeholder(lua_State * L) {
    int nargs = lua_gettop(L);
    if (nargs == 0)
        return push_expr(L, mk_expr_placeholder());
    else
        return push_expr(L, mk_expr_placeholder(some_expr(to_expr(L, 1))));
}
Example #5
0
 optional<expr> expand_core(expr const & e) {
     lean_assert(!is_lambda(e));
     expr t = ctx().whnf(ctx().infer(e));
     if (!is_pi(t))
         return none_expr();
     expr r = mk_lambda(name("x"), binding_domain(t), mk_app(e, mk_var(0)));
     return some_expr(visit(r));
 }
Example #6
0
action mk_exprs_action(name const & sep, expr const & rec, optional<expr> const & ini,
                       optional<name> const & terminator, bool right, unsigned rbp) {
    if (get_free_var_range(rec) > 2)
        throw exception("invalid notation, the expression used to combine a sequence of expressions "
                        "must not contain free variables with de Bruijn indices greater than 1");
    expr new_rec = annotate_macro_subterms(rec);
    optional<expr> new_ini = ini ? some_expr(annotate_macro_subterms(*ini)) : none_expr();
    return action(new exprs_action_cell(sep, new_rec, new_ini, terminator, right, rbp));
}
Example #7
0
optional<expr> defeq_canonizer::find_defeq(name const & h, expr const & e) {
    list<expr> const * lst = m_state.m_M.find(h);
    if (!lst) return none_expr();
    for (expr const & e1 : *lst) {
        if (locals_subset(e1, e) && m_ctx.is_def_eq(e1, e))
            return some_expr(e1);
    }
    return none_expr();
}
Example #8
0
/** \brief Given \c a, an expression that is the denotation of an expression, if \c a is a variable,
    then use the actions in the transitions \c ts to expand \c a. The idea is to produce a head symbol
    we can use to decide whether the notation should be considered during pretty printing.

    \see get_head_index
*/
static expr expand_pp_pattern(unsigned num, transition const * ts, expr const & a) {
    lean_assert(is_simple(num, ts));
    if (!is_var(a))
        return a;
    return replace(a, [&](expr const & e) {
            if (is_var(e)) {
                unsigned vidx = var_idx(e);
                unsigned i = num;
                unsigned offset = 0;
                while (i > 0) {
                    --i;
                    action const & act = ts[i].get_action();
                    switch (act.kind()) {
                    case action_kind::Binder: case action_kind::Binders: case action_kind::Skip:
                        break;
                    case action_kind::Ext: case action_kind::LuaExt:
                        lean_unreachable();
                    case action_kind::Expr:
                        if (vidx == 0) return none_expr();
                        offset++;
                        vidx--;
                        break;
                    case action_kind::Exprs:
                        if (vidx == 0)
                            return some_expr(lift_free_vars(act.get_rec(), offset));
                        offset++;
                        vidx--;
                        break;
                    case action_kind::ScopedExpr:
                        if (vidx == 0)
                            return some_expr(lift_free_vars(act.get_rec(), offset));
                        offset++;
                        vidx--;
                        break;
                    }
                }
                return none_expr();
            } else {
                return none_expr();
            }
        });
}
Example #9
0
static int mk_exprs_action(lua_State * L) {
    int nargs = lua_gettop(L);
    unsigned rbp = nargs <= 5 ? 0 : lua_tonumber(L, 6);
    optional<name> terminator;
    if (nargs >= 4) terminator = to_optional_name(L, 4);
    return push_notation_action(L, mk_exprs_action(to_name_ext(L, 1),
                                                   to_expr(L, 2),
                                                   lua_isnil(L, 3) ? none_expr() : some_expr(to_expr(L, 3)),
                                                   terminator,
                                                   lua_toboolean(L, 5),
                                                   rbp));
}
Example #10
0
 virtual optional<expr> expand(expr const & m, extension_context & ctx) const {
     environment const & env = ctx.env();
     check_num_args(env, m);
     expr l     = whnf(macro_arg(m, 0), ctx);
     expr not_l = whnf(mk_app(*g_not, l), ctx);
     expr H1    = macro_arg(m, 1);
     expr H2    = macro_arg(m, 2);
     expr C1    = infer_type(H1, ctx, true);
     expr C2    = infer_type(H2, ctx, true);
     expr R     = mk_resolvent(env, ctx, m, l, not_l, C1, C2);
     return some_expr(mk_or_elim_tree1(l, not_l, C1, H1, C2, H2, R, ctx));
 }
Example #11
0
action replace(action const & a, std::function<expr(expr const &)> const & f) {
    switch (a.kind()) {
    case action_kind::Skip: case action_kind::Binder: case action_kind::Binders:
    case action_kind::Ext:  case action_kind::LuaExt: case action_kind::Expr:
        return a;
    case action_kind::Exprs:
        return mk_exprs_action(a.get_sep(), f(a.get_rec()), a.get_initial() ? some_expr(f(*a.get_initial())) : none_expr(), a.get_terminator(),
                               a.is_fold_right(), a.rbp());
    case action_kind::ScopedExpr:
        return mk_scoped_expr_action(f(a.get_rec()), a.rbp(), a.use_lambda_abstraction());
    }
    lean_unreachable(); // LCOV_EXCL_LINE
}
Example #12
0
 optional<constraints> try_instance(expr const & inst, expr const & inst_type) {
     type_checker & tc     = m_C->tc();
     name_generator & ngen = m_C->m_ngen;
     tag g                 = inst.get_tag();
     try {
         flet<local_context> scope(m_ctx, m_ctx);
         buffer<expr> locals;
         expr meta_type = m_meta_type;
         while (true) {
             meta_type = tc.whnf(meta_type).first;
             if (!is_pi(meta_type))
                 break;
             expr local  = mk_local(ngen.next(), binding_name(meta_type),
                                    binding_domain(meta_type), binding_info(meta_type));
             m_ctx.add_local(local);
             locals.push_back(local);
             meta_type = instantiate(binding_body(meta_type), local);
         }
         expr type  = inst_type;
         expr r     = inst;
         buffer<constraint> cs;
         while (true) {
             type = tc.whnf(type).first;
             if (!is_pi(type))
                 break;
             expr arg;
             if (binding_info(type).is_inst_implicit()) {
                 pair<expr, constraint> ac = mk_class_instance_elaborator(m_C, m_ctx, some_expr(binding_domain(type)),
                                                                          g, m_depth+1);
                 arg = ac.first;
                 cs.push_back(ac.second);
             } else {
                 arg = m_ctx.mk_meta(m_C->m_ngen, some_expr(binding_domain(type)), g);
             }
             r    = mk_app(r, arg, g);
             type = instantiate(binding_body(type), arg);
         }
         r = Fun(locals, r);
         trace(meta_type, r);
         bool relax   = m_C->m_relax;
         constraint c = mk_eq_cnstr(m_meta, r, m_jst, relax);
         return optional<constraints>(mk_constraints(c, cs));
     } catch (exception &) {
         return optional<constraints>();
     }
 }
Example #13
0
 virtual optional<expr> expand(expr const & m, abstract_type_context & ctx) const {
     check_macro(m);
     expr const & s  = macro_arg(m, 0);
     expr new_s      = ctx.whnf(s);
     buffer<expr> c_args;
     expr const & c  = get_app_args(new_s, c_args);
     if (is_constant(c) && const_name(c) == m_constructor_name && m_idx < c_args.size()) {
         return some_expr(c_args[m_idx]);
     } else {
         // expand into recursor
         expr s_type = ctx.whnf(ctx.infer(s));
         buffer<expr> args;
         expr const & I = get_app_args(s_type, args);
         if (!is_constant(I) || length(m_ps) != length(const_levels(I)))
             return none_expr();
         expr r = instantiate_univ_params(m_val, m_ps, const_levels(I));
         args.push_back(new_s);
         return some(instantiate_rev(r, args.size(), args.data()));
     }
 }
Example #14
0
 virtual optional<expr> expand(expr const & m, extension_context &) const {
     check_macro(m);
     return some_expr(macro_arg(m, 1));
 }
Example #15
0
File: num.cpp Project: avigad/lean
optional<expr> is_neg(expr const & e) {
    if (!is_const_app(e, get_has_neg_neg_name(), 3))
        return none_expr();
    return some_expr(app_arg(e));
}
Example #16
0
File: num.cpp Project: avigad/lean
optional<expr> is_bit1(expr const & e) {
    if (!is_const_app(e, get_bit1_name(), 4))
        return none_expr();
    return some_expr(app_arg(e));
}
Example #17
0
optional<expr> depends_on(unsigned sz, expr const * es, expr const & h) {
    for (unsigned i = 0; i < sz; i++)
        if (depends_on(es[i], h))
            return some_expr(es[i]);
    return none_expr();
}
declaration update_declaration(declaration const & d, level_param_names const & ps, expr const & type) {
    return update_declaration(d, optional<level_param_names>(ps), some_expr(type), none_expr());
}
Example #19
0
 virtual optional<expr> expand(expr const & m, abstract_type_context &) const {
     check_macro(m);
     return some_expr(macro_arg(m, 1));
 }
Example #20
0
optional<expr> expand_string_macro(expr const & e) {
    if (!is_string_macro(e)) return none_expr();
    return some_expr(from_string_core(to_string_macro(e).get_value()));
}
Example #21
0
optional<expr> placeholder_type(expr const & e) {
    if (is_local(e) && is_placeholder(e))
        return some_expr(mlocal_type(e));
    else
        return none_expr();
}
Example #22
0
 optional<expr> apply(optional<expr> const & a) {
     if (a)
         return some_expr(apply(*a));
     else
         return a;
 }
Example #23
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);
        });
}
Example #24
0
 virtual optional<expr> expand(expr const &, abstract_type_context &) const {
     return some_expr(from_string_core(m_value));
 }
declaration update_declaration_value(declaration const & d, expr const & value) {
    return update_declaration(d, optional<level_param_names>(), none_expr(), some_expr(value));
}
Example #26
0
optional<expr> substitution::get_expr(name const & m) const {
    auto it = m_expr_subst.find(m);
    return it ? some_expr(*it) : none_expr();
}
/** \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);
}