Skip to content

Conversation

@rtfeldman
Copy link
Contributor

Fixes #8848

This PR fixes a panic that occurred when mutable variables (var $index) were reassigned within nested scopes like match branches. The panic message was "trying to add var at rank 2, but current rank is 1" during the generalization phase of type checking.

The root cause: When a type variable in var_pool at rank N was unified with a variable from a nested scope (rank N+1), and that nested scope was subsequently popped, the variable would resolve to a descriptor still at rank N+1. This caused a panic when trying to add it to tmp_var_pool during generalization.

The fix:

  • Cap the effective rank at the rank being generalized when copying variables to the temporary pool
  • Update the descriptor's rank to match, treating the higher rank as stale since its scope has already been popped
  • Added a regression test that reproduces the original issue

Co-authored by Claude Opus 4.5 (claude-opus-4-5-20251101)

rtfeldman and others added 5 commits December 31, 2025 07:39
When a type variable in var_pool at rank N was unified with a variable
from a nested scope at rank N+1 (after being added to the pool), and
that nested scope was subsequently popped, the variable would resolve
to a descriptor still at rank N+1. This caused a panic when trying to
add it to tmp_var_pool during generalization.

The fix caps the effective rank at the rank being generalized and updates
the descriptor's rank to match, treating the higher rank as stale since
its scope has already been popped.

Co-Authored-By: Claude <claude-opus-4-5-20251101@anthropic.com>
- Move test/fx/issue8848.roc to test/cli/issue8848.roc to avoid platform overhead
- Add test to roc_subcommands.zig instead of fx_platform_test.zig
- The CLI test runs `roc check` without needing to build a platform

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The test now correctly detects when the compiler panics by checking for
signal termination (SIGABRT) instead of just checking the exit status.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@jaredramirez
Copy link
Collaborator

if you reassign a mutable var in a higher rank (eg inside a function), it should unify with the root mutable variable at the lower rank. this unification should lower the reassignment variable, making it not eligible for generalization when that higher rank is popped. at that point, it should be moved into the lower rank.

that said, it sounds like that is not happening, hence this bug! i'm gonna pull this down and investigate, as i'm pretty sure the effective_rank fix makes the panic go away, but doesn't fix the underlying issue

@jaredramirez jaredramirez self-requested a review December 31, 2025 15:24
@jaredramirez
Copy link
Collaborator

hmm actually, i think this may have to do with value restriction! when we exit a nested scope and it's not a lambda, we simply pop the var pool rank, but actually we need to move those variables to the rank rank above or, we can not push a new rank at all if the nested expr isn't a lambda. will look into

The test only needs to verify that type checking with mutable var
reassignment in nested scopes doesn't cause a panic. A snapshot test
is the minimally-expensive test type that still exercises the fix.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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.

trying to add var at rank 2, but current rank is 1

3 participants