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.
 
 

114 lines
4.1 KiB

# `#[Immutable]` compile-time effect checking
## Purpose
`#[Immutable]` is a TypePHP compile-time annotation modelled after C++ `const`.
It prevents accidental mutation in statically compiled code without adding a
wrapper object, Zend metadata, runtime branch, or ABI change.
It is intentionally a best-effort static tool rather than a security boundary.
Calls whose target is deliberately made dynamic are an escape hatch and do not
receive a runtime guard.
## Supported targets
```php
#[Immutable]
public function name(): string
{
return $this->name;
}
function inspect(#[Immutable] User $user): string
{
return $user->name();
}
```
The attribute is valid on methods and on function, method, and closure
parameters. On an instance method it makes `$this` immutable. On a parameter it
makes the binding immutable and, when it can contain an object, treats the
referenced object as immutable as well.
## Rejected operations
For an immutable root such as `$this` or `$user`, the compiler rejects:
- assignment, destructuring, and array-element or object-property writes;
- compound assignment, `++`, `--`, `unset()`, taking a reference, and
`foreach (... as &$value)`;
- a statically named method call unless the resolved method is also marked
`#[Immutable]`;
- a mutating value extension such as `$array->sort()`; read-only array/string
methods remain available;
- passing an object to a statically resolved parameter that is not itself
`#[Immutable]`;
- passing any immutable value to a mutable by-reference parameter, including
extension functions such as `sort()`;
- storing an immutable object identity in an object property, array,
global/static variable, or returning/yielding it as a mutable value.
An immutable by-reference parameter is supported. It acts like a C++ `const &`:
the reference is accepted because the callee is checked against mutation.
`#[MethodsFor]` follows the same contract. An object extension is callable on
an immutable receiver only when its receiver parameter is marked
`#[Immutable]`.
## Aliases, closures, generators, and inheritance
Local aliases of immutable objects remain immutable:
```php
$alias = $user;
$alias->rename('new'); // compile-time error
```
`clone` creates a distinct mutable object and therefore intentionally drops the
annotation. Captured variables, arrow functions, closure `$this`, and Fiber
generator bodies carry immutable metadata into their generated function
contexts.
An overriding class or interface method may strengthen an ordinary contract by
adding `#[Immutable]`, but it cannot remove `#[Immutable]` from an inherited
method or parameter.
## Value versus object semantics
Scalar values and PHP copy-on-write values can be read and copied normally. For
example, `count($values)` and `$copy = $values` do not modify an immutable array.
The compiler propagates immutability through an expression only when object
identity is possible.
## Explicit escape hatches
The following intentionally bypass static method-effect checking:
```php
$method = 'rename';
$user->$method('new');
$callable = getRuntimeCallable();
$callable($user);
```
The same applies to other runtime-only mechanisms that hide the target from the
compiler, including reflection and dynamic ZendVM code. TypePHP neither inserts
a runtime read-only proxy nor attempts to recover the escaped value later.
This boundary is deliberate: `#[Immutable]` should cost nothing in generated
code and should not complicate PHPX/ZendVM object semantics.
## Property hooks and magic access
Property-hook reads are lowered to generated method calls. Consequently, a hook
used through an immutable receiver must itself carry an `#[Immutable]` method
contract; otherwise the generated call is rejected. Fully dynamic magic access
is covered by the same escape-hatch rule as other runtime-only behavior.
## Implementation boundaries
The implementation is isolated in `src/Immutable/ImmutableSupportTrait.php`.
`FunctionDef` and `ArgInfo` retain only compile-time effect bits, while each
`FunctionContext` stores the immutable roots and object aliases relevant to that
body. Checks run during AST lowering and emit no C++ code when successful.