parent
a70a0ad078
commit
a8f7c29ea0
3 changed files with 196 additions and 5 deletions
@ -0,0 +1,190 @@ |
||||
# AOT/PHP Incompatibility List |
||||
|
||||
This document lists only the key areas in which the current AOT compiler is |
||||
incompatible with or more restrictive than standard PHP. |
||||
|
||||
## Program structure |
||||
|
||||
- Executable statements are not allowed at global scope; only static constructs |
||||
such as declarations, `use`, `declare`, and constant definitions are allowed. |
||||
- Function declarations are not allowed inside functions or methods. |
||||
- Named class declarations are not allowed inside functions or methods. |
||||
- Binary mode must define a global `main()`. |
||||
- `main()` may either take no parameters or have the signature |
||||
`(int $argc, array $argv)`. |
||||
- `main()` must return `void`. |
||||
|
||||
## Declarations and types |
||||
|
||||
- Variable variables `$$var` are not supported. |
||||
- PHP 8.5 `#[NoDiscard]` is not supported yet. |
||||
- The PHP 8.5 `(void)` cast is supported for explicitly discarding a value; the |
||||
operand is still evaluated and its side effects are preserved, and the cast |
||||
cannot be used in value contexts such as assignments, returns, arguments, or |
||||
conditions. |
||||
- Support for PHP 8.5 `clone()` / clone-with requires the linked `libphp` to be |
||||
version 8.5 or later. Public and dynamic properties, private/protected/readonly |
||||
properties, property hooks, call ordering, error propagation, and callable |
||||
paths are all covered by PHPT tests. |
||||
- PHP 8.4 property hooks are compiled into AOT getters/setters and register the |
||||
corresponding Zend hook metadata; direct property reads/writes, Reflection, |
||||
and object iteration are all supported. Taking a reference to a hooked |
||||
property is currently not supported. |
||||
- PHP 8.4 Reflection Lazy Objects cannot be used with TypePHP AOT classes. AOT |
||||
classes are registered as persistent internal classes, and Zend's |
||||
`zend_object_make_lazy()` explicitly rejects internal classes. Zend PHP user |
||||
classes loaded dynamically at runtime are not subject to this restriction. |
||||
- `private(set)` and `protected(set)` asymmetric property visibility is |
||||
supported, including constructor property promotion. Zend-backed objects |
||||
perform the scope check through the PHP 8.4+ class-level object handler and |
||||
preserve the promoted / set-visibility / implicit-final reflection flags; |
||||
Native objects enforce the equivalent scope rules through compile-time access |
||||
checks. |
||||
- Final properties declared through constructor promotion are supported, but |
||||
TypePHP requires an explicit `public`, `protected`, or `private` modifier; |
||||
PHP 8.5's implicitly public form, `final int $value`, is not accepted. As a |
||||
TypePHP extension, this syntax is independent of the PHP source-syntax version |
||||
supported by the linked `libphp` and remains available when using a PHP 8.4 |
||||
`libphp.so`. |
||||
- TypePHP forbids attributes on global or namespaced constant declarations; PHP |
||||
8.5 global constant attributes are out of scope. Class constant attributes are |
||||
not affected by this restriction. |
||||
- Returning by reference from closures or arrow functions is not supported. |
||||
- PHP 8.5 `static function` expressions in global constants, class constants, |
||||
parameter defaults, or property defaults are not supported yet. Closures |
||||
nested inside initializer expressions are likewise rejected at compile time. |
||||
- `__construct()` may not have a return value. |
||||
- A parameter with a default value may not appear before a required parameter |
||||
(PHP permits this legacy pattern but treats the former parameter as required). |
||||
- Variadic parameters by reference `&...$args` are not supported. |
||||
- Union, intersection, and nullable types are still represented as `mixed/any` |
||||
in C++, but the static analysis phase uses known expression types to reject |
||||
definitely incompatible arguments, return values, and property assignments |
||||
ahead of time; dynamic values still retain their runtime type checks. |
||||
- Once a local variable's type has been statically inferred as a concrete native |
||||
type, reassigning it to an incompatible type within the same scope is not |
||||
supported. |
||||
|
||||
## declare |
||||
|
||||
- `declare(ticks=...)` is not supported. |
||||
- `declare(encoding=...)` accepts only `UTF-8`. |
||||
- `declare(strict_types=...)` accepts only `strict_types=1`. |
||||
- No other `declare` directives are supported. |
||||
|
||||
## Calls and references |
||||
|
||||
- `exit(message: $value)` is available as a TypePHP named-argument extension; it |
||||
enters the same exit path as the positional form `exit($value)`. |
||||
- TypePHP uses strict argument-count rules: non-variadic functions do not accept |
||||
extra arguments beyond the declared signature, and `func_get_args()` does not |
||||
implicitly relax the signature. |
||||
- Reference parameters and write-back semantics are supported for ordinary |
||||
functions, ordinary methods, and native direct calls with known signatures; |
||||
do not mistakenly describe the compiler's internal cross-trait dynamic-dispatch |
||||
limitation as "TypePHP does not support reference parameters". |
||||
- Closures and arrow functions do not support reference parameters. |
||||
- Reference assignment cannot create a reference from a complex static-property |
||||
expression. |
||||
- Calls whose argument signature cannot be determined at compile time — dynamic |
||||
calls, closure calls, and the like — cannot convert reference parameters |
||||
automatically; `refval()` or the equivalent keyword method `toRef()` must be |
||||
used explicitly. |
||||
- `refval()` / `toRef()` only accept variables, array elements, or object |
||||
properties. |
||||
- A call that uses argument unpacking followed by named arguments falls back to |
||||
dynamic dispatch and cannot use the native call path. |
||||
|
||||
## Object model |
||||
|
||||
- Reserved keyword methods such as `toInt()`, `toString()`, and `toArray()` are |
||||
resolved before ordinary object methods; an application method of the same |
||||
name that takes arguments is not called with ordinary object-method semantics. |
||||
- `toAny()` and `toRef()` are non-overridable TypePHP keyword methods, and |
||||
ordinary class-like declarations must not define methods with these names |
||||
(method names are case-insensitive, per PHP rules). A Native class may only |
||||
explicitly define a `toAny()` conversion method returning `mixed/any`; no |
||||
implicit conversion is provided. Native classes do not support `toRef()`. |
||||
- Fixed-layout typed properties that are not explicitly initialized use the |
||||
zero value of their type and do not preserve Zend PHP's full uninitialized |
||||
state; expressions such as `??` that depend on the uninitialized state may |
||||
therefore behave differently. |
||||
- A subclass may not shadow a parent's private property with a `private` |
||||
property of the same name; `public` / `protected` declarations of the same name |
||||
are treated as the same inherited property slot and must still satisfy the |
||||
type, visibility, and `readonly` compatibility requirements. |
||||
- To avoid introducing extra dynamic checks in the typed-property write path, a |
||||
native typed property falls back to `setProperty()` when the right-hand side's |
||||
type is unknown or inconsistent with the property type; some scalar |
||||
assignments may then follow Zend's weak type conversion instead of the AOT |
||||
default strict semantics. |
||||
|
||||
## Expressions and control flow |
||||
|
||||
- A `match` arm condition may not itself be a `match` expression. |
||||
- The value target in a by-reference `foreach` may only be a variable. |
||||
- `foreach` list destructuring does not support binding elements by reference. |
||||
- Appending, inserting, `unset()`, and wholesale replacement of `std::vector`, |
||||
`std::map`, and `std::ordered_map` are forbidden during a `foreach`; |
||||
non-structural updates of existing elements can still be done with assignment |
||||
operators. |
||||
- Fixed native typed object properties cannot be freely `unset()` with PHP's |
||||
standard uninitialized-property semantics. |
||||
- Calling `unset()` on a native-typed variable does not delete the variable as |
||||
it would in standard PHP. |
||||
|
||||
## Runtime dynamic capabilities |
||||
|
||||
- `ClassName::class` only supports string literals or statically resolvable |
||||
class names. |
||||
- `static::class` is not supported in positions that require a compile-time |
||||
constant class name. |
||||
- `__CLASS__` may only be used within a `class` definition (PHP allows |
||||
it elsewhere and returns an empty string). |
||||
- `__TRAIT__` may only be used within a `trait` definition (PHP allows |
||||
it elsewhere and returns an empty string). |
||||
- Dynamic property chains, dynamic class names, dynamic function names, and |
||||
dynamic callbacks all go through the Zend runtime fallback and are not |
||||
guaranteed to be natively optimized; reference parameters of dynamic calls |
||||
still require an explicit `refval()` or `toRef()`. |
||||
- When `Closure::bind()` binds a static closure that accesses private members, |
||||
the current behavior is not fully consistent with standard PHP. |
||||
- Storing a first-class callable in a nullable typed `Closure` property |
||||
currently has runtime stability limitations. |
||||
- All source files must be encoded as `UTF-8`. |
||||
|
||||
## Compiler bootstrapping and internal refactoring constraints |
||||
|
||||
This section describes the constraints that apply when the compiler itself is |
||||
compiled with TypePHP. These are not additional PHP semantic differences for |
||||
user code. |
||||
|
||||
- Before the refactoring, a statically resolvable `$this->method()` call within |
||||
the same core class generated a native C++ direct call. Reference parameters |
||||
were mapped directly to `php::Ref` or a C++ reference, and write-back |
||||
semantics worked normally. |
||||
- After splitting the caller and callee into different traits, compiling the |
||||
trait body on its own makes it impossible to determine the final host class |
||||
from the trait's `$this`. The current method resolver may lower a cross-trait |
||||
call to a Zend method call, for example generating |
||||
`this_.call(..., php::ArgList{value})`. |
||||
- The `ArgList` of a dynamic method call does not automatically promote ordinary |
||||
arguments to references based solely on the callee wrapper's arginfo. If the |
||||
callee method declares `&$value`, the wrapper fetches the argument through |
||||
`getCallArgByRef()`, while the caller passes an ordinary value; the result is a |
||||
`must be passed by reference` warning, and modifications made by the callee |
||||
cannot be written back to the caller. |
||||
- Therefore, cross-trait APIs inside the compiler must not use reference output |
||||
parameters or a protocol in which a passed scalar or array is modified and |
||||
then read by the caller. They should return a result value, a tuple array, or |
||||
a DTO — for example, use `[$type, $class] = resolveTypeDecl(...)` instead of |
||||
`parseTypeDecl(..., &$class)`. |
||||
- Internal helpers for string accumulation, array sorting, parse-result output, |
||||
and the like should preferably be designed around pure return values: |
||||
`$code .= format(...)`, `$files = sort(...)`. Only when the call is confirmed |
||||
to stay a native direct call may reference write-back be relied upon. |
||||
- Every time a method is moved to a trait, a parent class, or a separate |
||||
component, at least one test covering that call must be recompiled with the |
||||
bootstrapped artifact; running tests only with `bin/tpc.php` cannot reveal |
||||
problems where "the source compiler works but the bootstrapped compiler |
||||
regresses". |
||||
Loading…
Reference in new issue