Skip to content

Commit c896a44

Browse files
hyperpolymathclaude
andcommitted
fix(layout): restore Layout.* aggregate modules to full ipkg build
Unblocks the aggregate-library layout contracts that were gated out of typed-wasm.ipkg in the previous commit, after resolving the 11 Idris2 0.8 compatibility issues catalogued in the Layout-fix entry of PROOF-NEEDS.md (mutual recursion, visibility, imports, nested `with` pattern unification, Refl-impossible reduction, auto-bound-implicit shadowing, `(x y : T)` binder syntax, `prefix` reserved keyword, non-linear SubStruct transitivity, `Bool`-vs-`Type` predicate mix, and over-strong WasmGCEq constructor). All 21 modules now build cleanly under Idris2 0.8.0-712523a89 with zero believe_me / assert_total / postulate / sorry; %default total preserved throughout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a01edf3 commit c896a44

8 files changed

Lines changed: 456 additions & 223 deletions

File tree

PROOF-NEEDS.md

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -483,48 +483,63 @@ After each session: run `idris2 --check` on every file in
483483
adjacent code (no new unsafe code should land), update this file's
484484
inventory table, commit.
485485

486-
## Outstanding infrastructure work — Layout module fix (added 2026-04-18)
487-
488-
`Layout/Types.idr` was never compiling cleanly under this Idris2 0.8
489-
build. The directory rename `layout/``Layout/` (filesystem case
490-
matching the `Layout.*` module names) and the `import layout.Types`
491-
`import Layout.Types` typo fix in `TypedWasm/ABI/Layout.idr` were
492-
made on 2026-04-18, which surfaced the underlying issues:
493-
494-
1. **Mutual recursion not declared.** `WasmHeapType` references
495-
`WasmValType` (line 65) but `WasmValType` is declared later
496-
(line 73). Wrapping both in `mutual` is needed.
497-
2. **Visibility annotations missing.** `data WasmPrimitive` /
498-
`WasmHeapType` / `WasmValType` and the layout values
499-
(`stringLayout`, `optionLayout`, `resultLayout`, `enumLayout`,
500-
`recordLayout`) all lack `public export`, so downstream
501-
constructors are not visible and `Refl`-based proofs do not
502-
unify.
503-
3. **Imports missing.** `Decidable.Equality` is used (`DecEq`,
504-
`Yes`, `No`, `decEq`) but never imported; `Data.List` similarly.
505-
4. **Nested `with` block patterns** in the `DecEq WasmHeapType`
506-
instance (lines 178-234) trigger pattern-variable unification
507-
errors under this Idris2 build.
508-
5. **Refl-impossible patterns** (`stringOptionDistinct _ Refl
509-
impossible` etc.) need the layout values to reduce at unification
510-
time — even with `public export` the reduction does not happen
511-
under this build, so a different proof strategy is needed
512-
(probably explicit `case ... of` with constructor mismatch).
513-
514-
**Current gating (2026-04-18):** the four `Layout.*` modules are
515-
commented out of `typed-wasm.ipkg` and the `import TypedWasm.ABI.Layout`
516-
line in `TypedWasm/ABI/Proofs.idr` is gated. This unblocks the full
517-
ipkg build for the typed-wasm core (TypedWasm.ABI.* modules) — which
518-
is the primary purpose of typed-wasm. The aggregate-library Layout
519-
contracts remain in the tree for restoration once the issues above
520-
are addressed.
521-
522-
**Remediation plan:** add `public export` to every Layout type/value;
523-
add `import Decidable.Equality, Data.List`; rewrite the `DecEq
524-
WasmHeapType` and `DecEq WasmValType` instances without nested `with`
525-
(use case-of or explicit witness construction); replace the
526-
Refl-impossible patterns with `case ... of` + constructor analysis.
527-
Estimated effort: one focused session.
486+
## Outstanding infrastructure work — Layout module fix ✅ DONE 2026-04-18
487+
488+
**Status:** resolved in the same session it was identified. All four
489+
`Layout.*` modules plus the `TypedWasm.ABI.Layout` bridge are back in
490+
`typed-wasm.ipkg`, the `import TypedWasm.ABI.Layout` is restored in
491+
`TypedWasm/ABI/Proofs.idr`, and the full 21-module ipkg builds cleanly
492+
under Idris2 0.8.0-712523a89.
493+
494+
**Fixes applied, by issue:**
495+
496+
1. **Mutual recursion not declared** — wrapped `WasmHeapType` and
497+
`WasmValType` in a single `mutual` block (and separately
498+
`WasmValTypeValid` / `WasmGCLayoutValid` in `TypedWasm.ABI.Layout`).
499+
2. **Visibility annotations missing** — added `public export` to every
500+
data type, constructor, function, layout value, and `DecEq` / data
501+
instance in `Layout/Types.idr`, `Layout/ABI.idr`, `Layout/Stdlib.idr`,
502+
`Layout/AirborneSubmarineSquadron.idr`, and `TypedWasm/ABI/Layout.idr`.
503+
3. **Imports missing** — added `Decidable.Equality` + `Data.List` to
504+
`Layout/Types.idr`; `Data.List.Quantifiers` to `TypedWasm.ABI.Layout`;
505+
`Data.List` to `Layout/AirborneSubmarineSquadron.idr`.
506+
4. **Nested `with` block patterns** — rewrote the `DecEq WasmHeapType`
507+
and `DecEq WasmValType` instances as four plain mutual functions
508+
(`decEqHT`, `decEqVT`, `decEqVTList`, `decEqFields`) with thin
509+
non-mutual interface wrappers. This sidesteps Idris2 0.8's
510+
interface-resolution chain through `List (String, WasmValType)`.
511+
5. **Refl-impossible patterns** — replaced `LHS impossible` clauses
512+
with `prf = case prf of Refl impossible`. The scope change lets
513+
Idris2 reduce the layout values after they see `public export`.
514+
6. **Auto-bound implicit shadowing** — lowercase references to
515+
`stringLayout`, `resultLayout`, etc. inside type signatures were
516+
being treated as implicit pattern variables that shadowed the real
517+
definitions. Fully qualifying as `Layout.Types.stringLayout` (and
518+
friends) suppresses the auto-bind and lets the definitions reduce.
519+
7. **`(x y : T)` binder syntax** — Idris2 0.8 prefers comma-separated
520+
`(x, y : T)`; updated all affected binders.
521+
8. **`prefix` is reserved** — renamed `SubStruct`'s `prefix` argument
522+
to `pre` (operator fixity keyword in Idris2 0.8).
523+
9. **Non-linear SubStruct transitivity pattern** — rewrote
524+
`data Subtype`'s `SubStruct` constructor to carry an explicit
525+
equality witness `prf : fs = pre ++ rest`, then proved
526+
`subTrans` for the SubStruct/SubStruct case using `trans`, `cong`,
527+
and `sym appendAssociative`. (The direction of
528+
`appendAssociative : l ++ (c ++ r) = (l ++ c) ++ r` needs `sym` to
529+
rearrange `(pre ++ rest1) ++ rest2` into `pre ++ (rest1 ++ rest2)`.)
530+
10. **`WasmGCLayoutValid` struct-field predicate** — replaced the
531+
`case vt of … => True` predicate (which mixed `Bool` with `Type`)
532+
with a proper `WasmValTypeValid` inductive in a mutual block.
533+
11. **`WasmGCEq` constructor** — replaced the over-strong
534+
`MkGCEq : decEq h1 h2 = Yes Refl -> WasmGCEq h1 h2` with the
535+
simpler propositional form `MkGCEq : h1 = h2 -> WasmGCEq h1 h2`.
536+
The original encoding required a non-trivial theorem about `decEq`
537+
to construct a reflexive witness.
538+
539+
**Net result:** the aggregate-library Layout contracts (the secondary
540+
purpose of typed-wasm, per ADR-004) are back in the ipkg alongside
541+
the typed-wasm core, with zero `believe_me` / `assert_total` /
542+
`postulate` / `sorry`. `%default total` preserved throughout.
528543

529544
## Pre-existing notes (preserved from prior revision)
530545

src/abi/Layout/ABI.idr

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
module Layout.ABI
2828

2929
import Layout.Types
30+
import Data.List.Quantifiers
3031

3132
%default total
3233

@@ -36,6 +37,7 @@ import Layout.Types
3637

3738
||| How a source-language type is passed across a WasmGC module boundary.
3839
||| This is the agreed mapping — both consumer languages must conform.
40+
public export
3941
data PassingConvention
4042
= ByValue WasmValType -- primitive or small value type; copied
4143
| ByRef WasmHeapType -- heap-allocated: passed as typed WasmGC reference
@@ -44,6 +46,7 @@ data PassingConvention
4446
-- Required for effect handlers (linear capabilities must not be duplicated).
4547

4648
||| A cross-language function signature at the ABI level.
49+
public export
4750
record CrossLangSig where
4851
constructor MkCrossLangSig
4952
params : List PassingConvention
@@ -65,6 +68,7 @@ record CrossLangSig where
6568
||| *affine type system* when the source language declares the parameter as
6669
||| affine (e.g. a consumed effect handler). That annotation lives in the
6770
||| TypeLL L10 layer, not here.
71+
public export
6872
passConvention : WasmValType -> PassingConvention
6973
passConvention (WVT_Prim p) = ByValue (WVT_Prim p)
7074
passConvention (WVT_Ref h) = ByRef h
@@ -75,26 +79,31 @@ passConvention (WVT_RefNull h) = ByRef h
7579
-- ─────────────────────────────────────────────────────────────────────────────
7680

7781
||| Primitives are always passed by value.
82+
public export
7883
primByValue : (p : WasmPrimitive) -> passConvention (WVT_Prim p) = ByValue (WVT_Prim p)
7984
primByValue _ = Refl
8085

8186
||| Non-nullable heap references are always passed by reference.
87+
public export
8288
refByRef : (h : WasmHeapType) -> passConvention (WVT_Ref h) = ByRef h
8389
refByRef _ = Refl
8490

8591
||| Nullable heap references are passed by reference (null is a valid typed reference).
92+
public export
8693
refNullByRef : (h : WasmHeapType) -> passConvention (WVT_RefNull h) = ByRef h
8794
refNullByRef _ = Refl
8895

8996
||| `passConvention` never produces `ByAffineRef`.
9097
||| Affine passing is a type-system annotation (L10), not a layout property.
98+
public export
9199
noAffineRefFromPassConvention
92100
: (v : WasmValType)
101+
-> {h : WasmHeapType}
93102
-> passConvention v = ByAffineRef h
94103
-> Void
95-
noAffineRefFromPassConvention (WVT_Prim _) Refl impossible
96-
noAffineRefFromPassConvention (WVT_Ref _) Refl impossible
97-
noAffineRefFromPassConvention (WVT_RefNull _) Refl impossible
104+
noAffineRefFromPassConvention (WVT_Prim _) prf = case prf of Refl impossible
105+
noAffineRefFromPassConvention (WVT_Ref _) prf = case prf of Refl impossible
106+
noAffineRefFromPassConvention (WVT_RefNull _) prf = case prf of Refl impossible
98107

99108
-- ─────────────────────────────────────────────────────────────────────────────
100109
-- Signature helpers
@@ -103,13 +112,22 @@ noAffineRefFromPassConvention (WVT_RefNull _) Refl impossible
103112
||| Build a pure cross-language signature from WasmValType lists.
104113
||| "Pure" here means no affine capabilities: all params and returns go through
105114
||| `passConvention`, so no ByAffineRef can appear.
115+
public export
106116
pureSig : List WasmValType -> List WasmValType -> CrossLangSig
107117
pureSig ins outs = MkCrossLangSig (map passConvention ins) (map passConvention outs)
108118

109119
||| A pure signature never contains ByAffineRef in its parameters.
120+
|||
121+
||| The original pureSigNoAffineParams used an auto-bound implicit `h` that
122+
||| quantified at the wrong level (it became part of each list element's
123+
||| predicate rather than universally scoped over the whole list). The fix is
124+
||| to bind the predicate concretely against *any* heap type the element might
125+
||| match, using an inner forall in the predicate itself.
126+
public export
110127
pureSigNoAffineParams
111-
: (ins outs : List WasmValType)
112-
-> All (\c => Not (c = ByAffineRef h)) (params (pureSig ins outs))
128+
: (ins, outs : List WasmValType)
129+
-> All (\c => (h : WasmHeapType) -> Not (c = ByAffineRef h))
130+
(params (Layout.ABI.pureSig ins outs))
113131
pureSigNoAffineParams [] _ = []
114132
pureSigNoAffineParams (v :: vs) outs =
115-
noAffineRefFromPassConvention v :: pureSigNoAffineParams vs outs
133+
(\h => noAffineRefFromPassConvention v {h}) :: pureSigNoAffineParams vs outs

src/abi/Layout/AirborneSubmarineSquadron.idr

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,27 @@ module Layout.AirborneSubmarineSquadron
1616

1717
import Layout.Types
1818
import Layout.ABI
19+
import Data.List
1920

2021
%default total
2122

23+
public export
2224
snapshotAbiVersion : String
2325
snapshotAbiVersion = "airborne-submarine-squadron.step_state.v1"
2426

27+
public export
2528
snapshotFieldCount : Nat
2629
snapshotFieldCount = 29
2730

31+
public export
2832
inputFieldCount : Nat
2933
inputFieldCount = 5
3034

35+
public export
3136
stepStateArity : Nat
3237
stepStateArity = snapshotFieldCount + inputFieldCount
3338

39+
public export
3440
snapshotFieldNames : List String
3541
snapshotFieldNames =
3642
[ "tick"
@@ -64,6 +70,7 @@ snapshotFieldNames =
6470
, "mission_failed"
6571
]
6672

73+
public export
6774
inputFieldNames : List String
6875
inputFieldNames =
6976
[ "thrust_x"
@@ -73,35 +80,54 @@ inputFieldNames =
7380
, "toggle_env"
7481
]
7582

76-
snapshotFieldCountProof : length snapshotFieldNames = snapshotFieldCount
83+
public export
84+
snapshotFieldCountProof
85+
: length Layout.AirborneSubmarineSquadron.snapshotFieldNames
86+
= Layout.AirborneSubmarineSquadron.snapshotFieldCount
7787
snapshotFieldCountProof = Refl
7888

79-
inputFieldCountProof : length inputFieldNames = inputFieldCount
89+
public export
90+
inputFieldCountProof
91+
: length Layout.AirborneSubmarineSquadron.inputFieldNames
92+
= Layout.AirborneSubmarineSquadron.inputFieldCount
8093
inputFieldCountProof = Refl
8194

95+
public export
8296
snapshotResultHeap : WasmHeapType
8397
snapshotResultHeap = WHT_Array (WVT_Prim WasmI32)
8498

99+
public export
85100
snapshotResultConvention : PassingConvention
86101
snapshotResultConvention = ByRef snapshotResultHeap
87102

103+
public export
88104
i32Convention : PassingConvention
89105
i32Convention = ByValue (WVT_Prim WasmI32)
90106

107+
public export
91108
stepStateParams : List PassingConvention
92109
stepStateParams = replicate stepStateArity i32Convention
93110

94-
stepStateArityProof : length stepStateParams = stepStateArity
111+
public export
112+
stepStateArityProof
113+
: length Layout.AirborneSubmarineSquadron.stepStateParams
114+
= Layout.AirborneSubmarineSquadron.stepStateArity
95115
stepStateArityProof = Refl
96116

117+
public export
97118
initStateSig : CrossLangSig
98119
initStateSig = MkCrossLangSig [] [snapshotResultConvention]
99120

121+
public export
100122
stepStateSig : CrossLangSig
101123
stepStateSig = MkCrossLangSig stepStateParams [snapshotResultConvention]
102124

103-
initStateNoParams : params initStateSig = []
125+
public export
126+
initStateNoParams : params Layout.AirborneSubmarineSquadron.initStateSig = []
104127
initStateNoParams = Refl
105128

106-
stepStateReturnsSnapshot : returns stepStateSig = [snapshotResultConvention]
129+
public export
130+
stepStateReturnsSnapshot
131+
: returns Layout.AirborneSubmarineSquadron.stepStateSig
132+
= [Layout.AirborneSubmarineSquadron.snapshotResultConvention]
107133
stepStateReturnsSnapshot = Refl

0 commit comments

Comments
 (0)