diff --git a/Zend/tests/type_declarations/list_type_nullable.phpt b/Zend/tests/type_declarations/list_type_nullable.phpt new file mode 100644 index 000000000000..002947a29ff3 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_nullable.phpt @@ -0,0 +1,16 @@ +--TEST-- +nullable list parameter accepts null and matching object list +--FILE-- + $users): void { + echo $users === null ? "null\n" : count($users) . "\n"; +} + +f(null); +f([new User]); +?> +--EXPECT-- +null +1 diff --git a/Zend/tests/type_declarations/list_type_param_basic.phpt b/Zend/tests/type_declarations/list_type_param_basic.phpt new file mode 100644 index 000000000000..b3dc94a112e3 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_param_basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +list parameter accepts matching object list +--FILE-- + $users): void { + echo count($users), "\n"; +} + +f([new User, new User]); +?> +--EXPECT-- +2 diff --git a/Zend/tests/type_declarations/list_type_param_contravariance.phpt b/Zend/tests/type_declarations/list_type_param_contravariance.phpt new file mode 100644 index 000000000000..8318224c1267 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_param_contravariance.phpt @@ -0,0 +1,22 @@ +--TEST-- +array parameter type is contravariant with list +--FILE-- + $items): void { + echo "A\n"; + } +} + +class B extends A { + public function setItems(array $items): void { + echo count($items), "\n"; + } +} + +(new B)->setItems([new User]); +?> +--EXPECT-- +1 diff --git a/Zend/tests/type_declarations/list_type_param_rejects_assoc.phpt b/Zend/tests/type_declarations/list_type_param_rejects_assoc.phpt new file mode 100644 index 000000000000..aa709daa44f2 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_param_rejects_assoc.phpt @@ -0,0 +1,18 @@ +--TEST-- +list parameter rejects associative array +--FILE-- + $users): void { + echo "inside\n"; +} + +try { + f(['x' => new User]); +} catch (TypeError $e) { + echo "TypeError\n"; +} +?> +--EXPECT-- +TypeError diff --git a/Zend/tests/type_declarations/list_type_param_rejects_bad_element.phpt b/Zend/tests/type_declarations/list_type_param_rejects_bad_element.phpt new file mode 100644 index 000000000000..f0f89901be7a --- /dev/null +++ b/Zend/tests/type_declarations/list_type_param_rejects_bad_element.phpt @@ -0,0 +1,18 @@ +--TEST-- +list parameter rejects non-matching object element +--FILE-- + $users): void { + echo "inside\n"; +} + +try { + f([new stdClass]); +} catch (TypeError $e) { + echo "TypeError\n"; +} +?> +--EXPECT-- +TypeError diff --git a/Zend/tests/type_declarations/list_type_property_basic.phpt b/Zend/tests/type_declarations/list_type_property_basic.phpt new file mode 100644 index 000000000000..31c5df0a1606 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_property_basic.phpt @@ -0,0 +1,23 @@ +--TEST-- +list property accepts matching object list and rejects bad element +--FILE-- + $users; +} + +$box = new Box; +$box->users = [new User, new User]; +echo count($box->users), "\n"; + +try { + $box->users = [new stdClass]; +} catch (TypeError $e) { + echo "TypeError\n"; +} +?> +--EXPECT-- +2 +TypeError diff --git a/Zend/tests/type_declarations/list_type_property_nullable.phpt b/Zend/tests/type_declarations/list_type_property_nullable.phpt new file mode 100644 index 000000000000..20f963a4bb91 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_property_nullable.phpt @@ -0,0 +1,18 @@ +--TEST-- +nullable list property accepts null and matching object list +--FILE-- + $users = null; +} + +$box = new Box; +var_dump($box->users); +$box->users = [new User]; +echo count($box->users), "\n"; +?> +--EXPECT-- +NULL +1 diff --git a/Zend/tests/type_declarations/list_type_property_reflection.phpt b/Zend/tests/type_declarations/list_type_property_reflection.phpt new file mode 100644 index 000000000000..ad85a1674087 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_property_reflection.phpt @@ -0,0 +1,15 @@ +--TEST-- +list property reflection string +--FILE-- + $users; +} + +$prop = new ReflectionProperty(Box::class, 'users'); +echo (string) $prop->getType(), "\n"; +?> +--EXPECT-- +list diff --git a/Zend/tests/type_declarations/list_type_reflection.phpt b/Zend/tests/type_declarations/list_type_reflection.phpt new file mode 100644 index 000000000000..196f1ba1ac3e --- /dev/null +++ b/Zend/tests/type_declarations/list_type_reflection.phpt @@ -0,0 +1,13 @@ +--TEST-- +list parameter reflection string +--FILE-- + $users): void {} + +$param = (new ReflectionFunction('f'))->getParameters()[0]; +echo (string) $param->getType(), "\n"; +?> +--EXPECT-- +list diff --git a/Zend/tests/type_declarations/list_type_return_basic.phpt b/Zend/tests/type_declarations/list_type_return_basic.phpt new file mode 100644 index 000000000000..ac371e897442 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_return_basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +list return type accepts matching object list +--FILE-- + { + return [new User, new User]; +} + +echo count(f()), "\n"; +?> +--EXPECT-- +2 diff --git a/Zend/tests/type_declarations/list_type_return_covariance.phpt b/Zend/tests/type_declarations/list_type_return_covariance.phpt new file mode 100644 index 000000000000..4813b1ed0d83 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_return_covariance.phpt @@ -0,0 +1,22 @@ +--TEST-- +list return type is covariant with array +--FILE-- + { + return [new User]; + } +} + +echo count((new B)->getItems()), "\n"; +?> +--EXPECT-- +1 diff --git a/Zend/tests/type_declarations/list_type_return_rejects_bad_element.phpt b/Zend/tests/type_declarations/list_type_return_rejects_bad_element.phpt new file mode 100644 index 000000000000..89e878ee45e6 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_return_rejects_bad_element.phpt @@ -0,0 +1,18 @@ +--TEST-- +list return type rejects non-matching object element +--FILE-- + { + return [new stdClass]; +} + +try { + f(); +} catch (TypeError $e) { + echo "TypeError\n"; +} +?> +--EXPECT-- +TypeError diff --git a/Zend/tests/type_declarations/list_type_scalar_string.phpt b/Zend/tests/type_declarations/list_type_scalar_string.phpt new file mode 100644 index 000000000000..6f93b23a3dc7 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_scalar_string.phpt @@ -0,0 +1,20 @@ +--TEST-- +list parameter accepts strings and rejects non-string elements +--FILE-- + $items): void { + echo count($items), "\n"; +} + +f(["a", "b"]); + +try { + f(["a", 123]); +} catch (TypeError $e) { + echo "TypeError\n"; +} +?> +--EXPECT-- +2 +TypeError diff --git a/Zend/tests/type_declarations/list_type_union_null.phpt b/Zend/tests/type_declarations/list_type_union_null.phpt new file mode 100644 index 000000000000..175f3dbb8c92 --- /dev/null +++ b/Zend/tests/type_declarations/list_type_union_null.phpt @@ -0,0 +1,10 @@ +--TEST-- +list cannot be part of a union type +--FILE-- +|null $users): void {} +?> +--EXPECTF-- +Fatal error: Type list cannot be part of a union type in %s on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index a7e26711cd17..403ce7e34fb0 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2398,6 +2398,11 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio default: ZEND_UNREACHABLE(); } break; + case ZEND_AST_LIST_TYPE: + APPEND_STR("list<"); + zend_ast_export_ex(str, ast->child[0], priority, indent); + APPEND_STR(">"); + break; case ZEND_AST_TYPE: switch (ast->attr & ~ZEND_TYPE_NULLABLE) { case IS_ARRAY: APPEND_STR("array"); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 24b77d7d3493..68b2aa717004 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -79,6 +79,7 @@ enum _zend_ast_kind { /* 1 child node */ ZEND_AST_VAR = 1 << ZEND_AST_NUM_CHILDREN_SHIFT, + ZEND_AST_LIST_TYPE, ZEND_AST_CONST, ZEND_AST_UNPACK, ZEND_AST_UNARY_PLUS, diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a96af71aa900..dc0225ac6b3b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1444,6 +1444,13 @@ static zend_string *add_intersection_type(zend_string *str, zend_string *zend_type_to_string_resolved(const zend_type type, const zend_class_entry *scope) { zend_string *str = NULL; + if (ZEND_TYPE_IS_TYPED_LIST(type)) { + zend_string *inner = zend_type_to_string_resolved(ZEND_TYPE_LIST(type)->types[0], scope); + zend_string *result = zend_string_concat3("list<", 5, ZSTR_VAL(inner), ZSTR_LEN(inner), ">", 1); + zend_string_release(inner); + return result; + } + /* Pure intersection type */ if (ZEND_TYPE_IS_INTERSECTION(type)) { ZEND_ASSERT(!ZEND_TYPE_IS_UNION(type)); @@ -7364,6 +7371,16 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */ static zend_type zend_compile_single_typename(zend_ast *ast) { ZEND_ASSERT(!(ast->attr & ZEND_TYPE_NULLABLE)); + if (ast->kind == ZEND_AST_LIST_TYPE) { + zend_ast *inner_ast = ast->child[0]; + zend_type inner_type = zend_compile_single_typename(inner_ast); + zend_type_list *type_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(1)); + type_list->num_types = 1; + type_list->types[0] = inner_type; + zend_type type = ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY | _ZEND_TYPE_TYPED_LIST_BIT | _ZEND_TYPE_ARENA_BIT); + ZEND_TYPE_SET_LIST(type, type_list); + return type; + } if (ast->kind == ZEND_AST_TYPE) { if (ast->attr == IS_STATIC && !CG(active_class_entry) && zend_is_scope_known()) { zend_error_noreturn(E_COMPILE_ERROR, @@ -7592,6 +7609,9 @@ static zend_type zend_compile_typename_ex( } single_type = zend_compile_single_typename(type_ast); + if (ZEND_TYPE_IS_TYPED_LIST(single_type)) { + zend_error_noreturn(E_COMPILE_ERROR, "Type list cannot be part of a union type"); + } uint32_t single_type_mask = ZEND_TYPE_PURE_MASK(single_type); if (single_type_mask == MAY_BE_ANY) { @@ -8197,7 +8217,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 ZEND_TYPE_FULL_MASK(arg_info->type) |= arg_info_flags; if (opcode == ZEND_RECV) { opline->op2.num = type_ast ? - ZEND_TYPE_FULL_MASK(arg_info->type) : MAY_BE_ANY; + (ZEND_TYPE_IS_TYPED_LIST(arg_info->type) ? 0 : ZEND_TYPE_FULL_MASK(arg_info->type)) : MAY_BE_ANY; } if (is_promoted) { diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 85461eaa1569..9db567f8489d 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1036,9 +1036,16 @@ static bool zend_check_and_resolve_property_or_class_constant_class_type( return false; } +static zend_always_inline bool zend_check_typed_list_type( + const zend_type *type, zval *arg, bool is_return_type, bool is_internal); + static zend_always_inline bool i_zend_check_property_type(const zend_property_info *info, zval *property, bool strict) { ZEND_ASSERT(!Z_ISREF_P(property)); + if (UNEXPECTED(ZEND_TYPE_IS_TYPED_LIST(info->type))) { + return zend_check_typed_list_type(&info->type, property, false, false); + } + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(info->type, Z_TYPE_P(property)))) { return 1; } @@ -1148,10 +1155,45 @@ static bool zend_check_intersection_type_from_list( return true; } +static zend_always_inline bool zend_check_type( + const zend_type *type, zval *arg, bool is_return_type, bool is_internal); + +static zend_always_inline bool zend_check_typed_list_type( + const zend_type *type, zval *arg, bool is_return_type, bool is_internal) +{ + const zend_type *inner_type; + zval *element; + + if (UNEXPECTED(Z_TYPE_P(arg) != IS_ARRAY)) { + return false; + } + + if (UNEXPECTED(!zend_array_is_list(Z_ARRVAL_P(arg)))) { + return false; + } + + inner_type = &ZEND_TYPE_LIST(*type)->types[0]; + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arg), element) { + if (UNEXPECTED(!zend_check_type(inner_type, element, is_return_type, is_internal))) { + return false; + } + } ZEND_HASH_FOREACH_END(); + + return true; +} + static zend_always_inline bool zend_check_type_slow( const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type, bool is_internal) { + if (UNEXPECTED(ZEND_TYPE_IS_TYPED_LIST(*type))) { + if (Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(*type)) { + return true; + } + return zend_check_typed_list_type(type, arg, is_return_type, is_internal); + } + if (ZEND_TYPE_IS_COMPLEX(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { zend_class_entry *ce; if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) { @@ -1222,7 +1264,8 @@ static zend_always_inline bool zend_check_type( arg = Z_REFVAL_P(arg); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(*type, Z_TYPE_P(arg)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(*type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(*type, Z_TYPE_P(arg)))) { return 1; } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index bf4c84404a67..86812047d6d2 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -686,6 +686,11 @@ static inheritance_status zend_perform_covariant_type_check( /* Builtin types may be removed, but not added */ uint32_t fe_type_mask = ZEND_TYPE_PURE_MASK(fe_type); uint32_t proto_type_mask = ZEND_TYPE_PURE_MASK(proto_type); + + if (ZEND_TYPE_IS_TYPED_LIST(fe_type) && (proto_type_mask & MAY_BE_ARRAY)) { + return INHERITANCE_SUCCESS; + } + uint32_t added_types = fe_type_mask & ~proto_type_mask; if (added_types) { if ((added_types & MAY_BE_STATIC) diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index b4dda00404ea..b4a5706bf6d6 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -871,6 +871,7 @@ type_expr_without_static: type_without_static: T_ARRAY { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); } + | T_LIST '<' type_without_static '>' { $$ = zend_ast_create(ZEND_AST_LIST_TYPE, $3); } | T_CALLABLE { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); } | name { $$ = $1; } ; diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 30d77da67323..1c8e625cb05e 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -143,6 +143,8 @@ typedef struct { #define _ZEND_TYPE_INTERSECTION_BIT (1u << 19) /* Whether the type is a union type */ #define _ZEND_TYPE_UNION_BIT (1u << 18) +/* Whether this represents a typed list array type. */ +#define _ZEND_TYPE_TYPED_LIST_BIT (1u << 30) /* Type mask excluding the flags above. */ #define _ZEND_TYPE_MAY_BE_MASK ((1u << 18) - 1) /* Must have same value as MAY_BE_NULL */ @@ -174,6 +176,9 @@ typedef struct { #define ZEND_TYPE_IS_UNION(t) \ ((((t).type_mask) & _ZEND_TYPE_UNION_BIT) != 0) +#define ZEND_TYPE_IS_TYPED_LIST(t) \ + ((((t).type_mask) & _ZEND_TYPE_TYPED_LIST_BIT) != 0) + #define ZEND_TYPE_USES_ARENA(t) \ ((((t).type_mask) & _ZEND_TYPE_ARENA_BIT) != 0) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 1f7e09d1be35..ef2ab96ddc8f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4491,7 +4491,8 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index d5860da23b4c..26e6767f00c8 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -11209,7 +11209,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } @@ -21518,7 +21519,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_VERIFY_RETURN ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } @@ -29563,7 +29565,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_VERIFY_RETURN ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } @@ -36950,7 +36953,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_VERIFY_RETURN ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } @@ -49159,7 +49163,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_VERIFY_RETURN ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } @@ -63765,7 +63770,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_VERIF ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } @@ -73974,7 +73980,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_VERIFY_RETURN_TYPE ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } @@ -82019,7 +82026,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_VERIFY_RETURN_TYPE ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } @@ -89406,7 +89414,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_VERIFY_RETURN_TYPE ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } @@ -101513,7 +101522,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_VERIFY_RETURN_TYPE ZVAL_DEREF(retval_ptr); } - if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { + if (EXPECTED(!ZEND_TYPE_IS_TYPED_LIST(ret_info->type)) + && EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) { ZEND_VM_NEXT_OPCODE(); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 94d8bb7d149c..445242e8c204 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1468,6 +1468,9 @@ static reflection_type_kind get_type_kind(zend_type type) { uint32_t type_mask_without_null = ZEND_TYPE_PURE_MASK_WITHOUT_NULL(type); if (ZEND_TYPE_HAS_LIST(type)) { + if (ZEND_TYPE_IS_TYPED_LIST(type)) { + return NAMED_TYPE; + } if (ZEND_TYPE_IS_INTERSECTION(type)) { return INTERSECTION_TYPE; }