-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Extend exhaustivity checks to and-types #2155
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
Extend exhaustivity checks to and-types #2155
Conversation
http://dotty-ci.epfl.ch/lampepfl/dotty/1516/5 :
@felixmulder This is not a very precise error message :). |
Thanks for the quick turnaround @AleksanderBG . While waiting to see the failures, I think it's possible to merge
|
@smarter: re-ran it locally, seems to work just fine...amending the test suite tomorrow to dump all the logs on any failure. My best guess is that something threw an exception and the test called |
*/ | ||
def commonOpenTypeSpace(tp1: Type, tp2: Type): Space | ||
/** Return a space containing the values of both atom (non-decomposable) types. */ | ||
def intersectAtomTypes(tp1: Type, tp2: Type): Space |
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.
nit-pick: I would say AtomicTypes
instead of AtomTypes
.
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.
Can do - that does sound more correct than the other one.
I've merged Regarding the test failures, I've been running
Is there something I should be doing apart from just |
@AleksanderBG |
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 are making some big changes to the CI recently, the CI is most likely to be some instability -- but the partest failure might indicate some problem with our new partest framework. \cc @felixmulder.
|
||
if ((tp1.classSymbol.is(Trait) || tp2.classSymbol.is(Trait)) | ||
&& isOpen(tp1) && isOpen(tp2)) | ||
Typ(AndType(tp1, tp2), true) |
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.
Normally, we expect programmers will not abuse AND-types in pattern matches. However, to be safer, it's better to handle the case if tp1
and tp2
are themselves AND-types if it doesn't significantly complicate the implementation.
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.
Could you elaborate how and-types could be abused here? Judging from the classSymbol
implementation, if the type is an and-type, a symbol will be returned only if the and-type is "effectively" a class/trait, which seems to be correct in this case.
We could potentially simplify the and-type here if that's the case, 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.
What I've in mind is the case where the two types are Trait1 & Trait2
and Trait3 & Trait4
, in which case, the intersection would be Trait1 & Trait2 & Trait3 & Trait4
.
@@ -84,6 +84,9 @@ trait SpaceLogic { | |||
/** Is `tp1` the same type as `tp2`? */ | |||
def isEqualType(tp1: Type, tp2: Type): Boolean | |||
|
|||
/** Return a space containing the values of both atom (non-decomposable) types. */ | |||
def intersectAtomTypes(tp1: Type, tp2: Type): Space |
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.
Better to add one additional note saying that tp1
and tp2
are not subtype of each other.
BTW, @AleksanderBG I'm not sure if the CLA fails because you haven't signed the CLA? If not, you can do it below: |
@smarter thank you very much for the presentation - it was quite helpful! sealed trait T
object O { val x = 5 } extends T
(??? : T & { val x: Int }) match { ... } I had some problems with the compiler output during tests in |
Possibly by changing the |
The tests fail since I added an additional line of comment to |
implementability(and) match { | ||
case Unimplementable => Empty | ||
case _ => Typ(and, true) | ||
} |
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.
By implementability
, I guess you mean whether the greatest lower bound of tp1
and tp2
is possibly inhabited or not.
I'm wondering if there's some facility in Typer to soundly approximate GLB of two types? My intuition is that there should have some similar facilities, in order to simplify intersection types, but I don't know where it's located.
TypeComparer#glb
is close, but it seems it does not use the knowledge of Final
and single inheritance. Is it possible to refine TypeComparer#glb
to take advantage of Final
and single inheritance? Ping @smarter @odersky .
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 thought about it, but this is slightly different from inhabitation - I'm not interested in whether there exists a value of the given type, but rather whether it's possible to create a class or trait of this type. For instance, a singleton type is inhabited, but not implementable. The type being inhabited is a stronger statement, and I don't think it's necessary to check for it, since all types which are inhabited but not implementable will be caught by the subtyping check.
Checking for inhabitation would work too and I tried to look for an existing method, but I didn't find any. I'd be happy to use it if there's one.
There are still some cases to be added to I also looked at the methods in TypeComparer, but I don't think any of As a sidenote - debug printing I've added is only temporary and I'll remove it |
Rebased to master & touched up the commit history. |
Check if at least one of the types is a trait before deciding that their intersection is non-empty.
Handle: - nested and-types - refined types - type aliasing Add line end trimming to patmat tests to work around an issue where lines end with spaces.
For consistency with or-types.
@odersky when can I count on your review? I can improve this if needed, |
I am not too familiar with Space, defer to @liufengyun for that. What I can see LGTM. |
OK, let's merge then. Thanks a lot @AleksanderBG . |
Thank you too for you help @liufengyun! |
As discussed on gitter, this PR extends exhaustivity checks to and-types. It's possible to check exhaustivity of pattern matching on them in case of "marker" types like this:
I've defined and-types as checkable if either type is checkable, decomposable if either type is decomposable and used space intersection for decomposition. However, this gives wrong results for this case:
The underlying issue is that the intersection of two open types was previously approximated to be empty. I've added a check to
intersect
to see if it's possible to have a value of both types and return a type space of the and-type if that's the case. This changes slightly how space subtraction behaves - it's now possible to get "degenerate" unions from subtracting two constructor spaces ("degenerate" in the sense that one of the spaces is exactly the left-hand-side of the subtraction). This doesn't change the final result of exhaustivity check, though, so I'm not sure if it's an issue.I'm also not completely sure if I got the
isOpen
implementation right - I'm not really familiar with how symbols work, so it's entirely possible there's a mistake there.I'm also aware that
commonOpenTypeSpace
may sometimes return a space that can't be inhabited, since for instance the types are classes with conflicting methods, but I don't think it's an issue, since a) such and-types should be rare and b) we will at most emit a warning that the match may not be exhaustive./cc @liufengyun