fix(translator): let a trailing child variadic absorb parent parameters (#54)
Zend's zend_do_perform_implementation_check does not compare variadic-ness
per position. Its rules are:
- a variadic parent requires a variadic child (unbounded contract);
- a trailing child variadic stands in for every remaining parent
position (decorator pattern), with the variadic's type checked for
contravariance against each covered parent parameter and by-ref-ness
matched per position;
- when the parent is variadic, extra child parameters are validated
against the parent's variadic slot.
validateMethodOverrideSignature required an exact per-position variadic
match, rejecting valid programs such as parent f(int $a, int $b)
overridden by f(int ...$args). Rework the position loop per the Zend
rules; the required-argument-count and extra-optional-parameter checks
are unchanged.
The pre-existing testVariadicMismatch expectation (untyped f($x)
overridden by f(...$x) must fail) contradicts Zend 8.4, which accepts
it; the test now asserts the program compiles.
master
parent
aa1da2eb54
commit
c6e6997db8
8 changed files with 152 additions and 10 deletions
@ -0,0 +1,12 @@ |
|||||||
|
<?php |
||||||
|
class A |
||||||
|
{ |
||||||
|
public function f(int ...$args): void {} |
||||||
|
} |
||||||
|
|
||||||
|
class B extends A |
||||||
|
{ |
||||||
|
public function f(int $a = 1, int ...$rest): void {} |
||||||
|
} |
||||||
|
|
||||||
|
function main() {} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
<?php |
||||||
|
class A |
||||||
|
{ |
||||||
|
public function f(int ...$args): void {} |
||||||
|
} |
||||||
|
|
||||||
|
class B extends A |
||||||
|
{ |
||||||
|
public function f(int $a = 0): void {} |
||||||
|
} |
||||||
|
|
||||||
|
function main() {} |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
<?php |
||||||
|
class A |
||||||
|
{ |
||||||
|
public function f(int $a, int $b): int |
||||||
|
{ |
||||||
|
return $a + $b; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class B extends A |
||||||
|
{ |
||||||
|
public function f(int ...$args): int |
||||||
|
{ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class C extends A |
||||||
|
{ |
||||||
|
public function f(int $a, int ...$rest): int |
||||||
|
{ |
||||||
|
return $a; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() {} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
<?php |
||||||
|
class A |
||||||
|
{ |
||||||
|
public function f(int $a, string $b): void {} |
||||||
|
} |
||||||
|
|
||||||
|
class B extends A |
||||||
|
{ |
||||||
|
public function f(int ...$args): void {} |
||||||
|
} |
||||||
|
|
||||||
|
function main() {} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
<?php |
||||||
|
class A |
||||||
|
{ |
||||||
|
public function f(int &$a): void {} |
||||||
|
} |
||||||
|
|
||||||
|
class B extends A |
||||||
|
{ |
||||||
|
public function f(int ...$args): void {} |
||||||
|
} |
||||||
|
|
||||||
|
function main() {} |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use TypePhp\Exception\TestError; |
||||||
|
|
||||||
|
/** |
||||||
|
* Zend's inheritance check lets a trailing child variadic stand in for every |
||||||
|
* remaining parent parameter position (the decorator pattern), provided the |
||||||
|
* variadic's type is contravariant-compatible with each covered position and |
||||||
|
* by-ref-ness matches. A variadic parent still requires a variadic child, and |
||||||
|
* when the parent is variadic every extra child parameter is validated against |
||||||
|
* the parent's variadic slot. |
||||||
|
*/ |
||||||
|
class MethodOverrideVariadicTest extends BaseTest |
||||||
|
{ |
||||||
|
public function testTrailingChildVariadicAbsorbsParentParameters(): void |
||||||
|
{ |
||||||
|
$this->compile('override_variadic_absorbs_params.php'); |
||||||
|
} |
||||||
|
|
||||||
|
public function testChildVariadicTypeMustCoverEveryAbsorbedPosition(): void |
||||||
|
{ |
||||||
|
$this->exec( |
||||||
|
'Declaration of `B::f()` must be compatible with `A::f()`', |
||||||
|
'override_variadic_bad_type.php', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function testChildVariadicMustMatchByRefOfAbsorbedPosition(): void |
||||||
|
{ |
||||||
|
$this->exec( |
||||||
|
'Declaration of `B::f()` must be compatible with `A::f()`', |
||||||
|
'override_variadic_byref_mismatch.php', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function testVariadicParentRequiresVariadicChild(): void |
||||||
|
{ |
||||||
|
$this->exec( |
||||||
|
'Declaration of `B::f()` must be compatible with `A::f()`', |
||||||
|
'override_parent_variadic_child_not.php', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function testExtraChildParametersCheckedAgainstParentVariadic(): void |
||||||
|
{ |
||||||
|
$this->compile('override_parent_variadic_child_extra.php'); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue