Repository navigation
lsp: one walk per check, not two - #208
Merged
Merged
Conversation
A check asked `check_files_diag` for the errors and then `lint_program`
for the lint findings, and `lint_program` reaches the L5xx notes through
`lint_conversions` — a second merge and a second full lowering walk over
the same program, asking again what the first had just computed. On the
compiler's own program that second walk was about half of every check.
`check_and_lint` (lint.zg) asks both in one walk. `lint_conversions_of`
(emit.zg) is `lint_conversions` over a program already merged that also
hands back the walk's findings, and it keeps the one copy of the rule
that a program with errors gets no conversion advice. Keeping the notes
changes no finding — `want_lints` is read by `chk_note_at` alone — so
the errors are still the ones `check-equal` holds to the build's.
`lint_program` and `lint_conversions` keep their shape; the rules they
share moved into `lint_merged`, and `lint_program` now merges once.
One check of `src/compiler/zerg/lint.zg` in `zerg lsp`: 11.7 s -> 6.6 s,
172 G -> 91 G instructions retired, 217 MB -> 154 MB peak.
Refs #22.
`make lsp` now opens `src/compiler/zergc.zg` in a real session and
measures what the check cost from OUTSIDE the process, against
`zerg build --emit check` on the same program — one walk by
definition. At 1.5 walks or more it fails, and so it does when the
check publishes any error: the program builds, so an error there is a
check that aborted part-way, which costs less than a walk and would
otherwise pass. The unit is instructions retired where macOS `time -l`
reports them, CPU time elsewhere: not wall time, and not CPU time on
Apple silicon, where the core a process lands on moves the seconds and
not the work.
Every other case asks WHAT a check says, and a check that walks twice
says the same thing — `check-equal` is green on both servers.
Two holes the one walk opened are closed in section 1 as well. The
server now takes its L5xx notes from the walk that found the errors,
and the drop of those notes when the program has an error is shared
with `zerg lint` (`lint_conversions_of`) — so no buffer could see that
drop go missing, and the comparison with `zerg lint` cannot either,
since both lose it together. A buffer with an error AND a literal the
walk notes is added, held to `zerg lint`, and asserted outright to
show an error and no note; the assertion is what goes red, shown
below with the drop deleted. And the lint half is compared
in order rather than sorted, because the order — tree rules, notes,
what `#[allow(…)]` says — is the linter's answer too.
Red on the two-walk server (main):
WALK one check of src/compiler/zergc.zg costs 2.07 walks
(instructions: check 83162423161, server 172225314890)
Red with the drop of the notes deleted from `lint_conversions_of`:
NOTED a buffer with an error: errors ['E3033 …'], conversion notes
['L502 the literal `1` is a float here …', …]
Red with the check made to abort on the compiler's program:
WALK the server reports an error in src/compiler/zergc.zg, which
builds: simulated abort
Green on the one-walk one:
WALK one check of src/compiler/zergc.zg costs 1.09 walks
(instructions: check 83258518710, server 90846980400)
Refs #22.
The `make lsp` step was guarded on the private corpus fetch, so a
broken corpus secret switched it off — and with it the one-walk case,
which measures a check of src/compiler and needs nothing the corpus
holds. The corpus only widens the list of buffers; the examples alone
clear every floor the gate sets. The guard is gone, and `lsp` joins
gates-check's ledger of gates that read the corpus and tolerate its
absence, so the registry and the workflow agree.
Refs #22.
The tool chapter said `publishDiagnostics` walks the program twice and
named one walk as the next thing to measure. It is measured and built:
the request is answered by `lex_diags` and `check_and_lint`, and the
paragraph records the cost the gate measures — 2.07 walks before, 1.09
after, in instructions retired against `zerg build --emit check` — and
that `make lsp` fails at one and a half. Instructions rather than
seconds, for the reason the gate gives. en and zh-TW together.
Refs #22.
cmj0121
force-pushed
the
fix/22-one-walk
branch
from
September 22, 2026 04:04
d9a3bc2 to
76d5fd1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #22. Part of #15.
A check now answers diagnostics and lint in one lowering walk. The server used to run
check_files_diag(a merge and walk #1) and thenlint_program, whoselint_conversionsmerged again and ran walk #2 to collect the L5xx notes.lint.check_and_lintmerges once, walks once keeping both the errors and the notes, and runs the tree rules on the same merged program.zerg lintandzerg buildbehave as before;zerg lintalso stops parsing the program twice.On the compiler's own program, one check went from 2.07 walks to 1.09, measured in instructions retired against
zerg build --emit check.Commits
fix(lsp): one lowering walk per check, not two—emit.lint_conversions_of(one walk over a merged program; the no-notes-when-errors rule lives only here),lint.check_and_lint, one guarded call inls_checktest(lsp): a check of the compiler's program costs one walk—scripts/lsp-check.sh: section 6 openssrc/compiler/zergc.zgin a real session, measures the server from outside againstzerg build --emit check, fails at one and a half walks or on any error; section 1 holds a buffer with an error and an L502 literal to "an error and no note", and compares lint findings in orderbuild(ci): the language-server gate runs without the corpus— themake lspstep no longer waits on the corpus fetch;gates-checklistslspas tolerating its absencedoc(lsp): diagnostics are one walk, and the gate that says so—docs/tooling/lsp.mdandlsp.zh-TW.mdEvidence
lint_conversions_of, green with itmake testgreen on the branch headNot in this PR
textDocument/codeActionstill runs its own walk — lsp: a quick fix costs no second walk #204🤖 Generated with Claude Code