Skip to content

Commit a9b986a

Browse files
committed
Merge branch 'PHP-8.0'
* PHP-8.0: Fix bug #80055
2 parents f5b2ded + 4ece62f commit a9b986a

File tree

2 files changed

+80
-39
lines changed

2 files changed

+80
-39
lines changed

Zend/tests/bug80055.phpt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
Bug #80055: Abstract trait methods returning "self" cannot be fulfilled by traits
3+
--FILE--
4+
<?php
5+
6+
trait AbstractTrait {
7+
abstract public function selfReturner(): self;
8+
}
9+
10+
trait ConcreteTrait {
11+
public function selfReturner(): self {
12+
return $this;
13+
}
14+
}
15+
16+
class Test {
17+
use AbstractTrait;
18+
use ConcreteTrait;
19+
}
20+
21+
?>
22+
===DONE===
23+
--EXPECT--
24+
===DONE===

Zend/zend_inheritance.c

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929

3030
static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce);
3131
static void add_compatibility_obligation(
32-
zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn,
33-
zend_class_entry *parent_scope);
32+
zend_class_entry *ce, const zend_function *child_fn, zend_class_entry *child_scope,
33+
const zend_function *parent_fn, zend_class_entry *parent_scope);
3434
static void add_property_compatibility_obligation(
3535
zend_class_entry *ce, const zend_property_info *child_prop,
3636
const zend_property_info *parent_prop);
@@ -522,12 +522,12 @@ static inheritance_status zend_do_perform_arg_type_hint_check(
522522
}
523523
/* }}} */
524524

525-
/* For abstract trait methods, proto_scope will differ from proto->common.scope,
525+
/* For trait methods, fe_scope/proto_scope may differ from fe/proto->common.scope,
526526
* as self will refer to the self of the class the trait is used in, not the trait
527527
* the method was declared in. */
528528
static inheritance_status zend_do_perform_implementation_check(
529-
const zend_function *fe, const zend_function *proto,
530-
zend_class_entry *proto_scope) /* {{{ */
529+
const zend_function *fe, zend_class_entry *fe_scope,
530+
const zend_function *proto, zend_class_entry *proto_scope) /* {{{ */
531531
{
532532
uint32_t i, num_args, proto_num_args, fe_num_args;
533533
inheritance_status status, local_status;
@@ -589,7 +589,7 @@ static inheritance_status zend_do_perform_implementation_check(
589589
}
590590

591591
local_status = zend_do_perform_arg_type_hint_check(
592-
fe->common.scope, fe_arg_info, proto_scope, proto_arg_info);
592+
fe_scope, fe_arg_info, proto_scope, proto_arg_info);
593593

594594
if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) {
595595
if (UNEXPECTED(local_status == INHERITANCE_ERROR)) {
@@ -614,7 +614,7 @@ static inheritance_status zend_do_perform_implementation_check(
614614
}
615615

616616
local_status = zend_perform_covariant_type_check(
617-
fe->common.scope, fe->common.arg_info[-1].type,
617+
fe_scope, fe->common.arg_info[-1].type,
618618
proto_scope, proto->common.arg_info[-1].type);
619619

620620
if (UNEXPECTED(local_status != INHERITANCE_SUCCESS)) {
@@ -778,10 +778,11 @@ static zend_always_inline uint32_t func_lineno(const zend_function *fn) {
778778
}
779779

780780
static void ZEND_COLD emit_incompatible_method_error(
781-
const zend_function *child, const zend_function *parent, zend_class_entry *parent_scope,
781+
const zend_function *child, zend_class_entry *child_scope,
782+
const zend_function *parent, zend_class_entry *parent_scope,
782783
inheritance_status status) {
783784
zend_string *parent_prototype = zend_get_function_declaration(parent, parent_scope);
784-
zend_string *child_prototype = zend_get_function_declaration(child, child->common.scope);
785+
zend_string *child_prototype = zend_get_function_declaration(child, child_scope);
785786
if (status == INHERITANCE_UNRESOLVED) {
786787
/* Fetch the first unresolved class from registered autoloads */
787788
zend_string *unresolved_class = NULL;
@@ -803,23 +804,26 @@ static void ZEND_COLD emit_incompatible_method_error(
803804
}
804805

805806
static void perform_delayable_implementation_check(
806-
zend_class_entry *ce, const zend_function *fe,
807+
zend_class_entry *ce,
808+
const zend_function *fe, zend_class_entry *fe_scope,
807809
const zend_function *proto, zend_class_entry *proto_scope)
808810
{
809-
inheritance_status status = zend_do_perform_implementation_check(fe, proto, proto_scope);
811+
inheritance_status status =
812+
zend_do_perform_implementation_check(fe, fe_scope, proto, proto_scope);
810813
if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
811814
if (EXPECTED(status == INHERITANCE_UNRESOLVED)) {
812-
add_compatibility_obligation(ce, fe, proto, proto_scope);
815+
add_compatibility_obligation(ce, fe, fe_scope, proto, proto_scope);
813816
} else {
814817
ZEND_ASSERT(status == INHERITANCE_ERROR);
815-
emit_incompatible_method_error(fe, proto, proto_scope, status);
818+
emit_incompatible_method_error(fe, fe_scope, proto, proto_scope, status);
816819
}
817820
}
818821
}
819822

820823
static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
821-
zend_function *child, zend_function *parent,
822-
zend_class_entry *ce, zend_class_entry *parent_scope, zval *child_zv,
824+
zend_function *child, zend_class_entry *child_scope,
825+
zend_function *parent, zend_class_entry *parent_scope,
826+
zend_class_entry *ce, zval *child_zv,
823827
zend_bool check_visibility, zend_bool check_only, zend_bool checked) /* {{{ */
824828
{
825829
uint32_t child_flags;
@@ -919,22 +923,21 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(
919923

920924
if (!checked) {
921925
if (check_only) {
922-
return zend_do_perform_implementation_check(child, parent, parent_scope);
926+
return zend_do_perform_implementation_check(child, child_scope, parent, parent_scope);
923927
}
924-
perform_delayable_implementation_check(ce, child, parent, parent_scope);
928+
perform_delayable_implementation_check(ce, child, child_scope, parent, parent_scope);
925929
}
926930
return INHERITANCE_SUCCESS;
927931
}
928932
/* }}} */
929933

930934
static zend_never_inline void do_inheritance_check_on_method(
931-
zend_function *child, zend_function *parent,
932-
zend_class_entry *ce, zend_class_entry *parent_scope,
933-
zval *child_zv, zend_bool check_visibility) /* {{{ */
935+
zend_function *child, zend_class_entry *child_scope,
936+
zend_function *parent, zend_class_entry *parent_scope,
937+
zend_class_entry *ce, zval *child_zv, zend_bool check_visibility)
934938
{
935-
do_inheritance_check_on_method_ex(child, parent, ce, parent_scope, child_zv, check_visibility, 0, 0);
939+
do_inheritance_check_on_method_ex(child, child_scope, parent, parent_scope, ce, child_zv, check_visibility, 0, 0);
936940
}
937-
/* }}} */
938941

939942
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) /* {{{ */
940943
{
@@ -950,11 +953,12 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function
950953

951954
if (checked) {
952955
do_inheritance_check_on_method_ex(
953-
func, parent, ce, parent->common.scope, child,
956+
func, func->common.scope, parent, parent->common.scope, ce, child,
954957
/* check_visibility */ 1, 0, checked);
955958
} else {
956959
do_inheritance_check_on_method(
957-
func, parent, ce, parent->common.scope, child, /* check_visibility */ 1);
960+
func, func->common.scope, parent, parent->common.scope, ce, child,
961+
/* check_visibility */ 1);
958962
}
959963
} else {
960964

@@ -1557,6 +1561,12 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry
15571561
}
15581562
/* }}} */
15591563

1564+
static zend_class_entry *fixup_trait_scope(const zend_function *fn, zend_class_entry *ce)
1565+
{
1566+
/* self in trait methods should be resolved to the using class, not the trait. */
1567+
return fn->common.scope->ce_flags & ZEND_ACC_TRAIT ? ce : fn->common.scope;
1568+
}
1569+
15601570
static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_string *key, zend_function *fn) /* {{{ */
15611571
{
15621572
zend_function *existing_fn = NULL;
@@ -1577,12 +1587,10 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_
15771587
* As such, "abstract protected" was sometimes used to indicate trait requirements,
15781588
* even though the "implementing" method was private. Do not check visibility
15791589
* requirements to maintain backwards-compatibility with such usage.
1580-
*
1581-
* The parent_scope passed here is not fn->common.scope, because we want "self" to be
1582-
* resolved against the using class, not the declaring trait.
15831590
*/
15841591
do_inheritance_check_on_method(
1585-
existing_fn, fn, ce, /* parent_scope */ ce, NULL, /* check_visibility */ 0);
1592+
existing_fn, fixup_trait_scope(existing_fn, ce), fn, fixup_trait_scope(fn, ce),
1593+
ce, NULL, /* check_visibility */ 0);
15861594
return;
15871595
}
15881596

@@ -1597,10 +1605,11 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_
15971605
ZSTR_VAL(ce->name), ZSTR_VAL(name),
15981606
ZSTR_VAL(existing_fn->common.scope->name), ZSTR_VAL(existing_fn->common.function_name));
15991607
} else {
1600-
/* inherited members are overridden by members inserted by traits */
1601-
/* check whether the trait method fulfills the inheritance requirements */
1608+
/* Inherited members are overridden by members inserted by traits.
1609+
* Check whether the trait method fulfills the inheritance requirements. */
16021610
do_inheritance_check_on_method(
1603-
fn, existing_fn, ce, existing_fn->common.scope, NULL, /* check_visibility */ 1);
1611+
fn, fixup_trait_scope(fn, ce), existing_fn, fixup_trait_scope(existing_fn, ce),
1612+
ce, NULL, /* check_visibility */ 1);
16041613
}
16051614
}
16061615

@@ -2186,6 +2195,7 @@ typedef struct {
21862195
* so use copies of functions here as well. */
21872196
zend_function parent_fn;
21882197
zend_function child_fn;
2198+
zend_class_entry *child_scope;
21892199
zend_class_entry *parent_scope;
21902200
};
21912201
struct {
@@ -2234,8 +2244,9 @@ static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *de
22342244
}
22352245

22362246
static void add_compatibility_obligation(
2237-
zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn,
2238-
zend_class_entry *parent_scope) {
2247+
zend_class_entry *ce,
2248+
const zend_function *child_fn, zend_class_entry *child_scope,
2249+
const zend_function *parent_fn, zend_class_entry *parent_scope) {
22392250
HashTable *obligations = get_or_init_obligations_for_class(ce);
22402251
variance_obligation *obligation = emalloc(sizeof(variance_obligation));
22412252
obligation->type = OBLIGATION_COMPATIBILITY;
@@ -2250,6 +2261,7 @@ static void add_compatibility_obligation(
22502261
} else {
22512262
memcpy(&obligation->parent_fn, parent_fn, sizeof(zend_op_array));
22522263
}
2264+
obligation->child_scope = child_scope;
22532265
obligation->parent_scope = parent_scope;
22542266
zend_hash_next_index_insert_ptr(obligations, obligation);
22552267
}
@@ -2279,14 +2291,16 @@ static int check_variance_obligation(zval *zv) {
22792291
}
22802292
} else if (obligation->type == OBLIGATION_COMPATIBILITY) {
22812293
inheritance_status status = zend_do_perform_implementation_check(
2282-
&obligation->child_fn, &obligation->parent_fn, obligation->parent_scope);
2294+
&obligation->child_fn, obligation->child_scope,
2295+
&obligation->parent_fn, obligation->parent_scope);
22832296
if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
22842297
if (EXPECTED(status == INHERITANCE_UNRESOLVED)) {
22852298
return ZEND_HASH_APPLY_KEEP;
22862299
}
22872300
ZEND_ASSERT(status == INHERITANCE_ERROR);
22882301
emit_incompatible_method_error(
2289-
&obligation->child_fn, &obligation->parent_fn, obligation->parent_scope, status);
2302+
&obligation->child_fn, obligation->child_scope,
2303+
&obligation->parent_fn, obligation->parent_scope, status);
22902304
}
22912305
/* Either the compatibility check was successful or only threw a warning. */
22922306
} else {
@@ -2353,10 +2367,12 @@ static void report_variance_errors(zend_class_entry *ce) {
23532367
/* Just used to populate the delayed_autoloads table,
23542368
* which will be used when printing the "unresolved" error. */
23552369
inheritance_status status = zend_do_perform_implementation_check(
2356-
&obligation->child_fn, &obligation->parent_fn, obligation->parent_scope);
2370+
&obligation->child_fn, obligation->child_scope,
2371+
&obligation->parent_fn, obligation->parent_scope);
23572372
ZEND_ASSERT(status == INHERITANCE_UNRESOLVED);
23582373
emit_incompatible_method_error(
2359-
&obligation->child_fn, &obligation->parent_fn, obligation->parent_scope, status);
2374+
&obligation->child_fn, obligation->child_scope,
2375+
&obligation->parent_fn, obligation->parent_scope, status);
23602376
} else if (obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY) {
23612377
emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop);
23622378
} else {
@@ -2482,8 +2498,9 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e
24822498
zend_function *child_func = Z_FUNC_P(zv);
24832499
inheritance_status status =
24842500
do_inheritance_check_on_method_ex(
2485-
child_func, parent_func, ce, parent_func->common.scope, NULL,
2486-
/* check_visibility */ 1, 1, 0);
2501+
child_func, child_func->common.scope,
2502+
parent_func, parent_func->common.scope,
2503+
ce, NULL, /* check_visibility */ 1, 1, 0);
24872504

24882505
if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
24892506
return status;

0 commit comments

Comments
 (0)