TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
13 KiB
163 lines
13 KiB
# PHP Incompatibility Classification
|
|
|
|
This document classifies TypePHP AOT incompatibilities by their cause and
|
|
expected future direction.
|
|
|
|
The goal is to distinguish:
|
|
|
|
- Hard limits that should not be promised as fully compatible PHP behavior.
|
|
- Intentional TypePHP language rules.
|
|
- Features that are implementable but not supported yet.
|
|
- Partial support where the current behavior is known to differ from PHP.
|
|
|
|
The main compatibility checklist remains `INCOMPATIBLE_PHP_FEATURES.md`. This
|
|
document explains how those items should be interpreted.
|
|
|
|
## Categories
|
|
|
|
### Hard Limit
|
|
|
|
The feature conflicts with the current AOT execution model, or exact PHP
|
|
compatibility would require unreasonable runtime mirroring, heavy dynamic state,
|
|
or semantics that are not naturally visible from compiled C++ frames.
|
|
|
|
This does not always mean "theoretically impossible", but TypePHP should not
|
|
promise full PHP compatibility for these cases.
|
|
|
|
### Intentional Rule
|
|
|
|
The feature could be implemented, but TypePHP intentionally rejects or restricts
|
|
it to keep the AOT model explicit, predictable and optimizable.
|
|
|
|
These are language or product rules, not missing implementation work.
|
|
|
|
### Pending
|
|
|
|
The feature is technically implementable and should be described as currently
|
|
unsupported, not as impossible.
|
|
|
|
These items usually need better IR, symbol binding, runtime helpers, or codegen
|
|
lowering.
|
|
|
|
### Partial
|
|
|
|
The feature exists but does not fully match standard PHP behavior in all edge
|
|
cases.
|
|
|
|
These items should be documented with the exact boundary.
|
|
|
|
## Hard Limits and Non-Promised Compatibility
|
|
|
|
| Feature | Classification | Reason |
|
|
|---|---|---|
|
|
| `eval()` accessing AOT-compiled local variables | Hard Limit | AOT locals are C++ stack variables. PHP code executed by Zend VM through `eval()` cannot naturally access that compiled stack frame. Exact compatibility would require a locals mirror and synchronization layer. |
|
|
| Standard PHP variable deletion semantics for `unset($nativeTypedVar)` | Hard Limit / Intentional Rule | Native typed locals are C++ variables, not entries in a PHP symbol table. They cannot be deleted like PHP zval variables. |
|
|
| Fully standard uninitialized / `unset()` semantics for fixed native typed object properties | Hard Limit / Intentional Rule | Fixed native property storage conflicts with PHP's dynamic property state, uninitialized state and unset behavior. |
|
|
| `Closure::bind()` with static closures accessing private members across AOT/native boundaries | Hard Limit / Partial | This depends on Zend closure scope, private visibility checks and AOT native method/property access. Partial support may be possible, but complete equivalence is difficult. |
|
|
| Non-UTF-8 source files | Intentional Rule | Other encodings could be converted before compilation, but TypePHP requires UTF-8 to keep parsing and generated code deterministic. |
|
|
| `declare(encoding=...)` values other than `UTF-8` | Intentional Rule | Same reason as source file encoding. |
|
|
|
|
## Intentional TypePHP Rules
|
|
|
|
| Feature | Classification | Reason |
|
|
|---|---|---|
|
|
| No executable statements in global scope | Intentional Rule | A global execution block could be generated, but it complicates initialization order, side effects and include-like behavior. TypePHP requires executable code to be under functions or methods. |
|
|
| 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. |
|
|
| 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. |
|
|
| Reassigning a statically inferred native local to an incompatible type | Intentional Rule | This is the cost of native type optimization. Use dynamic zval variables when PHP-style type changes are required. |
|
|
| Native `std::int` overflow and integer division behavior | Intentional Rule | Native numeric types trade PHP compatibility for performance and C++ storage. |
|
|
| `__CLASS__` outside class context and `__TRAIT__` outside trait context | Intentional Rule | PHP returns an empty string for legacy compatibility. TypePHP rejects this as a clearer rule. |
|
|
| Strict function argument counts | Intentional Rule | Non-variadic functions reject extra arguments. `func_get_args()` does not implicitly make a function variadic. |
|
|
| Reserved keyword methods such as `toArray()` | Intentional Rule | Conversion keywords are resolved before ordinary object methods to keep conversion lowering static and predictable. |
|
|
| Zero-initialized fixed typed property slots | Intentional Rule / Partial | Native fixed-layout slots use their type's zero value instead of preserving every Zend uninitialized-property transition. |
|
|
| Structural mutation of `std` containers during `foreach` | Intentional Rule | Native C++ iterators may be invalidated by append, insertion, erase or whole-container replacement. TypePHP rejects these operations inside the active loop while allowing non-structural element updates. |
|
|
|
|
## Implementable but Currently Unsupported
|
|
|
|
| Feature | Classification | Implementation Direction |
|
|
|---|---|---|
|
|
| Variable variables (`$$var`) | Pending | Add a function-local symbol table mirror for dynamic locals, and disable or synchronize native locals that escape into dynamic lookup. |
|
|
| Closure or arrow function returning by reference | Pending | Closure metadata and wrappers must preserve return-by-reference and emit `ReturnRef`. |
|
|
| Closure and arrow function by-reference parameters | Pending | Closure arginfo must preserve by-reference parameters and call lowering must pass reference slots. |
|
|
| By-reference variadic parameters (`&...$args`) | Pending | Variadic storage must preserve references instead of copying values. |
|
|
| By-reference parameters with default values | Pending | Need PHP-compatible handling for omitted arguments using temporary default values while still binding references for passed arguments. |
|
|
| Reference assignment from complex static property expressions | Pending | Static property reference targets need complete lowering and lifetime handling. |
|
|
| Dynamic calls automatically converting by-reference arguments | Pending | Runtime callable metadata or reflection can identify by-reference parameters and build reference arguments dynamically. |
|
|
| Calls with unpack plus trailing named arguments staying native | Pending | Normalize and reorder call arguments in IR before native-call selection. |
|
|
| Dynamic `parent::method()` name | Pending | Needs runtime parent method lookup with correct call scope. |
|
|
| Private typed property access on cloned objects through variables | Pending / Partial | Requires a complete declaring-class-aware access resolver. |
|
|
| `ReflectionProperty::isPromoted()` for constructor-promoted properties | Pending | Generated class metadata should record promoted-property flags. |
|
|
| `echo` with assignment expressions | Pending | Requires expression lowering that preserves evaluation order and returns the assigned value. |
|
|
| Nested `match` expressions in arm conditions | Pending | Requires recursive match lowering and temporary value ordering. |
|
|
| `foreach` by-reference value targets beyond simple variables | Pending | Requires explicit lvalue/reference target modeling. |
|
|
| `foreach` by-reference with list destructuring | Pending | Requires by-reference foreach value lowering followed by destructuring assignment. |
|
|
| Dynamic `ClassName::class` | Pending | Runtime class-name resolution can be used when the class expression is dynamic. |
|
|
| `static::class` in runtime contexts | Pending / Partial | Runtime contexts can use called-class lookup. True compile-time constant contexts should remain unsupported. |
|
|
| Dynamic property chains, class names, function names and callbacks in native-optimized paths | Partial | Supported through a Zend runtime fallback. Native dispatch is only an optimization; automatic by-reference argument conversion remains unsupported. |
|
|
| First-class callable stored in nullable `Closure` typed property | Pending / Partial | Requires stable runtime lifetime, refcount and typed-property write handling. |
|
|
| Attribute arguments containing arrays or `new` expressions | Pending | Requires full constant-expression and attribute metadata generation support. |
|
|
| Static analysis of union, intersection and nullable types | Pending optimization | Requires a real union/intersection type lattice instead of treating these as `mixed/any` during static analysis. |
|
|
|
|
## Partial Support and Behavioral Differences
|
|
|
|
| Feature | Classification | Boundary |
|
|
|---|---|---|
|
|
| `eval()` | Partial / Hard Limit | `eval()` can execute PHP code through Zend VM, but it cannot access compiled local variables. Use return values or `$GLOBALS` for data exchange. |
|
|
| Dynamic calls and callbacks | Partial | Zend runtime fallback handles dynamic calls and callbacks. By-reference arguments still need explicit `refval()` / `toRef()`, and native-call optimization is not guaranteed. |
|
|
| Dynamic properties and dynamic property chains | Partial | Dynamic property reads and writes use the runtime property API; native property optimization is not guaranteed. |
|
|
| Native typed properties | Partial / Intentional Rule | Fast native paths may not preserve every PHP dynamic state transition. Unknown or incompatible values can fall back to `setProperty()`. |
|
|
| Reflection metadata | Partial | Runtime declarations exist, but some AOT-specific metadata such as promoted-property flags may be incomplete. |
|
|
|
|
## Self-hosting Compatibility Notes
|
|
|
|
The compiler itself is a TypePHP program, so an internal refactor can change
|
|
how its own calls are lowered even when the PHP source-level API is unchanged.
|
|
This is an implementation compatibility boundary, not a new user-facing rule
|
|
that reference parameters are unsupported.
|
|
|
|
| Internal pattern | Status | Boundary and required design |
|
|
|---|---|---|
|
|
| Statically resolved function or method with by-reference parameters | Supported | Native direct calls preserve reference slots and write-back semantics. This was the path used before the core methods were split into traits. |
|
|
| Cross-trait `$this->method()` with a by-reference output parameter | Self-hosting Partial | While compiling a trait body, the final consuming class may be unknown. The call can fall back to `this_.call()` with ordinary `ArgList` values, while the callee wrapper expects `getCallArgByRef()`. This produces a by-reference warning and loses write-back. |
|
|
| Cross-trait helper returning a value, tuple array or DTO | Supported / Required internally | Return data explicitly and assign it at the call site. Do not use by-reference output parameters for compiler services that may cross trait boundaries. |
|
|
| Moving an existing method into a trait | Requires bootstrap verification | Test both the PHP-source compiler and the newly bootstrapped `tpc`; source-compiler tests alone do not exercise the changed lowering path. |
|
|
|
|
The observed regression after refactoring followed this exact sequence:
|
|
|
|
1. Before extraction, calls such as file sorting, captured-statement appending and
|
|
type declaration parsing were statically resolved inside the core class.
|
|
2. After extraction, their callers and implementations lived in different
|
|
traits mixed into `Translator`, `CompilerBase` or `Preprocessor`.
|
|
3. The self-hosted compiler emitted Zend dynamic method calls for those
|
|
cross-trait edges and passed ordinary values.
|
|
4. Callee wrappers still correctly advertised and parsed reference parameters,
|
|
but they could not retroactively turn the caller's value argument into the
|
|
caller's reference slot.
|
|
5. The fixes replaced internal output-parameter protocols with explicit return
|
|
values. User-level statically known reference calls remain supported.
|
|
|
|
## Documentation Rule
|
|
|
|
When documenting a compatibility difference, use one of these labels:
|
|
|
|
- `Hard Limit`
|
|
- `Intentional Rule`
|
|
- `Pending`
|
|
- `Partial`
|
|
|
|
Avoid using only "unsupported" unless the reason is also clear.
|
|
|
|
Recommended wording:
|
|
|
|
- "Hard limit: not promised to match PHP exactly."
|
|
- "Intentional TypePHP rule."
|
|
- "Currently unsupported; implementable in a future compiler/runtime revision."
|
|
- "Partially supported with the following boundary."
|
|
|
|
This distinction helps users know whether they should rewrite code permanently,
|
|
wait for future support, or disable native optimization for a specific path.
|
|
|