Skip to content

$IterationLimit: count the current chain of rewrites, not every lookup - #2000

Open
tombouctou wants to merge 2 commits into
Mathics3:masterfrom
tombouctou:fix-iteration-limit-per-chain
Open

tombouctou wants to merge 2 commits into
Mathics3:masterfrom
tombouctou:fix-iteration-limit-per-chain

Conversation

@tombouctou

Copy link
Copy Markdown
Contributor

Fixes #1993.

Since #1477, Symbol.evaluate adds 1 to evaluation.iteration_count each time it rewrites a symbol by an ownvalue. It never subtracts it again, and the count is only reset at the start of a top-level evaluation. So the count is the total number of symbol lookups in the whole evaluation, not the length of one chain of rewrites. Any long computation that just looks up a variable 4096 times is aborted:

In[1]:= x = {a}; Do[x, {5000}]; x
$IterationLimit::itlim: Iteration limit of 4096 exceeded.
Out[1]= $Aborted

WMA returns {a} here. Its $IterationLimit reference page describes the limit as "the maximum length of evaluation chain used in trying to evaluate any expression". In practice, loading a package of a few thousand definitions with Get fails partway through.

This PR gives the step back when the lookup finishes, using try/finally. The count is then the depth of the current chain x -> y -> ..., which is what $IterationLimit should bound. Endless chains are still stopped. The doctest Block[{$IterationLimit = 20}, yin := yang; yang := yin; yin] and the tests added in #1477 pass unchanged.

New tests in test/builtin/test_evaluation.py:

  • 30 separate lookups under $IterationLimit = 20 give the value. This test fails on master.
  • A chain of 30 rewrites passes under a limit of 40.
  • The same chain is aborted under a limit of 20.

pytest and the doctests give the same results as on master (the only failures are environmental ones, XML/ZIP/JPEG FileFormat and the CLI tests).

🤖 Generated with Claude Code

tombouctou and others added 2 commits October 16, 2026 09:36
Symbol.evaluate incremented evaluation.iteration_count on each ownvalue
rewrite and never decremented it, so the count was a total over the
whole evaluation. Any long computation that merely looked up symbols
with values 4096 times failed:

    x = {a}; Do[x, {5000}]; x   (* $IterationLimit::itlim *)

Give the step back once the lookup is done, so that the count is the
length of the current chain x -> y -> ..., which is what
$IterationLimit bounds. Endless chains such as yin := yang;
yang := yin are still stopped.

Fixes Mathics3#1993.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mmatera

mmatera commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

LGTM

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.

$IterationLimit counts all OwnValue lookups in a top-level evaluation, so long loops and Get[] abort

2 participants