Skip to content

Commit ef3fe97

Browse files
committed
Cache per-guard isSuperTypeOf() results in createConditionalExpressions() instead of recomputing them per target
- Both the forward (`guard`-supertype-of-`their`) and reverse (`their`-supertype-of-`guard`) `isSuperTypeOf()` checks in the guard-selection loop depend only on the guard expression, not on the target `$exprString` being iterated. They were recomputed once per (target, guard) pair, an O(N*M) redundancy that is very expensive on big union / constant-array types. - Cache both results per `$guardExprString` in local arrays, so each pair of supertype checks runs at most once per guard per branch merge. - Keep the reverse check (added in #5848 for the enum-guard false positive fixed by bug-14807) as the last operand of the OR so it is only computed when the cheaper forward-based conditions did not already decide — it is now also memoized. - This neutralizes both the #5848 reverse-check cost and the #5876 target-count amplification that compounded in the same machinery: bug-14462.php ~-31%, bug-10538.php ~-21% locally, while all analysis output (including bug-14807) is unchanged.
1 parent c473981 commit ef3fe97

1 file changed

Lines changed: 13 additions & 3 deletions

File tree

‎src/Analyser/MutatingScope.php‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3901,6 +3901,14 @@ private function createConditionalExpressions(
39013901
return $conditionalExpressions;
39023902
}
39033903

3904+
// Both isSuperTypeOf() checks below depend only on the guard (and its
3905+
// their-branch type), not on the target $exprString, so their results are
3906+
// invariant across the target loop. Cache them per guard to avoid
3907+
// recomputing expensive supertype checks on big union / constant-array
3908+
// types once per (target, guard) pair.
3909+
$guardIsSuperTypeOfTheirExprCache = [];
3910+
$theirExprIsSuperTypeOfGuardCache = [];
3911+
39043912
foreach ($newVariableTypes as $exprString => $holder) {
39053913
if ($holder->getExpr() instanceof VirtualNode) {
39063914
continue;
@@ -3924,12 +3932,13 @@ private function createConditionalExpressions(
39243932
array_key_exists($guardExprString, $theirExpressionTypes)
39253933
&& $theirExpressionTypes[$guardExprString]->getCertainty()->yes()
39263934
) {
3927-
$guardIsSuperTypeOfTheirExpr = $guardHolder->getType()->isSuperTypeOf($theirExpressionTypes[$guardExprString]->getType());
3928-
$theirExprIsSuperTypeOfGuard = $theirExpressionTypes[$guardExprString]->getType()->isSuperTypeOf($guardHolder->getType());
3935+
$guardIsSuperTypeOfTheirExpr = $guardIsSuperTypeOfTheirExprCache[$guardExprString] ??= $guardHolder->getType()->isSuperTypeOf($theirExpressionTypes[$guardExprString]->getType());
39293936

3937+
// The reverse isSuperTypeOf() check is expensive on big union /
3938+
// constant-array types, so it is evaluated last and only when the
3939+
// cheaper forward-based conditions did not already decide.
39303940
if (
39313941
$guardIsSuperTypeOfTheirExpr->yes()
3932-
|| $theirExprIsSuperTypeOfGuard->yes()
39333942
|| (
39343943
array_key_exists($exprString, $theirExpressionTypes)
39353944
&& $theirExpressionTypes[$exprString]->getCertainty()->yes()
@@ -3940,6 +3949,7 @@ private function createConditionalExpressions(
39403949
&& $holder->getType()->equals($guardHolder->getType())
39413950
&& !$guardIsSuperTypeOfTheirExpr->no()
39423951
)
3952+
|| ($theirExprIsSuperTypeOfGuardCache[$guardExprString] ??= $theirExpressionTypes[$guardExprString]->getType()->isSuperTypeOf($guardHolder->getType()))->yes()
39433953
) {
39443954
continue;
39453955
}

0 commit comments

Comments
 (0)