summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gdbinit23
-rw-r--r--Zend/tests/type_declarations/typed_properties_054.phpt2
-rw-r--r--Zend/zend_API.h11
-rw-r--r--Zend/zend_compile.c183
-rw-r--r--Zend/zend_compile.h3
-rw-r--r--Zend/zend_execute.c320
-rw-r--r--Zend/zend_execute.h5
-rw-r--r--Zend/zend_inheritance.c51
-rw-r--r--Zend/zend_string.h6
-rw-r--r--Zend/zend_type_info.h15
-rw-r--r--Zend/zend_types.h76
-rw-r--r--Zend/zend_vm_def.h5
-rw-r--r--Zend/zend_vm_execute.h25
-rw-r--r--ext/com_dotnet/com_handlers.c2
-rw-r--r--ext/opcache/Optimizer/dfa_pass.c5
-rw-r--r--ext/opcache/Optimizer/zend_inference.c65
-rw-r--r--ext/opcache/Optimizer/zend_inference.h6
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c4
-rw-r--r--ext/opcache/jit/zend_jit_helpers.c190
-rw-r--r--ext/opcache/jit/zend_jit_x86.dasc62
-rw-r--r--ext/reflection/php_reflection.c54
-rw-r--r--ext/standard/var.c21
-rw-r--r--ext/zend_test/test.c4
23 files changed, 496 insertions, 642 deletions
diff --git a/.gdbinit b/.gdbinit
index 8e54830ef2..a903c086e5 100644
--- a/.gdbinit
+++ b/.gdbinit
@@ -252,37 +252,34 @@ define ____printzv_contents
____printzv &$zvalue->value.ref->val $arg1
end
if $type == 11
- printf "const: %s", $zvalue->value.str->val
+ printf "CONSTANT_AST"
end
if $type == 12
- printf "CONSTANT_AST"
+ printf "CALLABLE"
end
if $type == 13
- printf "indirect: "
- ____printzv $zvalue->value.zv $arg1
+ printf "ITERABLE"
end
if $type == 14
- printf "pointer: %p", $zvalue->value.ptr
+ printf "VOID"
end
if $type == 15
- printf "_ERROR"
+ printf "indirect: "
+ ____printzv $zvalue->value.zv $arg1
end
if $type == 16
- printf "_BOOL"
+ printf "pointer: %p", $zvalue->value.ptr
end
if $type == 17
- printf "CALLABLE"
+ printf "_ERROR"
end
if $type == 18
- printf "ITERABLE"
+ printf "_BOOL"
end
if $type == 19
- printf "VOID"
- end
- if $type == 20
printf "_NUMBER"
end
- if $type > 20
+ if $type > 19
printf "unknown type %d", $type
end
printf "\n"
diff --git a/Zend/tests/type_declarations/typed_properties_054.phpt b/Zend/tests/type_declarations/typed_properties_054.phpt
index 38de9815ed..cb771d6e64 100644
--- a/Zend/tests/type_declarations/typed_properties_054.phpt
+++ b/Zend/tests/type_declarations/typed_properties_054.phpt
@@ -9,4 +9,4 @@ $obj = new A;
var_dump($obj);
?>
--EXPECTF--
-Fatal error: Property A::$a cannot have type callable in %s on line %d
+Fatal error: Property A::$a cannot have type ?callable in %s on line %d
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 5cdba4450b..acc6bb9cf3 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -27,6 +27,7 @@
#include "zend_operators.h"
#include "zend_variables.h"
#include "zend_execute.h"
+#include "zend_type_info.h"
BEGIN_EXTERN_C()
@@ -98,11 +99,11 @@ typedef struct _zend_fcall_info_cache {
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 0},
#define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, 0, pass_by_ref, 0},
#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 },
-#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE(IS_ARRAY, allow_null), pass_by_ref, 0 },
-#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE(IS_CALLABLE, allow_null), pass_by_ref, 0 },
-#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE(type_hint, allow_null), pass_by_ref, 0 },
+#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(IS_ARRAY, allow_null), pass_by_ref, 0 },
+#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(IS_CALLABLE, allow_null), pass_by_ref, 0 },
+#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 },
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 1 },
-#define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE(type_hint, allow_null), pass_by_ref, 1 },
+#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 },
#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 },
#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \
@@ -114,7 +115,7 @@ typedef struct _zend_fcall_info_cache {
#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \
static const zend_internal_arg_info name[] = { \
- { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE(type, allow_null), return_reference, 0 },
+ { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE_CODE(type, allow_null), return_reference, 0 },
#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(name, type, allow_null) \
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, 0, -1, type, allow_null)
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 19992e2cbd..986584c690 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -1080,6 +1080,70 @@ ZEND_API int do_bind_class(zval *lcname, zend_string *lc_parent_name) /* {{{ */
}
/* }}} */
+zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope) {
+ zend_bool nullable = ZEND_TYPE_ALLOW_NULL(type);
+ zend_string *str;
+ if (ZEND_TYPE_IS_NAME(type)) {
+ zend_string *name = ZEND_TYPE_NAME(type);
+ if (scope) {
+ if (zend_string_equals_literal_ci(name, "self")) {
+ name = scope->name;
+ } else if (zend_string_equals_literal_ci(name, "parent") && scope->parent) {
+ name = scope->parent->name;
+ }
+ }
+ str = zend_string_copy(name);
+ } else if (ZEND_TYPE_IS_CE(type)) {
+ str = zend_string_copy(ZEND_TYPE_CE(type)->name);
+ } else {
+ uint32_t type_mask = ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(type));
+ switch (type_mask) {
+ case MAY_BE_FALSE|MAY_BE_TRUE:
+ str = ZSTR_KNOWN(ZEND_STR_BOOL);
+ break;
+ case MAY_BE_LONG:
+ str = ZSTR_KNOWN(ZEND_STR_INT);
+ break;
+ case MAY_BE_DOUBLE:
+ str = ZSTR_KNOWN(ZEND_STR_FLOAT);
+ break;
+ case MAY_BE_STRING:
+ str = ZSTR_KNOWN(ZEND_STR_STRING);
+ break;
+ case MAY_BE_ARRAY:
+ str = ZSTR_KNOWN(ZEND_STR_ARRAY);
+ break;
+ case MAY_BE_OBJECT:
+ str = ZSTR_KNOWN(ZEND_STR_OBJECT);
+ break;
+ case MAY_BE_CALLABLE:
+ str = ZSTR_KNOWN(ZEND_STR_CALLABLE);
+ break;
+ case MAY_BE_ITERABLE:
+ str = ZSTR_KNOWN(ZEND_STR_ITERABLE);
+ break;
+ case MAY_BE_VOID:
+ str = ZSTR_KNOWN(ZEND_STR_VOID);
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ }
+
+ if (nullable) {
+ zend_string *nullable_str = zend_string_alloc(ZSTR_LEN(str) + 1, 0);
+ ZSTR_VAL(nullable_str)[0] = '?';
+ memcpy(ZSTR_VAL(nullable_str) + 1, ZSTR_VAL(str), ZSTR_LEN(str));
+ ZSTR_VAL(nullable_str)[ZSTR_LEN(nullable_str)] = '\0';
+ zend_string_release(str);
+ return nullable_str;
+ }
+ return str;
+}
+
+zend_string *zend_type_to_string(zend_type type) {
+ return zend_type_to_string_resolved(type, NULL);
+}
+
static void zend_mark_function_as_generator() /* {{{ */
{
if (!CG(active_op_array)->function_name) {
@@ -1089,19 +1153,22 @@ static void zend_mark_function_as_generator() /* {{{ */
if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
zend_arg_info return_info = CG(active_op_array)->arg_info[-1];
+ zend_bool valid_type;
+ if (ZEND_TYPE_IS_CLASS(return_info.type)) {
+ zend_string *name = ZEND_TYPE_NAME(return_info.type);
+ valid_type = zend_string_equals_literal_ci(name, "Traversable")
+ || zend_string_equals_literal_ci(name, "Iterator")
+ || zend_string_equals_literal_ci(name, "Generator");
+ } else {
+ valid_type = (ZEND_TYPE_MASK(return_info.type) & MAY_BE_ITERABLE) != 0;
+ }
- if (ZEND_TYPE_CODE(return_info.type) != IS_ITERABLE) {
- const char *msg = "Generators may only declare a return type of Generator, Iterator, Traversable, or iterable, %s is not permitted";
-
- if (!ZEND_TYPE_IS_CLASS(return_info.type)) {
- zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(ZEND_TYPE_CODE(return_info.type)));
- }
-
- if (!zend_string_equals_literal_ci(ZEND_TYPE_NAME(return_info.type), "Traversable")
- && !zend_string_equals_literal_ci(ZEND_TYPE_NAME(return_info.type), "Iterator")
- && !zend_string_equals_literal_ci(ZEND_TYPE_NAME(return_info.type), "Generator")) {
- zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(ZEND_TYPE_NAME(return_info.type)));
- }
+ if (!valid_type) {
+ zend_string *str = zend_type_to_string(return_info.type);
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Generators may only declare a return type of " \
+ "Generator, Iterator, Traversable, or iterable, %s is not permitted",
+ ZSTR_VAL(str));
}
}
@@ -2051,11 +2118,12 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */
static void zend_emit_return_type_check(
znode *expr, zend_arg_info *return_info, zend_bool implicit) /* {{{ */
{
- if (ZEND_TYPE_IS_SET(return_info->type)) {
+ zend_type type = return_info->type;
+ if (ZEND_TYPE_IS_SET(type)) {
zend_op *opline;
/* `return ...;` is illegal in a void function (but `return;` isn't) */
- if (ZEND_TYPE_CODE(return_info->type) == IS_VOID) {
+ if (ZEND_TYPE_IS_MASK(type) && ZEND_TYPE_CONTAINS_CODE(type, IS_VOID)) {
if (expr) {
if (expr->op_type == IS_CONST && Z_TYPE(expr->u.constant) == IS_NULL) {
zend_error_noreturn(E_COMPILE_ERROR,
@@ -2070,7 +2138,7 @@ static void zend_emit_return_type_check(
}
if (!expr && !implicit) {
- if (ZEND_TYPE_ALLOW_NULL(return_info->type)) {
+ if (ZEND_TYPE_ALLOW_NULL(type)) {
zend_error_noreturn(E_COMPILE_ERROR,
"A function with return type must return a value "
"(did you mean \"return null;\" instead of \"return;\"?)");
@@ -2081,12 +2149,8 @@ static void zend_emit_return_type_check(
}
if (expr && expr->op_type == IS_CONST) {
- if ((ZEND_TYPE_CODE(return_info->type) == Z_TYPE(expr->u.constant))
- ||((ZEND_TYPE_CODE(return_info->type) == _IS_BOOL)
- && (Z_TYPE(expr->u.constant) == IS_FALSE
- || Z_TYPE(expr->u.constant) == IS_TRUE))
- || (ZEND_TYPE_ALLOW_NULL(return_info->type)
- && Z_TYPE(expr->u.constant) == IS_NULL)) {
+ if (ZEND_TYPE_IS_MASK(type)
+ && ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE(expr->u.constant))) {
/* we don't need run-time check */
return;
}
@@ -5248,7 +5312,7 @@ static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null
}
if (ast->kind == ZEND_AST_TYPE) {
- return ZEND_TYPE_ENCODE(ast->attr, allow_null);
+ return ZEND_TYPE_ENCODE_CODE(ast->attr, allow_null);
} else {
zend_string *class_name = zend_ast_get_str(ast);
zend_uchar type = zend_lookup_builtin_type_by_name(class_name);
@@ -5259,7 +5323,7 @@ static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null
"Type declaration '%s' must be unqualified",
ZSTR_VAL(zend_string_tolower(class_name)));
}
- return ZEND_TYPE_ENCODE(type, allow_null);
+ return ZEND_TYPE_ENCODE_CODE(type, allow_null);
} else {
uint32_t fetch_type = zend_get_class_fetch_type_ast(ast);
if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
@@ -5291,7 +5355,9 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
arg_infos->is_variadic = 0;
arg_infos->type = zend_compile_typename(return_type_ast, 0);
- if (ZEND_TYPE_CODE(arg_infos->type) == IS_VOID && ZEND_TYPE_ALLOW_NULL(arg_infos->type)) {
+ if (ZEND_TYPE_IS_MASK(arg_infos->type)
+ && ZEND_TYPE_CONTAINS_CODE(arg_infos->type, IS_VOID)
+ && ZEND_TYPE_ALLOW_NULL(arg_infos->type)) {
zend_error_noreturn(E_COMPILE_ERROR, "Void type cannot be nullable");
}
@@ -5369,7 +5435,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
arg_info->pass_by_reference = is_ref;
arg_info->is_variadic = is_variadic;
/* TODO: Keep compatibility, but may be better reset "allow_null" ??? */
- arg_info->type = ZEND_TYPE_ENCODE(0, 1);
+ arg_info->type = ZEND_TYPE_ENCODE_CODE(0, 1);
if (type_ast) {
uint32_t default_type = default_ast ? Z_TYPE(default_node.u.constant) : IS_UNDEF;
@@ -5381,9 +5447,9 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
arg_info->type = zend_compile_typename(type_ast, default_type == IS_NULL);
is_class = ZEND_TYPE_IS_CLASS(arg_info->type);
- arg_type = ZEND_TYPE_CODE(arg_info->type);
+ arg_type = !is_class ? ZEND_TYPE_MASK(arg_info->type) : 0;
- if (arg_type == IS_VOID) {
+ if (arg_type & MAY_BE_VOID) {
zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type");
}
@@ -5391,45 +5457,36 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
if (is_class) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with a class type can only be NULL");
- } else switch (arg_type) {
- case IS_CALLABLE:
+ } else {
+ if (arg_type & MAY_BE_CALLABLE) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with callable type can only be NULL");
- break;
-
- case IS_ARRAY:
+ } else if (arg_type & MAY_BE_ARRAY) {
if (default_type != IS_ARRAY) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with array type can only be an array or NULL");
}
- break;
-
- case IS_DOUBLE:
+ } else if (arg_type & MAY_BE_DOUBLE) {
if (default_type != IS_DOUBLE && default_type != IS_LONG) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with a float type can only be float, integer, or NULL");
}
- break;
-
- case IS_ITERABLE:
+ } else if (arg_type & MAY_BE_ITERABLE) {
if (default_type != IS_ARRAY) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with iterable type can only be an array or NULL");
}
- break;
-
- case IS_OBJECT:
+ } else if (arg_type & MAY_BE_OBJECT) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
"with an object type can only be NULL");
- break;
-
- default:
- if (!ZEND_SAME_FAKE_TYPE(arg_type, default_type)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
- "with a %s type can only be %s or NULL",
- zend_get_type_by_const(arg_type), zend_get_type_by_const(arg_type));
- }
- break;
+ } else if (!ZEND_TYPE_CONTAINS_CODE(arg_type, default_type)) {
+ zend_string *type_str =
+ zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(arg_type));
+ zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters "
+ "with a %s type can only be %s or NULL",
+ ZSTR_VAL(type_str), ZSTR_VAL(type_str));
+ zend_string_release(type_str);
+ }
}
}
@@ -5953,12 +6010,12 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) /
if (type_ast) {
type = zend_compile_typename(type_ast, 0);
- if (ZEND_TYPE_CODE(type) == IS_VOID || ZEND_TYPE_CODE(type) == IS_CALLABLE) {
+ if (ZEND_TYPE_IS_MASK(type)
+ && (ZEND_TYPE_MASK(type) & (MAY_BE_VOID|MAY_BE_CALLABLE))) {
+ zend_string *str = zend_type_to_string(type);
zend_error_noreturn(E_COMPILE_ERROR,
"Property %s::$%s cannot have type %s",
- ZSTR_VAL(ce->name),
- ZSTR_VAL(name),
- zend_get_type_by_const(ZEND_TYPE_CODE(type)));
+ ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(str));
}
}
@@ -5984,32 +6041,32 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) /
if (ZEND_TYPE_IS_SET(type) && !Z_CONSTANT(value_zv)) {
if (Z_TYPE(value_zv) == IS_NULL) {
if (!ZEND_TYPE_ALLOW_NULL(type)) {
- const char *name = ZEND_TYPE_IS_CLASS(type)
- ? ZSTR_VAL(ZEND_TYPE_NAME(type)) : zend_get_type_by_const(ZEND_TYPE_CODE(type));
+ zend_string *str = zend_type_to_string(type);
zend_error_noreturn(E_COMPILE_ERROR,
"Default value for property of type %s may not be null. "
"Use the nullable type ?%s to allow null default value",
- name, name);
+ ZSTR_VAL(str), ZSTR_VAL(str));
}
} else if (ZEND_TYPE_IS_CLASS(type)) {
zend_error_noreturn(E_COMPILE_ERROR,
"Property of type %s may not have default value", ZSTR_VAL(ZEND_TYPE_NAME(type)));
- } else if (ZEND_TYPE_CODE(type) == IS_ARRAY || ZEND_TYPE_CODE(type) == IS_ITERABLE) {
+ } else if (ZEND_TYPE_MASK(type) & (MAY_BE_ARRAY|MAY_BE_ITERABLE)) {
if (Z_TYPE(value_zv) != IS_ARRAY) {
+ zend_string *str = zend_type_to_string(type);
zend_error_noreturn(E_COMPILE_ERROR,
"Default value for property of type %s can only be an array",
- zend_get_type_by_const(ZEND_TYPE_CODE(type)));
+ ZSTR_VAL(str));
}
- } else if (ZEND_TYPE_CODE(type) == IS_DOUBLE) {
+ } else if (ZEND_TYPE_MASK(type) & MAY_BE_DOUBLE) {
if (Z_TYPE(value_zv) != IS_DOUBLE && Z_TYPE(value_zv) != IS_LONG) {
zend_error_noreturn(E_COMPILE_ERROR,
"Default value for property of type float can only be float or int");
}
- } else if (!ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(type), Z_TYPE(value_zv))) {
+ } else if (!ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE(value_zv))) {
+ zend_string *str = zend_type_to_string(type);
zend_error_noreturn(E_COMPILE_ERROR,
"Default value for property of type %s can only be %s",
- zend_get_type_by_const(ZEND_TYPE_CODE(type)),
- zend_get_type_by_const(ZEND_TYPE_CODE(type)));
+ ZSTR_VAL(str), ZSTR_VAL(str));
}
}
} else if (!ZEND_TYPE_IS_SET(type)) {
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index a960115aab..c4c2b8bac7 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -847,6 +847,9 @@ int ZEND_FASTCALL zendlex(zend_parser_stack_elem *elem);
void zend_assert_valid_class_name(const zend_string *const_name);
+zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope);
+zend_string *zend_type_to_string(zend_type type);
+
/* BEGIN: OPCODES */
#include "zend_vm_opcodes.h"
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 1d628a2352..cf32ba55ac 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -582,39 +582,26 @@ static zend_never_inline ZEND_COLD int zend_wrong_assign_to_variable_reference(z
return 1;
}
-static void zend_format_type(zend_type type, const char **part1, const char **part2) {
- *part1 = ZEND_TYPE_ALLOW_NULL(type) ? "?" : "";
- if (ZEND_TYPE_IS_CLASS(type)) {
- if (ZEND_TYPE_IS_CE(type)) {
- *part2 = ZSTR_VAL(ZEND_TYPE_CE(type)->name);
- } else {
- *part2 = ZSTR_VAL(ZEND_TYPE_NAME(type));
- }
- } else {
- *part2 = zend_get_type_by_const(ZEND_TYPE_CODE(type));
- }
-}
-
static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop, const char *type) {
- const char *prop_type1, *prop_type2;
- zend_format_type(prop->type, &prop_type1, &prop_type2);
+ zend_string *type_str = zend_type_to_string(prop->type);
zend_type_error(
- "Cannot auto-initialize an %s inside property %s::$%s of type %s%s",
+ "Cannot auto-initialize an %s inside property %s::$%s of type %s",
type,
ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name),
- prop_type1, prop_type2
+ ZSTR_VAL(type_str)
);
+ zend_string_release(type_str);
}
static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(zend_property_info *prop, const char *type) {
- const char *prop_type1, *prop_type2;
- zend_format_type(prop->type, &prop_type1, &prop_type2);
+ zend_string *type_str = zend_type_to_string(prop->type);
zend_type_error(
- "Cannot auto-initialize an %s inside a reference held by property %s::$%s of type %s%s",
+ "Cannot auto-initialize an %s inside a reference held by property %s::$%s of type %s",
type,
ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name),
- prop_type1, prop_type2
+ ZSTR_VAL(type_str)
);
+ zend_string_release(type_str);
}
static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_error(
@@ -724,22 +711,25 @@ static ZEND_COLD void zend_verify_type_error_common(
*need_kind = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type));
}
} else {
- switch (ZEND_TYPE_CODE(arg_info->type)) {
- case IS_OBJECT:
+ zend_type type = ZEND_TYPE_WITHOUT_NULL(arg_info->type);
+ switch (ZEND_TYPE_MASK(type)) {
+ case MAY_BE_OBJECT:
*need_msg = "be an ";
*need_kind = "object";
break;
- case IS_CALLABLE:
+ case MAY_BE_CALLABLE:
*need_msg = "be callable";
*need_kind = "";
break;
- case IS_ITERABLE:
+ case MAY_BE_ITERABLE:
*need_msg = "be iterable";
*need_kind = "";
break;
default:
+ /* TODO: The zend_type_to_string() result is guaranteed interned here.
+ * It would be beter to switch all this code to use zend_string though. */
*need_msg = "be of the type ";
- *need_kind = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type));
+ *need_kind = ZSTR_VAL(zend_type_to_string(type));
break;
}
}
@@ -764,7 +754,7 @@ static ZEND_COLD void zend_verify_type_error_common(
}
}
-static ZEND_COLD void zend_verify_arg_error(
+ZEND_API ZEND_COLD void zend_verify_arg_error(
const zend_function *zf, const zend_arg_info *arg_info,
int arg_num, const zend_class_entry *ce, zval *value)
{
@@ -793,105 +783,100 @@ static ZEND_COLD void zend_verify_arg_error(
}
}
-static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg)
+static zend_bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg)
{
- switch (type_hint) {
- case _IS_BOOL: {
- zend_bool dest;
+ if (type_mask & (MAY_BE_TRUE|MAY_BE_FALSE)) {
+ zend_bool dest;
- if (!zend_parse_arg_bool_weak(arg, &dest)) {
- return 0;
- }
- zval_ptr_dtor(arg);
- ZVAL_BOOL(arg, dest);
- return 1;
+ if (!zend_parse_arg_bool_weak(arg, &dest)) {
+ return 0;
}
- case IS_LONG: {
- zend_long dest;
+ zval_ptr_dtor(arg);
+ ZVAL_BOOL(arg, dest);
+ return 1;
+ }
+ if (type_mask & MAY_BE_LONG) {
+ zend_long dest;
- if (!zend_parse_arg_long_weak(arg, &dest)) {
- return 0;
- }
- zval_ptr_dtor(arg);
- ZVAL_LONG(arg, dest);
- return 1;
+ if (!zend_parse_arg_long_weak(arg, &dest)) {
+ return 0;
}
- case IS_DOUBLE: {
- double dest;
+ zval_ptr_dtor(arg);
+ ZVAL_LONG(arg, dest);
+ return 1;
+ }
+ if (type_mask & MAY_BE_DOUBLE) {
+ double dest;
- if (!zend_parse_arg_double_weak(arg, &dest)) {
- return 0;
- }
- zval_ptr_dtor(arg);
- ZVAL_DOUBLE(arg, dest);
- return 1;
+ if (!zend_parse_arg_double_weak(arg, &dest)) {
+ return 0;
}
- case IS_STRING: {
- zend_string *dest;
+ zval_ptr_dtor(arg);
+ ZVAL_DOUBLE(arg, dest);
+ return 1;
+ }
+ if (type_mask & MAY_BE_STRING) {
+ zend_string *dest;
- /* on success "arg" is converted to IS_STRING */
- return zend_parse_arg_str_weak(arg, &dest);
- }
- default:
- return 0;
+ /* on success "arg" is converted to IS_STRING */
+ return zend_parse_arg_str_weak(arg, &dest);
}
+ return 0;
}
#if ZEND_DEBUG
/* Used to sanity-check internal arginfo types without performing any actual type conversions. */
-static zend_bool zend_verify_weak_scalar_type_hint_no_sideeffect(zend_uchar type_hint, zval *arg)
-{
- switch (type_hint) {
- case _IS_BOOL: {
- zend_bool dest;
- return zend_parse_arg_bool_weak(arg, &dest);
- }
- case IS_LONG: {
- zend_long dest;
- if (Z_TYPE_P(arg) == IS_STRING) {
- /* Handle this case separately to avoid the "non well-formed" warning */
- double dval;
- zend_uchar type = is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, &dval, 1);
- if (type == IS_LONG) {
- return 1;
- }
- if (type == IS_DOUBLE) {
- return !zend_isnan(dval) && ZEND_DOUBLE_FITS_LONG(dval);
+static zend_bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_mask, zval *arg)
+{
+ if (type_mask & (MAY_BE_TRUE|MAY_BE_FALSE)) {
+ zend_bool dest;
+ return zend_parse_arg_bool_weak(arg, &dest);
+ }
+ if (type_mask & MAY_BE_LONG) {
+ zend_long dest;
+ if (Z_TYPE_P(arg) == IS_STRING) {
+ /* Handle this case separately to avoid the "non well-formed" warning */
+ double dval;
+ zend_uchar type = is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, &dval, 1);
+ if (type == IS_LONG) {
+ return 1;
+ }
+ if (type == IS_DOUBLE) {
+ return !zend_isnan(dval) && ZEND_DOUBLE_FITS_LONG(dval);
- }
- return 0;
}
- return zend_parse_arg_long_weak(arg, &dest);
+ return 0;
}
- case IS_DOUBLE: {
- double dest;
- if (Z_TYPE_P(arg) == IS_STRING) {
- /* Handle this case separately to avoid the "non well-formed" warning */
- return is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, NULL, 1) != 0;
- }
- return zend_parse_arg_double_weak(arg, &dest);
+ return zend_parse_arg_long_weak(arg, &dest);
+ }
+ if (type_mask & MAY_BE_DOUBLE) {
+ double dest;
+ if (Z_TYPE_P(arg) == IS_STRING) {
+ /* Handle this case separately to avoid the "non well-formed" warning */
+ return is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, NULL, 1) != 0;
}
- case IS_STRING:
- /* We don't call cast_object here, because this check must be side-effect free. As this
- * is only used for a sanity check of arginfo/zpp consistency, it's okay if we accept
- * more than actually allowed here. */
- return Z_TYPE_P(arg) < IS_STRING || Z_TYPE_P(arg) == IS_OBJECT;
- default:
- return 0;
+ return zend_parse_arg_double_weak(arg, &dest);
+ }
+ if (type_mask & MAY_BE_STRING) {
+ /* We don't call cast_object here, because this check must be side-effect free. As this
+ * is only used for a sanity check of arginfo/zpp consistency, it's okay if we accept
+ * more than actually allowed here. */
+ return Z_TYPE_P(arg) < IS_STRING || Z_TYPE_P(arg) == IS_OBJECT;
}
+ return 0;
}
#endif
-static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, zend_bool strict, zend_bool is_internal_arg)
+ZEND_API zend_bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, zend_bool strict, zend_bool is_internal_arg)
{
if (UNEXPECTED(strict)) {
/* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */
- if (type_hint != IS_DOUBLE || Z_TYPE_P(arg) != IS_LONG) {
+ if (!(type_mask & MAY_BE_DOUBLE) || Z_TYPE_P(arg) != IS_LONG) {
return 0;
}
} else if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) {
/* NULL may be accepted only by nullable hints (this is already checked) */
- if (is_internal_arg && (type_hint <= IS_STRING || type_hint == _IS_BOOL)) {
+ if (is_internal_arg && (type_mask & (MAY_BE_TRUE|MAY_BE_FALSE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING))) {
/* As an exception, null is allowed for scalar types in weak mode. */
return 1;
}
@@ -899,15 +884,15 @@ static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, z
}
#if ZEND_DEBUG
if (is_internal_arg) {
- return zend_verify_weak_scalar_type_hint_no_sideeffect(type_hint, arg);
+ return zend_verify_weak_scalar_type_hint_no_sideeffect(type_mask, arg);
}
#endif
- return zend_verify_weak_scalar_type_hint(type_hint, arg);
+ return zend_verify_weak_scalar_type_hint(type_mask, arg);
}
ZEND_COLD zend_never_inline void zend_verify_property_type_error(zend_property_info *info, zval *property)
{
- const char *prop_type1, *prop_type2;
+ zend_string *type_str;
/* we _may_ land here in case reading already errored and runtime cache thus has not been updated (i.e. it contains a valid but unrelated info) */
if (EG(exception)) {
@@ -915,23 +900,23 @@ ZEND_COLD zend_never_inline void zend_verify_property_type_error(zend_property_i
}
// TODO Switch to a more standard error message?
- zend_format_type(info->type, &prop_type1, &prop_type2);
- (void) prop_type1;
+ type_str = zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(info->type));
if (ZEND_TYPE_IS_CLASS(info->type)) {
zend_type_error("Typed property %s::$%s must be an instance of %s%s, %s used",
ZSTR_VAL(info->ce->name),
zend_get_unmangled_property_name(info->name),
- prop_type2,
+ ZSTR_VAL(type_str),
ZEND_TYPE_ALLOW_NULL(info->type) ? " or null" : "",
Z_TYPE_P(property) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(property)->name) : zend_get_type_by_const(Z_TYPE_P(property)));
} else {
zend_type_error("Typed property %s::$%s must be %s%s, %s used",
ZSTR_VAL(info->ce->name),
zend_get_unmangled_property_name(info->name),
- prop_type2,
+ ZSTR_VAL(type_str),
ZEND_TYPE_ALLOW_NULL(info->type) ? " or null" : "",
Z_TYPE_P(property) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(property)->name) : zend_get_type_by_const(Z_TYPE_P(property)));
}
+ zend_string_release(type_str);
}
static zend_bool zend_resolve_class_type(zend_type *type, zend_class_entry *self_ce) {
@@ -979,17 +964,13 @@ static zend_always_inline zend_bool i_zend_check_property_type(zend_property_inf
return instanceof_function(Z_OBJCE_P(property), ZEND_TYPE_CE(info->type));
}
- ZEND_ASSERT(ZEND_TYPE_CODE(info->type) != IS_CALLABLE);
- if (EXPECTED(ZEND_TYPE_CODE(info->type) == Z_TYPE_P(property))) {
- return 1;
- } else if (EXPECTED(Z_TYPE_P(property) == IS_NULL)) {
- return ZEND_TYPE_ALLOW_NULL(info->type);
- } else if (ZEND_TYPE_CODE(info->type) == _IS_BOOL && EXPECTED(Z_TYPE_P(property) == IS_FALSE || Z_TYPE_P(property) == IS_TRUE)) {
+ ZEND_ASSERT(!(ZEND_TYPE_MASK(info->type) & MAY_BE_CALLABLE));
+ if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(info->type, Z_TYPE_P(property)))) {
return 1;
- } else if (ZEND_TYPE_CODE(info->type) == IS_ITERABLE) {
+ } else if (ZEND_TYPE_MASK(info->type) & MAY_BE_ITERABLE) {
return zend_is_iterable(property);
} else {
- return zend_verify_scalar_type_hint(ZEND_TYPE_CODE(info->type), property, strict, 0);
+ return zend_verify_scalar_type_hint(ZEND_TYPE_MASK(info->type), property, strict, 0);
}
}
@@ -1030,6 +1011,7 @@ static zend_always_inline zend_bool zend_check_type(
zend_bool is_return_type, zend_bool is_internal)
{
zend_reference *ref = NULL;
+ uint32_t type_mask;
if (!ZEND_TYPE_IS_SET(type)) {
return 1;
@@ -1054,22 +1036,15 @@ static zend_always_inline zend_bool zend_check_type(
return instanceof_function(Z_OBJCE_P(arg), *ce);
}
return Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(type);
- } else if (EXPECTED(ZEND_TYPE_CODE(type) == Z_TYPE_P(arg))) {
- return 1;
- }
-
- if (Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(type)) {
- /* Null passed to nullable type */
+ } else if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(arg)))) {
return 1;
}
- if (ZEND_TYPE_CODE(type) == IS_CALLABLE) {
+ type_mask = ZEND_TYPE_MASK(type);
+ if (type_mask & MAY_BE_CALLABLE) {
return zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL);
- } else if (ZEND_TYPE_CODE(type) == IS_ITERABLE) {
+ } else if (type_mask & MAY_BE_ITERABLE) {
return zend_is_iterable(arg);
- } else if (ZEND_TYPE_CODE(type) == _IS_BOOL &&
- EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) {
- return 1;
} else if (ref && ZEND_REF_HAS_TYPE_SOURCES(ref)) {
return 0; /* we cannot have conversions for typed refs */
} else if (is_internal && is_return_type) {
@@ -1078,7 +1053,7 @@ static zend_always_inline zend_bool zend_check_type(
* apply coercions. */
return 0;
} else {
- return zend_verify_scalar_type_hint(ZEND_TYPE_CODE(type), arg,
+ return zend_verify_scalar_type_hint(type_mask, arg,
is_return_type ? ZEND_RET_USES_STRICT_TYPES() : ZEND_ARG_USES_STRICT_TYPES(),
is_internal);
}
@@ -1229,7 +1204,7 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
zend_class_entry *ce = NULL;
void *dummy_cache_slot = NULL;
- if (ZEND_TYPE_CODE(ret_info->type) == IS_VOID) {
+ if (ZEND_TYPE_IS_MASK(ret_info->type) && (ZEND_TYPE_MASK(ret_info->type) & MAY_BE_VOID)) {
if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) {
zend_verify_void_return_error(zf, zend_zval_type_name(ret), "");
return 0;
@@ -1260,7 +1235,9 @@ static ZEND_COLD int zend_verify_missing_return_type(const zend_function *zf, vo
{
zend_arg_info *ret_info = zf->common.arg_info - 1;
- if (ZEND_TYPE_IS_SET(ret_info->type) && UNEXPECTED(ZEND_TYPE_CODE(ret_info->type) != IS_VOID)) {
+ if (ZEND_TYPE_IS_SET(ret_info->type)
+ && (!ZEND_TYPE_IS_MASK(ret_info->type)
+ || !(ZEND_TYPE_MASK(ret_info->type) & MAY_BE_VOID))) {
zend_class_entry *ce = NULL;
if (ZEND_TYPE_IS_CLASS(ret_info->type)) {
if (EXPECTED(*cache_slot)) {
@@ -1610,7 +1587,7 @@ static zend_property_info *zend_get_prop_not_accepting_double(zend_reference *re
{
zend_property_info *prop;
ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) {
- if (ZEND_TYPE_CODE(prop->type) != IS_DOUBLE) {
+ if (!ZEND_TYPE_IS_MASK(prop->type) || !(ZEND_TYPE_MASK(prop->type) & MAY_BE_DOUBLE)) {
return prop;
}
} ZEND_REF_FOREACH_TYPE_SOURCES_END();
@@ -1641,19 +1618,20 @@ static ZEND_COLD zend_long zend_throw_incdec_ref_error(zend_reference *ref OPLIN
}
static ZEND_COLD zend_long zend_throw_incdec_prop_error(zend_property_info *prop OPLINE_DC) {
- const char *prop_type1, *prop_type2;
- zend_format_type(prop->type, &prop_type1, &prop_type2);
+ zend_string *type_str = zend_type_to_string(prop->type);
if (ZEND_IS_INCREMENT(opline->opcode)) {
- zend_type_error("Cannot increment property %s::$%s of type %s%s past its maximal value",
+ zend_type_error("Cannot increment property %s::$%s of type %s past its maximal value",
ZSTR_VAL(prop->ce->name),
zend_get_unmangled_property_name(prop->name),
- prop_type1, prop_type2);
+ ZSTR_VAL(type_str));
+ zend_string_release(type_str);
return ZEND_LONG_MAX;
} else {
- zend_type_error("Cannot decrement property %s::$%s of type %s%s past its minimal value",
+ zend_type_error("Cannot decrement property %s::$%s of type %s past its minimal value",
ZSTR_VAL(prop->ce->name),
zend_get_unmangled_property_name(prop->name),
- prop_type1, prop_type2);
+ ZSTR_VAL(type_str));
+ zend_string_release(type_str);
return ZEND_LONG_MIN;
}
}
@@ -2578,8 +2556,7 @@ static zend_always_inline zend_bool check_type_array_assignable(zend_type type)
if (!type) {
return 1;
}
- return ZEND_TYPE_IS_CODE(type)
- && (ZEND_TYPE_CODE(type) == IS_ARRAY || ZEND_TYPE_CODE(type) == IS_ITERABLE);
+ return ZEND_TYPE_IS_MASK(type) && (ZEND_TYPE_MASK(type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY));
}
static zend_always_inline zend_bool check_type_stdClass_assignable(zend_type type) {
@@ -2593,7 +2570,7 @@ static zend_always_inline zend_bool check_type_stdClass_assignable(zend_type typ
return zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "stdclass");
}
} else {
- return ZEND_TYPE_CODE(type) == IS_OBJECT;
+ return (ZEND_TYPE_MASK(type) & MAY_BE_OBJECT) != 0;
}
}
@@ -2987,58 +2964,59 @@ static zend_always_inline int zend_fetch_static_property_address(zval **retval,
}
ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(zend_property_info *prop1, zend_property_info *prop2, zval *zv) {
- const char *prop1_type1, *prop1_type2, *prop2_type1, *prop2_type2;
- zend_format_type(prop1->type, &prop1_type1, &prop1_type2);
- zend_format_type(prop2->type, &prop2_type1, &prop2_type2);
- zend_type_error("Reference with value of type %s held by property %s::$%s of type %s%s is not compatible with property %s::$%s of type %s%s",
+ zend_string *type1_str = zend_type_to_string(prop1->type);
+ zend_string *type2_str = zend_type_to_string(prop2->type);
+ zend_type_error("Reference with value of type %s held by property %s::$%s of type %s is not compatible with property %s::$%s of type %s",
Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)),
ZSTR_VAL(prop1->ce->name),
zend_get_unmangled_property_name(prop1->name),
- prop1_type1, prop1_type2,
+ ZSTR_VAL(type1_str),
ZSTR_VAL(prop2->ce->name),
zend_get_unmangled_property_name(prop2->name),
- prop2_type1, prop2_type2
+ ZSTR_VAL(type2_str)
);
+ zend_string_release(type1_str);
+ zend_string_release(type2_str);
}
ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(zend_property_info *prop, zval *zv) {
- const char *prop_type1, *prop_type2;
- zend_format_type(prop->type, &prop_type1, &prop_type2);
- zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s%s",
+ zend_string *type_str = zend_type_to_string(prop->type);
+ zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s",
Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)),
ZSTR_VAL(prop->ce->name),
zend_get_unmangled_property_name(prop->name),
- prop_type1, prop_type2
+ ZSTR_VAL(type_str)
);
+ zend_string_release(type_str);
}
ZEND_API ZEND_COLD void zend_throw_conflicting_coercion_error(zend_property_info *prop1, zend_property_info *prop2, zval *zv) {
- const char *prop1_type1, *prop1_type2, *prop2_type1, *prop2_type2;
- zend_format_type(prop1->type, &prop1_type1, &prop1_type2);
- zend_format_type(prop2->type, &prop2_type1, &prop2_type2);
- zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s%s and property %s::$%s of type %s%s, as this would result in an inconsistent type conversion",
+ zend_string *type1_str = zend_type_to_string(prop1->type);
+ zend_string *type2_str = zend_type_to_string(prop2->type);
+ zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s and property %s::$%s of type %s, as this would result in an inconsistent type conversion",
Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)),
ZSTR_VAL(prop1->ce->name),
zend_get_unmangled_property_name(prop1->name),
- prop1_type1, prop1_type2,
+ ZSTR_VAL(type1_str),
ZSTR_VAL(prop2->ce->name),
zend_get_unmangled_property_name(prop2->name),
- prop2_type1, prop2_type2
+ ZSTR_VAL(type2_str)
);
+ zend_string_release(type1_str);
+ zend_string_release(type2_str);
}
/* 1: valid, 0: invalid, -1: may be valid after type coercion */
static zend_always_inline int i_zend_verify_type_assignable_zval(
zend_type *type_ptr, zend_class_entry *self_ce, zval *zv, zend_bool strict) {
zend_type type = *type_ptr;
- zend_uchar type_code;
+ uint32_t type_mask;
zend_uchar zv_type = Z_TYPE_P(zv);
- if (ZEND_TYPE_ALLOW_NULL(type) && zv_type == IS_NULL) {
- return 1;
- }
-
if (ZEND_TYPE_IS_CLASS(type)) {
+ if (ZEND_TYPE_ALLOW_NULL(type) && zv_type == IS_NULL) {
+ return 1;
+ }
if (!ZEND_TYPE_IS_CE(type)) {
if (!zend_resolve_class_type(type_ptr, self_ce)) {
return 0;
@@ -3048,26 +3026,25 @@ static zend_always_inline int i_zend_verify_type_assignable_zval(
return zv_type == IS_OBJECT && instanceof_function(Z_OBJCE_P(zv), ZEND_TYPE_CE(type));
}
- type_code = ZEND_TYPE_CODE(type);
- if (type_code == zv_type ||
- (type_code == _IS_BOOL && (zv_type == IS_FALSE || zv_type == IS_TRUE))) {
+ if (ZEND_TYPE_CONTAINS_CODE(type, zv_type)) {
return 1;
}
- if (type_code == IS_ITERABLE) {
+ type_mask = ZEND_TYPE_MASK(type);
+ if (type_mask & MAY_BE_ITERABLE) {
return zend_is_iterable(zv);
}
/* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */
if (strict) {
- if (type_code == IS_DOUBLE && zv_type == IS_LONG) {
+ if ((type_mask & MAY_BE_DOUBLE) && zv_type == IS_LONG) {
return -1;
}
return 0;
}
/* No weak conversions for arrays and objects */
- if (type_code == IS_ARRAY || type_code == IS_OBJECT) {
+ if (type_mask & (MAY_BE_ARRAY|MAY_BE_OBJECT)) {
return 0;
}
@@ -3089,7 +3066,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference
* must be the same (modulo nullability). To handle this, remember the first type we see and
* compare against it when coercion becomes necessary. */
zend_property_info *seen_prop = NULL;
- zend_uchar seen_type;
+ uint32_t seen_type_mask;
zend_bool needs_coercion = 0;
ZEND_ASSERT(Z_TYPE_P(zv) != IS_REFERENCE);
@@ -3106,14 +3083,16 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference
if (!seen_prop) {
seen_prop = prop;
- seen_type = ZEND_TYPE_IS_CLASS(prop->type) ? IS_OBJECT : ZEND_TYPE_CODE(prop->type);
- } else if (needs_coercion && seen_type != ZEND_TYPE_CODE(prop->type)) {
+ seen_type_mask = ZEND_TYPE_IS_CLASS(prop->type)
+ ? MAY_BE_OBJECT : ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop->type));
+ } else if (needs_coercion
+ && seen_type_mask != ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop->type))) {
zend_throw_conflicting_coercion_error(seen_prop, prop, zv);
return 0;
}
} ZEND_REF_FOREACH_TYPE_SOURCES_END();
- if (UNEXPECTED(needs_coercion && !zend_verify_weak_scalar_type_hint(seen_type, zv))) {
+ if (UNEXPECTED(needs_coercion && !zend_verify_weak_scalar_type_hint(seen_type_mask, zv))) {
zend_throw_ref_type_error_zval(seen_prop, zv);
return 0;
}
@@ -3174,12 +3153,13 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_propert
if (result < 0) {
zend_property_info *ref_prop = ZEND_REF_FIRST_SOURCE(Z_REF_P(orig_val));
- if (ZEND_TYPE_CODE(prop_info->type) != ZEND_TYPE_CODE(ref_prop->type)) {
+ if (ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop_info->type))
+ != ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(ref_prop->type))) {
/* Invalid due to conflicting coercion */
zend_throw_ref_type_error_type(ref_prop, prop_info, val);
return 0;
}
- if (zend_verify_weak_scalar_type_hint(ZEND_TYPE_CODE(prop_info->type), val)) {
+ if (zend_verify_weak_scalar_type_hint(ZEND_TYPE_MASK(prop_info->type), val)) {
return 1;
}
}
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index 3840e39bcc..0dcf89f1c1 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -60,6 +60,11 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_propert
ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(zend_property_info *prop, zval *zv);
ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(zend_property_info *prop1, zend_property_info *prop2, zval *zv);
+ZEND_API zend_bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, zend_bool strict, zend_bool is_internal_arg);
+ZEND_API ZEND_COLD void zend_verify_arg_error(
+ const zend_function *zf, const zend_arg_info *arg_info,
+ int arg_num, const zend_class_entry *ce, zval *value);
+
#define ZEND_REF_TYPE_SOURCES(ref) \
(ref)->sources
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 3c77026e4c..b0e35a9552 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -348,7 +348,7 @@ static inheritance_status zend_perform_covariant_type_check(
}
return unlinked_instanceof(fe_ce, proto_ce) ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
- } else if (ZEND_TYPE_CODE(proto_type) == IS_ITERABLE) {
+ } else if (ZEND_TYPE_MASK(proto_type) & MAY_BE_ITERABLE) {
if (ZEND_TYPE_IS_CLASS(fe_type)) {
zend_string *fe_class_name = resolve_class_name(fe, ZEND_TYPE_NAME(fe_type));
zend_class_entry *fe_ce = lookup_class(fe, fe_class_name);
@@ -360,9 +360,9 @@ static inheritance_status zend_perform_covariant_type_check(
? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
}
- return ZEND_TYPE_CODE(fe_type) == IS_ITERABLE || ZEND_TYPE_CODE(fe_type) == IS_ARRAY
+ return ZEND_TYPE_MASK(fe_type) & (MAY_BE_ARRAY|MAY_BE_ITERABLE)
? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
- } else if (ZEND_TYPE_CODE(proto_type) == IS_OBJECT) {
+ } else if (ZEND_TYPE_MASK(proto_type) & MAY_BE_OBJECT) {
if (ZEND_TYPE_IS_CLASS(fe_type)) {
/* Currently, any class name would be allowed here. We still perform a class lookup
* for forward-compatibility reasons, as we may have named types in the future that
@@ -376,9 +376,10 @@ static inheritance_status zend_perform_covariant_type_check(
return INHERITANCE_SUCCESS;
}
- return ZEND_TYPE_CODE(fe_type) == IS_OBJECT ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
+ return ZEND_TYPE_MASK(fe_type) & MAY_BE_OBJECT ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
} else {
- return ZEND_TYPE_CODE(fe_type) == ZEND_TYPE_CODE(proto_type)
+ return ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(fe_type))
+ == ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(proto_type))
? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
}
}
@@ -516,33 +517,10 @@ static inheritance_status zend_do_perform_implementation_check(
static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function *fptr, zend_arg_info *arg_info, int return_hint) /* {{{ */
{
-
- if (ZEND_TYPE_IS_SET(arg_info->type) && ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
- smart_str_appendc(str, '?');
- }
-
- if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
- const char *class_name;
- size_t class_name_len;
-
- class_name = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type));
- class_name_len = ZSTR_LEN(ZEND_TYPE_NAME(arg_info->type));
-
- if (!strcasecmp(class_name, "self") && fptr->common.scope) {
- class_name = ZSTR_VAL(fptr->common.scope->name);
- class_name_len = ZSTR_LEN(fptr->common.scope->name);
- } else if (!strcasecmp(class_name, "parent") && fptr->common.scope && fptr->common.scope->parent) {
- class_name = ZSTR_VAL(fptr->common.scope->parent->name);
- class_name_len = ZSTR_LEN(fptr->common.scope->parent->name);
- }
-
- smart_str_appendl(str, class_name, class_name_len);
- if (!return_hint) {
- smart_str_appendc(str, ' ');
- }
- } else if (ZEND_TYPE_IS_CODE(arg_info->type)) {
- const char *type_name = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type));
- smart_str_appends(str, type_name);
+ if (ZEND_TYPE_IS_SET(arg_info->type)) {
+ zend_string *type_str = zend_type_to_string_resolved(arg_info->type, fptr->common.scope);
+ smart_str_append(str, type_str);
+ zend_string_release(type_str);
if (!return_hint) {
smart_str_appendc(str, ' ');
}
@@ -942,14 +920,13 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
if (UNEXPECTED(ZEND_TYPE_IS_SET(parent_info->type))) {
if (!property_types_compatible(parent_info, child_info)) {
+ zend_string *type_str =
+ zend_type_to_string_resolved(parent_info->type, parent_info->ce);
zend_error_noreturn(E_COMPILE_ERROR,
- "Type of %s::$%s must be %s%s (as in class %s)",
+ "Type of %s::$%s must be %s (as in class %s)",
ZSTR_VAL(ce->name),
ZSTR_VAL(key),
- ZEND_TYPE_ALLOW_NULL(parent_info->type) ? "?" : "",
- ZEND_TYPE_IS_CLASS(parent_info->type)
- ? ZSTR_VAL(ZEND_TYPE_IS_CE(parent_info->type) ? ZEND_TYPE_CE(parent_info->type)->name : zend_resolve_property_type(ZEND_TYPE_NAME(parent_info->type), parent_info->ce))
- : zend_get_type_by_const(ZEND_TYPE_CODE(parent_info->type)),
+ ZSTR_VAL(type_str),
ZSTR_VAL(ce->parent->name));
}
} else if (UNEXPECTED(ZEND_TYPE_IS_SET(child_info->type) && !ZEND_TYPE_IS_SET(parent_info->type))) {
diff --git a/Zend/zend_string.h b/Zend/zend_string.h
index 674bea2f60..a38c1cae8c 100644
--- a/Zend/zend_string.h
+++ b/Zend/zend_string.h
@@ -506,6 +506,12 @@ EMPTY_SWITCH_DEFAULT_CASE()
_(ZEND_STR_ARGV, "argv") \
_(ZEND_STR_ARGC, "argc") \
_(ZEND_STR_ARRAY_CAPITALIZED, "Array") \
+ _(ZEND_STR_BOOL, "bool") \
+ _(ZEND_STR_INT, "int") \
+ _(ZEND_STR_FLOAT, "float") \
+ _(ZEND_STR_CALLABLE, "callable") \
+ _(ZEND_STR_ITERABLE, "iterable") \
+ _(ZEND_STR_VOID, "void") \
typedef enum _zend_known_string_id {
diff --git a/Zend/zend_type_info.h b/Zend/zend_type_info.h
index 72550b6fc3..c991fd5db5 100644
--- a/Zend/zend_type_info.h
+++ b/Zend/zend_type_info.h
@@ -34,7 +34,12 @@
#define MAY_BE_ANY (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)
#define MAY_BE_REF (1 << IS_REFERENCE) /* may be reference */
-#define MAY_BE_ARRAY_SHIFT (IS_REFERENCE)
+/* These are used in zend_type, but not for type inference. */
+#define MAY_BE_CALLABLE (1 << IS_CALLABLE)
+#define MAY_BE_ITERABLE (1 << IS_ITERABLE)
+#define MAY_BE_VOID (1 << IS_VOID)
+
+#define MAY_BE_ARRAY_SHIFT (IS_VOID)
#define MAY_BE_ARRAY_OF_NULL (MAY_BE_NULL << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_FALSE (MAY_BE_FALSE << MAY_BE_ARRAY_SHIFT)
@@ -48,11 +53,11 @@
#define MAY_BE_ARRAY_OF_ANY (MAY_BE_ANY << MAY_BE_ARRAY_SHIFT)
#define MAY_BE_ARRAY_OF_REF (MAY_BE_REF << MAY_BE_ARRAY_SHIFT)
-#define MAY_BE_ARRAY_KEY_LONG (1<<21)
-#define MAY_BE_ARRAY_KEY_STRING (1<<22)
+#define MAY_BE_ARRAY_KEY_LONG (1<<25)
+#define MAY_BE_ARRAY_KEY_STRING (1<<26)
#define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING)
-#define MAY_BE_ERROR (1<<23)
-#define MAY_BE_CLASS (1<<24)
+#define MAY_BE_ERROR (1<<27)
+#define MAY_BE_CLASS (1<<28)
#endif /* ZEND_TYPE_INFO_H */
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index 653a07b6b2..be6c52b8ca 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -106,61 +106,73 @@ typedef void (*copy_ctor_func_t)(zval *pElement);
* It shouldn't be used directly. Only through ZEND_TYPE_* macros.
*
* ZEND_TYPE_IS_SET() - checks if type-hint exists
- * ZEND_TYPE_IS_CODE() - checks if type-hint refer to standard type
+ * ZEND_TYPE_IS_MASK() - checks if type-hint refer to standard type
* ZEND_TYPE_IS_CLASS() - checks if type-hint refer to some class
* ZEND_TYPE_IS_CE() - checks if type-hint refer to some class by zend_class_entry *
* ZEND_TYPE_IS_NAME() - checks if type-hint refer to some class by zend_string *
*
* ZEND_TYPE_NAME() - returns referenced class name
* ZEND_TYPE_CE() - returns referenced class entry
- * ZEND_TYPE_CODE() - returns standard type code (e.g. IS_LONG, _IS_BOOL)
+ * ZEND_TYPE_MASK() - returns MAY_BE_* type mask
*
* ZEND_TYPE_ALLOW_NULL() - checks if NULL is allowed
*
- * ZEND_TYPE_ENCODE() and ZEND_TYPE_ENCODE_CLASS() should be used for
- * construction.
+ * ZEND_TYPE_ENCODE_*() should be used for construction.
*/
typedef uintptr_t zend_type;
+#define _ZEND_TYPE_CODE_MAX ((Z_L(1)<<(IS_VOID+1))-1)
+#define _ZEND_TYPE_FLAG_MASK Z_L(0x3)
+#define _ZEND_TYPE_CE_BIT Z_L(0x1)
+/* Must have same value as MAY_BE_NULL */
+#define _ZEND_TYPE_NULLABLE_BIT Z_L(0x2)
+
#define ZEND_TYPE_IS_SET(t) \
- ((t) > Z_L(0x3))
+ ((t) > _ZEND_TYPE_FLAG_MASK)
-#define ZEND_TYPE_IS_CODE(t) \
- (((t) > Z_L(0x3)) && ((t) <= Z_L(0x3ff)))
+#define ZEND_TYPE_IS_MASK(t) \
+ (((t) > _ZEND_TYPE_FLAG_MASK) && ((t) <= _ZEND_TYPE_CODE_MAX))
#define ZEND_TYPE_IS_CLASS(t) \
- ((t) > Z_L(0x3ff))
+ ((t) > _ZEND_TYPE_CODE_MAX)
#define ZEND_TYPE_IS_CE(t) \
- (((t) & Z_L(0x2)) != 0)
+ (((t) & _ZEND_TYPE_CE_BIT) != 0)
#define ZEND_TYPE_IS_NAME(t) \
(ZEND_TYPE_IS_CLASS(t) && !ZEND_TYPE_IS_CE(t))
#define ZEND_TYPE_NAME(t) \
- ((zend_string*)((t) & ~Z_L(0x3)))
+ ((zend_string*)((t) & ~_ZEND_TYPE_FLAG_MASK))
#define ZEND_TYPE_CE(t) \
- ((zend_class_entry*)((t) & ~Z_L(0x3)))
+ ((zend_class_entry*)((t) & ~_ZEND_TYPE_FLAG_MASK))
+
+#define ZEND_TYPE_MASK(t) \
+ (t)
-#define ZEND_TYPE_CODE(t) \
- ((t) >> Z_L(2))
+#define ZEND_TYPE_CONTAINS_CODE(t, code) \
+ (((t) & (1 << (code))) != 0)
#define ZEND_TYPE_ALLOW_NULL(t) \
- (((t) & Z_L(0x1)) != 0)
+ (((t) & _ZEND_TYPE_NULLABLE_BIT) != 0)
#define ZEND_TYPE_WITHOUT_NULL(t) \
- ((t) & ~Z_L(0x1))
+ ((t) & ~_ZEND_TYPE_NULLABLE_BIT)
-#define ZEND_TYPE_ENCODE(code, allow_null) \
- (((code) << Z_L(2)) | ((allow_null) ? Z_L(0x1) : Z_L(0x0)))
+#define ZEND_TYPE_ENCODE_MASK(maybe_code) \
+ (maybe_code)
+
+#define ZEND_TYPE_ENCODE_CODE(code, allow_null) \
+ (((code) == _IS_BOOL ? (MAY_BE_FALSE|MAY_BE_TRUE) : (1 << (code))) \
+ | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : Z_L(0x0)))
#define ZEND_TYPE_ENCODE_CE(ce, allow_null) \
- (((uintptr_t)(ce)) | ((allow_null) ? Z_L(0x3) : Z_L(0x2)))
+ (((uintptr_t)(ce)) | _ZEND_TYPE_CE_BIT | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : Z_L(0x0)))
#define ZEND_TYPE_ENCODE_CLASS(class_name, allow_null) \
- (((uintptr_t)(class_name)) | ((allow_null) ? Z_L(0x1) : Z_L(0x0)))
+ (((uintptr_t)(class_name)) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : Z_L(0x0)))
#define ZEND_TYPE_ENCODE_CLASS_CONST_0(class_name) \
((zend_type) class_name)
@@ -410,7 +422,7 @@ struct _zend_ast_ref {
/*zend_ast ast; zend_ast follows the zend_ast_ref structure */
};
-/* regular data types */
+/* Regular data types: Must be in sync with zend_variables.c. */
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
@@ -422,21 +434,21 @@ struct _zend_ast_ref {
#define IS_OBJECT 8
#define IS_RESOURCE 9
#define IS_REFERENCE 10
+#define IS_CONSTANT_AST 11 /* Constant expressions */
-/* constant expressions */
-#define IS_CONSTANT_AST 11
+/* Fake types used only for type hinting. IS_VOID should be the last. */
+#define IS_CALLABLE 12
+#define IS_ITERABLE 13
+#define IS_VOID 14
/* internal types */
-#define IS_INDIRECT 13
-#define IS_PTR 14
-#define _IS_ERROR 15
-
-/* fake types used only for type hinting (Z_TYPE(zv) can not use them) */
-#define _IS_BOOL 16
-#define IS_CALLABLE 17
-#define IS_ITERABLE 18
-#define IS_VOID 19
-#define _IS_NUMBER 20
+#define IS_INDIRECT 15
+#define IS_PTR 16
+#define _IS_ERROR 17
+
+/* used for casts */
+#define _IS_BOOL 18
+#define _IS_NUMBER 19
static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
return pz->u1.v.type;
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 8884810f65..4e95859a7f 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -4276,9 +4276,8 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
}
if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
- && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
- && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
+ && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))
+ && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 70d89048e7..a48b964e45 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -9077,9 +9077,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP
}
if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
- && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
- && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
+ && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))
+ && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
@@ -19480,9 +19479,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
}
if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
- && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
- && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
+ && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))
+ && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
@@ -27502,9 +27500,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
}
if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
- && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
- && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
+ && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))
+ && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
@@ -34608,9 +34605,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
}
if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
- && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
- && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
+ && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))
+ && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
@@ -46713,9 +46709,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
}
if (UNEXPECTED(!ZEND_TYPE_IS_CLASS(ret_info->type)
- && ZEND_TYPE_CODE(ret_info->type) != IS_CALLABLE
- && ZEND_TYPE_CODE(ret_info->type) != IS_ITERABLE
- && !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(retval_ptr))
+ && !(ZEND_TYPE_MASK(ret_info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))
+ && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
&& retval_ref != retval_ptr)
) {
diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c
index 3893017553..fd4cb4ab3b 100644
--- a/ext/com_dotnet/com_handlers.c
+++ b/ext/com_dotnet/com_handlers.c
@@ -319,7 +319,7 @@ static zend_function *com_method_get(zend_object **object_ptr, zend_string *name
f.arg_info = ecalloc(bindptr.lpfuncdesc->cParams, sizeof(zend_arg_info));
for (i = 0; i < bindptr.lpfuncdesc->cParams; i++) {
- f.arg_info[i].type = ZEND_TYPE_ENCODE(0,1);
+ f.arg_info[i].type = ZEND_TYPE_ENCODE_CODE(0,1);
if (bindptr.lpfuncdesc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FOUT) {
f.arg_info[i].pass_by_reference = ZEND_SEND_BY_REF;
}
diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c
index 5401c9df6a..5ea7775921 100644
--- a/ext/opcache/Optimizer/dfa_pass.c
+++ b/ext/opcache/Optimizer/dfa_pass.c
@@ -312,6 +312,8 @@ static inline zend_bool can_elide_return_type_check(
zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use];
zend_ssa_var_info *def_info = &ssa->var_info[ssa_op->op1_def];
+ /* TODO: It would be better to rewrite this without using def_info,
+ * which may not be an exact representation of the type. */
if (use_info->type & MAY_BE_REF) {
return 0;
}
@@ -322,7 +324,8 @@ static inline zend_bool can_elide_return_type_check(
}
/* These types are not represented exactly */
- if (ZEND_TYPE_CODE(info->type) == IS_CALLABLE || ZEND_TYPE_CODE(info->type) == IS_ITERABLE) {
+ if (ZEND_TYPE_IS_MASK(info->type)
+ && (ZEND_TYPE_MASK(info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))) {
return 0;
}
diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c
index e8a9918b39..2e30857013 100644
--- a/ext/opcache/Optimizer/zend_inference.c
+++ b/ext/opcache/Optimizer/zend_inference.c
@@ -1419,18 +1419,22 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int
return 1;
} else if (op_array->arg_info &&
opline->op1.num <= op_array->num_args) {
- if (ZEND_TYPE_CODE(op_array->arg_info[opline->op1.num-1].type) == IS_LONG) {
- tmp->underflow = 0;
- tmp->min = ZEND_LONG_MIN;
- tmp->max = ZEND_LONG_MAX;
- tmp->overflow = 0;
- return 1;
- } else if (ZEND_TYPE_CODE(op_array->arg_info[opline->op1.num-1].type) == _IS_BOOL) {
- tmp->underflow = 0;
- tmp->min = 0;
- tmp->max = 1;
- tmp->overflow = 0;
- return 1;
+ zend_type type = op_array->arg_info[opline->op1.num-1].type;
+ if (ZEND_TYPE_IS_MASK(type)) {
+ uint32_t mask = ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(type));
+ if (mask == MAY_BE_LONG) {
+ tmp->underflow = 0;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 0;
+ return 1;
+ } else if (mask == (MAY_BE_FALSE|MAY_BE_TRUE)) {
+ tmp->underflow = 0;
+ tmp->min = 0;
+ tmp->max = 1;
+ tmp->overflow = 0;
+ return 1;
+ }
}
}
}
@@ -2232,22 +2236,23 @@ static inline zend_class_entry *get_class_entry(const zend_script *script, zend_
return NULL;
}
-static uint32_t zend_convert_type_code_to_may_be(zend_uchar type_code) {
- switch (type_code) {
- case IS_VOID:
- return MAY_BE_NULL;
- case IS_CALLABLE:
- return MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- case IS_ITERABLE:
- return MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- case IS_ARRAY:
- return MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- case _IS_BOOL:
- return MAY_BE_TRUE|MAY_BE_FALSE;
- default:
- ZEND_ASSERT(type_code < IS_REFERENCE);
- return 1 << type_code;
+static uint32_t zend_convert_type_declaration_mask(uint32_t type_mask) {
+ if (type_mask & MAY_BE_VOID) {
+ type_mask &= ~MAY_BE_VOID;
+ type_mask |= MAY_BE_NULL;
+ }
+ if (type_mask & MAY_BE_CALLABLE) {
+ type_mask &= ~MAY_BE_CALLABLE;
+ type_mask |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ if (type_mask & MAY_BE_ITERABLE) {
+ type_mask &= ~MAY_BE_ITERABLE;
+ type_mask |= MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ if (type_mask & MAY_BE_ARRAY) {
+ type_mask |= MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
}
+ return type_mask;
}
uint32_t zend_fetch_arg_info_type(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce)
@@ -2261,8 +2266,8 @@ uint32_t zend_fetch_arg_info_type(const zend_script *script, zend_arg_info *arg_
tmp |= MAY_BE_OBJECT;
*pce = get_class_entry(script, lcname);
zend_string_release_ex(lcname, 0);
- } else if (ZEND_TYPE_IS_CODE(arg_info->type)) {
- tmp |= zend_convert_type_code_to_may_be(ZEND_TYPE_CODE(arg_info->type));
+ } else if (ZEND_TYPE_IS_MASK(arg_info->type)) {
+ tmp |= zend_convert_type_declaration_mask(ZEND_TYPE_MASK(arg_info->type));
} else {
tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
}
@@ -2366,7 +2371,7 @@ static uint32_t zend_fetch_prop_type(const zend_script *script, zend_property_in
if (prop_info && ZEND_TYPE_IS_SET(prop_info->type)) {
uint32_t type = ZEND_TYPE_IS_CLASS(prop_info->type)
? MAY_BE_OBJECT
- : zend_convert_type_code_to_may_be(ZEND_TYPE_CODE(prop_info->type));
+ : zend_convert_type_declaration_mask(ZEND_TYPE_MASK(prop_info->type));
if (ZEND_TYPE_ALLOW_NULL(prop_info->type)) {
type |= MAY_BE_NULL;
diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h
index 03cbb5e82b..80a58405e7 100644
--- a/ext/opcache/Optimizer/zend_inference.h
+++ b/ext/opcache/Optimizer/zend_inference.h
@@ -26,11 +26,11 @@
/* Bitmask for type inference (zend_ssa_var_info.type) */
#include "zend_type_info.h"
-#define MAY_BE_IN_REG (1<<25) /* value allocated in CPU register */
+#define MAY_BE_IN_REG (1<<29) /* value allocated in CPU register */
//TODO: remome MAY_BE_RC1, MAY_BE_RCN???
-#define MAY_BE_RC1 (1<<27) /* may be non-reference with refcount == 1 */
-#define MAY_BE_RCN (1<<28) /* may be non-reference with refcount > 1 */
+#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */
+#define MAY_BE_RCN (1<<31) /* may be non-reference with refcount > 1 */
#define MAY_HAVE_DTOR \
(MAY_BE_OBJECT|MAY_BE_RESOURCE \
diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c
index 952ea95ab2..7bac2d7538 100644
--- a/ext/opcache/Optimizer/zend_optimizer.c
+++ b/ext/opcache/Optimizer/zend_optimizer.c
@@ -657,8 +657,8 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
case ZEND_VERIFY_RETURN_TYPE: {
zend_arg_info *ret_info = op_array->arg_info - 1;
if (ZEND_TYPE_IS_CLASS(ret_info->type)
- || ZEND_TYPE_CODE(ret_info->type) == IS_CALLABLE
- || !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(val))
+ || (ZEND_TYPE_IS_MASK(ret_info->type)
+ && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(val)))
|| (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
return 0;
}
diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c
index 18af7234a7..54b888a132 100644
--- a/ext/opcache/jit/zend_jit_helpers.c
+++ b/ext/opcache/jit/zend_jit_helpers.c
@@ -1115,169 +1115,6 @@ static zval* ZEND_FASTCALL zend_jit_fetch_global_helper(zend_execute_data *execu
return value;
}
-static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg)
-{
- switch (type_hint) {
- case _IS_BOOL: {
- zend_bool dest;
-
- if (!zend_parse_arg_bool_weak(arg, &dest)) {
- return 0;
- }
- zval_ptr_dtor(arg);
- ZVAL_BOOL(arg, dest);
- return 1;
- }
- case IS_LONG: {
- zend_long dest;
-
- if (!zend_parse_arg_long_weak(arg, &dest)) {
- return 0;
- }
- zval_ptr_dtor(arg);
- ZVAL_LONG(arg, dest);
- return 1;
- }
- case IS_DOUBLE: {
- double dest;
-
- if (!zend_parse_arg_double_weak(arg, &dest)) {
- return 0;
- }
- zval_ptr_dtor(arg);
- ZVAL_DOUBLE(arg, dest);
- return 1;
- }
- case IS_STRING: {
- zend_string *dest;
-
- /* on success "arg" is converted to IS_STRING */
- if (!zend_parse_arg_str_weak(arg, &dest)) {
- return 0;
- }
- return 1;
- }
- default:
- return 0;
- }
-}
-
-static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, zend_bool strict)
-{
- if (UNEXPECTED(strict)) {
- /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */
- if (type_hint != IS_DOUBLE || Z_TYPE_P(arg) != IS_LONG) {
- return 0;
- }
- } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) {
- /* NULL may be accepted only by nullable hints (this is already checked) */
- return 0;
- }
- return zend_verify_weak_scalar_type_hint(type_hint, arg);
-}
-
-static ZEND_COLD void zend_verify_type_error_common(
- const zend_function *zf, const zend_arg_info *arg_info,
- const zend_class_entry *ce, zval *value,
- const char **fname, const char **fsep, const char **fclass,
- const char **need_msg, const char **need_kind, const char **need_or_null,
- const char **given_msg, const char **given_kind)
-{
- zend_bool is_interface = 0;
- *fname = ZSTR_VAL(zf->common.function_name);
-
- if (zf->common.scope) {
- *fsep = "::";
- *fclass = ZSTR_VAL(zf->common.scope->name);
- } else {
- *fsep = "";
- *fclass = "";
- }
-
- if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
- if (ce) {
- if (ce->ce_flags & ZEND_ACC_INTERFACE) {
- *need_msg = "implement interface ";
- is_interface = 1;
- } else {
- *need_msg = "be an instance of ";
- }
- *need_kind = ZSTR_VAL(ce->name);
- } else {
- /* We don't know whether it's a class or interface, assume it's a class */
- *need_msg = "be an instance of ";
- *need_kind = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type));
- }
- } else {
- switch (ZEND_TYPE_CODE(arg_info->type)) {
- case IS_CALLABLE:
- *need_msg = "be callable";
- *need_kind = "";
- break;
- case IS_ITERABLE:
- *need_msg = "be iterable";
- *need_kind = "";
- break;
- case IS_OBJECT:
- *need_msg = "be an object";
- *need_kind = "";
- break;
- default:
- *need_msg = "be of the type ";
- *need_kind = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type));
- break;
- }
- }
-
- if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
- *need_or_null = is_interface ? " or be null" : " or null";
- } else {
- *need_or_null = "";
- }
-
- if (value) {
- if (ZEND_TYPE_IS_CLASS(arg_info->type) && Z_TYPE_P(value) == IS_OBJECT) {
- *given_msg = "instance of ";
- *given_kind = ZSTR_VAL(Z_OBJCE_P(value)->name);
- } else {
- *given_msg = zend_zval_type_name(value);
- *given_kind = "";
- }
- } else {
- *given_msg = "none";
- *given_kind = "";
- }
-}
-
-static ZEND_COLD void zend_verify_arg_error(
- const zend_function *zf, const zend_arg_info *arg_info,
- int arg_num, const zend_class_entry *ce, zval *value)
-{
- zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data;
- const char *fname, *fsep, *fclass;
- const char *need_msg, *need_kind, *need_or_null, *given_msg, *given_kind;
-
- if (value && !Z_ISUNDEF_P(value)) {
- zend_verify_type_error_common(
- zf, arg_info, ce, value,
- &fname, &fsep, &fclass, &need_msg, &need_kind, &need_or_null, &given_msg, &given_kind);
-
- if (zf->common.type == ZEND_USER_FUNCTION) {
- if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) {
- zend_type_error("Argument %d passed to %s%s%s() must %s%s%s, %s%s given, called in %s on line %d",
- arg_num, fclass, fsep, fname, need_msg, need_kind, need_or_null, given_msg, given_kind,
- ZSTR_VAL(ptr->func->op_array.filename), ptr->opline->lineno);
- } else {
- zend_type_error("Argument %d passed to %s%s%s() must %s%s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, need_or_null, given_msg, given_kind);
- }
- } else {
- zend_type_error("Argument %d passed to %s%s%s() must %s%s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, need_or_null, given_msg, given_kind);
- }
- } else {
- zend_missing_arg_error(EG(current_execute_data));
- }
-}
-
static void ZEND_FASTCALL zend_jit_verify_arg_object(zval *arg, zend_op_array *op_array, uint32_t arg_num, zend_arg_info *arg_info, void **cache_slot)
{
zend_class_entry *ce;
@@ -1299,13 +1136,14 @@ static void ZEND_FASTCALL zend_jit_verify_arg_object(zval *arg, zend_op_array *o
static void ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, zend_op_array *op_array, uint32_t arg_num, zend_arg_info *arg_info, void **cache_slot)
{
zend_class_entry *ce = NULL;
-
- if (Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
- /* Null passed to nullable type */
- return;
- }
+ uint32_t type_mask;
if (UNEXPECTED(ZEND_TYPE_IS_CLASS(arg_info->type))) {
+ if (Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
+ /* Null passed to nullable type */
+ return;
+ }
+
/* This is always an error - we fetch the class name for the error message here */
if (EXPECTED(*cache_slot)) {
ce = (zend_class_entry *) *cache_slot;
@@ -1316,20 +1154,20 @@ static void ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, zend_op_array *op_
}
}
goto err;
- } else if (ZEND_TYPE_CODE(arg_info->type) == IS_CALLABLE) {
+ }
+
+ type_mask = ZEND_TYPE_MASK(arg_info->type);
+ if (type_mask & MAY_BE_CALLABLE) {
if (zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL) == 0) {
goto err;
}
- } else if (ZEND_TYPE_CODE(arg_info->type) == IS_ITERABLE) {
+ } else if (type_mask & MAY_BE_ITERABLE) {
if (zend_is_iterable(arg) == 0) {
goto err;
}
- } else if (ZEND_TYPE_CODE(arg_info->type) == _IS_BOOL &&
- EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) {
- return;
- } else if (ZEND_TYPE_CODE(arg_info->type) != Z_TYPE_P(arg)) {
+ } else {
if (Z_ISUNDEF_P(arg) ||
- zend_verify_scalar_type_hint(ZEND_TYPE_CODE(arg_info->type), arg, ZEND_ARG_USES_STRICT_TYPES()) == 0) {
+ zend_verify_scalar_type_hint(ZEND_TYPE_MASK(arg_info->type), arg, ZEND_ARG_USES_STRICT_TYPES(), /* is_internal */ 0) == 0) {
goto err;
}
}
@@ -1485,7 +1323,7 @@ static zend_property_info *zend_jit_get_prop_not_accepting_double(zend_reference
{
zend_property_info *prop;
ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) {
- if (ZEND_TYPE_CODE(prop->type) != IS_DOUBLE) {
+ if (!ZEND_TYPE_IS_MASK(prop->type) || !(ZEND_TYPE_MASK(prop->type) & MAY_BE_DOUBLE)) {
return prop;
}
} ZEND_REF_FOREACH_TYPE_SOURCES_END();
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index 90f674a939..c716068923 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -7019,19 +7019,10 @@ static uint32_t skip_valid_arguments(zend_op_array *op_array, zend_ssa *ssa, zen
zend_arg_info *arg_info = func->op_array.arg_info + num_args;
if (ZEND_TYPE_IS_SET(arg_info->type)) {
- if (!ZEND_TYPE_IS_CLASS(arg_info->type)) {
- unsigned char code = ZEND_TYPE_CODE(arg_info->type);
+ if (ZEND_TYPE_IS_MASK(arg_info->type)) {
+ uint32_t type_mask = ZEND_TYPE_MASK(arg_info->type);
uint32_t info = _ssa_op1_info(op_array, ssa, call_info->arg_info[num_args].opline);
-
- if (code == _IS_BOOL) {
- if (info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_FALSE|MAY_BE_TRUE))) {
- break;
- }
- } else if (code <= IS_RESOURCE) {
- if (info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (1 << code))) {
- break;
- }
- } else {
+ if ((info & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~type_mask) {
break;
}
} else {
@@ -8945,6 +8936,16 @@ static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, zend_op_array
{
uint32_t arg_num = opline->op1.num;
+ | cmp dword EX->This.u2.num_args, arg_num
+ | jb >1
+ |.cold_code
+ |1:
+ | SAVE_VALID_OPLINE opline
+ | mov FCARG1a, FP
+ | EXT_CALL zend_missing_arg_error, r0
+ | jmp ->exception_handler
+ |.code
+
if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
zend_arg_info *arg_info = NULL;
@@ -8966,18 +8967,13 @@ static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, zend_op_array
| add r0, offsetof(zend_reference, val)
}
if (!ZEND_TYPE_IS_CLASS(type)) {
- unsigned char code = ZEND_TYPE_CODE(type);
-
- if (code == _IS_BOOL) {
- | cmp byte [r0 + 8], IS_FALSE
- | je >1
- | cmp byte [r0 + 8], IS_TRUE
- | jne >8
- |1:
- } else {
- | cmp byte [r0 + 8], code
- | jne >8
- }
+ // TODO: Use bt?
+ uint32_t type_mask = ZEND_TYPE_MASK(type);
+ | mov edx, 1
+ | mov cl, byte [r0 + 8]
+ | shl edx, cl
+ | test edx, type_mask
+ | je >8
} else {
| SAVE_VALID_OPLINE opline
| cmp byte [r0 + 8], IS_OBJECT
@@ -9054,16 +9050,6 @@ static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, zend_op_array
}
}
- | cmp dword EX->This.u2.num_args, arg_num
- | jb >1
- |.cold_code
- |1:
- | SAVE_VALID_OPLINE opline
- | mov FCARG1a, FP
- | EXT_CALL zend_missing_arg_error, r0
- | jmp ->exception_handler
- |.code
-
if ((opline+1)->opcode != ZEND_RECV && (opline+1)->opcode != ZEND_RECV_INIT) {
last_valid_opline = NULL;
if (!zend_jit_set_valid_ip(Dst, opline + 1)) {
@@ -9125,8 +9111,12 @@ static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, zend_op_a
| LOAD_ZVAL_ADDR r0, res_addr
| ZVAL_DEREF r0, MAY_BE_REF
if (!ZEND_TYPE_IS_CLASS(arg_info->type)) {
- | cmp byte [r0 + 8], ZEND_TYPE_CODE(arg_info->type)
- | jne >8
+ // TODO: Use bt?
+ | mov edx, 1
+ | mov cl, byte [r0 + 8]
+ | shl edx, cl
+ | test edx, ZEND_TYPE_MASK(arg_info->type)
+ | je >8
} else {
| cmp byte [r0 + 8], IS_OBJECT
| jne >8
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 5cc023831e..18e3a0cecc 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -582,17 +582,14 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
} else {
smart_str_append_printf(str, "<required> ");
}
- if (ZEND_TYPE_IS_CLASS(arg_info->type)) {
- smart_str_append_printf(str, "%s ",
- ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type)));
- if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
- smart_str_append_printf(str, "or NULL ");
- }
- } else if (ZEND_TYPE_IS_CODE(arg_info->type)) {
- smart_str_append_printf(str, "%s ", zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type)));
+ if (ZEND_TYPE_IS_SET(arg_info->type)) {
+ /* TODO: We should be using ?Type instead of "or NULL" here. */
+ zend_string *type_str = zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(arg_info->type));
+ smart_str_append_printf(str, "%s ", ZSTR_VAL(type_str));
if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
smart_str_append_printf(str, "or NULL ");
}
+ zend_string_release(type_str);
}
if (arg_info->pass_by_reference) {
smart_str_appendc(str, '&');
@@ -802,17 +799,15 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent
smart_str_free(&param_indent);
if (fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
smart_str_append_printf(str, " %s- Return [ ", indent);
- if (ZEND_TYPE_IS_CLASS(fptr->common.arg_info[-1].type)) {
- smart_str_append_printf(str, "%s ",
- ZSTR_VAL(ZEND_TYPE_NAME(fptr->common.arg_info[-1].type)));
- if (ZEND_TYPE_ALLOW_NULL(fptr->common.arg_info[-1].type)) {
- smart_str_appends(str, "or NULL ");
- }
- } else if (ZEND_TYPE_IS_CODE(fptr->common.arg_info[-1].type)) {
- smart_str_append_printf(str, "%s ", zend_get_type_by_const(ZEND_TYPE_CODE(fptr->common.arg_info[-1].type)));
+ if (ZEND_TYPE_IS_SET(fptr->common.arg_info[-1].type)) {
+ /* TODO: We should use ?Type instead of "or NULL" here */
+ zend_string *type_str =
+ zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(fptr->common.arg_info[-1].type));
+ smart_str_append_printf(str, "%s ", ZSTR_VAL(type_str));
if (ZEND_TYPE_ALLOW_NULL(fptr->common.arg_info[-1].type)) {
smart_str_appends(str, "or NULL ");
}
+ zend_string_release(type_str);
}
smart_str_appends(str, "]\n");
}
@@ -2565,13 +2560,15 @@ ZEND_METHOD(reflection_parameter, isArray)
{
reflection_object *intern;
parameter_reference *param;
+ zend_type type;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
GET_REFLECTION_OBJECT_PTR(param);
- RETVAL_BOOL(ZEND_TYPE_CODE(param->arg_info->type) == IS_ARRAY);
+ type = ZEND_TYPE_WITHOUT_NULL(param->arg_info->type);
+ RETVAL_BOOL(ZEND_TYPE_MASK(type) == MAY_BE_ARRAY);
}
/* }}} */
@@ -2581,13 +2578,15 @@ ZEND_METHOD(reflection_parameter, isCallable)
{
reflection_object *intern;
parameter_reference *param;
+ zend_type type;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
GET_REFLECTION_OBJECT_PTR(param);
- RETVAL_BOOL(ZEND_TYPE_CODE(param->arg_info->type) == IS_CALLABLE);
+ type = ZEND_TYPE_WITHOUT_NULL(param->arg_info->type);
+ RETVAL_BOOL(ZEND_TYPE_MASK(type) == MAY_BE_CALLABLE);
}
/* }}} */
@@ -2817,19 +2816,6 @@ ZEND_METHOD(reflection_type, allowsNull)
}
/* }}} */
-/* {{{ reflection_type_name */
-static zend_string *reflection_type_name(type_reference *param) {
- if (ZEND_TYPE_IS_NAME(param->type)) {
- return zend_string_copy(ZEND_TYPE_NAME(param->type));
- } else if (ZEND_TYPE_IS_CE(param->type)) {
- return zend_string_copy(ZEND_TYPE_CE(param->type)->name);
- } else {
- const char *name = zend_get_type_by_const(ZEND_TYPE_CODE(param->type));
- return zend_string_init(name, strlen(name), 0);
- }
-}
-/* }}} */
-
/* {{{ proto public string ReflectionType::__toString()
Return the text of the type hint */
ZEND_METHOD(reflection_type, __toString)
@@ -2842,7 +2828,7 @@ ZEND_METHOD(reflection_type, __toString)
}
GET_REFLECTION_OBJECT_PTR(param);
- RETURN_STR(reflection_type_name(param));
+ RETURN_STR(zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(param->type)));
}
/* }}} */
@@ -2858,7 +2844,7 @@ ZEND_METHOD(reflection_named_type, getName)
}
GET_REFLECTION_OBJECT_PTR(param);
- RETURN_STR(reflection_type_name(param));
+ RETURN_STR(zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(param->type)));
}
/* }}} */
@@ -2874,7 +2860,7 @@ ZEND_METHOD(reflection_named_type, isBuiltin)
}
GET_REFLECTION_OBJECT_PTR(param);
- RETVAL_BOOL(ZEND_TYPE_IS_CODE(param->type));
+ RETVAL_BOOL(ZEND_TYPE_IS_MASK(param->type));
}
/* }}} */
diff --git a/ext/standard/var.c b/ext/standard/var.c
index 410c0fdeb9..7b75bffe85 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -77,12 +77,10 @@ static void php_object_property_dump(zend_property_info *prop_info, zval *zv, ze
if (Z_TYPE_P(zv) == IS_UNDEF) {
ZEND_ASSERT(prop_info->type);
- php_printf("%*cuninitialized(%s%s)\n",
- level + 1, ' ',
- ZEND_TYPE_ALLOW_NULL(prop_info->type) ? "?" : "",
- ZEND_TYPE_IS_CLASS(prop_info->type) ?
- ZSTR_VAL(ZEND_TYPE_IS_CE(prop_info->type) ? ZEND_TYPE_CE(prop_info->type)->name : ZEND_TYPE_NAME(prop_info->type)) :
- zend_get_type_by_const(ZEND_TYPE_CODE(prop_info->type)));
+ zend_string *type_str = zend_type_to_string(prop_info->type);
+ php_printf("%*cuninitialized(%s)\n",
+ level + 1, ' ', ZSTR_VAL(type_str));
+ zend_string_release(type_str);
} else {
php_var_dump(zv, level + 2);
}
@@ -260,13 +258,10 @@ static void zval_object_property_dump(zend_property_info *prop_info, zval *zv, z
ZEND_PUTS("]=>\n");
}
if (prop_info && Z_TYPE_P(zv) == IS_UNDEF) {
- ZEND_ASSERT(prop_info->type);
- php_printf("%*cuninitialized(%s%s)\n",
- level + 1, ' ',
- ZEND_TYPE_ALLOW_NULL(prop_info->type) ? "?" : "",
- ZEND_TYPE_IS_CLASS(prop_info->type) ?
- ZSTR_VAL(ZEND_TYPE_IS_CE(prop_info->type) ? ZEND_TYPE_CE(prop_info->type)->name : ZEND_TYPE_NAME(prop_info->type)) :
- zend_get_type_by_const(ZEND_TYPE_CODE(prop_info->type)));
+ zend_string *type_str = zend_type_to_string(prop_info->type);
+ php_printf("%*cuninitialized(%s)\n",
+ level + 1, ' ', ZSTR_VAL(type_str));
+ zend_string_release(type_str);
} else {
php_debug_zval_dump(zv, level + 2);
}
diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c
index 63608124eb..2b1813ee47 100644
--- a/ext/zend_test/test.c
+++ b/ext/zend_test/test.c
@@ -219,7 +219,7 @@ PHP_MINIT_FUNCTION(zend_test)
zval val;
ZVAL_LONG(&val, 123);
zend_declare_typed_property(
- zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, ZEND_TYPE_ENCODE(IS_LONG, 0));
+ zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, ZEND_TYPE_ENCODE_CODE(IS_LONG, 0));
zend_string_release(name);
}
@@ -240,7 +240,7 @@ PHP_MINIT_FUNCTION(zend_test)
ZVAL_LONG(&val, 123);
zend_declare_typed_property(
zend_test_class, name, &val, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, NULL,
- ZEND_TYPE_ENCODE(IS_LONG, 0));
+ ZEND_TYPE_ENCODE_CODE(IS_LONG, 0));
zend_string_release(name);
}