Skip to content

Commit c803402

Browse files
committed
Merge branch 'PHP-8.3'
* PHP-8.3: Fix zend_separate_if_call_and_write for FUNC_ARGs
2 parents 06c4092 + c2bb9bc commit c803402

File tree

6 files changed

+133
-3
lines changed

6 files changed

+133
-3
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ PHP NEWS
55
Core:
66
. Fixed bug GH-12073 (Segfault when freeing incompletely initialized
77
closures). (ilutov)
8+
. Fixed bug GH-12102 (Incorrect compile error when using array access on TMP
9+
value in function call). (ilutov)
810

911
<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>

Zend/Optimizer/optimize_func_calls.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
typedef struct _optimizer_call_info {
3232
zend_function *func;
3333
zend_op *opline;
34+
zend_op *last_check_func_arg_opline;
3435
bool is_prototype;
3536
bool try_inline;
3637
uint32_t func_arg_num;
@@ -235,6 +236,14 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
235236
if (call_stack[call - 1].func_arg_num != (uint32_t)-1
236237
&& has_known_send_mode(&call_stack[call - 1], call_stack[call - 1].func_arg_num)) {
237238
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) {
239+
/* There's no TMP specialization for FETCH_OBJ_W/FETCH_DIM_W. Avoid
240+
* converting it and error at runtime in the FUNC_ARG variant. */
241+
if ((opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG || opline->opcode == ZEND_FETCH_DIM_FUNC_ARG)
242+
&& (opline->op1_type == IS_TMP_VAR || call_stack[call - 1].last_check_func_arg_opline == NULL)) {
243+
/* Don't remove the associated CHECK_FUNC_ARG opcode. */
244+
call_stack[call - 1].last_check_func_arg_opline = NULL;
245+
break;
246+
}
238247
if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
239248
opline->opcode -= 9;
240249
} else {
@@ -278,11 +287,21 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
278287

279288
if (has_known_send_mode(&call_stack[call - 1], opline->op2.num)) {
280289
call_stack[call - 1].func_arg_num = opline->op2.num;
281-
MAKE_NOP(opline);
290+
call_stack[call - 1].last_check_func_arg_opline = opline;
282291
}
283292
break;
284-
case ZEND_SEND_VAR_EX:
285293
case ZEND_SEND_FUNC_ARG:
294+
/* Don't transform SEND_FUNC_ARG if any FETCH opcodes weren't transformed. */
295+
if (call_stack[call - 1].last_check_func_arg_opline == NULL) {
296+
if (opline->op2_type == IS_CONST) {
297+
call_stack[call - 1].try_inline = 0;
298+
}
299+
break;
300+
}
301+
MAKE_NOP(call_stack[call - 1].last_check_func_arg_opline);
302+
call_stack[call - 1].last_check_func_arg_opline = NULL;
303+
ZEND_FALLTHROUGH;
304+
case ZEND_SEND_VAR_EX:
286305
if (opline->op2_type == IS_CONST) {
287306
call_stack[call - 1].try_inline = 0;
288307
break;

Zend/tests/gh12102_1.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
GH-12102: Incorrect "Cannot use temporary expression in write context" error for BP_VAR_FUNC_ARG
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
byVal(func_get_args()[0]);
8+
try {
9+
byRef(func_get_args()[0]);
10+
} catch (Error $e) {
11+
echo $e->getMessage(), "\n";
12+
}
13+
}
14+
15+
/* Intentionally declared after test() to avoid compile-time checking of ref args. */
16+
17+
function byVal($arg) {
18+
var_dump($arg);
19+
}
20+
21+
function byRef(&$arg) {
22+
var_dump($arg);
23+
}
24+
25+
test('y');
26+
27+
?>
28+
--EXPECT--
29+
string(1) "y"
30+
Cannot use temporary expression in write context

Zend/tests/gh12102_2.phpt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
GH-12102: Incorrect "Cannot use temporary expression in write context" error for BP_VAR_FUNC_ARG
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
global $ref;
8+
byVal(getRef()[0]);
9+
var_dump($ref);
10+
byRef(getRef()[0]);
11+
var_dump($ref);
12+
}
13+
14+
/* Intentionally declared after test() to avoid compile-time checking of ref args. */
15+
16+
function &getRef() {
17+
global $ref;
18+
$ref = [];
19+
return $ref;
20+
}
21+
22+
function byVal($arg) {
23+
$arg[] = 42;
24+
}
25+
26+
function byRef(&$arg) {
27+
$arg[] = 42;
28+
}
29+
30+
test();
31+
32+
?>
33+
--EXPECTF--
34+
Warning: Undefined array key 0 in %s on line %d
35+
array(0) {
36+
}
37+
array(1) {
38+
[0]=>
39+
array(1) {
40+
[0]=>
41+
int(42)
42+
}
43+
}

Zend/tests/gh12102_3.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
GH-12102: Incorrect "Cannot use temporary expression in write context" error for BP_VAR_FUNC_ARG
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
byVal(C[0]);
8+
try {
9+
byRef(C[0]);
10+
} catch (Error $e) {
11+
echo $e->getMessage(), "\n";
12+
}
13+
}
14+
15+
/* Intentionally declared after test() to avoid compile-time checking of ref args. */
16+
17+
const C = ['foo'];
18+
19+
function byVal($arg) {
20+
var_dump($arg);
21+
}
22+
23+
function byRef(&$arg) {
24+
var_dump($arg);
25+
}
26+
27+
test('y');
28+
29+
?>
30+
--EXPECT--
31+
string(3) "foo"
32+
Cannot use temporary expression in write context

Zend/zend_compile.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2929,7 +2929,11 @@ static zend_op *zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t t
29292929

29302930
static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t type) /* {{{ */
29312931
{
2932-
if (type != BP_VAR_R && type != BP_VAR_IS && zend_is_call(ast)) {
2932+
if (type != BP_VAR_R
2933+
&& type != BP_VAR_IS
2934+
/* Whether a FUNC_ARG is R may only be determined at runtime. */
2935+
&& type != BP_VAR_FUNC_ARG
2936+
&& zend_is_call(ast)) {
29332937
if (node->op_type == IS_VAR) {
29342938
zend_op *opline = zend_emit_op(NULL, ZEND_SEPARATE, node, NULL);
29352939
opline->result_type = IS_VAR;

0 commit comments

Comments
 (0)