Skip to content

Partials v2 #12

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

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
be1ea82
partial application
krakjoe Apr 19, 2021
c419531
Re-use trampoline call frame, use an opcode-based trampoline
arnaud-lb Jun 2, 2025
c6f97c9
Pipe optimization
arnaud-lb Jun 22, 2025
a7dc4ed
ws
arnaud-lb Jun 23, 2025
fe910ad
unused function
arnaud-lb Jun 23, 2025
d73829e
comment
arnaud-lb Jun 23, 2025
3e13275
Fix refcounting
arnaud-lb Jun 23, 2025
6c18b30
RFC examples
arnaud-lb Jun 23, 2025
78521dd
Generated file
arnaud-lb Jun 23, 2025
3dcc430
Positional placeholders that run into the variadic portion generate a…
arnaud-lb Jun 27, 2025
3ddb387
Add invokable test
arnaud-lb Jul 2, 2025
bfc3e45
Move _IS_PLACEHOLDER constants
arnaud-lb Jul 2, 2025
12bfa65
Add return type test
arnaud-lb Jul 2, 2025
22dcd98
Use the same AST for FCCs and PFAs
arnaud-lb Jul 2, 2025
d0d1425
Add closing PHP tag in tests
arnaud-lb Jul 2, 2025
4646d0e
Minimal JIT support
arnaud-lb Jul 3, 2025
c5b5672
$this/scope rebinding validation
arnaud-lb Jul 3, 2025
b977297
Attribute support
arnaud-lb Jul 3, 2025
1143b34
Fix stack poisoning
arnaud-lb Jul 3, 2025
1132a8b
Fix platform-dependent tests
arnaud-lb Jul 3, 2025
e84faa9
Fix handling of empty named arg
arnaud-lb Jul 3, 2025
768be12
Generated file
arnaud-lb Jul 3, 2025
f3f1ab4
Fix default value reflection
arnaud-lb Jul 3, 2025
1df5c85
Remove dead code
arnaud-lb Jul 3, 2025
13fa9fb
Fix ReflectionFunction::isPartial()
arnaud-lb Jul 3, 2025
8cd83eb
Cleanup
arnaud-lb Jul 3, 2025
00b53fb
Fix build
arnaud-lb Jul 3, 2025
1d5fad2
Merge branch 'master' into partials-v2
arnaud-lb Jul 3, 2025
ef69e82
Handle clone syntax
arnaud-lb Jul 3, 2025
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
1 change: 1 addition & 0 deletions Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
case ZEND_SEND_VAR_NO_REF_EX:
case ZEND_SEND_REF:
case ZEND_SEND_FUNC_ARG:
case ZEND_SEND_PLACEHOLDER:
case ZEND_CHECK_FUNC_ARG:
if (opline->op2_type == IS_CONST) {
opline->result.num = cache_size;
Expand Down
6 changes: 4 additions & 2 deletions Zend/Optimizer/optimize_func_calls.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
case ZEND_CALLABLE_CONVERT:
case ZEND_CALLABLE_CONVERT_PARTIAL:
call--;
if (call_stack[call].func && call_stack[call].opline) {
zend_op *fcall = call_stack[call].opline;
Expand Down Expand Up @@ -225,13 +226,14 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
* At this point we also know whether or not the result of
* the DO opcode is used, allowing to optimize calls to
* ZEND_ACC_NODISCARD functions. */
if (opline->opcode != ZEND_CALLABLE_CONVERT) {
if (opline->opcode != ZEND_CALLABLE_CONVERT && opline->opcode != ZEND_CALLABLE_CONVERT_PARTIAL) {
opline->opcode = zend_get_call_op(fcall, call_stack[call].func, !RESULT_UNUSED(opline));
}

if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
&& call_stack[call].try_inline
&& opline->opcode != ZEND_CALLABLE_CONVERT) {
&& opline->opcode != ZEND_CALLABLE_CONVERT
&& opline->opcode != ZEND_CALLABLE_CONVERT_PARTIAL) {
zend_try_inline_call(op_array, fcall, opline, call_stack[call].func);
}
}
Expand Down
2 changes: 2 additions & 0 deletions Zend/Optimizer/zend_call_graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
case ZEND_CALLABLE_CONVERT:
case ZEND_CALLABLE_CONVERT_PARTIAL:
func_info->flags |= ZEND_FUNC_HAS_CALLS;
if (call_info) {
call_info->caller_call_opline = opline;
Expand All @@ -144,6 +145,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
case ZEND_SEND_VAR_NO_REF:
case ZEND_SEND_VAR_NO_REF_EX:
case ZEND_SEND_USER:
case ZEND_SEND_PLACEHOLDER:
if (call_info) {
if (opline->op2_type == IS_CONST) {
call_info->named_args = 1;
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -3901,6 +3901,7 @@ static zend_always_inline zend_result _zend_update_type_info(
}
break;
case ZEND_CALLABLE_CONVERT:
case ZEND_CALLABLE_CONVERT_PARTIAL:
UPDATE_SSA_TYPE(MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN, ssa_op->result_def);
UPDATE_SSA_OBJ_TYPE(zend_ce_closure, /* is_instanceof */ false, ssa_op->result_def);
break;
Expand Down
90 changes: 90 additions & 0 deletions Zend/tests/partial_application/attributes_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
--TEST--
Partial application copies attributes
--FILE--
<?php

#[Attribute]
class Test {}

#[NoDiscard]
function f($a, #[SensitiveParameter] $b, #[Test] ...$c) {
}

function dump_attributes($function) {
echo "Function attributes:\n";
$r = new ReflectionFunction($function);
var_dump($r->getAttributes());

foreach ($r->getParameters() as $i => $p) {
echo "Parameter $i:\n";
var_dump($p->getAttributes());
}
}

dump_attributes('f');

$f = f(1, ?, ?, ...);

dump_attributes($f);

?>
--EXPECTF--
Function attributes:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(9) "NoDiscard"
}
}
Parameter 0:
array(0) {
}
Parameter 1:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(18) "SensitiveParameter"
}
}
Parameter 2:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(4) "Test"
}
}
Function attributes:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(9) "NoDiscard"
}
}
Parameter 0:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(18) "SensitiveParameter"
}
}
Parameter 1:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(4) "Test"
}
}
Parameter 2:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(4) "Test"
}
}
52 changes: 52 additions & 0 deletions Zend/tests/partial_application/attributes_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--TEST--
Partial application preserves #[SensitiveParameter]
--FILE--
<?php

function f($a, #[SensitiveParameter] $b, $c) {
throw new Exception();
}

echo "# During partial application:\n";

try {
$f = f(1, 'sensitive');
} catch (Error $e) {
echo $e, "\n\n";
}

echo "# In trampoline:\n";

try {
$f = f(1, ?, ?)('sensitive');
} catch (Error $e) {
echo $e, "\n\n";
}

echo "# In execution:\n";

try {
$f = f(1, ?, ?)('sensitive', 3);
} catch (Exception $e) {
echo $e, "\n";
}

?>
--EXPECTF--
# During partial application:
ArgumentCountError: Too few arguments to function f(), 2 passed in %s on line %d and exactly 3 expected in %s:%d
Stack trace:
#0 %s(%d): f(1, Object(SensitiveParameterValue))
#1 {main}

# In trampoline:
Error: not enough arguments for application of f, 1 given and exactly 2 expected, declared in %s on line %d in %s:%d
Stack trace:
#0 %s(%d): Closure->f(Object(SensitiveParameterValue))
#1 {main}

# In execution:
Exception in %s:%d
Stack trace:
#0 %s(%d): f(1, Object(SensitiveParameterValue), 3)
#1 {main}
16 changes: 16 additions & 0 deletions Zend/tests/partial_application/attributes_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Partial application preserves #[NoDiscard]
--FILE--
<?php

#[NoDiscard] function f($a) {
}

$f = f(?);
$f(1);

(void) $f(1);

?>
--EXPECTF--
Warning: The return value of function f() should either be used or intentionally ignored by casting it as (void) in %s on line 7
14 changes: 14 additions & 0 deletions Zend/tests/partial_application/attributes_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
Partial application preserves #[Deprecated]
--FILE--
<?php

#[Deprecated] function f($a) {
}

$f = f(?);
$f(1);

?>
--EXPECTF--
Deprecated: Function f() is deprecated in %s on line 7
9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: multiple ...
--FILE--
<?php
foo(..., ...);
?>
--EXPECTF--
Fatal error: Variadic placeholder may only appear once in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: only named arguments after ...
--FILE--
<?php
foo(..., ?);
?>
--EXPECTF--
Fatal error: Only named arguments may follow variadic placeholder in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: named arguments must come after placeholder
--FILE--
<?php
foo(n: 5, ?);
?>
--EXPECTF--
Fatal error: Cannot use positional argument after named argument in %s on line %d

8 changes: 8 additions & 0 deletions Zend/tests/partial_application/compile_errors_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
Partial application compile errors: named arguments must come after variadic placeholder
--FILE--
<?php
foo(n: 5, ...);
?>
--EXPECTF--
Fatal error: Cannot use positional argument after named argument in %s on line %d
9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: follow variadic with un-named arg
--FILE--
<?php
foo(..., $a);
?>
--EXPECTF--
Fatal error: Only named arguments may follow variadic placeholder in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: mix application with unpack (placeholder after)
--FILE--
<?php
foo(...["foo" => "bar"], ...);
?>
--EXPECTF--
Fatal error: Cannot combine partial application and unpacking in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: mix application with unpack (placeholder before)
--FILE--
<?php
foo(..., ...["foo" => "bar"]);
?>
--EXPECTF--
Fatal error: Cannot combine partial application and unpacking %s on line %d

38 changes: 38 additions & 0 deletions Zend/tests/partial_application/errors_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
Partial application errors: placeholder count errors
--FILE--
<?php
function foo($a, $b, $c) {

}

try {
foo(?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

try {
foo(?, ?, ?, ?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

try {
property_exists(?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

try {
usleep(?, ?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}
?>
--EXPECTF--
not enough arguments or placeholders for application of foo, 1 given and exactly 3 expected, declared in %s on line 2
too many arguments or placeholders for application of foo, 4 given and a maximum of 3 expected, declared in %s on line 2
not enough arguments or placeholders for application of property_exists, 1 given and exactly 2 expected
too many arguments or placeholders for application of usleep, 2 given and a maximum of 1 expected

17 changes: 17 additions & 0 deletions Zend/tests/partial_application/errors_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Partial application errors: named parameter overwrites placeholder
--FILE--
<?php
function foo($a) {

}

try {
foo(?, a: 1);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}
?>
--EXPECT--
Named parameter $a overwrites previous placeholder

Loading
Loading