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
15 changes: 15 additions & 0 deletions phpunit/code/intval-single-argument.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
/**
* This file is part of TypePHP(AOT).
*
* @link https://www.swoole.com/aot/
* @contact service@swoole.com
*/

function main(): void
{
var_dump(intval('42'));
var_dump(strval(42));
var_dump(floatval('3.14'));
var_dump(boolval(1));
}
21 changes: 21 additions & 0 deletions phpunit/code/intval-unpacked-argument.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php
/**
* This file is part of TypePHP(AOT).
*
* @link https://www.swoole.com/aot/
* @contact service@swoole.com
*/

function main(): void
{
$withBase = ['ff', 16];
$single = ['42'];

var_dump(intval(...$withBase));
var_dump(intval(...$single));
var_dump(strval(...$single));
var_dump(floatval(...$single));
var_dump(boolval(...$single));
var_dump(intval('ff', ...[16]));
var_dump(intval(value: '42'));
}
15 changes: 15 additions & 0 deletions phpunit/code/intval-with-base.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
/**
* This file is part of TypePHP(AOT).
*
* @link https://www.swoole.com/aot/
* @contact service@swoole.com
*/

function main(): void
{
$base = 16;

var_dump(intval('ff', 16));
var_dump(intval('ff', $base));
}
67 changes: 67 additions & 0 deletions phpunit/src/ConversionArityTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
/**
* This file is part of TypePHP(AOT).
*
* @link https://www.swoole.com/aot/
* @contact service@swoole.com
*/

namespace TypePhp\Tests;

use PHPUnit\Framework\TestCase;
use TypePhp\CompilerTest;

/**
* @internal
* @coversNothing
*/
class ConversionArityTest extends TestCase
{
public function testIntvalWithABaseReachesTheRuntimeFunction(): void
{
$cpp = $this->compileToCpp('intval-with-base.php');

// The Native cast carries no base, so both calls must keep the second
// argument by going through the dynamic path.
self::assertStringNotContainsString('php::toInt(', $cpp);
self::assertSame(2, substr_count($cpp, '16L'));
}

public function testUnpackedAndNamedArgumentsStayOnTheDynamicPath(): void
{
$cpp = $this->compileToCpp('intval-unpacked-argument.php');

// An unpacked argument is one Node\Arg whatever its runtime arity is,
// so the array itself must never be handed to a Native cast.
self::assertStringNotContainsString('php::toInt(', $cpp);
self::assertStringNotContainsString('php::toString(', $cpp);
self::assertStringNotContainsString('php::toFloat(', $cpp);
self::assertStringNotContainsString('php::toBool(', $cpp);

// Five full unpacks plus the partial intval('ff', ...[16]).
self::assertSame(6, substr_count($cpp, 'appendUnpacked('));
}

public function testSingleArgumentConversionsStillLowerToNativeCasts(): void
{
$cpp = $this->compileToCpp('intval-single-argument.php');

self::assertStringContainsString('php::toInt(', $cpp);
self::assertStringContainsString('php::toString(', $cpp);
self::assertStringContainsString('php::toFloat(', $cpp);
self::assertStringContainsString('php::toBool(', $cpp);
}

private function compileToCpp(string $file): string
{
global $translator;

$compiler = CompilerTest::create(TYPEPHP_ROOT_PATH);
$translator = $compiler;
$source = TYPEPHP_ROOT_PATH . '/phpunit/code/' . $file;
$compiler->addFiles([$source]);
$compiler->prepareFile($source);

return file_get_contents($compiler->convertFile($source));
}
}
19 changes: 17 additions & 2 deletions src/Optimizer/FuncCallOptimizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -527,8 +527,23 @@ protected function genVariadicCall(string $target, Node\Expr\FuncCall $expr, str
return $target . '(' . implode(', ', $args) . ')';
}

protected function dispatchConversion(Node\Expr\FuncCall $expr, string $convType): string
{
protected function dispatchConversion(Node\Expr\FuncCall $expr, string $convType): string|false
{
// These four are lowered as single-argument Native casts, which cannot
// carry intval()'s $base. Any other arity must reach the runtime
// function instead of silently dropping the extra argument.
//
// An unpacked or named argument is a single Node\Arg whatever its
// runtime arity turns out to be, so neither may be read as the value
// being converted; both stay on the dynamic path like dispatchFuncCall()
// already does for every other builtin.
if (count($expr->args) !== 1
|| !($expr->args[0] instanceof Node\Arg)
|| $expr->args[0]->unpack
|| $expr->args[0]->name !== null
) {
return false;
}
$arg = $expr->args[0]->value;
$type = $this->detectTypeOfExpr($arg);
$nativeClass = $this->detectClassOfExpr($arg);
Expand Down
35 changes: 35 additions & 0 deletions tests/compiler/stdlib/type_conv.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,29 @@ function main() {
var_dump(intval(true));
var_dump(intval(false));

// intval with an explicit base
var_dump(intval("ff", 16));
var_dump(intval("0x1A", 16));
var_dump(intval("101", 2));
var_dump(intval("777", 8));
$base = 16;
var_dump(intval("ff", $base));

// An unpacked argument carries its own arity, which only the runtime
// knows, so these must not be lowered as single-argument casts.
$withBase = ["ff", 16];
$single = ["42"];
var_dump(intval(...$withBase));
var_dump(intval(...$single));
var_dump(strval(...$single));
var_dump(floatval(...$single));
var_dump(boolval(...$single));
var_dump(intval("ff", ...[16]));

// A named argument is likewise a single Arg node that does not have to
// be the value being converted.
var_dump(intval(value: "42"));

// floatval
var_dump(floatval(42));
var_dump(floatval("3.14"));
Expand All @@ -44,6 +67,18 @@ int(42)
int(3)
int(1)
int(0)
int(255)
int(26)
int(5)
int(511)
int(255)
int(255)
int(42)
string(2) "42"
float(42)
bool(true)
int(255)
int(42)
float(42)
float(3.14)
float(42)
Expand Down
Loading