feat(frontends/lean/elaborator): decorate error messages when overloads are used

This commit is contained in:
Leonardo de Moura 2016-09-29 15:00:45 -07:00
parent f2e8b8794c
commit f97e238b8e
8 changed files with 131 additions and 47 deletions

View file

@ -1125,7 +1125,7 @@ expr elaborator::visit_overload_candidate(expr const & fn, buffer<expr> const &
return visit_base_app_core(fn, arg_mask::Default, args, true, expected_type, ref);
}
void elaborator::throw_no_overload_applicable(buffer<expr> const & fns, buffer<elaborator_exception> const & error_msgs, expr const & ref) {
format elaborator::mk_no_overload_applicable_msg(buffer<expr> const & fns, buffer<elaborator_exception> const & error_msgs) {
format r("none of the overloads are applicable");
lean_assert(error_msgs.size() == fns.size());
for (unsigned i = 0; i < fns.size(); i++) {
@ -1135,48 +1135,12 @@ void elaborator::throw_no_overload_applicable(buffer<expr> const & fns, buffer<e
r += line() + format("error for") + space() + f_fmt;
r += line() + error_msgs[i].pp();
}
throw elaborator_exception(ref, r);
return r;
}
optional<expr> elaborator::visit_overloaded_app_with_expected(buffer<expr> const & fns, buffer<expr> const & args,
expr const & expected_type, expr const & ref) {
snapshot S(*this);
buffer<std::tuple<expr, snapshot, first_pass_info>> candidates;
// TODO(Leo): use error messages to decorate nested exceptions
buffer<elaborator_exception> error_msgs;
for (expr const & fn : fns) {
try {
// Restore state
S.restore(*this);
bool has_args = !args.empty();
expr new_fn = visit_function(fn, has_args, ref);
first_pass_info info;
first_pass(new_fn, args, expected_type, ref, info);
candidates.emplace_back(new_fn, snapshot(*this), info);
} catch (elaborator_exception & ex) {
error_msgs.push_back(ex);
} catch (exception & ex) {
error_msgs.push_back(elaborator_exception(ref, format(ex.what())));
}
}
if (candidates.empty()) {
S.restore(*this);
return none_expr();
}
if (candidates.size() == 1) {
// Restore successful state
auto & c = candidates[0];
expr fn = std::get<0>(c);
std::get<1>(c).restore(*this);
first_pass_info & info = std::get<2>(c);
return some_expr(second_pass(fn, args, ref, info));
}
/* Failed to disambiguate using expected type, switch to basic */
S.restore(*this);
return none_expr();
void elaborator::throw_no_overload_applicable(buffer<expr> const & fns, buffer<elaborator_exception> const & error_msgs,
expr const & ref) {
throw elaborator_exception(ref, mk_no_overload_applicable_msg(fns, error_msgs));
}
expr elaborator::visit_overloaded_app_core(buffer<expr> const & fns, buffer<expr> const & args,
@ -1243,16 +1207,94 @@ expr elaborator::visit_overloaded_app_core(buffer<expr> const & fns, buffer<expr
}
}
expr elaborator::visit_overloaded_app_with_expected(buffer<expr> const & fns, buffer<expr> const & args,
expr const & expected_type, expr const & ref) {
snapshot S(*this);
buffer<std::tuple<expr, snapshot, first_pass_info>> candidates;
buffer<elaborator_exception> error_msgs;
for (expr const & fn : fns) {
try {
// Restore state
S.restore(*this);
bool has_args = !args.empty();
expr new_fn = visit_function(fn, has_args, ref);
first_pass_info info;
first_pass(new_fn, args, expected_type, ref, info);
candidates.emplace_back(new_fn, snapshot(*this), info);
} catch (elaborator_exception & ex) {
error_msgs.push_back(ex);
} catch (exception & ex) {
error_msgs.push_back(elaborator_exception(ref, format(ex.what())));
}
}
if (candidates.empty()) {
try {
/* Failed to disambiguate using expected type, switch to basic */
S.restore(*this);
return visit_overloaded_app_core(fns, args, some_expr(expected_type), ref);
} catch (elaborator_exception & ex) {
auto pp_fn = mk_pp_ctx();
format msg = format("switched to basic overload resolution where arguments are elaborated without "
"any information about the expected type, because failed to elaborate all candidates "
"using the expected type");
msg += pp_indent(pp_fn, expected_type);
msg += line() + format("this can happen because, for example, coercions were not considered in the process");
msg += line() + mk_no_overload_applicable_msg(fns, error_msgs);
throw nested_elaborator_exception(ref, ex, msg);
}
}
if (candidates.size() == 1) {
// Restore successful state
auto & c = candidates[0];
expr fn = std::get<0>(c);
std::get<1>(c).restore(*this);
first_pass_info & info = std::get<2>(c);
try {
return second_pass(fn, args, ref, info);
} catch (elaborator_exception & ex) {
auto pp_fn = mk_pp_ctx();
format msg = format("overload was disambiguated using expected type");
msg += line() + pp_overloads(pp_fn, fns);
msg += line() + format("the only applicable one seemed to be: ") + pp(fn);
throw nested_elaborator_exception(ref, ex, msg);
}
}
try {
/* Failed to disambiguate using expected type, switch to basic */
S.restore(*this);
return visit_overloaded_app_core(fns, args, some_expr(expected_type), ref);
} catch (elaborator_exception & ex) {
auto pp_fn = mk_pp_ctx();
format msg = format("switched to basic overload resolution where arguments are elaborated without "
"any information about the expected type because it failed to disambiguate "
"overload using the expected type");
msg += pp_indent(pp_fn, expected_type);
msg += line() + format("the following overloaded terms were applicable");
for (auto const & c : candidates)
msg += pp_indent(pp_fn, std::get<0>(c));
throw nested_elaborator_exception(ref, ex, msg);
}
}
expr elaborator::visit_overloaded_app(buffer<expr> const & fns, buffer<expr> const & args,
optional<expr> const & expected_type, expr const & ref) {
trace_elab_detail(tout() << "overloaded application at " << pos_string_for(ref);
auto pp_fn = mk_pp_ctx();
tout() << pp_overloads(pp_fn, fns) << "\n";);
if (expected_type) {
if (auto r = visit_overloaded_app_with_expected(fns, args, *expected_type, ref))
return *r;
return visit_overloaded_app_with_expected(fns, args, *expected_type, ref);
} else {
try {
return visit_overloaded_app_core(fns, args, expected_type, ref);
} catch (elaborator_exception & ex) {
format msg = format("switched to basic overload resolution where arguments are elaborated without "
"any information about the expected type because expected type was not available");
throw nested_elaborator_exception(ref, ex, msg);
}
}
return visit_overloaded_app_core(fns, args, expected_type, ref);
}
expr elaborator::visit_no_confusion_app(expr const & fn, buffer<expr> const & args, optional<expr> const & expected_type,

View file

@ -166,7 +166,8 @@ private:
bool is_with_expected_candidate(expr const & fn);
struct first_pass_info;
void first_pass(expr const & fn, buffer<expr> const & args, expr const & expected_type, expr const & ref, first_pass_info & info);
void first_pass(expr const & fn, buffer<expr> const & args, expr const & expected_type, expr const & ref,
first_pass_info & info);
expr second_pass(expr const & fn, buffer<expr> const & args, expr const & ref, first_pass_info & info);
expr visit_base_app_simple(expr const & _fn, arg_mask amask, buffer<expr> const & args,
bool args_already_visited, optional<expr> const & expected_type, expr const & ref);
@ -175,13 +176,14 @@ private:
expr visit_base_app(expr const & fn, arg_mask amask, buffer<expr> const & args,
optional<expr> const & expected_type, expr const & ref);
void validate_overloads(buffer<expr> const & fns, expr const & ref);
format mk_no_overload_applicable_msg(buffer<expr> const & fns, buffer<elaborator_exception> const & error_msgs);
[[ noreturn ]]
void throw_no_overload_applicable(buffer<expr> const & fns, buffer<elaborator_exception> const & error_msgs,
expr const & ref);
expr visit_overload_candidate(expr const & fn, buffer<expr> const & args,
optional<expr> const & expected_type, expr const & ref);
optional<expr> visit_overloaded_app_with_expected(buffer<expr> const & fns, buffer<expr> const & args,
expr const & expected_type, expr const & ref);
expr visit_overloaded_app_with_expected(buffer<expr> const & fns, buffer<expr> const & args,
expr const & expected_type, expr const & ref);
expr visit_overloaded_app_core(buffer<expr> const & fns, buffer<expr> const & args,
optional<expr> const & expected_type, expr const & ref);
expr visit_overloaded_app(buffer<expr> const & fns, buffer<expr> const & args,

View file

@ -3,3 +3,5 @@ pr a b : N
choice_expl.lean:15:6: error: ambiguous overload, possible interpretations
N2.pr a b
N1.pr a b
Additional information:
choice_expl.lean:15:6: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type because expected type was not available

View file

@ -1,6 +1,8 @@
elab11.lean:6:6: error: ambiguous overload, possible interpretations
bla.f 1
boo.f 1
Additional information:
elab11.lean:6:6: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type because expected type was not available
bla.f 1 :
boo.f 1 : bool
elab11.lean:16:7: error: none of the overloads are applicable
@ -19,3 +21,23 @@ has type
bool
but is expected to have type
string
Additional information:
elab11.lean:16:7: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type, because failed to elaborate all candidates using the expected type
string
this can happen because, for example, coercions were not considered in the process
none of the overloads are applicable
error for bla.f
type mismatch
f ?m_1
has type
but is expected to have type
string
error for boo.f
type mismatch
f ?m_1
has type
bool
but is expected to have type
string

View file

@ -11,12 +11,18 @@ function expected at
error for boo.f
function expected at
boo.f 0 1 2
Additional information:
elab4.lean:13:6: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type because expected type was not available
elab4.lean:15:6: error: ambiguous overload, possible interpretations
foo.f 0 1
boo.f 0 1
Additional information:
elab4.lean:15:6: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type because expected type was not available
foo.f bool.tt 2 : bool
bla.f bool.tt bool.ff bool.tt : bool → bool
elab4.lean:21:6: error: ambiguous overload, possible interpretations
bla.f bool.tt bool.ff
foo.f bool.tt bool.ff
Additional information:
elab4.lean:21:6: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type because expected type was not available
foo.f 0 1 :

View file

@ -10,9 +10,15 @@ function expected at
error for boo.f
function expected at
f 0 1 2
Additional information:
elab4b.lean:9:6: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type because expected type was not available
elab4b.lean:11:6: error: ambiguous overload, possible interpretations
foo.f 0 1
boo.f 0 1
Additional information:
elab4b.lean:11:6: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type because expected type was not available
elab4b.lean:13:6: error: ambiguous overload, possible interpretations
bla.f bool.tt bool.ff
foo.f bool.tt bool.ff
Additional information:
elab4b.lean:13:6: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type because expected type was not available

View file

@ -1,6 +1,8 @@
nary_overload.lean:16:6: error: ambiguous overload, possible interpretations
[a, b, c]
[a, b, c]
Additional information:
nary_overload.lean:16:6: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type because expected type was not available
[a, b, c] : vec A
[a, b, c] : lst A
@vec.cons A a (@vec.cons A b (@vec.cons A c (@vec.nil A))) : vec.{1} A

View file

@ -20,5 +20,7 @@ has type
bool
but is expected to have type
nat
Additional information:
over_notation.lean:11:9: context: switched to basic overload resolution where arguments are elaborated without any information about the expected type because expected type was not available
f 1 (f 2 (f 3 0)) : nat
g "a" (g "b" (g "c" "")) : string