Skip to content

Eliminate Pjs_fn_make, Pjs_fn_make_unit, and unsafe_adjust_to_arity - #8570

Open
cristianoc wants to merge 1 commit into
codex/nary-dead-codefrom
codex/nary-pjs-fn-make
Open

Eliminate Pjs_fn_make, Pjs_fn_make_unit, and unsafe_adjust_to_arity#8570
cristianoc wants to merge 1 commit into
codex/nary-dead-codefrom
codex/nary-pjs-fn-make

Conversation

@cristianoc

@cristianoc cristianoc commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Part of the n-ary functions series tracked in #8557 (item 7; stacked via base branch only — each PR merges independently).

What

With structural arity, "function of arity n" is a construction invariant rather than a goal state: translcore builds every Lfunction with exactly the parameters its type declares. The arity-enforcement layer therefore disappears:

  • Function literals are emitted directly; the Pjs_fn_make wrapper that every function passed through was resolved as a no-op by lam_pass_alpha_conversion, but only after deep_flatten, simplify_exits and simplify_alias had run with the function hidden inside an Lprim, acting as an accidental optimization barrier.
  • Pjs_fn_make_unit was a one-bit metadata channel: its entire effect was setting one_unit_arg so js_exp_make drops the unit parameter. translcore now sets the attribute directly, gated on the parameter pattern binding no identifiers (a () or _ pattern) — a more principled test than the alpha pass's check that the parameter was named "param".
  • The active-pattern currying split in transl_function is deleted. It preserved pattern-effect timing across curried application steps, which no longer exist: total applications supply all arguments at once and explicit partial application eta-defers the entire call. The old output proves the point — the split's closures were immediately applied by the arity adapter, so only the allocations are gone (see mutable_uncurry_test).
  • The I<N> unboxed-record producer (the @this method-callback encoding) is removed: the general Record_unboxed translation already returns the single field unboxed, and the wrapped value is a literal of matching arity.

With no producers left, both primitive constructors and every consumer arm are deleted, including the 230-line unsafe_adjust_to_arity (its only callers were the two Pjs_fn_make resolution sites).

Recursive modules

Removing the wrapper lets the static recursive-module compilation path see module members that are plain functions, replacing the Primitive_module.init/update bootstrap with hoisted function declarations. This is safe because the static path's own applicability check now sees the functions it was designed to check — the wrapper was hiding them, pessimizing compilation — and the bootstrap demonstrably remains for members that are not plain functions (rec_module_test keeps its lazy/value cases dynamic).

Review notes

  • Removing the wrapper moves beta reduction of immediately applied functions five passes earlier (verified with -debug-ir). A follow-up in this PR keeps the beta-residue let chains local in Lam_pass_deep_flatten so Lam_pass_lets_dce can still substitute single-use arguments into the call — corpus impact: exactly one snapshot (a_recursive_type.mjs), restored to master's inline form.
  • The wider inliner exposure also surfaced a pre-existing argument-evaluation-order bug in Lam_beta_reduce (reproducible on master, visible in bs_set_int_test.mjs's checked-in output); fixed separately in Fix argument evaluation order under function inlining #8572.

Verification

Stdlib byte-identical; full test suite green; JS output changes limited to removed adapter closures, removed no-op module bootstraps, better name preservation, and constant propagation.

🤖 Generated with Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36801fdbc0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CHANGELOG.md Outdated
- Sync the platform npm package's compiler binaries (`packages/@rescript/<platform>/bin`) via dune promotion on every `dune build`, instead of Makefile/CI copy steps that only ran when make did: a plain `dune build` can no longer leave `cli/*.js` and the test harnesses running a stale compiler. https://github.com/rescript-lang/rescript/pull/8560
- Remove unused compiler IR definitions, modules, helpers, error variants, and Typedtree fields. https://github.com/rescript-lang/rescript/pull/8551 https://github.com/rescript-lang/rescript/pull/8555
- Cleanups enabled by structural arity: remove the unreachable `Too_many_arguments` error and the `?in_function` threading through the type checker that existed only to decorate it; remove the dead `function$`-vs-arrow unification bridge, `Ctype.arity`, and the unused parsetree arity helpers; deduplicate the analysis arrow-flattening helpers. https://github.com/rescript-lang/rescript/pull/8569
- Eliminate the `Pjs_fn_make`/`Pjs_fn_make_unit` arity-adjustment primitives and the `unsafe_adjust_to_arity` machinery: with structural arity, functions are constructed at their final arity, so the enforcement layer (and the active-pattern currying split it compensated for) is deleted. Generated code improves: no adapter closures for patterns on mutable fields, better constant propagation and name preservation, and recursive modules whose members are plain functions compile statically without the runtime bootstrap.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Append the PR link to the changelog entry

This newly added user-facing changelog entry ends without its PR URL, although repository policy requires entries under the current unreleased version to end with the corresponding PR link. Append that link so the release note remains traceable to the change.

AGENTS.md reference: AGENTS.md:L138-L138

Useful? React with 👍 / 👎.

@cristianoc
cristianoc force-pushed the codex/nary-pjs-fn-make branch from 966cce5 to f36394c Compare August 20, 2026 07:24
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.47619% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.87%. Comparing base (2423f75) to head (b7d4b07).

Files with missing lines Patch % Lines
compiler/core/lam_primitive.ml 0.00% 2 Missing ⚠️
Additional details and impacted files
@@                   Coverage Diff                    @@
##           codex/nary-dead-code    #8570      +/-   ##
========================================================
+ Coverage                 75.82%   75.87%   +0.05%     
========================================================
  Files                       474      474              
  Lines                     62870    62768     -102     
========================================================
- Hits                      47671    47627      -44     
+ Misses                    15199    15141      -58     
Files with missing lines Coverage Δ
compiler/core/lam.ml 72.26% <ø> (+1.19%) ⬆️
compiler/core/lam_analysis.ml 66.91% <100.00%> (-0.25%) ⬇️
compiler/core/lam_compile.ml 85.42% <ø> (+0.31%) ⬆️
compiler/core/lam_compile_primitive.ml 75.49% <ø> (+0.33%) ⬆️
compiler/core/lam_convert.ml 79.32% <ø> (-0.12%) ⬇️
compiler/core/lam_eta_conversion.ml 0.00% <ø> (-14.78%) ⬇️
compiler/core/lam_pass_alpha_conversion.ml 77.35% <ø> (-3.60%) ⬇️
compiler/core/lam_pass_deep_flatten.ml 95.55% <100.00%> (+1.65%) ⬆️
compiler/core/lam_print.ml 12.13% <ø> (+0.08%) ⬆️
compiler/ml/lambda.ml 70.37% <ø> (ø)
... and 4 more

... and 12 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pkg-pr-new

pkg-pr-new Bot commented Aug 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript-lang/rescript@8570

@rescript/belt

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/belt@8570

@rescript/darwin-arm64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/darwin-arm64@8570

@rescript/darwin-x64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/darwin-x64@8570

@rescript/linux-arm64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/linux-arm64@8570

@rescript/linux-x64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/linux-x64@8570

@rescript/runtime

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/runtime@8570

@rescript/win32-x64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/win32-x64@8570

commit: b7d4b07

With structural arity, "function of arity n" is a construction
invariant rather than a goal state: translcore builds every Lfunction
with exactly the parameters its type declares. The arity-enforcement
layer therefore disappears:

- Function literals are emitted directly; the Pjs_fn_make wrapper that
  every function passed through was resolved as a no-op by
  lam_pass_alpha_conversion, but only *after* deep_flatten,
  simplify_exits and simplify_alias had run with the function hidden
  inside an Lprim, acting as an accidental optimization barrier.
- Pjs_fn_make_unit was a one-bit metadata channel: its entire effect
  was setting one_unit_arg so js_exp_make drops the unit parameter.
  translcore now sets the attribute directly, gated on the parameter
  pattern binding no identifiers (a () or _ pattern) - a more
  principled test than the alpha pass's check that the parameter was
  named "param".
- The active-pattern currying split in transl_function is deleted. It
  preserved pattern-effect timing across curried application steps,
  which no longer exist: total applications supply all arguments at
  once and explicit partial application eta-defers the entire call.
  The old output proves the point - the split's closures were
  immediately applied by the arity adapter, so only the allocations
  are gone (see mutable_uncurry_test).
- The I<N> unboxed-record producer (the @this method-callback
  encoding) is removed: the general Record_unboxed translation already
  returns the single field unboxed, and the wrapped value is a literal
  of matching arity.

With no producers left, both primitive constructors and every consumer
arm are deleted, including the 230-line unsafe_adjust_to_arity (its
only callers were the two Pjs_fn_make resolution sites).

On recursive modules: removing the wrapper lets the static
recursive-module compilation path see module members that are plain
functions, replacing the Primitive_module.init/update bootstrap with
hoisted function declarations. This is safe because the static path's
own applicability check now sees the functions it was designed to
check - the wrapper was hiding them, pessimizing compilation - and the
bootstrap demonstrably remains for members that are not plain
functions (rec_module_test keeps its lazy/value cases dynamic).

Verified: stdlib byte-identical; full test suite green; JS output
changes limited to removed adapter closures, removed no-op module
bootstraps, better name preservation, and constant propagation.

Keep beta-residue let chains (immutable blocks and aliases feeding a
final apply) local in Lam_pass_deep_flatten instead of hoisting them
into the enclosing group: with the wrapper gone, beta reduction happens
before flatten2, and hoisting the argument bindings to toplevel put
them beyond Lam_pass_lets_dce's reach (a_recursive_type kept a named
intermediate that master inlined; the snapshot pins the restored form).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants