summaryrefslogtreecommitdiff
path: root/Zend
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-05-08 12:30:16 +0200
committerNikita Popov <nikita.ppv@gmail.com>2019-05-24 09:30:37 +0200
commit49a3b03e9fa3b6a7ef5302a48203b03e9d870ce9 (patch)
treee50a48fcc488996a3f86361fb22609e273c155e0 /Zend
parentafec3a9208c2a34762a87cb797f28e7e61c88cca (diff)
downloadphp-git-49a3b03e9fa3b6a7ef5302a48203b03e9d870ce9.tar.gz
Implement basic variance support
This is a minimal variance implementation: It does not support any cyclic type dependencies. Additionally the preloading requirements are much more restrictive than necessary. Hopefully we can relax these in the future.
Diffstat (limited to 'Zend')
-rw-r--r--Zend/tests/bug62441.phpt2
-rw-r--r--Zend/tests/return_types/008.phpt8
-rw-r--r--Zend/tests/return_types/generators003.phpt8
-rw-r--r--Zend/tests/return_types/inheritance005.phpt8
-rw-r--r--Zend/tests/return_types/inheritance006.phpt8
-rw-r--r--Zend/tests/return_types/inheritance007.phpt8
-rw-r--r--Zend/tests/type_declarations/variance/class_order.phpt19
-rw-r--r--Zend/tests/type_declarations/variance/class_order_autoload.phpt25
-rw-r--r--Zend/tests/type_declarations/variance/enum_forward_compat.phpt26
-rw-r--r--Zend/tests/type_declarations/variance/object_variance.phpt22
-rw-r--r--Zend/tests/type_declarations/variance/parent_in_class.phpt45
-rw-r--r--Zend/zend_inheritance.c205
-rw-r--r--Zend/zend_operators.c4
13 files changed, 299 insertions, 89 deletions
diff --git a/Zend/tests/bug62441.phpt b/Zend/tests/bug62441.phpt
index 581c0dad68..2b9f35cb30 100644
--- a/Zend/tests/bug62441.phpt
+++ b/Zend/tests/bug62441.phpt
@@ -16,4 +16,4 @@ namespace ns {
}
?>
--EXPECTF--
-Fatal error: Declaration of ns\Foo::method(ns\stdClass $o) must be compatible with Iface::method(stdClass $o) in %s on line %d
+Fatal error: Could not check compatibility between ns\Foo::method(ns\stdClass $o) and Iface::method(stdClass $o), because class ns\stdClass is not available in %s on line %d
diff --git a/Zend/tests/return_types/008.phpt b/Zend/tests/return_types/008.phpt
index a178607feb..d5178ebd9d 100644
--- a/Zend/tests/return_types/008.phpt
+++ b/Zend/tests/return_types/008.phpt
@@ -14,6 +14,8 @@ class qux implements foo {
}
$qux = new qux();
-var_dump($qux->bar());
---EXPECTF--
-Fatal error: Declaration of qux::bar(): qux must be compatible with foo::bar(): foo in %s008.php on line 8
+echo get_class($qux->bar());
+
+?>
+--EXPECT--
+qux
diff --git a/Zend/tests/return_types/generators003.phpt b/Zend/tests/return_types/generators003.phpt
index 489ef895a7..3b524eff2b 100644
--- a/Zend/tests/return_types/generators003.phpt
+++ b/Zend/tests/return_types/generators003.phpt
@@ -15,6 +15,8 @@ class SomeCollection implements Collection {
}
$some = new SomeCollection();
-var_dump($some->getIterator());
---EXPECTF--
-Fatal error: Declaration of SomeCollection::getIterator(): Generator must be compatible with Collection::getIterator(): Iterator in %sgenerators003.php on line 7
+echo get_class($some->getIterator());
+
+?>
+--EXPECT--
+Generator
diff --git a/Zend/tests/return_types/inheritance005.phpt b/Zend/tests/return_types/inheritance005.phpt
index 6d8dfc8da9..9793051da8 100644
--- a/Zend/tests/return_types/inheritance005.phpt
+++ b/Zend/tests/return_types/inheritance005.phpt
@@ -13,5 +13,9 @@ class Bar extends Foo {
return new Bar;
}
}
---EXPECTF--
-Fatal error: Declaration of Bar::test(): Bar must be compatible with Foo::test(): Foo in %sinheritance005.php on line 9
+
+echo get_class(Bar::test());
+
+?>
+--EXPECT--
+Bar
diff --git a/Zend/tests/return_types/inheritance006.phpt b/Zend/tests/return_types/inheritance006.phpt
index 65cb1d2f72..8b7a37a1f4 100644
--- a/Zend/tests/return_types/inheritance006.phpt
+++ b/Zend/tests/return_types/inheritance006.phpt
@@ -17,5 +17,9 @@ class Bar extends Foo {
return new B;
}
}
---EXPECTF--
-Fatal error: Declaration of Bar::test(): B must be compatible with Foo::test(): A in %sinheritance006.php on line 11
+
+echo get_class(Bar::test());
+
+?>
+--EXPECT--
+B
diff --git a/Zend/tests/return_types/inheritance007.phpt b/Zend/tests/return_types/inheritance007.phpt
index d04ef71b1c..b08aaf9e99 100644
--- a/Zend/tests/return_types/inheritance007.phpt
+++ b/Zend/tests/return_types/inheritance007.phpt
@@ -15,5 +15,9 @@ class Bar extends Foo {
return new ArrayObject([1, 2]);
}
}
---EXPECTF--
-Fatal error: Declaration of Bar::test(): ArrayObject must be compatible with Foo::test(): Traversable in %sinheritance007.php on line 9
+
+echo get_class(Bar::test());
+
+?>
+--EXPECT--
+ArrayObject
diff --git a/Zend/tests/type_declarations/variance/class_order.phpt b/Zend/tests/type_declarations/variance/class_order.phpt
new file mode 100644
index 0000000000..df66e78b78
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Returns are covariant, but we don't allow the code due to class ordering
+--FILE--
+<?php
+
+class A {
+ public function method() : B {}
+}
+class B extends A {
+ public function method() : C {}
+}
+class C extends B {
+}
+
+new C;
+
+?>
+--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
diff --git a/Zend/tests/type_declarations/variance/class_order_autoload.phpt b/Zend/tests/type_declarations/variance/class_order_autoload.phpt
new file mode 100644
index 0000000000..a4ea534577
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/class_order_autoload.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Returns are covariant, but we don't allow the code due to class ordering (autoload variation)
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ if ($class === 'A') {
+ class A {
+ public function method() : B {}
+ }
+ } else if ($class == 'B') {
+ class B extends A {
+ public function method() : C {}
+ }
+ } else {
+ class C extends B {
+ }
+ }
+});
+
+$c = new C;
+
+?>
+--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
diff --git a/Zend/tests/type_declarations/variance/enum_forward_compat.phpt b/Zend/tests/type_declarations/variance/enum_forward_compat.phpt
new file mode 100644
index 0000000000..3213aaaf76
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/enum_forward_compat.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Forward compatibility with types that look like classes but aren't
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+ var_dump($class);
+ if ($class === 'X') {
+ class X {}
+ } else {
+ class Y {}
+ }
+});
+
+class A {
+ public function method(X $param) : object {}
+}
+
+class B extends A {
+ public function method(object $param) : Y {}
+}
+
+?>
+--EXPECT--
+string(1) "X"
+string(1) "Y"
diff --git a/Zend/tests/type_declarations/variance/object_variance.phpt b/Zend/tests/type_declarations/variance/object_variance.phpt
new file mode 100644
index 0000000000..3ab0a311bf
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/object_variance.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Testing object's variance in inheritance
+--FILE--
+<?php
+
+interface I1 {
+ function method1(I1 $o): object;
+}
+interface I2 extends I1 {
+ function method1(object $o): I1;
+}
+final class C1 implements I2 {
+ function method1($o = null): self {
+ return $this;
+ }
+}
+
+$o = new C1();
+echo get_class($o->method1());
+?>
+--EXPECT--
+C1
diff --git a/Zend/tests/type_declarations/variance/parent_in_class.phpt b/Zend/tests/type_declarations/variance/parent_in_class.phpt
new file mode 100644
index 0000000000..88b711bddd
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/parent_in_class.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Use of parent inside a class that has / has no parent
+--FILE--
+<?php
+
+// Illegal: A::parent is ill-defined
+class A {
+ public function method(parent $x) {}
+}
+class B extends A {
+ public function method(parent $x) {}
+}
+
+// Legal: A2::parent == P2
+class P2 {}
+class A2 extends P2 {
+ public function method(parent $x) {}
+}
+class B2 extends A2 {
+ public function method(P2 $x) {}
+}
+
+// Legal: B3::parent == A3 is subclass of A3::parent == P3 in covariant position
+class P3 {}
+class A3 extends P3 {
+ public function method($x): parent {}
+}
+class B3 extends A3 {
+ public function method($x): parent {}
+}
+
+// Illegal: B4::parent == A4 is subclass of A4::parent == P4 in contravariant position
+class P4 {}
+class A4 extends P4 {
+ public function method(parent $x) {}
+}
+class B4 extends A4 {
+ public function method(parent $x) {}
+}
+
+?>
+--EXPECTF--
+Warning: Declaration of B4::method(A4 $x) should be compatible with A4::method(P4 $x) in %s on line %d
+
+Warning: Could not check compatibility between B::method(A $x) and A::method(parent $x), because class parent is not available in %s on line %d
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index eb3886268f..36d765386d 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -22,6 +22,7 @@
#include "zend_compile.h"
#include "zend_execute.h"
#include "zend_inheritance.h"
+#include "zend_interfaces.h"
#include "zend_smart_str.h"
#include "zend_operators.h"
@@ -173,7 +174,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) {
+ if (ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS)) {
return ce->parent->name;
} else {
return ce->parent_name;
@@ -198,16 +199,18 @@ 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)) {
- return zend_lookup_class(name);
- }
-
- ce = zend_lookup_class_ex(name, NULL, /* autoload */ 0);
- if (ce && class_visible(ce)) {
- return ce;
+ ce = zend_lookup_class(name);
+ if (ce) {
+ return ce;
+ }
+ } else {
+ ce = zend_lookup_class_ex(name, NULL, /* autoload */ 0);
+ if (ce && class_visible(ce)) {
+ return ce;
+ }
}
- /* When checking whether early binding is possible, the current class will not be registered
- * yet, so check for it explicitly. */
+ /* 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;
}
@@ -215,6 +218,15 @@ static zend_class_entry *lookup_class(const zend_function *fe, zend_string *name
return NULL;
}
+/* Instanceof that's safe to use on unlinked classes. For the unlinked case, we only handle
+ * class identity here. */
+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))) {
+ return instanceof_function(ce1, ce2);
+ }
+ return ce1 == ce2;
+}
+
/* Unresolved means that class declarations that are currently not available are needed to
* determine whether the inheritance is valid or not. At runtime UNRESOLVED should be treated
* as an ERROR. */
@@ -225,6 +237,7 @@ typedef enum {
} inheritance_status;
static inheritance_status zend_perform_covariant_type_check(
+ zend_string **unresolved_class,
const zend_function *fe, zend_arg_info *fe_arg_info,
const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */
{
@@ -235,52 +248,72 @@ static inheritance_status zend_perform_covariant_type_check(
return INHERITANCE_ERROR;
}
- if (ZEND_TYPE_IS_CLASS(fe_type) && ZEND_TYPE_IS_CLASS(proto_type)) {
- zend_string *fe_class_name = resolve_class_name(fe, ZEND_TYPE_NAME(fe_type));
- zend_string *proto_class_name = resolve_class_name(proto, ZEND_TYPE_NAME(proto_type));
-
- if (fe_class_name != proto_class_name && strcasecmp(ZSTR_VAL(fe_class_name), ZSTR_VAL(proto_class_name)) != 0) {
- if (fe->common.type != ZEND_USER_FUNCTION) {
- return INHERITANCE_ERROR;
- } else {
- /* Check for class alias */
- zend_class_entry *fe_ce, *proto_ce;
+ if (ZEND_TYPE_IS_CLASS(proto_type)) {
+ zend_string *fe_class_name, *proto_class_name;
+ zend_class_entry *fe_ce, *proto_ce;
+ if (!ZEND_TYPE_IS_CLASS(fe_type)) {
+ return INHERITANCE_ERROR;
+ }
- fe_ce = lookup_class(fe, fe_class_name);
- proto_ce = lookup_class(proto, proto_class_name);
+ fe_class_name = resolve_class_name(fe, ZEND_TYPE_NAME(fe_type));
+ proto_class_name = resolve_class_name(proto, ZEND_TYPE_NAME(proto_type));
+ if (zend_string_equals_ci(fe_class_name, proto_class_name)) {
+ return INHERITANCE_SUCCESS;
+ }
- if (!fe_ce || !proto_ce) {
- return INHERITANCE_UNRESOLVED;
- }
+ fe_ce = lookup_class(fe, fe_class_name);
+ if (!fe_ce) {
+ *unresolved_class = fe_class_name;
+ return INHERITANCE_UNRESOLVED;
+ }
- if (fe_ce->type == ZEND_INTERNAL_CLASS ||
- proto_ce->type == ZEND_INTERNAL_CLASS ||
- fe_ce != proto_ce) {
- return INHERITANCE_ERROR;
- }
- }
+ proto_ce = lookup_class(proto, proto_class_name);
+ if (!proto_ce) {
+ *unresolved_class = proto_class_name;
+ return INHERITANCE_UNRESOLVED;
}
- } else if (ZEND_TYPE_CODE(fe_type) != ZEND_TYPE_CODE(proto_type)) {
- if (ZEND_TYPE_CODE(proto_type) == IS_ITERABLE) {
- if (ZEND_TYPE_CODE(fe_type) == IS_ARRAY) {
- return INHERITANCE_SUCCESS;
- }
- if (ZEND_TYPE_IS_CLASS(fe_type) &&
- zend_string_equals_literal_ci(ZEND_TYPE_NAME(fe_type), "Traversable")) {
- return INHERITANCE_SUCCESS;
+ return unlinked_instanceof(fe_ce, proto_ce) ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
+ } else if (ZEND_TYPE_CODE(proto_type) == IS_ITERABLE) {
+ if (ZEND_TYPE_IS_CLASS(fe_type)) {
+ zend_string *fe_class_name = resolve_class_name(fe, ZEND_TYPE_NAME(fe_type));
+ zend_class_entry *fe_ce = lookup_class(fe, fe_class_name);
+ if (!fe_ce) {
+ *unresolved_class = fe_class_name;
+ return INHERITANCE_UNRESOLVED;
+ }
+ return unlinked_instanceof(fe_ce, zend_ce_traversable)
+ ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
+ }
+
+ return ZEND_TYPE_CODE(fe_type) == IS_ITERABLE || ZEND_TYPE_CODE(fe_type) == IS_ARRAY
+ ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
+ } else if (ZEND_TYPE_CODE(proto_type) == IS_OBJECT) {
+ if (ZEND_TYPE_IS_CLASS(fe_type)) {
+ /* Currently, any class name would be allowed here. We still perform a class lookup
+ * for forward-compatibility reasons, as we may have named types in the future that
+ * are not classes (such as enums or typedefs). */
+ zend_string *fe_class_name = resolve_class_name(fe, ZEND_TYPE_NAME(fe_type));
+ zend_class_entry *fe_ce = lookup_class(fe, fe_class_name);
+ if (!fe_ce) {
+ *unresolved_class = fe_class_name;
+ return INHERITANCE_UNRESOLVED;
}
+ return INHERITANCE_SUCCESS;
}
- /* Incompatible built-in types */
- return INHERITANCE_ERROR;
+ return ZEND_TYPE_CODE(fe_type) == IS_OBJECT ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
+ } else {
+ return ZEND_TYPE_CODE(fe_type) == ZEND_TYPE_CODE(proto_type)
+ ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
}
-
- return INHERITANCE_SUCCESS;
}
/* }}} */
-static inheritance_status zend_do_perform_arg_type_hint_check(const zend_function *fe, zend_arg_info *fe_arg_info, const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */
+static inheritance_status zend_do_perform_arg_type_hint_check(
+ zend_string **unresolved_class,
+ const zend_function *fe, zend_arg_info *fe_arg_info,
+ const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */
{
if (!ZEND_TYPE_IS_SET(fe_arg_info->type)) {
/* Child with no type is always compatible */
@@ -294,11 +327,13 @@ static inheritance_status zend_do_perform_arg_type_hint_check(const zend_functio
/* Contravariant type check is performed as a covariant type check with swapped
* argument order. */
- return zend_perform_covariant_type_check(proto, proto_arg_info, fe, fe_arg_info);
+ return zend_perform_covariant_type_check(
+ unresolved_class, proto, proto_arg_info, fe, fe_arg_info);
}
/* }}} */
-static inheritance_status zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto) /* {{{ */
+static inheritance_status zend_do_perform_implementation_check(
+ zend_string **unresolved_class, const zend_function *fe, const zend_function *proto) /* {{{ */
{
uint32_t i, num_args;
inheritance_status status, local_status;
@@ -368,7 +403,8 @@ static inheritance_status zend_do_perform_implementation_check(const zend_functi
proto_arg_info = &proto->common.arg_info[proto->common.num_args];
}
- local_status = zend_do_perform_arg_type_hint_check(fe, fe_arg_info, proto, proto_arg_info);
+ local_status = zend_do_perform_arg_type_hint_check(
+ unresolved_class, fe, fe_arg_info, proto, proto_arg_info);
if (local_status == INHERITANCE_ERROR) {
return INHERITANCE_ERROR;
}
@@ -390,7 +426,8 @@ static inheritance_status zend_do_perform_implementation_check(const zend_functi
return INHERITANCE_ERROR;
}
- local_status = zend_perform_covariant_type_check(fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1);
+ local_status = zend_perform_covariant_type_check(
+ unresolved_class, fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1);
if (local_status == INHERITANCE_ERROR) {
return INHERITANCE_ERROR;
}
@@ -568,10 +605,30 @@ static zend_always_inline uint32_t func_lineno(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,
+ 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);
+ if (status == INHERITANCE_UNRESOLVED) {
+ zend_error_at(error_level, NULL, func_lineno(child),
+ "Could not check compatibility between %s and %s, because class %s is not available",
+ ZSTR_VAL(child_prototype), ZSTR_VAL(parent_prototype), ZSTR_VAL(unresolved_class));
+ } else {
+ zend_error_at(error_level, NULL, func_lineno(child),
+ "Declaration of %s %s be compatible with %s",
+ ZSTR_VAL(child_prototype), error_verb, ZSTR_VAL(parent_prototype));
+ }
+ zend_string_efree(child_prototype);
+ zend_string_efree(parent_prototype);
+}
+
static void do_inheritance_check_on_method(zend_function *child, zend_function *parent, zend_class_entry *ce, zval *child_zv) /* {{{ */
{
uint32_t child_flags;
uint32_t parent_flags = parent->common.fn_flags;
+ inheritance_status status;
+ zend_string *unresolved_class;
if (UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) {
zend_error_at_noreturn(E_COMPILE_ERROR, NULL, func_lineno(child),
@@ -650,12 +707,10 @@ 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");
}
- if (UNEXPECTED(zend_do_perform_implementation_check(child, parent) != INHERITANCE_SUCCESS)) {
+ status = zend_do_perform_implementation_check(&unresolved_class, child, parent);
+ if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
int error_level;
const char *error_verb;
- zend_string *method_prototype = zend_get_function_declaration(parent);
- zend_string *child_prototype = zend_get_function_declaration(child);
-
if (child->common.prototype && (
child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT
)) {
@@ -663,18 +718,15 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
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(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) != INHERITANCE_SUCCESS)) {
+ 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";
}
- zend_error_at(error_level, NULL, func_lineno(child),
- "Declaration of %s %s be compatible with %s",
- ZSTR_VAL(child_prototype), error_verb, ZSTR_VAL(method_prototype));
- zend_string_efree(child_prototype);
- zend_string_efree(method_prototype);
+ emit_incompatible_method_error(
+ error_level, error_verb, child, parent, status, unresolved_class);
}
}
} while (0);
@@ -1383,6 +1435,8 @@ static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_s
{
zend_function *existing_fn = NULL;
zend_function *new_fn;
+ zend_string *unresolved_class;
+ inheritance_status status;
if ((existing_fn = zend_hash_find_ptr(&ce->function_table, key)) != NULL) {
/* if it is the same function with the same visibility and has not been assigned a class scope yet, regardless
@@ -1400,18 +1454,20 @@ 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 */
- if (UNEXPECTED(zend_do_perform_implementation_check(fn, existing_fn) != INHERITANCE_SUCCESS)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
- ZSTR_VAL(zend_get_function_declaration(fn)),
- ZSTR_VAL(zend_get_function_declaration(existing_fn)));
+ status = zend_do_perform_implementation_check(
+ &unresolved_class, fn, existing_fn);
+ if (status != INHERITANCE_SUCCESS) {
+ 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 */
- if (UNEXPECTED(zend_do_perform_implementation_check(existing_fn, fn) != INHERITANCE_SUCCESS)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
- ZSTR_VAL(zend_get_function_declaration(existing_fn)),
- ZSTR_VAL(zend_get_function_declaration(fn)));
+ status = zend_do_perform_implementation_check(
+ &unresolved_class, existing_fn, fn);
+ if (status != INHERITANCE_SUCCESS) {
+ emit_incompatible_method_error(
+ E_COMPILE_ERROR, "must", existing_fn, fn, status, unresolved_class);
}
return;
}
@@ -1425,17 +1481,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 */
- if (UNEXPECTED(zend_do_perform_implementation_check(fn, existing_fn) != INHERITANCE_SUCCESS)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
- ZSTR_VAL(zend_get_function_declaration(fn)),
- ZSTR_VAL(zend_get_function_declaration(existing_fn)));
+ status = zend_do_perform_implementation_check(&unresolved_class, fn, existing_fn);
+ if (status != INHERITANCE_SUCCESS) {
+ 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 */
- if (UNEXPECTED(zend_do_perform_implementation_check(existing_fn, fn) != INHERITANCE_SUCCESS)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
- ZSTR_VAL(zend_get_function_declaration(existing_fn)),
- ZSTR_VAL(zend_get_function_declaration(fn)));
+ status = zend_do_perform_implementation_check(&unresolved_class, existing_fn, fn);
+ if (status != INHERITANCE_SUCCESS) {
+ emit_incompatible_method_error(
+ E_COMPILE_ERROR, "must", existing_fn, fn, status, unresolved_class);
}
return;
} else if (UNEXPECTED(existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT)) {
@@ -2102,12 +2158,13 @@ zend_bool zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce)
ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent_func) {
uint32_t parent_flags = parent_func->common.fn_flags;
zend_function *func = zend_hash_find_ptr(&ce->function_table, key);
+ zend_string *unresolved_class;
if (!func || (parent_flags & ZEND_ACC_PRIVATE) ||
((parent_flags & ZEND_ACC_CTOR) && !(parent_flags & ZEND_ACC_ABSTRACT))
) {
continue;
}
- if (zend_do_perform_implementation_check(func, parent_func) == INHERITANCE_UNRESOLVED) {
+ if (zend_do_perform_implementation_check(&unresolved_class, func, parent_func) == INHERITANCE_UNRESOLVED) {
return 0;
}
} ZEND_HASH_FOREACH_END();
diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c
index 170599be74..7b25eb1d52 100644
--- a/Zend/zend_operators.c
+++ b/Zend/zend_operators.c
@@ -2260,7 +2260,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_ASSERT(instance_ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
for (i = 0; i < instance_ce->num_interfaces; i++) {
if (instanceof_interface_only(instance_ce->interfaces[i], ce)) {
return 1;
@@ -2288,7 +2288,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_ASSERT(instance_ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
for (i = 0; i < instance_ce->num_interfaces; i++) {
if (instanceof_interface(instance_ce->interfaces[i], ce)) {
return 1;