Skip to content

Commit 22dcd98

Browse files
committed
Use the same AST for FCCs and PFAs
1 parent 12bfa65 commit 22dcd98

File tree

10 files changed

+131
-68
lines changed

10 files changed

+131
-68
lines changed

Zend/tests/partial_application/compile_errors_006.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ Partial application compile errors: mix application with unpack (placeholder aft
55
foo(...["foo" => "bar"], ...);
66
?>
77
--EXPECTF--
8-
Fatal error: Cannot use positional argument after argument unpacking %s on line %d
8+
Fatal error: Cannot combine partial application and unpacking in %s on line %d
99

Zend/zend_ast.c

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(void) {
6161
ast->kind = ZEND_AST_CALLABLE_CONVERT;
6262
ast->attr = 0;
6363
ast->lineno = CG(zend_lineno);
64+
ast->args = NULL;
6465
ZEND_MAP_PTR_INIT(ast->fptr, NULL);
6566

6667
return (zend_ast *) ast;
@@ -508,6 +509,41 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zend_ast *ast, zend_ast *op)
508509
return (zend_ast *) list;
509510
}
510511

512+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list(zend_ast *arg) {
513+
zend_ast *list = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg);
514+
515+
if (arg->kind == ZEND_AST_PLACEHOLDER_ARG
516+
|| (arg->kind == ZEND_AST_NAMED_ARG
517+
&& arg->child[1]->kind == ZEND_AST_PLACEHOLDER_ARG)) {
518+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)zend_ast_create_fcc();
519+
fcc_ast->args = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg);
520+
return (zend_ast*)fcc_ast;
521+
}
522+
523+
return list;
524+
}
525+
526+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_arg_list_add(zend_ast *list, zend_ast *arg)
527+
{
528+
if (list->kind == ZEND_AST_CALLABLE_CONVERT) {
529+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)list;
530+
fcc_ast->args = zend_ast_list_add(fcc_ast->args, arg);
531+
return (zend_ast*)fcc_ast;
532+
}
533+
534+
ZEND_ASSERT(list->kind == ZEND_AST_ARG_LIST);
535+
536+
if (arg->kind == ZEND_AST_PLACEHOLDER_ARG
537+
|| (arg->kind == ZEND_AST_NAMED_ARG
538+
&& arg->child[1]->kind == ZEND_AST_PLACEHOLDER_ARG)) {
539+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)zend_ast_create_fcc();
540+
fcc_ast->args = zend_ast_list_add(list, arg);
541+
return (zend_ast*)fcc_ast;
542+
}
543+
544+
return zend_ast_list_add(list, arg);
545+
}
546+
511547
static zend_result zend_ast_add_array_element(zval *result, zval *offset, zval *expr)
512548
{
513549
if (Z_TYPE_P(offset) == IS_UNDEF) {
@@ -1058,6 +1094,14 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
10581094
case ZEND_AST_CALL: {
10591095
ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT);
10601096
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[1];
1097+
1098+
zend_ast_list *args = zend_ast_get_list(fcc_ast->args);
1099+
ZEND_ASSERT(args->children > 0);
1100+
if (args->children != 1 || args->child[0]->attr != _IS_PLACEHOLDER_VARIADIC) {
1101+
/* TODO: PFAs */
1102+
return FAILURE;
1103+
}
1104+
10611105
fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
10621106

10631107
if (!fptr) {
@@ -1085,6 +1129,13 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
10851129
ZEND_ASSERT(ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT);
10861130
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[2];
10871131

1132+
zend_ast_list *args = zend_ast_get_list(fcc_ast->args);
1133+
ZEND_ASSERT(args->children > 0);
1134+
if (args->children != 1 || args->child[0]->attr != _IS_PLACEHOLDER_VARIADIC) {
1135+
/* TODO: PFAs */
1136+
return FAILURE;
1137+
}
1138+
10881139
fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
10891140

10901141
if (!fptr) {
@@ -1246,7 +1297,8 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast)
12461297
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
12471298
size = sizeof(zend_ast_op_array);
12481299
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
1249-
size = sizeof(zend_ast_fcc);
1300+
zend_ast *args_ast = ((zend_ast_fcc*)ast)->args;
1301+
size = sizeof(zend_ast_fcc) + zend_ast_tree_size(args_ast);
12501302
} else if (zend_ast_is_list(ast)) {
12511303
uint32_t i;
12521304
zend_ast_list *list = zend_ast_get_list(ast);
@@ -1323,6 +1375,8 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
13231375
new->lineno = old->lineno;
13241376
ZEND_MAP_PTR_INIT(new->fptr, ZEND_MAP_PTR(old->fptr));
13251377
buf = (void*)((char*)buf + sizeof(zend_ast_fcc));
1378+
new->args = buf;
1379+
buf = zend_ast_tree_copy(old->args, buf);
13261380
} else if (zend_ast_is_decl(ast)) {
13271381
/* Not implemented. */
13281382
ZEND_UNREACHABLE();
@@ -1406,6 +1460,10 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast)
14061460
zend_ast_destroy(decl->child[3]);
14071461
ast = decl->child[4];
14081462
goto tail_call;
1463+
} else if (EXPECTED(ast->kind == ZEND_AST_CALLABLE_CONVERT)) {
1464+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast;
1465+
1466+
zend_ast_destroy(fcc_ast->args);
14091467
}
14101468
}
14111469

@@ -2451,9 +2509,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
24512509
zend_ast_export_ex(str, ast->child[1], 0, indent);
24522510
smart_str_appendc(str, ')');
24532511
break;
2454-
case ZEND_AST_CALLABLE_CONVERT:
2455-
smart_str_appends(str, "...");
2456-
break;
2512+
case ZEND_AST_CALLABLE_CONVERT:;
2513+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast;
2514+
ast = fcc_ast->args;
2515+
goto simple_list;
24572516
case ZEND_AST_CLASS_CONST:
24582517
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
24592518
smart_str_appends(str, "::");
@@ -2970,16 +3029,12 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr)
29703029
return ast;
29713030
}
29723031

2973-
zend_ast_list * ZEND_FASTCALL zend_ast_call_get_arg_list(zend_ast *ast)
3032+
zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast)
29743033
{
29753034
if (ast->kind == ZEND_AST_CALL) {
2976-
if (ast->child[1]->kind == ZEND_AST_ARG_LIST) {
2977-
return zend_ast_get_list(ast->child[1]);
2978-
}
3035+
return ast->child[1];
29793036
} else if (ast->kind == ZEND_AST_STATIC_CALL || ast->kind == ZEND_AST_METHOD_CALL) {
2980-
if (ast->child[2]->kind == ZEND_AST_ARG_LIST) {
2981-
return zend_ast_get_list(ast->child[2]);
2982-
}
3037+
return ast->child[2];
29833038
}
29843039

29853040
return NULL;

Zend/zend_ast.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,12 @@ typedef struct _zend_ast_decl {
231231
zend_ast *child[5];
232232
} zend_ast_decl;
233233

234+
// TODO: rename
234235
typedef struct _zend_ast_fcc {
235236
zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */
236237
zend_ast_attr attr; /* Additional attribute, use depending on node type */
237238
uint32_t lineno; /* Line number */
239+
zend_ast *args;
238240
ZEND_MAP_PTR_DEF(zend_function *, fptr);
239241
} zend_ast_fcc;
240242

@@ -324,6 +326,15 @@ ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind ki
324326

325327
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zend_ast *list, zend_ast *op);
326328

329+
/* Wraps the list into a ZEND_AST_CALLABLE_CONVERT if arg is a
330+
* ZEND_AST_PLACEHOLDER_ARG. */
331+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list(zend_ast *arg);
332+
333+
/* Like zend_ast_list_add(), but wraps the list into a ZEND_AST_CALLABLE_CONVERT
334+
* if any arg is a ZEND_AST_PLACEHOLDER_ARG. list can be a zend_ast_list, or a
335+
* zend_ast_fcc. */
336+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_arg_list_add(zend_ast *list, zend_ast *arg);
337+
327338
ZEND_API zend_ast *zend_ast_create_decl(
328339
zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment,
329340
zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4
@@ -427,6 +438,6 @@ static zend_always_inline zend_ast *zend_ast_list_rtrim(zend_ast *ast) {
427438

428439
zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr);
429440

430-
zend_ast_list * ZEND_FASTCALL zend_ast_call_get_arg_list(zend_ast *ast);
441+
zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast);
431442

432443
#endif

Zend/zend_compile.c

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3696,8 +3696,8 @@ static uint32_t zend_get_arg_num(zend_function *fn, zend_string *arg_name) {
36963696
}
36973697

36983698
static uint32_t zend_compile_args(
3699-
zend_ast *ast, zend_function *fbc, bool *may_have_extra_named_args,
3700-
bool *is_call_partial) /* {{{ */
3699+
zend_ast *ast, zend_function *fbc, bool is_call_partial,
3700+
bool *may_have_extra_named_args) /* {{{ */
37013701
{
37023702
zend_ast_list *args = zend_ast_get_list(ast);
37033703
uint32_t i;
@@ -3712,8 +3712,6 @@ static uint32_t zend_compile_args(
37123712
bool may_have_undef = 0;
37133713
/* Whether there may be any extra named arguments collected into a variadic. */
37143714
*may_have_extra_named_args = 0;
3715-
/* Whether this is a partial call */
3716-
*is_call_partial = false;
37173715

37183716
for (i = 0; i < args->children; ++i) {
37193717
zend_ast *arg = args->child[i];
@@ -3730,7 +3728,7 @@ static uint32_t zend_compile_args(
37303728
"Cannot use argument unpacking after named arguments");
37313729
}
37323730

3733-
if (*is_call_partial) {
3731+
if (is_call_partial) {
37343732
zend_error_noreturn(E_COMPILE_ERROR,
37353733
"Cannot combine partial application and unpacking");
37363734
}
@@ -3824,7 +3822,6 @@ static uint32_t zend_compile_args(
38243822
opline->result.var = EX_NUM_TO_VAR(arg_num - 1);
38253823
}
38263824

3827-
*is_call_partial = true;
38283825
continue;
38293826
}
38303827

@@ -3938,7 +3935,7 @@ static uint32_t zend_compile_args(
39383935
}
39393936
}
39403937

3941-
if (!*is_call_partial) {
3938+
if (!is_call_partial) {
39423939
if (may_have_undef) {
39433940
zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL);
39443941
}
@@ -4010,27 +4007,38 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun
40104007
{
40114008
zend_op *opline;
40124009
uint32_t opnum_init = get_next_op_number() - 1;
4010+
bool is_partial_call = false;
40134011

40144012
if (args_ast->kind == ZEND_AST_CALLABLE_CONVERT) {
4013+
is_partial_call = true;
4014+
40154015
opline = &CG(active_op_array)->opcodes[opnum_init];
40164016
opline->extended_value = 0;
40174017

40184018
if (opline->opcode == ZEND_NEW) {
40194019
zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression");
40204020
}
40214021

4022-
if (opline->opcode == ZEND_INIT_FCALL) {
4023-
opline->op1.num = zend_vm_calc_used_stack(0, fbc);
4022+
zend_ast_list *fcc_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args);
4023+
4024+
/* FCCs are a special case of PFAs with a single variadic placeholder */
4025+
if (fcc_args->children == 1 && fcc_args->child[0]->attr == _IS_PLACEHOLDER_VARIADIC) {
4026+
4027+
if (opline->opcode == ZEND_INIT_FCALL) {
4028+
opline->op1.num = zend_vm_calc_used_stack(0, fbc);
4029+
}
4030+
4031+
zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL);
4032+
4033+
return true;
40244034
}
40254035

4026-
zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL);
4027-
return true;
4036+
args_ast = ((zend_ast_fcc*)args_ast)->args;
40284037
}
40294038

40304039
bool may_have_extra_named_args;
4031-
bool is_partial_call;
40324040
uint32_t arg_count = zend_compile_args(args_ast, fbc,
4033-
&may_have_extra_named_args, &is_partial_call);
4041+
is_partial_call, &may_have_extra_named_args);
40344042

40354043
if (is_partial_call) {
40364044
zend_compile_call_partial(result, arg_count, may_have_extra_named_args, opnum_init, fbc);
@@ -6519,11 +6527,13 @@ static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg)
65196527
return NULL;
65206528
}
65216529

6522-
zend_ast_list *arg_list = zend_ast_call_get_arg_list(callable_ast);
6523-
if (!arg_list) {
6530+
zend_ast *args_ast = zend_ast_call_get_args(callable_ast);
6531+
if (!args_ast || args_ast->kind != ZEND_AST_CALLABLE_CONVERT) {
65246532
return NULL;
65256533
}
65266534

6535+
zend_ast_list *arg_list = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args);
6536+
65276537
zend_ast *first_placeholder = NULL;
65286538
bool uses_variadic_placeholder = false;
65296539

@@ -6557,10 +6567,7 @@ static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg)
65576567
}
65586568
}
65596569

6560-
if (first_placeholder == NULL) {
6561-
/* Not a PFA */
6562-
return NULL;
6563-
}
6570+
ZEND_ASSERT(first_placeholder);
65646571

65656572
zend_ast *new_arg_list = zend_ast_create_list(0, arg_list->kind);
65666573
for (uint32_t i = 0; i < arg_list->children; i++) {
@@ -6601,30 +6608,14 @@ static void zend_compile_pipe(znode *result, zend_ast *ast)
66016608

66026609
/* Turn the operand into a function parameter list. */
66036610
zend_ast *arg = zend_ast_create_znode(&wrapped_operand_result);
6604-
zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg);
66056611

66066612
zend_ast *fcall_ast;
66076613
znode callable_result;
66086614

66096615
zend_ast *pfa_arg_list_ast = NULL;
66106616

6611-
/* Turn $foo |> bar(...) into bar($foo). */
6612-
if (callable_ast->kind == ZEND_AST_CALL
6613-
&& callable_ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT) {
6614-
fcall_ast = zend_ast_create(ZEND_AST_CALL,
6615-
callable_ast->child[0], arg_list_ast);
6616-
/* Turn $foo |> bar::baz(...) into bar::baz($foo). */
6617-
} else if (callable_ast->kind == ZEND_AST_STATIC_CALL
6618-
&& callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) {
6619-
fcall_ast = zend_ast_create(ZEND_AST_STATIC_CALL,
6620-
callable_ast->child[0], callable_ast->child[1], arg_list_ast);
6621-
/* Turn $foo |> $bar->baz(...) into $bar->baz($foo). */
6622-
} else if (callable_ast->kind == ZEND_AST_METHOD_CALL
6623-
&& callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) {
6624-
fcall_ast = zend_ast_create(ZEND_AST_METHOD_CALL,
6625-
callable_ast->child[0], callable_ast->child[1], arg_list_ast);
66266617
/* Turn $foo |> PFA into plain function call if possible */
6627-
} else if ((pfa_arg_list_ast = zend_partial_apply(callable_ast, arg))) {
6618+
if ((pfa_arg_list_ast = zend_partial_apply(callable_ast, arg))) {
66286619
switch (callable_ast->kind) {
66296620
case ZEND_AST_CALL:
66306621
fcall_ast = zend_ast_create(ZEND_AST_CALL,
@@ -6644,6 +6635,7 @@ static void zend_compile_pipe(znode *result, zend_ast *ast)
66446635
}
66456636
/* Turn $foo |> $expr into ($expr)($foo) */
66466637
} else {
6638+
zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg);
66476639
zend_compile_expr(&callable_result, callable_ast);
66486640
callable_ast = zend_ast_create_znode(&callable_result);
66496641
fcall_ast = zend_ast_create(ZEND_AST_CALL,
@@ -11570,6 +11562,13 @@ static void zend_compile_const_expr_fcc(zend_ast **ast_ptr)
1157011562
if ((*args_ast)->kind != ZEND_AST_CALLABLE_CONVERT) {
1157111563
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
1157211564
}
11565+
11566+
zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)*args_ast)->args);
11567+
if (args->children != 1 || args->child[0]->attr != _IS_PLACEHOLDER_VARIADIC) {
11568+
// TODO: PFAs
11569+
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
11570+
}
11571+
1157311572
ZEND_MAP_PTR_NEW(((zend_ast_fcc *)*args_ast)->fptr);
1157411573

1157511574
switch ((*ast_ptr)->kind) {

Zend/zend_fibers.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,9 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
583583
EG(vm_stack_page_size) = ZEND_FIBER_VM_STACK_SIZE;
584584

585585
fiber->execute_data = (zend_execute_data *) stack->top;
586+
#ifdef __SANITIZE_ADDRESS__
587+
__asan_unpoison_memory_region(stack->top, sizeof(zend_execute_data));
588+
#endif
586589
fiber->stack_bottom = fiber->execute_data;
587590

588591
memset(fiber->execute_data, 0, sizeof(zend_execute_data));

0 commit comments

Comments
 (0)