Preserve TemplateType in IntersectionType::shuffleArray() when intersection is already a list#5694
Merged
ondrejmirtes merged 1 commit intoMay 18, 2026
Conversation
…tersection is already a list - In `IntersectionType::shuffleArray()`, skip calling `shuffleArray()` on `TemplateType` members when the intersection is already a list, since shuffling/sorting a list produces another list with the same structure, so the template identity is preserved. - When the intersection is not a list (e.g. `T&array<string, int>`), template types are still correctly dropped because sorting reindexes keys to list format, changing the array structure. - Update existing test in `sort.php` that asserted the buggy behavior (T being dropped from `T&list<...>` after usort). - Key-preserving sorts (`uasort`, `asort`, `ksort`, etc.) already preserve template types correctly via `makeListMaybe()` since `ArrayType::makeListMaybe()` returns `$this`. - Probed analogous operations (`reverseArray`, `mapValueType`, `popArray`, `shiftArray`, `sliceArray`, `spliceArray`) and confirmed they either already preserve template types or correctly drop them (when the operation genuinely changes array structure).
Contributor
|
@phpstan-bot do we have a similar bug in one of the other |
Member
|
@staabm Sorry, I was faster, feel free to research it 😊 |
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
When an intersection type like
T&list<int>was passed throughshuffleArray()(used forsort,rsort,usort,shuffle), the template typeTwas lost becauseTemplateArrayType::shuffleArray()(inherited fromArrayType) constructs a new plainArrayType, discarding the template identity. Since sorting a list produces another list with the same key structure,Tshould be preserved.Changes
src/Type/IntersectionType.php: ModifiedshuffleArray()to check whether the intersection is already a list. If so,TemplateTypemembers are passed through unchanged (since list→list preserves key structure). Non-list intersections still drop template types correctly.tests/PHPStan/Analyser/nsrt/bug-14631.php: New regression test covering:usort/sort/rsort/shuffleonT&list<int>→ T preservedusort/sort/rsort/shuffleonT&array<string, int>→ T correctly droppeduasort/asort/ksorton both list and non-list intersections → T always preserved (key-preserving sorts)tests/PHPStan/Analyser/nsrt/sort.php: Updated existing assertion from buggy expectation (T dropped) to correct expectation (T preserved).Root cause
IntersectionType::shuffleArray()calledshuffleArray()on every member type unconditionally. ForTemplateArrayType(which extendsArrayType),shuffleArray()creates a freshArrayType+AccessoryArrayListTypeintersection, losing the template identity. The fix recognizes that when the intersection is already a list, shuffling doesn't change the key structure, so template types can safely be kept.Analogous cases probed
reverseArray:ArrayType::reverseArray()returns$this, so template types are already preserved. No bug.makeListMaybe(key-preserving sorts):ArrayType::makeListMaybe()returns$this, so template types are already preserved. No bug.mapValueType,popArray,shiftArray,sliceArray,spliceArray: These operations genuinely change the array's values or structure, so dropping template types is correct behavior. No bug.Test
The regression test in
bug-14631.phpcovers all combinations of sort type (list-making vs key-preserving) and input type (list intersection vs non-list intersection), verifying template type preservation/dropping in each case.Fixes phpstan/phpstan#14631