Fix phpstan/phpstan#13440: Seemingly same type reports as invalid/incompatible.#5073
Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Closed
Fix phpstan/phpstan#13440: Seemingly same type reports as invalid/incompatible.#5073phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
…tity - TemplateMixedType and TemplateStrictMixedType with same scope+name represent the same template parameter but fail equals() due to different concrete classes - At level 9+ transformCommonType converts TemplateMixedType to TemplateStrictMixedType in accepting type but not in accepted closure params - Added fallback check in isValidVariance() for invariant templates: if both sides are TemplateType with matching scope and name, treat as equal - New regression test in tests/PHPStan/Rules/Classes/data/bug-13440.php Closes phpstan/phpstan#13440
Contributor
|
When dumping $a and $b the issue was kinda clear The second one was transformed into a strict type by the RuleLevelHelper but not the first one. |
a846da0 to
63ad626
Compare
staabm
approved these changes
Feb 28, 2026
Contributor
staabm
left a comment
There was a problem hiding this comment.
I think it makes sense. I am not generics expert though.
Contributor
|
I don't think that's the right fix ; I'll open a new PR |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a false positive where PHPStan reports "Parameter #2 $cb of class Box constructor expects Closure(Foo): TNewReturn, Closure(Foo): TNewReturn given" — the expected and given types are identical but flagged as incompatible due to an invariance check failure. This only occurs at level 9+ (
checkExplicitMixed: true).Changes
src/Type/Generic/TemplateTypeVariance.php— in theisValidVariance()method's invariant branch, added a fallback check: ifequals()fails but both types areTemplateTypeinstances with the same scope and name, treat them as equaltests/PHPStan/Rules/Classes/data/bug-13440.php— regression test data filetests/PHPStan/Rules/Classes/InstantiationRuleTest.php— addedcheckExplicitMixedproperty andtestBug13440()test methodRoot cause
At level 9+,
RuleLevelHelper::transformCommonType()convertsTemplateMixedTypetoTemplateStrictMixedType(viatoStrictMixedType()) to enforce stricter mixed type checking. This transformation preserves the template identity (name, scope, variance) but changes the concrete class.The accepting type (constructor parameter) gets fully transformed, so
TValinsideClosure(Foo<TVal>): TNewReturnbecomes aTemplateStrictMixedType. However,transformAcceptedType()deliberately does NOT transform closure parameter types (for sound contravariance reasons), so the given type keepsTValasTemplateMixedType.When the invariance check in
isValidVariance()compares these two template types usingequals(), it fails becauseTemplateTypeTrait::equals()checks$type instanceof self— andTemplateMixedTypeis not an instance ofTemplateStrictMixedType(or vice versa). The fix adds a secondary check: if both sides areTemplateTypeinstances with the same scope and name, they represent the same template parameter and should pass the invariance check.Test
Added a regression test that reproduces the issue: a generic class
Box<TVal, TReturn>with a method that callsnew self($this->val, $cb)where$cbis a closure with a generic type parameterFoo<TVal>. WithcheckExplicitMixed: true, this previously produced a false positive argument.type error.Fixes phpstan/phpstan#13440