Repository navigation
Conversation
added 3 commits
October 28, 2026 11:47
Background compile resets a stale unit (clearing its synthetics) before re-parsing and re-entering it, and the parser polls for interrupts via signalParseProgress. A scex askForResponse interrupt serviced there could force an accessor symbol left lazy by an aborted pass, failing with "no synthetic tree found for bean accessor" (seen in ScopeAndTypeCompletionTest on CI). Disable interrupts while parsing.
Add a createIGlobal factory to ScexPresentationCompiler and a test that installs an IGlobal recording any checkForMoreWork call made while Global.parseAndEnter is on the stack. It fails deterministically if the signalParseProgress override is dropped or a scalac upgrade adds another check point inside parse/enter, unlike the timing-dependent original flake. Also make the signalParseProgress comment accurate: it defers everything pollForWork does (interrupts, work items, cancellation, shutdown and fresh-run requests, yielding) for every parse on the compiler thread.
This was referenced Oct 28, 2026
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.
Problem
ScopeAndTypeCompletionTest("simple scope completion test") fails intermittently on CI with:Despite the wording, this has nothing to do with Java/bean getters. Scalac's
AccessorTypeCompleteruses that message for everyvalgetter; here it is the getter ofval utilStuffin the profile utils. It also still fails with-SCEXno-getter-adapters.Root cause
A race between scex's presentation compiler requests and the PC's background compile:
askLoadedTyped) and finishes withaskFilesDeleted. Both bumpminRunId, so every loaded shared unit (utils, profile object, adapters) becomes stale and is re-parsed, re-entered and re-typechecked bybackgroundCompile.FreshRunReqcan leave the utils unit entered but not typechecked. Its getter symbol stays lazy, and its type is completed from a tree inunit.synthetics.backgroundCompilecallsensureUpToDate, which runsreset(unit). That clearssyntheticsbefore the unit is re-parsed. The parser callssignalParseProgress, which goes throughpollForWorkand runs pending interrupts.askForResponsework (scopeMembers/translateMember) can run as one of those interrupts. It sees the old lazy getter, forces it, and finds no synthetic tree.Instrumentation at the moment of failure confirmed this every time. The utils unit was
NotLoadedwith emptysynthetics, and the PC thread was inbackgroundCompile → parseAndEnter → Parser → signalParseProgress → pollForWork.Fix
Override
signalParseProgressinIGlobalas a no-op, so the presentation compiler doesn't poll for work while parsing. That makes reset, parse and re-enter of a unit atomic with respect to scex's requests. Polling still happens during typechecking, but by then every loaded unit has been entered and its synthetic trees are back.The override applies to every parse on the compiler thread. It defers everything
pollForWorkdoes: interrupts, queued work items, cancellation checks, shutdown and fresh-run requests, and yielding. All of it resumes at the next typer check point. Scex sources are small, so the delay is negligible.askShutdownis fire-and-forget, soreset()never blocks on it.ScexPresentationCompilergets aprotected def createIGlobal(...)factory so tests can supply their ownIGlobal. Production behaviour is unchanged.Testing
Regression test.
ParsePollingTestinstalls anIGlobalthat records anycheckForMoreWorkcall made whileGlobal.parseAndEnteris on the stack, then runs a scope completion and a type completion. It also asserts the hook was actually called, so it can't pass vacuously. Unlike the original flake, it doesn't depend on timing. With the override removed it fails every time, reportingsignalParseProgress → checkForMoreWorkinside the parser. It would also catch a scalac upgrade that adds another check point inside parse/enter.Stress test. I also ran a temporary stress test (not committed) that repeats the suite's first three tests on fresh compilers, pinned to one CPU with
taskset -c 0to approximate a slow CI runner:The full
sbt testrun andscalafmtCheckAllpass.Follow-up
The underlying churn stays: shared units (utils, profile objects, adapters) are kept loaded as sources, so every request makes them stale. The background pass then re-parses and re-typechecks them, and the request's
askFilesDeletedoften aborts that pass partway through. Avoiding it means not keeping shared units loaded as sources, which is a larger change outside this PR. It is tracked in #191.A related race with the same root cause,
getErrorslosing errors becausebackgroundCompileresets the reporter, is still open as #17. This PR doesn't fix it.🤖 Generated with Claude Code