Skip to content

Commit e732440

Browse files
committed
Handle object/iterable, fixup rebase
I think this is still not quite correct :/
1 parent eebd857 commit e732440

File tree

5 files changed

+112
-44
lines changed

5 files changed

+112
-44
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Replacing object type with not-loadable intersection type
3+
--FILE--
4+
<?php
5+
6+
7+
class Test {
8+
function method(): object {}
9+
}
10+
class Test2 extends Test {
11+
function method(): X&Y {}
12+
}
13+
14+
?>
15+
===DONE===
16+
--EXPECTF--
17+
Fatal error: Could not check compatibility between Test2::method(): X&Y and Test::method(): object, because class X is not available in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Replacing iterable type with non-Traversable intersection type
3+
--FILE--
4+
<?php
5+
6+
interface X {}
7+
interface Y {}
8+
9+
class Test {
10+
function method(): iterable {}
11+
}
12+
class Test2 extends Test {
13+
function method(): X&Y {}
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: Declaration of Test2::method(): X&Y must be compatible with Test::method(): iterable in %s on line %d

Zend/tests/type_declarations/intersection_types/variance/valid7.phpt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@ Replacing object type with intersection type
33
--FILE--
44
<?php
55

6-
interface X {}
6+
// It's sufficient that Y is loadable.
77
interface Y {}
88

99
class Test {
1010
function method(): object {}
11+
function method2(): object|int {}
12+
function method3(): Y|int {}
1113
}
1214
class Test2 extends Test {
1315
function method(): X&Y {}
16+
function method2(): X&Y {}
17+
function method3(): X&Y {}
1418
}
1519

1620
?>
1721
===DONE===
18-
--EXPECTF--
19-
TODO
22+
--EXPECT--
23+
===DONE===
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Replacing iterable type with intersection type
3+
--FILE--
4+
<?php
5+
6+
abstract class MyIterator implements Traversable {}
7+
8+
class Test {
9+
function method(): iterable {}
10+
function method2(): iterable|int {}
11+
function method3(): iterable|Z {}
12+
}
13+
class Test2 extends Test {
14+
function method(): X&Traversable {}
15+
function method2(): X&MyIterator {}
16+
function method3(): X&Z {}
17+
}
18+
19+
?>
20+
===DONE===
21+
--EXPECT--
22+
===DONE===

Zend/zend_inheritance.c

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,7 @@ static void track_class_dependency(zend_class_entry *ce, zend_string *class_name
406406
/* Check whether any type in the fe_type intersection type is a subtype of the proto class. */
407407
static inheritance_status zend_is_intersection_subtype_of_class(
408408
zend_class_entry *fe_scope, zend_type fe_type,
409-
zend_class_entry *proto_scope, zend_string *proto_class_name, zend_class_entry *proto_ce,
410-
bool register_unresolved)
409+
zend_class_entry *proto_scope, zend_string *proto_class_name, zend_class_entry *proto_ce)
411410
{
412411
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(fe_type));
413412
bool have_unresolved = false;
@@ -425,11 +424,10 @@ static inheritance_status zend_is_intersection_subtype_of_class(
425424
return INHERITANCE_SUCCESS;
426425
}
427426

428-
if (!proto_ce) proto_ce = lookup_class(proto_scope, proto_class_name, register_unresolved);
429-
fe_ce =
430-
lookup_class(fe_scope, fe_class_name, register_unresolved);
427+
if (!proto_ce) proto_ce = lookup_class(proto_scope, proto_class_name);
428+
fe_ce = lookup_class(fe_scope, fe_class_name);
431429
} else if (ZEND_TYPE_HAS_CE(*single_type)) {
432-
if (!proto_ce) proto_ce = lookup_class(proto_scope, proto_class_name, register_unresolved);
430+
if (!proto_ce) proto_ce = lookup_class(proto_scope, proto_class_name);
433431
fe_ce = ZEND_TYPE_CE(*single_type);
434432
} else {
435433
/* standard type in an intersection type is impossible,
@@ -533,6 +531,19 @@ static inheritance_status zend_is_class_subtype_of_type(
533531
return is_intersection ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
534532
}
535533

534+
static zend_string *get_class_from_type(
535+
zend_class_entry **ce, zend_class_entry *scope, zend_type single_type) {
536+
if (ZEND_TYPE_HAS_NAME(single_type)) {
537+
*ce = NULL;
538+
return resolve_class_name(scope, ZEND_TYPE_NAME(single_type));
539+
}
540+
if (ZEND_TYPE_HAS_CE(single_type)) {
541+
*ce = ZEND_TYPE_CE(single_type);
542+
return (*ce)->name;
543+
}
544+
return NULL;
545+
}
546+
536547
static void register_unresolved_classes(zend_class_entry *scope, zend_type type) {
537548
zend_type *single_type;
538549
ZEND_TYPE_FOREACH(type, single_type) {
@@ -595,15 +606,26 @@ static inheritance_status zend_perform_covariant_type_check(
595606
bool have_unresolved = false;
596607

597608
if (ZEND_TYPE_IS_INTERSECTION(fe_type)) {
598-
if (proto_type_mask & MAY_BE_OBJECT) {
599-
/* TODO We can't just return success here, because the class must be loaded. */
600-
}
601-
if (proto_type_mask & MAY_BE_ITERABLE) {
602-
/* TODO */
603-
}
604-
if (proto_type_mask) {
605-
/* An intersection type cannot be a subtype of other builtin types. */
606-
return INHERITANCE_ERROR;
609+
if (proto_type_mask & (MAY_BE_OBJECT|MAY_BE_ITERABLE)) {
610+
bool any_class = (proto_type_mask & MAY_BE_OBJECT) != 0;
611+
ZEND_TYPE_FOREACH(fe_type, single_type) {
612+
zend_class_entry *fe_ce;
613+
zend_string *fe_class_name = get_class_from_type(&fe_ce, fe_scope, *single_type);
614+
if (!fe_class_name) {
615+
continue;
616+
}
617+
if (!fe_ce) {
618+
fe_ce = lookup_class(fe_scope, fe_class_name);
619+
}
620+
if (fe_ce) {
621+
if (any_class || unlinked_instanceof(fe_ce, zend_ce_traversable)) {
622+
track_class_dependency(fe_ce, fe_class_name);
623+
return INHERITANCE_SUCCESS;
624+
}
625+
} else {
626+
have_unresolved = true;
627+
}
628+
} ZEND_TYPE_FOREACH_END();
607629
}
608630

609631
/* U_1&...&U_n < V_1&...&V_m if forall V_j. exists U_i. U_i < V_j.
@@ -613,22 +635,15 @@ static inheritance_status zend_perform_covariant_type_check(
613635
early_exit_status =
614636
ZEND_TYPE_IS_INTERSECTION(proto_type) ? INHERITANCE_ERROR : INHERITANCE_SUCCESS;
615637
ZEND_TYPE_FOREACH(proto_type, single_type) {
616-
inheritance_status status;
617-
zend_string *proto_class_name;
618-
zend_class_entry *proto_ce = NULL;
619-
620-
if (ZEND_TYPE_HAS_NAME(*single_type)) {
621-
proto_class_name = resolve_class_name(proto_scope, ZEND_TYPE_NAME(*single_type));
622-
} else if (ZEND_TYPE_HAS_CE(*single_type)) {
623-
proto_ce = ZEND_TYPE_CE(*single_type);
624-
proto_class_name = proto_ce->name;
625-
} else {
638+
zend_class_entry *proto_ce;
639+
zend_string *proto_class_name =
640+
get_class_from_type(&proto_ce, proto_scope, *single_type);
641+
if (!proto_class_name) {
626642
continue;
627643
}
628644

629-
status = zend_is_intersection_subtype_of_class(
630-
fe_scope, fe_type, proto_scope, proto_class_name, proto_ce,
631-
/* register_unresolved */ false);
645+
inheritance_status status = zend_is_intersection_subtype_of_class(
646+
fe_scope, fe_type, proto_scope, proto_class_name, proto_ce);
632647
if (status == early_exit_status) {
633648
return status;
634649
}
@@ -643,22 +658,14 @@ static inheritance_status zend_perform_covariant_type_check(
643658
* whether proto_type is a union or intersection (only the inner check differs). */
644659
early_exit_status = INHERITANCE_ERROR;
645660
ZEND_TYPE_FOREACH(fe_type, single_type) {
646-
inheritance_status status;
647-
zend_string *fe_class_name;
648-
zend_class_entry *fe_ce = NULL;
649-
650-
if (ZEND_TYPE_HAS_NAME(*single_type)) {
651-
fe_class_name = resolve_class_name(fe_scope, ZEND_TYPE_NAME(*single_type));
652-
} else if (ZEND_TYPE_HAS_CE(*single_type)) {
653-
fe_ce = ZEND_TYPE_CE(*single_type);
654-
fe_class_name = fe_ce->name;
655-
} else {
661+
zend_class_entry *fe_ce;
662+
zend_string *fe_class_name = get_class_from_type(&fe_ce, fe_scope, *single_type);
663+
if (!fe_class_name) {
656664
continue;
657665
}
658666

659-
status = zend_is_class_subtype_of_type(
660-
fe_scope, fe_class_name, fe_ce, proto_scope, proto_type,
661-
/* register_unresolved */ false);
667+
inheritance_status status = zend_is_class_subtype_of_type(
668+
fe_scope, fe_class_name, fe_ce, proto_scope, proto_type);
662669
if (status == early_exit_status) {
663670
return early_exit_status;
664671
}

0 commit comments

Comments
 (0)