Skip to content

Index literal union members by value instead of scanning pairwise - #102

Merged
JulianaSaran merged 3 commits into
0.xfrom
IP-475-large-literal-list-condition
Sep 21, 2026
Merged

JulianaSaran merged 3 commits into
0.xfrom
IP-475-large-literal-list-condition

Conversation

@JulianaSaran

Copy link
Copy Markdown
Contributor

Description

A condition with a big pasted literal list (BusinessActivities intersects ['DRI-2090', … ×940])
compiles quadratically, twice: UnionType::join dedups members pairwise (recomputing both shapes
per pair), and UnionShape::of scans pairwise again on every shape() call — one intersects
resolve asks ~4 times. ~880k comparisons per condition; a real product with 34 endorsement
conditions takes 14.2s to compile, so production hits the 30s limit rendering the
publication-checks badge (whose cache can then never warm) and running quote previews. Prod
traces die in each of the two loops (attached as a comment).

Literals are now found through an index keyed by LiteralShape::valueKey() — a hash key
consistent with equals: equal literals always share a key; sharing one only nominates, the
sameness judgment still decides (big ints sharing a float image stay distinct, NAN equals
nothing). Non-literal members keep the pairwise scan — only literals arrive in bulk. Same move
as #73; here the key isn't the whole equality, so the bucket verifies.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Performance improvement

Related Issues

Relates to IP-475 (internal tracker; production timeouts in Product Catalogue).

Changes Made

  • LiteralShape::valueKey() — canonical hash key consistent with value equality.
  • DistinctShapes (@internal) — insert-only set under an injected sameness judgment;
    literals via the key index, everything else pairwise.
  • UnionShape::of and UnionType::join dedup through it; join computes each type's
    shape once instead of once per comparison.
  • Tests: a pairwise oracle (of() ≡ naive scan over a hostile pool) and a lockstep law
    (ValueEquality::equals(a,b) ⇒ same key, with drift sentinels: case, whitespace,
    unicode composition, ±0.0, the 2^53 boundary, NAN/INF), plus corner pins.

Testing

  • All existing tests pass (composer test) — 1,316 tests, zend.assertions=1
  • Added new tests for the changes
  • Static analysis passes (composer test:types)
  • Code coverage remains at 100% — no coverage driver on my machine; relying on CI
  • Mutation testing passes (composer test:infection) — same, relying on CI

Code Quality Checklist

  • My code follows the project's coding standards
  • I have run vendor/bin/pint to format my code
  • I have added/updated PHPDoc comments where necessary
  • I have updated the documentation (if applicable)
  • My changes generate no new warnings or errors
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Documentation

  • I have updated the README.md (not applicable — internal performance change)
  • I have updated the CHANGELOG.md
  • I have added/updated code examples (not applicable)

Breaking Changes

N/A — behaviour is pinned identical by the oracle test; the index only changes cost.

Additional Notes

  • Bulk non-literal unions (e.g. hundreds of record literals) stay pairwise — same as
    today, no regression.
  • UnionShape::equals and union×union overlap remain O(N·M) — follow-up material.
  • Deeper option: a value-keyed set representation for enum-sized unions (or full
    hash-consing) — this PR is the minimal step and doesn't block it; the oracle and
    lockstep tests carry over unchanged.

🤖 Generated with Claude Code

@JulianaSaran
JulianaSaran force-pushed the IP-475-large-literal-list-condition branch from 115d194 to e1ed759 Compare September 21, 2026 12:08
@JulianaSaran
JulianaSaran marked this pull request as ready for review September 21, 2026 13:12
@JulianaSaran
JulianaSaran merged commit 6197285 into 0.x Sep 21, 2026
6 checks passed
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.

2 participants