-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make incremental compilation aware of synthesized mirrors #18310
Conversation
This will be used later in this PR to perform dependency recording during Typer. The new API is slightly higher level: - Dependencies are recorded using `addUsedName`, `addUsedRawName` and `addClassDependency`. - `addUsedName` takes a Symbol instead of a Name and takes care of name mangling, `addUsedRawName` is still available when there is no symbol (currently this only happens when recording a dependency on a renamed import). - These methods do not take a `from` argument, instead they take care of calling `resolveDependencySource` internally. - Recorded dependencies are sent to Zinc using `sendToZinc`.
This option is meant to avoid overcompilation involving sealed classes. By default, Zinc invalidates all usages of a sealed class when a child is added or removed, even though `val x: Sealed = new Child1` will work the same if `Sealed` gets an extra child. When this option is on, Zinc only takes children into account if the name usage is reported with `UseScope.PatMatTarget`. Note that currently this is only set when traversing the scrutinee of a pattern match (just like in the Scala 2 implementation), but to safely turn this option on by default we would need to kept track of all call to `children` and `sealedDescendants` in the compiler. There were two issues related to this option before this commit: - ExtractAPI was unnecessarily extracting the `@Child` annotation, so the API hash of a sealed class changed even with `useOptimizedSealed` enabled. - When registering a used name with `UseScope.PatMatTarget` we should also register it with `UseScope.Default` because (part of) the scrutinee type might not be a sealed class, in which case it won't have a name hash for PatMatTarget but should still lead to invalidations. Since we only ever use two values for the `UseScope` parameter of the `usedName` callback, we create two vals `DefaultScopes` and `PatMatScopes` that we reuse for performance.
A product mirror needs to be resynthesized if any class parameter changes, and a sum mirror needs to be resynthesized if any child of the sealed type changes, but previously this did not reliably work because the dependency recording in ExtractDependencies was unaware of mirrors. Instead of making ExtractDependencies aware of mirrors, we solve this by directly recording the dependencies when the mirror is synthesized, this way we can be sure to always correctly invalidate users of mirrors, even if the synthesized mirror type is not present in the AST at phase ExtractDependencies. This is the first time that we record dependencies outside of the ExtractDependencies phase, in the future we should see if we can extend this mechanism to record more dependencies during typechecking to make incremental compilation more robust (e.g. by keeping track of symbols looked up by macros). Eventually, we might even want to completely get rid of the ExtractDependencies phase and record all dependencies on the fly if it turns out to be faster.
Performance test finished successfully: Visit https://dotty-bench.epfl.ch/18310/ to see the changes. Benchmarks is based on merging with main (a73316a) |
if (includeSealedChildren) | ||
_names(name) = PatMatScopes | ||
else | ||
_names.getOrElseUpdate(name, DefaultScopes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this going to reuse the closure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The by-name closure isn't guaranteed to be reused, it's up to the JVM
@bishabosha, I think it requires #17594 for backporting. Is that right? |
yes, at least it is using the |
A product mirror needs to be resynthesized if any class parameter changes, and
a sum mirror needs to be resynthesized if any child of the sealed type changes,
but previously this did not reliably work because the dependency recording in
ExtractDependencies was unaware of mirrors.
Instead of making ExtractDependencies aware of mirrors, we solve this by
directly recording the dependencies when the mirror is synthesized, this way we
can be sure to always correctly invalidate users of mirrors, even if the
synthesized mirror type is not present in the AST at phase ExtractDependencies.
This is the first time that we record dependencies outside of the
ExtractDependencies phase, in the future we should see if we can extend this
mechanism to record more dependencies during typechecking to make incremental
compilation more robust (e.g. by keeping track of symbols looked up by macros).
Eventually, we might even want to completely get rid of the ExtractDependencies
phase and record all dependencies on the fly if it turns out to be faster.