Skip to content

Compile character range patterns in linear time - #8868

Merged
cknitt merged 2 commits into
masterfrom
fix-char-range-quadratic
Oct 16, 2026
Merged

cknitt merged 2 commits into
masterfrom
fix-char-range-quadratic

Conversation

@cknitt

@cknitt cknitt commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Fixes #8866.

A character range pattern is expanded into one or-pattern alternative per character. Three steps were quadratic in the number of alternatives:

  • Typing (Typecore, Ppat_or): each level of the nested or-pattern ran alpha_pat over the entire right subtree, copying it even when there are no variables to rename. This caused the memory blowup. When alpha_env is empty the copy is now skipped. enter_orpat_variables has already checked that both sides bind the same (here: no) variables, so it would be a no-op.
  • Exhaustiveness and unused-case checks (Parmatch.filter_all): every row did a linear search for its group. For int, char and string constants and for polymorphic variants, simple_match is equality, so their groups are now looked up by hashing. Group order and row order stay the same.
  • Default matrices (Parmatch.get_mins le_pats, used by Matching.as_matrix, check_unused, check_partial): compared all pairs of rows. The new get_mins_pats only compares a row starting with an int/char/string constant with rows starting with the same constant or with a different kind of pattern. No other row can cover it, so the result is identical.
  • Switch compilation (Matching.divide for constants): the cell for each key was found with List.find; it now uses a hash table for int/char/string keys. Floats and bigints keep the linear search, since they compare semantically (1.0 = 1.).

The hash tables are created lazily, because most calls are on small matrices.

before after
'\u{D7FE}' .. '\u{E001}' (~2k chars) 0.64 s, 327 MB 0.01 s, 25 MB
'\u{100}' .. '\u{1100}' (~4k chars) 2.64 s, 1.2 GB 0.03 s, 28 MB
'\u{4E00}' .. '\u{9FFF}' (CJK, ~21k chars) killed after 39 s at 15 GB 0.16 s, 72 MB
'\u{0}' .. '\u{FFFF}' (65k chars) effectively hangs 0.52 s, 138 MB
switch with 4,000 int cases 1.09 s 0.23 s
switch with 4,000 string cases 0.88 s 0.16 s

Generated JavaScript and warnings are byte-identical to master across tests/tests/src, the super_errors fixtures, the runtime, and a stress file. That file has overlapping ranges, duplicate int/string/float/bigint/polyvariant cases, tuples with mixed heads, guards, constructors and arrays, and exercises warnings 8, 11 and 12. On ordinary code (tests/tests + Belt), CPU time is unchanged within noise and allocation is up about 0.5%.

Big int/string switches are not fully linear yet. The remaining cost is check_unused checking each row against the previous ones.

New test: tests/tests/src/char_range_test.res covers the CJK block range and several adjacent ranges, checking boundaries at runtime. Before this change the compiler ran out of memory on it (killed after 39 s at 15 GB).

🤖 Generated with Claude Code

https://claude.ai/code/session_01KDBKAdTcAxA3HsjtVESRCr

@cknitt
cknitt marked this pull request as ready for review October 15, 2026 17:05
@cknitt

cknitt commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 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-15T17:10:15.505228Z d61ce0b 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. More of your lovely PRs please.

Reviewed commit: d61ce0bade

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

@codecov

codecov Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.02%. Comparing base (bc2e0e1) to head (4bb9898).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8868      +/-   ##
==========================================
+ Coverage   80.99%   81.02%   +0.02%     
==========================================
  Files         453      453              
  Lines       62989    63030      +41     
==========================================
+ Hits        51020    51070      +50     
+ Misses      11969    11960       -9     
Files with missing lines Coverage Δ
compiler/ml/matching.ml 77.38% <100.00%> (+0.17%) ⬆️
compiler/ml/parmatch.ml 77.31% <100.00%> (+0.65%) ⬆️
compiler/ml/typecore.ml 86.72% <100.00%> (ø)

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

cknitt and others added 2 commits October 15, 2026 18:20
Skip the no-op alpha renaming of variable-free or-patterns, and find
pattern groups, default matrix minima and switch cells by hashing.

Fixes #8866.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDBKAdTcAxA3HsjtVESRCr
Signed-off-by: Christoph Knittel <ck@cca.io>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDBKAdTcAxA3HsjtVESRCr
Signed-off-by: Christoph Knittel <ck@cca.io>
@cknitt
cknitt force-pushed the fix-char-range-quadratic branch from d61ce0b to 4bb9898 Compare October 15, 2026 18:21
@pkg-pr-new

pkg-pr-new Bot commented Oct 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

rescript

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

@rescript/belt

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

@rescript/darwin-arm64

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

@rescript/darwin-x64

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

@rescript/linux-arm64

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

@rescript/linux-x64

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

@rescript/runtime

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

@rescript/win32-x64

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

commit: 4bb9898

@github-actions

Copy link
Copy Markdown

@cknitt
cknitt requested a review from cristianoc October 15, 2026 18:56

@cristianoc cristianoc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TIL this was not done in linear time earlier

@cknitt
cknitt merged commit f36c206 into master Oct 16, 2026
26 checks passed
@cknitt
cknitt deleted the fix-char-range-quadratic branch October 16, 2026 04:42
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.

Character range patterns compile in quadratic time and memory

2 participants