Skip to content

Conversation

@ondrejmirtes
Copy link
Member

No description provided.

@ondrejmirtes
Copy link
Member Author

/cc @VincentLanglet This change solves a few issues, including your own: https://github.com/phpstan/phpstan-src/actions/runs/19044800760

Unfortunately it introduces also some problems which I can't dig into right now. Could you please debug why some existing tests are failing and some errors reappear?

I think some of it might be related to #4372 but not sure.

@ondrejmirtes
Copy link
Member Author

Also /cc @staabm because you might have touched this more recently 😊

Comment on lines 696 to 709
if ($offsetType !== null) {
$constantScalars = $offsetType->getConstantScalarTypes();
$constantScalarsCount = count($constantScalars);
if ($constantScalarsCount > 1 && $constantScalarsCount < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) {
$arrays = [];
foreach ($constantScalars as $constantScalar) {
$builder = ConstantArrayTypeBuilder::createFromConstantArray($this);
$builder->setOffsetValueType($constantScalar, $valueType);
$arrays[] = $builder->getArray();
}

return TypeCombinator::union($this, ...$arrays);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder, whether this code should be moved into the impl of ConstantArrayTypeBuilder->setOffsetValueType()?

Copy link
Member Author

Choose a reason for hiding this comment

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

It should be handled there but this code should not be copied, but adapted (it's not so easy to do it there). I just wanted to try a quick PoC first.

return new ErrorType(); // undefined offset
}

public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
Copy link
Contributor

@staabm staabm Nov 3, 2025

Choose a reason for hiding this comment

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

looking at this unionValues parameter, makes me feel we nearly everytime ignore it.

I wonder whether this bool represents the same 2 use-cases as setOffsetValueType() vs. setExistingOffsetValueType().

like unionValues=true is the same as setOffsetValueType()
and unionValues=false is the same as setExistingOffsetValueType()

Copy link
Member Author

Choose a reason for hiding this comment

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

The unionValues parameter is some hack I came up with, there's no deeper thought in it and it's likely the proper solution is different.

Copy link
Contributor

Choose a reason for hiding this comment

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

The unionValues parameter is some hack I came up with,

What is the purpose of this hack ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I did some research yesterday. it was introduced with 5d64483

$arrays[] = $builder->getArray();
}

return TypeCombinator::union($this, ...$arrays);
Copy link
Contributor

Choose a reason for hiding this comment

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

I might be tired but why

				return TypeCombinator::union($this, ...$arrays);

and not

Suggested change
return TypeCombinator::union($this, ...$arrays);
return TypeCombinator::union(...$arrays);

?

Copy link
Member Author

Choose a reason for hiding this comment

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

I was also tired, it makes sense without the $this 😊 Updated that.

if ($offsetType !== null) {
$constantScalars = $offsetType->getConstantScalarTypes();
$constantScalarsCount = count($constantScalars);
if ($constantScalarsCount > 1 && $constantScalarsCount < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need similar handling for integer ranges with less than array-limit elements?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that would make sense!

@VincentLanglet
Copy link
Contributor

Looking at failure

1) PHPStan\Rules\Classes\InstantiationRuleTest::testBug7594
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'
+'55: Parameter #1 $boolAbilities of class Bug7594\HelloWorld constructor expects array<0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17, bool>, array<int<0, max>, bool> given.
 '

=> Related to for loop, which generalize the result computed after the second loop.

if ($count >= self::GENERALIZE_AFTER_ITERATION) {
$bodyScope = $prevScope->generalizeWith($bodyScope);
}

And looking at all the other issues, I think it's also the fact that for loop generalize result creating less precise ones, except:

4) PHPStan\Analyser\NodeScopeResolverTest::testFile with data set "tests\PHPStan\Analyser\nsrt\constant-array-type-set.php" ('D:\a\phpstan-src\phpstan-src\...et.php')
Failed assertions in D:\a\phpstan-src\phpstan-src\tests\PHPStan\Analyser\nsrt\constant-array-type-set.php:

Line 24:
Expected: array{bool, bool, bool}
Actual:   array{false, false, true}|array{false, true, false}|array{true, false, false}

Line 36:
Expected: array{0: bool, 1: bool, 2: bool, 3?: true}
Actual:   array{false, false, false, true}|array{false, false, true}|array{false, true, false}|array{true, false, false}

Line 42:
Expected: array{bool, bool, false}
Actual:   array{false, true, false}|array{true, false, false}

Line 104:
Expected: array{bool, bool, false}
Actual:   array{false, true, false}|array{true, false, false}

Seems expected to me

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.

4 participants