Files
capy-bearer/docs/work/scope-reflection.md

5.6 KiB

Capy scope reflection

Objective and invariants

Replace type(value) with value::type. Add static type and struct reflection without adding runtime type metadata to ordinary values.

Preserve these invariants:

  • Reflection must not change struct object layouts.
  • ABI changes must have explicit version increments.
  • value::items must evaluate the receiver once.
  • Reflection must preserve managed-value ownership.
  • Every declared struct must carry a descriptor. Units that do not use value::items must not import the reflection host function.
  • Imported type lookup must continue to work.

Success criteria

  • value::type works for dependent generic parameters and results.
  • The compiler rejects type(value).
  • value::type_name returns the concrete static type name as a string.
  • A struct value exposes its field count through value::size as s64.
  • A struct value exposes a dval map through value::items.
  • Each items entry contains value and type_name keys.
  • Indexed and fixed-key DValue access both work.
  • Reflection preserves ARC and evaluates the receiver once.
  • Documentation, generated files, manifests, and artifact hashes are current.
  • The complete acceptance suite passes.

Current state

  • Status: complete.
  • Source and runtime: /root/mount_ssh/capy-bearer and capy-bearer-dev:/Code/capy-bearer
  • Baseline: daba223a9e73a4df5e3724b5721ed2c827eadc57

Goal tree

  • G1: Define the reflection contract and representation.
    • G1.1: Trace scope lookup, struct layout, DValue conversion, and ARC.
    • G1.2: Define an embedded descriptor for every declared struct.
    • G1.3: Define one metadata-driven runtime path for field values.
  • G2: Implement the metadata and syntax.
    • G2.1: Emit a reflection descriptor for every struct declaration.
    • G2.2: Remove type(value) and keep dependent value::type.
    • G2.3: Implement value::type_name, value::size, and value::items through the descriptors.
  • G3: Add focused tests.
    • G3.1: Cover generic dependent types and rejected type(value).
    • G3.2: Cover field access, dynamic keys, nested values, ARC, and evaluate-once behavior.
    • G3.3: Cover invalid receivers and unknown scope members.
  • G4: Update documentation and generated artifacts.
  • G5: Run acceptance and adversarial review.
  • G6: Commit, push, and update project notes.

Decisions, assumptions, and risks

  • value::type_name reports the concrete static type after alias resolution.
  • Every struct declaration must emit reflection metadata into the Wasm unit. Reflection must not depend on #exports or an external .api file.
  • A custom section alone is insufficient because guest reflection code cannot read it. The unit needs descriptor data in linear memory.
  • value::size and value::items apply only to structs.
  • value::items has this shape: {field_name: {value: field_value, type_name: "field_type"}}.
  • The DValue conversion must support every type that a struct can store. Nested structs and arrays need recursive conversion.
  • Function fields can use the existing DValue callable encoding. Structs cannot store module handles.
  • The compiler currently also accepts import_alias::Type. This work must not remove that imported type spelling unless the implementation proves it obsolete.
  • Type aliases lose their source spelling during current type resolution. Reflection will report the resolved type unless preserving alias spelling has a clear local implementation.

Evidence

  • 2026-09-01: ScopeLookup parses any expression::identifier. The compiler currently gives it meaning only for dependent ::type and imported types.
  • 2026-09-01: DValue construction currently accepts scalars, strings, DValues, callables, map literals, and list literals. It does not directly accept struct or array expressions.
  • 2026-09-01: The compiler keeps struct field metadata only while it compiles. #exports copies selected type declarations to external unit API metadata. The Wasm unit has no general struct reflection descriptor.
  • 2026-09-01: An initial expression-specific lowering prototype was discarded. It did not meet the embedded-metadata requirement.
  • 2026-09-01: Commit 1fe0bd8 added and pushed the embedded reflection implementation.

Result

  • The compiler emits recursive descriptors in linear memory for local and imported structs.
  • Reflection lowering uses descriptor offsets relative to __memory_base.
  • value::items calls bearer_capy_reflect_struct_brrb. It preserves callable and receiver ownership.
  • Core ABI 32 and compiler unit ABI 34 identify the new host function and unit metadata.
  • The compiler emits no reflection host import when source does not use value::items.

Verification

  • Native frontend, compiler, Wasm, CLI, and tracked fixture tests passed.
  • The phase-one runtime suite passed.
  • The reflection fixture passed in the live runtime. It covered fixed and indexed field access, nested structs, typed arrays, DValues, wide integers, callables, an empty struct, receiver evaluation, and ARC.
  • Documentation generator and compile-timeout tests passed.
  • Guide tests passed after the generated example included its type alias.
  • Artifact hashes were regenerated after the ABI and embedded descriptors changed unit bytes. The non-write golden test passed.

Concerns

  • Imported struct descriptors have compile-time coverage. Imported struct values do not have a current cross-unit runtime path.
  • The reflection host validates all addresses against the unit linear memory. A unit can still pass descriptors that point at other data in its own memory. This does not cross the unit memory boundary.