You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
To reproduce the unexpected behavior, I made a simplified snippet that contains essentially a method that iterates over two indices in a single for loop. Originally it comes from a piece of code dealing with 2D arrays (a matrix/linear algebra library). This snippet triggers Parameter #1 of method expects int<0, max>, int given, although I added a set of checks to make sure that both indices are non-negative during the iteration.
Interestingly, the error disappeared if:
either we replace $columnIndex = 0; with e.g. $columnIndex = 1; (a value that is > 0) on Line 20 before the loop
or we replace $columnIndex = 0; with e.g. $columnIndex = 1; (a value that is > 0) on Line 27 inside the loop
There's a loop involved. That always changes my "simple" to "could be impossible" 😁
Having said that, I would step-debug through the loop analysis to figure out more.
Should lead to int<0, 2> instead. Whether this fixes the problem is another question :-)
I now understand why it's that simplified. phpstan/phpstan-src@b225f74/src/Analyser/NodeScopeResolver.php#L1432-L1438 calls generalizeType only a limiting time. And if we don't generalize generalizeType(1, 0|1|2) we end up with 100 failures because lot of int<0, max> integers are now considered 0|1|2|3|4|5|6 or similar unions.
There's a loop involved. That always changes my "simple" to "could be impossible"
Activity
VincentLanglet commentedon Mar 30, 2025
I feel like it's an easy fix I could try ; any idea where I should look in the code @ondrejmirtes ?
herndlm commentedon Mar 30, 2025
There's a loop involved. That always changes my "simple" to "could be impossible" 😁
Having said that, I would step-debug through the loop analysis to figure out more.
ondrejmirtes commentedon Mar 30, 2025
Yeah, the logic is wrong probably somewhere here: https://github.com/phpstan/phpstan-src/blob/b225f74805542bddc5249156d84143c0eddbc51e/src/Analyser/MutatingScope.php#L5130-L5440
This code (called from https://github.com/phpstan/phpstan-src/blob/b225f74805542bddc5249156d84143c0eddbc51e/src/Analyser/NodeScopeResolver.php#L1432-L1438 in case of for loops) deals with differentiating whether a loop does
$i++(an ever-changing value which has to be generalized), or$i = 3(stable value that can be kept as3).VincentLanglet commentedon Mar 30, 2025
After some tries, it seems that
MutatingScope::generalizeTypehas the following behavior:So a union of integers and an integerRange does not give the same results.
I'm unsure if TypeCombinator::union should always transform union of scalar into integerRange like
or if this logic should be only done inside
MutatingScope::generalizeTypeor if it's the wrong direction...staabm commentedon Mar 30, 2025
My gut feeling is that
Should lead to int<0, 2> instead. Whether this fixes the problem is another question :-)
VincentLanglet commentedon Mar 30, 2025
I now understand why it's that simplified.
phpstan/phpstan-src@b225f74/src/Analyser/NodeScopeResolver.php#L1432-L1438 calls generalizeType only a limiting time. And if we don't generalize
generalizeType(1, 0|1|2)we end up with 100 failures because lot ofint<0, max>integers are now considered0|1|2|3|4|5|6or similar unions.I now understand ^^'
Fix non-negative-int generalized to int in for loop
Fix non-negative-int generalized to int in for loop
Fix non-negative-int generalized to int in for loop
Fix non-negative-int generalized to int in for loop
Fix phpstan/phpstan#12163: non-negative-int evaluated to int (#5176)
phpstan-bot commentedon Mar 12, 2026
Closed via merging phpstan/phpstan-src#5176
Updated PHPStan to commit 9d660fbb57ec8219679d156c9588aadfb85ae015
Updated PHPStan to commit e8d1deeeeca6a97bd5c9b857118decbb26546922
Fix phpstan/phpstan#12163: non-negative-int evaluated to int (phpstan…
github-actions commentedon Apr 13, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.