Skip to content

Commit d2f563d

Browse files
committed
refactor(codegen): extract affinescript.ownership emission to lib/tw_ownership_section.ml
Tranche A3 of docs/specs/TYPED-WASM-ROADMAP.adoc. Behaviour-preserving move; downstream callers (lib/tw_verify.ml, lib/tw_interface.ml, test/test_e2e.ml) are unaffected. Why now: A3 was specified as a prerequisite for B1 (ADR-020 v2-parse support, now ACCEPTED + RATIFIED 2026-05-24). Putting the on-wire format in a dedicated module is cheap now and saves us editing codegen.ml inline when v2-parse + v2-emit land. Changes: - NEW lib/tw_ownership_section.ml — the dedicated home for: * type ownership_kind (Unrestricted/Linear/SharedBorrow/ExclBorrow) * ownership_kind_of_param (extracts from p_ownership or type shape) * ownership_kind_of_ret * ownership_kind_byte (0..3 encoding) * build_section (v1 binary serialiser) - MOD lib/codegen.ml — re-exports preserve the [Codegen.X] surface: * type ownership_kind = Tw_ownership_section.ownership_kind = | ... (type equation keeps Codegen.Linear etc. resolving as constructors so `open Codegen` in Tw_verify / Tw_interface / Test_e2e continues to work) * `let ownership_kind_of_param = Tw_ownership_section.…` and friends — pure aliases. * `let build_ownership_section = Tw_ownership_section.build_section` — alias preserving the prior public name. No semantic change. The v1 binary format and all enforcement (L7 aliasing + L10 linearity + L13 isolation) are unchanged. The 260+ test baseline should pass byte-for-byte; in particular the ownership-section round-trip cases (test_ownership_section_present / _roundtrip / _entry_count) test the exact same serialisation path. Not yet verified locally — no OCaml toolchain in this remote execution environment. CI is the verification surface; the change is mechanical and the type-equation re-export pattern is a standard OCaml idiom. Refs ADR-020, docs/specs/TYPED-WASM-ROADMAP.adoc §"Tranche A — A3"
1 parent d4522e4 commit d2f563d

2 files changed

Lines changed: 122 additions & 55 deletions

File tree

lib/codegen.ml

Lines changed: 26 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,17 @@ open Ast
1111
open Wasm
1212

1313
(** Ownership kind for typed-wasm schema annotations.
14-
Maps AffineScript ownership qualifiers to typed-wasm Level 7/10 verification. *)
15-
type ownership_kind =
16-
| Unrestricted (** Plain value, no ownership constraint (Wasm i32/f64 etc.) *)
17-
| Linear (** TyOwn / own — consumed exactly once (typed-wasm Level 10 linearity) *)
18-
| SharedBorrow (** TyRef / ref — read-only aliasing safety (typed-wasm Level 7) *)
19-
| ExclBorrow (** TyMut / mut — exclusive mutable aliasing safety (typed-wasm Level 7) *)
14+
15+
Re-exported from [Tw_ownership_section] (the dedicated home,
16+
extracted 2026-05-24 per A3 of TYPED-WASM-ROADMAP.adoc). The
17+
type equation preserves constructor accessibility so
18+
[Codegen.Linear] etc. continue to resolve and [open Codegen]
19+
in [Tw_verify] / [Tw_interface] / [Test_e2e] is unaffected. *)
20+
type ownership_kind = Tw_ownership_section.ownership_kind =
21+
| Unrestricted
22+
| Linear
23+
| SharedBorrow
24+
| ExclBorrow
2025

2126
(** Code generation context *)
2227
type context = {
@@ -111,19 +116,9 @@ let create_context () : context = {
111116
wasi_func_indices = [];
112117
}
113118

114-
(** Extract ownership kind from a parameter declaration.
115-
Checks p_ownership first; falls back to the shape of p_ty. *)
116-
let ownership_kind_of_param (p : param) : ownership_kind =
117-
match p.p_ownership with
118-
| Some Own -> Linear
119-
| Some Ref -> SharedBorrow
120-
| Some Mut -> ExclBorrow
121-
| None ->
122-
match p.p_ty with
123-
| TyOwn _ -> Linear
124-
| TyRef _ -> SharedBorrow
125-
| TyMut _ -> ExclBorrow
126-
| _ -> Unrestricted
119+
(** Extract ownership kind from a parameter declaration. Re-exported
120+
from [Tw_ownership_section] (A3 refactor, 2026-05-24). *)
121+
let ownership_kind_of_param = Tw_ownership_section.ownership_kind_of_param
127122

128123
(** If [ty] names a known struct (through any number of own/ref/mut wrappers),
129124
return that struct's name. Lets us recover a struct's field layout from
@@ -136,45 +131,21 @@ let rec struct_name_of_ty (ty : type_expr) : string option =
136131
| TyOwn inner | TyRef inner | TyMut inner -> struct_name_of_ty inner
137132
| _ -> None
138133

139-
(** Extract ownership kind from an optional return type expression *)
140-
let ownership_kind_of_ret (ret : type_expr option) : ownership_kind =
141-
match ret with
142-
| Some (TyOwn _) -> Linear
143-
| Some (TyRef _) -> SharedBorrow
144-
| Some (TyMut _) -> ExclBorrow
145-
| _ -> Unrestricted
134+
(** Extract ownership kind from an optional return type expression.
135+
Re-exported from [Tw_ownership_section]. *)
136+
let ownership_kind_of_ret = Tw_ownership_section.ownership_kind_of_ret
146137

147-
(** Encode an ownership_kind as a single byte (0–3) *)
148-
let ownership_kind_byte = function
149-
| Unrestricted -> 0 | Linear -> 1 | SharedBorrow -> 2 | ExclBorrow -> 3
138+
(** Encode an ownership_kind as a single byte (0–3).
139+
Re-exported from [Tw_ownership_section]. *)
140+
let ownership_kind_byte = Tw_ownership_section.ownership_kind_byte
150141

151142
(** Build the payload for the [affinescript.ownership] Wasm custom section.
152-
Encoding (all little-endian):
153-
u32 entry_count
154-
per entry:
155-
u32 func_index
156-
u8 param_count
157-
u8* param_kind (one per param, see kind encoding above)
158-
u8 return_kind *)
159-
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
143+
Re-exported from [Tw_ownership_section.build_section] (A3 refactor,
144+
2026-05-24). The dedicated module is the home for the on-wire format;
145+
this alias preserves the [Codegen.build_ownership_section] public
146+
API surface that downstream callers (lib/tw_verify.ml,
147+
lib/tw_interface.ml, test/test_e2e.ml) rely on. *)
148+
let build_ownership_section = Tw_ownership_section.build_section
178149

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

lib/tw_ownership_section.ml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
(* SPDX-License-Identifier: MPL-2.0 *)
2+
(* SPDX-FileCopyrightText: 2026 hyperpolymath *)
3+
(*
4+
* tw_ownership_section.ml — the dedicated home for the
5+
* `affinescript.ownership` Wasm custom section: kind encoding,
6+
* extraction from the AST, and binary serialisation.
7+
*
8+
* Extracted from lib/codegen.ml at 2026-05-24 per Tranche A3 of
9+
* docs/specs/TYPED-WASM-ROADMAP.adoc. Behaviour-preserving move.
10+
*
11+
* The format itself is specified in
12+
* docs/specs/TYPED-WASM-INTERFACE.adoc (v1, current) and
13+
* docs/specs/TYPED-WASM-ROADMAP.adoc §"Tranche B" (v2, the
14+
* ratified ADR-020 widening; landed via [Tw_verify] parse-support
15+
* first, with the producer-emit flip deferred to the coordinated
16+
* landing window per ADR-021 — see TYPED-WASM-COORDINATION-LEDGER.adoc
17+
* Q-001).
18+
*
19+
* Backwards compatibility: [Codegen] re-exports the same names so
20+
* `open Codegen` continues to expose [ownership_kind] and friends —
21+
* downstream callers ([Tw_verify], [Tw_interface], [Test_e2e]) need
22+
* no change.
23+
*)
24+
25+
open Ast
26+
27+
(** Ownership kind for typed-wasm schema annotations.
28+
Maps AffineScript ownership qualifiers to typed-wasm Level 7/10
29+
verification. *)
30+
type ownership_kind =
31+
| Unrestricted (** Plain value, no ownership constraint (Wasm i32/f64 etc.) *)
32+
| Linear (** TyOwn / own — consumed exactly once (typed-wasm Level 10) *)
33+
| SharedBorrow (** TyRef / ref — read-only aliasing safety (typed-wasm Level 7) *)
34+
| ExclBorrow (** TyMut / mut — exclusive mutable aliasing safety (typed-wasm Level 7) *)
35+
36+
(** Extract ownership kind from a parameter declaration.
37+
Checks [p.p_ownership] first; falls back to the shape of [p.p_ty]. *)
38+
let ownership_kind_of_param (p : param) : ownership_kind =
39+
match p.p_ownership with
40+
| Some Own -> Linear
41+
| Some Ref -> SharedBorrow
42+
| Some Mut -> ExclBorrow
43+
| None ->
44+
match p.p_ty with
45+
| TyOwn _ -> Linear
46+
| TyRef _ -> SharedBorrow
47+
| TyMut _ -> ExclBorrow
48+
| _ -> Unrestricted
49+
50+
(** Extract ownership kind from an optional return type expression *)
51+
let ownership_kind_of_ret (ret : type_expr option) : ownership_kind =
52+
match ret with
53+
| Some (TyOwn _) -> Linear
54+
| Some (TyRef _) -> SharedBorrow
55+
| Some (TyMut _) -> ExclBorrow
56+
| _ -> Unrestricted
57+
58+
(** Encode an [ownership_kind] as a single byte (0..3). *)
59+
let ownership_kind_byte = function
60+
| Unrestricted -> 0
61+
| Linear -> 1
62+
| SharedBorrow -> 2
63+
| ExclBorrow -> 3
64+
65+
(** Build the payload for the [affinescript.ownership] Wasm custom section.
66+
67+
v1 encoding (current emit; LE):
68+
u32 entry_count
69+
per entry:
70+
u32 func_index
71+
u8 param_count
72+
u8* param_kind (one per param, see kind encoding above)
73+
u8 return_kind
74+
75+
Returns [Bytes.empty] when there are no annotations so the
76+
caller can omit the section entirely. *)
77+
let build_section
78+
(annots : (int * ownership_kind list * ownership_kind) list) : bytes =
79+
if annots = [] then Bytes.empty
80+
else
81+
let buf = Buffer.create 64 in
82+
let write_u32_le n =
83+
Buffer.add_char buf (Char.chr (n land 0xff));
84+
Buffer.add_char buf (Char.chr ((n lsr 8) land 0xff));
85+
Buffer.add_char buf (Char.chr ((n lsr 16) land 0xff));
86+
Buffer.add_char buf (Char.chr ((n lsr 24) land 0xff))
87+
in
88+
let write_u8 n = Buffer.add_char buf (Char.chr (n land 0xff)) in
89+
write_u32_le (List.length annots);
90+
List.iter (fun (func_idx, param_kinds, ret_kind) ->
91+
write_u32_le func_idx;
92+
write_u8 (List.length param_kinds);
93+
List.iter (fun k -> write_u8 (ownership_kind_byte k)) param_kinds;
94+
write_u8 (ownership_kind_byte ret_kind)
95+
) annots;
96+
Buffer.to_bytes buf

0 commit comments

Comments
 (0)