Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e9c28b1

Browse files
committed
Add variance checking for constrained associated type
1 parent 4c5a19a commit e9c28b1

File tree

3 files changed

+52
-3
lines changed

3 files changed

+52
-3
lines changed

‎Zend/tests/type_declarations/associated/associated_type_with_constraint_failed.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ class C implements I {
1313
}
1414

1515
?>
16-
--EXPECT--
16+
--EXPECTF--
17+
Fatal error: Declaration of C::foo(float $param): float must be compatible with I::foo(T<string|int> $param): T<string|int> in %s on line %d

‎Zend/zend_compile.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,12 @@ void shutdown_compiler(void) /* {{{ */
494494
CG(unlinked_uses) = NULL;
495495
}
496496
CG(current_linking_class) = NULL;
497-
ZEND_ASSERT(CG(bound_associated_types) == NULL);
497+
/* This can happen during a fatal error */
498+
if (CG(bound_associated_types)) {
499+
zend_hash_destroy(CG(bound_associated_types));
500+
FREE_HASHTABLE(CG(bound_associated_types));
501+
CG(bound_associated_types) = NULL;
502+
}
498503
}
499504
/* }}} */
500505

@@ -1436,6 +1441,26 @@ static zend_string *add_intersection_type(zend_string *str,
14361441
return str;
14371442
}
14381443

1444+
static zend_string *add_associated_type(zend_string *associated_type, zend_class_entry *scope)
1445+
{
1446+
const zend_type *constraint = zend_hash_find_ptr(scope->associated_types, associated_type);
1447+
ZEND_ASSERT(constraint != NULL);
1448+
1449+
zend_string *constraint_type_str = zend_type_to_string_resolved(*constraint, scope);
1450+
1451+
size_t len = ZSTR_LEN(associated_type) + ZSTR_LEN(constraint_type_str) + strlen("<>");
1452+
zend_string *result = zend_string_alloc(len, 0);
1453+
1454+
memcpy(ZSTR_VAL(result), ZSTR_VAL(associated_type), ZSTR_LEN(associated_type));
1455+
ZSTR_VAL(result)[ZSTR_LEN(associated_type)] = '<';
1456+
memcpy(ZSTR_VAL(result) + ZSTR_LEN(associated_type) + 1, ZSTR_VAL(constraint_type_str), ZSTR_LEN(constraint_type_str));
1457+
ZSTR_VAL(result)[len-1] = '>';
1458+
ZSTR_VAL(result)[len] = '0円';
1459+
1460+
zend_string_release(constraint_type_str);
1461+
return result;
1462+
}
1463+
14391464
zend_string *zend_type_to_string_resolved(const zend_type type, zend_class_entry *scope) {
14401465
zend_string *str = NULL;
14411466

@@ -1459,6 +1484,8 @@ zend_string *zend_type_to_string_resolved(const zend_type type, zend_class_entry
14591484
str = add_type_string(str, resolved, /* is_intersection */ false);
14601485
zend_string_release(resolved);
14611486
} ZEND_TYPE_LIST_FOREACH_END();
1487+
} else if (ZEND_TYPE_IS_ASSOCIATED(type)) {
1488+
str = add_associated_type(ZEND_TYPE_NAME(type), scope);
14621489
} else if (ZEND_TYPE_HAS_NAME(type)) {
14631490
str = resolve_class_name(ZEND_TYPE_NAME(type), scope);
14641491
}
@@ -9040,12 +9067,18 @@ static void zend_compile_use_trait(zend_ast *ast) /* {{{ */
90409067
static void zend_associated_table_ht_dtor(zval *val) {
90419068
/* NO OP as we only use it to be able to refer and save pointers to zend_types */
90429069
// TODO do we actually want to store copies of types?
9070+
zend_type *associated_type = Z_PTR_P(val);
9071+
if (associated_type != &zend_mixed_type) {
9072+
zend_type_release(*associated_type, false);
9073+
efree(associated_type);
9074+
}
90439075
}
90449076

90459077
static void zend_compile_associated_type(zend_ast *ast) {
90469078
zend_class_entry *ce = CG(active_class_entry);
90479079
HashTable *associated_types = ce->associated_types;
90489080
zend_ast *name_ast = ast->child[0];
9081+
zend_ast *type_ast = ast->child[1];
90499082
zend_string *name = zend_ast_get_str(name_ast);
90509083

90519084
if ((ce->ce_flags & ZEND_ACC_INTERFACE) == 0) {
@@ -9065,7 +9098,12 @@ static void zend_compile_associated_type(zend_ast *ast) {
90659098
"Cannot have two associated types with the same name \"%s\"", ZSTR_VAL(name));
90669099
}
90679100

9068-
zend_hash_add_new_ptr(associated_types, name, (void*) &zend_mixed_type);
9101+
if (type_ast != NULL) {
9102+
zend_type type = zend_compile_typename(type_ast);
9103+
zend_hash_add_new_mem(associated_types, name, &type, sizeof(type));
9104+
} else {
9105+
zend_hash_add_new_ptr(associated_types, name, (void*) &zend_mixed_type);
9106+
}
90699107
}
90709108

90719109
static void zend_compile_implements(zend_ast *ast) /* {{{ */

‎Zend/zend_inheritance.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,16 @@ static inheritance_status zend_is_type_subtype_of_associated_type(
693693
zend_string *associated_type_name = ZEND_TYPE_NAME(associated_type);
694694
const zend_type *bound_type_ptr = zend_hash_find_ptr(CG(bound_associated_types), associated_type_name);
695695
if (bound_type_ptr == NULL) {
696+
const zend_type *constraint = zend_hash_find_ptr(associated_type_scope->associated_types, associated_type_name);
697+
ZEND_ASSERT(constraint != NULL);
698+
/* Check that the provided type is a subtype of the constraint */
699+
const inheritance_status status = zend_perform_covariant_type_check(
700+
concrete_scope, concrete_type_ptr,
701+
associated_type_scope, constraint);
702+
if (status != INHERITANCE_SUCCESS) {
703+
return status;
704+
}
705+
696706
/* Loosing const qualifier here is OK because this hashtable never frees or does anything with the value */
697707
zend_hash_add_new_ptr(CG(bound_associated_types), associated_type_name, (void*)concrete_type_ptr);
698708
return INHERITANCE_SUCCESS;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /