Commit edc169e7705 for php.net
commit edc169e7705d5e4411865e9be92b50e82be78f4e
Author: Arnaud Le Blanc <365207+arnaud-lb@users.noreply.github.com>
Date: Mon Jul 27 19:00:03 2026 +0200
[PFA 4/n] Optimize constant pre-bound arguments (#22829)
Pre-bound arguments are bound to the generated closure's lexical vars:
```
function f($a, $b) {}
$f = f(1, ?);
// Generates:
$tmp = 1;
$f = function ($b) use ($tmp) {
return f($tmp, $b);
};
```
Detect which pre-bound arguments are constant and burn them into the generated
closure instead:
```
function f($a, $b) {}
$f = f(1, ?);
// Generates:
$f = function ($b) {
return f(1, $b);
};
```
diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c
index 77dc322fbde..dcb4e8bc841 100644
--- a/Zend/Optimizer/dfa_pass.c
+++ b/Zend/Optimizer/dfa_pass.c
@@ -469,6 +469,34 @@ static uint32_t zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
}
}
}
+
+ if (call_info->caller_call_opline && call_info->caller_call_opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) {
+ /* Build a bitset of constant pre-bound PFA args: These are args whose value is alway the same for all
+ * instances of a PFA. */
+ uint32_t const_args = 0;
+ for (uint32_t i = 0, l = MIN(sizeof(const_args)*CHAR_BIT, call_info->num_args); i < l; i++) {
+ zend_op *send_opline = call_info->arg_info[i].opline;
+ if (send_opline->op1_type == IS_CONST) {
+ zval *value = CT_CONSTANT_EX(op_array, send_opline->op1.constant);
+ if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
+ /* Const exprs can evaluate to non-const zvals (e.g. objects), and are not idempotent */
+ continue;
+ }
+ const_args |= (UINT32_C(1) << i);
+ }
+ }
+
+ /* Pass the bitset to the ZEND_CALLABLE_CONVERT_PARTIAL opline. */
+ zend_op *call_opline = call_info->caller_call_opline;
+ if (call_opline->op2_type == IS_UNUSED) {
+ call_opline->op2.num = const_args;
+ } else {
+ ZEND_ASSERT(call_opline->op2_type == IS_CONST);
+ zval *zv = CT_CONSTANT_EX(op_array, call_opline->op2.constant);
+ Z_EXTRA_P(zv) = const_args;
+ }
+ }
+
call_info = call_info->next_callee;
} while (call_info);
}
diff --git a/Zend/tests/partial_application/const_arg_opt.phpt b/Zend/tests/partial_application/const_arg_opt.phpt
new file mode 100644
index 00000000000..d63456a96a5
--- /dev/null
+++ b/Zend/tests/partial_application/const_arg_opt.phpt
@@ -0,0 +1,162 @@
+--TEST--
+Constant argument optimization
+--DESCRIPTION--
+Pre-bound arguments that are constant can be burned into the generated
+op_array instead of being passed via the Closure's lexical vars.
+--ENV--
+A=1
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--FILE--
+<?php
+
+function f($a, $b) {
+ var_dump([$a, $b]);
+}
+
+function g() {
+ return 3;
+}
+
+function h($a, $b, $c) {
+ var_dump([$a, $b, $c]);
+}
+
+function i($a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8, $a9, $a10, $a11, $a12, $a13, $a14, $a15, $a16, $a17, $a18, $a19, $a20, $a21, $a22, $a23, $a24, $a25, $a26, $a27, $a28, $a29, $a30, $a31, $a32, $a33) {
+ var_dump($a33);
+}
+
+function print_lexical_vars($f) {
+ $vars = new ReflectionFunction($f)->getClosureUsedVariables();
+ if ($vars === []) {
+ echo "no lexical vars\n";
+ } else {
+ $varNames = array_keys($vars);
+ echo 'lexical vars: ', implode(', ', $varNames), "\n";
+ }
+}
+
+echo "# Non-constant pre-bound argument:\n";
+$f = f(getenv('A'), ?);
+print_lexical_vars($f);
+$f(2);
+
+echo "# Constant pre-bound argument:\n";
+$f = f(2, ?);
+print_lexical_vars($f);
+$f(2);
+
+echo "# Constant pre-bound argument (inverted):\n";
+$f = f(?, 2);
+print_lexical_vars($f);
+$f(1);
+
+echo "# Inlined pre-bound argument:\n";
+$f = f(g(), ?);
+print_lexical_vars($f);
+$f(2);
+
+echo "# Constexpr pre-bound argument:\n";
+const B = 4;
+$f = f(B, ?);
+print_lexical_vars($f);
+$f(2);
+
+echo "# Mixed arguments:\n";
+$f = h(5, getenv('A'), ?);
+print_lexical_vars($f);
+$f(2);
+
+echo "# Constant array pre-bound argument:\n";
+$f = f(['foo' => 'bar'], ?);
+print_lexical_vars($f);
+$f(2);
+
+echo "# Named arguments:\n";
+$f = h(1, c: ?, b: ?);
+print_lexical_vars($f);
+$f(2, 3);
+
+echo "# Many arguments (optimization can not be applied for all args) :\n";
+$f = i(?, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33);
+print_lexical_vars($f);
+$f(33);
+
+?>
+--EXPECT--
+# Non-constant pre-bound argument:
+lexical vars: a
+array(2) {
+ [0]=>
+ string(1) "1"
+ [1]=>
+ int(2)
+}
+# Constant pre-bound argument:
+no lexical vars
+array(2) {
+ [0]=>
+ int(2)
+ [1]=>
+ int(2)
+}
+# Constant pre-bound argument (inverted):
+no lexical vars
+array(2) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(2)
+}
+# Inlined pre-bound argument:
+no lexical vars
+array(2) {
+ [0]=>
+ int(3)
+ [1]=>
+ int(2)
+}
+# Constexpr pre-bound argument:
+lexical vars: a
+array(2) {
+ [0]=>
+ int(4)
+ [1]=>
+ int(2)
+}
+# Mixed arguments:
+lexical vars: b
+array(3) {
+ [0]=>
+ int(5)
+ [1]=>
+ string(1) "1"
+ [2]=>
+ int(2)
+}
+# Constant array pre-bound argument:
+no lexical vars
+array(2) {
+ [0]=>
+ array(1) {
+ ["foo"]=>
+ string(3) "bar"
+ }
+ [1]=>
+ int(2)
+}
+# Named arguments:
+no lexical vars
+array(3) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(3)
+ [2]=>
+ int(2)
+}
+# Many arguments (optimization can not be applied for all args) :
+lexical vars: a33
+int(33)
diff --git a/Zend/tests/partial_application/references_004.phpt b/Zend/tests/partial_application/references_004.phpt
index e5163c710da..8d56d7dceaf 100644
--- a/Zend/tests/partial_application/references_004.phpt
+++ b/Zend/tests/partial_application/references_004.phpt
@@ -1,5 +1,10 @@
--TEST--
PFA receives variadic param by ref if the actual function does
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+opcache.file_update_protection=0
--FILE--
<?php
@@ -26,9 +31,8 @@ function foo($a, &...$args) {
Closure [ <user> static function {closure:%s:%d} ] {
@@ %sreferences_004.php 13 - 13
- - Bound Variables [2] {
- Variable #0 [ $a ]
- Variable #1 [ $args2 ]
+ - Bound Variables [1] {
+ Variable #0 [ $args2 ]
}
- Parameters [2] {
diff --git a/Zend/tests/partial_application/variation_debug_001.phpt b/Zend/tests/partial_application/variation_debug_001.phpt
index 04b63f3c401..cf215329f48 100644
--- a/Zend/tests/partial_application/variation_debug_001.phpt
+++ b/Zend/tests/partial_application/variation_debug_001.phpt
@@ -1,12 +1,19 @@
--TEST--
PFA variation: var_dump(), user function
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+opcache.file_update_protection=0
+--ENV--
+A=20
--FILE--
<?php
function bar($a = 1, $b = 2, ...$c) {
}
-var_dump(bar(?, new stdClass, 20, new stdClass, four: 4));
+var_dump(bar(?, new stdClass, (int)getenv('A'), new stdClass, four: 4));
?>
--EXPECTF--
object(Closure)#%d (5) {
diff --git a/Zend/tests/partial_application/variation_debug_002.phpt b/Zend/tests/partial_application/variation_debug_002.phpt
index a7c4c2d76e4..a4df317bbae 100644
--- a/Zend/tests/partial_application/variation_debug_002.phpt
+++ b/Zend/tests/partial_application/variation_debug_002.phpt
@@ -1,8 +1,20 @@
--TEST--
PFA variation: var_dump(), internal function
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+opcache.file_update_protection=0
+--ENV--
+A=1
--FILE--
<?php
-var_dump(array_map(?, [1, 2, 3], [4, 5, 6], four: new stdClass, ...));
+
+function notconst($value) {
+ return getenv('A') ? $value : null;
+}
+
+var_dump(array_map(?, notconst([1, 2, 3]), notconst([4, 5, 6]), four: new stdClass, ...));
?>
--EXPECTF--
object(Closure)#%d (5) {
@@ -11,7 +23,7 @@
["file"]=>
string(%d) "%svariation_debug_002.php"
["line"]=>
- int(2)
+ int(7)
["static"]=>
array(3) {
["array"]=>
diff --git a/Zend/tests/partial_application/variation_variadics_001.phpt b/Zend/tests/partial_application/variation_variadics_001.phpt
index 850f0eda149..43b4ff23e29 100644
--- a/Zend/tests/partial_application/variation_variadics_001.phpt
+++ b/Zend/tests/partial_application/variation_variadics_001.phpt
@@ -1,5 +1,12 @@
--TEST--
PFA variation: variadics, user function
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+opcache.file_update_protection=0
+--ENV--
+A=1
--FILE--
<?php
function foo($a, ...$b) {
@@ -16,11 +23,6 @@ function foo($a, ...$b) {
Closure [ <user> static function {closure:%s:%d} ] {
@@ %s 6 - 6
- - Bound Variables [2] {
- Variable #0 [ $a ]
- Variable #1 [ $b2 ]
- }
-
- Parameters [1] {
Parameter #0 [ <optional> ...$b ]
}
diff --git a/Zend/tests/partial_application/variation_variadics_002.phpt b/Zend/tests/partial_application/variation_variadics_002.phpt
index 21d8169fc42..33330723f31 100644
--- a/Zend/tests/partial_application/variation_variadics_002.phpt
+++ b/Zend/tests/partial_application/variation_variadics_002.phpt
@@ -1,5 +1,10 @@
--TEST--
PFA variation: variadics, internal function
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+opcache.file_update_protection=0
--FILE--
<?php
$sprintf = sprintf("%d %d %d", 100, ...);
@@ -12,11 +17,6 @@
Closure [ <user> static function {closure:%s:%d} ] {
@@ %svariation_variadics_002.php 2 - 2
- - Bound Variables [2] {
- Variable #0 [ $format ]
- Variable #1 [ $values2 ]
- }
-
- Parameters [1] {
Parameter #0 [ <optional> mixed ...$values ]
}
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index 8e735c6aba7..a17cb3d91b9 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -1352,7 +1352,7 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
ZEND_CALL_NUM_ARGS(frame), ZEND_CALL_ARG(frame, 1),
extra_named_params, named_positions,
fcc_ast->filename, &ast->lineno,
- (void**)cache_slot, fcc_ast->name, flags);
+ (void**)cache_slot, fcc_ast->name, flags, 0);
if (named_positions) {
zend_array_release(named_positions);
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 420a7cc0865..9c80cccb7cb 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -4162,6 +4162,8 @@ static void zend_compile_call_partial(znode *result, zend_ast_fcc *fcc_ast, uint
if (!Z_ISUNDEF_P(named_positions)) {
opline->op2.constant = zend_add_literal(named_positions);
opline->op2_type = IS_CONST;
+ } else {
+ opline->op2.num = 0;
}
}
diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c
index a78db3f51ef..ec60383a2bc 100644
--- a/Zend/zend_partial.c
+++ b/Zend/zend_partial.c
@@ -66,6 +66,14 @@ static zend_always_inline bool zp_is_non_static_closure(const zend_function *fun
return ((function->common.fn_flags & (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)) == ZEND_ACC_CLOSURE);
}
+/* Whether argument at offset 'offset' is const. Such arguments can be burned into the generated op_array */
+static inline bool zp_is_const_arg(uint32_t const_args, uint32_t offset) {
+ if (offset < sizeof(const_args) * CHAR_BIT) {
+ return const_args & (UINT32_C(1) << offset);
+ }
+ return false;
+}
+
static zend_never_inline ZEND_COLD void zp_args_underflow(
const zend_function *function, uint32_t args, uint32_t expected)
{
@@ -179,7 +187,7 @@ static zend_string *zp_get_func_param_name(const zend_function *function, uint32
* including params and used vars. */
static zp_names *zp_assign_names(uint32_t argc, zval *argv,
zend_function *function, bool variadic_partial,
- zend_array *extra_named_params)
+ zend_array *extra_named_params, uint32_t const_args)
{
zp_names *names = zend_arena_calloc(&CG(ast_arena),
1, zend_safe_address_guarded(argc, sizeof(*names->params), offsetof(zp_names, params)));
@@ -226,7 +234,7 @@ static zp_names *zp_assign_names(uint32_t argc, zval *argv,
/* Assign names for pre-bound params (lexical vars).
* There may be clashes, we ensure to generate unique names. */
for (uint32_t offset = 0; offset < argc; offset++) {
- if (Z_IS_PLACEHOLDER_P(&argv[offset]) || Z_ISUNDEF(argv[offset])) {
+ if (Z_IS_PLACEHOLDER_P(&argv[offset]) || Z_ISUNDEF(argv[offset]) || zp_is_const_arg(const_args, offset)) {
continue;
}
uint32_t n = 2;
@@ -510,7 +518,7 @@ static zend_ast *zp_compile_forwarding_call(
zp_names *var_names, bool uses_variadic_placeholder, uint32_t num_args,
zend_class_entry *called_scope, zend_type return_type,
bool forward_superfluous_args,
- zend_ast *stmts_ast)
+ zend_ast *stmts_ast, uint32_t const_args)
{
bool is_assert = zend_string_equals(function->common.function_name,
ZSTR_KNOWN(ZEND_STR_ASSERT));
@@ -551,6 +559,9 @@ static zend_ast *zp_compile_forwarding_call(
default_value_ast = zend_ast_create_zval(&default_value);
}
args_ast = zend_ast_list_add(args_ast, default_value_ast);
+ } else if (zp_is_const_arg(const_args, offset)) {
+ ZEND_ASSERT(Z_TYPE(argv[offset]) < IS_OBJECT);
+ args_ast = zend_ast_list_add(args_ast, zend_ast_create_zval(&argv[offset]));
} else {
args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_VAR,
zend_ast_create_zval_from_str(zend_string_copy(var_names->params[offset]))));
@@ -661,7 +672,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
const zend_array *named_positions,
zend_string *declaring_filename,
const uint32_t *declaring_lineno_ptr, void **cache_slot,
- zend_string *pfa_name, uint32_t flags) {
+ zend_string *pfa_name, uint32_t flags, uint32_t const_args) {
zend_op_array *op_array = NULL;
@@ -771,7 +782,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
/* Assign variable names */
zp_names *var_names = zp_assign_names(argc, argv, function,
- uses_variadic_placeholder, extra_named_params);
+ uses_variadic_placeholder, extra_named_params, const_args);
/* Generate AST */
@@ -825,15 +836,17 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
default_value_ast, attributes_ast, NULL, NULL);
} else if (!Z_ISUNDEF(argv[offset])) {
- // TODO: If the pre-bound parameter is a literal, it can be a
- // literal in the function body instead of a lexical var.
- zend_ast *lexical_var_ast = zend_ast_create_zval_from_str(
- zend_string_copy(var_names->params[offset]));
- if (zp_arg_must_be_sent_by_ref(function, offset+1)) {
- lexical_var_ast->attr = ZEND_BIND_REF;
+ if (zp_is_const_arg(const_args, offset)) {
+ /* Will be burned into the op_array */
+ } else {
+ zend_ast *lexical_var_ast = zend_ast_create_zval_from_str(
+ zend_string_copy(var_names->params[offset]));
+ if (zp_arg_must_be_sent_by_ref(function, offset+1)) {
+ lexical_var_ast->attr = ZEND_BIND_REF;
+ }
+ lexical_vars_ast = zend_ast_list_add(
+ lexical_vars_ast, lexical_var_ast);
}
- lexical_vars_ast = zend_ast_list_add(
- lexical_vars_ast, lexical_var_ast);
}
}
@@ -894,7 +907,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
no_forwarding_ast = zp_compile_forwarding_call(this_ptr, function,
argc, argv, extra_named_params,
var_names, uses_variadic_placeholder, num_params,
- called_scope, return_type, false, no_forwarding_ast);
+ called_scope, return_type, false, no_forwarding_ast, const_args);
if (!no_forwarding_ast) {
ZEND_ASSERT(EG(exception));
@@ -904,7 +917,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
forwarding_ast = zp_compile_forwarding_call(this_ptr, function,
argc, argv, extra_named_params,
var_names, uses_variadic_placeholder, num_params,
- called_scope, return_type, true, forwarding_ast);
+ called_scope, return_type, true, forwarding_ast, const_args);
if (!forwarding_ast) {
ZEND_ASSERT(EG(exception));
@@ -931,7 +944,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
stmts_ast = zp_compile_forwarding_call(this_ptr, function,
argc, argv, extra_named_params,
var_names, uses_variadic_placeholder, num_params,
- called_scope, return_type, false, stmts_ast);
+ called_scope, return_type, false, stmts_ast, const_args);
if (!stmts_ast) {
ZEND_ASSERT(EG(exception));
@@ -1000,7 +1013,7 @@ static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *funct
const zend_array *named_positions,
zend_string *declaring_filename,
const uint32_t *declaring_lineno_ptr, void **cache_slot,
- zend_string *pfa_name, uint32_t flags) {
+ zend_string *pfa_name, uint32_t flags, uint32_t const_args) {
if (EXPECTED(function->type == ZEND_INTERNAL_FUNCTION
? cache_slot[0] == function
@@ -1015,7 +1028,7 @@ static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *funct
if (UNEXPECTED(!op_array)) {
op_array = zp_compile(this_ptr, function, argc, argv,
extra_named_params, named_positions, declaring_filename, declaring_lineno_ptr,
- cache_slot, pfa_name, flags);
+ cache_slot, pfa_name, flags, const_args);
}
if (EXPECTED(op_array) && !(function->common.fn_flags & ZEND_ACC_NEVER_CACHE)) {
@@ -1037,7 +1050,7 @@ static void zp_free_unbound_args(uint32_t start, uint32_t argc, zval *argv)
/* Bind pre-bound arguments as lexical vars */
static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *argv,
- zend_array *extra_named_params) {
+ zend_array *extra_named_params, uint32_t const_args) {
zend_arg_info *arg_infos = function->common.arg_info;
uint32_t bind_offset = 0;
@@ -1052,7 +1065,7 @@ static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *
for (uint32_t offset = 0; offset < argc; offset++) {
zval *var = &argv[offset];
- if (Z_IS_PLACEHOLDER_P(var) || Z_ISUNDEF_P(var)) {
+ if (Z_IS_PLACEHOLDER_P(var) || Z_ISUNDEF_P(var) || zp_is_const_arg(const_args, offset)) {
continue;
}
zend_arg_info *arg_info;
@@ -1095,14 +1108,14 @@ void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
const zend_array *named_positions,
zend_string *declaring_filename,
const uint32_t *declaring_lineno_ptr, void **cache_slot,
- zend_string *pfa_name, uint32_t flags) {
+ zend_string *pfa_name, uint32_t flags, uint32_t const_args) {
ZEND_ASSERT(pfa_name);
const zend_op_array *op_array = zp_get_op_array(this_ptr, function, argc, argv,
extra_named_params, named_positions,
declaring_filename, declaring_lineno_ptr,
- cache_slot, pfa_name, flags);
+ cache_slot, pfa_name, flags, const_args);
if (UNEXPECTED(!op_array)) {
ZEND_ASSERT(EG(exception));
@@ -1130,7 +1143,7 @@ void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
function->common.scope, called_scope, &object,
(function->common.fn_flags & ZEND_ACC_CLOSURE) != 0);
- zp_bind(result, function, argc, argv, extra_named_params);
+ zp_bind(result, function, argc, argv, extra_named_params, const_args);
}
void zend_partial_op_array_dtor(zval *pDest)
diff --git a/Zend/zend_partial.h b/Zend/zend_partial.h
index 386823261e2..d3fcdae6afc 100644
--- a/Zend/zend_partial.h
+++ b/Zend/zend_partial.h
@@ -36,7 +36,7 @@ void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
const zend_array *named_positions,
zend_string *declaring_filename,
const uint32_t *declaring_lineno_ptr, void **cache_slot,
- zend_string *pfa_name, uint32_t flags);
+ zend_string *pfa_name, uint32_t flags, uint32_t const_args);
void zend_partial_op_array_dtor(zval *pDest);
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 3466bc68d69..d14230514b3 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -9875,6 +9875,13 @@ ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CONST, CONST|UNUSED, NUM)
void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS);
zval *named_positions = GET_OP2_ZVAL_PTR();
zend_string *pfa_name = Z_STR_P(GET_OP1_ZVAL_PTR());
+ uint32_t const_args;
+
+ if (OP2_TYPE == IS_UNUSED) {
+ const_args = opline->op2.num;
+ } else {
+ const_args = Z_EXTRA_P(named_positions);
+ }
zend_partial_create(EX_VAR(opline->result.var),
&call->This, call->func,
@@ -9883,7 +9890,7 @@ ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CONST, CONST|UNUSED, NUM)
call->extra_named_params : NULL,
OP2_TYPE == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
EX(func)->op_array.filename, &opline->lineno, cache_slot,
- pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS);
+ pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS, const_args);
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_release(call->extra_named_params);
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index c0a23e49d02..53bcdccd971 100644
Binary files a/Zend/zend_vm_execute.h and b/Zend/zend_vm_execute.h differ