-
Notifications
You must be signed in to change notification settings - Fork 104
Description
Summary
Creating a trait object (&TEvent) from an #external type (backed by externref) produces invalid wasm-gc code. The module passes moon check but fails at WebAssembly instantiation time with a type error.
Error
WebAssembly.instantiate(): Compiling function #117 failed:
type error in fallthru[0] (expected (ref 8), got externref) @+6010
Minimal Reproduction
Given this code where CustomEvent is an #external type and dispatch_event takes a trait object &TEvent:
#external
pub type JsValue
pub trait TJsValue {
to_js(Self) -> JsValue
}
pub impl TJsValue for JsValue with to_js(self) -> JsValue { self }
#external
pub type Event
pub impl TJsValue for Event with to_js(self) -> JsValue = "%identity"
pub trait TEvent: TJsValue {
event_type(Self) -> String
}
fn event_type_ffi(event : JsValue) -> String = "events" "type"
pub impl TEvent for Event with event_type(self) {
event_type_ffi(TJsValue::to_js(self))
}
#external
pub type CustomEvent
pub impl TJsValue for CustomEvent with to_js(self) -> JsValue = "%identity"
pub impl TEvent for CustomEvent with event_type(self) {
event_type_ffi(TJsValue::to_js(self))
}
fn dispatch_event_ffi(target : JsValue, event : JsValue) -> JsValue = "events" "dispatchEvent"
// This function takes a trait object &TEvent
pub fn dispatch_event(event : &TEvent) -> Unit {
let js = TJsValue::to_js(event)
let _ = dispatch_event_ffi(js, js)
}
fn new_custom_event_ffi(type_ : String) -> CustomEvent = "events" "newCustomEvent"
fn main {
let event : CustomEvent = new_custom_event_ffi("test")
// Passing an #external type as a trait object triggers the bug
dispatch_event(event)
}Steps:
moon check --target wasm-gc # passes
moon build --target wasm-gc # produces .wasm
# Instantiation fails:
node --input-type=module -e "
import fs from 'fs';
const bytes = fs.readFileSync('_build/wasm-gc/debug/build/externref_bug.wasm');
try {
await WebAssembly.instantiate(bytes, {
events: {
type: (e) => e.type,
dispatchEvent: (t, e) => t.dispatchEvent(e),
newCustomEvent: (t) => new CustomEvent(t),
},
}, { builtins: ['js-string'], importedStringConstants: '_' });
console.log('OK');
} catch(e) {
console.error('ERROR:', e.message);
}
"Note: the standalone minimal repro above may not trigger the bug on its own — it reliably reproduces when #external types and trait objects are used in a larger project (e.g. bikallem/webapi). In that context, calling EventTarget.dispatch_event(event : &TEvent) with a CustomEvent always fails.
Root Cause
The compiler generates a fallthru instruction that yields externref where a wasm-gc struct reference (ref N) is expected. The conversion from #external type (externref) to trait object (GC struct) is not handled correctly.
Workaround
Avoid passing #external types as trait objects on the wasm-gc target.
Environment
- moonc v0.8.1+6decb4ecd (2026-02-09)
- moon 0.1.20260209 (b129ae2 2026-02-09)
- Node.js v25.6.0
- Platform: Linux x86_64