Skip to content

Compile integer operations with an identity operand to the operand - #8871

Open
cristianoc wants to merge 1 commit into
cristianoc/semantic-difffrom
cristianoc/int-identity-demo
Open

cristianoc wants to merge 1 commit into
cristianoc/semantic-difffrom
cristianoc/int-identity-demo

Conversation

@cristianoc

@cristianoc cristianoc commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Integer x + 0, 0 + x, x - 0, x * 1, 1 * x and x / 1 compile to x. A ReScript int value is a 32-bit integer in JavaScript, so the | 0 truncation (or << 0 for * 1) these operations emitted leaves the value unchanged. int32_add, int32_minus, int32_mul and int32_div in compiler/core/js_exp_make.ml return the other operand when one operand is the literal identity. The dropped operand is a literal, so the kept operand is evaluated exactly as before.

Stacked on #8870. This description is the commit's record converted to markdown; the semantic diff link is in the comments.

let addZero = (x: int) => x + 0
let subZero = (x: int) => x - 0
let mulOne = (x: int) => x * 1
let divOne = (x: int) => x / 1
Before After
return x + 0 | 0; return x;
return x - 0 | 0; return x;
return (x << 0); return x;
return x / 1 | 0; return x;

Source: bsc Example.res with bsc built from aa2078e (Before) and from this commit (After).

Output: this changes the generated JS for these operations on a non-literal operand. One existing snapshot changes (tests/tests/src/gpr_3609_test.mjs: 0 + state._0 | 0 becomes state._0), and make lib produces no changed stdlib JS. Formats unchanged.

Review

  • Can an int operand hold a value outside the 32-bit range? Every int the compiler computes is already truncated: -x, Int.fromFloat(f) and Int.shiftRightUnsigned(x, y) compile to -x | 0, f | 0 and (x >>> y) | 0. An out-of-range value reaches an int only through an external or %raw that is typed int. For those values the result does change: Number.MAX_SAFE_INTEGER + 0 used to give -1 and now gives 9007199254740991. The compiler already makes the same assumption, since Plsrint by the literal 0 compiles to its operand (compiler/core/lam_compile_primitive.ml), which drops (x >>> 0) | 0.
  • Does x / 1 still check for division by zero? The divisor is the literal 1, so no check applies. The existing rule for a nonzero literal divisor already skipped the runtime div call.

Test plan

  • New test tests/tests/src/int_identity_ops_test.res (with its .mjs) covers each identity, the int32 boundaries, and checks that a side-effecting operand runs exactly once.
  • make, make lib and make test all exit 0 (318 ounit tests OK, 606 tests/tests mocha passing, 744 stdlib mocha passing). make checkformat exits 0.

🤖 Generated with Claude Code

cristianoc added a commit that referenced this pull request Oct 16, 2026
Integer `x + 0`, `0 + x`, `x - 0`, `x * 1`, `1 * x` and `x / 1` compile
to `x`. A ReScript `int` value is a 32-bit integer in JavaScript, so the
`| 0` truncation (or `<< 0` for `* 1`) that these operations emitted
leaves the value unchanged. The JavaScript expression builders in
`Js_exp_make` (`int32_add`, `int32_minus`, `int32_mul`, `int32_div`)
return the other operand when one operand is the literal identity. The
dropped operand is a literal, so evaluation of the kept operand is
unchanged.

Example (rescript):
    let addZero = (x: int) => x + 0
    let subZero = (x: int) => x - 0
    let mulOne = (x: int) => x * 1
    let divOne = (x: int) => x / 1

Before (js):
    function addZero(x) {
      return x + 0 | 0;
    }

    function subZero(x) {
      return x - 0 | 0;
    }

    function mulOne(x) {
      return (x << 0);
    }

    function divOne(x) {
      return x / 1 | 0;
    }

After (js):
    function addZero(x) {
      return x;
    }

    function subZero(x) {
      return x;
    }

    function mulOne(x) {
      return x;
    }

    function divOne(x) {
      return x;
    }

Source: `bsc Example.res` (CommonJS, to stdout) with bsc built from
aa2078e (Before) and from this commit (After); function bodies shown, the
module header and exports are identical.

Changelog entry: - Compile integer `x + 0`, `0 + x`, `x - 0`, `x * 1`,
    `1 * x` and `x / 1` to `x`. #8871

Review: Can an `int` operand hold a value outside the 32-bit range? Every
    int the compiler computes is already truncated: compiling `-x`,
    `Int.fromFloat(f)` and `Int.shiftRightUnsigned(x, y)` with this commit
    gives `-x | 0`, `f | 0` and `(x >>> y) | 0`. A value outside the range
    reaches an `int` only through an external or `%raw` typed `int`; for
    those the result changes, for example `Number.MAX_SAFE_INTEGER + 0` gave
    -1 and gives 9007199254740991. The compiler already relies on the same
    assumption: `Plsrint` by the literal 0 compiles to the operand
    (compiler/core/lam_compile_primitive.ml, `[e1; 0] -> e1`), dropping
    `(x >>> 0) | 0`.

Review: Does `x / 1` still check for division by zero? The divisor is the
    literal 1, so no check applies; the existing rule for a nonzero literal
    divisor already skipped the runtime `div` call.

Area: codegen tests
Changelog: polish
Output: changed integer `+ 0`, `0 +`, `- 0`, `* 1`, `1 *`, `/ 1` on a
 non-literal operand, now the bare operand; in tests/tests/src one existing
 snapshot changes (gpr_3609_test.mjs, `0 + state._0 | 0` -> `state._0`),
 and the stdlib build (make lib) emits no changed JS
Formats: none
Tests: +tests/tests/src/int_identity_ops_test.res
 +tests/tests/src/int_identity_ops_test.mjs
 ~tests/tests/src/gpr_3609_test.mjs
Ran: make test on bc2e0e1 + this stack (exit 0: 318 ounit tests OK,
 tests/tests mocha 606 passing including Int_identity_ops_test, stdlib
 mocha 744 passing); make checkformat (exit 0); yarn
 changelog:check (passes)
Depends: #8870 (stacked)
PR: #8871
Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cristianoc
cristianoc force-pushed the cristianoc/int-identity-demo branch from acae5ef to 69b6d5b Compare October 16, 2026 00:59
@cristianoc
cristianoc added this pull request to stack #8872 October 16, 2026 01:00
@cristianoc
cristianoc marked this pull request as ready for review October 16, 2026 01:00
cristianoc added a commit that referenced this pull request Oct 16, 2026
Integer `x + 0`, `0 + x`, `x - 0`, `x * 1`, `1 * x` and `x / 1` compile
to `x`. A ReScript `int` value is a 32-bit integer in JavaScript, so the
`| 0` truncation (or `<< 0` for `* 1`) that these operations emitted
leaves the value unchanged. The JavaScript expression builders in
`Js_exp_make` (`int32_add`, `int32_minus`, `int32_mul`, `int32_div`)
return the other operand when one operand is the literal identity. The
dropped operand is a literal, so evaluation of the kept operand is
unchanged.

Example (rescript):
    let addZero = (x: int) => x + 0
    let subZero = (x: int) => x - 0
    let mulOne = (x: int) => x * 1
    let divOne = (x: int) => x / 1

Before (js):
    function addZero(x) {
      return x + 0 | 0;
    }

    function subZero(x) {
      return x - 0 | 0;
    }

    function mulOne(x) {
      return (x << 0);
    }

    function divOne(x) {
      return x / 1 | 0;
    }

After (js):
    function addZero(x) {
      return x;
    }

    function subZero(x) {
      return x;
    }

    function mulOne(x) {
      return x;
    }

    function divOne(x) {
      return x;
    }

Source: `bsc Example.res` (CommonJS, to stdout) with bsc built from
aa2078e (Before) and from this commit (After); function bodies shown, the
module header and exports are identical.

Changelog entry: - Compile integer `x + 0`, `0 + x`, `x - 0`, `x * 1`,
    `1 * x` and `x / 1` to `x`. #8871

Review: Can an `int` operand hold a value outside the 32-bit range? Every
    int the compiler computes is already truncated: compiling `-x`,
    `Int.fromFloat(f)` and `Int.shiftRightUnsigned(x, y)` with this commit
    gives `-x | 0`, `f | 0` and `(x >>> y) | 0`. A value outside the range
    reaches an `int` only through an external or `%raw` typed `int`; for
    those the result changes, for example `Number.MAX_SAFE_INTEGER + 0` gave
    -1 and gives 9007199254740991. The compiler already relies on the same
    assumption: `Plsrint` by the literal 0 compiles to the operand
    (compiler/core/lam_compile_primitive.ml, `[e1; 0] -> e1`), dropping
    `(x >>> 0) | 0`.

Review: Does `x / 1` still check for division by zero? The divisor is the
    literal 1, so no check applies; the existing rule for a nonzero literal
    divisor already skipped the runtime `div` call.

Area: codegen tests
Changelog: polish
Output: changed integer `+ 0`, `0 +`, `- 0`, `* 1`, `1 *`, `/ 1` on a
 non-literal operand, now the bare operand; in tests/tests/src one existing
 snapshot changes (gpr_3609_test.mjs, `0 + state._0 | 0` -> `state._0`),
 and the stdlib build (make lib) emits no changed JS
Formats: none
Tests: +tests/tests/src/int_identity_ops_test.res
 +tests/tests/src/int_identity_ops_test.mjs
 ~tests/tests/src/gpr_3609_test.mjs
Ran: make test on bc2e0e1 + this stack (exit 0: 318 ounit tests OK,
 tests/tests mocha 606 passing including Int_identity_ops_test, stdlib
 mocha 744 passing); make checkformat (exit 0); yarn
 changelog:check (passes)
Depends: #8870 (stacked)
PR: #8871
Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cristianoc
cristianoc force-pushed the cristianoc/int-identity-demo branch from 69b6d5b to 5d2667d Compare October 16, 2026 01:01
cristianoc added a commit that referenced this pull request Oct 16, 2026
Integer `x + 0`, `0 + x`, `x - 0`, `x * 1`, `1 * x` and `x / 1` compile
to `x`. A ReScript `int` value is a 32-bit integer in JavaScript, so the
`| 0` truncation (or `<< 0` for `* 1`) that these operations emitted
leaves the value unchanged. The JavaScript expression builders in
`Js_exp_make` (`int32_add`, `int32_minus`, `int32_mul`, `int32_div`)
return the other operand when one operand is the literal identity. The
dropped operand is a literal, so evaluation of the kept operand is
unchanged.

Example (rescript):
    let addZero = (x: int) => x + 0
    let subZero = (x: int) => x - 0
    let mulOne = (x: int) => x * 1
    let divOne = (x: int) => x / 1

Before (js):
    function addZero(x) {
      return x + 0 | 0;
    }

    function subZero(x) {
      return x - 0 | 0;
    }

    function mulOne(x) {
      return (x << 0);
    }

    function divOne(x) {
      return x / 1 | 0;
    }

After (js):
    function addZero(x) {
      return x;
    }

    function subZero(x) {
      return x;
    }

    function mulOne(x) {
      return x;
    }

    function divOne(x) {
      return x;
    }

Source: `bsc Example.res` (CommonJS, to stdout) with bsc built from
aa2078e (Before) and from this commit (After); function bodies shown, the
module header and exports are identical.

Changelog entry: - Compile integer `x + 0`, `0 + x`, `x - 0`, `x * 1`,
    `1 * x` and `x / 1` to `x`. #8871

Review: Can an `int` operand hold a value outside the 32-bit range? Every
    int the compiler computes is already truncated: compiling `-x`,
    `Int.fromFloat(f)` and `Int.shiftRightUnsigned(x, y)` with this commit
    gives `-x | 0`, `f | 0` and `(x >>> y) | 0`. A value outside the range
    reaches an `int` only through an external or `%raw` typed `int`; for
    those the result changes, for example `Number.MAX_SAFE_INTEGER + 0` gave
    -1 and gives 9007199254740991. The compiler already relies on the same
    assumption: `Plsrint` by the literal 0 compiles to the operand
    (compiler/core/lam_compile_primitive.ml, `[e1; 0] -> e1`), dropping
    `(x >>> 0) | 0`.

Review: Does `x / 1` still check for division by zero? The divisor is the
    literal 1, so no check applies; the existing rule for a nonzero literal
    divisor already skipped the runtime `div` call.

Area: codegen tests
Changelog: polish
Output: changed integer `+ 0`, `0 +`, `- 0`, `* 1`, `1 *`, `/ 1` on a
 non-literal operand, now the bare operand; in tests/tests/src one existing
 snapshot changes (gpr_3609_test.mjs, `0 + state._0 | 0` -> `state._0`)
 and in the gentype example Docstrings.res.js six `+ 0` functions
 drop the addition (`a + 0 | 0` -> `a`),
 and the stdlib build (make lib) emits no changed JS
Formats: none
Tests: +tests/tests/src/int_identity_ops_test.res
 +tests/tests/src/int_identity_ops_test.mjs
 ~tests/tests/src/gpr_3609_test.mjs
 ~tests/gentype_tests/typescript-react-example/src/Docstrings.res.js
Ran: make test on bc2e0e1 + this stack (exit 0: 318 ounit tests OK,
 tests/tests mocha 606 passing including Int_identity_ops_test, stdlib
 mocha 744 passing); make checkformat (exit 0); yarn
 changelog:check (passes); make
 test-gentype, test-analysis, test-tools (exit 0); make test-rewatch
 fails locally on a temp-directory symlink (Path is a directory:
 rescript-gentype-root-*-link), with no generated JS change
Depends: #8870 (stacked)
PR: #8871
Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cristianoc
cristianoc force-pushed the cristianoc/int-identity-demo branch from 5d2667d to 77309cb Compare October 16, 2026 01:29
@codecov

codecov Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.02%. Comparing base (fb7a59b) to head (018a94f).

Additional details and impacted files
@@                    Coverage Diff                    @@
##           cristianoc/semantic-diff    #8871   +/-   ##
=========================================================
  Coverage                     81.02%   81.02%           
=========================================================
  Files                           453      453           
  Lines                         63030    63038    +8     
=========================================================
+ Hits                          51070    51078    +8     
  Misses                        11960    11960           
Files with missing lines Coverage Δ
compiler/core/js_exp_make.ml 67.93% <100.00%> (+0.36%) ⬆️
🚀 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 Oct 16, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript@8871

@rescript/belt

npm i https://pkg.pr.new/@rescript/belt@8871

@rescript/darwin-arm64

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

@rescript/darwin-x64

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

@rescript/linux-arm64

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

@rescript/linux-x64

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

@rescript/runtime

npm i https://pkg.pr.new/@rescript/runtime@8871

@rescript/win32-x64

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

commit: 018a94f

cristianoc added a commit that referenced this pull request Oct 16, 2026
Integer `x + 0`, `0 + x`, `x - 0`, `x * 1`, `1 * x` and `x / 1` compile
to `x`. A ReScript `int` value is a 32-bit integer in JavaScript, so the
`| 0` truncation (or `<< 0` for `* 1`) that these operations emitted
leaves the value unchanged. The JavaScript expression builders in
`Js_exp_make` (`int32_add`, `int32_minus`, `int32_mul`, `int32_div`)
return the other operand when one operand is the literal identity. The
dropped operand is a literal, so evaluation of the kept operand is
unchanged.

Example (rescript):
    let addZero = (x: int) => x + 0
    let subZero = (x: int) => x - 0
    let mulOne = (x: int) => x * 1
    let divOne = (x: int) => x / 1

Before (js):
    function addZero(x) {
      return x + 0 | 0;
    }

    function subZero(x) {
      return x - 0 | 0;
    }

    function mulOne(x) {
      return (x << 0);
    }

    function divOne(x) {
      return x / 1 | 0;
    }

After (js):
    function addZero(x) {
      return x;
    }

    function subZero(x) {
      return x;
    }

    function mulOne(x) {
      return x;
    }

    function divOne(x) {
      return x;
    }

Source: `bsc Example.res` (CommonJS, to stdout) with bsc built from
aa2078e (Before) and from this commit (After); function bodies shown, the
module header and exports are identical.

Changelog entry: - Compile integer `x + 0`, `0 + x`, `x - 0`, `x * 1`,
    `1 * x` and `x / 1` to `x`. #8871

Review: Can an `int` operand hold a value outside the 32-bit range? Every
    int the compiler computes is already truncated: compiling `-x`,
    `Int.fromFloat(f)` and `Int.shiftRightUnsigned(x, y)` with this commit
    gives `-x | 0`, `f | 0` and `(x >>> y) | 0`. A value outside the range
    reaches an `int` only through an external or `%raw` typed `int`; for
    those the result changes, for example `Number.MAX_SAFE_INTEGER + 0` gave
    -1 and gives 9007199254740991. The compiler already relies on the same
    assumption: `Plsrint` by the literal 0 compiles to the operand
    (compiler/core/lam_compile_primitive.ml, `[e1; 0] -> e1`), dropping
    `(x >>> 0) | 0`.

Review: Does `x / 1` still check for division by zero? The divisor is the
    literal 1, so no check applies; the existing rule for a nonzero literal
    divisor already skipped the runtime `div` call.

Area: codegen tests
Changelog: polish
Output: changed integer `+ 0`, `0 +`, `- 0`, `* 1`, `1 *`, `/ 1` on a
 non-literal operand, now the bare operand; in tests/tests/src one existing
 snapshot changes (gpr_3609_test.mjs, `0 + state._0 | 0` -> `state._0`)
 and in the gentype example Docstrings.res.js six `+ 0` functions
 drop the addition (`a + 0 | 0` -> `a`),
 and the stdlib build (make lib) emits no changed JS
Formats: none
Tests: +tests/tests/src/int_identity_ops_test.res
 +tests/tests/src/int_identity_ops_test.mjs
 ~tests/tests/src/gpr_3609_test.mjs
 ~tests/gentype_tests/typescript-react-example/src/Docstrings.res.js
PR: #8871
Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cristianoc
cristianoc force-pushed the cristianoc/int-identity-demo branch from 77309cb to 2064628 Compare October 16, 2026 07:08
cristianoc added a commit that referenced this pull request Oct 16, 2026
Integer `x + 0`, `0 + x`, `x - 0`, `x * 1`, `1 * x` and `x / 1` compile
to `x`. A ReScript `int` value is a 32-bit integer in JavaScript, so the
`| 0` truncation (or `<< 0` for `* 1`) that these operations emitted
leaves the value unchanged. The JavaScript expression builders in
`Js_exp_make` (`int32_add`, `int32_minus`, `int32_mul`, `int32_div`)
return the other operand when one operand is the literal identity. The
dropped operand is a literal, so evaluation of the kept operand is
unchanged.

Example (rescript):
    let addZero = (x: int) => x + 0
    let subZero = (x: int) => x - 0
    let mulOne = (x: int) => x * 1
    let divOne = (x: int) => x / 1

Before (js):
    function addZero(x) {
      return x + 0 | 0;
    }

    function subZero(x) {
      return x - 0 | 0;
    }

    function mulOne(x) {
      return (x << 0);
    }

    function divOne(x) {
      return x / 1 | 0;
    }

After (js):
    function addZero(x) {
      return x;
    }

    function subZero(x) {
      return x;
    }

    function mulOne(x) {
      return x;
    }

    function divOne(x) {
      return x;
    }

Source: `bsc Example.res` (CommonJS, to stdout) with bsc built from
aa2078e (Before) and from this commit (After); function bodies shown, the
module header and exports are identical.

Changelog entry: - Compile integer `x + 0`, `0 + x`, `x - 0`, `x * 1`,
    `1 * x` and `x / 1` to `x`. #8871

Review: Can an `int` operand hold a value outside the 32-bit range? Every
    int the compiler computes is already truncated: compiling `-x`,
    `Int.fromFloat(f)` and `Int.shiftRightUnsigned(x, y)` with this commit
    gives `-x | 0`, `f | 0` and `(x >>> y) | 0`. A value outside the range
    reaches an `int` only through an external or `%raw` typed `int`; for
    those the result changes, for example `Number.MAX_SAFE_INTEGER + 0` gave
    -1 and gives 9007199254740991. The compiler already relies on the same
    assumption: `Plsrint` by the literal 0 compiles to the operand
    (compiler/core/lam_compile_primitive.ml, `[e1; 0] -> e1`), dropping
    `(x >>> 0) | 0`.

Review: Does `x / 1` still check for division by zero? The divisor is the
    literal 1, so no check applies; the existing rule for a nonzero literal
    divisor already skipped the runtime `div` call.

Area: codegen tests
Changelog: polish
Output: changed integer `+ 0`, `0 +`, `- 0`, `* 1`, `1 *`, `/ 1` on a
 non-literal operand, now the bare operand; in tests/tests/src one existing
 snapshot changes (gpr_3609_test.mjs, `0 + state._0 | 0` -> `state._0`)
 and in the gentype example Docstrings.res.js six `+ 0` functions
 drop the addition (`a + 0 | 0` -> `a`),
 and the stdlib build (make lib) emits no changed JS
Formats: none
Tests: +tests/tests/src/int_identity_ops_test.res
 +tests/tests/src/int_identity_ops_test.mjs
 ~tests/tests/src/gpr_3609_test.mjs
 ~tests/gentype_tests/typescript-react-example/src/Docstrings.res.js
PR: #8871
Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cristianoc
cristianoc force-pushed the cristianoc/int-identity-demo branch from 2064628 to cddc404 Compare October 16, 2026 07:11
Integer `x + 0`, `0 + x`, `x - 0`, `x * 1`, `1 * x` and `x / 1` compile
to `x`. A ReScript `int` value is a 32-bit integer in JavaScript, so the
`| 0` truncation (or `<< 0` for `* 1`) that these operations emitted
leaves the value unchanged. The JavaScript expression builders in
`Js_exp_make` (`int32_add`, `int32_minus`, `int32_mul`, `int32_div`)
return the other operand when one operand is the literal identity. The
dropped operand is a literal, so evaluation of the kept operand is
unchanged.

Example (rescript):
    let addZero = (x: int) => x + 0
    let subZero = (x: int) => x - 0
    let mulOne = (x: int) => x * 1
    let divOne = (x: int) => x / 1

Before (js):
    function addZero(x) {
      return x + 0 | 0;
    }

    function subZero(x) {
      return x - 0 | 0;
    }

    function mulOne(x) {
      return (x << 0);
    }

    function divOne(x) {
      return x / 1 | 0;
    }

After (js):
    function addZero(x) {
      return x;
    }

    function subZero(x) {
      return x;
    }

    function mulOne(x) {
      return x;
    }

    function divOne(x) {
      return x;
    }

Source: `bsc Example.res` (CommonJS, to stdout) with bsc built from
aa2078e (Before) and from this commit (After); function bodies shown, the
module header and exports are identical.

Changelog entry: - Compile integer `x + 0`, `0 + x`, `x - 0`, `x * 1`,
    `1 * x` and `x / 1` to `x`. #8871

Review: Can an `int` operand hold a value outside the 32-bit range? Every
    int the compiler computes is already truncated: compiling `-x`,
    `Int.fromFloat(f)` and `Int.shiftRightUnsigned(x, y)` with this commit
    gives `-x | 0`, `f | 0` and `(x >>> y) | 0`. A value outside the range
    reaches an `int` only through an external or `%raw` typed `int`; for
    those the result changes, for example `Number.MAX_SAFE_INTEGER + 0` gave
    -1 and gives 9007199254740991. The compiler already relies on the same
    assumption: `Plsrint` by the literal 0 compiles to the operand
    (compiler/core/lam_compile_primitive.ml, `[e1; 0] -> e1`), dropping
    `(x >>> 0) | 0`.

Review: Does `x / 1` still check for division by zero? The divisor is the
    literal 1, so no check applies; the existing rule for a nonzero literal
    divisor already skipped the runtime `div` call.

Area: codegen tests
Changelog: polish
Output: changed integer `+ 0`, `0 +`, `- 0`, `* 1`, `1 *`, `/ 1` on a
 non-literal operand, now the bare operand; in tests/tests/src one existing
 snapshot changes (gpr_3609_test.mjs, `0 + state._0 | 0` -> `state._0`)
 and in the gentype example Docstrings.res.js six `+ 0` functions
 drop the addition (`a + 0 | 0` -> `a`),
 and the stdlib build (make lib) emits no changed JS
Formats: none
Tests: +tests/tests/src/int_identity_ops_test.res
 +tests/tests/src/int_identity_ops_test.mjs
 ~tests/tests/src/gpr_3609_test.mjs
 ~tests/gentype_tests/typescript-react-example/src/Docstrings.res.js
PR: #8871
Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cristianoc
cristianoc force-pushed the cristianoc/int-identity-demo branch from cddc404 to 018a94f Compare October 16, 2026 07:51
@github-actions

Copy link
Copy Markdown

This branch has not been deployed

No deployments
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.

1 participant