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
candidate unsafe == wart #46
Conversation
def apply(u: WartUniverse): u.Traverser = { | ||
import u.universe._ | ||
|
||
val EqEqName: TermName = "$eq$eq" |
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 you use this?
import reflect.NameTransformer
val EqEqName: TermName = NameTransformer.encode("==")
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.
Oh cool, I was wondering where that was. Will do.
@tpolecat "the TypeSymbol always claims to be non-abstract" At a wild guess, you are asking "isAbstract" when what you need to ask is "isDeferred". |
Also, the RHS is inferred as Any for equals because the expected type of the argument is Any. Int.== is overloaded with a bunch of non-Any types, leading it to eventually infer something more useful. |
@paulp aha |
Never computed. It would be very wasteful to do so. The die has been cast once you are calling the Any-accepting method. |
@paulp any idea why the RHS of |
It's the overloading of ==, which causes a whole different code path to be taken. You can see the same behavior in equals if you overload it. Add a "def equals(x: Bippy): Boolean = true" and it will infer the same type for both. |
val result = WartTestTraverser(Equal) { | ||
1 == 1.0 // can't do anything about widening here | ||
1 equals 1.0 // but we can catch it here | ||
} |
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.
The numeric widening thing is nutty. Here equals
triggers the wart and ==
doesn't.
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's because it isn't being widened. It is calling the overload which accepts a Double.
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.
Thanks, I'll update the test name.
So, to summarize, I'm still puzzled but |
I have narrowed my confusion to this case, which does not involve overloading.
|
Believe it or not that does involve overloading, albeit probably not intentionally. The == defined in AnyRef overloads the == defined in Any rather than overriding it, because the parameter type is an Any in Any and an AnyRef in AnyRef. The method equals also exists in both Any and AnyRef, but the one in AnyRef overrides the one in Any. I've never noticed this before; it should be considered a bug, especially since in normal code you can't overload those signatures since the methods would have the same type after erasure. Should be considered a bug - don't know if it will be. Oh, and keep in mind that it's the overloaded/buggy version which infers an accurate type, thereby avoiding recognition through your proxy indicator of Any-inference. You realize the 'Any' which is being inferred is the second type argument to map, right? (Which is itself another bug: see https://issues.scala-lang.org/browse/SI-6155 for some discussion.) |
See https://issues.scala-lang.org/browse/SI-8219 for yet more. |
Analysis of the accidental overloading is included in: scala/scala#3460 Why does overloading actually influence this? Arguments to methods that are still overloaded after an initial round of filtering by arity* are typechecked without an expected type. The argument types are then used to further filter the candidate overloaded alternatives. Finally, static overload resolution picks the winner.
A difference in type argument inference between |
You can choose some word other than "bug", or you can pin it on the architecture of the collections and/or the tragedy of universal equals or any number of places, but in the end, it's just broken. |
Guh. So if I'm reading this correctly it sounds like this may be the best I can do, and in fact it's only due to a bug that it works as well as it does. |
You got it. |
3b09288
to
9b0161d
Compare
Closing this since we now have the |
This is in response to #45.
This wart disables
==
for operands of non-conforming types. It disables, for instance, comparison betweenList
andSet
, but allows comparison betweenOption[Int]
andNone
. It would be nice, if the types are not equal, to require the supertype to be abstract; however I have not been able to figure out how to determine this (theTypeSymbol
always claims to be non-abstract).Note that implementing this wart for
equals
is, oddly, a different situation. In the expression(1 max 2) == List(3, 4).map(_ + 1)
the RHS has typeList[Int]
, but forequals
the RHS is inferred asAny ?
(which of course conforms with the LHS). Go figure.Anyway I thought I would throw this out as a starting point.