]> granicus.if.org Git - php/commitdiff
Inference: Fix undef handling for binary ops
authorNikita Popov <nikic@php.net>
Sat, 14 May 2016 11:54:41 +0000 (13:54 +0200)
committerNikita Popov <nikic@php.net>
Sat, 14 May 2016 15:16:32 +0000 (17:16 +0200)
We need to be careful about correctly handling that undef results
in a null value. Otherwise, apart from simply generating incorrect
results, we may also end up performing non-monotonic lattice
transitions, thus causing an infinite type inference loop (see
test).

Zend/tests/inference_infinite_loop.phpt [new file with mode: 0644]
ext/opcache/Optimizer/zend_inference.c

diff --git a/Zend/tests/inference_infinite_loop.phpt b/Zend/tests/inference_infinite_loop.phpt
new file mode 100644 (file)
index 0000000..1e94ea8
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+Type inference should not result in infinite loop
+--FILE--
+<?php
+
+function test() {
+    $b = false;
+    do {
+        $a = $a + PHP_INT_MAX + 2;
+        $a = 0;
+    } while ($b);
+}
+test();
+
+?>
+--EXPECTF--
+Notice: Undefined variable: a in %s on line %d
index c247c29b3ec09f3f941d11ad3174d2fb58eda2b4..06f3cdc670d519fb6f744a9f267d370e134cb7e9 100644 (file)
@@ -2236,12 +2236,12 @@ static uint32_t assign_dim_result_type(
 static uint32_t binary_op_result_type(
                zend_ssa *ssa, zend_uchar opcode, uint32_t t1, uint32_t t2, uint32_t result_var) {
        uint32_t tmp;
+       uint32_t t1_type = (t1 & MAY_BE_ANY) | (t1 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
+       uint32_t t2_type = (t2 & MAY_BE_ANY) | (t2 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
        switch (opcode) {
                case ZEND_ADD:
                        tmp = MAY_BE_RC1;
-                       if ((t1 & MAY_BE_ANY) == MAY_BE_LONG &&
-                               (t2 & MAY_BE_ANY) == MAY_BE_LONG) {
-
+                       if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) {
                                if (!ssa->var_info[result_var].has_range ||
                                    ssa->var_info[result_var].range.underflow ||
                                    ssa->var_info[result_var].range.overflow) {
@@ -2250,17 +2250,15 @@ static uint32_t binary_op_result_type(
                                } else {
                                        tmp |= MAY_BE_LONG;
                                }
-                       } else if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE ||
-                               (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+                       } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
                                tmp |= MAY_BE_DOUBLE;
-                       } else if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY &&
-                                          (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) {
+                       } else if (t1_type == MAY_BE_ARRAY && t2_type == MAY_BE_ARRAY) {
                                tmp |= MAY_BE_ARRAY;
                                tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
                                tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
                        } else {
                                tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
-                               if ((t1 & MAY_BE_ARRAY) && (t2 & MAY_BE_ARRAY)) {
+                               if ((t1_type & MAY_BE_ARRAY) && (t2_type & MAY_BE_ARRAY)) {
                                        tmp |= MAY_BE_ARRAY;
                                        tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
                                        tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
@@ -2270,8 +2268,7 @@ static uint32_t binary_op_result_type(
                case ZEND_SUB:
                case ZEND_MUL:
                        tmp = MAY_BE_RC1;
-                       if ((t1 & MAY_BE_ANY) == MAY_BE_LONG &&
-                               (t2 & MAY_BE_ANY) == MAY_BE_LONG) {
+                       if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) {
                                if (!ssa->var_info[result_var].has_range ||
                                    ssa->var_info[result_var].range.underflow ||
                                    ssa->var_info[result_var].range.overflow) {
@@ -2280,8 +2277,7 @@ static uint32_t binary_op_result_type(
                                } else {
                                        tmp |= MAY_BE_LONG;
                                }
-                       } else if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE ||
-                               (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+                       } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
                                tmp |= MAY_BE_DOUBLE;
                        } else {
                                tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
@@ -2290,8 +2286,7 @@ static uint32_t binary_op_result_type(
                case ZEND_DIV:
                case ZEND_POW:
                        tmp = MAY_BE_RC1;
-                       if ((t1 & MAY_BE_ANY) == MAY_BE_DOUBLE ||
-                           (t2 & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+                       if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
                                tmp |= MAY_BE_DOUBLE;
                        } else {
                                tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
@@ -2307,10 +2302,10 @@ static uint32_t binary_op_result_type(
                case ZEND_BW_AND:
                case ZEND_BW_XOR:
                        tmp = MAY_BE_RC1;
-                       if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
+                       if ((t1_type & MAY_BE_STRING) && (t2_type & MAY_BE_STRING)) {
                                tmp |= MAY_BE_STRING;
                        }
-                       if ((t1 & (MAY_BE_ANY-MAY_BE_STRING)) || (t2 & (MAY_BE_ANY-MAY_BE_STRING))) {
+                       if ((t1_type & ~MAY_BE_STRING) || (t2_type & ~MAY_BE_STRING)) {
                                tmp |= MAY_BE_LONG;
                        }
                        break;