@@ -719,7 +719,14 @@ public function specifyTypesInCondition(
719719 $ leftTypes = $ this ->specifyTypesInCondition ($ scope , $ expr ->left , $ context )->setRootExpr ($ expr );
720720 $ rightScope = $ scope ->filterByTruthyValue ($ expr ->left );
721721 $ rightTypes = $ this ->specifyTypesInCondition ($ rightScope , $ expr ->right , $ context )->setRootExpr ($ expr );
722- $ types = $ context ->true () ? $ leftTypes ->unionWith ($ rightTypes ) : $ leftTypes ->normalize ($ scope )->intersectWith ($ rightTypes ->normalize ($ rightScope ));
722+ if ($ context ->true ()) {
723+ $ types = $ leftTypes ->unionWith ($ rightTypes );
724+ } else {
725+ $ leftNormalized = $ leftTypes ->normalize ($ scope );
726+ $ rightNormalized = $ rightTypes ->normalize ($ rightScope );
727+ $ types = $ leftNormalized ->intersectWith ($ rightNormalized );
728+ $ types = $ this ->augmentDisjunctionTypes ($ scope , $ rightScope , $ leftNormalized , $ rightNormalized , $ expr ->left , $ expr ->right , false , $ types );
729+ }
723730 if ($ context ->false ()) {
724731 $ leftTypesForHolders = $ leftTypes ;
725732 $ rightTypesForHolders = $ rightTypes ;
@@ -773,8 +780,11 @@ public function specifyTypesInCondition(
773780 ) {
774781 $ types = $ leftTypes ->normalize ($ scope );
775782 } else {
776- $ types = $ leftTypes ->normalize ($ scope )->intersectWith ($ rightTypes ->normalize ($ rightScope ));
783+ $ leftNormalized = $ leftTypes ->normalize ($ scope );
784+ $ rightNormalized = $ rightTypes ->normalize ($ rightScope );
785+ $ types = $ leftNormalized ->intersectWith ($ rightNormalized );
777786 $ types = $ this ->augmentBooleanOrTruthyWithConditionalHolders ($ scope , $ rightScope , $ expr , $ types );
787+ $ types = $ this ->augmentDisjunctionTypes ($ scope , $ rightScope , $ leftNormalized , $ rightNormalized , $ expr ->left , $ expr ->right , true , $ types );
778788 }
779789 } else {
780790 $ types = $ leftTypes ->unionWith ($ rightTypes );
@@ -2061,6 +2071,83 @@ private function augmentBooleanOrTruthyWithConditionalHolders(MutatingScope $sco
20612071 return $ types ;
20622072 }
20632073
2074+ private function augmentDisjunctionTypes (
2075+ MutatingScope $ scope ,
2076+ MutatingScope $ rightScope ,
2077+ SpecifiedTypes $ leftNormalized ,
2078+ SpecifiedTypes $ rightNormalized ,
2079+ Expr $ leftExpr ,
2080+ Expr $ rightExpr ,
2081+ bool $ truthy ,
2082+ SpecifiedTypes $ types ,
2083+ ): SpecifiedTypes
2084+ {
2085+ $ candidateExprs = [];
2086+ foreach ($ leftNormalized ->getSureTypes () as $ exprString => [$ exprNode , $ type ]) {
2087+ $ candidateExprs [$ exprString ] = $ exprNode ;
2088+ }
2089+ foreach ($ rightNormalized ->getSureTypes () as $ exprString => [$ exprNode , $ type ]) {
2090+ $ candidateExprs [$ exprString ] = $ exprNode ;
2091+ }
2092+
2093+ $ existingSureTypes = $ types ->getSureTypes ();
2094+
2095+ $ viableCandidates = [];
2096+ foreach ($ candidateExprs as $ exprString => $ targetExpr ) {
2097+ if (isset ($ existingSureTypes [$ exprString ])) {
2098+ continue ;
2099+ }
2100+ if (!$ scope ->hasExpressionType ($ targetExpr )->yes ()) {
2101+ continue ;
2102+ }
2103+ $ viableCandidates [$ exprString ] = $ targetExpr ;
2104+ }
2105+
2106+ if ($ viableCandidates === []) {
2107+ return $ types ;
2108+ }
2109+
2110+ if ($ truthy ) {
2111+ $ leftFilteredScope = $ scope ->filterByTruthyValue ($ leftExpr );
2112+ $ rightFilteredScope = $ rightScope ->filterByTruthyValue ($ rightExpr );
2113+ } else {
2114+ $ leftFilteredScope = $ scope ->filterByFalseyValue ($ leftExpr );
2115+ $ rightFilteredScope = $ rightScope ->filterByFalseyValue ($ rightExpr );
2116+ }
2117+
2118+ foreach ($ viableCandidates as $ targetExpr ) {
2119+ if (!$ leftFilteredScope ->hasExpressionType ($ targetExpr )->yes ()) {
2120+ continue ;
2121+ }
2122+ if (!$ rightFilteredScope ->hasExpressionType ($ targetExpr )->yes ()) {
2123+ continue ;
2124+ }
2125+
2126+ $ originalType = $ scope ->getType ($ targetExpr );
2127+ $ leftType = $ leftFilteredScope ->getType ($ targetExpr );
2128+ $ rightType = $ rightFilteredScope ->getType ($ targetExpr );
2129+
2130+ if ($ leftType ->equals ($ originalType ) || !$ originalType ->isSuperTypeOf ($ leftType )->yes ()) {
2131+ continue ;
2132+ }
2133+
2134+ if ($ rightType ->equals ($ originalType ) || !$ originalType ->isSuperTypeOf ($ rightType )->yes ()) {
2135+ continue ;
2136+ }
2137+
2138+ $ unionType = TypeCombinator::union ($ leftType , $ rightType );
2139+ if ($ unionType ->equals ($ originalType )) {
2140+ continue ;
2141+ }
2142+
2143+ $ types = $ types ->unionWith (
2144+ $ this ->create ($ targetExpr , $ unionType , TypeSpecifierContext::createTrue (), $ scope ),
2145+ );
2146+ }
2147+
2148+ return $ types ;
2149+ }
2150+
20642151 /**
20652152 * @return array<string, ConditionalExpressionHolder[]>
20662153 */
0 commit comments