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:
parent
9bac91f5ef
commit
4229e498d2
36 changed files with 617 additions and 845 deletions
|
@ -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, ", ")
|
||||
|
|
|
@ -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)");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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,47 +200,26 @@ 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);
|
||||
} else {
|
||||
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, ctx.size() - def_ctx.size()), shared);
|
||||
return save_result(e, lift_free_vars(t, var_idx(e) + 1), shared);
|
||||
}
|
||||
}
|
||||
case expr_kind::Type:
|
||||
return save_result(e, mk_type(ty_level(e) + 1), shared);
|
||||
case expr_kind::App: {
|
||||
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 {
|
||||
unsigned num = num_args(e);
|
||||
lean_assert(num >= 2);
|
||||
buffer<expr> arg_types;
|
||||
|
@ -169,7 +231,8 @@ class type_checker::imp {
|
|||
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
|
||||
// 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)))
|
||||
|
@ -185,19 +248,21 @@ class type_checker::imp {
|
|||
}
|
||||
}
|
||||
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: {
|
||||
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);
|
||||
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(lt, *let_type(e), ctx, mk_justification))
|
||||
throw def_type_mismatch_exception(env(), ctx, let_name(e), *let_type(e), let_value(e), lt);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
bool is_convertible(expr const & t1, expr const & t2, 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());
|
||||
}
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))));
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
**)
|
||||
|
|
|
@ -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)
|
||||
**)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
**)
|
||||
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue