Skip to content

Commit c72e962

Browse files
committed
Improve type narrowing fix
We need to explicitly model the null return type for property accesses on non-objects.
1 parent 323f3c6 commit c72e962

File tree

2 files changed

+52
-173
lines changed

2 files changed

+52
-173
lines changed

Zend/Optimizer/zend_inference.c

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,11 +1764,6 @@ static uint32_t get_ssa_alias_types(zend_ssa_alias_kind alias) {
17641764
(ssa_var_info[__var].type & MAY_BE_REF) \
17651765
== (__type & MAY_BE_REF)); \
17661766
if (ssa_var_info[__var].type & ~__type) { \
1767-
if ((ssa_var_info[__var].type & ~__type & \
1768-
~(MAY_BE_RC1|MAY_BE_RCN)) == 0) { \
1769-
ssa_var_info[__var].type |= __type; \
1770-
break; \
1771-
} \
17721767
emit_type_narrowing_warning(op_array, ssa, __var); \
17731768
return FAILURE; \
17741769
} \
@@ -3375,33 +3370,40 @@ static zend_always_inline int _zend_update_type_info(
33753370
case ZEND_FETCH_OBJ_UNSET:
33763371
case ZEND_FETCH_OBJ_FUNC_ARG:
33773372
if (ssa_op->result_def >= 0) {
3378-
zend_property_info *prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op);
3379-
3380-
tmp = zend_fetch_prop_type(script, prop_info, &ce);
3381-
if (opline->result_type == IS_VAR) {
3382-
tmp |= MAY_BE_REF | MAY_BE_INDIRECT;
3383-
} else if (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || !(t1 & MAY_BE_RC1)) {
3384-
zend_class_entry *ce = NULL;
3385-
3386-
if (opline->op1_type == IS_UNUSED) {
3387-
ce = op_array->scope;
3388-
} else if (ssa_op->op1_use >= 0 && !ssa->var_info[ssa_op->op1_use].is_instanceof) {
3389-
ce = ssa->var_info[ssa_op->op1_use].ce;
3390-
}
3391-
if (prop_info) {
3392-
/* FETCH_OBJ_R/IS for plain property increments reference counter,
3393-
so it can't be 1 */
3394-
if (ce && !ce->create_object && !result_may_be_separated(ssa, ssa_op)) {
3395-
tmp &= ~MAY_BE_RC1;
3373+
uint32_t tmp = 0;
3374+
ce = NULL;
3375+
if (opline->op1_type != IS_UNUSED
3376+
&& (t1 & (MAY_BE_ANY | MAY_BE_UNDEF) & ~MAY_BE_OBJECT)) {
3377+
tmp |= MAY_BE_NULL;
3378+
}
3379+
if (opline->op1_type == IS_UNUSED || (t1 & MAY_BE_OBJECT)) {
3380+
zend_property_info *prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op);
3381+
tmp |= zend_fetch_prop_type(script, prop_info, &ce);
3382+
if (opline->result_type == IS_VAR) {
3383+
tmp |= MAY_BE_REF | MAY_BE_INDIRECT;
3384+
} else if (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || !(t1 & MAY_BE_RC1)) {
3385+
zend_class_entry *ce = NULL;
3386+
3387+
if (opline->op1_type == IS_UNUSED) {
3388+
ce = op_array->scope;
3389+
} else if (ssa_op->op1_use >= 0 && !ssa->var_info[ssa_op->op1_use].is_instanceof) {
3390+
ce = ssa->var_info[ssa_op->op1_use].ce;
33963391
}
3397-
} else {
3398-
if (ce && !ce->create_object && !ce->__get && !result_may_be_separated(ssa, ssa_op)) {
3399-
tmp &= ~MAY_BE_RC1;
3392+
if (prop_info) {
3393+
/* FETCH_OBJ_R/IS for plain property increments reference counter,
3394+
so it can't be 1 */
3395+
if (ce && !ce->create_object && !result_may_be_separated(ssa, ssa_op)) {
3396+
tmp &= ~MAY_BE_RC1;
3397+
}
3398+
} else {
3399+
if (ce && !ce->create_object && !ce->__get && !result_may_be_separated(ssa, ssa_op)) {
3400+
tmp &= ~MAY_BE_RC1;
3401+
}
3402+
}
3403+
if (opline->opcode == ZEND_FETCH_OBJ_IS) {
3404+
/* IS check may return null for uninitialized typed property. */
3405+
tmp |= MAY_BE_NULL;
34003406
}
3401-
}
3402-
if (opline->opcode == ZEND_FETCH_OBJ_IS) {
3403-
/* IS check may return null for uninitialized typed property. */
3404-
tmp |= MAY_BE_NULL;
34053407
}
34063408
}
34073409
UPDATE_SSA_TYPE(tmp, ssa_op->result_def);

ext/opcache/tests/opt/inference_001.phpt

Lines changed: 20 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -7,157 +7,34 @@ opcache.optimization_level=-1
77
--FILE--
88
<?php
99
function test() {
10-
$j = 0;
11-
for ($i = 0; $i = 10; $i++) {
12-
$e;
13-
$a = $a;
14-
$obj->$e;
15-
$i += $a;
16-
$j++;
17-
$e;
18-
$a == $Z + $a .= $i+= $a;
19-
$j++;
20-
$e;
21-
$a == $Z + $j++;
22-
$e;
23-
$a == $Z + $a = $a + $b = $i += $a;
10+
for ($i = 0; $i < 2; $i++) {
11+
$obj->x;
2412
$obj = new stdClass;
25-
$obj->prop1 = set_error_handler(function () {
26-
$$GLOBALS['a'] = null;
27-
});
28-
$obj->$a .= $i += $a;
29-
$obj = new stdClass;
30-
$obj->prop1 = $j++;
31-
$e;
32-
$a == $Z + $a = $a + $j++;
33-
$e;
34-
$a == $Z + $a = $a + $b = $aa = $a;
3513
}
3614
}
3715
test();
16+
17+
class Test {
18+
public int $x = 1;
19+
}
20+
21+
function test2() {
22+
for ($i = 0; $i < 2; $i++) {
23+
$obj->x;
24+
$obj = new Test;
25+
}
26+
}
27+
test2();
3828
?>
3929
DONE
4030
--EXPECTF--
41-
Warning: Undefined variable $a in %sinference_001.php on line 6
42-
43-
Warning: Undefined variable $obj in %sinference_001.php on line 7
44-
45-
Warning: Undefined variable $e in %sinference_001.php on line 7
46-
47-
Warning: Attempt to read property "" on null in %sinference_001.php on line 7
48-
49-
Warning: Undefined variable $Z in %sinference_001.php on line 11
50-
51-
Warning: Undefined variable $Z in %sinference_001.php on line 14
52-
53-
Warning: Undefined variable $Z in %sinference_001.php on line 16
54-
55-
Warning: Array to string conversion in %sinference_001.php on line 19
56-
57-
Warning: Array to string conversion in %sinference_001.php on line 19
58-
59-
Warning: Array to string conversion in %sinference_001.php on line 19
60-
61-
Warning: Array to string conversion in %sinference_001.php on line 19
62-
63-
Warning: Array to string conversion in %sinference_001.php on line 19
64-
65-
Warning: Array to string conversion in %sinference_001.php on line 19
66-
67-
Warning: Array to string conversion in %sinference_001.php on line 19
68-
69-
Warning: Array to string conversion in %sinference_001.php on line 19
70-
71-
Warning: Array to string conversion in %sinference_001.php on line 19
72-
73-
Warning: Array to string conversion in %sinference_001.php on line 19
74-
75-
Warning: Array to string conversion in %sinference_001.php on line 19
76-
77-
Warning: Array to string conversion in %sinference_001.php on line 19
78-
79-
Warning: Array to string conversion in %sinference_001.php on line 19
80-
81-
Warning: Array to string conversion in %sinference_001.php on line 19
82-
83-
Warning: Array to string conversion in %sinference_001.php on line 19
84-
85-
Warning: Array to string conversion in %sinference_001.php on line 19
86-
87-
Warning: Array to string conversion in %sinference_001.php on line 19
88-
89-
Warning: Array to string conversion in %sinference_001.php on line 19
90-
91-
Warning: Array to string conversion in %sinference_001.php on line 19
31+
Warning: Undefined variable $obj in %s on line %d
9232

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

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

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

99-
Warning: Array to string conversion in %sinference_001.php on line 19
100-
101-
Warning: Array to string conversion in %sinference_001.php on line 19
102-
103-
Warning: Array to string conversion in %sinference_001.php on line 19
104-
105-
Warning: Array to string conversion in %sinference_001.php on line 19
106-
107-
Warning: Array to string conversion in %sinference_001.php on line 19
108-
109-
Warning: Array to string conversion in %sinference_001.php on line 19
110-
111-
Warning: Array to string conversion in %sinference_001.php on line 19
112-
113-
Warning: Array to string conversion in %sinference_001.php on line 19
114-
115-
Warning: Array to string conversion in %sinference_001.php on line 19
116-
117-
Warning: Array to string conversion in %sinference_001.php on line 19
118-
119-
Warning: Array to string conversion in %sinference_001.php on line 19
120-
121-
Warning: Array to string conversion in %sinference_001.php on line 19
122-
123-
Warning: Array to string conversion in %sinference_001.php on line 19
124-
125-
Warning: Array to string conversion in %sinference_001.php on line 19
126-
127-
Warning: Array to string conversion in %sinference_001.php on line 19
128-
129-
Warning: Array to string conversion in %sinference_001.php on line 19
130-
131-
Warning: Array to string conversion in %sinference_001.php on line 19
132-
133-
Warning: Array to string conversion in %sinference_001.php on line 19
134-
135-
Warning: Array to string conversion in %sinference_001.php on line 19
136-
137-
Warning: Array to string conversion in %sinference_001.php on line 19
138-
139-
Warning: Array to string conversion in %sinference_001.php on line 19
140-
141-
Warning: Array to string conversion in %sinference_001.php on line 19
142-
143-
Warning: Array to string conversion in %sinference_001.php on line 19
144-
145-
Warning: Array to string conversion in %sinference_001.php on line 19
146-
147-
Warning: Array to string conversion in %sinference_001.php on line 19
148-
149-
Warning: Array to string conversion in %sinference_001.php on line 19
150-
151-
Warning: Array to string conversion in %sinference_001.php on line 19
152-
153-
Warning: Array to string conversion in %sinference_001.php on line 19
154-
155-
Warning: Array to string conversion in %sinference_001.php on line 19
156-
157-
Warning: Array to string conversion in %sinference_001.php on line 19
158-
159-
Fatal error: Uncaught TypeError: Unsupported operand types: null + string in %sinference_001.php:11
160-
Stack trace:
161-
#0 %sinference_001.php(30): test()
162-
#1 {main}
163-
thrown in %sinference_001.php on line 11
39+
Warning: Attempt to read property "x" on null in %s on line %d
40+
DONE

0 commit comments

Comments
 (0)