lean4-htt/tests/lean/derivingRpcEncoding.lean
Marc Huisinga 184dbae130
feat: reusable rpc refs (#8105)
This PR adds support for server-sided `RpcRef` reuse and fixes a bug
where trace nodes in the InfoView would close while the file was still
being processed.

The core of the trace node issue is that the server always serves new
RPC references in every single response to the client, which means that
the client is forced to reset its UI state.

In a previous attempt at fixing this (#8056), the server would memorize
the RPC-encoded JSON of interactive diagnostics (which includes RPC
references) and serve it for as long as it could reuse the snapshot
containing the diagnostics, so that RPC references are reused. The
problem with this was that the client then had multiple finalizers
registered for the same RPC reference (one for every reused RPC
reference that was served), and once the first reference was
garbage-collected, all other reused references would point into the
void.

This PR takes a different approach to resolve the issue: The meaning of
`$/lean/rpc/release` is relaxed from "Free the object pointed to by this
RPC reference" to "Decrement the RPC reference count of the object
pointed to by this RPC reference", and the server now maintains a
reference count to track how often a given `RpcRef` was served. Only
when every single served instance of the `RpcRef` has been released, the
object is freed. Additionally, the reuse mechanism is generalized from
being only supported for interactive diagnostics, to being supported for
any object using `WithRpcRef`. In order to make use of reusable RPC
references, downstream users still need to memorize the `WithRpcRef`
instances accordingly.

Closes #8053.

### Breaking changes

Since `WithRpcRef` is now capable of tracking its identity to decide
which `WithRpcRef` usage constitutes a reuse, the constructor of
`WithRpcRef` has been made `private` to discourage downstream users from
creating `WithRpcRef` instances with manually-set `id`s. Instead,
`WithRpcRef.mk` (which lives in `BaseIO`) is now the preferred way to
create `WithRpcRef` instances.
2025-06-03 12:35:12 +00:00

95 lines
2.4 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.Server.Rpc.Basic
open Lean Server
abbrev M := StateM RpcObjectStore
def M.run (x : ExceptT String M α) : Except String α :=
x.run' {}
def test (α : Type) [RpcEncodable α] (a : α) := M.run do
let json ← rpcEncode a
let _a : α ← ofExcept (rpcDecode json (← get))
return json
structure FooRef where
a : Array Nat
deriving Inhabited, TypeName
#eval do return test (WithRpcRef FooRef) (← WithRpcRef.mk default)
structure FooJson where
s : String
deriving FromJson, ToJson, Inhabited
structure Bar where
fooRef : WithRpcRef FooRef
fooJson : FooJson
deriving RpcEncodable, Inhabited
def Bar.mkDefault : BaseIO Bar := do return {
fooRef := ← WithRpcRef.mk default
fooJson := default
}
#eval do return test Bar (← Bar.mkDefault)
structure BarTrans where
bar : Bar
deriving RpcEncodable, Inhabited
#eval do return test BarTrans { bar := ← Bar.mkDefault }
structure Baz where
arr : Array String -- non-constant field
deriving RpcEncodable, Inhabited
#eval test Baz default
structure FooGeneric (α : Type) where
a : α
b? : Option α
deriving RpcEncodable, Inhabited
#eval test (FooGeneric Nat) default
#eval test (FooGeneric Nat) { a := 3, b? := some 42 }
inductive BazInductive
| baz (arr : Array Bar)
deriving RpcEncodable, Inhabited
#eval do return test BazInductive ⟨#[← Bar.mkDefault, ← Bar.mkDefault]⟩
inductive FooInductive (α : Type) where
| a : α → WithRpcRef FooRef → FooInductive α
| b : (n : Nat) → (a : α) → (m : Nat) → FooInductive α
deriving RpcEncodable, Inhabited
#eval do return test (FooInductive BazInductive) (.a default (← WithRpcRef.mk default))
#eval do return test (FooInductive BazInductive) (.b 42 default default)
inductive FooNested (α : Type) where
| a : α → Array (FooNested α) → FooNested α
deriving RpcEncodable, Inhabited
#eval test (FooNested BazInductive) (.a default #[default])
inductive FooParam (n : Nat) where
| a : Nat → FooParam n
deriving RpcEncodable, Inhabited
#eval test (FooParam 10) (.a 42)
inductive Unused (α : Type) | a
deriving RpcEncodable, Inhabited
structure NoRpcEncodable
#eval test (Unused NoRpcEncodable) default
structure UnusedStruct (α : Type)
deriving RpcEncodable, Inhabited
#eval test (UnusedStruct NoRpcEncodable) default
deriving instance Repr, RpcEncodable for Empty
#eval rpcDecode (α := Empty) .null {}