scala / scala3

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

Reflective structural call with array parameter crashes during pickling #11043

Open griggt opened 3 years ago

griggt commented 3 years ago

Minimized code

import scala.reflect.Selectable.reflectiveSelectable

object Test {
  type Runner = { def run(args: Array[String]): Unit }

  def test(args: Array[String], runner: Runner): Unit =
    runner.run(args)

  def main(args: Array[String]): Unit = {
    test(Array("foo", "bar"), new {
      def run(args: Array[String]): Unit = args foreach println
    })
  }
}

Output (click arrow to expand)

```scala scala.MatchError: JavaArrayType(TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String)) (of class dotty.tools.dotc.core.Types$CachedJavaArrayType) while compiling rs-test-1.scala Exception in thread "main" scala.MatchError: JavaArrayType(TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String)) (of class dotty.tools.dotc.core.Types$CachedJavaArrayType) at dotty.tools.dotc.core.tasty.TreePickler.pickleNewType(TreePickler.scala:302) at dotty.tools.dotc.core.tasty.TreePickler.pickleType(TreePickler.scala:171) at dotty.tools.dotc.core.tasty.TreePickler.pickleConstant(TreePickler.scala:153) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:453) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree$$anonfun$43$$anonfun$1(TreePickler.scala:518) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.immutable.List.foreach(List.scala:333) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree$$anonfun$17(TreePickler.scala:518) at dotty.tools.dotc.core.tasty.TreePickler.withLength(TreePickler.scala:69) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:518) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree$$anonfun$6(TreePickler.scala:468) at dotty.tools.dotc.core.tasty.TreePickler.withLength(TreePickler.scala:69) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:468) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree$$anonfun$38$$anonfun$1(TreePickler.scala:438) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.immutable.List.foreach(List.scala:333) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree$$anonfun$3(TreePickler.scala:438) at dotty.tools.dotc.core.tasty.TreePickler.withLength(TreePickler.scala:69) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:439) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree$$anonfun$3(TreePickler.scala:437) at dotty.tools.dotc.core.tasty.TreePickler.withLength(TreePickler.scala:69) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:439) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:427) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree$$anonfun$4(TreePickler.scala:444) at dotty.tools.dotc.core.tasty.TreePickler.withLength(TreePickler.scala:69) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:446) at dotty.tools.dotc.core.tasty.TreePickler.pickleTreeUnlessEmpty(TreePickler.scala:330) at dotty.tools.dotc.core.tasty.TreePickler.pickleDef$$anonfun$1(TreePickler.scala:345) at dotty.tools.dotc.core.tasty.TreePickler.withLength(TreePickler.scala:69) at dotty.tools.dotc.core.tasty.TreePickler.pickleDef(TreePickler.scala:347) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:562) at dotty.tools.dotc.core.tasty.TreePickler.pickleStats$$anonfun$2(TreePickler.scala:371) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.immutable.List.foreach(List.scala:333) at dotty.tools.dotc.core.tasty.TreePickler.pickleStats(TreePickler.scala:371) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree$$anonfun$26(TreePickler.scala:588) at dotty.tools.dotc.core.tasty.TreePickler.withLength(TreePickler.scala:69) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:589) at dotty.tools.dotc.core.tasty.TreePickler.pickleDef$$anonfun$1(TreePickler.scala:342) at dotty.tools.dotc.core.tasty.TreePickler.withLength(TreePickler.scala:69) at dotty.tools.dotc.core.tasty.TreePickler.pickleDef(TreePickler.scala:347) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:564) at dotty.tools.dotc.core.tasty.TreePickler.pickleStats$$anonfun$2(TreePickler.scala:371) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.immutable.List.foreach(List.scala:333) at dotty.tools.dotc.core.tasty.TreePickler.pickleStats(TreePickler.scala:371) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree$$anonfun$29(TreePickler.scala:604) at dotty.tools.dotc.core.tasty.TreePickler.withLength(TreePickler.scala:69) at dotty.tools.dotc.core.tasty.TreePickler.pickleTree(TreePickler.scala:604) at dotty.tools.dotc.core.tasty.TreePickler.pickle$$anonfun$1(TreePickler.scala:774) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.immutable.List.foreach(List.scala:333) at dotty.tools.dotc.core.tasty.TreePickler.pickle(TreePickler.scala:774) at dotty.tools.dotc.transform.Pickler.run$$anonfun$3$$anonfun$2(Pickler.scala:69) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.immutable.List.foreach(List.scala:333) at dotty.tools.dotc.transform.Pickler.run$$anonfun$1(Pickler.scala:106) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.immutable.List.foreach(List.scala:333) at dotty.tools.dotc.transform.Pickler.run(Pickler.scala:106) at dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:296) at scala.collection.immutable.List.map(List.scala:246) at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:297) at dotty.tools.dotc.transform.Pickler.runOn(Pickler.scala:111) at dotty.tools.dotc.Run.runPhases$4$$anonfun$4(Run.scala:185) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323) at dotty.tools.dotc.Run.runPhases$5(Run.scala:195) at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:203) at scala.runtime.function.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12) at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:67) at dotty.tools.dotc.Run.compileUnits(Run.scala:210) at dotty.tools.dotc.Run.compileSources(Run.scala:146) at dotty.tools.dotc.Run.compile(Run.scala:130) at dotty.tools.dotc.Driver.doCompile(Driver.scala:39) at dotty.tools.dotc.Driver.process(Driver.scala:186) at dotty.tools.dotc.Driver.process(Driver.scala:155) at dotty.tools.dotc.Driver.process(Driver.scala:167) at dotty.tools.dotc.Driver.main(Driver.scala:194) at dotty.tools.dotc.Main.main(Main.scala) ```

Expectation

Successful compilation and when run prints:

foo
bar

Workaround

-   runner.run(args)
+   reflectiveSelectable(runner).applyDynamic("run", classOf[Array[String]])(args)

Notes

Works as expected in 3.0.0-M1 and 2.13.4. The regression appears to be caused by 574f74103397ed733b2ed30d88ada829ec2666af.

https://github.com/lampepfl/dotty/blob/574f74103397ed733b2ed30d88ada829ec2666af/compiler/src/dotty/tools/dotc/typer/Dynamic.scala#L221-L226

nicolasstucki commented 3 years ago

Minimized to

import scala.reflect.Selectable.reflectiveSelectable

type Runner = { def run(args: Array[String]): Unit }

def test(runner: Runner): Unit = runner.run(???)
nicolasstucki commented 3 years ago

The issue is that now we generate a classOf[String[]] instead of classOf[Array[String]].

@sjrd, why did you use erased types in 574f741? Could we potentially use non erased types and let eraser take care of them?

sjrd commented 3 years ago

Because I didn't know how to, in particular what to do when you need to conjure up a concrete type valid as call site when your definition site as T where T >: A <: B with more or less arbitrary bounds?

smarter commented 3 years ago

What are you looking for exactly? If you need an applied class type which is a supertype of some arbitrary myType, then try myType.baseType(myType.classSymbol).

vmvazque commented 1 year ago

Is there any update on this issue? I'm seeing the same problem with the 3.3.0 compiler and just wondering if I should use the workaround or wait for a fix.

bishabosha commented 1 year ago

potential spree candidate? @mbovel