feat(kernel): add let-expressions to the kernel

The frontend is still using the old "let-expression macros".
We will use the new let-expressions to implement the new tactic framework.
This commit is contained in:
Leonardo de Moura 2016-02-29 16:40:17 -08:00
parent f55e456c84
commit 3c878ecd01
46 changed files with 321 additions and 31 deletions

View file

@ -170,6 +170,7 @@ lean_expr_kind lean_expr_get_kind(lean_expr e) {
case expr_kind::App: return LEAN_EXPR_APP;
case expr_kind::Lambda: return LEAN_EXPR_LAMBDA;
case expr_kind::Pi: return LEAN_EXPR_PI;
case expr_kind::Let: return LEAN_EXPR_LET;
case expr_kind::Macro: return LEAN_EXPR_MACRO;
}
lean_unreachable();

View file

@ -34,6 +34,7 @@ typedef enum {
LEAN_EXPR_APP,
LEAN_EXPR_LAMBDA,
LEAN_EXPR_PI,
LEAN_EXPR_LET,
LEAN_EXPR_MACRO,
} lean_expr_kind;

View file

@ -126,7 +126,8 @@ static expr parse_let(parser & p, pos_info const & pos) {
v = p.save_pos(mk_let_value(v), id_pos);
p.add_local_expr(id, v);
expr b = parse_let_body(p, pos);
return p.save_pos(mk_let(id, v, b), pos);
// TODO(Leo): use let-expression after we reimplement elaborator
return p.save_pos(mk_let_macro(id, v, b), pos);
}
}

View file

@ -1747,6 +1747,9 @@ expr elaborator::visit_core(expr const & e, constraint_seq & cs) {
case expr_kind::Lambda: return visit_lambda(e, cs);
case expr_kind::Pi: return visit_pi(e, cs);
case expr_kind::App: return visit_app(e, cs);
case expr_kind::Let:
// NOT IMPLEMENTED YET
lean_unreachable();
}
lean_unreachable(); // LCOV_EXCL_LINE
}
@ -2199,6 +2202,13 @@ static void visit_unassigned_mvars(expr const & e, std::function<void(expr const
visit(binding_body(e));
}
break;
case expr_kind::Let:
if (should_visit(e)) {
visit(let_type(e));
visit(let_value(e));
visit(let_body(e));
}
break;
}
};

View file

@ -469,6 +469,12 @@ expr parser::copy_with_new_pos(expr const & e, pos_info p) {
copy_with_new_pos(binding_domain(e), p),
copy_with_new_pos(binding_body(e), p)),
p);
case expr_kind::Let:
return save_pos(update_let(e,
copy_with_new_pos(let_type(e), p),
copy_with_new_pos(let_value(e), p),
copy_with_new_pos(let_body(e), p)),
p);
case expr_kind::Macro: {
buffer<expr> args;
for (unsigned i = 0; i < macro_num_args(e); i++)

View file

@ -829,7 +829,7 @@ auto pretty_fn::pp_macro(expr const & e) -> result {
auto pretty_fn::pp_let(expr e) -> result {
buffer<pair<name, expr>> decls;
while (true) {
if (!is_let(e))
if (!is_let_macro(e))
break;
name n = get_let_var_name(e);
expr v = get_let_value(e);
@ -1263,7 +1263,7 @@ auto pretty_fn::pp(expr const & e, bool ignore_hide) -> result {
if (is_placeholder(e)) return result(*g_placeholder_fmt);
if (is_show(e)) return pp_show(e);
if (is_have(e)) return pp_have(e);
if (is_let(e)) return pp_let(e);
if (is_let_macro(e)) return pp_let(e);
if (is_typed_expr(e)) return pp(get_typed_expr_expr(e));
if (is_let_value(e)) return pp(get_let_value_expr(e));
if (m_num_nat_coe)
@ -1282,6 +1282,9 @@ auto pretty_fn::pp(expr const & e, bool ignore_hide) -> result {
case expr_kind::Lambda: return pp_lambda(e);
case expr_kind::Pi: return pp_pi(e);
case expr_kind::Macro: return pp_macro(e);
case expr_kind::Let:
// NOT IMPLEMENTED YET
lean_unreachable();
}
lean_unreachable(); // LCOV_EXCL_LINE
}

View file

@ -68,7 +68,7 @@ expr default_converter::whnf_core(expr const & e) {
case expr_kind::Var: case expr_kind::Sort: case expr_kind::Meta: case expr_kind::Local:
case expr_kind::Pi: case expr_kind::Constant: case expr_kind::Lambda:
return e;
case expr_kind::Macro: case expr_kind::App:
case expr_kind::Macro: case expr_kind::App: case expr_kind::Let:
break;
}
@ -82,7 +82,7 @@ expr default_converter::whnf_core(expr const & e) {
// do the actual work
expr r;
switch (e.kind()) {
case expr_kind::Var: case expr_kind::Sort: case expr_kind::Meta: case expr_kind::Local:
case expr_kind::Var: case expr_kind::Sort: case expr_kind::Meta: case expr_kind::Local:
case expr_kind::Pi: case expr_kind::Constant: case expr_kind::Lambda:
lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::Macro:
@ -108,7 +108,11 @@ expr default_converter::whnf_core(expr const & e) {
r = f == f0 ? e : whnf_core(mk_rev_app(f, args.size(), args.data()));
}
break;
}}
}
case expr_kind::Let:
r = whnf_core(instantiate(let_body(e), let_value(e)));
break;
}
if (m_memoize)
m_whnf_core_cache.insert(mk_pair(e, r));
@ -190,7 +194,8 @@ pair<expr, constraint_seq> default_converter::whnf(expr const & e_prime) {
switch (e_prime.kind()) {
case expr_kind::Var: case expr_kind::Sort: case expr_kind::Meta: case expr_kind::Local: case expr_kind::Pi:
return to_ecs(e_prime);
case expr_kind::Lambda: case expr_kind::Macro: case expr_kind::App: case expr_kind::Constant:
case expr_kind::Lambda: case expr_kind::Macro: case expr_kind::App:
case expr_kind::Constant: case expr_kind::Let:
break;
}
@ -300,8 +305,8 @@ lbool default_converter::quick_is_def_eq(expr const & t, expr const & s, constra
return to_lbool(is_def_eq(sort_level(t), sort_level(s), cs));
case expr_kind::Meta:
lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::Var: case expr_kind::Local: case expr_kind::App:
case expr_kind::Constant: case expr_kind::Macro:
case expr_kind::Var: case expr_kind::Local: case expr_kind::App:
case expr_kind::Constant: case expr_kind::Macro: case expr_kind::Let:
// We do not handle these cases in this method.
break;
}

View file

@ -92,6 +92,12 @@ bool equiv_manager::is_equiv_core(expr const & a, expr const & b) {
case expr_kind::Sort:
result = sort_level(a) == sort_level(b);
break;
case expr_kind::Let:
result =
is_equiv_core(let_type(a), let_type(b)) &&
is_equiv_core(let_value(a), let_value(b)) &&
is_equiv_core(let_body(a), let_body(b));
break;
case expr_kind::Macro:
if (macro_def(a) != macro_def(b) || macro_num_args(a) != macro_num_args(b))
return false;

View file

@ -240,6 +240,29 @@ void expr_sort::dealloc() {
get_sort_allocator().recycle(this);
}
// Let expressions
DEF_THREAD_MEMORY_POOL(get_let_allocator, sizeof(expr_let));
expr_let::expr_let(name const & n, expr const & t, expr const & v, expr const & b, tag g):
expr_composite(expr_kind::Let,
::lean::hash(::lean::hash(t.hash(), v.hash()), b.hash()),
t.has_expr_metavar() || v.has_expr_metavar() || b.has_expr_metavar(),
t.has_univ_metavar() || v.has_univ_metavar() || b.has_univ_metavar(),
t.has_local() || v.has_local() || b.has_local(),
t.has_param_univ() || v.has_param_univ() || b.has_param_univ(),
inc_weight(add_weight(add_weight(get_weight(t), get_weight(v)), get_weight(b))),
std::max(std::max(get_free_var_range(t), get_free_var_range(v)), dec(get_free_var_range(b))),
g),
m_name(n), m_type(t), m_value(v), m_body(b) {
m_hash = ::lean::hash(m_hash, m_weight);
}
void expr_let::dealloc(buffer<expr_cell*> & todelete) {
dec_ref(m_body, todelete);
dec_ref(m_value, todelete);
dec_ref(m_type, todelete);
this->~expr_let();
get_let_allocator().recycle(this);
}
// Macro definition
bool macro_definition_cell::lt(macro_definition_cell const &) const { return false; }
bool macro_definition_cell::operator==(macro_definition_cell const & other) const { return typeid(*this) == typeid(other); }
@ -361,6 +384,9 @@ expr mk_app(expr const & f, expr const & a, tag g) {
expr mk_binding(expr_kind k, name const & n, expr const & t, expr const & e, binder_info const & i, tag g) {
return cache(expr(new (get_binding_allocator().allocate()) expr_binding(k, n, t, e, i, g)));
}
expr mk_let(name const & n, expr const & t, expr const & v, expr const & b, tag g) {
return cache(expr(new (get_let_allocator().allocate()) expr_let(n, t, v, b, g)));
}
expr mk_sort(level const & l, tag g) {
return cache(expr(new (get_sort_allocator().allocate()) expr_sort(l, g)));
}
@ -385,7 +411,8 @@ void expr_cell::dealloc() {
case expr_kind::App: static_cast<expr_app*>(it)->dealloc(todo); break;
case expr_kind::Lambda:
case expr_kind::Pi: static_cast<expr_binding*>(it)->dealloc(todo); break;
}
case expr_kind::Let: static_cast<expr_let*>(it)->dealloc(todo); break;
}
}
} catch (std::bad_alloc&) {
// We need this catch, because push_back may fail when expanding the buffer.
@ -510,7 +537,7 @@ unsigned get_weight(expr const & e) {
case expr_kind::Meta: case expr_kind::Local:
return 1;
case expr_kind::Lambda: case expr_kind::Pi: case expr_kind::Macro:
case expr_kind::App:
case expr_kind::App: case expr_kind::Let:
return static_cast<expr_composite*>(e.raw())->m_weight;
}
lean_unreachable(); // LCOV_EXCL_LINE
@ -591,6 +618,13 @@ expr update_macro(expr const & e, unsigned num, expr const * args) {
return mk_macro(to_macro(e)->m_definition, num, args, e.get_tag());
}
expr update_let(expr const & e, expr const & new_type, expr const & new_value, expr const & new_body) {
if (!is_eqp(let_type(e), new_type) || !is_eqp(let_value(e), new_value) || !is_eqp(let_body(e), new_body))
return mk_let(let_name(e), new_type, new_value, new_body);
else
return e;
}
bool is_atomic(expr const & e) {
switch (e.kind()) {
case expr_kind::Constant: case expr_kind::Sort:
@ -600,7 +634,7 @@ bool is_atomic(expr const & e) {
return to_macro(e)->get_num_args() == 0;
case expr_kind::App: case expr_kind::Meta:
case expr_kind::Local: case expr_kind::Lambda:
case expr_kind::Pi:
case expr_kind::Pi: case expr_kind::Let:
return false;
}
lean_unreachable(); // LCOV_EXCL_LINE
@ -691,6 +725,7 @@ std::ostream & operator<<(std::ostream & out, expr_kind const & k) {
case expr_kind::Lambda: out << "Lambda"; break;
case expr_kind::Pi: out << "Pi"; break;
case expr_kind::Macro: out << "Macro"; break;
case expr_kind::Let: out << "Let"; break;
}
return out;
}

View file

@ -41,10 +41,10 @@ class expr;
| App expr expr
| Lambda name expr expr
| Pi name expr expr
| Let name expr expr expr
| Macro macro
*/
enum class expr_kind { Var, Sort, Constant, Meta, Local, App, Lambda, Pi, Macro };
enum class expr_kind { Var, Sort, Constant, Meta, Local, App, Lambda, Pi, Let, Macro };
class expr_cell {
protected:
// The bits of the following field mean:
@ -138,8 +138,8 @@ public:
friend expr mk_app(expr const & f, expr const & a, tag g);
friend expr mk_binding(expr_kind k, name const & n, expr const & t, expr const & e, binder_info const & i,
tag g);
friend expr mk_let(name const & n, expr const & t, expr const & v, expr const & b, tag g);
friend expr mk_macro(macro_definition const & m, unsigned num, expr const * args, tag g);
friend bool is_eqp(expr const & a, expr const & b) { return a.m_ptr == b.m_ptr; }
};
@ -298,6 +298,22 @@ public:
binder const & get_binder() const { return m_binder; }
};
/** \brief Let-expressions */
class expr_let : public expr_composite {
name m_name;
expr m_type;
expr m_value;
expr m_body;
friend class expr_cell;
void dealloc(buffer<expr_cell*> & todelete);
public:
expr_let(name const & n, expr const & t, expr const & v, expr const & b, tag g);
name const & get_name() const { return m_name; }
expr const & get_type() const { return m_type; }
expr const & get_value() const { return m_value; }
expr const & get_body() const { return m_body; }
};
/** \brief Sort */
class expr_sort : public expr_cell {
level m_level;
@ -397,6 +413,7 @@ inline bool is_macro(expr_ptr e) { return e->kind() == expr_kind::Macro; }
inline bool is_app(expr_ptr e) { return e->kind() == expr_kind::App; }
inline bool is_lambda(expr_ptr e) { return e->kind() == expr_kind::Lambda; }
inline bool is_pi(expr_ptr e) { return e->kind() == expr_kind::Pi; }
inline bool is_let(expr_ptr e) { return e->kind() == expr_kind::Let; }
inline bool is_sort(expr_ptr e) { return e->kind() == expr_kind::Sort; }
inline bool is_binding(expr_ptr e) { return is_lambda(e) || is_pi(e); }
inline bool is_mlocal(expr_ptr e) { return is_metavar(e) || is_local(e); }
@ -450,6 +467,7 @@ inline expr mk_lambda(name const & n, expr const & t, expr const & e,
inline expr mk_pi(name const & n, expr const & t, expr const & e, binder_info const & i = binder_info(), tag g = nulltag) {
return mk_binding(expr_kind::Pi, n, t, e, i, g);
}
expr mk_let(name const & n, expr const & t, expr const & v, expr const & b, tag g = nulltag);
expr mk_sort(level const & l, tag g = nulltag);
expr mk_Prop();
@ -496,6 +514,7 @@ inline expr_sort * to_sort(expr_ptr e) { lean_assert(is_sort(e));
inline expr_mlocal * to_mlocal(expr_ptr e) { lean_assert(is_mlocal(e)); return static_cast<expr_mlocal*>(e); }
inline expr_local * to_local(expr_ptr e) { lean_assert(is_local(e)); return static_cast<expr_local*>(e); }
inline expr_mlocal * to_metavar(expr_ptr e) { lean_assert(is_metavar(e)); return static_cast<expr_mlocal*>(e); }
inline expr_let * to_let(expr_ptr e) { lean_assert(is_let(e)); return static_cast<expr_let*>(e); }
inline expr_macro * to_macro(expr_ptr e) { lean_assert(is_macro(e)); return static_cast<expr_macro*>(e); }
// =======================================
@ -524,6 +543,11 @@ inline name const & mlocal_name(expr_ptr e) { return to_mlocal(e)->g
inline expr const & mlocal_type(expr_ptr e) { return to_mlocal(e)->get_type(); }
inline name const & local_pp_name(expr_ptr e) { return to_local(e)->get_pp_name(); }
inline binder_info const & local_info(expr_ptr e) { return to_local(e)->get_info(); }
inline name const & let_name(expr_ptr e) { return to_let(e)->get_name(); }
inline expr const & let_type(expr_ptr e) { return to_let(e)->get_type(); }
inline expr const & let_value(expr_ptr e) { return to_let(e)->get_value(); }
inline expr const & let_body(expr_ptr e) { return to_let(e)->get_body(); }
inline bool is_constant(expr const & e, name const & n) { return is_constant(e) && const_name(e) == n; }
inline bool has_metavar(expr const & e) { return e.has_metavar(); }
@ -609,6 +633,8 @@ expr update_local(expr const & e, binder_info const & bi);
expr update_sort(expr const & e, level const & new_level);
expr update_constant(expr const & e, levels const & new_levels);
expr update_macro(expr const & e, unsigned num, expr const * args);
expr update_let(expr const & e, expr const & new_type, expr const & new_value, expr const & new_body);
// =======================================
// =======================================

View file

@ -97,6 +97,13 @@ class expr_eq_fn {
apply(binding_body(a), binding_body(b)) &&
(!CompareBinderInfo || binding_name(a) == binding_name(b)) &&
(!CompareBinderInfo || binding_info(a) == binding_info(b));
case expr_kind::Let:
check_system();
return
apply(let_type(a), let_type(b)) &&
apply(let_value(a), let_value(b)) &&
apply(let_body(a), let_body(b)) &&
(!CompareBinderInfo || let_name(a) == let_name(b));
case expr_kind::Sort:
return sort_level(a) == sort_level(b);
case expr_kind::Macro:

View file

@ -106,6 +106,11 @@ class for_each_fn {
todo.emplace_back(binding_body(e), offset + 1);
todo.emplace_back(binding_domain(e), offset);
goto begin_loop;
case expr_kind::Let:
todo.emplace_back(let_body(e), offset + 1);
todo.emplace_back(let_value(e), offset);
todo.emplace_back(let_type(e), offset);
goto begin_loop;
}
}
}

View file

@ -216,6 +216,14 @@ protected:
return update_binding(e, new_d, new_b);
}
expr visit_let(expr const & e) {
lean_assert(is_let(e));
expr new_t = visit(let_type(e));
expr new_v = visit(let_value(e));
expr new_b = visit(let_body(e));
return update_let(e, new_t, new_v, new_b);
}
expr visit(expr const & e) {
if (!has_metavar(e))
return e;
@ -238,6 +246,7 @@ protected:
case expr_kind::App: return save_result(e, visit_app(e));
case expr_kind::Lambda:
case expr_kind::Pi: return save_result(e, visit_binding(e));
case expr_kind::Let: return save_result(e, visit_let(e));
}
lean_unreachable();
}

View file

@ -58,6 +58,12 @@ class replace_rec_fn {
expr new_b = apply(binding_body(e), offset+1);
return save_result(e, offset, update_binding(e, new_d, new_b), shared);
}
case expr_kind::Let: {
expr new_t = apply(let_type(e), offset);
expr new_v = apply(let_value(e), offset);
expr new_b = apply(let_body(e), offset+1);
return save_result(e, offset, update_let(e, new_t, new_v, new_b), shared);
}
case expr_kind::Macro: {
buffer<expr> new_args;
unsigned nargs = macro_num_args(e);

View file

@ -300,6 +300,25 @@ pair<expr, constraint_seq> type_checker::infer_app(expr const & e, bool infer_on
}
}
pair<expr, constraint_seq> type_checker::infer_let(expr const & e, bool infer_only) {
if (!infer_only) {
pair<expr, constraint_seq> dtcs = infer_type_core(let_type(e), infer_only);
pair<expr, constraint_seq> scs = ensure_sort_core(dtcs.first, e);
pair<expr, constraint_seq> vcs = infer_type_core(let_value(e), infer_only);
expr v_type = vcs.first;
// TODO(Leo): we will remove justifications in the future.
as_delayed_justification jst(mk_justification("let mismatch"));
pair<bool, constraint_seq> dcs = is_def_eq(v_type, let_type(e), jst);
if (!dcs.first) {
throw_kernel_exception(m_env, e,
[=](formatter const & fmt) {
return pp_def_type_mismatch(fmt, let_name(e), let_type(e), v_type, true);
});
}
}
return infer_type_core(instantiate(let_body(e), let_value(e)), infer_only);
}
expr type_checker::infer_type_core(expr const & e, bool infer_only, constraint_seq & cs) {
auto r = infer_type_core(e, infer_only);
cs = cs + r.second;
@ -338,6 +357,7 @@ pair<expr, constraint_seq> type_checker::infer_type_core(expr const & e, bool in
case expr_kind::Lambda: r = infer_lambda(e, infer_only); break;
case expr_kind::Pi: r = infer_pi(e, infer_only); break;
case expr_kind::App: r = infer_app(e, infer_only); break;
case expr_kind::Let: r = infer_let(e, infer_only); break;
}
if (m_memoize)

View file

@ -126,6 +126,7 @@ class type_checker {
pair<expr, constraint_seq> infer_lambda(expr const & e, bool infer_only);
pair<expr, constraint_seq> infer_pi(expr const & e, bool infer_only);
pair<expr, constraint_seq> infer_app(expr const & e, bool infer_only);
pair<expr, constraint_seq> infer_let(expr const & e, bool infer_only);
pair<expr, constraint_seq> infer_type_core(expr const & e, bool infer_only);
pair<expr, constraint_seq> infer_type(expr const & e);
expr infer_type_core(expr const & e, bool infer_only, constraint_seq & cs);

View file

@ -31,6 +31,9 @@ unsigned abstract_expr_manager::hash(expr const & e) {
h = ::lean::hash(h, hash(binding_body(e)));
m_locals.pop_back();
return h;
case expr_kind::Let:
// Let-expressions must be unfolded before invoking this method
lean_unreachable();
case expr_kind::App:
buffer<expr> args;
expr const & f = get_app_args(e, args);
@ -80,6 +83,9 @@ bool abstract_expr_manager::is_equal(expr const & a, expr const & b) {
is_eq = is_equal(binding_body(a), binding_body(b));
m_locals.pop_back();
return is_eq;
case expr_kind::Let:
// Let-expressions must be unfolded before invoking this method
lean_unreachable();
case expr_kind::Macro:
if (macro_def(a) != macro_def(b) || macro_num_args(a) != macro_num_args(b))
return false;

View file

@ -957,6 +957,9 @@ void congruence_closure::internalize_core(name R, expr const & e, bool toplevel,
switch (e.kind()) {
case expr_kind::Var:
lean_unreachable();
case expr_kind::Let:
// Let-expressions must be unfolded before invoking this method
lean_unreachable();
case expr_kind::Sort:
return;
case expr_kind::Constant: case expr_kind::Local:

View file

@ -235,6 +235,7 @@ auto discr_tree::insert_erase(node && n, bool is_root, buffer<pair<expr, bool>>
lean_unreachable();
case expr_kind::Sort: case expr_kind::Lambda:
case expr_kind::Pi: case expr_kind::Macro:
case expr_kind::Let:
// unsupported
return insert_erase_atom(std::move(n), edge(edge_kind::Unsupported), todo, v, skip, ins);
}
@ -331,6 +332,7 @@ bool discr_tree::find(node const & n, list<pair<expr, bool>> todo, std::function
lean_unreachable();
case expr_kind::Sort: case expr_kind::Lambda:
case expr_kind::Pi: case expr_kind::Macro:
case expr_kind::Let:
// unsupported
return find_atom(n, edge(edge_kind::Unsupported), tail(todo), fn);
}

View file

@ -76,6 +76,9 @@ public:
for (unsigned i = 0; i < macro_num_args(e); i++)
collect_apps(macro_arg(e, i));
break;
case expr_kind::Let:
// let-expressions must be unfolded
lean_unreachable();
case expr_kind::App: {
buffer<expr> args;
expr const & f = get_app_args(e, args);

View file

@ -52,6 +52,9 @@ void forward_branch_extension::index_expr(expr const & e) {
case expr_kind::Pi:
// TODO(dhs): confirm that I only index quantified-free hypotheses
break;
case expr_kind::Let:
// Let-expressions must be unfolded before invoking this method
lean_unreachable();
case expr_kind::App:
index_expr(app_fn(e));
index_expr(app_arg(e));

View file

@ -367,6 +367,9 @@ struct mk_hi_lemma_fn {
case expr_kind::Meta: case expr_kind::Local:
case expr_kind::Pi:
return candidate_set();
case expr_kind::Let:
// let-expressions must be unfolded
lean_unreachable();
case expr_kind::Lambda:
if (has_idx_metavar(a))
return candidate_set(candidate(a));

View file

@ -219,6 +219,9 @@ static bool is_permutation(expr const & lhs, expr const & rhs, unsigned offset,
return
is_permutation(binding_domain(lhs), binding_domain(rhs), offset, p) &&
is_permutation(binding_body(lhs), binding_body(rhs), offset+1, p);
case expr_kind::Let:
// Let-expressions must be unfolded before invoking this method
lean_unreachable();
case expr_kind::App:
return
is_permutation(app_fn(lhs), app_fn(rhs), offset, p) &&

View file

@ -430,6 +430,9 @@ result simplifier::simplify(expr const & e, bool is_root) {
case expr_kind::App:
r = join(r, simplify_app(r.get_new()));
break;
case expr_kind::Let:
// whnf unfolds let-expressions
lean_unreachable();
}
if (!m_top_down) r = join(r, rewrite(whnf_eta(r.get_new())));

View file

@ -397,7 +397,7 @@ static lbool is_quick_ext_class(type_checker const & tc, expr const & type, name
while (true) {
switch (it->kind()) {
case expr_kind::Var: case expr_kind::Sort: case expr_kind::Local:
case expr_kind::Meta: case expr_kind::Lambda:
case expr_kind::Meta: case expr_kind::Lambda: case expr_kind::Let:
return l_false;
case expr_kind::Macro:
return l_undef;

View file

@ -21,6 +21,7 @@ expr copy(expr const & a) {
case expr_kind::Pi: return mk_pi(binding_name(a), binding_domain(a), binding_body(a), binding_info(a));
case expr_kind::Meta: return mk_metavar(mlocal_name(a), mlocal_type(a));
case expr_kind::Local: return mk_local(mlocal_name(a), local_pp_name(a), mlocal_type(a), local_info(a));
case expr_kind::Let: return mk_let(let_name(a), let_type(a), let_value(a), let_body(a));
}
lean_unreachable(); // LCOV_EXCL_LINE
}

View file

@ -140,6 +140,9 @@ class defeq_simplify_fn {
case expr_kind::App:
e = defeq_simplify_app(e);
break;
case expr_kind::Let:
lean_unreachable();
// whnf expands let-expressions
}
if (!m_top_down) e = rewrite(whnf_eta(e));

View file

@ -101,6 +101,11 @@ class expr_eq_modulo_placeholders_fn {
compare(binding_domain(a), binding_domain(b)) &&
compare(binding_body(a), binding_body(b)) &&
binding_info(a) == binding_info(b);
case expr_kind::Let:
return
compare(let_type(a), let_type(b)) &&
compare(let_value(a), let_value(b)) &&
compare(let_body(a), let_body(b));
case expr_kind::Sort:
return compare(sort_level(a), sort_level(b));
case expr_kind::Macro:

View file

@ -1209,6 +1209,9 @@ class equation_compiler_fn {
}
return true;
}
case expr_kind::Let:
// TODO(Leo): improve
return check_rhs(instantiate(let_body(e), let_value(e)), arg);
case expr_kind::Lambda:
case expr_kind::Pi:
if (!check_rhs(binding_domain(e), arg)) {
@ -1416,6 +1419,10 @@ class equation_compiler_fn {
expr local = mk_local(mk_fresh_name(), binding_name(e), new_domain, binding_info(e));
expr new_body = elim(instantiate(binding_body(e), local), b);
return copy_tag(e, Pi(local, new_body));
}
case expr_kind::Let: {
// TODO(Leo): improve
return elim(instantiate(let_body(e), let_value(e)), b);
}}
lean_unreachable();
}

View file

@ -7,6 +7,7 @@ Author: Leonardo de Moura
#include <unordered_map>
#include "kernel/expr_maps.h"
#include "kernel/for_each_fn.h"
#include "kernel/instantiate.h"
#include "kernel/inductive/inductive.h"
#include "library/module.h"
#include "library/unfold_macros.h"
@ -160,6 +161,9 @@ class exporter {
i = m_expr2idx.size();
m_out << i << " #EA " << e1 << " " << e2 << "\n";
break;
case expr_kind::Let:
i = export_expr(instantiate(let_body(e), let_value(e)));
break;
case expr_kind::Lambda:
i = export_binding(e, "#EL");
break;

View file

@ -38,6 +38,13 @@ bool is_lt(expr const & a, expr const & b, bool use_hash) {
return is_lt(binding_domain(a), binding_domain(b), use_hash);
else
return is_lt(binding_body(a), binding_body(b), use_hash);
case expr_kind::Let:
if (let_type(a) != let_type(b))
return is_lt(let_type(a), let_type(b), use_hash);
else if (let_value(a) != let_value(b))
return is_lt(let_value(a), let_value(b), use_hash);
else
return is_lt(let_body(a), let_body(b), use_hash);
case expr_kind::Sort:
return is_lt(sort_level(a), sort_level(b), use_hash);
case expr_kind::Local: case expr_kind::Meta:
@ -137,6 +144,17 @@ bool is_lt_no_level_params(expr const & a, expr const & b) {
return false;
else
return is_lt_no_level_params(binding_body(a), binding_body(b));
case expr_kind::Let:
if (is_lt_no_level_params(let_type(a), let_type(b)))
return true;
else if (is_lt_no_level_params(let_type(b), let_type(a)))
return false;
else if (is_lt_no_level_params(let_value(a), let_value(b)))
return true;
else if (is_lt_no_level_params(let_value(b), let_value(a)))
return false;
else
return is_lt_no_level_params(let_body(a), let_body(b));
case expr_kind::Sort:
return is_lt_no_level_params(sort_level(a), sort_level(b));
case expr_kind::Local: case expr_kind::Meta:

View file

@ -182,6 +182,10 @@ class expr_serializer : public object_serializer<expr, expr_hash_alloc, expr_eqp
write_binder_name(s, binding_name(a));
s << binding_info(a); write_core(binding_domain(a)); write_core(binding_body(a));
break;
case expr_kind::Let:
s << let_name(a);
write_core(let_type(a)); write_core(let_value(a)); write_core(let_body(a));
break;
case expr_kind::Meta:
lean_assert(!mlocal_name(a).is_anonymous());
s << mlocal_name(a); write_core(mlocal_type(a));
@ -239,6 +243,12 @@ public:
}
case expr_kind::Lambda: case expr_kind::Pi:
return read_binding(k);
case expr_kind::Let: {
name n = read_name(d);
expr t = read();
expr v = read();
return mk_let(n, t, v, read());
}
case expr_kind::Meta: {
name n = read_name(d);
return mk_metavar(n, read());

View file

@ -89,28 +89,28 @@ public:
}
};
expr mk_let(name const & n, expr const & v, expr const & b) {
expr mk_let_macro(name const & n, expr const & v, expr const & b) {
auto d = macro_definition(new let_macro_definition_cell(n));
expr args[2] = {v, b};
return mk_macro(d, 2, args);
}
bool is_let(expr const & e) {
bool is_let_macro(expr const & e) {
return is_macro(e) && dynamic_cast<let_macro_definition_cell const *>(macro_def(e).raw()) != nullptr;
}
name const & get_let_var_name(expr const & e) {
lean_assert(is_let(e));
lean_assert(is_let_macro(e));
return static_cast<let_macro_definition_cell const *>(macro_def(e).raw())->get_var_name();
}
expr const & get_let_value(expr const & e) {
lean_assert(is_let(e));
lean_assert(is_let_macro(e));
return macro_arg(e, 0);
}
expr const & get_let_body(expr const & e) {
lean_assert(is_let(e));
lean_assert(is_let_macro(e));
return macro_arg(e, 1);
}
@ -130,7 +130,7 @@ void initialize_let() {
if (num != 2) throw corrupted_stream_exception();
name n;
d >> n;
return mk_let(n, args[0], args[1]);
return mk_let_macro(n, args[0], args[1]);
});
}

View file

@ -7,13 +7,15 @@ Author: Leonardo de Moura
#pragma once
#include "kernel/expr.h"
// TODO(Leo): delete this module after we move to kernel let-expr
namespace lean {
expr mk_let_value(expr const & e);
bool is_let_value(expr const & e);
expr get_let_value_expr(expr const e);
expr mk_let(name const & n, expr const & v, expr const & b);
bool is_let(expr const & e);
expr mk_let_macro(name const & n, expr const & v, expr const & b);
bool is_let_macro(expr const & e);
name const & get_let_var_name(expr const & e);
expr const & get_let_value(expr const & e);
expr const & get_let_body(expr const & e);

View file

@ -27,6 +27,9 @@ unsigned light_lt_manager::get_weight_core(expr const & e) {
case expr_kind::Var: case expr_kind::Constant: case expr_kind::Sort:
case expr_kind::Meta: case expr_kind::Local:
return 1;
case expr_kind::Let:
// Let-expressions must be unfolded before invoking this method
lean_unreachable();
case expr_kind::Lambda: case expr_kind::Pi:
return safe_add(1, safe_add(get_weight(binding_domain(e)), get_weight(binding_body(e))));
case expr_kind::Macro:
@ -76,6 +79,9 @@ bool light_lt_manager::is_lt(expr const & a, expr const & b) {
if (a.kind() != b.kind()) return a.kind() < b.kind();
if (a == b) return false;
switch (a.kind()) {
case expr_kind::Let:
// Let-expressions must be unfolded before invoking this method
lean_unreachable();
case expr_kind::Var:
return var_idx(a) < var_idx(b);
case expr_kind::Constant:

View file

@ -95,6 +95,11 @@ void collect_locals(expr const & e, collected_locals & ls, bool restricted) {
visit(binding_domain(e));
visit(binding_body(e));
break;
case expr_kind::Let:
visit(let_type(e));
visit(let_value(e));
visit(let_body(e));
break;
}
};
visit(e);

View file

@ -323,6 +323,9 @@ class match_fn : public match_context {
return match_macro(p, t);
case expr_kind::App:
return match_app(p, t);
case expr_kind::Let:
// TODO(Leo): this module will be deleted in the future.
lean_unreachable();
}
lean_unreachable(); // LCOV_EXCL_LINE
}

View file

@ -62,12 +62,25 @@ struct max_sharing_fn::imp {
case expr_kind::Sort:
res = update_sort(a, apply(sort_level(a)));
break;
case expr_kind::App:
res = update_app(a, apply(app_fn(a)), apply(app_arg(a)));
case expr_kind::App: {
expr new_f = apply(app_fn(a));
expr new_a = apply(app_arg(a));
res = update_app(a, new_f, new_a);
break;
case expr_kind::Lambda: case expr_kind::Pi:
res = update_binding(a, apply(binding_domain(a)), apply(binding_body(a)));
}
case expr_kind::Lambda: case expr_kind::Pi: {
expr new_d = apply(binding_domain(a));
expr new_b = apply(binding_body(a));
res = update_binding(a, new_d, new_b);
break;
}
case expr_kind::Let: {
expr new_t = apply(let_type(a));
expr new_v = apply(let_value(a));
expr new_b = apply(let_body(a));
res = update_let(a, new_t, new_v, new_b);
break;
}
case expr_kind::Meta: case expr_kind::Local:
res = update_mlocal(a, apply(mlocal_type(a)));
break;

View file

@ -401,6 +401,9 @@ class normalize_fn {
return normalize_binding(e);
case expr_kind::App:
return normalize_app(e);
case expr_kind::Let:
// whnf unfolds let-exprs
lean_unreachable();
}
lean_unreachable(); // LCOV_EXCL_LINE
}

View file

@ -21,7 +21,8 @@ bool is_used_name(expr const & t, name const & n) {
if (found) return false; // already found
if ((is_constant(e) && const_name(e) == n) // t has a constant named n
|| (is_local(e) && (mlocal_name(e) == n || local_pp_name(e) == n)) // t has a local constant named n
|| (is_let(e) && get_let_var_name(e) == n)) {
// TODO(Leo): remove after we transition to kernel Let-expression
|| (is_let_macro(e) && get_let_var_name(e) == n)) {
found = true;
return false; // found it
}
@ -178,6 +179,15 @@ struct print_expr_fn {
print_child(e);
}
void print_let(expr const & e) {
out() << "let " << let_name(e) << " : ";
print(let_type(e));
out() << " := ";
print(let_value(e));
out() << " in ";
print(let_body(e));
}
void print_const(expr const & a) {
list<level> const & ls = const_levels(a);
out() << const_name(a);
@ -212,6 +222,9 @@ struct print_expr_fn {
case expr_kind::App:
print_app(a);
break;
case expr_kind::Let:
print_let(a);
break;
case expr_kind::Lambda:
print_binding("fun", a);
break;

View file

@ -30,6 +30,13 @@ expr replace_visitor::visit_binding(expr const & e) {
}
expr replace_visitor::visit_lambda(expr const & e) { return visit_binding(e); }
expr replace_visitor::visit_pi(expr const & e) { return visit_binding(e); }
expr replace_visitor::visit_let(expr const & e) {
lean_assert(is_let(e));
expr new_t = visit(let_type(e));
expr new_v = visit(let_value(e));
expr new_b = visit(let_body(e));
return update_let(e, new_t, new_v, new_b);
}
expr replace_visitor::visit_macro(expr const & e) {
lean_assert(is_macro(e));
buffer<expr> new_args;
@ -62,6 +69,7 @@ expr replace_visitor::visit(expr const & e) {
case expr_kind::App: return save_result(e, visit_app(e), shared);
case expr_kind::Lambda: return save_result(e, visit_lambda(e), shared);
case expr_kind::Pi: return save_result(e, visit_pi(e), shared);
case expr_kind::Let: return save_result(e, visit_let(e), shared);
}
lean_unreachable(); // LCOV_EXCL_LINE

View file

@ -30,6 +30,7 @@ protected:
virtual expr visit_binding(expr const &);
virtual expr visit_lambda(expr const &);
virtual expr visit_pi(expr const &);
virtual expr visit_let(expr const & e);
virtual expr visit(expr const &);
public:
expr operator()(expr const & e) { return visit(e); }

View file

@ -219,6 +219,8 @@ expr type_context::whnf_core(expr const & e) {
case expr_kind::Var: case expr_kind::Sort: case expr_kind::Meta: case expr_kind::Local:
case expr_kind::Pi: case expr_kind::Constant: case expr_kind::Lambda:
return e;
case expr_kind::Let:
return whnf_core(instantiate(let_body(e), let_value(e)));
case expr_kind::Macro:
if (auto m = expand_macro(e))
return whnf_core(*m);
@ -309,6 +311,7 @@ expr type_context::whnf(expr const & e) {
case expr_kind::Var: case expr_kind::Sort: case expr_kind::Meta: case expr_kind::Local: case expr_kind::Pi:
return e;
case expr_kind::Lambda: case expr_kind::Macro: case expr_kind::App: case expr_kind::Constant:
case expr_kind::Let:
break;
}
@ -884,6 +887,7 @@ lbool type_context::quick_is_def_eq(expr const & e1, expr const & e2) {
case expr_kind::Meta: case expr_kind::Var:
case expr_kind::Local: case expr_kind::App:
case expr_kind::Constant: case expr_kind::Macro:
case expr_kind::Let:
// We do not handle these cases in this method.
break;
}
@ -1221,6 +1225,9 @@ expr type_context::infer(expr const & e) {
case expr_kind::App:
r = infer_app(e);
break;
case expr_kind::Let:
r = infer(instantiate(let_body(e), let_value(e)));
break;
}
m_infer_cache.insert(mk_pair(e, r));
return r;
@ -1270,7 +1277,7 @@ lbool type_context::is_quick_class(expr const & type, name & result) {
while (true) {
switch (it->kind()) {
case expr_kind::Var: case expr_kind::Sort: case expr_kind::Local:
case expr_kind::Meta: case expr_kind::Lambda:
case expr_kind::Meta: case expr_kind::Lambda: case expr_kind::Let:
return l_false;
case expr_kind::Macro:
return l_undef;
@ -1449,7 +1456,7 @@ struct type_context::unification_hint_fn {
&& 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::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:
@ -2120,6 +2127,9 @@ expr normalizer::normalize(expr e) {
return normalize_binding(e);
case expr_kind::App:
return normalize_app(e);
case expr_kind::Let:
// whnf unfolds let-expressions
lean_unreachable();
}
lean_unreachable(); // LCOV_EXCL_LINE
}

View file

@ -74,6 +74,11 @@ class unfold_untrusted_macros_fn {
return r;
}
expr visit_let(expr const & e) {
// TODO(Leo): improve
return visit(instantiate(let_body(e), let_value(e)));
}
expr visit(expr const & e) {
switch (e.kind()) {
case expr_kind::Sort: case expr_kind::Constant:
@ -100,6 +105,8 @@ class unfold_untrusted_macros_fn {
return save_result(e, visit_app(e));
case expr_kind::Lambda: case expr_kind::Pi:
return save_result(e, visit_binding(e));
case expr_kind::Let:
return save_result(e, visit_let(e));
}
lean_unreachable();
}

View file

@ -2162,6 +2162,7 @@ struct unifier_fn {
mk_simple_projections();
mk_bindings_imitation();
break;
case expr_kind::Let:
case expr_kind::Macro:
lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::App:

View file

@ -124,6 +124,8 @@ static unsigned count_core(expr const & a, expr_set & s) {
return count_core(app_fn(a), s) + count_core(app_arg(a), s) + 1;
case expr_kind::Lambda: case expr_kind::Pi:
return count_core(binding_domain(a), s) + count_core(binding_body(a), s) + 1;
case expr_kind::Let:
lean_unreachable();
}
return 0;
}