lean4-htt/tests/pkg/module/Module/Basic.lean
Joachim Breitner b9243e19be
feat: make equational theorems of non-exposed defs private (#8519)
This PR makes the equational theorems of non-exposed defs private. If
the author of a module chose not to expose the body of their function,
then they likely don't want that implementation to leak through
equational theorems. Helps with #8419.

There is some amount of incidential complexity due to how `private`
works in lean, by mangling the name: lots of code paths that need now do
the right thing™ about private and non-private names, including the
whole reserved name machinery.

So this includes a number of refactorings:

* The logic for calculating an equational theorem name (or similar) is
now done by a single function, `mkEqLikeNameFor`, rather than all over
the place.

* Since the name of the equational theorem now depends on the current
context (in particular whether it’s a proper module, or a non-module
file), the forward map from declaration to equational theorem doesn’t
quite work anymore. This map is deleted; the list of equational theorems
are now always found by looking for declaration of the expected names
(`alreadyGenerated). If users define such theorems themselves (and make
it past the “do not allow reserved names to be declared”) they get to
keep both pieces.

* Because this map was deleted, mathlib’s `eqns` command can no longer
easily warn if equational lemmas have already been generated too early
(adaption branch exists). But in general I think lean could provide a
more principled way of supporting custom unfold lemmas, and ideally the
whole equational theorem machinery is just using that.

* The ReservedNamePredicate is used by `resolveExact`, so we need to
make sure that it returns the right name, including privateness. It is
not ok to just reserve both the private and non-private name but then
later in the ReservedNameAction produce just one of the two.
 
* We create `foo.def_eq` eagerly for well-founded recursion. This is
needed because we need feed in the proof of the rewriting done by
`wf_preprocess`. But if `foo.def_eq` is private in a module, then a
non-module importing it will still expect a non-private `foo.def_eq` to
exist. To patch that, we install a `copyPrivateUnfoldTheorem :
GetUnfoldEqnFn` that declares a theorem aliasing the private one. Seems
to work.
2025-06-04 11:52:08 +00:00

141 lines
2.7 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module
/-! Module docstring -/
/-- A definition. -/
def f := 1
#guard_msgs(drop warning) in
/-- A theorem. -/
theorem t : f = 1 := sorry
theorem trfl : f = 1 := rfl
@[expose] def fexp := 1
private def priv := 2
/-! Private decls should not be accessible in exported contexts. -/
/-- error: unknown identifier 'priv' -/
#guard_msgs in
abbrev h := priv
/-! Equational theorems tests. -/
def f_struct : Nat → Nat
| 0 => 0
| n + 1 => f_struct n
termination_by structural n => n
def f_wfrec : Nat → Nat → Nat
| 0, acc => acc
| n + 1, acc => f_wfrec n (acc + 1)
termination_by n => n
@[expose] def f_exp_wfrec : Nat → Nat → Nat
| 0, acc => acc
| n + 1, acc => f_exp_wfrec n (acc + 1)
termination_by n => n
@[inline] protected def Test.Option.map (f : α → β) : Option α → Option β
| some x => some (f x)
| none => none
/-- error: 'f.eq_def' is a reserved name -/
#guard_msgs in
def f.eq_def := 1
/-- error: 'fexp.eq_def' is a reserved name -/
#guard_msgs in
def fexp.eq_def := 1
/-- info: f.eq_def : f = 1 -/
#guard_msgs in
#check f.eq_def
/-- info: f.eq_unfold : f = 1 -/
#guard_msgs in
#check f.eq_unfold
/-- info: fexp.eq_def : fexp = 1 -/
#guard_msgs in
#check fexp.eq_def
/-- info: fexp.eq_unfold : fexp = 1 -/
#guard_msgs in
#check fexp.eq_unfold
/-- info: f_struct.eq_1 : f_struct 0 = 0 -/
#guard_msgs in
#check f_struct.eq_1
/--
info: f_struct.eq_def (x✝ : Nat) :
f_struct x✝ =
match x✝ with
| 0 => 0
| n.succ => f_struct n
-/
#guard_msgs in
#check f_struct.eq_def
/--
info: f_struct.eq_unfold :
f_struct = fun x =>
match x with
| 0 => 0
| n.succ => f_struct n
-/
#guard_msgs(pass trace, all) in
#check f_struct.eq_unfold
/-- info: f_wfrec.eq_1 (x✝ : Nat) : f_wfrec 0 x✝ = x✝ -/
#guard_msgs(pass trace, all) in
#check f_wfrec.eq_1
/--
info: f_wfrec.eq_def (x✝ x✝¹ : Nat) :
f_wfrec x✝ x✝¹ =
match x✝, x✝¹ with
| 0, acc => acc
| n.succ, acc => f_wfrec n (acc + 1)
-/
#guard_msgs in
#check f_wfrec.eq_def
/--
info: f_wfrec.eq_unfold :
f_wfrec = fun x x_1 =>
match x, x_1 with
| 0, acc => acc
| n.succ, acc => f_wfrec n (acc + 1)
-/
#guard_msgs in
#check f_wfrec.eq_unfold
/-- info: f_exp_wfrec.eq_1 (x✝ : Nat) : f_exp_wfrec 0 x✝ = x✝ -/
#guard_msgs in
#check f_exp_wfrec.eq_1
/--
info: f_exp_wfrec.eq_def (x✝ x✝¹ : Nat) :
f_exp_wfrec x✝ x✝¹ =
match x✝, x✝¹ with
| 0, acc => acc
| n.succ, acc => f_exp_wfrec n (acc + 1)
-/
#guard_msgs in
#check f_exp_wfrec.eq_def
/--
info: f_exp_wfrec.eq_unfold :
f_exp_wfrec = fun x x_1 =>
match x, x_1 with
| 0, acc => acc
| n.succ, acc => f_exp_wfrec n (acc + 1)
-/
#guard_msgs in
#check f_exp_wfrec.eq_unfold