A prelude function shadowed live keeps the prelude's own calls on the prelude's body, as a rebuild does

This commit is contained in:
Joseph Ferano 2026-09-25 22:28:58 +07:00
parent c41d27ec45
commit d07c7b4e2c
3 changed files with 65 additions and 6 deletions

View File

@ -1480,11 +1480,6 @@ Its signature changes in the session but its body is not recompiled, so every ca
stops on StaleCall naming a type nobody wrote. Proposal: recompile such callers.
Postponed 2026-09-25 while .fln takes priority.
** TODO A prelude function shadowed live is reached by the prelude's own calls
A defn of a prelude function's name sent to a running =flan dev= installs into the
host's cell for that name, so the prelude's calls compiled into the host follow it;
a rebuild gives them the prelude's again, as =Check.shadow_prelude= intends.
** DONE The dev loop, step 1: the reload primitive
A list of top-level forms is recompiled and installed into a running process, and
call sites compiled before those forms existed follow them through an indirection

View File

@ -1158,9 +1158,52 @@ let eval ?(origin = "<eval>") ?base ?forms ?pause ?(step = false) ?(running = tr
| None -> None)
program.Tast.fns
in
(* A name that takes over a prelude function's moves the prelude's body to
[Check.prelude_alias] and the prelude's own calls with it (see
[Check.shadow_prelude]). The process was built with those calls going
through the name's cell, which the new body is about to be installed
into, so the prelude's body is installed under its new name and every
body whose calls moved is compiled again: the prelude keeps its own
function, as a rebuild would give it. *)
let prelude_moved =
if not (List.exists (fun (f : Tast.fn) -> Check.internal_name f.Tast.name)
program.Tast.fns)
then []
else
let calls_moved (f : Tast.fn) (b : built) =
let hit = ref false in
let see (e : Tast.expr) =
match e.Tast.e with
| Tast.Call (m, _)
| Tast.FnAddr (Tast.Fnval m) | Tast.Closure (Tast.Fnval m, _)
when Check.internal_name m
&& not (List.exists
(fun (s : site) -> String.equal s.callee m) b.sites) ->
hit := true
| _ -> ()
in
List.iter (Tast.walk see) f.Tast.body;
List.iter (Tast.walk see) f.Tast.fdefers;
!hit
in
List.filter_map
(fun (f : Tast.fn) ->
if Check.internal_name f.Tast.name then
(if known t f.Tast.name || SM.mem f.Tast.name t.built then None
else Some f.Tast.name)
else
match SM.find_opt f.Tast.name t.built with
| Some b when calls_moved f b ->
(* A lifted clause is compiled with the body it came from. *)
(match f.Tast.fparent with
| Some p when p <> "<thick>" -> Some p
| _ -> Some f.Tast.name)
| _ -> None)
program.Tast.fns
in
let fns =
List.sort_uniq String.compare
(declared_fns @ def_inits @ from_generics @ new_instances)
(declared_fns @ def_inits @ from_generics @ new_instances @ prelude_moved)
in
(* A constant that changed and can be published: known to the host, not
consumed by the checker. The module stores its new value at the frame

View File

@ -265,6 +265,27 @@ let () =
if has c.Session.ir "flan_dev_cell" then
fail "a name the host has went through the registry";
(* A defn of a prelude function's name, sent live. The host's prelude calls
[rand-int] through the cell the new body goes into, so the prelude's body
moves to its own name and its callers are compiled again to call it, as
a rebuild would have them. Once: a second redefinition moves nothing. *)
(let t, _ = Session.create ~file:"programs/reload.flan" () in
let c =
Session.eval ~origin:"programs/reload.flan" t "(defn rand-int [] u64 4096)"
in
List.iter
(fun n ->
if not (List.mem n c.Session.fns) then
fail "shadowing rand-int live did not install %s: %s" n
(String.concat " " c.Session.fns))
[ "rand-int"; "prelude~/rand-int"; "rand"; "rand-int-range" ];
let c =
Session.eval ~origin:"programs/reload.flan" t "(defn rand-int [] u64 8)"
in
if c.Session.fns <> [ "rand-int" ] then
fail "redefining a shadowed rand-int again installed %s"
(String.concat " " c.Session.fns));
(* DWARF in a redefinition module, which is a property of the session and
not of the call. [Emit.redefinition] has taken a ~debug argument all
along and was tested with it; what was missing was anyone passing it, so