fix(frontends/lean): fix (and simplify) parameter universe inference

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2014-07-06 16:46:34 -07:00
parent 9be1a4ab46
commit 9a13bef4f3
12 changed files with 854 additions and 471 deletions

View file

@ -83,9 +83,10 @@ environment check_cmd(parser & p) {
mk_section_params(collect_locals(e), p, section_ps);
e = p.lambda_abstract(section_ps, e);
level_param_names ls = to_level_param_names(collect_univ_params(e));
e = p.elaborate(e, false);
type_checker tc(p.env(), p.mk_ngen(), mk_default_converter(p.env(), true));
expr type = tc.check(e, ls);
level_param_names new_ls;
std::tie(e, new_ls) = p.elaborate(e, false);
auto tc = mk_type_checker_with_hints(p.env(), p.mk_ngen());
expr type = tc->check(e, append(ls, new_ls));
p.regular_stream() << e << " : " << type << endl;
return p.env();
}

View file

@ -90,6 +90,14 @@ static environment declare_var(parser & p, environment env,
}
}
/** \brief If we are in a section, then add the new local levels to it. */
static void update_section_local_levels(parser & p, level_param_names const & new_ls) {
if (in_section(p.env())) {
for (auto const & l : new_ls)
p.add_local_level(l, mk_param_univ(l));
}
}
optional<binder_info> parse_binder_info(parser & p) {
optional<binder_info> bi = p.parse_optional_binder_info();
if (bi)
@ -109,7 +117,6 @@ environment variable_cmd_core(parser & p, bool is_axiom) {
if (!in_section(p.env()))
scope1.emplace(p);
parse_univ_params(p, ls_buffer);
parser::param_universe_scope scope2(p);
expr type;
if (!p.curr_is_token(g_colon)) {
buffer<expr> ps;
@ -129,8 +136,10 @@ environment variable_cmd_core(parser & p, bool is_axiom) {
update_univ_parameters(ls_buffer, collect_univ_params(type), p);
ls = to_list(ls_buffer.begin(), ls_buffer.end());
}
type = p.elaborate(type);
return declare_var(p, p.env(), n, ls, type, is_axiom, bi, pos);
level_param_names new_ls;
std::tie(type, new_ls) = p.elaborate(type);
update_section_local_levels(p, new_ls);
return declare_var(p, p.env(), n, append(ls, new_ls), type, is_axiom, bi, pos);
}
environment variable_cmd(parser & p) {
return variable_cmd_core(p, false);
@ -235,24 +244,18 @@ environment definition_cmd_core(parser & p, bool is_theorem, bool _is_opaque) {
value = p.parse_expr();
} else if (p.curr_is_token(g_colon)) {
p.next();
{
parser::param_universe_scope scope2(p);
type = p.parse_expr();
}
type = p.parse_expr();
p.check_token_next(g_assign, "invalid declaration, ':=' expected");
value = p.parse_expr();
} else {
buffer<expr> ps;
optional<local_environment> lenv;
{
parser::param_universe_scope scope2(p);
lenv = p.parse_binders(ps);
if (p.curr_is_token(g_colon)) {
p.next();
type = p.parse_scoped_expr(ps, *lenv);
} else {
type = p.save_pos(mk_expr_placeholder(), p.pos());
}
lenv = p.parse_binders(ps);
if (p.curr_is_token(g_colon)) {
p.next();
type = p.parse_scoped_expr(ps, *lenv);
} else {
type = p.save_pos(mk_expr_placeholder(), p.pos());
}
p.check_token_next(g_assign, "invalid declaration, ':=' expected");
value = p.parse_scoped_expr(ps, *lenv);
@ -274,17 +277,14 @@ environment definition_cmd_core(parser & p, bool is_theorem, bool _is_opaque) {
if (real_n != n)
env = add_alias(env, n, mk_constant(real_n));
}
level_param_names new_ls;
if (is_theorem) {
// TODO(Leo): delay theorems
auto type_value = p.elaborate(n, type, value);
type = type_value.first;
value = type_value.second;
env = module::add(env, check(env, mk_theorem(real_n, ls, type, value)));
std::tie(type, value, new_ls) = p.elaborate(n, type, value);
env = module::add(env, check(env, mk_theorem(real_n, append(ls, new_ls), type, value)));
} else {
auto type_value = p.elaborate(n, type, value);
type = type_value.first;
value = type_value.second;
env = module::add(env, check(env, mk_definition(env, real_n, ls, type, value, modifiers.m_is_opaque)));
std::tie(type, value, new_ls) = p.elaborate(n, type, value);
env = module::add(env, check(env, mk_definition(env, real_n, append(ls, new_ls), type, value, modifiers.m_is_opaque)));
}
if (modifiers.m_is_class)
env = add_class(env, real_n);
@ -315,11 +315,13 @@ static environment variables_cmd(parser & p) {
optional<parser::local_scope> scope1;
if (!in_section(p.env()))
scope1.emplace(p);
parser::param_universe_scope scope2(p);
expr type = p.parse_expr();
p.parse_close_binder_info(bi);
level_param_names ls = to_level_param_names(collect_univ_params(type));
type = p.elaborate(type);
level_param_names new_ls;
std::tie(type, new_ls) = p.elaborate(type);
update_section_local_levels(p, new_ls);
ls = append(ls, new_ls);
environment env = p.env();
for (auto id : ids)
env = declare_var(p, env, id, ls, type, true, bi, pos);

View file

@ -15,6 +15,7 @@ Author: Leonardo de Moura
#include "kernel/instantiate.h"
#include "kernel/type_checker.h"
#include "kernel/for_each_fn.h"
#include "kernel/replace_fn.h"
#include "kernel/kernel_exception.h"
#include "kernel/error_msgs.h"
#include "kernel/expr_maps.h"
@ -24,9 +25,11 @@ Author: Leonardo de Moura
#include "library/explicit.h"
#include "library/unifier.h"
#include "library/opaque_hints.h"
#include "library/locals.h"
#include "library/tactic/tactic.h"
#include "library/tactic/expr_to_tactic.h"
#include "library/error_handling/error_handling.h"
#include "frontends/lean/local_decls.h"
#include "frontends/lean/class.h"
#ifndef LEAN_DEFAULT_ELABORATOR_LOCAL_INSTANCES
@ -43,6 +46,79 @@ bool get_elaborator_local_instances(options const & opts) {
}
// ==========================================
/** \brief Functional object for converting the universe metavariables in an expression in new universe parameters.
The substitution is updated with the mapping metavar -> new param.
The set of parameter names m_params and the buffer m_new_params are also updated.
*/
class univ_metavars_to_params_fn {
environment const & m_env;
local_decls<level> const & m_lls;
substitution & m_subst;
name_set & m_params;
buffer<name> & m_new_params;
unsigned m_next_idx;
/** \brief Create a new universe parameter s.t. the new name does not occur in \c m_params, nor it is
a global universe in \e m_env or in the local_decls<level> m_lls.
The new name is added to \c m_params, and the new level parameter is returned.
The name is of the form "l_i" where \c i >= m_next_idx.
*/
level mk_new_univ_param() {
name l("l");
name r = l.append_after(m_next_idx);
while (m_lls.contains(r) || m_params.contains(r) || m_env.is_universe(r)) {
m_next_idx++;
r = l.append_after(m_next_idx);
}
m_params.insert(r);
m_new_params.push_back(r);
return mk_param_univ(r);
}
public:
univ_metavars_to_params_fn(environment const & env, local_decls<level> const & lls, substitution & s, name_set & ps, buffer<name> & new_ps):
m_env(env), m_lls(lls), m_subst(s), m_params(ps), m_new_params(new_ps), m_next_idx(1) {}
level apply(level const & l) {
return replace(l, [&](level const & l) {
if (!has_meta(l))
return some_level(l);
if (is_meta(l)) {
if (auto it = m_subst.get_level(meta_id(l))) {
return some_level(*it);
} else {
level new_p = mk_new_univ_param();
m_subst.d_assign(l, new_p);
return some_level(new_p);
}
}
return none_level();
});
}
expr apply(expr const & e) {
if (!has_univ_metavar(e)) {
return e;
} else {
return replace(e, [&](expr const & e, unsigned) {
if (!has_univ_metavar(e)) {
return some_expr(e);
} else if (is_sort(e)) {
return some_expr(update_sort(e, apply(sort_level(e))));
} else if (is_constant(e)) {
levels ls = map(const_levels(e), [&](level const & l) { return apply(l); });
return some_expr(update_constant(e, ls));
} else {
return none_expr();
}
});
}
}
expr operator()(expr const & e) { return apply(e); }
};
/** \brief Helper class for implementing the \c elaborate functions. */
class elaborator {
typedef list<expr> context;
typedef std::vector<constraint> constraint_vect;
@ -51,23 +127,24 @@ class elaborator {
typedef std::unique_ptr<type_checker> type_checker_ptr;
environment m_env;
local_decls<level> m_lls;
io_state m_ios;
name_generator m_ngen;
type_checker_ptr m_tc;
substitution m_subst;
context m_ctx;
pos_info_provider * m_pos_provider;
context m_ctx; // current local context: a list of local constants
pos_info_provider * m_pos_provider; // optional expression position information used when reporting errors.
justification m_accumulated; // accumulate justification of eagerly used substitutions
constraint_vect m_constraints;
tactic_hints m_tactic_hints;
mvar2meta m_mvar2meta;
constraint_vect m_constraints; // constraints that must be solved for the elaborated term to be type correct.
tactic_hints m_tactic_hints; // mapping from metavariable name ?m to tactic expression that should be used to solve it.
// this mapping is populated by the 'by tactic-expr' expression.
mvar2meta m_mvar2meta; // mapping from metavariable ?m to the (?m l_1 ... l_n) where [l_1 ... l_n] are the local constants
// representing the context where ?m was created.
name_set m_displayed_errors; // set of metavariables that we already reported unsolved/unassigned
bool m_check_unassigned;
bool m_use_local_instances;
/**
\brief Auxiliary object for creating backtracking points.
bool m_check_unassigned; // if true if display error messages if elaborated term still contains metavariables
bool m_use_local_instances; // if true class-instance resolution will use the local context
/** \brief Auxiliary object for creating backtracking points.
\remark A scope can only be created when m_constraints and m_subst are empty,
and m_accumulated is none.
*/
@ -93,6 +170,7 @@ class elaborator {
}
};
/* \brief Move all constraints generated by the type checker to the buffer m_constraints. */
void consume_tc_cnstrs() {
while (auto c = m_tc->next_cnstr())
m_constraints.push_back(*c);
@ -102,6 +180,12 @@ class elaborator {
virtual optional<constraints> next() = 0;
};
/** \brief 'Choice' expressions <tt>(choice e_1 ... e_n)</tt> are mapped into a metavariable \c ?m
and a choice constraints <tt>(?m in fn)</tt> where \c fn is a choice function.
The choice function produces a stream of alternatives. In this case, it produces a stream of
size \c n, one alternative for each \c e_i.
This is a helper class for implementing this choice functions.
*/
struct choice_expr_elaborator : public choice_elaborator {
elaborator & m_elab;
expr m_mvar;
@ -131,13 +215,24 @@ class elaborator {
}
};
/** \brief Whenever the elaborator finds a placeholder '_' or introduces an implicit argument, it creates
a metavariable \c ?m. If the expected type of ?m is unknown (e.g., it is another metavariable),
or if it is a 'class', then we also create a delayed choice constraint (?m in fn).
The unifier only process delayed choice constraints when there are no other kind of constraint to be
processed. The function \c fn produces a stream of alternative solutions for ?m.
In this case, \c fn will do the following:
1) if the elaborated type of ?m is a 'class' C, then the stream will contain all 'instances' of class C.
2) if the elaborated type of ?m is not a 'class', then fn produces a single empty solution.
This is a helper class for implementing this choice function.
*/
struct class_elaborator : public choice_elaborator {
elaborator & m_elab;
expr m_mvar;
expr m_mvar_type;
list<expr> m_local_instances;
list<name> m_instances;
context m_ctx;
expr m_mvar_type; // elaborated type of the metavariable
list<expr> m_local_instances; // local instances that should also be included in the class-instance resolution.
list<name> m_instances; // global declaration names that are class instances. This information is retrieved using #get_class_instances.
context m_ctx; // local context for m_mvar
substitution m_subst;
justification m_jst;
@ -193,9 +288,9 @@ class elaborator {
}
public:
elaborator(environment const & env, io_state const & ios, name_generator const & ngen, pos_info_provider * pp,
bool check_unassigned):
m_env(env), m_ios(ios),
elaborator(environment const & env, local_decls<level> const & lls, io_state const & ios, name_generator const & ngen,
pos_info_provider * pp, bool check_unassigned):
m_env(env), m_lls(lls), m_ios(ios),
m_ngen(ngen), m_tc(mk_type_checker_with_hints(env, m_ngen.mk_child())),
m_pos_provider(pp) {
m_check_unassigned = check_unassigned;
@ -231,8 +326,7 @@ public:
m_constraints.push_back(c);
}
/**
\brief Add \c c to \c m_constraints, but also tries to update \c m_subst using \c c.
/** \brief Add \c c to \c m_constraints, but also tries to update \c m_subst using \c c.
The idea is to "populate" \c m_subst using easy/simple constraints.
This trick improves the number of places where coercions can be applied.
In the future, we may also use this information to implement eager pruning of choice
@ -254,9 +348,7 @@ public:
throw unifier_exception(c.get_justification(), m_subst);
}
/**
\brief Eagerly instantiate metavars using \c m_subst.
/** \brief Eagerly instantiate metavars using \c m_subst.
\remark We update \c m_accumulated with any justifications used.
*/
expr instantiate_metavars(expr const & e) {
@ -270,11 +362,10 @@ public:
return e;
}
/**
\brief Given <tt>e[l_1, ..., l_n]</tt> and assuming \c m_ctx is
<tt>[l_n : A_n[l_1, ..., l_{n-1}], ..., l_1 : A_1 ]</tt>,
then the result is
<tt>(Pi (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), e[x_1, ... x_n])</tt>.
/** \brief Given <tt>e[l_1, ..., l_n]</tt> and assuming \c m_ctx is
<tt>[l_n : A_n[l_1, ..., l_{n-1}], ..., l_1 : A_1 ]</tt>,
then the result is
<tt>(Pi (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), e[x_1, ... x_n])</tt>.
*/
expr pi_abstract_context(expr e, tag g) {
for (auto const & p : m_ctx)
@ -286,8 +377,7 @@ public:
return save_tag(::lean::mk_app(f, a), g);
}
/**
\brief Assuming \c m_ctx is
/** \brief Assuming \c m_ctx is
<tt>[l_n : A_n[l_1, ..., l_{n-1}], ..., l_1 : A_1 ]</tt>,
return <tt>(f l_1 ... l_n)</tt>.
*/
@ -304,12 +394,11 @@ public:
return r;
}
/**
\brief Assuming \c m_ctx is
/** \brief Assuming \c m_ctx is
<tt>[l_n : A_n[l_1, ..., l_{n-1}], ..., l_1 : A_1 ]</tt>,
return a fresh metavariable \c ?m with type
return a fresh metavariable \c ?m with type
<tt>(Pi (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), Type.{?u})</tt>,
where \c ?u is a fresh universe metavariable.
where \c ?u is a fresh universe metavariable.
*/
expr mk_type_metavar(tag g) {
name n = m_ngen.next();
@ -318,12 +407,11 @@ public:
return save_tag(::lean::mk_metavar(n, t), g);
}
/**
\brief Assuming \c m_ctx is
/** \brief Assuming \c m_ctx is
<tt>[l_n : A_n[l_1, ..., l_{n-1}], ..., l_1 : A_1 ]</tt>,
return <tt>(?m l_1 ... l_n)</tt> where \c ?m is a fresh metavariable with type
return <tt>(?m l_1 ... l_n)</tt> where \c ?m is a fresh metavariable with type
<tt>(Pi (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), Type.{?u})</tt>,
and \c ?u is a fresh universe metavariable.
and \c ?u is a fresh universe metavariable.
\remark The type of the resulting expression is <tt>Type.{?u}</tt>
*/
@ -331,16 +419,15 @@ public:
return apply_context(mk_type_metavar(g), g);
}
/**
\brief Given <tt>type[l_1, ..., l_n]</tt> and assuming \c m_ctx is
/** \brief Given <tt>type[l_1, ..., l_n]</tt> and assuming \c m_ctx is
<tt>[l_n : A_n[l_1, ..., l_{n-1}], ..., l_1 : A_1 ]</tt>,
then the result is a fresh metavariable \c ?m with type
then the result is a fresh metavariable \c ?m with type
<tt>(Pi (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), type[x_1, ... x_n])</tt>.
If <tt>type</tt> is none, then the result is a fresh metavariable \c ?m1 with type
If <tt>type</tt> is none, then the result is a fresh metavariable \c ?m1 with type
<tt>(Pi (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), ?m2 x1 .... xn)</tt>,
where ?m2 is another fresh metavariable with type
where ?m2 is another fresh metavariable with type
<tt>(Pi (x_1 : A_1) ... (x_n : A_n[x_1, ..., x_{n-1}]), Type.{?u})</tt>,
and \c ?u is a fresh universe metavariable.
and \c ?u is a fresh universe metavariable.
*/
expr mk_metavar(optional<expr> const & type, tag g) {
name n = m_ngen.next();
@ -349,13 +436,12 @@ public:
return save_tag(::lean::mk_metavar(n, t), g);
}
/**
\brief Given <tt>type[l_1, ..., l_n]</tt> and assuming \c m_ctx is
<tt>[l_n : A_n[l_1, ..., l_{n-1}], ..., l_1 : A_1 ]</tt>,
return (?m l_1 ... l_n), where ?m is a fresh metavariable
created using \c mk_metavar.
/** \brief Given <tt>type[l_1, ..., l_n]</tt> and assuming \c m_ctx is
<tt>[l_n : A_n[l_1, ..., l_{n-1}], ..., l_1 : A_1 ]</tt>,
return (?m l_1 ... l_n), where ?m is a fresh metavariable
created using \c mk_metavar.
\see mk_metavar
\see mk_metavar
*/
expr mk_meta(optional<expr> const & type, tag g) {
expr mvar = mk_metavar(type, g);
@ -385,9 +471,8 @@ public:
return is_class(type);
}
/**
\brief Create a metavariable, but also add a class-constraint if type is a class
or a metavariable.
/** \brief Create a metavariable, but also add a class-constraint if type is a class
or a metavariable.
*/
expr mk_placeholder_meta(optional<expr> const & type, tag g) {
expr m = mk_meta(type, g);
@ -422,10 +507,8 @@ public:
return m;
}
/**
\brief Convert the metavariable to the metavariable application that captures
/** \brief Convert the metavariable to the metavariable application that captures
the context where it was defined.
*/
optional<expr> mvar_to_meta(expr mvar) {
if (auto it = m_mvar2meta.find(mlocal_name(mvar)))
@ -473,8 +556,7 @@ public:
return m;
}
/**
\brief Make sure \c f is really a function, if it is not, try to apply coercions.
/** \brief Make sure \c f is really a function, if it is not, try to apply coercions.
The result is a pair <tt>new_f, f_type</tt>, where new_f is the new value for \c f,
and \c f_type is its type (and a Pi-expression)
*/
@ -527,10 +609,9 @@ public:
return a;
}
/**
\brief Given an application \c e, where the expected type is d_type, and the argument type is a_type,
create a "delayed coercion". The idea is to create a choice constraint and postpone the coercion
search. We do that whenever d_type or a_type is a metavar application, and d_type or a_type is a coercion source/target.
/** \brief Given an application \c e, where the expected type is d_type, and the argument type is a_type,
create a "delayed coercion". The idea is to create a choice constraint and postpone the coercion
search. We do that whenever d_type or a_type is a metavar application, and d_type or a_type is a coercion source/target.
*/
expr mk_delayed_coercion(expr const & e, expr const & d_type, expr const & a_type) {
expr a = app_arg(e);
@ -884,14 +965,23 @@ public:
}
/** \brief Apply substitution and solve remaining metavariables using tactics. */
expr apply(substitution & s, expr const & e) {
expr apply(substitution & s, expr const & e, name_set & univ_params, buffer<name> & new_params) {
expr r = s.instantiate(e);
if (has_univ_metavar(r))
r = univ_metavars_to_params_fn(m_env, m_lls, s, univ_params, new_params)(r);
r = solve_unassigned_mvars(s, r);
display_unassigned_mvars(r, s);
return r;
}
expr operator()(expr const & e) {
std::tuple<expr, level_param_names> apply(substitution & s, expr const & e) {
auto ps = collect_univ_params(e);
buffer<name> new_ps;
expr r = apply(s, e, ps, new_ps);
return std::make_tuple(r, to_list(new_ps.begin(), new_ps.end()));
}
std::tuple<expr, level_param_names> operator()(expr const & e) {
expr r = visit(e);
auto p = solve().pull();
lean_assert(p);
@ -908,7 +998,7 @@ public:
return r;
}
expr operator()(expr const & e, expr const & expected_type) {
std::tuple<expr, level_param_names> operator()(expr const & e, expr const & expected_type) {
expr r = visit(e);
expr r_type = infer_type(r);
environment env = m_env;
@ -927,7 +1017,7 @@ public:
return apply(s, r);
}
std::pair<expr, expr> operator()(expr const & t, expr const & v, name const & n) {
std::tuple<expr, expr, level_param_names> operator()(expr const & t, expr const & v, name const & n) {
expr r_t = visit(t);
expr r_v = visit(v);
expr r_v_type = infer_type(r_v);
@ -944,18 +1034,23 @@ public:
auto p = solve().pull();
lean_assert(p);
substitution s = p->first;
return mk_pair(apply(s, r_t), apply(s, r_v));
name_set univ_params = collect_univ_params(r_v, collect_univ_params(r_t));
buffer<name> new_params;
expr new_r_t = apply(s, r_t, univ_params, new_params);
expr new_r_v = apply(s, r_v, univ_params, new_params);
return std::make_tuple(new_r_t, new_r_v, to_list(new_params.begin(), new_params.end()));
}
};
static name g_tmp_prefix = name::mk_internal_unique_name();
expr elaborate(environment const & env, io_state const & ios, expr const & e, pos_info_provider * pp, bool check_unassigned) {
return elaborator(env, ios, name_generator(g_tmp_prefix), pp, check_unassigned)(e);
std::tuple<expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, io_state const & ios,
expr const & e, pos_info_provider * pp, bool check_unassigned) {
return elaborator(env, lls, ios, name_generator(g_tmp_prefix), pp, check_unassigned)(e);
}
std::pair<expr, expr> elaborate(environment const & env, io_state const & ios, name const & n, expr const & t, expr const & v,
pos_info_provider * pp) {
return elaborator(env, ios, name_generator(g_tmp_prefix), pp, true)(t, v, n);
std::tuple<expr, expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, io_state const & ios,
name const & n, expr const & t, expr const & v, pos_info_provider * pp) {
return elaborator(env, lls, ios, name_generator(g_tmp_prefix), pp, true)(t, v, n);
}
}

View file

@ -10,9 +10,11 @@ Author: Leonardo de Moura
#include "kernel/environment.h"
#include "kernel/metavar.h"
#include "library/io_state.h"
#include "frontends/lean/local_decls.h"
namespace lean {
expr elaborate(environment const & env, io_state const & ios, expr const & e, pos_info_provider * pp = nullptr, bool check_unassigned = true);
std::pair<expr, expr> elaborate(environment const & env, io_state const & ios, name const & n, expr const & t, expr const & v,
pos_info_provider * pp = nullptr);
std::tuple<expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, io_state const & ios, expr const & e,
pos_info_provider * pp = nullptr, bool check_unassigned = true);
std::tuple<expr, expr, level_param_names> elaborate(environment const & env, local_decls<level> const & lls, io_state const & ios,
name const & n, expr const & t, expr const & v, pos_info_provider * pp = nullptr);
}

View file

@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <algorithm>
#include "util/sstream.h"
#include "util/name_map.h"
#include "kernel/replace_fn.h"
@ -16,6 +17,7 @@ Author: Leonardo de Moura
#include "library/placeholder.h"
#include "library/aliases.h"
#include "library/explicit.h"
#include "library/opaque_hints.h"
#include "frontends/lean/decl_cmds.h"
#include "frontends/lean/util.h"
#include "frontends/lean/parser.h"
@ -36,72 +38,6 @@ using inductive::inductive_decl_intros;
using inductive::intro_rule_name;
using inductive::intro_rule_type;
// Make sure that every inductive datatype (in decls) occurring in \c type has
// the universe levels \c lvl_params and section parameters \c section_params
static expr fix_inductive_occs(expr const & type, buffer<inductive_decl> const & decls,
buffer<name> const & lvl_params, buffer<expr> const & section_params) {
return replace(type, [&](expr const & e, unsigned) {
if (!is_constant(e))
return none_expr();
if (!std::any_of(decls.begin(), decls.end(),
[&](inductive_decl const & d) { return const_name(e) == inductive_decl_name(d); }))
return none_expr();
// found target
levels ls = const_levels(e);
unsigned n = length(ls);
if (n < lvl_params.size()) {
unsigned i = lvl_params.size() - n;
while (i > 0) {
--i;
ls = cons(mk_param_univ(lvl_params[i]), ls);
}
}
expr r = update_constant(e, ls);
for (unsigned i = 0; i < section_params.size(); i++)
r = mk_app(r, section_params[i]);
return some_expr(r);
});
}
static level mk_result_level(bool impredicative, buffer<level> const & ls) {
if (ls.empty()) {
return impredicative ? mk_level_one() : mk_level_zero();
} else {
level r = ls[0];
for (unsigned i = 1; i < ls.size(); i++)
r = mk_max(r, ls[i]);
if (is_not_zero(r))
return r;
else
return impredicative ? mk_max(r, mk_level_one()) : r;
}
}
static expr update_result_sort(type_checker & tc, expr t, level const & l) {
t = tc.whnf(t);
if (is_pi(t)) {
return update_binding(t, binding_domain(t), update_result_sort(tc, binding_body(t), l));
} else if (is_sort(t)) {
return update_sort(t, l);
} else {
lean_unreachable();
}
}
/** \brief Return the universe level of the given inductive datatype declaration. */
level get_datatype_result_level(type_checker & tc, inductive_decl const & d) {
expr d_t = tc.whnf(inductive_decl_type(d));
while (is_pi(d_t)) {
d_t = tc.whnf(binding_body(d_t));
}
if (!is_sort(d_t)) {
std::cout << "ERROR: " << inductive_decl_type(d) << "\n";
throw exception(sstream() << "invalid inductive datatype '" << inductive_decl_name(d) << "', "
"resultant type is not a sort");
}
return sort_level(d_t);
}
/** \brief Return true if \c u occurs in \c l */
bool occurs(level const & u, level const & l) {
bool found = false;
@ -113,37 +49,6 @@ bool occurs(level const & u, level const & l) {
return found;
}
static name g_tmp_prefix = name::mk_internal_unique_name();
/**
\brief Given a type \c t for an introduction rule, store the universe of the types of non-parameters in \c ls.
\remark aux_u is an temporary universe used for inductive decls. It should be ignored.
*/
static void accumulate_levels(type_checker & tc, expr t, unsigned num_params, level const & aux_u, buffer<level> & ls) {
name_generator ngen(g_tmp_prefix);
unsigned i = 0;
while (is_pi(t)) {
if (i >= num_params) {
expr s = tc.ensure_type(binding_domain(t));
level l = sort_level(s);
if (l == aux_u) {
// ignore, this is the auxiliary level
} else if (occurs(aux_u, l)) {
throw exception("failed to infer inductive datatype resultant universe, provide the universe levels explicitly");
} else if (std::find(ls.begin(), ls.end(), l) == ls.end()) {
ls.push_back(l);
}
}
t = instantiate(binding_body(t), mk_local(ngen.next(), binding_name(t), binding_domain(t), binding_info(t)));
i++;
}
}
void throw_all_or_nothing() {
throw exception("invalid mutually recursive datatype declaration, "
"if the universe level of one type is provided, then all of them should be");
}
inductive_decl update_inductive_decl(inductive_decl const & d, expr const & t) {
return inductive_decl(inductive_decl_name(d), t, inductive_decl_intros(d));
}
@ -156,231 +61,522 @@ intro_rule update_intro_rule(intro_rule const & r, expr const & t) {
return intro_rule(intro_rule_name(r), t);
}
static void elaborate_inductive(buffer<inductive_decl> & decls, level_param_names const & lvls, unsigned num_params, parser & p) {
// temporary environment used during elaboration
environment env = p.env();
// add fake universe level
name u_name(g_tmp_prefix, "u");
env = env.add_universe(u_name);
level u = mk_global_univ(u_name);
std::unique_ptr<type_checker> tc(new type_checker(env));
bool infer_result_universe = false;
unsigned first = true;
// elaborate inductive datatype types, and declare them in temporary environment.
for (inductive_decl & d : decls) {
level l = get_datatype_result_level(*tc, d);
expr t = inductive_decl_type(d);
if (is_placeholder(l)) {
if (first)
infer_result_universe = true;
else if (!infer_result_universe)
throw_all_or_nothing();
t = update_result_sort(*tc, t, u);
} else if (!first && infer_result_universe) {
throw_all_or_nothing();
}
t = p.elaborate(env, t);
env = env.add(check(env, mk_var_decl(inductive_decl_name(d), lvls, t)));
d = update_inductive_decl(d, t);
first = false;
}
tc.reset(new type_checker(env));
buffer<level> r_lvls; // used for inferring the universe level of resultant datatypes.
// elaborate introduction rules using temporary environment
for (inductive_decl & d : decls) {
buffer<intro_rule> intros;
for (intro_rule const & ir : inductive_decl_intros(d)) {
expr t = p.elaborate(env, intro_rule_type(ir));
if (infer_result_universe)
accumulate_levels(*tc, t, num_params, u, r_lvls);
intros.push_back(update_intro_rule(ir, t));
}
d = update_inductive_decl(d, intros);
}
if (infer_result_universe) {
level r_lvl = normalize(mk_result_level(env.impredicative(), r_lvls));
for (inductive_decl & d : decls) {
expr t = inductive_decl_type(d);
t = update_result_sort(*tc, t, r_lvl);
d = update_inductive_decl(d, t);
}
}
}
static name g_tmp_prefix = name::mk_internal_unique_name();
static environment create_alias(environment const & env, name const & full_id, name const & id, levels const & section_leves,
buffer<expr> const & section_params, parser & p) {
if (in_section(env)) {
expr r = mk_explicit(mk_constant(full_id, section_leves));
for (unsigned i = 0; i < section_params.size(); i++)
r = mk_app(r, section_params[i]);
p.add_local_expr(id, r);
return env;
} else if (full_id != id) {
return add_alias(env, id, mk_constant(full_id));
} else {
return env;
}
}
struct inductive_cmd_fn {
typedef std::unique_ptr<type_checker> type_checker_ptr;
parser & m_p;
environment m_env;
type_checker_ptr m_tc;
name m_namespace; // current namespace
pos_info m_pos; // current position for reporting errors
bool m_first; // true if parsing the first inductive type in a mutually recursive inductive decl.
buffer<name> m_explict_levels;
buffer<name> m_levels;
bool m_using_explicit_levels; // true if the user is providing explicit universe levels
unsigned m_num_params; // number of parameters
level m_u; // temporary auxiliary global universe used for inferring the result universe of an inductive datatype declaration.
bool m_infer_result_universe;
name_set m_relaxed_implicit_infer; // set of introduction rules where we do not use strict implicit parameter infererence
environment inductive_cmd(parser & p) {
parser::no_undef_id_error_scope err_scope(p);
environment env = p.env();
name const & ns = get_namespace(env);
bool first = true;
buffer<name> ls_buffer;
name_map<name> id_to_short_id;
// store intro rule name that are markes for relaxed implicit argument inference.
name_set relaxed_implicit_inference;
unsigned num_params = 0;
bool explicit_levels = false;
buffer<inductive_decl> decls;
while (true) {
parser::local_scope l_scope(p);
auto id_pos = p.pos();
name id = p.check_id_next("invalid inductive declaration, identifier expected");
inductive_cmd_fn(parser & p):m_p(p) {
m_env = p.env();
m_first = true;
m_using_explicit_levels = false;
m_num_params = 0;
name u_name(g_tmp_prefix, "u");
m_env = m_env.add_universe(u_name);
m_u = mk_global_univ(u_name);
m_infer_result_universe = false;
m_namespace = get_namespace(m_env);
m_tc = mk_type_checker_with_hints(m_env, m_p.mk_ngen());
}
[[ noreturn ]] void throw_error(char const * error_msg) { throw parser_error(error_msg, m_pos); }
[[ noreturn ]] void throw_error(sstream const & strm) { throw parser_error(strm, m_pos); }
/** \brief Parse the name of an inductive datatype or introduction rule,
prefix the current namespace to it and return it.
*/
name parse_decl_name() {
m_pos = m_p.pos();
name id = m_p.check_id_next("invalid declaration, identifier expected");
check_atomic(id);
name full_id = ns + id;
id_to_short_id.insert(full_id, id);
return m_namespace + id;
}
/** \brief Parse inductive declaration universe parameters.
If this is the first declaration in a mutually recursive block, then this method
stores the levels in m_explict_levels, and set m_using_explicit_levels to true (iff they were provided).
If this is not the first declaration, then this function just checks if the parsed parameters
match the ones in the first declaration (stored in \c m_explict_levels)
*/
void parse_inductive_univ_params() {
buffer<name> curr_ls_buffer;
expr type;
optional<parser::param_universe_scope> pu_scope;
if (parse_univ_params(p, curr_ls_buffer)) {
if (first) {
explicit_levels = true;
ls_buffer.append(curr_ls_buffer);
} else if (!explicit_levels) {
throw parser_error("invalid mutually recursive declaration, "
"explicit universe levels were not provided for previous inductive types in this declaration",
id_pos);
} else if (curr_ls_buffer.size() != ls_buffer.size()) {
throw parser_error("invalid mutually recursive declaration, "
"all inductive types must have the same number of universe paramaters", id_pos);
if (parse_univ_params(m_p, curr_ls_buffer)) {
if (m_first) {
m_using_explicit_levels = true;
m_explict_levels.append(curr_ls_buffer);
} else if (!m_using_explicit_levels) {
throw_error("invalid mutually recursive declaration, "
"explicit universe levels were not provided for previous inductive types in this declaration");
} else if (curr_ls_buffer.size() != m_explict_levels.size()) {
throw_error("invalid mutually recursive declaration, "
"all inductive types must have the same number of universe paramaters");
} else {
for (unsigned i = 0; i < ls_buffer.size(); i++) {
if (curr_ls_buffer[i] != ls_buffer[i])
throw parser_error("invalid mutually recursive inductive declaration, "
"all inductive types must have the same universe paramaters", id_pos);
for (unsigned i = 0; i < m_explict_levels.size(); i++) {
if (curr_ls_buffer[i] != m_explict_levels[i])
throw_error("invalid mutually recursive inductive declaration, "
"all inductive types must have the same universe paramaters");
}
}
} else {
if (first) {
explicit_levels = false;
} else if (explicit_levels) {
throw parser_error("invalid mutually recursive declaration, "
"explicit universe levels were provided for previous inductive types in this declaration",
id_pos);
if (m_first) {
m_using_explicit_levels = false;
} else if (m_using_explicit_levels) {
throw_error("invalid mutually recursive declaration, "
"explicit universe levels were provided for previous inductive types in this declaration");
}
// initialize param_universe_scope, we are using implicit universe levels
pu_scope.emplace(p);
}
}
/** \brief Parse the type of an inductive datatype */
expr parse_datatype_type() {
expr type;
buffer<expr> ps;
local_environment lenv = env;
auto params_pos = p.pos();
if (!p.curr_is_token(g_colon)) {
lenv = p.parse_binders(ps);
p.check_token_next(g_colon, "invalid inductive declaration, ':' expected");
{
parser::placeholder_universe_scope place_scope(p);
type = p.parse_scoped_expr(ps, lenv);
}
type = p.pi_abstract(ps, type);
m_pos = m_p.pos();
if (!m_p.curr_is_token(g_colon)) {
m_p.parse_binders(ps);
m_p.check_token_next(g_colon, "invalid inductive declaration, ':' expected");
type = m_p.parse_scoped_expr(ps);
type = m_p.pi_abstract(ps, type);
} else {
p.next();
parser::placeholder_universe_scope place_scope(p);
type = p.parse_scoped_expr(ps, lenv);
m_p.next();
type = m_p.parse_expr();
}
// check if number of parameters
if (first) {
num_params = ps.size();
} else {
if (m_first) {
m_num_params = ps.size();
} else if (m_num_params != ps.size()) {
// mutually recursive declaration checks
if (num_params != ps.size()) {
throw parser_error("invalid mutually recursive inductive declaration, "
"all inductive types must have the same number of arguments",
params_pos);
}
throw_error("invalid mutually recursive inductive declaration, all inductive types must have the same number of arguments");
}
// parse introduction rules
p.check_token_next(g_assign, "invalid inductive declaration, ':=' expected");
buffer<intro_rule> intros;
while (p.curr_is_token(g_bar)) {
p.next();
name intro_id = p.check_id_next("invalid introduction rule, identifier expected");
check_atomic(intro_id);
name full_intro_id = ns + intro_id;
id_to_short_id.insert(full_intro_id, intro_id);
bool strict = true;
if (p.curr_is_token(g_lcurly)) {
p.next();
p.check_token_next(g_rcurly, "invalid introduction rule, '}' expected");
strict = false;
relaxed_implicit_inference.insert(full_intro_id);
}
p.check_token_next(g_colon, "invalid introduction rule, ':' expected");
expr intro_type = p.parse_scoped_expr(ps, lenv);
intro_type = p.pi_abstract(ps, intro_type);
intro_type = infer_implicit(intro_type, ps.size(), strict);
intros.push_back(intro_rule(full_intro_id, intro_type));
}
decls.push_back(inductive_decl(full_id, type, to_list(intros.begin(), intros.end())));
if (!p.curr_is_token(g_with))
break;
p.next();
first = false;
return type;
}
// Collect (section) locals occurring in inductive_decls, and abstract them
// using these additional parameters.
name_set used_levels;
name_set section_locals;
for (inductive_decl const & d : decls) {
used_levels = collect_univ_params(inductive_decl_type(d), used_levels);
section_locals = collect_locals(inductive_decl_type(d), section_locals);
for (auto const & ir : inductive_decl_intros(d)) {
used_levels = collect_univ_params(intro_rule_type(ir), used_levels);
section_locals = collect_locals(intro_rule_type(ir), section_locals);
}
}
update_univ_parameters(ls_buffer, used_levels, p);
buffer<expr> section_params;
mk_section_params(section_locals, p, section_params);
// First, add section_params to inductive types type.
// We don't update the introduction rules in the first pass, because
// we will mark all section_params as implicit for them.
for (inductive_decl & d : decls) {
d = update_inductive_decl(d, p.pi_abstract(section_params, inductive_decl_type(d)));
}
// Add section_params to introduction rules type, and also "fix"
// occurrences of inductive types.
for (inductive_decl & d : decls) {
buffer<intro_rule> new_irs;
for (auto const & ir : inductive_decl_intros(d)) {
expr type = intro_rule_type(ir);
type = fix_inductive_occs(type, decls, ls_buffer, section_params);
type = p.pi_abstract(section_params, type);
bool strict = relaxed_implicit_inference.contains(intro_rule_name(ir));
type = infer_implicit(type, section_params.size(), strict);
new_irs.push_back(update_intro_rule(ir, type));
}
d = update_inductive_decl(d, new_irs);
}
num_params += section_params.size();
level_param_names ls = to_list(ls_buffer.begin(), ls_buffer.end());
elaborate_inductive(decls, ls, num_params, p);
env = module::add_inductive(env, ls, num_params, to_list(decls.begin(), decls.end()));
// Create aliases/local refs
levels section_levels = collect_section_levels(ls, p);
for (inductive_decl const & d : decls) {
name const & n = inductive_decl_name(d);
env = create_alias(env, n, *id_to_short_id.find(n), section_levels, section_params, p);
env = create_alias(env, n.append_after("_rec"), id_to_short_id.find(n)->append_after("_rec"), section_levels, section_params, p);
for (intro_rule const & ir : inductive_decl_intros(d)) {
name const & n = intro_rule_name(ir);
env = create_alias(env, n, *id_to_short_id.find(n), section_levels, section_params, p);
/** \brief Return the universe level of the given type, if it is not a sort, then raise an exception. */
level get_datatype_result_level(expr d_type) {
d_type = m_tc->whnf(d_type);
while (is_pi(d_type)) {
d_type = m_tc->whnf(binding_body(d_type));
}
if (!is_sort(d_type))
throw_error(sstream() << "invalid inductive datatype, resultant type is not a sort");
return sort_level(d_type);
}
/** \brief Update the result sort of the given type */
expr update_result_sort(expr t, level const & l) {
t = m_tc->whnf(t);
if (is_pi(t)) {
return update_binding(t, binding_domain(t), update_result_sort(binding_body(t), l));
} else if (is_sort(t)) {
return update_sort(t, l);
} else {
lean_unreachable();
}
}
return env;
/** \brief Elaborate the type of an inductive declaration. */
std::tuple<expr, level_param_names> elaborate_inductive_type(expr d_type) {
level l = get_datatype_result_level(d_type);
if (is_placeholder(l)) {
if (m_using_explicit_levels)
throw_error("resultant universe must be provided, when using explicit universe levels");
d_type = update_result_sort(d_type, m_u);
m_infer_result_universe = true;
}
return m_p.elaborate(m_env, d_type);
}
/** \brief Create a local constant based on the given binding */
expr mk_local_for(expr const & b) {
return mk_local(m_p.mk_fresh_name(), binding_name(b), binding_domain(b), binding_info(b));
}
/** \brief Check if the parameters of \c d_type and \c first_d_type are equal. */
void check_params(expr d_type, expr first_d_type) {
for (unsigned i = 0; i < m_num_params; i++) {
if (!m_tc->is_def_eq(binding_domain(d_type), binding_domain(first_d_type)))
throw_error(sstream() << "invalid parameter #" << (i+1) << " in mutually recursive inductive declaration, "
<< "all inductive types must have equivalent parameters");
expr l = mk_local_for(d_type);
d_type = instantiate(binding_body(d_type), l);
first_d_type = instantiate(binding_body(first_d_type), l);
}
}
/** \brief Check if the level names in d_lvls and \c first_d_lvls are equal. */
void check_levels(level_param_names d_lvls, level_param_names first_d_lvls) {
while (!is_nil(d_lvls) && !is_nil(first_d_lvls)) {
if (head(d_lvls) != head(first_d_lvls))
throw_error(sstream() << "invalid mutually recursive inductive declaration, "
<< "all declarations must use the same universe parameter names, mismatch: '"
<< head(d_lvls) << "' and '" << head(first_d_lvls) << "' "
<< "(if the universe parameters were inferred, then provide them explicitly to fix the problem)");
d_lvls = tail(d_lvls);
first_d_lvls = tail(first_d_lvls);
}
if (!is_nil(d_lvls) || !is_nil(first_d_lvls))
throw_error("invalid mutually recursive inductive declaration, "
"all declarations must have the same number of arguments "
"(this is error is probably due to inferred implicit parameters, provide all parameters explicitly to fix the problem");
}
/** \brief Add the parameters of the inductive decl type to the local scoped.
This method is executed before parsing introduction rules.
*/
void add_params_to_local_scope(expr d_type, buffer<expr> & params) {
for (unsigned i = 0; i < m_num_params; i++) {
expr l = mk_local_for(d_type);
m_p.add_local(l);
params.push_back(l);
d_type = instantiate(binding_body(d_type), l);
}
}
/** \brief Parse introduction rules in the scope of the given parameters.
Introduction rules with the annotation '{}' are marked for relaxed (aka non-strict) implicit parameter inference.
*/
list<intro_rule> parse_intro_rules(buffer<expr> & params) {
buffer<intro_rule> intros;
while (m_p.curr_is_token(g_bar)) {
m_p.next();
name intro_name = parse_decl_name();
bool strict = true;
if (m_p.curr_is_token(g_lcurly)) {
m_p.next();
m_p.check_token_next(g_rcurly, "invalid introduction rule, '}' expected");
strict = false;
m_relaxed_implicit_infer.insert(intro_name);
}
m_p.check_token_next(g_colon, "invalid introduction rule, ':' expected");
expr intro_type = m_p.parse_scoped_expr(params, m_env);
intro_type = m_p.pi_abstract(params, intro_type);
intro_type = infer_implicit(intro_type, params.size(), strict);
intros.push_back(intro_rule(intro_name, intro_type));
}
return to_list(intros.begin(), intros.end());
}
void parse_inductive_decls(buffer<inductive_decl> & decls) {
optional<expr> first_d_type;
optional<level_param_names> first_d_lvls;
while (true) {
parser::local_scope l_scope(m_p);
name d_name = parse_decl_name();
parse_inductive_univ_params();
expr d_type = parse_datatype_type();
m_p.check_token_next(g_assign, "invalid inductive declaration, ':=' expected");
level_param_names d_lvls;
std::tie(d_type, d_lvls) = elaborate_inductive_type(d_type);
if (!m_first) {
check_params(d_type, *first_d_type);
check_levels(d_lvls, *first_d_lvls);
}
buffer<expr> params;
add_params_to_local_scope(d_type, params);
auto d_intro_rules = parse_intro_rules(params);
decls.push_back(inductive_decl(d_name, d_type, d_intro_rules));
if (!m_p.curr_is_token(g_with)) {
m_levels.append(m_explict_levels);
for (auto l : d_lvls) m_levels.push_back(l);
break;
}
m_p.next();
m_first = false;
first_d_type = d_type;
first_d_lvls = d_lvls;
}
}
/** \brief Include in m_levels any section level referenced by decls. */
void include_section_levels(buffer<inductive_decl> const & decls) {
if (!in_section(m_env))
return;
name_set all_lvl_params;
for (auto const & d : decls) {
all_lvl_params = collect_univ_params(inductive_decl_type(d), all_lvl_params);
for (auto const & ir : inductive_decl_intros(d)) {
all_lvl_params = collect_univ_params(intro_rule_type(ir), all_lvl_params);
}
}
buffer<name> section_lvls;
all_lvl_params.for_each([&](name const & l) {
if (std::find(m_levels.begin(), m_levels.end(), l) == m_levels.end())
section_lvls.push_back(l);
});
std::sort(section_lvls.begin(), section_lvls.end(), [&](name const & n1, name const & n2) {
return m_p.get_local_level_index(n1) < m_p.get_local_level_index(n2);
});
buffer<name> new_levels;
new_levels.append(section_lvls);
new_levels.append(m_levels);
m_levels.clear();
m_levels.append(new_levels);
}
/** \brief Collect section local parameters used in the inductive decls */
name_set collect_section_locals(buffer<inductive_decl> const & decls) {
name_set section_locals;
for (auto const & d : decls) {
section_locals = collect_locals(inductive_decl_type(d), section_locals);
for (auto const & ir : inductive_decl_intros(d)) {
section_locals = collect_locals(intro_rule_type(ir), section_locals);
}
}
return section_locals;
}
/** \brief Make sure that every occurrence of an inductive datatype (in decls) in \c type has
section parameters \c section_params as arguments.
*/
expr fix_inductive_occs(expr const & type, buffer<inductive_decl> const & decls, buffer<expr> const & section_params) {
return replace(type, [&](expr const & e, unsigned) {
if (!is_constant(e))
return none_expr();
if (!std::any_of(decls.begin(), decls.end(),
[&](inductive_decl const & d) { return const_name(e) == inductive_decl_name(d); }))
return none_expr();
// found target
expr r = mk_app(mk_explicit(e), section_params);
return some_expr(r);
});
}
/** \brief Include the used section parameters as additional arguments.
The section parameters are stored in section_params
*/
void abstract_section_locals(buffer<inductive_decl> & decls, buffer<expr> & section_params) {
if (!in_section(m_env))
return;
name_set section_locals = collect_section_locals(decls);
if (section_locals.empty())
return;
mk_section_params(section_locals, m_p, section_params);
// First, add section_params to inductive types type.
for (inductive_decl & d : decls) {
d = update_inductive_decl(d, m_p.pi_abstract(section_params, inductive_decl_type(d)));
}
// Add section_params to introduction rules type, and also "fix"
// occurrences of inductive types.
for (inductive_decl & d : decls) {
buffer<intro_rule> new_irs;
for (auto const & ir : inductive_decl_intros(d)) {
expr type = intro_rule_type(ir);
type = fix_inductive_occs(type, decls, section_params);
type = m_p.pi_abstract(section_params, type);
bool strict = m_relaxed_implicit_infer.contains(intro_rule_name(ir));
type = infer_implicit(type, section_params.size(), strict);
new_irs.push_back(update_intro_rule(ir, type));
}
d = update_inductive_decl(d, new_irs);
}
}
/** \brief Declare inductive types in the scratch environment as var_decls.
We use this trick to be able to elaborate the introduction rules.
*/
void declare_inductive_types(buffer<inductive_decl> & decls) {
level_param_names ls = to_list(m_levels.begin(), m_levels.end());
for (auto const & d : decls) {
name d_name; expr d_type;
std::tie(d_name, d_type, std::ignore) = d;
m_env = m_env.add(check(m_env, mk_var_decl(d_name, ls, d_type)));
}
m_tc = mk_type_checker_with_hints(m_env, m_p.mk_ngen());
}
/** \brief Traverse the introduction rule type and collect the universes where non-parameters reside in \c r_lvls.
This information is used to compute the resultant universe level for the inductive datatype declaration.
*/
void accumulate_levels(expr intro_type, buffer<level> & r_lvls) {
unsigned i = 0;
while (is_pi(intro_type)) {
if (i >= m_num_params) {
expr s = m_tc->ensure_type(binding_domain(intro_type));
level l = sort_level(s);
if (l == m_u) {
// ignore, this is the auxiliary level
} else if (occurs(m_u, l)) {
throw exception("failed to infer inductive datatype resultant universe, provide the universe levels explicitly");
} else if (std::find(r_lvls.begin(), r_lvls.end(), l) == r_lvls.end()) {
r_lvls.push_back(l);
}
}
intro_type = instantiate(binding_body(intro_type), mk_local_for(intro_type));
i++;
}
}
/** \brief Elaborate introduction rules and destructively update \c decls with the elaborated versions.
\remark This method is invoked only after all inductive datatype types have been elaborated and
inserted into the scratch environment m_env.
This method also store in r_lvls inferred levels that must be in the resultant universe.
*/
void elaborate_intro_rules(buffer<inductive_decl> & decls, buffer<level> & r_lvls) {
for (auto & d : decls) {
name d_name; expr d_type; list<intro_rule> d_intros;
std::tie(d_name, d_type, d_intros) = d;
buffer<intro_rule> new_irs;
for (auto const & ir : d_intros) {
name ir_name; expr ir_type;
std::tie(ir_name, ir_type) = ir;
level_param_names new_ls;
std::tie(ir_type, new_ls) = m_p.elaborate(m_env, ir_type);
for (auto l : new_ls) m_levels.push_back(l);
accumulate_levels(ir_type, r_lvls);
new_irs.push_back(intro_rule(ir_name, ir_type));
}
d = inductive_decl(d_name, d_type, to_list(new_irs.begin(), new_irs.end()));
}
}
/** \brief If old_num_univ_params < m_levels.size(), then new universe params were collected when elaborating
the introduction rules. This method include them in all occurrences of the inductive datatype decls.
*/
void include_extra_univ_levels(buffer<inductive_decl> & decls, unsigned old_num_univ_params) {
if (m_levels.size() == old_num_univ_params)
return;
buffer<level> tmp;
for (auto l : m_levels) tmp.push_back(mk_param_univ(l));
levels new_ls = to_list(tmp.begin(), tmp.end());
for (auto & d : decls) {
buffer<intro_rule> new_irs;
for (auto & ir : inductive_decl_intros(d)) {
expr new_type = replace(intro_rule_type(ir), [&](expr const & e, unsigned) {
if (!is_constant(e))
return none_expr();
if (!std::any_of(decls.begin(), decls.end(),
[&](inductive_decl const & d) { return const_name(e) == inductive_decl_name(d); }))
return none_expr();
// found target
expr r = update_constant(e, new_ls);
return some_expr(r);
});
new_irs.push_back(update_intro_rule(ir, new_type));
}
d = update_inductive_decl(d, new_irs);
}
}
/** \brief Create the resultant universe level using the levels computed during introduction rule elaboration */
level mk_result_level(buffer<level> const & r_lvls) {
bool impredicative = m_env.impredicative();
if (r_lvls.empty()) {
return impredicative ? mk_level_one() : mk_level_zero();
} else {
level r = r_lvls[0];
for (unsigned i = 1; i < r_lvls.size(); i++)
r = mk_max(r, r_lvls[i]);
if (is_not_zero(r))
return normalize(r);
else
return impredicative ? normalize(mk_max(r, mk_level_one())) : normalize(r);
}
}
/** \brief Update the resultant universe level of the inductive datatypes using the inferred universe \c r_lvl */
void update_resultant_universe(buffer<inductive_decl> & decls, level const & r_lvl) {
for (inductive_decl & d : decls) {
expr t = inductive_decl_type(d);
t = update_result_sort(t, r_lvl);
d = update_inductive_decl(d, t);
}
}
/** \brief Create an alias for the fully qualified name \c full_id. */
environment create_alias(environment const & env, name const & full_id, levels const & section_leves, buffer<expr> const & section_params) {
name id(full_id.get_string());
if (in_section(env)) {
expr r = mk_explicit(mk_constant(full_id, section_leves));
r = mk_app(r, section_params);
m_p.add_local_expr(id, r);
return env;
} else if (full_id != id) {
return add_alias(env, id, mk_constant(full_id));
} else {
return env;
}
}
/** \brief Add aliases for the inductive datatype, introduction and elimination rules */
environment add_aliases(environment env, level_param_names const & ls, buffer<expr> const & section_params,
buffer<inductive_decl> const & decls) {
// Create aliases/local refs
levels section_levels = collect_section_levels(ls, m_p);
for (auto & d : decls) {
name d_name = inductive_decl_name(d);
name d_short_name(d_name.get_string());
env = create_alias(env, d_name, section_levels, section_params);
env = create_alias(env, d_name.append_after("_rec"), section_levels, section_params);
for (intro_rule const & ir : inductive_decl_intros(d)) {
name ir_name = intro_rule_name(ir);
env = create_alias(env, ir_name, section_levels, section_params);
}
}
return env;
}
/** \brief Auxiliary method used for debugging */
void display(std::ostream & out, buffer<inductive_decl> const & decls) {
if (!m_levels.empty()) {
out << "inductive level params:";
for (auto l : m_levels) out << " " << l;
out << "\n";
}
for (auto const & d : decls) {
name d_name; expr d_type; list<intro_rule> d_irules;
std::tie(d_name, d_type, d_irules) = d;
out << "inductive " << d_name << " : " << d_type << "\n";
for (auto const & ir : d_irules) {
name ir_name; expr ir_type;
std::tie(ir_name, ir_type) = ir;
out << " | " << ir_name << " : " << ir_type << "\n";
}
}
out << "\n";
}
environment operator()() {
parser::no_undef_id_error_scope err_scope(m_p);
buffer<inductive_decl> decls;
parse_inductive_decls(decls);
include_section_levels(decls);
buffer<expr> section_params;
abstract_section_locals(decls, section_params);
m_num_params += section_params.size();
declare_inductive_types(decls);
unsigned num_univ_params = m_levels.size();
buffer<level> r_lvls;
elaborate_intro_rules(decls, r_lvls);
include_extra_univ_levels(decls, num_univ_params);
if (m_infer_result_universe) {
level r_lvl = mk_result_level(r_lvls);
update_resultant_universe(decls, r_lvl);
}
level_param_names ls = to_list(m_levels.begin(), m_levels.end());
environment env = module::add_inductive(m_p.env(), ls, m_num_params, to_list(decls.begin(), decls.end()));
return add_aliases(env, ls, section_params, decls);
}
};
environment inductive_cmd(parser & p) {
return inductive_cmd_fn(p)();
}
void register_inductive_cmd(cmd_table & r) {
add_cmd(r, cmd_info("inductive", "declare an inductive datatype", inductive_cmd));
}

View file

@ -57,20 +57,6 @@ parser::local_scope::~local_scope() {
m_p.m_env = m_env;
}
parser::param_universe_scope::param_universe_scope(parser & p):m_p(p), m_old(m_p.m_type_use_placeholder) {
m_p.m_type_use_placeholder = false;
}
parser::param_universe_scope::~param_universe_scope() {
m_p.m_type_use_placeholder = m_old;
}
parser::placeholder_universe_scope::placeholder_universe_scope(parser & p):m_p(p), m_old(m_p.m_type_use_placeholder) {
m_p.m_type_use_placeholder = true;
}
parser::placeholder_universe_scope::~placeholder_universe_scope() {
m_p.m_type_use_placeholder = m_old;
}
parser::no_undef_id_error_scope::no_undef_id_error_scope(parser & p):m_p(p), m_old(m_p.m_no_undef_id_error) {
m_p.m_no_undef_id_error = true;
}
@ -91,7 +77,6 @@ parser::parser(environment const & env, io_state const & ios,
m_pos_table(std::make_shared<pos_info_table>()) {
m_scanner.set_line(line);
m_num_threads = num_threads;
m_type_use_placeholder = true;
m_no_undef_id_error = false;
m_found_errors = false;
updt_options();
@ -314,14 +299,12 @@ expr parser::mk_app(std::initializer_list<expr> const & args, pos_info const & p
}
void parser::push_local_scope() {
if (m_type_use_placeholder)
m_local_level_decls.push();
m_local_level_decls.push();
m_local_decls.push();
}
void parser::pop_local_scope() {
if (m_type_use_placeholder)
m_local_level_decls.pop();
m_local_level_decls.pop();
m_local_decls.pop();
}
@ -481,35 +464,22 @@ level parser::parse_level(unsigned rbp) {
}
expr parser::mk_Type() {
if (m_type_use_placeholder) {
return mk_sort(mk_level_placeholder());
} else {
unsigned i = 1;
name l("l");
name r = l.append_after(i);
while (m_local_level_decls.contains(r) || m_env.is_universe(r)) {
i++;
r = l.append_after(i);
}
level lvl = mk_param_univ(r);
add_local_level(r, lvl);
return mk_sort(lvl);
}
return mk_sort(mk_level_placeholder());
}
expr parser::elaborate(expr const & e, bool check_unassigned) {
std::tuple<expr, level_param_names> parser::elaborate(expr const & e, bool check_unassigned) {
parser_pos_provider pp(m_pos_table, get_stream_name(), m_last_cmd_pos);
return ::lean::elaborate(m_env, m_ios, e, &pp, check_unassigned);
return ::lean::elaborate(m_env, m_local_level_decls, m_ios, e, &pp, check_unassigned);
}
expr parser::elaborate(environment const & env, expr const & e) {
std::tuple<expr, level_param_names> parser::elaborate(environment const & env, expr const & e) {
parser_pos_provider pp(m_pos_table, get_stream_name(), m_last_cmd_pos);
return ::lean::elaborate(env, m_ios, e, &pp);
return ::lean::elaborate(env, m_local_level_decls, m_ios, e, &pp);
}
std::pair<expr, expr> parser::elaborate(name const & n, expr const & t, expr const & v) {
std::tuple<expr, expr, level_param_names> parser::elaborate(name const & n, expr const & t, expr const & v) {
parser_pos_provider pp(m_pos_table, get_stream_name(), m_last_cmd_pos);
return ::lean::elaborate(m_env, m_ios, n, t, v, &pp);
return ::lean::elaborate(m_env, m_local_level_decls, m_ios, n, t, v, &pp);
}
[[ noreturn ]] void throw_invalid_open_binder(pos_info const & pos) {

View file

@ -56,10 +56,6 @@ class parser {
unsigned m_next_tag_idx;
bool m_found_errors;
pos_info_table_ptr m_pos_table;
// If m_type_use_placeholder is true, then the token Type is parsed as Type.{_}.
// if it is false, then it is parsed as Type.{l} where l is a fresh parameter,
// and is automatically inserted into m_local_level_decls.
bool m_type_use_placeholder;
// By default, when the parser finds a unknown identifier, it signs an error.
// When the following flag is true, it creates a constant.
// This flag is when we are trying to parse mutually recursive declarations.
@ -243,16 +239,6 @@ public:
unsigned get_local_index(name const & n) const;
/** \brief Return the local parameter named \c n */
expr const * get_local(name const & n) const { return m_local_decls.find(n); }
/**
\brief By default, \c mk_Type returns <tt>Type.{_}</tt> where '_' is a new placeholder.
This scope object allows us to temporarily change this behavior.
In any scope containing this object, \c mk_Type returns <tt>Type.{l}</tt>, where
\c l is a fresh universe level parameter.
The new parameter is automatically added to \c m_local_level_decls.
*/
struct param_universe_scope { parser & m_p; bool m_old; param_universe_scope(parser &); ~param_universe_scope(); };
/** \brief Switch back to <tt>Type.{_}</tt>, see \c param_universe_scope */
struct placeholder_universe_scope { parser & m_p; bool m_old; placeholder_universe_scope(parser &); ~placeholder_universe_scope(); };
expr mk_Type();
/**
@ -263,9 +249,9 @@ public:
*/
struct no_undef_id_error_scope { parser & m_p; bool m_old; no_undef_id_error_scope(parser &); ~no_undef_id_error_scope(); };
expr elaborate(expr const & e, bool check_unassigned = true);
expr elaborate(environment const & env, expr const & e);
std::pair<expr, expr> elaborate(name const & n, expr const & t, expr const & v);
std::tuple<expr, level_param_names> elaborate(expr const & e, bool check_unassigned = true);
std::tuple<expr, level_param_names> elaborate(environment const & env, expr const & e);
std::tuple<expr, expr, level_param_names> elaborate(name const & n, expr const & t, expr const & v);
/** parse all commands in the input stream */
bool operator()() { return parse_commands(); }

View file

@ -130,15 +130,10 @@ list<To> map2(list<From> const & l, F && f) {
if (is_nil(l)) {
return list<To>();
} else {
buffer<typename list<From>::cell*> tmp;
to_buffer(l, tmp);
unsigned i = tmp.size();
list<To> r;
while (i > 0) {
--i;
r = cons(f(tmp[i]->head()), r);
}
return r;
buffer<To> new_vs;
for (auto const & v : l)
new_vs.push_back(f(v));
return to_list(new_vs.begin(), new_vs.end());
}
}

76
tests/lean/run/basic.lean Normal file
View file

@ -0,0 +1,76 @@
variable A.{l1 l2} : Type.{l1} → Type.{l2}
check A
definition tst.{l} (A : Type) (B : Type) (C : Type.{l}) : Type := A → B → C
check tst
variable group.{l} : Type.{l+1}
variable carrier.{l} : group.{l} → Type.{l}
definition to_carrier (g : group) := carrier g
check to_carrier.{1}
section
parameter A : Type
check A
definition B := A → A
end
variable N : Type.{1}
check B N
variable f : B N
check f
variable a : N
check f a
section
parameter T1 : Type
parameter T2 : Type
parameter f : T1 → T2 → T2
definition double (a : T1) (b : T2) := f a (f a b)
end
check double
check double.{1 2}
definition Bool [inline] := Type.{0}
variable eq : Π {A : Type}, A → A → Bool
infix `=`:50 := eq
check eq.{1}
section
universe l
universe u
parameter {T1 : Type.{l}}
parameter {T2 : Type.{l}}
parameter {T3 : Type.{u}}
parameter f : T1 → T2 → T2
definition is_proj2 := ∀ x y, f x y = y
definition is_proj3 (f : T1 → T2 → T3 → T3) := ∀ x y z, f x y z = z
end
check @is_proj2.{1}
check @is_proj3.{1 2}
namespace foo
section
parameters {T1 T2 : Type}
parameter {T3 : Type}
parameter f : T1 → T2 → T2
definition is_proj2 := ∀ x y, f x y = y
definition is_proj3 (f : T1 → T2 → T3 → T3) := ∀ x y z, f x y z = z
end
check @foo.is_proj2.{1}
check @foo.is_proj3.{1 2}
end
namespace bla
section
parameter {T1 : Type}
parameter {T2 : Type}
parameter {T3 : Type}
parameter f : T1 → T2 → T2
definition is_proj2 := ∀ x y, f x y = y
definition is_proj3 (f : T1 → T2 → T3 → T3) := ∀ x y z, f x y z = z
end
check @bla.is_proj2.{1 2}
check @bla.is_proj3.{1 2 3}
end

View file

@ -24,10 +24,11 @@ inductive group : Type :=
definition carrier (g : group) : Type
:= group_rec (λ c s, c) g
-- TODO: improve elaborator and avoid the .{l} in this declaration
definition group_to_struct.{l} [instance] (g : group.{l}) : group_struct (carrier g)
definition group_to_struct [instance] (g : group) : group_struct (carrier g)
:= group_rec (λ (A : Type) (s : group_struct A), s) g
check group_struct
definition mul {A : Type} {s : group_struct A} : A → A → A
:= group_struct_rec (λ mul one inv h1 h2 h3, mul) s

View file

@ -0,0 +1,51 @@
inductive list (A : Type) : Type :=
| nil {} : list A
| cons : A → list A → list A
section
parameter A : Type
inductive list2 : Type :=
| nil2 {} : list2
| cons2 : A → list2 → list2
end
variable num : Type.{1}
namespace Tree
inductive tree (A : Type) : Type :=
| node : A → forest A → tree A
with forest (A : Type) : Type :=
| nil : forest A
| cons : tree A → forest A → forest A
end
inductive group_struct (A : Type) : Type :=
| mk_group_struct : (A → A → A) → A → group_struct A
inductive group : Type :=
| mk_group : Π (A : Type), (A → A → A) → A → group
section
parameter A : Type
parameter B : Type
inductive pair : Type :=
| mk_pair : A → B → pair
end
definition Bool [inline] := Type.{0}
inductive eq {A : Type} (a : A) : A → Bool :=
| refl : eq a a
section
parameter {A : Type}
inductive eq2 (a : A) : A → Bool :=
| refl2 : eq2 a a
end
section
parameter A : Type
parameter B : Type
inductive triple (C : Type) : Type :=
| mk_triple : A → B → C → triple C
end

View file

@ -0,0 +1,8 @@
import standard
inductive nat : Type :=
| zero : nat
| succ : nat → nat
definition is_zero (n : nat)
:= nat_rec true (λ n r, false) n