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.
 
 

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.
readonly initialization window Intentional Rule A readonly property is writable only through direct $this access in its declaring class' __construct() or __clone(). Repeated initialization writes are allowed there; after either initialization path it is frozen. Ordinary calls to these lifecycle methods are rejected, while lexical parent initialization chains remain valid.

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.