summaryrefslogtreecommitdiff
path: root/Zend/zend_compile.c
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-09-25 13:21:13 +0200
committerNikita Popov <nikita.ppv@gmail.com>2019-11-08 15:15:48 +0100
commit999e32b65a8a4bb59e27e538fa68ffae4b99d863 (patch)
tree9b6c69d032b9b69fff2f3b71f95ad2566cdf4acb /Zend/zend_compile.c
parentac4e0f0852ce780e143013ceff45067a172e8a83 (diff)
downloadphp-git-999e32b65a8a4bb59e27e538fa68ffae4b99d863.tar.gz
Implement union types
According to RFC: https://wiki.php.net/rfc/union_types_v2 The type representation now makes use of both the pointer payload and the type mask at the same time. Additionall, zend_type_list is introduced as a new kind of pointer payload, which is used to store multiple class types. Each of the class types is a tagged pointer, which may be either a class name or class entry. The latter is only used for typed properties, while arguments/returns will instead use cache slots. A type list can contain a mix of both names and CEs at the same time, as not all classes may be resolvable. One thing this is missing is support for union types in arginfo and stubs, which I want to handle separately. I've also dropped the special object code from the JIT implementation for now -- I plan to add this back in a different form at a later time. For now I did not want to include non-trivial JIT changes together with large functional changes. Another possible piece of follow-up work is to implement "iterable" as an internal alias for "array|Traversable". I believe this will eliminate quite a few special-cases that had to be implemented. Closes GH-4838.
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);