scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.72k stars 1.04k forks source link

Runtime java.lang.AbstractMethodError when combining opaque type erasure, value class erasure, and generic type erasure #17281

Open dzanot opened 1 year ago

dzanot commented 1 year ago

Compiler version

3.3.0

Minimized code

https://scastie.scala-lang.org/B5fP4EPnSJeMRIsI84IeQQ

type IO[T] = T
opaque type NonNegLong = Long

object NonNegLong {
  def apply(l: Long): Option[NonNegLong] = if (l < 0) None else Some(l)
}

case class Baz(value: NonNegLong) extends AnyVal

trait InnerThing[F[_]]{
  def withBaz(baz: Baz): F[Unit]
}

object Main {
  val testInnerImpl = new InnerThing[IO] {
    override def withBaz(baz: Baz): IO[Unit] = println("withBaz")
  }

  @main def run = {
    println("Hello")
    NonNegLong(1l).map(Baz.apply(_)).fold(println("Nope"))(testInnerImpl.withBaz(_))
    println("Goodbye")
  }
}

Output

Hello

With excaption

java.lang.AbstractMethodError: Receiver class Main$$anon$1 does not define or inherit an implementation of the resolved method 'abstract java.lang.Object withBaz(long)' of interface InnerThing.
    at Main$.run$$anonfun$3(main.scala:22)
    at Main$.run$$anonfun$adapted$3(main.scala:22)
    at scala.Option.fold(Option.scala:263)
    at Main$.run(main.scala:22)
    at run.main(main.scala:20)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at sbt.Run.invokeMain(Run.scala:143)
    at sbt.Run.execute$1(Run.scala:93)
    at sbt.Run.$anonfun$runWithLoader$5(Run.scala:120)
    at sbt.Run$.executeSuccess(Run.scala:186)
    at sbt.Run.runWithLoader(Run.scala:120)
    at sbt.Run.run(Run.scala:127)
    at com.olegych.scastie.sbtscastie.SbtScastiePlugin$$anon$1.$anonfun$run$1(SbtScastiePlugin.scala:38)
    at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
    at sbt.util.InterfaceUtil$$anon$1.get(InterfaceUtil.scala:21)
    at sbt.ScastieTrapExit$App.run(ScastieTrapExit.scala:258)
    at java.base/java.lang.Thread.run(Thread.java:833)

Expectation

No AbstractMethodError. This works as expected if you do any one of:

nicolasstucki commented 1 year ago

We need to minimize this example to not have a dependency on cats.effect.IO.

dzanot commented 1 year ago

Here is a zero dependency repro: https://scastie.scala-lang.org/PKqpEimLRYOWZmbl72myFw

dzanot commented 1 year ago

@nicolasstucki does this need more information?

dzanot commented 1 year ago

Tested with 3.3.0 release, updated the repro

sjrd commented 1 year ago

Further minimization:

type IO[T] = T

opaque type NonNegLong = Long

object NonNegLong {
  def apply(l: Long): NonNegLong = l
}

class Baz(value: NonNegLong) extends AnyVal

trait InnerThing[F[_]] {
  def withBaz(baz: Baz): F[Unit]
}

class InnerThingImpl extends InnerThing[IO] {
  override def withBaz(baz: Baz): IO[Unit] = println("withBaz")
}

object Test {
  def main(args: Array[String]): Unit = {
    val testInnerImpl: InnerThing[IO] = new InnerThingImpl

    println("Hello")
    val nnl = new Baz(NonNegLong(1L))
    testInnerImpl.withBaz(nnl)
    println("Goodbye")
  }
}