feat(library/tactic): add 'congruence' tactic
It is the f_equal described at issue #500.
This commit is contained in:
parent
458d13025f
commit
415ca2b93f
12 changed files with 229 additions and 3 deletions
|
@ -43,7 +43,8 @@ opaque definition beta : tactic := builtin
|
||||||
opaque definition info : tactic := builtin
|
opaque definition info : tactic := builtin
|
||||||
opaque definition whnf : tactic := builtin
|
opaque definition whnf : tactic := builtin
|
||||||
opaque definition contradiction : tactic := builtin
|
opaque definition contradiction : tactic := builtin
|
||||||
opaque definition exfalso : tactic := builtin
|
opaque definition exfalso : tactic := builtin
|
||||||
|
opaque definition congruence : tactic := builtin
|
||||||
opaque definition rotate_left (k : num) := builtin
|
opaque definition rotate_left (k : num) := builtin
|
||||||
opaque definition rotate_right (k : num) := builtin
|
opaque definition rotate_right (k : num) := builtin
|
||||||
definition rotate (k : num) := rotate_left k
|
definition rotate (k : num) := rotate_left k
|
||||||
|
|
|
@ -44,6 +44,7 @@ opaque definition info : tactic := builtin
|
||||||
opaque definition whnf : tactic := builtin
|
opaque definition whnf : tactic := builtin
|
||||||
opaque definition contradiction : tactic := builtin
|
opaque definition contradiction : tactic := builtin
|
||||||
opaque definition exfalso : tactic := builtin
|
opaque definition exfalso : tactic := builtin
|
||||||
|
opaque definition congruence : tactic := builtin
|
||||||
opaque definition rotate_left (k : num) := builtin
|
opaque definition rotate_left (k : num) := builtin
|
||||||
opaque definition rotate_right (k : num) := builtin
|
opaque definition rotate_right (k : num) := builtin
|
||||||
definition rotate (k : num) := rotate_left k
|
definition rotate (k : num) := rotate_left k
|
||||||
|
|
|
@ -136,7 +136,7 @@
|
||||||
"generalize" "generalizes" "clear" "clears" "revert" "reverts" "back" "beta" "done" "exact" "rexact"
|
"generalize" "generalizes" "clear" "clears" "revert" "reverts" "back" "beta" "done" "exact" "rexact"
|
||||||
"refine" "repeat" "whnf" "rotate" "rotate_left" "rotate_right" "inversion" "cases" "rewrite" "esimp"
|
"refine" "repeat" "whnf" "rotate" "rotate_left" "rotate_right" "inversion" "cases" "rewrite" "esimp"
|
||||||
"unfold" "change" "check_expr" "contradiction" "exfalso" "split" "existsi" "constructor" "left" "right"
|
"unfold" "change" "check_expr" "contradiction" "exfalso" "split" "existsi" "constructor" "left" "right"
|
||||||
"injection"))
|
"injection" "congruence"))
|
||||||
word-end)
|
word-end)
|
||||||
(1 'font-lock-constant-face))
|
(1 'font-lock-constant-face))
|
||||||
;; Types
|
;; Types
|
||||||
|
|
|
@ -13,6 +13,7 @@ name const * g_bool_ff = nullptr;
|
||||||
name const * g_bool_tt = nullptr;
|
name const * g_bool_tt = nullptr;
|
||||||
name const * g_char = nullptr;
|
name const * g_char = nullptr;
|
||||||
name const * g_char_mk = nullptr;
|
name const * g_char_mk = nullptr;
|
||||||
|
name const * g_congr = nullptr;
|
||||||
name const * g_dite = nullptr;
|
name const * g_dite = nullptr;
|
||||||
name const * g_empty = nullptr;
|
name const * g_empty = nullptr;
|
||||||
name const * g_empty_rec = nullptr;
|
name const * g_empty_rec = nullptr;
|
||||||
|
@ -139,6 +140,7 @@ void initialize_constants() {
|
||||||
g_bool_tt = new name{"bool", "tt"};
|
g_bool_tt = new name{"bool", "tt"};
|
||||||
g_char = new name{"char"};
|
g_char = new name{"char"};
|
||||||
g_char_mk = new name{"char", "mk"};
|
g_char_mk = new name{"char", "mk"};
|
||||||
|
g_congr = new name{"congr"};
|
||||||
g_dite = new name{"dite"};
|
g_dite = new name{"dite"};
|
||||||
g_empty = new name{"empty"};
|
g_empty = new name{"empty"};
|
||||||
g_empty_rec = new name{"empty", "rec"};
|
g_empty_rec = new name{"empty", "rec"};
|
||||||
|
@ -266,6 +268,7 @@ void finalize_constants() {
|
||||||
delete g_bool_tt;
|
delete g_bool_tt;
|
||||||
delete g_char;
|
delete g_char;
|
||||||
delete g_char_mk;
|
delete g_char_mk;
|
||||||
|
delete g_congr;
|
||||||
delete g_dite;
|
delete g_dite;
|
||||||
delete g_empty;
|
delete g_empty;
|
||||||
delete g_empty_rec;
|
delete g_empty_rec;
|
||||||
|
@ -392,6 +395,7 @@ name const & get_bool_ff_name() { return *g_bool_ff; }
|
||||||
name const & get_bool_tt_name() { return *g_bool_tt; }
|
name const & get_bool_tt_name() { return *g_bool_tt; }
|
||||||
name const & get_char_name() { return *g_char; }
|
name const & get_char_name() { return *g_char; }
|
||||||
name const & get_char_mk_name() { return *g_char_mk; }
|
name const & get_char_mk_name() { return *g_char_mk; }
|
||||||
|
name const & get_congr_name() { return *g_congr; }
|
||||||
name const & get_dite_name() { return *g_dite; }
|
name const & get_dite_name() { return *g_dite; }
|
||||||
name const & get_empty_name() { return *g_empty; }
|
name const & get_empty_name() { return *g_empty; }
|
||||||
name const & get_empty_rec_name() { return *g_empty_rec; }
|
name const & get_empty_rec_name() { return *g_empty_rec; }
|
||||||
|
|
|
@ -15,6 +15,7 @@ name const & get_bool_ff_name();
|
||||||
name const & get_bool_tt_name();
|
name const & get_bool_tt_name();
|
||||||
name const & get_char_name();
|
name const & get_char_name();
|
||||||
name const & get_char_mk_name();
|
name const & get_char_mk_name();
|
||||||
|
name const & get_congr_name();
|
||||||
name const & get_dite_name();
|
name const & get_dite_name();
|
||||||
name const & get_empty_name();
|
name const & get_empty_name();
|
||||||
name const & get_empty_rec_name();
|
name const & get_empty_rec_name();
|
||||||
|
|
|
@ -8,6 +8,7 @@ bool.ff
|
||||||
bool.tt
|
bool.tt
|
||||||
char
|
char
|
||||||
char.mk
|
char.mk
|
||||||
|
congr
|
||||||
dite
|
dite
|
||||||
empty
|
empty
|
||||||
empty.rec
|
empty.rec
|
||||||
|
|
|
@ -5,6 +5,7 @@ inversion_tactic.cpp whnf_tactic.cpp revert_tactic.cpp
|
||||||
assert_tactic.cpp clear_tactic.cpp expr_to_tactic.cpp location.cpp
|
assert_tactic.cpp clear_tactic.cpp expr_to_tactic.cpp location.cpp
|
||||||
rewrite_tactic.cpp util.cpp class_instance_synth.cpp init_module.cpp
|
rewrite_tactic.cpp util.cpp class_instance_synth.cpp init_module.cpp
|
||||||
change_tactic.cpp check_expr_tactic.cpp let_tactic.cpp contradiction_tactic.cpp
|
change_tactic.cpp check_expr_tactic.cpp let_tactic.cpp contradiction_tactic.cpp
|
||||||
exfalso_tactic.cpp constructor_tactic.cpp injection_tactic.cpp)
|
exfalso_tactic.cpp constructor_tactic.cpp injection_tactic.cpp
|
||||||
|
congruence_tactic.cpp)
|
||||||
|
|
||||||
target_link_libraries(tactic ${LEAN_LIBS})
|
target_link_libraries(tactic ${LEAN_LIBS})
|
||||||
|
|
123
src/library/tactic/congruence_tactic.cpp
Normal file
123
src/library/tactic/congruence_tactic.cpp
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||||
|
Released under Apache 2.0 license as described in the file LICENSE.
|
||||||
|
|
||||||
|
Author: Leonardo de Moura
|
||||||
|
*/
|
||||||
|
#include "library/util.h"
|
||||||
|
#include "library/constants.h"
|
||||||
|
#include "library/reducible.h"
|
||||||
|
#include "library/tactic/util.h"
|
||||||
|
#include "library/tactic/intros_tactic.h"
|
||||||
|
#include "library/tactic/expr_to_tactic.h"
|
||||||
|
|
||||||
|
namespace lean {
|
||||||
|
tactic congruence_tactic() {
|
||||||
|
auto fn = [=](environment const & env, io_state const & ios, proof_state const & s) -> optional<proof_state> {
|
||||||
|
goals const & gs = s.get_goals();
|
||||||
|
if (empty(gs)) {
|
||||||
|
throw_no_goal_if_enabled(s);
|
||||||
|
return none_proof_state();
|
||||||
|
}
|
||||||
|
goal const & g = head(gs);
|
||||||
|
expr t = g.get_type();
|
||||||
|
substitution subst = s.get_subst();
|
||||||
|
name_generator ngen = s.get_ngen();
|
||||||
|
auto tc = mk_type_checker(env, ngen.mk_child(), s.relax_main_opaque());
|
||||||
|
constraint_seq cs;
|
||||||
|
t = tc->whnf(t, cs);
|
||||||
|
expr lhs, rhs;
|
||||||
|
if (!is_eq(t, lhs, rhs)) {
|
||||||
|
throw_tactic_exception_if_enabled(s, "invalid 'congruence' tactic, goal is not an equality");
|
||||||
|
return none_proof_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer<goal> new_goals;
|
||||||
|
|
||||||
|
auto mk_eq_goal = [&](expr const & lhs, expr const & rhs) {
|
||||||
|
expr new_type = mk_eq(*tc, lhs, rhs);
|
||||||
|
expr new_meta = g.mk_meta(ngen.next(), new_type);
|
||||||
|
new_goals.push_back(goal(new_meta, new_type));
|
||||||
|
return some_expr(new_meta);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::function<optional<expr>(expr const &, expr const &, bool)> process =
|
||||||
|
[&](expr const & lhs, expr const & rhs, bool first) {
|
||||||
|
if (tc->is_def_eq(lhs, rhs, justification(), cs)) {
|
||||||
|
return some_expr(mk_refl(*tc, lhs));
|
||||||
|
}
|
||||||
|
if (!is_app(lhs) || !is_app(rhs)) {
|
||||||
|
if (first) {
|
||||||
|
throw_tactic_exception_if_enabled(s, "invalid 'congruence' tactic, left (and right) hand side of equation must be a function application");
|
||||||
|
return none_expr();
|
||||||
|
}
|
||||||
|
return mk_eq_goal(lhs, rhs);
|
||||||
|
}
|
||||||
|
expr lhs_fn = app_fn(lhs);
|
||||||
|
expr rhs_fn = app_fn(rhs);
|
||||||
|
expr lhs_fn_type = tc->whnf(tc->infer(lhs_fn, cs), cs);
|
||||||
|
expr rhs_fn_type = tc->whnf(tc->infer(rhs_fn, cs), cs);
|
||||||
|
if (!tc->is_def_eq(lhs_fn_type, rhs_fn_type, justification(), cs)) {
|
||||||
|
if (first) {
|
||||||
|
throw_tactic_exception_if_enabled(s, "invalid 'congruence' tactic, functions do not have the same type");
|
||||||
|
return none_expr();
|
||||||
|
}
|
||||||
|
return mk_eq_goal(lhs, rhs);
|
||||||
|
}
|
||||||
|
expr fn_type;
|
||||||
|
if (is_pi(lhs_fn_type)) {
|
||||||
|
fn_type = lhs_fn_type;
|
||||||
|
} else if (is_pi(rhs_fn_type)) {
|
||||||
|
fn_type = rhs_fn_type;
|
||||||
|
} else if (first) {
|
||||||
|
throw_tactic_exception_if_enabled(s, "invalid 'congruence' tactic, failed to compute function type");
|
||||||
|
return none_expr();
|
||||||
|
} else {
|
||||||
|
return mk_eq_goal(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!closed(binding_body(fn_type))) {
|
||||||
|
if (first) {
|
||||||
|
throw_tactic_exception_if_enabled(s, "invalid 'congruence' tactic, cannot be applied to dependent functions");
|
||||||
|
return none_expr();
|
||||||
|
}
|
||||||
|
return mk_eq_goal(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
expr lhs_arg = app_arg(lhs);
|
||||||
|
expr rhs_arg = app_arg(rhs);
|
||||||
|
expr arg_pr;
|
||||||
|
if (tc->is_def_eq(lhs_arg, rhs_arg, justification(), cs)) {
|
||||||
|
arg_pr = mk_refl(*tc, lhs_arg);
|
||||||
|
} else {
|
||||||
|
arg_pr = *mk_eq_goal(lhs_arg, rhs_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<expr> opt_fn_pr = process(lhs_fn, rhs_fn, false);
|
||||||
|
if (!opt_fn_pr)
|
||||||
|
return none_expr();
|
||||||
|
expr fn_pr = *opt_fn_pr;
|
||||||
|
|
||||||
|
expr A = binding_domain(fn_type);
|
||||||
|
expr B = binding_body(fn_type);
|
||||||
|
level l1 = sort_level(tc->ensure_type(A, cs));
|
||||||
|
level l2 = sort_level(tc->ensure_type(B, cs));
|
||||||
|
|
||||||
|
return some_expr(mk_app({mk_constant(get_congr_name(), {l1, l2}),
|
||||||
|
A, B, lhs_fn, rhs_fn, lhs_arg, rhs_arg, fn_pr, arg_pr}));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (optional<expr> pr = process(lhs, rhs, true)) {
|
||||||
|
std::reverse(new_goals.begin(), new_goals.end());
|
||||||
|
assign(subst, g, *pr);
|
||||||
|
proof_state new_ps(s, to_list(new_goals.begin(), new_goals.end(), tail(gs)), subst, ngen);
|
||||||
|
if (solve_constraints(env, ios, new_ps, cs))
|
||||||
|
return some_proof_state(new_ps);
|
||||||
|
}
|
||||||
|
return none_proof_state();
|
||||||
|
};
|
||||||
|
return tactic01(fn);
|
||||||
|
}
|
||||||
|
void initialize_congruence_tactic() { register_simple_tac(name{"tactic", "congruence"}, congruence_tactic); }
|
||||||
|
void finalize_congruence_tactic() {}
|
||||||
|
}
|
11
src/library/tactic/congruence_tactic.h
Normal file
11
src/library/tactic/congruence_tactic.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||||
|
Released under Apache 2.0 license as described in the file LICENSE.
|
||||||
|
|
||||||
|
Author: Leonardo de Moura
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
namespace lean {
|
||||||
|
void initialize_congruence_tactic();
|
||||||
|
void finalize_congruence_tactic();
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ Author: Leonardo de Moura
|
||||||
#include "library/tactic/exfalso_tactic.h"
|
#include "library/tactic/exfalso_tactic.h"
|
||||||
#include "library/tactic/constructor_tactic.h"
|
#include "library/tactic/constructor_tactic.h"
|
||||||
#include "library/tactic/injection_tactic.h"
|
#include "library/tactic/injection_tactic.h"
|
||||||
|
#include "library/tactic/congruence_tactic.h"
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
void initialize_tactic_module() {
|
void initialize_tactic_module() {
|
||||||
|
@ -53,9 +54,11 @@ void initialize_tactic_module() {
|
||||||
initialize_exfalso_tactic();
|
initialize_exfalso_tactic();
|
||||||
initialize_constructor_tactic();
|
initialize_constructor_tactic();
|
||||||
initialize_injection_tactic();
|
initialize_injection_tactic();
|
||||||
|
initialize_congruence_tactic();
|
||||||
}
|
}
|
||||||
|
|
||||||
void finalize_tactic_module() {
|
void finalize_tactic_module() {
|
||||||
|
finalize_congruence_tactic();
|
||||||
finalize_injection_tactic();
|
finalize_injection_tactic();
|
||||||
finalize_constructor_tactic();
|
finalize_constructor_tactic();
|
||||||
finalize_exfalso_tactic();
|
finalize_exfalso_tactic();
|
||||||
|
|
46
tests/lean/hott/congr_tac.hlean
Normal file
46
tests/lean/hott/congr_tac.hlean
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
example (f : nat → nat → nat) (a b c : nat) : b = c → f a b = f a c :=
|
||||||
|
begin
|
||||||
|
intro bc,
|
||||||
|
congruence,
|
||||||
|
assumption
|
||||||
|
end
|
||||||
|
|
||||||
|
example (f g : nat → nat → nat) (a b c : nat) : f = g → b = c → f a b = g a c :=
|
||||||
|
begin
|
||||||
|
intro fg bc,
|
||||||
|
congruence,
|
||||||
|
exact fg,
|
||||||
|
exact bc
|
||||||
|
end
|
||||||
|
|
||||||
|
example (f g : nat → nat → nat) (a b c : nat) : f = g → b = c → f a b = g a c :=
|
||||||
|
by intros; congruence; assumption
|
||||||
|
|
||||||
|
inductive list (A : Type) :=
|
||||||
|
| nil {} : list A
|
||||||
|
| cons : A → list A → list A
|
||||||
|
|
||||||
|
namespace list
|
||||||
|
notation `[` a `]` := list.cons a list.nil
|
||||||
|
notation `[` l:(foldr `,` (h t, cons h t) nil `]`) := l
|
||||||
|
notation h :: t := cons h t
|
||||||
|
variable {T : Type}
|
||||||
|
definition append : list T → list T → list T
|
||||||
|
| [] l := l
|
||||||
|
| (h :: s) t := h :: (append s t)
|
||||||
|
notation l₁ ++ l₂ := append l₁ l₂
|
||||||
|
end list
|
||||||
|
open list
|
||||||
|
|
||||||
|
example (a b : nat) : a = b → [a] ++ [b] = [b] ++ [a] :=
|
||||||
|
begin
|
||||||
|
intro ab,
|
||||||
|
congruence,
|
||||||
|
{congruence,
|
||||||
|
exact ab},
|
||||||
|
{congruence,
|
||||||
|
exact (eq.symm ab)}
|
||||||
|
end
|
||||||
|
|
||||||
|
example (a b : nat) : a = b → [a] ++ [b] = [b] ++ [a] :=
|
||||||
|
by intros; repeat (congruence | assumption | apply eq.symm)
|
34
tests/lean/run/congr_tac.lean
Normal file
34
tests/lean/run/congr_tac.lean
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import data.list
|
||||||
|
|
||||||
|
example (f : nat → nat → nat) (a b c : nat) : b = c → f a b = f a c :=
|
||||||
|
begin
|
||||||
|
intro bc,
|
||||||
|
congruence,
|
||||||
|
assumption
|
||||||
|
end
|
||||||
|
|
||||||
|
example (f g : nat → nat → nat) (a b c : nat) : f = g → b = c → f a b = g a c :=
|
||||||
|
begin
|
||||||
|
intro fg bc,
|
||||||
|
congruence,
|
||||||
|
exact fg,
|
||||||
|
exact bc
|
||||||
|
end
|
||||||
|
|
||||||
|
example (f g : nat → nat → nat) (a b c : nat) : f = g → b = c → f a b = g a c :=
|
||||||
|
by intros; congruence; assumption
|
||||||
|
|
||||||
|
open list
|
||||||
|
|
||||||
|
example (a b : nat) : a = b → [a] ++ [b] = [b] ++ [a] :=
|
||||||
|
begin
|
||||||
|
intro ab,
|
||||||
|
congruence,
|
||||||
|
{congruence,
|
||||||
|
exact ab},
|
||||||
|
{congruence,
|
||||||
|
exact (eq.symm ab)}
|
||||||
|
end
|
||||||
|
|
||||||
|
example (a b : nat) : a = b → [a] ++ [b] = [b] ++ [a] :=
|
||||||
|
by intros; repeat (congruence | assumption | apply eq.symm)
|
Loading…
Reference in a new issue