Skip to content

Commit 439458c

Browse files
committed
Refactor bind_generic_types_for_inherited_interfaces
1 parent 106c3d2 commit 439458c

File tree

3 files changed

+59
-46
lines changed

3 files changed

+59
-46
lines changed

Zend/tests/type_declarations/abstract_generics/constraints/implicit_interface_different_bound_types.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ class C implements I2<float>, I1<string> {
1818

1919
?>
2020
--EXPECTF--
21-
Fatal error: Bound types for implicitly and explicitly implemented interfaces must match in %s on line %d
21+
Fatal error: Bound type T for interface I1 implemented explicitly in C with type string must match the implicitly bound type float from interface I2 in %s on line %d

Zend/tests/type_declarations/abstract_generics/constraints/implicit_interface_different_bound_types2.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ class C implements I1<string>, I2<float> {
1818

1919
?>
2020
--EXPECTF--
21-
Fatal error: Bound types for implicitly and explicitly implemented interfaces must match in %s on line %d
21+
Fatal error: Bound type T for interface I1 implemented explicitly in C with type string must match the implicitly bound type float from interface I2 in %s on line %d

Zend/zend_inheritance.c

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2328,61 +2328,74 @@ ZEND_ATTRIBUTE_NONNULL static void bind_generic_types_for_inherited_interfaces(z
23282328
ZEND_HASH_FOREACH_STR_KEY_PTR(iface_bound_types, lc_inherited_iface_name, interface_bound_types_for_inherited_iface) {
23292329
ZEND_ASSERT(lc_inherited_iface_name != NULL);
23302330

2331-
zend_string *generic_param_name = NULL;
2332-
zend_ulong generic_param_index = 0;
2333-
zend_type *bound_type_ptr = NULL;
2334-
HashTable *ce_bound_types_for_inherited_iface = NULL;
2335-
ALLOC_HASHTABLE(ce_bound_types_for_inherited_iface);
2336-
zend_hash_init(
2337-
ce_bound_types_for_inherited_iface,
2338-
zend_hash_num_elements(interface_bound_types_for_inherited_iface),
2339-
NULL,
2340-
zend_types_ht_dtor,
2341-
false /* TODO depends on internals */
2342-
);
2343-
ZEND_HASH_FOREACH_KEY_PTR(interface_bound_types_for_inherited_iface, generic_param_index, generic_param_name, bound_type_ptr) {
2344-
ZEND_ASSERT(generic_param_name == NULL); // TODO Change foreach macro;
2345-
zend_type bound_type = *bound_type_ptr;
2346-
if (ZEND_TYPE_IS_GENERIC_PARAM_NAME(bound_type)) {
2347-
ZEND_ASSERT(ce_bound_types_for_direct_iface != NULL &&
2348-
"If a bound type is generic then we must have bound types for the current interface");
2349-
const zend_type *ce_bound_type_ptr = zend_hash_index_find_ptr(ce_bound_types_for_direct_iface, bound_type_ptr->generic_param_index);
2350-
ZEND_ASSERT(ce_bound_type_ptr != NULL);
2351-
bound_type = *ce_bound_type_ptr;
2352-
}
2353-
2354-
zend_type_copy_ctor(&bound_type, true, false /* TODO Depends on internal or not? */);
2355-
zend_hash_index_add_mem(ce_bound_types_for_inherited_iface, generic_param_index,
2356-
&bound_type, sizeof(bound_type));
2357-
} ZEND_HASH_FOREACH_END();
2358-
23592331
const HashTable *existing_bound_types_for_inherited_iface = zend_hash_find_ptr(ce->bound_types, lc_inherited_iface_name);
23602332
if (EXPECTED(existing_bound_types_for_inherited_iface == NULL)) {
2333+
HashTable *ce_bound_types_for_inherited_iface = NULL;
2334+
ALLOC_HASHTABLE(ce_bound_types_for_inherited_iface);
2335+
zend_hash_init(
2336+
ce_bound_types_for_inherited_iface,
2337+
zend_hash_num_elements(interface_bound_types_for_inherited_iface),
2338+
NULL,
2339+
zend_types_ht_dtor,
2340+
false /* TODO depends on internals */
2341+
);
2342+
2343+
zend_ulong generic_param_index = 0;
2344+
const zend_type *bound_type_ptr = NULL;
2345+
ZEND_HASH_FOREACH_NUM_KEY_PTR(interface_bound_types_for_inherited_iface, generic_param_index, bound_type_ptr) {
2346+
zend_type bound_type = *bound_type_ptr;
2347+
if (ZEND_TYPE_IS_GENERIC_PARAM_NAME(bound_type)) {
2348+
ZEND_ASSERT(ce_bound_types_for_direct_iface != NULL &&
2349+
"If a bound type is generic then we must have bound types for the current interface");
2350+
const zend_type *ce_bound_type_ptr = zend_hash_index_find_ptr(ce_bound_types_for_direct_iface, bound_type_ptr->generic_param_index);
2351+
ZEND_ASSERT(ce_bound_type_ptr != NULL);
2352+
bound_type = *ce_bound_type_ptr;
2353+
}
2354+
2355+
zend_type_copy_ctor(&bound_type, true, false /* TODO Depends on internal or not? */);
2356+
zend_hash_index_add_mem(ce_bound_types_for_inherited_iface, generic_param_index,
2357+
&bound_type, sizeof(bound_type));
2358+
} ZEND_HASH_FOREACH_END();
2359+
zend_hash_add_new_ptr(ce->bound_types, lc_inherited_iface_name, ce_bound_types_for_inherited_iface);
23612360
} else {
2362-
zend_ulong idx;
2363-
zend_string *bound_name;
2364-
const zend_type *ptr;
2365-
ZEND_HASH_FOREACH_KEY_PTR(existing_bound_types_for_inherited_iface, idx, bound_name, ptr) {
2366-
if (bound_name != NULL) {
2367-
continue;
2361+
const uint32_t num_generic_types = zend_hash_num_elements(interface_bound_types_for_inherited_iface);
2362+
ZEND_ASSERT(zend_hash_num_elements(existing_bound_types_for_inherited_iface) == num_generic_types && "Existing bound types should have errored before");
2363+
2364+
for (zend_ulong bound_type_index = 0; bound_type_index < num_generic_types; bound_type_index++) {
2365+
const zend_type *iface_bound_type_ptr = zend_hash_index_find_ptr(interface_bound_types_for_inherited_iface, bound_type_index);
2366+
const zend_type *ce_bound_type_ptr = zend_hash_index_find_ptr(existing_bound_types_for_inherited_iface, bound_type_index);
2367+
ZEND_ASSERT(iface_bound_type_ptr != NULL && ce_bound_type_ptr != NULL);
2368+
if (ZEND_TYPE_IS_GENERIC_PARAM_NAME(*iface_bound_type_ptr)) {
2369+
iface_bound_type_ptr = zend_hash_index_find_ptr(ce_bound_types_for_direct_iface, iface_bound_type_ptr->generic_param_index);
2370+
ZEND_ASSERT(iface_bound_type_ptr != NULL);
23682371
}
2369-
const zend_type t1 = *ptr;
2370-
const zend_type *ptr2 = zend_hash_index_find_ptr(ce_bound_types_for_inherited_iface, idx);
2371-
ZEND_ASSERT(ptr2 != NULL);
2372-
const zend_type t2 = *ptr2;
2372+
const zend_type t1 = *iface_bound_type_ptr;
2373+
const zend_type t2 = *ce_bound_type_ptr;
23732374
if (
23742375
ZEND_TYPE_FULL_MASK(t1) != ZEND_TYPE_FULL_MASK(t2)
23752376
|| (ZEND_TYPE_HAS_NAME(t1) && !zend_string_equals(ZEND_TYPE_NAME(t1), ZEND_TYPE_NAME(t2)))
23762377
// || ZEND_TYPE_HAS_LIST(t1) && TODO Check list types are equal
23772378
) {
2378-
// TODO Improve this error message
2379-
zend_error_noreturn(E_COMPILE_ERROR, "Bound types for implicitly and explicitly implemented interfaces must match");
2379+
const zend_class_entry *inherited_iface = zend_hash_find_ptr(CG(class_table), lc_inherited_iface_name);
2380+
ZEND_ASSERT(inherited_iface != NULL);
2381+
const zend_generic_parameter param = inherited_iface->generic_parameters[bound_type_index];
2382+
2383+
zend_string *ce_bound_type_str = zend_type_to_string_resolved(t2, ce, NULL);
2384+
zend_string *iface_bound_type_str = zend_type_to_string_resolved(t1, iface, NULL);
2385+
zend_error_noreturn(E_COMPILE_ERROR,
2386+
"Bound type %s for interface %s implemented explicitly in %s with type %s must match the implicitly bound type %s from interface %s",
2387+
ZSTR_VAL(param.name),
2388+
ZSTR_VAL(inherited_iface->name),
2389+
ZSTR_VAL(ce->name),
2390+
ZSTR_VAL(ce_bound_type_str),
2391+
ZSTR_VAL(iface_bound_type_str),
2392+
ZSTR_VAL(iface->name)
2393+
);
2394+
zend_string_release_ex(ce_bound_type_str, false);
2395+
zend_string_release_ex(iface_bound_type_str, false);
23802396
}
2381-
} ZEND_HASH_FOREACH_END();
2382-
/* Remove current ones as they may be incomplete without the type name binding */
2383-
zend_hash_del(ce->bound_types, lc_inherited_iface_name);
2397+
}
23842398
}
2385-
zend_hash_add_new_ptr(ce->bound_types, lc_inherited_iface_name, ce_bound_types_for_inherited_iface);
23862399
} ZEND_HASH_FOREACH_END();
23872400
}
23882401

0 commit comments

Comments
 (0)