-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Track type variable dependencies to guide instantiation decisions #16042
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
Conversation
test performance please |
@mbovel @anatoliykmetyuk Benchmark bot seems to be down? |
test performance please |
performance test scheduled: 355 job(s) in queue, 1 running. |
4 similar comments
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
Performance test finished successfully: Visit https://dotty-bench.epfl.ch/16042/ to see the changes. Benchmarks is based on merging with main (afc6ce4) |
614c5b9
to
06f4cff
Compare
Some info on the parboiled2 test. It seems to be a hygiene violation caused somewhere in typedQuotePattern. The test failed when adding a new polytype to the constraint as follows (with -uniqid on, to disambiguate)
|
The perspective failure is also a hygiene violation (both added parameters are already referred to in the constraint). This time it's called from TypeComparer#matchCase.
|
So, the question to decide is this:
So in the end the question is: Do we prefer to keep the behavior stable wrt replace or do we choose to optimize, with potentially big gains for large constraints? |
60af4b9
to
7bda186
Compare
test performance please |
performance test scheduled: 355 job(s) in queue, 1 running. |
We now keep track of reverse type variable dependencies in constraints. E.g. is a constraint contains a clause like A >: List[B] We associate with `B` info that A depends co-variantly on it. Or, if A <: B => C we associate with `B` that `A` depends co-variantly on it and with `C` that `A` depends contra-variantly on it. These dependencies are then used to guide type variable instantiation. If an eligible type variable does not appear in the type of a typed expression, we interpolate it to one of its bounds. Previously this was done in an ad-hoc manner where we minimized the type variable if it had a lower bound and maximized it otherwise. We now take reverse dependencies into account. If maximizing a type variable would narrow the remaining constraint we minimize, and if minimizing a type variable would narrow the remaining constraint we maximize. Only if the type variable is not referred to from the remaining constraint we resort to the old heuristic based on the lower bound. Fixes scala#15864
Can be enabled globally with Config option (set to false by default) or locally through option -Ycheck-constraint-deps. The option is currently turned on for pos, run, and patmatExhaustivity tests.
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.
There were two other uses of isInstantiated in OrderingConstraint which I suggest replacements for, otherwise LGTM.
Co-authored-by: Guillaume Martres <[email protected]>
@smarter The two occurrences were replaced following your suggestions. |
* type parameter is instantiated to a larger type, the constraint would be narrowed | ||
* (i.e. solution set changes other than simply being made larger). | ||
*/ | ||
private var coDeps: ReverseDeps = SimpleIdentityMap.empty |
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.
I only just realised that this (and its co-conspirator contraDeps
) made OrderingConstraint into a mutable data structure (previously it was immutable).
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.
It's actually not mutable. coDeps
and contraDeps
are views that are maintained incrementally.
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.
That would only make it not destructive. And even so, there is a dropDeps
, which does remove from them, if I'm not mistaken.
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.
I am not sure what you mean by destructive vs mutable. Are LazyLists mutable? In my book they aren't. And, dropDeps
is not exported in Constraint
so it is morally private (we should probably make it private to be clear about it)
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.
Ah, I understand better now. You're right.
Even though it looks very different, I think I prefer the approach that's taken with the original components of OrderingConstraint, i.e. in ConstraintLens update, where depending on the previous constraint (which is the external, public reference) it was either mutated in place or copied on write, and then always returned the updated reference. And that logic was well separate from the rest of the domain logic. With these new variables, the updates just happen at some point of the logic, mutating the vars in place.
We now keep track of reverse type variable dependencies in constraints.
E.g. if a constraint contains a clause like
We associate with
B
info thatA
depends co-variantly on it. Or, ifwe associate with
B
thatA
depends co-variantly on it and withC
that
A
depends contra-variantly on it. Here, co-variant means thatthe allowable range of
A
narrows if the referred-to variableB
grows, andcontra-variant means that the allowable range of
A
narrows if the referred-tovariable
C
shrinks. If there's an invariant reference such asThen
A
depends both co- and contra-variantly onB
.These dependencies are then used to guide type variable instantiation.
If an eligible type variable does not appear in the type of a typed expression,
we interpolate it to one of its bounds. Previously this was done in an ad-hoc
manner where we minimized the type variable if it had a lower bound and maximized
it otherwise. We now take reverse dependencies into account. If maximizing a type
variable would narrow the remaining constraint we minimize, and if minimizing
a type variable would narrow the remaining constraint we maximize. Only if
the type variable is not referred to from the remaining constraint we resort
to the old heuristic based on the lower bound.
Fixes #15864
Todo: This could be generalized in several directions:
That could make the
replace
operation in a constraint more efficient.interpolate a type variable only if both the type of an expression and the enclosing
constraint agree in which direction this should be done.