Skip to content

Commit 4f3a594

Browse files
committed
Add param reordering
1 parent 817e7a3 commit 4f3a594

File tree

12 files changed

+1102
-684
lines changed

12 files changed

+1102
-684
lines changed

Zend/tests/partial_application/named_placeholder_001.phpt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,16 @@ $bar = bar(b: ?, ...);
3838

3939
echo (string) new ReflectionFunction($bar);
4040

41-
$bar(new A, new B, new C);
41+
$bar(new B, new A, new C);
4242

4343
try {
44-
$bar = $bar(?, a: ?);
44+
$bar = bar(?, a: ?);
4545
} catch (\Throwable $e) {
4646
echo $e->getMessage(), "\n";
4747
}
4848

4949
try {
50-
$bar = $bar(c: ?, ...);
50+
$bar = bar(c: ?, ...);
5151
} catch (\Throwable $e) {
5252
echo $e->getMessage(), "\n";
5353
}
@@ -99,8 +99,8 @@ Closure [ <user> static function {closure:%s:%d} ] {
9999
@@ %snamed_placeholder_001.php 34 - 34
100100

101101
- Parameters [3] {
102-
Parameter #0 [ <optional> $a = 1 ]
103-
Parameter #1 [ <optional> $b = 2 ]
102+
Parameter #0 [ <optional> $b = 2 ]
103+
Parameter #1 [ <optional> $a = 1 ]
104104
Parameter #2 [ <optional> ...$c ]
105105
}
106106
}
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
--TEST--
2+
Named parameters define order
3+
--FILE--
4+
<?php
5+
6+
function f($a, $b, $c, $d = null) {
7+
return [$a, $b, $c, $d];
8+
}
9+
10+
function g($a, $b, $c, ...$d) {
11+
return [$a, $b, $c, $d];
12+
}
13+
14+
echo "# All named\n";
15+
16+
$a = f(d: ?, c: ?, b: ?, a: ?);
17+
echo new ReflectionFunction($a), "\n";
18+
var_dump($a(1, 2, 3, 4));
19+
20+
echo "# Some named: Positional first, then named in specified order\n";
21+
22+
$a = f(?, ?, d: ?, c: ?);
23+
echo new ReflectionFunction($a), "\n";
24+
var_dump($a(1, 2, 3, 4));
25+
26+
echo "# Some named, one unspecified\n";
27+
28+
$a = f(?, c: ?, b: ?);
29+
echo new ReflectionFunction($a), "\n";
30+
var_dump($a(1, 2, 3, 4));
31+
32+
echo "# Some named, some implicit added by '...'\n";
33+
34+
$a = f(c: ?, b: ?, ...);
35+
echo new ReflectionFunction($a), "\n";
36+
var_dump($a(1, 2, 3, 4));
37+
38+
echo "# Some named, some implicit added by '...' on variadic function\n";
39+
40+
$a = g(c: ?, b: ?, ...);
41+
echo new ReflectionFunction($a), "\n";
42+
var_dump($a(1, 2, 3, 4, 5, 6));
43+
44+
echo "# Some prebound, some named\n";
45+
46+
$a = f(-1, c: ?, d: -2, b: ?);
47+
echo new ReflectionFunction($a), "\n";
48+
var_dump($a(1, 2));
49+
50+
echo "# Some named, some required missing\n";
51+
52+
try {
53+
$a = f(b: ?, c: ?);
54+
} catch (Error $e) {
55+
echo $e::class, ": ", $e->getMessage(), "\n";
56+
}
57+
58+
?>
59+
--EXPECTF--
60+
# All named
61+
Closure [ <user> static function {closure:%s:%d} ] {
62+
@@ %sparam_reorder.php 13 - 13
63+
64+
- Parameters [4] {
65+
Parameter #0 [ <required> $d ]
66+
Parameter #1 [ <required> $c ]
67+
Parameter #2 [ <required> $b ]
68+
Parameter #3 [ <required> $a ]
69+
}
70+
}
71+
72+
array(4) {
73+
[0]=>
74+
int(4)
75+
[1]=>
76+
int(3)
77+
[2]=>
78+
int(2)
79+
[3]=>
80+
int(1)
81+
}
82+
# Some named: Positional first, then named in specified order
83+
Closure [ <user> static function {closure:%s:%d} ] {
84+
@@ %sparam_reorder.php 19 - 19
85+
86+
- Parameters [4] {
87+
Parameter #0 [ <required> $a ]
88+
Parameter #1 [ <required> $b ]
89+
Parameter #2 [ <required> $d ]
90+
Parameter #3 [ <required> $c ]
91+
}
92+
}
93+
94+
array(4) {
95+
[0]=>
96+
int(1)
97+
[1]=>
98+
int(2)
99+
[2]=>
100+
int(4)
101+
[3]=>
102+
int(3)
103+
}
104+
# Some named, one unspecified
105+
Closure [ <user> static function {closure:%s:%d} ] {
106+
@@ %sparam_reorder.php 25 - 25
107+
108+
- Parameters [3] {
109+
Parameter #0 [ <required> $a ]
110+
Parameter #1 [ <required> $c ]
111+
Parameter #2 [ <required> $b ]
112+
}
113+
}
114+
115+
array(4) {
116+
[0]=>
117+
int(1)
118+
[1]=>
119+
int(3)
120+
[2]=>
121+
int(2)
122+
[3]=>
123+
NULL
124+
}
125+
# Some named, some implicit added by '...'
126+
Closure [ <user> static function {closure:%s:%d} ] {
127+
@@ %sparam_reorder.php 31 - 31
128+
129+
- Parameters [4] {
130+
Parameter #0 [ <required> $c ]
131+
Parameter #1 [ <required> $b ]
132+
Parameter #2 [ <required> $a ]
133+
Parameter #3 [ <optional> $d = NULL ]
134+
}
135+
}
136+
137+
array(4) {
138+
[0]=>
139+
int(3)
140+
[1]=>
141+
int(2)
142+
[2]=>
143+
int(1)
144+
[3]=>
145+
int(4)
146+
}
147+
# Some named, some implicit added by '...' on variadic function
148+
Closure [ <user> static function {closure:%s:%d} ] {
149+
@@ %sparam_reorder.php 37 - 37
150+
151+
- Parameters [4] {
152+
Parameter #0 [ <required> $c ]
153+
Parameter #1 [ <required> $b ]
154+
Parameter #2 [ <required> $a ]
155+
Parameter #3 [ <optional> ...$d ]
156+
}
157+
}
158+
159+
array(4) {
160+
[0]=>
161+
int(3)
162+
[1]=>
163+
int(2)
164+
[2]=>
165+
int(1)
166+
[3]=>
167+
array(3) {
168+
[0]=>
169+
int(4)
170+
[1]=>
171+
int(5)
172+
[2]=>
173+
int(6)
174+
}
175+
}
176+
# Some prebound, some named
177+
Closure [ <user> static function {closure:%s:%d} ] {
178+
@@ %sparam_reorder.php 43 - 43
179+
180+
- Bound Variables [2] {
181+
Variable #0 [ $a ]
182+
Variable #1 [ $d ]
183+
}
184+
185+
- Parameters [2] {
186+
Parameter #0 [ <required> $c ]
187+
Parameter #1 [ <required> $b ]
188+
}
189+
}
190+
191+
array(4) {
192+
[0]=>
193+
int(-1)
194+
[1]=>
195+
int(2)
196+
[2]=>
197+
int(1)
198+
[3]=>
199+
int(-2)
200+
}
201+
# Some named, some required missing
202+
ArgumentCountError: main(): Argument #1 not passed

Zend/tests/partial_application/pipe_optimization_007.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ $_main:
3636
0006 SEND_PLACEHOLDER string("a")
3737
0007 SEND_PLACEHOLDER string("b")
3838
0008 CHECK_PARTIAL_ARGS
39-
0009 T1 = CALLABLE_CONVERT_PARTIAL %d
39+
0009 T1 = CALLABLE_CONVERT_PARTIAL %d array(...)
4040
0010 INIT_DYNAMIC_CALL 1 T1
4141
0011 SEND_VAL_EX int(2) 1
4242
0012 DO_FCALL

Zend/tests/partial_application/rfc_examples_001.phpt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ $tests = [
6565
stuff(1, 'hi', 3.4, $point, 5, ...),
6666
fn(): array => stuff(1, 'hi', 3.4, $point, 5),
6767
],
68-
'Placeholders may be named, too. Their order doesn\'t matter as long as they come before the ..., if any' => [
68+
'Placeholders may be named, too' => [
6969
stuff(?, p: $point, f: ?, s: ?, m: 4),
70-
fn(int $i, string $s, float $f): array => stuff($i, $s, $f, $point, 4),
70+
fn(int $i, float $f, string $s): array => stuff($i, $s, $f, $point, 4),
7171
],
72-
'Placeholders may be named, too. Their order doesn\'t matter as long as they come before the ..., if any (2)' => [
72+
'Placeholders may be named, too (2)' => [
7373
stuff(m: 4, p: $point, i: ?, ...),
7474
fn(int $i, string $s, float $f): array => stuff($i, $s, $f, $point, 4),
7575
],
@@ -82,7 +82,11 @@ foreach ($tests as $test => [$pfa, $closure]) {
8282

8383
try {
8484
if (count($pfaReflector->getParameters()) !== count($closureReflector->getParameters())) {
85-
throw new Exception("Arity does not match");
85+
throw new Exception(sprintf(
86+
"Arity does not match: expected %d, got %d",
87+
count($closureReflector->getParameters()),
88+
count($pfaReflector->getParameters()),
89+
));
8690
}
8791

8892
$it = new MultipleIterator();
@@ -148,5 +152,5 @@ foreach ($tests as $test => [$pfa, $closure]) {
148152
# Named arguments can be pulled "out of order", and still work (2)
149153
# The ... "everything else" placeholder respects named arguments
150154
# Prefill all parameters, making a "delayed call" or "thunk"
151-
# Placeholders may be named, too. Their order doesn't matter as long as they come before the ..., if any
152-
# Placeholders may be named, too. Their order doesn't matter as long as they come before the ..., if any (2)
155+
# Placeholders may be named, too
156+
# Placeholders may be named, too (2)

Zend/zend_compile.c

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4008,28 +4008,75 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *f
40084008
}
40094009
/* }}} */
40104010

4011-
void zend_compile_call_partial(znode *result, zend_ast *call_ast,
4011+
void zend_compile_call_partial(znode *result, const zend_ast *call_ast,
40124012
uint32_t arg_count, bool may_have_extra_named_args, uint32_t opnum_init,
4013-
zend_function *fbc) { /* {{{ */
4013+
const zend_function *fbc) { /* {{{ */
40144014

4015-
zend_op *opline = &CG(active_op_array)->opcodes[opnum_init];
4015+
zend_op *init_opline = &CG(active_op_array)->opcodes[opnum_init];
40164016

4017-
opline->extended_value = arg_count;
4017+
init_opline->extended_value = arg_count;
40184018

4019-
ZEND_ASSERT(opline->opcode != ZEND_NEW);
4019+
ZEND_ASSERT(init_opline->opcode != ZEND_NEW);
40204020

4021-
if (opline->opcode == ZEND_INIT_FCALL) {
4022-
opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc);
4021+
if (init_opline->opcode == ZEND_INIT_FCALL) {
4022+
init_opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc);
40234023
}
40244024

4025-
opline = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT_PARTIAL,
4025+
zend_op *opline = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT_PARTIAL,
40264026
NULL, NULL);
40274027

40284028
opline->op1.num = zend_alloc_cache_slots(2);
40294029

40304030
if (may_have_extra_named_args) {
40314031
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
40324032
}
4033+
4034+
/* Collect placeholder order */
4035+
int level = 0;
4036+
zval order;
4037+
ZVAL_UNDEF(&order);
4038+
for (zend_op *send_op = init_opline+1; send_op < opline; send_op++) {
4039+
switch (send_op->opcode) {
4040+
case ZEND_INIT_FCALL:
4041+
case ZEND_INIT_FCALL_BY_NAME:
4042+
case ZEND_INIT_NS_FCALL_BY_NAME:
4043+
case ZEND_INIT_DYNAMIC_CALL:
4044+
case ZEND_INIT_USER_CALL:
4045+
case ZEND_INIT_METHOD_CALL:
4046+
case ZEND_INIT_STATIC_METHOD_CALL:
4047+
case ZEND_NEW:
4048+
level++;
4049+
break;
4050+
case ZEND_DO_FCALL:
4051+
case ZEND_DO_ICALL:
4052+
case ZEND_DO_UCALL:
4053+
case ZEND_DO_FCALL_BY_NAME:
4054+
case ZEND_CALLABLE_CONVERT:
4055+
case ZEND_CALLABLE_CONVERT_PARTIAL:
4056+
level--;
4057+
break;
4058+
case ZEND_SEND_PLACEHOLDER:
4059+
if (level != 0) {
4060+
break;
4061+
}
4062+
if (send_op->op2_type == IS_CONST) {
4063+
if (Z_ISUNDEF(order)) {
4064+
array_init(&order);
4065+
}
4066+
zval tmp;
4067+
ZVAL_LONG(&tmp, zend_hash_num_elements(Z_ARRVAL(order)));
4068+
zend_hash_add(Z_ARRVAL(order), Z_STR_P(CT_CONSTANT(send_op->op2)), &tmp);
4069+
}
4070+
break;
4071+
default:
4072+
break;
4073+
}
4074+
}
4075+
4076+
if (!Z_ISUNDEF(order)) {
4077+
opline->op2.constant = zend_add_literal(&order);
4078+
opline->op2_type = IS_CONST;
4079+
}
40334080
}
40344081

40354082
static bool zend_compile_call_common(znode *result, const zend_ast *call_ast, zend_ast *args_ast, const zend_function *fbc, uint32_t lineno) /* {{{ */
@@ -4123,7 +4170,7 @@ static bool zend_compile_function_name(znode *name_node, zend_ast *name_ast) /*
41234170
}
41244171
/* }}} */
41254172

4126-
static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *call_ast, zend_ast *args_ast, uint32_t lineno) /* {{{ */
4173+
static void zend_compile_dynamic_call(znode *result, znode *name_node, const zend_ast *call_ast, zend_ast *args_ast, uint32_t lineno) /* {{{ */
41274174
{
41284175
if (name_node->op_type == IS_CONST && Z_TYPE(name_node->u.constant) == IS_STRING) {
41294176
const char *colon;
@@ -4163,6 +4210,7 @@ static inline bool zend_args_contain_unpack_or_named(const zend_ast_list *args)
41634210
for (i = 0; i < args->children; ++i) {
41644211
const zend_ast *arg = args->child[i];
41654212
if (arg->kind == ZEND_AST_UNPACK || arg->kind == ZEND_AST_NAMED_ARG) {
4213+
return 1;
41664214
}
41674215
}
41684216
return 0;

Zend/zend_execute.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4653,6 +4653,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
46534653
case ZEND_DO_UCALL:
46544654
case ZEND_DO_FCALL_BY_NAME:
46554655
case ZEND_CALLABLE_CONVERT:
4656+
case ZEND_CALLABLE_CONVERT_PARTIAL:
46564657
level++;
46574658
break;
46584659
case ZEND_INIT_FCALL:

0 commit comments

Comments
 (0)