@@ -403,9 +403,11 @@ static void track_class_dependency(zend_class_entry *ce, zend_string *class_name
403
403
zend_hash_add_ptr (ht , class_name , ce );
404
404
}
405
405
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 (
408
409
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 ,
409
411
bool register_unresolved )
410
412
{
411
413
bool have_unresolved = false;
@@ -451,7 +453,7 @@ static inheritance_status zend_perform_intersection_covariant_class_type_check(
451
453
}
452
454
453
455
/* 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 (
455
457
zend_class_entry * fe_scope , zend_string * fe_class_name , zend_class_entry * fe_ce ,
456
458
zend_class_entry * proto_scope , zend_type proto_type ) {
457
459
bool have_unresolved = 0 ;
@@ -589,15 +591,16 @@ static inheritance_status zend_perform_covariant_type_check(
589
591
}
590
592
591
593
zend_type * single_type ;
592
- bool all_success = true;
594
+ inheritance_status early_exit_status ;
595
+ bool have_unresolved = false;
593
596
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. */
598
597
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 ;
601
604
ZEND_TYPE_FOREACH (proto_type , single_type ) {
602
605
inheritance_status status ;
603
606
zend_string * proto_class_name ;
@@ -614,39 +617,22 @@ static inheritance_status zend_perform_covariant_type_check(
614
617
continue ;
615
618
}
616
619
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;
639
628
}
640
629
} 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
- }
648
630
} 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 ;
650
636
ZEND_TYPE_FOREACH (fe_type , single_type ) {
651
637
inheritance_status status ;
652
638
zend_string * fe_class_name ;
@@ -661,21 +647,20 @@ static inheritance_status zend_perform_covariant_type_check(
661
647
continue ;
662
648
}
663
649
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 ,
666
652
/* register_unresolved */ false);
667
- if (status == INHERITANCE_ERROR ) {
668
- return INHERITANCE_ERROR ;
653
+ if (status == early_exit_status ) {
654
+ return early_exit_status ;
669
655
}
670
- if (status != INHERITANCE_SUCCESS ) {
671
- all_success = 0 ;
656
+ if (status == INHERITANCE_UNRESOLVED ) {
657
+ have_unresolved = true ;
672
658
}
673
659
} ZEND_TYPE_FOREACH_END ();
674
660
}
675
661
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 ;
679
664
}
680
665
681
666
register_unresolved_classes (fe_scope , fe_type );
0 commit comments