feat(library/type_inference): check types when assigning meta-variables
This commit is contained in:
parent
4eafcfe945
commit
885393f4de
2 changed files with 64 additions and 4 deletions
|
@ -49,6 +49,8 @@ type_inference::type_inference(environment const & env):
|
||||||
m_ngen(*g_prefix),
|
m_ngen(*g_prefix),
|
||||||
m_ext_ctx(new ext_ctx(*this)),
|
m_ext_ctx(new ext_ctx(*this)),
|
||||||
m_proj_info(get_projection_info_map(env)) {
|
m_proj_info(get_projection_info_map(env)) {
|
||||||
|
m_ignore_external_mvars = false;
|
||||||
|
m_check_types = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
type_inference::~type_inference() {
|
type_inference::~type_inference() {
|
||||||
|
@ -599,7 +601,7 @@ bool type_inference::is_def_eq_proof_irrel(expr const & e1, expr const & e2) {
|
||||||
|
|
||||||
/** \brief Given \c ma of the form <tt>?m t_1 ... t_n</tt>, (try to) assign
|
/** \brief Given \c ma of the form <tt>?m t_1 ... t_n</tt>, (try to) assign
|
||||||
?m to (an abstraction of) v. Return true if success and false otherwise. */
|
?m to (an abstraction of) v. Return true if success and false otherwise. */
|
||||||
bool type_inference::assign_mvar(expr const & ma, expr const & v) {
|
bool type_inference::process_assignment(expr const & ma, expr const & v) {
|
||||||
buffer<expr> args;
|
buffer<expr> args;
|
||||||
expr const & m = get_app_args(ma, args);
|
expr const & m = get_app_args(ma, args);
|
||||||
buffer<expr> locals;
|
buffer<expr> locals;
|
||||||
|
@ -617,6 +619,23 @@ bool type_inference::assign_mvar(expr const & ma, expr const & v) {
|
||||||
if (!validate_assignment(m, locals, v))
|
if (!validate_assignment(m, locals, v))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (m_check_types) {
|
||||||
|
try {
|
||||||
|
scope s(*this);
|
||||||
|
expr t1 = infer(ma);
|
||||||
|
expr t2 = infer(v);
|
||||||
|
flet<bool> ignore_ext_mvar(m_ignore_external_mvars, true);
|
||||||
|
if (!is_def_eq_core(t1, t2))
|
||||||
|
return false;
|
||||||
|
s.commit();
|
||||||
|
} catch (exception &) {
|
||||||
|
// We may fail to infer the type of ma or v because the type may contain meta-variables.
|
||||||
|
// Example: ma may be of the form (?m a), and the type of ?m may be ?M where ?M is a
|
||||||
|
// (external) metavariable created by the unifier.
|
||||||
|
// We believe this only happens when we are interacting with the elaborator.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
// easy case
|
// easy case
|
||||||
update_assignment(m, new_v);
|
update_assignment(m, new_v);
|
||||||
|
@ -655,7 +674,7 @@ lbool type_inference::quick_is_def_eq(expr const & e1, expr const & e2) {
|
||||||
if (is_assigned(f1)) {
|
if (is_assigned(f1)) {
|
||||||
return to_lbool(is_def_eq_core(subst_mvar(e1), e2));
|
return to_lbool(is_def_eq_core(subst_mvar(e1), e2));
|
||||||
} else {
|
} else {
|
||||||
return to_lbool(assign_mvar(e1, e2));
|
return to_lbool(process_assignment(e1, e2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,10 +683,13 @@ lbool type_inference::quick_is_def_eq(expr const & e1, expr const & e2) {
|
||||||
if (is_assigned(f2)) {
|
if (is_assigned(f2)) {
|
||||||
return to_lbool(is_def_eq_core(e1, subst_mvar(e2)));
|
return to_lbool(is_def_eq_core(e1, subst_mvar(e2)));
|
||||||
} else {
|
} else {
|
||||||
return to_lbool(assign_mvar(e2, e1));
|
return to_lbool(process_assignment(e2, e1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_ignore_external_mvars && (is_meta(e1) || is_meta(e2)))
|
||||||
|
return l_true;
|
||||||
|
|
||||||
if (e1.kind() == e2.kind()) {
|
if (e1.kind() == e2.kind()) {
|
||||||
switch (e1.kind()) {
|
switch (e1.kind()) {
|
||||||
case expr_kind::Lambda: case expr_kind::Pi:
|
case expr_kind::Lambda: case expr_kind::Pi:
|
||||||
|
|
|
@ -29,6 +29,44 @@ class type_inference {
|
||||||
std::vector<pair<level, level>> m_postponed;
|
std::vector<pair<level, level>> m_postponed;
|
||||||
name_map<projection_info> m_proj_info;
|
name_map<projection_info> m_proj_info;
|
||||||
|
|
||||||
|
// Internal (configuration) options for customers
|
||||||
|
|
||||||
|
/** If m_ignore_external_mvars is true, then we treat (external) expression meta-variables
|
||||||
|
as wildcards. That is, (?M t_1 ... t_n) matches any term T. We say a meta-variable is
|
||||||
|
external when is_mvar returns false for it, and this module can't assign it. */
|
||||||
|
bool m_ignore_external_mvars;
|
||||||
|
|
||||||
|
/** If m_check_types is true, then whenever we assign a meta-variable,
|
||||||
|
we check whether the type of the value matches the type of the meta-variable.
|
||||||
|
When this option is enabled, we turn on m_ignore_external_mvars while checking types.
|
||||||
|
|
||||||
|
At this point, this option is useful only for helping us solve universe unification constraints.
|
||||||
|
For example, consider the following unification problem
|
||||||
|
Given:
|
||||||
|
p : Type.{l1}
|
||||||
|
q : Type.{l2}
|
||||||
|
?m1 : Type.{?u1}
|
||||||
|
?m2 : Type.{?u2}
|
||||||
|
|
||||||
|
decidable.{max ?u1 u2} (?m1 -> ?m2) =?= decidable.{max l1 l2} (q -> p)
|
||||||
|
|
||||||
|
If m_check_types is turned off, the is_def_eq implemented here produces the incorrect solution
|
||||||
|
|
||||||
|
?u1 := l1
|
||||||
|
?u2 := l2
|
||||||
|
?m1 := q
|
||||||
|
?m2 := p
|
||||||
|
|
||||||
|
This solution is type incorrect.
|
||||||
|
|
||||||
|
?m1 : Type.{l1} q : Type.{l2}
|
||||||
|
?m2 : Type.{l2} p : Type.{l1}
|
||||||
|
|
||||||
|
TODO(Leo,Daniel): checking types is extra overhead, and it seems unnecessary most of the time.
|
||||||
|
We should investigate how often this kind of constraint occurs in blast.
|
||||||
|
*/
|
||||||
|
bool m_check_types;
|
||||||
|
|
||||||
bool is_opaque(declaration const & d) const;
|
bool is_opaque(declaration const & d) const;
|
||||||
optional<expr> expand_macro(expr const & m);
|
optional<expr> expand_macro(expr const & m);
|
||||||
optional<expr> reduce_projection(expr const & e);
|
optional<expr> reduce_projection(expr const & e);
|
||||||
|
@ -52,7 +90,7 @@ class type_inference {
|
||||||
bool is_def_eq_proof_irrel(expr const & e1, expr const & e2);
|
bool is_def_eq_proof_irrel(expr const & e1, expr const & e2);
|
||||||
|
|
||||||
expr subst_mvar(expr const & e);
|
expr subst_mvar(expr const & e);
|
||||||
bool assign_mvar(expr const & ma, expr const & v);
|
bool process_assignment(expr const & ma, expr const & v);
|
||||||
enum class reduction_status { Continue, DefUnknown, DefEqual, DefDiff };
|
enum class reduction_status { Continue, DefUnknown, DefEqual, DefDiff };
|
||||||
reduction_status lazy_delta_reduction_step(expr & t_n, expr & s_n);
|
reduction_status lazy_delta_reduction_step(expr & t_n, expr & s_n);
|
||||||
lbool lazy_delta_reduction(expr & t_n, expr & s_n);
|
lbool lazy_delta_reduction(expr & t_n, expr & s_n);
|
||||||
|
|
Loading…
Reference in a new issue