Skip to content

Cap cumulative unroll factor when nesting unrolled constant-array foreach#5614

Merged
ondrejmirtes merged 1 commit into2.1.xfrom
bug-14590-fix
May 8, 2026
Merged

Cap cumulative unroll factor when nesting unrolled constant-array foreach#5614
ondrejmirtes merged 1 commit into2.1.xfrom
bug-14590-fix

Conversation

@ondrejmirtes
Copy link
Copy Markdown
Member

Summary

  • tryProcessUnrolledConstantArrayForeach literally evaluates the body once per (key, value) pair. With deep nesting the cumulative product explodes — e.g. 9 nested foreach ([1, 2] as ...) loops causes 2⁹ = 512 inner body evaluations, and a body that appends to an outer array (like $cases[] = $v0) then makes scope merges over a growing constant array compound the cost super-linearly. The per-foreach FOREACH_UNROLL_LIMIT = 16 doesn't catch this because each individual loop only has 2 keys.
  • Threads a new foreachUnrollFactor field through StatementContext (getForeachUnrollFactor(), enterUnrolledForeach(int), preserved across enterDeep()). When unrolling would push the cumulative product over FOREACH_UNROLL_NESTED_LIMIT = 16, the foreach falls back to the regular non-unrolled analysis path.
  • Adds tests/bench/data/bug-14590.php so the regression is caught by RegressionBench.

Depth-9 reproducer: ~108s → ~3s. PHPStan still cleanly analyses itself; all 12066 existing tests pass.

Closes phpstan/phpstan#14590

Test plan

  • make tests — all 12066 tests pass
  • make phpstan — no errors
  • Reproducer from the issue completes in ~3s

…each

Closes phpstan/phpstan#14590

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ondrejmirtes ondrejmirtes merged commit f46f011 into 2.1.x May 8, 2026
647 of 655 checks passed
@ondrejmirtes ondrejmirtes deleted the bug-14590-fix branch May 8, 2026 10:11
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.

1 participant