refactor(kernel/type_checker): combine type_checker and type_inferer into a single class, and avoid code duplication

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-12-22 11:51:38 -08:00
parent 9bac91f5ef
commit 4229e498d2
36 changed files with 617 additions and 845 deletions

View file

@ -11,13 +11,12 @@ function parse_lean_tpl(str, args, env, opts, fmt)
else
-- Create the string "fun (a::1 : type-of-args[1]) ... (a::n : type-of-args[n]), $str",
-- where n is the size of args
local inferer = type_inferer(env)
local tbl = {"fun"}
for i = 1, #args do
table.insert(tbl, " (a::")
table.insert(tbl, i)
table.insert(tbl, " : ")
table.insert(tbl, tostring(inferer(args[i])))
table.insert(tbl, tostring(env:infer_type(args[i])))
table.insert(tbl, ")")
end
table.insert(tbl, ", ")

View file

@ -424,7 +424,7 @@ struct lean_extension : public environment_extension {
}
void add_coercion(expr const & f, environment const & env) {
expr type = env->infer_type(f);
expr type = env->type_check(f);
expr norm_type = env->normalize(type);
if (!is_arrow(norm_type))
throw exception("invalid coercion declaration, a coercion must have an arrow type (i.e., a non-dependent functional type)");

View file

@ -17,7 +17,6 @@ Author: Leonardo de Moura
#include "kernel/unification_constraint.h"
#include "kernel/instantiate.h"
#include "kernel/builtin.h"
#include "library/type_inferer.h"
#include "library/placeholder.h"
#include "library/elaborator/elaborator.h"
#include "frontends/lean/frontend.h"
@ -127,7 +126,6 @@ inline justification mk_overload_justification(context const & ctx, expr const &
class frontend_elaborator::imp {
environment m_env;
type_checker m_type_checker;
type_inferer m_type_inferer;
normalizer m_normalizer;
metavar_env m_menv;
buffer<unification_constraint> m_ucs;
@ -168,7 +166,7 @@ class frontend_elaborator::imp {
*/
optional<expr> get_type(expr const & e, context const & ctx) {
try {
return some_expr(m_ref.m_type_inferer(e, ctx));
return some_expr(m_ref.m_type_checker.infer_type(e, ctx));
} catch (exception &) {
return none_expr();
}
@ -431,7 +429,6 @@ public:
imp(environment const & env):
m_env(env),
m_type_checker(m_env),
m_type_inferer(m_env),
m_normalizer(m_env) {
}
@ -441,7 +438,7 @@ public:
expr new_e = preprocessor(*this)(e);
// std::cout << "After preprocessing\n" << new_e << "\n";
if (has_metavar(new_e)) {
m_type_checker.infer_type(new_e, context(), m_menv, m_ucs);
m_type_checker.check(new_e, context(), m_menv, m_ucs);
// for (auto c : m_ucs) {
// formatter fmt = mk_simple_formatter();
// std::cout << c.pp(fmt, options(), nullptr, false) << "\n";
@ -460,8 +457,8 @@ public:
expr new_e = preprocessor(*this)(e);
// std::cout << "After preprocessing\n" << new_t << "\n" << new_e << "\n";
if (has_metavar(new_e) || has_metavar(new_t)) {
m_type_checker.infer_type(new_t, context(), m_menv, m_ucs);
expr new_e_t = m_type_checker.infer_type(new_e, context(), m_menv, m_ucs);
m_type_checker.check(new_t, context(), m_menv, m_ucs);
expr new_e_t = m_type_checker.check(new_e, context(), m_menv, m_ucs);
m_ucs.push_back(mk_convertible_constraint(context(), new_e_t, new_t,
mk_def_type_match_justification(context(), n, e)));
// for (auto c : m_ucs) {
@ -494,7 +491,6 @@ public:
m_ucs.clear();
m_trace.clear();
m_type_checker.clear();
m_type_inferer.clear();
m_normalizer.clear();
}

View file

@ -37,7 +37,6 @@ Author: Leonardo de Moura
#include "kernel/find_fn.h"
#include "kernel/type_checker_justification.h"
#include "library/expr_lt.h"
#include "library/type_inferer.h"
#include "library/arith/arith.h"
#include "library/io_state.h"
#include "library/placeholder.h"
@ -156,7 +155,6 @@ class parser::imp {
io_state m_io_state;
scanner m_scanner;
frontend_elaborator m_elaborator;
type_inferer m_type_inferer;
macros const * m_macros;
scanner::token m_curr;
bool m_use_exceptions;
@ -1379,7 +1377,7 @@ class parser::imp {
next();
expr pr = parse_expr();
check_rparen_next("invalid apply command, ')' expected");
expr pr_type = m_type_inferer(pr);
expr pr_type = m_env->infer_type(pr);
return ::lean::apply_tactic(pr, pr_type);
} else {
name n = check_identifier_next("invalid apply command, identifier, '(' expr ')', or 'script-block' expected");
@ -1589,7 +1587,7 @@ class parser::imp {
// Example: apply_tactic.
metavar_env menv = s.get_menv().copy();
buffer<unification_constraint> ucs;
expr pr_type = type_checker(m_env).infer_type(pr, ctx, menv, ucs);
expr pr_type = type_checker(m_env).check(pr, ctx, menv, ucs);
ucs.push_back(mk_convertible_constraint(ctx, pr_type, expected_type, mk_type_match_justification(ctx, expected_type, pr)));
elaborator elb(m_env, menv, ucs.size(), ucs.data(), m_io_state.get_options());
metavar_env new_menv = elb.next();
@ -1808,7 +1806,7 @@ class parser::imp {
if (has_metavar(mvar_type))
throw metavar_not_synthesized_exception(mvar_ctx, mvar, mvar_type,
"failed to synthesize metavar, its type contains metavariables");
if (!m_type_inferer.is_proposition(mvar_type, mvar_ctx))
if (!is_proposition(mvar_type, m_env, mvar_ctx))
throw metavar_not_synthesized_exception(mvar_ctx, mvar, mvar_type, "failed to synthesize metavar, its type is not a proposition");
proof_state s = to_proof_state(m_env, mvar_ctx, mvar_type);
std::pair<optional<tactic>, pos_info> hint_and_pos = get_tactic_for(mvar);
@ -2051,7 +2049,7 @@ class parser::imp {
auto p = m_elaborator(parse_expr());
check_no_metavar(p, "invalid expression, it still contains metavariables after elaboration");
expr v = p.first;
expr t = infer_type(v, m_env);
expr t = type_check(v, m_env);
formatter fmt = m_io_state.get_formatter();
options opts = m_io_state.get_options();
unsigned indent = get_pp_indent(opts);
@ -2308,7 +2306,6 @@ class parser::imp {
void reset_env(environment env) {
m_env = env;
m_elaborator.reset(env);
m_type_inferer.reset(env);
m_io_state.set_formatter(mk_pp_formatter(env));
}
@ -2427,7 +2424,6 @@ public:
m_io_state(st),
m_scanner(in),
m_elaborator(env),
m_type_inferer(env),
m_use_exceptions(use_exceptions),
m_interactive(interactive) {
m_script_state = S;

View file

@ -284,7 +284,7 @@ void environment_cell::check_no_cached_type(expr const & e) {
*/
void environment_cell::check_type(name const & n, expr const & t, expr const & v) {
m_type_checker->check_type(t);
expr v_t = m_type_checker->infer_type(v);
expr v_t = m_type_checker->check(v);
if (!m_type_checker->is_convertible(v_t, t))
throw def_type_mismatch_exception(env(), n, t, v, v_t);
}
@ -333,7 +333,7 @@ void environment_cell::add_definition(name const & n, expr const & t, expr const
void environment_cell::add_definition(name const & n, expr const & v, bool opaque) {
check_no_cached_type(v);
check_name(n);
expr v_t = m_type_checker->infer_type(v);
expr v_t = m_type_checker->check(v);
unsigned w = get_max_weight(v) + 1;
register_named_object(mk_definition(n, v_t, v, opaque, w));
}
@ -393,6 +393,10 @@ object const & environment_cell::get_object(unsigned i, bool local) const {
}
}
expr environment_cell::type_check(expr const & e, context const & ctx) const {
return m_type_checker->check(e, ctx);
}
expr environment_cell::infer_type(expr const & e, context const & ctx) const {
return m_type_checker->infer_type(e, ctx);
}

View file

@ -198,6 +198,11 @@ public:
/** \brief Return true iff the environment has an object with the given name */
bool has_object(name const & n) const { return static_cast<bool>(find_object(n)); }
/**
\brief Type check the given expression, and return the type of \c e in the given context and this environment.
*/
expr type_check(expr const & e, context const & ctx = context()) const;
/**
\brief Return the type of \c e in the given context and this environment.
*/

View file

@ -30,52 +30,15 @@ class type_checker::imp {
context m_ctx;
cached_metavar_env m_menv;
unification_constraints * m_uc;
bool m_infer_only;
ro_environment env() const {
return ro_environment(m_env);
}
expr lift_free_vars(expr const & e, unsigned s, unsigned d) {
return ::lean::lift_free_vars(e, s, d, m_menv.to_some_menv());
}
expr lift_free_vars(expr const & e, unsigned d) {
return ::lean::lift_free_vars(e, d, m_menv.to_some_menv());
}
expr instantiate_with_closed(expr const & e, expr const & v) {
return ::lean::instantiate_with_closed(e, v, m_menv.to_some_menv());
}
expr instantiate(expr const & e, expr const & v) {
return ::lean::instantiate(e, v, m_menv.to_some_menv());
}
expr normalize(expr const & e, context const & ctx, bool unfold_opaque) {
return m_normalizer(e, ctx, m_menv.to_some_menv(), unfold_opaque);
}
expr check_pi(expr const & e, expr const & s, context const & ctx) {
if (is_pi(e))
return e;
expr r = normalize(e, ctx, false);
if (is_pi(r))
return r;
if (has_metavar(r) && m_menv && m_uc) {
// Create two fresh variables A and B,
// and assign r == (Pi(x : A), B)
expr A = m_menv->mk_metavar(ctx);
expr B = m_menv->mk_metavar(extend(ctx, g_x_name, A));
expr p = mk_pi(g_x_name, A, B);
justification jst = mk_function_expected_justification(ctx, s);
m_uc->push_back(mk_eq_constraint(ctx, e, p, jst));
return p;
}
r = normalize(e, ctx, true);
if (is_pi(r))
return r;
throw function_expected_exception(env(), ctx, s);
}
ro_environment env() const { return ro_environment(m_env); }
expr lift_free_vars(expr const & e, unsigned s, unsigned d) { return ::lean::lift_free_vars(e, s, d, m_menv.to_some_menv()); }
expr lift_free_vars(expr const & e, unsigned d) { return ::lean::lift_free_vars(e, d, m_menv.to_some_menv()); }
expr lower_free_vars(expr const & e, unsigned s, unsigned n) { return ::lean::lower_free_vars(e, s, n, m_menv.to_some_menv()); }
expr instantiate_with_closed(expr const & e, expr const & v) { return ::lean::instantiate_with_closed(e, v, m_menv.to_some_menv()); }
expr instantiate(expr const & e, expr const & v) { return ::lean::instantiate(e, v, m_menv.to_some_menv()); }
expr normalize(expr const & e, context const & ctx, bool unfold_opaque) { return m_normalizer(e, ctx, m_menv.to_some_menv(), unfold_opaque); }
expr check_type(expr const & e, expr const & s, context const & ctx) {
if (is_type(e))
@ -100,6 +63,72 @@ class type_checker::imp {
throw type_expected_exception(env(), ctx, s);
}
expr check_pi(expr const & e, expr const & s, context const & ctx) {
if (is_pi(e))
return e;
expr r = normalize(e, ctx, false);
if (is_pi(r))
return r;
if (has_metavar(r) && m_menv && m_uc) {
// Create two fresh variables A and B,
// and assign r == (Pi(x : A), B)
expr A = m_menv->mk_metavar(ctx);
expr B = m_menv->mk_metavar(extend(ctx, g_x_name, A));
expr p = mk_pi(g_x_name, A, B);
justification jst = mk_function_expected_justification(ctx, s);
m_uc->push_back(mk_eq_constraint(ctx, e, p, jst));
return p;
}
r = normalize(e, ctx, true);
if (is_pi(r))
return r;
throw function_expected_exception(env(), ctx, s);
}
/**
\brief Given \c t (a Pi term), this method returns the body (aka range)
of the function space for the element e in the domain of the Pi.
*/
expr get_pi_body(expr const & t, expr const & e) {
lean_assert(is_pi(t));
if (is_arrow(t))
return lower_free_vars(abst_body(t), 1, 1);
else
return instantiate(abst_body(t), e);
}
expr get_range(expr t, expr const & e, context const & ctx) {
unsigned num = num_args(e);
for (unsigned i = 1; i < num; i++) {
expr const & a = arg(e, i);
if (is_pi(t)) {
t = get_pi_body(t, a);
} else {
t = normalize(t, ctx, false);
if (is_pi(t)) {
t = get_pi_body(t, a);
} else if (has_metavar(t) && m_menv && m_uc) {
// Create two fresh variables A and B,
// and assign r == (Pi(x : A), B)
expr A = m_menv->mk_metavar(ctx);
expr B = m_menv->mk_metavar(extend(ctx, g_x_name, A));
expr p = mk_pi(g_x_name, A, B);
justification jst = mk_function_expected_justification(ctx, e);
m_uc->push_back(mk_eq_constraint(ctx, t, p, jst));
t = get_pi_body(p, a);
} else {
t = normalize(t, ctx, true);
if (is_pi(t)) {
t = get_pi_body(t, a);
} else {
throw function_expected_exception(env(), ctx, e);
}
}
}
}
return t;
}
expr save_result(expr const & e, expr const & r, bool shared) {
if (shared)
m_cache[e] = r;
@ -108,6 +137,60 @@ class type_checker::imp {
expr infer_type_core(expr const & e, context const & ctx) {
check_system("type checker");
// cheap cases, we do not cache results
switch (e.kind()) {
case expr_kind::MetaVar:
if (m_menv) {
if (m_menv->is_assigned(e))
return infer_type_core(*(m_menv->get_subst(e)), ctx);
else
return m_menv->get_type(e);
} else {
throw unexpected_metavar_occurrence(env(), e);
}
case expr_kind::Constant: {
if (const_type(e)) {
return *const_type(e);
} else {
object const & obj = env()->get_object(const_name(e));
if (obj.has_type())
return obj.get_type();
else
throw has_no_type_exception(env(), e);
}
break;
}
case expr_kind::Var: {
auto const & entry = lookup(ctx, var_idx(e));
if (entry.get_domain())
return lift_free_vars(*(entry.get_domain()), var_idx(e) + 1);
// Remark: the case where ce.get_domain() is not
// available is not considered cheap.
break;
}
case expr_kind::Value:
if (m_infer_only) {
return to_value(e).get_type();
} else {
name const & n = to_value(e).get_name();
object obj = env()->get_object(n);
if ((obj.is_builtin() && obj.get_value() == e) || (obj.is_builtin_set() && obj.in_builtin_set(e))) {
return to_value(e).get_type();
} else {
throw invalid_builtin_value_reference(env(), e);
}
}
case expr_kind::Type:
return mk_type(ty_level(e) + 1);
case expr_kind::Eq:
// cheap when we are just inferring types
if (m_infer_only)
return mk_bool_type();
case expr_kind::App: case expr_kind::Lambda:
case expr_kind::Pi: case expr_kind::Let:
break; // expensive cases
}
bool shared = false;
if (is_shared(e)) {
shared = true;
@ -117,87 +200,69 @@ class type_checker::imp {
}
switch (e.kind()) {
case expr_kind::MetaVar:
if (m_menv) {
if (m_menv->is_assigned(e)) {
optional<expr> s = m_menv->get_subst(e);
lean_assert(s);
return infer_type_core(*s, ctx);
} else {
return m_menv->get_type(e);
}
} else {
throw unexpected_metavar_occurrence(env(), e);
}
case expr_kind::Constant: {
if (const_type(e)) {
return save_result(e, *const_type(e), shared);
} else {
object const & obj = env()->get_object(const_name(e));
if (obj.has_type())
return save_result(e, obj.get_type(), shared);
else
throw has_no_type_exception(env(), e);
}
}
case expr_kind::MetaVar: case expr_kind::Constant: case expr_kind::Type: case expr_kind::Value:
lean_unreachable(); // LCOV_EXCL_LINE;
case expr_kind::Var: {
unsigned i = var_idx(e);
auto p = lookup_ext(ctx, i);
context_entry const & def = p.first;
context const & def_ctx = p.second;
lean_assert(ctx.size() > def_ctx.size());
if (optional<expr> const & d = def.get_domain()) {
return save_result(e, lift_free_vars(*d, ctx.size() - def_ctx.size()), shared);
lean_assert(!def.get_domain()); // was handled as cheap
optional<expr> const & b = def.get_body();
lean_assert(b);
expr t = infer_type_core(*b, def_ctx);
return save_result(e, lift_free_vars(t, var_idx(e) + 1), shared);
}
case expr_kind::App:
if (m_infer_only) {
expr const & f = arg(e, 0);
expr f_t = infer_type_core(f, ctx);
return save_result(e, get_range(f_t, e, ctx), shared);
} else {
optional<expr> const & b = def.get_body();
lean_assert(b);
expr t = infer_type_core(*b, def_ctx);
return save_result(e, lift_free_vars(t, ctx.size() - def_ctx.size()), shared);
unsigned num = num_args(e);
lean_assert(num >= 2);
buffer<expr> arg_types;
for (unsigned i = 0; i < num; i++) {
arg_types.push_back(infer_type_core(arg(e, i), ctx));
}
expr f_t = check_pi(arg_types[0], e, ctx);
unsigned i = 1;
while (true) {
expr const & c = arg(e, i);
expr const & c_t = arg_types[i];
// thunk for creating justification object if needed
auto mk_justification = [&](){ return mk_app_type_match_justification(ctx, e, i); };
if (!is_convertible(c_t, abst_domain(f_t), ctx, mk_justification))
throw app_type_mismatch_exception(env(), ctx, e, arg_types.size(), arg_types.data());
if (closed(abst_body(f_t)))
f_t = abst_body(f_t);
else if (closed(c))
f_t = instantiate_with_closed(abst_body(f_t), c);
else
f_t = instantiate(abst_body(f_t), c);
i++;
if (i == num)
return save_result(e, f_t, shared);
f_t = check_pi(f_t, e, ctx);
}
}
}
case expr_kind::Type:
return save_result(e, mk_type(ty_level(e) + 1), shared);
case expr_kind::App: {
unsigned num = num_args(e);
lean_assert(num >= 2);
buffer<expr> arg_types;
for (unsigned i = 0; i < num; i++) {
arg_types.push_back(infer_type_core(arg(e, i), ctx));
}
expr f_t = check_pi(arg_types[0], e, ctx);
unsigned i = 1;
while (true) {
expr const & c = arg(e, i);
expr const & c_t = arg_types[i];
auto mk_justification = [&](){ return mk_app_type_match_justification(ctx, e, i); }; // thunk for creating justification object if needed
if (!is_convertible(c_t, abst_domain(f_t), ctx, mk_justification))
throw app_type_mismatch_exception(env(), ctx, e, arg_types.size(), arg_types.data());
if (closed(abst_body(f_t)))
f_t = abst_body(f_t);
else if (closed(c))
f_t = instantiate_with_closed(abst_body(f_t), c);
else
f_t = instantiate(abst_body(f_t), c);
i++;
if (i == num)
return save_result(e, f_t, shared);
f_t = check_pi(f_t, e, ctx);
}
}
case expr_kind::Eq:
lean_assert(!m_infer_only);
infer_type_core(eq_lhs(e), ctx);
infer_type_core(eq_rhs(e), ctx);
return save_result(e, mk_bool_type(), shared);
case expr_kind::Lambda: {
expr d = infer_type_core(abst_domain(e), ctx);
check_type(d, abst_domain(e), ctx);
case expr_kind::Lambda:
if (!m_infer_only) {
expr d = infer_type_core(abst_domain(e), ctx);
check_type(d, abst_domain(e), ctx);
}
{
freset<cache> reset(m_cache);
return save_result(e,
mk_pi(abst_name(e), abst_domain(e), infer_type_core(abst_body(e), extend(ctx, abst_name(e), abst_domain(e)))),
shared);
}
}
case expr_kind::Pi: {
expr t1 = check_type(infer_type_core(abst_domain(e), ctx), abst_domain(e), ctx);
optional<expr> t2;
@ -217,32 +282,29 @@ class type_checker::imp {
}
}
case expr_kind::Let: {
expr lt = infer_type_core(let_value(e), ctx);
if (let_type(e)) {
expr ty = infer_type_core(*let_type(e), ctx);
check_type(ty, *let_type(e), ctx); // check if it is really a type
// thunk for creating justification object if needed
auto mk_justification = [&](){ return mk_def_type_match_justification(ctx, let_name(e), let_value(e)); };
if (!is_convertible(lt, *let_type(e), ctx, mk_justification))
throw def_type_mismatch_exception(env(), ctx, let_name(e), *let_type(e), let_value(e), lt);
optional<expr> lt;
if (m_infer_only) {
lt = let_type(e);
} else {
if (let_type(e)) {
expr value_ty = infer_type_core(let_value(e), ctx);
expr ty = infer_type_core(*let_type(e), ctx);
check_type(ty, *let_type(e), ctx); // check if it is really a type
// thunk for creating justification object if needed
auto mk_justification = [&](){ return mk_def_type_match_justification(ctx, let_name(e), let_value(e)); };
if (!is_convertible(value_ty, *let_type(e), ctx, mk_justification))
throw def_type_mismatch_exception(env(), ctx, let_name(e), *let_type(e), let_value(e), value_ty);
lt = let_type(e);
} else {
lt = infer_type_core(let_value(e), ctx);
}
}
{
freset<cache> reset(m_cache);
expr t = infer_type_core(let_body(e), extend(ctx, let_name(e), lt, let_value(e)));
return save_result(e, instantiate(t, let_value(e)), shared);
}
}
case expr_kind::Value: {
// Check if the builtin value (or its set) is declared in the environment.
name const & n = to_value(e).get_name();
object obj = env()->get_object(n);
if ((obj.is_builtin() && obj.get_value() == e) || (obj.is_builtin_set() && obj.in_builtin_set(e))) {
return save_result(e, to_value(e).get_type(), shared);
} else {
throw invalid_builtin_value_reference(env(), e);
}
}
}
}}
lean_unreachable(); // LCOV_EXCL_LINE
}
@ -302,20 +364,46 @@ class type_checker::imp {
clear_cache();
}
struct set_infer_only {
imp & m_ref;
bool m_old_infer_only;
set_infer_only(imp & r, bool flag):m_ref(r), m_old_infer_only(m_ref.m_infer_only) {
if (m_ref.m_infer_only != flag)
m_ref.clear_cache();
m_ref.m_infer_only = flag;
}
~set_infer_only() {
if (m_ref.m_infer_only != m_old_infer_only)
m_ref.clear_cache();
m_ref.m_infer_only = m_old_infer_only;
}
};
public:
imp(ro_environment const & env):
imp(ro_environment const & env, bool infer_only):
m_env(env),
m_normalizer(env) {
m_uc = nullptr;
m_infer_only = infer_only;
}
expr infer_type(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
expr infer_check(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc,
bool infer_only) {
set_infer_only set(*this, infer_only);
set_ctx(ctx);
update_menv(menv);
flet<unification_constraints*> set_uc(m_uc, uc);
return infer_type_core(e, ctx);
}
expr infer_type(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
return infer_check(e, ctx, menv, uc, true);
}
expr check(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
return infer_check(e, ctx, menv, uc, false);
}
bool is_convertible(expr const & t1, expr const & t2, context const & ctx) {
set_ctx(ctx);
update_menv(none_menv());
@ -332,6 +420,20 @@ public:
check_type(t, e, ctx);
}
bool is_proposition(expr const & e, context const & ctx, optional<metavar_env> const & menv) {
// Catch easy cases
switch (e.kind()) {
case expr_kind::Lambda: case expr_kind::Pi: case expr_kind::Type: return false;
case expr_kind::Eq: return true;
default: break;
}
expr t = infer_type(e, ctx, menv, nullptr);
if (is_bool(t))
return true;
else
return is_bool(normalize(t, ctx, true));
}
void clear_cache() {
m_cache.clear();
m_normalizer.clear();
@ -348,7 +450,7 @@ public:
}
};
type_checker::type_checker(ro_environment const & env):m_ptr(new imp(env)) {}
type_checker::type_checker(ro_environment const & env, bool infer_only):m_ptr(new imp(env, infer_only)) {}
type_checker::~type_checker() {}
expr type_checker::infer_type(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
return m_ptr->infer_type(e, ctx, menv, uc);
@ -362,19 +464,42 @@ expr type_checker::infer_type(expr const & e, context const & ctx, metavar_env c
expr type_checker::infer_type(expr const & e, context const & ctx) {
return infer_type(e, ctx, none_menv(), nullptr);
}
expr type_checker::check(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
return m_ptr->check(e, ctx, menv, uc);
}
expr type_checker::check(expr const & e, context const & ctx, metavar_env const & menv, buffer<unification_constraint> & uc) {
return m_ptr->check(e, ctx, some_menv(menv), &uc);
}
expr type_checker::check(expr const & e, context const & ctx, metavar_env const & menv) {
return m_ptr->check(e, ctx, some_menv(menv), nullptr);
}
expr type_checker::check(expr const & e, context const & ctx) {
return check(e, ctx, none_menv(), nullptr);
}
bool type_checker::is_convertible(expr const & t1, expr const & t2, context const & ctx) {
return m_ptr->is_convertible(t1, t2, ctx);
}
void type_checker::check_type(expr const & e, context const & ctx) {
m_ptr->check_type(e, ctx);
}
bool type_checker::is_proposition(expr const & e, context const & ctx, optional<metavar_env> const & menv) {
return m_ptr->is_proposition(e, ctx, menv);
}
bool type_checker::is_proposition(expr const & e, context const & ctx) {
return is_proposition(e, ctx, none_menv());
}
bool type_checker::is_proposition(expr const & e, context const & ctx, metavar_env const & menv) {
return is_proposition(e, ctx, some_menv(menv));
}
void type_checker::clear() { m_ptr->clear(); }
normalizer & type_checker::get_normalizer() { return m_ptr->get_normalizer(); }
expr infer_type(expr const & e, ro_environment const & env, context const & ctx) {
return type_checker(env).infer_type(e, ctx);
expr type_check(expr const & e, ro_environment const & env, context const & ctx) {
return type_checker(env).check(e, ctx);
}
bool is_convertible(expr const & given, expr const & expected, ro_environment const & env, context const & ctx) {
return type_checker(env).is_convertible(given, expected, ctx);
}
bool is_proposition(expr const & e, ro_environment const & env, context const & ctx) {
return type_checker(env).is_proposition(e, ctx);
}
}

View file

@ -22,12 +22,16 @@ class type_checker {
class imp;
std::unique_ptr<imp> m_ptr;
public:
type_checker(ro_environment const & env);
type_checker(ro_environment const & env, bool infer_only = false);
~type_checker();
/**
\brief Return the type of \c e in the context \c ctx.
It does not check whether the input expression is type correct or not.
The contract is: IF the input expression is type correct, then the inferred
type is correct.
\remark This method throws an exception if \c e is not type correct.
\remark If \c menv is not none, then \c e may contain metavariables.
@ -46,8 +50,26 @@ public:
*/
expr infer_type(expr const & e, context const & ctx = context());
/** \brief Throw an exception if \c e is not type correct in the context \c ctx. */
void check(expr const & e, context const & ctx = context()) { infer_type(e, ctx); }
/**
\brief Type check the given expression, and return the type of \c e in the context \c ctx.
\remark This method throws an exception if \c e is not type correct.
\remark If \c menv is not none, then \c e may contain metavariables.
New metavariables and unification constraints may be created by the type checker.
The new unification constraints are stored in \c new_constraints.
*/
expr check(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * new_constraints);
expr check(expr const & e, context const & ctx, metavar_env const & menv, buffer<unification_constraint> & new_constraints);
expr check(expr const & e, context const & ctx, metavar_env const & menv);
/**
\brief Type check the given expression, and return the type of \c e in the context \c ctx.
\remark This method throws an exception if \c e is not type
correct, or if \c e contains metavariables.
*/
expr check(expr const & e, context const & ctx = context());
/** \brief Throw an exception if \c e is not a type in the context \c ctx. */
void check_type(expr const & e, context const & ctx = context());
@ -55,13 +77,33 @@ public:
/** \brief Return true iff \c t1 is convertible to \c t2 in the context \c ctx. */
bool is_convertible(expr const & t1, expr const & t2, context const & ctx = context());
bool is_proposition(expr const & e, context const & ctx, optional<metavar_env> const & menv);
bool is_proposition(expr const & e, context const & ctx, metavar_env const & menv);
bool is_proposition(expr const & e, context const & ctx = context());
/** \brief Reset internal caches */
void clear();
/** \brief Return reference to the normalizer used by this type checker. */
normalizer & get_normalizer();
};
expr infer_type(expr const & e, ro_environment const & env, context const & ctx = context());
class type_inferer : public type_checker {
public:
type_inferer(ro_environment const & env):type_checker(env, true) {}
expr operator()(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * new_constraints) {
return infer_type(e, ctx, menv, new_constraints);
}
expr operator()(expr const & e, context const & ctx, metavar_env const & menv, buffer<unification_constraint> & new_constraints) {
return infer_type(e, ctx, menv, new_constraints);
}
expr operator()(expr const & e, context const & ctx, metavar_env const & menv) {
return infer_type(e, ctx, menv);
}
expr operator()(expr const & e, context const & ctx = context()) {
return infer_type(e, ctx);
}
};
expr type_check(expr const & e, ro_environment const & env, context const & ctx = context());
bool is_convertible(expr const & t1, expr const & t2, ro_environment const & env, context const & ctx = context());
bool is_proposition(expr const & e, ro_environment const & env, context const & ctx = context());
}

View file

@ -1,6 +1,5 @@
add_library(library kernel_bindings.cpp basic_thms.cpp deep_copy.cpp
max_sharing.cpp context_to_lambda.cpp io_state.cpp type_inferer.cpp
placeholder.cpp expr_lt.cpp substitution.cpp
fo_unify.cpp)
max_sharing.cpp context_to_lambda.cpp io_state.cpp placeholder.cpp
expr_lt.cpp substitution.cpp fo_unify.cpp)
target_link_libraries(library ${LEAN_LIBS})

View file

@ -18,8 +18,8 @@ Author: Leonardo de Moura
#include "kernel/instantiate.h"
#include "kernel/replace_fn.h"
#include "kernel/builtin.h"
#include "kernel/type_checker.h"
#include "kernel/update_expr.h"
#include "library/type_inferer.h"
#include "library/elaborator/elaborator.h"
#include "library/elaborator/elaborator_justification.h"

View file

@ -18,10 +18,10 @@ Author: Leonardo de Moura
#include "kernel/instantiate.h"
#include "kernel/occurs.h"
#include "kernel/builtin.h"
#include "kernel/type_checker.h"
#include "library/expr_lt.h"
#include "library/kernel_bindings.h"
#include "library/io_state.h"
#include "library/type_inferer.h"
// Lua Bindings for the Kernel classes. We do not include the Lua
// bindings in the kernel because we do not want to inflate the Kernel.
@ -1045,13 +1045,13 @@ static int environment_has_object(lua_State * L) {
return 1;
}
static int environment_check_type(lua_State * L) {
static int environment_type_check(lua_State * L) {
ro_shared_environment env(L, 1);
int nargs = lua_gettop(L);
if (nargs == 2)
return push_expr(L, env->infer_type(to_expr(L, 2)));
return push_expr(L, env->type_check(to_expr(L, 2)));
else
return push_expr(L, env->infer_type(to_expr(L, 2), to_context(L, 3)));
return push_expr(L, env->type_check(to_expr(L, 2), to_context(L, 3)));
}
static int environment_normalize(lua_State * L) {
@ -1104,11 +1104,11 @@ static int environment_local_objects(lua_State * L) {
static int environment_infer_type(lua_State * L) {
int nargs = lua_gettop(L);
type_inferer inferer(to_environment(L, 1));
ro_shared_environment env(L, 1);
if (nargs == 2)
return push_expr(L, inferer(to_expr(L, 2)));
return push_expr(L, env->infer_type(to_expr(L, 2)));
else
return push_expr(L, inferer(to_expr(L, 2), to_context(L, 3)));
return push_expr(L, env->infer_type(to_expr(L, 2), to_context(L, 3)));
}
static int environment_tostring(lua_State * L) {
@ -1150,7 +1150,7 @@ static const struct luaL_Reg environment_m[] = {
{"add_axiom", safe_function<environment_add_axiom>},
{"find_object", safe_function<environment_find_object>},
{"has_object", safe_function<environment_has_object>},
{"check_type", safe_function<environment_check_type>},
{"type_check", safe_function<environment_type_check>},
{"infer_type", safe_function<environment_infer_type>},
{"normalize", safe_function<environment_normalize>},
{"objects", safe_function<environment_objects>},
@ -1626,6 +1626,50 @@ static void open_metavar_env(lua_State * L) {
SET_GLOBAL_FUN(instantiate_metavars, "instantiate_metavars");
}
constexpr char const * type_inferer_mt = "type_inferer";
type_inferer & to_type_inferer(lua_State * L, int i) { return *static_cast<type_inferer*>(luaL_checkudata(L, i, type_inferer_mt)); }
DECL_PRED(type_inferer)
DECL_GC(type_inferer)
static int type_inferer_call(lua_State * L) {
int nargs = lua_gettop(L);
type_inferer & inferer = to_type_inferer(L, 1);
if (nargs == 2)
return push_expr(L, inferer(to_expr(L, 2)));
else
return push_expr(L, inferer(to_expr(L, 2), to_context(L, 3)));
}
static int type_inferer_clear(lua_State * L) {
to_type_inferer(L, 1).clear();
return 0;
}
static int mk_type_inferer(lua_State * L) {
void * mem = lua_newuserdata(L, sizeof(type_inferer));
new (mem) type_inferer(to_environment(L, 1));
luaL_getmetatable(L, type_inferer_mt);
lua_setmetatable(L, -2);
return 1;
}
static const struct luaL_Reg type_inferer_m[] = {
{"__gc", type_inferer_gc}, // never throws
{"__call", safe_function<type_inferer_call>},
{"clear", safe_function<type_inferer_clear>},
{0, 0}
};
void open_type_inferer(lua_State * L) {
luaL_newmetatable(L, type_inferer_mt);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
setfuncs(L, type_inferer_m, 0);
SET_GLOBAL_FUN(mk_type_inferer, "type_inferer");
SET_GLOBAL_FUN(type_inferer_pred, "is_type_inferer");
}
void open_kernel_module(lua_State * L) {
open_level(L);
open_local_context(L);
@ -1636,5 +1680,6 @@ void open_kernel_module(lua_State * L) {
open_object(L);
open_justification(L);
open_metavar_env(L);
open_type_inferer(L);
}
}

View file

@ -8,7 +8,6 @@ Author: Leonardo de Moura
#include "util/script_state.h"
#include "library/kernel_bindings.h"
#include "library/io_state.h"
#include "library/type_inferer.h"
#include "library/substitution.h"
#include "library/fo_unify.h"
#include "library/placeholder.h"
@ -17,7 +16,6 @@ namespace lean {
inline void open_core_module(lua_State * L) {
open_kernel_module(L);
open_io_state(L);
open_type_inferer(L);
open_substitution(L);
open_fo_unify(L);
open_placeholder(L);

View file

@ -13,10 +13,10 @@
#include "kernel/expr.h"
#include "kernel/printer.h"
#include "kernel/replace_fn.h"
#include "kernel/type_checker.h"
#include "library/basic_thms.h"
#include "library/rewriter/fo_match.h"
#include "library/rewriter/rewriter.h"
#include "library/type_inferer.h"
#include "util/buffer.h"
#include "util/trace.h"

View file

@ -14,9 +14,9 @@ Author: Soonho Kong
#include "kernel/environment.h"
#include "kernel/expr.h"
#include "kernel/replace_fn.h"
#include "kernel/type_checker.h"
#include "library/basic_thms.h"
#include "library/rewriter/rewriter.h"
#include "library/type_inferer.h"
#include "util/exception.h"
#include "util/scoped_map.h"
// TODO(soonhok)

View file

@ -8,9 +8,9 @@ Author: Leonardo de Moura
#include <algorithm>
#include "kernel/environment.h"
#include "kernel/instantiate.h"
#include "kernel/type_checker.h"
#include "library/fo_unify.h"
#include "library/kernel_bindings.h"
#include "library/type_inferer.h"
#include "library/tactic/goal.h"
#include "library/tactic/proof_builder.h"
#include "library/tactic/proof_state.h"
@ -49,7 +49,7 @@ static optional<proof_state> apply_tactic(ro_environment const & env, proof_stat
// We may solve more than one goal.
// We store the solved goals using a list of pairs
// name, args. Where the 'name' is the name of the solved goal.
type_inferer inferer(env);
type_checker checker(env);
list<std::pair<name, arg_list>> proof_info;
for (auto const & p : s.get_goals()) {
check_interrupted();
@ -69,7 +69,7 @@ static optional<proof_state> apply_tactic(ro_environment const & env, proof_stat
l = cons(mk_pair(some_expr(mvar_sol), name()), l);
th_type_c = instantiate(abst_body(th_type_c), mvar_sol, new_menv);
} else {
if (inferer.is_proposition(abst_domain(th_type_c), context(), new_menv)) {
if (checker.is_proposition(abst_domain(th_type_c), context(), new_menv)) {
name new_gname(gname, new_goal_idx);
new_goal_idx++;
l = cons(mk_pair(none_expr(), new_gname), l);

View file

@ -12,8 +12,8 @@ Author: Leonardo de Moura
#include "kernel/for_each_fn.h"
#include "kernel/replace_fn.h"
#include "kernel/abstract.h"
#include "kernel/type_checker.h"
#include "library/kernel_bindings.h"
#include "library/type_inferer.h"
#include "library/tactic/goal.h"
namespace lean {

View file

@ -1,344 +0,0 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "util/flet.h"
#include "util/freset.h"
#include "util/interrupt.h"
#include "kernel/environment.h"
#include "kernel/expr_maps.h"
#include "kernel/normalizer.h"
#include "kernel/builtin.h"
#include "kernel/kernel_exception.h"
#include "kernel/type_checker_justification.h"
#include "kernel/instantiate.h"
#include "kernel/free_vars.h"
#include "kernel/metavar.h"
#include "library/kernel_bindings.h"
#include "library/type_inferer.h"
namespace lean {
static name g_x_name("x");
class type_inferer::imp {
typedef expr_map<expr> cache;
typedef buffer<unification_constraint> unification_constraints;
ro_environment m_env;
context m_ctx;
cached_metavar_env m_menv;
unification_constraints * m_uc;
normalizer m_normalizer;
cache m_cache;
expr normalize(expr const & e, context const & ctx, bool unfold_opaque) {
return m_normalizer(e, ctx, m_menv.to_some_menv(), unfold_opaque);
}
expr lift_free_vars(expr const & e, unsigned s, unsigned d) {
return ::lean::lift_free_vars(e, s, d, m_menv.to_some_menv());
}
expr lift_free_vars(expr const & e, unsigned d) {
return ::lean::lift_free_vars(e, d, m_menv.to_some_menv());
}
expr lower_free_vars(expr const & e, unsigned s, unsigned n) {
return ::lean::lower_free_vars(e, s, n, m_menv.to_some_menv());
}
expr instantiate(expr const & e, unsigned n, expr const * s) {
return ::lean::instantiate(e, n, s, m_menv.to_some_menv());
}
expr check_type(expr const & e, expr const & s, context const & ctx) {
if (is_type(e))
return e;
if (is_bool(e))
return Type();
expr u = normalize(e, ctx, false);
if (is_type(u))
return u;
if (is_bool(u))
return Type();
if (has_metavar(u) && m_menv && m_uc) {
justification jst = mk_type_expected_justification(ctx, s);
m_uc->push_back(mk_convertible_constraint(ctx, u, TypeU, jst));
return u;
}
u = normalize(e, ctx, true);
if (is_type(u))
return u;
if (is_bool(u))
return Type();
throw type_expected_exception(m_env, ctx, s);
}
/**
\brief Given \c t (a Pi term), this method returns the body (aka range)
of the function space for the element e in the domain of the Pi.
*/
expr get_pi_body(expr const & t, expr const & e) {
lean_assert(is_pi(t));
if (is_arrow(t))
return lower_free_vars(abst_body(t), 1, 1);
else
return instantiate(abst_body(t), 1, &e);
}
expr get_range(expr t, expr const & e, context const & ctx) {
unsigned num = num_args(e);
for (unsigned i = 1; i < num; i++) {
expr const & a = arg(e, i);
if (is_pi(t)) {
t = get_pi_body(t, a);
} else {
t = normalize(t, ctx, false);
if (is_pi(t)) {
t = get_pi_body(t, a);
} else if (has_metavar(t) && m_menv && m_uc) {
// Create two fresh variables A and B,
// and assign r == (Pi(x : A), B)
expr A = m_menv->mk_metavar(ctx);
expr B = m_menv->mk_metavar(extend(ctx, g_x_name, A));
expr p = mk_pi(g_x_name, A, B);
justification jst = mk_function_expected_justification(ctx, e);
m_uc->push_back(mk_eq_constraint(ctx, t, p, jst));
t = get_pi_body(p, a);
} else {
t = normalize(t, ctx, true);
if (is_pi(t)) {
t = get_pi_body(t, a);
} else {
throw function_expected_exception(m_env, ctx, e);
}
}
}
}
return t;
}
expr infer_type(expr const & e, context const & ctx) {
// cheap cases, we do not cache results
switch (e.kind()) {
case expr_kind::MetaVar:
if (m_menv) {
if (m_menv->is_assigned(e))
return infer_type(*(m_menv->get_subst(e)), ctx);
else
return m_menv->get_type(e);
} else {
throw unexpected_metavar_occurrence(m_env, e);
}
case expr_kind::Constant: {
if (const_type(e)) {
return *const_type(e);
} else {
object const & obj = m_env->get_object(const_name(e));
if (obj.has_type())
return obj.get_type();
else
throw has_no_type_exception(m_env, e);
}
break;
}
case expr_kind::Var: {
auto p = lookup_ext(ctx, var_idx(e));
context_entry const & ce = p.first;
if (ce.get_domain()) {
context const & ce_ctx = p.second;
return lift_free_vars(*(ce.get_domain()), ctx.size() - ce_ctx.size());
}
// Remark: the case where ce.get_domain() is not
// available is not considered cheap.
break;
}
case expr_kind::Eq:
return mk_bool_type();
case expr_kind::Value:
return to_value(e).get_type();
case expr_kind::Type:
return mk_type(ty_level(e) + 1);
case expr_kind::App: case expr_kind::Lambda:
case expr_kind::Pi: case expr_kind::Let:
break; // expensive cases
}
check_system("type inference");
bool shared = false;
if (is_shared(e)) {
shared = true;
auto it = m_cache.find(e);
if (it != m_cache.end())
return it->second;
}
expr r;
switch (e.kind()) {
case expr_kind::Constant: case expr_kind::Eq:
case expr_kind::Value: case expr_kind::Type:
case expr_kind::MetaVar:
lean_unreachable(); // LCOV_EXCL_LINE
case expr_kind::Var: {
auto p = lookup_ext(ctx, var_idx(e));
context_entry const & ce = p.first;
context const & ce_ctx = p.second;
lean_assert(!ce.get_domain());
r = lift_free_vars(infer_type(*(ce.get_body()), ce_ctx), ctx.size() - ce_ctx.size());
break;
}
case expr_kind::App: {
expr const & f = arg(e, 0);
expr f_t = infer_type(f, ctx);
r = get_range(f_t, e, ctx);
break;
}
case expr_kind::Lambda: {
freset<cache> reset(m_cache);
r = mk_pi(abst_name(e), abst_domain(e), infer_type(abst_body(e), extend(ctx, abst_name(e), abst_domain(e))));
break;
}
case expr_kind::Pi: {
expr t1 = check_type(infer_type(abst_domain(e), ctx), abst_domain(e), ctx);
expr t2;
context new_ctx = extend(ctx, abst_name(e), abst_domain(e));
{
freset<cache> reset(m_cache);
t2 = check_type(infer_type(abst_body(e), new_ctx), abst_body(e), new_ctx);
}
if (is_type(t1) && is_type(t2)) {
r = mk_type(max(ty_level(t1), ty_level(t2)));
} else {
lean_assert(m_uc);
justification jst = mk_max_type_justification(ctx, e);
r = m_menv->mk_metavar(ctx);
m_uc->push_back(mk_max_constraint(new_ctx, lift_free_vars(t1, 0, 1), t2, r, jst));
}
break;
}
case expr_kind::Let: {
freset<cache> reset(m_cache);
r = infer_type(let_body(e), extend(ctx, let_name(e), let_type(e), let_value(e)));
break;
}}
if (shared) {
m_cache[e] = r;
}
return r;
}
void set_ctx(context const & ctx) {
if (!is_eqp(m_ctx, ctx)) {
clear();
m_ctx = ctx;
}
}
public:
imp(ro_environment const & env):
m_env(env),
m_normalizer(env) {
m_uc = nullptr;
}
expr operator()(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
set_ctx(ctx);
if (m_menv.update(menv))
clear_cache();
flet<unification_constraints*> set(m_uc, uc);
return infer_type(e, ctx);
}
void clear_cache() {
m_cache.clear();
m_normalizer.clear();
}
void clear() {
clear_cache();
m_menv.clear();
m_ctx = context();
}
bool is_proposition(expr const & e, context const & ctx, optional<metavar_env> const & menv) {
// Catch easy cases
switch (e.kind()) {
case expr_kind::Lambda: case expr_kind::Pi: case expr_kind::Type: return false;
case expr_kind::Eq: return true;
default: break;
}
expr t = operator()(e, ctx, menv, nullptr);
if (is_bool(t))
return true;
else
return is_bool(normalize(t, ctx, true));
}
};
type_inferer::type_inferer(ro_environment const & env):m_ptr(new imp(env)) {}
type_inferer::~type_inferer() {}
void type_inferer::reset(ro_environment const & env) { m_ptr.reset(new imp(env)); }
expr type_inferer::operator()(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc) {
return m_ptr->operator()(e, ctx, menv, uc);
}
expr type_inferer::operator()(expr const & e, context const & ctx, metavar_env const & menv, buffer<unification_constraint> & uc) {
return m_ptr->operator()(e, ctx, some_menv(menv), &uc);
}
expr type_inferer::operator()(expr const & e, context const & ctx) {
return operator()(e, ctx, none_menv(), nullptr);
}
bool type_inferer::is_proposition(expr const & e, context const & ctx, optional<metavar_env> const & menv) {
return m_ptr->is_proposition(e, ctx, menv);
}
bool type_inferer::is_proposition(expr const & e, context const & ctx) {
return is_proposition(e, ctx, none_menv());
}
bool type_inferer::is_proposition(expr const & e, context const & ctx, metavar_env const & menv) {
return is_proposition(e, ctx, some_menv(menv));
}
void type_inferer::clear() { m_ptr->clear(); }
constexpr char const * type_inferer_mt = "type_inferer";
type_inferer & to_type_inferer(lua_State * L, int i) { return *static_cast<type_inferer*>(luaL_checkudata(L, i, type_inferer_mt)); }
DECL_PRED(type_inferer)
DECL_GC(type_inferer)
static int type_inferer_call(lua_State * L) {
int nargs = lua_gettop(L);
type_inferer & inferer = to_type_inferer(L, 1);
if (nargs == 2)
return push_expr(L, inferer(to_expr(L, 2)));
else
return push_expr(L, inferer(to_expr(L, 2), to_context(L, 3)));
}
static int type_inferer_clear(lua_State * L) {
to_type_inferer(L, 1).clear();
return 0;
}
static int mk_type_inferer(lua_State * L) {
void * mem = lua_newuserdata(L, sizeof(type_inferer));
new (mem) type_inferer(to_environment(L, 1));
luaL_getmetatable(L, type_inferer_mt);
lua_setmetatable(L, -2);
return 1;
}
static const struct luaL_Reg type_inferer_m[] = {
{"__gc", type_inferer_gc}, // never throws
{"__call", safe_function<type_inferer_call>},
{"clear", safe_function<type_inferer_clear>},
{0, 0}
};
void open_type_inferer(lua_State * L) {
luaL_newmetatable(L, type_inferer_mt);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
setfuncs(L, type_inferer_m, 0);
SET_GLOBAL_FUN(mk_type_inferer, "type_inferer");
SET_GLOBAL_FUN(type_inferer_pred, "is_type_inferer");
}
}

View file

@ -1,46 +0,0 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <memory>
#include "util/buffer.h"
#include "util/optional.h"
#include "kernel/expr.h"
#include "kernel/context.h"
#include "kernel/unification_constraint.h"
namespace lean {
class environment;
class metavar_env;
/**
\brief Functional object for "quickly" inferring the type of expressions.
It does not check whether the input expression is type correct or not.
The contract is: IF the input expression is type correct, then the inferred
type is correct.
\remark The exceptions produced by this class are not informative.
That is, they are not meant for external users, but to sign that the
type could not be inferred.
*/
class type_inferer {
class imp;
std::unique_ptr<imp> m_ptr;
public:
type_inferer(ro_environment const & env);
~type_inferer();
expr operator()(expr const & e, context const & ctx, optional<metavar_env> const & menv, buffer<unification_constraint> * uc);
expr operator()(expr const & e, context const & ctx, metavar_env const & menv, buffer<unification_constraint> & uc);
expr operator()(expr const & e, context const & ctx = context());
bool is_proposition(expr const & e, context const & ctx, optional<metavar_env> const & menv);
bool is_proposition(expr const & e, context const & ctx, metavar_env const & menv);
bool is_proposition(expr const & e, context const & ctx = context());
void clear();
void reset(ro_environment const & env);
};
void open_type_inferer(lua_State * L);
}

View file

@ -129,7 +129,7 @@ static void tst5() {
environment env = mk_toplevel();
env->add_definition("a", Int, iVal(1), true); // add opaque definition
try {
std::cout << infer_type(iAdd(Const("a"), Int), env) << "\n";
std::cout << type_check(iAdd(Const("a"), Int), env) << "\n";
lean_unreachable();
} catch (exception const & ex) {
std::cout << "expected error: " << ex.what() << "\n";
@ -142,25 +142,25 @@ static void tst6() {
level w = env->add_uvar("w", u + 1);
env->add_var("f", mk_arrow(Type(u), Type(u)));
expr t = Const("f")(Int);
std::cout << "type of " << t << " is " << infer_type(t, env) << "\n";
std::cout << "type of " << t << " is " << type_check(t, env) << "\n";
try {
infer_type(Const("f")(Type(w)), env);
type_check(Const("f")(Type(w)), env);
lean_unreachable();
} catch (exception const & ex) {
std::cout << "expected error: " << ex.what() << "\n";
}
try {
infer_type(Const("f")(Type(u)), env);
type_check(Const("f")(Type(u)), env);
lean_unreachable();
} catch (exception const & ex) {
std::cout << "expected error: " << ex.what() << "\n";
}
t = Const("f")(Type());
std::cout << "type of " << t << " is " << infer_type(t, env) << "\n";
std::cout << infer_type(mk_arrow(Type(u), Type(w)), env) << "\n";
lean_assert(infer_type(mk_arrow(Type(u), Type(w)), env) == Type(max(u+1, w+1)));
std::cout << infer_type(mk_arrow(Int, Int), env) << "\n";
lean_assert(infer_type(mk_arrow(Int, Int), env) == Type());
std::cout << "type of " << t << " is " << type_check(t, env) << "\n";
std::cout << type_check(mk_arrow(Type(u), Type(w)), env) << "\n";
lean_assert(type_check(mk_arrow(Type(u), Type(w)), env) == Type(max(u+1, w+1)));
std::cout << type_check(mk_arrow(Int, Int), env) << "\n";
lean_assert(type_check(mk_arrow(Int, Int), env) == Type());
}
static void tst7() {
@ -169,7 +169,7 @@ static void tst7() {
env->add_var("b", Int);
expr t = If(Int, True, Const("a"), Const("b"));
std::cout << t << " --> " << normalize(t, env) << "\n";
std::cout << infer_type(t, env) << "\n";
std::cout << type_check(t, env) << "\n";
std::cout << "Environment\n" << env;
}

View file

@ -494,7 +494,7 @@ static void tst23() {
buffer<unification_constraint> up;
std::cout << F1 << "\n";
try {
std::cout << checker.infer_type(F1, context(), menv, up) << "\n";
std::cout << checker.check(F1, context(), menv, up) << "\n";
} catch (kernel_exception & ex) {
formatter fmt = mk_simple_formatter();
io_state st(options(), fmt);
@ -528,7 +528,7 @@ static void tst25() {
env->add_var("b", N);
expr m = menv->mk_metavar();
expr F = m(a, b);
std::cout << checker.infer_type(F, context(), menv, up) << "\n";
std::cout << checker.check(F, context(), menv, up) << "\n";
std::cout << menv << "\n";
std::cout << up << "\n";
}
@ -574,7 +574,7 @@ static void tst26() {
expr A4 = menv->mk_metavar();
expr F = cons(A1, m1(a), cons(A2, m2(n), cons(A3, m3(b), nil(A4))));
std::cout << F << "\n";
std::cout << checker.infer_type(F, context(), menv, up) << "\n";
std::cout << checker.check(F, context(), menv, up) << "\n";
std::cout << menv << "\n";
std::cout << up << "\n";
}
@ -608,7 +608,7 @@ static void tst27() {
expr m2 = menv->mk_metavar(context({{"x", T1}, {"y", T2}}));
expr F = Fun({{x, T1}, {y, T2}}, f(A1, x, y))(m1(a), m2(b));
std::cout << F << "\n";
std::cout << checker.infer_type(F, context(), menv, up) << "\n";
std::cout << checker.check(F, context(), menv, up) << "\n";
std::cout << menv << "\n";
std::cout << up << "\n";
}

View file

@ -11,6 +11,7 @@ Author: Leonardo de Moura
#include "util/trace.h"
#include "util/exception.h"
#include "util/interrupt.h"
#include "util/timeit.h"
#include "kernel/type_checker.h"
#include "kernel/environment.h"
#include "kernel/abstract.h"
@ -30,29 +31,29 @@ expr c(char const * n) { return mk_constant(n); }
static void tst1() {
environment env;
expr t0 = Type();
std::cout << infer_type(t0, env) << "\n";
lean_assert(infer_type(t0, env) == Type(level()+1));
std::cout << type_check(t0, env) << "\n";
lean_assert(type_check(t0, env) == Type(level()+1));
expr f = mk_pi("_", t0, t0);
std::cout << infer_type(f, env) << "\n";
lean_assert(infer_type(f, env) == Type(level()+1));
std::cout << type_check(f, env) << "\n";
lean_assert(type_check(f, env) == Type(level()+1));
level u = env->add_uvar("u", level() + 1);
level v = env->add_uvar("v", level() + 1);
expr g = mk_pi("_", Type(u), Type(v));
std::cout << infer_type(g, env) << "\n";
lean_assert(infer_type(g, env) == Type(max(u+1, v+1)));
std::cout << infer_type(Type(u), env) << "\n";
lean_assert(infer_type(Type(u), env) == Type(u+1));
std::cout << infer_type(mk_lambda("x", Type(u), Var(0)), env) << "\n";
lean_assert(infer_type(mk_lambda("x", Type(u), Var(0)), env) == mk_pi("_", Type(u), Type(u)));
std::cout << infer_type(mk_lambda("Nat", Type(), mk_lambda("n", Var(0), Var(0))), env) << "\n";
std::cout << type_check(g, env) << "\n";
lean_assert(type_check(g, env) == Type(max(u+1, v+1)));
std::cout << type_check(Type(u), env) << "\n";
lean_assert(type_check(Type(u), env) == Type(u+1));
std::cout << type_check(mk_lambda("x", Type(u), Var(0)), env) << "\n";
lean_assert(type_check(mk_lambda("x", Type(u), Var(0)), env) == mk_pi("_", Type(u), Type(u)));
std::cout << type_check(mk_lambda("Nat", Type(), mk_lambda("n", Var(0), Var(0))), env) << "\n";
expr nat = c("nat");
expr T = Fun("nat", Type(),
Fun("+", mk_arrow(nat, mk_arrow(nat, nat)),
Fun("m", nat, mk_app({c("+"), c("m"), c("m")}))));
std::cout << T << "\n";
std::cout << infer_type(T, env) << "\n";
std::cout << type_check(T, env) << "\n";
std::cout << Pi("nat", Type(), mk_arrow(mk_arrow(nat, mk_arrow(nat, nat)), mk_arrow(nat, nat))) << "\n";
lean_assert(infer_type(T, env) == Pi("nat", Type(), mk_arrow(mk_arrow(nat, mk_arrow(nat, nat)), mk_arrow(nat, nat))));
lean_assert(type_check(T, env) == Pi("nat", Type(), mk_arrow(mk_arrow(nat, mk_arrow(nat, nat)), mk_arrow(nat, nat))));
}
static void tst2() {
@ -69,7 +70,7 @@ static void tst2() {
Fun("v", mk_app({c("Vec"), c("n")}),
mk_app({c("len"), c("v")}))))));
std::cout << F << "\n";
std::cout << infer_type(F, env) << "\n";
std::cout << type_check(F, env) << "\n";
}
catch (exception & ex) {
std::cout << "Error: " << ex.what() << "\n";
@ -79,17 +80,17 @@ static void tst2() {
static void tst3() {
environment env = mk_toplevel();
expr f = Fun("a", Bool, Eq(Const("a"), True));
std::cout << infer_type(f, env) << "\n";
lean_assert(infer_type(f, env) == mk_arrow(Bool, Bool));
std::cout << type_check(f, env) << "\n";
lean_assert(type_check(f, env) == mk_arrow(Bool, Bool));
expr t = mk_let("a", none_expr(), True, Var(0));
std::cout << infer_type(t, env) << "\n";
std::cout << type_check(t, env) << "\n";
}
static void tst4() {
environment env = mk_toplevel();
expr a = Eq(iVal(1), iVal(2));
expr pr = mk_lambda("x", a, Var(0));
std::cout << infer_type(pr, env) << "\n";
std::cout << type_check(pr, env) << "\n";
}
static void tst5() {
@ -105,7 +106,7 @@ static void tst5() {
prop = And(P, prop);
}
expr impPr = Discharge(P, prop, Fun({H, P}, pr));
expr prop2 = infer_type(impPr, env);
expr prop2 = type_check(impPr, env);
lean_assert(Implies(P, prop) == prop2);
}
@ -116,7 +117,7 @@ static void tst6() {
expr x = Const("x");
expr t = Fun({A, Type()}, Fun({f, mk_arrow(Int, A)}, Fun({x, Int}, f(x, x))));
try {
infer_type(t, env);
type_check(t, env);
lean_unreachable();
} catch (exception & ex) {
std::cout << "Error: " << ex.what() << "\n";
@ -130,7 +131,7 @@ static void tst7() {
expr x = Const("x");
expr t = Fun({A, Type()}, Fun({f, mk_arrow(Int, mk_arrow(A, mk_arrow(A, mk_arrow(A, mk_arrow(A, mk_arrow(A, A))))))}, Fun({x, Int}, f(x, x))));
try {
infer_type(t, env);
type_check(t, env);
lean_unreachable();
} catch (exception & ex) {
std::cout << "Error: " << ex.what() << "\n";
@ -149,7 +150,7 @@ static void tst8() {
c = extend(c, "x", Bool);
expr t = P(Const("x"), Var(0));
try {
infer_type(t, env, c);
type_check(t, env, c);
lean_unreachable();
} catch (exception & ex) {
std::cout << "Error: " << ex.what() << "\n";
@ -168,7 +169,7 @@ static void tst9() {
c = extend(c, "x", Bool);
expr t = P(Const("x"), Var(0));
try {
infer_type(t, env, c);
type_check(t, env, c);
lean_unreachable();
} catch (exception & ex) {
std::cout << "Error: " << ex.what() << "\n";
@ -229,7 +230,7 @@ static void tst12() {
chrono::milliseconds dura(100);
interruptible_thread thread([&]() {
try {
std::cout << checker.infer_type(t) << "\n";
std::cout << checker.check(t) << "\n";
// Remark: if the following code is reached, we
// should decrease dura.
lean_unreachable();
@ -247,8 +248,8 @@ static void tst13() {
environment env = mk_toplevel();
env->add_var("f", Type() >> Type());
expr f = Const("f");
std::cout << infer_type(f(Bool), env) << "\n";
std::cout << infer_type(f(Eq(True, False)), env) << "\n";
std::cout << type_check(f(Bool), env) << "\n";
std::cout << type_check(f(Eq(True, False)), env) << "\n";
}
static void tst14() {
@ -263,7 +264,7 @@ static void tst14() {
formatter fmt = mk_simple_formatter();
io_state st(options(), fmt);
try {
std::cout << checker.infer_type(F) << "\n";
std::cout << checker.check(F) << "\n";
} catch (kernel_exception & ex) {
regular(st) << ex << "\n";
}
@ -285,16 +286,16 @@ static void tst15() {
expr F = Var(0)(Var(1));
expr F_copy = F;
type_checker checker(env);
std::cout << checker.infer_type(F, ctx1) << "\n";
lean_assert_eq(checker.infer_type(F, ctx1), vec1(Var(1), Int));
lean_assert_eq(checker.infer_type(F, ctx2), vec2(Var(1), Real));
lean_assert(is_eqp(checker.infer_type(F, ctx2), checker.infer_type(F, ctx2)));
lean_assert(is_eqp(checker.infer_type(F, ctx1), checker.infer_type(F, ctx1)));
expr r = checker.infer_type(F, ctx1);
std::cout << checker.check(F, ctx1) << "\n";
lean_assert_eq(checker.check(F, ctx1), vec1(Var(1), Int));
lean_assert_eq(checker.check(F, ctx2), vec2(Var(1), Real));
lean_assert(is_eqp(checker.check(F, ctx2), checker.check(F, ctx2)));
lean_assert(is_eqp(checker.check(F, ctx1), checker.check(F, ctx1)));
expr r = checker.check(F, ctx1);
checker.clear();
lean_assert(!is_eqp(r, checker.infer_type(F, ctx1)));
r = checker.infer_type(F, ctx1);
lean_assert(is_eqp(r, checker.infer_type(F, ctx1)));
lean_assert(!is_eqp(r, checker.check(F, ctx1)));
r = checker.check(F, ctx1);
lean_assert(is_eqp(r, checker.check(F, ctx1)));
}
static void check_justification_msg(justification const & t, char const * expected) {
@ -326,14 +327,14 @@ static void tst16() {
static void f1(type_checker & tc, expr const & F) {
metavar_env menv;
expr m1 = menv->mk_metavar(context(), some_expr(Bool >> Int));
expr r = tc.infer_type(F, context(), menv);
expr r = tc.check(F, context(), menv);
lean_assert_eq(r, Int);
}
static void f2(type_checker & tc, expr const & F) {
metavar_env menv;
expr m1 = menv->mk_metavar(context(), some_expr(Bool >> Bool));
expr r = tc.infer_type(F, context(), menv);
expr r = tc.check(F, context(), menv);
lean_assert_eq(r, Bool);
}
@ -353,6 +354,128 @@ static void tst17() {
f2(tc, F);
}
static std::ostream & operator<<(std::ostream & out, buffer<unification_constraint> const & uc) {
formatter fmt = mk_simple_formatter();
for (auto c : uc) {
out << c.pp(fmt, options(), nullptr, true) << "\n";
}
return out;
}
static void tst18() {
environment env = mk_toplevel();
type_inferer type_of(env);
expr f = Const("f");
expr g = Const("g");
expr a = Const("a");
expr b = Const("b");
expr A = Const("A");
env->add_var("f", Int >> (Int >> Int));
lean_assert(type_of(f(a, a)) == Int);
lean_assert(type_of(f(a)) == Int >> Int);
lean_assert(is_bool(type_of(And(a, f(a)))));
lean_assert(type_of(Fun({a, Int}, iAdd(a, iVal(1)))) == Int >> Int);
lean_assert(type_of(Let({a, iVal(10)}, iAdd(a, b))) == Int);
lean_assert(type_of(Type()) == Type(level() + 1));
lean_assert(type_of(Bool) == Type());
lean_assert(type_of(Pi({a, Type()}, Type(level() + 2))) == Type(level() + 3));
lean_assert(type_of(Pi({a, Type(level()+4)}, Type(level() + 2))) == Type(level() + 5));
lean_assert(type_of(Pi({a, Int}, Bool)) == Type());
env->add_var("g", Pi({A, Type()}, A >> A));
lean_assert(type_of(g(Int, a)) == Int);
lean_assert(type_of(g(Fun({a, Type()}, a)(Int), a)) == Fun({a, Type()}, a)(Int));
}
static expr mk_big(unsigned val, unsigned depth) {
if (depth == 0)
return iVal(val);
else
return iAdd(mk_big(val*2, depth-1), mk_big(val*2 + 1, depth-1));
}
static void tst19() {
environment env = mk_toplevel();
type_inferer type_of(env);
type_checker type_of_slow(env);
expr t = mk_big(0, 10);
{
timeit timer(std::cout, "light checker 10000 calls");
for (unsigned i = 0; i < 10000; i++) {
type_of(t);
type_of.clear();
}
}
{
timeit timer(std::cout, "type checker 300 calls");
for (unsigned i = 0; i < 300; i++) {
type_of_slow.check(t);
type_of_slow.clear();
}
}
}
static void tst20() {
environment env;
import_all(env);
context ctx1, ctx2;
expr A = Const("A");
expr vec1 = Const("vec1");
expr vec2 = Const("vec2");
env->add_var("vec1", Int >> (Type() >> Type()));
env->add_var("vec2", Real >> (Type() >> Type()));
ctx1 = extend(ctx1, "x", Int, iVal(1));
ctx1 = extend(ctx1, "f", Pi({A, Int}, vec1(A, Int)));
ctx2 = extend(ctx2, "x", Real, rVal(2));
ctx2 = extend(ctx2, "f", Pi({A, Real}, vec2(A, Real)));
expr F = Var(0)(Var(1));
expr F_copy = F;
type_inferer infer(env);
std::cout << infer(F, ctx1) << "\n";
lean_assert_eq(infer(F, ctx1), vec1(Var(1), Int));
lean_assert_eq(infer(F, ctx2), vec2(Var(1), Real));
lean_assert(is_eqp(infer(F, ctx2), infer(F, ctx2)));
lean_assert(is_eqp(infer(F, ctx1), infer(F, ctx1)));
expr r = infer(F, ctx1);
infer.clear();
lean_assert(!is_eqp(r, infer(F, ctx1)));
r = infer(F, ctx1);
lean_assert(is_eqp(r, infer(F, ctx1)));
}
static void tst21() {
environment env;
import_all(env);
metavar_env menv;
buffer<unification_constraint> uc;
type_inferer inferer(env);
expr list = Const("list");
expr nil = Const("nil");
expr cons = Const("cons");
expr A = Const("A");
env->add_var("list", Type() >> Type());
env->add_var("nil", Pi({A, Type()}, list(A)));
env->add_var("cons", Pi({A, Type()}, A >> (list(A) >> list(A))));
env->add_var("a", Int);
env->add_var("b", Int);
env->add_var("n", Nat);
env->add_var("m", Nat);
expr a = Const("a");
expr b = Const("b");
expr n = Const("n");
expr m = Const("m");
expr m1 = menv->mk_metavar();
expr m2 = menv->mk_metavar();
expr m3 = menv->mk_metavar();
expr A1 = menv->mk_metavar();
expr A2 = menv->mk_metavar();
expr A3 = menv->mk_metavar();
expr A4 = menv->mk_metavar();
expr F = cons(A1, m1(a), cons(A2, m2(n), cons(A3, m3(b), nil(A4))));
std::cout << F << "\n";
std::cout << inferer(F, context(), menv, uc) << "\n";
std::cout << uc << "\n";
}
int main() {
save_stack_info();
tst1();
@ -372,5 +495,9 @@ int main() {
tst15();
tst16();
tst17();
tst18();
tst19();
tst20();
tst21();
return has_violations() ? 1 : 0;
}

View file

@ -1,6 +1,3 @@
add_executable(type_inferer type_inferer.cpp)
target_link_libraries(type_inferer ${EXTRA_LIBS})
add_test(type_inferer ${CMAKE_CURRENT_BINARY_DIR}/type_inferer)
add_executable(formatter formatter.cpp)
target_link_libraries(formatter ${EXTRA_LIBS})
add_test(formatter ${CMAKE_CURRENT_BINARY_DIR}/formatter)

View file

@ -51,7 +51,7 @@ static void tst1() {
import_all(env);
expr e = mk_int_value(mpz(10));
lean_assert(is_int_value(e));
lean_assert(infer_type(e, env) == Int);
lean_assert(type_check(e, env) == Int);
std::cout << "e: " << e << "\n";
}
@ -62,13 +62,13 @@ static void tst2() {
std::cout << e << "\n";
std::cout << normalize(e, env) << "\n";
lean_assert(normalize(e, env) == iVal(40));
std::cout << infer_type(mk_int_add_fn(), env) << "\n";
lean_assert(infer_type(e, env) == Int);
lean_assert(infer_type(mk_app(mk_int_add_fn(), iVal(10)), env) == (Int >> Int));
std::cout << type_check(mk_int_add_fn(), env) << "\n";
lean_assert(type_check(e, env) == Int);
lean_assert(type_check(mk_app(mk_int_add_fn(), iVal(10)), env) == (Int >> Int));
lean_assert(is_int_value(normalize(e, env)));
expr e2 = Fun("a", Int, iAdd(Const("a"), iAdd(iVal(10), iVal(30))));
std::cout << e2 << " --> " << normalize(e2, env) << "\n";
lean_assert(infer_type(e2, env) == mk_arrow(Int, Int));
lean_assert(type_check(e2, env) == mk_arrow(Int, Int));
lean_assert(normalize(e2, env) == Fun("a", Int, iAdd(Const("a"), iVal(40))));
}
@ -79,13 +79,13 @@ static void tst3() {
std::cout << e << "\n";
std::cout << normalize(e, env) << "\n";
lean_assert(normalize(e, env) == iVal(300));
std::cout << infer_type(mk_int_mul_fn(), env) << "\n";
lean_assert(infer_type(e, env) == Int);
lean_assert(infer_type(mk_app(mk_int_mul_fn(), iVal(10)), env) == mk_arrow(Int, Int));
std::cout << type_check(mk_int_mul_fn(), env) << "\n";
lean_assert(type_check(e, env) == Int);
lean_assert(type_check(mk_app(mk_int_mul_fn(), iVal(10)), env) == mk_arrow(Int, Int));
lean_assert(is_int_value(normalize(e, env)));
expr e2 = Fun("a", Int, iMul(Const("a"), iMul(iVal(10), iVal(30))));
std::cout << e2 << " --> " << normalize(e2, env) << "\n";
lean_assert(infer_type(e2, env) == (Int >> Int));
lean_assert(type_check(e2, env) == (Int >> Int));
lean_assert(normalize(e2, env) == Fun("a", Int, iMul(Const("a"), iVal(300))));
}
@ -96,13 +96,13 @@ static void tst4() {
std::cout << e << "\n";
std::cout << normalize(e, env) << "\n";
lean_assert(normalize(e, env, context(), true) == iVal(-20));
std::cout << infer_type(mk_int_sub_fn(), env) << "\n";
lean_assert(infer_type(e, env) == Int);
lean_assert(infer_type(mk_app(mk_int_sub_fn(), iVal(10)), env) == mk_arrow(Int, Int));
std::cout << type_check(mk_int_sub_fn(), env) << "\n";
lean_assert(type_check(e, env) == Int);
lean_assert(type_check(mk_app(mk_int_sub_fn(), iVal(10)), env) == mk_arrow(Int, Int));
lean_assert(is_int_value(normalize(e, env, context(), true)));
expr e2 = Fun("a", Int, iSub(Const("a"), iSub(iVal(10), iVal(30))));
std::cout << e2 << " --> " << normalize(e2, env) << "\n";
lean_assert(infer_type(e2, env) == (Int >> Int));
lean_assert(type_check(e2, env) == (Int >> Int));
lean_assert_eq(normalize(e2, env, context(), true), Fun("a", Int, iAdd(Const("a"), iVal(20))));
}

View file

@ -48,7 +48,7 @@ static void tst1() {
expr A4 = menv->mk_metavar();
expr F = cons(A1, m1(a), cons(A2, m2(n), cons(A3, m3(b), nil(A4))));
std::cout << F << "\n";
std::cout << checker.infer_type(F, context(), menv, ucs) << "\n";
std::cout << checker.check(F, context(), menv, ucs) << "\n";
expr int_id = Fun({a, Int}, a);
expr nat_id = Fun({a, Nat}, a);
ucs.push_back(mk_choice_constraint(context(), m1, { int_id, mk_int_to_real_fn() }, justification()));
@ -92,7 +92,7 @@ static void tst2() {
expr nat_id = Fun({a, Nat}, a);
expr F = m1(g(m2, m3(a)), m4(nVal(0)));
std::cout << F << "\n";
std::cout << checker.infer_type(F, context(), menv, ucs) << "\n";
std::cout << checker.check(F, context(), menv, ucs) << "\n";
ucs.push_back(mk_choice_constraint(context(), m1, { mk_nat_le_fn(), mk_int_le_fn(), mk_real_le_fn() }, justification()));
ucs.push_back(mk_choice_constraint(context(), m3, { int_id, mk_int_to_real_fn() }, justification()));
ucs.push_back(mk_choice_constraint(context(), m4, { nat_id, mk_nat_to_int_fn(), mk_nat_to_real_fn() }, justification()));
@ -135,7 +135,7 @@ static void tst3() {
expr x = Const("x");
expr F = Fun({x, m1}, m2(f(m3, x), m4(nVal(10))))(m5(a));
std::cout << F << "\n";
std::cout << checker.infer_type(F, context(), menv, ucs) << "\n";
std::cout << checker.check(F, context(), menv, ucs) << "\n";
ucs.push_back(mk_choice_constraint(context(), m2, { mk_nat_le_fn(), mk_int_le_fn(), mk_real_le_fn() }, justification()));
ucs.push_back(mk_choice_constraint(context(), m4, { nat_id, mk_nat_to_int_fn(), mk_nat_to_real_fn() }, justification()));
ucs.push_back(mk_choice_constraint(context(), m5, { int_id, mk_int_to_real_fn() }, justification()));
@ -180,7 +180,7 @@ static void tst4() {
expr int_id = Fun({a, Int}, a);
expr F = Fun({{x, m1}, {y, m2}}, m3(f(m4, x), f(m5, y)))(m6(a), b);
std::cout << F << "\n";
std::cout << checker.infer_type(F, context(), menv, ucs) << "\n";
std::cout << checker.check(F, context(), menv, ucs) << "\n";
ucs.push_back(mk_choice_constraint(context(), m3, { mk_nat_le_fn(), mk_int_le_fn(), mk_real_le_fn() }, justification()));
ucs.push_back(mk_choice_constraint(context(), m6, { int_id, mk_int_to_real_fn() }, justification()));
elaborator elb(env, menv, ucs.size(), ucs.data());
@ -222,7 +222,7 @@ static void tst5() {
expr int_id = Fun({a, Int}, a);
expr F = Fun({{x, m1}, {y, m2}}, f(m3, x, y))(m4(a), b);
std::cout << F << "\n";
std::cout << checker.infer_type(F, context(), menv, ucs) << "\n";
std::cout << checker.check(F, context(), menv, ucs) << "\n";
ucs.push_back(mk_choice_constraint(context(), m4, { int_id, mk_int_to_real_fn() }, justification()));
elaborator elb(env, menv, ucs.size(), ucs.data());
elb.next();
@ -259,7 +259,7 @@ static void tst6() {
env->add_axiom("H2", Eq(a, b));
expr V = Subst(m1, m2, m3, m4, H1, H2);
expr expected = Eq(f(a, f(b, b)), a);
expr given = checker.infer_type(V, context(), menv, ucs);
expr given = checker.check(V, context(), menv, ucs);
ucs.push_back(mk_eq_constraint(context(), expected, given, justification()));
elaborator elb(env, menv, ucs.size(), ucs.data());
metavar_env s = elb.next();
@ -273,7 +273,7 @@ static expr elaborate(expr const & e, environment const & env) {
buffer<unification_constraint> ucs;
type_checker checker(env);
expr e2 = replace_placeholders_with_metavars(e, menv);
checker.infer_type(e2, context(), menv, ucs);
checker.check(e2, context(), menv, ucs);
elaborator elb(env, menv, ucs.size(), ucs.data());
metavar_env s = elb.next();
return s->instantiate_metavars(e2);
@ -831,7 +831,7 @@ void tst26() {
expr m1 = menv->mk_metavar();
expr F = Eq(g(m1, a), a);
std::cout << F << "\n";
std::cout << checker.infer_type(F, context(), menv, ucs) << "\n";
std::cout << checker.check(F, context(), menv, ucs) << "\n";
elaborator elb(env, menv, ucs.size(), ucs.data());
metavar_env s = elb.next();
std::cout << s->instantiate_metavars(F) << "\n";
@ -866,7 +866,7 @@ void tst27() {
expr m3 = menv->mk_metavar();
expr F = Fun({f, m1}, eq(m2, g(m3, f)(a), a));
std::cout << F << "\n";
std::cout << checker.infer_type(F, context(), menv, ucs) << "\n";
std::cout << checker.check(F, context(), menv, ucs) << "\n";
elaborator elb(env, menv, ucs.size(), ucs.data());
metavar_env s = elb.next();
std::cout << s->instantiate_metavars(F) << "\n";

View file

@ -140,7 +140,7 @@ static void tst2() {
// Print proof
std::cout << pr << "\n";
// Check whether the proof is correct or not.
std::cout << env->infer_type(pr) << "\n";
std::cout << env->type_check(pr) << "\n";
}
int main() {

View file

@ -1,147 +0,0 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "util/test.h"
#include "util/timeit.h"
#include "kernel/type_checker.h"
#include "kernel/environment.h"
#include "kernel/abstract.h"
#include "kernel/printer.h"
#include "library/type_inferer.h"
#include "library/arith/arith.h"
#include "library/all/all.h"
using namespace lean;
static std::ostream & operator<<(std::ostream & out, buffer<unification_constraint> const & uc) {
formatter fmt = mk_simple_formatter();
for (auto c : uc) {
out << c.pp(fmt, options(), nullptr, true) << "\n";
}
return out;
}
static void tst1() {
environment env = mk_toplevel();
type_inferer type_of(env);
expr f = Const("f");
expr g = Const("g");
expr a = Const("a");
expr b = Const("b");
expr A = Const("A");
env->add_var("f", Int >> (Int >> Int));
lean_assert(type_of(f(a, a)) == Int);
lean_assert(type_of(f(a)) == Int >> Int);
lean_assert(is_bool(type_of(And(a, f(a)))));
lean_assert(type_of(Fun({a, Int}, iAdd(a, iVal(1)))) == Int >> Int);
lean_assert(type_of(Let({a, iVal(10)}, iAdd(a, b))) == Int);
lean_assert(type_of(Type()) == Type(level() + 1));
lean_assert(type_of(Bool) == Type());
lean_assert(type_of(Pi({a, Type()}, Type(level() + 2))) == Type(level() + 3));
lean_assert(type_of(Pi({a, Type(level()+4)}, Type(level() + 2))) == Type(level() + 5));
lean_assert(type_of(Pi({a, Int}, Bool)) == Type());
env->add_var("g", Pi({A, Type()}, A >> A));
lean_assert(type_of(g(Int, a)) == Int);
lean_assert(type_of(g(Fun({a, Type()}, a)(Int), a)) == Fun({a, Type()}, a)(Int));
}
static expr mk_big(unsigned val, unsigned depth) {
if (depth == 0)
return iVal(val);
else
return iAdd(mk_big(val*2, depth-1), mk_big(val*2 + 1, depth-1));
}
static void tst2() {
environment env = mk_toplevel();
type_inferer type_of(env);
type_checker type_of_slow(env);
expr t = mk_big(0, 10);
{
timeit timer(std::cout, "light checker 10000 calls");
for (unsigned i = 0; i < 10000; i++) {
type_of(t);
type_of.clear();
}
}
{
timeit timer(std::cout, "type checker 300 calls");
for (unsigned i = 0; i < 300; i++) {
type_of_slow.infer_type(t);
type_of_slow.clear();
}
}
}
static void tst3() {
environment env;
import_all(env);
context ctx1, ctx2;
expr A = Const("A");
expr vec1 = Const("vec1");
expr vec2 = Const("vec2");
env->add_var("vec1", Int >> (Type() >> Type()));
env->add_var("vec2", Real >> (Type() >> Type()));
ctx1 = extend(ctx1, "x", Int, iVal(1));
ctx1 = extend(ctx1, "f", Pi({A, Int}, vec1(A, Int)));
ctx2 = extend(ctx2, "x", Real, rVal(2));
ctx2 = extend(ctx2, "f", Pi({A, Real}, vec2(A, Real)));
expr F = Var(0)(Var(1));
expr F_copy = F;
type_inferer infer(env);
std::cout << infer(F, ctx1) << "\n";
lean_assert_eq(infer(F, ctx1), vec1(Var(1), Int));
lean_assert_eq(infer(F, ctx2), vec2(Var(1), Real));
lean_assert(is_eqp(infer(F, ctx2), infer(F, ctx2)));
lean_assert(is_eqp(infer(F, ctx1), infer(F, ctx1)));
expr r = infer(F, ctx1);
infer.clear();
lean_assert(!is_eqp(r, infer(F, ctx1)));
r = infer(F, ctx1);
lean_assert(is_eqp(r, infer(F, ctx1)));
}
static void tst4() {
environment env;
import_all(env);
metavar_env menv;
buffer<unification_constraint> uc;
type_inferer inferer(env);
expr list = Const("list");
expr nil = Const("nil");
expr cons = Const("cons");
expr A = Const("A");
env->add_var("list", Type() >> Type());
env->add_var("nil", Pi({A, Type()}, list(A)));
env->add_var("cons", Pi({A, Type()}, A >> (list(A) >> list(A))));
env->add_var("a", Int);
env->add_var("b", Int);
env->add_var("n", Nat);
env->add_var("m", Nat);
expr a = Const("a");
expr b = Const("b");
expr n = Const("n");
expr m = Const("m");
expr m1 = menv->mk_metavar();
expr m2 = menv->mk_metavar();
expr m3 = menv->mk_metavar();
expr A1 = menv->mk_metavar();
expr A2 = menv->mk_metavar();
expr A3 = menv->mk_metavar();
expr A4 = menv->mk_metavar();
expr F = cons(A1, m1(a), cons(A2, m2(n), cons(A3, m3(b), nil(A4))));
std::cout << F << "\n";
std::cout << inferer(F, context(), menv, uc) << "\n";
std::cout << uc << "\n";
}
int main() {
save_stack_info();
tst1();
tst2();
tst3();
tst4();
return has_violations() ? 1 : 0;
}

View file

@ -6,42 +6,18 @@ let b := , a : := b in a
let a := 10, v1 := const a , v2 := v1 in v2 : vector Bool 10
let a := 10, v1 : vector Bool a := const a , v2 : vector Bool a := v1 in v2
let a := 10, v1 : vector Bool a := const a , v2 : vector Bool a := v1 in v2 : vector Bool 10
Failed to solve
a : := 10, v1 : vector ?M::0 a := const a ⊢ Bool ≈
Substitution
a : := 10, v1 : vector ?M::0 a := const a ⊢ ?M::0[lift:0:1] ≈
Destruct/Decompose
a : := 10, v1 : vector ?M::0 a := const a ⊢ vector (?M::0[lift:0:1]) a ≺ vector a
(line: 31: pos: 26) Type of definition 'v2' must be convertible to expected type.
Assignment
a : := 10 ⊢ ?M::0 ≈ Bool
Destruct/Decompose
a : := 10 ⊢ vector ?M::0 a ≺ vector Bool a
(line: 30: pos: 26) Type of definition 'v1' must be convertible to expected type.
Error (line: 31, pos: 26) type mismatch at definition 'v2', expected type
vector a
Given type:
vector Bool a
Assumed: foo
Coercion foo
Failed to solve
a : := 10, v1 : vector ?M::0 a := const a ⊢ Bool ≈
Substitution
a : := 10, v1 : vector ?M::0 a := const a ⊢ ?M::0[lift:0:1] ≈
Destruct/Decompose
a : := 10, v1 : vector ?M::0 a := const a ⊢ vector (?M::0[lift:0:1]) a ≺ vector a
(line: 40: pos: 26) Type of definition 'v2' must be convertible to expected type.
Assignment
a : := 10 ⊢ ?M::0 ≈ Bool
Destruct/Decompose
a : := 10 ⊢ vector ?M::0 a ≺ vector Bool a
(line: 39: pos: 26) Type of definition 'v1' must be convertible to expected type.
Error (line: 40, pos: 26) type mismatch at definition 'v2', expected type
vector a
Given type:
vector Bool a
Set: lean::pp::coercion
Failed to solve
a : := 10, v1 : vector ?M::0 a := const a ⊢ Bool ≈
Substitution
a : := 10, v1 : vector ?M::0 a := const a ⊢ ?M::0[lift:0:1] ≈
Destruct/Decompose
a : := 10, v1 : vector ?M::0 a := const a ⊢ vector (?M::0[lift:0:1]) a ≺ vector a
(line: 48: pos: 26) Type of definition 'v2' must be convertible to expected type.
Assignment
a : := 10 ⊢ ?M::0 ≈ Bool
Destruct/Decompose
a : := 10 ⊢ vector ?M::0 a ≺ vector Bool a
(line: 47: pos: 26) Type of definition 'v1' must be convertible to expected type.
Error (line: 48, pos: 26) type mismatch at definition 'v2', expected type
vector a
Given type:
vector Bool a

View file

@ -6,7 +6,7 @@ Variables x y z : Int
local plus = Const{"Int", "add"}
local x, y = Consts("x y")
local def = plus(plus(x, y), iVal(1000))
print(def, ":", env:check_type(def))
print(def, ":", env:type_check(def))
env:add_definition("sum", def)
**)

View file

@ -5,7 +5,7 @@ Variable x : Int
-- The type of the new variable is equal to the type
-- of x
local env = get_environment()
typeofx = env:check_type(Const("x"))
typeofx = env:type_check(Const("x"))
print("type of x is " .. tostring(typeofx))
env:add_var("y", typeofx)
**)

View file

@ -4,7 +4,7 @@ Variable x : Int
local N = 100
local env = get_environment()
-- Create N variables with the same type of x
typeofx = env:check_type(Const("x"))
typeofx = env:type_check(Const("x"))
for i = 1, N do
env:add_var("y_" .. i, typeofx)
end

View file

@ -2,7 +2,7 @@ Variable x : Int
(**
local env = get_environment()
ty_x = env:check_type(Const("x"))
ty_x = env:type_check(Const("x"))
c = context()
c = context(c, "x", ty_x)
c = context(c, "y", ty_x)

View file

@ -5,12 +5,12 @@ Variable x : Bool
local Int = Const("Int")
local plus = Const{"Int", "add"}
local x1, x2 = Consts("x1, x2")
print(env:check_type(Int))
print(env:check_type(plus))
print(env:type_check(Int))
print(env:type_check(plus))
env:add_var("x1", Int)
env:add_var("x2", Int)
print(plus(x1, x2))
print(env:check_type(plus(x1)))
print(env:type_check(plus(x1)))
function sum(...)
local args = {...}
@ -27,7 +27,7 @@ Variable x : Bool
local s = sum(x1, x1, x1, x2, x2)
print(s)
print(env:check_type(s))
print(env:type_check(s))
env:add_definition("sum1", s)
**)

View file

@ -6,5 +6,5 @@ e:add_var("N", Type())
N, M = Consts("N M")
e:add_var("a", N)
x, a = Consts("x, a")
check_error(function() e:check_type(fun(x, M, a)) end)
print(e:check_type(fun(x, N, a)))
check_error(function() e:type_check(fun(x, M, a)) end)
print(e:type_check(fun(x, N, a)))

View file

@ -27,4 +27,4 @@ child:add_axiom("H1", Eq(Const("x"), iVal(0)))
assert(child:has_object("H1"))
local ctx = context(context(), "x", Const("Int"), iVal(10))
assert(child:normalize(Var(0), ctx) == iVal(10))
assert(child:check_type(Var(0), ctx) == Const("Int"))
assert(child:type_check(Var(0), ctx) == Const("Int"))

View file

@ -9,4 +9,4 @@ print(parse_lean("f x (f x y)", env, opts))
-- parse_lean will use the elaborator to fill missing information
local F = parse_lean("fun x, f x x", env, opts)
print(F)
print(env:check_type(F))
print(env:type_check(F))

View file

@ -6,8 +6,8 @@ parse_lean_cmds([[
SetOption pp::colors false
]], env)
local f, x, y = Consts("f, x, y")
print(env:check_type(f(x, y)))
assert(env:check_type(f(x, y)) == Const("N"))
print(env:type_check(f(x, y)))
assert(env:type_check(f(x, y)) == Const("N"))
assert(not get_options():get{"pp", "colors"})
parse_lean_cmds([[
SetOption pp::colors true