Skip to content

fix: skip is_simple_stub for closures on wasm-gc#5

Open
bikallem wants to merge 1 commit intomoonbitlang:mainfrom
bikallem:main
Open

fix: skip is_simple_stub for closures on wasm-gc#5
bikallem wants to merge 1 commit intomoonbitlang:mainfrom
bikallem:main

Conversation

@bikallem
Copy link

Summary

  • On wasm-gc, closures passed to FFI stubs are externref regardless of their internal type signatures. Skip is_simple_stub validation for arrow types when target is Wasm_gc.

Fixes: moonbitlang/moonbit-docs#1131

Root Cause

check_stub_type in typeutil.ml validates types inside closure signatures via is_simple_stub (lines 1004–1031), which is more restrictive than the top-level validation — but shouldn't need to be, since closures are just externref on wasm-gc.

String

(* Top-level: line 969-972 — String is ALLOWED on wasm-gc imports *)
| T_builtin T_string ->
    if is_import_stub && !Basic_config.target <> Wasm_gc then
      Some error    (* only errors if target is NOT Wasm_gc *)
    else None

(* is_simple_stub: line 1019 — String is REJECTED on ALL imports *)
| T_builtin T_string | T_builtin T_bytes -> not is_import_stub
                                          (* no wasm-gc exception! *)

Enums (e.g. NotificationPermission)

(* Top-level: lines 997-999 — enums with no-arg constructors are OK *)
| Some { ty_desc = Variant_type constrs; _ }
  when Lst.for_all constrs (fun c -> c.cs_args = []) -> None

(* is_simple_stub: lines 1024-1027 — only Extern_type is OK *)
| T_constr { type_constructor = p; _ } ->
    match Global_env.find_type_by_path global_env p with
    | Some { ty_desc = Extern_type; _ } -> true
    | _ -> false    (* enums rejected! *)

Why it doesn't matter

On wasm-gc, a closure in an FFI stub becomes externref. The wasm import signature is (func (param externref) (result externref)) regardless of the MoonBit-level closure type. The types inside the closure are a MoonBit-level concern — they only matter when JS calls the closure back. They're irrelevant to the wasm import signature.

Fix

When !Basic_config.target = Wasm_gc, return None immediately for arrow types, skipping is_simple_stub entirely. This matches the existing wasm-gc special-casing pattern for T_string at line 970.

Test plan

  • Verify closures with String, Array[T], and enum params/returns compile in wasm-gc stubs
  • Verify non-wasm-gc targets still validate closure internals via is_simple_stub

🤖 Generated with Claude Code

On wasm-gc, closures passed to FFI stubs are externref regardless of
their internal type signatures. Skip is_simple_stub validation for
arrow types when target is Wasm_gc.

Fixes: moonbitlang/moonbit-docs#1131

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 11, 2026 14:09
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts FFI stub type validation for the wasm-gc backend so that closure (arrow) types don’t undergo the stricter is_simple_stub validation of their internal parameter/return types, aligning with the fact that closures are passed as externref in wasm import/export signatures on wasm-gc.

Changes:

  • Skip is_simple_stub validation for Tarrow types when !Basic_config.target = Wasm_gc.
  • Preserve existing rejection of async functions / disallowed function positions via the prior Tarrow { is_async; _ } when ... guard.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

[Bug] wasm-gc: "Invalid stub type" for closure parameters containing String or Array in FFI stubs

1 participant