Skip to content

Commit 91c6623

Browse files
committed
Merge branch 2.1.x into 2.2.x
2 parents 399ce26 + 3c61234 commit 91c6623

File tree

2 files changed

+293
-0
lines changed

2 files changed

+293
-0
lines changed

src/Type/TypeCombinator.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,88 @@ public static function union(Type ...$types): Type
155155
return new NeverType();
156156
}
157157

158+
// Fast path for single non-union type
159+
if ($typesCount === 1) {
160+
$singleType = $types[0];
161+
if (!$singleType instanceof UnionType && !$singleType->isArray()->yes()) {
162+
return $singleType;
163+
}
164+
}
165+
166+
// Fast path for common 2-type cases
167+
if ($typesCount === 2) {
168+
$a = $types[0];
169+
$b = $types[1];
170+
171+
// union(never, X) = X and union(X, never) = X
172+
if ($a instanceof NeverType && !$a->isExplicit()) {
173+
return $b;
174+
}
175+
if ($b instanceof NeverType && !$b->isExplicit()) {
176+
return $a;
177+
}
178+
179+
// union(mixed, X) = mixed (non-explicit, non-template, no subtracted)
180+
if ($a instanceof MixedType && !$a->isExplicitMixed() && !$a instanceof TemplateMixedType && $a->getSubtractedType() === null) {
181+
return $a;
182+
}
183+
if ($b instanceof MixedType && !$b->isExplicitMixed() && !$b instanceof TemplateMixedType && $b->getSubtractedType() === null) {
184+
return $b;
185+
}
186+
187+
// union(X, X) = X (same object identity)
188+
if ($a === $b) {
189+
return $a;
190+
}
191+
}
192+
193+
// Fast path for N>2: strip implicit NeverTypes and short-circuit on mixed
194+
if ($typesCount > 2) {
195+
$neverCount = 0;
196+
$hasUnionOrBenevolent = false;
197+
for ($i = 0; $i < $typesCount; $i++) {
198+
$t = $types[$i];
199+
if (
200+
$t instanceof MixedType
201+
&& !$t->isExplicitMixed()
202+
&& !$t instanceof TemplateMixedType
203+
&& $t->getSubtractedType() === null
204+
) {
205+
return $t;
206+
}
207+
if ($t instanceof NeverType && !$t->isExplicit()) {
208+
$neverCount++;
209+
} elseif ($t instanceof UnionType && !$t instanceof TemplateType) {
210+
$hasUnionOrBenevolent = true;
211+
}
212+
}
213+
214+
if ($neverCount > 0 && !$hasUnionOrBenevolent) {
215+
if ($neverCount === $typesCount) {
216+
return new NeverType();
217+
}
218+
219+
$filtered = [];
220+
for ($i = 0; $i < $typesCount; $i++) {
221+
if ($types[$i] instanceof NeverType && !$types[$i]->isExplicit()) {
222+
continue;
223+
}
224+
225+
$filtered[] = $types[$i];
226+
}
227+
$filteredCount = count($filtered);
228+
229+
if ($filteredCount === 1 && !$filtered[0]->isArray()->yes()) {
230+
return $filtered[0];
231+
}
232+
if ($filteredCount === 2) {
233+
return self::union($filtered[0], $filtered[1]);
234+
}
235+
$types = $filtered;
236+
$typesCount = $filteredCount;
237+
}
238+
}
239+
158240
$alreadyNormalized = [];
159241
$alreadyNormalizedCounter = 0;
160242

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
<?php
2+
3+
namespace UnionFastPath;
4+
5+
/**
6+
* Exercises TypeCombinator::union() fast path for trivial 2-type cases.
7+
*
8+
* Each early-return branch produces union(never, X) during scope merging.
9+
* Each if/else with unchanged variables produces union(X, X).
10+
* The many mixed parameters produce union(mixed, X) during narrowing.
11+
*/
12+
13+
/** @param array<string, mixed> $d */
14+
function narrowAndBranch(array $d, mixed $m1, mixed $m2, mixed $m3, mixed $m4): string
15+
{
16+
if (!is_string($m1)) { return ''; }
17+
if (!is_string($m2)) { return ''; }
18+
if (!is_string($m3)) { return ''; }
19+
if (!is_string($m4)) { return ''; }
20+
21+
$a = $b = $c = $d1 = $e = $f = $g = $h = '';
22+
23+
if (isset($d['a'])) { $a = (string) $d['a']; } else { return ''; }
24+
if (isset($d['b'])) { $b = (string) $d['b']; } else { return ''; }
25+
if (isset($d['c'])) { $c = (string) $d['c']; } else { return ''; }
26+
if (isset($d['d'])) { $d1 = (string) $d['d']; } else { return ''; }
27+
if (isset($d['e'])) { $e = (string) $d['e']; } else { return ''; }
28+
if (isset($d['f'])) { $f = (string) $d['f']; } else { return ''; }
29+
if (isset($d['g'])) { $g = (string) $d['g']; } else { return ''; }
30+
if (isset($d['h'])) { $h = (string) $d['h']; } else { return ''; }
31+
32+
return $a . $b . $c . $d1 . $e . $f . $g . $h . $m1 . $m2 . $m3 . $m4;
33+
}
34+
35+
/** @param array<string, mixed> $d */
36+
function narrowAndBranch2(array $d, mixed $m1, mixed $m2, mixed $m3, mixed $m4): string
37+
{
38+
if (!is_string($m1)) { return ''; }
39+
if (!is_string($m2)) { return ''; }
40+
if (!is_string($m3)) { return ''; }
41+
if (!is_string($m4)) { return ''; }
42+
43+
$a = $b = $c = $d1 = $e = $f = $g = $h = '';
44+
45+
if (isset($d['a1'])) { $a = (string) $d['a1']; } else { return ''; }
46+
if (isset($d['b1'])) { $b = (string) $d['b1']; } else { return ''; }
47+
if (isset($d['c1'])) { $c = (string) $d['c1']; } else { return ''; }
48+
if (isset($d['d1'])) { $d1 = (string) $d['d1']; } else { return ''; }
49+
if (isset($d['e1'])) { $e = (string) $d['e1']; } else { return ''; }
50+
if (isset($d['f1'])) { $f = (string) $d['f1']; } else { return ''; }
51+
if (isset($d['g1'])) { $g = (string) $d['g1']; } else { return ''; }
52+
if (isset($d['h1'])) { $h = (string) $d['h1']; } else { return ''; }
53+
54+
return $a . $b . $c . $d1 . $e . $f . $g . $h . $m1 . $m2 . $m3 . $m4;
55+
}
56+
57+
/** @param array<string, mixed> $d */
58+
function narrowAndBranch3(array $d, mixed $m1, mixed $m2, mixed $m3, mixed $m4): string
59+
{
60+
if (!is_string($m1)) { return ''; }
61+
if (!is_string($m2)) { return ''; }
62+
if (!is_string($m3)) { return ''; }
63+
if (!is_string($m4)) { return ''; }
64+
65+
$a = $b = $c = $d1 = $e = $f = $g = $h = '';
66+
67+
if (isset($d['a2'])) { $a = (string) $d['a2']; } else { return ''; }
68+
if (isset($d['b2'])) { $b = (string) $d['b2']; } else { return ''; }
69+
if (isset($d['c2'])) { $c = (string) $d['c2']; } else { return ''; }
70+
if (isset($d['d2'])) { $d1 = (string) $d['d2']; } else { return ''; }
71+
if (isset($d['e2'])) { $e = (string) $d['e2']; } else { return ''; }
72+
if (isset($d['f2'])) { $f = (string) $d['f2']; } else { return ''; }
73+
if (isset($d['g2'])) { $g = (string) $d['g2']; } else { return ''; }
74+
if (isset($d['h2'])) { $h = (string) $d['h2']; } else { return ''; }
75+
76+
return $a . $b . $c . $d1 . $e . $f . $g . $h . $m1 . $m2 . $m3 . $m4;
77+
}
78+
79+
/** @param array<string, mixed> $d */
80+
function narrowAndBranch4(array $d, mixed $m1, mixed $m2, mixed $m3, mixed $m4): string
81+
{
82+
if (!is_string($m1)) { return ''; }
83+
if (!is_string($m2)) { return ''; }
84+
if (!is_string($m3)) { return ''; }
85+
if (!is_string($m4)) { return ''; }
86+
87+
$a = $b = $c = $d1 = $e = $f = $g = $h = '';
88+
89+
if (isset($d['a3'])) { $a = (string) $d['a3']; } else { return ''; }
90+
if (isset($d['b3'])) { $b = (string) $d['b3']; } else { return ''; }
91+
if (isset($d['c3'])) { $c = (string) $d['c3']; } else { return ''; }
92+
if (isset($d['d3'])) { $d1 = (string) $d['d3']; } else { return ''; }
93+
if (isset($d['e3'])) { $e = (string) $d['e3']; } else { return ''; }
94+
if (isset($d['f3'])) { $f = (string) $d['f3']; } else { return ''; }
95+
if (isset($d['g3'])) { $g = (string) $d['g3']; } else { return ''; }
96+
if (isset($d['h3'])) { $h = (string) $d['h3']; } else { return ''; }
97+
98+
return $a . $b . $c . $d1 . $e . $f . $g . $h . $m1 . $m2 . $m3 . $m4;
99+
}
100+
101+
/** @param array<string, mixed> $d */
102+
function narrowAndBranch5(array $d, mixed $m1, mixed $m2, mixed $m3, mixed $m4): string
103+
{
104+
if (!is_string($m1)) { return ''; }
105+
if (!is_string($m2)) { return ''; }
106+
if (!is_string($m3)) { return ''; }
107+
if (!is_string($m4)) { return ''; }
108+
109+
$a = $b = $c = $d1 = $e = $f = $g = $h = '';
110+
111+
if (isset($d['a4'])) { $a = (string) $d['a4']; } else { return ''; }
112+
if (isset($d['b4'])) { $b = (string) $d['b4']; } else { return ''; }
113+
if (isset($d['c4'])) { $c = (string) $d['c4']; } else { return ''; }
114+
if (isset($d['d4'])) { $d1 = (string) $d['d4']; } else { return ''; }
115+
if (isset($d['e4'])) { $e = (string) $d['e4']; } else { return ''; }
116+
if (isset($d['f4'])) { $f = (string) $d['f4']; } else { return ''; }
117+
if (isset($d['g4'])) { $g = (string) $d['g4']; } else { return ''; }
118+
if (isset($d['h4'])) { $h = (string) $d['h4']; } else { return ''; }
119+
120+
return $a . $b . $c . $d1 . $e . $f . $g . $h . $m1 . $m2 . $m3 . $m4;
121+
}
122+
123+
function deepSwitch(mixed $val, mixed $a, mixed $b, mixed $c, mixed $d, mixed $e, mixed $f): string
124+
{
125+
if (!is_string($a)) { return ''; }
126+
if (!is_string($b)) { return ''; }
127+
if (!is_string($c)) { return ''; }
128+
if (!is_string($d)) { return ''; }
129+
if (!is_string($e)) { return ''; }
130+
if (!is_string($f)) { return ''; }
131+
132+
$r = '';
133+
if ($val === 'x1') { $r = $a; }
134+
elseif ($val === 'x2') { $r = $b; }
135+
elseif ($val === 'x3') { $r = $c; }
136+
elseif ($val === 'x4') { $r = $d; }
137+
elseif ($val === 'x5') { $r = $e; }
138+
elseif ($val === 'x6') { $r = $f; }
139+
elseif ($val === 'x7') { $r = $a . $b; }
140+
elseif ($val === 'x8') { $r = $c . $d; }
141+
elseif ($val === 'x9') { $r = $e . $f; }
142+
elseif ($val === 'x10') { $r = $a . $c; }
143+
elseif ($val === 'x11') { $r = $b . $d; }
144+
elseif ($val === 'x12') { $r = $c . $e; }
145+
elseif ($val === 'x13') { $r = $d . $f; }
146+
elseif ($val === 'x14') { $r = $a . $e; }
147+
elseif ($val === 'x15') { $r = $b . $f; }
148+
else { $r = $a . $b . $c . $d . $e . $f; }
149+
150+
return $r;
151+
}
152+
153+
function deepSwitch2(mixed $val, mixed $a, mixed $b, mixed $c, mixed $d, mixed $e, mixed $f): string
154+
{
155+
if (!is_string($a)) { return ''; }
156+
if (!is_string($b)) { return ''; }
157+
if (!is_string($c)) { return ''; }
158+
if (!is_string($d)) { return ''; }
159+
if (!is_string($e)) { return ''; }
160+
if (!is_string($f)) { return ''; }
161+
162+
$r = '';
163+
if ($val === 'y1') { $r = $a; }
164+
elseif ($val === 'y2') { $r = $b; }
165+
elseif ($val === 'y3') { $r = $c; }
166+
elseif ($val === 'y4') { $r = $d; }
167+
elseif ($val === 'y5') { $r = $e; }
168+
elseif ($val === 'y6') { $r = $f; }
169+
elseif ($val === 'y7') { $r = $a . $b; }
170+
elseif ($val === 'y8') { $r = $c . $d; }
171+
elseif ($val === 'y9') { $r = $e . $f; }
172+
elseif ($val === 'y10') { $r = $a . $c; }
173+
elseif ($val === 'y11') { $r = $b . $d; }
174+
elseif ($val === 'y12') { $r = $c . $e; }
175+
elseif ($val === 'y13') { $r = $d . $f; }
176+
elseif ($val === 'y14') { $r = $a . $e; }
177+
elseif ($val === 'y15') { $r = $b . $f; }
178+
else { $r = $a . $b . $c . $d . $e . $f; }
179+
180+
return $r;
181+
}
182+
183+
function deepSwitch3(mixed $val, mixed $a, mixed $b, mixed $c, mixed $d, mixed $e, mixed $f): string
184+
{
185+
if (!is_string($a)) { return ''; }
186+
if (!is_string($b)) { return ''; }
187+
if (!is_string($c)) { return ''; }
188+
if (!is_string($d)) { return ''; }
189+
if (!is_string($e)) { return ''; }
190+
if (!is_string($f)) { return ''; }
191+
192+
$r = '';
193+
if ($val === 'z1') { $r = $a; }
194+
elseif ($val === 'z2') { $r = $b; }
195+
elseif ($val === 'z3') { $r = $c; }
196+
elseif ($val === 'z4') { $r = $d; }
197+
elseif ($val === 'z5') { $r = $e; }
198+
elseif ($val === 'z6') { $r = $f; }
199+
elseif ($val === 'z7') { $r = $a . $b; }
200+
elseif ($val === 'z8') { $r = $c . $d; }
201+
elseif ($val === 'z9') { $r = $e . $f; }
202+
elseif ($val === 'z10') { $r = $a . $c; }
203+
elseif ($val === 'z11') { $r = $b . $d; }
204+
elseif ($val === 'z12') { $r = $c . $e; }
205+
elseif ($val === 'z13') { $r = $d . $f; }
206+
elseif ($val === 'z14') { $r = $a . $e; }
207+
elseif ($val === 'z15') { $r = $b . $f; }
208+
else { $r = $a . $b . $c . $d . $e . $f; }
209+
210+
return $r;
211+
}

0 commit comments

Comments
 (0)