From: Nikita Popov Date: Thu, 23 Jan 2020 11:55:28 +0000 (+0100) Subject: Merge branch 'PHP-7.4' X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9c23a50939851c3e82fdb0e85ac1fd781ecae167;p=php Merge branch 'PHP-7.4' * PHP-7.4: Fixed bug #79155 --- 9c23a50939851c3e82fdb0e85ac1fd781ecae167 diff --cc Zend/zend_compile.c index 2a82513016,5661f26439..2295b55e39 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@@ -5471,25 -5279,30 +5471,25 @@@ ZEND_API void zend_set_function_arg_fla } /* }}} */ -static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null) /* {{{ */ +static zend_type zend_compile_single_typename(zend_ast *ast) { - zend_bool allow_null = force_allow_null; - zend_ast_attr orig_ast_attr = ast->attr; - zend_type type; - if (ast->attr & ZEND_TYPE_NULLABLE) { - allow_null = 1; - ast->attr &= ~ZEND_TYPE_NULLABLE; - } - + ZEND_ASSERT(!(ast->attr & ZEND_TYPE_NULLABLE)); if (ast->kind == ZEND_AST_TYPE) { - return ZEND_TYPE_ENCODE(ast->attr, allow_null); + return (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, 0, 0); } else { zend_string *class_name = zend_ast_get_str(ast); - zend_uchar type = zend_lookup_builtin_type_by_name(class_name); + zend_uchar type_code = zend_lookup_builtin_type_by_name(class_name); - if (type != 0) { + if (type_code != 0) { if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) { zend_error_noreturn(E_COMPILE_ERROR, "Type declaration '%s' must be unqualified", ZSTR_VAL(zend_string_tolower(class_name))); } - return (zend_type) ZEND_TYPE_INIT_CODE(type, 0, 0); - type = ZEND_TYPE_ENCODE(type_code, allow_null); ++ return (zend_type) ZEND_TYPE_INIT_CODE(type_code, 0, 0); } else { + const char *correct_name; + zend_string *orig_name = zend_ast_get_str(ast); uint32_t fetch_type = zend_get_class_fetch_type_ast(ast); if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { class_name = zend_resolve_class_name_ast(ast); @@@ -5499,153 -5312,7 +5499,154 @@@ zend_string_addref(class_name); } - type = ZEND_TYPE_ENCODE_CLASS(class_name, allow_null); + if (ast->attr == ZEND_NAME_NOT_FQ + && zend_is_confusable_type(orig_name, &correct_name) + && zend_is_not_imported(orig_name)) { + const char *extra = + FC(current_namespace) ? " or import the class with \"use\"" : ""; + if (correct_name) { + zend_error(E_COMPILE_WARNING, + "\"%s\" will be interpreted as a class name. Did you mean \"%s\"? " + "Write \"\\%s\"%s to suppress this warning", + ZSTR_VAL(orig_name), correct_name, ZSTR_VAL(class_name), extra); + } else { + zend_error(E_COMPILE_WARNING, + "\"%s\" is not a supported builtin type " + "and will be interpreted as a class name. " + "Write \"\\%s\"%s to suppress this warning", + ZSTR_VAL(orig_name), ZSTR_VAL(class_name), extra); + } + } + + return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 0, 0); + } + } +} + +static zend_bool zend_type_contains_traversable(zend_type type) { + zend_type *single_type; + ZEND_TYPE_FOREACH(type, single_type) { + if (ZEND_TYPE_HAS_NAME(*single_type) + && zend_string_equals_literal_ci(ZEND_TYPE_NAME(*single_type), "Traversable")) { + return 1; + } + } ZEND_TYPE_FOREACH_END(); + return 0; +} + +// TODO: Ideally we'd canonicalize "iterable" into "array|Traversable" and essentially +// treat it as a built-in type alias. +static zend_type zend_compile_typename( + zend_ast *ast, zend_bool force_allow_null, zend_bool use_arena) /* {{{ */ +{ + zend_bool allow_null = force_allow_null; ++ zend_ast_attr orig_ast_attr = ast->attr; + zend_type type = ZEND_TYPE_INIT_NONE(0); + if (ast->attr & ZEND_TYPE_NULLABLE) { + allow_null = 1; + ast->attr &= ~ZEND_TYPE_NULLABLE; + } + + if (ast->kind == ZEND_AST_TYPE_UNION) { + zend_ast_list *list = zend_ast_get_list(ast); + for (uint32_t i = 0; i < list->children; i++) { + zend_ast *type_ast = list->child[i]; + zend_type single_type = zend_compile_single_typename(type_ast); + uint32_t type_mask_overlap = + ZEND_TYPE_PURE_MASK(type) & ZEND_TYPE_PURE_MASK(single_type); + if (type_mask_overlap) { + zend_type overlap_type = ZEND_TYPE_INIT_MASK(type_mask_overlap); + zend_string *overlap_type_str = zend_type_to_string(overlap_type); + zend_error_noreturn(E_COMPILE_ERROR, + "Duplicate type %s is redundant", ZSTR_VAL(overlap_type_str)); + } + ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(single_type); + ZEND_TYPE_FULL_MASK(single_type) &= ~_ZEND_TYPE_MAY_BE_MASK; + + if (ZEND_TYPE_HAS_CLASS(single_type)) { + if (!ZEND_TYPE_HAS_CLASS(type)) { + /* The first class type can be stored directly as the type ptr payload. */ + ZEND_TYPE_SET_PTR(type, ZEND_TYPE_NAME(single_type)); + ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_NAME_BIT; + } else { + zend_type_list *list; + if (ZEND_TYPE_HAS_LIST(type)) { + /* Add name to existing name list. */ + zend_type_list *old_list = ZEND_TYPE_LIST(type); + if (use_arena) { + // TODO: Add a zend_arena_realloc API? + list = zend_arena_alloc( + &CG(arena), ZEND_TYPE_LIST_SIZE(old_list->num_types + 1)); + memcpy(list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types)); + } else { + list = erealloc(old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types + 1)); + } + } else { + /* Switch from single name to name list. */ + size_t size = ZEND_TYPE_LIST_SIZE(2); + list = use_arena ? zend_arena_alloc(&CG(arena), size) : emalloc(size); + list->num_types = 1; + list->types[0] = type; + ZEND_TYPE_FULL_MASK(list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK; + } + + list->types[list->num_types++] = single_type; + ZEND_TYPE_SET_LIST(type, list); + if (use_arena) { + ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_ARENA_BIT; + } + + /* Check for trivially redundant class types */ + for (size_t i = 0; i < list->num_types - 1; i++) { + if (zend_string_equals_ci( + ZEND_TYPE_NAME(list->types[i]), ZEND_TYPE_NAME(single_type))) { + zend_string *single_type_str = zend_type_to_string(single_type); + zend_error_noreturn(E_COMPILE_ERROR, + "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); + } + } + } + } + } + } else { + type = zend_compile_single_typename(ast); + } + + if (allow_null) { + ZEND_TYPE_FULL_MASK(type) |= MAY_BE_NULL; + } + + uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); + if ((type_mask & (MAY_BE_ARRAY|MAY_BE_ITERABLE)) == (MAY_BE_ARRAY|MAY_BE_ITERABLE)) { + zend_string *type_str = zend_type_to_string(type); + zend_error_noreturn(E_COMPILE_ERROR, + "Type %s contains both iterable and array, which is redundant", ZSTR_VAL(type_str)); + } + + if ((type_mask & MAY_BE_ITERABLE) && zend_type_contains_traversable(type)) { + zend_string *type_str = zend_type_to_string(type); + zend_error_noreturn(E_COMPILE_ERROR, + "Type %s contains both iterable and Traversable, which is redundant", + ZSTR_VAL(type_str)); + } + + if ((type_mask & MAY_BE_OBJECT) && ZEND_TYPE_HAS_CLASS(type)) { + zend_string *type_str = zend_type_to_string(type); + zend_error_noreturn(E_COMPILE_ERROR, + "Type %s contains both object and a class type, which is redundant", + ZSTR_VAL(type_str)); + } + + if ((type_mask & MAY_BE_VOID) && (ZEND_TYPE_HAS_CLASS(type) || type_mask != MAY_BE_VOID)) { + zend_error_noreturn(E_COMPILE_ERROR, "Void can only be used as a standalone type"); + } + + if ((type_mask & (MAY_BE_NULL|MAY_BE_FALSE)) + && !ZEND_TYPE_HAS_CLASS(type) && !(type_mask & ~(MAY_BE_NULL|MAY_BE_FALSE))) { + if (type_mask == MAY_BE_NULL) { + zend_error_noreturn(E_COMPILE_ERROR, "Null can not be used as a standalone type"); + } else { + zend_error_noreturn(E_COMPILE_ERROR, "False can not be used as a standalone type"); } }