@@ -272,7 +272,6 @@ public function specifyTypesInCondition(
272272 ) {
273273 $ argType = $ scope ->getType ($ expr ->right ->getArgs ()[0 ]->value );
274274
275- $ sizeType = null ;
276275 if ($ leftType instanceof ConstantIntegerType) {
277276 if ($ orEqual ) {
278277 $ sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo ($ leftType ->getValue ());
@@ -281,6 +280,8 @@ public function specifyTypesInCondition(
281280 }
282281 } elseif ($ leftType instanceof IntegerRangeType) {
283282 $ sizeType = $ leftType ->shift ($ offset );
283+ } else {
284+ $ sizeType = $ leftType ;
284285 }
285286
286287 $ specifiedTypes = $ this ->specifyTypesForCountFuncCall ($ expr ->right , $ argType , $ sizeType , $ context , $ scope , $ expr );
@@ -1044,103 +1045,91 @@ public function specifyTypesInCondition(
10441045 return (new SpecifiedTypes ([], []))->setRootExpr ($ expr );
10451046 }
10461047
1047- private function specifyTypesForCountFuncCall (FuncCall $ countFuncCall , Type $ type , ?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
10481056 {
1049- if ($ sizeType === null ) {
1050- return null ;
1057+ if (count ($ countFuncCall ->getArgs ()) === 1 ) {
1058+ $ isNormalCount = TrinaryLogic::createYes ();
1059+ } else {
1060+ $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
1061+ $ isNormalCount = (new ConstantIntegerType (COUNT_NORMAL ))->isSuperTypeOf ($ mode )->result ->or ($ type ->getIterableValueType ()->isArray ()->negate ());
10511062 }
10521063
1053- if (
1054- $ this ->isFuncCallWithNormalCount ($ countFuncCall , $ scope )->yes ()
1055- && $ type ->isConstantArray ()->yes ()
1056- ) {
1057- $ resultType = TypeTraverser::map ($ type , function (Type $ type , callable $ traverse ) use ($ sizeType , $ context ) {
1058- if ($ type instanceof UnionType) {
1059- return $ traverse ($ type );
1060- }
1061-
1062- $ arraySize = $ type ->getArraySize ();
1063- $ isSize = $ sizeType ->isSuperTypeOf ($ arraySize );
1064- if ($ context ->truthy () && $ isSize ->no ()) {
1065- return new NeverType ();
1066- }
1067- if ($ context ->falsey () && !$ isSize ->yes ()) {
1068- return new NeverType ();
1069- }
1070-
1071- return $ this ->turnListIntoConstantArray ($ type , $ sizeType ) ?? $ type ;
1072- });
1073-
1074- return $ this ->create ($ countFuncCall ->getArgs ()[0 ]->value , $ resultType , $ context , $ scope )->setRootExpr ($ rootExpr );
1064+ if (!$ isNormalCount ->yes () || (!$ type ->isConstantArray ()->yes () && !$ type ->isList ()->yes ())) {
1065+ return null ;
10751066 }
10761067
1077- return null ;
1078- }
1068+ $ resultType = TypeTraverser::map ($ type , static function (Type $ type , callable $ traverse ) use ($ sizeType , $ context ) {
1069+ if ($ type instanceof UnionType) {
1070+ return $ traverse ($ type );
1071+ }
10791072
1080- private function turnListIntoConstantArray (Type $ type , Type $ sizeType ): ?Type
1081- {
1082- if (
1083- $ type ->isList ()->yes ()
1084- && $ sizeType instanceof ConstantIntegerType
1085- && $ sizeType ->getValue () < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1086- ) {
1087- // turn optional offsets non-optional
1088- $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1089- for ($ i = 0 ; $ i < $ sizeType ->getValue (); $ i ++) {
1090- $ offsetType = new ConstantIntegerType ($ i );
1091- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
1073+ $ isSizeSuperTypeOfArraySize = $ sizeType ->isSuperTypeOf ($ type ->getArraySize ());
1074+ if ($ context ->truthy () && $ isSizeSuperTypeOfArraySize ->no ()) {
1075+ return new NeverType ();
1076+ }
1077+ if ($ context ->falsey () && !$ isSizeSuperTypeOfArraySize ->yes ()) {
1078+ return new NeverType ();
10921079 }
1093- return $ valueTypesBuilder ->getArray ();
1094- }
10951080
1096- if (
1097- $ type ->isList ()->yes ()
1098- && $ sizeType instanceof IntegerRangeType
1099- && $ sizeType ->getMin () !== null
1100- ) {
1101- // turn optional offsets non-optional
1102- $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1103- for ($ i = 0 ; $ i < $ sizeType ->getMin (); $ i ++) {
1104- $ offsetType = new ConstantIntegerType ($ i );
1105- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
1106- }
1107- if ($ sizeType ->getMax () !== null ) {
1108- for ($ i = $ sizeType ->getMin (); $ i < $ sizeType ->getMax (); $ i ++) {
1109- $ offsetType = new ConstantIntegerType ($ i );
1110- $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), true );
1111- }
1112- } elseif ($ type ->isConstantArray ()->yes ()) {
1113- for ($ i = $ sizeType ->getMin ();; $ i ++) {
1114- $ offsetType = new ConstantIntegerType ($ i );
1115- $ hasOffset = $ type ->hasOffsetValueType ($ offsetType );
1116- if ($ hasOffset ->no ()) {
1117- break ;
1081+ if ($ type ->isList ()->yes ()) {
1082+ if (
1083+ $ sizeType instanceof ConstantIntegerType
1084+ && $ sizeType ->getValue () < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1085+ ) {
1086+ // turn optional offsets non-optional
1087+ $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1088+ for ($ i = 0 ; $ i < $ sizeType ->getValue (); $ i ++) {
1089+ $ offsetType = new ConstantIntegerType ($ i );
1090+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
11181091 }
1119- $ valueTypesBuilder ->setOffsetValueType ( $ offsetType , $ type -> getOffsetValueType ( $ offsetType ), ! $ hasOffset -> yes () );
1092+ return $ valueTypesBuilder ->getArray ( );
11201093 }
1121- } else {
1122- return null ;
1123- }
11241094
1125- $ arrayType = $ valueTypesBuilder ->getArray ();
1126- if ($ arrayType ->isIterableAtLeastOnce ()->yes ()) {
1127- return $ arrayType ;
1128- }
1129- }
1095+ if (
1096+ $ sizeType instanceof IntegerRangeType
1097+ && $ sizeType ->getMin () !== null
1098+ ) {
1099+ // turn optional offsets non-optional
1100+ $ valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty ();
1101+ for ($ i = 0 ; $ i < $ sizeType ->getMin (); $ i ++) {
1102+ $ offsetType = new ConstantIntegerType ($ i );
1103+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ));
1104+ }
1105+ if ($ sizeType ->getMax () !== null ) {
1106+ for ($ i = $ sizeType ->getMin (); $ i < $ sizeType ->getMax (); $ i ++) {
1107+ $ offsetType = new ConstantIntegerType ($ i );
1108+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), true );
1109+ }
1110+ } elseif ($ type ->isConstantArray ()->yes ()) {
1111+ for ($ i = $ sizeType ->getMin ();; $ i ++) {
1112+ $ offsetType = new ConstantIntegerType ($ i );
1113+ $ hasOffset = $ type ->hasOffsetValueType ($ offsetType );
1114+ if ($ hasOffset ->no ()) {
1115+ break ;
1116+ }
1117+ $ valueTypesBuilder ->setOffsetValueType ($ offsetType , $ type ->getOffsetValueType ($ offsetType ), !$ hasOffset ->yes ());
1118+ }
1119+ } else {
1120+ return TypeCombinator::intersect ($ type , new NonEmptyArrayType ());
1121+ }
11301122
1131- return null ;
1132- }
1123+ return $ valueTypesBuilder -> getArray () ;
1124+ }
11331125
1134- private function isFuncCallWithNormalCount (FuncCall $ countFuncCall , Scope $ scope ): TrinaryLogic
1135- {
1136- $ argType = $ scope ->getType ($ countFuncCall ->getArgs ()[0 ]->value );
1126+ return $ type ;
1127+ }
11371128
1138- if (count ($ countFuncCall ->getArgs ()) === 1 ) {
1139- return TrinaryLogic::createYes ();
1140- }
1141- $ mode = $ scope ->getType ($ countFuncCall ->getArgs ()[1 ]->value );
1129+ return TypeCombinator::intersect ($ type , new NonEmptyArrayType ());
1130+ });
11421131
1143- return ( new ConstantIntegerType ( COUNT_NORMAL ))-> isSuperTypeOf ( $ mode )-> result -> or ( $ argType -> getIterableValueType ()-> isArray ( )->negate () );
1132+ return $ this -> create ( $ countFuncCall -> getArgs ()[ 0 ]-> value , $ resultType , $ context , $ scope )->setRootExpr ( $ rootExpr );
11441133 }
11451134
11461135 private function specifyTypesForConstantBinaryExpression (
@@ -2177,30 +2166,15 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
21772166 return $ specifiedTypes ;
21782167 }
21792168
2180- if ($ context ->truthy ()) {
2181- if ($ argType ->isArray ()->yes ()) {
2182- if (
2183- $ argType ->isConstantArray ()->yes ()
2184- && $ rightType ->isSuperTypeOf ($ argType ->getArraySize ())->no ()
2185- ) {
2186- return $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NeverType (), $ context , $ scope )->setRootExpr ($ expr );
2187- }
2188-
2189- $ funcTypes = $ this ->create ($ unwrappedLeftExpr , $ rightType , $ context , $ scope )->setRootExpr ($ expr );
2190- $ isNormalCount = $ this ->isFuncCallWithNormalCount ($ unwrappedLeftExpr , $ scope );
2191- $ constArray = $ isNormalCount ->yes () ? $ this ->turnListIntoConstantArray ($ argType , $ rightType ) : null ;
2192- if ($ constArray !== null ) {
2193- return $ funcTypes ->unionWith (
2194- $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , $ constArray , $ context , $ scope )->setRootExpr ($ expr ),
2195- );
2196- } elseif (IntegerRangeType::fromInterval (1 , null )->isSuperTypeOf ($ rightType )->yes ()) {
2197- return $ funcTypes ->unionWith (
2198- $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NonEmptyArrayType (), $ context , $ scope )->setRootExpr ($ expr ),
2199- );
2200- }
2201-
2202- return $ funcTypes ;
2169+ if ($ context ->truthy () && $ argType ->isArray ()->yes ()) {
2170+ $ funcTypes = $ this ->create ($ unwrappedLeftExpr , $ rightType , $ context , $ scope )->setRootExpr ($ expr );
2171+ if (IntegerRangeType::fromInterval (1 , null )->isSuperTypeOf ($ rightType )->yes ()) {
2172+ return $ funcTypes ->unionWith (
2173+ $ this ->create ($ unwrappedLeftExpr ->getArgs ()[0 ]->value , new NonEmptyArrayType (), $ context , $ scope )->setRootExpr ($ expr ),
2174+ );
22032175 }
2176+
2177+ return $ funcTypes ;
22042178 }
22052179 }
22062180
0 commit comments