Skip to content

Multiple type applications on overloaded method crashes scalac #10628

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

Open
diesalbla opened this issue Nov 27, 2017 · 7 comments · May be fixed by scala/scala#10832
Open

Multiple type applications on overloaded method crashes scalac #10628

diesalbla opened this issue Nov 27, 2017 · 7 comments · May be fixed by scala/scala#10832

Comments

@diesalbla
Copy link

diesalbla commented Nov 27, 2017

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 a to a single list of several parameters A.a[Int, String](42) .
@hrhino
Copy link

hrhino commented Nov 27, 2017

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!

@hrhino hrhino changed the title Overloaded methods Multiple type applications on overloaded method crashes scalac Nov 27, 2017
@Jasper-M
Copy link

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

@dimitarg
Copy link

dimitarg commented May 24, 2023

This caused some considerable head scratching today, I wrote

val paymentTermCodec = enumOf[PaymentTerm][42]

💥

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.

@SethTisue SethTisue added this to the Backlog milestone May 24, 2023
@som-snytt som-snytt self-assigned this Sep 15, 2023
@som-snytt som-snytt modified the milestones: Backlog, 2.13.15 Aug 12, 2024
@SethTisue SethTisue modified the milestones: 2.13.15, 2.13.16 Aug 22, 2024
@SethTisue SethTisue modified the milestones: 2.13.16, Backlog Nov 5, 2024
@SethTisue
Copy link
Member

closed PR a volunteer could try to finish: scala/scala#10832

@som-snytt
Copy link

Auntie Polly will thank you.

@joroKr21
Copy link
Member

joroKr21 commented Nov 5, 2024

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)
      }

@som-snytt
Copy link

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants