lean2/src/library/unfold_macros.cpp
2015-01-20 12:36:56 -08:00

154 lines
5.1 KiB
C++

/*
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "util/interrupt.h"
#include "kernel/type_checker.h"
#include "kernel/find_fn.h"
#include "kernel/expr_maps.h"
#include "kernel/instantiate.h"
#include "kernel/abstract.h"
#include "library/unfold_macros.h"
#include "library/replace_visitor.h"
#include "library/generic_exception.h"
namespace lean {
class unfold_untrusted_macros_fn {
typedef expr_bi_struct_map<expr> cache;
type_checker m_tc;
unsigned m_trust_lvl;
cache m_cache;
expr save_result(expr const & e, expr && r) {
m_cache.insert(std::make_pair(e, r));
return r;
}
expr visit_macro(expr const & e) {
buffer<expr> new_args;
for (unsigned i = 0; i < macro_num_args(e); i++)
new_args.push_back(visit(macro_arg(e, i)));
auto def = macro_def(e);
expr r = update_macro(e, new_args.size(), new_args.data());
if (def.trust_level() >= m_trust_lvl) {
if (optional<expr> new_r = m_tc.expand_macro(r)) {
return *new_r;
} else {
throw_generic_exception("failed to expand macro", e);
}
} else {
return r;
}
}
expr visit_app(expr const & e) {
expr new_f = visit(app_fn(e));
expr new_v = visit(app_arg(e));
return update_app(e, new_f, new_v);
}
expr visit_binding(expr e) {
expr_kind k = e.kind();
buffer<expr> es;
buffer<expr> ls;
while (e.kind() == k) {
expr d = visit(instantiate_rev(binding_domain(e), ls.size(), ls.data()));
expr l = mk_local(m_tc.mk_fresh_name(), binding_name(e), d, binding_info(e));
ls.push_back(l);
es.push_back(e);
e = binding_body(e);
}
e = visit(instantiate_rev(e, ls.size(), ls.data()));
expr r = abstract_locals(e, ls.size(), ls.data());
while (!ls.empty()) {
expr d = mlocal_type(ls.back());
ls.pop_back();
d = abstract_locals(d, ls.size(), ls.data());
r = update_binding(es.back(), d, r);
es.pop_back();
}
return r;
}
expr visit(expr const & e) {
switch (e.kind()) {
case expr_kind::Sort: case expr_kind::Constant:
case expr_kind::Var: case expr_kind::Meta:
case expr_kind::Local:
return e;
default:
break;
}
check_system("unfold macros");
auto it = m_cache.find(e);
if (it != m_cache.end())
return it->second;
switch (e.kind()) {
case expr_kind::Sort: case expr_kind::Constant:
case expr_kind::Var: case expr_kind::Meta:
case expr_kind::Local:
lean_unreachable();
case expr_kind::Macro:
return save_result(e, visit_macro(e));
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));
}
lean_unreachable();
}
public:
unfold_untrusted_macros_fn(environment const & env):
m_tc(env), m_trust_lvl(env.trust_lvl()) {}
expr operator()(expr const & e) { return visit(e); }
};
bool contains_untrusted_macro(unsigned trust_lvl, expr const & e) {
return static_cast<bool>(find(e, [&](expr const & e, unsigned) {
return is_macro(e) && macro_def(e).trust_level() >= trust_lvl;
}));
}
expr unfold_untrusted_macros(environment const & env, expr const & e) {
if (contains_untrusted_macro(env.trust_lvl(), e)) {
return unfold_untrusted_macros_fn(env)(e);
} else {
return e;
}
}
bool contains_untrusted_macro(unsigned trust_lvl, declaration const & d) {
if (contains_untrusted_macro(trust_lvl, d.get_type()))
return true;
return (d.is_definition() || d.is_theorem()) && contains_untrusted_macro(trust_lvl, d.get_value());
}
declaration unfold_untrusted_macros(environment const & env, declaration const & d) {
if (contains_untrusted_macro(env.trust_lvl(), d)) {
expr new_t = unfold_untrusted_macros(env, d.get_type());
if (d.is_theorem()) {
expr new_v = unfold_untrusted_macros(env, d.get_value());
return mk_theorem(d.get_name(), d.get_univ_params(), new_t, new_v, d.get_module_idx());
} else if (d.is_definition()) {
expr new_v = unfold_untrusted_macros(env, d.get_value());
return mk_definition(d.get_name(), d.get_univ_params(), new_t, new_v,
d.is_opaque(), d.get_weight(), d.get_module_idx(), d.use_conv_opt());
} else if (d.is_axiom()) {
return mk_axiom(d.get_name(), d.get_univ_params(), new_t);
} else if (d.is_constant_assumption()) {
return mk_constant_assumption(d.get_name(), d.get_univ_params(), new_t);
} else {
lean_unreachable();
}
} else {
return d;
}
}
}