summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--UPGRADING4
-rw-r--r--Zend/tests/bug30922.phpt2
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload1.phpt37
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload2.phpt38
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload3.phpt45
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload4.phpt44
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload5.phpt60
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload_error1.phpt (renamed from Zend/tests/type_declarations/variance/class_order_autoload.phpt)10
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload_error2.phpt27
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload_error3.phpt38
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload_error4.phpt39
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload_error5.phpt52
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload_error6.phpt39
-rw-r--r--Zend/tests/type_declarations/variance/class_order_error.phpt (renamed from Zend/tests/type_declarations/variance/class_order.phpt)0
-rw-r--r--Zend/zend_API.c2
-rw-r--r--Zend/zend_compile.c14
-rw-r--r--Zend/zend_compile.h11
-rw-r--r--Zend/zend_execute_API.c4
-rw-r--r--Zend/zend_globals.h3
-rw-r--r--Zend/zend_inheritance.c350
-rw-r--r--Zend/zend_interfaces.c4
-rw-r--r--Zend/zend_opcode.c4
-rw-r--r--Zend/zend_operators.c4
23 files changed, 762 insertions, 69 deletions
diff --git a/UPGRADING b/UPGRADING
index 6c61085834..eceb9b6f0a 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -163,7 +163,9 @@ PHP 7.4 UPGRADE NOTES
public function method(): B {}
}
- This feature is currently restricted to non-cyclic type references only.
+ Full variance support is only available if autoloading is used. Inside a
+ single file only non-cyclic type references are possible, because all
+ classes need to be available before they are referenced.
RFC: https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters
. Added support for coalesce assign (??=) operator. For example:
diff --git a/Zend/tests/bug30922.phpt b/Zend/tests/bug30922.phpt
index 0d5d8ae838..001845a7cb 100644
--- a/Zend/tests/bug30922.phpt
+++ b/Zend/tests/bug30922.phpt
@@ -10,4 +10,4 @@ var_dump($a instanceOf A);
echo "ok\n";
?>
--EXPECTF--
-Fatal error: Interface 'RecurisiveFooFar' not found in %sbug30922.php on line %d
+Fatal error: Interface RecurisiveFooFar cannot implement itself in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload1.phpt b/Zend/tests/type_declarations/variance/class_order_autoload1.phpt
new file mode 100644
index 0000000000..d68d2e8afa
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload1.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Class order allowed with autoloading (1)
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ if ($class === 'A') {
+ class A {
+ public function method() : B {}
+ }
+ var_dump(new A);
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method() : C {}
+ }
+ var_dump(new B);
+ } else {
+ class C extends B {
+ }
+ var_dump(new C);
+ }
+});
+
+var_dump(new C);
+
+?>
+===DONE===
+--EXPECT--
+object(A)#2 (0) {
+}
+object(B)#2 (0) {
+}
+object(C)#2 (0) {
+}
+object(C)#2 (0) {
+}
+===DONE===
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload2.phpt b/Zend/tests/type_declarations/variance/class_order_autoload2.phpt
new file mode 100644
index 0000000000..f6229d3995
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload2.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Class order allowed with autoloading (2)
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ if ($class === 'A') {
+ class A {
+ public function method() : B {}
+ }
+ var_dump(new A);
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method() : C {}
+ }
+ var_dump(new B);
+ } else {
+ class C extends B {
+ }
+ var_dump(new C);
+ }
+});
+
+// Same as autoload1 test case, but with a different autoloading root
+var_dump(new B);
+
+?>
+===DONE===
+--EXPECT--
+object(A)#2 (0) {
+}
+object(C)#2 (0) {
+}
+object(B)#2 (0) {
+}
+object(B)#2 (0) {
+}
+===DONE===
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload3.phpt b/Zend/tests/type_declarations/variance/class_order_autoload3.phpt
new file mode 100644
index 0000000000..d09f2a9c45
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload3.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Class order allowed with autoloading (3)
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ if ($class == 'A') {
+ class A {
+ public function method(): X {}
+ }
+ var_dump(new A);
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method(): Y {}
+ }
+ var_dump(new B);
+ } else if ($class == 'X') {
+ class X {
+ public function method(): A {}
+ }
+ var_dump(new X);
+ } else if ($class == 'Y') {
+ class Y extends X {
+ public function method(): B {}
+ }
+ var_dump(new Y);
+ }
+});
+
+var_dump(new B);
+
+?>
+===DONE===
+--EXPECT--
+object(A)#2 (0) {
+}
+object(X)#2 (0) {
+}
+object(Y)#2 (0) {
+}
+object(B)#2 (0) {
+}
+object(B)#2 (0) {
+}
+===DONE===
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload4.phpt b/Zend/tests/type_declarations/variance/class_order_autoload4.phpt
new file mode 100644
index 0000000000..37070b444c
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload4.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Class order allowed with autoloading (4)
+--FILE--
+<?php
+
+// Same as autoload3 test case, but with X, Y being interfaces.
+spl_autoload_register(function($class) {
+ if ($class == 'A') {
+ class A {
+ public function method(): X {}
+ }
+ var_dump(new A);
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method(): Y {}
+ }
+ var_dump(new B);
+ } else if ($class == 'X') {
+ interface X {
+ public function method(): A;
+ }
+ var_dump(interface_exists('X'));
+ } else if ($class == 'Y') {
+ interface Y extends X {
+ public function method(): B;
+ }
+ var_dump(interface_exists('Y'));
+ }
+});
+
+var_dump(new B);
+
+?>
+===DONE===
+--EXPECT--
+object(A)#2 (0) {
+}
+bool(true)
+bool(true)
+object(B)#2 (0) {
+}
+object(B)#2 (0) {
+}
+===DONE===
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload5.phpt b/Zend/tests/type_declarations/variance/class_order_autoload5.phpt
new file mode 100644
index 0000000000..77e9a0a9ec
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload5.phpt
@@ -0,0 +1,60 @@
+--TEST--
+Class order allowed with autoloading (5)
+--FILE--
+<?php
+
+// Similar to variance3, but one more class hierarchy in the cycle
+spl_autoload_register(function($class) {
+ if ($class == 'A') {
+ class A {
+ public function method(): X {}
+ }
+ var_dump(new A);
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method(): Y {}
+ }
+ var_dump(new B);
+ } else if ($class == 'X') {
+ class X {
+ public function method(): Q {}
+ }
+ var_dump(new X);
+ } else if ($class == 'Y') {
+ class Y extends X {
+ public function method(): R {}
+ }
+ var_dump(new Y);
+ } else if ($class == 'Q') {
+ class Q {
+ public function method(): A {}
+ }
+ var_dump(new Q);
+ } else if ($class == 'R') {
+ class R extends Q {
+ public function method(): B {}
+ }
+ var_dump(new R);
+ }
+});
+
+var_dump(new B);
+
+?>
+===DONE===
+--EXPECT--
+object(A)#2 (0) {
+}
+object(X)#2 (0) {
+}
+object(Q)#2 (0) {
+}
+object(R)#2 (0) {
+}
+object(Y)#2 (0) {
+}
+object(B)#2 (0) {
+}
+object(B)#2 (0) {
+}
+===DONE===
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload.phpt b/Zend/tests/type_declarations/variance/class_order_autoload_error1.phpt
index a4ea534577..9af4041604 100644
--- a/Zend/tests/type_declarations/variance/class_order_autoload.phpt
+++ b/Zend/tests/type_declarations/variance/class_order_autoload_error1.phpt
@@ -1,16 +1,16 @@
--TEST--
-Returns are covariant, but we don't allow the code due to class ordering (autoload variation)
+Variance error in the presence of autoloading (1)
--FILE--
<?php
spl_autoload_register(function($class) {
if ($class === 'A') {
class A {
- public function method() : B {}
+ public function method() : C {}
}
} else if ($class == 'B') {
class B extends A {
- public function method() : C {}
+ public function method() : B {}
}
} else {
class C extends B {
@@ -18,8 +18,8 @@ spl_autoload_register(function($class) {
}
});
-$c = new C;
+$b = new B;
?>
--EXPECTF--
-Fatal error: Could not check compatibility between B::method(): C and A::method(): B, because class C is not available in %s on line %d
+Fatal error: Declaration of B::method(): B must be compatible with A::method(): C in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload_error2.phpt b/Zend/tests/type_declarations/variance/class_order_autoload_error2.phpt
new file mode 100644
index 0000000000..48d2e0b956
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload_error2.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Variance error in the presence of autoloading (2)
+--FILE--
+<?php
+
+// Same as autoload_error1, but for argument types.
+spl_autoload_register(function($class) {
+ if ($class === 'A') {
+ class A {
+ public function method(B $x) {}
+ }
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method(C $x) {}
+ }
+ } else {
+ class C extends B {
+ }
+ }
+});
+
+$b = new B;
+$c = new C;
+
+?>
+--EXPECTF--
+Warning: Declaration of B::method(C $x) should be compatible with A::method(B $x) in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload_error3.phpt b/Zend/tests/type_declarations/variance/class_order_autoload_error3.phpt
new file mode 100644
index 0000000000..23b60b4584
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload_error3.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Variance error in the presence of autoloading (3)
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ if ($class == 'A') {
+ class A {
+ public function method(): X {}
+ }
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method(): Y {}
+ }
+ } else if ($class == 'X') {
+ class X {
+ public function method(): Q {}
+ }
+ } else if ($class == 'Y') {
+ class Y extends X {
+ public function method(): R {}
+ }
+ } else if ($class == 'Q') {
+ class Q {
+ public function method(): B {}
+ }
+ } else if ($class == 'R') {
+ class R extends Q {
+ public function method(): A {}
+ }
+ }
+});
+
+$b = new B;
+
+?>
+--EXPECTF--
+Fatal error: Declaration of R::method(): A must be compatible with Q::method(): B in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload_error4.phpt b/Zend/tests/type_declarations/variance/class_order_autoload_error4.phpt
new file mode 100644
index 0000000000..6acf9313f4
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload_error4.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Variance error in the presence of autoloading (4)
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ if ($class == 'A') {
+ class A {
+ public function method(): X {}
+ }
+ var_dump(new A);
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method(): Y {}
+ }
+ var_dump(new B);
+ } else if ($class == 'X') {
+ class X {
+ public function method(): B {}
+ }
+ var_dump(new X);
+ } else if ($class == 'Y') {
+ class Y extends X {
+ public function method(): A {}
+ }
+ var_dump(new Y);
+ }
+});
+
+var_dump(new B);
+
+?>
+--EXPECTF--
+object(A)#2 (0) {
+}
+object(X)#2 (0) {
+}
+
+Fatal error: Declaration of Y::method(): A must be compatible with X::method(): B in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload_error5.phpt b/Zend/tests/type_declarations/variance/class_order_autoload_error5.phpt
new file mode 100644
index 0000000000..a6a46f84a2
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload_error5.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Variance error in the presence of autoloading (5)
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ if ($class == 'A') {
+ class A {
+ public function method(): X {}
+ }
+ var_dump(new A);
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method(): Y {}
+ }
+ var_dump(new B);
+ } else if ($class == 'X') {
+ class X {
+ public function method(Y $a) {}
+ }
+ var_dump(new X);
+ } else if ($class == 'Y') {
+ class Y extends X {
+ public function method(Z $a) {}
+ }
+ var_dump(new Y);
+ } else if ($class == 'Z') {
+ class Z extends Y {
+ public function method($a) {}
+ }
+ var_dump(new Z);
+ }
+});
+
+var_dump(new B);
+
+?>
+--EXPECTF--
+object(A)#2 (0) {
+}
+object(X)#2 (0) {
+}
+
+Warning: Declaration of Y::method(Z $a) should be compatible with X::method(Y $a) in %s on line %d
+object(Z)#2 (0) {
+}
+object(Y)#2 (0) {
+}
+object(B)#2 (0) {
+}
+object(B)#2 (0) {
+}
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload_error6.phpt b/Zend/tests/type_declarations/variance/class_order_autoload_error6.phpt
new file mode 100644
index 0000000000..4b54c9554d
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload_error6.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Variance error in the presence of autoloading (6)
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ if ($class == 'A') {
+ class A {
+ public function method(): X {}
+ }
+ var_dump(new A);
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method(): Y {}
+ }
+ var_dump(new B);
+ } else if ($class == 'X') {
+ class X {
+ public function method(): X {}
+ }
+ var_dump(new X);
+ } else if ($class == 'Y') {
+ class Y extends X {
+ public function method(): Unknown {}
+ }
+ var_dump(new Y);
+ }
+});
+
+var_dump(new B);
+
+?>
+--EXPECTF--
+object(A)#2 (0) {
+}
+object(X)#2 (0) {
+}
+
+Fatal error: Could not check compatibility between Y::method(): Unknown and X::method(): X, because class Unknown is not available in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/class_order.phpt b/Zend/tests/type_declarations/variance/class_order_error.phpt
index df66e78b78..df66e78b78 100644
--- a/Zend/tests/type_declarations/variance/class_order.phpt
+++ b/Zend/tests/type_declarations/variance/class_order_error.phpt
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index f88fc2caf8..13c661699f 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -2649,7 +2649,7 @@ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class
class_entry->type = ZEND_INTERNAL_CLASS;
zend_initialize_class_data(class_entry, 0);
- class_entry->ce_flags = ce_flags | ZEND_ACC_CONSTANTS_UPDATED | ZEND_ACC_LINKED;
+ class_entry->ce_flags = ce_flags | ZEND_ACC_CONSTANTS_UPDATED | ZEND_ACC_LINKED | ZEND_ACC_RESOLVED_PARENT | ZEND_ACC_RESOLVED_INTERFACES;
class_entry->info.internal.module = EG(current_module);
if (class_entry->info.internal.builtin_functions) {
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index aa7125cfe1..85c0bf8cd5 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -370,6 +370,9 @@ void init_compiler(void) /* {{{ */
zend_hash_init(&CG(filenames_table), 8, NULL, ZVAL_PTR_DTOR, 0);
zend_llist_init(&CG(open_files), sizeof(zend_file_handle), (void (*)(void *)) file_handle_dtor, 0);
CG(unclean_shutdown) = 0;
+
+ CG(delayed_variance_obligations) = NULL;
+ CG(delayed_autoloads) = NULL;
}
/* }}} */
@@ -379,6 +382,17 @@ void shutdown_compiler(void) /* {{{ */
zend_stack_destroy(&CG(delayed_oplines_stack));
zend_hash_destroy(&CG(filenames_table));
zend_arena_destroy(CG(arena));
+
+ if (CG(delayed_variance_obligations)) {
+ zend_hash_destroy(CG(delayed_variance_obligations));
+ FREE_HASHTABLE(CG(delayed_variance_obligations));
+ CG(delayed_variance_obligations) = NULL;
+ }
+ if (CG(delayed_autoloads)) {
+ zend_hash_destroy(CG(delayed_autoloads));
+ FREE_HASHTABLE(CG(delayed_autoloads));
+ CG(delayed_autoloads) = NULL;
+ }
}
/* }}} */
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 3f2ec6ba07..b47d762a74 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -269,8 +269,14 @@ typedef struct _zend_oparray_context {
/* Children must reuse parent get_iterator() | | | */
#define ZEND_ACC_REUSE_GET_ITERATOR (1 << 18) /* X | | | */
/* | | | */
-/* Class is being linked. Don't free strings. | | | */
-#define ZEND_ACC_LINKING_IN_PROGRESS (1 << 19) /* X | | | */
+/* Parent class is resolved (CE). | | | */
+#define ZEND_ACC_RESOLVED_PARENT (1 << 19) /* X | | | */
+/* | | | */
+/* Interfaces are resolved (CEs). | | | */
+#define ZEND_ACC_RESOLVED_INTERFACES (1 << 20) /* X | | | */
+/* | | | */
+/* Class has unresolved variance obligations. | | | */
+#define ZEND_ACC_UNRESOLVED_VARIANCE (1 << 21) /* X | | | */
/* | | | */
/* Function Flags (unused: 28...30) | | | */
/* ============== | | | */
@@ -852,6 +858,7 @@ void zend_assert_valid_class_name(const zend_string *const_name);
#define ZEND_FETCH_CLASS_NO_AUTOLOAD 0x80
#define ZEND_FETCH_CLASS_SILENT 0x0100
#define ZEND_FETCH_CLASS_EXCEPTION 0x0200
+#define ZEND_FETCH_CLASS_ALLOW_UNLINKED 0x0400
#define ZEND_PARAM_REF (1<<0)
#define ZEND_PARAM_VARIADIC (1<<1)
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 431031acba..e0cc560d4b 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -34,6 +34,7 @@
#include "zend_vm.h"
#include "zend_float.h"
#include "zend_weakrefs.h"
+#include "zend_inheritance.h"
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
@@ -916,7 +917,8 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
zend_string_release_ex(lc_name, 0);
}
ce = (zend_class_entry*)Z_PTR_P(zv);
- if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) {
+ if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED)) &&
+ !(flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED)) {
return NULL;
}
return ce;
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h
index c95faef22b..d88fdabcf7 100644
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@ -124,6 +124,9 @@ struct _zend_compiler_globals {
void *map_ptr_base;
size_t map_ptr_size;
size_t map_ptr_last;
+
+ HashTable *delayed_variance_obligations;
+ HashTable *delayed_autoloads;
};
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 738e528308..ba1612c189 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -26,6 +26,11 @@
#include "zend_smart_str.h"
#include "zend_operators.h"
+static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce);
+static void add_compatibility_obligation(
+ zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn,
+ zend_bool always_error);
+
static void overridden_ptr_dtor(zval *zv) /* {{{ */
{
efree_size(Z_PTR_P(zv), sizeof(zend_function));
@@ -174,7 +179,7 @@ static zend_string *resolve_class_name(const zend_function *fe, zend_string *nam
zend_class_entry *ce = fe->common.scope;
ZEND_ASSERT(ce);
if (zend_string_equals_literal_ci(name, "parent") && ce->parent) {
- if (ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS)) {
+ if (ce->ce_flags & ZEND_ACC_RESOLVED_PARENT) {
return ce->parent->name;
} else {
return ce->parent_name;
@@ -199,32 +204,75 @@ static zend_bool class_visible(zend_class_entry *ce) {
static zend_class_entry *lookup_class(const zend_function *fe, zend_string *name) {
zend_class_entry *ce;
if (!CG(in_compilation)) {
- ce = zend_lookup_class(name);
+ uint32_t flags = ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD;
+ ce = zend_lookup_class_ex(name, NULL, flags);
if (ce) {
return ce;
}
+
+ /* We'll autoload this class and process delayed variance obligations later. */
+ if (!CG(delayed_autoloads)) {
+ ALLOC_HASHTABLE(CG(delayed_autoloads));
+ zend_hash_init(CG(delayed_autoloads), 0, NULL, NULL, 0);
+ }
+ zend_hash_add_empty_element(CG(delayed_autoloads), name);
} else {
ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
if (ce && class_visible(ce)) {
return ce;
}
- }
- /* The current class may not be registered yet, so check for it explicitly. */
- if (zend_string_equals_ci(fe->common.scope->name, name)) {
- return fe->common.scope;
+ /* The current class may not be registered yet, so check for it explicitly. */
+ if (zend_string_equals_ci(fe->common.scope->name, name)) {
+ return fe->common.scope;
+ }
}
return NULL;
}
-/* Instanceof that's safe to use on unlinked classes. For the unlinked case, we only handle
- * class identity here. */
+/* Instanceof that's safe to use on unlinked classes. */
static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) {
- if ((ce1->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS))) {
+ zend_class_entry *ce;
+
+ if (ce1 == ce2) {
+ return 1;
+ }
+
+ if (ce1->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_RESOLVED_INTERFACES)) {
return instanceof_function(ce1, ce2);
}
- return ce1 == ce2;
+
+ ce = ce1;
+ while (ce->parent) {
+ if (ce->ce_flags & ZEND_ACC_RESOLVED_PARENT) {
+ ce = ce->parent;
+ } else {
+ ce = zend_lookup_class_ex(ce->parent_name, NULL,
+ ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD);
+ if (!ce) {
+ break;
+ }
+ }
+ if (ce == ce2) {
+ return 1;
+ }
+ }
+
+ if (ce1->num_interfaces) {
+ uint32_t i;
+ ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES));
+ for (i = 0; i < ce1->num_interfaces; i++) {
+ 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)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
}
/* Unresolved means that class declarations that are currently not available are needed to
@@ -261,13 +309,14 @@ static inheritance_status zend_perform_covariant_type_check(
return INHERITANCE_SUCCESS;
}
+ /* Make sure to always load both classes, to avoid only registering one of them as
+ * a delayed autoload. */
fe_ce = lookup_class(fe, fe_class_name);
+ proto_ce = lookup_class(proto, proto_class_name);
if (!fe_ce) {
*unresolved_class = fe_class_name;
return INHERITANCE_UNRESOLVED;
}
-
- proto_ce = lookup_class(proto, proto_class_name);
if (!proto_ce) {
*unresolved_class = proto_class_name;
return INHERITANCE_UNRESOLVED;
@@ -440,6 +489,17 @@ static inheritance_status zend_do_perform_implementation_check(
}
/* }}} */
+static inheritance_status perform_delayable_implementation_check(
+ zend_string **unresolved_class, zend_class_entry *ce,
+ const zend_function *fe, const zend_function *proto, zend_bool always_error) {
+ inheritance_status status = zend_do_perform_implementation_check(
+ unresolved_class, fe, proto);
+ if (status == INHERITANCE_UNRESOLVED) {
+ add_compatibility_obligation(ce, fe, proto, always_error);
+ }
+ return status;
+}
+
static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function *fptr, zend_arg_info *arg_info, int return_hint) /* {{{ */
{
@@ -601,12 +661,13 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function
}
/* }}} */
-static zend_always_inline uint32_t func_lineno(zend_function *fn) {
+static zend_always_inline uint32_t func_lineno(const zend_function *fn) {
return fn->common.type == ZEND_USER_FUNCTION ? fn->op_array.line_start : 0;
}
static void ZEND_COLD emit_incompatible_method_error(
- int error_level, const char *error_verb, zend_function *child, zend_function *parent,
+ int error_level, const char *error_verb,
+ const zend_function *child, const zend_function *parent,
inheritance_status status, zend_string *unresolved_class) {
zend_string *parent_prototype = zend_get_function_declaration(parent);
zend_string *child_prototype = zend_get_function_declaration(child);
@@ -623,6 +684,28 @@ static void ZEND_COLD emit_incompatible_method_error(
zend_string_efree(parent_prototype);
}
+static void ZEND_COLD emit_incompatible_method_error_or_warning(
+ const zend_function *child, const zend_function *parent,
+ inheritance_status status, zend_string *unresolved_class, zend_bool always_error) {
+ int error_level;
+ const char *error_verb;
+ if (always_error ||
+ (child->common.prototype &&
+ (child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) ||
+ ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) &&
+ (!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+ zend_perform_covariant_type_check(&unresolved_class, child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) != INHERITANCE_SUCCESS))
+ ) {
+ error_level = E_COMPILE_ERROR;
+ error_verb = "must";
+ } else {
+ error_level = E_WARNING;
+ error_verb = "should";
+ }
+ emit_incompatible_method_error(
+ error_level, error_verb, child, parent, status, unresolved_class);
+}
+
static void do_inheritance_check_on_method(zend_function *child, zend_function *parent, zend_class_entry *ce, zval *child_zv) /* {{{ */
{
uint32_t child_flags;
@@ -707,26 +790,11 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
}
- status = zend_do_perform_implementation_check(&unresolved_class, child, parent);
- if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
- int error_level;
- const char *error_verb;
- if (child->common.prototype && (
- child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT
- )) {
- error_level = E_COMPILE_ERROR;
- error_verb = "must";
- } else if ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) &&
- (!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- zend_perform_covariant_type_check(&unresolved_class, child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) != INHERITANCE_SUCCESS)) {
- error_level = E_COMPILE_ERROR;
- error_verb = "must";
- } else {
- error_level = E_WARNING;
- error_verb = "should";
- }
- emit_incompatible_method_error(
- error_level, error_verb, child, parent, status, unresolved_class);
+ status = perform_delayable_implementation_check(
+ &unresolved_class, ce, child, parent, /*always_error*/0);
+ if (status == INHERITANCE_ERROR) {
+ emit_incompatible_method_error_or_warning(
+ child, parent, status, unresolved_class, /*always_error*/0);
}
}
} while (0);
@@ -904,6 +972,7 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en
ce->interfaces[ce->num_interfaces++] = entry;
}
}
+ ce->ce_flags |= ZEND_ACC_RESOLVED_INTERFACES;
/* and now call the implementing handlers */
while (ce_num < ce->num_interfaces) {
@@ -1005,6 +1074,7 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
zend_string_release_ex(ce->parent_name, 0);
}
ce->parent = parent_ce;
+ ce->ce_flags |= ZEND_ACC_RESOLVED_PARENT;
/* Inherit interfaces */
if (parent_ce->num_interfaces) {
@@ -1314,10 +1384,11 @@ static void zend_do_implement_interfaces(zend_class_entry *ce) /* {{{ */
}
for (i = 0; i < ce->num_interfaces; i++) {
- iface = zend_fetch_class_by_name(ce->interface_names[i].name,
- ce->interface_names[i].lc_name, ZEND_FETCH_CLASS_INTERFACE);
- if (UNEXPECTED(iface == NULL)) {
- return;
+ iface = zend_fetch_class_by_name(
+ ce->interface_names[i].name, ce->interface_names[i].lc_name,
+ ZEND_FETCH_CLASS_INTERFACE|ZEND_FETCH_CLASS_ALLOW_UNLINKED);
+ if (!(iface->ce_flags & ZEND_ACC_LINKED)) {
+ add_dependency_obligation(ce, iface);
}
if (UNEXPECTED(!(iface->ce_flags & ZEND_ACC_INTERFACE))) {
efree(interfaces);
@@ -1354,6 +1425,7 @@ static void zend_do_implement_interfaces(zend_class_entry *ce) /* {{{ */
ce->num_interfaces = num_interfaces;
ce->interfaces = interfaces;
+ ce->ce_flags |= ZEND_ACC_RESOLVED_INTERFACES;
i = ce->parent ? ce->parent->num_interfaces : 0;
for (; i < ce->num_interfaces; i++) {
@@ -1454,18 +1526,18 @@ static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_s
if ((existing_fn = zend_hash_find_ptr(*overridden, key)) != NULL) {
if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
/* Make sure the trait method is compatible with previosly declared abstract method */
- status = zend_do_perform_implementation_check(
- &unresolved_class, fn, existing_fn);
- if (status != INHERITANCE_SUCCESS) {
+ status = perform_delayable_implementation_check(
+ &unresolved_class, ce, fn, existing_fn, /*always_error*/ 1);
+ if (status == INHERITANCE_ERROR) {
emit_incompatible_method_error(
E_COMPILE_ERROR, "must", fn, existing_fn, status, unresolved_class);
}
}
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
/* Make sure the abstract declaration is compatible with previous declaration */
- status = zend_do_perform_implementation_check(
- &unresolved_class, existing_fn, fn);
- if (status != INHERITANCE_SUCCESS) {
+ status = perform_delayable_implementation_check(
+ &unresolved_class, ce, existing_fn, fn, /*always_error*/ 1);
+ if (status == INHERITANCE_ERROR) {
emit_incompatible_method_error(
E_COMPILE_ERROR, "must", existing_fn, fn, status, unresolved_class);
}
@@ -1481,15 +1553,17 @@ static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_s
} else if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT &&
(existing_fn->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0) {
/* Make sure the trait method is compatible with previosly declared abstract method */
- status = zend_do_perform_implementation_check(&unresolved_class, fn, existing_fn);
- if (status != INHERITANCE_SUCCESS) {
+ status = perform_delayable_implementation_check(
+ &unresolved_class, ce, fn, existing_fn, /*always_error*/ 1);
+ if (status == INHERITANCE_ERROR) {
emit_incompatible_method_error(
E_COMPILE_ERROR, "must", fn, existing_fn, status, unresolved_class);
}
} else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
/* Make sure the abstract declaration is compatible with previous declaration */
- status = zend_do_perform_implementation_check(&unresolved_class, existing_fn, fn);
- if (status != INHERITANCE_SUCCESS) {
+ status = perform_delayable_implementation_check(
+ &unresolved_class, ce, existing_fn, fn, /*always_error*/ 1);
+ if (status == INHERITANCE_ERROR) {
emit_incompatible_method_error(
E_COMPILE_ERROR, "must", existing_fn, fn, status, unresolved_class);
}
@@ -2130,11 +2204,172 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */
}
/* }}} */
+typedef struct {
+ enum { OBLIGATION_DEPENDENCY, OBLIGATION_COMPATIBILITY } type;
+ union {
+ zend_class_entry *dependency_ce;
+ struct {
+ const zend_function *parent_fn;
+ const zend_function *child_fn;
+ zend_bool always_error;
+ };
+ };
+} variance_obligation;
+
+static void variance_obligation_dtor(zval *zv) {
+ efree(Z_PTR_P(zv));
+}
+
+static void variance_obligation_ht_dtor(zval *zv) {
+ zend_hash_destroy(Z_PTR_P(zv));
+ FREE_HASHTABLE(Z_PTR_P(zv));
+}
+
+static HashTable *get_or_init_obligations_for_class(zend_class_entry *ce) {
+ HashTable *ht;
+ zend_ulong key;
+ if (!CG(delayed_variance_obligations)) {
+ ALLOC_HASHTABLE(CG(delayed_variance_obligations));
+ zend_hash_init(CG(delayed_variance_obligations), 0, NULL, variance_obligation_ht_dtor, 0);
+ }
+
+ key = (zend_ulong) (uintptr_t) ce;
+ ht = zend_hash_index_find_ptr(CG(delayed_variance_obligations), key);
+ if (ht) {
+ return ht;
+ }
+
+ ALLOC_HASHTABLE(ht);
+ zend_hash_init(ht, 0, NULL, variance_obligation_dtor, 0);
+ zend_hash_index_add_new_ptr(CG(delayed_variance_obligations), key, ht);
+ ce->ce_flags |= ZEND_ACC_UNRESOLVED_VARIANCE;
+ return ht;
+}
+
+static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce) {
+ HashTable *obligations = get_or_init_obligations_for_class(ce);
+ variance_obligation *obligation = emalloc(sizeof(variance_obligation));
+ obligation->type = OBLIGATION_DEPENDENCY;
+ obligation->dependency_ce = dependency_ce;
+ zend_hash_next_index_insert_ptr(obligations, obligation);
+}
+
+static void add_compatibility_obligation(
+ zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn,
+ zend_bool always_error) {
+ HashTable *obligations = get_or_init_obligations_for_class(ce);
+ variance_obligation *obligation = emalloc(sizeof(variance_obligation));
+ obligation->type = OBLIGATION_COMPATIBILITY;
+ obligation->child_fn = child_fn;
+ obligation->parent_fn = parent_fn;
+ obligation->always_error = always_error;
+ zend_hash_next_index_insert_ptr(obligations, obligation);
+}
+
+static void resolve_delayed_variance_obligations(zend_class_entry *ce);
+
+static int check_variance_obligation(zval *zv) {
+ variance_obligation *obligation = Z_PTR_P(zv);
+ if (obligation->type == OBLIGATION_DEPENDENCY) {
+ zend_class_entry *dependency_ce = obligation->dependency_ce;
+ if (dependency_ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
+ resolve_delayed_variance_obligations(dependency_ce);
+ }
+ if (!(dependency_ce->ce_flags & ZEND_ACC_LINKED)) {
+ return ZEND_HASH_APPLY_KEEP;
+ }
+ } else {
+ zend_string *unresolved_class;
+ inheritance_status status = zend_do_perform_implementation_check(
+ &unresolved_class, obligation->child_fn, obligation->parent_fn);
+ if (status == INHERITANCE_UNRESOLVED) {
+ return ZEND_HASH_APPLY_KEEP;
+ }
+ if (status == INHERITANCE_ERROR) {
+ emit_incompatible_method_error_or_warning(
+ obligation->child_fn, obligation->parent_fn, status, unresolved_class,
+ obligation->always_error);
+ }
+ /* Either the compatibility check was successful or only threw a warning. */
+ }
+ return ZEND_HASH_APPLY_REMOVE;
+}
+
+static void load_delayed_classes() {
+ HashTable *delayed_autoloads = CG(delayed_autoloads);
+ zend_string *name;
+
+ if (!delayed_autoloads) {
+ return;
+ }
+
+ /* Take ownership of this HT, to avoid concurrent modification during autoloading. */
+ CG(delayed_autoloads) = NULL;
+
+ ZEND_HASH_FOREACH_STR_KEY(delayed_autoloads, name) {
+ zend_lookup_class(name);
+ } ZEND_HASH_FOREACH_END();
+
+ zend_hash_destroy(delayed_autoloads);
+ FREE_HASHTABLE(delayed_autoloads);
+}
+
+static void resolve_delayed_variance_obligations(zend_class_entry *ce) {
+ HashTable *all_obligations = CG(delayed_variance_obligations), *obligations;
+ zend_ulong num_key = (zend_ulong) (uintptr_t) ce;
+
+ ZEND_ASSERT(all_obligations != NULL);
+ obligations = zend_hash_index_find_ptr(all_obligations, num_key);
+ ZEND_ASSERT(obligations != NULL);
+
+ zend_hash_apply(obligations, check_variance_obligation);
+ if (zend_hash_num_elements(obligations) == 0) {
+ ce->ce_flags &= ~ZEND_ACC_UNRESOLVED_VARIANCE;
+ ce->ce_flags |= ZEND_ACC_LINKED;
+ zend_hash_index_del(all_obligations, num_key);
+ }
+}
+
+static void report_variance_errors(zend_class_entry *ce) {
+ HashTable *all_obligations = CG(delayed_variance_obligations), *obligations;
+ variance_obligation *obligation;
+ zend_ulong num_key = (zend_ulong) (uintptr_t) ce;
+
+ ZEND_ASSERT(all_obligations != NULL);
+ obligations = zend_hash_index_find_ptr(all_obligations, num_key);
+ ZEND_ASSERT(obligations != NULL);
+
+ ZEND_HASH_FOREACH_PTR(obligations, obligation) {
+ inheritance_status status;
+ zend_string *unresolved_class;
+
+ /* There should not be any unresolved parents at this point. */
+ ZEND_ASSERT(obligation->type == OBLIGATION_COMPATIBILITY);
+
+ /* Just used to fetch the unresolved_class in this case. */
+ status = zend_do_perform_implementation_check(
+ &unresolved_class, obligation->child_fn, obligation->parent_fn);
+ ZEND_ASSERT(status == INHERITANCE_UNRESOLVED);
+ emit_incompatible_method_error_or_warning(
+ obligation->child_fn, obligation->parent_fn,
+ status, unresolved_class, obligation->always_error);
+ } ZEND_HASH_FOREACH_END();
+
+ /* Only warnings were thrown above -- that means that there are incompatibilities, but only
+ * ones that we permit. Mark all classes with open obligations as fully linked. */
+ ce->ce_flags &= ~ZEND_ACC_UNRESOLVED_VARIANCE;
+ ce->ce_flags |= ZEND_ACC_LINKED;
+ zend_hash_index_del(all_obligations, num_key);
+}
+
ZEND_API void zend_do_link_class(zend_class_entry *ce) /* {{{ */
{
- ce->ce_flags |= ZEND_ACC_LINKING_IN_PROGRESS;
if (ce->parent_name) {
- zend_class_entry *parent = zend_fetch_class_by_name(ce->parent_name, NULL, 0);
+ zend_class_entry *parent = zend_fetch_class_by_name(
+ ce->parent_name, NULL, ZEND_FETCH_CLASS_ALLOW_UNLINKED);
+ if (!(parent->ce_flags & ZEND_ACC_LINKED)) {
+ add_dependency_obligation(ce, parent);
+ }
zend_do_inheritance(ce, parent);
}
if (ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS) {
@@ -2148,8 +2383,19 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce) /* {{{ */
}
zend_build_properties_info_table(ce);
- ce->ce_flags &= ~ZEND_ACC_LINKING_IN_PROGRESS;
- ce->ce_flags |= ZEND_ACC_LINKED;
+
+ if (!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE)) {
+ ce->ce_flags |= ZEND_ACC_LINKED;
+ return;
+ }
+
+ load_delayed_classes();
+ if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
+ resolve_delayed_variance_obligations(ce);
+ if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
+ report_variance_errors(ce);
+ }
+ }
}
/* Check whether early binding is prevented due to unresolved types in inheritance checks. */
diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c
index 4d58a9898a..ff6784be31 100644
--- a/Zend/zend_interfaces.c
+++ b/Zend/zend_interfaces.c
@@ -292,7 +292,7 @@ static int zend_implement_traversable(zend_class_entry *interface, zend_class_en
return SUCCESS;
}
if (class_type->num_interfaces) {
- ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
+ ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
for (i = 0; i < class_type->num_interfaces; i++) {
if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
return SUCCESS;
@@ -322,7 +322,7 @@ static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entr
} else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
/* c-level get_iterator cannot be changed (exception being only Traversable is implemented) */
if (class_type->num_interfaces) {
- ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
+ ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
for (i = 0; i < class_type->num_interfaces; i++) {
if (class_type->interfaces[i] == zend_ce_iterator) {
zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index ae70dee0ce..6c8ff85b10 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -238,7 +238,7 @@ ZEND_API void destroy_zend_class(zval *zv)
}
switch (ce->type) {
case ZEND_USER_CLASS:
- if (ce->parent_name && !(ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS))) {
+ if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) {
zend_string_release_ex(ce->parent_name, 0);
}
if (ce->default_properties_table) {
@@ -298,7 +298,7 @@ ZEND_API void destroy_zend_class(zval *zv)
}
zend_hash_destroy(&ce->constants_table);
if (ce->num_interfaces > 0) {
- if (!(ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS))) {
+ if (!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
uint32_t i;
for (i = 0; i < ce->num_interfaces; i++) {
diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c
index a2da9d1fe3..6033fafebe 100644
--- a/Zend/zend_operators.c
+++ b/Zend/zend_operators.c
@@ -2303,7 +2303,7 @@ static zend_bool ZEND_FASTCALL instanceof_interface_only(const zend_class_entry
uint32_t i;
if (instance_ce->num_interfaces) {
- ZEND_ASSERT(instance_ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
+ ZEND_ASSERT(instance_ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
for (i = 0; i < instance_ce->num_interfaces; i++) {
if (instanceof_interface_only(instance_ce->interfaces[i], ce)) {
return 1;
@@ -2331,7 +2331,7 @@ static zend_bool ZEND_FASTCALL instanceof_interface(const zend_class_entry *inst
uint32_t i;
if (instance_ce->num_interfaces) {
- ZEND_ASSERT(instance_ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
+ ZEND_ASSERT(instance_ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
for (i = 0; i < instance_ce->num_interfaces; i++) {
if (instanceof_interface(instance_ce->interfaces[i], ce)) {
return 1;