-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Preserve singletons in unions when they're explicitly written in the code #844
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
02e3805
to
2842762
Compare
Keeping singletons in unions for pattern alternatives causes very deep subtype recursions in https://github.com/lampepfl/dotty/blob/master/src/dotty/tools/dotc/parsing/JavaScanners.scala#L56 which we disallow in our tests by default, so I'll disable that for now. @odersky : do you think we should preserve singleton types in alternatives, even if it causes deep subtyping checks? |
…code Note that we do not keep singletons in pattern alternatives like `case x @ (1 | 2)` because if there are many alternatives like in `JavaScanner#fetchToken`, we end up with deep subtyping checks. Fixes scala#829
2842762
to
69112aa
Compare
I'd tend to not preserve singletons in alternatives. In
you don't write the singleton type explicitly. I think it is fair to say x
?
On Thu, Oct 22, 2015 at 5:56 PM, Guillaume Martres <notifications@github.com
Martin Odersky |
Yes, that now works: https://github.com/smarter/dotty/blob/fix/lub-singletons/tests/pos/singletons-lubs.scala#L7 |
@@ -246,7 +246,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. | |||
case AndType(l, r) => | |||
simplify(l, theMap) & simplify(r, theMap) | |||
case OrType(l, r) => |
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.
Simplified is called in lots of places for types that are not written. So it should not do the lub with keepSingletons = 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.
I think this is OK, because if we call simplified
on a type not written by a user which contains an OrType
, then this OrType
should have been constructed in the code by calling |
or lub
, so this OrType
should not have a singleton in it. And if we do not keep singletons in simplified
, then we will always lose them because we call simplified
in Typer#adapt
.
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 about that. Constraint handling does not do lubs. The purpose of simplify is to clean up after it. See it this way: If simplify only gets code where ortypes are computed by lubs, why would it do the lubs again?
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.
If simplify only gets code where ortypes are computed by lubs, why would it do the lubs again?
Because we call simplify
on the types inside the OrType
, and the lub of the result might not be an OrType
?
I am not sure about that. Constraint handling does not do lubs.
OK, so this should only have an impact when we instantiate type variables, but we already approximate unions in that case: https://github.com/lampepfl/dotty/blob/master/src/dotty/tools/dotc/core/Types.scala#L2481 isn't that enough? If it's not, we can always special case the .simplified
call when instantiating type variables to not keep singletons: https://github.com/lampepfl/dotty/blob/master/src/dotty/tools/dotc/core/Types.scala#L2475
Thinking about it, I found a different way to do it. Drop the boolean parameters (boolean parameters that have to be passed through several intermediaries are a code smell anyways). Create a new function
Then replace
in adapt with That should do it, right? And we are sure it would not do too much. |
case OrTypeTree(l, r) if l.tpe.isConstant && r.tpe.isConstant => We have to deal with explicit singleton types which are not constant types too if we want this to not compile: val a: Int = 1
val b: Int = 2
val c: Int = 3
val d: Int = 4
val foo: a.type | b.type = 1
val bar: c.type | d.type = foo |
OK, replace isConstant with .isInstanceOf[SingletonType] then. On Mon, Oct 26, 2015 at 1:06 PM, Guillaume Martres <notifications@github.com
Martin Odersky |
OK, but then: case OrTypeTree(l, r) if l.tpe.isInstanceOf[SingletonType] && r.tpe.isInstanceOf[SingletonType] => What about I still think that the PR as is should work, could you give an example where we could end up with a tree whose type contains a singleton type not written by the user? |
In fact, I think we need to change |
My main issue with this approach is that it is operational only. We need a spec that says clearly when singletons are widened. I can't see how to develop such a spec from the current implementation. So how about we do this first, and then decide on the PR? |
Sure, that sounds like a good idea. |
There are two cases where we should not widen singletons in unions:
val x: 1 | 2
case x @ (1 | 2) =>
Fixes #829.
Review by @odersky .