Skip to content

Commit 7b57bfb

Browse files
committed
1 parent fb07c62 commit 7b57bfb

17 files changed

+1586
-1401
lines changed

Zend/Optimizer/block_pass.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
274274
* If it's not local, then the other blocks successors must also eventually either FREE or consume the temporary,
275275
* hence removing the temporary is not safe in the general case, especially when other consumers are not FREE.
276276
* A FREE may not be removed without also removing the source's result, because otherwise that would cause a memory leak. */
277-
if (opline->op1_type == IS_TMP_VAR) {
277+
if (opline->extended_value == ZEND_FREE_VOID_CAST) {
278+
/* Keep the ZEND_FREE opcode alive. */
279+
} else if (opline->op1_type == IS_TMP_VAR) {
278280
src = VAR_SOURCE(opline->op1);
279281
if (src) {
280282
switch (src->opcode) {

Zend/Optimizer/dce.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ static inline bool may_have_side_effects(
8080
case ZEND_IS_IDENTICAL:
8181
case ZEND_IS_NOT_IDENTICAL:
8282
case ZEND_QM_ASSIGN:
83-
case ZEND_FREE:
8483
case ZEND_FE_FREE:
8584
case ZEND_TYPE_CHECK:
8685
case ZEND_DEFINED:
@@ -127,6 +126,8 @@ static inline bool may_have_side_effects(
127126
case ZEND_ARRAY_KEY_EXISTS:
128127
/* No side effects */
129128
return 0;
129+
case ZEND_FREE:
130+
return opline->extended_value == ZEND_FREE_VOID_CAST;
130131
case ZEND_ADD_ARRAY_ELEMENT:
131132
/* TODO: We can't free two vars. Keep instruction alive. <?php [0, "$a" => "$b"]; */
132133
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
casting different variables to void
3+
--FILE--
4+
<?php
5+
6+
$r = fopen(__FILE__, "r");
7+
8+
class test {
9+
private $var1 = 1;
10+
public $var2 = 2;
11+
protected $var3 = 3;
12+
13+
function __toString() {
14+
return "10";
15+
}
16+
}
17+
18+
$o = new test;
19+
20+
$vars = array(
21+
"string",
22+
"",
23+
"\0",
24+
"8754456",
25+
9876545,
26+
0.10,
27+
array(),
28+
array(1,2,3),
29+
false,
30+
true,
31+
NULL,
32+
$r,
33+
$o
34+
);
35+
36+
foreach ($vars as $var) {
37+
(void)$var;
38+
}
39+
40+
// Cast literals to verify behavior for optimized const zvals
41+
(void)"foo";
42+
(void)["foo", "bar"];
43+
44+
echo "Done\n";
45+
?>
46+
--EXPECTF--
47+
Done
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
casting to void destroys the value.
3+
--FILE--
4+
<?php
5+
6+
class WithDestructor {
7+
public function __destruct() {
8+
echo __METHOD__, PHP_EOL;
9+
}
10+
}
11+
12+
function test(): WithDestructor {
13+
return new WithDestructor();
14+
}
15+
16+
function do_it(): void {
17+
echo "Before", PHP_EOL;
18+
19+
(void)test();
20+
21+
echo "After", PHP_EOL;
22+
}
23+
24+
do_it();
25+
26+
?>
27+
--EXPECT--
28+
Before
29+
WithDestructor::__destruct
30+
After
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
casting to void is a statement
3+
--FILE--
4+
<?php
5+
6+
$r = fopen(__FILE__, "r");
7+
8+
class test {
9+
private $var1 = 1;
10+
public $var2 = 2;
11+
protected $var3 = 3;
12+
13+
function __toString() {
14+
return "10";
15+
}
16+
}
17+
18+
$o = new test;
19+
20+
$vars = array(
21+
"string",
22+
"",
23+
"\0",
24+
"8754456",
25+
9876545,
26+
0.10,
27+
array(),
28+
array(1,2,3),
29+
false,
30+
true,
31+
NULL,
32+
$r,
33+
$o
34+
);
35+
36+
foreach ($vars as $var) {
37+
$tmp = (void)$var;
38+
var_dump($tmp);
39+
}
40+
41+
echo "Done\n";
42+
?>
43+
--EXPECTF--
44+
Parse error: syntax error, unexpected token "(void)" in %s on line %d

Zend/zend_ast.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ enum _zend_ast_kind {
8484
ZEND_AST_UNARY_PLUS,
8585
ZEND_AST_UNARY_MINUS,
8686
ZEND_AST_CAST,
87+
ZEND_AST_CAST_VOID,
8788
ZEND_AST_EMPTY,
8889
ZEND_AST_ISSET,
8990
ZEND_AST_SILENCE,

Zend/zend_compile.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10589,6 +10589,25 @@ static void zend_compile_include_or_eval(znode *result, zend_ast *ast) /* {{{ */
1058910589
}
1059010590
/* }}} */
1059110591

10592+
static void zend_compile_void_cast(znode *result, zend_ast *ast)
10593+
{
10594+
zend_ast *expr_ast = ast->child[0];
10595+
znode expr_node;
10596+
zend_op *opline;
10597+
10598+
zend_do_extended_fcall_begin();
10599+
zend_compile_expr(&expr_node, expr_ast);
10600+
10601+
switch (expr_node.op_type) {
10602+
case IS_TMP_VAR:
10603+
case IS_VAR:
10604+
case IS_CONST:
10605+
opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
10606+
opline->extended_value = ZEND_FREE_VOID_CAST;
10607+
break;
10608+
}
10609+
}
10610+
1059210611
static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
1059310612
{
1059410613
zend_ast *var_ast = ast->child[0];
@@ -11547,6 +11566,9 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
1154711566
case ZEND_AST_THROW:
1154811567
zend_compile_expr(NULL, ast);
1154911568
break;
11569+
case ZEND_AST_CAST_VOID:
11570+
zend_compile_void_cast(NULL, ast);
11571+
break;
1155011572
default:
1155111573
{
1155211574
znode result;

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
10921092

10931093
#define ZEND_FREE_ON_RETURN (1<<0)
10941094
#define ZEND_FREE_SWITCH (1<<1)
1095+
#define ZEND_FREE_VOID_CAST (1<<2)
10951096

10961097
#define ZEND_SEND_BY_VAL 0u
10971098
#define ZEND_SEND_BY_REF 1u

Zend/zend_language_parser.y

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
217217
%token T_OBJECT_CAST "'(object)'"
218218
%token T_BOOL_CAST "'(bool)'"
219219
%token T_UNSET_CAST "'(unset)'"
220+
%token T_VOID_CAST "'(void)'"
220221
%token T_OBJECT_OPERATOR "'->'"
221222
%token T_NULLSAFE_OBJECT_OPERATOR "'?->'"
222223
%token T_DOUBLE_ARROW "'=>'"
@@ -534,6 +535,7 @@ statement:
534535
{ $$ = zend_ast_create(ZEND_AST_TRY, $3, $5, $6); }
535536
| T_GOTO T_STRING ';' { $$ = zend_ast_create(ZEND_AST_GOTO, $2); }
536537
| T_STRING ':' { $$ = zend_ast_create(ZEND_AST_LABEL, $1); }
538+
| T_VOID_CAST expr ';' { $$ = zend_ast_create(ZEND_AST_CAST_VOID, $2); }
537539
;
538540

539541
catch_list:

Zend/zend_language_scanner.l

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,10 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_
16571657
RETURN_TOKEN(T_UNSET_CAST);
16581658
}
16591659

1660+
<ST_IN_SCRIPTING>"("{TABS_AND_SPACES}("void"){TABS_AND_SPACES}")" {
1661+
RETURN_TOKEN(T_VOID_CAST);
1662+
}
1663+
16601664
<ST_IN_SCRIPTING>"eval" {
16611665
RETURN_TOKEN_WITH_IDENT(T_EVAL);
16621666
}

Zend/zend_vm_def.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3202,12 +3202,12 @@ ZEND_VM_COLD_CONST_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, JMP_ADDR)
32023202
ZEND_VM_JMP(opline);
32033203
}
32043204

3205-
ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
3205+
ZEND_VM_HANDLER(70, ZEND_FREE, CONST|TMPVAR, ANY)
32063206
{
32073207
USE_OPLINE
32083208

32093209
SAVE_OPLINE();
3210-
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
3210+
FREE_OP1();
32113211
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
32123212
}
32133213

0 commit comments

Comments
 (0)