From 270e5e3c5b7b6bf39dfbef67381990b553a52a96 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 11 Sep 2019 16:27:28 +0200 Subject: [PATCH] Only allow "nearly linked" classes for parent/interface The requirements for parent/interface are difference than for the variance checks in type declarations. The latter can work on fully unlinked classes, but the former need inheritance to be essentially finished, only variance checks may still be outstanding. Adding a new flag for this because we have lots of space, but we could also represent these "inheritance states" more compactly in the future. --- Zend/tests/bug30922.phpt | 2 +- .../variance/unlinked_parent_1.phpt | 15 +++++++++++++++ .../variance/unlinked_parent_2.phpt | 15 +++++++++++++++ Zend/zend_compile.h | 6 +++++- Zend/zend_execute_API.c | 8 ++++++-- Zend/zend_inheritance.c | 10 +++++----- 6 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 Zend/tests/type_declarations/variance/unlinked_parent_1.phpt create mode 100644 Zend/tests/type_declarations/variance/unlinked_parent_2.phpt diff --git a/Zend/tests/bug30922.phpt b/Zend/tests/bug30922.phpt index 001845a7cb..444758c903 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 cannot implement itself in %s on line %d +Fatal error: Interface 'RecurisiveFooFar' not found in %s on line %d diff --git a/Zend/tests/type_declarations/variance/unlinked_parent_1.phpt b/Zend/tests/type_declarations/variance/unlinked_parent_1.phpt new file mode 100644 index 0000000000..2030f3bd8b --- /dev/null +++ b/Zend/tests/type_declarations/variance/unlinked_parent_1.phpt @@ -0,0 +1,15 @@ +--TEST-- +Using an unlinked parent class +--FILE-- + +--EXPECTF-- +Fatal error: Class 'B' not found in %s on line %d diff --git a/Zend/tests/type_declarations/variance/unlinked_parent_2.phpt b/Zend/tests/type_declarations/variance/unlinked_parent_2.phpt new file mode 100644 index 0000000000..9e21f85770 --- /dev/null +++ b/Zend/tests/type_declarations/variance/unlinked_parent_2.phpt @@ -0,0 +1,15 @@ +--TEST-- +Using an unlinked parent interface +--FILE-- + +--EXPECTF-- +Fatal error: Interface 'B' not found in %s on line %d diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 3e2a9cbe96..2a8ac77145 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -229,7 +229,7 @@ typedef struct _zend_oparray_context { /* op_array or class is preloaded | | | */ #define ZEND_ACC_PRELOADED (1 << 10) /* X | X | | */ /* | | | */ -/* Class Flags (unused: 22...) | | | */ +/* Class Flags (unused: 23...) | | | */ /* =========== | | | */ /* | | | */ /* Special class types | | | */ @@ -278,6 +278,9 @@ typedef struct _zend_oparray_context { /* Class has unresolved variance obligations. | | | */ #define ZEND_ACC_UNRESOLVED_VARIANCE (1 << 21) /* X | | | */ /* | | | */ +/* Class is linked apart from variance obligations. | | | */ +#define ZEND_ACC_NEARLY_LINKED (1 << 22) /* X | | | */ +/* | | | */ /* Function Flags (unused: 23, 26) | | | */ /* ============== | | | */ /* | | | */ @@ -864,6 +867,7 @@ void zend_assert_valid_class_name(const zend_string *const_name); #define ZEND_FETCH_CLASS_SILENT 0x0100 #define ZEND_FETCH_CLASS_EXCEPTION 0x0200 #define ZEND_FETCH_CLASS_ALLOW_UNLINKED 0x0400 +#define ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED 0x0800 #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 4f7f54f893..ddef72ebd3 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -917,8 +917,12 @@ 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)) && - !(flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED)) { + if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) { + if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) || + ((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) && + (ce->ce_flags & ZEND_ACC_NEARLY_LINKED))) { + return ce; + } return NULL; } return ce; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 9c8acca865..a108b2e85e 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1005,9 +1005,8 @@ static inline void do_implement_interface(zend_class_entry *ce, zend_class_entry if (!(ce->ce_flags & ZEND_ACC_INTERFACE) && iface->interface_gets_implemented && iface->interface_gets_implemented(iface, ce) == FAILURE) { zend_error_noreturn(E_CORE_ERROR, "Class %s could not implement interface %s", ZSTR_VAL(ce->name), ZSTR_VAL(iface->name)); } - if (UNEXPECTED(ce == iface)) { - zend_error_noreturn(E_ERROR, "Interface %s cannot implement itself", ZSTR_VAL(ce->name)); - } + /* This should be prevented by the class lookup logic. */ + ZEND_ASSERT(ce != iface); } /* }}} */ @@ -1457,7 +1456,7 @@ 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|ZEND_FETCH_CLASS_ALLOW_UNLINKED); + ZEND_FETCH_CLASS_INTERFACE|ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED); if (!(iface->ce_flags & ZEND_ACC_LINKED)) { add_dependency_obligation(ce, iface); } @@ -2404,7 +2403,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na { if (ce->parent_name) { zend_class_entry *parent = zend_fetch_class_by_name( - ce->parent_name, lc_parent_name, ZEND_FETCH_CLASS_ALLOW_UNLINKED); + ce->parent_name, lc_parent_name, ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED); if (!(parent->ce_flags & ZEND_ACC_LINKED)) { add_dependency_obligation(ce, parent); } @@ -2427,6 +2426,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na return; } + ce->ce_flags |= ZEND_ACC_NEARLY_LINKED; load_delayed_classes(); if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) { resolve_delayed_variance_obligations(ce); -- 2.40.0