fix(compiler): support first-class callable of internal functions (#93)
* fix(compiler): support first-class callable of internal functions PHP 8.1 `foo(...)` builds a Closure from a single VariadicPlaceholder argument instead of calling foo. Two paths mishandled it: - checkInternalFunctionArgCount() counted the placeholder as a real argument, so first-class callables of internal functions requiring 2+ parameters (e.g. `posix_kill(...)`) failed with a bogus "expects at least N arguments, 1 given". - The math-function return-type optimization read `$expr->args[0]->value` on callables such as `round(...)`, crashing on the undefined VariadicPlaceholder::$value property. Guard both with isFirstClassCallable() so the expression falls through to Closure (Type::OBJECT) resolution. * fix(compiler): infer builtin callables before return optimizations --------- Co-authored-by: Tinywan <tinywan@gmail.com>master
parent
38d828cbbe
commit
055dee01f8
5 changed files with 72 additions and 4 deletions
@ -0,0 +1,19 @@ |
||||
--TEST-- |
||||
First-class callable of builtins requiring multiple arguments |
||||
--FILE-- |
||||
<?php |
||||
function main(): void { |
||||
// str_repeat() and array_slice() are internal functions with 2 required |
||||
// parameters. `foo(...)` builds a Closure; its single VariadicPlaceholder |
||||
// must not be counted as one real argument and rejected by the internal |
||||
// function argument-count check. |
||||
$repeat = str_repeat(...); |
||||
echo $repeat('ab', 3), "\n"; |
||||
|
||||
$slice = array_slice(...); |
||||
echo implode(',', $slice([1, 2, 3, 4, 5], 1, 2)), "\n"; |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
ababab |
||||
2,3 |
||||
@ -0,0 +1,16 @@ |
||||
--TEST-- |
||||
First-class callable of a math function (round) |
||||
--FILE-- |
||||
<?php |
||||
function main(): void { |
||||
// round() is in the math-function return-type optimization list. For the |
||||
// first-class callable `round(...)`, args[0] is a VariadicPlaceholder with |
||||
// no ->value, so the optimization must be skipped and the expression must |
||||
// resolve to a Closure instead of crashing. |
||||
$values = [1.4, 2.6, 3.5]; |
||||
$rounded = array_map(round(...), $values); |
||||
echo implode(',', $rounded), "\n"; |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
1,3,4 |
||||
@ -0,0 +1,14 @@ |
||||
--TEST-- |
||||
First-class callable of a stream-returning function |
||||
--FILE-- |
||||
<?php |
||||
function useOpener(Closure $open): void { |
||||
echo "callable\n"; |
||||
} |
||||
|
||||
function main(): void { |
||||
useOpener(fopen(...)); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
callable |
||||
Loading…
Reference in new issue