-
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
Do level checking on instantiation #15746
Conversation
524ab45
to
d03e626
Compare
@smarter I have put everything under two config flags, so one can switch between
That way, we can fix the immediate issues while we take more time to decide what kind of checking is best. |
@WojciechMazur This commit is fairly high risk for regressions. Can you run the large CB with this branch to see what would break? |
Sure, I've started the build and its progress can be found here https://scala3.westeurope.cloudapp.azure.com/blue/organizations/jenkins/runBuild/detail/runBuild/6/pipeline |
So I conclude we have some breakage. @WojciechMazur from your experience, can you give an estimate how bad it is? On the other hand, the current situation with unsound and buggy type variable instantiation is untenable. So we will need to make the effort to minimize the regressions and try to fix them individually. I am not sure whether this is better done with a merged PR or before. In any case we should merge this PR at the start of a release cycle. |
Introduce separate Config flags that enable level checking on constraints or level checking on instantiation.
I think we should merge this now, as long as we are in the beginning of a release cycle, and have a chance to catch regressions. Who can give it a review? |
Shouldn't we check whether this breaks more or less things in the open community build than the previous level-checking logic before settling on this approach?
|
I don't think so, since we can selectively turn either logic on. Level checking on constraint formation clearly does not work as is, and requires major changes to several parts of the type checker to make it work. There's no-one scheduled or volunteering to do these changes, so we cannot count on them to be done. Level checking on instantiation has a chance to work, we have to go through the effort of minimization and see what the reasons for the breakages are. Not doing level checking at all (which is the current state) also clearly does not work, and, if not addressed will just encourage more unsound code.
I am actually not so sure about this. The worst that could happen is that a type variable gets a lower level than expected, which is still sound. I find the alternative of instantiating type variables very complicated, and will not have time to do it. So i propose we merge with the option of improving this aspect later. |
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's no-one scheduled or volunteering to do these changes
It's something I'd like to do, although I can't promise when exactly right now.
The worst that could happen is that a type variable gets a lower level than expected, which is still sound.
It's probably sound, but it leads to weird "action at a distance" effects. Type inference bugs are already very hard to debug because there's so many interactions to consider, but with this change I fear the situation would get 10x harder to deal with: now some logic in a retracted TyperState might influence whether your code compiles or not, this will lead to regressions that take enormous effort to understand and that probably cannot be fixed.
418b9bd
to
4649fd9
Compare
I think in summary, with the long list of failures associated with the current disabled behavior, it's still better to go ahead and merge this. I am not happy about it, but I don't see a practicable alternative. |
I opened issue #15884 to keep track of the nestingLevel update problem. |
I think that would be good. But we cannot keep the code in the current problematic state until this is resolved. I think this needs to go into 3.2.1. So I propose we get back to it later, without the pressure of time constraints. |
Ok, but it would still be good if someone else could review this then. |
@dwijnand Do you think you could give this a go? It's plain constraints as opposed to GADT constraints but since the two are duals it should be close. |
I found a solution for the problem of hidden state in nestingLevel: Make the state explicit in TyperState where updates to nesting levels are stored. |
Nice! I was considering whether the nestingLevel should be stored in the Constraint, because there's multiple places where we rollback constraints without rolling back the full TyperState, e.g:
But perhaps these places should use TyperState#{snapshot, resetTo} instead. |
Yes, that seems right. |
Actually, I think we need the TyperState shapshot only for harmonize. The operations in ProtoTypes and TypeComparer only affect constraints but do not instantiate type variables. |
The previous way never reset any type variable since it traversed `ownedVars`, and instantiated TypeVars are no longer in the ownedVars of their owning TyperState.
In fact, I am not sure what to do about harmonize. It seems that resetting the constraint is fishy and resetting the typerstate is clearly wrong. We need to come back to this. |
Regressions found in the open community build:
|
Akka failure:
|
Is there a way to minimize the Akka failure? So far it does not tell us much. ?.CAP is an unknown instance of some type parameter. It's quite plausible that we first instantiated a type variable to something unsound and then this was the fallback. But it could also be something else. |
I can't reproduce it, but the code looks like something that you could easily fix with an explicit type argument or type annotation. The fix looks good to me. I might continue to try to recreate a minimised version of Akka's failure tomorrow. |
Level checking was first implemented #14026. The idea was to avoid level-incorrect constraints from the outset, by cloning type variables and adjusting bounds as needed. This fixed a number of issues, but also caused a number of regressions since the number of added type variables had subtle interactions with other aspects of type checking.
This PR is more conservative in its change set: It still allows level-incorrect constraints to be formed, but fixes levels when type variables are instantiated. To do this soundy, it uses an "untrusted bounds" mode in the type comparer, under which we do not accidentally forget types when forming lubs or glbs with other references that have bad bounds.
One can choose between level checking when forming constraints (i.e. #14026) vs level checking on instantiation (i.e. #15746) by setting the value
Config.checkLevelsOnConstraints
Fixes #8900
Fixes #15595