expr_let::expr_let(name const & n, expr const & t, expr const & v, expr const & b): expr_cell(expr_kind::Let, ::lean::hash(v.hash(), b.hash()), v.has_metavar() || b.has_metavar() || (t && t.has_metavar())), m_name(n), m_type(t), m_value(v), m_body(b) { }
bool apply(expr const & a, expr const & b) { if (is_eqp(a, b)) return true; if (a.hash() != b.hash()) return false; if (a.kind() != b.kind()) return false; if (is_var(a)) return var_idx(a) == var_idx(b); if (m_cache.check(a, b)) return true; switch (a.kind()) { case expr_kind::Var: lean_unreachable(); // LCOV_EXCL_LINE case expr_kind::Constant: return const_name(a) == const_name(b) && compare(const_levels(a), const_levels(b), [](level const & l1, level const & l2) { return l1 == l2; }); case expr_kind::Meta: return mlocal_name(a) == mlocal_name(b) && apply(mlocal_type(a), mlocal_type(b)); case expr_kind::Local: return mlocal_name(a) == mlocal_name(b) && apply(mlocal_type(a), mlocal_type(b)) && (!CompareBinderInfo || local_pp_name(a) == local_pp_name(b)) && (!CompareBinderInfo || local_info(a) == local_info(b)); case expr_kind::App: check_system(); return apply(app_fn(a), app_fn(b)) && apply(app_arg(a), app_arg(b)); case expr_kind::Lambda: case expr_kind::Pi: check_system(); return apply(binding_domain(a), binding_domain(b)) && 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: check_system(); if (macro_def(a) != macro_def(b) || macro_num_args(a) != macro_num_args(b)) return false; for (unsigned i = 0; i < macro_num_args(a); i++) { if (!apply(macro_arg(a, i), macro_arg(b, i))) return false; } return true; } lean_unreachable(); // LCOV_EXCL_LINE }
bool is_lt(expr const & a, expr const & b, bool use_hash) { if (is_eqp(a, b)) return false; unsigned wa = get_weight(a); unsigned wb = get_weight(b); if (wa < wb) return true; if (wa > wb) return false; if (a.kind() != b.kind()) return a.kind() < b.kind(); if (use_hash) { if (a.hash() < b.hash()) return true; if (a.hash() > b.hash()) return false; } if (a == b) return false; switch (a.kind()) { case expr_kind::Var: return var_idx(a) < var_idx(b); case expr_kind::Constant: if (const_name(a) != const_name(b)) return const_name(a) < const_name(b); else return is_lt(const_levels(a), const_levels(b), use_hash); case expr_kind::App: if (app_fn(a) != app_fn(b)) return is_lt(app_fn(a), app_fn(b), use_hash); else return is_lt(app_arg(a), app_arg(b), use_hash); case expr_kind::Lambda: case expr_kind::Pi: if (binding_domain(a) != binding_domain(b)) 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: if (mlocal_name(a) != mlocal_name(b)) return mlocal_name(a) < mlocal_name(b); else return is_lt(mlocal_type(a), mlocal_type(b), use_hash); case expr_kind::Macro: if (macro_def(a) != macro_def(b)) return macro_def(a) < macro_def(b); if (macro_num_args(a) != macro_num_args(b)) return macro_num_args(a) < macro_num_args(b); for (unsigned i = 0; i < macro_num_args(a); i++) { if (macro_arg(a, i) != macro_arg(b, i)) return is_lt(macro_arg(a, i), macro_arg(b, i), use_hash); } return false; } lean_unreachable(); // LCOV_EXCL_LINE }
bool expr_eq_fn::apply(expr const & a, expr const & b) { if (is_eqp(a, b)) return true; if (a.hash() != b.hash()) return false; if (a.kind() != b.kind()) return false; if (is_var(a)) return var_idx(a) == var_idx(b); if (m_counter >= LEAN_EQ_CACHE_THRESHOLD && is_shared(a) && is_shared(b)) { auto p = std::make_pair(a.raw(), b.raw()); if (!m_eq_visited) m_eq_visited.reset(new expr_cell_pair_set); if (m_eq_visited->find(p) != m_eq_visited->end()) return true; m_eq_visited->insert(p); } check_system("expression equality test"); switch (a.kind()) { case expr_kind::Var: lean_unreachable(); // LCOV_EXCL_LINE case expr_kind::Constant: return const_name(a) == const_name(b) && compare(const_levels(a), const_levels(b), [](level const & l1, level const & l2) { return l1 == l2; }); case expr_kind::Local: case expr_kind::Meta: return mlocal_name(a) == mlocal_name(b) && apply(mlocal_type(a), mlocal_type(b)); case expr_kind::App: m_counter++; return apply(app_fn(a), app_fn(b)) && apply(app_arg(a), app_arg(b)); case expr_kind::Lambda: case expr_kind::Pi: m_counter++; return apply(binding_domain(a), binding_domain(b)) && apply(binding_body(a), binding_body(b)) && (!m_compare_binder_info || binding_info(a) == binding_info(b)); case expr_kind::Sort: return sort_level(a) == sort_level(b); case expr_kind::Macro: m_counter++; if (macro_def(a) != macro_def(b) || macro_num_args(a) != macro_num_args(b)) return false; for (unsigned i = 0; i < macro_num_args(a); i++) { if (!apply(macro_arg(a, i), macro_arg(b, i))) return false; } return true; case expr_kind::Let: m_counter++; return apply(let_type(a), let_type(b)) && apply(let_value(a), let_value(b)) && apply(let_body(a), let_body(b)); } lean_unreachable(); // LCOV_EXCL_LINE }
expr_app::expr_app(expr const & fn, expr const & arg, tag g): expr_composite(expr_kind::App, ::lean::hash(fn.hash(), arg.hash()), fn.has_expr_metavar() || arg.has_expr_metavar(), fn.has_univ_metavar() || arg.has_univ_metavar(), fn.has_local() || arg.has_local(), fn.has_param_univ() || arg.has_param_univ(), inc_weight(add_weight(get_weight(fn), get_weight(arg))), std::max(get_free_var_range(fn), get_free_var_range(arg)), g), m_fn(fn), m_arg(arg) { m_hash = ::lean::hash(m_hash, m_weight); }
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); }
expr_binding::expr_binding(expr_kind k, name const & n, expr const & t, expr const & b, binder_info const & i, tag g): expr_composite(k, ::lean::hash(t.hash(), b.hash()), t.has_expr_metavar() || b.has_expr_metavar(), t.has_univ_metavar() || b.has_univ_metavar(), t.has_local() || b.has_local(), t.has_param_univ() || b.has_param_univ(), inc_weight(add_weight(get_weight(t), get_weight(b))), std::max(get_free_var_range(t), dec(get_free_var_range(b))), g), m_binder(n, t, i), m_body(b) { m_hash = ::lean::hash(m_hash, m_weight); lean_assert(k == expr_kind::Lambda || k == expr_kind::Pi); }
unsigned abstract_expr_manager::hash(expr const & e) { unsigned h; switch (e.kind()) { case expr_kind::Constant: case expr_kind::Local: case expr_kind::Meta: case expr_kind::Sort: case expr_kind::Var: case expr_kind::Macro: return e.hash(); case expr_kind::Lambda: case expr_kind::Pi: h = hash(binding_domain(e)); // Remark binding_domain(e) may contain de-bruijn variables. // We can instantiate them eagerly as we do here, or lazily. // The lazy approach is potentially more efficient, but we would have // to use something more sophisticated than an instantiate_rev at expr_kind::App m_locals.push_back(instantiate_rev(m_tctx.mk_tmp_local(binding_domain(e)), m_locals.size(), m_locals.data())); 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); unsigned prefix_sz = m_congr_lemma_manager.get_specialization_prefix_size(instantiate_rev(f, m_locals.size(), m_locals.data()), args.size()); expr new_f = e; unsigned rest_sz = args.size() - prefix_sz; for (unsigned i = 0; i < rest_sz; i++) new_f = app_fn(new_f); new_f = instantiate_rev(new_f, m_locals.size(), m_locals.data()); optional<congr_lemma> congr = m_congr_lemma_manager.mk_congr(new_f, rest_sz); h = hash(new_f); if (!congr) { for (unsigned i = prefix_sz; i < args.size(); i++) { h = ::lean::hash(h, hash(args[i])); } } else { lean_assert(length(congr->get_arg_kinds()) == rest_sz); unsigned i = prefix_sz; for_each(congr->get_arg_kinds(), [&](congr_arg_kind const & c_kind) { if (c_kind != congr_arg_kind::Cast) { h = ::lean::hash(h, hash(args[i])); } i++; }); } return h; } lean_unreachable(); }
unsigned hash_bi(expr const & e) { unsigned h = e.hash(); for_each(e, [&](expr const & e, unsigned) { if (is_binding(e)) { h = hash(h, hash(binding_name(e).hash(), binding_info(e).hash())); } else if (is_local(e)) { h = hash(h, hash(mlocal_name(e).hash(), local_info(e).hash())); return false; // do not visit type } else if (is_metavar(e)) { return false; // do not visit type } return true; }); return h; }
expr_abstraction::expr_abstraction(expr_kind k, name const & n, expr const & t, expr const & b): expr_cell(k, ::lean::hash(t.hash(), b.hash()), t.has_metavar() || b.has_metavar()), m_name(n), m_domain(t), m_body(b) { }
expr_eq::expr_eq(expr const & lhs, expr const & rhs): expr_cell(expr_kind::Eq, ::lean::hash(lhs.hash(), rhs.hash()), lhs.has_metavar() || rhs.has_metavar()), m_lhs(lhs), m_rhs(rhs) { }
bool is_lt(expr const & a, expr const & b, bool use_hash) { if (is_eqp(a, b)) return false; unsigned da = get_depth(a); unsigned db = get_depth(b); if (da < db) return true; if (da > db) return false; if (a.kind() != b.kind()) return a.kind() < b.kind(); if (use_hash) { if (a.hash() < b.hash()) return true; if (a.hash() > b.hash()) return false; } if (a == b) return false; if (is_var(a)) return var_idx(a) < var_idx(b); switch (a.kind()) { case expr_kind::Var: lean_unreachable(); // LCOV_EXCL_LINE case expr_kind::Constant: return const_name(a) < const_name(b); case expr_kind::App: if (num_args(a) != num_args(b)) return num_args(a) < num_args(b); for (unsigned i = 0; i < num_args(a); i++) { if (arg(a, i) != arg(b, i)) return is_lt(arg(a, i), arg(b, i), use_hash); } lean_unreachable(); // LCOV_EXCL_LINE case expr_kind::Lambda: // Remark: we ignore get_abs_name because we want alpha-equivalence case expr_kind::Pi: if (abst_domain(a) != abst_domain(b)) return is_lt(abst_domain(a), abst_domain(b), use_hash); else return is_lt(abst_body(a), abst_body(b), use_hash); case expr_kind::Type: return ty_level(a) < ty_level(b); case expr_kind::Value: return to_value(a) < to_value(b); 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::MetaVar: if (metavar_name(a) != metavar_name(b)) { return metavar_name(a) < metavar_name(b); } else { auto it1 = metavar_lctx(a).begin(); auto it2 = metavar_lctx(b).begin(); auto end1 = metavar_lctx(a).end(); auto end2 = metavar_lctx(b).end(); for (; it1 != end1 && it2 != end2; ++it1, ++it2) { if (it1->kind() != it2->kind()) { return it1->kind() < it2->kind(); } else if (it1->s() != it2->s()) { return it1->s() < it2->s(); } else if (it1->is_inst()) { if (it1->v() != it2->v()) return is_lt(it1->v(), it2->v(), use_hash); } else { if (it1->n() != it2->n()) return it1->n() < it2->n(); } } return it1 == end1 && it2 != end2; } } lean_unreachable(); // LCOV_EXCL_LINE }