summaryrefslogtreecommitdiff
path: root/Zend/zend_inheritance.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_inheritance.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_inheritance.c')
-rw-r--r--Zend/zend_inheritance.c320
1 files changed, 202 insertions, 118 deletions
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index af81c327e1..9b8a47f365 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -40,14 +40,31 @@ static void overridden_ptr_dtor(zval *zv) /* {{{ */
}
/* }}} */
+static void zend_type_copy_ctor(zend_type *type, zend_bool persistent) {
+ if (ZEND_TYPE_HAS_LIST(*type)) {
+ zend_type_list *old_list = ZEND_TYPE_LIST(*type);
+ size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types);
+ zend_type_list *new_list = ZEND_TYPE_USES_ARENA(*type)
+ ? zend_arena_alloc(&CG(arena), size) : pemalloc(size, persistent);
+ memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types));
+ ZEND_TYPE_SET_PTR(*type, new_list);
+
+ void *entry;
+ ZEND_TYPE_LIST_FOREACH(new_list, entry) {
+ ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
+ zend_string_addref(ZEND_TYPE_LIST_GET_NAME(entry));
+ } ZEND_TYPE_LIST_FOREACH_END();
+ } else if (ZEND_TYPE_HAS_NAME(*type)) {
+ zend_string_addref(ZEND_TYPE_NAME(*type));
+ }
+}
+
static zend_property_info *zend_duplicate_property_info_internal(zend_property_info *property_info) /* {{{ */
{
zend_property_info* new_property_info = pemalloc(sizeof(zend_property_info), 1);
memcpy(new_property_info, property_info, sizeof(zend_property_info));
zend_string_addref(new_property_info->name);
- if (ZEND_TYPE_IS_NAME(new_property_info->type)) {
- zend_string_addref(ZEND_TYPE_NAME(new_property_info->type));
- }
+ zend_type_copy_ctor(&new_property_info->type, /* persistent */ 1);
return new_property_info;
}
@@ -219,7 +236,8 @@ static zend_bool class_visible(zend_class_entry *ce) {
}
}
-static zend_class_entry *lookup_class(zend_class_entry *scope, zend_string *name) {
+static zend_class_entry *lookup_class(
+ zend_class_entry *scope, zend_string *name, zend_bool register_unresolved) {
zend_class_entry *ce;
if (!CG(in_compilation)) {
uint32_t flags = ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD;
@@ -228,12 +246,14 @@ static zend_class_entry *lookup_class(zend_class_entry *scope, zend_string *name
return ce;
}
- /* We'll autoload this class and process delayed variance obligations later. */
- if (!CG(delayed_autoloads)) {
- ALLOC_HASHTABLE(CG(delayed_autoloads));
- zend_hash_init(CG(delayed_autoloads), 0, NULL, NULL, 0);
+ if (register_unresolved) {
+ /* We'll autoload this class and process delayed variance obligations later. */
+ if (!CG(delayed_autoloads)) {
+ ALLOC_HASHTABLE(CG(delayed_autoloads));
+ zend_hash_init(CG(delayed_autoloads), 0, NULL, NULL, 0);
+ }
+ zend_hash_add_empty_element(CG(delayed_autoloads), name);
}
- zend_hash_add_empty_element(CG(delayed_autoloads), name);
} else {
ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
if (ce && class_visible(ce)) {
@@ -302,6 +322,23 @@ static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce
return 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();
+ return 0;
+ }
+ if (ZEND_TYPE_HAS_NAME(type)) {
+ return zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "Traversable");
+ }
+ return 0;
+}
+
/* Unresolved means that class declarations that are currently not available are needed to
* determine whether the inheritance is valid or not. At runtime UNRESOLVED should be treated
* as an ERROR. */
@@ -311,86 +348,150 @@ typedef enum {
INHERITANCE_SUCCESS = 1,
} inheritance_status;
-static inheritance_status zend_perform_covariant_type_check(
- zend_string **unresolved_class,
- const zend_function *fe, zend_arg_info *fe_arg_info,
- const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */
-{
- zend_type fe_type = fe_arg_info->type, proto_type = proto_arg_info->type;
- ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_type) && ZEND_TYPE_IS_SET(proto_type));
-
- if (ZEND_TYPE_ALLOW_NULL(fe_type) && !ZEND_TYPE_ALLOW_NULL(proto_type)) {
- return INHERITANCE_ERROR;
+static inheritance_status zend_perform_covariant_class_type_check(
+ zend_class_entry *fe_scope, zend_string *fe_class_name,
+ zend_class_entry *proto_scope, zend_type proto_type,
+ zend_bool register_unresolved) {
+ zend_bool have_unresolved = 0;
+ zend_class_entry *fe_ce = NULL;
+ if (ZEND_TYPE_FULL_MASK(proto_type) & MAY_BE_OBJECT) {
+ /* 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
+ * are not classes (such as enums or typedefs). */
+ if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name, register_unresolved);
+ if (!fe_ce) {
+ have_unresolved = 1;
+ } else {
+ return INHERITANCE_SUCCESS;
+ }
}
-
- if (ZEND_TYPE_IS_CLASS(proto_type)) {
- zend_string *fe_class_name, *proto_class_name;
- zend_class_entry *fe_ce, *proto_ce;
- if (!ZEND_TYPE_IS_CLASS(fe_type)) {
- return INHERITANCE_ERROR;
+ if (ZEND_TYPE_FULL_MASK(proto_type) & MAY_BE_ITERABLE) {
+ if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name, register_unresolved);
+ if (!fe_ce) {
+ have_unresolved = 1;
+ } else if (unlinked_instanceof(fe_ce, zend_ce_traversable)) {
+ return INHERITANCE_SUCCESS;
}
-
- fe_class_name = resolve_class_name(fe->common.scope, ZEND_TYPE_NAME(fe_type));
- proto_class_name = resolve_class_name(proto->common.scope, ZEND_TYPE_NAME(proto_type));
+ }
+ if (ZEND_TYPE_HAS_NAME(proto_type)) {
+ zend_string *proto_class_name = resolve_class_name(proto_scope, ZEND_TYPE_NAME(proto_type));
if (zend_string_equals_ci(fe_class_name, proto_class_name)) {
return INHERITANCE_SUCCESS;
}
/* Make sure to always load both classes, to avoid only registering one of them as
* a delayed autoload. */
- fe_ce = lookup_class(fe->common.scope, fe_class_name);
- proto_ce = lookup_class(proto->common.scope, proto_class_name);
- if (!fe_ce) {
- *unresolved_class = fe_class_name;
- return INHERITANCE_UNRESOLVED;
- }
- if (!proto_ce) {
- *unresolved_class = proto_class_name;
- return INHERITANCE_UNRESOLVED;
+ if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name, register_unresolved);
+ zend_class_entry *proto_ce =
+ lookup_class(proto_scope, proto_class_name, register_unresolved);
+ if (!fe_ce || !proto_ce) {
+ have_unresolved = 1;
+ } else if (unlinked_instanceof(fe_ce, proto_ce)) {
+ return INHERITANCE_SUCCESS;
}
+ }
+ if (ZEND_TYPE_HAS_LIST(proto_type)) {
+ void *entry;
+ ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(proto_type), entry) {
+ ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
+ zend_string *proto_class_name =
+ resolve_class_name(proto_scope, ZEND_TYPE_LIST_GET_NAME(entry));
+ if (zend_string_equals_ci(fe_class_name, proto_class_name)) {
+ return INHERITANCE_SUCCESS;
+ }
- return unlinked_instanceof(fe_ce, proto_ce) ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
- } else if (ZEND_TYPE_FULL_MASK(proto_type) & MAY_BE_ITERABLE) {
- if (ZEND_TYPE_IS_CLASS(fe_type)) {
- zend_string *fe_class_name =
- resolve_class_name(fe->common.scope, ZEND_TYPE_NAME(fe_type));
- zend_class_entry *fe_ce = lookup_class(fe->common.scope, fe_class_name);
- if (!fe_ce) {
- *unresolved_class = fe_class_name;
- return INHERITANCE_UNRESOLVED;
+ if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name, register_unresolved);
+ zend_class_entry *proto_ce =
+ lookup_class(proto_scope, proto_class_name, register_unresolved);
+ if (!fe_ce || !proto_ce) {
+ have_unresolved = 1;
+ } else if (unlinked_instanceof(fe_ce, proto_ce)) {
+ return INHERITANCE_SUCCESS;
}
- return unlinked_instanceof(fe_ce, zend_ce_traversable)
- ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
+ } ZEND_TYPE_LIST_FOREACH_END();
+ }
+ return have_unresolved ? INHERITANCE_UNRESOLVED : INHERITANCE_ERROR;
+}
+
+static inheritance_status zend_perform_covariant_type_check(
+ zend_class_entry *fe_scope, zend_type fe_type,
+ zend_class_entry *proto_scope, zend_type proto_type) /* {{{ */
+{
+ ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_type) && ZEND_TYPE_IS_SET(proto_type));
+
+ /* Builtin types may be removed, but not added */
+ uint32_t fe_type_mask = ZEND_TYPE_PURE_MASK(fe_type);
+ uint32_t proto_type_mask = ZEND_TYPE_PURE_MASK(proto_type);
+ uint32_t added_types = fe_type_mask & ~proto_type_mask;
+ if (added_types) {
+ // TODO: Make "iterable" an alias of "array|Traversable" instead,
+ // so these special cases will be handled automatically.
+ if (added_types == MAY_BE_ITERABLE
+ && (proto_type_mask & MAY_BE_ARRAY)
+ && zend_type_contains_traversable(proto_type)) {
+ /* Replacing array|Traversable with iterable is okay */
+ } else if (added_types == MAY_BE_ARRAY && (proto_type_mask & MAY_BE_ITERABLE)) {
+ /* Replacing iterable with array is okay */
+ } else {
+ /* Otherwise adding new types is illegal */
+ return INHERITANCE_ERROR;
+ }
+ }
+
+ if (ZEND_TYPE_HAS_NAME(fe_type)) {
+ zend_string *fe_class_name = resolve_class_name(fe_scope, ZEND_TYPE_NAME(fe_type));
+ inheritance_status status = zend_perform_covariant_class_type_check(
+ fe_scope, fe_class_name, proto_scope, proto_type, /* register_unresolved */ 0);
+ if (status != INHERITANCE_UNRESOLVED) {
+ return status;
}
- return ZEND_TYPE_FULL_MASK(fe_type) & (MAY_BE_ARRAY|MAY_BE_ITERABLE)
- ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
- } else if (ZEND_TYPE_FULL_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
- * are not classes (such as enums or typedefs). */
+ zend_perform_covariant_class_type_check(
+ fe_scope, fe_class_name, proto_scope, proto_type, /* register_unresolved */ 1);
+ return INHERITANCE_UNRESOLVED;
+ }
+
+ if (ZEND_TYPE_HAS_LIST(fe_type)) {
+ void *entry;
+ zend_bool all_success = 1;
+
+ /* First try to check whether we can succeed without resolving anything */
+ ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(fe_type), entry) {
+ ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
zend_string *fe_class_name =
- resolve_class_name(fe->common.scope, ZEND_TYPE_NAME(fe_type));
- zend_class_entry *fe_ce = lookup_class(fe->common.scope, fe_class_name);
- if (!fe_ce) {
- *unresolved_class = fe_class_name;
- return INHERITANCE_UNRESOLVED;
+ resolve_class_name(fe_scope, ZEND_TYPE_LIST_GET_NAME(entry));
+ inheritance_status status = zend_perform_covariant_class_type_check(
+ fe_scope, fe_class_name, proto_scope, proto_type, /* register_unresolved */ 0);
+ if (status == INHERITANCE_ERROR) {
+ return INHERITANCE_ERROR;
+ }
+
+ if (status != INHERITANCE_SUCCESS) {
+ all_success = 0;
}
+ } ZEND_TYPE_LIST_FOREACH_END();
+
+ /* All individual checks suceeded, overall success */
+ if (all_success) {
return INHERITANCE_SUCCESS;
}
- return ZEND_TYPE_FULL_MASK(fe_type) & MAY_BE_OBJECT
- ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
- } else {
- return ZEND_TYPE_PURE_MASK_WITHOUT_NULL(fe_type) == ZEND_TYPE_PURE_MASK_WITHOUT_NULL(proto_type)
- ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
+ /* Register all classes that may have to be resolved */
+ ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(fe_type), entry) {
+ ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
+ zend_string *fe_class_name =
+ resolve_class_name(fe_scope, ZEND_TYPE_LIST_GET_NAME(entry));
+ zend_perform_covariant_class_type_check(
+ fe_scope, fe_class_name, proto_scope, proto_type, /* register_unresolved */ 1);
+ } ZEND_TYPE_LIST_FOREACH_END();
+ return INHERITANCE_UNRESOLVED;
}
+
+ return INHERITANCE_SUCCESS;
}
/* }}} */
static inheritance_status zend_do_perform_arg_type_hint_check(
- zend_string **unresolved_class,
const zend_function *fe, zend_arg_info *fe_arg_info,
const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */
{
@@ -407,12 +508,12 @@ static inheritance_status zend_do_perform_arg_type_hint_check(
/* Contravariant type check is performed as a covariant type check with swapped
* argument order. */
return zend_perform_covariant_type_check(
- unresolved_class, proto, proto_arg_info, fe, fe_arg_info);
+ proto->common.scope, proto_arg_info->type, fe->common.scope, fe_arg_info->type);
}
/* }}} */
static inheritance_status zend_do_perform_implementation_check(
- zend_string **unresolved_class, const zend_function *fe, const zend_function *proto) /* {{{ */
+ const zend_function *fe, const zend_function *proto) /* {{{ */
{
uint32_t i, num_args;
inheritance_status status, local_status;
@@ -478,8 +579,7 @@ static inheritance_status zend_do_perform_implementation_check(
proto_arg_info = &proto->common.arg_info[proto->common.num_args];
}
- local_status = zend_do_perform_arg_type_hint_check(
- unresolved_class, fe, fe_arg_info, proto, proto_arg_info);
+ local_status = zend_do_perform_arg_type_hint_check(fe, fe_arg_info, proto, proto_arg_info);
if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) {
if (UNEXPECTED(local_status == INHERITANCE_ERROR)) {
@@ -504,7 +604,8 @@ static inheritance_status zend_do_perform_implementation_check(
}
local_status = zend_perform_covariant_type_check(
- unresolved_class, fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1);
+ fe->common.scope, fe->common.arg_info[-1].type,
+ proto->common.scope, proto->common.arg_info[-1].type);
if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) {
if (UNEXPECTED(local_status == INHERITANCE_ERROR)) {
@@ -663,10 +764,17 @@ static zend_always_inline uint32_t func_lineno(const zend_function *fn) {
static void ZEND_COLD emit_incompatible_method_error(
const zend_function *child, const zend_function *parent,
- inheritance_status status, zend_string *unresolved_class) {
+ inheritance_status status) {
zend_string *parent_prototype = zend_get_function_declaration(parent);
zend_string *child_prototype = zend_get_function_declaration(child);
if (status == INHERITANCE_UNRESOLVED) {
+ /* Fetch the first unresolved class from registered autoloads */
+ zend_string *unresolved_class = NULL;
+ ZEND_HASH_FOREACH_STR_KEY(CG(delayed_autoloads), unresolved_class) {
+ break;
+ } ZEND_HASH_FOREACH_END();
+ ZEND_ASSERT(unresolved_class);
+
zend_error_at(E_COMPILE_ERROR, NULL, func_lineno(child),
"Could not check compatibility between %s and %s, because class %s is not available",
ZSTR_VAL(child_prototype), ZSTR_VAL(parent_prototype), ZSTR_VAL(unresolved_class));
@@ -683,17 +791,13 @@ static void perform_delayable_implementation_check(
zend_class_entry *ce, const zend_function *fe,
const zend_function *proto)
{
- zend_string *unresolved_class;
- inheritance_status status = zend_do_perform_implementation_check(
- &unresolved_class, fe, proto);
-
+ inheritance_status status = zend_do_perform_implementation_check(fe, proto);
if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
if (EXPECTED(status == INHERITANCE_UNRESOLVED)) {
add_compatibility_obligation(ce, fe, proto);
} else {
ZEND_ASSERT(status == INHERITANCE_ERROR);
- emit_incompatible_method_error(
- fe, proto, status, unresolved_class);
+ emit_incompatible_method_error(fe, proto, status);
}
}
}
@@ -792,10 +896,7 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(z
if (!checked) {
if (check_only) {
- zend_string *unresolved_class;
-
- return zend_do_perform_implementation_check(
- &unresolved_class, child, parent);
+ return zend_do_perform_implementation_check(child, parent);
}
perform_delayable_implementation_check(ce, child, parent);
}
@@ -845,39 +946,28 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function
inheritance_status property_types_compatible(
const zend_property_info *parent_info, const zend_property_info *child_info) {
- zend_string *parent_name, *child_name;
- zend_class_entry *parent_type_ce, *child_type_ce;
if (ZEND_TYPE_PURE_MASK(parent_info->type) == ZEND_TYPE_PURE_MASK(child_info->type)
&& ZEND_TYPE_NAME(parent_info->type) == ZEND_TYPE_NAME(child_info->type)) {
return INHERITANCE_SUCCESS;
}
- if (!ZEND_TYPE_IS_CLASS(parent_info->type) || !ZEND_TYPE_IS_CLASS(child_info->type) ||
- ZEND_TYPE_ALLOW_NULL(parent_info->type) != ZEND_TYPE_ALLOW_NULL(child_info->type)) {
+ if (ZEND_TYPE_IS_SET(parent_info->type) != ZEND_TYPE_IS_SET(child_info->type)) {
return INHERITANCE_ERROR;
}
- parent_name = ZEND_TYPE_IS_CE(parent_info->type)
- ? ZEND_TYPE_CE(parent_info->type)->name
- : resolve_class_name(parent_info->ce, ZEND_TYPE_NAME(parent_info->type));
- child_name = ZEND_TYPE_IS_CE(child_info->type)
- ? ZEND_TYPE_CE(child_info->type)->name
- : resolve_class_name(child_info->ce, ZEND_TYPE_NAME(child_info->type));
- if (zend_string_equals_ci(parent_name, child_name)) {
+ /* Perform a covariant type check in both directions to determined invariance. */
+ inheritance_status status1 = zend_perform_covariant_type_check(
+ child_info->ce, child_info->type, parent_info->ce, parent_info->type);
+ inheritance_status status2 = zend_perform_covariant_type_check(
+ parent_info->ce, parent_info->type, child_info->ce, child_info->type);
+ if (status1 == INHERITANCE_SUCCESS && status2 == INHERITANCE_SUCCESS) {
return INHERITANCE_SUCCESS;
}
-
- /* Check for class aliases */
- parent_type_ce = ZEND_TYPE_IS_CE(parent_info->type)
- ? ZEND_TYPE_CE(parent_info->type)
- : lookup_class(parent_info->ce, parent_name);
- child_type_ce = ZEND_TYPE_IS_CE(child_info->type)
- ? ZEND_TYPE_CE(child_info->type)
- : lookup_class(child_info->ce, child_name);
- if (!parent_type_ce || !child_type_ce) {
- return INHERITANCE_UNRESOLVED;
+ if (status1 == INHERITANCE_ERROR || status2 == INHERITANCE_ERROR) {
+ return INHERITANCE_ERROR;
}
- return parent_type_ce == child_type_ce ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
+ ZEND_ASSERT(status1 == INHERITANCE_UNRESOLVED && status2 == INHERITANCE_UNRESOLVED);
+ return INHERITANCE_UNRESOLVED;
}
static void emit_incompatible_property_error(
@@ -1958,9 +2048,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
Z_TRY_ADDREF_P(prop_value);
doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL;
- if (ZEND_TYPE_IS_NAME(property_info->type)) {
- zend_string_addref(ZEND_TYPE_NAME(property_info->type));
- }
+ zend_type_copy_ctor(&property_info->type, /* persistent */ 0);
zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, property_info->type);
zend_string_release_ex(prop_name, 0);
} ZEND_HASH_FOREACH_END();
@@ -2225,16 +2313,14 @@ static int check_variance_obligation(zval *zv) {
return ZEND_HASH_APPLY_KEEP;
}
} else if (obligation->type == OBLIGATION_COMPATIBILITY) {
- zend_string *unresolved_class;
inheritance_status status = zend_do_perform_implementation_check(
- &unresolved_class, obligation->child_fn, obligation->parent_fn);
+ obligation->child_fn, obligation->parent_fn);
if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
if (EXPECTED(status == INHERITANCE_UNRESOLVED)) {
return ZEND_HASH_APPLY_KEEP;
}
ZEND_ASSERT(status == INHERITANCE_ERROR);
- emit_incompatible_method_error(
- obligation->child_fn, obligation->parent_fn, status, unresolved_class);
+ emit_incompatible_method_error(obligation->child_fn, obligation->parent_fn, status);
}
/* Either the compatibility check was successful or only threw a warning. */
} else {
@@ -2297,16 +2383,14 @@ static void report_variance_errors(zend_class_entry *ce) {
ZEND_ASSERT(obligations != NULL);
ZEND_HASH_FOREACH_PTR(obligations, obligation) {
- inheritance_status status;
- zend_string *unresolved_class;
-
if (obligation->type == OBLIGATION_COMPATIBILITY) {
- /* Just used to fetch the unresolved_class in this case. */
- status = zend_do_perform_implementation_check(
- &unresolved_class, obligation->child_fn, obligation->parent_fn);
+ /* Just used to populate the delayed_autoloads table,
+ * which will be used when printing the "unresolved" error. */
+ inheritance_status status = zend_do_perform_implementation_check(
+ obligation->child_fn, obligation->parent_fn);
ZEND_ASSERT(status == INHERITANCE_UNRESOLVED);
emit_incompatible_method_error(
- obligation->child_fn, obligation->parent_fn, status, unresolved_class);
+ obligation->child_fn, obligation->parent_fn, status);
} else if (obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY) {
emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop);
} else {