Skip to content

Commit bd19772

Browse files
committed
Use zend_type inside type lists
Instead of having a completely independent encoding for type list entries. This is going to use more memory, but I'm not particularly concerned about that, as type unions that contain multiple classes should be uncommon. On the other hand, this allows us to treat top-level types and types inside lists mostly the same. A new ZEND_TYPE_FOREACH macros allows to transparently treat list and non-list types the same way. I'm not using it everywhere it could be used for now, just the places that seemed most obvious. Of course, this will make any future type system changes much simpler, as it will not be necessary to duplicate all logic two times.
1 parent afdaa91 commit bd19772

14 files changed

+180
-285
lines changed

Zend/zend.c

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -600,10 +600,10 @@ static void function_copy_ctor(zval *zv) /* {{{ */
600600
memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types));
601601
ZEND_TYPE_SET_PTR(new_arg_info[i].type, new_list);
602602

603-
void **entry;
604-
ZEND_TYPE_LIST_FOREACH_PTR(new_list, entry) {
605-
zend_string *name = zend_string_dup(ZEND_TYPE_LIST_GET_NAME(*entry), 1);
606-
*entry = ZEND_TYPE_LIST_ENCODE_NAME(name);
603+
zend_type *list_type;
604+
ZEND_TYPE_LIST_FOREACH(new_list, list_type) {
605+
zend_string *name = zend_string_dup(ZEND_TYPE_NAME(*list_type), 1);
606+
ZEND_TYPE_SET_PTR(*list_type, name);
607607
} ZEND_TYPE_LIST_FOREACH_END();
608608
} else if (ZEND_TYPE_HAS_NAME(arg_info[i].type)) {
609609
zend_string *name = zend_string_dup(ZEND_TYPE_NAME(arg_info[i].type), 1);
@@ -983,20 +983,14 @@ static void zend_resolve_property_types(void) /* {{{ */
983983

984984
if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(ce))) {
985985
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
986-
if (ZEND_TYPE_HAS_LIST(prop_info->type)) {
987-
void **entry;
988-
ZEND_TYPE_LIST_FOREACH_PTR(ZEND_TYPE_LIST(prop_info->type), entry) {
989-
if (ZEND_TYPE_LIST_IS_NAME(*entry)) {
990-
zend_string *type_name = ZEND_TYPE_LIST_GET_NAME(*entry);
991-
*entry = ZEND_TYPE_LIST_ENCODE_CE(resolve_type_name(type_name));
992-
zend_string_release(type_name);
993-
}
994-
} ZEND_TYPE_LIST_FOREACH_END();
995-
} else if (ZEND_TYPE_HAS_NAME(prop_info->type)) {
996-
zend_string *type_name = ZEND_TYPE_NAME(prop_info->type);
997-
ZEND_TYPE_SET_CE(prop_info->type, resolve_type_name(type_name));
998-
zend_string_release(type_name);
999-
}
986+
zend_type *single_type;
987+
ZEND_TYPE_FOREACH(prop_info->type, single_type) {
988+
if (ZEND_TYPE_HAS_NAME(*single_type)) {
989+
zend_string *type_name = ZEND_TYPE_NAME(*single_type);
990+
ZEND_TYPE_SET_CE(*single_type, resolve_type_name(type_name));
991+
zend_string_release(type_name);
992+
}
993+
} ZEND_TYPE_FOREACH_END();
1000994
} ZEND_HASH_FOREACH_END();
1001995
}
1002996
ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;

Zend/zend_compile.c

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,13 +1164,12 @@ static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scop
11641164
zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope) {
11651165
zend_string *str = NULL;
11661166
if (ZEND_TYPE_HAS_LIST(type)) {
1167-
void *elem;
1168-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), elem) {
1169-
if (ZEND_TYPE_LIST_IS_CE(elem)) {
1170-
str = add_type_string(str, ZEND_TYPE_LIST_GET_CE(elem)->name);
1167+
zend_type *list_type;
1168+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
1169+
if (ZEND_TYPE_HAS_CE(*list_type)) {
1170+
str = add_type_string(str, ZEND_TYPE_CE(*list_type)->name);
11711171
} else {
1172-
str = add_type_string(str,
1173-
resolve_class_name(ZEND_TYPE_LIST_GET_NAME(elem), scope));
1172+
str = add_type_string(str, resolve_class_name(ZEND_TYPE_NAME(*list_type), scope));
11741173
}
11751174
} ZEND_TYPE_LIST_FOREACH_END();
11761175
} else if (ZEND_TYPE_HAS_NAME(type)) {
@@ -1245,23 +1244,16 @@ static void zend_mark_function_as_generator() /* {{{ */
12451244

12461245
if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
12471246
zend_type return_type = CG(active_op_array)->arg_info[-1].type;
1248-
zend_bool valid_type = 0;
1249-
if (ZEND_TYPE_HAS_CLASS(return_type)) {
1250-
if (ZEND_TYPE_HAS_LIST(return_type)) {
1251-
void *entry;
1252-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(return_type), entry) {
1253-
ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
1254-
if (is_generator_compatible_class_type(ZEND_TYPE_LIST_GET_NAME(entry))) {
1255-
valid_type = 1;
1256-
break;
1257-
}
1258-
} ZEND_TYPE_LIST_FOREACH_END();
1259-
} else {
1260-
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(return_type));
1261-
valid_type = is_generator_compatible_class_type(ZEND_TYPE_NAME(return_type));
1262-
}
1263-
} else {
1264-
valid_type = (ZEND_TYPE_FULL_MASK(return_type) & MAY_BE_ITERABLE) != 0;
1247+
zend_bool valid_type = (ZEND_TYPE_FULL_MASK(return_type) & MAY_BE_ITERABLE) != 0;
1248+
if (!valid_type) {
1249+
zend_type *single_type;
1250+
ZEND_TYPE_FOREACH(return_type, single_type) {
1251+
if (ZEND_TYPE_HAS_NAME(*single_type)
1252+
&& is_generator_compatible_class_type(ZEND_TYPE_NAME(*single_type))) {
1253+
valid_type = 1;
1254+
break;
1255+
}
1256+
} ZEND_TYPE_FOREACH_END();
12651257
}
12661258

12671259
if (!valid_type) {
@@ -5521,17 +5513,13 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
55215513
}
55225514

55235515
static zend_bool zend_type_contains_traversable(zend_type type) {
5524-
if (ZEND_TYPE_HAS_LIST(type)) {
5525-
void *entry;
5526-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), entry) {
5527-
ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
5528-
if (zend_string_equals_literal_ci(ZEND_TYPE_LIST_GET_NAME(entry), "Traversable")) {
5529-
return 1;
5530-
}
5531-
} ZEND_TYPE_LIST_FOREACH_END();
5532-
} else if (ZEND_TYPE_HAS_NAME(type)) {
5533-
return zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "Traversable");
5534-
}
5516+
zend_type *single_type;
5517+
ZEND_TYPE_FOREACH(type, single_type) {
5518+
if (ZEND_TYPE_HAS_NAME(*single_type)
5519+
&& zend_string_equals_literal_ci(ZEND_TYPE_NAME(*single_type), "Traversable")) {
5520+
return 1;
5521+
}
5522+
} ZEND_TYPE_FOREACH_END();
55355523
return 0;
55365524
}
55375525

@@ -5561,6 +5549,7 @@ static zend_type zend_compile_typename(
55615549
"Duplicate type %s is redundant", ZSTR_VAL(overlap_type_str));
55625550
}
55635551
ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(single_type);
5552+
ZEND_TYPE_FULL_MASK(single_type) &= ~_ZEND_TYPE_MAY_BE_MASK;
55645553

55655554
if (ZEND_TYPE_HAS_CLASS(single_type)) {
55665555
if (!ZEND_TYPE_HAS_CLASS(type)) {
@@ -5580,15 +5569,16 @@ static zend_type zend_compile_typename(
55805569
} else {
55815570
list = erealloc(old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types + 1));
55825571
}
5583-
list->types[list->num_types++] = ZEND_TYPE_NAME(single_type);
55845572
} else {
55855573
/* Switch from single name to name list. */
55865574
size_t size = ZEND_TYPE_LIST_SIZE(2);
55875575
list = use_arena ? zend_arena_alloc(&CG(arena), size) : emalloc(size);
5588-
list->num_types = 2;
5589-
list->types[0] = ZEND_TYPE_NAME(type);
5590-
list->types[1] = ZEND_TYPE_NAME(single_type);
5576+
list->num_types = 1;
5577+
list->types[0] = type;
5578+
ZEND_TYPE_FULL_MASK(list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
55915579
}
5580+
5581+
list->types[list->num_types++] = single_type;
55925582
ZEND_TYPE_SET_LIST(type, list);
55935583
if (use_arena) {
55945584
ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_ARENA_BIT;
@@ -5597,8 +5587,7 @@ static zend_type zend_compile_typename(
55975587
/* Check for trivially redundant class types */
55985588
for (size_t i = 0; i < list->num_types - 1; i++) {
55995589
if (zend_string_equals_ci(
5600-
ZEND_TYPE_LIST_GET_NAME(list->types[i]),
5601-
ZEND_TYPE_NAME(single_type))) {
5590+
ZEND_TYPE_NAME(list->types[i]), ZEND_TYPE_NAME(single_type))) {
56025591
zend_string *single_type_str = zend_type_to_string(single_type);
56035592
zend_error_noreturn(E_COMPILE_ERROR,
56045593
"Duplicate type %s is redundant", ZSTR_VAL(single_type_str));

Zend/zend_execute.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -941,18 +941,18 @@ static zend_bool zend_check_and_resolve_property_class_type(
941941
zend_property_info *info, zend_class_entry *object_ce) {
942942
zend_class_entry *ce;
943943
if (ZEND_TYPE_HAS_LIST(info->type)) {
944-
void **entry;
945-
ZEND_TYPE_LIST_FOREACH_PTR(ZEND_TYPE_LIST(info->type), entry) {
946-
if (ZEND_TYPE_LIST_IS_NAME(*entry)) {
947-
zend_string *name = ZEND_TYPE_LIST_GET_NAME(*entry);
944+
zend_type *list_type;
945+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(info->type), list_type) {
946+
if (ZEND_TYPE_HAS_NAME(*list_type)) {
947+
zend_string *name = ZEND_TYPE_NAME(*list_type);
948948
ce = resolve_single_class_type(name, info->ce);
949949
if (!ce) {
950950
continue;
951951
}
952952
zend_string_release(name);
953-
*entry = ZEND_TYPE_LIST_ENCODE_CE(ce);
953+
ZEND_TYPE_SET_CE(*list_type, ce);
954954
} else {
955-
ce = ZEND_TYPE_LIST_GET_CE(*entry);
955+
ce = ZEND_TYPE_CE(*list_type);
956956
}
957957
if (instanceof_function(object_ce, ce)) {
958958
return 1;
@@ -1032,12 +1032,12 @@ static zend_always_inline zend_bool zend_check_type_slow(
10321032
if (ZEND_TYPE_HAS_CLASS(type) && Z_TYPE_P(arg) == IS_OBJECT) {
10331033
zend_class_entry *ce;
10341034
if (ZEND_TYPE_HAS_LIST(type)) {
1035-
void *entry;
1036-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), entry) {
1035+
zend_type *list_type;
1036+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
10371037
if (*cache_slot) {
10381038
ce = *cache_slot;
10391039
} else {
1040-
ce = zend_fetch_class(ZEND_TYPE_LIST_GET_NAME(entry),
1040+
ce = zend_fetch_class(ZEND_TYPE_NAME(*list_type),
10411041
(ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
10421042
if (!ce) {
10431043
cache_slot++;

Zend/zend_inheritance.c

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ static void zend_type_copy_ctor(zend_type *type, zend_bool persistent) {
4949
memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types));
5050
ZEND_TYPE_SET_PTR(*type, new_list);
5151

52-
void *entry;
53-
ZEND_TYPE_LIST_FOREACH(new_list, entry) {
54-
ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
55-
zend_string_addref(ZEND_TYPE_LIST_GET_NAME(entry));
52+
zend_type *list_type;
53+
ZEND_TYPE_LIST_FOREACH(new_list, list_type) {
54+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type));
55+
zend_string_addref(ZEND_TYPE_NAME(*list_type));
5656
} ZEND_TYPE_LIST_FOREACH_END();
5757
} else if (ZEND_TYPE_HAS_NAME(*type)) {
5858
zend_string_addref(ZEND_TYPE_NAME(*type));
@@ -323,19 +323,13 @@ static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce
323323
}
324324

325325
static zend_bool zend_type_contains_traversable(zend_type type) {
326-
if (ZEND_TYPE_HAS_LIST(type)) {
327-
void *entry;
328-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), entry) {
329-
ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
330-
if (zend_string_equals_literal_ci(ZEND_TYPE_LIST_GET_NAME(entry), "Traversable")) {
331-
return 1;
332-
}
333-
} ZEND_TYPE_LIST_FOREACH_END();
334-
return 0;
335-
}
336-
if (ZEND_TYPE_HAS_NAME(type)) {
337-
return zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "Traversable");
338-
}
326+
zend_type *single_type;
327+
ZEND_TYPE_FOREACH(type, single_type) {
328+
if (ZEND_TYPE_HAS_NAME(*single_type)
329+
&& zend_string_equals_literal_ci(ZEND_TYPE_NAME(*single_type), "Traversable")) {
330+
return 1;
331+
}
332+
} ZEND_TYPE_FOREACH_END();
339333
return 0;
340334
}
341335

@@ -373,33 +367,18 @@ static inheritance_status zend_perform_covariant_class_type_check(
373367
return INHERITANCE_SUCCESS;
374368
}
375369
}
376-
if (ZEND_TYPE_HAS_NAME(proto_type)) {
377-
zend_string *proto_class_name = resolve_class_name(proto_scope, ZEND_TYPE_NAME(proto_type));
378-
if (zend_string_equals_ci(fe_class_name, proto_class_name)) {
379-
return INHERITANCE_SUCCESS;
380-
}
381370

382-
/* Make sure to always load both classes, to avoid only registering one of them as
383-
* a delayed autoload. */
384-
if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name, register_unresolved);
385-
zend_class_entry *proto_ce =
386-
lookup_class(proto_scope, proto_class_name, register_unresolved);
387-
if (!fe_ce || !proto_ce) {
388-
have_unresolved = 1;
389-
} else if (unlinked_instanceof(fe_ce, proto_ce)) {
390-
return INHERITANCE_SUCCESS;
391-
}
392-
}
393-
if (ZEND_TYPE_HAS_LIST(proto_type)) {
394-
void *entry;
395-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(proto_type), entry) {
396-
ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
371+
zend_type *single_type;
372+
ZEND_TYPE_FOREACH(proto_type, single_type) {
373+
if (ZEND_TYPE_HAS_NAME(*single_type)) {
397374
zend_string *proto_class_name =
398-
resolve_class_name(proto_scope, ZEND_TYPE_LIST_GET_NAME(entry));
375+
resolve_class_name(proto_scope, ZEND_TYPE_NAME(*single_type));
399376
if (zend_string_equals_ci(fe_class_name, proto_class_name)) {
400377
return INHERITANCE_SUCCESS;
401378
}
402379

380+
/* Make sure to always load both classes, to avoid only registering one of them as
381+
* a delayed autoload. */
403382
if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name, register_unresolved);
404383
zend_class_entry *proto_ce =
405384
lookup_class(proto_scope, proto_class_name, register_unresolved);
@@ -408,8 +387,9 @@ static inheritance_status zend_perform_covariant_class_type_check(
408387
} else if (unlinked_instanceof(fe_ce, proto_ce)) {
409388
return INHERITANCE_SUCCESS;
410389
}
411-
} ZEND_TYPE_LIST_FOREACH_END();
412-
}
390+
}
391+
} ZEND_TYPE_FOREACH_END();
392+
413393
return have_unresolved ? INHERITANCE_UNRESOLVED : INHERITANCE_ERROR;
414394
}
415395

@@ -452,14 +432,14 @@ static inheritance_status zend_perform_covariant_type_check(
452432
}
453433

454434
if (ZEND_TYPE_HAS_LIST(fe_type)) {
455-
void *entry;
435+
zend_type *list_type;
456436
zend_bool all_success = 1;
457437

458438
/* First try to check whether we can succeed without resolving anything */
459-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(fe_type), entry) {
460-
ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
439+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(fe_type), list_type) {
440+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type));
461441
zend_string *fe_class_name =
462-
resolve_class_name(fe_scope, ZEND_TYPE_LIST_GET_NAME(entry));
442+
resolve_class_name(fe_scope, ZEND_TYPE_NAME(*list_type));
463443
inheritance_status status = zend_perform_covariant_class_type_check(
464444
fe_scope, fe_class_name, proto_scope, proto_type, /* register_unresolved */ 0);
465445
if (status == INHERITANCE_ERROR) {
@@ -477,10 +457,10 @@ static inheritance_status zend_perform_covariant_type_check(
477457
}
478458

479459
/* Register all classes that may have to be resolved */
480-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(fe_type), entry) {
481-
ZEND_ASSERT(ZEND_TYPE_LIST_IS_NAME(entry));
460+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(fe_type), list_type) {
461+
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type));
482462
zend_string *fe_class_name =
483-
resolve_class_name(fe_scope, ZEND_TYPE_LIST_GET_NAME(entry));
463+
resolve_class_name(fe_scope, ZEND_TYPE_NAME(*list_type));
484464
zend_perform_covariant_class_type_check(
485465
fe_scope, fe_class_name, proto_scope, proto_type, /* register_unresolved */ 1);
486466
} ZEND_TYPE_LIST_FOREACH_END();

Zend/zend_opcode.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,10 @@ ZEND_API void destroy_zend_function(zend_function *function)
104104

105105
ZEND_API void zend_type_release(zend_type type, zend_bool persistent) {
106106
if (ZEND_TYPE_HAS_LIST(type)) {
107-
void *entry;
108-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), entry) {
109-
if (ZEND_TYPE_LIST_IS_NAME(entry)) {
110-
zend_string_release(ZEND_TYPE_LIST_GET_NAME(entry));
107+
zend_type *list_type;
108+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
109+
if (ZEND_TYPE_HAS_NAME(*list_type)) {
110+
zend_string_release(ZEND_TYPE_NAME(*list_type));
111111
}
112112
} ZEND_TYPE_LIST_FOREACH_END();
113113
if (!ZEND_TYPE_USES_ARENA(type)) {

0 commit comments

Comments
 (0)