Skip to content

zend_compile: Allow (void) in for’s initializer and loop expression #18303

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions Zend/tests/type_casts/gh18301_cast_to_void_for.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
--TEST--
GH-18301: casting to void is allowed in for’s expression lists
--FILE--
<?php

$count = 0;

#[NoDiscard]
function incCount() {
global $count;
$count++;
return $count;
}

for ( $count = 0, (void)incCount(), incCount(); (void)incCount(), incCount() < 30; incCount(), $count++, incCount(), (void)incCount()) {
echo $count . "\n";
}

?>
--EXPECTF--
Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
4

Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d

Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
10

Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d

Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
16

Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d

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

Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d

Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
28

Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d

Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
GH-18301: casting to void is not allowed at the end of a for condition
--FILE--
<?php

for (;(void)true;);
?>
--EXPECTF--
Parse error: syntax error, unexpected token ";", expecting "," in %s on line %d
56 changes: 31 additions & 25 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -5717,6 +5717,26 @@ static void zend_compile_return(zend_ast *ast) /* {{{ */
}
/* }}} */

static void zend_compile_void_cast(znode *result, zend_ast *ast)
{
zend_ast *expr_ast = ast->child[0];
znode expr_node;
zend_op *opline;

zend_compile_expr(&expr_node, expr_ast);

switch (expr_node.op_type) {
case IS_TMP_VAR:
case IS_VAR:
opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
opline->extended_value = ZEND_FREE_VOID_CAST;
break;
case IS_CONST:
zend_do_free(&expr_node);
break;
}
}

static void zend_compile_echo(zend_ast *ast) /* {{{ */
{
zend_op *opline;
Expand Down Expand Up @@ -5967,7 +5987,7 @@ static void zend_compile_do_while(zend_ast *ast) /* {{{ */
}
/* }}} */

static void zend_compile_expr_list(znode *result, zend_ast *ast) /* {{{ */
static void zend_compile_for_expr_list(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast_list *list;
uint32_t i;
Expand All @@ -5984,7 +6004,13 @@ static void zend_compile_expr_list(znode *result, zend_ast *ast) /* {{{ */
zend_ast *expr_ast = list->child[i];

zend_do_free(result);
zend_compile_expr(result, expr_ast);
if (expr_ast->kind == ZEND_AST_CAST_VOID) {
zend_compile_void_cast(NULL, expr_ast);
result->op_type = IS_CONST;
ZVAL_NULL(&result->u.constant);
} else {
zend_compile_expr(result, expr_ast);
}
}
}
/* }}} */
Expand All @@ -5999,7 +6025,7 @@ static void zend_compile_for(zend_ast *ast) /* {{{ */
znode result;
uint32_t opnum_start, opnum_jmp, opnum_loop;

zend_compile_expr_list(&result, init_ast);
zend_compile_for_expr_list(&result, init_ast);
zend_do_free(&result);

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

opnum_loop = get_next_op_number();
zend_compile_expr_list(&result, loop_ast);
zend_compile_for_expr_list(&result, loop_ast);
zend_do_free(&result);

zend_update_jump_target_to_next(opnum_jmp);
zend_compile_expr_list(&result, cond_ast);
zend_compile_for_expr_list(&result, cond_ast);
zend_do_extended_stmt();

zend_emit_cond_jump(ZEND_JMPNZ, &result, opnum_start);
Expand Down Expand Up @@ -10594,26 +10620,6 @@ static void zend_compile_include_or_eval(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */

static void zend_compile_void_cast(znode *result, zend_ast *ast)
{
zend_ast *expr_ast = ast->child[0];
znode expr_node;
zend_op *opline;

zend_compile_expr(&expr_node, expr_ast);

switch (expr_node.op_type) {
case IS_TMP_VAR:
case IS_VAR:
opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
opline->extended_value = ZEND_FREE_VOID_CAST;
break;
case IS_CONST:
zend_do_free(&expr_node);
break;
}
}

static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
Expand Down
12 changes: 10 additions & 2 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> callable_expr callable_variable static_member new_variable
%type <ast> encaps_var encaps_var_offset isset_variables
%type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt
%type <ast> alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
%type <ast> alt_if_stmt for_cond_exprs for_exprs switch_case_list global_var_list static_var_list
%type <ast> echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list
%type <ast> implements_list case_list if_stmt_without_else
%type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list
Expand Down Expand Up @@ -508,7 +508,7 @@ statement:
{ $$ = zend_ast_create(ZEND_AST_WHILE, $3, $5); }
| T_DO statement T_WHILE '(' expr ')' ';'
{ $$ = zend_ast_create(ZEND_AST_DO_WHILE, $2, $5); }
| T_FOR '(' for_exprs ';' for_exprs ';' for_exprs ')' for_statement
| T_FOR '(' for_exprs ';' for_cond_exprs ';' for_exprs ')' for_statement
{ $$ = zend_ast_create(ZEND_AST_FOR, $3, $5, $7, $9); }
| T_SWITCH '(' expr ')' switch_case_list
{ $$ = zend_ast_create(ZEND_AST_SWITCH, $3, $5); }
Expand Down Expand Up @@ -1169,13 +1169,21 @@ echo_expr:
expr { $$ = zend_ast_create(ZEND_AST_ECHO, $1); }
;

for_cond_exprs:
%empty { $$ = NULL; }
| non_empty_for_exprs ',' expr { $$ = zend_ast_list_add($1, $3); }
| expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); }
;

for_exprs:
%empty { $$ = NULL; }
| non_empty_for_exprs { $$ = $1; }
;

non_empty_for_exprs:
non_empty_for_exprs ',' expr { $$ = zend_ast_list_add($1, $3); }
| non_empty_for_exprs ',' T_VOID_CAST expr { $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_CAST_VOID, $4)); }
| T_VOID_CAST expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, zend_ast_create(ZEND_AST_CAST_VOID, $2)); }
| expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); }
;

Expand Down
Loading