Skip to content

feat(types): accept an array of any object at an array<Object> parameter#519

Open
chadmandoo wants to merge 1 commit into
illegalstudio:mainfrom
chadmandoo:upstream-pr/array-element-object-covariance
Open

feat(types): accept an array of any object at an array<Object> parameter#519
chadmandoo wants to merge 1 commit into
illegalstudio:mainfrom
chadmandoo:upstream-pr/array-element-object-covariance

Conversation

@chadmandoo

Copy link
Copy Markdown
Contributor

What

PHP array parameters carry no element-type enforcement (there are no generics), so a declared array<Object> should accept an array of any object — a sibling or a subtype alike.

Previously rejected (two concrete types implementing different interfaces, passed to the same array param):

final class QCond implements CondA { /* ... */ }
final class CCond implements CondB { /* ... */ }

final class QB {
    /** @var list<QCond|CCond> */
    private array $conditions;
    private function withConditions(array $conditions): self { /* ... */ }
    public function where(string $s): self   { return $this->withConditions([new QCond($s)]); }
    public function compound(string $s): self { return $this->withConditions([new CCond($s)]); } // rejected
}

How

In type_accepts, the Array(expected_elem) arm now also accepts an Array(actual_elem) when both element types are objects. Both sides share the same run representation (a boxed object pointer), so there is no element re-typing — the callee reads elements through the class table regardless of the static element name.

Test

test_array_element_object_sibling_covariance_at_param — a withConditions-style builder passing an array of a sibling concrete object, byte-parity vs PHP 8.5.

🤖 Generated with Claude Code

chadmandoo added a commit to chadmandoo/elephc that referenced this pull request Jul 10, 2026
Survey-driven object/Mixed covariance at callback + interface boundaries
(85% → 88%, 262 → 269/307). illegalstudio#470 (mixed/union arg boundary) already landed
in 5052070; this completes the family:

- ARRAY-CALLBACK OBJECT/MIXED ELEMENTS (illegalstudio#519 / EC-28b): array_map fabricated
  an IntLiteral dummy for non-scalar elements, so a typed callback
  (`fn (FieldDescriptor $f) => ...`) over a `list<FieldDescriptor>` failed
  "expects Object, got Int". array_map now routes through the object-aware
  comparator_dummy_arg_for_elem (synthetic-var + env binding), and that helper
  gains a Mixed arm — a bare `array` element infers as Mixed (phpdoc generics
  aren't enforced on the PHP `array` type), and Mixed accepts any declared
  parameter at the runtime-enforced boundary. usort/uasort widen their element
  gate from Object to Object|Mixed so the same typed-comparator shape checks.
  Clears the array_map/usort/uasort `callback parameter expects Object, got
  Int` family (8).
- INTERFACE COVARIANT SELF-RETURN (#547 / EC-53): a `withX(): static`/self
  implementation of an interface-typed return (PSR-7 immutability methods)
  cannot go through type_accepts — the class is not yet registered mid-schema.
  Accept it from the facts at hand (this validation IS the proof the class
  implements the interface). Clears FileResponse et al. `withStatus incompatible
  return type` (3).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PHP `array` parameters carry no element-type enforcement (there are no
generics), so a declared `array<Object>` accepts an array of ANY object — a
sibling or a subtype alike. Both sides share the same run representation (a
boxed object pointer), so there is no element re-typing and the callee reads
them through the class table regardless of the static element name.

This is what a query-builder-style `withConditions(array $conditions)` needs
when one call passes `array<QueryCondition>` and another `array<CompoundCondition>`
(two concrete types implementing different interfaces), matching the docblock
`list<QueryConditionInterface|CompoundConditionInterface>`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chadmandoo chadmandoo force-pushed the upstream-pr/array-element-object-covariance branch from 7e32d0c to 2d807ff Compare July 11, 2026 14:02
@github-actions github-actions Bot added area:types Touches type checking, inference, or compatibility. size:xs Very small pull request. type:feature Introduces new user-visible behavior or capabilities. labels Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:types Touches type checking, inference, or compatibility. size:xs Very small pull request. type:feature Introduces new user-visible behavior or capabilities.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant