mirror of
https://github.com/php/php-src.git
synced 2024-09-21 18:07:23 +00:00
Don't make argument nullable based on AST null initializer
Closes GH-4720.
This commit is contained in:
parent
d6ef63db53
commit
cdd4e591a4
10
UPGRADING
10
UPGRADING
@ -76,6 +76,16 @@ PHP 8.0 UPGRADE NOTES
|
||||
. The precedence of the concatenation operator has changed relative to
|
||||
bitshifts and addition as well as subtraction.
|
||||
RFC: https://wiki.php.net/rfc/concatenation_precedence
|
||||
. Arguments with a default-value that resolves to null at run-time will no
|
||||
longer implicitly mark the argument type as nullable. Either use an explicit
|
||||
nullable type, or an explicit null default value instead.
|
||||
|
||||
// Replace
|
||||
function test(int $arg = CONST_RESOLVING_TO_NULL) {}
|
||||
// With
|
||||
function test(?int $arg = CONST_RESOLVING_TO_NULL) {}
|
||||
// Or
|
||||
function test(int $arg = null) {}
|
||||
|
||||
- COM:
|
||||
. Removed the ability to import case-insensitive constants from type
|
||||
|
@ -9,7 +9,7 @@ function a(array $a = FOO) {
|
||||
var_dump($a);
|
||||
}
|
||||
|
||||
function b(array $b = BAR) {
|
||||
function b(?array $b = BAR) {
|
||||
var_dump($b);
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,10 @@ function int_val_default_null(int $a = NULL_VAL) {
|
||||
return $a;
|
||||
}
|
||||
|
||||
function nullable_int_val_default_null(?int $a = NULL_VAL) {
|
||||
return $a;
|
||||
}
|
||||
|
||||
echo "Testing int val" . PHP_EOL;
|
||||
var_dump(int_val());
|
||||
|
||||
@ -58,13 +62,27 @@ echo "Testing string add val" . PHP_EOL;
|
||||
var_dump(string_add_val());
|
||||
|
||||
echo "Testing int with default null constant" . PHP_EOL;
|
||||
var_dump(int_val_default_null());
|
||||
try {
|
||||
var_dump(int_val_default_null());
|
||||
} catch (TypeError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
echo "Testing int with null null constant" . PHP_EOL;
|
||||
var_dump(int_val_default_null(null));
|
||||
try {
|
||||
var_dump(int_val_default_null(null));
|
||||
} catch (TypeError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
echo "Testing nullable int with default null constant" . PHP_EOL;
|
||||
var_dump(nullable_int_val_default_null());
|
||||
|
||||
echo "Testing nullable int with null null constant" . PHP_EOL;
|
||||
var_dump(nullable_int_val_default_null(null));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--EXPECTF--
|
||||
Testing int val
|
||||
int(10)
|
||||
Testing float val
|
||||
@ -78,6 +96,10 @@ float(10.7)
|
||||
Testing string add val
|
||||
string(14) "this is a test"
|
||||
Testing int with default null constant
|
||||
NULL
|
||||
Argument 1 passed to int_val_default_null() must be of the type int, null given, called in %s on line %d
|
||||
Testing int with null null constant
|
||||
Argument 1 passed to int_val_default_null() must be of the type int, null given, called in %s on line %d
|
||||
Testing nullable int with default null constant
|
||||
NULL
|
||||
Testing nullable int with null null constant
|
||||
NULL
|
||||
|
@ -793,23 +793,6 @@ static ZEND_COLD void zend_verify_arg_error(
|
||||
}
|
||||
}
|
||||
|
||||
static int is_null_constant(zend_class_entry *scope, zval *default_value)
|
||||
{
|
||||
if (Z_TYPE_P(default_value) == IS_CONSTANT_AST) {
|
||||
zval constant;
|
||||
|
||||
ZVAL_COPY(&constant, default_value);
|
||||
if (UNEXPECTED(zval_update_constant_ex(&constant, scope) != SUCCESS)) {
|
||||
return 0;
|
||||
}
|
||||
if (Z_TYPE(constant) == IS_NULL) {
|
||||
return 1;
|
||||
}
|
||||
zval_ptr_dtor_nogc(&constant);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg)
|
||||
{
|
||||
switch (type_hint) {
|
||||
@ -1043,7 +1026,7 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf
|
||||
static zend_always_inline zend_bool zend_check_type(
|
||||
zend_type type,
|
||||
zval *arg, zend_class_entry **ce, void **cache_slot,
|
||||
zval *default_value, zend_class_entry *scope,
|
||||
zend_class_entry *scope,
|
||||
zend_bool is_return_type, zend_bool is_internal)
|
||||
{
|
||||
zend_reference *ref = NULL;
|
||||
@ -1063,19 +1046,19 @@ static zend_always_inline zend_bool zend_check_type(
|
||||
} else {
|
||||
*ce = zend_fetch_class(ZEND_TYPE_NAME(type), (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
|
||||
if (UNEXPECTED(!*ce)) {
|
||||
return Z_TYPE_P(arg) == IS_NULL && (ZEND_TYPE_ALLOW_NULL(type) || (default_value && is_null_constant(scope, default_value)));
|
||||
return Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(type);
|
||||
}
|
||||
*cache_slot = (void *) *ce;
|
||||
}
|
||||
if (EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
|
||||
return instanceof_function(Z_OBJCE_P(arg), *ce);
|
||||
}
|
||||
return Z_TYPE_P(arg) == IS_NULL && (ZEND_TYPE_ALLOW_NULL(type) || (default_value && is_null_constant(scope, default_value)));
|
||||
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) || (default_value && is_null_constant(scope, default_value)))) {
|
||||
if (Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(type)) {
|
||||
/* Null passed to nullable type */
|
||||
return 1;
|
||||
}
|
||||
@ -1104,7 +1087,7 @@ static zend_always_inline zend_bool zend_check_type(
|
||||
* because this case is already checked at compile-time. */
|
||||
}
|
||||
|
||||
static zend_always_inline int zend_verify_recv_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot)
|
||||
static zend_always_inline int zend_verify_recv_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot)
|
||||
{
|
||||
zend_arg_info *cur_arg_info = &zf->common.arg_info[arg_num-1];
|
||||
zend_class_entry *ce;
|
||||
@ -1113,7 +1096,7 @@ static zend_always_inline int zend_verify_recv_arg_type(zend_function *zf, uint3
|
||||
cur_arg_info = &zf->common.arg_info[arg_num-1];
|
||||
|
||||
ce = NULL;
|
||||
if (UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, &ce, cache_slot, default_value, zf->common.scope, 0, 0))) {
|
||||
if (UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, &ce, cache_slot, zf->common.scope, 0, 0))) {
|
||||
zend_verify_arg_error(zf, cur_arg_info, arg_num, ce, arg);
|
||||
return 0;
|
||||
}
|
||||
@ -1121,7 +1104,7 @@ static zend_always_inline int zend_verify_recv_arg_type(zend_function *zf, uint3
|
||||
return 1;
|
||||
}
|
||||
|
||||
static zend_always_inline int zend_verify_variadic_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot)
|
||||
static zend_always_inline int zend_verify_variadic_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot)
|
||||
{
|
||||
zend_arg_info *cur_arg_info;
|
||||
zend_class_entry *ce;
|
||||
@ -1131,7 +1114,7 @@ static zend_always_inline int zend_verify_variadic_arg_type(zend_function *zf, u
|
||||
cur_arg_info = &zf->common.arg_info[zf->common.num_args];
|
||||
|
||||
ce = NULL;
|
||||
if (UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, &ce, cache_slot, default_value, zf->common.scope, 0, 0))) {
|
||||
if (UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, &ce, cache_slot, zf->common.scope, 0, 0))) {
|
||||
zend_verify_arg_error(zf, cur_arg_info, arg_num, ce, arg);
|
||||
return 0;
|
||||
}
|
||||
@ -1158,7 +1141,7 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED int zend_verify_internal_arg_type
|
||||
break;
|
||||
}
|
||||
|
||||
if (UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, &ce, &dummy_cache_slot, NULL, fbc->common.scope, 0, /* is_internal */ 1))) {
|
||||
if (UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, &ce, &dummy_cache_slot, fbc->common.scope, 0, /* is_internal */ 1))) {
|
||||
return 0;
|
||||
}
|
||||
arg++;
|
||||
@ -1254,7 +1237,7 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (UNEXPECTED(!zend_check_type(ret_info->type, ret, &ce, &dummy_cache_slot, NULL, NULL, 1, /* is_internal */ 1))) {
|
||||
if (UNEXPECTED(!zend_check_type(ret_info->type, ret, &ce, &dummy_cache_slot, NULL, 1, /* is_internal */ 1))) {
|
||||
zend_verify_internal_return_error(zf, ce, ret);
|
||||
return 0;
|
||||
}
|
||||
@ -1268,7 +1251,7 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
|
||||
zend_arg_info *ret_info = zf->common.arg_info - 1;
|
||||
zend_class_entry *ce = NULL;
|
||||
|
||||
if (UNEXPECTED(!zend_check_type(ret_info->type, ret, &ce, cache_slot, NULL, NULL, 1, 0))) {
|
||||
if (UNEXPECTED(!zend_check_type(ret_info->type, ret, &ce, cache_slot, NULL, 1, 0))) {
|
||||
zend_verify_return_error(zf, ce, ret);
|
||||
}
|
||||
}
|
||||
|
@ -5198,7 +5198,7 @@ ZEND_VM_HOT_HANDLER(63, ZEND_RECV, NUM, UNUSED|CACHE_SLOT)
|
||||
zval *param = EX_VAR(opline->result.var);
|
||||
|
||||
SAVE_OPLINE();
|
||||
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, NULL, CACHE_ADDR(opline->op2.num)) || EG(exception))) {
|
||||
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, CACHE_ADDR(opline->op2.num)) || EG(exception))) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
@ -5243,10 +5243,8 @@ ZEND_VM_HOT_HANDLER(64, ZEND_RECV_INIT, NUM, CONST, CACHE_SLOT)
|
||||
}
|
||||
|
||||
if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0)) {
|
||||
zval *default_value = RT_CONSTANT(opline, opline->op2);
|
||||
|
||||
SAVE_OPLINE();
|
||||
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, default_value, CACHE_ADDR(opline->extended_value)) || EG(exception))) {
|
||||
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, CACHE_ADDR(opline->extended_value)) || EG(exception))) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
@ -5275,7 +5273,7 @@ ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED|CACHE_SLOT)
|
||||
param = EX_VAR_NUM(EX(func)->op_array.last_var + EX(func)->op_array.T);
|
||||
if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0)) {
|
||||
do {
|
||||
zend_verify_variadic_arg_type(EX(func), arg_num, param, NULL, CACHE_ADDR(opline->op2.num));
|
||||
zend_verify_variadic_arg_type(EX(func), arg_num, param, CACHE_ADDR(opline->op2.num));
|
||||
if (Z_OPT_REFCOUNTED_P(param)) Z_ADDREF_P(param);
|
||||
ZEND_HASH_FILL_ADD(param);
|
||||
param++;
|
||||
|
@ -3046,10 +3046,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CON
|
||||
}
|
||||
|
||||
if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0)) {
|
||||
zval *default_value = RT_CONSTANT(opline, opline->op2);
|
||||
|
||||
SAVE_OPLINE();
|
||||
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, default_value, CACHE_ADDR(opline->extended_value)) || EG(exception))) {
|
||||
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, CACHE_ADDR(opline->extended_value)) || EG(exception))) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
@ -3127,7 +3125,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_SPEC_UNUSED_H
|
||||
zval *param = EX_VAR(opline->result.var);
|
||||
|
||||
SAVE_OPLINE();
|
||||
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, NULL, CACHE_ADDR(opline->op2.num)) || EG(exception))) {
|
||||
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, CACHE_ADDR(opline->op2.num)) || EG(exception))) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
@ -3155,7 +3153,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_VARIADIC_SPEC_UNUSED_HAND
|
||||
param = EX_VAR_NUM(EX(func)->op_array.last_var + EX(func)->op_array.T);
|
||||
if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0)) {
|
||||
do {
|
||||
zend_verify_variadic_arg_type(EX(func), arg_num, param, NULL, CACHE_ADDR(opline->op2.num));
|
||||
zend_verify_variadic_arg_type(EX(func), arg_num, param, CACHE_ADDR(opline->op2.num));
|
||||
if (Z_OPT_REFCOUNTED_P(param)) Z_ADDREF_P(param);
|
||||
ZEND_HASH_FILL_ADD(param);
|
||||
param++;
|
||||
|
@ -3104,11 +3104,6 @@ static int zend_update_type_info(const zend_op_array *op_array,
|
||||
ce = NULL;
|
||||
if (arg_info) {
|
||||
tmp = zend_fetch_arg_info_type(script, arg_info, &ce);
|
||||
if (opline->opcode == ZEND_RECV_INIT &&
|
||||
Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants)) == IS_CONSTANT_AST) {
|
||||
/* The constant may resolve to NULL */
|
||||
tmp |= MAY_BE_NULL;
|
||||
}
|
||||
if (arg_info->pass_by_reference) {
|
||||
tmp |= MAY_BE_REF;
|
||||
}
|
||||
|
@ -1115,23 +1115,6 @@ static zval* ZEND_FASTCALL zend_jit_fetch_global_helper(zend_execute_data *execu
|
||||
return value;
|
||||
}
|
||||
|
||||
static int is_null_constant(zend_class_entry *scope, zval *default_value)
|
||||
{
|
||||
if (Z_CONSTANT_P(default_value)) {
|
||||
zval constant;
|
||||
|
||||
ZVAL_COPY(&constant, default_value);
|
||||
if (UNEXPECTED(zval_update_constant_ex(&constant, scope) != SUCCESS)) {
|
||||
return 0;
|
||||
}
|
||||
if (Z_TYPE(constant) == IS_NULL) {
|
||||
return 1;
|
||||
}
|
||||
zval_ptr_dtor(&constant);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg)
|
||||
{
|
||||
switch (type_hint) {
|
||||
@ -1313,12 +1296,11 @@ 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, zval *default_value)
|
||||
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) || (default_value && is_null_constant(op_array->scope, default_value)))) {
|
||||
if (Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(arg_info->type)) {
|
||||
/* Null passed to nullable type */
|
||||
return;
|
||||
}
|
||||
|
@ -9021,16 +9021,13 @@ static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, zend_op_array
|
||||
| mov CARG3, arg_num
|
||||
| LOAD_ADDR CARG4, (ptrdiff_t)arg_info
|
||||
| mov aword A5, r0
|
||||
| mov aword A6, 0
|
||||
| EXT_CALL zend_jit_verify_arg_slow, r0
|
||||
|.elif X64
|
||||
| mov CARG3, arg_num
|
||||
| LOAD_ADDR CARG4, (ptrdiff_t)arg_info
|
||||
| mov CARG5, r0
|
||||
| xor CARG6, CARG6
|
||||
| EXT_CALL zend_jit_verify_arg_slow, r0
|
||||
|.else
|
||||
| push 0
|
||||
| push r0
|
||||
| push (ptrdiff_t)arg_info
|
||||
| push arg_num
|
||||
@ -9190,16 +9187,13 @@ static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, zend_op_a
|
||||
| mov CARG3, arg_num
|
||||
| LOAD_ADDR CARG4, (ptrdiff_t)arg_info
|
||||
| mov aword A5, r0
|
||||
| ADDR_OP2_2 mov, aword A6, zv, r0
|
||||
| EXT_CALL zend_jit_verify_arg_slow, r0
|
||||
|.elif X64
|
||||
| mov CARG3, arg_num
|
||||
| LOAD_ADDR CARG4, (ptrdiff_t)arg_info
|
||||
| mov CARG5, r0
|
||||
| LOAD_ADDR CARG6, zv
|
||||
| EXT_CALL zend_jit_verify_arg_slow, r0
|
||||
|.else
|
||||
| push zv
|
||||
| push r0
|
||||
| push (ptrdiff_t)arg_info
|
||||
| push arg_num
|
||||
|
Loading…
Reference in New Issue
Block a user