Skip to content

Commit 69a33af

Browse files
hyperpolymathclaude
andcommitted
feat(layout): add WasmGC layout proofs for cross-language ABI contracts
Layout.Types: DecEq for all three WasmGC type constructors; nullability witnesses (IsNonNull/IsNullable); distinctness proofs (string ≠ option, prim ≠ ref, prim ≠ refNull). Layout.ABI: passConvention function + three by-Refl proofs (primByValue, refByRef, refNullByRef); noAffineRefFromPassConvention proves ByAffineRef is never emitted by the pure layout layer; pureSig + pureSigNoAffineParams for building pure cross-language signatures. Layout.Stdlib: agreed layouts + nullability witnesses for List, Pair, and function references; distinctness proofs against stringLayout. typed-wasm.ipkg: register all three new modules. STATE.a2ml: cross-language layout milestone → 80%; two known open items documented (List recursive types, DecEq String propagation). No believe_me. %default total throughout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent efb5196 commit 69a33af

5 files changed

Lines changed: 349 additions & 21 deletions

File tree

.machine_readable/6a2/STATE.a2ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ maturity = "experimental" # experimental | alpha | beta | production | lts
2929
[route-to-mvp]
3030
milestones = [
3131
{ name = "Checked L1-L10 ABI core", completion = 100 },
32+
{ name = "Cross-language layout proofs (layout/ subtree)", completion = 80 },
3233
{ name = "Parser, Zig FFI, and smoke-test surface", completion = 80 },
3334
{ name = "De-template release/build metadata", completion = 15 },
3435
{ name = "Evidence-backed benchmarks and aspect coverage", completion = 10 },
@@ -40,10 +41,14 @@ issues = [
4041
"Release and container scaffolding still contains template residue outside the checked core.",
4142
"The L11-L12 research modules are not part of the checked package and currently fail standalone type-checking.",
4243
"Venue-facing paper and benchmark claims still outrun the evidence present in-repo.",
44+
"layout/Types.idr: WHT_Struct DecEq requires List (String, WasmValType) DecEq — needs DecEq String to resolve.",
45+
"layout/Stdlib.idr: listTailField uses placeholder recursive type; needs WasmGC recursive type modelling.",
4346
]
4447

4548
[critical-next-actions]
4649
actions = [
50+
"Resolve List (String, WasmValType) DecEq in Types.idr (add DecEq String + DecEq (a,b) instances or restrict to simpler equality).",
51+
"Model WasmGC recursive types properly in Layout.Types to fix List self-reference placeholder.",
4752
"Remove template residue from release, container, and packaging surfaces.",
4853
"Keep all public claims bounded to the checked L1-L10 core until the higher levels compile and are integrated.",
4954
"Add real benchmark, aspect, and end-to-end evidence before any stable or publication push.",

src/abi/layout/ABI.idr

Lines changed: 80 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,109 @@
77
--
88
-- How functions cross module boundaries between AffineScript and Ephapax:
99
--
10-
-- - Primitives (i32, f64) are passed by value
11-
-- - Heap-allocated types are passed as (ref T) — typed WasmGC references
12-
-- - Effect handlers are passed as affine capability references (transferred)
10+
-- - Primitives (i32, f64, …) are passed by value
11+
-- - Non-nullable heap references (ref T) are passed as ByRef
12+
-- - Nullable heap references (ref null T) are passed as ByRef
13+
-- (the callee is responsible for null checking)
14+
-- - Effect handlers (affine capabilities) are passed as ByAffineRef;
15+
-- ownership transfers at the call site
1316
-- - No raw pointer arithmetic at the ABI boundary
1417
--
15-
-- Status: STUB — conventions documented; formal proofs pending.
18+
-- Proofs in this module:
19+
-- * passConvention — the canonical mapping from WasmValType to PassingConvention
20+
-- * primByValue — all WVT_Prim types map to ByValue
21+
-- * refByRef — all WVT_Ref types map to ByRef
22+
-- * refNullByRef — all WVT_RefNull types map to ByRef
23+
-- * noAffineRefForPure — ByAffineRef never appears for primitive or non-affine types
24+
--
25+
-- No believe_me, assert_total, or unsafe patterns permitted.
1626

1727
module Layout.ABI
1828

1929
import Layout.Types
2030

31+
%default total
32+
2133
-- ─────────────────────────────────────────────────────────────────────────────
2234
-- Calling conventions
2335
-- ─────────────────────────────────────────────────────────────────────────────
2436

2537
||| How a source-language type is passed across a WasmGC module boundary.
2638
||| This is the agreed mapping — both consumer languages must conform.
2739
data PassingConvention
28-
= ByValue WasmValType -- primitive or small value type
29-
| ByRef WasmHeapType -- heap-allocated: passed as typed reference
40+
= ByValue WasmValType -- primitive or small value type; copied
41+
| ByRef WasmHeapType -- heap-allocated: passed as typed WasmGC reference
3042
| ByAffineRef WasmHeapType
31-
-- ^ Affine capability: ownership transferred; caller cannot use after call
43+
-- ^ Affine capability: ownership transferred; caller cannot use after call.
44+
-- Required for effect handlers (linear capabilities must not be duplicated).
3245

3346
||| A cross-language function signature at the ABI level.
3447
record CrossLangSig where
3548
constructor MkCrossLangSig
3649
params : List PassingConvention
3750
returns : List PassingConvention
38-
-- Note: WasmGC supports multi-value returns.
51+
-- Note: WasmGC supports multi-value returns natively.
52+
53+
-- ─────────────────────────────────────────────────────────────────────────────
54+
-- Canonical passing-convention function
55+
-- ─────────────────────────────────────────────────────────────────────────────
56+
57+
||| The canonical mapping from a WasmGC value type to its passing convention.
58+
|||
59+
||| Rules:
60+
||| WVT_Prim p → ByValue (WVT_Prim p) — primitives are always copied
61+
||| WVT_Ref h → ByRef h — non-nullable heap ref
62+
||| WVT_RefNull h → ByRef h — nullable heap ref (null is a valid ByRef value)
63+
|||
64+
||| `ByAffineRef` is NOT in scope of this function — it is applied by the
65+
||| *affine type system* when the source language declares the parameter as
66+
||| affine (e.g. a consumed effect handler). That annotation lives in the
67+
||| TypeLL L10 layer, not here.
68+
passConvention : WasmValType -> PassingConvention
69+
passConvention (WVT_Prim p) = ByValue (WVT_Prim p)
70+
passConvention (WVT_Ref h) = ByRef h
71+
passConvention (WVT_RefNull h) = ByRef h
72+
73+
-- ─────────────────────────────────────────────────────────────────────────────
74+
-- Proofs of the passing-convention mapping
75+
-- ─────────────────────────────────────────────────────────────────────────────
76+
77+
||| Primitives are always passed by value.
78+
primByValue : (p : WasmPrimitive) -> passConvention (WVT_Prim p) = ByValue (WVT_Prim p)
79+
primByValue _ = Refl
80+
81+
||| Non-nullable heap references are always passed by reference.
82+
refByRef : (h : WasmHeapType) -> passConvention (WVT_Ref h) = ByRef h
83+
refByRef _ = Refl
84+
85+
||| Nullable heap references are passed by reference (null is a valid typed reference).
86+
refNullByRef : (h : WasmHeapType) -> passConvention (WVT_RefNull h) = ByRef h
87+
refNullByRef _ = Refl
88+
89+
||| `passConvention` never produces `ByAffineRef`.
90+
||| Affine passing is a type-system annotation (L10), not a layout property.
91+
noAffineRefFromPassConvention
92+
: (v : WasmValType)
93+
-> passConvention v = ByAffineRef h
94+
-> Void
95+
noAffineRefFromPassConvention (WVT_Prim _) Refl impossible
96+
noAffineRefFromPassConvention (WVT_Ref _) Refl impossible
97+
noAffineRefFromPassConvention (WVT_RefNull _) Refl impossible
3998

4099
-- ─────────────────────────────────────────────────────────────────────────────
41-
-- Invariants (to be proved)
100+
-- Signature helpers
42101
-- ─────────────────────────────────────────────────────────────────────────────
43102

44-
-- TODO: prove that ByValue and ByRef are the only two cases that arise for
45-
-- pure AffineScript exports (no affine capabilities cross the boundary in
46-
-- pure-functional exports).
103+
||| Build a pure cross-language signature from WasmValType lists.
104+
||| "Pure" here means no affine capabilities: all params and returns go through
105+
||| `passConvention`, so no ByAffineRef can appear.
106+
pureSig : List WasmValType -> List WasmValType -> CrossLangSig
107+
pureSig ins outs = MkCrossLangSig (map passConvention ins) (map passConvention outs)
47108

48-
-- TODO: prove that ByAffineRef is required for effect handlers (they are
49-
-- capabilities that must not be duplicated).
109+
||| A pure signature never contains ByAffineRef in its parameters.
110+
pureSigNoAffineParams
111+
: (ins outs : List WasmValType)
112+
-> All (\c => Not (c = ByAffineRef h)) (params (pureSig ins outs))
113+
pureSigNoAffineParams [] _ = []
114+
pureSigNoAffineParams (v :: vs) outs =
115+
noAffineRefFromPassConvention v :: pureSigNoAffineParams vs outs

src/abi/layout/Stdlib.idr

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
-- SPDX-License-Identifier: PMPL-1.0-or-later
2+
-- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
--
4+
-- src/abi/layout/Stdlib.idr
5+
--
6+
-- Agreed WasmGC layouts for standard library types shared between all
7+
-- consumer languages (AffineScript, Ephapax, and any future WasmGC target).
8+
--
9+
-- Every agreed layout in this module comes with:
10+
-- 1. The concrete WasmValType / WasmHeapType definition
11+
-- 2. A nullability witness (IsNonNull or IsNullable from Layout.Types)
12+
-- 3. A tag-field proof where relevant (tagged-union types)
13+
--
14+
-- Adding a new shared type requires:
15+
-- a. Define the layout
16+
-- b. Add a nullability witness
17+
-- c. Add distinctness proofs against existing layouts (no accidental aliasing)
18+
-- d. Open a PR on nextgen-languages/affinescript and nextgen-languages/ephapax
19+
-- to confirm both compilers adopt the new layout
20+
--
21+
-- No believe_me, assert_total, or unsafe patterns permitted.
22+
23+
module Layout.Stdlib
24+
25+
import Layout.Types
26+
27+
%default total
28+
29+
-- ─────────────────────────────────────────────────────────────────────────────
30+
-- List T
31+
-- ─────────────────────────────────────────────────────────────────────────────
32+
--
33+
-- Encoding: (ref (struct (field T-or-null) (field (ref null self))))
34+
-- i.e. a linked-list node where:
35+
-- field 0 "head": (ref T) — the element (non-nullable)
36+
-- field 1 "tail": (ref null List) — the rest, nullable (null = empty list)
37+
--
38+
-- Note: this encoding uses WasmGC recursive types. The `self` reference
39+
-- is represented here by an empty struct placeholder until WasmGC recursive
40+
-- types are modelled in this Idris2 framework.
41+
42+
||| The element slot in a List node.
43+
||| For a List of elements of heap type `elem`, the head field holds a (ref elem).
44+
listHeadField : WasmHeapType -> (String, WasmValType)
45+
listHeadField elem = ("head", WVT_Ref elem)
46+
47+
||| The tail slot in a List node — nullable because the empty list is null.
48+
||| The tail heap type is a placeholder empty struct; in real WasmGC this would
49+
||| be a recursive self-reference to the list struct type.
50+
listTailField : (String, WasmValType)
51+
listTailField = ("tail", WVT_RefNull (WHT_Struct []))
52+
-- Note: replace the placeholder WHT_Struct [] with the actual recursive type
53+
-- when WasmGC recursive references are modelled.
54+
55+
||| The agreed WasmGC heap type for `List elem`.
56+
listHeapType : WasmHeapType -> WasmHeapType
57+
listHeapType elem = WHT_Struct [listHeadField elem, listTailField]
58+
59+
||| The agreed WasmGC value type for `List elem` — a non-nullable struct reference.
60+
||| Empty lists are represented as the null tail of a containing node, not as
61+
||| a nullable `List` reference itself. This matches Ephapax's linear list design.
62+
listLayout : WasmHeapType -> WasmValType
63+
listLayout elem = WVT_Ref (listHeapType elem)
64+
65+
||| List values are non-nullable references.
66+
listLayoutNonNull : (elem : WasmHeapType) -> IsNonNull (listLayout elem)
67+
listLayoutNonNull _ = RefIsNonNull
68+
69+
-- ─────────────────────────────────────────────────────────────────────────────
70+
-- Tuple (pairs and n-tuples)
71+
-- ─────────────────────────────────────────────────────────────────────────────
72+
--
73+
-- Encoding: (ref (struct (field T1) (field T2) …))
74+
-- All fields are non-nullable; tuples are always fully initialised.
75+
76+
||| The agreed WasmGC heap type for a pair (T1, T2).
77+
pairHeapType : WasmValType -> WasmValType -> WasmHeapType
78+
pairHeapType t1 t2 = WHT_Struct [("fst", t1), ("snd", t2)]
79+
80+
||| The agreed WasmGC value type for a pair.
81+
pairLayout : WasmValType -> WasmValType -> WasmValType
82+
pairLayout t1 t2 = WVT_Ref (pairHeapType t1 t2)
83+
84+
||| Pair values are non-nullable.
85+
pairLayoutNonNull : (t1 t2 : WasmValType) -> IsNonNull (pairLayout t1 t2)
86+
pairLayoutNonNull _ _ = RefIsNonNull
87+
88+
-- ─────────────────────────────────────────────────────────────────────────────
89+
-- Function references
90+
-- ─────────────────────────────────────────────────────────────────────────────
91+
--
92+
-- Encoding: (ref (func (param T1 … Tn) (result R1 … Rm)))
93+
-- WasmGC function references are typed by their signature.
94+
95+
||| The agreed WasmGC value type for a function with given param/result types.
96+
funcRefLayout : List WasmValType -> List WasmValType -> WasmValType
97+
funcRefLayout params results = WVT_Ref (WHT_Func params results)
98+
99+
||| Function references are non-nullable.
100+
funcRefLayoutNonNull
101+
: (ps rs : List WasmValType)
102+
-> IsNonNull (funcRefLayout ps rs)
103+
funcRefLayoutNonNull _ _ = RefIsNonNull
104+
105+
-- ─────────────────────────────────────────────────────────────────────────────
106+
-- Distinctness: stdlib types are pairwise distinct from core layouts
107+
-- ─────────────────────────────────────────────────────────────────────────────
108+
109+
||| List layouts are distinct from String layout (different element types and structure).
110+
listNotString : (elem : WasmHeapType) -> listLayout elem = stringLayout -> Void
111+
listNotString _ Refl impossible
112+
113+
||| Pair layouts are distinct from String layout.
114+
pairNotString : (t1 t2 : WasmValType) -> pairLayout t1 t2 = stringLayout -> Void
115+
pairNotString _ _ Refl impossible
116+
117+
||| Function-reference layouts are distinct from String layout.
118+
funcRefNotString : (ps rs : List WasmValType) -> funcRefLayout ps rs = stringLayout -> Void
119+
funcRefNotString _ _ Refl impossible

0 commit comments

Comments
 (0)