flan/lib/dune
Joseph Ferano 22cc0bc1c2 Names that did not exist when the process started
Editing a defvar or a defn is a symbol the host exports. Adding one is not:
there is nothing to bind to and ELF cannot grow a symbol. runtime/flan_dev.c is
the two lookups that cover it - flan_dev_cell for a new function's cell,
flan_dev_global for a new global's storage - both idempotent, so the second
module to mention a name gets what the first one got. That is the whole point:
two modules with their own copy of a new function would each call their own,
and redefining it would update one of them.

The compiler picks per name. A name the host has is a symbol and costs one load
at a call site; a name it lacks is a registry lookup cached at install time in
a module-local slot, and costs two. The common case pays nothing for the
general one.

The redefinition unit is now a list of top-level forms rather than one
function. It has to be: v3 of the fixture adds a var and uses it from a
redefined bump, and splitting that into two loads leaves a module referring to
storage that does not exist yet. C-c C-c passes one name, C-c C-k passes a
file's worth, one path either way.

Four rules, each silent if broken. Every lookup resolves before any body is
published, or a caller reaches a function whose slots are still null - asserted
on the emitted flan_reload_install, since it cannot be race-tested.
flan_dev_global refuses a size change, which is the layout-drift rule's first
enforcement point rather than another exception to it. Nothing is ever
dlclosed, because a cell holds an address inside a module's text. And the table
is fixed capacity, because a module holds a cell's address for as long as it is
loaded and a realloc would strand it.

The test that separates this from a plausible wrong version is v4, which
redefines a name v3 introduced at run time. v3's bump is already installed and
is not rebuilt, so it picks v4 up only if its call goes through a cell both
modules found by the same name. Had v3 cached the function's address instead,
every other assertion would still pass and the transcript would read 246
instead of 432.

Sizes are spelled LLVM's way, ptrtoint getelementptr null 1, rather than by a
layout calculator in OCaml that would have to agree with LLVM's on every
target.
2026-09-10 21:34:31 +07:00

24 lines
729 B
Plaintext

(library
(name flan)
(libraries unix))
; The host shim is Flan's, not the user's, so the compiler carries it rather
; than looking for it in an install directory. Generated from the real .c files
; so there is only ever one copy to edit. flan_dev.c goes into a dev build
; only — it is the run-time name lookup a REPL needs and a release build has
; no use for.
(rule
(target runtime_src.ml)
(deps
%{workspace_root}/runtime/flan_rt.c
%{workspace_root}/runtime/flan_dev.c)
(action
(with-stdout-to
runtime_src.ml
(progn
(echo "let source = {c|\n")
(cat %{workspace_root}/runtime/flan_rt.c)
(echo "|c}\n\nlet dev_source = {c|\n")
(cat %{workspace_root}/runtime/flan_dev.c)
(echo "|c}\n")))))