From a6cd47b8dc77c30c87dc29f17efc488da493097a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 22 Sep 2026 11:49:04 +0800 Subject: [PATCH] fix(compiler): unbox runtime modulo results --- .../src/TypedScalarArithmeticCodegenTest.php | 2 +- src/Parser/BinaryOpTrait.php | 14 +++--- .../runtime-mod-int-property-assignment.phpt | 45 +++++++++++++++++++ 3 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 tests/compiler/object_property/runtime-mod-int-property-assignment.phpt diff --git a/phpunit/src/TypedScalarArithmeticCodegenTest.php b/phpunit/src/TypedScalarArithmeticCodegenTest.php index 4680e0d7..c1a57e46 100644 --- a/phpunit/src/TypedScalarArithmeticCodegenTest.php +++ b/phpunit/src/TypedScalarArithmeticCodegenTest.php @@ -23,7 +23,7 @@ final class TypedScalarArithmeticCodegenTest extends \BaseTest { $code = $this->compileFixture(); - self::assertStringContainsString('php::fn::mod(a, b)', $code); + self::assertStringContainsString('php::toInt(php::fn::mod(a, b))', $code); self::assertStringNotContainsString('((a) % (b))', $code); } diff --git a/src/Parser/BinaryOpTrait.php b/src/Parser/BinaryOpTrait.php index a87657fa..62c34430 100644 --- a/src/Parser/BinaryOpTrait.php +++ b/src/Parser/BinaryOpTrait.php @@ -143,17 +143,21 @@ trait BinaryOpTrait } if ($op === '%') { - if (!($leftType === Type::INT and $rightType === Type::INT)) { - return 'php::fn::mod(' . $leftExpr . ', ' . $rightExpr . ')'; - } + $requiresRuntimeModulo = $leftType !== Type::INT || $rightType !== Type::INT; // varint_types retains PHP's catchable modulo errors and // PHP_INT_MIN % -1 behavior. Native integers use raw C++ rules. - if ($this->varIntTypes + if (!$requiresRuntimeModulo + && $this->varIntTypes && !$this->isExplicitNativeArithmeticExpr($left) && !$this->isExplicitNativeArithmeticExpr($right) && $this->evaluateConstantIntArithmetic($left, $right, '%') === null ) { - return 'php::fn::mod(' . $leftExpr . ', ' . $rightExpr . ')'; + $requiresRuntimeModulo = true; + } + if ($requiresRuntimeModulo) { + return $this->convertIntExpr( + 'php::fn::mod(' . $leftExpr . ', ' . $rightExpr . ')', + ); } } diff --git a/tests/compiler/object_property/runtime-mod-int-property-assignment.phpt b/tests/compiler/object_property/runtime-mod-int-property-assignment.phpt new file mode 100644 index 00000000..9b8a6227 --- /dev/null +++ b/tests/compiler/object_property/runtime-mod-int-property-assignment.phpt @@ -0,0 +1,45 @@ +--TEST-- +Runtime modulo result can be assigned to an optimized int property +--FILE-- +items = $provider(); + } + + public function move(): void + { + $count = count($this->items); + $this->selected = $this->selected % $count; + } + + public function selected(): int + { + return $this->selected; + } +} + +} + +namespace { + +function main(): void +{ + $cursor = new RuntimeModuloNamespace\Cursor(); + $cursor->refresh(fn(): array => [1, 2]); + $cursor->move(); + var_dump($cursor->selected()); +} + +} +?> +--EXPECT-- +int(1)