flan/lib/dune
Joseph Ferano c8091bdbf9 One unannotated add, answering 5 to the integers and 3.75 to the floats
The boundary and the operators, which are the two halves of dyn being a type
rather than a word the checker tolerates.

Typed to dyn is implicit and dyn to typed is not, and the asymmetry is the
design: boxing loses nothing and can happen wherever a dyn is wanted, while
unboxing can fail at run time on a value the compiler cannot inspect, so it
happens only where somebody wrote a type. Both go through expect, because
expect is already the one place a wanted type meets a produced one, and every
annotating site already calls it.

Literals take their width from the dyn, not from the default. (defvar x dyn 5)
holds an i64 five: the ABI carries one integer width, so the defaulting question
never arises, and the literal is built at i64 rather than boxed after defaulting
to i32 -- which also means 3000000000 is a dyn integer.

An operator with one dyn operand is the runtime's. binary has already checked
the second operand against the first, so a mixed pair arrives with the typed
side boxed and the fold only has to call flan_dyn_add instead of adding. The
comparisons answer bool and not a dyn holding one, because a comparison is
almost always the test of an if; a program that wants it as a value boxes it
again for free at that boundary. = and != never trap -- two values of unrelated
types are unequal, not an error -- and the orderings do.

Types.equal had no Dyn case, so dyn was equal to nothing including itself.

print hands the whole value to the runtime rather than walking it: every other
arm of the structural printer exists because a Flan value carries no header and
only the compiler knows what it is, and a dyn is the exact reverse.

The compiler carries the dyn runtime the way it already carries flan_rt.c, with
the header pasted in front of the stub so there is one self-contained
translation unit and one contract.
2026-09-19 05:55:48 +07:00

54 lines
2.5 KiB
Plaintext

(library
(name flan)
(libraries unix)
; -linkall because lib/macro.ml installs itself into Parse.expander at module
; initialisation and nothing references it. Without it the linker drops the
; module from every executable that does not name it -- bin/main.exe among
; them -- and a program calling a macro would fail with an unknown name
; instead of expanding. The alternative was an install call at every entry
; point, including ones in files this cannot reach.
(library_flags (-linkall))
; Running a macro means dlopening it into the compiler, and OCaml has no
; dlopen for ELF -- Dynlink loads OCaml. These are the stubs for it, and the
; only C the compiler itself is built from. See lib/dynload_stubs.c.
(foreign_stubs
(language c)
(names dynload_stubs))
; No (c_library_flags (-ldl)): since glibc 2.34 dlopen lives in libc itself
; and libdl is a stub, and naming it breaks the merged build -- the partial
; link -output-complete-obj performs cannot resolve -ldl, so `flan dev` in
; one process fails at the link with "cannot find -ldl" while the ordinary
; build is unaffected. Add it back only with a platform guard.
)
; 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.
; [dyn_source] is runtime/flan_dyn_stub.c with runtime/flan_dyn.h pasted in
; front of it, because the generated module is one string and the stub includes
; the header by name. THE MERGE REPLACES THE STUB WITH runtime/flan_dyn.c and
; this rule keeps its shape — the header stays the contract both sides are
; diffed against, and concatenating it here is what makes the compiler carry a
; self-contained translation unit the way it already carries flan_rt.c.
(rule
(target runtime_src.ml)
(deps
%{workspace_root}/runtime/flan_rt.c
%{workspace_root}/runtime/flan_dev.c
%{workspace_root}/runtime/flan_dyn.h
%{workspace_root}/runtime/flan_dyn_stub.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\nlet dyn_source = {c|\n")
(cat %{workspace_root}/runtime/flan_dyn.h)
(cat %{workspace_root}/runtime/flan_dyn_stub.c)
(echo "|c}\n")))))