Skip to content
2 changes: 2 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2699,6 +2699,8 @@ static void zend_emit_return_type_check(
}

opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
/* Store pure type mask in extended_value */
opline->extended_value = ZEND_TYPE_PURE_MASK(type);
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();
Expand Down
5 changes: 4 additions & 1 deletion Zend/zend_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,11 @@ typedef struct {
#define ZEND_TYPE_PURE_MASK_WITHOUT_NULL(t) \
((t).type_mask & _ZEND_TYPE_MAY_BE_MASK & ~_ZEND_TYPE_NULLABLE_BIT)

#define ZEND_TYPE_MASK_CONTAINS_CODE(mask, code) \
(((mask) & (1u << (code))) != 0)

#define ZEND_TYPE_CONTAINS_CODE(t, code) \
(((t).type_mask & (1u << (code))) != 0)
ZEND_TYPE_MASK_CONTAINS_CODE((t).type_mask, code)

#define ZEND_TYPE_ALLOW_NULL(t) \
(((t).type_mask & _ZEND_TYPE_NULLABLE_BIT) != 0)
Expand Down
72 changes: 69 additions & 3 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
* php zend_vm_gen.php
*/

#include <stdint.h>

ZEND_VM_HELPER(zend_add_helper, ANY, ANY, zval *op_1, zval *op_2)
{
USE_OPLINE
Expand Down Expand Up @@ -4471,7 +4473,6 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
#if !ZEND_VM_SPEC || (OP1_TYPE != IS_UNUSED)
USE_OPLINE
zval *retval_ref, *retval_ptr;
zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
retval_ref = retval_ptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);

if (OP1_TYPE == IS_CONST) {
Expand All @@ -4486,7 +4487,8 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
ZVAL_DEREF(retval_ptr);
}

if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) {
uint32_t pure_type_mask = opline->extended_value;
if (EXPECTED(ZEND_TYPE_MASK_CONTAINS_CODE(pure_type_mask, Z_TYPE_P(retval_ptr)))) {
ZEND_VM_NEXT_OPCODE();
}

Expand All @@ -4496,7 +4498,7 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
if (UNEXPECTED(EG(exception))) {
HANDLE_EXCEPTION();
}
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) {
if (pure_type_mask & MAY_BE_NULL) {
ZEND_VM_NEXT_OPCODE();
}
}
Expand All @@ -4517,6 +4519,8 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
}
}

/* Fetch full return type info from function arg_info */
const zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
SAVE_OPLINE();
if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
Expand All @@ -4527,6 +4531,68 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
}
}

ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_VERIFY_RETURN_TYPE, (((res_info & MAY_BE_REF) == 0) && op1_info == MAY_BE_LONG), ZEND_VERIFY_RETURN_TYPE_FOR_LONG, CONST|TMPVAR|CV, UNUSED)
{
USE_OPLINE

zval *retval_ptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
if (OP1_TYPE == IS_CONST) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), retval_ptr);
retval_ptr = EX_VAR(opline->result.var);
}

uint32_t pure_type_mask = opline->extended_value;
if (pure_type_mask & MAY_BE_LONG) {
ZVAL_LONG(retval_ptr, Z_LVAL_P(retval_ptr));
ZEND_VM_NEXT_OPCODE();
}

/* Type preference order: float -> string -> bool */
/* int to float is always valid even with strict types */
if (pure_type_mask & MAY_BE_DOUBLE) {
ZVAL_DOUBLE(retval_ptr, (double)Z_LVAL_P(retval_ptr));
ZEND_VM_NEXT_OPCODE();
}

if (UNEXPECTED(EX_USES_STRICT_TYPES())) {
SAVE_OPLINE();
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}

if (pure_type_mask & MAY_BE_STRING) {
ZVAL_STR(retval_ptr, zend_long_to_str(Z_LVAL_P(retval_ptr)));
} else if (pure_type_mask & MAY_BE_BOOL) {
ZVAL_BOOL(retval_ptr, Z_LVAL_P(retval_ptr));
} else {
SAVE_OPLINE();
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_VERIFY_RETURN_TYPE, (((res_info & MAY_BE_REF) == 0) && (op1_info == MAY_BE_DOUBLE || op1_info == MAY_BE_STRING)), ZEND_VERIFY_RETURN_TYPE_FOR_STRING_AND_DOUBLE, CONST|TMPVAR|CV, UNUSED)
{
USE_OPLINE
zval *retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
if (OP1_TYPE == IS_CONST) {
ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr);
retval_ptr = EX_VAR(opline->result.var);
}
uint32_t pure_type_mask = opline->extended_value;

if (EXPECTED(ZEND_TYPE_MASK_CONTAINS_CODE(pure_type_mask, Z_TYPE_P(retval_ptr)))) {
ZEND_VM_NEXT_OPCODE();
} else if (EXPECTED(zend_verify_scalar_type_hint(pure_type_mask, retval_ptr, EX_USES_STRICT_TYPES(), /* is_internal*/ false))) {
ZEND_VM_NEXT_OPCODE();
} else {
SAVE_OPLINE();
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
}

ZEND_VM_COLD_HANDLER(201, ZEND_VERIFY_NEVER_TYPE, UNUSED, UNUSED)
{
SAVE_OPLINE();
Expand Down
Loading
Loading