diff --git a/.gdbinit b/.gdbinit index 8e54830ef2a1b..a903c086e53b4 100644 --- a/.gdbinit +++ b/.gdbinit @@ -252,37 +252,34 @@ define ____printzv_contents ____printzv &$zvalue->value.ref->val $arg1 end if $type == 11 - printf "const: %s", $zvalue->value.str->val + printf "CONSTANT_AST" end if $type == 12 - printf "CONSTANT_AST" + printf "CALLABLE" end if $type == 13 - printf "indirect: " - ____printzv $zvalue->value.zv $arg1 + printf "ITERABLE" end if $type == 14 - printf "pointer: %p", $zvalue->value.ptr + printf "VOID" end if $type == 15 - printf "_ERROR" + printf "indirect: " + ____printzv $zvalue->value.zv $arg1 end if $type == 16 - printf "_BOOL" + printf "pointer: %p", $zvalue->value.ptr end if $type == 17 - printf "CALLABLE" + printf "_ERROR" end if $type == 18 - printf "ITERABLE" + printf "_BOOL" end if $type == 19 - printf "VOID" - end - if $type == 20 printf "_NUMBER" end - if $type > 20 + if $type > 19 printf "unknown type %d", $type end printf "\n" diff --git a/Zend/tests/type_declarations/typed_properties_054.phpt b/Zend/tests/type_declarations/typed_properties_054.phpt index 38de9815edff5..cb771d6e64549 100644 --- a/Zend/tests/type_declarations/typed_properties_054.phpt +++ b/Zend/tests/type_declarations/typed_properties_054.phpt @@ -9,4 +9,4 @@ $obj = new A; var_dump($obj); ?> --EXPECTF-- -Fatal error: Property A::$a cannot have type callable in %s on line %d +Fatal error: Property A::$a cannot have type ?callable in %s on line %d diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 5cdba4450b5d6..acc6bb9cf3718 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -27,6 +27,7 @@ #include "zend_operators.h" #include "zend_variables.h" #include "zend_execute.h" +#include "zend_type_info.h" BEGIN_EXTERN_C() @@ -98,11 +99,11 @@ typedef struct _zend_fcall_info_cache { #define ZEND_ARG_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 0}, #define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, 0, pass_by_ref, 0}, #define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 0 }, -#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE(IS_ARRAY, allow_null), pass_by_ref, 0 }, -#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE(IS_CALLABLE, allow_null), pass_by_ref, 0 }, -#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE(type_hint, allow_null), pass_by_ref, 0 }, +#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(IS_ARRAY, allow_null), pass_by_ref, 0 }, +#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(IS_CALLABLE, allow_null), pass_by_ref, 0 }, +#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(type_hint, allow_null), pass_by_ref, 0 }, #define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 1 }, -#define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE(type_hint, allow_null), pass_by_ref, 1 }, +#define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(type_hint, allow_null), pass_by_ref, 1 }, #define ZEND_ARG_VARIADIC_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 1 }, #define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \ @@ -114,7 +115,7 @@ typedef struct _zend_fcall_info_cache { #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \ static const zend_internal_arg_info name[] = { \ - { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE(type, allow_null), return_reference, 0 }, + { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE_CODE(type, allow_null), return_reference, 0 }, #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(name, type, allow_null) \ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, 0, -1, type, allow_null) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 19992e2cbd49f..8f8920c46224b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1080,6 +1080,70 @@ ZEND_API int do_bind_class(zval *lcname, zend_string *lc_parent_name) /* {{{ */ } /* }}} */ +zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope) { + zend_bool nullable = ZEND_TYPE_ALLOW_NULL(type); + zend_string *str; + if (ZEND_TYPE_IS_NAME(type)) { + zend_string *name = ZEND_TYPE_NAME(type); + if (scope) { + if (zend_string_equals_literal_ci(name, "self")) { + name = scope->name; + } else if (zend_string_equals_literal_ci(name, "parent") && scope->parent) { + name = scope->parent->name; + } + } + str = zend_string_copy(name); + } else if (ZEND_TYPE_IS_CE(type)) { + str = zend_string_copy(ZEND_TYPE_CE(type)->name); + } else { + uint32_t type_mask = ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(type)); + switch (type_mask) { + case MAY_BE_FALSE|MAY_BE_TRUE: + str = ZSTR_KNOWN(ZEND_STR_BOOL); + break; + case MAY_BE_LONG: + str = ZSTR_KNOWN(ZEND_STR_INT); + break; + case MAY_BE_DOUBLE: + str = ZSTR_KNOWN(ZEND_STR_FLOAT); + break; + case MAY_BE_STRING: + str = ZSTR_KNOWN(ZEND_STR_STRING); + break; + case MAY_BE_ARRAY: + str = ZSTR_KNOWN(ZEND_STR_ARRAY); + break; + case MAY_BE_OBJECT: + str = ZSTR_KNOWN(ZEND_STR_OBJECT); + break; + case MAY_BE_CALLABLE: + str = ZSTR_KNOWN(ZEND_STR_CALLABLE); + break; + case MAY_BE_ITERABLE: + str = ZSTR_KNOWN(ZEND_STR_ITERABLE); + break; + case MAY_BE_VOID: + str = ZSTR_KNOWN(ZEND_STR_VOID); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + } + + if (nullable) { + zend_string *nullable_str = zend_string_alloc(ZSTR_LEN(str) + 1, 0); + ZSTR_VAL(nullable_str)[0] = '?'; + memcpy(ZSTR_VAL(nullable_str) + 1, ZSTR_VAL(str), ZSTR_LEN(str)); + ZSTR_VAL(nullable_str)[ZSTR_LEN(nullable_str)] = '\0'; + zend_string_release(str); + return nullable_str; + } + return str; +} + +zend_string *zend_type_to_string(zend_type type) { + return zend_type_to_string_resolved(type, NULL); +} + static void zend_mark_function_as_generator() /* {{{ */ { if (!CG(active_op_array)->function_name) { @@ -1089,19 +1153,22 @@ static void zend_mark_function_as_generator() /* {{{ */ if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { zend_arg_info return_info = CG(active_op_array)->arg_info[-1]; + zend_bool valid_type; + if (ZEND_TYPE_IS_CLASS(return_info.type)) { + zend_string *name = ZEND_TYPE_NAME(return_info.type); + valid_type = zend_string_equals_literal_ci(name, "Traversable") + || zend_string_equals_literal_ci(name, "Iterator") + || zend_string_equals_literal_ci(name, "Generator"); + } else { + valid_type = (ZEND_TYPE_MASK(return_info.type) & MAY_BE_ITERABLE) != 0; + } - if (ZEND_TYPE_CODE(return_info.type) != IS_ITERABLE) { - const char *msg = "Generators may only declare a return type of Generator, Iterator, Traversable, or iterable, %s is not permitted"; - - if (!ZEND_TYPE_IS_CLASS(return_info.type)) { - zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(ZEND_TYPE_CODE(return_info.type))); - } - - if (!zend_string_equals_literal_ci(ZEND_TYPE_NAME(return_info.type), "Traversable") - && !zend_string_equals_literal_ci(ZEND_TYPE_NAME(return_info.type), "Iterator") - && !zend_string_equals_literal_ci(ZEND_TYPE_NAME(return_info.type), "Generator")) { - zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(ZEND_TYPE_NAME(return_info.type))); - } + if (!valid_type) { + zend_string *str = zend_type_to_string(return_info.type); + zend_error_noreturn(E_COMPILE_ERROR, + "Generators may only declare a return type of " \ + "Generator, Iterator, Traversable, or iterable, %s is not permitted", + ZSTR_VAL(str)); } } @@ -2051,11 +2118,12 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */ static void zend_emit_return_type_check( znode *expr, zend_arg_info *return_info, zend_bool implicit) /* {{{ */ { - if (ZEND_TYPE_IS_SET(return_info->type)) { + zend_type type = return_info->type; + if (ZEND_TYPE_IS_SET(type)) { zend_op *opline; /* `return ...;` is illegal in a void function (but `return;` isn't) */ - if (ZEND_TYPE_CODE(return_info->type) == IS_VOID) { + if (ZEND_TYPE_IS_CODE(type) && ZEND_TYPE_CONTAINS_CODE(type, IS_VOID)) { if (expr) { if (expr->op_type == IS_CONST && Z_TYPE(expr->u.constant) == IS_NULL) { zend_error_noreturn(E_COMPILE_ERROR, @@ -2070,7 +2138,7 @@ static void zend_emit_return_type_check( } if (!expr && !implicit) { - if (ZEND_TYPE_ALLOW_NULL(return_info->type)) { + if (ZEND_TYPE_ALLOW_NULL(type)) { zend_error_noreturn(E_COMPILE_ERROR, "A function with return type must return a value " "(did you mean \"return null;\" instead of \"return;\"?)"); @@ -2081,12 +2149,8 @@ static void zend_emit_return_type_check( } if (expr && expr->op_type == IS_CONST) { - if ((ZEND_TYPE_CODE(return_info->type) == Z_TYPE(expr->u.constant)) - ||((ZEND_TYPE_CODE(return_info->type) == _IS_BOOL) - && (Z_TYPE(expr->u.constant) == IS_FALSE - || Z_TYPE(expr->u.constant) == IS_TRUE)) - || (ZEND_TYPE_ALLOW_NULL(return_info->type) - && Z_TYPE(expr->u.constant) == IS_NULL)) { + if (ZEND_TYPE_IS_CODE(type) + && ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE(expr->u.constant))) { /* we don't need run-time check */ return; } @@ -5248,7 +5312,7 @@ static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null } if (ast->kind == ZEND_AST_TYPE) { - return ZEND_TYPE_ENCODE(ast->attr, allow_null); + return ZEND_TYPE_ENCODE_CODE(ast->attr, allow_null); } else { zend_string *class_name = zend_ast_get_str(ast); zend_uchar type = zend_lookup_builtin_type_by_name(class_name); @@ -5259,7 +5323,7 @@ static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null "Type declaration '%s' must be unqualified", ZSTR_VAL(zend_string_tolower(class_name))); } - return ZEND_TYPE_ENCODE(type, allow_null); + return ZEND_TYPE_ENCODE_CODE(type, allow_null); } else { uint32_t fetch_type = zend_get_class_fetch_type_ast(ast); if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { @@ -5291,7 +5355,9 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ arg_infos->is_variadic = 0; arg_infos->type = zend_compile_typename(return_type_ast, 0); - if (ZEND_TYPE_CODE(arg_infos->type) == IS_VOID && ZEND_TYPE_ALLOW_NULL(arg_infos->type)) { + if (ZEND_TYPE_IS_CODE(arg_infos->type) + && ZEND_TYPE_CONTAINS_CODE(arg_infos->type, IS_VOID) + && ZEND_TYPE_ALLOW_NULL(arg_infos->type)) { zend_error_noreturn(E_COMPILE_ERROR, "Void type cannot be nullable"); } @@ -5369,7 +5435,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ arg_info->pass_by_reference = is_ref; arg_info->is_variadic = is_variadic; /* TODO: Keep compatibility, but may be better reset "allow_null" ??? */ - arg_info->type = ZEND_TYPE_ENCODE(0, 1); + arg_info->type = ZEND_TYPE_ENCODE_CODE(0, 1); if (type_ast) { uint32_t default_type = default_ast ? Z_TYPE(default_node.u.constant) : IS_UNDEF; @@ -5381,9 +5447,9 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ arg_info->type = zend_compile_typename(type_ast, default_type == IS_NULL); is_class = ZEND_TYPE_IS_CLASS(arg_info->type); - arg_type = ZEND_TYPE_CODE(arg_info->type); + arg_type = !is_class ? ZEND_TYPE_MASK(arg_info->type) : 0; - if (arg_type == IS_VOID) { + if (arg_type & MAY_BE_VOID) { zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type"); } @@ -5391,45 +5457,36 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ if (is_class) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " "with a class type can only be NULL"); - } else switch (arg_type) { - case IS_CALLABLE: + } else { + if (arg_type & MAY_BE_CALLABLE) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " "with callable type can only be NULL"); - break; - - case IS_ARRAY: + } else if (arg_type & MAY_BE_ARRAY) { if (default_type != IS_ARRAY) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " "with array type can only be an array or NULL"); } - break; - - case IS_DOUBLE: + } else if (arg_type & MAY_BE_DOUBLE) { if (default_type != IS_DOUBLE && default_type != IS_LONG) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " "with a float type can only be float, integer, or NULL"); } - break; - - case IS_ITERABLE: + } else if (arg_type & MAY_BE_ITERABLE) { if (default_type != IS_ARRAY) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " "with iterable type can only be an array or NULL"); } - break; - - case IS_OBJECT: + } else if (arg_type & MAY_BE_OBJECT) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " "with an object type can only be NULL"); - break; - - default: - if (!ZEND_SAME_FAKE_TYPE(arg_type, default_type)) { - zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " - "with a %s type can only be %s or NULL", - zend_get_type_by_const(arg_type), zend_get_type_by_const(arg_type)); - } - break; + } else if (!ZEND_TYPE_CONTAINS_CODE(arg_type, default_type)) { + zend_string *type_str = + zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(arg_type)); + zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " + "with a %s type can only be %s or NULL", + ZSTR_VAL(type_str), ZSTR_VAL(type_str)); + zend_string_release(type_str); + } } } @@ -5953,12 +6010,12 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) / if (type_ast) { type = zend_compile_typename(type_ast, 0); - if (ZEND_TYPE_CODE(type) == IS_VOID || ZEND_TYPE_CODE(type) == IS_CALLABLE) { + if (ZEND_TYPE_IS_CODE(type) + && (ZEND_TYPE_MASK(type) & (MAY_BE_VOID|MAY_BE_CALLABLE))) { + zend_string *str = zend_type_to_string(type); zend_error_noreturn(E_COMPILE_ERROR, "Property %s::$%s cannot have type %s", - ZSTR_VAL(ce->name), - ZSTR_VAL(name), - zend_get_type_by_const(ZEND_TYPE_CODE(type))); + ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(str)); } } @@ -5984,32 +6041,32 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) / if (ZEND_TYPE_IS_SET(type) && !Z_CONSTANT(value_zv)) { if (Z_TYPE(value_zv) == IS_NULL) { if (!ZEND_TYPE_ALLOW_NULL(type)) { - const char *name = ZEND_TYPE_IS_CLASS(type) - ? ZSTR_VAL(ZEND_TYPE_NAME(type)) : zend_get_type_by_const(ZEND_TYPE_CODE(type)); + zend_string *str = zend_type_to_string(type); zend_error_noreturn(E_COMPILE_ERROR, "Default value for property of type %s may not be null. " "Use the nullable type ?%s to allow null default value", - name, name); + ZSTR_VAL(str), ZSTR_VAL(str)); } } else if (ZEND_TYPE_IS_CLASS(type)) { zend_error_noreturn(E_COMPILE_ERROR, "Property of type %s may not have default value", ZSTR_VAL(ZEND_TYPE_NAME(type))); - } else if (ZEND_TYPE_CODE(type) == IS_ARRAY || ZEND_TYPE_CODE(type) == IS_ITERABLE) { + } else if (ZEND_TYPE_MASK(type) & (MAY_BE_ARRAY|MAY_BE_ITERABLE)) { if (Z_TYPE(value_zv) != IS_ARRAY) { + zend_string *str = zend_type_to_string(type); zend_error_noreturn(E_COMPILE_ERROR, "Default value for property of type %s can only be an array", - zend_get_type_by_const(ZEND_TYPE_CODE(type))); + ZSTR_VAL(str)); } - } else if (ZEND_TYPE_CODE(type) == IS_DOUBLE) { + } else if (ZEND_TYPE_MASK(type) & MAY_BE_DOUBLE) { if (Z_TYPE(value_zv) != IS_DOUBLE && Z_TYPE(value_zv) != IS_LONG) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for property of type float can only be float or int"); } - } else if (!ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(type), Z_TYPE(value_zv))) { + } else if (!ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE(value_zv))) { + zend_string *str = zend_type_to_string(type); zend_error_noreturn(E_COMPILE_ERROR, "Default value for property of type %s can only be %s", - zend_get_type_by_const(ZEND_TYPE_CODE(type)), - zend_get_type_by_const(ZEND_TYPE_CODE(type))); + ZSTR_VAL(str), ZSTR_VAL(str)); } } } else if (!ZEND_TYPE_IS_SET(type)) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index a960115aab632..c4c2b8bac7a0c 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -847,6 +847,9 @@ int ZEND_FASTCALL zendlex(zend_parser_stack_elem *elem); void zend_assert_valid_class_name(const zend_string *const_name); +zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope); +zend_string *zend_type_to_string(zend_type type); + /* BEGIN: OPCODES */ #include "zend_vm_opcodes.h" diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 1d628a235206d..3b5fa3022cab2 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -582,39 +582,26 @@ static zend_never_inline ZEND_COLD int zend_wrong_assign_to_variable_reference(z return 1; } -static void zend_format_type(zend_type type, const char **part1, const char **part2) { - *part1 = ZEND_TYPE_ALLOW_NULL(type) ? "?" : ""; - if (ZEND_TYPE_IS_CLASS(type)) { - if (ZEND_TYPE_IS_CE(type)) { - *part2 = ZSTR_VAL(ZEND_TYPE_CE(type)->name); - } else { - *part2 = ZSTR_VAL(ZEND_TYPE_NAME(type)); - } - } else { - *part2 = zend_get_type_by_const(ZEND_TYPE_CODE(type)); - } -} - static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop, const char *type) { - const char *prop_type1, *prop_type2; - zend_format_type(prop->type, &prop_type1, &prop_type2); + zend_string *type_str = zend_type_to_string(prop->type); zend_type_error( - "Cannot auto-initialize an %s inside property %s::$%s of type %s%s", + "Cannot auto-initialize an %s inside property %s::$%s of type %s", type, ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), - prop_type1, prop_type2 + ZSTR_VAL(type_str) ); + zend_string_release(type_str); } static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(zend_property_info *prop, const char *type) { - const char *prop_type1, *prop_type2; - zend_format_type(prop->type, &prop_type1, &prop_type2); + zend_string *type_str = zend_type_to_string(prop->type); zend_type_error( - "Cannot auto-initialize an %s inside a reference held by property %s::$%s of type %s%s", + "Cannot auto-initialize an %s inside a reference held by property %s::$%s of type %s", type, ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), - prop_type1, prop_type2 + ZSTR_VAL(type_str) ); + zend_string_release(type_str); } static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_error( @@ -724,22 +711,25 @@ static ZEND_COLD void zend_verify_type_error_common( *need_kind = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type)); } } else { - switch (ZEND_TYPE_CODE(arg_info->type)) { - case IS_OBJECT: + zend_type type = ZEND_TYPE_WITHOUT_NULL(arg_info->type); + switch (ZEND_TYPE_MASK(type)) { + case MAY_BE_OBJECT: *need_msg = "be an "; *need_kind = "object"; break; - case IS_CALLABLE: + case MAY_BE_CALLABLE: *need_msg = "be callable"; *need_kind = ""; break; - case IS_ITERABLE: + case MAY_BE_ITERABLE: *need_msg = "be iterable"; *need_kind = ""; break; default: + /* TODO: The zend_type_to_string() result is guaranteed interned here. + * It would be beter to switch all this code to use zend_string though. */ *need_msg = "be of the type "; - *need_kind = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type)); + *need_kind = ZSTR_VAL(zend_type_to_string(type)); break; } } @@ -764,7 +754,7 @@ static ZEND_COLD void zend_verify_type_error_common( } } -static ZEND_COLD void zend_verify_arg_error( +ZEND_API ZEND_COLD void zend_verify_arg_error( const zend_function *zf, const zend_arg_info *arg_info, int arg_num, const zend_class_entry *ce, zval *value) { @@ -793,105 +783,100 @@ static ZEND_COLD void zend_verify_arg_error( } } -static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg) +static zend_bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) { - switch (type_hint) { - case _IS_BOOL: { - zend_bool dest; + if (type_mask & (MAY_BE_TRUE|MAY_BE_FALSE)) { + zend_bool dest; - if (!zend_parse_arg_bool_weak(arg, &dest)) { - return 0; - } - zval_ptr_dtor(arg); - ZVAL_BOOL(arg, dest); - return 1; + if (!zend_parse_arg_bool_weak(arg, &dest)) { + return 0; } - case IS_LONG: { - zend_long dest; + zval_ptr_dtor(arg); + ZVAL_BOOL(arg, dest); + return 1; + } + if (type_mask & MAY_BE_LONG) { + zend_long dest; - if (!zend_parse_arg_long_weak(arg, &dest)) { - return 0; - } - zval_ptr_dtor(arg); - ZVAL_LONG(arg, dest); - return 1; + if (!zend_parse_arg_long_weak(arg, &dest)) { + return 0; } - case IS_DOUBLE: { - double dest; + zval_ptr_dtor(arg); + ZVAL_LONG(arg, dest); + return 1; + } + if (type_mask & MAY_BE_DOUBLE) { + double dest; - if (!zend_parse_arg_double_weak(arg, &dest)) { - return 0; - } - zval_ptr_dtor(arg); - ZVAL_DOUBLE(arg, dest); - return 1; + if (!zend_parse_arg_double_weak(arg, &dest)) { + return 0; } - case IS_STRING: { - zend_string *dest; + zval_ptr_dtor(arg); + ZVAL_DOUBLE(arg, dest); + return 1; + } + if (type_mask & MAY_BE_STRING) { + zend_string *dest; - /* on success "arg" is converted to IS_STRING */ - return zend_parse_arg_str_weak(arg, &dest); - } - default: - return 0; + /* on success "arg" is converted to IS_STRING */ + return zend_parse_arg_str_weak(arg, &dest); } + return 0; } #if ZEND_DEBUG /* Used to sanity-check internal arginfo types without performing any actual type conversions. */ -static zend_bool zend_verify_weak_scalar_type_hint_no_sideeffect(zend_uchar type_hint, zval *arg) -{ - switch (type_hint) { - case _IS_BOOL: { - zend_bool dest; - return zend_parse_arg_bool_weak(arg, &dest); - } - case IS_LONG: { - zend_long dest; - if (Z_TYPE_P(arg) == IS_STRING) { - /* Handle this case separately to avoid the "non well-formed" warning */ - double dval; - zend_uchar type = is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, &dval, 1); - if (type == IS_LONG) { - return 1; - } - if (type == IS_DOUBLE) { - return !zend_isnan(dval) && ZEND_DOUBLE_FITS_LONG(dval); +static zend_bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_mask, zval *arg) +{ + if (type_mask & (MAY_BE_TRUE|MAY_BE_FALSE)) { + zend_bool dest; + return zend_parse_arg_bool_weak(arg, &dest); + } + if (type_mask & MAY_BE_LONG) { + zend_long dest; + if (Z_TYPE_P(arg) == IS_STRING) { + /* Handle this case separately to avoid the "non well-formed" warning */ + double dval; + zend_uchar type = is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, &dval, 1); + if (type == IS_LONG) { + return 1; + } + if (type == IS_DOUBLE) { + return !zend_isnan(dval) && ZEND_DOUBLE_FITS_LONG(dval); - } - return 0; } - return zend_parse_arg_long_weak(arg, &dest); + return 0; } - case IS_DOUBLE: { - double dest; - if (Z_TYPE_P(arg) == IS_STRING) { - /* Handle this case separately to avoid the "non well-formed" warning */ - return is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, NULL, 1) != 0; - } - return zend_parse_arg_double_weak(arg, &dest); + return zend_parse_arg_long_weak(arg, &dest); + } + if (type_mask & MAY_BE_DOUBLE) { + double dest; + if (Z_TYPE_P(arg) == IS_STRING) { + /* Handle this case separately to avoid the "non well-formed" warning */ + return is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, NULL, 1) != 0; } - case IS_STRING: - /* We don't call cast_object here, because this check must be side-effect free. As this - * is only used for a sanity check of arginfo/zpp consistency, it's okay if we accept - * more than actually allowed here. */ - return Z_TYPE_P(arg) < IS_STRING || Z_TYPE_P(arg) == IS_OBJECT; - default: - return 0; + return zend_parse_arg_double_weak(arg, &dest); + } + if (type_mask & MAY_BE_STRING) { + /* We don't call cast_object here, because this check must be side-effect free. As this + * is only used for a sanity check of arginfo/zpp consistency, it's okay if we accept + * more than actually allowed here. */ + return Z_TYPE_P(arg) < IS_STRING || Z_TYPE_P(arg) == IS_OBJECT; } + return 0; } #endif -static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, zend_bool strict, zend_bool is_internal_arg) +ZEND_API zend_bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, zend_bool strict, zend_bool is_internal_arg) { if (UNEXPECTED(strict)) { /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */ - if (type_hint != IS_DOUBLE || Z_TYPE_P(arg) != IS_LONG) { + if (!(type_mask & MAY_BE_DOUBLE) || Z_TYPE_P(arg) != IS_LONG) { return 0; } } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) { /* NULL may be accepted only by nullable hints (this is already checked) */ - if (is_internal_arg && (type_hint <= IS_STRING || type_hint == _IS_BOOL)) { + if (is_internal_arg && (type_mask & (MAY_BE_TRUE|MAY_BE_FALSE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING))) { /* As an exception, null is allowed for scalar types in weak mode. */ return 1; } @@ -899,15 +884,15 @@ static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, z } #if ZEND_DEBUG if (is_internal_arg) { - return zend_verify_weak_scalar_type_hint_no_sideeffect(type_hint, arg); + return zend_verify_weak_scalar_type_hint_no_sideeffect(type_mask, arg); } #endif - return zend_verify_weak_scalar_type_hint(type_hint, arg); + return zend_verify_weak_scalar_type_hint(type_mask, arg); } ZEND_COLD zend_never_inline void zend_verify_property_type_error(zend_property_info *info, zval *property) { - const char *prop_type1, *prop_type2; + zend_string *type_str; /* we _may_ land here in case reading already errored and runtime cache thus has not been updated (i.e. it contains a valid but unrelated info) */ if (EG(exception)) { @@ -915,23 +900,23 @@ ZEND_COLD zend_never_inline void zend_verify_property_type_error(zend_property_i } // TODO Switch to a more standard error message? - zend_format_type(info->type, &prop_type1, &prop_type2); - (void) prop_type1; + type_str = zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(info->type)); if (ZEND_TYPE_IS_CLASS(info->type)) { zend_type_error("Typed property %s::$%s must be an instance of %s%s, %s used", ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name), - prop_type2, + ZSTR_VAL(type_str), ZEND_TYPE_ALLOW_NULL(info->type) ? " or null" : "", Z_TYPE_P(property) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(property)->name) : zend_get_type_by_const(Z_TYPE_P(property))); } else { zend_type_error("Typed property %s::$%s must be %s%s, %s used", ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name), - prop_type2, + ZSTR_VAL(type_str), ZEND_TYPE_ALLOW_NULL(info->type) ? " or null" : "", Z_TYPE_P(property) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(property)->name) : zend_get_type_by_const(Z_TYPE_P(property))); } + zend_string_release(type_str); } static zend_bool zend_resolve_class_type(zend_type *type, zend_class_entry *self_ce) { @@ -979,17 +964,13 @@ static zend_always_inline zend_bool i_zend_check_property_type(zend_property_inf return instanceof_function(Z_OBJCE_P(property), ZEND_TYPE_CE(info->type)); } - ZEND_ASSERT(ZEND_TYPE_CODE(info->type) != IS_CALLABLE); - if (EXPECTED(ZEND_TYPE_CODE(info->type) == Z_TYPE_P(property))) { - return 1; - } else if (EXPECTED(Z_TYPE_P(property) == IS_NULL)) { - return ZEND_TYPE_ALLOW_NULL(info->type); - } else if (ZEND_TYPE_CODE(info->type) == _IS_BOOL && EXPECTED(Z_TYPE_P(property) == IS_FALSE || Z_TYPE_P(property) == IS_TRUE)) { + ZEND_ASSERT(!(ZEND_TYPE_MASK(info->type) & MAY_BE_CALLABLE)); + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(info->type, Z_TYPE_P(property)))) { return 1; - } else if (ZEND_TYPE_CODE(info->type) == IS_ITERABLE) { + } else if (ZEND_TYPE_MASK(info->type) & MAY_BE_ITERABLE) { return zend_is_iterable(property); } else { - return zend_verify_scalar_type_hint(ZEND_TYPE_CODE(info->type), property, strict, 0); + return zend_verify_scalar_type_hint(ZEND_TYPE_MASK(info->type), property, strict, 0); } } @@ -1030,6 +1011,7 @@ static zend_always_inline zend_bool zend_check_type( zend_bool is_return_type, zend_bool is_internal) { zend_reference *ref = NULL; + uint32_t type_mask; if (!ZEND_TYPE_IS_SET(type)) { return 1; @@ -1054,22 +1036,15 @@ static zend_always_inline zend_bool zend_check_type( return instanceof_function(Z_OBJCE_P(arg), *ce); } return Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(type); - } else if (EXPECTED(ZEND_TYPE_CODE(type) == Z_TYPE_P(arg))) { - return 1; - } - - if (Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(type)) { - /* Null passed to nullable type */ + } else if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(arg)))) { return 1; } - if (ZEND_TYPE_CODE(type) == IS_CALLABLE) { + type_mask = ZEND_TYPE_MASK(type); + if (type_mask & MAY_BE_CALLABLE) { return zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL); - } else if (ZEND_TYPE_CODE(type) == IS_ITERABLE) { + } else if (type_mask & MAY_BE_ITERABLE) { return zend_is_iterable(arg); - } else if (ZEND_TYPE_CODE(type) == _IS_BOOL && - EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { - return 1; } else if (ref && ZEND_REF_HAS_TYPE_SOURCES(ref)) { return 0; /* we cannot have conversions for typed refs */ } else if (is_internal && is_return_type) { @@ -1078,7 +1053,7 @@ static zend_always_inline zend_bool zend_check_type( * apply coercions. */ return 0; } else { - return zend_verify_scalar_type_hint(ZEND_TYPE_CODE(type), arg, + return zend_verify_scalar_type_hint(type_mask, arg, is_return_type ? ZEND_RET_USES_STRICT_TYPES() : ZEND_ARG_USES_STRICT_TYPES(), is_internal); } @@ -1229,7 +1204,7 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret) zend_class_entry *ce = NULL; void *dummy_cache_slot = NULL; - if (ZEND_TYPE_CODE(ret_info->type) == IS_VOID) { + if (ZEND_TYPE_IS_CODE(ret_info->type) && (ZEND_TYPE_MASK(ret_info->type) & MAY_BE_VOID)) { if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) { zend_verify_void_return_error(zf, zend_zval_type_name(ret), ""); return 0; @@ -1260,7 +1235,9 @@ static ZEND_COLD int zend_verify_missing_return_type(const zend_function *zf, vo { zend_arg_info *ret_info = zf->common.arg_info - 1; - if (ZEND_TYPE_IS_SET(ret_info->type) && UNEXPECTED(ZEND_TYPE_CODE(ret_info->type) != IS_VOID)) { + if (ZEND_TYPE_IS_SET(ret_info->type) + && (!ZEND_TYPE_IS_CODE(ret_info->type) + || !(ZEND_TYPE_MASK(ret_info->type) & MAY_BE_VOID))) { zend_class_entry *ce = NULL; if (ZEND_TYPE_IS_CLASS(ret_info->type)) { if (EXPECTED(*cache_slot)) { @@ -1610,7 +1587,7 @@ static zend_property_info *zend_get_prop_not_accepting_double(zend_reference *re { zend_property_info *prop; ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) { - if (ZEND_TYPE_CODE(prop->type) != IS_DOUBLE) { + if (!ZEND_TYPE_IS_CODE(prop->type) || !(ZEND_TYPE_MASK(prop->type) & MAY_BE_DOUBLE)) { return prop; } } ZEND_REF_FOREACH_TYPE_SOURCES_END(); @@ -1641,19 +1618,20 @@ static ZEND_COLD zend_long zend_throw_incdec_ref_error(zend_reference *ref OPLIN } static ZEND_COLD zend_long zend_throw_incdec_prop_error(zend_property_info *prop OPLINE_DC) { - const char *prop_type1, *prop_type2; - zend_format_type(prop->type, &prop_type1, &prop_type2); + zend_string *type_str = zend_type_to_string(prop->type); if (ZEND_IS_INCREMENT(opline->opcode)) { - zend_type_error("Cannot increment property %s::$%s of type %s%s past its maximal value", + zend_type_error("Cannot increment property %s::$%s of type %s past its maximal value", ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), - prop_type1, prop_type2); + ZSTR_VAL(type_str)); + zend_string_release(type_str); return ZEND_LONG_MAX; } else { - zend_type_error("Cannot decrement property %s::$%s of type %s%s past its minimal value", + zend_type_error("Cannot decrement property %s::$%s of type %s past its minimal value", ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), - prop_type1, prop_type2); + ZSTR_VAL(type_str)); + zend_string_release(type_str); return ZEND_LONG_MIN; } } @@ -2578,8 +2556,7 @@ static zend_always_inline zend_bool check_type_array_assignable(zend_type type) if (!type) { return 1; } - return ZEND_TYPE_IS_CODE(type) - && (ZEND_TYPE_CODE(type) == IS_ARRAY || ZEND_TYPE_CODE(type) == IS_ITERABLE); + return ZEND_TYPE_IS_CODE(type) && (ZEND_TYPE_MASK(type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)); } static zend_always_inline zend_bool check_type_stdClass_assignable(zend_type type) { @@ -2593,7 +2570,7 @@ static zend_always_inline zend_bool check_type_stdClass_assignable(zend_type typ return zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "stdclass"); } } else { - return ZEND_TYPE_CODE(type) == IS_OBJECT; + return (ZEND_TYPE_MASK(type) & MAY_BE_OBJECT) != 0; } } @@ -2987,58 +2964,59 @@ static zend_always_inline int zend_fetch_static_property_address(zval **retval, } ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(zend_property_info *prop1, zend_property_info *prop2, zval *zv) { - const char *prop1_type1, *prop1_type2, *prop2_type1, *prop2_type2; - zend_format_type(prop1->type, &prop1_type1, &prop1_type2); - zend_format_type(prop2->type, &prop2_type1, &prop2_type2); - zend_type_error("Reference with value of type %s held by property %s::$%s of type %s%s is not compatible with property %s::$%s of type %s%s", + zend_string *type1_str = zend_type_to_string(prop1->type); + zend_string *type2_str = zend_type_to_string(prop2->type); + zend_type_error("Reference with value of type %s held by property %s::$%s of type %s is not compatible with property %s::$%s of type %s", Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)), ZSTR_VAL(prop1->ce->name), zend_get_unmangled_property_name(prop1->name), - prop1_type1, prop1_type2, + ZSTR_VAL(type1_str), ZSTR_VAL(prop2->ce->name), zend_get_unmangled_property_name(prop2->name), - prop2_type1, prop2_type2 + ZSTR_VAL(type2_str) ); + zend_string_release(type1_str); + zend_string_release(type2_str); } ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(zend_property_info *prop, zval *zv) { - const char *prop_type1, *prop_type2; - zend_format_type(prop->type, &prop_type1, &prop_type2); - zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s%s", + zend_string *type_str = zend_type_to_string(prop->type); + zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s", Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)), ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), - prop_type1, prop_type2 + ZSTR_VAL(type_str) ); + zend_string_release(type_str); } ZEND_API ZEND_COLD void zend_throw_conflicting_coercion_error(zend_property_info *prop1, zend_property_info *prop2, zval *zv) { - const char *prop1_type1, *prop1_type2, *prop2_type1, *prop2_type2; - zend_format_type(prop1->type, &prop1_type1, &prop1_type2); - zend_format_type(prop2->type, &prop2_type1, &prop2_type2); - zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s%s and property %s::$%s of type %s%s, as this would result in an inconsistent type conversion", + zend_string *type1_str = zend_type_to_string(prop1->type); + zend_string *type2_str = zend_type_to_string(prop2->type); + zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s and property %s::$%s of type %s, as this would result in an inconsistent type conversion", Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)), ZSTR_VAL(prop1->ce->name), zend_get_unmangled_property_name(prop1->name), - prop1_type1, prop1_type2, + ZSTR_VAL(type1_str), ZSTR_VAL(prop2->ce->name), zend_get_unmangled_property_name(prop2->name), - prop2_type1, prop2_type2 + ZSTR_VAL(type2_str) ); + zend_string_release(type1_str); + zend_string_release(type2_str); } /* 1: valid, 0: invalid, -1: may be valid after type coercion */ static zend_always_inline int i_zend_verify_type_assignable_zval( zend_type *type_ptr, zend_class_entry *self_ce, zval *zv, zend_bool strict) { zend_type type = *type_ptr; - zend_uchar type_code; + uint32_t type_mask; zend_uchar zv_type = Z_TYPE_P(zv); - if (ZEND_TYPE_ALLOW_NULL(type) && zv_type == IS_NULL) { - return 1; - } - if (ZEND_TYPE_IS_CLASS(type)) { + if (ZEND_TYPE_ALLOW_NULL(type) && zv_type == IS_NULL) { + return 1; + } if (!ZEND_TYPE_IS_CE(type)) { if (!zend_resolve_class_type(type_ptr, self_ce)) { return 0; @@ -3048,26 +3026,25 @@ static zend_always_inline int i_zend_verify_type_assignable_zval( return zv_type == IS_OBJECT && instanceof_function(Z_OBJCE_P(zv), ZEND_TYPE_CE(type)); } - type_code = ZEND_TYPE_CODE(type); - if (type_code == zv_type || - (type_code == _IS_BOOL && (zv_type == IS_FALSE || zv_type == IS_TRUE))) { + if (ZEND_TYPE_CONTAINS_CODE(type, zv_type)) { return 1; } - if (type_code == IS_ITERABLE) { + type_mask = ZEND_TYPE_MASK(type); + if (type_mask & MAY_BE_ITERABLE) { return zend_is_iterable(zv); } /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */ if (strict) { - if (type_code == IS_DOUBLE && zv_type == IS_LONG) { + if ((type_mask & MAY_BE_DOUBLE) && zv_type == IS_LONG) { return -1; } return 0; } /* No weak conversions for arrays and objects */ - if (type_code == IS_ARRAY || type_code == IS_OBJECT) { + if (type_mask & (MAY_BE_ARRAY|MAY_BE_OBJECT)) { return 0; } @@ -3089,7 +3066,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference * must be the same (modulo nullability). To handle this, remember the first type we see and * compare against it when coercion becomes necessary. */ zend_property_info *seen_prop = NULL; - zend_uchar seen_type; + uint32_t seen_type_mask; zend_bool needs_coercion = 0; ZEND_ASSERT(Z_TYPE_P(zv) != IS_REFERENCE); @@ -3106,14 +3083,16 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference if (!seen_prop) { seen_prop = prop; - seen_type = ZEND_TYPE_IS_CLASS(prop->type) ? IS_OBJECT : ZEND_TYPE_CODE(prop->type); - } else if (needs_coercion && seen_type != ZEND_TYPE_CODE(prop->type)) { + seen_type_mask = ZEND_TYPE_IS_CLASS(prop->type) + ? MAY_BE_OBJECT : ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop->type)); + } else if (needs_coercion + && seen_type_mask != ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop->type))) { zend_throw_conflicting_coercion_error(seen_prop, prop, zv); return 0; } } ZEND_REF_FOREACH_TYPE_SOURCES_END(); - if (UNEXPECTED(needs_coercion && !zend_verify_weak_scalar_type_hint(seen_type, zv))) { + if (UNEXPECTED(needs_coercion && !zend_verify_weak_scalar_type_hint(seen_type_mask, zv))) { zend_throw_ref_type_error_zval(seen_prop, zv); return 0; } @@ -3174,12 +3153,13 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_propert if (result < 0) { zend_property_info *ref_prop = ZEND_REF_FIRST_SOURCE(Z_REF_P(orig_val)); - if (ZEND_TYPE_CODE(prop_info->type) != ZEND_TYPE_CODE(ref_prop->type)) { + if (ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop_info->type)) + != ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(ref_prop->type))) { /* Invalid due to conflicting coercion */ zend_throw_ref_type_error_type(ref_prop, prop_info, val); return 0; } - if (zend_verify_weak_scalar_type_hint(ZEND_TYPE_CODE(prop_info->type), val)) { + if (zend_verify_weak_scalar_type_hint(ZEND_TYPE_MASK(prop_info->type), val)) { return 1; } } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 3840e39bccf5e..0dcf89f1c1f02 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -60,6 +60,11 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_propert ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(zend_property_info *prop, zval *zv); ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(zend_property_info *prop1, zend_property_info *prop2, zval *zv); +ZEND_API zend_bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, zend_bool strict, zend_bool is_internal_arg); +ZEND_API ZEND_COLD void zend_verify_arg_error( + const zend_function *zf, const zend_arg_info *arg_info, + int arg_num, const zend_class_entry *ce, zval *value); + #define ZEND_REF_TYPE_SOURCES(ref) \ (ref)->sources diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 3c77026e4c80b..b0e35a9552fb9 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -348,7 +348,7 @@ static inheritance_status zend_perform_covariant_type_check( } return unlinked_instanceof(fe_ce, proto_ce) ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; - } else if (ZEND_TYPE_CODE(proto_type) == IS_ITERABLE) { + } else if (ZEND_TYPE_MASK(proto_type) & MAY_BE_ITERABLE) { if (ZEND_TYPE_IS_CLASS(fe_type)) { zend_string *fe_class_name = resolve_class_name(fe, ZEND_TYPE_NAME(fe_type)); zend_class_entry *fe_ce = lookup_class(fe, fe_class_name); @@ -360,9 +360,9 @@ static inheritance_status zend_perform_covariant_type_check( ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; } - return ZEND_TYPE_CODE(fe_type) == IS_ITERABLE || ZEND_TYPE_CODE(fe_type) == IS_ARRAY + return ZEND_TYPE_MASK(fe_type) & (MAY_BE_ARRAY|MAY_BE_ITERABLE) ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; - } else if (ZEND_TYPE_CODE(proto_type) == IS_OBJECT) { + } else if (ZEND_TYPE_MASK(proto_type) & MAY_BE_OBJECT) { if (ZEND_TYPE_IS_CLASS(fe_type)) { /* Currently, any class name would be allowed here. We still perform a class lookup * for forward-compatibility reasons, as we may have named types in the future that @@ -376,9 +376,10 @@ static inheritance_status zend_perform_covariant_type_check( return INHERITANCE_SUCCESS; } - return ZEND_TYPE_CODE(fe_type) == IS_OBJECT ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; + return ZEND_TYPE_MASK(fe_type) & MAY_BE_OBJECT ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; } else { - return ZEND_TYPE_CODE(fe_type) == ZEND_TYPE_CODE(proto_type) + return ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(fe_type)) + == ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(proto_type)) ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; } } @@ -516,33 +517,10 @@ static inheritance_status zend_do_perform_implementation_check( static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function *fptr, zend_arg_info *arg_info, int return_hint) /* {{{ */ { - - if (ZEND_TYPE_IS_SET(arg_info->type) && ZEND_TYPE_ALLOW_NULL(arg_info->type)) { - smart_str_appendc(str, '?'); - } - - if (ZEND_TYPE_IS_CLASS(arg_info->type)) { - const char *class_name; - size_t class_name_len; - - class_name = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type)); - class_name_len = ZSTR_LEN(ZEND_TYPE_NAME(arg_info->type)); - - if (!strcasecmp(class_name, "self") && fptr->common.scope) { - class_name = ZSTR_VAL(fptr->common.scope->name); - class_name_len = ZSTR_LEN(fptr->common.scope->name); - } else if (!strcasecmp(class_name, "parent") && fptr->common.scope && fptr->common.scope->parent) { - class_name = ZSTR_VAL(fptr->common.scope->parent->name); - class_name_len = ZSTR_LEN(fptr->common.scope->parent->name); - } - - smart_str_appendl(str, class_name, class_name_len); - if (!return_hint) { - smart_str_appendc(str, ' '); - } - } else if (ZEND_TYPE_IS_CODE(arg_info->type)) { - const char *type_name = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type)); - smart_str_appends(str, type_name); + if (ZEND_TYPE_IS_SET(arg_info->type)) { + zend_string *type_str = zend_type_to_string_resolved(arg_info->type, fptr->common.scope); + smart_str_append(str, type_str); + zend_string_release(type_str); if (!return_hint) { smart_str_appendc(str, ' '); } @@ -942,14 +920,13 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke if (UNEXPECTED(ZEND_TYPE_IS_SET(parent_info->type))) { if (!property_types_compatible(parent_info, child_info)) { + zend_string *type_str = + zend_type_to_string_resolved(parent_info->type, parent_info->ce); zend_error_noreturn(E_COMPILE_ERROR, - "Type of %s::$%s must be %s%s (as in class %s)", + "Type of %s::$%s must be %s (as in class %s)", ZSTR_VAL(ce->name), ZSTR_VAL(key), - ZEND_TYPE_ALLOW_NULL(parent_info->type) ? "?" : "", - ZEND_TYPE_IS_CLASS(parent_info->type) - ? ZSTR_VAL(ZEND_TYPE_IS_CE(parent_info->type) ? ZEND_TYPE_CE(parent_info->type)->name : zend_resolve_property_type(ZEND_TYPE_NAME(parent_info->type), parent_info->ce)) - : zend_get_type_by_const(ZEND_TYPE_CODE(parent_info->type)), + ZSTR_VAL(type_str), ZSTR_VAL(ce->parent->name)); } } else if (UNEXPECTED(ZEND_TYPE_IS_SET(child_info->type) && !ZEND_TYPE_IS_SET(parent_info->type))) { diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 674bea2f6005a..a38c1cae8c552 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -506,6 +506,12 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_ARGV, "argv") \ _(ZEND_STR_ARGC, "argc") \ _(ZEND_STR_ARRAY_CAPITALIZED, "Array") \ + _(ZEND_STR_BOOL, "bool") \ + _(ZEND_STR_INT, "int") \ + _(ZEND_STR_FLOAT, "float") \ + _(ZEND_STR_CALLABLE, "callable") \ + _(ZEND_STR_ITERABLE, "iterable") \ + _(ZEND_STR_VOID, "void") \ typedef enum _zend_known_string_id { diff --git a/Zend/zend_type_info.h b/Zend/zend_type_info.h index 72550b6fc3a93..c991fd5db5994 100644 --- a/Zend/zend_type_info.h +++ b/Zend/zend_type_info.h @@ -34,7 +34,12 @@ #define MAY_BE_ANY (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE) #define MAY_BE_REF (1 << IS_REFERENCE) /* may be reference */ -#define MAY_BE_ARRAY_SHIFT (IS_REFERENCE) +/* These are used in zend_type, but not for type inference. */ +#define MAY_BE_CALLABLE (1 << IS_CALLABLE) +#define MAY_BE_ITERABLE (1 << IS_ITERABLE) +#define MAY_BE_VOID (1 << IS_VOID) + +#define MAY_BE_ARRAY_SHIFT (IS_VOID) #define MAY_BE_ARRAY_OF_NULL (MAY_BE_NULL << MAY_BE_ARRAY_SHIFT) #define MAY_BE_ARRAY_OF_FALSE (MAY_BE_FALSE << MAY_BE_ARRAY_SHIFT) @@ -48,11 +53,11 @@ #define MAY_BE_ARRAY_OF_ANY (MAY_BE_ANY << MAY_BE_ARRAY_SHIFT) #define MAY_BE_ARRAY_OF_REF (MAY_BE_REF << MAY_BE_ARRAY_SHIFT) -#define MAY_BE_ARRAY_KEY_LONG (1<<21) -#define MAY_BE_ARRAY_KEY_STRING (1<<22) +#define MAY_BE_ARRAY_KEY_LONG (1<<25) +#define MAY_BE_ARRAY_KEY_STRING (1<<26) #define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING) -#define MAY_BE_ERROR (1<<23) -#define MAY_BE_CLASS (1<<24) +#define MAY_BE_ERROR (1<<27) +#define MAY_BE_CLASS (1<<28) #endif /* ZEND_TYPE_INFO_H */ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 653a07b6b2479..56a67f0d3642f 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -113,7 +113,7 @@ typedef void (*copy_ctor_func_t)(zval *pElement); * * ZEND_TYPE_NAME() - returns referenced class name * ZEND_TYPE_CE() - returns referenced class entry - * ZEND_TYPE_CODE() - returns standard type code (e.g. IS_LONG, _IS_BOOL) + * ZEND_TYPE_MASK() - returns MAY_BE_* type mask * * ZEND_TYPE_ALLOW_NULL() - checks if NULL is allowed * @@ -123,44 +123,57 @@ typedef void (*copy_ctor_func_t)(zval *pElement); typedef uintptr_t zend_type; +#define _ZEND_TYPE_CODE_MAX ((Z_L(1)<<(IS_VOID+1))-1) +#define _ZEND_TYPE_FLAG_MASK Z_L(0x3) +#define _ZEND_TYPE_CE_BIT Z_L(0x1) +/* Must have same value as MAY_BE_NULL */ +#define _ZEND_TYPE_NULLABLE_BIT Z_L(0x2) + #define ZEND_TYPE_IS_SET(t) \ - ((t) > Z_L(0x3)) + ((t) > _ZEND_TYPE_FLAG_MASK) #define ZEND_TYPE_IS_CODE(t) \ - (((t) > Z_L(0x3)) && ((t) <= Z_L(0x3ff))) + (((t) > _ZEND_TYPE_FLAG_MASK) && ((t) <= _ZEND_TYPE_CODE_MAX)) #define ZEND_TYPE_IS_CLASS(t) \ - ((t) > Z_L(0x3ff)) + ((t) > _ZEND_TYPE_CODE_MAX) #define ZEND_TYPE_IS_CE(t) \ - (((t) & Z_L(0x2)) != 0) + (((t) & _ZEND_TYPE_CE_BIT) != 0) #define ZEND_TYPE_IS_NAME(t) \ (ZEND_TYPE_IS_CLASS(t) && !ZEND_TYPE_IS_CE(t)) #define ZEND_TYPE_NAME(t) \ - ((zend_string*)((t) & ~Z_L(0x3))) + ((zend_string*)((t) & ~_ZEND_TYPE_FLAG_MASK)) #define ZEND_TYPE_CE(t) \ - ((zend_class_entry*)((t) & ~Z_L(0x3))) + ((zend_class_entry*)((t) & ~_ZEND_TYPE_FLAG_MASK)) + +#define ZEND_TYPE_MASK(t) \ + (t) -#define ZEND_TYPE_CODE(t) \ - ((t) >> Z_L(2)) +#define ZEND_TYPE_CONTAINS_CODE(t, code) \ + (((t) & (1 << (code))) != 0) #define ZEND_TYPE_ALLOW_NULL(t) \ - (((t) & Z_L(0x1)) != 0) + (((t) & _ZEND_TYPE_NULLABLE_BIT) != 0) #define ZEND_TYPE_WITHOUT_NULL(t) \ - ((t) & ~Z_L(0x1)) + ((t) & ~_ZEND_TYPE_NULLABLE_BIT) -#define ZEND_TYPE_ENCODE(code, allow_null) \ - (((code) << Z_L(2)) | ((allow_null) ? Z_L(0x1) : Z_L(0x0))) +#define ZEND_TYPE_ENCODE(maybe_code) \ + (maybe_code) + +#define ZEND_TYPE_ENCODE_CODE(code, allow_null) \ + (((code) == _IS_BOOL ? (MAY_BE_FALSE|MAY_BE_TRUE) : (1 << (code))) \ + | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : Z_L(0x0))) #define ZEND_TYPE_ENCODE_CE(ce, allow_null) \ - (((uintptr_t)(ce)) | ((allow_null) ? Z_L(0x3) : Z_L(0x2))) + (((uintptr_t)(ce)) | _ZEND_TYPE_CE_BIT | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : Z_L(0x0))) #define ZEND_TYPE_ENCODE_CLASS(class_name, allow_null) \ - (((uintptr_t)(class_name)) | ((allow_null) ? Z_L(0x1) : Z_L(0x0))) + (((uintptr_t)(class_name)) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : Z_L(0x0))) #define ZEND_TYPE_ENCODE_CLASS_CONST_0(class_name) \ ((zend_type) class_name) @@ -410,7 +423,7 @@ struct _zend_ast_ref { /*zend_ast ast; zend_ast follows the zend_ast_ref structure */ }; -/* regular data types */ +/* Regular data types: Must be in sync with zend_variables.c. */ #define IS_UNDEF 0 #define IS_NULL 1 #define IS_FALSE 2 @@ -422,21 +435,21 @@ struct _zend_ast_ref { #define IS_OBJECT 8 #define IS_RESOURCE 9 #define IS_REFERENCE 10 +#define IS_CONSTANT_AST 11 /* Constant expressions */ -/* constant expressions */ -#define IS_CONSTANT_AST 11 +/* Fake types used only for type hinting. IS_VOID should be the last. */ +#define IS_CALLABLE 12 +#define IS_ITERABLE 13 +#define IS_VOID 14 /* internal types */ -#define IS_INDIRECT 13 -#define IS_PTR 14 -#define _IS_ERROR 15 - -/* fake types used only for type hinting (Z_TYPE(zv) can not use them) */ -#define _IS_BOOL 16 -#define IS_CALLABLE 17 -#define IS_ITERABLE 18 -#define IS_VOID 19 -#define _IS_NUMBER 20 +#define IS_INDIRECT 15 +#define IS_PTR 16 +#define _IS_ERROR 17 + +/* used for casts */ +#define _IS_BOOL 18 +#define _IS_NUMBER 19 static zend_always_inline zend_uchar zval_get_type(const zval* pz) { return pz->u1.v.type; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 8884810f65dc7..4e95859a7fcb7 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4276,9 +4276,8 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV } if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type) - && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE - && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE - && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr)) + && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE)) + && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)) && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) && retval_ref != retval_ptr) ) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 70d89048e7eee..a48b964e45ae7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -9077,9 +9077,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP } if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type) - && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE - && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE - && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr)) + && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE)) + && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)) && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) && retval_ref != retval_ptr) ) { @@ -19480,9 +19479,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN } if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type) - && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE - && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE - && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr)) + && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE)) + && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)) && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) && retval_ref != retval_ptr) ) { @@ -27502,9 +27500,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN } if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type) - && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE - && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE - && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr)) + && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE)) + && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)) && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) && retval_ref != retval_ptr) ) { @@ -34608,9 +34605,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED } if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type) - && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE - && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE - && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr)) + && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE)) + && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)) && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) && retval_ref != retval_ptr) ) { @@ -46713,9 +46709,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU } if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type) - && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE - && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE - && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr)) + && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE)) + && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)) && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) && retval_ref != retval_ptr) ) { diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index 5401c9df6a78c..887071a7c6612 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -312,6 +312,8 @@ static inline zend_bool can_elide_return_type_check( zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use]; zend_ssa_var_info *def_info = &ssa->var_info[ssa_op->op1_def]; + /* TODO: It would be better to rewrite this without using def_info, + * which may not be an exact representation of the type. */ if (use_info->type & MAY_BE_REF) { return 0; } @@ -322,7 +324,8 @@ static inline zend_bool can_elide_return_type_check( } /* These types are not represented exactly */ - if (ZEND_TYPE_CODE(info->type) == IS_CALLABLE || ZEND_TYPE_CODE(info->type) == IS_ITERABLE) { + if (ZEND_TYPE_IS_CODE(info->type) + && (ZEND_TYPE_MASK(info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))) { return 0; } diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index e8a9918b398e0..63fe05d48781d 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -1419,18 +1419,22 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int return 1; } else if (op_array->arg_info && opline->op1.num <= op_array->num_args) { - if (ZEND_TYPE_CODE(op_array->arg_info[opline->op1.num-1].type) == IS_LONG) { - tmp->underflow = 0; - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - tmp->overflow = 0; - return 1; - } else if (ZEND_TYPE_CODE(op_array->arg_info[opline->op1.num-1].type) == _IS_BOOL) { - tmp->underflow = 0; - tmp->min = 0; - tmp->max = 1; - tmp->overflow = 0; - return 1; + zend_type type = op_array->arg_info[opline->op1.num-1].type; + if (ZEND_TYPE_IS_CODE(type)) { + uint32_t mask = ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(type)); + if (mask == MAY_BE_LONG) { + tmp->underflow = 0; + tmp->min = ZEND_LONG_MIN; + tmp->max = ZEND_LONG_MAX; + tmp->overflow = 0; + return 1; + } else if (mask == (MAY_BE_FALSE|MAY_BE_TRUE)) { + tmp->underflow = 0; + tmp->min = 0; + tmp->max = 1; + tmp->overflow = 0; + return 1; + } } } } @@ -2232,22 +2236,23 @@ static inline zend_class_entry *get_class_entry(const zend_script *script, zend_ return NULL; } -static uint32_t zend_convert_type_code_to_may_be(zend_uchar type_code) { - switch (type_code) { - case IS_VOID: - return MAY_BE_NULL; - case IS_CALLABLE: - return MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - case IS_ITERABLE: - return MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - case IS_ARRAY: - return MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - case _IS_BOOL: - return MAY_BE_TRUE|MAY_BE_FALSE; - default: - ZEND_ASSERT(type_code < IS_REFERENCE); - return 1 << type_code; +static uint32_t zend_convert_type_declaration_mask(uint32_t type_mask) { + if (type_mask & MAY_BE_VOID) { + type_mask &= ~MAY_BE_VOID; + type_mask |= MAY_BE_NULL; + } + if (type_mask & MAY_BE_CALLABLE) { + type_mask &= ~MAY_BE_CALLABLE; + type_mask |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + } + if (type_mask & MAY_BE_ITERABLE) { + type_mask &= ~MAY_BE_ITERABLE; + type_mask |= MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + } + if (type_mask & MAY_BE_ARRAY) { + type_mask |= MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; } + return type_mask; } uint32_t zend_fetch_arg_info_type(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce) @@ -2262,7 +2267,7 @@ uint32_t zend_fetch_arg_info_type(const zend_script *script, zend_arg_info *arg_ *pce = get_class_entry(script, lcname); zend_string_release_ex(lcname, 0); } else if (ZEND_TYPE_IS_CODE(arg_info->type)) { - tmp |= zend_convert_type_code_to_may_be(ZEND_TYPE_CODE(arg_info->type)); + tmp |= zend_convert_type_declaration_mask(ZEND_TYPE_MASK(arg_info->type)); } else { tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; } @@ -2366,7 +2371,7 @@ static uint32_t zend_fetch_prop_type(const zend_script *script, zend_property_in if (prop_info && ZEND_TYPE_IS_SET(prop_info->type)) { uint32_t type = ZEND_TYPE_IS_CLASS(prop_info->type) ? MAY_BE_OBJECT - : zend_convert_type_code_to_may_be(ZEND_TYPE_CODE(prop_info->type)); + : zend_convert_type_declaration_mask(ZEND_TYPE_MASK(prop_info->type)); if (ZEND_TYPE_ALLOW_NULL(prop_info->type)) { type |= MAY_BE_NULL; diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h index 03cbb5e82b627..80a58405e7999 100644 --- a/ext/opcache/Optimizer/zend_inference.h +++ b/ext/opcache/Optimizer/zend_inference.h @@ -26,11 +26,11 @@ /* Bitmask for type inference (zend_ssa_var_info.type) */ #include "zend_type_info.h" -#define MAY_BE_IN_REG (1<<25) /* value allocated in CPU register */ +#define MAY_BE_IN_REG (1<<29) /* value allocated in CPU register */ //TODO: remome MAY_BE_RC1, MAY_BE_RCN??? -#define MAY_BE_RC1 (1<<27) /* may be non-reference with refcount == 1 */ -#define MAY_BE_RCN (1<<28) /* may be non-reference with refcount > 1 */ +#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */ +#define MAY_BE_RCN (1<<31) /* may be non-reference with refcount > 1 */ #define MAY_HAVE_DTOR \ (MAY_BE_OBJECT|MAY_BE_RESOURCE \ diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 952ea95ab2ded..00e0c308efc02 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -657,8 +657,8 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, case ZEND_VERIFY_RETURN_TYPE: { zend_arg_info *ret_info = op_array->arg_info - 1; if (ZEND_TYPE_IS_CLASS(ret_info->type) - || ZEND_TYPE_CODE(ret_info->type) == IS_CALLABLE - || !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(val)) + || (ZEND_TYPE_IS_CODE(ret_info->type) + && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(val))) || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) { return 0; } diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 18af7234a74b8..f68ea6172ba43 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1115,169 +1115,6 @@ static zval* ZEND_FASTCALL zend_jit_fetch_global_helper(zend_execute_data *execu return value; } -static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg) -{ - switch (type_hint) { - case _IS_BOOL: { - zend_bool dest; - - if (!zend_parse_arg_bool_weak(arg, &dest)) { - return 0; - } - zval_ptr_dtor(arg); - ZVAL_BOOL(arg, dest); - return 1; - } - case IS_LONG: { - zend_long dest; - - if (!zend_parse_arg_long_weak(arg, &dest)) { - return 0; - } - zval_ptr_dtor(arg); - ZVAL_LONG(arg, dest); - return 1; - } - case IS_DOUBLE: { - double dest; - - if (!zend_parse_arg_double_weak(arg, &dest)) { - return 0; - } - zval_ptr_dtor(arg); - ZVAL_DOUBLE(arg, dest); - return 1; - } - case IS_STRING: { - zend_string *dest; - - /* on success "arg" is converted to IS_STRING */ - if (!zend_parse_arg_str_weak(arg, &dest)) { - return 0; - } - return 1; - } - default: - return 0; - } -} - -static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, zend_bool strict) -{ - if (UNEXPECTED(strict)) { - /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */ - if (type_hint != IS_DOUBLE || Z_TYPE_P(arg) != IS_LONG) { - return 0; - } - } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) { - /* NULL may be accepted only by nullable hints (this is already checked) */ - return 0; - } - return zend_verify_weak_scalar_type_hint(type_hint, arg); -} - -static ZEND_COLD void zend_verify_type_error_common( - const zend_function *zf, const zend_arg_info *arg_info, - const zend_class_entry *ce, zval *value, - const char **fname, const char **fsep, const char **fclass, - const char **need_msg, const char **need_kind, const char **need_or_null, - const char **given_msg, const char **given_kind) -{ - zend_bool is_interface = 0; - *fname = ZSTR_VAL(zf->common.function_name); - - if (zf->common.scope) { - *fsep = "::"; - *fclass = ZSTR_VAL(zf->common.scope->name); - } else { - *fsep = ""; - *fclass = ""; - } - - if (ZEND_TYPE_IS_CLASS(arg_info->type)) { - if (ce) { - if (ce->ce_flags & ZEND_ACC_INTERFACE) { - *need_msg = "implement interface "; - is_interface = 1; - } else { - *need_msg = "be an instance of "; - } - *need_kind = ZSTR_VAL(ce->name); - } else { - /* We don't know whether it's a class or interface, assume it's a class */ - *need_msg = "be an instance of "; - *need_kind = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type)); - } - } else { - switch (ZEND_TYPE_CODE(arg_info->type)) { - case IS_CALLABLE: - *need_msg = "be callable"; - *need_kind = ""; - break; - case IS_ITERABLE: - *need_msg = "be iterable"; - *need_kind = ""; - break; - case IS_OBJECT: - *need_msg = "be an object"; - *need_kind = ""; - break; - default: - *need_msg = "be of the type "; - *need_kind = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type)); - break; - } - } - - if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) { - *need_or_null = is_interface ? " or be null" : " or null"; - } else { - *need_or_null = ""; - } - - if (value) { - if (ZEND_TYPE_IS_CLASS(arg_info->type) && Z_TYPE_P(value) == IS_OBJECT) { - *given_msg = "instance of "; - *given_kind = ZSTR_VAL(Z_OBJCE_P(value)->name); - } else { - *given_msg = zend_zval_type_name(value); - *given_kind = ""; - } - } else { - *given_msg = "none"; - *given_kind = ""; - } -} - -static ZEND_COLD void zend_verify_arg_error( - const zend_function *zf, const zend_arg_info *arg_info, - int arg_num, const zend_class_entry *ce, zval *value) -{ - zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; - const char *fname, *fsep, *fclass; - const char *need_msg, *need_kind, *need_or_null, *given_msg, *given_kind; - - if (value && !Z_ISUNDEF_P(value)) { - zend_verify_type_error_common( - zf, arg_info, ce, value, - &fname, &fsep, &fclass, &need_msg, &need_kind, &need_or_null, &given_msg, &given_kind); - - if (zf->common.type == ZEND_USER_FUNCTION) { - if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) { - zend_type_error("Argument %d passed to %s%s%s() must %s%s%s, %s%s given, called in %s on line %d", - arg_num, fclass, fsep, fname, need_msg, need_kind, need_or_null, given_msg, given_kind, - ZSTR_VAL(ptr->func->op_array.filename), ptr->opline->lineno); - } else { - zend_type_error("Argument %d passed to %s%s%s() must %s%s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, need_or_null, given_msg, given_kind); - } - } else { - zend_type_error("Argument %d passed to %s%s%s() must %s%s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, need_or_null, given_msg, given_kind); - } - } else { - zend_missing_arg_error(EG(current_execute_data)); - } -} - static void ZEND_FASTCALL zend_jit_verify_arg_object(zval *arg, zend_op_array *op_array, uint32_t arg_num, zend_arg_info *arg_info, void **cache_slot) { zend_class_entry *ce; @@ -1299,13 +1136,14 @@ static void ZEND_FASTCALL zend_jit_verify_arg_object(zval *arg, zend_op_array *o static void ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, zend_op_array *op_array, uint32_t arg_num, zend_arg_info *arg_info, void **cache_slot) { zend_class_entry *ce = NULL; - - if (Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(arg_info->type)) { - /* Null passed to nullable type */ - return; - } + uint32_t type_mask; if (UNEXPECTED(ZEND_TYPE_IS_CLASS(arg_info->type))) { + if (Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(arg_info->type)) { + /* Null passed to nullable type */ + return; + } + /* This is always an error - we fetch the class name for the error message here */ if (EXPECTED(*cache_slot)) { ce = (zend_class_entry *) *cache_slot; @@ -1316,20 +1154,20 @@ static void ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, zend_op_array *op_ } } goto err; - } else if (ZEND_TYPE_CODE(arg_info->type) == IS_CALLABLE) { + } + + type_mask = ZEND_TYPE_MASK(arg_info->type); + if (type_mask & MAY_BE_CALLABLE) { if (zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL) == 0) { goto err; } - } else if (ZEND_TYPE_CODE(arg_info->type) == IS_ITERABLE) { + } else if (type_mask & MAY_BE_ITERABLE) { if (zend_is_iterable(arg) == 0) { goto err; } - } else if (ZEND_TYPE_CODE(arg_info->type) == _IS_BOOL && - EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { - return; - } else if (ZEND_TYPE_CODE(arg_info->type) != Z_TYPE_P(arg)) { + } else { if (Z_ISUNDEF_P(arg) || - zend_verify_scalar_type_hint(ZEND_TYPE_CODE(arg_info->type), arg, ZEND_ARG_USES_STRICT_TYPES()) == 0) { + zend_verify_scalar_type_hint(ZEND_TYPE_MASK(arg_info->type), arg, ZEND_ARG_USES_STRICT_TYPES(), /* is_internal */ 0) == 0) { goto err; } } @@ -1485,7 +1323,7 @@ static zend_property_info *zend_jit_get_prop_not_accepting_double(zend_reference { zend_property_info *prop; ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) { - if (ZEND_TYPE_CODE(prop->type) != IS_DOUBLE) { + if (!ZEND_TYPE_IS_CODE(prop->type) || !(ZEND_TYPE_MASK(prop->type) & MAY_BE_DOUBLE)) { return prop; } } ZEND_REF_FOREACH_TYPE_SOURCES_END(); diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 90f674a93970d..cd7dce6dc2f08 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -7019,19 +7019,10 @@ static uint32_t skip_valid_arguments(zend_op_array *op_array, zend_ssa *ssa, zen zend_arg_info *arg_info = func->op_array.arg_info + num_args; if (ZEND_TYPE_IS_SET(arg_info->type)) { - if (!ZEND_TYPE_IS_CLASS(arg_info->type)) { - unsigned char code = ZEND_TYPE_CODE(arg_info->type); + if (ZEND_TYPE_IS_CODE(arg_info->type)) { + uint32_t type_mask = ZEND_TYPE_MASK(arg_info->type); uint32_t info = _ssa_op1_info(op_array, ssa, call_info->arg_info[num_args].opline); - - if (code == _IS_BOOL) { - if (info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_FALSE|MAY_BE_TRUE))) { - break; - } - } else if (code <= IS_RESOURCE) { - if (info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (1 << code))) { - break; - } - } else { + if ((info & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~type_mask) { break; } } else { @@ -8945,6 +8936,16 @@ static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, zend_op_array { uint32_t arg_num = opline->op1.num; + | cmp dword EX->This.u2.num_args, arg_num + | jb >1 + |.cold_code + |1: + | SAVE_VALID_OPLINE opline + | mov FCARG1a, FP + | EXT_CALL zend_missing_arg_error, r0 + | jmp ->exception_handler + |.code + if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) { zend_arg_info *arg_info = NULL; @@ -8966,18 +8967,12 @@ static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, zend_op_array | add r0, offsetof(zend_reference, val) } if (!ZEND_TYPE_IS_CLASS(type)) { - unsigned char code = ZEND_TYPE_CODE(type); - - if (code == _IS_BOOL) { - | cmp byte [r0 + 8], IS_FALSE - | je >1 - | cmp byte [r0 + 8], IS_TRUE - | jne >8 - |1: - } else { - | cmp byte [r0 + 8], code - | jne >8 - } + uint32_t type_mask = ZEND_TYPE_MASK(type); + | mov edx, 1 + | mov cl, byte [r0 + 8] + | shl edx, cl + | test edx, type_mask + | je >8 } else { | SAVE_VALID_OPLINE opline | cmp byte [r0 + 8], IS_OBJECT @@ -9054,16 +9049,6 @@ static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, zend_op_array } } - | cmp dword EX->This.u2.num_args, arg_num - | jb >1 - |.cold_code - |1: - | SAVE_VALID_OPLINE opline - | mov FCARG1a, FP - | EXT_CALL zend_missing_arg_error, r0 - | jmp ->exception_handler - |.code - if ((opline+1)->opcode != ZEND_RECV && (opline+1)->opcode != ZEND_RECV_INIT) { last_valid_opline = NULL; if (!zend_jit_set_valid_ip(Dst, opline + 1)) { @@ -9125,8 +9110,11 @@ static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, zend_op_a | LOAD_ZVAL_ADDR r0, res_addr | ZVAL_DEREF r0, MAY_BE_REF if (!ZEND_TYPE_IS_CLASS(arg_info->type)) { - | cmp byte [r0 + 8], ZEND_TYPE_CODE(arg_info->type) - | jne >8 + | mov edx, 1 + | mov cl, byte [r0 + 8] + | shl edx, cl + | test edx, ZEND_TYPE_MASK(arg_info->type) + | je >8 } else { | cmp byte [r0 + 8], IS_OBJECT | jne >8 diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 5cc023831eae2..b6aa739be34e0 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -582,17 +582,14 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_ } else { smart_str_append_printf(str, " "); } - if (ZEND_TYPE_IS_CLASS(arg_info->type)) { - smart_str_append_printf(str, "%s ", - ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type))); - if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) { - smart_str_append_printf(str, "or NULL "); - } - } else if (ZEND_TYPE_IS_CODE(arg_info->type)) { - smart_str_append_printf(str, "%s ", zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type))); + if (ZEND_TYPE_IS_SET(arg_info->type)) { + /* TODO: We should be using ?Type instead of "or NULL" here. */ + zend_string *type_str = zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(arg_info->type)); + smart_str_append_printf(str, "%s ", ZSTR_VAL(type_str)); if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) { smart_str_append_printf(str, "or NULL "); } + zend_string_release(type_str); } if (arg_info->pass_by_reference) { smart_str_appendc(str, '&'); @@ -802,17 +799,15 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent smart_str_free(¶m_indent); if (fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { smart_str_append_printf(str, " %s- Return [ ", indent); - if (ZEND_TYPE_IS_CLASS(fptr->common.arg_info[-1].type)) { - smart_str_append_printf(str, "%s ", - ZSTR_VAL(ZEND_TYPE_NAME(fptr->common.arg_info[-1].type))); - if (ZEND_TYPE_ALLOW_NULL(fptr->common.arg_info[-1].type)) { - smart_str_appends(str, "or NULL "); - } - } else if (ZEND_TYPE_IS_CODE(fptr->common.arg_info[-1].type)) { - smart_str_append_printf(str, "%s ", zend_get_type_by_const(ZEND_TYPE_CODE(fptr->common.arg_info[-1].type))); + if (ZEND_TYPE_IS_SET(fptr->common.arg_info[-1].type)) { + /* TODO: We should use ?Type instead of "or NULL" here */ + zend_string *type_str = + zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(fptr->common.arg_info[-1].type)); + smart_str_append_printf(str, "%s ", ZSTR_VAL(type_str)); if (ZEND_TYPE_ALLOW_NULL(fptr->common.arg_info[-1].type)) { smart_str_appends(str, "or NULL "); } + zend_string_release(type_str); } smart_str_appends(str, "]\n"); } @@ -2565,13 +2560,15 @@ ZEND_METHOD(reflection_parameter, isArray) { reflection_object *intern; parameter_reference *param; + zend_type type; if (zend_parse_parameters_none() == FAILURE) { return; } GET_REFLECTION_OBJECT_PTR(param); - RETVAL_BOOL(ZEND_TYPE_CODE(param->arg_info->type) == IS_ARRAY); + type = ZEND_TYPE_WITHOUT_NULL(param->arg_info->type); + RETVAL_BOOL(ZEND_TYPE_MASK(type) == MAY_BE_ARRAY); } /* }}} */ @@ -2581,13 +2578,15 @@ ZEND_METHOD(reflection_parameter, isCallable) { reflection_object *intern; parameter_reference *param; + zend_type type; if (zend_parse_parameters_none() == FAILURE) { return; } GET_REFLECTION_OBJECT_PTR(param); - RETVAL_BOOL(ZEND_TYPE_CODE(param->arg_info->type) == IS_CALLABLE); + type = ZEND_TYPE_WITHOUT_NULL(param->arg_info->type); + RETVAL_BOOL(ZEND_TYPE_MASK(type) == MAY_BE_CALLABLE); } /* }}} */ @@ -2817,19 +2816,6 @@ ZEND_METHOD(reflection_type, allowsNull) } /* }}} */ -/* {{{ reflection_type_name */ -static zend_string *reflection_type_name(type_reference *param) { - if (ZEND_TYPE_IS_NAME(param->type)) { - return zend_string_copy(ZEND_TYPE_NAME(param->type)); - } else if (ZEND_TYPE_IS_CE(param->type)) { - return zend_string_copy(ZEND_TYPE_CE(param->type)->name); - } else { - const char *name = zend_get_type_by_const(ZEND_TYPE_CODE(param->type)); - return zend_string_init(name, strlen(name), 0); - } -} -/* }}} */ - /* {{{ proto public string ReflectionType::__toString() Return the text of the type hint */ ZEND_METHOD(reflection_type, __toString) @@ -2842,7 +2828,7 @@ ZEND_METHOD(reflection_type, __toString) } GET_REFLECTION_OBJECT_PTR(param); - RETURN_STR(reflection_type_name(param)); + RETURN_STR(zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(param->type))); } /* }}} */ @@ -2858,7 +2844,7 @@ ZEND_METHOD(reflection_named_type, getName) } GET_REFLECTION_OBJECT_PTR(param); - RETURN_STR(reflection_type_name(param)); + RETURN_STR(zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(param->type))); } /* }}} */ diff --git a/ext/standard/var.c b/ext/standard/var.c index 410c0fdeb94f9..7b75bffe85d75 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -77,12 +77,10 @@ static void php_object_property_dump(zend_property_info *prop_info, zval *zv, ze if (Z_TYPE_P(zv) == IS_UNDEF) { ZEND_ASSERT(prop_info->type); - php_printf("%*cuninitialized(%s%s)\n", - level + 1, ' ', - ZEND_TYPE_ALLOW_NULL(prop_info->type) ? "?" : "", - ZEND_TYPE_IS_CLASS(prop_info->type) ? - ZSTR_VAL(ZEND_TYPE_IS_CE(prop_info->type) ? ZEND_TYPE_CE(prop_info->type)->name : ZEND_TYPE_NAME(prop_info->type)) : - zend_get_type_by_const(ZEND_TYPE_CODE(prop_info->type))); + zend_string *type_str = zend_type_to_string(prop_info->type); + php_printf("%*cuninitialized(%s)\n", + level + 1, ' ', ZSTR_VAL(type_str)); + zend_string_release(type_str); } else { php_var_dump(zv, level + 2); } @@ -260,13 +258,10 @@ static void zval_object_property_dump(zend_property_info *prop_info, zval *zv, z ZEND_PUTS("]=>\n"); } if (prop_info && Z_TYPE_P(zv) == IS_UNDEF) { - ZEND_ASSERT(prop_info->type); - php_printf("%*cuninitialized(%s%s)\n", - level + 1, ' ', - ZEND_TYPE_ALLOW_NULL(prop_info->type) ? "?" : "", - ZEND_TYPE_IS_CLASS(prop_info->type) ? - ZSTR_VAL(ZEND_TYPE_IS_CE(prop_info->type) ? ZEND_TYPE_CE(prop_info->type)->name : ZEND_TYPE_NAME(prop_info->type)) : - zend_get_type_by_const(ZEND_TYPE_CODE(prop_info->type))); + zend_string *type_str = zend_type_to_string(prop_info->type); + php_printf("%*cuninitialized(%s)\n", + level + 1, ' ', ZSTR_VAL(type_str)); + zend_string_release(type_str); } else { php_debug_zval_dump(zv, level + 2); } diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 63608124eb167..2b1813ee4780f 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -219,7 +219,7 @@ PHP_MINIT_FUNCTION(zend_test) zval val; ZVAL_LONG(&val, 123); zend_declare_typed_property( - zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, ZEND_TYPE_ENCODE(IS_LONG, 0)); + zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, ZEND_TYPE_ENCODE_CODE(IS_LONG, 0)); zend_string_release(name); } @@ -240,7 +240,7 @@ PHP_MINIT_FUNCTION(zend_test) ZVAL_LONG(&val, 123); zend_declare_typed_property( zend_test_class, name, &val, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, NULL, - ZEND_TYPE_ENCODE(IS_LONG, 0)); + ZEND_TYPE_ENCODE_CODE(IS_LONG, 0)); zend_string_release(name); }