Skip to content

Add Component Model composite ValType wrappers: list / record / tuple / enum / flags#283

Open
glassmonkey wants to merge 6 commits into
bytecodealliance:mainfrom
glassmonkey:component-composite-types
Open

Add Component Model composite ValType wrappers: list / record / tuple / enum / flags#283
glassmonkey wants to merge 6 commits into
bytecodealliance:mainfrom
glassmonkey:component-composite-types

Conversation

@glassmonkey
Copy link
Copy Markdown
Contributor

Continues the type-hierarchy work from #282 by exposing payload accessors for the product/enum composite kinds (list, record, tuple, enum, flags). Follow-up to #282 (merged). Tracks #280.

Summary

Each composite kind ships as a Go wrapper type with count/nth-style accessors mirroring the underlying C API:

  • ComponentListType (Element)
  • ComponentRecordType (FieldCount / FieldNth)
  • ComponentTupleType (TypeCount / TypeNth)
  • ComponentEnumType (CaseCount / CaseNth)
  • ComponentFlagsType (FlagCount / FlagNth)

For enum/flags, the C API's generic names_count / names_nth are renamed to the WIT-domain terms (case, flag); record/tuple already use WIT terms (field / type).

ComponentValType gains a downcast method per kind (List, Record, Tuple, Enum, Flags) returning nil for any other kind. The corresponding five ComponentValTypeKind* constants — commented out in #282 — are now uncommented. Downcast methods return independently-owned wrappers (the C API exposes *_clone helpers used internally), so each wrapper has its own lifecycle and Close().

Out of scope (next slices)

  • variant / option / result (sum types) — deferred to share a focused PR with their value marshaling
  • own / borrow resources, future / stream / error-context / map
  • Value-side marshaling for the kinds added here
  • ComponentFuncType

Style notes

New tests follow the #282 review feedback (individual test functions over cases slice, no newComponent helper, minimal comments). Existing tests are intentionally not touched.

Open question

Tuple's accessor pair is currently TypeCount / TypeNth(i). Happy to rename to Element(i) / ElementCount (or similar) if you'd prefer wording that disambiguates "the tuple's element types" from "the tuple type itself".

Verified

  • go build ./... clean
  • go test ./... and go test -tags debug ./... pass
  • go vet ./... clean
  • gofmt -l . clean

…m/flags

Continues the type-hierarchy work from bytecodealliance#282 by exposing payload accessors
for the product/enum composite kinds: list, record, tuple, enum, flags.
Each ships as a Go wrapper type with the C API's count/nth-style accessors
(plus list/record's element/field types via independent ComponentValType
clones, matching ComponentItem.TypeAlias()):

  - ComponentListType    (Element)
  - ComponentRecordType  (FieldCount / FieldNth)
  - ComponentTupleType   (TypesCount / TypesNth)
  - ComponentEnumType    (NamesCount / NamesNth)
  - ComponentFlagsType   (NamesCount / NamesNth)

ComponentValType gains a downcast method per kind (List, Record, Tuple,
Enum, Flags) returning nil for any other kind. The corresponding five
ComponentValTypeKind* constants -- commented out in bytecodealliance#282 -- are now
uncommented.

Out of scope for this slice (per the roadmap split): variant / option /
result (sum types, deferred to share a focused PR with their value
marshaling), resource own/borrow, future/stream/error-context/map, and
value-side marshaling for the kinds added here.

Verified: go build ./..., go test ./..., go test -tags debug ./...,
go vet ./..., gofmt -l . -- all clean. 14 new test functions.
Aligns the Tuple/Enum/Flags accessors with the singular noun + Count/Nth
pattern already established by ComponentType.{Import,Export}{Count,Nth}
and the record-side FieldCount/FieldNth added in this slice:

  - ComponentTupleType: TypesCount/TypesNth   -> TypeCount/TypeNth
  - ComponentEnumType:  NamesCount/NamesNth   -> CaseCount/CaseNth
  - ComponentFlagsType: NamesCount/NamesNth   -> FlagCount/FlagNth

CaseNth/FlagNth also pick the WIT-domain term (enum "cases", flags
"flags") over the C API's generic "_names_nth", matching how FieldNth
uses the WIT term "field".
Two adjustments to align with the test names established in
component_type_feat_component_model_test.go:

1. Fix stale method names left from the previous rename commit
   (TupleType.TypesNth -> TypeNth, EnumType.NamesNth -> CaseNth,
   FlagsType.NamesNth -> FlagNth):

   - TupleTypesNthOutOfRange   -> TupleTypeNthOutOfRange
   - EnumNamesNthOutOfRange    -> EnumCaseNthOutOfRange
   - FlagsNamesNthOutOfRange   -> FlagsFlagNthOutOfRange

2. Switch the wrong-kind tests from "<Method>OnNon<X>ReturnsNil" to
   "<Method>ReturnsNilForNon<X>Kind", matching the only existing
   precedent (TestComponentItemTypeAliasReturnsNilForNonTypeKind):

   - ListOnNonListReturnsNil       -> ListReturnsNilForNonListKind
   - RecordOnNonRecordReturnsNil   -> RecordReturnsNilForNonRecordKind
   - TupleOnNonTupleReturnsNil     -> TupleReturnsNilForNonTupleKind
   - EnumOnNonEnumReturnsNil       -> EnumReturnsNilForNonEnumKind
   - FlagsOnNonFlagsReturnsNil     -> FlagsReturnsNilForNonFlagsKind
Pre-existing wasmtime-go test files (e.g. functype_test.go,
importtype_test.go, valtype_test.go) follow a "one test per type, multiple
inline assertions" pattern. The previous V1 layout (14 tests split by
kind x variation axis with OutOfRange / ReturnsNilForNon<X>Kind suffixes)
was modeled on the recently-merged bytecodealliance#282 component tests, which have
diverged from that pre-existing canonical style.

Collapse to 5 tests, one per composite kind. Each test covers happy path
+ index-out-of-range (where applicable) + wrong-kind-returns-nil inline.

Net: 224 -> 65 lines.
- ComponentValTypeKind doc: drop the slice-scope narrative; the kind set
  is already self-documenting from the constants below.
- Commented-out-kinds note: collapse to one line; the rationale lives in
  the PR description, not in the source.
- Downcast lifecycle note: move the "independently-owned wrapper" remark
  from ComponentValType.List's per-method comment up to the type-level
  doc, since it applies to every downcast (List/Record/Tuple/Enum/Flags).
- Tests: replace the five copy-pasted "X() returns nil on a non-X kind"
  blocks with a single TestComponentValTypeDowncastNilForOtherKinds that
  probes all five downcasts against a u32 type alias.
@glassmonkey glassmonkey force-pushed the component-composite-types branch from b580910 to 12cd8cd Compare May 19, 2026 04:40
…time-go style

externtype.go already exposes the same pattern (ExternType.FuncType /
GlobalType / TableType / MemoryType: return the underlying type for the
matching kind, nil otherwise) but uses different conventions than the
ones this slice originally adopted:

  - Doc wording: existing methods say "returns the underlying X ... if
    it's a Y type. Otherwise returns nil." -- not "downcast method".
  - Test shape: TestFuncType asserts NotNil for the matching accessor
    and Nil for every other accessor inline, not via a separate
    cross-kind test function.

Adjust this slice to match:

  - Drop the "downcast" framing from the ComponentValType type comment
    and from each composite accessor's doc; switch to the
    "returns the underlying X if this is a Y type. Otherwise returns
    nil." form, in one line.
  - Compress the ComponentValType type comment from 6 lines to 4.
  - Move the "other-kind accessors return nil" assertions from a
    dedicated TestComponentValTypeDowncastNilForOtherKinds into each
    of the five per-kind tests inline, mirroring TestFuncType. The
    dedicated test is removed.
Copy link
Copy Markdown
Member

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

var nameLen C.size_t
var out C.wasmtime_component_valtype_t
found := C.wasmtime_component_record_type_field_nth(rt.ptr(), C.size_t(i), &nameP, &nameLen, &out)
runtime.KeepAlive(rt)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This'll want to be below the C.GoStringN call below because the rt value owns the returned string and needs to stay alive

var nameP *C.char
var nameLen C.size_t
found := C.wasmtime_component_enum_type_names_nth(et.ptr(), C.size_t(i), &nameP, &nameLen)
runtime.KeepAlive(et)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above, this needs to be below the GoStringN call

var nameP *C.char
var nameLen C.size_t
found := C.wasmtime_component_flags_type_names_nth(ft.ptr(), C.size_t(i), &nameP, &nameLen)
runtime.KeepAlive(ft)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto to above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants