[php-src] PHP-8.4: Fix too wide type inference for ASSIGN_DIM_OP

From: Date: Thu, 08 Oct 2026 16:49:47 +0000
Subject: [php-src] PHP-8.4: Fix too wide type inference for ASSIGN_DIM_OP
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-142090@lists.php.net to get a copy of this message
Author: ndossche (ndossche) Date: 2026-10-08T18:47:50+02:00 Commit: https://github.com/php/php-src/commit/fb1bdd6ff6968c42cc14bcc24035aefde7367b99 Raw diff: https://github.com/php/php-src/commit/fb1bdd6ff6968c42cc14bcc24035aefde7367b99.diff Fix too wide type inference for ASSIGN_DIM_OP The checks for a null result were done on the incorrect variables: t1 and t2 were already overwritten with the key and value type. This results in a null being inferred in the result in cases where this isn't possible. Closes GH-24092. Changed paths: A ext/opcache/tests/opt/assign_dim_op_result_type.phpt M NEWS M Zend/Optimizer/zend_inference.c Diff: diff --git a/NEWS b/NEWS index 12b88a7e8e6f..fd17e6a83fe6 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,7 @@ PHP NEWS root trace at the opcache.jit_max_root_traces limit, causing spurious "Too few arguments" errors and crashes). (RV7PR) . Fix type inference of ADD_ARRAY_UNPACK with integer keys. (ndossche) + . Fix too wide type inference for ASSIGN_DIM_OP. (ndossche) - SOAP: . Fixed use of uninitialized func in do_request() on OOM bailout. diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 66369a154820..0c24691ebba6 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -2809,11 +2809,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; 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]]

« previous php.cvs (#142090) next »