A recur from an arm of a match over an enum is in tail position, and a session's blind spot for macros an expansion defines is recorded

This commit is contained in:
Joseph Ferano 2026-09-25 10:39:42 +07:00
parent e9e151e1a4
commit 60ef7b2e06
4 changed files with 19 additions and 5 deletions

View File

@ -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 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". 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 ** DONE until and cond are prelude macros; when and dotimes stay special forms
CLOSED: [2026-09-25] CLOSED: [2026-09-25]
=cond= builds its chain through the prelude function =cond-chain= and refuses =cond= builds its chain through the prelude function =cond-chain= and refuses

View File

@ -601,10 +601,10 @@ let loaded_for (forms : Form.t list) : loaded option =
List.filter (fun (n, _) -> Hashtbl.mem taken n) mine List.filter (fun (n, _) -> Hashtbl.mem taken n) mine
in in
let all = prelude @ List.map fst 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 (* A file that calls no macro costs one scan and no module. Few do, since
macro pays nothing: a file that calls none costs one scan and no cond is a macro; for a file with no macros of its own the module is the
compiler. Without it every build in the suite would link a macro module prelude's alone, one cached .so shared by every such file, so the usual
for the prelude's macros and pay a clang driver to answer nothing. *) cost is a stat and a dlopen. *)
if all = [] || not (List.exists (names_macro all) forms) then None if all = [] || not (List.exists (names_macro all) forms) then None
else begin else begin
let extra = rounds ~prelude mine in let extra = rounds ~prelude mine in

View File

@ -25,7 +25,15 @@
(calls) (calls)
:west) :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 (defn main [] i32
(print (steps-to-west :north)) (println "")
(println (name :north)) (println (name :north))
(println (name (turn :north))) (println (name (turn :north)))
(println (name (turn (turn :north)))) (println (name (turn (turn :north))))

View File

@ -423,7 +423,7 @@ let () =
(* match over an enum lowers to a chain of [=] over one temporary, so (* match over an enum lowers to a chain of [=] over one temporary, so
"(called) " printed once is the scrutinee evaluated once. *) "(called) " printed once is the scrutinee evaluated once. *)
let match_enum_out = 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 in
outputs "match over an enum" "programs/match-enum.flan" match_enum_out; outputs "match over an enum" "programs/match-enum.flan" match_enum_out;
outputs ~opt:"-O0" "match over an enum, -O0" "programs/match-enum.flan" outputs ~opt:"-O0" "match over an enum, -O0" "programs/match-enum.flan"