diff --git a/NEXT.md b/NEXT.md
index e151fd9..cef7c9a 100644
--- a/NEXT.md
+++ b/NEXT.md
@@ -1904,7 +1904,7 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them.
What is left for the macro lane, and it is one thing: **`load.ml:312` refuses an imported union outright**, so a
union is file-local. That is not a blocker for `Form` — the prelude is parsed and prepended into the same flat
- namespace before `collect` runs, so a `defunion Form` in `prelude.ml` is an ordinary same-file declaration and
+ namespace before `collect` runs, so a `defdata Form` in `prelude.ml` is an ordinary same-file declaration and
needs no import and no `load.ml` change. Verified by declaring one there and matching it from a program.
**Macros landed on top of this** and needed no `load.ml` change for `Form`, exactly as this said. See
diff --git a/bin/main.ml b/bin/main.ml
index 13ee066..8945a6e 100644
--- a/bin/main.ml
+++ b/bin/main.ml
@@ -39,7 +39,7 @@ let summarise (d : Flan.Ast.decl) =
| Import (a, p) -> Printf.sprintf "import %s %S" a p
| Defalias (n, _) -> Printf.sprintf "defalias %s" n
| Defstruct (n, fs) -> Printf.sprintf "defstruct %s (%d fields)" n (List.length fs)
- | Defunion (n, vs) -> Printf.sprintf "defunion %s (%d cases)" n (List.length vs)
+ | Defdata (n, vs) -> Printf.sprintf "defdata %s (%d cases)" n (List.length vs)
| Defvar (n, _, _) -> Printf.sprintf "defvar %s" n
| Defconst (n, _, _) -> Printf.sprintf "defconst %s" n
| Declare (fn, csym) ->
diff --git a/docs/BUILT.md b/docs/BUILT.md
index 70f538a..3217e32 100644
--- a/docs/BUILT.md
+++ b/docs/BUILT.md
@@ -724,7 +724,7 @@ reason, and a `defmacro` typed at the REPL becomes an ordinary `Ast.Defn` that n
it was rather than half-fixed here, and pinned in `test_session` so that changing it is a decision.
**An expression that expands to a declaration is refused, by name.** `defn`, `defvar`, `defconst`, `defstruct`,
-`defunion`, `defenum`, `defalias`, `defmacro` and `import` are heads `Parse.expr` now rejects — the arm that used to
+`defdata`, `defenum`, `defalias`, `defmacro` and `import` are heads `Parse.expr` now rejects — the arm that used to
say it for `defmacro` alone, generalised. It sits in the head dispatch and not in a walk over what the expander
answered, so it catches a declaration nested anywhere in the expansion for free, catches one **typed** by hand with the
same sentence instead of "unknown name defvar", and cannot drift out of sync with `decl`'s list the way a second copy
@@ -2686,7 +2686,7 @@ are involved, and none are needed" — and monomorphisation is what would buy it
## Unions, and the tag they carry
-`defunion` parsed and its shape was checked long before this; naming the type (`check.ml:312`) and constructing a
+`defdata` parsed and its shape was checked long before this; naming the type (`check.ml:312`) and constructing a
value (`:1075`) were both refused as milestone 6. They are not any more.
### It is closer than the milestone number suggested, and the reason is `Option`
@@ -2739,7 +2739,7 @@ the payload at offset 8, and 40/8 for a struct holding one.
### The surface
```clojure
-(defunion Shape
+(defdata Shape
[Empty
(Dot [x f64 y f64])
(Rect [w i32 h i32])
@@ -2801,13 +2801,13 @@ is a layout change, the same way reordering a struct's fields is.
A union that contains itself by value has no finite size, and `payload_lay`
would recurse forever laying one out rather than failing. It does not get the
-chance: `check_finite` already walked a union's cases, so `(defunion T [Leaf
+chance: `check_finite` already walked a union's cases, so `(defdata T [Leaf
(Node [l T r T])])` is refused with *"T contains itself by value, so it has no
size — go through (Ptr T)"*, and so is a pair of unions that contain each
other. Through a pointer it works, and that is the shape a `Form` has:
```clojure
-(defunion Tree [Leaf (Node [l (Ptr Tree) n i32])])
+(defdata Tree [Leaf (Node [l (Ptr Tree) n i32])])
(defn depth [t (Ptr Tree)] i32
(match (deref t)
@@ -2842,7 +2842,7 @@ enum arm already had, and for the same reason: the name is erased before any bac
An **imported** union is still refused by name at `load.ml:312`, so a union is file-local. That is not a blocker for
the macro expander: the prelude is parsed and prepended into the same flat namespace before `collect` runs, so a
-`defunion Form` in `prelude.ml` is an ordinary same-file declaration needing no import — verified by declaring one
+`defdata Form` in `prelude.ml` is an ordinary same-file declaration needing no import — verified by declaring one
there and matching it from a program. `dev.ml`'s inspector still says "union values are milestone 6" for a stopped
frame's locals, and `shim.ml`'s "a Flan union has no C layout" is now inaccurate as prose though the refusal it guards
is still right: a union has a C layout and still may not cross to C by value, because the shim flattens aggregates.
@@ -3022,7 +3022,7 @@ qualified under the alias the package was imported as. See "A package may declar
### `Form`, and the three numbers
-A macro's parameter and its result are `Form`, so `Form` has to exist on the Flan side: a `defunion` in `prelude.ml`
+A macro's parameter and its result are `Form`, so `Form` has to exist on the Flan side: a `defdata` in `prelude.ml`
mirroring `lib/form.ml`. It mirrors `Form.value` and **not** `Form.t` — there is no `loc` field, deliberately. A macro
cannot invent a source location, so the unmarshaller stamps the **call site's** `Loc.t` onto every node of what a
macro returns. That is the structural answer to "keep the source location of the call site attached to what a macro
@@ -3746,7 +3746,7 @@ the thing it calls. `Prelude.bootstrap` is the hook, a ref rather than a paramet
`Check.program` and it cannot be told.
Only `defn`s are dropped: the functions that survive still mention the prelude's types, and a reduced prelude missing
-them would not check. A `defstruct`, `defunion`, `defalias`, `defenum` or `defvar` therefore stays whatever it names.
+them would not check. A `defstruct`, `defdata`, `defalias`, `defenum` or `defvar` therefore stays whatever it names.
There used to be a sharper reason — `Parse.prelude_types` memoised the prelude's type names for the parser's
return-type guess, and it can be forced for the first time inside a bootstrap build, so a reduced set cached there
would have been wrong for every compile afterwards. That set is gone with the guess; see *The return type is the slot*
diff --git a/emacs/flan-inspect.el b/emacs/flan-inspect.el
index 10a4416..c77d2e1 100644
--- a/emacs/flan-inspect.el
+++ b/emacs/flan-inspect.el
@@ -62,7 +62,7 @@
;; frame it means;
;;
;; the slot root names one frame and one slot, so it is exact, and it
-;; reaches an option's payload and a union case's fields, which have offsets
+;; reaches an option's payload and a data type case's fields, which have offsets
;; but no accessor. It needs a stopped program, it is refused if the
;; frame's body was redefined since it was entered — the same slot
;; fingerprint the listing is refused by — and it cannot root at an
@@ -275,7 +275,7 @@ whatever the value came from."
;; is walking a type, so a field is its name and an element is its number.
;; Two cases need more than the name.
;;
-;; A union's payload sits at an offset that depends on which case the value
+;; A data type's payload sits at an offset that depends on which case the value
;; is in, and only the renderer knows which case it currently is — it wrote
;; `(Union.case {.f …})'. So the type travels with the step and the wire
;; spelling is `Union.case.f'. Guessing the case from a field name two
@@ -686,14 +686,14 @@ root, which is what makes a mixed stack unconstructible."
(unless step (user-error "flan: nothing to inspect on this line"))
(let ((why (flan-inspect-refusal node flan-inspect--root)))
(when why (user-error "flan: %s" why)))
- ;; A union case's field, under an expression root. This is a refusal of
+ ;; A data type case's field, under an expression root. This is a refusal of
;; the *parent* and not of the value at point, which is why it is here and
;; not in `flan-inspect-refusal\=': a struct field that happens to hold a
- ;; union is reached by an ordinary accessor and must stay enterable; it is
- ;; a field *of the union itself* that has no accessor. `(match ...)\=' is
- ;; how a union is opened in the language, and it binds names rather than
+ ;; data type is reached by an ordinary accessor and must stay enterable;
+ ;; it is a field *of the data type itself* that has no accessor. `(match ...)\=' is
+ ;; how a data type is opened in the language, and it binds names rather than
;; producing a value to send, so there is nothing to build here. The
- ;; renderer wrote the head as `Union.case\=', which is the one type spelling
+ ;; renderer wrote the head as `Type.case\=', which is the one type spelling
;; with a dot in it — a package qualifies with a slash.
(let ((ty (plist-get flan-inspect--node :type)))
(when (and (not (eq (car-safe flan-inspect--root) :slot))
@@ -701,14 +701,14 @@ root, which is what makes a mixed stack unconstructible."
(string-match-p "\\." ty))
(user-error
"flan: %s"
- (concat "a union case's field: it is reached by (match ...) in the "
+ (concat "a data type case's field: it is reached by (match ...) in the "
"language, not by an accessor, so there is no expression to "
"send. `i' on a local in the break buffer roots at the frame's "
"slot instead, and that root steps into it by offset"))))
;; The line carries the step that names the field; what the wire needs
;; beyond the name is the type it is a field *of*, and that is this
- ;; buffer's own node — the parent of the one at point. A union's payload
- ;; sits at an offset that depends on the case, so `Union.case\=' has to
+ ;; buffer's own node — the parent of the one at point. A data type's payload
+ ;; sits at an offset that depends on the case, so `Type.case\=' has to
;; travel with the name. An option's payload has no name at all and is
;; the symbol `some\='.
(let* ((step (if (eq (plist-get flan-inspect--node :kind) 'option)
diff --git a/emacs/flan-mode.el b/emacs/flan-mode.el
index 17a2bb4..deb09dd 100644
--- a/emacs/flan-mode.el
+++ b/emacs/flan-mode.el
@@ -89,7 +89,7 @@
:prefix "flan-")
(defconst flan--definers
- '("defn" "defvar" "defconst" "defstruct" "defunion" "defenum" "defalias"
+ '("defn" "defvar" "defconst" "defstruct" "defdata" "defenum" "defalias"
"declare" "import" "package")
"Forms that introduce a top-level name.")
@@ -132,7 +132,7 @@ below — and not again here.")
;; that ignored the column would offer one.
(defvar flan-imenu-generic-expression
`(("Functions" ,(concat "^(defn\\s-+" flan--name-re) 1)
- ("Types" ,(concat "^(def\\(?:struct\\|union\\|enum\\|alias\\)\\s-+"
+ ("Types" ,(concat "^(def\\(?:struct\\|data\\|enum\\|alias\\)\\s-+"
flan--name-re)
1)
("Variables" ,(concat "^(def\\(?:var\\|const\\)\\s-+" flan--name-re) 1)
@@ -434,7 +434,7 @@ decision to `calculate-lisp-indent'."
(flan--count-indent method indent-point last-sexp head-column))
((eq method :defn) (+ lisp-body-indent head-column))
;; No spec. Anything else spelled `def…' is a definition and indents
- ;; like one, which covers `defstruct', `defunion', `defenum', `defvar',
+ ;; like one, which covers `defstruct', `defdata', `defenum', `defvar',
;; `defconst' and `defalias' without naming them.
((and name (string-match-p "\\`def" name))
(+ lisp-body-indent head-column))
diff --git a/emacs/test-flan-cider.el b/emacs/test-flan-cider.el
index 767ef19..f9e912a 100644
--- a/emacs/test-flan-cider.el
+++ b/emacs/test-flan-cider.el
@@ -562,10 +562,10 @@ unwind would send the next one to a daemon that is not there."
(test-flan--check "and the trail says so"
(string-match-p "\\`o \\[frame 0\\]\\.some\n" (buffer-string)))))
-;; A union case's field. The payload sits at an offset that depends on which
+;; A data type case's field. The payload sits at an offset that depends on which
;; case the value is in, and only the renderer knows which it currently holds
;; — it wrote the head `Shape.circle'. So the case travels with the name.
-(test-flan--check "a union field carries its case on the wire"
+(test-flan--check "a data type field carries its case on the wire"
(equal (flan-inspect-wire-step '(:field "r" "Shape.circle"))
"Shape.circle.r"))
(test-flan--check "a struct field does not"
@@ -576,15 +576,15 @@ unwind would send the next one to a daemon that is not there."
;; And the other half of that pair: under an *expression* root there is no
;; accessor to send, so RET refuses there rather than sending `(.at s)' for
-;; the checker to reject. A union's fields are reached by `(match ...)' in
+;; the checker to reject. A data type's fields are reached by `(match ...)' in
;; the language, which binds names rather than producing a value. It is a
;; refusal of the parent, not of the value at point — a struct field that
-;; merely *holds* a union is an ordinary accessor and stays enterable.
+;; merely *holds* a data type is an ordinary accessor and stays enterable.
(let ((buf (test-flan--inspect "s" "(Shape.circle {.at (V {.x 1 .y 2})})")))
(with-current-buffer buf
(goto-char (point-min))
(flan-inspect-next)
- (test-flan--check "an expression root refuses a union case's field"
+ (test-flan--check "an expression root refuses a data type case's field"
(string-match-p
"reached by (match"
(or (test-flan--caught #'flan-inspect-into) "")))))
@@ -600,7 +600,7 @@ unwind would send the next one to a daemon that is not there."
(flan-inspect--show '(:expr "c") nil)
(with-current-buffer " *test-inspect*"
(goto-char (point-min))
- (flan-inspect-next) (flan-inspect-next) ; .s, which holds the union
+ (flan-inspect-next) (flan-inspect-next) ; .s, which holds the data type
(test-flan--check "but a struct field that merely holds one is enterable"
(progn (flan-inspect-into) (equal (car asked) "(.s c)")))))))
@@ -615,7 +615,7 @@ unwind would send the next one to a daemon that is not there."
(goto-char (point-min))
(flan-inspect-next)
(flan-inspect-into)
- (test-flan--check "RET into a union field names the case it is in"
+ (test-flan--check "RET into a data type field names the case it is in"
(equal (plist-get (car test-flan--asked) :path)
'("Shape.circle.at")))))
diff --git a/lib/ast.ml b/lib/ast.ml
index 2edcf93..75473d1 100644
--- a/lib/ast.ml
+++ b/lib/ast.ml
@@ -152,7 +152,7 @@ and decl_kind =
| Import of string * string (* alias, path *)
| Defalias of string * texpr
| Defstruct of string * field list
- | Defunion of string * variant list
+ | Defdata of string * variant list
| Defn of fn
(* No body, so no [defn]: a foreign function, and the string is the C symbol
it is actually called by (plan.org, Types — [declare] is kept only where
@@ -182,7 +182,7 @@ and init = Zeroed | Uninit | Init of expr
drift apart. *)
let declared_name (d : decl) =
match d.d with
- | Defenum (n, _) | Defalias (n, _) | Defstruct (n, _) | Defunion (n, _)
+ | Defenum (n, _) | Defalias (n, _) | Defstruct (n, _) | Defdata (n, _)
| Defvar (n, _, _) | Defconst (n, _, _) -> Some n
| Declare (fn, _) | DeclareC (fn, _) | Defn fn -> Some fn.name
| Package _ | Import _ -> None
diff --git a/lib/check.ml b/lib/check.ml
index 9253654..2df96bb 100644
--- a/lib/check.ml
+++ b/lib/check.ml
@@ -14,7 +14,7 @@
The rule from the two misparse bugs applies here too: *anything not yet
implemented is rejected by name*, never approximated. Milestone 2 is
calc-me.flan and nothing more (plan.org, Build sequence), so [Vec], [Map],
- [Result]/[try], user unions, closures, [dotimes], [defer], generics and
+ [Result]/[try], user data types, closures, [dotimes], [defer], generics and
cross-package imports are all errors with a message that says which
milestone they belong to. *)
@@ -43,17 +43,17 @@ type binding = {
type env = {
structs : (string, Tast.structure) Hashtbl.t;
- unions : (string, Tast.union) Hashtbl.t;
- (* Every union case, twice over: once under its full spelling ["U.C"], which
+ datas : (string, Tast.data) Hashtbl.t;
+ (* Every data type case, twice over: once under its full spelling ["U.C"], which
is how a value of it is written, and once under the bare ["C"], which is
how a [match] arm names it and how a mistake spells a constructor. The
full spelling is a key rather than something split out of a dotted name at
- the use site, because a union's own name can contain a slash (an imported
+ the use site, because a data type's own name can contain a slash (an imported
[rl/U]) and may one day contain a dot; string surgery would own an edge
this does not have to.
The bare entry is deliberately last-writer-wins and is *only* used to say
- "C is a case of U, write (U.C ...)". Two unions may share a case name —
+ "C is a case of U, write (U.C ...)". Two data types may share a case name —
construction is qualified and a pattern resolves against the scrutinee, so
both are unambiguous — and refusing that would be a restriction with no
mechanism behind it. *)
@@ -124,7 +124,7 @@ type env = {
let new_env () = {
structs = Hashtbl.create 16;
- unions = Hashtbl.create 16;
+ datas = Hashtbl.create 16;
cases = Hashtbl.create 32;
aliases = Hashtbl.create 16;
consts = Hashtbl.create 16;
@@ -161,7 +161,7 @@ let declared_note env name =
match Hashtbl.find_opt env.structs name with
| Some s -> List.map (fun (f : Tast.field) -> f.Tast.fname) s.Tast.fields
| None ->
- (match Hashtbl.find_opt env.unions name with
+ (match Hashtbl.find_opt env.datas name with
| Some u -> List.map (fun (c : Tast.variant) -> c.Tast.vname) u.Tast.cases
| None -> [])
in
@@ -282,7 +282,7 @@ type ctx = {
[dead] is the slots whose value has been moved out, with where it went, so
that a second use names the first rather than reporting a type error about
nothing. It is flow-sensitive at an [if]: the two arms are checked from
- the same starting set and the *union* survives the join, so moving in one
+ the same starting set and the *data type* survives the join, so moving in one
arm only is still a move afterwards — and moving in both arms, which is
legal, is not two errors.
@@ -538,7 +538,7 @@ let map_type ?(preds = []) loc (k : Types.t) (v : Types.t) =
elements, a [defvar] with no initialiser are all all-bytes-zero — and a
zeroed function value is a null pointer with a signature on it, which is the
one kind of zero that cannot be used for anything. Every other type's zero
- is a value: 0, false, an empty slice, [None], a union's first case. So these
+ is a value: 0, false, an empty slice, [None], a data type's first case. So these
are refused where they are written rather than left to crash at the call.
A parameter, a return type, a [let] binding and an [(Option (Fn ...))] are
@@ -658,7 +658,7 @@ and near_miss env n =
Types.primitive_names
@ Hashtbl.fold (fun k _ acc -> k :: acc) env.aliases []
@ Hashtbl.fold (fun k _ acc -> k :: acc) env.structs []
- @ Hashtbl.fold (fun k _ acc -> k :: acc) env.unions []
+ @ Hashtbl.fold (fun k _ acc -> k :: acc) env.datas []
@ Hashtbl.fold (fun k _ acc -> k :: acc) env.enums []
in
List.find_opt (fun c -> c <> n && one_edit n c) candidates
@@ -711,12 +711,12 @@ and resolve_name env ~seen loc n =
fail loc "the type alias %s is defined in terms of itself" n
else resolve env ~seen:(n :: seen) (Hashtbl.find env.aliases n)
| _ when Hashtbl.mem env.structs n -> Types.Named n
- (* A union is [Named] exactly as a struct is: one case in [Types.t]
+ (* A data type is [Named] exactly as a struct is: one case in [Types.t]
covers both, and which table the name is in is what tells them apart.
- Keeping them one case is what lets a union be a field, a parameter, a
+ Keeping them one case is what lets a data type be a field, a parameter, a
return type and a slot without a single one of those paths learning
- that unions exist. *)
- | _ when Hashtbl.mem env.unions n -> Types.Named n
+ that data types exist. *)
+ | _ when Hashtbl.mem env.datas n -> Types.Named n
| _ when Hashtbl.mem env.enums n -> Types.Enum n
(* A typo in a primitive is lowercase too, and the type-variable rule
below would otherwise report [f65] as unimplemented generics and send
@@ -1250,16 +1250,16 @@ let rec key_pair env loc (k : Types.t) : Tast.fnref * Tast.fnref =
| t when bytewise_key t ->
Tast.Rtfn "flan_hash_flat", Tast.Rtfn "flan_eq_flat"
| Types.Named n when Hashtbl.mem env.structs n -> struct_key_pair env loc n
- (* A union key would have to hash the tag and then only the bytes the case in
- hand actually uses — the rest of the payload is indeterminate, exactly as
- a struct's padding is, so hashing the blob would make two equal values
+ (* A data type key would have to hash the tag and then only the bytes the
+ case in hand actually uses — the rest of the payload is indeterminate,
+ exactly as a struct's padding is, so hashing the blob would make two equal values
hash differently. That is a per-case walk driven by a switch, which is a
different shape from the field list [struct_key_pair] emits and which
nothing has yet wanted. Refused by name rather than written untested. *)
- | Types.Named n when Hashtbl.mem env.unions n ->
+ | Types.Named n when Hashtbl.mem env.datas n ->
fail loc
- "%s is a union, and a union is not a map key: the payload past the case \
- in hand is indeterminate, so hashing the bytes would make two equal \
+ "%s is a data type, and a data type is not a map key: the payload past \
+ the case in hand is indeterminate, so hashing the bytes would make two equal \
values hash differently. Hashing one needs a per-case walk, which is \
not written — key on the tag, or on a struct holding what you meant" n
| Types.Array (_, e) ->
@@ -1768,19 +1768,19 @@ and var ctx loc ~want name =
same reason: there is nothing to put in the braces. A case that does
have fields is refused here rather than silently zeroed, because ZII
on a constructor would quietly produce a value nobody wrote. *)
- | Some (uname, c) when String.contains name '.' ->
+ | Some (dname, c) when String.contains name '.' ->
if c.Tast.vfields <> [] then
fail loc
"%s has fields, so it needs them — write (%s {.%s ...})"
name name
(List.hd c.Tast.vfields).Tast.fname;
expect loc ~want
- (mk loc (Types.Named uname)
- (Tast.MakeCase (uname, c.Tast.vname, [])))
- | Some (uname, c) ->
+ (mk loc (Types.Named dname)
+ (Tast.MakeCase (dname, c.Tast.vname, [])))
+ | Some (dname, c) ->
fail loc
- "%s is a case of the union %s, and a union value names both — \
- write %s.%s" name uname uname c.Tast.vname
+ "%s is a case of the data type %s, and a data type value names both — \
+ write %s.%s" name dname dname c.Tast.vname
| None ->
(* A bare function name *is* the function. This is a Lisp-1 — one
top-level namespace, enforced, so a defn and a defvar cannot share
@@ -2483,7 +2483,7 @@ and check_if ctx ?(tail = false) ?want loc c t e =
let t = branch ctx (fun () -> in_tail (fun () -> check ctx t)) in
expect loc ~want (mk loc Types.Unit (Tast.If (c, t, unit_at loc)))
| Some e ->
- (* Both arms start from the same dead set and the union survives: moving in
+ (* Both arms start from the same dead set and the data type survives: moving in
one arm only still kills the binding afterwards, and moving in both —
which is legal and common — is not reported twice. A flat set would have
refused [(if c (free v) (free v))] and allowed the use after a one-armed
@@ -2514,31 +2514,31 @@ and check_if ctx ?(tail = false) ?want loc c t e =
mk loc ty (Tast.If (c, t, e))
(* A record-shaped literal: one form for both, because [(Name {.f v})] is the
- same syntax whether [Name] is a struct or a union case, and the two differ
+ same syntax whether [Name] is a struct or a data type case, and the two differ
only in what is built at the end. Deciding here rather than in the parser is
what lets the decision be made against the tables, exactly. *)
and check_struct ctx ~want loc name kvs =
match Hashtbl.find_opt ctx.env.structs name with
| None ->
(match Hashtbl.find_opt ctx.env.cases name with
- (* The full spelling [U.C], which is how a union value is written. Checked
+ (* The full spelling [U.C], which is how a data type value is written. Checked
before the diagnostics below, since the bare-name entry in the same
table is only ever a hint. *)
- | Some (uname, c) when String.contains name '.' ->
- check_case ctx ~want loc uname c kvs
+ | Some (dname, c) when String.contains name '.' ->
+ check_case ctx ~want loc dname c kvs
(* A bare case name. This is the bug NEXT.md listed under "Bugs found and
- not yet fixed": [(A {.x 1})] on a case of a union reported "unknown
+ not yet fixed": [(A {.x 1})] on a case of a data type reported "unknown
struct A", because nothing in [env] could tell a case name from a
misspelling. It can now, so it says what was meant. *)
- | Some (uname, c) ->
+ | Some (dname, c) ->
fail loc
- "%s is a case of the union %s, not a struct — a union value names \
+ "%s is a case of the data type %s, not a struct — a data type value names \
both, as (%s.%s {.field value ...})"
- name uname uname c.Tast.vname
+ name dname dname c.Tast.vname
| None ->
- if Hashtbl.mem ctx.env.unions name then
+ if Hashtbl.mem ctx.env.datas name then
fail loc
- "%s is a union, and a union value names the case as well as the \
+ "%s is a data type, and a data type value names the case as well as the \
type — write (%s.%s {.field value ...}) for one of %s"
name name (first_case_name ctx.env name) (case_list ctx.env name)
else
@@ -2573,25 +2573,25 @@ and check_struct ctx ~want loc name kvs =
in
expect loc ~want (mk loc (Types.Named name) (Tast.Make (name, fields)))
-(* The cases of a union, as written, for a message that has to name them. *)
-and case_list env uname =
- match Hashtbl.find_opt env.unions uname with
+(* The cases of a data type, as written, for a message that has to name them. *)
+and case_list env dname =
+ match Hashtbl.find_opt env.datas dname with
| None -> "its cases"
| Some u ->
String.concat ", "
- (List.map (fun (c : Tast.variant) -> uname ^ "." ^ c.Tast.vname)
+ (List.map (fun (c : Tast.variant) -> dname ^ "." ^ c.Tast.vname)
u.Tast.cases)
-and first_case_name env uname =
- match Hashtbl.find_opt env.unions uname with
+and first_case_name env dname =
+ match Hashtbl.find_opt env.datas dname with
| Some { Tast.cases = c :: _; _ } -> c.Tast.vname
| _ -> "Case"
(* [(U.C {.f v ...})]. The fields are checked and filled in exactly as a
struct's are — same ZII, same duplicate and unknown-field refusals — and the
only difference is the node at the end and the type it carries. *)
-and check_case ctx ~want loc uname (c : Tast.variant) kvs =
- let full = uname ^ "." ^ c.Tast.vname in
+and check_case ctx ~want loc dname (c : Tast.variant) kvs =
+ let full = dname ^ "." ^ c.Tast.vname in
let seen = Hashtbl.create 8 in
List.iter
(fun (k, (v : Ast.expr)) ->
@@ -2603,7 +2603,7 @@ and check_case ctx ~want loc uname (c : Tast.variant) kvs =
| None -> ());
if Tast.vfield_index c k = None then
Loc.failk "check/unknown-field" v.Ast.loc
- ~notes:(declared_note ctx.env uname)
+ ~notes:(declared_note ctx.env dname)
"%s has no field %s" full k;
Hashtbl.add seen k v)
kvs;
@@ -2616,7 +2616,7 @@ and check_case ctx ~want loc uname (c : Tast.variant) kvs =
c.Tast.vfields
in
expect loc ~want
- (mk loc (Types.Named uname) (Tast.MakeCase (uname, c.Tast.vname, fields)))
+ (mk loc (Types.Named dname) (Tast.MakeCase (dname, c.Tast.vname, fields)))
and check_arr ctx ~want loc items =
let elem_want =
@@ -2650,17 +2650,17 @@ and check_arr ctx ~want loc items =
and check_match ctx ?(tail = false) ?want loc scrutinee arms =
let s = check ctx scrutinee in
- (* What the arms are alternatives over. An [Option] is a two-case union
+ (* What the arms are alternatives over. An [Option] is a two-case data type
wearing a special coat, so the two shapes below are the same shape: a set
of case names, an arity and a payload type per case, and a tag. Keeping
- them apart here rather than desugaring [Option] into a declared union is
- deliberate — [Option] is generic and no declared union is, so the coat is
+ them apart here rather than desugaring [Option] into a declared data type is
+ deliberate — [Option] is generic and no declared data type is, so the coat is
the part that cannot yet be taken off. *)
let subject =
match s.Tast.ty with
| Types.Option t -> `Option t
- | Types.Named n when Hashtbl.mem ctx.env.unions n ->
- `Union (Hashtbl.find ctx.env.unions n)
+ | Types.Named n when Hashtbl.mem ctx.env.datas n ->
+ `Data (Hashtbl.find ctx.env.datas n)
(* An enum is the one scrutinee that is not a milestone away: it is an i32
at run time and its members are all known, so the arms would be a chain
of [=] with an exhaustiveness check over [env.enums] — a desugaring, not
@@ -2674,7 +2674,7 @@ and check_match ctx ?(tail = false) ?want loc scrutinee arms =
of (= k :member), but a keyword has no case in the pattern type yet. \
Use cond" n
| other ->
- fail loc "match works on an Option or a union, not on %s"
+ fail loc "match works on an Option or a data type, not on %s"
(Types.to_string other)
in
(* Which case each arm names, and the type of each name it binds. This is the
@@ -2691,13 +2691,14 @@ and check_match ctx ?(tail = false) ?want loc scrutinee arms =
| `Option _, Ast.Pctor (c, _) ->
fail a.Ast.aloc
"%s is not a case of Option — the cases are Some and None" c
- | `Union u, Ast.Pctor (c, names) ->
+ | `Data u, Ast.Pctor (c, names) ->
(* A pattern names the case bare: the scrutinee's type already says which
- union, so [(Node l r)] is unambiguous even where two unions share the
- case name. The qualified spelling is accepted too, since that is how
+ data type, so [(Node l r)] is unambiguous even where two data types
+ share the case name. The qualified spelling is accepted too, since
+ that is how
the value was written and writing it again should not be an error. *)
let bare =
- let full = u.Tast.uname ^ "." in
+ let full = u.Tast.dname ^ "." in
let n = String.length full in
if String.length c > n && String.sub c 0 n = full then
String.sub c n (String.length c - n)
@@ -2706,7 +2707,7 @@ and check_match ctx ?(tail = false) ?want loc scrutinee arms =
(match Tast.case_index u bare with
| None ->
fail a.Ast.aloc "%s is not a case of %s — the cases are %s" c
- u.Tast.uname
+ u.Tast.dname
(String.concat ", "
(List.map (fun (v : Tast.variant) -> v.Tast.vname) u.Tast.cases))
| Some (_, v) ->
@@ -2718,7 +2719,7 @@ and check_match ctx ?(tail = false) ?want loc scrutinee arms =
fail a.Ast.aloc
"%s.%s has %d field%s, and this pattern binds %d — a case pattern \
binds every field, in declaration order (%s)"
- u.Tast.uname bare (List.length v.Tast.vfields)
+ u.Tast.dname bare (List.length v.Tast.vfields)
(if List.length v.Tast.vfields = 1 then "" else "s")
(List.length names)
(String.concat " "
@@ -2732,7 +2733,7 @@ and check_match ctx ?(tail = false) ?want loc scrutinee arms =
let seen = Hashtbl.create 8 in
let saw_wild = ref false in
(* The same rule as [if], and for the same reason: the arms are alternatives,
- so each is checked from the state before the match and the union of what
+ so each is checked from the state before the match and the data type of what
they moved survives the join. Checked in sequence against one mutating set
they would report the second arm's (free v) as a use after the first arm's
move, which is a legal program refused. *)
@@ -2772,25 +2773,25 @@ and check_match ctx ?(tail = false) ?want loc scrutinee arms =
ctx.dead <- !joined;
(* Exhaustiveness is refused, not defaulted. A match that silently fell
through would have to produce a value of the match's type out of nothing,
- and there is no such value for most types; and the case a union grows
+ and there is no such value for most types; and the case a data type grows
tomorrow is exactly the one a reader wants to be told about today. A [_]
arm is the way to say "the rest", written where it can be seen. *)
let missing =
match subject with
| `Option _ -> List.filter (fun c -> not (Hashtbl.mem seen c)) [ "Some"; "None" ]
- | `Union u ->
+ | `Data u ->
List.filter_map
(fun (c : Tast.variant) ->
if Hashtbl.mem seen c.Tast.vname then None
- else Some (u.Tast.uname ^ "." ^ c.Tast.vname))
+ else Some (u.Tast.dname ^ "." ^ c.Tast.vname))
u.Tast.cases
in
if not !saw_wild && missing <> [] then
- (* The union's declaration, because that is where the case list this match
+ (* The data type's declaration, because that is where the case list this match
failed to cover actually lives, and because adding a case there is what
makes a match non-exhaustive in the first place. *)
Loc.failk "check/non-exhaustive-match" loc
- ~notes:(match subject with `Union u -> declared_note ctx.env u.Tast.uname
+ ~notes:(match subject with `Data u -> declared_note ctx.env u.Tast.dname
| _ -> [])
"this match is not exhaustive — %s %s no arm. Add %s, or a _ arm for \
the rest"
@@ -2810,15 +2811,15 @@ and struct_target ctx (target : Ast.expr) : Tast.expr * string =
| Types.Named n when Hashtbl.mem ctx.env.structs n -> t, n
| Types.Ptr (Types.Named n) when Hashtbl.mem ctx.env.structs n ->
mk t.Tast.loc (Types.Named n) (Tast.Deref t), n
- (* A union's fields belong to one case, and which case it is holding is only
- known after the tag has been read. [.field] would have to be a read that
- might be reading something else, so it is not one: [match] is how a union
- is opened, and it binds the fields it has proved are there. *)
+ (* A data type's fields belong to one case, and which case it is holding is
+ only known after the tag has been read. [.field] would have to be a read
+ that might be reading something else, so it is not one: [match] is how a
+ data type is opened, and it binds the fields it has proved are there. *)
| (Types.Named n | Types.Ptr (Types.Named n))
- when Hashtbl.mem ctx.env.unions n ->
+ when Hashtbl.mem ctx.env.datas n ->
fail target.Ast.loc
- "%s is a union, and a union's fields belong to a case — which one it is \
- holding is what the tag says, so they are reached by (match ...), \
+ "%s is a data type, and a data type's fields belong to a case — which \
+ one it is holding is what the tag says, so they are reached by (match ...), \
whose arms bind the fields of the case they matched"
n
| other ->
@@ -3174,9 +3175,9 @@ and file_guard ctx loc ~path_slot ~op mk_steps =
[ mk loc Types.Unit (Tast.While (notok (), [ body ], [])) ]))
(* Is this bare symbol the name of a type? Every table [resolve_name] will look
- in, and the union table is one of them: a union is [Named] exactly as a
+ in, and the data type table is one of them: a data type is [Named] exactly as a
struct is, so (vec-new Form) is as ordinary as (vec-new Cell). It was left
- out when unions landed, which made the prelude's own (vec-new Form) fail
+ out when data types landed, which made the prelude's own (vec-new Form) fail
with "nothing here says what (vec-new) is a Vec of" — a message about a
missing annotation for a program that had written one. One list, read by
both callers, so the next kind of type added cannot be added to one of
@@ -3191,7 +3192,7 @@ and type_named ctx n =
|| List.mem_assoc n ctx.env.subst
|| List.mem n Types.primitive_names
|| Hashtbl.mem ctx.env.structs n
- || Hashtbl.mem ctx.env.unions n
+ || Hashtbl.mem ctx.env.datas n
|| Hashtbl.mem ctx.env.enums n
|| Hashtbl.mem ctx.env.aliases n
@@ -4758,7 +4759,7 @@ and named_call ctx ~want loc name args =
let rc =
{ Render.structs =
Hashtbl.fold (fun _ v acc -> v :: acc) ctx.env.structs [];
- unions = Hashtbl.fold (fun _ v acc -> v :: acc) ctx.env.unions [];
+ datas = Hashtbl.fold (fun _ v acc -> v :: acc) ctx.env.datas [];
enums = Hashtbl.fold (fun k v acc -> (k, v) :: acc) ctx.env.enums [];
emit = emitter;
(* [println] never follows a pointer, and the allocation registry does
@@ -4900,18 +4901,18 @@ and named_call ctx ~want loc name args =
let args = map2_lr (fun p a -> check ctx ~want:p a) params args in
expect loc ~want (mk loc ret (Tast.Call (name, args)))
| None ->
- if Hashtbl.mem ctx.env.unions name then
+ if Hashtbl.mem ctx.env.datas name then
fail loc
- "%s is a union type — a union value names the case too, as (%s.%s {.field value ...})"
+ "%s is a data type — a data type value names the case too, as (%s.%s {.field value ...})"
name name (first_case_name ctx.env name)
else if Hashtbl.mem ctx.env.cases name then
(* [(U.C)] and [(C)]: a case written as a call. Both are how someone
reaches for a constructor, and neither is one. *)
- let uname, c = Hashtbl.find ctx.env.cases name in
+ let dname, c = Hashtbl.find ctx.env.cases name in
fail loc
- "%s is a case of the union %s — write (%s.%s {.field value ...}), \
+ "%s is a case of the data type %s — write (%s.%s {.field value ...}), \
or %s.%s on its own when it has no fields"
- name uname uname c.Tast.vname uname c.Tast.vname
+ name dname dname c.Tast.vname dname c.Tast.vname
else if Hashtbl.mem ctx.env.structs name then
fail loc
"%s is a type — a struct value is written (%s {.field value ...})"
@@ -5161,7 +5162,7 @@ let rec const_int env (e : Ast.expr) : int64 option =
let collect env (decls : Ast.decl list) =
(* One pass over every declaration kind before any of the others, because
- the tables below are per-kind — structs, unions, aliases, enums, functions
+ the tables below are per-kind — structs, data types, aliases, enums, functions
and globals each have their own — and a collision between two of them
would otherwise be found by LLVM, as [redefinition of function
'@flan.item'], or not at all. A [defn item] and a [defvar item] are two
@@ -5190,9 +5191,9 @@ let collect env (decls : Ast.decl list) =
| Ast.Defstruct (n, _) ->
Hashtbl.replace env.locs n d.Ast.dloc;
Hashtbl.replace env.structs n { Tast.sname = n; fields = [] }
- | Ast.Defunion (n, _) ->
+ | Ast.Defdata (n, _) ->
Hashtbl.replace env.locs n d.Ast.dloc;
- Hashtbl.replace env.unions n { Tast.uname = n; cases = [] }
+ Hashtbl.replace env.datas n { Tast.dname = n; cases = [] }
| Ast.Defalias (n, t) -> Hashtbl.replace env.aliases n t
| _ -> ())
decls;
@@ -5310,15 +5311,15 @@ let collect env (decls : Ast.decl list) =
(Types.to_string f.Tast.fty))
fields;
Hashtbl.replace env.structs n { Tast.sname = n; fields }
- | Ast.Defunion (n, vs) ->
- (* A union with no cases has no value, so nothing could ever be given
+ | Ast.Defdata (n, vs) ->
+ (* A data type with no cases has no value, so nothing could ever be given
one, and a parameter of that type would be a function nothing can
call. It parses; it is refused here rather than surviving to a
layout with a tag and no case for the tag to name. *)
if vs = [] then
fail loc
- "%s declares no cases, so no value of it can exist — a union is \
- (defunion %s [(Case [field Type ...]) ...])" n n;
+ "%s declares no cases, so no value of it can exist — a data type is \
+ (defdata %s [(Case [field Type ...]) ...])" n n;
let cnames = List.map (fun (v : Ast.variant) -> v.Ast.vname) vs in
if List.length (List.sort_uniq compare cnames) <> List.length cnames
then fail loc "%s declares the same case twice" n;
@@ -5334,18 +5335,19 @@ let collect env (decls : Ast.decl list) =
n v.Ast.vname;
let vfields = List.map field v.Ast.vfields in
(* The same refusal a struct field gets, for the same reason
- and in the same words: a union case's fields are a struct,
- the union copies bytewise on assignment, and recursive
+ and in the same words: a data type case's fields are a struct,
+ the data type copies bytewise on assignment, and recursive
teardown arrives with [drop]. Refusing it here rather than
at a use keeps the two declarations honest with each other
- — a union that could hold a Vec where a struct could not
+ — a data type that could hold a Vec where a struct could not
would be a hole in the same rule. *)
List.iter
(fun (f : Tast.field) ->
if Types.is_move_only f.Tast.fty then
fail v.Ast.vloc
"%s.%s's field %s is %s, which is move-only, and a \
- union case that owns one makes the union move-only \
+ data type case that owns one makes the data type \
+ move-only \
too — transitively, with recursive teardown. That \
rule arrives with drop (step 5 in NEXT.md); until \
then hold the %s in a local and pass it"
@@ -5355,7 +5357,7 @@ let collect env (decls : Ast.decl list) =
{ Tast.vname = v.Ast.vname; vfields })
vs
in
- Hashtbl.replace env.unions n { Tast.uname = n; cases };
+ Hashtbl.replace env.datas n { Tast.dname = n; cases };
List.iter
(fun (c : Tast.variant) ->
Hashtbl.replace env.cases (n ^ "." ^ c.Tast.vname) (n, c);
@@ -5451,7 +5453,7 @@ let check_finite env =
match Hashtbl.find_opt env.structs name with
| Some s -> List.iter (fun (f : Tast.field) -> ty seen f.Tast.fty) s.Tast.fields
| None ->
- match Hashtbl.find_opt env.unions name with
+ match Hashtbl.find_opt env.datas name with
| None -> ()
| Some u ->
List.iter
@@ -5464,7 +5466,7 @@ let check_finite env =
| _ -> ()
in
Hashtbl.iter (fun n _ -> walk [] n) env.structs;
- Hashtbl.iter (fun n _ -> walk [] n) env.unions
+ Hashtbl.iter (fun n _ -> walk [] n) env.datas
(* ── Declarations: pass 2, check bodies ────────────────────────────── *)
@@ -5661,7 +5663,7 @@ let check_global env (d : Ast.decl) : Tast.global option =
| Ast.Zeroed -> { Tast.e = Tast.Zero ty; ty; loc = d.Ast.dloc }
| Ast.Uninit ->
(* Everywhere else [uninit] is an opt-out from ZII and the bytes are
- whatever they were: a garbage f64 is a garbage number. A union is
+ whatever they were: a garbage f64 is a garbage number. A data type is
the one type where that is qualitatively worse — the tag steers
control flow, a tag no case names falls past every comparison in a
[match], and the block after them is [unreachable], which LLVM is
@@ -5669,13 +5671,13 @@ let check_global env (d : Ast.decl) : Tast.global option =
becomes "the optimiser may do anything" is refused by name, and the
zeroed form, which is the first declared case, is named beside it. *)
(match ty with
- | Types.Named un when Hashtbl.mem env.unions un ->
+ | Types.Named un when Hashtbl.mem env.datas un ->
fail d.Ast.dloc
- "%s is a union, and uninit on one is refused: its tag steers \
+ "%s is a data type, and uninit on one is refused: its tag steers \
every match, and a tag no case names has no arm to reach. Drop \
the uninit — a zeroed %s is %s, which is a real case"
(Types.to_string ty) un
- (match Hashtbl.find_opt env.unions un with
+ (match Hashtbl.find_opt env.datas un with
| Some { Tast.cases = c :: _; _ } -> un ^ "." ^ c.Tast.vname
| _ -> "its first case")
| _ -> ());
@@ -5815,7 +5817,7 @@ let build_program ~keep_going (decls : Ast.decl list) : Tast.program * env =
|> List.sort (fun (a : Tast.extern) b -> String.compare a.Tast.esym b.Tast.esym)
in
({ Tast.structs = values (fun (s : Tast.structure) -> s.Tast.sname) env.structs;
- unions = values (fun (u : Tast.union) -> u.Tast.uname) env.unions;
+ datas = values (fun (u : Tast.data) -> u.Tast.dname) env.datas;
globals; externs; fns; cshim },
env)
diff --git a/lib/dev.ml b/lib/dev.ml
index 69139c4..a0f4d25 100644
--- a/lib/dev.ml
+++ b/lib/dev.ml
@@ -735,26 +735,27 @@ let layout t ~ty =
| None ->
(* Two types the checker knows and this op cannot describe. An enum's
members are erased to i32 before [Tast.program] exists, which is the
- same fact that makes a defenum unreloadable; a union is declared and
+ same fact that makes a defenum unreloadable; a data type is declared and
has no values yet. Either way, saying which kind it is beats "no such
type" for a name that plainly exists. *)
if Hashtbl.mem t.session.Session.env.Check.enums ty then
error (ty ^ " is an enum, not a struct; its members are erased to i32")
else if
- List.exists (fun (u : Tast.union) -> String.equal u.Tast.uname ty)
- t.session.Session.program.Tast.unions
+ List.exists (fun (u : Tast.data) -> String.equal u.Tast.dname ty)
+ t.session.Session.program.Tast.datas
then
- (* Unions have landed, so "milestone 6" was stale — but what replaces it
- is not a layout. This op's reply is a flat [:fields] list, and a union
+ (* Data types have landed, so "milestone 6" was stale — but what replaces it
+ is not a layout. This op's reply is a flat [:fields] list, and a data type
is a tag and one payload per case: there is no one field list to
answer with, and flattening the cases into one would describe storage
no value ever has. So it says which kind of type this is, and where
the question it was probably asked for *is* answered — the renderer
- walks a union now, so a union value prints in a frame's locals and at
+ walks a data type now, so a data type value prints in a frame's
+ locals and at
`C-x C-e' with its case and that case's fields. *)
error
(ty
- ^ " is a union, not a struct; a union is a tag and one payload per case, so it has no single field list for this op to answer with. Its value renders with its case and fields in a frame's locals and at C-x C-e")
+ ^ " is a data type, not a struct; a data type is a tag and one payload per case, so it has no single field list for this op to answer with. Its value renders with its case and fields in a frame's locals and at C-x C-e")
else
let suffix = "/" ^ ty in
let candidates =
@@ -1221,7 +1222,7 @@ let render_addr (s : Session.t) ~addr ~(ty : Types.t)
let extra = ref [] and nslots = ref 0 in
let c =
{ Render.structs = s.Session.program.Tast.structs;
- unions = s.Session.program.Tast.unions;
+ datas = s.Session.program.Tast.datas;
enums =
Hashtbl.fold (fun k v acc -> (k, v) :: acc) s.Session.env.Check.enums [];
emit = Session.dev_emitter;
diff --git a/lib/emit.ml b/lib/emit.ml
index 337ea9e..a5aeaad 100644
--- a/lib/emit.ml
+++ b/lib/emit.ml
@@ -220,12 +220,12 @@ type m = {
out : Buffer.t;
strs : Buffer.t; (* string literal constants *)
structs : (string, Tast.structure) Hashtbl.t;
- (* The declared unions, by name. [Types.Named] covers both a struct and a
- union, so which table the name is in is the only thing that says which
+ (* The declared data types, by name. [Types.Named] covers both a struct and a
+ data type, so which table the name is in is the only thing that says which
this is — the same arrangement the checker uses, and for the same reason:
- a union is a type like any other everywhere except at its layout, its
+ a data type is a type like any other everywhere except at its layout, its
construction and its match. *)
- unions : (string, Tast.union) Hashtbl.t;
+ datas : (string, Tast.data) Hashtbl.t;
globals : (string, Types.t) Hashtbl.t;
(* Flan name -> C symbol, for the foreign functions. A call to one names the
symbol directly; there is no thunk. *)
@@ -300,7 +300,7 @@ let rec lay m (t : Types.t) : int * int =
in
s, a
| None ->
- match Hashtbl.find_opt m.unions n with
+ match Hashtbl.find_opt m.datas n with
| Some u ->
(* The tag then the payload, as one struct, so the answer is the same
arithmetic every other aggregate here gets rather than a second
@@ -332,11 +332,11 @@ and lay_fields m tys =
tys;
align_up !off !al, !al, List.rev !rev
-(* The size and alignment of a union's payload: room for the largest case, with
+(* The size and alignment of a data type's payload: room for the largest case, with
the alignment the widest member of any case needs, and the size rounded up
- to it so the blob divides evenly into [k x iA]. A union of payload-less
+ to it so the blob divides evenly into [k x iA]. A data type of payload-less
cases has a zero-size payload and is a bare tag. *)
-and payload_lay m (u : Tast.union) : int * int =
+and payload_lay m (u : Tast.data) : int * int =
let align = ref 1 and size = ref 0 in
List.iter
(fun (c : Tast.variant) ->
@@ -440,7 +440,7 @@ let rec dty m d (t : Types.t) : int =
(List.map (fun (fl : Tast.field) -> (fl.Tast.fname, fl.Tast.fty))
st.Tast.fields)
| None ->
- match Hashtbl.find_opt m.unions sn with
+ match Hashtbl.find_opt m.datas sn with
(* The truth about the bytes, and nothing cleverer: a tag and a blob.
DWARF 5 has DW_TAG_variant_part for exactly this, and lldb's C
support does not use it — a debugger that was handed one would
@@ -1110,8 +1110,8 @@ and value_at f (e : Tast.expr) : string =
ins f "store %s %s, ptr %s" (ll ty) v' ptr);
"zeroinitializer"
| Tast.Make (_, fields) -> aggregate f e.Tast.ty fields
- | Tast.MakeCase (uname, case, fields) ->
- emit_make_case f uname case fields
+ | Tast.MakeCase (dname, case, fields) ->
+ emit_make_case f dname case fields
| Tast.CaseField (target, case, i) ->
load f (case_field_addr f target case i) e.Tast.ty
| Tast.Arr items -> aggregate f e.Tast.ty items
@@ -1312,26 +1312,26 @@ and place f (p : Tast.place) : string * Types.t =
(* A struct or fixed-array value, built field by field from zeroinitializer.
The checker already filled the omitted fields in with Zero, so this is
simply every field in declaration order. *)
-(* A union value, built in memory rather than with [insertvalue], because the
+(* A data type value, built in memory rather than with [insertvalue], because the
payload's declared type is a blob of integers and the case's fields are not:
the two views of the same bytes are what a gep expresses and what a chain of
[insertvalue] cannot. The alloca is what [mem2reg] removes when nobody takes
an address of it. *)
-and emit_make_case f uname case fields =
- let ty = Types.Named uname in
- let u = Hashtbl.find f.md.unions uname in
+and emit_make_case f dname case fields =
+ let ty = Types.Named dname in
+ let u = Hashtbl.find f.md.datas dname in
let tag = match Tast.case_index u case with
| Some (i, _) -> i
- | None -> failwith ("no case " ^ case ^ " of " ^ uname)
+ | None -> failwith ("no case " ^ case ^ " of " ^ dname)
in
let tmp = alloca f ty in
ins f "store %s zeroinitializer, ptr %s" (ll ty) tmp;
let tp = fresh f in
- ins f "%s = getelementptr inbounds %s, ptr %s, i32 0, i32 0" tp (sname uname) tmp;
+ ins f "%s = getelementptr inbounds %s, ptr %s, i32 0, i32 0" tp (sname dname) tmp;
ins f "store i32 %d, ptr %s" tag tp;
if fields <> [] then begin
- let pp = payload_addr f uname tmp in
- let cty = sname (uname ^ "." ^ case) in
+ let pp = payload_addr f dname tmp in
+ let cty = sname (dname ^ "." ^ case) in
List.iteri
(fun i (p : Tast.expr) ->
let v = value f p in
@@ -1342,26 +1342,26 @@ and emit_make_case f uname case fields =
end;
load f tmp ty
-(* The payload blob's address. A union with no payload has no field 1, so this
+(* The payload blob's address. A data type with no payload has no field 1, so this
is only ever reached for one that has fields to reach. *)
-and payload_addr f uname base =
+and payload_addr f dname base =
let p = fresh f in
- ins f "%s = getelementptr inbounds %s, ptr %s, i32 0, i32 1" p (sname uname) base;
+ ins f "%s = getelementptr inbounds %s, ptr %s, i32 0, i32 1" p (sname dname) base;
p
-(* The address of one field of one case of a union value. The single place in
+(* The address of one field of one case of a data type value. The single place in
this backend that knows how a payload is read, so [match]'s binds and the
structural printer cannot come to different conclusions about it. *)
and case_field_addr f (target : Tast.expr) case i =
- let uname = match target.Tast.ty with
+ let dname = match target.Tast.ty with
| Types.Named n -> n
| t -> failwith ("case field of " ^ Types.to_string t)
in
let base = addr f target in
- let pp = payload_addr f uname base in
+ let pp = payload_addr f dname base in
let p = fresh f in
ins f "%s = getelementptr inbounds %s, ptr %s, i32 0, i32 %d"
- p (sname (uname ^ "." ^ case)) pp i;
+ p (sname (dname ^ "." ^ case)) pp i;
p
and aggregate f ty parts =
@@ -1787,17 +1787,17 @@ and emit_while f c body latch =
and emit_match f ty scrut arms =
(* The two subjects are the same shape and are read differently: an [Option]
is an SSA aggregate with an i8 tag and its payload in field 1, a declared
- union is read through its address because its payload is a blob that has
+ data type is read through its address because its payload is a blob that has
to be reinterpreted. So the tag and the binds are each produced by one of
two small functions and everything else below is shared. *)
- let uname =
+ let dname =
match scrut.Tast.ty with
- | Types.Named n when Hashtbl.mem f.md.unions n -> Some n
+ | Types.Named n when Hashtbl.mem f.md.datas n -> Some n
| Types.Option _ -> None
| t -> failwith ("match on " ^ Types.to_string t)
in
let tag, read_tag, bind_of =
- match uname with
+ match dname with
| None ->
let sv = value f scrut in
let sty = ll scrut.Tast.ty in
@@ -1813,7 +1813,7 @@ and emit_match f ty scrut arms =
ins f "store %s %s, ptr %s" (ll payload_ty) v f.slots.(slot);
bind_slot f slot)
| Some n ->
- let u = Hashtbl.find f.md.unions n in
+ let u = Hashtbl.find f.md.datas n in
(* Evaluated once, into a place, so that a scrutinee that is a call is
not re-run per arm. [addr] already spills a non-place for us. *)
let base = addr f scrut in
@@ -2551,22 +2551,22 @@ let rec const m (e : Tast.expr) =
| _ -> "{ " ^ String.concat ", " inner ^ " }")
| Tast.Some_ v ->
Printf.sprintf "{ i8 1, %s %s }" (ll v.Tast.ty) (const m v)
- (* A union's payload is declared as a blob of integers, so a constant of one
+ (* A data type's payload is declared as a blob of integers, so a constant of one
would have to be the case's fields *serialised into those integers* —
which is a byte-level encoder this compiler does not have, and which could
not express a string field at all, since that is a pointer the linker has
to relocate and a byte array has nowhere to put a relocation. Refused by
name, here, where the rest of the same rule is. A zeroed global is fine
and needs none of this: it is the first declared case, all-bytes-zero. *)
- | Tast.MakeCase (uname, case, _) ->
+ | Tast.MakeCase (dname, case, _) ->
fail e.Tast.loc
- "a global cannot be initialised with %s.%s — a union's payload is a \
+ "a global cannot be initialised with %s.%s — a data type's payload is a \
blob, and writing a case into one at link time needs a byte-level \
encoder that does not exist (a string field could not be encoded at \
all). Declare the global zeroed, which is %s.%s, and assign the case \
you meant in a function"
- uname case uname
- (match Hashtbl.find_opt m.unions uname with
+ dname case dname
+ (match Hashtbl.find_opt m.datas dname with
| Some { Tast.cases = c :: _; _ } -> c.Tast.vname
| _ -> "its first case")
| _ ->
@@ -2807,7 +2807,7 @@ let new_module ~checks ~dev ~known ?(debug = false) ?(sanitize = false)
(p : Tast.program) =
let m = {
out = Buffer.create 8192; strs = Buffer.create 512;
- structs = Hashtbl.create 16; unions = Hashtbl.create 16;
+ structs = Hashtbl.create 16; datas = Hashtbl.create 16;
globals = Hashtbl.create 16;
externs = Hashtbl.create 32;
checks; dev; known; nstr = 0; nfi = 0; sanitize;
@@ -2815,8 +2815,8 @@ let new_module ~checks ~dev ~known ?(debug = false) ?(sanitize = false)
} in
List.iter (fun (s : Tast.structure) -> Hashtbl.replace m.structs s.Tast.sname s)
p.Tast.structs;
- List.iter (fun (u : Tast.union) -> Hashtbl.replace m.unions u.Tast.uname u)
- p.Tast.unions;
+ List.iter (fun (u : Tast.data) -> Hashtbl.replace m.datas u.Tast.dname u)
+ p.Tast.datas;
List.iter (fun (g : Tast.global) -> Hashtbl.replace m.globals g.Tast.gname g.Tast.gty)
p.Tast.globals;
List.iter (fun (e : Tast.extern) -> Hashtbl.replace m.externs e.Tast.ename e.Tast.esym)
@@ -2828,7 +2828,7 @@ let new_module ~checks ~dev ~known ?(debug = false) ?(sanitize = false)
(String.concat ", "
(List.map (fun (f : Tast.field) -> ll f.Tast.fty) s.Tast.fields))))
p.Tast.structs;
- (* A union is a tag and a blob, and each of its cases is a struct laid over
+ (* A data type is a tag and a blob, and each of its cases is a struct laid over
the blob. Both are emitted as named types so that every reader — a
construction, a match arm, the structural printer — geps rather than
computing byte offsets of its own.
@@ -2840,25 +2840,25 @@ let new_module ~checks ~dev ~known ?(debug = false) ?(sanitize = false)
the point — the macro expander's [Form] has to be the same bytes in the
compiler and in the dlopened macro. *)
List.iter
- (fun (u : Tast.union) ->
+ (fun (u : Tast.data) ->
List.iter
(fun (c : Tast.variant) ->
Buffer.add_string m.out
(Printf.sprintf "%s = type { %s }\n"
- (sname (u.Tast.uname ^ "." ^ c.Tast.vname))
+ (sname (u.Tast.dname ^ "." ^ c.Tast.vname))
(String.concat ", "
(List.map (fun (f : Tast.field) -> ll f.Tast.fty)
c.Tast.vfields))))
u.Tast.cases)
- p.Tast.unions;
+ p.Tast.datas;
List.iter
- (fun (u : Tast.union) ->
+ (fun (u : Tast.data) ->
let size, align = payload_lay m u in
Buffer.add_string m.out
- (Printf.sprintf "%s = type { i32%s }\n" (sname u.Tast.uname)
+ (Printf.sprintf "%s = type { i32%s }\n" (sname u.Tast.dname)
(if size = 0 then ""
else Printf.sprintf ", [%d x i%d]" (size / align) (align * 8))))
- p.Tast.unions;
+ p.Tast.datas;
Buffer.add_char m.out '\n';
(* The foreign declarations. Every struct that crosses this boundary was
flattened by a C shim, so each of these is scalars only and no calling
diff --git a/lib/expand.ml b/lib/expand.ml
index 9cef480..a179009 100644
--- a/lib/expand.ml
+++ b/lib/expand.ml
@@ -19,10 +19,10 @@
offset 8. Those three numbers are the whole agreement between this file and
the compiled macro, and they are not taken on trust — test_acceptance.ml's
"Form's image format" asks LLVM for each of them through the same ptrtoint
- oracle the DWARF offsets go through. Change the prelude's defunion and that
+ oracle the DWARF offsets go through. Change the prelude's defdata and that
test says which number moved.
- The tag is the case's position in the prelude's (defunion Form ...), which
+ The tag is the case's position in the prelude's (defdata Form ...), which
is why that list is a layout contract and says so. *)
let form_size = 24
@@ -48,7 +48,7 @@ let tag_of_int = function
failwith
(Printf.sprintf
"a macro returned a Form with tag %ld, and Form has nine cases. The \
- prelude's (defunion Form ...) and lib/expand.ml's tag list are one \
+ prelude's (defdata Form ...) and lib/expand.ml's tag list are one \
contract and have come apart"
n)
diff --git a/lib/load.ml b/lib/load.ml
index b1fc2d3..c5382be 100644
--- a/lib/load.ml
+++ b/lib/load.ml
@@ -328,8 +328,9 @@ let qualify_decl owned alias (d : Ast.decl) : Ast.decl =
than anything a user wrote. *)
| Ast.Import (a, _) ->
fail loc "internal: the import of %s was not resolved before qualifying" a
- | Ast.Defunion (n, _) ->
- fail loc "%s is a union, and an imported union is not implemented yet \
+ | Ast.Defdata (n, _) ->
+ fail loc "%s is a data type, and an imported data type is not \
+ implemented yet \
(milestone 4)" n
in
{ d with Ast.d = k }
@@ -358,7 +359,7 @@ let qualify_decl owned alias (d : Ast.decl) : Ast.decl =
binders tracked are the ones a macro body can hold — its own parameter,
[let], [loop], [fn] and [dotimes]. A [match] pattern's names and a
[restart-case] clause's parameters are not tracked, which is a gap and a
- narrow one: it takes a macro body that both destructures a union and binds a
+ narrow one: it takes a macro body that both destructures a data type and binds a
name the package also declares at the top level. *)
let rec form_syms (f : Form.t) acc =
@@ -593,7 +594,7 @@ let decl_uses acc (d : Ast.decl) =
| Ast.Package _ | Ast.Import _ | Ast.Defenum _ -> ()
| Ast.Defalias (_, t) -> texpr_uses acc t
| Ast.Defstruct (_, fs) -> List.iter field fs
- | Ast.Defunion (_, vs) ->
+ | Ast.Defdata (_, vs) ->
List.iter (fun (v : Ast.variant) -> List.iter field v.Ast.vfields) vs
| Ast.Defn f -> fn f
(* Both declaration forms name types in their signature and nothing else.
diff --git a/lib/macro.ml b/lib/macro.ml
index 7be9038..26de4f2 100644
--- a/lib/macro.ml
+++ b/lib/macro.ml
@@ -108,7 +108,7 @@ let building = ref false
depends on a macro *removed*. Directly or transitively, because a function
calling a dropped one is as unbuildable as the dropped one itself.
- Only [defn]s are dropped. A [defstruct], [defunion], [defalias], [defenum]
+ Only [defn]s are dropped. A [defstruct], [defdata], [defalias], [defenum]
or [defvar] stays whatever it names: the functions that survive still
mention those types, and a reduced prelude missing them would not check.
There used to be a sharper reason — [Parse.prelude_types] memoised the
diff --git a/lib/parse.ml b/lib/parse.ml
index bdfb134..fbf5edf 100644
--- a/lib/parse.ml
+++ b/lib/parse.ml
@@ -455,7 +455,7 @@ and form f mk (head : Form.t) (args : Form.t list) : Ast.expr =
a head, which is the same property that makes a quasiquoted macro call
output rather than a dependency. Building a declaration as a value is what
a macro is for. *)
- | Sym ("defmacro" | "defn" | "defvar" | "defconst" | "defstruct" | "defunion"
+ | Sym ("defmacro" | "defn" | "defvar" | "defconst" | "defstruct" | "defdata"
| "defenum" | "defalias" | "import" as name) ->
fail f
"%s is a top-level declaration, not an expression. A quasiquoted one is \
@@ -850,10 +850,26 @@ let rec decl (f : Form.t) : Ast.decl =
| [ n; { v = Vec fs; _ } ] -> mk (Ast.Defstruct (sym n, fields f fs))
| _ -> fail f "defstruct is (defstruct Name [field Type ...])")
- | List ({ v = Sym "defunion"; _ } :: args) ->
+ | List ({ v = Sym "defdata"; _ } :: args) ->
(match args with
- | [ n; { v = Vec vs; _ } ] -> mk (Ast.Defunion (sym n, List.map variant vs))
- | _ -> fail f "defunion is (defunion Name [(Case [field Type ...]) ...])")
+ | [ n; { v = Vec vs; _ } ] -> mk (Ast.Defdata (sym n, List.map variant vs))
+ | _ -> fail f "defdata is (defdata Name [(Case [field Type ...]) ...])")
+
+ (* The tagged sum used to be spelled [defunion], and every file in the tree
+ said so until this rename. The old spelling is not an alias, and it is not
+ silently accepted either: the name is being reserved for a different type
+ — C's untagged union, where the members overlay one another and nothing
+ says which is live — so a [defunion] left behind by a stale file must not
+ keep meaning what it used to. Accepting it as an alias is the one option
+ that cannot be taken: the day the untagged form lands, the same text would
+ go on compiling and would mean the opposite thing, which is the silent
+ misparse the [defn] case below was rewritten to make impossible. *)
+ | List ({ v = Sym "defunion"; _ } :: _) ->
+ Loc.failk "parse/defunion-renamed" f.loc
+ "defunion is now defdata — (defdata Name [(Case [field Type ...]) ...]). \
+ The name defunion is reserved for a different type, so this is renamed \
+ rather than aliased: a file that kept the old spelling would otherwise \
+ go on compiling and mean something else"
(* The slot after the parameters is unconditionally the return type. It used
to be optional, and the parser decided return-type-versus-body by looking
@@ -1060,7 +1076,7 @@ and variant (f : Form.t) : Ast.variant =
| List [ { v = Sym n; _ }; { v = Vec fs; _ } ] ->
{ Ast.vname = n; vfields = fields f fs; vloc = f.loc }
| List [ { v = Sym n; _ } ] -> { Ast.vname = n; vfields = []; vloc = f.loc }
- | _ -> fail f "a union case is Name or (Name [field Type ...])"
+ | _ -> fail f "a data type case is Name or (Name [field Type ...])"
(* Macro expansion, which runs over [Form] and therefore before anything in
this file. It cannot be called directly: expanding a macro means compiling
diff --git a/lib/prelude.ml b/lib/prelude.ml
index 473026a..3ed65e6 100644
--- a/lib/prelude.ml
+++ b/lib/prelude.ml
@@ -1466,9 +1466,9 @@ let source = {flan|
;; site attached to what a macro produces", and it is what the queued
;; structured-error work will read.
;;
-;; Case order is the tag order (docs/BUILT.md, unions), so this list is a layout
+;; Case order is the tag order (docs/BUILT.md, data types), so this list is a layout
;; contract with lib/expand.ml's marshaller and may not be reordered.
-(defunion Form
+(defdata Form
[(Sym [s string])
(Kw [s string])
(Int [i i64])
diff --git a/lib/reach.ml b/lib/reach.ml
index 331f4cf..d9f0ec8 100644
--- a/lib/reach.ml
+++ b/lib/reach.ml
@@ -18,7 +18,7 @@
calls [@InitWindow] only moves the failure from the linker's argument
list to its symbol table.
- Only [fns] and [externs] are pruned. Globals, structs and unions stay:
+ Only [fns] and [externs] are pruned. Globals, structs and data types stay:
a dropped function is a loud link error, a dropped global would be a
silently different program, and an unreferenced global is bytes in BSS that
cost nothing. A [defvar brush rl/Texture2D] in a headless build is exactly
diff --git a/lib/render.ml b/lib/render.ml
index 218c1e3..04a2c05 100644
--- a/lib/render.ml
+++ b/lib/render.ml
@@ -57,10 +57,11 @@ type pointers = {
type ctx = {
structs : Tast.structure list;
- (* The declared unions. [Types.Named] covers a struct and a union alike, so
+ (* The declared data types. [Types.Named] covers a struct and a data type
+ alike, so
which list the name is in is what says which this is — the same
arrangement the checker and the emitter use. *)
- unions : Tast.union list;
+ datas : Tast.data list;
enums : (string * (string * int64) list) list;
emit : emitter;
(* [None] in a build with no registry to ask, which is every release build
@@ -225,15 +226,15 @@ let rec render c depth (e : Tast.expr) : Tast.expr list =
(Tast.If (is_some,
do_ ((lit "(some " :: render c (depth + 1) some) @ [ lit ")" ]),
lit "none")) ]
- (* A union, printed as the source would write it: the case is recovered
+ (* A data type, printed as the source would write it: the case is recovered
from the tag by a chain of comparisons, exactly as an enum's member name
is, and only the case in hand has its fields read. Reading the others
would be reading a payload that is not there. *)
| Types.Named n
- when List.exists (fun (u : Tast.union) -> String.equal u.Tast.uname n)
- c.unions ->
+ when List.exists (fun (u : Tast.data) -> String.equal u.Tast.dname n)
+ c.datas ->
let u =
- List.find (fun (u : Tast.union) -> String.equal u.Tast.uname n) c.unions
+ List.find (fun (u : Tast.data) -> String.equal u.Tast.dname n) c.datas
in
let tag = { Tast.e = Tast.Field (e, 0); ty = Types.Int Types.I32; loc } in
let one i (v : Tast.variant) otherwise =
@@ -269,7 +270,7 @@ let rec render c depth (e : Tast.expr) : Tast.expr list =
in
unit_ (Tast.If (is, body, otherwise))
in
- (* The fallback is a tag no case names, which only a scribbled-over union
+ (* The fallback is a tag no case names, which only a scribbled-over data type
could hold. Showing the number is more use than showing a case it is
not. *)
let base =
diff --git a/lib/session.ml b/lib/session.ml
index f6b0ba3..89d20bc 100644
--- a/lib/session.ml
+++ b/lib/session.ml
@@ -748,7 +748,7 @@ let render_locals ?(origin = "
Allocator$tdefunion, matched by casedefdata, matched by casei32()Neverfirst-even does above.
(Option T) is how absence is spelled: a lookup miss, an empty
collection, the end of a stream. match works on an Option and
-on a defunion, and on nothing else. some unwraps
+on a defdata, and on nothing else. some unwraps
Some and early-returns None from the enclosing function.
(defconst nums [4 i32] [4 8 15 16])
@@ -2108,7 +2108,7 @@ disagree with the first.