Skip to content

Commit da18f55

Browse files
committed
Start implementing intersection types
1 parent 78e1f19 commit da18f55

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1401
-183
lines changed

Zend/Optimizer/compact_literals.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ static size_t type_num_classes(const zend_op_array *op_array, uint32_t arg_num)
7373
arg_info = op_array->arg_info - 1;
7474
}
7575

76-
if (ZEND_TYPE_HAS_CLASS(arg_info->type)) {
76+
if (ZEND_TYPE_IS_COMPLEX(arg_info->type)) {
7777
if (ZEND_TYPE_HAS_LIST(arg_info->type)) {
7878
return ZEND_TYPE_LIST(arg_info->type)->num_types;
7979
}

Zend/Optimizer/dfa_pass.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ static inline bool can_elide_return_type_check(
312312
return 0;
313313
}
314314

315-
if (ZEND_TYPE_HAS_CLASS(info->type)) {
315+
if (ZEND_TYPE_IS_COMPLEX(info->type)) {
316316
if (!use_info->ce || !def_info->ce || !safe_instanceof(use_info->ce, def_info->ce)) {
317317
return 0;
318318
}

Zend/Optimizer/zend_inference.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,7 +2219,7 @@ ZEND_API uint32_t zend_fetch_arg_info_type(const zend_script *script, zend_arg_i
22192219
}
22202220

22212221
tmp = zend_convert_type_declaration_mask(ZEND_TYPE_PURE_MASK(arg_info->type));
2222-
if (ZEND_TYPE_HAS_CLASS(arg_info->type)) {
2222+
if (ZEND_TYPE_IS_COMPLEX(arg_info->type)) {
22232223
tmp |= MAY_BE_OBJECT;
22242224
/* As we only have space to store one CE, we use a plain object type for class unions. */
22252225
if (ZEND_TYPE_HAS_NAME(arg_info->type)) {
@@ -2327,7 +2327,7 @@ static uint32_t zend_fetch_prop_type(const zend_script *script, zend_property_in
23272327
}
23282328
if (prop_info && ZEND_TYPE_IS_SET(prop_info->type)) {
23292329
uint32_t type = zend_convert_type_declaration_mask(ZEND_TYPE_PURE_MASK(prop_info->type));
2330-
if (ZEND_TYPE_HAS_CLASS(prop_info->type)) {
2330+
if (ZEND_TYPE_IS_COMPLEX(prop_info->type)) {
23312331
type |= MAY_BE_OBJECT;
23322332
if (pce) {
23332333
if (ZEND_TYPE_HAS_CE(prop_info->type)) {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
--TEST--
2+
Added element of intersection type
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
interface Z {}
9+
10+
class A implements X, Y, Z {}
11+
12+
class Collection {
13+
public X&Y $intersect;
14+
}
15+
16+
function foo(): X&Y {
17+
return new A();
18+
}
19+
20+
function bar(X&Y $o): void {
21+
var_dump($o);
22+
}
23+
24+
try {
25+
$o = foo();
26+
var_dump($o);
27+
} catch (\TypeError $e) {
28+
echo $e->getMessage(), "\n";
29+
}
30+
31+
$c = new Collection();
32+
$a = new A();
33+
34+
try {
35+
$c->intersect = $a;
36+
echo 'OK', \PHP_EOL;
37+
} catch (\TypeError $e) {
38+
echo $e->getMessage(), "\n";
39+
}
40+
41+
try {
42+
bar($a);
43+
} catch (\TypeError $e) {
44+
echo $e->getMessage(), "\n";
45+
}
46+
47+
?>
48+
--EXPECT--
49+
object(A)#1 (0) {
50+
}
51+
OK
52+
object(A)#3 (0) {
53+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
--TEST--
2+
Assigning values to intersection types
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
interface Z {}
9+
10+
class TestParent implements X, Y {}
11+
class TestChild extends TestParent implements Z {}
12+
13+
class A {
14+
15+
public X&Y&Z $prop;
16+
17+
public function method1(X&Y $a): X&Y&Z {
18+
return new TestChild();
19+
}
20+
public function method2(X $a): X&Y {
21+
return new TestParent();
22+
}
23+
}
24+
25+
$tp = new TestParent();
26+
$tc = new TestChild();
27+
28+
$o = new A();
29+
try {
30+
$o->prop = $tp;
31+
} catch (TypeError $e) {
32+
echo $e->getMessage(), \PHP_EOL;
33+
}
34+
35+
$o->prop = $tc;
36+
37+
$r = $o->method1($tp);
38+
var_dump($r);
39+
$r = $o->method2($tp);
40+
var_dump($r);
41+
$r = $o->method1($tc);
42+
var_dump($r);
43+
$r = $o->method2($tc);
44+
var_dump($r);
45+
46+
47+
?>
48+
--EXPECTF--
49+
Cannot assign TestParent to property A::$prop of type X&Y&Z
50+
object(TestChild)#%d (0) {
51+
}
52+
object(TestParent)#%d (0) {
53+
}
54+
object(TestChild)#%d (0) {
55+
}
56+
object(TestParent)#%d (0) {
57+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
array type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): array&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type array cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
bool type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): bool&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type bool cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
callable type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): callable&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type callable cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
false type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): false&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type false cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
float type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): float&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type float cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
int type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): int&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type int cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
iterable type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): iterable&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type iterable cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
mixed type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): mixed&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type mixed cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
never type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): never&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type never cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
null type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): null&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type null cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Intersection type cannot be nullable
3+
--FILE--
4+
<?php
5+
6+
function foo(): ?Countable&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Parse error: syntax error, unexpected token "&", expecting "{" in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
object type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): object&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type object cannot be part of an intersection type in %s on line %d
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
parent type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
class A {}
7+
8+
class B extends A {
9+
public function foo(): parent&Iterator {}
10+
}
11+
12+
?>
13+
--EXPECTF--
14+
Fatal error: Type parent cannot be part of an intersection type in %s on line %d
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
self type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function foo(): self&Iterator {}
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Type self cannot be part of an intersection type in %s on line %d
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
static type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function foo(): static&Iterator {}
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Type static cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
string type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): string&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type string cannot be part of an intersection type in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
void type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): void&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type void cannot be part of an intersection type in %s on line %d
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--TEST--
2+
Missing one element of intersection type
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
interface Z {}
9+
10+
class A implements X {}
11+
12+
class Collection {
13+
public X&Y $intersect;
14+
}
15+
16+
function foo(): X&Y {
17+
return new A();
18+
}
19+
20+
function bar(X&Y $o): void {
21+
var_dump($o);
22+
}
23+
24+
try {
25+
$o = foo();
26+
var_dump($o);
27+
} catch (\TypeError $e) {
28+
echo $e->getMessage(), "\n";
29+
}
30+
31+
$c = new Collection();
32+
$a = new A();
33+
34+
try {
35+
$c->intersect = $a;
36+
} catch (\TypeError $e) {
37+
echo $e->getMessage(), "\n";
38+
}
39+
40+
try {
41+
bar($a);
42+
} catch (\TypeError $e) {
43+
echo $e->getMessage(), "\n";
44+
}
45+
46+
?>
47+
--EXPECTF--
48+
foo(): Return value must be of type X&Y, A returned
49+
Cannot assign A to property Collection::$intersect of type X&Y
50+
bar(): Argument #1 ($o) must be of type X&Y, A given, called in %s on line %d

0 commit comments

Comments
 (0)