diff --git a/phpunit/code/intval-single-argument.php b/phpunit/code/intval-single-argument.php new file mode 100644 index 00000000..269416b4 --- /dev/null +++ b/phpunit/code/intval-single-argument.php @@ -0,0 +1,15 @@ +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 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)); + } +} diff --git a/src/Optimizer/FuncCallOptimizer.php b/src/Optimizer/FuncCallOptimizer.php index fe2ae690..419a2f45 100644 --- a/src/Optimizer/FuncCallOptimizer.php +++ b/src/Optimizer/FuncCallOptimizer.php @@ -527,8 +527,14 @@ trait FuncCallOptimizer 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. + if (count($expr->args) !== 1) { + return false; + } $arg = $expr->args[0]->value; $type = $this->detectTypeOfExpr($arg); $nativeClass = $this->detectClassOfExpr($arg); diff --git a/tests/compiler/stdlib/type_conv.phpt b/tests/compiler/stdlib/type_conv.phpt index a71656c6..dcc7ddbd 100644 --- a/tests/compiler/stdlib/type_conv.phpt +++ b/tests/compiler/stdlib/type_conv.phpt @@ -18,6 +18,14 @@ 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)); + // floatval var_dump(floatval(42)); var_dump(floatval("3.14")); @@ -44,6 +52,11 @@ int(42) int(3) int(1) int(0) +int(255) +int(26) +int(5) +int(511) +int(255) float(42) float(3.14) float(42)