static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info) /* {{{ */
{
/* `return ...;` is illegal in a void function (but `return;` isn't) */
- if (expr && return_info->type_hint == IS_VOID) {
- zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value");
+ if (return_info->type_hint == IS_VOID) {
+ if (expr) {
+ zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value");
+ }
+ /* we don't need run-time check */
+ return;
}
if (return_info->type_hint != IS_UNDEF) {
- zend_op *opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
+ zend_op *opline;
+
+ if (expr && expr->op_type == IS_CONST) {
+ if ((return_info->type_hint == Z_TYPE(expr->u.constant))
+ ||((return_info->type_hint == _IS_BOOL)
+ && (Z_TYPE(expr->u.constant) == IS_FALSE
+ || Z_TYPE(expr->u.constant) == IS_TRUE))
+ || (return_info->allow_null
+ && Z_TYPE(expr->u.constant) == IS_NULL)) {
+ /* we don't need run-time check */
+ return;
+ }
+ }
+
+ opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
if (expr && expr->op_type == IS_CONST) {
opline->result_type = expr->op_type = IS_TMP_VAR;
opline->result.var = expr->u.op.var = get_temporary_variable(CG(active_op_array));
op_array->opcodes[op2].opcode = ZEND_PRE_DEC;
SET_UNUSED(op_array->opcodes[op2].op2);
+ } else if (op_array->opcodes[op2].opcode == ZEND_VERIFY_RETURN_TYPE
+ && ssa->ops[op2].op1_def == i
+ && ssa->ops[op2].op1_use >= 0
+ && ssa->ops[op2].op1_use_chain == -1
+ && ssa->vars[i].use_chain >= 0
+ && (ssa->var_info[ssa->ops[op2].op1_use].type & (MAY_BE_ANY|MAY_BE_UNDEF)) == (ssa->var_info[ssa->ops[op2].op1_def].type & MAY_BE_ANY)) {
+ /* remove useless type check */
+ int var1 = ssa->ops[op2].op1_use;
+ int ret = ssa->vars[i].use_chain;
+
+ ssa->vars[var1].use_chain = ret;
+ ssa->ops[ret].op1_use = var1;
+
+ ssa->vars[i].definition = -1;
+ ssa->vars[i].use_chain = -1;
+
+ ssa->ops[op2].op1_def = -1;
+ ssa->ops[op2].op1_use = -1;
+
+ MAKE_NOP(&op_array->opcodes[op2]);
+ remove_nops = 1;
+
} else if (ssa->ops[op2].op1_def == i
&& !RETURN_VALUE_USED(&op_array->opcodes[op2])
&& ssa->ops[op2].op1_use >= 0
return NULL;
}
+static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce)
+{
+ uint32_t tmp = 0;
+
+ *pce = NULL;
+ if (arg_info->class_name) {
+ // class type hinting...
+ zend_string *lcname = zend_string_tolower(arg_info->class_name);
+ tmp |= MAY_BE_OBJECT;
+ *pce = get_class_entry(script, lcname);
+ zend_string_release(lcname);
+ } else if (arg_info->type_hint != IS_UNDEF) {
+ if (arg_info->type_hint == IS_VOID) {
+ tmp |= MAY_BE_NULL;
+ } else if (arg_info->type_hint == IS_CALLABLE) {
+ tmp |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ } else if (arg_info->type_hint == IS_ARRAY) {
+ tmp |= MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ } else if (arg_info->type_hint == _IS_BOOL) {
+ tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
+ } else {
+ ZEND_ASSERT(arg_info->type_hint < IS_REFERENCE);
+ tmp |= 1 << arg_info->type_hint;
+ }
+ } else {
+ tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ if (arg_info->allow_null) {
+ tmp |= MAY_BE_NULL;
+ }
+ return tmp;
+}
+
static void zend_update_type_info(const zend_op_array *op_array,
zend_ssa *ssa,
const zend_script *script,
ce = NULL;
if (arg_info) {
- tmp = 0;
- if (arg_info->class_name) {
- // class type hinting...
- zend_string *lcname = zend_string_tolower(arg_info->class_name);
- tmp |= MAY_BE_OBJECT;
- ce = get_class_entry(script, lcname);
- zend_string_release(lcname);
- } else if (arg_info->type_hint != IS_UNDEF) {
- if (arg_info->type_hint == IS_CALLABLE) {
- tmp |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- } else if (arg_info->type_hint == IS_ARRAY) {
- tmp |= MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- } else if (arg_info->type_hint == _IS_BOOL) {
- tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
- } else {
- ZEND_ASSERT(arg_info->type_hint < IS_REFERENCE);
- tmp |= 1 << arg_info->type_hint;
- }
- } else {
- tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
- }
- if (arg_info->allow_null) {
- tmp |= MAY_BE_NULL;
- } else if (opline->opcode == ZEND_RECV_INIT &&
+ tmp = zend_fetch_arg_info(script, arg_info, &ce);
+ if (opline->opcode == ZEND_RECV_INIT &&
Z_CONSTANT_P(CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants))) {
/* The constant may resolve to NULL */
tmp |= MAY_BE_NULL;
case ZEND_DEFINED:
UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_FALSE|MAY_BE_TRUE, ssa_ops[i].result_def);
break;
+ case ZEND_VERIFY_RETURN_TYPE:
+ {
+ zend_arg_info *ret_info = op_array->arg_info - 1;
+
+ tmp = zend_fetch_arg_info(script, ret_info, &ce);
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ if (opline->op1_type == IS_CONST) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ } else {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].op1_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+ }
+ }
+ break;
+ }
default:
unknown_opcode:
if (ssa_ops[i].op1_def >= 0) {
return 0;
}
+void zend_init_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ zend_ssa_var_info *ret)
+{
+ if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_arg_info *ret_info = op_array->arg_info - 1;
+ zend_ssa_range tmp_range = {0, 0, 0, 0};
+
+ ret->type = zend_fetch_arg_info(script, ret_info, &ret->ce);
+ ret->is_instanceof = (ret->ce) ? 1 : 0;
+ ret->range = tmp_range;
+ ret->has_range = 0;
+ }
+}
+
void zend_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
int recursive,
int widening,
zend_ssa_var_info *ret)
}
}
}
- if (tmp_is_instanceof < 0) {
- tmp_is_instanceof = 0;
- tmp_ce = NULL;
- }
- if (tmp_has_range < 0) {
- tmp_has_range = 0;
+
+ if (!(op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
+ if (tmp_is_instanceof < 0) {
+ tmp_is_instanceof = 0;
+ tmp_ce = NULL;
+ }
+ if (tmp_has_range < 0) {
+ tmp_has_range = 0;
+ }
+ ret->type = tmp;
+ ret->ce = tmp_ce;
+ ret->is_instanceof = tmp_is_instanceof;
}
- ret->type = tmp;
- ret->ce = tmp_ce;
- ret->is_instanceof = tmp_is_instanceof;
ret->range = tmp_range;
ret->has_range = tmp_has_range;
}
zend_type_narrowing(op_array, script, ssa);
if (ZEND_FUNC_INFO(op_array)) {
- zend_func_return_info(op_array, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info);
+ zend_func_return_info(op_array, script, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info);
}
free_alloca(worklist, use_heap);