Skip to content

Cross-package function using arguments returns undefined (synthetic-arguments param inflates cross-module arity) — #321 gateway #1816

Description

@proggeramlug

Summary

A cross-package (compilePackages) function that uses arguments returns
undefined for calls with fewer args than the inflated param count.

Perry appends a synthetic arguments param (name: "arguments", is_rest: true)
to any function whose body references arguments (#677). Cross-package, the
importer's param-count collection (compile.rs ~606, func.params.len())
counts that synthetic param as a real one, inflating the arity, and the
cross-module call mishandles it (the synthetic arguments is treated as a
trailing-rest collecting only args after the fixed params, instead of the full
argument list). Under-filled calls then return undefined.

Minimal repro (compilePackages)

node_modules/mypkg/src/index.ts:

export function usesArgs(a: number, b?: number): number {
  return arguments.length === 1 ? a : a + (b as number);
}
export function noArgs(a: number, b?: number): number {
  return b === undefined ? a : a + b;
}

entry (perry.compilePackages: ["mypkg"]):

import { usesArgs, noArgs } from "mypkg";
console.log(usesArgs(5), usesArgs(5, 3), noArgs(5), noArgs(5, 3));
node perry
usesArgs(5) 5 undefined
usesArgs(5,3) 8 8
noArgs(5) 5 5
noArgs(5,3) 8 8

--print-hir on the entry: usesArgs (params: 3) vs noArgs (params: 2) — the
synthetic arguments param inflates usesArgs to 3. Single-file (non-package)
arguments-using functions work; only the cross-package path is broken.

Impact — the #321 (Effect framework) gateway blocker

effect's pipe and dual (the two most-used combinators) both use arguments,
so cross-package they return undefined. This cascades:

  • pipe(a, fn) → undefined
  • Effect.map / Effect.pipe_op of undefined in the fiber runtime
  • S.Struct({...}) decode → _tag of undefined in ParseResult
  • effect's runtime init: liveServices = pipe(Context.empty(), Context.add(...)…)
    → undefined → currentServices.initial undefined →
    FiberRuntime.refreshRefCache.unsafeMap of undefined.

The effect DoD (deep import, Schema init, S.Number decode, import { Effect }
barrel Effect.runSync(Effect.succeed(42))) already works (#1804/#1809/#1810/
#1811/#1812/#1815) because those paths don't depend on a pipe/arguments
result. This bug is the gateway to the rest of effect.

Fix direction

The cross-module param count + call must treat the synthetic arguments param
specially: exclude it from the user-visible arity, and populate it with the
full argument list (as the same-module synthetic-arguments path does), not
as a trailing rest. Sites: compile.rs exported-func param-count collection +
the cross-module call lowering's rest/arity handling. Broad blast radius
(all cross-module calls to arguments-using functions) — needs a thorough
regression sweep.

Refs #321, #1758, #677.

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