Skip to content

A promoted readonly parameter with an iterable<T> or Closure(…) docblock fails with "Cannot modify readonly property" #79

Description

@klunejko

I believe this was introduced with the wrapper changes (unified syntax style) in v0.10.3 (#74)

final class RIPromotedReadonly
{
    /** @param iterable<RIAdapter> $adapters */
    public function __construct(private readonly iterable $adapters) {}
}

final class RIPromotedReadonlyClosure
{
    /** @param \Closure(int): int $factory */
    public function __construct(private readonly \Closure $factory) {}
}

new RIPromotedReadonly([new RIAdapterA()]);                        // Error: Cannot modify readonly property …::$adapters
new RIPromotedReadonlyClosure(static fn (int $i): int => $i + 1);  // Error: Cannot modify readonly property …::$factory

The same class without readonly, a promoted readonly array<int,T> and a promoted readonly iterable without a docblock all pass. Cause, from the shipped source: src/Internal/Ast/FunctionContractInjector.php → buildParamWrappers() injects $adapters = \TypePHP\Internal\RuntimeTypeChecker::wrapIterable(__METHOD__, 'adapters', $adapters, $this); for an iterable-typed parameter (isIterableCandidate(), and wrapCallable for a callable-typed one) and, when $param->isPromoted(), appends $this->adapters = $adapters; — a second write to a property the engine already initialised from the promotion, which PHP rejects for readonly.

Expected: a promoted readonly parameter is validated without a second assignment (validate the wrapped value and leave the promoted property as initialised, or initialise the property once from the wrapped value instead of relying on the promotion), or the wrapper is skipped for readonly promotions with a documented note.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions