fix(types): join mismatched array/array branch merges as array<mixed> instead of bare mixed#596
Conversation
… instead of bare mixed The type checker collapsed a heterogeneous-element match/ternary/`??` array merge (e.g. `[1, 2]` vs `["a", "b"]`) to bare `Mixed`, so PHP-valid array uses of the result were rejected at compile time: passing it to a by-ref `array` parameter, `in_array()`/`array_sum()`, and spread (`[...$r]`). The spread rejection additionally emitted a misleading follow-on "Undefined variable" diagnostic naming the assignment target. `merge_match_arm_result_type` now mirrors the lowering-side `wider_type_for_merge` array/array and assoc/assoc rules through a new pure `merge_array_branch_types` helper: differing element (or key/value) types widen elementwise to `Mixed`, so the merged value keeps an `array<mixed>` type and array operations type-check just as they do for homogeneous merges. Non-array merges are untouched — scalar unions stay `Mixed` and object|null merges stay nullable unions — and an indexed-vs-associative mix still falls through to `Mixed`, matching lowering. Runtime correctness of heterogeneous merges is tracked by illegalstudio#549 / PR illegalstudio#583; this change only aligns the static type. The residual "Undefined variable" cascade for genuinely-failing spreads (repro: `$x = 5; $s = [...$x]; count($s);`) is a separate latent defect left for a follow-up. Claude-Session: https://claude.ai/code/session_01HRvbcjPHa3kAojdCjNZL5L
|
The misleading |
|
Updated this PR in place after merging the latest What changed:
Focused validation:
The independent failed-assignment Published head: |
|
Heads-up on the CI failure on All three constants are the same |
|
CI follow-up for
Focused validation:
Published head: |
|
Looking into this. On the current head The individual test name is not surfaced in the exposed job log, so we cannot name it yet. The rest of the matrix on that head is green. This comes from the CI logs; we have not reproduced it locally yet and are not claiming a cause. |
|
Thanks @mirchaemanuel |
|
Followed up on the single red check here, and I don't think it's this PR. Correcting my earlier comment too: I said the test name wasn't recoverable — it wasn't from the job-log API, but it is in the run's full log archive. The check isn't a failing assertion, it's a timeout: Killed at exactly the global 60s
Same test, same machine (macOS aarch64), back to back. This branch doesn't move it. Three other things point the same way. The test is pre-existing — added by So it reads as a runner-side timeout on a test that normally has a ~4x margin, not a regression. Could you re-run that one job? I don't have admin rights on the repo, so I can't trigger it, and it's the fastest way to confirm. I deliberately did not add a |
Fixes #587
Root cause
The type checker's branch-result join had no array-specific case. A merge such
as
match($argc) { 1 => [1, 2], default => ["a", "b"] }therefore collapsedfrom
array<int>/array<string>to baremixed, even though the EIR mergematerializes one array container with widened element storage.
That checker/runtime disagreement rejected PHP-valid consumers:
arrayparameters;array_sum()andin_array();??had a second divergence: it used the general syntactic join, so arrayalternatives could keep the left element type instead of widening elementwise.
Fix
widening their element or key/value dimensions.
Neverplaceholders defer to populated branches,bool/falsenormalize tobool, and real element-type disagreements use boxedmixedstorage.??after removing null from its valueside.
match, ternary, and?:already share the branch-result join.indexed-vs-associative merges.
mixed, rejecting valid array uses (by-ref, in_array, array_sum, spread) #587:array_sum()over indexedarray<mixed>and associativearray<*, mixed>;in_array()over indexedarray<mixed>.The runtime helpers are implemented for macOS/Linux AArch64 and Linux x86_64,
borrow their Mixed cells without ownership transfer, and keep hash iteration
read-only.
The misleading
Undefined variablefollow-on no longer fires for this issuebecause spread now type-checks. The underlying failed-assignment diagnostic
cascade remains a separate defect, reproducible independently with
<?php $x = 5; $s = [...$x]; echo count($s);.Integration
The branch now includes current
main, including #583's heterogeneous mergematerialization. The PR therefore covers the missing checker contract and the
adjacent builtin backend paths end to end rather than remaining checker-only.
Test plan
cargo buildcargo test -p elephc --lib merge_array_branchcargo test -p elephc --lib merge_null_coalesce_result_typecargo test --test error_tests heterogeneous_match,??, empty/nullbranches,
array_sum(),in_array(), by-reference mutation, and spreadcargo test --test codegen_tests heterogeneous_(35 passed)
git diff --checkhttps://claude.ai/code/session_01HRvbcjPHa3kAojdCjNZL5L