fix(compiler): preserve dynamic arithmetic result types (#133) --skip-tests

* fix(compiler): preserve dynamic arithmetic result types

* fix(compiler): preserve referenced arithmetic result types
master
yavon007 2 weeks ago committed by GitHub
parent cff557b649
commit 4f61dda6c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 13
      phpunit/code/dynamic-arithmetic-result.php
  2. 31
      phpunit/src/DynamicArithmeticResultTest.php
  3. 7
      src/CompilerBase.php
  4. 139
      tests/compiler/dynamic_arithmetic_result.phpt

@ -0,0 +1,13 @@
<?php
function dynamicResult(array $values, int $divisor): array
{
return [$values['x'] / $divisor];
}
function nativeIntResult(int $left, int $right): int
{
return $left / $right;
}
function nativeFloatResult(float $left, float $right): float
{
return $left / $right;
}

@ -0,0 +1,31 @@
<?php
use TypePhp\CompilerTest;
final class DynamicArithmeticResultTest extends \BaseTest
{
public function testDynamicResultRemainsBoxedAndNativeArithmeticStaysNative(): void
{
global $translator;
$compiler = CompilerTest::create(TYPEPHP_ROOT_PATH);
$translator = $compiler;
$source = TYPEPHP_ROOT_PATH . '/phpunit/code/dynamic-arithmetic-result.php';
$compiler->addFiles([$source]);
$compiler->prepareFile($source);
$code = file_get_contents($compiler->convertFile($source));
self::assertIsString($code);
$dynamic = substr($code, strpos($code, 'php_dynamicresult('));
$dynamic = substr($dynamic, 0, strpos($dynamic, "\n}") + 3);
self::assertDoesNotMatchRegularExpression('/php::toInt\([^;]*divisor/s', $dynamic);
self::assertMatchesRegularExpression('/php::Int php_nativeintresult\(php::Int left, php::Int right\)/', $code);
self::assertMatchesRegularExpression('/php::Float php_nativefloatresult\(php::Float left, php::Float right\)/', $code);
foreach (['php_nativeintresult(', 'php_nativefloatresult('] as $name) {
$body = substr($code, strpos($code, $name));
$body = substr($body, 0, strpos($body, "\n}") + 3);
self::assertStringContainsString('((left) / (right))', $body);
self::assertStringNotContainsString('php::Var', $body);
}
self::assertMatchesRegularExpression('/php::Var (tmp_var_\d+);.*?\1 = \(+tmp_var_\d+\) \/ \(divisor\)/s', $dynamic);
}
}

@ -3458,6 +3458,13 @@ class CompilerBase implements PropertyAccessContext
} }
return Type::BIGINT; return Type::BIGINT;
} }
// A dynamic operand keeps the runtime result type. Inferring
// Int from the other operand would truncate floating results
// when ordered evaluation materializes this expression.
if ($leftType === Type::VAR || $leftType === Type::REF
|| $rightType === Type::VAR || $rightType === Type::REF) {
return Type::VAR;
}
if ($leftType === Type::FLOAT || $rightType === Type::FLOAT) { if ($leftType === Type::FLOAT || $rightType === Type::FLOAT) {
return Type::FLOAT; return Type::FLOAT;
} }

@ -0,0 +1,139 @@
--TEST--
Dynamic arithmetic values keep PHP numeric semantics in nested array returns
--FILE--
<?php
function dynamic_arithmetic(array $values, int $n): array
{
return [
'forward' => [
'add' => $values['float'] + $n,
'subtract' => $values['float'] - $n,
'multiply' => $values['float'] * $n,
'divide' => $values['float'] / $n,
'integer_divide' => $values['integer'] / $n,
],
'reverse' => [
'add' => $n + $values['float'],
'subtract' => $n - $values['float'],
'multiply' => $n * $values['float'],
'divide' => $n / $values['float'],
],
];
}
function dynamic_return(mixed $value, int $n): mixed
{
return $value / $n;
}
function dynamic_nested(array $values, int $n): mixed
{
return ($values['float'] + $n) * $n;
}
function dynamic_kinds(mixed $value, int $n, float $fraction): array
{
return [$value / $n, $value + $fraction, $fraction - $value,
$value ** $n, $value % $n, $value << $n, $value >> $n,
$value & $n, $value | $n, $value ^ $n];
}
function divisor(): int
{
return 2;
}
function dynamic_reference_arithmetic(): array
{
$values = [5.5];
foreach ($values as &$value) {
return [
'forward' => [$value / divisor(), $value + divisor(), $value - divisor(), $value * divisor()],
'reverse' => [divisor() / $value, divisor() + $value, divisor() - $value, divisor() * $value],
];
}
}
function main()
{
var_dump(dynamic_arithmetic(['float' => 5.5, 'integer' => 5], 2));
var_dump(dynamic_return(5.5, 2), dynamic_nested(['float' => 5.5], 2));
var_dump(dynamic_kinds(4, 2, 0.5));
var_dump(dynamic_reference_arithmetic());
}
?>
--EXPECT--
array(2) {
["forward"]=>
array(5) {
["add"]=>
float(7.5)
["subtract"]=>
float(3.5)
["multiply"]=>
float(11)
["divide"]=>
float(2.75)
["integer_divide"]=>
float(2.5)
}
["reverse"]=>
array(4) {
["add"]=>
float(7.5)
["subtract"]=>
float(-3.5)
["multiply"]=>
float(11)
["divide"]=>
float(0.36363636363636365)
}
}
float(2.75)
float(15)
array(10) {
[0]=>
int(2)
[1]=>
float(4.5)
[2]=>
float(-3.5)
[3]=>
int(16)
[4]=>
int(0)
[5]=>
int(16)
[6]=>
int(1)
[7]=>
int(0)
[8]=>
int(6)
[9]=>
int(6)
}
array(2) {
["forward"]=>
array(4) {
[0]=>
float(2.75)
[1]=>
float(7.5)
[2]=>
float(3.5)
[3]=>
float(11)
}
["reverse"]=>
array(4) {
[0]=>
float(0.36363636363636365)
[1]=>
float(7.5)
[2]=>
float(-3.5)
[3]=>
float(11)
}
}
Loading…
Cancel
Save