@@ -399,9 +399,11 @@ static void track_class_dependency(zend_class_entry *ce, zend_string *class_name
399
399
zend_hash_add_ptr (ht , class_name , ce );
400
400
}
401
401
402
- static inheritance_status zend_perform_intersection_covariant_class_type_check (
403
- zend_class_entry * proto_scope , zend_string * proto_class_name , zend_class_entry * proto_ce ,
402
+ /* Check whether any type in fe_type is a subtype of the proto class.
403
+ * This is independently of whether fe_type is a union or intersection. */
404
+ static inheritance_status zend_is_any_type_subtype_of_class (
404
405
zend_class_entry * fe_scope , zend_type fe_type ,
406
+ zend_class_entry * proto_scope , zend_string * proto_class_name , zend_class_entry * proto_ce ,
405
407
bool register_unresolved )
406
408
{
407
409
bool have_unresolved = false;
@@ -447,7 +449,7 @@ static inheritance_status zend_perform_intersection_covariant_class_type_check(
447
449
}
448
450
449
451
/* Check whether a single class proto type is a subtype of a potentially complex fe_type. */
450
- static inheritance_status zend_perform_covariant_class_type_check (
452
+ static inheritance_status zend_is_class_subtype_of_type (
451
453
zend_class_entry * fe_scope , zend_string * fe_class_name , zend_class_entry * fe_ce ,
452
454
zend_class_entry * proto_scope , zend_type proto_type ,
453
455
bool register_unresolved ) {
@@ -577,15 +579,16 @@ static inheritance_status zend_perform_covariant_type_check(
577
579
}
578
580
579
581
zend_type * single_type ;
580
- bool all_success = true;
582
+ inheritance_status early_exit_status ;
583
+ bool have_unresolved = false;
581
584
582
- /* If the child type is an intersection type then we need to loop over
583
- * the parents first. For intersection types, loop over the parent types first
584
- * as the child can add types, however none of them can be a supertype of
585
- * a parent type. */
586
585
if (ZEND_TYPE_IS_INTERSECTION (fe_type )) {
587
- bool parent_union_has_unresolved = false;
588
- /* First try to check whether we can succeed without resolving anything */
586
+ /* U_1&...&U_n < V_1&...&V_m if forall V_j. exists U_i. U_i < V_j.
587
+ * U_1&...&U_n < V_1|...|V_m if exists V_j. exists U_i. U_i < V_j.
588
+ * As such, we need to iterate over proto_type (V_j) first and use a different
589
+ * quantifier depending on whether fe_type is a union or an intersection. */
590
+ early_exit_status =
591
+ ZEND_TYPE_IS_INTERSECTION (proto_type ) ? INHERITANCE_ERROR : INHERITANCE_SUCCESS ;
589
592
ZEND_TYPE_FOREACH (proto_type , single_type ) {
590
593
inheritance_status status ;
591
594
zend_string * proto_class_name ;
@@ -602,39 +605,22 @@ static inheritance_status zend_perform_covariant_type_check(
602
605
continue ;
603
606
}
604
607
605
- status = zend_perform_intersection_covariant_class_type_check (
606
- proto_scope , proto_class_name , proto_ce ,
607
- fe_scope , fe_type , /* register_unresolved */ false);
608
-
609
- /* If the parent is a union type then the intersection type must only be
610
- * a subtype of one of them */
611
- if (ZEND_TYPE_IS_UNION (proto_type )) {
612
- if (status == INHERITANCE_SUCCESS ) {
613
- return INHERITANCE_SUCCESS ;
614
- }
615
- if (status == INHERITANCE_UNRESOLVED ) {
616
- all_success = false;
617
- }
618
- } else {
619
- if (status == INHERITANCE_ERROR ) {
620
- return INHERITANCE_ERROR ;
621
- }
622
- if (status != INHERITANCE_SUCCESS ) {
623
- ZEND_ASSERT (status == INHERITANCE_UNRESOLVED );
624
- parent_union_has_unresolved = true;
625
- all_success = false;
626
- }
608
+ status = zend_is_any_type_subtype_of_class (
609
+ fe_scope , fe_type , proto_scope , proto_class_name , proto_ce ,
610
+ /* register_unresolved */ false);
611
+ if (status == early_exit_status ) {
612
+ return status ;
613
+ }
614
+ if (status == INHERITANCE_UNRESOLVED ) {
615
+ have_unresolved = true;
627
616
}
628
617
} ZEND_TYPE_FOREACH_END ();
629
-
630
- /* Reaching this means either the child intersection type is a valid/unresolved
631
- * subtype of a parent single/intersection type, either it is an INvalid subtype
632
- * when the parent is a union or it is unresolved, we check which case this is */
633
- if (ZEND_TYPE_IS_UNION (proto_type ) && !parent_union_has_unresolved ) {
634
- return INHERITANCE_ERROR ;
635
- }
636
618
} else {
637
- /* First try to check whether we can succeed without resolving anything */
619
+ /* U_1|...|U_n < V_1|...|V_m if forall U_i. exists V_j. U_i < V_j.
620
+ * U_1|...|U_n < V_1&...&V_m if forall U_i. forall V_j. U_i < V_j.
621
+ * We need to iterate over fe_type (U_i) first and the logic is independent of
622
+ * whether proto_type is a union or intersection (only the inner check differs). */
623
+ early_exit_status = INHERITANCE_ERROR ;
638
624
ZEND_TYPE_FOREACH (fe_type , single_type ) {
639
625
inheritance_status status ;
640
626
zend_string * fe_class_name ;
@@ -649,21 +635,20 @@ static inheritance_status zend_perform_covariant_type_check(
649
635
continue ;
650
636
}
651
637
652
- status = zend_perform_covariant_class_type_check ( fe_scope ,
653
- fe_class_name , fe_ce , proto_scope , proto_type ,
638
+ status = zend_is_class_subtype_of_type (
639
+ fe_scope , fe_class_name , fe_ce , proto_scope , proto_type ,
654
640
/* register_unresolved */ false);
655
- if (status == INHERITANCE_ERROR ) {
656
- return INHERITANCE_ERROR ;
641
+ if (status == early_exit_status ) {
642
+ return early_exit_status ;
657
643
}
658
- if (status != INHERITANCE_SUCCESS ) {
659
- all_success = 0 ;
644
+ if (status == INHERITANCE_UNRESOLVED ) {
645
+ have_unresolved = true ;
660
646
}
661
647
} ZEND_TYPE_FOREACH_END ();
662
648
}
663
649
664
- /* All individual checks succeeded, overall success */
665
- if (all_success ) {
666
- return INHERITANCE_SUCCESS ;
650
+ if (!have_unresolved ) {
651
+ return early_exit_status == INHERITANCE_ERROR ? INHERITANCE_SUCCESS : INHERITANCE_ERROR ;
667
652
}
668
653
669
654
/* Register all classes that may have to be resolved */
@@ -683,8 +668,8 @@ static inheritance_status zend_perform_covariant_type_check(
683
668
continue ;
684
669
}
685
670
686
- zend_perform_intersection_covariant_class_type_check ( proto_scope ,
687
- proto_class_name , proto_ce , fe_scope , fe_type ,
671
+ zend_is_any_type_subtype_of_class (
672
+ fe_scope , fe_type , proto_scope , proto_class_name , proto_ce ,
688
673
/* register_unresolved */ true);
689
674
} ZEND_TYPE_FOREACH_END ();
690
675
} else {
@@ -701,8 +686,8 @@ static inheritance_status zend_perform_covariant_type_check(
701
686
continue ;
702
687
}
703
688
704
- zend_perform_covariant_class_type_check ( fe_scope ,
705
- fe_class_name , fe_ce , proto_scope , proto_type ,
689
+ zend_is_class_subtype_of_type (
690
+ fe_scope , fe_class_name , fe_ce , proto_scope , proto_type ,
706
691
/* register_unresolved */ true);
707
692
} ZEND_TYPE_FOREACH_END ();
708
693
}
0 commit comments