Conversation
PHP converts a float operand of these operators to int and always yields an
int. TypePHP inferred float for them and emitted the raw C++ operator, which
does not exist for double, so ordinary PHP code failed in the C++ compiler:
function f(float $a, float $b): int { return $a & $b; }
// error: invalid operands of types php::Float and php::Float
// to binary operator&
The same happened for |, ^, << and >>, for an int and a float operand, and
for the compound forms on an int local ($x &= 3.0, $x %= 2.5). The type of
`$x = $float % $int` was also inferred as float (var_dump printed float(1)
where PHP prints int(1)).
- Type inference: with a float operand, %, <<, >>, &, | and ^ produce int.
- Codegen: a bitwise or shift operator with a float operand goes through the
php::Var operator, so Zend applies its own float-to-int conversion (the
deprecation of a lossy conversion, 0 for NAN/INF/out of range, and the
ArithmeticError of a negative shift). This happens before the Int -> Float
promotion, which would round a large int operand (PHP_INT_MAX ^ 1.0).
- An int local's compound %=, &=, |=, ^=, <<= or >>= with a float operand
takes the same route. On a native float local the result would have to
become an int, which a native local cannot do; that is now a compile-time
error instead of invalid C++.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #140
PHP converts a float operand of
%,<<,>>,&,|and^to int and always yields an int. TypePHP inferredfloatfor these expressions and emitted the raw C++ operator, which does not exist fordouble, so the generated C++ did not compile.The change
CompilerBase): with a float operand,%,<<,>>,&,|and^produceint.BinaryOpTrait): a bitwise or shift operator with a float operand goes through thephp::Varoperator, so Zend applies its own float-to-int conversion (deprecation of a lossy conversion,0forNAN/INF/out of range,ArithmeticErrorfor a negative shift). This is placed before the existing Int → Float promotion, which would otherwise round a large int operand (PHP_INT_MAX ^ 1.0must stay9223372036854775806).%already went throughphp::fn::mod.AssignOpTrait):%=,&=,|=,^=,<<=,>>=with a float operand on an int local take the same route. On a native float local the result would have to become an int, which a native local cannot do (the documented "reassigning a native local to an incompatible type" rule), so that is now a clear compile-time error —Cannot apply %= to a native float variable: PHP converts the result to int— instead of invalid C++.Verified on a binary
PHP 8.4.26 ZTS with embed, GCC 11, Linux x64. 20 cases compared against PHP (
6.0 & 3.0,-1.0 & 255.0,1.5 & 1.0,1e20 & 1.0,NAN & 1.0,INF & 1.0,2.0 | PHP_INT_MAX,PHP_INT_MAX ^ 1.0,PHP_INT_MIN ^ 0.0,1.0 << 3.0,1.0 << 64.0,1.0 << -1.0,-8.0 >> 1,7.5 % 2,7.5 % 0,$x &= 3.0,$x %= 2.5,$x <<= 1.0, and literals):tests/compiler/operator/float-bitwise-operands.phptinvalid operands ... operator&)FloatLocalIntOnlyAssignOpTesttests/compiler(full)Found with a differential fuzzer; the same session found #137.