/* 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 #include #include #include "runtime/sstream.h" #include "runtime/thread.h" #include "kernel/environment.h" #include "kernel/kernel_exception.h" #include "kernel/type_checker.h" #include "kernel/quot.h" namespace lean { environment_header::environment_header(unsigned trust_lvl, std::unique_ptr ext): m_trust_lvl(trust_lvl), m_norm_ext(std::move(ext)) {} environment_extension::~environment_extension() {} environment::environment(unsigned trust_lvl): environment(trust_lvl, mk_id_normalizer_extension()) {} environment::environment(unsigned trust_lvl, std::unique_ptr ext): m_header(std::make_shared(trust_lvl, std::move(ext))), m_extensions(std::make_shared()) {} environment::~environment() {} optional environment::find(name const & n) const { constant_info const * r = m_constants.find(n); return r ? some_constant_info(*r) : none_constant_info(); } constant_info environment::get(name const & n) const { constant_info const * r = m_constants.find(n); if (!r) throw unknown_constant_exception(*this, n); return *r; } bool environment::is_recursor(name const & n) const { return m_header->is_recursor(*this, n) || (m_quot_initialized && quot_is_rec(n)); } bool environment::is_builtin(name const & n) const { return m_header->is_builtin(*this, n) || (m_quot_initialized && quot_is_decl(n)); } 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, bool non_meta_only) { bool memoize = true; type_checker checker(env, memoize, non_meta_only); check_constant_val(env, v, checker); } environment environment::add_axiom(declaration const & d, bool check) const { axiom_val const & v = d.to_axiom_val(); if (check) check_constant_val(*this, v.to_constant_val(), !d.is_meta()); return add(constant_info(d)); } environment environment::add_definition(declaration const & d, bool check) const { definition_val const & v = d.to_definition_val(); if (v.is_meta()) { /* Meta definition can be recursive. So, we check the header, add, and then type check the body. */ if (check) { bool memoize = true; bool non_meta_only = false; type_checker checker(*this, memoize, non_meta_only); check_constant_val(*this, v.to_constant_val(), checker); } environment new_env = add(constant_info(d)); if (check) { bool memoize = true; bool non_meta_only = false; type_checker checker(new_env, memoize, non_meta_only); 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 new_env; } else { if (check) { bool memoize = true; type_checker checker(*this, memoize); 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 add(constant_info(d)); } } environment environment::add_theorem(declaration const & d, bool check) const { theorem_val const & v = d.to_theorem_val(); if (check) { // TODO(Leo): we must add support for handling tasks here bool memoize = true; type_checker checker(*this, memoize); 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 add(constant_info(d)); } environment environment::add_mutual(declaration const & d, bool check) const { definition_vals const & vs = d.to_definition_vals(); /* Check declarations header */ if (check) { bool memoize = true; bool non_meta_only = false; type_checker checker(*this, memoize, non_meta_only); for (definition_val const & v : vs) { if (!v.is_meta()) throw kernel_exception(*this, "invalid mutual definition, declaration is not tagged as meta"); 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) { bool memoize = true; bool non_meta_only = false; type_checker checker(new_env, memoize, non_meta_only); for (definition_val const & v : vs) { 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 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::MutualDefinition: return add_mutual(d, check); case declaration_kind::Quot: return add_quot(); case declaration_kind::Inductive: return add_inductive(d); } lean_unreachable(); } class extension_manager { std::vector> m_exts; mutex m_mutex; public: unsigned register_extension(std::shared_ptr const & ext) { lock_guard lock(m_mutex); unsigned r = m_exts.size(); m_exts.push_back(ext); return r; } bool has_ext(unsigned extid) const { return extid < m_exts.size(); } environment_extension const & get_initial(unsigned extid) { lock_guard lock(m_mutex); return *(m_exts[extid].get()); } }; static extension_manager * g_extension_manager = nullptr; static extension_manager & get_extension_manager() { return *g_extension_manager; } void initialize_environment() { g_extension_manager = new extension_manager(); } void finalize_environment() { delete g_extension_manager; } unsigned environment::register_extension(std::shared_ptr const & initial) { return get_extension_manager().register_extension(initial); } [[ noreturn ]] void throw_invalid_extension(environment const & env) { throw kernel_exception(env, "invalid environment extension identifier"); } environment_extension const & environment::get_extension(unsigned id) const { if (!get_extension_manager().has_ext(id)) throw_invalid_extension(*this); if (id >= m_extensions->size() || !(*m_extensions)[id]) return get_extension_manager().get_initial(id); return *((*m_extensions)[id].get()); } environment environment::update(unsigned id, std::shared_ptr const & ext) const { if (!get_extension_manager().has_ext(id)) throw_invalid_extension(*this); auto new_exts = std::make_shared(*m_extensions); if (id >= new_exts->size()) new_exts->resize(id+1); (*new_exts)[id] = ext; return environment(*this, new_exts); } void environment::for_each_constant(std::function const & f) const { m_constants.for_each([&](name const &, constant_info const & c) { return f(c); }); } }