The mechanical half, ahead of the parser change that needs it. tools/unit-return.py
fills the empty slot with () and rewrites Unit as () wherever a type is spelled --
(Fn [i32] Unit), (Map i32 Unit), a return type written out.
Deciding whether a defn already had a return type is the whole difficulty, and
the script does it the way parse.ml did: is_type_form is transcribed rather than
improved, because being identical to the parser it replaces is what makes the
sweep meaning-preserving. It is re-runnable, so the lanes that branched before
this can have the same pass at merge:
python3 tools/unit-return.py .
python3 tools/unit-return.py --in-strings test/test_flan.ml test/test_acceptance.ml \
test/test_session.ml emacs/test-flan-dev.el emacs/test-flan-mode.el
python3 tools/unit-return.py --raw-ml lib/prelude.ml
python3 tools/unit-return.py --in-html web/index.html
-v logs every defn it saw and what it decided, which is how a sweep of 440 sites
gets reviewed at all. Embedded modes pool a file's type declarations across all
its fragments, because a snippet split across concatenation -- decls ^ "(defn f
[s [u8]] Cursor ...)" -- cannot see the names the other half declared; pooled
names count only in bare-symbol position, for the same reason the prelude's do.
A fragment that cuts off mid-form is skipped rather than guessed at. Five sites
in test_flan.ml still needed a hand, and they are in this commit.
Two things ride along because the sweep needs them: parse.ml reads a lone () as
the return type of a function with no body, which was not a shape the old
optional slot could produce; and the map refusals name () rather than Unit, since
that is now the spelling a caller wrote.
73 lines
2.5 KiB
Plaintext
73 lines
2.5 KiB
Plaintext
;;;; defer in a let whose extent is the function body.
|
|
;;;;
|
|
;;;; A let is not a frame here: its bindings are function slots like any other,
|
|
;;;; and nothing is released at scope exit (spec-memory.md, "When storage is
|
|
;;;; released"). So a let at the top level of a function body has exactly the
|
|
;;;; function's extent, a defer written in it always registers, and it is as
|
|
;;;; safe as one written at the top level. A let nested inside such a let has
|
|
;;;; the same extent and the same permission.
|
|
;;;;
|
|
;;;; The numbers differ per failure, so a wrong answer names its own cause.
|
|
|
|
(defvar order i64)
|
|
|
|
(defn note [n i32] () (set order (+ (* order 10) (i64 n))))
|
|
|
|
;;; One defer in a let, with the acquisition it is paired with above it. This
|
|
;;; is the shape the relaxation exists for: acquire, defer the release beside
|
|
;;; it, then use it.
|
|
(defn one [] i64
|
|
(let [a 1]
|
|
(defer (note a))
|
|
(note 9))
|
|
order)
|
|
|
|
;;; Two resources in one let. This is the case a flag granted once per block
|
|
;;; instead of once per form gets wrong: the first defer registers and the
|
|
;;; second is refused.
|
|
(defn two [] ()
|
|
(let [a 1 b 2]
|
|
(defer (note a))
|
|
(defer (note b))
|
|
(note 9)))
|
|
|
|
;;; A nested let still has the function's extent, so a defer in it registers
|
|
;;; too — and it registers *later* than the outer one, so it runs first.
|
|
(defn nested [] ()
|
|
(let [a 1]
|
|
(defer (note a))
|
|
(let [b 2]
|
|
(defer (note b))
|
|
(note 9))))
|
|
|
|
;;; Registration order is one order across the boundary: a defer at the top
|
|
;;; level and a defer inside a let interleave by where they are written, not by
|
|
;;; which construct they are in. Written 1, 2, 3; run 3, 2, 1.
|
|
(defn mixed [] ()
|
|
(defer (note 1))
|
|
(let [x 2]
|
|
(defer (note x))
|
|
(defer (note 3))
|
|
(note 9)))
|
|
|
|
;;; An early return runs them too, and innermost-first, exactly as it does for
|
|
;;; a defer at the top level. The let's binding is still live when the defer
|
|
;;; reads it, because the slot is the function's.
|
|
(defn early [] i64
|
|
(let [a 1]
|
|
(defer (note a))
|
|
(let [b 2]
|
|
(defer (note b))
|
|
(return 7)))
|
|
0)
|
|
|
|
(defn main [] i32
|
|
(set order 0) (print (one)) (println "") ; 9 — read before the defer runs
|
|
(set order 0) (one) (print order) (println "") ; 91
|
|
(set order 0) (two) (print order) (println "") ; 921 — reverse of writing
|
|
(set order 0) (nested)(print order) (println "") ; 921
|
|
(set order 0) (mixed) (print order) (println "") ; 9321
|
|
(set order 0) (print (early)) (println "") ; 7
|
|
(print order) (println "") ; 21
|
|
0)
|