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; }
obj_t *env_lookup(env_t *env, obj_t *var) { /* * for frame in env: * for binding in frame: * if binding.name == var: * return binding * assert False, 'unbound variable' */ assert(is_symbol(var)); #if ENV_TRACE printf_unchecked("lookup(%ls, %O)\n", string_value(symbol_name(var)), env); #endif while (!is_null(env)) { obj_t *frame = pair_car(env); #if ENV_TRACE if (pair_cdr(env)) { printf(" FRAME"); obj_t *p = frame; while (!is_null(p)) { printf_unchecked(" %O: %O", binding_name(pair_car(p)), binding_value(pair_car(p))); p = pair_cdr(p); } printf("\n"); } else { printf(" FRAME [builtins]\n"); } #endif while (!is_null(frame)) { obj_t *binding = pair_car(frame); assert(is_binding(binding)); if (binding_name(binding) == var) { #if ENV_TRACE printf(" found\n\n"); #endif return binding; } frame = pair_cdr(frame); } env = pair_cdr(env); } fprintf(stderr, "unbound variable \"%ls\"\n", string_value(symbol_name(var))); assert(false && "unbound variable"); }
// Return true iff lhs is of the form (B (x : ?m1), ?m2) or (B (x : ?m1), ?m2 x), // where B is lambda or Pi static bool is_valid_congr_rule_binding_lhs(expr const & lhs, name_set & found_mvars) { lean_assert(is_binding(lhs)); expr const & d = binding_domain(lhs); expr const & b = binding_body(lhs); if (!is_metavar(d)) return false; if (is_metavar(b) && b != d) { found_mvars.insert(mlocal_name(b)); found_mvars.insert(mlocal_name(d)); return true; } if (is_app(b) && is_metavar(app_fn(b)) && is_var(app_arg(b), 0) && app_fn(b) != d) { found_mvars.insert(mlocal_name(app_fn(b))); found_mvars.insert(mlocal_name(d)); return true; } return false; }
expr replace_visitor::visit_binding(expr const & e) { lean_assert(is_binding(e)); expr new_d = visit(binding_domain(e)); expr new_b = visit(binding_body(e)); return update_binding(e, new_d, new_b); }
void add_congr_core(environment const & env, simp_rule_sets & s, name const & n) { declaration const & d = env.get(n); type_checker tc(env); buffer<level> us; unsigned num_univs = d.get_num_univ_params(); for (unsigned i = 0; i < num_univs; i++) { us.push_back(mk_meta_univ(name(*g_prefix, i))); } levels ls = to_list(us); expr pr = mk_constant(n, ls); expr e = instantiate_type_univ_params(d, ls); buffer<bool> explicit_args; buffer<expr> metas; unsigned idx = 0; while (is_pi(e)) { expr mvar = mk_metavar(name(*g_prefix, idx), binding_domain(e)); idx++; explicit_args.push_back(is_explicit(binding_info(e))); metas.push_back(mvar); e = instantiate(binding_body(e), mvar); pr = mk_app(pr, mvar); } expr rel, lhs, rhs; if (!is_simp_relation(env, e, rel, lhs, rhs) || !is_constant(rel)) { throw exception(sstream() << "invalid congruence rule, '" << n << "' resulting type is not of the form t ~ s, where '~' is a transitive and reflexive relation"); } name_set found_mvars; buffer<expr> lhs_args, rhs_args; expr const & lhs_fn = get_app_args(lhs, lhs_args); expr const & rhs_fn = get_app_args(rhs, rhs_args); if (is_constant(lhs_fn)) { if (!is_constant(rhs_fn) || const_name(lhs_fn) != const_name(rhs_fn) || lhs_args.size() != rhs_args.size()) { throw exception(sstream() << "invalid congruence rule, '" << n << "' resulting type is not of the form (" << const_name(lhs_fn) << " ...) " << "~ (" << const_name(lhs_fn) << " ...), where ~ is '" << const_name(rel) << "'"); } for (expr const & lhs_arg : lhs_args) { if (is_sort(lhs_arg)) continue; if (!is_metavar(lhs_arg) || found_mvars.contains(mlocal_name(lhs_arg))) { throw exception(sstream() << "invalid congruence rule, '" << n << "' the left-hand-side of the congruence resulting type must be of the form (" << const_name(lhs_fn) << " x_1 ... x_n), where each x_i is a distinct variable or a sort"); } found_mvars.insert(mlocal_name(lhs_arg)); } } else if (is_binding(lhs)) { if (lhs.kind() != rhs.kind()) { throw exception(sstream() << "invalid congruence rule, '" << n << "' kinds of the left-hand-side and right-hand-side of " << "the congruence resulting type do not match"); } if (!is_valid_congr_rule_binding_lhs(lhs, found_mvars)) { throw exception(sstream() << "invalid congruence rule, '" << n << "' left-hand-side of the congruence resulting type must " << "be of the form (fun/Pi (x : A), B x)"); } } else { throw exception(sstream() << "invalid congruence rule, '" << n << "' left-hand-side is not an application nor a binding"); } buffer<expr> congr_hyps; lean_assert(metas.size() == explicit_args.size()); for (unsigned i = 0; i < metas.size(); i++) { expr const & mvar = metas[i]; if (explicit_args[i] && !found_mvars.contains(mlocal_name(mvar))) { buffer<expr> locals; expr type = mlocal_type(mvar); while (is_pi(type)) { expr local = mk_local(tc.mk_fresh_name(), binding_domain(type)); locals.push_back(local); type = instantiate(binding_body(type), local); } expr h_rel, h_lhs, h_rhs; if (!is_simp_relation(env, type, h_rel, h_lhs, h_rhs) || !is_constant(h_rel)) continue; unsigned j = 0; for (expr const & local : locals) { j++; if (!only_found_mvars(mlocal_type(local), found_mvars)) { throw exception(sstream() << "invalid congruence rule, '" << n << "' argument #" << j << " of parameter #" << (i+1) << " contains " << "unresolved parameters"); } } if (!only_found_mvars(h_lhs, found_mvars)) { throw exception(sstream() << "invalid congruence rule, '" << n << "' argument #" << (i+1) << " is not a valid hypothesis, the left-hand-side contains " << "unresolved parameters"); } if (!is_valid_congr_hyp_rhs(h_rhs, found_mvars)) { throw exception(sstream() << "invalid congruence rule, '" << n << "' argument #" << (i+1) << " is not a valid hypothesis, the right-hand-side must be " << "of the form (m l_1 ... l_n) where m is parameter that was not " << "'assigned/resolved' yet and l_i's are locals"); } found_mvars.insert(mlocal_name(mvar)); congr_hyps.push_back(mvar); } } congr_rule rule(n, ls, to_list(metas), lhs, rhs, pr, to_list(congr_hyps)); s.insert(const_name(rel), rule); }