Skip to content

Commit 9ac8820

Browse files
authored
zend_compile: Allow (void) in for’s initializer and loop expression (php#18303)
* zend_compile: Allow `(void)` in for’s initializer and loop expression The initializer and loop expression of a `for()` loop only accept expressions, but they act like statements in spirit - their results are completely ignored. Allow `(void)` there to allow suppressing `#[\NoDiscard]`, since there is no semantic ambiguity of what `(void)` returns anyways. Fixes php#18301 * zend_language_parser: Simplify `for()` grammar
1 parent 89e5f6c commit 9ac8820

File tree

4 files changed

+96
-27
lines changed

4 files changed

+96
-27
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
GH-18301: casting to void is allowed in for’s expression lists
3+
--FILE--
4+
<?php
5+
6+
$count = 0;
7+
8+
#[NoDiscard]
9+
function incCount() {
10+
global $count;
11+
$count++;
12+
return $count;
13+
}
14+
15+
for ( $count = 0, (void)incCount(), incCount(); (void)incCount(), incCount() < 30; incCount(), $count++, incCount(), (void)incCount()) {
16+
echo $count . "\n";
17+
}
18+
19+
?>
20+
--EXPECTF--
21+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
22+
4
23+
24+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
25+
26+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
27+
10
28+
29+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
30+
31+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
32+
16
33+
34+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
35+
36+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
37+
22
38+
39+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
40+
41+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
42+
28
43+
44+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
45+
46+
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
GH-18301: casting to void is not allowed at the end of a for condition
3+
--FILE--
4+
<?php
5+
6+
for (;(void)true;);
7+
?>
8+
--EXPECTF--
9+
Parse error: syntax error, unexpected token ";", expecting "," in %s on line %d

Zend/zend_compile.c

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5717,6 +5717,26 @@ static void zend_compile_return(zend_ast *ast) /* {{{ */
57175717
}
57185718
/* }}} */
57195719

5720+
static void zend_compile_void_cast(znode *result, zend_ast *ast)
5721+
{
5722+
zend_ast *expr_ast = ast->child[0];
5723+
znode expr_node;
5724+
zend_op *opline;
5725+
5726+
zend_compile_expr(&expr_node, expr_ast);
5727+
5728+
switch (expr_node.op_type) {
5729+
case IS_TMP_VAR:
5730+
case IS_VAR:
5731+
opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
5732+
opline->extended_value = ZEND_FREE_VOID_CAST;
5733+
break;
5734+
case IS_CONST:
5735+
zend_do_free(&expr_node);
5736+
break;
5737+
}
5738+
}
5739+
57205740
static void zend_compile_echo(zend_ast *ast) /* {{{ */
57215741
{
57225742
zend_op *opline;
@@ -5967,7 +5987,7 @@ static void zend_compile_do_while(zend_ast *ast) /* {{{ */
59675987
}
59685988
/* }}} */
59695989

5970-
static void zend_compile_expr_list(znode *result, zend_ast *ast) /* {{{ */
5990+
static void zend_compile_for_expr_list(znode *result, zend_ast *ast) /* {{{ */
59715991
{
59725992
zend_ast_list *list;
59735993
uint32_t i;
@@ -5984,7 +6004,13 @@ static void zend_compile_expr_list(znode *result, zend_ast *ast) /* {{{ */
59846004
zend_ast *expr_ast = list->child[i];
59856005

59866006
zend_do_free(result);
5987-
zend_compile_expr(result, expr_ast);
6007+
if (expr_ast->kind == ZEND_AST_CAST_VOID) {
6008+
zend_compile_void_cast(NULL, expr_ast);
6009+
result->op_type = IS_CONST;
6010+
ZVAL_NULL(&result->u.constant);
6011+
} else {
6012+
zend_compile_expr(result, expr_ast);
6013+
}
59886014
}
59896015
}
59906016
/* }}} */
@@ -5999,7 +6025,7 @@ static void zend_compile_for(zend_ast *ast) /* {{{ */
59996025
znode result;
60006026
uint32_t opnum_start, opnum_jmp, opnum_loop;
60016027

6002-
zend_compile_expr_list(&result, init_ast);
6028+
zend_compile_for_expr_list(&result, init_ast);
60036029
zend_do_free(&result);
60046030

60056031
opnum_jmp = zend_emit_jump(0);
@@ -6010,11 +6036,11 @@ static void zend_compile_for(zend_ast *ast) /* {{{ */
60106036
zend_compile_stmt(stmt_ast);
60116037

60126038
opnum_loop = get_next_op_number();
6013-
zend_compile_expr_list(&result, loop_ast);
6039+
zend_compile_for_expr_list(&result, loop_ast);
60146040
zend_do_free(&result);
60156041

60166042
zend_update_jump_target_to_next(opnum_jmp);
6017-
zend_compile_expr_list(&result, cond_ast);
6043+
zend_compile_for_expr_list(&result, cond_ast);
60186044
zend_do_extended_stmt();
60196045

60206046
zend_emit_cond_jump(ZEND_JMPNZ, &result, opnum_start);
@@ -10594,26 +10620,6 @@ static void zend_compile_include_or_eval(znode *result, zend_ast *ast) /* {{{ */
1059410620
}
1059510621
/* }}} */
1059610622

10597-
static void zend_compile_void_cast(znode *result, zend_ast *ast)
10598-
{
10599-
zend_ast *expr_ast = ast->child[0];
10600-
znode expr_node;
10601-
zend_op *opline;
10602-
10603-
zend_compile_expr(&expr_node, expr_ast);
10604-
10605-
switch (expr_node.op_type) {
10606-
case IS_TMP_VAR:
10607-
case IS_VAR:
10608-
opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
10609-
opline->extended_value = ZEND_FREE_VOID_CAST;
10610-
break;
10611-
case IS_CONST:
10612-
zend_do_free(&expr_node);
10613-
break;
10614-
}
10615-
}
10616-
1061710623
static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
1061810624
{
1061910625
zend_ast *var_ast = ast->child[0];

Zend/zend_language_parser.y

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
268268
%type <ast> callable_expr callable_variable static_member new_variable
269269
%type <ast> encaps_var encaps_var_offset isset_variables
270270
%type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt
271-
%type <ast> alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
271+
%type <ast> alt_if_stmt for_cond_exprs for_exprs switch_case_list global_var_list static_var_list
272272
%type <ast> echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list
273273
%type <ast> implements_list case_list if_stmt_without_else
274274
%type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list
@@ -508,7 +508,7 @@ statement:
508508
{ $$ = zend_ast_create(ZEND_AST_WHILE, $3, $5); }
509509
| T_DO statement T_WHILE '(' expr ')' ';'
510510
{ $$ = zend_ast_create(ZEND_AST_DO_WHILE, $2, $5); }
511-
| T_FOR '(' for_exprs ';' for_exprs ';' for_exprs ')' for_statement
511+
| T_FOR '(' for_exprs ';' for_cond_exprs ';' for_exprs ')' for_statement
512512
{ $$ = zend_ast_create(ZEND_AST_FOR, $3, $5, $7, $9); }
513513
| T_SWITCH '(' expr ')' switch_case_list
514514
{ $$ = zend_ast_create(ZEND_AST_SWITCH, $3, $5); }
@@ -1169,13 +1169,21 @@ echo_expr:
11691169
expr { $$ = zend_ast_create(ZEND_AST_ECHO, $1); }
11701170
;
11711171

1172+
for_cond_exprs:
1173+
%empty { $$ = NULL; }
1174+
| non_empty_for_exprs ',' expr { $$ = zend_ast_list_add($1, $3); }
1175+
| expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); }
1176+
;
1177+
11721178
for_exprs:
11731179
%empty { $$ = NULL; }
11741180
| non_empty_for_exprs { $$ = $1; }
11751181
;
11761182

11771183
non_empty_for_exprs:
11781184
non_empty_for_exprs ',' expr { $$ = zend_ast_list_add($1, $3); }
1185+
| non_empty_for_exprs ',' T_VOID_CAST expr { $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_CAST_VOID, $4)); }
1186+
| T_VOID_CAST expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, zend_ast_create(ZEND_AST_CAST_VOID, $2)); }
11791187
| expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); }
11801188
;
11811189

0 commit comments

Comments
 (0)