[php-src] master: Merge branch 'PHP-8.6'

From: Date: Thu, 08 Oct 2026 16:49:54 +0000
Subject: [php-src] master: Merge branch 'PHP-8.6'
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-142094@lists.php.net to get a copy of this message
Author: ndossche (ndossche) Date: 2026-10-08T18:49:39+02:00 Commit: https://github.com/php/php-src/commit/eb4a7ae65f9671a4f3fef3dca9115259deb87b94 Raw diff: https://github.com/php/php-src/commit/eb4a7ae65f9671a4f3fef3dca9115259deb87b94.diff Merge branch 'PHP-8.6' * PHP-8.6: Fix OSS-Fuzz #568005340: FETCH_DIM_FUNC_ARG partial conversion Fix too wide type inference for ASSIGN_DIM_OP Fix type inference of ADD_ARRAY_UNPACK with integer keys Changed paths: A ext/opcache/tests/jit/add_array_unpack_packed.phpt A ext/opcache/tests/opt/assign_dim_op_result_type.phpt A ext/opcache/tests/opt/fetch_dim_func_arg_unused_chain.phpt M Zend/Optimizer/optimize_func_calls.c M Zend/Optimizer/zend_inference.c Diff: diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c index 0e6c5797c680..4275bdb4071c 100644 --- a/Zend/Optimizer/optimize_func_calls.c +++ b/Zend/Optimizer/optimize_func_calls.c @@ -247,12 +247,13 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_FETCH_OBJ_FUNC_ARG: case ZEND_FETCH_DIM_FUNC_ARG: if (call_stack[call - 1].func_arg_num != (uint32_t)-1 + && call_stack[call - 1].last_check_func_arg_opline != NULL && has_known_send_mode(&call_stack[call - 1], call_stack[call - 1].func_arg_num)) { if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) { /* There's no TMP specialization for FETCH_OBJ_W/FETCH_DIM_W. Avoid * converting it and error at runtime in the FUNC_ARG variant. */ if ((opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG || opline->opcode == ZEND_FETCH_DIM_FUNC_ARG) - && (opline->op1_type == IS_TMP_VAR || call_stack[call - 1].last_check_func_arg_opline == NULL)) { + && opline->op1_type == IS_TMP_VAR) { /* Don't remove the associated CHECK_FUNC_ARG opcode. */ call_stack[call - 1].last_check_func_arg_opline = NULL; break; @@ -268,6 +269,8 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not. * Performing the replacement would create an invalid opcode. */ call_stack[call - 1].try_inline = false; + /* Don't remove the associated CHECK_FUNC_ARG opcode. */ + call_stack[call - 1].last_check_func_arg_opline = NULL; break; } diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 9b46134a7bae..71c9247edf67 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -2810,11 +2810,11 @@ static zend_always_inline zend_result _zend_update_type_info( * null will be returned. */ tmp |= MAY_BE_NULL; } - if (t2 & (MAY_BE_ARRAY | MAY_BE_OBJECT)) { + if (OP2_INFO() & (MAY_BE_ARRAY | MAY_BE_OBJECT)) { /* Arrays and objects cannot be used as keys. */ tmp |= MAY_BE_NULL; } - if (t1 & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY))) { + if (OP1_INFO() & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY))) { /* null and false are implicitly converted to array, anything else * results in a null return value. */ tmp |= MAY_BE_NULL; @@ -3455,7 +3455,11 @@ static zend_always_inline zend_result _zend_update_type_info( case ZEND_ADD_ARRAY_UNPACK: tmp = ssa_var_info[ssa_op->result_use].type; ZEND_ASSERT(tmp & MAY_BE_ARRAY); - tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); + if (t1 & MAY_BE_ARRAY_KEY_LONG) { + /* Integer keys are appended without copying the hash/packed layout of the source array. */ + tmp |= MAY_BE_HASH_ONLY(tmp) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG; + } + tmp |= t1 & (MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); if (t1 & MAY_BE_OBJECT) { tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY; } diff --git a/ext/opcache/tests/jit/add_array_unpack_packed.phpt b/ext/opcache/tests/jit/add_array_unpack_packed.phpt new file mode 100644 index 000000000000..f0b6b9a33cec --- /dev/null +++ b/ext/opcache/tests/jit/add_array_unpack_packed.phpt @@ -0,0 +1,24 @@ +--TEST-- +JIT: unpacking a hash array with integer keys produces a packed array +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit_buffer_size=64M +opcache.jit=1205 +--EXTENSIONS-- +opcache +--FILE-- +<?php +function f($c) { + $b = $c ? [-5 => 1, -6 => 2, -7 => 3] : [-7 => 2]; + $a = [...$b]; + foreach ($a as $k => $v) { + echo "$k => $v\n"; + } +} +f(true); +?> +--EXPECT-- +0 => 1 +1 => 2 +2 => 3 diff --git a/ext/opcache/tests/opt/assign_dim_op_result_type.phpt b/ext/opcache/tests/opt/assign_dim_op_result_type.phpt new file mode 100644 index 000000000000..b449cebdbd9b --- /dev/null +++ b/ext/opcache/tests/opt/assign_dim_op_result_type.phpt @@ -0,0 +1,82 @@ +--TEST-- +Type inference of ASSIGN_DIM_OP result uses the container and key types +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x400000 +opcache.preload= +--EXTENSIONS-- +opcache +--FILE-- +<?php +function f(int $p, int $q, int $k) { + $a = [$p, $q]; + $r = ($a[$k] += 1); + return $r; +} + +function g(int $p, int $q, int $k) { + $a = [[$p], [$q]]; + $r = ($a[$k] += [1]); + return $r; +} +?> +--EXPECTF-- +$_main: + ; (lines=1, args=0, vars=0, tmps=%d, ssa_vars=0, no_loops) + ; (after dfa pass) + ; %s + ; return [long] RANGE[1..1] +BB0: + ; start exit lines=[0-0] + ; level=0 +0000 RETURN int(1) + +f: + ; (lines=9, args=3, vars=5, tmps=%d, ssa_vars=14, no_loops) + ; (after dfa pass) + ; %s + ; return [long, double] + ; #0.CV0($p) NOVAL [undef] + ; #1.CV1($q) NOVAL [undef] + ; #2.CV2($k) NOVAL [undef] + ; #3.CV3($a) NOVAL [undef] + ; #4.CV4($r) NOVAL [undef] +BB0: + ; start exit lines=[0-8] + ; level=0 +0000 #5.CV0($p) [long] RANGE[MIN..MAX] = RECV 1 +0001 #6.CV1($q) [long] RANGE[MIN..MAX] = RECV 2 +0002 #7.CV2($k) [long] RANGE[MIN..MAX] = RECV 3 +0003 #8.T5 NOESC [[packed, hash] array [long] of [long]] = INIT_ARRAY 2 (packed) #5.CV0($p) [long] RANGE[MIN..MAX] NEXT +0004 ADD_ARRAY_ELEMENT #6.CV1($q) [long] RANGE[MIN..MAX] NEXT #8.T5 NOESC [[packed, hash] array [long] of [long]] -> #9.T5 NOESC [[packed, hash] array [long] of [long]] +0005 #10.CV3($a) NOESC [[packed, hash] array [long] of [long]] = QM_ASSIGN #9.T5 NOESC [[packed, hash] array [long] of [long]] +0006 #13.CV4($r) [long, double] = ASSIGN_DIM_OP (ADD) #10.CV3($a) NOESC [[packed, hash] array [long] of [long]] -> #11.CV3($a) NOVAL NOESC [[packed, hash] array [long] of [long, double]] #7.CV2($k) [long] RANGE[MIN..MAX] +0007 OP_DATA int(1) +0008 RETURN #13.CV4($r) [long, double] + +g: + ; (lines=11, args=3, vars=5, tmps=%d, ssa_vars=16, no_loops) + ; (after dfa pass) + ; %s + ; return [long, double, array of [any, ref]] + ; #0.CV0($p) NOVAL [undef] + ; #1.CV1($q) NOVAL [undef] + ; #2.CV2($k) NOVAL [undef] + ; #3.CV3($a) NOVAL [undef] + ; #4.CV4($r) NOVAL [undef] +BB0: + ; start exit lines=[0-10] + ; level=0 +0000 #5.CV0($p) [long] RANGE[MIN..MAX] = RECV 1 +0001 #6.CV1($q) [long] RANGE[MIN..MAX] = RECV 2 +0002 #7.CV2($k) [long] RANGE[MIN..MAX] = RECV 3 +0003 #8.T5 NOESC [[packed, hash] array [long] of [long]] = INIT_ARRAY 1 (packed) #5.CV0($p) [long] RANGE[MIN..MAX] NEXT +0004 #9.T6 NOESC [[packed, hash] array [long] of [array]] = INIT_ARRAY 2 (packed) #8.T5 NOESC [[packed, hash] array [long] of [long]] NEXT +0005 #10.T7 NOESC [[packed, hash] array [long] of [long]] = INIT_ARRAY 1 (packed) #6.CV1($q) [long] RANGE[MIN..MAX] NEXT +0006 ADD_ARRAY_ELEMENT #10.T7 NOESC [[packed, hash] array [long] of [long]] NEXT #9.T6 NOESC [[packed, hash] array [long] of [array]] -> #11.T6 NOESC [[packed, hash] array [long] of [array]] +0007 #12.CV3($a) NOESC [[packed, hash] array [long] of [array]] = QM_ASSIGN #11.T6 NOESC [[packed, hash] array [long] of [array]] +0008 #15.CV4($r) [long, double, array of [any, ref]] = ASSIGN_DIM_OP (ADD) #12.CV3($a) NOESC [[packed, hash] array [long] of [array]] -> #13.CV3($a) NOVAL NOESC [[packed, hash] array [long] of [long, double, array]] #7.CV2($k) [long] RANGE[MIN..MAX] +0009 OP_DATA array(...) +0010 RETURN #15.CV4($r) [long, double, array of [any, ref]] diff --git a/ext/opcache/tests/opt/fetch_dim_func_arg_unused_chain.phpt b/ext/opcache/tests/opt/fetch_dim_func_arg_unused_chain.phpt new file mode 100644 index 000000000000..4b2bcf01b49e --- /dev/null +++ b/ext/opcache/tests/opt/fetch_dim_func_arg_unused_chain.phpt @@ -0,0 +1,29 @@ +--TEST-- +FETCH_DIM_FUNC_ARG with UNUSED op2 followed by other FUNC_ARG fetches must not be partially converted +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--EXTENSIONS-- +opcache +--FILE-- +<?php +function test() { + try { + new Node($a[]->b); + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } + try { + byVal($a[][0]); + } catch (Error $e) { + echo $e->getMessage(), "\n"; + } +} +class Node { function __construct() {} } +function byVal($x) {} +test(); +?> +--EXPECT-- +Cannot use [] for reading +Cannot use [] for reading

« previous php.cvs (#142094) next »