feat(library/tactic/rewrite_tactic): fail when nothing is rewritten

fixes #686
This commit is contained in:
Leonardo de Moura 2015-06-28 12:05:00 -07:00
parent d1eaa7bcda
commit 2f75768243
5 changed files with 43 additions and 14 deletions

View file

@ -112,16 +112,20 @@ deserializer & operator>>(deserializer & d, location & loc) {
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();
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) {
occ_idx++;
if (occ.contains(occ_idx))
if (occ.contains(occ_idx)) {
replaced = true;
return some_expr(mk_var(offset+idx));
}
}
return none_expr();
}, use_cache);
return replaced ? some_expr(r) : none_expr();
}
}

View file

@ -54,8 +54,10 @@ public:
/** \brief Replace occurrences of \c t in \c e with the free variable #vidx.
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 {
public:

View file

@ -1330,6 +1330,16 @@ class rewrite_fn {
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) {
add_target(hyp, true);
expr Pa = apply_beta_eta(mlocal_type(hyp));
@ -1344,21 +1354,22 @@ class rewrite_fn {
return false;
bool has_dep_elim = inductive::has_dep_elim(m_env, get_eq_name());
unsigned vidx = has_dep_elim ? 1 : 0;
expr Px = replace_occurrences(Pa, a, occ, vidx);
expr Pb = instantiate(Px, vidx, b);
optional<expr> Px = replace_occurrences(Pa, a, occ, vidx);
if (!Px) {
return false;
}
expr Pb = instantiate(*Px, vidx, b);
expr A = m_relaxed_tc->infer(a).first;
level l1 = sort_level(m_relaxed_tc->ensure_type(Pa).first);
level l2 = sort_level(m_relaxed_tc->ensure_type(A).first);
expr H;
if (has_dep_elim) {
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});
} 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);
buffer<expr> new_hyps;
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].
bool has_dep_elim = inductive::has_dep_elim(m_env, get_eq_name());
unsigned vidx = has_dep_elim ? 1 : 0;
expr Px = replace_occurrences(Pa, a, occ, vidx);
expr Pb = instantiate(Px, vidx, b);
optional<expr> Px = replace_occurrences(Pa, a, occ, vidx);
if (!Px) {
throw_nothing_rewritten_ex();
return false;
}
expr Pb = instantiate(*Px, vidx, b);
expr A = m_relaxed_tc->infer(a).first;
level l1 = sort_level(m_relaxed_tc->ensure_type(Pa).first);
level l2 = sort_level(m_relaxed_tc->ensure_type(A).first);
@ -1405,10 +1420,10 @@ class rewrite_fn {
expr H;
if (has_dep_elim) {
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});
} 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);
goal new_g(M, Pb);

View 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

View 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)