/* Copyright (c) 2018 Sebastian Ullrich. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Sebastian Ullrich Lean interface to the old elaborator/elaboration parts of the parser */ #include #include #include "library/replace_visitor.h" #include "library/placeholder.h" #include "library/explicit.h" #include "library/annotation.h" #include "util/timeit.h" #include "library/locals.h" #include "library/trace.h" #include "frontends/lean/elaborator.h" #include "frontends/lean/parser.h" #include "frontends/lean/decl_cmds.h" #include "frontends/lean/definition_cmds.h" #include "frontends/lean/brackets.h" #include "frontends/lean/choice.h" #include "frontends/lean/inductive_cmds.h" #include "frontends/lean/structure_cmd.h" #include "frontends/lean/util.h" #include "frontends/lean/pp.h" #include "frontends/lean/simple_pos_info_provider.h" namespace lean { struct resolve_names_fn : public replace_visitor { parser & m_p; names m_locals; bool m_assume_local = false; resolve_names_fn(parser & p) : m_p(p) {} virtual expr visit_constant(expr const &) override { lean_unreachable(); } virtual expr visit_local(expr const & e) override { if (m_assume_local) return e; lean_unreachable(); } virtual expr visit_binding(expr const & e) override { expr new_d = visit(binding_domain(e)); flet set(m_locals, cons(binding_name(e), m_locals)); expr new_b = visit(binding_body(e)); return update_binding(e, new_d, new_b); } virtual expr visit_let(expr const & e) override { expr new_type = visit(let_type(e)); expr new_val = visit(let_value(e)); flet set(m_locals, cons(let_name(e), m_locals)); expr new_body = visit(let_body(e)); return update_let(e, new_type, new_val, new_body); } expr visit_pre_equations(expr const & e) { equations_header header; bool is_match = get_bool(mdata_data(e), "match").value_or(false); if (is_match) { parser::local_scope scope1(m_p); match_definition_scope scope2(m_p.env()); header = mk_match_header(scope2.get_name(), scope2.get_actual_name()); } else { header = mk_match_header("dummy", "dummy"); } buffer eqns; get_app_args(get_annotation_arg(e), eqns); if (eqns.empty()) { eqns.push_back(copy_pos(e, mk_no_equation())); } else { for (auto & eqn : eqns) { expr lhs = app_fn(eqn); expr rhs = app_arg(eqn); { flet _(m_assume_local, true); lhs = visit(lhs); } buffer new_locals; bool skip_main_fn = true; lhs = m_p.patexpr_to_pattern(lhs, skip_main_fn, new_locals); if (is_match) new_locals.insert(0, mk_local("_matchFn", mk_expr_placeholder())); names locals = m_locals; // NOTE: appends `new_locals` to `locals` in reverse for (auto const & l : new_locals) locals = cons(local_name_p(l), locals); flet _(m_locals, locals); rhs = visit(rhs); eqn = Fun(new_locals, mk_equation(lhs, rhs), m_p); } } return mk_equations(header, eqns.size(), eqns.data()); } virtual expr visit(expr const & e) override { if (is_placeholder(e) || is_as_is(e) || is_emptyc_or_emptys(e) || is_as_atomic(e)) { return e; } else if (is_annotation(e, "preEquations")) { return visit_pre_equations(e); } else if (is_annotation(e, "preresolved")) { expr e2 = unwrap_pos(e); auto m = mdata_data(e2); expr id = mdata_expr(e2); if (!m_assume_local) { if (auto l = m_p.resolve_local(const_name(id), m_p.pos_of(e), m_locals)) { return copy_pos(e, *l); } } buffer new_args; for (unsigned i = 0;; i++) { if (auto n = get_name(m, name(name(), i))) { if (is_internal_name(*n)) { if (m_assume_local) continue; // never resolve to section variables in patterns // section variable for (pair const & p : m_p.m_local_decls.get_entries()) if (local_name_p(p.second) == *n) return p.second; throw elaborator_exception(e, format("invalid reference to section variable '") + format(const_name(id).escape()) + format("' outside of section")); } new_args.push_back(copy_pos(e, mk_const(*n, const_levels(id)))); } else { break; } } if (new_args.empty()) { if (m_assume_local) return mk_local(const_name(id), mk_expr_placeholder()); auto const & pip = *get_pos_info_provider(); report_message(message(pip.get_file_name(), pip.get_pos_info_or_some(e), message_severity::ERROR, "unknown identifier '" + const_name(id).escape() + "'")); return m_p.mk_sorry(pip.get_pos_info_or_some(e)); } return mk_choice(new_args.size(), new_args.data()); } else { return replace_visitor::visit(e); } } }; expr resolve_names(parser & p, expr const & e) { return resolve_names_fn(p)(e); } decl_attributes to_decl_attributes(environment const & env, expr const & e, bool local) { decl_attributes attributes(!local); buffer attrs; get_app_args(e, attrs); for (auto const & e : attrs) { buffer args; auto attr = get_app_args(e, args); auto n = const_name(attr); // attributes.set_attribute(env, n, get_attribute(env, n).parse_data(e)); } return attributes; } environment elab_attribute_cmd(environment env, expr const & cmd) { auto const & data = mdata_data(cmd); auto const & e = mdata_expr(cmd); bool local = *get_bool(data, "local"); auto attributes = to_decl_attributes(env, app_fn(e), local); buffer eids; get_app_args(app_arg(e), eids); for (auto const & e : eids) env = attributes.apply_all(env, get_dummy_ios(), const_name(e)); return env; } cmd_meta to_cmd_meta(environment const & env, expr const & e) { auto const & data = mdata_data(e); cmd_meta m(to_decl_attributes(env, mdata_expr(e), false)); m.m_modifiers.m_is_unsafe = get_bool(data, "unsafe").value_or(false); m.m_modifiers.m_is_mutual = get_bool(data, "mutual").value_or(false); m.m_modifiers.m_is_noncomputable = get_bool(data, "noncomputable").value_or(false); m.m_modifiers.m_is_private = get_bool(data, "private").value_or(false); m.m_modifiers.m_is_protected = get_bool(data, "protected").value_or(false); if (auto s = get_string(data, "docString")) m.m_doc_string = s->to_std_string(); return m; } void elab_check_cmd(parser & p, expr const & cmd) { // TODO(Sebastian) // transient_cmd_scope cmd_scope(p); expr e = mdata_expr(cmd); bool check_unassigend = false; names ls; metavar_context mctx; e = resolve_names(p, e); std::tie(e, ls) = p.elaborate("_check", mctx, e, check_unassigend); names new_ls = to_names(collect_univ_params(e)); type_context_old tc(p.env()); expr type = tc.infer(e); if (is_synthetic_sorry(e) && (is_synthetic_sorry(type) || is_metavar(type))) { // do not show useless type-checking results such as ?? : ?M_1 return; } auto out = p.mk_message(p.cmd_pos(), p.pos(), INFORMATION); formatter fmt = out.get_formatter(); unsigned indent = get_pp_indent(p.get_options()); format e_fmt = fmt(e); format type_fmt = fmt(type); format r = group(e_fmt + space() + format(":") + nest(indent, line() + type_fmt)); out.set_caption("check result") << r; out.report(); } expr const & get_arg_names(expr const & e, buffer & ns) { buffer args; auto const & fn = get_app_args(e, args); for (auto const & e : args) ns.push_back(const_name(e)); return fn; } void elab_axiom_cmd(parser & p, expr const & cmd) { buffer args; buffer ls; get_app_args(mdata_expr(cmd), args); auto fn = get_arg_names(args[1], ls); expr type = args[2]; type = resolve_names(p, type); p.set_env(elab_var(p, variable_kind::Axiom, to_cmd_meta(p.env(), args[0]), get_pos_info_provider()->get_pos_info_or_some(cmd), optional(), const_name(fn), type, ls)); } static expr unpack_mutual_definition(parser & p, expr const & cmd, buffer & lp_names, buffer & fns, buffer & prv_names, buffer & params) { parser::local_scope scope1(p); auto header_pos = p.pos(); buffer args, eqns; get_app_args(cmd, args); get_arg_names(args[2], lp_names); get_app_args(args[3], fns); get_app_args(args[4], params); for (auto & param : params) { param = update_local_p(param, resolve_names(p, local_type_p(param))); p.add_local(param); } auto val = args[5]; buffer full_names; buffer full_actual_names; for (unsigned i = 0; i < fns.size(); i++) { expr & fn = fns[i]; expr fn_type = local_type_p(fn); fn_type = resolve_names(p, fn_type); name n = local_name_p(fn); declaration_name_scope scope2(n); if (n.is_anonymous()) n = synthesize_instance_name(p, fn_type, scope2, p.pos_of(fn)); declaration_name_scope scope3("_main"); full_names.push_back(scope3.get_name()); full_actual_names.push_back(scope3.get_actual_name()); prv_names.push_back(scope2.get_actual_name()); fn = mk_local(n, n, fn_type, mk_rec_info()); p.add_local(fn); if (!is_annotation(val, "preEquations")) { val = resolve_names(p, val); } } optional wf_tacs; if (args.size() > 6) wf_tacs = args[6]; if (is_annotation(val, "preEquations")) { // TODO(Sebastian): this uses the wrong declaration name scope val = resolve_names(p, val); to_equations(val, eqns); val = mk_equations(p, fns, full_names, full_actual_names, eqns, wf_tacs, header_pos); } return val; } void elab_defs_cmd(parser & p, expr const & cmd) { buffer args; // std::cout << "elab_defs_cmd\n" << cmd << "\n--------------\n"; get_app_args(mdata_expr(cmd), args); auto meta = to_cmd_meta(p.env(), args[0]); auto kind = static_cast(lit_value(args[1]).get_nat().get_small_value()); declaration_info_scope scope(p, kind, meta); buffer lp_names; buffer fns, params; /* TODO(Leo): allow a different doc string for each function in a mutual definition. */ optional doc_string = meta.m_doc_string; environment env = p.env(); private_name_scope prv_scope(meta.m_modifiers.m_is_private, env); buffer prv_names; expr val = unpack_mutual_definition(p, mdata_expr(cmd), lp_names, fns, prv_names, params); auto header_pos = get_pos_info_provider()->get_pos_info_or_some(cmd); p.set_env(elab_defs(p, kind, meta, lp_names, fns, prv_names, params, val, header_pos)); } static environment elab_inductives_cmd(parser & p, expr const & cmd) { parser::local_scope _(p); // auto header_pos = get_pos_info_provider()->get_pos_info_or_some(cmd); buffer args, attrs, pre_inds, params, all_intro_rules, infer_kinds; buffer lp_names; get_app_args(mdata_expr(cmd), args); auto meta = to_cmd_meta(p.env(), args[0]); get_app_args(args[1], attrs); get_arg_names(args[2], lp_names); get_app_args(args[3], pre_inds); get_app_args(args[4], params); get_app_args(args[5], all_intro_rules); get_app_args(args[6], infer_kinds); for (expr & i : pre_inds) p.add_local(i); for (expr & param : params) { param = update_local_p(param, resolve_names(p, local_type_p(param))); p.add_local(param); } buffer mut_attrs; name_map implicit_infer_map; buffer inds; buffer> intro_rules; for (unsigned i = 0; i < pre_inds.size(); i++) { expr const & pre_ind = pre_inds[i]; auto ind_type = local_type_p(pre_ind); ind_type = resolve_names(p, ind_type); // check_attrs(attrs); mut_attrs.push_back(to_decl_attributes(p.env(), attrs[i], false)); buffer intro_rules_i, infer_kinds_i; get_app_args(all_intro_rules[i], intro_rules_i); get_app_args(infer_kinds[i], infer_kinds_i); for (unsigned j = 0; j < intro_rules_i.size(); j++) { auto & ir = intro_rules_i[j]; name ir_name = get_namespace(p.env()) + local_name_p(pre_ind) + local_name_p(ir); auto kind = static_cast(lit_value(infer_kinds_i[j]).get_nat().get_small_value()); implicit_infer_map.insert(ir_name, kind); expr ir_type = local_type_p(ir); ir_type = resolve_names(p, ir_type); ir = mk_local(ir_name, ir_type); } expr ind = mk_local(get_namespace(p.env()) + local_name_p(pre_ind), ind_type); inds.push_back(ind); intro_rules.push_back(intro_rules_i); // HACK: without this line, `ind` would be wrapped in an `as_is` annotation by `collect_implicit_locals` params.push_back(ind); } for (buffer & irs : intro_rules) { for (expr & ir : irs) { ir = replace_locals(ir, pre_inds, inds); } } elaborate_inductive_decls(p, meta, mut_attrs, lp_names, implicit_infer_map, params, inds, intro_rules); return p.env(); } static void elab_variables_cmd(parser & p, expr const & cmd) { buffer vars; get_app_args(mdata_expr(cmd), vars); for (auto const & var : vars) { // Hack: to make sure we get different universe parameters for each parameter. // Alternative: elaborate once and copy types replacing universes in new_ls. auto id = local_name_p(var); auto type = local_type_p(var); type = resolve_names(p, type); buffer ls; p.set_env(elab_var(p, variable_kind::Variable, cmd_meta(), p.pos_of(cmd), some(local_info_p(var)), id, type, ls)); } } static void elaborate_command(parser & p, expr const & cmd) { auto const & data = mdata_data(cmd); if (auto const & cmd_name = get_name(data, "command")) { if (*cmd_name == "attribute") { p.set_env(elab_attribute_cmd(p.env(), cmd)); return; } else if (*cmd_name == "#check") { elab_check_cmd(p, cmd); return; } else if (*cmd_name == "axiom") { elab_axiom_cmd(p, cmd); return; } else if (*cmd_name == "defs") { elab_defs_cmd(p, cmd); return; } else if (*cmd_name == "inductives") { p.set_env(elab_inductives_cmd(p, cmd)); return; } else if (*cmd_name == "structure") { elab_structure_cmd(p, cmd); return; } else if (*cmd_name == "initQuot") { p.set_env(p.env().add(mk_quot_decl())); return; } else if (*cmd_name == "variables") { elab_variables_cmd(p, cmd); return; } } throw elaborator_exception(cmd, "unexpected input to 'elaborate_command'"); } options to_options(b_obj_arg o) { return options(o, true); } name_set to_name_set(b_obj_arg o) { name_set ns; names l(o, true); for (auto const & n : l) { ns.insert(n); } return ns; } name_generator to_name_generator(b_obj_arg o) { name_generator ngen; ngen.m_prefix = name(cnstr_get(o, 0), true); ngen.m_next_idx = cnstr_get_scalar(o, sizeof(void*)); return ngen; } object_ref to_obj(name_generator const & ngen) { auto o = mk_cnstr(0, ngen.m_prefix, sizeof(uint32 )); cnstr_set_scalar(o.raw(), sizeof(void*), ngen.m_next_idx); return o; } /* elaborate_command (filename : string) : expr → old_elaborator_state → option old_elaborator_state × message_log */ // TODO(Sebastian): replace `string` with `message` in the new runtime extern "C" obj_res lean_elaborator_elaborate_command(b_obj_arg vm_filename, obj_arg vm_cmd, b_obj_arg vm_st) { auto vm_e = cnstr_get(vm_st, 0); environment env(vm_e, true); auto filename = string_to_std(vm_filename); std::stringstream in; auto ngen = to_name_generator(cnstr_get(vm_st, 1)); list_ref vm_lds(cnstr_get(vm_st, 2), true); local_level_decls lds; name_set lvars; for (auto const & vm_ld : vm_lds) { auto n = name(cnstr_get(vm_ld.raw(), 0), true); lds.insert(n, level(cnstr_get(vm_ld.raw(), 1), true)); // all local decls are variables in Lean 4 lvars.insert(n); } list_ref vm_eds(cnstr_get(vm_st, 3), true); local_expr_decls eds; name_set vars; for (auto const & vm_ed : vm_eds) { auto n = name(cnstr_get(vm_ed.raw(), 0), true); auto data = cnstr_get(vm_ed.raw(), 1); eds.insert(n, mk_local(name(cnstr_get(data, 0), true), n, expr(cnstr_get(data, 1), true), cnstr_get_scalar(data, sizeof(void*) * 2))); // all local decls are variables in Lean 4 vars.insert(n); } auto includes = to_name_set(cnstr_get(vm_st, 4)); auto options = to_options(cnstr_get(vm_st, 5)); auto cmd = expr(vm_cmd); auto pos = get_pos(cmd).value_or(pos_info {1, 0}); message_log log; obj_arg vm_out = mk_option_none(); { scope_message_log scope3(log); io_state ios(options, mk_pretty_formatter_factory()); ios.set_regular_channel(ios.get_diagnostic_channel_ptr()); scope_global_ios scope_ios(ios); type_context_old tc(env, options); scope_trace_env scope(env, options, tc); scope_traces_as_messages scope2(filename, pos); parser p(env, ios, in, filename); auto s = p.mk_snapshot(); p.reset(snapshot(p.env(), ngen, lds, eds, lvars, vars, includes, options, true, false, parser_scope_stack(), nat(cnstr_get(vm_st, 6), true).get_small_value(), pos_info{1, 0})); p.m_last_cmd_pos = pos; auto ns = name(cnstr_get(vm_st, 7), true); p.set_env(set_namespace(env, ns)); try { elaborate_command(p, cmd); s = p.mk_snapshot(); // sort levels by reverse insertion order. ugh. rb_map new_lds; s->m_lds.for_each([&](name const & n, level const &) { new_lds.insert(s->m_lds.find_idx(n), n); }); vm_lds = list_ref(); new_lds.for_each([&](unsigned const &, name const & n) { auto vm_ld = mk_cnstr(0, n, mk_cnstr(4, n)); vm_lds = cons(vm_ld, vm_lds); }); vm_eds = list_ref(); for (auto const & ed : s->m_eds.get_entries()) { if (!is_local_p(ed.second)) { // obsolete local ref, ignore continue; } auto vm_sec_var = mk_cnstr(0, local_name_p(ed.second), local_type_p(ed.second), sizeof(binder_info)); cnstr_set_scalar(vm_sec_var.raw(), sizeof(void*) * 2, local_info_p(ed.second)); auto vm_ed = mk_cnstr(0, ed.first, vm_sec_var); vm_eds = cons(vm_ed, vm_eds); } object * args[] = { p.env().to_obj_arg(), to_obj(s->m_ngen).steal(), vm_lds.steal(), vm_eds.steal(), cnstr_get(vm_st, 4), cnstr_get(vm_st, 5), cnstr_get(vm_st, 6), cnstr_get(vm_st, 7) }; inc(cnstr_get(vm_st, 4)); inc(cnstr_get(vm_st, 5)); inc(cnstr_get(vm_st, 6)); inc(cnstr_get(vm_st, 7)); auto vm_st2 = mk_cnstr(0, 8, args); vm_out = mk_option_some(vm_st2.steal()); } catch (exception & e) { message_builder builder(env, ios, filename, pos, message_severity::ERROR); builder.set_exception(e); builder.report(); } } return mk_cnstr(0, object_ref(vm_out), log.raw()).steal(); } extern "C" obj_res lean_expr_local(obj_arg vm_name, obj_arg vm_pp_name, obj_arg vm_type, uint8 vm_binder_info) { return mk_local(name(vm_name), name(vm_pp_name), expr(vm_type), static_cast(vm_binder_info)).steal(); } }