The refused shape is defmacro at C-x C-e, and the union's boundary is defn's
The head dispatch already names it; test_repl pins it beside the defvar case, and the editor-typed defmacro moved below the describe assertion that says an evaluation changes nothing -- that one does, on purpose. BUILT.md gains the boundary of the shape: the set only grows, a deleted defmacro still expands, and that is exactly what a deleted defn already does.
This commit is contained in:
parent
b9f5b5c44c
commit
9fc04193d9
14
BUILT.md
14
BUILT.md
@ -5453,6 +5453,13 @@ since.** Applied to macros, that is both halves of the fix and neither is option
|
||||
this path — it parses to a `Defn` and installs a body like any other. The only thing missing was the session
|
||||
remembering that the name is a macro.
|
||||
|
||||
**The boundary of that shape, written down rather than discovered:** the set only ever grows. Delete
|
||||
`(defmacro tenfold ...)` from the buffer, reload, and `(tenfold 7)` still expands, because a union never removes.
|
||||
That is not a leak to fix later — it is exactly what `defn` does. `Session.eval` keeps `kept @ added`, so a
|
||||
function the file no longer declares is still in the session and still callable, and the running process still has
|
||||
its body loaded. A session is what the program was built from plus every change *accepted*, and a deletion is not
|
||||
a change anything sent. The one operation that forgets is restarting the session, which is what it is for.
|
||||
|
||||
**The session does not re-read the file, and that was the live alternative.** It has `~origin`, the buffer's own
|
||||
path, and re-reading would pick up macros the session has never been told about. It would also read whatever is
|
||||
*saved*, while the buffer on screen is whatever is *typed* — so an expansion would silently use a body the reader
|
||||
@ -5486,6 +5493,13 @@ The names stay unqualified. A buffer writes its own macro's bare name, so that i
|
||||
answer to; a file inside a package the program also imports ends up holding both, the bare one from here and
|
||||
`alias/name` from `Load`, which is what the two call sites each need.
|
||||
|
||||
**One shape stays refused, and its refusal was already right.** `(defmacro ...)` at `C-x C-e` is a declaration,
|
||||
and `Parse.expr`'s head dispatch already names `defmacro` first among the heads it refuses — "defmacro is a
|
||||
top-level declaration, not an expression". It matters more now than when it was written: before this, nobody would
|
||||
type a `defmacro` at `C-x C-e`, because the session forgot it either way. Now that one typed at the editor means
|
||||
something, reaching for `C-x C-e` on it is a reflex, and what comes back says what it is rather than complaining
|
||||
about an unknown function. `test_repl` pins it beside the `defvar` case.
|
||||
|
||||
Both editor paths work and they are different wraps — `Parse.expr`'s for `C-x C-e` and `Parse.decl`'s for
|
||||
`C-c C-c`. `test/test_session.ml` covers both, plus a `defmacro` the file never had followed by a call to it,
|
||||
plus editing that macro and calling it again; the assertions are on the IR and not on the absence of an
|
||||
|
||||
@ -168,19 +168,6 @@ let () =
|
||||
same read that built the program. Over a real socket, because that is
|
||||
the path a person is on; test_session has the in-process halves. *)
|
||||
value "the file's own macro" "(tenfold 7)" "70";
|
||||
(* A macro the file never had, typed at the editor and then called. This
|
||||
is the shape the fix chose: a [defmacro] evaluated into a session
|
||||
*joins* it, exactly as a [defn] does, and the next evaluation can call
|
||||
it. Two round trips, because that is the whole of the claim. *)
|
||||
(let r =
|
||||
request c
|
||||
(Printf.sprintf "(:op \"eval\" :code %s :file \"/tmp/buf.flan\")"
|
||||
(quote "(defmacro thrice [args] `(* ~(at args 0) 3))"))
|
||||
in
|
||||
if status r <> "ok" then
|
||||
fail "evaluating a defmacro over the socket: %s"
|
||||
(Option.value ~default:(status r) (field r "message")));
|
||||
value "a macro defined at the editor" "(thrice 14)" "42";
|
||||
|
||||
(* The one that proves it ran inside the process: the program increments
|
||||
[ticks] every frame, so two evaluations of it must disagree. A copy
|
||||
@ -215,12 +202,36 @@ let () =
|
||||
refuses "a declaration inside an expression" "(do 1 (defn f [] i32 1))"
|
||||
"top-level declaration";
|
||||
refuses "an unknown name" "no-such-name" "unknown name";
|
||||
(* [defmacro] is in that same head list, and it is the shape that stays
|
||||
refused now that a [defmacro] typed at the editor means something: a
|
||||
person will reach for C-x C-e on one by reflex, and what they get is
|
||||
the sentence naming it a declaration rather than an arity complaint
|
||||
about an unknown function. C-c C-c is where a declaration goes, which
|
||||
is the case below. *)
|
||||
refuses "a defmacro at C-x C-e" "(defmacro m [args] args)"
|
||||
"top-level declaration";
|
||||
|
||||
(* And the session is untouched by all of it: an evaluation is not a
|
||||
declaration, so nothing named eval/N accumulates in the program. *)
|
||||
let r = request c "(:op \"describe\")" in
|
||||
if status r <> "ok" then fail "describe after evaluating: %s" (status r);
|
||||
|
||||
(* Below the line above deliberately, because this one *does* change the
|
||||
session: a macro the file never had, typed at the editor and then
|
||||
called. It is the shape the fix chose — a [defmacro] evaluated into a
|
||||
session joins it, exactly as a [defn] does, and the next evaluation
|
||||
can call it. Two round trips, because that is the whole of the
|
||||
claim. *)
|
||||
(let r =
|
||||
request c
|
||||
(Printf.sprintf "(:op \"eval\" :code %s :file \"/tmp/buf.flan\")"
|
||||
(quote "(defmacro thrice [args] `(* ~(at args 0) 3))"))
|
||||
in
|
||||
if status r <> "ok" then
|
||||
fail "evaluating a defmacro over the socket: %s"
|
||||
(Option.value ~default:(status r) (field r "message")));
|
||||
value "a macro defined at the editor" "(thrice 14)" "42";
|
||||
|
||||
ignore (request c "(:op \"close\")");
|
||||
Unix.close c
|
||||
end;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user