Skip to content

Commit c79b933

Browse files
committed
Add destructuring coalesce
1 parent 69eb38b commit c79b933

19 files changed

+1822
-1545
lines changed

Zend/Optimizer/block_pass.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
169169
if (opline->opcode != ZEND_CASE
170170
&& opline->opcode != ZEND_CASE_STRICT
171171
&& opline->opcode != ZEND_FETCH_LIST_R
172+
&& opline->opcode != ZEND_FETCH_LIST_IS
172173
&& opline->opcode != ZEND_SWITCH_LONG
173174
&& opline->opcode != ZEND_SWITCH_STRING
174175
&& opline->opcode != ZEND_MATCH
@@ -371,6 +372,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
371372

372373
case ZEND_FETCH_LIST_R:
373374
case ZEND_FETCH_LIST_W:
375+
case ZEND_FETCH_LIST_IS:
374376
if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
375377
/* LIST variable will be deleted later by FREE */
376378
Tsource[VAR_NUM(opline->op1.var)] = NULL;

Zend/Optimizer/compact_literals.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
264264
case ZEND_FETCH_DIM_UNSET:
265265
case ZEND_FETCH_LIST_R:
266266
case ZEND_FETCH_LIST_W:
267+
case ZEND_FETCH_LIST_IS:
267268
case ZEND_ASSIGN_DIM_OP:
268269
if (opline->op1_type == IS_CONST) {
269270
LITERAL_INFO(opline->op1.constant, 1);

Zend/Optimizer/sccp.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1508,10 +1508,11 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
15081508
case ZEND_FETCH_DIM_R:
15091509
case ZEND_FETCH_DIM_IS:
15101510
case ZEND_FETCH_LIST_R:
1511+
case ZEND_FETCH_LIST_IS:
15111512
SKIP_IF_TOP(op1);
15121513
SKIP_IF_TOP(op2);
15131514

1514-
if (ct_eval_fetch_dim(&zv, op1, op2, (opline->opcode != ZEND_FETCH_LIST_R)) == SUCCESS) {
1515+
if (ct_eval_fetch_dim(&zv, op1, op2, (opline->opcode != ZEND_FETCH_LIST_R && opline->opcode != ZEND_FETCH_LIST_IS)) == SUCCESS) {
15151516
SET_RESULT(result, &zv);
15161517
zval_ptr_dtor_nogc(&zv);
15171518
break;

Zend/Optimizer/zend_inference.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3245,6 +3245,7 @@ static zend_always_inline zend_result _zend_update_type_info(
32453245
case ZEND_FETCH_DIM_FUNC_ARG:
32463246
case ZEND_FETCH_LIST_R:
32473247
case ZEND_FETCH_LIST_W:
3248+
case ZEND_FETCH_LIST_IS:
32483249
if (ssa_op->op1_def >= 0) {
32493250
uint32_t key_type = 0;
32503251
tmp = t1 & ~(MAY_BE_RC1|MAY_BE_RCN);
@@ -3397,7 +3398,8 @@ static zend_always_inline zend_result _zend_update_type_info(
33973398
|| opline->opcode == ZEND_FETCH_DIM_R
33983399
|| opline->opcode == ZEND_FETCH_DIM_IS
33993400
|| opline->opcode == ZEND_FETCH_DIM_UNSET
3400-
|| opline->opcode == ZEND_FETCH_LIST_R) {
3401+
|| opline->opcode == ZEND_FETCH_LIST_R
3402+
|| opline->opcode == ZEND_FETCH_LIST_IS) {
34013403
UPDATE_SSA_TYPE(tmp, ssa_op->op1_def);
34023404
} else {
34033405
/* invalid key type */
@@ -3409,10 +3411,10 @@ static zend_always_inline zend_result _zend_update_type_info(
34093411
}
34103412
/* FETCH_LIST on a string behaves like FETCH_R on null */
34113413
tmp = zend_array_element_type(
3412-
opline->opcode != ZEND_FETCH_LIST_R ? t1 : ((t1 & ~MAY_BE_STRING) | MAY_BE_NULL),
3414+
opline->opcode != ZEND_FETCH_LIST_R && opline->opcode != ZEND_FETCH_LIST_IS ? t1 : ((t1 & ~MAY_BE_STRING) | MAY_BE_NULL),
34133415
opline->op1_type,
34143416
opline->opcode != ZEND_FETCH_DIM_R && opline->opcode != ZEND_FETCH_DIM_IS
3415-
&& opline->opcode != ZEND_FETCH_LIST_R,
3417+
&& opline->opcode != ZEND_FETCH_LIST_R && opline->opcode != ZEND_FETCH_LIST_IS,
34163418
opline->op2_type == IS_UNUSED);
34173419
if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG && (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE))) {
34183420
tmp |= MAY_BE_NULL;
@@ -4524,6 +4526,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
45244526
case ZEND_FE_FETCH_R:
45254527
case ZEND_FE_FETCH_RW:
45264528
case ZEND_FETCH_LIST_R:
4529+
case ZEND_FETCH_LIST_IS:
45274530
case ZEND_QM_ASSIGN:
45284531
case ZEND_SEND_VAL:
45294532
case ZEND_SEND_VAL_EX:

Zend/Optimizer/zend_optimizer.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ bool zend_optimizer_update_op2_const(zend_op_array *op_array,
571571
case ZEND_FETCH_DIM_UNSET:
572572
case ZEND_FETCH_LIST_R:
573573
case ZEND_FETCH_LIST_W:
574+
case ZEND_FETCH_LIST_IS:
574575
if (Z_TYPE_P(val) == IS_STRING) {
575576
zend_ulong index;
576577

@@ -636,6 +637,7 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array,
636637
* we want to try to replace all uses of the temporary.
637638
*/
638639
case ZEND_FETCH_LIST_R:
640+
case ZEND_FETCH_LIST_IS:
639641
case ZEND_CASE:
640642
case ZEND_CASE_STRICT:
641643
case ZEND_SWITCH_LONG:
@@ -648,6 +650,7 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array,
648650
/* If this opcode doesn't keep the operand alive, we're done. Check
649651
* this early, because op replacement may modify the opline. */
650652
bool is_last = opline->opcode != ZEND_FETCH_LIST_R
653+
&& opline->opcode != ZEND_FETCH_LIST_IS
651654
&& opline->opcode != ZEND_CASE
652655
&& opline->opcode != ZEND_CASE_STRICT
653656
&& opline->opcode != ZEND_SWITCH_LONG
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
--TEST--
2+
Destructuring operations with coalesce
3+
--FILE--
4+
<?php
5+
6+
[$a ?? 1] = [];
7+
var_dump($a);
8+
9+
["strkey" => $a ?? 2] = [];
10+
var_dump($a);
11+
12+
[, $a ?? 0] = [0, 3];
13+
var_dump($a);
14+
15+
["strkey" => $a ?? 0] = ["strkey" => 4];
16+
var_dump($a);
17+
18+
[$a ?? 5] = "";
19+
var_dump($a);
20+
21+
[$a ?? 6] = [null];
22+
var_dump($a);
23+
24+
[[$a ?? 7]] = [[]];
25+
var_dump($a);
26+
27+
[[$a ?? 8]] = [];
28+
var_dump($a);
29+
30+
[$a ?? 9] = 0;
31+
var_dump($a);
32+
33+
[$a ?? 10] = $undefinedVar;
34+
var_dump($a);
35+
36+
[[$a] ?? [11]] = [];
37+
var_dump($a);
38+
39+
?>
40+
--EXPECTF--
41+
int(1)
42+
int(2)
43+
int(3)
44+
int(4)
45+
int(5)
46+
int(6)
47+
int(7)
48+
49+
Warning: Undefined array key 0 in %s on line %d
50+
int(8)
51+
int(9)
52+
53+
Warning: Undefined variable $undefinedVar in %s on line %d
54+
int(10)
55+
int(11)

Zend/zend_compile.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3062,7 +3062,7 @@ static void zend_compile_list_assign(
30623062

30633063
for (i = 0; i < list->children; ++i) {
30643064
zend_ast *elem_ast = list->child[i];
3065-
zend_ast *var_ast, *key_ast;
3065+
zend_ast *var_ast, *key_ast, *coaleasce_default_ast = NULL;
30663066
znode fetch_result, dim_node;
30673067
zend_op *opline;
30683068

@@ -3105,16 +3105,38 @@ static void zend_compile_list_assign(
31053105
Z_TRY_ADDREF(expr_node->u.constant);
31063106
}
31073107

3108+
zend_uchar opcode = ZEND_FETCH_LIST_R;
3109+
if (var_ast->kind == ZEND_AST_COALESCE) {
3110+
coaleasce_default_ast = var_ast->child[1];
3111+
var_ast = var_ast->child[0];
3112+
3113+
opcode = ZEND_FETCH_LIST_IS;
3114+
} else if (elem_ast->attr) {
3115+
opcode = expr_node->op_type == IS_CV ? ZEND_FETCH_DIM_W : ZEND_FETCH_LIST_W;
3116+
}
3117+
31083118
zend_verify_list_assign_target(var_ast, array_style);
31093119

3110-
opline = zend_emit_op(&fetch_result,
3111-
elem_ast->attr ? (expr_node->op_type == IS_CV ? ZEND_FETCH_DIM_W : ZEND_FETCH_LIST_W) : ZEND_FETCH_LIST_R, expr_node, &dim_node);
3120+
opline = zend_emit_op(&fetch_result, opcode, expr_node, &dim_node);
31123121

31133122
if (dim_node.op_type == IS_CONST) {
31143123
zend_handle_numeric_dim(opline, &dim_node);
31153124
}
31163125

3117-
if (elem_ast->attr) {
3126+
if (coaleasce_default_ast) {
3127+
znode default_node;
3128+
3129+
uint32_t opnum = get_next_op_number();
3130+
zend_emit_op_tmp(&fetch_result, ZEND_COALESCE, &fetch_result, NULL);
3131+
3132+
zend_compile_expr(&default_node, coaleasce_default_ast);
3133+
3134+
opline = zend_emit_op_tmp(NULL, ZEND_QM_ASSIGN, &default_node, NULL);
3135+
SET_NODE(opline->result, &fetch_result);
3136+
3137+
opline = &CG(active_op_array)->opcodes[opnum];
3138+
opline->op2.opline_num = get_next_op_number();
3139+
} else if (elem_ast->attr) {
31183140
zend_emit_op(&fetch_result, ZEND_MAKE_REF, &fetch_result, NULL);
31193141
}
31203142
if (var_ast->kind == ZEND_AST_ARRAY) {

Zend/zend_execute.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2808,6 +2808,12 @@ static zend_never_inline void ZEND_FASTCALL zend_fetch_dimension_address_LIST_r(
28082808
zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_R, 1, 0 EXECUTE_DATA_CC);
28092809
}
28102810

2811+
static zend_never_inline void ZEND_FASTCALL zend_fetch_dimension_address_LIST_is(zval *container, zval *dim, int dim_type OPLINE_DC EXECUTE_DATA_DC)
2812+
{
2813+
zval *result = EX_VAR(opline->result.var);
2814+
zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1, 0 EXECUTE_DATA_CC);
2815+
}
2816+
28112817
ZEND_API void zend_fetch_dimension_const(zval *result, zval *container, zval *dim, int type)
28122818
{
28132819
zend_fetch_dimension_address_read(result, container, dim, IS_TMP_VAR, type, 0, 0 NO_EXECUTE_DATA_CC);

Zend/zend_opcode.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,7 @@ static bool keeps_op1_alive(zend_op *opline) {
869869
|| opline->opcode == ZEND_SWITCH_STRING
870870
|| opline->opcode == ZEND_MATCH
871871
|| opline->opcode == ZEND_FETCH_LIST_R
872+
|| opline->opcode == ZEND_FETCH_LIST_IS
872873
|| opline->opcode == ZEND_COPY_TMP) {
873874
return 1;
874875
}

Zend/zend_vm_def.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2348,6 +2348,18 @@ ZEND_VM_HANDLER(98, ZEND_FETCH_LIST_R, CONST|TMPVARCV, CONST|TMPVAR|CV)
23482348
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
23492349
}
23502350

2351+
ZEND_VM_HANDLER(45, ZEND_FETCH_LIST_IS, CONST|TMPVARCV, CONST|TMPVAR|CV)
2352+
{
2353+
USE_OPLINE
2354+
zval *container;
2355+
2356+
SAVE_OPLINE();
2357+
container = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_IS);
2358+
zend_fetch_dimension_address_LIST_is(container, GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R), OP2_TYPE OPLINE_CC EXECUTE_DATA_CC);
2359+
FREE_OP2();
2360+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
2361+
}
2362+
23512363
ZEND_VM_HANDLER(155, ZEND_FETCH_LIST_W, VAR, CONST|TMPVAR|CV)
23522364
{
23532365
USE_OPLINE

0 commit comments

Comments
 (0)