Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 42 additions & 0 deletions Zend/tests/partial_application/const_arg_opt_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
Constant argument optimization - strict_types bug
--CREDITS--
Ryan @ Calif.io
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=-1
opcache.file_update_protection=0
--FILE--
<?php

declare(strict_types=1);

function cand_86014_typed(int $bound, mixed $placeholder): void
{
echo 'CALLED:', get_debug_type($bound), ':', $bound, "\n";
}

function cand_86014_make_invalid(): Closure
{
return cand_86014_typed('123', ?);
}

try {
$partial = cand_86014_make_invalid();
echo "CONSTRUCTED\n";
} catch (Throwable $e) {
echo 'CREATE: ', get_class($e), ': ', $e->getMessage(), "\n";
}

if (isset($partial)) {
try {
$partial(null);
} catch (Throwable $e) {
echo 'CALL: ', get_class($e), ': ', $e->getMessage(), "\n";
}
}

?>
--EXPECT--
CREATE: TypeError: cand_86014_typed(): Argument #1 ($bound) must be of type int, string given
32 changes: 32 additions & 0 deletions Zend/tests/partial_application/const_arg_opt_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--TEST--
Constant argument optimization - strict_types
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=-1
opcache.file_update_protection=0
--FILE--
<?php
declare(strict_types=1);

function f(int $a, $b) { return $a; }

// Check that optimizations are enabled
$f = f(3, ?);
$usedVars = new ReflectionFunction($f)->getClosureUsedVariables();
if ($usedVars !== []) {
echo "const arg optimization was not applied\n";
var_dump($usedVars);
exit;
}

var_dump($f(0));

// Actual test. Not catching this as it disables optimizations
f("3", ?)(0);

?>
--EXPECTF--
int(3)

Fatal error: Uncaught TypeError: f(): Argument #1 ($a) must be of type int, string given in %a
24 changes: 24 additions & 0 deletions Zend/zend_partial.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,30 @@ static zend_ast *zp_compile_forwarding_call(
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);

/* This argument never changes, so we can burn it into the op_array
* and check its type ahead of time. */

zend_arg_info *arg_info;
if (offset < function->common.num_args) {
arg_info = &function->common.arg_info[offset];
} else if (function->common.fn_flags & ZEND_ACC_VARIADIC) {
arg_info = &function->common.arg_info[function->common.num_args];
} else {
arg_info = NULL;
}
if (arg_info && ZEND_TYPE_IS_SET(arg_info->type)
&& UNEXPECTED(!zend_check_type_ex(&arg_info->type, &argv[offset],
/* current_frame */ true, /* is_internal */ false))) {
zend_string *need_msg = zend_type_to_string_resolved(arg_info->type,
function->common.scope);
zend_argument_type_error_ex(function, offset + 1,
"must be of type %s, %s given",
ZSTR_VAL(need_msg), zend_zval_value_name(&argv[offset]));
Comment on lines +581 to +583

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this being thrown "at compile time"? As I imagine this could be unexpected.

@arnaud-lb arnaud-lb Aug 13, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly at compile time. This code executes when a PFA expression is evaluated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, then I can't see how this would differ in behaviour :)

zend_string_release(need_msg);
goto error;
}

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,
Expand Down
Loading