Skip to content

perry-codegen: residual link errors on Effect — bare _pull + class-as-function references (Union/Emit/Order) #461

Description

@proggeramlug

Symptom

After #460 (contextual-keyword re-exports) lands, the link of the Effect repro from #321 still fails on ~7 missing symbols that don't fit the keyword pattern. Filing as a single investigation bucket since they likely share root causes (mangling / class-vs-function emission) and are easier to triage together than as 7 micro-issues.

Missing symbols (post-#460)

_pull                                                              # bare, unmangled — single occurrence
_perry_fn_node_modules_effect_src_Order_ts__Order                  # namespace-pattern Order.Order
_perry_fn_node_modules_effect_src_SchemaAST_ts__Union              # `export class Union`
_perry_fn_node_modules_effect_src_internal_channel_subexecutor_ts__Emit  # `export class Emit`

Bug 1 — bare _pull symbol (no module prefix)

"_pull", referenced from:
    _perry_closure_node_modules_effect_src_internal_stream_ts__1115
    _perry_closure_node_modules_effect_src_internal_stream_ts__1121
    _perry_closure_node_modules_effect_src_internal_stream_ts__1125
    ... (~7 closures, all in internal/stream.ts)

Source: effect/src/internal/stream.ts defines const pull = (...) => { ... } as a local arrow function inside three different exported functions (lines 7839, 8114, 8516). Closures inside those functions capture pull — and the captured reference is being emitted as the bare LLVM symbol _pull without the _perry_closure_<module>_<n> or _perry_local_<scope>_pull mangling.

Two follow-on questions:

  • Why no module prefix? (probable bug: closure-capture path for nested-arrow const-bound functions falls back to the raw identifier.)
  • The same name pull is bound three times in disjoint scopes in the same file — even after mangling is fixed, codegen needs to keep them distinct. Likely already handled by closure-id, but worth confirming once the prefix is restored.

Bug 2 — class-as-function references (Union, Emit, Order)

SchemaAST.ts:1709 declares export class Union<M>. internal/channel/subexecutor.ts:215 declares export class Emit<R>. The link errors reference these as _perry_fn_<module>__Union / __Emit — i.e., codegen emits a function reference at the call site for what is in source a class.

Likely scenarios:

  • The reference site sees Union(...) (called without new) — TS allows this for class if a constructor signature is callable, but Perry's mangling needs to look up the class registry rather than emit _perry_fn_.
  • Or: the reference is on the type side (Order.Order) and the type-only import path is leaking into value-position symbol emission.

Order.ts has no export const Order — only export interface OrderTypeLambda and a bunch of named arrow functions. The reference _perry_fn_..._Order_ts__Order originating from DateTime_ts__init, Fiber_ts__init, LogLevel_ts__init smells like the namespace-pattern import type * as order from \"./Order.js\" — Effect uses Order.Order in TS-type position throughout, and one of those references is probably being lowered to a value emit by mistake.

Reproduction

Same as #321 / #460:

cd /tmp/effect-compat && perry compile test_minimal.ts -o /tmp/effect_out
# After #460 lands, only the symbols above will remain undefined.

Suggested investigation order

  1. _pull first — most likely a single-line fix in the closure-capture mangling path; small blast radius.
  2. Union / Emit next — single test case (any class referenced without new) should reproduce in isolation.
  3. Order last — probably depends on type-only-import leak fixes already in flight.

Cross-references

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions