From ce5322027268e0ffd34fd91570f399af4d1cbfce Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 5 May 2026 14:19:19 +0100 Subject: [PATCH 1/7] Add tests prior to optimization --- ...ace_verify_return_type_for_this_class.phpt | 62 ++++++++++++++++++ ...ace_verify_return_type_for_this_class.phpt | 59 +++++++++++++++++ ...ace_verify_return_type_for_this_class.phpt | 61 +++++++++++++++++ ...nce_verify_return_type_for_this_class.phpt | 65 +++++++++++++++++++ ...cit_verify_return_type_for_this_class.phpt | 54 +++++++++++++++ ...ent_verify_return_type_for_this_class.phpt | 54 +++++++++++++++ ...cit_verify_return_type_for_this_class.phpt | 52 +++++++++++++++ ...elf_verify_return_type_for_this_class.phpt | 52 +++++++++++++++ ...elf_verify_return_type_for_this_trait.phpt | 55 ++++++++++++++++ ...tic_verify_return_type_for_this_class.phpt | 55 ++++++++++++++++ ...tic_verify_return_type_for_this_trait.phpt | 55 ++++++++++++++++ 11 files changed, 624 insertions(+) create mode 100644 ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/inherited_interface_verify_return_type_for_this_class.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/inherited_interface_via_class_inheritance_verify_return_type_for_this_class.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt diff --git a/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..ae8ea84fea7f --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt @@ -0,0 +1,62 @@ +--TEST-- +Return type check elision for direct interface return type and $this in class method when interface extends another one +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=3, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-13 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("i2") +0001 DECLARE_CLASS string("c") +0002 RETURN int(1) + +C::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:7-9 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=3, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-13 +0000 DECLARE_CLASS string("i2") +0001 DECLARE_CLASS string("c") +0002 RETURN int(1) + +C::foo: + ; (lines=3, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:7-9 +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) diff --git a/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..64f56b85b0b5 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt @@ -0,0 +1,59 @@ +--TEST-- +Return type check elision for direct interface return type and $this in class method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=2, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-12 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("c") +0001 RETURN int(1) + +C::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:6-8 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-12 +0000 DECLARE_CLASS string("c") +0001 RETURN int(1) + +C::foo: + ; (lines=3, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:6-8 +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) diff --git a/ext/opcache/tests/opt/verify_return_type/inherited_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/inherited_interface_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..f585af5919cc --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/inherited_interface_verify_return_type_for_this_class.phpt @@ -0,0 +1,61 @@ +--TEST-- +Return type check elision for inherited interface return type and $this in class method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=3, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-12 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("i2") +0001 DECLARE_CLASS string("c") +0002 RETURN int(1) + +C::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:6-8 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=3, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-12 +0000 DECLARE_CLASS string("i2") +0001 DECLARE_CLASS string("c") +0002 RETURN int(1) + +C::foo: + ; (lines=3, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:6-8 +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) diff --git a/ext/opcache/tests/opt/verify_return_type/inherited_interface_via_class_inheritance_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/inherited_interface_via_class_inheritance_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..49c0b922f47b --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/inherited_interface_via_class_inheritance_verify_return_type_for_this_class.phpt @@ -0,0 +1,65 @@ +--TEST-- +Return type check elision for inherited interface via class extension return type and $this in class method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=4, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-14 + ; return [] RANGE[0..0] +0000 DECLARE_CLASS string("i2") +0001 DECLARE_CLASS string("c1") +0002 DECLARE_CLASS_DELAYED string("c2") string("c1") +0003 RETURN int(1) + +C2::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:8-10 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=4, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-14 +0000 DECLARE_CLASS string("i2") +0001 DECLARE_CLASS string("c1") +0002 DECLARE_CLASS_DELAYED string("c2") string("c1") +0003 RETURN int(1) + +C2::foo: + ; (lines=3, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:8-10 +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) diff --git a/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..83df0bde2de5 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt @@ -0,0 +1,54 @@ +--TEST-- +Return type check elision for explicit parent return type and $this in class method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-12 + ; return [] RANGE[0..0] +0000 RETURN int(1) + +C2::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:6-8 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-12 +0000 RETURN int(1) + +C2::foo: + ; (lines=2, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:6-8 +0000 T0 = FETCH_THIS +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..e232df9b5803 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt @@ -0,0 +1,54 @@ +--TEST-- +Return type check elision for parent return type and $this in class method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-12 + ; return [] RANGE[0..0] +0000 RETURN int(1) + +C2::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:6-8 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-12 +0000 RETURN int(1) + +C2::foo: + ; (lines=2, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:6-8 +0000 T0 = FETCH_THIS +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..289b8c66e0d1 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt @@ -0,0 +1,52 @@ +--TEST-- +Return type check elision for explicit self return type and $this in class method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-10 + ; return [] RANGE[0..0] +0000 RETURN int(1) + +C::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:4-6 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-10 +0000 RETURN int(1) + +C::foo: + ; (lines=2, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:4-6 +0000 T0 = FETCH_THIS +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..daddf47f3327 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt @@ -0,0 +1,52 @@ +--TEST-- +Return type check elision for self return type and $this in class method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-10 + ; return [] RANGE[0..0] +0000 RETURN int(1) + +C::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:4-6 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-10 +0000 RETURN int(1) + +C::foo: + ; (lines=2, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:4-6 +0000 T0 = FETCH_THIS +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt new file mode 100644 index 000000000000..db6974f66fe3 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt @@ -0,0 +1,55 @@ +--TEST-- +Return type check elision for self return type and $this in trait method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-10 + ; return [] RANGE[0..0] +0000 RETURN int(1) + +T::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:4-6 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-10 +0000 RETURN int(1) + +T::foo: + ; (lines=3, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:4-6 +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) diff --git a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..7d6b959cfbb6 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt @@ -0,0 +1,55 @@ +--TEST-- +Return type check elision for static return type and $this in class method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-10 + ; return [] RANGE[0..0] +0000 RETURN int(1) + +C::returnStatic: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:4-6 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-10 +0000 RETURN int(1) + +C::returnStatic: + ; (lines=3, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:4-6 +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) diff --git a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt new file mode 100644 index 000000000000..f74d80349b03 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt @@ -0,0 +1,55 @@ +--TEST-- +Return type check elision for static return type and $this in trait method +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-10 + ; return [] RANGE[0..0] +0000 RETURN int(1) + +T::returnStatic: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:4-6 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=1, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-10 +0000 RETURN int(1) + +T::returnStatic: + ; (lines=3, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:4-6 +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) From f6d7bb819c71d3f4125cf7dd9f871cc4c5221b89 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 4 May 2026 21:14:46 +0100 Subject: [PATCH 2/7] Do not emit ZEND_VERIFY_RETURN_TYPE if we return $this and return value is static --- Zend/zend_compile.c | 11 +++++++++++ ...atic_verify_return_type_for_this_class.phpt | 18 ++++++------------ ...atic_verify_return_type_for_this_trait.phpt | 18 ++++++------------ 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7e9f7ceac8db..963bacad204a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2696,6 +2696,17 @@ static void zend_emit_return_type_check( /* we don't need run-time check */ return; } + + /* If return type contains static and we are returning $this + * (determined by checking if the previous opcode is ZEND_FETCH_THIS) + * then we don't need to check the return type */ + if (expr && ZEND_TYPE_CONTAINS_CODE(type, IS_STATIC)) { + const zend_op_array *op_array = CG(active_op_array); + zend_op previous = op_array->opcodes[op_array->last-1]; + if (previous.opcode == ZEND_FETCH_THIS) { + return; + } + } opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL); if (expr && expr->op_type == IS_CONST) { diff --git a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt index 7d6b959cfbb6..2b87b7cbc734 100644 --- a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt @@ -26,17 +26,14 @@ $_main: 0000 RETURN int(1) C::returnStatic: - ; (lines=5, args=0, vars=0, tmps=1) + ; (lines=4, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:4-6 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -0003 VERIFY_RETURN_TYPE -0004 RETURN null -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 +0002 VERIFY_RETURN_TYPE +0003 RETURN null $_main: ; (lines=1, args=0, vars=0, tmps=0) @@ -45,11 +42,8 @@ $_main: 0000 RETURN int(1) C::returnStatic: - ; (lines=3, args=0, vars=0, tmps=1) + ; (lines=2, args=0, vars=0, tmps=1) ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt index f74d80349b03..fc8f852ba707 100644 --- a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt +++ b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt @@ -26,17 +26,14 @@ $_main: 0000 RETURN int(1) T::returnStatic: - ; (lines=5, args=0, vars=0, tmps=1) + ; (lines=4, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:4-6 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -0003 VERIFY_RETURN_TYPE -0004 RETURN null -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 +0002 VERIFY_RETURN_TYPE +0003 RETURN null $_main: ; (lines=1, args=0, vars=0, tmps=0) @@ -45,11 +42,8 @@ $_main: 0000 RETURN int(1) T::returnStatic: - ; (lines=3, args=0, vars=0, tmps=1) + ; (lines=2, args=0, vars=0, tmps=1) ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 From 21ad1933a4279a1e9243887199b04797c335602e Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 5 May 2026 15:08:28 +0100 Subject: [PATCH 3/7] Optimizer: Implement basic unlinked interface traversal in safe_instanceof() --- Zend/Optimizer/dfa_pass.c | 46 ++++++++++++++++--- ...ace_verify_return_type_for_this_class.phpt | 7 +-- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index 77dc322fbdec..3b003f725068 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -252,15 +252,49 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op free_alloca(shiftlist, use_heap); } -static bool safe_instanceof(const zend_class_entry *ce1, const zend_class_entry *ce2) { +static bool safe_instanceof( + const zend_class_entry *ce1, + const zend_class_entry *ce2, + const zend_script *script, + const zend_op_array *op_array +) { if (ce1 == ce2) { return true; } - if (!(ce1->ce_flags & ZEND_ACC_LINKED)) { - /* This case could be generalized, similarly to unlinked_instanceof */ - return false; + if (ce1->ce_flags & ZEND_ACC_LINKED) { + return instanceof_function(ce1, ce2); + } + + /* TODO Handle unlinked parents ike in unlinked_instanceof()? */ + + if (ce1->num_interfaces) { + uint32_t i; + if (ce1->ce_flags & ZEND_ACC_RESOLVED_INTERFACES) { + /* Unlike the normal instanceof_function(), we have to perform a recursive + * check here, as the parent interfaces might not have been fully copied yet. */ + for (i = 0; i < ce1->num_interfaces; i++) { + if (safe_instanceof(ce1->interfaces[i], ce2, script, op_array)) { + return true; + } + } + } else { + for (i = 0; i < ce1->num_interfaces; i++) { + const zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, ce1->interface_names[i].lc_name); + if (!ce) { + continue; + } + /* Avoid recursing if class implements itself. */ + if (ce == ce1) { + continue; + } + if (safe_instanceof(ce, ce2, script, op_array)) { + return true; + } + } + } } - return instanceof_function(ce1, ce2); + + return false; } static inline bool can_elide_list_type( @@ -280,7 +314,7 @@ static inline bool can_elide_list_type( zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type)); const zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname); zend_string_release(lcname); - bool result = ce && safe_instanceof(use_info->ce, ce); + bool result = ce && safe_instanceof(use_info->ce, ce, script, op_array); if (result == !is_intersection) { return result; } diff --git a/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt index 64f56b85b0b5..46737c9fc310 100644 --- a/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt @@ -49,11 +49,8 @@ $_main: 0001 RETURN int(1) C::foo: - ; (lines=3, args=0, vars=0, tmps=1) + ; (lines=2, args=0, vars=0, tmps=1) ; (after optimizer) ; %s:6-8 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 From 15c01db5afdc79da79d2909e2c8a26f423a4a5bd Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 5 May 2026 00:39:24 +0100 Subject: [PATCH 4/7] Elide return type check for directly provable instances of $this at compile time --- Zend/tests/return_types/025_2.phpt | 14 ++++ Zend/zend_compile.c | 66 +++++++++++++++++-- ...ace_verify_return_type_for_this_class.phpt | 18 ++--- ...ace_verify_return_type_for_this_class.phpt | 11 ++-- ...cit_verify_return_type_for_this_class.phpt | 11 ++-- ...ent_verify_return_type_for_this_class.phpt | 11 ++-- ...cit_verify_return_type_for_this_class.phpt | 11 ++-- ...elf_verify_return_type_for_this_class.phpt | 11 ++-- ...elf_verify_return_type_for_this_trait.phpt | 18 ++--- 9 files changed, 105 insertions(+), 66 deletions(-) create mode 100644 Zend/tests/return_types/025_2.phpt diff --git a/Zend/tests/return_types/025_2.phpt b/Zend/tests/return_types/025_2.phpt new file mode 100644 index 000000000000..50683c5c1267 --- /dev/null +++ b/Zend/tests/return_types/025_2.phpt @@ -0,0 +1,14 @@ +--TEST-- +Return type of self is allowed in closure but $this return value must be checked as closure might not be bound to a class +--FILE-- +getMessage(), PHP_EOL; +} +?> +--EXPECT-- +Error: Using $this when not in object context diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 963bacad204a..aa8a928c567c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2642,6 +2642,58 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr, uint32_t t } /* }}} */ +static bool zend_is_this_instance_of_name(const zend_string *type_name) +{ + if (zend_string_equals_ci(CG(active_class_entry)->name, type_name)) { + return true; + } + if (zend_string_equals_ci(type_name, ZSTR_KNOWN(ZEND_STR_SELF))) { + return true; + } + if (zend_string_equals_ci(type_name, ZSTR_KNOWN(ZEND_STR_PARENT))) { + return true; + } + + ZEND_ASSERT((CG(active_class_entry)->ce_flags & ZEND_ACC_LINKED) == 0); + if (CG(active_class_entry)->num_interfaces) { + for (uint32_t i = 0; i < CG(active_class_entry)->num_interfaces; i++) { + if (zend_string_equals_ci(CG(active_class_entry)->interface_names[i].lc_name, type_name)) { + return true; + } + } + } + const zend_string *parent_name = CG(active_class_entry)->parent_name; + if (parent_name && zend_string_equals_ci(parent_name, type_name)) { + return true; + } + + return false; +} + +static bool zend_is_this_valid_for_return_type(zend_type type) +{ + /* Closures can be bound to a class scope, however it might not and this must type error */ + if (CG(active_op_array)->fn_flags & ZEND_ACC_CLOSURE) { + return false; + } + + if (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_OBJECT|MAY_BE_STATIC)) { + return true; + } + + const zend_type *single_type; + ZEND_TYPE_FOREACH(type, single_type) { + if (ZEND_TYPE_HAS_NAME(*single_type)) { + const zend_string *name = ZEND_TYPE_NAME(*single_type); + if (zend_is_this_instance_of_name(name)) { + return true; + } + } + } ZEND_TYPE_FOREACH_END(); + + return false; +} + static void zend_emit_return_type_check( znode *expr, const zend_arg_info *return_info, bool implicit) /* {{{ */ { @@ -2696,16 +2748,16 @@ static void zend_emit_return_type_check( /* we don't need run-time check */ return; } - + /* If return type contains static and we are returning $this * (determined by checking if the previous opcode is ZEND_FETCH_THIS) * then we don't need to check the return type */ - if (expr && ZEND_TYPE_CONTAINS_CODE(type, IS_STATIC)) { - const zend_op_array *op_array = CG(active_op_array); - zend_op previous = op_array->opcodes[op_array->last-1]; - if (previous.opcode == ZEND_FETCH_THIS) { - return; - } + const zend_op_array *op_array = CG(active_op_array); + if (expr && op_array->last >= 1 + && op_array->opcodes[op_array->last-1].opcode == ZEND_FETCH_THIS + && zend_is_this_valid_for_return_type(type)) { + ZEND_ASSERT((expr->op_type & (IS_VAR|IS_TMP_VAR)) && expr->u.op.var == op_array->opcodes[op_array->last-1].result.var); + return; } opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL); diff --git a/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt index ae8ea84fea7f..0ed9d39e6763 100644 --- a/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt @@ -31,17 +31,14 @@ $_main: 0002 RETURN int(1) C::foo: - ; (lines=5, args=0, vars=0, tmps=1) + ; (lines=4, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:7-9 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -0003 VERIFY_RETURN_TYPE -0004 RETURN null -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 +0002 VERIFY_RETURN_TYPE +0003 RETURN null $_main: ; (lines=3, args=0, vars=0, tmps=0) @@ -52,11 +49,8 @@ $_main: 0002 RETURN int(1) C::foo: - ; (lines=3, args=0, vars=0, tmps=1) + ; (lines=2, args=0, vars=0, tmps=1) ; (after optimizer) ; %s:7-9 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt index 46737c9fc310..3f9b73f2feb6 100644 --- a/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt @@ -29,17 +29,14 @@ $_main: 0001 RETURN int(1) C::foo: - ; (lines=5, args=0, vars=0, tmps=1) + ; (lines=4, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:6-8 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -0003 VERIFY_RETURN_TYPE -0004 RETURN null -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 +0002 VERIFY_RETURN_TYPE +0003 RETURN null $_main: ; (lines=2, args=0, vars=0, tmps=0) diff --git a/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt index 83df0bde2de5..a1f5302f7bc2 100644 --- a/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt @@ -28,17 +28,14 @@ $_main: 0000 RETURN int(1) C2::foo: - ; (lines=5, args=0, vars=0, tmps=1) + ; (lines=4, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:6-8 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -0003 VERIFY_RETURN_TYPE -0004 RETURN null -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 +0002 VERIFY_RETURN_TYPE +0003 RETURN null $_main: ; (lines=1, args=0, vars=0, tmps=0) diff --git a/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt index e232df9b5803..2eafbfbb822d 100644 --- a/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt @@ -28,17 +28,14 @@ $_main: 0000 RETURN int(1) C2::foo: - ; (lines=5, args=0, vars=0, tmps=1) + ; (lines=4, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:6-8 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -0003 VERIFY_RETURN_TYPE -0004 RETURN null -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 +0002 VERIFY_RETURN_TYPE +0003 RETURN null $_main: ; (lines=1, args=0, vars=0, tmps=0) diff --git a/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt index 289b8c66e0d1..ef138191ba33 100644 --- a/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt @@ -26,17 +26,14 @@ $_main: 0000 RETURN int(1) C::foo: - ; (lines=5, args=0, vars=0, tmps=1) + ; (lines=4, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:4-6 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -0003 VERIFY_RETURN_TYPE -0004 RETURN null -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 +0002 VERIFY_RETURN_TYPE +0003 RETURN null $_main: ; (lines=1, args=0, vars=0, tmps=0) diff --git a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt index daddf47f3327..b59d919ea49f 100644 --- a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt @@ -26,17 +26,14 @@ $_main: 0000 RETURN int(1) C::foo: - ; (lines=5, args=0, vars=0, tmps=1) + ; (lines=4, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:4-6 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -0003 VERIFY_RETURN_TYPE -0004 RETURN null -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 +0002 VERIFY_RETURN_TYPE +0003 RETURN null $_main: ; (lines=1, args=0, vars=0, tmps=0) diff --git a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt index db6974f66fe3..924d96cb0732 100644 --- a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt +++ b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt @@ -26,17 +26,14 @@ $_main: 0000 RETURN int(1) T::foo: - ; (lines=5, args=0, vars=0, tmps=1) + ; (lines=4, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:4-6 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -0003 VERIFY_RETURN_TYPE -0004 RETURN null -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 +0002 VERIFY_RETURN_TYPE +0003 RETURN null $_main: ; (lines=1, args=0, vars=0, tmps=0) @@ -45,11 +42,8 @@ $_main: 0000 RETURN int(1) T::foo: - ; (lines=3, args=0, vars=0, tmps=1) + ; (lines=2, args=0, vars=0, tmps=1) ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 VERIFY_RETURN_TYPE T0 -0002 RETURN T0 -LIVE RANGES: - 0: 0001 - 0002 (tmp/var) +0001 RETURN T0 From 15b87fd0d999f405061d199410df17f89df58065 Mon Sep 17 00:00:00 2001 From: azjezz Date: Tue, 16 Jun 2026 21:51:38 +0100 Subject: [PATCH 5/7] Optimizer: prove return types for unlinked classes via names Signed-off-by: azjezz --- Zend/Optimizer/dfa_pass.c | 69 ++++++++++------ ...ace_verify_return_type_for_this_class.phpt | 81 +++++++++++++++++++ ...ent_verify_return_type_for_this_class.phpt | 55 +++++++++++++ 3 files changed, 180 insertions(+), 25 deletions(-) create mode 100644 ext/opcache/tests/opt/verify_return_type/optimizer_inherited_interface_via_interface_verify_return_type_for_this_class.phpt create mode 100644 ext/opcache/tests/opt/verify_return_type/optimizer_inherited_interface_via_parent_verify_return_type_for_this_class.phpt diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index 3b003f725068..14c51a1fad43 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -252,45 +252,64 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op free_alloca(shiftlist, use_heap); } +/* Returns true if every instance of ce1 (or a subclass) is provably an instance + * of the target class. target_ce is the resolved class entry for the target + * when available; it may be NULL for a class declared in the same compilation + * unit that is not yet linked, in which case we fall back to matching + * target_lc against ce1's own declared parent and interfaces. */ static bool safe_instanceof( const zend_class_entry *ce1, - const zend_class_entry *ce2, + const zend_class_entry *target_ce, + const zend_string *target_lc, const zend_script *script, const zend_op_array *op_array ) { - if (ce1 == ce2) { + if (target_ce && ce1 == target_ce) { + return true; + } + if (zend_string_equals_ci(ce1->name, target_lc)) { return true; } if (ce1->ce_flags & ZEND_ACC_LINKED) { - return instanceof_function(ce1, ce2); + return target_ce && instanceof_function(ce1, target_ce); } - /* TODO Handle unlinked parents ike in unlinked_instanceof()? */ - - if (ce1->num_interfaces) { - uint32_t i; + for (uint32_t i = 0; i < ce1->num_interfaces; i++) { + const zend_class_entry *iface; if (ce1->ce_flags & ZEND_ACC_RESOLVED_INTERFACES) { /* Unlike the normal instanceof_function(), we have to perform a recursive * check here, as the parent interfaces might not have been fully copied yet. */ - for (i = 0; i < ce1->num_interfaces; i++) { - if (safe_instanceof(ce1->interfaces[i], ce2, script, op_array)) { - return true; - } - } + iface = ce1->interfaces[i]; } else { - for (i = 0; i < ce1->num_interfaces; i++) { - const zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, ce1->interface_names[i].lc_name); - if (!ce) { - continue; - } - /* Avoid recursing if class implements itself. */ - if (ce == ce1) { - continue; - } - if (safe_instanceof(ce, ce2, script, op_array)) { - return true; - } + if (zend_string_equals_ci(ce1->interface_names[i].lc_name, target_lc)) { + return true; } + + iface = zend_optimizer_get_class_entry(script, op_array, ce1->interface_names[i].lc_name); + } + + /* Skip if unresolvable/not-yet-copied, or the class implements itself. */ + if (!iface || iface == ce1) { + continue; + } + + if (safe_instanceof(iface, target_ce, target_lc, script, op_array)) { + return true; + } + } + + /* ce1 is unlinked here (the linked case returned above), so the + * parent/parent_name union holds the as-yet-unresolved parent name. */ + if (ce1->parent_name) { + if (zend_string_equals_ci(ce1->parent_name, target_lc)) { + return true; + } + + zend_string *parent_lc = zend_string_tolower(ce1->parent_name); + const zend_class_entry *parent = zend_optimizer_get_class_entry(script, op_array, parent_lc); + zend_string_release(parent_lc); + if (parent && parent != ce1 && safe_instanceof(parent, target_ce, target_lc, script, op_array)) { + return true; } } @@ -313,8 +332,8 @@ static inline bool can_elide_list_type( if (ZEND_TYPE_HAS_NAME(*single_type)) { zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type)); const zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname); + bool result = use_info->ce && safe_instanceof(use_info->ce, ce, lcname, script, op_array); zend_string_release(lcname); - bool result = ce && safe_instanceof(use_info->ce, ce, script, op_array); if (result == !is_intersection) { return result; } diff --git a/ext/opcache/tests/opt/verify_return_type/optimizer_inherited_interface_via_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/optimizer_inherited_interface_via_interface_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..c3bf3db18186 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/optimizer_inherited_interface_via_interface_verify_return_type_for_this_class.phpt @@ -0,0 +1,81 @@ +--TEST-- +Return type check elision in the optimizer for an interface reached through a (resolvable) implemented interface and $this +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=3, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-13 + ; return [] RANGE[0..0] +0000 JMPZ bool(true) 0002 +0001 DECLARE_CLASS string("d") +0002 RETURN int(1) + +D::getIterator: + ; (lines=5, args=0, vars=0, tmps=2) + ; (before optimizer) + ; %s:4-6 + ; return [] RANGE[0..0] +0000 T0 = NEW 0 string("Exception") +0001 DO_FCALL +0002 THROW T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (new) + +D::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:7-9 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-13 +0000 DECLARE_CLASS string("d") +0001 RETURN int(1) + +D::getIterator: + ; (lines=3, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:4-6 +0000 T0 = NEW 0 string("Exception") +0001 DO_FCALL +0002 THROW T0 +LIVE RANGES: + 0: 0001 - 0002 (new) + +D::foo: + ; (lines=2, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:7-9 +0000 T0 = FETCH_THIS +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/optimizer_inherited_interface_via_parent_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/optimizer_inherited_interface_via_parent_verify_return_type_for_this_class.phpt new file mode 100644 index 000000000000..5476bf9435e7 --- /dev/null +++ b/ext/opcache/tests/opt/verify_return_type/optimizer_inherited_interface_via_parent_verify_return_type_for_this_class.phpt @@ -0,0 +1,55 @@ +--TEST-- +Return type check elision in the optimizer for an interface reached through a (resolvable) parent class and $this +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x30000 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=3, args=0, vars=0, tmps=0) + ; (before optimizer) + ; %s:1-10 + ; return [] RANGE[0..0] +0000 JMPZ bool(true) 0002 +0001 DECLARE_CLASS string("c") string("arrayobject") +0002 RETURN int(1) + +C::foo: + ; (lines=5, args=0, vars=0, tmps=1) + ; (before optimizer) + ; %s:4-6 + ; return [] RANGE[0..0] +0000 T0 = FETCH_THIS +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) + +$_main: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %s:1-10 +0000 DECLARE_CLASS string("c") string("arrayobject") +0001 RETURN int(1) + +C::foo: + ; (lines=2, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:4-6 +0000 T0 = FETCH_THIS +0001 RETURN T0 From 3ed6985f21d7bc9954784b9fd838153623e6f3fd Mon Sep 17 00:00:00 2001 From: azjezz Date: Wed, 17 Jun 2026 11:03:10 +0100 Subject: [PATCH 6/7] drop compile-time $this return elision in favour of the optimizer --- Zend/Optimizer/dfa_pass.c | 20 ++++++ Zend/zend_compile.c | 63 ------------------- ...ace_verify_return_type_for_this_class.phpt | 13 ++-- ...ace_verify_return_type_for_this_class.phpt | 13 ++-- ...cit_verify_return_type_for_this_class.phpt | 13 ++-- ...ent_verify_return_type_for_this_class.phpt | 13 ++-- ...cit_verify_return_type_for_this_class.phpt | 13 ++-- ...elf_verify_return_type_for_this_class.phpt | 13 ++-- ...elf_verify_return_type_for_this_trait.phpt | 18 ++++-- ...tic_verify_return_type_for_this_class.phpt | 13 ++-- ...tic_verify_return_type_for_this_trait.phpt | 13 ++-- 11 files changed, 96 insertions(+), 109 deletions(-) diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index 14c51a1fad43..ca80ff1624c5 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -342,6 +342,16 @@ static inline bool can_elide_list_type( return is_intersection; } +/* Whether the SSA variable is the result of a ZEND_FETCH_THIS, i.e. is $this. */ +static bool zend_ssa_var_is_this(const zend_op_array *op_array, const zend_ssa *ssa, int var) { + if (var < 0) { + return false; + } + + int def = ssa->vars[var].definition; + return def >= 0 && op_array->opcodes[def].opcode == ZEND_FETCH_THIS; +} + static inline bool can_elide_return_type_check( const zend_script *script, zend_op_array *op_array, zend_ssa *ssa, zend_ssa_op *ssa_op) { zend_arg_info *arg_info = &op_array->arg_info[-1]; @@ -362,6 +372,16 @@ static inline bool can_elide_return_type_check( return true; } + /* A `static` return type only accepts the late-static-bound class. Returning + * $this always satisfies it, since $this is by definition an instance of + * `static`. Closures are excluded as they may be rebound to another scope. */ + if (disallowed_types == MAY_BE_OBJECT + && (ZEND_TYPE_PURE_MASK(arg_info->type) & MAY_BE_STATIC) + && !(op_array->fn_flags & ZEND_ACC_CLOSURE) + && zend_ssa_var_is_this(op_array, ssa, ssa_op->op1_use)) { + return true; + } + if (disallowed_types == MAY_BE_OBJECT && use_info->ce && ZEND_TYPE_IS_COMPLEX(arg_info->type)) { return can_elide_list_type(script, op_array, use_info, arg_info->type); } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index aa8a928c567c..7e9f7ceac8db 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2642,58 +2642,6 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr, uint32_t t } /* }}} */ -static bool zend_is_this_instance_of_name(const zend_string *type_name) -{ - if (zend_string_equals_ci(CG(active_class_entry)->name, type_name)) { - return true; - } - if (zend_string_equals_ci(type_name, ZSTR_KNOWN(ZEND_STR_SELF))) { - return true; - } - if (zend_string_equals_ci(type_name, ZSTR_KNOWN(ZEND_STR_PARENT))) { - return true; - } - - ZEND_ASSERT((CG(active_class_entry)->ce_flags & ZEND_ACC_LINKED) == 0); - if (CG(active_class_entry)->num_interfaces) { - for (uint32_t i = 0; i < CG(active_class_entry)->num_interfaces; i++) { - if (zend_string_equals_ci(CG(active_class_entry)->interface_names[i].lc_name, type_name)) { - return true; - } - } - } - const zend_string *parent_name = CG(active_class_entry)->parent_name; - if (parent_name && zend_string_equals_ci(parent_name, type_name)) { - return true; - } - - return false; -} - -static bool zend_is_this_valid_for_return_type(zend_type type) -{ - /* Closures can be bound to a class scope, however it might not and this must type error */ - if (CG(active_op_array)->fn_flags & ZEND_ACC_CLOSURE) { - return false; - } - - if (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_OBJECT|MAY_BE_STATIC)) { - return true; - } - - const zend_type *single_type; - ZEND_TYPE_FOREACH(type, single_type) { - if (ZEND_TYPE_HAS_NAME(*single_type)) { - const zend_string *name = ZEND_TYPE_NAME(*single_type); - if (zend_is_this_instance_of_name(name)) { - return true; - } - } - } ZEND_TYPE_FOREACH_END(); - - return false; -} - static void zend_emit_return_type_check( znode *expr, const zend_arg_info *return_info, bool implicit) /* {{{ */ { @@ -2749,17 +2697,6 @@ static void zend_emit_return_type_check( return; } - /* If return type contains static and we are returning $this - * (determined by checking if the previous opcode is ZEND_FETCH_THIS) - * then we don't need to check the return type */ - const zend_op_array *op_array = CG(active_op_array); - if (expr && op_array->last >= 1 - && op_array->opcodes[op_array->last-1].opcode == ZEND_FETCH_THIS - && zend_is_this_valid_for_return_type(type)) { - ZEND_ASSERT((expr->op_type & (IS_VAR|IS_TMP_VAR)) && expr->u.op.var == op_array->opcodes[op_array->last-1].result.var); - return; - } - opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL); if (expr && expr->op_type == IS_CONST) { opline->result_type = expr->op_type = IS_TMP_VAR; diff --git a/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt index 0ed9d39e6763..c572039479b0 100644 --- a/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt @@ -31,14 +31,17 @@ $_main: 0002 RETURN int(1) C::foo: - ; (lines=4, args=0, vars=0, tmps=1) + ; (lines=5, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:7-9 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 RETURN T0 -0002 VERIFY_RETURN_TYPE -0003 RETURN null +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) $_main: ; (lines=3, args=0, vars=0, tmps=0) @@ -53,4 +56,4 @@ C::foo: ; (after optimizer) ; %s:7-9 0000 T0 = FETCH_THIS -0001 RETURN T0 +0001 RETURN T0 \ No newline at end of file diff --git a/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt index 3f9b73f2feb6..e0a608af187a 100644 --- a/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt @@ -29,14 +29,17 @@ $_main: 0001 RETURN int(1) C::foo: - ; (lines=4, args=0, vars=0, tmps=1) + ; (lines=5, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:6-8 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 RETURN T0 -0002 VERIFY_RETURN_TYPE -0003 RETURN null +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) $_main: ; (lines=2, args=0, vars=0, tmps=0) @@ -50,4 +53,4 @@ C::foo: ; (after optimizer) ; %s:6-8 0000 T0 = FETCH_THIS -0001 RETURN T0 +0001 RETURN T0 \ No newline at end of file diff --git a/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt index a1f5302f7bc2..80f6b76e591a 100644 --- a/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt @@ -28,14 +28,17 @@ $_main: 0000 RETURN int(1) C2::foo: - ; (lines=4, args=0, vars=0, tmps=1) + ; (lines=5, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:6-8 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 RETURN T0 -0002 VERIFY_RETURN_TYPE -0003 RETURN null +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) $_main: ; (lines=1, args=0, vars=0, tmps=0) @@ -48,4 +51,4 @@ C2::foo: ; (after optimizer) ; %s:6-8 0000 T0 = FETCH_THIS -0001 RETURN T0 +0001 RETURN T0 \ No newline at end of file diff --git a/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt index 2eafbfbb822d..6d3b6e661b32 100644 --- a/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt @@ -28,14 +28,17 @@ $_main: 0000 RETURN int(1) C2::foo: - ; (lines=4, args=0, vars=0, tmps=1) + ; (lines=5, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:6-8 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 RETURN T0 -0002 VERIFY_RETURN_TYPE -0003 RETURN null +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) $_main: ; (lines=1, args=0, vars=0, tmps=0) @@ -48,4 +51,4 @@ C2::foo: ; (after optimizer) ; %s:6-8 0000 T0 = FETCH_THIS -0001 RETURN T0 +0001 RETURN T0 \ No newline at end of file diff --git a/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt index ef138191ba33..2b1bfdb529b7 100644 --- a/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt @@ -26,14 +26,17 @@ $_main: 0000 RETURN int(1) C::foo: - ; (lines=4, args=0, vars=0, tmps=1) + ; (lines=5, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:4-6 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 RETURN T0 -0002 VERIFY_RETURN_TYPE -0003 RETURN null +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) $_main: ; (lines=1, args=0, vars=0, tmps=0) @@ -46,4 +49,4 @@ C::foo: ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 RETURN T0 +0001 RETURN T0 \ No newline at end of file diff --git a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt index b59d919ea49f..c95fc594f00e 100644 --- a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt @@ -26,14 +26,17 @@ $_main: 0000 RETURN int(1) C::foo: - ; (lines=4, args=0, vars=0, tmps=1) + ; (lines=5, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:4-6 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 RETURN T0 -0002 VERIFY_RETURN_TYPE -0003 RETURN null +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) $_main: ; (lines=1, args=0, vars=0, tmps=0) @@ -46,4 +49,4 @@ C::foo: ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 RETURN T0 +0001 RETURN T0 \ No newline at end of file diff --git a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt index 924d96cb0732..c29fd735a668 100644 --- a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt +++ b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt @@ -26,14 +26,17 @@ $_main: 0000 RETURN int(1) T::foo: - ; (lines=4, args=0, vars=0, tmps=1) + ; (lines=5, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:4-6 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 RETURN T0 -0002 VERIFY_RETURN_TYPE -0003 RETURN null +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) $_main: ; (lines=1, args=0, vars=0, tmps=0) @@ -42,8 +45,11 @@ $_main: 0000 RETURN int(1) T::foo: - ; (lines=2, args=0, vars=0, tmps=1) + ; (lines=3, args=0, vars=0, tmps=1) ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 RETURN T0 +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) \ No newline at end of file diff --git a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt index 2b87b7cbc734..a4637e212a2c 100644 --- a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt @@ -26,14 +26,17 @@ $_main: 0000 RETURN int(1) C::returnStatic: - ; (lines=4, args=0, vars=0, tmps=1) + ; (lines=5, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:4-6 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 RETURN T0 -0002 VERIFY_RETURN_TYPE -0003 RETURN null +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) $_main: ; (lines=1, args=0, vars=0, tmps=0) @@ -46,4 +49,4 @@ C::returnStatic: ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 RETURN T0 +0001 RETURN T0 \ No newline at end of file diff --git a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt index fc8f852ba707..5225e929d9f1 100644 --- a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt +++ b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt @@ -26,14 +26,17 @@ $_main: 0000 RETURN int(1) T::returnStatic: - ; (lines=4, args=0, vars=0, tmps=1) + ; (lines=5, args=0, vars=0, tmps=1) ; (before optimizer) ; %s:4-6 ; return [] RANGE[0..0] 0000 T0 = FETCH_THIS -0001 RETURN T0 -0002 VERIFY_RETURN_TYPE -0003 RETURN null +0001 VERIFY_RETURN_TYPE T0 +0002 RETURN T0 +0003 VERIFY_RETURN_TYPE +0004 RETURN null +LIVE RANGES: + 0: 0001 - 0002 (tmp/var) $_main: ; (lines=1, args=0, vars=0, tmps=0) @@ -46,4 +49,4 @@ T::returnStatic: ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 RETURN T0 +0001 RETURN T0 \ No newline at end of file From c1985cc4d6064d582b4c4aeb96fb489b1170265f Mon Sep 17 00:00:00 2001 From: azjezz Date: Wed, 17 Jun 2026 19:41:52 +0100 Subject: [PATCH 7/7] EOL Signed-off-by: azjezz --- ...ct_extended_interface_verify_return_type_for_this_class.phpt | 2 +- .../direct_interface_verify_return_type_for_this_class.phpt | 2 +- .../parent_explicit_verify_return_type_for_this_class.phpt | 2 +- .../parent_verify_return_type_for_this_class.phpt | 2 +- .../self_explicit_verify_return_type_for_this_class.phpt | 2 +- .../self_verify_return_type_for_this_class.phpt | 2 +- .../self_verify_return_type_for_this_trait.phpt | 2 +- .../static_verify_return_type_for_this_class.phpt | 2 +- .../static_verify_return_type_for_this_trait.phpt | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt index c572039479b0..d7ccf3e9df66 100644 --- a/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/direct_extended_interface_verify_return_type_for_this_class.phpt @@ -56,4 +56,4 @@ C::foo: ; (after optimizer) ; %s:7-9 0000 T0 = FETCH_THIS -0001 RETURN T0 \ No newline at end of file +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt index e0a608af187a..46737c9fc310 100644 --- a/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/direct_interface_verify_return_type_for_this_class.phpt @@ -53,4 +53,4 @@ C::foo: ; (after optimizer) ; %s:6-8 0000 T0 = FETCH_THIS -0001 RETURN T0 \ No newline at end of file +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt index 80f6b76e591a..83df0bde2de5 100644 --- a/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/parent_explicit_verify_return_type_for_this_class.phpt @@ -51,4 +51,4 @@ C2::foo: ; (after optimizer) ; %s:6-8 0000 T0 = FETCH_THIS -0001 RETURN T0 \ No newline at end of file +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt index 6d3b6e661b32..e232df9b5803 100644 --- a/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/parent_verify_return_type_for_this_class.phpt @@ -51,4 +51,4 @@ C2::foo: ; (after optimizer) ; %s:6-8 0000 T0 = FETCH_THIS -0001 RETURN T0 \ No newline at end of file +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt index 2b1bfdb529b7..289b8c66e0d1 100644 --- a/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/self_explicit_verify_return_type_for_this_class.phpt @@ -49,4 +49,4 @@ C::foo: ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 RETURN T0 \ No newline at end of file +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt index c95fc594f00e..daddf47f3327 100644 --- a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_class.phpt @@ -49,4 +49,4 @@ C::foo: ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 RETURN T0 \ No newline at end of file +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt index c29fd735a668..db6974f66fe3 100644 --- a/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt +++ b/ext/opcache/tests/opt/verify_return_type/self_verify_return_type_for_this_trait.phpt @@ -52,4 +52,4 @@ T::foo: 0001 VERIFY_RETURN_TYPE T0 0002 RETURN T0 LIVE RANGES: - 0: 0001 - 0002 (tmp/var) \ No newline at end of file + 0: 0001 - 0002 (tmp/var) diff --git a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt index a4637e212a2c..7605c0b4e45f 100644 --- a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt +++ b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_class.phpt @@ -49,4 +49,4 @@ C::returnStatic: ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 RETURN T0 \ No newline at end of file +0001 RETURN T0 diff --git a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt index 5225e929d9f1..b8bd13ab9316 100644 --- a/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt +++ b/ext/opcache/tests/opt/verify_return_type/static_verify_return_type_for_this_trait.phpt @@ -49,4 +49,4 @@ T::returnStatic: ; (after optimizer) ; %s:4-6 0000 T0 = FETCH_THIS -0001 RETURN T0 \ No newline at end of file +0001 RETURN T0