From: Nikita Popov Date: Thu, 17 Oct 2019 11:42:47 +0000 (+0200) Subject: Merge branch 'PHP-7.4' X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9a634a9c6b71d4efcd07378caf2ec01eddd3e82a;p=php Merge branch 'PHP-7.4' * PHP-7.4: Integrate property types with variance system --- 9a634a9c6b71d4efcd07378caf2ec01eddd3e82a diff --cc Zend/zend_inheritance.c index 2ac73f617f,cc85af6859..5f9389ed6e --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@@ -29,7 -29,11 +29,10 @@@ 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); + zend_class_entry *ce, const zend_function *child_fn, const zend_function *parent_fn); + static void add_property_compatibility_obligation( + zend_class_entry *ce, const zend_property_info *child_prop, + const zend_property_info *parent_prop); static void overridden_ptr_dtor(zval *zv) /* {{{ */ { @@@ -339,10 -342,11 +341,11 @@@ static inheritance_status zend_perform_ } return unlinked_instanceof(fe_ce, proto_ce) ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; - } else if (ZEND_TYPE_CODE(proto_type) == IS_ITERABLE) { + } else if (ZEND_TYPE_MASK(proto_type) & MAY_BE_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); + zend_string *fe_class_name = + resolve_class_name(fe->common.scope, ZEND_TYPE_NAME(fe_type)); + zend_class_entry *fe_ce = lookup_class(fe->common.scope, fe_class_name); if (!fe_ce) { *unresolved_class = fe_class_name; return INHERITANCE_UNRESOLVED; @@@ -872,11 -912,27 +860,25 @@@ inheritance_status property_types_compa /* Check for class aliases */ parent_type_ce = ZEND_TYPE_IS_CE(parent_info->type) ? ZEND_TYPE_CE(parent_info->type) - : zend_lookup_class(parent_name); + : lookup_class(parent_info->ce, parent_name); child_type_ce = ZEND_TYPE_IS_CE(child_info->type) ? ZEND_TYPE_CE(child_info->type) - : zend_lookup_class(child_name); - return parent_type_ce && child_type_ce && parent_type_ce == child_type_ce; + : lookup_class(child_info->ce, child_name); + if (!parent_type_ce || !child_type_ce) { + return INHERITANCE_UNRESOLVED; + } + return parent_type_ce == child_type_ce ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; + } + + static void emit_incompatible_property_error( + const zend_property_info *child, const zend_property_info *parent) { ++ zend_string *type_str = zend_type_to_string_resolved(parent->type, parent->ce); + zend_error_noreturn(E_COMPILE_ERROR, - "Type of %s::$%s must be %s%s (as in class %s)", ++ "Type of %s::$%s must be %s (as in class %s)", + ZSTR_VAL(child->ce->name), + ZSTR_VAL(child->name), - ZEND_TYPE_ALLOW_NULL(parent->type) ? "?" : "", - ZEND_TYPE_IS_CLASS(parent->type) - ? ZSTR_VAL(ZEND_TYPE_IS_CE(parent->type) ? ZEND_TYPE_CE(parent->type)->name : resolve_class_name(parent->ce, ZEND_TYPE_NAME(parent->type))) - : zend_get_type_by_const(ZEND_TYPE_CODE(parent->type)), ++ ZSTR_VAL(type_str), + ZSTR_VAL(parent->ce->name)); } static void do_inherit_property(zend_property_info *parent_info, zend_string *key, zend_class_entry *ce) /* {{{ */ @@@ -2132,7 -2233,12 +2135,11 @@@ typedef struct struct { const zend_function *parent_fn; const zend_function *child_fn; - zend_bool always_error; }; + struct { + const zend_property_info *parent_prop; + const zend_property_info *child_prop; + }; }; } variance_obligation; @@@ -2205,10 -2325,22 +2223,21 @@@ static int check_variance_obligation(zv return ZEND_HASH_APPLY_KEEP; } ZEND_ASSERT(status == INHERITANCE_ERROR); - emit_incompatible_method_error_or_warning( - obligation->child_fn, obligation->parent_fn, status, unresolved_class, - obligation->always_error); + emit_incompatible_method_error( + obligation->child_fn, obligation->parent_fn, status, unresolved_class); } /* Either the compatibility check was successful or only threw a warning. */ + } else { + ZEND_ASSERT(obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY); + inheritance_status status = + property_types_compatible(obligation->parent_prop, obligation->child_prop); + if (status != INHERITANCE_SUCCESS) { + if (status == INHERITANCE_UNRESOLVED) { + return ZEND_HASH_APPLY_KEEP; + } + ZEND_ASSERT(status == INHERITANCE_ERROR); + emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop); + } } return ZEND_HASH_APPLY_REMOVE; } @@@ -2261,15 -2393,19 +2290,18 @@@ static void report_variance_errors(zend 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( - obligation->child_fn, obligation->parent_fn, status, unresolved_class); + if (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); ++ emit_incompatible_method_error( ++ obligation->child_fn, obligation->parent_fn, status, unresolved_class); + } else if (obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY) { + emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop); + } else { + zend_error_noreturn(E_CORE_ERROR, "Bug #78647"); + } } ZEND_HASH_FOREACH_END(); /* Only warnings were thrown above -- that means that there are incompatibilities, but only @@@ -2377,8 -2513,10 +2409,9 @@@ ZEND_API int zend_do_link_class(zend_cl /* Check whether early binding is prevented due to unresolved types in inheritance checks. */ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */ { - inheritance_status ret = INHERITANCE_SUCCESS; zend_string *key; zend_function *parent_func; + zend_property_info *parent_info; ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent_func) { zval *zv = zend_hash_find_ex(&ce->function_table, key, 1); @@@ -2393,7 -2535,29 +2426,25 @@@ } } ZEND_HASH_FOREACH_END(); + ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->properties_info, key, parent_info) { + zval *zv; + if ((parent_info->flags & ZEND_ACC_PRIVATE) || !ZEND_TYPE_IS_SET(parent_info->type)) { + continue; + } + + zv = zend_hash_find_ex(&ce->properties_info, key, 1); + if (zv) { + zend_property_info *child_info = Z_PTR_P(zv); + if (ZEND_TYPE_IS_SET(child_info->type)) { + inheritance_status status = property_types_compatible(parent_info, child_info); + if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { - if (EXPECTED(status == INHERITANCE_UNRESOLVED)) { - return INHERITANCE_UNRESOLVED; - } - ZEND_ASSERT(status == INHERITANCE_ERROR); - ret = INHERITANCE_ERROR; ++ return status; + } + } + } + } ZEND_HASH_FOREACH_END(); + - return ret; + return INHERITANCE_SUCCESS; } /* }}} */