Skip to content

Commit

Permalink
feat(tactic/clear_value): preserve order and naming (#3700)
Browse files Browse the repository at this point in the history
  • Loading branch information
cipher1024 committed Aug 13, 2020
1 parent cacb54e commit d6ffc1a
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 11 deletions.
46 changes: 36 additions & 10 deletions src/tactic/core.lean
Original file line number Diff line number Diff line change
Expand Up @@ -497,17 +497,43 @@ meta def revert_deps (e : expr) : tactic ℕ := do
meta def is_local_def (e : expr) : tactic unit :=
retrieve $ do revert e, expr.elet _ _ _ _ ← target, skip

/-- `clear_value e` clears the body of the local definition `e`, changing it into a regular
hypothesis. A hypothesis `e : α := t` is changed to `e : α`.
/-- like `split_on_p p xs`, `partition_local_deps_aux vs xs acc` searches for matches in `xs`
(using membership to `vs` instead of a predicate) and breaks `xs` when matches are found.
whereas `split_on_p p xs` removes the matches, `partition_local_deps_aux vs xs acc` includes
them in the following partition. Also, `partition_local_deps_aux vs xs acc` discards the partition
running up to the first match. -/
private def partition_local_deps_aux {α} [decidable_eq α] (vs : list α) : list α → list α → list (list α)
| [] acc := [acc.reverse]
| (l :: ls) acc :=
if l ∈ vs then acc.reverse :: partition_local_deps_aux ls [l]
else partition_local_deps_aux ls (l :: acc)

/-- `partition_local_deps vs`, with `vs` a list of local constants,
reorders `vs` in the order they appear in the local context together
with the variables that follow them. If local context is `[a,b,c,d,e,f]`,
and that we call `partition_local_deps [d,b]`, we get `[[d,e,f], [b,c]]`.
The head of each list is one of the variables given as a parameter. -/
meta def partition_local_deps (vs : list expr) : tactic (list (list expr)) :=
do ls ← local_context,
pure (partition_local_deps_aux vs ls []).tail.reverse

/-- `clear_value [e₀, e₁, e₂, ...]` clears the body of the local definitions `e₀`, `e₁`, `e₂`, ... changing them into regular
hypotheses. A hypothesis `e : α := t` is changed to `e : α`. The order of locals `e₀`, `e₁`, `e₂` does not
matter as a permutation will be chosen so as to preserve type correctness.
This tactic is called `clearbody` in Coq. -/
meta def clear_value (e : expr) : tactic unit := do
n ← revert_after e,
is_local_def e <|>
pp e >>= λ s, fail format!"Cannot clear the body of {s}. It is not a local definition.",
let nm := e.local_pp_name,
(generalize' e nm >> clear e) <|>
fail format!"Cannot clear the body of {nm}. The resulting goal is not type correct.",
intron n
meta def clear_value (vs : list expr) : tactic unit := do
ls ← partition_local_deps vs,
ls.mmap' $ λ vs, do
{ revert_lst vs,
(expr.elet v t d b) ← target | fail format!"Cannot clear the body of {vs.head}. It is not a local definition.",
let e := expr.pi v binder_info.default t b,
type_check e <|> fail format!"Cannot clear the body of {vs.head}. The resulting goal is not type correct.",
g ← mk_meta_var e,
h ← note `h none g,
tactic.exact $ h d,
gs ← get_goals,
set_goals $ g :: gs },
ls.reverse.mmap' $ λ vs, intro_lst $ vs.map expr.local_pp_name

/-- A variant of `simplify_bottom_up`. Given a tactic `post` for rewriting subexpressions,
`simp_bottom_up post e` tries to rewrite `e` starting at the leaf nodes. Returns the resulting
Expand Down
2 changes: 1 addition & 1 deletion src/tactic/interactive.lean
Original file line number Diff line number Diff line change
Expand Up @@ -1228,7 +1228,7 @@ add_tactic_doc
/-- `clear_value n₁ n₂ ...` clears the bodies of the local definitions `n₁, n₂ ...`, changing them
into regular hypotheses. A hypothesis `n : α := t` is changed to `n : α`. -/
meta def clear_value (ns : parse ident*) : tactic unit :=
propagate_tags $ ns.reverse.mmap' $ λ n, get_local n >>= tactic.clear_value
propagate_tags $ ns.reverse.mmap get_local >>= tactic.clear_value

add_tactic_doc
{ name := "clear_value",
Expand Down
24 changes: 24 additions & 0 deletions test/tactics.lean
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,30 @@ begin
exact unit.star
end

/-- test `clear_value` and the preservation of naming -/
example : ∀ x y : ℤ, let z := x + y in x = z - y → x = y - z → true :=
begin
introv h h,
guard_hyp x := ℤ,
guard_hyp y := ℤ,
guard_hyp z := ℤ,
guard_hyp h := x = y - z,
suffices : true, -- test the type of the second assumption named `h`
{ clear h,
guard_hyp h := x = z - y,
assumption },
do { to_expr ```(z) >>= is_local_def },
clear_value z,
guard_hyp z := ℤ,
success_if_fail { do { to_expr ```(z) >>= is_local_def } },
guard_hyp h := x = y - z,
suffices : true,
{ clear h,
guard_hyp h := x = z - y,
assumption },
trivial
end

/- Test whether generalize' always uses the exact name stated by the user, even if that name already
exists. -/
example (n : Type) (k : ℕ) : k = 5 → unit :=
Expand Down

0 comments on commit d6ffc1a

Please sign in to comment.