Skip to content

feat: canonical type IDs, P3 component support, 54 WAST fixes (90→36)#220

Merged
avrabe merged 22 commits intomainfrom
fix/canonical-type-ids
Apr 7, 2026
Merged

feat: canonical type IDs, P3 component support, 54 WAST fixes (90→36)#220
avrabe merged 22 commits intomainfrom
fix/canonical-type-ids

Conversation

@avrabe
Copy link
Copy Markdown
Collaborator

@avrabe avrabe commented Apr 6, 2026

Summary

Major push on WebAssembly spec conformance and P3 component model support.

WAST Conformance: 90 → 36 failures (65,597/65,633 = 99.945%)

Runtime fixes

  • Canonical type IDs with finality + iso-recursive rec-group canonicalization
  • GcTypeInfo::Func extended with params/results for function type distinction
  • GcFieldStorage::Ref/RefNull variants preserving concrete type indices
  • call_indirect canonical type matching with subtype chain walking
  • any.convert_extern / extern.convert_any round-trip with thread-local side table
  • br_on_non_null multi-value branch preservation
  • Element segment GC instructions (array.new, array.new_fixed, struct.new)
  • array.new_elem / array.init_elem using resolved_elem_items + dropped checks

Validator fixes

  • Unreachable code type checking (br, br_if, select, local.tee)
  • br_on_cast nullability + label type validation + fall-through types
  • table.copy element type compatibility + mixed table32/table64 length
  • Import type subtyping: PreciseTypeInfo, rec-group matching, finality

Decoder fixes

  • Empty custom section rejection
  • Array field mutability byte validation
  • Custom-descriptors proposal markers (0x4D/0x4C)

P3 Component Support

  • Library mode for nested components (no _start required)
  • Lenient import linking for WAC-composed components
  • Deferred InlineExports resolution
  • Nested engine discovery and swap for entry point
  • Inter-component call dispatch via InterComponentHandler
  • P3 CLI (p3_cli.wasm) runs and prints help text

Linker fixes

  • Struct field type index relativization in cross-module rec-group comparison
  • Binary-level global ref type parsing preserving nullability

Test plan

  • All v0.5.0 components: hello_rust, calculator, datetime, hello_c_cli, hello_cpp_cli, yolo
  • P3 CLI: runs help text, nested components instantiate
  • WAST: 65,597 of 65,633 assertions pass (99.945%)
  • CI pipeline

🤖 Generated with Claude Code

avrabe and others added 17 commits April 5, 2026 23:43
…onversion

Implement canonical type IDs for structural type equivalence in ref.test,
ref.cast, and br_on_cast with concrete types. Add proper any.convert_extern
/ extern.convert_any semantics using Value::Ref for internalized externs.
Support GC instructions (array.new, array.new_fixed, struct.new) in element
segment expression evaluation. Fix array.new_elem to use resolved_elem_items.

WAST conformance: 90 → 82 failures (65,551/65,633 = 99.875%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Custom sections must contain at least a valid name per the WebAssembly
spec. A custom section with size 0 was incorrectly accepted. Now properly
returns 'unexpected end' error.

WAST conformance: 82 → 80 failures (65,553/65,633 = 99.878%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ved items

- Validate array field mutability byte in decoder (must be 0 or 1)
- Check element segment dropped status in array.init_elem and array.new_elem
- Use resolved_elem_items in array.init_elem for pre-evaluated GC values

WAST conformance: 80 → 77 failures (65,556/65,633 = 99.883%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BrOnNonNull was only preserving 1 value when branching, but multi-value
blocks require preserving all result values. Now uses the same
values_to_preserve logic as the Br handler.

WAST conformance: 77 → 76 failures (65,557/65,633 = 99.884%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tity

Implement a thread-local side table for preserving original values through
the externalize→internalize cycle. Uses high-bit sentinel (0x80000000) in
ExternRef.index to distinguish table entries from host-provided externrefs.
Host externrefs become opaque anyref (Value::Ref), while round-tripped
values recover their original I31Ref/StructRef/ArrayRef identity.

WAST conformance: 76 → 73 failures (65,560/65,633 = 99.889%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mponents

Add deferred resolution for InlineExports that reference source instances
not yet instantiated. Resolves after all core instances are created. This
is needed for WAC-composed P3 components where the section ordering puts
InlineExports before their source instance.

Note: P3 CLI still fails at nested component instantiation (issue #211)
because from_parsed requires _start for library components.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add from_parsed_library() that instantiates components without requiring
_start export. Needed for WAC-composed P3 components where nested library
components provide functions but don't have a command entry point.

Also passes parent's host_registry to nested components for WASI resolution.

P3 CLI progress: gets past _start check, now fails on arg_ref resolution
for inter-component imports (text:processor/analyzer, etc.). The arg_refs
loop is currently unimplemented — this is the next step for full P3 support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enable execution of WAC-composed P3 (Preview 3) components with nested
library components. The implementation:

- Lenient import linking for nested library components: non-WASI imports
  that can't be resolved (inter-component interfaces) are stub-resolved
  so the component can still instantiate
- Nested engine discovery: when the parent has no core instances, search
  nested components for _start or wasi:cli/run entry points
- Engine swap: take the nested component's engine as the parent's runtime
  engine, storing it AFTER the main_handle search
- Runtime entry point search: scan all instance handles in the engine to
  find the correct one exporting the entry point
- WASI handler re-setup for P3 components with nested instances

Tested with p3_cli.wasm from wasm-component-examples v0.5.0.
Note: inter-component function calls (fibonacci, analyze) require full
arg_ref resolution which is not yet implemented.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Support descriptor (0x4D) and describes (0x4C) type markers from the
WebAssembly custom-descriptors proposal. Each clause can appear at most
once per type definition; duplicates are rejected as malformed.

WAST conformance: 73 → 72 failures (65,561/65,633 = 99.890%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix 4 issues in the validator that allowed invalid types in unreachable code:

1. validate_branch_with_module: check concrete values against branch
   target types even when unreachable (polymorphic underflow still OK)
2. br_if: properly pop branch values and push label types in unreachable
   code (br_if defines concrete output types regardless of reachability)
3. select: check concrete operands match even in unreachable code
4. local.tee: model [t] -> [t] stack effect so polymorphic underflow
   pushes the local's type correctly

WAST conformance: 72 → 65 failures (65,568/65,633 = 99.901%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Import subtyping (12 of 13 fixed):
- PreciseTypeInfo for global ref nullability, rec group boundaries, finality
- Binary-level global type parser preserving nullable vs non-nullable
- Rec-group-aware type matching for function/tag import validation
- Finality checking in cross-module type comparison

br_on_cast/fail validation (6 fixed):
- Nullability check: rt2 nullable + rt1 non-nullable is type mismatch
- Branch label type validation for cast result types
- Proper fall-through type computation (diff type rt1\rt2)

WAST conformance: 65 → 47 failures (65,586/65,633 = 99.928%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ypes

Extend GcTypeInfo::Func to carry param/result types so canonical type IDs
distinguish different function signatures. Add GcFieldStorage::Ref/RefNull
variants to preserve concrete type indices in struct/array fields.

Add call_indirect_type_matches() that uses canonical type IDs with subtype
chain walking for proper GC type matching, falling back to structural
comparison for pre-GC modules.

Update ref_test_value_with_module to prefer canonical IDs when available.

WAST conformance: 47 → 36 failures (65,597/65,633 = 99.945%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per the spec, table.copy's length operand follows the destination table's
index type, not dst64 || src64. This fixes mixed table32/table64 copy
validation.

WAST conformance: 36 → 35 failures (65,598/65,633 = 99.947%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…table64

Corrects the table.copy length operand type for mixed table32/table64
copies. Length is i32 unless BOTH tables are table64.

WAST conformance: 35 → 34 failures (65,599/65,633 = 99.948%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ison

When comparing struct field types across modules, reference type indices
must be relativized to their rec-group positions. Fields containing
Ref(idx)/RefNull(idx) now check if the referenced type is inside or
outside the rec group and compare accordingly.

WAST conformance: 34 → 31 failures (65,602/65,633 = 99.953%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ents

Add InterComponentHandler that chains WASI dispatch with inter-component
routing for WAC-composed P3 components. Non-WASI function calls are
routed to nested component engines based on interface name → component
index mapping.

The handler tries multiple export name formats:
1. Plain function name
2. Interface-qualified: "interface@version#function"
3. Async-lift: "[async-lift]interface@version#function"

The dispatch reaches the correct nested component but P3 async functions
use the async protocol (task.return, subtask.drop) which requires a
coroutine runtime not yet implemented. Synchronous P3 functions would
work; async ones hit call stack exhaustion from the async-lift wrapper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add validation that the source table's element type is a subtype of the
destination table's element type for table.copy instructions.

WAST conformance: 31 → 30 failures (65,603/65,633 = 99.954%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

🔍 Build Diagnostics Report

Summary

Metric Base Branch This PR Change
Errors 0 0 0
Warnings 5 5 0

🎯 Impact Analysis

Issues in Files You Modified

  • 0 new errors introduced by your changes
  • 0 new warnings introduced by your changes
  • 0 total errors in modified files
  • 0 total warnings in modified files
  • 0 files you modified

Cascading Issues (Your Changes Breaking Other Files)

  • 0 new errors in unchanged files
  • 0 new warnings in unchanged files
  • 0 unchanged files now affected

Note: "Cascading issues" are errors in files you didn't modify, caused by your changes (e.g., breaking API changes, dependency issues).

✅ No Issues Detected

Perfect! Your changes don't introduce any new errors or warnings, and don't break any existing code.


📊 Full diagnostic data available in workflow artifacts

🔧 To reproduce locally:

# Install cargo-kiln
cargo install --path cargo-kiln

# Analyze your changes
cargo-kiln build --output json --filter-severity error
cargo-kiln check --output json --filter-severity warning

Implement the P3 async task protocol builtins that Meld-fused components
import from the $root namespace:

- [context-get-N] / [context-set-N]: per-instance task-local context slots
- [task-return]N: capture async function return values (variadic sigs)
- [waitable-set-new]: allocate waitable set handles
- [waitable-set-poll]: synchronous no-op (returns 0 in sync mode)
- [waitable-set-drop] / [waitable-join] / [task-cancel]: no-ops for sync

Builtins are dispatched in both import resolution paths (initial call and
Instruction::Call handler) before falling through to WASI/default dispatch.
Handles Meld's $N instance suffix stripping for multi-instance components.

Also adds unlinked import → host handler fallback so fused modules can
resolve WASI imports without explicit import links.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

avrabe and others added 4 commits April 6, 2026 17:07
Add P3 async builtin dispatch in the call_indirect import handler, which
is the path used by Meld-fused modules (they use table-based dispatch for
all imports). Also adds host_handler fallback for unlinked WASI imports.

Fused P3 modules execute successfully with --function "0" flag.
All P3 builtins (context-get/set, task-return, waitable-set-*) are
intercepted before WASI dispatch.

Adds automatic entry point detection for fused modules: tries numbered
exports ("0", "1") when _start is not found.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add non-nullable StackType variants (NonNullFuncRef, NonNullAnyRef, etc.)
and update heap_type_to_stack_type to respect the nullable flag from
br_on_cast instruction encoding.

Fixes 4 additional assert_invalid tests where the validator accepted
modules with nullable/non-nullable type mismatches.

WAST conformance: 34 → 30 failures (65,603/65,633 = 99.954%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@avrabe avrabe merged commit 9c831f0 into main Apr 7, 2026
17 of 18 checks passed
@avrabe avrabe deleted the fix/canonical-type-ids branch April 7, 2026 01:15
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.

1 participant