lean4-htt/src/library/equations_compiler/compiler.cpp
Leonardo de Moura 85092412c7 refactor: remove Expr.FVar hack
@Kha @dselsam:
This hack was preventing us from making `Expr` a "real" Lean type.
This was bad for a few reasons:
- It was hard to extend/modify `Expr` in Lean since we would also have
to modify the C++ code that creates the `Expr` objects with the hidden
fields.
- `Expr.lam` and `Expr.forallE` were not following the Lean layout
standard where we sort fields by size. @Kha: recall we used that to
avoid a UB. The issue with `Expr.lam` and `Expr.forallE` is that they
have a "visible" field (`BinderInfo`), which is smaller than
hidden fields such as hash code.
- `Expr.fvar` had only one field at `Expr.lean,` but four behind the
scenes.

I added a new constructor `Local` that is only accessible from C++.
It is only used in legacy code we inherited from Lean2.
We will eventually delete it.

This refactoring was quite painful since many parts of the codebase
were mixing the new `Expr.fvar` with the old `Expr.local`.
I doubt I would be able to do it without the new staging framework
@Kha built.

BTW, some of the patches are horrible. I didn't care much since we
are going to deleted the super ugly files. That being said,
you should expect new weird bevaior due to `Expr.fvar` vs `Expr.local`.

Next step: use the new `ExprCachedData` to make all `Expr` hidden visibles
accessible from Lean.

checkpoint
2019-11-15 14:04:26 -08:00

175 lines
7.3 KiB
C++

/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <algorithm>
#include <string>
#include "kernel/find_fn.h"
#include "kernel/replace_fn.h"
#include "kernel/instantiate.h"
#include "library/trace.h"
#include "library/locals.h"
#include "library/util.h"
#include "library/replace_visitor.h"
#include "library/replace_visitor_with_tc.h"
#include "library/equations_compiler/compiler.h"
#include "library/equations_compiler/util.h"
#include "library/equations_compiler/structural_rec.h"
#include "library/equations_compiler/unbounded_rec.h"
#include "library/equations_compiler/wf_rec.h"
#include "library/equations_compiler/partial_rec.h"
#include "library/equations_compiler/elim_match.h"
#include "frontends/lean/elaborator.h"
namespace lean {
#define trace_compiler(Code) lean_trace("eqn_compiler", scope_trace_env _scope1(ctx.env(), ctx); Code)
static bool has_nested_rec(local_context const & lctx, expr const & eqns) {
return static_cast<bool>(find(eqns, [&](expr const & e, unsigned) {
return is_fvar(e) && is_rec(lctx.get_local_decl(e).get_info());
}));
}
static eqn_compiler_result compile_equations_core(environment & env, elaborator & elab, metavar_context & mctx, local_context const & lctx, expr const & eqns) {
type_context_old ctx(env, mctx, lctx, elab.get_cache(), transparency_mode::Semireducible);
trace_compiler(tout() << "compiling\n" << eqns << "\n";);
trace_compiler(tout() << "recursive: " << is_recursive_eqns(ctx, eqns) << "\n";);
trace_compiler(tout() << "nested recursion: " << has_nested_rec(lctx, eqns) << "\n";);
trace_compiler(tout() << "using_well_founded: " << is_wf_equations(eqns) << "\n";);
equations_header const & header = get_equations_header(eqns);
trace_compiler(tout() << "partial: " << header.m_is_partial << "\n";);
// lean_assert(header.m_is_unsafe || !has_nested_rec(eqns));
if (header.m_is_unsafe) {
if (is_wf_equations(eqns)) {
throw exception("invalid use of 'using_well_founded', we do not need to use well founded recursion for unsafe definitions, since they can use unbounded recursion");
}
return unbounded_rec(env, elab, mctx, lctx, eqns);
}
if (header.m_is_partial && is_recursive_eqns(ctx, eqns)) {
if (is_wf_equations(eqns)) {
throw exception("invalid use of 'using_well_founded', we do not need to use well founded recursion for partial definitions, since they can use unbounded recursion");
}
return partial_rec(env, elab, mctx, lctx, eqns);
}
if (is_wf_equations(eqns)) {
return wf_rec(env, elab, mctx, lctx, eqns);
}
if (header.m_num_fns == 1) {
if (!is_recursive_eqns(ctx, eqns)) {
return mk_nonrec(env, elab, mctx, lctx, eqns);
} else if (auto r = try_structural_rec(env, elab, mctx, lctx, eqns)) {
return *r;
}
}
return wf_rec(env, elab, mctx, lctx, eqns);
}
static expr remove_aux_main_name(expr const & e) {
buffer<expr> args;
expr fn = get_app_args(e, args);
if (!is_constant(fn)) return e;
name n = const_name(fn);
if (n.is_string() && n.get_string() == "_main") {
n = n.get_prefix();
fn = mk_constant(n, const_levels(fn));
return mk_app(fn, args);
}
return e;
}
struct eta_expand_rec_apps_fn : public replace_visitor_with_tc {
eta_expand_rec_apps_fn(type_context_old & ctx): replace_visitor_with_tc(ctx) {}
virtual expr visit_fvar(expr const & e) {
if (is_rec(m_ctx.lctx().get_local_decl(e).get_info())) {
expr e2 = m_ctx.eta_expand(e);
lean_assert(!is_fvar(e2));
return visit(e2);
}
return e;
}
virtual expr visit_app(expr const & e) {
expr const & fn = app_fn(e);
if (is_fvar(fn) && is_rec(m_ctx.lctx().get_local_decl(fn).get_info())) {
// do not eta-expand `fn`
expr arg = visit(app_arg(e));
return mk_app(fn, arg);
} else {
return replace_visitor::visit_app(e);
}
}
};
static expr compile_equations_main(environment & env, elaborator & elab,
metavar_context & mctx, local_context const & lctx, expr const & _eqns,
bool report_cexs) {
// all following code assumes that all recursive occurrences are applications
type_context_old ctx(env, mctx, lctx, elab.get_cache(), transparency_mode::Semireducible);
expr eqns = eta_expand_rec_apps_fn(ctx)(_eqns);
// tout() << "compile_equations_main\n" << eqns << "\n";
// equations_header const & header = get_equations_header(eqns);
eqn_compiler_result r;
// if (!header.m_is_unsafe && has_nested_rec(eqns)) {
// r = pull_nested_rec_fn(env, elab, mctx, lctx)(eqns);
// tout() << "pull_nested_rec_fn\n" << head(r.m_fns) << "\n";
// } else {
r = compile_equations_core(env, elab, mctx, lctx, eqns);
// }
if (report_cexs && r.m_counter_examples) {
auto pp = mk_pp_ctx(env, elab.get_options(), mctx, lctx);
auto fmt = format("non-exhaustive match, the following cases are missing:");
for (auto & ce : r.m_counter_examples) {
fmt += line() + pp(remove_aux_main_name(ce));
}
elab.report_or_throw({_eqns, fmt});
}
buffer<expr> fns; to_buffer(r.m_fns, fns);
expr eqn_result = mk_equations_result(fns.size(), fns.data());
return eqn_result;
}
expr compile_equations(environment & env, elaborator & elab, metavar_context & mctx, local_context const & lctx, expr const & eqns) {
equations_header const & header = get_equations_header(eqns);
type_context_old ctx(env, mctx, lctx, elab.get_cache(), transparency_mode::Semireducible);
if (!header.m_is_unsafe &&
!header.m_is_lemma &&
!header.m_is_noncomputable &&
/* Remark: we don't need special compilation scheme for non recursive equations */
is_recursive_eqns(ctx, eqns)) {
/* Compile equations but do not generate code since we will use `brec_on` or `well_founded.fix`. */
equations_header new_header = header;
new_header.m_gen_code = false;
expr result = compile_equations_main(env, elab, mctx, lctx, update_equations(eqns, new_header), true);
/* Then, we compile the equations again using `unsafe` and generate code.
The motivations are:
- Clear execution cost semantics for recursive functions.
- Auxiliary unsafe definition may assist recursive definition unfolding in the type_context_old object. */
equations_header aux_header = header;
aux_header.m_is_unsafe = true;
aux_header.m_is_partial = false;
aux_header.m_aux_lemmas = false;
aux_header.m_fn_actual_names = map(header.m_fn_actual_names, mk_unsafe_rec_name);
expr aux_eqns = remove_wf_annotation_from_equations(update_equations(eqns, aux_header));
aux_eqns = eta_expand_rec_apps_fn(ctx)(aux_eqns);
unbounded_rec(env, elab, mctx, lctx, aux_eqns, some_expr(result));
return result;
} else {
return compile_equations_main(env, elab, mctx, lctx, eqns, true);
}
}
void initialize_eqn_compiler() {
}
void finalize_eqn_compiler() {
}
}