Skip to content

Commit 64b40f6

Browse files
committed
Make ASSIGN, ASSIGN_OP, INC and DEC opcodes to return IS_TMP_VAR instead of IS_VAR.
This helps to avoid unnecessary IS_REFERENCE checks. This changes some notices "Only variables should be passed by reference" to exception "Cannot pass parameter %d by reference". Also, for consistency, compile-time fatal error "Only variables can be passed by reference" was converted to exception "Cannot pass parameter %d by reference"
1 parent ad3f768 commit 64b40f6

16 files changed

+199
-86
lines changed

UPGRADING

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ PHP 8.0 UPGRADE NOTES
125125
warning.
126126
. Uncaught exceptions now go through "clean shutdown", which means that
127127
destructors will be called after an uncaught exception.
128+
. Compile time fatal error "Only variables can be passed by reference" has been
129+
delayed until run-time and converted to "Cannot pass parameter by reference"
130+
exception.
131+
. Some "Only variables should be passed by reference" notices have been converted
132+
to "Cannot pass parameter by reference" exception.
128133

129134
- COM:
130135
. Removed the ability to import case-insensitive constants from type

UPGRADING.INTERNALS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ PHP 8.0 INTERNALS UPGRADE NOTES
1111
h. zend_value_error()
1212
i. get_closure() object handler
1313
j. compare_objects() and compare() object handlers
14+
k. The 'I' length modifier
15+
l. Some VM instructions switched to IS_TMP_VAR result insted of IS_VAR
1416

1517
2. Build system changes
1618
a. Abstract
@@ -89,6 +91,13 @@ PHP 8.0 INTERNALS UPGRADE NOTES
8991
The 'v' format from the custom snprintf and spprintf implementations has
9092
been removed. Use the standard 's' format instead.
9193

94+
l. Some VM instructions switched to IS_TMP_VAR result insted of IS_VAR.
95+
Actually, all assignments (ZEND_ASSIGN, ZEND_ASSIGN_DIM, ZEND_ASSIGN_OBJ,
96+
ZEND_ASSIGN_STATIC_PROP), all compound assignments (ZEND_ASSIGN_OP,
97+
ZEND_ASSIGN_DIM_OP, ZEND_ASSIGN_OBJ_OP, ZEND_ASSIGN_STATIC_PROP_OP) and all
98+
pre increments/decrements (ZEND_PRE_INC, ZEND_PRE_DEC, ZEND_PRE_INC_OBJ
99+
ZEND_PRE_DEC_OBJ, ZEND_PRE_INC_STATIC_PROP ZEND_PRE_DEC_STATIC_PROP).
100+
92101
========================
93102
2. Build system changes
94103
========================

Zend/tests/bug72038.phpt

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,31 @@ Bug #72038 (Function calls with values to a by-ref parameter don't always throw
33
--FILE--
44
<?php
55

6-
test($foo = new stdClass);
7-
var_dump($foo);
8-
test($bar = 2);
9-
var_dump($bar);
10-
test($baz = &$bar);
11-
var_dump($baz);
6+
try {
7+
test($foo = new stdClass);
8+
var_dump($foo);
9+
} catch (Throwable $e) {
10+
echo "Exception: " . $e->getMessage() . "\n";
11+
}
12+
try {
13+
test($bar = 2);
14+
var_dump($bar);
15+
} catch (Throwable $e) {
16+
echo "Exception: " . $e->getMessage() . "\n";
17+
}
18+
try {
19+
test($baz = &$bar);
20+
var_dump($baz);
21+
} catch (Throwable $e) {
22+
echo "Exception: " . $e->getMessage() . "\n";
23+
}
1224

1325
function test(&$param) {
1426
$param = 1;
1527
}
1628

1729
?>
18-
--EXPECTF--
19-
Notice: Only variables should be passed by reference in %s on line %d
20-
object(stdClass)#1 (0) {
21-
}
22-
23-
Notice: Only variables should be passed by reference in %s on line %d
24-
int(2)
30+
--EXPECT--
31+
Exception: Cannot pass parameter 1 by reference
32+
Exception: Cannot pass parameter 1 by reference
2533
int(1)

Zend/tests/bug73663_2.phpt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@ change(list($val) = $array);
1212
var_dump($array);
1313
?>
1414
--EXPECTF--
15-
Fatal error: Only variables can be passed by reference in %s on line %d
15+
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
16+
Stack trace:
17+
#0 {main}
18+
thrown in %s on line %d

Zend/tests/bug78154.phpt

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@ Bug #78154: SEND_VAR_NO_REF does not always send reference
44
<?php
55

66
namespace {
7-
var_dump(similar_text('a', 'a', $c=0x44444444));
8-
var_dump($c);
7+
try {
8+
var_dump(similar_text('a', 'a', $c=0x44444444));
9+
var_dump($c);
10+
} catch (Throwable $e) {
11+
echo "Exception: " . $e->getMessage() . "\n";
12+
}
913
}
1014
namespace Foo {
11-
var_dump(similar_text('a', 'a', $d=0x44444444));
12-
var_dump($d);
15+
try {
16+
var_dump(similar_text('a', 'a', $d=0x44444444));
17+
var_dump($d);
18+
} catch (\Throwable $e) {
19+
echo "Exception: " . $e->getMessage() . "\n";
20+
}
1321
}
1422

1523
?>
16-
--EXPECTF--
17-
Notice: Only variables should be passed by reference in %s on line %d
18-
int(1)
19-
int(1145324612)
20-
21-
Notice: Only variables should be passed by reference in %s on line %d
22-
int(1)
23-
int(1145324612)
24+
--EXPECT--
25+
Exception: Cannot pass parameter 3 by reference
26+
Exception: Cannot pass parameter 3 by reference

Zend/tests/errmsg_022.phpt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@ foo(1);
1111
echo "Done\n";
1212
?>
1313
--EXPECTF--
14-
Fatal error: Only variables can be passed by reference in %s on line %d
14+
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
15+
Stack trace:
16+
#0 {main}
17+
thrown in %s on line %d

Zend/tests/variadic/by_ref_error.phpt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ test(1);
99

1010
?>
1111
--EXPECTF--
12-
Fatal error: Only variables can be passed by reference in %s on line %d
12+
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
13+
Stack trace:
14+
#0 {main}
15+
thrown in %s on line %d

Zend/zend_compile.c

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,8 @@ void zend_do_free(znode *op1) /* {{{ */
718718
if (op1->op_type == IS_TMP_VAR) {
719719
zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
720720

721-
while (opline->opcode == ZEND_END_SILENCE) {
721+
while (opline->opcode == ZEND_END_SILENCE ||
722+
opline->opcode == ZEND_OP_DATA) {
722723
opline--;
723724
}
724725

@@ -738,6 +739,22 @@ void zend_do_free(znode *op1) /* {{{ */
738739
opline->opcode -= 2;
739740
opline->result_type = IS_UNUSED;
740741
return;
742+
case ZEND_ASSIGN:
743+
case ZEND_ASSIGN_DIM:
744+
case ZEND_ASSIGN_OBJ:
745+
case ZEND_ASSIGN_STATIC_PROP:
746+
case ZEND_ASSIGN_OP:
747+
case ZEND_ASSIGN_DIM_OP:
748+
case ZEND_ASSIGN_OBJ_OP:
749+
case ZEND_ASSIGN_STATIC_PROP_OP:
750+
case ZEND_PRE_INC_STATIC_PROP:
751+
case ZEND_PRE_DEC_STATIC_PROP:
752+
case ZEND_PRE_INC_OBJ:
753+
case ZEND_PRE_DEC_OBJ:
754+
case ZEND_PRE_INC:
755+
case ZEND_PRE_DEC:
756+
opline->result_type = IS_UNUSED;
757+
return;
741758
}
742759
}
743760

@@ -2921,7 +2938,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
29212938
zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W, 0);
29222939
zend_compile_expr(&expr_node, expr_ast);
29232940
zend_delayed_compile_end(offset);
2924-
zend_emit_op(result, ZEND_ASSIGN, &var_node, &expr_node);
2941+
zend_emit_op_tmp(result, ZEND_ASSIGN, &var_node, &expr_node);
29252942
return;
29262943
case ZEND_AST_STATIC_PROP:
29272944
offset = zend_delayed_compile_begin();
@@ -2930,6 +2947,8 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
29302947

29312948
opline = zend_delayed_compile_end(offset);
29322949
opline->opcode = ZEND_ASSIGN_STATIC_PROP;
2950+
opline->result_type = IS_TMP_VAR;
2951+
result->op_type = IS_TMP_VAR;
29332952

29342953
zend_emit_op_data(&expr_node);
29352954
return;
@@ -2953,6 +2972,8 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
29532972

29542973
opline = zend_delayed_compile_end(offset);
29552974
opline->opcode = ZEND_ASSIGN_DIM;
2975+
opline->result_type = IS_TMP_VAR;
2976+
result->op_type = IS_TMP_VAR;
29562977

29572978
opline = zend_emit_op_data(&expr_node);
29582979
return;
@@ -2963,6 +2984,8 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
29632984

29642985
opline = zend_delayed_compile_end(offset);
29652986
opline->opcode = ZEND_ASSIGN_OBJ;
2987+
opline->result_type = IS_TMP_VAR;
2988+
result->op_type = IS_TMP_VAR;
29662989

29672990
zend_emit_op_data(&expr_node);
29682991
return;
@@ -3086,7 +3109,7 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
30863109
zend_delayed_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
30873110
zend_compile_expr(&expr_node, expr_ast);
30883111
zend_delayed_compile_end(offset);
3089-
opline = zend_emit_op(result, ZEND_ASSIGN_OP, &var_node, &expr_node);
3112+
opline = zend_emit_op_tmp(result, ZEND_ASSIGN_OP, &var_node, &expr_node);
30903113
opline->extended_value = opcode;
30913114
return;
30923115
case ZEND_AST_STATIC_PROP:
@@ -3098,6 +3121,8 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
30983121
cache_slot = opline->extended_value;
30993122
opline->opcode = ZEND_ASSIGN_STATIC_PROP_OP;
31003123
opline->extended_value = opcode;
3124+
opline->result_type = IS_TMP_VAR;
3125+
result->op_type = IS_TMP_VAR;
31013126

31023127
opline = zend_emit_op_data(&expr_node);
31033128
opline->extended_value = cache_slot;
@@ -3110,6 +3135,8 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
31103135
opline = zend_delayed_compile_end(offset);
31113136
opline->opcode = ZEND_ASSIGN_DIM_OP;
31123137
opline->extended_value = opcode;
3138+
opline->result_type = IS_TMP_VAR;
3139+
result->op_type = IS_TMP_VAR;
31133140

31143141
zend_emit_op_data(&expr_node);
31153142
return;
@@ -3122,6 +3149,8 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
31223149
cache_slot = opline->extended_value;
31233150
opline->opcode = ZEND_ASSIGN_OBJ_OP;
31243151
opline->extended_value = opcode;
3152+
opline->result_type = IS_TMP_VAR;
3153+
result->op_type = IS_TMP_VAR;
31253154

31263155
opline = zend_emit_op_data(&expr_node);
31273156
opline->extended_value = cache_slot;
@@ -3236,11 +3265,9 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
32363265
opcode = ZEND_SEND_VAR_EX;
32373266
}
32383267
} else {
3239-
if (fbc) {
3268+
/* Delay "Only variables can be passed by reference" error to execution */
3269+
if (fbc && !ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
32403270
opcode = ZEND_SEND_VAL;
3241-
if (ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
3242-
zend_error_noreturn(E_COMPILE_ERROR, "Only variables can be passed by reference");
3243-
}
32443271
} else {
32453272
opcode = ZEND_SEND_VAL_EX;
32463273
}
@@ -7577,13 +7604,17 @@ void zend_compile_pre_incdec(znode *result, zend_ast *ast) /* {{{ */
75777604
if (var_ast->kind == ZEND_AST_PROP) {
75787605
zend_op *opline = zend_compile_prop(result, var_ast, BP_VAR_RW, 0);
75797606
opline->opcode = ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC_OBJ : ZEND_PRE_DEC_OBJ;
7607+
opline->result_type = IS_TMP_VAR;
7608+
result->op_type = IS_TMP_VAR;
75807609
} else if (var_ast->kind == ZEND_AST_STATIC_PROP) {
75817610
zend_op *opline = zend_compile_static_prop(result, var_ast, BP_VAR_RW, 0, 0);
75827611
opline->opcode = ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC_STATIC_PROP : ZEND_PRE_DEC_STATIC_PROP;
7612+
opline->result_type = IS_TMP_VAR;
7613+
result->op_type = IS_TMP_VAR;
75837614
} else {
75847615
znode var_node;
75857616
zend_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
7586-
zend_emit_op(result, ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC : ZEND_PRE_DEC,
7617+
zend_emit_op_tmp(result, ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC : ZEND_PRE_DEC,
75877618
&var_node, NULL);
75887619
}
75897620
}
@@ -7764,20 +7795,26 @@ void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
77647795
opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
77657796
switch (var_ast->kind) {
77667797
case ZEND_AST_VAR:
7767-
zend_emit_op(&assign_node, ZEND_ASSIGN, &var_node_w, &default_node);
7798+
zend_emit_op_tmp(&assign_node, ZEND_ASSIGN, &var_node_w, &default_node);
77687799
break;
77697800
case ZEND_AST_STATIC_PROP:
77707801
opline->opcode = ZEND_ASSIGN_STATIC_PROP;
7802+
opline->result_type = IS_TMP_VAR;
7803+
var_node_w.op_type = IS_TMP_VAR;
77717804
zend_emit_op_data(&default_node);
77727805
assign_node = var_node_w;
77737806
break;
77747807
case ZEND_AST_DIM:
77757808
opline->opcode = ZEND_ASSIGN_DIM;
7809+
opline->result_type = IS_TMP_VAR;
7810+
var_node_w.op_type = IS_TMP_VAR;
77767811
zend_emit_op_data(&default_node);
77777812
assign_node = var_node_w;
77787813
break;
77797814
case ZEND_AST_PROP:
77807815
opline->opcode = ZEND_ASSIGN_OBJ;
7816+
opline->result_type = IS_TMP_VAR;
7817+
var_node_w.op_type = IS_TMP_VAR;
77817818
zend_emit_op_data(&default_node);
77827819
assign_node = var_node_w;
77837820
break;

ext/opcache/Optimizer/block_pass.c

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -250,13 +250,38 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
250250
case ZEND_FREE:
251251
if (opline->op1_type == IS_TMP_VAR) {
252252
src = VAR_SOURCE(opline->op1);
253-
if (src &&
254-
(src->opcode == ZEND_BOOL || src->opcode == ZEND_BOOL_NOT)) {
255-
/* T = BOOL(X), FREE(T) => T = BOOL(X) */
256-
/* The remaining BOOL is removed by a separate optimization */
257-
VAR_SOURCE(opline->op1) = NULL;
258-
MAKE_NOP(opline);
259-
++(*opt_count);
253+
if (src) {
254+
switch (src->opcode) {
255+
case ZEND_BOOL:
256+
case ZEND_BOOL_NOT:
257+
/* T = BOOL(X), FREE(T) => T = BOOL(X) */
258+
/* The remaining BOOL is removed by a separate optimization */
259+
VAR_SOURCE(opline->op1) = NULL;
260+
MAKE_NOP(opline);
261+
++(*opt_count);
262+
break;
263+
case ZEND_ASSIGN:
264+
case ZEND_ASSIGN_DIM:
265+
case ZEND_ASSIGN_OBJ:
266+
case ZEND_ASSIGN_STATIC_PROP:
267+
case ZEND_ASSIGN_OP:
268+
case ZEND_ASSIGN_DIM_OP:
269+
case ZEND_ASSIGN_OBJ_OP:
270+
case ZEND_ASSIGN_STATIC_PROP_OP:
271+
case ZEND_PRE_INC:
272+
case ZEND_PRE_DEC:
273+
case ZEND_PRE_INC_OBJ:
274+
case ZEND_PRE_DEC_OBJ:
275+
case ZEND_PRE_INC_STATIC_PROP:
276+
case ZEND_PRE_DEC_STATIC_PROP:
277+
src->result_type = IS_UNUSED;
278+
VAR_SOURCE(opline->op1) = NULL;
279+
MAKE_NOP(opline);
280+
++(*opt_count);
281+
break;
282+
default:
283+
break;
284+
}
260285
}
261286
} else if (opline->op1_type == IS_VAR) {
262287
src = VAR_SOURCE(opline->op1);
@@ -1649,7 +1674,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
16491674

16501675
while (opline >= end) {
16511676
/* usage checks */
1652-
if (opline->result_type == IS_VAR) {
1677+
if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
16531678
if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
16541679
switch (opline->opcode) {
16551680
case ZEND_ASSIGN_OP:
@@ -1666,13 +1691,6 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
16661691
case ZEND_DO_FCALL_BY_NAME:
16671692
opline->result_type = IS_UNUSED;
16681693
break;
1669-
}
1670-
} else {
1671-
zend_bitset_excl(usage, VAR_NUM(opline->result.var));
1672-
}
1673-
} else if (opline->result_type == IS_TMP_VAR) {
1674-
if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
1675-
switch (opline->opcode) {
16761694
case ZEND_POST_INC:
16771695
case ZEND_POST_DEC:
16781696
case ZEND_POST_INC_OBJ:

ext/opcache/Optimizer/zend_ssa.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ static void place_essa_pis(
427427
pi_range_not_equals(pi, -1, 1);
428428
}
429429
}
430-
} else if (opline->op1_type == IS_VAR &&
430+
} else if (opline->op1_type == IS_TMP_VAR &&
431431
((opline-1)->opcode == ZEND_PRE_INC ||
432432
(opline-1)->opcode == ZEND_PRE_DEC) &&
433433
opline->op1.var == (opline-1)->result.var &&

ext/pcre/tests/preg_match_all_error3.phpt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,9 @@ var_dump(preg_match_all($regex, $subject, 'test'));
1616
echo "Done";
1717
?>
1818
--EXPECTF--
19-
Fatal error: Only variables can be passed by reference in %spreg_match_all_error3.php on line %d
19+
*** Testing preg_match_all() : error conditions ***
20+
21+
Fatal error: Uncaught Error: Cannot pass parameter 3 by reference in %s:%d
22+
Stack trace:
23+
#0 {main}
24+
thrown in %s on line %d

0 commit comments

Comments
 (0)