100 Commits

Author SHA1 Message Date
6e8cc52bc7 Merge branch 'master' into worktree-agent-ad097f67134ba0552 2026-09-25 12:02:48 +07:00
2ff1da88da A slice can be read-only: bytes-view answers a [const u8], and a store through one is refused at compile time 2026-09-25 11:43:41 +07:00
20fa04d9df A program never waits on an editor that stops reading its pushes, and the editor keeps a bounded tail of the program's output 2026-09-25 11:42:54 +07:00
c671f23b2c Merge master 2026-09-25 11:36:46 +07:00
5062052425 Raylib's strings, models, file lists and matrix stack are bound, and a dev build can name what is still loaded at exit 2026-09-25 11:28:02 +07:00
05d86b243f A library resource is known by its identifying field, a Get or a consuming Load is counted as raylib means it, bare pointers are not resources, the report waits for FLAN_DEV_LEAKS, and a release build emits the code it did before the tracker 2026-09-25 11:27:41 +07:00
9e26275f9b A quasiquote nests as SBCL's does, a match takes an enum, and until is a prelude macro 2026-09-25 11:27:14 +07:00
494c57d764 Merge branch 'master' into worktree-agent-ae2997a1e90aa9e02 2026-09-25 11:18:12 +07:00
840b3b215e A Map's churn test fails a table that doubles instead of sweeping, and nothing recalled is cited as read 2026-09-25 11:18:12 +07:00
f4725788c5 The daemon pushes the program's output and the watch table to an editor that asked for them, and every key a Flan buffer's own map binds is that buffer's under Evil 2026-09-25 11:16:49 +07:00
ed8dbbcd62 A handler-bind clause runs with only the handlers outside its own form in force, and a stale call in main's running loop stays listed until the callee changes back or the program re-runs
The first is CLHS 9.1.4.1: a clause that signalled the condition it handles used to re-enter itself until the stack ran out. The stale list now names a lifted clause by the function it is written in, and BUILT.md's cost line is measured against master.
2026-09-25 11:15:52 +07:00
d03b02b347 Merge master 2026-09-25 11:12:30 +07:00
0d771494a6 Merge branch 'master' into worktree-agent-a8f5ad274482eeb9c 2026-09-25 11:07:02 +07:00
8bebff61eb Merge branch 'master' into worktree-agent-a8f88805fa4af49cf 2026-09-25 11:01:36 +07:00
cbfac9f474 A Map is a Swiss table, one control byte a slot with key and value side by side
Removal marks a deleted slot unless no probe can have passed it, and a rebuild at the same capacity sweeps them. The header, the entry points and the iteration contract are unchanged. A Map has no bounds check, so the entry asking to convert one is closed with nothing to convert.
2026-09-25 11:00:22 +07:00
3f30b82bcb A return computes its value before its defers, and a dozen small refusals and constants say what the program meant 2026-09-25 10:54:09 +07:00
0a6ea7c32b A caller excused for going stale takes back the generic copies its failed check cached, and a package's stale callers are named at their own file and line 2026-09-25 10:51:44 +07:00
fe20f68ada The runtime's races are driven by tests, a destroyed arena's record is reused, and a dyn index names its site 2026-09-25 10:51:08 +07:00
08545c757f cond is a special form again because the prelude relies on it, and until is a prelude macro because only programs use it 2026-09-25 10:48:40 +07:00
cc39ff2a01 A destroyed arena's allocator is reused by the next arena-new with its epoch kept, a stale dyn view names its site, and the spec fixes the Vec at five words in every build 2026-09-25 10:48:30 +07:00
a49ed2243a Merge branch 'master' into worktree-agent-a4be641f87966b149 2026-09-25 10:48:05 +07:00
0863b62711 A string returned from C is copied into the context allocator, Model, Mesh and FilePathList cross the raylib boundary, rlgl's matrix stack is a package, and a dev build reports the library resources a program never released 2026-09-25 10:44:49 +07:00
39d35f51db A function whose signature changed installs, and a caller compiled against the old one stops on StaleCall at the call
A dev cell carries its body's signature word beside the body, every call through a cell (and every function value taken from one) compares it with the word the site was compiled for, and the session lists the stale callers by file and line on the reply. Both backends, both installers; release builds have neither the word nor the compare.
2026-09-25 10:40:18 +07:00
7abe760288 cond and until are prelude macros, and a macro module holds only the macros its forms reach 2026-09-25 10:28:32 +07:00
dd160aa31b A return computes its value before it runs its defers 2026-09-25 10:19:09 +07:00
4d10f3c327 An allocator outlives the arena arena-destroy hands back, so a container made from it traps on a read of live memory 2026-09-25 10:18:31 +07:00
e511174d3c No top-level value in the compiler goes unreferenced 2026-09-25 10:16:25 +07:00
3636f31cba A running program's reply never says it has not called (agent/start ...), because the agent's constructor has bound the socket before main 2026-09-25 10:16:25 +07:00
0167fcef43 A quasiquote inside a quasiquote nests, and a macro that writes a macro defines one the program can call 2026-09-25 10:14:41 +07:00
795ba945d9 A dyn value held beside a sibling that can allocate is rooted until it is used, on every backend 2026-09-25 09:32:34 +07:00
0d629d791f The array an index or a slice is taken of is rooted while its index runs, when it is a temporary 2026-09-25 09:30:41 +07:00
11dc230974 A defn- is private to its module, and the module's own macros may call it 2026-09-25 08:57:20 +07:00
793b0eea9e A defn- is private to its module, the package's own macros may write calls to it, and the refusal names the fix 2026-09-25 08:55:37 +07:00
6f0f4957f4 The break buffer, the inspector and the watch table show what a stopped program holds 2026-09-25 08:37:15 +07:00
ff744120fc A release build's watch is only its value, next-error lets go of a stack that has resumed, and the frame a stop is in is always shown 2026-09-25 08:36:53 +07:00
0653f341d0 A dyn value held in an operand list is rooted while a sibling operand may allocate 2026-09-25 08:36:34 +07:00
63f1a1357f Merge branch 'master' into worktree-agent-ad23aafb29b84cc8b 2026-09-25 08:33:28 +07:00
6fd456c0a2 A function declared with defn- is callable only from the files of its own package 2026-09-25 07:42:40 +07:00
88584f76bc The records say what the tree does, and an arena grown in place answers to its budget 2026-09-25 07:39:11 +07:00
85b9f35a66 (watch "name" v) renders any value into the watch table the way print renders it 2026-09-25 07:22:59 +07:00
8b354d4bc8 The notes say what is built: no pool, frames on x86, implicit widening, and the budget in the spec 2026-09-25 07:05:55 +07:00
9194f918fc An aggregate that reads its own destination sees the old value, on x86 too
x86 built a settling right-hand side straight into its destination, so (set p (P {.a (.b p) .b (.a p)})) wrote .a and read it back for .b. assign now also asks whether the value reads storage the destination lies in, and goes through the temporary when it does. The global-initialiser entry was already closed by startup_plan's Set; the unrooted-temporary entry turns out to be a both-backends rooting gap and stays open for a decision.
2026-09-25 07:05:18 +07:00
57fe91f303 Five records become one, and every citation lands somewhere
FIX.org, NEXT.md, DISCUSS.org, docs/DISCUSS.md and the session handoff at the
root are one TODO.org now: 293 entries under seven subsystem headings, each
carrying an org keyword that says where it stands. A DONE entry is a few lines
saying what was decided and what that rules out; the reasoning that would not
compress — the embedding spike and the four reports the hand-written x86
backend was built from — moved into docs/BUILT.md instead, and its entries
point there in one line.

Every entry was checked against the tree before it got a keyword, and the
prose was wrong in both directions. Things the deleted files called open were
built: the first-evaluation stall, main being redefinable, macro parameter
lists, the type-limit constants, the array constructors, the byte fills,
inc/dec, the discard's fontification, the Emacs buffers, rt_die's _exit, the
backtrace surface, and the acceptance failure that could print and still exit
zero. Things they called done were not: the backend reports' no-plan buckets
had gone stale in the other direction, the value-dependent defvar was
superseded rather than built, and macro-expansion source locations are on an
unmerged lane, so that entry is NEXT and names the branch.

Every comment that cited one of the five by name now cites a heading that
exists, in TODO.org or in docs/BUILT.md. The session reports under
docs/handoffs/ keep naming the files they worked on, because rewriting them
would falsify what those sessions did; each carries a note saying where the
content went.
2026-09-21 21:05:48 +07:00
a28aeda633 The thunk memo was keyed on a name that two signatures can share
Review found it, and it is a silent miscompile on both backends rather
than a refusal anywhere.  mangle_ty flattens a whole signature into one
hyphen-joined string, so (CFn [(Ptr i32)] i32) and (CFn [ptr i32] i32) --
the second over a struct someone called ptr -- flatten alike; keyed on
that, the second widening reused the first's thunk at the wrong arity.
The key is the types now, compared with Types.equal, and the symbol is a
counter over the thunks already minted, so nothing is derived from a
spelling.  fn-thunk-share.flan is the pair, and it prints 5 and 17.

And a regression beside it: a generic whose function parameter binds the
type variable.  (defn apply2 [f (Fn [$t] $t) x $t] ...) called as
(apply2 bump 1) compiled before this lane and stopped, because bind_ty
had no arm admitting a CFn argument at an Fn pattern -- and once it had
one, the call still handed one word to an instance declaring two, because
a parameter that still mentions a variable is checked with no expectation
and expect never sees the pair.  Both halves: the arm, and the widening
in generic_call's catch-up pass beside the numeric one.  The same gap hid
new functionality -- a CFn argument at a (CFn [$t] $t) parameter had no
arm either -- and mentions had no CFn case, so bound_exactly answered
wrong for a variable living only inside one.

The corpus missed all of it because the prelude binds $t from an earlier
argument, so the parameter is concrete before bind_ty sees it.

While here: escaping's enumeration is the *clean* set now rather than the
suspect set.  It had a hole where a list like that cannot -- an (at s 0)
over a slice of Fn read as clean while the Vec, struct and pointer
spellings were refused.  Unreachable today, and the header claims the
list is closed.  The same inversion fixes which refusal message an index
read gets.

Three minors: Anull was defined and never constructed; the capture-dyn
message substituted a descriptor into a noun slot; and session.ml
rendered a defn's changed signature as (Fn [...] ...), which is now a
real type and not the same as (CFn [...] ...) -- it writes the parameters
and the return the way a defn writes them.

And one rounding corrected in the docs: handler-bind is not free for a
program that captures nothing.  %handler grew from 24 bytes to 32, every
push writes a null into the new field, every clause gains ptr %env with
an alloca and a store, and flan_signal passes one more argument per
dispatch -- twenty changed x86 lines on loops.flan.  Small, real, and
paid by every conditions program.
2026-09-21 13:41:44 +07:00
4cb53a0a9f What the two function types changed in the documents
The four refusals the function-value lane wrote down are now three and a
half: capture is a feature, the escape is the refusal in its place, and a
struct field or a global of function type is refused for the same zero as
before with a sharper reason behind it.  The handler note is amended
rather than deleted -- its reading was right, and saying so is worth more
than a clean paragraph.

The convention section carries both rulings, the name and every name it
beat, the warning that CFn is not C interop today, the four reasons to
reach for it, what was measured rather than asserted about an ordinary
defn, and the wasm32 finding that killed the first design -- being
exactly typed is checkable by a verifier and the alternative was an
argument from a calling convention.

FIX.org carries the rulings verbatim, what the escaping lane inherits and
changes, the thunk's environment holding a code pointer rather than a GC
object, and two things recorded and not built: a CFn under a future
--no-conditions reaching C's exact convention, and CFn in a struct or a
fixed array, which ZII refuses and capture has nothing to do with.
2026-09-21 13:41:44 +07:00
290d322b44 An assignment is whole or it never happened, on x86 too
Two bugs, both in lib/x86.ml, both reached by signalling a condition from
inside the value being assigned.

The backend has no register wide enough to hold a struct, so it built every
aggregate in its destination, element by element as they were computed. A
transfer out of the middle left the destination in the middle: a global of
four numbers read part old and part new, and so did a local, a struct field,
an array element, a place behind a pointer. A union case was worse than the
rest, since its destination is zeroed before the fields are written.

The second one only looks like the first. An aggregate result is written
through a hidden pointer the caller supplies, and the transfer exit zeroed
that result on its way out, on the correct reasoning that the caller never
reads it. At (set g (f)) the pointer is g, so it zeroed the caller's
variable. It zeroes scalars only now — and with the first half in place
nothing reaches it, so that one is defence in depth and FIX.org says so
rather than claiming a test it does not have.

Assignment builds into a frame temporary and copies, which is the shape
emit.ml always had. The copy is paid where it buys something: [settles] says
whether lowering an expression is bound to reach the end of it, and a
right-hand side that settles is still built in place. That includes a
conversion, except the one direction that is checked — a float narrowed to an
integer — because an array of this language's literals is written
[(u32 1) (u32 2)] and refusing every cast would have taxed the commonest
aggregate there is. A let pays only inside a loop, because a slot reached
once per frame is recorded as bound after the value lands.

half-write.flan is every destination crossed with every right-hand side, run
on both backends, with a scalar row so a regression in one is not read as the
other. dev-halfwrite.flan asks the break loop the half a running program
cannot ask itself: the local, which is invisible to the program and plain to
an editor reading the frame.

The def-edit paragraph in docs/BUILT.md described this as open and is now the
claim it was waiting for. FIX.org records the two lanes the review of this one
turned up and this one does not take: an aggregate built in place can read its
own destination, which is an aliasing question rather than a transfer one; and
the temporary is a frame buffer no root table names, which matters the day a
struct may hold a dyn field.
2026-09-21 12:37:08 +07:00
54d12811f8 Merge branch 'worktree-agent-afd7ad8d87f71fa16' into dev-loop
# Conflicts:
#	FIX.org
2026-09-21 12:19:15 +07:00
ef2ac41172 Merge branch 'worktree-agent-a1b7f33fe11893c24' into dev-loop
# Conflicts:
#	FIX.org
2026-09-21 12:12:36 +07:00
ae46c5a878 Three sentences that were true only while the state lagged
Follow-ups to the re-run flip, and all of one kind: each said something
correct about a committed re-run that still read as parked, and the flip made
it false.

flan_merged_park's note that the program stays PROGRAM_PARKED across a poll is
now true of every round but the one it leaves on — which is the round that
drains the ring with a run already taken, and the round the fix's own argument
leans on as the long one.

Program.rerun's refusal told the reader to close a window or let the program
finish. With a thunk stopped in the break loop a re-run is accepted, the state
goes running, and a second is refused by that sentence — advice about a game
loop for somebody whose thread is in a break. The C cannot see a break; this
end can, so Dev.rerun passes what it already asked the agent and the refusal
says resume or abort instead.

The five-second timeout in eval_expr and run_render_thunk reached its
break-loop sentence through the Parked arm, so a program stopped between runs
was asked whether it calls (agent/poll). Both gain the arm that names the
break. run_render_thunk's two sentences become a function first: the new one
asks the agent, and that was the body of a five-millisecond tick.

docs/BUILT.md gains the case an editor author meets — :parked nil :stopped t
with no frames running — and FIX.org records that test_dev.ml:763's refusal
assertion was racy before this and is narrowed by it.
2026-09-21 12:11:23 +07:00
13145cc52a Merge branch 'worktree-agent-aba36ddb6a383320f' into dev-loop
# Conflicts:
#	FIX.org
2026-09-21 12:10:23 +07:00