]> granicus.if.org Git - php/commitdiff
Change representation of zend_type from type code to MAY_BE_* mask
authorNikita Popov <nikita.ppv@gmail.com>
Thu, 19 Sep 2019 10:11:29 +0000 (12:11 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Mon, 23 Sep 2019 13:31:35 +0000 (15:31 +0200)
This switches zend_type from storing a single IS_* type code to
storing a MAY_BE_* type mask. Right now most code still assumes
that there is only a single type in the mask (or two together
with MAY_BE_NULL). But this will make it a lot simpler to introduce
union types.

An additional advantage (and why I'm doing this separately), is
that a number of special cases no longer need to be handled
separately: We can do a single mask & (1 << type) check to handle
all simple types, booleans (true|false) and null.

23 files changed:
.gdbinit
Zend/tests/type_declarations/typed_properties_054.phpt
Zend/zend_API.h
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_execute.c
Zend/zend_execute.h
Zend/zend_inheritance.c
Zend/zend_string.h
Zend/zend_type_info.h
Zend/zend_types.h
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
ext/com_dotnet/com_handlers.c
ext/opcache/Optimizer/dfa_pass.c
ext/opcache/Optimizer/zend_inference.c
ext/opcache/Optimizer/zend_inference.h
ext/opcache/Optimizer/zend_optimizer.c
ext/opcache/jit/zend_jit_helpers.c
ext/opcache/jit/zend_jit_x86.dasc
ext/reflection/php_reflection.c
ext/standard/var.c
ext/zend_test/test.c

index 8e54830ef2a1b46b4258ae76a15fb0f7630765ab..a903c086e53b427cde2d735e5cdf2672cc674858 100644 (file)
--- 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"
index 38de9815edff5aafd1ba97cb07ead876a01b8d65..cb771d6e645492ec52cdf15e358f6c5c0f3f2218 100644 (file)
@@ -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
index 5cdba4450b5d64c299d8bf0456250a64b4e07f9a..acc6bb9cf37181449ef6d8ea97ad4f3440e0527f 100644 (file)
@@ -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)
 
index 19992e2cbd49f6ee958ab6fb12e04a2261d82547..986584c690eaebeb347a2d26ba5fd6f70057243a 100644 (file)
@@ -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_MASK(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_MASK(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_MASK(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_MASK(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)) {
index a960115aab632355c1e223fcd9100217cf98f27b..c4c2b8bac7a0cdbd1dd4c7aa26b315b1a8098ed7 100644 (file)
@@ -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"
index 1d628a235206d8ac28b6f5593aad7f9f1a0d4c66..cf32ba55aca743c17a67b4d281706dd9ef830595 100644 (file)
@@ -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_MASK(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_MASK(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_MASK(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_MASK(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;
                        }
                }
index 3840e39bccf5e09b0a310f303fe45de5d65af0c4..0dcf89f1c1f0236dae6ec9dec8aadc65f7efa63b 100644 (file)
@@ -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
 
index 3c77026e4c80bb23b02baae80ab25e9b1a00ad90..b0e35a9552fb96b2de950414c2c628a151e6c2ce 100644 (file)
@@ -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))) {
index 674bea2f6005a85fa26f2f0263834dff58f5a480..a38c1cae8c5521d67242898c6c995a2a9a395a03 100644 (file)
@@ -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 {
index 72550b6fc3a9362e14c1b5f0800bcffdd1ee2e38..c991fd5db5994a9583ebf66fbde9ae55324e92ec 100644 (file)
 #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)
 #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 */
index 653a07b6b247969d911885a2e338b7cef03a0111..be6c52b8cac36d88598c6e45bd16b37e4f405d07 100644 (file)
@@ -106,61 +106,73 @@ typedef void (*copy_ctor_func_t)(zval *pElement);
  * It shouldn't be used directly. Only through ZEND_TYPE_* macros.
  *
  * ZEND_TYPE_IS_SET()     - checks if type-hint exists
- * ZEND_TYPE_IS_CODE()    - checks if type-hint refer to standard type
+ * ZEND_TYPE_IS_MASK()    - checks if type-hint refer to standard type
  * ZEND_TYPE_IS_CLASS()   - checks if type-hint refer to some class
  * ZEND_TYPE_IS_CE()      - checks if type-hint refer to some class by zend_class_entry *
  * ZEND_TYPE_IS_NAME()    - checks if type-hint refer to some class by zend_string *
  *
  * 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
  *
- * ZEND_TYPE_ENCODE() and ZEND_TYPE_ENCODE_CLASS() should be used for
- * construction.
+ * ZEND_TYPE_ENCODE_*() should be used for construction.
  */
 
 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)))
+#define ZEND_TYPE_IS_MASK(t) \
+       (((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_MASK(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 +422,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 +434,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;
index 8884810f65dc761a5cded5f8a346c9d44a858013..4e95859a7fcb785d81d244e8e8f76d2b36323154 100644 (file)
@@ -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)
                ) {
index 70d89048e7eee24ec20f545ef4fbbc99a2b40c62..a48b964e45ae78682cac8b08c3ca57109c80b076 100644 (file)
@@ -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)
                ) {
index 38930175532f2d5970d77574d032538a446ca1c4..fd4cb4ab3b2663584f076f9d25c79a1cbfcedb02 100644 (file)
@@ -319,7 +319,7 @@ static zend_function *com_method_get(zend_object **object_ptr, zend_string *name
                                                        f.arg_info = ecalloc(bindptr.lpfuncdesc->cParams, sizeof(zend_arg_info));
 
                                                        for (i = 0; i < bindptr.lpfuncdesc->cParams; i++) {
-                                                               f.arg_info[i].type = ZEND_TYPE_ENCODE(0,1);
+                                                               f.arg_info[i].type = ZEND_TYPE_ENCODE_CODE(0,1);
                                                                if (bindptr.lpfuncdesc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FOUT) {
                                                                        f.arg_info[i].pass_by_reference = ZEND_SEND_BY_REF;
                                                                }
index 5401c9df6a78c9c6c1eecab8ba6b11dc32912854..5ea77759217c3ab77d5eca809a4df6ee32782122 100644 (file)
@@ -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_MASK(info->type)
+                       && (ZEND_TYPE_MASK(info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))) {
                return 0;
        }
 
index e8a9918b398e0eec225be852acb9201370057db8..2e308570136c894be6d9a67c9a1e63e0b694a400 100644 (file)
@@ -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_MASK(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)
@@ -2261,8 +2266,8 @@ uint32_t zend_fetch_arg_info_type(const zend_script *script, zend_arg_info *arg_
                tmp |= MAY_BE_OBJECT;
                *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));
+       } else if (ZEND_TYPE_IS_MASK(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;
index 03cbb5e82b627ce938037d87df659691f34c760d..80a58405e799925c9e7a53d654bc2d79d3081bf2 100644 (file)
 /* 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 \
index 952ea95ab2dedb69aba0d5bd473a0ca8c05213a2..7bac2d7538cc498fc2547aa68cfb68f9d911ef56 100644 (file)
@@ -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_MASK(ret_info->type)
+                                                       && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(val)))
                                                || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
                                                return 0;
                                        }
index 18af7234a74b8aa9ba4ea573df439fd285e5d687..54b888a132f3b3708167bc8988e574ea46192ad1 100644 (file)
@@ -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_MASK(prop->type) || !(ZEND_TYPE_MASK(prop->type) & MAY_BE_DOUBLE)) {
                        return prop;
                }
        } ZEND_REF_FOREACH_TYPE_SOURCES_END();
index 90f674a93970dc01479510e2d6b0339e73179e2e..c71606892329dff2b114c4e17d751ecf4787f790 100644 (file)
@@ -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_MASK(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,13 @@ 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
-                                       }
+                                       // TODO: Use bt?
+                                       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 +9050,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 +9111,12 @@ 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
+                               // TODO: Use bt?
+                       |       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
index 5cc023831eae22d344c26d3cad0cf016316f92ef..18e3a0cecc5be2d71f5dbe59d123f1d93669714c 100644 (file)
@@ -582,17 +582,14 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
        } else {
                smart_str_append_printf(str, "<required> ");
        }
-       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(&param_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)));
 }
 /* }}} */
 
@@ -2874,7 +2860,7 @@ ZEND_METHOD(reflection_named_type, isBuiltin)
        }
        GET_REFLECTION_OBJECT_PTR(param);
 
-       RETVAL_BOOL(ZEND_TYPE_IS_CODE(param->type));
+       RETVAL_BOOL(ZEND_TYPE_IS_MASK(param->type));
 }
 /* }}} */
 
index 410c0fdeb94f9cb734ad07972f0dd5623d4f8251..7b75bffe85d75757d052eaea7ae6736dee8062d3 100644 (file)
@@ -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);
        }
index 63608124eb167f8a4cf20190e8ce9215dedc3b03..2b1813ee4780f13b17b27a635f33929440bb615b 100644 (file)
@@ -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);
        }