summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-10-15 12:46:07 +0200
committerNikita Popov <nikita.ppv@gmail.com>2020-10-15 14:24:25 +0200
commit4ece62fba9ff4ea5e500a329ae2c349f91771ff8 (patch)
treed074dc6269376e173e94d9f50cdc448ddc0745cb
parentecd51aa099854a85585671947f053b69d5b73ceb (diff)
downloadphp-git-4ece62fba9ff4ea5e500a329ae2c349f91771ff8.tar.gz
Fix bug #80055
We need to perform trait scope fixup for both methods involved in the inheritance check. For that purpose we already need to thread through a separate fn scope through the entire inheritance checking machinery.
-rw-r--r--NEWS4
-rw-r--r--Zend/tests/bug80055.phpt24
-rw-r--r--Zend/zend_inheritance.c95
3 files changed, 84 insertions, 39 deletions
diff --git a/NEWS b/NEWS
index b6cf08b3f2..481d67ddcb 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.0.0RC3
+- Core:
+ . Fixed bug #8055 (Abstract trait methods returning "self" cannot be
+ fulfilled by traits). (Nikita)
+
- IMAP:
. Fixed bug #80239 (imap_rfc822_write_address() leaks memory). (cmb)
diff --git a/Zend/tests/bug80055.phpt b/Zend/tests/bug80055.phpt
new file mode 100644
index 0000000000..f43ab490a3
--- /dev/null
+++ b/Zend/tests/bug80055.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Bug #80055: Abstract trait methods returning "self" cannot be fulfilled by traits
+--FILE--
+<?php
+
+trait AbstractTrait {
+ abstract public function selfReturner(): self;
+}
+
+trait ConcreteTrait {
+ public function selfReturner(): self {
+ return $this;
+ }
+}
+
+class Test {
+ use AbstractTrait;
+ use ConcreteTrait;
+}
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index cf4fdea45f..191126eed7 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -29,8 +29,8 @@
static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce);
static void add_compatibility_obligation(
- zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn,
- zend_class_entry *parent_scope);
+ zend_class_entry *ce, const zend_function *child_fn, zend_class_entry *child_scope,
+ const zend_function *parent_fn, zend_class_entry *parent_scope);
static void add_property_compatibility_obligation(
zend_class_entry *ce, const zend_property_info *child_prop,
const zend_property_info *parent_prop);
@@ -522,12 +522,12 @@ static inheritance_status zend_do_perform_arg_type_hint_check(
}
/* }}} */
-/* For abstract trait methods, proto_scope will differ from proto->common.scope,
+/* For trait methods, fe_scope/proto_scope may differ from fe/proto->common.scope,
* as self will refer to the self of the class the trait is used in, not the trait
* the method was declared in. */
static inheritance_status zend_do_perform_implementation_check(
- const zend_function *fe, const zend_function *proto,
- zend_class_entry *proto_scope) /* {{{ */
+ const zend_function *fe, zend_class_entry *fe_scope,
+ const zend_function *proto, zend_class_entry *proto_scope) /* {{{ */
{
uint32_t i, num_args, proto_num_args, fe_num_args;
inheritance_status status, local_status;
@@ -589,7 +589,7 @@ static inheritance_status zend_do_perform_implementation_check(
}
local_status = zend_do_perform_arg_type_hint_check(
- fe->common.scope, fe_arg_info, proto_scope, proto_arg_info);
+ fe_scope, fe_arg_info, proto_scope, proto_arg_info);
if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) {
if (UNEXPECTED(local_status == INHERITANCE_ERROR)) {
@@ -614,7 +614,7 @@ static inheritance_status zend_do_perform_implementation_check(
}
local_status = zend_perform_covariant_type_check(
- fe->common.scope, fe->common.arg_info[-1].type,
+ fe_scope, fe->common.arg_info[-1].type,
proto_scope, proto->common.arg_info[-1].type);
if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) {
@@ -778,10 +778,11 @@ 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, zend_class_entry *parent_scope,
+ const zend_function *child, zend_class_entry *child_scope,
+ const zend_function *parent, zend_class_entry *parent_scope,
inheritance_status status) {
zend_string *parent_prototype = zend_get_function_declaration(parent, parent_scope);
- zend_string *child_prototype = zend_get_function_declaration(child, child->common.scope);
+ zend_string *child_prototype = zend_get_function_declaration(child, child_scope);
if (status == INHERITANCE_UNRESOLVED) {
/* Fetch the first unresolved class from registered autoloads */
zend_string *unresolved_class = NULL;
@@ -803,23 +804,26 @@ static void ZEND_COLD emit_incompatible_method_error(
}
static void perform_delayable_implementation_check(
- zend_class_entry *ce, const zend_function *fe,
+ zend_class_entry *ce,
+ const zend_function *fe, zend_class_entry *fe_scope,
const zend_function *proto, zend_class_entry *proto_scope)
{
- inheritance_status status = zend_do_perform_implementation_check(fe, proto, proto_scope);
+ inheritance_status status =
+ zend_do_perform_implementation_check(fe, fe_scope, proto, proto_scope);
if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
if (EXPECTED(status == INHERITANCE_UNRESOLVED)) {
- add_compatibility_obligation(ce, fe, proto, proto_scope);
+ add_compatibility_obligation(ce, fe, fe_scope, proto, proto_scope);
} else {
ZEND_ASSERT(status == INHERITANCE_ERROR);
- emit_incompatible_method_error(fe, proto, proto_scope, status);
+ emit_incompatible_method_error(fe, fe_scope, proto, proto_scope, status);
}
}
}
static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
- zend_function *child, zend_function *parent,
- zend_class_entry *ce, zend_class_entry *parent_scope, zval *child_zv,
+ zend_function *child, zend_class_entry *child_scope,
+ zend_function *parent, zend_class_entry *parent_scope,
+ zend_class_entry *ce, zval *child_zv,
zend_bool check_visibility, zend_bool check_only, zend_bool checked) /* {{{ */
{
uint32_t child_flags;
@@ -919,22 +923,21 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
if (!checked) {
if (check_only) {
- return zend_do_perform_implementation_check(child, parent, parent_scope);
+ return zend_do_perform_implementation_check(child, child_scope, parent, parent_scope);
}
- perform_delayable_implementation_check(ce, child, parent, parent_scope);
+ perform_delayable_implementation_check(ce, child, child_scope, parent, parent_scope);
}
return INHERITANCE_SUCCESS;
}
/* }}} */
static zend_never_inline void do_inheritance_check_on_method(
- zend_function *child, zend_function *parent,
- zend_class_entry *ce, zend_class_entry *parent_scope,
- zval *child_zv, zend_bool check_visibility) /* {{{ */
+ zend_function *child, zend_class_entry *child_scope,
+ zend_function *parent, zend_class_entry *parent_scope,
+ zend_class_entry *ce, zval *child_zv, zend_bool check_visibility)
{
- do_inheritance_check_on_method_ex(child, parent, ce, parent_scope, child_zv, check_visibility, 0, 0);
+ do_inheritance_check_on_method_ex(child, child_scope, parent, parent_scope, ce, child_zv, check_visibility, 0, 0);
}
-/* }}} */
static zend_always_inline void do_inherit_method(zend_string *key, zend_function *parent, zend_class_entry *ce, zend_bool is_interface, zend_bool checked) /* {{{ */
{
@@ -950,11 +953,12 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function
if (checked) {
do_inheritance_check_on_method_ex(
- func, parent, ce, parent->common.scope, child,
+ func, func->common.scope, parent, parent->common.scope, ce, child,
/* check_visibility */ 1, 0, checked);
} else {
do_inheritance_check_on_method(
- func, parent, ce, parent->common.scope, child, /* check_visibility */ 1);
+ func, func->common.scope, parent, parent->common.scope, ce, child,
+ /* check_visibility */ 1);
}
} else {
@@ -1557,6 +1561,12 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry
}
/* }}} */
+static zend_class_entry *fixup_trait_scope(const zend_function *fn, zend_class_entry *ce)
+{
+ /* self in trait methods should be resolved to the using class, not the trait. */
+ return fn->common.scope->ce_flags & ZEND_ACC_TRAIT ? ce : fn->common.scope;
+}
+
static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_string *key, zend_function *fn) /* {{{ */
{
zend_function *existing_fn = NULL;
@@ -1577,12 +1587,10 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_
* As such, "abstract protected" was sometimes used to indicate trait requirements,
* even though the "implementing" method was private. Do not check visibility
* requirements to maintain backwards-compatibility with such usage.
- *
- * The parent_scope passed here is not fn->common.scope, because we want "self" to be
- * resolved against the using class, not the declaring trait.
*/
do_inheritance_check_on_method(
- existing_fn, fn, ce, /* parent_scope */ ce, NULL, /* check_visibility */ 0);
+ existing_fn, fixup_trait_scope(existing_fn, ce), fn, fixup_trait_scope(fn, ce),
+ ce, NULL, /* check_visibility */ 0);
return;
}
@@ -1597,10 +1605,11 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_
ZSTR_VAL(ce->name), ZSTR_VAL(name),
ZSTR_VAL(existing_fn->common.scope->name), ZSTR_VAL(existing_fn->common.function_name));
} else {
- /* inherited members are overridden by members inserted by traits */
- /* check whether the trait method fulfills the inheritance requirements */
+ /* Inherited members are overridden by members inserted by traits.
+ * Check whether the trait method fulfills the inheritance requirements. */
do_inheritance_check_on_method(
- fn, existing_fn, ce, existing_fn->common.scope, NULL, /* check_visibility */ 1);
+ fn, fixup_trait_scope(fn, ce), existing_fn, fixup_trait_scope(existing_fn, ce),
+ ce, NULL, /* check_visibility */ 1);
}
}
@@ -2186,6 +2195,7 @@ typedef struct {
* so use copies of functions here as well. */
zend_function parent_fn;
zend_function child_fn;
+ zend_class_entry *child_scope;
zend_class_entry *parent_scope;
};
struct {
@@ -2234,8 +2244,9 @@ static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *de
}
static void add_compatibility_obligation(
- zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn,
- zend_class_entry *parent_scope) {
+ zend_class_entry *ce,
+ const zend_function *child_fn, zend_class_entry *child_scope,
+ const zend_function *parent_fn, zend_class_entry *parent_scope) {
HashTable *obligations = get_or_init_obligations_for_class(ce);
variance_obligation *obligation = emalloc(sizeof(variance_obligation));
obligation->type = OBLIGATION_COMPATIBILITY;
@@ -2250,6 +2261,7 @@ static void add_compatibility_obligation(
} else {
memcpy(&obligation->parent_fn, parent_fn, sizeof(zend_op_array));
}
+ obligation->child_scope = child_scope;
obligation->parent_scope = parent_scope;
zend_hash_next_index_insert_ptr(obligations, obligation);
}
@@ -2279,14 +2291,16 @@ static int check_variance_obligation(zval *zv) {
}
} else if (obligation->type == OBLIGATION_COMPATIBILITY) {
inheritance_status status = zend_do_perform_implementation_check(
- &obligation->child_fn, &obligation->parent_fn, obligation->parent_scope);
+ &obligation->child_fn, obligation->child_scope,
+ &obligation->parent_fn, obligation->parent_scope);
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, obligation->parent_scope, status);
+ &obligation->child_fn, obligation->child_scope,
+ &obligation->parent_fn, obligation->parent_scope, status);
}
/* Either the compatibility check was successful or only threw a warning. */
} else {
@@ -2353,10 +2367,12 @@ static void report_variance_errors(zend_class_entry *ce) {
/* 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, obligation->parent_scope);
+ &obligation->child_fn, obligation->child_scope,
+ &obligation->parent_fn, obligation->parent_scope);
ZEND_ASSERT(status == INHERITANCE_UNRESOLVED);
emit_incompatible_method_error(
- &obligation->child_fn, &obligation->parent_fn, obligation->parent_scope, status);
+ &obligation->child_fn, obligation->child_scope,
+ &obligation->parent_fn, obligation->parent_scope, status);
} else if (obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY) {
emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop);
} else {
@@ -2482,8 +2498,9 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e
zend_function *child_func = Z_FUNC_P(zv);
inheritance_status status =
do_inheritance_check_on_method_ex(
- child_func, parent_func, ce, parent_func->common.scope, NULL,
- /* check_visibility */ 1, 1, 0);
+ child_func, child_func->common.scope,
+ parent_func, parent_func->common.scope,
+ ce, NULL, /* check_visibility */ 1, 1, 0);
if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
return status;