Repository navigation
fix(compiler): make numeric string arithmetic match PHP semantics - #136
alwaysLinger wants to merge 1 commit into
Conversation
…heir arithmetic matches PHP semantics across all modes
结论:不建议合入当前方案评审基于 head TypePHP 中,字符串字面量始终是 1. 字符串参与数值运算需要显式转换
function main(): void {
$s = "1e2"; // string
$f = (float) $s; // float
var_dump($f / 3);
}无法满足静态操作数类型要求的表达式,应由 TypePHP 编译器报告类型错误,不能依赖字符串内容决定是否接受,更不能生成非法 C++。 字符串自身支持的操作仍保持字符串语义,例如两侧均为字符串的 2. 高精度类型接受字符串构造参数是明确的转换接口
显式构造之后,可以在保持构造函数语义的前提下进行常量优化。调整字符串类型规则时,应保留这些高精度类型的字符串构造能力。 3. 本 PR 需要调整的实现
4. 已确认的代码生成问题以下是当前实现的行为证据,不表示这些未显式转换的数值运算应被 TypePHP 接受。 整数边界: src/CompilerBase.php:2434–2435 直接生成 位运算和移位: ((1e+0) & (1L))
((2e+0) << (1L))src/CompilerBase.php:2431–2432 将数字字符串统一输出为 double 字面量,导致 C++ 编译错误; 验证范围
|
|
已重构。str 与 int/float 运算时,会提升为 var 类型,当前行为已与 php 完全一致。 |
问题
同一算术表达式,数字串字面量形态与变量形态行为不一致——变量形态正确,字符串形态偏离:
PHP 手册规定:纯十进制整数且在 int64 范围内的数字串("5"、"10")按 int 参与算术,其余数字串("1e2"、"1.5")按 float。此偏离不在官方不兼容清单内。
根因
三条路径都未把数字串字面量当作数值操作数:
src/CompilerBase.phpdetectTypeOfExpr()算术分支,一侧为原生 Int 时无条件返回Type::INT——赋值目标被声明为php::Int槽位,double 值写入即截断。src/CompilerBase.phpparseNumericIdentifier()用(string) floatval("1e2")转换数字串,得到"100"(无小数点),发出的 C++ 是整型字面量。src/Parser/BinaryOpTrait.phpconstantNumericValue()不处理Node\Scalar\String_,字符串派生的常量绕过溢出检查与折叠。修复
isIntNumericString()/isFloatNumericString()(src/Parser/TypeDetectionTrait.php):is_numeric+ 纯整数语法正则 + int64 范围检查,与 PHP 数字串分类语义一致。detectTypeOfExpr():一侧为浮点数字串时,+ - * / **结果提升为Type::FLOAT(%/移位/位运算排除);varint 模式下整型语法数字串视同 int 操作数参与常量求值。parseNumericIdentifier():浮点数字串经仓库现有shortestFloatRepr()发射,保留 C++ double 形态(1e+2);溢出为 INF 时退回运行时字符串路径。constantNumericValue():读取数字串字面量(整型语法 → intval,其余有限数字串 → floatval)。现有常量溢出防护因此覆盖字符串常量:native 模式下"9223372036854775807" * 2与 int 字面量写法一致地被编译期拒绝;varint 模式下折叠为 PHP float。行为变化
"9223372036854775807" * 2从“编译通过、运行时未定义行为”变为编译期拒绝——与 int 字面量写法一致。"5" / 2从int(2)修正为float(2.5)(官方文档 varint_types 职责:“溢出转浮点与小数除法”)。"9223372036854775807" * 2从运行时崩溃(ValueError: Invalid BigInt numeric string)修复为精确 BigInt 运算。保持不变
"10" / 2 → int(5)、"5" + 1 → int(6)——逐字节与修复前一致。$f = "1e2"; $f / 3)原本正确,未触碰。%/移位/位运算不涉及。验证
Linux x86-64 Docker,PHP 8.4.26 NTS,GCC:
php基准逐字节对拍(含大写E、负指数、.5/5.、首尾空白、INF、int64 边界、幂/比较/移位/位运算/复合赋值),native 与 varint 双模式,-O0/-O2/-O3一致;use bigint_types独立对拍:修复前运行时崩溃、修复后精确 BigInt。tests/compiler/{operator,basic,std-array,class}188 项无新增失败(唯一失败pow-int-overflow在未修改基线上同样失败,为容器precision=14环境差异);全量 phpunit 2409 项仅 2 例失败(容器缺 python3,基线相同)。