Skip to content

Keep the creation-time fixpoint for closures assigned to their own by-ref variable - #6912

Open
ondrejmirtes wants to merge 2 commits into
2.3.xfrom
closure-self-assigned-by-ref
Open

ondrejmirtes wants to merge 2 commits into
2.3.xfrom
closure-self-assigned-by-ref

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

Follow-up to 9ed2617, which covered $fx() called from nested closures but still analysed these as null:

  • call_user_func($fx), $fx->__invoke(), $a = $fx; $a(); inside $fx = static function () use (&$fx) { … }

A closure assigned to one of its own by-ref variables now keeps the creation-time fixpoint for its by-ref uses (approach from #6818).

It also restores precision lost in that commit: nested closures are scanned only for the variables they use, and arrow functions without names shadowed by their parameters, so function (Closure $count) { $count(); } inside a closure with use (&$count) no longer widens 0 to int<0, max> (approach from #6721).

Both are mirrored in turbo-ext.

Tests: nsrt/bug-15442.php and CallCallablesRuleTest::testBug15442; they fail without the change (5 assertions / 1 error) and pass with it, with and without the extension. Full suite passes in both modes, walk-trace.php is identical on the new fixtures.

Closes phpstan/phpstan#15442

…-ref variable

A closure assigned to one of its by-ref variables - $f = function () use (&$f) -
reaches itself through that variable in ways invocations cannot all be
followed: call_user_func($f), $f->__invoke(), aliasing. Its by-ref uses now
keep the creation-time fixpoint instead of seeing the variable as null.

Nested closures are scanned only for the variables they capture, and arrow
functions without those shadowed by their parameters, so an unrelated
nested $count() no longer widens the enclosing closure's by-ref $count.

Amp-Thread-ID: https://ampcode.com/threads/T-01a19c29-188b-7572-9730-387584a8a9fd
…e walk

The check looked the closure up in a per-frame map of every closure
assigned in the body, nested function-likes included: a linear search per
question over all closures assigned to the variable, and each enclosing
frame of a nested closure walking the same subtree again. The walk now
marks the closures themselves, so each closure is decided once, by the
first walk that reaches it, and getAssignedClosures() is back to the
top-level map findAssignedClosures() needs.

Amp-Thread-ID: https://ampcode.com/threads/T-01a19c29-188b-7572-9730-387584a8a9fd
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.

Recursive function is analysed as null in some cases since v2.3.0

1 participant