Skip to content

feat(types): array element covariance into a declared array<Object> parameter#520

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

feat(types): array element covariance into a declared array<Object> parameter#520
chadmandoo wants to merge 1 commit into
illegalstudio:mainfrom
chadmandoo:upstream-pr/array-element-covariance-mixed

Conversation

@chadmandoo

Copy link
Copy Markdown
Contributor

What

A PHP array parameter is a bare array — PHP does not enforce the @param array<T> element type at runtime, so the declared element type is a hint, not a hard bound. Two cases were rejected:

final class Builder {
    /** @param list<CondI> $conditions */
    public function __construct(private array $conditions) {}
    private function mkGroup(string $op): Group { return new Group($op); }
    public function addLeaf(Leaf $c): self {
        return $this->withConditions([...$this->conditions, $c]); // spread -> Array(Mixed)
    }
    public function addGroup(string $op): self {
        return $this->withConditions([$this->mkGroup($op)]);       // method-call element
    }
}

How

  • types_compatible accepts Array(Mixed) (a spread literal or an adaptive local) and an element-compatible array in either covariance direction for a declared Array(T) param — mirroring the whole-value (_, Mixed) => true trust posture, one container level down.
  • array_literal_element_type_for_ir types a method-call element as Mixed instead of the syntactic Int fallback. The old fallback stored an object-returning method's result in an Array(Int) and int-cast it at the element store (a hard backend error).

Test

test_array_element_covariance_spread_and_method_call — a fluent builder using both a spread element and a method-call element into an object-element array, byte-parity vs PHP 8.5.

🤖 Generated with Claude Code

…arameter

A PHP `array` parameter is a bare `array` — PHP does NOT enforce the `@param
array<T>` element type at runtime, so the declared element type is a hint, not
a hard bound.

- `types_compatible` accepts `Array(Mixed)` (a spread literal `[...$list, $x]`
  or an adaptive local) and an element-compatible array in either covariance
  direction for a declared `Array(T)` parameter — mirroring the whole-value
  `(_, Mixed) => true` trust posture, one container level down.
- `array_literal_element_type_for_ir` now types a method-call element
  (`[$this->mk()]`) as `Mixed` instead of the syntactic `Int` fallback. The old
  fallback stored an object-returning method's result in an `Array(Int)` and
  int-cast it at the element store (a hard backend error).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chadmandoo
chadmandoo force-pushed the upstream-pr/array-element-covariance-mixed branch from c174d77 to 78d6c4a Compare July 11, 2026 14:04
@github-actions github-actions Bot added area:eir Touches EIR definitions, lowering, validation, or passes. 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
@nahime0

nahime0 commented Jul 24, 2026

Copy link
Copy Markdown
Member

Closing as superseded

This PR is no longer needed on current main. The motivating fluent-builder scenario already compiles and runs correctly there (output G:AND|, matching PHP).

Why

Two later PRs landed the complete fix for the same surface:

  1. fix(types): safely widen object elements for bare array parameters #519 — bare array parameters with object elements stay Array(Mixed) via specialize_generic_array_param_hint, so sibling/spread call sites are not checked against an Elephc-invented array<ConcreteObject> bound.
  2. fix(ir-lower): type an indexed array-literal method-call element by the callee return #561array_literal_element_type_for_ir types method-call elements from the callee’s declared return type, instead of the syntactic Int fallback (and instead of stamping Mixed).

What this PR was

A two-part workaround, not the final model:

  • Checker: bidirectional Array(T) / Array(U) acceptance in types_compatible — a global call-boundary relaxation, broader than the bare-array param case that actually needed fixing.
  • IR: MethodCall / NullsafeMethodCall / StaticMethodCallMixed in array literals — avoids the int-cast backend error, but is coarser than typing by return type.

Cherry-picking this onto current main also leaves a dead duplicate MethodCall arm after #561 (unreachable_patterns warning), which conflicts with the zero-warnings policy.

Decision

Closing without merge. No rebase needed. If we still want the fluent-builder codegen test from this PR, it can be reintroduced on main as a pure test add — without the production hunks.

@nahime0 nahime0 closed this Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:eir Touches EIR definitions, lowering, validation, or passes. 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.

2 participants