Skip to content

compile(call): ns.member(...nonLiteralArray) — a namespace-import member call with a spread argument evaluates to the function instead of calling it; breaks effect's Layer.mergeAll and every OpenCode command #10197

Description

@proggeramlug

Parent: #10107 (OpenCode v1.18.30). This is the bootstrap wall behind Cannot read properties of undefined (reading 'size') in every real OpenCode command (run, models, serve, TUI) after #10160 landed.

Repro (2 modules, seconds to compile)

// lib.ts
export const count = (...xs: any[]) => xs.length
export const mergeAll = (...ctxs: any[]) => { const m = new Map(); for (let i = 0; i < ctxs.length; i++) ctxs[i].mapUnsafe.forEach((v: any, k: any) => m.set(k, v)); return { mapUnsafe: m } }
export const mk = (k: string) => ({ mapUnsafe: new Map([[k, k.length]]) })
export const fill = (n: number, f: (i: number) => any) => { const out = new Array(n); for (let i = 0; i < n; i++) out[i] = f(i); return out }

// main.ts
import * as Lib from "./lib"
import { fill, mk } from "./lib"
const other = fill(3, (i) => mk("k".repeat(i + 1)))
console.log(Lib.count(1, 2, 3))          // 3                      (ok)
console.log(Lib.count(...other))         // perry: the FUNCTION `(...xs) => xs.length`; bun/node: 3
console.log(Lib.count(0, ...other))      // perry: the function;   bun/node: 4
console.log(Lib.mergeAll(...other))      // perry: the function;   bun/node: { mapUnsafe: Map(3) }
console.log((Lib as any)["mergeAll"](...other)) // perry: the function
console.log(Lib.mk(...(["zz"] as [string])))    // ok — literal array spread
const m = Lib.mergeAll; console.log(m(...other)) // ok — local alias
console.log(Lib.mergeAll.call(null, ...other), Lib.mergeAll.apply(null, other)) // ok

perry compile main.ts --no-auto-optimize (perry 0.5.1544 + three unrelated fixes, linux-x64): a call on a namespace-import member whose arguments contain a spread of a non-literal array is lowered to the member read alone — the call never happens and the expression evaluates to the function value. Any downstream property read on the "result" then fails ((fn).mapUnsafe is undefined.size throws), which is exactly what happens in effect's Layer.mergeAllEffect:

internalEffect.map((context) => Context.mergeAll(...(context as any)))   // effect/src/Layer.ts:1519, Context is `import * as`

so Layer.merge / Layer.mergeAll produce a wrong context, Context.merge(self, that) later reads that.mapUnsafe.size of a non-context, and OpenCode's AppRuntime (a Layer.mergeAll of ~50 services) dies before its first log line. Verified: a 60-module effect probe with Effect.provide(Layer.mergeAll(a, b)) fails under perry and passes under bun; provideMerge (no spread) works.

Acceptance

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regression

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions