}
/* }}} */
-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);
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");
}
}