fix(kernel,library,frontends/lean): improve error messages

see #669
This commit is contained in:
Leonardo de Moura 2015-06-14 19:44:00 -07:00
parent ff5022c2f4
commit d8620ef4c9
15 changed files with 92 additions and 64 deletions

View file

@ -61,7 +61,7 @@ static optional<pair<expr, expr>> mk_op(environment const & env, local_context &
return optional<pair<expr, expr>>();
r = mk_app(r, explicit_arg);
expr type = tc->infer(explicit_arg, cs);
justification j = mk_app_justification(r, explicit_arg, binding_domain(op_type), type);
justification j = mk_app_justification(r, op_type, explicit_arg, type);
if (!tc->is_def_eq(binding_domain(op_type), type, j, cs))
return optional<pair<expr, expr>>();
op_type = instantiate(binding_body(op_type), explicit_arg);

View file

@ -318,7 +318,7 @@ expr elaborator::visit_choice(expr const & e, optional<expr> const & t, constrai
name_generator && /* ngen */) {
return choose(std::make_shared<choice_expr_elaborator>(*this, ctx, full_ctx, meta, type, e));
};
auto pp_fn = [=](formatter const & fmt, pos_info_provider const * pos_prov, substitution const &, bool is_main) {
auto pp_fn = [=](formatter const & fmt, pos_info_provider const * pos_prov, substitution const &, bool is_main, bool) {
format r = pp_previous_error_header(fmt, pos_prov, some_expr(e), is_main);
r += format("none of the overloads is applicable:");
for (unsigned i = 0; i < get_num_choices(e); i++) {
@ -449,7 +449,7 @@ pair<expr, expr> elaborator::ensure_fun(expr f, constraint_seq & cs) {
} else {
local_context ctx = m_context;
local_context full_ctx = m_full_context;
justification j = mk_justification(f, [=](formatter const & fmt, substitution const & subst) {
justification j = mk_justification(f, [=](formatter const & fmt, substitution const & subst, bool) {
return pp_function_expected(fmt, substitution(subst).instantiate(f));
});
auto choice_fn = [=](expr const & meta, expr const &, substitution const &, name_generator &&) {
@ -667,7 +667,7 @@ expr elaborator::visit_app(expr const & e, constraint_seq & cs) {
expr a = visit_expecting_type_of(app_arg(e), d_type, a_cs);
expr a_type = infer_type(a, a_cs);
expr r = mk_app(f, a, e.get_tag());
justification j = mk_app_justification(r, a, d_type, a_type);
justification j = mk_app_justification(r, f_type, a, a_type);
auto new_a_cs = ensure_has_type(a, a_type, d_type, j);
expr new_a = new_a_cs.first;
cs += f_cs + new_a_cs.second + a_cs;
@ -1175,7 +1175,7 @@ expr elaborator::visit_equations(expr const & eqns, constraint_seq & cs) {
new_cs.linearize(tmp_cs);
for (constraint const & c : tmp_cs) {
justification j = c.get_justification();
auto pp_fn = [=](formatter const & fmt, pos_info_provider const * pp, substitution const & s, bool is_main) {
auto pp_fn = [=](formatter const & fmt, pos_info_provider const * pp, substitution const & s, bool is_main, bool) {
if (!is_main)
return format();
format r = j.pp(fmt, pp, s);
@ -1230,12 +1230,12 @@ expr elaborator::visit_equation(expr const & eq, constraint_seq & cs) {
expr lhs_type = infer_type(new_lhs, cs);
expr rhs_type = infer_type(new_rhs, cs);
justification j = mk_justification(eq, [=](formatter const & fmt, substitution const & subst) {
justification j = mk_justification(eq, [=](formatter const & fmt, substitution const & subst, bool as_error) {
substitution s(subst);
name n = local_pp_name(lhs_fn);
if (is_match_binder_name(n))
n = "match";
return pp_def_type_mismatch(fmt, n, s.instantiate(lhs_type), s.instantiate(rhs_type));
return pp_def_type_mismatch(fmt, n, s.instantiate(lhs_type), s.instantiate(rhs_type), as_error);
});
pair<expr, constraint_seq> new_rhs_cs = ensure_has_type(new_rhs, rhs_type, lhs_type, j);
new_rhs = new_rhs_cs.first;
@ -1379,7 +1379,7 @@ expr elaborator::visit_structure_instance(expr const & e, constraint_seq & cs) {
}
S_mk = mk_app(S_mk, v, result_tag);
expr v_type = infer_type(v, cs);
justification j = mk_app_justification(S_mk, v, d_type, v_type);
justification j = mk_app_justification(S_mk, S_mk_type, v, v_type);
auto new_v_cs = ensure_has_type(v, v_type, d_type, j);
expr new_v = new_v_cs.first;
cs += new_v_cs.second;
@ -2062,9 +2062,9 @@ std::tuple<expr, expr, level_param_names> elaborator::operator()(expr const & t,
constraint_seq v_cs;
expr r_v = visit(v, v_cs);
expr r_v_type = infer_type(r_v, v_cs);
justification j = mk_justification(r_v, [=](formatter const & fmt, substitution const & subst) {
justification j = mk_justification(r_v, [=](formatter const & fmt, substitution const & subst, bool as_error) {
substitution s(subst);
return pp_def_type_mismatch(fmt, n, s.instantiate(r_t), s.instantiate(r_v_type));
return pp_def_type_mismatch(fmt, n, s.instantiate(r_t), s.instantiate(r_v_type), as_error);
});
pair<expr, constraint_seq> r_v_cs = ensure_has_type(r_v, r_v_type, r_t, j);
r_v = r_v_cs.first;

View file

@ -336,7 +336,7 @@ bool default_converter::try_eta_expansion_core(expr const & t, expr const & s, c
} else if (auto m = m_tc->is_stuck(s_type)) {
name_generator ngen = m_tc->mk_ngen();
expr r = mk_pi_for(ngen, *m);
justification j = mk_justification(s, [=](formatter const & fmt, substitution const & subst) {
justification j = mk_justification(s, [=](formatter const & fmt, substitution const & subst, bool) {
return pp_function_expected(fmt, substitution(subst).instantiate(s));
});
aux_cs += mk_eq_cnstr(s_type, r, j);

View file

@ -63,30 +63,51 @@ std::tuple<format, format> pp_until_different(formatter const & fmt, expr const
return pp_until_different(fmt, e1, e2, get_distinguishing_pp_options());
}
format pp_app_type_mismatch(formatter const & fmt, expr const & app, expr const & arg, expr const & expected_type, expr const & given_type) {
format pp_app_type_mismatch(formatter const & _fmt, expr const & app, expr const & fn_type, expr const & arg, expr const & given_type, bool as_error) {
formatter fmt(_fmt);
format r;
format expected_fmt, given_fmt;
lean_assert(is_pi(fn_type));
if (!is_explicit(binding_info(fn_type))) {
// For implicit arguments to be shown if argument is implicit
options opts = fmt.get_options();
// TODO(Leo): this is hackish, the option is defined in the folder library
opts = opts.update_if_undef(name{"pp", "implicit"}, true);
fmt = fmt.update_options(opts);
}
expr expected_type = binding_domain(fn_type);
std::tie(expected_fmt, given_fmt) = pp_until_different(fmt, expected_type, given_type);
r += format("type mismatch at application");
if (as_error)
r += format("type mismatch at application");
else
r += format("application type constraint");
r += pp_indent_expr(fmt, app);
r += compose(line(), format("term"));
r += pp_indent_expr(fmt, arg);
r += compose(line(), format("has type"));
r += given_fmt;
r += compose(line(), format("but is expected to have type"));
r += expected_fmt;
if (as_error) {
r += compose(line(), format("but is expected to have type"));
r += expected_fmt;
}
return r;
}
format pp_def_type_mismatch(formatter const & fmt, name const & n, expr const & expected_type, expr const & given_type) {
format pp_def_type_mismatch(formatter const & fmt, name const & n, expr const & expected_type, expr const & given_type, bool as_error) {
format expected_fmt, given_fmt;
std::tie(expected_fmt, given_fmt) = pp_until_different(fmt, expected_type, given_type);
format r("type mismatch at definition '");
format r;
if (as_error)
r += format("type mismatch at definition '");
else
r += format("definition type constraint '");
r += format(n);
r += format("', has type");
r += given_fmt;
r += compose(line(), format("but is expected to have type"));
r += expected_fmt;
if (as_error) {
r += compose(line(), format("but is expected to have type"));
r += expected_fmt;
}
return r;
}

View file

@ -11,8 +11,8 @@ namespace lean {
format pp_indent_expr(formatter const & fmt, expr const & e);
format pp_type_expected(formatter const & fmt, expr const & e);
format pp_function_expected(formatter const & fmt, expr const & e);
format pp_app_type_mismatch(formatter const & fmt, expr const & app, expr const & arg, expr const & expected_type, expr const & given_type);
format pp_def_type_mismatch(formatter const & fmt, name const & n, expr const & expected_type, expr const & given_type);
format pp_app_type_mismatch(formatter const & fmt, expr const & app, expr const & fn_type, expr const & arg, expr const & given_type, bool as_error);
format pp_def_type_mismatch(formatter const & fmt, name const & n, expr const & expected_type, expr const & given_type, bool as_error);
format pp_decl_has_metavars(formatter const & fmt, name const & n, expr const & e, bool is_type);
/** \brief Set a list extra configuration options that are used to try to distinguish error such as given/expected type mismatch

View file

@ -293,7 +293,7 @@ public:
};
format justification::pp_core(formatter const & fmt, pos_info_provider const * p, substitution const & s,
justification_set & set, bool is_main) const {
justification_set & set, bool is_main, bool as_error) const {
if (set.contains(*this))
return format();
set.insert(*this);
@ -302,25 +302,25 @@ format justification::pp_core(formatter const & fmt, pos_info_provider const * p
return format();
switch (it->m_kind) {
case justification_kind::Asserted:
return to_asserted(it)->m_fn(fmt, p, s, is_main);
return to_asserted(it)->m_fn(fmt, p, s, is_main, as_error);
case justification_kind::Wrapper:
return to_wrapper(it)->m_fn(fmt, p, s, is_main);
return to_wrapper(it)->m_fn(fmt, p, s, is_main, as_error);
case justification_kind::Assumption:
if (is_main)
return format(format("Assumption "), format(to_assumption(it)->m_idx));
else
return format();
case justification_kind::Composite: {
format r1 = to_composite(it)->m_child[0].pp_core(fmt, p, s, set, is_main);
format r2 = to_composite(it)->m_child[1].pp_core(fmt, p, s, set, false);
format r1 = to_composite(it)->m_child[0].pp_core(fmt, p, s, set, is_main, as_error);
format r2 = to_composite(it)->m_child[1].pp_core(fmt, p, s, set, false, as_error);
return r1 + r2;
}}
lean_unreachable();
}
format justification::pp(formatter const & fmt, pos_info_provider const * p, substitution const & s) const {
format justification::pp(formatter const & fmt, pos_info_provider const * p, substitution const & s, bool as_error) const {
justification_set set;
return pp_core(fmt, p, s, set, true);
return pp_core(fmt, p, s, set, true, as_error);
}
justification mk_wrapper(justification const & j, optional<expr> const & s, pp_jst_fn const & fn) {
@ -346,18 +346,18 @@ justification mk_justification(optional<expr> const & s, pp_jst_fn const & fn) {
return justification(new (get_asserted_allocator().allocate()) asserted_cell(fn, s));
}
justification mk_justification(optional<expr> const & s, pp_jst_sfn const & fn) {
return mk_justification(s, [=](formatter const & fmt, pos_info_provider const *, substitution const & subst, bool is_main) {
return mk_justification(s, [=](formatter const & fmt, pos_info_provider const *, substitution const & subst, bool is_main, bool as_error) {
// Remark: we are not using to_pos(s, p) anymore because we don't try to display complicated error messages anymore.
// return compose(to_pos(s, p), fn(fmt, subst));
if (is_main)
return fn(fmt, subst);
return fn(fmt, subst, as_error);
else
return format();
});
}
justification mk_justification(char const * msg, optional<expr> const & s) {
std::string _msg(msg);
return mk_justification(s, [=](formatter const &, pos_info_provider const *, substitution const &, bool is_main) {
return mk_justification(s, [=](formatter const &, pos_info_provider const *, substitution const &, bool is_main, bool) {
if (is_main)
return format(_msg);
else

View file

@ -23,7 +23,7 @@ struct justification_cell;
The pp_jst_fn is a generic funciton that produces these messages. We can associate these functions
to justification objects.
*/
typedef std::function<format(formatter const &, pos_info_provider const *, substitution const &, bool)> pp_jst_fn;
typedef std::function<format(formatter const &, pos_info_provider const *, substitution const &, bool, bool)> pp_jst_fn;
class justification_set;
@ -43,7 +43,7 @@ class justification {
justification_cell * m_ptr;
justification(justification_cell * ptr);
format pp_core(formatter const & fmt, pos_info_provider const * p, substitution const & s,
justification_set & visited, bool is_main) const;
justification_set & visited, bool is_main, bool as_error) const;
public:
justification();
justification(justification const & s);
@ -66,7 +66,7 @@ public:
\brief Convert this justification into a format object. This method is usually used to report
"error" messages to users.
*/
format pp(formatter const & fmt, pos_info_provider const * p, substitution const & s) const;
format pp(formatter const & fmt, pos_info_provider const * p, substitution const & s, bool as_error = true) const;
/**
\brief Return an expression associated with the justification object.
*/
@ -85,7 +85,7 @@ public:
/**
\brief Simpler version of pp_jst_fn
*/
typedef std::function<format(formatter const &, substitution const &)> pp_jst_sfn;
typedef std::function<format(formatter const &, substitution const &, bool)> pp_jst_sfn;
/** \brief Return a format object containing position information for the given expression (if available) */
format to_pos(optional<expr> const & e, pos_info_provider const * p);

View file

@ -106,7 +106,7 @@ pair<expr, constraint_seq> type_checker::ensure_sort_core(expr e, expr const & s
} else if (is_meta(ecs.first)) {
expr r = mk_sort(mk_meta_univ(m_gen.next()));
justification j = mk_justification(s,
[=](formatter const & fmt, substitution const & subst) {
[=](formatter const & fmt, substitution const & subst, bool) {
return pp_type_expected(fmt, substitution(subst).instantiate(s));
});
return to_ecs(r, mk_eq_cnstr(ecs.first, r, j), ecs.second);
@ -124,7 +124,7 @@ pair<expr, constraint_seq> type_checker::ensure_pi_core(expr e, expr const & s)
return ecs;
} else if (auto m = is_stuck(ecs.first)) {
expr r = mk_pi_for(m_gen, *m);
justification j = mk_justification(s, [=](formatter const & fmt, substitution const & subst) {
justification j = mk_justification(s, [=](formatter const & fmt, substitution const & subst, bool) {
return pp_function_expected(fmt, substitution(subst).instantiate(s));
});
return to_ecs(r, mk_eq_cnstr(ecs.first, r, j), ecs.second);
@ -136,7 +136,7 @@ pair<expr, constraint_seq> type_checker::ensure_pi_core(expr e, expr const & s)
static constexpr char const * g_macro_error_msg = "failed to type check macro expansion";
justification type_checker::mk_macro_jst(expr const & e) {
return mk_justification(e, [=](formatter const &, substitution const &) {
return mk_justification(e, [=](formatter const &, substitution const &, bool) {
return format(g_macro_error_msg);
});
}
@ -155,10 +155,10 @@ app_delayed_justification::app_delayed_justification(expr const & e, expr const
expr const & a_type):
m_e(e), m_arg(arg), m_fn_type(f_type), m_arg_type(a_type) {}
justification mk_app_justification(expr const & e, expr const & arg, expr const & d_type, expr const & a_type) {
auto pp_fn = [=](formatter const & fmt, substitution const & subst) {
justification mk_app_justification(expr const & e, expr const & fn_type, expr const & arg, expr const & a_type) {
auto pp_fn = [=](formatter const & fmt, substitution const & subst, bool as_error) {
substitution s(subst);
return pp_app_type_mismatch(fmt, s.instantiate(e), s.instantiate(arg), s.instantiate(d_type), s.instantiate(a_type));
return pp_app_type_mismatch(fmt, s.instantiate(e), s.instantiate(fn_type), s.instantiate(arg), s.instantiate(a_type), as_error);
};
return mk_justification(e, pp_fn);
}
@ -167,7 +167,7 @@ justification app_delayed_justification::get() {
if (!m_jst) {
// We should not have a reference to this object inside the closure.
// So, we create the following locals that will be captured by the closure instead of 'this'.
m_jst = mk_app_justification(m_e, m_arg, binding_domain(m_fn_type), m_arg_type);
m_jst = mk_app_justification(m_e, m_fn_type, m_arg, m_arg_type);
}
return *m_jst;
}
@ -264,15 +264,15 @@ pair<expr, constraint_seq> type_checker::infer_app(expr const & e, bool infer_on
pair<expr, constraint_seq> acs = infer_type_core(app_arg(e), infer_only);
expr a_type = acs.first;
app_delayed_justification jst(e, app_arg(e), f_type, a_type);
expr d_type = binding_domain(pics.first);
expr d_type = binding_domain(f_type);
pair<bool, constraint_seq> dcs = is_def_eq(a_type, d_type, jst);
if (!dcs.first) {
throw_kernel_exception(m_env, e,
[=](formatter const & fmt) {
return pp_app_type_mismatch(fmt, e, app_arg(e), d_type, a_type);
return pp_app_type_mismatch(fmt, e, f_type, app_arg(e), a_type, true);
});
}
return mk_pair(instantiate(binding_body(pics.first), app_arg(e)),
return mk_pair(instantiate(binding_body(f_type), app_arg(e)),
pics.second + ftcs.second + dcs.second + acs.second);
} else {
buffer<expr> args;
@ -491,7 +491,7 @@ certified_declaration check(environment const & env, declaration const & d, name
expr val_type = checker2.check(d.get_value(), d.get_univ_params()).first;
if (!checker2.is_def_eq(val_type, d.get_type()).first) {
throw_kernel_exception(env, d.get_value(), [=](formatter const & fmt) {
return pp_def_type_mismatch(fmt, d.get_name(), d.get_type(), val_type);
return pp_def_type_mismatch(fmt, d.get_name(), d.get_type(), val_type, true);
});
}
}

View file

@ -1402,14 +1402,14 @@ static int mk_justification(lua_State * L) {
return push_justification(L, justification());
} else if (nargs == 1) {
std::string s = lua_tostring(L, 1);
return push_justification(L, mk_justification(none_expr(), [=](formatter const &, substitution const &) {
return push_justification(L, mk_justification(none_expr(), [=](formatter const &, substitution const &, bool) {
return format(s.c_str());
}));
} else {
std::string s = lua_tostring(L, 1);
environment env = to_environment(L, 2);
expr e = to_expr(L, 3);
justification j = mk_justification(some_expr(e), [=](formatter const & fmt, substitution const & subst) {
justification j = mk_justification(some_expr(e), [=](formatter const & fmt, substitution const & subst, bool) {
expr new_e = substitution(subst).instantiate(e);
format r;
r += format(s.c_str());

View file

@ -641,7 +641,7 @@ struct unifier_fn {
justification mk_assign_justification(expr const & m, expr const & m_type, expr const & v_type, justification const & j) {
auto r = j.get_main_expr();
if (!r) r = m;
justification new_j = mk_justification(r, [=](formatter const & fmt, substitution const & subst) {
justification new_j = mk_justification(r, [=](formatter const & fmt, substitution const & subst, bool) {
substitution s(subst);
format r;
expr _m = s.instantiate(m);
@ -657,8 +657,8 @@ struct unifier_fn {
r += given_fmt;
r += compose(line(), format("but is expected to have type"));
r += expected_fmt;
r += compose(line(), format("the assignment was attempted when trying to solve"));
r += nest(2*get_pp_indent(fmt.get_options()), compose(line(), j.pp(fmt, nullptr, subst)));
r += compose(line(), format("the assignment was attempted when processing"));
r += nest(2*get_pp_indent(fmt.get_options()), compose(line(), j.pp(fmt, nullptr, subst, false)));
return r;
});
return mk_composite1(new_j, j);
@ -712,7 +712,7 @@ struct unifier_fn {
justification mk_invalid_local_ctx_justification(expr const & lhs, expr const & rhs, justification const & j,
expr const & bad_local) {
justification new_j = mk_justification(get_app_fn(lhs), [=](formatter const & fmt, substitution const & subst) {
justification new_j = mk_justification(get_app_fn(lhs), [=](formatter const & fmt, substitution const & subst, bool) {
format r = format("invalid local context when tried to assign");
r += pp_indent_expr(fmt, rhs);
buffer<expr> locals;
@ -730,8 +730,8 @@ struct unifier_fn {
}
r += nest(get_pp_indent(fmt.get_options()), compose(line(), aux));
}
r += compose(line(), format("the assignment was attempted when trying to solve"));
r += nest(2*get_pp_indent(fmt.get_options()), compose(line(), j.pp(fmt, nullptr, subst)));
r += compose(line(), format("the assignment was attempted when processing"));
r += nest(2*get_pp_indent(fmt.get_options()), compose(line(), j.pp(fmt, nullptr, subst, false)));
return r;
});
return mk_composite1(new_j, j);

View file

@ -792,7 +792,7 @@ format pp_type_mismatch(formatter const & fmt, expr const & v, expr const & v_ty
}
justification mk_type_mismatch_jst(expr const & v, expr const & v_type, expr const & t, expr const & src) {
return mk_justification(src, [=](formatter const & fmt, substitution const & subst) {
return mk_justification(src, [=](formatter const & fmt, substitution const & subst, bool) {
substitution s(subst);
return pp_type_mismatch(fmt, s.instantiate(v), s.instantiate(v_type), s.instantiate(t));
});
@ -870,7 +870,7 @@ expr instantiate_meta(expr const & meta, substitution & subst) {
}
justification mk_failed_to_synthesize_jst(environment const & env, expr const & m) {
return mk_justification(m, [=](formatter const & fmt, substitution const & subst) {
return mk_justification(m, [=](formatter const & fmt, substitution const & subst, bool) {
substitution tmp(subst);
expr new_m = instantiate_meta(m, tmp);
expr new_type = type_checker(env).infer(new_m).first;

View file

@ -4,12 +4,10 @@ placeholder has type
Type₁
but is expected to have type
Prop
the assignment was attempted when trying to solve
type mismatch at application
the assignment was attempted when processing
application type constraint
(λ {T : Prop} (t : T), t) bool.tt
term
bool.tt
has type
bool
but is expected to have type
bool

View file

@ -5,12 +5,10 @@ placeholder has type
Category
but is expected to have type
Type
the assignment was attempted when trying to solve
type mismatch at application
the assignment was attempted when processing
application type constraint
Category.mk a (category.mk b c)
term
category.mk b c
has type
category a
but is expected to have type
category a

3
tests/lean/mismatch.lean Normal file
View file

@ -0,0 +1,3 @@
definition id {A : Type} {a : A} := a
check @id nat 1

View file

@ -0,0 +1,8 @@
mismatch.lean:3:7: error: type mismatch at application
@id nat 1
term
1
has type
num
but is expected to have type
nat