Complete varint types migration and fixed-storage safety

master
韩天峰 1 month ago
parent b92f782606
commit 2166e1a1a1
  1. 59
      CHANGELOG.md
  2. 20
      README-CN.md
  3. 24
      README.md
  4. 1
      benchmark/bridge/benchmark.php
  5. 1
      benchmark/bridge/run.php
  6. 1
      benchmark/dynamic-call/benchmark.php
  7. 1
      benchmark/dynamic-call/run.php
  8. 1
      benchmark/micro_bench.php
  9. 1
      benchmark/property-access/benchmark.php
  10. 1
      benchmark/property-access/run.php
  11. 1
      benchmark/static-cache/benchmark.php
  12. 1
      benchmark/static-cache/run.php
  13. 1
      bin/analyze-test-coverage.php
  14. 1
      bin/clean-phpt-artifacts.php
  15. 1
      bin/run-integration-tests.php
  16. 29
      docs/en/HIGH_PRECISION_TYPES.md
  17. 11
      docs/en/INCOMPATIBLE_PHP_FEATURES.md
  18. 40
      docs/en/NATIVE_TYPES.md
  19. 2
      docs/en/PHP_INCOMPATIBILITY_CLASSIFICATION.md
  20. 2
      docs/en/STD_CONTAINERS.md
  21. 10
      docs/en/UNIVERSAL_METHODS.md
  22. 27
      docs/zh-cn/HIGH_PRECISION_TYPES.md
  23. 8
      docs/zh-cn/INCOMPATIBLE_PHP_FEATURES.md
  24. 36
      docs/zh-cn/NATIVE_TYPES.md
  25. 2
      docs/zh-cn/STD_CONTAINERS.md
  26. 10
      docs/zh-cn/UNIVERSAL_METHODS.md
  27. 1
      examples/debug/prop.php
  28. 1
      examples/high-precision.php
  29. 1
      examples/lib-demo/php-src/logic.php
  30. 1
      examples/minecraft-demo/main.php
  31. 1
      examples/minecraft-godot/php-src/world.php
  32. 1
      examples/ocean-demo/main.php
  33. 1
      examples/ocean-godot/php-src/ocean.php
  34. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/Ai.php
  35. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/Card.php
  36. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/Character.php
  37. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/Combo.php
  38. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/Deck.php
  39. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/Faction.php
  40. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/Game.php
  41. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/GameController.php
  42. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/MoveGenerator.php
  43. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/PlayerState.php
  44. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/Render.php
  45. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/Skill.php
  46. 1
      examples/onepiece-doudizhu-win32/php-src/doudizhu/Sound.php
  47. 1
      examples/void.php
  48. 1
      examples/wasm-hello/src/WasiDemo.php
  49. 1
      examples/wasm-hello/src/main.php
  50. 1
      phpunit/code/bigfloat-unsupported-mod.php
  51. 1
      phpunit/code/bigfloat-unsupported-shift.php
  52. 1
      phpunit/code/bigint-post-inc.php
  53. 1
      phpunit/code/bigint-pre-inc.php
  54. 4
      phpunit/code/closure/use-reference-capture.php
  55. 1
      phpunit/code/constant-overflow-native-add.php
  56. 1
      phpunit/code/constant-overflow-native-div-subtree.php
  57. 1
      phpunit/code/constant-overflow-native-div.php
  58. 1
      phpunit/code/constant-overflow-native-mod.php
  59. 1
      phpunit/code/constant-overflow-native-mul.php
  60. 1
      phpunit/code/constant-overflow-native-neg.php
  61. 1
      phpunit/code/constant-overflow-native-nested-add.php
  62. 1
      phpunit/code/constant-overflow-native-nested-div.php
  63. 1
      phpunit/code/constant-overflow-native-nested-mod.php
  64. 1
      phpunit/code/constant-overflow-native-nested-zero.php
  65. 1
      phpunit/code/constant-overflow-native-nonconstant.php
  66. 1
      phpunit/code/constant-overflow-native-ok.php
  67. 1
      phpunit/code/constant-overflow-native-sub.php
  68. 1
      phpunit/code/constant-overflow-warning.php
  69. 2
      phpunit/code/count-literal-fold-unsafe.php
  70. 1
      phpunit/code/decimal-pre-dec.php
  71. 1
      phpunit/code/decimal-unsupported-bitand.php
  72. 1
      phpunit/code/decimal-unsupported-bitor.php
  73. 9
      phpunit/code/fixed-reference-argument.php
  74. 8
      phpunit/code/fixed-reference-array.php
  75. 7
      phpunit/code/fixed-reference-assignment.php
  76. 11
      phpunit/code/fixed-reference-explicit-any.php
  77. 13
      phpunit/code/fixed-reference-generic-object.php
  78. 10
      phpunit/code/fixed-reference-object.php
  79. 7
      phpunit/code/fixed-reference-return.php
  80. 8
      phpunit/code/fixed-reference-static.php
  81. 8
      phpunit/code/fixed-reference-std-container.php
  82. 8
      phpunit/code/fixed-reference-stream.php
  83. 8
      phpunit/code/fixed-reference-string.php
  84. 9
      phpunit/code/fixed-reference-to-ref.php
  85. 3
      phpunit/code/override_byref_return_added.php
  86. 3
      phpunit/code/override_byref_return_dropped.php
  87. 1
      phpunit/code/preprocessor/namespace_ending_comment_unbracketed.php
  88. 1
      phpunit/code/re-assign-obj-to-str.php
  89. 1
      phpunit/code/shift-boundary-native-neg-left.php
  90. 1
      phpunit/code/shift-boundary-native-neg-right.php
  91. 1
      phpunit/code/shift-boundary-native-negative.php
  92. 1
      phpunit/code/shift-boundary-native-nested-neg-right.php
  93. 1
      phpunit/code/shift-boundary-native-nested-overflow.php
  94. 1
      phpunit/code/shift-boundary-native-ok.php
  95. 1
      phpunit/code/shift-boundary-native-overflow.php
  96. 1
      phpunit/code/shift-boundary-native-sign-bit.php
  97. 1
      phpunit/code/shift-boundary-native-wrapped-overflow.php
  98. 1
      phpunit/code/shift-boundary-warning.php
  99. 1
      phpunit/code/trait-aliased-constructor-parent-call.php
  100. 1
      phpunit/code/trait_constructor_conflict.php
  101. Some files were not shown because too many files have changed in this diff Show More

@ -2,6 +2,39 @@
## 0.8.0
### Breaking: native scalar storage is now the default
`use native_types` has been removed. Inferred `int`, `float`, and `bool`
locals now use `php::Int`, `php::Float`, and `php::Bool` by default. Their
storage type is fixed and the compiler never promotes one of these locals to
`php::Var` merely because a later operation needs dynamic PHP semantics.
Two explicit escape hatches remain:
- `use varint_types` is a file-level mode that stores inferred integer values
in `php::Var`, preserving PHP's overflow-to-float, non-integral division,
and other Zend integer arithmetic behavior. It does not box `float` or
`bool` locals.
- `std::any($value)` erases the static type of that individual expression, so
a variable initialized from it uses `php::Var` and may participate in PHP
reference or dynamic-value operations.
For example, `$i = 100` is now permanently an integer local. Reusing `$i` as a
`foreach` key is rejected because PHP keys have type `int|string`; write
`$i = std::any(100)` or select `use varint_types` if this reuse is intentional.
Projects must remove `use native_types` and add the new compatibility mode only
to files that genuinely depend on Zend integer widening semantics.
Fixed-storage locals can no longer be converted to PHP references. This rule
applies to native scalars, strings, arrays, objects, streams, high-precision
values, and `std` containers. Zend references are untyped, so allowing one to
alias fixed C++ storage could corrupt the variable's type. Use `std::any()`
before reference operations and explicitly convert the result back afterward.
TypePHP is always strict. Project sources no longer need
`declare(strict_types=1)`; the directive remains accepted as a redundant PHP
compatibility declaration, while `strict_types=0` is rejected.
### Breaking: compile-time API namespace cleanup
TypePHP compile-time APIs now occupy only two global class symbols:
@ -45,6 +78,32 @@ should review the change log and run their full test suite before upgrading.
## 0.8.0(中文)
### 破坏性变更:默认使用原生标量存储
`use native_types` 已移除。推断出的 `int`、`float`、`bool` 局部变量现在默认分别
使用 `php::Int`、`php::Float`、`php::Bool`。这些变量的存储类型一旦确定便不会因为
后续操作需要 PHP 动态语义而被编译器自动提升为 `php::Var`。
只保留两个显式出口:
- `use varint_types` 是文件级模式,使推断出的整数使用 `php::Var`,保留 PHP 的
整数溢出转浮点、整数除法产生非整数结果等 Zend 算术语义;它不会装箱 `float`
或 `bool`。
- `std::any($value)` 只擦除该表达式的静态类型。以它初始化的变量使用 `php::Var`,
可参与引用或其他动态值操作。
例如 `$i = 100` 现在固定为整数局部变量。由于 PHP 的 foreach key 类型为
`int|string`,之后复用 `$i` 作为 key 会在编译期报错;确需复用时,应写成
`$i = std::any(100)` 或在文件中声明 `use varint_types`。升级项目必须删除
`use native_types`,并且只为真正依赖 Zend 整数扩展语义的文件添加新兼容模式。
固定存储的局部变量不再允许转换为 PHP 引用,包括原生标量、字符串、数组、对象、
stream、高精度值和 `std` 容器。Zend 引用没有类型约束,若允许其指向固定 C++ 存储,
可能破坏变量类型。需要引用操作时先使用 `std::any()`,操作完成后再显式转换回目标类型。
TypePHP 始终使用严格类型,项目源码不再需要 `declare(strict_types=1)`。该声明仍作为
冗余的 PHP 兼容语法被接受,而 `strict_types=0` 会被拒绝。
### 破坏性变更:整理编译期 API 命名空间
TypePHP 编译期 API 现在只占用两个全局类符号:

@ -109,8 +109,11 @@ AST,待全部项目符号就绪后再在 convert 阶段解析。这一两阶
- **原生进程入口。** 二进制模式直接启动原生可执行文件,不需要 PHP CLI 或独立的
解释器进程。可执行文件仍会嵌入或链接 PHPX、`libphp` 及项目配置的原生库,部署包
中必须提供这些运行时依赖。
- **渐进式类型,真正带来收益。** 只在性能关键处添加 `use native_types`、`std::`
容器和类型声明,其余保持普通 PHP。
- **默认使用强标量类型。** 推断出的 `int`、`float`、`bool` 局部变量直接使用
C++ 原生存储。单个动态值使用 `std::any()`;只有文件确实依赖 PHP 整数扩展语义时,
才使用 `use varint_types`。
- **始终严格调用。** TypePHP 不启用 PHP 的弱标量类型转换,无需声明
`declare(strict_types=1)`。
- **Zend 生态互通。** 扩展模式以标准 PHP 扩展形式加载,项目可以调用受支持的
内置函数,并显式声明依赖的其他 Zend 扩展。
@ -299,7 +302,9 @@ TypePHP 会在适合 AOT 编译的范围内保持 PHP 语法和运行行为,
- 全局作用域只允许声明,可执行语句必须位于函数或方法内;
- 二进制模式对 `main()` 使用严格签名;
- `use native_types` 会让标量声明使用固定原生存储,之后不能改为不兼容类型;
- 推断出的 `int`、`float`、`bool` 默认使用固定原生存储,之后不能改为不兼容类型;
- `use varint_types` 使推断出的整数存入 `php::Var`,保留 PHP 的整数溢出和除法语义;
`std::any()` 则只擦除单个表达式的静态类型;
- 静态可确定的调用和属性会直接编译,受支持的动态操作则通过 PHPX/Zend runtime
fallback 执行;
- `.stub.php` 用于声明 C++ 或外部库 API,函数体必须为空,stub 文件禁止声明
@ -367,7 +372,6 @@ function main(): void
```php
<?php
use native_types;
function fib(int $n): int
{
@ -391,15 +395,14 @@ bin/tpc.php fib.php -O3 -o fib
./fib 30
```
使用 `use native_types` 后,`int` 变量变为 C++ `int64_t`,算术运算直接编译为
CPU 指令,而不是 ZendVM 调用。
默认情况下,推断和声明的 `int` 变量都会变为 C++ `int64_t`,算术运算直接编译为
CPU 指令,而不是 ZendVM 调用。仅当本文件需要 PHP 的整数溢出转浮点、整数除法产生
非整数结果等语义时,才添加 `use varint_types`。
### 2. 高精度数值
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void
{
@ -425,7 +428,6 @@ function main(): void
```php
<?php
use native_types;
function main(): void
{

@ -127,9 +127,11 @@ This two-phase design keeps multi-file and self-hosted builds deterministic.
executable and does not require the PHP CLI or a separate interpreter
process. The executable still embeds/links PHPX, `libphp`, and any configured
native libraries, which must be available in the deployment package.
- **Gradual typing that actually pays off.** Add `use native_types`, `std::`
containers, and type declarations only where performance matters; the rest
stays ordinary PHP.
- **Strong scalar types by default.** Inferred `int`, `float`, and `bool`
locals use native C++ storage. Use `std::any()` for an individual dynamic
value, or `use varint_types` when a file requires PHP integer widening.
- **Always-strict calls.** TypePHP never enables PHP's weak scalar coercion;
`declare(strict_types=1)` is unnecessary.
- **Zend ecosystem interop.** Extension mode loads as a standard PHP extension,
and projects can call supported internal functions and require other Zend
extensions explicitly.
@ -331,8 +333,10 @@ ahead-of-time compilation, but it also makes several deliberate restrictions:
- global scope is declaration-only; executable statements must be inside a
function or method;
- binary mode has a strict `main()` signature;
- `use native_types` opts scalar declarations into fixed native storage, so a
value cannot later change to an incompatible type;
- inferred `int`, `float`, and `bool` values use fixed native storage by
default and cannot later change to an incompatible type;
- `use varint_types` stores inferred integers in `php::Var` for PHP-compatible
overflow and division behavior; `std::any()` erases one expression's type;
- statically-known calls and properties are compiled directly, while supported
dynamic operations use PHPX/Zend runtime fallbacks;
- `.stub.php` files declare C++ or imported-library APIs and must contain empty
@ -405,7 +409,6 @@ inherited final method is a compile-time error.
```php
<?php
use native_types;
function fib(int $n): int
{
@ -429,15 +432,15 @@ bin/tpc.php fib.php -O3 -o fib
./fib 30
```
With `use native_types`, `int` variables become C++ `int64_t` and arithmetic
compiles to plain CPU instructions instead of ZendVM calls.
By default, inferred and declared `int` variables become C++ `int64_t`, and
arithmetic compiles to plain CPU instructions instead of ZendVM calls. Add
`use varint_types` only when a file requires PHP's overflow-to-float and
non-integral integer-division behavior.
### 2. High-precision numerics
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void
{
@ -464,7 +467,6 @@ See [High-precision types](docs/en/HIGH_PRECISION_TYPES.md) and
```php
<?php
use native_types;
function main(): void
{

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
const BRIDGE_ITERATIONS = 10_000_000;
const BRIDGE_CONTAINER_ITERATIONS = 1_000_000;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
$root = dirname(__DIR__, 2);
$source = __DIR__ . '/benchmark.php';

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
const DYNAMIC_CALL_ITERATIONS = 1_000_000;
const DYNAMIC_CALL_ROUNDS = 5;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
$root = dirname(__DIR__, 2);
$source = __DIR__ . '/benchmark.php';

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function hallo() {
}

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
final class DynamicPropertyEntity
{

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
$root = dirname(__DIR__, 2);
$source = __DIR__ . '/benchmark.php';

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
const STATIC_CACHE_ITERATIONS = 1_000_000;
const STATIC_CACHE_WARMUPS = 2;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
$root = dirname(__DIR__, 2);
$source = __DIR__ . '/benchmark.php';

@ -7,7 +7,6 @@
* @contact service@swoole.com
*/
declare(strict_types=1);
use TypePhp\Testing\TestCoverageAnalyzer;

@ -1,7 +1,6 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
const DEFAULT_TEST_DIR = 'tests';

@ -1,7 +1,6 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
namespace TypePhp\IntegrationTest;

@ -48,14 +48,11 @@ The AOT compiler provides three high-precision types, built on mature C/C++ math
Prerequisites for using high-precision types:
1. Declare `declare(strict_types=1)` at the top of the file
2. Import the native type declaration `use native_types`
3. The system must have the corresponding C++ libraries installed (`libgmp-dev`, `libmpdec-dev`, `libmpfr-dev`)
- The system must have the corresponding C++ libraries installed (`libgmp-dev`, `libmpdec-dev`, `libmpfr-dev`).
- TypePHP always uses strict typing; no `declare(strict_types=1)` directive is required.
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// Your high-precision computation code
@ -74,7 +71,9 @@ php bin/tpc.php my_program.php -o my_program
./my_program
```
> **Tip**: Like all native_types, the Big* types can only be used in AOT compile mode and cannot run in the normal PHP interpreter. The AOT compiler performs compile-time evaluation of functions such as `std::bigInt()` and directly generates C++ code.
> **Tip**: Big* types can only be used in AOT compile mode and cannot run in
> the normal PHP interpreter. The compiler recognizes functions such as
> `std::bigInt()` and directly generates C++ code.
---
@ -133,11 +132,9 @@ $g = std::bigFloat("3.14159265358979323846"); // from string (exact)
### 4.2 Type Annotation
Under `use native_types`, Big* type variables automatically get native C++ storage types:
Big* constructors always produce their dedicated boxed C++ storage type:
```php
use native_types;
// The compiler automatically infers the type as php::BigInt / php::Decimal / php::BigFloat
$a = std::bigInt(100); // → C++: php::Variant(new BigInt(100))
$b = std::decimal("100.50"); // → C++: php::Variant(new Decimal("100.50"))
@ -586,9 +583,11 @@ This restriction also applies to comparison operations. Before comparing, both s
Big* types are a proprietary feature of the AOT compiler, relying on compile-time code generation and C++ underlying libraries. The source code cannot be directly interpreted and executed by the `php` command.
### 12.8 Enabling `use native_types`
### 12.8 No file-level opt-in required
Forgetting to add `use native_types` causes Big* variables to be treated as Var (generic type), losing most of the performance advantages of native types.
Big* constructors determine their result type directly. No file-level native
type declaration is required. `use varint_types` affects only inferred ordinary
integers and does not change BigInt, Decimal, or BigFloat storage.
---
@ -598,8 +597,6 @@ Forgetting to add `use native_types` causes Big* variables to be treated as Var
```php
<?php
declare(strict_types=1);
use native_types;
/**
* Compute the factorial of n, supporting arbitrarily large results
@ -625,8 +622,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// use Decimal to represent amounts exactly
@ -661,8 +656,6 @@ total: 64.7676
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// use BigFloat for high-precision math computation
@ -690,8 +683,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// BigInt — large integer operations

@ -77,7 +77,7 @@ incompatible with or more restrictive than standard PHP.
- `declare(ticks=...)` is not supported.
- `declare(encoding=...)` accepts only `UTF-8`.
- `declare(strict_types=...)` accepts only `strict_types=1`.
- TypePHP always uses strict typing. `declare(strict_types=1)` is accepted but redundant; `strict_types=0` is rejected.
- No other `declare` directives are supported.
## Calls and references
@ -103,6 +103,15 @@ incompatible with or more restrictive than standard PHP.
used explicitly.
- `std::ref()` / `toRef()` only accept variables, array elements, or object
properties.
- A local with fixed storage (`Int`, `Float`, `Bool`, `Str`, `Array`, `Object`,
`Stream`, high-precision values, or a `std` container) cannot be made into a
PHP reference. Zend references are untyped and could replace such storage
with an incompatible value. Objects, streams, typed objects, and `std`
containers already use handle/reference-like value semantics, so adding a
PHP reference to the local variable is unnecessary as well. Initialize the
value with `std::any()` when PHP reference semantics are required, then
convert it back explicitly with a keyword such as `toArray()` or
`toString()`.
- A call that uses argument unpacking followed by named arguments falls back to
dynamic dispatch and cannot use the native call path.

@ -4,6 +4,15 @@
**The AOT compiler supports 6 native/high-precision types**:
Since TypePHP 0.8, inferred `int`, `float`, and `bool` locals use native
`php::Int`, `php::Float`, and `php::Bool` storage by default. The old
`use native_types` directive has been removed. Native locals are never
silently promoted to `php::Var` later.
Use `use varint_types` only for a file that needs Zend PHP integer widening
semantics. It boxes inferred integers, but not floats or booleans. Use
`std::any($value)` when only one value must be dynamic or reference-capable.
### Basic Native Types
1. ✅ `std::int` - Native integer type (zend_long, 8 bytes)
2. ✅ `std::float` - Native floating-point type (double, 8 bytes)
@ -24,7 +33,6 @@ Pay particular attention to `unset($obj->prop)` and assigning `null` on fixed-va
```php
<?php
use native_types;
class User {
public int $id = 0;
@ -263,8 +271,6 @@ BigInt, Decimal, and BigFloat all inherit from `php::Box` and are stored inside
### Declaration and Construction
```php
use native_types;
// Construct a BigInt from an integer literal
$a = std::bigInt(100);
$b = std::bigInt("123456789012345678901234567890"); // Very long integer string
@ -496,11 +502,15 @@ Both sides are Int
### Rule 1: Var Dominates
When at least one side of the operands is of type `Var` (not declared with `use native_types`), both sides are treated as `Var`, using ZendVM's `add_function` / `div_function` and other operation functions, fully following PHP's native type conversion (type juggling) semantics.
When at least one operand is `Var`, both sides use the PHPX/Zend arithmetic
path and follow PHP type-juggling semantics. A `Var` comes from an expression
such as `std::any(...)`, a dynamic runtime value, or an inferred integer in a
file that declares `use varint_types`.
```php
use varint_types;
$a = 10; // Var, stores int(10)
$b = 2.5; // Var, stores float(2.5)
$b = 2.5; // php::Float (varint_types affects only inferred integers)
$c = $a + $b; // Both sides are Var → ZendVM operation → float(12.5)
```
@ -508,10 +518,11 @@ C++ code generation: `int64_t` and `double` values are implicitly converted to `
### Rule 2: Float Takes Precedence over Int
When both sides are native types (declared via `use native_types` or `std::int()`/`std::float()`), if either side is Float, both sides are converted to Float for the operation. Only when both sides are Int is integer arithmetic used.
When both sides are native types (the default, or explicitly constructed with
`std::int()` / `std::float()` inside a varint file), Float takes precedence.
Only two Int operands use integer arithmetic.
```php
use native_types;
$a = 10; // php::Int
$b = 2.5; // php::Float
$c = $a + $b; // Float + Float → double addition
@ -521,7 +532,10 @@ $e = 3; // php::Int
$f = $d + $e; // Int + Int → int64_t addition
```
> **Note**: native-type variables **do not change their own type** during operations. For example, `Int += Float` executes `int64_t += double` in C++, and the result is truncated to int64_t, which differs from PHP behavior (in PHP the variable becomes float). This is intentional semantics of `use native_types`.
> **Note**: native variables **do not change storage type** during operations.
> For example, `Int += Float` remains Int. This fixed-type behavior is now the
> default; choose `use varint_types` or `std::any()` when dynamic integer
> widening is required.
### Rule 3: Safe Promotion of High-Precision Types
@ -560,15 +574,15 @@ When the operands include `BigInt`, `Decimal`, or `BigFloat`, only explicit and
Compound assignment operators such as `+=`, `-=`, `*=`, `/=`, `%=` follow the same type promotion rules, but the RHS is converted to the type of the LHS variable. If the LHS is Var, the RHS keeps its original type (Var's `operator+=` takes over); if the LHS is a native type, the RHS is explicitly converted to that type.
```php
$a = 10; // Var
use varint_types;
$a = 10; // Var because this file selected varint_types
$a += 2.5; // Var::operator+=(float) → ZendVM → $a becomes float(12.5)
use native_types;
$b = 10; // php::Int
$b = std::int(10); // Explicit php::Int inside a varint_types file
$b += 2.5; // int64_t += double → C++ implicit truncation → $b = 12 (Int)
```
---
**Last updated**: May 26, 2026
**Applicable version**: PHP AOT Compiler v1.x
**Last updated**: September 6, 2026
**Applicable version**: TypePHP 0.8+

@ -66,7 +66,7 @@ These items should be documented with the exact boundary.
| Binary mode requires global `main()` | Intentional Rule | This defines the binary entry ABI. |
| `main()` only accepts no parameters or `(int $argc, array $argv)` | Intentional Rule | Keeps the entry ABI explicit and stable. |
| `main()` must return `void` | Intentional Rule | An integer exit-code convention could be added later, but current TypePHP rules reject return values. |
| `declare(strict_types=...)` only supports `strict_types=1` | Intentional Rule | Supporting mixed strict/weak typing is possible, but TypePHP keeps strict behavior predictable. |
| TypePHP is always strict; `strict_types=0` is rejected | Intentional Rule | Per-file weak typing conflicts with TypePHP's fixed storage and static type guarantees. `strict_types=1` remains accepted as a redundant compatibility directive. |
| Default parameter before required parameter | Intentional Rule | PHP allows this legacy pattern but ignores the default. TypePHP rejects it to avoid misleading declarations. |
| Child class overriding parent private property | Intentional Rule / Pending if dynamicized | PHP stores private properties by declaring class. TypePHP native/fixed layouts make this expensive. Rejecting it keeps property layout predictable. |
| `__construct()` return value | Intentional Rule | PHP constructors should not return values. TypePHP rejects this explicitly. |

@ -511,8 +511,6 @@ sec: 67.638107061386108
### std::array
Test code:
```php
use native_types;
function main(int $argc, array $argv): void
{
$u = (int)$argv[2];

@ -917,8 +917,6 @@ Format: `{type_prefix}_{snake_case_method_name}`
```php
<?php
declare(strict_types=1);
use native_types;
/**
* Extension method: determine whether an Int is prime
@ -974,8 +972,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
$raw = " <h1>Hello World!</h1> \n";
@ -998,8 +994,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
$data = [5, 2, 8, 1, 9, 3, 7];
@ -1034,8 +1028,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// Factorial of a large integer (using compound assignment for brevity)
@ -1072,8 +1064,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// Write to the file

@ -48,14 +48,11 @@ AOT 编译器提供了三种高精度类型,底层基于成熟的 C/C++ 数学
使用高精度类型的前提条件:
1. 文件头部声明 `declare(strict_types=1)`
2. 导入原生类型声明 `use native_types`
3. 系统已安装对应的 C++ 库(`libgmp-dev`、`libmpdec-dev`、`libmpfr-dev`)
- 系统已安装对应的 C++ 库(`libgmp-dev`、`libmpdec-dev`、`libmpfr-dev`)。
- TypePHP 始终使用严格类型,无需声明 `declare(strict_types=1)`。
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// 你的高精度计算代码
@ -74,7 +71,8 @@ php bin/tpc.php my_program.php -o my_program
./my_program
```
> **提示**:和所有 native_types 一样,Big* 类型只能在 AOT 编译模式下使用,不能在普通 PHP 解释器中运行。AOT 编译器会对 `std::bigInt()` 等函数进行编译期求值,直接生成 C++ 代码。
> **提示**:Big* 类型只能在 AOT 编译模式下使用,不能在普通 PHP 解释器中运行。
> 编译器会识别 `std::bigInt()` 等函数并直接生成 C++ 代码。
---
@ -133,11 +131,9 @@ $g = std::bigFloat("3.14159265358979323846"); // 从字符串(精
### 4.2 类型标注
在 `use native_types` 下,Big* 类型变量自动获得原生 C++ 存储类型:
Big* 构造函数始终产生对应的专用 C++ 装箱存储类型:
```php
use native_types;
// 编译器自动推断类型为 php::BigInt / php::Decimal / php::BigFloat
$a = std::bigInt(100); // → C++: php::Variant(new BigInt(100))
$b = std::decimal("100.50"); // → C++: php::Variant(new Decimal("100.50"))
@ -586,9 +582,10 @@ $c = $a + std::bigFloat($b->toString()); // ✅
Big* 类型是 AOT 编译器的专有特性,依赖编译期代码生成和 C++ 底层库。源码不能被 `php` 命令直接解释执行。
### 12.8 启用 `use native_types`
### 12.8 无需文件级开关
忘记添加 `use native_types` 会导致 Big* 变量被当作 Var(通用类型),失去原生类型的大部分性能优势。
Big* 构造函数会直接确定结果类型,不需要文件级原生类型声明。`use varint_types`
只影响推断出的普通整数,不改变 BigInt、Decimal 或 BigFloat 的存储。
---
@ -598,8 +595,6 @@ Big* 类型是 AOT 编译器的专有特性,依赖编译期代码生成和 C++
```php
<?php
declare(strict_types=1);
use native_types;
/**
* 计算 n 的阶乘,支持任意大的结果
@ -625,8 +620,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// 使用 Decimal 精确表示金额
@ -661,8 +654,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// 使用 BigFloat 进行高精度数学运算
@ -690,8 +681,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// BigInt — 大整数运算

@ -36,7 +36,7 @@
- 不支持 `declare(ticks=...)`。
- `declare(encoding=...)` 只允许 `UTF-8`。
- `declare(strict_types=...)` 只允许 `strict_types=1`。
- TypePHP 始终使用严格类型。`declare(strict_types=1)` 可兼容接受但没有作用;`strict_types=0` 会被拒绝。
- 不支持其他 `declare` 指令。
## 调用与引用
@ -48,6 +48,12 @@
- 引用赋值不支持从复杂静态属性表达式建立引用。
- 动态调用、闭包调用等编译期无法确定参数签名的调用,不能自动转换引用参数;需要显式使用 `std::ref()` 或等价关键词方法 `toRef()`。
- `std::ref()` / `toRef()` 只接受变量、数组元素或对象属性。
- 采用固定存储的局部变量(`Int`、`Float`、`Bool`、`Str`、`Array`、`Object`、
`Stream`、高精度值或 `std` 容器)不能转换为 PHP 引用。Zend 引用没有类型约束,
可能把固定存储替换成不兼容的值。对象、stream、typed object 与 `std` 容器本身
已采用句柄或引用式值语义,再对局部变量建立 PHP 引用也没有意义。确需 PHP 引用
语义时,应使用 `std::any()` 初始化,完成引用操作后再通过 `toArray()`、
`toString()` 等关键词显式转换回来。
- 带 unpack 且尾部追加 named arguments 的调用会退化为动态调用,不能使用 native call。
## 对象模型

@ -4,6 +4,14 @@
**AOT 编译器支持 6 种原生/高精度类型**:
从 TypePHP 0.8 起,推断出的 `int`、`float`、`bool` 局部变量默认分别使用
`php::Int`、`php::Float`、`php::Bool` 原生存储。原有 `use native_types` 已移除,
编译器也绝不会在后续流程中把原生局部变量静默提升为 `php::Var`。
只有文件确实需要 Zend PHP 的整数扩展语义时才使用 `use varint_types`;它只装箱
推断出的整数,不影响 float 和 bool。只有单个值需要动态或引用语义时,使用
`std::any($value)`。
### 基础原生类型
1. ✅ `std::int` - 原生整数类型 (zend_long, 8 字节)
2. ✅ `std::float` - 原生浮点类型 (double, 8 字节)
@ -24,7 +32,6 @@ AOT 编译器要求对象属性在整个生命周期内始终保持声明时的
```php
<?php
use native_types;
class User {
public int $id = 0;
@ -263,8 +270,6 @@ BigInt、Decimal、BigFloat 均继承自 `php::Box`,存储于 `php::Variant`
### 声明与构造
```php
use native_types;
// 从整数字面量构造 BigInt
$a = std::bigInt(100);
$b = std::bigInt("123456789012345678901234567890"); // 超长整数字符串
@ -496,11 +501,14 @@ BigFloat / Decimal / BigInt 参与
### 规则一:Var 主导
当运算数中至少有一边是 `Var` 类型(非 `use native_types` 声明),两边均作为 `Var` 处理,使用 ZendVM 的 `add_function` / `div_function` 等运算函数,完全遵循 PHP 原生类型转换(type juggling)语义。
当至少一个运算数为 `Var` 时,两边都进入 PHPX/Zend 算术路径,遵循 PHP 的类型
转换语义。`Var` 可以来自 `std::any(...)`、动态运行时值,或声明了
`use varint_types` 的文件中推断出的整数。
```php
use varint_types;
$a = 10; // Var,存 int(10)
$b = 2.5; // Var,存 float(2.5)
$b = 2.5; // php::Float(varint_types 只影响推断整数)
$c = $a + $b; // 两边为 Var → ZendVM 运算 → float(12.5)
```
@ -508,10 +516,10 @@ C++ 代码生成:`int64_t` 和 `double` 值通过 `php::Variant` 的模板构
### 规则二:Float 优先于 Int
当两边均为原生类型(通过 `use native_types` 或 `std::int()`/`std::float()` 声明),如果任一边是 Float,则两边均转为 Float 运算。仅当两边都是 Int 才使用整数运算。
当两边均为原生类型(默认行为,或在 varint 文件中显式使用 `std::int()` /
`std::float()`)时,Float 优先。仅当两边都是 Int 才使用整数运算。
```php
use native_types;
$a = 10; // php::Int
$b = 2.5; // php::Float
$c = $a + $b; // Float + Float → double 加法
@ -521,7 +529,9 @@ $e = 3; // php::Int
$f = $d + $e; // Int + Int → int64_t 加法
```
> **注意**:原生类型变量在运算中**不会改变自身类型**。如 `Int += Float` 在 C++ 中执行 `int64_t += double`,结果截断为 int64_t,与 PHP 行为不同(PHP 中变量会变为 float)。这是 `use native_types` 有意为之的语义。
> **注意**:原生变量在运算中**不会改变存储类型**。例如 `Int += Float` 的结果仍是
> Int。这种固定类型行为现在是默认规则;需要动态整数扩展时应显式选择
> `use varint_types` 或 `std::any()`。
### 规则三:高精度类型的安全提升
@ -560,15 +570,15 @@ $f = $d + $e; // Int + Int → int64_t 加法
`+=`、`-=`、`*=`、`/=`、`%=` 等复合赋值运算符遵循相同的类型提升规则,但 RHS 会被转换为 LHS 变量的类型。若 LHS 为 Var,RHS 保持原类型(Var 的 `operator+=` 接管);若 LHS 为原生类型,RHS 显式转换为该类型。
```php
$a = 10; // Var
use varint_types;
$a = 10; // 该文件启用 varint_types,因此为 Var
$a += 2.5; // Var::operator+=(float) → ZendVM → $a 变为 float(12.5)
use native_types;
$b = 10; // php::Int
$b = std::int(10); // varint_types 文件中显式声明 php::Int
$b += 2.5; // int64_t += double → C++ 隐式截断 → $b = 12 (Int)
```
---
**最后更新**: 2026 年 5 月 26 日
**适用版本**: PHP AOT Compiler v1.x
**最后更新**: 2026 年 9 月 6 日
**适用版本**: TypePHP 0.8+

@ -510,8 +510,6 @@ sec: 67.638107061386108
### std::array
测试代码:
```php
use native_types;
function main(int $argc, array $argv): void
{
$u = (int)$argv[2];

@ -917,8 +917,6 @@ echo $x->contains("test");
```php
<?php
declare(strict_types=1);
use native_types;
/**
* 扩展方法:判断 Int 是否为素数
@ -974,8 +972,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
$raw = " <h1>Hello World!</h1> \n";
@ -998,8 +994,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
$data = [5, 2, 8, 1, 9, 3, 7];
@ -1034,8 +1028,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// 大整数阶乘(使用复合赋值,更简洁)
@ -1072,8 +1064,6 @@ function main(): void {
```php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
// 写入文件

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
class Data {
public int $value = 0;
public bool $bv = true;

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{
$integer = std::bigInt("123456789012345678901234567890");

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
function demo_add(int $a, int $b): int
{

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
const BLOCK_GRASS = 1;
const BLOCK_SAND = 2;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
const DEMO_BLOCK_GRASS = 1;
const DEMO_BLOCK_SAND = 2;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
const KEY_W = 0x57;
const KEY_A = 0x41;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
const OCEAN_WEATHER_SUNNY = 0;
const OCEAN_WEATHER_CLOUDY = 1;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace Yangweijie\Ui2\Games\OnePieceDoudizhu;

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main() {
$v = usleep(111);
var_dump($v);

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
final class WasiDemo

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
#[WasmExport(name: 'get-demo-report')]
function getDemoReport(string $argumentsJson, string $greeting, string $stdin): string

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main()
{
$a = std::bigFloat("3.14");

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main()
{
$a = std::bigFloat("10.0");

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main()
{
$a = std::bigInt(100);

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main()
{
$a = std::bigInt(100);

@ -1,7 +1,7 @@
<?php
function main(): void
{
$arr = [1, 2];
$arr = std::any([1, 2]);
$copy = function () use ($arr) {
$arr[] = 3;
return $arr;
@ -14,7 +14,7 @@ function main(): void
};
$ref();
$value = 'old';
$value = std::any('old');
$returnCapturedRef = function () use (&$value) {
return $value;
};

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function addToMaximum(int $value): int
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,7 +1,6 @@
<?php
use varint_types;
declare(strict_types=1);
function main(): void
{

@ -31,7 +31,7 @@ function main(): void
$rest = [1, 2, 3, 4, 5];
$i = 0;
$plain = 1;
$ref = 1;
$ref = std::any(1);
$object = new MagicHolder();
echo count([bump(), bump()]), "\n";

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main()
{
$a = std::decimal("100");

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main()
{
$a = std::decimal("3.14");

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main()
{
$a = std::decimal("100");

@ -0,0 +1,9 @@
<?php
function fixedReferenceArgumentTarget(string &$value): void {}
function fixedReferenceArgument(): void
{
$value = 'fixed';
fixedReferenceArgumentTarget($value);
}

@ -0,0 +1,8 @@
<?php
function fixedReferenceArray(): void
{
$value = [];
$closure = static function () use (&$value): void {};
$closure();
}

@ -0,0 +1,7 @@
<?php
function fixedReferenceAssignment(): void
{
$value = [];
$reference =& $value;
}

@ -0,0 +1,11 @@
<?php
function fixedReferenceExplicitAny(): void
{
$value = std::any('fixed');
$closure = static function () use (&$value): void {
$value = [];
};
$closure();
$array = $value->toArray();
}

@ -0,0 +1,13 @@
<?php
function fixedReferenceGenericObjectValue(): object
{
return new stdClass();
}
function fixedReferenceGenericObject(): void
{
$value = fixedReferenceGenericObjectValue();
$closure = static function () use (&$value): void {};
$closure();
}

@ -0,0 +1,10 @@
<?php
final class FixedReferenceObjectValue {}
function fixedReferenceObject(): void
{
$value = new FixedReferenceObjectValue();
$closure = static function () use (&$value): void {};
$closure();
}

@ -0,0 +1,7 @@
<?php
function &fixedReferenceReturn(): mixed
{
$value = 'fixed';
return $value;
}

@ -0,0 +1,8 @@
<?php
function fixedReferenceStatic(): void
{
static $value = 'fixed';
$closure = static function () use (&$value): void {};
$closure();
}

@ -0,0 +1,8 @@
<?php
function fixedReferenceStdContainer(): void
{
$value = std::vector(Type::Int);
$closure = static function () use (&$value): void {};
$closure();
}

@ -0,0 +1,8 @@
<?php
function fixedReferenceStream(): void
{
$value = fopen(__FILE__, 'r');
$closure = static function () use (&$value): void {};
$closure();
}

@ -0,0 +1,8 @@
<?php
function fixedReferenceString(): void
{
$value = 'fixed';
$closure = static function () use (&$value): void {};
$closure();
}

@ -0,0 +1,9 @@
<?php
function fixedReferenceToRefTarget(mixed &$value): void {}
function fixedReferenceToRef(): void
{
$value = [];
fixedReferenceToRefTarget($value->toRef());
}

@ -11,7 +11,8 @@ class B extends A
{
public function &f(): array
{
static $a = [];
static $a;
$a ??= [];
return $a;
}
}

@ -3,7 +3,8 @@ class A
{
public function &f(): array
{
static $a = [];
static $a;
$a ??= [];
return $a;
}
}

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
namespace NamespaceEndingComment;

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main()
{
$x = "hello";

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
function main(): void
{

@ -1,7 +1,6 @@
<?php
use varint_types;
declare(strict_types=1);
function main(): void
{

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
class Base
{

@ -1,6 +1,5 @@
<?php
declare(strict_types=1);
trait TraitA
{

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save