lean4-htt/src/frontends/lean/vm_elaborator.cpp
Leonardo de Moura 0b7d987699 feat(frontends/lean, library/init/lean): opaque constants
@kha I have added support for opaque constants to the old C++ frontend,
and made sure the new frontend can still parse `library/init/core.lean`.
The kernel should enforce that opaque constants are really opaque, and
the following example should fail
```
constant x : nat := 0
theorem foo : x = 0 := rfl
```
If it doesn't, it is a bug.

Here are some remaining issues:
1- `environment.mk_empty` is currently an axiom because we cannot create
an inhabitant of an opaque type. A possible solution is to use
`option environment` instead of `environment`.

2- There is no support for opaque constants in the new
frontend. However, I modified it to handle axioms, and fixed the literal
values with decl_cmd_kind. I tried to mark some of my changes with
comments, but it is probably much easier for you to just check the
commit change list.

3- I did not add any support for automatically constructing `e`
at `constant x : t := e`. I think we can do this later
after we replace the old frontend with the new one. BTW, it took only a
few minutes to provide the inhabitants manually.
2019-03-15 17:41:44 -07:00

616 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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 <string>
#include <iostream>
#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 "library/vm/vm.h"
#include "library/vm/vm_string.h"
#include "library/vm/vm_option.h"
#include "library/vm/vm_nat.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<names> 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<names> 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<expr> 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<bool> _(m_assume_local, true);
lhs = visit(lhs);
}
buffer<expr> 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("_match_fn", 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<names> _(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, "pre_equations")) {
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<expr> 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<name, expr> 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<expr> attrs;
get_app_args(e, attrs);
for (auto const & e : attrs) {
buffer<expr> 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<expr> eids; get_app_args(app_arg(e), eids);
for (auto const & e : eids)
env = attributes.apply(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, "doc_string"))
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() + colon() + nest(indent, line() + type_fmt));
out.set_caption("check result") << r;
out.report();
}
expr const & get_arg_names(expr const & e, buffer<name> & ns) {
buffer<expr> 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<expr> args;
buffer<name> 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<binder_info>(), const_name(fn), type, ls));
}
static expr unpack_mutual_definition(parser & p, expr const & cmd, buffer<name> & lp_names, buffer<expr> & fns, buffer<name> & prv_names,
buffer<expr> & params) {
parser::local_scope scope1(p);
auto header_pos = p.pos();
buffer<expr> 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<name> full_names;
buffer<name> 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, "pre_equations")) {
val = resolve_names(p, val);
}
}
optional<expr> wf_tacs;
if (args.size() > 6)
wf_tacs = args[6];
if (is_annotation(val, "pre_equations")) {
// 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<expr> args;
get_app_args(mdata_expr(cmd), args);
auto meta = to_cmd_meta(p.env(), args[0]);
auto kind = static_cast<decl_cmd_kind>(lit_value(args[1]).get_nat().get_small_value());
declaration_info_scope scope(p, kind, meta);
buffer<name> lp_names;
buffer<expr> fns, params;
/* TODO(Leo): allow a different doc string for each function in a mutual definition. */
optional<std::string> doc_string = meta.m_doc_string;
environment env = p.env();
private_name_scope prv_scope(meta.m_modifiers.m_is_private, env);
buffer<name> 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<expr> args, attrs, pre_inds, params, all_intro_rules, infer_kinds;
buffer<name> 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<decl_attributes> mut_attrs;
name_map<implicit_infer_kind> implicit_infer_map;
buffer<expr> inds;
buffer<buffer<expr>> 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<expr> 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<implicit_infer_kind>(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<expr> & 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<expr> 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<name> 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 == "init_quot") {
p.set_env(module::add(p.env(), mk_quot_decl()));
return;
} else if (*cmd_name == "variables") {
elab_variables_cmd(p, cmd);
return;
}
}
throw elaborator_exception(cmd, "unexpected input to 'elaborate_command'");
}
/* TEMPORARY code for the old runtime */
void env_finalizer(void * env) {
delete static_cast<environment*>(env);
}
void env_foreach(void * /* env */, b_obj_arg /* fn */) {
// TODO(leo, kha)
}
static external_object_class * g_env_class = nullptr;
environment const & to_env(b_obj_arg o) {
lean_assert(external_class(o) == g_env_class);
return *static_cast<environment *>(external_data(o));
}
obj_res to_lean_environment(environment const & env) {
return alloc_external(g_env_class, new environment(env));
}
options to_options(b_obj_arg o) {
options opts;
kvmap m = kvmap(o, true);
for (auto const & kv : m) {
switch (kv.snd().kind()) {
case data_value_kind::Bool:
opts = opts.update(kv.fst(), kv.snd().get_bool());
break;
case data_value_kind::Name:
opts = opts.update(kv.fst(), kv.snd().get_name());
break;
case data_value_kind::Nat:
opts = opts.update(kv.fst(), kv.snd().get_nat().get_small_value());
break;
case data_value_kind::String:
opts = opts.update(kv.fst(), kv.snd().get_string());
break;
}
}
return opts;
}
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<uint32>(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;
}
extern "C" obj_res lean_environment_mk_empty(b_obj_arg) {
return to_lean_environment(environment());
}
extern "C" uint8 lean_environment_contains(b_obj_arg lean_environment, b_obj_arg vm_n) {
return static_cast<uint8>(static_cast<bool>(to_env(lean_environment).find(name(vm_n, true))));
}
/* 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);
auto env = to_env(vm_e);
auto filename = string_to_std(vm_filename);
std::stringstream in;
auto ngen = to_name_generator(cnstr_get(vm_st, 1));
list_ref<object_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<object_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<binder_info>(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}));
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<unsigned, name, unsigned_rev_cmp> 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<object_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<object_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[] = {
to_lean_environment(p.env()),
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<binder_info>(vm_binder_info)).steal();
}
static vm_obj vm_lean_expr_local(vm_obj const &, vm_obj const &, vm_obj const &, vm_obj const &) {
throw exception("elaborator support has not been implemented in the old VM");
}
static vm_obj vm_lean_environment_mk_empty(vm_obj const &) {
throw exception("elaborator support has not been implemented in the old VM");
}
static vm_obj vm_lean_environment_contains(vm_obj const &, vm_obj const &) {
throw exception("elaborator support has not been implemented in the old VM");
}
static vm_obj vm_lean_elaborator_elaborate_command(vm_obj const &, vm_obj const &, vm_obj const &) {
throw exception("elaborator support has not been implemented in the old VM");
}
void initialize_vm_elaborator() {
g_env_class = register_external_object_class(env_finalizer, env_foreach);
DECLARE_VM_BUILTIN(name({"lean", "expr", "local"}), vm_lean_expr_local);
DECLARE_VM_BUILTIN(name({"lean", "environment", "mk_empty"}), vm_lean_environment_mk_empty);
DECLARE_VM_BUILTIN(name({"lean", "environment", "contains"}), vm_lean_environment_contains);
DECLARE_VM_BUILTIN(name({"lean", "elaborator", "elaborate_command"}), vm_lean_elaborator_elaborate_command);
}
void finalize_vm_elaborator() {
}
}