Skip to content

Remove Int.Ref and the %incr, %decr and %refget builtins - #8616

Open
cristianoc wants to merge 1 commit into
lambda/normalize-at-constructionfrom
lambda/remove-int-ref
Open

Remove Int.Ref and the %incr, %decr and %refget builtins#8616
cristianoc wants to merge 1 commit into
lambda/normalize-at-constructionfrom
lambda/remove-int-ref

Conversation

@cristianoc

Copy link
Copy Markdown
Collaborator

Int.Ref existed only as the destination of a v13 deprecation, and the
%incr, %decr and %refget builtins behind it were inherited from OCaml's
Pervasives rather than designed for ReScript. r.contents = r.contents + 1
is shorter than the call it replaces and compiles to the same JavaScript.

Removing the builtins also removes Lambda.offset_ref, the last primitive
that lowered to a hand-built assignment. An external declared with one of
the removed builtin names is now rejected as unknown, which is what already
happens for any other unrecognised name.

Part of #8573. Stacked on #8615.

@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: a4e7ed8111

ℹ️ 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 compiler/ml/translcore.ml
@@ -268,7 +266,6 @@ let primitive_builtins : (string * Lambda.builtin) array =
(* BEGIN Triples for ref data type *)
("%makeref", Pmakeblock Lambda.ref_tag_info);
("%refset", Psetfield (0, Lambda.ref_field_set_info));

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 Update the remaining legacy primitive fixture

When make test builds the tests/tests project in scripts/test.js:81-84, tests/tests/src/test_per.res:158-161 is included by that project's recursive src configuration and still declares %refget, %incr, and %decr. With these table entries removed, translation raises Unknown builtin primitive "%refget" before the test suite can run; remove or migrate these declarations, or move them into an explicit negative-error fixture.

AGENTS.md reference: AGENTS.md:L41-L43

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Removed those three declarations in the same commit — thanks, they were stale and nothing else in the repo declares them.

One correction on the predicted failure, so the same finding doesn't get raised at P1 again: test_per.res compiles fine with them. Declaring an external whose primitive no longer exists is not an error — the external is optimized away when unused — and this file only declares %refget, %incr and %decr, never uses them. Unknown builtin primitive is raised at the use site, which is why make test passes on the branch as it stood. Verified directly:

type ref<'a> = {mutable contents: 'a}
external incr: ref<int> => unit = "%incr"
// compiles; adding `let f = r => incr(r)` is what errors

The neighbouring %makeref and %refset declarations stay, since those primitives still exist.

None of these were designed for ReScript. %incr, %decr and %refget arrived
with OCaml's Pervasives in the 2016 initial export and were carried unexamined
through every stdlib reshuffle since. Int.Ref itself was created in April 2025
(#7371) not because anyone wanted it, but as somewhere for the Pervasives.incr
deprecation to point; the primitives it wrapped were removed for v13 two weeks
ago. Outside this repository, GitHub code search finds no user of either the
externals or the API.

What the primitive bought was unboxing: expanding at the call site kept the
field write syntactically visible, so Lam_pass_eliminate_ref could still turn
a local ref into a mutable variable. A call through an ordinary function
cannot - the reference appears as a bare Lvar and the pass gives up. That is
not special to increment. Its body is six nodes against a small_inline_size of
five, and cross-module inlining is off, so the inliner cannot reach it.

Writing the update directly does keep the unboxing, and is shorter than the
call it replaces:

    Int.Ref.increment(v)   ->   v.contents = v.contents + 1

53 call sites across 30 test files change that way, and their generated
JavaScript is byte-identical. Only two outputs move: Stdlib_Int loses an empty
Ref object and its export, and test_incr_ref loses onExpression - added to pin
that the primitive bound its argument before mentioning it twice, which has
nothing left to test now that no expansion happens.

Lambda.offset_ref and the Offset_ref builtin go with them. Nothing in lambda.ml
now builds a term outside the constructors and the traversals.

Int.Ref.t went too. It was a type alias for ref<int> introduced alongside the
two functions, and with them gone the module held nothing else.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8g8qwBARAcvW9MyuKQq8H
@cristianoc
cristianoc force-pushed the lambda/remove-int-ref branch from a4e7ed8 to ac3542b Compare September 4, 2026 12:47
@cristianoc
cristianoc changed the base branch from lambda/normalize-at-construction to master September 4, 2026 12:48
@codecov

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.88889% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.32%. Comparing base (56fe3bc) to head (ac3542b).

Files with missing lines Patch % Lines
compiler/ml/lambda.ml 68.42% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8616      +/-   ##
==========================================
- Coverage   77.32%   77.32%   -0.01%     
==========================================
  Files         467      467              
  Lines       63313    63318       +5     
==========================================
+ Hits        48957    48960       +3     
- Misses      14356    14358       +2     
Files with missing lines Coverage Δ
compiler/ml/matching.ml 77.58% <100.00%> (+0.25%) ⬆️
compiler/ml/translcore.ml 86.13% <100.00%> (+0.02%) ⬆️
compiler/ml/lambda.ml 72.04% <68.42%> (-0.49%) ⬇️

... and 1 file 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 Sep 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

rescript

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

@rescript/belt

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

@rescript/darwin-arm64

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

@rescript/darwin-x64

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

@rescript/linux-arm64

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

@rescript/linux-x64

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

@rescript/runtime

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

@rescript/win32-x64

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

commit: ac3542b

@cristianoc
cristianoc changed the base branch from master to lambda/normalize-at-construction September 4, 2026 13:28
@cristianoc
cristianoc requested a review from cknitt September 4, 2026 13:50
@cknitt

cknitt commented Sep 4, 2026

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T19:27:17.951172Z ac3542b Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cknitt

cknitt commented Sep 4, 2026

Copy link
Copy Markdown
Member

I agree that the usefulness of Int.Ref.increment etc. is rather doubtful.

But they are not deprecated yet in v12.
We could deprecate them in a final v12 release though.

@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: ac3542b815

ℹ️ 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
- Correct the structured function details produced by `rescript-tools doc` and exposed by `RescriptTools.Docgen`: parameters now retain labels and optionality, nested functions, tuples, variables, and generic arguments retain their type structure, return types are identified correctly, and non-function values no longer receive fake function details. This changes the published docgen detail schema. https://github.com/rescript-lang/rescript/pull/8576
- Make object-field mutability part of the type. A property has one type for reading and writing. Assignment requires `@set`, except on an inferred open row, where assignment makes the field settable. Private rows are not inferred open rows, so a field in `type t = private {.."x": int}` is writable only when annotated with `@set`. Coercions never grant or widen write capability. Previously, getter and setter types were tracked independently, allowing a property to be written at a different type than it was read and allowing writes through a value coerced to a type without `@set`. https://github.com/rescript-lang/rescript/pull/8597
- Remove the undocumented object-field attribute forms `@get` (bare or with a `null`/`undefined`/`nullable` payload) and `@set({no_get: ...})` on object types. Only bare `@set` marks a field settable; nullable getter types are written directly (`null<t>`, `undefined<t>`, `nullable<t>`). https://github.com/rescript-lang/rescript/pull/8597
- Remove `Int.Ref` and the `%incr`, `%decr` and `%refget` builtins behind it. Write `r.contents = r.contents + 1` instead; an `external` declared with one of the removed names is now rejected. https://github.com/rescript-lang/rescript/pull/8616

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require binding before migrating expression arguments

When the argument is not already a variable—such as the deleted regression case Int.Ref.increment(mkRef())—the advertised substitution becomes mkRef().contents = mkRef().contents + 1, which invokes mkRef() twice and can read from and write to different references. Document that expression receivers must first be bound once, and retain an end-to-end regression test for this migration pattern.

AGENTS.md reference: AGENTS.md:L41-L41

Useful? React with 👍 / 👎.

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