Skip to content

Commit 7784315

Browse files
committed
wip
1 parent 877abd4 commit 7784315

18 files changed

+884
-607
lines changed

Zend/tests/match/block_arg_return.phpt

+10-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22
Match expression block must not use return
33
--FILE--
44
<?php
5-
foo(match (1) {
6-
1 => { return; }
7-
});
5+
function test() {
6+
var_dump(match (1) {
7+
1 => { return; }
8+
});
9+
echo 'Unreached';
10+
}
11+
test();
812
?>
9-
--EXPECTF--
10-
Fatal error: Match expression whose result is used must not contain return, break, continue or goto in %s on line %d
13+
===DONE===
14+
--EXPECT--
15+
===DONE===

Zend/tests/match/block_expr_break_escape.phpt

+10-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22
Match expression block must not use break
33
--FILE--
44
<?php
5-
var_dump(match ($value) {
6-
1 => { break; },
7-
});
5+
function test() {
6+
str_repeat('a', 10) . match (1) {
7+
1 => { return; },
8+
};
9+
}
10+
11+
test();
812
?>
9-
--EXPECTF--
10-
Fatal error: Match expression whose result is used must not contain return, break, continue or goto in %s on line %d
13+
===DONE===
14+
--EXPECT--
15+
===DONE===

Zend/tests/match/block_expr_goto_escape.phpt

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ var_dump(match (1) {
99
});
1010
after:
1111
?>
12-
--EXPECTF--
13-
Fatal error: Match expression whose result is used must not contain return, break, continue or goto in %s on line %d
12+
===DONE===
13+
--EXPECT--
14+
===DONE===

Zend/tests/match/block_expr_return.phpt

+11-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22
Match expression block must not use return
33
--FILE--
44
<?php
5-
var_dump(match ($value) {
6-
1 => { return; },
7-
});
5+
6+
function test($a) {
7+
var_dump([$a] + match ($a) {
8+
42 => { return $a; },
9+
});
10+
}
11+
12+
var_dump(test(42));
13+
814
?>
9-
--EXPECTF--
10-
Fatal error: Match expression whose result is used must not contain return, break, continue or goto in %s on line %d
15+
--EXPECT--
16+
int(42)

Zend/tests/match/bugs/001.phpt

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
WIP
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
foreach ([1, 2, 3] as $value) {
8+
$x = match (1) {
9+
1 => { return 42; },
10+
};
11+
var_dump($value);
12+
}
13+
throw new Exception('Unreachable');
14+
}
15+
16+
var_dump(test());
17+
18+
?>
19+
--EXPECT--
20+
int(42)

Zend/tests/match/bugs/002.phpt

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
WIP
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
foreach ([1, 2, 3] as $value) {
8+
$x = match (1) {
9+
1 => { continue 2; },
10+
};
11+
var_dump($value);
12+
}
13+
return 42;
14+
}
15+
16+
var_dump(test());
17+
18+
?>
19+
--EXPECT--
20+
int(42)

Zend/tests/match/bugs/003.phpt

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
WIP
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
foreach ([1, 2, 3] as $value) {
8+
str_repeat('a', 10) . match (1) {
9+
1 => {
10+
try {
11+
return 42;
12+
} finally {}
13+
},
14+
};
15+
var_dump($value);
16+
}
17+
throw new Unreachable();
18+
}
19+
20+
var_dump(test());
21+
22+
?>
23+
--EXPECT--
24+
int(42)

Zend/tests/match/bugs/004.phpt

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
WIP
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
return str_repeat('a', 10) . match (1) {
8+
1 => {
9+
try {
10+
try {
11+
return 41;
12+
} finally {
13+
throw new Exception();
14+
}
15+
} catch (Exception) {}
16+
'b'
17+
},
18+
};
19+
}
20+
21+
var_dump(test());
22+
23+
?>
24+
--EXPECT--
25+
string(11) "aaaaaaaaaab"

Zend/tests/match/bugs/005.phpt

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
WIP
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
return str_repeat('a', 10) . match (1) {
8+
1 => {
9+
foreach (range(1, 10) as $value) {
10+
return 'foo';
11+
}
12+
'b'
13+
},
14+
};
15+
}
16+
17+
var_dump(test());
18+
19+
?>
20+
--EXPECT--
21+
string(3) "foo"

Zend/tests/match/bugs/006.phpt

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
WIP
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
str_repeat('a', 10) . match (1) {
8+
1 => {
9+
str_repeat('a', 10) . match (1) {
10+
1 => {
11+
return 'foo';
12+
},
13+
};
14+
},
15+
};
16+
}
17+
18+
var_dump(test());
19+
20+
?>
21+
--EXPECT--
22+
string(3) "foo"

Zend/tests/match/bugs/007.phpt

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
WIP
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
return str_repeat('a', 10) . match (1) {
8+
1 => {
9+
try {
10+
try {
11+
return str_repeat('a', 10) . match (1) {
12+
1 => {
13+
return 'foo';
14+
},
15+
};
16+
} finally {
17+
throw new Exception();
18+
}
19+
} catch (Exception) {}
20+
'b'
21+
},
22+
};
23+
}
24+
25+
var_dump(test());
26+
27+
?>
28+
--EXPECT--
29+
string(11) "aaaaaaaaaab"

Zend/zend_compile.c

+48-34
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ typedef struct _zend_loop_var {
6464
uint8_t var_type;
6565
uint32_t var_num;
6666
uint32_t try_catch_offset;
67+
uint32_t opcode_start;
6768
} zend_loop_var;
6869

6970
static inline uint32_t zend_alloc_cache_slots(unsigned count) {
@@ -720,8 +721,10 @@ static inline void zend_begin_loop(
720721
brk_cont_element->parent = parent;
721722
brk_cont_element->kind = kind;
722723

724+
uint32_t start = get_next_op_number();
725+
info.opcode_start = start;
726+
723727
if (loop_var && (loop_var->op_type & (IS_VAR|IS_TMP_VAR))) {
724-
uint32_t start = get_next_op_number();
725728

726729
info.opcode = free_opcode;
727730
info.var_type = loop_var->op_type;
@@ -5594,17 +5597,10 @@ static bool zend_handle_loops_and_finally_ex(zend_long depth, znode *return_valu
55945597
return 1;
55955598
}
55965599

5597-
zend_brk_cont_element *brk_ctrl_element = CG(context).current_brk_cont != -1
5598-
? &CG(context).brk_cont_array[CG(context).current_brk_cont]
5599-
: NULL;
5600-
if (brk_ctrl_element && brk_ctrl_element->kind == ZEND_BRK_CONT_KIND_MATCH_EXPR) {
5601-
zend_error_noreturn(E_COMPILE_ERROR, "Match expression whose result is used must not contain return, break, continue or goto");
5602-
}
5600+
uint32_t free_range_opnum = (uint32_t)-1;
56035601

56045602
base = zend_stack_base(&CG(loop_var_stack));
56055603
for (; loop_var >= base; loop_var--) {
5606-
bool decrement_depth = false;
5607-
56085604
if (loop_var->opcode == ZEND_FAST_CALL) {
56095605
zend_op *opline = get_next_op();
56105606

@@ -5615,40 +5611,43 @@ static bool zend_handle_loops_and_finally_ex(zend_long depth, znode *return_valu
56155611
SET_NODE(opline->op2, return_value);
56165612
}
56175613
opline->op1.num = loop_var->try_catch_offset;
5614+
if (free_range_opnum != (uint32_t)-1) {
5615+
zend_op *opline = &CG(active_op_array)->opcodes[free_range_opnum];
5616+
opline->op1.num = loop_var->opcode_start;
5617+
free_range_opnum = (uint32_t)-1;
5618+
}
56185619
} else if (loop_var->opcode == ZEND_DISCARD_EXCEPTION) {
56195620
zend_op *opline = get_next_op();
56205621
opline->opcode = ZEND_DISCARD_EXCEPTION;
56215622
opline->op1_type = IS_TMP_VAR;
56225623
opline->op1.var = loop_var->var_num;
56235624
} else if (loop_var->opcode == ZEND_RETURN) {
56245625
/* Stack separator */
5626+
if (free_range_opnum != (uint32_t)-1) {
5627+
zend_op *opline = &CG(active_op_array)->opcodes[free_range_opnum];
5628+
opline->op1.num = 0;
5629+
}
56255630
break;
5626-
} else if (depth <= 1) {
5627-
return 1;
5628-
} else if (loop_var->opcode == ZEND_NOP) {
5629-
/* Loop doesn't have freeable variable */
5630-
decrement_depth = true;
56315631
} else {
5632-
zend_op *opline;
5633-
5634-
ZEND_ASSERT(loop_var->var_type & (IS_VAR|IS_TMP_VAR));
5635-
opline = get_next_op();
5636-
opline->opcode = loop_var->opcode;
5637-
opline->op1_type = loop_var->var_type;
5638-
opline->op1.var = loop_var->var_num;
5639-
opline->extended_value = ZEND_FREE_ON_RETURN;
5640-
decrement_depth = true;
5641-
}
5642-
if (decrement_depth) {
5643-
depth--;
5644-
if (brk_ctrl_element) {
5645-
if (brk_ctrl_element->parent != -1) {
5646-
brk_ctrl_element = &CG(context).brk_cont_array[brk_ctrl_element->parent];
5647-
if (brk_ctrl_element->kind == ZEND_BRK_CONT_KIND_MATCH_EXPR) {
5648-
zend_error_noreturn(E_COMPILE_ERROR, "Match expression whose result is used must not contain return, break, continue or goto");
5649-
}
5650-
} else {
5651-
brk_ctrl_element = NULL;
5632+
if (free_range_opnum != (uint32_t)-1) {
5633+
zend_op *opline = &CG(active_op_array)->opcodes[free_range_opnum];
5634+
opline->op1.num = loop_var->opcode_start + 1;
5635+
free_range_opnum = (uint32_t)-1;
5636+
}
5637+
/* ZEND_FREE_RANGE does not decrease depth. */
5638+
if (loop_var->opcode != ZEND_FREE_RANGE && --depth == 0) {
5639+
break;
5640+
}
5641+
if (loop_var->opcode != ZEND_NOP) {
5642+
ZEND_ASSERT(loop_var->var_type == IS_UNUSED || (loop_var->var_type & (IS_VAR|IS_TMP_VAR)));
5643+
zend_op *opline = get_next_op();
5644+
opline->opcode = loop_var->opcode;
5645+
opline->op1_type = loop_var->var_type;
5646+
opline->op1.var = loop_var->var_num;
5647+
opline->extended_value = ZEND_FREE_ON_RETURN;
5648+
if (loop_var->opcode == ZEND_FREE_RANGE) {
5649+
free_range_opnum = get_next_op_number() - 1;
5650+
opline->op2.var = loop_var->opcode_start;
56525651
}
56535652
}
56545653
}
@@ -6446,10 +6445,20 @@ static void zend_compile_match(znode *result, zend_ast *ast)
64466445
zend_ast_list *arms = zend_ast_get_list(ast->child[1]);
64476446
bool has_default_arm = 0;
64486447
uint32_t opnum_match = (uint32_t)-1;
6448+
uint32_t start_opnum = get_next_op_number();
64496449

64506450
znode expr_node;
64516451
zend_compile_expr(&expr_node, expr_ast);
64526452

6453+
if (result) {
6454+
zend_loop_var info = {0};
6455+
info.opcode = ZEND_FREE_RANGE;
6456+
info.var_type = IS_UNUSED;
6457+
info.var_num = (uint32_t)-1;
6458+
info.opcode_start = start_opnum;
6459+
zend_stack_push(&CG(loop_var_stack), &info);
6460+
}
6461+
64536462
zend_begin_loop(ZEND_FREE, &expr_node, result ? ZEND_BRK_CONT_KIND_MATCH_EXPR : ZEND_BRK_CONT_KIND_SWITCH_MATCH_STMT);
64546463

64556464
znode case_node;
@@ -6620,6 +6629,9 @@ static void zend_compile_match(znode *result, zend_ast *ast)
66206629

66216630
zend_end_loop(get_next_op_number(), &expr_node);
66226631

6632+
/* Pop ZEND_FREE_RANGE. */
6633+
zend_stack_del_top(&CG(loop_var_stack));
6634+
66236635
if (expr_node.op_type & (IS_VAR|IS_TMP_VAR)) {
66246636
zend_op *opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
66256637
opline->extended_value = ZEND_FREE_SWITCH;
@@ -6662,6 +6674,7 @@ static void zend_compile_try(zend_ast *ast) /* {{{ */
66626674
}
66636675

66646676
try_catch_offset = zend_add_try_element(get_next_op_number());
6677+
uint32_t try_opnum = get_next_op_number();
66656678

66666679
if (finally_ast) {
66676680
zend_loop_var fast_call;
@@ -6675,6 +6688,7 @@ static void zend_compile_try(zend_ast *ast) /* {{{ */
66756688
fast_call.var_type = IS_TMP_VAR;
66766689
fast_call.var_num = CG(context).fast_call_var;
66776690
fast_call.try_catch_offset = try_catch_offset;
6691+
fast_call.opcode_start = try_opnum;
66786692
zend_stack_push(&CG(loop_var_stack), &fast_call);
66796693
}
66806694

0 commit comments

Comments
 (0)