2013-07-25 02:36:54 +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
|
|
|
|
*/
|
|
|
|
#include <algorithm>
|
2013-09-13 10:35:29 +00:00
|
|
|
#include <limits>
|
2013-09-13 03:04:10 +00:00
|
|
|
#include "util/scoped_map.h"
|
|
|
|
#include "util/list.h"
|
|
|
|
#include "util/flet.h"
|
|
|
|
#include "util/buffer.h"
|
|
|
|
#include "util/sexpr/options.h"
|
|
|
|
#include "kernel/normalizer.h"
|
|
|
|
#include "kernel/expr.h"
|
|
|
|
#include "kernel/context.h"
|
|
|
|
#include "kernel/environment.h"
|
|
|
|
#include "kernel/builtin.h"
|
|
|
|
#include "kernel/free_vars.h"
|
|
|
|
#include "kernel/kernel_exception.h"
|
2013-08-23 16:16:54 +00:00
|
|
|
|
|
|
|
#ifndef LEAN_KERNEL_NORMALIZER_MAX_DEPTH
|
|
|
|
#define LEAN_KERNEL_NORMALIZER_MAX_DEPTH std::numeric_limits<unsigned>::max()
|
|
|
|
#endif
|
2013-07-25 02:36:54 +00:00
|
|
|
|
|
|
|
namespace lean {
|
2013-08-23 16:16:54 +00:00
|
|
|
static name g_kernel_normalizer_max_depth {"kernel", "normalizer", "max_depth"};
|
|
|
|
RegisterUnsignedOption(g_kernel_normalizer_max_depth, LEAN_KERNEL_NORMALIZER_MAX_DEPTH, "(kernel) maximum recursion depth for expression normalizer");
|
|
|
|
unsigned get_normalizer_max_depth(options const & opts) {
|
|
|
|
return opts.get_unsigned(g_kernel_normalizer_max_depth, LEAN_KERNEL_NORMALIZER_MAX_DEPTH);
|
|
|
|
}
|
|
|
|
|
2013-08-03 21:16:32 +00:00
|
|
|
class svalue;
|
2013-08-03 23:09:21 +00:00
|
|
|
typedef list<svalue> value_stack; //!< Normalization stack
|
2013-08-03 21:16:32 +00:00
|
|
|
enum class svalue_kind { Expr, Closure, BoundedVar };
|
|
|
|
/** \brief Stack value: simple expressions, closures and bounded variables. */
|
|
|
|
class svalue {
|
|
|
|
svalue_kind m_kind;
|
|
|
|
unsigned m_bvar;
|
|
|
|
expr m_expr;
|
2013-08-03 23:09:21 +00:00
|
|
|
value_stack m_ctx;
|
2013-07-25 02:36:54 +00:00
|
|
|
public:
|
2013-08-15 06:18:50 +00:00
|
|
|
svalue() {}
|
2013-08-03 23:09:21 +00:00
|
|
|
explicit svalue(expr const & e): m_kind(svalue_kind::Expr), m_expr(e) {}
|
|
|
|
explicit svalue(unsigned k): m_kind(svalue_kind::BoundedVar), m_bvar(k) {}
|
|
|
|
svalue(expr const & e, value_stack const & c):m_kind(svalue_kind::Closure), m_expr(e), m_ctx(c) { lean_assert(is_lambda(e)); }
|
2013-07-25 02:36:54 +00:00
|
|
|
|
2013-08-03 21:16:32 +00:00
|
|
|
svalue_kind kind() const { return m_kind; }
|
2013-07-26 02:13:45 +00:00
|
|
|
|
2013-08-03 21:16:32 +00:00
|
|
|
bool is_expr() const { return kind() == svalue_kind::Expr; }
|
|
|
|
bool is_closure() const { return kind() == svalue_kind::Closure; }
|
|
|
|
bool is_bounded_var() const { return kind() == svalue_kind::BoundedVar; }
|
2013-07-26 02:13:45 +00:00
|
|
|
|
2013-07-30 08:39:29 +00:00
|
|
|
expr const & get_expr() const { lean_assert(is_expr() || is_closure()); return m_expr; }
|
2013-08-03 23:09:21 +00:00
|
|
|
value_stack const & get_ctx() const { lean_assert(is_closure()); return m_ctx; }
|
2013-07-30 08:39:29 +00:00
|
|
|
unsigned get_var_idx() const { lean_assert(is_bounded_var()); return m_bvar; }
|
2013-07-25 02:36:54 +00:00
|
|
|
};
|
|
|
|
|
2013-08-03 23:09:21 +00:00
|
|
|
svalue_kind kind(svalue const & v) { return v.kind(); }
|
|
|
|
expr const & to_expr(svalue const & v) { return v.get_expr(); }
|
|
|
|
value_stack const & stack_of(svalue const & v) { return v.get_ctx(); }
|
|
|
|
unsigned to_bvar(svalue const & v) { return v.get_var_idx(); }
|
2013-07-30 07:25:19 +00:00
|
|
|
|
2013-08-03 23:09:21 +00:00
|
|
|
value_stack extend(value_stack const & s, svalue const & v) { return cons(v, s); }
|
2013-07-25 02:36:54 +00:00
|
|
|
|
2013-08-03 21:16:32 +00:00
|
|
|
/** \brief Expression normalizer. */
|
2013-08-20 03:00:35 +00:00
|
|
|
class normalizer::imp {
|
2013-08-15 06:18:50 +00:00
|
|
|
typedef scoped_map<expr, svalue, expr_hash, expr_eqp> cache;
|
|
|
|
|
2013-08-24 23:11:35 +00:00
|
|
|
environment const & m_env;
|
|
|
|
context m_ctx;
|
|
|
|
cache m_cache;
|
|
|
|
unsigned m_max_depth;
|
|
|
|
unsigned m_depth;
|
|
|
|
volatile bool m_interrupted;
|
2013-08-20 03:00:35 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Auxiliary object for saving the current context.
|
|
|
|
We need this to be able to process values in the context.
|
|
|
|
*/
|
|
|
|
struct save_context {
|
|
|
|
imp & m_imp;
|
|
|
|
context m_old_ctx;
|
|
|
|
save_context(imp & imp):m_imp(imp), m_old_ctx(m_imp.m_ctx) { m_imp.m_cache.clear(); }
|
|
|
|
~save_context() { m_imp.m_ctx = m_old_ctx; }
|
|
|
|
};
|
2013-07-30 07:25:19 +00:00
|
|
|
|
2013-08-03 23:09:21 +00:00
|
|
|
svalue lookup(value_stack const & s, unsigned i, unsigned k) {
|
2013-07-30 08:39:29 +00:00
|
|
|
unsigned j = i;
|
2013-08-03 23:09:21 +00:00
|
|
|
value_stack const * it1 = &s;
|
2013-07-30 08:39:29 +00:00
|
|
|
while (*it1) {
|
|
|
|
if (j == 0)
|
2013-07-30 07:25:19 +00:00
|
|
|
return head(*it1);
|
2013-07-30 08:39:29 +00:00
|
|
|
--j;
|
2013-07-30 07:25:19 +00:00
|
|
|
it1 = &tail(*it1);
|
|
|
|
}
|
2013-08-14 21:42:48 +00:00
|
|
|
auto p = lookup_ext(m_ctx, j);
|
|
|
|
context_entry const & entry = p.first;
|
|
|
|
context const & entry_c = p.second;
|
2013-08-20 03:00:35 +00:00
|
|
|
if (entry.get_body()) {
|
|
|
|
save_context save(*this); // it restores the context and cache
|
|
|
|
m_ctx = entry_c;
|
2013-09-07 18:11:45 +00:00
|
|
|
unsigned k = m_ctx.size();
|
2013-08-20 03:00:35 +00:00
|
|
|
return svalue(reify(normalize(entry.get_body(), value_stack(), k), k));
|
|
|
|
} else {
|
2013-09-07 18:11:45 +00:00
|
|
|
return svalue(entry_c.size());
|
2013-08-20 03:00:35 +00:00
|
|
|
}
|
2013-07-25 02:36:54 +00:00
|
|
|
}
|
2013-07-30 07:25:19 +00:00
|
|
|
|
2013-08-03 21:16:32 +00:00
|
|
|
/** \brief Convert the closure \c a into an expression using the given stack in a context that contains \c k binders. */
|
2013-08-03 23:09:21 +00:00
|
|
|
expr reify_closure(expr const & a, value_stack const & s, unsigned k) {
|
2013-07-30 07:25:19 +00:00
|
|
|
lean_assert(is_lambda(a));
|
2013-08-03 21:16:32 +00:00
|
|
|
expr new_t = reify(normalize(abst_domain(a), s, k), k);
|
|
|
|
expr new_b = reify(normalize(abst_body(a), extend(s, svalue(k)), k+1), k+1);
|
2013-08-06 18:27:14 +00:00
|
|
|
return mk_lambda(abst_name(a), new_t, new_b);
|
2013-07-25 02:36:54 +00:00
|
|
|
}
|
2013-07-30 07:25:19 +00:00
|
|
|
|
2013-08-03 21:16:32 +00:00
|
|
|
/** \brief Convert the value \c v back into an expression in a context that contains \c k binders. */
|
|
|
|
expr reify(svalue const & v, unsigned k) {
|
2013-07-30 07:25:19 +00:00
|
|
|
switch (v.kind()) {
|
2013-08-03 21:16:32 +00:00
|
|
|
case svalue_kind::Expr: return to_expr(v);
|
2013-08-06 18:27:14 +00:00
|
|
|
case svalue_kind::BoundedVar: return mk_var(k - to_bvar(v) - 1);
|
2013-08-03 21:16:32 +00:00
|
|
|
case svalue_kind::Closure: return reify_closure(to_expr(v), stack_of(v), k);
|
2013-07-30 07:25:19 +00:00
|
|
|
}
|
|
|
|
lean_unreachable();
|
|
|
|
return expr();
|
2013-07-26 02:13:45 +00:00
|
|
|
}
|
2013-07-25 02:36:54 +00:00
|
|
|
|
2013-08-03 21:16:32 +00:00
|
|
|
/** \brief Normalize the expression \c a in a context composed of stack \c s and \c k binders. */
|
2013-08-03 23:09:21 +00:00
|
|
|
svalue normalize(expr const & a, value_stack const & s, unsigned k) {
|
2013-08-23 15:53:45 +00:00
|
|
|
flet<unsigned> l(m_depth, m_depth+1);
|
2013-08-25 18:00:48 +00:00
|
|
|
check_interrupted(m_interrupted);
|
2013-08-23 16:16:54 +00:00
|
|
|
if (m_depth > m_max_depth)
|
|
|
|
throw kernel_exception(m_env, "normalizer maximum recursion depth exceeded");
|
2013-08-15 06:18:50 +00:00
|
|
|
bool shared = false;
|
|
|
|
if (is_shared(a)) {
|
|
|
|
shared = true;
|
|
|
|
auto it = m_cache.find(a);
|
|
|
|
if (it != m_cache.end())
|
|
|
|
return it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
svalue r;
|
2013-07-30 07:25:19 +00:00
|
|
|
switch (a.kind()) {
|
|
|
|
case expr_kind::Var:
|
2013-08-15 06:18:50 +00:00
|
|
|
r = lookup(s, var_idx(a), k);
|
|
|
|
break;
|
2013-08-05 03:52:14 +00:00
|
|
|
case expr_kind::Constant: {
|
2013-08-16 22:09:26 +00:00
|
|
|
object const & obj = m_env.get_object(const_name(a));
|
2013-08-13 22:13:54 +00:00
|
|
|
if (obj.is_definition() && !obj.is_opaque()) {
|
2013-08-15 06:18:50 +00:00
|
|
|
r = normalize(obj.get_value(), value_stack(), 0);
|
2013-09-13 19:57:40 +00:00
|
|
|
} else {
|
2013-08-15 06:18:50 +00:00
|
|
|
r = svalue(a);
|
2013-08-05 03:52:14 +00:00
|
|
|
}
|
2013-08-15 06:18:50 +00:00
|
|
|
break;
|
2013-08-05 03:52:14 +00:00
|
|
|
}
|
|
|
|
case expr_kind::Type: case expr_kind::Value:
|
2013-08-15 06:18:50 +00:00
|
|
|
r = svalue(a);
|
|
|
|
break;
|
2013-07-30 07:25:19 +00:00
|
|
|
case expr_kind::App: {
|
2013-08-03 21:16:32 +00:00
|
|
|
svalue f = normalize(arg(a, 0), s, k);
|
2013-07-30 07:25:19 +00:00
|
|
|
unsigned i = 1;
|
|
|
|
unsigned n = num_args(a);
|
|
|
|
while (true) {
|
|
|
|
if (f.is_closure()) {
|
|
|
|
// beta reduction
|
|
|
|
expr const & fv = to_expr(f);
|
2013-08-15 06:18:50 +00:00
|
|
|
{
|
|
|
|
cache::mk_scope sc(m_cache);
|
|
|
|
value_stack new_s = extend(stack_of(f), normalize(arg(a, i), s, k));
|
|
|
|
f = normalize(abst_body(fv), new_s, k);
|
|
|
|
}
|
|
|
|
if (i == n - 1) {
|
|
|
|
r = f;
|
|
|
|
break;
|
|
|
|
}
|
2013-07-30 07:25:19 +00:00
|
|
|
i++;
|
2013-08-07 15:17:33 +00:00
|
|
|
} else {
|
2013-07-30 07:25:19 +00:00
|
|
|
buffer<expr> new_args;
|
2013-08-04 02:57:06 +00:00
|
|
|
expr new_f = reify(f, k);
|
|
|
|
new_args.push_back(new_f);
|
2013-07-30 07:25:19 +00:00
|
|
|
for (; i < n; i++)
|
2013-08-03 21:16:32 +00:00
|
|
|
new_args.push_back(reify(normalize(arg(a, i), s, k), k));
|
2013-08-04 02:57:06 +00:00
|
|
|
if (is_value(new_f)) {
|
2013-08-15 06:18:50 +00:00
|
|
|
expr m;
|
|
|
|
if (to_value(new_f).normalize(new_args.size(), new_args.data(), m)) {
|
2013-09-07 03:45:26 +00:00
|
|
|
r = normalize(m, s, k);
|
2013-08-15 06:18:50 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-08-04 02:57:06 +00:00
|
|
|
}
|
2013-08-15 06:18:50 +00:00
|
|
|
r = svalue(mk_app(new_args.size(), new_args.data()));
|
|
|
|
break;
|
2013-07-30 07:25:19 +00:00
|
|
|
}
|
2013-07-26 02:13:45 +00:00
|
|
|
}
|
2013-08-15 06:18:50 +00:00
|
|
|
break;
|
2013-07-25 02:36:54 +00:00
|
|
|
}
|
2013-08-04 16:37:52 +00:00
|
|
|
case expr_kind::Eq: {
|
2013-08-15 06:18:50 +00:00
|
|
|
expr new_lhs = reify(normalize(eq_lhs(a), s, k), k);
|
|
|
|
expr new_rhs = reify(normalize(eq_rhs(a), s, k), k);
|
|
|
|
if (new_lhs == new_rhs) {
|
|
|
|
r = svalue(mk_bool_value(true));
|
|
|
|
} else if (is_value(new_lhs) && is_value(new_rhs)) {
|
|
|
|
r = svalue(mk_bool_value(false));
|
2013-08-04 16:37:52 +00:00
|
|
|
} else {
|
2013-08-15 06:18:50 +00:00
|
|
|
r = svalue(mk_eq(new_lhs, new_rhs));
|
2013-08-04 16:37:52 +00:00
|
|
|
}
|
2013-08-15 06:18:50 +00:00
|
|
|
break;
|
2013-08-04 16:37:52 +00:00
|
|
|
}
|
2013-07-30 07:25:19 +00:00
|
|
|
case expr_kind::Lambda:
|
2013-08-15 06:18:50 +00:00
|
|
|
r = svalue(a, s);
|
|
|
|
break;
|
2013-07-30 07:25:19 +00:00
|
|
|
case expr_kind::Pi: {
|
2013-08-03 21:16:32 +00:00
|
|
|
expr new_t = reify(normalize(abst_domain(a), s, k), k);
|
2013-08-15 06:18:50 +00:00
|
|
|
expr new_b;
|
|
|
|
{
|
|
|
|
cache::mk_scope sc(m_cache);
|
|
|
|
new_b = reify(normalize(abst_body(a), extend(s, svalue(k)), k+1), k+1);
|
|
|
|
}
|
|
|
|
r = svalue(mk_pi(abst_name(a), new_t, new_b));
|
|
|
|
break;
|
2013-08-04 16:37:52 +00:00
|
|
|
}
|
2013-08-15 06:18:50 +00:00
|
|
|
case expr_kind::Let: {
|
|
|
|
svalue v = normalize(let_value(a), s, k);
|
|
|
|
{
|
|
|
|
cache::mk_scope sc(m_cache);
|
|
|
|
r = normalize(let_body(a), extend(s, v), k+1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}}
|
|
|
|
if (shared) {
|
|
|
|
m_cache.insert(a, r);
|
2013-08-04 16:37:52 +00:00
|
|
|
}
|
2013-08-15 06:18:50 +00:00
|
|
|
return r;
|
2013-07-25 02:36:54 +00:00
|
|
|
}
|
|
|
|
|
2013-08-20 03:00:35 +00:00
|
|
|
bool is_convertible_core(expr const & expected, expr const & given) {
|
|
|
|
if (expected == given) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
expr const * e = &expected;
|
|
|
|
expr const * g = &given;
|
|
|
|
while (true) {
|
|
|
|
if (is_type(*e) && is_type(*g)) {
|
|
|
|
if (m_env.is_ge(ty_level(*e), ty_level(*g)))
|
|
|
|
return true;
|
|
|
|
}
|
2013-08-30 08:23:12 +00:00
|
|
|
|
|
|
|
if (is_type(*e) && *g == mk_bool_type())
|
|
|
|
return true;
|
|
|
|
|
2013-08-20 03:00:35 +00:00
|
|
|
if (is_pi(*e) && is_pi(*g) && abst_domain(*e) == abst_domain(*g)) {
|
|
|
|
e = &abst_body(*e);
|
|
|
|
g = &abst_body(*g);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_ctx(context const & ctx) {
|
|
|
|
if (!is_eqp(ctx, m_ctx)) {
|
|
|
|
m_ctx = ctx;
|
|
|
|
m_cache.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-30 07:25:19 +00:00
|
|
|
public:
|
2013-08-23 16:16:54 +00:00
|
|
|
imp(environment const & env, unsigned max_depth):
|
2013-08-20 03:00:35 +00:00
|
|
|
m_env(env) {
|
|
|
|
m_interrupted = false;
|
2013-08-23 16:16:54 +00:00
|
|
|
m_max_depth = max_depth;
|
2013-08-23 15:53:45 +00:00
|
|
|
m_depth = 0;
|
2013-07-30 07:25:19 +00:00
|
|
|
}
|
|
|
|
|
2013-08-20 03:00:35 +00:00
|
|
|
expr operator()(expr const & e, context const & ctx) {
|
|
|
|
set_ctx(ctx);
|
2013-09-07 18:11:45 +00:00
|
|
|
unsigned k = m_ctx.size();
|
2013-08-03 23:09:21 +00:00
|
|
|
return reify(normalize(e, value_stack(), k), k);
|
2013-07-30 07:25:19 +00:00
|
|
|
}
|
2013-08-20 03:00:35 +00:00
|
|
|
|
|
|
|
bool is_convertible(expr const & expected, expr const & given, context const & ctx) {
|
|
|
|
if (is_convertible_core(expected, given))
|
|
|
|
return true;
|
|
|
|
set_ctx(ctx);
|
2013-09-07 18:11:45 +00:00
|
|
|
unsigned k = m_ctx.size();
|
2013-08-20 03:00:35 +00:00
|
|
|
expr e_n = reify(normalize(expected, value_stack(), k), k);
|
|
|
|
expr g_n = reify(normalize(given, value_stack(), k), k);
|
|
|
|
return is_convertible_core(e_n, g_n);
|
|
|
|
}
|
|
|
|
|
|
|
|
void clear() { m_ctx = context(); m_cache.clear(); }
|
|
|
|
void set_interrupt(bool flag) { m_interrupted = flag; }
|
2013-07-30 07:25:19 +00:00
|
|
|
};
|
|
|
|
|
2013-08-23 16:16:54 +00:00
|
|
|
normalizer::normalizer(environment const & env, unsigned max_depth):m_ptr(new imp(env, max_depth)) {}
|
|
|
|
normalizer::normalizer(environment const & env):normalizer(env, std::numeric_limits<unsigned>::max()) {}
|
|
|
|
normalizer::normalizer(environment const & env, options const & opts):normalizer(env, get_normalizer_max_depth(opts)) {}
|
2013-08-20 03:00:35 +00:00
|
|
|
normalizer::~normalizer() {}
|
|
|
|
expr normalizer::operator()(expr const & e, context const & ctx) { return (*m_ptr)(e, ctx); }
|
|
|
|
bool normalizer::is_convertible(expr const & t1, expr const & t2, context const & ctx) { return m_ptr->is_convertible(t1, t2, ctx); }
|
|
|
|
void normalizer::clear() { m_ptr->clear(); }
|
|
|
|
void normalizer::set_interrupt(bool flag) { m_ptr->set_interrupt(flag); }
|
|
|
|
|
2013-07-30 07:25:19 +00:00
|
|
|
expr normalize(expr const & e, environment const & env, context const & ctx) {
|
2013-08-20 03:00:35 +00:00
|
|
|
return normalizer(env)(e, ctx);
|
|
|
|
}
|
|
|
|
bool is_convertible(expr const & expected, expr const & given, environment const & env, context const & ctx) {
|
|
|
|
return normalizer(env).is_convertible(expected, given, ctx);
|
2013-07-25 02:36:54 +00:00
|
|
|
}
|
|
|
|
}
|
2013-08-20 03:00:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Remark:
|
|
|
|
|
|
|
|
Eta-reduction + Cumulativity + Set theoretic interpretation is unsound.
|
|
|
|
Example:
|
|
|
|
f : (Type 2) -> (Type 2)
|
|
|
|
(fun (x : (Type 1)) (f x)) : (Type 1) -> (Type 2)
|
|
|
|
The domains of these two terms are different. So, they must have different denotations.
|
|
|
|
|
|
|
|
However, by eta-reduction, we have:
|
|
|
|
(fun (x : (Type 1)) (f x)) == f
|
|
|
|
|
|
|
|
For now, we will disable it.
|
|
|
|
REMARK: we can workaround this problem by applying only when the domain of f is equal
|
|
|
|
to the domain of the lambda abstraction.
|
|
|
|
|
|
|
|
Cody Roux suggested we use Eta-expanded normal forms.
|
|
|
|
|
|
|
|
Remark: The source code for eta-reduction can be found in the commit 519a290f320c6a
|
|
|
|
*/
|