feat(library/tactic/rewrite_tactic): fail when nothing is rewritten
fixes #686
This commit is contained in:
parent
d1eaa7bcda
commit
2f75768243
5 changed files with 43 additions and 14 deletions
|
@ -112,16 +112,20 @@ deserializer & operator>>(deserializer & d, location & loc) {
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
expr replace_occurrences(expr const & e, expr const & t, occurrence const & occ, unsigned idx) {
|
optional<expr> replace_occurrences(expr const & e, expr const & t, occurrence const & occ, unsigned idx) {
|
||||||
bool use_cache = occ.is_all();
|
bool use_cache = occ.is_all();
|
||||||
unsigned occ_idx = 0;
|
unsigned occ_idx = 0;
|
||||||
return replace(e, [&](expr const & e, unsigned offset) {
|
bool replaced = false;
|
||||||
|
expr r = replace(e, [&](expr const & e, unsigned offset) {
|
||||||
if (e == t) {
|
if (e == t) {
|
||||||
occ_idx++;
|
occ_idx++;
|
||||||
if (occ.contains(occ_idx))
|
if (occ.contains(occ_idx)) {
|
||||||
|
replaced = true;
|
||||||
return some_expr(mk_var(offset+idx));
|
return some_expr(mk_var(offset+idx));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return none_expr();
|
return none_expr();
|
||||||
}, use_cache);
|
}, use_cache);
|
||||||
|
return replaced ? some_expr(r) : none_expr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,8 +54,10 @@ public:
|
||||||
|
|
||||||
/** \brief Replace occurrences of \c t in \c e with the free variable #vidx.
|
/** \brief Replace occurrences of \c t in \c e with the free variable #vidx.
|
||||||
The j-th occurrence is replaced iff occ.contains(j)
|
The j-th occurrence is replaced iff occ.contains(j)
|
||||||
|
|
||||||
|
Return none when no occurrence was replaced.
|
||||||
*/
|
*/
|
||||||
expr replace_occurrences(expr const & e, expr const & t, occurrence const & occ, unsigned vidx);
|
optional<expr> replace_occurrences(expr const & e, expr const & t, occurrence const & occ, unsigned vidx);
|
||||||
|
|
||||||
class location {
|
class location {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -1330,6 +1330,16 @@ class rewrite_fn {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void throw_nothing_rewritten_ex() {
|
||||||
|
if (m_ps.report_failure()) {
|
||||||
|
throw_rewrite_exception("invalid 'rewrite' tactic, no occurrence was replaced, "
|
||||||
|
"this error may occur when the 'at' modifier is used (e.g., 'at {2}'). Lean "
|
||||||
|
"uses the following semantics: first it finds a matching subterm; then uses the bound "
|
||||||
|
"arguments to look for further syntactically identical occurrences; and then it replaces "
|
||||||
|
"the occurrences specified by the 'at' modifier (when provided)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool process_rewrite_hypothesis(expr const & hyp, expr const & orig_elem, expr const & pattern, occurrence const & occ) {
|
bool process_rewrite_hypothesis(expr const & hyp, expr const & orig_elem, expr const & pattern, occurrence const & occ) {
|
||||||
add_target(hyp, true);
|
add_target(hyp, true);
|
||||||
expr Pa = apply_beta_eta(mlocal_type(hyp));
|
expr Pa = apply_beta_eta(mlocal_type(hyp));
|
||||||
|
@ -1344,21 +1354,22 @@ class rewrite_fn {
|
||||||
return false;
|
return false;
|
||||||
bool has_dep_elim = inductive::has_dep_elim(m_env, get_eq_name());
|
bool has_dep_elim = inductive::has_dep_elim(m_env, get_eq_name());
|
||||||
unsigned vidx = has_dep_elim ? 1 : 0;
|
unsigned vidx = has_dep_elim ? 1 : 0;
|
||||||
expr Px = replace_occurrences(Pa, a, occ, vidx);
|
optional<expr> Px = replace_occurrences(Pa, a, occ, vidx);
|
||||||
expr Pb = instantiate(Px, vidx, b);
|
if (!Px) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
expr Pb = instantiate(*Px, vidx, b);
|
||||||
expr A = m_relaxed_tc->infer(a).first;
|
expr A = m_relaxed_tc->infer(a).first;
|
||||||
level l1 = sort_level(m_relaxed_tc->ensure_type(Pa).first);
|
level l1 = sort_level(m_relaxed_tc->ensure_type(Pa).first);
|
||||||
level l2 = sort_level(m_relaxed_tc->ensure_type(A).first);
|
level l2 = sort_level(m_relaxed_tc->ensure_type(A).first);
|
||||||
expr H;
|
expr H;
|
||||||
if (has_dep_elim) {
|
if (has_dep_elim) {
|
||||||
expr Haeqx = mk_app(mk_constant(get_eq_name(), {l2}), A, a, mk_var(0));
|
expr Haeqx = mk_app(mk_constant(get_eq_name(), {l2}), A, a, mk_var(0));
|
||||||
expr P = mk_lambda("x", A, mk_lambda("H", Haeqx, Px));
|
expr P = mk_lambda("x", A, mk_lambda("H", Haeqx, *Px));
|
||||||
H = mk_app({mk_constant(get_eq_rec_name(), {l1, l2}), A, a, P, hyp, b, Heq});
|
H = mk_app({mk_constant(get_eq_rec_name(), {l1, l2}), A, a, P, hyp, b, Heq});
|
||||||
} else {
|
} else {
|
||||||
H = mk_app({mk_constant(get_eq_rec_name(), {l1, l2}), A, a, mk_lambda("x", A, Px), hyp, b, Heq});
|
H = mk_app({mk_constant(get_eq_rec_name(), {l1, l2}), A, a, mk_lambda("x", A, *Px), hyp, b, Heq});
|
||||||
}
|
}
|
||||||
|
|
||||||
expr new_hyp = update_mlocal(hyp, Pb);
|
expr new_hyp = update_mlocal(hyp, Pb);
|
||||||
buffer<expr> new_hyps;
|
buffer<expr> new_hyps;
|
||||||
buffer<expr> args;
|
buffer<expr> args;
|
||||||
|
@ -1396,8 +1407,12 @@ class rewrite_fn {
|
||||||
// where M is a metavariable application of a fresh metavariable, and H is a witness (based on M) for P[a].
|
// where M is a metavariable application of a fresh metavariable, and H is a witness (based on M) for P[a].
|
||||||
bool has_dep_elim = inductive::has_dep_elim(m_env, get_eq_name());
|
bool has_dep_elim = inductive::has_dep_elim(m_env, get_eq_name());
|
||||||
unsigned vidx = has_dep_elim ? 1 : 0;
|
unsigned vidx = has_dep_elim ? 1 : 0;
|
||||||
expr Px = replace_occurrences(Pa, a, occ, vidx);
|
optional<expr> Px = replace_occurrences(Pa, a, occ, vidx);
|
||||||
expr Pb = instantiate(Px, vidx, b);
|
if (!Px) {
|
||||||
|
throw_nothing_rewritten_ex();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
expr Pb = instantiate(*Px, vidx, b);
|
||||||
expr A = m_relaxed_tc->infer(a).first;
|
expr A = m_relaxed_tc->infer(a).first;
|
||||||
level l1 = sort_level(m_relaxed_tc->ensure_type(Pa).first);
|
level l1 = sort_level(m_relaxed_tc->ensure_type(Pa).first);
|
||||||
level l2 = sort_level(m_relaxed_tc->ensure_type(A).first);
|
level l2 = sort_level(m_relaxed_tc->ensure_type(A).first);
|
||||||
|
@ -1405,10 +1420,10 @@ class rewrite_fn {
|
||||||
expr H;
|
expr H;
|
||||||
if (has_dep_elim) {
|
if (has_dep_elim) {
|
||||||
expr Haeqx = mk_app(mk_constant(get_eq_name(), {l2}), A, b, mk_var(0));
|
expr Haeqx = mk_app(mk_constant(get_eq_name(), {l2}), A, b, mk_var(0));
|
||||||
expr P = mk_lambda("x", A, mk_lambda("H", Haeqx, Px));
|
expr P = mk_lambda("x", A, mk_lambda("H", Haeqx, *Px));
|
||||||
H = mk_app({mk_constant(get_eq_rec_name(), {l1, l2}), A, b, P, M, a, Heq});
|
H = mk_app({mk_constant(get_eq_rec_name(), {l1, l2}), A, b, P, M, a, Heq});
|
||||||
} else {
|
} else {
|
||||||
H = mk_app({mk_constant(get_eq_rec_name(), {l1, l2}), A, b, mk_lambda("x", A, Px), M, a, Heq});
|
H = mk_app({mk_constant(get_eq_rec_name(), {l1, l2}), A, b, mk_lambda("x", A, *Px), M, a, Heq});
|
||||||
}
|
}
|
||||||
check_term(H);
|
check_term(H);
|
||||||
goal new_g(M, Pb);
|
goal new_g(M, Pb);
|
||||||
|
|
7
tests/lean/rw_at_failure.lean
Normal file
7
tests/lean/rw_at_failure.lean
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import data.nat
|
||||||
|
open nat
|
||||||
|
|
||||||
|
example (a b : nat) : a = b → 0 + a = 0 + b :=
|
||||||
|
begin
|
||||||
|
rewrite zero_add at {2} -- Should fail since nothing is rewritten
|
||||||
|
end
|
1
tests/lean/rw_at_failure.lean.expected.out
Normal file
1
tests/lean/rw_at_failure.lean.expected.out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rw_at_failure.lean:6:10: error: invalid 'rewrite' tactic, no occurrence was replaced, this error may occur when the 'at' modifier is used (e.g., 'at {2}'). Lean uses the following semantics: first it finds a matching subterm; then uses the bound arguments to look for further syntactically identical occurrences; and then it replaces the occurrences specified by the 'at' modifier (when provided)
|
Loading…
Reference in a new issue