Skip to content

Commit b5fba85

Browse files
arnaud-lbkrakjoe
andauthored
Partial function application (#20848)
RFC: https://wiki.php.net/rfc/partial_function_application_v2 Co-authored-by: Joe Watkins <krakjoe@php.net>
1 parent 47355da commit b5fba85

161 files changed

Lines changed: 8057 additions & 697 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.6.0alpha3
44

5+
- Core:
6+
. Implemented partial function application RFC. (Arnaud)
7+
58
- GMP:
69
. Fixed GMP power and shift operators to reject GMP right operands outside
710
the unsigned long range instead of silently truncating them. (Weilin Du)

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ PHP 8.6 UPGRADE NOTES
235235
RFC: https://wiki.php.net/rfc/debugable-enums
236236
. #[\Override] can now be applied to class constants, including enum cases.
237237
RFC: https://wiki.php.net/rfc/override_constants
238+
. Implemented partial function application
239+
RFC: https://wiki.php.net/rfc/partial_function_application_v2
238240

239241
- Curl:
240242
. curl_getinfo() return array now includes a new size_delivered key, which

UPGRADING.INTERNALS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES
132132
. zend_argument_error_variadic() now takes a new 'function' parameters.
133133
. Added zend_argument_error_ex(), zend_argument_type_error_ex(),
134134
zend_argument_value_error_ex().
135+
. Added zend_ast_dup().
136+
. Added zend_compile_ast().
137+
. Added zend_check_type_ex().
138+
. Added zend_create_partial_closure().
135139

136140
========================
137141
2. Build system changes

Zend/Optimizer/compact_literals.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
733733
case ZEND_SEND_VAR_NO_REF_EX:
734734
case ZEND_SEND_REF:
735735
case ZEND_SEND_FUNC_ARG:
736+
case ZEND_SEND_PLACEHOLDER:
736737
case ZEND_CHECK_FUNC_ARG:
737738
if (opline->op2_type == IS_CONST) {
738739
opline->result.num = cache_size;
@@ -745,6 +746,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
745746
cache_size += sizeof(void *);
746747
}
747748
break;
749+
case ZEND_CALLABLE_CONVERT_PARTIAL:
750+
opline->op1.num = cache_size;
751+
cache_size += 2 * sizeof(void *);
752+
break;
748753
}
749754
opline++;
750755
}

Zend/Optimizer/optimize_func_calls.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
191191
case ZEND_DO_UCALL:
192192
case ZEND_DO_FCALL_BY_NAME:
193193
case ZEND_CALLABLE_CONVERT:
194+
case ZEND_CALLABLE_CONVERT_PARTIAL:
194195
call--;
195196
if (call_stack[call].func && call_stack[call].opline) {
196197
zend_op *fcall = call_stack[call].opline;
@@ -223,13 +224,14 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
223224
* At this point we also know whether or not the result of
224225
* the DO opcode is used, allowing to optimize calls to
225226
* ZEND_ACC_NODISCARD functions. */
226-
if (opline->opcode != ZEND_CALLABLE_CONVERT) {
227+
if (opline->opcode != ZEND_CALLABLE_CONVERT && opline->opcode != ZEND_CALLABLE_CONVERT_PARTIAL) {
227228
opline->opcode = zend_get_call_op(fcall, call_stack[call].func, !RESULT_UNUSED(opline));
228229
}
229230

230231
if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
231232
&& call_stack[call].try_inline
232-
&& opline->opcode != ZEND_CALLABLE_CONVERT) {
233+
&& opline->opcode != ZEND_CALLABLE_CONVERT
234+
&& opline->opcode != ZEND_CALLABLE_CONVERT_PARTIAL) {
233235
zend_try_inline_call(op_array, fcall, opline, call_stack[call].func);
234236
}
235237
}

Zend/Optimizer/zend_call_graph.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
124124
case ZEND_DO_UCALL:
125125
case ZEND_DO_FCALL_BY_NAME:
126126
case ZEND_CALLABLE_CONVERT:
127+
case ZEND_CALLABLE_CONVERT_PARTIAL:
127128
func_info->flags |= ZEND_FUNC_HAS_CALLS;
128129
if (call_info) {
129130
call_info->caller_call_opline = opline;
@@ -140,6 +141,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
140141
case ZEND_SEND_VAR_NO_REF:
141142
case ZEND_SEND_VAR_NO_REF_EX:
142143
case ZEND_SEND_USER:
144+
case ZEND_SEND_PLACEHOLDER:
143145
if (call_info) {
144146
if (opline->op2_type == IS_CONST) {
145147
call_info->named_args = true;

Zend/Optimizer/zend_inference.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3903,6 +3903,7 @@ static zend_always_inline zend_result _zend_update_type_info(
39033903
}
39043904
break;
39053905
case ZEND_CALLABLE_CONVERT:
3906+
case ZEND_CALLABLE_CONVERT_PARTIAL:
39063907
UPDATE_SSA_TYPE(MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN, ssa_op->result_def);
39073908
UPDATE_SSA_OBJ_TYPE(zend_ce_closure, /* is_instanceof */ false, ssa_op->result_def);
39083909
break;

Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt

Lines changed: 0 additions & 10 deletions
This file was deleted.

Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
PFA of assert() behaves like a dynamic call to assert()
3+
--FILE--
4+
<?php
5+
6+
try {
7+
echo "# Static call:\n";
8+
assert(false);
9+
} catch (Error $e) {
10+
echo $e::class, ": ", $e->getMessage(), "\n";
11+
}
12+
13+
try {
14+
echo "# Dynamic call:\n";
15+
(function ($f) { $f(false); })('assert');
16+
} catch (Error $e) {
17+
echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n";
18+
}
19+
20+
try {
21+
echo "# PFA call:\n";
22+
$f = assert(?);
23+
$f(false);
24+
} catch (Error $e) {
25+
echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n";
26+
}
27+
28+
try {
29+
echo "# Upper-case assert():\n";
30+
$f = ASSERT(?);
31+
$f(false);
32+
} catch (Error $e) {
33+
echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n";
34+
}
35+
36+
?>
37+
--EXPECT--
38+
# Static call:
39+
AssertionError: assert(false)
40+
# Dynamic call:
41+
AssertionError: (no message)
42+
# PFA call:
43+
AssertionError: (no message)
44+
# Upper-case assert():
45+
AssertionError: (no message)

0 commit comments

Comments
 (0)