New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(tactic/frozen): add tactics about frozen locals #7652
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/- | ||
Copyright (c) 2021 Jannis Limperg. All rights reserved. | ||
Released under Apache 2.0 license as described in the file LICENSE. | ||
Authors: Jannis Limperg | ||
-/ | ||
|
||
import tactic.dependencies | ||
|
||
/-! | ||
# Tactics About Frozen Local Instances | ||
|
||
Under certain circumstances, Lean 'freezes' local constants which are instances. | ||
This means that these local constants cannot be reverted: `revert` will throw an | ||
error. The same applies to the dependencies of frozen local instances since | ||
reverting a dependency would imply that we must revert the frozen local instance | ||
as well. Example: | ||
|
||
```lean | ||
example {α} [i : has_add α] (x y : α) : x + y = y + x := | ||
begin | ||
revert i, -- error: i is a frozen local instance | ||
revert α, -- error: α is a dependency of i | ||
end | ||
``` | ||
|
||
Frozen local instances are good for performance: instances can be cached more | ||
effectively if we know that they cannot be removed from the context. | ||
|
||
This module provides various tactics related to frozen instances, e.g. to find | ||
out whether a local constant is frozen or to unfreeze local instances if | ||
necessary. We say that a local constant is frozen if it is either a frozen local | ||
instance or a dependency of a frozen local instance. | ||
|
||
Related core tactics: `tactic.frozen_local_instances`, | ||
`tactic.freeze_local_instances`, `tactic.unfreeze_local_instances`, | ||
`tactic.revertible_context`. | ||
-/ | ||
|
||
open native | ||
open name_set (local_list_to_name_set) | ||
|
||
namespace tactic | ||
|
||
/-- Returns true if local instances are frozen. Note that there may not | ||
actually be instances in the local context, so the frozen status may not have | ||
any effect. -/ | ||
meta def are_local_instances_frozen : tactic bool := do | ||
(some _) ← frozen_local_instances | pure ff, | ||
pure tt | ||
|
||
/-- Returns the frozen local instances. If local instances are not frozen, or if | ||
there are no local instances, the returned list is empty. Unlike | ||
`tactic.frozen_local_instances`, this tactic never returns local constants | ||
which do not appear in the context. -/ | ||
meta def frozen_local_instances' : tactic (list expr) := do | ||
(some frozen) ← frozen_local_instances | pure [], | ||
ctx ← local_context, | ||
let ctx := local_list_to_name_set ctx, | ||
pure $ frozen.filter (λ h, ctx.contains h.local_uniq_name) | ||
|
||
/-- Returns the unique names of all frozen local constants. -/ | ||
meta def frozen_locals_name_set : tactic name_set := do | ||
frozen ← frozen_local_instances', | ||
deps ← dependency_name_sets_of_hyps_inclusive frozen, | ||
pure $ deps.foldl name_set.union mk_name_set | ||
|
||
/-- Returns all frozen local constants. The local constants are returned in no | ||
particular order. -/ | ||
meta def frozen_locals : tactic (list expr) := do | ||
frozen ← frozen_local_instances', | ||
deps ← dependencies_of_hyps_inclusive frozen, | ||
pure deps.join | ||
|
||
/-- Returns all local constants that are *not* frozen. The locals are returned | ||
in the order in which they appear in the context. This is a more precise (but | ||
less efficient) version of `tactic.revertible_local_context`. -/ | ||
meta def non_frozen_locals : tactic (list expr) := do | ||
frozen ← frozen_locals_name_set, | ||
ctx ← local_context, | ||
pure $ ctx.filter (λ h, ¬ frozen.contains h.local_uniq_name) | ||
|
||
/-- `is_frozen h` returns true if the local constant `h` is frozen. -/ | ||
meta def is_frozen (h : expr) : tactic bool := do | ||
frozen ← frozen_locals_name_set, | ||
pure $ frozen.contains h.local_uniq_name | ||
|
||
/-- `any_is_frozen_name_set hs` returns true if any of the local constants whose | ||
unique names appear in `hs` are frozen. -/ | ||
meta def any_is_frozen_name_set (hs : name_set) : tactic bool := do | ||
frozen ← frozen_locals_name_set, | ||
pure $ frozen.fold ff (λ n b, b || hs.contains n) | ||
|
||
/-- `any_is_frozen hs` returns true if any of the local constants `hs` are | ||
frozen. -/ | ||
meta def any_is_frozen (hs : list expr) : tactic bool := | ||
any_is_frozen_name_set $ local_list_to_name_set hs | ||
|
||
/-- `unfreeze_if_necessary_for_name_set hs` unfreezes local instances if any of | ||
the local constants whose unique names appear in `hs` are frozen. -/ | ||
meta def unfreeze_if_necessary_for_name_set (hs : name_set) : tactic bool := do | ||
tt ← any_is_frozen_name_set hs | pure ff, | ||
unfreeze_local_instances, | ||
pure tt | ||
|
||
/-- `unfreeze_if_necessary_for hs` unfreezes local instances if any of the local | ||
constants in `hs` are frozen. You can use this before `revert_lst hs` to make | ||
sure that the `revert` will not fail due to frozen local instances. -/ | ||
meta def unfreeze_if_necessary_for (hs : list expr) : tactic bool := | ||
unfreeze_if_necessary_for_name_set $ local_list_to_name_set hs | ||
|
||
end tactic |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfreeze should always be followed by freeze, since thawed instances are slow. Please check out and use the
unfreezing
combinator.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, there's also
unfreezing_hyp
which is similar to this function.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I found the
unfreezing
combinator after I had written all this stuff. My eventual use case is to make sure that certainrevert
s aren't blocked by frozen instances. Is it even worth checking whether therevert
would be blocked (which can be somewhat expensive), versus justunfreezing (revert_lst ...)
without the check?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Freeze and unfreeze are fairly cheap. Freeze requires lean to check for every hypothesis whether it is a type class or not, but that's it. There should be no performance impact (after the freeze) if the instances do not change.
The main performance issue is staying in the unfrozen state, because this disables the TC cache.