feat(library/class_instance_resolution): recursively invoke type class resolution when parameters are instances
This commit is contained in:
parent
6acf7afa16
commit
5f43b9b183
1 changed files with 83 additions and 61 deletions
|
@ -175,66 +175,16 @@ struct cienv {
|
||||||
virtual void pop() { m_cienv.m_state = m_stack.back(); m_stack.pop_back(); }
|
virtual void pop() { m_cienv.m_state = m_stack.back(); m_stack.pop_back(); }
|
||||||
virtual void commit() { m_stack.pop_back(); }
|
virtual void commit() { m_stack.pop_back(); }
|
||||||
|
|
||||||
static bool has_meta_arg(expr e) {
|
|
||||||
while (is_app(e)) {
|
|
||||||
if (is_meta(app_arg(e)))
|
|
||||||
return true;
|
|
||||||
e = app_fn(e);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** IF \c e is of the form (f ... (?m t_1 ... t_n) ...) where ?m is an unassigned
|
|
||||||
metavariable whose type is a type class, and (?m t_1 ... t_n) must be synthesized
|
|
||||||
by type class resolution, then we return ?m.
|
|
||||||
Otherwise, we return none */
|
|
||||||
optional<pair<expr, expr>> find_unsynth_metavar(expr const & e) {
|
|
||||||
if (!has_meta_arg(e))
|
|
||||||
return optional<pair<expr, expr>>();
|
|
||||||
buffer<expr> args;
|
|
||||||
expr const & fn = get_app_args(e, args);
|
|
||||||
expr type = m_cienv.infer_type(fn);
|
|
||||||
unsigned i = 0;
|
|
||||||
while (i < args.size()) {
|
|
||||||
type = m_cienv.whnf(type);
|
|
||||||
if (!is_pi(type))
|
|
||||||
return optional<pair<expr, expr>>();
|
|
||||||
expr const & arg = args[i];
|
|
||||||
if (binding_info(type).is_inst_implicit() && is_meta(arg)) {
|
|
||||||
expr const & m = get_app_fn(arg);
|
|
||||||
if (is_mvar(m)) {
|
|
||||||
expr m_type = instantiate_uvars_mvars(infer_metavar(m));
|
|
||||||
if (!has_expr_metavar_relaxed(m_type)) {
|
|
||||||
return some(mk_pair(m, m_type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type = instantiate(binding_body(type), arg);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return optional<pair<expr, expr>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mk_instance(expr const & m, expr const & m_type) {
|
|
||||||
lean_assert(m);
|
|
||||||
if (auto r = m_cienv.mk_nested_instance(m_type)) {
|
|
||||||
m_cienv.update_assignment(m, *r);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool on_is_def_eq_failure(expr & e1, expr & e2) {
|
virtual bool on_is_def_eq_failure(expr & e1, expr & e2) {
|
||||||
if (is_app(e1) && is_app(e2)) {
|
if (is_app(e1) && is_app(e2)) {
|
||||||
if (auto p1 = find_unsynth_metavar(e1)) {
|
if (auto p1 = m_cienv.find_unsynth_metavar(e1)) {
|
||||||
if (mk_instance(p1->first, p1->second)) {
|
if (m_cienv.mk_nested_instance(p1->first, p1->second)) {
|
||||||
e1 = m_cienv.instantiate_uvars_mvars(e1);
|
e1 = m_cienv.instantiate_uvars_mvars(e1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto p2 = find_unsynth_metavar(e2)) {
|
if (auto p2 = m_cienv.find_unsynth_metavar(e2)) {
|
||||||
if (mk_instance(p2->first, p2->second)) {
|
if (m_cienv.mk_nested_instance(p2->first, p2->second)) {
|
||||||
e2 = m_cienv.instantiate_uvars_mvars(e2);
|
e2 = m_cienv.instantiate_uvars_mvars(e2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -399,6 +349,47 @@ struct cienv {
|
||||||
return !n.is_atomic() && n.get_prefix() == *g_prefix;
|
return !n.is_atomic() && n.get_prefix() == *g_prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function for find_unsynth_metavar
|
||||||
|
static bool has_meta_arg(expr e) {
|
||||||
|
while (is_app(e)) {
|
||||||
|
if (is_meta(app_arg(e)))
|
||||||
|
return true;
|
||||||
|
e = app_fn(e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** IF \c e is of the form (f ... (?m t_1 ... t_n) ...) where ?m is an unassigned
|
||||||
|
metavariable whose type is a type class, and (?m t_1 ... t_n) must be synthesized
|
||||||
|
by type class resolution, then we return ?m.
|
||||||
|
Otherwise, we return none */
|
||||||
|
optional<pair<expr, expr>> find_unsynth_metavar(expr const & e) {
|
||||||
|
if (!has_meta_arg(e))
|
||||||
|
return optional<pair<expr, expr>>();
|
||||||
|
buffer<expr> args;
|
||||||
|
expr const & fn = get_app_args(e, args);
|
||||||
|
expr type = infer_type(fn);
|
||||||
|
unsigned i = 0;
|
||||||
|
while (i < args.size()) {
|
||||||
|
type = whnf(type);
|
||||||
|
if (!is_pi(type))
|
||||||
|
return optional<pair<expr, expr>>();
|
||||||
|
expr const & arg = args[i];
|
||||||
|
if (binding_info(type).is_inst_implicit() && is_meta(arg)) {
|
||||||
|
expr const & m = get_app_fn(arg);
|
||||||
|
if (is_mvar(m)) {
|
||||||
|
expr m_type = instantiate_uvars_mvars(infer_type(m));
|
||||||
|
if (!has_expr_metavar_relaxed(m_type)) {
|
||||||
|
return some(mk_pair(m, m_type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type = instantiate(binding_body(type), arg);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return optional<pair<expr, expr>>();
|
||||||
|
}
|
||||||
|
|
||||||
/** \brief If the constant \c e is a class, return its name */
|
/** \brief If the constant \c e is a class, return its name */
|
||||||
optional<name> constant_is_class(expr const & e) {
|
optional<name> constant_is_class(expr const & e) {
|
||||||
name const & cls_name = const_name(e);
|
name const & cls_name = const_name(e);
|
||||||
|
@ -710,6 +701,25 @@ struct cienv {
|
||||||
return empty(m_state.m_stack);
|
return empty(m_state.m_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Try to make sure \c type does not contain meta-variables. It tries to:
|
||||||
|
- Instantiate assigned meta-variables
|
||||||
|
- Recursively invoke type class resolution.
|
||||||
|
This is needed when a type class has a parameter that is an instance. */
|
||||||
|
bool check_metavars_in_type(expr & type) {
|
||||||
|
type = instantiate_uvars_mvars(type);
|
||||||
|
// Remark: we use has_expr_metavar_relaxed instead of has_expr_metavar, because
|
||||||
|
// we want to ignore metavariables occurring in the type of local constants occurring in mvar_type.
|
||||||
|
// This can happen when type class resolution is invoked from the unifier.
|
||||||
|
|
||||||
|
if (!has_expr_metavar_relaxed(type))
|
||||||
|
return true;
|
||||||
|
if (auto p = find_unsynth_metavar(type)) {
|
||||||
|
if (mk_nested_instance(p->first, p->second))
|
||||||
|
return check_metavars_in_type(type);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool mk_choice_point(expr const & mvar) {
|
bool mk_choice_point(expr const & mvar) {
|
||||||
lean_assert(is_mvar(mvar));
|
lean_assert(is_mvar(mvar));
|
||||||
if (m_choices.size() > m_max_depth) {
|
if (m_choices.size() > m_max_depth) {
|
||||||
|
@ -718,16 +728,13 @@ struct cienv {
|
||||||
"(the class-instance resolution trace can be visualized by setting option 'class.trace_instances')",
|
"(the class-instance resolution trace can be visualized by setting option 'class.trace_instances')",
|
||||||
mlocal_type(m_main_mvar));
|
mlocal_type(m_main_mvar));
|
||||||
}
|
}
|
||||||
|
expr mvar_type = mlocal_type(mvar);
|
||||||
|
if (!check_metavars_in_type(mvar_type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
bool toplevel_choice = m_choices.empty();
|
bool toplevel_choice = m_choices.empty();
|
||||||
m_choices.push_back(choice());
|
m_choices.push_back(choice());
|
||||||
choice & r = m_choices.back();
|
choice & r = m_choices.back();
|
||||||
expr mvar_type = instantiate_uvars_mvars(mlocal_type(mvar));
|
|
||||||
if (has_expr_metavar_relaxed(mvar_type)) {
|
|
||||||
// Remark: we use has_expr_metavar_relaxed instead of has_expr_metavar, because
|
|
||||||
// we want to ignore metavariables occurring in the type of local constants occurring in mvar_type.
|
|
||||||
// This can happen when type class resolution is invoked from the unifier.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto cname = is_class(mvar_type);
|
auto cname = is_class(mvar_type);
|
||||||
if (!cname)
|
if (!cname)
|
||||||
return false;
|
return false;
|
||||||
|
@ -878,6 +885,8 @@ struct cienv {
|
||||||
return ensure_no_meta(r);
|
return ensure_no_meta(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Create a nested type class instance of the given type
|
||||||
|
\remark This method is used to resolve nested type class resolution problems. */
|
||||||
optional<expr> mk_nested_instance(expr const & type) {
|
optional<expr> mk_nested_instance(expr const & type) {
|
||||||
std::vector<choice> choices;
|
std::vector<choice> choices;
|
||||||
m_choices.swap(choices); // save choice stack
|
m_choices.swap(choices); // save choice stack
|
||||||
|
@ -887,6 +896,19 @@ struct cienv {
|
||||||
m_choices.swap(choices); // restore choice stack
|
m_choices.swap(choices); // restore choice stack
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief Create a nested type class instance of the given type, and assign it to metavariable \c m.
|
||||||
|
Return true iff the instance was successfully created.
|
||||||
|
\remark This method is used to resolve nested type class resolution problems. */
|
||||||
|
bool mk_nested_instance(expr const & m, expr const & m_type) {
|
||||||
|
lean_assert(is_mvar(m));
|
||||||
|
if (auto r = mk_nested_instance(m_type)) {
|
||||||
|
update_assignment(m, *r);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MK_THREAD_LOCAL_GET_DEF(cienv, get_cienv);
|
MK_THREAD_LOCAL_GET_DEF(cienv, get_cienv);
|
||||||
|
|
Loading…
Reference in a new issue