Skip to content

Make char an alias of int - #8892

Merged
cknitt merged 2 commits into
masterfrom
char-int-alias
Oct 19, 2026
Merged

cknitt merged 2 commits into
masterfrom
char-int-alias

Conversation

@cknitt

@cknitt cknitt commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

First step of #7028.

char was a private abbreviation of int: a char could be coerced to an int with :>, but an int could not be used as a char. It is now a plain alias, so char literals and ints mix freely, including in one switch:

let fromInt: char = 65

let mixed = x =>
  switch x {
  | 'a' => "a"
  | 98 => "b"
  | 'c' .. 'e' => "c-e"
  | _ => "other"
  }

// `codePointAt` returns option<int>; emoji literals are full code points
let reaction = s =>
  switch String.codePointAt(s, 0) {
  | Some('😀' .. '😆') => "happy"
  | Some(0x1F44B) => "wave" // '👋'
  | Some(_) | None => "other"
  }

// reaction("😄 hi") == "happy", reaction("👋") == "wave", reaction("hi") == "other"

reaction compiles to:

function reaction(s) {
  let match = s.codePointAt(0);
  if (match !== undefined) {
    if (match >= 128512) {
      if (match >= 128519) {
        return "other";
      } else {
        return "happy";
      }
    } else if (match !== 128075) {
      return "other";
    } else {
      return "wave";
    }
  } else {
    return "other";
  }
}
  • Pattern matching treats a char constant and an int constant with the same value as the same constant (Parmatch.const_compare / const_key) for exhaustiveness checks, unused-case checks, and switch compilation. Char literals keep their /* 'a' */97 comments in the JS and their char spelling in warning examples.

  • Switch bounds: char switches use int32 bounds instead of 0..max_int, because a char can now be negative. A switch whose range starts at '\000' therefore gets an extra x >= 0 check in the output. Before, it was skipped because chars were never negative. This costs at most one more comparison per path, and it changes two snapshots (adt_optimize_test.mjs, string_unicode_test.mjs). Other char ranges already checked both bounds, so their output is unchanged. For example, in string_unicode_test.mjs:

    // Before                          // After
    if (x !== 333) {                   if (x >= 256) {
      if (x >= 256) {                    if (x !== 333) {
        return 3;                          return 3;
      } else {                           } else {
        return 0;                          return 2;
      }                                  }
    } else {                           } else if (x >= 0) {
      return 2;                          return 0;
    }                                  } else {
                                         return 3;
                                       }
  • Runtime: Primitive_string_extern.getChar (codePointAt) now returns option<int> instead of option<char>, so the runtime no longer uses char. The generated JS is unchanged.

  • Editor: pipe completion on a char value now offers Int functions. It previously offered none, because it looked in the removed Stdlib.Char module and its type filter treated char and int as different types.

  • Not included: integer range patterns (1 .. 5) still report Invalid_interval here. They come later in this stack: Represent char range patterns with a typed-tree node #8893 adds an interval pattern node, and Support int range patterns #8894 accepts int ranges.

New tests:

  • char_int_alias_test.res: conversions, mixed patterns, codePointAt results, emoji (astral code points and surrogates), negative values.
  • super_errors: warning 8 and warning 11 on mixed char/int patterns.
  • Analysis: CompletionChar.res covers pipe completion on a char.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JALXUyHhkDqzcB3WuW9Svo

@cknitt
cknitt marked this pull request as ready for review October 18, 2026 07:59
@cknitt

cknitt commented Oct 18, 2026

Copy link
Copy Markdown
Member Author

@codex review

@codecov

codecov Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.05%. Comparing base (1869441) to head (eaf8b90).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #8892   +/-   ##
=======================================
  Coverage   81.04%   81.05%           
=======================================
  Files         455      455           
  Lines       63508    63509    +1     
=======================================
+ Hits        51469    51475    +6     
+ Misses      12039    12034    -5     
Files with missing lines Coverage Δ
analysis/src/type_utils.ml 66.13% <100.00%> (+0.17%) ⬆️
compiler/ml/matching.ml 77.34% <100.00%> (-0.04%) ⬇️
compiler/ml/parmatch.ml 77.37% <100.00%> (+0.06%) ⬆️
compiler/ml/predef.ml 99.28% <100.00%> (-0.01%) ⬇️

... and 2 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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 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-10-18T11:09:00.437250Z 1df38d6 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.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 1df38d69b0

ℹ️ 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".

@pkg-pr-new

pkg-pr-new Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

rescript

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

@rescript/belt

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

@rescript/darwin-arm64

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

@rescript/darwin-x64

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

@rescript/linux-arm64

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

@rescript/linux-x64

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

@rescript/runtime

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

@rescript/win32-x64

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

commit: eaf8b90

@github-actions

Copy link
Copy Markdown

@cknitt

cknitt commented Oct 18, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 1df38d69b0

ℹ️ 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".

@cknitt
cknitt requested a review from cristianoc October 18, 2026 08:37
@cknitt
cknitt added this pull request to stack #8895 October 18, 2026 09:55
@cknitt

cknitt commented Oct 18, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 1df38d69b0

ℹ️ 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".

@cknitt
cknitt force-pushed the char-int-alias branch 2 times, most recently from 8b63ce5 to 001621d Compare October 18, 2026 17:49
cknitt and others added 2 commits October 19, 2026 06:32
`char` was a private abbreviation of `int`: a char could be coerced to an
int with `:>`, but an int could not be used as a char. It is now a plain
alias, so char literals and ints mix freely, including in one `switch`.
Pattern matching treats a char constant and an int constant with the same
value as the same constant, for exhaustiveness, unused cases and switch
compilation. Char literals keep their spelling in generated JavaScript
comments and in warning examples. First step of #7028.

Example (rescript):
    let f = x =>
      switch x {
      | '\000' .. '\255' => 0
      | 'ō' => 2
      | _ => 3
      }

Before (js):
    function f(x) {
      if (x !== 333) {
        if (x >= 256) {
          return 3;
        } else {
          return 0;
        }
      } else {
        return 2;
      }
    }

After (js):
    function f(x) {
      if (x >= 256) {
        if (x !== 333) {
          return 3;
        } else {
          return 2;
        }
      } else if (x >= 0) {
        return 0;
      } else {
        return 3;
      }
    }

Source: tests/tests/src/string_unicode_test.mjs in the diff (the range starts
at code point 0, written as a literal NUL character in the test).

Review: Why keep `Const_char` instead of typing char literals as ints? It
keeps the `/* 'a' */97` comments in the JavaScript and the char spelling in
warnings such as "for example: | '\x00'".

Review: Why do char switches now use int32 bounds instead of 0..max_int? A
char can now hold any int, including negative ones, so a switch can no longer
assume `x >= 0`. Two committed snapshots change for that reason.

Review: Do integer range patterns such as `1 .. 5` work now? No, they still
report `Invalid_interval`. Char ranges expand to one case per value, which
does not scale to arbitrary ints; that needs interval patterns of their own.

Area: typechecker lambda analysis tests
Output: changed tests/tests/src/adt_optimize_test.mjs and
 string_unicode_test.mjs, char switches whose range starts at '\000' now
 send negative values to the default case
Formatting: unchanged
Formats: none
PPX: unchanged
Editor: pipe completion on a `char` value offers `Int` functions; it offered
 none before (tests/analysis_tests/tests/src/CompletionChar.res)
Tests: +tests/tests/src/char_int_alias_test.res
 +tests/analysis_tests/tests/src/CompletionChar.res
 +tests/build_tests/super_errors/fixtures/warning_8_char_int_patterns.res
 +tests/build_tests/super_errors/fixtures/warning_11_equivalent_char_int_patterns.res
 ~tests/tests/src/adt_optimize_test.mjs ~tests/tests/src/string_unicode_test.mjs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JALXUyHhkDqzcB3WuW9Svo
Signed-off-by: Christoph Knittel <ck@cca.io>
`Primitive_string_extern.getChar` returned `option<char>`; it returns
`option<int>` now that `char` is an alias of `int`, which leaves no use of
`char` in the runtime. The generated JavaScript is unchanged.

Area: runtime
Output: unchanged
Tests: none, because the type is an alias of the old one and the output is
 unchanged
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JALXUyHhkDqzcB3WuW9Svo
Signed-off-by: Christoph Knittel <ck@cca.io>
@github-actions

Copy link
Copy Markdown

@cknitt
cknitt merged commit 3354d90 into master Oct 19, 2026
31 checks passed
@cknitt
cknitt deleted the char-int-alias branch October 19, 2026 07:15
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