@@ -272,22 +272,21 @@ public function specifyTypesInCondition(
272
272
) {
273
273
$ argType = $ scope ->getType ($ expr ->right ->getArgs ()[0 ]->value );
274
274
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 ());
285
280
}
281
+ } elseif ($ leftType instanceof IntegerRangeType) {
282
+ $ sizeType = $ leftType ->shift ($ offset );
283
+ } else {
284
+ $ sizeType = $ leftType ;
285
+ }
286
286
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 );
291
290
}
292
291
293
292
if (
@@ -1046,115 +1045,95 @@ public function specifyTypesInCondition(
1046
1045
return (new SpecifiedTypes ([], []))->setRootExpr ($ expr );
1047
1046
}
1048
1047
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
1050
1056
{
1051
- if ($ sizeType === null ) {
1052
- return null ;
1053
- }
1054
-
1055
1057
if (count ($ countFuncCall ->getArgs ()) === 1 ) {
1056
1058
$ isNormalCount = TrinaryLogic::createYes ();
1057
1059
} else {
1058
1060
$ 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 ());
1060
1062
}
1061
1063
1064
+ $ isList = $ type ->isList ();
1062
1065
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
1065
1069
) {
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 ;
1090
1071
}
1091
1072
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
+ }
1105
1079
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 ;
1117
1082
}
1118
- return $ valueTypesBuilder ->getArray ();
1119
- }
1120
1083
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 ++) {
1135
1092
$ offsetType = new ConstantIntegerType ($ i );
1136
- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), true );
1093
+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ arrayType ->getOffsetValueType ($ offsetType ));
1137
1094
}
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 ++) {
1140
1107
$ 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 );
1144
1114
}
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 ;
1146
1127
}
1147
- } else {
1148
- return null ;
1149
- }
1150
1128
1151
- $ arrayType = $ valueTypesBuilder ->getArray ();
1152
- if ($ arrayType ->isIterableAtLeastOnce ()->yes ()) {
1153
- return $ arrayType ;
1129
+ $ resultTypes [] = $ valueTypesBuilder ->getArray ();
1130
+ continue ;
1154
1131
}
1132
+
1133
+ $ resultTypes [] = $ arrayType ;
1155
1134
}
1156
1135
1157
- return null ;
1136
+ return $ this -> create ( $ countFuncCall -> getArgs ()[ 0 ]-> value , TypeCombinator:: union (... $ resultTypes ), $ context , $ scope )-> setRootExpr ( $ rootExpr ) ;
1158
1137
}
1159
1138
1160
1139
private function specifyTypesForConstantBinaryExpression (
@@ -2186,36 +2165,20 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
2186
2165
);
2187
2166
}
2188
2167
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 ;
2194
2171
}
2195
2172
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
+ );
2218
2179
}
2180
+
2181
+ return $ funcTypes ;
2219
2182
}
2220
2183
}
2221
2184
0 commit comments