Skip to content

[RFC] Partial Function Application #6898

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f445a7d
partial application
krakjoe Apr 19, 2021
f522e46
test coverage
krakjoe Jun 4, 2021
638dc40
closure bind/call support
krakjoe Jun 5, 2021
4c18688
fix a couple of bugs
krakjoe Jun 11, 2021
dcec4f9
fix another bug
krakjoe Jun 11, 2021
e29da6f
I need a break
krakjoe Jun 11, 2021
216c101
fix an asan error, and an off by one
krakjoe Jun 12, 2021
6bd2f25
a test, and fix
krakjoe Jun 16, 2021
4f4e6de
fix place holder -> placeholder
krakjoe Jun 17, 2021
003ecbe
fix inconsistency
krakjoe Jun 17, 2021
800be73
fix magic assertion failure where result is unused
krakjoe Jun 17, 2021
f7863f4
collect argv
krakjoe Jun 17, 2021
004166f
used/unused trampoline test
krakjoe Jun 17, 2021
667d3d6
drop dup check
krakjoe Jun 17, 2021
3fd01b6
fix ubsan
krakjoe Jun 17, 2021
90fcab6
Update Zend/zend_language_parser.y
krakjoe Jun 17, 2021
0fafba3
Update Zend/zend_compile.c
krakjoe Jun 17, 2021
bdf2108
drop dup check for permanent trampoline
krakjoe Jun 17, 2021
89a5330
fix flags
krakjoe Jun 17, 2021
ead14d3
fixing things
krakjoe Jun 17, 2021
b9aa841
Update Zend/zend_partial.c
krakjoe Jun 17, 2021
a1d76e6
fix tests, fix count
krakjoe Jun 17, 2021
88aa29c
comments
krakjoe Jun 17, 2021
81b64ff
drop silly macro
krakjoe Jun 17, 2021
67c3b66
fix another gc issue
krakjoe Jun 18, 2021
3f8a12a
fix uaf in unfinished calls, fix leak args
krakjoe Jun 18, 2021
bde700f
put assert back
krakjoe Jun 18, 2021
b699d65
fix indent, repeat type
krakjoe Jun 18, 2021
0e8a0a0
use hash add variants
krakjoe Jun 18, 2021
711b33a
insert null in debug info
krakjoe Jun 18, 2021
9400a69
drop sprintf for zend_string_init
krakjoe Jun 18, 2021
583264d
fix uaf, order of destruction
krakjoe Jun 18, 2021
6afa0d1
set ce ptr
krakjoe Jun 18, 2021
12d6006
fix null ptr deref
krakjoe Jun 18, 2021
fd62047
fix wrong signature check
krakjoe Jun 18, 2021
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
2 changes: 2 additions & 0 deletions Zend/Optimizer/zend_call_graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ ZEND_API int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_
case ZEND_DO_ICALL:
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
case ZEND_DO_FCALL_PARTIAL:
func_info->flags |= ZEND_FUNC_HAS_CALLS;
if (call_info) {
call_info->caller_call_opline = opline;
Expand All @@ -122,6 +123,7 @@ ZEND_API int 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 = 1;
Expand Down
9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: multiple ...
--FILE--
<?php
foo(..., ...);
?>
--EXPECTF--
Fatal error: Variadic placeholder may only appear once in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: only named arguments after ...
--FILE--
<?php
foo(..., ?);
?>
--EXPECTF--
Fatal error: Only named arguments may follow variadic placeholder in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: named arguments must come after placeholder
--FILE--
<?php
foo(n: 5, ?);
?>
--EXPECTF--
Fatal error: Named arguments must come after all placeholders 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--
Partial application compile errors: named arguments must come after variadic placeholder
--FILE--
<?php
foo(n: 5, ...);
?>
--EXPECTF--
Fatal error: Named arguments must come after all placeholders in %s on line %d
9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: follow variadic with un-named arg
--FILE--
<?php
foo(..., $a);
?>
--EXPECTF--
Fatal error: Only named arguments may follow variadic placeholder in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: mix application with unpack (placeholder after)
--FILE--
<?php
foo(...["foo" => "bar"], ...);
?>
--EXPECTF--
Fatal error: Cannot combine partial application and unpacking %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Partial application compile errors: mix application with unpack (placeholder before)
--FILE--
<?php
foo(..., ...["foo" => "bar"]);
?>
--EXPECTF--
Fatal error: Cannot combine partial application and unpacking %s on line %d

38 changes: 38 additions & 0 deletions Zend/tests/partial_application/errors_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
Partial application errors: placeholder count errors
--FILE--
<?php
function foo($a, $b, $c) {

}

try {
foo(?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

try {
foo(?, ?, ?, ?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

try {
property_exists(?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

try {
usleep(?, ?);
} catch (Error $ex) {
Copy link

Choose a reason for hiding this comment

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

For consistency, this should be ArgumentCountError ( https://3v4l.org/PbJh5 )

printf("%s\n", $ex->getMessage());
}
?>
--EXPECTF--
not enough arguments or placeholders for application of foo, 1 given and exactly 3 expected, declared in %s on line 2
too many arguments or placeholders for application of foo, 4 given and a maximum of 3 expected, declared in %s on line 2
not enough arguments or placeholders for application of property_exists, 1 given and exactly 2 expected
too many arguments or placeholders for application of usleep, 2 given and a maximum of 1 expected

17 changes: 17 additions & 0 deletions Zend/tests/partial_application/errors_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Partial application errors: named parameter overwrites placeholder
--FILE--
<?php
function foo($a) {

}

try {
foo(?, a: 1);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}
?>
--EXPECT--
Named parameter $a overwrites previous placeholder

78 changes: 78 additions & 0 deletions Zend/tests/partial_application/errors_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
--TEST--
Partial application errors: missing parameters
--FILE--
<?php
function foo($a, ...$b) {

}

function bar($a, $b, $c) {}

$foo = foo(?);

try {
$foo();
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

$foo = foo(?, ?);

try {
$foo(1);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

$bar = bar(?, ?, ...);

try {
$bar(1);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

class Foo {
public function bar($a, ...$b) {}
}

$foo = new Foo;

$bar = $foo->bar(?);

try {
$bar();
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

$usleep = usleep(...);

try {
$usleep();
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

$usleep = usleep(?);

try {
$usleep();
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

try {
$usleep(1, 2);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}
?>
--EXPECTF--
not enough arguments for application of foo, 0 given and exactly 1 expected, declared in %s on line 8
not enough arguments for application of foo, 1 given and exactly 2 expected, declared in %s on line 16
not enough arguments for application of bar, 1 given and at least 2 expected, declared in %s on line 24
not enough arguments for application of Foo::bar, 0 given and exactly 1 expected, declared in %s on line 38
not enough arguments for implementation of usleep, 0 given and exactly 1 expected
not enough arguments for application of usleep, 0 given and exactly 1 expected
too many arguments for application of usleep, 2 given and a maximum of 1 expected
14 changes: 14 additions & 0 deletions Zend/tests/partial_application/export_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
Partial application ast export
--INI--
assert.exception=1
--FILE--
<?php
try {
assert(0 && foo(?) && foo(...));
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}
?>
--EXPECT--
assert(0 && foo(?) && foo(...))
50 changes: 50 additions & 0 deletions Zend/tests/partial_application/extra_collect_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
Partial application named parameters: extra collection
--FILE--
<?php
function foo(...$args) {
var_dump($args);
}

$foo = foo(..., foo: "foo");

$bar = $foo(..., bar: "bar");

$baz = $bar(..., baz: "baz");

$baz();

$foo = foo(...);

$bar = $foo(..., bar: "bar");

$baz = $bar(..., baz: "baz");

$baz();

$foo = foo(..., foo: "foo");

$foo(bar: "bar");
?>
--EXPECT--
array(3) {
["foo"]=>
string(3) "foo"
["bar"]=>
string(3) "bar"
["baz"]=>
string(3) "baz"
}
array(2) {
["bar"]=>
string(3) "bar"
["baz"]=>
string(3) "baz"
}
array(2) {
["foo"]=>
string(3) "foo"
["bar"]=>
string(3) "bar"
}

24 changes: 24 additions & 0 deletions Zend/tests/partial_application/factory_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Partial application factory: pass
--FILE--
<?php
class Foo {
public function __destruct() {
printf("%s\n", __METHOD__);
}
}

$foo = new Foo(...);

$two = [$foo(), $foo()];

if ($two[0] === $two[1]) {
var_dump($two);
} else {
echo "OK\n";
}
?>
--EXPECTF--
OK
Foo::__destruct
Foo::__destruct
30 changes: 30 additions & 0 deletions Zend/tests/partial_application/factory_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--TEST--
Partial application factory: normal
--FILE--
<?php
class Foo {
public function __construct($a) {
printf("%s\n", __METHOD__);
}

public function __destruct() {
printf("%s\n", __METHOD__);
}
}

$foo = new Foo(...);

$two = [$foo(1), $foo(1)];

if ($two[0] === $two[1]) {
var_dump($two);
} else {
echo "OK\n";
}
?>
--EXPECTF--
Foo::__construct
Foo::__construct
OK
Foo::__destruct
Foo::__destruct
24 changes: 24 additions & 0 deletions Zend/tests/partial_application/factory_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Partial application factory: exception
--FILE--
<?php
class Foo {
public function __construct() {
throw new Exception("boo");
}

public function __destruct() {
printf("%s\n", __METHOD__);
}
}

$foo = new Foo(...);

try {
$foo();
} catch (Exception $ex) {
printf("%s\n", $ex->getMessage());
}
?>
--EXPECT--
boo
18 changes: 18 additions & 0 deletions Zend/tests/partial_application/factory_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Partial application factory object properties initialization
--FILE--
<?php
class Foo {
public function __construct(public int $arg = 1) {}
}

$foo = new Foo(...);

var_dump($foo());
?>
--EXPECTF--
object(Foo)#%d (1) {
["arg"]=>
int(1)
}

Loading