How to Fix: GC: array.fill not filling the array
Why array.fill Triggers unreachable in Wasm GC and How to Fix It
This failure usually means the module is relying on a GC array operation that the current engine, runtime, or toolchain does not fully support yet. In this case, array.fill reaches an unreachable instruction because the feature path for WebAssembly GC arrays is either incomplete, incorrectly lowered, or guarded behind experimental support.
Understanding the Root Cause
The issue appears when a module defines a mutable array type such as an array of i8 values and then uses array.fill expecting the engine to initialize a range of elements safely. Instead, execution traps and lands on unreachable. That typically points to one of four low-level causes:
- Partial WebAssembly GC implementation: The runtime recognizes the syntax for array.fill but does not correctly execute it for the specific element type or reference layout.
- Feature flag mismatch: The module was compiled with GC proposal features enabled, but the runtime was launched without the matching experimental flags.
- Incorrect bounds or length assumptions: If the generated code computes a bad offset or length, the backend may trap rather than perform the fill.
- Backend bug in lowering: Some toolchains parse the instruction correctly but emit invalid internal IR, causing the engine to fall into a trap path.
For a module like:
(module
(type $string (array (mut i8)))
(export "_start" (func $f))
(func $f
;; array creation and array.fill usage here
)
)
the critical detail is that array.fill is part of the evolving WebAssembly GC instruction set. If your engine version does not fully implement this operation for mutable packed arrays like i8, a trap is a realistic outcome even when the module is otherwise valid.
Step-by-Step Solution
The safest fix is to verify support first, then use a compatible fallback if needed.
1. Confirm that GC support is actually enabled
If you are running the module in a VM, shell, or browser engine with experimental WebAssembly features, make sure GC is enabled consistently during both compilation and execution.
# Example: verify your runtime supports wasm GC features
# Use the appropriate runtime flag for your engine/version
If the environment does not support array.fill reliably, the module may validate but still fail at runtime.
2. Reduce the repro to isolate array.fill
Create a minimal module that only allocates the array and fills it. This helps determine whether the problem is with array.fill itself or with surrounding code.
(module
(type $bytes (array (mut i8)))
(func (export "_start")
(local $arr (ref null $bytes))
;; pseudo-structure:
;; allocate array
;; call array.fill
;; observe trap
)
)
If the reduced case still traps, you are likely hitting an engine or implementation bug rather than an application logic bug.
3. Replace array.fill with an explicit loop as a workaround
Until the runtime correctly supports the instruction, use element-by-element writes. This avoids the buggy bulk fill path while preserving behavior.
(module
(type $bytes (array (mut i8)))
(func $fill_loop (param $arr (ref $bytes)) (param $start i32) (param $len i32) (param $value i32)
(local $i i32)
(local.set $i (i32.const 0))
(block $done
(loop $loop
(br_if $done
(i32.ge_u (local.get $i) (local.get $len)))
(array.set $bytes
(local.get $arr)
(i32.add (local.get $start) (local.get $i))
(local.get $value))
(local.set $i
(i32.add (local.get $i) (i32.const 1)))
(br $loop))))
(func (export "_start")
;; allocate array
;; call $fill_loop instead of array.fill
))
This workaround is slower than a native bulk operation, but it is predictable and avoids the unreachable trap caused by broken lowering or unsupported execution paths.
4. Verify index and length calculations
Even if the root bug is runtime-related, you should still validate that the fill range is legal:
- start >= 0
- len >= 0
- start + len <= array length
;; Conceptual check before filling
;; if (start + len > array_length) trap or branch away
Incorrect bounds can produce traps that look similar to implementation bugs.
5. Test against a newer runtime or engine build
If this is a known issue in an older implementation, upgrading may resolve it. Check the relevant engine’s issue tracker, changelog, or experimental feature notes through its official repository or release documentation.
When reporting the bug upstream, include:
- the minimal repro module
- runtime version
- feature flags used
- whether the failure happens only with i8 arrays or all array element types
Common Edge Cases
- Packed element types: Arrays of i8 or other packed representations may fail differently from wider scalar types because of sign extension, storage packing, or backend lowering differences.
- Null references: If the target array reference is null, operations like array.set or array.fill will trap immediately.
- Out-of-bounds fill ranges: A correct instruction still traps if the range exceeds the array length.
- Compiler/runtime mismatch: A module built with one experimental feature set may not run correctly on another engine revision.
- Assuming validation guarantees execution: Passing validation does not always mean every experimental instruction path is implemented correctly in early GC builds.
FAQ
Is this a bug in my Wasm module or in the runtime?
If the module is valid, the indices are in range, and a minimal repro still traps on array.fill, it is very likely a runtime or backend implementation bug.
Why does replacing array.fill with a loop work?
An explicit loop uses repeated array.set operations instead of the bulk fill instruction. That bypasses the specific execution path that is reaching unreachable.
Does this affect all GC arrays or only array (mut i8)?
It can affect multiple array forms, but packed mutable arrays such as i8 are often more likely to expose implementation gaps. Testing the same repro with a different element type can help narrow the issue.
If your goal is a reliable short-term fix, treat array.fill as experimental in the affected environment, replace it with a manual loop, and retest under the latest runtime build with matching WebAssembly GC flags enabled.