From 8659b76bc595a805def597882069d59156ddd6a6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 16:15:58 +0000 Subject: [PATCH 1/6] Implement the settled/roaming memory model (spec #212, #214, #216) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Syntax: a `^` token, `^T` as a type atom beside `&T`, and `x ^T Type` for an inline type parameter that takes an owner. Six new conflict states, all in the existing `<` declared-operator family; the ledger and census record them. Semantics: `Ty.Roaming` beside `Ty.Reference` (was `Ty.Guest`). `^` only on a local, a parameter or a return type, only on a reference type; a bare reference-typed result or abort type is an error, as is a marker on `this`. A new `states.ml` decides whether a place is settled, roaming, borrowed, contingent or fresh, and the analyses read it: a move takes only a roaming symbol, a field of one (leaving the root partly spent), a verb result or a case form; a reference is minted only from a settled place, through struct fields and `ArrayRef` elements, and a declared subscript is followed to its projection; a bare reference-type parameter and `this` are borrows; a `^T` parameter is scoped to the body's top block. `guests.ml` and `owners.ml` become `references.ml` and `scopes.ml`, and diagnostics use the owner / reference / scope vocabulary. A wrong-kind type argument is reported at its origin (generics.md §3.6). `@primitives$ArrayRef` with its literal constructor, `fill`, and subscript. `@primitives$String` is a value type, `print` borrows its text and `push` takes `^T`, which closes the old `print` divergence. Lowering and runtime: a reference is the settled owner's address, so the anchor pool, backpointers, forwarders and floating are gone (anchor.c is removed) and a reference-type instance is its members alone. A `^T` argument is moved into the callee and held in its body's arena; a borrow is passed by address. Every overwrite is in place, writing into each boxed member's existing block, and a block relocates only when its owner escapes. `ArrayRef` lowers as `Array` does, with `fill` a counted loop. Fixtures and goldens are rewritten in the new model (`hosts` and `guests` become `owners` and `settled`), with new codegen, runtime and reject cases for in-place overwrites observed through references, `ArrayRef`, takes, partial spending and the type-level rules. Design docs describe the new model and record a reference as a 64-bit address rather than a segmented offset. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01ArFxFkp985vTC6aHAAxp82 --- docs/ambiguity/proof-obligations.md | 17 +- docs/design/lowering.md | 231 ++--- docs/design/semantics.md | 183 ++-- docs/design/symbols.md | 9 +- docs/spec-divergences.md | 83 +- editors/tree-sitter-zane/grammar.js | 8 +- .../tree-sitter-zane/queries/highlights.scm | 1 + editors/typst/Zane.sublime-syntax | 2 + grammar/lexicon.coda | 1 + grammar/syntax.mly | 13 +- lib/cgt/build.ml | 9 +- lib/cgt/lower.ml | 254 +++--- lib/cgt/nodes.ml | 63 +- lib/cgt/outcome.ml | 2 +- lib/cgt/program.ml | 17 +- lib/cgt/runtime.ml | 19 +- lib/cgt/state.ml | 6 +- lib/cgt/symbol.ml | 3 +- lib/cgt/to_tree_graph.ml | 16 +- lib/cgt/type_layout.ml | 178 ++-- lib/cgt/verbs.ml | 8 +- lib/codegen/emit.ml | 27 +- lib/cst/lexer.ml | 1 + lib/cst/nodes.ml | 7 +- lib/cst/parser.mly | 14 +- lib/cst/to_span_text.ml | 4 +- lib/cst/to_tree_graph.ml | 12 +- lib/sst/lower.ml | 10 +- lib/sst/nodes.ml | 5 +- lib/sst/to_span_text.ml | 9 +- lib/sst/to_tree_graph.ml | 9 +- lib/tst/analyses/exits.ml | 2 +- lib/tst/analyses/moves.ml | 281 ++++--- lib/tst/analyses/read_only.ml | 37 +- lib/tst/analyses/{guests.ml => references.ml} | 291 +++---- lib/tst/analyses/{owners.ml => scopes.ml} | 119 +-- lib/tst/analyses/spawns.ml | 70 +- lib/tst/analyses/states.ml | 125 +++ lib/tst/check/check.ml | 38 +- lib/tst/check/context.ml | 10 +- lib/tst/check/instances.ml | 40 +- lib/tst/check/overloads.ml | 6 +- lib/tst/dune | 2 +- lib/tst/model/env.ml | 7 +- lib/tst/model/intrinsics.ml | 57 +- lib/tst/model/ty.ml | 42 +- lib/tst/passes/collect.ml | 2 +- lib/tst/passes/type_decls.ml | 202 ++++- lib/tst/passes/verb_signatures.ml | 39 +- lib/tst/render/to_tree_graph.ml | 2 +- lib/tst/semantics.ml | 4 +- runtime/anchor.c | 100 --- runtime/block.c | 133 ++- runtime/dune | 3 +- runtime/list.c | 12 +- runtime/main.c | 3 +- runtime/slot.c | 12 +- runtime/snapshot.c | 14 +- runtime/value.c | 131 ++- runtime/zane.h | 23 +- runtime/zane_internal.h | 66 +- tests/codegen/dune.inc | 230 +++-- tests/codegen/fixtures/arrayrefs/main.zn | 114 +++ tests/codegen/fixtures/boxes/main.zn | 48 +- tests/codegen/fixtures/constants/main.zn | 4 +- tests/codegen/fixtures/defaults/main.zn | 2 +- tests/codegen/fixtures/lambdas/main.zn | 14 +- tests/codegen/fixtures/lists/main.zn | 26 +- tests/codegen/fixtures/operators/main.zn | 4 +- .../fixtures/{hosts => owners}/main.zn | 82 +- tests/codegen/fixtures/references/main.zn | 10 +- tests/codegen/fixtures/regions/main.zn | 37 +- .../fixtures/{guests => settled}/main.zn | 84 +- tests/codegen/fixtures/spawns/main.zn | 23 +- tests/codegen/fixtures/texts/main.zn | 38 +- tests/codegen/golden/arrayrefs.cgt | 784 +++++++++++++++++ .../golden/arrayrefs.out} | 4 + tests/codegen/golden/arrays.cgt | 36 +- tests/codegen/golden/boxes.cgt | 701 +++++++++------- tests/codegen/golden/boxes.out | 3 + tests/codegen/golden/constants.cgt | 110 ++- tests/codegen/golden/counting.cgt | 11 +- tests/codegen/golden/defaults.cgt | 112 ++- tests/codegen/golden/guests.cgt | 786 ------------------ tests/codegen/golden/hello.cgt | 5 +- tests/codegen/golden/lambdas.cgt | 127 ++- tests/codegen/golden/lists.cgt | 512 ++++++------ tests/codegen/golden/outcomes.cgt | 7 +- .../codegen/golden/{hosts.cgt => owners.cgt} | 715 +++++++++------- .../codegen/golden/{guests.out => owners.out} | 1 - tests/codegen/golden/references.cgt | 76 +- tests/codegen/golden/settled.cgt | 695 ++++++++++++++++ .../codegen/golden/{hosts.out => settled.out} | 3 + tests/codegen/golden/shapes.cgt | 7 +- tests/codegen/golden/spawned.cgt | 33 +- tests/codegen/golden/spawns.cgt | 332 ++++---- tests/codegen/golden/texts.cgt | 759 +++++++++-------- tests/gen/gen_rules.ml | 2 +- tests/grammar/golden/parser.conflicts.census | 8 +- tests/grammar/golden/stock.conflicts.census | 4 +- tests/parser/golden/main.sst.spans | 6 +- tests/runtime/anchors.c | 84 -- tests/runtime/blocks.c | 155 ++-- tests/runtime/dune | 8 +- tests/runtime/dune.inc | 34 +- tests/runtime/golden/blocks.out | 5 + tests/runtime/golden/texts.out | 1 + tests/runtime/spawns.c | 5 +- tests/runtime/texts.c | 38 +- .../fixtures/typing/accept/app/main.zn | 25 + .../fixtures/typing/reject/bad/marks.zn | 43 + .../fixtures/typing/reject/bad/moves.zn | 78 +- .../reject/bad/{guests.zn => readonly.zn} | 14 +- .../fixtures/typing/reject/bad/rests.zn | 15 +- .../reject/bad/{owners.zn => scopes.zn} | 20 +- .../fixtures/typing/reject/bad/sources.zn | 60 +- .../fixtures/typing/reject/bad/temporary.zn | 12 +- .../fixtures/typing/reject/bad/types.zn | 2 +- tests/semantics/golden/typing.accept.decls | 55 +- tests/semantics/golden/typing.accept.tst | 499 ++++++----- .../semantics/golden/typing.accept.tst.spans | 70 ++ tests/semantics/golden/typing.reject.err | 402 +++++---- tests/unit/unit.ml | 10 +- 123 files changed, 6553 insertions(+), 4731 deletions(-) rename lib/tst/analyses/{guests.ml => references.ml} (50%) rename lib/tst/analyses/{owners.ml => scopes.ml} (80%) create mode 100644 lib/tst/analyses/states.ml delete mode 100644 runtime/anchor.c create mode 100644 tests/codegen/fixtures/arrayrefs/main.zn rename tests/codegen/fixtures/{hosts => owners}/main.zn (57%) rename tests/codegen/fixtures/{guests => settled}/main.zn (56%) create mode 100644 tests/codegen/golden/arrayrefs.cgt rename tests/{runtime/golden/anchors.out => codegen/golden/arrayrefs.out} (63%) delete mode 100644 tests/codegen/golden/guests.cgt rename tests/codegen/golden/{hosts.cgt => owners.cgt} (65%) rename tests/codegen/golden/{guests.out => owners.out} (95%) create mode 100644 tests/codegen/golden/settled.cgt rename tests/codegen/golden/{hosts.out => settled.out} (83%) delete mode 100644 tests/runtime/anchors.c create mode 100644 tests/semantics/fixtures/typing/reject/bad/marks.zn rename tests/semantics/fixtures/typing/reject/bad/{guests.zn => readonly.zn} (86%) rename tests/semantics/fixtures/typing/reject/bad/{owners.zn => scopes.zn} (69%) diff --git a/docs/ambiguity/proof-obligations.md b/docs/ambiguity/proof-obligations.md index 5ac04a72..5c909b93 100644 --- a/docs/ambiguity/proof-obligations.md +++ b/docs/ambiguity/proof-obligations.md @@ -59,8 +59,8 @@ numbers in a proof report refer to. | Automaton | Conflict states | with shift/reduce | with reduce/reduce | | --------- | --------------: | ----------------: | -----------------: | -| `--GLR`, the parser that ships | 56 | 55 | 1 | -| stock | 49 | 48 | 1 | +| `--GLR`, the parser that ships | 62 | 61 | 1 | +| stock | 55 | 54 | 1 | Menhir explains each conflict state once, so the explanations file holds one block per state; a state with both kinds of conflict counts in both of Menhir's @@ -95,7 +95,7 @@ are not independent problems: | Lookahead | States | Reduction | Root | | --------- | -----: | --------- | ---- | | `(` | 15 | `loption_generics_ ->` | before a call or a lambda, including standalone constructor type arguments | -| `<` | 12 | `loption_generics_ ->` | against `<` as a declared operator | +| `<` | 18 | `loption_generics_ ->` | against `<` as a declared operator | | `(` `<` | 3 | `loption_generics_ ->` | a named type opening a call or a generic list | | `(` `<` `{` `.` | 3 | `loption_generics_ ->` | a named type opening a constructor body | | `(` | 3 | `list_verb_type_suffix_ ->` | a type opening a statement, against a call | @@ -118,13 +118,16 @@ type from a constructor call or lambda continuing after that name. The constructor-type regression checks the permitted and rejected spellings; operator operands and ordinary function arguments no longer admit bare types. -The `<` row is about the declaration form, not the comparison. Its twelve states +The `<` row is about the declaration form, not the comparison. Its eighteen states all reduce toward `ret_type "<" "(" params ")" body`, the declaration of the `<` operator, against shifting `<` as the opening bracket of a generic argument list: after a name type, `Foo …` and `Foo <(a Int) { }` open with the same -two tokens. Three of the twelve are the same fork after an abort type, -`Int?Foo` against `Int?Foo <(a Int) { }`. Dropping `<` and `>` from the -operators a declaration may name removes all twelve and nothing else, which is what identifies the family; it is a +two tokens. Six of the eighteen are the same fork after an abort type, +`Int?Foo` against `Int?Foo <(a Int) { }`. Each fork comes three times, +once for each way a return type may name its type: bare, `&Foo` and `^Foo`, +the roaming owner a reference-typed return is written as. The `^` forms add +six states and nothing else, since `^` begins no expression. Dropping `<` and `>` from the +operators a declaration may name removes all eighteen and nothing else, which is what identifies the family; it is a language change rather than a restructuring, so it is a measurement here and not a proposal. diff --git a/docs/design/lowering.md b/docs/design/lowering.md index e8a694a1..9585e8ea 100644 --- a/docs/design/lowering.md +++ b/docs/design/lowering.md @@ -1,6 +1,6 @@ # Lowering: designing the CGT -> **Status: built through §8 step 10.** Stage 4 — lowering the TST to the +> **Status: built through §8 step 11.** Stage 4 — lowering the TST to the > code-generation tree — and the codegen that reads it follow this design, and > every step §8 lists is built and tested. Each decision is numbered > (**L1**…). §8 lists the order they were built in, and §9 the questions still @@ -8,9 +8,9 @@ The **CGT** is the one input codegen reads ([`stages.md`](stages.md)). The TST says what a program means, in the language's own terms: calls to overloads, -concepts, block arguments, handlers, hosts and guests. The CGT says what the +concepts, block arguments, handlers, owners and references. The CGT says what the machine does: functions over primitive storage, with every allocation, copy, -move, guest and death written out. Lowering is the only stage that turns one +move, reference and death written out. Lowering is the only stage that turns one into the other, so codegen never reads a type declaration, never resolves a name, and never asks what a `&` means. @@ -65,11 +65,13 @@ no concept survives lowering. one of: - a scalar: `i1`, `i8`, `i32`, `i64`, `f64`; -- a struct of CGT types, in declaration order ([`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §3.3), with - a `u32` backpointer first when it is a reference type; +- a struct of CGT types, in declaration order ([`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §3.3); a + reference type's instance carries nothing else; - a sum: a tag and the widest case's bytes, aligned for every case; -- a tether: a `u32` segmented offset ([`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §4.2); -- a fixed array: `@primitives$Array` is `n` elements of `T` inline, one +- a pointer: a reference, the address of the settled owner it names + ([`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §4.1, and §9 below for the representation); +- a fixed array: `@primitives$Array`, and `@primitives$ArrayRef` + which has its layout ([`generics.md`](https://github.com/zane-lang/spec/blob/911d749/spec/generics.md) §8.4), is `n` elements of `T` inline, one after another at `T`'s stride (its size rounded up to its alignment), so it is statically sized and sits in a slot like a struct. Positions count from 1 ([`control-flow.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/control-flow.md) §5.1): element `i` is at @@ -92,17 +94,18 @@ of owning edges, and none other to start with ([`adt.md`](https://github.com/zan **L6. Calls name one function and pass places.** A CGT call names the function it calls, so overload resolution, method lookup and the operator and flip forms are gone. What each argument passes follows its parameter's mode -([`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §2.9): +([`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §2.9): | Parameter | Passed as | |---|---| | value type (a borrow) | the address of the caller's slot, or the value itself when it is a scalar | -| `T`, a reference type (swallowed) | the address of the caller's slot, which keeps the value ([`lifetimes.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/lifetimes.md) §1.5) | -| `&T` (a guest) | a tether | -| `this` | the address of the subject's place, resolved once at the call | +| `T`, a reference type (a borrow) | the address of the caller's slot, which keeps the owner | +| `^T` (a take) | the moved value itself, which the callee holds in its body's arena, so the body's drain ends it unless the body moves it on ([`lifetimes.md`](https://github.com/zane-lang/spec/blob/911d749/spec/lifetimes.md) §1.5) | +| `&T` (a reference) | the address of the settled owner it names | +| `this` | the address of the subject's place, taken once at the call | -A call site that passes a guest to a `T`-typed place, or mints one for an -`&T` parameter, does so with an explicit CGT operation (§3), not inside the +A call site that mints a reference for an `&T` parameter, or moves an owner +into a `^T` one, does so with an explicit CGT operation (§3), not inside the call. **L7. A result is written into a destination the caller names.** Every verb @@ -124,42 +127,45 @@ tracks what is live. that owns storage opens its scope's arena on entry and drains it on every way out: falling off the end, `return`, `abort`, or an exit (§4). Draining first waits for the scope's spawned work (the water tower, [`concurrency.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/concurrency.md) -§4.1). Then it ends every hosting identity the scope still holds in bulk: it -returns the terminal anchors of those identities and the forwarders on the -scope's retirement stack ([`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §4.6), and unmaps -the scope's fixed-size and dynamic chunks together (`memory.md` §3.2). There is no -per-object pass at a drain; an object that dies earlier — overwritten, or -its container gone — is destroyed there, by its own `destroy` (L9). Lowering +§4.1). Then it returns the blocks its values still own, and releases the +scope's fixed-size and dynamic chunks together ([`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §3.2). +There is no other per-object pass at a drain; an object that dies earlier — +overwritten, or its container gone — is destroyed there, by its own +`destroy` (L9). Lowering may fold nested scopes into one arena when nothing observes the difference ([`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §3.1); the first version gives every block that declares a local its own. **L9. Storage operations are CGT nodes, each a call into the runtime or a few -instructions.** The set lowering writes, with the sections of [`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) +instructions.** The set lowering writes, with the sections of [`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) that define each: - `slot` — a fixed-size slot in a scope's arena; - `copy` — a value-type copy, recursive through its boxed payloads (§2.3); -- `move` — a rehost into a destination of the same type, with its anchor - merge (§3.7, §4.5); -- `destroy` — a reference object's or a value's death, returning its dynamic - blocks and retiring its anchor (§4.6); -- `float` — a contingent occupant's move into an anonymous host of the same - owner (§2.8.1); -- `mint` — a tether taken from a place, creating its anchor if it has none - (§4.3); -- `resolve` — a tether's address, through its anchor chain (§4.4). - -Where the TST has a `Let`, an `Assign` or a hosting argument, lowering picks +- `move` — a roaming owner's inline bytes copied into a destination of the + same type, its source spent; its dynamic blocks stay where they are unless + the move is an escape, which relocates them first (§3.5); +- `overwrite` — a replacement written in place at the occupant's address, + each boxed member's block kept and written into, recursively, and the rest + of the occupant's blocks returned (§2.2, §3.6); +- `destroy` — an owner's or a value's death, returning its dynamic blocks + (§3.2). + +A reference needs no operation of its own: minting one takes the address of +a settled place, which the semantic pass alone may admit (§2.8), and a +settled owner never moves, so the address holds until its scope drains +(§4.2). + +Where the TST has a `Let`, an `Assign` or an owning argument, lowering picks from this list using what the move analysis already knows: whether the source -is a place or a fresh result, whether the destination is fresh, stable or -contingent, and whether the type is a value or a reference. +is a place or a fresh result, whether the destination is fresh or already +holds a value, and whether the type is a value or a reference. -**L10. A place is an address inside a call, and a tether when stored.** A -field read, an element read or a `this` is an LLVM pointer for as long as one -expression needs it. Only `&` storage — a local, a field, an element, a -parameter — holds a tether. So a guest costs its anchor load where the spec -says it does, and nowhere else. +**L10. A place is an address inside a call, and a reference is one when +stored.** A field read, an element read or a `this` is an LLVM pointer for as +long as one expression needs it, and `&` storage — a local, a field, an +element, a parameter — keeps that pointer. So reading through a reference is +one load, which is what the spec says it costs. --- @@ -255,8 +261,8 @@ in every object that reads it, shared as a generic instance is ## 6. The runtime is a C library **L17. The runtime is written in C and linked with every program.** It owns -the chunk directory, the scope arenas and their size stacks, the global anchor -pool, the thread pool, and the few primitives a program cannot state in the +the chunk directory, the scope arenas and their size stacks, the thread pool, +and the few primitives a program cannot state in the language: printing, `List` growth, `String` storage. Its interface is a small set of C functions that CGT storage operations (L9) and intrinsic calls lower to, so a change to how an arena works changes the runtime and nothing in the @@ -264,8 +270,7 @@ compiler. It lives in `runtime/`, is built by `clang`, which already links every program, and is tested in C on its own. C because it adds no toolchain beside the LLVM the compiler already uses, and -because an arena and an anchor pool are exactly the kind of -code C states without ceremony. +because an arena is exactly the kind of code C states without ceremony. --- @@ -276,8 +281,8 @@ Lowering lives in `lib/cgt/`, beside `lib/tst/`: `nodes.ml` for the tree, whole program built with it, and `to_tree_graph.ml` to render it. Codegen lives in `lib/codegen/`: `emit.ml` builds the module through the bindings, and `build.ml` writes the object file and links it with the runtime. The -runtime is written as nine parts in `runtime/` (`main.c`, `arena.c`, -`anchor.c` and so on), with `zane.h` as the ABI that emitted code calls and +runtime is written as eight parts in `runtime/` (`main.c`, `arena.c`, +`block.c` and so on), with `zane.h` as the ABI that emitted code calls and `zane_internal.h` as what the parts share. `runtime/dune` joins the parts in a fixed order into one generated `zane.c` and embeds it, with both headers, into the compiler as strings, so a build needs nothing beside the compiler @@ -371,7 +376,8 @@ test passing. host. The runtime checks that scopes drain in order and that none is left open, and is tested in C on its own. 6. **Guests.** The anchor pool, `mint`, `resolve`, anchor merges, the - backpointer (L5), and `float`. A reference-type subject and a swallowed + backpointer (L5), and `float`, all since replaced by step 11. A + reference-type subject and a swallowed argument are passed by address (L6), and a `match` binder is its payload's address (L13). The runtime keeps, arrives, vacates, merges and floats identities from a per-type layout of where each host's @@ -406,13 +412,26 @@ test passing. passed as a guest, and spawned calls to an intrinsic or to a verb expanded where it is called. After this step lowering refuses no program the TST accepts. +11. **Settled and roaming owners.** The memory model of spec + [#212](https://github.com/zane-lang/spec/pull/212) and + [#214](https://github.com/zane-lang/spec/pull/214). A reference is the + settled owner's address, so the anchor pool, the backpointer, forwarders + and floating are gone, and a reference-type instance is its members + alone. A `^T` argument is moved into the callee, which holds it in its + body's arena; a bare reference-type parameter is a borrow, passed by + address. Every overwrite is made in place, keeping each boxed member's + block, and a block relocates only when its owner escapes. + `@primitives$ArrayRef` lowers as `Array` does, with `fill` a counted + loop over its lambda. `@primitives$String` is a value type. The runtime + is tested in C on its own: an overwrite keeps a boxed member's address, + and arrival leaves blocks that outlive their destination where they are. --- ## 9. Open questions - **How many scopes get an arena.** L8 gives every block that declares a local - its own. For now only a block that hosts a reference-type local opens one, + its own. For now only a block that holds a reference-type local opens one, and a value-type local stays an LLVM stack slot, since nothing tells the two placements apart until a value owns dynamic storage, which it does only through a boxed member; such a value is held in the arena too. @@ -425,20 +444,25 @@ test passing. next scope that reaches it, where [`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §3.1 unmaps a scope's chunks at its drain. The spec leaves arena granularity to the implementation and fixes only that a scope's memory is released together, which this does. -- **Anchors as the runtime keeps them.** An anchor cell holds its host's - address rather than a segmented offset, and a tether or backpointer holds - a cell's index in one pool that grows by segments, where [`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §4.1 has pages of - cells named by segmented offsets. A forwarder retires with the identity - it forwards to rather than at its former source scope's drain, which is - later but still after every guest that could name it. Nothing a program - does can tell these apart. -- **A floated host outlives its owner.** A variant payload's anchored - occupant floats ([`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §2.8.1) into a block of the program's own - region, open until the program ends, rather than until its owner scope - drains, and the blocks it owns move there too, as does anything later - stored into it. A host floated in a spawned call goes there as well. That - region goes with the program, unchecked. A host has no destructor, so the - longer life is not observable. +- **A reference is an address.** [`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §4.1 stores a + reference as the `u32` segmented offset of the settled owner it names. The + runtime addresses everything else with native pointers rather than + segmented offsets -- slots, handles and blocks alike, as the next item + says -- so a reference is the owner's 64-bit address too. Minting one is + taking the place's address, and reading through one is a load. A settled + owner never moves, and the semantic pass admits a reference only to a + settled place, so the address holds until the owner's scope drains + (§4.2). Nothing a program does can tell the two representations apart; + moving the runtime's addressing to segmented offsets is one change for + all of them. +- **Every overwrite is in place.** [`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §2.2 requires + it of a settled owner: the replacement is written at the occupant's + address, and each boxed member reached through struct fields and + `ArrayRef` elements keeps its block, which the incoming payload is written + into, recursively. The runtime does the same for every overwrite of a value + that owns blocks -- a roaming owner, a list's element, a variant payload of + the same case, a value -- since nothing references those and reusing a + block of the right size is never observable. - **Each scope's dynamic region.** A scope's blocks are in a region of its own, as [`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §3.1–3.2 has it: chunks of its own, a bump frontier, and a stack of returned blocks per size and alignment, all given back at its @@ -446,11 +470,16 @@ test passing. of that size, in place when it is last at the frontier, or else into new bytes. Where the runtime departs from the spec: - A block is placed where its value is made, which is the innermost - scope, and moves into the region of the scope that holds the place the - value arrives in: a slot, an overwritten place, a list's element. The - spec builds a fresh value in its destination directly (§3.7); here a - value built for an older scope's place is moved there once. A list's - growth goes to the region that holds the list. + scope. When the value arrives in a place -- a slot, an overwritten + place, a list's element -- its blocks stay where they are if their + region outlives the place's: the same scope, an enclosing one of the + same context, or the program's own region. Otherwise the arrival is an + escape (§3.5), and they move into the region that holds the place + first. The spec builds a fresh value in its destination directly + (§2.3); here a value built for an older scope's place is moved there + once. A region of another context, such as a spawned call's, is taken + as shorter-lived, so a result coming home always moves its blocks. A + list's growth goes to the region that holds the list. - A value leaving scopes that drain -- a `return`, a `resolve`, an `abort`, a `return` from a block argument or an arm -- moves every block it owns in them into the scope the exit returns to, before the drain @@ -464,8 +493,8 @@ test passing. - Every block is at least a word, and aligned to one; a list's is aligned to a cache line. A chunk a region gives back is kept for the next region rather than unmapped. - - A value that owns a block is held in its scope's arena like a host, - and so is a fresh host or value that nothing keeps, such as a result + - A value that owns a block is held in its scope's arena like an owner, + and so is a fresh owner or value that nothing keeps, such as a result that is dropped or an operand, so the drain returns its blocks. Having returned them, a drain finds no block out in its region, and the runtime stops a program where it does, since that block's owner is @@ -478,8 +507,8 @@ test passing. context for the call. The call's result is kept in its frame, with its blocks in the first scope of the call's context, until it comes home: at a read of the local it is bound to, or at its block's drain. There it is - copied into a slot the block reserved, its anchors follow it, and its - blocks move into the block's region, as any value's do where it arrives. + copied into a slot the block reserved, and its blocks move into the + block's region, as any value's do where it arrives from another context. The context then goes back to a pool for the next call. The chunk map names each chunk's context along with its index or scope. @@ -487,9 +516,8 @@ test passing. spawned is out, that call can reach its storage: a `mut` subject that owns blocks is written where it lives, in the spawner's region. So while any is out, the context's scopes and regions change under its lock, and another - thread always takes that lock for a context it reaches. Anchor cells are - kept in segments that stay where they are, and are made and retired under - a lock. The spec leaves all of this to the implementation. + thread always takes that lock for a context it reaches. The spec leaves + all of this to the implementation. - **The pool steals work.** Each pool thread keeps a deque of the calls it spawned and runs its own newest first; a thread with none left steals another's oldest ([`concurrency.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/concurrency.md) §2.4). Calls spawned from the program's @@ -500,9 +528,9 @@ test passing. `setThreads` resizes the pool while it runs: more threads start at once, and a thread over the count leaves when it next finds no work, its deque kept for the next thread to start. The pool keeps at most 4095 threads. -- **A write through a host from spawned work.** A spawned `mut` call whose - subject is reached through a host -- a member of a reference-type - instance, of what a guest names, or an element of a list -- is the one +- **A write through an owner from spawned work.** A spawned `mut` call whose + subject is reached through an owner -- a member of a reference-type + instance, of what a reference names, or an element of a list -- is the one way spawned work writes where another thread may read at the same time ([`concurrency.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/concurrency.md) §4.2–§4.4). Such a call works on a copy of its subject in its frame, deep for a value that owns blocks, and writes it back when it @@ -513,7 +541,7 @@ test passing. written where it is, since no other thread can reach it (§4.3). Other threads see the call's writes all at once, when it returns, which is one of the orders §3.7 already allows. -- **Snapshots.** A value read through a host into a fresh binding -- a +- **Snapshots.** A value read through an owner into a fresh binding -- a local, an argument, an operand -- is read as a snapshot (§4.4): its bytes are taken when every write-back begun is done, and taken again if one began meanwhile. Nothing a snapshot names is ever returned while a reader @@ -524,9 +552,9 @@ test passing. place reads it where it is. Each snapshot is a call into the runtime, where an inline check of the two counts would do; that, and how long retired values are kept, is left to measurement. -- **A host lent to a running spawn.** The spawning block may not write a - host it lent a spawn that may still be reading it; the checker rejects - that write ([`spec-divergences.md`](../spec-divergences.md) §14), so the +- **An owner lent to a running spawn.** The spawning block may not write an + owner it lent a spawn that may still be reading it; the checker rejects + that write ([`spec-divergences.md`](../spec-divergences.md) §13), so the only writes that race a reader are spawned write-backs, which the snapshots above cover. - **Where a spawned call is waited for.** Only a spawned call bound by a @@ -536,41 +564,39 @@ test passing. there. Only timing tells the two apart. - **A spawned call that can abort or exit.** Its frame holds the call's whole outcome (L12), which comes home into a slot laid out for it: the - result's hosts and blocks under the done tag, the abort value's under the + result's blocks under the done tag, the abort value's under the aborted one. The call settles once, on the spawning thread, where it is first read or where its block ends - ([`spec-divergences.md`](../spec-divergences.md) §13). A flag the spawn sets + ([`spec-divergences.md`](../spec-divergences.md) §12). A flag the spawn sets says whether it has. An abort takes the abort value out of the slot and runs the handler written at the spawn, lowered where it settles but in the context of the spawn, so its `abort`, `return` and exit go where they would from there; its `resolve` puts the result in the slot. An exit ends the run of the block the spawn is in. - **An abort value no binder names is held.** A handler without a binder, - such as `??`'s, still holds the abort value in its own scope when it is a - host or owns a block, so the handler's drain ends it. + such as `??`'s, still holds the abort value in its own scope when it is an + owner or owns a block, so the handler's drain ends it. - **A value parameter is borrowed.** A value that owns a block is copied whole where it is stored from a place ([`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §2.3), and passed as it is where it is only read: a value parameter is read-only (§2.9), so the caller keeps it, and a fresh one is held in the caller's scope first. A callee that stores its parameter copies it. -- **A case read of a host.** What a case read gives is its payload, which +- **A case read of an owner.** What a case read gives is its payload, which the variant keeps, or what its handler resolves, which is fresh. So a read - whose type is a host, or a value that owns a block, gives an address: the + whose type is a reference type, or a value that owns a block, gives an address: the payload's, or that of a slot reserved in the reading scope before the read, which the handler's `resolve` fills. Either way what it gives has one owner, and the drain ends the slot. -- **A literal's bytes stay where the program keeps them.** A string view's - handle points into the dynamic region ([`types.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/types.md) §2.7). A literal's +- **A literal's bytes stay where the program keeps them.** A string's + handle points into the dynamic region ([`types.md`](https://github.com/zane-lang/spec/blob/911d749/spec/types.md) §2.7). A literal's points at the bytes the program embeds instead, and the handle also holds the room of the block it owns, which is 0 for a literal's, so a literal - takes no block and returns none. Like any reference type's instance, the - view starts with its backpointer. -- **A `this` address across a call that moves.** L10 resolves `this` once per - call. That is safe while nothing in the call relocates what it names, which - the single-writer rule ([`concurrency.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/concurrency.md) §4.3) should - guarantee. A guest subject is resolved once, at the call; a call inside - the method that moves the subject's host would leave the address stale, - which nothing checks yet. + takes no block and returns none, and a copy of one shares the embedded + bytes. +- **A `this` address is taken once per call.** The subject is a borrow + ([`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §2.9), so nothing in the call moves what it names: + a settled owner never moves, and a roaming one is moved only in the block + that declares it, which is not inside the call. - **Moving to a newer LLVM.** llvm-project's bindings track every release, but opam packages them only up to 19. A newer release means building them from that release's `llvm/bindings/ocaml`, or waiting for opam. @@ -582,28 +608,29 @@ test passing. destination the caller names. Lowering passes and returns value types as LLVM aggregate values instead, which copies what L6 would lend; a value parameter cannot be written, so nothing observes the difference. A - reference-type result is returned the same way, and arrives where the - caller hosts it: its anchors follow it there, and its blocks move into - that scope's region. A `mut` subject, a - reference-type subject and a swallowed argument are passed by address. + reference-type result, and a `^T` argument, are passed the same way, and + arrive where the caller or the callee holds them, their blocks staying + where they are unless the arrival is an escape. A `mut` subject, a + reference-type subject and a borrowed reference-type argument are passed + by address. - **An outcome as a sum, for now.** L12 returns a tag and has the caller pass slots for the result and the abort value. Until results are written into destinations, a function that can abort or exit returns a sum of three cases instead: done with its result, aborted with its abort value, and exited. A function that can only finish returns its result as before. An exit ends the run of the block the call is written in - ([`spec-divergences.md`](../spec-divergences.md) §11), so each run of a block + ([`spec-divergences.md`](../spec-divergences.md) §10), so each run of a block argument has a label to leave. - **A 64-bit target.** Codegen sizes a sum's payload room assuming 8-byte pointers and C struct layout, which holds for x86-64 and AArch64. Another target reads the sizes from LLVM's data layout. - **Integer division by zero.** The spec leaves it open - ([`spec-divergences.md`](../spec-divergences.md) §10). Until it says, the + ([`spec-divergences.md`](../spec-divergences.md) §9). Until it says, the program stops: what it wrote so far is kept, the runtime writes `division by zero` to stderr, and the status is 1. The one other quotient an integer cannot hold, the most negative value over `-1`, wraps, as `+` and `*` do. - **An index out of range.** The spec leaves it open ([`control-flow.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/control-flow.md) - §5.2, [`spec-divergences.md`](../spec-divergences.md) §16). Until it says, the program stops as it does at a division by zero: + §5.2, [`spec-divergences.md`](../spec-divergences.md) §15). Until it says, the program stops as it does at a division by zero: what it wrote so far is kept, the runtime writes `index out of range` to stderr, and the status is 1, for a list and an array alike. - **A type argument passes nothing.** A generic verb is lowered once per diff --git a/docs/design/semantics.md b/docs/design/semantics.md index 8256d776..7b638fe5 100644 --- a/docs/design/semantics.md +++ b/docs/design/semantics.md @@ -59,7 +59,7 @@ That splits the work like this: |---|---| | Package assembly and imports ([`packages.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/packages.md) §2–§3) | Moves, stores and lifetimes ([`lifetimes.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/lifetimes.md) §1) | | Type declarations, aliases, value-downstream ([`memory.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/memory.md) §2.10) | Resting places published with a signature ([`lifetimes.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/lifetimes.md) §1.11) | -| Signatures, inline generic parameters ([`generics.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/generics.md) §3–§4) | Read-only guests: a guest derived from a parameter stays read-only ([`effects.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/effects.md) §4.4) | +| Signatures, inline generic parameters ([`generics.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/generics.md) §3–§4) | Read-only references: a reference derived from a parameter stays read-only ([`effects.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/effects.md) §4.4) | | Overload identity and resolution ([`functions.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/functions.md) §4–§6) | `spawn` safety ([`concurrency.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/concurrency.md) §3–§4) | | Implicit constructors at coercion sites ([`types.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/types.md) §4) | | | `:`/`!` against `mut` ([`functions.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/functions.md) §2.5) | | @@ -163,13 +163,19 @@ else. operator import (§3.6). 3. **Types.** Resolve every `type` and `alias` right-hand side to a `Ty.t` (§4). Check here: alias cycles; moulds only on a right-hand side - (`types.md` §5.3); value-downstream (`memory.md` §2.10); `&` only on a - reference type (`memory.md` §2.4). + (`types.md` §5.3); value-downstream (`memory.md` §2.10); `&` and `^` only + on a reference type, and `^` only on a local, a parameter or a return type + ([`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §2.1, §2.4, [`syntax.md`](https://github.com/zane-lang/spec/blob/911d749/spec/syntax.md) + §2.3); a type argument of the wrong kind, reported where it is written + ([`generics.md`](https://github.com/zane-lang/spec/blob/911d749/spec/generics.md) §3.6). 4. **Signatures.** Resolve every verb's parameter and return types, introducing inline generic parameters at their first marked occurrence (`generics.md` §3.2, §4.4). Check here: - overload identity, including no overloads that differ only by passing mode - or by the `mut` of a function-type parameter (`functions.md` §4.1); + (`T`, `^T` or `&T`) or by the `mut` of a function-type parameter + (`functions.md` §4.1); + - a reference-typed result or abort type written `^T` or `&T`, never bare, + and no marker on `this` ([`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §2.9); - the operator home-package rule ([`operators.md`](https://github.com/zane-lang/spec/blob/e0b4249/spec/operators.md) §2.2); - implicit-constructor source and destination kinds, and the orphan rule (`types.md` §4.4–§4.5); @@ -180,53 +186,83 @@ else. Then the analyses of D1's right-hand column run over the finished tree. -**Read-only guests** (`lib/tst/analyses/read_only.ml`, -[`effects.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/effects.md) §4.4): a `!` call whose subject reaches a guest taken from +**Read-only references** (`lib/tst/analyses/read_only.ml`, +[`effects.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/effects.md) §4.4): a `!` call whose subject reaches a reference taken from a read-only binding is an error. It follows each -guest through locals, fields, arguments and returns, and summarises every verb +reference through locals, fields, arguments and returns, and summarises every verb by which parameters reach its result and which come to rest in its `this`, the resting places of `lifetimes.md` §1.11 without their owners. A call substitutes its arguments into the callee's summary; summaries are computed to a fixed point first, because verbs may call each other in a cycle. -**Guest sources and stores** (`lib/tst/analyses/guests.ml`) are the store rules that +**States** (`lib/tst/analyses/states.ml`, [`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §2.1, +§2.8.1). The one function the three analyses below read to know what state +the place an expression denotes is in: +- **settled**: a bare reference-type local or a package constant, what a + reference names, and a struct field or `ArrayRef` element of a settled root; +- **roaming**: a local or parameter declared `^T`, and a field or `ArrayRef` + element of one; +- **borrowed**: a bare reference-type parameter, `this`, and what is reached + from either; +- **contingent**: a list's element, a variant's payload, a case read, a + `match` binder, and a member of a temporary — storage that is neither an + owner a store may move from nor a place a reference may name; +- **fresh**: a verb's result, a case form, or any other value nothing owns. + +A declared subscript is followed through its body to the projection it ends +at ([`functions.md`](https://github.com/zane-lang/spec/blob/911d749/spec/functions.md) §2.9), so it is settled exactly +when that projection is an `ArrayRef` element of a settled root. Every answer +comes from declared types along the path, so it is the same at every point of +the body. + +**References** (`lib/tst/analyses/references.ml`) are the store rules that need nothing but the store in hand: -- a new guest is minted only from a stable place: a symbol, or fields reached - from one, with no `[]` and no variant case on the way ([`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §2.8); -- a swallowed parameter is never bound into `&` storage ([`memory.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/memory.md) §2.9); -- a store never goes through a guest, unless that guest is a parameter the - path starts at ([`lifetimes.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/lifetimes.md) §1.1). - -**Moves** (`lib/tst/analyses/moves.ml`, [`lifetimes.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/lifetimes.md) §1.2–§1.3, §1.6, §1.8). A -reference-type value stored where a host goes — a hosting local or field, a -`T` parameter, a return, an element, a case payload — is moved: -- only a symbol, a verb's result or a case form is moved; a field, an - element, a case payload, a package constant, a guest and `this` are not; -- a symbol is moved only in the block that declares it, and a parameter is - declared at the top of the body; +- a new reference is minted only from a settled place ([`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) + §2.8); a value that is already a reference is copied, from anywhere; +- a store never goes through a reference, unless that reference is a + parameter the path starts at ([`lifetimes.md`](https://github.com/zane-lang/spec/blob/911d749/spec/lifetimes.md) §1.1). + +A borrow needs no rule of its own: it is no place to mint from and no owner +to move from, so it is never stored or returned +([`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §2.9). + +**Moves** (`lib/tst/analyses/moves.ml`, [`lifetimes.md`](https://github.com/zane-lang/spec/blob/911d749/spec/lifetimes.md) §1.2–§1.3, §1.6, §1.8). A +reference-type value stored where an owner goes — an owning local or field, a +`^T` parameter, a return, an element, a case payload — is moved: +- only a roaming symbol, a field of one, a verb's result or a case form is + moved; a settled owner, a borrow, `this`, an element, a case payload, a + package constant and a reference are not; +- a bare reference-type parameter is a borrow, so passing to one moves + nothing; +- a roaming symbol is moved only in the block that declares it, and a + parameter is declared at the top of the body; - a moved symbol is spent: using it is an error until a store refills it, in - that same block. + that same block. A field moved out of a roaming symbol leaves that field + spent and the symbol spent as a whole, until a store in the symbol's block + refills the field ([`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §2.8.1). A symbol is spent or refilled only in its own block, and a nested block can do neither, so one walk in source order sees every use against the right state. -**Owners** (`lib/tst/analyses/owners.ml`, [`lifetimes.md`](https://github.com/zane-lang/spec/blob/b0675d6/spec/lifetimes.md) §1.1, §1.4, §1.7, §1.10, §1.11). A -local is owned by its declaring block, a field or element by its root's -owner, and a parameter or `init{ }` by the call site, which outlives the body. -A value names the owners of the hosts it reaches through a guest, its own and -those it carries. A `let`, an assignment, a field of `init{ }` and a return -are legal only when every owner the value names outlives the destination's. -A move needs no check of its own (§1.4): a symbol moves only in its declaring -block, so the host it moves into is declared there or above. +**Scopes** (`lib/tst/analyses/scopes.ml`, [`lifetimes.md`](https://github.com/zane-lang/spec/blob/911d749/spec/lifetimes.md) §1.1, §1.4, §1.7, §1.10, §1.11). A +local's scope is its declaring block, a field's or element's is its root's, +and a `^T` parameter's is the body's top block; any other parameter and +`init{ }` stand for the call site, which outlives the body. A value names the +scopes of the owners it reaches through a reference, its own and those it +carries. A `let`, an assignment, a field of `init{ }` and a return are legal +only when every scope the value names outlives the destination's. A move +needs no check of its own (§1.4): a symbol moves only in its declaring block, +so the owner it moves into is declared there or above. A store from one parameter into a place reached from another is where the first -comes to rest (§1.11). It goes in the verb's summary, as a pair of parameter +comes to rest (§1.11), whether it is a reference or a `^T` parameter carrying +one. It goes in the verb's summary, as a pair of parameter indices, and each call makes that store with its own arguments and compares there. A call in a body can store one parameter into another in turn, so the summaries are computed to a fixed point over every body before any reports. **Exits** (`lib/tst/analyses/exits.ml`, [`docs/spec-divergences.md`](../spec-divergences.md) -§11). A verb exits when `@controlflow$exitFromCall` is in its own frame: its +§10). A verb exits when `@controlflow$exitFromCall` is in its own frame: its body, or a block written there. A call to one ends the run of the block it is written in, so it is an error in no block. A lambda's own frame holds no `@controlflow$exitFromCall` at all @@ -252,15 +288,15 @@ constructor only where the verb is written out, so lowering checks that one. **Spawns** (`lib/tst/analyses/spawns.ml`, [`concurrency.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/concurrency.md) §4.2–§4.3). A spawned `mut` call writes its subject, so a subject of a reference type, or a -guest to one, is an error. A spawn written as a statement or bound by a `let` +reference to one, is an error. A spawn written as a statement or bound by a `let` borrows its subject's place until the block it is written in drains, since the drain waits for it; one read where it is written is waited for at once and borrows nothing past itself. While a borrow lasts, a second spawn borrowing an overlapping place is an error, and so is any read or write of one in that block or a block inside it. Two places overlap when one's path of fields and cases is a prefix of the other's, and any two elements of one list overlap. -A place reached through a guest is the place the guest names, followed as -below for a lent host; where the checker cannot follow it, two places may +A place reached through a reference is the place the reference names, followed +as below for a lent owner; where the checker cannot follow it, two places may overlap when either's type may hold the other's. A spawned subject's index, or its case read's handler, is read at the spawn like any other read. In a block that runs more than once, a spawn takes its subject from a local @@ -270,17 +306,17 @@ verb runs more than once: one it passes on to such a position, or passes anywhere from inside a block that runs more than once, computed to a fixed point over every body. -The same spawn is lent every host passed to it, directly or through a guest, +The same spawn is lent every owner passed to it, directly or through a reference, until the same drain, and the block may not write one meanwhile -([`spec-divergences.md`](../spec-divergences.md) §14): not by assignment, not as +([`spec-divergences.md`](../spec-divergences.md) §13): not by assignment, not as a `!` call's subject, not by moving it out. A spawned `mut` call on part of it is allowed, since it writes back (docs/design/lowering.md §9). Where a write goes -through a guest, the checker follows the guest to the place it was minted -from, through other guests, as long as the guest has not been bound again in -a block inside its own. A guest whose place it cannot follow may name any host -of its type, so a write through it clashes with a lent host that could be, -or contain, or be inside, what it writes; so does a write to a known place -when the lent host came through such a guest. +through a reference, the checker follows the reference to the place it was +minted from, through other references, as long as the reference has not been +bound again in a block inside its own. A reference whose place it cannot +follow may name any owner of its type, so a write through it clashes with a +lent owner that could be, or contain, or be inside, what it writes; so does a +write to a known place when the lent owner came through such a reference. **D4. Diagnostics accumulate.** The parser stops at the first error, which suits a parser. A type checker that stops at the first error fails the author once per @@ -297,7 +333,8 @@ exits non-zero if there is any. (* lib/tst/model/ty.ml *) type t = | Named of { id : Type_id.t; args : arg list } (* a declared type, applied *) - | Guest of t (* &T *) + | Reference of t (* &T *) + | Roaming of t (* ^T *) | Primitive of Primitive.t (* @primitives$Int, ... *) | Concept of concept (* literals, blocks *) | Verb of verb (* a function type *) @@ -368,7 +405,7 @@ Where the typing rules need care: | Operator | Candidates from the operand types' home packages only; imports add none (`operators.md` §2.2). A swapped `Op` is resolved as the primitive with operands in passed order (see D8). | | Abort handler | Required on every abortable call and on every member read of a variant, rejected on a total member read (D13); the handler's `resolve` values must have the handled operation's success type; every path ends in `resolve`, `return` or `abort` (`error-handling.md` §3.1–§3.2). | | `match` | Every case covered by exactly one arm; every arm yields the same type — no arm is a coercion site, so "the same" is exact (`adt.md` §5). | -| Block argument | Typed `@concepts$Block`: a block yields nothing ([`spec-divergences.md`](../spec-divergences.md) §12). | +| Block argument | Typed `@concepts$Block`: a block yields nothing ([`spec-divergences.md`](../spec-divergences.md) §11). | | Collection literal | `@concepts$Array` when every element has the same concrete type `T`; with a bare literal element it fixes no `T` and cannot drive inference (`generics.md` §5.4). | --- @@ -620,7 +657,7 @@ only reject more, never let a write through. - An intrinsic, or a call through a function value, has no body to summarise. It is taken to hand every argument back in its result and, when it writes its subject, to store every argument there — each only where the parameter's type - can hold a guest. + can hold a reference. - A path into a value is cut at four steps, since a recursive type would let one grow without end. A cut path names the place that contains the real one. - A block argument may run any number of times, so after it a local holds what @@ -629,40 +666,47 @@ only reject more, never let a write through. stored. - A generic verb's summary is the union over its instances. -**What the owner analysis assumes.** Each choice can only reject more. +**What the scope analysis assumes.** Each choice can only reject more. - A local names everything ever stored in it, at any path: the body is walked until that stops growing, then once more to report. - A call's result names what each argument names as its parameter takes it, - a guest parameter adding the owner of the place it is minted from: a verb - may return a guest rooted in any parameter (`lifetimes.md` §1.7). + a reference parameter adding the scope of the place it is minted from: a + verb may return a reference rooted in any `&T` parameter (`lifetimes.md` + §1.7). - A resting place (§1.11) is kept as the pair of parameters, not the path - between them. Every step of a path takes its root's owner, so the call - compares the owner of the argument's place, or, for an argument that is a - guest, the owners it names. -- A value read through a guest parameter, `other.port`, names that - parameter's host. A guest the host carries outlives the host (§1.1), so the - host is the shorter of the two. + between them. Every step of a path takes its root's scope, so the call + compares the scope of the argument's place, or, for an argument that is a + reference, the scopes it names. +- A value read through a reference parameter, `other.port`, names that + parameter's owner. A reference the owner carries outlives the owner (§1.1), + so the owner is the shorter of the two. - `push` keeps its value in `this`; no other intrinsic keeps anything. A call through a function value keeps nothing, since its type carries no summary. **What moves, where the spec leaves it to the table.** - A subscript's body is a place (`functions.md` §2.9), so it moves nothing - out; reading `list[i]` into a host is what the move rule then rejects. + out; reading `list[i]` into an owner is what the move rule then rejects. - A case read and what its handler resolves are a place too: the store the whole expression feeds decides whether it moves. -- An intrinsic operator or constructor reads its operands. -- The runtime's `print` takes `text &@primitives$String`, a guest, so a - string type wrapping one hands it its field, which it could not move. The spec declares a plain - `@primitives$String` ([`spec-divergences.md`](../spec-divergences.md) §9). - -**Where a guest source is decided.** -- A `match` binder is its case's payload, so no guest is minted from it. -- A method's subject is a guest the call lends, not storage, so a call never +- An intrinsic operator or constructor reads its operands, and `push` takes + its value as `^T`. +- `^T` describes a place, not a value, so no expression's type carries it: a + `^T` local read, and a call returning `^T`, are values of type `T`. What a + place holds is read from the local's or the signature's declared type. + +**Where a reference source is decided.** +- A `match` binder is its case's payload, so no reference is minted from it. +- A method's subject is a borrow the call lends, not storage, so a call never mints one for it: `list[i]:inspect()` is a read. -- The swallowed-parameter rule looks at a store into a field, an element or a - case payload written in the body, directly or through a guest local that - ever held the parameter. One reached through a call's resting places - (`lifetimes.md` §1.11) is not checked yet. + +**Where a wrong-kind type argument is reported** (`generics.md` §3.6). An +explicit argument, in a type written anywhere, is reported where it is +written. An inferred one is reported at the value argument it was read from, +when the instance's own signature puts it in a value mould's field; that +instance is then not made, so nothing inside a forwarding verb reports it +again. A mistake that only a generic body's own code makes, and that its +signature does not show, is reported inside the instance, which names the +call that required it. **`main`** is not required, since a library built on its own is also a root. When the root declares one, it takes no parameters, and it may return any @@ -672,5 +716,4 @@ type, whose value is discarded (`packages.md` §6.2). ## 10. Not done yet -- Resting places for a function value, whose type would have to carry them, - and the swallowed-parameter rule applied through a call's resting places. +- Resting places for a function value, whose type would have to carry them. diff --git a/docs/design/symbols.md b/docs/design/symbols.md index 643c315d..d04bff3a 100644 --- a/docs/design/symbols.md +++ b/docs/design/symbols.md @@ -23,7 +23,8 @@ geometry$List<%primitives$Int> %primitives$Int ``` -Arguments are separated by `, `. A guest is written `&` before its type. +Arguments are separated by `, `. A reference is written `&` before its type, +and a roaming owner `^`. An intrinsic namespace is written with `%` where the source writes `@`: `%primitives$Int` is `@primitives$Int`. A linker reads `@` in an exported @@ -98,7 +99,7 @@ pkg$Op.apply$lambda1 ## Layout tables -The table the runtime reads to find a type's hosts and blocks +The table the runtime reads to find the blocks a type's values own ([`lowering.md`](lowering.md) L9) is called by the type's name. An outcome's table is called `outcome of T`, with ` ? A` added when the verb can abort with `A`. @@ -118,8 +119,8 @@ the function a spawned call to a verb expanded where it is called (`zane.expanded.N`) or to an intrinsic (`zane.intrinsic.N`) is made into ([`lowering.md`](lowering.md) §9), each string literal's bytes (`zane.text`), the entry that makes a program's constants and then calls -`main` (`zane.start`), and the variable a guest to a program value such as -`@program$console` is anchored at (`zane.value.@program$console`). +`main` (`zane.start`), and the variable whose address a reference to a +program value such as `@program$console` holds (`zane.value.@program$console`). ## What the compiler does today diff --git a/docs/spec-divergences.md b/docs/spec-divergences.md index d39ad293..54abfaa0 100644 --- a/docs/spec-divergences.md +++ b/docs/spec-divergences.md @@ -17,7 +17,7 @@ settles, in one pass rather than section by section. Until then: the claim can be rechecked rather than taken on trust. A choice the spec leaves open is recorded here when a running program can -observe it, as §10 and §13 are. The rest sit beside the design they belong +observe it, as §9 and §12 are. The rest sit beside the design they belong to: what the type checker decides is in [`design/semantics.md`](design/semantics.md) §9, with D12 and D14, and how lowering and the runtime place and represent things is in @@ -239,7 +239,7 @@ register(Int); // rejected: an ordinary function argument held Slot = Int; // rejected: the value of a declaration arr Array(Int + 1); // rejected: an operand room Slots(Array, 2); // rejected: an applied type -room Slots(&Int, 2); // rejected: a guest type +room Slots(&Int, 2); // rejected: a reference type room Slots(Int[3], 2); // rejected: an array type ``` @@ -381,37 +381,7 @@ mention one. It needs a digit on each side, and none of the loose operators of (`docs/ambiguity/proof-obligations.md`). Reconciling means either the spec adopting the separator or the lexer dropping it. -## 9. The console's `print` takes a guest - -**Spec** — [`effects.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/effects.md) -§6.6, as of `7fa876f`, which introduced it: - -```zane -@primitives$Unit print(this @runtime$Console, text @primitives$String) mut -``` - -`@primitives$String` is a reference type ([`types.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/types.md) -§2.7), so a plain parameter of it swallows its argument: the caller moves a -view in. - -**Compiler** — `text &@primitives$String`, a guest. `print` writes the view -out and keeps nothing, which is what a guest parameter is for. Zane has no -borrow of a reference type, and a guest differs from one only in taking a -place rather than any expression, so a view built for the call is stored -first: - -```zane -text @primitives$String("hello world"); -@program$console!print(text); // accepted -@program$console!print(@primitives$String("hello world")); // rejected: a temporary -``` - -A swallowing `print` would also leave `core`'s `String` no way to reach the -console, since its view is a field (`text.raw`), and a field is not a -move-source ([`lifetimes.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/lifetimes.md) -§1.2). Reconciling means the spec declaring the parameter `&@primitives$String`. - -## 10. An integer division by zero stops the program +## 9. An integer division by zero stops the program **Spec** — silent. [`operators.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/operators.md) §4.3 says division is declared rather than derived, and nothing says what @@ -431,7 +401,7 @@ quotient Int = Int(1) / zero(); // stops here, status 1 Reconciling means the spec stating an outcome, or making `/` abortable, now that aborts lower (step 4 of `docs/design/lowering.md` §8). -## 11. An exit ends the run of a block +## 10. An exit ends the run of a block **Spec** — [`control-flow.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/control-flow.md) §2.3 and §4.2: `@controlflow$exitFromCall` ends the invocation that called @@ -443,7 +413,7 @@ the verb containing it, and blocks are transparent to it, so a `guard` inside written in, and a call to an exiting verb is legal only inside a block. A verb's body must end in an explicit `return`, `Unit()` included ([`error-handling.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/error-handling.md) -§8), while a block yields nothing (§12 below), so it has nothing to give and +§8), while a block yields nothing (§11 below), so it has nothing to give and is the one thing an exit can end. ```zane @@ -463,7 +433,7 @@ written there. `lib/tst/analyses/exits.ml` checks the calls, and `tests/semantics/fixtures/typing/reject/bad/exits.zn` is the rejected case. Reconciling means the spec adopting this reading. -## 12. A block yields nothing +## 11. A block yields nothing **Spec** — [`control-flow.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/control-flow.md) §2.1 and §2.4: a block's type is `@concepts$Block`, or `@concepts$Block` @@ -491,7 +461,7 @@ done Bool = if(ready) { condition of §3.3 has no form here. Reconciling means the spec dropping `Block`, or the compiler taking it back. -## 13. A spawned call's handler runs where the call settles +## 12. A spawned call's handler runs where the call settles **Spec** — [`concurrency.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/concurrency.md) §3.3: an abortable spawned call attaches `?` or `??` directly to the @@ -503,7 +473,7 @@ or what happens when nothing reads the symbol. its local is first read, or where the block it is spawned in ends, when nothing has read it by then. Settling waits for the call. An abort runs the handler there, and an exit ends the run of the block the spawn is written in -(§11), from there. A block left before it ends -- by a `return`, an exit or +(§10), from there. A block left before it ends -- by a `return`, an exit or an `abort` -- still waits for the call as it drains, but runs no handler, and the outcome dies with the block. @@ -519,21 +489,22 @@ check(q == Int(7)); // settled already: the handler does not run again `tests/codegen/fixtures/spawns` has these cases. Reconciling means the spec saying where the handler runs. -## 14. A block does not write a host it lent a running spawn +## 13. A block does not write an owner it lent a running spawn **Spec** — [`concurrency.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/concurrency.md) §4.2 lets spawned work read the reference-type object graph without writing it, which makes "every concurrent **read** of the reference-typed object graph safe by construction". §4.3 keeps the spawning block off a location while a spawn holds its mutable borrow. Neither says anything about the -spawning block writing a host it passed to a spawn that only reads it, and +spawning block writing an owner it passed to a spawn that only reads it, and that block keeps running while the spawn does. **Compiler** — a spawn written as a statement or bound by a `let` is lent -every host passed to it, directly or through a guest, until its block drains, -and the block may not write one meanwhile: by assignment, as a `!` call's -subject, or by moving it out. Where a guest's host cannot be followed, the -write is judged by type, so some programs that would not race are rejected. +every owner passed to it, directly or through a reference, until its block +drains, and the block may not write one meanwhile: by assignment, as a `!` +call's subject, or by moving it out. Where the owner a reference names cannot +be followed, the write is judged by type, so some programs that would not race +are rejected. ```zane view &Dial = dial; @@ -547,7 +518,7 @@ spawn dial.reading!nudge(); // accepted: written back (§4.4) Reconciling means the spec stating a rule for this write, this one or another. -## 15. A case is not a type +## 14. A case is not a type **Spec** — [`adt.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/adt.md) §3: "A member projected as a type is written `Expr.intLit`", and §5.2: "if the @@ -569,13 +540,13 @@ projected case type would still be the whole variant (§3.2), and the narrowing it enables is an optimization the tag jump already gives. Reconciling means adding the type form and its narrowing, or the spec dropping it. -## 16. An index out of range stops the program +## 15. An index out of range stops the program **Spec** — silent. [`control-flow.md`](https://github.com/zane-lang/spec/blob/7fa876f/spec/control-flow.md) §5.2 fixes the ordinal base and leaves "the language-level behavior for out-of-range element access" as a separate question. -**Compiler** — the program stops as it does at a division by zero (§10): what +**Compiler** — the program stops as it does at a division by zero (§9): what it wrote so far is flushed, the runtime writes `index out of range` to stderr, and the status is 1, for a list and an array alike. `tests/codegen/fixtures/range` and `tests/codegen/fixtures/bounds` are the @@ -592,8 +563,9 @@ Kept briefly so a reader who remembers them can see they were closed on purpose, and by what. The first three closed at the `034f11a` re-pin, when the spec moved to `;`-terminated statements and a brace that ends one. The next closed from the other side, when the compiler adopted a spec rule it had been -standing in for, and the last when the compiler followed the spec in removing a -form. +standing in for, the next when the compiler followed the spec in removing a +form, and the last when the spec's memory model made the compiler's departure +unnecessary. - **Statements are terminated, not separated.** The spec separated statements by newline and called it "the one place a newline is structural"; the @@ -676,3 +648,16 @@ form. lexer, and the call is written `show(Color.red)`. The `Pipe` and `MethodTarget` nodes went with it, since a method target was only ever a pipe's callee. + +- **The console's `print` took a reference.** The spec declares + `print(this @runtime$Console, text @primitives$String)`, and while + `@primitives$String` was a reference type a plain parameter of it consumed + its argument, which would have spent the caller's string and left `core`'s + `String` no way to pass the view it holds in a field. The compiler took + `text &@primitives$String` instead. Spec + [#209](https://github.com/zane-lang/spec/pull/209) made + `@primitives$String` a value type, whose parameter is a read-only borrow, + and [#212](https://github.com/zane-lang/spec/pull/212) made a bare + reference-type parameter a borrow too, so the spec's own signature reads the + text without taking it. The compiler now declares `print` exactly as the + spec does, and a string built in the argument is passed as it is. diff --git a/editors/tree-sitter-zane/grammar.js b/editors/tree-sitter-zane/grammar.js index 69d73d9b..0fbd5a4f 100644 --- a/editors/tree-sitter-zane/grammar.js +++ b/editors/tree-sitter-zane/grammar.js @@ -7,7 +7,7 @@ module.exports = grammar({ source_file: $ => choice($._list_top_decl_, seq()), abort_handle: $ => choice(prec.right(1, seq("?", $.identifier, $.body)), prec.right(1, seq("??", $.expr))), abort_handle_braced: $ => choice(seq("?", $.identifier, $.body_braced), prec(8, seq("??", $.expr_braced))), - abort_ret_type: $ => choice(seq($.type_expr, "?", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", $.identifier, "$", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "@", $.identifier, "$", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "&", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "&", $.identifier, "$", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "&", "@", $.identifier, "$", $.type_identifier, optional($._loption_generics_))), + abort_ret_type: $ => choice(seq($.type_expr, "?", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", $.identifier, "$", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "@", $.identifier, "$", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "&", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "&", $.identifier, "$", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "&", "@", $.identifier, "$", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "^", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "^", $.identifier, "$", $.type_identifier, optional($._loption_generics_)), seq($.type_expr, "?", "^", "@", $.identifier, "$", $.type_identifier, optional($._loption_generics_))), app: $ => choice($.func_callee, seq($.type_identifier, ".", $.identifier), seq($.identifier, "$", $.type_identifier, ".", $.identifier), seq("@", $.identifier, "$", $.type_identifier, ".", $.identifier)), app_braced: $ => choice($.primary_braced, $.braced_verb_call), block_body: $ => seq("{", optional($._list_stat_), "}"), @@ -22,7 +22,7 @@ module.exports = grammar({ braced_verb_call: $ => choice(seq($.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq("@", $.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq("@", $.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}")), _computed_call_no_trailing_arg_: $ => choice(seq($.func_callee, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")"), seq($.app, ":", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")"), seq($.app, "!", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")")), _computed_call_trailing_arg_: $ => choice(seq($.func_callee, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", "{", optional($._list_stat_), "}"), seq($.func_callee, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", $.map_lit), seq($.app, ":", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", "{", optional($._list_stat_), "}"), seq($.app, ":", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", $.map_lit), seq($.app, "!", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", "{", optional($._list_stat_), "}"), seq($.app, "!", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", $.map_lit)), - constructor_field: $ => choice(seq($.identifier, $.type_expr), seq($.identifier, $.type_expr, "=", $.expr), seq($.identifier, $.type_identifier, "Type"), seq($.identifier, $.type_identifier, "Type", "=", $.expr), seq($.identifier, $.type_identifier, "(", ")"), seq($.identifier, $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.identifier, "$", $.type_identifier, "(", ")"), seq($.identifier, $.identifier, "$", $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "(", ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}")), + constructor_field: $ => choice(seq($.identifier, $.type_expr), seq($.identifier, $.type_expr, "=", $.expr), seq($.identifier, $.type_identifier, "Type"), seq($.identifier, $.type_identifier, "Type", "=", $.expr), seq($.identifier, "^", $.type_identifier, "Type"), seq($.identifier, "^", $.type_identifier, "Type", "=", $.expr), seq($.identifier, $.type_identifier, "(", ")"), seq($.identifier, $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.identifier, "$", $.type_identifier, "(", ")"), seq($.identifier, $.identifier, "$", $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "(", ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}")), enum_map_decl: $ => choice(seq($.type_identifier, optional($._loption_generics_), ".", $.identifier, $.type_expr, "{", optional($._list_terminated_enum_map_entry_SEMICOLON__), "}"), seq($.identifier, "$", $.type_identifier, optional($._loption_generics_), ".", $.identifier, $.type_expr, "{", optional($._list_terminated_enum_map_entry_SEMICOLON__), "}"), seq("@", $.identifier, "$", $.type_identifier, optional($._loption_generics_), ".", $.identifier, $.type_expr, "{", optional($._list_terminated_enum_map_entry_SEMICOLON__), "}")), expr: $ => choice($.app, $.app_braced, $.block_call, seq("spawn", $.unbraced_verb_call), seq("spawn", $.braced_verb_call), seq($.ret_type, "(", optional($._loption_separated_nonempty_list_COMMA_param__), ")", $.body), seq($.ret_type, "(", "this", $.type_expr, optional($._loption_preceded_COMMA_separated_nonempty_list_COMMA_param___), ")", optional($._boption_MUT_), $.body), prec.left(5, seq($.expr, "==", $.expr)), prec.left(5, seq($.expr, "<=", $.expr)), prec.left(5, seq($.expr, ">=", $.expr)), prec.left(5, seq($.expr, "<", $.expr)), prec.left(5, seq($.expr, ">", $.expr)), prec.left(5, seq($.expr, "~=", $.expr)), prec.left(6, seq($.expr, "+", $.expr)), prec.left(6, seq($.expr, "-", $.expr)), prec.left(7, seq($.expr, "*", $.expr)), prec.left(7, seq($.expr, "/", $.expr)), prec.left(2, seq($.expr, "'==", $.expr)), prec.left(2, seq($.expr, "'~=", $.expr)), prec.left(2, seq($.expr, "'<=", $.expr)), prec.left(2, seq($.expr, "'>=", $.expr)), prec.left(2, seq($.expr, "'<", $.expr)), prec.left(2, seq($.expr, "'>", $.expr)), prec.left(3, seq($.expr, "'+", $.expr)), prec.left(3, seq($.expr, "'-", $.expr)), prec.left(4, seq($.expr, "'*", $.expr)), prec.left(4, seq($.expr, "'/", $.expr)), prec(9, seq("~", $.expr)), prec(9, seq("&", $.ref_target)), prec(8, seq($.expr, $.abort_handle))), expr_braced: $ => choice($.app_braced, $.block_call, seq("spawn", $.braced_verb_call), seq($.ret_type, "(", optional($._loption_separated_nonempty_list_COMMA_param__), ")", $.body_braced), seq($.ret_type, "(", "this", $.type_expr, optional($._loption_preceded_COMMA_separated_nonempty_list_COMMA_param___), ")", optional($._boption_MUT_), $.body_braced), prec.left(5, seq($.expr, "==", $.expr_braced)), prec.left(5, seq($.expr, "<=", $.expr_braced)), prec.left(5, seq($.expr, ">=", $.expr_braced)), prec.left(5, seq($.expr, "<", $.expr_braced)), prec.left(5, seq($.expr, ">", $.expr_braced)), prec.left(5, seq($.expr, "~=", $.expr_braced)), prec.left(6, seq($.expr, "+", $.expr_braced)), prec.left(6, seq($.expr, "-", $.expr_braced)), prec.left(7, seq($.expr, "*", $.expr_braced)), prec.left(7, seq($.expr, "/", $.expr_braced)), prec.left(2, seq($.expr, "'==", $.expr_braced)), prec.left(2, seq($.expr, "'~=", $.expr_braced)), prec.left(2, seq($.expr, "'<=", $.expr_braced)), prec.left(2, seq($.expr, "'>=", $.expr_braced)), prec.left(2, seq($.expr, "'<", $.expr_braced)), prec.left(2, seq($.expr, "'>", $.expr_braced)), prec.left(3, seq($.expr, "'+", $.expr_braced)), prec.left(3, seq($.expr, "'-", $.expr_braced)), prec.left(4, seq($.expr, "'*", $.expr_braced)), prec.left(4, seq($.expr, "'/", $.expr_braced)), prec(9, seq("~", $.expr_braced)), prec(9, seq("&", $.ref_target_braced)), seq($.expr, $.abort_handle_braced)), @@ -49,7 +49,7 @@ module.exports = grammar({ _loption_separated_nonempty_list_COMMA_param_type__: $ => $._separated_nonempty_list_COMMA_param_type_, map_lit: $ => seq("{", $._nonempty_list_terminated_map_entry_SEMICOLON__, "}"), _nonempty_list_terminated_map_entry_SEMICOLON__: $ => choice(seq($.expr, ",", $.expr, ";"), seq($.expr, ",", $.expr, ";", $._nonempty_list_terminated_map_entry_SEMICOLON__)), - param: $ => choice(seq($.identifier, $.type_expr), seq($.identifier, $.type_identifier, "Type"), seq($.type_identifier, "Type")), + param: $ => choice(seq($.identifier, $.type_expr), seq($.identifier, $.type_identifier, "Type"), seq($.identifier, "^", $.type_identifier, "Type"), seq($.type_identifier, "Type")), primary: $ => choice($.integer_literal, $.decimal_literal, $.string_literal, "true", "false", seq("[", optional($._loption_separated_nonempty_list_COMMA_expr__), "]"), "this", $.identifier, seq($.identifier, "$", $.identifier), seq("@", $.identifier, "$", $.identifier), seq("(", $.expr, ")")), primary_braced: $ => choice(seq("init", "{", optional($._list_terminated_field_arg_SEMICOLON__), "}"), $.map_lit, seq("match", "(", $._separated_nonempty_list_COMMA_expr_, ")", "{", optional($._list_match_arm_), "}")), ref_target: $ => choice($.app, $.app_braced, prec(9, seq("&", $.ref_target)), prec(9, seq("~", $.ref_target))), @@ -75,7 +75,7 @@ module.exports = grammar({ _type_decl_enum_moulded_value_: $ => choice(seq("type", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "enum", "[", $._separated_nonempty_list_COMMA_lname_, "]"), seq("type", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "#", "enum", "[", $._separated_nonempty_list_COMMA_lname_, "]"), seq("alias", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "enum", "[", $._separated_nonempty_list_COMMA_lname_, "]"), seq("alias", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "#", "enum", "[", $._separated_nonempty_list_COMMA_lname_, "]")), _type_decl_moulded_value_: $ => choice(seq("type", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "struct", "{", optional($._list_body_field_), "}"), seq("type", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "variant", "{", optional($._list_body_field_), "}"), seq("type", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "#", "struct", "{", optional($._list_body_field_), "}"), seq("type", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "#", "variant", "{", optional($._list_body_field_), "}"), seq("alias", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "struct", "{", optional($._list_body_field_), "}"), seq("alias", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "variant", "{", optional($._list_body_field_), "}"), seq("alias", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "#", "struct", "{", optional($._list_body_field_), "}"), seq("alias", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", "#", "variant", "{", optional($._list_body_field_), "}")), _type_decl_raw_value_: $ => choice(seq("type", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", $.type_expr), seq("alias", $.type_identifier, optional($._loption_delimited_LESS_separated_nonempty_list_COMMA_generic_param__MORE__), "=", $.type_expr)), - type_expr: $ => choice(seq($.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq($.identifier, "$", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("@", $.identifier, "$", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("&", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("&", $.identifier, "$", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("&", "@", $.identifier, "$", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq($.abort_ret_type, "[", optional($._loption_separated_nonempty_list_COMMA_param_type__), "]", optional($._list_verb_type_suffix_)), seq($.abort_ret_type, "[", "this", $.type_expr, optional($._loption_preceded_COMMA_separated_nonempty_list_COMMA_param_type___), "]", optional($._boption_MUT_), optional($._list_verb_type_suffix_))), + type_expr: $ => choice(seq($.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq($.identifier, "$", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("@", $.identifier, "$", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("&", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("&", $.identifier, "$", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("&", "@", $.identifier, "$", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("^", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("^", $.identifier, "$", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq("^", "@", $.identifier, "$", $.type_identifier, optional($._loption_generics_), optional($._list_verb_type_suffix_)), seq($.abort_ret_type, "[", optional($._loption_separated_nonempty_list_COMMA_param_type__), "]", optional($._list_verb_type_suffix_)), seq($.abort_ret_type, "[", "this", $.type_expr, optional($._loption_preceded_COMMA_separated_nonempty_list_COMMA_param_type___), "]", optional($._boption_MUT_), optional($._list_verb_type_suffix_))), unbraced_verb_call: $ => choice($._computed_call_no_trailing_arg_, seq($.type_identifier, "(", ")"), seq($.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.type_identifier, ".", $.identifier, "(", ")"), seq($.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, "$", $.type_identifier, "(", ")"), seq($.identifier, "$", $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, "$", $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, "$", $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq("@", $.identifier, "$", $.type_identifier, "(", ")"), seq("@", $.identifier, "$", $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq("@", $.identifier, "$", $.type_identifier, ".", $.identifier, "(", ")"), seq("@", $.identifier, "$", $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")")), integer_literal: $ => new RustRegex("(?:(?:[0-9])+(?:(?:'(?:[0-9])+))*)"), decimal_literal: $ => new RustRegex("(?:(?:(?:[0-9])+(?:(?:'(?:[0-9])+))*)\\.(?:[0-9])+)"), diff --git a/editors/tree-sitter-zane/queries/highlights.scm b/editors/tree-sitter-zane/queries/highlights.scm index ac36f516..91d19dc9 100644 --- a/editors/tree-sitter-zane/queries/highlights.scm +++ b/editors/tree-sitter-zane/queries/highlights.scm @@ -22,6 +22,7 @@ "$" @operator "#" @operator "&" @operator +"^" @operator "@" @operator "!" @operator "?" @operator diff --git a/editors/typst/Zane.sublime-syntax b/editors/typst/Zane.sublime-syntax index 0ee60642..da772fea 100644 --- a/editors/typst/Zane.sublime-syntax +++ b/editors/typst/Zane.sublime-syntax @@ -120,6 +120,8 @@ contexts: scope: keyword.operator.zane - match: "\\&" scope: keyword.operator.zane + - match: "\\^" + scope: keyword.operator.zane - match: "@" scope: keyword.operator.zane - match: "!" diff --git a/grammar/lexicon.coda b/grammar/lexicon.coda index 6def88f1..e4341dec 100644 --- a/grammar/lexicon.coda +++ b/grammar/lexicon.coda @@ -295,6 +295,7 @@ literal_tokens [ DOLLAR $ operator keyword.operator HASH "#" operator keyword.operator AMPERSAND & operator keyword.operator + CARET ^ operator keyword.operator AT @ operator keyword.operator EXCL ! operator keyword.operator QSTNMARK ? operator keyword.operator diff --git a/grammar/syntax.mly b/grammar/syntax.mly index 3c607cc3..1c51104b 100644 --- a/grammar/syntax.mly +++ b/grammar/syntax.mly @@ -134,7 +134,10 @@ top_decl: type_ } | AMPERSAND type_=type_base { - type_expr $loc (Nodes.Type_expr.Guest type_) + type_expr $loc (Nodes.Type_expr.Reference type_) + } + | CARET type_=type_base { + type_expr $loc (Nodes.Type_expr.Roaming type_) } (* A suffix is parsed as its own bracket group and only then applied to the @@ -321,7 +324,13 @@ map_lit: ignore concept_; param_type $loc (Nodes.Param_type.InferredType - { name; concept = concept $loc(concept_) Nodes.Concept.Type }) + { name; concept = concept $loc(concept_) Nodes.Concept.Type; roaming = false }) + } + | CARET name=uname concept_=UTYPE { + ignore concept_; + param_type $loc + (Nodes.Param_type.InferredType + { name; concept = concept $loc(concept_) Nodes.Concept.Type; roaming = true }) } %inline constructor_field: diff --git a/lib/cgt/build.ml b/lib/cgt/build.ml index 99d8c520..ba75d362 100644 --- a/lib/cgt/build.ml +++ b/lib/cgt/build.ml @@ -20,7 +20,7 @@ let binop : Sst.Nodes.Operator.node -> Expr.binop = function | Eq -> Expr.Eq | Less -> Expr.Less -(* An exit ends the run of a block (docs/spec-divergences.md §11). Semantics +(* An exit ends the run of a block (docs/spec-divergences.md §10). Semantics rejects one anywhere else, so this is not reached. *) let no_block span = Diagnostic.bug ~span "lowering: an exit ends the block its call is written in, and this is in none" @@ -47,10 +47,10 @@ let arena st scope = scope.arena <- Some a; a -(* A new local: held in the block's arena when it is a host or owns a +(* A new local: held in the block's arena when it is an owner or owns a block. *) let bind st scope span t id value = - if held st span t then Stat.Host { id; scope = arena st scope; value; layout = layout st span t } + if held st span t then Stat.Hold { id; scope = arena st scope; value; layout = layout st span t } else Stat.Let { id; value } let bind_local st scope (l : T.Local.t) id value = @@ -65,14 +65,13 @@ let array_type t n = let is_array_lit (e : T.Expr.t) = match e.T.Expr.node with T.Expr.Array_lit _ -> true | _ -> false -(* A value of type [t] leaving the arenas an exit drains: a host, or a +(* A value of type [t] leaving the arenas an exit drains: an owner, or a value that owns a block, takes its blocks out of them first. *) let escape st span t exit (value : Expr.t) = if held st span t then { value with Expr.node = Expr.Escape { value; layout = layout st span t; exit } } else value let layout_table l = ptr (Expr.Layout l) -let resolve (tether : Expr.t) = ptr (Expr.Resolve tether) let local_ptr id = ptr (Expr.Local id) (* A storage primitive's operator. The scalars have the machine's own, and diff --git a/lib/cgt/lower.ml b/lib/cgt/lower.ml index 388c69cf..baa5c9e8 100644 --- a/lib/cgt/lower.ml +++ b/lib/cgt/lower.ml @@ -53,8 +53,8 @@ let rec expr st ctx (e : T.Expr.t) : Expr.t = | T.Expr.Call { callee = { owner = S.Intrinsic "@runtime$print"; _ }; args; handler = None } -> ( (* The program has one console (effects.md §6.6), so the subject names - nothing the runtime needs. The text is a guest to a place in this - frame, and the runtime reads the view stored there (L10). *) + nothing the runtime needs. The text is a borrowed value, which the + runtime reads where it is (L6). *) match args with | [ T.Arg.Value _console; T.Arg.Value text ] -> { @@ -96,11 +96,12 @@ let rec expr st ctx (e : T.Expr.t) : Expr.t = | T.Expr.Call { callee = { owner = S.Intrinsic "@primitives$push"; _ }; args; handler = None } -> ( (* The value is taken first, then the list makes room for it, which may - move its elements, and the value moves into the new last element. *) + move its elements' bytes, and the value moves into the new last + element. *) match args with | [ T.Arg.Value list; T.Arg.Value value ] -> let t = - match element (Tty.strip_guest list.T.Expr.ty) with + match element (Tty.strip_mode list.T.Expr.ty) with | Some t -> t | None -> Diagnostic.bug ~span "lowering: `push` onto something not a list" in @@ -112,10 +113,7 @@ let rec expr st ctx (e : T.Expr.t) : Expr.t = let l = layout st span t in let room = Expr.Runtime - { - fn = Runtime.List_push; - args = [ lend st ctx span list; int (stride st span t); layout_table l ]; - } + { fn = Runtime.List_push; args = [ lend st ctx span list; int (stride st span t) ] } in let body = [ @@ -136,7 +134,7 @@ let rec expr st ctx (e : T.Expr.t) : Expr.t = match args with | [ T.Arg.Value list ] -> let value = borrow st ctx span list in - { Expr.node = Expr.Member { value; index = 2 }; ty = Nodes.Ty.I64 } + { Expr.node = Expr.Member { value; index = 1 }; ty = Nodes.Ty.I64 } | _ -> Diagnostic.bug ~span "lowering: `size` with the wrong arguments") | T.Expr.Subscript _ -> ( match addr st ctx span e with @@ -176,7 +174,7 @@ let rec expr st ctx (e : T.Expr.t) : Expr.t = call st ctx span (verb_of st id instance) [ T.Arg.Value value ] handler e.T.Expr.ty | T.Expr.Field { target; slot; _ } -> let t = ty st span e.T.Expr.ty in - let owner = Tty.strip_guest target.T.Expr.ty in + let owner = Tty.strip_mode target.T.Expr.ty in let index = member_index st owner slot in if boxed st owner (field_type st span owner slot) then (* A boxed member's payload is in its block. A fresh value is held @@ -185,9 +183,10 @@ let rec expr st ctx (e : T.Expr.t) : Expr.t = match addr st ctx span e with | Some p -> { Expr.node = Expr.Deref p; ty = t } | None -> Diagnostic.bug ~span "lowering expected a boxed member to have a place" - else if Tty.is_guest target.T.Expr.ty then - (* Read through the guest, where its host is now (memory.md §4.4). *) - let base = resolve (expr st ctx target) in + else if Tty.is_ref target.T.Expr.ty then + (* Read through the reference, at the address of the owner it names + (memory.md §4.1). *) + let base = expr st ctx target in let within = ty st span owner in { Expr.node = Expr.Deref (ptr (Expr.Offset { base; within; path = [ index ] })); ty = t } else if collapsed st owner then { (borrow st ctx span target) with ty = t } @@ -221,15 +220,15 @@ let rec expr st ctx (e : T.Expr.t) : Expr.t = | Some _ -> { Expr.node = Expr.Deref (constant st decl); ty = ty st span e.T.Expr.ty } | None -> Diagnostic.bug ~span "lowering found no package constant here") | T.Expr.Call_value { callee; args; handler } -> ( - match Tty.strip_guest callee.T.Expr.ty with + match Tty.strip_mode callee.T.Expr.ty with | Tty.Verb fv -> let v = value_verb "value" fv (value_params span fv) { T.Block.stats = []; span } in let fn = value_of st ctx callee in invoke ~fn st ctx span v (arguments st ctx span v args) handler | _ -> Diagnostic.bug ~span "lowering expected a function value here") - (* `&` written before a place: a guest to it, minted, or a guest it holds - copied (memory.md §2.6). *) - | T.Expr.Ref value -> guest st ctx span value + (* `&` written before a place: a reference to it, minted, or a reference it + holds copied (memory.md §2.6). *) + | T.Expr.Ref value -> reference_to st ctx span value | T.Expr.Call { callee = { owner = S.Intrinsic spelling; _ }; _ } when String.starts_with ~prefix:"@controlflow$" spelling -> refuse span (Printf.sprintf "lowering does not handle `%s` used as a value yet" spelling) @@ -255,8 +254,9 @@ and primitive st ctx span spelling args (t : Tty.t) : Expr.t = (* A new list is empty, and owns no block until its first `push`. *) | "List", [ _ ] -> { Expr.node = Expr.Runtime { fn = Runtime.List_new; args = [] }; ty = Nodes.Ty.Handle } - | "Array", [ T.Arg.Value v ] -> ( - match ((literal_of ctx v).T.Expr.node, array_of (Tty.strip_guest t)) with + | "ArrayRef.fill", [ _; T.Arg.Value make ] -> fill st ctx span make t + | ("Array" | "ArrayRef"), [ T.Arg.Value v ] -> ( + match ((literal_of ctx v).T.Expr.node, array_of (Tty.strip_mode t)) with | T.Expr.Array_lit values, Some (e, n) when List.length values = n -> let element i v = (i, moved st ctx span e v) in { Expr.node = Expr.Record (List.mapi element values); ty = ty st span t } @@ -277,6 +277,58 @@ and primitive st ctx span spelling args (t : Tty.t) : Expr.t = | _, [ T.Arg.Value v ] -> literal ctx span name v | _ -> Diagnostic.bug ~span (Printf.sprintf "lowering: `%s` with the wrong arguments" spelling) +(* `ArrayRef.fill(n, make)` (generics.md §8.4): a slot this scope holds, + each element made by calling `make` with its position, counted from 1, in + order, and moved into place; then the whole array moves out of the slot, + which is spent, into wherever the call's value goes. *) +and fill st ctx span (make : T.Expr.t) (t : Tty.t) : Expr.t = + match (array_of (Tty.strip_mode t), make.T.Expr.ty) with + | Some (e, n), Tty.Verb fv -> + let arr = ty st span t in + let int k = { Expr.node = Expr.Int (Int64.of_int k); ty = Nodes.Ty.I64 } in + let f = fresh st and slot = fresh st and i = fresh st and made = fresh st in + let label = fresh st and result = fresh st in + let v = value_verb "value" fv (value_params span fv) { T.Block.stats = []; span } in + let fn = { Expr.node = Expr.Local f; ty = Nodes.Ty.Ptr } in + let index = { Expr.node = Expr.Local i; ty = Nodes.Ty.I64 } in + let call = invoke ~fn st ctx span v [ index ] None in + let at = + ptr + (Expr.Runtime + { + fn = Runtime.Array_at; + args = [ ptr (Expr.Address slot); index; int n; int (stride st span e) ]; + }) + in + let next = { Expr.node = Expr.Binary { op = Expr.Add; left = index; right = int 1 }; ty = Nodes.Ty.I64 } in + let whole = layout st span t in + let body = + [ + Stat.Let { id = f; value = value_of st ctx make }; + Stat.Reserve { id = slot; scope = arena st ctx.scope; ty = arr; layout = whole }; + Stat.Let { id = i; value = int 1 }; + Stat.Repeat + { + count = int n; + body = + [ + Stat.Let { id = made; value = call }; + Stat.Place + { + address = at; + value = { Expr.node = Expr.Local made; ty = call.Expr.ty }; + layout = layout st span e; + }; + Stat.assign i next; + ]; + }; + Stat.assign result + { Expr.node = Expr.Take { address = ptr (Expr.Address slot); layout = whole }; ty = arr }; + ] + in + { Expr.node = Expr.Expand { label; body; result = Some result }; ty = arr } + | _ -> Diagnostic.bug ~span "lowering: `ArrayRef.fill` with the wrong arguments" + (* L14. A lambda captures nothing (functions.md §7.4), so it is lifted out as a function of its own, once, and its value is that function's address. A call through it ends only the ways its type says, which is why a @@ -322,10 +374,6 @@ and record st ctx span t (fields : T.Field_value.t list) = in match (lowered, fields) with | Nodes.Ty.Void, [] -> { Expr.node = Expr.Unit; ty = Nodes.Ty.Void } - (* A reference type's instance starts untethered (memory.md §4.3). *) - | Nodes.Ty.Struct (bp :: rest), _ when reference st t && List.length rest = List.length fields -> - let untethered = (0, { Expr.node = Expr.Int 0L; ty = bp }) in - { Expr.node = Expr.Record (untethered :: members fields); ty = lowered } | Nodes.Ty.Struct ms, _ when List.length ms = List.length fields -> { Expr.node = Expr.Record (members fields); ty = lowered } | _, [ f ] when collapsed st t -> { (value f) with ty = lowered } @@ -335,31 +383,31 @@ and record st ctx span t (fields : T.Field_value.t list) = (* Storage, moves and borrows *) (* ---------------------------------------------------------------------- *) -(* A value read where its type's own storage is wanted: through a guest, the - host it names. *) +(* A value read where its type's own storage is wanted: through a reference, + the owner it names. *) and value_of st ctx (e : T.Expr.t) = - if Tty.is_guest e.T.Expr.ty then - let t = ty st e.T.Expr.span (Tty.strip_guest e.T.Expr.ty) in - { Expr.node = Expr.Deref (resolve (expr st ctx e)); ty = t } + if Tty.is_ref e.T.Expr.ty then + let t = ty st e.T.Expr.span (Tty.strip_mode e.T.Expr.ty) in + { Expr.node = Expr.Deref (expr st ctx e); ty = t } else read st ctx e -(* A value read out of its place. One reached through a host is read as a +(* A value read out of its place. One reached through an owner is read as a snapshot (concurrency.md §4.4). *) and read st ctx (e : T.Expr.t) = let span = e.T.Expr.span in - if through_host st e && not (reference st e.T.Expr.ty) then + if through_owner st e && not (reference st e.T.Expr.ty) then match addr st ctx span e with | Some p -> { Expr.node = Expr.Snapshot p; ty = ty st span e.T.Expr.ty } | None -> expr st ctx e else expr st ctx e -(* The address of the value a place holds (L10): through a guest, the host - it names now (memory.md §4.4); otherwise the place's own storage. *) +(* The address of the value a place holds (L10): through a reference, the + owner it names (memory.md §4.1); otherwise the place's own storage. *) and addr st ctx span (e : T.Expr.t) : Expr.t option = - if Tty.is_guest e.T.Expr.ty then Some (resolve (expr st ctx e)) else storage st ctx span e + if Tty.is_ref e.T.Expr.ty then Some (expr st ctx e) else storage st ctx span e (* The address of a place's own storage: a local's slot, a `mut` subject's - or swallowed argument's pointer, and a member of what any place holds. *) + or borrowed argument's pointer, and a member of what any place holds. *) and storage st ctx span (e : T.Expr.t) : Expr.t option = match e.T.Expr.node with | T.Expr.Var (T.Name_ref.Local l) -> ( @@ -369,7 +417,7 @@ and storage st ctx span (e : T.Expr.t) : Expr.t option = | Future { settle; at = Some p } -> Some (joined st (settle ()) p) | _ -> None) | T.Expr.Field { target; slot; _ } -> - let owner = Tty.strip_guest target.T.Expr.ty in + let owner = Tty.strip_mode target.T.Expr.ty in let base = match addr st ctx span target with | Some base -> Some base @@ -391,42 +439,43 @@ and storage st ctx span (e : T.Expr.t) : Expr.t option = | Some _ -> Some (constant st decl)) (* What the program has, such as `@program$console`, holds nothing a program reads (effects.md §6.6), so its place is a variable of the - program's that holds only its backpointer, which a guest to it is - anchored at. *) + program's that holds nothing, whose address a reference to it holds. *) | T.Expr.Var (T.Name_ref.Intrinsic name) -> let symbol = "zane.value." ^ name in if not (List.exists (fun (g : Global.t) -> g.Global.symbol = symbol) st.globals) then - st.globals <- { Global.symbol; linkage = Linkage.Local; ty = Nodes.Ty.I32 } :: st.globals; + st.globals <- { Global.symbol; linkage = Linkage.Local; ty = Nodes.Ty.I64 } :: st.globals; Some (ptr (Expr.Global symbol)) | T.Expr.Subscript { target; impl; args } -> Some (subscript st ctx span target impl args) | T.Expr.Case_read { target; case; handler } when held st span e.T.Expr.ty -> Some (case_place st ctx span target case handler e.T.Expr.ty) | _ -> None -(* A guest to a place, minted, or an existing guest copied as the identity - it ends at (memory.md §2.6, §4.4). *) -and guest st ctx span (e : T.Expr.t) = - if Tty.is_guest e.T.Expr.ty then { Expr.node = Expr.Terminal (expr st ctx e); ty = Nodes.Ty.I32 } +(* A reference to a place, minted as the place's address, or an existing + reference copied (memory.md §2.6, §4.1). The semantic pass let only a + settled place mint one, and a settled owner never moves, so the address + holds until the owner's scope drains. *) +and reference_to st ctx span (e : T.Expr.t) = + if Tty.is_ref e.T.Expr.ty then expr st ctx e else match addr st ctx span e with - | Some p -> { Expr.node = Expr.Mint p; ty = Nodes.Ty.I32 } - | None -> Diagnostic.bug ~span "lowering expected a guest source here" + | Some p -> p + | None -> Diagnostic.bug ~span "lowering expected a reference source here" -(* A value on its way into storage of type [dst]: a guest there is minted - or copied, and a reference-type host read from a place moves, which - spends the place (memory.md §3.7). *) +(* A value on its way into storage of type [dst]: a reference there is + minted or copied, and a reference-type owner read from a place moves, + which spends the place (lifetimes.md §1.6). *) and moved st ctx span dst (e : T.Expr.t) = - if Tty.is_guest dst then guest st ctx span e - else if hosted st dst && not (Tty.is_guest e.T.Expr.ty) then + if Tty.is_ref dst then reference_to st ctx span e + else if owned st dst && not (Tty.is_ref e.T.Expr.ty) then match e.T.Expr.node with | T.Expr.Var _ | T.Expr.Field _ -> ( match addr st ctx span e with | Some address -> let l = layout st span dst in { Expr.node = Expr.Take { address; layout = l }; ty = ty st span dst } - (* Semantics moves a host only out of a symbol (moves.ml), which is - a place. *) - | None -> Diagnostic.bug ~span "lowering expected a host to move out of a place") + (* Semantics moves an owner only out of a roaming symbol or a field + of one (moves.ml), which is a place. *) + | None -> Diagnostic.bug ~span "lowering expected an owner to move out of a place") | _ -> expr st ctx e else if owns st span dst && is_place e then let value = value_of st ctx e in @@ -437,14 +486,14 @@ and moved st ctx span dst (e : T.Expr.t) = making a fresh one: a place, a member of any value, which a fresh value is borrowed for, an element, or what a case read gives. *) and is_place (e : T.Expr.t) = - Tty.is_guest e.T.Expr.ty + Tty.is_ref e.T.Expr.ty || match e.T.Expr.node with | T.Expr.Var _ | T.Expr.Field _ | T.Expr.Subscript _ | T.Expr.Case_read _ -> true | _ -> false (* A value read where it is only looked at, as a value parameter or an - operand: a place is read where it is, and a fresh host, or a fresh value + operand: a place is read where it is, and a fresh owner, or a fresh value that owns a block, is held in this scope first, so the scope's drain ends it and returns its blocks. *) and borrow st ctx span (e : T.Expr.t) = @@ -464,7 +513,7 @@ and borrow st ctx span (e : T.Expr.t) = else value_of st ctx e (* The address a callee is lent (L6): the caller's place, or a fresh value - stored here first, which this scope then hosts. *) + stored here first, which this scope then holds. *) and lend st ctx span (a : T.Expr.t) = match addr st ctx span a with | Some p -> p @@ -495,7 +544,7 @@ and match_ st ctx span scrutinees (arms : T.Arm.t list) handler ret = let stored = List.map (fun (e : T.Expr.t) -> - let tsty = Tty.strip_guest e.T.Expr.ty in + let tsty = Tty.strip_mode e.T.Expr.ty in let within = ty st span tsty in let lets, pointer = match addr st ctx span e with @@ -506,7 +555,7 @@ and match_ st ctx span scrutinees (arms : T.Arm.t list) handler ret = let value = value_of st ctx e in let id = fresh st in let pointer = fresh st in - (* A fresh instance is hosted here, like any other. *) + (* A fresh instance is held here, like any other. *) let at = Stat.Let { id = pointer; value = ptr (Expr.Address id) } in ([ bind st ctx.scope span tsty id value; at ], pointer) in @@ -532,7 +581,7 @@ and match_ st ctx span scrutinees (arms : T.Arm.t list) handler ret = | None -> [] | Some l -> let index = case_index st span tsty p.case in - let path = (if reference st tsty then [ 1 ] else []) @ [ index ] in + let path = [ index ] in let within = ty st span tsty in let at = ptr (Expr.Offset { base = local_ptr pointer; within; path }) in (* A boxed payload is in its block. *) @@ -597,7 +646,7 @@ and map_read st ctx span target map ret = (* A handler, run where its operation aborted: its binder holds the abort value, and a `resolve` gives the operation its value and goes past it. *) (* A handler is a block of its own, so an abort value of a reference type - is hosted in the handler's arena, which is innermost wherever the handler + is held in the handler's arena, which is innermost wherever the handler runs. *) and handle ?resolve ?dropped st ctx (h : T.Handler.t) label result span (value : Expr.t) = let scope = { arena = None; settles = [] } in @@ -608,7 +657,7 @@ and handle ?resolve ?dropped st ctx (h : T.Handler.t) label result span (value : Hashtbl.replace ctx.env l.T.Local.id (Slot id); [ bind_local st scope l id value ] (* An abort value no binder names, of type [dropped], is still held - here when it is a host or owns a block, so the drain ends it. *) + here when it is an owner or owns a block, so the drain ends it. *) | None, Some t when held st span t -> [ bind st scope span t (fresh st) value ] | None, _ -> [ Stat.Eval value ] in @@ -633,7 +682,7 @@ and case_read st ctx span (target : T.Expr.t) case handler ret = let value = borrow st ctx span target in let id = fresh st in let live = case_index st span target.T.Expr.ty case in - let tsty = Tty.strip_guest target.T.Expr.ty in + let tsty = Tty.strip_mode target.T.Expr.ty in let source = sum_of st span tsty { Expr.node = Expr.Local id; ty = value.Expr.ty } in let payload i = { Expr.node = Expr.Payload { value = source; index = i }; ty = t } in let cases = @@ -645,14 +694,14 @@ and case_read st ctx span (target : T.Expr.t) case handler ret = let body = [ Stat.Let { id; value }; Stat.Switch { value = source; cases } ] @ otherwise in { Expr.node = Expr.Expand { label; body; result }; ty = t } -(* A case read of a host, or of a value that owns a block, as a place: the +(* A case read of an owner, or of a value that owns a block, as a place: the payload where the variant holds it when the case is live, or else a slot reserved in this scope before the read, which the handler's `resolve` fills. Either way what the read gives has one owner, and a copy of it owns blocks of its own. *) and case_place st ctx span (target : T.Expr.t) case handler ret = let t = ty st span ret in - let tsty = Tty.strip_guest target.T.Expr.ty in + let tsty = Tty.strip_mode target.T.Expr.ty in let within = ty st span tsty in let label = fresh st in let result = fresh st in @@ -662,7 +711,7 @@ and case_place st ctx span (target : T.Expr.t) case handler ret = let live = case_index st span tsty case in let whole = { Expr.node = Expr.Deref (local_ptr base); ty = within } in let payload = - let path = (if reference st tsty then [ 1 ] else []) @ [ live ] in + let path = [ live ] in let at = ptr (Expr.Offset { base = local_ptr base; within; path }) in if boxed st tsty ret then ptr (Expr.Deref at) else at in @@ -700,7 +749,7 @@ and subscript st ctx span (target : T.Expr.t) (impl : T.Verb_ref.t) args = let int n = { Expr.node = Expr.Int (Int64.of_int n); ty = Nodes.Ty.I64 } in match (impl.owner, args) with | S.Intrinsic "@primitives$[]", [ index ] -> ( - match (element (Tty.strip_guest target.T.Expr.ty), array_of (Tty.strip_guest target.T.Expr.ty)) with + match (element (Tty.strip_mode target.T.Expr.ty), array_of (Tty.strip_mode target.T.Expr.ty)) with | Some t, _ -> ptr (Expr.Runtime @@ -888,7 +937,7 @@ and arguments st ctx span v args = A call that can abort or exit settles on this thread, once, where it is first read, or where the block ends when nothing reads it first - (docs/spec-divergences.md §13). An abort runs the handler written at the + (docs/spec-divergences.md §12). An abort runs the handler written at the spawn (§3.3), or goes where an abort from here goes, and its `resolve` gives the result; an exit ends the run of the block the spawn is in. *) and spawn st ctx span (e : T.Expr.t) = @@ -906,7 +955,7 @@ and spawn st ctx span (e : T.Expr.t) = let v, args = wrapped st span callee args e.T.Expr.ty in spawn_call st ctx span v None ~writes:false args handler | T.Expr.Call_value { callee; args; handler } -> ( - match Tty.strip_guest callee.T.Expr.ty with + match Tty.strip_mode callee.T.Expr.ty with | Tty.Verb fv -> let v = value_verb "value" fv (value_params span fv) { T.Block.stats = []; span } in let fn = value_of st ctx callee in @@ -1020,7 +1069,7 @@ and spawn_call st ctx span v fn ~writes passed handler = let o = outcome st span v in let whole = returned o in let args = arguments st ctx span v passed in - (* A `mut` subject reached through a host is copied into the frame, + (* A `mut` subject reached through an owner is copied into the frame, and the call works on the copy, which it writes back when it returns (§4.4). A function value's subject is lent by its address (L14), so one it may not write is read into the frame here, where @@ -1030,8 +1079,8 @@ and spawn_call st ctx span v fn ~writes passed handler = | this :: _, T.Arg.Value subject :: _ when by_address st v this && this.T.Local.name = "this" && (not (reference st this.T.Local.ty)) - && (through_host st subject || not writes) -> - Some (this.T.Local.ty, through_host st subject) + && (through_owner st subject || not writes) -> + Some (this.T.Local.ty, through_owner st subject) | _ -> None in let pre, args = @@ -1165,12 +1214,13 @@ and spawn_call st ctx span v fn ~writes passed handler = (* Calls and expansion *) (* ---------------------------------------------------------------------- *) -(* An argument as the callee takes it (L6): a place it may write or take - the host from is lent by its address, a guest is minted or copied, and - a value is borrowed. *) +(* An argument as the callee takes it (L6): a place it may write, or a + borrowed owner, is lent by its address, a reference is minted or copied, + a `^T` argument is moved, and a value is borrowed. *) and argument st ctx span v (p : T.Local.t) a = if by_address st v p then lend st ctx span a - else if Tty.is_guest p.T.Local.ty then guest st ctx span a + else if Tty.is_ref p.T.Local.ty then reference_to st ctx span a + else if roaming st p.T.Local.ty then moved st ctx span p.T.Local.ty a else borrow st ctx span a (* L12. A call that can end more than one way is switched on how it ended: @@ -1210,9 +1260,10 @@ and invoke ?fn st ctx span v args handler = { Expr.node = Expr.Expand { label; body; result }; ty = o.ok } (* L11. A parameter is bound to what it stands for in the body: a block - argument to its code, a literal to itself, the subject or a swallowed - host to the caller's place, and any other argument to a new slot holding - its value (L6). *) + argument to its code, a literal to itself, the subject or a borrowed + owner to the caller's place, a `^T` argument to a slot this scope holds + the moved value in, and any other argument to a new slot holding its + value (L6). *) and expand st ctx span v args handler ret = if List.mem v.decl ctx.expanding then Diagnostic.bug ~span (Printf.sprintf "lowering: `%s` expands into itself" v.signature.S.name); @@ -1248,7 +1299,7 @@ and expand st ctx span v args handler ret = bind p (Slot id); if held st span arr then let layout = layout st span arr in - [ Stat.Host { id; scope = arena st ctx.scope; value; layout } ] + [ Stat.Hold { id; scope = arena st ctx.scope; value; layout } ] else [ Stat.Let { id; value } ] (* A literal stands for itself. A spawned call's array literal is a value in a slot by now (specialized), which the parameter @@ -1269,7 +1320,8 @@ and expand st ctx span v args handler ret = []) | ( T.Arg.Value { T.Expr.node = T.Expr.Var (T.Name_ref.Local l); T.Expr.ty = at; _ }, _ ) - when (p.T.Local.name = "this" || hosted st p.T.Local.ty) && not (Tty.is_guest at) -> ( + when (p.T.Local.name = "this" || (owned st p.T.Local.ty && not (roaming st p.T.Local.ty))) + && not (Tty.is_ref at) -> ( match lookup ctx span l with | (Slot _ | Pointer _) as b -> bind p b; @@ -1279,16 +1331,24 @@ and expand st ctx span v args handler ret = bind p (Pointer id); settle () @ [ Stat.Let { id; value = at } ] | _ -> Diagnostic.bug ~span "lowering expected a place here") - (* Any other place the body may write, or take the host from, is - lent by its address. *) - | T.Arg.Value a, _ when p.T.Local.name = "this" || hosted st p.T.Local.ty -> + (* Any other place the body may write, or that it borrows, is lent + by its address. *) + | T.Arg.Value a, _ + when p.T.Local.name = "this" || (owned st p.T.Local.ty && not (roaming st p.T.Local.ty)) -> let value = lend st ctx span a in let id = fresh st in bind p (Pointer id); [ Stat.Let { id; value } ] + (* A taken owner is moved into a slot of its own, which this scope + holds, so it dies with the scope unless the body moves it on. *) + | T.Arg.Value a, _ when roaming st p.T.Local.ty -> + let value = moved st ctx span p.T.Local.ty a in + let id = fresh st in + bind p (Slot id); + [ Build.bind st ctx.scope span p.T.Local.ty id value ] | T.Arg.Value a, _ -> let value = - if Tty.is_guest p.T.Local.ty then guest st ctx span a else borrow st ctx span a + if Tty.is_ref p.T.Local.ty then reference_to st ctx span a else borrow st ctx span a in let id = fresh st in bind p (Slot id); @@ -1327,17 +1387,20 @@ and expand st ctx span v args handler ret = (* Blocks, statements and places *) (* ---------------------------------------------------------------------- *) -(* L8: a block that hosts a reference-type local has an arena of its own. *) -and block st ctx (b : T.Block.t) = +(* L8: a block that holds a reference-type local has an arena of its own. + [enter] runs first, in the block's scope: a function's `^T` parameters + are held there, so the body's drain ends any it did not move on. *) +and block ?(enter = fun _ -> []) st ctx (b : T.Block.t) = let scope = { arena = None; settles = [] } in - let body = List.concat_map (stat st { ctx with scope }) b.T.Block.stats in + let entered = enter scope in + let body = entered @ List.concat_map (stat st { ctx with scope }) b.T.Block.stats in (* A spawned call that can abort or exit, and that nothing has read, settles where the block ends. *) let body = body @ List.concat_map (fun settle -> settle ()) (List.rev scope.settles) in match scope.arena with None -> body | Some id -> [ Stat.Scope { id; body } ] (* A block argument's code, where it was written. An exit ends this run of - it (docs/spec-divergences.md §11). *) + it (docs/spec-divergences.md §10). *) and code st ctx span (arg : T.Arg.t) = let run ctx b = let label = fresh st in @@ -1387,15 +1450,12 @@ and stat st ctx (s : T.Stat.t) : Stat.t list = | None -> refuse span "lowering does not store into this place yet" in if held st span t then - (* A host replaced in place keeps, merges or floats its identities - (memory.md §4.5), and what it replaces returns its blocks. An - element is a contingent place (§2.2). *) + (* An owner, or a value that owns blocks, is replaced in place + (memory.md §2.2): at the same address, each boxed member's block + kept, and the rest of what it replaces returning its blocks. *) let address = address () in let value = moved st ctx span t value in - let contingent = - match target.T.Expr.node with T.Expr.Subscript _ -> true | _ -> false - in - [ Stat.Overwrite { address; value; layout = layout st span t; contingent } ] + [ Stat.Overwrite { address; value; layout = layout st span t } ] else match place st ctx span target with | Some place -> [ Stat.Assign { place; value = moved st ctx span t value } ] @@ -1436,8 +1496,8 @@ and stat st ctx (s : T.Stat.t) : Stat.t list = (* Where an assignment stores: a local, or the place a `mut` subject points at, and the members below it. A struct of one member adds no step. A - place reached through a guest has no local to start from, and is stored - through its address instead. *) + place reached through a reference has no local to start from, and is + stored through its address instead. *) and place st ctx span (target : T.Expr.t) : Expr.place option = match target.T.Expr.node with | T.Expr.Var (T.Name_ref.Local l) -> ( @@ -1449,7 +1509,7 @@ and place st ctx span (target : T.Expr.t) : Expr.place option = call has returned. *) | Future _ -> None | _ -> Diagnostic.bug ~span "lowering expected a place here") - | T.Expr.Field { target = inner; _ } when Tty.is_guest inner.T.Expr.ty -> None + | T.Expr.Field { target = inner; _ } when Tty.is_ref inner.T.Expr.ty -> None | T.Expr.Field { target = inner; slot; _ } -> Option.map (fun (p : Expr.place) -> diff --git a/lib/cgt/nodes.ml b/lib/cgt/nodes.ml index ce848a18..62e63947 100644 --- a/lib/cgt/nodes.ml +++ b/lib/cgt/nodes.ml @@ -5,21 +5,20 @@ be understood. It grows with each step of docs/design/lowering.md §8; what is here is what lowering handles so far. *) -(* A CGT type is a machine layout (L5). [Handle] is `@primitives$String`, - the string view (types.md §2.7), and `@primitives$List`: reference - types whose instance is a backpointer and a handle (memory.md §3.6), a +(* A CGT type is a machine layout (L5). [Handle] is `@primitives$String` + (types.md §2.7) and `@primitives$List`: a handle (memory.md §3.6), a pointer to the first byte or element, the length in bytes or elements, with no terminator, and the room in bytes of the block they are in, which is 0 when the handle owns none: a literal's bytes are the module's own. [Void] is `Unit`, which has no storage. [Array] is `@primitives$Array`: [n] elements inline, each at the element's stride, its size rounded up to its alignment (L5). - [Struct] is a value struct's members in declaration order, [Sum] a value - variant's or enum's cases: a tag and room for the widest payload. [Ptr] - is the address of a place, which is how a `mut` subject is passed (L6). - [I32] is `@primitives$I32`, and a reference-type instance's backpointer - and a guest's tether, each an anchor's identity (memory.md §4.2). A boxed member is a [Ptr] to - its payload's block (adt.md §4). *) + [Struct] is a struct's members in declaration order, [Sum] a variant's or + enum's cases: a tag and room for the widest payload. [Ptr] is the address + of a place, which is how a `mut` subject and a borrow are passed (L6), + and what a reference holds: the address of the settled owner it names + (memory.md §4.1). [I32] is `@primitives$I32`. A boxed member is a [Ptr] + to its payload's block (adt.md §4). *) module Ty = struct type t = | Void @@ -41,7 +40,7 @@ module Ty = struct | I1 -> (1, 1) | I32 -> (4, 4) | I64 | F64 | Ptr -> (8, 8) - | Handle -> (32, 8) + | Handle -> (24, 8) | Struct ts -> let size, align = List.fold_left @@ -88,10 +87,8 @@ module Ty = struct | Array (t, n) -> Printf.sprintf "[%d x %s]" n (to_string t) end -(* Where a type's hosts and owned blocks are (memory.md §3.6, §4.5): the - instance itself when it is a reference type, every reference-type host - inside it, outermost first, and every handle and boxed member, each of - which may own a dynamic block. A list's block holds elements laid out as +(* Where a type's owned blocks are (memory.md §3.6): every handle and boxed + member, outermost first, each of which may own a dynamic block. A list's block holds elements laid out as [elements] says, [stride] bytes apart, and a box's holds one payload of [size] bytes laid out as [payload] says. A position inside a variant payload is there only while each of [tags] -- a tag's offset and the case @@ -99,7 +96,6 @@ end the program lists each one once, so a layout may name itself. *) module Layout = struct type kind = - | Host | Text | List of { stride : int; elements : string } | Box of { size : int; payload : string } @@ -151,13 +147,8 @@ module Expr = struct (* The address of a member inside the place an address names, down [path] through the struct type [within]. *) | Offset of { base : t; within : Ty.t; path : int list } - (* A guest's tether minted from a host's address, the address a tether - resolves to, and the identity it ends at (L9). *) - | Mint of t - | Resolve of t - | Terminal of t (* A move out of the place an address names: its value, and the place - is spent (memory.md §3.7). *) + is spent (lifetimes.md §1.6). *) | Take of { address : t; layout : Layout.t } (* A value copied whole: every block it owns is copied too, so the copy owns blocks of its own (memory.md §2.3). *) @@ -171,7 +162,7 @@ module Expr = struct | Function of string (* The address of a variable of the program, by its symbol. *) | Global of string - (* The value at an address reached through a host, read as a coherent + (* The value at an address reached through an owner, read as a coherent snapshot: retried while a spawned call writes back there (concurrency.md §4.4). *) | Snapshot of t @@ -192,11 +183,11 @@ module Expr = struct [Repeat] are `@controlflow$branch` and `@controlflow$repeat`, [Switch] jumps on a sum's tag (L13), and [Leave] ends the [Expand] its label names. [Scope] is a block's arena (L8), entered before its body and - drained on every way out of it, and [Host] fills a new local whose slot + drained on every way out of it, and [Hold] fills a new local whose slot is in that arena. *) and stat = | Let of { id : int; value : t } - | Host of { id : int; scope : int; value : t; layout : Layout.t } + | Hold of { id : int; scope : int; value : t; layout : Layout.t } | Scope of { id : int; body : stat list } | Assign of { place : place; value : t } | Eval of t @@ -205,16 +196,15 @@ module Expr = struct | Repeat of { count : t; body : stat list } | Switch of { value : t; cases : (int * stat list) list } | Leave of int - (* A store through an address, and a value replaced there: a host's - identities kept, merged or floated (memory.md §4.5), the blocks the - old value owned returned, and the new one's moved into the place's - region. A contingent place -- a list's element - -- keeps none of its identities, and an anchored occupant floats. *) + (* A store through an address, and a value replaced there in place + (memory.md §2.2): written at the occupant's address, each boxed + member's block kept, the other blocks the old value owned returned, + and the new one's arriving in the place's region. *) | Store of { address : t; value : t } - | Overwrite of { address : t; value : t; layout : Layout.t; contingent : bool } - (* A value moved into a fresh place at an address: stored, the anchors - it carries follow it there, and the blocks it owns move into the - region of the scope that holds the place. *) + | Overwrite of { address : t; value : t; layout : Layout.t } + (* A value moved into a fresh place at an address: stored, and the + blocks it owns that the place's region outlives move into it, which is + an escape (memory.md §3.5). *) | Place of { address : t; value : t; layout : Layout.t } (* A slot of type [ty] in a scope's arena, zeroed so that it holds nothing until a [Place] fills it, which the drain then ends. *) @@ -260,9 +250,6 @@ module Expr = struct | Case _ -> "Case" | Payload _ -> "Payload" | Offset _ -> "Offset" - | Mint _ -> "Mint" - | Resolve _ -> "Resolve" - | Terminal _ -> "Terminal" | Take _ -> "Take" | Copy _ -> "Copy" | Box _ -> "Box" @@ -283,7 +270,7 @@ end module Stat = struct type t = Expr.stat = | Let of { id : int; value : Expr.t } - | Host of { id : int; scope : int; value : Expr.t; layout : Layout.t } + | Hold of { id : int; scope : int; value : Expr.t; layout : Layout.t } | Scope of { id : int; body : t list } | Assign of { place : Expr.place; value : Expr.t } | Eval of Expr.t @@ -293,7 +280,7 @@ module Stat = struct | Switch of { value : Expr.t; cases : (int * t list) list } | Leave of int | Store of { address : Expr.t; value : Expr.t } - | Overwrite of { address : Expr.t; value : Expr.t; layout : Layout.t; contingent : bool } + | Overwrite of { address : Expr.t; value : Expr.t; layout : Layout.t } | Place of { address : Expr.t; value : Expr.t; layout : Layout.t } | Reserve of { id : int; scope : int; ty : Ty.t; layout : Layout.t } | Spawn of { diff --git a/lib/cgt/outcome.ml b/lib/cgt/outcome.ml index 125c272f..d9ffbcb2 100644 --- a/lib/cgt/outcome.ml +++ b/lib/cgt/outcome.ml @@ -35,7 +35,7 @@ let exited = 2 let outcome_case o index (payload : Expr.t) = { Expr.node = Expr.Case { index; payload }; ty = returned o } -(* Where the hosts and blocks of a call's outcome are, when it can end more +(* Where the blocks of a call's outcome are, when it can end more than one way: the result's under the done tag, and the abort value's under the aborted one. *) let outcome_layout st span (v : verb) = diff --git a/lib/cgt/program.ml b/lib/cgt/program.ml index db881dd8..5759448f 100644 --- a/lib/cgt/program.ml +++ b/lib/cgt/program.ml @@ -32,6 +32,21 @@ let func st (v : verb) : Func.t = end) v.params in + (* A `^T` parameter is an owner of the body (lifetimes.md §1.5): it is held + in the body's own arena, so the body's drain ends it unless the body + moved it on. *) + let enter scope = + List.concat + (List.map2 + (fun (p : T.Local.t) (id, t) -> + if roaming st p.T.Local.ty && held st p.T.Local.span p.T.Local.ty then begin + let kept = fresh st in + Hashtbl.replace env p.T.Local.id (Slot kept); + [ bind_local st scope p kept { Expr.node = Expr.Local id; ty = t } ] + end + else []) + v.params params) + in List.iter (fun (id, lit) -> Hashtbl.replace env id (Literal lit)) v.literals; let o = outcome st span v in let linkage = @@ -69,7 +84,7 @@ let func st (v : verb) : Func.t = scope = { arena = None; settles = [] }; } in - { Func.symbol = symbol st v; linkage; params; ret = returned o; body = block st ctx v.body } + { Func.symbol = symbol st v; linkage; params; ret = returned o; body = block ~enter st ctx v.body } end (* L16. The function that makes a package constant: the first call makes diff --git a/lib/cgt/runtime.ml b/lib/cgt/runtime.ml index 72f1933e..722b1f21 100644 --- a/lib/cgt/runtime.ml +++ b/lib/cgt/runtime.ml @@ -11,9 +11,6 @@ type fn = | Divide_by_zero | Scope_enter | Slot - | Mint - | Resolve - | Terminal | Promote | Arrive | Vacate @@ -35,7 +32,7 @@ type fn = | Constant_end | Writeback -let all = [ Print; Text_join; Text_equal; Divide_by_zero; Scope_enter; Slot; Mint; Resolve; Terminal; Promote; Arrive; Vacate; Copy; Overwrite; Box; List_new; List_push; List_at; Array_at; Scope_drain; Frame; Spawn; Join; Set_threads; Set_threads_auto; Snapshot; Constant_begin; Constant_end; Writeback ] +let all = [ Print; Text_join; Text_equal; Divide_by_zero; Scope_enter; Slot; Promote; Arrive; Vacate; Copy; Overwrite; Box; List_new; List_push; List_at; Array_at; Scope_drain; Frame; Spawn; Join; Set_threads; Set_threads_auto; Snapshot; Constant_begin; Constant_end; Writeback ] let name = function | Print -> "zane_print" @@ -44,9 +41,6 @@ let name = function | Divide_by_zero -> "zane_divide_by_zero" | Scope_enter -> "zane_scope_enter" | Slot -> "zane_slot" - | Mint -> "zane_mint" - | Resolve -> "zane_resolve" - | Terminal -> "zane_terminal" | Promote -> "zane_promote" | Arrive -> "zane_arrive" | Vacate -> "zane_vacate" @@ -68,8 +62,8 @@ let name = function | Constant_end -> "zane_constant_end" | Writeback -> "zane_writeback" -(* A value as the C ABI passes it: [I32] is a guest's tether (`uint32_t`), - [I64] an `int64_t`, and [Ptr] any pointer, a function's included. *) +(* A value as the C ABI passes it: [I32] a `uint32_t`, [I64] an `int64_t`, + and [Ptr] any pointer, a function's included. *) type ty = Void | I32 | I64 | Ptr (* The return type, then the parameters. *) @@ -80,17 +74,14 @@ let signature = function | Divide_by_zero -> (Void, [ ]) | Scope_enter -> (I64, [ ]) | Slot -> (Ptr, [ I64; I64; I64; Ptr ]) - | Mint -> (I32, [ Ptr ]) - | Resolve -> (Ptr, [ I32 ]) - | Terminal -> (I32, [ I32 ]) | Promote -> (Void, [ Ptr; Ptr; I64 ]) | Arrive -> (Void, [ Ptr; Ptr ]) | Vacate -> (Void, [ Ptr; Ptr ]) | Copy -> (Void, [ Ptr; Ptr ]) - | Overwrite -> (Void, [ Ptr; Ptr; I64; Ptr; I64 ]) + | Overwrite -> (Void, [ Ptr; Ptr; I64; Ptr ]) | Box -> (Ptr, [ I64; I64 ]) | List_new -> (Void, [ Ptr ]) - | List_push -> (Ptr, [ Ptr; I64; Ptr ]) + | List_push -> (Ptr, [ Ptr; I64 ]) | List_at -> (Ptr, [ Ptr; I64; I64 ]) | Array_at -> (Ptr, [ Ptr; I64; I64; I64 ]) | Scope_drain -> (Void, [ I64 ]) diff --git a/lib/cgt/state.ml b/lib/cgt/state.ml index d3fe1671..b31fd7c3 100644 --- a/lib/cgt/state.ml +++ b/lib/cgt/state.ml @@ -63,17 +63,17 @@ and ctx = { (* What `@controlflow$exitFromCall` does here: end the invocation that called the verb whose body it is in. *) exit_call : Source.Span.t -> Stat.t list; - (* The block being lowered, which hosts its reference-type locals (L8). *) + (* The block being lowered, which holds its reference-type locals (L8). *) scope : scope; } -(* A block's arena, made the first time the block hosts something, and what +(* A block's arena, made the first time the block holds something, and what settles each call spawned in it that can abort or exit, in case nothing reads it first. *) and scope = { mutable arena : int option; mutable settles : (unit -> Stat.t list) list } (* A `return` from an expansion stores into [result], which has the TST type - [ret]: a value moves there, or a guest is minted, as into any storage. *) + [ret]: a value moves there, or a reference is minted, as into any storage. *) and exit = Function | Leave of { label : int; result : int option; ret : Tty.t } (* A package constant: its symbol, its package, its declared type and its diff --git a/lib/cgt/symbol.ml b/lib/cgt/symbol.ml index b436022b..52936c15 100644 --- a/lib/cgt/symbol.ml +++ b/lib/cgt/symbol.ml @@ -29,7 +29,8 @@ let rec ty_ stamp = function | Tty.Named ({ Tty.package; name }, args) -> stamp package ^ package ^ "$" ^ name ^ args_ stamp args | Tty.Intrinsic { namespace = ns; name; args } -> namespace ns ^ "$" ^ name ^ args_ stamp args - | Tty.Guest t -> "&" ^ ty_ stamp t + | Tty.Reference t -> "&" ^ ty_ stamp t + | Tty.Roaming t -> "^" ^ ty_ stamp t | Tty.Concept c -> concept stamp c | Tty.Verb v -> verb_type stamp v | Tty.Param p -> Diagnostic.bug ("Symbol.ty: the type parameter " ^ p.Tty.name ^ " is not concrete") diff --git a/lib/cgt/to_tree_graph.ml b/lib/cgt/to_tree_graph.ml index a5cb38d7..6db7f5cc 100644 --- a/lib/cgt/to_tree_graph.ml +++ b/lib/cgt/to_tree_graph.ml @@ -4,8 +4,8 @@ open Tree_graph open Nodes -(* A layout, one position per line: a host as `@8 (16 bytes) when [0]=1`, - a string's handle as `text @8`, a list's as `list @8 of T (16 bytes +(* A layout, one position per line: a string's handle as `text @8 when + [0]=1`, a list's as `list @8 of T (16 bytes apart)`, and a boxed member as `box @8 of T (24 bytes)`. *) let positions (ps : Layout.position list) = map_seq @@ -18,7 +18,6 @@ let positions (ps : Layout.position list) = " when " ^ String.concat ", " (List.map tag ts) in match p.kind with - | Layout.Host -> Leaf (Printf.sprintf "@%d (%d bytes)%s" p.offset p.size tags) | Layout.Text -> Leaf (Printf.sprintf "text @%d%s" p.offset tags) | Layout.List { stride; elements } -> Leaf (Printf.sprintf "list @%d of %s (%d bytes apart)%s" p.offset elements stride tags) @@ -87,9 +86,6 @@ let rec expr (e : Expr.t) = ("path", Leaf (String.concat "." (List.map string_of_int path))); ("base", expr base); ]) - | Expr.Mint p -> group "mint" (expr p) - | Expr.Resolve t -> group "resolve" (expr t) - | Expr.Terminal t -> group "terminal" (expr t) | Expr.Take { address; layout = l } -> group "take" (fields @@ -144,8 +140,8 @@ let rec expr (e : Expr.t) = and stat = function | Stat.Let { id; value } -> group "let" (fields [ ("local", Leaf (Printf.sprintf "#%d" id)); ("value", expr value) ]) - | Stat.Host { id; scope; value; layout = l } -> - group "host" + | Stat.Hold { id; scope; value; layout = l } -> + group "hold" (fields ([ ("local", Leaf (Printf.sprintf "#%d" id)); @@ -155,8 +151,8 @@ and stat = function @ [ ("layout", layout l) ])) | Stat.Store { address; value } -> group "store" (fields [ ("address", expr address); ("value", expr value) ]) - | Stat.Overwrite { address; value; layout = l; contingent } -> - group (if contingent then "overwrite contingent" else "overwrite") + | Stat.Overwrite { address; value; layout = l } -> + group "overwrite" (fields [ ("address", expr address); ("value", expr value); ("layout", layout l) ]) | Stat.Place { address; value; layout = l } -> group "place" diff --git a/lib/cgt/type_layout.ml b/lib/cgt/type_layout.ml index ffdbaf3f..22352147 100644 --- a/lib/cgt/type_layout.ml +++ b/lib/cgt/type_layout.ml @@ -1,6 +1,6 @@ (* How a TST type becomes a CGT layout (docs/design/lowering.md L5): a declared type's definition, its members and payloads, its machine layout, - and where its hosts and owned blocks are. *) + and where its owned blocks are. *) module T = Tst.Nodes module Tty = Tst.Ty @@ -27,8 +27,8 @@ let definition st (t : Tty.t) = | _ -> None) | _ -> None -(* `@primitives$String`, the string view, and `@primitives$List`: the - storage primitives that are reference types, each a handle. *) +(* `@primitives$String`, a value type, and `@primitives$List`, a + reference type: the storage primitives that are each a handle. *) let is_text = function | Tty.Intrinsic { namespace = "primitives"; name = "String"; args = [] } -> true | _ -> false @@ -39,28 +39,38 @@ let element = function let is_list t = Option.is_some (element t) -(* `@primitives$Array`, a value type: its element type and length. *) +(* `@primitives$Array`, a value type, and `@primitives$ArrayRef`, + a reference type with the same layout (generics.md §8.4): the element + type and length. *) let array_of = function | Tty.Intrinsic - { namespace = "primitives"; name = "Array"; args = [ Tty.Type e; Tty.Number (Tty.Known n) ] } - -> + { + namespace = "primitives"; + name = "Array" | "ArrayRef"; + args = [ Tty.Type e; Tty.Number (Tty.Known n) ]; + } -> Some (e, n) | _ -> None -(* A reference type: a `#` type, whose instances are hosted (memory.md §2.1), - or a string or a list, whose instance is a handle (§3.6). *) +let is_array_ref = function + | Tty.Intrinsic { namespace = "primitives"; name = "ArrayRef"; _ } -> true + | _ -> false + +(* A reference type: a `#` type, a list or an `ArrayRef`, whose instances are + owned (memory.md §2.1). A roaming owner is one of its type. *) let reference st t = - is_text t || is_list t || match definition st t with Some (_, true) -> true | _ -> false + let t = match t with Tty.Roaming t -> t | t -> t in + is_list t || is_array_ref t || match definition st t with Some (_, true) -> true | _ -> false (* The types a type holds inline: its members and payloads, what it is - distinct from, and an array's elements. A guest holds a tether, and a - list's elements are in its block. *) + distinct from, and an array's elements. A reference holds an address, and + a list's elements are in its block. *) let inline st t = match (definition st t, array_of t) with | Some ((T.Decl.Struct ms | T.Decl.Variant ms), _), _ -> - List.filter (fun m -> not (Tty.is_guest m)) (List.map snd ms) + List.filter (fun m -> not (Tty.is_ref m)) (List.map snd ms) | Some (T.Decl.Distinct u, _), _ -> [ u ] - | None, Some (e, _) when not (Tty.is_guest e) -> [ e ] + | None, Some (e, _) when not (Tty.is_ref e) -> [ e ] | _ -> [] (* adt.md §4: a member is boxed when its type leads back to the type that @@ -68,7 +78,7 @@ let inline st t = A boxed member is a pointer to its payload's block. *) let boxed st holder member = let rec reaches seen t = - (not (Tty.is_guest t)) + (not (Tty.is_ref t)) && (Tty.equal t holder || (not (List.exists (Tty.equal t) seen)) && List.exists (reaches (t :: seen)) (inline st t)) in @@ -81,21 +91,23 @@ let collapsed st t = | Some (T.Decl.Struct [ (_, m) ], false) -> not (boxed st t m) | _ -> false -(* A reference type's instance begins with its backpointer (L5), so its - members start one index later. *) -let member_index st t slot = if reference st t then slot + 1 else slot +(* A member's index in its struct's layout: its place in declaration order + (memory.md §3.3). *) +let member_index _st _t slot = slot (* L5: a storage primitive has a machine layout, a string or a list is a handle, and an array is its elements inline. A value struct has its members' in declaration order, except that one of a single member has that member's (concepts-vs-primitives.md) and an empty one has none. A value variant is a sum of its payloads, and - an enum a sum of cases with none. A reference type's instance is the same - shape after a `u32` backpointer (memory.md §3.3), a guest is a `u32` - tether (§4.2), and a boxed member a pointer (adt.md §4). A function value - is the address of the function a lambda was lifted to (L14). *) + an enum a sum of cases with none. A reference type's instance has the + same shape and nothing more (memory.md §3.3), a reference is the address + of the owner it names (§4.1), and a boxed member a pointer (adt.md §4). + A function value is the address of the function a lambda was lifted to + (L14). *) let rec ty st span (t : Tty.t) : Nodes.Ty.t = match t with + | Tty.Roaming t -> ty st span t | _ when is_text t || is_list t -> Nodes.Ty.Handle | Tty.Intrinsic { namespace = "primitives"; name; args = [] } -> ( match name with @@ -105,12 +117,12 @@ let rec ty st span (t : Tty.t) : Nodes.Ty.t = | "I32" -> Nodes.Ty.I32 | "Float" -> Nodes.Ty.F64 | _ -> unhandled span t) - | Tty.Guest _ -> Nodes.Ty.I32 + | Tty.Reference _ -> Nodes.Ty.Ptr | Tty.Verb _ -> Nodes.Ty.Ptr - | Tty.Intrinsic - { namespace = "primitives"; name = "Array"; args = [ Tty.Type e; Tty.Number (Tty.Known n) ] } - -> - Nodes.Ty.Array (ty st span e, n) + | Tty.Intrinsic _ when Option.is_some (array_of t) -> ( + match array_of t with + | Some (e, n) -> Nodes.Ty.Array (ty st span e, n) + | None -> unhandled span t) | Tty.Named _ -> ( let member m = if boxed st t m then Nodes.Ty.Ptr else ty st span m in let sum ts = Nodes.Ty.Sum ts in @@ -120,12 +132,9 @@ let rec ty st span (t : Tty.t) : Nodes.Ty.t = | Some (T.Decl.Struct ms, false) -> Nodes.Ty.Struct (List.map (fun (_, m) -> member m) ms) | Some (T.Decl.Variant cs, false) -> sum (List.map (fun (_, c) -> member c) cs) | Some (T.Decl.Enum cs, false) -> sum (List.map (fun _ -> Nodes.Ty.Void) cs) - | Some (T.Decl.Struct ms, true) -> - Nodes.Ty.Struct (Nodes.Ty.I32 :: List.map (fun (_, m) -> member m) ms) - | Some (T.Decl.Variant cs, true) -> - Nodes.Ty.Struct [ Nodes.Ty.I32; sum (List.map (fun (_, c) -> member c) cs) ] - | Some (T.Decl.Enum cs, true) -> - Nodes.Ty.Struct [ Nodes.Ty.I32; sum (List.map (fun _ -> Nodes.Ty.Void) cs) ] + | Some (T.Decl.Struct ms, true) -> Nodes.Ty.Struct (List.map (fun (_, m) -> member m) ms) + | Some (T.Decl.Variant cs, true) -> sum (List.map (fun (_, c) -> member c) cs) + | Some (T.Decl.Enum cs, true) -> sum (List.map (fun _ -> Nodes.Ty.Void) cs) | Some (T.Decl.Distinct u, _) -> member u | _ -> unhandled span t) | _ -> unhandled span t @@ -135,23 +144,22 @@ let stride st span t = let size, align = Nodes.Ty.size_align (ty st span t) in max 1 ((size + align - 1) / align * align) -(* Where a type's hosts and owned blocks are (memory.md §3.6, §4.5): the - instance, when it is a reference type, each reference-type member, each - handle, and each boxed member, down through variant payloads under the - tag that makes each live, and through each of an array's elements. A - guest is a tether, and owns nothing. *) +(* Where a type's owned blocks are (memory.md §3.6): each handle and each + boxed member, down through variant payloads under the tag that makes each + live, and through each of an array's elements. A reference is an + address, and owns nothing. *) let rec positions st span (t : Tty.t) base tags : Layout.position list = let at kind size = { Layout.kind; offset = base; size; tags } in let handle = fst (Nodes.Ty.size_align Nodes.Ty.Handle) in match (t, element t) with - | Tty.Guest _, _ -> [] + | Tty.Reference _, _ -> [] + | Tty.Roaming t, _ -> positions st span t base tags | _, Some e -> let elements = layout st span e in - [ at Layout.Host handle; at (Layout.List { stride = stride st span e; elements }) handle ] - | _ when is_text t -> [ at Layout.Host handle; at Layout.Text handle ] - | ( Tty.Intrinsic - { namespace = "primitives"; name = "Array"; args = [ Tty.Type e; Tty.Number (Tty.Known n) ] }, - _ ) -> ( + [ at (Layout.List { stride = stride st span e; elements }) handle ] + | _ when is_text t -> [ at Layout.Text handle ] + | Tty.Intrinsic _, _ when Option.is_some (array_of t) -> ( + let e, n = Option.get (array_of t) in match positions st span e base tags with | [] -> [] | _ -> @@ -161,11 +169,8 @@ let rec positions st span (t : Tty.t) base tags : Layout.position list = match definition st t with | Some (T.Decl.Distinct u, _) -> positions st span u base tags | Some (T.Decl.Struct [ (_, m) ], false) when collapsed st t -> positions st span m base tags - | Some (definition, reference) -> ( + | Some (definition, _) -> ( let lowered = ty st span t in - let own = - if reference then [ at Layout.Host (fst (Nodes.Ty.size_align lowered)) ] else [] - in let member m base tags = if boxed st t m then let size = fst (Nodes.Ty.size_align (ty st span m)) in @@ -173,11 +178,6 @@ let rec positions st span (t : Tty.t) base tags : Layout.position list = [ { Layout.kind = Layout.Box { size; payload }; offset = base; size = 8; tags } ] else positions st span m base tags in - (* Where the members or the sum start: after the backpointer, in a - reference type's instance. *) - let starts ts = - if reference then List.tl (Nodes.Ty.offsets ts) else Nodes.Ty.offsets ts - in let variant cs sum = List.concat (List.mapi @@ -186,12 +186,10 @@ let rec positions st span (t : Tty.t) base tags : Layout.position list = in match (definition, lowered) with | T.Decl.Struct ms, Nodes.Ty.Struct ts -> - own - @ List.concat (List.map2 (fun (_, m) at -> member m (base + at) tags) ms (starts ts)) - | T.Decl.Variant cs, Nodes.Ty.Struct ts when reference -> - own @ variant cs (base + List.hd (starts ts)) + List.concat + (List.map2 (fun (_, m) at -> member m (base + at) tags) ms (Nodes.Ty.offsets ts)) | T.Decl.Variant cs, Nodes.Ty.Sum _ -> variant cs base - | _ -> own) + | _ -> []) | None -> []) (* A type's layout, by the type's symbol (Symbol.ty), which is also the name @@ -206,57 +204,49 @@ and layout st span (t : Tty.t) : Layout.t = end; name -(* Whether a local of this type is a host: a reference type's instance lives - in its scope's arena (memory.md §3.3). A guest is a tether, not a host. *) -let hosted st t = (not (Tty.is_guest t)) && reference st t +(* Whether a local of this type is an owner: a reference type's instance + lives in its scope's arena (memory.md §3.3). A reference is an address, + not an owner. *) +let owned st t = (not (Tty.is_ref t)) && reference st t + +(* Whether a local of this type is a roaming owner: one the body may move + on, or that dies with it (lifetimes.md §1.5). *) +let roaming st t = Tty.is_roaming t && reference st t -(* Whether a place is reached through a host: a member or a case of a - reference-type instance, or of one a guest names, an element of a list, - and an element of an array reached through a host. A spawned call may - write back a value there while another thread reads it (concurrency.md - §4.4). *) -let rec through_host st (e : T.Expr.t) = +(* Whether a place is reached through an owner: a member or a case of a + reference-type instance, or of one a reference names, an element of a + list or an `ArrayRef`, and an element of an array reached through one. A + spawned call may write back a value there while another thread reads it + (concurrency.md §4.4). *) +let rec through_owner st (e : T.Expr.t) = match e.T.Expr.node with | T.Expr.Field { target; _ } | T.Expr.Case_read { target; _ } -> - Tty.is_guest target.T.Expr.ty || reference st (Tty.strip_guest target.T.Expr.ty) || through_host st target - | T.Expr.Subscript { target; _ } when Option.is_some (array_of (Tty.strip_guest target.T.Expr.ty)) -> - through_host st target + Tty.is_ref target.T.Expr.ty || reference st (Tty.strip_mode target.T.Expr.ty) || through_owner st target + | T.Expr.Subscript { target; _ } + when Option.is_some (array_of (Tty.strip_mode target.T.Expr.ty)) + && not (is_array_ref (Tty.strip_mode target.T.Expr.ty)) -> + through_owner st target | T.Expr.Subscript _ -> true | _ -> false (* Whether a value of this type owns a dynamic block (memory.md §3.6), which it returns when it dies and copies when it is copied. *) -let owns st span t = - (not (Tty.is_guest t)) - && List.exists (fun (p : Layout.position) -> p.kind <> Layout.Host) (positions st span t 0 []) +let owns st span t = (not (Tty.is_ref t)) && positions st span t 0 [] <> [] -(* Whether a local of this type is held in its scope's arena: a host, or a +(* Whether a local of this type is held in its scope's arena: an owner, or a value that owns a block, which the scope's drain returns (L8). *) -let held st span t = hosted st t || owns st span t +let held st span t = owned st t || owns st span t -(* The sum inside a variant or enum: a reference one's comes after its - backpointer. *) -let sum_of st span t (value : Expr.t) = - if reference st t then - match ty st span t with - | Nodes.Ty.Struct [ _; s ] -> { Expr.node = Expr.Member { value; index = 1 }; ty = s } - | _ -> value - else value +(* The sum inside a variant or enum. *) +let sum_of _st _span _t (value : Expr.t) = value -(* A case of a variant or enum, built: a reference one is its backpointer, - untethered, and then the case. *) +(* A case of a variant or enum, built. *) let case_of st span t index (payload : Expr.t) = - let lowered = ty st span t in - match lowered with - | Nodes.Ty.Struct [ bp; s ] when reference st t -> - let untethered = { Expr.node = Expr.Int 0L; ty = bp } in - let case = { Expr.node = Expr.Case { index; payload }; ty = s } in - { Expr.node = Expr.Record [ (0, untethered); (1, case) ]; ty = lowered } - | _ -> { Expr.node = Expr.Case { index; payload }; ty = lowered } + { Expr.node = Expr.Case { index; payload }; ty = ty st span t } (* The cases of a variant or enum, in declaration order. *) let cases st span t = - let t = Tty.strip_guest t in + let t = Tty.strip_mode t in match definition st t with | Some (T.Decl.Variant cs, _) -> List.map fst cs | Some (T.Decl.Enum cs, _) -> cs @@ -272,12 +262,12 @@ let case_index st span t case = (* The type a variant case carries, and a struct field. *) let payload_type st span t case = - match definition st (Tty.strip_guest t) with + match definition st (Tty.strip_mode t) with | Some (T.Decl.Variant cs, _) -> ( match List.assoc_opt case cs with Some c -> c | None -> unhandled span t) | _ -> unhandled span t let field_type st span t slot = - match definition st (Tty.strip_guest t) with + match definition st (Tty.strip_mode t) with | Some (T.Decl.Struct ms, _) when slot < List.length ms -> snd (List.nth ms slot) | _ -> unhandled span t diff --git a/lib/cgt/verbs.ml b/lib/cgt/verbs.ml index 61d8f029..348750c8 100644 --- a/lib/cgt/verbs.ml +++ b/lib/cgt/verbs.ml @@ -57,11 +57,13 @@ let expands (v : verb) = (fun (p : T.Local.t) -> match p.T.Local.ty with Tty.Concept _ -> true | _ -> false) v.params -(* L6: a `mut` method's subject, a reference-type subject, and a swallowed +(* L6: a `mut` method's subject, a reference-type subject, and a borrowed reference-type argument are passed as the address of the caller's place; - the caller's host keeps the value (lifetimes.md §1.5). *) + the caller stays a full owner (memory.md §2.9). A `^T` argument is the + moved value itself. *) let by_address st (v : verb) (p : T.Local.t) = - (v.signature.S.is_mut && p.T.Local.name = "this") || hosted st p.T.Local.ty + (v.signature.S.is_mut && p.T.Local.name = "this") + || (owned st p.T.Local.ty && not (Tty.is_roaming p.T.Local.ty)) (* L14: a function value is called as a verb of its type would be, so its type gives the verb it is called as: the subject first, named `this`, as a diff --git a/lib/codegen/emit.ml b/lib/codegen/emit.ml index ba1e0273..5fd2ed40 100644 --- a/lib/codegen/emit.ml +++ b/lib/codegen/emit.ml @@ -30,7 +30,7 @@ let rec lltype env (t : Ty.t) = | Ty.I32 -> Llvm.i32_type env.ctx | Ty.I64 -> env.i64 | Ty.F64 -> Llvm.double_type env.ctx - | Ty.Handle -> Llvm.struct_type env.ctx [| Llvm.i32_type env.ctx; env.ptr; env.i64; env.i64 |] + | Ty.Handle -> Llvm.struct_type env.ctx [| env.ptr; env.i64; env.i64 |] | Ty.Ptr -> env.ptr | Ty.Struct ts -> Llvm.struct_type env.ctx (Array.of_list (List.map (stored env) ts)) | Ty.Sum ts -> ( @@ -95,7 +95,6 @@ let layouts env (named : (Layout.t * Layout.position list) list) = let position (p : Layout.position) = let kind, extra, inner = match p.kind with - | Layout.Host -> (0, 0, int 0) | Layout.Text -> (1, 0, int 0) | Layout.List { stride; elements } -> (2, stride, table elements) | Layout.Box { size; payload } -> (3, size, table payload) @@ -118,8 +117,8 @@ let layout env (l : Layout.t) = let listed env (l : Layout.t) = match Hashtbl.find_opt env.layouts l with Some (_, listed) -> listed | None -> false -(* A string literal is constant bytes the module owns, with no terminator. - Its instance starts untethered, and owns no block. *) +(* A string literal is constant bytes the module owns, with no terminator, + and owns no block. *) let text env s = let bytes = Llvm.const_string env.ctx s in let g = Llvm.define_global "zane.text" bytes env.m in @@ -127,8 +126,7 @@ let text env s = Llvm.set_global_constant true g; Llvm.set_unnamed_addr true g; let n = Llvm.const_int env.i64 in - Llvm.const_struct env.ctx - [| Llvm.const_int (Llvm.i32_type env.ctx) 0; g; n (String.length s); n 0 |] + Llvm.const_struct env.ctx [| g; n (String.length s); n 0 |] (* ---------------------------------------------------------------------- *) (* Frames and slots *) @@ -170,9 +168,8 @@ let slot env fr id t = Hashtbl.replace fr.locals id (s, t); s -(* A value moved into a fresh place: stored, the anchors it carries follow - it there (memory.md §4.5), and its blocks move into the place's region - (§3.5). *) +(* A value moved into a fresh place: stored, and the blocks it owns that the + place's region outlives move into it (memory.md §3.5). *) let place env b p v l = ignore (Llvm.build_store v p b); if listed env l then ignore (call_runtime env b Cgt.Runtime.Arrive [| p; layout env l |]) @@ -260,10 +257,6 @@ and expr env fr b (e : Expr.t) : Llvm.llvalue option = (base, within) path in Some at - | Expr.Mint p -> Some (call_runtime env b Cgt.Runtime.Mint [| value_of env fr b p |]) - | Expr.Resolve t -> Some (call_runtime env b Cgt.Runtime.Resolve [| value_of env fr b t |]) - | Expr.Terminal t -> - Some (call_runtime env b Cgt.Runtime.Terminal [| value_of env fr b t |]) | Expr.Take { address; layout = l } -> ( let p = value_of env fr b address in match e.Expr.ty with @@ -460,7 +453,7 @@ and stat env fr b (s : Stat.t) = stats env fr b body; if not (has_terminator b) then ignore (call_runtime env b Cgt.Runtime.Scope_drain [| arena |]); fr.open_ <- List.tl fr.open_ - | Stat.Host { id; scope; value; layout = l } -> ( + | Stat.Hold { id; scope; value; layout = l } -> ( match expr env fr b value with | None -> () | Some v -> @@ -482,7 +475,7 @@ and stat env fr b (s : Stat.t) = | Stat.Store { address; value } -> ( let p = value_of env fr b address in match expr env fr b value with Some v -> ignore (Llvm.build_store v p b) | None -> ()) - | Stat.Overwrite { address; value; layout = l; contingent } -> ( + | Stat.Overwrite { address; value; layout = l } -> ( let p = value_of env fr b address in match expr env fr b value with | None -> () @@ -490,9 +483,7 @@ and stat env fr b (s : Stat.t) = let incoming = spill env fr b v in let n x = Llvm.const_int env.i64 x in let size = n (fst (size_align value.Expr.ty)) in - let contingent = n (if contingent then 1 else 0) in - ignore - (call_runtime env b Cgt.Runtime.Overwrite [| p; incoming; size; layout env l; contingent |])) + ignore (call_runtime env b Cgt.Runtime.Overwrite [| p; incoming; size; layout env l |])) | Stat.If { cond; body } -> let c = value_of env fr b cond in let taken = block env fr and after = block env fr in diff --git a/lib/cst/lexer.ml b/lib/cst/lexer.ml index a10765d8..777fc4cc 100644 --- a/lib/cst/lexer.ml +++ b/lib/cst/lexer.ml @@ -27,6 +27,7 @@ let rec token buf = | "$" -> DOLLAR | "#" -> HASH | "&" -> AMPERSAND + | "^" -> CARET | "@" -> AT | "!" -> EXCL | "?" -> QSTNMARK diff --git a/lib/cst/nodes.ml b/lib/cst/nodes.ml index f28542e3..022fd055 100644 --- a/lib/cst/nodes.ml +++ b/lib/cst/nodes.ml @@ -398,7 +398,8 @@ and Type_expr : sig and node = | Path of { name : Name_type.t; generics : Generic_arg.t list } - | Guest of t + | Reference of t + | Roaming of t | Verb of Verb_type.t end = Type_expr @@ -411,7 +412,9 @@ and Param_type : sig and node = | Concrete of Type_expr.t | Concept of Concept.t - | InferredType of { name : Name.t; concept : Concept.t } + (* [roaming] is `^T Type`: a parameter that takes an owner when [name] is + filled with a reference type (memory.md §2.9). *) + | InferredType of { name : Name.t; concept : Concept.t; roaming : bool } end = Param_type and Param : sig diff --git a/lib/cst/parser.mly b/lib/cst/parser.mly index 79843f61..74670f78 100644 --- a/lib/cst/parser.mly +++ b/lib/cst/parser.mly @@ -34,6 +34,7 @@ open Statement_shape %token DOLLAR "$" %token HASH "#" %token AMPERSAND "&" +%token CARET "^" %token AT "@" %token EXCL "!" %token QSTNMARK "?" @@ -200,7 +201,10 @@ top_decl: type_ } | AMPERSAND type_=type_base { - type_expr $loc (Nodes.Type_expr.Guest type_) + type_expr $loc (Nodes.Type_expr.Reference type_) + } + | CARET type_=type_base { + type_expr $loc (Nodes.Type_expr.Roaming type_) } (* A suffix is parsed as its own bracket group and only then applied to the @@ -387,7 +391,13 @@ map_lit: ignore concept_; param_type $loc (Nodes.Param_type.InferredType - { name; concept = concept $loc(concept_) Nodes.Concept.Type }) + { name; concept = concept $loc(concept_) Nodes.Concept.Type; roaming = false }) + } + | CARET name=uname concept_=UTYPE { + ignore concept_; + param_type $loc + (Nodes.Param_type.InferredType + { name; concept = concept $loc(concept_) Nodes.Concept.Type; roaming = true }) } %inline constructor_field: diff --git a/lib/cst/to_span_text.ml b/lib/cst/to_span_text.ml index 27a8fd09..42583522 100644 --- a/lib/cst/to_span_text.ml +++ b/lib/cst/to_span_text.ml @@ -85,7 +85,7 @@ let rec type_expr d (x : Type_expr.t) = | Type_expr.Path { name = n; generics } -> name_type d n; each d generic_arg generics - | Type_expr.Guest t -> type_expr d t + | Type_expr.Reference t | Type_expr.Roaming t -> type_expr d t | Type_expr.Verb v -> verb_type d v and verb_type d (x : Verb_type.t) = @@ -124,7 +124,7 @@ and param_type d (x : Param_type.t) = match x.Param_type.node with | Param_type.Concrete t -> type_expr d t | Param_type.Concept c -> concept d c - | Param_type.InferredType { name = n; concept = c } -> + | Param_type.InferredType { name = n; concept = c; _ } -> name d "name" n; concept d c diff --git a/lib/cst/to_tree_graph.ml b/lib/cst/to_tree_graph.ml index f7a9ce03..f5db50fd 100644 --- a/lib/cst/to_tree_graph.ml +++ b/lib/cst/to_tree_graph.ml @@ -44,11 +44,10 @@ let rec concept_to_node (x: Nodes.Concept.t) = match x.Nodes.Concept.node with and param_type_to_node (x: Nodes.Param_type.t) = match x.Nodes.Param_type.node with | Concrete x -> type_to_node x | Concept x -> concept_to_node x - | InferredType { name; concept } -> - fields [ - ("name", Leaf (text name)); - ("concept", concept_to_node concept); - ] + | InferredType { name; concept; roaming } -> + fields + ([ ("name", Leaf (text name)); ("concept", concept_to_node concept) ] + @ if roaming then [ ("roaming", Leaf "^") ] else []) and verb_type_to_node (x: Nodes.Verb_type.t) = match x.Nodes.Verb_type.node with | Func { params; ret_type } -> @@ -83,7 +82,8 @@ and generic_arg_to_node (x: Nodes.Generic_arg.t) = match x.Nodes.Generic_arg.nod and type_to_node (x: Nodes.Type_expr.t) = match x.Nodes.Type_expr.node with | Verb x -> verb_type_to_node x - | Guest x -> group "guest" (type_to_node x) + | Reference x -> group "reference" (type_to_node x) + | Roaming x -> group "roaming" (type_to_node x) | Path { name; generics } -> fields [ ("qualifier", name_type_to_node name); diff --git a/lib/sst/lower.ml b/lib/sst/lower.ml index fd3f74f2..cfced899 100644 --- a/lib/sst/lower.ml +++ b/lib/sst/lower.ml @@ -65,8 +65,10 @@ let rec type_expr (x : C.Type_expr.t) : S.Type_expr.t = S.Type_expr.Path { name = name_type n; generics = List.map generic_arg generics }; } - | C.Type_expr.Guest inner -> - { S.Type_expr.span; node = S.Type_expr.Guest (type_expr inner) } + | C.Type_expr.Reference inner -> + { S.Type_expr.span; node = S.Type_expr.Reference (type_expr inner) } + | C.Type_expr.Roaming inner -> + { S.Type_expr.span; node = S.Type_expr.Roaming (type_expr inner) } | C.Type_expr.Verb v -> { S.Type_expr.span; node = S.Type_expr.Verb (verb_type v) } @@ -115,8 +117,8 @@ and param_type (x : C.Param_type.t) : S.Param_type.t = match x.C.Param_type.node with | C.Param_type.Concrete t -> S.Param_type.Concrete (type_expr t) | C.Param_type.Concept c -> S.Param_type.Concept (concept c) - | C.Param_type.InferredType { name = n; concept = c } -> - S.Param_type.InferredType { name = name n; concept = concept c } + | C.Param_type.InferredType { name = n; concept = c; roaming } -> + S.Param_type.InferredType { name = name n; concept = concept c; roaming } in { S.Param_type.node; span } diff --git a/lib/sst/nodes.ml b/lib/sst/nodes.ml index 9e97bdcc..de22d1f7 100644 --- a/lib/sst/nodes.ml +++ b/lib/sst/nodes.ml @@ -304,7 +304,8 @@ and Type_expr : sig and node = | Path of { name : Name_type.t; generics : Generic_arg.t list } - | Guest of t + | Reference of t + | Roaming of t | Verb of Verb_type.t end = Type_expr @@ -317,7 +318,7 @@ and Param_type : sig and node = | Concrete of Type_expr.t | Concept of Concept.t - | InferredType of { name : Name.t; concept : Concept.t } + | InferredType of { name : Name.t; concept : Concept.t; roaming : bool } end = Param_type and Param : sig diff --git a/lib/sst/to_span_text.ml b/lib/sst/to_span_text.ml index 32e602c2..63d07c81 100644 --- a/lib/sst/to_span_text.ml +++ b/lib/sst/to_span_text.ml @@ -115,8 +115,11 @@ let rec type_expr d (x : Type_expr.t) = line d "type_expr Path" x.Type_expr.span; name_type (deeper d) n; each (deeper d) generic_arg generics - | Type_expr.Guest inner -> - line d "type_expr Guest" x.Type_expr.span; + | Type_expr.Reference inner -> + line d "type_expr Reference" x.Type_expr.span; + type_expr (deeper d) inner + | Type_expr.Roaming inner -> + line d "type_expr Roaming" x.Type_expr.span; type_expr (deeper d) inner | Type_expr.Verb v -> line d "type_expr Verb" x.Type_expr.span; @@ -167,7 +170,7 @@ and param_type d (x : Param_type.t) = | Param_type.Concept c -> line d "param_type Concept" x.Param_type.span; concept (deeper d) c - | Param_type.InferredType { name = n; concept = c } -> + | Param_type.InferredType { name = n; concept = c; _ } -> line d "param_type InferredType" x.Param_type.span; name (deeper d) "name" n; concept (deeper d) c diff --git a/lib/sst/to_tree_graph.ml b/lib/sst/to_tree_graph.ml index 29b00402..7289129e 100644 --- a/lib/sst/to_tree_graph.ml +++ b/lib/sst/to_tree_graph.ml @@ -49,8 +49,10 @@ and param_type_to_node (x : Nodes.Param_type.t) = match x.Nodes.Param_type.node with | Concrete x -> type_to_node x | Concept x -> concept_to_node x - | InferredType { name; concept } -> - fields [ ("name", Leaf (text name)); ("concept", concept_to_node concept) ] + | InferredType { name; concept; roaming } -> + fields + ([ ("name", Leaf (text name)); ("concept", concept_to_node concept) ] + @ if roaming then [ ("roaming", Leaf "^") ] else []) and verb_type_to_node (x : Nodes.Verb_type.t) = match x.Nodes.Verb_type.node with @@ -89,7 +91,8 @@ and generic_arg_to_node (x : Nodes.Generic_arg.t) = and type_to_node (x : Nodes.Type_expr.t) = match x.Nodes.Type_expr.node with | Verb x -> verb_type_to_node x - | Guest x -> group "guest" (type_to_node x) + | Reference x -> group "reference" (type_to_node x) + | Roaming x -> group "roaming" (type_to_node x) | Path { name; generics } -> fields [ diff --git a/lib/tst/analyses/exits.ml b/lib/tst/analyses/exits.ml index 6cf6eb9d..53ff972f 100644 --- a/lib/tst/analyses/exits.ml +++ b/lib/tst/analyses/exits.ml @@ -1,4 +1,4 @@ -(* Exits (control-flow.md §4.2, as docs/spec-divergences.md §11 reads it): an +(* Exits (control-flow.md §4.2, as docs/spec-divergences.md §10 reads it): an analysis over the finished TST (docs/design/semantics.md D1). A verb exits when `@controlflow$exitFromCall` is in its own frame: its diff --git a/lib/tst/analyses/moves.ml b/lib/tst/analyses/moves.ml index 56a4652a..5769f9fd 100644 --- a/lib/tst/analyses/moves.ml +++ b/lib/tst/analyses/moves.ml @@ -1,23 +1,31 @@ -(* Moves: which values a hosting store may take, and what a move leaves +(* Moves: which values a store into an owner may take, and what a move leaves behind (lifetimes.md §1.2, §1.3, §1.6, §1.8). An analysis over the finished TST (docs/design/semantics.md D1). - A reference-type value is never copied, so storing one where a host goes -- - a hosting local or field, a swallowing `T` parameter, a return, an element, - a case payload -- moves it. What may be moved: + A reference-type value is never copied, so storing one where an owner goes + -- an owning local or field, a `^T` parameter, a return, an element, a case + payload -- moves it. What may be moved is a roaming value the store is + entitled to consume: - - a symbol, local or parameter, named bare, and only in the block that - declares it (§1.3), where a parameter is declared at the top of the body; - - a verb's result, or a case form: a fresh value nothing hosts yet. + - a roaming owner named by a symbol, a local or a parameter declared + `^T`, and only in the block that declares it (§1.3), where a parameter + is declared at the top of the body; + - a field of such a symbol, which leaves the symbol partly spent; + - a verb's result, or a case form: a fresh value nothing owns yet. - A field, an element, a case payload, a package constant or a guest is not - one (§1.2). A moved symbol is spent: any use of it is an error until a - store refills it, and that store too is confined to its declaring block - (§1.6). Passing to a `T` parameter is a move like any other (§1.8). + A settled owner never moves (memory.md §2.1), a borrow -- a bare + reference-type parameter, and `this` -- is the caller's, and an element, a + case payload, a package constant and a reference are none of the above. + A bare reference-type parameter is a borrow, so passing to one moves + nothing; passing to a `^T` one is a move like any other (§1.8). - Because a symbol changes between hosting and spent only in its own block, - one walk in source order sees each use against the right state: a nested - block can neither spend nor refill it. *) + A moved symbol is spent: any use of it is an error until a store refills + it, and that store too is confined to its declaring block (§1.6). A field + moved out of a roaming symbol leaves that field spent, and the symbol + spent as a whole, until the field is refilled. Because a symbol changes + between owning and spent only in its own block, one walk in source order + sees each use against the right state: a nested block can neither spend + nor refill it. *) module T = Nodes module S = Signature @@ -29,22 +37,36 @@ type walk = { declared : (int, int) Hashtbl.t; (* Where each spent symbol was moved. *) spent : (int, Source.Span.t) Hashtbl.t; - (* `this`, which a method never moves (memory.md §2.9). *) - subjects : (int, unit) Hashtbl.t; - (* A match binder is its case's payload (adt.md §5). *) - binders : (int, unit) Hashtbl.t; + (* The fields moved out of a roaming symbol, each with where it was moved. *) + partly : (int, (string list * Source.Span.t) list) Hashtbl.t; + states : States.t; mutable block : int; mutable ret : Ty.t; mutable resolve : Ty.t list; } +(* Whether storage of type [t] owns what is stored in it. A reference names + an owner elsewhere, and a type parameter is filled per instance (D12). *) +let owning env (t : Ty.t) = + match t with + | Ty.Reference _ | Ty.Param _ | Ty.Roaming (Ty.Param _) | Ty.Error -> false + | t -> Type_decls.is_reference env (Ty.strip_mode t) -(* Whether storage of type [t] hosts what is stored in it. *) -let hosting env (t : Ty.t) = - match t with Ty.Guest _ | Ty.Param _ | Ty.Error -> false | t -> Type_decls.is_reference env t +(* A parameter of type [t] takes its argument by moving it only when it is + written `^T`. A bare reference-type parameter is a borrow (memory.md §2.9), + which moves nothing. *) +let taken env (t : Ty.t) = if Ty.is_roaming t then t else if owning env t then Ty.Error else t let line (span : Source.Span.t) = span.Source.Span.start_.Lexing.pos_lnum +let dotted path = String.concat "." path + +(* Whether one field path lies inside the other: reading either reads the + moved field. *) +let overlaps a b = + let rec prefix x y = match (x, y) with [], _ -> true | p :: x, q :: y -> p = q && prefix x y | _ -> false in + prefix a b || prefix b a + (* ---------------------------------------------------------------------- *) (* Moving *) (* ---------------------------------------------------------------------- *) @@ -53,36 +75,93 @@ let move env w (v : T.Expr.t) = let say what = Env.error env v.T.Expr.span (Printf.sprintf - "%s is not a move-source: a host is moved only from a symbol, a verb's result or a \ - case form" + "%s is not a move-source: an owner is moved only from a roaming symbol (one declared \ + `^T`), a field of one, a verb's result or a case form" what) in - match v.T.Expr.node with - | T.Expr.Var (T.Name_ref.Local l) -> - let name = Env.quote l.T.Local.name in - if Hashtbl.mem w.subjects l.T.Local.id then + if Ty.is_ref v.T.Expr.ty then + say "a reference, which names an owner rather than being one," + else + match (States.state w.states v, v.T.Expr.node) with + | States.Fresh, _ -> () + | States.Roaming, _ -> ( + match States.field_path v with + | Some (l, path) -> + let name = Env.quote l.T.Local.name in + if Hashtbl.find_opt w.declared l.T.Local.id <> Some w.block then + Env.error env v.T.Expr.span + (Printf.sprintf + "%s is declared in an enclosing block, and a roaming owner is moved only in \ + the block that declares it" + name) + else if + (* Moving out of a place that is already spent was reported + as a use, and changes nothing. *) + Hashtbl.mem w.spent l.T.Local.id + || List.exists + (fun (p, _) -> overlaps p path) + (Option.value ~default:[] (Hashtbl.find_opt w.partly l.T.Local.id)) + then () + else if path = [] then begin + if not (Hashtbl.mem w.spent l.T.Local.id) then + Hashtbl.replace w.spent l.T.Local.id v.T.Expr.span + end + else + let old = Option.value ~default:[] (Hashtbl.find_opt w.partly l.T.Local.id) in + Hashtbl.replace w.partly l.T.Local.id ((path, v.T.Expr.span) :: old) + | None -> say "this place") + | States.Settled, T.Expr.Var (T.Name_ref.Local l) -> + Env.error env v.T.Expr.span + (Printf.sprintf + "%s is a settled owner, which may be referenced and so never moves; an owner that \ + is to move is declared `^T`" + (Env.quote l.T.Local.name)) + | States.Settled, T.Expr.Var (T.Name_ref.Global _) -> say "a package constant" + | States.Settled, T.Expr.Field _ -> + say "a field of a settled owner, which is overwritten in place and never moved out," + | States.Settled, _ -> say "this settled place" + | States.Borrowed, T.Expr.Var (T.Name_ref.Local l) when Hashtbl.mem w.states.States.subjects l.T.Local.id -> Env.error env v.T.Expr.span - "`this` is the object the method was called on, and a method never moves it" - else if Hashtbl.mem w.binders l.T.Local.id then say "a variant case's payload" - else if (match l.T.Local.ty with Ty.Guest _ -> true | _ -> false) then - say (name ^ ", a guest,") - else if Hashtbl.find_opt w.declared l.T.Local.id <> Some w.block then + "`this` is a borrow of the object the method was called on, and a method never moves it" + | States.Borrowed, T.Expr.Var (T.Name_ref.Local l) -> Env.error env v.T.Expr.span (Printf.sprintf - "%s is declared in an enclosing block, and a symbol is moved only in the block \ - that declares it" - name) - else if not (Hashtbl.mem w.spent l.T.Local.id) then - Hashtbl.replace w.spent l.T.Local.id v.T.Expr.span - | T.Expr.Var (T.Name_ref.Global _) -> say "a package constant" - | T.Expr.Var _ -> say "this value" - | T.Expr.Field _ -> say "a field" - | T.Expr.Subscript _ -> say "an element of a container" - | T.Expr.Case_read _ -> say "a variant case's payload" - | _ -> ( match v.T.Expr.ty with Ty.Guest _ -> say "a guest" | _ -> ()) + "%s is a borrow: the caller lends it for the call, and a borrow is never moved, \ + stored or returned; a parameter that takes its argument is declared `^T`" + (Env.quote l.T.Local.name)) + | States.Borrowed, _ -> say "a part of a borrow" + | States.Contingent, T.Expr.Subscript _ -> say "an element of a container" + | States.Contingent, (T.Expr.Case_read _ | T.Expr.Var _) -> say "a variant case's payload" + | States.Contingent, _ -> say "a part of a temporary value" (* A value stored where a value of type [into] goes. *) -let store env w into v = if hosting env into then move env w v +let store env w into v = if owning env into then move env w v + +(* ---------------------------------------------------------------------- *) +(* Spent places *) +(* ---------------------------------------------------------------------- *) + +(* A use of [l] at field [path] ([] for the symbol itself). *) +let use env w (e : T.Expr.t) (l : T.Local.t) path = + let name = Env.quote l.T.Local.name in + match Hashtbl.find_opt w.spent l.T.Local.id with + | Some at -> + Env.error env e.T.Expr.span + (Printf.sprintf "%s was moved on line %d, and is spent until a store refills it" name (line at)) + | None -> ( + let moved = Option.value ~default:[] (Hashtbl.find_opt w.partly l.T.Local.id) in + match List.find_opt (fun (p, _) -> overlaps p path) moved with + | Some (p, at) -> + Env.error env e.T.Expr.span + (if path = [] then + Printf.sprintf + "%s is partly spent: its field %s was moved on line %d, and the owner is spent \ + as a whole until a store refills that field" + name (Env.quote (dotted p)) (line at) + else + Printf.sprintf "%s was moved on line %d, and is spent until a store refills it" + (Env.quote (dotted (l.T.Local.name :: p))) (line at)) + | None -> ()) (* ---------------------------------------------------------------------- *) (* Walking a body *) @@ -93,14 +172,9 @@ let store env w into v = if hosting env into then move env w v place, so whether it moves is the outer store's question. *) let rec expr env ?(into = Ty.Error) w (e : T.Expr.t) = match e.T.Expr.node with - | T.Expr.Var (T.Name_ref.Local l) -> ( - match Hashtbl.find_opt w.spent l.T.Local.id with - | Some at -> - Env.error env e.T.Expr.span - (Printf.sprintf - "%s was moved on line %d, and is spent until a store refills it" - (Env.quote l.T.Local.name) (line at)) - | None -> ()) + | T.Expr.Var (T.Name_ref.Local l) -> use env w e l [] + | T.Expr.Field _ when Option.is_some (States.field_path e) -> ( + match States.field_path e with Some (l, path) -> use env w e l path | None -> ()) | T.Expr.Integer_lit _ | T.Expr.Decimal_lit _ | T.Expr.Text_lit _ | T.Expr.Bool_lit _ | T.Expr.Type_arg _ | T.Expr.Enum_member _ | T.Expr.Invalid | T.Expr.Var _ -> () @@ -121,7 +195,7 @@ let rec expr env ?(into = Ty.Error) w (e : T.Expr.t) = value env w v item) entries | T.Expr.Case { case; payload } -> - value env w (Option.value ~default:Ty.Error (Guests.member_type env e.T.Expr.ty case)) payload + value env w (Option.value ~default:Ty.Error (References.member_type env e.T.Expr.ty case)) payload | T.Expr.Field { target; _ } | T.Expr.Map_read { target; _ } | T.Expr.Ref target | T.Expr.Spawn target -> expr env w target @@ -130,11 +204,10 @@ let rec expr env ?(into = Ty.Error) w (e : T.Expr.t) = (* A case read is a place, and so is what its handler resolves: the store it feeds decides whether anything moves. *) handler_block env w Ty.Error handler - | T.Expr.Init fields -> fields_ env w e.T.Expr.ty fields + | T.Expr.Init fields -> + fields_ env w (fun f -> References.member_type env e.T.Expr.ty f.T.Field_value.name) fields | T.Expr.Construct_fields { ctor; fields; handler } -> - fields_ env w - (match Env.signature_of env ctor with Some sg -> sg.S.ret | None -> e.T.Expr.ty) - fields; + fields_ env w (References.entry_type env ctor) fields; opt_handler env w into handler | T.Expr.Match m -> List.iter (expr env w) m.T.Match.scrutinees; @@ -143,7 +216,7 @@ let rec expr env ?(into = Ty.Error) w (e : T.Expr.t) = let binders = List.filter_map (fun (p : T.Pattern.t) -> p.T.Pattern.binder) a.T.Arm.patterns in - List.iter (fun (b : T.Local.t) -> Hashtbl.replace w.binders b.T.Local.id ()) binders; + List.iter (States.binder w.states) binders; (* A `return` in an arm gives the arm's value (docs/design/semantics.md §9). *) let verb = w.ret in @@ -160,7 +233,7 @@ let rec expr env ?(into = Ty.Error) w (e : T.Expr.t) = rest | _ -> args in - pass env w (Guests.param_types env callee) args; + pass env w (References.param_types env callee) args; opt_handler env w into handler | T.Expr.Call_value { callee; args; handler } -> expr env w callee; @@ -173,14 +246,14 @@ let rec expr env ?(into = Ty.Error) w (e : T.Expr.t) = opt_handler env w into handler | T.Expr.Subscript { target; impl; args } -> expr env w target; - pass env w (Guests.param_types env impl) (List.map (fun a -> T.Arg.Value a) args) + pass env w (References.param_types env impl) (List.map (fun a -> T.Arg.Value a) args) | T.Expr.Op { left; right; impl; swapped; handler; _ } -> let args = if swapped then [ right; left ] else [ left; right ] in let tys = match Env.signature_of env impl with (* An intrinsic operator reads its operands (docs/design/semantics.md §9). *) | Some { S.owner = S.Intrinsic _; _ } | None -> [] - | Some _ -> Guests.param_types env ~subject:true impl + | Some _ -> References.param_types env ~subject:true impl in pass env w tys (List.map (fun a -> T.Arg.Value a) args); opt_handler env w into handler @@ -192,19 +265,19 @@ let rec expr env ?(into = Ty.Error) w (e : T.Expr.t) = (* An intrinsic operator or constructor reads what it is given. *) and read_by env (r : T.Verb_ref.t) = - match r.T.Verb_ref.owner with S.Intrinsic _ -> [] | S.Declared _ -> Guests.param_types env r + match r.T.Verb_ref.owner with S.Intrinsic _ -> [] | S.Declared _ -> References.param_types env r and value env w into v = expr env ~into w v; store env w into v -(* Arguments in order, each read and then, for a `T` parameter, moved: a +(* Arguments in order, each read and then, for a `^T` parameter, moved: a symbol passed twice is spent by the first. *) and pass env w tys args = let rec go tys args = match (tys, args) with | ty :: tys, T.Arg.Value v :: args -> - value env w ty v; + value env w (taken env ty) v; go tys args | _ :: tys, a :: args -> arg env w a; @@ -218,11 +291,15 @@ and pass env w tys args = and arg env w = function T.Arg.Value e -> expr env w e | T.Arg.Block b -> block env w b -and fields_ env w ty fields = +(* `init{ }` entries fill the fields of what it builds, and a field + constructor's entries are its parameters (types.md §3.3); [slot_type] + gives each one's type. They run in the order written, so an entry that + moves a symbol spends it for every entry after it. *) +and fields_ env w slot_type fields = List.iter (fun (f : T.Field_value.t) -> - let into = Guests.member_type env ty f.T.Field_value.name in - value env w (Option.value ~default:Ty.Error into) f.T.Field_value.value) + let into = Option.value ~default:Ty.Error (slot_type f) in + value env w into f.T.Field_value.value) fields and opt_handler env w ty = Option.iter (handler_block env w ty) @@ -238,9 +315,7 @@ and lambda env w (e : T.Expr.t) (l : T.Lambda.t) = | Ty.Verb v -> (Option.is_some v.Ty.this_, v.Ty.ret) | _ -> (false, Ty.Error) in - (match l.T.Lambda.params with - | this :: _ when has_this -> Hashtbl.replace w.subjects this.T.Local.id () - | _ -> ()); + params w has_this l.T.Lambda.params; let saved = (w.ret, w.resolve) in w.ret <- ret; w.resolve <- []; @@ -266,56 +341,74 @@ and stat env w (s : T.Stat.t) = Hashtbl.replace w.declared local.T.Local.id w.block | T.Stat.Assign { target; value = v } -> ( value env w target.T.Expr.ty v; - match target.T.Expr.node with - | T.Expr.Var (T.Name_ref.Local l) when Hashtbl.mem w.spent l.T.Local.id -> - (* A store into a spent symbol refills it (§1.6). *) - if Hashtbl.find_opt w.declared l.T.Local.id = Some w.block then - Hashtbl.remove w.spent l.T.Local.id - else - Env.error env target.T.Expr.span - (Printf.sprintf - "%s is spent, and a store that refills it must be in the block that declares it" - (Env.quote l.T.Local.name)) - | T.Expr.Var _ -> () - | _ -> expr env w target) + match States.field_path target with + | Some (l, path) -> refill env w target l path + | None -> expr env w target) | T.Stat.Return e -> value env w w.ret e | T.Stat.Resolve e -> value env w (match w.resolve with ty :: _ -> ty | [] -> Ty.Error) e +(* A store into a symbol, or into a field of one: an owner it holds is + overwritten, and a spent symbol or field is refilled, which is confined + to the symbol's declaring block (§1.6). What the store reads of the + symbol on its way down is only where it writes, not a use. *) +and refill env w (target : T.Expr.t) (l : T.Local.t) path = + let moved = Option.value ~default:[] (Hashtbl.find_opt w.partly l.T.Local.id) in + let refilled = List.filter (fun (p, _) -> overlaps p path && List.length p >= List.length path) moved in + let spent = Hashtbl.mem w.spent l.T.Local.id in + let blocked = List.filter (fun (p, _) -> overlaps p path && List.length p < List.length path) moved in + if spent && path <> [] then use env w target l path + else if blocked <> [] then use env w target l path + else if spent || refilled <> [] then + if Hashtbl.find_opt w.declared l.T.Local.id = Some w.block then begin + Hashtbl.remove w.spent l.T.Local.id; + Hashtbl.replace w.partly l.T.Local.id (List.filter (fun e -> not (List.memq e refilled)) moved) + end + else + Env.error env target.T.Expr.span + (Printf.sprintf + "%s is spent, and a store that refills it must be in the block that declares %s" + (Env.quote (dotted (l.T.Local.name :: path))) (Env.quote l.T.Local.name)) + +and params w has_this (ps : T.Local.t list) = + List.iteri + (fun i (p : T.Local.t) -> + if i = 0 && has_this then States.subject w.states p else States.param w.states p) + ps + (* ---------------------------------------------------------------------- *) (* Declarations *) (* ---------------------------------------------------------------------- *) -let fresh blocks ret = +let fresh projections blocks ret = { blocks; declared = Hashtbl.create 32; spent = Hashtbl.create 8; - subjects = Hashtbl.create 2; - binders = Hashtbl.create 8; + partly = Hashtbl.create 4; + states = States.create projections; block = 0; ret; resolve = []; } -let verb env blocks (sg : S.t) (params : T.Local.t list) body = - let w = fresh blocks sg.S.ret in - (match params with - | this :: _ when S.is_method sg -> Hashtbl.replace w.subjects this.T.Local.id () - | _ -> ()); - block env ~bind:params w body +let verb env projections blocks (sg : S.t) (ps : T.Local.t list) body = + let w = fresh projections blocks sg.S.ret in + params w (S.is_method sg || sg.S.kind = S.Subscript) ps; + block env ~bind:ps w body let run env (p : T.Program.t) = + let projections = States.projections p in (* Blocks are numbered across the whole run. *) let blocks = ref 0 in - let fresh = fresh blocks in + let fresh = fresh projections blocks in List.iter (fun (pkg : T.Package.t) -> List.iter (fun (d : T.Decl.t) -> match d.T.Decl.node with | T.Decl.Verb { signature; body = T.Decl.Checked { params; body } } -> - verb env blocks signature params body + verb env projections blocks signature params body (* A subscript's body is a place, not a value it hands over (functions.md §2.9): it is read, and moves nothing out. *) | T.Decl.Subscript { value = Some v; _ } -> expr env (fresh Ty.Error) v @@ -337,5 +430,5 @@ let run env (p : T.Program.t) = (fun (i : T.Instance.t) -> let sg = i.T.Instance.signature in let sg = if sg.S.kind = S.Subscript then { sg with S.ret = Ty.Error } else sg in - verb env blocks sg i.T.Instance.params i.T.Instance.body) + verb env projections blocks sg i.T.Instance.params i.T.Instance.body) p.T.Program.instances diff --git a/lib/tst/analyses/read_only.ml b/lib/tst/analyses/read_only.ml index 62297329..c6d0b40f 100644 --- a/lib/tst/analyses/read_only.ml +++ b/lib/tst/analyses/read_only.ml @@ -1,17 +1,17 @@ -(* Read-only guests (effects.md §4.4): the first analysis over the finished +(* Read-only references (effects.md §4.4): the first analysis over the finished TST (docs/design/semantics.md D1). - A guest derived from a read-only binding is read-only wherever it goes: + A reference derived from a read-only binding is read-only wherever it goes: bound to a local, stored in a field, passed as an argument, or returned. §4.1 is checked while the tree is built, because it needs nothing but the place a write names: a parameter, or something reached through one. This - rule needs to know where a guest went, which the tree only shows once every + rule needs to know where a reference went, which the tree only shows once every call in it has a callee. So it runs after, over the whole program. - What it tracks is a [taint]: for a value, the places in it that hold a guest + What it tracks is a [taint]: for a value, the places in it that hold a reference taken from a parameter. Each [elem] says that at [at] inside the value sits something reached from parameter [origin] at [src]. A value type carries - nothing, because a value is copied deep and holds no guest (memory.md + nothing, because a value is copied deep and holds no reference (memory.md §2.10), so storing one drops its taint. A verb's [summary] is what a call needs without the body: which parameters @@ -52,9 +52,9 @@ let rec is_prefix p q = let rec drop p q = match (p, q) with _ :: p, _ :: q -> drop p q | _, q -> q -(* The taint of the part of a value at [p]. A guest at [p] or below stays, - re-rooted; a guest above [p] covers it, since [p] is then reached through - that guest. *) +(* The taint of the part of a value at [p]. A reference at [p] or below stays, + re-rooted; a reference above [p] covers it, since [p] is then reached through + that reference. *) let navigate s p = Taint.fold (fun e acc -> @@ -72,10 +72,11 @@ let whole s = Taint.map (fun e -> elem e.origin e.src []) s (* Types *) (* ---------------------------------------------------------------------- *) -(* Whether storage of type [t] can hold a guest. *) +(* Whether storage of type [t] can hold a reference. *) let rec carries env ?(seen = []) (t : Ty.t) = match t with - | Ty.Guest _ | Ty.Param _ -> true + | Ty.Reference _ | Ty.Param _ -> true + | Ty.Roaming t -> carries env ~seen t | Ty.Error | Ty.Verb _ | Ty.Concept _ -> false | Ty.Intrinsic { args; _ } -> List.exists (carries_arg env ~seen) args | Ty.Named (tid, args) -> ( @@ -99,7 +100,7 @@ and carries_arg env ~seen = function Ty.Type t -> carries env ~seen t | Ty.Numbe let store env t s = if carries env t then s else Taint.empty (* The declared type of a struct's field, which is what decides whether the - field can hold a guest: the value written into it may be a place a guest + field can hold a reference: the value written into it may be a place a reference is minted from, whose own type says nothing about that. *) let field_type env (t : Ty.t) name = match t with @@ -199,7 +200,7 @@ let taint_by_id w id = Option.value ~default:Taint.empty (Hashtbl.find_opt w.tai let taint_of w (l : T.Local.t) = taint_by_id w l.T.Local.id (* §4.1 already reported a write whose place is reached straight from a - read-only parameter; this rule adds the guests derived from one. *) + read-only parameter; this rule adds the references derived from one. *) let direct w (l : T.Local.t) = match Hashtbl.find_opt w.origins l.T.Local.id with Some b -> b.read_only | None -> false @@ -392,7 +393,7 @@ and call env w (e : T.Expr.t) (callee : T.Verb_ref.t) taints args = (* What a verb with no body to summarise -- an intrinsic, a function value -- may have kept of its arguments: anything a parameter's type can hold a - guest in. A parameter whose type cannot hold one keeps nothing. *) + reference in. A parameter whose type cannot hold one keeps nothing. *) and passed env tys taints = let rec go tys taints = match (tys, taints) with @@ -402,7 +403,7 @@ and passed env tys taints = in go tys taints -(* A `!` call writes its subject: it may not reach a read-only guest, and what +(* A `!` call writes its subject: it may not reach a read-only reference, and what the call stores in it is now there. *) and write_subject env w args taints into = match (args, taints) with @@ -411,7 +412,7 @@ and write_subject env w args taints into = (match place subject with Some (l, p) -> add w l p into | None -> ()) | _ -> () -(* effects.md §4.4: a `!` call is an error when its subject reaches a guest +(* effects.md §4.4: a `!` call is an error when its subject reaches a reference derived from a read-only binding. *) and check_subject env w (subject : T.Expr.t) own = let reaches = @@ -422,7 +423,7 @@ and check_subject env w (subject : T.Expr.t) own = in if read_only w reaches then error env w subject.T.Expr.span - (Printf.sprintf "a `!` call writes its subject, and it reaches a guest taken from %s" + (Printf.sprintf "a `!` call writes its subject, and it reaches a reference taken from %s" (blame w reaches)) and opt_handler env w = function Some h -> handler_value env w h | None -> Taint.empty @@ -481,8 +482,8 @@ and stat env w (s : T.Stat.t) = let v = store env target.T.Expr.ty (expr env w value) in match place target with | Some (l, []) when not (direct w l) -> Hashtbl.replace w.taints l.T.Local.id v - (* A store through a guest is rejected whatever the guest was taken - from (lifetimes.md §1.1, [Guests]), so only a `!` call is left for + (* A store through a reference is rejected whatever the reference was taken + from (lifetimes.md §1.1, [References]), so only a `!` call is left for this rule. *) | Some (l, p) -> add w l p v | None -> ignore (expr env w target)) diff --git a/lib/tst/analyses/guests.ml b/lib/tst/analyses/references.ml similarity index 50% rename from lib/tst/analyses/guests.ml rename to lib/tst/analyses/references.ml index d7871da5..e6bc2135 100644 --- a/lib/tst/analyses/guests.ml +++ b/lib/tst/analyses/references.ml @@ -1,49 +1,43 @@ -(* Where a guest may come from, and where it may be stored: the local half of - the store rules, an analysis over the finished TST (docs/design/semantics.md D1). - - Three rules, each decided by the store it looks at alone: - - - A new guest is minted only from a stable place (memory.md §2.8): a - symbol, or fields reached from one, with no `[]` and no variant case on - the way. A value that is already a guest is copied, and may come from - anywhere. - - A swallowed parameter is never bound into `&` storage (memory.md §2.9). - Its value is hosted at the call site, which has given up its host. - - A store never goes through a guest (lifetimes.md §1.1). What lies beyond - one belongs to a tree the path's root does not name. A guest parameter, - `this` included, may be the root: the call site settles a store through - it. - - The owner comparison of lifetimes.md §1.1 and where a parameter comes to - rest (§1.11) need more than the store in hand, and are not here. *) +(* Where a reference may come from, and where one may be stored: the local + half of the store rules, an analysis over the finished TST + (docs/design/semantics.md D1). + + Two rules, each decided by the store it looks at alone: + + - A new reference is minted only from a settled place (memory.md §2.8): a + settled symbol or package constant, or a path from one -- or from an + `&T` parameter, or through any reference -- that passes only through + struct fields and `ArrayRef` elements. A roaming owner may still move, a + list's element and a variant's payload come and go, a borrow is the + caller's for the call, and a temporary is no place at all. A value that + is already a reference is copied, and may come from anywhere. A declared + subscript is followed to the projection its body ends at (functions.md + §2.9). + - A store never goes through a reference (lifetimes.md §1.1). What lies + beyond one belongs to a tree the path's root does not name. A reference + parameter, or `this`, may be the root: the call site settles a store + through it. + + Every borrow falls out of the first rule and the move rule together + (moves.ml): it is no place to mint from and no owner to move, so it is + never stored or returned. The scope comparison of lifetimes.md §1.1, and + where a parameter comes to rest (§1.11), need more than the store in hand, + and are in scopes.ml. *) module T = Nodes module S = Signature -type param_kind = This | Guest | Swallow | Borrow - type walk = { - params : (int, string * param_kind) Hashtbl.t; - (* A match binder is its case's payload (adt.md §5), so it is not stable. *) - binders : (int, unit) Hashtbl.t; - (* A guest local that may name a swallowed parameter's value, by that - parameter. A local is never taken off it, so where a block argument runs - again does not matter. *) - swallowed : (int, string) Hashtbl.t; - (* A body is walked until [swallowed] stops growing, and reports only on - the walk after. *) - mutable report : bool; + states : States.t; (* Where a `return` and a `resolve` send their value. *) mutable ret : Ty.t; mutable resolve : Ty.t list; } -let error env w span message = if w.report then Env.error env span message - (* The declared type of a struct field or a variant case, with the type's arguments substituted. *) let member_type env (t : Ty.t) name = - match Ty.strip_guest t with + match Ty.strip_mode t with | Ty.Named (tid, args) -> ( match Hashtbl.find_opt env.Env.type_infos_by_id tid with | Some { Env.definition = Some (Env.Struct ms | Env.Variant ms); params; _ } -> ( @@ -55,7 +49,7 @@ let member_type env (t : Ty.t) name = (* A call's parameter types, at the generic arguments it was instantiated with. The subject is left out unless [subject]: a method takes its subject - as a guest, and never mints one for it (memory.md §2.9). *) + as a borrow, and never mints a reference for it (memory.md §2.9). *) let param_types env ?(subject = false) (r : T.Verb_ref.t) = match Env.signature_of env r with | None -> [] @@ -66,89 +60,70 @@ let param_types env ?(subject = false) (r : T.Verb_ref.t) = let s = List.map (fun ((p : Ty.param), a) -> (p.Ty.id, a)) r.T.Verb_ref.instance in List.map (fun (p : S.param) -> Ty.subst s p.S.ty) ps +(* A field constructor's entry is its parameter in the entry's slot + (types.md §3.3). *) +let entry_type env (r : T.Verb_ref.t) (f : T.Field_value.t) = + List.nth_opt (param_types env r) f.T.Field_value.slot + (* ---------------------------------------------------------------------- *) -(* The three rules *) +(* The two rules *) (* ---------------------------------------------------------------------- *) -(* Whether a path is a stable place a guest may be minted from. A path that - starts at a value which is already a guest -- a call that returns one -- - starts at stable storage. *) -let rec source w (e : T.Expr.t) = - match e.T.Expr.node with - | T.Expr.Var (T.Name_ref.Local l) when Hashtbl.mem w.binders l.T.Local.id -> `Case - | T.Expr.Var _ | T.Expr.Invalid -> `Stable - | T.Expr.Field { target; _ } -> source w target - | T.Expr.Subscript _ -> `Subscript - | T.Expr.Case_read _ -> `Case - | _ when Ty.is_guest e.T.Expr.ty -> `Stable - | _ -> `Temporary - let mint env w (v : T.Expr.t) = - let say = error env w v.T.Expr.span in - match source w v with - | `Stable -> () - | `Subscript -> say "a new guest must come from a stable place, and this path crosses `[]`" - | `Case -> - say "a new guest must come from a stable place, and a variant case's payload is not one" - | `Temporary -> say "a new guest names a place, and this is a temporary value" - -let rec root (e : T.Expr.t) = - match e.T.Expr.node with - | T.Expr.Var (T.Name_ref.Local l) -> Some l - | T.Expr.Field { target; _ } | T.Expr.Subscript { target; _ } | T.Expr.Case_read { target; _ } - -> - root target - | _ -> None - -(* The swallowed parameter a value is reached from, directly or through a - guest local that holds one. *) -let swallowed_of w (v : T.Expr.t) = - match root v with - | Some l -> ( - match Hashtbl.find_opt w.params l.T.Local.id with - | Some (name, Swallow) -> Some name - | _ -> Hashtbl.find_opt w.swallowed l.T.Local.id) - | None -> None - -let hold w (l : T.Local.t) (v : T.Expr.t) = - if Ty.is_guest l.T.Local.ty && not (Hashtbl.mem w.swallowed l.T.Local.id) then - Option.iter (Hashtbl.replace w.swallowed l.T.Local.id) (swallowed_of w v) - -(* A value [v] stored where a value of type [into] goes. [storage] is a field, - an element or a case payload: somewhere a guest comes to rest, rather than - a local or an argument. *) -let store env w ?(storage = false) (into : Ty.t) (v : T.Expr.t) = - if Ty.is_guest into then begin - (match v.T.Expr.ty with - | Ty.Guest _ | Ty.Error | Ty.Param _ -> () - | _ -> mint env w v); - if storage then - match swallowed_of w v with - | Some name -> - error env w v.T.Expr.span - (Printf.sprintf - "%s swallows its argument, so it may not be bound into `&` storage; a \ - parameter stored as a guest is declared `&`" - (Env.quote name)) - | None -> () - end - -(* lifetimes.md §1.1: no step of a store's destination goes through a guest, - unless it is the root and a parameter. What a guest names is changed by a - `mut` call on it (effects.md §4.3), which the message points to. *) + let say = Env.error env v.T.Expr.span in + match (States.state w.states v, v.T.Expr.node) with + | States.Settled, _ -> () + | States.Roaming, _ -> + say + "a new reference must come from a settled place, and this is a roaming owner, which \ + may still move; settle it first by moving it into an owner declared without `^`" + | States.Borrowed, _ + when match States.field_path v with + | Some (l, _) -> Hashtbl.mem w.states.States.subjects l.T.Local.id + | None -> false -> + say + "`this` is a borrow, never a source of a reference; a verb that hands out a reference \ + into an object takes the object as an `&T` parameter" + | States.Borrowed, _ -> + say + "a new reference must come from a settled place, and this is a borrow, which is the \ + caller's only for the call; take the parameter as `&T` to keep a reference to it" + | States.Contingent, (T.Expr.Case_read _ | T.Expr.Var _) -> + say + "a new reference must come from a settled place, and a variant case's payload is \ + roaming: the variant may change case while it lives" + | States.Contingent, T.Expr.Subscript _ -> + say + "a new reference must come from a settled place, and a list's element is roaming: \ + elements come and go while the list lives; an `ArrayRef`'s elements may be referenced" + | States.Contingent, _ -> + say "a new reference must come from a settled place, and this is part of a temporary value" + | States.Fresh, _ -> say "a new reference names a place, and this is a temporary value" + +(* A value [v] stored where a value of type [into] goes. *) +let store env w (into : Ty.t) (v : T.Expr.t) = + if Ty.is_ref into then + match v.T.Expr.ty with Ty.Reference _ | Ty.Error | Ty.Param _ -> () | _ -> mint env w v + +(* lifetimes.md §1.1: no step of a store's destination goes through a + reference, unless it is the root and a parameter. What a reference names + is changed by a `mut` call on it (effects.md §4.3), which the message + points to. *) let rec through env w (e : T.Expr.t) = match e.T.Expr.node with | T.Expr.Field { target; _ } | T.Expr.Subscript { target; _ } | T.Expr.Case_read { target; _ } -> let param_root = match target.T.Expr.node with - | T.Expr.Var (T.Name_ref.Local l) -> Hashtbl.mem w.params l.T.Local.id + | T.Expr.Var (T.Name_ref.Local l) -> + Hashtbl.mem w.states.States.params l.T.Local.id + || Hashtbl.mem w.states.States.subjects l.T.Local.id | _ -> false in - if Ty.is_guest target.T.Expr.ty && not param_root then - error env w target.T.Expr.span - "a store may not go through a guest, since what it names belongs to a tree this \ - path's root does not own; change it with a `mut` method called through the guest" + if Ty.is_ref target.T.Expr.ty && not param_root then + Env.error env target.T.Expr.span + "a store may not go through a reference, since what it names belongs to a tree this \ + path's root does not hold; change it with a `mut` method called through the reference" else through env w target | _ -> () @@ -161,7 +136,15 @@ let rec expr env w (e : T.Expr.t) = | T.Expr.Integer_lit _ | T.Expr.Decimal_lit _ | T.Expr.Text_lit _ | T.Expr.Bool_lit _ | T.Expr.Type_arg _ | T.Expr.Enum_member _ | T.Expr.Invalid | T.Expr.Var _ -> () - | T.Expr.Array_lit items -> List.iter (expr env w) items + | T.Expr.Array_lit items -> + let element = + match e.T.Expr.ty with Ty.Concept (Ty.Array_lit (t, _)) -> t | _ -> Ty.Error + in + List.iter + (fun i -> + expr env w i; + store env w element i) + items | T.Expr.Map_lit entries -> List.iter (fun (k, v) -> @@ -170,7 +153,7 @@ let rec expr env w (e : T.Expr.t) = entries | T.Expr.Case { case; payload } -> expr env w payload; - Option.iter (fun t -> store env w ~storage:true t payload) (member_type env e.T.Expr.ty case) + Option.iter (fun t -> store env w t payload) (member_type env e.T.Expr.ty case) | T.Expr.Field { target; _ } | T.Expr.Map_read { target; _ } -> expr env w target | T.Expr.Case_read { target; handler; _ } -> expr env w target; @@ -179,19 +162,17 @@ let rec expr env w (e : T.Expr.t) = expr env w inner; store env w e.T.Expr.ty inner | T.Expr.Spawn inner -> expr env w inner - | T.Expr.Init fields -> fields_ env w e.T.Expr.ty fields + | T.Expr.Init fields -> + fields_ env w (fun f -> member_type env e.T.Expr.ty f.T.Field_value.name) fields | T.Expr.Construct_fields { ctor; fields; handler } -> - fields_ env w (match Env.signature_of env ctor with Some sg -> sg.S.ret | None -> e.T.Expr.ty) fields; + fields_ env w (entry_type env ctor) fields; opt_handler env w e.T.Expr.ty handler | T.Expr.Match m -> List.iter (expr env w) m.T.Match.scrutinees; List.iter (fun (a : T.Arm.t) -> List.iter - (fun (p : T.Pattern.t) -> - Option.iter - (fun (b : T.Local.t) -> Hashtbl.replace w.binders b.T.Local.id ()) - p.T.Pattern.binder) + (fun (p : T.Pattern.t) -> Option.iter (States.binder w.states) p.T.Pattern.binder) a.T.Arm.patterns; (* A `return` in an arm gives the arm's value (docs/design/semantics.md §9). *) @@ -256,12 +237,12 @@ and pass env w tys args = and arg env w = function T.Arg.Value e -> expr env w e | T.Arg.Block b -> block env w b -and fields_ env w ty fields = +and fields_ env w slot_type fields = List.iter (fun (f : T.Field_value.t) -> let v = f.T.Field_value.value in expr env w v; - Option.iter (fun t -> store env w ~storage:true t v) (member_type env ty f.T.Field_value.name)) + Option.iter (fun t -> store env w t v) (slot_type f)) fields and opt_handler env w ty = Option.iter (handler_block env w ty) @@ -278,7 +259,7 @@ and lambda env w (e : T.Expr.t) (l : T.Lambda.t) = | _ -> (false, Ty.Error) in let saved = (w.ret, w.resolve) in - params env w has_this l.T.Lambda.params; + params w has_this l.T.Lambda.params; w.ret <- ret; w.resolve <- []; block env w l.T.Lambda.body; @@ -292,17 +273,12 @@ and stat env w (s : T.Stat.t) = | T.Stat.Expr e | T.Stat.Spawn e | T.Stat.Abort e -> expr env w e | T.Stat.Let { local; value } -> expr env w value; - store env w local.T.Local.ty value; - hold w local value + store env w local.T.Local.ty value | T.Stat.Assign { target; value } -> expr env w value; expr env w target; through env w target; - (match target.T.Expr.node with - | T.Expr.Var (T.Name_ref.Local l) -> hold w l value - | _ -> ()); - let storage = match target.T.Expr.node with T.Expr.Var _ -> false | _ -> true in - store env w ~storage target.T.Expr.ty value + store env w target.T.Expr.ty value | T.Stat.Return e -> expr env w e; store env w w.ret e @@ -310,79 +286,50 @@ and stat env w (s : T.Stat.t) = expr env w e; match w.resolve with ty :: _ -> store env w ty e | [] -> ()) -and params env w has_this (ps : T.Local.t list) = +and params w has_this (ps : T.Local.t list) = List.iteri (fun i (p : T.Local.t) -> - let kind = - if i = 0 && has_this then This - else if Ty.is_guest p.T.Local.ty then Guest - else if Type_decls.is_reference env p.T.Local.ty then Swallow - else Borrow - in - Hashtbl.replace w.params p.T.Local.id (p.T.Local.name, kind)) + if i = 0 && has_this then States.subject w.states p else States.param w.states p) ps (* ---------------------------------------------------------------------- *) (* Declarations *) (* ---------------------------------------------------------------------- *) -let fresh ret = - { - params = Hashtbl.create 8; - binders = Hashtbl.create 8; - swallowed = Hashtbl.create 8; - report = false; - ret; - resolve = []; - } +let fresh projections ret = { states = States.create projections; ret; resolve = [] } -(* Walk until no guest local is found to hold a swallowed parameter it was - not known to, then once more to report. *) -let walk ret run = - let w = fresh ret in - let rec settle () = - let before = Hashtbl.length w.swallowed in - w.ret <- ret; - run w; - if Hashtbl.length w.swallowed > before then settle () - in - settle (); - w.report <- true; - w.ret <- ret; +let verb env projections (sg : S.t) ps run = + let w = fresh projections sg.S.ret in + params w (S.is_method sg || sg.S.kind = S.Subscript) ps; run w -let verb env (sg : S.t) ps run = - walk sg.S.ret (fun w -> - params env w (S.is_method sg) ps; - run w) - let run env (p : T.Program.t) = + let projections = States.projections p in List.iter (fun (pkg : T.Package.t) -> List.iter (fun (d : T.Decl.t) -> match d.T.Decl.node with | T.Decl.Verb { signature; body = T.Decl.Checked { params; body } } -> - verb env signature params (fun w -> block env w body) + verb env projections signature params (fun w -> block env w body) | T.Decl.Subscript { signature; params; value = Some v } -> - verb env signature params (fun w -> - expr env w v; - store env w w.ret v) + verb env projections signature params (fun w -> expr env w v) | T.Decl.Constant { ty; value; _ } -> - walk ty (fun w -> - expr env w value; - store env w ty value) + let w = fresh projections ty in + expr env w value; + store env w ty value | T.Decl.Enum_map { ty; entries; _ } -> - walk ty (fun w -> - List.iter - (fun (_, v) -> - expr env w v; - store env w ty v) - entries) + let w = fresh projections ty in + List.iter + (fun (_, v) -> + expr env w v; + store env w ty v) + entries | _ -> ()) pkg.T.Package.decls) p.T.Program.packages; List.iter (fun (i : T.Instance.t) -> - verb env i.T.Instance.signature i.T.Instance.params (fun w -> block env w i.T.Instance.body)) + verb env projections i.T.Instance.signature i.T.Instance.params (fun w -> + block env w i.T.Instance.body)) p.T.Program.instances diff --git a/lib/tst/analyses/owners.ml b/lib/tst/analyses/scopes.ml similarity index 80% rename from lib/tst/analyses/owners.ml rename to lib/tst/analyses/scopes.ml index 06304344..f73b2eb2 100644 --- a/lib/tst/analyses/owners.ml +++ b/lib/tst/analyses/scopes.ml @@ -1,25 +1,27 @@ -(* Owners: a store may not raise a value above what it names (lifetimes.md +(* Scopes: a store may not raise a value above what it names (lifetimes.md §1.1, §1.4, §1.7, §1.10, §1.11). An analysis over the finished TST (docs/design/semantics.md D1). - Every place has an owner. A local is owned by the block that declares it, - and a field or element by its root symbol's owner. A parameter, `this` - included, and a constructor's `init{ }` stand for places in the caller's - frame: their owner is the call site, which outlives every block of the - body. + Every place has a scope. A local's is the block that declares it, and a + field's or element's is its root symbol's. A `^T` parameter is an owner of + the body, scoped to the body's top block (§1.5). Any other parameter, + `this` included, and a constructor's `init{ }` stand for places in the + caller's frame: their scope is the call site, which outlives every block + of the body. - What a value [names] is the owners of the hosts it reaches through a guest: - its own, if it is one, and those it carries (§1.10). A store -- a `let`, an - assignment, a field of `init{ }`, a return -- is legal only when every - owner the stored value names outlives the destination's. A block outlives - the blocks nested in it; the call site outlives the body. + What a value [names] is the scopes of the owners it reaches through a + reference: its own, if it is one, and those it carries (§1.10). A store -- + a `let`, an assignment, a field of `init{ }`, a return -- is legal only + when every scope the stored value names outlives the destination's. A + block outlives the blocks nested in it; the call site outlives the body. A local's names are the union of every value stored in it, at any path, so a body is walked until they stop growing and then once more to report. A - call's result names what its arguments name, and the owner of every place a - guest parameter mints one from, since a verb may return a guest rooted in - any parameter (§1.7). §1.4 needs no check of its own: a symbol moves only in - its declaring block, so the host it moves into is declared there or above. + call's result names what its arguments name, and the scope of every place + a reference parameter mints one from, since a verb may return a reference + rooted in any `&T` parameter (§1.7). §1.4 needs no check of its own: a + symbol moves only in its declaring block, so the owner it moves into is + declared there or above. A store the body cannot settle -- one from a parameter into a place reached from another -- is where the first comes to rest (§1.11), and is published @@ -33,7 +35,7 @@ module S = Signature (* [Param i] is the verb's parameter [i], the subject first: the caller's, like [Caller], but told apart from the others. *) -type owner = Caller | Global | Block of int | Param of int +type scope = Caller | Global | Block of int | Param of int (* Each parameter that comes to rest in a place reached from another, as the pair of their indices. *) @@ -50,9 +52,9 @@ let summary_of summaries (r : T.Verb_ref.t) = | S.Intrinsic "@primitives$push" -> Rests.singleton (1, 0) | S.Intrinsic _ -> Rests.empty -(* An owner, and the symbol it was found through, for the message. *) +(* A scope, and the symbol it was found through, for the message. *) module Names = Set.Make (struct - type t = owner * string + type t = scope * string let compare = compare end) @@ -68,6 +70,8 @@ type walk = { parent : (int, int) Hashtbl.t; (* Each parameter's index; a lambda's are -1, since no summary names them. *) params : (int, int) Hashtbl.t; + (* The `^T` parameters, which the body owns (§1.5). *) + roaming : (int, unit) Hashtbl.t; (* Where the verb's parameters come to rest. *) mutable rests : Rests.t; mutable block : int; @@ -87,7 +91,7 @@ type walk = { let rec within w b d = b = d || match Hashtbl.find_opt w.parent d with Some p -> within w b p | None -> false -(* Whether owner [a] outlives owner [d]: lives at least as long. *) +(* Whether scope [a] outlives scope [d]: lives at least as long. *) let outlives w a d = match (a, d) with | (Caller | Global | Param _), _ -> true @@ -107,7 +111,11 @@ let add w (l : T.Local.t) n = end end -let owner_of_local w (l : T.Local.t) = +(* A `^T` parameter is declared at the top of the body, which is block 1: + every walk numbers the body's own block first. *) +let scope_of_local w (l : T.Local.t) = + if Hashtbl.mem w.roaming l.T.Local.id then Block 1 + else match Hashtbl.find_opt w.params l.T.Local.id with | Some i when i >= 0 -> Param i | Some _ -> Caller @@ -117,24 +125,24 @@ let owner_of_local w (l : T.Local.t) = let result w (e : T.Expr.t) = Option.value ~default:Names.empty (Hashtbl.find_opt w.results e.T.Expr.span) -let carries env t = match t with Ty.Guest _ -> true | t -> Read_only.carries env t +let carries env t = match t with Ty.Reference _ -> true | t -> Read_only.carries env t let keep env t n = if carries env t then n else Names.empty (* ---------------------------------------------------------------------- *) (* What a value names *) (* ---------------------------------------------------------------------- *) -(* The owner of the host at place [e], for a guest minted from it. A step - through a guest leaves the root's tree for the one the guest names. *) -let rec host env w (e : T.Expr.t) = +(* The scope of the owner at place [e], for a reference minted from it. A + step through a reference leaves the root's tree for the one it names. *) +let rec place env w (e : T.Expr.t) = match e.T.Expr.node with - | T.Expr.Var (T.Name_ref.Local l) -> Names.singleton (owner_of_local w l, l.T.Local.name) + | T.Expr.Var (T.Name_ref.Local l) -> Names.singleton (scope_of_local w l, l.T.Local.name) | T.Expr.Var (T.Name_ref.Global { name; _ }) -> Names.singleton (Global, name) | T.Expr.Var _ -> Names.empty | T.Expr.Field { target; _ } | T.Expr.Subscript { target; _ } | T.Expr.Case_read { target; _ } -> - if Ty.is_guest target.T.Expr.ty then names env w target else host env w target - | _ -> if Ty.is_guest e.T.Expr.ty then names env w e else Names.empty + if Ty.is_ref target.T.Expr.ty then names env w target else place env w target + | _ -> if Ty.is_ref e.T.Expr.ty then names env w e else Names.empty (* What a value of an expression names, before any store. *) and names env w (e : T.Expr.t) = @@ -144,7 +152,7 @@ and names env w (e : T.Expr.t) = | T.Expr.Var (T.Name_ref.Global { name; _ }) -> Names.singleton (Global, name) | T.Expr.Field { target; _ } | T.Expr.Subscript { target; _ } -> names env w target | T.Expr.Case_read { target; _ } -> names env w target - | T.Expr.Ref inner -> if Ty.is_guest inner.T.Expr.ty then names env w inner else host env w inner + | T.Expr.Ref inner -> if Ty.is_ref inner.T.Expr.ty then names env w inner else place env w inner | T.Expr.Spawn inner -> names env w inner | T.Expr.Array_lit items -> let element = @@ -152,7 +160,7 @@ and names env w (e : T.Expr.t) = in List.fold_left (fun acc i -> Names.union acc (stored env w element i)) Names.empty items | T.Expr.Case { case; payload } -> - stored env w (Option.value ~default:payload.T.Expr.ty (Guests.member_type env e.T.Expr.ty case)) + stored env w (Option.value ~default:payload.T.Expr.ty (References.member_type env e.T.Expr.ty case)) payload | T.Expr.Init fields -> fields_names env w e.T.Expr.ty fields | T.Expr.Construct_fields { ctor; fields; _ } -> @@ -163,32 +171,33 @@ and names env w (e : T.Expr.t) = let subject, args = match (Env.signature_of env callee, args) with | Some sg, T.Arg.Value s :: rest when S.is_method sg -> - ((if Ty.is_guest s.T.Expr.ty then names env w s else host env w s), rest) + ((if Ty.is_ref s.T.Expr.ty then names env w s else place env w s), rest) | _ -> (Names.empty, args) in - Names.union subject (passed env w (Guests.param_types env callee) args) + Names.union subject (passed env w (References.param_types env callee) args) | T.Expr.Call_value { callee; args; _ } -> ( match callee.T.Expr.ty with | Ty.Verb { Ty.this_; params; _ } -> passed env w (Option.to_list this_ @ params) args | _ -> Names.empty) | T.Expr.Op { left; right; impl; swapped; _ } -> let args = if swapped then [ right; left ] else [ left; right ] in - passed env w (Guests.param_types env ~subject:true impl) (List.map (fun a -> T.Arg.Value a) args) + passed env w (References.param_types env ~subject:true impl) (List.map (fun a -> T.Arg.Value a) args) | T.Expr.Flip { impl; value; _ } | T.Expr.Coerce { ctor = impl; value } -> - passed env w (Guests.param_types env impl) [ T.Arg.Value value ] + passed env w (References.param_types env impl) [ T.Arg.Value value ] | _ -> Names.empty) (* A match's arms, and a handler's `resolve`, were walked before the store asked: what they handed on is kept by the expression's span. *) |> Names.union (keep env e.T.Expr.ty (result w e)) (* What a value names once stored where a value of type [into] goes: a - guest minted from a place names that place's host. *) + reference minted from a place names that place's owner. *) and stored env w into (v : T.Expr.t) = keep env into - (if Ty.is_guest into && not (Ty.is_guest v.T.Expr.ty) then host env w v else names env w v) + (if Ty.is_ref into && not (Ty.is_ref v.T.Expr.ty) then place env w v else names env w v) -(* A verb may hand back a guest rooted in any parameter, so its result - names what each argument names as that parameter takes it. *) +(* A verb may hand back a reference rooted in any `&T` parameter, or one a + parameter carries, so its result names what each argument names as that + parameter takes it. *) and passed env w tys args = let rec go tys args acc = match (tys, args) with @@ -203,7 +212,7 @@ and passed env w tys args = and fields_names env w ty fields = List.fold_left (fun acc (f : T.Field_value.t) -> - let into = Option.value ~default:Ty.Error (Guests.member_type env ty f.T.Field_value.name) in + let into = Option.value ~default:Ty.Error (References.member_type env ty f.T.Field_value.name) in Names.union acc (stored env w into f.T.Field_value.value)) Names.empty fields @@ -211,7 +220,7 @@ and fields_names env w ty fields = (* Stores *) (* ---------------------------------------------------------------------- *) -(* §1.1: every owner a stored value names outlives the destination's. One +(* §1.1: every scope a stored value names outlives the destination's. One parameter stored into another's place comes to rest there (§1.11), which only a call can settle. [via] says which call stored it. *) let check env ?(via = "") w (at : T.Expr.t) dest (what : string) n = @@ -225,21 +234,21 @@ let check env ?(via = "") w (at : T.Expr.t) dest (what : string) n = ((match dest with | Block _ -> Printf.sprintf - "this stores a guest to %s, whose block ends before %s's does" (Env.quote name) + "this stores a reference to %s, whose block ends before %s's does" (Env.quote name) what | Caller | Global | Param _ -> Printf.sprintf - "this stores a guest to %s in %s, which outlives the body %s is owned by" + "this stores a reference to %s in %s, which outlives the body %s is scoped to" (Env.quote name) what (Env.quote name)) ^ via)) n -(* The owner of an assignment's destination, the local it is reached from +(* The scope of an assignment's destination, the local it is reached from unless that is a parameter, and how a message names it. *) let rec destination w (e : T.Expr.t) = match e.T.Expr.node with | T.Expr.Var (T.Name_ref.Local l) -> - let o = owner_of_local w l in + let o = scope_of_local w l in let param = Hashtbl.mem w.params l.T.Local.id in Some ( o, @@ -283,7 +292,7 @@ let rec expr env w (e : T.Expr.t) = let v = f.T.Field_value.value in expr env w v; let into = - Option.value ~default:Ty.Error (Guests.member_type env e.T.Expr.ty f.T.Field_value.name) + Option.value ~default:Ty.Error (References.member_type env e.T.Expr.ty f.T.Field_value.name) in check env w v Caller "the object `init{ }` fills" (stored env w into v)) fields @@ -341,26 +350,26 @@ let rec expr env w (e : T.Expr.t) = | T.Expr.Lambda l -> (* A lambda captures nothing (concurrency.md §5.2): its parameters are its caller's, and so is its return. *) - List.iter - (fun (p : T.Local.t) -> Hashtbl.replace w.params p.T.Local.id (-1)) - l.T.Lambda.params; + (* A `^T` parameter is the body's own, declared in its top block. *) + let roaming, lent = List.partition (fun (p : T.Local.t) -> Ty.is_roaming p.T.Local.ty) l.T.Lambda.params in + List.iter (fun (p : T.Local.t) -> Hashtbl.replace w.params p.T.Local.id (-1)) lent; let ret = match e.T.Expr.ty with Ty.Verb v -> v.Ty.ret | _ -> Ty.Error in let saved = (w.ret, w.resolve) in w.ret <- Verb ret; w.resolve <- []; - block env w l.T.Lambda.body; + block env ~bind:roaming w l.T.Lambda.body; w.ret <- fst saved; w.resolve <- snd saved (* §1.11: each parameter the callee keeps is stored into the place the argument for the other names, and the local that place is in now names it - too. A subject is taken as a guest, never minted one for. *) + too. A subject is a borrow, never minted a reference for. *) and rest env w callee args = let rs = summary_of w.summaries callee in if not (Rests.is_empty rs) then begin let sg = Env.signature_of env callee in let method_ = match sg with Some sg -> S.is_method sg | None -> false in - let tys = Array.of_list (Guests.param_types env ~subject:true callee) in + let tys = Array.of_list (References.param_types env ~subject:true callee) in let args = Array.of_list args in let name i = match sg with @@ -374,7 +383,7 @@ and rest env w callee args = match (args.(i), args.(j)) with | T.Arg.Value v, T.Arg.Value d -> let n = - if method_ && i = 0 then if Ty.is_guest v.T.Expr.ty then names env w v else host env w v + if method_ && i = 0 then if Ty.is_ref v.T.Expr.ty then names env w v else place env w v else stored env w tys.(i) v in let via = @@ -391,20 +400,20 @@ and rest env w callee args = | Global | Block _ -> Env.quote root in check env ~via w v o what n) - (host env w d); + (place env w d); Option.iter (fun l -> add w l n) (root_local w d) | _ -> ()) rs end (* The local an argument is a place in, unless it is a parameter or the place - is reached through a guest: that local holds what is stored there. *) + is reached through a reference: that local holds what is stored there. *) and root_local w (e : T.Expr.t) = match e.T.Expr.node with | T.Expr.Var (T.Name_ref.Local l) when not (Hashtbl.mem w.params l.T.Local.id) -> Some l | T.Expr.Ref inner -> root_local w inner | T.Expr.Field { target; _ } | T.Expr.Subscript { target; _ } | T.Expr.Case_read { target; _ } - when not (Ty.is_guest target.T.Expr.ty) -> + when not (Ty.is_ref target.T.Expr.ty) -> root_local w target | _ -> None @@ -486,6 +495,7 @@ let fresh summaries = declared = Hashtbl.create 32; parent = Hashtbl.create 16; params = Hashtbl.create 8; + roaming = Hashtbl.create 4; rests = Rests.empty; results = Hashtbl.create 8; block = 0; @@ -503,6 +513,7 @@ let walk summaries ~report decl ret (params : T.Local.t list) body = List.iteri (fun i (p : T.Local.t) -> Hashtbl.replace w.params p.T.Local.id i; + if Ty.is_roaming p.T.Local.ty then Hashtbl.replace w.roaming p.T.Local.id (); Hashtbl.replace w.names p.T.Local.id (Names.singleton (Param i, p.T.Local.name))) params; let once () = diff --git a/lib/tst/analyses/spawns.ml b/lib/tst/analyses/spawns.ml index 75a4bfe9..c234df37 100644 --- a/lib/tst/analyses/spawns.ml +++ b/lib/tst/analyses/spawns.ml @@ -13,11 +13,11 @@ where it began; only a spawn written as a statement, or bound by a `let`, keeps one. - Such a spawn is also lent every host it is passed, directly or through a - guest, and may read it until the same drain. The spec leaves the lending + Such a spawn is also lent every owner it is passed, directly or through a + reference, and may read it until the same drain. The spec leaves the lending block free to write what it lent; here it may not, directly, through a `!` - call, by moving it, or through a guest that may name it - (docs/spec-divergences.md §14). *) + call, by moving it, or through a reference that may name it + (docs/spec-divergences.md §13). *) module T = Nodes module S = Signature @@ -129,7 +129,7 @@ let find_multi multi bodies = !changed (* The types a type holds by owning edges: its members, what it is distinct - from, a list's elements. A guest member holds none. *) + from, a list's elements. A reference member holds none. *) let members env (t : Ty.t) = match t with | Ty.Named (tid, args) -> ( @@ -150,13 +150,13 @@ let rec contains env ?(seen = []) outer inner = || (not (List.exists (Ty.equal outer) seen)) && List.exists (fun m -> - (match m with Ty.Guest _ -> false | _ -> true) + (match m with Ty.Reference _ -> false | _ -> true) && contains env ~seen:(outer :: seen) m inner) (members env outer) (* The types a place passes through, from the place itself to its root. *) let rec chain (e : T.Expr.t) = - Ty.strip_guest e.T.Expr.ty + Ty.strip_mode e.T.Expr.ty :: (match e.T.Expr.node with | T.Expr.Field { target; _ } | T.Expr.Subscript { target; _ } | T.Expr.Case_read { target; _ } @@ -164,12 +164,12 @@ let rec chain (e : T.Expr.t) = chain target | _ -> []) -(* A host lent to a spawn: its place when the checker knows it, the type of - the host, and the argument it was lent through. *) -type lend = { at : place option; host : Ty.t; through : T.Expr.t } +(* An owner lent to a spawn: its place when the checker knows it, the type of + the owner, and the argument it was lent through. *) +type lend = { at : place option; lent : Ty.t; through : T.Expr.t } (* A place as written, where it is when the checker can follow it through - guests, and the type of what it holds. *) + references, and the type of what it holds. *) type claim = { written : place; at : place option; ty : Ty.t } (* A block being walked: the locals declared in it, whether it runs more than @@ -182,7 +182,7 @@ type frame = { } (* Two claims on storage overlap when their places do. Where a place is - reached through a guest whose host the checker cannot follow, they may + reached through a reference whose owner the checker cannot follow, they may overlap when either's type may hold the other's. *) let clashes env a b = match (a.at, b.at) with @@ -206,27 +206,27 @@ let rec beside (e : T.Expr.t) = type piece = Plain of Exits.part | Run of T.Block.t * bool let walk_body env multi (body : T.Block.t) = - (* Where each guest local points, when the checker knows: the place it was - minted from, followed through other guests. *) + (* Where each reference local points, when the checker knows: the place it was + minted from, followed through other references. *) let origins : (int, place option) Hashtbl.t = Hashtbl.create 8 in let resolve p = match p.local.T.Local.ty with - | Ty.Guest _ -> ( + | Ty.Reference _ -> ( match Hashtbl.find_opt origins p.local.T.Local.id with | Some (Some o) -> Some { o with path = o.path @ p.path } | _ -> None) | _ -> Some p in - (* The host a guest-typed expression names, when known. *) + (* The owner a reference-typed expression names, when known. *) let origin (e : T.Expr.t) = match (e.T.Expr.ty, e.T.Expr.node) with - | Ty.Guest _, T.Expr.Var (T.Name_ref.Local l) -> ( + | Ty.Reference _, T.Expr.Var (T.Name_ref.Local l) -> ( match Hashtbl.find_opt origins l.T.Local.id with Some o -> o | None -> None) - | Ty.Guest _, _ -> None + | Ty.Reference _, _ -> None | _ -> Option.bind (place_of e) resolve in (* A write to the place [e], or a move out of it, while a spawn may still - read a host it was lent. Where either place is unknown, what might + read an owner it was lent. Where either place is unknown, what might overlap is decided by type. *) let write frames (e : T.Expr.t) = match place_of e with @@ -238,7 +238,7 @@ let walk_body env multi (body : T.Block.t) = match (written, l.at) with | Some w, Some a -> overlap w a | _ -> - contains env (Ty.strip_guest e.T.Expr.ty) l.host || List.exists (fun c -> contains env l.host c) types + contains env (Ty.strip_mode e.T.Expr.ty) l.lent || List.exists (fun c -> contains env l.lent c) types in match List.find_map (fun f -> List.find_opt clash f.lends) frames with | Some l -> @@ -249,19 +249,19 @@ let walk_body env multi (body : T.Block.t) = in Env.error env e.T.Expr.span (Printf.sprintf - "this writes %s, which may be part of a host lent through %s to a spawned call \ + "this writes %s, which may be part of an owner lent through %s to a spawned call \ that may read it until its block drains" (describe p) lent) | None -> ()) in - (* A host read from a place into hosting storage moves out of the place. *) + (* An owner read from a place into owning storage moves out of the place. *) let moved frames (e : T.Expr.t) = match e.T.Expr.ty with - | Ty.Guest _ -> () + | Ty.Reference _ -> () | t when Type_decls.is_reference env t -> write frames e | _ -> () in - let claim (e : T.Expr.t) p = { written = p; at = resolve p; ty = Ty.strip_guest e.T.Expr.ty } in + let claim (e : T.Expr.t) p = { written = p; at = resolve p; ty = Ty.strip_mode e.T.Expr.ty } in let rec block frames often (b : T.Block.t) = let f = { declared = Hashtbl.create 8; often; borrows = []; lends = [] } in let frames = f :: frames in @@ -275,20 +275,20 @@ let walk_body env multi (body : T.Block.t) = | T.Stat.Let { local; value } -> expr frames value; (match local.T.Local.ty with - | Ty.Guest _ -> Hashtbl.replace origins local.T.Local.id (origin value) + | Ty.Reference _ -> Hashtbl.replace origins local.T.Local.id (origin value) | _ -> moved frames value); declare frames local | T.Stat.Assign { target; value } -> expr frames value; (match (target.T.Expr.ty, target.T.Expr.node) with - | Ty.Guest _, T.Expr.Var (T.Name_ref.Local l) -> - (* A guest bound again points where the value does, unless the + | Ty.Reference _, T.Expr.Var (T.Name_ref.Local l) -> + (* A reference bound again points where the value does, unless the binding is in a block inside its own, which may not run. *) let own = match frames with f :: _ -> Hashtbl.mem f.declared l.T.Local.id | [] -> false in Hashtbl.replace origins l.T.Local.id (if own then origin value else None) - | Ty.Guest _, _ -> () + | Ty.Reference _, _ -> () | _ -> write frames target; moved frames value); @@ -310,7 +310,7 @@ let walk_body env multi (body : T.Block.t) = (describe p) (describe b)) | None -> () and expr frames (e : T.Expr.t) = - (* A `!` call writes its subject, and a host passed to a hosting + (* A `!` call writes its subject, and an owner passed to a owning parameter moves. *) (match e.T.Expr.node with | T.Expr.Call { callee; args; _ } | T.Expr.Construct { ctor = callee; args; _ } -> ( @@ -325,7 +325,7 @@ let walk_body env multi (body : T.Block.t) = (fun (p : S.param) a -> match (a, p.S.ty) with | T.Arg.Value ({ T.Expr.node = T.Expr.Var (T.Name_ref.Local _); _ } as v), t - when (match t with Ty.Guest _ -> false | _ -> true) && Type_decls.is_reference env t -> + when (match t with Ty.Reference _ -> false | _ -> true) && Type_decls.is_reference env t -> moved frames v | _ -> ()) sg.S.params args @@ -367,7 +367,7 @@ let walk_body env multi (body : T.Block.t) = match inner.T.Expr.node with | T.Expr.Call { callee; args = T.Arg.Value subject :: rest; handler } when (match Env.signature_of env callee with Some sg -> sg.S.is_mut | None -> false) -> - let owner = match subject.T.Expr.ty with Ty.Guest t -> t | t -> t in + let owner = match subject.T.Expr.ty with Ty.Reference t -> t | t -> t in let reference = Type_decls.is_reference env owner in if reference then Env.error env subject.T.Expr.span @@ -420,16 +420,16 @@ let walk_body env multi (body : T.Block.t) = if keeps then lend frames (List.filter_map value args) | _ -> expr frames inner and value = function T.Arg.Value v -> Some v | T.Arg.Block _ -> None - (* The hosts a lasting spawn is passed, directly or through a guest. *) + (* The owners a lasting spawn is passed, directly or through a reference. *) and lend frames args = List.iter (fun (a : T.Expr.t) -> - let host = Ty.strip_guest a.T.Expr.ty in + let lent = Ty.strip_mode a.T.Expr.ty in let lent = match a.T.Expr.ty with - | Ty.Guest _ -> Some { at = origin a; host; through = a } + | Ty.Reference _ -> Some { at = origin a; lent; through = a } | t when Type_decls.is_reference env t && Option.is_some (place_of a) -> - Some { at = origin a; host; through = a } + Some { at = origin a; lent; through = a } | _ -> None in match (lent, frames) with Some l, f :: _ -> f.lends <- l :: f.lends | _ -> ()) diff --git a/lib/tst/analyses/states.ml b/lib/tst/analyses/states.ml new file mode 100644 index 00000000..714dfeab --- /dev/null +++ b/lib/tst/analyses/states.ml @@ -0,0 +1,125 @@ +(* The state of the place an expression denotes (memory.md §2.1, §2.8.1): the + one fact the move, reference and scope analyses all read, so that none of + them can decide it differently. + + An owner of a reference type is settled or roaming. A bare symbol of a + reference type and a package constant are settled; a symbol or parameter + declared `^T` is roaming. What a reference names is settled. A borrow -- + a bare reference-type parameter, and `this` -- is neither: it is the + caller's owner, lent for the call (§2.9). Fixed storage takes its root's + state: a struct field, an `ArrayRef` element. Dynamic storage is + [Contingent]: a list's element and a variant's payload come and go while + their container lives, so they are roaming, and they are never moved out + of it either (lifetimes.md §1.2). A verb's result and a case form are + [Fresh]: nothing owns them yet. + + Every decision is made from the declared types along the path, so it is + the same at every point of the body. *) + +module T = Nodes +module S = Signature + +type state = Settled | Roaming | Borrowed | Contingent | Fresh + +(* What an analysis tells the function about the body it is in: which + locals are the subject, ordinary parameters, and match binders. *) +type t = { + subjects : (int, unit) Hashtbl.t; + params : (int, unit) Hashtbl.t; + binders : (int, unit) Hashtbl.t; + (* A declared subscript's place, by declaration: its `this` and the place + its body projects (functions.md §2.9). *) + projections : (int, int * T.Expr.t) Hashtbl.t; +} + +let create projections = + { + subjects = Hashtbl.create 2; + params = Hashtbl.create 8; + binders = Hashtbl.create 8; + projections; + } + +let subject s (l : T.Local.t) = Hashtbl.replace s.subjects l.T.Local.id () +let param s (l : T.Local.t) = Hashtbl.replace s.params l.T.Local.id () +let binder s (l : T.Local.t) = Hashtbl.replace s.binders l.T.Local.id () + +(* The place each declared subscript's body projects, read from the + declaration, or from any instance of a generic one: which place it is + does not depend on the arguments. *) +let projections (p : T.Program.t) = + let table = Hashtbl.create 8 in + List.iter + (fun (pkg : T.Package.t) -> + List.iter + (fun (d : T.Decl.t) -> + match d.T.Decl.node with + | T.Decl.Subscript { params = this :: _; value = Some v; _ } -> + Hashtbl.replace table d.T.Decl.id (this.T.Local.id, v) + | _ -> ()) + pkg.T.Package.decls) + p.T.Program.packages; + List.iter + (fun (i : T.Instance.t) -> + match (i.T.Instance.params, i.T.Instance.body.T.Block.stats) with + | this :: _, [ { T.Stat.node = T.Stat.Return v; _ } ] + when i.T.Instance.signature.S.kind = S.Subscript + && not (Hashtbl.mem table i.T.Instance.decl) -> + Hashtbl.replace table i.T.Instance.decl (this.T.Local.id, v) + | _ -> ()) + p.T.Program.instances; + table + +let is_intrinsic name = function + | Ty.Intrinsic { namespace = "primitives"; name = n; _ } -> n = name + | _ -> false + +(* A step into fixed storage keeps its base's state. A member of a fresh + value is part of a temporary, which is neither an owner a store may move + from nor a place a reference may name, as an element is not. *) +let fixed base = match base with Fresh -> Contingent | s -> s + +(* [inside] maps a subscript body's `this` to the state of the place it was + called on, while that body's projection is followed. *) +let rec state ?(inside = []) s (e : T.Expr.t) = + match e.T.Expr.node with + | T.Expr.Var (T.Name_ref.Local l) -> ( + match List.assoc_opt l.T.Local.id inside with + | Some st -> st + | None -> + if Hashtbl.mem s.binders l.T.Local.id then Contingent + else if Hashtbl.mem s.subjects l.T.Local.id then Borrowed + else ( + match l.T.Local.ty with + | Ty.Roaming _ -> Roaming + | Ty.Reference _ -> Settled + | _ -> if Hashtbl.mem s.params l.T.Local.id then Borrowed else Settled)) + | T.Expr.Var (T.Name_ref.Global _ | T.Name_ref.Intrinsic _) -> Settled + | T.Expr.Var _ -> Fresh + | T.Expr.Field { target; _ } -> + if Ty.is_ref target.T.Expr.ty then Settled else fixed (state ~inside s target) + | T.Expr.Subscript { target; impl; _ } -> ( + let base () = if Ty.is_ref target.T.Expr.ty then Settled else state ~inside s target in + match impl.T.Verb_ref.owner with + | S.Intrinsic _ -> + let t = Ty.strip_mode target.T.Expr.ty in + if is_intrinsic "List" t then Contingent else fixed (base ()) + | S.Declared id -> ( + match Hashtbl.find_opt s.projections id with + (* A projection that reaches itself again is settled by nothing it + finds, so it is taken as the dynamic kind. *) + | Some (this, body) when List.length inside < 16 -> + state ~inside:((this, base ()) :: inside) s body + | _ -> Contingent)) + | T.Expr.Case_read _ -> Contingent + | _ -> if Ty.is_ref e.T.Expr.ty then Settled else Fresh + +(* The local a place is reached from, and the field steps from it to the + place, outermost first: `car.engine.rotor` is `car` and + [engine; rotor]. [None] when the path holds any other step. *) +let rec field_path (e : T.Expr.t) = + match e.T.Expr.node with + | T.Expr.Var (T.Name_ref.Local l) -> Some (l, []) + | T.Expr.Field { target; field; _ } when not (Ty.is_ref target.T.Expr.ty) -> + Option.map (fun (l, path) -> (l, path @ [ field ])) (field_path target) + | _ -> None diff --git a/lib/tst/check/check.ml b/lib/tst/check/check.ml index fadc42a6..d5b061c0 100644 --- a/lib/tst/check/check.ml +++ b/lib/tst/check/check.ml @@ -53,14 +53,14 @@ let rec expr env ?(flow = false) ctx (e : N.Expr.t) : T.Expr.t = let v = expr env ctx inner in (match v.T.Expr.ty with | Ty.Error | Ty.Param _ -> () - | Ty.Guest _ -> () + | Ty.Reference _ -> () | t -> if not (Type_decls.is_reference env t) then error env span (Printf.sprintf - "`&` takes a guest of a reference type, and %s is a value type" + "`&` takes a reference to a reference type, and %s is a value type" (quote (Ty.to_string t)))); - mk (T.Expr.Ref v) (Ty.Guest (Ty.strip_guest v.T.Expr.ty)) span + mk (T.Expr.Ref v) (Ty.Reference (Ty.strip_mode v.T.Expr.ty)) span | N.Expr.Init fields -> init env ctx span fields | N.Expr.Spawn call -> spawn env ctx call | N.Expr.Match m -> match_ env ~flow ctx m @@ -275,7 +275,7 @@ and member env ~flow ctx span target (field : N.Name.t) handle = invalid span) and private_access ctx tid = - match Option.map Ty.strip_guest ctx.this_type with + match Option.map Ty.strip_mode ctx.this_type with | Some (Ty.Named (this_tid, _)) -> this_tid = tid | _ -> false @@ -428,7 +428,7 @@ and actual_of env ctx (a : N.Call_arg.t) = (* A block argument captures the scope it is written in (control-flow.md §2.2) and yields nothing: a `return`, `resolve` or `abort` in it acts on - what encloses the call (docs/spec-divergences.md §12). *) + what encloses the call (docs/spec-divergences.md §11). *) and block_argument env ctx (b : N.Block.t) = (block_in env (push ctx) b, Ty.Concept Ty.Block) and verb_call env ~flow ctx (vc : N.Verb_call.t) : T.Expr.t * S.t option = @@ -448,7 +448,7 @@ and verb_call env ~flow ctx (vc : N.Verb_call.t) : T.Expr.t * S.t option = and any_error actuals = List.exists (fun a -> Ty.contains_error a.aty) actuals and finish_call env ~flow ctx ~span ~what (o : outcome) handle build = - request env o.sig_ o.subst span; + request env ~args:o.converted o.sig_ o.subst span; let ok = Ty.subst o.subst o.sig_.ret in let abort = Option.map (Ty.subst o.subst) o.sig_.abort in let handler = handle_call env ~flow ctx ~what ~span ~abort ~ok handle in @@ -539,7 +539,7 @@ and function_call env ~flow ctx span (callee : N.Expr.t) actuals handle = (* Calling a function value. It is one value with one type, so there is one candidate; its arguments are still coercion sites. *) and call_value env ~flow ctx span (fn : T.Expr.t) actuals handle = - match Ty.strip_guest fn.T.Expr.ty with + match Ty.strip_mode fn.T.Expr.ty with | Ty.Error -> skip_handler env ctx handle; (invalid span, None) @@ -806,7 +806,7 @@ and constructor_call env ctx span (name : N.Constructor_name.t) (args : N.Constr with | None -> invalid span | Some o -> - request env o.sig_ o.subst span; + request env ~args:o.converted o.sig_ o.subst span; let ret = Ty.subst o.subst o.sig_.ret in mk (T.Expr.Construct @@ -855,7 +855,7 @@ and field_constructor_call env ctx span what cands (fs : N.Field_arg.t list) = match report_resolution env ~span ~what ~args (resolve env field_cands slots_for) field_cands with | None -> invalid span | Some o -> - request env o.sig_ o.subst span; + request env ~args:o.converted o.sig_ o.subst span; let converted = List.combine o.sig_.params o.converted in let fields = List.filter_map @@ -1178,6 +1178,15 @@ and lambda env ctx span ~this_type ~params ~ret_type ~is_mut ~body = if not (ends ~resolve:false typed.T.Block.stats) then error env body.N.Block.span "not every path through this lambda returns; a block body returns explicitly"; let v = { Ty.this_ = Option.map fst this_; params = List.map fst ps; ret; abort; is_mut } in + (match this_ with + | Some (((Ty.Reference _ | Ty.Roaming _) as t), _) -> + error env span + (Printf.sprintf + "the subject is written %s, and it is always a borrow, so neither `^` nor `&` is \ + written on `this`" + (quote (Ty.to_string t))) + | _ -> ()); + Type_decls.check_function_types env span (Ty.Verb v); mk (T.Expr.Lambda { params = List.map snd (Option.to_list this_) @ List.map snd ps; body = typed }) (Ty.Verb v) span @@ -1269,7 +1278,8 @@ and local_declaration env ctx (d : N.Decl.t) = | N.Decl.Var { name; type_; value } -> let v = expr env ctx value in let declared = declared_type env ctx type_ v.T.Expr.ty in - Type_decls.check_storage env type_.N.Type_expr.span "a local" declared; + Type_decls.check_storage env ~roaming:true type_.N.Type_expr.span "a local" declared; + Type_decls.check_function_types env type_.N.Type_expr.span declared; if not (Ty.assignable ~dst:declared ~src:v.T.Expr.ty) then error env v.T.Expr.span (Printf.sprintf @@ -1292,14 +1302,14 @@ and declared_type env ctx (te : N.Type_expr.t) (value_ty : Ty.t) = | N.Type_expr.Path { name; generics = [] } -> ( match Type_decls.resolve_head env (type_scope ctx) name with | Type_decls.Declared d as head -> ( - match (Hashtbl.find_opt env.type_infos d.id, Ty.strip_guest value_ty) with - | Some info, Ty.Named (tid, _) when info.params <> [] && tid = info.tid -> Ty.strip_guest value_ty + match (Hashtbl.find_opt env.type_infos d.id, Ty.strip_mode value_ty) with + | Some info, Ty.Named (tid, _) when info.params <> [] && tid = info.tid -> Ty.strip_mode value_ty | Some info, Ty.Error when info.params <> [] -> Ty.Error | _ -> Type_decls.apply env (type_scope ctx) te.N.Type_expr.span head name []) | Type_decls.Intrinsic_type info as head when info.params <> [] -> ( - match Ty.strip_guest value_ty with + match Ty.strip_mode value_ty with | Ty.Intrinsic { namespace; name = n; _ } when namespace = info.namespace && n = info.name -> - Ty.strip_guest value_ty + Ty.strip_mode value_ty | Ty.Error -> Ty.Error | _ -> Type_decls.apply env (type_scope ctx) te.N.Type_expr.span head name []) | Type_decls.Unknown -> Ty.Error diff --git a/lib/tst/check/context.ml b/lib/tst/check/context.ml index e4d84aa6..57050b7a 100644 --- a/lib/tst/check/context.ml +++ b/lib/tst/check/context.ml @@ -91,14 +91,20 @@ let declare env ctx role (name : N.Name.t) ty = bind ctx role local; local -let mk node ty span = { T.Expr.node; ty; span } +(* `^` says how a place holds its owner, not what the value is (memory.md + §2.1), so no expression's type carries it: a `^T` local read, and a call + that returns `^T`, are values of type `T`. What the place holds is read + from the local's or the signature's declared type. *) +let mk node ty span = + let ty = match ty with Ty.Roaming t -> t | t -> t in + { T.Expr.node; ty; span } let invalid span = mk T.Expr.Invalid Ty.Error span (* A declared type's definition with its arguments applied. A distinct type reads through to the type it was defined as, since it is "structurally equal to its right-hand side" (types.md §5.1). *) let rec definition env ?(depth = 0) (t : Ty.t) : (Ty.type_id * definition) option = - match Ty.strip_guest t with + match Ty.strip_mode t with | Ty.Named (tid, args) -> ( match Type_decls.type_info_of_id env tid with | Some ({ definition = Some def; _ } as info) -> ( diff --git a/lib/tst/check/instances.ml b/lib/tst/check/instances.ml index b3892718..e0e81e87 100644 --- a/lib/tst/check/instances.ml +++ b/lib/tst/check/instances.ml @@ -48,11 +48,49 @@ let check_result env (s : S.t) subst at = (quote (Ty.to_string ret)) (quote (Ty.to_string c))); false +(* generics.md §3.6: an instance that puts a reference type where a value + mould holds only values is reported at the type's origin, the argument it + was read from: a type passed as a value, or the value an inferred one was + inferred from. A generic verb that only forwards its parameter is never + the origin, so the instance is not made, and nothing inside it reports + the same mistake again. *) +let check_kinds env (s : S.t) subst at (args : T.Arg.t option list) = + let instantiated = List.map (fun (p : S.param) -> (p, Ty.subst subst p.S.ty)) s.params in + let found = + List.find_map + (fun t -> Type_decls.wrong_kind env (Ty.strip_mode t)) + (Ty.subst subst s.ret :: List.map snd instantiated) + in + match found with + | None -> true + | Some ((_, bad) as f) -> + let reads (p : S.param) = + List.exists + (fun (q : Ty.param) -> + match List.assoc_opt q.id subst with + | Some (Ty.Type x) -> + Ty.equal (Ty.strip_mode x) (Ty.strip_mode bad) + && (p.S.binds = Some q || List.exists (fun r -> r.Ty.id = q.id) (Ty.free_params p.S.ty)) + | _ -> false) + s.generics + in + let origin = + List.find_map + (fun ((p : S.param), a) -> + match a with + | Some (T.Arg.Value e) when reads p -> Some e.T.Expr.span + | _ -> None) + (List.combine s.params (if List.length args = List.length s.params then args else List.map (fun _ -> None) s.params)) + in + error env (Option.value ~default:at origin) (Type_decls.describe_wrong_kind f); + false + (* A call that builds storage out of a concept was reported, and its instance would only report it again. *) -let request env (s : S.t) subst at = +let request env ?(args = []) (s : S.t) subst at = match s.owner with | _ when not (check_result env s subst at) -> () + | _ when s.generics <> [] && not (check_kinds env s subst at args) -> () | _ when !(env.defining) -> () | S.Declared id when s.generics <> [] -> let args = binding_args s subst in diff --git a/lib/tst/check/overloads.ml b/lib/tst/check/overloads.ml index eaf67705..4327fa5d 100644 --- a/lib/tst/check/overloads.ml +++ b/lib/tst/check/overloads.ml @@ -37,7 +37,7 @@ let arg_expr = function T.Arg.Value e -> Some e | T.Arg.Block _ -> None home package of either, which is the only place one may be (types.md §4.5), so no import is involved. *) let implicit_constructors env ~src ~dst = - let dst = Ty.strip_guest dst in + let dst = Ty.strip_mode dst in match Verb_signatures.type_key dst with | None -> [] | Some key -> @@ -48,7 +48,7 @@ let implicit_constructors env ~src ~dst = match s.params with | [ p ] -> ( let open_ = s.generics in - match Ty.unify ~open_ [] p.ty (Ty.strip_guest src) with + match Ty.unify ~open_ [] p.ty (Ty.strip_mode src) with | None -> None | Some subst -> ( match Ty.unify ~open_ subst s.ret dst with @@ -115,7 +115,7 @@ let try_candidate env ~phase (s : S.t) (slots : actual option list) : outcome op | None -> (None, None)) | None -> ( match - Ty.unify ~open_ subst (Ty.strip_guest p.ty) (Ty.held_as ~dst:p.ty ~src:a.aty) + Ty.unify ~open_ subst (Ty.strip_mode p.ty) (Ty.held_as ~dst:p.ty ~src:a.aty) with | Some subst -> (Some subst, Some `Exact) | None -> (Some subst, Some `Convert))) diff --git a/lib/tst/dune b/lib/tst/dune index dfbc7383..024dceeb 100644 --- a/lib/tst/dune +++ b/lib/tst/dune @@ -9,7 +9,7 @@ ; 1 and 2), [Type_decls] (3) and [Verb_signatures] (4); ; - check/ pass 5, [Check], and the parts of it that are not the ; recursive walk, with [Program] running it over the build; -; - analyses/ [Read_only], [Guests], [Moves], [Owners], [Exits], +; - analyses/ [Read_only], [States], [References], [Moves], [Scopes], [Exits], ; [Expansions], [Literal_ranges] and [Spawns], over the tree ; pass 5 builds; ; - render/ [To_tree_graph] and [To_span_text]. diff --git a/lib/tst/model/env.ml b/lib/tst/model/env.ml index 6dc4d543..0d5ceade 100644 --- a/lib/tst/model/env.ml +++ b/lib/tst/model/env.ml @@ -173,7 +173,9 @@ type t = { type_infos_by_id : (Ty.type_id, type_info) Hashtbl.t; alias_infos : (int, alias_info) Hashtbl.t; ready : bool ref; - deferred_guests : (Ty.t * Span.t) list ref; + deferred_marks : (Ty.t * Span.t) list ref; + (* Checks that need every type's kind, made once the types are defined. *) + deferred_kinds : (unit -> unit) list ref; (* Pass 4 ([Verb_signatures]): every verb's signature, each constant's type, every verb where a call site finds it -- constructors under the type they build, methods under their name, operators under their token, @@ -227,7 +229,8 @@ let create () = type_infos_by_id = Hashtbl.create 64; alias_infos = Hashtbl.create 16; ready = ref false; - deferred_guests = ref []; + deferred_marks = ref []; + deferred_kinds = ref []; signatures = Hashtbl.create 256; constant_types = Hashtbl.create 32; constructors = Hashtbl.create 64; diff --git a/lib/tst/model/intrinsics.ml b/lib/tst/model/intrinsics.ml index 1ec1e062..5827bb70 100644 --- a/lib/tst/model/intrinsics.ml +++ b/lib/tst/model/intrinsics.ml @@ -37,10 +37,13 @@ let types = t "primitives" "Float"; t "primitives" "Bool"; t "primitives" "Unit"; - (* "opaque runtime primitives used by fundamental types" (syntax.md §2.7): - the storage behind `core`'s `String`, which is a reference type. *) - t ~reference:true "primitives" "String"; + (* The storage behind `core`'s `String`: a value type whose handle owns + its bytes, so a copy owns bytes of its own (types.md §2.7). *) + t "primitives" "String"; t ~params:[ Ty.Type_kind; Ty.Number_kind ] "primitives" "Array"; + (* Array's layout as a reference type, whose elements are fixed storage + (generics.md §8.4). *) + t ~params:[ Ty.Type_kind; Ty.Number_kind ] ~reference:true "primitives" "ArrayRef"; t ~params:[ Ty.Type_kind ] ~reference:true "primitives" "List"; t ~reference:true "runtime" "Console"; t ~reference:true "runtime" "Runtime"; @@ -131,15 +134,24 @@ let flips = §2.7): a literal becomes a primitive only where it is written, or inside a type's own implicit conversion. *) let constructors = - let ctor ?(implicit = false) ?(generics = []) name params ret = + let ctor ?(implicit = false) ?(generics = []) ?member name params ret = + let spelling = + "@primitives$" ^ name ^ match member with Some m -> "." ^ m | None -> "" + in ( ("primitives", name), verb ~generics ~namespace:"primitives" - ~kind:(S.Constructor { implicit; member = None; fields = false }) - ~name:("@primitives$" ^ name) ~spelling:("@primitives$" ^ name) params ret ) + ~kind:(S.Constructor { implicit; member; fields = false }) + ~name:spelling ~spelling params ret ) in let element = Ty.fresh_param ~name:"T" ~kind:Ty.Type_kind in let length = Ty.fresh_param ~name:"n" ~kind:Ty.Number_kind in let list_element = Ty.fresh_param ~name:"T" ~kind:Ty.Type_kind in + let fixed name e n = prim name ~args:[ Ty.Type (Ty.Param e); Ty.Number (Ty.Number_param n) ] in + let elements e n = Ty.Concept (Ty.Array_lit (Ty.Param e, Ty.Number_param n)) in + let ref_element = Ty.fresh_param ~name:"T" ~kind:Ty.Type_kind in + let ref_length = Ty.fresh_param ~name:"n" ~kind:Ty.Number_kind in + let fill_element = Ty.fresh_param ~name:"T" ~kind:Ty.Type_kind in + let fill_length = Ty.fresh_param ~name:"n" ~kind:Ty.Number_kind in List.map (fun s -> ctor s [ param "value" (Ty.Concept (literal s)) ] (prim s)) scalars @@ -147,11 +159,28 @@ let constructors = ctor "String" [ param "value" (Ty.Concept Ty.Text_lit) ] (prim "String"); ctor "Unit" [] (prim "Unit"); ctor ~generics:[ element; length ] "Array" + [ param "values" (elements element length) ] + (fixed "Array" element length); + (* An `ArrayRef` is built from an array literal, each element moved into + its place, or by `fill`, which calls `make` once per position, in + order, with the position counted from 1 (generics.md §8.4). *) + ctor ~generics:[ ref_element; ref_length ] "ArrayRef" + [ param "values" (elements ref_element ref_length) ] + (fixed "ArrayRef" ref_element ref_length); + ctor ~generics:[ fill_element; fill_length ] ~member:"fill" "ArrayRef" [ - param "values" - (Ty.Concept (Ty.Array_lit (Ty.Param element, Ty.Number_param length))); + param ~binds:fill_length "n" (Ty.Concept Ty.Integer_lit); + param "make" + (Ty.Verb + { + Ty.this_ = None; + params = [ prim "Int" ]; + ret = Ty.Roaming (Ty.Param fill_element); + abort = None; + is_mut = false; + }); ] - (prim "Array" ~args:[ Ty.Type (Ty.Param element); Ty.Number (Ty.Number_param length) ]); + (fixed "ArrayRef" fill_element fill_length); ctor ~generics:[ list_element ] "List" [ param ~binds:list_element "T" (Ty.Concept Ty.Type_value) ] (prim "List" ~args:[ Ty.Type (Ty.Param list_element) ]); @@ -160,6 +189,8 @@ let constructors = let subscripts = let element = Ty.fresh_param ~name:"T" ~kind:Ty.Type_kind in let length = Ty.fresh_param ~name:"n" ~kind:Ty.Number_kind in + let ref_element = Ty.fresh_param ~name:"T" ~kind:Ty.Type_kind in + let ref_length = Ty.fresh_param ~name:"n" ~kind:Ty.Number_kind in let list_element = Ty.fresh_param ~name:"T" ~kind:Ty.Type_kind in let subscript generics subject element = verb ~generics ~namespace:"primitives" ~kind:S.Subscript ~name:"[]" @@ -171,6 +202,10 @@ let subscripts = subscript [ element; length ] (prim "Array" ~args:[ Ty.Type (Ty.Param element); Ty.Number (Ty.Number_param length) ]) (Ty.Param element); + subscript [ ref_element; ref_length ] + (prim "ArrayRef" + ~args:[ Ty.Type (Ty.Param ref_element); Ty.Number (Ty.Number_param ref_length) ]) + (Ty.Param ref_element); subscript [ list_element ] (prim "List" ~args:[ Ty.Type (Ty.Param list_element) ]) (Ty.Param list_element); @@ -188,14 +223,14 @@ let methods = in [ meth ~is_mut:true "runtime" "print" - [ param "this" (runtime "Console"); param "text" (Ty.Guest (prim "String")) ] + [ param "this" (runtime "Console"); param "text" (prim "String") ] (prim "Unit"); meth ~abort:(Some (prim "Unit")) ~is_mut:true "runtime" "setThreads" [ param "this" (runtime "Runtime"); param "count" (prim "Int") ] (prim "Unit"); meth ~is_mut:true "runtime" "setThreadsAuto" [ param "this" (runtime "Runtime") ] (prim "Unit"); meth ~generics:[ list_element ] ~is_mut:true "primitives" "push" - [ param "this" list; param "value" (Ty.Param list_element) ] + [ param "this" list; param "value" (Ty.Roaming (Ty.Param list_element)) ] (prim "Unit"); meth ~generics:[ list_element ] "primitives" "size" [ param "this" list ] (prim "Int"); ] diff --git a/lib/tst/model/ty.ml b/lib/tst/model/ty.ml index ca20bbac..6cc3cb9c 100644 --- a/lib/tst/model/ty.ml +++ b/lib/tst/model/ty.ml @@ -26,7 +26,10 @@ type type_id = { package : string; name : string } type t = | Named of type_id * arg list | Intrinsic of { namespace : string; name : string; args : arg list } - | Guest of t + (* `&T`: a reference to a settled owner (memory.md §2.4). *) + | Reference of t + (* `^T`: a roaming owner (memory.md §2.1). *) + | Roaming of t | Concept of concept | Verb of verb | Param of param @@ -47,7 +50,7 @@ and concept = | Array_lit of t * number | Map_lit of t * t (* A block argument: statements that yield nothing (docs/spec-divergences.md - §12). *) + §11). *) | Block (* The type of a type written where a value goes: what a `T Type` value parameter accepts (generics.md §5.3). *) @@ -75,7 +78,7 @@ let bool_primitive = Intrinsic { namespace = "primitives"; name = "Bool"; args = let rec contains_error = function | Error -> true | Named (_, args) | Intrinsic { args; _ } -> List.exists arg_contains_error args - | Guest t -> contains_error t + | Reference t | Roaming t -> contains_error t | Concept c -> concept_contains_error c | Verb v -> Option.fold ~none:false ~some:contains_error v.this_ @@ -101,7 +104,8 @@ let rec to_string = function | Named ({ package; name }, args) -> package ^ "$" ^ name ^ args_to_string args | Intrinsic { namespace; name; args } -> "@" ^ namespace ^ "$" ^ name ^ args_to_string args - | Guest t -> "&" ^ to_string t + | Reference t -> "&" ^ to_string t + | Roaming t -> "^" ^ to_string t | Concept c -> concept_to_string c | Verb v -> verb_to_string v | Param p -> p.name @@ -161,7 +165,8 @@ let rec subst (s : subst) t = | Some (Number _) | None -> t) | Named (id, args) -> Named (id, List.map (subst_arg s) args) | Intrinsic i -> Intrinsic { i with args = List.map (subst_arg s) i.args } - | Guest t -> Guest (subst s t) + | Reference t -> Reference (subst s t) + | Roaming t -> Roaming (subst s t) | Concept c -> Concept (subst_concept s c) | Verb v -> Verb @@ -209,7 +214,7 @@ let free_params t = let rec go = function | Param p -> add p | Named (_, args) | Intrinsic { args; _ } -> List.iter go_arg args - | Guest t -> go t + | Reference t | Roaming t -> go t | Concept c -> go_concept c | Verb v -> Option.iter go v.this_; @@ -231,12 +236,13 @@ let free_params t = (* Comparison *) (* ---------------------------------------------------------------------- *) -(* The passing mode is not part of what a value is: a `&T` argument and a `T` +(* The passing mode is not part of what a value is: a `&T`, a `^T` and a `T` one carry the same type, and which mode a position may take is a question for the lifetime analysis (docs/design/semantics.md D1), not for typing. So the - comparisons below look through a guest marker at the top of either side. *) -let strip_guest = function Guest t -> t | t -> t -let is_guest = function Guest _ -> true | _ -> false + comparisons below look through a mode marker, `&` or `^`, at the top of either side. *) +let strip_mode = function Reference t | Roaming t -> t | t -> t +let is_ref = function Reference _ -> true | _ -> false +let is_roaming = function Roaming _ -> true | _ -> false let rec equal a b = match (a, b) with @@ -244,7 +250,7 @@ let rec equal a b = | Named (x, xs), Named (y, ys) -> x = y && args_equal xs ys | Intrinsic x, Intrinsic y -> x.namespace = y.namespace && x.name = y.name && args_equal x.args y.args - | Guest x, Guest y -> equal x y + | Reference x, Reference y | Roaming x, Roaming y -> equal x y | Concept x, Concept y -> concept_equal x y | Verb x, Verb y -> Option.equal equal x.this_ y.this_ @@ -283,14 +289,14 @@ and concept_equal a b = does. An argument is held the same way (§4.1 keeps it from matching two overloads). *) let held_as ~dst ~src = - match (strip_guest dst, strip_guest src) with + match (strip_mode dst, strip_mode src) with | Verb d, Verb s when d.is_mut && not s.is_mut -> Verb { s with is_mut = true } | _, s -> s (* Whether a value of [src] may be stored where [dst] is declared, at a position that is not a coercion site: a declaration, an assignment, a `return`. Exact, up to the passing mode and [held_as]. *) -let assignable ~dst ~src = equal (strip_guest dst) (held_as ~dst ~src) +let assignable ~dst ~src = equal (strip_mode dst) (held_as ~dst ~src) (* A function type without its `mut`, which overload identity does not read (functions.md §4.1). *) @@ -321,10 +327,15 @@ let rec unify ~open_ (s : subst) pattern actual : subst option = | Some (Number _) -> None | None -> if is_bare_literal actual then None else Some ((p.id, Type actual) :: s)) | _, Error -> Some s + (* `^T` over a type parameter takes an owner when it is filled with a + reference type and borrows a value otherwise (memory.md §2.9), so a + function type's `^T` is filled from a value type's result written bare. *) + | Roaming (Param p), actual when is_open p && not (is_roaming actual) -> + unify ~open_ s (Param p) actual | Named (x, xs), Named (y, ys) when x = y -> unify_args ~open_ s xs ys | Intrinsic x, Intrinsic y when x.namespace = y.namespace && x.name = y.name -> unify_args ~open_ s x.args y.args - | Guest x, Guest y -> unify ~open_ s x y + | Reference x, Reference y | Roaming x, Roaming y -> unify ~open_ s x y | Concept x, Concept y -> unify_concept ~open_ s x y | Verb x, Verb y when List.length x.params = List.length y.params @@ -389,7 +400,8 @@ let canonical ts = | Param p -> "$" ^ string_of_int (index p) | Named ({ package; name }, args) -> package ^ "$" ^ name ^ go_args args | Intrinsic { namespace; name; args } -> "@" ^ namespace ^ "$" ^ name ^ go_args args - | Guest t -> "&" ^ go t + | Reference t -> "&" ^ go t + | Roaming t -> "^" ^ go t | Concept c -> ( match c with | Array_lit (t, n) -> "@concepts$Array<" ^ go t ^ "," ^ go_number n ^ ">" diff --git a/lib/tst/passes/collect.ml b/lib/tst/passes/collect.ml index 65a7d964..8a9f3db9 100644 --- a/lib/tst/passes/collect.ml +++ b/lib/tst/passes/collect.ml @@ -270,7 +270,7 @@ let check_import_overloads env (file : file) = (fun (s : Signature.t) -> Ty.canonical (List.map - (fun (p : Signature.param) -> Ty.without_mut (Ty.strip_guest p.ty)) + (fun (p : Signature.param) -> Ty.without_mut (Ty.strip_mode p.ty)) s.params)) (Hashtbl.find_opt env.signatures d.id) in diff --git a/lib/tst/passes/type_decls.ml b/lib/tst/passes/type_decls.ml index 8dfda0f8..a1da6db9 100644 --- a/lib/tst/passes/type_decls.ml +++ b/lib/tst/passes/type_decls.ml @@ -61,7 +61,7 @@ let rec is_reference env ?(seen = []) (t : Ty.t) = match Intrinsics.find_type namespace name with | Some t -> t.reference | None -> false) - | Ty.Guest _ -> true + | Ty.Reference _ -> true | _ -> false let type_info_of_id env tid = Hashtbl.find_opt env.type_infos_by_id tid @@ -73,7 +73,7 @@ let rec concept_in (t : Ty.t) = | Ty.Concept _ -> Some t | Ty.Named (_, args) | Ty.Intrinsic { args; _ } -> List.find_map (function Ty.Type t -> concept_in t | Ty.Number _ -> None) args - | Ty.Guest t -> concept_in t + | Ty.Reference t -> concept_in t | _ -> None let mentions_concept t = Option.is_some (concept_in t) @@ -87,25 +87,141 @@ let describe_concept (t : Ty.t) = Printf.sprintf "%s holds %s, a concept type" (quote (Ty.to_string t)) (quote (Ty.to_string c)) | None -> quote (Ty.to_string t) -let check_storage env span what (t : Ty.t) = +(* [roaming] says whether the storage may be a roaming owner: a local may, + and a field, a constant or a type's definition may not (syntax.md §2.3). *) +let check_storage env ?(roaming = false) span what (t : Ty.t) = if mentions_concept t then error env span (Printf.sprintf "%s, which may type a parameter but is never storage, so it cannot be %s" (describe_concept t) what) - -let check_guest env span (inner : Ty.t) = + else if Ty.is_roaming t && not roaming then + error env span + (Printf.sprintf + "%s is a roaming owner, and `^` is written only on a local, a parameter or a return \ + type, so it cannot be %s" + (quote (Ty.to_string t)) what) + +(* `&` and `^` mark only a reference type (memory.md §2.1, §2.4). A type + parameter may be either kind, and is checked where it is filled. Kinds + are known only once every type is defined, so a marker met before then + is checked afterwards. *) +let check_marker env span (marked : Ty.t) = if !(env.ready) then begin - match inner with - | Ty.Param _ | Ty.Error -> () - | _ -> - if not (is_reference env inner) then - error env span - (Printf.sprintf - "%s is a value type, and `&` marks only a reference type (a `#` \ - mould or a reference primitive)" - (quote (Ty.to_string inner))) + match marked with + | Ty.Reference (Ty.Param _ | Ty.Error) | Ty.Roaming (Ty.Param _ | Ty.Error) -> () + | Ty.Reference inner when not (is_reference env inner) -> + error env span + (Printf.sprintf + "%s is a value type, and `&` marks only a reference type (a `#` \ + mould or a reference primitive)" + (quote (Ty.to_string inner))) + | Ty.Roaming inner when not (is_reference env inner) -> + error env span + (Printf.sprintf + "%s is a value type, and `^` marks only a roaming owner of a reference type; a \ + value is never owned, only copied" + (quote (Ty.to_string inner))) + | _ -> () end - else env.deferred_guests := (inner, span) :: !(env.deferred_guests) + else env.deferred_marks := (marked, span) :: !(env.deferred_marks) + +(* A reference-typed result is a roaming owner or a reference, written `^T` + or `&T`: a bare one would hand back a borrow, which is never returned + (memory.md §2.9, syntax.md §4.10). An abort hands its value to the + caller's handler as a return hands it to the caller (lifetimes.md §1.7), + so an abort type follows the same rule. *) +let bare_reference env (t : Ty.t) = + match t with + | Ty.Reference _ | Ty.Roaming _ | Ty.Param _ | Ty.Error -> false + | t -> is_reference env t + +let check_result env span what (t : Ty.t) = + if bare_reference env t then + error env span + (Printf.sprintf + "%s %s, a reference type, bare; a borrow is never returned, so write `^%s` to hand back \ + an owner or `&%s` to hand back a reference" + what (quote (Ty.to_string t)) (Ty.to_string t) (Ty.to_string t)) + +(* The same rule for every function type written inside [t]. *) +let rec check_function_types env span (t : Ty.t) = + match t with + | Ty.Verb v -> + check_result env span "this function type returns" v.Ty.ret; + Option.iter (check_result env span "this function type aborts with") v.Ty.abort; + List.iter (check_function_types env span) (Option.to_list v.Ty.this_ @ v.Ty.params @ [ v.Ty.ret ]) + | Ty.Named (_, args) | Ty.Intrinsic { args; _ } -> + List.iter (function Ty.Type t -> check_function_types env span t | Ty.Number _ -> ()) args + | Ty.Reference t | Ty.Roaming t -> check_function_types env span t + | _ -> () + +(* A type argument of the wrong kind (generics.md §3.6): a reference type, + or an `&`, that an instance puts where a value mould holds only values + (memory.md §2.10). The walk follows what the instance builds: the fields + of each mould it applies, with the arguments substituted, down through + the moulds those fields apply in turn, and an `@primitives$Array`'s + elements. It does not look inside the arguments themselves, which were + checked where they were written. What it finds is the slot that rejects + the type, as the path that reaches it, and the type. *) +let rec wrong_kind env ?(seen = []) (t : Ty.t) : (string list * Ty.t) option = + let bad (ft : Ty.t) = + match ft with + | Ty.Reference _ -> true + | Ty.Param _ | Ty.Error | Ty.Roaming _ -> false + | ft -> is_reference env ft + in + match t with + | Ty.Named (tid, args) when not (List.mem tid seen) -> ( + match Hashtbl.find_opt env.type_infos_by_id tid with + | Some ({ definition = Some (Struct fs | Variant fs); _ } as info) -> + let value = info.reference = Some false in + let fields = List.map (fun (n, ft) -> (n, Ty.instantiate info.params args ft)) fs in + let step n = Printf.sprintf "%s's field %s" (quote (Ty.to_string t)) (quote n) in + List.find_map + (fun (n, ft) -> + if value && bad ft then Some ([ step n ], ft) + else + Option.map + (fun (path, b) -> (step n :: path, b)) + (wrong_kind env ~seen:(tid :: seen) (Ty.strip_mode ft))) + fields + | Some ({ definition = Some (Distinct u); _ } as info) -> + wrong_kind env ~seen:(tid :: seen) (Ty.instantiate info.params args u) + | _ -> None) + | Ty.Intrinsic { namespace = "primitives"; name = "Array"; args = Ty.Type e :: _ } when bad e -> + Some ([ Printf.sprintf "the elements of %s" (quote (Ty.to_string t)) ], e) + | _ -> None + +let describe_wrong_kind (path, bad) = + Printf.sprintf + "%s is a reference type, and it reaches %s, a value type's slot; a value type holds only \ + values, all the way down" + (quote (Ty.to_string bad)) (String.concat ", then " path) + +(* Report a wrong-kind type once kinds are known: at the argument written + explicitly that brought the rejected type in, or at [span]. *) +let check_kinds env span (t : Ty.t) (args : (Ty.arg * Span.t) list) = + let run () = + match wrong_kind env t with + | None -> () + | Some ((_, bad) as found) -> + let rec mentions (x : Ty.t) = + Ty.equal (Ty.strip_mode x) (Ty.strip_mode bad) + || + match x with + | Ty.Named (_, a) | Ty.Intrinsic { args = a; _ } -> + List.exists (function Ty.Type x -> mentions x | Ty.Number _ -> false) a + | Ty.Reference x | Ty.Roaming x -> mentions x + | _ -> false + in + let at = + match List.find_opt (function Ty.Type x, _ -> mentions x | _ -> false) args with + | Some (_, s) -> s + | None -> span + in + error env at (describe_wrong_kind found) + in + if !(env.ready) then run () else env.deferred_kinds := run :: !(env.deferred_kinds) (* ---------------------------------------------------------------------- *) (* Resolution *) @@ -178,17 +294,31 @@ let parse_number env span text = let rec resolve env scope (te : N.Type_expr.t) : Ty.t = let span = te.N.Type_expr.span in match te.N.Type_expr.node with - | N.Type_expr.Guest inner -> - let t = resolve env scope inner in - check_guest env span t; - Ty.Guest t + | N.Type_expr.Reference inner -> + let t = Ty.Reference (resolve env scope inner) in + check_marker env span t; + t + | N.Type_expr.Roaming inner -> + let t = Ty.Roaming (resolve env scope inner) in + check_marker env span t; + t | N.Type_expr.Verb v -> Ty.Verb (verb_type env scope v) | N.Type_expr.Path { name; generics } -> apply env scope span (resolve_head env scope name) name generics and generic_arg env scope (a : N.Generic_arg.t) : Ty.arg = let span = a.N.Generic_arg.span in match a.N.Generic_arg.node with - | N.Generic_arg.Type t -> Ty.Type (resolve env scope t) + | N.Generic_arg.Type t -> + let t = resolve env scope t in + (* A type argument is what a type holds, and a type's members are + never roaming owners (syntax.md §2.3). *) + if Ty.is_roaming t then + error env span + (Printf.sprintf + "%s is a roaming owner, and `^` is written only on a local, a parameter or a \ + return type, never inside another type's arguments" + (quote (Ty.to_string t))); + Ty.Type t | N.Generic_arg.Number text -> Ty.Number (parse_number env span text) | N.Generic_arg.NumberRef n -> ( match List.assoc_opt n.N.Name.text scope.params with @@ -257,7 +387,10 @@ and apply env scope span head (name : N.Name_type.t) generics : Ty.t = Ty.Error | Intrinsic_type info -> ( match apply_args env scope span (quote ("@" ^ info.namespace ^ "$" ^ info.name)) info.params generics with - | Some args -> Ty.Intrinsic { namespace = info.namespace; name = info.name; args } + | Some args -> + let t = Ty.Intrinsic { namespace = info.namespace; name = info.name; args } in + check_kinds env span t (arg_spans args generics); + t | None -> Ty.Error) | Concept_type c -> concept env scope span c generics | Declared d -> ( @@ -265,7 +398,10 @@ and apply env scope span head (name : N.Name_type.t) generics : Ty.t = | Some info -> ( let kinds = List.map (fun (p : Ty.param) -> p.kind) info.params in match apply_args env scope span (quote info.tid.name) kinds generics with - | Some args -> Ty.Named (info.tid, args) + | Some args -> + let t = Ty.Named (info.tid, args) in + check_kinds env span t (arg_spans args generics); + t | None -> Ty.Error) | None -> ( match Hashtbl.find_opt env.alias_infos d.id with @@ -280,6 +416,9 @@ and apply env scope span head (name : N.Name_type.t) generics : Ty.t = ignore name; Ty.Error)) +and arg_spans args (generics : N.Generic_arg.t list) = + List.combine args (List.map (fun (g : N.Generic_arg.t) -> g.N.Generic_arg.span) generics) + and concept env scope span c generics : Ty.t = let args () = List.map (generic_arg env scope) generics in let expect n = @@ -301,7 +440,7 @@ and concept env scope span c generics : Ty.t = | _ -> error env span "`@concepts$Block` takes no type argument: a block yields nothing \ - (docs/spec-divergences.md §12)"; + (docs/spec-divergences.md §11)"; Ty.Error) | "Array" -> ( match args () with @@ -344,7 +483,7 @@ and param_type env scope (p : N.Param_type.t) : Ty.t = (* Only a type's header entry is written this way, and the grammar builds one nowhere else. *) | N.Param_type.Concept { N.Concept.node = N.Concept.Named _; _ } -> Ty.Error - | N.Param_type.InferredType { name; _ } -> ( + | N.Param_type.InferredType { name; roaming; _ } -> ( if not scope.signature then begin error env p.N.Param_type.span (Printf.sprintf @@ -355,7 +494,7 @@ and param_type env scope (p : N.Param_type.t) : Ty.t = end else match List.assoc_opt name.N.Name.text scope.params with - | Some (Ty.Type t) -> t + | Some (Ty.Type t) -> if roaming then Ty.Roaming t else t | _ -> Ty.Error) (* An alias is expanded where it is written (D5), so it has no identity of @@ -560,6 +699,11 @@ let check_distinct_cycles env () = let check_value_downstream env () = List.iter (fun (info : type_info) -> + (match info.definition with + | Some (Struct fs) | Some (Variant fs) -> + List.iter (fun (_, t) -> check_function_types env info.decl.span t) fs + | Some (Distinct t) -> check_function_types env info.decl.span t + | _ -> ()); if info.reference = Some false then let members = match info.definition with @@ -569,7 +713,7 @@ let check_value_downstream env () = List.iter (fun (name, (t : Ty.t)) -> match t with - | Ty.Guest _ -> + | Ty.Reference _ -> error env info.decl.span (Printf.sprintf "%s is a value type, so its member %s cannot be an `&`; only a \ @@ -593,6 +737,8 @@ let run env = List.iter (fun a -> ignore (alias_target env a)) (List.sort (fun a b -> compare a.alias_decl.id b.alias_decl.id) aliases); check_distinct_cycles env (); env.ready := true; - List.iter (fun (t, span) -> check_guest env span t) (List.rev !(env.deferred_guests)); - env.deferred_guests := []; + List.iter (fun (t, span) -> check_marker env span t) (List.rev !(env.deferred_marks)); + env.deferred_marks := []; + List.iter (fun run -> run ()) (List.rev !(env.deferred_kinds)); + env.deferred_kinds := []; check_value_downstream env () diff --git a/lib/tst/passes/verb_signatures.ml b/lib/tst/passes/verb_signatures.ml index 04f74115..cb23891b 100644 --- a/lib/tst/passes/verb_signatures.ml +++ b/lib/tst/passes/verb_signatures.ml @@ -42,7 +42,7 @@ let introduce env intro span name kind = (* The names a type writes where a number goes. *) let rec number_refs (te : N.Type_expr.t) = match te.N.Type_expr.node with - | N.Type_expr.Guest inner -> number_refs inner + | N.Type_expr.Reference inner | N.Type_expr.Roaming inner -> number_refs inner | N.Type_expr.Verb v -> ( match v.N.Verb_type.node with | N.Verb_type.Func { params; ret_type } -> @@ -71,7 +71,7 @@ and ret_number_refs (r : N.Ret_type.t) = let rec scan_type env intro (te : N.Type_expr.t) = match te.N.Type_expr.node with - | N.Type_expr.Guest inner -> scan_type env intro inner + | N.Type_expr.Reference inner | N.Type_expr.Roaming inner -> scan_type env intro inner | N.Type_expr.Verb v -> ( match v.N.Verb_type.node with | N.Verb_type.Func { params; ret_type } -> @@ -197,7 +197,7 @@ let rec home (t : Ty.t) : S.home option = | Ty.Named (tid, _) -> Some (S.Package tid.Ty.package) | Ty.Intrinsic { namespace; _ } -> Some (S.Namespace namespace) | Ty.Concept _ -> Some (S.Namespace "concepts") - | Ty.Guest t -> home t + | Ty.Reference t -> home t | _ -> None (* The names a verb writes where a number goes, in its signature or in any @@ -504,13 +504,13 @@ let params_key erase (s : S.t) = Ty.canonical (List.map (fun (p : S.param) -> er parameter types, and none that differ only in a passing mode or in the `mut` of a function-type parameter. *) let check_overload_set env what (sigs : (decl * S.t) list) = - let modes t = Ty.without_mut (Ty.strip_guest t) in + let modes t = Ty.without_mut (Ty.strip_mode t) in let rec go seen = function | [] -> () | ((d : decl), s) :: rest -> (match List.find_opt (fun (_, t) -> params_key modes t = params_key modes s) seen with | Some ((first : decl), t) -> - if params_key Ty.strip_guest t <> params_key Ty.strip_guest s then + if params_key Ty.strip_mode t <> params_key Ty.strip_mode s then error env d.span (Printf.sprintf "illegal overload set: this %s differs from the one at %s only by \ @@ -576,7 +576,7 @@ let check_implicit env (d : decl) (s : S.t) = | [ source ] -> let source_ty = source.ty in (match source_ty with - | Ty.Guest _ -> + | Ty.Reference _ -> error env d.span "the source of an implicit constructor cannot be an `&`; it must be a \ value type or a concept type" @@ -618,6 +618,30 @@ let check_constructor_target env (d : decl) (s : S.t) = | _ -> ()) | _ -> () +(* memory.md §2.9: the subject is always a borrow, so no marker is written + on it, and a reference-typed result is written `^T` or `&T`. A + constructor's result is the value it builds, which is fresh, and a + subscript's is the place it projects, so neither is a return. *) +let check_modes env (d : decl) (s : S.t) = + (match (s.kind, s.params) with + | (S.Method | S.Subscript), { S.ty = (Ty.Reference _ | Ty.Roaming _) as t; _ } :: _ -> + error env d.span + (Printf.sprintf + "the subject is written %s, and it is always a borrow of the object the method is \ + called on, so neither `^` nor `&` is written on `this`" + (quote (Ty.to_string t))) + | _ -> ()); + List.iter (fun (p : S.param) -> Type_decls.check_function_types env d.span p.S.ty) s.params; + match s.kind with + | S.Constructor _ | S.Subscript -> () + | _ -> + Type_decls.check_result env d.span + (Printf.sprintf "%s returns" (quote s.name)) s.ret; + Option.iter + (Type_decls.check_result env d.span (Printf.sprintf "%s aborts with" (quote s.name))) + s.abort; + Type_decls.check_function_types env d.span s.ret + let enum_map env (d : decl) = match d.kind with | Enum_map { enum; property; type_; entries } -> ( @@ -756,7 +780,8 @@ let run env = | S.Operator | S.Flip -> check_operator_home env d s | _ -> ()); check_implicit env d s; - check_constructor_target env d s) + check_constructor_target env d s; + check_modes env d s) built; (* `main` takes no parameters and declares no abort type; it may return any type (packages.md §6.2). *) diff --git a/lib/tst/render/to_tree_graph.ml b/lib/tst/render/to_tree_graph.ml index d7715f20..bb12f65c 100644 --- a/lib/tst/render/to_tree_graph.ml +++ b/lib/tst/render/to_tree_graph.ml @@ -71,7 +71,7 @@ let rec expr (e : Expr.t) : node = node "map_read" [ ("target", expr target); ("map", Leaf (Printf.sprintf "%s #%d" property map)) ] | Expr.Subscript { target; impl; args } -> node "subscript" [ ("impl", Leaf (verb_ref impl)); ("target", expr target); ("args", map_seq expr args) ] - | Expr.Ref inner -> node "guest" [ ("of", expr inner) ] + | Expr.Ref inner -> node "reference" [ ("of", expr inner) ] | Expr.Init fs -> node "init" [ ("fields", map_seq field_value fs) ] | Expr.Spawn inner -> node "spawn" [ ("call", expr inner) ] | Expr.Match m -> diff --git a/lib/tst/semantics.ml b/lib/tst/semantics.ml index f115043b..b120c361 100644 --- a/lib/tst/semantics.ml +++ b/lib/tst/semantics.ml @@ -16,9 +16,9 @@ let check (packages : Assembly.package list) = let program = Program.run env in (* The analyses over the finished tree (D1). *) Read_only.run env program; - Guests.run env program; + References.run env program; Moves.run env program; - Owners.run env program; + Scopes.run env program; Exits.run env program; Expansions.run env program; Literal_ranges.run env program; diff --git a/runtime/anchor.c b/runtime/anchor.c deleted file mode 100644 index a2adfc8d..00000000 --- a/runtime/anchor.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "zane_internal.h" - -/* ---------------------------------------------------------------------- */ -/* Anchors and tethers (memory.md §4, docs/design/lowering.md §9) */ -/* ---------------------------------------------------------------------- */ - -int zane_next_position(const int64_t *layout, int64_t *cursor, int64_t *left, - zane_position *p) { - if (*left == 0) return 0; - const int64_t *at = layout + *cursor; - *p = (zane_position){ at[0], at[1], at[2], at[3], (const int64_t *)(intptr_t)at[4], at[5], - at + 6 }; - *cursor += 6 + 2 * at[5]; - (*left)--; - return 1; -} - -int zane_present(const char *base, const zane_position *p) { - for (int64_t i = 0; i < p->conditions; i++) - if (*(const int32_t *)(base + p->tags[2 * i]) != (int32_t)p->tags[2 * i + 1]) return 0; - return 1; -} - -uint32_t *zane_backpointer(char *base, const zane_position *p) { - return (uint32_t *)(base + p->offset); -} - -/* A host that is there, whose identity a caller may read. */ -int zane_hosts(const char *base, const zane_position *p) { - return p->kind == ZANE_HOST && zane_present(base, p); -} - -/* Cells are kept in segments, so one stays where it is while others are - made, and a guest reads its own from any thread. Cells are made and - retired under a lock. */ -static zane_cell *zane_cell_segments[(1ull << 32) / ZANE_CELL_SEGMENT]; -static uint32_t zane_cell_count = 1; -static uint32_t zane_free_cell; /* a stack of returned cells, linked by `sibling` */ -pthread_mutex_t zane_anchors = PTHREAD_MUTEX_INITIALIZER; - -zane_cell *zane_anchor(uint32_t id) { - return &zane_cell_segments[id / ZANE_CELL_SEGMENT][id % ZANE_CELL_SEGMENT]; -} - -/* A new cell; the lock is held. */ -static uint32_t zane_new_cell(void *target) { - uint32_t id = zane_free_cell; - if (id) { - zane_free_cell = zane_anchor(id)->sibling; - } else { - if (zane_cell_count == UINT32_MAX) zane_broken("out of anchors"); - zane_cell **segment = &zane_cell_segments[zane_cell_count / ZANE_CELL_SEGMENT]; - if (!*segment && !(*segment = malloc(ZANE_CELL_SEGMENT * sizeof **segment))) - zane_broken("out of memory for anchors"); - id = zane_cell_count++; - } - *zane_anchor(id) = (zane_cell){ target, 0, 0, 0 }; - return id; -} - -/* A cell retired with its forwarders; the lock is held. */ -static void zane_retire_held(uint32_t id) { - uint32_t f = zane_anchor(id)->forwarders; - while (f) { - uint32_t next = zane_anchor(f)->sibling; - zane_retire_held(f); - f = next; - } - zane_anchor(id)->sibling = zane_free_cell; - zane_free_cell = id; -} - -void zane_retire(uint32_t id) { - pthread_mutex_lock(&zane_anchors); - zane_retire_held(id); - pthread_mutex_unlock(&zane_anchors); -} - -/* The identity a tether ends at, following forwarders. */ -uint32_t zane_terminal(uint32_t tether) { - if (tether == 0) zane_broken("an untethered guest"); - while (zane_anchor(tether)->forward) tether = zane_anchor(tether)->forward; - return tether; -} - -/* The address a guest names. */ -void *zane_resolve(uint32_t tether) { return zane_anchor(zane_terminal(tether))->target; } - -/* A guest to the host at `payload`: its anchor, made the first time. Calls - running at once may each mint one to a host they read, and get the same. */ -uint32_t zane_mint(void *payload) { - uint32_t *backpointer = payload; - uint32_t id = __atomic_load_n(backpointer, __ATOMIC_ACQUIRE); - if (id) return id; - pthread_mutex_lock(&zane_anchors); - id = *backpointer; - if (!id) __atomic_store_n(backpointer, id = zane_new_cell(payload), __ATOMIC_RELEASE); - pthread_mutex_unlock(&zane_anchors); - return id; -} diff --git a/runtime/block.c b/runtime/block.c index 37a94716..52955ab3 100644 --- a/runtime/block.c +++ b/runtime/block.c @@ -4,6 +4,28 @@ /* Dynamic blocks (memory.md §3.6, docs/design/lowering.md §9) */ /* ---------------------------------------------------------------------- */ +/* Where a layout's positions are: a count, then for each position its kind, + offset, size, a list's stride or a box's payload size, the layout of a + list's elements or a box's payload, and the variant tags that must be live + for it to be there, as a count and then (tag offset, tag) pairs. */ +int zane_next_position(const int64_t *layout, int64_t *cursor, int64_t *left, + zane_position *p) { + if (*left == 0) return 0; + const int64_t *at = layout + *cursor; + *p = (zane_position){ at[0], at[1], at[2], at[3], (const int64_t *)(intptr_t)at[4], at[5], + at + 6 }; + *cursor += 6 + 2 * at[5]; + (*left)--; + return 1; +} + +/* Whether a position is there: every variant tag it lies under is live. */ +int zane_present(const char *base, const zane_position *p) { + for (int64_t i = 0; i < p->conditions; i++) + if (*(const int32_t *)(base + p->tags[2 * i]) != (int32_t)p->tags[2 * i + 1]) return 0; + return 1; +} + /* A block is returned when the handle or boxed member that owns it dies, to the region it is in. What it is decides its size and alignment: a string's bytes are as long as its room, a list's elements start at a @@ -14,81 +36,44 @@ void zane_unblock(const zane_position *p, void *block, int64_t room) { void *zane_at(char *base, const zane_position *p) { return base + p->offset; } -/* Whether `offset` is inside one of the `n` hosts that start at `from`, - each `size` bytes long. */ -int zane_inside(int64_t offset, const int64_t *from, const int64_t *size, int64_t n) { - for (int64_t i = 0; i < n; i++) - if (offset >= from[i] && offset < from[i] + size[i]) return 1; - return 0; -} - -/* The value at `base` dies, but for the `n` hosts at `from` that floated: - each block it owns is returned, once what lives in the block has died, - and each host inside a block ends its identity. `identities` says whether - the hosts held inline end theirs too, as at a drain, or are the caller's - to keep or float, as at an overwrite. */ -void zane_end(char *base, const int64_t *layout, int identities, const int64_t *from, - const int64_t *length, int64_t n) { - zane_position p; - ZANE_EACH(layout, p) { - if (!zane_present(base, &p) || zane_inside(p.offset, from, length, n)) continue; - switch (p.kind) { - case ZANE_HOST: { - uint32_t id = *zane_backpointer(base, &p); - if (identities && id) zane_retire(id); - break; - } - case ZANE_TEXT: { - zane_text *t = zane_at(base, &p); - if (t->room) zane_unblock(&p, (void *)t->bytes, t->room); - t->bytes = NULL; - t->length = t->room = 0; - break; - } - case ZANE_LIST: { - zane_list *l = zane_at(base, &p); - if (p.inner) - for (int64_t i = 0; i < l->count; i++) - zane_end(l->items + i * p.extra, p.inner, 1, NULL, NULL, 0); - if (l->room) zane_unblock(&p, l->items, l->room); - l->items = NULL; - l->count = l->room = 0; - break; - } - case ZANE_BOX: { - char **box = zane_at(base, &p); - if (!*box) break; - if (p.inner) zane_end(*box, p.inner, 1, NULL, NULL, 0); - zane_unblock(&p, *box, 0); - *box = NULL; - break; - } - } +/* The block the handle or boxed member at `p` owns is returned, once what + lives in it has died. */ +void zane_end_at(char *base, const zane_position *p) { + switch (p->kind) { + case ZANE_TEXT: { + zane_text *t = zane_at(base, p); + if (t->room) zane_unblock(p, (void *)t->bytes, t->room); + t->bytes = NULL; + t->length = t->room = 0; + break; + } + case ZANE_LIST: { + zane_list *l = zane_at(base, p); + if (p->inner) + for (int64_t i = 0; i < l->count; i++) zane_end(l->items + i * p->extra, p->inner); + if (l->room) zane_unblock(p, l->items, l->room); + l->items = NULL; + l->count = l->room = 0; + break; + } + case ZANE_BOX: { + char **box = zane_at(base, p); + if (!*box) break; + if (p->inner) zane_end(*box, p->inner); + zane_unblock(p, *box, 0); + *box = NULL; + break; + } } } -/* How many blocks the value at `base` owns, down through them, counting - only positions from `lo` up to `hi`. */ -int64_t zane_owned(char *base, const int64_t *layout, int64_t lo, int64_t hi) { - int64_t n = 0; +/* The value at `base` dies: each block it owns is returned, down through + the blocks inside them (lifetimes.md §2.1). */ +void zane_end(char *base, const int64_t *layout) { zane_position p; ZANE_EACH(layout, p) { - if (p.offset < lo || p.offset >= hi || !zane_present(base, &p)) continue; - if (p.kind == ZANE_TEXT) { - n += ((zane_text *)zane_at(base, &p))->room != 0; - } else if (p.kind == ZANE_LIST) { - zane_list *l = zane_at(base, &p); - if (p.inner) - for (int64_t i = 0; i < l->count; i++) - n += zane_owned(l->items + i * p.extra, p.inner, 0, INT64_MAX); - n += l->room != 0; - } else if (p.kind == ZANE_BOX) { - char *box = *(char **)zane_at(base, &p); - if (box && p.inner) n += zane_owned(box, p.inner, 0, INT64_MAX); - n += box != NULL; - } + if (zane_present(base, &p)) zane_end_at(base, &p); } - return n; } /* The scope a value is made in is the innermost; where it arrives decides @@ -97,16 +82,12 @@ static zane_mark *zane_here(void) { return zane_mark_at(zane_self, zane_self->de /* The value at `value` was copied from another place: each block it names is still the original's, so it gets a copy of its own, down through the - blocks inside it, and each host in it starts untethered (memory.md - §2.3). */ + blocks inside it (memory.md §2.3). */ void zane_copy(char *value, const int64_t *layout) { zane_position p; ZANE_EACH(layout, p) { if (!zane_present(value, &p)) continue; switch (p.kind) { - case ZANE_HOST: - *zane_backpointer(value, &p) = 0; - break; case ZANE_TEXT: { zane_text *t = zane_at(value, &p); if (!t->room) break; @@ -152,13 +133,13 @@ void zane_print(const zane_text *text) { void zane_text_join(zane_text *out, const zane_text *left, const zane_text *right) { int64_t length = left->length + right->length; if (length == 0) { - *out = (zane_text){ 0, "", 0, 0 }; + *out = (zane_text){ "", 0, 0 }; return; } char *bytes = zane_alloc(zane_here(), length, 8); memcpy(bytes, left->bytes, (size_t)left->length); memcpy(bytes + left->length, right->bytes, (size_t)right->length); - *out = (zane_text){ 0, bytes, length, length }; + *out = (zane_text){ bytes, length, length }; } /* `==` on `@primitives$String`: the same bytes. */ diff --git a/runtime/dune b/runtime/dune index 4a3a155d..66246da4 100644 --- a/runtime/dune +++ b/runtime/dune @@ -12,14 +12,13 @@ (rule (target zane.c) - (deps main.c arena.c anchor.c block.c value.c list.c slot.c spawn.c snapshot.c) + (deps main.c arena.c block.c value.c list.c slot.c spawn.c snapshot.c) (action (with-stdout-to %{target} (progn (cat main.c) (cat arena.c) - (cat anchor.c) (cat block.c) (cat value.c) (cat list.c) diff --git a/runtime/list.c b/runtime/list.c index 2399266d..c0348e55 100644 --- a/runtime/list.c +++ b/runtime/list.c @@ -13,16 +13,17 @@ static void zane_out_of_range(void) { } /* `@primitives$List(T)`: empty, owning no block. */ -void zane_list_new(zane_list *out) { *out = (zane_list){ 0, NULL, 0, 0 }; } +void zane_list_new(zane_list *out) { *out = (zane_list){ NULL, 0, 0 }; } /* Room for one more element, `stride` bytes wide, at the end: the address the caller then moves it into. A full list's block doubles, from 128 bytes, in the region of the scope that holds the list (§3.6): into a returned block of that size, or in place when it is the last thing at the region's frontier with room after it in its chunk, or else into new - bytes there. Elements that move take their anchors along, as `layout` - says. */ -void *zane_list_push(zane_list *list, int64_t stride, const int64_t *layout) { + bytes there. Nothing references an element, which is roaming (memory.md + §2.8.1), and the blocks the elements own stay where they are, so a move + copies their bytes and nothing else. */ +void *zane_list_push(zane_list *list, int64_t stride) { int64_t needed = (list->count + 1) * stride; if (needed > list->room) { int64_t room = list->room ? list->room * 2 : 128; @@ -44,9 +45,6 @@ void *zane_list_push(zane_list *list, int64_t stride, const int64_t *layout) { if (list->room) zane_free(list->items, list->room, ZANE_LINE); list->items = items; list->room = room; - if (layout) - for (int64_t i = 0; i < list->count; i++) - zane_move(items + i * stride, layout, region, 0); } } return list->items + list->count++ * stride; diff --git a/runtime/main.c b/runtime/main.c index c867564d..61b5d75a 100644 --- a/runtime/main.c +++ b/runtime/main.c @@ -20,8 +20,7 @@ void zane_broken(const char *what) { final flush has nothing left to fail on. The program's own scope is open around `main`, and when it returns every other scope has drained, and every call it spawned has returned. What its own region still holds -- - floated hosts, and what they own, which may have changed since they - floated -- goes with the program. On Windows stdout and stderr start in + the package constants and what they own -- goes with the program. On Windows stdout and stderr start in text mode, which would write each `\n` as `\r\n`; a program writes the bytes it says, as it does everywhere else. */ int main(void) { diff --git a/runtime/slot.c b/runtime/slot.c index c0bb94ed..c8f2908a 100644 --- a/runtime/slot.c +++ b/runtime/slot.c @@ -5,9 +5,9 @@ /* ---------------------------------------------------------------------- */ /* A zeroed slot in the innermost scope's arena, which is the only one a - program ever places a slot in. A slot holding hosts or blocks is listed, - with its layout, so the drain can end the identities and return the - blocks; one filled later holds nothing until then. */ + program ever places a slot in. A slot whose value may own blocks is + listed, with its layout, so the drain can return them; one filled later + holds nothing until then. */ void *zane_slot(int64_t scope, int64_t size, int64_t align, const int64_t *layout) { zane_context *c = zane_self; if (scope != c->depth - 1) zane_broken("a slot placed in a scope that is not innermost"); @@ -16,9 +16,9 @@ void *zane_slot(int64_t scope, int64_t size, int64_t align, const int64_t *layou char *slot = zane_bump(size, align); memset(slot, 0, (size_t)size); if (layout && layout[0] > 0) { - zane_hosted *h = zane_bump(sizeof *h, 8); - *h = (zane_hosted){ m->hosts, slot, layout }; - m->hosts = h; + zane_held *h = zane_bump(sizeof *h, 8); + *h = (zane_held){ m->held, slot, layout }; + m->held = h; } zane_unlock(c); return slot; diff --git a/runtime/snapshot.c b/runtime/snapshot.c index 9c2bebf5..c5f12d5d 100644 --- a/runtime/snapshot.c +++ b/runtime/snapshot.c @@ -4,10 +4,10 @@ /* Snapshots (concurrency.md §4.4, docs/design/lowering.md §9) */ /* ---------------------------------------------------------------------- */ -/* A spawned `mut` call whose subject is reached through a host works on a +/* A spawned `mut` call whose subject is reached through an owner works on a copy of its own, and writes it back when it returns. A write-back counts itself begun, replaces the bytes, and counts itself done; a reader of a - value reached through a host takes its bytes when every write-back begun + value reached through an owner takes its bytes when every write-back begun is done, and keeps them when none began while it read. The bytes move a word at a time, as atomics, so a read is torn only where it is retried. */ static uint64_t zane_begun, zane_done; @@ -71,7 +71,7 @@ static void zane_forget(zane_mark *m) { while (m->retired) { zane_retired *r = m->retired; m->retired = r->next; - zane_end((char *)(r + 1), r->layout, 1, NULL, NULL, 0); + zane_end((char *)(r + 1), r->layout); zane_free((char *)r, (int64_t)sizeof *r + r->size, 8); } } @@ -80,7 +80,7 @@ static void zane_forget(zane_mark *m) { holds nothing, since the result took its blocks home. */ static void zane_release(zane_context *c) { zane_mark *m = zane_mark_at(c, 0); - for (zane_hosted *h = m->hosts; h; h = h->next) zane_end(h->slot, h->layout, 1, NULL, NULL, 0); + for (zane_held *h = m->held; h; h = h->next) zane_end(h->slot, h->layout); zane_forget(m); zane_lock(c); if (m->live != 0) zane_broken("a dynamic block outlived its owner"); @@ -124,9 +124,7 @@ static void zane_join_task(zane_task *t) { /* A read of what a spawned call returns. */ void zane_join(char *frame) { zane_join_task((zane_task *)frame - 1); } -/* Every identity the scope still hosts ends: its anchors, and the - forwarders to them, retire. The blocks its values still own are - returned, and by then no block is out in its region, since every one has +/* The blocks the scope's values still own are returned, and by then no block is out in its region, since every one has an owner in the scope or has moved out with it. Then both regions are released together. First, as the water tower has it (§4.1), the scope waits for every call it spawned, and each result comes home. */ @@ -135,7 +133,7 @@ void zane_scope_drain(int64_t scope) { if (scope != c->depth - 1 || scope == 0) zane_broken("a scope drained out of order"); zane_mark *m = zane_mark_at(c, scope); for (zane_task *t = m->tasks; t; t = t->next) zane_join_task(t); - for (zane_hosted *h = m->hosts; h; h = h->next) zane_end(h->slot, h->layout, 1, NULL, NULL, 0); + for (zane_held *h = m->held; h; h = h->next) zane_end(h->slot, h->layout); zane_forget(m); zane_lock(c); if (m->live != 0) zane_broken("a dynamic block outlived its owner"); diff --git a/runtime/value.c b/runtime/value.c index 1a76bdaf..315dea88 100644 --- a/runtime/value.c +++ b/runtime/value.c @@ -1,20 +1,33 @@ #include "zane_internal.h" /* ---------------------------------------------------------------------- */ -/* Values arriving, leaving and replaced (memory.md §3.5, §3.7, §4.5) */ +/* Values arriving, leaving and replaced (memory.md §2.2, §3.5) */ /* ---------------------------------------------------------------------- */ -/* Whether a block must move into `region`: any block elsewhere when `from` - is 0, and otherwise one in this context's scope `from` or a later one. */ +/* Whether region `r` lives as long as region `s`: it is `s`, or an + enclosing scope of the same context, or the program's own region. A + region of another context is taken as shorter-lived, which only costs a + relocation that was not needed (docs/design/lowering.md §9). */ +static int zane_outlives(const zane_mark *r, const zane_mark *s) { + return r == s || r == zane_program || (r->context == s->context && r->depth <= s->depth); +} + +/* Whether a block must move into `region`. When `from` is 0 the block is + arriving where `region` holds it, and moves only when its own region + does not outlive that one: a move whose destination outlives the blocks + is an escape, and any other leaves them where they are (§3.5). When + `from` is set the blocks are leaving this context's scopes from `from` + in, which drain first. */ static int zane_leaves(const void *block, zane_mark *region, int64_t from) { zane_mark *r = zane_region_at(block); - return r != region && (from == 0 || (r->context == zane_self && r->depth >= from)); + if (from == 0) return !zane_outlives(r, region); + return r != region && r->context == zane_self && r->depth >= from; } /* The block that the handle or boxed member at `at` owns moves into - `region` when it is in `from` or a later scope and not there already: an - equal block there takes what lives in it, down through the blocks it - owns, and the old one is returned (memory.md §3.5). */ + `region` when it must: an equal block there takes what lives in it, down + through the blocks it owns, and the old one is returned (memory.md + §3.5). */ static void zane_move_at(char *at, const zane_position *p, zane_mark *region, int64_t from) { switch (p->kind) { case ZANE_TEXT: { @@ -50,22 +63,18 @@ static void zane_move_at(char *at, const zane_position *p, zane_mark *region, in } } -/* The value at `value` is where it now lives: the anchors of the hosts it - holds follow them there (§4.5), and every block it owns that is in - `from` or a later scope moves into `region`. */ +/* The value at `value` is where it now lives: every block it owns that + must move into `region` does, as `zane_leaves` decides. */ void zane_move(char *value, const int64_t *layout, zane_mark *region, int64_t from) { zane_position p; ZANE_EACH(layout, p) { - uint32_t id; - if (!zane_present(value, &p)) continue; - if (p.kind != ZANE_HOST) zane_move_at(zane_at(value, &p), &p, region, from); - else if ((id = *zane_backpointer(value, &p))) zane_anchor(id)->target = value + p.offset; + if (zane_present(value, &p)) zane_move_at(zane_at(value, &p), &p, region, from); } } -/* A value arrived at `slot`, which had no identity: the anchors it carries - follow it there (§4.5), and the blocks it owns move into the region of - the scope that holds the slot. */ +/* A value arrived at `slot`: the blocks it owns that the slot's region + outlives move into it, which is an escape (memory.md §3.5); the rest stay + where they are. */ void zane_arrive(char *slot, const int64_t *layout) { zane_move(slot, layout, zane_region_at(slot), 0); } @@ -77,16 +86,13 @@ void zane_promote(char *value, const int64_t *layout, int64_t depth) { zane_move(value, layout, zane_mark_at(zane_self, depth - 1), depth); } -/* A host moved out of `slot`: the slot is spent, and its identities and - blocks left with the host. */ +/* An owner moved out of `slot`: the slot is spent, and its blocks left with + the owner (lifetimes.md §1.6). */ void zane_vacate(char *slot, const int64_t *layout) { zane_position p; ZANE_EACH(layout, p) { if (!zane_present(slot, &p)) continue; switch (p.kind) { - case ZANE_HOST: - *zane_backpointer(slot, &p) = 0; - break; case ZANE_TEXT: case ZANE_LIST: { zane_list *l = zane_at(slot, &p); @@ -101,64 +107,39 @@ void zane_vacate(char *slot, const int64_t *layout) { } } -/* Floated hosts, and the blocks they took along when they floated, which - stay out until the program ends. */ -_Atomic int64_t zane_floated; - -/* `incoming` replaces what `slot` holds (memory.md §3.7, §4.5). A contingent - place's anchored occupant -- a variant payload's, or anything in a list's - element when `contingent` is set -- first floats into an anonymous host in - the program's own region, taking along the anchors of every host inside - it, and its blocks, which move there too. What stays dies, and its blocks are - returned. Then each stable host keeps its identity: the incoming one - takes it, and an identity the incoming one brought forwards to it. A - contingent one keeps the incoming host's own. What arrives moves its - blocks into the slot's region. */ -void zane_overwrite(char *slot, char *incoming, int64_t size, const int64_t *layout, - int64_t contingent) { - zane_position p, q; +/* `incoming` replaces what `slot` holds, in place (memory.md §2.2): the + replacement is written at the occupant's address, so every reference to + the slot, or to anything inside it, observes the replacement. A boxed + member present in both keeps its block: the incoming payload is written + into it, recursively, and the block that brought the payload is + returned. So no boxed member moves to a new block, however deep, and a + reference into one keeps its address. Everything else the occupant owns + dies, and what the replacement brought arrives. `incoming` is complete + before this runs, so a replacement made from the occupant is safe (§2.3). */ +void zane_overwrite(char *slot, char *incoming, int64_t size, const int64_t *layout) { int64_t positions = layout ? layout[0] : 0; - int64_t n = 0, from[positions + 1], length[positions + 1]; - ZANE_EACH(layout, p) { - uint32_t id; - if ((p.conditions == 0 && !contingent) || !zane_hosts(slot, &p)) continue; - if (zane_inside(p.offset, from, length, n)) continue; - if (!(id = *zane_backpointer(slot, &p))) continue; - /* A floated host is a block of the program's own region, so what - later arrives in it, or grows from it, is placed there too. It - lives until the program ends, and so do its blocks - (docs/design/lowering.md §9). */ - char *anonymous = zane_alloc(zane_program, p.size, 8); - memcpy(anonymous, slot + p.offset, (size_t)p.size); - zane_floated += 1 + zane_owned(slot, layout, p.offset, p.offset + p.size); - ZANE_EACH(layout, q) { - uint32_t inner; - if (q.offset < p.offset || q.offset >= p.offset + p.size) continue; - if (!zane_present(slot, &q)) continue; - char *at = anonymous + (q.offset - p.offset); - if (q.kind != ZANE_HOST) zane_move_at(at, &q, zane_program, 0); - else if ((inner = *zane_backpointer(slot, &q))) zane_anchor(inner)->target = at; - } - from[n] = p.offset; - length[n++] = p.size; - } - zane_end(slot, layout, 0, from, length, n); + int64_t n = 0; + zane_position kept[positions + 1]; + char *blocks[positions + 1]; + zane_position p; ZANE_EACH(layout, p) { - if (!zane_hosts(incoming, &p)) continue; - uint32_t *brought = zane_backpointer(incoming, &p); - uint32_t kept = p.conditions == 0 && !contingent ? *zane_backpointer(slot, &p) : 0; - if (kept) { - if (*brought && *brought != kept) { - pthread_mutex_lock(&zane_anchors); - zane_anchor(*brought)->forward = kept; - zane_anchor(*brought)->sibling = zane_anchor(kept)->forwarders; - zane_anchor(kept)->forwarders = *brought; - pthread_mutex_unlock(&zane_anchors); - } - *brought = kept; + if (!zane_present(slot, &p)) continue; + char *old = p.kind == ZANE_BOX ? *(char **)zane_at(slot, &p) : NULL; + if (old && zane_present(incoming, &p) && *(char **)zane_at(incoming, &p)) { + kept[n] = p; + blocks[n++] = old; + } else { + zane_end_at(slot, &p); } } memcpy(slot, incoming, (size_t)size); + for (int64_t i = 0; i < n; i++) { + char **at = zane_at(slot, &kept[i]); + char *brought = *at; + zane_overwrite(blocks[i], brought, kept[i].extra, kept[i].inner); + zane_unblock(&kept[i], brought, 0); + *at = blocks[i]; + } zane_arrive(slot, layout); } diff --git a/runtime/zane.h b/runtime/zane.h index 0fc3bd95..05a12a90 100644 --- a/runtime/zane.h +++ b/runtime/zane.h @@ -8,20 +8,17 @@ #include -/* `@primitives$String`, the string view, and `@primitives$List`: - reference types whose instance is its backpointer and a handle to its - bytes or elements. A string has no terminator, and its length is in - bytes; a list's is in elements. `room` is the size of the block the - handle owns, or 0 when it owns none: a literal's bytes are the program's - own and are never returned. */ +/* `@primitives$String`, a value type, and `@primitives$List`, a + reference type: each a handle to its bytes or elements. A string has no + terminator, and its length is in bytes; a list's is in elements. `room` + is the size of the block the handle owns, or 0 when it owns none: a + literal's bytes are the program's own and are never returned. */ typedef struct { - uint32_t bp; const char *bytes; int64_t length, room; } zane_text; typedef struct { - uint32_t bp; char *items; int64_t count, room; } zane_list; @@ -35,11 +32,6 @@ void zane_divide_by_zero(void); /* arena.c */ int64_t zane_scope_enter(void); -/* anchor.c */ -uint32_t zane_terminal(uint32_t tether); -void *zane_resolve(uint32_t tether); -uint32_t zane_mint(void *payload); - /* block.c */ void zane_copy(char *value, const int64_t *layout); void *zane_box(int64_t size, int64_t align); @@ -53,12 +45,11 @@ void zane_constant_end(int64_t *state, char *value, const int64_t *layout); void zane_arrive(char *slot, const int64_t *layout); void zane_promote(char *value, const int64_t *layout, int64_t depth); void zane_vacate(char *slot, const int64_t *layout); -void zane_overwrite(char *slot, char *incoming, int64_t size, const int64_t *layout, - int64_t contingent); +void zane_overwrite(char *slot, char *incoming, int64_t size, const int64_t *layout); /* list.c */ void zane_list_new(zane_list *out); -void *zane_list_push(zane_list *list, int64_t stride, const int64_t *layout); +void *zane_list_push(zane_list *list, int64_t stride); void *zane_list_at(zane_list *list, int64_t index, int64_t stride); void *zane_array_at(char *array, int64_t index, int64_t count, int64_t stride); diff --git a/runtime/zane_internal.h b/runtime/zane_internal.h index caf2203f..b63676ac 100644 --- a/runtime/zane_internal.h +++ b/runtime/zane_internal.h @@ -51,14 +51,14 @@ enum { ZANE_DEPTH = 1 << 15, ZANE_SEGMENT = 64, ZANE_CONTEXTS = (1 << 16) - 1 }; enum { ZANE_PAGES = 1 << 14 }; -/* A host placed in a scope, with where its contained hosts' backpointers - are, so the scope's drain can end their identities. It lives in the - scope's own arena. */ -typedef struct zane_hosted { - struct zane_hosted *next; +/* A slot held in a scope because what it holds owns blocks, with the + layout that says where they are, so the scope's drain can return them. + It lives in the scope's own arena. */ +typedef struct zane_held { + struct zane_held *next; char *slot; const int64_t *layout; -} zane_hosted; +} zane_held; /* The returned blocks of one size and alignment, each naming the next. */ typedef struct zane_stack { @@ -87,7 +87,7 @@ typedef struct zane_retired { } zane_retired; /* Each open scope of a context, innermost last: where its slots began, - what it hosts, the calls it spawned, and its dynamic region with the + what it holds, the calls it spawned, and its dynamic region with the number of blocks out in it. The first of the program's is the program's own, open until it ends. */ typedef struct { @@ -95,7 +95,7 @@ typedef struct { int64_t depth; uint32_t chunks; size_t frontier; - zane_hosted *hosts; + zane_held *held; zane_task *tasks; zane_mapping *mappings; char *chunk; @@ -122,16 +122,13 @@ struct zane_context { zane_context *next; }; -/* A reference-type instance begins with a `u32` backpointer, and so does - every host it contains; a string's or a list's handle, and a boxed - member's pointer, name the block it owns. A layout lists where they are: - a count, then for each position, outermost first, its kind, its offset, - its size, a list's stride or a box's payload size, the layout of a - list's elements or a box's payload, and the variant tags that must be - live for it to be there, as a count and then (tag offset, tag) pairs. A - host under no tag is stable; one under a tag is a variant payload, a - contingent place (memory.md §2.2). */ -enum { ZANE_HOST = 0, ZANE_TEXT = 1, ZANE_LIST = 2, ZANE_BOX = 3 }; +/* A string's or a list's handle, and a boxed member's pointer, name the + block it owns. A layout lists where they are: a count, then for each + position its kind, its offset, its size, a list's stride or a box's + payload size, the layout of a list's elements or a box's payload, and + the variant tags that must be live for it to be there, as a count and + then (tag offset, tag) pairs. */ +enum { ZANE_TEXT = 1, ZANE_LIST = 2, ZANE_BOX = 3 }; typedef struct { int64_t kind, offset, size, extra; @@ -145,26 +142,13 @@ typedef struct { for (int64_t zane_cursor = 1, zane_left = (layout) ? (layout)[0] : 0; \ zane_next_position((layout), &zane_cursor, &zane_left, &(p));) -/* A cell is a payload anchor, naming the host's address, or a forwarder to - another cell. Tethers and backpointers hold a cell's index, and index 0 - is never a cell, so it stands for untethered. A cell keeps the cells that - forward to it, which retire with it: every guest that could still name a - forwarder died before the identity it forwards to ended (§4.6). */ -typedef struct { - void *target; - uint32_t forward; /* the cell this one forwards to, or 0 */ - uint32_t forwarders; /* the first cell forwarding here */ - uint32_t sibling; /* the next cell forwarding where this one does */ -} zane_cell; - -enum { ZANE_CELL_SEGMENT = 1 << 16 }; /* A spawned call. Its frame -- room for its result, then its arguments -- follows this header in the fixed region of the scope that spawned it, which waits for it before it drains (§4.1). The call runs in a context of its own, which it keeps until its result comes home: copied to `dest`, - `size` bytes laid out as `layout` says, where its anchors follow it and - its blocks move into the destination's region. */ + `size` bytes laid out as `layout` says, where its blocks move into the + destination's region. */ struct zane_task { zane_task *next; /* the next the same scope spawned */ zane_task *before, *after; /* in its deque, while queued */ @@ -214,27 +198,17 @@ extern _Atomic int64_t zane_blocks; char *zane_alloc(zane_mark *region, int64_t size, int64_t align); void zane_free(char *block, int64_t size, int64_t align); -/* anchor.c */ +/* block.c */ int zane_next_position(const int64_t *layout, int64_t *cursor, int64_t *left, zane_position *p); int zane_present(const char *base, const zane_position *p); -uint32_t *zane_backpointer(char *base, const zane_position *p); -int zane_hosts(const char *base, const zane_position *p); -extern pthread_mutex_t zane_anchors; -zane_cell *zane_anchor(uint32_t id); -void zane_retire(uint32_t id); - -/* block.c */ void zane_unblock(const zane_position *p, void *block, int64_t room); void *zane_at(char *base, const zane_position *p); -int zane_inside(int64_t offset, const int64_t *from, const int64_t *size, int64_t n); -void zane_end(char *base, const int64_t *layout, int identities, const int64_t *from, - const int64_t *length, int64_t n); -int64_t zane_owned(char *base, const int64_t *layout, int64_t lo, int64_t hi); +void zane_end_at(char *base, const zane_position *p); +void zane_end(char *base, const int64_t *layout); /* value.c */ void zane_move(char *value, const int64_t *layout, zane_mark *region, int64_t from); -extern _Atomic int64_t zane_floated; /* spawn.c */ int zane_state(zane_task *t); diff --git a/tests/codegen/dune.inc b/tests/codegen/dune.inc index c25a4c06..b6f3fa2e 100644 --- a/tests/codegen/dune.inc +++ b/tests/codegen/dune.inc @@ -1,5 +1,51 @@ ; Written by tests/gen/gen_rules.ml. Promote a change with `dune promote`. +(rule + (deps (source_tree fixtures/arrayrefs)) + (action + (with-stdout-to + arrayrefs.cgt.actual + (run %{exe:../../bin/zanec/zanec.exe} --cgt --package fixtures/arrayrefs)))) + +(rule + (alias runtest) + (action + (diff golden/arrayrefs.cgt arrayrefs.cgt.actual))) + +(rule + (deps (source_tree fixtures/arrayrefs)) + (targets arrayrefs.exe) + (action + (run %{exe:../../bin/zanec/zanec.exe} --build arrayrefs.exe --package fixtures/arrayrefs))) + +(rule + (deps (source_tree fixtures/arrayrefs)) + (targets arrayrefs.optimized.exe) + (action + (run %{exe:../../bin/zanec/zanec.exe} --build arrayrefs.optimized.exe --optimize --package fixtures/arrayrefs))) + +(rule + (action + (with-stdout-to + arrayrefs.out.actual + (run ./arrayrefs.exe)))) + +(rule + (alias runtest) + (action + (diff golden/arrayrefs.out arrayrefs.out.actual))) + +(rule + (action + (with-stdout-to + arrayrefs.optimized.out.actual + (run ./arrayrefs.optimized.exe)))) + +(rule + (alias runtest) + (action + (diff golden/arrayrefs.out arrayrefs.optimized.out.actual))) + (rule (deps (source_tree fixtures/arrays)) (action @@ -268,52 +314,6 @@ (action (diff golden/defaults.out defaults.optimized.out.actual))) -(rule - (deps (source_tree fixtures/guests)) - (action - (with-stdout-to - guests.cgt.actual - (run %{exe:../../bin/zanec/zanec.exe} --cgt --package fixtures/guests)))) - -(rule - (alias runtest) - (action - (diff golden/guests.cgt guests.cgt.actual))) - -(rule - (deps (source_tree fixtures/guests)) - (targets guests.exe) - (action - (run %{exe:../../bin/zanec/zanec.exe} --build guests.exe --package fixtures/guests))) - -(rule - (deps (source_tree fixtures/guests)) - (targets guests.optimized.exe) - (action - (run %{exe:../../bin/zanec/zanec.exe} --build guests.optimized.exe --optimize --package fixtures/guests))) - -(rule - (action - (with-stdout-to - guests.out.actual - (run ./guests.exe)))) - -(rule - (alias runtest) - (action - (diff golden/guests.out guests.out.actual))) - -(rule - (action - (with-stdout-to - guests.optimized.out.actual - (run ./guests.optimized.exe)))) - -(rule - (alias runtest) - (action - (diff golden/guests.out guests.optimized.out.actual))) - (rule (deps (source_tree fixtures/hello)) (action @@ -360,52 +360,6 @@ (action (diff golden/hello.out hello.optimized.out.actual))) -(rule - (deps (source_tree fixtures/hosts)) - (action - (with-stdout-to - hosts.cgt.actual - (run %{exe:../../bin/zanec/zanec.exe} --cgt --package fixtures/hosts)))) - -(rule - (alias runtest) - (action - (diff golden/hosts.cgt hosts.cgt.actual))) - -(rule - (deps (source_tree fixtures/hosts)) - (targets hosts.exe) - (action - (run %{exe:../../bin/zanec/zanec.exe} --build hosts.exe --package fixtures/hosts))) - -(rule - (deps (source_tree fixtures/hosts)) - (targets hosts.optimized.exe) - (action - (run %{exe:../../bin/zanec/zanec.exe} --build hosts.optimized.exe --optimize --package fixtures/hosts))) - -(rule - (action - (with-stdout-to - hosts.out.actual - (run ./hosts.exe)))) - -(rule - (alias runtest) - (action - (diff golden/hosts.out hosts.out.actual))) - -(rule - (action - (with-stdout-to - hosts.optimized.out.actual - (run ./hosts.optimized.exe)))) - -(rule - (alias runtest) - (action - (diff golden/hosts.out hosts.optimized.out.actual))) - (rule (deps (source_tree fixtures/lambdas)) (action @@ -578,6 +532,52 @@ (action (diff golden/outcomes.out outcomes.optimized.out.actual))) +(rule + (deps (source_tree fixtures/owners)) + (action + (with-stdout-to + owners.cgt.actual + (run %{exe:../../bin/zanec/zanec.exe} --cgt --package fixtures/owners)))) + +(rule + (alias runtest) + (action + (diff golden/owners.cgt owners.cgt.actual))) + +(rule + (deps (source_tree fixtures/owners)) + (targets owners.exe) + (action + (run %{exe:../../bin/zanec/zanec.exe} --build owners.exe --package fixtures/owners))) + +(rule + (deps (source_tree fixtures/owners)) + (targets owners.optimized.exe) + (action + (run %{exe:../../bin/zanec/zanec.exe} --build owners.optimized.exe --optimize --package fixtures/owners))) + +(rule + (action + (with-stdout-to + owners.out.actual + (run ./owners.exe)))) + +(rule + (alias runtest) + (action + (diff golden/owners.out owners.out.actual))) + +(rule + (action + (with-stdout-to + owners.optimized.out.actual + (run ./owners.optimized.exe)))) + +(rule + (alias runtest) + (action + (diff golden/owners.out owners.optimized.out.actual))) + (rule (deps (source_tree fixtures/range)) (targets range.exe) @@ -696,6 +696,52 @@ (action (diff golden/regions.out regions.optimized.out.actual))) +(rule + (deps (source_tree fixtures/settled)) + (action + (with-stdout-to + settled.cgt.actual + (run %{exe:../../bin/zanec/zanec.exe} --cgt --package fixtures/settled)))) + +(rule + (alias runtest) + (action + (diff golden/settled.cgt settled.cgt.actual))) + +(rule + (deps (source_tree fixtures/settled)) + (targets settled.exe) + (action + (run %{exe:../../bin/zanec/zanec.exe} --build settled.exe --package fixtures/settled))) + +(rule + (deps (source_tree fixtures/settled)) + (targets settled.optimized.exe) + (action + (run %{exe:../../bin/zanec/zanec.exe} --build settled.optimized.exe --optimize --package fixtures/settled))) + +(rule + (action + (with-stdout-to + settled.out.actual + (run ./settled.exe)))) + +(rule + (alias runtest) + (action + (diff golden/settled.out settled.out.actual))) + +(rule + (action + (with-stdout-to + settled.optimized.out.actual + (run ./settled.optimized.exe)))) + +(rule + (alias runtest) + (action + (diff golden/settled.out settled.optimized.out.actual))) + (rule (deps (source_tree fixtures/shapes)) (action diff --git a/tests/codegen/fixtures/arrayrefs/main.zn b/tests/codegen/fixtures/arrayrefs/main.zn new file mode 100644 index 00000000..e470ed4d --- /dev/null +++ b/tests/codegen/fixtures/arrayrefs/main.zn @@ -0,0 +1,114 @@ +/// `@primitives$ArrayRef` (generics.md §8.4): `Array`'s layout as a +/// reference type. Built from a literal, its elements moved in, or by +/// `fill`, which calls a lambda once per position in order. Its elements are +/// fixed storage: an element of a settled `ArrayRef` may be referenced, and +/// is overwritten in place, so the reference observes the replacement +/// (memory.md §2.2, §2.8). A declared subscript that projects one is a +/// reference source too (functions.md §2.9). Each check prints `yes` when it +/// holds and `no` when it does not. +package arrayrefs; + +alias Int = @primitives$Int +alias Bool = @primitives$Bool +alias Unit = @primitives$Unit +alias String = @primitives$String + +/// Advances the counter through the inclusive range, running the block once +/// per step. +Unit to(this Int, end Int, body @concepts$Block) mut { + @controlflow$repeat(end + ~this + Int(1), { + @controlflow$branch(true, body); + this = this + Int(1); + }); + return Unit(); +} + +type Player = #struct { + score Int; + name String; +} + +Player(score Int) => init{ score; name = String("p"); } + +Unit add(this Player, n Int) mut { + this.score = this.score + n; + return Unit(); +} + +Int scoreOf(p &Player) => p.score + +/// An owner whose members are an `ArrayRef`, projected by a subscript. +type Squad = #struct { + _members @primitives$ArrayRef; +} + +Squad(members ^@primitives$ArrayRef) => init{ _members = members; } + +(this Squad)[index Int] => this._members[index] + +Unit replace(this Squad, index Int, player ^Player) mut { + this._members[index] = player; + return Unit(); +} + +Unit check(ok Bool) { + yes String("yes\n"); + no String("no\n"); + @controlflow$branch(ok, { + @program$console!print(yes); + }); + @controlflow$branch(~ok, { + @program$console!print(no); + }); + return Unit(); +} + +Unit main() { + // Built from a literal, its elements moved in, read and written by index. + team @primitives$ArrayRef = @primitives$ArrayRef([Player(Int(1)), Player(Int(2)), Player(Int(3))]); + check(team[Int(2)].score == Int(2)); + team[Int(2)]!add(Int(10)); + check(team[Int(2)].score == Int(12)); + + // An element of a settled `ArrayRef` is referenced, and observes an + // overwrite of the element in place. + second &Player = team[Int(2)]; + team[Int(2)] = Player(Int(20)); + check(second.score == Int(20)); + check(scoreOf(team[Int(3)]) == Int(3)); + second!add(Int(1)); + check(team[Int(2)].score == Int(21)); + + // The whole `ArrayRef` overwritten in place: the reference still names + // its second element. + team = @primitives$ArrayRef([Player(Int(7)), Player(Int(8)), Player(Int(9))]); + check(second.score == Int(8)); + + // Made by `fill`, element by element, in order. + grid @primitives$ArrayRef = @primitives$ArrayRef.fill(100, ^Player(i Int) => Player(i * i)); + check(grid[Int(10)].score == Int(100)); + check(grid[Int(100)].score == Int(10000)); + + // A declared subscript whose projection ends at an `ArrayRef` element is + // a reference source. + squad Squad(@primitives$ArrayRef([Player(Int(5)), Player(Int(6)), Player(Int(7))])); + lead &Player = squad[Int(1)]; + squad!replace(Int(1), Player(Int(50))); + check(lead.score == Int(50)); + + // A roaming `ArrayRef` moves whole, and settles where it lands. + spare ^@primitives$ArrayRef = @primitives$ArrayRef([Player(Int(4)), Player(Int(5)), Player(Int(6))]); + spare[Int(1)]!add(Int(1)); + kept @primitives$ArrayRef = spare; + check(kept[Int(1)].score == Int(5)); + + // Many filled and dropped: each pass returns every block it made. + total Int(0); + j Int(1); + j!to(Int(1000)) { + row @primitives$ArrayRef = @primitives$ArrayRef.fill(4, ^Player(i Int) => Player(i)); + total = total + row[Int(4)].score; + } + check(total == Int(4000)); + return Unit(); +} diff --git a/tests/codegen/fixtures/boxes/main.zn b/tests/codegen/fixtures/boxes/main.zn index 4746c875..b03a9722 100644 --- a/tests/codegen/fixtures/boxes/main.zn +++ b/tests/codegen/fixtures/boxes/main.zn @@ -1,7 +1,9 @@ /// Boxed members: types that contain themselves (adt.md §4). A value one is -/// copied whole, each copy owning blocks of its own; a reference one hosts -/// its children, which stay where they are when it moves, so guests into -/// them hold. A case read gives a boxed payload, or what its handler +/// copied whole, each copy owning blocks of its own; a reference one owns +/// its children, which stay where they are when it moves. An overwrite of a +/// settled owner writes into the blocks its boxed members already have, so a +/// reference into one keeps naming it and observes the replacement +/// (memory.md §2.2). A case read gives a boxed payload, or what its handler /// resolves. Each check prints `yes` when it holds and `no` when it does /// not. package boxes; @@ -49,7 +51,7 @@ Int total(tree Tree) => match (tree) { p node => total(p.left) + total(p.right); } -/// A reference type that hosts its children. +/// A reference type that owns its children. type Chain = #variant { end Unit; link Link; @@ -60,13 +62,32 @@ type Link = #struct { rest Chain; } -Link(name String, rest Chain) => init{ name; rest; } +Link(name String, rest ^Chain) => init{ name; rest; } Int length(this Chain) => match (this) { e end => Int(0); l link => Int(1) + l.rest:length(); } +/// A cycle through a reference struct, whose boxed field a reference may +/// name. +type Tower = #struct { + height Int; + next Storey; +} + +type Storey = #variant { + top Unit; + more Tower; +} + +Tower(height Int, next ^Storey) => init{ height; next; } + +Int levels(this Storey) => match (this) { + t top => Int(0); + m more => Int(1) + m.next:levels(); +} + Unit check(ok Bool) { yes String("yes\n"); no String("no\n"); @@ -101,17 +122,26 @@ Unit main() { check(total(copy) == Int(6)); check(total(tree) == Int(0)); - // A reference chain: a guest to a host inside a boxed payload holds - // while the chain moves. - chain Chain = Chain.link(Link(String("a"), Chain.link(Link(String("b"), Chain.end(Unit()))))); + // A roaming chain moves with its boxed links where they are, and settles + // where it lands. + chain ^Chain = Chain.link(Link(String("a"), Chain.link(Link(String("b"), Chain.end(Unit()))))); check(chain:length() == Int(2)); - first &Chain = chain; moved Chain = chain; + first &Chain = moved; check(first:length() == Int(2)); check((moved.link ? none { resolve Link(String("none"), Chain.end(Unit())); }).name == String("a")); check((Chain.end(Unit()).link ? none { resolve Link(String("none"), Chain.end(Unit())); }).name == String("none")); + // A reference to a settled owner's boxed field observes an overwrite of + // the owner, which reuses the field's block. + tower Tower(Int(1), Storey.more(Tower(Int(2), Storey.top(Unit())))); + upper &Storey = tower.next; + check(upper:levels() == Int(1)); + tower = Tower(Int(5), Storey.more(Tower(Int(6), Storey.more(Tower(Int(7), Storey.top(Unit())))))); + check(upper:levels() == Int(2)); + check(tower.height == Int(5)); + // Many built and dropped: each pass returns every block it made. i Int(1); i!to(Int(1000)) { diff --git a/tests/codegen/fixtures/constants/main.zn b/tests/codegen/fixtures/constants/main.zn index 80ecf38c..04904550 100644 --- a/tests/codegen/fixtures/constants/main.zn +++ b/tests/codegen/fixtures/constants/main.zn @@ -1,6 +1,6 @@ /// Package constants (docs/design/lowering.md L16): each made once, before /// `main`, one that reads another making that one first whatever their -/// order. A string that owns its block, a host a guest names, and a value +/// order. A string that owns its block, an owner a reference names, and a value /// struct, read in a verb, passed, and read from a spawned call. Each check /// prints `yes` when it holds and `no` when it does not. package constants; @@ -10,7 +10,7 @@ alias Bool = @primitives$Bool alias Unit = @primitives$Unit alias String = @primitives$String -Unit say(text &String) { +Unit say(text String) { @program$console!print(text); return Unit(); } diff --git a/tests/codegen/fixtures/defaults/main.zn b/tests/codegen/fixtures/defaults/main.zn index 6a5f3c6a..7cc9fb06 100644 --- a/tests/codegen/fixtures/defaults/main.zn +++ b/tests/codegen/fixtures/defaults/main.zn @@ -11,7 +11,7 @@ alias Bool = @primitives$Bool alias Unit = @primitives$Unit alias String = @primitives$String -Unit say(text &String) { +Unit say(text String) { @program$console!print(text); return Unit(); } diff --git a/tests/codegen/fixtures/lambdas/main.zn b/tests/codegen/fixtures/lambdas/main.zn index 5e702a05..e6361241 100644 --- a/tests/codegen/fixtures/lambdas/main.zn +++ b/tests/codegen/fixtures/lambdas/main.zn @@ -1,8 +1,8 @@ /// Function values (functions.md §7): lambdas lifted to functions of their /// own, held by lambda-variables at package scope and in a body, passed, /// returned and stored, and called through and spawned, with an abort, a -/// subject, a string, a host and a guest. Each check prints `yes` when it holds and `no` when it -/// does not. +/// subject, a string, an owner and a reference. Each check prints `yes` when it +/// holds and `no` when it does not. package lambdas; alias Int = @primitives$Int @@ -60,7 +60,7 @@ Unit bump(this Counter, by Int, f Unit[this Counter, Int] mut) mut { return Unit(); } -/// A reference type: a function value takes its host by the caller's place. +/// A reference type: a function value borrows its owner by the caller's place. type Tally = #struct { total Int; } @@ -75,7 +75,7 @@ Op.apply Int[Int] { thrice = Int(x Int) { return x * Int(3); }; } -/// A host whose member spawned calls through function values write and read. +/// An owner whose member spawned calls through function values write and read. type Shelf = #struct { counter Counter; } @@ -138,10 +138,10 @@ Unit main() { c!bump(Int(4), still); check(c.count == Int(5)); - // A string passed and returned, a host lent, and a guest read through. + // A string passed and returned, an owner lent, and a reference read through. shout String(text String) { return text + String("!"); } check(shout(String("hey")) == String("hey!")); - echo String(text &String) { return text + text; } + echo String(text String) { return text + text; } word String("ab"); check(echo(word) == String("abab")); tally Tally(Int(2)); @@ -173,7 +173,7 @@ Unit main() { seen Int = spawn get(c); check(seen == Int(7)); - // The same, with the subject reached through a host. + // The same, with the subject reached through an owner. shelf Shelf(Counter(Int(10))); if(true) { spawn add(shelf.counter, Int(5)); diff --git a/tests/codegen/fixtures/lists/main.zn b/tests/codegen/fixtures/lists/main.zn index 78ffd4f8..3fb22fbc 100644 --- a/tests/codegen/fixtures/lists/main.zn +++ b/tests/codegen/fixtures/lists/main.zn @@ -1,10 +1,10 @@ /// Lists: `@primitives$List`, a handle to a block that grows (memory.md /// §3.6). Pushed to, measured, indexed and written at an index; lists of -/// scalars, strings, hosts and lists; a generic type over one with a -/// declared subscript, and a generic verb. A host that moves into a list -/// keeps its guests while the list grows and moves it again, and one -/// replaced at an index floats. Each check prints `yes` when it holds and -/// `no` when it does not. +/// scalars, strings, owners and lists; a generic type over one with a +/// declared subscript, and a generic verb. A roaming owner moves into a +/// list, whose elements move with every block it grows into, and one +/// replaced at an index dies. Each check prints `yes` when it holds and `no` +/// when it does not. package lists; alias Int = @primitives$Int @@ -91,30 +91,28 @@ Unit main() { words[Int(1)] = String("or"); check(words[Int(1)] == String("or")); - // Hosts: a guest follows its host into the list, and through every - // block the list grows into. + // Owners: a roaming one moves into the list, which keeps its elements + // through every block it grows into. people List = @primitives$List(Person); - ada Person(String("ada"), Int(36)); - g &Person = ada; + ada ^Person = Person(String("ada"), Int(36)); people!push(ada); j Int(1); j!to(Int(1000)) { people!push(Person(String("x"), j)); } - check(g.name == String("ada")); + check(people[Int(1)].name == String("ada")); check(people[Int(1001)].age == Int(1000)); - // An anchored element replaced at its index floats, and its guest lives - // on. + // An element replaced at its index: the old one dies with its block. people[Int(1)] = Person(String("grace"), Int(45)); - check(g.age == Int(36)); check(people[Int(1)].name == String("grace")); + check(people:size() == Int(1001)); // A list of lists. rows List = @primitives$List(Row); k Int(1); k!to(Int(3)) { - row List = @primitives$List(Int); + row ^Row = @primitives$List(Int); row!push(k); row!push(k * Int(10)); rows!push(row); diff --git a/tests/codegen/fixtures/operators/main.zn b/tests/codegen/fixtures/operators/main.zn index bab04976..86792929 100644 --- a/tests/codegen/fixtures/operators/main.zn +++ b/tests/codegen/fixtures/operators/main.zn @@ -35,7 +35,7 @@ Unit check(holds Bool) { } /// A value that says when it is read. -Int say(value Int, name &String) { +Int say(value Int, name String) { @program$console!print(name); return value; } @@ -47,7 +47,7 @@ Weight(grams Int) => init{ grams; } Bool <(left Weight, right Weight) => left.grams < right.grams -Weight heavy(grams Int, name &String) { +Weight heavy(grams Int, name String) { @program$console!print(name); return Weight(grams); } diff --git a/tests/codegen/fixtures/hosts/main.zn b/tests/codegen/fixtures/owners/main.zn similarity index 57% rename from tests/codegen/fixtures/hosts/main.zn rename to tests/codegen/fixtures/owners/main.zn index 7670c06b..977e4d77 100644 --- a/tests/codegen/fixtures/hosts/main.zn +++ b/tests/codegen/fixtures/owners/main.zn @@ -1,10 +1,13 @@ -/// Reference types: `#struct` and `#variant` instances hosted in their -/// scope's arena, moved by `let`, `return` and a swallowing parameter, and -/// written through `mut` methods. Every way out of a block drains its arena: -/// falling off the end, `return`, `abort`, a `resolve`, and an exit. The -/// runtime stops the program if a scope is drained out of order or left -/// open. Each check prints `yes` when it holds and `no` when it does not. -package hosts; +/// Reference types: `#struct` and `#variant` instances owned in their +/// scope's arena. A roaming owner (`^T`) moves by `let`, `return`, `abort` +/// and a `^T` parameter, and settles where it lands; a bare parameter +/// borrows; and `mut` methods write through the subject. A taken owner the +/// body does not move on dies with the body. Every way out of a block drains +/// its arena: falling off the end, `return`, `abort`, a `resolve`, and an +/// exit. The runtime stops the program if a scope is drained out of order or +/// left open. Each check prints `yes` when it holds and `no` when it does +/// not. +package owners; alias Int = @primitives$Int alias Bool = @primitives$Bool @@ -47,22 +50,33 @@ Unit deposit(this Account, amount Int) mut { Int funds(this Account) => this.balance -/// Moves the local it built out through its return. -Account opened(owner Int, amount Int) { - a Account(owner); +/// Moves the roaming local it built out through its return. +^Account opened(owner Int, amount Int) { + a ^Account = Account(owner); a!deposit(amount); return a; } -/// Swallows its argument. -Int closed(account Account) => account.balance +/// Borrows its argument. +Int balanceOf(account Account) => account.balance + +/// Takes its argument, which dies with the body. +Int closed(account ^Account) => account.balance + +/// Takes its argument and hands it back: a parameter is read-only, so it +/// moves into a local first. +^Account relayed(account ^Account) { + kept ^Account = account; + kept!deposit(Int(1)); + return kept; +} type Slot = #variant { empty Unit; held Account; } -/// Returns from inside a block that hosts a local. +/// Returns from inside a block that holds a local. Int richest(limit Int) { i Int(1); i!to(limit) { @@ -74,7 +88,7 @@ Int richest(limit Int) { return Int(0); } -/// Aborts from inside a block that hosts a local. +/// Aborts from inside a block that holds a local. Int?Int overdrawn(amount Int) { a Account = opened(Int(9), Int(5)); if(a.balance < amount) { @@ -84,8 +98,8 @@ Int?Int overdrawn(amount Int) { return a.balance; } -/// Aborts with a reference type, which the handler's binder hosts. -Int?Account refused(owner Int) { +/// Aborts with a roaming owner, which the handler's binder holds. +Int?^Account refused(owner Int) { if(owner == Int(0)) { abort opened(Int(7), Int(70)); } @@ -97,11 +111,11 @@ type Choice = variant { skip Unit; } -/// A `match` arm aborts from inside its own hosting block, and the -/// `match`'s handler hosts the abort value there. +/// A `match` arm aborts from inside its own block, and the `match`'s handler +/// holds the abort value there. Int chosen(c Choice) => match (c) { n pick { - a Account = opened(n, n); + a ^Account = opened(n, n); if(a.balance == Int(0)) { abort a; } @@ -114,7 +128,7 @@ Int chosen(c Choice) => match (c) { resolve e.owner * Int(100); } -/// Sums balances, skipping one pass with `guard` after hosting a local. +/// Sums balances, skipping one pass with `guard` after holding a local. Int total(skipped Int) { sum Int(0); i Int(1); @@ -139,19 +153,27 @@ Unit check(ok Bool) { } Unit main() { - // A host, written through a `mut` method and read through another. - a Account = opened(Int(1), Int(5)); + // A roaming owner, written through a `mut` method and read through + // another. + a ^Account = opened(Int(1), Int(5)); a!deposit(Int(10)); check(a:funds() == Int(15)); - // A move, a refilled host, and a swallowed argument. + // A move that settles, a refilled roaming owner, a borrow, a take, and a + // relay. b Account = a; check(b.balance == Int(15)); a = Account(Int(2)); check(a.owner == Int(2)); - check(closed(b) == Int(15)); - - // A variant hosting a struct. + check(balanceOf(b) == Int(15)); + check(balanceOf(a) == Int(0)); + check(closed(a) == Int(0)); + c ^Account = opened(Int(3), Int(7)); + c = relayed(c); + check(c.balance == Int(8)); + check(closed(Account(Int(4))) == Int(0)); + + // A variant holding a struct. s Slot = Slot.held(Account(Int(3))); check(match (s) { e empty => false; @@ -169,13 +191,15 @@ Unit main() { check(chosen(Choice.pick(Int(0))) == Int(0)); check(chosen(Choice.skip(Unit())) == Int(600)); - // A hundred thousand passes, each hosting two locals and draining them. + // A hundred thousand passes, each holding two owners and draining them, + // and each taking one into a call that drops it. count Int(0); i Int(1); i!to(Int(100000)) { - c Account = opened(i, Int(1)); - d Account = c; + e ^Account = opened(i, Int(1)); + d Account = e; count = count + d.balance; + count = count + closed(opened(i, Int(0))); } check(count == Int(100000)); return Unit(); diff --git a/tests/codegen/fixtures/references/main.zn b/tests/codegen/fixtures/references/main.zn index 9b2ec94a..7c5e0765 100644 --- a/tests/codegen/fixtures/references/main.zn +++ b/tests/codegen/fixtures/references/main.zn @@ -1,6 +1,7 @@ -/// Guests written out with `&` (memory.md §2.6): minted from a host, copied -/// from a guest, passed, and following the host when it is overwritten. -/// `@program$console` passed where a guest is wanted. Each check prints +/// References written out with `&` (memory.md §2.6): minted from a settled +/// owner, copied from a reference, passed, and observing the owner when it +/// is overwritten in place. `@program$console` passed where a reference is +/// wanted. Each check prints /// `yes` when it holds and `no` when it does not. package references; @@ -34,8 +35,7 @@ Unit main() { g &Person = &a; check(age(&a) + g.age == Int(6)); h &Person = &g; - b Person(Int(4)); - a = b; + a = Person(Int(4)); check(h.age == Int(4)); pass(@program$console); pass(@program$console); diff --git a/tests/codegen/fixtures/regions/main.zn b/tests/codegen/fixtures/regions/main.zn index 090ad15b..127db3b9 100644 --- a/tests/codegen/fixtures/regions/main.zn +++ b/tests/codegen/fixtures/regions/main.zn @@ -1,9 +1,9 @@ /// Regions: every scope keeps the blocks its values own in a dynamic region /// of its own, returned together at its drain (memory.md §3.1–3.2). A value -/// that leaves a scope takes its blocks out first: a host returned with a -/// guest made to it, a list built in a loop, a result left from a `match` -/// arm, a string aborted with, and values stored or pushed from an inner -/// block into an outer one. The runtime stops the program if a drain finds a +/// that escapes a scope takes its blocks out first: a roaming owner +/// returned, a list built in a loop, a result left from a `match` arm, a +/// string aborted with, and values stored or pushed from an inner block into +/// an outer one, which a reference then names. The runtime stops the program if a drain finds a /// block still out, so a run that ends cleanly moved every one. Each check /// prints `yes` when it holds and `no` when it does not. package regions; @@ -49,19 +49,18 @@ Unit check(ok Bool) { return Unit(); } -/// A host made here, read through a guest, and returned: its blocks leave -/// with it before this call's scope drains. -Person made() { - p Person(String("ada") + String(" lovelace")); - g &Person = p; - g.friends!push(String("charles") + String(" babbage")); - check(g.name == String("ada lovelace")); +/// A roaming owner made here, written, and returned: its blocks leave with +/// it before this call's scope drains. +^Person made() { + p ^Person = Person(String("ada") + String(" lovelace")); + p.friends!push(String("charles") + String(" babbage")); + check(p.name == String("ada lovelace")); return p; } -/// A list of strings built in a loop, where each pass hosts one of its own. -Words built(n Int) { - words List = @primitives$List(String); +/// A list of strings built in a loop, where each pass holds one of its own. +^Words built(n Int) { + words ^Words = @primitives$List(String); i Int(1); i!to(n) { piece String = String("w") + String("!"); @@ -71,8 +70,8 @@ Words built(n Int) { } /// A list of lists, returned from a scope nested in the call's. -List nested() { - rows List = @primitives$List(Words); +^List nested() { + rows ^List = @primitives$List(Words); i Int(1); i!to(Int(3)) { rows!push(built(i)); @@ -80,7 +79,7 @@ List nested() { return rows; } -/// A result left from a `match` arm that hosts a string of its own. +/// A result left from a `match` arm that holds a string of its own. String chosen(c Choice) => match (c) { l left { here String = String("le") + String("ft"); @@ -95,8 +94,8 @@ String?String refused() { } Unit main() { - // A returned host keeps its blocks, and a guest made to it here reaches - // them. + // A returned owner keeps its blocks, settles here, and a reference made + // to it reaches them. p Person = made(); h &Person = p; check(h.name == String("ada lovelace")); diff --git a/tests/codegen/fixtures/guests/main.zn b/tests/codegen/fixtures/settled/main.zn similarity index 56% rename from tests/codegen/fixtures/guests/main.zn rename to tests/codegen/fixtures/settled/main.zn index 832c3cd3..320cfd48 100644 --- a/tests/codegen/fixtures/guests/main.zn +++ b/tests/codegen/fixtures/settled/main.zn @@ -1,10 +1,11 @@ -/// Guests: `&T` minted from hosts, fields and parameters, read and written -/// through, passed and returned, and stored in fields. A guest follows its -/// host through moves, sees a stable overwrite, forwards when two anchored -/// hosts merge, and keeps a variant payload alive when the case changes -/// (memory.md §2.8.1, §4.5). Each check prints `yes` when it holds and `no` -/// when it does not. -package guests; +/// References: `&T` minted from settled owners, their fields and +/// parameters, read and written through, passed and returned, and stored in +/// fields. A settled owner never moves, so a reference is its address: an +/// overwrite of the owner, or of the owner around it, is made in place and +/// the reference observes the replacement (memory.md §2.2, §4). A roaming +/// owner settles where it lands. Each check prints `yes` when it holds and +/// `no` when it does not. +package settled; alias Int = @primitives$Int alias Bool = @primitives$Bool @@ -42,7 +43,7 @@ Int twice(this Node) => this.value * Int(2) Int peek(node &Node) => node.value -/// Returns one of two guests it was given. +/// Returns one of two references it was given. &Node larger(a &Node, b &Node) { if(b.value < a.value) { return a; @@ -55,7 +56,7 @@ type Car = #struct { spare Node; } -Car(engine &Node, spare Node) => init{ engine; spare; } +Car(engine &Node, spare ^Node) => init{ engine; spare; } type Holder = #variant { some Node; @@ -75,7 +76,8 @@ Unit check(ok Bool) { } Unit main() { - // A guest reads and writes its host, and the host's writes show. + // A reference reads and writes the settled owner it names, and the + // owner's own writes show through it. a Node(Int(1)); g &Node = a; a!bump(); @@ -85,50 +87,41 @@ Unit main() { check((peek(g) == Int(3)) * (peek(a) == Int(3))); check(g:twice() == Int(6)); - // A move takes the identity along, and a refill starts a new one. - b Node = a; - g!bump(); - check(b.value == Int(4)); - a = Node(Int(100)); - check(g.value == Int(4)); - - // A stable overwrite keeps the identity. - b = Node(Int(7)); + // An overwrite of a settled owner is made in place, and the reference + // observes the replacement. + a = Node(Int(7)); check(g.value == Int(7)); - // Two anchored hosts merge: the moved one's guests forward to the host - // that stays. - c Node(Int(20)); - h &Node = c; - b = c; - h!bump(); - check(g.value == Int(21)); - check(b.value == Int(21)); + // A roaming owner settles where it lands, and is referenced from there. + spare ^Node = Node(Int(20)); + spare!bump(); + b Node = spare; + h &Node = b; + check(h.value == Int(21)); + b = Node(Int(100)); + check(h.value == Int(100)); - // A guest passed and returned, and one repointed. + // A reference passed and returned, and one repointed. check(larger(a, b).value == Int(100)); - g = a; + g = b; check(g.value == Int(100)); - // A guest field, and a guest minted from a hosting field. - car Car(b, Node(Int(5))); + // A reference field, and a reference minted from an owning field of a + // settled owner. + car Car(a, Node(Int(5))); car.engine!bump(); - check(b.value == Int(22)); + check(a.value == Int(8)); s &Node = car.spare; - moved Car = car; s!bump(); - check(moved.spare.value == Int(6)); - moved.spare = Node(Int(50)); + check(car.spare.value == Int(6)); + car.spare = Node(Int(50)); check(s.value == Int(50)); - // A variant payload floats when the case changes, and its guests live on. - n Node(Int(9)); - w &Node = n; - holder Holder = Holder.some(n); - holder = Holder.none(Unit()); - check(w.value == Int(9)); - w!bump(); - check(w.value == Int(10)); + // The whole owner overwritten in place: a reference into its field + // observes the replacement's field. + car = Car(b, Node(Int(60))); + check(s.value == Int(60)); + check(car.engine.value == Int(100)); // A binder is its payload's place: a write through it stays. kept Holder = Holder.some(Node(Int(30))); @@ -144,7 +137,7 @@ Unit main() { z none => false; }); - // A fresh scrutinee is hosted where it is matched. + // A fresh scrutinee is held where it is matched. check(match (Holder.some(Node(Int(40)))) { k some { k!bump(); @@ -153,7 +146,8 @@ Unit main() { z none => false; }); - // Many guests minted and dropped: each pass's anchors retire with it. + // Many references minted and dropped: each pass's owner drains with its + // block, and nothing is kept for a reference. total Int(0); i Int(1); i!to(Int(100000)) { diff --git a/tests/codegen/fixtures/spawns/main.zn b/tests/codegen/fixtures/spawns/main.zn index db464ee4..963f279c 100644 --- a/tests/codegen/fixtures/spawns/main.zn +++ b/tests/codegen/fixtures/spawns/main.zn @@ -1,8 +1,8 @@ /// Spawns: calls started with `spawn` run on the pool's threads while the /// caller goes on (concurrency.md §3). A local bound to one waits for it /// where it is read, and a block waits for every call spawned in it before -/// it drains (§4.1). Results come home with their blocks and their anchors: -/// numbers, strings, lists and hosts, read, left unread, or dropped. The +/// it drains (§4.1). Results come home with their blocks: numbers, strings, +/// lists and owners, read, left unread, or dropped. The /// runtime stops the program if a drain finds a block still out. Each check /// prints `yes` when it holds and `no` when it does not. package spawns; @@ -54,7 +54,7 @@ Unit deepen(this Countdown, n Int) mut { return Unit(); } -/// A host holding values that spawned calls write while others read them. +/// An owner holding values that spawned calls write while others read them. type Holder = #struct { count Countdown; tally Tally; @@ -62,7 +62,7 @@ type Holder = #struct { Holder(count Countdown) => init{ count; tally = Tally(Int(0)); } -/// Reads the countdown through the host `n` times. Each read is a whole +/// Reads the countdown through the owner `n` times. Each read is a whole /// snapshot, so it sees every level a write-back put there or none. Int watch(h &Holder, n Int, whole Int) { seen Int(0); @@ -121,12 +121,12 @@ String nameOf(p &Person) { return p.name + String("!"); } -Person born(name String, age Int) { +^Person born(name String, age Int) { return Person(name + String(" lovelace"), age); } -List words(n Int) { - all List = @primitives$List(String); +^List words(n Int) { + all ^List = @primitives$List(String); i Int(1); i!to(n) { all!push(String("w") + String("!")); @@ -179,18 +179,19 @@ Unit main() { check(a == Int(6765)); // A string made on another thread comes home with its bytes, and the - // host lent to the call lives until it returns. + // value lent to the call lives until it returns. who String("ada"); s String = spawn greet(who); check(s == String("hi ada")); - // A guest passed to a spawned call. + // A reference passed to a spawned call. p Person(String("grace"), Int(85)); g &Person = p; n String = spawn nameOf(g); check(n == String("grace!")); - // A host returned: its anchor follows it home. + // An owner returned: it settles where it comes home, and a reference + // names it there. q Person = spawn born(String("ada"), Int(36)); h &Person = q; check(h.name == String("ada lovelace")); @@ -219,7 +220,7 @@ Unit main() { }); check(depth(c1) + depth(c2) + depth(c3) == Int(900)); - // A value in a host written by a spawned call while another reads it: + // A value in an owner written by a spawned call while another reads it: // the writer works on a copy it writes back, and each read is a // coherent snapshot. holder Holder(Countdown.done(Unit())); diff --git a/tests/codegen/fixtures/texts/main.zn b/tests/codegen/fixtures/texts/main.zn index b1be0b63..0e296693 100644 --- a/tests/codegen/fixtures/texts/main.zn +++ b/tests/codegen/fixtures/texts/main.zn @@ -1,7 +1,7 @@ -/// Strings: `@primitives$String`, the string view, a reference type whose -/// handle owns its bytes (memory.md §3.6). Joined, compared and printed; -/// hosted, moved, guested, overwritten, floated, returned, discarded and -/// aborted with. The runtime stops the program if a block outlives its +/// Strings: `@primitives$String`, a value type whose handle owns its bytes +/// (memory.md §3.6, types.md §2.7). Joined, compared and printed; copied +/// deep, held in an owner and read through a reference to it, overwritten, +/// carried in a payload, returned, discarded and aborted with. The runtime stops the program if a block outlives its /// owner, and a block returned twice fails under the C library, so a run that /// ends cleanly returned each block once. Each check prints `yes` when it /// holds and `no` when it does not. @@ -45,7 +45,7 @@ type Holder = #variant { none Unit; } -String twice(text &String) => text + text +String twice(text String) => text + text String?String refuse(text String) { abort text + String("!"); @@ -72,36 +72,40 @@ Unit main() { check(~(a == b)); check(twice(a) == String("abab")); - // A move takes the bytes along, and a refill brings new ones. + // A copy owns bytes of its own, so overwriting the original leaves it + // as it was. c String = b; check(c == String("abcd\n")); b = String("again"); check(b == String("again")); + check(c == String("abcd\n")); - // A host holding a string: moved, guested, and its string replaced. + // An owner holding a string, read through a reference, and its string + // replaced; a copy of the field keeps the old bytes. p Person(String("ada")); g &Person = p; - q Person = p; check(g:greeting() == String("hello, ada")); - q.name = a + String("!"); + old String = p.name; + p.name = a + String("!"); check(g:greeting() == String("hello, ab!")); - n &String = q.name; - check(n == String("ab!")); + check(old == String("ada")); - // A payload read through a binder, and one that floats. + // A payload read through a binder, and a payload owner that dies with + // its case. t Tagged = Tagged.named(String("abcd?")); check(match (t) { k named => k == String("abcd?"); z anonymous => false; }); t = Tagged.anonymous(Unit()); - kept Person(String("floating")); - w &Person = kept; + kept ^Person = Person(String("held")); + kept!rename(a + String("?")); holder Holder = Holder.some(kept); + check(match (holder) { + h some => h:greeting() == String("hello, ab?"); + z none => false; + }); holder = Holder.none(Unit()); - check(w:greeting() == String("hello, floating")); - w!rename(a + String("!")); - check(w:greeting() == String("hello, ab!")); // A result nobody keeps, and a string aborted with. twice(a); diff --git a/tests/codegen/golden/arrayrefs.cgt b/tests/codegen/golden/arrayrefs.cgt new file mode 100644 index 00000000..1256e3e5 --- /dev/null +++ b/tests/codegen/golden/arrayrefs.cgt @@ -0,0 +1,784 @@ +└── program: + ├── entry: arrayrefs$main() + ├── layouts[0] > layout: + │ ├── type: %primitives$ArrayRef + │ ├── positions[0]: text @8 + │ ├── positions[1]: text @40 + │ └── positions[2]: text @72 + ├── layouts[1] > layout: + │ ├── type: arrayrefs$Player + │ └── positions[0]: text @8 + ├── layouts[2] > layout: + │ ├── type: %primitives$ArrayRef + │ ├── positions[0]: text @8 + │ ├── positions[1]: text @40 + │ ├── positions[2]: text @72 + │ ├── positions[3]: text @104 + │ ├── positions[4]: text @136 + │ ├── positions[5]: text @168 + │ ├── positions[6]: text @200 + │ ├── positions[7]: text @232 + │ ├── positions[8]: text @264 + │ ├── positions[9]: text @296 + │ ├── positions[10]: text @328 + │ ├── positions[11]: text @360 + │ ├── positions[12]: text @392 + │ ├── positions[13]: text @424 + │ ├── positions[14]: text @456 + │ ├── positions[15]: text @488 + │ ├── positions[16]: text @520 + │ ├── positions[17]: text @552 + │ ├── positions[18]: text @584 + │ ├── positions[19]: text @616 + │ ├── positions[20]: text @648 + │ ├── positions[21]: text @680 + │ ├── positions[22]: text @712 + │ ├── positions[23]: text @744 + │ ├── positions[24]: text @776 + │ ├── positions[25]: text @808 + │ ├── positions[26]: text @840 + │ ├── positions[27]: text @872 + │ ├── positions[28]: text @904 + │ ├── positions[29]: text @936 + │ ├── positions[30]: text @968 + │ ├── positions[31]: text @1000 + │ ├── positions[32]: text @1032 + │ ├── positions[33]: text @1064 + │ ├── positions[34]: text @1096 + │ ├── positions[35]: text @1128 + │ ├── positions[36]: text @1160 + │ ├── positions[37]: text @1192 + │ ├── positions[38]: text @1224 + │ ├── positions[39]: text @1256 + │ ├── positions[40]: text @1288 + │ ├── positions[41]: text @1320 + │ ├── positions[42]: text @1352 + │ ├── positions[43]: text @1384 + │ ├── positions[44]: text @1416 + │ ├── positions[45]: text @1448 + │ ├── positions[46]: text @1480 + │ ├── positions[47]: text @1512 + │ ├── positions[48]: text @1544 + │ ├── positions[49]: text @1576 + │ ├── positions[50]: text @1608 + │ ├── positions[51]: text @1640 + │ ├── positions[52]: text @1672 + │ ├── positions[53]: text @1704 + │ ├── positions[54]: text @1736 + │ ├── positions[55]: text @1768 + │ ├── positions[56]: text @1800 + │ ├── positions[57]: text @1832 + │ ├── positions[58]: text @1864 + │ ├── positions[59]: text @1896 + │ ├── positions[60]: text @1928 + │ ├── positions[61]: text @1960 + │ ├── positions[62]: text @1992 + │ ├── positions[63]: text @2024 + │ ├── positions[64]: text @2056 + │ ├── positions[65]: text @2088 + │ ├── positions[66]: text @2120 + │ ├── positions[67]: text @2152 + │ ├── positions[68]: text @2184 + │ ├── positions[69]: text @2216 + │ ├── positions[70]: text @2248 + │ ├── positions[71]: text @2280 + │ ├── positions[72]: text @2312 + │ ├── positions[73]: text @2344 + │ ├── positions[74]: text @2376 + │ ├── positions[75]: text @2408 + │ ├── positions[76]: text @2440 + │ ├── positions[77]: text @2472 + │ ├── positions[78]: text @2504 + │ ├── positions[79]: text @2536 + │ ├── positions[80]: text @2568 + │ ├── positions[81]: text @2600 + │ ├── positions[82]: text @2632 + │ ├── positions[83]: text @2664 + │ ├── positions[84]: text @2696 + │ ├── positions[85]: text @2728 + │ ├── positions[86]: text @2760 + │ ├── positions[87]: text @2792 + │ ├── positions[88]: text @2824 + │ ├── positions[89]: text @2856 + │ ├── positions[90]: text @2888 + │ ├── positions[91]: text @2920 + │ ├── positions[92]: text @2952 + │ ├── positions[93]: text @2984 + │ ├── positions[94]: text @3016 + │ ├── positions[95]: text @3048 + │ ├── positions[96]: text @3080 + │ ├── positions[97]: text @3112 + │ ├── positions[98]: text @3144 + │ └── positions[99]: text @3176 + ├── layouts[3] > layout: + │ ├── type: arrayrefs$Squad + │ ├── positions[0]: text @8 + │ ├── positions[1]: text @40 + │ └── positions[2]: text @72 + ├── layouts[4] > layout: + │ ├── type: ^%primitives$ArrayRef + │ ├── positions[0]: text @8 + │ ├── positions[1]: text @40 + │ └── positions[2]: text @72 + ├── layouts[5] > layout: + │ ├── type: %primitives$ArrayRef + │ ├── positions[0]: text @8 + │ ├── positions[1]: text @40 + │ ├── positions[2]: text @72 + │ └── positions[3]: text @104 + ├── layouts[6] > layout: + │ ├── type: %primitives$String + │ └── positions[0]: text @0 + ├── layouts[7] > layout: + │ ├── type: ^arrayrefs$Player + │ └── positions[0]: text @8 + ├── funcs[0] > func: + │ ├── symbol: arrayrefs$main() + │ ├── params: + │ ├── ret: void + │ └── body[0] > scope: + │ ├── id: %2 + │ ├── body[0] > hold: + │ │ ├── local: #1 + │ │ ├── scope: %2 + │ │ ├── value > record: + │ │ │ ├── type: [3 x {i64, handle}] + │ │ │ ├── members[0] > member 0 > call: + │ │ │ │ ├── type: {i64, handle} + │ │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ │ └── args[0]: 1 : i64 + │ │ │ ├── members[1] > member 1 > call: + │ │ │ │ ├── type: {i64, handle} + │ │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ │ └── args[0]: 2 : i64 + │ │ │ └── members[2] > member 2 > call: + │ │ │ ├── type: {i64, handle} + │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ └── args[0]: 3 : i64 + │ │ └── layout: %primitives$ArrayRef + │ ├── body[1] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, handle} + │ │ │ ├── path: 0 + │ │ │ └── base > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0]: address of #1 : ptr + │ │ │ ├── args[1]: 2 : i64 + │ │ │ ├── args[2]: 3 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ └── right: 2 : i64 + │ ├── body[2] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$add(this arrayrefs$Player, %primitives$Int) + │ │ ├── args[0] > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0]: address of #1 : ptr + │ │ │ ├── args[1]: 2 : i64 + │ │ │ ├── args[2]: 3 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ └── args[1]: 10 : i64 + │ ├── body[3] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, handle} + │ │ │ ├── path: 0 + │ │ │ └── base > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0]: address of #1 : ptr + │ │ │ ├── args[1]: 2 : i64 + │ │ │ ├── args[2]: 3 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ └── right: 12 : i64 + │ ├── body[4] > let: + │ │ ├── local: #3 + │ │ └── value > runtime: + │ │ ├── type: ptr + │ │ ├── fn: zane_array_at + │ │ ├── args[0]: address of #1 : ptr + │ │ ├── args[1]: 2 : i64 + │ │ ├── args[2]: 3 : i64 + │ │ └── args[3]: 32 : i64 + │ ├── body[5] > overwrite: + │ │ ├── address > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0]: address of #1 : ptr + │ │ │ ├── args[1]: 2 : i64 + │ │ │ ├── args[2]: 3 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ ├── value > call: + │ │ │ ├── type: {i64, handle} + │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ └── args[0]: 20 : i64 + │ │ └── layout: arrayrefs$Player + │ ├── body[6] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, handle} + │ │ │ ├── path: 0 + │ │ │ └── base: local #3 : ptr + │ │ └── right: 20 : i64 + │ ├── body[7] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > call: + │ │ │ ├── type: i64 + │ │ │ ├── fn: arrayrefs$scoreOf(&arrayrefs$Player) + │ │ │ └── args[0] > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0]: address of #1 : ptr + │ │ │ ├── args[1]: 3 : i64 + │ │ │ ├── args[2]: 3 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ └── right: 3 : i64 + │ ├── body[8] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$add(this arrayrefs$Player, %primitives$Int) + │ │ ├── args[0]: local #3 : ptr + │ │ └── args[1]: 1 : i64 + │ ├── body[9] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, handle} + │ │ │ ├── path: 0 + │ │ │ └── base > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0]: address of #1 : ptr + │ │ │ ├── args[1]: 2 : i64 + │ │ │ ├── args[2]: 3 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ └── right: 21 : i64 + │ ├── body[10] > overwrite: + │ │ ├── address: address of #1 : ptr + │ │ ├── value > record: + │ │ │ ├── type: [3 x {i64, handle}] + │ │ │ ├── members[0] > member 0 > call: + │ │ │ │ ├── type: {i64, handle} + │ │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ │ └── args[0]: 7 : i64 + │ │ │ ├── members[1] > member 1 > call: + │ │ │ │ ├── type: {i64, handle} + │ │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ │ └── args[0]: 8 : i64 + │ │ │ └── members[2] > member 2 > call: + │ │ │ ├── type: {i64, handle} + │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ └── args[0]: 9 : i64 + │ │ └── layout: %primitives$ArrayRef + │ ├── body[11] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, handle} + │ │ │ ├── path: 0 + │ │ │ └── base: local #3 : ptr + │ │ └── right: 8 : i64 + │ ├── body[12] > hold: + │ │ ├── local: #10 + │ │ ├── scope: %2 + │ │ ├── value > expand: + │ │ │ ├── type: [100 x {i64, handle}] + │ │ │ ├── label: @8 + │ │ │ ├── result: #9 + │ │ │ ├── body[0] > let: + │ │ │ │ ├── local: #4 + │ │ │ │ └── value: function arrayrefs$main()$lambda1 + │ │ │ ├── body[1] > reserve: + │ │ │ │ ├── local: #5 + │ │ │ │ ├── scope: %2 + │ │ │ │ ├── type: [100 x {i64, handle}] + │ │ │ │ └── layout: %primitives$ArrayRef + │ │ │ ├── body[2] > let: + │ │ │ │ ├── local: #6 + │ │ │ │ └── value: 1 : i64 + │ │ │ ├── body[3] > repeat: + │ │ │ │ ├── count: 100 : i64 + │ │ │ │ ├── body[0] > let: + │ │ │ │ │ ├── local: #7 + │ │ │ │ │ └── value > call value: + │ │ │ │ │ ├── type: {i64, handle} + │ │ │ │ │ ├── fn: local #4 : ptr + │ │ │ │ │ └── args[0]: local #6 : i64 + │ │ │ │ ├── body[1] > place: + │ │ │ │ │ ├── address > runtime: + │ │ │ │ │ │ ├── type: ptr + │ │ │ │ │ │ ├── fn: zane_array_at + │ │ │ │ │ │ ├── args[0]: address of #5 : ptr + │ │ │ │ │ │ ├── args[1]: local #6 : i64 + │ │ │ │ │ │ ├── args[2]: 100 : i64 + │ │ │ │ │ │ └── args[3]: 32 : i64 + │ │ │ │ │ ├── value: local #7 : {i64, handle} + │ │ │ │ │ └── layout: arrayrefs$Player + │ │ │ │ └── body[2] > assign: + │ │ │ │ ├── place: #6 + │ │ │ │ └── value > binary: + │ │ │ │ ├── type: i64 + │ │ │ │ ├── op: + + │ │ │ │ ├── left: local #6 : i64 + │ │ │ │ └── right: 1 : i64 + │ │ │ └── body[4] > assign: + │ │ │ ├── place: #9 + │ │ │ └── value > take: + │ │ │ ├── type: [100 x {i64, handle}] + │ │ │ ├── address: address of #5 : ptr + │ │ │ └── layout: %primitives$ArrayRef + │ │ └── layout: %primitives$ArrayRef + │ ├── body[13] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, handle} + │ │ │ ├── path: 0 + │ │ │ └── base > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0]: address of #10 : ptr + │ │ │ ├── args[1]: 10 : i64 + │ │ │ ├── args[2]: 100 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ └── right: 100 : i64 + │ ├── body[14] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, handle} + │ │ │ ├── path: 0 + │ │ │ └── base > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0]: address of #10 : ptr + │ │ │ ├── args[1]: 100 : i64 + │ │ │ ├── args[2]: 100 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ └── right: 10000 : i64 + │ ├── body[15] > hold: + │ │ ├── local: #11 + │ │ ├── scope: %2 + │ │ ├── value > call: + │ │ │ ├── type: {[3 x {i64, handle}]} + │ │ │ ├── fn: arrayrefs$Squad(^%primitives$ArrayRef) + │ │ │ └── args[0] > record: + │ │ │ ├── type: [3 x {i64, handle}] + │ │ │ ├── members[0] > member 0 > call: + │ │ │ │ ├── type: {i64, handle} + │ │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ │ └── args[0]: 5 : i64 + │ │ │ ├── members[1] > member 1 > call: + │ │ │ │ ├── type: {i64, handle} + │ │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ │ └── args[0]: 6 : i64 + │ │ │ └── members[2] > member 2 > call: + │ │ │ ├── type: {i64, handle} + │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ └── args[0]: 7 : i64 + │ │ └── layout: arrayrefs$Squad + │ ├── body[16] > let: + │ │ ├── local: #16 + │ │ └── value > expand: + │ │ ├── type: ptr + │ │ ├── label: @14 + │ │ ├── result: #15 + │ │ ├── body[0] > let: + │ │ │ ├── local: #12 + │ │ │ └── value: address of #11 : ptr + │ │ ├── body[1] > let: + │ │ │ ├── local: #13 + │ │ │ └── value: 1 : i64 + │ │ └── body[2] > assign: + │ │ ├── place: #15 + │ │ └── value > runtime: + │ │ ├── type: ptr + │ │ ├── fn: zane_array_at + │ │ ├── args[0] > offset: + │ │ │ ├── within: {[3 x {i64, handle}]} + │ │ │ ├── path: 0 + │ │ │ └── base: local #12 : ptr + │ │ ├── args[1]: local #13 : i64 + │ │ ├── args[2]: 3 : i64 + │ │ └── args[3]: 32 : i64 + │ ├── body[17] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$replace(this arrayrefs$Squad, %primitives$Int, ^arrayrefs$Player) + │ │ ├── args[0]: address of #11 : ptr + │ │ ├── args[1]: 1 : i64 + │ │ └── args[2] > call: + │ │ ├── type: {i64, handle} + │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ └── args[0]: 50 : i64 + │ ├── body[18] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, handle} + │ │ │ ├── path: 0 + │ │ │ └── base: local #16 : ptr + │ │ └── right: 50 : i64 + │ ├── body[19] > hold: + │ │ ├── local: #17 + │ │ ├── scope: %2 + │ │ ├── value > record: + │ │ │ ├── type: [3 x {i64, handle}] + │ │ │ ├── members[0] > member 0 > call: + │ │ │ │ ├── type: {i64, handle} + │ │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ │ └── args[0]: 4 : i64 + │ │ │ ├── members[1] > member 1 > call: + │ │ │ │ ├── type: {i64, handle} + │ │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ │ └── args[0]: 5 : i64 + │ │ │ └── members[2] > member 2 > call: + │ │ │ ├── type: {i64, handle} + │ │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ │ └── args[0]: 6 : i64 + │ │ └── layout: ^%primitives$ArrayRef + │ ├── body[20] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$add(this arrayrefs$Player, %primitives$Int) + │ │ ├── args[0] > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0]: address of #17 : ptr + │ │ │ ├── args[1]: 1 : i64 + │ │ │ ├── args[2]: 3 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ └── args[1]: 1 : i64 + │ ├── body[21] > hold: + │ │ ├── local: #18 + │ │ ├── scope: %2 + │ │ ├── value > take: + │ │ │ ├── type: [3 x {i64, handle}] + │ │ │ ├── address: address of #17 : ptr + │ │ │ └── layout: %primitives$ArrayRef + │ │ └── layout: %primitives$ArrayRef + │ ├── body[22] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, handle} + │ │ │ ├── path: 0 + │ │ │ └── base > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0]: address of #18 : ptr + │ │ │ ├── args[1]: 1 : i64 + │ │ │ ├── args[2]: 3 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ └── right: 5 : i64 + │ ├── body[23] > let: + │ │ ├── local: #19 + │ │ └── value: 0 : i64 + │ ├── body[24] > let: + │ │ ├── local: #20 + │ │ └── value: 1 : i64 + │ ├── body[25] > eval > expand: + │ │ ├── type: void + │ │ ├── label: @22 + │ │ ├── body[0] > let: + │ │ │ ├── local: #21 + │ │ │ └── value: 1000 : i64 + │ │ ├── body[1] > repeat: + │ │ │ ├── count > binary: + │ │ │ │ ├── type: i64 + │ │ │ │ ├── op: + + │ │ │ │ ├── left > binary: + │ │ │ │ │ ├── type: i64 + │ │ │ │ │ ├── op: + + │ │ │ │ │ ├── left: local #21 : i64 + │ │ │ │ │ └── right > flip: + │ │ │ │ │ ├── type: i64 + │ │ │ │ │ └── value: local #20 : i64 + │ │ │ │ └── right: 1 : i64 + │ │ │ ├── body[0] > if: + │ │ │ │ ├── cond: true : i1 + │ │ │ │ └── body[0] > scope: + │ │ │ │ ├── id: %31 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #32 + │ │ │ │ │ ├── scope: %31 + │ │ │ │ │ ├── value > expand: + │ │ │ │ │ │ ├── type: [4 x {i64, handle}] + │ │ │ │ │ │ ├── label: @29 + │ │ │ │ │ │ ├── result: #30 + │ │ │ │ │ │ ├── body[0] > let: + │ │ │ │ │ │ │ ├── local: #25 + │ │ │ │ │ │ │ └── value: function arrayrefs$main()$lambda2 + │ │ │ │ │ │ ├── body[1] > reserve: + │ │ │ │ │ │ │ ├── local: #26 + │ │ │ │ │ │ │ ├── scope: %31 + │ │ │ │ │ │ │ ├── type: [4 x {i64, handle}] + │ │ │ │ │ │ │ └── layout: %primitives$ArrayRef + │ │ │ │ │ │ ├── body[2] > let: + │ │ │ │ │ │ │ ├── local: #27 + │ │ │ │ │ │ │ └── value: 1 : i64 + │ │ │ │ │ │ ├── body[3] > repeat: + │ │ │ │ │ │ │ ├── count: 4 : i64 + │ │ │ │ │ │ │ ├── body[0] > let: + │ │ │ │ │ │ │ │ ├── local: #28 + │ │ │ │ │ │ │ │ └── value > call value: + │ │ │ │ │ │ │ │ ├── type: {i64, handle} + │ │ │ │ │ │ │ │ ├── fn: local #25 : ptr + │ │ │ │ │ │ │ │ └── args[0]: local #27 : i64 + │ │ │ │ │ │ │ ├── body[1] > place: + │ │ │ │ │ │ │ │ ├── address > runtime: + │ │ │ │ │ │ │ │ │ ├── type: ptr + │ │ │ │ │ │ │ │ │ ├── fn: zane_array_at + │ │ │ │ │ │ │ │ │ ├── args[0]: address of #26 : ptr + │ │ │ │ │ │ │ │ │ ├── args[1]: local #27 : i64 + │ │ │ │ │ │ │ │ │ ├── args[2]: 4 : i64 + │ │ │ │ │ │ │ │ │ └── args[3]: 32 : i64 + │ │ │ │ │ │ │ │ ├── value: local #28 : {i64, handle} + │ │ │ │ │ │ │ │ └── layout: arrayrefs$Player + │ │ │ │ │ │ │ └── body[2] > assign: + │ │ │ │ │ │ │ ├── place: #27 + │ │ │ │ │ │ │ └── value > binary: + │ │ │ │ │ │ │ ├── type: i64 + │ │ │ │ │ │ │ ├── op: + + │ │ │ │ │ │ │ ├── left: local #27 : i64 + │ │ │ │ │ │ │ └── right: 1 : i64 + │ │ │ │ │ │ └── body[4] > assign: + │ │ │ │ │ │ ├── place: #30 + │ │ │ │ │ │ └── value > take: + │ │ │ │ │ │ ├── type: [4 x {i64, handle}] + │ │ │ │ │ │ ├── address: address of #26 : ptr + │ │ │ │ │ │ └── layout: %primitives$ArrayRef + │ │ │ │ │ └── layout: %primitives$ArrayRef + │ │ │ │ └── body[1] > assign: + │ │ │ │ ├── place: #19 + │ │ │ │ └── value > binary: + │ │ │ │ ├── type: i64 + │ │ │ │ ├── op: + + │ │ │ │ ├── left: local #19 : i64 + │ │ │ │ └── right > snapshot: + │ │ │ │ ├── type: i64 + │ │ │ │ └── ptr > offset: + │ │ │ │ ├── within: {i64, handle} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base > runtime: + │ │ │ │ ├── type: ptr + │ │ │ │ ├── fn: zane_array_at + │ │ │ │ ├── args[0]: address of #32 : ptr + │ │ │ │ ├── args[1]: 4 : i64 + │ │ │ │ ├── args[2]: 4 : i64 + │ │ │ │ └── args[3]: 32 : i64 + │ │ │ └── body[1] > assign: + │ │ │ ├── place: #20 + │ │ │ └── value > binary: + │ │ │ ├── type: i64 + │ │ │ ├── op: + + │ │ │ ├── left: local #20 : i64 + │ │ │ └── right: 1 : i64 + │ │ ├── body[2] > eval: unit : void + │ │ └── body[3]: leave @22 + │ ├── body[26] > eval > call: + │ │ ├── type: void + │ │ ├── fn: arrayrefs$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left: local #19 : i64 + │ │ └── right: 4000 : i64 + │ └── body[27] > return: unit : void + ├── funcs[1] > func: + │ ├── symbol: arrayrefs$Player(%primitives$Int) + │ ├── params[0]: local #1 : i64 + │ ├── ret: {i64, handle} + │ └── body[0] > return > escape: + │ ├── exit: function + │ ├── value > record: + │ │ ├── type: {i64, handle} + │ │ ├── members[0] > member 0: local #1 : i64 + │ │ └── members[1] > member 1: "p" : handle + │ └── layout: arrayrefs$Player + ├── funcs[2] > func: + │ ├── symbol: arrayrefs$check(%primitives$Bool) + │ ├── params[0]: local #1 : i1 + │ ├── ret: void + │ └── body[0] > scope: + │ ├── id: %3 + │ ├── body[0] > hold: + │ │ ├── local: #2 + │ │ ├── scope: %3 + │ │ ├── value: "yes\n" : handle + │ │ └── layout: %primitives$String + │ ├── body[1] > hold: + │ │ ├── local: #4 + │ │ ├── scope: %3 + │ │ ├── value: "no\n" : handle + │ │ └── layout: %primitives$String + │ ├── body[2] > if: + │ │ ├── cond: local #1 : i1 + │ │ └── body[0] > eval > runtime: + │ │ ├── type: void + │ │ ├── fn: zane_print + │ │ └── args[0]: local #2 : handle + │ ├── body[3] > if: + │ │ ├── cond > flip: + │ │ │ ├── type: i1 + │ │ │ └── value: local #1 : i1 + │ │ └── body[0] > eval > runtime: + │ │ ├── type: void + │ │ ├── fn: zane_print + │ │ └── args[0]: local #4 : handle + │ └── body[4] > return: unit : void + ├── funcs[3] > func: + │ ├── symbol: arrayrefs$add(this arrayrefs$Player, %primitives$Int) + │ ├── params[0]: local #1 : ptr + │ ├── params[1]: local #2 : i64 + │ ├── ret: void + │ ├── body[0] > assign: + │ │ ├── place: *#1.0 + │ │ └── value > binary: + │ │ ├── type: i64 + │ │ ├── op: + + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, handle} + │ │ │ ├── path: 0 + │ │ │ └── base: local #1 : ptr + │ │ └── right: local #2 : i64 + │ └── body[1] > return: unit : void + ├── funcs[4] > func: + │ ├── symbol: arrayrefs$scoreOf(&arrayrefs$Player) + │ ├── params[0]: local #1 : ptr + │ ├── ret: i64 + │ └── body[0] > return > snapshot: + │ ├── type: i64 + │ └── ptr > offset: + │ ├── within: {i64, handle} + │ ├── path: 0 + │ └── base: local #1 : ptr + ├── funcs[5] > func: + │ ├── symbol: arrayrefs$main()$lambda1 + │ ├── params[0]: local #1 : i64 + │ ├── ret: {i64, handle} + │ └── body[0] > return > escape: + │ ├── exit: function + │ ├── value > call: + │ │ ├── type: {i64, handle} + │ │ ├── fn: arrayrefs$Player(%primitives$Int) + │ │ └── args[0] > binary: + │ │ ├── type: i64 + │ │ ├── op: * + │ │ ├── left: local #1 : i64 + │ │ └── right: local #1 : i64 + │ └── layout: ^arrayrefs$Player + ├── funcs[6] > func: + │ ├── symbol: arrayrefs$Squad(^%primitives$ArrayRef) + │ ├── params[0]: local #1 : [3 x {i64, handle}] + │ ├── ret: {[3 x {i64, handle}]} + │ └── body[0] > scope: + │ ├── id: %3 + │ ├── body[0] > hold: + │ │ ├── local: #2 + │ │ ├── scope: %3 + │ │ ├── value: local #1 : [3 x {i64, handle}] + │ │ └── layout: ^%primitives$ArrayRef + │ └── body[1] > return > escape: + │ ├── exit: function + │ ├── value > record: + │ │ ├── type: {[3 x {i64, handle}]} + │ │ └── members[0] > member 0 > take: + │ │ ├── type: [3 x {i64, handle}] + │ │ ├── address: address of #2 : ptr + │ │ └── layout: %primitives$ArrayRef + │ └── layout: arrayrefs$Squad + ├── funcs[7] > func: + │ ├── symbol: arrayrefs$replace(this arrayrefs$Squad, %primitives$Int, ^arrayrefs$Player) + │ ├── params[0]: local #1 : ptr + │ ├── params[1]: local #2 : i64 + │ ├── params[2]: local #3 : {i64, handle} + │ ├── ret: void + │ └── body[0] > scope: + │ ├── id: %5 + │ ├── body[0] > hold: + │ │ ├── local: #4 + │ │ ├── scope: %5 + │ │ ├── value: local #3 : {i64, handle} + │ │ └── layout: ^arrayrefs$Player + │ ├── body[1] > overwrite: + │ │ ├── address > runtime: + │ │ │ ├── type: ptr + │ │ │ ├── fn: zane_array_at + │ │ │ ├── args[0] > offset: + │ │ │ │ ├── within: {[3 x {i64, handle}]} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base: local #1 : ptr + │ │ │ ├── args[1]: local #2 : i64 + │ │ │ ├── args[2]: 3 : i64 + │ │ │ └── args[3]: 32 : i64 + │ │ ├── value > take: + │ │ │ ├── type: {i64, handle} + │ │ │ ├── address: address of #4 : ptr + │ │ │ └── layout: arrayrefs$Player + │ │ └── layout: arrayrefs$Player + │ └── body[2] > return: unit : void + └── funcs[8] > func: + ├── symbol: arrayrefs$main()$lambda2 + ├── params[0]: local #1 : i64 + ├── ret: {i64, handle} + └── body[0] > return > escape: + ├── exit: function + ├── value > call: + │ ├── type: {i64, handle} + │ ├── fn: arrayrefs$Player(%primitives$Int) + │ └── args[0]: local #1 : i64 + └── layout: ^arrayrefs$Player diff --git a/tests/runtime/golden/anchors.out b/tests/codegen/golden/arrayrefs.out similarity index 63% rename from tests/runtime/golden/anchors.out rename to tests/codegen/golden/arrayrefs.out index 7700d6f2..de5620b3 100644 --- a/tests/runtime/golden/anchors.out +++ b/tests/codegen/golden/arrayrefs.out @@ -5,3 +5,7 @@ yes yes yes yes +yes +yes +yes +yes diff --git a/tests/codegen/golden/arrays.cgt b/tests/codegen/golden/arrays.cgt index 27f47c4a..ff6ee4ee 100644 --- a/tests/codegen/golden/arrays.cgt +++ b/tests/codegen/golden/arrays.cgt @@ -2,7 +2,7 @@ ├── entry: arrays$main() ├── layouts[0] > layout: │ ├── type: arrays$Box - │ └── positions[0]: @0 (48 bytes) + │ └── positions: ├── layouts[1] > layout: │ ├── type: %primitives$Array │ ├── positions[0]: box @8 of %primitives$Array (32 bytes) when [0]=1 @@ -12,8 +12,7 @@ │ └── positions[0]: box @8 of %primitives$Array (32 bytes) when [0]=1 ├── layouts[3] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── funcs[0] > func: │ ├── symbol: arrays$main() │ ├── params: @@ -176,11 +175,11 @@ │ │ │ ├── args[0]: local #6 : {[4 x i64], i64} │ │ │ └── args[1]: 4 : i64 │ │ └── right: 40 : i64 - │ ├── body[13] > host: + │ ├── body[13] > hold: │ │ ├── local: #7 │ │ ├── scope: %8 │ │ ├── value > call: - │ │ │ ├── type: {i32, {[4 x i64], i64}} + │ │ │ ├── type: {{[4 x i64], i64}} │ │ │ ├── fn: arrays$Box(arrays$Grid) │ │ │ └── args[0]: local #6 : {[4 x i64], i64} │ │ └── layout: arrays$Box @@ -192,8 +191,8 @@ │ │ │ │ ├── within: {[4 x i64], i64} │ │ │ │ ├── path: 0 │ │ │ │ └── base > offset: - │ │ │ │ ├── within: {i32, {[4 x i64], i64}} - │ │ │ │ ├── path: 1 + │ │ │ │ ├── within: {{[4 x i64], i64}} + │ │ │ │ ├── path: 0 │ │ │ │ └── base: address of #7 : ptr │ │ │ ├── args[1]: 1 : i64 │ │ │ ├── args[2]: 4 : i64 @@ -217,8 +216,8 @@ │ │ │ │ │ ├── within: {[4 x i64], i64} │ │ │ │ │ ├── path: 0 │ │ │ │ │ └── base > offset: - │ │ │ │ │ ├── within: {i32, {[4 x i64], i64}} - │ │ │ │ │ ├── path: 1 + │ │ │ │ │ ├── within: {{[4 x i64], i64}} + │ │ │ │ │ ├── path: 0 │ │ │ │ │ └── base: address of #7 : ptr │ │ │ │ ├── args[1]: 1 : i64 │ │ │ │ ├── args[2]: 4 : i64 @@ -229,12 +228,12 @@ │ │ │ ├── args[0] > snapshot: │ │ │ │ ├── type: {[4 x i64], i64} │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, {[4 x i64], i64}} - │ │ │ │ ├── path: 1 + │ │ │ │ ├── within: {{[4 x i64], i64}} + │ │ │ │ ├── path: 0 │ │ │ │ └── base: address of #7 : ptr │ │ │ └── args[1]: 4 : i64 │ │ └── right: 50 : i64 - │ ├── body[16] > host: + │ ├── body[16] > hold: │ │ ├── local: #9 │ │ ├── scope: %8 │ │ ├── value > case: @@ -275,7 +274,7 @@ │ │ │ ├── fn: arrays$total(arrays$Tree) │ │ │ └── args[0]: local #9 : │ │ └── right: 6 : i64 - │ ├── body[18] > host: + │ ├── body[18] > hold: │ │ ├── local: #10 │ │ ├── scope: %8 │ │ ├── value > copy: @@ -357,12 +356,12 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -449,13 +448,12 @@ ├── funcs[8] > func: │ ├── symbol: arrays$Box(arrays$Grid) │ ├── params[0]: local #1 : {[4 x i64], i64} - │ ├── ret: {i32, {[4 x i64], i64}} + │ ├── ret: {{[4 x i64], i64}} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, {[4 x i64], i64}} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ └── members[1] > member 1: local #1 : {[4 x i64], i64} + │ │ ├── type: {{[4 x i64], i64}} + │ │ └── members[0] > member 0: local #1 : {[4 x i64], i64} │ └── layout: arrays$Box └── funcs[9] > func: ├── symbol: arrays$total(arrays$Tree) diff --git a/tests/codegen/golden/boxes.cgt b/tests/codegen/golden/boxes.cgt index acafd9cc..939e32f6 100644 --- a/tests/codegen/golden/boxes.cgt +++ b/tests/codegen/golden/boxes.cgt @@ -12,25 +12,33 @@ │ └── positions[1]: box @8 of boxes$Tree (16 bytes) ├── layouts[3] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── layouts[4] > layout: - │ ├── type: boxes$Link - │ ├── positions[0]: @0 (48 bytes) - │ ├── positions[1]: @8 (32 bytes) - │ ├── positions[2]: text @8 - │ └── positions[3]: box @40 of boxes$Chain (24 bytes) - ├── layouts[5] > layout: │ ├── type: boxes$Chain - │ ├── positions[0]: @0 (24 bytes) - │ └── positions[1]: box @16 of boxes$Link (48 bytes) when [8]=1 + │ └── positions[0]: box @8 of boxes$Link (32 bytes) when [0]=1 + ├── layouts[5] > layout: + │ ├── type: boxes$Link + │ ├── positions[0]: text @0 + │ └── positions[1]: box @24 of boxes$Chain (16 bytes) + ├── layouts[6] > layout: + │ ├── type: ^boxes$Chain + │ └── positions[0]: box @8 of boxes$Link (32 bytes) when [0]=1 + ├── layouts[7] > layout: + │ ├── type: boxes$Storey + │ └── positions[0]: box @8 of boxes$Tower (16 bytes) when [0]=1 + ├── layouts[8] > layout: + │ ├── type: boxes$Tower + │ └── positions[0]: box @8 of boxes$Storey (16 bytes) + ├── layouts[9] > layout: + │ ├── type: ^boxes$Storey + │ └── positions[0]: box @8 of boxes$Tower (16 bytes) when [0]=1 ├── funcs[0] > func: │ ├── symbol: boxes$main() │ ├── params: │ ├── ret: void │ └── body[0] > scope: │ ├── id: %2 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #1 │ │ ├── scope: %2 │ │ ├── value > case: @@ -59,7 +67,7 @@ │ │ │ ├── fn: boxes$depth(boxes$Countdown) │ │ │ └── args[0]: local #1 : │ │ └── right: 2 : i64 - │ ├── body[2] > host: + │ ├── body[2] > hold: │ │ ├── local: #3 │ │ ├── scope: %2 │ │ ├── value > copy: @@ -96,7 +104,7 @@ │ │ │ ├── fn: boxes$depth(boxes$Countdown) │ │ │ └── args[0]: local #1 : │ │ └── right: 0 : i64 - │ ├── body[6] > host: + │ ├── body[6] > hold: │ │ ├── local: #8 │ │ ├── scope: %2 │ │ ├── value > copy: @@ -209,7 +217,7 @@ │ │ │ │ └── value: address of #12 : ptr │ │ │ └── body[6]: leave @9 │ │ └── right: 1 : i64 - │ ├── body[9] > host: + │ ├── body[9] > hold: │ │ ├── local: #25 │ │ ├── scope: %2 │ │ ├── value > case: @@ -223,7 +231,7 @@ │ │ │ │ │ ├── type: │ │ │ │ │ ├── label: @14 │ │ │ │ │ ├── result: #15 - │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ ├── local: #13 │ │ │ │ │ │ ├── scope: %2 │ │ │ │ │ │ ├── value > case: @@ -238,7 +246,7 @@ │ │ │ │ ├── type: │ │ │ │ ├── label: @23 │ │ │ │ ├── result: #24 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #22 │ │ │ │ │ ├── scope: %2 │ │ │ │ │ ├── value > case: @@ -252,7 +260,7 @@ │ │ │ │ │ │ │ │ ├── type: │ │ │ │ │ │ │ │ ├── label: @17 │ │ │ │ │ │ │ │ ├── result: #18 - │ │ │ │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ │ │ │ ├── local: #16 │ │ │ │ │ │ │ │ │ ├── scope: %2 │ │ │ │ │ │ │ │ │ ├── value > case: @@ -267,7 +275,7 @@ │ │ │ │ │ │ │ ├── type: │ │ │ │ │ │ │ ├── label: @20 │ │ │ │ │ │ │ ├── result: #21 - │ │ │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ │ │ ├── local: #19 │ │ │ │ │ │ │ │ ├── scope: %2 │ │ │ │ │ │ │ │ ├── value > case: @@ -285,7 +293,7 @@ │ │ │ │ └── value: local #22 : │ │ │ └── layout: boxes$Pair │ │ └── layout: boxes$Tree - │ ├── body[10] > host: + │ ├── body[10] > hold: │ │ ├── local: #26 │ │ ├── scope: %2 │ │ ├── value > copy: @@ -322,85 +330,54 @@ │ │ │ ├── fn: boxes$total(boxes$Tree) │ │ │ └── args[0]: local #25 : │ │ └── right: 0 : i64 - │ ├── body[14] > host: - │ │ ├── local: #39 + │ ├── body[14] > hold: + │ │ ├── local: #33 │ │ ├── scope: %2 - │ │ ├── value > record: - │ │ │ ├── type: {i32, } - │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ └── members[1] > member 1 > case: - │ │ │ ├── type: - │ │ │ ├── index: 1 - │ │ │ └── payload > box: - │ │ │ ├── value > call: - │ │ │ │ ├── type: {i32, handle, ptr} - │ │ │ │ ├── fn: boxes$Link(%primitives$String, boxes$Chain) - │ │ │ │ ├── args[0] > expand: - │ │ │ │ │ ├── type: ptr - │ │ │ │ │ ├── label: @28 - │ │ │ │ │ ├── result: #29 - │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ ├── local: #27 - │ │ │ │ │ │ ├── scope: %2 - │ │ │ │ │ │ ├── value: "a" : handle - │ │ │ │ │ │ └── layout: %primitives$String - │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ ├── place: #29 - │ │ │ │ │ └── value: address of #27 : ptr - │ │ │ │ └── args[1] > expand: - │ │ │ │ ├── type: ptr - │ │ │ │ ├── label: @37 - │ │ │ │ ├── result: #38 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #36 - │ │ │ │ │ ├── scope: %2 - │ │ │ │ │ ├── value > record: - │ │ │ │ │ │ ├── type: {i32, } - │ │ │ │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ │ │ │ └── members[1] > member 1 > case: - │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ ├── index: 1 - │ │ │ │ │ │ └── payload > box: - │ │ │ │ │ │ ├── value > call: - │ │ │ │ │ │ │ ├── type: {i32, handle, ptr} - │ │ │ │ │ │ │ ├── fn: boxes$Link(%primitives$String, boxes$Chain) - │ │ │ │ │ │ │ ├── args[0] > expand: - │ │ │ │ │ │ │ │ ├── type: ptr - │ │ │ │ │ │ │ │ ├── label: @31 - │ │ │ │ │ │ │ │ ├── result: #32 - │ │ │ │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ │ │ │ ├── local: #30 - │ │ │ │ │ │ │ │ │ ├── scope: %2 - │ │ │ │ │ │ │ │ │ ├── value: "b" : handle - │ │ │ │ │ │ │ │ │ └── layout: %primitives$String - │ │ │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ │ │ ├── place: #32 - │ │ │ │ │ │ │ │ └── value: address of #30 : ptr - │ │ │ │ │ │ │ └── args[1] > expand: - │ │ │ │ │ │ │ ├── type: ptr - │ │ │ │ │ │ │ ├── label: @34 - │ │ │ │ │ │ │ ├── result: #35 - │ │ │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ │ │ ├── local: #33 - │ │ │ │ │ │ │ │ ├── scope: %2 - │ │ │ │ │ │ │ │ ├── value > record: - │ │ │ │ │ │ │ │ │ ├── type: {i32, } - │ │ │ │ │ │ │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ │ │ │ │ │ │ └── members[1] > member 1 > case: - │ │ │ │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ │ │ │ ├── index: 0 - │ │ │ │ │ │ │ │ │ └── payload: unit : void - │ │ │ │ │ │ │ │ └── layout: boxes$Chain - │ │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ │ ├── place: #35 - │ │ │ │ │ │ │ └── value: address of #33 : ptr - │ │ │ │ │ │ └── layout: boxes$Link - │ │ │ │ │ └── layout: boxes$Chain - │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #38 - │ │ │ │ └── value: address of #36 : ptr - │ │ │ └── layout: boxes$Link - │ │ └── layout: boxes$Chain + │ │ ├── value > case: + │ │ │ ├── type: + │ │ │ ├── index: 1 + │ │ │ └── payload > box: + │ │ │ ├── value > call: + │ │ │ │ ├── type: {handle, ptr} + │ │ │ │ ├── fn: boxes$Link(%primitives$String, ^boxes$Chain) + │ │ │ │ ├── args[0] > expand: + │ │ │ │ │ ├── type: handle + │ │ │ │ │ ├── label: @28 + │ │ │ │ │ ├── result: #29 + │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ ├── local: #27 + │ │ │ │ │ │ ├── scope: %2 + │ │ │ │ │ │ ├── value: "a" : handle + │ │ │ │ │ │ └── layout: %primitives$String + │ │ │ │ │ └── body[1] > assign: + │ │ │ │ │ ├── place: #29 + │ │ │ │ │ └── value: local #27 : handle + │ │ │ │ └── args[1] > case: + │ │ │ │ ├── type: + │ │ │ │ ├── index: 1 + │ │ │ │ └── payload > box: + │ │ │ │ ├── value > call: + │ │ │ │ │ ├── type: {handle, ptr} + │ │ │ │ │ ├── fn: boxes$Link(%primitives$String, ^boxes$Chain) + │ │ │ │ │ ├── args[0] > expand: + │ │ │ │ │ │ ├── type: handle + │ │ │ │ │ │ ├── label: @31 + │ │ │ │ │ │ ├── result: #32 + │ │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ │ ├── local: #30 + │ │ │ │ │ │ │ ├── scope: %2 + │ │ │ │ │ │ │ ├── value: "b" : handle + │ │ │ │ │ │ │ └── layout: %primitives$String + │ │ │ │ │ │ └── body[1] > assign: + │ │ │ │ │ │ ├── place: #32 + │ │ │ │ │ │ └── value: local #30 : handle + │ │ │ │ │ └── args[1] > case: + │ │ │ │ │ ├── type: + │ │ │ │ │ ├── index: 0 + │ │ │ │ │ └── payload: unit : void + │ │ │ │ └── layout: boxes$Link + │ │ │ └── layout: boxes$Link + │ │ └── layout: ^boxes$Chain │ ├── body[15] > eval > call: │ │ ├── type: void │ │ ├── fn: boxes$check(%primitives$Bool) @@ -410,19 +387,19 @@ │ │ ├── left > call: │ │ │ ├── type: i64 │ │ │ ├── fn: boxes$length(this boxes$Chain) - │ │ │ └── args[0]: address of #39 : ptr + │ │ │ └── args[0]: address of #33 : ptr │ │ └── right: 2 : i64 - │ ├── body[16] > let: - │ │ ├── local: #40 - │ │ └── value > mint: address of #39 : ptr - │ ├── body[17] > host: - │ │ ├── local: #41 + │ ├── body[16] > hold: + │ │ ├── local: #34 │ │ ├── scope: %2 │ │ ├── value > take: - │ │ │ ├── type: {i32, } - │ │ │ ├── address: address of #39 : ptr + │ │ │ ├── type: + │ │ │ ├── address: address of #33 : ptr │ │ │ └── layout: boxes$Chain │ │ └── layout: boxes$Chain + │ ├── body[17] > let: + │ │ ├── local: #35 + │ │ └── value: address of #34 : ptr │ ├── body[18] > eval > call: │ │ ├── type: void │ │ ├── fn: boxes$check(%primitives$Bool) @@ -432,7 +409,7 @@ │ │ ├── left > call: │ │ │ ├── type: i64 │ │ │ ├── fn: boxes$length(this boxes$Chain) - │ │ │ └── args[0] > resolve: local #40 : i32 + │ │ │ └── args[0]: local #35 : ptr │ │ └── right: 2 : i64 │ ├── body[19] > eval > call: │ │ ├── type: void @@ -443,97 +420,80 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > member: + │ │ │ ├── args[0] > snapshot: │ │ │ │ ├── type: handle - │ │ │ │ ├── index: 1 - │ │ │ │ └── value > deref: - │ │ │ │ ├── type: {i32, handle, ptr} - │ │ │ │ └── ptr > expand: + │ │ │ │ └── ptr > offset: + │ │ │ │ ├── within: {handle, ptr} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base > expand: │ │ │ │ ├── type: ptr - │ │ │ │ ├── label: @42 - │ │ │ │ ├── result: #43 + │ │ │ │ ├── label: @36 + │ │ │ │ ├── result: #37 │ │ │ │ ├── body[0] > reserve: - │ │ │ │ │ ├── local: #45 + │ │ │ │ │ ├── local: #39 │ │ │ │ │ ├── scope: %2 - │ │ │ │ │ ├── type: {i32, handle, ptr} + │ │ │ │ │ ├── type: {handle, ptr} │ │ │ │ │ └── layout: boxes$Link │ │ │ │ ├── body[1] > let: - │ │ │ │ │ ├── local: #44 - │ │ │ │ │ └── value: address of #41 : ptr + │ │ │ │ │ ├── local: #38 + │ │ │ │ │ └── value: address of #34 : ptr │ │ │ │ ├── body[2] > switch: - │ │ │ │ │ ├── value > member: + │ │ │ │ │ ├── value > deref: │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ ├── index: 1 - │ │ │ │ │ │ └── value > deref: - │ │ │ │ │ │ ├── type: {i32, } - │ │ │ │ │ │ └── ptr: local #44 : ptr + │ │ │ │ │ │ └── ptr: local #38 : ptr │ │ │ │ │ ├── cases[0] > case 0: │ │ │ │ │ ├── cases[1] > case 1[0] > assign: - │ │ │ │ │ │ ├── place: #43 + │ │ │ │ │ │ ├── place: #37 │ │ │ │ │ │ └── value > deref: │ │ │ │ │ │ ├── type: ptr │ │ │ │ │ │ └── ptr > offset: - │ │ │ │ │ │ ├── within: {i32, } - │ │ │ │ │ │ ├── path: 1.1 - │ │ │ │ │ │ └── base: local #44 : ptr - │ │ │ │ │ └── cases[1] > case 1[1]: leave @42 + │ │ │ │ │ │ ├── within: + │ │ │ │ │ │ ├── path: 1 + │ │ │ │ │ │ └── base: local #38 : ptr + │ │ │ │ │ └── cases[1] > case 1[1]: leave @36 │ │ │ │ ├── body[3] > let: - │ │ │ │ │ ├── local: #46 + │ │ │ │ │ ├── local: #40 │ │ │ │ │ └── value: unit : void │ │ │ │ └── body[4] > scope: - │ │ │ │ ├── id: %50 + │ │ │ │ ├── id: %44 │ │ │ │ ├── body[0] > place: - │ │ │ │ │ ├── address: address of #45 : ptr + │ │ │ │ │ ├── address: address of #39 : ptr │ │ │ │ │ ├── value > call: - │ │ │ │ │ │ ├── type: {i32, handle, ptr} - │ │ │ │ │ │ ├── fn: boxes$Link(%primitives$String, boxes$Chain) + │ │ │ │ │ │ ├── type: {handle, ptr} + │ │ │ │ │ │ ├── fn: boxes$Link(%primitives$String, ^boxes$Chain) │ │ │ │ │ │ ├── args[0] > expand: - │ │ │ │ │ │ │ ├── type: ptr - │ │ │ │ │ │ │ ├── label: @48 - │ │ │ │ │ │ │ ├── result: #49 - │ │ │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ │ │ ├── local: #47 - │ │ │ │ │ │ │ │ ├── scope: %50 + │ │ │ │ │ │ │ ├── type: handle + │ │ │ │ │ │ │ ├── label: @42 + │ │ │ │ │ │ │ ├── result: #43 + │ │ │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ │ │ ├── local: #41 + │ │ │ │ │ │ │ │ ├── scope: %44 │ │ │ │ │ │ │ │ ├── value: "none" : handle │ │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ │ ├── place: #49 - │ │ │ │ │ │ │ └── value: address of #47 : ptr - │ │ │ │ │ │ └── args[1] > expand: - │ │ │ │ │ │ ├── type: ptr - │ │ │ │ │ │ ├── label: @52 - │ │ │ │ │ │ ├── result: #53 - │ │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ │ ├── local: #51 - │ │ │ │ │ │ │ ├── scope: %50 - │ │ │ │ │ │ │ ├── value > record: - │ │ │ │ │ │ │ │ ├── type: {i32, } - │ │ │ │ │ │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ │ │ │ │ │ └── members[1] > member 1 > case: - │ │ │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ │ │ ├── index: 0 - │ │ │ │ │ │ │ │ └── payload: unit : void - │ │ │ │ │ │ │ └── layout: boxes$Chain - │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ ├── place: #53 - │ │ │ │ │ │ └── value: address of #51 : ptr + │ │ │ │ │ │ │ ├── place: #43 + │ │ │ │ │ │ │ └── value: local #41 : handle + │ │ │ │ │ │ └── args[1] > case: + │ │ │ │ │ │ ├── type: + │ │ │ │ │ │ ├── index: 0 + │ │ │ │ │ │ └── payload: unit : void │ │ │ │ │ └── layout: boxes$Link │ │ │ │ ├── body[1] > assign: - │ │ │ │ │ ├── place: #43 - │ │ │ │ │ └── value: address of #45 : ptr - │ │ │ │ └── body[2]: leave @42 + │ │ │ │ │ ├── place: #37 + │ │ │ │ │ └── value: address of #39 : ptr + │ │ │ │ └── body[2]: leave @36 │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @55 - │ │ │ ├── result: #56 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #54 + │ │ │ ├── label: @46 + │ │ │ ├── result: #47 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #45 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "a" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #56 - │ │ │ └── value: local #54 : handle + │ │ │ ├── place: #47 + │ │ │ └── value: local #45 : handle │ │ └── right: 1 : i64 │ ├── body[20] > eval > call: │ │ ├── type: void @@ -544,123 +504,196 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > member: + │ │ │ ├── args[0] > snapshot: │ │ │ │ ├── type: handle - │ │ │ │ ├── index: 1 - │ │ │ │ └── value > deref: - │ │ │ │ ├── type: {i32, handle, ptr} - │ │ │ │ └── ptr > expand: + │ │ │ │ └── ptr > offset: + │ │ │ │ ├── within: {handle, ptr} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base > expand: │ │ │ │ ├── type: ptr - │ │ │ │ ├── label: @57 - │ │ │ │ ├── result: #58 + │ │ │ │ ├── label: @48 + │ │ │ │ ├── result: #49 │ │ │ │ ├── body[0] > reserve: - │ │ │ │ │ ├── local: #60 + │ │ │ │ │ ├── local: #51 │ │ │ │ │ ├── scope: %2 - │ │ │ │ │ ├── type: {i32, handle, ptr} + │ │ │ │ │ ├── type: {handle, ptr} │ │ │ │ │ └── layout: boxes$Link │ │ │ │ ├── body[1] > let: - │ │ │ │ │ ├── local: #59 + │ │ │ │ │ ├── local: #50 │ │ │ │ │ └── value > expand: │ │ │ │ │ ├── type: ptr - │ │ │ │ │ ├── label: @70 - │ │ │ │ │ ├── result: #71 - │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ ├── local: #69 + │ │ │ │ │ ├── label: @58 + │ │ │ │ │ ├── result: #59 + │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ ├── local: #57 │ │ │ │ │ │ ├── scope: %2 - │ │ │ │ │ │ ├── value > record: - │ │ │ │ │ │ │ ├── type: {i32, } - │ │ │ │ │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ │ │ │ │ └── members[1] > member 1 > case: - │ │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ │ ├── index: 0 - │ │ │ │ │ │ │ └── payload: unit : void + │ │ │ │ │ │ ├── value > case: + │ │ │ │ │ │ │ ├── type: + │ │ │ │ │ │ │ ├── index: 0 + │ │ │ │ │ │ │ └── payload: unit : void │ │ │ │ │ │ └── layout: boxes$Chain │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ ├── place: #71 - │ │ │ │ │ └── value: address of #69 : ptr + │ │ │ │ │ ├── place: #59 + │ │ │ │ │ └── value: address of #57 : ptr │ │ │ │ ├── body[2] > switch: - │ │ │ │ │ ├── value > member: + │ │ │ │ │ ├── value > deref: │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ ├── index: 1 - │ │ │ │ │ │ └── value > deref: - │ │ │ │ │ │ ├── type: {i32, } - │ │ │ │ │ │ └── ptr: local #59 : ptr + │ │ │ │ │ │ └── ptr: local #50 : ptr │ │ │ │ │ ├── cases[0] > case 0: │ │ │ │ │ ├── cases[1] > case 1[0] > assign: - │ │ │ │ │ │ ├── place: #58 + │ │ │ │ │ │ ├── place: #49 │ │ │ │ │ │ └── value > deref: │ │ │ │ │ │ ├── type: ptr │ │ │ │ │ │ └── ptr > offset: - │ │ │ │ │ │ ├── within: {i32, } - │ │ │ │ │ │ ├── path: 1.1 - │ │ │ │ │ │ └── base: local #59 : ptr - │ │ │ │ │ └── cases[1] > case 1[1]: leave @57 + │ │ │ │ │ │ ├── within: + │ │ │ │ │ │ ├── path: 1 + │ │ │ │ │ │ └── base: local #50 : ptr + │ │ │ │ │ └── cases[1] > case 1[1]: leave @48 │ │ │ │ ├── body[3] > let: - │ │ │ │ │ ├── local: #61 + │ │ │ │ │ ├── local: #52 │ │ │ │ │ └── value: unit : void │ │ │ │ └── body[4] > scope: - │ │ │ │ ├── id: %65 + │ │ │ │ ├── id: %56 │ │ │ │ ├── body[0] > place: - │ │ │ │ │ ├── address: address of #60 : ptr + │ │ │ │ │ ├── address: address of #51 : ptr │ │ │ │ │ ├── value > call: - │ │ │ │ │ │ ├── type: {i32, handle, ptr} - │ │ │ │ │ │ ├── fn: boxes$Link(%primitives$String, boxes$Chain) + │ │ │ │ │ │ ├── type: {handle, ptr} + │ │ │ │ │ │ ├── fn: boxes$Link(%primitives$String, ^boxes$Chain) │ │ │ │ │ │ ├── args[0] > expand: - │ │ │ │ │ │ │ ├── type: ptr - │ │ │ │ │ │ │ ├── label: @63 - │ │ │ │ │ │ │ ├── result: #64 - │ │ │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ │ │ ├── local: #62 - │ │ │ │ │ │ │ │ ├── scope: %65 + │ │ │ │ │ │ │ ├── type: handle + │ │ │ │ │ │ │ ├── label: @54 + │ │ │ │ │ │ │ ├── result: #55 + │ │ │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ │ │ ├── local: #53 + │ │ │ │ │ │ │ │ ├── scope: %56 │ │ │ │ │ │ │ │ ├── value: "none" : handle │ │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ │ ├── place: #64 - │ │ │ │ │ │ │ └── value: address of #62 : ptr - │ │ │ │ │ │ └── args[1] > expand: - │ │ │ │ │ │ ├── type: ptr - │ │ │ │ │ │ ├── label: @67 - │ │ │ │ │ │ ├── result: #68 - │ │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ │ ├── local: #66 - │ │ │ │ │ │ │ ├── scope: %65 - │ │ │ │ │ │ │ ├── value > record: - │ │ │ │ │ │ │ │ ├── type: {i32, } - │ │ │ │ │ │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ │ │ │ │ │ └── members[1] > member 1 > case: - │ │ │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ │ │ ├── index: 0 - │ │ │ │ │ │ │ │ └── payload: unit : void - │ │ │ │ │ │ │ └── layout: boxes$Chain - │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ ├── place: #68 - │ │ │ │ │ │ └── value: address of #66 : ptr + │ │ │ │ │ │ │ ├── place: #55 + │ │ │ │ │ │ │ └── value: local #53 : handle + │ │ │ │ │ │ └── args[1] > case: + │ │ │ │ │ │ ├── type: + │ │ │ │ │ │ ├── index: 0 + │ │ │ │ │ │ └── payload: unit : void │ │ │ │ │ └── layout: boxes$Link │ │ │ │ ├── body[1] > assign: - │ │ │ │ │ ├── place: #58 - │ │ │ │ │ └── value: address of #60 : ptr - │ │ │ │ └── body[2]: leave @57 + │ │ │ │ │ ├── place: #49 + │ │ │ │ │ └── value: address of #51 : ptr + │ │ │ │ └── body[2]: leave @48 │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @73 - │ │ │ ├── result: #74 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #72 + │ │ │ ├── label: @61 + │ │ │ ├── result: #62 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #60 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "none" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #74 - │ │ │ └── value: local #72 : handle + │ │ │ ├── place: #62 + │ │ │ └── value: local #60 : handle │ │ └── right: 1 : i64 - │ ├── body[21] > let: - │ │ ├── local: #75 + │ ├── body[21] > hold: + │ │ ├── local: #63 + │ │ ├── scope: %2 + │ │ ├── value > call: + │ │ │ ├── type: {i64, ptr} + │ │ │ ├── fn: boxes$Tower(%primitives$Int, ^boxes$Storey) + │ │ │ ├── args[0]: 1 : i64 + │ │ │ └── args[1] > case: + │ │ │ ├── type: + │ │ │ ├── index: 1 + │ │ │ └── payload > box: + │ │ │ ├── value > call: + │ │ │ │ ├── type: {i64, ptr} + │ │ │ │ ├── fn: boxes$Tower(%primitives$Int, ^boxes$Storey) + │ │ │ │ ├── args[0]: 2 : i64 + │ │ │ │ └── args[1] > case: + │ │ │ │ ├── type: + │ │ │ │ ├── index: 0 + │ │ │ │ └── payload: unit : void + │ │ │ └── layout: boxes$Tower + │ │ └── layout: boxes$Tower + │ ├── body[22] > let: + │ │ ├── local: #64 + │ │ └── value > deref: + │ │ ├── type: ptr + │ │ └── ptr > offset: + │ │ ├── within: {i64, ptr} + │ │ ├── path: 1 + │ │ └── base: address of #63 : ptr + │ ├── body[23] > eval > call: + │ │ ├── type: void + │ │ ├── fn: boxes$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > call: + │ │ │ ├── type: i64 + │ │ │ ├── fn: boxes$levels(this boxes$Storey) + │ │ │ └── args[0]: local #64 : ptr + │ │ └── right: 1 : i64 + │ ├── body[24] > overwrite: + │ │ ├── address: address of #63 : ptr + │ │ ├── value > call: + │ │ │ ├── type: {i64, ptr} + │ │ │ ├── fn: boxes$Tower(%primitives$Int, ^boxes$Storey) + │ │ │ ├── args[0]: 5 : i64 + │ │ │ └── args[1] > case: + │ │ │ ├── type: + │ │ │ ├── index: 1 + │ │ │ └── payload > box: + │ │ │ ├── value > call: + │ │ │ │ ├── type: {i64, ptr} + │ │ │ │ ├── fn: boxes$Tower(%primitives$Int, ^boxes$Storey) + │ │ │ │ ├── args[0]: 6 : i64 + │ │ │ │ └── args[1] > case: + │ │ │ │ ├── type: + │ │ │ │ ├── index: 1 + │ │ │ │ └── payload > box: + │ │ │ │ ├── value > call: + │ │ │ │ │ ├── type: {i64, ptr} + │ │ │ │ │ ├── fn: boxes$Tower(%primitives$Int, ^boxes$Storey) + │ │ │ │ │ ├── args[0]: 7 : i64 + │ │ │ │ │ └── args[1] > case: + │ │ │ │ │ ├── type: + │ │ │ │ │ ├── index: 0 + │ │ │ │ │ └── payload: unit : void + │ │ │ │ └── layout: boxes$Tower + │ │ │ └── layout: boxes$Tower + │ │ └── layout: boxes$Tower + │ ├── body[25] > eval > call: + │ │ ├── type: void + │ │ ├── fn: boxes$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > call: + │ │ │ ├── type: i64 + │ │ │ ├── fn: boxes$levels(this boxes$Storey) + │ │ │ └── args[0]: local #64 : ptr + │ │ └── right: 2 : i64 + │ ├── body[26] > eval > call: + │ │ ├── type: void + │ │ ├── fn: boxes$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, ptr} + │ │ │ ├── path: 0 + │ │ │ └── base: address of #63 : ptr + │ │ └── right: 5 : i64 + │ ├── body[27] > let: + │ │ ├── local: #65 │ │ └── value: 1 : i64 - │ ├── body[22] > eval > expand: + │ ├── body[28] > eval > expand: │ │ ├── type: void - │ │ ├── label: @77 + │ │ ├── label: @67 │ │ ├── body[0] > let: - │ │ │ ├── local: #76 + │ │ │ ├── local: #66 │ │ │ └── value: 1000 : i64 │ │ ├── body[1] > repeat: │ │ │ ├── count > binary: @@ -669,18 +702,18 @@ │ │ │ │ ├── left > binary: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #76 : i64 + │ │ │ │ │ ├── left: local #66 : i64 │ │ │ │ │ └── right > flip: │ │ │ │ │ ├── type: i64 - │ │ │ │ │ └── value: local #75 : i64 + │ │ │ │ │ └── value: local #65 : i64 │ │ │ │ └── right: 1 : i64 │ │ │ ├── body[0] > if: │ │ │ │ ├── cond: true : i1 │ │ │ │ └── body[0] > scope: - │ │ │ │ ├── id: %81 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #80 - │ │ │ │ │ ├── scope: %81 + │ │ │ │ ├── id: %71 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #70 + │ │ │ │ │ ├── scope: %71 │ │ │ │ │ ├── value > case: │ │ │ │ │ │ ├── type: │ │ │ │ │ │ ├── index: 1 @@ -696,31 +729,31 @@ │ │ │ │ │ │ │ └── layout: boxes$Countdown │ │ │ │ │ │ └── layout: boxes$Countdown │ │ │ │ │ └── layout: boxes$Countdown - │ │ │ │ ├── body[1] > host: - │ │ │ │ │ ├── local: #82 - │ │ │ │ │ ├── scope: %81 + │ │ │ │ ├── body[1] > hold: + │ │ │ │ │ ├── local: #72 + │ │ │ │ │ ├── scope: %71 │ │ │ │ │ ├── value > copy: │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ ├── value: local #80 : + │ │ │ │ │ │ ├── value: local #70 : │ │ │ │ │ │ └── layout: boxes$Countdown │ │ │ │ │ └── layout: boxes$Countdown │ │ │ │ └── body[2] > overwrite: - │ │ │ │ ├── address: address of #80 : ptr + │ │ │ │ ├── address: address of #70 : ptr │ │ │ │ ├── value > copy: │ │ │ │ │ ├── type: - │ │ │ │ │ ├── value: local #82 : + │ │ │ │ │ ├── value: local #72 : │ │ │ │ │ └── layout: boxes$Countdown │ │ │ │ └── layout: boxes$Countdown │ │ │ └── body[1] > assign: - │ │ │ ├── place: #75 + │ │ │ ├── place: #65 │ │ │ └── value > binary: │ │ │ ├── type: i64 │ │ │ ├── op: + - │ │ │ ├── left: local #75 : i64 + │ │ │ ├── left: local #65 : i64 │ │ │ └── right: 1 : i64 │ │ ├── body[2] > eval: unit : void - │ │ └── body[3]: leave @77 - │ └── body[23] > return: unit : void + │ │ └── body[3]: leave @67 + │ └── body[29] > return: unit : void ├── funcs[1] > func: │ ├── symbol: boxes$depth(boxes$Countdown) │ ├── params[0]: local #1 : @@ -773,12 +806,12 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -884,28 +917,107 @@ │ │ └── base: local #6 : ptr │ └── cases[1] > case 1[2]: leave @2 ├── funcs[5] > func: - │ ├── symbol: boxes$Link(%primitives$String, boxes$Chain) + │ ├── symbol: boxes$Link(%primitives$String, ^boxes$Chain) + │ ├── params[0]: local #1 : handle + │ ├── params[1]: local #2 : + │ ├── ret: {handle, ptr} + │ └── body[0] > scope: + │ ├── id: %4 + │ ├── body[0] > hold: + │ │ ├── local: #3 + │ │ ├── scope: %4 + │ │ ├── value: local #2 : + │ │ └── layout: ^boxes$Chain + │ └── body[1] > return > escape: + │ ├── exit: function + │ ├── value > record: + │ │ ├── type: {handle, ptr} + │ │ ├── members[0] > member 0 > copy: + │ │ │ ├── type: handle + │ │ │ ├── value: local #1 : handle + │ │ │ └── layout: %primitives$String + │ │ └── members[1] > member 1 > box: + │ │ ├── value > take: + │ │ │ ├── type: + │ │ │ ├── address: address of #3 : ptr + │ │ │ └── layout: boxes$Chain + │ │ └── layout: boxes$Chain + │ └── layout: boxes$Link + ├── funcs[6] > func: + │ ├── symbol: boxes$length(this boxes$Chain) │ ├── params[0]: local #1 : ptr - │ ├── params[1]: local #2 : ptr - │ ├── ret: {i32, handle, ptr} - │ └── body[0] > return > escape: - │ ├── exit: function - │ ├── value > record: - │ │ ├── type: {i32, handle, ptr} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ ├── members[1] > member 1 > take: - │ │ │ ├── type: handle - │ │ │ ├── address: local #1 : ptr - │ │ │ └── layout: %primitives$String - │ │ └── members[2] > member 2 > box: - │ │ ├── value > take: - │ │ │ ├── type: {i32, } - │ │ │ ├── address: local #2 : ptr - │ │ │ └── layout: boxes$Chain - │ │ └── layout: boxes$Chain - │ └── layout: boxes$Link - └── funcs[6] > func: - ├── symbol: boxes$length(this boxes$Chain) + │ ├── ret: i64 + │ └── body[0] > return > expand: + │ ├── type: i64 + │ ├── label: @2 + │ ├── result: #3 + │ ├── body[0] > let: + │ │ ├── local: #4 + │ │ └── value: local #1 : ptr + │ └── body[1] > switch: + │ ├── value > deref: + │ │ ├── type: + │ │ └── ptr: local #4 : ptr + │ ├── cases[0] > case 0[0] > let: + │ │ ├── local: #5 + │ │ └── value > offset: + │ │ ├── within: + │ │ ├── path: 0 + │ │ └── base: local #4 : ptr + │ ├── cases[0] > case 0[1] > assign: + │ │ ├── place: #3 + │ │ └── value: 0 : i64 + │ ├── cases[0] > case 0[2]: leave @2 + │ ├── cases[1] > case 1[0] > let: + │ │ ├── local: #6 + │ │ └── value > deref: + │ │ ├── type: ptr + │ │ └── ptr > offset: + │ │ ├── within: + │ │ ├── path: 1 + │ │ └── base: local #4 : ptr + │ ├── cases[1] > case 1[1] > assign: + │ │ ├── place: #3 + │ │ └── value > binary: + │ │ ├── type: i64 + │ │ ├── op: + + │ │ ├── left: 1 : i64 + │ │ └── right > call: + │ │ ├── type: i64 + │ │ ├── fn: boxes$length(this boxes$Chain) + │ │ └── args[0] > deref: + │ │ ├── type: ptr + │ │ └── ptr > offset: + │ │ ├── within: {handle, ptr} + │ │ ├── path: 1 + │ │ └── base: local #6 : ptr + │ └── cases[1] > case 1[2]: leave @2 + ├── funcs[7] > func: + │ ├── symbol: boxes$Tower(%primitives$Int, ^boxes$Storey) + │ ├── params[0]: local #1 : i64 + │ ├── params[1]: local #2 : + │ ├── ret: {i64, ptr} + │ └── body[0] > scope: + │ ├── id: %4 + │ ├── body[0] > hold: + │ │ ├── local: #3 + │ │ ├── scope: %4 + │ │ ├── value: local #2 : + │ │ └── layout: ^boxes$Storey + │ └── body[1] > return > escape: + │ ├── exit: function + │ ├── value > record: + │ │ ├── type: {i64, ptr} + │ │ ├── members[0] > member 0: local #1 : i64 + │ │ └── members[1] > member 1 > box: + │ │ ├── value > take: + │ │ │ ├── type: + │ │ │ ├── address: address of #3 : ptr + │ │ │ └── layout: boxes$Storey + │ │ └── layout: boxes$Storey + │ └── layout: boxes$Tower + └── funcs[8] > func: + ├── symbol: boxes$levels(this boxes$Storey) ├── params[0]: local #1 : ptr ├── ret: i64 └── body[0] > return > expand: @@ -916,17 +1028,14 @@ │ ├── local: #4 │ └── value: local #1 : ptr └── body[1] > switch: - ├── value > member: + ├── value > deref: │ ├── type: - │ ├── index: 1 - │ └── value > deref: - │ ├── type: {i32, } - │ └── ptr: local #4 : ptr + │ └── ptr: local #4 : ptr ├── cases[0] > case 0[0] > let: │ ├── local: #5 │ └── value > offset: - │ ├── within: {i32, } - │ ├── path: 1.0 + │ ├── within: + │ ├── path: 0 │ └── base: local #4 : ptr ├── cases[0] > case 0[1] > assign: │ ├── place: #3 @@ -937,8 +1046,8 @@ │ └── value > deref: │ ├── type: ptr │ └── ptr > offset: - │ ├── within: {i32, } - │ ├── path: 1.1 + │ ├── within: + │ ├── path: 1 │ └── base: local #4 : ptr ├── cases[1] > case 1[1] > assign: │ ├── place: #3 @@ -948,11 +1057,11 @@ │ ├── left: 1 : i64 │ └── right > call: │ ├── type: i64 - │ ├── fn: boxes$length(this boxes$Chain) + │ ├── fn: boxes$levels(this boxes$Storey) │ └── args[0] > deref: │ ├── type: ptr │ └── ptr > offset: - │ ├── within: {i32, handle, ptr} - │ ├── path: 2 + │ ├── within: {i64, ptr} + │ ├── path: 1 │ └── base: local #6 : ptr └── cases[1] > case 1[2]: leave @2 diff --git a/tests/codegen/golden/boxes.out b/tests/codegen/golden/boxes.out index de5620b3..03faa900 100644 --- a/tests/codegen/golden/boxes.out +++ b/tests/codegen/golden/boxes.out @@ -9,3 +9,6 @@ yes yes yes yes +yes +yes +yes diff --git a/tests/codegen/golden/constants.cgt b/tests/codegen/golden/constants.cgt index 35814e04..79d6c077 100644 --- a/tests/codegen/golden/constants.cgt +++ b/tests/codegen/golden/constants.cgt @@ -2,16 +2,13 @@ ├── entry: zane.start ├── layouts[0] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── layouts[1] > layout: │ ├── type: %primitives$Int │ └── positions: ├── layouts[2] > layout: │ ├── type: constants$Person - │ ├── positions[0]: @0 (48 bytes) - │ ├── positions[1]: @8 (32 bytes) - │ └── positions[2]: text @8 + │ └── positions[0]: text @0 ├── layouts[3] > layout: │ ├── type: constants$Point │ └── positions: @@ -25,7 +22,7 @@ ├── globals[7]: constants$later.state : i64 ├── globals[8]: constants$greeting.value : handle ├── globals[9]: constants$greeting.state : i64 - ├── globals[10]: constants$ada.value : {i32, handle, i64} + ├── globals[10]: constants$ada.value : {handle, i64} ├── globals[11]: constants$ada.state : i64 ├── globals[12]: constants$origin.value : {i64, i64} ├── globals[13]: constants$origin.state : i64 @@ -71,15 +68,15 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %2 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #1 │ │ ├── scope: %2 │ │ ├── value: "main\n" : handle │ │ └── layout: %primitives$String │ ├── body[1] > eval > call: │ │ ├── type: void - │ │ ├── fn: constants$say(&%primitives$String) - │ │ └── args[0] > mint: address of #1 : ptr + │ │ ├── fn: constants$say(%primitives$String) + │ │ └── args[0]: local #1 : handle │ ├── body[2] > eval > call: │ │ ├── type: void │ │ ├── fn: constants$check(%primitives$Bool) @@ -110,11 +107,13 @@ │ │ └── right: 94 : i64 │ ├── body[4] > eval > call: │ │ ├── type: void - │ │ ├── fn: constants$say(&%primitives$String) - │ │ └── args[0] > mint > call: - │ │ ├── type: ptr - │ │ ├── fn: constants$greeting - │ │ └── args: + │ │ ├── fn: constants$say(%primitives$String) + │ │ └── args[0] > deref: + │ │ ├── type: handle + │ │ └── ptr > call: + │ │ ├── type: ptr + │ │ ├── fn: constants$greeting + │ │ └── args: │ ├── body[5] > eval > call: │ │ ├── type: void │ │ ├── fn: constants$check(%primitives$Bool) @@ -134,7 +133,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @4 │ │ │ ├── result: #5 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #3 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "hello\n" : handle @@ -145,7 +144,7 @@ │ │ └── right: 1 : i64 │ ├── body[6] > let: │ │ ├── local: #6 - │ │ └── value > mint > call: + │ │ └── value > call: │ │ ├── type: ptr │ │ ├── fn: constants$ada │ │ └── args: @@ -158,9 +157,9 @@ │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, handle, i64} - │ │ │ ├── path: 2 - │ │ │ └── base > resolve: local #6 : i32 + │ │ │ ├── within: {handle, i64} + │ │ │ ├── path: 1 + │ │ │ └── base: local #6 : ptr │ │ └── right: 36 : i64 │ ├── body[8] > eval > call: │ │ ├── type: void @@ -171,7 +170,7 @@ │ │ ├── left > call: │ │ │ ├── type: i64 │ │ │ ├── fn: constants$age(&constants$Person) - │ │ │ └── args[0] > mint > call: + │ │ │ └── args[0] > call: │ │ │ ├── type: ptr │ │ │ ├── fn: constants$ada │ │ │ └── args: @@ -185,12 +184,12 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > member: + │ │ │ ├── args[0] > snapshot: │ │ │ │ ├── type: handle - │ │ │ │ ├── index: 1 - │ │ │ │ └── value > deref: - │ │ │ │ ├── type: {i32, handle, i64} - │ │ │ │ └── ptr > call: + │ │ │ │ └── ptr > offset: + │ │ │ │ ├── within: {handle, i64} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base > call: │ │ │ │ ├── type: ptr │ │ │ │ ├── fn: constants$ada │ │ │ │ └── args: @@ -198,7 +197,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @8 │ │ │ ├── result: #9 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #7 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "ada" : handle @@ -262,15 +261,13 @@ │ │ └── right: 94 : i64 │ └── body[13] > return: unit : void ├── funcs[2] > func: - │ ├── symbol: constants$say(&%primitives$String) - │ ├── params[0]: local #1 : i32 + │ ├── symbol: constants$say(%primitives$String) + │ ├── params[0]: local #1 : handle │ ├── ret: void │ ├── body[0] > eval > runtime: │ │ ├── type: void │ │ ├── fn: zane_print - │ │ └── args[0] > deref: - │ │ ├── type: handle - │ │ └── ptr > resolve: local #1 : i32 + │ │ └── args[0]: local #1 : handle │ └── body[1] > return: unit : void ├── funcs[3] > func: │ ├── symbol: constants$check(%primitives$Bool) @@ -278,12 +275,12 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -292,16 +289,16 @@ │ │ ├── cond: local #1 : i1 │ │ └── body[0] > eval > call: │ │ ├── type: void - │ │ ├── fn: constants$say(&%primitives$String) - │ │ └── args[0] > mint: address of #2 : ptr + │ │ ├── fn: constants$say(%primitives$String) + │ │ └── args[0]: local #2 : handle │ ├── body[3] > if: │ │ ├── cond > flip: │ │ │ ├── type: i1 │ │ │ └── value: local #1 : i1 │ │ └── body[0] > eval > call: │ │ ├── type: void - │ │ ├── fn: constants$say(&%primitives$String) - │ │ └── args[0] > mint: address of #4 : ptr + │ │ ├── fn: constants$say(%primitives$String) + │ │ └── args[0]: local #4 : handle │ └── body[4] > return: unit : void ├── funcs[4] > func: │ ├── symbol: constants$peek() @@ -324,14 +321,14 @@ │ └── args: ├── funcs[5] > func: │ ├── symbol: constants$age(&constants$Person) - │ ├── params[0]: local #1 : i32 + │ ├── params[0]: local #1 : ptr │ ├── ret: i64 │ └── body[0] > return > snapshot: │ ├── type: i64 │ └── ptr > offset: - │ ├── within: {i32, handle, i64} - │ ├── path: 2 - │ └── base > resolve: local #1 : i32 + │ ├── within: {handle, i64} + │ ├── path: 1 + │ └── base: local #1 : ptr ├── funcs[6] > func: │ ├── symbol: constants$limit │ ├── params: @@ -451,15 +448,15 @@ │ ├── ret: i64 │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "made\n" : handle │ │ └── layout: %primitives$String │ ├── body[1] > eval > call: │ │ ├── type: void - │ │ ├── fn: constants$say(&%primitives$String) - │ │ └── args[0] > mint: address of #2 : ptr + │ │ ├── fn: constants$say(%primitives$String) + │ │ └── args[0]: local #2 : handle │ └── body[2] > return: local #1 : i64 ├── funcs[11] > func: │ ├── symbol: constants$greeting @@ -485,7 +482,7 @@ │ │ │ │ ├── type: handle │ │ │ │ ├── label: @2 │ │ │ │ ├── result: #3 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #1 │ │ │ │ │ ├── scope: %4 │ │ │ │ │ ├── value: "hel" : handle @@ -497,7 +494,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @6 │ │ │ ├── result: #7 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #5 │ │ │ │ ├── scope: %4 │ │ │ │ ├── value: "lo\n" : handle @@ -530,20 +527,20 @@ │ │ ├── body[0] > store: │ │ │ ├── address: global constants$ada.value │ │ │ └── value > call: - │ │ │ ├── type: {i32, handle, i64} + │ │ │ ├── type: {handle, i64} │ │ │ ├── fn: constants$Person{name %primitives$String; age %primitives$Int} │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: ptr + │ │ │ │ ├── type: handle │ │ │ │ ├── label: @2 │ │ │ │ ├── result: #3 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #1 │ │ │ │ │ ├── scope: %4 │ │ │ │ │ ├── value: "ada" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #3 - │ │ │ │ └── value: address of #1 : ptr + │ │ │ │ └── value: local #1 : handle │ │ │ └── args[1]: 36 : i64 │ │ └── body[1] > eval > runtime: │ │ ├── type: void @@ -554,19 +551,18 @@ │ └── body[1] > return: global constants$ada.value ├── funcs[13] > func: │ ├── symbol: constants$Person{name %primitives$String; age %primitives$Int} - │ ├── params[0]: local #1 : ptr + │ ├── params[0]: local #1 : handle │ ├── params[1]: local #2 : i64 - │ ├── ret: {i32, handle, i64} + │ ├── ret: {handle, i64} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, handle, i64} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ ├── members[1] > member 1 > take: + │ │ ├── type: {handle, i64} + │ │ ├── members[0] > member 0 > copy: │ │ │ ├── type: handle - │ │ │ ├── address: local #1 : ptr + │ │ │ ├── value: local #1 : handle │ │ │ └── layout: %primitives$String - │ │ └── members[2] > member 2: local #2 : i64 + │ │ └── members[1] > member 1: local #2 : i64 │ └── layout: constants$Person ├── funcs[14] > func: │ ├── symbol: constants$origin diff --git a/tests/codegen/golden/counting.cgt b/tests/codegen/golden/counting.cgt index b7e7f46d..c5149606 100644 --- a/tests/codegen/golden/counting.cgt +++ b/tests/codegen/golden/counting.cgt @@ -2,30 +2,29 @@ ├── entry: counting$main() ├── layouts[0] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── funcs[0] > func: │ ├── symbol: counting$main() │ ├── params: │ ├── ret: void │ └── body[0] > scope: │ ├── id: %2 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #1 │ │ ├── scope: %2 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #3 │ │ ├── scope: %2 │ │ ├── value: "no\n" : handle │ │ └── layout: %primitives$String - │ ├── body[2] > host: + │ ├── body[2] > hold: │ │ ├── local: #4 │ │ ├── scope: %2 │ │ ├── value: "." : handle │ │ └── layout: %primitives$String - │ ├── body[3] > host: + │ ├── body[3] > hold: │ │ ├── local: #5 │ │ ├── scope: %2 │ │ ├── value: "\n" : handle diff --git a/tests/codegen/golden/defaults.cgt b/tests/codegen/golden/defaults.cgt index b0bb3d53..fd065646 100644 --- a/tests/codegen/golden/defaults.cgt +++ b/tests/codegen/golden/defaults.cgt @@ -2,13 +2,10 @@ ├── entry: defaults$main() ├── layouts[0] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── layouts[1] > layout: │ ├── type: defaults$Person - │ ├── positions[0]: @0 (48 bytes) - │ ├── positions[1]: @8 (32 bytes) - │ └── positions[2]: text @8 + │ └── positions[0]: text @0 ├── funcs[0] > func: │ ├── symbol: defaults$main() │ ├── params: @@ -58,17 +55,17 @@ │ │ │ ├── type: i64 │ │ │ ├── fn: defaults$noisy(%primitives$String, %primitives$Int) │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: ptr + │ │ │ │ ├── type: handle │ │ │ │ ├── label: @3 │ │ │ │ ├── result: #4 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #2 │ │ │ │ │ ├── scope: %5 │ │ │ │ │ ├── value: "d " : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #4 - │ │ │ │ └── value: address of #2 : ptr + │ │ │ │ └── value: local #2 : handle │ │ │ └── args[1]: 3 : i64 │ │ ├── body[1] > let: │ │ │ ├── local: #10 @@ -76,17 +73,17 @@ │ │ │ ├── type: i64 │ │ │ ├── fn: defaults$noisy(%primitives$String, %primitives$Int) │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: ptr + │ │ │ │ ├── type: handle │ │ │ │ ├── label: @8 │ │ │ │ ├── result: #9 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #7 │ │ │ │ │ ├── scope: %5 │ │ │ │ │ ├── value: "r\n" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #9 - │ │ │ │ └── value: address of #7 : ptr + │ │ │ │ └── value: local #7 : handle │ │ │ └── args[1]: 4 : i64 │ │ └── body[2] > assign: │ │ ├── place: #12 @@ -113,11 +110,11 @@ │ │ │ ├── index: 0 │ │ │ └── value: local #13 : {i64, i64} │ │ └── right: 7 : i64 - │ ├── body[4] > host: + │ ├── body[4] > hold: │ │ ├── local: #21 │ │ ├── scope: %5 │ │ ├── value > expand: - │ │ │ ├── type: {i32, handle, i64} + │ │ │ ├── type: {handle, i64} │ │ │ ├── label: @19 │ │ │ ├── result: #20 │ │ │ ├── body[0] > let: @@ -126,43 +123,43 @@ │ │ │ ├── body[1] > let: │ │ │ │ ├── local: #18 │ │ │ │ └── value > expand: - │ │ │ │ ├── type: ptr + │ │ │ │ ├── type: handle │ │ │ │ ├── label: @16 │ │ │ │ ├── result: #17 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #15 │ │ │ │ │ ├── scope: %5 │ │ │ │ │ ├── value: "nobody" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #17 - │ │ │ │ └── value: address of #15 : ptr + │ │ │ │ └── value: local #15 : handle │ │ │ └── body[2] > assign: │ │ │ ├── place: #20 │ │ │ └── value > call: - │ │ │ ├── type: {i32, handle, i64} + │ │ │ ├── type: {handle, i64} │ │ │ ├── fn: defaults$Person{name %primitives$String; age %primitives$Int} - │ │ │ ├── args[0]: local #18 : ptr + │ │ │ ├── args[0]: local #18 : handle │ │ │ └── args[1]: local #14 : i64 │ │ └── layout: defaults$Person - │ ├── body[5] > host: + │ ├── body[5] > hold: │ │ ├── local: #25 │ │ ├── scope: %5 │ │ ├── value > call: - │ │ │ ├── type: {i32, handle, i64} + │ │ │ ├── type: {handle, i64} │ │ │ ├── fn: defaults$Person{name %primitives$String; age %primitives$Int} │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: ptr + │ │ │ │ ├── type: handle │ │ │ │ ├── label: @23 │ │ │ │ ├── result: #24 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #22 │ │ │ │ │ ├── scope: %5 │ │ │ │ │ ├── value: "ada" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #24 - │ │ │ │ └── value: address of #22 : ptr + │ │ │ │ └── value: local #22 : handle │ │ │ └── args[1]: 0 : i64 │ │ └── layout: defaults$Person │ ├── body[6] > eval > call: @@ -177,14 +174,14 @@ │ │ │ ├── left > snapshot: │ │ │ │ ├── type: i64 │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, handle, i64} - │ │ │ │ ├── path: 2 + │ │ │ │ ├── within: {handle, i64} + │ │ │ │ ├── path: 1 │ │ │ │ └── base: address of #21 : ptr │ │ │ └── right > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, handle, i64} - │ │ │ ├── path: 2 + │ │ │ ├── within: {handle, i64} + │ │ │ ├── path: 1 │ │ │ └── base: address of #25 : ptr │ │ └── right: 36 : i64 │ ├── body[7] > let: @@ -261,17 +258,17 @@ │ │ │ ├── type: i64 │ │ │ ├── fn: defaults$noisy(%primitives$String, %primitives$Int) │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: ptr + │ │ │ │ ├── type: handle │ │ │ │ ├── label: @33 │ │ │ │ ├── result: #34 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #32 │ │ │ │ │ ├── scope: %5 │ │ │ │ │ ├── value: "b\n" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #34 - │ │ │ │ └── value: address of #32 : ptr + │ │ │ │ └── value: local #32 : handle │ │ │ └── args[1]: 2 : i64 │ │ ├── body[1] > let: │ │ │ ├── local: #36 @@ -337,12 +334,12 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -351,66 +348,65 @@ │ │ ├── cond: local #1 : i1 │ │ └── body[0] > eval > call: │ │ ├── type: void - │ │ ├── fn: defaults$say(&%primitives$String) - │ │ └── args[0] > mint: address of #2 : ptr + │ │ ├── fn: defaults$say(%primitives$String) + │ │ └── args[0]: local #2 : handle │ ├── body[3] > if: │ │ ├── cond > flip: │ │ │ ├── type: i1 │ │ │ └── value: local #1 : i1 │ │ └── body[0] > eval > call: │ │ ├── type: void - │ │ ├── fn: defaults$say(&%primitives$String) - │ │ └── args[0] > mint: address of #4 : ptr + │ │ ├── fn: defaults$say(%primitives$String) + │ │ └── args[0]: local #4 : handle │ └── body[4] > return: unit : void ├── funcs[3] > func: │ ├── symbol: defaults$noisy(%primitives$String, %primitives$Int) - │ ├── params[0]: local #1 : ptr + │ ├── params[0]: local #1 : handle │ ├── params[1]: local #2 : i64 │ ├── ret: i64 │ ├── body[0] > eval > call: │ │ ├── type: void - │ │ ├── fn: defaults$say(&%primitives$String) - │ │ └── args[0] > mint: local #1 : ptr + │ │ ├── fn: defaults$say(%primitives$String) + │ │ └── args[0]: local #1 : handle │ └── body[1] > return: local #2 : i64 ├── funcs[4] > func: │ ├── symbol: defaults$Person{name %primitives$String; age %primitives$Int} - │ ├── params[0]: local #1 : ptr + │ ├── params[0]: local #1 : handle │ ├── params[1]: local #2 : i64 - │ ├── ret: {i32, handle, i64} + │ ├── ret: {handle, i64} │ └── body[0] > scope: │ ├── id: %4 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #3 │ │ ├── scope: %4 │ │ ├── value: "made " : handle │ │ └── layout: %primitives$String │ ├── body[1] > eval > call: │ │ ├── type: void - │ │ ├── fn: defaults$say(&%primitives$String) - │ │ └── args[0] > mint: address of #3 : ptr + │ │ ├── fn: defaults$say(%primitives$String) + │ │ └── args[0]: local #3 : handle │ ├── body[2] > eval > call: │ │ ├── type: void - │ │ ├── fn: defaults$say(&%primitives$String) - │ │ └── args[0] > mint: local #1 : ptr - │ ├── body[3] > host: + │ │ ├── fn: defaults$say(%primitives$String) + │ │ └── args[0]: local #1 : handle + │ ├── body[3] > hold: │ │ ├── local: #5 │ │ ├── scope: %4 │ │ ├── value: "\n" : handle │ │ └── layout: %primitives$String │ ├── body[4] > eval > call: │ │ ├── type: void - │ │ ├── fn: defaults$say(&%primitives$String) - │ │ └── args[0] > mint: address of #5 : ptr + │ │ ├── fn: defaults$say(%primitives$String) + │ │ └── args[0]: local #5 : handle │ └── body[5] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, handle, i64} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ ├── members[1] > member 1 > take: + │ │ ├── type: {handle, i64} + │ │ ├── members[0] > member 0 > copy: │ │ │ ├── type: handle - │ │ │ ├── address: local #1 : ptr + │ │ │ ├── value: local #1 : handle │ │ │ └── layout: %primitives$String - │ │ └── members[2] > member 2: local #2 : i64 + │ │ └── members[1] > member 1: local #2 : i64 │ └── layout: defaults$Person ├── funcs[5] > func: │ ├── symbol: defaults$Tagged<%primitives$Bool>{value %primitives$Bool; count %primitives$Int} @@ -431,13 +427,11 @@ │ ├── members[0] > member 0: local #1 : i64 │ └── members[1] > member 1: local #2 : i64 └── funcs[7] > func: - ├── symbol: defaults$say(&%primitives$String) - ├── params[0]: local #1 : i32 + ├── symbol: defaults$say(%primitives$String) + ├── params[0]: local #1 : handle ├── ret: void ├── body[0] > eval > runtime: │ ├── type: void │ ├── fn: zane_print - │ └── args[0] > deref: - │ ├── type: handle - │ └── ptr > resolve: local #1 : i32 + │ └── args[0]: local #1 : handle └── body[1] > return: unit : void diff --git a/tests/codegen/golden/guests.cgt b/tests/codegen/golden/guests.cgt deleted file mode 100644 index 29f66e06..00000000 --- a/tests/codegen/golden/guests.cgt +++ /dev/null @@ -1,786 +0,0 @@ -└── program: - ├── entry: guests$main() - ├── layouts[0] > layout: - │ ├── type: guests$Node - │ └── positions[0]: @0 (16 bytes) - ├── layouts[1] > layout: - │ ├── type: guests$Car - │ ├── positions[0]: @0 (24 bytes) - │ └── positions[1]: @8 (16 bytes) - ├── layouts[2] > layout: - │ ├── type: guests$Holder - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: @16 (16 bytes) when [8]=0 - ├── layouts[3] > layout: - │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 - ├── funcs[0] > func: - │ ├── symbol: guests$main() - │ ├── params: - │ ├── ret: void - │ └── body[0] > scope: - │ ├── id: %2 - │ ├── body[0] > host: - │ │ ├── local: #1 - │ │ ├── scope: %2 - │ │ ├── value > call: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── fn: guests$Node(%primitives$Int) - │ │ │ └── args[0]: 1 : i64 - │ │ └── layout: guests$Node - │ ├── body[1] > let: - │ │ ├── local: #3 - │ │ └── value > mint: address of #1 : ptr - │ ├── body[2] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$bump(this guests$Node) - │ │ └── args[0]: address of #1 : ptr - │ ├── body[3] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #3 : i32 - │ │ └── right: 2 : i64 - │ ├── body[4] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$bump(this guests$Node) - │ │ └── args[0] > resolve: local #3 : i32 - │ ├── body[5] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base: address of #1 : ptr - │ │ └── right: 3 : i64 - │ ├── body[6] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: * - │ │ ├── left > binary: - │ │ │ ├── type: i1 - │ │ │ ├── op: == - │ │ │ ├── left > call: - │ │ │ │ ├── type: i64 - │ │ │ │ ├── fn: guests$peek(&guests$Node) - │ │ │ │ └── args[0] > terminal: local #3 : i32 - │ │ │ └── right: 3 : i64 - │ │ └── right > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > call: - │ │ │ ├── type: i64 - │ │ │ ├── fn: guests$peek(&guests$Node) - │ │ │ └── args[0] > mint: address of #1 : ptr - │ │ └── right: 3 : i64 - │ ├── body[7] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > call: - │ │ │ ├── type: i64 - │ │ │ ├── fn: guests$twice(this guests$Node) - │ │ │ └── args[0] > resolve: local #3 : i32 - │ │ └── right: 6 : i64 - │ ├── body[8] > host: - │ │ ├── local: #4 - │ │ ├── scope: %2 - │ │ ├── value > take: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── address: address of #1 : ptr - │ │ │ └── layout: guests$Node - │ │ └── layout: guests$Node - │ ├── body[9] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$bump(this guests$Node) - │ │ └── args[0] > resolve: local #3 : i32 - │ ├── body[10] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base: address of #4 : ptr - │ │ └── right: 4 : i64 - │ ├── body[11] > overwrite: - │ │ ├── address: address of #1 : ptr - │ │ ├── value > call: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── fn: guests$Node(%primitives$Int) - │ │ │ └── args[0]: 100 : i64 - │ │ └── layout: guests$Node - │ ├── body[12] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #3 : i32 - │ │ └── right: 4 : i64 - │ ├── body[13] > overwrite: - │ │ ├── address: address of #4 : ptr - │ │ ├── value > call: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── fn: guests$Node(%primitives$Int) - │ │ │ └── args[0]: 7 : i64 - │ │ └── layout: guests$Node - │ ├── body[14] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #3 : i32 - │ │ └── right: 7 : i64 - │ ├── body[15] > host: - │ │ ├── local: #5 - │ │ ├── scope: %2 - │ │ ├── value > call: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── fn: guests$Node(%primitives$Int) - │ │ │ └── args[0]: 20 : i64 - │ │ └── layout: guests$Node - │ ├── body[16] > let: - │ │ ├── local: #6 - │ │ └── value > mint: address of #5 : ptr - │ ├── body[17] > overwrite: - │ │ ├── address: address of #4 : ptr - │ │ ├── value > take: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── address: address of #5 : ptr - │ │ │ └── layout: guests$Node - │ │ └── layout: guests$Node - │ ├── body[18] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$bump(this guests$Node) - │ │ └── args[0] > resolve: local #6 : i32 - │ ├── body[19] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #3 : i32 - │ │ └── right: 21 : i64 - │ ├── body[20] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base: address of #4 : ptr - │ │ └── right: 21 : i64 - │ ├── body[21] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve > call: - │ │ │ ├── type: i32 - │ │ │ ├── fn: guests$larger(&guests$Node, &guests$Node) - │ │ │ ├── args[0] > mint: address of #1 : ptr - │ │ │ └── args[1] > mint: address of #4 : ptr - │ │ └── right: 100 : i64 - │ ├── body[22] > assign: - │ │ ├── place: #3 - │ │ └── value > mint: address of #1 : ptr - │ ├── body[23] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #3 : i32 - │ │ └── right: 100 : i64 - │ ├── body[24] > host: - │ │ ├── local: #10 - │ │ ├── scope: %2 - │ │ ├── value > call: - │ │ │ ├── type: {i32, i32, {i32, i64}} - │ │ │ ├── fn: guests$Car(&guests$Node, guests$Node) - │ │ │ ├── args[0] > mint: address of #4 : ptr - │ │ │ └── args[1] > expand: - │ │ │ ├── type: ptr - │ │ │ ├── label: @8 - │ │ │ ├── result: #9 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #7 - │ │ │ │ ├── scope: %2 - │ │ │ │ ├── value > call: - │ │ │ │ │ ├── type: {i32, i64} - │ │ │ │ │ ├── fn: guests$Node(%primitives$Int) - │ │ │ │ │ └── args[0]: 5 : i64 - │ │ │ │ └── layout: guests$Node - │ │ │ └── body[1] > assign: - │ │ │ ├── place: #9 - │ │ │ └── value: address of #7 : ptr - │ │ └── layout: guests$Car - │ ├── body[25] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$bump(this guests$Node) - │ │ └── args[0] > resolve > member: - │ │ ├── type: i32 - │ │ ├── index: 1 - │ │ └── value: local #10 : {i32, i32, {i32, i64}} - │ ├── body[26] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base: address of #4 : ptr - │ │ └── right: 22 : i64 - │ ├── body[27] > let: - │ │ ├── local: #11 - │ │ └── value > mint > offset: - │ │ ├── within: {i32, i32, {i32, i64}} - │ │ ├── path: 2 - │ │ └── base: address of #10 : ptr - │ ├── body[28] > host: - │ │ ├── local: #12 - │ │ ├── scope: %2 - │ │ ├── value > take: - │ │ │ ├── type: {i32, i32, {i32, i64}} - │ │ │ ├── address: address of #10 : ptr - │ │ │ └── layout: guests$Car - │ │ └── layout: guests$Car - │ ├── body[29] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$bump(this guests$Node) - │ │ └── args[0] > resolve: local #11 : i32 - │ ├── body[30] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > offset: - │ │ │ ├── within: {i32, i32, {i32, i64}} - │ │ │ ├── path: 2 - │ │ │ └── base: address of #12 : ptr - │ │ └── right: 6 : i64 - │ ├── body[31] > overwrite: - │ │ ├── address > offset: - │ │ │ ├── within: {i32, i32, {i32, i64}} - │ │ │ ├── path: 2 - │ │ │ └── base: address of #12 : ptr - │ │ ├── value > call: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── fn: guests$Node(%primitives$Int) - │ │ │ └── args[0]: 50 : i64 - │ │ └── layout: guests$Node - │ ├── body[32] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #11 : i32 - │ │ └── right: 50 : i64 - │ ├── body[33] > host: - │ │ ├── local: #13 - │ │ ├── scope: %2 - │ │ ├── value > call: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── fn: guests$Node(%primitives$Int) - │ │ │ └── args[0]: 9 : i64 - │ │ └── layout: guests$Node - │ ├── body[34] > let: - │ │ ├── local: #14 - │ │ └── value > mint: address of #13 : ptr - │ ├── body[35] > host: - │ │ ├── local: #15 - │ │ ├── scope: %2 - │ │ ├── value > record: - │ │ │ ├── type: {i32, <{i32, i64} | void>} - │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ └── members[1] > member 1 > case: - │ │ │ ├── type: <{i32, i64} | void> - │ │ │ ├── index: 0 - │ │ │ └── payload > take: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── address: address of #13 : ptr - │ │ │ └── layout: guests$Node - │ │ └── layout: guests$Holder - │ ├── body[36] > overwrite: - │ │ ├── address: address of #15 : ptr - │ │ ├── value > record: - │ │ │ ├── type: {i32, <{i32, i64} | void>} - │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ └── members[1] > member 1 > case: - │ │ │ ├── type: <{i32, i64} | void> - │ │ │ ├── index: 1 - │ │ │ └── payload: unit : void - │ │ └── layout: guests$Holder - │ ├── body[37] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #14 : i32 - │ │ └── right: 9 : i64 - │ ├── body[38] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$bump(this guests$Node) - │ │ └── args[0] > resolve: local #14 : i32 - │ ├── body[39] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #14 : i32 - │ │ └── right: 10 : i64 - │ ├── body[40] > host: - │ │ ├── local: #16 - │ │ ├── scope: %2 - │ │ ├── value > record: - │ │ │ ├── type: {i32, <{i32, i64} | void>} - │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ └── members[1] > member 1 > case: - │ │ │ ├── type: <{i32, i64} | void> - │ │ │ ├── index: 0 - │ │ │ └── payload > call: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── fn: guests$Node(%primitives$Int) - │ │ │ └── args[0]: 30 : i64 - │ │ └── layout: guests$Holder - │ ├── body[41] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > expand: - │ │ ├── type: i1 - │ │ ├── label: @17 - │ │ ├── result: #18 - │ │ ├── body[0] > let: - │ │ │ ├── local: #19 - │ │ │ └── value: address of #16 : ptr - │ │ └── body[1] > switch: - │ │ ├── value > member: - │ │ │ ├── type: <{i32, i64} | void> - │ │ │ ├── index: 1 - │ │ │ └── value > deref: - │ │ │ ├── type: {i32, <{i32, i64} | void>} - │ │ │ └── ptr: local #19 : ptr - │ │ ├── cases[0] > case 0[0] > let: - │ │ │ ├── local: #20 - │ │ │ └── value > offset: - │ │ │ ├── within: {i32, <{i32, i64} | void>} - │ │ │ ├── path: 1.0 - │ │ │ └── base: local #19 : ptr - │ │ ├── cases[0] > case 0[1] > eval > call: - │ │ │ ├── type: void - │ │ │ ├── fn: guests$bump(this guests$Node) - │ │ │ └── args[0]: local #20 : ptr - │ │ ├── cases[0] > case 0[2] > assign: - │ │ │ ├── place: #18 - │ │ │ └── value > binary: - │ │ │ ├── type: i1 - │ │ │ ├── op: == - │ │ │ ├── left > snapshot: - │ │ │ │ ├── type: i64 - │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, i64} - │ │ │ │ ├── path: 1 - │ │ │ │ └── base: local #20 : ptr - │ │ │ └── right: 31 : i64 - │ │ ├── cases[0] > case 0[3]: leave @17 - │ │ ├── cases[1] > case 1[0] > let: - │ │ │ ├── local: #21 - │ │ │ └── value > offset: - │ │ │ ├── within: {i32, <{i32, i64} | void>} - │ │ │ ├── path: 1.1 - │ │ │ └── base: local #19 : ptr - │ │ ├── cases[1] > case 1[1] > assign: - │ │ │ ├── place: #18 - │ │ │ └── value: false : i1 - │ │ └── cases[1] > case 1[2]: leave @17 - │ ├── body[42] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > expand: - │ │ ├── type: i1 - │ │ ├── label: @22 - │ │ ├── result: #23 - │ │ ├── body[0] > let: - │ │ │ ├── local: #24 - │ │ │ └── value: address of #16 : ptr - │ │ └── body[1] > switch: - │ │ ├── value > member: - │ │ │ ├── type: <{i32, i64} | void> - │ │ │ ├── index: 1 - │ │ │ └── value > deref: - │ │ │ ├── type: {i32, <{i32, i64} | void>} - │ │ │ └── ptr: local #24 : ptr - │ │ ├── cases[0] > case 0[0] > let: - │ │ │ ├── local: #25 - │ │ │ └── value > offset: - │ │ │ ├── within: {i32, <{i32, i64} | void>} - │ │ │ ├── path: 1.0 - │ │ │ └── base: local #24 : ptr - │ │ ├── cases[0] > case 0[1] > assign: - │ │ │ ├── place: #23 - │ │ │ └── value > binary: - │ │ │ ├── type: i1 - │ │ │ ├── op: == - │ │ │ ├── left > snapshot: - │ │ │ │ ├── type: i64 - │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, i64} - │ │ │ │ ├── path: 1 - │ │ │ │ └── base: local #25 : ptr - │ │ │ └── right: 31 : i64 - │ │ ├── cases[0] > case 0[2]: leave @22 - │ │ ├── cases[1] > case 1[0] > let: - │ │ │ ├── local: #26 - │ │ │ └── value > offset: - │ │ │ ├── within: {i32, <{i32, i64} | void>} - │ │ │ ├── path: 1.1 - │ │ │ └── base: local #24 : ptr - │ │ ├── cases[1] > case 1[1] > assign: - │ │ │ ├── place: #23 - │ │ │ └── value: false : i1 - │ │ └── cases[1] > case 1[2]: leave @22 - │ ├── body[43] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > expand: - │ │ ├── type: i1 - │ │ ├── label: @27 - │ │ ├── result: #28 - │ │ ├── body[0] > host: - │ │ │ ├── local: #29 - │ │ │ ├── scope: %2 - │ │ │ ├── value > record: - │ │ │ │ ├── type: {i32, <{i32, i64} | void>} - │ │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ │ └── members[1] > member 1 > case: - │ │ │ │ ├── type: <{i32, i64} | void> - │ │ │ │ ├── index: 0 - │ │ │ │ └── payload > call: - │ │ │ │ ├── type: {i32, i64} - │ │ │ │ ├── fn: guests$Node(%primitives$Int) - │ │ │ │ └── args[0]: 40 : i64 - │ │ │ └── layout: guests$Holder - │ │ ├── body[1] > let: - │ │ │ ├── local: #30 - │ │ │ └── value: address of #29 : ptr - │ │ └── body[2] > switch: - │ │ ├── value > member: - │ │ │ ├── type: <{i32, i64} | void> - │ │ │ ├── index: 1 - │ │ │ └── value > deref: - │ │ │ ├── type: {i32, <{i32, i64} | void>} - │ │ │ └── ptr: local #30 : ptr - │ │ ├── cases[0] > case 0[0] > let: - │ │ │ ├── local: #31 - │ │ │ └── value > offset: - │ │ │ ├── within: {i32, <{i32, i64} | void>} - │ │ │ ├── path: 1.0 - │ │ │ └── base: local #30 : ptr - │ │ ├── cases[0] > case 0[1] > eval > call: - │ │ │ ├── type: void - │ │ │ ├── fn: guests$bump(this guests$Node) - │ │ │ └── args[0]: local #31 : ptr - │ │ ├── cases[0] > case 0[2] > assign: - │ │ │ ├── place: #28 - │ │ │ └── value > binary: - │ │ │ ├── type: i1 - │ │ │ ├── op: == - │ │ │ ├── left > snapshot: - │ │ │ │ ├── type: i64 - │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, i64} - │ │ │ │ ├── path: 1 - │ │ │ │ └── base: local #31 : ptr - │ │ │ └── right: 41 : i64 - │ │ ├── cases[0] > case 0[3]: leave @27 - │ │ ├── cases[1] > case 1[0] > let: - │ │ │ ├── local: #32 - │ │ │ └── value > offset: - │ │ │ ├── within: {i32, <{i32, i64} | void>} - │ │ │ ├── path: 1.1 - │ │ │ └── base: local #30 : ptr - │ │ ├── cases[1] > case 1[1] > assign: - │ │ │ ├── place: #28 - │ │ │ └── value: false : i1 - │ │ └── cases[1] > case 1[2]: leave @27 - │ ├── body[44] > let: - │ │ ├── local: #33 - │ │ └── value: 0 : i64 - │ ├── body[45] > let: - │ │ ├── local: #34 - │ │ └── value: 1 : i64 - │ ├── body[46] > eval > expand: - │ │ ├── type: void - │ │ ├── label: @36 - │ │ ├── body[0] > let: - │ │ │ ├── local: #35 - │ │ │ └── value: 100000 : i64 - │ │ ├── body[1] > repeat: - │ │ │ ├── count > binary: - │ │ │ │ ├── type: i64 - │ │ │ │ ├── op: + - │ │ │ │ ├── left > binary: - │ │ │ │ │ ├── type: i64 - │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #35 : i64 - │ │ │ │ │ └── right > flip: - │ │ │ │ │ ├── type: i64 - │ │ │ │ │ └── value: local #34 : i64 - │ │ │ │ └── right: 1 : i64 - │ │ │ ├── body[0] > if: - │ │ │ │ ├── cond: true : i1 - │ │ │ │ └── body[0] > scope: - │ │ │ │ ├── id: %40 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #39 - │ │ │ │ │ ├── scope: %40 - │ │ │ │ │ ├── value > call: - │ │ │ │ │ │ ├── type: {i32, i64} - │ │ │ │ │ │ ├── fn: guests$Node(%primitives$Int) - │ │ │ │ │ │ └── args[0]: local #34 : i64 - │ │ │ │ │ └── layout: guests$Node - │ │ │ │ ├── body[1] > let: - │ │ │ │ │ ├── local: #41 - │ │ │ │ │ └── value > mint: address of #39 : ptr - │ │ │ │ └── body[2] > assign: - │ │ │ │ ├── place: #33 - │ │ │ │ └── value > binary: - │ │ │ │ ├── type: i64 - │ │ │ │ ├── op: + - │ │ │ │ ├── left: local #33 : i64 - │ │ │ │ └── right > call: - │ │ │ │ ├── type: i64 - │ │ │ │ ├── fn: guests$peek(&guests$Node) - │ │ │ │ └── args[0] > terminal: local #41 : i32 - │ │ │ └── body[1] > assign: - │ │ │ ├── place: #34 - │ │ │ └── value > binary: - │ │ │ ├── type: i64 - │ │ │ ├── op: + - │ │ │ ├── left: local #34 : i64 - │ │ │ └── right: 1 : i64 - │ │ ├── body[2] > eval: unit : void - │ │ └── body[3]: leave @36 - │ ├── body[47] > eval > call: - │ │ ├── type: void - │ │ ├── fn: guests$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left: local #33 : i64 - │ │ └── right: 5000050000 : i64 - │ └── body[48] > return: unit : void - ├── funcs[1] > func: - │ ├── symbol: guests$Node(%primitives$Int) - │ ├── params[0]: local #1 : i64 - │ ├── ret: {i32, i64} - │ └── body[0] > return > escape: - │ ├── exit: function - │ ├── value > record: - │ │ ├── type: {i32, i64} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ └── members[1] > member 1: local #1 : i64 - │ └── layout: guests$Node - ├── funcs[2] > func: - │ ├── symbol: guests$bump(this guests$Node) - │ ├── params[0]: local #1 : ptr - │ ├── ret: void - │ ├── body[0] > assign: - │ │ ├── place: *#1.1 - │ │ └── value > binary: - │ │ ├── type: i64 - │ │ ├── op: + - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base: local #1 : ptr - │ │ └── right: 1 : i64 - │ └── body[1] > return: unit : void - ├── funcs[3] > func: - │ ├── symbol: guests$check(%primitives$Bool) - │ ├── params[0]: local #1 : i1 - │ ├── ret: void - │ └── body[0] > scope: - │ ├── id: %3 - │ ├── body[0] > host: - │ │ ├── local: #2 - │ │ ├── scope: %3 - │ │ ├── value: "yes\n" : handle - │ │ └── layout: %primitives$String - │ ├── body[1] > host: - │ │ ├── local: #4 - │ │ ├── scope: %3 - │ │ ├── value: "no\n" : handle - │ │ └── layout: %primitives$String - │ ├── body[2] > if: - │ │ ├── cond: local #1 : i1 - │ │ └── body[0] > eval > runtime: - │ │ ├── type: void - │ │ ├── fn: zane_print - │ │ └── args[0]: local #2 : handle - │ ├── body[3] > if: - │ │ ├── cond > flip: - │ │ │ ├── type: i1 - │ │ │ └── value: local #1 : i1 - │ │ └── body[0] > eval > runtime: - │ │ ├── type: void - │ │ ├── fn: zane_print - │ │ └── args[0]: local #4 : handle - │ └── body[4] > return: unit : void - ├── funcs[4] > func: - │ ├── symbol: guests$peek(&guests$Node) - │ ├── params[0]: local #1 : i32 - │ ├── ret: i64 - │ └── body[0] > return > snapshot: - │ ├── type: i64 - │ └── ptr > offset: - │ ├── within: {i32, i64} - │ ├── path: 1 - │ └── base > resolve: local #1 : i32 - ├── funcs[5] > func: - │ ├── symbol: guests$twice(this guests$Node) - │ ├── params[0]: local #1 : ptr - │ ├── ret: i64 - │ └── body[0] > return > binary: - │ ├── type: i64 - │ ├── op: * - │ ├── left > snapshot: - │ │ ├── type: i64 - │ │ └── ptr > offset: - │ │ ├── within: {i32, i64} - │ │ ├── path: 1 - │ │ └── base: local #1 : ptr - │ └── right: 2 : i64 - ├── funcs[6] > func: - │ ├── symbol: guests$larger(&guests$Node, &guests$Node) - │ ├── params[0]: local #1 : i32 - │ ├── params[1]: local #2 : i32 - │ ├── ret: i32 - │ ├── body[0] > eval > expand: - │ │ ├── type: i1 - │ │ ├── label: @4 - │ │ ├── result: #5 - │ │ ├── body[0] > let: - │ │ │ ├── local: #3 - │ │ │ └── value > binary: - │ │ │ ├── type: i1 - │ │ │ ├── op: < - │ │ │ ├── left > snapshot: - │ │ │ │ ├── type: i64 - │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, i64} - │ │ │ │ ├── path: 1 - │ │ │ │ └── base > resolve: local #2 : i32 - │ │ │ └── right > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #1 : i32 - │ │ ├── body[1] > if: - │ │ │ ├── cond: local #3 : i1 - │ │ │ └── body[0] > return > terminal: local #1 : i32 - │ │ ├── body[2] > assign: - │ │ │ ├── place: #5 - │ │ │ └── value: local #3 : i1 - │ │ └── body[3]: leave @4 - │ └── body[1] > return > terminal: local #2 : i32 - └── funcs[7] > func: - ├── symbol: guests$Car(&guests$Node, guests$Node) - ├── params[0]: local #1 : i32 - ├── params[1]: local #2 : ptr - ├── ret: {i32, i32, {i32, i64}} - └── body[0] > return > escape: - ├── exit: function - ├── value > record: - │ ├── type: {i32, i32, {i32, i64}} - │ ├── members[0] > member 0: 0 : i32 - │ ├── members[1] > member 1 > terminal: local #1 : i32 - │ └── members[2] > member 2 > take: - │ ├── type: {i32, i64} - │ ├── address: local #2 : ptr - │ └── layout: guests$Node - └── layout: guests$Car diff --git a/tests/codegen/golden/hello.cgt b/tests/codegen/golden/hello.cgt index ac6573ee..791a0bba 100644 --- a/tests/codegen/golden/hello.cgt +++ b/tests/codegen/golden/hello.cgt @@ -2,15 +2,14 @@ ├── entry: hello$main() ├── layouts[0] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 └── funcs[0] > func: ├── symbol: hello$main() ├── params: ├── ret: void └── body[0] > scope: ├── id: %2 - ├── body[0] > host: + ├── body[0] > hold: │ ├── local: #1 │ ├── scope: %2 │ ├── value: "hello world" : handle diff --git a/tests/codegen/golden/lambdas.cgt b/tests/codegen/golden/lambdas.cgt index 1d0cfd82..1707b2b3 100644 --- a/tests/codegen/golden/lambdas.cgt +++ b/tests/codegen/golden/lambdas.cgt @@ -2,11 +2,10 @@ ├── entry: lambdas$main() ├── layouts[0] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── layouts[1] > layout: │ ├── type: lambdas$Tally - │ └── positions[0]: @0 (16 bytes) + │ └── positions: ├── layouts[2] > layout: │ ├── type: %primitives$Int │ └── positions: @@ -15,7 +14,7 @@ │ └── positions: ├── layouts[4] > layout: │ ├── type: lambdas$Shelf - │ └── positions[0]: @0 (16 bytes) + │ └── positions: ├── layouts[5] > layout: │ ├── type: lambdas$Counter │ └── positions: @@ -279,24 +278,24 @@ │ │ │ │ ├── type: handle │ │ │ │ ├── label: @21 │ │ │ │ ├── result: #22 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #20 │ │ │ │ │ ├── scope: %19 │ │ │ │ │ ├── value > call value: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── fn: local #15 : ptr │ │ │ │ │ │ └── args[0] > expand: - │ │ │ │ │ │ ├── type: ptr + │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── label: @17 │ │ │ │ │ │ ├── result: #18 - │ │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ │ ├── local: #16 │ │ │ │ │ │ │ ├── scope: %19 │ │ │ │ │ │ │ ├── value: "hey" : handle │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ └── body[1] > assign: │ │ │ │ │ │ ├── place: #18 - │ │ │ │ │ │ └── value: address of #16 : ptr + │ │ │ │ │ │ └── value: local #16 : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #22 @@ -305,7 +304,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @24 │ │ │ ├── result: #25 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #23 │ │ │ │ ├── scope: %19 │ │ │ │ ├── value: "hey!" : handle @@ -317,7 +316,7 @@ │ ├── body[25] > let: │ │ ├── local: #26 │ │ └── value: function lambdas$main()$lambda8 - │ ├── body[26] > host: + │ ├── body[26] > hold: │ │ ├── local: #27 │ │ ├── scope: %19 │ │ ├── value: "ab" : handle @@ -335,13 +334,13 @@ │ │ │ │ ├── type: handle │ │ │ │ ├── label: @29 │ │ │ │ ├── result: #30 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #28 │ │ │ │ │ ├── scope: %19 │ │ │ │ │ ├── value > call value: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── fn: local #26 : ptr - │ │ │ │ │ │ └── args[0] > mint: address of #27 : ptr + │ │ │ │ │ │ └── args[0]: local #27 : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #30 @@ -350,7 +349,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @32 │ │ │ ├── result: #33 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #31 │ │ │ │ ├── scope: %19 │ │ │ │ ├── value: "abab" : handle @@ -359,11 +358,11 @@ │ │ │ ├── place: #33 │ │ │ └── value: local #31 : handle │ │ └── right: 1 : i64 - │ ├── body[28] > host: + │ ├── body[28] > hold: │ │ ├── local: #34 │ │ ├── scope: %19 │ │ ├── value > call: - │ │ │ ├── type: {i32, i64} + │ │ │ ├── type: {i64} │ │ │ ├── fn: lambdas$Tally(%primitives$Int) │ │ │ └── args[0]: 2 : i64 │ │ └── layout: lambdas$Tally @@ -387,7 +386,7 @@ │ │ ├── left > call value: │ │ │ ├── type: i64 │ │ │ ├── fn: local #36 : ptr - │ │ │ └── args[0] > mint: address of #34 : ptr + │ │ │ └── args[0]: address of #34 : ptr │ │ └── right: 6 : i64 │ ├── body[33] > spawn: │ │ ├── task: #38 @@ -484,20 +483,20 @@ │ │ ├── task: #55 │ │ ├── scope: %19 │ │ ├── thunk: zane.spawn.3 - │ │ ├── frame: {handle, ptr, ptr} + │ │ ├── frame: {handle, ptr, handle} │ │ ├── args[0]: local #15 : ptr │ │ ├── args[1] > expand: - │ │ │ ├── type: ptr + │ │ │ ├── type: handle │ │ │ ├── label: @52 │ │ │ ├── result: #53 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #51 │ │ │ │ ├── scope: %19 │ │ │ │ ├── value: "go" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: │ │ │ ├── place: #53 - │ │ │ └── value: address of #51 : ptr + │ │ │ └── value: local #51 : handle │ │ ├── dest: #56 │ │ └── layout: %primitives$String │ ├── body[39] > eval > call: @@ -523,7 +522,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @60 │ │ │ ├── result: #61 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #59 │ │ │ │ ├── scope: %19 │ │ │ │ ├── value: "go!" : handle @@ -598,11 +597,11 @@ │ │ │ ├── type: i64 │ │ │ └── ptr: address of #73 : ptr │ │ └── right: 7 : i64 - │ ├── body[46] > host: + │ ├── body[46] > hold: │ │ ├── local: #76 │ │ ├── scope: %19 │ │ ├── value > call: - │ │ │ ├── type: {i32, i64} + │ │ │ ├── type: {i64} │ │ │ ├── fn: lambdas$Shelf(lambdas$Counter) │ │ │ └── args[0] > call: │ │ │ ├── type: i64 @@ -623,8 +622,8 @@ │ │ │ ├── body[0] > let: │ │ │ │ ├── local: #81 │ │ │ │ └── value > offset: - │ │ │ │ ├── within: {i32, i64} - │ │ │ │ ├── path: 1 + │ │ │ │ ├── within: {i64} + │ │ │ │ ├── path: 0 │ │ │ │ └── base: address of #76 : ptr │ │ │ └── body[1] > spawn: │ │ │ ├── task: #83 @@ -650,15 +649,15 @@ │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 │ │ │ └── base: address of #76 : ptr │ │ └── right: 15 : i64 │ ├── body[49] > let: │ │ ├── local: #85 │ │ └── value > offset: - │ │ ├── within: {i32, i64} - │ │ ├── path: 1 + │ │ ├── within: {i64} + │ │ ├── path: 0 │ │ └── base: address of #76 : ptr │ ├── body[50] > spawn: │ │ ├── task: #87 @@ -763,17 +762,17 @@ │ │ ├── type: void │ │ ├── fn: local #99 : ptr │ │ └── args[0] > expand: - │ │ ├── type: ptr + │ │ ├── type: handle │ │ ├── label: @101 │ │ ├── result: #102 - │ │ ├── body[0] > host: + │ │ ├── body[0] > hold: │ │ │ ├── local: #100 │ │ │ ├── scope: %19 │ │ │ ├── value: "said\n" : handle │ │ │ └── layout: %primitives$String │ │ └── body[1] > assign: │ │ ├── place: #102 - │ │ └── value: address of #100 : ptr + │ │ └── value: local #100 : handle │ └── body[58] > return: unit : void ├── funcs[1] > func: │ ├── symbol: lambdas$main()$lambda1 @@ -790,12 +789,12 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -975,7 +974,7 @@ │ └── body[0] > return: unit : void ├── funcs[15] > func: │ ├── symbol: lambdas$main()$lambda7 - │ ├── params[0]: local #1 : ptr + │ ├── params[0]: local #1 : handle │ ├── ret: handle │ └── body[0] > scope: │ ├── id: %5 @@ -984,14 +983,12 @@ │ ├── value > runtime: │ │ ├── type: handle │ │ ├── fn: zane_text_join - │ │ ├── args[0] > deref: - │ │ │ ├── type: handle - │ │ │ └── ptr: local #1 : ptr + │ │ ├── args[0]: local #1 : handle │ │ └── args[1] > expand: │ │ ├── type: handle │ │ ├── label: @3 │ │ ├── result: #4 - │ │ ├── body[0] > host: + │ │ ├── body[0] > hold: │ │ │ ├── local: #2 │ │ │ ├── scope: %5 │ │ │ ├── value: "!" : handle @@ -1002,30 +999,25 @@ │ └── layout: %primitives$String ├── funcs[16] > func: │ ├── symbol: lambdas$main()$lambda8 - │ ├── params[0]: local #1 : i32 + │ ├── params[0]: local #1 : handle │ ├── ret: handle │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > runtime: │ │ ├── type: handle │ │ ├── fn: zane_text_join - │ │ ├── args[0] > deref: - │ │ │ ├── type: handle - │ │ │ └── ptr > resolve: local #1 : i32 - │ │ └── args[1] > deref: - │ │ ├── type: handle - │ │ └── ptr > resolve: local #1 : i32 + │ │ ├── args[0]: local #1 : handle + │ │ └── args[1]: local #1 : handle │ └── layout: %primitives$String ├── funcs[17] > func: │ ├── symbol: lambdas$Tally(%primitives$Int) │ ├── params[0]: local #1 : i64 - │ ├── ret: {i32, i64} + │ ├── ret: {i64} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, i64} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ └── members[1] > member 1: local #1 : i64 + │ │ ├── type: {i64} + │ │ └── members[0] > member 0: local #1 : i64 │ └── layout: lambdas$Tally ├── funcs[18] > func: │ ├── symbol: lambdas$main()$lambda9 @@ -1033,28 +1025,28 @@ │ ├── params[1]: local #2 : i64 │ ├── ret: void │ ├── body[0] > assign: - │ │ ├── place: *#1.1 + │ │ ├── place: *#1.0 │ │ └── value > binary: │ │ ├── type: i64 │ │ ├── op: * │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 │ │ │ └── base: local #1 : ptr │ │ └── right: local #2 : i64 │ └── body[1] > return: unit : void ├── funcs[19] > func: │ ├── symbol: lambdas$main()$lambda10 - │ ├── params[0]: local #1 : i32 + │ ├── params[0]: local #1 : ptr │ ├── ret: i64 │ └── body[0] > return > snapshot: │ ├── type: i64 │ └── ptr > offset: - │ ├── within: {i32, i64} - │ ├── path: 1 - │ └── base > resolve: local #1 : i32 + │ ├── within: {i64} + │ ├── path: 0 + │ └── base: local #1 : ptr ├── funcs[20] > func: │ ├── symbol: lambdas$main()$lambda11 │ ├── params[0]: local #1 : ptr @@ -1065,13 +1057,12 @@ ├── funcs[21] > func: │ ├── symbol: lambdas$Shelf(lambdas$Counter) │ ├── params[0]: local #1 : i64 - │ ├── ret: {i32, i64} + │ ├── ret: {i64} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, i64} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ └── members[1] > member 1: local #1 : i64 + │ │ ├── type: {i64} + │ │ └── members[0] > member 0: local #1 : i64 │ └── layout: lambdas$Shelf ├── funcs[22] > func: │ ├── symbol: lambdas$Op.apply$lambda1 @@ -1093,14 +1084,12 @@ │ └── right: 3 : i64 ├── funcs[24] > func: │ ├── symbol: lambdas$main()$lambda12 - │ ├── params[0]: local #1 : ptr + │ ├── params[0]: local #1 : handle │ ├── ret: void │ ├── body[0] > eval > runtime: │ │ ├── type: void │ │ ├── fn: zane_print - │ │ └── args[0] > deref: - │ │ ├── type: handle - │ │ └── ptr: local #1 : ptr + │ │ └── args[0]: local #1 : handle │ └── body[1] > return: unit : void ├── funcs[25] > func: │ ├── symbol: lambdas$pick(%primitives$Bool)$lambda1 @@ -1163,7 +1152,7 @@ │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: - │ │ ├── within: {handle, ptr, ptr} + │ │ ├── within: {handle, ptr, handle} │ │ ├── path: 0 │ │ └── base: local #54 : ptr │ └── value > call value: @@ -1171,13 +1160,13 @@ │ ├── fn > deref: │ │ ├── type: ptr │ │ └── ptr > offset: - │ │ ├── within: {handle, ptr, ptr} + │ │ ├── within: {handle, ptr, handle} │ │ ├── path: 1 │ │ └── base: local #54 : ptr │ └── args[0] > deref: - │ ├── type: ptr + │ ├── type: handle │ └── ptr > offset: - │ ├── within: {handle, ptr, ptr} + │ ├── within: {handle, ptr, handle} │ ├── path: 2 │ └── base: local #54 : ptr ├── funcs[29] > func: diff --git a/tests/codegen/golden/lists.cgt b/tests/codegen/golden/lists.cgt index 8b458efb..555229b1 100644 --- a/tests/codegen/golden/lists.cgt +++ b/tests/codegen/golden/lists.cgt @@ -5,41 +5,38 @@ │ └── positions: ├── layouts[1] > layout: │ ├── type: %primitives$List<%primitives$Int> - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: list @0 of %primitives$Int (8 bytes apart) + │ └── positions[0]: list @0 of %primitives$Int (8 bytes apart) ├── layouts[2] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── layouts[3] > layout: │ ├── type: %primitives$List<%primitives$String> - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: list @0 of %primitives$String (32 bytes apart) + │ └── positions[0]: list @0 of %primitives$String (24 bytes apart) ├── layouts[4] > layout: │ ├── type: lists$Person - │ ├── positions[0]: @0 (48 bytes) - │ ├── positions[1]: @8 (32 bytes) - │ └── positions[2]: text @8 + │ └── positions[0]: text @0 ├── layouts[5] > layout: │ ├── type: %primitives$List - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: list @0 of lists$Person (48 bytes apart) + │ └── positions[0]: list @0 of lists$Person (32 bytes apart) ├── layouts[6] > layout: - │ ├── type: %primitives$List<%primitives$List<%primitives$Int>> - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: list @0 of %primitives$List<%primitives$Int> (32 bytes apart) + │ ├── type: ^lists$Person + │ └── positions[0]: text @0 ├── layouts[7] > layout: + │ ├── type: %primitives$List<%primitives$List<%primitives$Int>> + │ └── positions[0]: list @0 of %primitives$List<%primitives$Int> (24 bytes apart) + ├── layouts[8] > layout: + │ ├── type: ^%primitives$List<%primitives$Int> + │ └── positions[0]: list @0 of %primitives$Int (8 bytes apart) + ├── layouts[9] > layout: │ ├── type: lists$Stack<%primitives$String> - │ ├── positions[0]: @0 (40 bytes) - │ ├── positions[1]: @8 (32 bytes) - │ └── positions[2]: list @8 of %primitives$String (32 bytes apart) + │ └── positions[0]: list @0 of %primitives$String (24 bytes apart) ├── funcs[0] > func: │ ├── symbol: lists$main() │ ├── params: │ ├── ret: void │ └── body[0] > scope: │ ├── id: %2 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #1 │ │ ├── scope: %2 │ │ ├── value > runtime: @@ -55,7 +52,7 @@ │ │ ├── op: == │ │ ├── left > member: │ │ │ ├── type: i64 - │ │ │ ├── index: 2 + │ │ │ ├── index: 1 │ │ │ └── value: local #1 : handle │ │ └── right: 0 : i64 │ ├── body[2] > let: @@ -93,8 +90,7 @@ │ │ │ │ │ ├── type: ptr │ │ │ │ │ ├── fn: zane_list_push │ │ │ │ │ ├── args[0]: address of #1 : ptr - │ │ │ │ │ ├── args[1]: 8 : i64 - │ │ │ │ │ └── args[2]: layout %primitives$Int + │ │ │ │ │ └── args[1]: 8 : i64 │ │ │ │ └── body[2] > place: │ │ │ │ ├── address: local #9 : ptr │ │ │ │ ├── value: local #8 : i64 @@ -117,7 +113,7 @@ │ │ ├── left > call: │ │ │ ├── type: i64 │ │ │ ├── fn: lists$count<%primitives$Int>(&%primitives$List<%primitives$Int>) - │ │ │ └── args[0] > mint: address of #1 : ptr + │ │ │ └── args[0]: address of #1 : ptr │ │ └── right: 100 : i64 │ ├── body[5] > eval > call: │ │ ├── type: void @@ -162,9 +158,9 @@ │ │ ├── left > call: │ │ │ ├── type: i64 │ │ │ ├── fn: lists$sum(&%primitives$List<%primitives$Int>) - │ │ │ └── args[0] > mint: address of #1 : ptr + │ │ │ └── args[0]: address of #1 : ptr │ │ └── right: 5000 : i64 - │ ├── body[8] > host: + │ ├── body[8] > hold: │ │ ├── local: #11 │ │ ├── scope: %2 │ │ ├── value > runtime: @@ -184,13 +180,12 @@ │ │ │ ├── type: ptr │ │ │ ├── fn: zane_list_push │ │ │ ├── args[0]: address of #11 : ptr - │ │ │ ├── args[1]: 32 : i64 - │ │ │ └── args[2]: layout %primitives$String + │ │ │ └── args[1]: 24 : i64 │ │ └── body[2] > place: │ │ ├── address: local #13 : ptr │ │ ├── value: local #12 : handle │ │ └── layout: %primitives$String - │ ├── body[10] > host: + │ ├── body[10] > hold: │ │ ├── local: #15 │ │ ├── scope: %2 │ │ ├── value: "be" : handle @@ -200,9 +195,9 @@ │ │ ├── label: @18 │ │ ├── body[0] > let: │ │ │ ├── local: #16 - │ │ │ └── value > take: + │ │ │ └── value > copy: │ │ │ ├── type: handle - │ │ │ ├── address: address of #15 : ptr + │ │ │ ├── value: local #15 : handle │ │ │ └── layout: %primitives$String │ │ ├── body[1] > let: │ │ │ ├── local: #17 @@ -210,8 +205,7 @@ │ │ │ ├── type: ptr │ │ │ ├── fn: zane_list_push │ │ │ ├── args[0]: address of #11 : ptr - │ │ │ ├── args[1]: 32 : i64 - │ │ │ └── args[2]: layout %primitives$String + │ │ │ └── args[1]: 24 : i64 │ │ └── body[2] > place: │ │ ├── address: local #17 : ptr │ │ ├── value: local #16 : handle @@ -229,28 +223,28 @@ │ │ │ │ ├── type: handle │ │ │ │ ├── label: @20 │ │ │ │ ├── result: #21 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #19 │ │ │ │ │ ├── scope: %2 │ │ │ │ │ ├── value > runtime: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── fn: zane_text_join - │ │ │ │ │ │ ├── args[0] > deref: + │ │ │ │ │ │ ├── args[0] > snapshot: │ │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ │ └── ptr > runtime: │ │ │ │ │ │ │ ├── type: ptr │ │ │ │ │ │ │ ├── fn: zane_list_at │ │ │ │ │ │ │ ├── args[0]: address of #11 : ptr │ │ │ │ │ │ │ ├── args[1]: 1 : i64 - │ │ │ │ │ │ │ └── args[2]: 32 : i64 - │ │ │ │ │ │ └── args[1] > deref: + │ │ │ │ │ │ │ └── args[2]: 24 : i64 + │ │ │ │ │ │ └── args[1] > snapshot: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ └── ptr > runtime: │ │ │ │ │ │ ├── type: ptr │ │ │ │ │ │ ├── fn: zane_list_at │ │ │ │ │ │ ├── args[0]: address of #11 : ptr │ │ │ │ │ │ ├── args[1]: 2 : i64 - │ │ │ │ │ │ └── args[2]: 32 : i64 + │ │ │ │ │ │ └── args[2]: 24 : i64 │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #21 @@ -259,7 +253,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @23 │ │ │ ├── result: #24 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #22 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "tobe" : handle @@ -268,13 +262,13 @@ │ │ │ ├── place: #24 │ │ │ └── value: local #22 : handle │ │ └── right: 1 : i64 - │ ├── body[13] > overwrite contingent: + │ ├── body[13] > overwrite: │ │ ├── address > runtime: │ │ │ ├── type: ptr │ │ │ ├── fn: zane_list_at │ │ │ ├── args[0]: address of #11 : ptr │ │ │ ├── args[1]: 1 : i64 - │ │ │ └── args[2]: 32 : i64 + │ │ │ └── args[2]: 24 : i64 │ │ ├── value: "or" : handle │ │ └── layout: %primitives$String │ ├── body[14] > eval > call: @@ -286,19 +280,19 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > deref: + │ │ │ ├── args[0] > snapshot: │ │ │ │ ├── type: handle │ │ │ │ └── ptr > runtime: │ │ │ │ ├── type: ptr │ │ │ │ ├── fn: zane_list_at │ │ │ │ ├── args[0]: address of #11 : ptr │ │ │ │ ├── args[1]: 1 : i64 - │ │ │ │ └── args[2]: 32 : i64 + │ │ │ │ └── args[2]: 24 : i64 │ │ │ └── args[1] > expand: │ │ │ ├── type: handle │ │ │ ├── label: @26 │ │ │ ├── result: #27 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #25 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "or" : handle @@ -307,7 +301,7 @@ │ │ │ ├── place: #27 │ │ │ └── value: local #25 : handle │ │ └── right: 1 : i64 - │ ├── body[15] > host: + │ ├── body[15] > hold: │ │ ├── local: #28 │ │ ├── scope: %2 │ │ ├── value > runtime: @@ -315,58 +309,54 @@ │ │ │ ├── fn: zane_list_new │ │ │ └── args: │ │ └── layout: %primitives$List - │ ├── body[16] > host: + │ ├── body[16] > hold: │ │ ├── local: #32 │ │ ├── scope: %2 │ │ ├── value > call: - │ │ │ ├── type: {i32, handle, i64} + │ │ │ ├── type: {handle, i64} │ │ │ ├── fn: lists$Person(%primitives$String, %primitives$Int) │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: ptr + │ │ │ │ ├── type: handle │ │ │ │ ├── label: @30 │ │ │ │ ├── result: #31 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #29 │ │ │ │ │ ├── scope: %2 │ │ │ │ │ ├── value: "ada" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #31 - │ │ │ │ └── value: address of #29 : ptr + │ │ │ │ └── value: local #29 : handle │ │ │ └── args[1]: 36 : i64 - │ │ └── layout: lists$Person - │ ├── body[17] > let: - │ │ ├── local: #33 - │ │ └── value > mint: address of #32 : ptr - │ ├── body[18] > eval > expand: + │ │ └── layout: ^lists$Person + │ ├── body[17] > eval > expand: │ │ ├── type: void - │ │ ├── label: @36 + │ │ ├── label: @35 │ │ ├── body[0] > let: - │ │ │ ├── local: #34 + │ │ │ ├── local: #33 │ │ │ └── value > take: - │ │ │ ├── type: {i32, handle, i64} + │ │ │ ├── type: {handle, i64} │ │ │ ├── address: address of #32 : ptr │ │ │ └── layout: lists$Person │ │ ├── body[1] > let: - │ │ │ ├── local: #35 + │ │ │ ├── local: #34 │ │ │ └── value > runtime: │ │ │ ├── type: ptr │ │ │ ├── fn: zane_list_push │ │ │ ├── args[0]: address of #28 : ptr - │ │ │ ├── args[1]: 48 : i64 - │ │ │ └── args[2]: layout lists$Person + │ │ │ └── args[1]: 32 : i64 │ │ └── body[2] > place: - │ │ ├── address: local #35 : ptr - │ │ ├── value: local #34 : {i32, handle, i64} + │ │ ├── address: local #34 : ptr + │ │ ├── value: local #33 : {handle, i64} │ │ └── layout: lists$Person - │ ├── body[19] > let: - │ │ ├── local: #37 + │ ├── body[18] > let: + │ │ ├── local: #36 │ │ └── value: 1 : i64 - │ ├── body[20] > eval > expand: + │ ├── body[19] > eval > expand: │ │ ├── type: void - │ │ ├── label: @39 + │ │ ├── label: @38 │ │ ├── body[0] > let: - │ │ │ ├── local: #38 + │ │ │ ├── local: #37 │ │ │ └── value: 1000 : i64 │ │ ├── body[1] > repeat: │ │ │ ├── count > binary: @@ -375,58 +365,57 @@ │ │ │ │ ├── left > binary: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #38 : i64 + │ │ │ │ │ ├── left: local #37 : i64 │ │ │ │ │ └── right > flip: │ │ │ │ │ ├── type: i64 - │ │ │ │ │ └── value: local #37 : i64 + │ │ │ │ │ └── value: local #36 : i64 │ │ │ │ └── right: 1 : i64 │ │ │ ├── body[0] > if: │ │ │ │ ├── cond: true : i1 │ │ │ │ └── body[0] > scope: - │ │ │ │ ├── id: %45 + │ │ │ │ ├── id: %44 │ │ │ │ └── body[0] > eval > expand: │ │ │ │ ├── type: void - │ │ │ │ ├── label: @48 + │ │ │ │ ├── label: @47 │ │ │ │ ├── body[0] > let: - │ │ │ │ │ ├── local: #46 + │ │ │ │ │ ├── local: #45 │ │ │ │ │ └── value > call: - │ │ │ │ │ ├── type: {i32, handle, i64} + │ │ │ │ │ ├── type: {handle, i64} │ │ │ │ │ ├── fn: lists$Person(%primitives$String, %primitives$Int) │ │ │ │ │ ├── args[0] > expand: - │ │ │ │ │ │ ├── type: ptr - │ │ │ │ │ │ ├── label: @43 - │ │ │ │ │ │ ├── result: #44 - │ │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ │ ├── local: #42 - │ │ │ │ │ │ │ ├── scope: %45 + │ │ │ │ │ │ ├── type: handle + │ │ │ │ │ │ ├── label: @42 + │ │ │ │ │ │ ├── result: #43 + │ │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ │ ├── local: #41 + │ │ │ │ │ │ │ ├── scope: %44 │ │ │ │ │ │ │ ├── value: "x" : handle │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ ├── place: #44 - │ │ │ │ │ │ └── value: address of #42 : ptr - │ │ │ │ │ └── args[1]: local #37 : i64 + │ │ │ │ │ │ ├── place: #43 + │ │ │ │ │ │ └── value: local #41 : handle + │ │ │ │ │ └── args[1]: local #36 : i64 │ │ │ │ ├── body[1] > let: - │ │ │ │ │ ├── local: #47 + │ │ │ │ │ ├── local: #46 │ │ │ │ │ └── value > runtime: │ │ │ │ │ ├── type: ptr │ │ │ │ │ ├── fn: zane_list_push │ │ │ │ │ ├── args[0]: address of #28 : ptr - │ │ │ │ │ ├── args[1]: 48 : i64 - │ │ │ │ │ └── args[2]: layout lists$Person + │ │ │ │ │ └── args[1]: 32 : i64 │ │ │ │ └── body[2] > place: - │ │ │ │ ├── address: local #47 : ptr - │ │ │ │ ├── value: local #46 : {i32, handle, i64} + │ │ │ │ ├── address: local #46 : ptr + │ │ │ │ ├── value: local #45 : {handle, i64} │ │ │ │ └── layout: lists$Person │ │ │ └── body[1] > assign: - │ │ │ ├── place: #37 + │ │ │ ├── place: #36 │ │ │ └── value > binary: │ │ │ ├── type: i64 │ │ │ ├── op: + - │ │ │ ├── left: local #37 : i64 + │ │ │ ├── left: local #36 : i64 │ │ │ └── right: 1 : i64 │ │ ├── body[2] > eval: unit : void - │ │ └── body[3]: leave @39 - │ ├── body[21] > eval > call: + │ │ └── body[3]: leave @38 + │ ├── body[20] > eval > call: │ │ ├── type: void │ │ ├── fn: lists$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -435,26 +424,31 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > deref: + │ │ │ ├── args[0] > snapshot: │ │ │ │ ├── type: handle │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, handle, i64} - │ │ │ │ ├── path: 1 - │ │ │ │ └── base > resolve: local #33 : i32 + │ │ │ │ ├── within: {handle, i64} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base > runtime: + │ │ │ │ ├── type: ptr + │ │ │ │ ├── fn: zane_list_at + │ │ │ │ ├── args[0]: address of #28 : ptr + │ │ │ │ ├── args[1]: 1 : i64 + │ │ │ │ └── args[2]: 32 : i64 │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @50 - │ │ │ ├── result: #51 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #49 + │ │ │ ├── label: @49 + │ │ │ ├── result: #50 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #48 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "ada" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #51 - │ │ │ └── value: local #49 : handle + │ │ │ ├── place: #50 + │ │ │ └── value: local #48 : handle │ │ └── right: 1 : i64 - │ ├── body[22] > eval > call: + │ ├── body[21] > eval > call: │ │ ├── type: void │ │ ├── fn: lists$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -463,53 +457,40 @@ │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, handle, i64} - │ │ │ ├── path: 2 + │ │ │ ├── within: {handle, i64} + │ │ │ ├── path: 1 │ │ │ └── base > runtime: │ │ │ ├── type: ptr │ │ │ ├── fn: zane_list_at │ │ │ ├── args[0]: address of #28 : ptr │ │ │ ├── args[1]: 1001 : i64 - │ │ │ └── args[2]: 48 : i64 + │ │ │ └── args[2]: 32 : i64 │ │ └── right: 1000 : i64 - │ ├── body[23] > overwrite contingent: + │ ├── body[22] > overwrite: │ │ ├── address > runtime: │ │ │ ├── type: ptr │ │ │ ├── fn: zane_list_at │ │ │ ├── args[0]: address of #28 : ptr │ │ │ ├── args[1]: 1 : i64 - │ │ │ └── args[2]: 48 : i64 + │ │ │ └── args[2]: 32 : i64 │ │ ├── value > call: - │ │ │ ├── type: {i32, handle, i64} + │ │ │ ├── type: {handle, i64} │ │ │ ├── fn: lists$Person(%primitives$String, %primitives$Int) │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: ptr - │ │ │ │ ├── label: @53 - │ │ │ │ ├── result: #54 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #52 + │ │ │ │ ├── type: handle + │ │ │ │ ├── label: @52 + │ │ │ │ ├── result: #53 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #51 │ │ │ │ │ ├── scope: %2 │ │ │ │ │ ├── value: "grace" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #54 - │ │ │ │ └── value: address of #52 : ptr + │ │ │ │ ├── place: #53 + │ │ │ │ └── value: local #51 : handle │ │ │ └── args[1]: 45 : i64 │ │ └── layout: lists$Person - │ ├── body[24] > eval > call: - │ │ ├── type: void - │ │ ├── fn: lists$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > snapshot: - │ │ │ ├── type: i64 - │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, handle, i64} - │ │ │ ├── path: 2 - │ │ │ └── base > resolve: local #33 : i32 - │ │ └── right: 36 : i64 - │ ├── body[25] > eval > call: + │ ├── body[23] > eval > call: │ │ ├── type: void │ │ ├── fn: lists$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -518,46 +499,57 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > member: + │ │ │ ├── args[0] > snapshot: │ │ │ │ ├── type: handle - │ │ │ │ ├── index: 1 - │ │ │ │ └── value > deref: - │ │ │ │ ├── type: {i32, handle, i64} - │ │ │ │ └── ptr > runtime: + │ │ │ │ └── ptr > offset: + │ │ │ │ ├── within: {handle, i64} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base > runtime: │ │ │ │ ├── type: ptr │ │ │ │ ├── fn: zane_list_at │ │ │ │ ├── args[0]: address of #28 : ptr │ │ │ │ ├── args[1]: 1 : i64 - │ │ │ │ └── args[2]: 48 : i64 + │ │ │ │ └── args[2]: 32 : i64 │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @56 - │ │ │ ├── result: #57 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #55 + │ │ │ ├── label: @55 + │ │ │ ├── result: #56 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #54 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "grace" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #57 - │ │ │ └── value: local #55 : handle + │ │ │ ├── place: #56 + │ │ │ └── value: local #54 : handle │ │ └── right: 1 : i64 - │ ├── body[26] > host: - │ │ ├── local: #58 + │ ├── body[24] > eval > call: + │ │ ├── type: void + │ │ ├── fn: lists$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > member: + │ │ │ ├── type: i64 + │ │ │ ├── index: 1 + │ │ │ └── value: local #28 : handle + │ │ └── right: 1001 : i64 + │ ├── body[25] > hold: + │ │ ├── local: #57 │ │ ├── scope: %2 │ │ ├── value > runtime: │ │ │ ├── type: handle │ │ │ ├── fn: zane_list_new │ │ │ └── args: │ │ └── layout: %primitives$List<%primitives$List<%primitives$Int>> - │ ├── body[27] > let: - │ │ ├── local: #59 + │ ├── body[26] > let: + │ │ ├── local: #58 │ │ └── value: 1 : i64 - │ ├── body[28] > eval > expand: + │ ├── body[27] > eval > expand: │ │ ├── type: void - │ │ ├── label: @61 + │ │ ├── label: @60 │ │ ├── body[0] > let: - │ │ │ ├── local: #60 + │ │ │ ├── local: #59 │ │ │ └── value: 3 : i64 │ │ ├── body[1] > repeat: │ │ │ ├── count > binary: @@ -566,94 +558,91 @@ │ │ │ │ ├── left > binary: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #60 : i64 + │ │ │ │ │ ├── left: local #59 : i64 │ │ │ │ │ └── right > flip: │ │ │ │ │ ├── type: i64 - │ │ │ │ │ └── value: local #59 : i64 + │ │ │ │ │ └── value: local #58 : i64 │ │ │ │ └── right: 1 : i64 │ │ │ ├── body[0] > if: │ │ │ │ ├── cond: true : i1 │ │ │ │ └── body[0] > scope: - │ │ │ │ ├── id: %65 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #64 - │ │ │ │ │ ├── scope: %65 + │ │ │ │ ├── id: %64 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #63 + │ │ │ │ │ ├── scope: %64 │ │ │ │ │ ├── value > runtime: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── fn: zane_list_new │ │ │ │ │ │ └── args: - │ │ │ │ │ └── layout: %primitives$List<%primitives$Int> + │ │ │ │ │ └── layout: ^%primitives$List<%primitives$Int> │ │ │ │ ├── body[1] > eval > expand: │ │ │ │ │ ├── type: void - │ │ │ │ │ ├── label: @68 + │ │ │ │ │ ├── label: @67 │ │ │ │ │ ├── body[0] > let: - │ │ │ │ │ │ ├── local: #66 - │ │ │ │ │ │ └── value: local #59 : i64 + │ │ │ │ │ │ ├── local: #65 + │ │ │ │ │ │ └── value: local #58 : i64 │ │ │ │ │ ├── body[1] > let: - │ │ │ │ │ │ ├── local: #67 + │ │ │ │ │ │ ├── local: #66 │ │ │ │ │ │ └── value > runtime: │ │ │ │ │ │ ├── type: ptr │ │ │ │ │ │ ├── fn: zane_list_push - │ │ │ │ │ │ ├── args[0]: address of #64 : ptr - │ │ │ │ │ │ ├── args[1]: 8 : i64 - │ │ │ │ │ │ └── args[2]: layout %primitives$Int + │ │ │ │ │ │ ├── args[0]: address of #63 : ptr + │ │ │ │ │ │ └── args[1]: 8 : i64 │ │ │ │ │ └── body[2] > place: - │ │ │ │ │ ├── address: local #67 : ptr - │ │ │ │ │ ├── value: local #66 : i64 + │ │ │ │ │ ├── address: local #66 : ptr + │ │ │ │ │ ├── value: local #65 : i64 │ │ │ │ │ └── layout: %primitives$Int │ │ │ │ ├── body[2] > eval > expand: │ │ │ │ │ ├── type: void - │ │ │ │ │ ├── label: @71 + │ │ │ │ │ ├── label: @70 │ │ │ │ │ ├── body[0] > let: - │ │ │ │ │ │ ├── local: #69 + │ │ │ │ │ │ ├── local: #68 │ │ │ │ │ │ └── value > binary: │ │ │ │ │ │ ├── type: i64 │ │ │ │ │ │ ├── op: * - │ │ │ │ │ │ ├── left: local #59 : i64 + │ │ │ │ │ │ ├── left: local #58 : i64 │ │ │ │ │ │ └── right: 10 : i64 │ │ │ │ │ ├── body[1] > let: - │ │ │ │ │ │ ├── local: #70 + │ │ │ │ │ │ ├── local: #69 │ │ │ │ │ │ └── value > runtime: │ │ │ │ │ │ ├── type: ptr │ │ │ │ │ │ ├── fn: zane_list_push - │ │ │ │ │ │ ├── args[0]: address of #64 : ptr - │ │ │ │ │ │ ├── args[1]: 8 : i64 - │ │ │ │ │ │ └── args[2]: layout %primitives$Int + │ │ │ │ │ │ ├── args[0]: address of #63 : ptr + │ │ │ │ │ │ └── args[1]: 8 : i64 │ │ │ │ │ └── body[2] > place: - │ │ │ │ │ ├── address: local #70 : ptr - │ │ │ │ │ ├── value: local #69 : i64 + │ │ │ │ │ ├── address: local #69 : ptr + │ │ │ │ │ ├── value: local #68 : i64 │ │ │ │ │ └── layout: %primitives$Int │ │ │ │ └── body[3] > eval > expand: │ │ │ │ ├── type: void - │ │ │ │ ├── label: @74 + │ │ │ │ ├── label: @73 │ │ │ │ ├── body[0] > let: - │ │ │ │ │ ├── local: #72 + │ │ │ │ │ ├── local: #71 │ │ │ │ │ └── value > take: │ │ │ │ │ ├── type: handle - │ │ │ │ │ ├── address: address of #64 : ptr + │ │ │ │ │ ├── address: address of #63 : ptr │ │ │ │ │ └── layout: %primitives$List<%primitives$Int> │ │ │ │ ├── body[1] > let: - │ │ │ │ │ ├── local: #73 + │ │ │ │ │ ├── local: #72 │ │ │ │ │ └── value > runtime: │ │ │ │ │ ├── type: ptr │ │ │ │ │ ├── fn: zane_list_push - │ │ │ │ │ ├── args[0]: address of #58 : ptr - │ │ │ │ │ ├── args[1]: 32 : i64 - │ │ │ │ │ └── args[2]: layout %primitives$List<%primitives$Int> + │ │ │ │ │ ├── args[0]: address of #57 : ptr + │ │ │ │ │ └── args[1]: 24 : i64 │ │ │ │ └── body[2] > place: - │ │ │ │ ├── address: local #73 : ptr - │ │ │ │ ├── value: local #72 : handle + │ │ │ │ ├── address: local #72 : ptr + │ │ │ │ ├── value: local #71 : handle │ │ │ │ └── layout: %primitives$List<%primitives$Int> │ │ │ └── body[1] > assign: - │ │ │ ├── place: #59 + │ │ │ ├── place: #58 │ │ │ └── value > binary: │ │ │ ├── type: i64 │ │ │ ├── op: + - │ │ │ ├── left: local #59 : i64 + │ │ │ ├── left: local #58 : i64 │ │ │ └── right: 1 : i64 │ │ ├── body[2] > eval: unit : void - │ │ └── body[3]: leave @61 - │ ├── body[29] > eval > call: + │ │ └── body[3]: leave @60 + │ ├── body[28] > eval > call: │ │ ├── type: void │ │ ├── fn: lists$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -667,53 +656,53 @@ │ │ │ ├── args[0] > runtime: │ │ │ │ ├── type: ptr │ │ │ │ ├── fn: zane_list_at - │ │ │ │ ├── args[0]: address of #58 : ptr + │ │ │ │ ├── args[0]: address of #57 : ptr │ │ │ │ ├── args[1]: 3 : i64 - │ │ │ │ └── args[2]: 32 : i64 + │ │ │ │ └── args[2]: 24 : i64 │ │ │ ├── args[1]: 2 : i64 │ │ │ └── args[2]: 8 : i64 │ │ └── right: 30 : i64 - │ ├── body[30] > host: - │ │ ├── local: #75 + │ ├── body[29] > hold: + │ │ ├── local: #74 │ │ ├── scope: %2 │ │ ├── value > call: - │ │ │ ├── type: {i32, handle} + │ │ │ ├── type: {handle} │ │ │ ├── fn: lists$Stack<%primitives$String>(Type) │ │ │ └── args: │ │ └── layout: lists$Stack<%primitives$String> - │ ├── body[31] > eval > call: + │ ├── body[30] > eval > call: │ │ ├── type: void │ │ ├── fn: lists$add<%primitives$String>(this lists$Stack<%primitives$String>, %primitives$String) - │ │ ├── args[0]: address of #75 : ptr + │ │ ├── args[0]: address of #74 : ptr │ │ └── args[1] > expand: - │ │ ├── type: ptr - │ │ ├── label: @77 - │ │ ├── result: #78 - │ │ ├── body[0] > host: - │ │ │ ├── local: #76 + │ │ ├── type: handle + │ │ ├── label: @76 + │ │ ├── result: #77 + │ │ ├── body[0] > hold: + │ │ │ ├── local: #75 │ │ │ ├── scope: %2 │ │ │ ├── value: "first" : handle │ │ │ └── layout: %primitives$String │ │ └── body[1] > assign: - │ │ ├── place: #78 - │ │ └── value: address of #76 : ptr - │ ├── body[32] > eval > call: + │ │ ├── place: #77 + │ │ └── value: local #75 : handle + │ ├── body[31] > eval > call: │ │ ├── type: void │ │ ├── fn: lists$add<%primitives$String>(this lists$Stack<%primitives$String>, %primitives$String) - │ │ ├── args[0]: address of #75 : ptr + │ │ ├── args[0]: address of #74 : ptr │ │ └── args[1] > expand: - │ │ ├── type: ptr - │ │ ├── label: @80 - │ │ ├── result: #81 - │ │ ├── body[0] > host: - │ │ │ ├── local: #79 + │ │ ├── type: handle + │ │ ├── label: @79 + │ │ ├── result: #80 + │ │ ├── body[0] > hold: + │ │ │ ├── local: #78 │ │ │ ├── scope: %2 │ │ │ ├── value: "second" : handle │ │ │ └── layout: %primitives$String │ │ └── body[1] > assign: - │ │ ├── place: #81 - │ │ └── value: address of #79 : ptr - │ ├── body[33] > eval > call: + │ │ ├── place: #80 + │ │ └── value: local #78 : handle + │ ├── body[32] > eval > call: │ │ ├── type: void │ │ ├── fn: lists$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -722,43 +711,43 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > deref: + │ │ │ ├── args[0] > snapshot: │ │ │ │ ├── type: handle │ │ │ │ └── ptr > expand: │ │ │ │ ├── type: ptr - │ │ │ │ ├── label: @84 - │ │ │ │ ├── result: #85 + │ │ │ │ ├── label: @83 + │ │ │ │ ├── result: #84 │ │ │ │ ├── body[0] > let: - │ │ │ │ │ ├── local: #82 - │ │ │ │ │ └── value: address of #75 : ptr + │ │ │ │ │ ├── local: #81 + │ │ │ │ │ └── value: address of #74 : ptr │ │ │ │ ├── body[1] > let: - │ │ │ │ │ ├── local: #83 + │ │ │ │ │ ├── local: #82 │ │ │ │ │ └── value: 2 : i64 │ │ │ │ └── body[2] > assign: - │ │ │ │ ├── place: #85 + │ │ │ │ ├── place: #84 │ │ │ │ └── value > runtime: │ │ │ │ ├── type: ptr │ │ │ │ ├── fn: zane_list_at │ │ │ │ ├── args[0] > offset: - │ │ │ │ │ ├── within: {i32, handle} - │ │ │ │ │ ├── path: 1 - │ │ │ │ │ └── base: local #82 : ptr - │ │ │ │ ├── args[1]: local #83 : i64 - │ │ │ │ └── args[2]: 32 : i64 + │ │ │ │ │ ├── within: {handle} + │ │ │ │ │ ├── path: 0 + │ │ │ │ │ └── base: local #81 : ptr + │ │ │ │ ├── args[1]: local #82 : i64 + │ │ │ │ └── args[2]: 24 : i64 │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @87 - │ │ │ ├── result: #88 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #86 + │ │ │ ├── label: @86 + │ │ │ ├── result: #87 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #85 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "second" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #88 - │ │ │ └── value: local #86 : handle + │ │ │ ├── place: #87 + │ │ │ └── value: local #85 : handle │ │ └── right: 1 : i64 - │ ├── body[34] > eval > call: + │ ├── body[33] > eval > call: │ │ ├── type: void │ │ ├── fn: lists$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -767,24 +756,24 @@ │ │ ├── left > call: │ │ │ ├── type: i64 │ │ │ ├── fn: lists$count<%primitives$String>(&%primitives$List<%primitives$String>) - │ │ │ └── args[0] > mint > offset: - │ │ │ ├── within: {i32, handle} - │ │ │ ├── path: 1 - │ │ │ └── base: address of #75 : ptr + │ │ │ └── args[0] > offset: + │ │ │ ├── within: {handle} + │ │ │ ├── path: 0 + │ │ │ └── base: address of #74 : ptr │ │ └── right: 2 : i64 - │ └── body[35] > return: unit : void + │ └── body[34] > return: unit : void ├── funcs[1] > func: │ ├── symbol: lists$check(%primitives$Bool) │ ├── params[0]: local #1 : i1 │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -806,17 +795,17 @@ │ └── body[4] > return: unit : void ├── funcs[2] > func: │ ├── symbol: lists$count<%primitives$Int>(&%primitives$List<%primitives$Int>) - │ ├── params[0]: local #1 : i32 + │ ├── params[0]: local #1 : ptr │ ├── ret: i64 │ └── body[0] > return > member: │ ├── type: i64 - │ ├── index: 2 + │ ├── index: 1 │ └── value > deref: │ ├── type: handle - │ └── ptr > resolve: local #1 : i32 + │ └── ptr: local #1 : ptr ├── funcs[3] > func: │ ├── symbol: lists$sum(&%primitives$List<%primitives$Int>) - │ ├── params[0]: local #1 : i32 + │ ├── params[0]: local #1 : ptr │ ├── ret: i64 │ ├── body[0] > let: │ │ ├── local: #2 @@ -831,10 +820,10 @@ │ │ │ ├── local: #4 │ │ │ └── value > member: │ │ │ ├── type: i64 - │ │ │ ├── index: 2 + │ │ │ ├── index: 1 │ │ │ └── value > deref: │ │ │ ├── type: handle - │ │ │ └── ptr > resolve: local #1 : i32 + │ │ │ └── ptr: local #1 : ptr │ │ ├── body[1] > repeat: │ │ │ ├── count > binary: │ │ │ │ ├── type: i64 @@ -860,7 +849,7 @@ │ │ │ │ └── ptr > runtime: │ │ │ │ ├── type: ptr │ │ │ │ ├── fn: zane_list_at - │ │ │ │ ├── args[0] > resolve: local #1 : i32 + │ │ │ │ ├── args[0]: local #1 : ptr │ │ │ │ ├── args[1]: local #3 : i64 │ │ │ │ └── args[2]: 8 : i64 │ │ │ └── body[1] > assign: @@ -875,30 +864,28 @@ │ └── body[3] > return: local #2 : i64 ├── funcs[4] > func: │ ├── symbol: lists$Person(%primitives$String, %primitives$Int) - │ ├── params[0]: local #1 : ptr + │ ├── params[0]: local #1 : handle │ ├── params[1]: local #2 : i64 - │ ├── ret: {i32, handle, i64} + │ ├── ret: {handle, i64} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, handle, i64} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ ├── members[1] > member 1 > take: + │ │ ├── type: {handle, i64} + │ │ ├── members[0] > member 0 > copy: │ │ │ ├── type: handle - │ │ │ ├── address: local #1 : ptr + │ │ │ ├── value: local #1 : handle │ │ │ └── layout: %primitives$String - │ │ └── members[2] > member 2: local #2 : i64 + │ │ └── members[1] > member 1: local #2 : i64 │ └── layout: lists$Person ├── funcs[5] > func: │ ├── symbol: lists$Stack<%primitives$String>(Type) │ ├── params: - │ ├── ret: {i32, handle} + │ ├── ret: {handle} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, handle} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ └── members[1] > member 1 > runtime: + │ │ ├── type: {handle} + │ │ └── members[0] > member 0 > runtime: │ │ ├── type: handle │ │ ├── fn: zane_list_new │ │ └── args: @@ -906,16 +893,16 @@ ├── funcs[6] > func: │ ├── symbol: lists$add<%primitives$String>(this lists$Stack<%primitives$String>, %primitives$String) │ ├── params[0]: local #1 : ptr - │ ├── params[1]: local #2 : ptr + │ ├── params[1]: local #2 : handle │ ├── ret: void │ ├── body[0] > eval > expand: │ │ ├── type: void │ │ ├── label: @5 │ │ ├── body[0] > let: │ │ │ ├── local: #3 - │ │ │ └── value > take: + │ │ │ └── value > copy: │ │ │ ├── type: handle - │ │ │ ├── address: local #2 : ptr + │ │ │ ├── value: local #2 : handle │ │ │ └── layout: %primitives$String │ │ ├── body[1] > let: │ │ │ ├── local: #4 @@ -923,11 +910,10 @@ │ │ │ ├── type: ptr │ │ │ ├── fn: zane_list_push │ │ │ ├── args[0] > offset: - │ │ │ │ ├── within: {i32, handle} - │ │ │ │ ├── path: 1 + │ │ │ │ ├── within: {handle} + │ │ │ │ ├── path: 0 │ │ │ │ └── base: local #1 : ptr - │ │ │ ├── args[1]: 32 : i64 - │ │ │ └── args[2]: layout %primitives$String + │ │ │ └── args[1]: 24 : i64 │ │ └── body[2] > place: │ │ ├── address: local #4 : ptr │ │ ├── value: local #3 : handle @@ -935,11 +921,11 @@ │ └── body[1] > return: unit : void └── funcs[7] > func: ├── symbol: lists$count<%primitives$String>(&%primitives$List<%primitives$String>) - ├── params[0]: local #1 : i32 + ├── params[0]: local #1 : ptr ├── ret: i64 └── body[0] > return > member: ├── type: i64 - ├── index: 2 + ├── index: 1 └── value > deref: ├── type: handle - └── ptr > resolve: local #1 : i32 + └── ptr: local #1 : ptr diff --git a/tests/codegen/golden/outcomes.cgt b/tests/codegen/golden/outcomes.cgt index e9bdaf3e..e107ca1f 100644 --- a/tests/codegen/golden/outcomes.cgt +++ b/tests/codegen/golden/outcomes.cgt @@ -2,8 +2,7 @@ ├── entry: outcomes$main() ├── layouts[0] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── funcs[0] > func: │ ├── symbol: outcomes$main() │ ├── params: @@ -538,12 +537,12 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle diff --git a/tests/codegen/golden/hosts.cgt b/tests/codegen/golden/owners.cgt similarity index 65% rename from tests/codegen/golden/hosts.cgt rename to tests/codegen/golden/owners.cgt index 4691fb6c..443f2a10 100644 --- a/tests/codegen/golden/hosts.cgt +++ b/tests/codegen/golden/owners.cgt @@ -1,398 +1,464 @@ └── program: - ├── entry: hosts$main() + ├── entry: owners$main() ├── layouts[0] > layout: - │ ├── type: hosts$Account - │ └── positions[0]: @0 (24 bytes) + │ ├── type: ^owners$Account + │ └── positions: ├── layouts[1] > layout: - │ ├── type: hosts$Slot - │ ├── positions[0]: @0 (40 bytes) - │ └── positions[1]: @16 (24 bytes) when [8]=1 + │ ├── type: owners$Account + │ └── positions: ├── layouts[2] > layout: + │ ├── type: owners$Slot + │ └── positions: + ├── layouts[3] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── funcs[0] > func: - │ ├── symbol: hosts$main() + │ ├── symbol: owners$main() │ ├── params: │ ├── ret: void │ └── body[0] > scope: │ ├── id: %2 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #1 │ │ ├── scope: %2 │ │ ├── value > call: - │ │ │ ├── type: {i32, i64, i64} - │ │ │ ├── fn: hosts$opened(%primitives$Int, %primitives$Int) + │ │ │ ├── type: {i64, i64} + │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) │ │ │ ├── args[0]: 1 : i64 │ │ │ └── args[1]: 5 : i64 - │ │ └── layout: hosts$Account + │ │ └── layout: ^owners$Account │ ├── body[1] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$deposit(this hosts$Account, %primitives$Int) + │ │ ├── fn: owners$deposit(this owners$Account, %primitives$Int) │ │ ├── args[0]: address of #1 : ptr │ │ └── args[1]: 10 : i64 │ ├── body[2] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > call: │ │ │ ├── type: i64 - │ │ │ ├── fn: hosts$funds(this hosts$Account) + │ │ │ ├── fn: owners$funds(this owners$Account) │ │ │ └── args[0]: address of #1 : ptr │ │ └── right: 15 : i64 - │ ├── body[3] > host: + │ ├── body[3] > hold: │ │ ├── local: #3 │ │ ├── scope: %2 │ │ ├── value > take: - │ │ │ ├── type: {i32, i64, i64} + │ │ │ ├── type: {i64, i64} │ │ │ ├── address: address of #1 : ptr - │ │ │ └── layout: hosts$Account - │ │ └── layout: hosts$Account + │ │ │ └── layout: owners$Account + │ │ └── layout: owners$Account │ ├── body[4] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64, i64} - │ │ │ ├── path: 2 + │ │ │ ├── within: {i64, i64} + │ │ │ ├── path: 1 │ │ │ └── base: address of #3 : ptr │ │ └── right: 15 : i64 │ ├── body[5] > overwrite: │ │ ├── address: address of #1 : ptr │ │ ├── value > call: - │ │ │ ├── type: {i32, i64, i64} - │ │ │ ├── fn: hosts$Account(%primitives$Int) + │ │ │ ├── type: {i64, i64} + │ │ │ ├── fn: owners$Account(%primitives$Int) │ │ │ └── args[0]: 2 : i64 - │ │ └── layout: hosts$Account + │ │ └── layout: owners$Account │ ├── body[6] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64, i64} - │ │ │ ├── path: 1 + │ │ │ ├── within: {i64, i64} + │ │ │ ├── path: 0 │ │ │ └── base: address of #1 : ptr │ │ └── right: 2 : i64 │ ├── body[7] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > call: │ │ │ ├── type: i64 - │ │ │ ├── fn: hosts$closed(hosts$Account) + │ │ │ ├── fn: owners$balanceOf(owners$Account) │ │ │ └── args[0]: address of #3 : ptr │ │ └── right: 15 : i64 - │ ├── body[8] > host: + │ ├── body[8] > eval > call: + │ │ ├── type: void + │ │ ├── fn: owners$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > call: + │ │ │ ├── type: i64 + │ │ │ ├── fn: owners$balanceOf(owners$Account) + │ │ │ └── args[0]: address of #1 : ptr + │ │ └── right: 0 : i64 + │ ├── body[9] > eval > call: + │ │ ├── type: void + │ │ ├── fn: owners$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > call: + │ │ │ ├── type: i64 + │ │ │ ├── fn: owners$closed(^owners$Account) + │ │ │ └── args[0] > take: + │ │ │ ├── type: {i64, i64} + │ │ │ ├── address: address of #1 : ptr + │ │ │ └── layout: ^owners$Account + │ │ └── right: 0 : i64 + │ ├── body[10] > hold: │ │ ├── local: #4 │ │ ├── scope: %2 - │ │ ├── value > record: - │ │ │ ├── type: {i32, } - │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ └── members[1] > member 1 > case: - │ │ │ ├── type: - │ │ │ ├── index: 1 - │ │ │ └── payload > call: - │ │ │ ├── type: {i32, i64, i64} - │ │ │ ├── fn: hosts$Account(%primitives$Int) - │ │ │ └── args[0]: 3 : i64 - │ │ └── layout: hosts$Slot - │ ├── body[9] > eval > call: + │ │ ├── value > call: + │ │ │ ├── type: {i64, i64} + │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) + │ │ │ ├── args[0]: 3 : i64 + │ │ │ └── args[1]: 7 : i64 + │ │ └── layout: ^owners$Account + │ ├── body[11] > overwrite: + │ │ ├── address: address of #4 : ptr + │ │ ├── value > call: + │ │ │ ├── type: {i64, i64} + │ │ │ ├── fn: owners$relayed(^owners$Account) + │ │ │ └── args[0] > take: + │ │ │ ├── type: {i64, i64} + │ │ │ ├── address: address of #4 : ptr + │ │ │ └── layout: ^owners$Account + │ │ └── layout: owners$Account + │ ├── body[12] > eval > call: + │ │ ├── type: void + │ │ ├── fn: owners$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64, i64} + │ │ │ ├── path: 1 + │ │ │ └── base: address of #4 : ptr + │ │ └── right: 8 : i64 + │ ├── body[13] > eval > call: + │ │ ├── type: void + │ │ ├── fn: owners$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > call: + │ │ │ ├── type: i64 + │ │ │ ├── fn: owners$closed(^owners$Account) + │ │ │ └── args[0] > call: + │ │ │ ├── type: {i64, i64} + │ │ │ ├── fn: owners$Account(%primitives$Int) + │ │ │ └── args[0]: 4 : i64 + │ │ └── right: 0 : i64 + │ ├── body[14] > hold: + │ │ ├── local: #5 + │ │ ├── scope: %2 + │ │ ├── value > case: + │ │ │ ├── type: + │ │ │ ├── index: 1 + │ │ │ └── payload > call: + │ │ │ ├── type: {i64, i64} + │ │ │ ├── fn: owners$Account(%primitives$Int) + │ │ │ └── args[0]: 3 : i64 + │ │ └── layout: owners$Slot + │ ├── body[15] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > expand: │ │ ├── type: i1 - │ │ ├── label: @5 - │ │ ├── result: #6 + │ │ ├── label: @6 + │ │ ├── result: #7 │ │ ├── body[0] > let: - │ │ │ ├── local: #7 - │ │ │ └── value: address of #4 : ptr + │ │ │ ├── local: #8 + │ │ │ └── value: address of #5 : ptr │ │ └── body[1] > switch: - │ │ ├── value > member: - │ │ │ ├── type: - │ │ │ ├── index: 1 - │ │ │ └── value > deref: - │ │ │ ├── type: {i32, } - │ │ │ └── ptr: local #7 : ptr + │ │ ├── value > deref: + │ │ │ ├── type: + │ │ │ └── ptr: local #8 : ptr │ │ ├── cases[0] > case 0[0] > let: - │ │ │ ├── local: #8 + │ │ │ ├── local: #9 │ │ │ └── value > offset: - │ │ │ ├── within: {i32, } - │ │ │ ├── path: 1.0 - │ │ │ └── base: local #7 : ptr + │ │ │ ├── within: + │ │ │ ├── path: 0 + │ │ │ └── base: local #8 : ptr │ │ ├── cases[0] > case 0[1] > assign: - │ │ │ ├── place: #6 + │ │ │ ├── place: #7 │ │ │ └── value: false : i1 - │ │ ├── cases[0] > case 0[2]: leave @5 + │ │ ├── cases[0] > case 0[2]: leave @6 │ │ ├── cases[1] > case 1[0] > let: - │ │ │ ├── local: #9 + │ │ │ ├── local: #10 │ │ │ └── value > offset: - │ │ │ ├── within: {i32, } - │ │ │ ├── path: 1.1 - │ │ │ └── base: local #7 : ptr + │ │ │ ├── within: + │ │ │ ├── path: 1 + │ │ │ └── base: local #8 : ptr │ │ ├── cases[1] > case 1[1] > assign: - │ │ │ ├── place: #6 + │ │ │ ├── place: #7 │ │ │ └── value > binary: │ │ │ ├── type: i1 │ │ │ ├── op: == │ │ │ ├── left > snapshot: │ │ │ │ ├── type: i64 │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, i64, i64} - │ │ │ │ ├── path: 1 - │ │ │ │ └── base: local #9 : ptr + │ │ │ │ ├── within: {i64, i64} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base: local #10 : ptr │ │ │ └── right: 3 : i64 - │ │ └── cases[1] > case 1[2]: leave @5 - │ ├── body[10] > eval > call: + │ │ └── cases[1] > case 1[2]: leave @6 + │ ├── body[16] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > call: │ │ │ ├── type: i64 - │ │ │ ├── fn: hosts$richest(%primitives$Int) + │ │ │ ├── fn: owners$richest(%primitives$Int) │ │ │ └── args[0]: 9 : i64 │ │ └── right: 4 : i64 - │ ├── body[11] > eval > call: + │ ├── body[17] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @10 - │ │ │ ├── result: #11 + │ │ │ ├── label: @11 + │ │ │ ├── result: #12 │ │ │ ├── body[0] > let: - │ │ │ │ ├── local: #12 + │ │ │ │ ├── local: #13 │ │ │ │ └── value > call: │ │ │ │ ├── type: - │ │ │ │ ├── fn: hosts$overdrawn(%primitives$Int) + │ │ │ │ ├── fn: owners$overdrawn(%primitives$Int) │ │ │ │ └── args[0]: 1 : i64 │ │ │ └── body[1] > switch: - │ │ │ ├── value: local #12 : + │ │ │ ├── value: local #13 : │ │ │ ├── cases[0] > case 0[0] > assign: - │ │ │ │ ├── place: #11 + │ │ │ │ ├── place: #12 │ │ │ │ └── value > payload: │ │ │ │ ├── type: i64 │ │ │ │ ├── index: 0 - │ │ │ │ └── value: local #12 : - │ │ │ ├── cases[0] > case 0[1]: leave @10 + │ │ │ │ └── value: local #13 : + │ │ │ ├── cases[0] > case 0[1]: leave @11 │ │ │ ├── cases[1] > case 1[0] > eval > payload: │ │ │ │ ├── type: i64 │ │ │ │ ├── index: 1 - │ │ │ │ └── value: local #12 : + │ │ │ │ └── value: local #13 : │ │ │ ├── cases[1] > case 1[1] > assign: - │ │ │ │ ├── place: #11 + │ │ │ │ ├── place: #12 │ │ │ │ └── value: 0 : i64 - │ │ │ └── cases[1] > case 1[2]: leave @10 + │ │ │ └── cases[1] > case 1[2]: leave @11 │ │ └── right: 5 : i64 - │ ├── body[12] > eval > call: + │ ├── body[18] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @13 - │ │ │ ├── result: #14 + │ │ │ ├── label: @14 + │ │ │ ├── result: #15 │ │ │ ├── body[0] > let: - │ │ │ │ ├── local: #15 + │ │ │ │ ├── local: #16 │ │ │ │ └── value > call: │ │ │ │ ├── type: - │ │ │ │ ├── fn: hosts$overdrawn(%primitives$Int) + │ │ │ │ ├── fn: owners$overdrawn(%primitives$Int) │ │ │ │ └── args[0]: 7 : i64 │ │ │ └── body[1] > switch: - │ │ │ ├── value: local #15 : + │ │ │ ├── value: local #16 : │ │ │ ├── cases[0] > case 0[0] > assign: - │ │ │ │ ├── place: #14 + │ │ │ │ ├── place: #15 │ │ │ │ └── value > payload: │ │ │ │ ├── type: i64 │ │ │ │ ├── index: 0 - │ │ │ │ └── value: local #15 : - │ │ │ ├── cases[0] > case 0[1]: leave @13 + │ │ │ │ └── value: local #16 : + │ │ │ ├── cases[0] > case 0[1]: leave @14 │ │ │ ├── cases[1] > case 1[0] > let: - │ │ │ │ ├── local: #16 + │ │ │ │ ├── local: #17 │ │ │ │ └── value > payload: │ │ │ │ ├── type: i64 │ │ │ │ ├── index: 1 - │ │ │ │ └── value: local #15 : + │ │ │ │ └── value: local #16 : │ │ │ ├── cases[1] > case 1[1] > assign: - │ │ │ │ ├── place: #14 + │ │ │ │ ├── place: #15 │ │ │ │ └── value > binary: │ │ │ │ ├── type: i64 │ │ │ │ ├── op: + - │ │ │ │ ├── left: local #16 : i64 + │ │ │ │ ├── left: local #17 : i64 │ │ │ │ └── right: 1 : i64 - │ │ │ └── cases[1] > case 1[2]: leave @13 + │ │ │ └── cases[1] > case 1[2]: leave @14 │ │ └── right: 8 : i64 - │ ├── body[13] > eval > call: + │ ├── body[19] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > call: │ │ │ ├── type: i64 - │ │ │ ├── fn: hosts$total(%primitives$Int) + │ │ │ ├── fn: owners$total(%primitives$Int) │ │ │ └── args[0]: 3 : i64 │ │ └── right: 12 : i64 - │ ├── body[14] > eval > call: + │ ├── body[20] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @17 - │ │ │ ├── result: #18 + │ │ │ ├── label: @18 + │ │ │ ├── result: #19 │ │ │ ├── body[0] > let: - │ │ │ │ ├── local: #19 + │ │ │ │ ├── local: #20 │ │ │ │ └── value > call: - │ │ │ │ ├── type: - │ │ │ │ ├── fn: hosts$refused(%primitives$Int) + │ │ │ │ ├── type: + │ │ │ │ ├── fn: owners$refused(%primitives$Int) │ │ │ │ └── args[0]: 0 : i64 │ │ │ └── body[1] > switch: - │ │ │ ├── value: local #19 : + │ │ │ ├── value: local #20 : │ │ │ ├── cases[0] > case 0[0] > assign: - │ │ │ │ ├── place: #18 + │ │ │ │ ├── place: #19 │ │ │ │ └── value > payload: │ │ │ │ ├── type: i64 │ │ │ │ ├── index: 0 - │ │ │ │ └── value: local #19 : - │ │ │ ├── cases[0] > case 0[1]: leave @17 + │ │ │ │ └── value: local #20 : + │ │ │ ├── cases[0] > case 0[1]: leave @18 │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ ├── id: %21 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #20 - │ │ │ │ ├── scope: %21 + │ │ │ ├── id: %22 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #21 + │ │ │ │ ├── scope: %22 │ │ │ │ ├── value > payload: - │ │ │ │ │ ├── type: {i32, i64, i64} + │ │ │ │ │ ├── type: {i64, i64} │ │ │ │ │ ├── index: 1 - │ │ │ │ │ └── value: local #19 : - │ │ │ │ └── layout: hosts$Account + │ │ │ │ │ └── value: local #20 : + │ │ │ │ └── layout: ^owners$Account │ │ │ ├── body[1] > assign: - │ │ │ │ ├── place: #18 + │ │ │ │ ├── place: #19 │ │ │ │ └── value > snapshot: │ │ │ │ ├── type: i64 │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, i64, i64} - │ │ │ │ ├── path: 2 - │ │ │ │ └── base: address of #20 : ptr - │ │ │ └── body[2]: leave @17 + │ │ │ │ ├── within: {i64, i64} + │ │ │ │ ├── path: 1 + │ │ │ │ └── base: address of #21 : ptr + │ │ │ └── body[2]: leave @18 │ │ └── right: 70 : i64 - │ ├── body[15] > eval > call: + │ ├── body[21] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @22 - │ │ │ ├── result: #23 + │ │ │ ├── label: @23 + │ │ │ ├── result: #24 │ │ │ ├── body[0] > let: - │ │ │ │ ├── local: #24 + │ │ │ │ ├── local: #25 │ │ │ │ └── value > call: - │ │ │ │ ├── type: - │ │ │ │ ├── fn: hosts$refused(%primitives$Int) + │ │ │ │ ├── type: + │ │ │ │ ├── fn: owners$refused(%primitives$Int) │ │ │ │ └── args[0]: 4 : i64 │ │ │ └── body[1] > switch: - │ │ │ ├── value: local #24 : + │ │ │ ├── value: local #25 : │ │ │ ├── cases[0] > case 0[0] > assign: - │ │ │ │ ├── place: #23 + │ │ │ │ ├── place: #24 │ │ │ │ └── value > payload: │ │ │ │ ├── type: i64 │ │ │ │ ├── index: 0 - │ │ │ │ └── value: local #24 : - │ │ │ ├── cases[0] > case 0[1]: leave @22 + │ │ │ │ └── value: local #25 : + │ │ │ ├── cases[0] > case 0[1]: leave @23 │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ ├── id: %26 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #25 - │ │ │ │ ├── scope: %26 + │ │ │ ├── id: %27 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #26 + │ │ │ │ ├── scope: %27 │ │ │ │ ├── value > payload: - │ │ │ │ │ ├── type: {i32, i64, i64} + │ │ │ │ │ ├── type: {i64, i64} │ │ │ │ │ ├── index: 1 - │ │ │ │ │ └── value: local #24 : - │ │ │ │ └── layout: hosts$Account + │ │ │ │ │ └── value: local #25 : + │ │ │ │ └── layout: ^owners$Account │ │ │ ├── body[1] > assign: - │ │ │ │ ├── place: #23 + │ │ │ │ ├── place: #24 │ │ │ │ └── value > snapshot: │ │ │ │ ├── type: i64 │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, i64, i64} - │ │ │ │ ├── path: 2 - │ │ │ │ └── base: address of #25 : ptr - │ │ │ └── body[2]: leave @22 + │ │ │ │ ├── within: {i64, i64} + │ │ │ │ ├── path: 1 + │ │ │ │ └── base: address of #26 : ptr + │ │ │ └── body[2]: leave @23 │ │ └── right: 4 : i64 - │ ├── body[16] > eval > call: + │ ├── body[22] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > call: │ │ │ ├── type: i64 - │ │ │ ├── fn: hosts$chosen(hosts$Choice) + │ │ │ ├── fn: owners$chosen(owners$Choice) │ │ │ └── args[0] > case: │ │ │ ├── type: │ │ │ ├── index: 0 │ │ │ └── payload: 5 : i64 │ │ └── right: 5 : i64 - │ ├── body[17] > eval > call: + │ ├── body[23] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > call: │ │ │ ├── type: i64 - │ │ │ ├── fn: hosts$chosen(hosts$Choice) + │ │ │ ├── fn: owners$chosen(owners$Choice) │ │ │ └── args[0] > case: │ │ │ ├── type: │ │ │ ├── index: 0 │ │ │ └── payload: 0 : i64 │ │ └── right: 0 : i64 - │ ├── body[18] > eval > call: + │ ├── body[24] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == │ │ ├── left > call: │ │ │ ├── type: i64 - │ │ │ ├── fn: hosts$chosen(hosts$Choice) + │ │ │ ├── fn: owners$chosen(owners$Choice) │ │ │ └── args[0] > case: │ │ │ ├── type: │ │ │ ├── index: 1 │ │ │ └── payload: unit : void │ │ └── right: 600 : i64 - │ ├── body[19] > let: - │ │ ├── local: #27 - │ │ └── value: 0 : i64 - │ ├── body[20] > let: + │ ├── body[25] > let: │ │ ├── local: #28 + │ │ └── value: 0 : i64 + │ ├── body[26] > let: + │ │ ├── local: #29 │ │ └── value: 1 : i64 - │ ├── body[21] > eval > expand: + │ ├── body[27] > eval > expand: │ │ ├── type: void - │ │ ├── label: @30 + │ │ ├── label: @31 │ │ ├── body[0] > let: - │ │ │ ├── local: #29 + │ │ │ ├── local: #30 │ │ │ └── value: 100000 : i64 │ │ ├── body[1] > repeat: │ │ │ ├── count > binary: @@ -401,129 +467,143 @@ │ │ │ │ ├── left > binary: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #29 : i64 + │ │ │ │ │ ├── left: local #30 : i64 │ │ │ │ │ └── right > flip: │ │ │ │ │ ├── type: i64 - │ │ │ │ │ └── value: local #28 : i64 + │ │ │ │ │ └── value: local #29 : i64 │ │ │ │ └── right: 1 : i64 │ │ │ ├── body[0] > if: │ │ │ │ ├── cond: true : i1 │ │ │ │ └── body[0] > scope: - │ │ │ │ ├── id: %34 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #33 - │ │ │ │ │ ├── scope: %34 + │ │ │ │ ├── id: %35 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #34 + │ │ │ │ │ ├── scope: %35 │ │ │ │ │ ├── value > call: - │ │ │ │ │ │ ├── type: {i32, i64, i64} - │ │ │ │ │ │ ├── fn: hosts$opened(%primitives$Int, %primitives$Int) - │ │ │ │ │ │ ├── args[0]: local #28 : i64 + │ │ │ │ │ │ ├── type: {i64, i64} + │ │ │ │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) + │ │ │ │ │ │ ├── args[0]: local #29 : i64 │ │ │ │ │ │ └── args[1]: 1 : i64 - │ │ │ │ │ └── layout: hosts$Account - │ │ │ │ ├── body[1] > host: - │ │ │ │ │ ├── local: #35 - │ │ │ │ │ ├── scope: %34 + │ │ │ │ │ └── layout: ^owners$Account + │ │ │ │ ├── body[1] > hold: + │ │ │ │ │ ├── local: #36 + │ │ │ │ │ ├── scope: %35 │ │ │ │ │ ├── value > take: - │ │ │ │ │ │ ├── type: {i32, i64, i64} - │ │ │ │ │ │ ├── address: address of #33 : ptr - │ │ │ │ │ │ └── layout: hosts$Account - │ │ │ │ │ └── layout: hosts$Account - │ │ │ │ └── body[2] > assign: - │ │ │ │ ├── place: #27 + │ │ │ │ │ │ ├── type: {i64, i64} + │ │ │ │ │ │ ├── address: address of #34 : ptr + │ │ │ │ │ │ └── layout: owners$Account + │ │ │ │ │ └── layout: owners$Account + │ │ │ │ ├── body[2] > assign: + │ │ │ │ │ ├── place: #28 + │ │ │ │ │ └── value > binary: + │ │ │ │ │ ├── type: i64 + │ │ │ │ │ ├── op: + + │ │ │ │ │ ├── left: local #28 : i64 + │ │ │ │ │ └── right > snapshot: + │ │ │ │ │ ├── type: i64 + │ │ │ │ │ └── ptr > offset: + │ │ │ │ │ ├── within: {i64, i64} + │ │ │ │ │ ├── path: 1 + │ │ │ │ │ └── base: address of #36 : ptr + │ │ │ │ └── body[3] > assign: + │ │ │ │ ├── place: #28 │ │ │ │ └── value > binary: │ │ │ │ ├── type: i64 │ │ │ │ ├── op: + - │ │ │ │ ├── left: local #27 : i64 - │ │ │ │ └── right > snapshot: + │ │ │ │ ├── left: local #28 : i64 + │ │ │ │ └── right > call: │ │ │ │ ├── type: i64 - │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, i64, i64} - │ │ │ │ ├── path: 2 - │ │ │ │ └── base: address of #35 : ptr + │ │ │ │ ├── fn: owners$closed(^owners$Account) + │ │ │ │ └── args[0] > call: + │ │ │ │ ├── type: {i64, i64} + │ │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) + │ │ │ │ ├── args[0]: local #29 : i64 + │ │ │ │ └── args[1]: 0 : i64 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #28 + │ │ │ ├── place: #29 │ │ │ └── value > binary: │ │ │ ├── type: i64 │ │ │ ├── op: + - │ │ │ ├── left: local #28 : i64 + │ │ │ ├── left: local #29 : i64 │ │ │ └── right: 1 : i64 │ │ ├── body[2] > eval: unit : void - │ │ └── body[3]: leave @30 - │ ├── body[22] > eval > call: + │ │ └── body[3]: leave @31 + │ ├── body[28] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$check(%primitives$Bool) + │ │ ├── fn: owners$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == - │ │ ├── left: local #27 : i64 + │ │ ├── left: local #28 : i64 │ │ └── right: 100000 : i64 - │ └── body[23] > return: unit : void + │ └── body[29] > return: unit : void ├── funcs[1] > func: - │ ├── symbol: hosts$opened(%primitives$Int, %primitives$Int) + │ ├── symbol: owners$opened(%primitives$Int, %primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── params[1]: local #2 : i64 - │ ├── ret: {i32, i64, i64} + │ ├── ret: {i64, i64} │ └── body[0] > scope: │ ├── id: %4 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #3 │ │ ├── scope: %4 │ │ ├── value > call: - │ │ │ ├── type: {i32, i64, i64} - │ │ │ ├── fn: hosts$Account(%primitives$Int) + │ │ │ ├── type: {i64, i64} + │ │ │ ├── fn: owners$Account(%primitives$Int) │ │ │ └── args[0]: local #1 : i64 - │ │ └── layout: hosts$Account + │ │ └── layout: ^owners$Account │ ├── body[1] > eval > call: │ │ ├── type: void - │ │ ├── fn: hosts$deposit(this hosts$Account, %primitives$Int) + │ │ ├── fn: owners$deposit(this owners$Account, %primitives$Int) │ │ ├── args[0]: address of #3 : ptr │ │ └── args[1]: local #2 : i64 │ └── body[2] > return > escape: │ ├── exit: function │ ├── value > take: - │ │ ├── type: {i32, i64, i64} + │ │ ├── type: {i64, i64} │ │ ├── address: address of #3 : ptr - │ │ └── layout: hosts$Account - │ └── layout: hosts$Account + │ │ └── layout: ^owners$Account + │ └── layout: ^owners$Account ├── funcs[2] > func: - │ ├── symbol: hosts$deposit(this hosts$Account, %primitives$Int) + │ ├── symbol: owners$deposit(this owners$Account, %primitives$Int) │ ├── params[0]: local #1 : ptr │ ├── params[1]: local #2 : i64 │ ├── ret: void │ ├── body[0] > assign: - │ │ ├── place: *#1.2 + │ │ ├── place: *#1.1 │ │ └── value > binary: │ │ ├── type: i64 │ │ ├── op: + │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64, i64} - │ │ │ ├── path: 2 + │ │ │ ├── within: {i64, i64} + │ │ │ ├── path: 1 │ │ │ └── base: local #1 : ptr │ │ └── right: local #2 : i64 │ └── body[1] > return: unit : void ├── funcs[3] > func: - │ ├── symbol: hosts$funds(this hosts$Account) + │ ├── symbol: owners$funds(this owners$Account) │ ├── params[0]: local #1 : ptr │ ├── ret: i64 │ └── body[0] > return > snapshot: │ ├── type: i64 │ └── ptr > offset: - │ ├── within: {i32, i64, i64} - │ ├── path: 2 + │ ├── within: {i64, i64} + │ ├── path: 1 │ └── base: local #1 : ptr ├── funcs[4] > func: - │ ├── symbol: hosts$check(%primitives$Bool) + │ ├── symbol: owners$check(%primitives$Bool) │ ├── params[0]: local #1 : i1 │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -544,29 +624,76 @@ │ │ └── args[0]: local #4 : handle │ └── body[4] > return: unit : void ├── funcs[5] > func: - │ ├── symbol: hosts$Account(%primitives$Int) + │ ├── symbol: owners$Account(%primitives$Int) │ ├── params[0]: local #1 : i64 - │ ├── ret: {i32, i64, i64} + │ ├── ret: {i64, i64} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, i64, i64} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ ├── members[1] > member 1: local #1 : i64 - │ │ └── members[2] > member 2: 0 : i64 - │ └── layout: hosts$Account + │ │ ├── type: {i64, i64} + │ │ ├── members[0] > member 0: local #1 : i64 + │ │ └── members[1] > member 1: 0 : i64 + │ └── layout: owners$Account ├── funcs[6] > func: - │ ├── symbol: hosts$closed(hosts$Account) + │ ├── symbol: owners$balanceOf(owners$Account) │ ├── params[0]: local #1 : ptr │ ├── ret: i64 │ └── body[0] > return > snapshot: │ ├── type: i64 │ └── ptr > offset: - │ ├── within: {i32, i64, i64} - │ ├── path: 2 + │ ├── within: {i64, i64} + │ ├── path: 1 │ └── base: local #1 : ptr ├── funcs[7] > func: - │ ├── symbol: hosts$richest(%primitives$Int) + │ ├── symbol: owners$closed(^owners$Account) + │ ├── params[0]: local #1 : {i64, i64} + │ ├── ret: i64 + │ └── body[0] > scope: + │ ├── id: %3 + │ ├── body[0] > hold: + │ │ ├── local: #2 + │ │ ├── scope: %3 + │ │ ├── value: local #1 : {i64, i64} + │ │ └── layout: ^owners$Account + │ └── body[1] > return > snapshot: + │ ├── type: i64 + │ └── ptr > offset: + │ ├── within: {i64, i64} + │ ├── path: 1 + │ └── base: address of #2 : ptr + ├── funcs[8] > func: + │ ├── symbol: owners$relayed(^owners$Account) + │ ├── params[0]: local #1 : {i64, i64} + │ ├── ret: {i64, i64} + │ └── body[0] > scope: + │ ├── id: %3 + │ ├── body[0] > hold: + │ │ ├── local: #2 + │ │ ├── scope: %3 + │ │ ├── value: local #1 : {i64, i64} + │ │ └── layout: ^owners$Account + │ ├── body[1] > hold: + │ │ ├── local: #4 + │ │ ├── scope: %3 + │ │ ├── value > take: + │ │ │ ├── type: {i64, i64} + │ │ │ ├── address: address of #2 : ptr + │ │ │ └── layout: ^owners$Account + │ │ └── layout: ^owners$Account + │ ├── body[2] > eval > call: + │ │ ├── type: void + │ │ ├── fn: owners$deposit(this owners$Account, %primitives$Int) + │ │ ├── args[0]: address of #4 : ptr + │ │ └── args[1]: 1 : i64 + │ └── body[3] > return > escape: + │ ├── exit: function + │ ├── value > take: + │ │ ├── type: {i64, i64} + │ │ ├── address: address of #4 : ptr + │ │ └── layout: ^owners$Account + │ └── layout: ^owners$Account + ├── funcs[9] > func: + │ ├── symbol: owners$richest(%primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── ret: i64 │ ├── body[0] > let: @@ -594,19 +721,19 @@ │ │ │ │ ├── cond: true : i1 │ │ │ │ └── body[0] > scope: │ │ │ │ ├── id: %8 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #7 │ │ │ │ │ ├── scope: %8 │ │ │ │ │ ├── value > call: - │ │ │ │ │ │ ├── type: {i32, i64, i64} - │ │ │ │ │ │ ├── fn: hosts$opened(%primitives$Int, %primitives$Int) + │ │ │ │ │ │ ├── type: {i64, i64} + │ │ │ │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) │ │ │ │ │ │ ├── args[0]: local #2 : i64 │ │ │ │ │ │ └── args[1] > binary: │ │ │ │ │ │ ├── type: i64 │ │ │ │ │ │ ├── op: * │ │ │ │ │ │ ├── left: local #2 : i64 │ │ │ │ │ │ └── right: 10 : i64 - │ │ │ │ │ └── layout: hosts$Account + │ │ │ │ │ └── layout: owners$Account │ │ │ │ └── body[1] > eval > expand: │ │ │ │ ├── type: i1 │ │ │ │ ├── label: @10 @@ -620,16 +747,16 @@ │ │ │ │ │ └── right > snapshot: │ │ │ │ │ ├── type: i64 │ │ │ │ │ └── ptr > offset: - │ │ │ │ │ ├── within: {i32, i64, i64} - │ │ │ │ │ ├── path: 2 + │ │ │ │ │ ├── within: {i64, i64} + │ │ │ │ │ ├── path: 1 │ │ │ │ │ └── base: address of #7 : ptr │ │ │ │ ├── body[1] > if: │ │ │ │ │ ├── cond: local #9 : i1 │ │ │ │ │ └── body[0] > return > snapshot: │ │ │ │ │ ├── type: i64 │ │ │ │ │ └── ptr > offset: - │ │ │ │ │ ├── within: {i32, i64, i64} - │ │ │ │ │ ├── path: 1 + │ │ │ │ │ ├── within: {i64, i64} + │ │ │ │ │ ├── path: 0 │ │ │ │ │ └── base: address of #7 : ptr │ │ │ │ ├── body[2] > assign: │ │ │ │ │ ├── place: #11 @@ -645,21 +772,21 @@ │ │ ├── body[2] > eval: unit : void │ │ └── body[3]: leave @4 │ └── body[2] > return: 0 : i64 - ├── funcs[8] > func: - │ ├── symbol: hosts$overdrawn(%primitives$Int) + ├── funcs[10] > func: + │ ├── symbol: owners$overdrawn(%primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── ret: │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value > call: - │ │ │ ├── type: {i32, i64, i64} - │ │ │ ├── fn: hosts$opened(%primitives$Int, %primitives$Int) + │ │ │ ├── type: {i64, i64} + │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) │ │ │ ├── args[0]: 9 : i64 │ │ │ └── args[1]: 5 : i64 - │ │ └── layout: hosts$Account + │ │ └── layout: owners$Account │ ├── body[1] > eval > expand: │ │ ├── type: i1 │ │ ├── label: @5 @@ -672,31 +799,31 @@ │ │ │ ├── left > snapshot: │ │ │ │ ├── type: i64 │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, i64, i64} - │ │ │ │ ├── path: 2 + │ │ │ │ ├── within: {i64, i64} + │ │ │ │ ├── path: 1 │ │ │ │ └── base: address of #2 : ptr │ │ │ └── right: local #1 : i64 │ │ ├── body[1] > if: │ │ │ ├── cond: local #4 : i1 │ │ │ └── body[0] > scope: │ │ │ ├── id: %9 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #8 │ │ │ │ ├── scope: %9 │ │ │ │ ├── value > call: - │ │ │ │ │ ├── type: {i32, i64, i64} - │ │ │ │ │ ├── fn: hosts$opened(%primitives$Int, %primitives$Int) + │ │ │ │ │ ├── type: {i64, i64} + │ │ │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) │ │ │ │ │ ├── args[0]: 8 : i64 │ │ │ │ │ └── args[1]: local #1 : i64 - │ │ │ │ └── layout: hosts$Account + │ │ │ │ └── layout: owners$Account │ │ │ └── body[1] > return > case: │ │ │ ├── type: │ │ │ ├── index: 1 │ │ │ └── payload > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64, i64} - │ │ │ ├── path: 2 + │ │ │ ├── within: {i64, i64} + │ │ │ ├── path: 1 │ │ │ └── base: address of #8 : ptr │ │ ├── body[2] > assign: │ │ │ ├── place: #6 @@ -708,11 +835,11 @@ │ └── payload > snapshot: │ ├── type: i64 │ └── ptr > offset: - │ ├── within: {i32, i64, i64} - │ ├── path: 2 + │ ├── within: {i64, i64} + │ ├── path: 1 │ └── base: address of #2 : ptr - ├── funcs[9] > func: - │ ├── symbol: hosts$total(%primitives$Int) + ├── funcs[11] > func: + │ ├── symbol: owners$total(%primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── ret: i64 │ ├── body[0] > let: @@ -746,15 +873,15 @@ │ │ │ │ ├── label: @7 │ │ │ │ └── body[0] > scope: │ │ │ │ ├── id: %9 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #8 │ │ │ │ │ ├── scope: %9 │ │ │ │ │ ├── value > call: - │ │ │ │ │ │ ├── type: {i32, i64, i64} - │ │ │ │ │ │ ├── fn: hosts$opened(%primitives$Int, %primitives$Int) + │ │ │ │ │ │ ├── type: {i64, i64} + │ │ │ │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) │ │ │ │ │ │ ├── args[0]: local #3 : i64 │ │ │ │ │ │ └── args[1]: local #3 : i64 - │ │ │ │ │ └── layout: hosts$Account + │ │ │ │ │ └── layout: owners$Account │ │ │ │ ├── body[1] > eval > expand: │ │ │ │ │ ├── type: void │ │ │ │ │ ├── label: @10 @@ -762,7 +889,7 @@ │ │ │ │ │ │ ├── local: #11 │ │ │ │ │ │ └── value > call: │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ ├── fn: hosts$guard(%primitives$Bool) + │ │ │ │ │ │ ├── fn: owners$guard(%primitives$Bool) │ │ │ │ │ │ └── args[0] > binary: │ │ │ │ │ │ ├── type: i1 │ │ │ │ │ │ ├── op: == @@ -781,8 +908,8 @@ │ │ │ │ └── right > snapshot: │ │ │ │ ├── type: i64 │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, i64, i64} - │ │ │ │ ├── path: 2 + │ │ │ │ ├── within: {i64, i64} + │ │ │ │ ├── path: 1 │ │ │ │ └── base: address of #8 : ptr │ │ │ └── body[1] > assign: │ │ │ ├── place: #3 @@ -794,10 +921,10 @@ │ │ ├── body[2] > eval: unit : void │ │ └── body[3]: leave @5 │ └── body[3] > return: local #2 : i64 - ├── funcs[10] > func: - │ ├── symbol: hosts$refused(%primitives$Int) + ├── funcs[12] > func: + │ ├── symbol: owners$refused(%primitives$Int) │ ├── params[0]: local #1 : i64 - │ ├── ret: + │ ├── ret: │ ├── body[0] > eval > expand: │ │ ├── type: i1 │ │ ├── label: @3 @@ -812,26 +939,26 @@ │ │ ├── body[1] > if: │ │ │ ├── cond: local #2 : i1 │ │ │ └── body[0] > return > case: - │ │ │ ├── type: + │ │ │ ├── type: │ │ │ ├── index: 1 │ │ │ └── payload > escape: │ │ │ ├── exit: function │ │ │ ├── value > call: - │ │ │ │ ├── type: {i32, i64, i64} - │ │ │ │ ├── fn: hosts$opened(%primitives$Int, %primitives$Int) + │ │ │ │ ├── type: {i64, i64} + │ │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) │ │ │ │ ├── args[0]: 7 : i64 │ │ │ │ └── args[1]: 70 : i64 - │ │ │ └── layout: hosts$Account + │ │ │ └── layout: ^owners$Account │ │ ├── body[2] > assign: │ │ │ ├── place: #4 │ │ │ └── value: local #2 : i1 │ │ └── body[3]: leave @3 │ └── body[1] > return > case: - │ ├── type: + │ ├── type: │ ├── index: 0 │ └── payload: local #1 : i64 - ├── funcs[11] > func: - │ ├── symbol: hosts$chosen(hosts$Choice) + ├── funcs[13] > func: + │ ├── symbol: owners$chosen(owners$Choice) │ ├── params[0]: local #1 : │ ├── ret: i64 │ └── body[0] > return > expand: @@ -853,19 +980,19 @@ │ │ └── base: local #4 : ptr │ ├── cases[0] > case 0[1] > scope: │ │ ├── id: %7 - │ │ ├── body[0] > host: + │ │ ├── body[0] > hold: │ │ │ ├── local: #6 │ │ │ ├── scope: %7 │ │ │ ├── value > call: - │ │ │ │ ├── type: {i32, i64, i64} - │ │ │ │ ├── fn: hosts$opened(%primitives$Int, %primitives$Int) + │ │ │ │ ├── type: {i64, i64} + │ │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) │ │ │ │ ├── args[0] > deref: │ │ │ │ │ ├── type: i64 │ │ │ │ │ └── ptr: local #5 : ptr │ │ │ │ └── args[1] > deref: │ │ │ │ ├── type: i64 │ │ │ │ └── ptr: local #5 : ptr - │ │ │ └── layout: hosts$Account + │ │ │ └── layout: ^owners$Account │ │ ├── body[1] > eval > expand: │ │ │ ├── type: i1 │ │ │ ├── label: @9 @@ -878,22 +1005,22 @@ │ │ │ │ ├── left > snapshot: │ │ │ │ │ ├── type: i64 │ │ │ │ │ └── ptr > offset: - │ │ │ │ │ ├── within: {i32, i64, i64} - │ │ │ │ │ ├── path: 2 + │ │ │ │ │ ├── within: {i64, i64} + │ │ │ │ │ ├── path: 1 │ │ │ │ │ └── base: address of #6 : ptr │ │ │ │ └── right: 0 : i64 │ │ │ ├── body[1] > if: │ │ │ │ ├── cond: local #8 : i1 │ │ │ │ └── body[0] > scope: │ │ │ │ ├── id: %13 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #12 │ │ │ │ │ ├── scope: %13 │ │ │ │ │ ├── value > take: - │ │ │ │ │ │ ├── type: {i32, i64, i64} + │ │ │ │ │ │ ├── type: {i64, i64} │ │ │ │ │ │ ├── address: address of #6 : ptr - │ │ │ │ │ │ └── layout: hosts$Account - │ │ │ │ │ └── layout: hosts$Account + │ │ │ │ │ │ └── layout: owners$Account + │ │ │ │ │ └── layout: owners$Account │ │ │ │ ├── body[1] > assign: │ │ │ │ │ ├── place: #3 │ │ │ │ │ └── value > binary: @@ -902,8 +1029,8 @@ │ │ │ │ │ ├── left > snapshot: │ │ │ │ │ │ ├── type: i64 │ │ │ │ │ │ └── ptr > offset: - │ │ │ │ │ │ ├── within: {i32, i64, i64} - │ │ │ │ │ │ ├── path: 1 + │ │ │ │ │ │ ├── within: {i64, i64} + │ │ │ │ │ │ ├── path: 0 │ │ │ │ │ │ └── base: address of #12 : ptr │ │ │ │ │ └── right: 100 : i64 │ │ │ │ └── body[2]: leave @2 @@ -916,8 +1043,8 @@ │ │ │ └── value > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64, i64} - │ │ │ ├── path: 2 + │ │ │ ├── within: {i64, i64} + │ │ │ ├── path: 1 │ │ │ └── base: address of #6 : ptr │ │ └── body[3]: leave @2 │ ├── cases[1] > case 1[0] > let: @@ -928,14 +1055,14 @@ │ │ └── base: local #4 : ptr │ └── cases[1] > case 1[1] > scope: │ ├── id: %16 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #15 │ │ ├── scope: %16 │ │ ├── value > call: - │ │ │ ├── type: {i32, i64, i64} - │ │ │ ├── fn: hosts$Account(%primitives$Int) + │ │ │ ├── type: {i64, i64} + │ │ │ ├── fn: owners$Account(%primitives$Int) │ │ │ └── args[0]: 6 : i64 - │ │ └── layout: hosts$Account + │ │ └── layout: owners$Account │ ├── body[1] > assign: │ │ ├── place: #3 │ │ └── value > binary: @@ -944,13 +1071,13 @@ │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64, i64} - │ │ │ ├── path: 1 + │ │ │ ├── within: {i64, i64} + │ │ │ ├── path: 0 │ │ │ └── base: address of #15 : ptr │ │ └── right: 100 : i64 │ └── body[2]: leave @2 - └── funcs[12] > func: - ├── symbol: hosts$guard(%primitives$Bool) + └── funcs[14] > func: + ├── symbol: owners$guard(%primitives$Bool) ├── params[0]: local #1 : i1 ├── ret: ├── body[0] > if: diff --git a/tests/codegen/golden/guests.out b/tests/codegen/golden/owners.out similarity index 95% rename from tests/codegen/golden/guests.out rename to tests/codegen/golden/owners.out index 1065e38e..61b7996a 100644 --- a/tests/codegen/golden/guests.out +++ b/tests/codegen/golden/owners.out @@ -17,4 +17,3 @@ yes yes yes yes -yes diff --git a/tests/codegen/golden/references.cgt b/tests/codegen/golden/references.cgt index 35d2fd35..2c4b4868 100644 --- a/tests/codegen/golden/references.cgt +++ b/tests/codegen/golden/references.cgt @@ -2,29 +2,28 @@ ├── entry: references$main() ├── layouts[0] > layout: │ ├── type: references$Person - │ └── positions[0]: @0 (16 bytes) + │ └── positions: ├── layouts[1] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 - ├── globals[0]: zane.value.@program$console : i32 + │ └── positions[0]: text @0 + ├── globals[0]: zane.value.@program$console : i64 ├── funcs[0] > func: │ ├── symbol: references$main() │ ├── params: │ ├── ret: void │ └── body[0] > scope: │ ├── id: %2 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #1 │ │ ├── scope: %2 │ │ ├── value > call: - │ │ │ ├── type: {i32, i64} + │ │ │ ├── type: {i64} │ │ │ ├── fn: references$Person(%primitives$Int) │ │ │ └── args[0]: 3 : i64 │ │ └── layout: references$Person │ ├── body[1] > let: │ │ ├── local: #3 - │ │ └── value > terminal > mint: address of #1 : ptr + │ │ └── value: address of #1 : ptr │ ├── body[2] > eval > call: │ │ ├── type: void │ │ ├── fn: references$check(%primitives$Bool) @@ -37,33 +36,25 @@ │ │ │ ├── left > call: │ │ │ │ ├── type: i64 │ │ │ │ ├── fn: references$age(&references$Person) - │ │ │ │ └── args[0] > terminal > mint: address of #1 : ptr + │ │ │ │ └── args[0]: address of #1 : ptr │ │ │ └── right > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #3 : i32 + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #3 : ptr │ │ └── right: 6 : i64 │ ├── body[3] > let: │ │ ├── local: #4 - │ │ └── value > terminal > terminal: local #3 : i32 - │ ├── body[4] > host: - │ │ ├── local: #5 - │ │ ├── scope: %2 + │ │ └── value: local #3 : ptr + │ ├── body[4] > overwrite: + │ │ ├── address: address of #1 : ptr │ │ ├── value > call: - │ │ │ ├── type: {i32, i64} + │ │ │ ├── type: {i64} │ │ │ ├── fn: references$Person(%primitives$Int) │ │ │ └── args[0]: 4 : i64 │ │ └── layout: references$Person - │ ├── body[5] > overwrite: - │ │ ├── address: address of #1 : ptr - │ │ ├── value > take: - │ │ │ ├── type: {i32, i64} - │ │ │ ├── address: address of #5 : ptr - │ │ │ └── layout: references$Person - │ │ └── layout: references$Person - │ ├── body[6] > eval > call: + │ ├── body[5] > eval > call: │ │ ├── type: void │ │ ├── fn: references$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -72,52 +63,51 @@ │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #4 : i32 + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #4 : ptr │ │ └── right: 4 : i64 - │ ├── body[7] > eval > call: + │ ├── body[6] > eval > call: │ │ ├── type: void │ │ ├── fn: references$pass(&%runtime$Console) - │ │ └── args[0] > mint: global zane.value.@program$console - │ ├── body[8] > eval > call: + │ │ └── args[0]: global zane.value.@program$console + │ ├── body[7] > eval > call: │ │ ├── type: void │ │ ├── fn: references$pass(&%runtime$Console) - │ │ └── args[0] > mint: global zane.value.@program$console - │ └── body[9] > return: unit : void + │ │ └── args[0]: global zane.value.@program$console + │ └── body[8] > return: unit : void ├── funcs[1] > func: │ ├── symbol: references$Person(%primitives$Int) │ ├── params[0]: local #1 : i64 - │ ├── ret: {i32, i64} + │ ├── ret: {i64} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, i64} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ └── members[1] > member 1: local #1 : i64 + │ │ ├── type: {i64} + │ │ └── members[0] > member 0: local #1 : i64 │ └── layout: references$Person ├── funcs[2] > func: │ ├── symbol: references$age(&references$Person) - │ ├── params[0]: local #1 : i32 + │ ├── params[0]: local #1 : ptr │ ├── ret: i64 │ └── body[0] > return > snapshot: │ ├── type: i64 │ └── ptr > offset: - │ ├── within: {i32, i64} - │ ├── path: 1 - │ └── base > resolve: local #1 : i32 + │ ├── within: {i64} + │ ├── path: 0 + │ └── base: local #1 : ptr ├── funcs[3] > func: │ ├── symbol: references$check(%primitives$Bool) │ ├── params[0]: local #1 : i1 │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -139,6 +129,6 @@ │ └── body[4] > return: unit : void └── funcs[4] > func: ├── symbol: references$pass(&%runtime$Console) - ├── params[0]: local #1 : i32 + ├── params[0]: local #1 : ptr ├── ret: void └── body[0] > return: unit : void diff --git a/tests/codegen/golden/settled.cgt b/tests/codegen/golden/settled.cgt new file mode 100644 index 00000000..5c6c1033 --- /dev/null +++ b/tests/codegen/golden/settled.cgt @@ -0,0 +1,695 @@ +└── program: + ├── entry: settled$main() + ├── layouts[0] > layout: + │ ├── type: settled$Node + │ └── positions: + ├── layouts[1] > layout: + │ ├── type: ^settled$Node + │ └── positions: + ├── layouts[2] > layout: + │ ├── type: settled$Car + │ └── positions: + ├── layouts[3] > layout: + │ ├── type: settled$Holder + │ └── positions: + ├── layouts[4] > layout: + │ ├── type: %primitives$String + │ └── positions[0]: text @0 + ├── funcs[0] > func: + │ ├── symbol: settled$main() + │ ├── params: + │ ├── ret: void + │ └── body[0] > scope: + │ ├── id: %2 + │ ├── body[0] > hold: + │ │ ├── local: #1 + │ │ ├── scope: %2 + │ │ ├── value > call: + │ │ │ ├── type: {i64} + │ │ │ ├── fn: settled$Node(%primitives$Int) + │ │ │ └── args[0]: 1 : i64 + │ │ └── layout: settled$Node + │ ├── body[1] > let: + │ │ ├── local: #3 + │ │ └── value: address of #1 : ptr + │ ├── body[2] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$bump(this settled$Node) + │ │ └── args[0]: address of #1 : ptr + │ ├── body[3] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #3 : ptr + │ │ └── right: 2 : i64 + │ ├── body[4] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$bump(this settled$Node) + │ │ └── args[0]: local #3 : ptr + │ ├── body[5] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: address of #1 : ptr + │ │ └── right: 3 : i64 + │ ├── body[6] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: * + │ │ ├── left > binary: + │ │ │ ├── type: i1 + │ │ │ ├── op: == + │ │ │ ├── left > call: + │ │ │ │ ├── type: i64 + │ │ │ │ ├── fn: settled$peek(&settled$Node) + │ │ │ │ └── args[0]: local #3 : ptr + │ │ │ └── right: 3 : i64 + │ │ └── right > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > call: + │ │ │ ├── type: i64 + │ │ │ ├── fn: settled$peek(&settled$Node) + │ │ │ └── args[0]: address of #1 : ptr + │ │ └── right: 3 : i64 + │ ├── body[7] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > call: + │ │ │ ├── type: i64 + │ │ │ ├── fn: settled$twice(this settled$Node) + │ │ │ └── args[0]: local #3 : ptr + │ │ └── right: 6 : i64 + │ ├── body[8] > overwrite: + │ │ ├── address: address of #1 : ptr + │ │ ├── value > call: + │ │ │ ├── type: {i64} + │ │ │ ├── fn: settled$Node(%primitives$Int) + │ │ │ └── args[0]: 7 : i64 + │ │ └── layout: settled$Node + │ ├── body[9] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #3 : ptr + │ │ └── right: 7 : i64 + │ ├── body[10] > hold: + │ │ ├── local: #4 + │ │ ├── scope: %2 + │ │ ├── value > call: + │ │ │ ├── type: {i64} + │ │ │ ├── fn: settled$Node(%primitives$Int) + │ │ │ └── args[0]: 20 : i64 + │ │ └── layout: ^settled$Node + │ ├── body[11] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$bump(this settled$Node) + │ │ └── args[0]: address of #4 : ptr + │ ├── body[12] > hold: + │ │ ├── local: #5 + │ │ ├── scope: %2 + │ │ ├── value > take: + │ │ │ ├── type: {i64} + │ │ │ ├── address: address of #4 : ptr + │ │ │ └── layout: settled$Node + │ │ └── layout: settled$Node + │ ├── body[13] > let: + │ │ ├── local: #6 + │ │ └── value: address of #5 : ptr + │ ├── body[14] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #6 : ptr + │ │ └── right: 21 : i64 + │ ├── body[15] > overwrite: + │ │ ├── address: address of #5 : ptr + │ │ ├── value > call: + │ │ │ ├── type: {i64} + │ │ │ ├── fn: settled$Node(%primitives$Int) + │ │ │ └── args[0]: 100 : i64 + │ │ └── layout: settled$Node + │ ├── body[16] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #6 : ptr + │ │ └── right: 100 : i64 + │ ├── body[17] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base > call: + │ │ │ ├── type: ptr + │ │ │ ├── fn: settled$larger(&settled$Node, &settled$Node) + │ │ │ ├── args[0]: address of #1 : ptr + │ │ │ └── args[1]: address of #5 : ptr + │ │ └── right: 100 : i64 + │ ├── body[18] > assign: + │ │ ├── place: #3 + │ │ └── value: address of #5 : ptr + │ ├── body[19] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #3 : ptr + │ │ └── right: 100 : i64 + │ ├── body[20] > hold: + │ │ ├── local: #7 + │ │ ├── scope: %2 + │ │ ├── value > call: + │ │ │ ├── type: {ptr, {i64}} + │ │ │ ├── fn: settled$Car(&settled$Node, ^settled$Node) + │ │ │ ├── args[0]: address of #1 : ptr + │ │ │ └── args[1] > call: + │ │ │ ├── type: {i64} + │ │ │ ├── fn: settled$Node(%primitives$Int) + │ │ │ └── args[0]: 5 : i64 + │ │ └── layout: settled$Car + │ ├── body[21] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$bump(this settled$Node) + │ │ └── args[0] > member: + │ │ ├── type: ptr + │ │ ├── index: 0 + │ │ └── value: local #7 : {ptr, {i64}} + │ ├── body[22] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: address of #1 : ptr + │ │ └── right: 8 : i64 + │ ├── body[23] > let: + │ │ ├── local: #8 + │ │ └── value > offset: + │ │ ├── within: {ptr, {i64}} + │ │ ├── path: 1 + │ │ └── base: address of #7 : ptr + │ ├── body[24] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$bump(this settled$Node) + │ │ └── args[0]: local #8 : ptr + │ ├── body[25] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base > offset: + │ │ │ ├── within: {ptr, {i64}} + │ │ │ ├── path: 1 + │ │ │ └── base: address of #7 : ptr + │ │ └── right: 6 : i64 + │ ├── body[26] > overwrite: + │ │ ├── address > offset: + │ │ │ ├── within: {ptr, {i64}} + │ │ │ ├── path: 1 + │ │ │ └── base: address of #7 : ptr + │ │ ├── value > call: + │ │ │ ├── type: {i64} + │ │ │ ├── fn: settled$Node(%primitives$Int) + │ │ │ └── args[0]: 50 : i64 + │ │ └── layout: settled$Node + │ ├── body[27] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #8 : ptr + │ │ └── right: 50 : i64 + │ ├── body[28] > overwrite: + │ │ ├── address: address of #7 : ptr + │ │ ├── value > call: + │ │ │ ├── type: {ptr, {i64}} + │ │ │ ├── fn: settled$Car(&settled$Node, ^settled$Node) + │ │ │ ├── args[0]: address of #5 : ptr + │ │ │ └── args[1] > call: + │ │ │ ├── type: {i64} + │ │ │ ├── fn: settled$Node(%primitives$Int) + │ │ │ └── args[0]: 60 : i64 + │ │ └── layout: settled$Car + │ ├── body[29] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #8 : ptr + │ │ └── right: 60 : i64 + │ ├── body[30] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base > member: + │ │ │ ├── type: ptr + │ │ │ ├── index: 0 + │ │ │ └── value: local #7 : {ptr, {i64}} + │ │ └── right: 100 : i64 + │ ├── body[31] > hold: + │ │ ├── local: #9 + │ │ ├── scope: %2 + │ │ ├── value > case: + │ │ │ ├── type: <{i64} | void> + │ │ │ ├── index: 0 + │ │ │ └── payload > call: + │ │ │ ├── type: {i64} + │ │ │ ├── fn: settled$Node(%primitives$Int) + │ │ │ └── args[0]: 30 : i64 + │ │ └── layout: settled$Holder + │ ├── body[32] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > expand: + │ │ ├── type: i1 + │ │ ├── label: @10 + │ │ ├── result: #11 + │ │ ├── body[0] > let: + │ │ │ ├── local: #12 + │ │ │ └── value: address of #9 : ptr + │ │ └── body[1] > switch: + │ │ ├── value > deref: + │ │ │ ├── type: <{i64} | void> + │ │ │ └── ptr: local #12 : ptr + │ │ ├── cases[0] > case 0[0] > let: + │ │ │ ├── local: #13 + │ │ │ └── value > offset: + │ │ │ ├── within: <{i64} | void> + │ │ │ ├── path: 0 + │ │ │ └── base: local #12 : ptr + │ │ ├── cases[0] > case 0[1] > eval > call: + │ │ │ ├── type: void + │ │ │ ├── fn: settled$bump(this settled$Node) + │ │ │ └── args[0]: local #13 : ptr + │ │ ├── cases[0] > case 0[2] > assign: + │ │ │ ├── place: #11 + │ │ │ └── value > binary: + │ │ │ ├── type: i1 + │ │ │ ├── op: == + │ │ │ ├── left > snapshot: + │ │ │ │ ├── type: i64 + │ │ │ │ └── ptr > offset: + │ │ │ │ ├── within: {i64} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base: local #13 : ptr + │ │ │ └── right: 31 : i64 + │ │ ├── cases[0] > case 0[3]: leave @10 + │ │ ├── cases[1] > case 1[0] > let: + │ │ │ ├── local: #14 + │ │ │ └── value > offset: + │ │ │ ├── within: <{i64} | void> + │ │ │ ├── path: 1 + │ │ │ └── base: local #12 : ptr + │ │ ├── cases[1] > case 1[1] > assign: + │ │ │ ├── place: #11 + │ │ │ └── value: false : i1 + │ │ └── cases[1] > case 1[2]: leave @10 + │ ├── body[33] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > expand: + │ │ ├── type: i1 + │ │ ├── label: @15 + │ │ ├── result: #16 + │ │ ├── body[0] > let: + │ │ │ ├── local: #17 + │ │ │ └── value: address of #9 : ptr + │ │ └── body[1] > switch: + │ │ ├── value > deref: + │ │ │ ├── type: <{i64} | void> + │ │ │ └── ptr: local #17 : ptr + │ │ ├── cases[0] > case 0[0] > let: + │ │ │ ├── local: #18 + │ │ │ └── value > offset: + │ │ │ ├── within: <{i64} | void> + │ │ │ ├── path: 0 + │ │ │ └── base: local #17 : ptr + │ │ ├── cases[0] > case 0[1] > assign: + │ │ │ ├── place: #16 + │ │ │ └── value > binary: + │ │ │ ├── type: i1 + │ │ │ ├── op: == + │ │ │ ├── left > snapshot: + │ │ │ │ ├── type: i64 + │ │ │ │ └── ptr > offset: + │ │ │ │ ├── within: {i64} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base: local #18 : ptr + │ │ │ └── right: 31 : i64 + │ │ ├── cases[0] > case 0[2]: leave @15 + │ │ ├── cases[1] > case 1[0] > let: + │ │ │ ├── local: #19 + │ │ │ └── value > offset: + │ │ │ ├── within: <{i64} | void> + │ │ │ ├── path: 1 + │ │ │ └── base: local #17 : ptr + │ │ ├── cases[1] > case 1[1] > assign: + │ │ │ ├── place: #16 + │ │ │ └── value: false : i1 + │ │ └── cases[1] > case 1[2]: leave @15 + │ ├── body[34] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > expand: + │ │ ├── type: i1 + │ │ ├── label: @20 + │ │ ├── result: #21 + │ │ ├── body[0] > hold: + │ │ │ ├── local: #22 + │ │ │ ├── scope: %2 + │ │ │ ├── value > case: + │ │ │ │ ├── type: <{i64} | void> + │ │ │ │ ├── index: 0 + │ │ │ │ └── payload > call: + │ │ │ │ ├── type: {i64} + │ │ │ │ ├── fn: settled$Node(%primitives$Int) + │ │ │ │ └── args[0]: 40 : i64 + │ │ │ └── layout: settled$Holder + │ │ ├── body[1] > let: + │ │ │ ├── local: #23 + │ │ │ └── value: address of #22 : ptr + │ │ └── body[2] > switch: + │ │ ├── value > deref: + │ │ │ ├── type: <{i64} | void> + │ │ │ └── ptr: local #23 : ptr + │ │ ├── cases[0] > case 0[0] > let: + │ │ │ ├── local: #24 + │ │ │ └── value > offset: + │ │ │ ├── within: <{i64} | void> + │ │ │ ├── path: 0 + │ │ │ └── base: local #23 : ptr + │ │ ├── cases[0] > case 0[1] > eval > call: + │ │ │ ├── type: void + │ │ │ ├── fn: settled$bump(this settled$Node) + │ │ │ └── args[0]: local #24 : ptr + │ │ ├── cases[0] > case 0[2] > assign: + │ │ │ ├── place: #21 + │ │ │ └── value > binary: + │ │ │ ├── type: i1 + │ │ │ ├── op: == + │ │ │ ├── left > snapshot: + │ │ │ │ ├── type: i64 + │ │ │ │ └── ptr > offset: + │ │ │ │ ├── within: {i64} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base: local #24 : ptr + │ │ │ └── right: 41 : i64 + │ │ ├── cases[0] > case 0[3]: leave @20 + │ │ ├── cases[1] > case 1[0] > let: + │ │ │ ├── local: #25 + │ │ │ └── value > offset: + │ │ │ ├── within: <{i64} | void> + │ │ │ ├── path: 1 + │ │ │ └── base: local #23 : ptr + │ │ ├── cases[1] > case 1[1] > assign: + │ │ │ ├── place: #21 + │ │ │ └── value: false : i1 + │ │ └── cases[1] > case 1[2]: leave @20 + │ ├── body[35] > let: + │ │ ├── local: #26 + │ │ └── value: 0 : i64 + │ ├── body[36] > let: + │ │ ├── local: #27 + │ │ └── value: 1 : i64 + │ ├── body[37] > eval > expand: + │ │ ├── type: void + │ │ ├── label: @29 + │ │ ├── body[0] > let: + │ │ │ ├── local: #28 + │ │ │ └── value: 100000 : i64 + │ │ ├── body[1] > repeat: + │ │ │ ├── count > binary: + │ │ │ │ ├── type: i64 + │ │ │ │ ├── op: + + │ │ │ │ ├── left > binary: + │ │ │ │ │ ├── type: i64 + │ │ │ │ │ ├── op: + + │ │ │ │ │ ├── left: local #28 : i64 + │ │ │ │ │ └── right > flip: + │ │ │ │ │ ├── type: i64 + │ │ │ │ │ └── value: local #27 : i64 + │ │ │ │ └── right: 1 : i64 + │ │ │ ├── body[0] > if: + │ │ │ │ ├── cond: true : i1 + │ │ │ │ └── body[0] > scope: + │ │ │ │ ├── id: %33 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #32 + │ │ │ │ │ ├── scope: %33 + │ │ │ │ │ ├── value > call: + │ │ │ │ │ │ ├── type: {i64} + │ │ │ │ │ │ ├── fn: settled$Node(%primitives$Int) + │ │ │ │ │ │ └── args[0]: local #27 : i64 + │ │ │ │ │ └── layout: settled$Node + │ │ │ │ ├── body[1] > let: + │ │ │ │ │ ├── local: #34 + │ │ │ │ │ └── value: address of #32 : ptr + │ │ │ │ └── body[2] > assign: + │ │ │ │ ├── place: #26 + │ │ │ │ └── value > binary: + │ │ │ │ ├── type: i64 + │ │ │ │ ├── op: + + │ │ │ │ ├── left: local #26 : i64 + │ │ │ │ └── right > call: + │ │ │ │ ├── type: i64 + │ │ │ │ ├── fn: settled$peek(&settled$Node) + │ │ │ │ └── args[0]: local #34 : ptr + │ │ │ └── body[1] > assign: + │ │ │ ├── place: #27 + │ │ │ └── value > binary: + │ │ │ ├── type: i64 + │ │ │ ├── op: + + │ │ │ ├── left: local #27 : i64 + │ │ │ └── right: 1 : i64 + │ │ ├── body[2] > eval: unit : void + │ │ └── body[3]: leave @29 + │ ├── body[38] > eval > call: + │ │ ├── type: void + │ │ ├── fn: settled$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left: local #26 : i64 + │ │ └── right: 5000050000 : i64 + │ └── body[39] > return: unit : void + ├── funcs[1] > func: + │ ├── symbol: settled$Node(%primitives$Int) + │ ├── params[0]: local #1 : i64 + │ ├── ret: {i64} + │ └── body[0] > return > escape: + │ ├── exit: function + │ ├── value > record: + │ │ ├── type: {i64} + │ │ └── members[0] > member 0: local #1 : i64 + │ └── layout: settled$Node + ├── funcs[2] > func: + │ ├── symbol: settled$bump(this settled$Node) + │ ├── params[0]: local #1 : ptr + │ ├── ret: void + │ ├── body[0] > assign: + │ │ ├── place: *#1.0 + │ │ └── value > binary: + │ │ ├── type: i64 + │ │ ├── op: + + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #1 : ptr + │ │ └── right: 1 : i64 + │ └── body[1] > return: unit : void + ├── funcs[3] > func: + │ ├── symbol: settled$check(%primitives$Bool) + │ ├── params[0]: local #1 : i1 + │ ├── ret: void + │ └── body[0] > scope: + │ ├── id: %3 + │ ├── body[0] > hold: + │ │ ├── local: #2 + │ │ ├── scope: %3 + │ │ ├── value: "yes\n" : handle + │ │ └── layout: %primitives$String + │ ├── body[1] > hold: + │ │ ├── local: #4 + │ │ ├── scope: %3 + │ │ ├── value: "no\n" : handle + │ │ └── layout: %primitives$String + │ ├── body[2] > if: + │ │ ├── cond: local #1 : i1 + │ │ └── body[0] > eval > runtime: + │ │ ├── type: void + │ │ ├── fn: zane_print + │ │ └── args[0]: local #2 : handle + │ ├── body[3] > if: + │ │ ├── cond > flip: + │ │ │ ├── type: i1 + │ │ │ └── value: local #1 : i1 + │ │ └── body[0] > eval > runtime: + │ │ ├── type: void + │ │ ├── fn: zane_print + │ │ └── args[0]: local #4 : handle + │ └── body[4] > return: unit : void + ├── funcs[4] > func: + │ ├── symbol: settled$peek(&settled$Node) + │ ├── params[0]: local #1 : ptr + │ ├── ret: i64 + │ └── body[0] > return > snapshot: + │ ├── type: i64 + │ └── ptr > offset: + │ ├── within: {i64} + │ ├── path: 0 + │ └── base: local #1 : ptr + ├── funcs[5] > func: + │ ├── symbol: settled$twice(this settled$Node) + │ ├── params[0]: local #1 : ptr + │ ├── ret: i64 + │ └── body[0] > return > binary: + │ ├── type: i64 + │ ├── op: * + │ ├── left > snapshot: + │ │ ├── type: i64 + │ │ └── ptr > offset: + │ │ ├── within: {i64} + │ │ ├── path: 0 + │ │ └── base: local #1 : ptr + │ └── right: 2 : i64 + ├── funcs[6] > func: + │ ├── symbol: settled$larger(&settled$Node, &settled$Node) + │ ├── params[0]: local #1 : ptr + │ ├── params[1]: local #2 : ptr + │ ├── ret: ptr + │ ├── body[0] > eval > expand: + │ │ ├── type: i1 + │ │ ├── label: @4 + │ │ ├── result: #5 + │ │ ├── body[0] > let: + │ │ │ ├── local: #3 + │ │ │ └── value > binary: + │ │ │ ├── type: i1 + │ │ │ ├── op: < + │ │ │ ├── left > snapshot: + │ │ │ │ ├── type: i64 + │ │ │ │ └── ptr > offset: + │ │ │ │ ├── within: {i64} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base: local #2 : ptr + │ │ │ └── right > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #1 : ptr + │ │ ├── body[1] > if: + │ │ │ ├── cond: local #3 : i1 + │ │ │ └── body[0] > return: local #1 : ptr + │ │ ├── body[2] > assign: + │ │ │ ├── place: #5 + │ │ │ └── value: local #3 : i1 + │ │ └── body[3]: leave @4 + │ └── body[1] > return: local #2 : ptr + └── funcs[7] > func: + ├── symbol: settled$Car(&settled$Node, ^settled$Node) + ├── params[0]: local #1 : ptr + ├── params[1]: local #2 : {i64} + ├── ret: {ptr, {i64}} + └── body[0] > scope: + ├── id: %4 + ├── body[0] > hold: + │ ├── local: #3 + │ ├── scope: %4 + │ ├── value: local #2 : {i64} + │ └── layout: ^settled$Node + └── body[1] > return > escape: + ├── exit: function + ├── value > record: + │ ├── type: {ptr, {i64}} + │ ├── members[0] > member 0: local #1 : ptr + │ └── members[1] > member 1 > take: + │ ├── type: {i64} + │ ├── address: address of #3 : ptr + │ └── layout: settled$Node + └── layout: settled$Car diff --git a/tests/codegen/golden/hosts.out b/tests/codegen/golden/settled.out similarity index 83% rename from tests/codegen/golden/hosts.out rename to tests/codegen/golden/settled.out index 45b1a4f7..c09bc111 100644 --- a/tests/codegen/golden/hosts.out +++ b/tests/codegen/golden/settled.out @@ -13,3 +13,6 @@ yes yes yes yes +yes +yes +yes diff --git a/tests/codegen/golden/shapes.cgt b/tests/codegen/golden/shapes.cgt index 8b1d33ae..3ec3ef45 100644 --- a/tests/codegen/golden/shapes.cgt +++ b/tests/codegen/golden/shapes.cgt @@ -2,8 +2,7 @@ ├── entry: shapes$main() ├── layouts[0] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── funcs[0] > func: │ ├── symbol: shapes$main() │ ├── params: @@ -411,12 +410,12 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle diff --git a/tests/codegen/golden/spawned.cgt b/tests/codegen/golden/spawned.cgt index bd8bea1f..eb78a73b 100644 --- a/tests/codegen/golden/spawned.cgt +++ b/tests/codegen/golden/spawned.cgt @@ -2,12 +2,10 @@ ├── entry: spawned$main() ├── layouts[0] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── layouts[1] > layout: │ ├── type: %primitives$List<%primitives$String> - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: list @0 of %primitives$String (32 bytes apart) + │ └── positions[0]: list @0 of %primitives$String (24 bytes apart) ├── layouts[2] > layout: │ ├── type: %primitives$Int │ └── positions: @@ -16,8 +14,7 @@ │ └── positions: ├── layouts[4] > layout: │ ├── type: outcome of %primitives$Int ? %primitives$String - │ ├── positions[0]: @8 (32 bytes) when [0]=1 - │ └── positions[1]: text @8 when [0]=1 + │ └── positions[0]: text @8 when [0]=1 ├── layouts[5] > layout: │ ├── type: %primitives$Unit │ └── positions: @@ -30,7 +27,7 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %2 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #1 │ │ ├── scope: %2 │ │ ├── value > runtime: @@ -50,8 +47,7 @@ │ │ │ ├── type: ptr │ │ │ ├── fn: zane_list_push │ │ │ ├── args[0]: address of #1 : ptr - │ │ │ ├── args[1]: 32 : i64 - │ │ │ └── args[2]: layout %primitives$String + │ │ │ └── args[1]: 24 : i64 │ │ └── body[2] > place: │ │ ├── address: local #4 : ptr │ │ ├── value: local #3 : handle @@ -68,8 +64,7 @@ │ │ │ ├── type: ptr │ │ │ ├── fn: zane_list_push │ │ │ ├── args[0]: address of #1 : ptr - │ │ │ ├── args[1]: 32 : i64 - │ │ │ └── args[2]: layout %primitives$String + │ │ │ └── args[1]: 24 : i64 │ │ └── body[2] > place: │ │ ├── address: local #7 : ptr │ │ ├── value: local #6 : handle @@ -222,7 +217,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @40 │ │ │ ├── result: #41 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #39 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "hi!" : handle @@ -364,7 +359,7 @@ │ │ │ │ ├── cases[0] > case 0: │ │ │ │ └── cases[1] > case 1[0] > scope: │ │ │ │ ├── id: %60 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #59 │ │ │ │ │ ├── scope: %60 │ │ │ │ │ ├── value > take: @@ -462,7 +457,7 @@ │ ├── cases[0] > case 0: │ └── cases[1] > case 1[0] > scope: │ ├── id: %75 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #74 │ │ ├── scope: %75 │ │ ├── value > take: @@ -487,7 +482,7 @@ │ ├── ret: i64 │ └── body[0] > return > member: │ ├── type: i64 - │ ├── index: 2 + │ ├── index: 1 │ └── value > deref: │ ├── type: handle │ └── ptr: local #1 : ptr @@ -497,12 +492,12 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -564,7 +559,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @2 │ │ │ ├── result: #3 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #1 │ │ │ │ ├── scope: %4 │ │ │ │ ├── value: "hi" : handle @@ -576,7 +571,7 @@ │ │ ├── type: handle │ │ ├── label: @6 │ │ ├── result: #7 - │ │ ├── body[0] > host: + │ │ ├── body[0] > hold: │ │ │ ├── local: #5 │ │ │ ├── scope: %4 │ │ │ ├── value: "!" : handle diff --git a/tests/codegen/golden/spawns.cgt b/tests/codegen/golden/spawns.cgt index 837ae6db..f33a4c9a 100644 --- a/tests/codegen/golden/spawns.cgt +++ b/tests/codegen/golden/spawns.cgt @@ -5,35 +5,32 @@ │ └── positions: ├── layouts[1] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── layouts[2] > layout: │ ├── type: spawns$Person - │ ├── positions[0]: @0 (48 bytes) - │ ├── positions[1]: @8 (32 bytes) - │ └── positions[2]: text @8 + │ └── positions[0]: text @0 ├── layouts[3] > layout: - │ ├── type: %primitives$List<%primitives$String> - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: list @0 of %primitives$String (32 bytes apart) + │ ├── type: ^spawns$Person + │ └── positions[0]: text @0 ├── layouts[4] > layout: + │ ├── type: ^%primitives$List<%primitives$String> + │ └── positions[0]: list @0 of %primitives$String (24 bytes apart) + ├── layouts[5] > layout: │ ├── type: %primitives$Unit │ └── positions: - ├── layouts[5] > layout: + ├── layouts[6] > layout: │ ├── type: spawns$Countdown │ └── positions[0]: box @8 of spawns$Countdown (16 bytes) when [0]=1 - ├── layouts[6] > layout: - │ ├── type: spawns$Holder - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: box @16 of spawns$Countdown (16 bytes) when [8]=1 ├── layouts[7] > layout: + │ ├── type: spawns$Holder + │ └── positions[0]: box @8 of spawns$Countdown (16 bytes) when [0]=1 + ├── layouts[8] > layout: │ ├── type: spawns$Tally │ └── positions: - ├── layouts[8] > layout: - │ ├── type: outcome of %primitives$Int ? %primitives$String - │ ├── positions[0]: @8 (32 bytes) when [0]=1 - │ └── positions[1]: text @8 when [0]=1 ├── layouts[9] > layout: + │ ├── type: outcome of %primitives$Int ? %primitives$String + │ └── positions[0]: text @8 when [0]=1 + ├── layouts[10] > layout: │ ├── type: outcome of %primitives$Int │ └── positions: ├── funcs[0] > func: @@ -121,7 +118,7 @@ │ │ │ ├── type: i64 │ │ │ └── ptr: address of #10 : ptr │ │ └── right: 6765 : i64 - │ ├── body[6] > host: + │ ├── body[6] > hold: │ │ ├── local: #14 │ │ ├── scope: %11 │ │ ├── value: "ada" : handle @@ -130,8 +127,8 @@ │ │ ├── task: #16 │ │ ├── scope: %11 │ │ ├── thunk: zane.spawn.2 - │ │ ├── frame: {handle, ptr} - │ │ ├── args[0]: address of #14 : ptr + │ │ ├── frame: {handle, handle} + │ │ ├── args[0]: local #14 : handle │ │ ├── dest: #17 │ │ └── layout: %primitives$String │ ├── body[8] > eval > call: @@ -157,7 +154,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @21 │ │ │ ├── result: #22 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #20 │ │ │ │ ├── scope: %11 │ │ │ │ ├── value: "hi ada" : handle @@ -166,35 +163,35 @@ │ │ │ ├── place: #22 │ │ │ └── value: local #20 : handle │ │ └── right: 1 : i64 - │ ├── body[9] > host: + │ ├── body[9] > hold: │ │ ├── local: #26 │ │ ├── scope: %11 │ │ ├── value > call: - │ │ │ ├── type: {i32, handle, i64} + │ │ │ ├── type: {handle, i64} │ │ │ ├── fn: spawns$Person(%primitives$String, %primitives$Int) │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: ptr + │ │ │ │ ├── type: handle │ │ │ │ ├── label: @24 │ │ │ │ ├── result: #25 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #23 │ │ │ │ │ ├── scope: %11 │ │ │ │ │ ├── value: "grace" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #25 - │ │ │ │ └── value: address of #23 : ptr + │ │ │ │ └── value: local #23 : handle │ │ │ └── args[1]: 85 : i64 │ │ └── layout: spawns$Person │ ├── body[10] > let: │ │ ├── local: #27 - │ │ └── value > mint: address of #26 : ptr + │ │ └── value: address of #26 : ptr │ ├── body[11] > spawn: │ │ ├── task: #29 │ │ ├── scope: %11 │ │ ├── thunk: zane.spawn.3 - │ │ ├── frame: {handle, i32} - │ │ ├── args[0] > terminal: local #27 : i32 + │ │ ├── frame: {handle, ptr} + │ │ ├── args[0]: local #27 : ptr │ │ ├── dest: #30 │ │ └── layout: %primitives$String │ ├── body[12] > eval > call: @@ -220,7 +217,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @34 │ │ │ ├── result: #35 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #33 │ │ │ │ ├── scope: %11 │ │ │ │ ├── value: "grace!" : handle @@ -233,25 +230,25 @@ │ │ ├── task: #40 │ │ ├── scope: %11 │ │ ├── thunk: zane.spawn.4 - │ │ ├── frame: {{i32, handle, i64}, ptr, i64} + │ │ ├── frame: {{handle, i64}, handle, i64} │ │ ├── args[0] > expand: - │ │ │ ├── type: ptr + │ │ │ ├── type: handle │ │ │ ├── label: @37 │ │ │ ├── result: #38 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #36 │ │ │ │ ├── scope: %11 │ │ │ │ ├── value: "ada" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: │ │ │ ├── place: #38 - │ │ │ └── value: address of #36 : ptr + │ │ │ └── value: local #36 : handle │ │ ├── args[1]: 36 : i64 │ │ ├── dest: #41 - │ │ └── layout: spawns$Person + │ │ └── layout: ^spawns$Person │ ├── body[14] > let: │ │ ├── local: #44 - │ │ └── value > mint > expand: + │ │ └── value > expand: │ │ ├── type: ptr │ │ ├── label: @42 │ │ ├── result: #43 @@ -268,17 +265,17 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > deref: + │ │ │ ├── args[0] > snapshot: │ │ │ │ ├── type: handle │ │ │ │ └── ptr > offset: - │ │ │ │ ├── within: {i32, handle, i64} - │ │ │ │ ├── path: 1 - │ │ │ │ └── base > resolve: local #44 : i32 + │ │ │ │ ├── within: {handle, i64} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base: local #44 : ptr │ │ │ └── args[1] > expand: │ │ │ ├── type: handle │ │ │ ├── label: @46 │ │ │ ├── result: #47 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #45 │ │ │ │ ├── scope: %11 │ │ │ │ ├── value: "ada lovelace" : handle @@ -296,9 +293,9 @@ │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, handle, i64} - │ │ │ ├── path: 2 - │ │ │ └── base > resolve: local #44 : i32 + │ │ │ ├── within: {handle, i64} + │ │ │ ├── path: 1 + │ │ │ └── base: local #44 : ptr │ │ └── right: 36 : i64 │ ├── body[17] > spawn: │ │ ├── task: #49 @@ -307,7 +304,7 @@ │ │ ├── frame: {handle, i64} │ │ ├── args[0]: 50 : i64 │ │ ├── dest: #50 - │ │ └── layout: %primitives$List<%primitives$String> + │ │ └── layout: ^%primitives$List<%primitives$String> │ ├── body[18] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) @@ -316,7 +313,7 @@ │ │ ├── op: == │ │ ├── left > member: │ │ │ ├── type: i64 - │ │ │ ├── index: 2 + │ │ │ ├── index: 1 │ │ │ └── value > expand: │ │ │ ├── type: handle │ │ │ ├── label: @51 @@ -353,7 +350,7 @@ │ │ ├── op: == │ │ ├── left: local #53 : i64 │ │ └── right: 5 : i64 - │ ├── body[22] > host: + │ ├── body[22] > hold: │ │ ├── local: #58 │ │ ├── scope: %11 │ │ ├── value > case: @@ -361,7 +358,7 @@ │ │ │ ├── index: 0 │ │ │ └── payload: unit : void │ │ └── layout: spawns$Countdown - │ ├── body[23] > host: + │ ├── body[23] > hold: │ │ ├── local: #59 │ │ ├── scope: %11 │ │ ├── value > case: @@ -369,7 +366,7 @@ │ │ │ ├── index: 0 │ │ │ └── payload: unit : void │ │ └── layout: spawns$Countdown - │ ├── body[24] > host: + │ ├── body[24] > hold: │ │ ├── local: #60 │ │ ├── scope: %11 │ │ ├── value > case: @@ -427,17 +424,17 @@ │ │ │ ├── fn: spawns$depth(spawns$Countdown) │ │ │ └── args[0]: local #60 : │ │ └── right: 900 : i64 - │ ├── body[27] > host: + │ ├── body[27] > hold: │ │ ├── local: #72 │ │ ├── scope: %11 │ │ ├── value > call: - │ │ │ ├── type: {i32, , i64} + │ │ │ ├── type: {, i64} │ │ │ ├── fn: spawns$Holder(spawns$Countdown) │ │ │ └── args[0] > expand: │ │ │ ├── type: │ │ │ ├── label: @70 │ │ │ ├── result: #71 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #69 │ │ │ │ ├── scope: %11 │ │ │ │ ├── value > case: @@ -451,7 +448,7 @@ │ │ └── layout: spawns$Holder │ ├── body[28] > let: │ │ ├── local: #73 - │ │ └── value > mint: address of #72 : ptr + │ │ └── value: address of #72 : ptr │ ├── body[29] > if: │ │ ├── cond: true : i1 │ │ └── body[0] > scope: @@ -460,8 +457,8 @@ │ │ │ ├── task: #76 │ │ │ ├── scope: %78 │ │ │ ├── thunk: zane.spawn.10 - │ │ │ ├── frame: {i64, i32, i64, i64} - │ │ │ ├── args[0] > terminal: local #73 : i32 + │ │ │ ├── frame: {i64, ptr, i64, i64} + │ │ │ ├── args[0]: local #73 : ptr │ │ │ ├── args[1]: 300 : i64 │ │ │ ├── args[2]: 250 : i64 │ │ │ ├── dest: #77 @@ -469,8 +466,8 @@ │ │ ├── body[1] > let: │ │ │ ├── local: #79 │ │ │ └── value > offset: - │ │ │ ├── within: {i32, , i64} - │ │ │ ├── path: 1 + │ │ │ ├── within: {, i64} + │ │ │ ├── path: 0 │ │ │ └── base: address of #72 : ptr │ │ ├── body[2] > spawn: │ │ │ ├── task: #81 @@ -488,8 +485,8 @@ │ │ ├── body[3] > let: │ │ │ ├── local: #82 │ │ │ └── value > offset: - │ │ │ ├── within: {i32, , i64} - │ │ │ ├── path: 2 + │ │ │ ├── within: {, i64} + │ │ │ ├── path: 1 │ │ │ └── base: address of #72 : ptr │ │ ├── body[4] > spawn: │ │ │ ├── task: #84 @@ -530,8 +527,8 @@ │ │ │ └── args[0] > snapshot: │ │ │ ├── type: │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, , i64} - │ │ │ ├── path: 1 + │ │ │ ├── within: {, i64} + │ │ │ ├── path: 0 │ │ │ └── base: address of #72 : ptr │ │ └── right: 250 : i64 │ ├── body[31] > eval > call: @@ -543,8 +540,8 @@ │ │ ├── left > snapshot: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, , i64} - │ │ │ ├── path: 2 + │ │ │ ├── within: {, i64} + │ │ │ ├── path: 1 │ │ │ └── base: address of #72 : ptr │ │ └── right: 3 : i64 │ ├── body[32] > spawn: @@ -687,38 +684,38 @@ │ │ │ ├── task: #120 │ │ │ ├── scope: %118 │ │ │ ├── thunk: zane.spawn.17 - │ │ │ ├── frame: {handle, ptr} + │ │ │ ├── frame: {handle, handle} │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: ptr + │ │ │ │ ├── type: handle │ │ │ │ ├── label: @116 │ │ │ │ ├── result: #117 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #115 │ │ │ │ │ ├── scope: %118 │ │ │ │ │ ├── value: "nobody" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #117 - │ │ │ │ └── value: address of #115 : ptr + │ │ │ │ └── value: local #115 : handle │ │ │ ├── dest: #121 │ │ │ └── layout: %primitives$String │ │ ├── body[1] > spawn: │ │ │ ├── task: #126 │ │ │ ├── scope: %118 │ │ │ ├── thunk: zane.spawn.18 - │ │ │ ├── frame: {handle, ptr} + │ │ │ ├── frame: {handle, handle} │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: ptr + │ │ │ │ ├── type: handle │ │ │ │ ├── label: @123 │ │ │ │ ├── result: #124 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #122 │ │ │ │ │ ├── scope: %118 │ │ │ │ │ ├── value: "no one" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #124 - │ │ │ │ └── value: address of #122 : ptr + │ │ │ │ └── value: local #122 : handle │ │ │ ├── dest: #127 │ │ │ └── layout: %primitives$String │ │ ├── body[2] > spawn: @@ -728,7 +725,7 @@ │ │ │ ├── frame: {handle, i64} │ │ │ ├── args[0]: 5 : i64 │ │ │ ├── dest: #130 - │ │ │ └── layout: %primitives$List<%primitives$String> + │ │ │ └── layout: ^%primitives$List<%primitives$String> │ │ └── body[3] > spawn: │ │ ├── task: #132 │ │ ├── scope: %118 @@ -766,7 +763,7 @@ │ │ │ ├── frame: {handle, i64} │ │ │ ├── args[0]: 20 : i64 │ │ │ ├── dest: #139 - │ │ │ └── layout: %primitives$List<%primitives$String> + │ │ │ └── layout: ^%primitives$List<%primitives$String> │ │ ├── body[1] > eval > expand: │ │ │ ├── type: void │ │ │ ├── label: @141 @@ -794,7 +791,7 @@ │ │ ├── frame: {handle, i64} │ │ ├── args[0]: 20 : i64 │ │ ├── dest: #146 - │ │ └── layout: %primitives$List<%primitives$String> + │ │ └── layout: ^%primitives$List<%primitives$String> │ ├── body[43] > eval > runtime: │ │ ├── type: void │ │ ├── fn: zane_set_threads_auto @@ -852,7 +849,7 @@ │ │ │ │ ├── cases[0] > case 0: │ │ │ │ └── cases[1] > case 1[0] > scope: │ │ │ │ ├── id: %158 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #157 │ │ │ │ │ ├── scope: %158 │ │ │ │ │ ├── value > take: @@ -916,7 +913,7 @@ │ │ │ │ │ ├── cases[0] > case 0: │ │ │ │ │ └── cases[1] > case 1[0] > scope: │ │ │ │ │ ├── id: %163 - │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ ├── local: #162 │ │ │ │ │ │ ├── scope: %163 │ │ │ │ │ │ ├── value > take: @@ -943,7 +940,7 @@ │ │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ │ ├── label: @165 │ │ │ │ │ │ │ ├── result: #166 - │ │ │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ │ │ ├── local: #164 │ │ │ │ │ │ │ │ ├── scope: %167 │ │ │ │ │ │ │ │ ├── value: "by zero" : handle @@ -995,7 +992,7 @@ │ │ │ │ ├── cases[0] > case 0: │ │ │ │ └── cases[1] > case 1[0] > scope: │ │ │ │ ├── id: %172 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #171 │ │ │ │ │ ├── scope: %172 │ │ │ │ │ ├── value > take: @@ -1022,7 +1019,7 @@ │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── label: @174 │ │ │ │ │ │ ├── result: #175 - │ │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ │ ├── local: #173 │ │ │ │ │ │ │ ├── scope: %176 │ │ │ │ │ │ │ ├── value: "by zero" : handle @@ -1101,7 +1098,7 @@ │ │ │ │ ├── cases[0] > case 0: │ │ │ │ └── cases[1] > case 1[0] > scope: │ │ │ │ ├── id: %185 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #184 │ │ │ │ │ ├── scope: %185 │ │ │ │ │ ├── value > take: @@ -1157,7 +1154,7 @@ │ │ │ ├── cases[0] > case 0[1]: leave @188 │ │ │ └── cases[1] > case 1[0] > scope: │ │ │ ├── id: %192 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #191 │ │ │ │ ├── scope: %192 │ │ │ │ ├── value > payload: @@ -1198,7 +1195,7 @@ │ │ │ ├── cases[0] > case 0[1]: leave @193 │ │ │ └── cases[1] > case 1[0] > scope: │ │ │ ├── id: %197 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #196 │ │ │ │ ├── scope: %197 │ │ │ │ ├── value > payload: @@ -1211,7 +1208,7 @@ │ │ │ │ └── value: 100 : i64 │ │ │ └── body[2]: leave @193 │ │ └── right: 100 : i64 - │ ├── body[57] > host: + │ ├── body[57] > hold: │ │ ├── local: #198 │ │ ├── scope: %11 │ │ ├── value: "" : handle @@ -1238,7 +1235,7 @@ │ │ ├── cases[0] > case 0[1]: leave @199 │ │ └── cases[1] > case 1[0] > scope: │ │ ├── id: %203 - │ │ ├── body[0] > host: + │ │ ├── body[0] > hold: │ │ │ ├── local: #202 │ │ │ ├── scope: %203 │ │ │ ├── value > payload: @@ -1248,9 +1245,9 @@ │ │ │ └── layout: %primitives$String │ │ ├── body[1] > overwrite: │ │ │ ├── address: address of #198 : ptr - │ │ │ ├── value > take: + │ │ │ ├── value > copy: │ │ │ │ ├── type: handle - │ │ │ │ ├── address: address of #202 : ptr + │ │ │ │ ├── value: local #202 : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── layout: %primitives$String │ │ ├── body[2] > assign: @@ -1271,7 +1268,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @205 │ │ │ ├── result: #206 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #204 │ │ │ │ ├── scope: %11 │ │ │ │ ├── value: "by zero!" : handle @@ -1339,7 +1336,7 @@ │ │ │ ├── cases[0] > case 0: │ │ │ └── cases[1] > case 1[0] > scope: │ │ │ ├── id: %224 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #223 │ │ │ │ ├── scope: %224 │ │ │ │ ├── value > take: @@ -1381,7 +1378,7 @@ │ │ │ ├── cases[0] > case 0: │ │ │ └── cases[1] > case 1[0] > scope: │ │ │ ├── id: %227 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #226 │ │ │ │ ├── scope: %227 │ │ │ │ ├── value > take: @@ -1423,7 +1420,7 @@ │ │ ├── cases[0] > case 0: │ │ └── cases[1] > case 1[0] > scope: │ │ ├── id: %230 - │ │ ├── body[0] > host: + │ │ ├── body[0] > hold: │ │ │ ├── local: #229 │ │ │ ├── scope: %230 │ │ │ ├── value > take: @@ -1640,7 +1637,7 @@ │ │ ├── cases[0] > case 0: │ │ └── cases[1] > case 1[0] > scope: │ │ ├── id: %257 - │ │ ├── body[0] > host: + │ │ ├── body[0] > hold: │ │ │ ├── local: #256 │ │ │ ├── scope: %257 │ │ │ ├── value > take: @@ -1682,7 +1679,7 @@ │ │ ├── cases[0] > case 0: │ │ └── cases[1] > case 1[0] > scope: │ │ ├── id: %260 - │ │ ├── body[0] > host: + │ │ ├── body[0] > hold: │ │ │ ├── local: #259 │ │ │ ├── scope: %260 │ │ │ ├── value > take: @@ -1709,7 +1706,7 @@ │ │ │ │ ├── type: handle │ │ │ │ ├── label: @262 │ │ │ │ ├── result: #263 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #261 │ │ │ │ │ ├── scope: %264 │ │ │ │ │ ├── value: "by zero" : handle @@ -1749,7 +1746,7 @@ │ ├── cases[0] > case 0: │ └── cases[1] > case 1[0] > scope: │ ├── id: %267 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #266 │ │ ├── scope: %267 │ │ ├── value > take: @@ -1774,12 +1771,12 @@ │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -1835,7 +1832,7 @@ │ └── value: 2 : i64 ├── funcs[3] > func: │ ├── symbol: spawns$greet(%primitives$String) - │ ├── params[0]: local #1 : ptr + │ ├── params[0]: local #1 : handle │ ├── ret: handle │ └── body[0] > scope: │ ├── id: %5 @@ -1848,7 +1845,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @3 │ │ │ ├── result: #4 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #2 │ │ │ │ ├── scope: %5 │ │ │ │ ├── value: "hi " : handle @@ -1856,29 +1853,26 @@ │ │ │ └── body[1] > assign: │ │ │ ├── place: #4 │ │ │ └── value: local #2 : handle - │ │ └── args[1] > deref: - │ │ ├── type: handle - │ │ └── ptr: local #1 : ptr + │ │ └── args[1]: local #1 : handle │ └── layout: %primitives$String ├── funcs[4] > func: │ ├── symbol: spawns$Person(%primitives$String, %primitives$Int) - │ ├── params[0]: local #1 : ptr + │ ├── params[0]: local #1 : handle │ ├── params[1]: local #2 : i64 - │ ├── ret: {i32, handle, i64} + │ ├── ret: {handle, i64} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, handle, i64} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ ├── members[1] > member 1 > take: + │ │ ├── type: {handle, i64} + │ │ ├── members[0] > member 0 > copy: │ │ │ ├── type: handle - │ │ │ ├── address: local #1 : ptr + │ │ │ ├── value: local #1 : handle │ │ │ └── layout: %primitives$String - │ │ └── members[2] > member 2: local #2 : i64 + │ │ └── members[1] > member 1: local #2 : i64 │ └── layout: spawns$Person ├── funcs[5] > func: │ ├── symbol: spawns$nameOf(&spawns$Person) - │ ├── params[0]: local #1 : i32 + │ ├── params[0]: local #1 : ptr │ ├── ret: handle │ └── body[0] > scope: │ ├── id: %5 @@ -1887,17 +1881,17 @@ │ ├── value > runtime: │ │ ├── type: handle │ │ ├── fn: zane_text_join - │ │ ├── args[0] > deref: + │ │ ├── args[0] > snapshot: │ │ │ ├── type: handle │ │ │ └── ptr > offset: - │ │ │ ├── within: {i32, handle, i64} - │ │ │ ├── path: 1 - │ │ │ └── base > resolve: local #1 : i32 + │ │ │ ├── within: {handle, i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #1 : ptr │ │ └── args[1] > expand: │ │ ├── type: handle │ │ ├── label: @3 │ │ ├── result: #4 - │ │ ├── body[0] > host: + │ │ ├── body[0] > hold: │ │ │ ├── local: #2 │ │ │ ├── scope: %5 │ │ │ ├── value: "!" : handle @@ -1908,34 +1902,32 @@ │ └── layout: %primitives$String ├── funcs[6] > func: │ ├── symbol: spawns$born(%primitives$String, %primitives$Int) - │ ├── params[0]: local #1 : ptr + │ ├── params[0]: local #1 : handle │ ├── params[1]: local #2 : i64 - │ ├── ret: {i32, handle, i64} + │ ├── ret: {handle, i64} │ └── body[0] > scope: │ ├── id: %6 │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > call: - │ │ ├── type: {i32, handle, i64} + │ │ ├── type: {handle, i64} │ │ ├── fn: spawns$Person(%primitives$String, %primitives$Int) │ │ ├── args[0] > expand: - │ │ │ ├── type: ptr + │ │ │ ├── type: handle │ │ │ ├── label: @8 │ │ │ ├── result: #9 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #7 │ │ │ │ ├── scope: %6 │ │ │ │ ├── value > runtime: │ │ │ │ │ ├── type: handle │ │ │ │ │ ├── fn: zane_text_join - │ │ │ │ │ ├── args[0] > deref: - │ │ │ │ │ │ ├── type: handle - │ │ │ │ │ │ └── ptr: local #1 : ptr + │ │ │ │ │ ├── args[0]: local #1 : handle │ │ │ │ │ └── args[1] > expand: │ │ │ │ │ ├── type: handle │ │ │ │ │ ├── label: @4 │ │ │ │ │ ├── result: #5 - │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ ├── local: #3 │ │ │ │ │ │ ├── scope: %6 │ │ │ │ │ │ ├── value: " lovelace" : handle @@ -1946,23 +1938,23 @@ │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: │ │ │ ├── place: #9 - │ │ │ └── value: address of #7 : ptr + │ │ │ └── value: local #7 : handle │ │ └── args[1]: local #2 : i64 - │ └── layout: spawns$Person + │ └── layout: ^spawns$Person ├── funcs[7] > func: │ ├── symbol: spawns$words(%primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── ret: handle │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value > runtime: │ │ │ ├── type: handle │ │ │ ├── fn: zane_list_new │ │ │ └── args: - │ │ └── layout: %primitives$List<%primitives$String> + │ │ └── layout: ^%primitives$List<%primitives$String> │ ├── body[1] > let: │ │ ├── local: #4 │ │ └── value: 1 : i64 @@ -2000,7 +1992,7 @@ │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── label: @10 │ │ │ │ │ │ ├── result: #11 - │ │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ │ ├── local: #9 │ │ │ │ │ │ │ ├── scope: %12 │ │ │ │ │ │ │ ├── value: "w" : handle @@ -2012,7 +2004,7 @@ │ │ │ │ │ ├── type: handle │ │ │ │ │ ├── label: @14 │ │ │ │ │ ├── result: #15 - │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ ├── local: #13 │ │ │ │ │ │ ├── scope: %12 │ │ │ │ │ │ ├── value: "!" : handle @@ -2026,8 +2018,7 @@ │ │ │ │ │ ├── type: ptr │ │ │ │ │ ├── fn: zane_list_push │ │ │ │ │ ├── args[0]: address of #2 : ptr - │ │ │ │ │ ├── args[1]: 32 : i64 - │ │ │ │ │ └── args[2]: layout %primitives$String + │ │ │ │ │ └── args[1]: 24 : i64 │ │ │ │ └── body[2] > place: │ │ │ │ ├── address: local #17 : ptr │ │ │ │ ├── value: local #16 : handle @@ -2046,8 +2037,8 @@ │ ├── value > take: │ │ ├── type: handle │ │ ├── address: address of #2 : ptr - │ │ └── layout: %primitives$List<%primitives$String> - │ └── layout: %primitives$List<%primitives$String> + │ │ └── layout: ^%primitives$List<%primitives$String> + │ └── layout: ^%primitives$List<%primitives$String> ├── funcs[8] > func: │ ├── symbol: spawns$Tally(%primitives$Int) │ ├── params[0]: local #1 : i64 @@ -2169,24 +2160,23 @@ ├── funcs[12] > func: │ ├── symbol: spawns$Holder(spawns$Countdown) │ ├── params[0]: local #1 : - │ ├── ret: {i32, , i64} + │ ├── ret: {, i64} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, , i64} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ ├── members[1] > member 1 > copy: + │ │ ├── type: {, i64} + │ │ ├── members[0] > member 0 > copy: │ │ │ ├── type: │ │ │ ├── value: local #1 : │ │ │ └── layout: spawns$Countdown - │ │ └── members[2] > member 2 > call: + │ │ └── members[1] > member 1 > call: │ │ ├── type: i64 │ │ ├── fn: spawns$Tally(%primitives$Int) │ │ └── args[0]: 0 : i64 │ └── layout: spawns$Holder ├── funcs[13] > func: │ ├── symbol: spawns$watch(&spawns$Holder, %primitives$Int, %primitives$Int) - │ ├── params[0]: local #1 : i32 + │ ├── params[0]: local #1 : ptr │ ├── params[1]: local #2 : i64 │ ├── params[2]: local #3 : i64 │ ├── ret: i64 @@ -2218,7 +2208,7 @@ │ │ │ │ ├── cond: true : i1 │ │ │ │ └── body[0] > scope: │ │ │ │ ├── id: %11 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #10 │ │ │ │ │ ├── scope: %11 │ │ │ │ │ ├── value > copy: @@ -2226,9 +2216,9 @@ │ │ │ │ │ │ ├── value > snapshot: │ │ │ │ │ │ │ ├── type: │ │ │ │ │ │ │ └── ptr > offset: - │ │ │ │ │ │ │ ├── within: {i32, , i64} - │ │ │ │ │ │ │ ├── path: 1 - │ │ │ │ │ │ │ └── base > resolve: local #1 : i32 + │ │ │ │ │ │ │ ├── within: {, i64} + │ │ │ │ │ │ │ ├── path: 0 + │ │ │ │ │ │ │ └── base: local #1 : ptr │ │ │ │ │ │ └── layout: spawns$Countdown │ │ │ │ │ └── layout: spawns$Countdown │ │ │ │ ├── body[1] > let: @@ -2354,7 +2344,7 @@ │ │ │ │ ├── type: handle │ │ │ │ ├── label: @5 │ │ │ │ ├── result: #6 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #4 │ │ │ │ │ ├── scope: %7 │ │ │ │ │ ├── value: "by " : handle @@ -2366,7 +2356,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @9 │ │ │ ├── result: #10 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #8 │ │ │ │ ├── scope: %7 │ │ │ │ ├── value: "zero" : handle @@ -2428,7 +2418,7 @@ │ │ │ │ ├── cases[0] > case 0: │ │ │ │ └── cases[1] > case 1[0] > scope: │ │ │ │ ├── id: %10 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #9 │ │ │ │ │ ├── scope: %10 │ │ │ │ │ ├── value > take: @@ -2454,7 +2444,7 @@ │ │ │ │ │ ├── type: handle │ │ │ │ │ ├── label: @12 │ │ │ │ │ ├── result: #13 - │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ ├── local: #11 │ │ │ │ │ │ ├── scope: %14 │ │ │ │ │ │ ├── value: "!" : handle @@ -2488,7 +2478,7 @@ │ ├── cases[0] > case 0: │ └── cases[1] > case 1[0] > scope: │ ├── id: %19 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #18 │ │ ├── scope: %19 │ │ ├── value > take: @@ -2514,7 +2504,7 @@ │ │ ├── type: handle │ │ ├── label: @21 │ │ ├── result: #22 - │ │ ├── body[0] > host: + │ │ ├── body[0] > hold: │ │ │ ├── local: #20 │ │ │ ├── scope: %23 │ │ │ ├── value: "!" : handle @@ -2565,16 +2555,16 @@ │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: - │ │ ├── within: {handle, ptr} + │ │ ├── within: {handle, handle} │ │ ├── path: 0 │ │ └── base: local #15 : ptr │ └── value > call: │ ├── type: handle │ ├── fn: spawns$greet(%primitives$String) │ └── args[0] > deref: - │ ├── type: ptr + │ ├── type: handle │ └── ptr > offset: - │ ├── within: {handle, ptr} + │ ├── within: {handle, handle} │ ├── path: 1 │ └── base: local #15 : ptr ├── funcs[21] > func: @@ -2583,16 +2573,16 @@ │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: - │ │ ├── within: {handle, i32} + │ │ ├── within: {handle, ptr} │ │ ├── path: 0 │ │ └── base: local #28 : ptr │ └── value > call: │ ├── type: handle │ ├── fn: spawns$nameOf(&spawns$Person) │ └── args[0] > deref: - │ ├── type: i32 + │ ├── type: ptr │ └── ptr > offset: - │ ├── within: {handle, i32} + │ ├── within: {handle, ptr} │ ├── path: 1 │ └── base: local #28 : ptr ├── funcs[22] > func: @@ -2601,22 +2591,22 @@ │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: - │ │ ├── within: {{i32, handle, i64}, ptr, i64} + │ │ ├── within: {{handle, i64}, handle, i64} │ │ ├── path: 0 │ │ └── base: local #39 : ptr │ └── value > call: - │ ├── type: {i32, handle, i64} + │ ├── type: {handle, i64} │ ├── fn: spawns$born(%primitives$String, %primitives$Int) │ ├── args[0] > deref: - │ │ ├── type: ptr + │ │ ├── type: handle │ │ └── ptr > offset: - │ │ ├── within: {{i32, handle, i64}, ptr, i64} + │ │ ├── within: {{handle, i64}, handle, i64} │ │ ├── path: 1 │ │ └── base: local #39 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: - │ ├── within: {{i32, handle, i64}, ptr, i64} + │ ├── within: {{handle, i64}, handle, i64} │ ├── path: 2 │ └── base: local #39 : ptr ├── funcs[23] > func: @@ -2719,28 +2709,28 @@ │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: - │ │ ├── within: {i64, i32, i64, i64} + │ │ ├── within: {i64, ptr, i64, i64} │ │ ├── path: 0 │ │ └── base: local #75 : ptr │ └── value > call: │ ├── type: i64 │ ├── fn: spawns$watch(&spawns$Holder, %primitives$Int, %primitives$Int) │ ├── args[0] > deref: - │ │ ├── type: i32 + │ │ ├── type: ptr │ │ └── ptr > offset: - │ │ ├── within: {i64, i32, i64, i64} + │ │ ├── within: {i64, ptr, i64, i64} │ │ ├── path: 1 │ │ └── base: local #75 : ptr │ ├── args[1] > deref: │ │ ├── type: i64 │ │ └── ptr > offset: - │ │ ├── within: {i64, i32, i64, i64} + │ │ ├── within: {i64, ptr, i64, i64} │ │ ├── path: 2 │ │ └── base: local #75 : ptr │ └── args[2] > deref: │ ├── type: i64 │ └── ptr > offset: - │ ├── within: {i64, i32, i64, i64} + │ ├── within: {i64, ptr, i64, i64} │ ├── path: 3 │ └── base: local #75 : ptr ├── funcs[29] > func: @@ -2885,16 +2875,16 @@ │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: - │ │ ├── within: {handle, ptr} + │ │ ├── within: {handle, handle} │ │ ├── path: 0 │ │ └── base: local #119 : ptr │ └── value > call: │ ├── type: handle │ ├── fn: spawns$greet(%primitives$String) │ └── args[0] > deref: - │ ├── type: ptr + │ ├── type: handle │ └── ptr > offset: - │ ├── within: {handle, ptr} + │ ├── within: {handle, handle} │ ├── path: 1 │ └── base: local #119 : ptr ├── funcs[36] > func: @@ -2903,16 +2893,16 @@ │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: - │ │ ├── within: {handle, ptr} + │ │ ├── within: {handle, handle} │ │ ├── path: 0 │ │ └── base: local #125 : ptr │ └── value > call: │ ├── type: handle │ ├── fn: spawns$greet(%primitives$String) │ └── args[0] > deref: - │ ├── type: ptr + │ ├── type: handle │ └── ptr > offset: - │ ├── within: {handle, ptr} + │ ├── within: {handle, handle} │ ├── path: 1 │ └── base: local #125 : ptr ├── funcs[37] > func: diff --git a/tests/codegen/golden/texts.cgt b/tests/codegen/golden/texts.cgt index 664afff1..cec82c8c 100644 --- a/tests/codegen/golden/texts.cgt +++ b/tests/codegen/golden/texts.cgt @@ -2,36 +2,31 @@ ├── entry: texts$main() ├── layouts[0] > layout: │ ├── type: %primitives$String - │ ├── positions[0]: @0 (32 bytes) - │ └── positions[1]: text @0 + │ └── positions[0]: text @0 ├── layouts[1] > layout: │ ├── type: texts$Person - │ ├── positions[0]: @0 (40 bytes) - │ ├── positions[1]: @8 (32 bytes) - │ └── positions[2]: text @8 + │ └── positions[0]: text @0 ├── layouts[2] > layout: │ ├── type: texts$Tagged - │ ├── positions[0]: @0 (48 bytes) - │ ├── positions[1]: @16 (32 bytes) when [8]=0 - │ └── positions[2]: text @16 when [8]=0 + │ └── positions[0]: text @8 when [0]=0 ├── layouts[3] > layout: + │ ├── type: ^texts$Person + │ └── positions[0]: text @0 + ├── layouts[4] > layout: │ ├── type: texts$Holder - │ ├── positions[0]: @0 (56 bytes) - │ ├── positions[1]: @16 (40 bytes) when [8]=0 - │ ├── positions[2]: @24 (32 bytes) when [8]=0 - │ └── positions[3]: text @24 when [8]=0 + │ └── positions[0]: text @8 when [0]=0 ├── funcs[0] > func: │ ├── symbol: texts$main() │ ├── params: │ ├── ret: void │ └── body[0] > scope: │ ├── id: %2 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #1 │ │ ├── scope: %2 │ │ ├── value: "ab" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #6 │ │ ├── scope: %2 │ │ ├── value > runtime: @@ -42,7 +37,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @4 │ │ │ ├── result: #5 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #3 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "cd\n" : handle @@ -68,7 +63,7 @@ │ │ │ │ ├── type: handle │ │ │ │ ├── label: @11 │ │ │ │ ├── result: #12 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #10 │ │ │ │ │ ├── scope: %2 │ │ │ │ │ ├── value > runtime: @@ -79,7 +74,7 @@ │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── label: @8 │ │ │ │ │ │ ├── result: #9 - │ │ │ │ │ │ ├── body[0] > host: + │ │ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ │ │ ├── local: #7 │ │ │ │ │ │ │ ├── scope: %2 │ │ │ │ │ │ │ ├── value: "cd\n" : handle @@ -120,13 +115,13 @@ │ │ │ │ ├── type: handle │ │ │ │ ├── label: @14 │ │ │ │ ├── result: #15 - │ │ │ │ ├── body[0] > host: + │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #13 │ │ │ │ │ ├── scope: %2 │ │ │ │ │ ├── value > call: │ │ │ │ │ │ ├── type: handle - │ │ │ │ │ │ ├── fn: texts$twice(&%primitives$String) - │ │ │ │ │ │ └── args[0] > mint: address of #1 : ptr + │ │ │ │ │ │ ├── fn: texts$twice(%primitives$String) + │ │ │ │ │ │ └── args[0]: local #1 : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: │ │ │ │ ├── place: #15 @@ -135,7 +130,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @17 │ │ │ ├── result: #18 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #16 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "abab" : handle @@ -144,12 +139,12 @@ │ │ │ ├── place: #18 │ │ │ └── value: local #16 : handle │ │ └── right: 1 : i64 - │ ├── body[6] > host: + │ ├── body[6] > hold: │ │ ├── local: #19 │ │ ├── scope: %2 - │ │ ├── value > take: + │ │ ├── value > copy: │ │ │ ├── type: handle - │ │ │ ├── address: address of #6 : ptr + │ │ │ ├── value: local #6 : handle │ │ │ └── layout: %primitives$String │ │ └── layout: %primitives$String │ ├── body[7] > eval > call: @@ -166,7 +161,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @21 │ │ │ ├── result: #22 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #20 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "abcd\n" : handle @@ -193,7 +188,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @24 │ │ │ ├── result: #25 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #23 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "again" : handle @@ -202,36 +197,51 @@ │ │ │ ├── place: #25 │ │ │ └── value: local #23 : handle │ │ └── right: 1 : i64 - │ ├── body[10] > host: - │ │ ├── local: #29 + │ ├── body[10] > eval > call: + │ │ ├── type: void + │ │ ├── fn: texts$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > runtime: + │ │ │ ├── type: i64 + │ │ │ ├── fn: zane_text_equal + │ │ │ ├── args[0]: local #19 : handle + │ │ │ └── args[1] > expand: + │ │ │ ├── type: handle + │ │ │ ├── label: @27 + │ │ │ ├── result: #28 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #26 + │ │ │ │ ├── scope: %2 + │ │ │ │ ├── value: "abcd\n" : handle + │ │ │ │ └── layout: %primitives$String + │ │ │ └── body[1] > assign: + │ │ │ ├── place: #28 + │ │ │ └── value: local #26 : handle + │ │ └── right: 1 : i64 + │ ├── body[11] > hold: + │ │ ├── local: #32 │ │ ├── scope: %2 │ │ ├── value > call: - │ │ │ ├── type: {i32, handle} + │ │ │ ├── type: {handle} │ │ │ ├── fn: texts$Person(%primitives$String) │ │ │ └── args[0] > expand: - │ │ │ ├── type: ptr - │ │ │ ├── label: @27 - │ │ │ ├── result: #28 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #26 + │ │ │ ├── type: handle + │ │ │ ├── label: @30 + │ │ │ ├── result: #31 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #29 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "ada" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #28 - │ │ │ └── value: address of #26 : ptr - │ │ └── layout: texts$Person - │ ├── body[11] > let: - │ │ ├── local: #30 - │ │ └── value > mint: address of #29 : ptr - │ ├── body[12] > host: - │ │ ├── local: #31 - │ │ ├── scope: %2 - │ │ ├── value > take: - │ │ │ ├── type: {i32, handle} - │ │ │ ├── address: address of #29 : ptr - │ │ │ └── layout: texts$Person + │ │ │ ├── place: #31 + │ │ │ └── value: local #29 : handle │ │ └── layout: texts$Person + │ ├── body[12] > let: + │ │ ├── local: #33 + │ │ └── value: address of #32 : ptr │ ├── body[13] > eval > call: │ │ ├── type: void │ │ ├── fn: texts$check(%primitives$Bool) @@ -243,55 +253,68 @@ │ │ │ ├── fn: zane_text_equal │ │ │ ├── args[0] > expand: │ │ │ │ ├── type: handle - │ │ │ │ ├── label: @33 - │ │ │ │ ├── result: #34 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #32 + │ │ │ │ ├── label: @35 + │ │ │ │ ├── result: #36 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #34 │ │ │ │ │ ├── scope: %2 │ │ │ │ │ ├── value > call: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── fn: texts$greeting(this texts$Person) - │ │ │ │ │ │ └── args[0] > resolve: local #30 : i32 + │ │ │ │ │ │ └── args[0]: local #33 : ptr │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #34 - │ │ │ │ └── value: local #32 : handle + │ │ │ │ ├── place: #36 + │ │ │ │ └── value: local #34 : handle │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @36 - │ │ │ ├── result: #37 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #35 + │ │ │ ├── label: @38 + │ │ │ ├── result: #39 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #37 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "hello, ada" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #37 - │ │ │ └── value: local #35 : handle + │ │ │ ├── place: #39 + │ │ │ └── value: local #37 : handle │ │ └── right: 1 : i64 - │ ├── body[14] > overwrite: + │ ├── body[14] > hold: + │ │ ├── local: #40 + │ │ ├── scope: %2 + │ │ ├── value > copy: + │ │ │ ├── type: handle + │ │ │ ├── value > snapshot: + │ │ │ │ ├── type: handle + │ │ │ │ └── ptr > offset: + │ │ │ │ ├── within: {handle} + │ │ │ │ ├── path: 0 + │ │ │ │ └── base: address of #32 : ptr + │ │ │ └── layout: %primitives$String + │ │ └── layout: %primitives$String + │ ├── body[15] > overwrite: │ │ ├── address > offset: - │ │ │ ├── within: {i32, handle} - │ │ │ ├── path: 1 - │ │ │ └── base: address of #31 : ptr + │ │ │ ├── within: {handle} + │ │ │ ├── path: 0 + │ │ │ └── base: address of #32 : ptr │ │ ├── value > runtime: │ │ │ ├── type: handle │ │ │ ├── fn: zane_text_join │ │ │ ├── args[0]: local #1 : handle │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @39 - │ │ │ ├── result: #40 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #38 + │ │ │ ├── label: @42 + │ │ │ ├── result: #43 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #41 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "!" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #40 - │ │ │ └── value: local #38 : handle + │ │ │ ├── place: #43 + │ │ │ └── value: local #41 : handle │ │ └── layout: %primitives$String - │ ├── body[15] > eval > call: + │ ├── body[16] > eval > call: │ │ ├── type: void │ │ ├── fn: texts$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -302,38 +325,32 @@ │ │ │ ├── fn: zane_text_equal │ │ │ ├── args[0] > expand: │ │ │ │ ├── type: handle - │ │ │ │ ├── label: @42 - │ │ │ │ ├── result: #43 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #41 + │ │ │ │ ├── label: @45 + │ │ │ │ ├── result: #46 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #44 │ │ │ │ │ ├── scope: %2 │ │ │ │ │ ├── value > call: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── fn: texts$greeting(this texts$Person) - │ │ │ │ │ │ └── args[0] > resolve: local #30 : i32 + │ │ │ │ │ │ └── args[0]: local #33 : ptr │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #43 - │ │ │ │ └── value: local #41 : handle + │ │ │ │ ├── place: #46 + │ │ │ │ └── value: local #44 : handle │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @45 - │ │ │ ├── result: #46 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #44 + │ │ │ ├── label: @48 + │ │ │ ├── result: #49 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #47 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "hello, ab!" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #46 - │ │ │ └── value: local #44 : handle + │ │ │ ├── place: #49 + │ │ │ └── value: local #47 : handle │ │ └── right: 1 : i64 - │ ├── body[16] > let: - │ │ ├── local: #47 - │ │ └── value > mint > offset: - │ │ ├── within: {i32, handle} - │ │ ├── path: 1 - │ │ └── base: address of #31 : ptr │ ├── body[17] > eval > call: │ │ ├── type: void │ │ ├── fn: texts$check(%primitives$Bool) @@ -343,60 +360,52 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > deref: - │ │ │ │ ├── type: handle - │ │ │ │ └── ptr > resolve: local #47 : i32 + │ │ │ ├── args[0]: local #40 : handle │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @49 - │ │ │ ├── result: #50 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #48 + │ │ │ ├── label: @51 + │ │ │ ├── result: #52 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #50 │ │ │ │ ├── scope: %2 - │ │ │ │ ├── value: "ab!" : handle + │ │ │ │ ├── value: "ada" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #50 - │ │ │ └── value: local #48 : handle + │ │ │ ├── place: #52 + │ │ │ └── value: local #50 : handle │ │ └── right: 1 : i64 - │ ├── body[18] > host: - │ │ ├── local: #51 + │ ├── body[18] > hold: + │ │ ├── local: #53 │ │ ├── scope: %2 - │ │ ├── value > record: - │ │ │ ├── type: {i32, } - │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ └── members[1] > member 1 > case: - │ │ │ ├── type: - │ │ │ ├── index: 0 - │ │ │ └── payload: "abcd?" : handle + │ │ ├── value > case: + │ │ │ ├── type: + │ │ │ ├── index: 0 + │ │ │ └── payload: "abcd?" : handle │ │ └── layout: texts$Tagged │ ├── body[19] > eval > call: │ │ ├── type: void │ │ ├── fn: texts$check(%primitives$Bool) │ │ └── args[0] > expand: │ │ ├── type: i1 - │ │ ├── label: @52 - │ │ ├── result: #53 + │ │ ├── label: @54 + │ │ ├── result: #55 │ │ ├── body[0] > let: - │ │ │ ├── local: #54 - │ │ │ └── value: address of #51 : ptr + │ │ │ ├── local: #56 + │ │ │ └── value: address of #53 : ptr │ │ └── body[1] > switch: - │ │ ├── value > member: + │ │ ├── value > deref: │ │ │ ├── type: - │ │ │ ├── index: 1 - │ │ │ └── value > deref: - │ │ │ ├── type: {i32, } - │ │ │ └── ptr: local #54 : ptr + │ │ │ └── ptr: local #56 : ptr │ │ ├── cases[0] > case 0[0] > let: - │ │ │ ├── local: #55 + │ │ │ ├── local: #57 │ │ │ └── value > offset: - │ │ │ ├── within: {i32, } - │ │ │ ├── path: 1.0 - │ │ │ └── base: local #54 : ptr + │ │ │ ├── within: + │ │ │ ├── path: 0 + │ │ │ └── base: local #56 : ptr │ │ ├── cases[0] > case 0[1] > scope: - │ │ │ ├── id: %59 + │ │ │ ├── id: %61 │ │ │ ├── body[0] > assign: - │ │ │ │ ├── place: #53 + │ │ │ │ ├── place: #55 │ │ │ │ └── value > binary: │ │ │ │ ├── type: i1 │ │ │ │ ├── op: == @@ -405,134 +414,67 @@ │ │ │ │ │ ├── fn: zane_text_equal │ │ │ │ │ ├── args[0] > deref: │ │ │ │ │ │ ├── type: handle - │ │ │ │ │ │ └── ptr: local #55 : ptr + │ │ │ │ │ │ └── ptr: local #57 : ptr │ │ │ │ │ └── args[1] > expand: │ │ │ │ │ ├── type: handle - │ │ │ │ │ ├── label: @57 - │ │ │ │ │ ├── result: #58 - │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ ├── local: #56 - │ │ │ │ │ │ ├── scope: %59 + │ │ │ │ │ ├── label: @59 + │ │ │ │ │ ├── result: #60 + │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ ├── local: #58 + │ │ │ │ │ │ ├── scope: %61 │ │ │ │ │ │ ├── value: "abcd?" : handle │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ ├── place: #58 - │ │ │ │ │ └── value: local #56 : handle + │ │ │ │ │ ├── place: #60 + │ │ │ │ │ └── value: local #58 : handle │ │ │ │ └── right: 1 : i64 - │ │ │ └── body[1]: leave @52 + │ │ │ └── body[1]: leave @54 │ │ ├── cases[1] > case 1[0] > let: - │ │ │ ├── local: #60 + │ │ │ ├── local: #62 │ │ │ └── value > offset: - │ │ │ ├── within: {i32, } - │ │ │ ├── path: 1.1 - │ │ │ └── base: local #54 : ptr + │ │ │ ├── within: + │ │ │ ├── path: 1 + │ │ │ └── base: local #56 : ptr │ │ ├── cases[1] > case 1[1] > assign: - │ │ │ ├── place: #53 + │ │ │ ├── place: #55 │ │ │ └── value: false : i1 - │ │ └── cases[1] > case 1[2]: leave @52 + │ │ └── cases[1] > case 1[2]: leave @54 │ ├── body[20] > overwrite: - │ │ ├── address: address of #51 : ptr - │ │ ├── value > record: - │ │ │ ├── type: {i32, } - │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ └── members[1] > member 1 > case: - │ │ │ ├── type: - │ │ │ ├── index: 1 - │ │ │ └── payload: unit : void + │ │ ├── address: address of #53 : ptr + │ │ ├── value > case: + │ │ │ ├── type: + │ │ │ ├── index: 1 + │ │ │ └── payload: unit : void │ │ └── layout: texts$Tagged - │ ├── body[21] > host: - │ │ ├── local: #64 + │ ├── body[21] > hold: + │ │ ├── local: #66 │ │ ├── scope: %2 │ │ ├── value > call: - │ │ │ ├── type: {i32, handle} + │ │ │ ├── type: {handle} │ │ │ ├── fn: texts$Person(%primitives$String) │ │ │ └── args[0] > expand: - │ │ │ ├── type: ptr - │ │ │ ├── label: @62 - │ │ │ ├── result: #63 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #61 + │ │ │ ├── type: handle + │ │ │ ├── label: @64 + │ │ │ ├── result: #65 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #63 │ │ │ │ ├── scope: %2 - │ │ │ │ ├── value: "floating" : handle + │ │ │ │ ├── value: "held" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #63 - │ │ │ └── value: address of #61 : ptr - │ │ └── layout: texts$Person - │ ├── body[22] > let: - │ │ ├── local: #65 - │ │ └── value > mint: address of #64 : ptr - │ ├── body[23] > host: - │ │ ├── local: #66 - │ │ ├── scope: %2 - │ │ ├── value > record: - │ │ │ ├── type: {i32, <{i32, handle} | void>} - │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ └── members[1] > member 1 > case: - │ │ │ ├── type: <{i32, handle} | void> - │ │ │ ├── index: 0 - │ │ │ └── payload > take: - │ │ │ ├── type: {i32, handle} - │ │ │ ├── address: address of #64 : ptr - │ │ │ └── layout: texts$Person - │ │ └── layout: texts$Holder - │ ├── body[24] > overwrite: - │ │ ├── address: address of #66 : ptr - │ │ ├── value > record: - │ │ │ ├── type: {i32, <{i32, handle} | void>} - │ │ │ ├── members[0] > member 0: 0 : i32 - │ │ │ └── members[1] > member 1 > case: - │ │ │ ├── type: <{i32, handle} | void> - │ │ │ ├── index: 1 - │ │ │ └── payload: unit : void - │ │ └── layout: texts$Holder - │ ├── body[25] > eval > call: - │ │ ├── type: void - │ │ ├── fn: texts$check(%primitives$Bool) - │ │ └── args[0] > binary: - │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > runtime: - │ │ │ ├── type: i64 - │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: handle - │ │ │ │ ├── label: @68 - │ │ │ │ ├── result: #69 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #67 - │ │ │ │ │ ├── scope: %2 - │ │ │ │ │ ├── value > call: - │ │ │ │ │ │ ├── type: handle - │ │ │ │ │ │ ├── fn: texts$greeting(this texts$Person) - │ │ │ │ │ │ └── args[0] > resolve: local #65 : i32 - │ │ │ │ │ └── layout: %primitives$String - │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #69 - │ │ │ │ └── value: local #67 : handle - │ │ │ └── args[1] > expand: - │ │ │ ├── type: handle - │ │ │ ├── label: @71 - │ │ │ ├── result: #72 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #70 - │ │ │ │ ├── scope: %2 - │ │ │ │ ├── value: "hello, floating" : handle - │ │ │ │ └── layout: %primitives$String - │ │ │ └── body[1] > assign: - │ │ │ ├── place: #72 - │ │ │ └── value: local #70 : handle - │ │ └── right: 1 : i64 - │ ├── body[26] > eval > call: + │ │ │ ├── place: #65 + │ │ │ └── value: local #63 : handle + │ │ └── layout: ^texts$Person + │ ├── body[22] > eval > call: │ │ ├── type: void │ │ ├── fn: texts$rename(this texts$Person, %primitives$String) - │ │ ├── args[0] > resolve: local #65 : i32 + │ │ ├── args[0]: address of #66 : ptr │ │ └── args[1] > expand: - │ │ ├── type: ptr - │ │ ├── label: @77 - │ │ ├── result: #78 - │ │ ├── body[0] > host: - │ │ │ ├── local: #76 + │ │ ├── type: handle + │ │ ├── label: @71 + │ │ ├── result: #72 + │ │ ├── body[0] > hold: + │ │ │ ├── local: #70 │ │ │ ├── scope: %2 │ │ │ ├── value > runtime: │ │ │ │ ├── type: handle @@ -540,66 +482,116 @@ │ │ │ │ ├── args[0]: local #1 : handle │ │ │ │ └── args[1] > expand: │ │ │ │ ├── type: handle - │ │ │ │ ├── label: @74 - │ │ │ │ ├── result: #75 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #73 + │ │ │ │ ├── label: @68 + │ │ │ │ ├── result: #69 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #67 │ │ │ │ │ ├── scope: %2 - │ │ │ │ │ ├── value: "!" : handle + │ │ │ │ │ ├── value: "?" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #75 - │ │ │ │ └── value: local #73 : handle + │ │ │ │ ├── place: #69 + │ │ │ │ └── value: local #67 : handle │ │ │ └── layout: %primitives$String │ │ └── body[1] > assign: - │ │ ├── place: #78 - │ │ └── value: address of #76 : ptr - │ ├── body[27] > eval > call: + │ │ ├── place: #72 + │ │ └── value: local #70 : handle + │ ├── body[23] > hold: + │ │ ├── local: #73 + │ │ ├── scope: %2 + │ │ ├── value > case: + │ │ │ ├── type: <{handle} | void> + │ │ │ ├── index: 0 + │ │ │ └── payload > take: + │ │ │ ├── type: {handle} + │ │ │ ├── address: address of #66 : ptr + │ │ │ └── layout: texts$Person + │ │ └── layout: texts$Holder + │ ├── body[24] > eval > call: │ │ ├── type: void │ │ ├── fn: texts$check(%primitives$Bool) - │ │ └── args[0] > binary: + │ │ └── args[0] > expand: │ │ ├── type: i1 - │ │ ├── op: == - │ │ ├── left > runtime: - │ │ │ ├── type: i64 - │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0] > expand: - │ │ │ │ ├── type: handle - │ │ │ │ ├── label: @80 - │ │ │ │ ├── result: #81 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #79 - │ │ │ │ │ ├── scope: %2 - │ │ │ │ │ ├── value > call: - │ │ │ │ │ │ ├── type: handle - │ │ │ │ │ │ ├── fn: texts$greeting(this texts$Person) - │ │ │ │ │ │ └── args[0] > resolve: local #65 : i32 - │ │ │ │ │ └── layout: %primitives$String - │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #81 - │ │ │ │ └── value: local #79 : handle - │ │ │ └── args[1] > expand: - │ │ │ ├── type: handle - │ │ │ ├── label: @83 - │ │ │ ├── result: #84 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #82 - │ │ │ │ ├── scope: %2 - │ │ │ │ ├── value: "hello, ab!" : handle - │ │ │ │ └── layout: %primitives$String - │ │ │ └── body[1] > assign: - │ │ │ ├── place: #84 - │ │ │ └── value: local #82 : handle - │ │ └── right: 1 : i64 - │ ├── body[28] > host: - │ │ ├── local: #85 + │ │ ├── label: @74 + │ │ ├── result: #75 + │ │ ├── body[0] > let: + │ │ │ ├── local: #76 + │ │ │ └── value: address of #73 : ptr + │ │ └── body[1] > switch: + │ │ ├── value > deref: + │ │ │ ├── type: <{handle} | void> + │ │ │ └── ptr: local #76 : ptr + │ │ ├── cases[0] > case 0[0] > let: + │ │ │ ├── local: #77 + │ │ │ └── value > offset: + │ │ │ ├── within: <{handle} | void> + │ │ │ ├── path: 0 + │ │ │ └── base: local #76 : ptr + │ │ ├── cases[0] > case 0[1] > scope: + │ │ │ ├── id: %81 + │ │ │ ├── body[0] > assign: + │ │ │ │ ├── place: #75 + │ │ │ │ └── value > binary: + │ │ │ │ ├── type: i1 + │ │ │ │ ├── op: == + │ │ │ │ ├── left > runtime: + │ │ │ │ │ ├── type: i64 + │ │ │ │ │ ├── fn: zane_text_equal + │ │ │ │ │ ├── args[0] > expand: + │ │ │ │ │ │ ├── type: handle + │ │ │ │ │ │ ├── label: @79 + │ │ │ │ │ │ ├── result: #80 + │ │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ │ ├── local: #78 + │ │ │ │ │ │ │ ├── scope: %81 + │ │ │ │ │ │ │ ├── value > call: + │ │ │ │ │ │ │ │ ├── type: handle + │ │ │ │ │ │ │ │ ├── fn: texts$greeting(this texts$Person) + │ │ │ │ │ │ │ │ └── args[0]: local #77 : ptr + │ │ │ │ │ │ │ └── layout: %primitives$String + │ │ │ │ │ │ └── body[1] > assign: + │ │ │ │ │ │ ├── place: #80 + │ │ │ │ │ │ └── value: local #78 : handle + │ │ │ │ │ └── args[1] > expand: + │ │ │ │ │ ├── type: handle + │ │ │ │ │ ├── label: @83 + │ │ │ │ │ ├── result: #84 + │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ ├── local: #82 + │ │ │ │ │ │ ├── scope: %81 + │ │ │ │ │ │ ├── value: "hello, ab?" : handle + │ │ │ │ │ │ └── layout: %primitives$String + │ │ │ │ │ └── body[1] > assign: + │ │ │ │ │ ├── place: #84 + │ │ │ │ │ └── value: local #82 : handle + │ │ │ │ └── right: 1 : i64 + │ │ │ └── body[1]: leave @74 + │ │ ├── cases[1] > case 1[0] > let: + │ │ │ ├── local: #85 + │ │ │ └── value > offset: + │ │ │ ├── within: <{handle} | void> + │ │ │ ├── path: 1 + │ │ │ └── base: local #76 : ptr + │ │ ├── cases[1] > case 1[1] > assign: + │ │ │ ├── place: #75 + │ │ │ └── value: false : i1 + │ │ └── cases[1] > case 1[2]: leave @74 + │ ├── body[25] > overwrite: + │ │ ├── address: address of #73 : ptr + │ │ ├── value > case: + │ │ │ ├── type: <{handle} | void> + │ │ │ ├── index: 1 + │ │ │ └── payload: unit : void + │ │ └── layout: texts$Holder + │ ├── body[26] > hold: + │ │ ├── local: #86 │ │ ├── scope: %2 │ │ ├── value > call: │ │ │ ├── type: handle - │ │ │ ├── fn: texts$twice(&%primitives$String) - │ │ │ └── args[0] > mint: address of #1 : ptr + │ │ │ ├── fn: texts$twice(%primitives$String) + │ │ │ └── args[0]: local #1 : handle │ │ └── layout: %primitives$String - │ ├── body[29] > eval > call: + │ ├── body[27] > eval > call: │ │ ├── type: void │ │ ├── fn: texts$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -610,91 +602,91 @@ │ │ │ ├── fn: zane_text_equal │ │ │ ├── args[0] > expand: │ │ │ │ ├── type: handle - │ │ │ │ ├── label: @95 - │ │ │ │ ├── result: #96 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #94 + │ │ │ │ ├── label: @96 + │ │ │ │ ├── result: #97 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #95 │ │ │ │ │ ├── scope: %2 │ │ │ │ │ ├── value > expand: │ │ │ │ │ │ ├── type: handle - │ │ │ │ │ │ ├── label: @89 - │ │ │ │ │ │ ├── result: #90 + │ │ │ │ │ │ ├── label: @90 + │ │ │ │ │ │ ├── result: #91 │ │ │ │ │ │ ├── body[0] > let: - │ │ │ │ │ │ │ ├── local: #91 + │ │ │ │ │ │ │ ├── local: #92 │ │ │ │ │ │ │ └── value > call: │ │ │ │ │ │ │ ├── type: │ │ │ │ │ │ │ ├── fn: texts$refuse(%primitives$String) │ │ │ │ │ │ │ └── args[0] > expand: - │ │ │ │ │ │ │ ├── type: ptr - │ │ │ │ │ │ │ ├── label: @87 - │ │ │ │ │ │ │ ├── result: #88 - │ │ │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ │ │ ├── local: #86 + │ │ │ │ │ │ │ ├── type: handle + │ │ │ │ │ │ │ ├── label: @88 + │ │ │ │ │ │ │ ├── result: #89 + │ │ │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ │ │ ├── local: #87 │ │ │ │ │ │ │ │ ├── scope: %2 │ │ │ │ │ │ │ │ ├── value: "no" : handle │ │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ │ ├── place: #88 - │ │ │ │ │ │ │ └── value: address of #86 : ptr + │ │ │ │ │ │ │ ├── place: #89 + │ │ │ │ │ │ │ └── value: local #87 : handle │ │ │ │ │ │ └── body[1] > switch: - │ │ │ │ │ │ ├── value: local #91 : + │ │ │ │ │ │ ├── value: local #92 : │ │ │ │ │ │ ├── cases[0] > case 0[0] > assign: - │ │ │ │ │ │ │ ├── place: #90 + │ │ │ │ │ │ │ ├── place: #91 │ │ │ │ │ │ │ └── value > payload: │ │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ │ ├── index: 0 - │ │ │ │ │ │ │ └── value: local #91 : - │ │ │ │ │ │ ├── cases[0] > case 0[1]: leave @89 + │ │ │ │ │ │ │ └── value: local #92 : + │ │ │ │ │ │ ├── cases[0] > case 0[1]: leave @90 │ │ │ │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ │ │ │ ├── id: %93 - │ │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ │ ├── local: #92 - │ │ │ │ │ │ │ ├── scope: %93 + │ │ │ │ │ │ ├── id: %94 + │ │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ │ ├── local: #93 + │ │ │ │ │ │ │ ├── scope: %94 │ │ │ │ │ │ │ ├── value > payload: │ │ │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ │ │ ├── index: 1 - │ │ │ │ │ │ │ │ └── value: local #91 : + │ │ │ │ │ │ │ │ └── value: local #92 : │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ ├── body[1] > assign: - │ │ │ │ │ │ │ ├── place: #90 + │ │ │ │ │ │ │ ├── place: #91 │ │ │ │ │ │ │ └── value > escape: - │ │ │ │ │ │ │ ├── exit: @89 - │ │ │ │ │ │ │ ├── value > take: + │ │ │ │ │ │ │ ├── exit: @90 + │ │ │ │ │ │ │ ├── value > copy: │ │ │ │ │ │ │ │ ├── type: handle - │ │ │ │ │ │ │ │ ├── address: address of #92 : ptr + │ │ │ │ │ │ │ │ ├── value: local #93 : handle │ │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ │ └── layout: %primitives$String - │ │ │ │ │ │ └── body[2]: leave @89 + │ │ │ │ │ │ └── body[2]: leave @90 │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #96 - │ │ │ │ └── value: local #94 : handle + │ │ │ │ ├── place: #97 + │ │ │ │ └── value: local #95 : handle │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @98 - │ │ │ ├── result: #99 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #97 + │ │ │ ├── label: @99 + │ │ │ ├── result: #100 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #98 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "no!" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #99 - │ │ │ └── value: local #97 : handle + │ │ │ ├── place: #100 + │ │ │ └── value: local #98 : handle │ │ └── right: 1 : i64 - │ ├── body[30] > host: - │ │ ├── local: #100 + │ ├── body[28] > hold: + │ │ ├── local: #101 │ │ ├── scope: %2 │ │ ├── value: "" : handle │ │ └── layout: %primitives$String - │ ├── body[31] > let: - │ │ ├── local: #101 + │ ├── body[29] > let: + │ │ ├── local: #102 │ │ └── value: 1 : i64 - │ ├── body[32] > eval > expand: + │ ├── body[30] > eval > expand: │ │ ├── type: void - │ │ ├── label: @103 + │ │ ├── label: @104 │ │ ├── body[0] > let: - │ │ │ ├── local: #102 + │ │ │ ├── local: #103 │ │ │ └── value: 10000 : i64 │ │ ├── body[1] > repeat: │ │ │ ├── count > binary: @@ -703,64 +695,64 @@ │ │ │ │ ├── left > binary: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #102 : i64 + │ │ │ │ │ ├── left: local #103 : i64 │ │ │ │ │ └── right > flip: │ │ │ │ │ ├── type: i64 - │ │ │ │ │ └── value: local #101 : i64 + │ │ │ │ │ └── value: local #102 : i64 │ │ │ │ └── right: 1 : i64 │ │ │ ├── body[0] > if: │ │ │ │ ├── cond: true : i1 │ │ │ │ └── body[0] > scope: - │ │ │ │ ├── id: %109 - │ │ │ │ ├── body[0] > host: - │ │ │ │ │ ├── local: #110 - │ │ │ │ │ ├── scope: %109 + │ │ │ │ ├── id: %110 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #111 + │ │ │ │ │ ├── scope: %110 │ │ │ │ │ ├── value > runtime: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── fn: zane_text_join │ │ │ │ │ │ ├── args[0]: local #1 : handle │ │ │ │ │ │ └── args[1] > expand: │ │ │ │ │ │ ├── type: handle - │ │ │ │ │ │ ├── label: @107 - │ │ │ │ │ │ ├── result: #108 - │ │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ │ ├── local: #106 - │ │ │ │ │ │ │ ├── scope: %109 + │ │ │ │ │ │ ├── label: @108 + │ │ │ │ │ │ ├── result: #109 + │ │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ │ ├── local: #107 + │ │ │ │ │ │ │ ├── scope: %110 │ │ │ │ │ │ │ ├── value: "y" : handle │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ ├── place: #108 - │ │ │ │ │ │ └── value: local #106 : handle + │ │ │ │ │ │ ├── place: #109 + │ │ │ │ │ │ └── value: local #107 : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > overwrite: - │ │ │ │ ├── address: address of #100 : ptr + │ │ │ │ ├── address: address of #101 : ptr │ │ │ │ ├── value > runtime: │ │ │ │ │ ├── type: handle │ │ │ │ │ ├── fn: zane_text_join - │ │ │ │ │ ├── args[0]: local #110 : handle + │ │ │ │ │ ├── args[0]: local #111 : handle │ │ │ │ │ └── args[1] > expand: │ │ │ │ │ ├── type: handle - │ │ │ │ │ ├── label: @112 - │ │ │ │ │ ├── result: #113 - │ │ │ │ │ ├── body[0] > host: - │ │ │ │ │ │ ├── local: #111 - │ │ │ │ │ │ ├── scope: %109 + │ │ │ │ │ ├── label: @113 + │ │ │ │ │ ├── result: #114 + │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ ├── local: #112 + │ │ │ │ │ │ ├── scope: %110 │ │ │ │ │ │ ├── value: "z" : handle │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ ├── place: #113 - │ │ │ │ │ └── value: local #111 : handle + │ │ │ │ │ ├── place: #114 + │ │ │ │ │ └── value: local #112 : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #101 + │ │ │ ├── place: #102 │ │ │ └── value > binary: │ │ │ ├── type: i64 │ │ │ ├── op: + - │ │ │ ├── left: local #101 : i64 + │ │ │ ├── left: local #102 : i64 │ │ │ └── right: 1 : i64 │ │ ├── body[2] > eval: unit : void - │ │ └── body[3]: leave @103 - │ ├── body[33] > eval > call: + │ │ └── body[3]: leave @104 + │ ├── body[31] > eval > call: │ │ ├── type: void │ │ ├── fn: texts$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -769,33 +761,33 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0]: local #100 : handle + │ │ │ ├── args[0]: local #101 : handle │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @115 - │ │ │ ├── result: #116 - │ │ │ ├── body[0] > host: - │ │ │ │ ├── local: #114 + │ │ │ ├── label: @116 + │ │ │ ├── result: #117 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #115 │ │ │ │ ├── scope: %2 │ │ │ │ ├── value: "abyz" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #116 - │ │ │ └── value: local #114 : handle + │ │ │ ├── place: #117 + │ │ │ └── value: local #115 : handle │ │ └── right: 1 : i64 - │ └── body[34] > return: unit : void + │ └── body[32] > return: unit : void ├── funcs[1] > func: │ ├── symbol: texts$check(%primitives$Bool) │ ├── params[0]: local #1 : i1 │ ├── ret: void │ └── body[0] > scope: │ ├── id: %3 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %3 │ │ ├── value: "yes\n" : handle │ │ └── layout: %primitives$String - │ ├── body[1] > host: + │ ├── body[1] > hold: │ │ ├── local: #4 │ │ ├── scope: %3 │ │ ├── value: "no\n" : handle @@ -816,33 +808,28 @@ │ │ └── args[0]: local #4 : handle │ └── body[4] > return: unit : void ├── funcs[2] > func: - │ ├── symbol: texts$twice(&%primitives$String) - │ ├── params[0]: local #1 : i32 + │ ├── symbol: texts$twice(%primitives$String) + │ ├── params[0]: local #1 : handle │ ├── ret: handle │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > runtime: │ │ ├── type: handle │ │ ├── fn: zane_text_join - │ │ ├── args[0] > deref: - │ │ │ ├── type: handle - │ │ │ └── ptr > resolve: local #1 : i32 - │ │ └── args[1] > deref: - │ │ ├── type: handle - │ │ └── ptr > resolve: local #1 : i32 + │ │ ├── args[0]: local #1 : handle + │ │ └── args[1]: local #1 : handle │ └── layout: %primitives$String ├── funcs[3] > func: │ ├── symbol: texts$Person(%primitives$String) - │ ├── params[0]: local #1 : ptr - │ ├── ret: {i32, handle} + │ ├── params[0]: local #1 : handle + │ ├── ret: {handle} │ └── body[0] > return > escape: │ ├── exit: function │ ├── value > record: - │ │ ├── type: {i32, handle} - │ │ ├── members[0] > member 0: 0 : i32 - │ │ └── members[1] > member 1 > take: + │ │ ├── type: {handle} + │ │ └── members[0] > member 0 > copy: │ │ ├── type: handle - │ │ ├── address: local #1 : ptr + │ │ ├── value: local #1 : handle │ │ └── layout: %primitives$String │ └── layout: texts$Person ├── funcs[4] > func: @@ -860,7 +847,7 @@ │ │ │ ├── type: handle │ │ │ ├── label: @3 │ │ │ ├── result: #4 - │ │ │ ├── body[0] > host: + │ │ │ ├── body[0] > hold: │ │ │ │ ├── local: #2 │ │ │ │ ├── scope: %5 │ │ │ │ ├── value: "hello, " : handle @@ -868,32 +855,32 @@ │ │ │ └── body[1] > assign: │ │ │ ├── place: #4 │ │ │ └── value: local #2 : handle - │ │ └── args[1] > member: + │ │ └── args[1] > snapshot: │ │ ├── type: handle - │ │ ├── index: 1 - │ │ └── value > deref: - │ │ ├── type: {i32, handle} - │ │ └── ptr: local #1 : ptr + │ │ └── ptr > offset: + │ │ ├── within: {handle} + │ │ ├── path: 0 + │ │ └── base: local #1 : ptr │ └── layout: %primitives$String ├── funcs[5] > func: │ ├── symbol: texts$rename(this texts$Person, %primitives$String) │ ├── params[0]: local #1 : ptr - │ ├── params[1]: local #2 : ptr + │ ├── params[1]: local #2 : handle │ ├── ret: void │ ├── body[0] > overwrite: │ │ ├── address > offset: - │ │ │ ├── within: {i32, handle} - │ │ │ ├── path: 1 + │ │ │ ├── within: {handle} + │ │ │ ├── path: 0 │ │ │ └── base: local #1 : ptr - │ │ ├── value > take: + │ │ ├── value > copy: │ │ │ ├── type: handle - │ │ │ ├── address: local #2 : ptr + │ │ │ ├── value: local #2 : handle │ │ │ └── layout: %primitives$String │ │ └── layout: %primitives$String │ └── body[1] > return: unit : void └── funcs[6] > func: ├── symbol: texts$refuse(%primitives$String) - ├── params[0]: local #1 : ptr + ├── params[0]: local #1 : handle ├── ret: └── body[0] > scope: ├── id: %5 @@ -905,14 +892,12 @@ ├── value > runtime: │ ├── type: handle │ ├── fn: zane_text_join - │ ├── args[0] > deref: - │ │ ├── type: handle - │ │ └── ptr: local #1 : ptr + │ ├── args[0]: local #1 : handle │ └── args[1] > expand: │ ├── type: handle │ ├── label: @3 │ ├── result: #4 - │ ├── body[0] > host: + │ ├── body[0] > hold: │ │ ├── local: #2 │ │ ├── scope: %5 │ │ ├── value: "!" : handle diff --git a/tests/gen/gen_rules.ml b/tests/gen/gen_rules.ml index 77779dd7..4ab32cdb 100644 --- a/tests/gen/gen_rules.ml +++ b/tests/gen/gen_rules.ml @@ -150,7 +150,7 @@ let semantics () = (* runtime *) -let parts = [ "main"; "arena"; "anchor"; "block"; "value"; "list"; "slot"; "spawn"; "snapshot" ] +let parts = [ "main"; "arena"; "block"; "value"; "list"; "slot"; "spawn"; "snapshot" ] let runtime () = let sources = String.concat " " (List.map (Printf.sprintf "../../runtime/%s.c") parts) in diff --git a/tests/grammar/golden/parser.conflicts.census b/tests/grammar/golden/parser.conflicts.census index f8346a0b..7b28287d 100644 --- a/tests/grammar/golden/parser.conflicts.census +++ b/tests/grammar/golden/parser.conflicts.census @@ -1,15 +1,21 @@ -56 conflict states +62 conflict states 1 shift/reduce [LESS] abort_ret_type -> type_expr QSTNMARK AMPERSAND AT LIDENT DOLLAR UIDENT 1 shift/reduce [LESS] abort_ret_type -> type_expr QSTNMARK AMPERSAND LIDENT DOLLAR UIDENT 1 shift/reduce [LESS] abort_ret_type -> type_expr QSTNMARK AMPERSAND UIDENT 1 shift/reduce [LESS] abort_ret_type -> type_expr QSTNMARK AT LIDENT DOLLAR UIDENT + 1 shift/reduce [LESS] abort_ret_type -> type_expr QSTNMARK CARET AT LIDENT DOLLAR UIDENT + 1 shift/reduce [LESS] abort_ret_type -> type_expr QSTNMARK CARET LIDENT DOLLAR UIDENT + 1 shift/reduce [LESS] abort_ret_type -> type_expr QSTNMARK CARET UIDENT 1 shift/reduce [LESS] abort_ret_type -> type_expr QSTNMARK LIDENT DOLLAR UIDENT 1 shift/reduce [LESS] abort_ret_type -> type_expr QSTNMARK UIDENT 1 shift/reduce [LESS] type_expr -> AMPERSAND AT LIDENT DOLLAR UIDENT 1 shift/reduce [LESS] type_expr -> AMPERSAND LIDENT DOLLAR UIDENT 1 shift/reduce [LESS] type_expr -> AMPERSAND UIDENT 1 shift/reduce [LESS] type_expr -> AT LIDENT DOLLAR UIDENT + 1 shift/reduce [LESS] type_expr -> CARET AT LIDENT DOLLAR UIDENT + 1 shift/reduce [LESS] type_expr -> CARET LIDENT DOLLAR UIDENT + 1 shift/reduce [LESS] type_expr -> CARET UIDENT 1 shift/reduce [LESS] type_expr -> LIDENT DOLLAR UIDENT 1 shift/reduce [LESS] type_expr -> UIDENT 2 shift/reduce [LPAREN] app -> AT LIDENT DOLLAR UIDENT DOT LIDENT diff --git a/tests/grammar/golden/stock.conflicts.census b/tests/grammar/golden/stock.conflicts.census index 02669e31..967ea289 100644 --- a/tests/grammar/golden/stock.conflicts.census +++ b/tests/grammar/golden/stock.conflicts.census @@ -1,6 +1,6 @@ -49 conflict states +55 conflict states - 12 shift/reduce [LESS] loption(generics) -> + 18 shift/reduce [LESS] loption(generics) -> 2 shift/reduce [LPAREN] app -> AT LIDENT DOLLAR UIDENT DOT LIDENT 2 shift/reduce [LPAREN] app -> LIDENT DOLLAR UIDENT DOT LIDENT 2 shift/reduce [LPAREN] app -> UIDENT DOT LIDENT diff --git a/tests/parser/golden/main.sst.spans b/tests/parser/golden/main.sst.spans index f41d73d8..d4b19db2 100644 --- a/tests/parser/golden/main.sst.spans +++ b/tests/parser/golden/main.sst.spans @@ -1023,7 +1023,7 @@ package | package main; /// Every im … ettled"); return Unit(); } mould Struct | struct { env &Environment; position Int; } body_field | env &Environment; name env | env - type_expr Guest | &Environment + type_expr Reference | &Environment type_expr Path | Environment name_type | Environment ident Environment | Environment @@ -1042,7 +1042,7 @@ package | package main; /// Every im … ettled"); return Unit(); } param | env &Environment name env | env param_type Concrete | &Environment - type_expr Guest | &Environment + type_expr Reference | &Environment type_expr Path | Environment name_type | Environment ident Environment | Environment @@ -1099,7 +1099,7 @@ package | package main; /// Every im … ettled"); return Unit(); } param | env &Environment name env | env param_type Concrete | &Environment - type_expr Guest | &Environment + type_expr Reference | &Environment type_expr Path | Environment name_type | Environment ident Environment | Environment diff --git a/tests/runtime/anchors.c b/tests/runtime/anchors.c deleted file mode 100644 index 2c3b971c..00000000 --- a/tests/runtime/anchors.c +++ /dev/null @@ -1,84 +0,0 @@ -/* The runtime's anchors and tethers, tested in C on their own - (docs/design/lowering.md L17), over hand-written layouts. Each check prints `yes` - when it holds and `no` when it does not. */ - -#include "zane_internal.h" - -#include - -static void check(int ok) { puts(ok ? "yes" : "no"); } - -/* A host of one `Int`, and a host holding one stable and one variant-payload - host of that kind: `{ bp; spare Node; slot #variant { empty; held Node } }`. */ -typedef struct { - uint32_t bp; - int64_t value; -} node; - -typedef struct { - uint32_t bp; - node spare; - uint32_t slot_bp; - int32_t tag; - node held; -} car; - -static const int64_t node_layout[] = { 1, ZANE_HOST, 0, sizeof(node), 0, 0, 0 }; -static const int64_t car_layout[] = { - 3, - ZANE_HOST, 0, sizeof(car), 0, 0, 0, - ZANE_HOST, offsetof(car, spare), sizeof(node), 0, 0, 0, - ZANE_HOST, offsetof(car, held), sizeof(node), 0, 0, 1, offsetof(car, tag), 1, -}; - -void zane_main(void) { - int64_t scope = zane_scope_enter(); - - /* A guest names its host, and minting again gives the same anchor. */ - node *a = zane_slot(scope, sizeof(node), 8, node_layout); - *a = (node){ 0, 1 }; - uint32_t g = zane_mint(a); - check(g != 0 && zane_mint(a) == g && zane_resolve(g) == a); - - /* A move takes the anchor along: the guest follows the host. */ - node *b = zane_slot(scope, sizeof(node), 8, node_layout); - *b = *a; - zane_vacate((char *)a, node_layout); - zane_arrive((char *)b, node_layout); - check(zane_resolve(g) == b && a->bp == 0); - - /* A stable overwrite keeps the identity: the guest sees the new host. */ - node fresh = { 0, 7 }; - zane_overwrite((char *)b, (char *)&fresh, sizeof(node), node_layout, 0); - check(zane_resolve(g) == b && b->value == 7 && b->bp == g); - - /* Moving an anchored host into an anchored one merges them: the - incoming identity forwards to the one that stays. */ - node *c = zane_slot(scope, sizeof(node), 8, node_layout); - *c = (node){ 0, 3 }; - uint32_t h = zane_mint(c); - node moving = *c; - zane_vacate((char *)c, node_layout); - zane_overwrite((char *)b, (char *)&moving, sizeof(node), node_layout, 0); - check(zane_terminal(h) == g && zane_resolve(h) == b && b->value == 3); - - /* A variant payload's anchored occupant floats when the case changes, - and a stable field's identity stays with the field. */ - car *k = zane_slot(scope, sizeof(car), 8, car_layout); - *k = (car){ .spare = { 0, 5 }, .tag = 1, .held = { 0, 9 } }; - uint32_t spare = zane_mint(&k->spare), held = zane_mint(&k->held); - car next = { .spare = { 0, 6 }, .tag = 0 }; - zane_overwrite((char *)k, (char *)&next, sizeof(car), car_layout, 0); - node *floating = zane_resolve(held); - check(floating != &k->held && floating->value == 9); - check(zane_resolve(spare) == &k->spare && k->spare.value == 6); - - /* A drain retires the scope's anchors: the next one reuses a cell. */ - zane_scope_drain(scope); - scope = zane_scope_enter(); - node *d = zane_slot(scope, sizeof(node), 8, node_layout); - *d = (node){ 0, 0 }; - uint32_t reused = zane_mint(d); - check(reused == g || reused == h || reused == spare); - zane_scope_drain(scope); -} diff --git a/tests/runtime/blocks.c b/tests/runtime/blocks.c index 4c74c329..7c218e6c 100644 --- a/tests/runtime/blocks.c +++ b/tests/runtime/blocks.c @@ -12,10 +12,9 @@ static void check(int ok) { puts(ok ? "yes" : "no"); } /* The scope whose region holds `at`. */ static int64_t zane_region_of(const void *at) { return zane_region_at(at)->depth; } -/* A host of one `Int`, a list of them, a list of strings, and a value that - boxes itself: `variant { done Unit; more Countdown; }`. */ +/* An owner of one `Int`, a list of them, a list of strings, and a value + that boxes itself: `variant { done Unit; more Countdown; }`. */ typedef struct { - uint32_t bp; int64_t value; } node; @@ -24,20 +23,24 @@ typedef struct { char *more; } countdown; -static const int64_t node_layout[] = { 1, ZANE_HOST, 0, sizeof(node), 0, 0, 0 }; +/* A struct that boxes a node: `#struct { id Int; inner Box; }`, where the + box holds `#struct { value Int; name String; }`. */ +typedef struct { + int64_t value; + zane_text name; +} named; + +typedef struct { + int64_t id; + char *inner; +} outer_t; + static const int64_t text_layout[] = { - 2, - ZANE_HOST, 0, sizeof(zane_text), 0, 0, 0, + 1, ZANE_TEXT, 0, sizeof(zane_text), 0, 0, 0, }; -static int64_t nodes_layout[] = { - 2, - ZANE_HOST, 0, sizeof(zane_list), 0, 0, 0, - ZANE_LIST, 0, sizeof(zane_list), sizeof(node), 0, 0, -}; static int64_t texts_layout[] = { - 2, - ZANE_HOST, 0, sizeof(zane_list), 0, 0, 0, + 1, ZANE_LIST, 0, sizeof(zane_list), sizeof(zane_text), 0, 0, }; static int64_t countdown_layout[] = { @@ -45,48 +48,49 @@ static int64_t countdown_layout[] = { ZANE_BOX, offsetof(countdown, more), sizeof(char *), sizeof(countdown), 0, 1, offsetof(countdown, tag), 1, }; +static const int64_t named_layout[] = { + 1, + ZANE_TEXT, offsetof(named, name), sizeof(zane_text), 0, 0, 0, +}; +static int64_t outer_layout[] = { + 1, + ZANE_BOX, offsetof(outer_t, inner), sizeof(char *), sizeof(named), 0, 0, +}; static int depth(const countdown *c) { return c->tag == 1 ? 1 + depth((countdown *)c->more) : 0; } void zane_main(void) { /* A position's fifth word is the layout of what its block holds, an address known only at run time. */ - nodes_layout[1 + 6 + 4] = (int64_t)(intptr_t)node_layout; - texts_layout[1 + 6 + 4] = (int64_t)(intptr_t)text_layout; + texts_layout[1 + 4] = (int64_t)(intptr_t)text_layout; countdown_layout[1 + 4] = (int64_t)(intptr_t)countdown_layout; + outer_layout[1 + 4] = (int64_t)(intptr_t)named_layout; int64_t scope = zane_scope_enter(); - /* An element's guest follows it through every block the list grows - into, and the list owns one block at a time. */ - zane_list *list = zane_slot(scope, sizeof(zane_list), 8, nodes_layout); + /* An element's bytes move with every block the list grows into, and + the list owns one block at a time. */ + zane_list *list = zane_slot(scope, sizeof(zane_list), 8, NULL); zane_list_new(list); - node *first = zane_list_push(list, sizeof(node), node_layout); - *first = (node){ 0, 7 }; - uint32_t g = zane_mint(first); + node *first = zane_list_push(list, sizeof(node)); + *first = (node){ 7 }; /* A block after the list's keeps it from growing where it is. */ void *after = zane_box(8, 8); - for (int64_t i = 0; i < 100; i++) - *(node *)zane_list_push(list, sizeof(node), node_layout) = (node){ 0, i }; + for (int64_t i = 0; i < 100; i++) *(node *)zane_list_push(list, sizeof(node)) = (node){ i }; zane_free(after, 8, 8); - node *now = zane_resolve(g); - check(now == zane_list_at(list, 1, sizeof(node)) && now->value == 7 && now != first); - check(list->count == 101 && list->room == 2048 && zane_blocks == 1); - - /* An anchored element replaced at its index floats, keeping its value - for its guest, and the element takes the new one. */ - node replacing = { 0, 9 }; - zane_overwrite((char *)now, (char *)&replacing, sizeof(node), node_layout, 1); - check(((node *)zane_resolve(g))->value == 7 && now->value == 9 && now->bp == 0); + node *now = zane_list_at(list, 1, sizeof(node)); + check(now->value == 7 && now != first); + check(list->count == 101 && list->room == 1024 && zane_blocks == 1); + zane_free(list->items, list->room, ZANE_LINE); + *list = (zane_list){ NULL, 0, 0 }; /* A list of strings returns every block it owns at the drain. */ zane_list *words = zane_slot(scope, sizeof(zane_list), 8, texts_layout); zane_list_new(words); - zane_text ab = { 0, "ab", 2, 0 }; - for (int i = 0; i < 3; i++) - zane_text_join(zane_list_push(words, sizeof(zane_text), NULL), &ab, &ab); - check(zane_blocks - zane_floated == 5); + zane_text ab = { "ab", 2, 0 }; + for (int i = 0; i < 3; i++) zane_text_join(zane_list_push(words, sizeof(zane_text)), &ab, &ab); + check(zane_blocks == 4); zane_scope_drain(scope); - check(zane_blocks - zane_floated == 0); + check(zane_blocks == 0); /* A copy of a boxed value owns blocks of its own, and each is returned once. */ @@ -99,12 +103,57 @@ void zane_main(void) { countdown *b = zane_slot(scope, sizeof(countdown), 8, countdown_layout); *b = *a; zane_copy((char *)b, countdown_layout); - check(depth(a) == 2 && depth(b) == 2 && b->more != a->more && zane_blocks - zane_floated == 4); + check(depth(a) == 2 && depth(b) == 2 && b->more != a->more && zane_blocks == 4); countdown done = { 0, NULL }; - zane_overwrite((char *)a, (char *)&done, sizeof(countdown), countdown_layout, 0); - check(depth(a) == 0 && depth(b) == 2 && zane_blocks - zane_floated == 2); + zane_overwrite((char *)a, (char *)&done, sizeof(countdown), countdown_layout); + check(depth(a) == 0 && depth(b) == 2 && zane_blocks == 2); + zane_scope_drain(scope); + check(zane_blocks == 0); + + /* An overwrite is in place (memory.md §2.2): a boxed member keeps its + block, so an address into it still names the member, which now holds + the replacement; the replacement's own block and the bytes the old + member owned are returned. */ + scope = zane_scope_enter(); + outer_t *o = zane_slot(scope, sizeof(outer_t), 8, outer_layout); + named *kept_box = zane_box(sizeof(named), 8); + *kept_box = (named){ 1, { NULL, 0, 0 } }; + zane_text_join(&kept_box->name, &ab, &ab); + *o = (outer_t){ 10, (char *)kept_box }; + named *seen = (named *)o->inner; + outer_t incoming = { 20, zane_box(sizeof(named), 8) }; + *(named *)incoming.inner = (named){ 2, { NULL, 0, 0 } }; + zane_text_join(&((named *)incoming.inner)->name, &ab, &(zane_text){ "cd", 2, 0 }); + check(zane_blocks == 4); + zane_overwrite((char *)o, (char *)&incoming, sizeof(outer_t), outer_layout); + check(o->id == 20 && o->inner == (char *)kept_box && seen->value == 2 && + memcmp(seen->name.bytes, "abcd", 4) == 0 && zane_blocks == 2); zane_scope_drain(scope); - check(zane_blocks - zane_floated == 0); + check(zane_blocks == 0); + + /* Arriving in an inner scope leaves blocks in an enclosing one where + they are, since that one outlives the destination; arriving in an + enclosing scope moves them out first, which is an escape (§3.5). */ + int64_t enclosing = zane_scope_enter(); + zane_text made; + zane_text_join(&made, &ab, &ab); + int64_t nested = zane_scope_enter(); + zane_text *down = zane_slot(nested, sizeof(zane_text), 8, text_layout); + *down = made; + zane_arrive((char *)down, text_layout); + check(zane_region_of(down->bytes) == enclosing); + zane_text young; + zane_text_join(&young, &ab, &ab); + zane_promote((char *)&young, text_layout, nested); + zane_vacate((char *)down, text_layout); + zane_scope_drain(nested); + check(zane_region_of(young.bytes) == enclosing); + zane_text *up = zane_slot(enclosing, sizeof(zane_text), 8, text_layout); + *up = young; + zane_text *other_up = zane_slot(enclosing, sizeof(zane_text), 8, text_layout); + *other_up = made; + zane_scope_drain(enclosing); + check(zane_blocks == 0); /* A list's block grows where it is while it is the last thing at its region's frontier, and a block it gives back serves the next of its @@ -112,29 +161,29 @@ void zane_main(void) { int64_t outer = zane_scope_enter(); zane_list *ints = zane_slot(outer, sizeof(zane_list), 8, NULL); zane_list_new(ints); - for (int64_t i = 0; i < 16; i++) *(int64_t *)zane_list_push(ints, 8, NULL) = i; + for (int64_t i = 0; i < 16; i++) *(int64_t *)zane_list_push(ints, 8) = i; char *before = ints->items; - *(int64_t *)zane_list_push(ints, 8, NULL) = 16; - check(ints->items == before && ints->room == 256 && zane_blocks - zane_floated == 1); + *(int64_t *)zane_list_push(ints, 8) = 16; + check(ints->items == before && ints->room == 256 && zane_blocks == 1); zane_list *other = zane_slot(outer, sizeof(zane_list), 8, NULL); zane_list_new(other); - zane_list_push(other, 8, NULL); + zane_list_push(other, 8); char *first_block = other->items; after = zane_box(8, 8); - for (int64_t i = 0; i < 16; i++) zane_list_push(other, 8, NULL); + for (int64_t i = 0; i < 16; i++) zane_list_push(other, 8); zane_free(after, 8, 8); zane_list *third = zane_slot(outer, sizeof(zane_list), 8, NULL); zane_list_new(third); - zane_list_push(third, 8, NULL); + zane_list_push(third, 8); check(other->items != first_block && third->items == first_block); /* A list pushed to from an inner scope keeps its block in the scope that holds it, and a string leaving an inner scope moves its bytes out before the drain. */ int64_t inner_scope = zane_scope_enter(); - for (int64_t i = 0; i < 100; i++) zane_list_push(third, 8, NULL); + for (int64_t i = 0; i < 100; i++) zane_list_push(third, 8); zane_text left; - zane_text_join(&left, &(zane_text){ 0, "ab", 2, 0 }, &(zane_text){ 0, "cd", 2, 0 }); + zane_text_join(&left, &(zane_text){ "ab", 2, 0 }, &(zane_text){ "cd", 2, 0 }); check(zane_region_of(left.bytes) == inner_scope); zane_promote((char *)&left, text_layout, inner_scope); zane_scope_drain(inner_scope); @@ -145,7 +194,7 @@ void zane_main(void) { zane_arrive((char *)kept, text_layout); zane_list *lists[] = { ints, other, third }; for (int i = 0; i < 3; i++) { - zane_list emptied = { 0, NULL, 0, 0 }; + zane_list emptied = { NULL, 0, 0 }; zane_list *l = lists[i]; /* These lists were placed with no layout, so return their blocks by hand. */ @@ -153,14 +202,14 @@ void zane_main(void) { *l = emptied; } zane_scope_drain(outer); - check(zane_blocks - zane_floated == 0); + check(zane_blocks == 0); /* A layout that lists nothing may be no table at all. */ int64_t plain = 1, replacing_plain = 2; zane_arrive((char *)&plain, NULL); zane_promote((char *)&plain, NULL, 1); zane_copy((char *)&plain, NULL); - zane_overwrite((char *)&plain, (char *)&replacing_plain, 8, NULL, 0); + zane_overwrite((char *)&plain, (char *)&replacing_plain, 8, NULL); zane_vacate((char *)&plain, NULL); check(plain == 2); @@ -177,7 +226,7 @@ void zane_main(void) { static zane_text constant; int64_t making = zane_scope_enter(); check(zane_constant_begin(&state) == 1); - zane_text_join(&constant, &(zane_text){ 0, "ab", 2, 0 }, &(zane_text){ 0, "cd", 2, 0 }); + zane_text_join(&constant, &(zane_text){ "ab", 2, 0 }, &(zane_text){ "cd", 2, 0 }); zane_constant_end(&state, (char *)&constant, text_layout); zane_scope_drain(making); check(zane_region_at(constant.bytes) == zane_program && memcmp(constant.bytes, "abcd", 4) == 0); diff --git a/tests/runtime/dune b/tests/runtime/dune index 767fd9b4..17d2ca41 100644 --- a/tests/runtime/dune +++ b/tests/runtime/dune @@ -1,8 +1,8 @@ ; The runtime on its own (docs/design/lowering.md L17): `arenas.c`, -; `anchors.c`, `texts.c`, `blocks.c` and `spawns.c` link its parts, built -; separately, and exercise the scope arenas, the anchors, the strings, the -; lists, arrays, boxes and dynamic regions, package constants, and spawned -; calls directly, and the golden files hold what each printed. +; `texts.c`, `blocks.c` and `spawns.c` link its parts, built separately, and +; exercise the scope arenas, the strings, the lists, arrays, boxes, in-place +; overwrites and dynamic regions, package constants, and spawned calls +; directly, and the golden files hold what each printed. ; The rules themselves are written from the golden files by ; tests/gen/gen_rules.ml, into dune.inc. A fixture added without its rules diff --git a/tests/runtime/dune.inc b/tests/runtime/dune.inc index ae4aab56..77d3c622 100644 --- a/tests/runtime/dune.inc +++ b/tests/runtime/dune.inc @@ -1,29 +1,11 @@ ; Written by tests/gen/gen_rules.ml. Promote a change with `dune promote`. (rule - (deps anchors.c ../../runtime/zane.h ../../runtime/zane_internal.h ../../runtime/main.c ../../runtime/arena.c ../../runtime/anchor.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c) - (targets anchors.exe) - (action - (run clang -std=c11 -Wall -Wextra -Werror -O2 -pthread -I ../../runtime -o anchors.exe anchors.c - ../../runtime/main.c ../../runtime/arena.c ../../runtime/anchor.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c))) - -(rule - (action - (with-stdout-to - anchors.out.actual - (run ./anchors.exe)))) - -(rule - (alias runtest) - (action - (diff golden/anchors.out anchors.out.actual))) - -(rule - (deps arenas.c ../../runtime/zane.h ../../runtime/zane_internal.h ../../runtime/main.c ../../runtime/arena.c ../../runtime/anchor.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c) + (deps arenas.c ../../runtime/zane.h ../../runtime/zane_internal.h ../../runtime/main.c ../../runtime/arena.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c) (targets arenas.exe) (action (run clang -std=c11 -Wall -Wextra -Werror -O2 -pthread -I ../../runtime -o arenas.exe arenas.c - ../../runtime/main.c ../../runtime/arena.c ../../runtime/anchor.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c))) + ../../runtime/main.c ../../runtime/arena.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c))) (rule (action @@ -37,11 +19,11 @@ (diff golden/arenas.out arenas.out.actual))) (rule - (deps blocks.c ../../runtime/zane.h ../../runtime/zane_internal.h ../../runtime/main.c ../../runtime/arena.c ../../runtime/anchor.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c) + (deps blocks.c ../../runtime/zane.h ../../runtime/zane_internal.h ../../runtime/main.c ../../runtime/arena.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c) (targets blocks.exe) (action (run clang -std=c11 -Wall -Wextra -Werror -O2 -pthread -I ../../runtime -o blocks.exe blocks.c - ../../runtime/main.c ../../runtime/arena.c ../../runtime/anchor.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c))) + ../../runtime/main.c ../../runtime/arena.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c))) (rule (action @@ -55,11 +37,11 @@ (diff golden/blocks.out blocks.out.actual))) (rule - (deps spawns.c ../../runtime/zane.h ../../runtime/zane_internal.h ../../runtime/main.c ../../runtime/arena.c ../../runtime/anchor.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c) + (deps spawns.c ../../runtime/zane.h ../../runtime/zane_internal.h ../../runtime/main.c ../../runtime/arena.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c) (targets spawns.exe) (action (run clang -std=c11 -Wall -Wextra -Werror -O2 -pthread -I ../../runtime -o spawns.exe spawns.c - ../../runtime/main.c ../../runtime/arena.c ../../runtime/anchor.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c))) + ../../runtime/main.c ../../runtime/arena.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c))) (rule (action @@ -73,11 +55,11 @@ (diff golden/spawns.out spawns.out.actual))) (rule - (deps texts.c ../../runtime/zane.h ../../runtime/zane_internal.h ../../runtime/main.c ../../runtime/arena.c ../../runtime/anchor.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c) + (deps texts.c ../../runtime/zane.h ../../runtime/zane_internal.h ../../runtime/main.c ../../runtime/arena.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c) (targets texts.exe) (action (run clang -std=c11 -Wall -Wextra -Werror -O2 -pthread -I ../../runtime -o texts.exe texts.c - ../../runtime/main.c ../../runtime/arena.c ../../runtime/anchor.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c))) + ../../runtime/main.c ../../runtime/arena.c ../../runtime/block.c ../../runtime/value.c ../../runtime/list.c ../../runtime/slot.c ../../runtime/spawn.c ../../runtime/snapshot.c))) (rule (action diff --git a/tests/runtime/golden/blocks.out b/tests/runtime/golden/blocks.out index c09bc111..c545665d 100644 --- a/tests/runtime/golden/blocks.out +++ b/tests/runtime/golden/blocks.out @@ -16,3 +16,8 @@ yes yes yes yes +yes +yes +yes +yes +yes diff --git a/tests/runtime/golden/texts.out b/tests/runtime/golden/texts.out index dc5f499f..74e8d372 100644 --- a/tests/runtime/golden/texts.out +++ b/tests/runtime/golden/texts.out @@ -7,3 +7,4 @@ yes yes yes yes +yes diff --git a/tests/runtime/spawns.c b/tests/runtime/spawns.c index e519893f..03c38806 100644 --- a/tests/runtime/spawns.c +++ b/tests/runtime/spawns.c @@ -8,8 +8,7 @@ static void check(int ok) { puts(ok ? "yes" : "no"); } static const int64_t text_layout[] = { - 2, - ZANE_HOST, 0, sizeof(zane_text), 0, 0, 0, + 1, ZANE_TEXT, 0, sizeof(zane_text), 0, 0, 0, }; @@ -98,7 +97,7 @@ static int holds(const zane_text *t, const char *s) { } void zane_main(void) { - zane_text ab = { 0, "ab", 2, 0 }, cd = { 0, "cd", 2, 0 }; + zane_text ab = { "ab", 2, 0 }, cd = { "cd", 2, 0 }; /* A result comes home when it is read: its bytes move into the region of the scope that holds the slot, and the call's context is diff --git a/tests/runtime/texts.c b/tests/runtime/texts.c index f446bbe6..2bbf9a65 100644 --- a/tests/runtime/texts.c +++ b/tests/runtime/texts.c @@ -12,28 +12,24 @@ static int holds(const zane_text *t, const char *bytes) { return t->length == (int64_t)strlen(bytes) && memcmp(t->bytes, bytes, strlen(bytes)) == 0; } -/* A host holding a string in a variant payload: `#variant { held String; +/* An owner holding a string in a variant payload: `#variant { held String; empty Unit }`. */ typedef struct { - uint32_t bp; int32_t tag; zane_text held; } holder; static const int64_t text_layout[] = { - 2, - ZANE_HOST, 0, sizeof(zane_text), 0, 0, 0, + 1, ZANE_TEXT, 0, sizeof(zane_text), 0, 0, 0, }; static const int64_t holder_layout[] = { - 3, - ZANE_HOST, 0, sizeof(holder), 0, 0, 0, - ZANE_HOST, offsetof(holder, held), sizeof(zane_text), 0, 0, 1, offsetof(holder, tag), 0, + 1, ZANE_TEXT, offsetof(holder, held), sizeof(zane_text), 0, 0, 1, offsetof(holder, tag), 0, }; void zane_main(void) { - zane_text ab = { 0, "ab", 2, 0 }, cd = { 0, "cd", 2, 0 }, empty = { 0, "", 0, 0 }; + zane_text ab = { "ab", 2, 0 }, cd = { "cd", 2, 0 }, empty = { "", 0, 0 }; /* A join owns a block of its own; joining nothing owns none. */ zane_text abcd; @@ -42,7 +38,7 @@ void zane_main(void) { zane_text none; zane_text_join(&none, &empty, &empty); check(none.length == 0 && none.room == 0 && zane_blocks == 1); - check(zane_text_equal(&abcd, &(zane_text){ 0, "abcd", 4, 0 }) && !zane_text_equal(&ab, &cd)); + check(zane_text_equal(&abcd, &(zane_text){ "abcd", 4, 0 }) && !zane_text_equal(&ab, &cd)); /* A drain returns the blocks its scope's strings own, and leaves a literal's bytes alone. */ @@ -68,26 +64,24 @@ void zane_main(void) { /* An overwrite returns the block it replaces, and keeps the new one. */ zane_text incoming; zane_text_join(&incoming, &cd, &cd); - zane_overwrite((char *)t, (char *)&incoming, sizeof(zane_text), text_layout, 0); + zane_overwrite((char *)t, (char *)&incoming, sizeof(zane_text), text_layout); check(holds(t, "cdcd") && zane_blocks == 1); zane_scope_drain(scope); check(zane_blocks == 0); - /* A payload that floats takes its block along into the program's own - region, and it stays readable through the guest; one nobody names dies - with its block. */ + /* A payload that disappears when the variant changes case dies with + its block: nothing references a payload (memory.md §2.8.1). */ scope = zane_scope_enter(); holder *h = zane_slot(scope, sizeof(holder), 8, holder_layout); - h->bp = 0; h->tag = 0; zane_text_join(&h->held, &ab, &cd); - uint32_t g = zane_mint(&h->held); - holder emptied = { 0, 1, { 0 } }; - zane_overwrite((char *)h, (char *)&emptied, sizeof(holder), holder_layout, 0); - check(holds(zane_resolve(g), "abcd") && zane_blocks == 2 && zane_floated == 2); - h->tag = 0; - zane_text_join(&h->held, &cd, &ab); - zane_overwrite((char *)h, (char *)&emptied, sizeof(holder), holder_layout, 0); - check(zane_blocks == 2); + holder emptied = { 1, { 0 } }; + zane_overwrite((char *)h, (char *)&emptied, sizeof(holder), holder_layout); + check(h->tag == 1 && zane_blocks == 0); + holder full = { 0, { 0 } }; + zane_text_join(&full.held, &cd, &ab); + zane_overwrite((char *)h, (char *)&full, sizeof(holder), holder_layout); + check(holds(&h->held, "cdab") && zane_blocks == 1); zane_scope_drain(scope); + check(zane_blocks == 0); } diff --git a/tests/semantics/fixtures/typing/accept/app/main.zn b/tests/semantics/fixtures/typing/accept/app/main.zn index df745147..50439352 100644 --- a/tests/semantics/fixtures/typing/accept/app/main.zn +++ b/tests/semantics/fixtures/typing/accept/app/main.zn @@ -97,6 +97,23 @@ Float?String safeDivide(numerator Float, denominator Float) { return numerator / denominator; } +/// Owners of a reference type (memory.md §2): a roaming one taken and +/// handed back, a settled one referenced, and an `ArrayRef` whose elements +/// may be referenced. +type Crew = #struct { + lead Shape; + backup &Shape; +} + +Crew(lead ^Shape, backup &Shape) => init{ lead; backup; } + +^Shape grown(shape ^Shape) { + kept ^Shape = shape; + return kept; +} + +&Shape leadOf(crew &Crew) => crew.lead + String describe(corner Corner) => match (corner) { [topLeft, topRight] => String("top"); [bottomLeft, bottomRight] => String("bottom"); @@ -128,6 +145,14 @@ Unit main() { floats Pair(Float(1.0), Float(2.0)); both Float = sum(floats); + spare ^Shape = Shape.circle(Float(1.0)); + spare = grown(spare); + crew Crew(spare, shape); + lead &Shape = leadOf(crew); + shapes @primitives$ArrayRef = @primitives$ArrayRef([Shape.circle(Float(3.0)), Shape.circle(Float(4.0))]); + second &Shape = shapes[Int(2)]; + tiles @primitives$ArrayRef = @primitives$ArrayRef.fill(4, Int(n Int) => n * n); + numbers Array = @primitives$Array([Int(1), Int(2), Int(3)]); first Int = numbers[Int(1)]; length Int = count(numbers); diff --git a/tests/semantics/fixtures/typing/reject/bad/marks.zn b/tests/semantics/fixtures/typing/reject/bad/marks.zn new file mode 100644 index 00000000..2cc167b5 --- /dev/null +++ b/tests/semantics/fixtures/typing/reject/bad/marks.zn @@ -0,0 +1,43 @@ +package bad; + +type Kite = #struct { span Int; } +Kite(span Int) => init{ span; } + +/// `^` marks a roaming owner of a reference type, written on a local, a +/// parameter or a return type, and nowhere else (syntax.md §2.3). +type Tether = #struct { kite ^Kite; } + +alias Kites = List<^Kite> + +Unit fly(span ^Int) => Unit() + +Unit tug(this ^Kite) => Unit() + +/// A reference-typed result is `^T` or `&T`: a bare one would hand back a +/// borrow (memory.md §2.9). An abort hands its value to the caller as a +/// return does, so an abort type follows the same rule. +Kite build() => Kite(Int(1)) +Int?Kite refuse() => Int(0) +Unit launch(make Kite[Int]) => Unit() + +/// Overloads differ by more than a passing mode (functions.md §4.1). +Unit hold(kite Kite) => Unit() +Unit hold(kite ^Kite) => Unit() + +/// A reference type cannot fill a value mould's field, and the mistake is +/// reported where the type enters (generics.md §3.6): the explicit argument, +/// or the value an inferred one is read from, not inside a verb that +/// forwards it. +type Hamper = struct { item T; } +Hamper(item ^T Type) => init{ item; } +Hamper pack(item ^T Type) => Hamper(item) + +alias Kited = Hamper + +Unit wrong() { + a Hamper(Kite(Int(2))); + b Hamper = pack(Int(3)); + c Hamper = pack(Kite(Int(4))); + d @primitives$Array([Kite(Int(5)), Kite(Int(6))]); + return Unit(); +} diff --git a/tests/semantics/fixtures/typing/reject/bad/moves.zn b/tests/semantics/fixtures/typing/reject/bad/moves.zn index 720b8177..637388c3 100644 --- a/tests/semantics/fixtures/typing/reject/bad/moves.zn +++ b/tests/semantics/fixtures/typing/reject/bad/moves.zn @@ -5,32 +5,45 @@ type Saucer = #struct { cup Cup; } type Order = #variant { hot Cup; none Int; } Cup(size Int) => init{ size; } -Saucer(cup Cup) => init{ cup; } +Saucer(cup ^Cup) => init{ cup; } -Unit drink(cup Cup) => Unit() -Unit look(cup &Cup) => Unit() -Cup brew() => Cup(Int(1)) -Unit serve(first Cup, second Cup) => Unit() +Unit drink(cup ^Cup) => Unit() +Unit see(cup Cup) => Unit() +^Cup brew() => Cup(Int(1)) +Unit serve(first ^Cup, second ^Cup) => Unit() Unit loop(body @concepts$Block) => Unit() -/// A host moves from a symbol, a verb's result or a case form, and from -/// nothing else (lifetimes.md §1.2). -Unit sources(saucer Saucer, cups List, order Order, guest &Cup) { +/// An owner moves from a roaming symbol, a field of one, a verb's result or +/// a case form, and from nothing else (lifetimes.md §1.2): not a settled +/// owner, a part of a borrow, an element, a reference or a payload. +Unit sources(saucer Saucer, cups List, order Order, ref &Cup) { a Cup = brew(); b Cup = saucer.cup; c Cup = cups[Int(1)]; - d Cup = guest; + d Cup = ref; e Order = Order.hot(Cup(Int(2))); drink(order.hot ?? Cup(Int(3))); + settled Cup = Cup(Int(4)); + f Cup = settled; return Unit(); } -/// A symbol moves only in the block that declares it (§1.3). -Unit blocks(cup Cup) { - outer Cup = Cup(Int(4)); +/// A borrow is the caller's for the call, and is never moved: neither a bare +/// parameter nor the subject (memory.md §2.9). +Unit borrowed(cup Cup) { + drink(cup); + return Unit(); +} + +^Saucer give(this Saucer) => this + +/// A roaming symbol moves only in the block that declares it (§1.3), and a +/// `^T` parameter is declared at the top of the body. +Unit blocks(cup ^Cup) { + outer ^Cup = Cup(Int(4)); loop({ drink(outer); - inner Cup = Cup(Int(5)); + inner ^Cup = Cup(Int(5)); drink(inner); }); drink(cup); @@ -38,40 +51,53 @@ Unit blocks(cup Cup) { } /// A moved symbol is spent: using it again is an error until a store in its -/// own block refills it (§1.6). Passing to a `T` parameter moves (§1.8), and -/// a `&T` parameter does not. +/// own block refills it (§1.6). Passing to a `^T` parameter moves (§1.8), and +/// a borrow does not. Unit spent() { - cup Cup = Cup(Int(6)); - look(cup); + cup ^Cup = Cup(Int(6)); + see(cup); drink(cup); - look(cup); + see(cup); loop({ cup = Cup(Int(7)); }); cup = Cup(Int(8)); - look(cup); - both Cup = Cup(Int(9)); + see(cup); + both ^Cup = Cup(Int(9)); serve(both, both); return Unit(); } -/// A method never moves its subject. -Saucer give(this Saucer) => this +/// A field moved out of a roaming owner leaves the field spent, and the +/// owner spent as a whole, until a store in the owner's block refills the +/// field (memory.md §2.8.1). +Unit partly() { + saucer ^Saucer = Saucer(Cup(Int(10))); + cup Cup = saucer.cup; + see(saucer.cup); + whole ^Saucer = saucer; + loop({ + saucer.cup = Cup(Int(11)); + }); + saucer.cup = Cup(Int(12)); + again ^Saucer = saucer; + return Unit(); +} /// A generic parameter moves at the type it was called with. -Unit stock(cup Cup) { +Unit stock(cup ^Cup) { cups List = @primitives$List(Cup); cups!push(cup); - look(cup); + see(cup); return Unit(); } /// A match arm hands its value to wherever the match's value goes: here a -/// method's subject, which is not a move, so `kept` stays a host. +/// method's subject, which is a borrow, so `kept` still owns its cup. Int sip(this Cup) => this.size Unit arms(order Order) { - kept Cup = Cup(Int(10)); + kept ^Cup = Cup(Int(10)); n Int = (match (order) { hot => kept; none => kept; diff --git a/tests/semantics/fixtures/typing/reject/bad/guests.zn b/tests/semantics/fixtures/typing/reject/bad/readonly.zn similarity index 86% rename from tests/semantics/fixtures/typing/reject/bad/guests.zn rename to tests/semantics/fixtures/typing/reject/bad/readonly.zn index e9c8e126..52f3b5ad 100644 --- a/tests/semantics/fixtures/typing/reject/bad/guests.zn +++ b/tests/semantics/fixtures/typing/reject/bad/readonly.zn @@ -8,7 +8,7 @@ Desk(pen &Pen) => init{ pen; } Unit fill(this Pen) mut => Unit() -/// A `mut` method writes what its `this` reaches, guests included. +/// A `mut` method writes what its `this` reaches, references included. Unit use(this Desk) mut { this.pen!fill(); return Unit(); @@ -21,7 +21,7 @@ Unit hold(this Desk, pen &Pen) mut { &Pen pass(pen &Pen) => pen -/// A guest taken from a read-only parameter stays read-only wherever it +/// A reference taken from a read-only parameter stays read-only wherever it /// goes: into a local, into a field through a constructor, back out of a /// call, or into `this` through a `mut` call (effects.md §4.4). Unit sneak(pen &Pen) { @@ -39,7 +39,7 @@ Unit sneak(pen &Pen) { return Unit(); } -/// `this` is read-only in a method that is not `mut`, and so is a guest taken +/// `this` is read-only in a method that is not `mut`, and so is a reference taken /// from it. Unit look(this Desk) { k &Pen = this.pen; @@ -47,7 +47,7 @@ Unit look(this Desk) { return Unit(); } -/// Writing through `this` is a write once a call has stored such a guest +/// Writing through `this` is a write once a call has stored such a reference /// there. Unit tidy(this Desk, pen &Pen) mut { this!use(); @@ -68,7 +68,7 @@ Unit loop(pen &Pen) { } /// A value copied out of a read-only parameter is the verb's own, and a field -/// that holds no guest carries nothing. +/// that holds no reference carries nothing. type Caddy = #struct { count Int; pen &Pen; } Caddy(count Int, pen &Pen) => init{ count; pen; } @@ -103,9 +103,9 @@ Unit rotate(pen &Pen) { /// A summary does not depend on the order verbs are declared in: `forward` /// is walked before `make`, and still learns that only `other` reaches the /// result. -Desk forward(pen &Pen, other &Pen) => make(pen, other) +^Desk forward(pen &Pen, other &Pen) => make(pen, other) -Desk make(pen &Pen, other &Pen) => Desk(other) +^Desk make(pen &Pen, other &Pen) => Desk(other) Unit order(pen &Pen) { own Pen(Int(6)); diff --git a/tests/semantics/fixtures/typing/reject/bad/rests.zn b/tests/semantics/fixtures/typing/reject/bad/rests.zn index 278975cc..354e1f3f 100644 --- a/tests/semantics/fixtures/typing/reject/bad/rests.zn +++ b/tests/semantics/fixtures/typing/reject/bad/rests.zn @@ -7,13 +7,14 @@ type Bay = #struct { plugs List; } Port(n Int) => init{ n; } Plug(port &Port) => init{ port; } -Frame(plug Plug, port Port) => init{ plug; port; } +Frame(plug ^Plug, port ^Port) => init{ plug; port; } Bay() => init{ plugs = @primitives$List(Plug); } Unit nest(body @concepts$Block) => Unit() -/// A parameter stored into a place reached from another comes to rest there -/// (lifetimes.md §1.11), and each call compares what it stores. +/// A reference parameter stored into a place reached from another comes to +/// rest there (lifetimes.md §1.11), and so does a taken one that carries a +/// reference; each call compares what it stores. Unit wire(this Plug, port &Port) mut { this.port = port; return Unit(); @@ -25,7 +26,7 @@ Unit relay(this Plug, port &Port) mut { return Unit(); } -Unit dock(this Bay, plug Plug) mut { +Unit dock(this Bay, plug ^Plug) mut { this.plugs!push(plug); return Unit(); } @@ -44,7 +45,7 @@ Unit rest() { near Port(Int(4)); frame.plug!wire(near); frame.plug!relay(near); - spare Plug(far); + spare ^Plug = Plug(far); spare!wire(near); frame.plug = spare; }); @@ -59,9 +60,9 @@ Unit queue() { bay Bay(); nest({ near Port(Int(6)); - arriving Plug(near); + arriving ^Plug = Plug(near); plugs!push(arriving); - docked Plug(near); + docked ^Plug = Plug(near); bay!dock(docked); }); return Unit(); diff --git a/tests/semantics/fixtures/typing/reject/bad/owners.zn b/tests/semantics/fixtures/typing/reject/bad/scopes.zn similarity index 69% rename from tests/semantics/fixtures/typing/reject/bad/owners.zn rename to tests/semantics/fixtures/typing/reject/bad/scopes.zn index aef7313d..6db2b99f 100644 --- a/tests/semantics/fixtures/typing/reject/bad/owners.zn +++ b/tests/semantics/fixtures/typing/reject/bad/scopes.zn @@ -6,13 +6,13 @@ type Pin = #struct { leaf &Leaf; } type Tag = #variant { plain Int; pinned &Leaf; } Leaf(size Int) => init{ size; } -Branch(leaf Leaf) => init{ leaf; } +Branch(leaf ^Leaf) => init{ leaf; } Pin(leaf &Leaf) => init{ leaf; } Unit inside(body @concepts$Block) => Unit() -/// A store may not raise a value above what it names: a guest into a place -/// owned by a block that outlives the host's (lifetimes.md §1.1). +/// A store may not raise a value above what it names: a reference into a +/// place scoped to a block that outlives the owner's (lifetimes.md §1.1). Unit raise() { outer Branch(Leaf(Int(1))); r &Leaf = outer.leaf; @@ -25,15 +25,15 @@ Unit raise() { return Unit(); } -/// A value carries the guests reachable inside it, and each store asks again -/// (§1.10): a struct, and a case form. +/// A roaming value carries the references reachable inside it, and each +/// store of it asks again (§1.10): a struct, and a case form. Unit carry() { far Branch(Leaf(Int(3))); - parked Pin(far.leaf); + parked ^Pin = Pin(far.leaf); tag Tag = Tag.plain(Int(0)); inside({ near Branch(Leaf(Int(4))); - arriving Pin(near.leaf); + arriving ^Pin = Pin(near.leaf); parked = arriving; tag = Tag.pinned(near.leaf); tag = Tag.plain(Int(5)); @@ -41,9 +41,9 @@ Unit carry() { return Unit(); } -/// A returned guest is rooted in a parameter (§1.7), and so is one stored in -/// `this`: the caller's frame outlives the body. -&Leaf root(this Branch) => this.leaf +/// A returned reference is rooted in an `&T` parameter (§1.7), and so is one +/// stored in `this`: the caller's frame outlives the body. +&Leaf root(branch &Branch) => branch.leaf &Leaf lost() { value Leaf(Int(6)); return value; diff --git a/tests/semantics/fixtures/typing/reject/bad/sources.zn b/tests/semantics/fixtures/typing/reject/bad/sources.zn index 45117f62..ee8331c5 100644 --- a/tests/semantics/fixtures/typing/reject/bad/sources.zn +++ b/tests/semantics/fixtures/typing/reject/bad/sources.zn @@ -7,49 +7,77 @@ type Slot = #variant { full Bell; empty Int; } Bell(tone Int) => init{ tone; } &Bell ring(bell &Bell) => bell -Bell forge() => Bell(Int(1)) +^Bell forge() => Bell(Int(1)) Int weight(bell &Bell) => bell.tone -/// A new guest comes from a stable place: a symbol, or fields reached from -/// one (memory.md §2.8). A value that is already a guest is copied, from -/// wherever it is. -Unit mint(rack Rack, bells List, hooks List<&Bell>, slot Slot) { +/// A new reference comes from a settled place: a settled symbol, or a path +/// from one, or from a reference, through struct fields (memory.md §2.8). +/// A list's element, a payload and a temporary are not one; a value that is +/// already a reference is copied, from wherever it is. +Unit mint(rack &Rack, bells &List, hooks &List<&Bell>, slot &Slot) { a &Bell = rack.bell; - b &Bell = bells[Int(0)]; - c &Bell = hooks[Int(0)]; + b &Bell = bells[Int(1)]; + c &Bell = hooks[Int(1)]; d &Bell = slot.full ?? rack.bell; e &Bell = forge(); - f &Bell = ring(bells[Int(0)]); + f &Bell = ring(bells[Int(1)]); g &Bell = ring(rack.bell); h Int = match (slot) { x full => ring(x).tone; empty => Int(0); } tones List = @primitives$List(Int); - tones[weight(bells[Int(0)])] = Int(1); + tones[weight(bells[Int(1)])] = Int(1); return Unit(); } -/// A swallowed parameter is hosted at the call site, so it is never bound -/// into `&` storage; returning it as a guest is a different matter -/// (memory.md §2.9). +/// A roaming owner may still move, so nothing references it or anything in +/// it; an `ArrayRef`'s elements are fixed storage, which take their root's +/// state (memory.md §2.8.1, generics.md §8.4). +Unit fixed() { + spare ^Bell = Bell(Int(5)); + s &Bell = spare; + row @primitives$ArrayRef = @primitives$ArrayRef([Bell(Int(1)), Bell(Int(2))]); + r &Bell = row[Int(1)]; + loose ^@primitives$ArrayRef = @primitives$ArrayRef([Bell(Int(3)), Bell(Int(4))]); + l &Bell = loose[Int(1)]; + return Unit(); +} + +/// A declared subscript is a reference source exactly when the projection +/// its body ends at is one (functions.md §2.9). +type Belfry = #struct { _row @primitives$ArrayRef; } +type Cupboard = #struct { _items List; } + +(this Belfry)[index Int] => this._row[index] +(this Cupboard)[index Int] => this._items[index] + +Unit project(belfry &Belfry, cupboard &Cupboard) { + a &Bell = belfry[Int(1)]; + b &Bell = cupboard[Int(1)]; + return Unit(); +} + +/// A borrow is the caller's only for the call: it is never bound into `&` +/// storage and never returned as `&T` (memory.md §2.9). Neither is the +/// subject, which is a borrow too. Unit hang(this Rack, bell Bell, spare &Bell) mut { this.hook = bell; this.hook = spare; held &Bell = bell; - this.hook = held; later &Bell = spare; repeat({ this.hook = later; - later = bell; + later = this.bell; }); return Unit(); } &Bell lend(bell Bell) => bell +&Bell own(this Rack) => this.bell -/// A store never goes through a guest, unless the guest is a parameter the -/// path starts at (lifetimes.md §1.1). +/// A store never goes through a reference, unless the reference is a +/// parameter the path starts at (lifetimes.md §1.1). Unit retone(this Rack, other &Rack) mut { this.bell = Bell(Int(2)); this.hook.tone = Int(3); diff --git a/tests/semantics/fixtures/typing/reject/bad/temporary.zn b/tests/semantics/fixtures/typing/reject/bad/temporary.zn index 1b43adf3..d454c82f 100644 --- a/tests/semantics/fixtures/typing/reject/bad/temporary.zn +++ b/tests/semantics/fixtures/typing/reject/bad/temporary.zn @@ -1,8 +1,14 @@ package bad; -/// `print` takes a guest, and a guest names a place: a view built in the -/// argument is a temporary, so it is stored first (memory.md §2.8). +type Note = #struct { pitch @primitives$Int; } + +Note(pitch @primitives$Int) => init{ pitch; } + +@primitives$Unit hear(note &Note) => @primitives$Unit() + +/// A reference names a place: a value built in the argument is a temporary, +/// so it is stored first (memory.md §2.8). @primitives$Unit greet() { - @program$console!print(@primitives$String("hello world")); + hear(Note(@primitives$Int(1))); return @primitives$Unit(); } diff --git a/tests/semantics/fixtures/typing/reject/bad/types.zn b/tests/semantics/fixtures/typing/reject/bad/types.zn index 13d8eeef..dcf46386 100644 --- a/tests/semantics/fixtures/typing/reject/bad/types.zn +++ b/tests/semantics/fixtures/typing/reject/bad/types.zn @@ -5,7 +5,7 @@ type Box = struct { item T; } type Holder = struct { many Box; sized Array; - text String; + list List; guest ∬ literal @concepts$Int; inline Box; diff --git a/tests/semantics/golden/typing.accept.decls b/tests/semantics/golden/typing.accept.decls index 319593c3..c1536554 100644 --- a/tests/semantics/golden/typing.accept.decls +++ b/tests/semantics/golden/typing.accept.decls @@ -58,61 +58,72 @@ │ │ └── signature: app$Pair<@primitives$Float> pairOf(@concepts$Float, @concepts$Float) #22 │ ├── decls[22] > verb: │ │ └── signature: @primitives$Float?@primitives$String safeDivide(@primitives$Float, @primitives$Float) #23 - │ ├── decls[23] > verb: - │ │ └── signature: @primitives$String describe(shapes$Corner) #24 + │ ├── decls[23] > type: + │ │ ├── name: Crew #24 + │ │ ├── kind: reference + │ │ ├── struct[0]: lead : shapes$Shape + │ │ └── struct[1]: backup : &shapes$Shape │ ├── decls[24] > verb: - │ │ └── signature: @primitives$Unit main() #25 - │ └── decls[25] > verb: - │ └── signature: T twice(T) #26 + │ │ └── signature: Crew(^shapes$Shape, &shapes$Shape) #25 + │ ├── decls[25] > verb: + │ │ └── signature: ^shapes$Shape grown(^shapes$Shape) #26 + │ ├── decls[26] > verb: + │ │ └── signature: &shapes$Shape leadOf(&app$Crew) #27 + │ ├── decls[27] > verb: + │ │ └── signature: @primitives$String describe(shapes$Corner) #28 + │ ├── decls[28] > verb: + │ │ └── signature: @primitives$Unit main() #29 + │ └── decls[29] > verb: + │ └── signature: T twice(T) #30 └── packages[1] > package: ├── name: shapes ├── decls[0] > alias: - │ ├── name: Float #27 + │ ├── name: Float #31 │ └── target: @primitives$Float ├── decls[1] > alias: - │ ├── name: Unit #28 + │ ├── name: Unit #32 │ └── target: @primitives$Unit ├── decls[2] > alias: - │ ├── name: String #29 + │ ├── name: String #33 │ └── target: @primitives$String ├── decls[3] > type: - │ ├── name: Vec2 #30 + │ ├── name: Vec2 #34 │ ├── kind: value │ ├── struct[0]: x : @primitives$Float │ └── struct[1]: y : @primitives$Float ├── decls[4] > verb: - │ └── signature: Vec2(@primitives$Float, @primitives$Float) #31 + │ └── signature: Vec2(@primitives$Float, @primitives$Float) #35 ├── decls[5] > verb: - │ └── signature: Vec2.zero() #32 + │ └── signature: Vec2.zero() #36 ├── decls[6] > verb: - │ └── signature: Vec2{x @primitives$Float; y @primitives$Float = ...} #33 + │ └── signature: Vec2{x @primitives$Float; y @primitives$Float = ...} #37 ├── decls[7] > verb: - │ └── signature: shapes$Vec2 +(shapes$Vec2, shapes$Vec2) #34 + │ └── signature: shapes$Vec2 +(shapes$Vec2, shapes$Vec2) #38 ├── decls[8] > verb: - │ └── signature: shapes$Vec2 ~(shapes$Vec2) #35 + │ └── signature: shapes$Vec2 ~(shapes$Vec2) #39 ├── decls[9] > verb: - │ └── signature: @primitives$Float length(this shapes$Vec2) #36 + │ └── signature: @primitives$Float length(this shapes$Vec2) #40 ├── decls[10] > verb: - │ └── signature: @primitives$Unit scale(this shapes$Vec2, @primitives$Float) mut #37 + │ └── signature: @primitives$Unit scale(this shapes$Vec2, @primitives$Float) mut #41 ├── decls[11] > verb: - │ └── signature: implicit Vec2(@concepts$Float) #38 + │ └── signature: implicit Vec2(@concepts$Float) #42 ├── decls[12] > type: - │ ├── name: Shape #39 + │ ├── name: Shape #43 │ ├── kind: reference │ ├── variant[0]: circle : @primitives$Float │ ├── variant[1]: square : @primitives$Float │ └── variant[2]: point : shapes$Vec2 ├── decls[13] > type: - │ ├── name: Corner #40 + │ ├── name: Corner #44 │ ├── kind: value │ ├── enum[0]: topLeft │ ├── enum[1]: topRight │ ├── enum[2]: bottomLeft │ └── enum[3]: bottomRight ├── decls[14] > enum_map: - │ ├── map: shapes$Corner.label #41 + │ ├── map: shapes$Corner.label #45 │ └── type: @primitives$String ├── decls[15] > verb: - │ └── signature: @primitives$Float _half(@primitives$Float) #42 + │ └── signature: @primitives$Float _half(@primitives$Float) #46 └── decls[16] > verb: - └── signature: @primitives$Float area(&shapes$Shape) #43 + └── signature: @primitives$Float area(&shapes$Shape) #47 diff --git a/tests/semantics/golden/typing.accept.tst b/tests/semantics/golden/typing.accept.tst index 0bba9a36..6945d501 100644 --- a/tests/semantics/golden/typing.accept.tst +++ b/tests/semantics/golden/typing.accept.tst @@ -235,12 +235,43 @@ │ │ ├── impl: @primitives$/ │ │ ├── left: numerator #16 : @primitives$Float │ │ └── right: denominator #17 : @primitives$Float - │ ├── decls[23] > verb: - │ │ ├── signature: @primitives$String describe(shapes$Corner) #24 - │ │ ├── params[0]: corner #18 : shapes$Corner + │ ├── decls[23] > type: + │ │ ├── name: Crew #24 + │ │ ├── kind: reference + │ │ ├── struct[0]: lead : shapes$Shape + │ │ └── struct[1]: backup : &shapes$Shape + │ ├── decls[24] > verb: + │ │ ├── signature: Crew(^shapes$Shape, &shapes$Shape) #25 + │ │ ├── params[0]: lead #18 : ^shapes$Shape + │ │ ├── params[1]: backup #19 : &shapes$Shape + │ │ └── body[0] > return > init: + │ │ ├── type: app$Crew + │ │ ├── fields[0] > field: + │ │ │ ├── name: lead (slot 0) + │ │ │ └── value: lead #18 : shapes$Shape + │ │ └── fields[1] > field: + │ │ ├── name: backup (slot 1) + │ │ └── value: backup #19 : &shapes$Shape + │ ├── decls[25] > verb: + │ │ ├── signature: ^shapes$Shape grown(^shapes$Shape) #26 + │ │ ├── params[0]: shape #20 : ^shapes$Shape + │ │ ├── body[0] > let: + │ │ │ ├── local: kept #21 : ^shapes$Shape + │ │ │ └── value: shape #20 : shapes$Shape + │ │ └── body[1] > return: kept #21 : shapes$Shape + │ ├── decls[26] > verb: + │ │ ├── signature: &shapes$Shape leadOf(&app$Crew) #27 + │ │ ├── params[0]: crew #22 : &app$Crew + │ │ └── body[0] > return > field: + │ │ ├── type: shapes$Shape + │ │ ├── target: crew #22 : &app$Crew + │ │ └── field: lead (slot 0) + │ ├── decls[27] > verb: + │ │ ├── signature: @primitives$String describe(shapes$Corner) #28 + │ │ ├── params[0]: corner #23 : shapes$Corner │ │ └── body[0] > return > match: │ │ ├── type: @primitives$String - │ │ ├── scrutinees[0]: corner #18 : shapes$Corner + │ │ ├── scrutinees[0]: corner #23 : shapes$Corner │ │ ├── arms[0] > arm: │ │ │ ├── patterns[0]: topLeft │ │ │ └── body[0] > return > construct: @@ -265,8 +296,8 @@ │ │ ├── type: @primitives$String │ │ ├── ctor: @primitives$String │ │ └── args[0]: "bottom" : @concepts$String - │ ├── decls[24] > verb: - │ │ ├── signature: @primitives$Unit main() #25 + │ ├── decls[28] > verb: + │ │ ├── signature: @primitives$Unit main() #29 │ │ ├── params: │ │ ├── body[0] > do > call: │ │ │ ├── type: @primitives$Unit @@ -276,35 +307,35 @@ │ │ │ ├── ctor: @primitives$String │ │ │ └── args[0]: "hello" : @concepts$String │ │ ├── body[1] > let: - │ │ │ ├── local: origin #19 : shapes$Vec2 + │ │ │ ├── local: origin #24 : shapes$Vec2 │ │ │ └── value > construct: │ │ │ ├── type: shapes$Vec2 - │ │ │ ├── ctor: Vec2.zero #32 + │ │ │ ├── ctor: Vec2.zero #36 │ │ │ └── args: │ │ ├── body[2] > let: - │ │ │ ├── local: moved #20 : shapes$Vec2 + │ │ │ ├── local: moved #25 : shapes$Vec2 │ │ │ └── value > op: │ │ │ ├── type: shapes$Vec2 │ │ │ ├── op: + - │ │ │ ├── impl: + #34 - │ │ │ ├── left: origin #19 : shapes$Vec2 + │ │ │ ├── impl: + #38 + │ │ │ ├── left: origin #24 : shapes$Vec2 │ │ │ └── right > coerce: │ │ │ ├── type: shapes$Vec2 - │ │ │ ├── ctor: Vec2 #38 + │ │ │ ├── ctor: Vec2 #42 │ │ │ └── value: 3.0 : @concepts$Float │ │ ├── body[3] > do > call: │ │ │ ├── type: @primitives$Unit - │ │ │ ├── callee: scale #37 - │ │ │ ├── args[0]: moved #20 : shapes$Vec2 + │ │ │ ├── callee: scale #41 + │ │ │ ├── args[0]: moved #25 : shapes$Vec2 │ │ │ └── args[1] > construct: │ │ │ ├── type: @primitives$Float │ │ │ ├── ctor: @primitives$Float │ │ │ └── args[0]: 2.0 : @concepts$Float │ │ ├── body[4] > let: - │ │ │ ├── local: flat #21 : shapes$Vec2 + │ │ │ ├── local: flat #26 : shapes$Vec2 │ │ │ └── value > construct_fields: │ │ │ ├── type: shapes$Vec2 - │ │ │ ├── ctor: Vec2 #33 + │ │ │ ├── ctor: Vec2 #37 │ │ │ └── fields[0] > field: │ │ │ ├── name: x (slot 0) │ │ │ └── value > construct: @@ -312,13 +343,13 @@ │ │ │ ├── ctor: @primitives$Float │ │ │ └── args[0]: 1.0 : @concepts$Float │ │ ├── body[5] > let: - │ │ │ ├── local: size #22 : @primitives$Float + │ │ │ ├── local: size #27 : @primitives$Float │ │ │ └── value > call: │ │ │ ├── type: @primitives$Float - │ │ │ ├── callee: length #36 - │ │ │ └── args[0]: moved #20 : shapes$Vec2 + │ │ │ ├── callee: length #40 + │ │ │ └── args[0]: moved #25 : shapes$Vec2 │ │ ├── body[6] > let: - │ │ │ ├── local: shape #23 : shapes$Shape + │ │ │ ├── local: shape #28 : shapes$Shape │ │ │ └── value > case: │ │ │ ├── type: shapes$Shape │ │ │ ├── case: circle @@ -327,16 +358,16 @@ │ │ │ ├── ctor: @primitives$Float │ │ │ └── args[0]: 2.0 : @concepts$Float │ │ ├── body[7] > let: - │ │ │ ├── local: covered #24 : @primitives$Float + │ │ │ ├── local: covered #29 : @primitives$Float │ │ │ └── value > call: │ │ │ ├── type: @primitives$Float - │ │ │ ├── callee: area #43 - │ │ │ └── args[0]: shape #23 : shapes$Shape + │ │ │ ├── callee: area #47 + │ │ │ └── args[0]: shape #28 : shapes$Shape │ │ ├── body[8] > let: - │ │ │ ├── local: radius #25 : @primitives$Float + │ │ │ ├── local: radius #30 : @primitives$Float │ │ │ └── value > case_read: │ │ │ ├── type: @primitives$Float - │ │ │ ├── target: shape #23 : shapes$Shape + │ │ │ ├── target: shape #28 : shapes$Shape │ │ │ ├── case: circle │ │ │ └── handler: │ │ │ ├── binder: none @@ -345,27 +376,27 @@ │ │ │ ├── ctor: @primitives$Float │ │ │ └── args[0]: 0.0 : @concepts$Float │ │ ├── body[9] > let: - │ │ │ ├── local: half #27 : @primitives$Float + │ │ │ ├── local: half #32 : @primitives$Float │ │ │ └── value > call: │ │ │ ├── type: @primitives$Float │ │ │ ├── callee: safeDivide #23 - │ │ │ ├── args[0]: covered #24 : @primitives$Float + │ │ │ ├── args[0]: covered #29 : @primitives$Float │ │ │ ├── args[1] > construct: │ │ │ │ ├── type: @primitives$Float │ │ │ │ ├── ctor: @primitives$Float │ │ │ │ └── args[0]: 2.0 : @concepts$Float │ │ │ └── handler: - │ │ │ ├── binder: reason #26 : @primitives$String + │ │ │ ├── binder: reason #31 : @primitives$String │ │ │ ├── body[0] > do > call: │ │ │ │ ├── type: @primitives$Unit │ │ │ │ ├── callee: say #11 - │ │ │ │ └── args[0]: reason #26 : @primitives$String + │ │ │ │ └── args[0]: reason #31 : @primitives$String │ │ │ └── body[1] > resolve > construct: │ │ │ ├── type: @primitives$Float │ │ │ ├── ctor: @primitives$Float │ │ │ └── args[0]: 0.0 : @concepts$Float │ │ ├── body[10] > let: - │ │ │ ├── local: picked #28 : @primitives$Int + │ │ │ ├── local: picked #33 : @primitives$Int │ │ │ └── value > call: │ │ │ ├── type: @primitives$Int │ │ │ ├── callee: pick #12 with T = @primitives$Int @@ -379,7 +410,7 @@ │ │ │ │ └── args[0]: 2 : @concepts$Int │ │ │ └── args[2]: true : @primitives$Bool │ │ ├── body[11] > let: - │ │ │ ├── local: chance #29 : @primitives$Float + │ │ │ ├── local: chance #34 : @primitives$Float │ │ │ └── value > call: │ │ │ ├── type: @primitives$Float │ │ │ ├── callee: pick #12 with T = @primitives$Float @@ -393,7 +424,7 @@ │ │ │ │ └── args[0]: 1.5 : @concepts$Float │ │ │ └── args[2]: false : @primitives$Bool │ │ ├── body[12] > let: - │ │ │ ├── local: pair #30 : app$Pair<@primitives$Int> + │ │ │ ├── local: pair #35 : app$Pair<@primitives$Int> │ │ │ └── value > construct: │ │ │ ├── type: app$Pair<@primitives$Int> │ │ │ ├── ctor: Pair #14 with T = @primitives$Int @@ -406,13 +437,13 @@ │ │ │ ├── ctor: @primitives$Int │ │ │ └── args[0]: 4 : @concepts$Int │ │ ├── body[13] > let: - │ │ │ ├── local: total #31 : @primitives$Int + │ │ │ ├── local: total #36 : @primitives$Int │ │ │ └── value > call: │ │ │ ├── type: @primitives$Int │ │ │ ├── callee: sum #15 with T = @primitives$Int - │ │ │ └── args[0]: pair #30 : app$Pair<@primitives$Int> + │ │ │ └── args[0]: pair #35 : app$Pair<@primitives$Int> │ │ ├── body[14] > let: - │ │ │ ├── local: floats #32 : app$Pair<@primitives$Float> + │ │ │ ├── local: floats #37 : app$Pair<@primitives$Float> │ │ │ └── value > construct: │ │ │ ├── type: app$Pair<@primitives$Float> │ │ │ ├── ctor: Pair #14 with T = @primitives$Float @@ -425,13 +456,87 @@ │ │ │ ├── ctor: @primitives$Float │ │ │ └── args[0]: 2.0 : @concepts$Float │ │ ├── body[15] > let: - │ │ │ ├── local: both #33 : @primitives$Float + │ │ │ ├── local: both #38 : @primitives$Float │ │ │ └── value > call: │ │ │ ├── type: @primitives$Float │ │ │ ├── callee: sum #15 with T = @primitives$Float - │ │ │ └── args[0]: floats #32 : app$Pair<@primitives$Float> + │ │ │ └── args[0]: floats #37 : app$Pair<@primitives$Float> │ │ ├── body[16] > let: - │ │ │ ├── local: numbers #34 : @primitives$Array<@primitives$Int, 3> + │ │ │ ├── local: spare #39 : ^shapes$Shape + │ │ │ └── value > case: + │ │ │ ├── type: shapes$Shape + │ │ │ ├── case: circle + │ │ │ └── payload > construct: + │ │ │ ├── type: @primitives$Float + │ │ │ ├── ctor: @primitives$Float + │ │ │ └── args[0]: 1.0 : @concepts$Float + │ │ ├── body[17] > assign: + │ │ │ ├── target: spare #39 : shapes$Shape + │ │ │ └── value > call: + │ │ │ ├── type: shapes$Shape + │ │ │ ├── callee: grown #26 + │ │ │ └── args[0]: spare #39 : shapes$Shape + │ │ ├── body[18] > let: + │ │ │ ├── local: crew #40 : app$Crew + │ │ │ └── value > construct: + │ │ │ ├── type: app$Crew + │ │ │ ├── ctor: Crew #25 + │ │ │ ├── args[0]: spare #39 : shapes$Shape + │ │ │ └── args[1]: shape #28 : shapes$Shape + │ │ ├── body[19] > let: + │ │ │ ├── local: lead #41 : &shapes$Shape + │ │ │ └── value > call: + │ │ │ ├── type: &shapes$Shape + │ │ │ ├── callee: leadOf #27 + │ │ │ └── args[0]: crew #40 : app$Crew + │ │ ├── body[20] > let: + │ │ │ ├── local: shapes #42 : @primitives$ArrayRef + │ │ │ └── value > construct: + │ │ │ ├── type: @primitives$ArrayRef + │ │ │ ├── ctor: @primitives$ArrayRef with T = shapes$Shape, n = 2 + │ │ │ └── args[0] > array: + │ │ │ ├── type: @concepts$Array + │ │ │ ├── items[0] > case: + │ │ │ │ ├── type: shapes$Shape + │ │ │ │ ├── case: circle + │ │ │ │ └── payload > construct: + │ │ │ │ ├── type: @primitives$Float + │ │ │ │ ├── ctor: @primitives$Float + │ │ │ │ └── args[0]: 3.0 : @concepts$Float + │ │ │ └── items[1] > case: + │ │ │ ├── type: shapes$Shape + │ │ │ ├── case: circle + │ │ │ └── payload > construct: + │ │ │ ├── type: @primitives$Float + │ │ │ ├── ctor: @primitives$Float + │ │ │ └── args[0]: 4.0 : @concepts$Float + │ │ ├── body[21] > let: + │ │ │ ├── local: second #43 : &shapes$Shape + │ │ │ └── value > subscript: + │ │ │ ├── type: shapes$Shape + │ │ │ ├── impl: @primitives$[] with T = shapes$Shape, n = 2 + │ │ │ ├── target: shapes #42 : @primitives$ArrayRef + │ │ │ └── args[0] > construct: + │ │ │ ├── type: @primitives$Int + │ │ │ ├── ctor: @primitives$Int + │ │ │ └── args[0]: 2 : @concepts$Int + │ │ ├── body[22] > let: + │ │ │ ├── local: tiles #45 : @primitives$ArrayRef<@primitives$Int, 4> + │ │ │ └── value > construct: + │ │ │ ├── type: @primitives$ArrayRef<@primitives$Int, 4> + │ │ │ ├── ctor: @primitives$ArrayRef.fill with T = @primitives$Int, n = 4 + │ │ │ ├── args[0]: 4 : @concepts$Int + │ │ │ └── args[1] > lambda: + │ │ │ ├── type: @primitives$Int[@primitives$Int] + │ │ │ ├── params[0]: n #44 : @primitives$Int + │ │ │ └── body[0] > return > op: + │ │ │ ├── type: @primitives$Int + │ │ │ ├── op: * + │ │ │ ├── impl: @primitives$* + │ │ │ ├── left: n #44 : @primitives$Int + │ │ │ └── right: n #44 : @primitives$Int + │ │ ├── body[23] > let: + │ │ │ ├── local: numbers #46 : @primitives$Array<@primitives$Int, 3> │ │ │ └── value > construct: │ │ │ ├── type: @primitives$Array<@primitives$Int, 3> │ │ │ ├── ctor: @primitives$Array with T = @primitives$Int, n = 3 @@ -449,83 +554,83 @@ │ │ │ ├── type: @primitives$Int │ │ │ ├── ctor: @primitives$Int │ │ │ └── args[0]: 3 : @concepts$Int - │ │ ├── body[17] > let: - │ │ │ ├── local: first #35 : @primitives$Int + │ │ ├── body[24] > let: + │ │ │ ├── local: first #47 : @primitives$Int │ │ │ └── value > subscript: │ │ │ ├── type: @primitives$Int │ │ │ ├── impl: @primitives$[] with T = @primitives$Int, n = 3 - │ │ │ ├── target: numbers #34 : @primitives$Array<@primitives$Int, 3> + │ │ │ ├── target: numbers #46 : @primitives$Array<@primitives$Int, 3> │ │ │ └── args[0] > construct: │ │ │ ├── type: @primitives$Int │ │ │ ├── ctor: @primitives$Int │ │ │ └── args[0]: 1 : @concepts$Int - │ │ ├── body[18] > let: - │ │ │ ├── local: length #36 : @primitives$Int + │ │ ├── body[25] > let: + │ │ │ ├── local: length #48 : @primitives$Int │ │ │ └── value > call: │ │ │ ├── type: @primitives$Int │ │ │ ├── callee: count #16 with T = @primitives$Int, n = 3 - │ │ │ └── args[0]: numbers #34 : @primitives$Array<@primitives$Int, 3> - │ │ ├── body[19] > let: - │ │ │ ├── local: sized #37 : @primitives$Int + │ │ │ └── args[0]: numbers #46 : @primitives$Array<@primitives$Int, 3> + │ │ ├── body[26] > let: + │ │ │ ├── local: sized #49 : @primitives$Int │ │ │ └── value > call: │ │ │ ├── type: @primitives$Int │ │ │ ├── callee: measured #17 with n = 3 - │ │ │ ├── args[0]: numbers #34 : @primitives$Array<@primitives$Int, 3> + │ │ │ ├── args[0]: numbers #46 : @primitives$Array<@primitives$Int, 3> │ │ │ └── args[1]: 3 : @concepts$Int - │ │ ├── body[20] > let: - │ │ │ ├── local: like #38 : @primitives$Int + │ │ ├── body[27] > let: + │ │ │ ├── local: like #50 : @primitives$Int │ │ │ └── value > call: │ │ │ ├── type: @primitives$Int │ │ │ ├── callee: sizedLike #20 with n = 3 - │ │ │ ├── args[0]: numbers #34 : @primitives$Array<@primitives$Int, 3> + │ │ │ ├── args[0]: numbers #46 : @primitives$Array<@primitives$Int, 3> │ │ │ └── args[1]: 3 : @concepts$Int - │ │ ├── body[21] > let: - │ │ │ ├── local: passed #39 : @primitives$Int + │ │ ├── body[28] > let: + │ │ │ ├── local: passed #51 : @primitives$Int │ │ │ └── value > call: │ │ │ ├── type: @primitives$Int │ │ │ ├── callee: forwarded #19 with count = 3 - │ │ │ ├── args[0]: numbers #34 : @primitives$Array<@primitives$Int, 3> + │ │ │ ├── args[0]: numbers #46 : @primitives$Array<@primitives$Int, 3> │ │ │ └── args[1]: 3 : @concepts$Int - │ │ ├── body[22] > let: - │ │ │ ├── local: ints #40 : app$Pair<@primitives$Int> + │ │ ├── body[29] > let: + │ │ │ ├── local: ints #52 : app$Pair<@primitives$Int> │ │ │ └── value > call: │ │ │ ├── type: app$Pair<@primitives$Int> │ │ │ ├── callee: pairOf #21 │ │ │ ├── args[0]: 2 : @concepts$Int │ │ │ └── args[1]: 3 : @concepts$Int - │ │ ├── body[23] > let: - │ │ │ ├── local: decimals #41 : app$Pair<@primitives$Float> + │ │ ├── body[30] > let: + │ │ │ ├── local: decimals #53 : app$Pair<@primitives$Float> │ │ │ └── value > call: │ │ │ ├── type: app$Pair<@primitives$Float> │ │ │ ├── callee: pairOf #22 │ │ │ ├── args[0]: 2.5 : @concepts$Float │ │ │ └── args[1]: 3.5 : @concepts$Float - │ │ ├── body[24] > let: - │ │ │ ├── local: label #42 : @primitives$String + │ │ ├── body[31] > let: + │ │ │ ├── local: label #54 : @primitives$String │ │ │ └── value > map_read: │ │ │ ├── type: @primitives$String │ │ │ ├── target: .topLeft : shapes$Corner - │ │ │ └── map: label #41 - │ │ ├── body[25] > let: - │ │ │ ├── local: side #43 : @primitives$String + │ │ │ └── map: label #45 + │ │ ├── body[32] > let: + │ │ │ ├── local: side #55 : @primitives$String │ │ │ └── value > call: │ │ │ ├── type: @primitives$String - │ │ │ ├── callee: describe #24 + │ │ │ ├── callee: describe #28 │ │ │ └── args[0]: .bottomRight : shapes$Corner - │ │ ├── body[26] > do > call: + │ │ ├── body[33] > do > call: │ │ │ ├── type: @primitives$Unit │ │ │ ├── callee: say #11 - │ │ │ └── args[0]: side #43 : @primitives$String - │ │ ├── body[27] > let: - │ │ │ ├── local: i #44 : @primitives$Int + │ │ │ └── args[0]: side #55 : @primitives$String + │ │ ├── body[34] > let: + │ │ │ ├── local: i #56 : @primitives$Int │ │ │ └── value > construct: │ │ │ ├── type: @primitives$Int │ │ │ ├── ctor: @primitives$Int │ │ │ └── args[0]: 1 : @concepts$Int - │ │ ├── body[28] > do > call: + │ │ ├── body[35] > do > call: │ │ │ ├── type: @primitives$Unit │ │ │ ├── callee: to #10 - │ │ │ ├── args[0]: i #44 : @primitives$Int + │ │ │ ├── args[0]: i #56 : @primitives$Int │ │ │ ├── args[1] > construct: │ │ │ │ ├── type: @primitives$Int │ │ │ │ ├── ctor: @primitives$Int @@ -535,30 +640,30 @@ │ │ │ ├── callee: say #11 │ │ │ └── args[0] > call: │ │ │ ├── type: @primitives$String - │ │ │ ├── callee: describe #24 + │ │ │ ├── callee: describe #28 │ │ │ └── args[0]: .topLeft : shapes$Corner - │ │ ├── body[29] > let: - │ │ │ ├── local: double #46 : @primitives$Float[@primitives$Float] + │ │ ├── body[36] > let: + │ │ │ ├── local: double #58 : @primitives$Float[@primitives$Float] │ │ │ └── value > lambda: │ │ │ ├── type: @primitives$Float[@primitives$Float] - │ │ │ ├── params[0]: value #45 : @primitives$Float + │ │ │ ├── params[0]: value #57 : @primitives$Float │ │ │ └── body[0] > return > op: │ │ │ ├── type: @primitives$Float │ │ │ ├── op: * │ │ │ ├── impl: @primitives$* - │ │ │ ├── left: value #45 : @primitives$Float + │ │ │ ├── left: value #57 : @primitives$Float │ │ │ └── right > construct: │ │ │ ├── type: @primitives$Float │ │ │ ├── ctor: @primitives$Float │ │ │ └── args[0]: 2.0 : @concepts$Float - │ │ ├── body[30] > let: - │ │ │ ├── local: doubled #47 : @primitives$Float + │ │ ├── body[37] > let: + │ │ │ ├── local: doubled #59 : @primitives$Float │ │ │ └── value > call_value: │ │ │ ├── type: @primitives$Float - │ │ │ ├── callee: double #46 : @primitives$Float[@primitives$Float] - │ │ │ └── args[0]: half #27 : @primitives$Float - │ │ ├── body[31] > let: - │ │ │ ├── local: chain #48 : @primitives$Bool + │ │ │ ├── callee: double #58 : @primitives$Float[@primitives$Float] + │ │ │ └── args[0]: half #32 : @primitives$Float + │ │ ├── body[38] > let: + │ │ │ ├── local: chain #60 : @primitives$Bool │ │ │ └── value > call: │ │ │ ├── type: @primitives$Bool │ │ │ ├── callee: if #7 @@ -566,7 +671,7 @@ │ │ │ │ ├── type: @primitives$Bool │ │ │ │ ├── op: < │ │ │ │ ├── impl: @primitives$< - │ │ │ │ ├── left: total #31 : @primitives$Int + │ │ │ │ ├── left: total #36 : @primitives$Int │ │ │ │ └── right > construct: │ │ │ │ ├── type: @primitives$Int │ │ │ │ ├── ctor: @primitives$Int @@ -578,15 +683,15 @@ │ │ │ ├── type: @primitives$String │ │ │ ├── ctor: @primitives$String │ │ │ └── args[0]: "small" : @concepts$String - │ │ ├── body[32] > do > call: + │ │ ├── body[39] > do > call: │ │ │ ├── type: @primitives$Unit │ │ │ ├── callee: elif #8 - │ │ │ ├── args[0]: chain #48 : @primitives$Bool + │ │ │ ├── args[0]: chain #60 : @primitives$Bool │ │ │ ├── args[1] > op: │ │ │ │ ├── type: @primitives$Bool │ │ │ │ ├── op: == │ │ │ │ ├── impl: @primitives$== - │ │ │ │ ├── left: total #31 : @primitives$Int + │ │ │ │ ├── left: total #36 : @primitives$Int │ │ │ │ └── right > construct: │ │ │ │ ├── type: @primitives$Int │ │ │ │ ├── ctor: @primitives$Int @@ -598,10 +703,10 @@ │ │ │ ├── type: @primitives$String │ │ │ ├── ctor: @primitives$String │ │ │ └── args[0]: "seven" : @concepts$String - │ │ ├── body[33] > do > call: + │ │ ├── body[40] > do > call: │ │ │ ├── type: @primitives$Unit │ │ │ ├── callee: else #9 - │ │ │ ├── args[0]: chain #48 : @primitives$Bool + │ │ │ ├── args[0]: chain #60 : @primitives$Bool │ │ │ └── args[1] > block[0] > do > call: │ │ │ ├── type: @primitives$Unit │ │ │ ├── callee: say #11 @@ -609,43 +714,43 @@ │ │ │ ├── type: @primitives$String │ │ │ ├── ctor: @primitives$String │ │ │ └── args[0]: "large" : @concepts$String - │ │ └── body[34] > return > construct: + │ │ └── body[41] > return > construct: │ │ ├── type: @primitives$Unit │ │ ├── ctor: @primitives$Unit │ │ └── args: - │ └── decls[25] > verb: - │ ├── signature: T twice(T) #26 + │ └── decls[29] > verb: + │ ├── signature: T twice(T) #30 │ └── body: checked per instance ├── packages[1] > package: │ ├── name: shapes │ ├── decls[0] > alias: - │ │ ├── name: Float #27 + │ │ ├── name: Float #31 │ │ └── target: @primitives$Float │ ├── decls[1] > alias: - │ │ ├── name: Unit #28 + │ │ ├── name: Unit #32 │ │ └── target: @primitives$Unit │ ├── decls[2] > alias: - │ │ ├── name: String #29 + │ │ ├── name: String #33 │ │ └── target: @primitives$String │ ├── decls[3] > type: - │ │ ├── name: Vec2 #30 + │ │ ├── name: Vec2 #34 │ │ ├── kind: value │ │ ├── struct[0]: x : @primitives$Float │ │ └── struct[1]: y : @primitives$Float │ ├── decls[4] > verb: - │ │ ├── signature: Vec2(@primitives$Float, @primitives$Float) #31 - │ │ ├── params[0]: x #49 : @primitives$Float - │ │ ├── params[1]: y #50 : @primitives$Float + │ │ ├── signature: Vec2(@primitives$Float, @primitives$Float) #35 + │ │ ├── params[0]: x #61 : @primitives$Float + │ │ ├── params[1]: y #62 : @primitives$Float │ │ └── body[0] > return > init: │ │ ├── type: shapes$Vec2 │ │ ├── fields[0] > field: │ │ │ ├── name: x (slot 0) - │ │ │ └── value: x #49 : @primitives$Float + │ │ │ └── value: x #61 : @primitives$Float │ │ └── fields[1] > field: │ │ ├── name: y (slot 1) - │ │ └── value: y #50 : @primitives$Float + │ │ └── value: y #62 : @primitives$Float │ ├── decls[5] > verb: - │ │ ├── signature: Vec2.zero() #32 + │ │ ├── signature: Vec2.zero() #36 │ │ ├── params: │ │ └── body[0] > return > init: │ │ ├── type: shapes$Vec2 @@ -662,35 +767,35 @@ │ │ ├── ctor: @primitives$Float │ │ └── args[0]: 0.0 : @concepts$Float │ ├── decls[6] > verb: - │ │ ├── signature: Vec2{x @primitives$Float; y @primitives$Float = ...} #33 - │ │ ├── params[0]: x #53 : @primitives$Float - │ │ ├── params[1]: y #54 : @primitives$Float + │ │ ├── signature: Vec2{x @primitives$Float; y @primitives$Float = ...} #37 + │ │ ├── params[0]: x #65 : @primitives$Float + │ │ ├── params[1]: y #66 : @primitives$Float │ │ └── body[0] > return > init: │ │ ├── type: shapes$Vec2 │ │ ├── fields[0] > field: │ │ │ ├── name: x (slot 0) - │ │ │ └── value: x #53 : @primitives$Float + │ │ │ └── value: x #65 : @primitives$Float │ │ └── fields[1] > field: │ │ ├── name: y (slot 1) - │ │ └── value: y #54 : @primitives$Float + │ │ └── value: y #66 : @primitives$Float │ ├── decls[7] > verb: - │ │ ├── signature: shapes$Vec2 +(shapes$Vec2, shapes$Vec2) #34 - │ │ ├── params[0]: left #55 : shapes$Vec2 - │ │ ├── params[1]: right #56 : shapes$Vec2 + │ │ ├── signature: shapes$Vec2 +(shapes$Vec2, shapes$Vec2) #38 + │ │ ├── params[0]: left #67 : shapes$Vec2 + │ │ ├── params[1]: right #68 : shapes$Vec2 │ │ └── body[0] > return > construct: │ │ ├── type: shapes$Vec2 - │ │ ├── ctor: Vec2 #31 + │ │ ├── ctor: Vec2 #35 │ │ ├── args[0] > op: │ │ │ ├── type: @primitives$Float │ │ │ ├── op: + │ │ │ ├── impl: @primitives$+ │ │ │ ├── left > field: │ │ │ │ ├── type: @primitives$Float - │ │ │ │ ├── target: left #55 : shapes$Vec2 + │ │ │ │ ├── target: left #67 : shapes$Vec2 │ │ │ │ └── field: x (slot 0) │ │ │ └── right > field: │ │ │ ├── type: @primitives$Float - │ │ │ ├── target: right #56 : shapes$Vec2 + │ │ │ ├── target: right #68 : shapes$Vec2 │ │ │ └── field: x (slot 0) │ │ └── args[1] > op: │ │ ├── type: @primitives$Float @@ -698,35 +803,35 @@ │ │ ├── impl: @primitives$+ │ │ ├── left > field: │ │ │ ├── type: @primitives$Float - │ │ │ ├── target: left #55 : shapes$Vec2 + │ │ │ ├── target: left #67 : shapes$Vec2 │ │ │ └── field: y (slot 1) │ │ └── right > field: │ │ ├── type: @primitives$Float - │ │ ├── target: right #56 : shapes$Vec2 + │ │ ├── target: right #68 : shapes$Vec2 │ │ └── field: y (slot 1) │ ├── decls[8] > verb: - │ │ ├── signature: shapes$Vec2 ~(shapes$Vec2) #35 - │ │ ├── params[0]: value #57 : shapes$Vec2 + │ │ ├── signature: shapes$Vec2 ~(shapes$Vec2) #39 + │ │ ├── params[0]: value #69 : shapes$Vec2 │ │ └── body[0] > return > construct: │ │ ├── type: shapes$Vec2 - │ │ ├── ctor: Vec2 #31 + │ │ ├── ctor: Vec2 #35 │ │ ├── args[0] > flip: │ │ │ ├── type: @primitives$Float │ │ │ ├── impl: @primitives$~ │ │ │ └── value > field: │ │ │ ├── type: @primitives$Float - │ │ │ ├── target: value #57 : shapes$Vec2 + │ │ │ ├── target: value #69 : shapes$Vec2 │ │ │ └── field: x (slot 0) │ │ └── args[1] > flip: │ │ ├── type: @primitives$Float │ │ ├── impl: @primitives$~ │ │ └── value > field: │ │ ├── type: @primitives$Float - │ │ ├── target: value #57 : shapes$Vec2 + │ │ ├── target: value #69 : shapes$Vec2 │ │ └── field: y (slot 1) │ ├── decls[9] > verb: - │ │ ├── signature: @primitives$Float length(this shapes$Vec2) #36 - │ │ ├── params[0]: this #58 : shapes$Vec2 + │ │ ├── signature: @primitives$Float length(this shapes$Vec2) #40 + │ │ ├── params[0]: this #70 : shapes$Vec2 │ │ └── body[0] > return > op: │ │ ├── type: @primitives$Float │ │ ├── op: + @@ -737,11 +842,11 @@ │ │ │ ├── impl: @primitives$* │ │ │ ├── left > field: │ │ │ │ ├── type: @primitives$Float - │ │ │ │ ├── target: this #58 : shapes$Vec2 + │ │ │ │ ├── target: this #70 : shapes$Vec2 │ │ │ │ └── field: x (slot 0) │ │ │ └── right > field: │ │ │ ├── type: @primitives$Float - │ │ │ ├── target: this #58 : shapes$Vec2 + │ │ │ ├── target: this #70 : shapes$Vec2 │ │ │ └── field: x (slot 0) │ │ └── right > op: │ │ ├── type: @primitives$Float @@ -749,20 +854,20 @@ │ │ ├── impl: @primitives$* │ │ ├── left > field: │ │ │ ├── type: @primitives$Float - │ │ │ ├── target: this #58 : shapes$Vec2 + │ │ │ ├── target: this #70 : shapes$Vec2 │ │ │ └── field: y (slot 1) │ │ └── right > field: │ │ ├── type: @primitives$Float - │ │ ├── target: this #58 : shapes$Vec2 + │ │ ├── target: this #70 : shapes$Vec2 │ │ └── field: y (slot 1) │ ├── decls[10] > verb: - │ │ ├── signature: @primitives$Unit scale(this shapes$Vec2, @primitives$Float) mut #37 - │ │ ├── params[0]: this #59 : shapes$Vec2 - │ │ ├── params[1]: by #60 : @primitives$Float + │ │ ├── signature: @primitives$Unit scale(this shapes$Vec2, @primitives$Float) mut #41 + │ │ ├── params[0]: this #71 : shapes$Vec2 + │ │ ├── params[1]: by #72 : @primitives$Float │ │ ├── body[0] > assign: │ │ │ ├── target > field: │ │ │ │ ├── type: @primitives$Float - │ │ │ │ ├── target: this #59 : shapes$Vec2 + │ │ │ │ ├── target: this #71 : shapes$Vec2 │ │ │ │ └── field: x (slot 0) │ │ │ └── value > op: │ │ │ ├── type: @primitives$Float @@ -770,13 +875,13 @@ │ │ │ ├── impl: @primitives$* │ │ │ ├── left > field: │ │ │ │ ├── type: @primitives$Float - │ │ │ │ ├── target: this #59 : shapes$Vec2 + │ │ │ │ ├── target: this #71 : shapes$Vec2 │ │ │ │ └── field: x (slot 0) - │ │ │ └── right: by #60 : @primitives$Float + │ │ │ └── right: by #72 : @primitives$Float │ │ ├── body[1] > assign: │ │ │ ├── target > field: │ │ │ │ ├── type: @primitives$Float - │ │ │ │ ├── target: this #59 : shapes$Vec2 + │ │ │ │ ├── target: this #71 : shapes$Vec2 │ │ │ │ └── field: y (slot 1) │ │ │ └── value > op: │ │ │ ├── type: @primitives$Float @@ -784,16 +889,16 @@ │ │ │ ├── impl: @primitives$* │ │ │ ├── left > field: │ │ │ │ ├── type: @primitives$Float - │ │ │ │ ├── target: this #59 : shapes$Vec2 + │ │ │ │ ├── target: this #71 : shapes$Vec2 │ │ │ │ └── field: y (slot 1) - │ │ │ └── right: by #60 : @primitives$Float + │ │ │ └── right: by #72 : @primitives$Float │ │ └── body[2] > return > construct: │ │ ├── type: @primitives$Unit │ │ ├── ctor: @primitives$Unit │ │ └── args: │ ├── decls[11] > verb: - │ │ ├── signature: implicit Vec2(@concepts$Float) #38 - │ │ ├── params[0]: value #61 : @concepts$Float + │ │ ├── signature: implicit Vec2(@concepts$Float) #42 + │ │ ├── params[0]: value #73 : @concepts$Float │ │ └── body[0] > return > init: │ │ ├── type: shapes$Vec2 │ │ ├── fields[0] > field: @@ -801,28 +906,28 @@ │ │ │ └── value > construct: │ │ │ ├── type: @primitives$Float │ │ │ ├── ctor: @primitives$Float - │ │ │ └── args[0]: value #61 : @concepts$Float + │ │ │ └── args[0]: value #73 : @concepts$Float │ │ └── fields[1] > field: │ │ ├── name: y (slot 1) │ │ └── value > construct: │ │ ├── type: @primitives$Float │ │ ├── ctor: @primitives$Float - │ │ └── args[0]: value #61 : @concepts$Float + │ │ └── args[0]: value #73 : @concepts$Float │ ├── decls[12] > type: - │ │ ├── name: Shape #39 + │ │ ├── name: Shape #43 │ │ ├── kind: reference │ │ ├── variant[0]: circle : @primitives$Float │ │ ├── variant[1]: square : @primitives$Float │ │ └── variant[2]: point : shapes$Vec2 │ ├── decls[13] > type: - │ │ ├── name: Corner #40 + │ │ ├── name: Corner #44 │ │ ├── kind: value │ │ ├── enum[0]: topLeft │ │ ├── enum[1]: topRight │ │ ├── enum[2]: bottomLeft │ │ └── enum[3]: bottomRight │ ├── decls[14] > enum_map: - │ │ ├── map: shapes$Corner.label #41 + │ │ ├── map: shapes$Corner.label #45 │ │ ├── type: @primitives$String │ │ ├── entries[0]: │ │ │ ├── member: topLeft @@ -849,42 +954,42 @@ │ │ ├── ctor: @primitives$String │ │ └── args[0]: "bottom right" : @concepts$String │ ├── decls[15] > verb: - │ │ ├── signature: @primitives$Float _half(@primitives$Float) #42 - │ │ ├── params[0]: value #62 : @primitives$Float + │ │ ├── signature: @primitives$Float _half(@primitives$Float) #46 + │ │ ├── params[0]: value #74 : @primitives$Float │ │ └── body[0] > return > op: │ │ ├── type: @primitives$Float │ │ ├── op: / │ │ ├── impl: @primitives$/ - │ │ ├── left: value #62 : @primitives$Float + │ │ ├── left: value #74 : @primitives$Float │ │ └── right > construct: │ │ ├── type: @primitives$Float │ │ ├── ctor: @primitives$Float │ │ └── args[0]: 2.0 : @concepts$Float │ └── decls[16] > verb: - │ ├── signature: @primitives$Float area(&shapes$Shape) #43 - │ ├── params[0]: shape #63 : &shapes$Shape + │ ├── signature: @primitives$Float area(&shapes$Shape) #47 + │ ├── params[0]: shape #75 : &shapes$Shape │ └── body[0] > return > match: │ ├── type: @primitives$Float - │ ├── scrutinees[0]: shape #63 : &shapes$Shape + │ ├── scrutinees[0]: shape #75 : &shapes$Shape │ ├── arms[0] > arm: - │ │ ├── patterns[0]: r #64 : @primitives$Float <- circle + │ │ ├── patterns[0]: r #76 : @primitives$Float <- circle │ │ └── body[0] > return > op: │ │ ├── type: @primitives$Float │ │ ├── op: * │ │ ├── impl: @primitives$* - │ │ ├── left: r #64 : @primitives$Float - │ │ └── right: r #64 : @primitives$Float + │ │ ├── left: r #76 : @primitives$Float + │ │ └── right: r #76 : @primitives$Float │ ├── arms[1] > arm: - │ │ ├── patterns[0]: s #65 : @primitives$Float <- square + │ │ ├── patterns[0]: s #77 : @primitives$Float <- square │ │ └── body[0] > return > call: │ │ ├── type: @primitives$Float - │ │ ├── callee: _half #42 + │ │ ├── callee: _half #46 │ │ └── args[0] > op: │ │ ├── type: @primitives$Float │ │ ├── op: + │ │ ├── impl: @primitives$+ - │ │ ├── left: s #65 : @primitives$Float - │ │ └── right: s #65 : @primitives$Float + │ │ ├── left: s #77 : @primitives$Float + │ │ └── right: s #77 : @primitives$Float │ └── arms[2] > arm: │ ├── patterns[0]: point │ └── body[0] > return > construct: @@ -893,137 +998,137 @@ │ └── args[0]: 0.0 : @concepts$Float ├── instances[0] > instance: │ ├── of: pick #12 with T = @primitives$Float - │ ├── params[0]: first #75 : @primitives$Float - │ ├── params[1]: second #76 : @primitives$Float - │ ├── params[2]: takeFirst #77 : @primitives$Bool + │ ├── params[0]: first #87 : @primitives$Float + │ ├── params[1]: second #88 : @primitives$Float + │ ├── params[2]: takeFirst #89 : @primitives$Bool │ ├── body[0] > let: - │ │ ├── local: chosen #78 : @primitives$Float - │ │ └── value: second #76 : @primitives$Float + │ │ ├── local: chosen #90 : @primitives$Float + │ │ └── value: second #88 : @primitives$Float │ ├── body[1] > let: - │ │ ├── local: ran #79 : @primitives$Bool + │ │ ├── local: ran #91 : @primitives$Bool │ │ └── value > call: │ │ ├── type: @primitives$Bool │ │ ├── callee: if #7 - │ │ ├── args[0]: takeFirst #77 : @primitives$Bool + │ │ ├── args[0]: takeFirst #89 : @primitives$Bool │ │ └── args[1] > block[0] > assign: - │ │ ├── target: chosen #78 : @primitives$Float - │ │ └── value: first #75 : @primitives$Float - │ └── body[2] > return: chosen #78 : @primitives$Float + │ │ ├── target: chosen #90 : @primitives$Float + │ │ └── value: first #87 : @primitives$Float + │ └── body[2] > return: chosen #90 : @primitives$Float ├── instances[1] > instance: │ ├── of: pick #12 with T = @primitives$Int - │ ├── params[0]: first #70 : @primitives$Int - │ ├── params[1]: second #71 : @primitives$Int - │ ├── params[2]: takeFirst #72 : @primitives$Bool + │ ├── params[0]: first #82 : @primitives$Int + │ ├── params[1]: second #83 : @primitives$Int + │ ├── params[2]: takeFirst #84 : @primitives$Bool │ ├── body[0] > let: - │ │ ├── local: chosen #73 : @primitives$Int - │ │ └── value: second #71 : @primitives$Int + │ │ ├── local: chosen #85 : @primitives$Int + │ │ └── value: second #83 : @primitives$Int │ ├── body[1] > let: - │ │ ├── local: ran #74 : @primitives$Bool + │ │ ├── local: ran #86 : @primitives$Bool │ │ └── value > call: │ │ ├── type: @primitives$Bool │ │ ├── callee: if #7 - │ │ ├── args[0]: takeFirst #72 : @primitives$Bool + │ │ ├── args[0]: takeFirst #84 : @primitives$Bool │ │ └── args[1] > block[0] > assign: - │ │ ├── target: chosen #73 : @primitives$Int - │ │ └── value: first #70 : @primitives$Int - │ └── body[2] > return: chosen #73 : @primitives$Int + │ │ ├── target: chosen #85 : @primitives$Int + │ │ └── value: first #82 : @primitives$Int + │ └── body[2] > return: chosen #85 : @primitives$Int ├── instances[2] > instance: │ ├── of: Pair #14 with T = @primitives$Float - │ ├── params[0]: left #68 : @primitives$Float - │ ├── params[1]: right #69 : @primitives$Float + │ ├── params[0]: left #80 : @primitives$Float + │ ├── params[1]: right #81 : @primitives$Float │ └── body[0] > return > init: │ ├── type: app$Pair<@primitives$Float> │ ├── fields[0] > field: │ │ ├── name: left (slot 0) - │ │ └── value: left #68 : @primitives$Float + │ │ └── value: left #80 : @primitives$Float │ └── fields[1] > field: │ ├── name: right (slot 1) - │ └── value: right #69 : @primitives$Float + │ └── value: right #81 : @primitives$Float ├── instances[3] > instance: │ ├── of: Pair #14 with T = @primitives$Int - │ ├── params[0]: left #66 : @primitives$Int - │ ├── params[1]: right #67 : @primitives$Int + │ ├── params[0]: left #78 : @primitives$Int + │ ├── params[1]: right #79 : @primitives$Int │ └── body[0] > return > init: │ ├── type: app$Pair<@primitives$Int> │ ├── fields[0] > field: │ │ ├── name: left (slot 0) - │ │ └── value: left #66 : @primitives$Int + │ │ └── value: left #78 : @primitives$Int │ └── fields[1] > field: │ ├── name: right (slot 1) - │ └── value: right #67 : @primitives$Int + │ └── value: right #79 : @primitives$Int ├── instances[4] > instance: │ ├── of: sum #15 with T = @primitives$Float - │ ├── params[0]: pair #81 : app$Pair<@primitives$Float> + │ ├── params[0]: pair #93 : app$Pair<@primitives$Float> │ └── body[0] > return > op: │ ├── type: @primitives$Float │ ├── op: + │ ├── impl: @primitives$+ │ ├── left > field: │ │ ├── type: @primitives$Float - │ │ ├── target: pair #81 : app$Pair<@primitives$Float> + │ │ ├── target: pair #93 : app$Pair<@primitives$Float> │ │ └── field: left (slot 0) │ └── right > field: │ ├── type: @primitives$Float - │ ├── target: pair #81 : app$Pair<@primitives$Float> + │ ├── target: pair #93 : app$Pair<@primitives$Float> │ └── field: right (slot 1) ├── instances[5] > instance: │ ├── of: sum #15 with T = @primitives$Int - │ ├── params[0]: pair #80 : app$Pair<@primitives$Int> + │ ├── params[0]: pair #92 : app$Pair<@primitives$Int> │ └── body[0] > return > op: │ ├── type: @primitives$Int │ ├── op: + │ ├── impl: @primitives$+ │ ├── left > field: │ │ ├── type: @primitives$Int - │ │ ├── target: pair #80 : app$Pair<@primitives$Int> + │ │ ├── target: pair #92 : app$Pair<@primitives$Int> │ │ └── field: left (slot 0) │ └── right > field: │ ├── type: @primitives$Int - │ ├── target: pair #80 : app$Pair<@primitives$Int> + │ ├── target: pair #92 : app$Pair<@primitives$Int> │ └── field: right (slot 1) ├── instances[6] > instance: │ ├── of: count #16 with T = @primitives$Int, n = 3 - │ ├── params[0]: values #82 : @primitives$Array<@primitives$Int, 3> + │ ├── params[0]: values #94 : @primitives$Array<@primitives$Int, 3> │ └── body[0] > return > construct: │ ├── type: @primitives$Int │ ├── ctor: @primitives$Int │ └── args[0]: n = 3 : @concepts$Int ├── instances[7] > instance: │ ├── of: measured #17 with n = 3 - │ ├── params[0]: values #83 : @primitives$Array<@primitives$Int, 3> + │ ├── params[0]: values #95 : @primitives$Array<@primitives$Int, 3> │ └── body[0] > return > construct: │ ├── type: @primitives$Int │ ├── ctor: @primitives$Int │ └── args[0]: n = 3 : @concepts$Int ├── instances[8] > instance: │ ├── of: relayed #18 with n = 3 - │ ├── params[0]: values #88 : @primitives$Array<@primitives$Int, 3> + │ ├── params[0]: values #100 : @primitives$Array<@primitives$Int, 3> │ └── body[0] > return > call: │ ├── type: @primitives$Int │ ├── callee: measured #17 with n = 3 - │ ├── args[0]: values #88 : @primitives$Array<@primitives$Int, 3> + │ ├── args[0]: values #100 : @primitives$Array<@primitives$Int, 3> │ └── args[1]: n = 3 : @concepts$Int ├── instances[9] > instance: │ ├── of: forwarded #19 with count = 3 - │ ├── params[0]: values #87 : @primitives$Array<@primitives$Int, 3> + │ ├── params[0]: values #99 : @primitives$Array<@primitives$Int, 3> │ └── body[0] > return > call: │ ├── type: @primitives$Int │ ├── callee: relayed #18 with n = 3 - │ ├── args[0]: values #87 : @primitives$Array<@primitives$Int, 3> + │ ├── args[0]: values #99 : @primitives$Array<@primitives$Int, 3> │ └── args[1]: count = 3 : @concepts$Int └── instances[10] > instance: ├── of: sizedLike #20 with n = 3 - ├── params[0]: values #84 : @primitives$Array<@primitives$Int, 3> + ├── params[0]: values #96 : @primitives$Array<@primitives$Int, 3> ├── body[0] > let: - │ ├── local: measure #86 : @primitives$Int[@primitives$Array<@primitives$Int, 3>] + │ ├── local: measure #98 : @primitives$Int[@primitives$Array<@primitives$Int, 3>] │ └── value > lambda: │ ├── type: @primitives$Int[@primitives$Array<@primitives$Int, 3>] - │ ├── params[0]: held #85 : @primitives$Array<@primitives$Int, 3> + │ ├── params[0]: held #97 : @primitives$Array<@primitives$Int, 3> │ └── body[0] > return > construct: │ ├── type: @primitives$Int │ ├── ctor: @primitives$Int │ └── args[0]: n = 3 : @concepts$Int └── body[1] > return > call_value: ├── type: @primitives$Int - ├── callee: measure #86 : @primitives$Int[@primitives$Array<@primitives$Int, 3>] - └── args[0]: values #84 : @primitives$Array<@primitives$Int, 3> + ├── callee: measure #98 : @primitives$Int[@primitives$Array<@primitives$Int, 3>] + └── args[0]: values #96 : @primitives$Array<@primitives$Int, 3> diff --git a/tests/semantics/golden/typing.accept.tst.spans b/tests/semantics/golden/typing.accept.tst.spans index c2cab0d0..09850305 100644 --- a/tests/semantics/golden/typing.accept.tst.spans +++ b/tests/semantics/golden/typing.accept.tst.spans @@ -138,6 +138,31 @@ package app expr Op / | numerator / denominator expr Var numerator | numerator expr Var denominator | denominator + decl Type Crew | type Crew = #struct { lead Shape; backup &Shape; } + decl Verb Crew | Crew(lead ^Shape, backup & … ) => init{ lead; backup; } + param lead | lead ^Shape + param backup | backup &Shape + block | => init{ lead; backup; } + stat Return | => init{ lead; backup; } + expr Init | init{ lead; backup; } + field lead | lead + expr Var lead | lead + field backup | backup + expr Var backup | backup + decl Verb grown | ^Shape grown(shape ^Shape) … pe = shape; return kept; } + param shape | shape ^Shape + block | { kept ^Shape = shape; return kept; } + stat Let | kept ^Shape = shape; + local kept | kept + expr Var shape | shape + stat Return | return kept; + expr Var kept | kept + decl Verb leadOf | &Shape leadOf(crew &Crew) => crew.lead + param crew | crew &Crew + block | => crew.lead + stat Return | => crew.lead + expr Field lead | crew.lead + expr Var crew | crew decl Verb describe | String describe(corner Cor … ht] => String("bottom"); } param corner | corner Corner block | => match (corner) { [topLe … ht] => String("bottom"); } @@ -269,6 +294,51 @@ package app local both | both expr Call sum | sum(floats) expr Var floats | floats + stat Let | spare ^Shape = Shape.circle(Float(1.0)); + local spare | spare + expr Case circle | Shape.circle(Float(1.0)) + expr Construct @primitives$Float | Float(1.0) + expr Decimal_lit | 1.0 + stat Assign | spare = grown(spare); + expr Var spare | spare + expr Call grown | grown(spare) + expr Var spare | spare + stat Let | crew Crew(spare, shape); + local crew | crew + expr Construct Crew | Crew(spare, shape) + expr Var spare | spare + expr Var shape | shape + stat Let | lead &Shape = leadOf(crew); + local lead | lead + expr Call leadOf | leadOf(crew) + expr Var crew | crew + stat Let | shapes @primitives$ArrayRe … hape.circle(Float(4.0))]); + local shapes | shapes + expr Construct @primitives$ArrayRef | @primitives$ArrayRef([Shap … Shape.circle(Float(4.0))]) + expr Array_lit | [Shape.circle(Float(3.0)), Shape.circle(Float(4.0))] + expr Case circle | Shape.circle(Float(3.0)) + expr Construct @primitives$Float | Float(3.0) + expr Decimal_lit | 3.0 + expr Case circle | Shape.circle(Float(4.0)) + expr Construct @primitives$Float | Float(4.0) + expr Decimal_lit | 4.0 + stat Let | second &Shape = shapes[Int(2)]; + local second | second + expr Subscript | shapes[Int(2)] + expr Var shapes | shapes + expr Construct @primitives$Int | Int(2) + expr Integer_lit | 2 + stat Let | tiles @primitives$ArrayRef … l(4, Int(n Int) => n * n); + local tiles | tiles + expr Construct @primitives$ArrayRef.fill | @primitives$ArrayRef.fill(4, Int(n Int) => n * n) + expr Integer_lit | 4 + expr Lambda | Int(n Int) => n * n + param n | n Int + block | => n * n + stat Return | => n * n + expr Op * | n * n + expr Var n | n + expr Var n | n stat Let | numbers Array = @p … [Int(1), Int(2), Int(3)]); local numbers | numbers expr Construct @primitives$Array | @primitives$Array([Int(1), Int(2), Int(3)]) diff --git a/tests/semantics/golden/typing.reject.err b/tests/semantics/golden/typing.reject.err index d4af3ea9..4c465a89 100644 --- a/tests/semantics/golden/typing.reject.err +++ b/tests/semantics/golden/typing.reject.err @@ -125,7 +125,7 @@ Error: `guard` can exit, which ends the block its call is written in, and this c File "fixtures/typing/reject/bad/exits.zn", line 19, characters 23-44: 19 | Bool lazily(condition @concepts$Block) => true | ^^^^^^^^^^^^^^^^^^^^^ -Error: `@concepts$Block` takes no type argument: a block yields nothing (docs/spec-divergences.md §12) +Error: `@concepts$Block` takes no type argument: a block yields nothing (docs/spec-divergences.md §11) File "fixtures/typing/reject/bad/exits.zn", line 23, characters 9-23: 23 | resolve ready; | ^^^^^^^^^^^^^^ @@ -162,46 +162,6 @@ File "fixtures/typing/reject/bad/expansions.zn", line 39, characters 45-58: 39 | Int around(stand Stand, n @concepts$Int) => stand[Int(n)] | ^^^^^^^^^^^^^ Error: `around` expands into itself: it takes a block or a literal, so each call to it is written out in place of the call, and this call is reached again -File "fixtures/typing/reject/bad/guests.zn", line 29, characters 5-6: -29 | k!fill(); - | ^ -Error: a `!` call writes its subject, and it reaches a guest taken from `pen`, a read-only parameter -File "fixtures/typing/reject/bad/guests.zn", line 31, characters 5-9: -31 | desk!use(); - | ^^^^ -Error: a `!` call writes its subject, and it reaches a guest taken from `pen`, a read-only parameter -File "fixtures/typing/reject/bad/guests.zn", line 33, characters 5-9: -33 | back!fill(); - | ^^^^ -Error: a `!` call writes its subject, and it reaches a guest taken from `pen`, a read-only parameter -File "fixtures/typing/reject/bad/guests.zn", line 38, characters 5-9: -38 | mine!use(); - | ^^^^ -Error: a `!` call writes its subject, and it reaches a guest taken from `pen`, a read-only parameter -File "fixtures/typing/reject/bad/guests.zn", line 46, characters 5-6: -46 | k!fill(); - | ^ -Error: a `!` call writes its subject, and it reaches a guest taken from `this`, which is read-only in a method that is not `mut` -File "fixtures/typing/reject/bad/guests.zn", line 55, characters 5-9: -55 | this!use(); - | ^^^^ -Error: a `!` call writes its subject, and it reaches a guest taken from `pen`, a read-only parameter -File "fixtures/typing/reject/bad/guests.zn", line 64, characters 9-13: -64 | desk!use(); - | ^^^^ -Error: a `!` call writes its subject, and it reaches a guest taken from `pen`, a read-only parameter -File "fixtures/typing/reject/bad/guests.zn", line 65, characters 9-13: -65 | desk!hold(pen); - | ^^^^ -Error: a `!` call writes its subject, and it reaches a guest taken from `pen`, a read-only parameter -File "fixtures/typing/reject/bad/guests.zn", line 98, characters 5-6: -98 | a!fill(); - | ^ -Error: a `!` call writes its subject, and it reaches a guest taken from `pen`, a read-only parameter -File "fixtures/typing/reject/bad/guests.zn", line 99, characters 5-6: -99 | b!fill(); - | ^ -Error: a `!` call writes its subject, and it reaches a guest taken from `pen`, a read-only parameter File "fixtures/typing/reject/bad/imports.zn", line 3, characters 8-15: 3 | import missing; | ^^^^^^^ @@ -302,6 +262,54 @@ File "fixtures/typing/reject/bad/literals.zn", line 61, characters 16-36: 61 | Bin{ count Int(99999999999999999999); } => init{ count; } | ^^^^^^^^^^^^^^^^^^^^ Error: `99999999999999999999` is out of range for `@primitives$Int` +File "fixtures/typing/reject/bad/marks.zn", line 8, characters 30-35: +8 | type Tether = #struct { kite ^Kite; } + | ^^^^^ +Error: `^bad$Kite` is a roaming owner, and `^` is written only on a local, a parameter or a return type, so it cannot be a field +File "fixtures/typing/reject/bad/marks.zn", line 10, characters 20-25: +10 | alias Kites = List<^Kite> + | ^^^^^ +Error: `^bad$Kite` is a roaming owner, and `^` is written only on a local, a parameter or a return type, never inside another type's arguments +File "fixtures/typing/reject/bad/marks.zn", line 12, characters 15-19: +12 | Unit fly(span ^Int) => Unit() + | ^^^^ +Error: `@primitives$Int` is a value type, and `^` marks only a roaming owner of a reference type; a value is never owned, only copied +File "fixtures/typing/reject/bad/marks.zn", line 14, characters 1-31: +14 | Unit tug(this ^Kite) => Unit() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: the subject is written `^bad$Kite`, and it is always a borrow of the object the method is called on, so neither `^` nor `&` is written on `this` +File "fixtures/typing/reject/bad/marks.zn", line 19, characters 1-29: +19 | Kite build() => Kite(Int(1)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: `build` returns `bad$Kite`, a reference type, bare; a borrow is never returned, so write `^bad$Kite` to hand back an owner or `&bad$Kite` to hand back a reference +File "fixtures/typing/reject/bad/marks.zn", line 20, characters 1-28: +20 | Int?Kite refuse() => Int(0) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: `refuse` aborts with `bad$Kite`, a reference type, bare; a borrow is never returned, so write `^bad$Kite` to hand back an owner or `&bad$Kite` to hand back a reference +File "fixtures/typing/reject/bad/marks.zn", line 21, characters 1-38: +21 | Unit launch(make Kite[Int]) => Unit() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: this function type returns `bad$Kite`, a reference type, bare; a borrow is never returned, so write `^bad$Kite` to hand back an owner or `&bad$Kite` to hand back a reference +File "fixtures/typing/reject/bad/marks.zn", line 25, characters 1-32: +25 | Unit hold(kite ^Kite) => Unit() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: illegal overload set: this function differs from the one at fixtures/typing/reject/bad/marks.zn:24 only by the passing mode on a parameter; rename one declaration or choose a single signature +File "fixtures/typing/reject/bad/marks.zn", line 35, characters 22-26: +35 | alias Kited = Hamper + | ^^^^ +Error: `bad$Kite` is a reference type, and it reaches `bad$Hamper`'s field `item`, a value type's slot; a value type holds only values, all the way down +File "fixtures/typing/reject/bad/marks.zn", line 38, characters 14-26: +38 | a Hamper(Kite(Int(2))); + | ^^^^^^^^^^^^ +Error: `bad$Kite` is a reference type, and it reaches `bad$Hamper`'s field `item`, a value type's slot; a value type holds only values, all the way down +File "fixtures/typing/reject/bad/marks.zn", line 40, characters 21-33: +40 | c Hamper = pack(Kite(Int(4))); + | ^^^^^^^^^^^^ +Error: `bad$Kite` is a reference type, and it reaches `bad$Hamper`'s field `item`, a value type's slot; a value type holds only values, all the way down +File "fixtures/typing/reject/bad/marks.zn", line 41, characters 25-53: +41 | d @primitives$Array([Kite(Int(5)), Kite(Int(6))]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: `bad$Kite` is a reference type, and it reaches the elements of `@primitives$Array`, a value type's slot; a value type holds only values, all the way down File "fixtures/typing/reject/bad/matches.zn", line 6, characters 29-44: 6 | Int answer(value Answer) => match (value) { | ^^^^^^^^^^^^^^^ @@ -326,46 +334,66 @@ File "fixtures/typing/reject/bad/matches.zn", line 17, characters 32-37: 17 | Int plain(value Int) => match (value) { | ^^^^^ Error: a match dispatches on a variant or an enum, and `@primitives$Int` is neither -File "fixtures/typing/reject/bad/moves.zn", line 20, characters 13-23: -20 | b Cup = saucer.cup; +File "fixtures/typing/reject/bad/moves.zn", line 21, characters 13-23: +21 | b Cup = saucer.cup; | ^^^^^^^^^^ -Error: a field is not a move-source: a host is moved only from a symbol, a verb's result or a case form -File "fixtures/typing/reject/bad/moves.zn", line 21, characters 13-25: -21 | c Cup = cups[Int(1)]; +Error: a part of a borrow is not a move-source: an owner is moved only from a roaming symbol (one declared `^T`), a field of one, a verb's result or a case form +File "fixtures/typing/reject/bad/moves.zn", line 22, characters 13-25: +22 | c Cup = cups[Int(1)]; | ^^^^^^^^^^^^ -Error: an element of a container is not a move-source: a host is moved only from a symbol, a verb's result or a case form -File "fixtures/typing/reject/bad/moves.zn", line 22, characters 13-18: -22 | d Cup = guest; - | ^^^^^ -Error: `guest`, a guest, is not a move-source: a host is moved only from a symbol, a verb's result or a case form -File "fixtures/typing/reject/bad/moves.zn", line 24, characters 11-35: -24 | drink(order.hot ?? Cup(Int(3))); +Error: an element of a container is not a move-source: an owner is moved only from a roaming symbol (one declared `^T`), a field of one, a verb's result or a case form +File "fixtures/typing/reject/bad/moves.zn", line 23, characters 13-16: +23 | d Cup = ref; + | ^^^ +Error: a reference, which names an owner rather than being one, is not a move-source: an owner is moved only from a roaming symbol (one declared `^T`), a field of one, a verb's result or a case form +File "fixtures/typing/reject/bad/moves.zn", line 25, characters 11-35: +25 | drink(order.hot ?? Cup(Int(3))); | ^^^^^^^^^^^^^^^^^^^^^^^^ -Error: a variant case's payload is not a move-source: a host is moved only from a symbol, a verb's result or a case form -File "fixtures/typing/reject/bad/moves.zn", line 32, characters 15-20: -32 | drink(outer); +Error: a variant case's payload is not a move-source: an owner is moved only from a roaming symbol (one declared `^T`), a field of one, a verb's result or a case form +File "fixtures/typing/reject/bad/moves.zn", line 27, characters 13-20: +27 | f Cup = settled; + | ^^^^^^^ +Error: `settled` is a settled owner, which may be referenced and so never moves; an owner that is to move is declared `^T` +File "fixtures/typing/reject/bad/moves.zn", line 34, characters 11-14: +34 | drink(cup); + | ^^^ +Error: `cup` is a borrow: the caller lends it for the call, and a borrow is never moved, stored or returned; a parameter that takes its argument is declared `^T` +File "fixtures/typing/reject/bad/moves.zn", line 38, characters 30-34: +38 | ^Saucer give(this Saucer) => this + | ^^^^ +Error: `this` is a borrow of the object the method was called on, and a method never moves it +File "fixtures/typing/reject/bad/moves.zn", line 45, characters 15-20: +45 | drink(outer); | ^^^^^ -Error: `outer` is declared in an enclosing block, and a symbol is moved only in the block that declares it -File "fixtures/typing/reject/bad/moves.zn", line 47, characters 10-13: -47 | look(cup); - | ^^^ -Error: `cup` was moved on line 46, and is spent until a store refills it -File "fixtures/typing/reject/bad/moves.zn", line 49, characters 9-12: -49 | cup = Cup(Int(7)); +Error: `outer` is declared in an enclosing block, and a roaming owner is moved only in the block that declares it +File "fixtures/typing/reject/bad/moves.zn", line 60, characters 9-12: +60 | see(cup); | ^^^ -Error: `cup` is spent, and a store that refills it must be in the block that declares it -File "fixtures/typing/reject/bad/moves.zn", line 54, characters 17-21: -54 | serve(both, both); +Error: `cup` was moved on line 59, and is spent until a store refills it +File "fixtures/typing/reject/bad/moves.zn", line 62, characters 9-12: +62 | cup = Cup(Int(7)); + | ^^^ +Error: `cup` is spent, and a store that refills it must be in the block that declares `cup` +File "fixtures/typing/reject/bad/moves.zn", line 67, characters 17-21: +67 | serve(both, both); | ^^^^ -Error: `both` was moved on line 54, and is spent until a store refills it -File "fixtures/typing/reject/bad/moves.zn", line 59, characters 29-33: -59 | Saucer give(this Saucer) => this - | ^^^^ -Error: `this` is the object the method was called on, and a method never moves it -File "fixtures/typing/reject/bad/moves.zn", line 65, characters 10-13: -65 | look(cup); - | ^^^ -Error: `cup` was moved on line 64, and is spent until a store refills it +Error: `both` was moved on line 67, and is spent until a store refills it +File "fixtures/typing/reject/bad/moves.zn", line 77, characters 9-19: +77 | see(saucer.cup); + | ^^^^^^^^^^ +Error: `saucer.cup` was moved on line 76, and is spent until a store refills it +File "fixtures/typing/reject/bad/moves.zn", line 78, characters 21-27: +78 | whole ^Saucer = saucer; + | ^^^^^^ +Error: `saucer` is partly spent: its field `cup` was moved on line 76, and the owner is spent as a whole until a store refills that field +File "fixtures/typing/reject/bad/moves.zn", line 80, characters 9-19: +80 | saucer.cup = Cup(Int(11)); + | ^^^^^^^^^^ +Error: `saucer.cup` is spent, and a store that refills it must be in the block that declares `saucer` +File "fixtures/typing/reject/bad/moves.zn", line 91, characters 9-12: +91 | see(cup); + | ^^^ +Error: `cup` was moved on line 90, and is spent until a store refills it File "fixtures/typing/reject/bad/names.zn", line 4, characters 6-11: 4 | type Twice = struct { b Int; } | ^^^^^ @@ -446,102 +474,162 @@ File "fixtures/typing/reject/bad/overloads.zn", line 39, characters 1-18: 39 | Int?Unit main() { | ^^^^^^^^^^^^^^^^^ Error: `main` declares no abort type: no caller exists to handle an abort -File "fixtures/typing/reject/bad/owners.zn", line 22, characters 13-23: +File "fixtures/typing/reject/bad/readonly.zn", line 29, characters 5-6: +29 | k!fill(); + | ^ +Error: a `!` call writes its subject, and it reaches a reference taken from `pen`, a read-only parameter +File "fixtures/typing/reject/bad/readonly.zn", line 31, characters 5-9: +31 | desk!use(); + | ^^^^ +Error: a `!` call writes its subject, and it reaches a reference taken from `pen`, a read-only parameter +File "fixtures/typing/reject/bad/readonly.zn", line 33, characters 5-9: +33 | back!fill(); + | ^^^^ +Error: a `!` call writes its subject, and it reaches a reference taken from `pen`, a read-only parameter +File "fixtures/typing/reject/bad/readonly.zn", line 38, characters 5-9: +38 | mine!use(); + | ^^^^ +Error: a `!` call writes its subject, and it reaches a reference taken from `pen`, a read-only parameter +File "fixtures/typing/reject/bad/readonly.zn", line 46, characters 5-6: +46 | k!fill(); + | ^ +Error: a `!` call writes its subject, and it reaches a reference taken from `this`, which is read-only in a method that is not `mut` +File "fixtures/typing/reject/bad/readonly.zn", line 55, characters 5-9: +55 | this!use(); + | ^^^^ +Error: a `!` call writes its subject, and it reaches a reference taken from `pen`, a read-only parameter +File "fixtures/typing/reject/bad/readonly.zn", line 64, characters 9-13: +64 | desk!use(); + | ^^^^ +Error: a `!` call writes its subject, and it reaches a reference taken from `pen`, a read-only parameter +File "fixtures/typing/reject/bad/readonly.zn", line 65, characters 9-13: +65 | desk!hold(pen); + | ^^^^ +Error: a `!` call writes its subject, and it reaches a reference taken from `pen`, a read-only parameter +File "fixtures/typing/reject/bad/readonly.zn", line 98, characters 5-6: +98 | a!fill(); + | ^ +Error: a `!` call writes its subject, and it reaches a reference taken from `pen`, a read-only parameter +File "fixtures/typing/reject/bad/readonly.zn", line 99, characters 5-6: +99 | b!fill(); + | ^ +Error: a `!` call writes its subject, and it reaches a reference taken from `pen`, a read-only parameter +File "fixtures/typing/reject/bad/rests.zn", line 36, characters 15-20: +36 | this!wire(local); + | ^^^^^ +Error: this stores a reference to `local` in the caller's `this`, which outlives the body `local` is scoped to (`wire` keeps `port` in `this`) +File "fixtures/typing/reject/bad/rests.zn", line 46, characters 25-29: +46 | frame.plug!wire(near); + | ^^^^ +Error: this stores a reference to `near`, whose block ends before `frame`'s does (`wire` keeps `port` in `this`) +File "fixtures/typing/reject/bad/rests.zn", line 47, characters 26-30: +47 | frame.plug!relay(near); + | ^^^^ +Error: this stores a reference to `near`, whose block ends before `frame`'s does (`relay` keeps `port` in `this`) +File "fixtures/typing/reject/bad/rests.zn", line 50, characters 22-27: +50 | frame.plug = spare; + | ^^^^^ +Error: this stores a reference to `near`, whose block ends before `frame`'s does +File "fixtures/typing/reject/bad/rests.zn", line 64, characters 20-28: +64 | plugs!push(arriving); + | ^^^^^^^^ +Error: this stores a reference to `near`, whose block ends before `plugs`'s does (`push` keeps `value` in `this`) +File "fixtures/typing/reject/bad/rests.zn", line 66, characters 18-24: +66 | bay!dock(docked); + | ^^^^^^ +Error: this stores a reference to `near`, whose block ends before `bay`'s does (`dock` keeps `plug` in `this`) +File "fixtures/typing/reject/bad/scopes.zn", line 22, characters 13-23: 22 | r = inner.leaf; | ^^^^^^^^^^ -Error: this stores a guest to `inner`, whose block ends before `r`'s does -File "fixtures/typing/reject/bad/owners.zn", line 37, characters 18-26: +Error: this stores a reference to `inner`, whose block ends before `r`'s does +File "fixtures/typing/reject/bad/scopes.zn", line 37, characters 18-26: 37 | parked = arriving; | ^^^^^^^^ -Error: this stores a guest to `near`, whose block ends before `parked`'s does -File "fixtures/typing/reject/bad/owners.zn", line 38, characters 15-36: +Error: this stores a reference to `near`, whose block ends before `parked`'s does +File "fixtures/typing/reject/bad/scopes.zn", line 38, characters 15-36: 38 | tag = Tag.pinned(near.leaf); | ^^^^^^^^^^^^^^^^^^^^^ -Error: this stores a guest to `near`, whose block ends before `tag`'s does -File "fixtures/typing/reject/bad/owners.zn", line 49, characters 12-17: +Error: this stores a reference to `near`, whose block ends before `tag`'s does +File "fixtures/typing/reject/bad/scopes.zn", line 49, characters 12-17: 49 | return value; | ^^^^^ -Error: this stores a guest to `value` in the caller's result, which outlives the body `value` is owned by -File "fixtures/typing/reject/bad/owners.zn", line 55, characters 17-22: +Error: this stores a reference to `value` in the caller's result, which outlives the body `value` is scoped to +File "fixtures/typing/reject/bad/scopes.zn", line 55, characters 17-22: 55 | this.leaf = local; | ^^^^^ -Error: this stores a guest to `local` in the caller's `this`, which outlives the body `local` is owned by -File "fixtures/typing/reject/bad/owners.zn", line 63, characters 12-23: +Error: this stores a reference to `local` in the caller's `this`, which outlives the body `local` is scoped to +File "fixtures/typing/reject/bad/scopes.zn", line 63, characters 12-23: 63 | return pass(value); | ^^^^^^^^^^^ -Error: this stores a guest to `value` in the caller's result, which outlives the body `value` is owned by -File "fixtures/typing/reject/bad/owners.zn", line 73, characters 13-24: +Error: this stores a reference to `value` in the caller's result, which outlives the body `value` is scoped to +File "fixtures/typing/reject/bad/scopes.zn", line 73, characters 13-24: 73 | r = match (t) { | ^^^^^^^^^^^ -Error: this stores a guest to `near`, whose block ends before `r`'s does -File "fixtures/typing/reject/bad/rests.zn", line 35, characters 15-20: -35 | this!wire(local); - | ^^^^^ -Error: this stores a guest to `local` in the caller's `this`, which outlives the body `local` is owned by (`wire` keeps `port` in `this`) -File "fixtures/typing/reject/bad/rests.zn", line 45, characters 25-29: -45 | frame.plug!wire(near); - | ^^^^ -Error: this stores a guest to `near`, whose block ends before `frame`'s does (`wire` keeps `port` in `this`) -File "fixtures/typing/reject/bad/rests.zn", line 46, characters 26-30: -46 | frame.plug!relay(near); - | ^^^^ -Error: this stores a guest to `near`, whose block ends before `frame`'s does (`relay` keeps `port` in `this`) -File "fixtures/typing/reject/bad/rests.zn", line 49, characters 22-27: -49 | frame.plug = spare; - | ^^^^^ -Error: this stores a guest to `near`, whose block ends before `frame`'s does -File "fixtures/typing/reject/bad/rests.zn", line 63, characters 20-28: -63 | plugs!push(arriving); - | ^^^^^^^^ -Error: this stores a guest to `near`, whose block ends before `plugs`'s does (`push` keeps `value` in `this`) -File "fixtures/typing/reject/bad/rests.zn", line 65, characters 18-24: -65 | bay!dock(docked); - | ^^^^^^ -Error: this stores a guest to `near`, whose block ends before `bay`'s does (`dock` keeps `plug` in `this`) -File "fixtures/typing/reject/bad/sources.zn", line 18, characters 15-28: -18 | b &Bell = bells[Int(0)]; +Error: this stores a reference to `near`, whose block ends before `r`'s does +File "fixtures/typing/reject/bad/sources.zn", line 19, characters 15-28: +19 | b &Bell = bells[Int(1)]; | ^^^^^^^^^^^^^ -Error: a new guest must come from a stable place, and this path crosses `[]` -File "fixtures/typing/reject/bad/sources.zn", line 20, characters 15-37: -20 | d &Bell = slot.full ?? rack.bell; +Error: a new reference must come from a settled place, and a list's element is roaming: elements come and go while the list lives; an `ArrayRef`'s elements may be referenced +File "fixtures/typing/reject/bad/sources.zn", line 21, characters 15-37: +21 | d &Bell = slot.full ?? rack.bell; | ^^^^^^^^^^^^^^^^^^^^^^ -Error: a new guest must come from a stable place, and a variant case's payload is not one -File "fixtures/typing/reject/bad/sources.zn", line 21, characters 15-22: -21 | e &Bell = forge(); +Error: a new reference must come from a settled place, and a variant case's payload is roaming: the variant may change case while it lives +File "fixtures/typing/reject/bad/sources.zn", line 22, characters 15-22: +22 | e &Bell = forge(); | ^^^^^^^ -Error: a new guest names a place, and this is a temporary value -File "fixtures/typing/reject/bad/sources.zn", line 22, characters 20-33: -22 | f &Bell = ring(bells[Int(0)]); +Error: a new reference names a place, and this is a temporary value +File "fixtures/typing/reject/bad/sources.zn", line 23, characters 20-33: +23 | f &Bell = ring(bells[Int(1)]); | ^^^^^^^^^^^^^ -Error: a new guest must come from a stable place, and this path crosses `[]` -File "fixtures/typing/reject/bad/sources.zn", line 25, characters 24-25: -25 | x full => ring(x).tone; +Error: a new reference must come from a settled place, and a list's element is roaming: elements come and go while the list lives; an `ArrayRef`'s elements may be referenced +File "fixtures/typing/reject/bad/sources.zn", line 26, characters 24-25: +26 | x full => ring(x).tone; | ^ -Error: a new guest must come from a stable place, and a variant case's payload is not one -File "fixtures/typing/reject/bad/sources.zn", line 29, characters 18-31: -29 | tones[weight(bells[Int(0)])] = Int(1); +Error: a new reference must come from a settled place, and a variant case's payload is roaming: the variant may change case while it lives +File "fixtures/typing/reject/bad/sources.zn", line 30, characters 18-31: +30 | tones[weight(bells[Int(1)])] = Int(1); | ^^^^^^^^^^^^^ -Error: a new guest must come from a stable place, and this path crosses `[]` -File "fixtures/typing/reject/bad/sources.zn", line 37, characters 17-21: -37 | this.hook = bell; - | ^^^^ -Error: `bell` swallows its argument, so it may not be bound into `&` storage; a parameter stored as a guest is declared `&` -File "fixtures/typing/reject/bad/sources.zn", line 40, characters 17-21: -40 | this.hook = held; +Error: a new reference must come from a settled place, and a list's element is roaming: elements come and go while the list lives; an `ArrayRef`'s elements may be referenced +File "fixtures/typing/reject/bad/sources.zn", line 39, characters 15-20: +39 | s &Bell = spare; + | ^^^^^ +Error: a new reference must come from a settled place, and this is a roaming owner, which may still move; settle it first by moving it into an owner declared without `^` +File "fixtures/typing/reject/bad/sources.zn", line 43, characters 15-28: +43 | l &Bell = loose[Int(1)]; + | ^^^^^^^^^^^^^ +Error: a new reference must come from a settled place, and this is a roaming owner, which may still move; settle it first by moving it into an owner declared without `^` +File "fixtures/typing/reject/bad/sources.zn", line 57, characters 15-31: +57 | b &Bell = cupboard[Int(1)]; + | ^^^^^^^^^^^^^^^^ +Error: a new reference must come from a settled place, and a list's element is roaming: elements come and go while the list lives; an `ArrayRef`'s elements may be referenced +File "fixtures/typing/reject/bad/sources.zn", line 65, characters 17-21: +65 | this.hook = bell; | ^^^^ -Error: `bell` swallows its argument, so it may not be bound into `&` storage; a parameter stored as a guest is declared `&` -File "fixtures/typing/reject/bad/sources.zn", line 43, characters 21-26: -43 | this.hook = later; - | ^^^^^ -Error: `bell` swallows its argument, so it may not be bound into `&` storage; a parameter stored as a guest is declared `&` -File "fixtures/typing/reject/bad/sources.zn", line 55, characters 5-14: -55 | this.hook.tone = Int(3); +Error: a new reference must come from a settled place, and this is a borrow, which is the caller's only for the call; take the parameter as `&T` to keep a reference to it +File "fixtures/typing/reject/bad/sources.zn", line 67, characters 18-22: +67 | held &Bell = bell; + | ^^^^ +Error: a new reference must come from a settled place, and this is a borrow, which is the caller's only for the call; take the parameter as `&T` to keep a reference to it +File "fixtures/typing/reject/bad/sources.zn", line 71, characters 17-26: +71 | later = this.bell; + | ^^^^^^^^^ +Error: `this` is a borrow, never a source of a reference; a verb that hands out a reference into an object takes the object as an `&T` parameter +File "fixtures/typing/reject/bad/sources.zn", line 76, characters 26-30: +76 | &Bell lend(bell Bell) => bell + | ^^^^ +Error: a new reference must come from a settled place, and this is a borrow, which is the caller's only for the call; take the parameter as `&T` to keep a reference to it +File "fixtures/typing/reject/bad/sources.zn", line 77, characters 25-34: +77 | &Bell own(this Rack) => this.bell + | ^^^^^^^^^ +Error: `this` is a borrow, never a source of a reference; a verb that hands out a reference into an object takes the object as an `&T` parameter +File "fixtures/typing/reject/bad/sources.zn", line 83, characters 5-14: +83 | this.hook.tone = Int(3); | ^^^^^^^^^ -Error: a store may not go through a guest, since what it names belongs to a tree this path's root does not own; change it with a `mut` method called through the guest -File "fixtures/typing/reject/bad/sources.zn", line 57, characters 5-8: -57 | own.bell = Bell(Int(4)); +Error: a store may not go through a reference, since what it names belongs to a tree this path's root does not hold; change it with a `mut` method called through the reference +File "fixtures/typing/reject/bad/sources.zn", line 85, characters 5-8: +85 | own.bell = Bell(Int(4)); | ^^^ -Error: a store may not go through a guest, since what it names belongs to a tree this path's root does not own; change it with a `mut` method called through the guest +Error: a store may not go through a reference, since what it names belongs to a tree this path's root does not hold; change it with a `mut` method called through the reference File "fixtures/typing/reject/bad/spawns.zn", line 44, characters 11-15: 44 | spawn dial!tune(); | ^^^^ @@ -577,19 +665,19 @@ Error: this block runs more than once, so a spawned `mut` call in it takes its s File "fixtures/typing/reject/bad/spawns.zn", line 100, characters 9-21: 100 | dial.reading = Gauge(Int(3)); | ^^^^^^^^^^^^ -Error: this writes `dial.reading`, which may be part of a host lent through `view` to a spawned call that may read it until its block drains +Error: this writes `dial.reading`, which may be part of an owner lent through `view` to a spawned call that may read it until its block drains File "fixtures/typing/reject/bad/spawns.zn", line 101, characters 9-27: 101 | dial.reading.level!bump(); | ^^^^^^^^^^^^^^^^^^ -Error: this writes `dial.reading.level`, which may be part of a host lent through `view` to a spawned call that may read it until its block drains +Error: this writes `dial.reading.level`, which may be part of an owner lent through `view` to a spawned call that may read it until its block drains File "fixtures/typing/reject/bad/spawns.zn", line 102, characters 9-21: 102 | view.reading!nudge(); | ^^^^^^^^^^^^ -Error: this writes `view.reading`, which may be part of a host lent through `view` to a spawned call that may read it until its block drains +Error: this writes `view.reading`, which may be part of an owner lent through `view` to a spawned call that may read it until its block drains File "fixtures/typing/reject/bad/spawns.zn", line 107, characters 9-22: 107 | other.reading!nudge(); | ^^^^^^^^^^^^^ -Error: this writes `other.reading`, which may be part of a host lent through `view` to a spawned call that may read it until its block drains +Error: this writes `other.reading`, which may be part of an owner lent through `view` to a spawned call that may read it until its block drains File "fixtures/typing/reject/bad/spawns.zn", line 122, characters 15-28: 122 | spawn other.reading!nudge(); | ^^^^^^^^^^^^^ @@ -602,10 +690,10 @@ File "fixtures/typing/reject/bad/spawns.zn", line 133, characters 9-13: 133 | view!push(Gauge(Int(2))); | ^^^^ Error: `view` is borrowed by a spawned `mut` call on `gauges[]`, which holds it until its block drains -File "fixtures/typing/reject/bad/temporary.zn", line 6, characters 28-61: -6 | @program$console!print(@primitives$String("hello world")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Error: a new guest names a place, and this is a temporary value +File "fixtures/typing/reject/bad/temporary.zn", line 12, characters 10-34: +12 | hear(Note(@primitives$Int(1))); + | ^^^^^^^^^^^^^^^^^^^^^^^^ +Error: a new reference names a place, and this is a temporary value File "fixtures/typing/reject/bad/types.zn", line 5, characters 1-23: 5 | type Holder = struct { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -613,7 +701,7 @@ Error: `Holder` is a value type, so its member `guest` cannot be an `&`; only a File "fixtures/typing/reject/bad/types.zn", line 5, characters 1-23: 5 | type Holder = struct { | ^^^^^^^^^^^^^^^^^^^^^^ -Error: `Holder` is a value type, so its member `text` cannot hold `@primitives$String`, a reference type; a value type is a value all the way down +Error: `Holder` is a value type, so its member `list` cannot hold `@primitives$List<@primitives$Int>`, a reference type; a value type is a value all the way down File "fixtures/typing/reject/bad/types.zn", line 6, characters 10-23: 6 | many Box; | ^^^^^^^^^^^^^ diff --git a/tests/unit/unit.ml b/tests/unit/unit.ml index df603f3d..bd120492 100644 --- a/tests/unit/unit.ml +++ b/tests/unit/unit.ml @@ -29,14 +29,14 @@ let param name = Ty.fresh_param ~name ~kind:Ty.Type_kind let ty () = let t = param "T" and u = param "U" in - check "equal keeps a guest apart" (not (Ty.equal (Ty.Guest int) int)); - check "assignable looks through a guest" (Ty.assignable ~dst:int ~src:(Ty.Guest int)); + check "equal keeps a guest apart" (not (Ty.equal (Ty.Reference int) int)); + check "assignable looks through a guest" (Ty.assignable ~dst:int ~src:(Ty.Reference int)); check "equal tells primitives apart" (not (Ty.equal int float)); check "Error equals anything" (Ty.equal Ty.Error (named "Point")); check "parameters are equal by id, not name" (not (Ty.equal (Ty.Param t) (Ty.Param { t with Ty.id = t.Ty.id + 1000 }))); - check "strip_guest" (Ty.strip_guest (Ty.Guest int) = int); - check "is_guest" (Ty.is_guest (Ty.Guest int) && not (Ty.is_guest int)); + check "strip_mode" (Ty.strip_mode (Ty.Reference int) = int); + check "is_ref" (Ty.is_ref (Ty.Reference int) && not (Ty.is_ref int)); let pair = named "Pair" ~args:[ Ty.Type (Ty.Param t); Ty.Type (Ty.Param u) ] in check "subst replaces every parameter" (Ty.equal @@ -48,7 +48,7 @@ let ty () = (Ty.bindings [ t; u ] [ Ty.Type int; Ty.Type float ] = [ (t.Ty.id, Ty.Type int); (u.Ty.id, Ty.Type float) ]); check "instantiate" - (Ty.equal (Ty.instantiate [ t ] [ Ty.Type int ] (Ty.Guest (Ty.Param t))) (Ty.Guest int)); + (Ty.equal (Ty.instantiate [ t ] [ Ty.Type int ] (Ty.Reference (Ty.Param t))) (Ty.Reference int)); raises_bug "instantiate with too few arguments is a bug" (fun () -> Ty.instantiate [ t; u ] [ Ty.Type int ] (Ty.Param t)); raises_bug "instantiate with too many arguments is a bug" (fun () -> From 0880736b23e8472208d71dd45bcbe978696c798f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 16:16:45 +0000 Subject: [PATCH 2/6] docs: record where the compiler writes `^` beyond what the spec shows Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01ArFxFkp985vTC6aHAAxp82 --- docs/design/semantics.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/design/semantics.md b/docs/design/semantics.md index 7b638fe5..646c292f 100644 --- a/docs/design/semantics.md +++ b/docs/design/semantics.md @@ -708,6 +708,22 @@ again. A mistake that only a generic body's own code makes, and that its signature does not show, is reported inside the instance, which names the call that required it. +**Where `^` is written, beyond what the spec shows.** +- An abort type follows the return rule: a reference-typed one is `^T` or + `&T`, never bare. [`syntax.md`](https://github.com/zane-lang/spec/blob/911d749/spec/syntax.md) §4.10 states the rule for a + return type; an abort hands its value to the caller's handler as a return + hands it to the caller ([`lifetimes.md`](https://github.com/zane-lang/spec/blob/911d749/spec/lifetimes.md) §1.7), and a + bare one would hand back a borrow. +- `x ^T Type` introduces `T` and takes an owner when `T` is filled with a + reference type, borrowing a value otherwise. [`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) + §2.9 gives that meaning to "a type parameter written `^T`", and a generic + verb that stores its argument, such as a constructor whose field is `T`, + has no other way to take one, since `T` is introduced at its first marked + occurrence ([`generics.md`](https://github.com/zane-lang/spec/blob/911d749/spec/generics.md) §3.2). +- A function type's `^T` result over a type parameter is filled by a value + type's result written bare: `ArrayRef.fill`'s lambda is `^T[Int]`, and an + `Int(n Int)` lambda fills it with `T` = `Int`. + **`main`** is not required, since a library built on its own is also a root. When the root declares one, it takes no parameters, and it may return any type, whose value is discarded (`packages.md` §6.2). From 9012177311c5909ecbae08868764bdcb0ed18806 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 18:52:03 +0000 Subject: [PATCH 3/6] docs: drop the abort-type note, which the spec now states Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01ArFxFkp985vTC6aHAAxp82 --- docs/design/semantics.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/design/semantics.md b/docs/design/semantics.md index 646c292f..bf346e35 100644 --- a/docs/design/semantics.md +++ b/docs/design/semantics.md @@ -709,11 +709,6 @@ signature does not show, is reported inside the instance, which names the call that required it. **Where `^` is written, beyond what the spec shows.** -- An abort type follows the return rule: a reference-typed one is `^T` or - `&T`, never bare. [`syntax.md`](https://github.com/zane-lang/spec/blob/911d749/spec/syntax.md) §4.10 states the rule for a - return type; an abort hands its value to the caller's handler as a return - hands it to the caller ([`lifetimes.md`](https://github.com/zane-lang/spec/blob/911d749/spec/lifetimes.md) §1.7), and a - bare one would hand back a borrow. - `x ^T Type` introduces `T` and takes an owner when `T` is filled with a reference type, borrowing a value otherwise. [`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) §2.9 gives that meaning to "a type parameter written `^T`", and a generic From 3015da2bf15d46926ec1d791523268ca9103ac96 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 19:03:50 +0000 Subject: [PATCH 4/6] Accept `&T Type`, and report a value type that fills an `&T` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A type parameter's introduction carries the passing mode (spec generics.md §3.2): `x &T Type` takes a reference, inferred from the place it is minted from. `&` marks only a reference type, so a value type that fills an `&T` written over a type parameter, on a field or a verb's parameter, is a wrong-kind argument reported at its origin (generics.md §3.6). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01ArFxFkp985vTC6aHAAxp82 --- docs/design/semantics.md | 6 -- editors/tree-sitter-zane/grammar.js | 4 +- grammar/syntax.mly | 10 +- lib/cst/nodes.ml | 11 ++- lib/cst/parser.mly | 10 +- lib/cst/to_tree_graph.ml | 8 +- lib/sst/lower.ml | 7 +- lib/sst/nodes.ml | 6 +- lib/sst/to_tree_graph.ml | 8 +- lib/tst/check/instances.ml | 14 ++- lib/tst/model/ty.ml | 4 + lib/tst/passes/type_decls.ml | 92 ++++++++++++++----- tests/codegen/fixtures/references/main.zn | 15 ++- tests/codegen/golden/references.cgt | 75 ++++++++++++++- tests/codegen/golden/references.out | 2 + .../fixtures/typing/reject/bad/marks.zn | 20 ++++ tests/semantics/golden/typing.reject.err | 12 +++ 17 files changed, 249 insertions(+), 55 deletions(-) diff --git a/docs/design/semantics.md b/docs/design/semantics.md index bf346e35..a0dc322d 100644 --- a/docs/design/semantics.md +++ b/docs/design/semantics.md @@ -709,12 +709,6 @@ signature does not show, is reported inside the instance, which names the call that required it. **Where `^` is written, beyond what the spec shows.** -- `x ^T Type` introduces `T` and takes an owner when `T` is filled with a - reference type, borrowing a value otherwise. [`memory.md`](https://github.com/zane-lang/spec/blob/911d749/spec/memory.md) - §2.9 gives that meaning to "a type parameter written `^T`", and a generic - verb that stores its argument, such as a constructor whose field is `T`, - has no other way to take one, since `T` is introduced at its first marked - occurrence ([`generics.md`](https://github.com/zane-lang/spec/blob/911d749/spec/generics.md) §3.2). - A function type's `^T` result over a type parameter is filled by a value type's result written bare: `ArrayRef.fill`'s lambda is `^T[Int]`, and an `Int(n Int)` lambda fills it with `T` = `Int`. diff --git a/editors/tree-sitter-zane/grammar.js b/editors/tree-sitter-zane/grammar.js index 0fbd5a4f..12214e29 100644 --- a/editors/tree-sitter-zane/grammar.js +++ b/editors/tree-sitter-zane/grammar.js @@ -22,7 +22,7 @@ module.exports = grammar({ braced_verb_call: $ => choice(seq($.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq("@", $.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq("@", $.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}")), _computed_call_no_trailing_arg_: $ => choice(seq($.func_callee, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")"), seq($.app, ":", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")"), seq($.app, "!", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")")), _computed_call_trailing_arg_: $ => choice(seq($.func_callee, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", "{", optional($._list_stat_), "}"), seq($.func_callee, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", $.map_lit), seq($.app, ":", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", "{", optional($._list_stat_), "}"), seq($.app, ":", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", $.map_lit), seq($.app, "!", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", "{", optional($._list_stat_), "}"), seq($.app, "!", $.primary, "(", optional($._loption_separated_nonempty_list_COMMA_call_arg__), ")", $.map_lit)), - constructor_field: $ => choice(seq($.identifier, $.type_expr), seq($.identifier, $.type_expr, "=", $.expr), seq($.identifier, $.type_identifier, "Type"), seq($.identifier, $.type_identifier, "Type", "=", $.expr), seq($.identifier, "^", $.type_identifier, "Type"), seq($.identifier, "^", $.type_identifier, "Type", "=", $.expr), seq($.identifier, $.type_identifier, "(", ")"), seq($.identifier, $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.identifier, "$", $.type_identifier, "(", ")"), seq($.identifier, $.identifier, "$", $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "(", ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}")), + constructor_field: $ => choice(seq($.identifier, $.type_expr), seq($.identifier, $.type_expr, "=", $.expr), seq($.identifier, $.type_identifier, "Type"), seq($.identifier, $.type_identifier, "Type", "=", $.expr), seq($.identifier, "^", $.type_identifier, "Type"), seq($.identifier, "^", $.type_identifier, "Type", "=", $.expr), seq($.identifier, "&", $.type_identifier, "Type"), seq($.identifier, "&", $.type_identifier, "Type", "=", $.expr), seq($.identifier, $.type_identifier, "(", ")"), seq($.identifier, $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.identifier, "$", $.type_identifier, "(", ")"), seq($.identifier, $.identifier, "$", $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, $.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "(", ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "(", ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "(", $._separated_nonempty_list_COMMA_constructor_call_arg_, ")"), seq($.identifier, "@", $.identifier, "$", $.type_identifier, ".", $.identifier, "{", optional($._list_terminated_constructor_field_arg_SEMICOLON__), "}")), enum_map_decl: $ => choice(seq($.type_identifier, optional($._loption_generics_), ".", $.identifier, $.type_expr, "{", optional($._list_terminated_enum_map_entry_SEMICOLON__), "}"), seq($.identifier, "$", $.type_identifier, optional($._loption_generics_), ".", $.identifier, $.type_expr, "{", optional($._list_terminated_enum_map_entry_SEMICOLON__), "}"), seq("@", $.identifier, "$", $.type_identifier, optional($._loption_generics_), ".", $.identifier, $.type_expr, "{", optional($._list_terminated_enum_map_entry_SEMICOLON__), "}")), expr: $ => choice($.app, $.app_braced, $.block_call, seq("spawn", $.unbraced_verb_call), seq("spawn", $.braced_verb_call), seq($.ret_type, "(", optional($._loption_separated_nonempty_list_COMMA_param__), ")", $.body), seq($.ret_type, "(", "this", $.type_expr, optional($._loption_preceded_COMMA_separated_nonempty_list_COMMA_param___), ")", optional($._boption_MUT_), $.body), prec.left(5, seq($.expr, "==", $.expr)), prec.left(5, seq($.expr, "<=", $.expr)), prec.left(5, seq($.expr, ">=", $.expr)), prec.left(5, seq($.expr, "<", $.expr)), prec.left(5, seq($.expr, ">", $.expr)), prec.left(5, seq($.expr, "~=", $.expr)), prec.left(6, seq($.expr, "+", $.expr)), prec.left(6, seq($.expr, "-", $.expr)), prec.left(7, seq($.expr, "*", $.expr)), prec.left(7, seq($.expr, "/", $.expr)), prec.left(2, seq($.expr, "'==", $.expr)), prec.left(2, seq($.expr, "'~=", $.expr)), prec.left(2, seq($.expr, "'<=", $.expr)), prec.left(2, seq($.expr, "'>=", $.expr)), prec.left(2, seq($.expr, "'<", $.expr)), prec.left(2, seq($.expr, "'>", $.expr)), prec.left(3, seq($.expr, "'+", $.expr)), prec.left(3, seq($.expr, "'-", $.expr)), prec.left(4, seq($.expr, "'*", $.expr)), prec.left(4, seq($.expr, "'/", $.expr)), prec(9, seq("~", $.expr)), prec(9, seq("&", $.ref_target)), prec(8, seq($.expr, $.abort_handle))), expr_braced: $ => choice($.app_braced, $.block_call, seq("spawn", $.braced_verb_call), seq($.ret_type, "(", optional($._loption_separated_nonempty_list_COMMA_param__), ")", $.body_braced), seq($.ret_type, "(", "this", $.type_expr, optional($._loption_preceded_COMMA_separated_nonempty_list_COMMA_param___), ")", optional($._boption_MUT_), $.body_braced), prec.left(5, seq($.expr, "==", $.expr_braced)), prec.left(5, seq($.expr, "<=", $.expr_braced)), prec.left(5, seq($.expr, ">=", $.expr_braced)), prec.left(5, seq($.expr, "<", $.expr_braced)), prec.left(5, seq($.expr, ">", $.expr_braced)), prec.left(5, seq($.expr, "~=", $.expr_braced)), prec.left(6, seq($.expr, "+", $.expr_braced)), prec.left(6, seq($.expr, "-", $.expr_braced)), prec.left(7, seq($.expr, "*", $.expr_braced)), prec.left(7, seq($.expr, "/", $.expr_braced)), prec.left(2, seq($.expr, "'==", $.expr_braced)), prec.left(2, seq($.expr, "'~=", $.expr_braced)), prec.left(2, seq($.expr, "'<=", $.expr_braced)), prec.left(2, seq($.expr, "'>=", $.expr_braced)), prec.left(2, seq($.expr, "'<", $.expr_braced)), prec.left(2, seq($.expr, "'>", $.expr_braced)), prec.left(3, seq($.expr, "'+", $.expr_braced)), prec.left(3, seq($.expr, "'-", $.expr_braced)), prec.left(4, seq($.expr, "'*", $.expr_braced)), prec.left(4, seq($.expr, "'/", $.expr_braced)), prec(9, seq("~", $.expr_braced)), prec(9, seq("&", $.ref_target_braced)), seq($.expr, $.abort_handle_braced)), @@ -49,7 +49,7 @@ module.exports = grammar({ _loption_separated_nonempty_list_COMMA_param_type__: $ => $._separated_nonempty_list_COMMA_param_type_, map_lit: $ => seq("{", $._nonempty_list_terminated_map_entry_SEMICOLON__, "}"), _nonempty_list_terminated_map_entry_SEMICOLON__: $ => choice(seq($.expr, ",", $.expr, ";"), seq($.expr, ",", $.expr, ";", $._nonempty_list_terminated_map_entry_SEMICOLON__)), - param: $ => choice(seq($.identifier, $.type_expr), seq($.identifier, $.type_identifier, "Type"), seq($.identifier, "^", $.type_identifier, "Type"), seq($.type_identifier, "Type")), + param: $ => choice(seq($.identifier, $.type_expr), seq($.identifier, $.type_identifier, "Type"), seq($.identifier, "^", $.type_identifier, "Type"), seq($.identifier, "&", $.type_identifier, "Type"), seq($.type_identifier, "Type")), primary: $ => choice($.integer_literal, $.decimal_literal, $.string_literal, "true", "false", seq("[", optional($._loption_separated_nonempty_list_COMMA_expr__), "]"), "this", $.identifier, seq($.identifier, "$", $.identifier), seq("@", $.identifier, "$", $.identifier), seq("(", $.expr, ")")), primary_braced: $ => choice(seq("init", "{", optional($._list_terminated_field_arg_SEMICOLON__), "}"), $.map_lit, seq("match", "(", $._separated_nonempty_list_COMMA_expr_, ")", "{", optional($._list_match_arm_), "}")), ref_target: $ => choice($.app, $.app_braced, prec(9, seq("&", $.ref_target)), prec(9, seq("~", $.ref_target))), diff --git a/grammar/syntax.mly b/grammar/syntax.mly index 1c51104b..ad09a691 100644 --- a/grammar/syntax.mly +++ b/grammar/syntax.mly @@ -324,13 +324,19 @@ map_lit: ignore concept_; param_type $loc (Nodes.Param_type.InferredType - { name; concept = concept $loc(concept_) Nodes.Concept.Type; roaming = false }) + { name; concept = concept $loc(concept_) Nodes.Concept.Type; marker = Nodes.Marker.Bare }) } | CARET name=uname concept_=UTYPE { ignore concept_; param_type $loc (Nodes.Param_type.InferredType - { name; concept = concept $loc(concept_) Nodes.Concept.Type; roaming = true }) + { name; concept = concept $loc(concept_) Nodes.Concept.Type; marker = Nodes.Marker.Roaming }) + } + | AMPERSAND name=uname concept_=UTYPE { + ignore concept_; + param_type $loc + (Nodes.Param_type.InferredType + { name; concept = concept $loc(concept_) Nodes.Concept.Type; marker = Nodes.Marker.Reference }) } %inline constructor_field: diff --git a/lib/cst/nodes.ml b/lib/cst/nodes.ml index 022fd055..bf86cb42 100644 --- a/lib/cst/nodes.ml +++ b/lib/cst/nodes.ml @@ -403,6 +403,10 @@ and Type_expr : sig | Verb of Verb_type.t end = Type_expr +and Marker : sig + type t = Bare | Roaming | Reference +end = Marker + and Param_type : sig type t = { node : node; @@ -412,9 +416,10 @@ and Param_type : sig and node = | Concrete of Type_expr.t | Concept of Concept.t - (* [roaming] is `^T Type`: a parameter that takes an owner when [name] is - filled with a reference type (memory.md §2.9). *) - | InferredType of { name : Name.t; concept : Concept.t; roaming : bool } + (* [marker] is the passing mode written before the introduction: `^T Type` + takes an owner when [name] is filled with a reference type, and `&T Type` + takes a reference (memory.md §2.9). *) + | InferredType of { name : Name.t; concept : Concept.t; marker : Marker.t } end = Param_type and Param : sig diff --git a/lib/cst/parser.mly b/lib/cst/parser.mly index 74670f78..c04e1d52 100644 --- a/lib/cst/parser.mly +++ b/lib/cst/parser.mly @@ -391,13 +391,19 @@ map_lit: ignore concept_; param_type $loc (Nodes.Param_type.InferredType - { name; concept = concept $loc(concept_) Nodes.Concept.Type; roaming = false }) + { name; concept = concept $loc(concept_) Nodes.Concept.Type; marker = Nodes.Marker.Bare }) } | CARET name=uname concept_=UTYPE { ignore concept_; param_type $loc (Nodes.Param_type.InferredType - { name; concept = concept $loc(concept_) Nodes.Concept.Type; roaming = true }) + { name; concept = concept $loc(concept_) Nodes.Concept.Type; marker = Nodes.Marker.Roaming }) + } + | AMPERSAND name=uname concept_=UTYPE { + ignore concept_; + param_type $loc + (Nodes.Param_type.InferredType + { name; concept = concept $loc(concept_) Nodes.Concept.Type; marker = Nodes.Marker.Reference }) } %inline constructor_field: diff --git a/lib/cst/to_tree_graph.ml b/lib/cst/to_tree_graph.ml index f5db50fd..ad62948d 100644 --- a/lib/cst/to_tree_graph.ml +++ b/lib/cst/to_tree_graph.ml @@ -44,10 +44,14 @@ let rec concept_to_node (x: Nodes.Concept.t) = match x.Nodes.Concept.node with and param_type_to_node (x: Nodes.Param_type.t) = match x.Nodes.Param_type.node with | Concrete x -> type_to_node x | Concept x -> concept_to_node x - | InferredType { name; concept; roaming } -> + | InferredType { name; concept; marker } -> fields ([ ("name", Leaf (text name)); ("concept", concept_to_node concept) ] - @ if roaming then [ ("roaming", Leaf "^") ] else []) + @ + match marker with + | Bare -> [] + | Roaming -> [ ("roaming", Leaf "^") ] + | Reference -> [ ("reference", Leaf "&") ]) and verb_type_to_node (x: Nodes.Verb_type.t) = match x.Nodes.Verb_type.node with | Func { params; ret_type } -> diff --git a/lib/sst/lower.ml b/lib/sst/lower.ml index cfced899..9dc62a24 100644 --- a/lib/sst/lower.ml +++ b/lib/sst/lower.ml @@ -117,8 +117,11 @@ and param_type (x : C.Param_type.t) : S.Param_type.t = match x.C.Param_type.node with | C.Param_type.Concrete t -> S.Param_type.Concrete (type_expr t) | C.Param_type.Concept c -> S.Param_type.Concept (concept c) - | C.Param_type.InferredType { name = n; concept = c; roaming } -> - S.Param_type.InferredType { name = name n; concept = concept c; roaming } + | C.Param_type.InferredType { name = n; concept = c; marker } -> + let marker : S.Marker.t = + match marker with Bare -> Bare | Roaming -> Roaming | Reference -> Reference + in + S.Param_type.InferredType { name = name n; concept = concept c; marker } in { S.Param_type.node; span } diff --git a/lib/sst/nodes.ml b/lib/sst/nodes.ml index de22d1f7..48d132d9 100644 --- a/lib/sst/nodes.ml +++ b/lib/sst/nodes.ml @@ -309,6 +309,10 @@ and Type_expr : sig | Verb of Verb_type.t end = Type_expr +and Marker : sig + type t = Bare | Roaming | Reference +end = Marker + and Param_type : sig type t = { node : node; @@ -318,7 +322,7 @@ and Param_type : sig and node = | Concrete of Type_expr.t | Concept of Concept.t - | InferredType of { name : Name.t; concept : Concept.t; roaming : bool } + | InferredType of { name : Name.t; concept : Concept.t; marker : Marker.t } end = Param_type and Param : sig diff --git a/lib/sst/to_tree_graph.ml b/lib/sst/to_tree_graph.ml index 7289129e..43849c4d 100644 --- a/lib/sst/to_tree_graph.ml +++ b/lib/sst/to_tree_graph.ml @@ -49,10 +49,14 @@ and param_type_to_node (x : Nodes.Param_type.t) = match x.Nodes.Param_type.node with | Concrete x -> type_to_node x | Concept x -> concept_to_node x - | InferredType { name; concept; roaming } -> + | InferredType { name; concept; marker } -> fields ([ ("name", Leaf (text name)); ("concept", concept_to_node concept) ] - @ if roaming then [ ("roaming", Leaf "^") ] else []) + @ + match marker with + | Bare -> [] + | Roaming -> [ ("roaming", Leaf "^") ] + | Reference -> [ ("reference", Leaf "&") ]) and verb_type_to_node (x : Nodes.Verb_type.t) = match x.Nodes.Verb_type.node with diff --git a/lib/tst/check/instances.ml b/lib/tst/check/instances.ml index e0e81e87..3fa7a477 100644 --- a/lib/tst/check/instances.ml +++ b/lib/tst/check/instances.ml @@ -49,7 +49,7 @@ let check_result env (s : S.t) subst at = false (* generics.md §3.6: an instance that puts a reference type where a value - mould holds only values is reported at the type's origin, the argument it + mould holds only values, or a value type under an `&`, is reported at the type's origin, the argument it was read from: a type passed as a value, or the value an inferred one was inferred from. A generic verb that only forwards its parameter is never the origin, so the instance is not made, and nothing inside it reports @@ -58,12 +58,18 @@ let check_kinds env (s : S.t) subst at (args : T.Arg.t option list) = let instantiated = List.map (fun (p : S.param) -> (p, Ty.subst subst p.S.ty)) s.params in let found = List.find_map - (fun t -> Type_decls.wrong_kind env (Ty.strip_mode t)) - (Ty.subst subst s.ret :: List.map snd instantiated) + (fun (slot, raw, t) -> + match Type_decls.filled_references env raw t with + | bad :: _ -> Some { Type_decls.path = [ slot ]; bad; under_reference = true } + | [] -> Type_decls.wrong_kind env (Ty.strip_mode t)) + (("the result", s.ret, Ty.subst subst s.ret) + :: List.map + (fun ((p : S.param), t) -> (Printf.sprintf "the parameter %s" (quote p.S.name), p.S.ty, t)) + instantiated) in match found with | None -> true - | Some ((_, bad) as f) -> + | Some ({ Type_decls.bad; _ } as f) -> let reads (p : S.param) = List.exists (fun (q : Ty.param) -> diff --git a/lib/tst/model/ty.ml b/lib/tst/model/ty.ml index 6cc3cb9c..e841c9dd 100644 --- a/lib/tst/model/ty.ml +++ b/lib/tst/model/ty.ml @@ -332,6 +332,10 @@ let rec unify ~open_ (s : subst) pattern actual : subst option = function type's `^T` is filled from a value type's result written bare. *) | Roaming (Param p), actual when is_open p && not (is_roaming actual) -> unify ~open_ s (Param p) actual + (* `&T` over a type parameter is filled from the place the reference is + minted from, whose own type is the owner's. *) + | Reference (Param p), actual when is_open p && not (is_ref actual) -> + unify ~open_ s (Param p) actual | Named (x, xs), Named (y, ys) when x = y -> unify_args ~open_ s xs ys | Intrinsic x, Intrinsic y when x.namespace = y.namespace && x.name = y.name -> unify_args ~open_ s x.args y.args diff --git a/lib/tst/passes/type_decls.ml b/lib/tst/passes/type_decls.ml index a1da6db9..eb69d9ba 100644 --- a/lib/tst/passes/type_decls.ml +++ b/lib/tst/passes/type_decls.ml @@ -127,7 +127,7 @@ let check_marker env span (marked : Ty.t) = (* A reference-typed result is a roaming owner or a reference, written `^T` or `&T`: a bare one would hand back a borrow, which is never returned - (memory.md §2.9, syntax.md §4.10). An abort hands its value to the + (memory.md §2.9, syntax.md §3.1). An abort hands its value to the caller's handler as a return hands it to the caller (lifetimes.md §1.7), so an abort type follows the same rule. *) let bare_reference env (t : Ty.t) = @@ -157,13 +157,38 @@ let rec check_function_types env span (t : Ty.t) = (* A type argument of the wrong kind (generics.md §3.6): a reference type, or an `&`, that an instance puts where a value mould holds only values - (memory.md §2.10). The walk follows what the instance builds: the fields - of each mould it applies, with the arguments substituted, down through - the moulds those fields apply in turn, and an `@primitives$Array`'s - elements. It does not look inside the arguments themselves, which were - checked where they were written. What it finds is the slot that rejects - the type, as the path that reaches it, and the type. *) -let rec wrong_kind env ?(seen = []) (t : Ty.t) : (string list * Ty.t) option = + (memory.md §2.10), or a value type it puts under an `&` written over a + type parameter (memory.md §2.4). The walk follows what the instance + builds: the fields of each mould it applies, with the arguments + substituted, down through the moulds those fields apply in turn, and an + `@primitives$Array`'s elements. It does not look inside the arguments + themselves, which were checked where they were written. What it finds is + the slot that rejects the type, as the path that reaches it, and the + type. *) +type wrong_kind = { path : string list; bad : Ty.t; under_reference : bool } + +(* A value type for `&`: what `&` cannot mark (memory.md §2.4). *) +let is_value env (t : Ty.t) = + match t with + | Ty.Param _ | Ty.Error | Ty.Concept _ | Ty.Reference _ | Ty.Roaming _ -> false + | t -> not (is_reference env t) + +(* The value types [inst] puts where [raw] writes `&` over a type parameter. + An `&` written over a concrete type was checked where it was written. *) +let rec filled_references env (raw : Ty.t) (inst : Ty.t) = + match (raw, inst) with + | Ty.Reference (Ty.Param _), Ty.Reference x -> if is_value env x then [ x ] else [] + | (Ty.Reference r | Ty.Roaming r), (Ty.Reference i | Ty.Roaming i) -> filled_references env r i + | Ty.Named (_, ra), Ty.Named (_, ia) | Ty.Intrinsic { args = ra; _ }, Ty.Intrinsic { args = ia; _ } + when List.length ra = List.length ia -> + List.concat + (List.map2 + (fun r i -> + match (r, i) with Ty.Type r, Ty.Type i -> filled_references env r i | _ -> []) + ra ia) + | _ -> [] + +let rec wrong_kind env ?(seen = []) (t : Ty.t) : wrong_kind option = let bad (ft : Ty.t) = match ft with | Ty.Reference _ -> true @@ -175,28 +200,43 @@ let rec wrong_kind env ?(seen = []) (t : Ty.t) : (string list * Ty.t) option = match Hashtbl.find_opt env.type_infos_by_id tid with | Some ({ definition = Some (Struct fs | Variant fs); _ } as info) -> let value = info.reference = Some false in - let fields = List.map (fun (n, ft) -> (n, Ty.instantiate info.params args ft)) fs in let step n = Printf.sprintf "%s's field %s" (quote (Ty.to_string t)) (quote n) in List.find_map - (fun (n, ft) -> - if value && bad ft then Some ([ step n ], ft) + (fun (n, raw) -> + let ft = Ty.instantiate info.params args raw in + if value && bad ft then Some { path = [ step n ]; bad = ft; under_reference = false } else - Option.map - (fun (path, b) -> (step n :: path, b)) - (wrong_kind env ~seen:(tid :: seen) (Ty.strip_mode ft))) - fields + match filled_references env raw ft with + | x :: _ -> Some { path = [ step n ]; bad = x; under_reference = true } + | [] -> + Option.map + (fun found -> { found with path = step n :: found.path }) + (wrong_kind env ~seen:(tid :: seen) (Ty.strip_mode ft))) + fs | Some ({ definition = Some (Distinct u); _ } as info) -> wrong_kind env ~seen:(tid :: seen) (Ty.instantiate info.params args u) | _ -> None) | Ty.Intrinsic { namespace = "primitives"; name = "Array"; args = Ty.Type e :: _ } when bad e -> - Some ([ Printf.sprintf "the elements of %s" (quote (Ty.to_string t)) ], e) + Some + { + path = [ Printf.sprintf "the elements of %s" (quote (Ty.to_string t)) ]; + bad = e; + under_reference = false; + } | _ -> None -let describe_wrong_kind (path, bad) = - Printf.sprintf - "%s is a reference type, and it reaches %s, a value type's slot; a value type holds only \ - values, all the way down" - (quote (Ty.to_string bad)) (String.concat ", then " path) +let describe_wrong_kind { path; bad; under_reference } = + if under_reference then + Printf.sprintf + "%s is a value type, and it fills an `&`%s; `&` marks only a reference type, since a value \ + is never referenced, only copied" + (quote (Ty.to_string bad)) + (if path = [] then "" else " at " ^ String.concat ", then " path) + else + Printf.sprintf + "%s is a reference type, and it reaches %s, a value type's slot; a value type holds only \ + values, all the way down" + (quote (Ty.to_string bad)) (String.concat ", then " path) (* Report a wrong-kind type once kinds are known: at the argument written explicitly that brought the rejected type in, or at [span]. *) @@ -204,7 +244,7 @@ let check_kinds env span (t : Ty.t) (args : (Ty.arg * Span.t) list) = let run () = match wrong_kind env t with | None -> () - | Some ((_, bad) as found) -> + | Some ({ bad; _ } as found) -> let rec mentions (x : Ty.t) = Ty.equal (Ty.strip_mode x) (Ty.strip_mode bad) || @@ -483,7 +523,7 @@ and param_type env scope (p : N.Param_type.t) : Ty.t = (* Only a type's header entry is written this way, and the grammar builds one nowhere else. *) | N.Param_type.Concept { N.Concept.node = N.Concept.Named _; _ } -> Ty.Error - | N.Param_type.InferredType { name; roaming; _ } -> ( + | N.Param_type.InferredType { name; marker; _ } -> ( if not scope.signature then begin error env p.N.Param_type.span (Printf.sprintf @@ -494,7 +534,11 @@ and param_type env scope (p : N.Param_type.t) : Ty.t = end else match List.assoc_opt name.N.Name.text scope.params with - | Some (Ty.Type t) -> if roaming then Ty.Roaming t else t + | Some (Ty.Type t) -> ( + match marker with + | N.Marker.Bare -> t + | N.Marker.Roaming -> Ty.Roaming t + | N.Marker.Reference -> Ty.Reference t) | _ -> Ty.Error) (* An alias is expanded where it is written (D5), so it has no identity of diff --git a/tests/codegen/fixtures/references/main.zn b/tests/codegen/fixtures/references/main.zn index 7c5e0765..dfce8461 100644 --- a/tests/codegen/fixtures/references/main.zn +++ b/tests/codegen/fixtures/references/main.zn @@ -1,6 +1,7 @@ /// References written out with `&` (memory.md §2.6): minted from a settled /// owner, copied from a reference, passed, and observing the owner when it -/// is overwritten in place. `@program$console` passed where a reference is +/// is overwritten in place. A generic `&T Type` parameter, stored in a +/// generic mould's `&T` field. `@program$console` passed where a reference is /// wanted. Each check prints /// `yes` when it holds and `no` when it does not. package references; @@ -26,6 +27,14 @@ Person(age Int) => init{ age; } Int age(p &Person) => p.age +type Pin = #struct { + at &T; +} + +Pin(at &T Type) => init{ at; } + +Int ageOf(at &T Type, p Person) => p.age + Unit pass(c &@runtime$Console) { return Unit(); } @@ -37,6 +46,10 @@ Unit main() { h &Person = &g; a = Person(Int(4)); check(h.age == Int(4)); + pin Pin(a); + a = Person(Int(5)); + check(pin.at.age == Int(5)); + check(ageOf(a, a) == Int(5)); pass(@program$console); pass(@program$console); return Unit(); diff --git a/tests/codegen/golden/references.cgt b/tests/codegen/golden/references.cgt index 2c4b4868..b7fa4209 100644 --- a/tests/codegen/golden/references.cgt +++ b/tests/codegen/golden/references.cgt @@ -4,6 +4,9 @@ │ ├── type: references$Person │ └── positions: ├── layouts[1] > layout: + │ ├── type: references$Pin + │ └── positions: + ├── layouts[2] > layout: │ ├── type: %primitives$String │ └── positions[0]: text @0 ├── globals[0]: zane.value.@program$console : i64 @@ -67,15 +70,58 @@ │ │ │ ├── path: 0 │ │ │ └── base: local #4 : ptr │ │ └── right: 4 : i64 - │ ├── body[6] > eval > call: + │ ├── body[6] > hold: + │ │ ├── local: #5 + │ │ ├── scope: %2 + │ │ ├── value > call: + │ │ │ ├── type: {ptr} + │ │ │ ├── fn: references$Pin(&references$Person) + │ │ │ └── args[0]: address of #1 : ptr + │ │ └── layout: references$Pin + │ ├── body[7] > overwrite: + │ │ ├── address: address of #1 : ptr + │ │ ├── value > call: + │ │ │ ├── type: {i64} + │ │ │ ├── fn: references$Person(%primitives$Int) + │ │ │ └── args[0]: 5 : i64 + │ │ └── layout: references$Person + │ ├── body[8] > eval > call: + │ │ ├── type: void + │ │ ├── fn: references$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base > member: + │ │ │ ├── type: ptr + │ │ │ ├── index: 0 + │ │ │ └── value: local #5 : {ptr} + │ │ └── right: 5 : i64 + │ ├── body[9] > eval > call: + │ │ ├── type: void + │ │ ├── fn: references$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > call: + │ │ │ ├── type: i64 + │ │ │ ├── fn: references$ageOf(&references$Person, references$Person) + │ │ │ ├── args[0]: address of #1 : ptr + │ │ │ └── args[1]: address of #1 : ptr + │ │ └── right: 5 : i64 + │ ├── body[10] > eval > call: │ │ ├── type: void │ │ ├── fn: references$pass(&%runtime$Console) │ │ └── args[0]: global zane.value.@program$console - │ ├── body[7] > eval > call: + │ ├── body[11] > eval > call: │ │ ├── type: void │ │ ├── fn: references$pass(&%runtime$Console) │ │ └── args[0]: global zane.value.@program$console - │ └── body[8] > return: unit : void + │ └── body[12] > return: unit : void ├── funcs[1] > func: │ ├── symbol: references$Person(%primitives$Int) │ ├── params[0]: local #1 : i64 @@ -127,7 +173,28 @@ │ │ ├── fn: zane_print │ │ └── args[0]: local #4 : handle │ └── body[4] > return: unit : void - └── funcs[4] > func: + ├── funcs[4] > func: + │ ├── symbol: references$Pin(&references$Person) + │ ├── params[0]: local #1 : ptr + │ ├── ret: {ptr} + │ └── body[0] > return > escape: + │ ├── exit: function + │ ├── value > record: + │ │ ├── type: {ptr} + │ │ └── members[0] > member 0: local #1 : ptr + │ └── layout: references$Pin + ├── funcs[5] > func: + │ ├── symbol: references$ageOf(&references$Person, references$Person) + │ ├── params[0]: local #1 : ptr + │ ├── params[1]: local #2 : ptr + │ ├── ret: i64 + │ └── body[0] > return > snapshot: + │ ├── type: i64 + │ └── ptr > offset: + │ ├── within: {i64} + │ ├── path: 0 + │ └── base: local #2 : ptr + └── funcs[6] > func: ├── symbol: references$pass(&%runtime$Console) ├── params[0]: local #1 : ptr ├── ret: void diff --git a/tests/codegen/golden/references.out b/tests/codegen/golden/references.out index af7e09de..4ff957b4 100644 --- a/tests/codegen/golden/references.out +++ b/tests/codegen/golden/references.out @@ -1,2 +1,4 @@ yes yes +yes +yes diff --git a/tests/semantics/fixtures/typing/reject/bad/marks.zn b/tests/semantics/fixtures/typing/reject/bad/marks.zn index 2cc167b5..7e3c5ed9 100644 --- a/tests/semantics/fixtures/typing/reject/bad/marks.zn +++ b/tests/semantics/fixtures/typing/reject/bad/marks.zn @@ -41,3 +41,23 @@ Unit wrong() { d @primitives$Array([Kite(Int(5)), Kite(Int(6))]); return Unit(); } + +/// `&` marks only a reference type, so a value type that fills an `&` written +/// over a type parameter is the wrong kind, reported where it enters +/// (generics.md §3.6): the explicit argument, or the value an inferred one is +/// read from. +type Peg = #struct { at &T; } +Peg(at &T Type) => init{ at; } +Unit tap(at &T Type) => Unit() + +alias Pegged = Peg + +Unit pegs(kite Kite) { + count Int = Int(7); + e Peg(count); + tap(count); + settled Kite = Kite(Int(8)); + f Peg(settled); + tap(settled); + return Unit(); +} diff --git a/tests/semantics/golden/typing.reject.err b/tests/semantics/golden/typing.reject.err index 4c465a89..0b10e420 100644 --- a/tests/semantics/golden/typing.reject.err +++ b/tests/semantics/golden/typing.reject.err @@ -310,6 +310,18 @@ File "fixtures/typing/reject/bad/marks.zn", line 41, characters 25-53: 41 | d @primitives$Array([Kite(Int(5)), Kite(Int(6))]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Error: `bad$Kite` is a reference type, and it reaches the elements of `@primitives$Array`, a value type's slot; a value type holds only values, all the way down +File "fixtures/typing/reject/bad/marks.zn", line 53, characters 20-23: +53 | alias Pegged = Peg + | ^^^ +Error: `@primitives$Int` is a value type, and it fills an `&` at `bad$Peg<@primitives$Int>`'s field `at`; `&` marks only a reference type, since a value is never referenced, only copied +File "fixtures/typing/reject/bad/marks.zn", line 57, characters 11-16: +57 | e Peg(count); + | ^^^^^ +Error: `@primitives$Int` is a value type, and it fills an `&` at `bad$Peg<@primitives$Int>`'s field `at`; `&` marks only a reference type, since a value is never referenced, only copied +File "fixtures/typing/reject/bad/marks.zn", line 58, characters 9-14: +58 | tap(count); + | ^^^^^ +Error: `@primitives$Int` is a value type, and it fills an `&` at the parameter `at`; `&` marks only a reference type, since a value is never referenced, only copied File "fixtures/typing/reject/bad/matches.zn", line 6, characters 29-44: 6 | Int answer(value Answer) => match (value) { | ^^^^^^^^^^^^^^^ From 1558f4081922c4fb9facdabb3d5441380c9da779 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 20:03:05 +0000 Subject: [PATCH 5/6] Fix passing-mode, abort and spawn gaps found in review - A function argument matches a parameter only at the passing modes it was written with, once inference fills the parameter: `Int[Packet]` no longer fills `Int[^T]` with a reference type, nor `Int[Int]` an `Int[&T]`. - An argument lent to a borrow stays lent for the whole call, so a later argument may not take the same place. - Spawn safety reads the new modes: a `^T` argument or a `^T` local's initializer moves the owner, and a bare reference-type argument borrows it. - An abort is a store, like a return: the move, reference-source and scope checks see it at the declared abort type, lowering hands it on at that type, and a handler's binder names what the call's arguments name. - A bare type parameter as a result or abort type is rejected once an instance fills it with a reference type, including inside function types. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01ArFxFkp985vTC6aHAAxp82 --- lib/cgt/lower.ml | 5 +- lib/cgt/program.ml | 1 + lib/cgt/state.ml | 3 + lib/tst/analyses/moves.ml | 78 +- lib/tst/analyses/references.ml | 29 +- lib/tst/analyses/scopes.ml | 37 +- lib/tst/analyses/spawns.ml | 24 +- lib/tst/check/instances.ml | 24 +- lib/tst/check/overloads.ml | 13 +- lib/tst/model/ty.ml | 8 +- lib/tst/passes/type_decls.ml | 113 +- tests/codegen/fixtures/references/main.zn | 19 +- tests/codegen/fixtures/spawns/main.zn | 9 + tests/codegen/golden/owners.cgt | 18 +- tests/codegen/golden/references.cgt | 100 +- tests/codegen/golden/references.out | 1 + tests/codegen/golden/spawned.cgt | 5 +- tests/codegen/golden/spawns.cgt | 1637 +++++++++-------- tests/codegen/golden/spawns.out | 2 + tests/codegen/golden/texts.cgt | 35 +- .../fixtures/typing/reject/bad/conventions.zn | 80 + tests/semantics/golden/typing.reject.err | 36 + 22 files changed, 1397 insertions(+), 880 deletions(-) create mode 100644 tests/semantics/fixtures/typing/reject/bad/conventions.zn diff --git a/lib/cgt/lower.ml b/lib/cgt/lower.ml index baa5c9e8..a38928c7 100644 --- a/lib/cgt/lower.ml +++ b/lib/cgt/lower.ml @@ -1488,7 +1488,10 @@ and stat st ctx (s : T.Stat.t) : Stat.t list = [ Stat.Return (st.returns (escape st span st.ret None (moved st ctx span st.ret e))) ] | Leave { label; result; ret } -> leave label result (escape st span ret (Some label) (moved st ctx span ret e))) - | T.Stat.Abort e -> ctx.abort span (moved st ctx span e.T.Expr.ty e) + (* An abort hands its value to the caller's handler as a return hands it + to the caller, at the declared abort type: an `&T` abort type takes a + reference, whatever the expression's own type. *) + | T.Stat.Abort e -> ctx.abort span (escape st span st.aborts None (moved st ctx span st.aborts e)) | T.Stat.Resolve e -> ( match ctx.resolve with | Some resolve -> resolve e.T.Expr.ty (moved st ctx span e.T.Expr.ty e) diff --git a/lib/cgt/program.ml b/lib/cgt/program.ml index 5759448f..6edec3e0 100644 --- a/lib/cgt/program.ml +++ b/lib/cgt/program.ml @@ -65,6 +65,7 @@ let func st (v : verb) : Func.t = else begin st.returns <- (if plain o then Fun.id else outcome_case o done_); st.ret <- v.signature.S.ret; + st.aborts <- Option.value ~default:Tty.Error v.signature.S.abort; let ctx = { env; diff --git a/lib/cgt/state.ml b/lib/cgt/state.ml index b31fd7c3..5d7a7673 100644 --- a/lib/cgt/state.ml +++ b/lib/cgt/state.ml @@ -119,6 +119,8 @@ type state = { type. *) mutable returns : Expr.t -> Expr.t; mutable ret : Tty.t; + (* The verb's abort type, which an `abort` hands its value on as. *) + mutable aborts : Tty.t; (* The function each spawned call runs through, latest first. *) mutable spawned : Func.t list; (* The root package when it is a library built into an object, whose @@ -155,6 +157,7 @@ let create ~library ~stamp ~stamped = next = 0; returns = Fun.id; ret = Tty.Error; + aborts = Tty.Error; spawned = []; library; stamp; diff --git a/lib/tst/analyses/moves.ml b/lib/tst/analyses/moves.ml index 5769f9fd..7c95f439 100644 --- a/lib/tst/analyses/moves.ml +++ b/lib/tst/analyses/moves.ml @@ -3,8 +3,8 @@ TST (docs/design/semantics.md D1). A reference-type value is never copied, so storing one where an owner goes - -- an owning local or field, a `^T` parameter, a return, an element, a case - payload -- moves it. What may be moved is a roaming value the store is + -- an owning local or field, a `^T` parameter, a return, an abort, an + element, a case payload -- moves it. What may be moved is a roaming value the store is entitled to consume: - a roaming owner named by a symbol, a local or a parameter declared @@ -42,6 +42,8 @@ type walk = { states : States.t; mutable block : int; mutable ret : Ty.t; + (* Where an `abort` hands its value: the enclosing verb's abort type. *) + mutable abort : Ty.t; mutable resolve : Ty.t list; } @@ -230,17 +232,18 @@ let rec expr env ?(into = Ty.Error) w (e : T.Expr.t) = match (Env.signature_of env callee, args) with | Some sg, subject :: rest when S.is_method sg -> arg env w subject; - rest - | _ -> args + (lent_by subject, rest) + | _ -> ([], args) in - pass env w (References.param_types env callee) args; + let lent, args = args in + pass env ~lent w (References.param_types env callee) args; opt_handler env w into handler | T.Expr.Call_value { callee; args; handler } -> expr env w callee; (match (callee.T.Expr.ty, args) with | Ty.Verb { Ty.this_ = Some _; params; _ }, subject :: rest -> arg env w subject; - pass env w params rest + pass env ~lent:(lent_by subject) w params rest | Ty.Verb { Ty.params; _ }, _ -> pass env w params args | _ -> List.iter (arg env w) args); opt_handler env w into handler @@ -272,12 +275,19 @@ and value env w into v = store env w into v (* Arguments in order, each read and then, for a `^T` parameter, moved: a - symbol passed twice is spent by the first. *) -and pass env w tys args = + symbol passed twice is spent by the first. An argument passed to a borrow + is lent for the whole call (memory.md §2.9), so a later argument may not + take the place it lends, or any place overlapping it; [lent] starts with + the subject's. *) +and pass env ?(lent = []) w tys args = + let lent = ref lent in let rec go tys args = match (tys, args) with | ty :: tys, T.Arg.Value v :: args -> - value env w (taken env ty) v; + let into = taken env ty in + if owning env into then not_lent env !lent v; + value env w into v; + if not (Ty.is_roaming ty || Ty.is_ref ty) then lent := lent_by (T.Arg.Value v) @ !lent; go tys args | _ :: tys, a :: args -> arg env w a; @@ -291,6 +301,31 @@ and pass env w tys args = and arg env w = function T.Arg.Value e -> expr env w e | T.Arg.Block b -> block env w b +(* The place an argument lends to a borrow, as its root symbol and field + path. *) +and lent_by = function + | T.Arg.Value e -> ( + match States.field_path e with Some (l, path) -> [ (l, path) ] | None -> []) + | T.Arg.Block _ -> [] + +and not_lent env lent (v : T.Expr.t) = + match States.field_path v with + | Some (l, path) -> ( + match + List.find_opt + (fun ((m : T.Local.t), p) -> m.T.Local.id = l.T.Local.id && overlaps p path) + lent + with + | Some (m, p) -> + Env.error env v.T.Expr.span + (Printf.sprintf + "this takes %s, while an earlier argument of the same call borrows %s, and a \ + borrow lasts for the whole call" + (Env.quote (dotted (l.T.Local.name :: path))) + (Env.quote (dotted (m.T.Local.name :: p)))) + | None -> ()) + | None -> () + (* `init{ }` entries fill the fields of what it builds, and a field constructor's entries are its parameters (types.md §3.3); [slot_type] gives each one's type. They run in the order written, so an entry that @@ -310,18 +345,21 @@ and handler_block env w ty (h : T.Handler.t) = w.resolve <- (match w.resolve with _ :: r -> r | [] -> []) and lambda env w (e : T.Expr.t) (l : T.Lambda.t) = - let has_this, ret = + let has_this, ret, abort = match e.T.Expr.ty with - | Ty.Verb v -> (Option.is_some v.Ty.this_, v.Ty.ret) - | _ -> (false, Ty.Error) + | Ty.Verb v -> (Option.is_some v.Ty.this_, v.Ty.ret, Option.value ~default:Ty.Error v.Ty.abort) + | _ -> (false, Ty.Error, Ty.Error) in params w has_this l.T.Lambda.params; - let saved = (w.ret, w.resolve) in + let saved = (w.ret, w.abort, w.resolve) in w.ret <- ret; + w.abort <- abort; w.resolve <- []; block env ~bind:l.T.Lambda.params w l.T.Lambda.body; - w.ret <- fst saved; - w.resolve <- snd saved + let r, a, s = saved in + w.ret <- r; + w.abort <- a; + w.resolve <- s (* A block is its own scope: what it declares, [bind] included, is declared in it. *) @@ -335,7 +373,10 @@ and block env ?(bind = []) w (b : T.Block.t) = and stat env w (s : T.Stat.t) = match s.T.Stat.node with - | T.Stat.Expr e | T.Stat.Spawn e | T.Stat.Abort e -> expr env w e + | T.Stat.Expr e | T.Stat.Spawn e -> expr env w e + (* An abort hands its value to the caller's handler as a return hands it + to the caller (lifetimes.md §1.7): a store like any other. *) + | T.Stat.Abort e -> value env w w.abort e | T.Stat.Let { local; value = v } -> value env w local.T.Local.ty v; Hashtbl.replace w.declared local.T.Local.id w.block @@ -380,7 +421,7 @@ and params w has_this (ps : T.Local.t list) = (* Declarations *) (* ---------------------------------------------------------------------- *) -let fresh projections blocks ret = +let fresh ?(abort = Ty.Error) projections blocks ret = { blocks; declared = Hashtbl.create 32; @@ -389,11 +430,12 @@ let fresh projections blocks ret = states = States.create projections; block = 0; ret; + abort; resolve = []; } let verb env projections blocks (sg : S.t) (ps : T.Local.t list) body = - let w = fresh projections blocks sg.S.ret in + let w = fresh ?abort:sg.S.abort projections blocks sg.S.ret in params w (S.is_method sg || sg.S.kind = S.Subscript) ps; block env ~bind:ps w body diff --git a/lib/tst/analyses/references.ml b/lib/tst/analyses/references.ml index e6bc2135..c21de8db 100644 --- a/lib/tst/analyses/references.ml +++ b/lib/tst/analyses/references.ml @@ -31,6 +31,8 @@ type walk = { states : States.t; (* Where a `return` and a `resolve` send their value. *) mutable ret : Ty.t; + (* Where an `abort` sends its value: the enclosing verb's abort type. *) + mutable abort : Ty.t; mutable resolve : Ty.t list; } @@ -253,24 +255,32 @@ and handler_block env w ty (h : T.Handler.t) = w.resolve <- (match w.resolve with _ :: r -> r | [] -> []) and lambda env w (e : T.Expr.t) (l : T.Lambda.t) = - let has_this, ret = + let has_this, ret, abort = match e.T.Expr.ty with - | Ty.Verb v -> (Option.is_some v.Ty.this_, v.Ty.ret) - | _ -> (false, Ty.Error) + | Ty.Verb v -> (Option.is_some v.Ty.this_, v.Ty.ret, Option.value ~default:Ty.Error v.Ty.abort) + | _ -> (false, Ty.Error, Ty.Error) in - let saved = (w.ret, w.resolve) in + let saved = (w.ret, w.abort, w.resolve) in params w has_this l.T.Lambda.params; w.ret <- ret; + w.abort <- abort; w.resolve <- []; block env w l.T.Lambda.body; - w.ret <- fst saved; - w.resolve <- snd saved + let r, a, s = saved in + w.ret <- r; + w.abort <- a; + w.resolve <- s and block env w (b : T.Block.t) = List.iter (stat env w) b.T.Block.stats and stat env w (s : T.Stat.t) = match s.T.Stat.node with - | T.Stat.Expr e | T.Stat.Spawn e | T.Stat.Abort e -> expr env w e + | T.Stat.Expr e | T.Stat.Spawn e -> expr env w e + (* An abort hands its value to the caller's handler as a return does + (lifetimes.md §1.7). *) + | T.Stat.Abort e -> + expr env w e; + store env w w.abort e | T.Stat.Let { local; value } -> expr env w value; store env w local.T.Local.ty value @@ -296,10 +306,11 @@ and params w has_this (ps : T.Local.t list) = (* Declarations *) (* ---------------------------------------------------------------------- *) -let fresh projections ret = { states = States.create projections; ret; resolve = [] } +let fresh ?(abort = Ty.Error) projections ret = + { states = States.create projections; ret; abort; resolve = [] } let verb env projections (sg : S.t) ps run = - let w = fresh projections sg.S.ret in + let w = fresh ?abort:sg.S.abort projections sg.S.ret in params w (S.is_method sg || sg.S.kind = S.Subscript) ps; run w diff --git a/lib/tst/analyses/scopes.ml b/lib/tst/analyses/scopes.ml index f73b2eb2..8da67da2 100644 --- a/lib/tst/analyses/scopes.ml +++ b/lib/tst/analyses/scopes.ml @@ -79,6 +79,9 @@ type walk = { (* Where a `return` sends its value: the verb's caller, or the match it is an arm of. A `resolve` sends its value to its handler's expression. *) mutable ret : sink; + (* Where an `abort` sends its value: the verb's caller's handler, at the + verb's abort type. *) + mutable abort : Ty.t; mutable resolve : (Ty.t * Names.t ref) list; (* What a match's arms or a handler's `resolve` hand on, by the span of the expression they belong to. *) @@ -353,13 +356,20 @@ let rec expr env w (e : T.Expr.t) = (* A `^T` parameter is the body's own, declared in its top block. *) let roaming, lent = List.partition (fun (p : T.Local.t) -> Ty.is_roaming p.T.Local.ty) l.T.Lambda.params in List.iter (fun (p : T.Local.t) -> Hashtbl.replace w.params p.T.Local.id (-1)) lent; - let ret = match e.T.Expr.ty with Ty.Verb v -> v.Ty.ret | _ -> Ty.Error in - let saved = (w.ret, w.resolve) in + let ret, abort = + match e.T.Expr.ty with + | Ty.Verb v -> (v.Ty.ret, Option.value ~default:Ty.Error v.Ty.abort) + | _ -> (Ty.Error, Ty.Error) + in + let saved = (w.ret, w.abort, w.resolve) in w.ret <- Verb ret; + w.abort <- abort; w.resolve <- []; block env ~bind:roaming w l.T.Lambda.body; - w.ret <- fst saved; - w.resolve <- snd saved + let r, a, s = saved in + w.ret <- r; + w.abort <- a; + w.resolve <- s (* §1.11: each parameter the callee keeps is stored into the place the argument for the other names, and the local that place is in now names it @@ -439,6 +449,11 @@ and arg env w (call : T.Expr.t) = function and opt_handler env w e = Option.iter (handler_block env w e) and handler_block env w (e : T.Expr.t) (h : T.Handler.t) = + (* The handler's binder holds what the call aborted with, which may carry + a reference to whatever its arguments name, as its result may. *) + Option.iter + (fun (b : T.Local.t) -> add w b (names env w { e with T.Expr.ty = b.T.Local.ty })) + h.T.Handler.binder; let acc = ref Names.empty in let saved = w.resolve in w.resolve <- (e.T.Expr.ty, acc) :: saved; @@ -457,7 +472,11 @@ and block env ?(bind = []) w (b : T.Block.t) = and stat env w (s : T.Stat.t) = match s.T.Stat.node with - | T.Stat.Expr e | T.Stat.Spawn e | T.Stat.Abort e -> expr env w e + | T.Stat.Expr e | T.Stat.Spawn e -> expr env w e + (* An abort is a store into the caller's frame, as a return is (§1.7). *) + | T.Stat.Abort e -> + expr env w e; + check env w e Caller "the caller's handler" (stored env w w.abort e) | T.Stat.Let { local; value } -> expr env w value; Hashtbl.replace w.declared local.T.Local.id w.block; @@ -501,6 +520,7 @@ let fresh summaries = block = 0; fresh = 0; ret = Verb Ty.Error; + abort = Ty.Error; resolve = []; report = false; grew = false; @@ -508,7 +528,7 @@ let fresh summaries = (* Walk until no local names anything new, then once more if [report]. Block numbers restart each walk, so every walk numbers them alike. *) -let walk summaries ~report decl ret (params : T.Local.t list) body = +let walk summaries ~report decl (ret, abort) (params : T.Local.t list) body = let w = fresh summaries in List.iteri (fun i (p : T.Local.t) -> @@ -520,6 +540,7 @@ let walk summaries ~report decl ret (params : T.Local.t list) body = w.fresh <- 0; w.block <- 0; w.ret <- Verb ret; + w.abort <- Option.value ~default:Ty.Error abort; w.resolve <- []; body w in @@ -542,7 +563,7 @@ let bodies env (p : T.Program.t) = (fun (d : T.Decl.t) -> match d.T.Decl.node with | T.Decl.Verb { signature; body = T.Decl.Checked { params; body } } -> - Some (d.T.Decl.id, signature.S.ret, params, fun w -> block env w body) + Some (d.T.Decl.id, (signature.S.ret, signature.S.abort), params, fun w -> block env w body) | _ -> None) pkg.T.Package.decls) p.T.Program.packages @@ -552,7 +573,7 @@ let bodies env (p : T.Program.t) = else Some ( i.T.Instance.decl, - i.T.Instance.signature.S.ret, + (i.T.Instance.signature.S.ret, i.T.Instance.signature.S.abort), i.T.Instance.params, fun w -> block env w i.T.Instance.body )) p.T.Program.instances diff --git a/lib/tst/analyses/spawns.ml b/lib/tst/analyses/spawns.ml index c234df37..19e45779 100644 --- a/lib/tst/analyses/spawns.ml +++ b/lib/tst/analyses/spawns.ml @@ -228,7 +228,7 @@ let walk_body env multi (body : T.Block.t) = (* A write to the place [e], or a move out of it, while a spawn may still read an owner it was lent. Where either place is unknown, what might overlap is decided by type. *) - let write frames (e : T.Expr.t) = + let write ?(verb = "writes") frames (e : T.Expr.t) = match place_of e with | None -> () | Some p -> ( @@ -249,16 +249,16 @@ let walk_body env multi (body : T.Block.t) = in Env.error env e.T.Expr.span (Printf.sprintf - "this writes %s, which may be part of an owner lent through %s to a spawned call \ + "this %s %s, which may be part of an owner lent through %s to a spawned call \ that may read it until its block drains" - (describe p) lent) + verb (describe p) lent) | None -> ()) in (* An owner read from a place into owning storage moves out of the place. *) let moved frames (e : T.Expr.t) = match e.T.Expr.ty with | Ty.Reference _ -> () - | t when Type_decls.is_reference env t -> write frames e + | t when Type_decls.is_reference env (Ty.strip_mode t) -> write ~verb:"moves" frames e | _ -> () in let claim (e : T.Expr.t) p = { written = p; at = resolve p; ty = Ty.strip_mode e.T.Expr.ty } in @@ -276,6 +276,7 @@ let walk_body env multi (body : T.Block.t) = expr frames value; (match local.T.Local.ty with | Ty.Reference _ -> Hashtbl.replace origins local.T.Local.id (origin value) + | _ when Ty.is_roaming local.T.Local.ty -> moved frames value | _ -> moved frames value); declare frames local | T.Stat.Assign { target; value } -> @@ -310,8 +311,9 @@ let walk_body env multi (body : T.Block.t) = (describe p) (describe b)) | None -> () and expr frames (e : T.Expr.t) = - (* A `!` call writes its subject, and an owner passed to a owning - parameter moves. *) + (* A `!` call writes its subject, and an owner passed to a `^T` + parameter moves; a bare reference-type parameter borrows it + (memory.md §2.9). *) (match e.T.Expr.node with | T.Expr.Call { callee; args; _ } | T.Expr.Construct { ctor = callee; args; _ } -> ( match Env.signature_of env callee with @@ -321,11 +323,15 @@ let walk_body env multi (body : T.Block.t) = | _ -> ()); (match callee.T.Verb_ref.owner with | S.Declared _ when List.length sg.S.params = List.length args -> + let filled = + List.map (fun ((q : Ty.param), a) -> (q.Ty.id, a)) callee.T.Verb_ref.instance + in List.iter2 (fun (p : S.param) a -> - match (a, p.S.ty) with - | T.Arg.Value ({ T.Expr.node = T.Expr.Var (T.Name_ref.Local _); _ } as v), t - when (match t with Ty.Reference _ -> false | _ -> true) && Type_decls.is_reference env t -> + match a with + | T.Arg.Value v + when Ty.is_roaming p.S.ty + && Type_decls.is_reference env (Ty.strip_mode (Ty.subst filled p.S.ty)) -> moved frames v | _ -> ()) sg.S.params args diff --git a/lib/tst/check/instances.ml b/lib/tst/check/instances.ml index 3fa7a477..862408a5 100644 --- a/lib/tst/check/instances.ml +++ b/lib/tst/check/instances.ml @@ -56,16 +56,26 @@ let check_result env (s : S.t) subst at = the same mistake again. *) let check_kinds env (s : S.t) subst at (args : T.Arg.t option list) = let instantiated = List.map (fun (p : S.param) -> (p, Ty.subst subst p.S.ty)) s.params in + (* A subscript's result is a place, and a constructor's is the value it + builds: neither hands back a borrow. *) + let returns = match s.kind with S.Subscript | S.Constructor _ -> false | _ -> true in let found = List.find_map - (fun (slot, raw, t) -> + (fun (slot, top, raw, t) -> match Type_decls.filled_references env raw t with - | bad :: _ -> Some { Type_decls.path = [ slot ]; bad; under_reference = true } - | [] -> Type_decls.wrong_kind env (Ty.strip_mode t)) - (("the result", s.ret, Ty.subst subst s.ret) - :: List.map - (fun ((p : S.param), t) -> (Printf.sprintf "the parameter %s" (quote p.S.name), p.S.ty, t)) - instantiated) + | bad :: _ -> Some { Type_decls.path = [ slot ]; bad; slot = Type_decls.Under_reference } + | [] -> ( + match Type_decls.bare_results env ~top raw t with + | bad :: _ -> Some { Type_decls.path = [ slot ]; bad; slot = Type_decls.Bare_result } + | [] -> Type_decls.wrong_kind env (Ty.strip_mode t))) + (((Printf.sprintf "the result of %s" (quote s.name), returns, s.ret, Ty.subst subst s.ret) + :: Option.fold ~none:[] + ~some:(fun a -> [ (Printf.sprintf "the abort type of %s" (quote s.name), true, a, Ty.subst subst a) ]) + s.abort) + @ List.map + (fun ((p : S.param), t) -> + (Printf.sprintf "the parameter %s" (quote p.S.name), false, p.S.ty, t)) + instantiated) in match found with | None -> true diff --git a/lib/tst/check/overloads.ml b/lib/tst/check/overloads.ml index 4327fa5d..e2f16ab9 100644 --- a/lib/tst/check/overloads.ml +++ b/lib/tst/check/overloads.ml @@ -48,7 +48,7 @@ let implicit_constructors env ~src ~dst = match s.params with | [ p ] -> ( let open_ = s.generics in - match Ty.unify ~open_ [] p.ty (Ty.strip_mode src) with + match Ty.unify ~open_ [] (Ty.strip_mode p.ty) (Ty.strip_mode src) with | None -> None | Some subst -> ( match Ty.unify ~open_ subst s.ret dst with @@ -130,8 +130,19 @@ let try_candidate env ~phase (s : S.t) (slots : actual option list) : outcome op (subst', marks @ [ mark ])) (Some [], []) pairs in + (* A function argument matches only at the modes it was written with, + once inference has filled the parameter's type. *) + let modes_hold subst = + List.for_all + (fun ((p : S.param), slot) -> + match slot with + | Some a when p.binds = None -> Type_decls.modes_agree env (Ty.subst subst p.ty) a.aty + | _ -> true) + pairs + in match subst with | None -> None + | Some subst when not (modes_hold subst) -> None | Some subst -> if List.exists (fun m -> m = None) marks then None else if phase <> Implicit && List.exists (fun m -> m = Some `Convert) marks then None diff --git a/lib/tst/model/ty.ml b/lib/tst/model/ty.ml index e841c9dd..458dba0c 100644 --- a/lib/tst/model/ty.ml +++ b/lib/tst/model/ty.ml @@ -329,13 +329,11 @@ let rec unify ~open_ (s : subst) pattern actual : subst option = | _, Error -> Some s (* `^T` over a type parameter takes an owner when it is filled with a reference type and borrows a value otherwise (memory.md §2.9), so a - function type's `^T` is filled from a value type's result written bare. *) + function type's `^T` is filled from a value type's result written bare. + Only a value type may fill it that way, which kinds decide: the caller + checks the filled type ([Type_decls.modes_agree]). *) | Roaming (Param p), actual when is_open p && not (is_roaming actual) -> unify ~open_ s (Param p) actual - (* `&T` over a type parameter is filled from the place the reference is - minted from, whose own type is the owner's. *) - | Reference (Param p), actual when is_open p && not (is_ref actual) -> - unify ~open_ s (Param p) actual | Named (x, xs), Named (y, ys) when x = y -> unify_args ~open_ s xs ys | Intrinsic x, Intrinsic y when x.namespace = y.namespace && x.name = y.name -> unify_args ~open_ s x.args y.args diff --git a/lib/tst/passes/type_decls.ml b/lib/tst/passes/type_decls.ml index eb69d9ba..3663e43b 100644 --- a/lib/tst/passes/type_decls.ml +++ b/lib/tst/passes/type_decls.ml @@ -165,7 +165,9 @@ let rec check_function_types env span (t : Ty.t) = themselves, which were checked where they were written. What it finds is the slot that rejects the type, as the path that reaches it, and the type. *) -type wrong_kind = { path : string list; bad : Ty.t; under_reference : bool } +type slot = Value_slot | Under_reference | Bare_result + +type wrong_kind = { path : string list; bad : Ty.t; slot : slot } (* A value type for `&`: what `&` cannot mark (memory.md §2.4). *) let is_value env (t : Ty.t) = @@ -186,6 +188,74 @@ let rec filled_references env (raw : Ty.t) (inst : Ty.t) = (fun r i -> match (r, i) with Ty.Type r, Ty.Type i -> filled_references env r i | _ -> []) ra ia) + | Ty.Verb r, Ty.Verb i -> ( + match (verb_parts r, verb_parts i) with + | rs, is when List.length rs = List.length is -> + List.concat (List.map2 (filled_references env) rs is) + | _ -> []) + | _ -> [] + +and verb_parts (v : Ty.verb) = + Option.to_list v.Ty.this_ @ v.Ty.params @ [ v.Ty.ret ] @ Option.to_list v.Ty.abort + +(* A function type's parts keep their passing modes: a borrow, a take and a + reference are passed differently, so a function is used only at the + modes it was written with. The one latitude is `^` over a type + parameter filled with a value type, which is a plain value (memory.md + §2.9). [expected] is a parameter's type with its arguments filled in; + [actual] is the argument's. *) +let rec modes_agree env (expected : Ty.t) (actual : Ty.t) = + let rec erase (t : Ty.t) : Ty.t = + match t with + | Ty.Roaming v when Ty.free_params v = [] && v <> Ty.Error && not (is_reference env v) -> erase v + | Ty.Roaming v -> Ty.Roaming (erase v) + | Ty.Reference v -> Ty.Reference (erase v) + | Ty.Verb v -> + Ty.Verb + { + v with + Ty.this_ = Option.map erase v.Ty.this_; + params = List.map erase v.Ty.params; + ret = erase v.Ty.ret; + abort = Option.map erase v.Ty.abort; + } + | Ty.Named (id, args) -> Ty.Named (id, List.map erase_arg args) + | Ty.Intrinsic i -> Ty.Intrinsic { i with args = List.map erase_arg i.args } + | t -> t + and erase_arg = function Ty.Type t -> Ty.Type (erase t) | n -> n in + match (Ty.strip_mode expected, Ty.strip_mode actual) with + | Ty.Verb _, Ty.Verb _ -> + Ty.contains_error expected || Ty.contains_error actual + || Ty.equal (erase (Ty.strip_mode expected)) (erase (Ty.held_as ~dst:expected ~src:actual)) + | Ty.Named (x, xs), Ty.Named (y, ys) when x = y && List.length xs = List.length ys -> + List.for_all2 (modes_agree_arg env) xs ys + | Ty.Intrinsic x, Ty.Intrinsic y when List.length x.args = List.length y.args -> + List.for_all2 (modes_agree_arg env) x.args y.args + | _ -> true + +and modes_agree_arg env a b = + match (a, b) with Ty.Type a, Ty.Type b -> modes_agree env a b | _ -> true + +(* The reference types [inst] puts where [raw] writes a bare type parameter + as a result or abort type: of the verb itself when [top], and of every + function type inside it. Such a result would hand back a borrow + (memory.md §2.9), which a written signature is rejected for. *) +let rec bare_results env ~top (raw : Ty.t) (inst : Ty.t) = + let here r i = match r with Ty.Param _ when is_reference env i -> [ i ] | _ -> [] in + let inside r i = bare_results env ~top:false r i in + match (raw, inst) with + | Ty.Param _, i when top -> here raw i + | (Ty.Reference r | Ty.Roaming r), (Ty.Reference i | Ty.Roaming i) -> inside r i + | Ty.Verb r, Ty.Verb i + when List.length r.Ty.params = List.length i.Ty.params + && Option.is_some r.Ty.abort = Option.is_some i.Ty.abort -> + here r.Ty.ret i.Ty.ret + @ (match (r.Ty.abort, i.Ty.abort) with Some r, Some i -> here r i | _ -> []) + @ List.concat (List.map2 inside (verb_parts r) (verb_parts i)) + | Ty.Named (_, ra), Ty.Named (_, ia) | Ty.Intrinsic { args = ra; _ }, Ty.Intrinsic { args = ia; _ } + when List.length ra = List.length ia -> + List.concat + (List.map2 (fun r i -> match (r, i) with Ty.Type r, Ty.Type i -> inside r i | _ -> []) ra ia) | _ -> [] let rec wrong_kind env ?(seen = []) (t : Ty.t) : wrong_kind option = @@ -204,11 +274,12 @@ let rec wrong_kind env ?(seen = []) (t : Ty.t) : wrong_kind option = List.find_map (fun (n, raw) -> let ft = Ty.instantiate info.params args raw in - if value && bad ft then Some { path = [ step n ]; bad = ft; under_reference = false } + if value && bad ft then Some { path = [ step n ]; bad = ft; slot = Value_slot } else - match filled_references env raw ft with - | x :: _ -> Some { path = [ step n ]; bad = x; under_reference = true } - | [] -> + match (filled_references env raw ft, bare_results env ~top:false raw ft) with + | x :: _, _ -> Some { path = [ step n ]; bad = x; slot = Under_reference } + | [], x :: _ -> Some { path = [ step n ]; bad = x; slot = Bare_result } + | [], [] -> Option.map (fun found -> { found with path = step n :: found.path }) (wrong_kind env ~seen:(tid :: seen) (Ty.strip_mode ft))) @@ -221,22 +292,28 @@ let rec wrong_kind env ?(seen = []) (t : Ty.t) : wrong_kind option = { path = [ Printf.sprintf "the elements of %s" (quote (Ty.to_string t)) ]; bad = e; - under_reference = false; + slot = Value_slot; } | _ -> None -let describe_wrong_kind { path; bad; under_reference } = - if under_reference then - Printf.sprintf - "%s is a value type, and it fills an `&`%s; `&` marks only a reference type, since a value \ - is never referenced, only copied" - (quote (Ty.to_string bad)) - (if path = [] then "" else " at " ^ String.concat ", then " path) - else - Printf.sprintf - "%s is a reference type, and it reaches %s, a value type's slot; a value type holds only \ - values, all the way down" - (quote (Ty.to_string bad)) (String.concat ", then " path) +let describe_wrong_kind { path; bad; slot } = + match slot with + | Under_reference -> + Printf.sprintf + "%s is a value type, and it fills an `&`%s; `&` marks only a reference type, since a value \ + is never referenced, only copied" + (quote (Ty.to_string bad)) + (if path = [] then "" else " at " ^ String.concat ", then " path) + | Bare_result -> + Printf.sprintf + "%s is a reference type, and it fills %s, written bare; a borrow is never returned or \ + aborted, so that position needs `^` or `&` to take this type" + (quote (Ty.to_string bad)) (String.concat ", then " path) + | Value_slot -> + Printf.sprintf + "%s is a reference type, and it reaches %s, a value type's slot; a value type holds only \ + values, all the way down" + (quote (Ty.to_string bad)) (String.concat ", then " path) (* Report a wrong-kind type once kinds are known: at the argument written explicitly that brought the rejected type in, or at [span]. *) diff --git a/tests/codegen/fixtures/references/main.zn b/tests/codegen/fixtures/references/main.zn index dfce8461..19bb2cb7 100644 --- a/tests/codegen/fixtures/references/main.zn +++ b/tests/codegen/fixtures/references/main.zn @@ -1,8 +1,8 @@ /// References written out with `&` (memory.md §2.6): minted from a settled /// owner, copied from a reference, passed, and observing the owner when it /// is overwritten in place. A generic `&T Type` parameter, stored in a -/// generic mould's `&T` field. `@program$console` passed where a reference is -/// wanted. Each check prints +/// generic mould's `&T` field. An `&T` abort type handing a reference to the +/// handler. `@program$console` passed where a reference is wanted. Each check prints /// `yes` when it holds and `no` when it does not. package references; @@ -35,6 +35,16 @@ Pin(at &T Type) => init{ at; } Int ageOf(at &T Type, p Person) => p.age +type Wrap = #struct { + child Person; +} + +Wrap(child ^Person) => init{ child; } + +Int?&Person refuse(w &Wrap) { + abort w.child; +} + Unit pass(c &@runtime$Console) { return Unit(); } @@ -50,6 +60,11 @@ Unit main() { a = Person(Int(5)); check(pin.at.age == Int(5)); check(ageOf(a, a) == Int(5)); + w Wrap(Person(Int(9))); + n Int = refuse(w) ? e { + resolve e.age; + } + check(n == Int(9)); pass(@program$console); pass(@program$console); return Unit(); diff --git a/tests/codegen/fixtures/spawns/main.zn b/tests/codegen/fixtures/spawns/main.zn index 963f279c..94a2968b 100644 --- a/tests/codegen/fixtures/spawns/main.zn +++ b/tests/codegen/fixtures/spawns/main.zn @@ -162,6 +162,8 @@ Int pick(n Int) { return n; } +Int ageOf(p Person) => p.age + Unit main() { // The pool resized before it starts, and a count it refuses. @program$runtime!setThreads(Int(2)) ? u { @@ -190,6 +192,13 @@ Unit main() { n String = spawn nameOf(g); check(n == String("grace!")); + // An owner lent to a spawned call by a borrow may be borrowed again + // while the call reads it. + lent ^Person = Person(String("ada"), Int(36)); + read Int = spawn ageOf(lent); + check(ageOf(lent) == Int(36)); + check(read == Int(36)); + // An owner returned: it settles where it comes home, and a reference // names it there. q Person = spawn born(String("ada"), Int(36)); diff --git a/tests/codegen/golden/owners.cgt b/tests/codegen/golden/owners.cgt index 443f2a10..6478b3b7 100644 --- a/tests/codegen/golden/owners.cgt +++ b/tests/codegen/golden/owners.cgt @@ -943,11 +943,14 @@ │ │ │ ├── index: 1 │ │ │ └── payload > escape: │ │ │ ├── exit: function - │ │ │ ├── value > call: - │ │ │ │ ├── type: {i64, i64} - │ │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) - │ │ │ │ ├── args[0]: 7 : i64 - │ │ │ │ └── args[1]: 70 : i64 + │ │ │ ├── value > escape: + │ │ │ │ ├── exit: function + │ │ │ │ ├── value > call: + │ │ │ │ │ ├── type: {i64, i64} + │ │ │ │ │ ├── fn: owners$opened(%primitives$Int, %primitives$Int) + │ │ │ │ │ ├── args[0]: 7 : i64 + │ │ │ │ │ └── args[1]: 70 : i64 + │ │ │ │ └── layout: ^owners$Account │ │ │ └── layout: ^owners$Account │ │ ├── body[2] > assign: │ │ │ ├── place: #4 @@ -1016,10 +1019,7 @@ │ │ │ │ ├── body[0] > hold: │ │ │ │ │ ├── local: #12 │ │ │ │ │ ├── scope: %13 - │ │ │ │ │ ├── value > take: - │ │ │ │ │ │ ├── type: {i64, i64} - │ │ │ │ │ │ ├── address: address of #6 : ptr - │ │ │ │ │ │ └── layout: owners$Account + │ │ │ │ │ ├── value: local #6 : {i64, i64} │ │ │ │ │ └── layout: owners$Account │ │ │ │ ├── body[1] > assign: │ │ │ │ │ ├── place: #3 diff --git a/tests/codegen/golden/references.cgt b/tests/codegen/golden/references.cgt index b7fa4209..5d0c263b 100644 --- a/tests/codegen/golden/references.cgt +++ b/tests/codegen/golden/references.cgt @@ -7,8 +7,14 @@ │ ├── type: references$Pin │ └── positions: ├── layouts[2] > layout: + │ ├── type: references$Wrap + │ └── positions: + ├── layouts[3] > layout: │ ├── type: %primitives$String │ └── positions[0]: text @0 + ├── layouts[4] > layout: + │ ├── type: ^references$Person + │ └── positions: ├── globals[0]: zane.value.@program$console : i64 ├── funcs[0] > func: │ ├── symbol: references$main() @@ -113,15 +119,70 @@ │ │ │ ├── args[0]: address of #1 : ptr │ │ │ └── args[1]: address of #1 : ptr │ │ └── right: 5 : i64 - │ ├── body[10] > eval > call: + │ ├── body[10] > hold: + │ │ ├── local: #6 + │ │ ├── scope: %2 + │ │ ├── value > call: + │ │ │ ├── type: {{i64}} + │ │ │ ├── fn: references$Wrap(^references$Person) + │ │ │ └── args[0] > call: + │ │ │ ├── type: {i64} + │ │ │ ├── fn: references$Person(%primitives$Int) + │ │ │ └── args[0]: 9 : i64 + │ │ └── layout: references$Wrap + │ ├── body[11] > let: + │ │ ├── local: #11 + │ │ └── value > expand: + │ │ ├── type: i64 + │ │ ├── label: @7 + │ │ ├── result: #8 + │ │ ├── body[0] > let: + │ │ │ ├── local: #9 + │ │ │ └── value > call: + │ │ │ ├── type: + │ │ │ ├── fn: references$refuse(&references$Wrap) + │ │ │ └── args[0]: address of #6 : ptr + │ │ └── body[1] > switch: + │ │ ├── value: local #9 : + │ │ ├── cases[0] > case 0[0] > assign: + │ │ │ ├── place: #8 + │ │ │ └── value > payload: + │ │ │ ├── type: i64 + │ │ │ ├── index: 0 + │ │ │ └── value: local #9 : + │ │ ├── cases[0] > case 0[1]: leave @7 + │ │ ├── cases[1] > case 1[0] > let: + │ │ │ ├── local: #10 + │ │ │ └── value > payload: + │ │ │ ├── type: ptr + │ │ │ ├── index: 1 + │ │ │ └── value: local #9 : + │ │ ├── cases[1] > case 1[1] > assign: + │ │ │ ├── place: #8 + │ │ │ └── value > snapshot: + │ │ │ ├── type: i64 + │ │ │ └── ptr > offset: + │ │ │ ├── within: {i64} + │ │ │ ├── path: 0 + │ │ │ └── base: local #10 : ptr + │ │ └── cases[1] > case 1[2]: leave @7 + │ ├── body[12] > eval > call: + │ │ ├── type: void + │ │ ├── fn: references$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left: local #11 : i64 + │ │ └── right: 9 : i64 + │ ├── body[13] > eval > call: │ │ ├── type: void │ │ ├── fn: references$pass(&%runtime$Console) │ │ └── args[0]: global zane.value.@program$console - │ ├── body[11] > eval > call: + │ ├── body[14] > eval > call: │ │ ├── type: void │ │ ├── fn: references$pass(&%runtime$Console) │ │ └── args[0]: global zane.value.@program$console - │ └── body[12] > return: unit : void + │ └── body[15] > return: unit : void ├── funcs[1] > func: │ ├── symbol: references$Person(%primitives$Int) │ ├── params[0]: local #1 : i64 @@ -194,7 +255,38 @@ │ ├── within: {i64} │ ├── path: 0 │ └── base: local #2 : ptr - └── funcs[6] > func: + ├── funcs[6] > func: + │ ├── symbol: references$Wrap(^references$Person) + │ ├── params[0]: local #1 : {i64} + │ ├── ret: {{i64}} + │ └── body[0] > scope: + │ ├── id: %3 + │ ├── body[0] > hold: + │ │ ├── local: #2 + │ │ ├── scope: %3 + │ │ ├── value: local #1 : {i64} + │ │ └── layout: ^references$Person + │ └── body[1] > return > escape: + │ ├── exit: function + │ ├── value > record: + │ │ ├── type: {{i64}} + │ │ └── members[0] > member 0 > take: + │ │ ├── type: {i64} + │ │ ├── address: address of #2 : ptr + │ │ └── layout: references$Person + │ └── layout: references$Wrap + ├── funcs[7] > func: + │ ├── symbol: references$refuse(&references$Wrap) + │ ├── params[0]: local #1 : ptr + │ ├── ret: + │ └── body[0] > return > case: + │ ├── type: + │ ├── index: 1 + │ └── payload > offset: + │ ├── within: {{i64}} + │ ├── path: 0 + │ └── base: local #1 : ptr + └── funcs[8] > func: ├── symbol: references$pass(&%runtime$Console) ├── params[0]: local #1 : ptr ├── ret: void diff --git a/tests/codegen/golden/references.out b/tests/codegen/golden/references.out index 4ff957b4..49c0756f 100644 --- a/tests/codegen/golden/references.out +++ b/tests/codegen/golden/references.out @@ -2,3 +2,4 @@ yes yes yes yes +yes diff --git a/tests/codegen/golden/spawned.cgt b/tests/codegen/golden/spawned.cgt index eb78a73b..697b4552 100644 --- a/tests/codegen/golden/spawned.cgt +++ b/tests/codegen/golden/spawned.cgt @@ -642,7 +642,10 @@ │ │ ├── index: 1 │ │ └── payload > escape: │ │ ├── exit: function - │ │ ├── value: "over" : handle + │ │ ├── value > escape: + │ │ │ ├── exit: function + │ │ │ ├── value: "over" : handle + │ │ │ └── layout: %primitives$String │ │ └── layout: %primitives$String │ └── body[1] > return > case: │ ├── type: diff --git a/tests/codegen/golden/spawns.cgt b/tests/codegen/golden/spawns.cgt index f33a4c9a..47aebee1 100644 --- a/tests/codegen/golden/spawns.cgt +++ b/tests/codegen/golden/spawns.cgt @@ -226,37 +226,93 @@ │ │ │ ├── place: #35 │ │ │ └── value: local #33 : handle │ │ └── right: 1 : i64 - │ ├── body[13] > spawn: - │ │ ├── task: #40 + │ ├── body[13] > hold: + │ │ ├── local: #39 + │ │ ├── scope: %11 + │ │ ├── value > call: + │ │ │ ├── type: {handle, i64} + │ │ │ ├── fn: spawns$Person(%primitives$String, %primitives$Int) + │ │ │ ├── args[0] > expand: + │ │ │ │ ├── type: handle + │ │ │ │ ├── label: @37 + │ │ │ │ ├── result: #38 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #36 + │ │ │ │ │ ├── scope: %11 + │ │ │ │ │ ├── value: "ada" : handle + │ │ │ │ │ └── layout: %primitives$String + │ │ │ │ └── body[1] > assign: + │ │ │ │ ├── place: #38 + │ │ │ │ └── value: local #36 : handle + │ │ │ └── args[1]: 36 : i64 + │ │ └── layout: ^spawns$Person + │ ├── body[14] > spawn: + │ │ ├── task: #41 │ │ ├── scope: %11 │ │ ├── thunk: zane.spawn.4 + │ │ ├── frame: {i64, ptr} + │ │ ├── args[0]: address of #39 : ptr + │ │ ├── dest: #42 + │ │ └── layout: %primitives$Int + │ ├── body[15] > eval > call: + │ │ ├── type: void + │ │ ├── fn: spawns$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > call: + │ │ │ ├── type: i64 + │ │ │ ├── fn: spawns$ageOf(spawns$Person) + │ │ │ └── args[0]: address of #39 : ptr + │ │ └── right: 36 : i64 + │ ├── body[16] > eval > call: + │ │ ├── type: void + │ │ ├── fn: spawns$check(%primitives$Bool) + │ │ └── args[0] > binary: + │ │ ├── type: i1 + │ │ ├── op: == + │ │ ├── left > expand: + │ │ │ ├── type: i64 + │ │ │ ├── label: @43 + │ │ │ ├── result: #44 + │ │ │ ├── body[0]: join #41 + │ │ │ └── body[1] > assign: + │ │ │ ├── place: #44 + │ │ │ └── value > deref: + │ │ │ ├── type: i64 + │ │ │ └── ptr: address of #42 : ptr + │ │ └── right: 36 : i64 + │ ├── body[17] > spawn: + │ │ ├── task: #49 + │ │ ├── scope: %11 + │ │ ├── thunk: zane.spawn.5 │ │ ├── frame: {{handle, i64}, handle, i64} │ │ ├── args[0] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @37 - │ │ │ ├── result: #38 + │ │ │ ├── label: @46 + │ │ │ ├── result: #47 │ │ │ ├── body[0] > hold: - │ │ │ │ ├── local: #36 + │ │ │ │ ├── local: #45 │ │ │ │ ├── scope: %11 │ │ │ │ ├── value: "ada" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #38 - │ │ │ └── value: local #36 : handle + │ │ │ ├── place: #47 + │ │ │ └── value: local #45 : handle │ │ ├── args[1]: 36 : i64 - │ │ ├── dest: #41 + │ │ ├── dest: #50 │ │ └── layout: ^spawns$Person - │ ├── body[14] > let: - │ │ ├── local: #44 + │ ├── body[18] > let: + │ │ ├── local: #53 │ │ └── value > expand: │ │ ├── type: ptr - │ │ ├── label: @42 - │ │ ├── result: #43 - │ │ ├── body[0]: join #40 + │ │ ├── label: @51 + │ │ ├── result: #52 + │ │ ├── body[0]: join #49 │ │ └── body[1] > assign: - │ │ ├── place: #43 - │ │ └── value: address of #41 : ptr - │ ├── body[15] > eval > call: + │ │ ├── place: #52 + │ │ └── value: address of #50 : ptr + │ ├── body[19] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -270,21 +326,21 @@ │ │ │ │ └── ptr > offset: │ │ │ │ ├── within: {handle, i64} │ │ │ │ ├── path: 0 - │ │ │ │ └── base: local #44 : ptr + │ │ │ │ └── base: local #53 : ptr │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @46 - │ │ │ ├── result: #47 + │ │ │ ├── label: @55 + │ │ │ ├── result: #56 │ │ │ ├── body[0] > hold: - │ │ │ │ ├── local: #45 + │ │ │ │ ├── local: #54 │ │ │ │ ├── scope: %11 │ │ │ │ ├── value: "ada lovelace" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #47 - │ │ │ └── value: local #45 : handle + │ │ │ ├── place: #56 + │ │ │ └── value: local #54 : handle │ │ └── right: 1 : i64 - │ ├── body[16] > eval > call: + │ ├── body[20] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -295,17 +351,17 @@ │ │ │ └── ptr > offset: │ │ │ ├── within: {handle, i64} │ │ │ ├── path: 1 - │ │ │ └── base: local #44 : ptr + │ │ │ └── base: local #53 : ptr │ │ └── right: 36 : i64 - │ ├── body[17] > spawn: - │ │ ├── task: #49 + │ ├── body[21] > spawn: + │ │ ├── task: #58 │ │ ├── scope: %11 - │ │ ├── thunk: zane.spawn.5 + │ │ ├── thunk: zane.spawn.6 │ │ ├── frame: {handle, i64} │ │ ├── args[0]: 50 : i64 - │ │ ├── dest: #50 + │ │ ├── dest: #59 │ │ └── layout: ^%primitives$List<%primitives$String> - │ ├── body[18] > eval > call: + │ ├── body[22] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -316,90 +372,90 @@ │ │ │ ├── index: 1 │ │ │ └── value > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @51 - │ │ │ ├── result: #52 - │ │ │ ├── body[0]: join #49 + │ │ │ ├── label: @60 + │ │ │ ├── result: #61 + │ │ │ ├── body[0]: join #58 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #52 + │ │ │ ├── place: #61 │ │ │ └── value > deref: │ │ │ ├── type: handle - │ │ │ └── ptr: address of #50 : ptr + │ │ │ └── ptr: address of #59 : ptr │ │ └── right: 50 : i64 - │ ├── body[19] > let: - │ │ ├── local: #53 + │ ├── body[23] > let: + │ │ ├── local: #62 │ │ └── value > call: │ │ ├── type: i64 │ │ ├── fn: spawns$Tally(%primitives$Int) │ │ └── args[0]: 0 : i64 - │ ├── body[20] > if: + │ ├── body[24] > if: │ │ ├── cond: true : i1 │ │ └── body[0] > scope: - │ │ ├── id: %57 + │ │ ├── id: %66 │ │ └── body[0] > spawn: - │ │ ├── task: #56 - │ │ ├── scope: %57 - │ │ ├── thunk: zane.spawn.6 + │ │ ├── task: #65 + │ │ ├── scope: %66 + │ │ ├── thunk: zane.spawn.7 │ │ ├── frame: {void, ptr, i64} - │ │ ├── args[0]: address of #53 : ptr + │ │ ├── args[0]: address of #62 : ptr │ │ └── args[1]: 5 : i64 - │ ├── body[21] > eval > call: + │ ├── body[25] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == - │ │ ├── left: local #53 : i64 + │ │ ├── left: local #62 : i64 │ │ └── right: 5 : i64 - │ ├── body[22] > hold: - │ │ ├── local: #58 + │ ├── body[26] > hold: + │ │ ├── local: #67 │ │ ├── scope: %11 │ │ ├── value > case: │ │ │ ├── type: │ │ │ ├── index: 0 │ │ │ └── payload: unit : void │ │ └── layout: spawns$Countdown - │ ├── body[23] > hold: - │ │ ├── local: #59 + │ ├── body[27] > hold: + │ │ ├── local: #68 │ │ ├── scope: %11 │ │ ├── value > case: │ │ │ ├── type: │ │ │ ├── index: 0 │ │ │ └── payload: unit : void │ │ └── layout: spawns$Countdown - │ ├── body[24] > hold: - │ │ ├── local: #60 + │ ├── body[28] > hold: + │ │ ├── local: #69 │ │ ├── scope: %11 │ │ ├── value > case: │ │ │ ├── type: │ │ │ ├── index: 0 │ │ │ └── payload: unit : void │ │ └── layout: spawns$Countdown - │ ├── body[25] > if: + │ ├── body[29] > if: │ │ ├── cond: true : i1 │ │ └── body[0] > scope: - │ │ ├── id: %64 + │ │ ├── id: %73 │ │ ├── body[0] > spawn: - │ │ │ ├── task: #63 - │ │ │ ├── scope: %64 - │ │ │ ├── thunk: zane.spawn.7 + │ │ │ ├── task: #72 + │ │ │ ├── scope: %73 + │ │ │ ├── thunk: zane.spawn.8 │ │ │ ├── frame: {void, ptr, i64} - │ │ │ ├── args[0]: address of #58 : ptr + │ │ │ ├── args[0]: address of #67 : ptr │ │ │ └── args[1]: 200 : i64 │ │ ├── body[1] > spawn: - │ │ │ ├── task: #66 - │ │ │ ├── scope: %64 - │ │ │ ├── thunk: zane.spawn.8 + │ │ │ ├── task: #75 + │ │ │ ├── scope: %73 + │ │ │ ├── thunk: zane.spawn.9 │ │ │ ├── frame: {void, ptr, i64} - │ │ │ ├── args[0]: address of #59 : ptr + │ │ │ ├── args[0]: address of #68 : ptr │ │ │ └── args[1]: 300 : i64 │ │ └── body[2] > spawn: - │ │ ├── task: #68 - │ │ ├── scope: %64 - │ │ ├── thunk: zane.spawn.9 + │ │ ├── task: #77 + │ │ ├── scope: %73 + │ │ ├── thunk: zane.spawn.10 │ │ ├── frame: {void, ptr, i64} - │ │ ├── args[0]: address of #60 : ptr + │ │ ├── args[0]: address of #69 : ptr │ │ └── args[1]: 400 : i64 - │ ├── body[26] > eval > call: + │ ├── body[30] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -414,28 +470,28 @@ │ │ │ │ ├── left > call: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── fn: spawns$depth(spawns$Countdown) - │ │ │ │ │ └── args[0]: local #58 : + │ │ │ │ │ └── args[0]: local #67 : │ │ │ │ └── right > call: │ │ │ │ ├── type: i64 │ │ │ │ ├── fn: spawns$depth(spawns$Countdown) - │ │ │ │ └── args[0]: local #59 : + │ │ │ │ └── args[0]: local #68 : │ │ │ └── right > call: │ │ │ ├── type: i64 │ │ │ ├── fn: spawns$depth(spawns$Countdown) - │ │ │ └── args[0]: local #60 : + │ │ │ └── args[0]: local #69 : │ │ └── right: 900 : i64 - │ ├── body[27] > hold: - │ │ ├── local: #72 + │ ├── body[31] > hold: + │ │ ├── local: #81 │ │ ├── scope: %11 │ │ ├── value > call: │ │ │ ├── type: {, i64} │ │ │ ├── fn: spawns$Holder(spawns$Countdown) │ │ │ └── args[0] > expand: │ │ │ ├── type: - │ │ │ ├── label: @70 - │ │ │ ├── result: #71 + │ │ │ ├── label: @79 + │ │ │ ├── result: #80 │ │ │ ├── body[0] > hold: - │ │ │ │ ├── local: #69 + │ │ │ │ ├── local: #78 │ │ │ │ ├── scope: %11 │ │ │ │ ├── value > case: │ │ │ │ │ ├── type: @@ -443,61 +499,61 @@ │ │ │ │ │ └── payload: unit : void │ │ │ │ └── layout: spawns$Countdown │ │ │ └── body[1] > assign: - │ │ │ ├── place: #71 - │ │ │ └── value: local #69 : + │ │ │ ├── place: #80 + │ │ │ └── value: local #78 : │ │ └── layout: spawns$Holder - │ ├── body[28] > let: - │ │ ├── local: #73 - │ │ └── value: address of #72 : ptr - │ ├── body[29] > if: + │ ├── body[32] > let: + │ │ ├── local: #82 + │ │ └── value: address of #81 : ptr + │ ├── body[33] > if: │ │ ├── cond: true : i1 │ │ └── body[0] > scope: - │ │ ├── id: %78 + │ │ ├── id: %87 │ │ ├── body[0] > spawn: - │ │ │ ├── task: #76 - │ │ │ ├── scope: %78 - │ │ │ ├── thunk: zane.spawn.10 + │ │ │ ├── task: #85 + │ │ │ ├── scope: %87 + │ │ │ ├── thunk: zane.spawn.11 │ │ │ ├── frame: {i64, ptr, i64, i64} - │ │ │ ├── args[0]: local #73 : ptr + │ │ │ ├── args[0]: local #82 : ptr │ │ │ ├── args[1]: 300 : i64 │ │ │ ├── args[2]: 250 : i64 - │ │ │ ├── dest: #77 + │ │ │ ├── dest: #86 │ │ │ └── layout: %primitives$Int │ │ ├── body[1] > let: - │ │ │ ├── local: #79 + │ │ │ ├── local: #88 │ │ │ └── value > offset: │ │ │ ├── within: {, i64} │ │ │ ├── path: 0 - │ │ │ └── base: address of #72 : ptr + │ │ │ └── base: address of #81 : ptr │ │ ├── body[2] > spawn: - │ │ │ ├── task: #81 - │ │ │ ├── scope: %78 - │ │ │ ├── thunk: zane.spawn.11 + │ │ │ ├── task: #90 + │ │ │ ├── scope: %87 + │ │ │ ├── thunk: zane.spawn.12 │ │ │ ├── frame: {void, ptr, i64, } - │ │ │ ├── args[0]: local #79 : ptr + │ │ │ ├── args[0]: local #88 : ptr │ │ │ ├── args[1]: 250 : i64 │ │ │ └── args[2] > copy: │ │ │ ├── type: │ │ │ ├── value > snapshot: │ │ │ │ ├── type: - │ │ │ │ └── ptr: local #79 : ptr + │ │ │ │ └── ptr: local #88 : ptr │ │ │ └── layout: spawns$Countdown │ │ ├── body[3] > let: - │ │ │ ├── local: #82 + │ │ │ ├── local: #91 │ │ │ └── value > offset: │ │ │ ├── within: {, i64} │ │ │ ├── path: 1 - │ │ │ └── base: address of #72 : ptr + │ │ │ └── base: address of #81 : ptr │ │ ├── body[4] > spawn: - │ │ │ ├── task: #84 - │ │ │ ├── scope: %78 - │ │ │ ├── thunk: zane.spawn.12 + │ │ │ ├── task: #93 + │ │ │ ├── scope: %87 + │ │ │ ├── thunk: zane.spawn.13 │ │ │ ├── frame: {void, ptr, i64, i64} - │ │ │ ├── args[0]: local #82 : ptr + │ │ │ ├── args[0]: local #91 : ptr │ │ │ ├── args[1]: 3 : i64 │ │ │ └── args[2] > snapshot: │ │ │ ├── type: i64 - │ │ │ └── ptr: local #82 : ptr + │ │ │ └── ptr: local #91 : ptr │ │ └── body[5] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) @@ -506,16 +562,16 @@ │ │ ├── op: == │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @85 - │ │ │ ├── result: #86 - │ │ │ ├── body[0]: join #76 + │ │ │ ├── label: @94 + │ │ │ ├── result: #95 + │ │ │ ├── body[0]: join #85 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #86 + │ │ │ ├── place: #95 │ │ │ └── value > deref: │ │ │ ├── type: i64 - │ │ │ └── ptr: address of #77 : ptr + │ │ │ └── ptr: address of #86 : ptr │ │ └── right: 300 : i64 - │ ├── body[30] > eval > call: + │ ├── body[34] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -529,9 +585,9 @@ │ │ │ └── ptr > offset: │ │ │ ├── within: {, i64} │ │ │ ├── path: 0 - │ │ │ └── base: address of #72 : ptr + │ │ │ └── base: address of #81 : ptr │ │ └── right: 250 : i64 - │ ├── body[31] > eval > call: + │ ├── body[35] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -542,33 +598,33 @@ │ │ │ └── ptr > offset: │ │ │ ├── within: {, i64} │ │ │ ├── path: 1 - │ │ │ └── base: address of #72 : ptr + │ │ │ └── base: address of #81 : ptr │ │ └── right: 3 : i64 - │ ├── body[32] > spawn: - │ │ ├── task: #88 + │ ├── body[36] > spawn: + │ │ ├── task: #97 │ │ ├── scope: %11 - │ │ ├── thunk: zane.spawn.13 + │ │ ├── thunk: zane.spawn.14 │ │ ├── frame: {i64, i64} │ │ ├── args[0]: 18 : i64 - │ │ ├── dest: #89 + │ │ ├── dest: #98 │ │ └── layout: %primitives$Int - │ ├── body[33] > spawn: - │ │ ├── task: #91 + │ ├── body[37] > spawn: + │ │ ├── task: #100 │ │ ├── scope: %11 - │ │ ├── thunk: zane.spawn.14 + │ │ ├── thunk: zane.spawn.15 │ │ ├── frame: {i64, i64} │ │ ├── args[0]: 17 : i64 - │ │ ├── dest: #92 + │ │ ├── dest: #101 │ │ └── layout: %primitives$Int - │ ├── body[34] > spawn: - │ │ ├── task: #94 + │ ├── body[38] > spawn: + │ │ ├── task: #103 │ │ ├── scope: %11 - │ │ ├── thunk: zane.spawn.15 + │ │ ├── thunk: zane.spawn.16 │ │ ├── frame: {i64, i64} │ │ ├── args[0]: 20 : i64 - │ │ ├── dest: #95 + │ │ ├── dest: #104 │ │ └── layout: %primitives$Int - │ ├── body[35] > eval > call: + │ ├── body[39] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -579,45 +635,45 @@ │ │ │ ├── op: + │ │ │ ├── left > expand: │ │ │ │ ├── type: i64 - │ │ │ │ ├── label: @96 - │ │ │ │ ├── result: #97 - │ │ │ │ ├── body[0]: join #88 + │ │ │ │ ├── label: @105 + │ │ │ │ ├── result: #106 + │ │ │ │ ├── body[0]: join #97 │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #97 + │ │ │ │ ├── place: #106 │ │ │ │ └── value > deref: │ │ │ │ ├── type: i64 - │ │ │ │ └── ptr: address of #89 : ptr + │ │ │ │ └── ptr: address of #98 : ptr │ │ │ └── right > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @98 - │ │ │ ├── result: #99 - │ │ │ ├── body[0]: join #91 + │ │ │ ├── label: @107 + │ │ │ ├── result: #108 + │ │ │ ├── body[0]: join #100 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #99 + │ │ │ ├── place: #108 │ │ │ └── value > deref: │ │ │ ├── type: i64 - │ │ │ └── ptr: address of #92 : ptr + │ │ │ └── ptr: address of #101 : ptr │ │ └── right > expand: │ │ ├── type: i64 - │ │ ├── label: @100 - │ │ ├── result: #101 - │ │ ├── body[0]: join #94 + │ │ ├── label: @109 + │ │ ├── result: #110 + │ │ ├── body[0]: join #103 │ │ └── body[1] > assign: - │ │ ├── place: #101 + │ │ ├── place: #110 │ │ └── value > deref: │ │ ├── type: i64 - │ │ └── ptr: address of #95 : ptr - │ ├── body[36] > let: - │ │ ├── local: #102 + │ │ └── ptr: address of #104 : ptr + │ ├── body[40] > let: + │ │ ├── local: #111 │ │ └── value: 0 : i64 - │ ├── body[37] > let: - │ │ ├── local: #103 + │ ├── body[41] > let: + │ │ ├── local: #112 │ │ └── value: 1 : i64 - │ ├── body[38] > eval > expand: + │ ├── body[42] > eval > expand: │ │ ├── type: void - │ │ ├── label: @105 + │ │ ├── label: @114 │ │ ├── body[0] > let: - │ │ │ ├── local: #104 + │ │ │ ├── local: #113 │ │ │ └── value: 10 : i64 │ │ ├── body[1] > repeat: │ │ │ ├── count > binary: @@ -626,117 +682,117 @@ │ │ │ │ ├── left > binary: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #104 : i64 + │ │ │ │ │ ├── left: local #113 : i64 │ │ │ │ │ └── right > flip: │ │ │ │ │ ├── type: i64 - │ │ │ │ │ └── value: local #103 : i64 + │ │ │ │ │ └── value: local #112 : i64 │ │ │ │ └── right: 1 : i64 │ │ │ ├── body[0] > if: │ │ │ │ ├── cond: true : i1 │ │ │ │ └── body[0] > scope: - │ │ │ │ ├── id: %111 + │ │ │ │ ├── id: %120 │ │ │ │ ├── body[0] > spawn: - │ │ │ │ │ ├── task: #109 - │ │ │ │ │ ├── scope: %111 - │ │ │ │ │ ├── thunk: zane.spawn.16 + │ │ │ │ │ ├── task: #118 + │ │ │ │ │ ├── scope: %120 + │ │ │ │ │ ├── thunk: zane.spawn.17 │ │ │ │ │ ├── frame: {i64, i64} - │ │ │ │ │ ├── args[0]: local #103 : i64 - │ │ │ │ │ ├── dest: #110 + │ │ │ │ │ ├── args[0]: local #112 : i64 + │ │ │ │ │ ├── dest: #119 │ │ │ │ │ └── layout: %primitives$Int │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #102 + │ │ │ │ ├── place: #111 │ │ │ │ └── value > binary: │ │ │ │ ├── type: i64 │ │ │ │ ├── op: + - │ │ │ │ ├── left: local #102 : i64 + │ │ │ │ ├── left: local #111 : i64 │ │ │ │ └── right > expand: │ │ │ │ ├── type: i64 - │ │ │ │ ├── label: @112 - │ │ │ │ ├── result: #113 - │ │ │ │ ├── body[0]: join #109 + │ │ │ │ ├── label: @121 + │ │ │ │ ├── result: #122 + │ │ │ │ ├── body[0]: join #118 │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #113 + │ │ │ │ ├── place: #122 │ │ │ │ └── value > deref: │ │ │ │ ├── type: i64 - │ │ │ │ └── ptr: address of #110 : ptr + │ │ │ │ └── ptr: address of #119 : ptr │ │ │ └── body[1] > assign: - │ │ │ ├── place: #103 + │ │ │ ├── place: #112 │ │ │ └── value > binary: │ │ │ ├── type: i64 │ │ │ ├── op: + - │ │ │ ├── left: local #103 : i64 + │ │ │ ├── left: local #112 : i64 │ │ │ └── right: 1 : i64 │ │ ├── body[2] > eval: unit : void - │ │ └── body[3]: leave @105 - │ ├── body[39] > eval > call: + │ │ └── body[3]: leave @114 + │ ├── body[43] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == - │ │ ├── left: local #102 : i64 + │ │ ├── left: local #111 : i64 │ │ └── right: 143 : i64 - │ ├── body[40] > if: + │ ├── body[44] > if: │ │ ├── cond: true : i1 │ │ └── body[0] > scope: - │ │ ├── id: %118 + │ │ ├── id: %127 │ │ ├── body[0] > spawn: - │ │ │ ├── task: #120 - │ │ │ ├── scope: %118 - │ │ │ ├── thunk: zane.spawn.17 + │ │ │ ├── task: #129 + │ │ │ ├── scope: %127 + │ │ │ ├── thunk: zane.spawn.18 │ │ │ ├── frame: {handle, handle} │ │ │ ├── args[0] > expand: │ │ │ │ ├── type: handle - │ │ │ │ ├── label: @116 - │ │ │ │ ├── result: #117 + │ │ │ │ ├── label: @125 + │ │ │ │ ├── result: #126 │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ ├── local: #115 - │ │ │ │ │ ├── scope: %118 + │ │ │ │ │ ├── local: #124 + │ │ │ │ │ ├── scope: %127 │ │ │ │ │ ├── value: "nobody" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #117 - │ │ │ │ └── value: local #115 : handle - │ │ │ ├── dest: #121 + │ │ │ │ ├── place: #126 + │ │ │ │ └── value: local #124 : handle + │ │ │ ├── dest: #130 │ │ │ └── layout: %primitives$String │ │ ├── body[1] > spawn: - │ │ │ ├── task: #126 - │ │ │ ├── scope: %118 - │ │ │ ├── thunk: zane.spawn.18 + │ │ │ ├── task: #135 + │ │ │ ├── scope: %127 + │ │ │ ├── thunk: zane.spawn.19 │ │ │ ├── frame: {handle, handle} │ │ │ ├── args[0] > expand: │ │ │ │ ├── type: handle - │ │ │ │ ├── label: @123 - │ │ │ │ ├── result: #124 + │ │ │ │ ├── label: @132 + │ │ │ │ ├── result: #133 │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ ├── local: #122 - │ │ │ │ │ ├── scope: %118 + │ │ │ │ │ ├── local: #131 + │ │ │ │ │ ├── scope: %127 │ │ │ │ │ ├── value: "no one" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #124 - │ │ │ │ └── value: local #122 : handle - │ │ │ ├── dest: #127 + │ │ │ │ ├── place: #133 + │ │ │ │ └── value: local #131 : handle + │ │ │ ├── dest: #136 │ │ │ └── layout: %primitives$String │ │ ├── body[2] > spawn: - │ │ │ ├── task: #129 - │ │ │ ├── scope: %118 - │ │ │ ├── thunk: zane.spawn.19 + │ │ │ ├── task: #138 + │ │ │ ├── scope: %127 + │ │ │ ├── thunk: zane.spawn.20 │ │ │ ├── frame: {handle, i64} │ │ │ ├── args[0]: 5 : i64 - │ │ │ ├── dest: #130 + │ │ │ ├── dest: #139 │ │ │ └── layout: ^%primitives$List<%primitives$String> │ │ └── body[3] > spawn: - │ │ ├── task: #132 - │ │ ├── scope: %118 - │ │ ├── thunk: zane.spawn.20 + │ │ ├── task: #141 + │ │ ├── scope: %127 + │ │ ├── thunk: zane.spawn.21 │ │ ├── frame: {void} │ │ └── args: - │ ├── body[41] > eval > expand: + │ ├── body[45] > eval > expand: │ │ ├── type: void - │ │ ├── label: @133 + │ │ ├── label: @142 │ │ ├── body[0] > let: - │ │ │ ├── local: #134 + │ │ │ ├── local: #143 │ │ │ └── value > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_set_threads @@ -745,30 +801,30 @@ │ │ ├── cond > binary: │ │ │ ├── type: i1 │ │ │ ├── op: == - │ │ │ ├── left: local #134 : i64 + │ │ │ ├── left: local #143 : i64 │ │ │ └── right: 0 : i64 │ │ ├── body[0] > let: - │ │ │ ├── local: #135 + │ │ │ ├── local: #144 │ │ │ └── value: unit : void │ │ ├── body[1] > eval: unit : void - │ │ └── body[2]: leave @133 - │ ├── body[42] > if: + │ │ └── body[2]: leave @142 + │ ├── body[46] > if: │ │ ├── cond: true : i1 │ │ └── body[0] > scope: - │ │ ├── id: %140 + │ │ ├── id: %149 │ │ ├── body[0] > spawn: - │ │ │ ├── task: #138 - │ │ │ ├── scope: %140 - │ │ │ ├── thunk: zane.spawn.21 + │ │ │ ├── task: #147 + │ │ │ ├── scope: %149 + │ │ │ ├── thunk: zane.spawn.22 │ │ │ ├── frame: {handle, i64} │ │ │ ├── args[0]: 20 : i64 - │ │ │ ├── dest: #139 + │ │ │ ├── dest: #148 │ │ │ └── layout: ^%primitives$List<%primitives$String> │ │ ├── body[1] > eval > expand: │ │ │ ├── type: void - │ │ │ ├── label: @141 + │ │ │ ├── label: @150 │ │ │ ├── body[0] > let: - │ │ │ │ ├── local: #142 + │ │ │ │ ├── local: #151 │ │ │ │ └── value > runtime: │ │ │ │ ├── type: i64 │ │ │ │ ├── fn: zane_set_threads @@ -777,53 +833,53 @@ │ │ │ ├── cond > binary: │ │ │ │ ├── type: i1 │ │ │ │ ├── op: == - │ │ │ │ ├── left: local #142 : i64 + │ │ │ │ ├── left: local #151 : i64 │ │ │ │ └── right: 0 : i64 │ │ │ ├── body[0] > let: - │ │ │ │ ├── local: #143 + │ │ │ │ ├── local: #152 │ │ │ │ └── value: unit : void │ │ │ ├── body[1] > eval: unit : void - │ │ │ └── body[2]: leave @141 + │ │ │ └── body[2]: leave @150 │ │ └── body[2] > spawn: - │ │ ├── task: #145 - │ │ ├── scope: %140 - │ │ ├── thunk: zane.spawn.22 + │ │ ├── task: #154 + │ │ ├── scope: %149 + │ │ ├── thunk: zane.spawn.23 │ │ ├── frame: {handle, i64} │ │ ├── args[0]: 20 : i64 - │ │ ├── dest: #146 + │ │ ├── dest: #155 │ │ └── layout: ^%primitives$List<%primitives$String> - │ ├── body[43] > eval > runtime: + │ ├── body[47] > eval > runtime: │ │ ├── type: void │ │ ├── fn: zane_set_threads_auto │ │ └── args: - │ ├── body[44] > let: - │ │ ├── local: #147 + │ ├── body[48] > let: + │ │ ├── local: #156 │ │ └── value: 0 : i64 - │ ├── body[45] > let: - │ │ ├── local: #151 + │ ├── body[49] > let: + │ │ ├── local: #160 │ │ └── value: true : i1 - │ ├── body[46] > spawn: - │ │ ├── task: #149 + │ ├── body[50] > spawn: + │ │ ├── task: #158 │ │ ├── scope: %11 - │ │ ├── thunk: zane.spawn.23 + │ │ ├── thunk: zane.spawn.24 │ │ ├── frame: {, i64, i64} │ │ ├── args[0]: 9 : i64 │ │ ├── args[1]: 3 : i64 - │ │ ├── dest: #150 + │ │ ├── dest: #159 │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ ├── body[47] > let: - │ │ ├── local: #155 + │ ├── body[51] > let: + │ │ ├── local: #164 │ │ └── value: true : i1 - │ ├── body[48] > spawn: - │ │ ├── task: #153 + │ ├── body[52] > spawn: + │ │ ├── task: #162 │ │ ├── scope: %11 - │ │ ├── thunk: zane.spawn.24 + │ │ ├── thunk: zane.spawn.25 │ │ ├── frame: {, i64, i64} │ │ ├── args[0]: 9 : i64 │ │ ├── args[1]: 0 : i64 - │ │ ├── dest: #154 + │ │ ├── dest: #163 │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ ├── body[49] > eval > call: + │ ├── body[53] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -831,60 +887,60 @@ │ │ ├── op: == │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @159 - │ │ │ ├── result: #160 + │ │ │ ├── label: @168 + │ │ │ ├── result: #169 │ │ │ ├── body[0] > eval > expand: │ │ │ │ ├── type: void - │ │ │ │ ├── label: @156 - │ │ │ │ ├── body[0]: join #149 + │ │ │ │ ├── label: @165 + │ │ │ │ ├── body[0]: join #158 │ │ │ │ └── body[1] > if: - │ │ │ │ ├── cond: local #151 : i1 + │ │ │ │ ├── cond: local #160 : i1 │ │ │ │ ├── body[0] > assign: - │ │ │ │ │ ├── place: #151 + │ │ │ │ │ ├── place: #160 │ │ │ │ │ └── value: false : i1 │ │ │ │ └── body[1] > switch: │ │ │ │ ├── value > deref: │ │ │ │ │ ├── type: - │ │ │ │ │ └── ptr: address of #150 : ptr + │ │ │ │ │ └── ptr: address of #159 : ptr │ │ │ │ ├── cases[0] > case 0: │ │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ │ ├── id: %158 + │ │ │ │ ├── id: %167 │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ ├── local: #157 - │ │ │ │ │ ├── scope: %158 + │ │ │ │ │ ├── local: #166 + │ │ │ │ │ ├── scope: %167 │ │ │ │ │ ├── value > take: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── address > offset: │ │ │ │ │ │ │ ├── within: │ │ │ │ │ │ │ ├── path: 1 - │ │ │ │ │ │ │ └── base: address of #150 : ptr + │ │ │ │ │ │ │ └── base: address of #159 : ptr │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ └── layout: %primitives$String │ │ │ │ ├── body[1] > assign: - │ │ │ │ │ ├── place: #147 + │ │ │ │ │ ├── place: #156 │ │ │ │ │ └── value > binary: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #147 : i64 + │ │ │ │ │ ├── left: local #156 : i64 │ │ │ │ │ └── right: 1 : i64 │ │ │ │ ├── body[2] > place: - │ │ │ │ │ ├── address: address of #150 : ptr + │ │ │ │ │ ├── address: address of #159 : ptr │ │ │ │ │ ├── value > case: │ │ │ │ │ │ ├── type: │ │ │ │ │ │ ├── index: 0 │ │ │ │ │ │ └── payload: 0 : i64 │ │ │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ │ │ │ └── body[3]: leave @156 + │ │ │ │ └── body[3]: leave @165 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #160 + │ │ │ ├── place: #169 │ │ │ └── value > deref: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: │ │ │ ├── within: │ │ │ ├── path: 0 - │ │ │ └── base: address of #150 : ptr + │ │ │ └── base: address of #159 : ptr │ │ └── right: 3 : i64 - │ ├── body[50] > eval > call: + │ ├── body[54] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -895,37 +951,37 @@ │ │ │ ├── op: + │ │ │ ├── left > expand: │ │ │ │ ├── type: i64 - │ │ │ │ ├── label: @168 - │ │ │ │ ├── result: #169 + │ │ │ │ ├── label: @177 + │ │ │ │ ├── result: #178 │ │ │ │ ├── body[0] > eval > expand: │ │ │ │ │ ├── type: void - │ │ │ │ │ ├── label: @161 - │ │ │ │ │ ├── body[0]: join #153 + │ │ │ │ │ ├── label: @170 + │ │ │ │ │ ├── body[0]: join #162 │ │ │ │ │ └── body[1] > if: - │ │ │ │ │ ├── cond: local #155 : i1 + │ │ │ │ │ ├── cond: local #164 : i1 │ │ │ │ │ ├── body[0] > assign: - │ │ │ │ │ │ ├── place: #155 + │ │ │ │ │ │ ├── place: #164 │ │ │ │ │ │ └── value: false : i1 │ │ │ │ │ └── body[1] > switch: │ │ │ │ │ ├── value > deref: │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ └── ptr: address of #154 : ptr + │ │ │ │ │ │ └── ptr: address of #163 : ptr │ │ │ │ │ ├── cases[0] > case 0: │ │ │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ │ │ ├── id: %163 + │ │ │ │ │ ├── id: %172 │ │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ │ ├── local: #162 - │ │ │ │ │ │ ├── scope: %163 + │ │ │ │ │ │ ├── local: #171 + │ │ │ │ │ │ ├── scope: %172 │ │ │ │ │ │ ├── value > take: │ │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ │ ├── address > offset: │ │ │ │ │ │ │ │ ├── within: │ │ │ │ │ │ │ │ ├── path: 1 - │ │ │ │ │ │ │ │ └── base: address of #154 : ptr + │ │ │ │ │ │ │ │ └── base: address of #163 : ptr │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ └── body[1] > scope: - │ │ │ │ │ ├── id: %167 + │ │ │ │ │ ├── id: %176 │ │ │ │ │ ├── body[0] > eval > call: │ │ │ │ │ │ ├── type: void │ │ │ │ │ │ ├── fn: spawns$check(%primitives$Bool) @@ -935,76 +991,76 @@ │ │ │ │ │ │ ├── left > runtime: │ │ │ │ │ │ │ ├── type: i64 │ │ │ │ │ │ │ ├── fn: zane_text_equal - │ │ │ │ │ │ │ ├── args[0]: local #162 : handle + │ │ │ │ │ │ │ ├── args[0]: local #171 : handle │ │ │ │ │ │ │ └── args[1] > expand: │ │ │ │ │ │ │ ├── type: handle - │ │ │ │ │ │ │ ├── label: @165 - │ │ │ │ │ │ │ ├── result: #166 + │ │ │ │ │ │ │ ├── label: @174 + │ │ │ │ │ │ │ ├── result: #175 │ │ │ │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ │ │ │ ├── local: #164 - │ │ │ │ │ │ │ │ ├── scope: %167 + │ │ │ │ │ │ │ │ ├── local: #173 + │ │ │ │ │ │ │ │ ├── scope: %176 │ │ │ │ │ │ │ │ ├── value: "by zero" : handle │ │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ │ ├── place: #166 - │ │ │ │ │ │ │ └── value: local #164 : handle + │ │ │ │ │ │ │ ├── place: #175 + │ │ │ │ │ │ │ └── value: local #173 : handle │ │ │ │ │ │ └── right: 1 : i64 │ │ │ │ │ ├── body[1] > assign: - │ │ │ │ │ │ ├── place: #147 + │ │ │ │ │ │ ├── place: #156 │ │ │ │ │ │ └── value > binary: │ │ │ │ │ │ ├── type: i64 │ │ │ │ │ │ ├── op: + - │ │ │ │ │ │ ├── left: local #147 : i64 + │ │ │ │ │ │ ├── left: local #156 : i64 │ │ │ │ │ │ └── right: 1 : i64 │ │ │ │ │ ├── body[2] > place: - │ │ │ │ │ │ ├── address: address of #154 : ptr + │ │ │ │ │ │ ├── address: address of #163 : ptr │ │ │ │ │ │ ├── value > case: │ │ │ │ │ │ │ ├── type: │ │ │ │ │ │ │ ├── index: 0 │ │ │ │ │ │ │ └── payload: 7 : i64 │ │ │ │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ │ │ │ │ └── body[3]: leave @161 + │ │ │ │ │ └── body[3]: leave @170 │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #169 + │ │ │ │ ├── place: #178 │ │ │ │ └── value > deref: │ │ │ │ ├── type: i64 │ │ │ │ └── ptr > offset: │ │ │ │ ├── within: │ │ │ │ ├── path: 0 - │ │ │ │ └── base: address of #154 : ptr + │ │ │ │ └── base: address of #163 : ptr │ │ │ └── right > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @177 - │ │ │ ├── result: #178 + │ │ │ ├── label: @186 + │ │ │ ├── result: #187 │ │ │ ├── body[0] > eval > expand: │ │ │ │ ├── type: void - │ │ │ │ ├── label: @170 - │ │ │ │ ├── body[0]: join #153 + │ │ │ │ ├── label: @179 + │ │ │ │ ├── body[0]: join #162 │ │ │ │ └── body[1] > if: - │ │ │ │ ├── cond: local #155 : i1 + │ │ │ │ ├── cond: local #164 : i1 │ │ │ │ ├── body[0] > assign: - │ │ │ │ │ ├── place: #155 + │ │ │ │ │ ├── place: #164 │ │ │ │ │ └── value: false : i1 │ │ │ │ └── body[1] > switch: │ │ │ │ ├── value > deref: │ │ │ │ │ ├── type: - │ │ │ │ │ └── ptr: address of #154 : ptr + │ │ │ │ │ └── ptr: address of #163 : ptr │ │ │ │ ├── cases[0] > case 0: │ │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ │ ├── id: %172 + │ │ │ │ ├── id: %181 │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ ├── local: #171 - │ │ │ │ │ ├── scope: %172 + │ │ │ │ │ ├── local: #180 + │ │ │ │ │ ├── scope: %181 │ │ │ │ │ ├── value > take: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── address > offset: │ │ │ │ │ │ │ ├── within: │ │ │ │ │ │ │ ├── path: 1 - │ │ │ │ │ │ │ └── base: address of #154 : ptr + │ │ │ │ │ │ │ └── base: address of #163 : ptr │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > scope: - │ │ │ │ ├── id: %176 + │ │ │ │ ├── id: %185 │ │ │ │ ├── body[0] > eval > call: │ │ │ │ │ ├── type: void │ │ │ │ │ ├── fn: spawns$check(%primitives$Bool) @@ -1014,65 +1070,65 @@ │ │ │ │ │ ├── left > runtime: │ │ │ │ │ │ ├── type: i64 │ │ │ │ │ │ ├── fn: zane_text_equal - │ │ │ │ │ │ ├── args[0]: local #171 : handle + │ │ │ │ │ │ ├── args[0]: local #180 : handle │ │ │ │ │ │ └── args[1] > expand: │ │ │ │ │ │ ├── type: handle - │ │ │ │ │ │ ├── label: @174 - │ │ │ │ │ │ ├── result: #175 + │ │ │ │ │ │ ├── label: @183 + │ │ │ │ │ │ ├── result: #184 │ │ │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ │ │ ├── local: #173 - │ │ │ │ │ │ │ ├── scope: %176 + │ │ │ │ │ │ │ ├── local: #182 + │ │ │ │ │ │ │ ├── scope: %185 │ │ │ │ │ │ │ ├── value: "by zero" : handle │ │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ │ ├── place: #175 - │ │ │ │ │ │ └── value: local #173 : handle + │ │ │ │ │ │ ├── place: #184 + │ │ │ │ │ │ └── value: local #182 : handle │ │ │ │ │ └── right: 1 : i64 │ │ │ │ ├── body[1] > assign: - │ │ │ │ │ ├── place: #147 + │ │ │ │ │ ├── place: #156 │ │ │ │ │ └── value > binary: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #147 : i64 + │ │ │ │ │ ├── left: local #156 : i64 │ │ │ │ │ └── right: 1 : i64 │ │ │ │ ├── body[2] > place: - │ │ │ │ │ ├── address: address of #154 : ptr + │ │ │ │ │ ├── address: address of #163 : ptr │ │ │ │ │ ├── value > case: │ │ │ │ │ │ ├── type: │ │ │ │ │ │ ├── index: 0 │ │ │ │ │ │ └── payload: 7 : i64 │ │ │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ │ │ │ └── body[3]: leave @170 + │ │ │ │ └── body[3]: leave @179 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #178 + │ │ │ ├── place: #187 │ │ │ └── value > deref: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: │ │ │ ├── within: │ │ │ ├── path: 0 - │ │ │ └── base: address of #154 : ptr + │ │ │ └── base: address of #163 : ptr │ │ └── right: 14 : i64 - │ ├── body[51] > eval > call: + │ ├── body[55] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == - │ │ ├── left: local #147 : i64 + │ │ ├── left: local #156 : i64 │ │ └── right: 1 : i64 - │ ├── body[52] > let: - │ │ ├── local: #182 + │ ├── body[56] > let: + │ │ ├── local: #191 │ │ └── value: true : i1 - │ ├── body[53] > spawn: - │ │ ├── task: #180 + │ ├── body[57] > spawn: + │ │ ├── task: #189 │ │ ├── scope: %11 - │ │ ├── thunk: zane.spawn.25 + │ │ ├── thunk: zane.spawn.26 │ │ ├── frame: {, i64, i64} │ │ ├── args[0]: 1 : i64 │ │ ├── args[1]: 0 : i64 - │ │ ├── dest: #181 + │ │ ├── dest: #190 │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ ├── body[54] > eval > call: + │ ├── body[58] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -1080,53 +1136,53 @@ │ │ ├── op: == │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @186 - │ │ │ ├── result: #187 + │ │ │ ├── label: @195 + │ │ │ ├── result: #196 │ │ │ ├── body[0] > eval > expand: │ │ │ │ ├── type: void - │ │ │ │ ├── label: @183 - │ │ │ │ ├── body[0]: join #180 + │ │ │ │ ├── label: @192 + │ │ │ │ ├── body[0]: join #189 │ │ │ │ └── body[1] > if: - │ │ │ │ ├── cond: local #182 : i1 + │ │ │ │ ├── cond: local #191 : i1 │ │ │ │ ├── body[0] > assign: - │ │ │ │ │ ├── place: #182 + │ │ │ │ │ ├── place: #191 │ │ │ │ │ └── value: false : i1 │ │ │ │ └── body[1] > switch: │ │ │ │ ├── value > deref: │ │ │ │ │ ├── type: - │ │ │ │ │ └── ptr: address of #181 : ptr + │ │ │ │ │ └── ptr: address of #190 : ptr │ │ │ │ ├── cases[0] > case 0: │ │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ │ ├── id: %185 + │ │ │ │ ├── id: %194 │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ ├── local: #184 - │ │ │ │ │ ├── scope: %185 + │ │ │ │ │ ├── local: #193 + │ │ │ │ │ ├── scope: %194 │ │ │ │ │ ├── value > take: │ │ │ │ │ │ ├── type: handle │ │ │ │ │ │ ├── address > offset: │ │ │ │ │ │ │ ├── within: │ │ │ │ │ │ │ ├── path: 1 - │ │ │ │ │ │ │ └── base: address of #181 : ptr + │ │ │ │ │ │ │ └── base: address of #190 : ptr │ │ │ │ │ │ └── layout: %primitives$String │ │ │ │ │ └── layout: %primitives$String │ │ │ │ ├── body[1] > place: - │ │ │ │ │ ├── address: address of #181 : ptr + │ │ │ │ │ ├── address: address of #190 : ptr │ │ │ │ │ ├── value > case: │ │ │ │ │ │ ├── type: │ │ │ │ │ │ ├── index: 0 │ │ │ │ │ │ └── payload: 4 : i64 │ │ │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ │ │ │ └── body[2]: leave @183 + │ │ │ │ └── body[2]: leave @192 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #187 + │ │ │ ├── place: #196 │ │ │ └── value > deref: │ │ │ ├── type: i64 │ │ │ └── ptr > offset: │ │ │ ├── within: │ │ │ ├── path: 0 - │ │ │ └── base: address of #181 : ptr + │ │ │ └── base: address of #190 : ptr │ │ └── right: 4 : i64 - │ ├── body[55] > eval > call: + │ ├── body[59] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -1134,40 +1190,40 @@ │ │ ├── op: == │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @188 - │ │ │ ├── result: #189 + │ │ │ ├── label: @197 + │ │ │ ├── result: #198 │ │ │ ├── body[0] > let: - │ │ │ │ ├── local: #190 + │ │ │ │ ├── local: #199 │ │ │ │ └── value > call: │ │ │ │ ├── type: │ │ │ │ ├── fn: spawns$relay(%primitives$Int, %primitives$Int) │ │ │ │ ├── args[0]: 8 : i64 │ │ │ │ └── args[1]: 2 : i64 │ │ │ └── body[1] > switch: - │ │ │ ├── value: local #190 : + │ │ │ ├── value: local #199 : │ │ │ ├── cases[0] > case 0[0] > assign: - │ │ │ │ ├── place: #189 + │ │ │ │ ├── place: #198 │ │ │ │ └── value > payload: │ │ │ │ ├── type: i64 │ │ │ │ ├── index: 0 - │ │ │ │ └── value: local #190 : - │ │ │ ├── cases[0] > case 0[1]: leave @188 + │ │ │ │ └── value: local #199 : + │ │ │ ├── cases[0] > case 0[1]: leave @197 │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ ├── id: %192 + │ │ │ ├── id: %201 │ │ │ ├── body[0] > hold: - │ │ │ │ ├── local: #191 - │ │ │ │ ├── scope: %192 + │ │ │ │ ├── local: #200 + │ │ │ │ ├── scope: %201 │ │ │ │ ├── value > payload: │ │ │ │ │ ├── type: handle │ │ │ │ │ ├── index: 1 - │ │ │ │ │ └── value: local #190 : + │ │ │ │ │ └── value: local #199 : │ │ │ │ └── layout: %primitives$String │ │ │ ├── body[1] > assign: - │ │ │ │ ├── place: #189 + │ │ │ │ ├── place: #198 │ │ │ │ └── value: 0 : i64 - │ │ │ └── body[2]: leave @188 + │ │ │ └── body[2]: leave @197 │ │ └── right: 5 : i64 - │ ├── body[56] > eval > call: + │ ├── body[60] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -1175,86 +1231,86 @@ │ │ ├── op: == │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @193 - │ │ │ ├── result: #194 + │ │ │ ├── label: @202 + │ │ │ ├── result: #203 │ │ │ ├── body[0] > let: - │ │ │ │ ├── local: #195 + │ │ │ │ ├── local: #204 │ │ │ │ └── value > call: │ │ │ │ ├── type: │ │ │ │ ├── fn: spawns$relay(%primitives$Int, %primitives$Int) │ │ │ │ ├── args[0]: 8 : i64 │ │ │ │ └── args[1]: 0 : i64 │ │ │ └── body[1] > switch: - │ │ │ ├── value: local #195 : + │ │ │ ├── value: local #204 : │ │ │ ├── cases[0] > case 0[0] > assign: - │ │ │ │ ├── place: #194 + │ │ │ │ ├── place: #203 │ │ │ │ └── value > payload: │ │ │ │ ├── type: i64 │ │ │ │ ├── index: 0 - │ │ │ │ └── value: local #195 : - │ │ │ ├── cases[0] > case 0[1]: leave @193 + │ │ │ │ └── value: local #204 : + │ │ │ ├── cases[0] > case 0[1]: leave @202 │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ ├── id: %197 + │ │ │ ├── id: %206 │ │ │ ├── body[0] > hold: - │ │ │ │ ├── local: #196 - │ │ │ │ ├── scope: %197 + │ │ │ │ ├── local: #205 + │ │ │ │ ├── scope: %206 │ │ │ │ ├── value > payload: │ │ │ │ │ ├── type: handle │ │ │ │ │ ├── index: 1 - │ │ │ │ │ └── value: local #195 : + │ │ │ │ │ └── value: local #204 : │ │ │ │ └── layout: %primitives$String │ │ │ ├── body[1] > assign: - │ │ │ │ ├── place: #194 + │ │ │ │ ├── place: #203 │ │ │ │ └── value: 100 : i64 - │ │ │ └── body[2]: leave @193 + │ │ │ └── body[2]: leave @202 │ │ └── right: 100 : i64 - │ ├── body[57] > hold: - │ │ ├── local: #198 + │ ├── body[61] > hold: + │ │ ├── local: #207 │ │ ├── scope: %11 │ │ ├── value: "" : handle │ │ └── layout: %primitives$String - │ ├── body[58] > eval > expand: + │ ├── body[62] > eval > expand: │ │ ├── type: i64 - │ │ ├── label: @199 - │ │ ├── result: #200 + │ │ ├── label: @208 + │ │ ├── result: #209 │ │ ├── body[0] > let: - │ │ │ ├── local: #201 + │ │ │ ├── local: #210 │ │ │ └── value > call: │ │ │ ├── type: │ │ │ ├── fn: spawns$relay(%primitives$Int, %primitives$Int) │ │ │ ├── args[0]: 8 : i64 │ │ │ └── args[1]: 0 : i64 │ │ └── body[1] > switch: - │ │ ├── value: local #201 : + │ │ ├── value: local #210 : │ │ ├── cases[0] > case 0[0] > assign: - │ │ │ ├── place: #200 + │ │ │ ├── place: #209 │ │ │ └── value > payload: │ │ │ ├── type: i64 │ │ │ ├── index: 0 - │ │ │ └── value: local #201 : - │ │ ├── cases[0] > case 0[1]: leave @199 + │ │ │ └── value: local #210 : + │ │ ├── cases[0] > case 0[1]: leave @208 │ │ └── cases[1] > case 1[0] > scope: - │ │ ├── id: %203 + │ │ ├── id: %212 │ │ ├── body[0] > hold: - │ │ │ ├── local: #202 - │ │ │ ├── scope: %203 + │ │ │ ├── local: #211 + │ │ │ ├── scope: %212 │ │ │ ├── value > payload: │ │ │ │ ├── type: handle │ │ │ │ ├── index: 1 - │ │ │ │ └── value: local #201 : + │ │ │ │ └── value: local #210 : │ │ │ └── layout: %primitives$String │ │ ├── body[1] > overwrite: - │ │ │ ├── address: address of #198 : ptr + │ │ │ ├── address: address of #207 : ptr │ │ │ ├── value > copy: │ │ │ │ ├── type: handle - │ │ │ │ ├── value: local #202 : handle + │ │ │ │ ├── value: local #211 : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── layout: %primitives$String │ │ ├── body[2] > assign: - │ │ │ ├── place: #200 + │ │ │ ├── place: #209 │ │ │ └── value: 0 : i64 - │ │ └── body[3]: leave @199 - │ ├── body[59] > eval > call: + │ │ └── body[3]: leave @208 + │ ├── body[63] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -1263,208 +1319,208 @@ │ │ ├── left > runtime: │ │ │ ├── type: i64 │ │ │ ├── fn: zane_text_equal - │ │ │ ├── args[0]: local #198 : handle + │ │ │ ├── args[0]: local #207 : handle │ │ │ └── args[1] > expand: │ │ │ ├── type: handle - │ │ │ ├── label: @205 - │ │ │ ├── result: #206 + │ │ │ ├── label: @214 + │ │ │ ├── result: #215 │ │ │ ├── body[0] > hold: - │ │ │ │ ├── local: #204 + │ │ │ │ ├── local: #213 │ │ │ │ ├── scope: %11 │ │ │ │ ├── value: "by zero!" : handle │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: - │ │ │ ├── place: #206 - │ │ │ └── value: local #204 : handle + │ │ │ ├── place: #215 + │ │ │ └── value: local #213 : handle │ │ └── right: 1 : i64 - │ ├── body[60] > let: - │ │ ├── local: #207 + │ ├── body[64] > let: + │ │ ├── local: #216 │ │ └── value: 0 : i64 - │ ├── body[61] > if: + │ ├── body[65] > if: │ │ ├── cond: true : i1 │ │ └── body[0] > scope: - │ │ ├── id: %212 + │ │ ├── id: %221 │ │ ├── body[0] > let: - │ │ │ ├── local: #213 + │ │ │ ├── local: #222 │ │ │ └── value: true : i1 │ │ ├── body[1] > spawn: - │ │ │ ├── task: #210 - │ │ │ ├── scope: %212 - │ │ │ ├── thunk: zane.spawn.26 + │ │ │ ├── task: #219 + │ │ │ ├── scope: %221 + │ │ │ ├── thunk: zane.spawn.27 │ │ │ ├── frame: {, i64, i64} │ │ │ ├── args[0]: 1 : i64 │ │ │ ├── args[1]: 0 : i64 - │ │ │ ├── dest: #211 + │ │ │ ├── dest: #220 │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String │ │ ├── body[2] > let: - │ │ │ ├── local: #217 + │ │ │ ├── local: #226 │ │ │ └── value: true : i1 │ │ ├── body[3] > spawn: - │ │ │ ├── task: #215 - │ │ │ ├── scope: %212 - │ │ │ ├── thunk: zane.spawn.27 + │ │ │ ├── task: #224 + │ │ │ ├── scope: %221 + │ │ │ ├── thunk: zane.spawn.28 │ │ │ ├── frame: {, i64, i64} │ │ │ ├── args[0]: 2 : i64 │ │ │ ├── args[1]: 0 : i64 - │ │ │ ├── dest: #216 + │ │ │ ├── dest: #225 │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String │ │ ├── body[4] > let: - │ │ │ ├── local: #221 + │ │ │ ├── local: #230 │ │ │ └── value: true : i1 │ │ ├── body[5] > spawn: - │ │ │ ├── task: #219 - │ │ │ ├── scope: %212 - │ │ │ ├── thunk: zane.spawn.28 + │ │ │ ├── task: #228 + │ │ │ ├── scope: %221 + │ │ │ ├── thunk: zane.spawn.29 │ │ │ ├── frame: {, i64, i64} │ │ │ ├── args[0]: 2 : i64 │ │ │ ├── args[1]: 1 : i64 - │ │ │ ├── dest: #220 + │ │ │ ├── dest: #229 │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String │ │ ├── body[6] > eval > expand: │ │ │ ├── type: void - │ │ │ ├── label: @222 - │ │ │ ├── body[0]: join #210 + │ │ │ ├── label: @231 + │ │ │ ├── body[0]: join #219 │ │ │ └── body[1] > if: - │ │ │ ├── cond: local #213 : i1 + │ │ │ ├── cond: local #222 : i1 │ │ │ ├── body[0] > assign: - │ │ │ │ ├── place: #213 + │ │ │ │ ├── place: #222 │ │ │ │ └── value: false : i1 │ │ │ └── body[1] > switch: │ │ │ ├── value > deref: │ │ │ │ ├── type: - │ │ │ │ └── ptr: address of #211 : ptr + │ │ │ │ └── ptr: address of #220 : ptr │ │ │ ├── cases[0] > case 0: │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ ├── id: %224 + │ │ │ ├── id: %233 │ │ │ ├── body[0] > hold: - │ │ │ │ ├── local: #223 - │ │ │ │ ├── scope: %224 + │ │ │ │ ├── local: #232 + │ │ │ │ ├── scope: %233 │ │ │ │ ├── value > take: │ │ │ │ │ ├── type: handle │ │ │ │ │ ├── address > offset: │ │ │ │ │ │ ├── within: │ │ │ │ │ │ ├── path: 1 - │ │ │ │ │ │ └── base: address of #211 : ptr + │ │ │ │ │ │ └── base: address of #220 : ptr │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── layout: %primitives$String │ │ │ ├── body[1] > assign: - │ │ │ │ ├── place: #207 + │ │ │ │ ├── place: #216 │ │ │ │ └── value > binary: │ │ │ │ ├── type: i64 │ │ │ │ ├── op: + - │ │ │ │ ├── left: local #207 : i64 + │ │ │ │ ├── left: local #216 : i64 │ │ │ │ └── right: 1 : i64 │ │ │ ├── body[2] > place: - │ │ │ │ ├── address: address of #211 : ptr + │ │ │ │ ├── address: address of #220 : ptr │ │ │ │ ├── value > case: │ │ │ │ │ ├── type: │ │ │ │ │ ├── index: 0 │ │ │ │ │ └── payload: 0 : i64 │ │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ │ │ └── body[3]: leave @222 + │ │ │ └── body[3]: leave @231 │ │ ├── body[7] > eval > expand: │ │ │ ├── type: void - │ │ │ ├── label: @225 - │ │ │ ├── body[0]: join #215 + │ │ │ ├── label: @234 + │ │ │ ├── body[0]: join #224 │ │ │ └── body[1] > if: - │ │ │ ├── cond: local #217 : i1 + │ │ │ ├── cond: local #226 : i1 │ │ │ ├── body[0] > assign: - │ │ │ │ ├── place: #217 + │ │ │ │ ├── place: #226 │ │ │ │ └── value: false : i1 │ │ │ └── body[1] > switch: │ │ │ ├── value > deref: │ │ │ │ ├── type: - │ │ │ │ └── ptr: address of #216 : ptr + │ │ │ │ └── ptr: address of #225 : ptr │ │ │ ├── cases[0] > case 0: │ │ │ └── cases[1] > case 1[0] > scope: - │ │ │ ├── id: %227 + │ │ │ ├── id: %236 │ │ │ ├── body[0] > hold: - │ │ │ │ ├── local: #226 - │ │ │ │ ├── scope: %227 + │ │ │ │ ├── local: #235 + │ │ │ │ ├── scope: %236 │ │ │ │ ├── value > take: │ │ │ │ │ ├── type: handle │ │ │ │ │ ├── address > offset: │ │ │ │ │ │ ├── within: │ │ │ │ │ │ ├── path: 1 - │ │ │ │ │ │ └── base: address of #216 : ptr + │ │ │ │ │ │ └── base: address of #225 : ptr │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── layout: %primitives$String │ │ │ ├── body[1] > assign: - │ │ │ │ ├── place: #207 + │ │ │ │ ├── place: #216 │ │ │ │ └── value > binary: │ │ │ │ ├── type: i64 │ │ │ │ ├── op: + - │ │ │ │ ├── left: local #207 : i64 + │ │ │ │ ├── left: local #216 : i64 │ │ │ │ └── right: 10 : i64 │ │ │ ├── body[2] > place: - │ │ │ │ ├── address: address of #216 : ptr + │ │ │ │ ├── address: address of #225 : ptr │ │ │ │ ├── value > case: │ │ │ │ │ ├── type: │ │ │ │ │ ├── index: 0 │ │ │ │ │ └── payload: 0 : i64 │ │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ │ │ └── body[3]: leave @225 + │ │ │ └── body[3]: leave @234 │ │ └── body[8] > eval > expand: │ │ ├── type: void - │ │ ├── label: @228 - │ │ ├── body[0]: join #219 + │ │ ├── label: @237 + │ │ ├── body[0]: join #228 │ │ └── body[1] > if: - │ │ ├── cond: local #221 : i1 + │ │ ├── cond: local #230 : i1 │ │ ├── body[0] > assign: - │ │ │ ├── place: #221 + │ │ │ ├── place: #230 │ │ │ └── value: false : i1 │ │ └── body[1] > switch: │ │ ├── value > deref: │ │ │ ├── type: - │ │ │ └── ptr: address of #220 : ptr + │ │ │ └── ptr: address of #229 : ptr │ │ ├── cases[0] > case 0: │ │ └── cases[1] > case 1[0] > scope: - │ │ ├── id: %230 + │ │ ├── id: %239 │ │ ├── body[0] > hold: - │ │ │ ├── local: #229 - │ │ │ ├── scope: %230 + │ │ │ ├── local: #238 + │ │ │ ├── scope: %239 │ │ │ ├── value > take: │ │ │ │ ├── type: handle │ │ │ │ ├── address > offset: │ │ │ │ │ ├── within: │ │ │ │ │ ├── path: 1 - │ │ │ │ │ └── base: address of #220 : ptr + │ │ │ │ │ └── base: address of #229 : ptr │ │ │ │ └── layout: %primitives$String │ │ │ └── layout: %primitives$String │ │ ├── body[1] > assign: - │ │ │ ├── place: #207 + │ │ │ ├── place: #216 │ │ │ └── value > binary: │ │ │ ├── type: i64 │ │ │ ├── op: + - │ │ │ ├── left: local #207 : i64 + │ │ │ ├── left: local #216 : i64 │ │ │ └── right: 100 : i64 │ │ ├── body[2] > place: - │ │ │ ├── address: address of #220 : ptr + │ │ │ ├── address: address of #229 : ptr │ │ │ ├── value > case: │ │ │ │ ├── type: │ │ │ │ ├── index: 0 │ │ │ │ └── payload: 0 : i64 │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ │ └── body[3]: leave @228 - │ ├── body[62] > eval > call: + │ │ └── body[3]: leave @237 + │ ├── body[66] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == - │ │ ├── left: local #207 : i64 + │ │ ├── left: local #216 : i64 │ │ └── right: 11 : i64 - │ ├── body[63] > let: - │ │ ├── local: #231 + │ ├── body[67] > let: + │ │ ├── local: #240 │ │ └── value: 0 : i64 - │ ├── body[64] > let: - │ │ ├── local: #232 + │ ├── body[68] > let: + │ │ ├── local: #241 │ │ └── value: 1 : i64 - │ ├── body[65] > eval > expand: + │ ├── body[69] > eval > expand: │ │ ├── type: void - │ │ ├── label: @234 + │ │ ├── label: @243 │ │ ├── body[0] > let: - │ │ │ ├── local: #233 + │ │ │ ├── local: #242 │ │ │ └── value: 4 : i64 │ │ ├── body[1] > repeat: │ │ │ ├── count > binary: @@ -1473,95 +1529,95 @@ │ │ │ │ ├── left > binary: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #233 : i64 + │ │ │ │ │ ├── left: local #242 : i64 │ │ │ │ │ └── right > flip: │ │ │ │ │ ├── type: i64 - │ │ │ │ │ └── value: local #232 : i64 + │ │ │ │ │ └── value: local #241 : i64 │ │ │ │ └── right: 1 : i64 │ │ │ ├── body[0] > if: │ │ │ │ ├── cond: true : i1 │ │ │ │ └── body[0] > eval > expand: │ │ │ │ ├── type: void - │ │ │ │ ├── label: @236 + │ │ │ │ ├── label: @245 │ │ │ │ └── body[0] > scope: - │ │ │ │ ├── id: %240 + │ │ │ │ ├── id: %249 │ │ │ │ ├── body[0] > let: - │ │ │ │ │ ├── local: #241 + │ │ │ │ │ ├── local: #250 │ │ │ │ │ └── value: true : i1 │ │ │ │ ├── body[1] > spawn: - │ │ │ │ │ ├── task: #238 - │ │ │ │ │ ├── scope: %240 - │ │ │ │ │ ├── thunk: zane.spawn.29 + │ │ │ │ │ ├── task: #247 + │ │ │ │ │ ├── scope: %249 + │ │ │ │ │ ├── thunk: zane.spawn.30 │ │ │ │ │ ├── frame: {, i64} - │ │ │ │ │ ├── args[0]: local #232 : i64 - │ │ │ │ │ ├── dest: #239 + │ │ │ │ │ ├── args[0]: local #241 : i64 + │ │ │ │ │ ├── dest: #248 │ │ │ │ │ └── layout: outcome of %primitives$Int │ │ │ │ ├── body[2] > assign: - │ │ │ │ │ ├── place: #231 + │ │ │ │ │ ├── place: #240 │ │ │ │ │ └── value > binary: │ │ │ │ │ ├── type: i64 │ │ │ │ │ ├── op: + - │ │ │ │ │ ├── left: local #231 : i64 + │ │ │ │ │ ├── left: local #240 : i64 │ │ │ │ │ └── right > expand: │ │ │ │ │ ├── type: i64 - │ │ │ │ │ ├── label: @243 - │ │ │ │ │ ├── result: #244 + │ │ │ │ │ ├── label: @252 + │ │ │ │ │ ├── result: #253 │ │ │ │ │ ├── body[0] > eval > expand: │ │ │ │ │ │ ├── type: void - │ │ │ │ │ │ ├── label: @242 - │ │ │ │ │ │ ├── body[0]: join #238 + │ │ │ │ │ │ ├── label: @251 + │ │ │ │ │ │ ├── body[0]: join #247 │ │ │ │ │ │ └── body[1] > if: - │ │ │ │ │ │ ├── cond: local #241 : i1 + │ │ │ │ │ │ ├── cond: local #250 : i1 │ │ │ │ │ │ ├── body[0] > assign: - │ │ │ │ │ │ │ ├── place: #241 + │ │ │ │ │ │ │ ├── place: #250 │ │ │ │ │ │ │ └── value: false : i1 │ │ │ │ │ │ └── body[1] > switch: │ │ │ │ │ │ ├── value > deref: │ │ │ │ │ │ │ ├── type: - │ │ │ │ │ │ │ └── ptr: address of #239 : ptr + │ │ │ │ │ │ │ └── ptr: address of #248 : ptr │ │ │ │ │ │ ├── cases[0] > case 0: - │ │ │ │ │ │ └── cases[1] > case 2[0]: leave @236 + │ │ │ │ │ │ └── cases[1] > case 2[0]: leave @245 │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ ├── place: #244 + │ │ │ │ │ ├── place: #253 │ │ │ │ │ └── value > deref: │ │ │ │ │ ├── type: i64 │ │ │ │ │ └── ptr > offset: │ │ │ │ │ ├── within: │ │ │ │ │ ├── path: 0 - │ │ │ │ │ └── base: address of #239 : ptr + │ │ │ │ │ └── base: address of #248 : ptr │ │ │ │ └── body[3] > eval > expand: │ │ │ │ ├── type: void - │ │ │ │ ├── label: @245 - │ │ │ │ ├── body[0]: join #238 + │ │ │ │ ├── label: @254 + │ │ │ │ ├── body[0]: join #247 │ │ │ │ └── body[1] > if: - │ │ │ │ ├── cond: local #241 : i1 + │ │ │ │ ├── cond: local #250 : i1 │ │ │ │ ├── body[0] > assign: - │ │ │ │ │ ├── place: #241 + │ │ │ │ │ ├── place: #250 │ │ │ │ │ └── value: false : i1 │ │ │ │ └── body[1] > switch: │ │ │ │ ├── value > deref: │ │ │ │ │ ├── type: - │ │ │ │ │ └── ptr: address of #239 : ptr + │ │ │ │ │ └── ptr: address of #248 : ptr │ │ │ │ ├── cases[0] > case 0: - │ │ │ │ └── cases[1] > case 2[0]: leave @236 + │ │ │ │ └── cases[1] > case 2[0]: leave @245 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #232 + │ │ │ ├── place: #241 │ │ │ └── value > binary: │ │ │ ├── type: i64 │ │ │ ├── op: + - │ │ │ ├── left: local #232 : i64 + │ │ │ ├── left: local #241 : i64 │ │ │ └── right: 1 : i64 │ │ ├── body[2] > eval: unit : void - │ │ └── body[3]: leave @234 - │ ├── body[66] > eval > call: + │ │ └── body[3]: leave @243 + │ ├── body[70] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: │ │ ├── type: i1 │ │ ├── op: == - │ │ ├── left: local #231 : i64 + │ │ ├── left: local #240 : i64 │ │ └── right: 8 : i64 - │ ├── body[67] > eval > call: + │ ├── body[71] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -1572,38 +1628,38 @@ │ │ │ ├── fn: spawns$fib(%primitives$Int) │ │ │ └── args[0]: 10 : i64 │ │ └── right: 55 : i64 - │ ├── body[68] > spawn: - │ │ ├── task: #247 + │ ├── body[72] > spawn: + │ │ ├── task: #256 │ │ ├── scope: %11 - │ │ ├── thunk: zane.spawn.30 + │ │ ├── thunk: zane.spawn.31 │ │ ├── frame: {i64, i64} │ │ ├── args[0]: 12 : i64 - │ │ ├── dest: #248 + │ │ ├── dest: #257 │ │ └── layout: %primitives$Int - │ ├── body[69] > store: + │ ├── body[73] > store: │ │ ├── address > expand: │ │ │ ├── type: ptr - │ │ │ ├── label: @249 - │ │ │ ├── result: #250 - │ │ │ ├── body[0]: join #247 + │ │ │ ├── label: @258 + │ │ │ ├── result: #259 + │ │ │ ├── body[0]: join #256 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #250 - │ │ │ └── value: address of #248 : ptr + │ │ │ ├── place: #259 + │ │ │ └── value: address of #257 : ptr │ │ └── value > binary: │ │ ├── type: i64 │ │ ├── op: + │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @251 - │ │ │ ├── result: #252 - │ │ │ ├── body[0]: join #247 + │ │ │ ├── label: @260 + │ │ │ ├── result: #261 + │ │ │ ├── body[0]: join #256 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #252 + │ │ │ ├── place: #261 │ │ │ └── value > deref: │ │ │ ├── type: i64 - │ │ │ └── ptr: address of #248 : ptr + │ │ │ └── ptr: address of #257 : ptr │ │ └── right: 1 : i64 - │ ├── body[70] > eval > call: + │ ├── body[74] > eval > call: │ │ ├── type: void │ │ ├── fn: spawns$check(%primitives$Bool) │ │ └── args[0] > binary: @@ -1611,87 +1667,87 @@ │ │ ├── op: == │ │ ├── left > expand: │ │ │ ├── type: i64 - │ │ │ ├── label: @253 - │ │ │ ├── result: #254 - │ │ │ ├── body[0]: join #247 + │ │ │ ├── label: @262 + │ │ │ ├── result: #263 + │ │ │ ├── body[0]: join #256 │ │ │ └── body[1] > assign: - │ │ │ ├── place: #254 + │ │ │ ├── place: #263 │ │ │ └── value > deref: │ │ │ ├── type: i64 - │ │ │ └── ptr: address of #248 : ptr + │ │ │ └── ptr: address of #257 : ptr │ │ └── right: 145 : i64 - │ ├── body[71] > return: unit : void - │ ├── body[72] > eval > expand: + │ ├── body[75] > return: unit : void + │ ├── body[76] > eval > expand: │ │ ├── type: void - │ │ ├── label: @255 - │ │ ├── body[0]: join #149 + │ │ ├── label: @264 + │ │ ├── body[0]: join #158 │ │ └── body[1] > if: - │ │ ├── cond: local #151 : i1 + │ │ ├── cond: local #160 : i1 │ │ ├── body[0] > assign: - │ │ │ ├── place: #151 + │ │ │ ├── place: #160 │ │ │ └── value: false : i1 │ │ └── body[1] > switch: │ │ ├── value > deref: │ │ │ ├── type: - │ │ │ └── ptr: address of #150 : ptr + │ │ │ └── ptr: address of #159 : ptr │ │ ├── cases[0] > case 0: │ │ └── cases[1] > case 1[0] > scope: - │ │ ├── id: %257 + │ │ ├── id: %266 │ │ ├── body[0] > hold: - │ │ │ ├── local: #256 - │ │ │ ├── scope: %257 + │ │ │ ├── local: #265 + │ │ │ ├── scope: %266 │ │ │ ├── value > take: │ │ │ │ ├── type: handle │ │ │ │ ├── address > offset: │ │ │ │ │ ├── within: │ │ │ │ │ ├── path: 1 - │ │ │ │ │ └── base: address of #150 : ptr + │ │ │ │ │ └── base: address of #159 : ptr │ │ │ │ └── layout: %primitives$String │ │ │ └── layout: %primitives$String │ │ ├── body[1] > assign: - │ │ │ ├── place: #147 + │ │ │ ├── place: #156 │ │ │ └── value > binary: │ │ │ ├── type: i64 │ │ │ ├── op: + - │ │ │ ├── left: local #147 : i64 + │ │ │ ├── left: local #156 : i64 │ │ │ └── right: 1 : i64 │ │ ├── body[2] > place: - │ │ │ ├── address: address of #150 : ptr + │ │ │ ├── address: address of #159 : ptr │ │ │ ├── value > case: │ │ │ │ ├── type: │ │ │ │ ├── index: 0 │ │ │ │ └── payload: 0 : i64 │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ │ └── body[3]: leave @255 - │ ├── body[73] > eval > expand: + │ │ └── body[3]: leave @264 + │ ├── body[77] > eval > expand: │ │ ├── type: void - │ │ ├── label: @258 - │ │ ├── body[0]: join #153 + │ │ ├── label: @267 + │ │ ├── body[0]: join #162 │ │ └── body[1] > if: - │ │ ├── cond: local #155 : i1 + │ │ ├── cond: local #164 : i1 │ │ ├── body[0] > assign: - │ │ │ ├── place: #155 + │ │ │ ├── place: #164 │ │ │ └── value: false : i1 │ │ └── body[1] > switch: │ │ ├── value > deref: │ │ │ ├── type: - │ │ │ └── ptr: address of #154 : ptr + │ │ │ └── ptr: address of #163 : ptr │ │ ├── cases[0] > case 0: │ │ └── cases[1] > case 1[0] > scope: - │ │ ├── id: %260 + │ │ ├── id: %269 │ │ ├── body[0] > hold: - │ │ │ ├── local: #259 - │ │ │ ├── scope: %260 + │ │ │ ├── local: #268 + │ │ │ ├── scope: %269 │ │ │ ├── value > take: │ │ │ │ ├── type: handle │ │ │ │ ├── address > offset: │ │ │ │ │ ├── within: │ │ │ │ │ ├── path: 1 - │ │ │ │ │ └── base: address of #154 : ptr + │ │ │ │ │ └── base: address of #163 : ptr │ │ │ │ └── layout: %primitives$String │ │ │ └── layout: %primitives$String │ │ └── body[1] > scope: - │ │ ├── id: %264 + │ │ ├── id: %273 │ │ ├── body[0] > eval > call: │ │ │ ├── type: void │ │ │ ├── fn: spawns$check(%primitives$Bool) @@ -1701,70 +1757,70 @@ │ │ │ ├── left > runtime: │ │ │ │ ├── type: i64 │ │ │ │ ├── fn: zane_text_equal - │ │ │ │ ├── args[0]: local #259 : handle + │ │ │ │ ├── args[0]: local #268 : handle │ │ │ │ └── args[1] > expand: │ │ │ │ ├── type: handle - │ │ │ │ ├── label: @262 - │ │ │ │ ├── result: #263 + │ │ │ │ ├── label: @271 + │ │ │ │ ├── result: #272 │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ ├── local: #261 - │ │ │ │ │ ├── scope: %264 + │ │ │ │ │ ├── local: #270 + │ │ │ │ │ ├── scope: %273 │ │ │ │ │ ├── value: "by zero" : handle │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #263 - │ │ │ │ └── value: local #261 : handle + │ │ │ │ ├── place: #272 + │ │ │ │ └── value: local #270 : handle │ │ │ └── right: 1 : i64 │ │ ├── body[1] > assign: - │ │ │ ├── place: #147 + │ │ │ ├── place: #156 │ │ │ └── value > binary: │ │ │ ├── type: i64 │ │ │ ├── op: + - │ │ │ ├── left: local #147 : i64 + │ │ │ ├── left: local #156 : i64 │ │ │ └── right: 1 : i64 │ │ ├── body[2] > place: - │ │ │ ├── address: address of #154 : ptr + │ │ │ ├── address: address of #163 : ptr │ │ │ ├── value > case: │ │ │ │ ├── type: │ │ │ │ ├── index: 0 │ │ │ │ └── payload: 7 : i64 │ │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ │ └── body[3]: leave @258 - │ └── body[74] > eval > expand: + │ │ └── body[3]: leave @267 + │ └── body[78] > eval > expand: │ ├── type: void - │ ├── label: @265 - │ ├── body[0]: join #180 + │ ├── label: @274 + │ ├── body[0]: join #189 │ └── body[1] > if: - │ ├── cond: local #182 : i1 + │ ├── cond: local #191 : i1 │ ├── body[0] > assign: - │ │ ├── place: #182 + │ │ ├── place: #191 │ │ └── value: false : i1 │ └── body[1] > switch: │ ├── value > deref: │ │ ├── type: - │ │ └── ptr: address of #181 : ptr + │ │ └── ptr: address of #190 : ptr │ ├── cases[0] > case 0: │ └── cases[1] > case 1[0] > scope: - │ ├── id: %267 + │ ├── id: %276 │ ├── body[0] > hold: - │ │ ├── local: #266 - │ │ ├── scope: %267 + │ │ ├── local: #275 + │ │ ├── scope: %276 │ │ ├── value > take: │ │ │ ├── type: handle │ │ │ ├── address > offset: │ │ │ │ ├── within: │ │ │ │ ├── path: 1 - │ │ │ │ └── base: address of #181 : ptr + │ │ │ │ └── base: address of #190 : ptr │ │ │ └── layout: %primitives$String │ │ └── layout: %primitives$String │ ├── body[1] > place: - │ │ ├── address: address of #181 : ptr + │ │ ├── address: address of #190 : ptr │ │ ├── value > case: │ │ │ ├── type: │ │ │ ├── index: 0 │ │ │ └── payload: 4 : i64 │ │ └── layout: outcome of %primitives$Int ? %primitives$String - │ └── body[2]: leave @265 + │ └── body[2]: leave @274 ├── funcs[1] > func: │ ├── symbol: spawns$check(%primitives$Bool) │ ├── params[0]: local #1 : i1 @@ -1901,6 +1957,16 @@ │ │ └── value: local #2 : handle │ └── layout: %primitives$String ├── funcs[6] > func: + │ ├── symbol: spawns$ageOf(spawns$Person) + │ ├── params[0]: local #1 : ptr + │ ├── ret: i64 + │ └── body[0] > return > snapshot: + │ ├── type: i64 + │ └── ptr > offset: + │ ├── within: {handle, i64} + │ ├── path: 1 + │ └── base: local #1 : ptr + ├── funcs[7] > func: │ ├── symbol: spawns$born(%primitives$String, %primitives$Int) │ ├── params[0]: local #1 : handle │ ├── params[1]: local #2 : i64 @@ -1941,7 +2007,7 @@ │ │ │ └── value: local #7 : handle │ │ └── args[1]: local #2 : i64 │ └── layout: ^spawns$Person - ├── funcs[7] > func: + ├── funcs[8] > func: │ ├── symbol: spawns$words(%primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── ret: handle @@ -2039,12 +2105,12 @@ │ │ ├── address: address of #2 : ptr │ │ └── layout: ^%primitives$List<%primitives$String> │ └── layout: ^%primitives$List<%primitives$String> - ├── funcs[8] > func: + ├── funcs[9] > func: │ ├── symbol: spawns$Tally(%primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── ret: i64 │ └── body[0] > return: local #1 : i64 - ├── funcs[9] > func: + ├── funcs[10] > func: │ ├── symbol: spawns$add(this spawns$Tally, %primitives$Int) │ ├── params[0]: local #1 : ptr │ ├── params[1]: local #2 : i64 @@ -2059,7 +2125,7 @@ │ │ │ └── ptr: local #1 : ptr │ │ └── right: local #2 : i64 │ └── body[1] > return: unit : void - ├── funcs[10] > func: + ├── funcs[11] > func: │ ├── symbol: spawns$deepen(this spawns$Countdown, %primitives$Int) │ ├── params[0]: local #1 : ptr │ ├── params[1]: local #2 : i64 @@ -2111,7 +2177,7 @@ │ │ ├── body[2] > eval: unit : void │ │ └── body[3]: leave @5 │ └── body[2] > return: unit : void - ├── funcs[11] > func: + ├── funcs[12] > func: │ ├── symbol: spawns$depth(spawns$Countdown) │ ├── params[0]: local #1 : │ ├── ret: i64 @@ -2157,7 +2223,7 @@ │ │ ├── type: │ │ └── ptr: local #6 : ptr │ └── cases[1] > case 1[2]: leave @2 - ├── funcs[12] > func: + ├── funcs[13] > func: │ ├── symbol: spawns$Holder(spawns$Countdown) │ ├── params[0]: local #1 : │ ├── ret: {, i64} @@ -2174,7 +2240,7 @@ │ │ ├── fn: spawns$Tally(%primitives$Int) │ │ └── args[0]: 0 : i64 │ └── layout: spawns$Holder - ├── funcs[13] > func: + ├── funcs[14] > func: │ ├── symbol: spawns$watch(&spawns$Holder, %primitives$Int, %primitives$Int) │ ├── params[0]: local #1 : ptr │ ├── params[1]: local #2 : i64 @@ -2263,7 +2329,7 @@ │ │ ├── body[2] > eval: unit : void │ │ └── body[3]: leave @7 │ └── body[3] > return: local #4 : i64 - ├── funcs[14] > func: + ├── funcs[15] > func: │ ├── symbol: spawns$pair(%primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── ret: i64 @@ -2272,7 +2338,7 @@ │ ├── body[0] > spawn: │ │ ├── task: #3 │ │ ├── scope: %5 - │ │ ├── thunk: zane.spawn.31 + │ │ ├── thunk: zane.spawn.32 │ │ ├── frame: {i64, i64} │ │ ├── args[0]: local #1 : i64 │ │ ├── dest: #4 @@ -2280,7 +2346,7 @@ │ ├── body[1] > spawn: │ │ ├── task: #7 │ │ ├── scope: %5 - │ │ ├── thunk: zane.spawn.32 + │ │ ├── thunk: zane.spawn.33 │ │ ├── frame: {i64, i64} │ │ ├── args[0] > binary: │ │ │ ├── type: i64 @@ -2314,12 +2380,12 @@ │ └── value > deref: │ ├── type: i64 │ └── ptr: address of #8 : ptr - ├── funcs[15] > func: + ├── funcs[16] > func: │ ├── symbol: spawns$nothing() │ ├── params: │ ├── ret: void │ └── body[0] > return: unit : void - ├── funcs[16] > func: + ├── funcs[17] > func: │ ├── symbol: spawns$divide(%primitives$Int, %primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── params[1]: local #2 : i64 @@ -2337,33 +2403,36 @@ │ │ ├── index: 1 │ │ └── payload > escape: │ │ ├── exit: function - │ │ ├── value > runtime: - │ │ │ ├── type: handle - │ │ │ ├── fn: zane_text_join - │ │ │ ├── args[0] > expand: + │ │ ├── value > escape: + │ │ │ ├── exit: function + │ │ │ ├── value > runtime: │ │ │ │ ├── type: handle - │ │ │ │ ├── label: @5 - │ │ │ │ ├── result: #6 - │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ ├── local: #4 - │ │ │ │ │ ├── scope: %7 - │ │ │ │ │ ├── value: "by " : handle - │ │ │ │ │ └── layout: %primitives$String - │ │ │ │ └── body[1] > assign: - │ │ │ │ ├── place: #6 - │ │ │ │ └── value: local #4 : handle - │ │ │ └── args[1] > expand: - │ │ │ ├── type: handle - │ │ │ ├── label: @9 - │ │ │ ├── result: #10 - │ │ │ ├── body[0] > hold: - │ │ │ │ ├── local: #8 - │ │ │ │ ├── scope: %7 - │ │ │ │ ├── value: "zero" : handle - │ │ │ │ └── layout: %primitives$String - │ │ │ └── body[1] > assign: - │ │ │ ├── place: #10 - │ │ │ └── value: local #8 : handle + │ │ │ │ ├── fn: zane_text_join + │ │ │ │ ├── args[0] > expand: + │ │ │ │ │ ├── type: handle + │ │ │ │ │ ├── label: @5 + │ │ │ │ │ ├── result: #6 + │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ ├── local: #4 + │ │ │ │ │ │ ├── scope: %7 + │ │ │ │ │ │ ├── value: "by " : handle + │ │ │ │ │ │ └── layout: %primitives$String + │ │ │ │ │ └── body[1] > assign: + │ │ │ │ │ ├── place: #6 + │ │ │ │ │ └── value: local #4 : handle + │ │ │ │ └── args[1] > expand: + │ │ │ │ ├── type: handle + │ │ │ │ ├── label: @9 + │ │ │ │ ├── result: #10 + │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ ├── local: #8 + │ │ │ │ │ ├── scope: %7 + │ │ │ │ │ ├── value: "zero" : handle + │ │ │ │ │ └── layout: %primitives$String + │ │ │ │ └── body[1] > assign: + │ │ │ │ ├── place: #10 + │ │ │ │ └── value: local #8 : handle + │ │ │ └── layout: %primitives$String │ │ └── layout: %primitives$String │ └── body[1] > return > case: │ ├── type: @@ -2373,7 +2442,7 @@ │ ├── op: / │ ├── left: local #1 : i64 │ └── right: local #2 : i64 - ├── funcs[17] > func: + ├── funcs[18] > func: │ ├── symbol: spawns$relay(%primitives$Int, %primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── params[1]: local #2 : i64 @@ -2386,7 +2455,7 @@ │ ├── body[1] > spawn: │ │ ├── task: #4 │ │ ├── scope: %6 - │ │ ├── thunk: zane.spawn.33 + │ │ ├── thunk: zane.spawn.34 │ │ ├── frame: {, i64, i64} │ │ ├── args[0]: local #1 : i64 │ │ ├── args[1]: local #2 : i64 @@ -2436,22 +2505,25 @@ │ │ │ │ ├── index: 1 │ │ │ │ └── payload > escape: │ │ │ │ ├── exit: function - │ │ │ │ ├── value > runtime: - │ │ │ │ │ ├── type: handle - │ │ │ │ │ ├── fn: zane_text_join - │ │ │ │ │ ├── args[0]: local #9 : handle - │ │ │ │ │ └── args[1] > expand: - │ │ │ │ │ ├── type: handle - │ │ │ │ │ ├── label: @12 - │ │ │ │ │ ├── result: #13 - │ │ │ │ │ ├── body[0] > hold: - │ │ │ │ │ │ ├── local: #11 - │ │ │ │ │ │ ├── scope: %14 - │ │ │ │ │ │ ├── value: "!" : handle - │ │ │ │ │ │ └── layout: %primitives$String - │ │ │ │ │ └── body[1] > assign: - │ │ │ │ │ ├── place: #13 - │ │ │ │ │ └── value: local #11 : handle + │ │ │ │ ├── value > escape: + │ │ │ │ │ ├── exit: function + │ │ │ │ │ ├── value > runtime: + │ │ │ │ │ │ ├── type: handle + │ │ │ │ │ │ ├── fn: zane_text_join + │ │ │ │ │ │ ├── args[0]: local #9 : handle + │ │ │ │ │ │ └── args[1] > expand: + │ │ │ │ │ │ ├── type: handle + │ │ │ │ │ │ ├── label: @12 + │ │ │ │ │ │ ├── result: #13 + │ │ │ │ │ │ ├── body[0] > hold: + │ │ │ │ │ │ │ ├── local: #11 + │ │ │ │ │ │ │ ├── scope: %14 + │ │ │ │ │ │ │ ├── value: "!" : handle + │ │ │ │ │ │ │ └── layout: %primitives$String + │ │ │ │ │ │ └── body[1] > assign: + │ │ │ │ │ │ ├── place: #13 + │ │ │ │ │ │ └── value: local #11 : handle + │ │ │ │ │ └── layout: %primitives$String │ │ │ │ └── layout: %primitives$String │ │ │ └── body[1] > assign: │ │ │ ├── place: #16 @@ -2496,24 +2568,27 @@ │ ├── index: 1 │ └── payload > escape: │ ├── exit: function - │ ├── value > runtime: - │ │ ├── type: handle - │ │ ├── fn: zane_text_join - │ │ ├── args[0]: local #18 : handle - │ │ └── args[1] > expand: - │ │ ├── type: handle - │ │ ├── label: @21 - │ │ ├── result: #22 - │ │ ├── body[0] > hold: - │ │ │ ├── local: #20 - │ │ │ ├── scope: %23 - │ │ │ ├── value: "!" : handle - │ │ │ └── layout: %primitives$String - │ │ └── body[1] > assign: - │ │ ├── place: #22 - │ │ └── value: local #20 : handle + │ ├── value > escape: + │ │ ├── exit: function + │ │ ├── value > runtime: + │ │ │ ├── type: handle + │ │ │ ├── fn: zane_text_join + │ │ │ ├── args[0]: local #18 : handle + │ │ │ └── args[1] > expand: + │ │ │ ├── type: handle + │ │ │ ├── label: @21 + │ │ │ ├── result: #22 + │ │ │ ├── body[0] > hold: + │ │ │ │ ├── local: #20 + │ │ │ │ ├── scope: %23 + │ │ │ │ ├── value: "!" : handle + │ │ │ │ └── layout: %primitives$String + │ │ │ └── body[1] > assign: + │ │ │ ├── place: #22 + │ │ │ └── value: local #20 : handle + │ │ └── layout: %primitives$String │ └── layout: %primitives$String - ├── funcs[18] > func: + ├── funcs[19] > func: │ ├── symbol: spawns$pick(%primitives$Int) │ ├── params[0]: local #1 : i64 │ ├── ret: @@ -2531,7 +2606,7 @@ │ ├── type: │ ├── index: 0 │ └── payload: local #1 : i64 - ├── funcs[19] > func: + ├── funcs[20] > func: │ ├── symbol: zane.spawn.1 │ ├── params[0]: local #8 : ptr │ ├── ret: void @@ -2549,7 +2624,7 @@ │ ├── within: {i64, i64} │ ├── path: 1 │ └── base: local #8 : ptr - ├── funcs[20] > func: + ├── funcs[21] > func: │ ├── symbol: zane.spawn.2 │ ├── params[0]: local #15 : ptr │ ├── ret: void @@ -2567,7 +2642,7 @@ │ ├── within: {handle, handle} │ ├── path: 1 │ └── base: local #15 : ptr - ├── funcs[21] > func: + ├── funcs[22] > func: │ ├── symbol: zane.spawn.3 │ ├── params[0]: local #28 : ptr │ ├── ret: void @@ -2585,15 +2660,33 @@ │ ├── within: {handle, ptr} │ ├── path: 1 │ └── base: local #28 : ptr - ├── funcs[22] > func: + ├── funcs[23] > func: │ ├── symbol: zane.spawn.4 - │ ├── params[0]: local #39 : ptr + │ ├── params[0]: local #40 : ptr + │ ├── ret: void + │ └── body[0] > store: + │ ├── address > offset: + │ │ ├── within: {i64, ptr} + │ │ ├── path: 0 + │ │ └── base: local #40 : ptr + │ └── value > call: + │ ├── type: i64 + │ ├── fn: spawns$ageOf(spawns$Person) + │ └── args[0] > deref: + │ ├── type: ptr + │ └── ptr > offset: + │ ├── within: {i64, ptr} + │ ├── path: 1 + │ └── base: local #40 : ptr + ├── funcs[24] > func: + │ ├── symbol: zane.spawn.5 + │ ├── params[0]: local #48 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {{handle, i64}, handle, i64} │ │ ├── path: 0 - │ │ └── base: local #39 : ptr + │ │ └── base: local #48 : ptr │ └── value > call: │ ├── type: {handle, i64} │ ├── fn: spawns$born(%primitives$String, %primitives$Int) @@ -2602,22 +2695,22 @@ │ │ └── ptr > offset: │ │ ├── within: {{handle, i64}, handle, i64} │ │ ├── path: 1 - │ │ └── base: local #39 : ptr + │ │ └── base: local #48 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {{handle, i64}, handle, i64} │ ├── path: 2 - │ └── base: local #39 : ptr - ├── funcs[23] > func: - │ ├── symbol: zane.spawn.5 - │ ├── params[0]: local #48 : ptr + │ └── base: local #48 : ptr + ├── funcs[25] > func: + │ ├── symbol: zane.spawn.6 + │ ├── params[0]: local #57 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {handle, i64} │ │ ├── path: 0 - │ │ └── base: local #48 : ptr + │ │ └── base: local #57 : ptr │ └── value > call: │ ├── type: handle │ ├── fn: spawns$words(%primitives$Int) @@ -2626,10 +2719,10 @@ │ └── ptr > offset: │ ├── within: {handle, i64} │ ├── path: 1 - │ └── base: local #48 : ptr - ├── funcs[24] > func: - │ ├── symbol: zane.spawn.6 - │ ├── params[0]: local #55 : ptr + │ └── base: local #57 : ptr + ├── funcs[26] > func: + │ ├── symbol: zane.spawn.7 + │ ├── params[0]: local #64 : ptr │ ├── ret: void │ └── body[0] > eval > call: │ ├── type: void @@ -2639,16 +2732,16 @@ │ │ └── ptr > offset: │ │ ├── within: {void, ptr, i64} │ │ ├── path: 1 - │ │ └── base: local #55 : ptr + │ │ └── base: local #64 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {void, ptr, i64} │ ├── path: 2 - │ └── base: local #55 : ptr - ├── funcs[25] > func: - │ ├── symbol: zane.spawn.7 - │ ├── params[0]: local #62 : ptr + │ └── base: local #64 : ptr + ├── funcs[27] > func: + │ ├── symbol: zane.spawn.8 + │ ├── params[0]: local #71 : ptr │ ├── ret: void │ └── body[0] > eval > call: │ ├── type: void @@ -2658,16 +2751,16 @@ │ │ └── ptr > offset: │ │ ├── within: {void, ptr, i64} │ │ ├── path: 1 - │ │ └── base: local #62 : ptr + │ │ └── base: local #71 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {void, ptr, i64} │ ├── path: 2 - │ └── base: local #62 : ptr - ├── funcs[26] > func: - │ ├── symbol: zane.spawn.8 - │ ├── params[0]: local #65 : ptr + │ └── base: local #71 : ptr + ├── funcs[28] > func: + │ ├── symbol: zane.spawn.9 + │ ├── params[0]: local #74 : ptr │ ├── ret: void │ └── body[0] > eval > call: │ ├── type: void @@ -2677,16 +2770,16 @@ │ │ └── ptr > offset: │ │ ├── within: {void, ptr, i64} │ │ ├── path: 1 - │ │ └── base: local #65 : ptr + │ │ └── base: local #74 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {void, ptr, i64} │ ├── path: 2 - │ └── base: local #65 : ptr - ├── funcs[27] > func: - │ ├── symbol: zane.spawn.9 - │ ├── params[0]: local #67 : ptr + │ └── base: local #74 : ptr + ├── funcs[29] > func: + │ ├── symbol: zane.spawn.10 + │ ├── params[0]: local #76 : ptr │ ├── ret: void │ └── body[0] > eval > call: │ ├── type: void @@ -2696,22 +2789,22 @@ │ │ └── ptr > offset: │ │ ├── within: {void, ptr, i64} │ │ ├── path: 1 - │ │ └── base: local #67 : ptr + │ │ └── base: local #76 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {void, ptr, i64} │ ├── path: 2 - │ └── base: local #67 : ptr - ├── funcs[28] > func: - │ ├── symbol: zane.spawn.10 - │ ├── params[0]: local #75 : ptr + │ └── base: local #76 : ptr + ├── funcs[30] > func: + │ ├── symbol: zane.spawn.11 + │ ├── params[0]: local #84 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {i64, ptr, i64, i64} │ │ ├── path: 0 - │ │ └── base: local #75 : ptr + │ │ └── base: local #84 : ptr │ └── value > call: │ ├── type: i64 │ ├── fn: spawns$watch(&spawns$Holder, %primitives$Int, %primitives$Int) @@ -2720,22 +2813,22 @@ │ │ └── ptr > offset: │ │ ├── within: {i64, ptr, i64, i64} │ │ ├── path: 1 - │ │ └── base: local #75 : ptr + │ │ └── base: local #84 : ptr │ ├── args[1] > deref: │ │ ├── type: i64 │ │ └── ptr > offset: │ │ ├── within: {i64, ptr, i64, i64} │ │ ├── path: 2 - │ │ └── base: local #75 : ptr + │ │ └── base: local #84 : ptr │ └── args[2] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {i64, ptr, i64, i64} │ ├── path: 3 - │ └── base: local #75 : ptr - ├── funcs[29] > func: - │ ├── symbol: zane.spawn.11 - │ ├── params[0]: local #80 : ptr + │ └── base: local #84 : ptr + ├── funcs[31] > func: + │ ├── symbol: zane.spawn.12 + │ ├── params[0]: local #89 : ptr │ ├── ret: void │ ├── body[0] > eval > call: │ │ ├── type: void @@ -2743,13 +2836,13 @@ │ │ ├── args[0] > offset: │ │ │ ├── within: {void, ptr, i64, } │ │ │ ├── path: 3 - │ │ │ └── base: local #80 : ptr + │ │ │ └── base: local #89 : ptr │ │ └── args[1] > deref: │ │ ├── type: i64 │ │ └── ptr > offset: │ │ ├── within: {void, ptr, i64, } │ │ ├── path: 2 - │ │ └── base: local #80 : ptr + │ │ └── base: local #89 : ptr │ └── body[1] > eval > runtime: │ ├── type: void │ ├── fn: zane_writeback @@ -2758,16 +2851,16 @@ │ │ └── ptr > offset: │ │ ├── within: {void, ptr, i64, } │ │ ├── path: 1 - │ │ └── base: local #80 : ptr + │ │ └── base: local #89 : ptr │ ├── args[1] > offset: │ │ ├── within: {void, ptr, i64, } │ │ ├── path: 3 - │ │ └── base: local #80 : ptr + │ │ └── base: local #89 : ptr │ ├── args[2]: 16 : i64 │ └── args[3]: layout spawns$Countdown - ├── funcs[30] > func: - │ ├── symbol: zane.spawn.12 - │ ├── params[0]: local #83 : ptr + ├── funcs[32] > func: + │ ├── symbol: zane.spawn.13 + │ ├── params[0]: local #92 : ptr │ ├── ret: void │ ├── body[0] > eval > call: │ │ ├── type: void @@ -2775,13 +2868,13 @@ │ │ ├── args[0] > offset: │ │ │ ├── within: {void, ptr, i64, i64} │ │ │ ├── path: 3 - │ │ │ └── base: local #83 : ptr + │ │ │ └── base: local #92 : ptr │ │ └── args[1] > deref: │ │ ├── type: i64 │ │ └── ptr > offset: │ │ ├── within: {void, ptr, i64, i64} │ │ ├── path: 2 - │ │ └── base: local #83 : ptr + │ │ └── base: local #92 : ptr │ └── body[1] > eval > runtime: │ ├── type: void │ ├── fn: zane_writeback @@ -2790,22 +2883,22 @@ │ │ └── ptr > offset: │ │ ├── within: {void, ptr, i64, i64} │ │ ├── path: 1 - │ │ └── base: local #83 : ptr + │ │ └── base: local #92 : ptr │ ├── args[1] > offset: │ │ ├── within: {void, ptr, i64, i64} │ │ ├── path: 3 - │ │ └── base: local #83 : ptr + │ │ └── base: local #92 : ptr │ ├── args[2]: 8 : i64 │ └── args[3]: layout spawns$Tally - ├── funcs[31] > func: - │ ├── symbol: zane.spawn.13 - │ ├── params[0]: local #87 : ptr + ├── funcs[33] > func: + │ ├── symbol: zane.spawn.14 + │ ├── params[0]: local #96 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {i64, i64} │ │ ├── path: 0 - │ │ └── base: local #87 : ptr + │ │ └── base: local #96 : ptr │ └── value > call: │ ├── type: i64 │ ├── fn: spawns$pair(%primitives$Int) @@ -2814,16 +2907,16 @@ │ └── ptr > offset: │ ├── within: {i64, i64} │ ├── path: 1 - │ └── base: local #87 : ptr - ├── funcs[32] > func: - │ ├── symbol: zane.spawn.14 - │ ├── params[0]: local #90 : ptr + │ └── base: local #96 : ptr + ├── funcs[34] > func: + │ ├── symbol: zane.spawn.15 + │ ├── params[0]: local #99 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {i64, i64} │ │ ├── path: 0 - │ │ └── base: local #90 : ptr + │ │ └── base: local #99 : ptr │ └── value > call: │ ├── type: i64 │ ├── fn: spawns$pair(%primitives$Int) @@ -2832,16 +2925,16 @@ │ └── ptr > offset: │ ├── within: {i64, i64} │ ├── path: 1 - │ └── base: local #90 : ptr - ├── funcs[33] > func: - │ ├── symbol: zane.spawn.15 - │ ├── params[0]: local #93 : ptr + │ └── base: local #99 : ptr + ├── funcs[35] > func: + │ ├── symbol: zane.spawn.16 + │ ├── params[0]: local #102 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {i64, i64} │ │ ├── path: 0 - │ │ └── base: local #93 : ptr + │ │ └── base: local #102 : ptr │ └── value > call: │ ├── type: i64 │ ├── fn: spawns$fib(%primitives$Int) @@ -2850,16 +2943,16 @@ │ └── ptr > offset: │ ├── within: {i64, i64} │ ├── path: 1 - │ └── base: local #93 : ptr - ├── funcs[34] > func: - │ ├── symbol: zane.spawn.16 - │ ├── params[0]: local #108 : ptr + │ └── base: local #102 : ptr + ├── funcs[36] > func: + │ ├── symbol: zane.spawn.17 + │ ├── params[0]: local #117 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {i64, i64} │ │ ├── path: 0 - │ │ └── base: local #108 : ptr + │ │ └── base: local #117 : ptr │ └── value > call: │ ├── type: i64 │ ├── fn: spawns$fib(%primitives$Int) @@ -2868,16 +2961,16 @@ │ └── ptr > offset: │ ├── within: {i64, i64} │ ├── path: 1 - │ └── base: local #108 : ptr - ├── funcs[35] > func: - │ ├── symbol: zane.spawn.17 - │ ├── params[0]: local #119 : ptr + │ └── base: local #117 : ptr + ├── funcs[37] > func: + │ ├── symbol: zane.spawn.18 + │ ├── params[0]: local #128 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {handle, handle} │ │ ├── path: 0 - │ │ └── base: local #119 : ptr + │ │ └── base: local #128 : ptr │ └── value > call: │ ├── type: handle │ ├── fn: spawns$greet(%primitives$String) @@ -2886,16 +2979,16 @@ │ └── ptr > offset: │ ├── within: {handle, handle} │ ├── path: 1 - │ └── base: local #119 : ptr - ├── funcs[36] > func: - │ ├── symbol: zane.spawn.18 - │ ├── params[0]: local #125 : ptr + │ └── base: local #128 : ptr + ├── funcs[38] > func: + │ ├── symbol: zane.spawn.19 + │ ├── params[0]: local #134 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {handle, handle} │ │ ├── path: 0 - │ │ └── base: local #125 : ptr + │ │ └── base: local #134 : ptr │ └── value > call: │ ├── type: handle │ ├── fn: spawns$greet(%primitives$String) @@ -2904,16 +2997,16 @@ │ └── ptr > offset: │ ├── within: {handle, handle} │ ├── path: 1 - │ └── base: local #125 : ptr - ├── funcs[37] > func: - │ ├── symbol: zane.spawn.19 - │ ├── params[0]: local #128 : ptr + │ └── base: local #134 : ptr + ├── funcs[39] > func: + │ ├── symbol: zane.spawn.20 + │ ├── params[0]: local #137 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {handle, i64} │ │ ├── path: 0 - │ │ └── base: local #128 : ptr + │ │ └── base: local #137 : ptr │ └── value > call: │ ├── type: handle │ ├── fn: spawns$words(%primitives$Int) @@ -2922,24 +3015,24 @@ │ └── ptr > offset: │ ├── within: {handle, i64} │ ├── path: 1 - │ └── base: local #128 : ptr - ├── funcs[38] > func: - │ ├── symbol: zane.spawn.20 - │ ├── params[0]: local #131 : ptr + │ └── base: local #137 : ptr + ├── funcs[40] > func: + │ ├── symbol: zane.spawn.21 + │ ├── params[0]: local #140 : ptr │ ├── ret: void │ └── body[0] > eval > call: │ ├── type: void │ ├── fn: spawns$nothing() │ └── args: - ├── funcs[39] > func: - │ ├── symbol: zane.spawn.21 - │ ├── params[0]: local #137 : ptr + ├── funcs[41] > func: + │ ├── symbol: zane.spawn.22 + │ ├── params[0]: local #146 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {handle, i64} │ │ ├── path: 0 - │ │ └── base: local #137 : ptr + │ │ └── base: local #146 : ptr │ └── value > call: │ ├── type: handle │ ├── fn: spawns$words(%primitives$Int) @@ -2948,16 +3041,16 @@ │ └── ptr > offset: │ ├── within: {handle, i64} │ ├── path: 1 - │ └── base: local #137 : ptr - ├── funcs[40] > func: - │ ├── symbol: zane.spawn.22 - │ ├── params[0]: local #144 : ptr + │ └── base: local #146 : ptr + ├── funcs[42] > func: + │ ├── symbol: zane.spawn.23 + │ ├── params[0]: local #153 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {handle, i64} │ │ ├── path: 0 - │ │ └── base: local #144 : ptr + │ │ └── base: local #153 : ptr │ └── value > call: │ ├── type: handle │ ├── fn: spawns$words(%primitives$Int) @@ -2966,16 +3059,16 @@ │ └── ptr > offset: │ ├── within: {handle, i64} │ ├── path: 1 - │ └── base: local #144 : ptr - ├── funcs[41] > func: - │ ├── symbol: zane.spawn.23 - │ ├── params[0]: local #148 : ptr + │ └── base: local #153 : ptr + ├── funcs[43] > func: + │ ├── symbol: zane.spawn.24 + │ ├── params[0]: local #157 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 0 - │ │ └── base: local #148 : ptr + │ │ └── base: local #157 : ptr │ └── value > call: │ ├── type: │ ├── fn: spawns$divide(%primitives$Int, %primitives$Int) @@ -2984,22 +3077,22 @@ │ │ └── ptr > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 1 - │ │ └── base: local #148 : ptr + │ │ └── base: local #157 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {, i64, i64} │ ├── path: 2 - │ └── base: local #148 : ptr - ├── funcs[42] > func: - │ ├── symbol: zane.spawn.24 - │ ├── params[0]: local #152 : ptr + │ └── base: local #157 : ptr + ├── funcs[44] > func: + │ ├── symbol: zane.spawn.25 + │ ├── params[0]: local #161 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 0 - │ │ └── base: local #152 : ptr + │ │ └── base: local #161 : ptr │ └── value > call: │ ├── type: │ ├── fn: spawns$divide(%primitives$Int, %primitives$Int) @@ -3008,22 +3101,22 @@ │ │ └── ptr > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 1 - │ │ └── base: local #152 : ptr + │ │ └── base: local #161 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {, i64, i64} │ ├── path: 2 - │ └── base: local #152 : ptr - ├── funcs[43] > func: - │ ├── symbol: zane.spawn.25 - │ ├── params[0]: local #179 : ptr + │ └── base: local #161 : ptr + ├── funcs[45] > func: + │ ├── symbol: zane.spawn.26 + │ ├── params[0]: local #188 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 0 - │ │ └── base: local #179 : ptr + │ │ └── base: local #188 : ptr │ └── value > call: │ ├── type: │ ├── fn: spawns$divide(%primitives$Int, %primitives$Int) @@ -3032,22 +3125,22 @@ │ │ └── ptr > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 1 - │ │ └── base: local #179 : ptr + │ │ └── base: local #188 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {, i64, i64} │ ├── path: 2 - │ └── base: local #179 : ptr - ├── funcs[44] > func: - │ ├── symbol: zane.spawn.26 - │ ├── params[0]: local #209 : ptr + │ └── base: local #188 : ptr + ├── funcs[46] > func: + │ ├── symbol: zane.spawn.27 + │ ├── params[0]: local #218 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 0 - │ │ └── base: local #209 : ptr + │ │ └── base: local #218 : ptr │ └── value > call: │ ├── type: │ ├── fn: spawns$divide(%primitives$Int, %primitives$Int) @@ -3056,22 +3149,22 @@ │ │ └── ptr > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 1 - │ │ └── base: local #209 : ptr + │ │ └── base: local #218 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {, i64, i64} │ ├── path: 2 - │ └── base: local #209 : ptr - ├── funcs[45] > func: - │ ├── symbol: zane.spawn.27 - │ ├── params[0]: local #214 : ptr + │ └── base: local #218 : ptr + ├── funcs[47] > func: + │ ├── symbol: zane.spawn.28 + │ ├── params[0]: local #223 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 0 - │ │ └── base: local #214 : ptr + │ │ └── base: local #223 : ptr │ └── value > call: │ ├── type: │ ├── fn: spawns$divide(%primitives$Int, %primitives$Int) @@ -3080,22 +3173,22 @@ │ │ └── ptr > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 1 - │ │ └── base: local #214 : ptr + │ │ └── base: local #223 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {, i64, i64} │ ├── path: 2 - │ └── base: local #214 : ptr - ├── funcs[46] > func: - │ ├── symbol: zane.spawn.28 - │ ├── params[0]: local #218 : ptr + │ └── base: local #223 : ptr + ├── funcs[48] > func: + │ ├── symbol: zane.spawn.29 + │ ├── params[0]: local #227 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 0 - │ │ └── base: local #218 : ptr + │ │ └── base: local #227 : ptr │ └── value > call: │ ├── type: │ ├── fn: spawns$divide(%primitives$Int, %primitives$Int) @@ -3104,22 +3197,22 @@ │ │ └── ptr > offset: │ │ ├── within: {, i64, i64} │ │ ├── path: 1 - │ │ └── base: local #218 : ptr + │ │ └── base: local #227 : ptr │ └── args[1] > deref: │ ├── type: i64 │ └── ptr > offset: │ ├── within: {, i64, i64} │ ├── path: 2 - │ └── base: local #218 : ptr - ├── funcs[47] > func: - │ ├── symbol: zane.spawn.29 - │ ├── params[0]: local #237 : ptr + │ └── base: local #227 : ptr + ├── funcs[49] > func: + │ ├── symbol: zane.spawn.30 + │ ├── params[0]: local #246 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {, i64} │ │ ├── path: 0 - │ │ └── base: local #237 : ptr + │ │ └── base: local #246 : ptr │ └── value > call: │ ├── type: │ ├── fn: spawns$pick(%primitives$Int) @@ -3128,16 +3221,16 @@ │ └── ptr > offset: │ ├── within: {, i64} │ ├── path: 1 - │ └── base: local #237 : ptr - ├── funcs[48] > func: - │ ├── symbol: zane.spawn.30 - │ ├── params[0]: local #246 : ptr + │ └── base: local #246 : ptr + ├── funcs[50] > func: + │ ├── symbol: zane.spawn.31 + │ ├── params[0]: local #255 : ptr │ ├── ret: void │ └── body[0] > store: │ ├── address > offset: │ │ ├── within: {i64, i64} │ │ ├── path: 0 - │ │ └── base: local #246 : ptr + │ │ └── base: local #255 : ptr │ └── value > call: │ ├── type: i64 │ ├── fn: spawns$fib(%primitives$Int) @@ -3146,9 +3239,9 @@ │ └── ptr > offset: │ ├── within: {i64, i64} │ ├── path: 1 - │ └── base: local #246 : ptr - ├── funcs[49] > func: - │ ├── symbol: zane.spawn.31 + │ └── base: local #255 : ptr + ├── funcs[51] > func: + │ ├── symbol: zane.spawn.32 │ ├── params[0]: local #2 : ptr │ ├── ret: void │ └── body[0] > store: @@ -3165,8 +3258,8 @@ │ ├── within: {i64, i64} │ ├── path: 1 │ └── base: local #2 : ptr - ├── funcs[50] > func: - │ ├── symbol: zane.spawn.32 + ├── funcs[52] > func: + │ ├── symbol: zane.spawn.33 │ ├── params[0]: local #6 : ptr │ ├── ret: void │ └── body[0] > store: @@ -3183,8 +3276,8 @@ │ ├── within: {i64, i64} │ ├── path: 1 │ └── base: local #6 : ptr - └── funcs[51] > func: - ├── symbol: zane.spawn.33 + └── funcs[53] > func: + ├── symbol: zane.spawn.34 ├── params[0]: local #3 : ptr ├── ret: void └── body[0] > store: diff --git a/tests/codegen/golden/spawns.out b/tests/codegen/golden/spawns.out index 4df0ee21..25fd7591 100644 --- a/tests/codegen/golden/spawns.out +++ b/tests/codegen/golden/spawns.out @@ -24,3 +24,5 @@ yes yes yes yes +yes +yes diff --git a/tests/codegen/golden/texts.cgt b/tests/codegen/golden/texts.cgt index cec82c8c..d1ecc89e 100644 --- a/tests/codegen/golden/texts.cgt +++ b/tests/codegen/golden/texts.cgt @@ -889,20 +889,23 @@ ├── index: 1 └── payload > escape: ├── exit: function - ├── value > runtime: - │ ├── type: handle - │ ├── fn: zane_text_join - │ ├── args[0]: local #1 : handle - │ └── args[1] > expand: - │ ├── type: handle - │ ├── label: @3 - │ ├── result: #4 - │ ├── body[0] > hold: - │ │ ├── local: #2 - │ │ ├── scope: %5 - │ │ ├── value: "!" : handle - │ │ └── layout: %primitives$String - │ └── body[1] > assign: - │ ├── place: #4 - │ └── value: local #2 : handle + ├── value > escape: + │ ├── exit: function + │ ├── value > runtime: + │ │ ├── type: handle + │ │ ├── fn: zane_text_join + │ │ ├── args[0]: local #1 : handle + │ │ └── args[1] > expand: + │ │ ├── type: handle + │ │ ├── label: @3 + │ │ ├── result: #4 + │ │ ├── body[0] > hold: + │ │ │ ├── local: #2 + │ │ │ ├── scope: %5 + │ │ │ ├── value: "!" : handle + │ │ │ └── layout: %primitives$String + │ │ └── body[1] > assign: + │ │ ├── place: #4 + │ │ └── value: local #2 : handle + │ └── layout: %primitives$String └── layout: %primitives$String diff --git a/tests/semantics/fixtures/typing/reject/bad/conventions.zn b/tests/semantics/fixtures/typing/reject/bad/conventions.zn new file mode 100644 index 00000000..826e4686 --- /dev/null +++ b/tests/semantics/fixtures/typing/reject/bad/conventions.zn @@ -0,0 +1,80 @@ +package bad; + +type Parcel = #struct { label String; } +Parcel(label String) => init{ label; } + +/// A function is used only at the passing modes it was written with: a +/// borrow, a take and a reference are passed differently (memory.md §2.9), +/// so inference does not turn one into another. +type Courier = #struct { deliver Int[^T]; } +Courier(deliver Int[^T], hint T Type) => init{ deliver; } +type Pointer = #struct { follow Int[&T]; } +Pointer(follow Int[&T], hint T Type) => init{ follow; } + +Unit couriers() { + c Courier(Int(p Parcel) => Int(1), Parcel(String("hint"))); + d Pointer(Int(n Int) => n, Int(2)); + return Unit(); +} + +/// An argument passed to a borrow is lent for the whole call, so a later +/// argument of the same call may not take it. +Unit observe(before Parcel, taken ^Parcel) => Unit() +Unit inspect(this Parcel, taken ^Parcel) => Unit() + +Unit lending() { + p ^Parcel = Parcel(String("left")); + observe(p, p); + q ^Parcel = Parcel(String("right")); + q:inspect(q); + return Unit(); +} + +/// A spawned call is lent the owners it borrows until its block drains, so +/// passing one to a `^T` parameter, which moves it, is an error there. +Unit glimpse(p Parcel) => Unit() +Unit keep(p ^Parcel) => Unit() + +Unit loaned() { + p ^Parcel = Parcel(String("lent")); + spawn glimpse(p); + keep(p); + return Unit(); +} + +/// An abort is a store into the caller's handler, like a return: a borrow is +/// never moved out by one, and a reference it hands back may not name the +/// body's own locals. +Int?^Parcel refuse(p Parcel) { abort p; } +Int?&Parcel wander() { + p Parcel(String("wander")); + abort &p; +} + +/// A bare type parameter as a result hands back a borrow once it is filled +/// with a reference type, which a written signature may not do. +T same(p ^T Type) => p + +Unit results() { + p Parcel = same(Parcel(String("same"))); + return Unit(); +} + +/// A handler's binder holds what the call aborted with, which may carry a +/// reference to anything its arguments name. +type Satchel = #struct { at &Parcel; } +Satchel(at &Parcel) => init{ at; } +Int?^Satchel hand(p &Parcel) { abort Satchel(p); } + +Unit binders() { + kept Parcel(String("kept")); + outer &Parcel = &kept; + @controlflow$branch(true, { + inner Parcel(String("inner")); + n Int = hand(&inner) ? e { + outer = e.at; + resolve Int(0); + } + }); + return Unit(); +} diff --git a/tests/semantics/golden/typing.reject.err b/tests/semantics/golden/typing.reject.err index 0b10e420..0c15612e 100644 --- a/tests/semantics/golden/typing.reject.err +++ b/tests/semantics/golden/typing.reject.err @@ -114,6 +114,42 @@ File "fixtures/typing/reject/bad/calls.zn", line 44, characters 5-20: 44 | resolve Int(1); | ^^^^^^^^^^^^^^^ Error: `resolve` finishes a handler, and this is in none: a block yields nothing, so it is not one +File "fixtures/typing/reject/bad/conventions.zn", line 15, characters 7-63: +15 | c Courier(Int(p Parcel) => Int(1), Parcel(String("hint"))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: no `Courier` accepts (@primitives$Int[bad$Parcel], bad$Parcel); the candidate is `Courier(@primitives$Int[^T], T)` +File "fixtures/typing/reject/bad/conventions.zn", line 16, characters 7-39: +16 | d Pointer(Int(n Int) => n, Int(2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: no `Pointer` accepts (@primitives$Int[@primitives$Int], @primitives$Int); the candidate is `Pointer(@primitives$Int[&T], T)` +File "fixtures/typing/reject/bad/conventions.zn", line 27, characters 16-17: +27 | observe(p, p); + | ^ +Error: this takes `p`, while an earlier argument of the same call borrows `p`, and a borrow lasts for the whole call +File "fixtures/typing/reject/bad/conventions.zn", line 29, characters 15-16: +29 | q:inspect(q); + | ^ +Error: this takes `q`, while an earlier argument of the same call borrows `q`, and a borrow lasts for the whole call +File "fixtures/typing/reject/bad/conventions.zn", line 41, characters 10-11: +41 | keep(p); + | ^ +Error: this moves `p`, which may be part of an owner lent through `p` to a spawned call that may read it until its block drains +File "fixtures/typing/reject/bad/conventions.zn", line 48, characters 38-39: +48 | Int?^Parcel refuse(p Parcel) { abort p; } + | ^ +Error: `p` is a borrow: the caller lends it for the call, and a borrow is never moved, stored or returned; a parameter that takes its argument is declared `^T` +File "fixtures/typing/reject/bad/conventions.zn", line 51, characters 11-13: +51 | abort &p; + | ^^ +Error: this stores a reference to `p` in the caller's handler, which outlives the body `p` is scoped to +File "fixtures/typing/reject/bad/conventions.zn", line 59, characters 21-43: +59 | p Parcel = same(Parcel(String("same"))); + | ^^^^^^^^^^^^^^^^^^^^^^ +Error: `bad$Parcel` is a reference type, and it fills the result of `same`, written bare; a borrow is never returned or aborted, so that position needs `^` or `&` to take this type +File "fixtures/typing/reject/bad/conventions.zn", line 75, characters 21-25: +75 | outer = e.at; + | ^^^^ +Error: this stores a reference to `inner`, whose block ends before `outer`'s does File "fixtures/typing/reject/bad/exits.zn", line 6, characters 5-17: 6 | guard(ready); | ^^^^^^^^^^^^ From e381975e14a166d877dbbc4d3d9bab8127d636fd Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 20:16:12 +0000 Subject: [PATCH 6/6] Read through `^T` where a type's home, concepts or kind is asked Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01ArFxFkp985vTC6aHAAxp82 --- lib/tst/passes/type_decls.ml | 3 ++- lib/tst/passes/verb_signatures.ml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/tst/passes/type_decls.ml b/lib/tst/passes/type_decls.ml index 3663e43b..4afabff6 100644 --- a/lib/tst/passes/type_decls.ml +++ b/lib/tst/passes/type_decls.ml @@ -62,6 +62,7 @@ let rec is_reference env ?(seen = []) (t : Ty.t) = | Some t -> t.reference | None -> false) | Ty.Reference _ -> true + | Ty.Roaming t -> is_reference env ~seen t | _ -> false let type_info_of_id env tid = Hashtbl.find_opt env.type_infos_by_id tid @@ -73,7 +74,7 @@ let rec concept_in (t : Ty.t) = | Ty.Concept _ -> Some t | Ty.Named (_, args) | Ty.Intrinsic { args; _ } -> List.find_map (function Ty.Type t -> concept_in t | Ty.Number _ -> None) args - | Ty.Reference t -> concept_in t + | Ty.Reference t | Ty.Roaming t -> concept_in t | _ -> None let mentions_concept t = Option.is_some (concept_in t) diff --git a/lib/tst/passes/verb_signatures.ml b/lib/tst/passes/verb_signatures.ml index cb23891b..4b239de5 100644 --- a/lib/tst/passes/verb_signatures.ml +++ b/lib/tst/passes/verb_signatures.ml @@ -197,7 +197,7 @@ let rec home (t : Ty.t) : S.home option = | Ty.Named (tid, _) -> Some (S.Package tid.Ty.package) | Ty.Intrinsic { namespace; _ } -> Some (S.Namespace namespace) | Ty.Concept _ -> Some (S.Namespace "concepts") - | Ty.Reference t -> home t + | Ty.Reference t | Ty.Roaming t -> home t | _ -> None (* The names a verb writes where a number goes, in its signature or in any