Skip to content

Commit 320fe29

Browse files
committed
core: Warn when coercing NAN to other types
RFC: https://wiki.php.net/rfc/warnings-php-8-5#coercing_nan_to_other_types Closes GH-19573
1 parent d27e1e1 commit 320fe29

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+697
-110
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ PHP NEWS
1717
(nielsdos)
1818
. Casting floats that are not representable as ints now emits a warning.
1919
(Girgias)
20+
. Casting NAN to other types now emits a warning. (Girgias)
2021

2122
- Bz2:
2223
. Fixed bug GH-19810 (Broken bzopen() stream mode validation). (ilutov)

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ PHP 8.5 UPGRADE NOTES
5959
floats) to int if they cannot be represented as one. This affects explicit
6060
int casts and implicit int casts.
6161
RFC: https://wiki.php.net/rfc/warnings-php-8-5#casting_out_of_range_floats_to_int
62+
. A warning is now emitted when casting NAN to other types.
63+
RFC: https://wiki.php.net/rfc/warnings-php-8-5#coercing_nan_to_other_types
6264

6365
- BZ2:
6466
. bzcompress() now throws a ValueError when $block_size is not between

Zend/Optimizer/block_pass.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -436,18 +436,11 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
436436
Tsource[VAR_NUM(opline->op1.var)] = NULL;
437437
break;
438438
}
439-
ZEND_FALLTHROUGH;
440-
441-
case ZEND_IS_EQUAL:
442-
case ZEND_IS_NOT_EQUAL:
443439
if (opline->op1_type == IS_CONST &&
444440
opline->op2_type == IS_CONST) {
445441
goto optimize_constant_binary_op;
446442
}
447-
/* IS_EQ(TRUE, X) => BOOL(X)
448-
* IS_EQ(FALSE, X) => BOOL_NOT(X)
449-
* IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
450-
* IS_NOT_EQ(FALSE, X) => BOOL(X)
443+
/*
451444
* CASE(TRUE, X) => BOOL(X)
452445
* CASE(FALSE, X) => BOOL_NOT(X)
453446
*/
@@ -478,6 +471,21 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
478471
goto optimize_bool;
479472
}
480473
break;
474+
475+
case ZEND_IS_EQUAL:
476+
case ZEND_IS_NOT_EQUAL:
477+
if (opline->op1_type == IS_CONST &&
478+
opline->op2_type == IS_CONST) {
479+
goto optimize_constant_binary_op;
480+
}
481+
/* IS_EQ(TRUE, X) => BOOL(X)
482+
* IS_EQ(FALSE, X) => BOOL_NOT(X)
483+
* IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
484+
* IS_NOT_EQ(FALSE, X) => BOOL(X)
485+
* Those optimizations are not safe if the other operand ends up being NAN
486+
* as BOOL/BOOL_NOT will warn, while IS_EQUAL/IS_NOT_EQUAL do not.
487+
*/
488+
break;
481489
case ZEND_IS_IDENTICAL:
482490
if (opline->op1_type == IS_CONST &&
483491
opline->op2_type == IS_CONST) {

Zend/Optimizer/sccp.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,10 @@ static inline zend_result ct_eval_bool_cast(zval *result, zval *op) {
335335
ZVAL_TRUE(result);
336336
return SUCCESS;
337337
}
338+
/* NAN warns when casting */
339+
if (Z_TYPE_P(op) == IS_DOUBLE && zend_isnan(Z_DVAL_P(op))) {
340+
return FAILURE;
341+
}
338342

339343
ZVAL_BOOL(result, zend_is_true(op));
340344
return SUCCESS;

Zend/Optimizer/zend_inference.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5109,14 +5109,16 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
51095109
case ZEND_PRE_DEC:
51105110
case ZEND_POST_DEC:
51115111
return (t1 & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
5112-
case ZEND_BOOL_NOT:
51135112
case ZEND_JMPZ:
51145113
case ZEND_JMPNZ:
51155114
case ZEND_JMPZ_EX:
51165115
case ZEND_JMPNZ_EX:
5117-
case ZEND_BOOL:
51185116
case ZEND_JMP_SET:
51195117
return (t1 & MAY_BE_OBJECT);
5118+
case ZEND_BOOL:
5119+
case ZEND_BOOL_NOT:
5120+
/* NAN Cast to bool will warn, but if we have a range it is fine */
5121+
return (t1 & MAY_BE_OBJECT) || ((t1 & MAY_BE_DOUBLE) && !OP1_HAS_RANGE());
51205122
case ZEND_BOOL_XOR:
51215123
return (t1 & MAY_BE_OBJECT) || (t2 & MAY_BE_OBJECT);
51225124
case ZEND_IS_EQUAL:

Zend/Optimizer/zend_optimizer.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ zend_result zend_optimizer_eval_unary_op(zval *result, uint8_t opcode, zval *op1
7474
}
7575
return unary_op(result, op1);
7676
} else { /* ZEND_BOOL */
77+
if (Z_TYPE_P(op1) == IS_DOUBLE && zend_isnan(Z_DVAL_P(op1))) {
78+
return FAILURE;
79+
}
7780
ZVAL_BOOL(result, zend_is_true(op1));
7881
return SUCCESS;
7982
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Checking NAN in a switch statement with true/false
3+
--FILE--
4+
<?php
5+
6+
$nan = fdiv(0, 0);
7+
switch ($nan) {
8+
case true:
9+
echo "true";
10+
break;
11+
case false:
12+
echo "false";
13+
break;
14+
}
15+
?>
16+
--EXPECT--
17+
true

Zend/tests/type_coercion/float_to_int/explicit_casts_should_not_warn.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ foreach($values as $value) {
2626

2727
?>
2828
--EXPECTF--
29+
Warning: unexpected NAN value was coerced to string in %s on line %d
2930
int(3)
3031
int(3)
3132

Zend/tests/type_coercion/float_to_int/explicit_casts_should_not_warn_32bit.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Explicit (int) cast must not warn 32bit variation
2+
Explicit (int) cast must not warn if value is representable 32bit variation
33
--SKIPIF--
44
<?php
55
if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only");
@@ -26,6 +26,7 @@ foreach($values as $value) {
2626

2727
?>
2828
--EXPECTF--
29+
Warning: unexpected NAN value was coerced to string in %s on line %d
2930
int(3)
3031
int(3)
3132

Zend/tests/type_coercion/float_to_int/union_int_string_type_arg.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ int(1)
2323

2424
Deprecated: Implicit conversion from float 1.5 to int loses precision in %s on line %d
2525
int(1)
26+
27+
Warning: unexpected NAN value was coerced to string in %s on line %d
2628
string(3) "NAN"
2729
string(8) "1.0E+121"
2830
string(3) "INF"

0 commit comments

Comments
 (0)