Skip to content

Commit bb79ef7

Browse files
committed
Fix GH-9556 "iterable" alias "array|Traversable" breaks PHP 8.1 code
Closes GH-9558
1 parent 235e152 commit bb79ef7

15 files changed

+192
-4
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
A DNF type which contains object is redundant
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
9+
function test(): (A&B)|object {}
10+
11+
?>
12+
===DONE===
13+
--EXPECTF--
14+
Fatal error: Type (A&B)|object contains both object and a class type, which is redundant 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+
A DNF type which contains object is redundant 2
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
9+
function test(): object|(A&B) {}
10+
11+
?>
12+
===DONE===
13+
--EXPECTF--
14+
Fatal error: Type (A&B)|object contains both object and a class type, which is redundant 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+
iterable type with array should be redundant
3+
--FILE--
4+
<?php
5+
6+
function bar(): array|iterable|null {
7+
return null;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Duplicate type array is redundant 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+
iterable type with array should be redundant
3+
--FILE--
4+
<?php
5+
6+
function bar(): iterable|array|null {
7+
return null;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Duplicate type array is redundant 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+
iterable type with second iterable should be redundant
3+
--FILE--
4+
<?php
5+
6+
function bar(): iterable|iterable|null {
7+
return null;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Duplicate type array is redundant in %s on line %d
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
iterable type with object should NOT be redundant 1
3+
--FILE--
4+
<?php
5+
6+
function bar(): iterable|object|null {
7+
return null;
8+
}
9+
10+
?>
11+
===DONE===
12+
--EXPECT--
13+
===DONE===
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
iterable type with object should NOT be redundant 2
3+
--FILE--
4+
<?php
5+
6+
function bar(): object|iterable|null {
7+
return null;
8+
}
9+
10+
?>
11+
===DONE===
12+
--EXPECT--
13+
===DONE===
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
iterable type with object and class T should be redundant
3+
--FILE--
4+
<?php
5+
6+
function bar(): object|iterable|T|null {
7+
return null;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Type Traversable|T|object|array|null contains both object and a class type, which is redundant 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+
iterable type with object and class T should be redundant
3+
--FILE--
4+
<?php
5+
6+
function bar(): iterable|object|T|null {
7+
return null;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Type Traversable|T|object|array|null contains both object and a class type, which is redundant 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+
iterable type with object and class T should be redundant
3+
--FILE--
4+
<?php
5+
6+
function bar(): T|object|iterable|null {
7+
return null;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Type T|Traversable|object|array|null contains both object and a class type, which is redundant 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+
iterable type with object and class T should be redundant
3+
--FILE--
4+
<?php
5+
6+
function bar(): T|iterable|object|null {
7+
return null;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Type T|Traversable|object|array|null contains both object and a class type, which is redundant 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+
iterable type with object should be allowed in variance checks
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public object|iterable $x;
8+
public object|array $y;
9+
}
10+
class B extends A {
11+
public object|array $x;
12+
public object|iterable $y;
13+
}
14+
15+
?>
16+
===DONE===
17+
--EXPECT--
18+
===DONE===
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Using both object and a class type 2
3+
--FILE--
4+
<?php
5+
6+
function test(): Test|object {
7+
}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Type Test|object contains both object and a class type, which is redundant 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+
object and static are redundant 2
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public function foo(): object|static {}
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Type static|object contains both object and a class type, which is redundant in %s on line %d

Zend/zend_compile.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6364,6 +6364,7 @@ static zend_type zend_compile_typename(
63646364
zend_ast_list *list = zend_ast_get_list(ast);
63656365
zend_type_list *type_list;
63666366
bool is_composite = false;
6367+
bool has_only_iterable_class = true;
63676368
ALLOCA_FLAG(use_heap)
63686369

63696370
type_list = do_alloca(ZEND_TYPE_LIST_SIZE(list->children), use_heap);
@@ -6372,17 +6373,20 @@ static zend_type zend_compile_typename(
63726373
for (uint32_t i = 0; i < list->children; i++) {
63736374
zend_ast *type_ast = list->child[i];
63746375
zend_type single_type;
6376+
uint32_t type_mask = ZEND_TYPE_FULL_MASK(type);
63756377

63766378
if (type_ast->kind == ZEND_AST_TYPE_INTERSECTION) {
6379+
has_only_iterable_class = false;
63776380
is_composite = true;
63786381
/* The first class type can be stored directly as the type ptr payload. */
63796382
if (ZEND_TYPE_IS_COMPLEX(type) && !ZEND_TYPE_HAS_LIST(type)) {
63806383
/* Switch from single name to name list. */
63816384
type_list->num_types = 1;
63826385
type_list->types[0] = type;
63836386
ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
6384-
ZEND_TYPE_SET_LIST(type, type_list);
63856387
}
6388+
/* Mark type as list type */
6389+
ZEND_TYPE_SET_LIST(type, type_list);
63866390

63876391
single_type = zend_compile_typename(type_ast, false);
63886392
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(single_type));
@@ -6407,6 +6411,9 @@ static zend_type zend_compile_typename(
64076411
if (single_type_mask == MAY_BE_ANY) {
64086412
zend_error_noreturn(E_COMPILE_ERROR, "Type mixed can only be used as a standalone type");
64096413
}
6414+
if (ZEND_TYPE_IS_COMPLEX(single_type) && !ZEND_TYPE_IS_ITERABLE_FALLBACK(single_type)) {
6415+
has_only_iterable_class = false;
6416+
}
64106417

64116418
uint32_t type_mask_overlap = ZEND_TYPE_PURE_MASK(type) & single_type_mask;
64126419
if (type_mask_overlap) {
@@ -6415,8 +6422,9 @@ static zend_type zend_compile_typename(
64156422
zend_error_noreturn(E_COMPILE_ERROR,
64166423
"Duplicate type %s is redundant", ZSTR_VAL(overlap_type_str));
64176424
}
6418-
if ( ((ZEND_TYPE_PURE_MASK(type) & MAY_BE_TRUE) && (single_type_mask == MAY_BE_FALSE))
6419-
|| ((ZEND_TYPE_PURE_MASK(type) & MAY_BE_FALSE) && (single_type_mask == MAY_BE_TRUE)) ) {
6425+
6426+
if ( ((type_mask & MAY_BE_TRUE) && (single_type_mask == MAY_BE_FALSE))
6427+
|| ((type_mask & MAY_BE_FALSE) && (single_type_mask == MAY_BE_TRUE)) ) {
64206428
zend_error_noreturn(E_COMPILE_ERROR,
64216429
"Type contains both true and false, bool should be used instead");
64226430
}
@@ -6458,7 +6466,8 @@ static zend_type zend_compile_typename(
64586466
free_alloca(type_list, use_heap);
64596467

64606468
uint32_t type_mask = ZEND_TYPE_FULL_MASK(type);
6461-
if ((type_mask & MAY_BE_OBJECT) && (ZEND_TYPE_IS_COMPLEX(type) || (type_mask & MAY_BE_STATIC))) {
6469+
if ((type_mask & MAY_BE_OBJECT) &&
6470+
((!has_only_iterable_class && ZEND_TYPE_IS_COMPLEX(type)) || (type_mask & MAY_BE_STATIC))) {
64626471
zend_string *type_str = zend_type_to_string(type);
64636472
zend_error_noreturn(E_COMPILE_ERROR,
64646473
"Type %s contains both object and a class type, which is redundant",

0 commit comments

Comments
 (0)