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 f72c2e6

Browse files
committed
Support invariant bound types
1 parent 263f12a commit f72c2e6

File tree

6 files changed

+142
-14
lines changed

6 files changed

+142
-14
lines changed

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,25 @@ interface I {
88
public function foo(T $param): T;
99
}
1010

11-
class C implements I {
12-
public function foo(string $param): string {}
11+
class CS implements I {
12+
public function foo(string $param): string {
13+
return $param . '!';
14+
}
1315
}
1416

17+
class CI implements I {
18+
public function foo(int $param): int {
19+
return $param + 42;
20+
}
21+
}
22+
23+
$cs = new CS();
24+
var_dump($cs->foo("Hello"));
25+
26+
$ci = new CI();
27+
var_dump($ci->foo(5));
28+
1529
?>
16-
--EXPECTF--
17-
Fatal error: Declaration of C::foo(string $param): string must be compatible with I::foo(T $param): T in %s on line %d
30+
--EXPECT--
31+
string(6) "Hello!"
32+
int(47)

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

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,65 @@ interface I {
1010
public function get(K $key): V;
1111
}
1212

13-
class C implements I {
14-
public function set(int $key, string $value): void {}
15-
public function get(int $key): string {}
13+
class C1 implements I {
14+
public array $a = [];
15+
public function set(int $key, string $value): void {
16+
$this->a[$key] = $value . '!';
17+
}
18+
public function get(int $key): string {
19+
return $this->a[$key];
20+
}
1621
}
1722

23+
class C2 implements I {
24+
public array $a = [];
25+
public function set(string $key, object $value): void {
26+
$this->a[$key] = $value;
27+
}
28+
public function get(string $key): object {
29+
return $this->a[$key];
30+
}
31+
}
32+
33+
$c1 = new C1();
34+
$c1->set(5, "Hello");
35+
var_dump($c1->a);
36+
var_dump($c1->get(5));
37+
38+
$c2 = new C2();
39+
$c2->set('C1', $c1);
40+
var_dump($c2->a);
41+
var_dump($c2->get('C1'));
42+
43+
try {
44+
$c1->set('blah', "Hello");
45+
} catch (\Throwable $e) {
46+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
47+
}
48+
49+
1850
?>
19-
--EXPECTF--
20-
Fatal error: Declaration of C::set(int $key, string $value): void must be compatible with I::set(K $key, V $value): void in %s on line %d
51+
--EXPECT--
52+
array(1) {
53+
[5]=>
54+
string(6) "Hello!"
55+
}
56+
string(6) "Hello!"
57+
array(1) {
58+
["C1"]=>
59+
object(C1)#1 (1) {
60+
["a"]=>
61+
array(1) {
62+
[5]=>
63+
string(6) "Hello!"
64+
}
65+
}
66+
}
67+
object(C1)#1 (1) {
68+
["a"]=>
69+
array(1) {
70+
[5]=>
71+
string(6) "Hello!"
72+
}
73+
}
74+
TypeError: C1::set(): Argument #1 ($key) must be of type int, string given, called in %s on line %d

‎Zend/zend.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,7 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
732732

733733
compiler_globals->script_encoding_list = NULL;
734734
compiler_globals->current_linking_class = NULL;
735+
compiler_globals->bound_associated_types = NULL;
735736

736737
/* Map region is going to be created and resized at run-time. */
737738
compiler_globals->map_ptr_real_base = NULL;

‎Zend/zend_compile.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ void init_compiler(void) /* {{{ */
463463
CG(delayed_autoloads) = NULL;
464464
CG(unlinked_uses) = NULL;
465465
CG(current_linking_class) = NULL;
466+
CG(bound_associated_types) = NULL;
466467
}
467468
/* }}} */
468469

@@ -491,6 +492,7 @@ void shutdown_compiler(void) /* {{{ */
491492
CG(unlinked_uses) = NULL;
492493
}
493494
CG(current_linking_class) = NULL;
495+
ZEND_ASSERT(CG(bound_associated_types) == NULL);
494496
}
495497
/* }}} */
496498

‎Zend/zend_globals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ struct _zend_compiler_globals {
151151
HashTable *delayed_autoloads;
152152
HashTable *unlinked_uses;
153153
zend_class_entry *current_linking_class;
154+
/* Those are initialized and destroyed by zend_do_inheritance_ex() */
155+
HashTable *bound_associated_types;
154156

155157
uint32_t rtd_key_counter;
156158

‎Zend/zend_inheritance.c

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -675,20 +675,40 @@ 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+
678682
static inheritance_status zend_is_type_subtype_of_associated_type(
679683
zend_class_entry *concrete_scope,
680684
const zend_type *concrete_type_ptr,
681-
constzend_type*associated_type_ptr,
682-
HashTable*associated_types
685+
zend_class_entry*associated_type_scope,
686+
constzend_type*associated_type_ptr
683687
) {
684688
const zend_type associated_type = *associated_type_ptr;
685-
const zend_type concrete_type = *concrete_type_ptr;
686689

690+
ZEND_ASSERT(CG(bound_associated_types) && "Have associated type");
687691
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(associated_type));
688692

689693
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+
concrete_scope, concrete_type_ptr, associated_type_scope, bound_type_ptr);
690705

691-
return INHERITANCE_ERROR;
706+
if (sub_type_status != super_type_status) {
707+
return INHERITANCE_ERROR;
708+
} else {
709+
return sub_type_status;
710+
}
711+
}
692712
}
693713

694714
ZEND_API inheritance_status zend_perform_covariant_type_check(
@@ -706,9 +726,15 @@ ZEND_API inheritance_status zend_perform_covariant_type_check(
706726
return INHERITANCE_SUCCESS;
707727
}
708728

729+
/* If we check for concrete return type */
709730
if (ZEND_TYPE_IS_ASSOCIATED(proto_type)) {
710731
return zend_is_type_subtype_of_associated_type(
711-
fe_scope, fe_type_ptr, proto_type_ptr, NULL);
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);
712738
}
713739

714740
/* Builtin types may be removed, but not added */
@@ -1852,6 +1878,10 @@ static void zend_link_hooked_object_iter(zend_class_entry *ce) {
18521878
}
18531879
#endif
18541880

1881+
static void zend_bound_associated_table_ht_dtor(zval *val) {
1882+
/* NO OP as we only use it to be able to refer and save pointers to zend_types */
1883+
}
1884+
18551885
ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked) /* {{{ */
18561886
{
18571887
zend_property_info *property_info;
@@ -1895,6 +1925,14 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
18951925
ce->default_object_handlers = parent_ce->default_object_handlers;
18961926
ce->ce_flags |= ZEND_ACC_RESOLVED_PARENT;
18971927

1928+
// TODO Add associated_types HT for bound type checking
1929+
if (parent_ce->num_associated_types) {
1930+
printf("Hello\n");
1931+
HashTable *ht = emalloc(sizeof(HashTable));
1932+
zend_hash_init(ht, parent_ce->num_associated_types, NULL, zend_bound_associated_table_ht_dtor, false);
1933+
CG(bound_associated_types) = ht;
1934+
}
1935+
18981936
/* Inherit properties */
18991937
if (parent_ce->default_properties_count) {
19001938
zval *src, *dst, *end;
@@ -2063,6 +2101,12 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
20632101
}
20642102
}
20652103
ce->ce_flags |= parent_ce->ce_flags & (ZEND_HAS_STATIC_IN_METHODS | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_HAS_READONLY_PROPS | ZEND_ACC_USE_GUARDS | ZEND_ACC_NOT_SERIALIZABLE | ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES);
2104+
2105+
if (CG(bound_associated_types)) {
2106+
zend_hash_destroy(CG(bound_associated_types));
2107+
efree(CG(bound_associated_types));
2108+
CG(bound_associated_types) = NULL;
2109+
}
20662110
}
20672111
/* }}} */
20682112

@@ -2195,6 +2239,11 @@ static void do_interface_implementation(zend_class_entry *ce, zend_class_entry *
21952239
ZEND_INHERITANCE_RESET_CHILD_OVERRIDE;
21962240
}
21972241

2242+
if (iface->num_associated_types) {
2243+
HashTable *ht = emalloc(sizeof(HashTable));
2244+
zend_hash_init(ht, iface->num_associated_types, NULL, zend_bound_associated_table_ht_dtor, false);
2245+
CG(bound_associated_types) = ht;
2246+
}
21982247
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) {
21992248
do_inherit_iface_constant(key, c, ce, iface);
22002249
} ZEND_HASH_FOREACH_END();
@@ -2216,6 +2265,11 @@ static void do_interface_implementation(zend_class_entry *ce, zend_class_entry *
22162265
if (iface->num_interfaces) {
22172266
zend_do_inherit_interfaces(ce, iface);
22182267
}
2268+
if (CG(bound_associated_types)) {
2269+
zend_hash_destroy(CG(bound_associated_types));
2270+
efree(CG(bound_associated_types));
2271+
CG(bound_associated_types) = NULL;
2272+
}
22192273
}
22202274
/* }}} */
22212275

0 commit comments

Comments
(0)

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