Repository navigation
Fix a stack overflow on option of a recursive @unboxed variant - #9019
Conversation
Why:
Deciding whether `option` of a recursive `@unboxed` variant can be
unboxed walked the payload types without remembering the variants it
was in, so `Recur(t)` sent it into `t` forever and the compiler died
with a stack overflow (#9018).
The walk remembers the variant declarations it is in. A variant met again
adds no way to be undefined, since a value reaches `undefined` through
finitely many unboxed payloads, so the answer comes from its other cases.
JS modified (option of a recursive unboxed variant):
Example:
@unboxed type rec t = Recur(t) | End
let x: option<t> = Some(End)
Before (js): Fatal error: exception Stack overflow
After (js): let x = "End";
Fixes #9018
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMkrYaktctdKc4M41WF36t
Signed-off-by: Christoph Knittel <ck@cca.io>
Changelog🐛 Bug fix
Semantic Diff1 commit · Full page Stop at recursion when checking whether a variant can be undefinedfbd72f7 · cknitt + Claude · Oct 24, 2026, 12:09 UTC
The walk remembers the variant declarations it is in. A variant met again adds no way to be undefined, since a value reaches JS modified (option of a recursive unboxed variant) @unboxed type rec t = Recur(t) | End
let x: option<t> = Some(End)
-Fatal error: exception Stack overflow
+let x = "End";Fixes #9018 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #9019 +/- ##
=======================================
Coverage 81.31% 81.31%
=======================================
Files 456 456
Lines 64003 64006 +3
=======================================
+ Hits 52044 52047 +3
Misses 11959 11959
🚀 New features to boost your workflow:
|
rescript
@rescript/belt
@rescript/darwin-arm64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-9019 |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Fixes #9018.
Deciding whether
optionof a type can be unboxed (Typeopt.type_cannot_contain_undefined) checks the payload of every unboxed case. For@unboxed type rec t = Recur(t) | Endit went fromtintotforever, and the compiler crashed with a stack overflow.The walk now remembers the variant declarations it is inside. A variant it meets again adds no way to be
undefined: a value reachesundefinedthrough finitely many unboxed payloads, so the variant's other cases decide.Some(End)now compiles to"End".New test:
option_recursive_unboxed_test.res. It covers a self-recursive variant, a mutually recursive pair (both unboxed inoption), and a recursive variant with an@as(undefined)case, which stays boxed.Found by Codex on #9017, where the new check for
@notUndefinedon abbreviations reaches the same walk.🤖 Generated with Claude Code
https://claude.ai/code/session_01CMkrYaktctdKc4M41WF36t