Skip to content

Commit 1d3fc50

Browse files
committed
Start implementing part of the variance checks
1 parent b45f962 commit 1d3fc50

16 files changed

+475
-44
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
interface X {}
10+
11+
class Test implements B, C {}
12+
13+
class Foo {
14+
public function foo(): (B&C)|X {
15+
return new Test();
16+
}
17+
}
18+
19+
/* This fails because A is a parent type for B */
20+
class FooChild extends Foo {
21+
public function foo(): (A&C)|X {
22+
return new Test();
23+
}
24+
}
25+
26+
?>
27+
--EXPECTF--
28+
Fatal error: Declaration of FooChild::foo(): (A&C)|X must be compatible with Foo::foo(): (B&C)|X 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 removes one of intersection type members
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
interface X {}
9+
10+
class Test implements A, B {}
11+
12+
class Foo {
13+
public function foo(): (A&B)|X {
14+
return new Test();
15+
}
16+
}
17+
18+
/* This fails because just A larger than A&B */
19+
class FooChild extends Foo {
20+
public function foo(): A|X {
21+
return new Test();
22+
}
23+
}
24+
25+
?>
26+
--EXPECTF--
27+
Fatal error: Declaration of FooChild::foo(): A|X must be compatible with Foo::foo(): (A&B)|X 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+
Co-variance check failure for intersection type where child removes one of intersection type members
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
interface C {}
9+
interface X {}
10+
11+
class Test implements A, B, C {}
12+
13+
class Foo {
14+
public function foo(): (A&B&C)|X {
15+
return new Test();
16+
}
17+
}
18+
19+
/* This fails because just (A&B) larger than ((A&B)&C) */
20+
class FooChild extends Foo {
21+
public function foo(): (A&B)|X {
22+
return new Test();
23+
}
24+
}
25+
26+
?>
27+
--EXPECTF--
28+
Fatal error: Declaration of FooChild::foo(): (A&B)|X must be compatible with Foo::foo(): (A&B&C)|X in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Co-variance failure for intersection type where child is union, but not all members are a subtype of intersection 1
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
9+
interface L {}
10+
11+
class TestOne implements X, Y {}
12+
class TestTwo implements X {}
13+
14+
interface A
15+
{
16+
public function foo(): (X&Y)|L;
17+
}
18+
19+
interface B extends A
20+
{
21+
public function foo(): TestOne|TestTwo;
22+
}
23+
24+
?>
25+
--EXPECTF--
26+
Fatal error: Declaration of B::foo(): TestOne|TestTwo must be compatible with A::foo(): (X&Y)|L in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Co-variance failure for intersection type where child is union, but not all members are a subtype of intersection 2
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
9+
class TestOne implements X, Y {}
10+
11+
interface A
12+
{
13+
public function foo(): X&Y;
14+
}
15+
16+
interface B extends A
17+
{
18+
public function foo(): TestOne|int;
19+
}
20+
21+
?>
22+
--EXPECTF--
23+
Fatal error: Declaration of B::foo(): TestOne|int must be compatible with A::foo(): X&Y in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Co-variance failure for intersection type where child is union, but not all members are a subtype of intersection 2
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
interface Z extends Y {}
9+
10+
class TestOne implements X, Z {}
11+
class TestTwo implements X, Y {}
12+
13+
interface A
14+
{
15+
public function foo(): X&Z;
16+
}
17+
18+
interface B extends A
19+
{
20+
public function foo(): TestOne|TestTwo;
21+
}
22+
23+
?>
24+
--EXPECTF--
25+
Fatal error: Declaration of B::foo(): TestOne|TestTwo must be compatible with A::foo(): X&Z in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Property types must be invariant
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
9+
class A {
10+
public (X&Y)|L $prop;
11+
}
12+
class B extends A {
13+
public (X&Y&Z)|L $prop;
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: Type of B::$prop must be (X&Y)|L (as in class A) in %s on line %d
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Intersection type reduction invalid invariant type check
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
class A implements X, Y {}
9+
class B {}
10+
11+
class Test {
12+
public (X&Y)|B $prop;
13+
}
14+
class Test2 extends Test {
15+
public A|B $prop;
16+
}
17+
18+
?>
19+
===DONE===
20+
--EXPECTF--
21+
Fatal error: Type of Test2::$prop must be (X&Y)|B (as in class Test) in %s on line %d
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
Valid inheritence - co-variance
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
interface C {}
9+
10+
class Test implements A, B, C {}
11+
12+
class Foo {
13+
public function foo(): A {
14+
return new Test();
15+
}
16+
}
17+
18+
class FooChild extends Foo {
19+
public function foo(): A&B {
20+
return new Test();
21+
}
22+
}
23+
24+
class FooSecondChild extends FooChild {
25+
public function foo(): A&B&C {
26+
return new Test();
27+
}
28+
}
29+
30+
$o = new FooChild();
31+
var_dump($o->foo());
32+
$o = new FooSecondChild();
33+
var_dump($o->foo());
34+
35+
?>
36+
--EXPECTF--
37+
object(Test)#%d (0) {
38+
}
39+
object(Test)#%d (0) {
40+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Commutative intersection types
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
interface X {}
9+
10+
class Foo {
11+
public X|(A&B) $prop;
12+
public function foo(X|(A&B) $v): X|(A&B) {}
13+
}
14+
15+
class FooChild extends Foo {
16+
public (B&A)|X $prop;
17+
public function foo((B&A)|X $v): (B&A)|X {}
18+
}
19+
20+
?>
21+
===DONE===
22+
--EXPECT--
23+
===DONE===
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
Intersection type reduction valid invariant type check
3+
--FILE--
4+
<?php
5+
6+
class A {}
7+
class B extends A {}
8+
interface X {}
9+
10+
class Test {
11+
public (A&B)|X $prop;
12+
}
13+
class Test2 extends Test {
14+
public B|X $prop;
15+
}
16+
17+
?>
18+
===DONE===
19+
--EXPECT--
20+
===DONE===
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
Replacing union of classes respecting intersection type by intersection type
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
9+
class TestOne implements X, Y {}
10+
class TestTwo implements X, Y {}
11+
class Foo {}
12+
13+
interface A
14+
{
15+
public function foo(TestOne|TestTwo|Foo $param): (X&Y)|Foo;
16+
}
17+
18+
interface B extends A
19+
{
20+
public function foo((X&Y)|Foo $param): TestOne|TestTwo|Foo;
21+
}
22+
23+
interface C extends A
24+
{
25+
public function foo(X|Foo $param): TestTwo;
26+
}
27+
28+
interface D extends A
29+
{
30+
public function foo(Y|Foo $param): TestOne;
31+
}
32+
33+
interface E extends B
34+
{
35+
public function foo(X|Foo $param): TestTwo;
36+
}
37+
38+
interface F extends B
39+
{
40+
public function foo(Y|Foo $param): TestOne;
41+
}
42+
43+
?>
44+
===DONE===
45+
--EXPECT--
46+
===DONE===
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
Replacing union type by intersection type
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
interface Z {}
9+
10+
class A {
11+
public function test(): X|Z {}
12+
}
13+
class B extends A {
14+
public function test(): (X&Y)|Z {}
15+
}
16+
17+
?>
18+
===DONE===
19+
--EXPECT--
20+
===DONE===
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Replacing object type with intersection type
3+
--FILE--
4+
<?php
5+
6+
// It's sufficient that Y is loadable.
7+
interface Y {}
8+
9+
class Test {
10+
function method1(): object {}
11+
function method2(): object|int {}
12+
function method3(): Y|int {}
13+
}
14+
class Test2 extends Test {
15+
function method1(): (X&Y)|Countable {}
16+
function method2(): (X&Y)|int {}
17+
function method3(): (X&Y)|int {}
18+
}
19+
20+
?>
21+
===DONE===
22+
--EXPECT--
23+
===DONE===

0 commit comments

Comments
 (0)