Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Pure Intersection types #6799

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

Merged
merged 40 commits into from
Jul 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
941d55c
Start implementing intersection types
Girgias Mar 14, 2021
f9c78be
Gross intersection type in parameter types syntax ambiguity workaround
iluuu1994 Mar 18, 2021
2d30485
Fix variance when child is union and parent is intersection
Girgias Apr 23, 2021
062d557
Reorder execution and expand comment
Girgias Apr 24, 2021
49f53a9
Collapse branches together
Girgias Apr 24, 2021
f98f402
Fix Reflection test after rebase (Fiber)
Girgias May 4, 2021
0824c47
Fix after review
Girgias May 18, 2021
af47afc
Alias T_AMPERSAND
Girgias May 29, 2021
9e9de43
Revert "Alias T_AMPERSAND"
Girgias Jun 1, 2021
05723db
Fix an additional variance bug
Girgias Jun 1, 2021
7c4f70c
Address review
Girgias Jun 1, 2021
93ca487
Allow self and parent to be part of an intersection type
Girgias Jun 1, 2021
cff68c9
Support static type in intersection
Girgias Jun 2, 2021
b541b24
Revert "Support static type in intersection"
Girgias Jun 2, 2021
020b586
Revert "Allow self and parent to be part of an intersection type"
Girgias Jun 2, 2021
b0c7519
Move common code into always inlined functions
Girgias Jun 9, 2021
8ca34b3
Comment nits
Girgias Jun 9, 2021
b7c322e
Apply nit to reflection test
Girgias Jun 9, 2021
1bf0dd7
Adjust dfa pass
Girgias Jun 18, 2021
ac74551
Drop unnecessary try/catch
Girgias Jun 18, 2021
a0a1d90
Refactor zend_resolve_ce()
Girgias Jun 18, 2021
a96e956
Refactor zend_check_type_slow()
Girgias Jun 18, 2021
e334504
Comment and reorder the parser hack
Girgias Jun 18, 2021
a02eb62
Refactor check for self/parent
Girgias Jun 18, 2021
9508af7
Export common type checking code for JIT
Girgias Jun 18, 2021
5ae1d2a
Fix parse error message for "&"
nikic Jun 30, 2021
3cbab3b
Merge zend_is_single_type_subtype_intersection() function
nikic Jun 30, 2021
a47b77a
Rebase fixup
nikic Jun 30, 2021
c8b965a
Unify early exit status handling, add comments
nikic Jun 30, 2021
2070319
Adjust misleading comment
nikic Jun 30, 2021
2c6d155
Add failing test
nikic Jun 30, 2021
681f1c4
Handle object/iterable, fixup rebase
nikic Jul 1, 2021
1446dec
Handle intersection in can_elide_return_type_check()
nikic Jul 1, 2021
6166ed0
Code cleanup
nikic Jul 1, 2021
a6d28dc
Return status like in branch above
Girgias Jul 5, 2021
129f1ad
Add a variance test
Girgias Jul 5, 2021
ea33768
Don't load classes of an intersection if parent has object
Girgias Jul 5, 2021
8873527
Revert "Don't load classes of an intersection if parent has object"
Girgias Jul 5, 2021
891c59b
Add comment
Girgias Jul 5, 2021
799b0cb
[skip-ci] Add UPGRADING entry and fix comments
Girgias Jul 5, 2021
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
7 changes: 5 additions & 2 deletions 7 UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ PHP 8.1 UPGRADE NOTES
PHP cross-version compatibility concerns, a `#[ReturnTypeWillChange]`
attribute can be added to silence the deprecation notice.
RFC: https://wiki.php.net/rfc/internal_method_return_types
. Added support for intersection types.
They cannot be combined with union types.
RFC: https://wiki.php.net/rfc/pure-intersection-types

- Fileinfo:
. The fileinfo functions now accept and return, respectively, finfo objects
Expand Down Expand Up @@ -307,7 +310,7 @@ PHP 8.1 UPGRADE NOTES
Previously, -a without readline had the same behavior as calling php without
any arguments, apart from printing an additional "Interactive mode enabled"
message. This mode was not, in fact, interactive.

- phpdbg:
. Remote functionality from phpdbg has been removed.

Expand Down Expand Up @@ -453,7 +456,7 @@ PHP 8.1 UPGRADE NOTES
MYSQLI_REFRESH_SLAVE, in line with an upstream change in MySQL. The old
constant is still available for backwards-compatibility reasons, but may
be deprecated/removed in the future.

- Sockets:
. TCP_DEFER_ACCEPT socket option added where available.

Expand Down
2 changes: 1 addition & 1 deletion 2 Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ static size_t type_num_classes(const zend_op_array *op_array, uint32_t arg_num)
arg_info = op_array->arg_info - 1;
}

if (ZEND_TYPE_HAS_CLASS(arg_info->type)) {
if (ZEND_TYPE_IS_COMPLEX(arg_info->type)) {
if (ZEND_TYPE_HAS_LIST(arg_info->type)) {
return ZEND_TYPE_LIST(arg_info->type)->num_types;
}
Expand Down
12 changes: 8 additions & 4 deletions 12 Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,19 +310,23 @@ static inline bool can_elide_return_type_check(
return true;
}

if (disallowed_types == MAY_BE_OBJECT && use_info->ce && ZEND_TYPE_HAS_CLASS(arg_info->type)) {
if (disallowed_types == MAY_BE_OBJECT && use_info->ce && ZEND_TYPE_IS_COMPLEX(arg_info->type)) {
zend_type *single_type;
/* For intersection: result==false is failure, default is success.
* For union: result==true is success, default is failure. */
bool is_intersection = ZEND_TYPE_IS_INTERSECTION(arg_info->type);
ZEND_TYPE_FOREACH(arg_info->type, single_type) {
if (ZEND_TYPE_HAS_NAME(*single_type)) {
zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type));
zend_class_entry *ce = zend_optimizer_get_class_entry(script, lcname);
zend_string_release(lcname);
if (ce && safe_instanceof(use_info->ce, ce)) {
/* One of the class union types matched. */
return true;
bool result = ce && safe_instanceof(use_info->ce, ce);
if (result == !is_intersection) {
return result;
}
}
} ZEND_TYPE_FOREACH_END();
return is_intersection;
}

return false;
Expand Down
2 changes: 1 addition & 1 deletion 2 Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -2221,7 +2221,7 @@ static uint32_t zend_convert_type(const zend_script *script, zend_type type, zen
}

uint32_t tmp = zend_convert_type_declaration_mask(ZEND_TYPE_PURE_MASK(type));
if (ZEND_TYPE_HAS_CLASS(type)) {
if (ZEND_TYPE_IS_COMPLEX(type)) {
tmp |= MAY_BE_OBJECT;
if (pce) {
/* As we only have space to store one CE,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--TEST--
Added element of intersection type
--FILE--
<?php

interface X {}
interface Y {}
interface Z {}

class A implements X, Y, Z {}

class Collection {
public X&Y $intersect;
}

function foo(): X&Y {
return new A();
}

function bar(X&Y $o): void {
var_dump($o);
}

$o = foo();
var_dump($o);

$c = new Collection();
$a = new A();

$c->intersect = $a;
echo 'OK', \PHP_EOL;
bar($a);
?>
--EXPECT--
object(A)#1 (0) {
}
OK
object(A)#3 (0) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
--TEST--
Assigning values to intersection types
--FILE--
<?php

interface X {}
interface Y {}
interface Z {}

class TestParent implements X, Y {}
class TestChild extends TestParent implements Z {}

class A {

public X&Y&Z $prop;

public function method1(X&Y $a): X&Y&Z {
return new TestChild();
}
public function method2(X $a): X&Y {
return new TestParent();
}
}

$tp = new TestParent();
$tc = new TestChild();

$o = new A();
try {
$o->prop = $tp;
} catch (TypeError $e) {
echo $e->getMessage(), \PHP_EOL;
}

$o->prop = $tc;

$r = $o->method1($tp);
var_dump($r);
$r = $o->method2($tp);
var_dump($r);
$r = $o->method1($tc);
var_dump($r);
$r = $o->method2($tc);
var_dump($r);


?>
--EXPECTF--
Cannot assign TestParent to property A::$prop of type X&Y&Z
object(TestChild)#%d (0) {
}
object(TestParent)#%d (0) {
}
object(TestChild)#%d (0) {
}
object(TestParent)#%d (0) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
array type cannot take part in an intersection type
--FILE--
<?php

function foo(): array&Iterator {}

?>
--EXPECTF--
Fatal error: Type array cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
bool type cannot take part in an intersection type
--FILE--
<?php

function foo(): bool&Iterator {}

?>
--EXPECTF--
Fatal error: Type bool cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
callable type cannot take part in an intersection type
--FILE--
<?php

function foo(): callable&Iterator {}

?>
--EXPECTF--
Fatal error: Type callable cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
false type cannot take part in an intersection type
--FILE--
<?php

function foo(): false&Iterator {}

?>
--EXPECTF--
Fatal error: Type false cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
float type cannot take part in an intersection type
--FILE--
<?php

function foo(): float&Iterator {}

?>
--EXPECTF--
Fatal error: Type float cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
int type cannot take part in an intersection type
--FILE--
<?php

function foo(): int&Iterator {}

?>
--EXPECTF--
Fatal error: Type int cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
iterable type cannot take part in an intersection type
--FILE--
<?php

function foo(): iterable&Iterator {}

?>
--EXPECTF--
Fatal error: Type iterable cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
mixed type cannot take part in an intersection type
--FILE--
<?php

function foo(): mixed&Iterator {}

?>
--EXPECTF--
Fatal error: Type mixed cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
never type cannot take part in an intersection type
--FILE--
<?php

function foo(): never&Iterator {}

?>
--EXPECTF--
Fatal error: Type never cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
null type cannot take part in an intersection type
--FILE--
<?php

function foo(): null&Iterator {}

?>
--EXPECTF--
Fatal error: Type null cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Intersection type cannot be nullable
--FILE--
<?php

function foo(): ?Countable&Iterator {}

?>
--EXPECTF--
Parse error: syntax error, unexpected token "&", expecting "{" in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
object type cannot take part in an intersection type
--FILE--
<?php

function foo(): object&Iterator {}

?>
--EXPECTF--
Fatal error: Type object cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
parent type cannot take part in an intersection type
--FILE--
<?php

class A {}

class B extends A {
public function foo(): parent&Iterator {}
}

?>
--EXPECTF--
Fatal error: Type parent cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
self type cannot take part in an intersection type
--FILE--
<?php

class A {
public function foo(): self&Iterator {}
}

?>
--EXPECTF--
Fatal error: Type self cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
static type cannot take part in an intersection type
--FILE--
<?php

class A {
public function foo(): static&Iterator {}
}

?>
--EXPECTF--
Fatal error: Type static cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
string type cannot take part in an intersection type
--FILE--
<?php

function foo(): string&Iterator {}

?>
--EXPECTF--
Fatal error: Type string cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
void type cannot take part in an intersection type
--FILE--
<?php

function foo(): void&Iterator {}

?>
--EXPECTF--
Fatal error: Type void cannot be part of an intersection type in %s on line %d
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.