Flan's tagged sum has been spelled defunion since it landed, which was accurate right up until the language wanted C's untagged union as well. Both cannot be called the same thing, and the tagged one is the one with an alternative name that says what it is: a case, its fields, and a tag that steers which case is live is a data type, not a union. So the form is defdata everywhere -- the parser, the AST, the checker, both backends, the prelude's Form, the editor's font-locking and imenu, the docs and every .flan file in the tree. The internal vocabulary moves with it: Tast.union is Tast.data, uname is dname, the tables the checker and the emitter keep are datas. Leaving them would have inverted the words permanently, with surface defunion meaning one thing and env.unions meaning the other, which is exactly the kind of drift the comments in those files exist to prevent. What did not move is case, variant and vfields: a tagged sum still has cases, and it still has one live at a time. defunion is not kept as an alias. An alias would compile the day the untagged form lands and mean the opposite of what it used to -- the same silent misparse that made defn's return type mandatory, and worse, because the reader would have no reason to look. The old spelling is a named refusal instead, parse/defunion-renamed, which says what it is now called and that the name is reserved for something else. It fires on the head alone, so (defunion U [A B]) -- which would otherwise have parsed cleanly as one field A of type B -- is refused with the rest.
54 lines
1.8 KiB
Plaintext
54 lines
1.8 KiB
Plaintext
;;;; A stopped stack whose OUTER frame holds a local the evaluator cannot see.
|
|
;;;;
|
|
;;;; dev-locals.flan is about what one frame holds; this one is about which
|
|
;;;; frame the answer came from. The whole of the bug the `inspect' verb
|
|
;;;; exists for is that an expression is evaluated where the evaluator stands,
|
|
;;;; so a local's *name* reaches the right storage only when the frame is the
|
|
;;;; innermost one. `mark' below is a global AND a local of the outer frame,
|
|
;;;; holding different things of different types: evaluating the name answers
|
|
;;;; the global, and rooting at the frame and slot answers the frame.
|
|
;;;;
|
|
;;;; The other locals are the shapes a path step has to walk and that an
|
|
;;;; expression cannot reach at all: an option's payload, which has no
|
|
;;;; accessor form in the language, and a union case's field, whose offset
|
|
;;;; depends on which case the value is in.
|
|
(import agent "vendor:agent")
|
|
|
|
(defstruct Point [x f32 y f32])
|
|
(defstruct Boom [why i32])
|
|
|
|
(defdata Shape
|
|
[Empty
|
|
(Dot [x f64 y f64])
|
|
(Rect [w i32 h i32])])
|
|
|
|
;; The discriminator. `outer' binds a local of this name to something else, so
|
|
;; every claim about which frame answered is visible in the value itself.
|
|
(defvar mark i64)
|
|
|
|
;; The innermost frame, and it is deliberately dull: it holds nothing worth
|
|
;; inspecting, so that the frame worth inspecting is not the one an expression
|
|
;; would have found by luck.
|
|
(defn deeper [] i64
|
|
(restart-case
|
|
(do (error (Boom {.why 7})) 1)
|
|
(carry-on [] 5)))
|
|
|
|
(defn outer [] i64
|
|
(let [mark (Point {.x 1.5 .y 2.5})
|
|
xs [10 20 30]
|
|
box (Some (Point {.x 4.5 .y 5.5}))
|
|
s (Shape.Rect {.w 3 .h 6})]
|
|
(deeper)))
|
|
|
|
(defvar ticks i64)
|
|
|
|
(defn main [] i32
|
|
(set mark 99)
|
|
(agent/start "/tmp/flan-dev-inspect-fallback.sock")
|
|
(print (outer)) (println "")
|
|
(dotimes [i 4000]
|
|
(agent/wait 5)
|
|
(set ticks (+ ticks 1)))
|
|
0)
|