diff --git a/src/Type/ArrayType.php b/src/Type/ArrayType.php index 288caefdc6..2976a325b5 100644 --- a/src/Type/ArrayType.php +++ b/src/Type/ArrayType.php @@ -410,6 +410,24 @@ public function setExistingOffsetValueType(Type $offsetType, Type $valueType): T } } + if ( + $this->itemType->isArray()->yes() + && $valueType->isArray()->yes() + && $this->itemType->getIterableValueType()->isConstantArray()->yes() + && $valueType->getIterableValueType()->isConstantArray()->yes() + ) { + $newItemType = $this->itemType->setExistingOffsetValueType( + $valueType->getIterableKeyType(), + $valueType->getIterableValueType(), + ); + if ($newItemType !== $this->itemType) { + return new self( + $this->keyType, + $newItemType, + ); + } + } + return new self( $this->keyType, TypeCombinator::union($this->itemType, $valueType), diff --git a/tests/PHPStan/Analyser/nsrt/bug-13637.php b/tests/PHPStan/Analyser/nsrt/bug-13637.php new file mode 100644 index 0000000000..7c2bdd3cf7 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13637.php @@ -0,0 +1,45 @@ +>> +*/ +function doesNotWork() : array { + $final = []; + + for ($i = 0; $i < 5; $i++) { + $j = $i * 2; + $k = $j +1; + $l = $i * 3; + $final[$i][$j][$k]['abc'] = $i; + $final[$i][$j][$k]['def'] = $i; + $final[$i][$j][$k]['ghi'] = $i; + + assertType("array{abc: int<0, 4>, def: int<0, 4>, ghi: int<0, 4>}", $final[$i][$j][$k]); + } + + return $final; +} + +/** +* @return array> +*/ +function thisWorks() : array { + $final = []; + + for ($i = 0; $i < 5; $i++) { + $j = $i * 2; + $k = $j +1; + $l = $i * 3; + $final[$i][$j]['abc'] = $i; + $final[$i][$j]['def'] = $i; + $final[$i][$j]['ghi'] = $i; + + assertType("array{abc: int<0, 4>, def: int<0, 4>, ghi: int<0, 4>}", $final[$i][$j]); + } + + return $final; +}