Skip to content

Commit

Permalink
Improve type narrowing fix
Browse files Browse the repository at this point in the history
We need to explicitly model the null return type for property
accesses on non-objects.
  • Loading branch information
nikic committed Apr 10, 2022
1 parent 323f3c6 commit c72e962
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 173 deletions.
62 changes: 32 additions & 30 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -1764,11 +1764,6 @@ static uint32_t get_ssa_alias_types(zend_ssa_alias_kind alias) {
(ssa_var_info[__var].type & MAY_BE_REF) \
== (__type & MAY_BE_REF)); \
if (ssa_var_info[__var].type & ~__type) { \
if ((ssa_var_info[__var].type & ~__type & \
~(MAY_BE_RC1|MAY_BE_RCN)) == 0) { \
ssa_var_info[__var].type |= __type; \
break; \
} \
emit_type_narrowing_warning(op_array, ssa, __var); \
return FAILURE; \
} \
Expand Down Expand Up @@ -3375,33 +3370,40 @@ static zend_always_inline int _zend_update_type_info(
case ZEND_FETCH_OBJ_UNSET:
case ZEND_FETCH_OBJ_FUNC_ARG:
if (ssa_op->result_def >= 0) {
zend_property_info *prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op);

tmp = zend_fetch_prop_type(script, prop_info, &ce);
if (opline->result_type == IS_VAR) {
tmp |= MAY_BE_REF | MAY_BE_INDIRECT;
} else if (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || !(t1 & MAY_BE_RC1)) {
zend_class_entry *ce = NULL;

if (opline->op1_type == IS_UNUSED) {
ce = op_array->scope;
} else if (ssa_op->op1_use >= 0 && !ssa->var_info[ssa_op->op1_use].is_instanceof) {
ce = ssa->var_info[ssa_op->op1_use].ce;
}
if (prop_info) {
/* FETCH_OBJ_R/IS for plain property increments reference counter,
so it can't be 1 */
if (ce && !ce->create_object && !result_may_be_separated(ssa, ssa_op)) {
tmp &= ~MAY_BE_RC1;
uint32_t tmp = 0;
ce = NULL;
if (opline->op1_type != IS_UNUSED
&& (t1 & (MAY_BE_ANY | MAY_BE_UNDEF) & ~MAY_BE_OBJECT)) {
tmp |= MAY_BE_NULL;
}
if (opline->op1_type == IS_UNUSED || (t1 & MAY_BE_OBJECT)) {
zend_property_info *prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op);
tmp |= zend_fetch_prop_type(script, prop_info, &ce);
if (opline->result_type == IS_VAR) {
tmp |= MAY_BE_REF | MAY_BE_INDIRECT;
} else if (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || !(t1 & MAY_BE_RC1)) {
zend_class_entry *ce = NULL;

if (opline->op1_type == IS_UNUSED) {
ce = op_array->scope;
} else if (ssa_op->op1_use >= 0 && !ssa->var_info[ssa_op->op1_use].is_instanceof) {
ce = ssa->var_info[ssa_op->op1_use].ce;
}
} else {
if (ce && !ce->create_object && !ce->__get && !result_may_be_separated(ssa, ssa_op)) {
tmp &= ~MAY_BE_RC1;
if (prop_info) {
/* FETCH_OBJ_R/IS for plain property increments reference counter,
so it can't be 1 */
if (ce && !ce->create_object && !result_may_be_separated(ssa, ssa_op)) {
tmp &= ~MAY_BE_RC1;
}
} else {
if (ce && !ce->create_object && !ce->__get && !result_may_be_separated(ssa, ssa_op)) {
tmp &= ~MAY_BE_RC1;
}
}
if (opline->opcode == ZEND_FETCH_OBJ_IS) {
/* IS check may return null for uninitialized typed property. */
tmp |= MAY_BE_NULL;
}
}
if (opline->opcode == ZEND_FETCH_OBJ_IS) {
/* IS check may return null for uninitialized typed property. */
tmp |= MAY_BE_NULL;
}
}
UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
Expand Down
163 changes: 20 additions & 143 deletions ext/opcache/tests/opt/inference_001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,157 +7,34 @@ opcache.optimization_level=-1
--FILE--
<?php
function test() {
$j = 0;
for ($i = 0; $i = 10; $i++) {
$e;
$a = $a;
$obj->$e;
$i += $a;
$j++;
$e;
$a == $Z + $a .= $i+= $a;
$j++;
$e;
$a == $Z + $j++;
$e;
$a == $Z + $a = $a + $b = $i += $a;
for ($i = 0; $i < 2; $i++) {
$obj->x;
$obj = new stdClass;
$obj->prop1 = set_error_handler(function () {
$$GLOBALS['a'] = null;
});
$obj->$a .= $i += $a;
$obj = new stdClass;
$obj->prop1 = $j++;
$e;
$a == $Z + $a = $a + $j++;
$e;
$a == $Z + $a = $a + $b = $aa = $a;
}
}
test();

class Test {
public int $x = 1;
}

function test2() {
for ($i = 0; $i < 2; $i++) {
$obj->x;
$obj = new Test;
}
}
test2();
?>
DONE
--EXPECTF--
Warning: Undefined variable $a in %sinference_001.php on line 6

Warning: Undefined variable $obj in %sinference_001.php on line 7

Warning: Undefined variable $e in %sinference_001.php on line 7

Warning: Attempt to read property "" on null in %sinference_001.php on line 7

Warning: Undefined variable $Z in %sinference_001.php on line 11

Warning: Undefined variable $Z in %sinference_001.php on line 14

Warning: Undefined variable $Z in %sinference_001.php on line 16

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19
Warning: Undefined variable $obj in %s on line %d

Warning: Array to string conversion in %sinference_001.php on line 19
Warning: Attempt to read property "x" on null in %s on line %d

Warning: Array to string conversion in %sinference_001.php on line 19
Warning: Undefined property: stdClass::$x in %s on line %d

Warning: Array to string conversion in %sinference_001.php on line 19
Warning: Undefined variable $obj in %s on line %d

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Warning: Array to string conversion in %sinference_001.php on line 19

Fatal error: Uncaught TypeError: Unsupported operand types: null + string in %sinference_001.php:11
Stack trace:
#0 %sinference_001.php(30): test()
#1 {main}
thrown in %sinference_001.php on line 11
Warning: Attempt to read property "x" on null in %s on line %d
DONE

0 comments on commit c72e962

Please sign in to comment.