Skip to content

Union types #4838

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 48 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0ac57d5
Make zend_type a 2-field struct
nikic Sep 20, 2019
8b62d66
Store pass_by_reference+is_variadic in type mask
nikic Sep 24, 2019
4a9735f
WIP Union types
nikic Sep 25, 2019
8f9c57f
Fetch unresolved classes from autoload table
nikic Oct 18, 2019
9c58da4
Fix coercion error
nikic Oct 18, 2019
48e3379
Remove special exceptions when failing to resolve prop type class
nikic Oct 18, 2019
82cfa0c
Fix the typed reference assignment logic
nikic Oct 18, 2019
c786f78
Make loading in type errors robust
nikic Oct 18, 2019
67c0d29
Add tests for redundant types
nikic Oct 18, 2019
af73a41
Tweak error messages
nikic Oct 18, 2019
4a8d0af
Handle iterable + Traversable
nikic Oct 18, 2019
471c27f
Move test files
nikic Oct 18, 2019
f681fb6
More tests
nikic Oct 18, 2019
20495e1
Check for standalone null/false
nikic Oct 18, 2019
291f52c
Use smart_str for type error
nikic Oct 18, 2019
b49438b
Proper type error for union types
nikic Oct 18, 2019
887656d
Fix weak typing logic
nikic Oct 18, 2019
24f863a
Cleanup MAY_BE_BOOL
nikic Oct 18, 2019
37b3997
Add weak typing tests
nikic Oct 18, 2019
ccf57e9
Support union types in AST printer
nikic Oct 18, 2019
c1152c9
Add strict type variant of test
nikic Oct 21, 2019
7fc7982
Implement correct incdec handling
nikic Oct 21, 2019
209f7f1
Test property references
nikic Oct 21, 2019
5213df3
Implement full property invariance check
nikic Oct 21, 2019
9e50dfe
Add ReflectionUnionType
nikic Oct 21, 2019
b0f545c
Add a class loading test
nikic Oct 21, 2019
824ed44
Remove unused variable
nikic Oct 21, 2019
f1d05a4
Try to fix ZTS build (untested)
nikic Oct 21, 2019
fad9419
Allocate property type lists on arena
nikic Oct 22, 2019
686e61f
Remove duplicate test
nikic Oct 23, 2019
e0e204d
Add test for void + class
nikic Oct 23, 2019
476adfa
Run coverage job
nikic Oct 23, 2019
e7aecad
Adjust test after property type loading changes
nikic Oct 23, 2019
716cd38
Add test for generator with multiple class return types
nikic Oct 23, 2019
7e7d240
Add test for iterable+Traversable redundancy with extra class
nikic Oct 23, 2019
6abe540
Add inheritance test and fix some related issues
nikic Oct 23, 2019
59802bb
Fixup reflection after rebase
nikic Nov 7, 2019
f85a717
Revert "Run coverage job"
nikic Nov 8, 2019
c06e6c9
Add json skipifs
nikic Nov 8, 2019
956d3a3
Extra variance test
nikic Nov 8, 2019
8bb01a8
Fix issues related to inheritance of internal union types
nikic Nov 8, 2019
3a9ff5d
Properly destroy internal property types
nikic Nov 8, 2019
edcd6d9
Fix missing cache slot increment
nikic Nov 8, 2019
b036ffa
Use goto for common code-paths
nikic Nov 8, 2019
ccbd868
Add test for iterable variance
nikic Nov 8, 2019
54f9bd4
Fix accidental whitespace change
nikic Nov 8, 2019
c443f3b
Apply cache slot fix to jit as well
nikic Nov 8, 2019
b1615d1
Fix file cache
nikic Nov 8, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Zend/tests/assert/expect_015.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ assert(0 && ($a = function &(array &$a, ?X $b = null) use ($c,&$d) : ?X {
}
}));

assert(0 && ($a = function &(array &$a, X $b = null) use ($c,&$d) : X {
assert(0 && ($a = function &(array &$a, X $b = null, int|float $c) use ($c,&$d) : X {
final class A {
final protected function f2() {
if (!$x) {
Expand Down Expand Up @@ -204,7 +204,7 @@ Warning: assert(): assert(0 && ($a = function &(array &$a, ?X $b = null) use($c,

})) failed in %sexpect_015.php on line %d

Warning: assert(): assert(0 && ($a = function &(array &$a, X $b = null) use($c, &$d): X {
Warning: assert(): assert(0 && ($a = function &(array &$a, X $b = null, int|float $c) use($c, &$d): X {
final class A {
protected final function f2() {
if (!$x) {
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/return_types/generators002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ function test1() : StdClass {
yield 1;
}
--EXPECTF--
Fatal error: Generators may only declare a return type of Generator, Iterator, Traversable, or iterable, StdClass is not permitted in %s on line %d
Fatal error: Generators may only declare a return type containing Generator, Iterator, Traversable, or iterable, StdClass is not permitted in %s on line %d
2 changes: 1 addition & 1 deletion Zend/tests/type_declarations/nullable_void.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ function test() : ?void {

?>
--EXPECTF--
Fatal error: Void type cannot be nullable in %s on line %d
Fatal error: Void can only be used as a standalone type in %s on line %d
6 changes: 3 additions & 3 deletions Zend/tests/type_declarations/typed_properties_043.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ var_dump(Bar::$selfProp, Bar::$selfNullProp, Bar::$parentProp);

?>
--EXPECT--
Cannot write a value to a 'self' typed static property of a trait
Cannot write a non-null value to a 'self' typed static property of a trait
Cannot access parent:: when current class scope has no parent
Cannot assign stdClass to property Test::$selfProp of type self
Cannot assign stdClass to property Test::$selfNullProp of type ?self
Cannot assign stdClass to property Test::$parentProp of type parent
NULL
object(Bar)#3 (0) {
}
Expand Down
8 changes: 6 additions & 2 deletions Zend/tests/type_declarations/typed_properties_095.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,26 @@ var_dump(_ZendTestClass::$staticIntProp);
int(123)
Cannot assign string to property _ZendTestClass::$intProp of type int
Cannot assign _ZendTestClass to property _ZendTestClass::$classProp of type ?stdClass
object(_ZendTestClass)#1 (2) {
object(_ZendTestClass)#1 (3) {
["intProp"]=>
int(456)
["classProp"]=>
object(stdClass)#2 (0) {
}
["classUnionProp"]=>
NULL
}
int(123)
Cannot assign string to property _ZendTestClass::$intProp of type int
Cannot assign Test to property _ZendTestClass::$classProp of type ?stdClass
object(Test)#4 (2) {
object(Test)#4 (3) {
["intProp"]=>
int(456)
["classProp"]=>
object(stdClass)#1 (0) {
}
["classUnionProp"]=>
NULL
}
int(123)
Cannot assign string to property _ZendTestClass::$staticIntProp of type int
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Generator return value has to have Traversable-ish, but may also have extra types
--FILE--
<?php

interface I {
public function test(): iterable|false;
}

class C implements I {
public function test(): iterable|false {
yield;
}
}

var_dump((new C)->test());

?>
--EXPECT--
object(Generator)#2 (0) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Generator return type with multiple classes
--FILE--
<?php

interface I {
public function test(): Generator|ArrayAccess|array;
}
class C implements I {
function test(): Generator|ArrayAccess|array {
yield;
}
}

?>
===DONE===
--EXPECT--
===DONE===
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Argument default value not legal for any type in the union
--FILE--
<?php

function test(int|float $arg = "0") {
}

?>
--EXPECTF--
Fatal error: Cannot use string as default value for parameter $arg of type int|float in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Default value not legal for any type in the union
--FILE--
<?php

class Test {
public int|float $prop = "0";
}

?>
--EXPECTF--
Fatal error: Cannot use string as default value for property Test::$prop of type int|float in %s on line %d
132 changes: 132 additions & 0 deletions Zend/tests/type_declarations/union_types/incdec_prop.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
--TEST--
Increment/decrement a typed property with int|float type
--FILE--
<?php

class Test {
public int|float $prop;
public int|bool $prop2;
}

/* Incrementing a int|float property past int min/max is legal */

$test = new Test;
$test->prop = PHP_INT_MAX;
$x = $test->prop++;
var_dump(is_double($test->prop));

$test->prop = PHP_INT_MAX;
$x = ++$test->prop;
var_dump(is_double($test->prop));

$test->prop = PHP_INT_MIN;
$x = $test->prop--;
var_dump(is_double($test->prop));

$test->prop = PHP_INT_MIN;
$x = --$test->prop;
var_dump(is_double($test->prop));

$test = new Test;
$test->prop = PHP_INT_MAX;
$r =& $test->prop;
$x = $test->prop++;
var_dump(is_double($test->prop));

$test->prop = PHP_INT_MAX;
$x = ++$test->prop;
$r =& $test->prop;
var_dump(is_double($test->prop));

$test->prop = PHP_INT_MIN;
$x = $test->prop--;
$r =& $test->prop;
var_dump(is_double($test->prop));

$test->prop = PHP_INT_MIN;
$x = --$test->prop;
$r =& $test->prop;
var_dump(is_double($test->prop));

/* Incrementing a non-int|float property past int min/max is an error,
* even if the result of the overflow (a float) would technically be allowed
* under a type coercion. */

try {
$test->prop2 = PHP_INT_MAX;
$x = $test->prop2++;
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

try {
$test->prop2 = PHP_INT_MAX;
$x = ++$test->prop2;
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

try {
$test->prop2 = PHP_INT_MIN;
$x = $test->prop2--;
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

try {
$test->prop2 = PHP_INT_MIN;
$x = --$test->prop2;
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

try {
$test->prop2 = PHP_INT_MAX;
$r =& $test->prop2;
$x = $test->prop2++;
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

try {
$test->prop2 = PHP_INT_MAX;
$r =& $test->prop2;
$x = ++$test->prop2;
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

try {
$test->prop2 = PHP_INT_MIN;
$r =& $test->prop2;
$x = $test->prop2--;
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

try {
$test->prop2 = PHP_INT_MIN;
$r =& $test->prop2;
$x = --$test->prop2;
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
Cannot increment property Test::$prop2 of type int|bool past its maximal value
Cannot increment property Test::$prop2 of type int|bool past its maximal value
Cannot decrement property Test::$prop2 of type int|bool past its minimal value
Cannot decrement property Test::$prop2 of type int|bool past its minimal value
Cannot increment a reference held by property Test::$prop2 of type int|bool past its maximal value
Cannot increment a reference held by property Test::$prop2 of type int|bool past its maximal value
Cannot decrement a reference held by property Test::$prop2 of type int|bool past its minimal value
Cannot decrement a reference held by property Test::$prop2 of type int|bool past its minimal value
46 changes: 46 additions & 0 deletions Zend/tests/type_declarations/union_types/inheritance.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
--TEST--
Various inheritance scenarios for properties/methods with union types
--FILE--
<?php

class X {
public A|B|int $prop;
public function method(A|B|int $arg): A|B|int { }

private A|B|int $prop2;
private function method2(A|B|int $arg): A|B|int { }
}

class Y extends X {
}

trait T {
public A|B|int $prop;
public function method(A|B|int $arg): A|B|int { }

private A|B|int $prop2;
private function method2(A|B|int $arg): A|B|int { }
}

class Z {
use T;
}

class U extends X {
use T;
}

class V extends X {
use T;

public A|B|int $prop;
public function method(A|B|int $arg): A|B|int { }

private A|B|int $prop2;
private function method2(A|B|int $arg): A|B|int { }
}

?>
===DONE===
--EXPECT--
===DONE===
33 changes: 33 additions & 0 deletions Zend/tests/type_declarations/union_types/inheritance_internal.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
Inheritance of union type from internal class
--SKIPIF--
<?php
if (!extension_loaded('zend-test')) die('skip requires zend-test extension');
?>
--FILE--
<?php

class C extends _ZendTestClass {}

$obj = new _ZendTestChildClass;
$obj->classUnionProp = new stdClass;
$obj->classUnionProp = new ArrayIterator;
try {
$obj->classUnionProp = new DateTime;
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

$obj = new C;
$obj->classUnionProp = new stdClass;
$obj->classUnionProp = new ArrayIterator;
try {
$obj->classUnionProp = new DateTime;
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot assign DateTime to property _ZendTestClass::$classUnionProp of type stdClass|Iterator|null
Cannot assign DateTime to property _ZendTestClass::$classUnionProp of type stdClass|Iterator|null
45 changes: 45 additions & 0 deletions Zend/tests/type_declarations/union_types/legal_default_values.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
--TEST--
The default value must be legal for one of the types in the union
--FILE--
<?php

class Test {
public int|float $a = 1;
public int|float $b = 2.0;
public float|string $c = 3; // Strict typing exception
public float|string $d = 4.0;
public float|string $e = "5";
}

function test(
int|float $a = 1,
int|float $b = 2.0,
float|string $c = 3, // Strict typing exception
float|string $d = 4.0,
float|string $e = "5"
) {
var_dump($a, $b, $c, $d, $e);
}

var_dump(new Test);
test();

?>
--EXPECT--
object(Test)#1 (5) {
["a"]=>
int(1)
["b"]=>
float(2)
["c"]=>
float(3)
["d"]=>
float(4)
["e"]=>
string(1) "5"
}
int(1)
float(2)
float(3)
float(4)
string(1) "5"
Loading