Every citation of a moved document now resolves from where it is written
This commit is contained in:
parent
eb641cf8eb
commit
9d5689ffa2
200
NEXT.md
200
NEXT.md
@ -7,7 +7,7 @@ of taste, and it is worth your reading of it: `runtime/flan_rt.c:70-86` allocate
|
||||
`restart-case` that offers it, on that frame's own stack**, so the runtime cannot push one on a program's
|
||||
behalf. Offering `use-value` at a division would mean an alloca plus a push and pop emitted at every
|
||||
division in every checked build — which is the identical cost `lib/prelude.ml:70-76` already refuses, in
|
||||
prose and on the record, for indexing. `HANDOFF-arith.md` is the full write-up.
|
||||
prose and on the record, for indexing. `docs/handoffs/HANDOFF-arith.md` is the full write-up.
|
||||
|
||||
So the question back to you: **is signal-and-die-with-a-location enough for arithmetic, the way it is for
|
||||
`BoundsError`, or do you want `saturate` on the cast arm** — which is the one case where clamping is
|
||||
@ -26,14 +26,14 @@ Nothing is running and nothing is half-built. Read this first.
|
||||
`spike/x86/survey.sh` builds every program in `test/programs` and every probe in `spike/x86` both
|
||||
ways and compares **stdout, stderr and exit status**: **97 MATCH, 0 DIFFER, 0 refused by name.** That
|
||||
is up from 41/1/41 at the start of the day. Conditions, bounds checks, `Fnval`'s indirection cell and
|
||||
`--x86 --dev` all landed. `DISCUSS.md` items 16, 17 and 18 are the three reports.
|
||||
`--x86 --dev` all landed. `docs/DISCUSS.md` items 16, 17 and 18 are the three reports.
|
||||
|
||||
**`HANDOFF-x86-rt.md` at the root has the remaining seven items in order.** Item 1 is the substantial
|
||||
**`docs/handoffs/HANDOFF-x86-rt.md` at the root has the remaining seven items in order.** Item 1 is the substantial
|
||||
one and is the reason the backend exists:
|
||||
|
||||
> **A redefinition emitter in `lib/x86.ml`** — the counterpart to `Emit.redefinition`, producing a
|
||||
> `.so`. Until it exists the backend builds whole programs and cannot serve a single `C-c C-c`, which
|
||||
> is where `DISCUSS.md` item 13's ~21ms of a 35ms redefinition actually lives.
|
||||
> is where `docs/DISCUSS.md` item 13's ~21ms of a 35ms redefinition actually lives.
|
||||
|
||||
**And the constraint the last lane found, which is the thing not to rediscover:** publishing a cell an
|
||||
LLVM-built module can store into puts an edge on `lib/x86.ml`'s own licence that *"a dev build is
|
||||
@ -78,7 +78,7 @@ request and `Session.eval` commits below the checker.
|
||||
|
||||
## Queued, nothing blocked on a decision
|
||||
|
||||
1. `HANDOFF-x86-rt.md`'s seven items, item 1 first.
|
||||
1. `docs/handoffs/HANDOFF-x86-rt.md`'s seven items, item 1 first.
|
||||
2. `text_codepoints_loading` and `textures_image_processing` — ordinary porting now that
|
||||
`slice-from-ptr` exists.
|
||||
3. Generic structs and `$n` array lengths — decided, unblocked, sections below. **Stopped once
|
||||
@ -101,7 +101,7 @@ something that is not memory. Their old entries stand; do not schedule either.
|
||||
**Built.** `flan_slice_promise_error` is its own function in `runtime/flan_rt.c`, shaped like the two
|
||||
beside it — it signals `BoundsError`, walks the handlers, offers the break loop, and only then falls
|
||||
through to a message and a status. The sentence names the promise and the number that broke it, and a
|
||||
second line says what is *not* checked. See [`BUILT.md`](BUILT.md), "`slice-from-ptr` refuses in its
|
||||
second line says what is *not* checked. See [`docs/BUILT.md`](docs/BUILT.md), "`slice-from-ptr` refuses in its
|
||||
own words". The original entry follows.
|
||||
|
||||
Small, and known rough at the time it shipped. `(slice-from-ptr p n)` with a **computed** negative
|
||||
@ -127,10 +127,10 @@ is the whole reason it was left. Say what the caller promised and what was passe
|
||||
**Named here because it is not on the list below and would otherwise be discovered late.**
|
||||
|
||||
Finishing items 1-6 below gives a second **whole-program** compiler. `--x86` is refused with `--dev`
|
||||
today, deliberately, and `DISCUSS.md` item 16 says it has no cells. So at the end of that list the
|
||||
today, deliberately, and `docs/DISCUSS.md` item 16 says it has no cells. So at the end of that list the
|
||||
backend can build `sand.flan` and cannot serve a single `C-c C-c`.
|
||||
|
||||
What the backend was for is `DISCUSS.md` item 13's measurement: `llc` and `ld` are **about 21ms of a
|
||||
What the backend was for is `docs/DISCUSS.md` item 13's measurement: `llc` and `ld` are **about 21ms of a
|
||||
35ms redefinition**, and removing them is the only thing that touches that number. Collecting it means
|
||||
emitting a **redefinition module** — one function, compiled and installed into a running process
|
||||
through an indirection cell — which is what `Build.shared` does on the LLVM path.
|
||||
@ -150,13 +150,13 @@ Concretely, and none of it is scoped yet:
|
||||
- **DWARF.** `flan dev --debug` exists and `--x86` refuses `--debug`. A dev backend that cannot be
|
||||
debugged is a narrower tool than the one it replaces.
|
||||
- **Then measure, because the whole case rests on it.** If a hand-written backend does not actually
|
||||
beat 35ms end to end, the argument for it was wrong and that is worth knowing. `DISCUSS.md` item 13
|
||||
beat 35ms end to end, the argument for it was wrong and that is worth knowing. `docs/DISCUSS.md` item 13
|
||||
says the backend is decided at step 4 on a measurement taken at step 3, and this is that
|
||||
measurement finally being takeable.
|
||||
|
||||
## The backend, in the order that finishes it
|
||||
|
||||
`DISCUSS.md` item 16 is the report and its verdict is the sequencing: *"the wiring is done and it was
|
||||
`docs/DISCUSS.md` item 16 is the report and its verdict is the sequencing: *"the wiring is done and it was
|
||||
the easy half. What is left is conditions, and the measurement moved them from 'first obstacle' to
|
||||
'the only obstacle'."* 40 of 111 programs lower today; the other 40 are refused by name.
|
||||
|
||||
@ -211,7 +211,7 @@ question 4 seriously now that there are two backends that can disagree.
|
||||
policy that distinguishes it from `two_process`. `HANDOFF-f1.md` is gone with it.
|
||||
- ~~**The memcheck half of the registry**~~ — done. `flan_arena_proc`'s `free-all` issues memcheck's
|
||||
`MAKE_MEM_UNDEFINED` over the arena, vendored rather than included; it is a control in
|
||||
`test_valgrind.ml` now, and `BUILT.md` carries the cost. The registry answer and the memcheck
|
||||
`test_valgrind.ml` now, and `docs/BUILT.md` carries the cost. The registry answer and the memcheck
|
||||
answer stay two claims.
|
||||
|
||||
## Not in the freeze, because it is already decided and unblocked
|
||||
@ -236,7 +236,7 @@ everything below it is the standing queue and the decision record.
|
||||
- **`pause` marking, both halves.** `C-u` before an eval marks the innermost form, `C-u C-u` the
|
||||
top-level one.
|
||||
- **`spy-num`.** A watch slot keeps count, min, max, last, mean, windowed to the editor's last tick.
|
||||
- **Frame rollback**, `PORTING.md` Tier 1 item 6. `restore` goes in the `continue` clause, not a defer.
|
||||
- **Frame rollback**, `docs/PORTING.md` Tier 1 item 6. `restore` goes in the `continue` clause, not a defer.
|
||||
- **The dev allocation registry is finished.** `M-x flan-inspect-address`, `M-x flan-allocations`,
|
||||
`M-x flan-leaks`.
|
||||
- **15 raylib examples**, an idiomatic layer over the bindings, and `vendor/raylib/vector.flan`.
|
||||
@ -252,7 +252,7 @@ everything below it is the standing queue and the decision record.
|
||||
|
||||
~~**`worktree-agent-a859480edc827ab73` — macros importable from a package.**~~ **Finished.** The suite
|
||||
is green, the acceptance wiring asserts both halves of the rule, and the dev loop is covered. See
|
||||
BUILT.md, "A package may declare a macro, and its name is the package's".
|
||||
docs/BUILT.md, "A package may declare a macro, and its name is the package's".
|
||||
|
||||
The finding is worth keeping: the old refusal claimed collecting a package's macros needed a second
|
||||
import resolver at the Form level. It did not. The file being compiled is parsed before `Load` runs
|
||||
@ -260,7 +260,7 @@ too, so no shape of the feature could have left import resolution where it was
|
||||
forms now and uses the one resolver that always existed.
|
||||
|
||||
~~One gap left, and it is older than this work: **`C-x C-e` expands no macros at all.**~~ **Done** —
|
||||
see [`BUILT.md`](BUILT.md), "`C-x C-e` expands too, and a declaration is not an expression". It was
|
||||
see [`docs/BUILT.md`](docs/BUILT.md), "`C-x C-e` expands too, and a declaration is not an expression". It was
|
||||
the wrap `Parse.decl` already had, and the decision it was waiting on came out as: an expression that
|
||||
expands to a declaration is **refused by name**, in `Parse.expr`'s head dispatch, so a `defn` nested
|
||||
anywhere in the expansion and a `defvar` typed by hand get the same sentence. The spin refusal fires
|
||||
@ -269,7 +269,7 @@ expands is the prelude's macros and the imported packages' — and, since the la
|
||||
the file's own as well.
|
||||
|
||||
Two things left behind it, neither this lane's. ~~**A session expands no macro the buffer itself
|
||||
declares**~~ — **done**, see [`BUILT.md`](BUILT.md), "A session expands the buffer's own macros". The
|
||||
declares**~~ — **done**, see [`docs/BUILT.md`](docs/BUILT.md), "A session expands the buffer's own macros". The
|
||||
session seeds its file's own `defmacro`s in `Session.create`, from the same read that produced
|
||||
`decls`, and an evaluated `defmacro` joins the set so the next evaluation can call it — the shape
|
||||
`defn` already has. Re-reading the file was the alternative and was refused: it would put
|
||||
@ -303,7 +303,7 @@ the Emacs side". **Superseded** — the registry lane built that properly. Delet
|
||||
|
||||
## For siam-farmer
|
||||
|
||||
`PORTING.md`'s verdict is unconditional now: the game's state is **fixed arrays with counts**, so it
|
||||
`docs/PORTING.md`'s verdict is unconditional now: the game's state is **fixed arrays with counts**, so it
|
||||
fits in `defvar` globals, nothing is move-only, and generics is off its critical path. Tier 0 and Tier 1
|
||||
items 4, 5 and 6 are all done. Nothing blocks writing it.
|
||||
|
||||
@ -407,7 +407,7 @@ allocation**, which is Odin's `Small_Array` and a good fit for a game that refus
|
||||
(defvar enemies (Small-Array 64 Enemy))
|
||||
```
|
||||
|
||||
`PORTING.md`'s recommendation for the game's state was fixed arrays with counts held in `defvar` globals, kept
|
||||
`docs/PORTING.md`'s recommendation for the game's state was fixed arrays with counts held in `defvar` globals, kept
|
||||
deliberately away from `Vec` so nothing is move-only. `(Small-Array $n $t)` is that pattern with a type behind it
|
||||
instead of two variables kept in step by hand. That is the argument for building this, and it is stronger than
|
||||
"Odin has it".
|
||||
@ -436,7 +436,7 @@ separate question and should be answered deliberately rather than falling out of
|
||||
|
||||
## Decided 2026-09-13: generics by monomorphisation, checked abstractly, with `where` predicates
|
||||
|
||||
The spike answered it (`SPIKE-GENERICS.md`, on `worktree-agent-afcd2406f3660629b`): **it runs**, the whole feature
|
||||
The spike answered it (`docs/SPIKE-GENERICS.md`, on `worktree-agent-afcd2406f3660629b`): **it runs**, the whole feature
|
||||
is `lib/check.ml` and nothing else in `lib/`, and instantiation is under the noise floor at 1.8ms whole-program
|
||||
re-check. The bill is `llc`+`ld` at **+1.7ms per extra body**, so a generic used at three types adds ~7ms to a
|
||||
redefinition. `plan.org` is updated; what follows is the decision and the reasoning that does not belong there.
|
||||
@ -615,13 +615,13 @@ why they are here and not in a binding list.
|
||||
check now accepts an enum where the header says `int`, symmetrically, and still refuses
|
||||
anything that is not four bytes. `Camera3D.projection` is a `CameraProjection` again and the
|
||||
`rl/camera-projection` helper is gone; `.projection :perspective` resolves at the construction
|
||||
site, so the keyword half of the problem went away with it. See BUILT.md.
|
||||
site, so the keyword half of the problem went away with it. See docs/BUILT.md.
|
||||
|
||||
3. ~~**The header check does not reach `defconst` or `defenum`.**~~ **Closed.** It reaches both.
|
||||
`bindings` gained `enum`, `const` and `constant` lines that say what a Flan constant is called in
|
||||
C; every mapped name is compared by value, and a name the mapping cannot find, a rule that
|
||||
reaches nothing, and a `defenum` with no line at all are each reported rather than skipped. All
|
||||
eight raylib enums and all 16 `ConfigFlags` bits check out against 5.5. See BUILT.md.
|
||||
eight raylib enums and all 16 `ConfigFlags` bits check out against 5.5. See docs/BUILT.md.
|
||||
|
||||
4. ~~**raymath is `static inline`, so there is no symbol to bind.**~~ **Closed for the vector half.**
|
||||
`Clamp`, `Vector2Add`, `Remap` and the rest exist only in the header and `declare-c` has nothing
|
||||
@ -662,7 +662,7 @@ tip themselves. A lane that did not notice would produce plausible work against
|
||||
|
||||
**Built.** All seven items landed: the `eval_expr` pause path, `flan-dev--eval` sending the position,
|
||||
`flan-eval-defun` taking `C-u`, the overlay and its face, `test/programs/dev-pause.flan`, the `test_dev.ml` block and
|
||||
the `emacs/test-flan-dev.el` one. `HANDOFF-f2.md` is deleted; the reasoning is in `BUILT.md`, "A breakpoint is a
|
||||
the `emacs/test-flan-dev.el` one. `HANDOFF-f2.md` is deleted; the reasoning is in `docs/BUILT.md`, "A breakpoint is a
|
||||
function call, and the editor only says where".
|
||||
|
||||
### ~~From `HANDOFF-f1.md`~~ — the socket flake is fixed, and everything that followed it is done
|
||||
@ -730,7 +730,7 @@ actually worth having. The timers are 30s, down from a minute, because the build
|
||||
|
||||
### ~~From `HANDOFF-f3.md` — the watch is built; two tests are not~~
|
||||
|
||||
`spy-num` landed and `PORTING.md` Tier 1 item 5 is closed. The `spy` half already existed — the finding that
|
||||
`spy-num` landed and `docs/PORTING.md` Tier 1 item 5 is closed. The `spy` half already existed — the finding that
|
||||
reframed the task is that the table, the agent commands, the buffer and the ghost text were all there at `344e571`,
|
||||
and only the numeric accumulator was missing. A slot keeps count, min, max, last and mean; the window is since the
|
||||
editor's last tick rather than cumulative, because cumulative min/max reach the session's extremes within seconds of
|
||||
@ -745,19 +745,19 @@ stopped program does not sample, so an epoch-aware reader would blank the watch
|
||||
is exactly when the numbers are being read. What was actually wrong was in the editor — `flan-watch--tick` sent
|
||||
`:reset t` five times a second at a program that could not answer it — and it is now guarded on `flan-dev--stopped`,
|
||||
with the read still going out every tick. `emacs/test-flan-watch.el` pins both halves. `HANDOFF-f3.md` is deleted;
|
||||
the reasoning is in `BUILT.md`, "A hot loop keeps five numbers, and the window is the editor's".
|
||||
the reasoning is in `docs/BUILT.md`, "A hot loop keeps five numbers, and the window is the editor's".
|
||||
|
||||
### Not started: five more raylib core examples
|
||||
|
||||
Ten are in `examples/`. Five more were queued and the lane was stopped before it wrote anything, so there is nothing
|
||||
to pick up — only the brief. Pick for what they exercise of the bindings rather than for how they look, since the
|
||||
point of the corpus is finding gaps in `vendor/raylib/raylib.flan`. Upstream is
|
||||
`vendor/raylib/web/raylib-5.5/examples/core/`. `PORTING.md` §1's rule binds: a raylib function on a real per-frame
|
||||
`vendor/raylib/web/raylib-5.5/examples/core/`. `docs/PORTING.md` §1's rule binds: a raylib function on a real per-frame
|
||||
path is hand-written and header-checked, not left to the opt-in `FLAN_RAYLIB_H` import, because the default build
|
||||
has no `FLAN_RAYLIB_H` and still has to draw. An enum value has to be added by hand whatever happens — the importer
|
||||
generates functions and only functions.
|
||||
|
||||
### Landed: frame rollback, `PORTING.md` Tier 1 item 6
|
||||
### Landed: frame rollback, `docs/PORTING.md` Tier 1 item 6
|
||||
|
||||
`test/programs/frame-rollback.flan`, with three acceptance rows beside `bounds-condition.flan`'s (plain, `-O0`,
|
||||
dev). Nothing was added to the language, which was the premise: `snapshot` is `(set grid-backup grid)` and
|
||||
@ -820,7 +820,7 @@ written in `vendor/raylib` now, and have not been.
|
||||
|
||||
## ~~Queued: a restart is not a transaction, and the docs must say so~~ — **landed**
|
||||
|
||||
See [`BUILT.md`](BUILT.md), "A restart is not a transaction". **This entry was stale** — all three places already
|
||||
See [`docs/BUILT.md`](docs/BUILT.md), "A restart is not a transaction". **This entry was stale** — all three places already
|
||||
carried the note when it was re-read: `conditions.org` under *Gotchas*, `spec-conditions.md` §5, and
|
||||
`web/index.html`'s restart gotcha list, which was rewritten in place rather than gaining a second bullet beside the
|
||||
existing one. Nothing was left to write.
|
||||
@ -841,7 +841,7 @@ skip a frame and carry on rather than die — exactly the case where a non-idemp
|
||||
|
||||
## ~~Queued: a dev-build allocation registry~~ — **landed; one item left, and it is not a registry item**
|
||||
|
||||
Items 1 to 5 are built and the test is written. `BUILT.md`'s *"An address answers with a type"* is the account of the
|
||||
Items 1 to 5 are built and the test is written. `docs/BUILT.md`'s *"An address answers with a type"* is the account of the
|
||||
table, the note and the inspector's pointer arm; *"An address you have in your hand"* is the account of the reader —
|
||||
the address root, the breakdown, the leak report, and what "at exit" turned out to mean.
|
||||
|
||||
@ -861,7 +861,7 @@ do not need the registry. This is for plain structs, `Vec`, `Map` and pool stora
|
||||
|
||||
**On cost, as built.** One insert per allocation, always on in a dev build, no opt-out — the author's instruction,
|
||||
followed literally. Nothing was built per-region, no range recording, no per-allocator opt-out. Revisit only if a real
|
||||
program shows a problem, and `BUILT.md` still names **two** places a release build is not quite free: the readers
|
||||
program shows a problem, and `docs/BUILT.md` still names **two** places a release build is not quite free: the readers
|
||||
added since are functions nothing in a release build calls, and the exit report is registered by `atexit` from inside
|
||||
`flan_dev_reg_enable` rather than by a file-scope destructor, precisely so that it is not a third.
|
||||
|
||||
@ -871,14 +871,14 @@ Three things, in order. The first two are one line each and unblock a real game.
|
||||
|
||||
**1. `DrawTexturePro` is not bound — DONE.** It is `draw-texture-pro` in `vendor/raylib/raylib.flan` now, hand-written
|
||||
beside `draw-texture-rec` and read off a raylib header rather than remembered. `image-from-image` and `window-ready?`
|
||||
went in with it. The rule the gap exposed is written down in `BUILT.md` and `PORTING.md` §1: *a raylib function on a
|
||||
went in with it. The rule the gap exposed is written down in `docs/BUILT.md` and `docs/PORTING.md` §1: *a raylib function on a
|
||||
game's per-frame path is hand-written and header-checked, not left to the opt-in import* — the import widens the
|
||||
surface and must not be load-bearing, because the default build has no `FLAN_RAYLIB_H` and still has to draw.
|
||||
|
||||
**2. `Key` has no `left-shift` — DONE.** `left-shift 340`, and nothing else: `PORTING.md` §5 checked every other enum
|
||||
**2. `Key` has no `left-shift` — DONE.** `left-shift 340`, and nothing else: `docs/PORTING.md` §5 checked every other enum
|
||||
value the game touches and they were all already right.
|
||||
**1. `DrawTexturePro` — done, and not by a hand-written line.** It was the one true blocker for `siam-farmer`
|
||||
(see `PORTING.md`: every tile in both implementations goes through it, and neither `DrawTextureRec` nor
|
||||
(see `docs/PORTING.md`: every tile in both implementations goes through it, and neither `DrawTextureRec` nor
|
||||
`DrawTextureEx` substitutes). It was reachable only through the opt-in `FLAN_RAYLIB_H` import. The bindings are
|
||||
committed now, so `rl/draw-texture-pro` is in `vendor/raylib/generated.flan` and a default build has it. **Nothing
|
||||
should add it by hand** — a second `declare-c` for the same C symbol is refused for the whole program.
|
||||
@ -892,7 +892,7 @@ still a hand edit: the importer generates functions and only functions, so no `d
|
||||
the break loop, and tail into the old `flan_bounds_fail` message and status only if nothing answered. `Vec`'s two
|
||||
checks are plumbed the same way, since `(at v i)` and `(at arr i)` are one form in the source.
|
||||
|
||||
**No restart is established at the failing index**, and `BUILT.md` has the argument: `retry` exists for allocation and
|
||||
**No restart is established at the failing index**, and `docs/BUILT.md` has the argument: `retry` exists for allocation and
|
||||
for files because those attempts are *repeatable*, and nothing a handler can do makes index 51 valid for a length-50
|
||||
array. `use-value` for the index would cost every indexing operation a restart frame and buy a silently different
|
||||
element. What answers a bad index is the restart the program already had — a frame loop's `continue`, `sand.flan`'s
|
||||
@ -912,22 +912,22 @@ resumes with the session intact.
|
||||
different kind of failure — the region the container lived in was released — and there is no frame to go back to that
|
||||
would not read freed memory. The map path was left alone rather than converted half-way.
|
||||
|
||||
### What is left on `PORTING.md`'s list
|
||||
### What is left on `docs/PORTING.md`'s list
|
||||
|
||||
Tier 0 is finished, and so is item 5. **The watch for a running program is done** — the `spy` half was already
|
||||
built (a pushed table, the watch buffer, inline ghost text) and the `spy-num` half landed 2026-09-13: a hot-loop slot
|
||||
keeps count/min/max/last/mean, the write path does no formatting, and the window is since the editor's last tick
|
||||
rather than cumulative, which is a deliberate divergence from `watch.clj` argued in `BUILT.md`, "A hot loop keeps
|
||||
rather than cumulative, which is a deliberate divergence from `watch.clj` argued in `docs/BUILT.md`, "A hot loop keeps
|
||||
five numbers, and the window is the editor's". **Item 6 landed the same day**:
|
||||
`test/programs/frame-rollback.flan` is the worked example — `snapshot` at the top of the frame, `restore` in the
|
||||
`continue` clause — and `bounds-condition.flan`'s half-written abandoned frame is the thought it finishes. That was
|
||||
the last Tier 1 item anyone was going to move. Items 7 (`drop`), 8 (generics) and 9 (`(read-edn T bytes)`) are still
|
||||
on that list and still deferred with reasons written beside each; none is a blocker for this game.
|
||||
|
||||
**What `PORTING.md` says NOT to build, with evidence:** escaping closures (one capture site, fixed by one parameter),
|
||||
**What `docs/PORTING.md` says NOT to build, with evidence:** escaping closures (one capture site, fixed by one parameter),
|
||||
`Handle`/pools, `Result`/`try`, `handler-case`, `loop`/`recur` and tail calls, user allocators, structural typing —
|
||||
**none has a customer in that code**. (`Handle` and the pool were built anyway, and on the other reason: they are the
|
||||
gate on classes. The finding stands and is why they were built small — see [`BUILT.md`](BUILT.md). `loop`/`recur` was
|
||||
gate on classes. The finding stands and is why they were built small — see [`docs/BUILT.md`](docs/BUILT.md). `loop`/`recur` was
|
||||
built too, and the finding stands there as well: what it is not is **tail calls**, which are still not built and still
|
||||
have no customer.) And **generics is not the blocker** there either: the element-changing maps are
|
||||
five-line load-time loops. That last one hangs on a design decision the report states flatly — whether the game's
|
||||
@ -940,9 +940,9 @@ byte-identical between 5.5 and the vendored 6.0.
|
||||
|
||||
Both lanes committed their main work and died on trailing polish; both are merged and the suite is green.
|
||||
|
||||
- ~~**`pause` marking from Emacs was not built.**~~ **Built**, both halves. See `BUILT.md`, "A breakpoint is a
|
||||
- ~~**`pause` marking from Emacs was not built.**~~ **Built**, both halves. See `docs/BUILT.md`, "A breakpoint is a
|
||||
function call, and the editor only says where".
|
||||
- ~~**Ghost text** for the watch window — values shown inline at the code they belong to — is noted and not designed.~~ **Built.** See `BUILT.md`, "Ghost text finds its anchor in the buffer, not in the table".
|
||||
- ~~**Ghost text** for the watch window — values shown inline at the code they belong to — is noted and not designed.~~ **Built.** See `docs/BUILT.md`, "Ghost text finds its anchor in the buffer, not in the table".
|
||||
- `tools/unit-return.py` is re-runnable; run it over any `.flan` file a lane wrote before the conversion landed.
|
||||
|
||||
# Where this is
|
||||
@ -951,11 +951,11 @@ Both lanes committed their main work and died on trailing polish; both are merge
|
||||
|
||||
**Branch `dev-loop`, 199 commits, working tree clean, `dune test` green.**
|
||||
|
||||
**[`DISCUSS.md`](DISCUSS.md) is what has been *asked* and not answered** — open questions with the repo context that
|
||||
**[`docs/DISCUSS.md`](docs/DISCUSS.md) is what has been *asked* and not answered** — open questions with the repo context that
|
||||
bears on each, so an investigation starts from what exists. Nothing in it is a decision or a task; when one becomes
|
||||
either, it moves here.
|
||||
|
||||
**`NEXT.md` is what is left. [`BUILT.md`](BUILT.md) is why the existing parts are the shape they are** — the reload
|
||||
**`NEXT.md` is what is left. [`docs/BUILT.md`](docs/BUILT.md) is why the existing parts are the shape they are** — the reload
|
||||
primitive, cells, the agent, the session, the daemon, the Emacs client, conditions, the FFI shim, the layout, and the
|
||||
order it all got built in. This file was half build log until it was split; do not let it become one again. When a
|
||||
track here finishes, its explanation moves there and its entry here goes away.
|
||||
@ -994,7 +994,7 @@ The expander is written and the exit criterion plan.org set for milestone 5 is m
|
||||
unchanged. Running `test/programs/macro-unless.flan` means the compiler built a shared object, `dlopen`ed it into
|
||||
itself and called a Flan function to find out what `(unless c a b)` means.
|
||||
|
||||
The full explanation is in [`BUILT.md`](BUILT.md), "Macros: the compiler dlopens the program". Four things worth
|
||||
The full explanation is in [`docs/BUILT.md`](docs/BUILT.md), "Macros: the compiler dlopens the program". Four things worth
|
||||
knowing before touching any of it, because each cost something to find:
|
||||
|
||||
- **A call inside a quasiquote is output, not a compile-order dependency.** A macro body that *calls* another macro
|
||||
@ -1017,10 +1017,10 @@ difference being a cached `.so`; and a hello-world carries eight bytes of it, be
|
||||
|
||||
### Landed — a C header is read, so a binding is checked instead of trusted
|
||||
|
||||
`lib/cimport.ml`, `lib/cjson.ml`, a `headers` file beside `link`. Full reasoning in `BUILT.md`, "The header is read
|
||||
now"; DISCUSS.md item 6 is rewritten down to the two decisions left, both the author's.
|
||||
`lib/cimport.ml`, `lib/cjson.ml`, a `headers` file beside `link`. Full reasoning in `docs/BUILT.md`, "The header is read
|
||||
now"; docs/DISCUSS.md item 6 is rewritten down to the two decisions left, both the author's.
|
||||
|
||||
The gap closed is the one `BUILT.md` recorded as *trusted*: `declare-c` generates the wrapper, the typedefs and the
|
||||
The gap closed is the one `docs/BUILT.md` recorded as *trusted*: `declare-c` generates the wrapper, the typedefs and the
|
||||
prototype from one declaration, so they agree with each other by construction and only the library could disagree —
|
||||
and nothing had a second opinion to disagree with. Now clang is asked for a JSON AST dump of the header (shelled out,
|
||||
never libclang — the dependency plan.org rejected; Zig has since left it too, for Aro) and both halves are compared
|
||||
@ -1030,14 +1030,14 @@ against it.
|
||||
all 172 hand-written `declare-c` agree exactly.** Against the 5.1-dev header also installed on this machine, ten real
|
||||
differences — nine functions that version lacks and one that gained a parameter — so picking the wrong header is
|
||||
loud. Both comparisons run at build time and stop the build; verified by permuting `Texture2D` and by putting `f64`
|
||||
where raylib says `float`, which is the hazard `BUILT.md` names and says only a test can catch.
|
||||
where raylib says `float`, which is the hazard `docs/BUILT.md` names and says only a test can catch.
|
||||
|
||||
**Costs, measured, because they decide the remaining question.** Release build +4ms warm — `Reach.link` already drops
|
||||
a wrapper nothing reachable calls, confirmed on the wasm32 case it exists for with 256 extra declarations in play.
|
||||
Redefinition 31.0ms → 46.5ms. Dev build +333ms cold, once per session, since `Build.shared` compiles no C. Reading the
|
||||
header is cached (64ms → 17ms), keyed like the object cache; the cache was built against a measurement, not a guess.
|
||||
|
||||
**Re-measured, and the 15.5ms was misattributed** — see BUILT.md, "Where that 15.5ms actually is". A `C-c C-c` reads
|
||||
**Re-measured, and the 15.5ms was misattributed** — see docs/BUILT.md, "Where that 15.5ms actually is". A `C-c C-c` reads
|
||||
no header: `Session.eval` puts the forms through `Load`, and forms with no `(import …)` in them touch no package. The
|
||||
15.5ms is `flan reload`'s, and `flan reload` is a fresh process — ~14.5ms of it is session startup and ~4ms of *that*
|
||||
is the header. What a redefinition really pays for an imported package is **+3.6ms per eval** in `Check` and in
|
||||
@ -1057,7 +1057,7 @@ Worth knowing before touching it:
|
||||
172 are already bound by hand and left alone. Widening the binding is a `defstruct`, not a list edit.
|
||||
- **No `defstruct` is generated, and that is load-bearing.** Generate them and the header becomes the authority on
|
||||
layout, and checking the package's layouts against it would be comparing the header with itself — which is exactly
|
||||
why `BUILT.md` rejected a `_Static_assert` as circular. Keeping them hand-written is what makes the check a second
|
||||
why `docs/BUILT.md` rejected a `_Static_assert` as circular. Keeping them hand-written is what makes the check a second
|
||||
source.
|
||||
- **A refusal is a demotion, not a drop** — Zig's `failDecl`, which `Load.refuse_hidden` already implemented for
|
||||
`main`. `rl/get-gamepad-name` is a name that exists, cannot be had, and says why at the use site.
|
||||
@ -1066,7 +1066,7 @@ Worth knowing before touching it:
|
||||
- **`test/headers/sample.h`** is the importer's table — one function per decision, committed, no raylib needed. The
|
||||
raylib acceptance case skips without `FLAN_RAYLIB_H`; that one does not.
|
||||
|
||||
Two things that are *not* done, and are 6a and 6b in DISCUSS.md: whether the header stays a build-time read or becomes
|
||||
Two things that are *not* done, and are 6a and 6b in docs/DISCUSS.md: whether the header stays a build-time read or becomes
|
||||
a committed generator (`flan import-c` already prints the lines, so it costs nothing more to switch), and whether the
|
||||
172 hand-written lines migrate. Neither is blocked on correctness. The argument for the first is weaker than it
|
||||
looked — committing the generated lines would save ~4ms of session startup and none of the +3.6ms per redefinition,
|
||||
@ -1081,7 +1081,7 @@ sibling of a call that worked did not — by excluding them and hand-writing the
|
||||
|
||||
### Landed 2026-09-12 — six tracks, one session
|
||||
|
||||
Six agents in parallel worktrees. Kept short on purpose; the reasoning that outlives the change is in `BUILT.md` or in
|
||||
Six agents in parallel worktrees. Kept short on purpose; the reasoning that outlives the change is in `docs/BUILT.md` or in
|
||||
the commit that made it.
|
||||
|
||||
1. **`nth` removed**, an alias of `at` that was asymmetric — `check.ml` aliased them but `parse.ml` and
|
||||
@ -1140,7 +1140,7 @@ is written down rather than left out; see the unsettled list under the build ord
|
||||
The critical path, and the thing NEXT.md said was the only one standing between this and writing a game. Steps 1, 2 and
|
||||
3 of the build order below are struck; `Map` is step 4 and is untouched. `Allocator` is a builtin opaque type and
|
||||
needed nothing from milestone 5, which was the whole bet. Three amendments to a **frozen** `spec-memory.md`, made
|
||||
deliberately and stated as amendments in [`BUILT.md`](BUILT.md): `free-all` is retain-capacity with `arena-destroy`
|
||||
deliberately and stated as amendments in [`docs/BUILT.md`](docs/BUILT.md): `free-all` is retain-capacity with `arena-destroy`
|
||||
beside it; `context/allocator` is a dynamic variable rather than a literal calling-convention parameter; and the `Vec`
|
||||
header is six words in every build rather than four in release. One addition the spec does not have: a budget on the
|
||||
allocator, because `retry` needs a handler that can make the *same* request succeed.
|
||||
@ -1150,7 +1150,7 @@ allocator, because `retry` needs a handler that can make the *same* request succ
|
||||
`--sanitize` is a build flag beside `--debug`; `dune build --root . @sanitize` builds twenty-eight programs twice, plain
|
||||
and sanitized, and compares output and exit status. Its own alias and not `dune test`, because the sweep is about nine
|
||||
minutes. The checked sweep is **clean**. How ASan and UBSan reach a language whose IR is written by hand, and why the
|
||||
flag does not force `-O0` when `--debug` does, is in [`BUILT.md`](BUILT.md).
|
||||
flag does not force `-O0` when `--debug` does, is in [`docs/BUILT.md`](docs/BUILT.md).
|
||||
|
||||
Two defects came out of it, both found by reading rather than by the tools, both fixed with a regression case:
|
||||
`flan_bytes_to_i64`/`flan_bytes_to_f64` clamped a slice length with `(size_t)n` and so read 63 or 511 bytes off the end
|
||||
@ -1182,7 +1182,7 @@ it *would* have written.
|
||||
control that must report: index 3 of a `Vec` with len 2 and cap 4, with `--track-origins` naming the
|
||||
`aligned_alloc` in `flan_vec_push`. Two more controls pin a heap overrun and a padded `Map` key. `test/valgrind.supp`
|
||||
holds **no suppressions** — nothing false came up to suppress. Details, and the measured fact that memcheck catches
|
||||
0 of `bounds.flan`'s 6 cases where ASan catches 3, in [`BUILT.md`](BUILT.md).
|
||||
0 of `bounds.flan`'s 6 cases where ASan catches 3, in [`docs/BUILT.md`](docs/BUILT.md).
|
||||
|
||||
**The arena half of the hole is closed; the interior half cannot be.** `free-all` is retain-capacity, so the pages
|
||||
stay and `malloc` never learns the storage died — but memcheck can be told directly, and now is: `flan_arena_proc`
|
||||
@ -1192,7 +1192,7 @@ it *would* have written.
|
||||
control. Cost outside valgrind is five no-op instructions plus the request block — 23 on the `free-all` path only,
|
||||
about 1ns per reset — and the macro is vendored rather than `#include`d because the machine that runs the sweep
|
||||
has `valgrind` and not `valgrind-devel`, so a guarded include would silently compile to nothing exactly here. See
|
||||
[`BUILT.md`](BUILT.md).
|
||||
[`docs/BUILT.md`](docs/BUILT.md).
|
||||
|
||||
**Interior overruns remain invisible**, for a structural reason no client request fixes — an arena is one `malloc`,
|
||||
and the `Map`'s `keys | values | hashes | scratch` is one allocation too, so "probe overrun at high load" is not
|
||||
@ -1253,7 +1253,7 @@ Either way it is an amendment to a **frozen** `spec-conditions.md`, not a gap in
|
||||
program, at that program's next frame boundary. Verified against sand: an unsaved buffer edit to `game-draw`, and 240
|
||||
consecutive frames drew it.
|
||||
|
||||
Steps 1, 2 and 3 are done — see *The reload primitive* in `BUILT.md`. A list of top-level forms can be recompiled and installed
|
||||
Steps 1, 2 and 3 are done — see *The reload primitive* in `docs/BUILT.md`. A list of top-level forms can be recompiled and installed
|
||||
into a running process; call sites compiled before they existed follow them, and a `defn` or `defvar` the process was
|
||||
never built with can be added and then redefined again. That is the whole of `C-c C-c`, minus an editor: sand.flan takes
|
||||
a redefinition over a socket and installs it between frames.
|
||||
@ -1263,7 +1263,7 @@ running process was built with, and speaks a protocol an editor can talk to.
|
||||
|
||||
Milestone 4 is done: **sand.flan builds, links raylib and runs**, and its simulation has a headless acceptance case that
|
||||
runs on the `dune test` path at `-O0` and `-O2`. Milestones 2 and 3 are behind it (`calc-me.flan` compiles and runs; the
|
||||
interpreter was dropped — open decision #7, settled — see "Why there is no interpreter" in `BUILT.md`).
|
||||
interpreter was dropped — open decision #7, settled — see "Why there is no interpreter" in `docs/BUILT.md`).
|
||||
|
||||
```
|
||||
reader ✅ → parse ✅ → load ✅ → check ✅ → emit ✅ → clang ✅
|
||||
@ -1322,7 +1322,7 @@ $ flan run sand.flan # a window, 120 fps, hold space
|
||||
Five questions were put and answered in one sitting. Each is a decision, not a preference — build against them, and
|
||||
reopen one only with a reason rather than a taste.
|
||||
|
||||
~~**1. Assets are embedded at compile time, one file or one directory.**~~ **Built** — `(embed "p")`, `(embed "p" string)`, `(embed-dir "d")`. See BUILT.md, "Assets are baked in". Odin's answer, and the reason it is the right
|
||||
~~**1. Assets are embedded at compile time, one file or one directory.**~~ **Built** — `(embed "p")`, `(embed "p" string)`, `(embed-dir "d")`. See docs/BUILT.md, "Assets are baked in". Odin's answer, and the reason it is the right
|
||||
one here: it is a *compiler* feature, so it needs no build flags, no linker arguments and no per-target packaging, and
|
||||
it works identically on desktop and web. That matters more here than it does for Odin, because `Load` gives link flags
|
||||
only to directory packages — the single file doing `(rl/load-texture "brush.png")` is structurally the one file with
|
||||
@ -1331,7 +1331,7 @@ no link channel, which is what stopped the web lane from inventing a flag. Embed
|
||||
`--preload-file` stays available later for assets that should load lazily rather than be baked in; the `@web` link
|
||||
line already carries it if wanted.
|
||||
|
||||
~~**2. Reading a file works everywhere; writing is desktop-only and signals on web.**~~ **Built** — `barf` on the web signals `FileError` with reason `file-unsupported`, and `test/test_web.ml` runs it under node rather than asserting the artifact's shape. See BUILT.md, "slurp, barf, and the two ways they fail". Odin stubs its whole file API on
|
||||
~~**2. Reading a file works everywhere; writing is desktop-only and signals on web.**~~ **Built** — `barf` on the web signals `FileError` with reason `file-unsupported`, and `test/test_web.ml` runs it under node rather than asserting the artifact's shape. See docs/BUILT.md, "slurp, barf, and the two ways they fail". Odin stubs its whole file API on
|
||||
js/wasm — every operation returns `.Unsupported`, and `core/os/file_js.odin`'s own comment says the stubs exist only
|
||||
so importing `core:os` "panics cleanly". Take the restriction and not the mechanism. **Flan has no conditional
|
||||
compilation** — nothing in `parse.ml` or `check.ml` reads the target — so "isolate this code to desktop" is not
|
||||
@ -1340,7 +1340,7 @@ it is how a save file disappears with nothing said. So `barf` on web signals a c
|
||||
program decides. This is the language having something Odin does not; use it. Per-package target isolation, if a
|
||||
whole desktop-only package is ever wanted, is the `@native`/`@wasi`/`@web` link-line tagging the web lane built.
|
||||
|
||||
**3. Build the shadow stack.** ~~Not yet built.~~ **Built**, both halves — see BUILT.md. Kept here as the decision it
|
||||
**3. Build the shadow stack.** ~~Not yet built.~~ **Built**, both halves — see docs/BUILT.md. Kept here as the decision it
|
||||
was, with the measurement it asked for: +33% on call-heavy code over globals for the frames, +61% with the slot table,
|
||||
and 0.06% of a 60fps frame.
|
||||
|
||||
@ -1371,7 +1371,7 @@ length is not known until the file is read and therefore cannot exist before an
|
||||
refusal names the new one. `:keys` kept its colon — it names no field, so leaving it alone is what lets the dot mean
|
||||
exactly one thing. 681 labels across 45 `.flan` files including `vendor/`, plus 94 more in the Flan embedded in
|
||||
`lib/prelude.ml` and the tests. `Map` is now free to take `{:key value}` without colliding with struct literals. See
|
||||
BUILT.md, "The colon belongs to keys".
|
||||
docs/BUILT.md, "The colon belongs to keys".
|
||||
|
||||
**What it left for the Emacs lane, both verified.** `render.ml` still *prints* a struct with colons, deliberately:
|
||||
`emacs/flan-inspect.el:165` parses that output and hard-codes the colon when it reads a field out, so the printer
|
||||
@ -1408,7 +1408,7 @@ reason there is no collector.
|
||||
|
||||
**Globals in the break buffer — built.** One section under the stack, holding the union of the globals every frame on
|
||||
the current stack references, each entry annotated with the frames that touch it and ordered by the innermost one. It
|
||||
is `(:op "globals")` in `dev.ml` and `flan-cnr--insert-globals` in the break buffer. See BUILT.md, "Globals of a
|
||||
is `(:op "globals")` in `dev.ml` and `flan-cnr--insert-globals` in the break buffer. See docs/BUILT.md, "Globals of a
|
||||
stopped stack". ~~The one hole left open — the redefinition check is a fingerprint over a body's *slots*, so a new body
|
||||
that names different globals while binding the same locals is not caught.~~ **Closed**: `Reach.ref_fingerprint` is a
|
||||
second fingerprint over the set of globals a body names, carried beside the slot one in `%fninfo` and checked the same
|
||||
@ -1428,7 +1428,7 @@ mid-edit in another buffer.
|
||||
that nothing starts on managed classes "until ordinary `struct`, `Handle`, and reload semantics are working". All
|
||||
three now hold: structs work fully; reload works with one known hole (a changed signature is refused rather than
|
||||
versioned); and `(Handle T)` and `(Pool T)` exist, with the enumeration primitive `migrate-instances` was blocked on.
|
||||
See [`BUILT.md`](BUILT.md), "`(Handle T)` and the pool, which is what a stale reference answers with".
|
||||
See [`docs/BUILT.md`](docs/BUILT.md), "`(Handle T)` and the pool, which is what a stale reference answers with".
|
||||
|
||||
It was not an incidental precondition. `migrate-instances` has to *enumerate* live instances, and a pool behind a
|
||||
generational `(Handle T)` gives that by construction while a world arena and an owned region do not. plan.org presents
|
||||
@ -1482,11 +1482,11 @@ acquisitions against releases at that boundary and report what is still held at
|
||||
annotation, nothing running at a distance — it does not change how code is written, it reports when something was
|
||||
forgotten.
|
||||
|
||||
## ~~Before the batch below: read `DISCUSS.md`'s "NEXT SESSION STARTS HERE"~~ — **answered, and being unwound**
|
||||
## ~~Before the batch below: read `docs/DISCUSS.md`'s "NEXT SESSION STARTS HERE"~~ — **answered, and being unwound**
|
||||
|
||||
The architectural question raised at the end of 2026-09-12 — **putting the compiler inside the running program's
|
||||
process** — was researched (`DISCUSS.md` §14), then built: `flan dev` is one binary and one process, and the editor
|
||||
socket did not move. See "One process" in [`BUILT.md`](BUILT.md).
|
||||
process** — was researched (`docs/DISCUSS.md` §14), then built: `flan dev` is one binary and one process, and the editor
|
||||
socket did not move. See "One process" in [`docs/BUILT.md`](docs/BUILT.md).
|
||||
|
||||
What it reopened is now being deleted one piece at a time, each with its own green run:
|
||||
|
||||
@ -1516,7 +1516,7 @@ whether an in-process JIT or a hand-written backend is needed at all.
|
||||
|
||||
## ~~Queued: a second tier of the standard library, after macros~~ — **landed**
|
||||
|
||||
See [`BUILT.md`](BUILT.md), "The prelude's second tier". The diagnosis here was right and the prelude had 44
|
||||
See [`docs/BUILT.md`](docs/BUILT.md), "The prelude's second tier". The diagnosis here was right and the prelude had 44
|
||||
allocation-free functions because there was nothing to allocate from; there are 24 more now, and the "Refused, by
|
||||
name" block at the foot of `prelude.ml` is down from eight entries to four, each with a *different* reason rather
|
||||
than the one shared sentence.
|
||||
@ -1534,14 +1534,14 @@ Tests: `programs/strings.flan`, `programs/format.flan`, `programs/algorithms.fla
|
||||
them wants a language decision.
|
||||
|
||||
- ~~**`Map` keys and values.**~~ **Iteration is built** — `flan_map_next` and the `map-next!` builtin, exactly the
|
||||
shape this described. See [`BUILT.md`](BUILT.md), "`map-next!`, the one thing a Map could not do".
|
||||
shape this described. See [`docs/BUILT.md`](docs/BUILT.md), "`map-next!`, the one thing a Map could not do".
|
||||
`map-keys`/`map-values` as *prelude functions* stay refused, and the reason is now generics rather than the
|
||||
iterator: a `defn` has to name its types and `(defn map-keys [m {K V}] (Vec K))` has no `K`. The loop is three
|
||||
lines at the call site, where `K` is known.
|
||||
|
||||
- ~~**`map`, `filter`, `reduce`, and a sort taking a comparator.**~~ **All four are in the prelude.** The diagnosis
|
||||
was right and is now evidenced: **function values, not generics** — they arrived with no generics at all. See
|
||||
[`BUILT.md`](BUILT.md), "Function values, with no capture". They are one copy per element type (i32 and f32), which
|
||||
[`docs/BUILT.md`](docs/BUILT.md), "Function values, with no capture". They are one copy per element type (i32 and f32), which
|
||||
is the half generics would remove, and a `map` that *changes* the element type is the one shape that did not come
|
||||
with them — one copy per ordered pair of types rather than per type.
|
||||
|
||||
@ -1563,7 +1563,7 @@ them wants a language decision.
|
||||
Two smaller findings, both written down beside the code that ran into them:
|
||||
|
||||
- ~~**The prelude is never macro-expanded.**~~ **Fixed, and the diagnosis above was wrong in both halves** — see
|
||||
[`BUILT.md`](BUILT.md), "A prelude function may call a prelude macro". The prelude *does* reach the expander; the
|
||||
[`docs/BUILT.md`](docs/BUILT.md), "A prelude function may call a prelude macro". The prelude *does* reach the expander; the
|
||||
arity error came from the `Check.program` *inside* `Macro.compile`, where expansion is off. It is a cycle and not
|
||||
an ordering — a macro module is compiled from the prelude — so moving the prepend would have changed nothing. What
|
||||
fixed it is `Macro.reduce`, which makes the prelude smaller for that one build, plus dropping the prelude's own
|
||||
@ -1642,7 +1642,7 @@ destination is always honest about what it is.
|
||||
|
||||
Drop Clojure's `:eduction` branch — that is the pass-around case, and the one part that would need runtime machinery.
|
||||
|
||||
**Done.** See *`into`, which fuses at compile time because it is a macro* in [`BUILT.md`](BUILT.md). It is a prelude
|
||||
**Done.** See *`into`, which fuses at compile time because it is a macro* in [`docs/BUILT.md`](docs/BUILT.md). It is a prelude
|
||||
`defmacro` over a plain `defn` that walks the transforms in reverse, and all four of the macro limits bit without
|
||||
blocking anything: the three refusals are names nothing defines, `into-wrap` uses only special forms so
|
||||
`Macro.reduce` does not drop it, the quasiquotes are all single-level, and `into` lives in the prelude because a
|
||||
@ -1664,7 +1664,7 @@ this, and it does not exist.
|
||||
## ~~Queued: `loop`/`recur` (the return type is done)~~ — **landed**
|
||||
|
||||
~~**1. A `defn` must always state its return type, and unit is written `()`.**~~ **Done.** See *The return type is
|
||||
the slot, and unit is `()`* in [`BUILT.md`](BUILT.md).
|
||||
the slot, and unit is `()`* in [`docs/BUILT.md`](docs/BUILT.md).
|
||||
|
||||
The slot after the parameters is unconditionally a type, the pre-pass that collected a file's type names is gone
|
||||
along with `is_type_form`, `qualified_type`, `types_in`, `declared_types` and `prelude_types`, and `Parse.decl` no
|
||||
@ -1690,10 +1690,10 @@ python3 tools/unit-return.py --in-html web/index.html
|
||||
|
||||
`-v` logs every `defn` it saw and what it decided; `--check` changes nothing. It is re-runnable, and on this tree it
|
||||
reports exactly six sites, all in `test_flan.ml`, which spell the refused forms *on purpose* so the refusals can be
|
||||
tested. Read the diff of every non-`.flan` file — BUILT.md lists what the script can and cannot see.
|
||||
tested. Read the diff of every non-`.flan` file — docs/BUILT.md lists what the script can and cannot see.
|
||||
|
||||
~~**2. `loop` and `recur`.**~~ **Done.** See *`loop` and `recur`, and why `recur` is better than tail calls and not
|
||||
only cheaper* in [`BUILT.md`](BUILT.md). `emit.ml` is untouched: a loop is a `let`, a `While` whose condition is
|
||||
only cheaper* in [`docs/BUILT.md`](docs/BUILT.md). `emit.ml` is untouched: a loop is a `let`, a `While` whose condition is
|
||||
`true`, and two jumps, and the barrier question `recur` asks is the one labelled `break` already answered.
|
||||
|
||||
Three things the plan did not anticipate, each written up there. **Tail position is a permission that is withdrawn**
|
||||
@ -1718,7 +1718,7 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them.
|
||||
backtrace line never carried it, and `Dev.locals` compared slot counts and nothing else — four of the five
|
||||
hand-offs were never written, and printing both sides of the comparison could not have found it because there was
|
||||
no comparison. The mechanism was sound and stayed: it hashes slot *names* as well as types, so it does see a
|
||||
rename. See BUILT.md, "Locals of a stopped frame".
|
||||
rename. See docs/BUILT.md, "Locals of a stopped frame".
|
||||
|
||||
2. ~~**The colon-to-dot change.**~~ **Done**, and `Map` is unblocked: `{:key value}` is free. The sweep is
|
||||
`tools/colon-to-dot.py`, kept rather than thrown away, because the lanes that branched before it wrote Flan in the
|
||||
@ -1726,7 +1726,7 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them.
|
||||
`--in-strings` for a `test/*.ml` that embeds Flan.
|
||||
|
||||
3. ~~**`Map`, and the `defer` relaxation.**~~ **Both done.** See *`(Map K V)`, which is Odin's map* and *`defer` may be
|
||||
written in a `let`* in [`BUILT.md`](BUILT.md). The `defer` relaxation **amends `spec-memory.md`**, which said
|
||||
written in a `let`* in [`docs/BUILT.md`](docs/BUILT.md). The `defer` relaxation **amends `spec-memory.md`**, which said
|
||||
`(defer (free v))` for a `let`-bound `v` was not expressible; it is now. `Map` restricts one thing the spec does not:
|
||||
a fixed array is a key only when its elements compare bytewise, so an array of structs or of strings is refused by
|
||||
name. The measured answer to "is this another Python dict" is six times quicker cache-resident and *slower* at a
|
||||
@ -1743,7 +1743,7 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them.
|
||||
5. ~~**Union values**~~, then the macro expander, then `Result`/`try`. Promoted above `Handle` on the author's call —
|
||||
macros are the thing most worth wanting, and unions are the only thing between here and them.
|
||||
|
||||
**Union values are done.** See *Unions, and the tag they carry* in [`BUILT.md`](BUILT.md). The diagnosis was right:
|
||||
**Union values are done.** See *Unions, and the tag they carry* in [`docs/BUILT.md`](docs/BUILT.md). The diagnosis was right:
|
||||
`Option` is a two-case union wearing a special coat, so `Tast.arm`'s `acase` and `binds` already were union shape
|
||||
and `check_match` grew a second subject rather than a second path. A union is `Types.Named` exactly as a struct is,
|
||||
so every path that merely carries a type learned nothing.
|
||||
@ -1761,7 +1761,7 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them.
|
||||
needs no import and no `load.ml` change. Verified by declaring one there and matching it from a program.
|
||||
|
||||
**Macros landed on top of this** and needed no `load.ml` change for `Form`, exactly as this said. See
|
||||
[`BUILT.md`](BUILT.md), "Macros: the compiler dlopens the program", and the short list of what is left of them
|
||||
[`docs/BUILT.md`](docs/BUILT.md), "Macros: the compiler dlopens the program", and the short list of what is left of them
|
||||
below.
|
||||
|
||||
Macros are what buy `with-drawing` and `with-mode-2d` over raylib's begin/end pairs, the hiccup DSL if a JS backend
|
||||
@ -1778,7 +1778,7 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them.
|
||||
a C-shaped callback and somewhere to put a `flan_allocator`, neither of which is a type parameter.
|
||||
|
||||
6. ~~**`Handle` and the pool.**~~ **Built.** A reference to something that can die, that reports that it died rather
|
||||
than silently resolving to whatever reused the slot. See [`BUILT.md`](BUILT.md), "`(Handle T)` and the pool, which
|
||||
than silently resolving to whatever reused the slot. See [`docs/BUILT.md`](docs/BUILT.md), "`(Handle T)` and the pool, which
|
||||
is what a stale reference answers with". A handle is one `i64` — slot index low, generation high — so it copies,
|
||||
zeroes and compares like an integer and owns nothing; a live slot's generation is odd, which makes a zeroed handle
|
||||
resolve to nothing rather than to slot 0; and a generation that would wrap retires its slot instead, because "rare"
|
||||
@ -1800,7 +1800,7 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them.
|
||||
is consumed, because there is no borrowing parameter — a pre-existing `Vec` gap, not a pool one.
|
||||
|
||||
7. ~~**`break` and `continue`, with loop labels.**~~ **Built.** Labels are Odin's in the head position, both blockers
|
||||
are answered, and the refusals name the construct they refuse for. See BUILT.md, "`break` and `continue`, and the
|
||||
are answered, and the refusals name the construct they refuse for. See docs/BUILT.md, "`break` and `continue`, and the
|
||||
rule that replaced a blanket refusal". What was settled in conversation before it was built, kept:
|
||||
|
||||
**Labels, Odin-style but in the head position.** A keyword names a loop and `break` takes it:
|
||||
@ -1822,7 +1822,7 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them.
|
||||
hang.
|
||||
|
||||
8. ~~**Errors: a structured value with spans and notes, and more than one per compile.**~~ **Built.** See
|
||||
*An error is a value, and there is more than one of them* in [`BUILT.md`](BUILT.md). `Loc.Error` carries a
|
||||
*An error is a value, and there is more than one of them* in [`docs/BUILT.md`](docs/BUILT.md). `Loc.Error` carries a
|
||||
`diag` — a stable `kind`, a *span*, `notes` that each have their own span and severity, and the macro expansion
|
||||
the error came out of — and `flan check`/`flan build` print the source line with the offending span underlined,
|
||||
in the GNU format `compilation-mode` already parses. No editor work was needed and none was done.
|
||||
@ -1880,7 +1880,7 @@ debug tracking allocator, which is the leak safety net and a good candidate when
|
||||
|
||||
## Decided in discussion — the array constructor and the module system
|
||||
|
||||
**`(array 4 rl/Vector2)` makes a fixed array; `[4 T]` stays the type syntax. Built** — see BUILT.md, "`(array 4 rl/Vector2)`, and the one position with no type slot". The problem it solved: a `let`
|
||||
**`(array 4 rl/Vector2)` makes a fixed array; `[4 T]` stays the type syntax. Built** — see docs/BUILT.md, "`(array 4 rl/Vector2)`, and the one position with no type slot". The problem it solved: a `let`
|
||||
binding takes no type, so `(let [pts [4 rl/Vector2]] ...)` reads `[4 rl/Vector2]` as a two-element array *literal* and
|
||||
fails with *unknown name rl/Vector2*. It cost 32 hand-written `Vector2`s in one raylib example.
|
||||
|
||||
@ -1938,7 +1938,7 @@ package to *mark* a name private, and adding one means a parser change.
|
||||
|
||||
## Decided in discussion — three more, two now built
|
||||
|
||||
~~**A watch window, ported from the author's Clojure one.**~~ **Built.** See `BUILT.md`, "The watch window, and why
|
||||
~~**A watch window, ported from the author's Clojure one.**~~ **Built.** See `docs/BUILT.md`, "The watch window, and why
|
||||
it is the only listing that is pushed", and `emacs/MANUAL.md` under "Looking at values". Three of the original's
|
||||
decisions were kept unchanged — the program decides what is shown, the request is async, and the paint is
|
||||
`replace-buffer-contents` so point and scroll survive every tick.
|
||||
@ -1954,7 +1954,7 @@ could not have done at any price.
|
||||
|
||||
**What was not built, deliberately: the `(watch "hp" hp)` form.** Scalars work today through `declare-c` against four
|
||||
runtime entry points, which needs no compiler change at all. A struct or a slice needs a compile-time walk over its
|
||||
type — one arm in `check.ml` beside `print`, which `BUILT.md` writes out in full — and that file is held by another
|
||||
type — one arm in `check.ml` beside `print`, which `docs/BUILT.md` writes out in full — and that file is held by another
|
||||
lane, so it was left alone rather than reached into.
|
||||
|
||||
~~**Ghost text is gated on that same arm**, which is the finding worth keeping.~~ **Wrong, and ghost text is built.**
|
||||
@ -1965,10 +1965,10 @@ conclusion did not follow: the call site is in the *buffer*, and the name in the
|
||||
so the anchor is searched for rather than reported. Nothing new is asked of the daemon. The two open questions
|
||||
`flan-watch.el` recorded are answered rather than solved — overlays are replaced wholesale every repaint, so an edit
|
||||
has nothing to invalidate, and a watch in a loop shows the last value written exactly as the buffer does, because
|
||||
every better answer is the query UI this design exists to avoid. See `BUILT.md`, "Ghost text finds its anchor in the
|
||||
every better answer is the query UI this design exists to avoid. See `docs/BUILT.md`, "Ghost text finds its anchor in the
|
||||
buffer, not in the table".
|
||||
|
||||
~~**The inspector gets a second way to start: an address and a type.**~~ **Built.** See `BUILT.md`, "Two ways to root
|
||||
~~**The inspector gets a second way to start: an address and a type.**~~ **Built.** See `docs/BUILT.md`, "Two ways to root
|
||||
a walk, and why neither subsumes the other". It went in as a frame and a slot *index* rather than an address and a
|
||||
type — the daemon holds both and an index is the thing the listing can hand back, while an address is not something an
|
||||
editor should be holding. The one prediction that did not survive contact: `l` crossing between the two modes was
|
||||
@ -2042,11 +2042,11 @@ Ranked by how often they were hit, top two first because they are walls rather t
|
||||
list head — because `(Key n)` is a *value* now, and putting `Key` in `types` would make a body starting with one
|
||||
be eaten as a return type.
|
||||
3. ~~**`break` is not implemented.**~~ **Built, with `continue` and loop labels.** Both blockers are answered: the
|
||||
`in_frames` rule became a relative one rather than a blanket one, and `Tast.While` grew a latch. See BUILT.md,
|
||||
`in_frames` rule became a relative one rather than a blanket one, and `Tast.While` grew a latch. See docs/BUILT.md,
|
||||
"`break` and `continue`, and the rule that replaced a blanket refusal".
|
||||
4. ~~**A `let` binding takes no type annotation**~~ — still true, and **no longer the blocker it was**: `(array 4
|
||||
rl/Vector2)` is built and is the answer to the case that raised it. The reasoning below is kept because it is what
|
||||
chose between the three surfaces, and the first of them is not what was taken — see BUILT.md, "`(array 4
|
||||
chose between the three surfaces, and the first of them is not what was taken — see docs/BUILT.md, "`(array 4
|
||||
rl/Vector2)`, and the one position with no type slot". The original entry:
|
||||
|
||||
A fixed array is either a top-level `defvar` or a literal with
|
||||
@ -2089,11 +2089,11 @@ array with a struct element, a 2-D struct array, and `[N string]` as both `defco
|
||||
|
||||
### The web target: what it does not reach yet
|
||||
|
||||
`flan build --target=web` works, a raylib example builds unchanged and `test/test_web.ml` is green — see BUILT.md,
|
||||
`flan build --target=web` works, a raylib example builds unchanged and `test/test_web.ml` is green — see docs/BUILT.md,
|
||||
"The browser is the third target", for the mechanism and why asyncify rather than `emscripten_set_main_loop`. Four
|
||||
things it does not cover.
|
||||
|
||||
~~**1. `sand.flan` has no web build, and the cause is one missing `#include`.**~~ **Built. It opens.** See BUILT.md,
|
||||
~~**1. `sand.flan` has no web build, and the cause is one missing `#include`.**~~ **Built. It opens.** See docs/BUILT.md,
|
||||
"sand.flan in a browser", for the whole of it. Three summary lines, because the diagnosis below was right about the
|
||||
structure and wrong about the cause:
|
||||
|
||||
@ -2147,7 +2147,7 @@ The original text follows. `sand.flan` does `(rl/load-texture "brush.png")` agai
|
||||
**3. Nothing has been opened in a browser.** *Still true, and now it is the only thing left between here and
|
||||
"someone played with it".* `sand.flan` builds for the web, the module carries asyncify, raylib's GL imports and
|
||||
brush.png's own bytes whole, and `node sand.js` gets as far as `glfwInit` before dying on `window is not defined` —
|
||||
which proves the module is live and proves nothing about the canvas. BUILT.md carries the exact commands to serve and
|
||||
which proves the module is live and proves nothing about the canvas. docs/BUILT.md carries the exact commands to serve and
|
||||
open it, and the list of what only a human will discover: whether it paints, whether the audio round trip through
|
||||
MEMFS survives, and the canvas size. The `until` loop never exits on the web, so none of `main`'s `defer`s run —
|
||||
expected, and worth knowing before reading anything into it.
|
||||
@ -2167,7 +2167,7 @@ message.
|
||||
### `break`, and why it was not built — **it is built now**
|
||||
|
||||
Kept as written, because everything in it held and the two blockers at the end are the two things the build had to
|
||||
rule on. Both are ruled on in BUILT.md, "`break` and `continue`, and the rule that replaced a blanket refusal":
|
||||
rule on. Both are ruled on in docs/BUILT.md, "`break` and `continue`, and the rule that replaced a blanket refusal":
|
||||
the `in_frames` precedent was replaced by a barrier on the loop stack, which refuses a *crossing* rather than
|
||||
everything, and `Tast.While` grew the latch. The original note:
|
||||
|
||||
@ -2197,7 +2197,7 @@ plan.org's single line on it (831) names a `for` the language does not have and
|
||||
|
||||
1. ~~**Allocators, then `Vec` and `Map`.**~~ **Steps 1, 2 and 3 are done** — the allocator, the arena, `(Vec T)`,
|
||||
`StorageExhausted` and `retry`. `Map` is step 4 and is what is left of this item. See *Allocators, `(Vec T)` and
|
||||
`StorageExhausted`* in [`BUILT.md`](BUILT.md) for the shape, the three amendments to a frozen `spec-memory.md` and
|
||||
`StorageExhausted`* in [`docs/BUILT.md`](docs/BUILT.md) for the shape, the three amendments to a frozen `spec-memory.md` and
|
||||
the one addition. The claim below held: **`Vec` does not need generics** — that was wrong and is worth un-learning: Odin's containers are compiler
|
||||
builtins over a *type-erased* runtime (`base/runtime/dynamic_array_internal.odin`), where `$T` appears only in thin
|
||||
wrappers producing `size_of`/`align_of` at the call site, and per-key hash and equality are compiler-emitted
|
||||
@ -2237,7 +2237,7 @@ plan.org's single line on it (831) names a `for` the language does not have and
|
||||
|
||||
**Steps 1, 2 and 3 are built; 4 to 8 are what is left.** The reasoning is kept because it is what the remaining steps
|
||||
rest on, and because the escape it describes was tested rather than assumed — see *Allocators, `(Vec T)` and
|
||||
`StorageExhausted`* in [`BUILT.md`](BUILT.md).
|
||||
`StorageExhausted`* in [`docs/BUILT.md`](docs/BUILT.md).
|
||||
|
||||
The dependency nobody had written down, and the reason it looked worse than it is. `spec-memory.md` defines an
|
||||
allocator as "a procedure plus an opaque data pointer" — a function value. `check.ml` refuses function values four
|
||||
@ -2280,7 +2280,7 @@ expander last, on 6's unions.
|
||||
~~1. **`Allocator` and the arena.**~~ **Done.** The builtin opaque type, the four operations with `size` and `align`,
|
||||
the capability set read off the allocator value, `with-allocator`, `context/allocator`, `context/temp` and the epoch
|
||||
counter. `free-all` was decided as retain-capacity with `arena-destroy` beside it; the context is a dynamic variable
|
||||
rather than a literal calling-convention parameter; both are stated as amendments in `BUILT.md`. A user-written
|
||||
rather than a literal calling-convention parameter; both are stated as amendments in `docs/BUILT.md`. A user-written
|
||||
allocator is refused by name with milestone 5 as the reason.
|
||||
|
||||
~~2. **`(Vec T)`**~~ **Done**, over the type-erased runtime, with `push`, `reserve`, `at`, `len`, `as-slice`, `free` and
|
||||
@ -2340,7 +2340,7 @@ expander last, on 6's unions.
|
||||
not the next thing to build.
|
||||
- ~~**The operation table may be one operation short.**~~ **Decided.** `free-all` is retain-capacity and
|
||||
`arena-destroy` hands the pages back — two names rather than the mode parameter, so the table the spec froze at four
|
||||
operations did not grow. `BUILT.md` states it as the amendment it is.
|
||||
operations did not grow. `docs/BUILT.md` states it as the amendment it is.
|
||||
- **The `Vec` header is six words in release too, and should not stay that way.** The 32-byte layout the spec fixes is
|
||||
blocked on one thing: a redefinition module is built by `llc` and `ld` against a host built separately, and nothing
|
||||
makes the two agree on a struct size. Give the reload path a way to carry the build flags and this falls out.
|
||||
@ -2372,7 +2372,7 @@ expander last, on 6's unions.
|
||||
- ~~`web/examples/breakdemo.out` is stale and `check.sh` fails on it.~~ **Fixed.** Commit `4a6a8fa` made the break
|
||||
banner number its restarts and the `.out` was never repinned. Nothing had to drive the socket in the end:
|
||||
`check.sh` already builds this one `--dev` and runs it under `timeout 5`, keeping what it printed before it
|
||||
stopped, so the repin was the `.out` plus the two prose copies of the banner — `web/index.html` and `BUILT.md` —
|
||||
stopped, so the repin was the `.out` plus the two prose copies of the banner — `web/index.html` and `docs/BUILT.md` —
|
||||
and a sentence on the page saying what the numbers are for, since a restart is taken by position.
|
||||
|
||||
- ~~A shadowed restart is offered and cannot be taken.~~ **Fixed.** A restart is taken by *position* now:
|
||||
@ -2483,7 +2483,7 @@ memcheck sweep (`@valgrind`), whose alarm is looser at 5400s because memcheck is
|
||||
now exists** — `Session.render_locals` is exactly that thunk, rooted at an address the program supplies — so what is
|
||||
left is the first: keep the pointer, and give the agent a verb that hands it back. The type is already known: it is
|
||||
the `condition_name` the break loop reports, which `layout` already resolves.
|
||||
- **The type identity is settled, and it is the qualified name** — `layout` is in, see BUILT.md. `Load` qualifies
|
||||
- **The type identity is settled, and it is the qualified name** — `layout` is in, see docs/BUILT.md. `Load` qualifies
|
||||
every declaration at import, so the names in `Tast.structs` are a flat namespace where two packages' `Missing` are
|
||||
`a/Missing` and `b/Missing`; a bare name is refused with the candidates rather than resolved. `condition` inherits
|
||||
it for free: the string the break loop already reports *is* that name, because `Emit.struct_name_of` writes
|
||||
@ -2491,7 +2491,7 @@ memcheck sweep (`@valgrind`), whose alarm is looser at 5400s because memcheck is
|
||||
reads is not qualified by anything.
|
||||
- ~~**`(:op "backtrace")` is blocked** on frame metadata.~~ **Built**, and not out of DWARF: decision 3's shadow
|
||||
stack carries the name and the location on the frame itself, so a backtrace needs no debug information at all. See
|
||||
BUILT.md, "The shadow stack, and `backtrace`", for what it costs. **Locals landed with it** — the pointer-rooted
|
||||
docs/BUILT.md, "The shadow stack, and `backtrace`", for what it costs. **Locals landed with it** — the pointer-rooted
|
||||
render thunk turned out to be `Render.render` over a `Deref` of a slot's address, and one new arm in the backend.
|
||||
See "Locals of a stopped frame" for the four things it refuses. Restart source locations and arity are still blocked — `flan_restart` carries `prev`, `name_id`, `name` and `namelen`, so both need a new
|
||||
field in the frame, which means the compiler emitting it.
|
||||
@ -2614,7 +2614,7 @@ left of the item is visibility, which is listed above and needs a marker the par
|
||||
## Macros — landed; what is left of them
|
||||
|
||||
**The expander works and `unless` is a prelude `defmacro`.** How all of it fits together is in
|
||||
[`BUILT.md`](BUILT.md), "Macros: the compiler dlopens the program" — the image format, the thunk ABI, why quasiquote
|
||||
[`docs/BUILT.md`](docs/BUILT.md), "Macros: the compiler dlopens the program" — the image format, the thunk ABI, why quasiquote
|
||||
runs before the walk, the two different ways expansion fails to terminate, `-linkall`, and the three cost numbers.
|
||||
What follows is only the part that is still missing.
|
||||
|
||||
@ -2684,7 +2684,7 @@ tests assert on the reason, not just on the failure.
|
||||
## Handoff: the shadow stack lane, stopped mid-repair
|
||||
|
||||
Two commits landed and are green: the shadow stack with `(:op "backtrace")`, and `(:op "locals" :frame N)`. See
|
||||
BUILT.md's two new sections for the design and the measurements. A third commit was **half-built and its own test
|
||||
docs/BUILT.md's two new sections for the design and the measurements. A third commit was **half-built and its own test
|
||||
left red** on purpose; **it is finished now** — see the struck item 1 above — and the rest of this section is kept
|
||||
because the parts of it that were true are still worth having.
|
||||
|
||||
@ -2698,7 +2698,7 @@ wrote.
|
||||
|
||||
**Not obvious from the diff.** Two things cost a day between them. The linked-list frame beat an array-with-a-stack-
|
||||
pointer on both benchmarks, which is the opposite of what the escaping-alloca argument predicts, and the measurement
|
||||
that first said otherwise was comparing a 40-frame binary with a 600-frame one; every number in BUILT.md is now a
|
||||
that first said otherwise was comparing a 40-frame binary with a 600-frame one; every number in docs/BUILT.md is now a
|
||||
minimum of nine runs for that reason. And `redefinition`'s transient rule (`m.nstr = 0`) silently stops every module
|
||||
carrying a string literal from ever being unloaded — the frame descriptors go through their own counter, `m.nfi`, for
|
||||
that reason, and a locals thunk passes `~retains:false` because everything it emits is memcpy'd into the result buffer.
|
||||
|
||||
@ -109,7 +109,7 @@ let sanitize_flag = "--sanitize"
|
||||
let two_process_flag = "--two-process"
|
||||
|
||||
(* The hand-written x86-64 backend (lib/x86.ml) instead of LLVM. The dev
|
||||
backend from DISCUSS.md item 15, off by default and named explicitly:
|
||||
backend from docs/DISCUSS.md item 15, off by default and named explicitly:
|
||||
LLVM stays the release path and the default one. It covers a subset of the
|
||||
IR and refuses the rest by name, so a build that succeeds is one it really
|
||||
compiled. *)
|
||||
|
||||
@ -860,7 +860,7 @@ load a file out of `resources/` and neither file is in this tree.
|
||||
- `DotGothic16-Regular.ttf` is SIL OFL 1.1 and therefore redistributable, but it is 2 MB,
|
||||
and whether a compiler repository whose only binary asset is a 1 KB PNG should grow a 2 MB
|
||||
font is a call about the repository rather than about this port. Left to the author;
|
||||
`HANDOFF-raylib-ports.md` carries it as the open question. The example looks for it under
|
||||
`handoffs/HANDOFF-raylib-ports.md` carries it as the open question. The example looks for it under
|
||||
`examples/resources/`, says on screen when it is not there, and runs either way — raylib
|
||||
answers a missing path with the default font, whose glyphs are ASCII, so the kana draw as
|
||||
boxes and the program explains itself rather than looking broken.
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Two examples, `text_codepoints_loading` and `textures_image_processing`, ported into
|
||||
`examples/`. This file is the running record: what is done, what was decided without being
|
||||
able to ask, and what is still open. `PORTING.md` has the findings that outlive the port —
|
||||
able to ask, and what is still open. `../PORTING.md` has the findings that outlive the port —
|
||||
its last section, "Porting the raylib examples — the last two" — and this file has the
|
||||
state of the work.
|
||||
|
||||
@ -12,7 +12,7 @@ state of the work.
|
||||
- [x] `examples/textures-image-processing.flan` — builds
|
||||
- [x] `test/programs/raylib-codepoints.flan` and `test/programs/raylib-image-processing.flan`,
|
||||
both in the acceptance table, both gated on libraylib, both run plain and at `-O0`
|
||||
- [x] `PORTING.md` section for this round
|
||||
- [x] `../PORTING.md` section for this round
|
||||
- [x] `dune test --root .` green
|
||||
|
||||
Four commits, each one a working state:
|
||||
@ -39,7 +39,7 @@ Four commits, each one a working state:
|
||||
reported", *"a `(Ptr T)` where the header says `T *` and the hand-written line chose
|
||||
something more specific"*. The function implements the enum-against-i32 arm and nothing
|
||||
else. That missing arm is the only thing standing between this round's two refused
|
||||
bindings and ordinary binding work — see `PORTING.md` §A.1 and §A.2, which have the
|
||||
bindings and ordinary binding work — see `../PORTING.md` §A.1 and §A.2, which have the
|
||||
exact declarations and the exact error. **Not built: it is a compiler change and the
|
||||
freeze is on.** It is small and it is well specified, and it is the thing to do first if
|
||||
these two ports are ever revisited.
|
||||
|
||||
@ -6,9 +6,9 @@ newcomer at that. This is the plan and, below it, what actually happened.
|
||||
|
||||
## The plan
|
||||
|
||||
1. `git mv` the nine `HANDOFF-*.md` into `docs/handoffs/`, and `DISCUSS.md`,
|
||||
`BUILT.md`, `PORTING.md`, `SPIKE-GENERICS.md`, `REFERENCES.md` and
|
||||
`overview.md` into `docs/`. These are written once and read occasionally.
|
||||
1. `git mv` the nine `HANDOFF-*.md` into `docs/handoffs/`, and `../DISCUSS.md`,
|
||||
`../BUILT.md`, `../PORTING.md`, `../SPIKE-GENERICS.md`, `../REFERENCES.md` and
|
||||
`../overview.md` into `docs/`. These are written once and read occasionally.
|
||||
2. Leave `plan.org`, `spec-memory.md`, `spec-conditions.md`, `NEXT.md`, the
|
||||
`.flan` scratch programs and the build files where they are: they are cited
|
||||
by bare filename from source comments in dozens of places, and moving them
|
||||
|
||||
@ -61,7 +61,7 @@ So a reference to a symbol the object does not define goes through the GOT. Meas
|
||||
`call_flan`'s `` `Cell `` target loaded **one** level: `mov r11, cell@GOTPCREL(%rip)` then `call *%r11`. That
|
||||
reads as "call through the cell" in a disassembly and in fact *calls the cell*. The GOT slot holds the cell's
|
||||
**address**; reading what the cell holds is two loads, not one. Found by the segfault, not by reading — which is
|
||||
DISCUSS.md item 15's claim, now with a sixth instance. `load_sym` is the fix and is the only helper that
|
||||
../DISCUSS.md item 15's claim, now with a sixth instance. `load_sym` is the fix and is the only helper that
|
||||
double-loads; `Lgot` gets it right for free because `lmem` already had the shape.
|
||||
|
||||
## Scope, deliberately narrower than `Emit.redefinition`
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Handoff — the x86 backend's last two correctness items
|
||||
|
||||
Branch: `dev-loop`, worktree `agent-af9064091c0602dc8`. Three commits plus this one; nothing is half-written and
|
||||
nothing is reverted. The long-form report is **DISCUSS.md item 18**; this file is the operational version.
|
||||
nothing is reverted. The long-form report is **../DISCUSS.md item 18**; this file is the operational version.
|
||||
|
||||
## 1. The `flan_vec_as_slice` answer — got it, and it is complete
|
||||
|
||||
|
||||
@ -3,11 +3,11 @@
|
||||
This was the first brainstorm. It is kept for history and is **no longer
|
||||
accurate**. Read instead:
|
||||
|
||||
- `plan.org` — the design, the build sequence, and the open decisions
|
||||
- `spec-memory.md` — ownership, containers, places, generics, function values
|
||||
- `spec-conditions.md` — conditions and restarts, operational semantics
|
||||
- `sand.flan` — the first acceptance program
|
||||
- `syntax-sketch.flan` — the syntax, annotated with the decisions above
|
||||
- `../plan.org` — the design, the build sequence, and the open decisions
|
||||
- `../spec-memory.md` — ownership, containers, places, generics, function values
|
||||
- `../spec-conditions.md` — conditions and restarts, operational semantics
|
||||
- `../sand.flan` — the first acceptance program
|
||||
- `../syntax-sketch.flan` — the syntax, annotated with the decisions above
|
||||
|
||||
## What changed since
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Flan in Emacs
|
||||
|
||||
A manual for the editor side. It assumes you know Emacs and nothing about how
|
||||
Flan's dev loop is built — if you want that, `BUILT.md` has it.
|
||||
Flan's dev loop is built — if you want that, `docs/BUILT.md` has it.
|
||||
|
||||
The short version: you start a program, you keep it running, and you change it
|
||||
while it runs. Everything below is a variation on that.
|
||||
|
||||
@ -1409,7 +1409,7 @@ START and END, when given, are the region it came from, flashed on success.
|
||||
PAUSE, when given, is (BEG . END): the bounds of the form inside CODE the
|
||||
program should stop at. Only BEG goes on the wire — the daemon matches it
|
||||
against the location the reader attached to that form — and END is what the
|
||||
mark is drawn over here. Nothing is inserted in the buffer; see DISCUSS.md
|
||||
mark is drawn over here. Nothing is inserted in the buffer; see docs/DISCUSS.md
|
||||
§9."
|
||||
(let ((reply
|
||||
(flan-dev--request
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
;;
|
||||
;; Scalars only, so far. A struct or a slice needs a compile-time walk over
|
||||
;; its type — a `(watch "hp" hp)' form in the checker — and that is a file this
|
||||
;; change does not own. See BUILT.md.
|
||||
;; change does not own. See docs/BUILT.md.
|
||||
|
||||
;;; Code:
|
||||
|
||||
@ -533,7 +533,7 @@ Tears down both consumers. `flan-watch--drop' is the way to stop one of them."
|
||||
;; Still genuinely blocked on the checker, and unchanged by any of this: a
|
||||
;; **struct or a slice**. Rendering one is a compile-time walk over its type,
|
||||
;; the runtime has scalar entry points only, and no amount of searching the
|
||||
;; buffer produces a value the table does not hold. See BUILT.md.
|
||||
;; buffer produces a value the table does not hold. See docs/BUILT.md.
|
||||
|
||||
(provide 'flan-watch)
|
||||
;;; flan-watch.el ends here
|
||||
|
||||
@ -811,7 +811,7 @@ is written instead — the real `message' call the real command makes."
|
||||
|
||||
;; ── Marking a form with (pause) ───────────────────────────────────────
|
||||
;;
|
||||
;; DISCUSS.md §9: `C-u' before an evaluation marks a form so the program
|
||||
;; docs/DISCUSS.md §9: `C-u' before an evaluation marks a form so the program
|
||||
;; stops when it runs, and the buffer is never edited — the position goes on
|
||||
;; the wire beside the code and the daemon splices the call in after parsing.
|
||||
;;
|
||||
|
||||
@ -9,13 +9,13 @@
|
||||
;;;; is the same struct going into the call that actually draws with it.
|
||||
;;;;
|
||||
;;;; The hundred buildings are a `defvar` of fixed arrays rather than a Vec:
|
||||
;;;; a global cannot hold a Vec (PORTING.md §3) and does not need to here,
|
||||
;;;; a global cannot hold a Vec (docs/PORTING.md §3) and does not need to here,
|
||||
;;;; because the count is a constant in the C too. `(array n T)` is the zeroed
|
||||
;;;; fixed array, and the C's `= { 0 }` is exactly that.
|
||||
;;;;
|
||||
;;;; get-random-value comes from the generated half of the bindings. It is on
|
||||
;;;; no frame's path — it runs once, before the loop — and its Flan face is
|
||||
;;;; the C one, so PORTING.md §1's rule does not reach it.
|
||||
;;;; the C one, so docs/PORTING.md §1's rule does not reach it.
|
||||
;;;;
|
||||
;;;; One deliberate difference from the C: `(- camera.rotation 1.0)` and the
|
||||
;;;; clamp after it are written with the prelude's `clamp` macro rather than
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
;;;;
|
||||
;;;; examples/core/core_scissor_test.c. Needed begin-scissor-mode and
|
||||
;;;; end-scissor-mode, now hand-written in vendor/raylib/raylib.flan: they are
|
||||
;;;; a begin/end pair inside a frame, which is the class PORTING.md §1 says
|
||||
;;;; a begin/end pair inside a frame, which is the class docs/PORTING.md §1 says
|
||||
;;;; belongs in the hand-written half and not in the importer's. get-mouse-x
|
||||
;;;; and get-mouse-y come from the generated half — their Flan face is the C
|
||||
;;;; one, two plain i32s, and there is nothing for a hand-written line to say
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
;;;; window-state?, set-window-state, clear-window-state, toggle-fullscreen,
|
||||
;;;; minimize-window, maximize-window and restore-window all come from the
|
||||
;;;; generated half. That is the line drawn in vendor/raylib/bindings and it
|
||||
;;;; is worth saying why it falls here: the rule in PORTING.md §1 exists so
|
||||
;;;; is worth saying why it falls here: the rule in docs/PORTING.md §1 exists so
|
||||
;;;; that a build with no header set can still draw, and generated.flan is
|
||||
;;;; committed, so it can. What a hand-written line adds on top of that is a
|
||||
;;;; Flan face the C signature does not have — a Key instead of an int, a
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
;;;; Hand-written in raylib.flan rather than generated, and why each:
|
||||
;;;;
|
||||
;;;; begin-mode-3d / end-mode-3d a begin/end pair inside a frame, the
|
||||
;;;; class PORTING.md §1 names, and taken
|
||||
;;;; class docs/PORTING.md §1 names, and taken
|
||||
;;;; together the way begin-mode-2d is
|
||||
;;;; draw-cube / draw-cube-wires / draw-grid drawn every frame
|
||||
;;;; update-camera the Flan face differs: (Ptr Camera3D),
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
;;;; zeroed global standing in for the null pointer that means "the default
|
||||
;;;; ASCII set" — written for exactly this call, before anything called it.
|
||||
;;;;
|
||||
;;;; **Two things Flan would not do, both written up in PORTING.md.**
|
||||
;;;; **Two things Flan would not do, both written up in docs/PORTING.md.**
|
||||
;;;;
|
||||
;;;; 1. `GetCodepointPrevious` cannot be called at all. It reads BACKWARDS
|
||||
;;;; from the pointer it is handed, and a Flan `string` crosses to C as a
|
||||
@ -176,7 +176,7 @@
|
||||
;; Whether the TTF was there, asked once. A `defvar` and not the call itself
|
||||
;; in the draw loop: path-file? is a stat, and a syscall per frame to answer a
|
||||
;; question whose answer cannot change while the program runs is exactly what
|
||||
;; the per-frame rule in PORTING.md is about.
|
||||
;; the per-frame rule in docs/PORTING.md is about.
|
||||
(defvar font-present bool)
|
||||
(defvar show-font-atlas bool)
|
||||
(defvar cursor i32)
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
;;;; u8]` globals here. The dimensions are compile-time constants in the C
|
||||
;;;; too (the #defines and the two assignments right after), so nothing is
|
||||
;;;; lost by fixing them, and a global cannot hold a Vec in any case
|
||||
;;;; (PORTING.md §3). 375 is 25*15, written out because Flan's array length
|
||||
;;;; (docs/PORTING.md §3). 375 is 25*15, written out because Flan's array length
|
||||
;;;; must be a literal.
|
||||
|
||||
(import rl "vendor:raylib")
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
;;;; and because the picture is a demonstration of the filter rather than the
|
||||
;;;; point of the program.
|
||||
;;;;
|
||||
;;;; One gap, written up in PORTING.md and worked around in `reload-texture`
|
||||
;;;; One gap, written up in docs/PORTING.md and worked around in `reload-texture`
|
||||
;;;; below: LoadImageColors answers a (Ptr Color) and UpdateTexture takes a
|
||||
;;;; (Ptr u8), because the header spells its parameter `const void *` and Flan
|
||||
;;;; has no cast between pointer types. The address of the first field of the
|
||||
@ -143,7 +143,7 @@
|
||||
;; takes a (Ptr u8), the header having spelled that parameter `const void *`;
|
||||
;; and there is no cast between pointer types in Flan. What there is, is the
|
||||
;; address of the red channel of pixel zero, which is the address of the
|
||||
;; buffer said the long way round. See PORTING.md.
|
||||
;; buffer said the long way round. See docs/PORTING.md.
|
||||
(defn reload-texture [] ()
|
||||
(rl/unload-image im-copy)
|
||||
(set im-copy (rl/image-copy im-origin))
|
||||
|
||||
@ -189,7 +189,7 @@ let declared_name (d : decl) =
|
||||
|
||||
(* ── Instrumenting a form with (pause) ─────────────────────────────── *)
|
||||
|
||||
(* [C-u C-c C-c] marks a form so the program stops when it runs — DISCUSS.md
|
||||
(* [C-u C-c C-c] marks a form so the program stops when it runs — docs/DISCUSS.md
|
||||
§9. The mark travels beside the source as a position and is applied *here*,
|
||||
to the AST, rather than being spliced into the text the editor sends: text
|
||||
would shift every line and column after the insertion, and the error
|
||||
|
||||
@ -388,7 +388,7 @@ let wasm_main_source =
|
||||
(* ── The browser: the main loop, the shell, and the link ─────────────
|
||||
The mechanism, and why it is not the one the old note predicted.
|
||||
|
||||
BUILT.md says a web build "drives the loop with [emscripten_set_main_loop]
|
||||
docs/BUILT.md says a web build "drives the loop with [emscripten_set_main_loop]
|
||||
instead of a [while]. That is a different [main], not a different program."
|
||||
The first half is right about the browser and wrong about what it costs
|
||||
here: [emscripten_set_main_loop] wants the loop body as a callback, so every
|
||||
@ -916,7 +916,7 @@ let shared ?(opts = default) ~ir ~out () : timing =
|
||||
host. [flan reload] is precisely that caller. The complete answer is a
|
||||
marker symbol the host defines and a module references, so the loader
|
||||
refuses the pair at dlopen rather than the processor refusing it at a
|
||||
call. See HANDOFF-x86-aggregates.md. *)
|
||||
call. See docs/handoffs/HANDOFF-x86-aggregates.md. *)
|
||||
if opts.x86 then
|
||||
failwith
|
||||
"--x86: Build.shared is the LLVM redefinition path, and an --x86 host \
|
||||
|
||||
@ -1742,7 +1742,7 @@ and var ctx loc ~want name =
|
||||
(* spec-memory.md puts the allocator in the calling convention as
|
||||
[context/allocator] and [context/temp]. They read as names rather than
|
||||
calls because that is how the spec writes them, and they are dynamic
|
||||
variables at run time rather than extra parameters — see BUILT.md for why
|
||||
variables at run time rather than extra parameters — see docs/BUILT.md for why
|
||||
the literal reading of "calling convention" is deferred. *)
|
||||
| "context/allocator" ->
|
||||
expect loc ~want
|
||||
@ -3493,7 +3493,7 @@ and named_call ctx ~want loc name args =
|
||||
expect loc ~want
|
||||
(mk loc Types.Alloc (Tast.Prim (Tast.Rt "flan_arena_new", [ cap ])))
|
||||
(* Hands the pages back, which [free-all] deliberately does not — see
|
||||
BUILT.md, "free-all is retain-capacity". *)
|
||||
docs/BUILT.md, "free-all is retain-capacity". *)
|
||||
| "arena-destroy" ->
|
||||
arity loc name 1 args;
|
||||
let a = check ctx ~want:Types.Alloc (List.hd args) in
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
[declare-c] closed half the gap: the wrapper, the typedefs and the
|
||||
prototype are generated, so they cannot disagree with each other. The half
|
||||
it left open is the one BUILT.md records as *trusted* — that the signature
|
||||
it left open is the one docs/BUILT.md records as *trusted* — that the signature
|
||||
somebody typed is the function's real signature. Nothing checked it,
|
||||
because no header was ever read. This reads one.
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
package's [defstruct]s against the header's records — would be comparing
|
||||
the header with itself. Keeping the layouts hand-written is what makes
|
||||
[check_structs] an independent second source, and that check is the
|
||||
cheapest real closure of BUILT.md's trusted-not-guaranteed gap: a
|
||||
cheapest real closure of docs/BUILT.md's trusted-not-guaranteed gap: a
|
||||
[_Static_assert] was rejected there as circular for exactly this reason,
|
||||
and this is not circular, because the two sides have different authors.
|
||||
|
||||
@ -810,7 +810,7 @@ let of_dump ~env ~taken ~bound_syms ~config (d : dump) : imported =
|
||||
|
||||
(* The point of reading a header that the generator does not otherwise need.
|
||||
|
||||
BUILT.md rejected a [_Static_assert] on [sizeof]/[offsetof] as circular:
|
||||
docs/BUILT.md rejected a [_Static_assert] on [sizeof]/[offsetof] as circular:
|
||||
both sides would have come from the same field list. This is not circular.
|
||||
The [defstruct] was written by hand and the record comes from the library's
|
||||
own header, so a disagreement is real information — and it is the failure
|
||||
@ -868,7 +868,7 @@ let check_structs ~env ~(structs : (string * Ast.field list) list) (d : dump) =
|
||||
| None -> None)
|
||||
in
|
||||
(* Names and widths both. Order is what a permuted [defstruct] gets wrong and
|
||||
what BUILT.md says only a test can catch; width is the other half of the
|
||||
what docs/BUILT.md says only a test can catch; width is the other half of the
|
||||
same hazard and the one it calls out by name — [f64] where the library
|
||||
says [float] lays out eight bytes where there are four, and every field
|
||||
after it moves. Comparing the rendered Flan type rather than the C
|
||||
@ -1268,7 +1268,7 @@ let header ~loc ~header:h ~flags ~known_structs ~known_enums ~taken ~bound_syms
|
||||
|
||||
(* ── Printing a declaration back as source ─────────────────────────── *)
|
||||
|
||||
(* Which makes the third option in DISCUSS.md item 6 available at no extra
|
||||
(* Which makes the third option in docs/DISCUSS.md item 6 available at no extra
|
||||
cost: generate the declarations from the header, *commit the result*, and
|
||||
regenerate when the library moves. That trade — explicit in the source,
|
||||
checked against reality, no header read at build time — is a real one, and
|
||||
@ -1309,7 +1309,7 @@ let decl_source (d : Ast.decl) =
|
||||
something more specific for a reason it recorded.
|
||||
|
||||
What is left after those is a real disagreement about a width, an arity or a
|
||||
direction — which is exactly the class of bug BUILT.md warns about, where
|
||||
direction — which is exactly the class of bug docs/BUILT.md warns about, where
|
||||
[f64] against the library's [float] reads as garbage rather than as a link
|
||||
error. *)
|
||||
|
||||
@ -1378,7 +1378,7 @@ let diff_bound ~env ~(bound : (Ast.fn * string) list) (d : dump) =
|
||||
|
||||
(* Generate once, commit the result, regenerate when the library moves.
|
||||
|
||||
What that buys is in DISCUSS.md item 6 and it is not caching — the dump is
|
||||
What that buys is in docs/DISCUSS.md item 6 and it is not caching — the dump is
|
||||
already cached on disk and in memory, so a build that reads the header pays
|
||||
for it once either way. It is that no header is needed by *anybody*: the
|
||||
declarations are in the repository, so they are greppable, they diff when
|
||||
@ -1398,7 +1398,7 @@ let diff_bound ~env ~(bound : (Ast.fn * string) list) (d : dump) =
|
||||
they come from, because the one function that writes the file is this one
|
||||
and it refuses when the two disagree. A regeneration that quietly rewrote
|
||||
the bindings against a header the library does not match would produce
|
||||
exactly the failure BUILT.md warns about — a permuted struct read as five
|
||||
exactly the failure docs/BUILT.md warns about — a permuted struct read as five
|
||||
plausible numbers rather than as a link error — and it would produce it in a
|
||||
committed file that looks reviewed.
|
||||
|
||||
|
||||
@ -1017,7 +1017,7 @@ let locals t ~frame =
|
||||
refused) ]))
|
||||
|
||||
(* [(:op "inspect" :frame N :slot I :path (...))] — the inspector's second
|
||||
rooting mode. [BUILT.md]'s "Two ways to root a walk" says what each root
|
||||
rooting mode. [docs/BUILT.md]'s "Two ways to root a walk" says what each root
|
||||
can and cannot do; this is the half that names a frame.
|
||||
|
||||
[i] in the break buffer used to send a local's *name* to be evaluated as an
|
||||
@ -1080,7 +1080,7 @@ let inspect t ~frame ~slot ~path =
|
||||
|
||||
(* ── The allocation registry, read from this end ───────────────────── *)
|
||||
|
||||
(* [BUILT.md]'s "An address answers with a type" is what the table is and why.
|
||||
(* [docs/BUILT.md]'s "An address answers with a type" is what the table is and why.
|
||||
What follows is the reader: three verbs that ask the agent for what is
|
||||
recorded, and one of them turns a recorded *name* back into a type.
|
||||
|
||||
@ -2530,7 +2530,7 @@ let two_process ?(debug = false) ~file ~sock () =
|
||||
(* Everything above this line works the same either way. What follows is the
|
||||
merged build: one executable that is the compiled Flan program *and* holds
|
||||
the whole OCaml compiler, with the editor's socket served from a thread
|
||||
inside it. DISCUSS.md item 14 is the spike this is built from.
|
||||
inside it. docs/DISCUSS.md item 14 is the spike this is built from.
|
||||
|
||||
The shape, and it is this way round for a reason:
|
||||
|
||||
|
||||
@ -164,7 +164,7 @@ CAMLprim value flan_peek_bytes(value p, value off, value n) {
|
||||
*
|
||||
* The runtime system is released across the call. A delivery does a [dlopen],
|
||||
* which is milliseconds of relocation and the loader lock, and holding OCaml's
|
||||
* lock through it stalls every other OCaml thread for no reason — DISCUSS.md
|
||||
* lock through it stalls every other OCaml thread for no reason — docs/DISCUSS.md
|
||||
* §14's third cost, in the one place this lane creates it. Nothing the agent
|
||||
* does touches an OCaml value, so there is nothing to keep alive across it. */
|
||||
extern char *flan_agent_request(const char *line, uint64_t *len)
|
||||
|
||||
@ -1034,7 +1034,7 @@ and value_at f (e : Tast.expr) : string =
|
||||
same name would do, at the point the *address* is taken rather than at the
|
||||
call. What that does not give is a value taken before a redefinition and
|
||||
called after it: that one is still the old body, because there is nothing
|
||||
left to re-resolve once the address is in a slot. Named in BUILT.md rather
|
||||
left to re-resolve once the address is in a slot. Named in docs/BUILT.md rather
|
||||
than papered over with a trampoline. *)
|
||||
| Tast.FnAddr (Tast.Fnval n) -> body_of f n
|
||||
| Tast.Addr p -> fst (place f p)
|
||||
@ -2121,7 +2121,7 @@ and prim f (e : Tast.expr) (p : Tast.prim) (args : Tast.expr list) =
|
||||
only [Rt] symbols that can transfer, so they are the only ones that take
|
||||
the channel and the only ones guarded — everything else in this family
|
||||
is arithmetic over a container header and cannot reach a handler. A Vec
|
||||
is checked inside the runtime rather than in emitted IR (BUILT.md), so
|
||||
is checked inside the runtime rather than in emitted IR (docs/BUILT.md), so
|
||||
this is where (at v i) gets what (at arr i) gets from [check_at]. *)
|
||||
let signals =
|
||||
String.equal sym "flan_vec_at" || String.equal sym "flan_vec_as_slice"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
(** Macro expansion: the pass between the reader and [Parse].
|
||||
|
||||
There is no interpreter and there is not going to be one (BUILT.md, "Why
|
||||
There is no interpreter and there is not going to be one (docs/BUILT.md, "Why
|
||||
there is no interpreter"), so running a macro at compile time means
|
||||
compiling it and loading it into this process. Every piece of that is
|
||||
already built and measured — [Emit.macro_thunk], [Build.macro_module],
|
||||
|
||||
@ -341,7 +341,7 @@ let qualify_decl owned alias (d : Ast.decl) : Ast.decl =
|
||||
[(Form.Sym {.s "begin"})] whose name is a *string in an argument* rather
|
||||
than a name anything would rename. That is not an accident to work around:
|
||||
it is the property [Macro]'s walk depends on, the one that makes a
|
||||
quasiquoted call output rather than a compile-order dependency (BUILT.md,
|
||||
quasiquoted call output rather than a compile-order dependency (docs/BUILT.md,
|
||||
"A call inside a quasiquote is output, not a dependency"). It is also
|
||||
exactly what puts the name out of a rename's reach.
|
||||
|
||||
@ -421,7 +421,7 @@ let rec rename_form owned alias bound (f : Form.t) : Form.t =
|
||||
Desugared on the way out, because [Macro.program] is handed forms that
|
||||
[Parse.parse_forms] has already run [Expand.quasiquote] over and its rounds
|
||||
read them with that assumed. An un-desugared one would make a quasiquoted
|
||||
call look like a real one, which is the false ring BUILT.md records the
|
||||
call look like a real one, which is the false ring docs/BUILT.md records the
|
||||
first cycle test walking into. *)
|
||||
let qualify_macro owned alias (f : Form.t) : Form.t option =
|
||||
match f.Form.v with
|
||||
@ -704,7 +704,7 @@ let words line =
|
||||
nothing if it is not".
|
||||
|
||||
That marker is what lets a package offer the check without requiring it.
|
||||
[vendor/raylib] builds today against a shared library alone — BUILT.md's "no
|
||||
[vendor/raylib] builds today against a shared library alone — docs/BUILT.md's "no
|
||||
raylib headers are needed", which is a real property: a build needs
|
||||
libraylib linkable and not raylib-devel installed. A required header would
|
||||
take that away from everyone in order to give the check to the people who
|
||||
@ -969,7 +969,7 @@ let rec import ~seen ~open_ ~loc alias dir =
|
||||
the header is the package's own claim about what it binds; a
|
||||
[defstruct] that disagrees with it lays fields out in the
|
||||
wrong order and reads as five plausible numbers rather than as
|
||||
a link error, which is the failure BUILT.md says only a test
|
||||
a link error, which is the failure docs/BUILT.md says only a test
|
||||
can catch. Continuing past a known-wrong layout to produce a
|
||||
program that will read garbage is the shape the house rule
|
||||
against swallowing things exists to prevent.
|
||||
|
||||
@ -1466,7 +1466,7 @@ let source = {flan|
|
||||
;; site attached to what a macro produces", and it is what the queued
|
||||
;; structured-error work will read.
|
||||
;;
|
||||
;; Case order is the tag order (BUILT.md, unions), so this list is a layout
|
||||
;; Case order is the tag order (docs/BUILT.md, unions), so this list is a layout
|
||||
;; contract with lib/expand.ml's marshaller and may not be reordered.
|
||||
(defunion Form
|
||||
[(Sym [s string])
|
||||
|
||||
@ -87,7 +87,7 @@ let rec same_const (a : Tast.expr) (b : Tast.expr) =
|
||||
desugars a package's macro on its way out for the same reason — but
|
||||
[Parse.imported_macros] is read by [Macro.program] directly, past that map.
|
||||
An undesugared body still has its quasiquote in it, which makes a
|
||||
quasiquoted call look like a real one: the false ring BUILT.md records the
|
||||
quasiquoted call look like a real one: the false ring docs/BUILT.md records the
|
||||
first cycle test walking into.
|
||||
|
||||
Unqualified, and that is the point: a buffer writes its own macro's bare
|
||||
@ -345,7 +345,7 @@ type change = {
|
||||
}
|
||||
|
||||
(* [pause] is [C-u C-c C-c]: the position, in the source just sent, of the form
|
||||
the program should stop at — DISCUSS.md §9. It arrives as a separate field
|
||||
the program should stop at — docs/DISCUSS.md §9. It arrives as a separate field
|
||||
rather than spliced into [src], because splicing text would move every
|
||||
location after it, and it is applied below to the *declarations*, once
|
||||
parsing has attached those locations and [Load] has qualified the names.
|
||||
|
||||
12
lib/x86.ml
12
lib/x86.ml
@ -7,7 +7,7 @@
|
||||
|
||||
{1 The internal calling convention}
|
||||
|
||||
The spike's report (DISCUSS.md item 15) called the internal convention the
|
||||
The spike's report (docs/DISCUSS.md item 15) called the internal convention the
|
||||
sharpest obstacle, because LLVM's answer for a first-class struct is an
|
||||
implementation detail discoverable only by disassembly — a 24-byte struct
|
||||
comes back in [rax]:[rdx]:[rcx], and [rcx] is a register SysV never uses
|
||||
@ -745,7 +745,7 @@ let addr_sym f ~dst s =
|
||||
holds the cell's *address*. Collapsing them was this lane's one real bug,
|
||||
and it looked exactly right in the disassembly: [mov r11, cell@GOTPCREL(%rip)]
|
||||
beside [call *%r11] reads as "call through the cell" and in fact calls the
|
||||
cell. DISCUSS.md item 15 said this is how hand-encoding fails. *)
|
||||
cell. docs/DISCUSS.md item 15 said this is how hand-encoding fails. *)
|
||||
let load_sym f ~dst s =
|
||||
if f.ext s then begin
|
||||
load_int f.b ~dst ~mm:(Got s) ~size:8 ~signed:false;
|
||||
@ -1118,7 +1118,7 @@ and lower_at f (e : Tast.expr) (dst : loc) : unit =
|
||||
| Tast.None_ -> zero_value f dst t
|
||||
(* Reading an uninitialised value gives whatever the slot held: stable
|
||||
garbage rather than LLVM's [poison]. The one construct where the two
|
||||
backends are meant to differ — DISCUSS.md item 15, question 4. *)
|
||||
backends are meant to differ — docs/DISCUSS.md item 15, question 4. *)
|
||||
| Tast.Uninit _ -> ()
|
||||
| Tast.Local _ | Tast.Global _ | Tast.Field _ | Tast.Deref _ ->
|
||||
let src = lvalue f e in
|
||||
@ -1877,9 +1877,9 @@ and check_slice f (base : loc) (ty : Types.t) (loc : Loc.t) (lo : Tast.expr)
|
||||
bounds_call f "flan_slice_error" loc [ a; b; c ];
|
||||
lbl f.b ok)
|
||||
|
||||
(* [emit.ml]'s [check_div] and [check_cast], item 3 of HANDOFF-x86-rt.md's
|
||||
(* [emit.ml]'s [check_div] and [check_cast], item 3 of docs/handoffs/HANDOFF-x86-rt.md's
|
||||
list, and the one item on it that was blocked on a language decision rather
|
||||
than on code. That decision is in HANDOFF-arith.md: a divide or remainder by
|
||||
than on code. That decision is in docs/handoffs/HANDOFF-arith.md: a divide or remainder by
|
||||
zero, the one division that overflows, and a float to integer cast whose
|
||||
value does not fit all signal ArithError, exactly as a bad index signals
|
||||
BoundsError.
|
||||
@ -2112,7 +2112,7 @@ and call_c f ~sym ~args ~rty dst =
|
||||
[Rt] symbols that can transfer, so they are the only ones that take the
|
||||
channel and the only ones guarded — everything else in this family is
|
||||
arithmetic over a container header and cannot reach a handler. A Vec is
|
||||
checked inside the runtime rather than in emitted code (BUILT.md), so this
|
||||
checked inside the runtime rather than in emitted code (docs/BUILT.md), so this
|
||||
is where [(at v i)] gets what [(at arr i)] gets from [check_at]. *)
|
||||
and rt_signals sym =
|
||||
String.equal sym "flan_vec_at" || String.equal sym "flan_vec_as_slice"
|
||||
|
||||
@ -608,7 +608,7 @@ void flan_dev_watch_end(void) {
|
||||
* four [flan_dev_watch_emit_*] above are the emitter it would be pointed at,
|
||||
* shaped exactly like the [print] arm's. What is missing is the
|
||||
* [(watch "hp" hp)] arm in check.ml that joins the two, which is a file this
|
||||
* change does not own. BUILT.md says what that arm is. */
|
||||
* change does not own. docs/BUILT.md says what that arm is. */
|
||||
int32_t flan_dev_watch_i64(const char *name, int64_t x) {
|
||||
if (!flan_dev_watch_begin(name)) return 0;
|
||||
flan_dev_watch_emit_i64(x);
|
||||
@ -642,7 +642,7 @@ int32_t flan_dev_watch_str(const char *name, const char *s) {
|
||||
/* The scalar watch above keeps one value per name, and from a hot inner loop
|
||||
* that is nearly useless: you see whichever of the 91,200 cells happened to
|
||||
* run last. This is the other half — the port of [spy-num] from the author's
|
||||
* watch.clj, which exists for exactly that reason and is the piece PORTING.md
|
||||
* watch.clj, which exists for exactly that reason and is the piece docs/PORTING.md
|
||||
* calls the least obvious and the most valuable.
|
||||
*
|
||||
* **What a slot keeps: count, min, max, last, mean.** Five numbers, and the
|
||||
@ -1053,7 +1053,7 @@ static int flan_reg_full; /* something found no slot */
|
||||
* flan_rt.c are called unconditionally and begin with this load and a
|
||||
* not-taken branch, because flan_dev.c is linked into every build and a
|
||||
* second version of the allocator gated on a build flag is worse than a
|
||||
* branch. That is a real cost and not zero; BUILT.md says so rather than
|
||||
* branch. That is a real cost and not zero; docs/BUILT.md says so rather than
|
||||
* repeating the claim that a release build carries nothing. */
|
||||
static void flan_reg_report(void); /* the exit report, at the bottom */
|
||||
|
||||
@ -1378,7 +1378,7 @@ int64_t flan_dev_reg_by_type(int32_t live_only, int64_t *counts,
|
||||
* Registered by [flan_dev_reg_enable] and therefore only in a dev build,
|
||||
* which is the point: a file-scope destructor would run in *every* build,
|
||||
* because this file is compiled into every build, and that would be a third
|
||||
* place a release build is not free. BUILT.md names two and only two.
|
||||
* place a release build is not free. docs/BUILT.md names two and only two.
|
||||
*
|
||||
* Off unless FLAN_DEV_LEAKS is set, and that is not timidity. The acceptance
|
||||
* table reads programs/registry.flan's output with stderr folded in, so a
|
||||
|
||||
@ -774,7 +774,7 @@ static int flan_over_budget(flan_allocator *a, int64_t size) {
|
||||
* an address, or a range of them. So this half is unconditional and calls into
|
||||
* flan_dev.c, which is linked into every build and begins each of these with a
|
||||
* load of a flag that only a dev build ever sets. A release build pays a load
|
||||
* and a not-taken branch per free, which is not nothing, and BUILT.md says so.
|
||||
* and a not-taken branch per free, which is not nothing, and docs/BUILT.md says so.
|
||||
*/
|
||||
void flan_dev_reg_note(void *base, int64_t bytes, int64_t elem,
|
||||
const char *type, int64_t typelen);
|
||||
@ -905,7 +905,7 @@ static flan_allocator flan_heap = {
|
||||
/* -- The arena: one fixed backing buffer and a bump offset. ----------
|
||||
*
|
||||
* `free-all` is retain-capacity: offset = 0, the pages stay. That is an
|
||||
* announced amendment to spec-memory.md's operation table (see BUILT.md) and
|
||||
* announced amendment to spec-memory.md's operation table (see docs/BUILT.md) and
|
||||
* it is what Odin's arena_free_all already does in effect. Handing the pages
|
||||
* back is `arena-destroy`, a separate operation, because a frame arena reset
|
||||
* every frame must not return memory only to ask for it again.
|
||||
@ -998,7 +998,7 @@ static void *flan_arena_proc(flan_allocator *a, int32_t mode, void *p,
|
||||
* signature. The spec calls it part of the calling convention; taking that
|
||||
* literally would touch every function signature, the FFI shim, the dev
|
||||
* trampolines and the reload ABI, for the same observable behaviour. The
|
||||
* literal reading is deferred and BUILT.md says so.
|
||||
* literal reading is deferred and docs/BUILT.md says so.
|
||||
*
|
||||
* There are no threads in Flan, so a plain global is the whole of it. */
|
||||
|
||||
@ -1133,7 +1133,7 @@ _Noreturn void flan_free_all_fail(const uint8_t *loc, int64_t loclen) {
|
||||
*
|
||||
* ptr len cap allocator the release layout spec-memory.md fixes
|
||||
* gen bumped on every reallocation — the stale-slice
|
||||
* word. It has no reader yet; see BUILT.md.
|
||||
* word. It has no reader yet; see docs/BUILT.md.
|
||||
* epoch the allocator's epoch when this Vec last
|
||||
* touched it. Any operation on a container whose
|
||||
* recorded epoch has moved traps.
|
||||
@ -1142,7 +1142,7 @@ _Noreturn void flan_free_all_fail(const uint8_t *loc, int64_t loclen) {
|
||||
* is not laziness: a redefinition module is built by llc and ld against a host
|
||||
* that was built separately, and nothing makes the two agree on a struct size.
|
||||
* A layout that changes with a build flag is a layout that can disagree across
|
||||
* that boundary silently. Dropping them in release is deferred and BUILT.md
|
||||
* that boundary silently. Dropping them in release is deferred and docs/BUILT.md
|
||||
* says what it is blocked on.
|
||||
*
|
||||
* Every entry point returns int8_t 1/0 for "did it fit", and never reports
|
||||
@ -1243,7 +1243,7 @@ static int8_t flan_vec_grow(flan_vec *v, int64_t want, int64_t size,
|
||||
v->ptr = p;
|
||||
v->cap = cap;
|
||||
/* Any slice taken before this points at storage that may have moved. The
|
||||
* word is bumped here and read nowhere yet; see BUILT.md. */
|
||||
* word is bumped here and read nowhere yet; see docs/BUILT.md. */
|
||||
v->gen++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -95,4 +95,4 @@ int64_t spike_probe_align(int64_t x) {
|
||||
* omission: check.ml rejects an aggregate in a [declare] signature and the
|
||||
* generated shim flattens every one, so no Flan-emitted call ever passes a
|
||||
* struct to C. The aggregate problem is real but it is on the Flan-to-Flan
|
||||
* side, which is measured in DISCUSS.md item 15 and not from here. */
|
||||
* side, which is measured in docs/DISCUSS.md item 15 and not from here. */
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# What the hand-written x86-64 backend costs
|
||||
|
||||
`survey.sh` has said for three handoffs that this backend agrees with LLVM on all 97 corpus programs it can build.
|
||||
Item 7 of `HANDOFF-x86-rt.md` is the other half of that sentence — nobody had a number for what the agreement
|
||||
Item 7 of `docs/handoffs/HANDOFF-x86-rt.md` is the other half of that sentence — nobody had a number for what the agreement
|
||||
costs — and it came with a list of suspects: a guard after every call, three frame temporaries per bounds check,
|
||||
every intermediate in memory, `rep movsb` block copies, and an extra load per call site in a dev build. This is
|
||||
the measurement. It does not change anything; two of the five suspects turn out not to matter, and the one that
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# What does the hand-written backend cost, against LLVM, on the same programs?
|
||||
#
|
||||
# survey.sh answers "does it agree". This answers "what does agreeing cost",
|
||||
# which is item 7 of HANDOFF-x86-rt.md and the one thing about this backend
|
||||
# which is item 7 of docs/handoffs/HANDOFF-x86-rt.md and the one thing about this backend
|
||||
# nobody had a number for. It builds each program the same two ways the
|
||||
# survey does, and for each records three sizes and a time:
|
||||
#
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# Does the hand-written backend agree with LLVM?
|
||||
#
|
||||
# The only honest test of a hand-encoded backend is what the program prints and
|
||||
# what it exits with -- DISCUSS.md item 15 and item 16 both say so, and both
|
||||
# what it exits with -- docs/DISCUSS.md item 15 and item 16 both say so, and both
|
||||
# say it after a disassembly that read perfectly beside a wrong answer. So this
|
||||
# builds every program in test/programs twice, runs both, and diffs stdout,
|
||||
# stderr and the exit status. objdump is for after a program already has the
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
;;;; with it and the session is the thing the project is built around never
|
||||
;;;; having to restart. And the route in is the most ordinary one there is: a
|
||||
;;;; grid indexed from a mouse position is out of bounds the first time the
|
||||
;;;; pointer leaves the window (PORTING.md, §3).
|
||||
;;;; pointer leaves the window (docs/PORTING.md, §3).
|
||||
;;;;
|
||||
;;;; So a failed bounds check signals BoundsError with `error`, the same way a
|
||||
;;;; failed allocation signals StorageExhausted, and dies with the old message
|
||||
@ -28,7 +28,7 @@
|
||||
;;;;
|
||||
;;;; 1. The frame is abandoned and the program carries on. `frames` counts
|
||||
;;;; the ones that finished and `skipped` the ones that did not.
|
||||
;;;; 2. **Defers run.** A trap ran none, which BUILT.md recorded as following
|
||||
;;;; 2. **Defers run.** A trap ran none, which docs/BUILT.md recorded as following
|
||||
;;;; from the noreturn-then-unreachable shape rather than as a decision.
|
||||
;;;; The shape changed, so the question had to be answered rather than
|
||||
;;;; inherited: an *answered* bounds failure leaves through the same
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
;;;; editor picks up a stopped program with the stack, the locals and the
|
||||
;;;; globals readable and the restarts on offer.
|
||||
;;;;
|
||||
;;;; That is the claim `PORTING.md` said was missing, and it is worth testing
|
||||
;;;; That is the claim `docs/PORTING.md` said was missing, and it is worth testing
|
||||
;;;; end to end rather than reasoning about: before this, a bad index called
|
||||
;;;; exit(134), and with `flan dev` running as one process that took the
|
||||
;;;; compiler and the session with it.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
;;;; Frame rollback: the half of the frame loop that `continue` does not do.
|
||||
;;;;
|
||||
;;;; PORTING.md Tier 1 item 6. Not a language feature — restart-case, struct
|
||||
;;;; docs/PORTING.md Tier 1 item 6. Not a language feature — restart-case, struct
|
||||
;;;; assignment and fixed-arrays-as-values are all built. This is the worked
|
||||
;;;; example, the way sand.flan is the worked example for the loop itself.
|
||||
;;;;
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
;;;; It is all one function because a Pool is move-only exactly as a Vec is,
|
||||
;;;; so passing one to a helper *consumes* it — there is no borrowing
|
||||
;;;; parameter in the language yet. That is not a pool question and this
|
||||
;;;; program does not work around it; see BUILT.md.
|
||||
;;;; program does not work around it; see docs/BUILT.md.
|
||||
|
||||
(defstruct Enemy [hp i32 kind i32])
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
;;;;
|
||||
;;;; Nothing here introduces a name the host was not built with. X86.redefinition
|
||||
;;;; refuses those by name — that is the registry path, and it is item 1 of
|
||||
;;;; HANDOFF-x86-redef.md's "what remains" rather than anything to do with
|
||||
;;;; docs/handoffs/HANDOFF-x86-redef.md's "what remains" rather than anything to do with
|
||||
;;;; aggregates.
|
||||
|
||||
(defstruct Pair [a i64 b i64])
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
;;;; The aggregate case across the reload boundary, v1 (HANDOFF-x86-redef.md,
|
||||
;;;; The aggregate case across the reload boundary, v1 (docs/handoffs/HANDOFF-x86-redef.md,
|
||||
;;;; item 4).
|
||||
;;;;
|
||||
;;;; `reload.flan' proves that a redefined body is reached; every signature in
|
||||
|
||||
@ -1053,7 +1053,7 @@ let () =
|
||||
out of 54 is a fact about the Iroha that a lost byte anywhere in that
|
||||
chain would change. The two walk rows are what stands in for
|
||||
GetCodepointPrevious, which cannot be called from Flan at all — see
|
||||
PORTING.md, and the file's own header for what each row fails on. *)
|
||||
docs/PORTING.md, and the file's own header for what each row fails on. *)
|
||||
let raylib_codepoints_out =
|
||||
"codepoints 54\n\
|
||||
unique 49\n\
|
||||
@ -2799,7 +2799,7 @@ ERR@7 unexpected token: not the kind the caller was reading
|
||||
offset its own alignment allows, so the offset of field 1 in
|
||||
{ i8, T } *is* alignof(T). Reading [2 x i64] out of the emitted type and
|
||||
concluding 8 would be asserting the layout against itself, which is the
|
||||
circularity BUILT.md already rejected for _Static_assert. *)
|
||||
circularity docs/BUILT.md already rejected for _Static_assert. *)
|
||||
let llvm_align ir sname =
|
||||
let sty = Printf.sprintf "%%\"%s\"" sname in
|
||||
let b = Buffer.create 512 in
|
||||
|
||||
@ -1768,7 +1768,7 @@ let () =
|
||||
The redefinition binds a local, deliberately, because that is what
|
||||
the detector can see. [Emit.slot_fingerprint] hashes a body's
|
||||
*slots*, so a new body with the same slots and different global
|
||||
references is not caught — the hole is stated in BUILT.md, and a
|
||||
references is not caught — the hole is stated in docs/BUILT.md, and a
|
||||
test that asserted otherwise would be asserting a mechanism that is
|
||||
not there. *)
|
||||
let r =
|
||||
@ -2421,7 +2421,7 @@ let () =
|
||||
|
||||
(* ── Marking a form with (pause), from the editor's side ───────── *)
|
||||
|
||||
(* DISCUSS.md §9: C-u before an evaluation marks a form so the program
|
||||
(* docs/DISCUSS.md §9: C-u before an evaluation marks a form so the program
|
||||
stops when it runs. The mark travels as a *position* beside the code
|
||||
rather than spliced into it — splicing text would move every line and
|
||||
column after the insertion, and the error overlays, the break loop's
|
||||
|
||||
@ -1632,7 +1632,7 @@ let () =
|
||||
(* The struct check, which is the point of reading a header the generator
|
||||
does not otherwise need: the defstruct and the header's record have
|
||||
different authors, so a disagreement is real information. A
|
||||
_Static_assert was rejected in BUILT.md as circular for want of exactly
|
||||
_Static_assert was rejected in docs/BUILT.md as circular for want of exactly
|
||||
that. *)
|
||||
let structs_of ds =
|
||||
List.filter_map
|
||||
@ -1642,7 +1642,7 @@ let () =
|
||||
in
|
||||
check "a defstruct that matches the header is not reported"
|
||||
(Cimport.check_structs ~env ~structs:(structs_of fixture_ds) dump = []);
|
||||
(* Permuted: the failure BUILT.md says only a test can catch, because every
|
||||
(* Permuted: the failure docs/BUILT.md says only a test can catch, because every
|
||||
field still reads as a plausible number. *)
|
||||
check "a permuted defstruct is reported"
|
||||
(match
|
||||
@ -1651,7 +1651,7 @@ let () =
|
||||
with
|
||||
| [ ("Pair", why) ] -> contains why "field order"
|
||||
| _ -> false);
|
||||
(* Widened: the other half of the same hazard and the one BUILT.md names —
|
||||
(* Widened: the other half of the same hazard and the one docs/BUILT.md names —
|
||||
f64 where the library says float lays out eight bytes where there are
|
||||
four, and every field after it moves. *)
|
||||
check "a widened field is reported"
|
||||
@ -1684,7 +1684,7 @@ let () =
|
||||
~structs:(structs_of (program "(defstruct Feel [mood i32 n i32])\n")) dump
|
||||
= []);
|
||||
(* And the whole point of the check survives it. The tolerance is for a
|
||||
32-bit integer and nothing else, so the width hazard BUILT.md names — f64
|
||||
32-bit integer and nothing else, so the width hazard docs/BUILT.md names — f64
|
||||
where the library says float — still fails, in the very struct whose
|
||||
other field is an enum. *)
|
||||
check "a widened field beside an enum field is still reported"
|
||||
|
||||
@ -201,7 +201,7 @@ let () =
|
||||
fail "reload\n got: %S (exit %d)\n wanted: %S" text code want;
|
||||
|
||||
(* The same thing again, compiled by the dev backend end to end (x86.ml's
|
||||
header, HANDOFF-x86-rt.md item 1). Both halves, host and module, because
|
||||
header, docs/handoffs/HANDOFF-x86-rt.md item 1). Both halves, host and module, because
|
||||
the two backends' conventions agree on every scalar and disagree on
|
||||
every aggregate: an LLVM-built module dlopened into an --x86 host would
|
||||
be correct until the first redefined function took or returned a struct.
|
||||
@ -213,7 +213,7 @@ let () =
|
||||
flan_dev_global lookups that needs are refused there by name.
|
||||
|
||||
Read by running, not by reading. A disassembly reads correctly beside a
|
||||
wrong answer often enough (DISCUSS.md item 15) that only the printed
|
||||
wrong answer often enough (docs/DISCUSS.md item 15) that only the printed
|
||||
transcript settles it: [outer] is compiled once into the host and never
|
||||
rebuilt, so "after2 1204" can only mean its call site followed a body
|
||||
that this backend emitted, published through a cell it reached via the
|
||||
@ -365,7 +365,7 @@ let () =
|
||||
segfaults with this guard in place, re-measured after it landed. The
|
||||
guard catches a caller holding one option record; it cannot catch a
|
||||
caller holding two, and [flan reload] is exactly that caller. See
|
||||
HANDOFF-x86-aggregates.md. *)
|
||||
docs/handoffs/HANDOFF-x86-aggregates.md. *)
|
||||
(match Build.shared ~opts:x86 ~ir:"" ~out:(tmp "never.so") () with
|
||||
| _ -> fail "Build.shared accepted an --x86 option record"
|
||||
| exception Failure m when has m "--x86" -> ()
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
* settles it. Memcheck answers both, and needs no instrumentation whatever:
|
||||
* it works on the binary, so hand-written IR, clang-compiled C and libc all
|
||||
* arrive on the same footing. Nothing in [Emit] has to cooperate, which is
|
||||
* the exact opposite of the sanitize_address attribute story in BUILT.md.
|
||||
* the exact opposite of the sanitize_address attribute story in docs/BUILT.md.
|
||||
*
|
||||
* The two tools are not ordered, they are complementary, and the measurement
|
||||
* that shows it is bounds.flan: ASan catches three of its six deliberate
|
||||
|
||||
@ -181,7 +181,7 @@ let () =
|
||||
end;
|
||||
|
||||
(* ── raylib in the browser ────────────────────────────────────────
|
||||
The claim BUILT.md left open. core-basic-window.flan is built for the
|
||||
The claim docs/BUILT.md left open. core-basic-window.flan is built for the
|
||||
web unchanged — no edit to its `until` loop, which is the whole point
|
||||
of choosing asyncify over emscripten_set_main_loop — and the module is
|
||||
then read for the two things that prove the claim rather than assert
|
||||
@ -237,7 +237,7 @@ let () =
|
||||
no DOM and no WebGL, so the module instantiates, main runs, and
|
||||
InitWindow dies inside glfwInit on `window is not defined` — which
|
||||
says the module is live but says nothing about whether the canvas
|
||||
paints. Only a human opening it can say that; BUILT.md carries the
|
||||
paints. Only a human opening it can say that; docs/BUILT.md carries the
|
||||
commands. *)
|
||||
let out = Filename.concat scratch "flan-web-sand.html" in
|
||||
(match web_build "../sand.flan" out with
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
# malloc(cap), so every byte in it — handed out, padding, or still past
|
||||
# the offset — is one allocation to memcheck. It never learns that a
|
||||
# sub-object ended, so it has nothing to complain about and equally
|
||||
# nothing to catch. Recorded as a coverage ceiling in BUILT.md, not as a
|
||||
# nothing to catch. Recorded as a coverage ceiling in docs/BUILT.md, not as a
|
||||
# clean bill of health.
|
||||
#
|
||||
# What *is* caught is definedness rather than addressability, and that
|
||||
|
||||
2
vendor/raylib/bindings
vendored
2
vendor/raylib/bindings
vendored
@ -67,7 +67,7 @@ name IsAudioStreamProcessed audio-stream-processed?
|
||||
|
||||
# Hand-written in raylib.flan, so excluded here: a second declare-c for one C
|
||||
# symbol is refused for the whole program. These three are on a game's
|
||||
# per-frame path (PORTING.md), and a hand-written line is what the signature
|
||||
# per-frame path (docs/PORTING.md), and a hand-written line is what the signature
|
||||
# check has to compare against -- generated output agrees with the header by
|
||||
# construction, so it can only check the hand-written half.
|
||||
exclude DrawTexturePro
|
||||
|
||||
4
vendor/raylib/build-web.sh
vendored
4
vendor/raylib/build-web.sh
vendored
@ -12,7 +12,7 @@
|
||||
#
|
||||
# The output is a plain static archive plus nothing else: no headers are
|
||||
# installed, because the generated FFI shim declares the prototypes it uses
|
||||
# (see BUILT.md, "No raylib headers are needed").
|
||||
# (see docs/BUILT.md, "No raylib headers are needed").
|
||||
#
|
||||
# sh vendor/raylib/build-web.sh
|
||||
#
|
||||
@ -54,7 +54,7 @@ else
|
||||
|
||||
# GRAPHICS_API_OPENGL_ES2 is what WebGL is. PLATFORM_WEB makes rcore.c
|
||||
# include platforms/rcore_web.c, whose WindowShouldClose() is an
|
||||
# emscripten_sleep that returns false — see BUILT.md on why that is the whole
|
||||
# emscripten_sleep that returns false — see docs/BUILT.md on why that is the whole
|
||||
# reason a Flan `until` loop needs no rewriting for the browser.
|
||||
#
|
||||
# rglfw.c is not in the list: the web platform uses emscripten's own GLFW
|
||||
|
||||
2
vendor/raylib/modes.flan
vendored
2
vendor/raylib/modes.flan
vendored
@ -39,7 +39,7 @@
|
||||
;;;;
|
||||
;;;; The obvious fix is `defer`, and it is not available here. `defer` is a
|
||||
;;;; compile-time construct — the forms are copied into the function's exit
|
||||
;;;; paths — so BUILT.md refuses it inside a loop or a branch, and a
|
||||
;;;; paths — so docs/BUILT.md refuses it inside a loop or a branch, and a
|
||||
;;;; begin/end pair lives inside the game loop essentially always. Were it
|
||||
;;;; permitted there it would be wrong in the worse direction: one EndDrawing
|
||||
;;;; at function exit for N BeginDrawings. A macro expanding to a defer would
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user