fix(translator): allow overrides to add a by-ref return (#53) --skip-tests
* test(phpunit): anchor TypePhp autoloading to the current checkout
A git worktree shares vendor/ with the primary checkout via a symlink, and
Composer's generated autoloader resolves the TypePhp\ prefix relative to the
realpath of vendor/. The suite then silently loads and tests the OTHER
checkout's src/ tree. Prepend an autoloader anchored to this checkout so the
tests always exercise the sources they ship with; in a standalone checkout
this is a no-op.
* fix(translator): allow overrides to add a by-ref return
Zend's inheritance check treats return-by-reference as covariant
(zend_do_perform_implementation_check): an error is raised only when the
parent returns by reference and the child does not. The child adding `&`
is a strictly stronger guarantee and is accepted:
class A { public function f(): array {} }
class B extends A { public function &f(): array {} } // OK in Zend
validateMethodOverrideSignature compared returnsByRef with exact equality,
rejecting this valid program. Make the check one-directional; dropping a
parent's by-ref return remains fatal.
master
parent
407953c094
commit
0c69b8b357
5 changed files with 81 additions and 1 deletions
@ -0,0 +1,19 @@ |
||||
<?php |
||||
class A |
||||
{ |
||||
public function f(): array |
||||
{ |
||||
return []; |
||||
} |
||||
} |
||||
|
||||
class B extends A |
||||
{ |
||||
public function &f(): array |
||||
{ |
||||
static $a = []; |
||||
return $a; |
||||
} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,19 @@ |
||||
<?php |
||||
class A |
||||
{ |
||||
public function &f(): array |
||||
{ |
||||
static $a = []; |
||||
return $a; |
||||
} |
||||
} |
||||
|
||||
class B extends A |
||||
{ |
||||
public function f(): array |
||||
{ |
||||
return []; |
||||
} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,24 @@ |
||||
<?php |
||||
|
||||
use TypePhp\Exception\TestError; |
||||
|
||||
/** |
||||
* Zend treats by-ref returns as covariant in overrides: a child method may add |
||||
* `&` to its return (callers expecting a value still work), but it must not |
||||
* drop a by-ref return promised by the parent contract. |
||||
*/ |
||||
class MethodOverrideByRefReturnTest extends BaseTest |
||||
{ |
||||
public function testOverrideMayAddByRefReturn(): void |
||||
{ |
||||
$this->compile('override_byref_return_added.php'); |
||||
} |
||||
|
||||
public function testOverrideCannotDropByRefReturn(): void |
||||
{ |
||||
$this->exec( |
||||
'Declaration of `B::f()` must be compatible with `A::f()`', |
||||
'override_byref_return_dropped.php', |
||||
); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue