summaryrefslogtreecommitdiff
path: root/Zend/zend_compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r--Zend/zend_compile.c371
1 files changed, 273 insertions, 98 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index ea93e878d0..53fe9afde8 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -60,6 +60,10 @@ typedef struct _zend_loop_var {
} zend_loop_var;
static inline uint32_t zend_alloc_cache_slots(unsigned count) {
+ if (count == 0) {
+ return (uint32_t) -1;
+ }
+
zend_op_array *op_array = CG(active_op_array);
uint32_t ret = op_array->cache_size;
op_array->cache_size += count * sizeof(void*);
@@ -123,6 +127,7 @@ static void zend_destroy_property_info_internal(zval *zv) /* {{{ */
zend_property_info *property_info = Z_PTR_P(zv);
zend_string_release_ex(property_info->name, 1);
+ zend_type_release(property_info->type, /* persistent */ 1);
free(property_info);
}
/* }}} */
@@ -211,6 +216,8 @@ typedef struct _builtin_type_info {
} builtin_type_info;
static const builtin_type_info builtin_types[] = {
+ {ZEND_STRL("null"), IS_NULL},
+ {ZEND_STRL("false"), IS_FALSE},
{ZEND_STRL("int"), IS_LONG},
{ZEND_STRL("float"), IS_DOUBLE},
{ZEND_STRL("string"), IS_STRING},
@@ -1119,62 +1126,94 @@ ZEND_API int do_bind_class(zval *lcname, zend_string *lc_parent_name) /* {{{ */
}
/* }}} */
+static zend_string *add_type_string(zend_string *type, zend_string *new_type) {
+ zend_string *result;
+ if (type == NULL) {
+ return zend_string_copy(new_type);
+ }
+
+ // TODO: Switch to smart_str?
+ result = zend_string_alloc(ZSTR_LEN(type) + ZSTR_LEN(new_type) + 1, 0);
+ memcpy(ZSTR_VAL(result), ZSTR_VAL(type), ZSTR_LEN(type));
+ ZSTR_VAL(result)[ZSTR_LEN(type)] = '|';
+ memcpy(ZSTR_VAL(result) + ZSTR_LEN(type) + 1, ZSTR_VAL(new_type), ZSTR_LEN(new_type));
+ ZSTR_VAL(result)[ZSTR_LEN(type) + ZSTR_LEN(new_type) + 1] = '\0';
+ zend_string_release(type);
+ return result;
+}
+
+static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scope) {
+ 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;
+ }
+ }
+ return 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)) {
+ zend_string *str = NULL;
+ if (ZEND_TYPE_HAS_LIST(type)) {
+ void *elem;
+ ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), elem) {
+ if (ZEND_TYPE_LIST_IS_CE(elem)) {
+ str = add_type_string(str, ZEND_TYPE_LIST_GET_CE(elem)->name);
+ } else {
+ str = add_type_string(str,
+ resolve_class_name(ZEND_TYPE_LIST_GET_NAME(elem), scope));
+ }
+ } ZEND_TYPE_LIST_FOREACH_END();
+ } else if (ZEND_TYPE_HAS_NAME(type)) {
+ str = zend_string_copy(resolve_class_name(ZEND_TYPE_NAME(type), scope));
+ } else if (ZEND_TYPE_HAS_CE(type)) {
str = zend_string_copy(ZEND_TYPE_CE(type)->name);
- } else {
- uint32_t type_mask = ZEND_TYPE_PURE_MASK_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;
+ uint32_t type_mask = ZEND_TYPE_FULL_MASK(type);
+ if (type_mask & MAY_BE_CALLABLE) {
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_CALLABLE));
+ }
+ if (type_mask & MAY_BE_ITERABLE) {
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_ITERABLE));
+ }
+ if (type_mask & MAY_BE_OBJECT) {
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_OBJECT));
+ }
+ if (type_mask & MAY_BE_ARRAY) {
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_ARRAY));
+ }
+ if (type_mask & MAY_BE_STRING) {
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_STRING));
+ }
+ if (type_mask & MAY_BE_LONG) {
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_INT));
+ }
+ if (type_mask & MAY_BE_DOUBLE) {
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_FLOAT));
+ }
+ if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) {
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_BOOL));
+ } else if (type_mask & MAY_BE_FALSE) {
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_FALSE));
+ }
+ if (type_mask & MAY_BE_VOID) {
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_VOID));
+ }
+
+ if (type_mask & MAY_BE_NULL) {
+ zend_bool is_union = !str || memchr(ZSTR_VAL(str), '|', ZSTR_LEN(str)) != NULL;
+ if (!is_union) {
+ 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;
+ }
+
+ str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE));
}
return str;
}
@@ -1183,6 +1222,12 @@ zend_string *zend_type_to_string(zend_type type) {
return zend_type_to_string_resolved(type, NULL);
}
+static zend_bool is_generator_compatible_class_type(zend_string *name) {
+ return zend_string_equals_literal_ci(name, "Traversable")
+ || zend_string_equals_literal_ci(name, "Iterator")
+ || zend_string_equals_literal_ci(name, "Generator");
+}
+
static void zend_mark_function_as_generator() /* {{{ */
{
if (!CG(active_op_array)->function_name) {
@@ -1191,21 +1236,30 @@ 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");
+ zend_type return_type = CG(active_op_array)->arg_info[-1].type;
+ zend_bool valid_type = 0;
+ if (ZEND_TYPE_HAS_CLASS(return_type)) {
+ if (ZEND_TYPE_HAS_LIST(return_type)) {
+ void *entry;
+ ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(return_type), entry) {
+ ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
+ if (is_generator_compatible_class_type(ZEND_TYPE_LIST_GET_NAME(entry))) {
+ valid_type = 1;
+ break;
+ }
+ } ZEND_TYPE_LIST_FOREACH_END();
+ } else {
+ ZEND_ASSERT(ZEND_TYPE_HAS_NAME(return_type));
+ valid_type = is_generator_compatible_class_type(ZEND_TYPE_NAME(return_type));
+ }
} else {
- valid_type = (ZEND_TYPE_FULL_MASK(return_info.type) & MAY_BE_ITERABLE) != 0;
+ valid_type = (ZEND_TYPE_FULL_MASK(return_type) & MAY_BE_ITERABLE) != 0;
}
if (!valid_type) {
- zend_string *str = zend_type_to_string(return_info.type);
+ zend_string *str = zend_type_to_string(return_type);
zend_error_noreturn(E_COMPILE_ERROR,
- "Generators may only declare a return type of " \
+ "Generators may only declare a return type containing " \
"Generator, Iterator, Traversable, or iterable, %s is not permitted",
ZSTR_VAL(str));
}
@@ -2167,6 +2221,16 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */
}
/* }}} */
+static size_t zend_type_get_num_classes(zend_type type) {
+ if (!ZEND_TYPE_HAS_CLASS(type)) {
+ return 0;
+ }
+ if (ZEND_TYPE_HAS_LIST(type)) {
+ return ZEND_TYPE_LIST(type)->num_types;
+ }
+ return 1;
+}
+
static void zend_emit_return_type_check(
znode *expr, zend_arg_info *return_info, zend_bool implicit) /* {{{ */
{
@@ -2212,12 +2276,8 @@ static void zend_emit_return_type_check(
opline->result_type = expr->op_type = IS_TMP_VAR;
opline->result.var = expr->u.op.var = get_temporary_variable();
}
- if (ZEND_TYPE_IS_CLASS(return_info->type)) {
- opline->op2.num = CG(active_op_array)->cache_size;
- CG(active_op_array)->cache_size += sizeof(void*);
- } else {
- opline->op2.num = -1;
- }
+
+ opline->op2.num = zend_alloc_cache_slots(zend_type_get_num_classes(return_info->type));
}
}
/* }}} */
@@ -5372,16 +5432,11 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */
}
/* }}} */
-static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null) /* {{{ */
+static zend_type zend_compile_single_typename(zend_ast *ast)
{
- zend_bool allow_null = force_allow_null;
- if (ast->attr & ZEND_TYPE_NULLABLE) {
- allow_null = 1;
- ast->attr &= ~ZEND_TYPE_NULLABLE;
- }
-
+ ZEND_ASSERT(!(ast->attr & ZEND_TYPE_NULLABLE));
if (ast->kind == ZEND_AST_TYPE) {
- return (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, allow_null, 0);
+ return (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, 0, 0);
} else {
zend_string *class_name = zend_ast_get_str(ast);
zend_uchar type = zend_lookup_builtin_type_by_name(class_name);
@@ -5392,10 +5447,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)));
}
- if (type == IS_VOID && allow_null) {
- zend_error_noreturn(E_COMPILE_ERROR, "Void type cannot be nullable");
- }
- return (zend_type) ZEND_TYPE_INIT_CODE(type, allow_null, 0);
+ return (zend_type) ZEND_TYPE_INIT_CODE(type, 0, 0);
} else {
const char *correct_name;
zend_string *orig_name = zend_ast_get_str(ast);
@@ -5427,9 +5479,141 @@ static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null
}
}
- return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, allow_null, 0);
+ return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 0, 0);
+ }
+ }
+}
+
+static zend_bool zend_type_contains_traversable(zend_type type) {
+ if (ZEND_TYPE_HAS_LIST(type)) {
+ void *entry;
+ ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), entry) {
+ ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
+ if (zend_string_equals_literal_ci(ZEND_TYPE_LIST_GET_NAME(entry), "Traversable")) {
+ return 1;
+ }
+ } ZEND_TYPE_LIST_FOREACH_END();
+ } else if (ZEND_TYPE_HAS_NAME(type)) {
+ return zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "Traversable");
+ }
+ return 0;
+}
+
+// TODO: Ideally we'd canonicalize "iterable" into "array|Traversable" and essentially
+// treat it as a built-in type alias.
+static zend_type zend_compile_typename(
+ zend_ast *ast, zend_bool force_allow_null, zend_bool use_arena) /* {{{ */
+{
+ zend_bool allow_null = force_allow_null;
+ zend_type type = ZEND_TYPE_INIT_NONE(0);
+ if (ast->attr & ZEND_TYPE_NULLABLE) {
+ allow_null = 1;
+ ast->attr &= ~ZEND_TYPE_NULLABLE;
+ }
+
+ if (ast->kind == ZEND_AST_TYPE_UNION) {
+ zend_ast_list *list = zend_ast_get_list(ast);
+ for (uint32_t i = 0; i < list->children; i++) {
+ zend_ast *type_ast = list->child[i];
+ zend_type single_type = zend_compile_single_typename(type_ast);
+ uint32_t type_mask_overlap =
+ ZEND_TYPE_PURE_MASK(type) & ZEND_TYPE_PURE_MASK(single_type);
+ if (type_mask_overlap) {
+ zend_type overlap_type = ZEND_TYPE_INIT_MASK(type_mask_overlap);
+ zend_string *overlap_type_str = zend_type_to_string(overlap_type);
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Duplicate type %s is redundant", ZSTR_VAL(overlap_type_str));
+ }
+ ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(single_type);
+
+ if (ZEND_TYPE_HAS_CLASS(single_type)) {
+ if (!ZEND_TYPE_HAS_CLASS(type)) {
+ /* The first class type can be stored directly as the type ptr payload. */
+ ZEND_TYPE_SET_PTR(type, ZEND_TYPE_NAME(single_type));
+ ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_NAME_BIT;
+ } else {
+ zend_type_list *list;
+ if (ZEND_TYPE_HAS_LIST(type)) {
+ /* Add name to existing name list. */
+ zend_type_list *old_list = ZEND_TYPE_LIST(type);
+ if (use_arena) {
+ // TODO: Add a zend_arena_realloc API?
+ list = zend_arena_alloc(
+ &CG(arena), ZEND_TYPE_LIST_SIZE(old_list->num_types + 1));
+ memcpy(list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types));
+ } else {
+ list = erealloc(old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types + 1));
+ }
+ list->types[list->num_types++] = ZEND_TYPE_NAME(single_type);
+ } else {
+ /* Switch from single name to name list. */
+ size_t size = ZEND_TYPE_LIST_SIZE(2);
+ list = use_arena ? zend_arena_alloc(&CG(arena), size) : emalloc(size);
+ list->num_types = 2;
+ list->types[0] = ZEND_TYPE_NAME(type);
+ list->types[1] = ZEND_TYPE_NAME(single_type);
+ }
+ ZEND_TYPE_SET_LIST(type, list);
+ if (use_arena) {
+ ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_ARENA_BIT;
+ }
+
+ /* Check for trivially redundant class types */
+ for (size_t i = 0; i < list->num_types - 1; i++) {
+ if (zend_string_equals_ci(
+ ZEND_TYPE_LIST_GET_NAME(list->types[i]),
+ ZEND_TYPE_NAME(single_type))) {
+ zend_string *single_type_str = zend_type_to_string(single_type);
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Duplicate type %s is redundant", ZSTR_VAL(single_type_str));
+ }
+ }
+ }
+ }
+ }
+ } else {
+ type = zend_compile_single_typename(ast);
+ }
+
+ if (allow_null) {
+ ZEND_TYPE_FULL_MASK(type) |= MAY_BE_NULL;
+ }
+
+ uint32_t type_mask = ZEND_TYPE_PURE_MASK(type);
+ if ((type_mask & (MAY_BE_ARRAY|MAY_BE_ITERABLE)) == (MAY_BE_ARRAY|MAY_BE_ITERABLE)) {
+ zend_string *type_str = zend_type_to_string(type);
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Type %s contains both iterable and array, which is redundant", ZSTR_VAL(type_str));
+ }
+
+ if ((type_mask & MAY_BE_ITERABLE) && zend_type_contains_traversable(type)) {
+ zend_string *type_str = zend_type_to_string(type);
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Type %s contains both iterable and Traversable, which is redundant",
+ ZSTR_VAL(type_str));
+ }
+
+ if ((type_mask & MAY_BE_OBJECT) && ZEND_TYPE_HAS_CLASS(type)) {
+ zend_string *type_str = zend_type_to_string(type);
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Type %s contains both object and a class type, which is redundant",
+ ZSTR_VAL(type_str));
+ }
+
+ if ((type_mask & MAY_BE_VOID) && (ZEND_TYPE_HAS_CLASS(type) || type_mask != MAY_BE_VOID)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Void can only be used as a standalone type");
+ }
+
+ if ((type_mask & (MAY_BE_NULL|MAY_BE_FALSE))
+ && !ZEND_TYPE_HAS_CLASS(type) && !(type_mask & ~(MAY_BE_NULL|MAY_BE_FALSE))) {
+ if (type_mask == MAY_BE_NULL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Null can not be used as a standalone type");
+ } else {
+ zend_error_noreturn(E_COMPILE_ERROR, "False can not be used as a standalone type");
}
}
+
+ return type;
}
/* }}} */
@@ -5437,13 +5621,6 @@ static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null
static zend_bool zend_is_valid_default_value(zend_type type, zval *value)
{
ZEND_ASSERT(ZEND_TYPE_IS_SET(type));
- if (Z_TYPE_P(value) == IS_NULL && ZEND_TYPE_ALLOW_NULL(type)) {
- return 1;
- }
-
- if (ZEND_TYPE_IS_CLASS(type)) {
- return 0;
- }
if (ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(value))) {
return 1;
}
@@ -5469,7 +5646,8 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
/* Use op_array->arg_info[-1] for return type */
arg_infos = safe_emalloc(sizeof(zend_arg_info), list->children + 1, 0);
arg_infos->name = NULL;
- arg_infos->type = zend_compile_typename(return_type_ast, 0);
+ arg_infos->type = zend_compile_typename(
+ return_type_ast, /* force_allow_null */ 0, /* use_arena */ 0);
ZEND_TYPE_FULL_MASK(arg_infos->type) |= _ZEND_ARG_INFO_FLAGS(
(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0, /* is_variadic */ 0);
arg_infos++;
@@ -5544,13 +5722,11 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
if (type_ast) {
uint32_t default_type = default_ast ? Z_TYPE(default_node.u.constant) : IS_UNDEF;
- zend_bool is_class;
-
op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
- arg_info->type = zend_compile_typename(type_ast, default_type == IS_NULL);
- is_class = ZEND_TYPE_IS_CLASS(arg_info->type);
+ arg_info->type = zend_compile_typename(
+ type_ast, default_type == IS_NULL, /* use_arena */ 0);
- if (!is_class && (ZEND_TYPE_FULL_MASK(arg_info->type) & MAY_BE_VOID)) {
+ if (ZEND_TYPE_FULL_MASK(arg_info->type) & MAY_BE_VOID) {
zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type");
}
@@ -5570,9 +5746,8 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
if (type_ast) {
/* Allocate cache slot to speed-up run-time class resolution */
- if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
- opline->extended_value = zend_alloc_cache_slot();
- }
+ opline->extended_value =
+ zend_alloc_cache_slots(zend_type_get_num_classes(arg_info->type));
}
ZEND_TYPE_FULL_MASK(arg_info->type) |= _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic);
@@ -6078,7 +6253,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) /
zend_type type = ZEND_TYPE_INIT_NONE(0);
if (type_ast) {
- type = zend_compile_typename(type_ast, 0);
+ type = zend_compile_typename(type_ast, /* force_allow_null */ 0, /* use_arena */ 1);
if (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_VOID|MAY_BE_CALLABLE)) {
zend_string *str = zend_type_to_string(type);