-
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
Reduce projections of type aliases with class type prefixes #19931
Conversation
@rochala Can you help me fix the 2 presentation compiler test failures? I am not sure what to do here. |
I'll check it the first thing in the morning |
Dealiasing affects the typed trees which we are using for various features. I suppose it could be done if we analyse each select whether it is actually a type projection, and try recreating the original type by hand. I will try to implement those workarounds unless you have a better idea. |
Does it just apply to # projections I believe the only reason to use A # B is if both |
This functionality was only tested in one suite, the one that failed, so I can say it wasn't truly supported by presentation compiler. This also impacts generated semanticdb output. Feel free to add |
Projections P # X are types that we would like to avoid. If X is a class type, there's nothing we can do. If X is an abstract type, we use skolemization and rewrite to (x?: P).X. If X is an alias type we should simply dealias but this was not done before. This caused an exonential blowup in scala#19892, where we costructed types of the form ZPartialServerEndpoint[R, A, B, I, E, O, -C] # EndpointType[A, I, E, T, R] ... # EndpointType[A, I, E, T, R] When the were 5 or more such selections, sompile times blew up (33s for 5, timeout after 5 minutes for 6). I am still not qute sure where the blowup happened. Looking at stacktraces of random interrups it seemed to be in a deep recursion of memberDenot and asSeenFrom calls.I believe it would still be interesting to find out more about this, in case there are other similar situations where combinations of deep projections with wide applications cannot be avoided. But for this precise problem, eagerly dealising fixes it.
test performance please |
We should get benchmarks back before we can merge this. I want to rule out slowdowns in some scenarios. |
test performance please |
performance test scheduled: 149 job(s) in queue, 0 running. |
Performance test finished successfully: Visit https://dotty-bench.epfl.ch/19931/ to see the changes. Benchmarks is based on merging with main (a36849f) |
So performance seems to not worse and maybe a bit better. @smarter do you want to give this q quick review? |
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.
Otherwise LGTM.
* class type, reduce it to the dealiasd version of P # X. This means that at typer | ||
* we create projections only for inner classes with class prefixes, since projections | ||
* of P # X where X is an abstract type are handled by skolemization. At later phases | ||
* these projections might arise, though. |
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.
We can also create projections where the prefix is a type alias (they're not always reduced, e.g. when the alias has a different number of type parameters than the aliasee), which can trigger the performance cliff again:
abstract class ZPartialServerEndpoint[R, A, B, I, E, O, -C]
extends EndpointOps[A, I, E, O, C]{
override type ThisType[-_R] = ZPartialServerEndpoint[R, A, B, I, E, O, _R]
override type EndpointType[_A, _I, _E, _O, -_R] =ZPartialServerEndpoint[R, _A, B, _I, _E, _O, _R]
}
trait EndpointOps[A, I, E, O, -R] {
type EndpointType[_A, _I, _E, _O, -_R]
type ThisType[-_R]
def out[T]: EndpointType[A, I, E, T, R]
def description(d: String): ThisType[R]
}
object Test {
type Alias[R, A, B, I, E, O] = ZPartialServerEndpoint[R, A, B, I, E, O, Any]
def basicEndpoint[R](): Alias[R, Any, Any, Unit, Any, Unit] = ???
// commonts next to `.out[Any]` contain information about compilation time when chaining up to N `out` functions
val case1 =
basicEndpoint() // 1.5s
.out[Any] // 1.6s
.out[Any] // 1.7s
.out[Any] // 2s
.out[Any] // 4s
.out[Any] // 33s
.out[Any] // aborted after 5 min
}
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.
Yes, but I am not sure we can dealias without breaking stuff here. It is a super low-level code.
Projections P # X are types that we would like to avoid. If X is a class type, there's nothing we can do. If X is an abstract type, we use skolemization and rewrite to (x?: P).X. If X is an alias type we should simply dealias but this was not done before.
This caused an exponential blowup in #19892, where we constructed types of the form
ZPartialServerEndpoint[R, A, B, I, E, O, -C] # EndpointType[A, I, E, T, R] ... # EndpointType[A, I, E, T, R]
When the were 5 or more such selections, compile times blew up (33s for 5, timeout after 5 minutes for 6). I am still not quite sure where the blowup happened. Looking at stacktraces of random interrupts it seemed to be in a deep recursion of memberDenot and asSeenFrom calls.I believe it would still be interesting to find out more about this, in case there are other similar situations where combinations of deep projections with wide applications cannot be avoided.
But for this precise problem, eagerly dealiasing fixes it.
Fixes #19892