Skip to content

Commit efe6f2c

Browse files
Merge branch 'main' into claude/coordinator-feedback
2 parents 8310d34 + 3d4f495 commit efe6f2c

3 files changed

Lines changed: 141 additions & 63 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/interp.ml

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,14 +1027,25 @@ let create_initial_env () : env =
10271027
let eval_decl (env : env) (decl : top_level) : env result =
10281028
match decl with
10291029
| TopFn fd ->
1030-
let closure = VClosure {
1031-
cl_params = fd.fd_params;
1032-
cl_body = (match fd.fd_body with
1033-
| FnBlock blk -> ExprBlock blk
1034-
| FnExpr e -> e);
1035-
cl_env = env;
1036-
} in
1037-
Ok (extend_env fd.fd_name.name closure env)
1030+
(match fd.fd_body with
1031+
| FnExtern ->
1032+
(* Externs have no AST body to evaluate. Their runtime binding is
1033+
provided by [create_initial_env]'s builtin table (panic, error,
1034+
make_ref, …). Skip here so an inline `extern fn` declaration in
1035+
a test source (e.g. the STDLIB-04a Mut round-trip tests) doesn't
1036+
blow up the [FnBlock|FnExpr] match below.
1037+
Refs #328 root-cause for the interp pattern-match-failure. *)
1038+
Ok env
1039+
| FnBlock _ | FnExpr _ ->
1040+
let closure = VClosure {
1041+
cl_params = fd.fd_params;
1042+
cl_body = (match fd.fd_body with
1043+
| FnBlock blk -> ExprBlock blk
1044+
| FnExpr e -> e
1045+
| FnExtern -> assert false (* unreachable: outer match guards *));
1046+
cl_env = env;
1047+
} in
1048+
Ok (extend_env fd.fd_name.name closure env))
10381049

10391050
| TopConst tc ->
10401051
let* v = eval env tc.tc_value in

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)