feat(types): fluent wither-return covariance through descendant interfaces#518
Conversation
…faces A PSR-FIG immutable "wither" (`withHeader(): MessageInterface`) declares an ancestor interface as its return type but carries `@return static`. Called through a descendant-interface receiver, the result must keep the receiver's type so a subsequent descendant-only call type-checks — matching PHP's runtime `clone $this`. Both inference passes resolve a `with*` method whose declared return is a strict ancestor of the receiver interface back to the receiver: the checker (`infer_method_call_on_interface_type`) and the EIR lowering (`method_call_result_type`). Both are required — the checker alone leaves the EIR backend re-resolving to the ancestor and failing native codegen. Also accepts `Array(Mixed)` where an `AssocArray` parameter is declared (an untyped list literal passed to an `array<int, mixed>` param). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1db40f1 to
07e7347
Compare
|
Closing as superseded. #473 now implements sound covariant self-return validation for interface methods and removed the |
|
Superseded by #578. #518 identified the real PSR-style fluent-chain failure and provided the original reproducer. The replacement PR carries that reproducer and contributor credit forward, but removes the method-name heuristic and addresses the underlying type-system issue instead:
Keeping #518 closed in favor of #578 so review and CI continue on the complete replacement. Thanks @chadmandoo for surfacing the original issue and reproducer. |
What
A PSR-FIG immutable "wither" method (
withHeader(): MessageInterface) declares an ancestor interface as its return type but carries@return static. Called through a descendant-interface receiver, the result must keep the receiver's type so a subsequent descendant-only call type-checks — matching PHP's runtimeclone $thisand PHPStan's@return static.Previously rejected:
How
Both inference passes resolve a
with*method whose declared return is a strict ancestor of the receiver interface back to the receiver:infer_method_call_on_interface_typemethod_call_result_typeBoth are required: the checker fix alone leaves the EIR backend re-resolving the chained call to the ancestor and failing native codegen (
interface method call to unknown method Message::withMethod).Also accepts
Array(Mixed)where anAssocArrayparameter is declared (an untyped list literal passed to anarray<int, mixed>param).Test
test_interface_wither_ancestor_return_stays_receiver— aMessage/Request extends Messagewither chain, byte-parity vs PHP 8.5 (chain(new Req())printsPOST).🤖 Generated with Claude Code