feat(codegen): emit typed WIT list from Array data types (#345)#346
Conversation
An AADL `data` type with `Data_Representation => Array` previously
degraded to an opaque `type x = list<u8>` in `spar codegen --format wit`,
dropping the element type and dimension so an inter-core contract could
only say `list`, not the real payload.
Add a `DataShape::Array { element, len }` variant. `resolve_data_shape`
now recognizes the Data Modeling Annex `Array` representation, resolves
the `Base_Type` classifier to the element shape (recursively, reusing the
scalar/record resolution) and reads the first `Dimension` as the length.
Both the list-wrapped property syntax (`Base_Type => (classifier(T))`,
`Dimension => (16)`) and the bare forms are accepted.
Codegen emits a bounded fixed-size `list<T, N>` when the dimension is
known (no `cabi_realloc`, consistent with the no_alloc discipline) and an
unbounded `list<T>` otherwise. An Array whose `Base_Type` is absent or
resolves to an unsized/unresolvable element is NOT fabricated: a top-level
port gracefully keeps the legacy opaque `list<u8>`; an array-typed record
field takes the record no_alloc hard-error path. The Rust port type
mirrors wit-bindgen's lowering (`list<T, N>` -> `[T; N]`, `list<T>` ->
`Vec<T>`).
Oracles (spar-codegen/src/wit_gen.rs): the #345 NavState case yields
`type vehicle-state = list<f32, 16>;`; unbounded -> `list<f32>`; no
Base_Type and unsized-element both stay `list<u8>`; an array-typed record
field -> `nav: list<f32, 16>`. Each asserts the emitted WIT parses and
resolves under wit-parser. REQ-CODEGEN-WIT-ARRAY-001.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI blocked by self-hosted runner disk exhaustion (infra, not this diff)Three required checks failed — Proptest (extended), Fuzz smoke, Bench compile smoke — all with the same root cause: the self-hosted runners are out of disk.
This is not related to this PR's change: it's isolated to Two runners (runner5, runner11) are both at 100% — this looks fleet-wide, which likely also explains why the Action needed (runner admin): reclaim disk on the self-hosted runner fleet (prune Generated by Claude Code |
Rivet verification gate✅ 20/20 passed
Filter: Failed artifacts(none) Updated automatically by |
No code change. The prior run's Proptest/Fuzz/Bench-compile jobs failed only because the self-hosted runners were at 100% disk (ENOSPC / linker SIGBUS on unrelated crates); those jobs are terminal on the old commit and cannot re-run via the API here. The runner fleet has since recovered (the heavy Test + Mutation jobs are building the workspace again), so this empty commit re-triggers a clean CI run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Close the rust_gen coverage gap codecov flagged on the array change: add unit tests for `data_shape_to_rust_type` on `DataShape::Array` (fixed `[T; N]`, unbounded `Vec<T>`, array-of-record `[crate::types::X; N]`, and nested `[[f32; 3]; 2]`), and for the array-element recursion in `flatten_record_rust` (a record field of array-of-record still emits the inner struct, inner-first). No behavior change. REQ-CODEGEN-WIT-ARRAY-001. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Closes the #345 gap:
spar codegen --format witmapped every AADLdatatype withData_Representation => Arrayto an opaquetype x = list<u8>, dropping the element type and dimension — so an inter-core WIT contract could only saylist, not the real payload (the jess NavState16 × f32).This teaches codegen to emit a typed WIT list from a Data Modeling Annex
Arraydata type.What changed
spar-hir-def(resolver.rs) — newDataShape::Array { element, len }.resolve_data_shaperecognizesData_Representation => Array, resolves theBase_Typeclassifier to the element shape (recursively, reusing the existing scalar/record resolution), and reads the firstDimensionas the length. Both the maintainer's list-wrapped property syntax (Base_Type => (classifier(Pkg::T)),Dimension => (16)) and the bare forms are accepted.spar-codegen(wit_gen.rs) — a fixedDimensionemits a bounded fixed-sizelist<T, N>(nocabi_realloc, consistent with the RECORDS‑002 no_alloc discipline where only unboundedlist/stringforce dynamic allocation); noDimensionemitslist<T>. The element resolves through a sharedshape_wit_typehelper, so an array-of-record and an array-typed record field both work.spar-codegen(rust_gen.rs) — the Rust port type mirrors wit-bindgen's lowering:list<T, N>→[T; N],list<T>→Vec<T>.Honest scoping / no regression
An
ArraywhoseBase_Typeis absent or resolves to an unsized/unresolvable element (e.g. the standardBase_Types::Float, which carries noData_Size) is not fabricated:array_ofreturnsNone, so a top-level array port gracefully keeps the legacylist<u8>(no regression, no hard error), and an array-typed record field takes the record no_alloc hard-error path — exactly as an opaque field would.Not claimed here:
listbyte-layout ↔ canonical-ABI equivalence (that's #327's ordeal work); multi-dimensional arrays use only the firstDimension; no compiled-crate oracle for array ports on the Rust side.Oracles (executed unit tests,
spar-codegen/src/wit_gen.rs)type vehicle-state = list<f32, 16>;type samples = list<f32>;Base_Type→type mystery = list<u8>;(negative control)type blurry = list<u8>;(graceful fallback, no error)nav: list<f32, 16>Each asserts the emitted WIT parses + resolves under
wit_parser(a real fixed-size-list bind, not a string match).cargo test -p spar-codegen --lib→ 56 passed.cargo test -p spar-hir-def --lib→ 471 passed.cargo fmt --checkclean;cargo clippyon both crates clean. An independent clean-room agent re-derived each claim against its own fixtures.Tracked as REQ-CODEGEN-WIT-ARRAY-001.
Note: case (b) of #345 (a
data implementationwith subcomponents) already works today when the port references the implementation classifier (VehicleState.nav) rather than the baredatatype — details in the issue reply.🤖 Generated with Claude Code
Generated by Claude Code