@@ -675,6 +675,42 @@ static inheritance_status zend_is_intersection_subtype_of_type(
675675 return early_exit_status == INHERITANCE_ERROR ? INHERITANCE_SUCCESS : INHERITANCE_ERROR ;
676676}
677677
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+
678714ZEND_API inheritance_status zend_perform_covariant_type_check (
679715 zend_class_entry * fe_scope , const zend_type * fe_type_ptr ,
680716 zend_class_entry * proto_scope , const zend_type * proto_type_ptr )
@@ -690,6 +726,17 @@ ZEND_API inheritance_status zend_perform_covariant_type_check(
690726 return INHERITANCE_SUCCESS ;
691727 }
692728
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+
693740 /* Builtin types may be removed, but not added */
694741 uint32_t fe_type_mask = ZEND_TYPE_PURE_MASK (fe_type );
695742 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 *
21742221 ZEND_INHERITANCE_RESET_CHILD_OVERRIDE ;
21752222 }
21762223
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+ }
21772229 ZEND_HASH_MAP_FOREACH_STR_KEY_PTR (& iface -> constants_table , key , c ) {
21782230 do_inherit_iface_constant (key , c , ce , iface );
21792231 } ZEND_HASH_FOREACH_END ();
@@ -2195,6 +2247,11 @@ static void do_interface_implementation(zend_class_entry *ce, zend_class_entry *
21952247 if (iface -> num_interfaces ) {
21962248 zend_do_inherit_interfaces (ce , iface );
21972249 }
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+ }
21982255}
21992256/* }}} */
22002257
0 commit comments