-
Notifications
You must be signed in to change notification settings - Fork 8k
Warn when coercing NAN to other types #19573
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
Changes from 2 commits
bbe346d
d82cfee
bb64442
6e1d6ff
95ff6b7
5bc6fb1
0d7a7fc
941811f
975aa39
cfa7c99
58d90b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -436,21 +436,14 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array | |
Tsource[VAR_NUM(opline->op1.var)] = NULL; | ||
break; | ||
} | ||
ZEND_FALLTHROUGH; | ||
|
||
case ZEND_IS_EQUAL: | ||
case ZEND_IS_NOT_EQUAL: | ||
if (opline->op1_type == IS_CONST && | ||
opline->op2_type == IS_CONST) { | ||
opline->op2_type == IS_CONST) { | ||
goto optimize_constant_binary_op; | ||
} | ||
/* IS_EQ(TRUE, X) => BOOL(X) | ||
* IS_EQ(FALSE, X) => BOOL_NOT(X) | ||
* IS_NOT_EQ(TRUE, X) => BOOL_NOT(X) | ||
* IS_NOT_EQ(FALSE, X) => BOOL(X) | ||
* CASE(TRUE, X) => BOOL(X) | ||
* CASE(FALSE, X) => BOOL_NOT(X) | ||
*/ | ||
} | ||
/* | ||
* CASE(TRUE, X) => BOOL(X) | ||
* CASE(FALSE, X) => BOOL_NOT(X) | ||
*/ | ||
if (opline->op1_type == IS_CONST && | ||
(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE || | ||
Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) { | ||
|
@@ -464,19 +457,34 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array | |
SET_UNUSED(opline->op2); | ||
++(*opt_count); | ||
goto optimize_bool; | ||
} else if (opline->op2_type == IS_CONST && | ||
(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE || | ||
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) { | ||
/* Optimization of comparison with "null" is not safe, | ||
* because ("0" == null) is not equal to !("0") | ||
*/ | ||
opline->opcode = | ||
((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ? | ||
ZEND_BOOL : ZEND_BOOL_NOT; | ||
SET_UNUSED(opline->op2); | ||
++(*opt_count); | ||
goto optimize_bool; | ||
} else if (opline->op2_type == IS_CONST && | ||
(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE || | ||
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) { | ||
/* Optimization of comparison with "null" is not safe, | ||
* because ("0" == null) is not equal to !("0") | ||
*/ | ||
opline->opcode = | ||
((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ? | ||
ZEND_BOOL : ZEND_BOOL_NOT; | ||
SET_UNUSED(opline->op2); | ||
++(*opt_count); | ||
goto optimize_bool; | ||
} | ||
break; | ||
|
||
case ZEND_IS_EQUAL: | ||
case ZEND_IS_NOT_EQUAL: | ||
if (opline->op1_type == IS_CONST && | ||
opline->op2_type == IS_CONST) { | ||
goto optimize_constant_binary_op; | ||
} | ||
/* IS_EQ(TRUE, X) => BOOL(X) | ||
* IS_EQ(FALSE, X) => BOOL_NOT(X) | ||
* IS_NOT_EQ(TRUE, X) => BOOL_NOT(X) | ||
* IS_NOT_EQ(FALSE, X) => BOOL(X) | ||
* Those optimizations are not safe if the other operand end up being NAN | ||
* as BOOL/BOOL_NOT will warn which IS_EQUAL/IS_NOT_EQUAL do not. | ||
|
||
*/ | ||
break; | ||
case ZEND_IS_IDENTICAL: | ||
if (opline->op1_type == IS_CONST && | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
--TEST-- | ||
Checking NAN in a switch statement with true/false | ||
--FILE-- | ||
<?php | ||
|
||
$nan = fdiv(0, 0); | ||
switch ($nan) { | ||
case true: | ||
echo "true"; | ||
break; | ||
case false: | ||
echo "false"; | ||
break; | ||
} | ||
?> | ||
--EXPECT-- | ||
true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
--TEST-- | ||
NAN coerced to other types | ||
--FILE-- | ||
<?php | ||
|
||
$inputs = [ | ||
0, | ||
null, | ||
false, | ||
true, | ||
"", | ||
[], | ||
NAN, | ||
]; | ||
|
||
$nan = fdiv(0, 0); | ||
var_dump($nan); | ||
foreach ($inputs as $right) { | ||
echo 'Using '; | ||
var_export($right); | ||
echo ' as right op', PHP_EOL; | ||
var_dump($nan == $right); | ||
var_dump($nan != $right); | ||
var_dump($nan === $right); | ||
var_dump($nan !== $right); | ||
var_dump($nan < $right); | ||
var_dump($nan <= $right); | ||
var_dump($nan > $right); | ||
var_dump($nan >= $right); | ||
var_dump($nan <=> $right); | ||
} | ||
|
||
?> | ||
--EXPECT-- | ||
float(NAN) | ||
Using 0 as right op | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(false) | ||
bool(false) | ||
bool(false) | ||
int(1) | ||
Using NULL as right op | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(false) | ||
bool(true) | ||
bool(true) | ||
int(1) | ||
Using false as right op | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(false) | ||
bool(true) | ||
bool(true) | ||
int(1) | ||
Using true as right op | ||
bool(true) | ||
bool(false) | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(true) | ||
int(0) | ||
Using '' as right op | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(false) | ||
bool(false) | ||
bool(false) | ||
int(1) | ||
Using array ( | ||
) as right op | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(true) | ||
bool(true) | ||
bool(true) | ||
bool(false) | ||
bool(false) | ||
int(-1) | ||
Using NAN as right op | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(true) | ||
bool(false) | ||
bool(false) | ||
bool(false) | ||
bool(false) | ||
int(1) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
--TEST-- | ||
NAN coerced to other types | ||
--FILE-- | ||
<?php | ||
|
||
$nan = fdiv(0, 0); | ||
var_dump($nan); | ||
|
||
function implicit_to_bool(bool $v) { | ||
var_dump($v); | ||
} | ||
function implicit_to_string(string $v) { | ||
var_dump($v); | ||
} | ||
|
||
implicit_to_bool($nan); | ||
implicit_to_string($nan); | ||
|
||
var_dump((int) $nan); | ||
var_dump((bool) $nan); | ||
var_dump((string) $nan); | ||
var_dump((array) $nan); | ||
var_dump((object) $nan); | ||
|
||
$types = [ | ||
'null', | ||
'bool', | ||
'int', | ||
'string', | ||
'array', | ||
'object', | ||
]; | ||
|
||
foreach ($types as $type) { | ||
$nan = fdiv(0, 0); | ||
settype($nan, $type); | ||
var_dump($nan); | ||
} | ||
|
||
?> | ||
--EXPECTF-- | ||
float(NAN) | ||
|
||
Warning: unexpected NAN value was coerced to bool in %s on line %d | ||
bool(true) | ||
|
||
Warning: unexpected NAN value was coerced to string in %s on line %d | ||
string(3) "NAN" | ||
|
||
Warning: The float NAN is not representable as an int, cast occurred in %s on line %d | ||
int(0) | ||
|
||
Warning: unexpected NAN value was coerced to bool in %s on line %d | ||
bool(true) | ||
|
||
Warning: unexpected NAN value was coerced to string in %s on line %d | ||
string(3) "NAN" | ||
|
||
Warning: unexpected NAN value was coerced to array in %s on line %d | ||
array(1) { | ||
[0]=> | ||
float(NAN) | ||
} | ||
|
||
Warning: unexpected NAN value was coerced to object in %s on line %d | ||
object(stdClass)#%d (1) { | ||
["scalar"]=> | ||
float(NAN) | ||
} | ||
|
||
Warning: unexpected NAN value was coerced to null in %s on line %d | ||
NULL | ||
|
||
Warning: unexpected NAN value was coerced to bool in %s on line %d | ||
bool(true) | ||
|
||
Warning: The float NAN is not representable as an int, cast occurred in %s on line %d | ||
int(0) | ||
|
||
Warning: unexpected NAN value was coerced to string in %s on line %d | ||
string(3) "NAN" | ||
|
||
Warning: unexpected NAN value was coerced to array in %s on line %d | ||
array(1) { | ||
[0]=> | ||
float(NAN) | ||
} | ||
|
||
Warning: unexpected NAN value was coerced to object in %s on line %d | ||
object(stdClass)#%d (1) { | ||
["scalar"]=> | ||
float(NAN) | ||
} |
Uh oh!
There was an error while loading. Please reload this page.