Skip to content

Commit 2eaef93

Browse files
committed
Check for redundant types
1 parent a9c2be3 commit 2eaef93

8 files changed

+197
-20
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Duplicate class alias type
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
8+
use A as B;
9+
function foo(): (X&A)|(X&B) {}
10+
11+
?>
12+
--EXPECTF--
13+
Fatal error: Type X&A is redundant with type X&A in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Duplicate class alias type at runtime
3+
--FILE--
4+
<?php
5+
6+
class A {}
7+
interface X {}
8+
9+
class_alias('A', 'B');
10+
function foo(): (X&A)|(X&B) {}
11+
12+
?>
13+
===DONE===
14+
--EXPECT--
15+
===DONE===
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Intersection with child class
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
class A {}
8+
class B extends A {}
9+
10+
function test(): (A&X)|(B&X) {}
11+
12+
?>
13+
===DONE===
14+
--EXPECT--
15+
===DONE===
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
A less restrictive type constrain is part of the DNF type 001
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
9+
function test(): (A&B)|A {}
10+
11+
?>
12+
===DONE===
13+
--EXPECTF--
14+
Fatal error: Type A is less restrictive than type A&B in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
A less restrictive type constrain is part of the DNF type 002
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
9+
function test(): A|(A&B) {}
10+
11+
?>
12+
===DONE===
13+
--EXPECTF--
14+
Fatal error: Type A is less restrictive than type A&B in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
A less restrictive type constrain is part of the DNF type 003
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
interface C {}
9+
10+
function test(): (A&B)|(A&B&C) {}
11+
12+
?>
13+
===DONE===
14+
--EXPECTF--
15+
Fatal error: Type A&B is less restrictive than type A&B&C in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
A less restrictive type constrain is part of the DNF type 004
3+
--FILE--
4+
<?php
5+
6+
interface A {}
7+
interface B {}
8+
interface C {}
9+
10+
function test(): (A&B&C)|(A&B) {}
11+
12+
?>
13+
===DONE===
14+
--EXPECTF--
15+
Fatal error: Type A&B is less restrictive than type A&B&C in %s on line %d

Zend/zend_compile.c

Lines changed: 96 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6226,6 +6226,91 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
62266226
}
62276227
}
62286228

6229+
static void zend_are_intersection_types_redundant(zend_type left_type, zend_type right_type)
6230+
{
6231+
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(left_type));
6232+
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(right_type));
6233+
zend_type_list *l_type_list = ZEND_TYPE_LIST(left_type);
6234+
zend_type_list *r_type_list = ZEND_TYPE_LIST(right_type);
6235+
zend_type_list *smaller_type_list, *larger_type_list;
6236+
bool flipped = false;
6237+
6238+
if (r_type_list->num_types < l_type_list->num_types) {
6239+
smaller_type_list = r_type_list;
6240+
larger_type_list = l_type_list;
6241+
flipped = true;
6242+
} else {
6243+
smaller_type_list = l_type_list;
6244+
larger_type_list = r_type_list;
6245+
}
6246+
6247+
int sum = 0;
6248+
zend_type *outer_type;
6249+
ZEND_TYPE_LIST_FOREACH(smaller_type_list, outer_type)
6250+
zend_type *inner_type;
6251+
ZEND_TYPE_LIST_FOREACH(larger_type_list, inner_type)
6252+
if (zend_string_equals_ci(ZEND_TYPE_NAME(*inner_type), ZEND_TYPE_NAME(*outer_type))) {
6253+
sum++;
6254+
break;
6255+
}
6256+
ZEND_TYPE_LIST_FOREACH_END();
6257+
ZEND_TYPE_LIST_FOREACH_END();
6258+
6259+
if (sum == smaller_type_list->num_types) {
6260+
zend_string *l_type_str = zend_type_to_string(left_type);
6261+
zend_string *r_type_str = zend_type_to_string(right_type);
6262+
if (smaller_type_list->num_types == larger_type_list->num_types) {
6263+
if (flipped) {
6264+
zend_error_noreturn(E_COMPILE_ERROR, "Type %s is redundant with type %s",
6265+
ZSTR_VAL(r_type_str), ZSTR_VAL(l_type_str));
6266+
} else {
6267+
zend_error_noreturn(E_COMPILE_ERROR, "Type %s is redundant with type %s",
6268+
ZSTR_VAL(l_type_str), ZSTR_VAL(r_type_str));
6269+
}
6270+
} else {
6271+
if (flipped) {
6272+
zend_error_noreturn(E_COMPILE_ERROR, "Type %s is less restrictive than type %s",
6273+
ZSTR_VAL(r_type_str), ZSTR_VAL(l_type_str));
6274+
} else {
6275+
zend_error_noreturn(E_COMPILE_ERROR, "Type %s is less restrictive than type %s",
6276+
ZSTR_VAL(l_type_str), ZSTR_VAL(r_type_str));
6277+
}
6278+
}
6279+
}
6280+
}
6281+
6282+
static void zend_is_intersection_type_redundant_by_single_type(zend_type intersection_type, zend_type single_type)
6283+
{
6284+
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(intersection_type));
6285+
ZEND_ASSERT(!ZEND_TYPE_IS_INTERSECTION(single_type));
6286+
6287+
zend_type *single_intersection_type = NULL;
6288+
ZEND_TYPE_FOREACH(intersection_type, single_intersection_type)
6289+
if (zend_string_equals_ci(ZEND_TYPE_NAME(*single_intersection_type), ZEND_TYPE_NAME(single_type))) {
6290+
zend_string *single_type_str = zend_type_to_string(single_type);
6291+
zend_string *complete_type = zend_type_to_string(intersection_type);
6292+
zend_error_noreturn(E_COMPILE_ERROR, "Type %s is less restrictive than type %s",
6293+
ZSTR_VAL(single_type_str), ZSTR_VAL(complete_type));
6294+
}
6295+
ZEND_TYPE_FOREACH_END();
6296+
}
6297+
6298+
/* Used by both intersection and union types prior to transforming the type list to a full zend_type */
6299+
static void zend_is_type_list_redundant_by_single_type(zend_type_list *type_list, zend_type type)
6300+
{
6301+
ZEND_ASSERT(!ZEND_TYPE_IS_INTERSECTION(type));
6302+
for (size_t i = 0; i < type_list->num_types - 1; i++) {
6303+
if (ZEND_TYPE_IS_INTERSECTION(type_list->types[i])) {
6304+
zend_is_intersection_type_redundant_by_single_type(type_list->types[i], type);
6305+
continue;
6306+
}
6307+
if (zend_string_equals_ci(ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(type))) {
6308+
zend_string *single_type_str = zend_type_to_string(type);
6309+
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str));
6310+
}
6311+
}
6312+
}
6313+
62296314
static zend_type zend_compile_typename(
62306315
zend_ast *ast, bool force_allow_null) /* {{{ */
62316316
{
@@ -6266,7 +6351,15 @@ static zend_type zend_compile_typename(
62666351

62676352
type_list->types[type_list->num_types++] = single_type;
62686353

6269-
/* TODO Check for trivially redundant class types? */
6354+
/* Check for trivially redundant class types */
6355+
for (size_t i = 0; i < type_list->num_types - 1; i++) {
6356+
if (ZEND_TYPE_IS_INTERSECTION(type_list->types[i])) {
6357+
zend_are_intersection_types_redundant(single_type, type_list->types[i]);
6358+
continue;
6359+
}
6360+
/* Type from type list is a simple type */
6361+
zend_is_intersection_type_redundant_by_single_type(single_type, type_list->types[i]);
6362+
}
62706363
continue;
62716364
}
62726365

@@ -6304,17 +6397,7 @@ static zend_type zend_compile_typename(
63046397
type_list->types[type_list->num_types++] = single_type;
63056398

63066399
/* Check for trivially redundant class types */
6307-
for (size_t i = 0; i < type_list->num_types - 1; i++) {
6308-
if (ZEND_TYPE_IS_INTERSECTION(type_list->types[i])) {
6309-
continue;
6310-
}
6311-
if (zend_string_equals_ci(
6312-
ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(single_type))) {
6313-
zend_string *single_type_str = zend_type_to_string(single_type);
6314-
zend_error_noreturn(E_COMPILE_ERROR,
6315-
"Duplicate type %s is redundant", ZSTR_VAL(single_type_str));
6316-
}
6317-
}
6400+
zend_is_type_list_redundant_by_single_type(type_list, single_type);
63186401
}
63196402
}
63206403
}
@@ -6371,14 +6454,7 @@ static zend_type zend_compile_typename(
63716454
type_list->types[type_list->num_types++] = single_type;
63726455

63736456
/* Check for trivially redundant class types */
6374-
for (size_t i = 0; i < type_list->num_types - 1; i++) {
6375-
if (zend_string_equals_ci(
6376-
ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(single_type))) {
6377-
zend_string *single_type_str = zend_type_to_string(single_type);
6378-
zend_error_noreturn(E_COMPILE_ERROR,
6379-
"Duplicate type %s is redundant", ZSTR_VAL(single_type_str));
6380-
}
6381-
}
6457+
zend_is_type_list_redundant_by_single_type(type_list, single_type);
63826458
}
63836459

63846460
ZEND_ASSERT(list->children == type_list->num_types);

0 commit comments

Comments
 (0)