From 682a9be61b7e6e34bb04aea3e424961ff8462de6 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 14 Jul 2026 16:47:44 +0800 Subject: [PATCH] feat(compiler): add multi-return array adapter with safe parameter consumption - Implement multi-return forward args functionality for by-value parameters - Add support for parameter forwarding with php::takeValue wrapper - Create test cases for multi-return array adapter behavior - Update translator to handle forwarded arguments correctly - Add phpunit test coverage for multi-return tuple functionality - Implement proper argument consumption logic for multi-return scenarios --- phpunit/code/multi-return-tuple.php | 20 ++++ phpunit/src/MultiReturnTest.php | 13 +++ src/Translator.php | 8 +- .../array/multi-return-forward-args.phpt | 99 +++++++++++++++++++ 4 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 tests/compiler/array/multi-return-forward-args.phpt diff --git a/phpunit/code/multi-return-tuple.php b/phpunit/code/multi-return-tuple.php index 9113781c..10b0df5b 100644 --- a/phpunit/code/multi-return-tuple.php +++ b/phpunit/code/multi-return-tuple.php @@ -27,6 +27,26 @@ function phpunit_multi_repeated_value(): array return [$repeated, $repeated, $tail]; } +function phpunit_multi_forward_args( + mixed $value, + string $text, + array $items, + object $object, + int $count, + &$reference, + ...$rest, +): array { + return [$value, $text]; +} + +function phpunit_multi_forward_defaults( + string $text = 'default', + array $items = [], + ...$rest, +): array { + return [$text, $items]; +} + function phpunit_multi_side_effect(): array { return [time(), 2]; diff --git a/phpunit/src/MultiReturnTest.php b/phpunit/src/MultiReturnTest.php index 80f6a227..47c37144 100644 --- a/phpunit/src/MultiReturnTest.php +++ b/phpunit/src/MultiReturnTest.php @@ -46,6 +46,19 @@ final class MultiReturnTest extends TestCase 'array = php_phpunit_multi_values()', $code, ); + $this->assertStringContainsString( + 'return php::Array(typephp::detail::php_phpunit_multi_forward_args(' + . 'php::takeValue(value), php::takeValue(text), php::takeValue(items), ' + . 'php::takeValue(object), count, reference, php::takeValue(rest)));', + $code, + ); + $this->assertStringContainsString( + 'return php::Array(typephp::detail::php_phpunit_multi_forward_defaults(' + . 'php::takeValue(text), php::takeValue(items), php::takeValue(rest)));', + $code, + ); + $this->assertStringNotContainsString('php::takeValue(reference)', $code); + $this->assertStringNotContainsString('php::takeValue(count)', $code); $this->assertStringContainsString( 'std::tie(partialFirst, partialSecond, std::ignore) = typephp::detail::php_phpunit_multi_three_values()', $code, diff --git a/src/Translator.php b/src/Translator.php index b0d69677..676618a3 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -2713,7 +2713,7 @@ CODE; $cppCode .= $this->getIndent() . $cppType . ' ' . $var . ' = ' . $expr . ';' . PHP_EOL; } $callParam = $var; - if ($this->canConsumeWrapperArgument($argInfo)) { + if ($this->canConsumeForwardedArgument($argInfo)) { $callParam = 'php::takeValue(' . $var . ')'; } $callParams .= $callParam . ','; @@ -2739,7 +2739,7 @@ CODE; return $cppCode; } - private function canConsumeWrapperArgument(ArgInfo $argInfo): bool + private function canConsumeForwardedArgument(ArgInfo $argInfo): bool { if ($argInfo->byRef) { return false; @@ -3023,7 +3023,9 @@ CODE; if ($multiReturn) { $forwardArgs = implode(', ', array_map( - static fn($argInfo) => $argInfo->name, + fn($argInfo) => $this->canConsumeForwardedArgument($argInfo) + ? 'php::takeValue(' . $argInfo->name . ')' + : $argInfo->name, $this->functionDef->argInfoList, )); $code .= Type::ARRAY . ' ' . $nativeName . '(' . $this->functionDef->params . ') {' . PHP_EOL; diff --git a/tests/compiler/array/multi-return-forward-args.phpt b/tests/compiler/array/multi-return-forward-args.phpt new file mode 100644 index 00000000..b62d2645 --- /dev/null +++ b/tests/compiler/array/multi-return-forward-args.phpt @@ -0,0 +1,99 @@ +--TEST-- +Multi-return Array adapter safely consumes its by-value parameters +--FILE-- + 'direct']; + $directReference = 'before'; + [$value, $text, $items, $directObject, $count, $returnedReference, $rest] + = multi_return_forward_args('value', 'text', [1], $object, 2, $directReference, 'x'); + var_dump( + $value, + $text, + $items, + $directObject->name, + $count, + $directReference, + $returnedReference, + $rest, + ); + + $arrayReference = 'before'; + $array = multi_return_forward_args('array', 'adapter', [3], $object, 4, $arrayReference, 'y', 'z'); + var_dump($arrayReference, $array[0], $array[1], $array[2], $array[3]->name, $array[4], $array[5], $array[6]); + + $defaults = multi_return_forward_defaults(); + [$defaultText, $defaultItems, $defaultRest] = multi_return_forward_defaults(rest: 'named'); + var_dump($defaults, $defaultText, $defaultItems, $defaultRest); +} +?> +--EXPECT-- +string(5) "value" +string(4) "text" +array(1) { + [0]=> + int(1) +} +string(6) "direct" +int(2) +string(7) "updated" +string(7) "updated" +array(1) { + [0]=> + string(1) "x" +} +string(7) "updated" +string(5) "array" +string(7) "adapter" +array(1) { + [0]=> + int(3) +} +string(6) "direct" +int(4) +string(7) "updated" +array(2) { + [0]=> + string(1) "y" + [1]=> + string(1) "z" +} +array(3) { + [0]=> + string(7) "default" + [1]=> + array(0) { + } + [2]=> + array(0) { + } +} +string(7) "default" +array(0) { +} +array(1) { + ["rest"]=> + string(5) "named" +}