Skip to content

Commit c9159f3

Browse files
committed
Check for redundant types
1 parent bfb39a5 commit c9159f3

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
@@ -6218,6 +6218,91 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
62186218
}
62196219
}
62206220

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

62596344
type_list->types[type_list->num_types++] = single_type;
62606345

6261-
/* TODO Check for trivially redundant class types? */
6346+
/* Check for trivially redundant class types */
6347+
for (size_t i = 0; i < type_list->num_types - 1; i++) {
6348+
if (ZEND_TYPE_IS_INTERSECTION(type_list->types[i])) {
6349+
zend_are_intersection_types_redundant(single_type, type_list->types[i]);
6350+
continue;
6351+
}
6352+
/* Type from type list is a simple type */
6353+
zend_is_intersection_type_redundant_by_single_type(single_type, type_list->types[i]);
6354+
}
62626355
continue;
62636356
}
62646357

@@ -6296,17 +6389,7 @@ static zend_type zend_compile_typename(
62966389
type_list->types[type_list->num_types++] = single_type;
62976390

62986391
/* Check for trivially redundant class types */
6299-
for (size_t i = 0; i < type_list->num_types - 1; i++) {
6300-
if (ZEND_TYPE_IS_INTERSECTION(type_list->types[i])) {
6301-
continue;
6302-
}
6303-
if (zend_string_equals_ci(
6304-
ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(single_type))) {
6305-
zend_string *single_type_str = zend_type_to_string(single_type);
6306-
zend_error_noreturn(E_COMPILE_ERROR,
6307-
"Duplicate type %s is redundant", ZSTR_VAL(single_type_str));
6308-
}
6309-
}
6392+
zend_is_type_list_redundant_by_single_type(type_list, single_type);
63106393
}
63116394
}
63126395
}
@@ -6359,14 +6442,7 @@ static zend_type zend_compile_typename(
63596442
type_list->types[type_list->num_types++] = single_type;
63606443

63616444
/* Check for trivially redundant class types */
6362-
for (size_t i = 0; i < type_list->num_types - 1; i++) {
6363-
if (zend_string_equals_ci(
6364-
ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(single_type))) {
6365-
zend_string *single_type_str = zend_type_to_string(single_type);
6366-
zend_error_noreturn(E_COMPILE_ERROR,
6367-
"Duplicate type %s is redundant", ZSTR_VAL(single_type_str));
6368-
}
6369-
}
6445+
zend_is_type_list_redundant_by_single_type(type_list, single_type);
63706446
}
63716447

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

0 commit comments

Comments
 (0)