From 382ff9dfc9cd40f753b0a82ce51bc5f5c698692b Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 27 Feb 2026 09:30:08 +0100 Subject: [PATCH 1/4] Prevent unnecessary work in IntersectionType --- src/Type/IntersectionType.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 06e3d781ef2..66ef1e2b1a5 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -266,7 +266,16 @@ public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult return $otherType->isSuperTypeOf($this); } - $result = IsSuperTypeOfResult::maxMin(...array_map(static fn (Type $innerType) => $otherType->isSuperTypeOf($innerType), $this->types)); + $results = []; + foreach ($this->types as $innerType) { + $isSuperTypeOf = $otherType->isSuperTypeOf($innerType); + if ($isSuperTypeOf->yes()) { + return IsSuperTypeOfResult::createYes(); + } + $results[] = $isSuperTypeOf; + } + $result = IsSuperTypeOfResult::maxMin(...$results); + if ( !$result->no() && $this->isOversizedArray()->yes() @@ -280,7 +289,16 @@ public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult { - $result = AcceptsResult::maxMin(...array_map(static fn (Type $innerType) => $acceptingType->accepts($innerType, $strictTypes), $this->types)); + $results = []; + foreach ($this->types as $innerType) { + $isAcceptedBy = $acceptingType->isAcceptedBy($innerType, $strictTypes); + + if ($isAcceptedBy->yes()) { + return AcceptsResult::createYes(); + } + } + $result = AcceptsResult::maxMin(...$results); + if ($this->isOversizedArray()->yes()) { if (!$result->no()) { return AcceptsResult::createYes(); From f6d99ee075fec54daf8963d6a4f9f9d2ab47935a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 27 Feb 2026 09:39:31 +0100 Subject: [PATCH 2/4] fix --- src/Type/IntersectionType.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 66ef1e2b1a5..1fe468ecc4e 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -291,11 +291,11 @@ public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsRes { $results = []; foreach ($this->types as $innerType) { - $isAcceptedBy = $acceptingType->isAcceptedBy($innerType, $strictTypes); - + $isAcceptedBy = $acceptingType->accepts($innerType, $strictTypes); if ($isAcceptedBy->yes()) { return AcceptsResult::createYes(); } + $results[] = $isAcceptedBy; } $result = AcceptsResult::maxMin(...$results); From c1ff9b46133a57442acc3d172fe23cd2ed04436f Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 28 Feb 2026 09:58:34 +0100 Subject: [PATCH 3/4] refactor --- src/Type/AcceptsResult.php | 21 +++++++++++++++++++++ src/Type/IntersectionType.php | 26 ++++++++------------------ src/Type/IsSuperTypeOfResult.php | 21 +++++++++++++++++++++ 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/Type/AcceptsResult.php b/src/Type/AcceptsResult.php index 1690d7b90ce..754e92c20b1 100644 --- a/src/Type/AcceptsResult.php +++ b/src/Type/AcceptsResult.php @@ -152,4 +152,25 @@ public static function maxMin(self ...$operands): self return new self(TrinaryLogic::maxMin(...$results), array_values(array_unique($reasons))); } + /** + * @template T + * @param T[] $objects + * @param callable(T): self $callback + */ + public static function lazyMaxMin( + array $objects, + callable $callback, + ): self + { + $results = []; + foreach ($objects as $object) { + $isAcceptedBy = $callback($object); + if ($isAcceptedBy->result->yes()) { + return $isAcceptedBy; + } + $results[] = $isAcceptedBy; + } + return self::maxMin(...$results); + } + } diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 1fe468ecc4e..bd203c08bd8 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -266,15 +266,10 @@ public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult return $otherType->isSuperTypeOf($this); } - $results = []; - foreach ($this->types as $innerType) { - $isSuperTypeOf = $otherType->isSuperTypeOf($innerType); - if ($isSuperTypeOf->yes()) { - return IsSuperTypeOfResult::createYes(); - } - $results[] = $isSuperTypeOf; - } - $result = IsSuperTypeOfResult::maxMin(...$results); + $result = IsSuperTypeOfResult::lazyMaxMin( + $this->types, + static fn (Type $innerType) => $otherType->isSuperTypeOf($innerType), + ); if ( !$result->no() @@ -289,15 +284,10 @@ public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult { - $results = []; - foreach ($this->types as $innerType) { - $isAcceptedBy = $acceptingType->accepts($innerType, $strictTypes); - if ($isAcceptedBy->yes()) { - return AcceptsResult::createYes(); - } - $results[] = $isAcceptedBy; - } - $result = AcceptsResult::maxMin(...$results); + $result = AcceptsResult::lazyMaxMin( + $this->types, + static fn (Type $innerType) => $acceptingType->accepts($innerType, $strictTypes), + ); if ($this->isOversizedArray()->yes()) { if (!$result->no()) { diff --git a/src/Type/IsSuperTypeOfResult.php b/src/Type/IsSuperTypeOfResult.php index b0826f518cf..901ee0ca9b5 100644 --- a/src/Type/IsSuperTypeOfResult.php +++ b/src/Type/IsSuperTypeOfResult.php @@ -185,6 +185,27 @@ public static function maxMin(self ...$operands): self return new self(TrinaryLogic::maxMin(...$results), array_values(array_unique($reasons))); } + /** + * @template T + * @param T[] $objects + * @param callable(T): self $callback + */ + public static function lazyMaxMin( + array $objects, + callable $callback, + ): self + { + $results = []; + foreach ($objects as $object) { + $isSuperTypeOf = $callback($object); + if ($isSuperTypeOf->result->yes()) { + return $isSuperTypeOf; + } + $results[] = $isSuperTypeOf; + } + return self::maxMin(...$results); + } + public function negate(): self { return new self($this->result->negate(), $this->reasons); From adfd2b627c48cac6b628679af320ec37815911cb Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 28 Feb 2026 10:37:11 +0100 Subject: [PATCH 4/4] prevent nested loops --- src/Type/AcceptsResult.php | 14 +++++++++++++- src/Type/IsSuperTypeOfResult.php | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Type/AcceptsResult.php b/src/Type/AcceptsResult.php index 754e92c20b1..87afd50dcae 100644 --- a/src/Type/AcceptsResult.php +++ b/src/Type/AcceptsResult.php @@ -163,14 +163,26 @@ public static function lazyMaxMin( ): self { $results = []; + $reasons = []; + $hasNo = false; foreach ($objects as $object) { $isAcceptedBy = $callback($object); if ($isAcceptedBy->result->yes()) { return $isAcceptedBy; + } elseif ($isAcceptedBy->result->no()) { + $hasNo = true; } $results[] = $isAcceptedBy; + + foreach ($isAcceptedBy->reasons as $reason) { + $reasons[] = $reason; + } } - return self::maxMin(...$results); + + return new self( + $hasNo ? TrinaryLogic::createNo() : TrinaryLogic::createMaybe(), + array_values(array_unique($reasons)), + ); } } diff --git a/src/Type/IsSuperTypeOfResult.php b/src/Type/IsSuperTypeOfResult.php index 901ee0ca9b5..0c4fb92a50c 100644 --- a/src/Type/IsSuperTypeOfResult.php +++ b/src/Type/IsSuperTypeOfResult.php @@ -196,14 +196,26 @@ public static function lazyMaxMin( ): self { $results = []; + $reasons = []; + $hasNo = false; foreach ($objects as $object) { $isSuperTypeOf = $callback($object); if ($isSuperTypeOf->result->yes()) { return $isSuperTypeOf; + } elseif ($isSuperTypeOf->result->no()) { + $hasNo = true; } $results[] = $isSuperTypeOf; + + foreach ($isSuperTypeOf->reasons as $reason) { + $reasons[] = $reason; + } } - return self::maxMin(...$results); + + return new self( + $hasNo ? TrinaryLogic::createNo() : TrinaryLogic::createMaybe(), + array_values(array_unique($reasons)), + ); } public function negate(): self