Skip to content

Commit c9b8cbc

Browse files
committed
Add (void) cast
1 parent 7cc44af commit c9b8cbc

12 files changed

+80
-15
lines changed

Zend/Optimizer/block_pass.c

+3-1
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

+2-1
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))) {

Zend/tests/attributes/nodiscard/suppress_cast.phpt

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
#[\NoDiscard]: Casting to (bool) suppresses.
2+
#[\NoDiscard]: Casting to (void) suppresses.
33
--FILE--
44
<?php
55

@@ -43,22 +43,22 @@ $closure2 = #[\NoDiscard] function(): int {
4343
return 0;
4444
};
4545

46-
(bool)test();
47-
(bool)test2();
48-
(bool)test3(1, 2, named: 3);
49-
(bool)call_user_func("test");
46+
(void)test();
47+
(void)test2();
48+
(void)test3(1, 2, named: 3);
49+
(void)call_user_func("test");
5050
$fcc = test(...);
51-
(bool)$fcc();
51+
(void)$fcc();
5252

5353
$cls = new Clazz();
54-
(bool)$cls->test();
55-
(bool)$cls->test2();
56-
(bool)call_user_func([$cls, "test"]);
57-
(bool)Clazz::test3();
54+
(void)$cls->test();
55+
(void)$cls->test2();
56+
(void)call_user_func([$cls, "test"]);
57+
(void)Clazz::test3();
5858

59-
(bool)$closure();
59+
(void)$closure();
6060

61-
(bool)$closure2();
61+
(void)$closure2();
6262

6363
?>
6464
DONE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
#[\NoDiscard]: 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+
#[\NoDiscard]
13+
function test(): WithDestructor {
14+
return new WithDestructor();
15+
}
16+
17+
function do_it(): void {
18+
echo "Before", PHP_EOL;
19+
20+
(void)test();
21+
22+
echo "After", PHP_EOL;
23+
}
24+
25+
do_it();
26+
27+
?>
28+
--EXPECT--
29+
Before
30+
WithDestructor::__destruct
31+
After

Zend/zend_ast.h

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ enum _zend_ast_kind {
8383
ZEND_AST_UNARY_PLUS,
8484
ZEND_AST_UNARY_MINUS,
8585
ZEND_AST_CAST,
86+
ZEND_AST_CAST_VOID,
8687
ZEND_AST_EMPTY,
8788
ZEND_AST_ISSET,
8889
ZEND_AST_SILENCE,

Zend/zend_compile.c

+16
Original file line numberDiff line numberDiff line change
@@ -10590,6 +10590,19 @@ static void zend_compile_include_or_eval(znode *result, zend_ast *ast) /* {{{ */
1059010590
}
1059110591
/* }}} */
1059210592

10593+
static void zend_compile_void_cast(znode *result, zend_ast *ast)
10594+
{
10595+
zend_ast *expr_ast = ast->child[0];
10596+
znode expr_node;
10597+
zend_op *opline;
10598+
10599+
zend_do_extended_fcall_begin();
10600+
zend_compile_expr(&expr_node, expr_ast);
10601+
10602+
opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
10603+
opline->extended_value = ZEND_FREE_VOID_CAST;
10604+
}
10605+
1059310606
static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
1059410607
{
1059510608
zend_ast *var_ast = ast->child[0];
@@ -11486,6 +11499,9 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
1148611499
case ZEND_AST_THROW:
1148711500
zend_compile_expr(NULL, ast);
1148811501
break;
11502+
case ZEND_AST_CAST_VOID:
11503+
zend_compile_void_cast(NULL, ast);
11504+
break;
1148911505
default:
1149011506
{
1149111507
znode result;

Zend/zend_compile.h

+1
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
10951095

10961096
#define ZEND_FREE_ON_RETURN (1<<0)
10971097
#define ZEND_FREE_SWITCH (1<<1)
1098+
#define ZEND_FREE_VOID_CAST (1<<2)
10981099

10991100
#define ZEND_SEND_BY_VAL 0u
11001101
#define ZEND_SEND_BY_REF 1u

Zend/zend_language_parser.y

+2
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

+4
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
}

ext/tokenizer/tokenizer_data.c

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/tokenizer/tokenizer_data.stub.php

+5
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,11 @@
642642
* @cvalue T_UNSET_CAST
643643
*/
644644
const T_UNSET_CAST = UNKNOWN;
645+
/**
646+
* @var int
647+
* @cvalue T_VOID_CAST
648+
*/
649+
const T_VOID_CAST = UNKNOWN;
645650
/**
646651
* @var int
647652
* @cvalue T_OBJECT_OPERATOR

ext/tokenizer/tokenizer_data_arginfo.h

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)