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
{{ message }}
Repository navigation
Array literal with a by-reference item becomes *NEVER* after the referenced variable with a constant type is reassigned #15246
When an array literal holds a reference to a variable ([1, 2, &$x]) and that variable has a constant type, assigning a different value to the variable makes the whole array *NEVER*:
The same happens for any variable whose type is a finite set of constants: with @param 0|1 $x, $a = [&$x]; $x = 5; also gives *NEVER*. Everything read from $a afterwards is *NEVER* too, so rules have nothing to check it against.
Mechanism (line numbers from 2.3.x):
AssignHandler::processArrayByRefItems() links $x and $a[2] with two IntertwinedVariableByReferenceWithExpr entries (AssignHandler.php:2566, L2573).
On $x = 5, MutatingScope::assignVariable() propagates the new value to the linked slot with $scope->assignExpression($a[2], 5) (MutatingScope.php:3359).
For an ArrayDimFetch with a constant dim, assignExpression() ends up in specifyExpressionType(), which also narrows the parent array by intersecting it with HasOffsetValueType(2, 5) (MutatingScope.php:3725). That's the right behaviour for narrowing (if ($a[2] === 5)), but here it is a write. array{1, 2, 0} & hasOffsetValue(2, 5) is never.
With int $x the intersection happens to be array{1, 2, 5}, so the bug is invisible. With a constant old value, the stale offset type contradicts the new value.
Related, but a different construct: #14503 and #13967 are about $ref = &$array[...], not about by-reference items in an array literal.
Suggested fix: when a by-reference link propagates a new value to an ArrayDimFetch slot, write the offset (setExistingOffsetValueType()/setOffsetValueType() on the parent chain, the way the foreach by-reference link does through SetExistingOffsetValueTypeExpr) instead of going through specifyExpressionType(), which intersects with the old array type.
Did PHPStan help you today? Did it make you happy in any way?
Activity
phpstan-bot commentedon Sep 17, 2026
Closed via merging phpstan/phpstan-src#6456
github-actions commentedon Oct 19, 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.