plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
74c9190
— Peter John
2026-10-26T10:52:24+05:30
docs(plans): keep Int/Float as i64/f64; narrow Int to i32 at extern boundaries
docs/plans/2026-10-26-game-engine.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Plum for games: browser-first macroquad host, port of a simplified KaM
|
|
2
2
|
|
|
3
3
|
**Status:** draft. Planned only and not run; the user has more ideas to fold in first.
|
|
4
|
-
**Depends on:** `2026-10-26-zig-generics-and-numbers.md`, which supplies
|
|
4
|
+
**Depends on:** `2026-10-26-zig-generics-and-numbers.md`, which supplies duck-typed per-type-copy generics, no traits (enums plus duck-typed operator methods), native typed arrays, and `Int`/`Float` kept as i64/f64 with `Int` narrowed to Int at `extern fun` boundaries (no JS BigInt in the browser bridge). That plan runs first.
|
|
5
5
|
**Goal:** Port `../kam_macroquad`, a 98k-line Rust/macroquad remake of Knights and Merchants that already runs in the browser via wasm, to Plum as a **simplified** game. There is no mission scripting (rhai) and the rules are simpler, but animations and sounds stay identical. It runs in the browser first.
|
|
6
6
|
**Architecture:** the Rust/macroquad host keeps asset decoding, audio and the window, compiled to wasm32 for the web or native. The Plum wasm-gc module owns game state, rules, pathfinding, AI, animation timing and per-frame draw calls. On the web the two modules talk through JS (miniquad plugin), and natively through wasmtime.
|
|
7
7
|
**Detail:** tests
|
|
@@ -19,31 +19,31 @@ Survey of `../kam_macroquad`:
|
|
|
19
19
|
- ~1.9k `&mut self` methods, 967 tests, ~350 macroquad calls.
|
|
20
20
|
- Collections: `Vec` ×997, `Option` ×814, `HashMap`/`Set`/`BTree`/`VecDeque`/`BinaryHeap` ×145.
|
|
21
21
|
- Closures and iterator chains ×~3.7k each.
|
|
22
|
-
- Numbers: `
|
|
22
|
+
- Numbers: `Int` ×5.2k, `Float` ×1.8k, `u8`/`u16`/`u32`/`usize` ×5.7k (mostly binary asset parsing).
|
|
23
23
|
- Traits are small: 7 traits, 63 impls, 40 `dyn`.
|
|
24
24
|
- The asset side uses flate2, postcard, RON and serde.
|
|
25
25
|
|
|
26
26
|
1. **Split of responsibilities.** Ruling: the host keeps asset decoding (`.rx` sprites/atlases, `kamdat`, flate2, map files), the serde/postcard/RON formats and audio, because that removes almost all the unsigned/binary-parsing and serialization code from the port. The cost is that the host stays Rust and is game-specific.
|
|
27
|
-
- The host exposes **handles**: a sprite is `(atlas:
|
|
27
|
+
- The host exposes **handles**: a sprite is `(atlas: Int, rect index: Int)`, and sounds and maps are `Int` ids. Animations stay identical because Plum gets the same frame tables, read through host imports.
|
|
28
28
|
2. **Browser host first.**
|
|
29
29
|
- `plum-game` is a Rust crate on macroquad. For the web it compiles to wasm32 with macroquad's JS loader.
|
|
30
|
-
- A `plum_bridge.js` miniquad plugin instantiates Plum's wasm-gc module, wires Plum's `extern fun` imports to functions exported from the macroquad wasm, and has macroquad's loop call Plum's `init`, `update(g, dt:
|
|
30
|
+
- A `plum_bridge.js` miniquad plugin instantiates Plum's wasm-gc module, wires Plum's `extern fun` imports to functions exported from the macroquad wasm, and has macroquad's loop call Plum's `init`, `update(g, dt: Float)` and `draw(g)`.
|
|
31
31
|
- The native build runs the same Plum module under wasmtime (GC, multi-value and SIMD on), with the same extern API.
|
|
32
32
|
- Ruling: game state lives in the Plum `Game` ref, held by the host and passed to every call, so no mutable globals are needed.
|
|
33
33
|
3. **Extern API (`plum-std/Game.plum`).**
|
|
34
|
-
- Window: `screenSize() -> Vec2` and `frameTime() ->
|
|
35
|
-
- Drawing: `clear(c)`, `drawSprite(atlas, rect, x, y:
|
|
36
|
-
- Input: `isKeyDown(k: Key)`, `isKeyPressed`, `mousePos() -> Vec2`, `isMouseDown(b: MouseButton)` and `mouseWheel() ->
|
|
37
|
-
- Audio: `playSound(id:
|
|
38
|
-
- Assets, as host lookups by name: `atlasId`, `animFrames(unitKind, action, dir) ->
|
|
34
|
+
- Window: `screenSize() -> Vec2` and `frameTime() -> Float`.
|
|
35
|
+
- Drawing: `clear(c)`, `drawSprite(atlas, rect, x, y: Float, flipX: Bool, tint: Color)`, `drawRect`, `drawLine`, `drawText(s: Str, x, y, size: Float, c)`, `setCamera2D(target: Vec2, zoom: Float)` and `resetCamera()`.
|
|
36
|
+
- Input: `isKeyDown(k: Key)`, `isKeyPressed`, `mousePos() -> Vec2`, `isMouseDown(b: MouseButton)` and `mouseWheel() -> Float`.
|
|
37
|
+
- Audio: `playSound(id: Int, x, y: Float)` (positional, as in KaM), `playMusic(id)` and `stopMusic()`.
|
|
38
|
+
- Assets, as host lookups by name: `atlasId`, `animFrames(unitKind, action, dir) -> List[Int]` (frame indices, copied once at load) and `animStep`, `loadMap(name) -> MapData`, which copies terrain into Plum `Array[Int]`s once.
|
|
39
39
|
- `Color` and `Vec2` are structs flattened across the boundary. `Key` and `MouseButton` are enums matching macroquad's codes.
|
|
40
40
|
4. **Language additions this port needs** (beyond the Zig-generics plan and items 5–7 below):
|
|
41
|
-
- **Unboxed typed arrays.** With copies per type, `Array[
|
|
41
|
+
- **Unboxed typed arrays.** With copies per type, `Array[Int]` and `Array[Float]` lower to native `array (mut i64)` and `array (mut f64)`. These are for tile grids, fog of war, passability and path costs. Add `Array.filled(n, v)` and a 2D index helper.
|
|
42
42
|
- **Collections in std:** `Set[T]`, `Deque[T]` (ring buffer) and `Heap[T]` (binary min-heap with a key fn, for A*), all Plum code over `Array`.
|
|
43
43
|
- **Constants:** large top-level constant tables must parse and be usable in expressions. A probe file with `LIMIT = 10` followed by a `type` failed to parse (cause not yet confirmed), and the notes record a SCREAMING_CASE constant gotcha. The fix needs tests with 1k-entry tables.
|
|
44
|
-
- **Seeded RNG** (`Random(seed:
|
|
44
|
+
- **Seeded RNG** (`Random(seed: Int)` with xorshift/PCG) for deterministic AI and simulation.
|
|
45
45
|
- Lazy iteration is deferred. Hot loops use `for … range`, and it's revisited only if profiling shows `map`/`filter` allocation in `update`.
|
|
46
|
-
5. **Value structs** (`struct Vec2(x:
|
|
46
|
+
5. **Value structs** (`struct Vec2(x: Float, y: Float)`, `Color`, `TilePos(x: Int, y: Int)`): flattened locals and params, multi-value returns, copy semantics and in-place field assignment. They are boxed only inside `List`/`Map`/`Try`, and by then they belong to a generic copy, so a `List[Vec2]` copy stores them as GC structs.
|
|
47
47
|
6. **Duck-typed operators** on structs (`add`/`sub`/`mul`/`neg`/`eq`): `pos + dir * speed`.
|
|
48
48
|
7. **SIMD (late):** `f32x4` (wasm v128) and `Mat4`/`Vec4` in `plum-std/Math.plum`. KaM is 2D, so this is low priority for the port and kept for future 3D work.
|
|
49
49
|
8. **Port scope (simplified KaM):**
|
|
@@ -71,7 +71,7 @@ Success criteria:
|
|
|
71
71
|
- Both suites (`cargo test --workspace`, `plum test plum-std plum-examples`) are green after every task.
|
|
72
72
|
- The `plum-game` crate builds for `wasm32-unknown-unknown` (web) and native. CI runs the native headless tests.
|
|
73
73
|
- The asset files are read from `../kam_macroquad/assets` through the host. They are never copied into this repo, because of licensing.
|
|
74
|
-
-
|
|
74
|
+
- Plum code uses `Int` (i64) and `Float` (f64). The extern API declares `Int`/`Float`, the compiler narrows `Int` to Int at the boundary, and the host converts `Float` to macroquad's Float.
|
|
75
75
|
|
|
76
76
|
## Tasks
|
|
77
77
|
|
|
@@ -103,13 +103,13 @@ flowchart LR
|
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
### L1 typed arrays, collections, constants, RNG
|
|
106
|
-
- Native `Array[
|
|
106
|
+
- Native `Array[Int]`/`Array[Float]` (wasm-inspected), `Array.filled` and the 2D helper.
|
|
107
107
|
- `Set`, `Deque` and `Heap` in std.
|
|
108
108
|
- Fix top-level constant parsing and use.
|
|
109
109
|
- `Random(seed)`.
|
|
110
110
|
|
|
111
111
|
Tests:
|
|
112
|
-
- `array_i32_native` (the wasm declares `array (mut
|
|
112
|
+
- `array_i32_native` (the wasm declares `array (mut i64)`);
|
|
113
113
|
- `heap_pop_order` on 1000 random keys;
|
|
114
114
|
- `deque_wraparound`;
|
|
115
115
|
- `set_dedup`;
|
|
@@ -145,7 +145,7 @@ Tests: a headless mock recorder for call order and args; `color_flattened`; `mou
|
|
|
145
145
|
Check: a demo scene (sprites, sound, input) runs in Chrome and Firefox. Playwright headless-Chrome smoke test: canvas pixels change across frames and there are no console errors.
|
|
146
146
|
|
|
147
147
|
### P1 map + camera
|
|
148
|
-
Load a KaM map via the host, terrain into `Array[
|
|
148
|
+
Load a KaM map via the host, terrain into `Array[Int]`, tile rendering, camera pan/zoom, minimap. Check: renders the same as the Rust version on 2 maps (screenshot diff tolerance).
|
|
149
149
|
|
|
150
150
|
### P2 units + animations + sounds
|
|
151
151
|
Unit structs, directional animation state machine, walk interpolation, selection, positional sounds. Tests: the animation frame sequence for walk dir 3 equals the Rust table, and the sound fires on the step frames.
|
docs/plans/2026-10-26-zig-generics-and-numbers.md
CHANGED
|
@@ -1,41 +1,39 @@
|
|
|
1
|
-
# Zig-style generics, no traits
|
|
1
|
+
# Zig-style generics, no traits (Int/Float stay 64-bit)
|
|
2
2
|
|
|
3
3
|
**Status:** draft. Planned only and not run; the user has more ideas to fold in first.
|
|
4
4
|
**Goal:**
|
|
5
5
|
- Replace traits and erased generics with Zig-style duck-typed generics: no bounds, a body checked per instantiation at the use site, and one compiled copy per type.
|
|
6
6
|
- Use enums for mixed-type values.
|
|
7
|
-
-
|
|
7
|
+
- Keep the numeric model simple: `Int` = i64 and `Float` = f64 are the only numeric types. At `extern fun` boundaries `Int` is narrowed to wasm i32, so the browser host never sees a BigInt.
|
|
8
8
|
- Move Bool's remaining methods into the compiler.
|
|
9
9
|
|
|
10
10
|
**Architecture:** parser → checker (structural `TApp`/`TParam` kept; a generic body is re-checked per concrete instantiation) → instantiation worklist (on-demand, reachable-only) → codegen (unboxed, no `$Obj`/dyn) → hosts.
|
|
11
11
|
**Detail:** tests
|
|
12
12
|
**Relation to other plans:**
|
|
13
|
-
- This plan
|
|
13
|
+
- This plan supplies the generics, operators and numeric model that `2026-10-26-game-engine.md` builds on.
|
|
14
|
-
- It changes that plan's T4: operator traits become duck-typed operator methods.
|
|
15
14
|
- It reverses item 3 and part of item 4 of `2026-10-25-erased-generics.md` (trait types, typeId dispatch, boxing at generic edges).
|
|
16
15
|
|
|
17
16
|
## Design
|
|
18
17
|
|
|
19
18
|
What the user decided:
|
|
20
|
-
- Integer literals default to `i32` and float literals to `f32`. Later: only native wasm types, lowercase, no pretend widths (no u8/u32).
|
|
21
19
|
- Traits are removed in favour of Zig-style duck typing, with one copy per type ("Zig-faithful").
|
|
22
20
|
- Mixed types are handled with enums only (the Zig way).
|
|
23
21
|
- Comptime covers duck-typed type params only, with no compile-time execution.
|
|
24
22
|
- The `[T]` syntax is kept.
|
|
25
23
|
- Reference: axelmagn.com "Zig traits": traits are a convention (records of functions passed explicitly), not a language feature.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
|
|
24
|
+
- Numbers: the user first chose i32/f32 defaults, then only native wasm types. They settled on **keeping `Int`/`Float` as i64/f64**, because Plum does no binary decoding (the Rust host does), only game logic.
|
|
25
|
+
|
|
26
|
+
1. **Numbers stay `Int` (i64) and `Float` (f64).**
|
|
27
|
+
- No sized or unsigned types are added; `Byte`/`[]Byte` stay as they are for `Str`/`Buffer` internals.
|
|
28
|
+
- Wasm i64/f64 arithmetic runs at full speed on 64-bit hosts. The doubled memory for KaM-scale grids (a 128×128 grid is 128 KB) doesn't matter.
|
|
29
|
+
- The `Number = | Int | Float` enum and its implicit coercion stay as today. Generic numeric code is duck-typed (Design item 3), so a `sum[T]` works for both.
|
|
30
|
+
- **Extern narrowing:** in an `extern fun` signature, `Int` params and results lower to wasm **i32**. The value is wrapped going out and sign-extended coming back, and debug builds trap when an `Int` doesn't fit. `Float` stays f64, which is a plain JS number.
|
|
31
|
+
- Ruling: this avoids the browser bridge creating a JS `BigInt` for every i64 argument on hot paths like hundreds of `drawSprite` calls per frame. The cost is that Ints of 2^31 or more can't cross the host boundary, which is fine for handles, ids, key codes and tile indices.
|
|
32
|
+
- Future: if 3D or SIMD is ever needed, add `f32`/`f32x4` then. Not now.
|
|
33
|
+
2. **Bool fully built in.**
|
|
34
|
+
- Bool is already a built-in i32 (`b3bb7b5`).
|
|
35
|
+
- `toStr` becomes a compiler intrinsic and `parse` becomes `Str.parseBool() -> Try[Bool, [BadBoolStr]]`.
|
|
36
|
+
- `and`/`or` are dropped in favour of `&&`/`||`, and `Bool.plum` is deleted.
|
|
39
37
|
3. **Duck-typed generics.**
|
|
40
38
|
- Generic params carry no bounds (`[T]` only). Bound syntax and the `trait` item are removed from the grammar, and existing `trait`/`implements` source becomes a parse error with a migration hint.
|
|
41
39
|
- A generic body is type-checked once per concrete instantiation, with `TParam`s substituted, at the point the instantiation is first needed.
|
|
@@ -44,99 +42,88 @@ What the user decided:
|
|
|
44
42
|
4. **Instantiation (copy per type), on demand.**
|
|
45
43
|
- The worklist starts at `main`, exported fns and tests. Each call site with concrete type args enqueues `(fn, [types])`.
|
|
46
44
|
- Each key is checked and compiled once, and memoized.
|
|
47
|
-
- Generic types (`List[
|
|
45
|
+
- Generic types (`List[Int]`, `Try[Str, [A]]`) get one GC struct per distinct instantiation, keyed by the structural type, never by mangled strings.
|
|
48
46
|
- Only reachable instantiations are produced, which avoids the old "every method for every specialization" problem.
|
|
49
|
-
- Names in wasm are only for debugging (`List[
|
|
50
|
-
- Ruling: tag
|
|
51
|
-
5. **Unboxed runtime.** `$Obj`, `typeId` headers, `$Box*` and `$dyn$…` dispatchers are removed. Enum variants keep their exact GC subtypes for `match`. The closure ABI is typed per signature again, with no anyref boxing.
|
|
52
|
-
6. **Mixed types via enums.** `List[Shape]` becomes `enum Shape = | Circle(r:
|
|
47
|
+
- Names in wasm are only for debugging (`List[Int].map`).
|
|
48
|
+
- Ruling: tag unions go through this structurally, so generic tag payloads become possible.
|
|
49
|
+
5. **Unboxed runtime.** `$Obj`, `typeId` headers, `$Box*` and `$dyn$…` dispatchers are removed. Enum variants keep their exact GC subtypes for `match`. The closure ABI is typed per signature again, with no anyref boxing. Typed arrays fall out of this: `Array[Int]` and `Array[Float]` copies lower to native `array (mut i64)` and `array (mut f64)`.
|
|
50
|
+
6. **Mixed types via enums.** `List[Shape]` becomes `enum Shape = | Circle(r: Float) | Rect(w: Float, h: Float)` with methods that `match self`. `plum-examples/traits.plum` becomes `shapes.plum` written this way.
|
|
53
51
|
7. **Operators are duck-typed.** `a + b` on non-primitive operands calls `a.add(b)` if that method exists for the instantiated type; likewise `sub`, `mul`, `div`, `neg`, `eq` (for `==`) and `lt` (for `<`). Calls are resolved statically per instantiation.
|
|
54
52
|
8. **Explicit "trait values"** (the article's pattern) need no feature: a record of fn fields (`type Num[T](add: fn(T, T) -> T, zero: T)`) passed as an argument. When the argument is a top-level constant, the copy-per-type codegen calls its fns directly and they can be inlined. Document the pattern in the README.
|
|
55
|
-
9. **Out of scope:** comptime execution (`comptime` blocks, reflection), separate compilation, and any global cap on instantiation count. A warning is printed above 200 instantiations of one generic.
|
|
53
|
+
9. **Out of scope:** sized/unsigned numeric types, comptime execution (`comptime` blocks, reflection), separate compilation, and any global cap on instantiation count. A warning is printed above 200 instantiations of one generic.
|
|
56
54
|
|
|
57
55
|
Success criteria:
|
|
58
56
|
- `plum test plum-std plum-examples` and `cargo test --workspace` are green.
|
|
59
57
|
- A typical compile is ≤ 60 ms, against today's 20–25 ms erased. That's the agreed cost of copying per type.
|
|
60
|
-
- `List[
|
|
58
|
+
- `List[Int].map` in the wasm touches no boxes.
|
|
61
59
|
- A generic called with a type lacking a method fails with the use-site chain message.
|
|
60
|
+
- An `extern fun f(x: Int) -> Int` is imported with wasm type `(i32) -> i32`.
|
|
62
61
|
|
|
63
62
|
## Global constraints
|
|
64
63
|
|
|
65
64
|
- Both suites are green after every task. Regenerate the grammar only with `--disable-optimizations`.
|
|
66
|
-
- Error wording: `in <fn>[<types>] (called at <file>:<line>): <Type> has no method '<m>'`
|
|
65
|
+
- Error wording: `in <fn>[<types>] (called at <file>:<line>): <Type> has no method '<m>'`.
|
|
67
66
|
- No `$` name mangling anywhere. The instantiation key is `(item path, Vec<PlumType>)`.
|
|
68
67
|
|
|
69
68
|
## Tasks (sequential)
|
|
70
69
|
|
|
71
70
|
```mermaid
|
|
72
71
|
flowchart LR
|
|
73
|
-
T1[
|
|
72
|
+
T1[extern Int narrowing + Bool.plum removal]
|
|
74
|
-
T2[Bool/Number std removal]
|
|
75
|
-
|
|
73
|
+
T2[per-instantiation checking]
|
|
76
|
-
|
|
74
|
+
T3[on-demand instantiation codegen]
|
|
77
|
-
|
|
75
|
+
T4[remove traits, dyn, boxing]
|
|
78
|
-
|
|
76
|
+
T5[enums for mixed types + operators]
|
|
79
|
-
|
|
77
|
+
T6[migration + docs + bench]
|
|
80
78
|
%% taskgraph
|
|
81
79
|
T1 --> T2
|
|
82
80
|
T2 --> T3
|
|
83
81
|
T3 --> T4
|
|
84
82
|
T4 --> T5
|
|
85
83
|
T5 --> T6
|
|
86
|
-
T6 --> T7
|
|
87
84
|
```
|
|
88
85
|
|
|
89
|
-
### T1
|
|
90
|
-
|
|
91
|
-
- `
|
|
92
|
-
- `
|
|
93
|
-
- `
|
|
94
|
-
- `
|
|
95
|
-
- `
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
- `cast_trunc_sat`: `i32(f32(1e10)) == 2147483647`.
|
|
99
|
-
|
|
100
|
-
### T2 Bytes + std migration off Int/Float/Byte/Bool
|
|
101
|
-
Add the built-in `Bytes` (packed `array (mut i8)`, `get`/`getS`/`set`/`len`). `Str`/`Buffer` move to it. Delete Bool.plum, Number.plum, Byte.plum and ByteSlice.plum. Add intrinsic `toStr`/interpolation for primitives and `Str.parse*`. Rewrite every `Int`/`Float`/`Byte`/`Bool`/`True`/`False` in plum-std, choosing the width per use (i64/f64 for Time/Json/Uuid/Random). Tests:
|
|
102
|
-
- the existing std suites;
|
|
103
|
-
- `bytes_one_byte_per_elem`: the wasm declares `array (mut i8)`;
|
|
104
|
-
- `bytes_get_u_vs_s`: storing 200 gives `get == 200` and `getS == -56`;
|
|
105
|
-
- `time_millis_no_wrap`: `nowMillis() > 2147483647`.
|
|
106
|
-
|
|
107
|
-
### T3 per-instantiation checking
|
|
86
|
+
### T1 extern Int narrowing + Bool.plum removal
|
|
87
|
+
`Int` in `extern fun` signatures lowers to i32: wrap out, sign-extend in, and a debug-build range trap. Update the hosts (plum-cli, plum-runtime, examples_test linkers) for the narrowed import types. Delete Bool.plum, add an intrinsic `Bool.toStr`, and add `Str.parseBool`. Tests:
|
|
88
|
+
- `extern_int_is_i32`: the import type is `(i32) -> i32`;
|
|
89
|
+
- `extern_int_roundtrip_negative`: -5 out and back is still -5;
|
|
90
|
+
- `extern_int_overflow_traps_debug`;
|
|
91
|
+
- `bool_tostr_intrinsic`: `"{true}"` is `"True"`;
|
|
92
|
+
- `parse_bool`.
|
|
93
|
+
|
|
94
|
+
### T2 per-instantiation checking
|
|
108
95
|
Re-check generic bodies per concrete instantiation and produce the use-site chain errors. Bounds are removed from the checker. Tests:
|
|
109
96
|
- `duck_ok`: `max[Vec2]` works when Vec2 has `lt`.
|
|
110
97
|
- `duck_missing_method_chain`: the message includes `called at`.
|
|
111
98
|
- `uninstantiated_generic_not_type_errored`.
|
|
112
99
|
- `nested_instantiation_chain`: two levels deep.
|
|
113
100
|
|
|
114
|
-
###
|
|
101
|
+
### T3 on-demand instantiation codegen
|
|
115
|
-
Worklist from roots, one wasm fn/struct per `(item, types)`, structural keys, unboxed values, typed closures. Tests:
|
|
102
|
+
Worklist from roots, one wasm fn/struct per `(item, types)`, structural keys, unboxed values, typed closures, native typed arrays. Tests:
|
|
116
|
-
- `one_copy_per_distinct_type`: List[
|
|
103
|
+
- `one_copy_per_distinct_type`: List[Int] and List[Str] give 2 `map`s, and List[Int] used twice still gives 1.
|
|
117
104
|
- `unreachable_generic_not_emitted`.
|
|
118
|
-
- `
|
|
105
|
+
- `no_boxes_in_list_int_map` (wasm inspection).
|
|
106
|
+
- `array_int_native`: the wasm declares `array (mut i64)`.
|
|
119
|
-
- `generic_tag_payload`: `Try[
|
|
107
|
+
- `generic_tag_payload`: `Try[Int, [Bad(T)]]` works.
|
|
120
108
|
|
|
121
|
-
###
|
|
109
|
+
### T4 remove traits, dyn, boxing
|
|
122
110
|
Remove the grammar's `trait`/`implements`/bounds, the checker's trait tables and coercion, and codegen's `$Obj`/typeId/`$Box*`/`$dyn` and erased paths. Tests: `trait_syntax_rejected_with_hint`. Workspace and plum suites green. Report the LOC delta.
|
|
123
111
|
|
|
124
|
-
###
|
|
112
|
+
### T5 enums for mixed types + duck-typed operators
|
|
125
113
|
`traits.plum` becomes `shapes.plum` (enum Shape). Operator methods `add/sub/mul/div/neg/eq/lt`. The README documents the explicit "trait values" pattern (Design item 8). Tests:
|
|
126
114
|
- `shape_enum_area_sum`.
|
|
127
115
|
- `vec2_add_operator`.
|
|
128
116
|
- `operator_missing_method_error`.
|
|
129
|
-
- `num_record_passed_explicitly` (the article's pattern, e.g. `sumWith(nums,
|
|
117
|
+
- `num_record_passed_explicitly` (the article's pattern, e.g. `sumWith(nums, intNum)`).
|
|
130
118
|
|
|
131
|
-
###
|
|
132
|
-
Migrate every example, test and README snippet
|
|
119
|
+
### T6 migration, docs, bench
|
|
120
|
+
Migrate every example, test and README snippet off traits. Update the README sections "How generics compile" (now per-type copies, Zig-style) and "extern functions" (Int narrowed to i32). Rebuild the scratchpad bench and record compile times and wasm sizes against the erased baseline (typical 20–25 ms, Bool.wasm 19 KB).
|
|
133
121
|
|
|
134
122
|
## Rulings (user said "you decide")
|
|
135
123
|
|
|
136
124
|
- Ruling: the instantiation warning stays at 200 copies of one generic. It is only a warning, never an error.
|
|
137
|
-
- Ruling: `"{x}"` on a type with no `toStr` is a duck-typed compile error at the use site. Primitives, `Str`, `
|
|
125
|
+
- Ruling: `"{x}"` on a type with no `toStr` is a duck-typed compile error at the use site. Primitives, `Str`, `Bool` and tag unions format through compiler intrinsics.
|
|
138
|
-
- Ruling: `Str` stays capitalized, because it's a GC heap type, following "lowercase = wasm value type".
|
|
139
|
-
- Ruling: comptime performance means copies per type (Design item 4) and unboxed native values everywhere. That's why the generic boxing paths are removed in
|
|
126
|
+
- Ruling: comptime performance means copies per type (Design item 4) and unboxed native values everywhere. That's why the generic boxing paths are removed in T4.
|
|
140
127
|
|
|
141
128
|
## Open questions
|
|
142
129
|
|