Skip to content

TypeCombinator returns non-empty-array for union of isIterableAtLeastOnce()->yes() #3937

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 15, 2025

Conversation

staabm
Copy link
Contributor

@staabm staabm commented Apr 15, 2025

a array can be non-empty caused by different accessory types, namely NonEmptyArrayType, HasOffsetType, HasOffsetValueType.

before this PR we only returned a non-empty-array result from TypeCombinator::union() when all elements in the union were using the same accessory types, e.g.

  • union(array&non-empty, array&non-empty)
  • union(array&hasOffset('a'), array&hasOffset('a'))

after this PR we in addition return a non-empty-array for unions of arrays which are non-empty caused by different accessories, e.g.

  • union(array&non-empty, array&hasOffset('a'))
  • union(array&hasOffset('b'), array&hasOffset('a'))
  • union(array&hasOffset('b'), array&hasOffsetValue(4, 'a'))

refs #3924 (comment)

@@ -703,6 +707,10 @@ private static function processArrayAccessoryTypes(array $arrayTypes): array
$commonAccessoryTypes[] = $accessoryType[0];
}

if (TrinaryLogic::createYes()->and(...$isIterableAtLeastOnce)->yes()) {
$commonAccessoryTypes[] = new NonEmptyArrayType();
Copy link
Contributor Author

@staabm staabm Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

atm this can lead to some arrays, which - while processing - have the NonEmptyArrayType 2-times.
when TypeCombinator::union() finally ends, this "duplication" gets normalized away.

we could check at this IF-condition here, whether $commonAccessoryTypes already contains NonEmptyArrayType so we don't add it a 2nd time.
for readability I have chosen not todo this additionaly check. but I am fine with adding it, in case you think we should do it.

@staabm staabm marked this pull request as ready for review April 15, 2025 07:03
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

@staabm staabm force-pushed the non-empty-offsets branch from c9ef256 to 492f775 Compare April 15, 2025 13:09
@ondrejmirtes
Copy link
Member

I'd say this isn't necessary. Intersection type of ArrayType and HasOffsetValueType should already know it's iterable at least once (it's non-empty).

The question is - why the tests you changed already don't result in non-empty-array being expected...

@staabm
Copy link
Contributor Author

staabm commented Apr 15, 2025

The question is - why the tests you changed already don't result in non-empty-array being expected...

before this PR the TypeCombinator only takes accessory types into the final type, when they are contained in every element of the union (except OversizedArrayType)

see

$commonAccessoryTypes = [];
$arrayTypeCount = count($arrayTypes);
foreach ($accessoryTypes as $accessoryType) {
if (count($accessoryType) !== $arrayTypeCount) {
$firstKey = array_key_first($accessoryType);
if ($accessoryType[$firstKey] instanceof OversizedArrayType) {
$commonAccessoryTypes[] = $accessoryType[$firstKey];
}
continue;
}
if ($accessoryType[0] instanceof HasOffsetValueType) {
$commonAccessoryTypes[] = self::union(...$accessoryType);
continue;
}
$commonAccessoryTypes[] = $accessoryType[0];
}

therefore in e.g. TypeCombinator::union(array&hasOffset('b'), array&hasOffsetValue(4, 'a')) the resulting Type is just array (it is loosing all accessories, and therefore it no longer is e.g. non-empty)

@ondrejmirtes
Copy link
Member

Ohhh, I get it, this makes sense then!

@ondrejmirtes ondrejmirtes merged commit 7fd5022 into phpstan:2.1.x Apr 15, 2025
416 of 417 checks passed
@ondrejmirtes
Copy link
Member

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants