blob: 18d082f8ea4392b49da742afa2f0a3ff5cd4297f [file] [edit]
/*
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "util/name_generator.h"
#include "kernel/environment.h"
#include "kernel/type_checker.h"
#include "kernel/instantiate.h"
#include "kernel/kernel_exception.h"
namespace lean {
static name * g_ind_fresh = nullptr;
/**\ brief Return recursor name for the given inductive datatype name */
name mk_rec_name(name const & I) {
return I + name("rec");
}
class add_inductive_fn {
environment m_env;
name_generator m_ngen;
local_ctx m_lctx;
level_param_names m_lparams;
unsigned m_nparams;
buffer<inductive_type> m_ind_types;
buffer<unsigned> m_nindices;
level m_result_level;
/* m_lparams ==> m_levels */
levels m_levels;
/* We track whether the resultant universe cannot be zero for any
universe level instantiation */
bool m_is_not_zero;
/* A free variable for each parameter */
buffer<expr> m_params;
/* A constant for each inductive type */
buffer<expr> m_ind_cnsts;
level m_elim_level;
bool m_K_target;
struct rec_info {
name m_name;
local_ctx m_lctx;
buffer<expr> m_Cs; /* free variables for all motives */
expr m_C; /* free variable for "main" motive */
buffer<expr> m_minor; /* minor premises */
buffer<expr> m_indices;
expr m_major; /* major premise */
};
/* We have an entry for each inductive datatype being declared,
and for nested inductive datatypes. */
buffer<rec_info> m_rec_infos;
public:
add_inductive_fn(environment const & env, inductive_decl const & decl):
m_env(env), m_ngen(*g_ind_fresh), m_lparams(decl.get_lparams()) {
if (!decl.get_nparams().is_small())
throw kernel_exception(env, "invalid inductive datatype, number of parameters is too big");
m_nparams = decl.get_nparams().get_small_value();
to_buffer(decl.get_types(), m_ind_types);
}
void dump() {
std::cout << "add_inductive_decl\n";
for (inductive_type const & ind_type : m_ind_types) {
std::cout << "ind>> " << ind_type.get_name() << " : " << ind_type.get_type() << "\n";
for (constructor const & c : ind_type.get_cnstrs()) {
std::cout << ">> " << constructor_name(c) << " : " << constructor_type(c) << "\n";
}
}
}
/**
\brief Check whether the type of each datatype is well typed, and do not contain free variables or meta variables,
all inductive datatypes have the same parameters, the number of parameters match the argument m_nparams,
and the result universes are equivalent.
This method also initializes the fields:
- m_levels
- m_result_level
- m_nindices
- m_ind_cnsts
- m_params
\remark The local context m_lctx contains the free variables in m_params. */
void check_inductive_types() {
m_levels = param_names_to_levels(m_lparams);
bool first = true;
for (inductive_type const & ind_type : m_ind_types) {
expr type = ind_type.get_type();
check_no_metavar_no_fvar(m_env, ind_type.get_name(), type);
type_checker(m_env).check(type, m_lparams);
m_nindices.push_back(0);
unsigned i = 0;
while (is_pi(type)) {
if (i < m_nparams) {
expr param = m_lctx.mk_local_decl(m_ngen, binding_name(type), binding_domain(type), binding_info(type));
m_params.push_back(param);
type = instantiate(binding_body(type), param);
i++;
} else {
type = binding_body(type);
m_nindices.back()++;
}
}
if (i != m_nparams)
throw kernel_exception(m_env, "number of parameters mismatch in inductive datatype declaration");
type = type_checker(m_env, m_lctx).ensure_sort(type);
if (first) {
m_result_level = sort_level(type);
m_is_not_zero = is_not_zero(m_result_level);
} else if (!is_equivalent(sort_level(type), m_result_level)) {
throw kernel_exception(m_env, "mutually inductive types must live in the same universe");
}
m_ind_cnsts.push_back(mk_constant(ind_type.get_name(), m_levels));
first = false;
}
lean_assert(length(m_levels) == length(m_lparams));
lean_assert(m_nindices.size() == m_ind_types.size());
lean_assert(m_ind_cnsts.size() == m_ind_types.size());
lean_assert(m_params.size() == m_nparams);
}
/** \brief Add all datatype declarations to environment as axioms.
We will update them later using `mk_inductive` when we know the exact number of
recursors. */
void declare_inductive_types_as_constants() {
for (inductive_type const & ind_type : m_ind_types) {
name const & n = ind_type.get_name();
if (m_env.m_declarations.find(n))
throw already_declared_exception(m_env, n);
m_env.m_declarations.insert(n, mk_axiom(n, m_lparams, ind_type.get_type()));
}
}
/** \brief Return true if recursor can only map into Prop */
bool elim_only_at_universe_zero() {
if (m_is_not_zero) {
/* For every universe parameter assignment, the resultant universe is not 0.
So, it is not an inductive predicate */
return false;
}
if (m_ind_types.size() > 1) {
/* Mutually recursive inductive predicates only eliminate into Prop. */
return true;
}
unsigned num_intros = length(m_ind_types[0].get_cnstrs());
if (num_intros > 1) {
/* We have more than one constructor, then recursor for inductive predicate
can only eliminate intro Prop. */
return true;
}
if (num_intros == 0) {
/* empty inductive predicate (e.g., `false`) can eliminate into any universe */
return false;
}
/* We have only one constructor, the final check is, the type of each argument
that is not a parameter:
1- It must live in Prop, *OR*
2- It must occur in the return type. (this is essentially what is called a non-uniform parameter in Coq).
We can justify 2 by observing that this information is not a *secret* it is part of the type.
By eliminating to a non-proposition, we would not be revealing anything that is not already known. */
constructor const & cnstr = head(m_ind_types[0].get_cnstrs());
expr type = constructor_type(cnstr);
unsigned i = 0;
buffer<expr> to_check; /* Arguments that we must check if occur in the result type */
local_ctx lctx;
while (is_pi(type)) {
expr fvar = lctx.mk_local_decl(m_ngen, binding_name(type), binding_domain(type), binding_info(type));
if (i >= m_nparams) {
expr s = type_checker(m_env, lctx).ensure_type(binding_domain(type));
if (!is_zero(sort_level(s))) {
/* Current argument is not in Prop (i.e., condition 1 failed).
We save it in to_check to be able to try condition 2 above. */
to_check.push_back(fvar);
}
}
type = instantiate(binding_body(type), fvar);
i++;
}
buffer<expr> result_args;
get_app_args(type, result_args);
/* Check condition 2: every argument in to_check must occur in result_args */
for (expr const & arg : to_check) {
if (std::find(result_args.begin(), result_args.end(), arg) == result_args.end())
return true; /* Condition 2 failed */
}
return false;
}
/** \brief Initialize m_elim_level. */
void init_elim_level() {
if (elim_only_at_universe_zero()) {
m_elim_level = mk_level_zero();
} else {
name u("u");
int i = 1;
while (std::find(m_lparams.begin(), m_lparams.end(), u) != m_lparams.end()) {
u = name("u").append_after(i);
i++;
}
m_elim_level = mk_univ_param(u);
}
std::cout << ">> elim_level: " << m_elim_level << "\n";
}
void init_K_target() {
/* A declaration is target for K-like reduction when
it has one intro, the intro has 0 arguments, and it is an inductive predicate.
In the following for-loop we check if the intro rule has 0 fields. */
m_K_target =
m_ind_types.size() == 1 && /* It is not a mutual declaration (for simplicity, we don't gain anything by supporting K in mutual declarations. */
is_zero(m_result_level) && /* It is an inductive predicate. */
length(m_ind_types[0].get_cnstrs()) == 1; /* Inductive datatype has only one constructor. */
if (!m_K_target)
return;
expr it = constructor_type(head(m_ind_types[0].get_cnstrs()));
unsigned i = 0;
while (is_pi(it)) {
if (i < m_nparams) {
it = binding_body(it);
} else {
/* See comment above */
m_K_target = false;
break;
}
i++;
}
}
void check_constructor(constructor const & cnstr, unsigned /* idx */) {
check_no_metavar_no_fvar(m_env, constructor_name(cnstr), constructor_type(cnstr));
type_checker(m_env).check(constructor_type(cnstr), m_lparams);
}
void check_constructors() {
for (unsigned idx = 0; idx < m_ind_types.size(); idx++) {
inductive_type const & ind_type = m_ind_types[idx];
for (constructor const & cnstr : ind_type.get_cnstrs()) {
check_constructor(cnstr, idx);
}
}
}
environment operator()() {
dump();
check_inductive_types();
declare_inductive_types_as_constants();
init_elim_level();
init_K_target();
return m_env;
}
};
environment environment::add(inductive_decl const & decl) const {
return add_inductive_fn(*this, decl)();
}
void initialize_inductive() {
g_ind_fresh = new name("_ind_fresh");
}
void finalize_inductive() {
delete g_ind_fresh;
}
}