Skip to content

Regression: Closure::bind() scrambles arguments — TypeError closure vs newThis (Zend/zend_closures.c) #17633

Closed

Description

@PurHur

Category

Regression · php-src-strict · language / closures

Problem

Closure::bind($closure, $newThis, $scope) must return a closure bound to $newThis with $scope, including access to private methods on that scope. The VM builtin mis-dispatches operands: argument #1 is treated as the object (A) instead of the closure, yielding TypeError: Argument #1 ($closure) must be of type Closure, A given.

Repro Zend 8.2+ VM (2026-07-10)
Closure::bind(function(){ return $this->f(); }, new A(), A::class) where A::f() is private prints ok TypeError on bind()
Same with Closure::bindTo(...) binds + invokes (verify parity)

php-src reference

PHP implementation target

  • ext/standard/ closure builtins + lib/VM/ closure bind path — fix operand order / InternalFunction signature for Closure::bind
  • JIT/AOT: shared lowering for Closure::bind / bindTo if emitted; VM fix is sufficient first gate

Repro

./script/docker-exec.sh -- bash -lc 'php bin/vm.php test/repro/maintainer_gap_closure_bind_private_method.php'
# Zend: ok
# VM: TypeError Closure::bind(): Argument #1 ($closure) must be of type Closure, A given

Done when

  • Closure::bind() and Closure::bindTo() accept (closure, object, scope) in php-src order and invoke private methods when scope matches
  • VM/JIT/AOT agree when JIT path exists
  • Compliance .phpt guard for bind + private method access
  • php-src-strict; no php-compiler-strict shortcut

Activity

  1. added
    bugSomething isn't working
    area:vmVirtual machine
    implementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claim
    on Jul 10, 2026
  2. PurHur commented on Jul 10, 2026

    @PurHur
    OwnerAuthor

    claim: PHP Runtime → PHP — starting this run.

  3. PurHur commented on Jul 10, 2026

    @PurHur
    OwnerAuthor

    Merged via #17635.

    Root cause: slotForStaticClosureBindInlineClosureArg() backward scan hit inline new C() before the closure producer when CFG order is Closure → New_ → ClassConstFetch → StaticCall, wiring newThis into arg #0.

    Fix: Skip New_ siblings when scanning backward for the inline closure slot (lib/Compiler.php).

    Verification:

    php bin/vm.php test/repro/maintainer_gap_closure_bind_private_method.php  # ok
    vendor/bin/phpunit test/compliance/ClosureVMTest.php test/unit/ClosureCompileTest.php  # OK (45 tests)
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:vmVirtual machinebugSomething isn't workingimplementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claimphase-2:languagePhase 2 – language features

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions