lean4-htt/src/library/check.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

149 lines
5.1 KiB
C++

/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "kernel/expr_sets.h"
#include "kernel/instantiate.h"
#include "library/trace.h"
#include "library/type_context.h"
namespace lean {
struct check_fn {
type_context_old & m_ctx;
expr_set m_visited;
void visit_constant(expr const & e) {
constant_info info = m_ctx.env().get(const_name(e));
if (info.get_num_lparams() != length(const_levels(e))) {
lean_trace("check", scope_trace_env _(m_ctx.env(), m_ctx);
tout() << "incorrect of universe levels at " << e << "\n";);
throw exception("check failed, incorrect number of universe levels "
"(use 'set_option trace.check true' for additional details)");
}
}
void ensure_type(expr const & e) {
expr S = m_ctx.relaxed_whnf(m_ctx.infer(e));
if (is_sort(S)) return;
if (is_metavar(S)) {
level u = m_ctx.mk_univ_metavar_decl();
if (m_ctx.is_def_eq(S, mk_sort(u)))
return;
}
lean_trace("check", scope_trace_env _(m_ctx.env(), m_ctx);
tout() << "type expected at " << e << "\n";);
throw exception("check failed, type expected "
"(use 'set_option trace.check true' for additional details)");
}
void visit_binding(expr const & e, bool is_pi) {
type_context_old::tmp_locals locals(m_ctx);
expr it = e;
while (it.kind() == e.kind()) {
expr d = instantiate_rev(binding_domain(it), locals.size(), locals.data());
visit(d);
locals.push_local(binding_name(it), d, binding_info(it));
ensure_type(d);
it = binding_body(it);
}
expr b = instantiate_rev(it, locals.size(), locals.data());
visit(b);
if (is_pi)
ensure_type(b);
}
void visit_lambda(expr const & e) {
visit_binding(e, false);
}
void visit_pi(expr const & e) {
visit_binding(e, true);
}
void visit_let(expr const & e) {
visit(let_value(e));
visit(let_type(e));
expr v_type = m_ctx.infer(let_value(e));
if (!m_ctx.relaxed_is_def_eq(let_type(e), v_type)) {
lean_trace("check", scope_trace_env _(m_ctx.env(), m_ctx);
tout() << "type mismatch at let binder\n " << e << "\n";);
throw exception("check failed, type mismatch at let binder "
"(use 'set_option trace.check true' for additional details)");
}
/* Improve performance if bottleneck */
type_context_old::tmp_locals locals(m_ctx);
expr local = locals.push_local_from_let(e);
visit(instantiate(let_body(e), local));
}
void visit_app(expr const & e) {
visit(app_fn(e));
visit(app_arg(e));
expr f_type = m_ctx.relaxed_whnf(m_ctx.infer(app_fn(e)));
if (!is_pi(f_type)) {
lean_trace("check", scope_trace_env _(m_ctx.env(), m_ctx);
tout() << "function expected at\n " << e << "\ntype\n " << f_type << "\n";);
throw exception("check failed, function expected (use 'set_option trace.check true' for additional details)");
}
expr a_type = m_ctx.infer(app_arg(e));
expr d_type = binding_domain(f_type);
if (!m_ctx.relaxed_is_def_eq(a_type, d_type)) {
lean_trace("check", scope_trace_env _(m_ctx.env(), m_ctx);
tout() << "application type mismatch at\n " << e << "\nargument type\n "
<< a_type << "\nexpected type\n " << d_type;);
throw exception("check failed, application type mismatch "
"(use 'set_option trace.check true' for additional details)");
}
}
void visit(expr const & e) {
if (m_visited.find(e) != m_visited.end())
return;
m_visited.insert(e);
switch (e.kind()) {
case expr_kind::FVar:
case expr_kind::MVar:
case expr_kind::Sort:
case expr_kind::Lit:
case expr_kind::Local:
break; /* do nothing */
case expr_kind::Const:
return visit_constant(e);
case expr_kind::BVar:
lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::Lambda:
return visit_lambda(e);
case expr_kind::Pi:
return visit_pi(e);
case expr_kind::App:
return visit_app(e);
case expr_kind::Let:
return visit_let(e);
case expr_kind::MData:
return visit(mdata_expr(e));
case expr_kind::Proj:
return visit(proj_expr(e));
}
}
public:
check_fn(type_context_old & ctx):m_ctx(ctx) {}
void operator()(expr const & e) { return visit(e); }
};
void check(type_context_old & ctx, expr const & e) {
metavar_context mctx = ctx.mctx();
check_fn checker(ctx);
checker(e);
ctx.set_mctx(mctx);
}
void initialize_check() {
register_trace_class("check");
}
void finalize_check() {
}
}