Skip to content

Commit dab47b2

Browse files
committed
Make zend_type a 2-field struct
1 parent 1fe47ad commit dab47b2

17 files changed

+146
-167
lines changed

Zend/zend_API.c

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,11 +2062,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
20622062
}
20632063
if (ZEND_TYPE_IS_SET(info->type)) {
20642064
if (ZEND_TYPE_IS_CLASS(info->type)) {
2065-
const char *type_name = (const char*)info->type;
2066-
2067-
if (type_name[0] == '?') {
2068-
type_name++;
2069-
}
2065+
const char *type_name = info->type.literal_name;
20702066
if (!scope && (!strcasecmp(type_name, "self") || !strcasecmp(type_name, "parent"))) {
20712067
zend_error_noreturn(E_CORE_ERROR, "Cannot declare a return type of %s outside of a class scope", type_name);
20722068
}
@@ -2147,16 +2143,9 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
21472143
reg_function->common.arg_info = new_arg_info + 1;
21482144
for (i = 0; i < num_args; i++) {
21492145
if (ZEND_TYPE_IS_CLASS(new_arg_info[i].type)) {
2150-
const char *class_name = (const char*)new_arg_info[i].type;
2151-
zend_bool allow_null = 0;
2152-
zend_string *str;
2153-
2154-
if (class_name[0] == '?') {
2155-
class_name++;
2156-
allow_null = 1;
2157-
}
2158-
str = zend_string_init_interned(class_name, strlen(class_name), 1);
2159-
new_arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(str, allow_null);
2146+
const char *class_name = new_arg_info[i].type.literal_name;
2147+
ZEND_TYPE_NAME(new_arg_info[i].type) =
2148+
zend_string_init_interned(class_name, strlen(class_name), 1);
21602149
}
21612150
}
21622151
}

Zend/zend_API.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,12 @@ typedef struct _zend_fcall_info_cache {
9696

9797
#define ZEND_FE_END { NULL, NULL, NULL, 0, 0 }
9898

99-
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 0},
100-
#define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, 0, pass_by_ref, 0},
99+
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, {}, pass_by_ref, 0},
101100
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 0 },
102101
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(IS_ARRAY, allow_null), pass_by_ref, 0 },
103102
#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(IS_CALLABLE, allow_null), pass_by_ref, 0 },
104103
#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(type_hint, allow_null), pass_by_ref, 0 },
105-
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 1 },
104+
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, {}, pass_by_ref, 1 },
106105
#define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(type_hint, allow_null), pass_by_ref, 1 },
107106
#define ZEND_ARG_VARIADIC_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 1 },
108107

@@ -121,9 +120,9 @@ typedef struct _zend_fcall_info_cache {
121120

122121
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \
123122
static const zend_internal_arg_info name[] = { \
124-
{ (const char*)(zend_uintptr_t)(required_num_args), 0, return_reference, 0 },
123+
{ (const char*)(zend_uintptr_t)(required_num_args), {}, return_reference, 0 },
125124
#define ZEND_BEGIN_ARG_INFO(name, _unused) \
126-
ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
125+
ZEND_BEGIN_ARG_INFO_EX(name, {}, ZEND_RETURN_VALUE, -1)
127126
#define ZEND_END_ARG_INFO() };
128127

129128
/* Name macros */

Zend/zend_compile.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
10961096
} else if (ZEND_TYPE_IS_CE(type)) {
10971097
str = zend_string_copy(ZEND_TYPE_CE(type)->name);
10981098
} else {
1099-
uint32_t type_mask = ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(type));
1099+
uint32_t type_mask = ZEND_TYPE_MASK_WITHOUT_NULL(type);
11001100
switch (type_mask) {
11011101
case MAY_BE_FALSE|MAY_BE_TRUE:
11021102
str = ZSTR_KNOWN(ZEND_STR_BOOL);
@@ -2123,7 +2123,7 @@ static void zend_emit_return_type_check(
21232123
zend_op *opline;
21242124

21252125
/* `return ...;` is illegal in a void function (but `return;` isn't) */
2126-
if (ZEND_TYPE_IS_MASK(type) && ZEND_TYPE_CONTAINS_CODE(type, IS_VOID)) {
2126+
if (ZEND_TYPE_CONTAINS_CODE(type, IS_VOID)) {
21272127
if (expr) {
21282128
if (expr->op_type == IS_CONST && Z_TYPE(expr->u.constant) == IS_NULL) {
21292129
zend_error_noreturn(E_COMPILE_ERROR,
@@ -2149,8 +2149,7 @@ static void zend_emit_return_type_check(
21492149
}
21502150

21512151
if (expr && expr->op_type == IS_CONST) {
2152-
if (ZEND_TYPE_IS_MASK(type)
2153-
&& ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE(expr->u.constant))) {
2152+
if (ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE(expr->u.constant))) {
21542153
/* we don't need run-time check */
21552154
return;
21562155
}
@@ -5989,8 +5988,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) /
59895988
if (type_ast) {
59905989
type = zend_compile_typename(type_ast, 0);
59915990

5992-
if (ZEND_TYPE_IS_MASK(type)
5993-
&& (ZEND_TYPE_MASK(type) & (MAY_BE_VOID|MAY_BE_CALLABLE))) {
5991+
if (ZEND_TYPE_MASK(type) & (MAY_BE_VOID|MAY_BE_CALLABLE)) {
59945992
zend_string *str = zend_type_to_string(type);
59955993
zend_error_noreturn(E_COMPILE_ERROR,
59965994
"Property %s::$%s cannot have type %s",

Zend/zend_execute.c

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -711,8 +711,8 @@ static ZEND_COLD void zend_verify_type_error_common(
711711
*need_kind = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type));
712712
}
713713
} else {
714-
zend_type type = ZEND_TYPE_WITHOUT_NULL(arg_info->type);
715-
switch (ZEND_TYPE_MASK(type)) {
714+
uint32_t type_mask = ZEND_TYPE_MASK_WITHOUT_NULL(arg_info->type);
715+
switch (type_mask) {
716716
case MAY_BE_OBJECT:
717717
*need_msg = "be an ";
718718
*need_kind = "object";
@@ -726,11 +726,15 @@ static ZEND_COLD void zend_verify_type_error_common(
726726
*need_kind = "";
727727
break;
728728
default:
729+
{
729730
/* TODO: The zend_type_to_string() result is guaranteed interned here.
730731
* It would be beter to switch all this code to use zend_string though. */
732+
zend_type type = arg_info->type;
733+
ZEND_TYPE_MASK(type) &= ~MAY_BE_NULL;
731734
*need_msg = "be of the type ";
732735
*need_kind = ZSTR_VAL(zend_type_to_string(type));
733736
break;
737+
}
734738
}
735739
}
736740

@@ -1193,7 +1197,7 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
11931197
zend_class_entry *ce = NULL;
11941198
void *dummy_cache_slot = NULL;
11951199

1196-
if (ZEND_TYPE_IS_MASK(ret_info->type) && (ZEND_TYPE_MASK(ret_info->type) & MAY_BE_VOID)) {
1200+
if (ZEND_TYPE_MASK(ret_info->type) & MAY_BE_VOID) {
11971201
if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) {
11981202
zend_verify_void_return_error(zf, zend_zval_type_name(ret), "");
11991203
return 0;
@@ -1225,8 +1229,7 @@ static ZEND_COLD int zend_verify_missing_return_type(const zend_function *zf, vo
12251229
zend_arg_info *ret_info = zf->common.arg_info - 1;
12261230

12271231
if (ZEND_TYPE_IS_SET(ret_info->type)
1228-
&& (!ZEND_TYPE_IS_MASK(ret_info->type)
1229-
|| !(ZEND_TYPE_MASK(ret_info->type) & MAY_BE_VOID))) {
1232+
&& !(ZEND_TYPE_MASK(ret_info->type) & MAY_BE_VOID)) {
12301233
zend_class_entry *ce = NULL;
12311234
if (ZEND_TYPE_IS_CLASS(ret_info->type)) {
12321235
if (EXPECTED(*cache_slot)) {
@@ -1576,7 +1579,7 @@ static zend_property_info *zend_get_prop_not_accepting_double(zend_reference *re
15761579
{
15771580
zend_property_info *prop;
15781581
ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) {
1579-
if (!ZEND_TYPE_IS_MASK(prop->type) || !(ZEND_TYPE_MASK(prop->type) & MAY_BE_DOUBLE)) {
1582+
if (!(ZEND_TYPE_MASK(prop->type) & MAY_BE_DOUBLE)) {
15801583
return prop;
15811584
}
15821585
} ZEND_REF_FOREACH_TYPE_SOURCES_END();
@@ -2545,7 +2548,7 @@ static zend_always_inline zend_bool check_type_array_assignable(zend_type type)
25452548
if (!ZEND_TYPE_IS_SET(type)) {
25462549
return 1;
25472550
}
2548-
return ZEND_TYPE_IS_MASK(type) && (ZEND_TYPE_MASK(type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY));
2551+
return (ZEND_TYPE_MASK(type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) != 0;
25492552
}
25502553

25512554
static zend_always_inline zend_bool check_type_stdClass_assignable(zend_type type) {
@@ -3074,9 +3077,9 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference
30743077
if (!seen_prop) {
30753078
seen_prop = prop;
30763079
seen_type_mask = ZEND_TYPE_IS_CLASS(prop->type)
3077-
? MAY_BE_OBJECT : ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop->type));
3080+
? MAY_BE_OBJECT : ZEND_TYPE_MASK_WITHOUT_NULL(prop->type);
30783081
} else if (needs_coercion
3079-
&& seen_type_mask != ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop->type))) {
3082+
&& seen_type_mask != ZEND_TYPE_MASK_WITHOUT_NULL(prop->type)) {
30803083
zend_throw_conflicting_coercion_error(seen_prop, prop, zv);
30813084
return 0;
30823085
}
@@ -3143,8 +3146,8 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_propert
31433146

31443147
if (result < 0) {
31453148
zend_property_info *ref_prop = ZEND_REF_FIRST_SOURCE(Z_REF_P(orig_val));
3146-
if (ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop_info->type))
3147-
!= ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(ref_prop->type))) {
3149+
if (ZEND_TYPE_MASK_WITHOUT_NULL(prop_info->type)
3150+
!= ZEND_TYPE_MASK_WITHOUT_NULL(ref_prop->type)) {
31483151
/* Invalid due to conflicting coercion */
31493152
zend_throw_ref_type_error_type(ref_prop, prop_info, val);
31503153
return 0;

Zend/zend_inheritance.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,7 @@ static inheritance_status zend_perform_covariant_type_check(
378378

379379
return ZEND_TYPE_MASK(fe_type) & MAY_BE_OBJECT ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
380380
} else {
381-
return ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(fe_type))
382-
== ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(proto_type))
381+
return ZEND_TYPE_MASK_WITHOUT_NULL(fe_type) == ZEND_TYPE_MASK_WITHOUT_NULL(proto_type)
383382
? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
384383
}
385384
}
@@ -859,7 +858,8 @@ zend_string* zend_resolve_property_type(zend_string *type, zend_class_entry *sco
859858
zend_bool property_types_compatible(zend_property_info *parent_info, zend_property_info *child_info) {
860859
zend_string *parent_name, *child_name;
861860
zend_class_entry *parent_type_ce, *child_type_ce;
862-
if (parent_info->type == child_info->type) {
861+
if (ZEND_TYPE_MASK(parent_info->type) == ZEND_TYPE_MASK(child_info->type)
862+
&& ZEND_TYPE_NAME(parent_info->type) == ZEND_TYPE_NAME(child_info->type)) {
863863
return 1;
864864
}
865865

Zend/zend_types.h

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -120,73 +120,83 @@ typedef void (*copy_ctor_func_t)(zval *pElement);
120120
* ZEND_TYPE_ENCODE_*() should be used for construction.
121121
*/
122122

123-
typedef uintptr_t zend_type;
124-
125-
#define _ZEND_TYPE_CODE_MAX ((Z_L(1)<<(IS_VOID+1))-1)
126-
#define _ZEND_TYPE_FLAG_MASK Z_L(0x3)
127-
#define _ZEND_TYPE_CE_BIT Z_L(0x1)
123+
/* We could use the extra 32-bit of padding on 64-bit systems. */
124+
typedef struct {
125+
union {
126+
zend_class_entry *ce;
127+
zend_string *name;
128+
const char *literal_name;
129+
};
130+
uint32_t type_mask;
131+
} zend_type;
132+
133+
#define _ZEND_TYPE_MASK ((1u<<(IS_VOID+1))-1)
134+
#define _ZEND_TYPE_CE_BIT (1u << 30)
135+
#define _ZEND_TYPE_NAME_BIT (1u << 31)
128136
/* Must have same value as MAY_BE_NULL */
129-
#define _ZEND_TYPE_NULLABLE_BIT Z_L(0x2)
137+
#define _ZEND_TYPE_NULLABLE_BIT 0x2
130138

131139
#define ZEND_TYPE_IS_SET(t) \
132-
((t) != 0)
140+
((t).type_mask != 0)
133141

134142
#define ZEND_TYPE_IS_MASK(t) \
135-
((t) != 0 && (t) <= _ZEND_TYPE_CODE_MAX)
143+
((t).type_mask != 0 && (t) <= _ZEND_TYPE_CODE_MAX)
136144

137145
#define ZEND_TYPE_IS_CLASS(t) \
138-
((t) > _ZEND_TYPE_CODE_MAX)
146+
(((t.type_mask) & (_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_CE_BIT)) != 0)
139147

140148
#define ZEND_TYPE_IS_CE(t) \
141-
(((t) & _ZEND_TYPE_CE_BIT) != 0)
149+
(((t.type_mask) & _ZEND_TYPE_CE_BIT) != 0)
142150

143151
#define ZEND_TYPE_IS_NAME(t) \
144-
(ZEND_TYPE_IS_CLASS(t) && !ZEND_TYPE_IS_CE(t))
152+
(((t.type_mask) & _ZEND_TYPE_NAME_BIT) != 0)
153+
154+
#define ZEND_TYPE_IS_ONLY_MASK(t) \
155+
((t).type_mask != 0 && (t).ce == NULL)
145156

146157
#define ZEND_TYPE_NAME(t) \
147-
((zend_string*)((t) & ~_ZEND_TYPE_FLAG_MASK))
158+
((t).name)
148159

149160
#define ZEND_TYPE_CE(t) \
150-
((zend_class_entry*)((t) & ~_ZEND_TYPE_FLAG_MASK))
161+
((t).ce)
151162

152163
#define ZEND_TYPE_MASK(t) \
153-
(t)
164+
((t).type_mask)
165+
166+
#define ZEND_TYPE_MASK_WITHOUT_NULL(t) \
167+
((t).type_mask & ~_ZEND_TYPE_NULLABLE_BIT)
154168

155169
#define ZEND_TYPE_CONTAINS_CODE(t, code) \
156-
(((t) & (1 << (code))) != 0)
170+
(((t).type_mask & (1u << (code))) != 0)
157171

158172
#define ZEND_TYPE_ALLOW_NULL(t) \
159-
(((t) & _ZEND_TYPE_NULLABLE_BIT) != 0)
160-
161-
#define ZEND_TYPE_WITHOUT_NULL(t) \
162-
((t) & ~_ZEND_TYPE_NULLABLE_BIT)
173+
(((t).type_mask & _ZEND_TYPE_NULLABLE_BIT) != 0)
163174

164175
#define ZEND_TYPE_ENCODE_NONE() \
165-
(0)
176+
(zend_type) {{0}}
166177

167178
#define ZEND_TYPE_ENCODE_MASK(maybe_code) \
168-
(maybe_code)
179+
(zend_type) { {}, (maybe_code) }
169180

170181
#define ZEND_TYPE_ENCODE_CODE(code, allow_null) \
171-
(((code) == _IS_BOOL ? (MAY_BE_FALSE|MAY_BE_TRUE) : (1 << (code))) \
172-
| ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : Z_L(0x0)))
182+
ZEND_TYPE_ENCODE_MASK(((code) == _IS_BOOL ? (MAY_BE_FALSE|MAY_BE_TRUE) : (1 << (code))) \
183+
| ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0))
173184

174-
#define ZEND_TYPE_ENCODE_CE(ce, allow_null) \
175-
(((uintptr_t)(ce)) | _ZEND_TYPE_CE_BIT | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : Z_L(0x0)))
185+
#define ZEND_TYPE_ENCODE_CE(_ce, allow_null) \
186+
(zend_type) { \
187+
{ .ce = (_ce) }, \
188+
_ZEND_TYPE_CE_BIT | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) \
189+
}
176190

177191
#define ZEND_TYPE_ENCODE_CLASS(class_name, allow_null) \
178-
(((uintptr_t)(class_name)) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : Z_L(0x0)))
179-
180-
#define ZEND_TYPE_ENCODE_CLASS_CONST_0(class_name) \
181-
((zend_type) class_name)
182-
#define ZEND_TYPE_ENCODE_CLASS_CONST_1(class_name) \
183-
((zend_type) "?" class_name)
184-
#define ZEND_TYPE_ENCODE_CLASS_CONST_Q2(macro, class_name) \
185-
macro(class_name)
186-
#define ZEND_TYPE_ENCODE_CLASS_CONST_Q1(allow_null, class_name) \
187-
ZEND_TYPE_ENCODE_CLASS_CONST_Q2(ZEND_TYPE_ENCODE_CLASS_CONST_ ##allow_null, class_name)
192+
(zend_type) { \
193+
{ .name = (class_name) }, \
194+
_ZEND_TYPE_NAME_BIT | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) \
195+
}
196+
188197
#define ZEND_TYPE_ENCODE_CLASS_CONST(class_name, allow_null) \
189-
ZEND_TYPE_ENCODE_CLASS_CONST_Q1(allow_null, class_name)
198+
{ { .literal_name = (class_name) }, \
199+
_ZEND_TYPE_NAME_BIT | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) }
190200

191201
typedef union _zend_value {
192202
zend_long lval; /* long value */

ext/opcache/Optimizer/dfa_pass.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,7 @@ static inline zend_bool can_elide_return_type_check(
324324
}
325325

326326
/* These types are not represented exactly */
327-
if (ZEND_TYPE_IS_MASK(info->type)
328-
&& (ZEND_TYPE_MASK(info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))) {
327+
if (ZEND_TYPE_MASK(info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE)) {
329328
return 0;
330329
}
331330

0 commit comments

Comments
 (0)