Skip to content

Heap-allocate patched interface vtable so it survives in a global __cctor#251

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-interface-global-method-vtable
Jul 18, 2026
Merged

Heap-allocate patched interface vtable so it survives in a global __cctor#251
ASDAlexander77 merged 1 commit into
mainfrom
fix-interface-global-method-vtable

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Summary

Fixes a JIT crash (0xC0000005) when a top-level binding whose declared type is an interface is initialized from an object literal that has a method:

interface Counter { count: number; inc(): void; }
const counter: Counter = { count: 0, inc() { this.count = this.count + 1; } };
// counter.count / counter.inc() -> 0xC0000005

Root cause

Casting the (boxed) object literal to the interface builds a per-object vtable patched with the method's function pointer (mlirGenCreateInterfaceVTableForObject). That vtable was a stack VariableOp (alloca). For a local binding the alloca outlives its uses, but a global binding's initializer lowers to a __cctor function — so the alloca dangled once __cctor returned, leaving the interface's stored vtable pointer referencing freed stack memory. The crash then happened on the first field/method access through the interface.

Method-less interfaces (e.g. Point) were always fine: they point NewInterface directly at the shared global vtable, with no per-object patched copy.

Fix

Heap-allocate the patched vtable (NewOp + StoreOp + CastOp), putting it on the same footing as the object it describes (already NewOp-allocated). One localized change in MLIRGenInterfaces.cpp.

Test plan

  • New regression test 00interface_global_method.ts (registered compile + jit). Also exercises two independent same-shape globals to confirm their per-object vtables don't alias.
  • Full suite green: 354/354 JIT + 358/358 AOT (ctest -R "^test-jit-" / "^test-compile-" -j8).

Not covered (separate, still-open)

The cross-module (-shared) method-bearing interface case still crashes (null function-pointer call in the importer's __mlir_gctors) — a distinct issue in the shared-lib DLL-load / gctors-as-method symbol-resolution subsystem, not the vtable-lifetime bug fixed here. Documented in docs/object-literal-boxing-design.md §6 for follow-up.

🤖 Generated with Claude Code

…ctor

A top-level binding whose declared type is an interface, initialized from an
object literal that has a method (const c: Counter = { count: 0, inc(){...} }),
crashed the JIT with 0xC0000005 on the first field/method access.

Root cause: casting the (boxed) object literal to the interface builds a
per-object vtable patched with the method's function pointer
(mlirGenCreateInterfaceVTableForObject). That vtable was a stack VariableOp
(alloca). For a local binding the alloca outlives its uses, but a global
binding's initializer lowers to a __cctor function -- the alloca dangled once
the __cctor returned, leaving the interface's vtable pointer referencing freed
stack memory.

Fix: heap-allocate the patched vtable (NewOp + StoreOp + CastOp), putting it on
the same footing as the object it describes (already NewOp-allocated). The
method-less interface path is unaffected -- it points NewInterface directly at
the shared global vtable, no per-object copy.

Regression test 00interface_global_method.ts (also exercises two independent
same-shape globals to confirm their vtables don't alias). Full suite green:
354/354 JIT + 358/358 AOT.

The cross-module (-shared) method-bearing interface case is a separate,
still-open issue in the shared-lib symbol-resolution subsystem, documented in
docs/object-literal-boxing-design.md §6.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit b770463 into main Jul 18, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-interface-global-method-vtable branch July 18, 2026 23:17
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