-
Notifications
You must be signed in to change notification settings - Fork 21
Inappropriate Widening of Invariant Type #11941
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
Comments
Note that this appears to work correctly in Dotty. See here. |
scala> implicitly[A with B <:< Object with A with B]
res0: A with B <:< A with B = generalized constraint
scala> implicitly[Object with A with B <:< A with B]
res1: A with B <:< A with B = generalized constraint I guess the compiler expands the compound type at some point. Compiling the example with def f[A](implicit evidence$1: scala.reflect.ClassTag[A]): Arrow[Invariant[A with Y],Invariant[A with Z]] = {
scala.Predef.println("ClassTag in f is ".+(scala.Predef.implicitly[scala.reflect.ClassTag[A]](evidence$1)));
Test.this.identity[Invariant[A with Y]].>>>[Invariant[Object with A with Z]](Test.this.g[Object with A]((ClassTag.apply[Object with A](classOf[java.lang.Object]): scala.reflect.ClassTag[Object with A])))
}; |
This is just the normal LUB: scala> trait X; trait Y
defined trait X
defined trait Y
scala> :t def foo[A](b: Boolean) = if (b) Option.empty[A with X] else Option.empty[A with Y]
[A](b: Boolean)Option[Object with A]
scala> :t def bar[A <: AnyRef](b: Boolean) = if (b) Option.empty[A with X] else Option.empty[A with Y]
[A <: AnyRef](b: Boolean)Option[A] Since we can't assume that Actually not sure how it works in Dotty. Is there special handling? |
I think the problem here is that this is the parameter of an invariant type. |
I don't see why. Traits by default extend AnyRef, which is just a type alias of Object, since X and Y are trait, |
Yes, that's right |
It might be more illuminating to try what happens if we have: trait Base
trait X extends Base
trait Y extends Base |
Oh I keep forgetting that in the Dotty the LUB is just the union type. |
ClassTag is very capricious scala> classTag[X & Y | X & Z]
val res0: scala.reflect.ClassTag[X & Y | X & Z] = X
scala> classTag[Y & X | Z & X]
val res2: scala.reflect.ClassTag[Y & X | Z & X] = Object |
ClassTag[X] gives you the class of the erasure of type X, and the erasure of an intersection/union type is not commutative: scala/scala3#5139 (and we most probably cannot fix this without compromising further the two-way binary interop between scala 2 and dotty). Type inference can mean you get intersections/unions in different orders and therefore different classtags. |
(Actually afaik only intersection/compound types have non-commutative erasure, unions are fine I think) |
Seeing as implicit ClassTag synthesis isn't user-land code, maybe it should require a user's intervention when given an intersection type? At worst, a lint. |
@adamgfraser izumi Tag contains a |
@neko-kai Good idea! Let me look at that. |
Uh oh!
There was an error while loading. Please reload this page.
reproduction steps
problem
expectation
I don't understand how the type of the
ClassTag
ing
isObject
. We're explicitly providing the type tof
ofX
. So then the type ofidentity
should beArrow[X with Y, X with Y]
. The type of theIn
parameter tog
should have to be the same as the type of theOut
fromidentity
, which should get usX with Y
and lead to theA
type ing
being inferred asX
. But instead it isObject
. I would have expected the type of both tags to beX
or for this not to have compiled at all.Copying @jdegoes.
The text was updated successfully, but these errors were encountered: