Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
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
5 changes: 5 additions & 0 deletions Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,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 All @@ -747,6 +748,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
cache_size += sizeof(void *);
}
break;
case ZEND_CALLABLE_CONVERT_PARTIAL:
opline->op1.num = cache_size;
cache_size += 2 * sizeof(void *);
break;
}
opline++;
}
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 = true;
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 @@ -3906,6 +3906,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

This file was deleted.

This file was deleted.

35 changes: 35 additions & 0 deletions Zend/tests/partial_application/assert.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
PFA of assert() behaves like a dynamic call to assert()
--FILE--
<?php

try {
echo "# Static call:\n";
assert(false);
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}

try {
echo "# Dynamic call:\n";
(function ($f) { $f(false); })('assert');
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n";
}

try {
echo "# PFA call:\n";
$f = assert(?);
$f(false);
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n";
}

?>
--EXPECT--
# Static call:
AssertionError: assert(false)
# Dynamic call:
AssertionError: (no message)
# PFA call:
AssertionError: (no message)
88 changes: 88 additions & 0 deletions Zend/tests/partial_application/attributes_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
--TEST--
PFA inherits NoDiscard and SensitiveParameter attributes
--FILE--
<?php

#[Attribute]
class Test {}

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

function dump_attributes($function) {
$r = new ReflectionFunction($function);
var_dump($r->getAttributes());

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

echo "# Orig attributes:\n";

dump_attributes('f');

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

echo "# PFA attributes:\n";

dump_attributes($f);

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

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

$f = f(1, ?, ?, ?, ...)('normal param', 3, 'reified variadic', 'variadic');

?>
--EXPECTF--
Fatal error: Uncaught Exception in %s:%d
Stack trace:
#0 %s(%d): f(1, Object(SensitiveParameterValue), 3, Object(SensitiveParameterValue), Object(SensitiveParameterValue))
#1 %s(%d): {closure:pfa:%s:7}(Object(SensitiveParameterValue), 3, Object(SensitiveParameterValue), Object(SensitiveParameterValue))
#2 {main}
thrown in %s on line %d
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--
PFA preserves #[NoDiscard]
--FILE--
<?php

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

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

(void) $f(1);

?>
--EXPECTF--
Warning: The return value of function {closure:%s}() should either be used or intentionally ignored by casting it as (void) in %s on line 7
50 changes: 50 additions & 0 deletions Zend/tests/partial_application/clone.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
clone() can be partially applied
--FILE--
<?php

class C {
public function __construct(
public mixed $a,
public mixed $b,
) { }
}

$clone = clone(?);
var_dump($clone(new C(1, 2)));

$clone = clone(...);
var_dump($clone(new C(3, 4)));

$clone = clone(new C(5, 6), ?);
var_dump($clone(['a' => 7]));

$clone = clone(?, ['a' => 8]);
var_dump($clone(new C(9, 10)));

?>
--EXPECTF--
object(C)#%d (2) {
["a"]=>
int(1)
["b"]=>
int(2)
}
object(C)#%d (2) {
["a"]=>
int(3)
["b"]=>
int(4)
}
object(C)#%d (2) {
["a"]=>
int(7)
["b"]=>
int(6)
}
object(C)#%d (2) {
["a"]=>
int(8)
["b"]=>
int(10)
}
8 changes: 8 additions & 0 deletions Zend/tests/partial_application/compile_errors_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
PFA compile errors: multiple variadic placeholders
--FILE--
<?php
foo(..., ...);
?>
--EXPECTF--
Fatal error: Variadic placeholder may only appear once in %s on line %d
8 changes: 8 additions & 0 deletions Zend/tests/partial_application/compile_errors_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
PFA compile errors: variadic placeholder must be last
--FILE--
<?php
foo(..., ?);
?>
--EXPECTF--
Fatal error: Variadic placeholder must be last in %s on line %d
8 changes: 8 additions & 0 deletions Zend/tests/partial_application/compile_errors_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
PFA compile errors: placeholders can not appear after named args
--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--
PFA compile errors: variadic placeholder must be last, including after named args
--FILE--
<?php
foo(..., n: 5);
?>
--EXPECTF--
Fatal error: Variadic placeholder must be last in %s on line %d
8 changes: 8 additions & 0 deletions Zend/tests/partial_application/compile_errors_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
PFA compile errors: variadic placeholder must be last, including after positional args
--FILE--
<?php
foo(..., $a);
?>
--EXPECTF--
Fatal error: Variadic placeholder must be last in %s on line %d
8 changes: 8 additions & 0 deletions Zend/tests/partial_application/compile_errors_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
PFA compile errors: can not use unpacking in PFA, including with variadic placeholdres
--FILE--
<?php
foo(...["foo" => "bar"], ...);
?>
--EXPECTF--
Fatal error: Cannot combine partial application and unpacking in %s on line %d
Loading
Loading