TODO.org records what this lane settled and the one decision it left

This commit is contained in:
Joseph Ferano 2026-09-25 07:17:07 +07:00
parent f142cfaadf
commit 7b843f6753

View File

@ -73,11 +73,13 @@ is wrong where it was written rather than aborting the compile with no location.
The prelude's own =unless= has not been converted and still answers a bare
undefined name.
** TODO gensym's counter restarts in a second module
The counter lives in the loaded module and a module is dlopened once per compiler
process, so it is process-wide in practice — but the rounds already build more
than one module for a program whose macros call macros. Seed it from the module's
index.
** DONE gensym's counter restarts in a second module
CLOSED: [2026-09-25]
The counter is C data in the runtime (=flan_gensym_n=), and =lib/macro.ml= writes
the compiler's own count into the module before every macro call and reads it
back after. It counts across every module a compiler process loads — each round,
the program's module, and every expansion in a session. Rules out a counter per
module, seeded or not.
** TODO A quasiquote inside a quasiquote is refused
Nothing counts nesting levels — not the reader, deliberately, and not the
@ -141,11 +143,14 @@ as words the reader will not read back. =(/ 1.0 0.0)= is the only route to an
infinity, and the constant folder is integers only, so it cannot be a =defconst=.
Closing it needs a reader literal or a float-capable folding pass.
** TODO A u64 constant above 2^63 cannot be written in decimal
The reader reads a decimal integer literal as a signed 64-bit number; hex is read
as a bit pattern and works. The same limit has a second face: a cast's argument is
checked against the default type, so =(u64 2935910691)= is refused for not fitting
in an =i32=.
** DONE A u64 constant above 2^63 cannot be written in decimal
CLOSED: [2026-09-25]
A decimal between 2^63 and 2^64 is read as its bit pattern, as hex is. A cast's
integer literal that does not fit =i32= is checked at the cast's type; one that
fits keeps the =i32= default, so =(u32 -1)= still means what it did. It inherits
hex's hole: a decimal above 2^63 at =i64= is accepted as a negative, and a refusal
at a narrower type prints the negative number. Rules out a separate unsigned
literal in =Form=, whose layout the prelude's =Form= mirrors.
** DONE {.row .col} binds same-named locals
CLOSED: [2026-09-20]
@ -790,14 +795,11 @@ program that does not type-check. Moot for anything that compiles; only the
daemon's half-typed recompiles could feel it. A cheaper retry was tried and
shelved because it changes which literal gets the nicer message.
** TODO and's last operand gets a misdirected caret
=(println (and true true (vec-new i32)))= puts the caret on the second =true=. The
last operand of an =and= is the then arm and the then arm is typed first, so the
mismatch is blamed on the else arm, which carries the previous operand's location.
The fix is preferring the arm that is not a compiler temp when deciding whom to
blame. Three others were considered and rejected: relabelling the else arm reads
backwards, a bool sentinel reverts the =or= fix, and inverting the condition costs
a =not= per operand.
** DONE and's last operand gets a misdirected caret
CLOSED: [2026-09-25]
Already fixed by 3672da2, which blames the arm that is not a compiler temp; the
caret is on the last operand and =test/test_flan.ml= asserts its column. Rules
out relabelling the else arm, a bool sentinel, and inverting the condition.
** TODO Signature pairing's cold-rebuild edge
Whether a parameter vector reads as one annotated parameter or two dyn ones
@ -841,14 +843,20 @@ Iteration is built; the remaining refusal is generics. A =defn= has to name its
types and =(defn map-keys [m (Map K V)] (Vec K))= has no =K=. The loop is three
lines at the call site, where =K= is known.
** TODO (vec-new [u8]) is refused
The element type must be a bare symbol naming a type, so a =(Vec [u8])= can only
be made where the context names it. The fix is letting it take a type expression —
the same parser that already reads =[u8]= in a parameter list.
** DONE (vec-new [u8]) is refused
CLOSED: [2026-09-25]
The type positions of =vec-new= and =map-new= take a type expression: brackets, or
a parenthesised =Ptr=, =Option=, =Vec=, =Map=, =Fn= or =CFn=. Parse reads it with
=texpr= into =Ast.TypeArg=; a bare name is still left for the checker to tell a
type from an allocator. Rules out a type expression anywhere else in expression
position.
** TODO An array literal cannot say it is [f32]
A float literal defaults to =f64=, an array literal has no context, and a =let=
has no annotation. Same shape as =(vec-new [u8])= and probably the same fix.
Not the same fix: a bracket literal has no argument to put a type in. Decision:
how a literal names its element type — a spelling of its own, or a =let=
annotation.
** TODO A let binding takes no type annotation
Everything under the surface is there — the binding carries a type slot and the
@ -1808,12 +1816,12 @@ instrumented copy; the equivalent here is a dev-build-only instrumented
redefinition, which the cell indirection already makes deliverable. Open:
whether stepping suspends the frame loop, and what it does to a game's clock.
** TODO A NaN cast says "does not fit", which reads as too big
=runtime/flan_rt.c:1008= covers every out-of-range float with one sentence, so
=(i32 nan)= reports the =i32= bounds as if the value had overshot them. NaN and
the infinities convert to no integer at all and want saying so by name. Found
by filling a struct holding an =f32= with =(filled 0xFF)=, where every bit set
is NaN.
** DONE A NaN cast says "does not fit", which reads as too big
CLOSED: [2026-09-25]
Two more =ArithError= codes: 5 for a cast of NaN and 6 for a cast of an infinity,
each with its own sentence. Both backends choose the code on the cold path, so the
guard is still two compares. =lhs= and =rhs= still carry the range. Rules out
carrying the float value in the condition.
** TODO The break buffer prints fields, not the sentence the runtime wrote
=ArithError — op 4, lhs -2147483648, rhs 2147483647= where the runtime's own
@ -1837,12 +1845,12 @@ It prints the source line and carets for the stop (=flan-cnr.el:197=) and lists
frames, but no key opens the file at that line. Wants RET-on-a-frame, or =M-.=,
and =next-error= over the frame list.
** TODO loop's bindings should be sequential, like let's
=check_loop= (=lib/check.ml:4741=) checks every initialiser before binding any,
so =(loop [curr-r r next-r (inc curr-r)] ...)= cannot see =curr-r= and the
refusal reads as an unknown name. Every binding form is sequential — there is
no =let*= here and there is not going to be one. Check the other binding forms
for the same gap while fixing it.
** DONE loop's bindings should be sequential, like let's
CLOSED: [2026-09-25]
=check_loop= binds each name before checking the next initialiser; =recur= still
rebinds all at once. No other form had the gap: =let= was already sequential,
=dotimes= binds one name, and =fn=, =defn=, =match= and the handler and restart
clauses bind parameters with no initialisers.
** TODO C-c C-c reports one error, not every error in the form
Whole-file paths use =Check.program_all= and report every bad declaration. The