Skip to content

feat(validator): NonNull StackType variants for nullability tracking#234

Open
avrabe wants to merge 5 commits into
mainfrom
fix/nullability-refactor
Open

feat(validator): NonNull StackType variants for nullability tracking#234
avrabe wants to merge 5 commits into
mainfrom
fix/nullability-refactor

Conversation

@avrabe
Copy link
Copy Markdown
Collaborator

@avrabe avrabe commented Apr 13, 2026

Summary

Add structural foundation for StackType nullability tracking.

8 NonNull variants added with proper subtyping rules. No behavior change
yet — the variants exist but are not produced by any code path.

Next step: add parallel nullability sidecar data populated by the decoder
to actually produce NonNull types in block/function type resolution.

WAST: 15 failures unchanged (99.977%)

🤖 Generated with Claude Code

Add 8 non-nullable StackType variants (NonNullFuncRef, NonNullAnyRef, etc.)
with proper subtyping: NonNullX <: X, NonNullX follows same hierarchy as X.

Methods: to_non_nullable, to_nullable, is_nullable, from_value_type_with_nullability.

No behavior change yet — the variants exist but aren't produced by any code
path. This is the structural foundation for the nullability sidecar that will
fix the remaining 15 WAST failures.

Zero regressions: try_table(0), elem(0), local_init(0), i31(0).
WAST: 15 failures unchanged (65,618/65,633 = 99.977%)

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

github-actions Bot commented Apr 13, 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

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 13, 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 19, 2026 13:01
Per WebAssembly/gc#516, branching instructions (br_on_null, br_on_non_null,
br_on_cast, br_on_cast_fail) must validate the label type decomposition
via pop_operands+push_operands, not by inspecting the current stack. The
"inspect" shortcut preserves more-specific stack types across branches
which masks type mismatches against the label's declared type.

Fix applied:
- br_on_null: pop label_types, check stack compatibility, re-push label
  types (normalizes types to label's declaration), then push non-null ref
- br_on_non_null: validate label_last accepts non-null ref, pop/re-push
  the t* prefix
- br_on_cast: pop rt1, then pop/re-push label_types[:-1], push diff
- br_on_cast_fail: same pattern, push rt2 on fall-through

Result: 15 WAST failures → 9 (99.986% conformance). Remaining 9 are in
br_on_cast (1), br_on_cast_fail (1), custom-descriptors variants (4),
return_call_ref (1), type-subtyping (2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a new ValueType variant `NonNullAbstract(u8)` representing a
non-nullable abstract heap-type reference (e.g. `(ref any)`, `(ref func)`).
The u8 is the canonical WebAssembly abstract heap-type byte.

Background: the decoder currently loses non-nullability for abstract refs
in function signatures — `(ref any)` and `anyref` (= `(ref null any)`)
both decode to ValueType::AnyRef. This prevents the validator from
enforcing non-nullability for abstract refs, blocking ~6 of the 9 remaining
WAST failures (br_on_cast edge cases).

This commit:
- Adds `ValueType::NonNullAbstract(u8)` with Debug/to_binary/default_for_type
  handling
- Updates exhaustive matches in 8 downstream crates to handle the new variant
- Maps to `NonNullStackType::NonNull*` variants in the validator's
  `from_value_type`
- Marks as non-defaultable in `is_defaultable`

The decoder is NOT yet updated to produce this variant — doing so requires
updates to ValueType equality/subtype functions that compare by variant.
Enabling decoder production without those updates caused a 78-failure
regression. This commit just lays the infrastructure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…es (9→4)

Activates the dormant ValueType::NonNullAbstract variant by having decoders
produce it for non-nullable abstract heap types. Adds supporting subtype
rules and fixes catch_ref handler type per spec.

Changes:

Decoder (kiln-decoder/streaming_decoder.rs):
- parse_value_type now returns NonNullAbstract(byte) for non-nullable
  abstract heap refs like (ref any), (ref func), (ref exn).
- parse_value_type_for_local delegates to parse_value_type (sentinel
  encoding removed — NonNullAbstract supersedes it).

Validator (kiln-build-core/wast_validator.rs):
- parse_heap_type now returns NonNullAbstract(byte) for non-nullable
  abstract block types in the 0x63/0x64 encoding.
- br_on_cast / br_on_cast_fail: apply to_non_nullable() on ht1/ht2/diff
  so the subtype check against label types correctly enforces nullability.
- catch_ref / catch_all_ref: push NonNullExnRef (not ExnRef) per spec —
  exception references handed to catch handlers are non-null.
- is_subtype_of: added hierarchy rules for NonNull abstract refs
  (NonNullStructRef <: NonNullEqRef <: NonNullAnyRef, etc.).
- is_subtype_of_in_module: added rules for TypedFuncRef(_, false) <:
  NonNullFuncRef/StructRef/ArrayRef/EqRef/AnyRef based on concrete kind.

Runtime foundation (kiln-foundation/values.rs):
- matches_type: NonNullAbstract(byte) matches the corresponding non-null
  Value variant (and its subtype hierarchy: i31<:eq<:any etc).

Result: 9 WAST failures → 4. 99.994% conformance. Remaining 4 are module
validation rejections in br_on_cast custom-descriptors (2), type-subtyping
(1), and a cascaded assert_return from the type-subtyping rejection (1).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The WebAssembly GC spec permits tables of type (ref null \$t) where \$t is
a function type to be used with call_indirect / return_call_indirect. Our
validator previously rejected any table that wasn't exactly the abstract
funcref. Now we accept any ref type whose heap type is in the func
hierarchy (funcref, (ref null \$func_type), nofunc).

Fixes type-subtyping.wast module which uses (table (ref null \$t2) ...)
with call_indirect to invoke function subtypes polymorphically.

Result: 4 WAST failures → 2. 99.997% conformance. Remaining 2 are in
custom-descriptors proposal modules where top-level modules contain
br_on_cast with rt2 nullable and rt1 non-null — strict spec rejects but
the test file expects them to validate. Proposal-specific behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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