dev(library/type_context): basic normalization

This commit is contained in:
Leonardo de Moura 2016-03-09 19:04:40 -08:00
parent 6caca44e41
commit 1d32f13b08
2 changed files with 358 additions and 17 deletions

View file

@ -5,17 +5,107 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "util/flet.h"
#include "util/interrupt.h"
#include "kernel/instantiate.h"
#include "library/idx_metavar.h"
#include "library/reducible.h"
#include "library/type_context.h"
namespace lean {
expr type_context::relaxed_whnf(expr const & e) {
flet<transparency_mode> set(m_transparency_mode, transparency_mode::ALL);
return whnf(e);
/* TODO(Leo): delete this class as soon as we finish porting kernel to abstract type_context */
struct type_context_as_extension_context : public extension_context {
type_context & m_owner;
type_context_as_extension_context(type_context & o):m_owner(o) {}
virtual environment const & env() const { return m_owner.env(); }
virtual pair<expr, constraint_seq> whnf(expr const & e) {
return mk_pair(m_owner.whnf(e), constraint_seq());
}
virtual pair<bool, constraint_seq> is_def_eq(expr const & e1, expr const & e2, delayed_justification &) {
return mk_pair(m_owner.is_def_eq(e1, e2), constraint_seq());
}
virtual pair<expr, constraint_seq> check_type(expr const & e, bool) {
return mk_pair(m_owner.infer(e), constraint_seq());
}
virtual optional<expr> is_stuck(expr const & e) {
return m_owner.is_stuck(e);
}
};
type_context_cache::type_context_cache(environment const & env, options const & opts):
m_env(env),
m_options(opts),
m_proj_info(get_projection_info_map(env)),
m_frozen_mode(false),
m_local_instances_initialized(false) {
m_ci_max_depth = 12; // TODO(Leo): fix
}
bool type_context::relaxed_is_def_eq(expr const & e1, expr const & e2) {
flet<transparency_mode> set(m_transparency_mode, transparency_mode::ALL);
return is_def_eq(e1, e2);
bool type_context_cache::is_transparent(transparency_mode m, declaration const & d) {
if (m == transparency_mode::None)
return false;
name const & n = d.get_name();
if (m_proj_info.contains(n))
return false;
if (m == transparency_mode::All)
return true;
if (d.is_theorem())
return false;
auto s = get_reducible_status(m_env, d.get_name());
if (m == transparency_mode::Reducible && s == reducible_status::Reducible)
return true;
if (m == transparency_mode::Semireducible && s != reducible_status::Irreducible)
return true;
return false;
}
optional<declaration> type_context_cache::is_transparent(transparency_mode m, name const & n) {
auto & cache = m_transparency_cache[static_cast<unsigned>(m)];
auto it = cache.find(n);
if (it != cache.end()) {
return it->second;
}
optional<declaration> r;
if (auto d = m_env.find(n)) {
if (d->is_definition() && is_transparent(m, *d)) {
r = d;
}
}
cache.insert(mk_pair(n, r));
return r;
}
bool type_context_cache::should_unfold_macro(expr const &) {
// TODO(Leo): add predicate
return true;
}
void type_context::init_core(transparency_mode m) {
m_used_assignment = false;
m_transparency_mode = m;
m_tmp_mode = false;
}
type_context::type_context(metavar_context & mctx, local_context const & lctx, type_context_cache & cache,
transparency_mode m):
m_mctx(mctx), m_lctx(lctx), m_cache(&cache), m_cache_owner(false) {
init_core(m);
}
type_context::type_context(environment const & env, options const & opts, metavar_context & mctx, local_context const & lctx,
transparency_mode m):
m_mctx(mctx), m_lctx(lctx), m_cache(new type_context_cache(env, opts)), m_cache_owner(true) {
init_core(m);
}
type_context::~type_context() {
if (m_cache_owner)
delete m_cache;
}
name type_context::get_local_pp_name(expr const & e) const {
@ -33,4 +123,251 @@ expr type_context::push_local(name const & pp_name, expr const & type, binder_in
void type_context::pop_local() {
return m_lctx.pop_local_decl();
}
expr type_context::abstract_locals(expr const & e, unsigned num_locals, expr const * locals) {
// TODO(Leo)
return e;
}
/* ---------------------
Normalization
-------------------- */
optional<expr> type_context::reduce_projection(expr const & e) {
expr const & f = get_app_fn(e);
if (!is_constant(f))
return none_expr();
projection_info const * info = m_cache->m_proj_info.find(const_name(f));
if (!info)
return none_expr();
buffer<expr> args;
get_app_args(e, args);
if (args.size() <= info->m_nparams)
return none_expr();
unsigned mkidx = info->m_nparams;
expr const & mk = args[mkidx];
expr new_mk = whnf(mk);
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 none_expr();
buffer<expr> mk_args;
get_app_args(new_mk, mk_args);
unsigned i = info->m_nparams + info->m_i;
if (i >= mk_args.size())
none_expr();
expr r = mk_args[i];
r = mk_app(r, args.size() - mkidx - 1, args.data() + mkidx + 1);
return some_expr(r);
}
optional<expr> type_context::expand_macro(expr const & e) {
lean_assert(is_macro(e));
if (m_cache->should_unfold_macro(e)) {
type_context_as_extension_context ext(*this);
return macro_def(e).expand(e, ext);
} else {
return none_expr();
}
}
expr type_context::whnf_core(expr const & e) {
switch (e.kind()) {
case expr_kind::Var: case expr_kind::Sort:
case expr_kind::Pi: case expr_kind::Lambda:
case expr_kind::Constant: case expr_kind::Local:
/* Remark: we do not unfold Constants and
Local declarations eagerly in this method */
return e;
case expr_kind::Meta:
if (is_metavar_decl_ref(e)) {
if (m_mctx.is_assigned(e)) {
m_used_assignment = true;
return m_mctx.instantiate(e);
}
} else if (is_idx_metavar(e)) {
unsigned idx = to_meta_idx(e);
if (idx < m_tmp_eassignment.size()) {
if (auto v = m_tmp_eassignment[idx]) {
m_used_assignment = true;
return *v;
}
}
}
return e;
case expr_kind::Macro:
if (auto m = expand_macro(e)) {
check_system("whnf");
return whnf_core(*m);
} else {
return e;
}
case expr_kind::Let:
check_system("whnf");
return whnf_core(instantiate(let_body(e), let_value(e)));
case expr_kind::App: {
check_system("whnf");
buffer<expr> args;
expr f0 = get_app_rev_args(e, args);
expr f = whnf_core(f0);
if (is_lambda(f)) {
unsigned m = 1;
unsigned num_args = args.size();
while (is_lambda(binding_body(f)) && m < num_args) {
f = binding_body(f);
m++;
}
lean_assert(m <= num_args);
return whnf_core(mk_rev_app(instantiate(binding_body(f), m, args.data() + (num_args - m)),
num_args - m, args.data()));
} else {
return f == f0 ? e : whnf_core(mk_rev_app(f, args.size(), args.data()));
}
}}
lean_unreachable();
}
expr type_context::whnf(expr const & e) {
// TODO(Leo)
return e;
}
expr type_context::relaxed_whnf(expr const & e) {
flet<transparency_mode> set(m_transparency_mode, transparency_mode::All);
return whnf(e);
}
optional<expr> type_context::is_stuck(expr const & e) {
// TODO(Leo)
return none_expr();
}
/* ---------------
Type inference
--------------- */
expr type_context::infer(expr const & e) {
// TODO(Leo)
return e;
}
expr type_context::check(expr const & e) {
// TODO(Leo)
return e;
}
/* -----------------------------------
Unification / definitional equality
----------------------------------- */
/*
struct unification_hint_fn {
type_context & m_owner;
unification_hint const & m_hint;
buffer<optional<expr>> m_assignment;
unification_hint_fn(type_context & o, unification_hint const & hint):
m_owner(o), m_hint(hint) {
m_assignment.resize(m_hint.get_num_vars());
}
bool syntactic_match(expr const & pattern, expr const & e) {
unsigned idx;
switch (pattern.kind()) {
case expr_kind::Var:
idx = var_idx(pattern);
if (!m_assignment[idx]) {
m_assignment[idx] = some_expr(e);
return true;
} else {
return m_owner.is_def_eq(*m_assignment[idx], e);
}
case expr_kind::Constant:
return
is_constant(e) &&
const_name(pattern) == const_name(e) &&
m_owner.is_def_eq(const_levels(pattern), const_levels(e));
case expr_kind::Sort:
return is_sort(e) && m_owner.is_def_eq(sort_level(pattern), sort_level(e));
case expr_kind::Pi: case expr_kind::Lambda:
case expr_kind::Macro: case expr_kind::Let:
// Remark: we do not traverse inside of binders.
return pattern == e;
case expr_kind::App:
return
is_app(e) &&
syntactic_match(app_fn(pattern), app_fn(e)) &&
syntactic_match(app_arg(pattern), app_arg(e));
case expr_kind::Local: case expr_kind::Meta:
lean_unreachable();
}
lean_unreachable();
}
bool operator()(expr const & lhs, expr const & rhs) {
if (!syntactic_match(m_hint.get_lhs(), lhs)) {
lean_trace(name({"type_context", "unification_hint"}), tout() << "LHS does not match\n";);
return false;
} else if (!syntactic_match(m_hint.get_rhs(), rhs)) {
lean_trace(name({"type_context", "unification_hint"}), tout() << "RHS does not match\n";);
return false;
} else {
buffer<expr>
auto instantiate_assignment_fn = [&](expr const & e, unsigned offset) {
if (is_var(e)) {
unsigned idx = var_idx(e) + offset;
if (idx < m_assignment.size()) {
lean_assert(m_assignment[idx]);
return m_assignment[idx];
}
}
return none_expr();
};
buffer<expr_pair> constraints;
to_buffer(m_hint.get_constraints(), constraints);
for (expr_pair const & p : constraints) {
expr new_lhs = replace(p.first, instantiate_assignment_fn);
expr new_rhs = replace(p.second, instantiate_assignment_fn);
expr new_lhs_inst = m_owner.instantiate_uvars_mvars(new_lhs);
expr new_rhs_inst = m_owner.instantiate_uvars_mvars(new_rhs);
bool success = m_owner.is_def_eq(new_lhs, new_rhs);
lean_trace(name({"type_context", "unification_hint"}),
tout() << new_lhs_inst << " =?= " << new_rhs_inst << "..."
<< (success ? "success" : "failed") << "\n";);
if (!success) return false;
}
lean_trace(name({"type_context", "unification_hint"}),
tout() << "hint successfully applied\n";);
return true;
}
}
};
bool old_type_context::try_unification_hints(expr const & e1, expr const & e2) {
expr e1_fn = get_app_fn(e1);
expr e2_fn = get_app_fn(e2);
if (is_constant(e1_fn) && is_constant(e2_fn)) {
buffer<unification_hint> hints;
get_unification_hints(m_env, const_name(e1_fn), const_name(e2_fn), hints);
for (unification_hint const & hint : hints) {
scope s(*this);
lean_trace(name({"old_type_context", "unification_hint"}),
tout() << e1 << " =?= " << e2
<< ", pattern: " << hint.get_lhs() << " =?= " << hint.get_rhs() << "\n";);
if (unification_hint_fn(*this, hint)(e1, e2)) {
s.commit();
return true;
}
}
}
return false;
}
*/
bool type_context::is_def_eq(expr const & e1, expr const & e2) {
return false;
}
bool type_context::relaxed_is_def_eq(expr const & e1, expr const & e2) {
flet<transparency_mode> set(m_transparency_mode, transparency_mode::All);
return is_def_eq(e1, e2);
}
}

View file

@ -14,7 +14,7 @@ Author: Leonardo de Moura
#include "library/metavar_context.h"
namespace lean {
enum class transparency_mode { ALL, SEMIREDUCIBLE, REDUCIBLE, NONE };
enum class transparency_mode { All, Semireducible, Reducible, None };
/* \brief Cached information for type_context. */
class type_context_cache {
@ -36,10 +36,6 @@ class type_context_cache {
\remark The inferred type does not depend on reducibility annotations. */
infer_cache m_infer_cache;
/* Cache for whnf (without delta). As in m_infer_cache, we only cache results if the
metavariable assignment was not used. */
whnf_cache m_whnf_cache;
/* Mapping from name to optional<declaration>, this mapping is faster than the one
at environment. Moreover, it takes into account constant reducibility annotations.
We have four different modes.
@ -49,7 +45,8 @@ class type_context_cache {
- NONE (everything is opaque).
\remark In SEMIREDUCIBLE and REDUCIBLE modes, projections and theorems are considered
opaque independently of annotations. In ALL mode, projections are considered opaque.
opaque independently of annotations. In ALL mode, projections are considered opaque,
this is not a problem since type_context implements a custom reduction rule for projections.
The ALL mode is used for type inference where it is unacceptable to fail to infer a type.
The SEMIREDUCIBLE mode is used for scenarios where an is_def_eq is expected to succeed
@ -93,6 +90,9 @@ class type_context_cache {
friend class type_context;
void init(local_context const & lctx);
bool is_transparent(transparency_mode m, declaration const & d);
optional<declaration> is_transparent(transparency_mode m, name const & n);
bool should_unfold_macro(expr const & e);
public:
type_context_cache(environment const & env, options const & opts);
};
@ -112,8 +112,8 @@ class type_context : public abstract_type_context {
};
typedef buffer<scope> scopes;
local_context m_lctx;
metavar_context & m_mctx;
local_context m_lctx;
cache * m_cache;
bool m_cache_owner;
/* We only cache results when m_used_assignment is false */
@ -140,13 +140,11 @@ class type_context : public abstract_type_context {
/* Stack of backtracking point (aka scope) */
scopes m_scopes;
virtual bool on_is_def_eq_failure(expr const &, expr const &);
public:
type_context(metavar_context & mctx, local_context const & lctx, type_context_cache & cache,
transparency_mode m = transparency_mode::REDUCIBLE);
transparency_mode m = transparency_mode::Reducible);
type_context(environment const & env, options const & opts, metavar_context & mctx, local_context const & lctx,
transparency_mode m = transparency_mode::REDUCIBLE);
transparency_mode m = transparency_mode::Reducible);
virtual ~type_context();
virtual environment const & env() const override { return m_cache->m_env; }
@ -197,5 +195,11 @@ public:
}
}
};
private:
void init_core(transparency_mode m);
optional<expr> reduce_projection(expr const & e);
optional<expr> expand_macro(expr const & e);
expr whnf_core(expr const & e);
};
}