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

    bugSomething isn't workingenhancementNew feature or requeststatus:in-progressActively being worked on

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions