Skip to content

Keep the creation-time fixpoint for closures re-invoked from nested closures - #6911

Merged
ondrejmirtes merged 2 commits into
2.3.xfrom
closure-byref-nested-self-invocation
Nov 2, 2026
Merged

ondrejmirtes merged 2 commits into
2.3.xfrom
closure-byref-nested-self-invocation

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

The Build Issue Bot workflow has been red since the 2.2.x → 2.3.x merge (8598511) enabled unresolvedTemplateArguments and closureSignaturesFromUsages in bleeding edge. ../bin/phpstan reported three errors in issue-bot/src/Console/RunCommand.php::runPool().

PHPStan false positive (If condition is always false.)

A closure that invokes its own by-ref variable keeps the creation-time fixpoint for its by-ref uses, because not all of its invocations can be followed. ClosureSignatureInference::bodyYieldsOrInvokes() skipped closures and arrow functions written in the body. As a result, a closure re-invoking itself from a callback (use (&$runNext), with $runNext() inside a nested closure) was analysed only with the types at its outer invocation, so $rejected was always false. Nested closures and arrow functions are now scanned for such invocations; their yields still don't count. The C++ mirror in turbo-ext is updated the same way.

Issue bot

new Deferred() now infers its template type from resolve($results). $results is built with $results[$currentIndex] = … and then ksort(), so it is not provably a list, which the PHPDoc promises. runPool() now resolves with array_values($results).

Verification

  • New cases in nsrt/closure-byref-uses-at-invocation.php. The issue-bot-shaped case fails without the fix (Expected: bool, Actual: false), both in PHP and with the old native extension.
  • make tests passes with and without the turbo extension. make phpstan, coding standards, the strict turbo build, smoke.php, signature parity and side-by-side all pass.
  • issue-bot's ../bin/phpstan reports no errors locally.

…losures

A closure invoking its own by-ref variable keeps the creation-time fixpoint
for its by-ref uses, because its invocations cannot all be followed. The
check skipped closures and arrow functions written in the body, so a closure
re-invoking itself from a callback - use (&$runNext) with $runNext() in a
nested closure - was analysed only with the types at its outer invocation.
Nested closures and arrow functions are now scanned for such invocations;
their yields stay their own.

Amp-Thread-ID: https://ampcode.com/threads/T-01a19c12-751a-706e-8462-31643321a689
@ondrejmirtes
ondrejmirtes merged commit 2426bae into 2.3.x Nov 2, 2026
528 checks passed
@ondrejmirtes
ondrejmirtes deleted the closure-byref-nested-self-invocation branch November 2, 2026 12:00
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