Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Optional interfaces #17288

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Zend/zend.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ typedef struct _zend_class_name {
zend_string *lc_name;
} zend_class_name;

typedef struct _zend_interface_name {
zend_string *name;
zend_string *lc_name;
bool is_optional;
} zend_interface_name;

typedef struct _zend_trait_method_reference {
zend_string *method_name;
zend_string *class_name;
Expand Down Expand Up @@ -210,7 +216,7 @@ struct _zend_class_entry {
/* class_entry or string(s) depending on ZEND_ACC_LINKED */
union {
zend_class_entry **interfaces;
zend_class_name *interface_names;
zend_interface_name *interface_names;
};

zend_class_name *trait_names;
Expand Down
8 changes: 5 additions & 3 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -8039,12 +8039,13 @@ static void add_stringable_interface(zend_class_entry *ce) {

ce->num_interfaces++;
ce->interface_names =
erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
erealloc(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);
// TODO: Add known interned strings instead?
ce->interface_names[ce->num_interfaces - 1].name =
ZSTR_INIT_LITERAL("Stringable", 0);
ce->interface_names[ce->num_interfaces - 1].lc_name =
ZSTR_INIT_LITERAL("stringable", 0);
ce->interface_names[ce->num_interfaces - 1].is_optional = false;
}

static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, bool has_body) /* {{{ */
Expand Down Expand Up @@ -8950,16 +8951,17 @@ static void zend_compile_implements(zend_ast *ast) /* {{{ */
{
zend_ast_list *list = zend_ast_get_list(ast);
zend_class_entry *ce = CG(active_class_entry);
zend_class_name *interface_names;
zend_interface_name *interface_names;
uint32_t i;

interface_names = emalloc(sizeof(zend_class_name) * list->children);
interface_names = emalloc(sizeof(zend_interface_name) * list->children);

for (i = 0; i < list->children; ++i) {
zend_ast *class_ast = list->child[i];
interface_names[i].name =
zend_resolve_const_class_name_reference(class_ast, "interface name");
interface_names[i].lc_name = zend_string_tolower(interface_names[i].name);
interface_names[i].is_optional = ZEND_CLASS_NAME_OPTIONAL & class_ast->attr;
}

ce->num_interfaces = list->children;
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
#define ZEND_NAME_NOT_FQ 1
#define ZEND_NAME_RELATIVE 2

#define ZEND_CLASS_NAME_OPTIONAL 4

/* ZEND_FETCH_ flags in class name AST of new const expression must not clash with ZEND_NAME_ flags */
#define ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT 2

Expand Down
4 changes: 3 additions & 1 deletion Zend/zend_enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,16 @@ void zend_enum_add_interfaces(zend_class_entry *ce)

ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES));

ce->interface_names = erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
ce->interface_names = erealloc(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);

ce->interface_names[num_interfaces_before].name = zend_string_copy(zend_ce_unit_enum->name);
ce->interface_names[num_interfaces_before].lc_name = ZSTR_INIT_LITERAL("unitenum", 0);
ce->interface_names[num_interfaces_before].is_optional = false;

if (ce->enum_backing_type != IS_UNDEF) {
ce->interface_names[num_interfaces_before + 1].name = zend_string_copy(zend_ce_backed_enum->name);
ce->interface_names[num_interfaces_before + 1].lc_name = ZSTR_INIT_LITERAL("backedenum", 0);
ce->interface_names[num_interfaces_before + 1].is_optional = false;
}

ce->default_object_handlers = &zend_enum_object_handlers;
Expand Down
13 changes: 11 additions & 2 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -3513,23 +3513,32 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
}
}

size_t num_implementable_interfaces = 0;
if (ce->num_interfaces) {
for (i = 0; i < ce->num_interfaces; i++) {
zend_class_entry *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_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION);
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION |
(ce->interface_names[i].is_optional ? ZEND_FETCH_CLASS_SILENT : 0));

// Optional interfaces are skipped if they don't exist.
if (!iface && ce->interface_names[i].is_optional) {
continue;
}

if (!iface) {
check_unrecoverable_load_failure(ce);
free_alloca(traits_and_interfaces, use_heap);
return NULL;
}
traits_and_interfaces[ce->num_traits + i] = iface;
traits_and_interfaces[ce->num_traits + num_implementable_interfaces++] = iface;
if (iface) {
UPDATE_IS_CACHEABLE(iface);
}
}
}
ce->num_interfaces = num_implementable_interfaces;
tontonsb marked this conversation as resolved.
Show resolved Hide resolved

#ifndef ZEND_WIN32
if (ce->ce_flags & ZEND_ACC_ENUM) {
Expand Down
23 changes: 17 additions & 6 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> function_name non_empty_member_modifiers
%type <ast> property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body
%type <ast> optional_parameter_list
%type <ast> interface_name_list interface_name

%type <num> returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers
%type <num> method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers
Expand Down Expand Up @@ -355,10 +356,10 @@ legacy_namespace_name:
;

name:
T_STRING { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
| T_NAME_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
| T_NAME_FULLY_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_FQ; }
| T_NAME_RELATIVE { $$ = $1; $$->attr = ZEND_NAME_RELATIVE; }
T_STRING { $$ = $1; $$->attr |= ZEND_NAME_NOT_FQ; }
| T_NAME_QUALIFIED { $$ = $1; $$->attr |= ZEND_NAME_NOT_FQ; }
| T_NAME_FULLY_QUALIFIED { $$ = $1; $$->attr |= ZEND_NAME_FQ; }
| T_NAME_RELATIVE { $$ = $1; $$->attr |= ZEND_NAME_RELATIVE; }
;

attribute_decl:
Expand Down Expand Up @@ -666,12 +667,12 @@ extends_from:

interface_extends_list:
%empty { $$ = NULL; }
| T_EXTENDS class_name_list { $$ = $2; }
| T_EXTENDS interface_name_list { $$ = $2; }
;

implements_list:
%empty { $$ = NULL; }
| T_IMPLEMENTS class_name_list { $$ = $2; }
| T_IMPLEMENTS interface_name_list { $$ = $2; }
;

foreach_variable:
Expand Down Expand Up @@ -974,6 +975,11 @@ class_name_list:
| class_name_list ',' class_name { $$ = zend_ast_list_add($1, $3); }
;

interface_name_list:
interface_name { $$ = zend_ast_create_list(1, ZEND_AST_NAME_LIST, $1); }
| interface_name_list ',' interface_name { $$ = zend_ast_list_add($1, $3); }
;

trait_adaptations:
';' { $$ = NULL; }
| '{' '}' { $$ = NULL; }
Expand Down Expand Up @@ -1411,6 +1417,11 @@ class_name:
| name { $$ = $1; }
;

interface_name:
class_name { $$ = $1; }
| '?' name { $$ = $2; $$->attr |= ZEND_CLASS_NAME_OPTIONAL; }
;

class_name_reference:
class_name { $$ = $1; }
| new_variable { $$ = $1; }
Expand Down
3 changes: 2 additions & 1 deletion ext/opcache/zend_file_cache.c
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ static void zend_file_cache_serialize_class(zval *zv,

if (ce->num_interfaces) {
uint32_t i;
zend_class_name *interface_names;
zend_interface_name *interface_names;

ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));

Expand All @@ -803,6 +803,7 @@ static void zend_file_cache_serialize_class(zval *zv,
for (i = 0; i < ce->num_interfaces; i++) {
SERIALIZE_STR(interface_names[i].name);
SERIALIZE_STR(interface_names[i].lc_name);
// SERIALIZE_BOOL(interface_names[i].is_optional) ?
tontonsb marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
3 changes: 2 additions & 1 deletion ext/opcache/zend_persist.c
Original file line number Diff line number Diff line change
Expand Up @@ -1048,8 +1048,9 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
for (i = 0; i < ce->num_interfaces; i++) {
zend_accel_store_interned_string(ce->interface_names[i].name);
zend_accel_store_interned_string(ce->interface_names[i].lc_name);
// TODO: should we store .is_optional here? How?
tontonsb marked this conversation as resolved.
Show resolved Hide resolved
}
ce->interface_names = zend_shared_memdup_free(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
ce->interface_names = zend_shared_memdup_free(ce->interface_names, sizeof(zend_interface_name) * ce->num_interfaces);
}

if (ce->num_traits) {
Expand Down
2 changes: 1 addition & 1 deletion ext/opcache/zend_persist_calc.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ void zend_persist_class_entry_calc(zend_class_entry *ce)
ADD_INTERNED_STRING(ce->interface_names[i].name);
ADD_INTERNED_STRING(ce->interface_names[i].lc_name);
}
ADD_SIZE(sizeof(zend_class_name) * ce->num_interfaces);
ADD_SIZE(sizeof(zend_interface_name) * ce->num_interfaces);
}
}

Expand Down
Loading