Skip to content

codegen: indirect calls / first-class functions (arr[i](args), getCb()(args)) — LLVM supports natively, chad doesn't lower to it #598

@cs01

Description

@cs01

Summary

ChadScript's codegen only handles direct calls via named function symbols (call @foo(args) in LLVM IR). Calling through a function pointer — arr[i](args), getCb()(args), obj.field(args) where field holds a function — fails with "Immediately invoked function expressions (IIFE) are not supported".

LLVM supports indirect calls natively (call <sigtype> %ptr(args)) — this is purely a chad codegen gap.

Why it matters

Blocks:

  • Event-emitter patterns (listeners[event].forEach(cb => cb(arg)))
  • Dispatch tables (handlers[opcode](payload))
  • First-class functions stored in class fields and indexed through
  • Higher-order functions that aren't inlineable (arr.forEach(userCb) where userCb isn't a lambda literal)
  • Any port of existing TS libraries that use these patterns (Postgres driver, Redis, etc.)

Currently every chad library author hits this and works around with single-slot-per-event (see lib/net.ts in #585sock.on() can only have ONE listener per event because of this limitation).

Scope

This is a new codegen capability, not a drive-by fix. Rough shape:

  1. Type inferencer needs to track function type signatures (not just function names) so a Socket["onConnect"] expression has a known (data: string) => void type.
  2. Codegen needs an indirect-call lowering path: load fn pointer into SSA, emit call <sigtype> %ptr(args) instead of call @<name>(args).
  3. Function-pointer-array element type needs an LLVM representation (pointer-to-function with the right signature — related to issue codegen: function-pointer arrays not supported (Array<(x: string) => void>) #586).

Estimated 200-500 LOC of codegen + type-inferencer work. Should be queued behind the current typeOf-migration loop finishing (it touches the same type-inference hotspots).

Related

Workaround pattern

Until fixed, libraries use single-slot-per-event with explicit if (event === "foo") { this._fooCb = cb; } chains. See lib/net.ts Socket class for the pattern.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions