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