2013-10-24 17:45:59 +00:00
|
|
|
/*
|
|
|
|
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
|
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
|
|
|
|
Author: Leonardo de Moura
|
|
|
|
*/
|
2013-10-24 22:14:29 +00:00
|
|
|
#include <vector>
|
|
|
|
#include <utility>
|
2013-10-27 18:17:03 +00:00
|
|
|
#include <algorithm>
|
2013-10-29 17:05:46 +00:00
|
|
|
#include <limits>
|
2013-11-13 05:42:22 +00:00
|
|
|
#include "util/interrupt.h"
|
2013-10-24 17:45:59 +00:00
|
|
|
#include "kernel/type_checker.h"
|
2013-10-24 22:14:29 +00:00
|
|
|
#include "kernel/type_checker_justification.h"
|
|
|
|
#include "kernel/normalizer.h"
|
|
|
|
#include "kernel/replace_visitor.h"
|
|
|
|
#include "kernel/unification_constraint.h"
|
|
|
|
#include "kernel/instantiate.h"
|
2013-12-10 23:32:12 +00:00
|
|
|
#include "kernel/builtin.h"
|
2013-10-24 22:14:29 +00:00
|
|
|
#include "library/type_inferer.h"
|
|
|
|
#include "library/placeholder.h"
|
2013-10-24 17:45:59 +00:00
|
|
|
#include "library/elaborator/elaborator.h"
|
|
|
|
#include "frontends/lean/frontend.h"
|
|
|
|
#include "frontends/lean/frontend_elaborator.h"
|
|
|
|
|
|
|
|
namespace lean {
|
2013-10-24 22:14:29 +00:00
|
|
|
static name g_x_name("x");
|
2013-10-24 17:45:59 +00:00
|
|
|
static format g_assignment_fmt = format(":=");
|
|
|
|
static format g_unification_u_fmt = format("\u2248");
|
|
|
|
static format g_unification_fmt = format("=?=");
|
|
|
|
|
2013-12-10 23:32:12 +00:00
|
|
|
/**
|
|
|
|
\brief Internal value used to store choices for the elaborator.
|
|
|
|
This is a transient value that is only used to setup a problem
|
|
|
|
for the elaborator.
|
|
|
|
*/
|
|
|
|
struct choice_value : public value {
|
|
|
|
std::vector<expr> m_choices;
|
|
|
|
choice_value(unsigned num_fs, expr const * fs):m_choices(fs, fs + num_fs) {}
|
|
|
|
virtual ~choice_value() {}
|
|
|
|
virtual expr get_type() const { lean_unreachable(); } // LCOV_EXCL_LINE
|
|
|
|
virtual name get_name() const { return name("Choice"); }
|
|
|
|
virtual void display(std::ostream & out) const {
|
|
|
|
out << "(Choice";
|
|
|
|
for (auto c : m_choices) {
|
|
|
|
out << " (" << c << ")";
|
|
|
|
}
|
|
|
|
out << ")";
|
|
|
|
}
|
|
|
|
// Remark: we don't implement the pp methods because the lean::pp_fn formatter
|
|
|
|
// object has support for formatting choice internal values.
|
|
|
|
};
|
|
|
|
|
2013-10-24 17:45:59 +00:00
|
|
|
expr mk_choice(unsigned num_fs, expr const * fs) {
|
|
|
|
lean_assert(num_fs >= 2);
|
2013-12-10 23:32:12 +00:00
|
|
|
return mk_value(*(new choice_value(num_fs, fs)));
|
2013-10-24 17:45:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool is_choice(expr const & e) {
|
2013-12-10 23:32:12 +00:00
|
|
|
return is_value(e) && dynamic_cast<choice_value const *>(&to_value(e)) != nullptr;
|
2013-10-24 17:45:59 +00:00
|
|
|
}
|
|
|
|
|
2013-12-10 23:32:12 +00:00
|
|
|
choice_value const & to_choice_value(expr const & e) {
|
2013-10-24 17:45:59 +00:00
|
|
|
lean_assert(is_choice(e));
|
2013-12-10 23:32:12 +00:00
|
|
|
return static_cast<choice_value const &>(to_value(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned get_num_choices(expr const & e) {
|
|
|
|
return to_choice_value(e).m_choices.size();
|
2013-10-24 17:45:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expr const & get_choice(expr const & e, unsigned i) {
|
2013-12-10 23:32:12 +00:00
|
|
|
return to_choice_value(e).m_choices[i];
|
2013-10-24 17:45:59 +00:00
|
|
|
}
|
|
|
|
|
2013-10-24 22:14:29 +00:00
|
|
|
class coercion_justification_cell : public justification_cell {
|
|
|
|
context m_ctx;
|
2013-10-25 00:01:06 +00:00
|
|
|
expr m_src;
|
2013-10-24 22:14:29 +00:00
|
|
|
public:
|
2013-10-25 00:01:06 +00:00
|
|
|
coercion_justification_cell(context const & c, expr const & src):m_ctx(c), m_src(src) {}
|
2013-10-24 22:14:29 +00:00
|
|
|
virtual ~coercion_justification_cell() {}
|
|
|
|
virtual format pp_header(formatter const & fmt, options const & opts) const {
|
|
|
|
unsigned indent = get_pp_indent(opts);
|
2013-10-25 00:01:06 +00:00
|
|
|
format expr_fmt = fmt(m_ctx, m_src, false, opts);
|
2013-10-24 22:14:29 +00:00
|
|
|
format r;
|
|
|
|
r += format("Coercion for");
|
|
|
|
r += nest(indent, compose(line(), expr_fmt));
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
virtual void get_children(buffer<justification_cell*> &) const {}
|
2013-12-08 18:34:38 +00:00
|
|
|
virtual optional<expr> get_main_expr() const { return some_expr(m_src); }
|
2013-10-24 22:14:29 +00:00
|
|
|
context const & get_context() const { return m_ctx; }
|
|
|
|
};
|
|
|
|
|
|
|
|
class overload_justification_cell : public justification_cell {
|
|
|
|
context m_ctx;
|
|
|
|
expr m_app;
|
|
|
|
public:
|
|
|
|
overload_justification_cell(context const & c, expr const & app):m_ctx(c), m_app(app) {}
|
|
|
|
virtual ~overload_justification_cell() {}
|
|
|
|
virtual format pp_header(formatter const & fmt, options const & opts) const {
|
|
|
|
unsigned indent = get_pp_indent(opts);
|
|
|
|
format expr_fmt = fmt(m_ctx, m_app, false, opts);
|
|
|
|
format r;
|
|
|
|
r += format("Overloading at");
|
|
|
|
r += nest(indent, compose(line(), expr_fmt));
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
virtual void get_children(buffer<justification_cell*> &) const {}
|
2013-12-08 18:34:38 +00:00
|
|
|
virtual optional<expr> get_main_expr() const { return some_expr(m_app); }
|
2013-10-24 22:14:29 +00:00
|
|
|
context const & get_context() const { return m_ctx; }
|
|
|
|
expr const & get_app() const { return m_app; }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2013-10-25 00:01:06 +00:00
|
|
|
inline justification mk_coercion_justification(context const & ctx, expr const & e) {
|
|
|
|
return justification(new coercion_justification_cell(ctx, e));
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline justification mk_overload_justification(context const & ctx, expr const & app) {
|
|
|
|
return justification(new overload_justification_cell(ctx, app));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Actual implementation of the frontend_elaborator.
|
|
|
|
*/
|
2013-10-24 17:45:59 +00:00
|
|
|
class frontend_elaborator::imp {
|
2013-10-24 22:14:29 +00:00
|
|
|
frontend const & m_frontend;
|
|
|
|
environment const & m_env;
|
|
|
|
type_checker m_type_checker;
|
|
|
|
type_inferer m_type_inferer;
|
|
|
|
normalizer m_normalizer;
|
|
|
|
metavar_env m_menv;
|
|
|
|
buffer<unification_constraint> m_ucs;
|
|
|
|
// The following mapping is used to store the relationship
|
|
|
|
// between elaborated expressions and non-elaborated expressions.
|
|
|
|
// We need that because a frontend may associate line number information
|
|
|
|
// with the original non-elaborated expressions.
|
|
|
|
expr_map<expr> m_trace;
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Replace placeholders and choices with metavariables.
|
|
|
|
It also introduce metavariables where coercions are needed.
|
|
|
|
*/
|
|
|
|
struct preprocessor : public replace_visitor {
|
|
|
|
imp & m_ref;
|
|
|
|
preprocessor(imp & r):m_ref(r) {}
|
|
|
|
|
feat(library/elaborator): provide the metavar_env to instantiate and lift_free_vars in the elaborator, it will minimize the number of local_entries needed
The modifications started at commit 1852c86948dd7da8a2bac224e3c7c0ee309db224 made a big difference. For example, before these changes test tests/lean/implicit7.lean generated complicated constraints such as:
[x : Type; a : ?M::29[inst:1 ?M::0[lift:0:1]] x] ⊢ Pi B : Type, (Pi _ : x, (Pi _ : (?M::35[inst:0 #0, inst:1 #2, inst:2 #4, inst:3 #6, inst:5 #5, inst:6 #7, inst:7 #9, inst:9 #9, inst:10 #11, inst:13 ?M::0[lift:0:13]] x a B _), (?M::36[inst:1 #1, inst:2 #3, inst:3 #5, inst:4 #7, inst:6 #6, inst:7 #8, inst:8 #10, inst:10 #10, inst:11 #12, inst:14 ?M::0[lift:0:14]] x a B _ _))) ≈
?M::22 x a
After the changes, only very simple constraints are generated. The most complicated one is:
[] ⊢ Pi a : ?M::0, (Pi B : Type, (Pi _ : ?M::0, (Pi _ : B, ?M::0))) ≈ Pi x : ?M::17, ?M::18
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2013-12-14 23:55:12 +00:00
|
|
|
expr instantiate(expr const & e, expr const & v) {
|
|
|
|
return ::lean::instantiate(e, v, m_ref.m_menv);
|
|
|
|
}
|
|
|
|
|
2013-10-24 22:14:29 +00:00
|
|
|
virtual expr visit_constant(expr const & e, context const & ctx) {
|
|
|
|
if (is_placeholder(e)) {
|
2013-12-13 01:47:11 +00:00
|
|
|
expr m = m_ref.m_menv->mk_metavar(ctx, visit(const_type(e), ctx));
|
2013-10-24 22:14:29 +00:00
|
|
|
m_ref.m_trace[m] = e;
|
|
|
|
return m;
|
|
|
|
} else {
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Return the type of \c e if possible.
|
|
|
|
The idea is to use the type to catch the easy cases where we can solve
|
|
|
|
overloads (aka choices) and coercions during preprocessing.
|
|
|
|
*/
|
2013-12-08 07:21:07 +00:00
|
|
|
optional<expr> get_type(expr const & e, context const & ctx) {
|
2013-10-24 22:14:29 +00:00
|
|
|
try {
|
2013-12-08 18:34:38 +00:00
|
|
|
return some_expr(m_ref.m_type_inferer(e, ctx));
|
2013-10-24 22:14:29 +00:00
|
|
|
} catch (exception &) {
|
2013-12-08 18:34:38 +00:00
|
|
|
return none_expr();
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-12-08 18:34:38 +00:00
|
|
|
\brief Make sure f_t is a Pi, if it is not, then return none_expr()
|
2013-10-24 22:14:29 +00:00
|
|
|
*/
|
2013-12-08 07:21:07 +00:00
|
|
|
optional<expr> check_pi(optional<expr> const & f_t, context const & ctx) {
|
|
|
|
if (!f_t || is_pi(*f_t)) {
|
2013-10-24 22:14:29 +00:00
|
|
|
return f_t;
|
|
|
|
} else {
|
2013-12-14 23:41:50 +00:00
|
|
|
expr r = m_ref.m_normalizer(*f_t, ctx, m_ref.m_menv);
|
2013-10-24 22:14:29 +00:00
|
|
|
if (is_pi(r))
|
2013-12-08 18:34:38 +00:00
|
|
|
return some_expr(r);
|
2013-10-24 22:14:29 +00:00
|
|
|
else
|
2013-12-08 18:34:38 +00:00
|
|
|
return none_expr();
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expr add_coercion_mvar_app(list<expr_pair> const & l, expr const & a, expr const & a_t,
|
2013-10-25 00:01:06 +00:00
|
|
|
context const & ctx, expr const & original_a) {
|
2013-10-24 22:14:29 +00:00
|
|
|
buffer<expr> choices;
|
2013-12-13 01:47:11 +00:00
|
|
|
expr mvar = m_ref.m_menv->mk_metavar(ctx);
|
2013-10-24 22:14:29 +00:00
|
|
|
for (auto p : l) {
|
|
|
|
choices.push_back(p.second);
|
|
|
|
}
|
|
|
|
choices.push_back(mk_lambda(g_x_name, a_t, mk_var(0))); // add indentity function
|
|
|
|
std::reverse(choices.begin(), choices.end());
|
|
|
|
m_ref.m_ucs.push_back(mk_choice_constraint(ctx, mvar, choices.size(), choices.data(),
|
2013-10-25 00:01:06 +00:00
|
|
|
mk_coercion_justification(ctx, original_a)));
|
2013-10-24 22:14:29 +00:00
|
|
|
return mk_app(mvar, a);
|
|
|
|
}
|
|
|
|
|
2013-12-08 07:21:07 +00:00
|
|
|
optional<expr> find_coercion(list<expr_pair> const & l, expr const & to_type) {
|
2013-10-24 22:14:29 +00:00
|
|
|
for (auto p : l) {
|
2013-10-29 17:05:46 +00:00
|
|
|
if (p.first == to_type) {
|
2013-12-08 18:34:38 +00:00
|
|
|
return some_expr(p.second);
|
2013-10-29 17:05:46 +00:00
|
|
|
}
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
2013-12-08 18:34:38 +00:00
|
|
|
return none_expr();
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Try to solve overload at preprocessing time.
|
|
|
|
*/
|
2013-12-08 07:21:07 +00:00
|
|
|
void choose(buffer<expr> & f_choices, buffer<optional<expr>> & f_choice_types,
|
|
|
|
buffer<expr> const & args, buffer<optional<expr>> const & arg_types,
|
2013-10-29 17:05:46 +00:00
|
|
|
context const & ctx) {
|
|
|
|
unsigned best_num_coercions = std::numeric_limits<unsigned>::max();
|
|
|
|
unsigned num_choices = f_choices.size();
|
|
|
|
unsigned num_args = args.size();
|
|
|
|
buffer<unsigned> delayed;
|
|
|
|
buffer<unsigned> matched;
|
|
|
|
|
|
|
|
for (unsigned j = 0; j < num_choices; j++) {
|
2013-12-08 07:21:07 +00:00
|
|
|
optional<expr> f_t = f_choice_types[j];
|
2013-10-29 17:05:46 +00:00
|
|
|
unsigned num_coercions = 0; // number of coercions needed by current choice
|
|
|
|
unsigned num_skipped_args = 0;
|
|
|
|
unsigned i = 1;
|
|
|
|
for (; i < num_args; i++) {
|
|
|
|
f_t = check_pi(f_t, ctx);
|
|
|
|
if (!f_t) {
|
|
|
|
// can't process this choice at preprocessing time
|
|
|
|
delayed.push_back(j);
|
|
|
|
break;
|
|
|
|
} else {
|
2013-12-08 07:21:07 +00:00
|
|
|
expr expected = abst_domain(*f_t);
|
|
|
|
optional<expr> given = arg_types[i];
|
|
|
|
if (!given) {
|
|
|
|
num_skipped_args++;
|
|
|
|
} else {
|
|
|
|
if (!has_metavar(expected) && !has_metavar(*given)) {
|
|
|
|
if (m_ref.m_type_checker.is_convertible(*given, expected, ctx)) {
|
|
|
|
// compatible
|
|
|
|
} else if (m_ref.m_frontend.get_coercion(*given, expected)) {
|
|
|
|
// compatible if using coercion
|
|
|
|
num_coercions++;
|
|
|
|
} else {
|
|
|
|
// failed, this choice does not work
|
|
|
|
break;
|
|
|
|
}
|
2013-10-29 17:05:46 +00:00
|
|
|
} else {
|
2013-12-08 07:21:07 +00:00
|
|
|
num_skipped_args++;
|
2013-10-29 17:05:46 +00:00
|
|
|
}
|
|
|
|
}
|
feat(library/elaborator): provide the metavar_env to instantiate and lift_free_vars in the elaborator, it will minimize the number of local_entries needed
The modifications started at commit 1852c86948dd7da8a2bac224e3c7c0ee309db224 made a big difference. For example, before these changes test tests/lean/implicit7.lean generated complicated constraints such as:
[x : Type; a : ?M::29[inst:1 ?M::0[lift:0:1]] x] ⊢ Pi B : Type, (Pi _ : x, (Pi _ : (?M::35[inst:0 #0, inst:1 #2, inst:2 #4, inst:3 #6, inst:5 #5, inst:6 #7, inst:7 #9, inst:9 #9, inst:10 #11, inst:13 ?M::0[lift:0:13]] x a B _), (?M::36[inst:1 #1, inst:2 #3, inst:3 #5, inst:4 #7, inst:6 #6, inst:7 #8, inst:8 #10, inst:10 #10, inst:11 #12, inst:14 ?M::0[lift:0:14]] x a B _ _))) ≈
?M::22 x a
After the changes, only very simple constraints are generated. The most complicated one is:
[] ⊢ Pi a : ?M::0, (Pi B : Type, (Pi _ : ?M::0, (Pi _ : B, ?M::0))) ≈ Pi x : ?M::17, ?M::18
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2013-12-14 23:55:12 +00:00
|
|
|
f_t = some_expr(instantiate(abst_body(*f_t), args[i]));
|
2013-10-29 17:05:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i == num_args) {
|
|
|
|
if (num_skipped_args > 0) {
|
|
|
|
// should keep this choice because we could not check all arguments
|
|
|
|
delayed.push_back(j);
|
|
|
|
} else if (num_coercions < best_num_coercions) {
|
|
|
|
// found best choice
|
|
|
|
best_num_coercions = num_coercions;
|
|
|
|
matched.clear();
|
|
|
|
matched.push_back(j);
|
|
|
|
} else {
|
|
|
|
matched.push_back(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
matched.append(delayed);
|
|
|
|
|
|
|
|
if (matched.size() == 0) {
|
|
|
|
// TODO(Leo): must use another exception that stores the choices considered.
|
|
|
|
// We currently do nothing, and let the elaborator to sign the error
|
|
|
|
} else {
|
|
|
|
buffer<expr> to_keep;
|
2013-12-08 07:21:07 +00:00
|
|
|
buffer<optional<expr>> to_keep_types;
|
2013-12-10 23:32:12 +00:00
|
|
|
std::sort(matched.begin(), matched.end()); // we must preserve the original order
|
2013-10-29 17:05:46 +00:00
|
|
|
for (unsigned i : matched) {
|
|
|
|
to_keep.push_back(f_choices[i]);
|
|
|
|
to_keep_types.push_back(f_choice_types[i]);
|
|
|
|
}
|
|
|
|
f_choices.clear();
|
|
|
|
f_choice_types.clear();
|
|
|
|
f_choices.append(to_keep);
|
|
|
|
f_choice_types.append(to_keep_types);
|
|
|
|
}
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Create a metavariable for representing the choice.
|
|
|
|
*/
|
2013-10-24 22:29:56 +00:00
|
|
|
expr mk_overload_mvar(buffer<expr> & f_choices, context const & ctx, expr const & src) {
|
|
|
|
std::reverse(f_choices.begin(), f_choices.end());
|
2013-12-13 01:47:11 +00:00
|
|
|
expr mvar = m_ref.m_menv->mk_metavar(ctx);
|
2013-10-24 22:14:29 +00:00
|
|
|
m_ref.m_ucs.push_back(mk_choice_constraint(ctx, mvar, f_choices.size(), f_choices.data(),
|
|
|
|
mk_overload_justification(ctx, src)));
|
|
|
|
return mvar;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual expr visit_app(expr const & e, context const & ctx) {
|
2013-10-29 17:05:46 +00:00
|
|
|
expr f = arg(e, 0);
|
2013-12-08 07:21:07 +00:00
|
|
|
optional<expr> f_t;
|
|
|
|
buffer<expr> args;
|
|
|
|
buffer<optional<expr>> arg_types;
|
2013-10-29 17:05:46 +00:00
|
|
|
args.push_back(expr()); // placeholder
|
2013-12-08 18:34:38 +00:00
|
|
|
arg_types.push_back(none_expr()); // placeholder
|
2013-10-29 17:05:46 +00:00
|
|
|
for (unsigned i = 1; i < num_args(e); i++) {
|
|
|
|
expr a = arg(e, i);
|
|
|
|
expr new_a = visit(a, ctx);
|
2013-12-08 07:21:07 +00:00
|
|
|
optional<expr> new_a_t = get_type(new_a, ctx);
|
2013-10-29 17:05:46 +00:00
|
|
|
args.push_back(new_a);
|
|
|
|
arg_types.push_back(new_a_t);
|
|
|
|
}
|
|
|
|
|
2013-10-24 22:14:29 +00:00
|
|
|
if (is_choice(f)) {
|
|
|
|
buffer<expr> f_choices;
|
2013-12-08 07:21:07 +00:00
|
|
|
buffer<optional<expr>> f_choice_types;
|
2013-10-24 22:14:29 +00:00
|
|
|
unsigned num_alts = get_num_choices(f);
|
|
|
|
for (unsigned i = 0; i < num_alts; i++) {
|
|
|
|
expr c = get_choice(f, i);
|
|
|
|
expr new_c = visit(c, ctx);
|
2013-12-08 07:21:07 +00:00
|
|
|
optional<expr> new_c_t = get_type(new_c, ctx);
|
2013-10-24 22:14:29 +00:00
|
|
|
f_choices.push_back(new_c);
|
|
|
|
f_choice_types.push_back(new_c_t);
|
|
|
|
}
|
2013-10-29 17:05:46 +00:00
|
|
|
choose(f_choices, f_choice_types, args, arg_types, ctx);
|
|
|
|
if (f_choices.size() > 1) {
|
2013-10-24 22:14:29 +00:00
|
|
|
args[0] = mk_overload_mvar(f_choices, ctx, e);
|
|
|
|
for (unsigned i = 1; i < args.size(); i++) {
|
|
|
|
if (arg_types[i]) {
|
2013-12-08 07:21:07 +00:00
|
|
|
list<expr_pair> coercions = m_ref.m_frontend.get_coercions(*(arg_types[i]));
|
2013-10-24 22:14:29 +00:00
|
|
|
if (coercions)
|
2013-12-08 07:21:07 +00:00
|
|
|
args[i] = add_coercion_mvar_app(coercions, args[i], *(arg_types[i]), ctx, arg(e, i));
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
|
|
|
}
|
2013-10-29 17:05:46 +00:00
|
|
|
return mk_app(args);
|
|
|
|
} else {
|
|
|
|
// managed to solve overload at preprocessing time
|
|
|
|
f = f_choices[0];
|
|
|
|
f_t = f_choice_types[0];
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
2013-10-29 17:05:46 +00:00
|
|
|
f = visit(f, ctx);
|
|
|
|
f_t = get_type(f, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer<expr> new_args;
|
|
|
|
new_args.push_back(f);
|
|
|
|
for (unsigned i = 1; i < num_args(e); i++) {
|
|
|
|
f_t = check_pi(f_t, ctx);
|
|
|
|
expr a = arg(e, i);
|
|
|
|
expr new_a = args[i];
|
2013-12-08 07:21:07 +00:00
|
|
|
optional<expr> new_a_t = arg_types[i];
|
2013-10-29 17:05:46 +00:00
|
|
|
if (new_a_t) {
|
2013-12-08 07:21:07 +00:00
|
|
|
list<expr_pair> coercions = m_ref.m_frontend.get_coercions(*new_a_t);
|
2013-10-29 17:05:46 +00:00
|
|
|
if (coercions) {
|
|
|
|
if (!f_t) {
|
2013-12-08 07:21:07 +00:00
|
|
|
new_a = add_coercion_mvar_app(coercions, new_a, *new_a_t, ctx, a);
|
2013-10-29 17:05:46 +00:00
|
|
|
} else {
|
2013-12-08 07:21:07 +00:00
|
|
|
expr expected = abst_domain(*f_t);
|
|
|
|
if (expected != *new_a_t) {
|
|
|
|
optional<expr> c = find_coercion(coercions, expected);
|
2013-10-29 17:05:46 +00:00
|
|
|
if (c) {
|
2013-12-08 07:21:07 +00:00
|
|
|
new_a = mk_app(*c, new_a); // apply coercion
|
2013-10-29 17:05:46 +00:00
|
|
|
} else {
|
2013-12-08 07:21:07 +00:00
|
|
|
new_a = add_coercion_mvar_app(coercions, new_a, *new_a_t, ctx, a);
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-29 17:05:46 +00:00
|
|
|
new_args.push_back(new_a);
|
|
|
|
if (f_t)
|
feat(library/elaborator): provide the metavar_env to instantiate and lift_free_vars in the elaborator, it will minimize the number of local_entries needed
The modifications started at commit 1852c86948dd7da8a2bac224e3c7c0ee309db224 made a big difference. For example, before these changes test tests/lean/implicit7.lean generated complicated constraints such as:
[x : Type; a : ?M::29[inst:1 ?M::0[lift:0:1]] x] ⊢ Pi B : Type, (Pi _ : x, (Pi _ : (?M::35[inst:0 #0, inst:1 #2, inst:2 #4, inst:3 #6, inst:5 #5, inst:6 #7, inst:7 #9, inst:9 #9, inst:10 #11, inst:13 ?M::0[lift:0:13]] x a B _), (?M::36[inst:1 #1, inst:2 #3, inst:3 #5, inst:4 #7, inst:6 #6, inst:7 #8, inst:8 #10, inst:10 #10, inst:11 #12, inst:14 ?M::0[lift:0:14]] x a B _ _))) ≈
?M::22 x a
After the changes, only very simple constraints are generated. The most complicated one is:
[] ⊢ Pi a : ?M::0, (Pi B : Type, (Pi _ : ?M::0, (Pi _ : B, ?M::0))) ≈ Pi x : ?M::17, ?M::18
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2013-12-14 23:55:12 +00:00
|
|
|
f_t = some_expr(instantiate(abst_body(*f_t), new_a));
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
2013-10-29 17:05:46 +00:00
|
|
|
return mk_app(new_args);
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
|
|
|
|
2013-10-25 00:01:06 +00:00
|
|
|
virtual expr visit_let(expr const & e, context const & ctx) {
|
|
|
|
lean_assert(is_let(e));
|
2013-12-08 07:21:07 +00:00
|
|
|
return update_let(e, [&](optional<expr> const & t, expr const & v, expr const & b) {
|
|
|
|
optional<expr> new_t = visit(t, ctx);
|
2013-10-25 00:01:06 +00:00
|
|
|
expr new_v = visit(v, ctx);
|
|
|
|
if (new_t) {
|
2013-12-08 07:21:07 +00:00
|
|
|
optional<expr> new_v_t = get_type(new_v, ctx);
|
|
|
|
if (new_v_t && *new_t != *new_v_t) {
|
|
|
|
list<expr_pair> coercions = m_ref.m_frontend.get_coercions(*new_v_t);
|
2013-10-25 00:01:06 +00:00
|
|
|
if (coercions) {
|
2013-12-08 07:21:07 +00:00
|
|
|
new_v = add_coercion_mvar_app(coercions, new_v, *new_v_t, ctx, v);
|
2013-10-25 00:01:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
cache::mk_scope sc(m_cache);
|
2013-12-08 07:21:07 +00:00
|
|
|
expr new_b = visit(b, extend(ctx, let_name(e), new_t, new_v));
|
|
|
|
return std::make_tuple(new_t, new_v, new_b);
|
2013-10-25 00:01:06 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-12-08 07:21:07 +00:00
|
|
|
optional<expr> visit(optional<expr> const & e, context const & ctx) {
|
|
|
|
return replace_visitor::visit(e, ctx);
|
|
|
|
}
|
|
|
|
|
2013-10-24 22:14:29 +00:00
|
|
|
virtual expr visit(expr const & e, context const & ctx) {
|
2013-11-13 05:42:22 +00:00
|
|
|
check_interrupted();
|
2013-10-24 22:14:29 +00:00
|
|
|
expr r = replace_visitor::visit(e, ctx);
|
|
|
|
if (!is_eqp(r, e))
|
|
|
|
m_ref.m_trace[r] = e;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-10-27 18:02:29 +00:00
|
|
|
metavar_env elaborate_core() {
|
2013-10-27 18:17:03 +00:00
|
|
|
// std::stable_sort(m_ucs.begin(), m_ucs.end(),
|
|
|
|
// [](unification_constraint const & c1, unification_constraint const & c2) {
|
|
|
|
// return !is_choice(c1) && is_choice(c2);
|
|
|
|
// });
|
2013-10-24 22:14:29 +00:00
|
|
|
elaborator elb(m_env, m_menv, m_ucs.size(), m_ucs.data());
|
|
|
|
return elb.next();
|
|
|
|
}
|
|
|
|
|
2013-10-24 17:45:59 +00:00
|
|
|
public:
|
|
|
|
imp(frontend const & fe):
|
|
|
|
m_frontend(fe),
|
|
|
|
m_env(fe.get_environment()),
|
2013-10-24 22:14:29 +00:00
|
|
|
m_type_checker(m_env),
|
|
|
|
m_type_inferer(m_env),
|
|
|
|
m_normalizer(m_env) {
|
2013-10-24 17:45:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expr elaborate(expr const & e) {
|
2013-10-24 22:14:29 +00:00
|
|
|
// std::cout << "Elaborate " << e << "\n";
|
|
|
|
clear();
|
|
|
|
expr new_e = preprocessor(*this)(e);
|
|
|
|
// std::cout << "After preprocessing\n" << new_e << "\n";
|
|
|
|
if (has_metavar(new_e)) {
|
2013-12-13 01:47:11 +00:00
|
|
|
m_type_checker.infer_type(new_e, context(), m_menv, m_ucs);
|
2013-10-25 00:53:37 +00:00
|
|
|
// for (auto c : m_ucs) {
|
|
|
|
// formatter fmt = mk_simple_formatter();
|
|
|
|
// std::cout << c.pp(fmt, options(), nullptr, false) << "\n";
|
|
|
|
// }
|
2013-10-27 18:02:29 +00:00
|
|
|
metavar_env new_menv = elaborate_core();
|
2013-12-13 01:47:11 +00:00
|
|
|
return new_menv->instantiate_metavars(new_e);
|
2013-10-24 22:14:29 +00:00
|
|
|
} else {
|
|
|
|
return new_e;
|
|
|
|
}
|
2013-10-24 17:45:59 +00:00
|
|
|
}
|
|
|
|
|
2013-11-29 05:08:12 +00:00
|
|
|
std::tuple<expr, expr, metavar_env> elaborate(name const & n, expr const & t, expr const & e) {
|
2013-10-24 22:14:29 +00:00
|
|
|
// std::cout << "Elaborate " << t << " : " << e << "\n";
|
|
|
|
clear();
|
|
|
|
expr new_t = preprocessor(*this)(t);
|
|
|
|
expr new_e = preprocessor(*this)(e);
|
|
|
|
// std::cout << "After preprocessing\n" << new_t << "\n" << new_e << "\n";
|
|
|
|
if (has_metavar(new_e) || has_metavar(new_t)) {
|
2013-12-13 01:47:11 +00:00
|
|
|
m_type_checker.infer_type(new_t, context(), m_menv, m_ucs);
|
|
|
|
expr new_e_t = m_type_checker.infer_type(new_e, context(), m_menv, m_ucs);
|
2013-10-24 22:14:29 +00:00
|
|
|
m_ucs.push_back(mk_convertible_constraint(context(), new_e_t, new_t,
|
|
|
|
mk_def_type_match_justification(context(), n, e)));
|
2013-10-24 23:44:05 +00:00
|
|
|
// for (auto c : m_ucs) {
|
|
|
|
// formatter fmt = mk_simple_formatter();
|
|
|
|
// std::cout << c.pp(fmt, options(), nullptr, false) << "\n";
|
|
|
|
// }
|
2013-10-27 18:02:29 +00:00
|
|
|
metavar_env new_menv = elaborate_core();
|
2013-12-13 01:47:11 +00:00
|
|
|
return std::make_tuple(new_menv->instantiate_metavars(new_t),
|
|
|
|
new_menv->instantiate_metavars(new_e),
|
2013-11-29 05:08:12 +00:00
|
|
|
new_menv);
|
2013-10-24 22:14:29 +00:00
|
|
|
} else {
|
2013-11-29 05:08:12 +00:00
|
|
|
return std::make_tuple(new_t, new_e, metavar_env());
|
2013-10-24 22:14:29 +00:00
|
|
|
}
|
2013-10-24 17:45:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
expr const & get_original(expr const & e) {
|
2013-10-24 22:14:29 +00:00
|
|
|
expr const * r = &e;
|
|
|
|
while (true) {
|
|
|
|
auto it = m_trace.find(*r);
|
|
|
|
if (it == m_trace.end()) {
|
|
|
|
return *r;
|
|
|
|
} else {
|
|
|
|
r = &(it->second);
|
|
|
|
}
|
|
|
|
}
|
2013-10-24 17:45:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void clear() {
|
2013-10-24 22:14:29 +00:00
|
|
|
m_menv = metavar_env();
|
|
|
|
m_ucs.clear();
|
|
|
|
m_trace.clear();
|
|
|
|
m_type_checker.clear();
|
|
|
|
m_type_inferer.clear();
|
|
|
|
m_normalizer.clear();
|
2013-10-24 17:45:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
environment const & get_environment() const {
|
|
|
|
return m_env;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
frontend_elaborator::frontend_elaborator(frontend const & fe):m_ptr(new imp(fe)) {}
|
|
|
|
frontend_elaborator::~frontend_elaborator() {}
|
|
|
|
expr frontend_elaborator::operator()(expr const & e) { return m_ptr->elaborate(e); }
|
2013-11-29 05:08:12 +00:00
|
|
|
std::tuple<expr, expr, metavar_env> frontend_elaborator::operator()(name const & n, expr const & t, expr const & e) {
|
|
|
|
return m_ptr->elaborate(n, t, e);
|
|
|
|
}
|
2013-10-24 17:45:59 +00:00
|
|
|
expr const & frontend_elaborator::get_original(expr const & e) const { return m_ptr->get_original(e); }
|
|
|
|
void frontend_elaborator::clear() { m_ptr->clear(); }
|
|
|
|
environment const & frontend_elaborator::get_environment() const { return m_ptr->get_environment(); }
|
|
|
|
}
|