Stream: git-wasmtime

Topic: wasmtime / issue #11650 Unexpected cast trap when making ...


view this post on Zulip Wasmtime GitHub notifications bot (Sep 09 2025 at 12:11):

nathanielc opened issue #11650:

The issue I am having is I get a trap for a failed cast when I try and run my component. The component is two composed components, one that links several core modules together generated by a compiler I have written. This is where the bug is found.

Here is the main core module:

    (core module (;1;)
      (type (;0;) (func (param (ref struct) f64) (result f64)))
      (type (;1;) (sub (struct (field (ref 0)))))
      (type (;2;) (func (param (ref struct) f64) (result (ref 1))))
      (type (;3;) (sub (struct (field (ref 2)))))
      (type (;4;) (func (param (ref struct) i64) (result (ref 3))))
      (type (;5;) (func (param i64) (result f64)))
      (type (;6;) (sub (struct (field (ref 4)))))
      (type (;7;) (sub final 6 (struct (field (ref 4)))))
      (import "std::math" "compute" (func (;0;) (type 4)))
      (export "remote1" (func 0))
      (export "main" (func 1))
      (func (;1;) (type 5) (param i64) (result f64)
        (local (ref 6) (ref 3) (ref 1))
        ref.func 0
        struct.new 7
        ref.cast (ref 6)
        local.tee 1
        i64.const 1
        local.get 1
        struct.get 6 0
        call_ref 4
        local.tee 2
        f64.const 0x1p+1 (;=2;)
        local.get 2
        struct.get 3 0
        call_ref 2
        local.tee 3
        f64.const 0x1.8p+1 (;=3;)
        local.get 3
        struct.get 1 0
        call_ref 0
        return
      )
    )

The trap occurs on the call_ref 4 instruction. My DSL has curried closures so we are seeing a pattern of creating a struct (GC proposal) with a function reference as the first field and a its closed over env as the remaining fields.

Reading this code we create function ref to function 0 which is an imported function. Then after getting it out of the struct we call it with call_ref 4. We can easily see that the struct's field is of type 4 so this should be valid however I get the trap.

If I instead change the ref.func 0 to a locally defined function instead of an imported one it I do not get the trap.

    (core module (;1;)
      (type (;0;) (func (param (ref struct) f64) (result f64)))
      (type (;1;) (sub (struct (field (ref 0)))))
      (type (;2;) (func (param (ref struct) f64) (result (ref 1))))
      (type (;3;) (sub (struct (field (ref 2)))))
      (type (;4;) (func (param (ref struct) i64) (result (ref 3))))
      (type (;5;) (func (param i64) (result f64)))
      (type (;6;) (sub (struct (field (ref 4)))))
      (type (;7;) (sub final 6 (struct (field (ref 4)))))
      (import "std::math" "compute" (func (;0;) (type 4)))
      (export "remote1" (func 0))
      (export "main" (func 1))
      (export "foo" (func 2))
      (export "foo1" (func 3))
      (export "foo2" (func 4))
      (func (;1;) (type 5) (param i64) (result f64)
        (local (ref 6) (ref 3) (ref 1))
        ref.func 2
        struct.new 7
        ref.cast (ref 6)
        local.tee 1
        i64.const 1
        local.get 1
        struct.get 6 0
        call_ref 4
        local.tee 2
        f64.const 0x1p+1 (;=2;)
        local.get 2
        struct.get 3 0
        call_ref 2
        local.tee 3
        f64.const 0x1.8p+1 (;=3;)
        local.get 3
        struct.get 1 0
        call_ref 0
        return
      )
      (func (;2;) (type 4) (param (ref struct) i64) (result (ref 3))
            ref.func 3
            struct.new 3
            return
      )
      (func (;3;) (type 2) (param (ref struct) f64) (result (ref 1))
            ref.func 4
            struct.new 1
            return
      )
      (func (;4;) (type 0) (param (ref struct) f64) (result f64)
            f64.const 0x1p+1 (;=2;)
            return
      )
    )

Again lots of currying so I need multiple local functions to make this type check.

Here is the complete component

blr.wat.txt

I can invoke it with

wasmtime run -W gc=y,function-references=y,component-model-gc=y --invoke 'main(1)' blr.wat

I get this output:

Error: failed to run main module `blr.wat.txt`

Caused by:
    0: error while executing at wasm backtrace:
           0: 0x17e517 - <unknown>!<wasm function 1>
           1: 0x17e626 - <unknown>!<wasm function 1>
    1: wasm trap: cast failure

view this post on Zulip Wasmtime GitHub notifications bot (Sep 09 2025 at 12:11):

nathanielc added the bug label to Issue #11650.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 09 2025 at 12:12):

nathanielc edited issue #11650:

The issue I am having is I get a trap for a failed cast when I try and run my component. The component is two composed components, one that links several core modules together generated by a compiler I have written. This is where the bug is found.

Here is the main core module:

    (core module (;1;)
      (type (;0;) (func (param (ref struct) f64) (result f64)))
      (type (;1;) (sub (struct (field (ref 0)))))
      (type (;2;) (func (param (ref struct) f64) (result (ref 1))))
      (type (;3;) (sub (struct (field (ref 2)))))
      (type (;4;) (func (param (ref struct) i64) (result (ref 3))))
      (type (;5;) (func (param i64) (result f64)))
      (type (;6;) (sub (struct (field (ref 4)))))
      (type (;7;) (sub final 6 (struct (field (ref 4)))))
      (import "std::math" "compute" (func (;0;) (type 4)))
      (export "remote1" (func 0))
      (export "main" (func 1))
      (func (;1;) (type 5) (param i64) (result f64)
        (local (ref 6) (ref 3) (ref 1))
        ref.func 0
        struct.new 7
        ref.cast (ref 6)
        local.tee 1
        i64.const 1
        local.get 1
        struct.get 6 0
        call_ref 4
        local.tee 2
        f64.const 0x1p+1 (;=2;)
        local.get 2
        struct.get 3 0
        call_ref 2
        local.tee 3
        f64.const 0x1.8p+1 (;=3;)
        local.get 3
        struct.get 1 0
        call_ref 0
        return
      )
    )

The trap occurs on the call_ref 4 instruction. My DSL has curried closures so we are seeing a pattern of creating a struct (GC proposal) with a function reference as the first field and a its closed over env as the remaining fields.

Reading this code we create function ref to function 0 which is an imported function. Then after getting it out of the struct we call it with call_ref 4. We can easily see that the struct's field is of type 4 so this should be valid however I get the trap.

If I instead change the ref.func 0 to a locally defined function instead of an imported one it I do not get the trap.

    (core module (;1;)
      (type (;0;) (func (param (ref struct) f64) (result f64)))
      (type (;1;) (sub (struct (field (ref 0)))))
      (type (;2;) (func (param (ref struct) f64) (result (ref 1))))
      (type (;3;) (sub (struct (field (ref 2)))))
      (type (;4;) (func (param (ref struct) i64) (result (ref 3))))
      (type (;5;) (func (param i64) (result f64)))
      (type (;6;) (sub (struct (field (ref 4)))))
      (type (;7;) (sub final 6 (struct (field (ref 4)))))
      (import "std::math" "compute" (func (;0;) (type 4)))
      (export "remote1" (func 0))
      (export "main" (func 1))
      (export "foo" (func 2))
      (export "foo1" (func 3))
      (export "foo2" (func 4))
      (func (;1;) (type 5) (param i64) (result f64)
        (local (ref 6) (ref 3) (ref 1))
        ref.func 2
        struct.new 7
        ref.cast (ref 6)
        local.tee 1
        i64.const 1
        local.get 1
        struct.get 6 0
        call_ref 4
        local.tee 2
        f64.const 0x1p+1 (;=2;)
        local.get 2
        struct.get 3 0
        call_ref 2
        local.tee 3
        f64.const 0x1.8p+1 (;=3;)
        local.get 3
        struct.get 1 0
        call_ref 0
        return
      )
      (func (;2;) (type 4) (param (ref struct) i64) (result (ref 3))
            ref.func 3
            struct.new 3
            return
      )
      (func (;3;) (type 2) (param (ref struct) f64) (result (ref 1))
            ref.func 4
            struct.new 1
            return
      )
      (func (;4;) (type 0) (param (ref struct) f64) (result f64)
            f64.const 0x1p+1 (;=2;)
            return
      )
    )

Again lots of currying so I need multiple local functions to make this type check.

Here is the complete component

blr.wat.txt

I can invoke it with

wasmtime run -W gc=y,function-references=y,component-model-gc=y --invoke 'main(1)' blr.wat

I get this output:

Error: failed to run main module `blr.wat.txt`

Caused by:
    0: error while executing at wasm backtrace:
           0: 0x17e517 - <unknown>!<wasm function 1>
           1: 0x17e626 - <unknown>!<wasm function 1>
    1: wasm trap: cast failure

view this post on Zulip Wasmtime GitHub notifications bot (Sep 09 2025 at 12:12):

nathanielc edited issue #11650:

The issue I am having is I get a trap for a failed cast when I try and run my component. The component is two composed components, one that links several core modules together generated by a compiler I have written. This is where the bug is found.

Here is the main core module:

    (core module (;1;)
      (type (;0;) (func (param (ref struct) f64) (result f64)))
      (type (;1;) (sub (struct (field (ref 0)))))
      (type (;2;) (func (param (ref struct) f64) (result (ref 1))))
      (type (;3;) (sub (struct (field (ref 2)))))
      (type (;4;) (func (param (ref struct) i64) (result (ref 3))))
      (type (;5;) (func (param i64) (result f64)))
      (type (;6;) (sub (struct (field (ref 4)))))
      (type (;7;) (sub final 6 (struct (field (ref 4)))))
      (import "std::math" "compute" (func (;0;) (type 4)))
      (export "remote1" (func 0))
      (export "main" (func 1))
      (func (;1;) (type 5) (param i64) (result f64)
        (local (ref 6) (ref 3) (ref 1))
        ref.func 0
        struct.new 7
        ref.cast (ref 6)
        local.tee 1
        i64.const 1
        local.get 1
        struct.get 6 0
        call_ref 4
        local.tee 2
        f64.const 0x1p+1 (;=2;)
        local.get 2
        struct.get 3 0
        call_ref 2
        local.tee 3
        f64.const 0x1.8p+1 (;=3;)
        local.get 3
        struct.get 1 0
        call_ref 0
        return
      )
    )

The trap occurs on the call_ref 4 instruction. My DSL has curried closures so we are seeing a pattern of creating a struct (GC proposal) with a function reference as the first field and a its closed over env as the remaining fields.

Reading this code we create function ref to function 0 which is an imported function. Then after getting it out of the struct we call it with call_ref 4. We can easily see that the struct's field is of type 4 so this should be valid however I get the trap.

If I instead change the ref.func 0 to a locally defined function instead of an imported one it I do not get the trap.

    (core module (;1;)
      (type (;0;) (func (param (ref struct) f64) (result f64)))
      (type (;1;) (sub (struct (field (ref 0)))))
      (type (;2;) (func (param (ref struct) f64) (result (ref 1))))
      (type (;3;) (sub (struct (field (ref 2)))))
      (type (;4;) (func (param (ref struct) i64) (result (ref 3))))
      (type (;5;) (func (param i64) (result f64)))
      (type (;6;) (sub (struct (field (ref 4)))))
      (type (;7;) (sub final 6 (struct (field (ref 4)))))
      (import "std::math" "compute" (func (;0;) (type 4)))
      (export "remote1" (func 0))
      (export "main" (func 1))
      (export "foo" (func 2))
      (export "foo1" (func 3))
      (export "foo2" (func 4))
      (func (;1;) (type 5) (param i64) (result f64)
        (local (ref 6) (ref 3) (ref 1))
        ref.func 2
        struct.new 7
        ref.cast (ref 6)
        local.tee 1
        i64.const 1
        local.get 1
        struct.get 6 0
        call_ref 4
        local.tee 2
        f64.const 0x1p+1 (;=2;)
        local.get 2
        struct.get 3 0
        call_ref 2
        local.tee 3
        f64.const 0x1.8p+1 (;=3;)
        local.get 3
        struct.get 1 0
        call_ref 0
        return
      )
      (func (;2;) (type 4) (param (ref struct) i64) (result (ref 3))
            ref.func 3
            struct.new 3
            return
      )
      (func (;3;) (type 2) (param (ref struct) f64) (result (ref 1))
            ref.func 4
            struct.new 1
            return
      )
      (func (;4;) (type 0) (param (ref struct) f64) (result f64)
            f64.const 0x1p+1 (;=2;)
            return
      )
    )

Again lots of currying so I need multiple local functions to make this type check.

Here is the complete component

blr.wat.txt

I can invoke it with

wasmtime run -W gc=y,function-references=y,component-model-gc=y --invoke 'main(1)' blr.wat.txt

I get this output:

Error: failed to run main module `blr.wat.txt`

Caused by:
    0: error while executing at wasm backtrace:
           0: 0x17e517 - <unknown>!<wasm function 1>
           1: 0x17e626 - <unknown>!<wasm function 1>
    1: wasm trap: cast failure

view this post on Zulip Wasmtime GitHub notifications bot (Sep 09 2025 at 12:13):

nathanielc edited issue #11650:

The issue I am having is I get a trap for a failed cast when I try and run my component. The component is two composed components, one that links several core modules together generated by a compiler I have written. This is where the bug is found.

Here is the main core module:

    (core module (;1;)
      (type (;0;) (func (param (ref struct) f64) (result f64)))
      (type (;1;) (sub (struct (field (ref 0)))))
      (type (;2;) (func (param (ref struct) f64) (result (ref 1))))
      (type (;3;) (sub (struct (field (ref 2)))))
      (type (;4;) (func (param (ref struct) i64) (result (ref 3))))
      (type (;5;) (func (param i64) (result f64)))
      (type (;6;) (sub (struct (field (ref 4)))))
      (type (;7;) (sub final 6 (struct (field (ref 4)))))
      (import "std::math" "compute" (func (;0;) (type 4)))
      (export "remote1" (func 0))
      (export "main" (func 1))
      (func (;1;) (type 5) (param i64) (result f64)
        (local (ref 6) (ref 3) (ref 1))
        ref.func 0
        struct.new 7
        ref.cast (ref 6)
        local.tee 1
        i64.const 1
        local.get 1
        struct.get 6 0
        call_ref 4
        local.tee 2
        f64.const 0x1p+1 (;=2;)
        local.get 2
        struct.get 3 0
        call_ref 2
        local.tee 3
        f64.const 0x1.8p+1 (;=3;)
        local.get 3
        struct.get 1 0
        call_ref 0
        return
      )
    )

The trap occurs on the call_ref 4 instruction. My DSL has curried closures so we are seeing a pattern of creating a struct (GC proposal) with a function reference as the first field and a its closed over env as the remaining fields.

Reading this code we create function ref to function 0 which is an imported function. Then after getting it out of the struct we call it with call_ref 4. We can easily see that the struct's field is of type 4 so this should be valid however I get the trap.

If I instead change the ref.func 0 to a locally defined function instead of an imported one it I do not get the trap.

    (core module (;1;)
      (type (;0;) (func (param (ref struct) f64) (result f64)))
      (type (;1;) (sub (struct (field (ref 0)))))
      (type (;2;) (func (param (ref struct) f64) (result (ref 1))))
      (type (;3;) (sub (struct (field (ref 2)))))
      (type (;4;) (func (param (ref struct) i64) (result (ref 3))))
      (type (;5;) (func (param i64) (result f64)))
      (type (;6;) (sub (struct (field (ref 4)))))
      (type (;7;) (sub final 6 (struct (field (ref 4)))))
      (import "std::math" "compute" (func (;0;) (type 4)))
      (export "remote1" (func 0))
      (export "main" (func 1))
      (export "foo" (func 2))
      (export "foo1" (func 3))
      (export "foo2" (func 4))
      (func (;1;) (type 5) (param i64) (result f64)
        (local (ref 6) (ref 3) (ref 1))
        ref.func 2
        struct.new 7
        ref.cast (ref 6)
        local.tee 1
        i64.const 1
        local.get 1
        struct.get 6 0
        call_ref 4
        local.tee 2
        f64.const 0x1p+1 (;=2;)
        local.get 2
        struct.get 3 0
        call_ref 2
        local.tee 3
        f64.const 0x1.8p+1 (;=3;)
        local.get 3
        struct.get 1 0
        call_ref 0
        return
      )
      (func (;2;) (type 4) (param (ref struct) i64) (result (ref 3))
            ref.func 3
            struct.new 3
            return
      )
      (func (;3;) (type 2) (param (ref struct) f64) (result (ref 1))
            ref.func 4
            struct.new 1
            return
      )
      (func (;4;) (type 0) (param (ref struct) f64) (result f64)
            f64.const 0x1p+1 (;=2;)
            return
      )
    )

Again lots of currying so I need multiple local functions to make this type check.

Here is the complete component

blr.wat.txt

I can invoke it with

wasmtime run -W gc=y,function-references=y,component-model-gc=y --invoke 'main(1)' blr.wat.txt

I get this output:

Error: failed to run main module `blr.wat.txt`

Caused by:
    0: error while executing at wasm backtrace:
           0: 0x17e517 - <unknown>!<wasm function 1>
           1: 0x17e626 - <unknown>!<wasm function 1>
    1: wasm trap: cast failure

view this post on Zulip Wasmtime GitHub notifications bot (Sep 09 2025 at 15:23):

alexcrichton commented on issue #11650:

I followed up a bit on Zulip for this

view this post on Zulip Wasmtime GitHub notifications bot (Sep 09 2025 at 15:37):

nathanielc closed issue #11650:

The issue I am having is I get a trap for a failed cast when I try and run my component. The component is two composed components, one that links several core modules together generated by a compiler I have written. This is where the bug is found.

Here is the main core module:

    (core module (;1;)
      (type (;0;) (func (param (ref struct) f64) (result f64)))
      (type (;1;) (sub (struct (field (ref 0)))))
      (type (;2;) (func (param (ref struct) f64) (result (ref 1))))
      (type (;3;) (sub (struct (field (ref 2)))))
      (type (;4;) (func (param (ref struct) i64) (result (ref 3))))
      (type (;5;) (func (param i64) (result f64)))
      (type (;6;) (sub (struct (field (ref 4)))))
      (type (;7;) (sub final 6 (struct (field (ref 4)))))
      (import "std::math" "compute" (func (;0;) (type 4)))
      (export "remote1" (func 0))
      (export "main" (func 1))
      (func (;1;) (type 5) (param i64) (result f64)
        (local (ref 6) (ref 3) (ref 1))
        ref.func 0
        struct.new 7
        ref.cast (ref 6)
        local.tee 1
        i64.const 1
        local.get 1
        struct.get 6 0
        call_ref 4
        local.tee 2
        f64.const 0x1p+1 (;=2;)
        local.get 2
        struct.get 3 0
        call_ref 2
        local.tee 3
        f64.const 0x1.8p+1 (;=3;)
        local.get 3
        struct.get 1 0
        call_ref 0
        return
      )
    )

The trap occurs on the call_ref 4 instruction. My DSL has curried closures so we are seeing a pattern of creating a struct (GC proposal) with a function reference as the first field and a its closed over env as the remaining fields.

Reading this code we create function ref to function 0 which is an imported function. Then after getting it out of the struct we call it with call_ref 4. We can easily see that the struct's field is of type 4 so this should be valid however I get the trap.

If I instead change the ref.func 0 to a locally defined function instead of an imported one it I do not get the trap.

    (core module (;1;)
      (type (;0;) (func (param (ref struct) f64) (result f64)))
      (type (;1;) (sub (struct (field (ref 0)))))
      (type (;2;) (func (param (ref struct) f64) (result (ref 1))))
      (type (;3;) (sub (struct (field (ref 2)))))
      (type (;4;) (func (param (ref struct) i64) (result (ref 3))))
      (type (;5;) (func (param i64) (result f64)))
      (type (;6;) (sub (struct (field (ref 4)))))
      (type (;7;) (sub final 6 (struct (field (ref 4)))))
      (import "std::math" "compute" (func (;0;) (type 4)))
      (export "remote1" (func 0))
      (export "main" (func 1))
      (export "foo" (func 2))
      (export "foo1" (func 3))
      (export "foo2" (func 4))
      (func (;1;) (type 5) (param i64) (result f64)
        (local (ref 6) (ref 3) (ref 1))
        ref.func 2
        struct.new 7
        ref.cast (ref 6)
        local.tee 1
        i64.const 1
        local.get 1
        struct.get 6 0
        call_ref 4
        local.tee 2
        f64.const 0x1p+1 (;=2;)
        local.get 2
        struct.get 3 0
        call_ref 2
        local.tee 3
        f64.const 0x1.8p+1 (;=3;)
        local.get 3
        struct.get 1 0
        call_ref 0
        return
      )
      (func (;2;) (type 4) (param (ref struct) i64) (result (ref 3))
            ref.func 3
            struct.new 3
            return
      )
      (func (;3;) (type 2) (param (ref struct) f64) (result (ref 1))
            ref.func 4
            struct.new 1
            return
      )
      (func (;4;) (type 0) (param (ref struct) f64) (result f64)
            f64.const 0x1p+1 (;=2;)
            return
      )
    )

Again lots of currying so I need multiple local functions to make this type check.

Here is the complete component

blr.wat.txt

I can invoke it with

wasmtime run -W gc=y,function-references=y,component-model-gc=y --invoke 'main(1)' blr.wat.txt

I get this output:

Error: failed to run main module `blr.wat.txt`

Caused by:
    0: error while executing at wasm backtrace:
           0: 0x17e517 - <unknown>!<wasm function 1>
           1: 0x17e626 - <unknown>!<wasm function 1>
    1: wasm trap: cast failure

view this post on Zulip Wasmtime GitHub notifications bot (Sep 09 2025 at 15:37):

nathanielc commented on issue #11650:

This is a bug on my side, I had the stacktrace backwards in my head and read the wrong code. The call_ref 4 instruction is working as expected but the passed struct doesn't match what the function is expecting. Thanks again


Last updated: Dec 06 2025 at 06:05 UTC