-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix #9011: Make single enum values inherit from Product #9018
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
This was done just because compiling with a non-empty classpaths leads to conflicts when main is used as an annotation.
Based on #9017. It needs provablyDisjoint to be fixed first. |
library/src/scala/EnumValue.scala
Outdated
@@ -0,0 +1,10 @@ | |||
package scala | |||
|
|||
trait EnumValue extends Product: |
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.
For parity with case object this should extend Serializable too I think:
trait EnumValue extends Product: | |
trait EnumValue extends Product with Serializable: |
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.
Actually it probably makes more sense to have Product
and Serializable
added as parents to the enum class itself, that way they never show up when lubbing enum cases.
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 about what other effects this will have.
The failure comes from EnumValue now leaking into a result type of a union. We could fix it locally in the test cases. But I'd like to try first whether we can come up with a systematic fix. |
We now strip EnumValue parents from inferred types, unless they are required by the bound. This is analogous to widen unions and singletons. It should be generalized to more types, not just EnumValue.
so that these traits do not leak into inferred types of unions of cases.
We now drop EnumValue just when inferring types of enum cases. This can be done unconditionally since in any case every enum case extends Enum, which extends Product and Serializable. EnumValue is just an invisible implementation bundle. This means we disable for now the more general handling in widenInferred.
It is no longer visible in the types of enum constants, so no need to keep it in Scala. Also, update doc pages to match the new scheme.
def dropProtected(tp: Type): Type = tp.dealias match | ||
case tp @ AndType(tp1, tp2) => | ||
if isProtected(tp1) then tp2 | ||
else if isProtected(tp2) then tp1 | ||
else tp.derivedAndType(dropProtected(tp1), dropProtected(tp2)) |
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.
This can drop aliases in situation where it isn't necessary, this can be avoided like this:
def dropProtected(tp: Type): Type = tp.dealias match | |
case tp @ AndType(tp1, tp2) => | |
if isProtected(tp1) then tp2 | |
else if isProtected(tp2) then tp1 | |
else tp.derivedAndType(dropProtected(tp1), dropProtected(tp2)) | |
def dropProtected(tp: Type): Type = tp.dealias match | |
case tpd @ AndType(tp1, tp2) => | |
if isProtected(tp1) then tp2 | |
else if isProtected(tp2) then tp1 | |
else | |
val tpdw = tp.derivedAndType(dropProtected(tp1), dropProtected(tp2)) | |
if tpdw ne tpd then tpdw else tp |
@@ -300,6 +300,8 @@ trait ConstraintHandling[AbstractContext] { | |||
* (i.e. `inst.widenSingletons <:< bound` succeeds with satisfiable constraint) | |||
* 2. If `inst` is a union type, approximate the union type from above by an intersection | |||
* of all common base types, provided the result is a subtype of `bound`. | |||
* 3. (currently not enabled) If `inst` is an intersection with some protected base types, drop |
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 wouldn't use the word protected, unless the plan is to actually use the protected modifier with them. Maybe restricted ?
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. For while I thought one could use protected
for this, but now I think, probably not.
case tp @ AndType(tp1, tp2) => | ||
if isEnumValue(tp1) then tp2 | ||
else if isEnumValue(tp2) then tp1 | ||
else tp.derivedAndType(dropEnumValue(tp1), dropEnumValue(tp2)) |
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.
Same suggestion as above about dealiasing:
case tp @ AndType(tp1, tp2) => | |
if isEnumValue(tp1) then tp2 | |
else if isEnumValue(tp2) then tp1 | |
else tp.derivedAndType(dropEnumValue(tp1), dropEnumValue(tp2)) | |
case tpd @ AndType(tp1, tp2) => | |
if isEnumValue(tp1) then tp2 | |
else if isEnumValue(tp2) then tp1 | |
else | |
val tpdw = tpd.derivedAndType(dropEnumValue(tp1), dropEnumValue(tp2)) | |
if tpdw ne tpd then tpdw else tp |
else /*widenRestricted*/(widenOr(widenSingle(inst))) | ||
// widenRestricted is currently not called since it's special cased in `dropEnumValue` | ||
// in `Namer`. It's left in here in case we want to generalize the scheme to other | ||
// "protected inheritance" classes. |
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'm not sure we should leave dead code like this around without a clear idea of what we want to do with it. Is this something you're planning to experiment with more? I think it would make sense to use this mechanism for getting rid of Serializable and Product so that they don't show up when lubbing case classes.
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.
See #9028.
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.
Very nice, thanks! Can you leave a reference to this issue number in the comment?
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.
def check(elem: Eq[_])(x: Any, y: Any): Boolean = | ||
elem.asInstanceOf[Eq[Any]].eqv(x, y) | ||
|
||
def iterator[T](p: T) = p.asInstanceOf[Product].productIterator |
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.
Going back to the original issue (#9011), is this cast actually safe?
The example in the docs suggests that if we have a Mirror.ProductOf[T] then we can safely cast an instance of T to Product (in order to access productIterator etc.). But this is not the case.
If this isn't actually guaranteed, then the documentation needs to be updated to not rely on this cast in an example.
We can leave #9011 open with that remark.
…On Fri, May 22, 2020 at 7:30 PM Guillaume Martres ***@***.***> wrote:
***@***.**** requested changes on this pull request.
Otherwise LGTM.
------------------------------
In tests/run/i9011.scala
<#9018 (comment)>:
> +}
+
+object Eq {
+ given Eq[Int] {
+ def eqv(x: Int, y: Int) = x == y
+ }
+
+ inline def summonAll[T <: Tuple]: List[Eq[_]] = inline erasedValue[T] match {
+ case _: Unit => Nil
+ case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts]
+ }
+
+ def check(elem: Eq[_])(x: Any, y: Any): Boolean =
+ elem.asInstanceOf[Eq[Any]].eqv(x, y)
+
+ def iterator[T](p: T) = p.asInstanceOf[Product].productIterator
Going back to the original issue, is this cast actually safe?
The example in the docs suggests that if we have a Mirror.ProductOf[T]
then we can safely cast an instance of T to Product (in order to access
productIterator etc.). But this is not the case.
If this isn't actually guaranteed, then the documentation needs to be
updated to not rely on this cast in an example.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#9018 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAGCKVXAN3OSUBMVPD5NIOTRS2ZDXANCNFSM4NG2T5YA>
.
--
Prof. Martin Odersky
LAMP/IC, EPFL
|
No description provided.