lean4-htt/src/kernel/environment.cpp
Sebastian Ullrich 3770808b58
feat: split Lean.Kernel.Environment from Lean.Environment (#5145)
This PR splits the environment used by the kernel from that used by the
elaborator, providing the foundation for tracking of asynchronously
elaborated declarations, which will exist as a concept only in the
latter.

Minor changes:
* kernel diagnostics are moved from an environment extension to a direct
environment as they are the only extension used directly by the kernel
* `initQuot` is moved from an environment header field to a direct
environment as it is the only header field used by the kernel; this also
makes the remaining header immutable after import
2025-01-18 18:42:57 +00:00

302 lines
11 KiB
C++

/*
Copyright (c) 2013-2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <utility>
#include <vector>
#include <limits>
#include "runtime/sstream.h"
#include "runtime/thread.h"
#include "runtime/sharecommon.h"
#include "util/map_foreach.h"
#include "util/io.h"
#include "kernel/environment.h"
#include "kernel/kernel_exception.h"
#include "kernel/type_checker.h"
#include "kernel/quot.h"
namespace lean {
extern "C" object* lean_environment_add(object*, object*);
extern "C" object* lean_environment_find(object*, object*);
extern "C" object* lean_environment_mark_quot_init(object*);
extern "C" uint8 lean_environment_quot_init(object*);
extern "C" object* lean_kernel_record_unfold (object*, object*);
extern "C" object* lean_kernel_get_diag(object*);
extern "C" object* lean_kernel_set_diag(object*, object*);
extern "C" uint8* lean_kernel_diag_is_enabled(object*);
void diagnostics::record_unfold(name const & decl_name) {
m_obj = lean_kernel_record_unfold(to_obj_arg(), decl_name.to_obj_arg());
}
scoped_diagnostics::scoped_diagnostics(environment const & env, bool collect) {
if (collect) {
diagnostics d(env.get_diag());
if (lean_kernel_diag_is_enabled(d.to_obj_arg())) {
m_diag = new diagnostics(d);
} else
m_diag = nullptr;
} else {
m_diag = nullptr;
}
}
scoped_diagnostics::~scoped_diagnostics() {
if (m_diag)
delete m_diag;
}
environment scoped_diagnostics::update(environment const & env) const {
if (m_diag)
return env.set_diag(*m_diag);
else
return env;
}
diagnostics environment::get_diag() const {
return diagnostics(lean_kernel_get_diag(to_obj_arg()));
}
environment environment::set_diag(diagnostics const & diag) const {
return environment(lean_kernel_set_diag(to_obj_arg(), diag.to_obj_arg()));
}
bool environment::is_quot_initialized() const {
return lean_environment_quot_init(to_obj_arg()) != 0;
}
void environment::mark_quot_initialized() {
m_obj = lean_environment_mark_quot_init(m_obj);
}
optional<constant_info> environment::find(name const & n) const {
return to_optional<constant_info>(lean_environment_find(to_obj_arg(), n.to_obj_arg()));
}
constant_info environment::get(name const & n) const {
object * o = lean_environment_find(to_obj_arg(), n.to_obj_arg());
if (is_scalar(o))
throw unknown_constant_exception(*this, n);
constant_info r(cnstr_get(o, 0), true);
dec(o);
return r;
}
static void check_no_metavar(environment const & env, name const & n, expr const & e) {
if (has_metavar(e))
throw declaration_has_metavars_exception(env, n, e);
}
static void check_no_fvar(environment const & env, name const & n, expr const & e) {
if (has_fvar(e))
throw declaration_has_free_vars_exception(env, n, e);
}
void check_no_metavar_no_fvar(environment const & env, name const & n, expr const & e) {
check_no_metavar(env, n, e);
check_no_fvar(env, n, e);
}
static void check_name(environment const & env, name const & n) {
if (env.find(n))
throw already_declared_exception(env, n);
}
void environment::check_name(name const & n) const {
::lean::check_name(*this, n);
}
static void check_duplicated_univ_params(environment const & env, names ls) {
while (!is_nil(ls)) {
auto const & p = head(ls);
ls = tail(ls);
if (std::find(ls.begin(), ls.end(), p) != ls.end()) {
throw kernel_exception(env, sstream() << "failed to add declaration to environment, "
<< "duplicate universe level parameter: '"
<< p << "'");
}
}
}
void environment::check_duplicated_univ_params(names ls) const {
::lean::check_duplicated_univ_params(*this, ls);
}
static void check_constant_val(environment const & env, constant_val const & v, type_checker & checker) {
check_name(env, v.get_name());
check_duplicated_univ_params(env, v.get_lparams());
check_no_metavar_no_fvar(env, v.get_name(), v.get_type());
expr sort = checker.check(v.get_type(), v.get_lparams());
checker.ensure_sort(sort, v.get_type());
}
static void check_constant_val(environment const & env, constant_val const & v, diagnostics * diag, definition_safety ds) {
type_checker checker(env, diag, ds);
check_constant_val(env, v, checker);
}
static void check_constant_val(environment const & env, constant_val const & v, diagnostics * diag, bool safe_only) {
check_constant_val(env, v, diag, safe_only ? definition_safety::safe : definition_safety::unsafe);
}
void environment::add_core(constant_info const & info) {
m_obj = lean_environment_add(m_obj, info.to_obj_arg());
}
environment environment::add(constant_info const & info) const {
return environment(lean_environment_add(to_obj_arg(), info.to_obj_arg()));
}
environment environment::add_axiom(declaration const & d, bool check) const {
scoped_diagnostics diag(*this, check);
axiom_val const & v = d.to_axiom_val();
if (check)
check_constant_val(*this, v.to_constant_val(), diag.get(), !d.is_unsafe());
return diag.update(add(constant_info(d)));
}
environment environment::add_definition(declaration const & d, bool check) const {
scoped_diagnostics diag(*this, check);
definition_val const & v = d.to_definition_val();
if (v.is_unsafe()) {
/* Meta definition can be recursive.
So, we check the header, add, and then type check the body. */
if (check) {
type_checker checker(*this, diag.get(), definition_safety::unsafe);
check_constant_val(*this, v.to_constant_val(), checker);
}
environment new_env = add(constant_info(d));
if (check) {
type_checker checker(new_env, diag.get(), definition_safety::unsafe);
check_no_metavar_no_fvar(new_env, v.get_name(), v.get_value());
expr val_type = checker.check(v.get_value(), v.get_lparams());
if (!checker.is_def_eq(val_type, v.get_type()))
throw definition_type_mismatch_exception(new_env, d, val_type);
}
return diag.update(new_env);
} else {
if (check) {
type_checker checker(*this, diag.get());
check_constant_val(*this, v.to_constant_val(), checker);
check_no_metavar_no_fvar(*this, v.get_name(), v.get_value());
expr val_type = checker.check(v.get_value(), v.get_lparams());
if (!checker.is_def_eq(val_type, v.get_type()))
throw definition_type_mismatch_exception(*this, d, val_type);
}
return diag.update(add(constant_info(d)));
}
}
environment environment::add_theorem(declaration const & d, bool check) const {
scoped_diagnostics diag(*this, check);
theorem_val const & v = d.to_theorem_val();
if (check) {
type_checker checker(*this, diag.get());
sharecommon_persistent_fn share;
expr val(share(v.get_value().raw()));
expr type(share(v.get_type().raw()));
if (!checker.is_prop(type))
throw theorem_type_is_not_prop(*this, v.get_name(), type);
check_constant_val(*this, v.to_constant_val(), checker);
check_no_metavar_no_fvar(*this, v.get_name(), val);
expr val_type = checker.check(val, v.get_lparams());
if (!checker.is_def_eq(val_type, type))
throw definition_type_mismatch_exception(*this, d, val_type);
}
return diag.update(add(constant_info(d)));
}
environment environment::add_opaque(declaration const & d, bool check) const {
scoped_diagnostics diag(*this, check);
opaque_val const & v = d.to_opaque_val();
if (check) {
type_checker checker(*this, diag.get());
check_constant_val(*this, v.to_constant_val(), checker);
expr val_type = checker.check(v.get_value(), v.get_lparams());
if (!checker.is_def_eq(val_type, v.get_type()))
throw definition_type_mismatch_exception(*this, d, val_type);
}
return diag.update(add(constant_info(d)));
}
environment environment::add_mutual(declaration const & d, bool check) const {
scoped_diagnostics diag(*this, check);
definition_vals const & vs = d.to_definition_vals();
if (empty(vs))
throw kernel_exception(*this, "invalid empty mutual definition");
definition_safety safety = head(vs).get_safety();
if (safety == definition_safety::safe)
throw kernel_exception(*this, "invalid mutual definition, declaration is not tagged as unsafe/partial");
/* Check declarations header */
if (check) {
type_checker checker(*this, diag.get(), safety);
for (definition_val const & v : vs) {
if (v.get_safety() != safety)
throw kernel_exception(*this, "invalid mutual definition, declarations must have the same safety annotation");
check_constant_val(*this, v.to_constant_val(), checker);
}
}
/* Add declarations */
environment new_env = *this;
for (definition_val const & v : vs) {
new_env.add_core(constant_info(v));
}
/* Check actual definitions */
if (check) {
type_checker checker(new_env, diag.get(), safety);
for (definition_val const & v : vs) {
check_no_metavar_no_fvar(new_env, v.get_name(), v.get_value());
expr val_type = checker.check(v.get_value(), v.get_lparams());
if (!checker.is_def_eq(val_type, v.get_type()))
throw definition_type_mismatch_exception(new_env, d, val_type);
}
}
return diag.update(new_env);
}
environment environment::add(declaration const & d, bool check) const {
switch (d.kind()) {
case declaration_kind::Axiom: return add_axiom(d, check);
case declaration_kind::Definition: return add_definition(d, check);
case declaration_kind::Theorem: return add_theorem(d, check);
case declaration_kind::Opaque: return add_opaque(d, check);
case declaration_kind::MutualDefinition: return add_mutual(d, check);
case declaration_kind::Quot: return add_quot();
case declaration_kind::Inductive: return add_inductive(d);
}
lean_unreachable();
}
/*
addDeclCore (env : Environment) (maxHeartbeats : USize) (decl : @& Declaration)
(cancelTk? : @& Option IO.CancelToken) : Except Kernel.Exception Environment
*/
extern "C" LEAN_EXPORT object * lean_add_decl(object * env, size_t max_heartbeat, object * decl,
object * opt_cancel_tk) {
scope_max_heartbeat s(max_heartbeat);
scope_cancel_tk s2(is_scalar(opt_cancel_tk) ? nullptr : cnstr_get(opt_cancel_tk, 0));
return catch_kernel_exceptions<environment>([&]() {
return environment(env).add(declaration(decl, true));
});
}
extern "C" LEAN_EXPORT object * lean_add_decl_without_checking(object * env, object * decl) {
return catch_kernel_exceptions<environment>([&]() {
return environment(env).add(declaration(decl, true), false);
});
}
void environment::for_each_constant(std::function<void(constant_info const & d)> const & f) const {
smap_foreach(cnstr_get(raw(), 1), [&](object *, object * v) {
constant_info cinfo(v, true);
f(cinfo);
});
}
void initialize_environment() {
}
void finalize_environment() {
}
}