Skip to content

Commit 3985ebc

Browse files
committed
Add checks for redundant types
1 parent cff92e8 commit 3985ebc

File tree

6 files changed

+124
-1
lines changed

6 files changed

+124
-1
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Duplicate identical intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): (A&B)|(A&B) {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type A&B is redundant with A&B in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Intersection type is redudant with another intersection type: prior
3+
--FILE--
4+
<?php
5+
6+
function foo(): (A&B&C)|(A&B) {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type A&B&C is redundant with A&B in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Intersection type is redudant with another intersection type: after
3+
--FILE--
4+
<?php
5+
6+
function foo(): (A&B)|(A&B&C) {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type A&B&C is redundant with A&B in %s on line %d
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Single type is present in intersection of DNF: prior
3+
--FILE--
4+
<?php
5+
6+
function test(): Foo|(A&Foo) {
7+
}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Type A&Foo is redundant with Foo in %s on line %d
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Single type is present in intersection of DNF: after
3+
--FILE--
4+
<?php
5+
6+
function test(): (A&Foo)|Foo {
7+
}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Type A&Foo is redundant with Foo in %s on line %d

Zend/zend_compile.c

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6218,6 +6218,44 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
62186218
}
62196219
}
62206220

6221+
static void zend_is_intersection_redundant_with_intersection(const zend_type a, const zend_type b)
6222+
{
6223+
zend_type outer;
6224+
zend_type inner;
6225+
zend_type *single_outer;
6226+
zend_type *single_inner;
6227+
6228+
if (ZEND_TYPE_LIST(b)->num_types < ZEND_TYPE_LIST(a)->num_types) {
6229+
outer = b;
6230+
inner = a;
6231+
} else {
6232+
outer = a;
6233+
inner = b;
6234+
}
6235+
6236+
/* As outer is the intersection type with the least number of types,
6237+
* it means that it is only redundant if all types of it are found in inner */
6238+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(outer), single_outer) {
6239+
bool is_found = false;
6240+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(inner), single_inner) {
6241+
if (zend_string_equals_ci(ZEND_TYPE_NAME(*single_outer), ZEND_TYPE_NAME(*single_inner))) {
6242+
is_found = true;
6243+
break;
6244+
}
6245+
} ZEND_TYPE_LIST_FOREACH_END();
6246+
/* Type not found in inner, thus cannot be redundant */
6247+
if (!is_found) {
6248+
return;
6249+
}
6250+
} ZEND_TYPE_LIST_FOREACH_END();
6251+
6252+
/* Type is redundant */
6253+
zend_string *inner_type_str = zend_type_to_string(inner);
6254+
zend_string *outer_type_str = zend_type_to_string(outer);
6255+
zend_error_noreturn(E_COMPILE_ERROR,
6256+
"Type %s is redundant with %s", ZSTR_VAL(inner_type_str), ZSTR_VAL(outer_type_str));
6257+
}
6258+
62216259
static zend_type zend_compile_typename(
62226260
zend_ast *ast, bool force_allow_null) /* {{{ */
62236261
{
@@ -6258,7 +6296,28 @@ static zend_type zend_compile_typename(
62586296

62596297
type_list->types[type_list->num_types++] = single_type;
62606298

6261-
/* TODO Check for trivially redundant class types? */
6299+
/* Check for trivially redundant class types */
6300+
zend_type *individual_intersection_type;
6301+
for (size_t i = 0; i < type_list->num_types - 1; i++) {
6302+
if (ZEND_TYPE_HAS_LIST(type_list->types[i])) {
6303+
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(type_list->types[i]));
6304+
zend_is_intersection_redundant_with_intersection(single_type, type_list->types[i]);
6305+
continue;
6306+
}
6307+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(type_list->types[i]));
6308+
6309+
ZEND_TYPE_FOREACH(single_type, individual_intersection_type) {
6310+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*individual_intersection_type));
6311+
if (zend_string_equals_ci( ZEND_TYPE_NAME(type_list->types[i]),
6312+
ZEND_TYPE_NAME(*individual_intersection_type))) {
6313+
zend_string *single_type_str = zend_type_to_string(single_type);
6314+
zend_string *duplicate_type_str = zend_type_to_string(*individual_intersection_type);
6315+
zend_error_noreturn(E_COMPILE_ERROR,
6316+
"Type %s is redundant with %s", ZSTR_VAL(single_type_str),
6317+
ZSTR_VAL(duplicate_type_str));
6318+
}
6319+
} ZEND_TYPE_FOREACH_END();
6320+
}
62626321
continue;
62636322
}
62646323

@@ -6298,6 +6357,18 @@ static zend_type zend_compile_typename(
62986357
/* Check for trivially redundant class types */
62996358
for (size_t i = 0; i < type_list->num_types - 1; i++) {
63006359
if (ZEND_TYPE_IS_INTERSECTION(type_list->types[i])) {
6360+
zend_type *individual_intersection_type;
6361+
ZEND_TYPE_FOREACH(type_list->types[i], individual_intersection_type) {
6362+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*individual_intersection_type));
6363+
if (zend_string_equals_ci(ZEND_TYPE_NAME(single_type),
6364+
ZEND_TYPE_NAME(*individual_intersection_type))) {
6365+
zend_string *single_type_str = zend_type_to_string(single_type);
6366+
zend_string *intersection_str = zend_type_to_string(type_list->types[i]);
6367+
zend_error_noreturn(E_COMPILE_ERROR,
6368+
"Type %s is redundant with %s", ZSTR_VAL(intersection_str),
6369+
ZSTR_VAL(single_type_str));
6370+
}
6371+
} ZEND_TYPE_FOREACH_END();
63016372
continue;
63026373
}
63036374
if (zend_string_equals_ci(

0 commit comments

Comments
 (0)