Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9d8330b

Browse files
phpstan-botclaude
authored andcommitted
Eliminate augmentDisjunctionTypes overhead with count check and lazy right scope
Three optimizations to make augmentDisjunctionTypes zero-cost for the common case: 1. Count-based fast path: if intersectWith preserved all expression keys from both sides (checked via O(1) count comparison), no keys were dropped and augmentation is unnecessary. This handles the common case where both sides of &&/|| narrow the same expression keys. 2. Lazy left scope: compute only the left filtered scope first. For A && B && C && ..., leftExpr is always a simple expression, so filterByFalseyValue(leftExpr) is cheap. 3. Lazy right scope: compute the expensive rightScope.filterByFalseyValue(rightExpr) only when at least one candidate passes the left narrowing check. For long && chains where conditions test unrelated expressions (e.g. $a === $b && $c === $d), the left check fails for all candidates and the right scope is never computed. This avoids O(n²) recursive processing of compound right expressions. A/B benchmark on the same machine confirms zero measurable overhead: all 17 previously-flagged benchmark files show <1% difference with vs without augmentDisjunctionTypes. Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent cb9cfca commit 9d8330b

1 file changed

Lines changed: 33 additions & 15 deletions

File tree

src/Analyser/TypeSpecifier.php

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2082,16 +2082,23 @@ private function augmentDisjunctionTypes(
20822082
SpecifiedTypes $types,
20832083
): SpecifiedTypes
20842084
{
2085+
$leftSureTypes = $leftNormalized->getSureTypes();
2086+
$rightSureTypes = $rightNormalized->getSureTypes();
2087+
$existingSureTypes = $types->getSureTypes();
2088+
2089+
$existingCount = count($existingSureTypes);
2090+
if ($existingCount >= count($leftSureTypes) && $existingCount >= count($rightSureTypes)) {
2091+
return $types;
2092+
}
2093+
20852094
$candidateExprs = [];
2086-
foreach ($leftNormalized->getSureTypes() as $exprString => [$exprNode, $type]) {
2095+
foreach ($leftSureTypes as $exprString => [$exprNode, $type]) {
20872096
$candidateExprs[$exprString] = $exprNode;
20882097
}
2089-
foreach ($rightNormalized->getSureTypes() as $exprString => [$exprNode, $type]) {
2098+
foreach ($rightSureTypes as $exprString => [$exprNode, $type]) {
20902099
$candidateExprs[$exprString] = $exprNode;
20912100
}
20922101

2093-
$existingSureTypes = $types->getSureTypes();
2094-
20952102
$viableCandidates = [];
20962103
foreach ($candidateExprs as $exprString => $targetExpr) {
20972104
if (isset($existingSureTypes[$exprString])) {
@@ -2107,30 +2114,41 @@ private function augmentDisjunctionTypes(
21072114
return $types;
21082115
}
21092116

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+
$leftFilteredScope = $truthy
2118+
? $scope->filterByTruthyValue($leftExpr)
2119+
: $scope->filterByFalseyValue($leftExpr);
21172120

2121+
$leftNarrowedCandidates = [];
21182122
foreach ($viableCandidates as $exprString => $targetExpr) {
21192123
if (!$leftFilteredScope->hasExpressionType($targetExpr)->yes()) {
21202124
continue;
21212125
}
2122-
if (!$rightFilteredScope->hasExpressionType($targetExpr)->yes()) {
2123-
continue;
2124-
}
21252126

21262127
$originalType = $scope->getType($targetExpr);
21272128
$leftType = $leftFilteredScope->getType($targetExpr);
2128-
$rightType = $rightFilteredScope->getType($targetExpr);
21292129

21302130
if ($leftType->equals($originalType) || !$originalType->isSuperTypeOf($leftType)->yes()) {
21312131
continue;
21322132
}
21332133

2134+
$leftNarrowedCandidates[$exprString] = [$targetExpr, $originalType, $leftType];
2135+
}
2136+
2137+
if ($leftNarrowedCandidates === []) {
2138+
return $types;
2139+
}
2140+
2141+
$rightFilteredScope = $truthy
2142+
? $rightScope->filterByTruthyValue($rightExpr)
2143+
: $rightScope->filterByFalseyValue($rightExpr);
2144+
2145+
foreach ($leftNarrowedCandidates as $exprString => [$targetExpr, $originalType, $leftType]) {
2146+
if (!$rightFilteredScope->hasExpressionType($targetExpr)->yes()) {
2147+
continue;
2148+
}
2149+
2150+
$rightType = $rightFilteredScope->getType($targetExpr);
2151+
21342152
if ($rightType->equals($originalType) || !$originalType->isSuperTypeOf($rightType)->yes()) {
21352153
continue;
21362154
}

0 commit comments

Comments
 (0)