Skip to content

Commit 7a27ef4

Browse files
committed
Elide return type check for directly provable instances of $this at compile time
1 parent dd69c23 commit 7a27ef4

9 files changed

Lines changed: 105 additions & 66 deletions

Zend/tests/return_types/025_2.phpt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Return type of self is allowed in closure but $this return value must be checked as closure might not be bound to a class
3+
--FILE--
4+
<?php
5+
6+
$c = function(): self { return $this; };
7+
try {
8+
var_dump($c());
9+
} catch (Throwable $e) {
10+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
11+
}
12+
?>
13+
--EXPECT--
14+
Error: Using $this when not in object context

Zend/zend_compile.c

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2642,6 +2642,58 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr, uint32_t t
26422642
}
26432643
/* }}} */
26442644

2645+
static bool zend_is_this_instance_of_name(const zend_string *type_name)
2646+
{
2647+
if (zend_string_equals_ci(CG(active_class_entry)->name, type_name)) {
2648+
return true;
2649+
}
2650+
if (zend_string_equals_ci(type_name, ZSTR_KNOWN(ZEND_STR_SELF))) {
2651+
return true;
2652+
}
2653+
if (zend_string_equals_ci(type_name, ZSTR_KNOWN(ZEND_STR_PARENT))) {
2654+
return true;
2655+
}
2656+
2657+
ZEND_ASSERT((CG(active_class_entry)->ce_flags & ZEND_ACC_LINKED) == 0);
2658+
if (CG(active_class_entry)->num_interfaces) {
2659+
for (uint32_t i = 0; i < CG(active_class_entry)->num_interfaces; i++) {
2660+
if (zend_string_equals_ci(CG(active_class_entry)->interface_names[i].lc_name, type_name)) {
2661+
return true;
2662+
}
2663+
}
2664+
}
2665+
const zend_string *parent_name = CG(active_class_entry)->parent_name;
2666+
if (parent_name && zend_string_equals_ci(parent_name, type_name)) {
2667+
return true;
2668+
}
2669+
2670+
return false;
2671+
}
2672+
2673+
static bool zend_is_this_valid_for_return_type(zend_type type)
2674+
{
2675+
/* Closures can be bound to a class scope, however it might not and this must type error */
2676+
if (CG(active_op_array)->fn_flags & ZEND_ACC_CLOSURE) {
2677+
return false;
2678+
}
2679+
2680+
if (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_OBJECT|MAY_BE_STATIC)) {
2681+
return true;
2682+
}
2683+
2684+
const zend_type *single_type;
2685+
ZEND_TYPE_FOREACH(type, single_type) {
2686+
if (ZEND_TYPE_HAS_NAME(*single_type)) {
2687+
const zend_string *name = ZEND_TYPE_NAME(*single_type);
2688+
if (zend_is_this_instance_of_name(name)) {
2689+
return true;
2690+
}
2691+
}
2692+
} ZEND_TYPE_FOREACH_END();
2693+
2694+
return false;
2695+
}
2696+
26452697
static void zend_emit_return_type_check(
26462698
znode *expr, const zend_arg_info *return_info, bool implicit) /* {{{ */
26472699
{
@@ -2696,16 +2748,16 @@ static void zend_emit_return_type_check(
26962748
/* we don't need run-time check */
26972749
return;
26982750
}
2699-
2751+
27002752
/* If return type contains static and we are returning $this
27012753
* (determined by checking if the previous opcode is ZEND_FETCH_THIS)
27022754
* then we don't need to check the return type */
2703-
if (expr && ZEND_TYPE_CONTAINS_CODE(type, IS_STATIC)) {
2704-
const zend_op_array *op_array = CG(active_op_array);
2705-
zend_op previous = op_array->opcodes[op_array->last-1];
2706-
if (previous.opcode == ZEND_FETCH_THIS) {
2707-
return;
2708-
}
2755+
const zend_op_array *op_array = CG(active_op_array);
2756+
if (expr && op_array->last >= 1
2757+
&& op_array->opcodes[op_array->last-1].opcode == ZEND_FETCH_THIS
2758+
&& zend_is_this_valid_for_return_type(type)) {
2759+
ZEND_ASSERT((expr->op_type & (IS_VAR|IS_TMP_VAR)) && expr->u.op.var == op_array->opcodes[op_array->last-1].result.var);
2760+
return;
27092761
}
27102762

27112763
opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);

ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,14 @@ $_main:
3131
0002 RETURN int(1)
3232

3333
C::foo:
34-
; (lines=5, args=0, vars=0, tmps=1)
34+
; (lines=4, args=0, vars=0, tmps=1)
3535
; (before optimizer)
3636
; %s:7-9
3737
; return [] RANGE[0..0]
3838
0000 T0 = FETCH_THIS
39-
0001 VERIFY_RETURN_TYPE T0
40-
0002 RETURN T0
41-
0003 VERIFY_RETURN_TYPE
42-
0004 RETURN null
43-
LIVE RANGES:
44-
0: 0001 - 0002 (tmp/var)
39+
0001 RETURN T0
40+
0002 VERIFY_RETURN_TYPE
41+
0003 RETURN null
4542

4643
$_main:
4744
; (lines=3, args=0, vars=0, tmps=0)
@@ -52,11 +49,8 @@ $_main:
5249
0002 RETURN int(1)
5350

5451
C::foo:
55-
; (lines=3, args=0, vars=0, tmps=1)
52+
; (lines=2, args=0, vars=0, tmps=1)
5653
; (after optimizer)
5754
; %s:7-9
5855
0000 T0 = FETCH_THIS
59-
0001 VERIFY_RETURN_TYPE T0
60-
0002 RETURN T0
61-
LIVE RANGES:
62-
0: 0001 - 0002 (tmp/var)
56+
0001 RETURN T0

ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,14 @@ $_main:
2929
0001 RETURN int(1)
3030

3131
C::foo:
32-
; (lines=5, args=0, vars=0, tmps=1)
32+
; (lines=4, args=0, vars=0, tmps=1)
3333
; (before optimizer)
3434
; %s:6-8
3535
; return [] RANGE[0..0]
3636
0000 T0 = FETCH_THIS
37-
0001 VERIFY_RETURN_TYPE T0
38-
0002 RETURN T0
39-
0003 VERIFY_RETURN_TYPE
40-
0004 RETURN null
41-
LIVE RANGES:
42-
0: 0001 - 0002 (tmp/var)
37+
0001 RETURN T0
38+
0002 VERIFY_RETURN_TYPE
39+
0003 RETURN null
4340

4441
$_main:
4542
; (lines=2, args=0, vars=0, tmps=0)

ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,14 @@ $_main:
2828
0000 RETURN int(1)
2929

3030
C2::foo:
31-
; (lines=5, args=0, vars=0, tmps=1)
31+
; (lines=4, args=0, vars=0, tmps=1)
3232
; (before optimizer)
3333
; %s:6-8
3434
; return [] RANGE[0..0]
3535
0000 T0 = FETCH_THIS
36-
0001 VERIFY_RETURN_TYPE T0
37-
0002 RETURN T0
38-
0003 VERIFY_RETURN_TYPE
39-
0004 RETURN null
40-
LIVE RANGES:
41-
0: 0001 - 0002 (tmp/var)
36+
0001 RETURN T0
37+
0002 VERIFY_RETURN_TYPE
38+
0003 RETURN null
4239

4340
$_main:
4441
; (lines=1, args=0, vars=0, tmps=0)

ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,14 @@ $_main:
2828
0000 RETURN int(1)
2929

3030
C2::foo:
31-
; (lines=5, args=0, vars=0, tmps=1)
31+
; (lines=4, args=0, vars=0, tmps=1)
3232
; (before optimizer)
3333
; %s:6-8
3434
; return [] RANGE[0..0]
3535
0000 T0 = FETCH_THIS
36-
0001 VERIFY_RETURN_TYPE T0
37-
0002 RETURN T0
38-
0003 VERIFY_RETURN_TYPE
39-
0004 RETURN null
40-
LIVE RANGES:
41-
0: 0001 - 0002 (tmp/var)
36+
0001 RETURN T0
37+
0002 VERIFY_RETURN_TYPE
38+
0003 RETURN null
4239

4340
$_main:
4441
; (lines=1, args=0, vars=0, tmps=0)

ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,14 @@ $_main:
2626
0000 RETURN int(1)
2727

2828
C::foo:
29-
; (lines=5, args=0, vars=0, tmps=1)
29+
; (lines=4, args=0, vars=0, tmps=1)
3030
; (before optimizer)
3131
; %s:4-6
3232
; return [] RANGE[0..0]
3333
0000 T0 = FETCH_THIS
34-
0001 VERIFY_RETURN_TYPE T0
35-
0002 RETURN T0
36-
0003 VERIFY_RETURN_TYPE
37-
0004 RETURN null
38-
LIVE RANGES:
39-
0: 0001 - 0002 (tmp/var)
34+
0001 RETURN T0
35+
0002 VERIFY_RETURN_TYPE
36+
0003 RETURN null
4037

4138
$_main:
4239
; (lines=1, args=0, vars=0, tmps=0)

ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,14 @@ $_main:
2626
0000 RETURN int(1)
2727

2828
C::foo:
29-
; (lines=5, args=0, vars=0, tmps=1)
29+
; (lines=4, args=0, vars=0, tmps=1)
3030
; (before optimizer)
3131
; %s:4-6
3232
; return [] RANGE[0..0]
3333
0000 T0 = FETCH_THIS
34-
0001 VERIFY_RETURN_TYPE T0
35-
0002 RETURN T0
36-
0003 VERIFY_RETURN_TYPE
37-
0004 RETURN null
38-
LIVE RANGES:
39-
0: 0001 - 0002 (tmp/var)
34+
0001 RETURN T0
35+
0002 VERIFY_RETURN_TYPE
36+
0003 RETURN null
4037

4138
$_main:
4239
; (lines=1, args=0, vars=0, tmps=0)

ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,14 @@ $_main:
2626
0000 RETURN int(1)
2727

2828
T::foo:
29-
; (lines=5, args=0, vars=0, tmps=1)
29+
; (lines=4, args=0, vars=0, tmps=1)
3030
; (before optimizer)
3131
; %s:4-6
3232
; return [] RANGE[0..0]
3333
0000 T0 = FETCH_THIS
34-
0001 VERIFY_RETURN_TYPE T0
35-
0002 RETURN T0
36-
0003 VERIFY_RETURN_TYPE
37-
0004 RETURN null
38-
LIVE RANGES:
39-
0: 0001 - 0002 (tmp/var)
34+
0001 RETURN T0
35+
0002 VERIFY_RETURN_TYPE
36+
0003 RETURN null
4037

4138
$_main:
4239
; (lines=1, args=0, vars=0, tmps=0)
@@ -45,11 +42,8 @@ $_main:
4542
0000 RETURN int(1)
4643

4744
T::foo:
48-
; (lines=3, args=0, vars=0, tmps=1)
45+
; (lines=2, args=0, vars=0, tmps=1)
4946
; (after optimizer)
5047
; %s:4-6
5148
0000 T0 = FETCH_THIS
52-
0001 VERIFY_RETURN_TYPE T0
53-
0002 RETURN T0
54-
LIVE RANGES:
55-
0: 0001 - 0002 (tmp/var)
49+
0001 RETURN T0

0 commit comments

Comments
 (0)