lean4-htt/tests/elab/delabConst.lean
Joachim Breitner b763ab8a5e
refactor: keep IO.CancelToken task private, resolve promise before setting flag (#13569)
This PR addresses two review points on `IO.CancelToken`:

* `set` now resolves the underlying promise *before* writing the `Bool`
  fast-path flag, so observing `isSet = true` implies any synchronously
chained `onSet` callback has already run. The previous order (flag
first,
then resolve) was a subtle footgun: code seeing `isSet = true` could not
  rely on the cancellation task having fired.
* The underlying promise and the task it produces are kept private. The
prior `task : Task (Option Unit)` accessor is removed; consumers should
use `onSet` to react to cancellation. A comment on the structure records
  that re-exposing the task in the future requires re-auditing the order
  in `set` for races between the promise and the `Bool` flag.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 11:12:54 +00:00

240 lines
4.9 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.

import Lean
/-!
# Tests for delaboration of constants (and name unresolution)
-/
set_option linter.unusedVariables false
/-!
Make sure unresolution avoids conflicts.
-/
def A.B.x : Nat := 0
def A.B'.x : Nat := 0
/-- info: x : Nat -/
#guard_msgs in open A B in #check (A.B.x)
/-- info: B'.x : Nat -/
#guard_msgs in open A B in #check (A.B'.x)
/-- info: B.x : Nat -/
#guard_msgs in open A B B' in #check (A.B.x)
/-- info: B'.x : Nat -/
#guard_msgs in open A B B' in #check (A.B'.x)
/-!
Another conflict check.
-/
def B.x : Nat := 0
/-- info: A.B.x : Nat -/
#guard_msgs in open A in #check (A.B.x)
-- Resolution accepts this exact match.
/-- info: B.x : Nat -/
#guard_msgs in open A in #check (B.x)
/-!
Opening does not shadow the `x` that's already in `B`.
-/
namespace B
open A.B
/-- info: A.B.x : Nat -/
#guard_msgs in #check (_root_.A.B.x)
/-- info: x : Nat -/
#guard_msgs in #check (_root_.B.x)
/-- info: x : Nat -/
#guard_msgs in #check (x)
end B
/--
A global `x` needs the `_root_` qualifier.
-/
def x : Nat := 0
namespace B
/-- info: _root_.x : Nat -/
#guard_msgs in #check (_root_.x)
/-- info: x : Nat -/
#guard_msgs in #check (B.x)
end B
/-!
Name that shadows a local constant needs `_root_`.
-/
/--
trace: id : Nat
⊢ _root_.id 2 = 2
-/
#guard_msgs in
example (id : Nat) : _root_.id 2 = 2 := by
trace_state
rfl
/-!
Even with `id.{1}` notation, the name `id` must not shadow the local constant.
-/
/--
trace: id : Nat
⊢ Eq.{1} (_root_.id.{1} 2) 2
-/
#guard_msgs in
set_option pp.universes true in
example (id : Nat) : _root_.id 2 = 2 := by
trace_state
rfl
/-!
In `pp.fullNames` mode, local name shadowing is still checked.
-/
/--
trace: id : Nat
⊢ _root_.id 2 = 2
-/
#guard_msgs in
set_option pp.fullNames true in
example (id : Nat) : _root_.id 2 = 2 := by
trace_state
rfl
/-- info: `_root_.id -/
#guard_msgs in
variable (id : Nat) in
#eval Lean.Elab.Command.runTermElabM fun _ =>
Lean.unresolveNameGlobalAvoidingLocals ``id (fullNames := true)
/-!
`match` shadowing test. This used to print the first `match` arm as
`| Bool.true => 0`, which is incorrect, since `Bool.true` resolves to `MatchTest1.Bool.true`
-/
namespace MatchTest1
def Bool.true := 0
set_option linter.constructorNameAsVariable false in
def f (true : Bool) : Nat :=
match true with
| _root_.Bool.true => 0
| false => 1
/--
info: def MatchTest1.f : Bool → Nat :=
fun true =>
match true with
| _root_.Bool.true => 0
| false => 1
-/
#guard_msgs in
#print f
/-- info: Bool.true : Nat -/
#guard_msgs in #check (Bool.true)
end MatchTest1
/-!
`match` shadowing test. This used to print the first `match` arm as
`| Bool.true => 0`, which is incorrect, since `Bool.true` resolves to the local variable.
-/
namespace MatchTest2
set_option linter.constructorNameAsVariable false in
def f (true : Bool) : Nat :=
let Bool.true := 1
match true with
| _root_.Bool.true => 0
| false => 1
/--
info: def MatchTest2.f : Bool → Nat :=
fun true =>
have Bool.true := 1;
match true with
| _root_.Bool.true => 0
| false => 1
-/
#guard_msgs in
#print f
end MatchTest2
/-!
`match` shadowing test. Similar to `MatchTest2`, but `| Bool.false =>` is correct.
-/
namespace MatchTest3
set_option linter.constructorNameAsVariable false in
def f (true : Bool) :=
let Bool.true := true
let false := true
match true with
| _root_.Bool.true => false
| Bool.false =>
match true with
| Bool.false => false
| false => Bool.true
/--
info: def MatchTest3.f : Bool → Bool :=
fun true =>
have Bool.true := true;
have false := true;
match true with
| _root_.Bool.true => false
| Bool.false =>
match true with
| Bool.false => false
| false => Bool.true
-/
#guard_msgs in open Bool in #print f
end MatchTest3
namespace PrvTest.NS1
private abbrev fst : Nat × Nat → Nat := fun s => s.1
private abbrev snd : Nat × Nat → Nat := fun s => s.2
/-!
The private names get unresolved. Previously this would print as
`PrvTest.NS1.fst (x, x) = PrvTest.NS1.snd (x, x)`.
-/
/--
trace: x : Nat
⊢ fst (x, x) = snd (x, x)
-/
#guard_msgs in
example (x : Nat) : fst (x,x) = snd (x,x) := by
trace_state
rfl
end NS1
export NS1 (fst)
/-!
Private names get unresolved with respect to the namespace, and aliases are respected too.
-/
/--
trace: x : Nat
⊢ fst (x, x) = NS1.snd (x, x)
-/
#guard_msgs in
example (x : Nat) : fst (x,x) = NS1.snd (x,x) := by
trace_state
rfl
end PrvTest
/-!
The name `IO.CancelToken.promise✝` is a private imported name.
-/
/--
info: def IO.CancelToken.set : IO.CancelToken → BaseIO Unit :=
fun tk => do
IO.Promise.resolve () (IO.CancelToken.promise✝ tk)
ST.Ref.set (IO.CancelToken.setRef✝ tk) true
-/
#guard_msgs in #print IO.CancelToken.set
/-!
Even if `IO` is opened, it won't print as `CancelToken.promise✝`, but the full name.
-/
/--
info: def IO.CancelToken.set : CancelToken → BaseIO Unit :=
fun tk => do
Promise.resolve () (IO.CancelToken.promise✝ tk)
ST.Ref.set (IO.CancelToken.setRef✝ tk) true
-/
#guard_msgs in open IO in #print IO.CancelToken.set