Skip to content

Two-stage collapse for oversized constant arrays#5555

Merged
ondrejmirtes merged 2 commits into2.1.xfrom
oversize-stage-collapse
Apr 27, 2026
Merged

Two-stage collapse for oversized constant arrays#5555
ondrejmirtes merged 2 commits into2.1.xfrom
oversize-stage-collapse

Conversation

@ondrejmirtes
Copy link
Copy Markdown
Member

@ondrejmirtes ondrejmirtes commented Apr 27, 2026

Summary

  • optimizeConstantArrays runs a stage 1 same-key-set collapse before its lossy generalization fallback. Variants sharing a key signature mergeWith losslessly into a single shape; per-position record structure survives, only the values widen to a union.
  • reduceArrays final pass collapses the loop-accumulator triangular variant pattern (conditional $xs[] = … push sites leaving behind list variants of progressively longer length) into a single non-empty-list<unionValueType> when their cumulative countConstantArrayValueTypes exceeds the limit. Skips when every list variant shares one key signature — those are stage 1's job.

Knock-on effects

  • bug-10717: language-code lookup now produces the precise union of every code instead of bool|literal-string.
  • bug-13509: alert variants land on the precise union of seven record shapes instead of the previous &oversized-array decomposition.
  • New nsrt oversized-array-stages.php exercises both phases — Phase 1 (small literal preserved as-is) and Phase 2 (eight conditional pushes whose triangular union triggers list-collapse, preserving the per-record (kind, value, opts) correlation as a tagged union of the eight original record shapes).
  • New regression test bug-8636 (rule test on ReturnTypeRule): the playground sample from issue #8636 — a 30-entry HUGE_CONST whose [$c]['p7'] lookup previously inferred to array<int, string>|string — now analyses clean.

Closes phpstan/phpstan#8636

Test plan

  • vendor/bin/phpunit tests/PHPStan/Analyser/NodeScopeResolverTest.php — 1547 / 1547 pass.
  • vendor/bin/phpunit tests/PHPStan/Analyser/AnalyserIntegrationTest.php — 175 / 175 pass (testBug7963 stays green).
  • vendor/bin/phpunit tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php --filter testBug8636 — passes; TDD-validated to fail when TypeCombinator.php is restored to pre-fix state.

🤖 Generated with Claude Code

ondrejmirtes and others added 2 commits April 27, 2026 18:31
Backport of `TypeCombinator` changes from two `unsealed`-branch
commits, plus their test updates:

- `optimizeConstantArrays` now runs a stage 1 same-key-set collapse
  before falling back to the lossy generalization. Variants sharing a
  key signature `mergeWith` losslessly into a single shape; the
  per-position record structure survives, only the values widen.
- `reduceArrays` final pass collapses the loop-accumulator triangular
  variant pattern (conditional `$xs[] = …` push sites leaving behind
  list variants of progressively longer length) into a single
  `non-empty-list<unionValueType>` when their cumulative
  `countConstantArrayValueTypes` exceeds the limit. Skips when every
  list variant shares one key signature — those are stage 1's job
  (per-position precision instead of a flat fold), and on 2.1.x
  without the unsealed-branch's `getUnsealedTypes()` pre-pass the
  flat fold would otherwise pre-empt stage 1 and regress bug-7963's
  144-record literal.

Knock-on effects:

- bug-10717: language-code lookup now produces the precise union of
  every code instead of `bool|literal-string`.
- bug-13509: alert variants land on the precise union of seven
  record shapes instead of the previous `&oversized-array`
  decomposition.
- New nsrt `oversized-array-stages.php` exercises both phases:
  Phase 1 (small literal preserved as-is) and Phase 2 (eight
  conditional pushes with same-shape records — the triangular union
  of list variants pushes the count past the limit, list-collapse
  folds them into a `non-empty-list` whose value type preserves the
  per-record `(kind, value, opts)` correlation as a tagged union of
  the eight original record shapes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes phpstan/phpstan#8636

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ondrejmirtes ondrejmirtes merged commit 4279f35 into 2.1.x Apr 27, 2026
113 of 114 checks passed
@ondrejmirtes ondrejmirtes deleted the oversize-stage-collapse branch April 27, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant