Skip to content

Commit 4da3e77

Browse files
committed
Covariance on inheriting classes with iterable
1 parent bea9df5 commit 4da3e77

File tree

2 files changed

+40
-8
lines changed

2 files changed

+40
-8
lines changed

Zend/zend_compile.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,17 +1253,20 @@ static void zend_mark_function_as_generator() /* {{{ */
12531253
}
12541254

12551255
if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
1256-
const char *msg = "Generators may only declare a return type of Generator, Iterator or Traversable, %s is not permitted";
12571256
zend_arg_info return_info = CG(active_op_array)->arg_info[-1];
12581257

1259-
if (!return_info.class_name) {
1260-
zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(return_info.type_hint));
1261-
}
1258+
if (return_info.type_hint != IS_ITERABLE) {
1259+
const char *msg = "Generators may only declare a return type of Generator, Iterator, Traversable, or iterable, %s is not permitted";
1260+
1261+
if (!return_info.class_name) {
1262+
zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(return_info.type_hint));
1263+
}
12621264

1263-
if (!zend_string_equals_literal_ci(return_info.class_name, "Traversable")
1264-
&& !zend_string_equals_literal_ci(return_info.class_name, "Iterator")
1265-
&& !zend_string_equals_literal_ci(return_info.class_name, "Generator")) {
1266-
zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(return_info.class_name));
1265+
if (!zend_string_equals_literal_ci(return_info.class_name, "Traversable")
1266+
&& !zend_string_equals_literal_ci(return_info.class_name, "Iterator")
1267+
&& !zend_string_equals_literal_ci(return_info.class_name, "Generator")) {
1268+
zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(return_info.class_name));
1269+
}
12671270
}
12681271
}
12691272

Zend/zend_inheritance.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "zend_compile.h"
2323
#include "zend_execute.h"
2424
#include "zend_inheritance.h"
25+
#include "zend_interfaces.h"
2526
#include "zend_smart_str.h"
2627
#include "zend_inheritance.h"
2728

@@ -167,6 +168,26 @@ char *zend_visibility_string(uint32_t fn_flags) /* {{{ */
167168
}
168169
/* }}} */
169170

171+
static zend_bool zend_iterable_type_check(zend_arg_info *arg_info) /* {{{ */
172+
{
173+
if (arg_info->class_name) {
174+
zend_class_entry *ce;
175+
176+
ce = zend_lookup_class(arg_info->class_name);
177+
178+
if (ce && instanceof_function(ce, zend_ce_traversable)) {
179+
return 1;
180+
}
181+
}
182+
183+
if (arg_info->type_hint == IS_ITERABLE || arg_info->type_hint == IS_ARRAY) {
184+
return 1;
185+
}
186+
187+
return 0;
188+
}
189+
/* }}} */
190+
170191
static int zend_do_perform_type_hint_check(const zend_function *fe, zend_arg_info *fe_arg_info, const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */
171192
{
172193
if (ZEND_LOG_XOR(fe_arg_info->class_name, proto_arg_info->class_name)) {
@@ -314,6 +335,10 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
314335
} else {
315336
proto_arg_info = &proto->common.arg_info[proto->common.num_args];
316337
}
338+
339+
if (fe_arg_info->type_hint == IS_ITERABLE && zend_iterable_type_check(proto_arg_info)) {
340+
continue;
341+
}
317342

318343
if (!zend_do_perform_type_hint_check(fe, fe_arg_info, proto, proto_arg_info)) {
319344
return 0;
@@ -338,6 +363,10 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
338363
if (!(fe->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
339364
return 0;
340365
}
366+
367+
if (proto->common.arg_info[-1].type_hint == IS_ITERABLE && zend_iterable_type_check(fe->common.arg_info - 1)) {
368+
return 1;
369+
}
341370

342371
if (!zend_do_perform_type_hint_check(fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1)) {
343372
return 0;

0 commit comments

Comments
 (0)