diff --git a/TODO.org b/TODO.org index 24edcfed..28d1e7ab 100644 --- a/TODO.org +++ b/TODO.org @@ -90,6 +90,12 @@ expansion that defines a macro re-runs the expander. =~~@x= is refused. There is no =,',x=, since =quote= takes a symbol, and a macro defined by an expansion is not exported from a package. docs/BUILT.md, "Quasiquote runs before the walk". +** TODO A session does not remember a macro that an expansion defined +=Session.own_macros= reads =defmacro= heads off the forms as sent, so after +=(defsquare sq)= is evaluated the session's next form cannot call =sq=. A build +sees the whole file and can. The fix is collecting from the expanded forms, +which =Load.program= does not hand back. + ** DONE until and cond are prelude macros; when and dotimes stay special forms CLOSED: [2026-09-25] =cond= builds its chain through the prelude function =cond-chain= and refuses diff --git a/lib/macro.ml b/lib/macro.ml index ef998045..3a850ece 100644 --- a/lib/macro.ml +++ b/lib/macro.ml @@ -601,10 +601,10 @@ let loaded_for (forms : Form.t list) : loaded option = List.filter (fun (n, _) -> Hashtbl.mem taken n) mine in let all = prelude @ List.map fst mine in - (* The common case by a wide margin, and the reason a build that uses no - macro pays nothing: a file that calls none costs one scan and no - compiler. Without it every build in the suite would link a macro module - for the prelude's macros and pay a clang driver to answer nothing. *) + (* A file that calls no macro costs one scan and no module. Few do, since + cond is a macro; for a file with no macros of its own the module is the + prelude's alone, one cached .so shared by every such file, so the usual + cost is a stat and a dlopen. *) if all = [] || not (List.exists (names_macro all) forms) then None else begin let extra = rounds ~prelude mine in diff --git a/test/programs/match-enum.flan b/test/programs/match-enum.flan index 01123a06..a7ba148a 100644 --- a/test/programs/match-enum.flan +++ b/test/programs/match-enum.flan @@ -25,7 +25,15 @@ (calls) :west) +;; recur from inside an arm: the arm is the loop's tail. +(defn steps-to-west [from Dir] i32 + (loop [d from n 0] + (match d + :west n + _ (recur (turn d) (+ n 1))))) + (defn main [] i32 + (print (steps-to-west :north)) (println "") (println (name :north)) (println (name (turn :north))) (println (name (turn (turn :north)))) diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index 66c2d732..3e773f43 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -423,7 +423,7 @@ let () = (* match over an enum lowers to a chain of [=] over one temporary, so "(called) " printed once is the scrutinee evaluated once. *) let match_enum_out = - "north\nsideways\nsouth\n(called) sideways\nback to north\n" + "3\nnorth\nsideways\nsouth\n(called) sideways\nback to north\n" in outputs "match over an enum" "programs/match-enum.flan" match_enum_out; outputs ~opt:"-O0" "match over an enum, -O0" "programs/match-enum.flan"