@@ -272,22 +272,21 @@ public function specifyTypesInCondition(
272272 ) {
273273 $ argType = $ scope ->getType ($ expr ->right ->getArgs ()[0 ]->value );
274274
275- if ($ argType instanceof UnionType) {
276- $ sizeType = null ;
277- if ($ leftType instanceof ConstantIntegerType) {
278- if ($ orEqual ) {
279- $ sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo ($ leftType ->getValue ());
280- } else {
281- $ sizeType = IntegerRangeType::createAllGreaterThan ($ leftType ->getValue ());
282- }
283- } elseif ($ leftType instanceof IntegerRangeType) {
284- $ sizeType = $ leftType ;
275+ if ($ leftType instanceof ConstantIntegerType) {
276+ if ($ orEqual ) {
277+ $ sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo ($ leftType ->getValue ());
278+ } else {
279+ $ sizeType = IntegerRangeType::createAllGreaterThan ($ leftType ->getValue ());
285280 }
281+ } elseif ($ leftType instanceof IntegerRangeType) {
282+ $ sizeType = $ leftType ->shift ($ offset );
283+ } else {
284+ $ sizeType = $ leftType ;
285+ }
286286
287- $ narrowed = $ this ->narrowUnionByArraySize ($ expr ->right , $ argType , $ sizeType , $ context , $ scope , $ expr );
288- if ($ narrowed !== null ) {
289- return $ narrowed ;
290- }
287+ $ specifiedTypes = $ this ->specifyTypesForCountFuncCall ($ expr ->right , $ argType , $ sizeType , $ context , $ scope , $ expr );
288+ if ($ specifiedTypes !== null ) {
289+ $ result = $ result ->unionWith ($ specifiedTypes );
291290 }
292291
293292 if (
@@ -1046,115 +1045,95 @@ public function specifyTypesInCondition(
10461045 return (new SpecifiedTypes ([], []))->setRootExpr ($ expr );
10471046 }
10481047
1049- private function narrowUnionByArraySize (FuncCall $ countFuncCall , UnionType $ argType , ?Type $ sizeType , TypeSpecifierContext $ context , Scope $ scope , ?Expr $ rootExpr ): ?SpecifiedTypes
1048+ private function specifyTypesForCountFuncCall (
1049+ FuncCall $ countFuncCall ,
1050+ Type $ type ,
1051+ Type $ sizeType ,
1052+ TypeSpecifierContext $ context ,
1053+ Scope $ scope ,
1054+ Expr $ rootExpr ,
1055+ ): ?SpecifiedTypes
10501056 {
1051- if ($ sizeType === null ) {
1052- return null ;
1053- }
1054-
10551057 if (count ($ countFuncCall ->getArgs ()) === 1 ) {
10561058 $ isNormalCount = TrinaryLogic::createYes ();
10571059 } else {
10581060 $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
1059- $ isNormalCount = (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->result ->or ($ argType ->getIterableValueType ()->isArray ()->negate ());
1061+ $ isNormalCount = (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->result ->or ($ type ->getIterableValueType ()->isArray ()->negate ());
10601062 }
10611063
1064+ $ isList = $ type ->isList ();
10621065 if (
1063- $ isNormalCount ->yes ()
1064- && $ argType ->isConstantArray ()->yes ()
1066+ !$ isNormalCount ->yes ()
1067+ || (!$ type ->isConstantArray ()->yes () && !$ isList ->yes ())
1068+ || $ type ->isIterableAtLeastOnce ()->no () // array{} cannot be used for further narrowing
10651069 ) {
1066- $ result = [];
1067- foreach ($ argType ->getTypes () as $ innerType ) {
1068- $ arraySize = $ innerType ->getArraySize ();
1069- $ isSize = $ sizeType ->isSuperTypeOf ($ arraySize );
1070- if ($ context ->truthy ()) {
1071- if ($ isSize ->no ()) {
1072- continue ;
1073- }
1074-
1075- $ constArray = $ this ->turnListIntoConstantArray ($ countFuncCall , $ innerType , $ sizeType , $ scope );
1076- if ($ constArray !== null ) {
1077- $ innerType = $ constArray ;
1078- }
1079- }
1080- if ($ context ->falsey ()) {
1081- if (!$ isSize ->yes ()) {
1082- continue ;
1083- }
1084- }
1085-
1086- $ result [] = $ innerType ;
1087- }
1088-
1089- return $ this ->create ($ countFuncCall ->getArgs ()[0 ]->value , TypeCombinator::union (...$ result ), $ context , $ scope )->setRootExpr ($ rootExpr );
1070+ return null ;
10901071 }
10911072
1092- return null ;
1093- }
1094-
1095- private function turnListIntoConstantArray (FuncCall $ countFuncCall , Type $ type , Type $ sizeType , Scope $ scope ): ?Type
1096- {
1097- $ argType = $ scope ->getType ($ countFuncCall ->getArgs ()[0 ]->value );
1098-
1099- if (count ($ countFuncCall ->getArgs ()) === 1 ) {
1100- $ isNormalCount = TrinaryLogic::createYes ();
1101- } else {
1102- $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
1103- $ isNormalCount = (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->result ->or ($ argType ->getIterableValueType ()->isArray ()->negate ());
1104- }
1073+ $ resultTypes = [];
1074+ foreach ($ type ->getArrays () as $ arrayType ) {
1075+ $ isSizeSuperTypeOfArraySize = $ sizeType ->isSuperTypeOf ($ arrayType ->getArraySize ());
1076+ if ($ isSizeSuperTypeOfArraySize ->no ()) {
1077+ continue ;
1078+ }
11051079
1106- if (
1107- $ isNormalCount ->yes ()
1108- && $ type ->isList ()->yes ()
1109- && $ sizeType instanceof ConstantIntegerType
1110- && $ sizeType ->getValue () < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1111- ) {
1112- // turn optional offsets non-optional
1113- $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1114- for ($ i = 0 ; $ i < $ sizeType ->getValue (); $ i ++) {
1115- $ offsetType = new ConstantIntegerType ($ i );
1116- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
1080+ if ($ context ->falsey () && $ isSizeSuperTypeOfArraySize ->maybe ()) {
1081+ continue ;
11171082 }
1118- return $ valueTypesBuilder ->getArray ();
1119- }
11201083
1121- if (
1122- $ isNormalCount ->yes ()
1123- && $ type ->isList ()->yes ()
1124- && $ sizeType instanceof IntegerRangeType
1125- && $ sizeType ->getMin () !== null
1126- ) {
1127- // turn optional offsets non-optional
1128- $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1129- for ($ i = 0 ; $ i < $ sizeType ->getMin (); $ i ++) {
1130- $ offsetType = new ConstantIntegerType ($ i );
1131- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
1132- }
1133- if ($ sizeType ->getMax () !== null ) {
1134- for ($ i = $ sizeType ->getMin (); $ i < $ sizeType ->getMax (); $ i ++) {
1084+ if (
1085+ $ isList ->yes ()
1086+ && $ sizeType instanceof ConstantIntegerType
1087+ && $ sizeType ->getValue () < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1088+ ) {
1089+ // turn optional offsets non-optional
1090+ $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1091+ for ($ i = 0 ; $ i < $ sizeType ->getValue (); $ i ++) {
11351092 $ offsetType = new ConstantIntegerType ($ i );
1136- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), true );
1093+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ arrayType ->getOffsetValueType ($ offsetType ));
11371094 }
1138- } elseif ($ type ->isConstantArray ()->yes ()) {
1139- for ($ i = $ sizeType ->getMin ();; $ i ++) {
1095+ $ resultTypes [] = $ valueTypesBuilder ->getArray ();
1096+ continue ;
1097+ }
1098+
1099+ if (
1100+ $ isList ->yes ()
1101+ && $ sizeType instanceof IntegerRangeType
1102+ && $ sizeType ->getMin () !== null
1103+ ) {
1104+ // turn optional offsets non-optional
1105+ $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1106+ for ($ i = 0 ; $ i < $ sizeType ->getMin (); $ i ++) {
11401107 $ offsetType = new ConstantIntegerType ($ i );
1141- $ hasOffset = $ type ->hasOffsetValueType ($ offsetType );
1142- if ($ hasOffset ->no ()) {
1143- break ;
1108+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ arrayType ->getOffsetValueType ($ offsetType ));
1109+ }
1110+ if ($ sizeType ->getMax () !== null ) {
1111+ for ($ i = $ sizeType ->getMin (); $ i < $ sizeType ->getMax (); $ i ++) {
1112+ $ offsetType = new ConstantIntegerType ($ i );
1113+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ arrayType ->getOffsetValueType ($ offsetType ), true );
11441114 }
1145- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), !$ hasOffset ->yes ());
1115+ } elseif ($ arrayType ->isConstantArray ()->yes ()) {
1116+ for ($ i = $ sizeType ->getMin ();; $ i ++) {
1117+ $ offsetType = new ConstantIntegerType ($ i );
1118+ $ hasOffset = $ arrayType ->hasOffsetValueType ($ offsetType );
1119+ if ($ hasOffset ->no ()) {
1120+ break ;
1121+ }
1122+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ arrayType ->getOffsetValueType ($ offsetType ), !$ hasOffset ->yes ());
1123+ }
1124+ } else {
1125+ $ resultTypes [] = TypeCombinator::intersect ($ arrayType , new NonEmptyArrayType ());
1126+ continue ;
11461127 }
1147- } else {
1148- return null ;
1149- }
11501128
1151- $ arrayType = $ valueTypesBuilder ->getArray ();
1152- if ($ arrayType ->isIterableAtLeastOnce ()->yes ()) {
1153- return $ arrayType ;
1129+ $ resultTypes [] = $ valueTypesBuilder ->getArray ();
1130+ continue ;
11541131 }
1132+
1133+ $ resultTypes [] = $ arrayType ;
11551134 }
11561135
1157- return null ;
1136+ return $ this -> create ( $ countFuncCall -> getArgs ()[ 0 ]-> value , TypeCombinator:: union (... $ resultTypes ), $ context , $ scope )-> setRootExpr ( $ rootExpr ) ;
11581137 }
11591138
11601139 private function specifyTypesForConstantBinaryExpression (
@@ -2186,36 +2165,20 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
21862165 );
21872166 }
21882167
2189- if ($ argType instanceof UnionType) {
2190- $ narrowed = $ this ->narrowUnionByArraySize ($ unwrappedLeftExpr , $ argType , $ rightType , $ context , $ scope , $ expr );
2191- if ($ narrowed !== null ) {
2192- return $ narrowed ;
2193- }
2168+ $ specifiedTypes = $ this ->specifyTypesForCountFuncCall ($ unwrappedLeftExpr , $ argType , $ rightType , $ context , $ scope , $ expr );
2169+ if ($ specifiedTypes !== null ) {
2170+ return $ specifiedTypes ;
21942171 }
21952172
2196- if ($ context ->truthy ()) {
2197- if ($ argType ->isArray ()->yes ()) {
2198- if (
2199- $ argType ->isConstantArray ()->yes ()
2200- && $ rightType ->isSuperTypeOf ($ argType ->getArraySize ())->no ()
2201- ) {
2202- return $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NeverType (), $ context , $ scope )->setRootExpr ($ expr );
2203- }
2204-
2205- $ funcTypes = $ this ->create ($ unwrappedLeftExpr , $ rightType , $ context , $ scope )->setRootExpr ($ expr );
2206- $ constArray = $ this ->turnListIntoConstantArray ($ unwrappedLeftExpr , $ argType , $ rightType , $ scope );
2207- if ($ constArray !== null ) {
2208- return $ funcTypes ->unionWith (
2209- $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , $ constArray , $ context , $ scope )->setRootExpr ($ expr ),
2210- );
2211- } elseif (IntegerRangeType::fromInterval (1 , null )->isSuperTypeOf ($ rightType )->yes ()) {
2212- return $ funcTypes ->unionWith (
2213- $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NonEmptyArrayType (), $ context , $ scope )->setRootExpr ($ expr ),
2214- );
2215- }
2216-
2217- return $ funcTypes ;
2173+ if ($ context ->truthy () && $ argType ->isArray ()->yes ()) {
2174+ $ funcTypes = $ this ->create ($ unwrappedLeftExpr , $ rightType , $ context , $ scope )->setRootExpr ($ expr );
2175+ if (IntegerRangeType::fromInterval (1 , null )->isSuperTypeOf ($ rightType )->yes ()) {
2176+ return $ funcTypes ->unionWith (
2177+ $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NonEmptyArrayType (), $ context , $ scope )->setRootExpr ($ expr ),
2178+ );
22182179 }
2180+
2181+ return $ funcTypes ;
22192182 }
22202183 }
22212184
0 commit comments