Skip to content

Commit 7d9ddd2

Browse files
committed
Fix FREE_RANGE for standalone block exprs
1 parent 1de997b commit 7d9ddd2

File tree

3 files changed

+67
-20
lines changed

3 files changed

+67
-20
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Control flow in block
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
foreach ([1, 2, 3] as $v) {
8+
var_dump($v);
9+
str_repeat('a', 10) . (null ?? {
10+
continue;
11+
});
12+
}
13+
}
14+
15+
test();
16+
17+
?>
18+
--EXPECT--
19+
int(1)
20+
int(2)
21+
int(3)

Zend/zend_compile.c

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6438,6 +6438,7 @@ static bool can_match_use_jumptable(zend_ast_list *arms) {
64386438
}
64396439

64406440
static void zend_compile_stmt_list(zend_ast *ast);
6441+
static void zend_compile_block_expr(znode *result, zend_ast *ast, bool omit_free_range);
64416442

64426443
static void zend_compile_match(znode *result, zend_ast *ast)
64436444
{
@@ -6601,8 +6602,15 @@ static void zend_compile_match(znode *result, zend_ast *ast)
66016602
}
66026603

66036604
znode body_node;
6604-
zend_compile_expr(&body_node, body_ast);
66056605
if (result) {
6606+
/* Avoid ZEND_FREE_RANGE for block in match expression. Blocks in
6607+
* match arms do not have preceding instructions. The instructions
6608+
* preceding the match are handled by match itself. */
6609+
if (body_ast->kind == ZEND_AST_BLOCK_EXPR) {
6610+
zend_compile_block_expr(&body_node, body_ast, /* omit_free_range */ true);
6611+
} else {
6612+
zend_compile_expr(&body_node, body_ast);
6613+
}
66066614
if (is_first_case) {
66076615
zend_emit_op_tmp(result, ZEND_QM_ASSIGN, &body_node, NULL);
66086616
is_first_case = 0;
@@ -6611,7 +6619,7 @@ static void zend_compile_match(znode *result, zend_ast *ast)
66116619
SET_NODE(opline_qm_assign->result, result);
66126620
}
66136621
} else {
6614-
zend_do_free(&body_node);
6622+
zend_compile_stmt(body_ast);
66156623
}
66166624

66176625
jmp_end_opnums[i] = zend_emit_jump(0);
@@ -11405,6 +11413,36 @@ void zend_compile_top_stmt(zend_ast *ast) /* {{{ */
1140511413
}
1140611414
/* }}} */
1140711415

11416+
static void zend_compile_block_expr(znode *result, zend_ast *ast, bool omit_free_range)
11417+
{
11418+
bool prev_in_block_expr = CG(context).in_block_expr;
11419+
CG(context).in_block_expr = true;
11420+
if (result && !omit_free_range) {
11421+
zend_loop_var info = {0};
11422+
info.opcode = ZEND_FREE_RANGE;
11423+
info.var_type = IS_UNUSED;
11424+
info.var_num = (uint32_t)-1;
11425+
info.opcode_start = get_next_op_number();
11426+
zend_stack_push(&CG(loop_var_stack), &info);
11427+
}
11428+
zend_compile_stmt_list(ast->child[0]);
11429+
zend_ast *result_expr_ast = ast->child[1];
11430+
if (result_expr_ast) {
11431+
if (result) {
11432+
zend_compile_expr(result, result_expr_ast);
11433+
} else {
11434+
zend_compile_stmt(result_expr_ast);
11435+
}
11436+
} else if (result) {
11437+
result->op_type = IS_CONST;
11438+
ZVAL_NULL(&result->u.constant);
11439+
}
11440+
if (result && !omit_free_range) {
11441+
zend_stack_del_top(&CG(loop_var_stack));
11442+
}
11443+
CG(context).in_block_expr = prev_in_block_expr;
11444+
}
11445+
1140811446
static void zend_compile_stmt(zend_ast *ast) /* {{{ */
1140911447
{
1141011448
if (!ast) {
@@ -11507,10 +11545,12 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
1150711545
case ZEND_AST_THROW:
1150811546
zend_compile_expr(NULL, ast);
1150911547
break;
11510-
case ZEND_AST_MATCH: {
11548+
case ZEND_AST_MATCH:
1151111549
zend_compile_match(NULL, ast);
1151211550
break;
11513-
}
11551+
case ZEND_AST_BLOCK_EXPR:
11552+
zend_compile_block_expr(NULL, ast, /* omit_free_range */ false);
11553+
return;
1151411554
default:
1151511555
{
1151611556
znode result;
@@ -11525,21 +11565,6 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
1152511565
}
1152611566
/* }}} */
1152711567

11528-
static void zend_compile_block_expr(znode *result, zend_ast *ast)
11529-
{
11530-
bool prev_in_block_expr = CG(context).in_block_expr;
11531-
CG(context).in_block_expr = true;
11532-
zend_compile_stmt_list(ast->child[0]);
11533-
zend_ast *result_expr_ast = ast->child[1];
11534-
if (result_expr_ast) {
11535-
zend_compile_expr(result, result_expr_ast);
11536-
} else {
11537-
result->op_type = IS_CONST;
11538-
ZVAL_NULL(&result->u.constant);
11539-
}
11540-
CG(context).in_block_expr = prev_in_block_expr;
11541-
}
11542-
1154311568
static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
1154411569
{
1154511570
/* CG(zend_lineno) = ast->lineno; */
@@ -11677,7 +11702,7 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
1167711702
zend_compile_match(result, ast);
1167811703
return;
1167911704
case ZEND_AST_BLOCK_EXPR:
11680-
zend_compile_block_expr(result, ast);
11705+
zend_compile_block_expr(result, ast, /* omit_free_range */ false);
1168111706
return;
1168211707
default:
1168311708
ZEND_ASSERT(0 /* not supported */);

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ struct _zend_op {
155155
#endif
156156
};
157157

158+
158159
typedef struct _zend_brk_cont_element {
159160
int start;
160161
int cont;

0 commit comments

Comments
 (0)