bug
bug copied to clipboard
Multiple type applications on overloaded method crashes scalac
There is a corner case that occurs in the intersection of type application and method overloading.
The following program shows an object A with an overloaded method a, in both cases with a simple type parameter F, with different arguments.
object A {
def a[F](x: Int) = 0
def a[F](x: String) = 0
}
A.a[Int][String](0)
In the final line, when the method is applied with two lists of type arguments, the compiler crashes with a java.lang.ClassCastException.
Note that, as explained in #6729, this is not a syntax problem. There can be applications with several lists of type parameters, such as when the result of one is an object with an def apply[F] method.
Similar programs that do not crash the compiler are the following ones:
- If you remove the type parameters
F. - If you only have one method
a(no overloading) - If you apply the method
ato a single list of several parametersA.a[Int, String](42).
The stack trace is telling:
scala> A.a[Int][Int](0)
java.lang.ClassCastException: scala.reflect.internal.Types$PolyType cannot be cast to scala.reflect.internal.Types$OverloadedType
at scala.reflect.internal.Symbols$Symbol.alternatives(Symbols.scala:1941)
at scala.reflect.internal.Symbols$Symbol.filter(Symbols.scala:1947)
at scala.tools.nsc.typechecker.Infer$Inferencer.inferPolyAlternatives(Infer.scala:1423)
at scala.tools.nsc.typechecker.Typers$Typer.typedTypeApply(Typers.scala:4025)
at scala.tools.nsc.typechecker.Typers$Typer.typedTypeApply$1(Typers.scala:5344)
at scala.tools.nsc.typechecker.Typers$Typer.typedOutsidePatternMode$1(Typers.scala:5507)
Nice find!
This also crashes the compiler in cases that should compile.
scala> :pa
// Entering paste mode (ctrl-D to finish)
class Apply { def apply[A](x: Int) = 1 }
object A {
def a[F] = new Apply
def a[F](x: String) = 0
}
// Exiting paste mode, now interpreting.
defined class Apply
defined object A
scala> A.a[String]("a")
res2: Int = 0
scala> A.a[String](3)
res3: Int = 1
scala> A.a[String][Int](3)
java.lang.ClassCastException
This caused some considerable head scratching today, I wrote
val paymentTermCodec = enumOf[PaymentTerm][42]
:boom:
whereas I meant to write
val paymentTermCodec = enumOf[PaymentTerm](42)
(where enumOf has overloads with len: Int and underlying: Codec)
Not sure how Diego came up with their example, but above is at least one piece of anecdotal evidence it can come up in practice.
closed PR a volunteer could try to finish: https://github.com/scala/scala/pull/10832
Auntie Polly will thank you.
Why is the first alternative treated specially? I don't get the purpose of this code.
// Alternatives which conform to bounds
def checkWithinBounds(sym: Symbol): Unit = sym.alternatives match {
case Nil => if (!argtypes.exists(_.isErroneous)) fail()
case alt :: Nil => finish(alt, pre memberType alt)
case alts @ hd :: _ =>
log(s"Attaching AntiPolyType-carrying overloaded type to $sym")
// Multiple alternatives which are within bounds; spin up an
// overloaded type which carries an "AntiPolyType" as a prefix.
val tparams = new AsSeenFromMap(pre, hd.owner) mapOver hd.typeParams
val bounds = tparams map (_.tpeHK) // see e.g., #1236
val tpe = PolyType(tparams, OverloadedType(AntiPolyType(pre, bounds), alts))
finish(sym setInfo tpe, tpe)
}
I observe that changing the test pos/t1236 adding an upper bound and switching the order
//def foo[F[_]](q: (String, String)) = "hello"
def foo[F[_]](e: Empty[F]) = "world"
def foo[F[_] <: List[_]](q: (String, String)) = "hello"
val x = foo[List](ListEmpty)
shows the AntiPolyType still around at UnCurry.
My loose understanding was that it simply assists in providing an expected type for args while typing. (We know the alternatives take F[_] and the applied arg is List.) Once you look up the member type, you don't see APT any more, which is only for the overload.
at scala.reflect.internal.Types$PolyType.mapOver(Types.scala:3028)
at scala.reflect.internal.transform.UnCurry$$anon$1.apply(UnCurry.scala:71)