@@ -675,6 +675,42 @@ static inheritance_status zend_is_intersection_subtype_of_type(
675
675
return early_exit_status == INHERITANCE_ERROR ? INHERITANCE_SUCCESS : INHERITANCE_ERROR ;
676
676
}
677
677
678
+ ZEND_API inheritance_status zend_perform_covariant_type_check (
679
+ zend_class_entry * fe_scope , const zend_type * fe_type_ptr ,
680
+ zend_class_entry * proto_scope , const zend_type * proto_type_ptr );
681
+
682
+ static inheritance_status zend_is_type_subtype_of_associated_type (
683
+ zend_class_entry * concrete_scope ,
684
+ const zend_type * concrete_type_ptr ,
685
+ zend_class_entry * associated_type_scope ,
686
+ const zend_type * associated_type_ptr
687
+ ) {
688
+ const zend_type associated_type = * associated_type_ptr ;
689
+
690
+ ZEND_ASSERT (CG (bound_associated_types ) && "Have associated type" );
691
+ ZEND_ASSERT (ZEND_TYPE_HAS_NAME (associated_type ));
692
+
693
+ zend_string * associated_type_name = ZEND_TYPE_NAME (associated_type );
694
+ const zend_type * bound_type_ptr = zend_hash_find_ptr (CG (bound_associated_types ), associated_type_name );
695
+ if (bound_type_ptr == NULL ) {
696
+ /* Loosing const qualifier here is OK because this hashtable never frees or does anything with the value */
697
+ zend_hash_add_new_ptr (CG (bound_associated_types ), associated_type_name , (void * )concrete_type_ptr );
698
+ return INHERITANCE_SUCCESS ;
699
+ } else {
700
+ /* Associated type must be invariant */
701
+ const inheritance_status sub_type_status = zend_perform_covariant_type_check (
702
+ concrete_scope , concrete_type_ptr , associated_type_scope , bound_type_ptr );
703
+ const inheritance_status super_type_status = zend_perform_covariant_type_check (
704
+ associated_type_scope , bound_type_ptr , concrete_scope , concrete_type_ptr );
705
+
706
+ if (sub_type_status != super_type_status ) {
707
+ return INHERITANCE_ERROR ;
708
+ } else {
709
+ return sub_type_status ;
710
+ }
711
+ }
712
+ }
713
+
678
714
ZEND_API inheritance_status zend_perform_covariant_type_check (
679
715
zend_class_entry * fe_scope , const zend_type * fe_type_ptr ,
680
716
zend_class_entry * proto_scope , const zend_type * proto_type_ptr )
@@ -690,6 +726,17 @@ ZEND_API inheritance_status zend_perform_covariant_type_check(
690
726
return INHERITANCE_SUCCESS ;
691
727
}
692
728
729
+ /* If we check for concrete return type */
730
+ if (ZEND_TYPE_IS_ASSOCIATED (proto_type )) {
731
+ return zend_is_type_subtype_of_associated_type (
732
+ fe_scope , fe_type_ptr , proto_scope , proto_type_ptr );
733
+ }
734
+ /* If we check for concrete parameter type */
735
+ if (ZEND_TYPE_IS_ASSOCIATED (fe_type )) {
736
+ return zend_is_type_subtype_of_associated_type (
737
+ proto_scope , proto_type_ptr , fe_scope , fe_type_ptr );
738
+ }
739
+
693
740
/* Builtin types may be removed, but not added */
694
741
uint32_t fe_type_mask = ZEND_TYPE_PURE_MASK (fe_type );
695
742
uint32_t proto_type_mask = ZEND_TYPE_PURE_MASK (proto_type );
@@ -2174,6 +2221,11 @@ static void do_interface_implementation(zend_class_entry *ce, zend_class_entry *
2174
2221
ZEND_INHERITANCE_RESET_CHILD_OVERRIDE ;
2175
2222
}
2176
2223
2224
+ if (iface -> associated_types ) {
2225
+ HashTable * ht = emalloc (sizeof (HashTable ));
2226
+ zend_hash_init (ht , zend_hash_num_elements (iface -> associated_types ), NULL , NULL , false);
2227
+ CG (bound_associated_types ) = ht ;
2228
+ }
2177
2229
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR (& iface -> constants_table , key , c ) {
2178
2230
do_inherit_iface_constant (key , c , ce , iface );
2179
2231
} ZEND_HASH_FOREACH_END ();
@@ -2195,6 +2247,11 @@ static void do_interface_implementation(zend_class_entry *ce, zend_class_entry *
2195
2247
if (iface -> num_interfaces ) {
2196
2248
zend_do_inherit_interfaces (ce , iface );
2197
2249
}
2250
+ if (CG (bound_associated_types )) {
2251
+ zend_hash_destroy (CG (bound_associated_types ));
2252
+ efree (CG (bound_associated_types ));
2253
+ CG (bound_associated_types ) = NULL ;
2254
+ }
2198
2255
}
2199
2256
/* }}} */
2200
2257
0 commit comments