Skip to content

Commit 3d63044

Browse files
committed
Unify early exit status handling, add comments
1 parent 934a9a6 commit 3d63044

File tree

1 file changed

+34
-49
lines changed

1 file changed

+34
-49
lines changed

Zend/zend_inheritance.c

Lines changed: 34 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -403,9 +403,11 @@ static void track_class_dependency(zend_class_entry *ce, zend_string *class_name
403403
zend_hash_add_ptr(ht, class_name, ce);
404404
}
405405

406-
static inheritance_status zend_perform_intersection_covariant_class_type_check(
407-
zend_class_entry *proto_scope, zend_string *proto_class_name, zend_class_entry *proto_ce,
406+
/* Check whether any type in fe_type is a subtype of the proto class.
407+
* This is independently of whether fe_type is a union or intersection. */
408+
static inheritance_status zend_is_any_type_subtype_of_class(
408409
zend_class_entry *fe_scope, zend_type fe_type,
410+
zend_class_entry *proto_scope, zend_string *proto_class_name, zend_class_entry *proto_ce,
409411
bool register_unresolved)
410412
{
411413
bool have_unresolved = false;
@@ -451,7 +453,7 @@ static inheritance_status zend_perform_intersection_covariant_class_type_check(
451453
}
452454

453455
/* Check whether a single class proto type is a subtype of a potentially complex fe_type. */
454-
static inheritance_status zend_perform_covariant_class_type_check(
456+
static inheritance_status zend_is_class_subtype_of_type(
455457
zend_class_entry *fe_scope, zend_string *fe_class_name, zend_class_entry *fe_ce,
456458
zend_class_entry *proto_scope, zend_type proto_type) {
457459
bool have_unresolved = 0;
@@ -589,15 +591,16 @@ static inheritance_status zend_perform_covariant_type_check(
589591
}
590592

591593
zend_type *single_type;
592-
bool all_success = true;
594+
inheritance_status early_exit_status;
595+
bool have_unresolved = false;
593596

594-
/* If the child type is an intersection type then we need to loop over
595-
* the parents first. For intersection types, loop over the parent types first
596-
* as the child can add types, however none of them can be a supertype of
597-
* a parent type. */
598597
if (ZEND_TYPE_IS_INTERSECTION(fe_type)) {
599-
bool parent_union_has_unresolved = false;
600-
/* First try to check whether we can succeed without resolving anything */
598+
/* U_1&...&U_n < V_1&...&V_m if forall V_j. exists U_i. U_i < V_j.
599+
* U_1&...&U_n < V_1|...|V_m if exists V_j. exists U_i. U_i < V_j.
600+
* As such, we need to iterate over proto_type (V_j) first and use a different
601+
* quantifier depending on whether fe_type is a union or an intersection. */
602+
early_exit_status =
603+
ZEND_TYPE_IS_INTERSECTION(proto_type) ? INHERITANCE_ERROR : INHERITANCE_SUCCESS;
601604
ZEND_TYPE_FOREACH(proto_type, single_type) {
602605
inheritance_status status;
603606
zend_string *proto_class_name;
@@ -614,39 +617,22 @@ static inheritance_status zend_perform_covariant_type_check(
614617
continue;
615618
}
616619

617-
status = zend_perform_intersection_covariant_class_type_check(
618-
proto_scope, proto_class_name, proto_ce,
619-
fe_scope, fe_type, /* register_unresolved */ false);
620-
621-
/* If the parent is a union type then the intersection type must only be
622-
* a subtype of one of them */
623-
if (ZEND_TYPE_IS_UNION(proto_type)) {
624-
if (status == INHERITANCE_SUCCESS) {
625-
return INHERITANCE_SUCCESS;
626-
}
627-
if (status == INHERITANCE_UNRESOLVED) {
628-
all_success = false;
629-
}
630-
} else {
631-
if (status == INHERITANCE_ERROR) {
632-
return INHERITANCE_ERROR;
633-
}
634-
if (status != INHERITANCE_SUCCESS) {
635-
ZEND_ASSERT(status == INHERITANCE_UNRESOLVED);
636-
parent_union_has_unresolved = true;
637-
all_success = false;
638-
}
620+
status = zend_is_any_type_subtype_of_class(
621+
fe_scope, fe_type, proto_scope, proto_class_name, proto_ce,
622+
/* register_unresolved */ false);
623+
if (status == early_exit_status) {
624+
return status;
625+
}
626+
if (status == INHERITANCE_UNRESOLVED) {
627+
have_unresolved = true;
639628
}
640629
} ZEND_TYPE_FOREACH_END();
641-
642-
/* Reaching this means either the child intersection type is a valid/unresolved
643-
* subtype of a parent single/intersection type, either it is an INvalid subtype
644-
* when the parent is a union or it is unresolved, we check which case this is */
645-
if (ZEND_TYPE_IS_UNION(proto_type) && !parent_union_has_unresolved) {
646-
return INHERITANCE_ERROR;
647-
}
648630
} else {
649-
/* First try to check whether we can succeed without resolving anything */
631+
/* U_1|...|U_n < V_1|...|V_m if forall U_i. exists V_j. U_i < V_j.
632+
* U_1|...|U_n < V_1&...&V_m if forall U_i. forall V_j. U_i < V_j.
633+
* We need to iterate over fe_type (U_i) first and the logic is independent of
634+
* whether proto_type is a union or intersection (only the inner check differs). */
635+
early_exit_status = INHERITANCE_ERROR;
650636
ZEND_TYPE_FOREACH(fe_type, single_type) {
651637
inheritance_status status;
652638
zend_string *fe_class_name;
@@ -661,21 +647,20 @@ static inheritance_status zend_perform_covariant_type_check(
661647
continue;
662648
}
663649

664-
status = zend_perform_covariant_class_type_check(fe_scope,
665-
fe_class_name, fe_ce, proto_scope, proto_type,
650+
status = zend_is_class_subtype_of_type(
651+
fe_scope, fe_class_name, fe_ce, proto_scope, proto_type,
666652
/* register_unresolved */ false);
667-
if (status == INHERITANCE_ERROR) {
668-
return INHERITANCE_ERROR;
653+
if (status == early_exit_status) {
654+
return early_exit_status;
669655
}
670-
if (status != INHERITANCE_SUCCESS) {
671-
all_success = 0;
656+
if (status == INHERITANCE_UNRESOLVED) {
657+
have_unresolved = true;
672658
}
673659
} ZEND_TYPE_FOREACH_END();
674660
}
675661

676-
/* All individual checks succeeded, overall success */
677-
if (all_success) {
678-
return INHERITANCE_SUCCESS;
662+
if (!have_unresolved) {
663+
return early_exit_status == INHERITANCE_ERROR ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
679664
}
680665

681666
register_unresolved_classes(fe_scope, fe_type);

0 commit comments

Comments
 (0)