Skip to content

Conversation

@arnaud-lb
Copy link
Member

@arnaud-lb arnaud-lb commented Dec 16, 2025

Spinned off arnaud-lb#22

RFC: https://wiki.php.net/rfc/partial_function_application_v2

For FCCs, the parser generates a normal function call AST node, the but argument list is a ZEND_AST_CALLABLE_CONVERT / zend_ast_fcc node.

We extend this for PFAs so that zend_ast_fcc can represent arguments.

In this PR:

  • Support PFA syntax in grammar
  • Update zend_ast_fcc so that arguments can be represented
  • Support serialization of zend_ast_fcc arguments in SHM / file cache
  • Introduce zend_ast_arg_list_add(): Same as zend_ast_list_add(), but wraps the list in a ZEND_AST_CALLABLE_CONVERT when adding any placeholder argument.

Technically the arg list wrapping is not required, but it results in simpler code later as it will be very convenient in the compiler (determines whether a function calls is a PFA/FCC), and for PFA-in-const-expr support. It also allows to unify FCCs and PFAs in the grammar.

Copy link
Member

@TimWolla TimWolla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactoring for the zend_ast_fcc struct to hold parameters make sense to me to ship independently, but the grammar changes visible to userland (e.g. the support for ?) should probably be deferred to a later PR to not expose an incomplete implementation to users.

@arnaud-lb
Copy link
Member Author

Right. It seemed reasonable to me for master, since I'm going to implement the rest soon.

I've pushed a change to emit a compile error when trying to use ? or more than one FCC argument, so this is closer to how clone() was iteratively pushed to master.


zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args);
if (args->children != 1 || args->child[0]->attr != _IS_PLACEHOLDER_VARIADIC) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot create a Closure for call expression with more than one arguments, or non-variadic placeholders");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Cannot create a Closure" part is copied from "Cannot create Closure for new expression" above

Copy link
Member

@TimWolla TimWolla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't see any major issues as far as I'm qualified to tell.

Comment on lines +2408 to +2412
if (ast->attr == _IS_PLACEHOLDER_ARG) {
APPEND_STR("?");
} else if (ast->attr == _IS_PLACEHOLDER_VARIADIC) {
APPEND_STR("...");
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (ast->attr == _IS_PLACEHOLDER_ARG) {
APPEND_STR("?");
} else if (ast->attr == _IS_PLACEHOLDER_VARIADIC) {
APPEND_STR("...");
}
if (ast->attr == _IS_PLACEHOLDER_ARG) {
APPEND_STR("?");
} else if (ast->attr == _IS_PLACEHOLDER_VARIADIC) {
APPEND_STR("...");
} else {
ZEND_UNREACHABLE();
}

Comment on lines -1127 to +1225

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops. Sorry for introducing these. Can you push the a commit to trim the trailing spaces straight to master to denoise the diff?

Comment on lines +641 to +643
/* used for PFAs/FCCs */
#define _IS_PLACEHOLDER_ARG 20
#define _IS_PLACEHOLDER_VARIADIC 21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these required to be a zval type for the following implementation? Other AST attributes use a “distinct” set of values.

Comment on lines +1508 to +1510
zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast;

zend_ast_destroy(fcc_ast->args);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast;
zend_ast_destroy(fcc_ast->args);
zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast;
ast = fcc_ast->args;
goto tail_call;

?

Comment on lines +1184 to +1185
/* TODO: PFAs */
return FAILURE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the expectation for return FAILURE; is that there also is an Exception active:

Suggested change
/* TODO: PFAs */
return FAILURE;
zend_throw_error(NULL, "Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders");
return FAILURE;

zend_ast_list *args = zend_ast_get_list(fcc_ast->args);
ZEND_ASSERT(args->children > 0);
if (args->children != 1 || args->child[0]->attr != _IS_PLACEHOLDER_VARIADIC) {
/* TODO: PFAs */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/* TODO: PFAs */
zend_throw_error(NULL, "Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders");

if (list->kind == ZEND_AST_CALLABLE_CONVERT) {
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)list;
fcc_ast->args = zend_ast_list_add(fcc_ast->args, arg);
return (zend_ast*)fcc_ast;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is clearer (the fcc_ast doesn't actually change):

Suggested change
return (zend_ast*)fcc_ast;
return list;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants