From 7b843f675331327be2e208016b7908db9e586126 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 25 Sep 2026 07:17:07 +0700 Subject: [PATCH] TODO.org records what this lane settled and the one decision it left --- TODO.org | 76 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/TODO.org b/TODO.org index fcf9257b..097ba9b5 100644 --- a/TODO.org +++ b/TODO.org @@ -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