summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-10-06 16:30:58 +0200
committerNikita Popov <nikita.ppv@gmail.com>2020-10-06 16:33:14 +0200
commit3b7c8bb973cb4b37ae5f034651d9e4cd4147729f (patch)
treea82ce84ae2dbba4522cbaf7a680af3e50c68a62b
parentc6e7969f0586ff3129a36ffddb5dc1bb259ff463 (diff)
downloadphp-git-3b7c8bb973cb4b37ae5f034651d9e4cd4147729f.tar.gz
Fix bug #80126
When performing an unlinked instanceof, we also need to consider interfaces of parent classes, as they may not have been inherited yet.
-rw-r--r--NEWS1
-rw-r--r--Zend/tests/bug80126.phpt23
-rw-r--r--Zend/tests/bug80126_2.phpt27
-rw-r--r--Zend/zend_inheritance.c22
4 files changed, 61 insertions, 12 deletions
diff --git a/NEWS b/NEWS
index cc2f44a7db..8d02664ab7 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ PHP NEWS
(cmb)
. Fixed bug #79423 (copy command is limited to size of file it can copy).
(cmb)
+ . Fixed bug #80126 (Covariant return types failing compilation). (Nikita)
- MySQLnd:
. Fixed bug #80115 (mysqlnd.debug doesn't recognize absolute paths with
diff --git a/Zend/tests/bug80126.phpt b/Zend/tests/bug80126.phpt
new file mode 100644
index 0000000000..3087a40629
--- /dev/null
+++ b/Zend/tests/bug80126.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #80126: Covariant return types failing compilation
+--FILE--
+<?php
+
+interface I {
+ public function method(): I;
+}
+
+abstract class A implements I {
+ public function method(): I { }
+}
+
+class C extends A { }
+
+class C2 extends C {
+ public function method(): C2 { }
+}
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/tests/bug80126_2.phpt b/Zend/tests/bug80126_2.phpt
new file mode 100644
index 0000000000..707d1b5a45
--- /dev/null
+++ b/Zend/tests/bug80126_2.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Bug #80126: Covariant return types failing compilation (variation 2)
+--FILE--
+<?php
+
+interface I {
+ public function method(): I;
+}
+
+abstract class A implements I {
+ public function method(): I {
+ return new static();
+ }
+}
+
+class C extends A { }
+
+interface I2 { }
+
+class C2 extends C implements I2 {
+ public function method(): C2 { }
+}
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index e7dcf54e0d..190ec57813 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -259,8 +259,6 @@ static zend_class_entry *lookup_class(zend_class_entry *scope, zend_string *name
/* Instanceof that's safe to use on unlinked classes. */
static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) {
- zend_class_entry *ce;
-
if (ce1 == ce2) {
return 1;
}
@@ -269,18 +267,18 @@ static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce
return instanceof_function(ce1, ce2);
}
- ce = ce1;
- while (ce->parent) {
- if (ce->ce_flags & ZEND_ACC_RESOLVED_PARENT) {
- ce = ce->parent;
+ if (ce1->parent) {
+ zend_class_entry *parent_ce;
+ if (ce1->ce_flags & ZEND_ACC_RESOLVED_PARENT) {
+ parent_ce = ce1->parent;
} else {
- ce = zend_lookup_class_ex(ce->parent_name, NULL,
+ parent_ce = zend_lookup_class_ex(ce1->parent_name, NULL,
ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD);
- if (!ce) {
- break;
- }
}
- if (ce == ce2) {
+
+ /* It's not sufficient to only check the parent chain itself, as need to do a full
+ * recursive instanceof in case the parent interfaces haven't been copied yet. */
+ if (parent_ce && unlinked_instanceof(parent_ce, ce2)) {
return 1;
}
}
@@ -297,7 +295,7 @@ static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce
}
} else {
for (i = 0; i < ce1->num_interfaces; i++) {
- ce = zend_lookup_class_ex(
+ zend_class_entry *ce = zend_lookup_class_ex(
ce1->interface_names[i].name, ce1->interface_names[i].lc_name,
ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD);
if (ce && unlinked_instanceof(ce, ce2)) {