Repository navigation
Resolve template types of method calls on a concrete generic static return type - #6695
Conversation
7ac51a6 to
c062fdd
Compare
619e135 to
babb3d5
Compare
|
The last commit is a cherry-pick of #6697 (author kept): |
35cedda to
fd0daee
Compare
|
Rebased on the current The last commit is a cherry-pick of #6729 (author kept): the |
21d27fb to
eedada2
Compare
|
@staabm would you mind taking a look? With phpstan/phpstan#15468 merged, CI here is fully green now. The fix is in |
b5f2604 to
d58614e
Compare
b1ede1d to
9e52a3d
Compare
| # A reflection error, here a class that extends itself, still keeps the result cache from | ||
| # being saved. | ||
| cp loop/Loop.php src/Loop.php | ||
| OUTPUT=$(../bashunit assert exit_code "1" "../../bin/phpstan analyse -vvv --error-format raw") | ||
| echo "$OUTPUT" | ||
| ../bashunit assert contains 'Result cache was not saved because of non-ignorable exception: Reflection error: Circular reference' "$OUTPUT" |
There was a problem hiding this comment.
why do we need to adjust this tests expectations?
There was a problem hiding this comment.
This is not part of the fix: it comes from the last commit, a cherry-pick of #6729 (author kept), because the result-cache-parse-error E2E step fails on 2.3.x since the BetterReflection update, independently of this PR. It drops out of the diff once #6729 is merged and this branch is rebased, so please review it there.
9d475ba to
f73eb93
Compare
|
@staabm fixed. Removed not related changes |
|
@pawell67 please fix //cc @SanderMuller please review |
SanderMuller
left a comment
There was a problem hiding this comment.
I checked 2846823 against 2.3.x (5fb9346).
- The example from the description gives
array<int, string>with this PR, and the base givesarray<TKey of (int|string), TValue>. I also probed a subclass that binds the templates, a generic subclass, a union receiver andnew Box(). Two more probes used a nestedstatic<int, Box<int, TValue>>and a template method that returnsstatic<TKey|int, TValue|TOther>. Every result that changes is now the resolved type, and nothing changes for the worse. ForGSub<float>,$g->concrete()isGSub<string>on both sides. But->all()on it gavearray<int, float>on the base, and it now givesarray<int, string>. - Both new nsrt files fail with the change in
CalledOnTypeUnresolvedMethodPrototypeReflection.phpreverted.bug-12680.phpmatches Ondřej's playground snippet, and the reporter's snippet in phpstan/phpstan#12680 now givesmixedfor both lines instead ofTValue. - Turbo: I built the extension from this head (macOS arm64, PHP 8.5).
smoke.phpandsignature-parity.phppass, and the two nsrt files pass with the extension loaded. With the extension active and the PHP change reverted, my probe still gives the fixed types, so the C++ port carries the fix by itself. I did not build it for other platforms or PHP versions. - The full paratest suite passes with and without the extension (22615 tests).
make phpstanis clean, and so is phpcs on the changed source file. - Performance: cold self-analysis without the extension, 3 interleaved rounds. The median user CPU is 120.9 s on the base and 121.2 s with this PR. The runs took 119.7 to 128.9 s and 119.0 to 130.2 s, so there is no measurable difference.
- CI: the red jobs also fail on #6782, #6783 and #6785. The Turbo compile jobs fail in
zend_globals_macros.h('ZEND_API' does not name a type), and d78c82f on 2.3.x targets that. Its CI was still running when I checked.result-cache-parse-errorfails on every current 2.3.x PR. 87bbf77 on 2.3.x adds the same lint comment tobug-11507.php, so after a rebase that line drops out of the diff.
Two small things in the description. It still says the C++ mirror is not ported and the suite did not run with the extension, but c6d2009 ports it. And phpstan/phpstan#12680 is still open, so a Closes line could go in if this is the fix for it.
…eturn type A return type like `static<int, string>` (no template type in its arguments) was turned into a GenericObjectType that kept the ClassReflection of the declaring class, so methods called on the result saw unresolved TKey/TValue (`array<TKey, TValue>`). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Mirrors CalledOnTypeUnresolvedMethodPrototypeReflection::transformStaticType() from the previous commit. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2846823 to
2c8d328
Compare
|
thank you! |
A
static<...>return type whose arguments reference no template type is not resolved on a generic receiver:CalledOnTypeUnresolvedMethodPrototypeReflection::transformStaticType()turns theGenericStaticTypeintogetStaticObjectType(), which hands the declaring class'ClassReflection(without the type arguments) to theGenericObjectType. When an argument contains a template,traverse()recreates the object and the reflection is resolved lazily, which is why only the fully concrete case breaks. I reproduced this on 2.2.14 and 2.3.0, so it is not a regression.It is visible in Laravel's
Collection(@return static<int, mixed>onflatten(),collapse(),values(), …):$collection->flatten()->merge($x)leaksTKey/TValue, which Larastan has to baseline in its monica E2E.The fix builds the object without the stale reflection in
transformStaticType()only. I first changedGenericStaticType::getStaticObjectType(), but that also changesnew static(...)(NewHandler) and drops the expected error ofReturnTypeRuleTest::testBug4590, so I narrowed it to the method return type resolution.The
turbo-extmirror ofCalledOnTypeUnresolvedMethodPrototypeReflectionis ported in the same change, and the new nsrt files pass with and without the extension loaded.Closes phpstan/phpstan#12680
🤖 Generated with Claude Code