Skip to content

Commit 05316db

Browse files
committed
Start implementing intersection types
1 parent fd62917 commit 05316db

33 files changed

+958
-109
lines changed
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+
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: 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
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Intersection types in parameters
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
9+
class Foo implements A, B {}
10+
class Bar implements A {}
11+
12+
function foo(A&B $bar) {
13+
var_dump($bar);
14+
}
15+
16+
foo(new Foo());
17+
18+
try {
19+
foo(new Bar());
20+
} catch (\TypeError $e) {
21+
echo $e->getMessage(), \PHP_EOL;
22+
}
23+
24+
?>
25+
--EXPECTF--
26+
object(Foo)#1 (0) {
27+
}
28+
foo(): Argument #1 ($bar) must be of type A&B, Bar given, called in %s on line %d
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Co-variance check failure for intersection type where child replace one of intersection type members with a supertype
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B extends A {}
8+
interface C {}
9+
10+
class Test implements B, C {}
11+
12+
class Foo {
13+
public function foo(): B&C {
14+
return new Test();
15+
}
16+
}
17+
18+
/* This fails because A is a parent type for B */
19+
class FooChild extends Foo {
20+
public function foo(): A&C {
21+
return new Test();
22+
}
23+
}
24+
25+
?>
26+
--EXPECTF--
27+
Fatal error: Declaration of FooChild::foo(): A&C must be compatible with Foo::foo(): B&C in %s on line %d

0 commit comments

Comments
 (0)