Skip to content

fix(php): index the single-file LSP type registry - #2702

Merged
DeusData merged 3 commits into
mainfrom
fix/php-single-file-registry-finalize
Oct 19, 2026
Merged

DeusData merged 3 commits into
mainfrom
fix/php-single-file-registry-finalize

Conversation

@DeusData

Copy link
Copy Markdown
Owner

What

cbm_run_php_lsp (the single-file PHP LSP pass) never finalized its type registry. Every lookup during single-file resolution therefore scanned the stdlib plus every definition of the file linearly: O(lookups × definitions), quadratic in the size of one file. The cross-file path (cbm_run_php_lsp_cross) already indexes its registry.

The registry is now finalized with first-registered semantics (index_first_registered), and re-indexed once after trait flattening registers more methods:

  • First-registered semantics: the first registration of a duplicate QN wins, which gives exactly the answers of the old linear scan.
  • Re-index after flattening: this keeps lookups from tail-scanning the methods that flattening adds.
  • Memory: the index lives in a scratch arena that dies with the call, as in the cross-file path.

A test-only work ceiling (CBM_LSP_WORK_CEILING_HIT, compiled in seam builds only) lets the work-counter test stop a reverted, unbounded loop deterministically instead of hanging the runner. Product builds have no ceiling, so it can never decide graph content.

Evidence

  • Graph identity: byte-identical to main on every PHP and C# corpus repo, parallel and sequential, including dotnet/roslyn and dotnet/runtime. This was proven on the combined identity build L6-y-fbd9a2ae (this fix + the trait-method index + the iterative C# walker), all of which preserve output. This PR's code is unchanged from that build.
  • Work counter (CBM_LSP_WORK, deterministic, no clock), phplsp_single_file_lookup_work_is_linear_f12, 1,000 → 2,000 chained classes (gate: ratio ≤ 2.5):
    • without the fix: work 789,210 -> 2,578,710 (ratio 3.27), RED;
    • with it: work 3,208 -> 6,289 (ratio 1.96), GREEN.

Suites run locally: php_lsp, parallel, pipeline, complexity, incremental: 927 passed.

Lint: internal/cbm/lsp/ is outside CI's LINT_SRCS / LINT_HDRS, so I ran the gates directly on the changed files:

  • clang-format is clean on the lines this PR adds.
  • cppcheck 2.20 reports one style note in php_lsp.c (redundantInitialization of qualified, about line 4311). It is in existing code outside this diff, so I left it untouched.

cbm_run_php_lsp never finalized its type registry, so every lookup during
single-file resolution scanned the stdlib plus every definition of the
file linearly: O(lookups x definitions), quadratic in the size of one
file. The cross-file path already indexes its registry.

The registry is now finalized with first-registered semantics (the first
registration of a duplicate QN wins), which keeps exactly the answers of
the linear scan, and re-indexed once after trait flattening adds methods.
The index lives in a scratch arena that dies with the call.

A test-only work ceiling (CBM_LSP_WORK_CEILING_HIT, seam builds only) lets
the work-counter test stop a reverted, unbounded loop deterministically
instead of hanging the runner; product builds have no ceiling.

Signed-off-by: Martin Vogel <[email protected]>
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