Skip to content

Commit 8bcf450

Browse files
refactor(tw): extract Tw_section.Encode.ownership; delete orphan tw_ownership_section.ml (#456)
## Summary Establishes `lib/tw_section.{ml,mli}` as the canonical home for typed-wasm custom-section wire encoders, sized to absorb proposal 0001's two new sections (`typedwasm.regions`, `typedwasm.capabilities`) when they land. Net change: 6 files, +69 / −115 = **net −46 LOC**. ## What changes | File | Change | |---|---| | `lib/tw_section.ml` | **New** — `Encode.ownership` (operates on pre-byte-encoded triples, avoids type cycle with Codegen) | | `lib/tw_section.mli` | **New** — public interface | | `lib/dune` | `tw_section` added to modules list | | `lib/codegen.ml` | `build_ownership_section` body replaced with 6-line wrapper delegating to `Tw_section.Encode.ownership` (signature unchanged, byte-equal output) | | `lib/tw_verify.ml:213` | Stale doc comment fixed (`Tw_ownership_section.build_section` → `Tw_section.Encode.ownership`) | | `lib/tw_ownership_section.ml` | **Deleted** — was orphan, not in `lib/dune`, no callers | ## Rationale Filed as #444: prevent the 3-sections × 2-copies = 6-encoder fan-out when proposal 0001 ([typed-wasm#34](hyperpolymath/typed-wasm#34) / [typed-wasm#76](hyperpolymath/typed-wasm#76)) lands. Sharing the internal LE writers (`write_u32_le` / `write_u8`) inside `Tw_section.Encode` means future `Encode.regions` / `Encode.capabilities` land in one place rather than duplicating the buffer plumbing. ## Out of scope (deliberately) - **ADR-021 v2-emit flip** — `Tw_section.Encode.ownership` ships v1 unchanged. When cross-repo coordination unblocks the flip, it becomes a localised change to this one module. - **Tea_* hand-rolled fixtures** (`lib/tea_bridge.ml`, `lib/tea_cs_bridge.ml`, `lib/tea_router.ml`) — these hard-code demo bytes for tea demonstrations, separate concern from live producer-side dedup. Can be migrated in a follow-up if/when the tea demos need updating. - **Decode side** — `tw_interface.ml` reads via `Tw_verify.parse_ownership_section_payload` directly. No change needed; `Tw_section.Decode` is not introduced because there's no current duplicate to dedup. Can be added if proposal 0002's access-site codec needs symmetric Decode plumbing. ## Test plan - [x] `dune build` clean. - [x] `dune runtest` 352/352 passing. - [x] Byte-equality verified by the existing ownership round-trip tests (`test/test_e2e.ml:1218 test_ownership_roundtrip`, `:1249 test_ownership_entry_count`, `:1268 test_ownership_v2_parse_roundtrip`, `:1296 test_ownership_v2_unknown_version_empty`). Any byte divergence would fail the round-trip. ## Type cycle avoidance `Codegen → Tw_section → Codegen` would cycle if `Tw_section` referenced `Codegen.ownership_kind`. Avoided by having `Encode.ownership` take `(int * int list * int) list` — already-byte-encoded triples. Codegen does the `ownership_kind → byte` mapping (via `ownership_kind_byte`) before calling. Tw_section stays type-clean. Refs #444 Refs hyperpolymath/typed-wasm#34 Refs hyperpolymath/typed-wasm#76 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 9b9b321 commit 8bcf450

6 files changed

Lines changed: 69 additions & 115 deletions

File tree

lib/codegen.ml

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -157,24 +157,12 @@ let ownership_kind_byte = function
157157
u8* param_kind (one per param, see kind encoding above)
158158
u8 return_kind *)
159159
let build_ownership_section (annots : (int * ownership_kind list * ownership_kind) list) : bytes =
160-
if annots = [] then Bytes.empty
161-
else
162-
let buf = Buffer.create 64 in
163-
let write_u32_le n =
164-
Buffer.add_char buf (Char.chr (n land 0xff));
165-
Buffer.add_char buf (Char.chr ((n lsr 8) land 0xff));
166-
Buffer.add_char buf (Char.chr ((n lsr 16) land 0xff));
167-
Buffer.add_char buf (Char.chr ((n lsr 24) land 0xff))
168-
in
169-
let write_u8 n = Buffer.add_char buf (Char.chr (n land 0xff)) in
170-
write_u32_le (List.length annots);
171-
List.iter (fun (func_idx, param_kinds, ret_kind) ->
172-
write_u32_le func_idx;
173-
write_u8 (List.length param_kinds);
174-
List.iter (fun k -> write_u8 (ownership_kind_byte k)) param_kinds;
175-
write_u8 (ownership_kind_byte ret_kind)
176-
) annots;
177-
Buffer.to_bytes buf
160+
Tw_section.Encode.ownership
161+
(List.map (fun (func_idx, param_kinds, ret_kind) ->
162+
(func_idx,
163+
List.map ownership_kind_byte param_kinds,
164+
ownership_kind_byte ret_kind))
165+
annots)
178166

179167
(** Map AffineScript type to WASM value type *)
180168
let type_to_wasm (ty : type_expr) : value_type result =

lib/dune

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
token
8383
trait
8484
tw_interface
85+
tw_section
8586
tw_verify
8687
typecheck
8788
types

lib/tw_ownership_section.ml

Lines changed: 0 additions & 96 deletions
This file was deleted.

lib/tw_section.ml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
(* SPDX-License-Identifier: MPL-2.0 *)
2+
(* SPDX-FileCopyrightText: 2026 hyperpolymath *)
3+
4+
module Encode = struct
5+
let write_u32_le buf n =
6+
Buffer.add_char buf (Char.chr (n land 0xff));
7+
Buffer.add_char buf (Char.chr ((n lsr 8) land 0xff));
8+
Buffer.add_char buf (Char.chr ((n lsr 16) land 0xff));
9+
Buffer.add_char buf (Char.chr ((n lsr 24) land 0xff))
10+
11+
let write_u8 buf n = Buffer.add_char buf (Char.chr (n land 0xff))
12+
13+
let ownership (annots : (int * int list * int) list) : bytes =
14+
if annots = [] then Bytes.empty
15+
else
16+
let buf = Buffer.create 64 in
17+
write_u32_le buf (List.length annots);
18+
List.iter (fun (func_idx, param_kind_bytes, ret_kind_byte) ->
19+
write_u32_le buf func_idx;
20+
write_u8 buf (List.length param_kind_bytes);
21+
List.iter (fun b -> write_u8 buf b) param_kind_bytes;
22+
write_u8 buf ret_kind_byte
23+
) annots;
24+
Buffer.to_bytes buf
25+
end

lib/tw_section.mli

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
(* SPDX-License-Identifier: MPL-2.0 *)
2+
(* SPDX-FileCopyrightText: 2026 hyperpolymath *)
3+
4+
(** Canonical home for typed-wasm custom-section encoders.
5+
6+
Wire encoders operate on pre-byte-encoded triples to keep this
7+
module free of any dependency on producer-side ownership types
8+
(avoiding a cycle with [Codegen]). Callers map their domain
9+
types to bytes before invoking.
10+
11+
Forward-compatibility slot for typed-wasm proposal 0001 (issue
12+
hyperpolymath/typed-wasm#34): [Encode.regions] and
13+
[Encode.capabilities] land here when the proposal promotes to
14+
[accepted]. Sharing the internal LE writers (u8 / u32le / leb128)
15+
inside this module prevents the 3-sections × 2-copies = 6-encoder
16+
fan-out flagged by hyperpolymath/affinescript#444. *)
17+
18+
module Encode : sig
19+
(** v1 wire format for [typedwasm.ownership]:
20+
u32le entry_count
21+
per entry:
22+
u32le func_index
23+
u8 param_count
24+
u8* param_kind (one per param)
25+
u8 return_kind
26+
27+
Caller responsibilities:
28+
- Map domain ownership type → byte before calling
29+
(see [Codegen.ownership_kind_byte]).
30+
- Empty input → [Bytes.empty]; caller omits the section.
31+
32+
Producer-emit is v1 per ADR-021. Parser-side v2 support lives in
33+
[Tw_verify.parse_ownership_section_payload]; emit-side flip is
34+
cross-repo-coordinated, not local to this function. *)
35+
val ownership : (int * int list * int) list -> bytes
36+
end

lib/tw_verify.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ let verify_module
210210
211211
Per ADR-021's coordinated landing protocol, this verifier ships
212212
parse-support FIRST. AffineScript's emitter ([Codegen.build_ownership_
213-
section] / [Tw_ownership_section.build_section]) stays on v1 until
213+
section] / [Tw_section.Encode.ownership]) stays on v1 until
214214
[hyperpolymath/typed-wasm]'s Rust verifier + [hyperpolymath/ephapax]'s
215215
OCaml verifier also ship parse-support; then all producers flip to v2
216216
emit together. See [docs/specs/TYPED-WASM-COORDINATION-LEDGER.adoc]

0 commit comments

Comments
 (0)