Skip to content

Evaluate const expression cast at ct if possible #18347

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
27 changes: 2 additions & 25 deletions Zend/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,31 +82,8 @@ zend_result zend_optimizer_eval_unary_op(zval *result, uint8_t opcode, zval *op1

zend_result zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1) /* {{{ */
{
switch (type) {
case IS_NULL:
ZVAL_NULL(result);
return SUCCESS;
case _IS_BOOL:
ZVAL_BOOL(result, zval_is_true(op1));
return SUCCESS;
case IS_LONG:
ZVAL_LONG(result, zval_get_long(op1));
return SUCCESS;
case IS_DOUBLE:
ZVAL_DOUBLE(result, zval_get_double(op1));
return SUCCESS;
case IS_STRING:
/* Conversion from double to string takes into account run-time
'precision' setting and cannot be evaluated at compile-time */
if (Z_TYPE_P(op1) != IS_ARRAY && Z_TYPE_P(op1) != IS_DOUBLE) {
ZVAL_STR(result, zval_get_string(op1));
return SUCCESS;
}
break;
case IS_ARRAY:
ZVAL_COPY(result, op1);
convert_to_array(result);
return SUCCESS;
if (zend_try_ct_eval_cast(result, type, op1)) {
return SUCCESS;
}
return FAILURE;
}
Expand Down
65 changes: 65 additions & 0 deletions Zend/tests/constexpr/constant_expressions_cast.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,28 @@ const T9 = (array) new DateTime;
const T10 = (int) new DateTime;

var_dump(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);

const C_FLOAT = 0.3;
const C_EMPTY_ARRAY = [];
const C_ARRAY = ["a" => 1];
const C_INT = 5;
const C_EMPTY_STRING = "";
const C_CALLABLE = var_dump(...);
const C_USER_OBJECT = new X;
const C_DATE_TIME = new DateTime;

const T11 = (int) C_FLOAT;
const T12 = (bool) C_FLOAT;
const T13 = (string) C_EMPTY_ARRAY;
const T14 = (object) C_ARRAY;
const T15 = (float) C_INT;
const T16 = (array) C_EMPTY_STRING;
const T17 = (array) C_CALLABLE;
const T18 = (array) C_USER_OBJECT;
const T19 = (array) C_DATE_TIME;
const T20 = (int) C_DATE_TIME;

var_dump(T11, T12, T13, T14, T15, T16, T17, T18, T19, T20);
?>
--EXPECTF--
Warning: Array to string conversion in %s on line %d
Expand Down Expand Up @@ -62,3 +84,46 @@ array(3) {
string(%d) "%s"
}
int(1)

Warning: Array to string conversion in %s on line %d

Warning: Object of class DateTime could not be converted to int in %s on line %d
int(0)
bool(true)
string(5) "Array"
object(stdClass)#%d (1) {
["a"]=>
int(1)
}
float(5)
array(1) {
[0]=>
string(0) ""
}
array(1) {
[0]=>
object(Closure)#%d (2) {
["function"]=>
string(8) "var_dump"
["parameter"]=>
array(2) {
["$value"]=>
string(10) "<required>"
["$values"]=>
string(10) "<optional>"
}
}
}
array(1) {
["foo"]=>
int(3)
}
array(3) {
["date"]=>
string(%d) "%s"
["timezone_type"]=>
int(%d)
["timezone"]=>
string(%d) "%s"
}
int(1)
32 changes: 32 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -11842,6 +11842,34 @@ static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t
}
/* }}} */

bool zend_try_ct_eval_cast(zval *result, uint32_t type, zval *op1)
{
switch (type) {
case _IS_BOOL:
ZVAL_BOOL(result, zval_is_true(op1));
return true;
case IS_LONG:
ZVAL_LONG(result, zval_get_long(op1));
return true;
case IS_DOUBLE:
ZVAL_DOUBLE(result, zval_get_double(op1));
return true;
case IS_STRING:
/* Conversion from double to string takes into account run-time
'precision' setting and cannot be evaluated at compile-time */
if (Z_TYPE_P(op1) != IS_ARRAY && Z_TYPE_P(op1) != IS_DOUBLE) {
ZVAL_STR(result, zval_get_string(op1));
return true;
}
break;
case IS_ARRAY:
ZVAL_COPY(result, op1);
convert_to_array(result);
return true;
}
return false;
}

static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
{
zend_ast *ast = *ast_ptr;
Expand Down Expand Up @@ -12129,6 +12157,10 @@ static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
return;
case ZEND_AST_CAST:
zend_eval_const_expr(&ast->child[0]);
if (ast->child[0]->kind == ZEND_AST_ZVAL
&& zend_try_ct_eval_cast(&result, ast->attr, zend_ast_get_zval(ast->child[0]))) {
break;
}
return;
default:
return;
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -1301,4 +1301,6 @@ ZEND_API bool zend_is_op_long_compatible(const zval *op);
ZEND_API bool zend_binary_op_produces_error(uint32_t opcode, const zval *op1, const zval *op2);
ZEND_API bool zend_unary_op_produces_error(uint32_t opcode, const zval *op);

bool zend_try_ct_eval_cast(zval *result, uint32_t type, zval *op1);

#endif /* ZEND_COMPILE_H */
Loading