]> granicus.if.org Git - php/commitdiff
Merge branch 'PHP-7.4'
authorNikita Popov <nikita.ppv@gmail.com>
Thu, 23 Jan 2020 11:55:28 +0000 (12:55 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 23 Jan 2020 11:55:59 +0000 (12:55 +0100)
* PHP-7.4:
  Fixed bug #79155

1  2 
Zend/zend_compile.c

index 2a82513016bc673e2742a1bcbba0bc62b3e677e6,5661f26439663e513ac00a5a84a5505355332e49..2295b55e39a0ce2c7e163cf115fbf6d374fd5fb2
@@@ -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);
                                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");
                }
        }